From 852b7db7efd2de9409a578f2e19debd2cfaf2abd Mon Sep 17 00:00:00 2001 From: Thilo Borgmann <thilo.borgm...@mail.de> Date: Mon, 23 Nov 2015 23:19:23 +0100 Subject: [PATCH 1/3] lavd/avfoundation: Switch from run-time approach for format detection to format query and Organize frame buffer in buffer queue.
This fixes tickets #4089, #4437, #4463 and #4513. --- libavdevice/avfoundation.m | 385 +++++++++++++++++++-------------------------- 1 file changed, 166 insertions(+), 219 deletions(-) diff --git a/libavdevice/avfoundation.m b/libavdevice/avfoundation.m index 763e675..37f6be0 100644 --- a/libavdevice/avfoundation.m +++ b/libavdevice/avfoundation.m @@ -26,6 +26,7 @@ */ #import <AVFoundation/AVFoundation.h> +#import <CoreMedia/CoreMedia.h> #include <pthread.h> #include "libavutil/pixdesc.h" @@ -82,14 +83,10 @@ typedef struct { AVClass* class; - int frames_captured; - int audio_frames_captured; int64_t first_pts; int64_t first_audio_pts; - pthread_mutex_t frame_lock; - pthread_cond_t frame_wait_cond; id avf_delegate; - id avf_audio_delegate; + dispatch_queue_t dispatch_queue; AVRational framerate; int width, height; @@ -108,6 +105,8 @@ typedef struct int num_video_devices; + AVCaptureDeviceFormat *audio_format; + int audio_channels; int audio_bits_per_sample; int audio_float; @@ -124,20 +123,9 @@ typedef struct AVCaptureSession *capture_session; AVCaptureVideoDataOutput *video_output; AVCaptureAudioDataOutput *audio_output; - CMSampleBufferRef current_frame; - CMSampleBufferRef current_audio_frame; + CMBufferQueueRef frame_buffer; } AVFContext; -static void lock_frames(AVFContext* ctx) -{ - pthread_mutex_lock(&ctx->frame_lock); -} - -static void unlock_frames(AVFContext* ctx) -{ - pthread_mutex_unlock(&ctx->frame_lock); -} - /** FrameReciever class - delegate for AVCaptureSession */ @interface AVFFrameReceiver : NSObject @@ -167,65 +155,7 @@ static void unlock_frames(AVFContext* ctx) didOutputSampleBuffer:(CMSampleBufferRef)videoFrame fromConnection:(AVCaptureConnection *)connection { - lock_frames(_context); - - if (_context->current_frame != nil) { - CFRelease(_context->current_frame); - } - - _context->current_frame = (CMSampleBufferRef)CFRetain(videoFrame); - - pthread_cond_signal(&_context->frame_wait_cond); - - unlock_frames(_context); - - ++_context->frames_captured; -} - -@end - -/** AudioReciever class - delegate for AVCaptureSession - */ -@interface AVFAudioReceiver : NSObject -{ - AVFContext* _context; -} - -- (id)initWithContext:(AVFContext*)context; - -- (void) captureOutput:(AVCaptureOutput *)captureOutput - didOutputSampleBuffer:(CMSampleBufferRef)audioFrame - fromConnection:(AVCaptureConnection *)connection; - -@end - -@implementation AVFAudioReceiver - -- (id)initWithContext:(AVFContext*)context -{ - if (self = [super init]) { - _context = context; - } - return self; -} - -- (void) captureOutput:(AVCaptureOutput *)captureOutput - didOutputSampleBuffer:(CMSampleBufferRef)audioFrame - fromConnection:(AVCaptureConnection *)connection -{ - lock_frames(_context); - - if (_context->current_audio_frame != nil) { - CFRelease(_context->current_audio_frame); - } - - _context->current_audio_frame = (CMSampleBufferRef)CFRetain(audioFrame); - - pthread_cond_signal(&_context->frame_wait_cond); - - unlock_frames(_context); - - ++_context->audio_frames_captured; + CMBufferQueueEnqueue(_context->frame_buffer, videoFrame); } @end @@ -238,22 +168,16 @@ static void destroy_context(AVFContext* ctx) [ctx->video_output release]; [ctx->audio_output release]; [ctx->avf_delegate release]; - [ctx->avf_audio_delegate release]; ctx->capture_session = NULL; ctx->video_output = NULL; ctx->audio_output = NULL; ctx->avf_delegate = NULL; - ctx->avf_audio_delegate = NULL; av_freep(&ctx->audio_buffer); - pthread_mutex_destroy(&ctx->frame_lock); - pthread_cond_destroy(&ctx->frame_wait_cond); - - if (ctx->current_frame) { - CFRelease(ctx->current_frame); - } + CFRelease(ctx->frame_buffer); + dispatch_release(ctx->dispatch_queue); } static void parse_device_name(AVFormatContext *s) @@ -273,7 +197,7 @@ static void parse_device_name(AVFormatContext *s) /** * Configure the video device. * - * Configure the video device using a run-time approach to access properties + * Configure the video device using format query to access properties * since formats, activeFormat are available since iOS >= 7.0 or OSX >= 10.7 * and activeVideoMaxFrameDuration is available since i0S >= 7.0 and OSX >= 10.9. * @@ -301,6 +225,8 @@ static int configure_video_device(AVFormatContext *s, AVCaptureDevice *video_dev (dimensions.width == ctx->width && dimensions.height == ctx->height)) { selected_format = format; + ctx->width = dimensions.width; + ctx->height = dimensions.height; for (range in [format valueForKey:@"videoSupportedFrameRateRanges"]) { double max_framerate; @@ -372,7 +298,6 @@ static int add_video_device(AVFormatContext *s, AVCaptureDevice *video_device) struct AVFPixelFormatSpec pxl_fmt_spec; NSNumber *pixel_format; NSDictionary *capture_dict; - dispatch_queue_t queue; if (ctx->video_device_index < ctx->num_video_devices) { capture_input = (AVCaptureInput*) [[[AVCaptureDeviceInput alloc] initWithDevice:video_device error:&error] autorelease]; @@ -474,12 +399,7 @@ static int add_video_device(AVFormatContext *s, AVCaptureDevice *video_device) [ctx->video_output setVideoSettings:capture_dict]; [ctx->video_output setAlwaysDiscardsLateVideoFrames:YES]; - - ctx->avf_delegate = [[AVFFrameReceiver alloc] initWithContext:ctx]; - - queue = dispatch_queue_create("avf_queue", NULL); - [ctx->video_output setSampleBufferDelegate:ctx->avf_delegate queue:queue]; - dispatch_release(queue); + [ctx->video_output setSampleBufferDelegate:ctx->avf_delegate queue:ctx->dispatch_queue]; if ([ctx->capture_session canAddOutput:ctx->video_output]) { [ctx->capture_session addOutput:ctx->video_output]; @@ -496,7 +416,6 @@ static int add_audio_device(AVFormatContext *s, AVCaptureDevice *audio_device) AVFContext *ctx = (AVFContext*)s->priv_data; NSError *error = nil; AVCaptureDeviceInput* audio_dev_input = [[[AVCaptureDeviceInput alloc] initWithDevice:audio_device error:&error] autorelease]; - dispatch_queue_t queue; if (!audio_dev_input) { av_log(s, AV_LOG_ERROR, "Failed to create AV capture input device: %s\n", @@ -519,11 +438,8 @@ static int add_audio_device(AVFormatContext *s, AVCaptureDevice *audio_device) return 1; } - ctx->avf_audio_delegate = [[AVFAudioReceiver alloc] initWithContext:ctx]; + [ctx->audio_output setSampleBufferDelegate:ctx->avf_delegate queue:ctx->dispatch_queue]; - queue = dispatch_queue_create("avf_audio_queue", NULL); - [ctx->audio_output setSampleBufferDelegate:ctx->avf_audio_delegate queue:queue]; - dispatch_release(queue); if ([ctx->capture_session canAddOutput:ctx->audio_output]) { [ctx->capture_session addOutput:ctx->audio_output]; @@ -537,125 +453,112 @@ static int add_audio_device(AVFormatContext *s, AVCaptureDevice *audio_device) static int get_video_config(AVFormatContext *s) { - AVFContext *ctx = (AVFContext*)s->priv_data; - CVImageBufferRef image_buffer; - CGSize image_buffer_size; + AVFContext *ctx = (AVFContext*)s->priv_data; AVStream* stream = avformat_new_stream(s, NULL); if (!stream) { return 1; } - // Take stream info from the first frame. - while (ctx->frames_captured < 1) { - CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, YES); - } - - lock_frames(ctx); - - ctx->video_stream_index = stream->index; - - avpriv_set_pts_info(stream, 64, 1, avf_time_base); - - image_buffer = CMSampleBufferGetImageBuffer(ctx->current_frame); - image_buffer_size = CVImageBufferGetEncodedSize(image_buffer); - + ctx->video_stream_index = stream->index; stream->codec->codec_id = AV_CODEC_ID_RAWVIDEO; stream->codec->codec_type = AVMEDIA_TYPE_VIDEO; - stream->codec->width = (int)image_buffer_size.width; - stream->codec->height = (int)image_buffer_size.height; + stream->codec->width = ctx->width; + stream->codec->height = ctx->height; stream->codec->pix_fmt = ctx->pixel_format; - CFRelease(ctx->current_frame); - ctx->current_frame = nil; - - unlock_frames(ctx); + avpriv_set_pts_info(stream, 64, 1, avf_time_base); return 0; } +static enum AVCodecID get_audio_codec_id(AVCaptureDeviceFormat *audio_format) +{ + AudioStreamBasicDescription *audio_format_desc = (AudioStreamBasicDescription*)CMAudioFormatDescriptionGetStreamBasicDescription(audio_format.formatDescription); + int audio_linear = audio_format_desc->mFormatID == kAudioFormatLinearPCM; + int audio_bits_per_sample = audio_format_desc->mBitsPerChannel; + int audio_float = audio_format_desc->mFormatFlags & kAudioFormatFlagIsFloat; + int audio_be = audio_format_desc->mFormatFlags & kAudioFormatFlagIsBigEndian; + int audio_signed_integer = audio_format_desc->mFormatFlags & kAudioFormatFlagIsSignedInteger; + int audio_packed = audio_format_desc->mFormatFlags & kAudioFormatFlagIsPacked; + + enum AVCodecID ret = AV_CODEC_ID_NONE; + + if (audio_linear && + audio_float && + audio_bits_per_sample == 32 && + audio_packed) { + ret = audio_be ? AV_CODEC_ID_PCM_F32BE : AV_CODEC_ID_PCM_F32LE; + } else if (audio_linear && + audio_signed_integer && + audio_bits_per_sample == 16 && + audio_packed) { + ret = audio_be ? AV_CODEC_ID_PCM_S16BE : AV_CODEC_ID_PCM_S16LE; + } else if (audio_linear && + audio_signed_integer && + audio_bits_per_sample == 24 && + audio_packed) { + ret = audio_be ? AV_CODEC_ID_PCM_S24BE : AV_CODEC_ID_PCM_S24LE; + } else if (audio_linear && + audio_signed_integer && + audio_bits_per_sample == 32 && + audio_packed) { + ret = audio_be ? AV_CODEC_ID_PCM_S32BE : AV_CODEC_ID_PCM_S32LE; + } + + return ret; +} + static int get_audio_config(AVFormatContext *s) { - AVFContext *ctx = (AVFContext*)s->priv_data; - CMFormatDescriptionRef format_desc; + AVFContext *ctx = (AVFContext*)s->priv_data; AVStream* stream = avformat_new_stream(s, NULL); + AudioStreamBasicDescription *audio_format_desc = (AudioStreamBasicDescription*)CMAudioFormatDescriptionGetStreamBasicDescription(ctx->audio_format.formatDescription); if (!stream) { return 1; } - // Take stream info from the first frame. - while (ctx->audio_frames_captured < 1) { - CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, YES); - } - - lock_frames(ctx); - - ctx->audio_stream_index = stream->index; - - avpriv_set_pts_info(stream, 64, 1, avf_time_base); - - format_desc = CMSampleBufferGetFormatDescription(ctx->current_audio_frame); - const AudioStreamBasicDescription *basic_desc = CMAudioFormatDescriptionGetStreamBasicDescription(format_desc); - - if (!basic_desc) { - av_log(s, AV_LOG_ERROR, "audio format not available\n"); - return 1; - } - + ctx->audio_stream_index = stream->index; stream->codec->codec_type = AVMEDIA_TYPE_AUDIO; - stream->codec->sample_rate = basic_desc->mSampleRate; - stream->codec->channels = basic_desc->mChannelsPerFrame; + stream->codec->sample_rate = audio_format_desc->mSampleRate; + stream->codec->channels = audio_format_desc->mChannelsPerFrame; stream->codec->channel_layout = av_get_default_channel_layout(stream->codec->channels); - ctx->audio_channels = basic_desc->mChannelsPerFrame; - ctx->audio_bits_per_sample = basic_desc->mBitsPerChannel; - ctx->audio_float = basic_desc->mFormatFlags & kAudioFormatFlagIsFloat; - ctx->audio_be = basic_desc->mFormatFlags & kAudioFormatFlagIsBigEndian; - ctx->audio_signed_integer = basic_desc->mFormatFlags & kAudioFormatFlagIsSignedInteger; - ctx->audio_packed = basic_desc->mFormatFlags & kAudioFormatFlagIsPacked; - ctx->audio_non_interleaved = basic_desc->mFormatFlags & kAudioFormatFlagIsNonInterleaved; + avpriv_set_pts_info(stream, 64, 1, avf_time_base); + + ctx->audio_channels = audio_format_desc->mChannelsPerFrame; + ctx->audio_bits_per_sample = audio_format_desc->mBitsPerChannel; + int audio_float = audio_format_desc->mFormatFlags & kAudioFormatFlagIsFloat; + int audio_be = audio_format_desc->mFormatFlags & kAudioFormatFlagIsBigEndian; + int audio_signed_integer = audio_format_desc->mFormatFlags & kAudioFormatFlagIsSignedInteger; + int audio_packed = audio_format_desc->mFormatFlags & kAudioFormatFlagIsPacked; - if (basic_desc->mFormatID == kAudioFormatLinearPCM && - ctx->audio_float && + if (audio_format_desc->mFormatID == kAudioFormatLinearPCM && + audio_float && ctx->audio_bits_per_sample == 32 && - ctx->audio_packed) { - stream->codec->codec_id = ctx->audio_be ? AV_CODEC_ID_PCM_F32BE : AV_CODEC_ID_PCM_F32LE; - } else if (basic_desc->mFormatID == kAudioFormatLinearPCM && - ctx->audio_signed_integer && + audio_packed) { + stream->codec->codec_id = audio_be ? AV_CODEC_ID_PCM_F32BE : AV_CODEC_ID_PCM_F32LE; + } else if (audio_format_desc->mFormatID == kAudioFormatLinearPCM && + audio_signed_integer && ctx->audio_bits_per_sample == 16 && - ctx->audio_packed) { - stream->codec->codec_id = ctx->audio_be ? AV_CODEC_ID_PCM_S16BE : AV_CODEC_ID_PCM_S16LE; - } else if (basic_desc->mFormatID == kAudioFormatLinearPCM && - ctx->audio_signed_integer && + audio_packed) { + stream->codec->codec_id = audio_be ? AV_CODEC_ID_PCM_S16BE : AV_CODEC_ID_PCM_S16LE; + } else if (audio_format_desc->mFormatID == kAudioFormatLinearPCM && + audio_signed_integer && ctx->audio_bits_per_sample == 24 && - ctx->audio_packed) { - stream->codec->codec_id = ctx->audio_be ? AV_CODEC_ID_PCM_S24BE : AV_CODEC_ID_PCM_S24LE; - } else if (basic_desc->mFormatID == kAudioFormatLinearPCM && - ctx->audio_signed_integer && + audio_packed) { + stream->codec->codec_id = audio_be ? AV_CODEC_ID_PCM_S24BE : AV_CODEC_ID_PCM_S24LE; + } else if (audio_format_desc->mFormatID == kAudioFormatLinearPCM && + audio_signed_integer && ctx->audio_bits_per_sample == 32 && - ctx->audio_packed) { - stream->codec->codec_id = ctx->audio_be ? AV_CODEC_ID_PCM_S32BE : AV_CODEC_ID_PCM_S32LE; + audio_packed) { + stream->codec->codec_id = audio_be ? AV_CODEC_ID_PCM_S32BE : AV_CODEC_ID_PCM_S32LE; } else { av_log(s, AV_LOG_ERROR, "audio format is not supported\n"); return 1; } - if (ctx->audio_non_interleaved) { - CMBlockBufferRef block_buffer = CMSampleBufferGetDataBuffer(ctx->current_audio_frame); - ctx->audio_buffer_size = CMBlockBufferGetDataLength(block_buffer); - ctx->audio_buffer = av_malloc(ctx->audio_buffer_size); - if (!ctx->audio_buffer) { - av_log(s, AV_LOG_ERROR, "error allocating audio buffer\n"); - return 1; - } - } - - CFRelease(ctx->current_audio_frame); - ctx->current_audio_frame = nil; - - unlock_frames(ctx); - return 0; } @@ -673,9 +576,11 @@ static int avf_read_header(AVFormatContext *s) ctx->first_pts = av_gettime(); ctx->first_audio_pts = av_gettime(); + // Create dispatch queue and set delegate + CMBufferQueueCreate(kCFAllocatorDefault, 0, CMBufferQueueGetCallbacksForSampleBuffersSortedByOutputPTS(), &ctx->frame_buffer); + ctx->avf_delegate = [[AVFFrameReceiver alloc] initWithContext:ctx]; + ctx->dispatch_queue = dispatch_queue_create("org.ffmpeg.dispatch_queue", NULL); - pthread_mutex_init(&ctx->frame_lock, NULL); - pthread_cond_init(&ctx->frame_wait_cond, NULL); #if !TARGET_OS_IPHONE && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070 CGGetActiveDisplayList(0, NULL, &num_screens); @@ -895,70 +800,106 @@ fail: static int avf_read_packet(AVFormatContext *s, AVPacket *pkt) { AVFContext* ctx = (AVFContext*)s->priv_data; + CMItemCount count; + CMSampleTimingInfo timing_info; + int64_t *first_pts = NULL; do { - CVImageBufferRef image_buffer; - lock_frames(ctx); + CMSampleBufferRef current_frame = (CMSampleBufferRef)CMBufferQueueDequeueAndRetain(ctx->frame_buffer); + + if (!current_frame) { + CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.001, YES); + continue; + } - image_buffer = CMSampleBufferGetImageBuffer(ctx->current_frame); + if (!CMSampleBufferDataIsReady(current_frame)) { + CFRelease(current_frame); + continue; + } + + if (CMSampleBufferGetOutputSampleTimingInfoArray(current_frame, 1, &timing_info, &count) != noErr) { + CFRelease(current_frame); + continue; + } else if (timing_info.presentationTimeStamp.value == 0) { + CFRelease(current_frame); + continue; + } - if (ctx->current_frame != nil) { + CVImageBufferRef image_buffer = CMSampleBufferGetImageBuffer(current_frame); + CMBlockBufferRef block_buffer = CMSampleBufferGetDataBuffer(current_frame); + + if (image_buffer) { void *data; + if (av_new_packet(pkt, (int)CVPixelBufferGetDataSize(image_buffer)) < 0) { + CFRelease(current_frame); return AVERROR(EIO); } - CMItemCount count; - CMSampleTimingInfo timing_info; - - if (CMSampleBufferGetOutputSampleTimingInfoArray(ctx->current_frame, 1, &timing_info, &count) == noErr) { - AVRational timebase_q = av_make_q(1, timing_info.presentationTimeStamp.timescale); - pkt->pts = pkt->dts = av_rescale_q(timing_info.presentationTimeStamp.value, timebase_q, avf_time_base_q); - } - pkt->stream_index = ctx->video_stream_index; pkt->flags |= AV_PKT_FLAG_KEY; CVPixelBufferLockBaseAddress(image_buffer, 0); - data = CVPixelBufferGetBaseAddress(image_buffer); - memcpy(pkt->data, data, pkt->size); + if (CVPixelBufferIsPlanar(image_buffer)) { + int8_t *dst = pkt->data; + for (int i = 0; i < CVPixelBufferGetPlaneCount(image_buffer); i++) { + data = CVPixelBufferGetBaseAddressOfPlane(image_buffer, i); + size_t data_size = CVPixelBufferGetBytesPerRowOfPlane(image_buffer, i) * + CVPixelBufferGetHeightOfPlane(image_buffer, i); + memcpy(dst, data, data_size); + dst += data_size; + } + } else { + data = CVPixelBufferGetBaseAddress(image_buffer); + memcpy(pkt->data, data, pkt->size); + } CVPixelBufferUnlockBaseAddress(image_buffer, 0); - CFRelease(ctx->current_frame); - ctx->current_frame = nil; - } else if (ctx->current_audio_frame != nil) { - CMBlockBufferRef block_buffer = CMSampleBufferGetDataBuffer(ctx->current_audio_frame); - int block_buffer_size = CMBlockBufferGetDataLength(block_buffer); + + first_pts = &ctx->first_pts; + } else if (block_buffer) { + int block_buffer_size = CMBlockBufferGetDataLength(block_buffer); if (!block_buffer || !block_buffer_size) { + CFRelease(current_frame); return AVERROR(EIO); } - if (ctx->audio_non_interleaved && block_buffer_size > ctx->audio_buffer_size) { + // evaluate kAudioFormatFlagIsNonInterleaved which might have changed even if the capture session is locked + CMFormatDescriptionRef format_desc = CMSampleBufferGetFormatDescription(current_frame); + const AudioStreamBasicDescription *audio_format_desc = CMAudioFormatDescriptionGetStreamBasicDescription(format_desc); + int audio_non_interleaved = audio_format_desc->mFormatFlags & kAudioFormatFlagIsNonInterleaved; + + if (audio_non_interleaved && !ctx->audio_buffer) { + ctx->audio_buffer = av_malloc(block_buffer_size); + ctx->audio_buffer_size = block_buffer_size; + if (!ctx->audio_buffer) { + av_log(ctx, AV_LOG_ERROR, "error allocating audio buffer\n"); + CFRelease(current_frame); + return AVERROR(EIO); // something better for no memory? + } + } + + if (audio_non_interleaved && block_buffer_size > ctx->audio_buffer_size) { + CFRelease(current_frame); return AVERROR_BUFFER_TOO_SMALL; } if (av_new_packet(pkt, block_buffer_size) < 0) { + CFRelease(current_frame); return AVERROR(EIO); } - CMItemCount count; - CMSampleTimingInfo timing_info; - - if (CMSampleBufferGetOutputSampleTimingInfoArray(ctx->current_audio_frame, 1, &timing_info, &count) == noErr) { - AVRational timebase_q = av_make_q(1, timing_info.presentationTimeStamp.timescale); - pkt->pts = pkt->dts = av_rescale_q(timing_info.presentationTimeStamp.value, timebase_q, avf_time_base_q); - } - pkt->stream_index = ctx->audio_stream_index; pkt->flags |= AV_PKT_FLAG_KEY; - if (ctx->audio_non_interleaved) { + if (audio_non_interleaved) { int sample, c, shift, num_samples; OSStatus ret = CMBlockBufferCopyDataBytes(block_buffer, 0, pkt->size, ctx->audio_buffer); if (ret != kCMBlockBufferNoErr) { + CFRelease(current_frame); return AVERROR(EIO); } @@ -970,7 +911,7 @@ static int avf_read_packet(AVFormatContext *s, AVPacket *pkt) int##bps##_t **src; \ int##bps##_t *dest; \ src = av_malloc(ctx->audio_channels * sizeof(int##bps##_t*)); \ - if (!src) return AVERROR(EIO); \ + if (!src) {CFRelease(current_frame); return AVERROR(EIO);} \ for (c = 0; c < ctx->audio_channels; c++) { \ src[c] = ((int##bps##_t*)ctx->audio_buffer) + c * num_samples; \ } \ @@ -990,18 +931,24 @@ static int avf_read_packet(AVFormatContext *s, AVPacket *pkt) } else { OSStatus ret = CMBlockBufferCopyDataBytes(block_buffer, 0, pkt->size, pkt->data); if (ret != kCMBlockBufferNoErr) { + CFRelease(current_frame); return AVERROR(EIO); } } - CFRelease(ctx->current_audio_frame); - ctx->current_audio_frame = nil; - } else { - pkt->data = NULL; - pthread_cond_wait(&ctx->frame_wait_cond, &ctx->frame_lock); + first_pts = &ctx->first_audio_pts; + } + + if (first_pts) { + if (!*first_pts) { + *first_pts = timing_info.presentationTimeStamp.value; + } + // TODO: this produces non-monotonous DTS if bits_per_sample == 16 + AVRational timebase_q = av_make_q(1, timing_info.presentationTimeStamp.timescale); + pkt->pts = pkt->dts = av_rescale_q(timing_info.presentationTimeStamp.value - *first_pts, timebase_q, avf_time_base_q); } - unlock_frames(ctx); + CFRelease(current_frame); } while (!pkt->data); return 0; -- 2.3.2 (Apple Git-55)
_______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel