[vlc-devel] [PATCH] WIP: add VideoToolbox based decoder

Felix Paul Kühne fkuehne at videolan.org
Mon Oct 13 22:32:30 CEST 2014


---
 configure.ac                 |  11 +
 modules/codec/Makefile.am    |   7 +
 modules/codec/videotoolbox.m | 657 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 675 insertions(+)
 create mode 100644 modules/codec/videotoolbox.m

diff --git a/configure.ac b/configure.ac
index a1810b2..1523b07 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2445,6 +2445,17 @@ AS_IF([test "${enable_vda}" != "no"], [
 AM_CONDITIONAL([HAVE_AVCODEC_VDA], [test "${have_avcodec_vda}" = "yes"])
 
 dnl
+dnl VideoToolbox sans avcodec
+dnl
+AC_ARG_ENABLE(videotoolbox,
+  [  --enable-videotoolbox   VideoToolbox support (default auto)])
+if test "x${enable_videotoolbox}" != "xno" &&
+  (test "${SYS}" = "darwin" || test "${enable_videotoolbox}" = "yes")
+then
+  VLC_ADD_PLUGIN([videotoolbox])
+fi
+
+dnl
 dnl  avformat demuxer/muxer plugin
 dnl
 
diff --git a/modules/codec/Makefile.am b/modules/codec/Makefile.am
index 48d8a7d..bb8a738 100644
--- a/modules/codec/Makefile.am
+++ b/modules/codec/Makefile.am
@@ -259,6 +259,13 @@ libvorbis_plugin_la_LIBADD = $(VORBIS_LIBS)
 EXTRA_LTLIBRARIES += libvorbis_plugin.la
 codec_LTLIBRARIES += $(LTLIBvorbis)
 
+libvideotoolbox_plugin_la_SOURCES = codec/h264_nal.c codec/h264_nal.h codec/videotoolbox.m
+libvideotoolbox_plugin_la_CFLAGS = $(AM_CFLAGS) $(AVFORMAT_CFLAGS) -fobjc-arc
+libvideotoolbox_plugin_la_LDFLAGS = $(AM_LDFLAGS) $(SYMBOLIC_LDFLAGS) -Wl,-framework,Foundation,-framework,VideoToolbox,-framework,CoreMedia
+libvideotoolbox_plugin_la_LIBADD = $(AVFORMAT_LIBS)
+# if HAVE_AVCODEC_VIDEOTOOLBOX
+codec_LTLIBRARIES += libvideotoolbox_plugin.la
+# endif
 
 ### FFmpeg/libav ###
 
diff --git a/modules/codec/videotoolbox.m b/modules/codec/videotoolbox.m
new file mode 100644
index 0000000..3ebcdc0
--- /dev/null
+++ b/modules/codec/videotoolbox.m
@@ -0,0 +1,657 @@
+/*****************************************************************************
+ * videotoolbox.c: VideoToolbox decoder
+ *****************************************************************************
+ * Copyright © 2014 VideoLabs SAS
+ *
+ * Authors: Felix Paul Kühne <fkuehne # videolan.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#pragma mark preamble
+
+#ifdef HAVE_CONFIG_H
+# import "config.h"
+#endif
+
+#import <vlc_common.h>
+#import <vlc_plugin.h>
+#import <vlc_codec.h>
+#import "h264_nal.h"
+
+#import <VideoToolbox/VideoToolbox.h>
+
+#import <Foundation/Foundation.h>
+#import <TargetConditionals.h>
+
+#import <avio.h>
+
+#pragma mark - module descriptor
+
+static int OpenDecoder(vlc_object_t *);
+static void CloseDecoder(vlc_object_t *);
+
+#if MAC_OS_X_VERSION_MAX_ALLOWED < 1090
+const CFStringRef kVTVideoDecoderSpecification_EnableHardwareAcceleratedVideoDecoder =
+CFSTR("EnableHardwareAcceleratedVideoDecoder");
+const CFStringRef kVTVideoDecoderSpecification_RequireHardwareAcceleratedVideoDecoder = CFSTR("RequireHardwareAcceleratedVideoDecoder");
+#endif
+
+#if !TARGET_OS_IPHONE
+#define VT_REQUIRE_HW_DEC N_("Require hardware decoder")
+#define VT_REQUIRE_HW_DEC_LONGTEXT N_("In this mode, this decoder will be used only if there is a hardware decoder available. Alternatively, it will not try the software mode, but pass on to another decoder.")
+#endif
+
+vlc_module_begin()
+set_category(CAT_INPUT)
+set_subcategory(SUBCAT_INPUT_VCODEC)
+set_description(N_("VideoToolbox video decoder"))
+set_capability("decoder",800)
+set_callbacks(OpenDecoder, CloseDecoder)
+#if !TARGET_OS_IPHONE
+add_bool("videotoolbox-require-hw-decoder", false, VT_REQUIRE_HW_DEC, VT_REQUIRE_HW_DEC_LONGTEXT, false)
+#endif
+vlc_module_end()
+
+#pragma mark - local prototypes
+
+static CFDataRef avvCCreate(decoder_t *, uint8_t *, uint32_t);
+static CFDataRef ESDSCreate(decoder_t *, uint8_t *, uint32_t);
+static picture_t *DecodeBlock(decoder_t *, block_t **);
+static void DecoderCallback(void *, void *, OSStatus, VTDecodeInfoFlags,
+                             CVImageBufferRef, CMTime, CMTime);
+void VTDictionarySetInt32(CFMutableDictionaryRef, CFStringRef, int);
+static void copy422YpCbCr8(picture_t *, CVPixelBufferRef);
+
+ at interface VTStorageObject : NSObject
+
+ at property (retain) NSMutableArray *outputFrames;
+ at property (retain) NSMutableArray *presentationTimes;
+
+ at end
+
+ at implementation VTStorageObject
+
+// implementation is created by the compiler
+
+ at end
+
+#pragma mark - decoder structure
+
+struct decoder_sys_t
+{
+    VTDecompressionSessionRef   session;
+    CMVideoFormatDescriptionRef videoFormatDescription;
+
+    VTStorageObject             *storageObject;
+};
+
+
+#pragma mark - module open and close
+
+static int OpenDecoder(vlc_object_t *p_this)
+{
+    decoder_t *p_dec = (decoder_t *)p_this;
+    OSStatus status;
+    msg_Info(p_dec, "trying VideoToolbox module");
+
+    CMVideoCodecType codec;
+    size_t i_profile = 0xFFFF, i_level = 0xFFFF;
+
+    /* check for the codec we can and want to decode */
+    switch (p_dec->fmt_in.i_codec) {
+        case VLC_CODEC_H264:
+            codec = kCMVideoCodecType_H264;
+
+            h264_get_profile_level(&p_dec->fmt_in, &i_profile, &i_level, NULL);
+            if (i_profile > 100)
+                return VLC_EGENERIC;
+
+            break;
+        case VLC_CODEC_MP4V:
+            codec = kCMVideoCodecType_MPEG4Video;
+            break;
+#if !TARGET_OS_IPHONE
+            /* there is no mp2v decoder on iOS, so bailout early */
+        case VLC_CODEC_MPGV:
+            codec = kCMVideoCodecType_MPEG2Video;
+            break;
+#endif
+#if 0
+// FIXME: missing H263 support
+        case VLC_CODEC_H263:
+            codec = kCMVideoCodecType_H263;
+            break;
+#endif
+
+        default:
+            return VLC_EGENERIC;
+    }
+
+    decoder_sys_t *p_sys;
+    p_sys = malloc(sizeof(*p_sys));
+    if (!p_sys)
+        return VLC_ENOMEM;
+    p_dec->p_sys = p_sys;
+
+    /* setup the decoder */
+    CFMutableDictionaryRef decoderConfiguration = NULL;
+    decoderConfiguration = CFDictionaryCreateMutable(kCFAllocatorDefault,
+                                                      2,
+                                                      &kCFTypeDictionaryKeyCallBacks,
+                                                      &kCFTypeDictionaryValueCallBacks);
+    CFDictionarySetValue(decoderConfiguration,
+                         CFSTR("CVImageBufferChromaLocationBottomField"),
+                         CFSTR("left"));
+    CFDictionarySetValue(decoderConfiguration,
+                         CFSTR("CVImageBufferChromaLocationTopField"),
+                         CFSTR("left"));
+    CFDictionarySetValue(decoderConfiguration,
+                         CFSTR("FullRangeVideo"),
+                         kCFBooleanFalse);
+
+    /* fetch extradata */
+    CFMutableDictionaryRef extradata_info = NULL;
+    CFDataRef extradata = NULL;
+
+    extradata_info = CFDictionaryCreateMutable(kCFAllocatorDefault,
+                                               1,
+                                               &kCFTypeDictionaryKeyCallBacks,
+                                               &kCFTypeDictionaryValueCallBacks);
+
+    if (codec == kCMVideoCodecType_H264) {
+        msg_Dbg(p_dec, "creating avvC for H264 playback");
+        extradata = avvCCreate(p_dec,
+                                (uint8_t*)p_dec->fmt_in.p_extra,
+                                p_dec->fmt_in.i_extra);
+        if (extradata)
+            CFDictionarySetValue(extradata_info, CFSTR("avcC"), extradata);
+
+        CFDictionarySetValue(decoderConfiguration,
+                             kCMFormatDescriptionExtension_SampleDescriptionExtensionAtoms,
+                             extradata_info);
+    } else if (codec == kCMVideoCodecType_MPEG4Video) {
+        msg_Dbg(p_dec, "creating esds for MPEG4 playback");
+        extradata = ESDSCreate(p_dec,
+                                (uint8_t*)p_dec->fmt_in.p_extra,
+                                p_dec->fmt_in.i_extra);
+        if (extradata)
+            CFDictionarySetValue(extradata_info, CFSTR("esds"), extradata);
+
+        CFDictionarySetValue(decoderConfiguration,
+                             kCMFormatDescriptionExtension_SampleDescriptionExtensionAtoms,
+                             extradata_info);
+    } else
+        msg_Err(p_dec, "playing something non H264, non MPEG4");
+
+
+    if (extradata)
+        CFRelease(extradata);
+    CFRelease(extradata_info);
+
+    /* pixel aspect ratio */
+    CFMutableDictionaryRef par = CFDictionaryCreateMutable(kCFAllocatorDefault,
+                                                           2,
+                                                           &kCFTypeDictionaryKeyCallBacks,
+                                                           &kCFTypeDictionaryValueCallBacks);
+    VTDictionarySetInt32(par,
+                         CFSTR("HorizontalSpacing"),
+                         p_dec->fmt_in.video.i_sar_num);
+    VTDictionarySetInt32(par,
+                         CFSTR("VerticalSpacing"),
+                         p_dec->fmt_in.video.i_sar_den);
+    CFDictionarySetValue(decoderConfiguration,
+                         CFSTR ("CVPixelAspectRatio"),
+                         par);
+    CFRelease(par);
+
+#if !TARGET_OS_IPHONE
+    /* enable HW accelerated playback, since this is optional on OS X
+     * note that the backend may still fallback on software mode if no
+     * suitable hardware is available */
+    CFDictionarySetValue(decoderConfiguration,
+                         kVTVideoDecoderSpecification_EnableHardwareAcceleratedVideoDecoder,
+                         kCFBooleanTrue);
+
+    /* on OS X, we can force VT to fail if no suitable HW decoder is available,
+     * preventing the aforementioned SW fallback */
+    if (config_GetInt(p_dec, "videotoolbox-require-hw-decoder"))
+        CFDictionarySetValue(decoderConfiguration,
+                             kVTVideoDecoderSpecification_RequireHardwareAcceleratedVideoDecoder,
+                             kCFBooleanTrue);
+#endif
+
+    /* create video format description */
+    status = CMVideoFormatDescriptionCreate(kCFAllocatorDefault,
+                                            codec,
+                                            p_dec->fmt_in.video.i_width,
+                                            p_dec->fmt_in.video.i_height,
+                                            decoderConfiguration,
+                                            &p_sys->videoFormatDescription);
+    if (status) {
+        CFRelease(decoderConfiguration);
+        msg_Err(p_dec, "video format description creation failed (%i)", status);
+        goto error;
+    }
+
+    /* setup storage */
+    p_sys->storageObject = [[VTStorageObject alloc] init];
+    p_sys->storageObject.outputFrames = [[NSMutableArray alloc] init];
+    p_sys->storageObject.presentationTimes = [[NSMutableArray alloc] init];
+
+    /* setup decoder callback record */
+    VTDecompressionOutputCallbackRecord decoderCallbackRecord;
+    decoderCallbackRecord.decompressionOutputCallback = DecoderCallback;
+    decoderCallbackRecord.decompressionOutputRefCon = p_dec;
+
+    /* create decompression session */
+    status = VTDecompressionSessionCreate(kCFAllocatorDefault,
+                                          p_sys->videoFormatDescription,
+                                          decoderConfiguration,
+                                          NULL,
+                                          &decoderCallbackRecord,
+                                          &p_sys->session);
+
+    CFRelease(decoderConfiguration);
+
+    if (status) {
+        if (status == -12913)
+            msg_Err(p_dec, "VT is not available to sandboxed apps on this OS release");
+        else if (status == -12470)
+            msg_Err(p_dec, "VT not supported on this hardware");
+        else if (status == -12471)
+            msg_Err(p_dec, "Video format not supported by VT");
+        else
+            msg_Err(p_dec, "decompression session creation failed (%i)", status);
+
+        goto error;
+    }
+
+    /* return our proper VLC internal state */
+    p_dec->fmt_out.i_cat = VIDEO_ES;
+    p_dec->fmt_out.i_codec = VLC_CODEC_NV12;
+
+    p_dec->fmt_out.video.i_width = p_dec->fmt_in.video.i_width;
+    p_dec->fmt_out.video.i_height = p_dec->fmt_in.video.i_height;
+    p_dec->b_need_packetized = true;
+
+    p_dec->pf_decode_video = DecodeBlock;
+
+    msg_Info(p_dec, "Using VideoToolbox for video decoding");
+
+    return VLC_SUCCESS;
+
+error:
+    free(p_sys);
+    return VLC_EGENERIC;
+}
+
+static void CloseDecoder(vlc_object_t *p_this)
+{
+    decoder_t *p_dec = (decoder_t *)p_this;
+    decoder_sys_t *p_sys = p_dec->p_sys;
+
+    if (p_sys->session) {
+        VTDecompressionSessionWaitForAsynchronousFrames(p_sys->session);
+        VTDecompressionSessionInvalidate(p_sys->session);
+        CFRelease(p_sys->session);
+        p_sys->session = NULL;
+    }
+
+    if (p_sys->videoFormatDescription)
+        CFRelease(p_sys->videoFormatDescription);
+
+    free(p_sys);
+}
+
+#pragma mark - helpers
+
+static CFDataRef avvCCreate(decoder_t *p_dec, uint8_t *p_buf, uint32_t i_buf_size)
+{
+    VLC_UNUSED(p_dec);
+    CFDataRef data;
+
+    /* each NAL sent to the decoder is preceded by a 4 byte header
+     * we need to change the avcC header to signal headers of 4 bytes, if needed */
+    if (i_buf_size >= 4 && (p_buf[4] & 0x03) != 0x03) {
+        uint8_t *p_fixed_buf;
+        p_fixed_buf = malloc(i_buf_size);
+        if (!p_fixed_buf)
+            return NULL;
+
+        memcpy(p_fixed_buf, p_buf, i_buf_size);
+        p_fixed_buf[4] |= 0x03;
+
+        data = CFDataCreate(kCFAllocatorDefault,
+                            p_fixed_buf,
+                            i_buf_size);
+    } else
+        data = CFDataCreate(kCFAllocatorDefault,
+                            p_buf,
+                            i_buf_size);
+
+    return data;
+}
+
+static void write_mp4_description_length(AVIOContext *context, int length)
+{
+    uint8_t byte;
+
+    for (int i = 3; i >= 0; i--) {
+        byte = (length >> (i * 7)) & 0x7F;
+        if (i != 0)
+            byte |= 0x80;
+
+        avio_w8(context, byte);
+    }
+}
+
+static CFDataRef ESDSCreate(decoder_t *p_dec, uint8_t *p_buf, uint32_t i_buf_size)
+{
+    AVIOContext *context;
+    int status;
+    uint8_t *rw_extradata;
+    int full_size = 3 + 5 +13 + 5 + i_buf_size + 3;
+    int config_size = 13 + 5 + i_buf_size;
+    int padding = 12;
+
+    status = avio_open_dyn_buf(&context);
+    if (status != noErr)
+        msg_Err(p_dec, "opening dyn buf failed %i", status);
+
+    avio_w8(context, 0);        // Version
+    avio_wb24(context, 0);      // Flags
+
+    // elementary stream description tag
+    avio_w8(context, 0x03);     // ES description tag
+    write_mp4_description_length(context, full_size);
+    avio_wb16(context, 0);      // esid
+    avio_w8(context, 0);        // stream priority (0-3)
+
+    // decoder configuration description tag
+    avio_w8(context, 0x04);
+    write_mp4_description_length(context, config_size);
+    avio_w8(context, 32);       // object type identification (32 = MPEG4)
+    avio_w8(context, 0x11);     // stream type
+    avio_wb24(context, 0);      // buffer size
+    avio_wb32(context, 0);      // max bitrate
+    avio_wb32(context, 0);      // avg bitrate
+
+    // decoder specific description tag
+    avio_w8(context, 0x05);     // dec specific info tag
+    write_mp4_description_length(context, i_buf_size);
+    avio_write(context, p_buf, i_buf_size);
+
+    // sync layer configuration description tag
+    avio_w8(context, 0x06);     // tag
+    avio_w8(context, 0x01);     // length
+    avio_w8(context, 0x02);     // no SL
+
+    rw_extradata = malloc(full_size + padding);
+    avio_close_dyn_buf(context, &rw_extradata);
+
+    CFDataRef data = CFDataCreate(kCFAllocatorDefault,
+                                  rw_extradata,
+                                  full_size + padding);
+
+    return data;
+}
+
+static CMSampleBufferRef VTSampleBufferCreate(CMFormatDescriptionRef fmt_desc,
+                                              void *buffer,
+                                              int size,
+                                              mtime_t i_pts,
+                                              mtime_t i_dts,
+                                              mtime_t i_length)
+{
+    OSStatus status;
+    CMBlockBufferRef  block_buf;
+    CMSampleBufferRef sample_buf;
+
+    CMSampleTimingInfo timeInfo;
+    CMSampleTimingInfo timeInfoArray[1];
+    timeInfo.presentationTimeStamp = CMTimeMake(i_pts, 1000000);
+    timeInfo.decodeTimeStamp = CMTimeMake(i_dts, 1000000);
+    timeInfo.duration = CMTimeMake(i_length, 1000000);
+
+    timeInfoArray[0] = timeInfo;
+
+    block_buf  = NULL;
+    sample_buf = NULL;
+
+    status = CMBlockBufferCreateWithMemoryBlock(kCFAllocatorDefault,// structureAllocator
+                                                buffer,             // memoryBlock
+                                                size,               // blockLength
+                                                kCFAllocatorNull,   // blockAllocator
+                                                NULL,               // customBlockSource
+                                                0,                  // offsetToData
+                                                size,               // dataLength
+                                                0,                  // flags
+                                                &block_buf);
+
+    if (!status) {
+        status = CMSampleBufferCreate(kCFAllocatorDefault,  // allocator
+                                      block_buf,            // dataBuffer
+                                      TRUE,                 // dataReady
+                                      0,                    // makeDataReadyCallback
+                                      0,                    // makeDataReadyRefcon
+                                      fmt_desc,             // formatDescription
+                                      1,                    // numSamples
+                                      1,                    // numSampleTimingEntries
+                                      timeInfoArray,        // sampleTimingArray
+                                      0,                    // numSampleSizeEntries
+                                      NULL,                 // sampleSizeArray
+                                      &sample_buf);
+    }
+
+    if (block_buf)
+        CFRelease(block_buf);
+
+    return sample_buf;
+}
+
+void VTDictionarySetInt32(CFMutableDictionaryRef dict, CFStringRef key, int value)
+{
+    CFNumberRef number;
+    number = CFNumberCreate(NULL, kCFNumberSInt32Type, &value);
+    CFDictionarySetValue(dict, key, number);
+    CFRelease(number);
+}
+
+static void copy422YpCbCr8(picture_t *p_pic, CVPixelBufferRef buffer)
+{
+    int i_dst_stride, i_src_stride;
+    uint8_t *p_dst, *p_src;
+
+    CVPixelBufferLockBaseAddress( buffer, 0 );
+
+    for (int i_plane = 0; i_plane < p_pic->i_planes; i_plane++) {
+        p_dst = p_pic->p[i_plane].p_pixels;
+        p_src = CVPixelBufferGetBaseAddressOfPlane(buffer, i_plane);
+        i_dst_stride  = p_pic->p[i_plane].i_pitch;
+        i_src_stride  = CVPixelBufferGetBytesPerRowOfPlane(buffer, i_plane);
+
+        for (int i_line = 0; i_line < p_pic->p[i_plane].i_visible_lines ; i_line++) {
+            memcpy(p_dst, p_src, i_src_stride);
+
+            p_src += i_src_stride;
+            p_dst += i_dst_stride;
+        }
+    }
+
+    CVPixelBufferUnlockBaseAddress(buffer, 0);
+}
+
+#pragma mark - actual decoding
+
+static picture_t *DecodeBlock(decoder_t *p_dec, block_t **pp_block)
+{
+    decoder_sys_t *p_sys = p_dec->p_sys;
+    block_t *p_block;
+    VTDecodeFrameFlags decoderFlags = 0;
+    VTDecodeInfoFlags flagOut;
+    OSStatus status;
+
+    if (!pp_block)
+        return NULL;
+
+    if (!p_sys->session)
+        return NULL;
+
+    p_block = *pp_block;
+
+    if (likely(p_block)) {
+        if (p_block->i_dts < VLC_TS_INVALID || p_block->i_flags&(BLOCK_FLAG_DISCONTINUITY|BLOCK_FLAG_CORRUPTED)) {
+            block_Release(p_block);
+            goto skip;
+        }
+
+        /* feed to vt */
+        if (likely(p_block->i_buffer)) {
+            CMSampleBufferRef sampleBuffer;
+            sampleBuffer = VTSampleBufferCreate(p_sys->videoFormatDescription,
+                                                p_block->p_buffer,
+                                                p_block->i_buffer,
+                                                p_block->i_pts,
+                                                p_block->i_dts,
+                                                p_block->i_length);
+            if (sampleBuffer) {
+                decoderFlags = kVTDecodeFrame_EnableAsynchronousDecompression;
+
+                status = VTDecompressionSessionDecodeFrame(p_sys->session,
+                                                           sampleBuffer,
+                                                           decoderFlags,
+                                                           NULL, // sourceFrameRefCon
+                                                           &flagOut); // infoFlagsOut
+                if (status != noErr) {
+                    if (status == kCVReturnInvalidSize)
+                        msg_Err(p_dec, "decoder failure: invalid block size");
+                    else if (status == -8969 || status == -12909)
+                        msg_Err(p_dec, "decoder failure: bad data");
+                    else
+                        msg_Info(p_dec, "decoding frame failed (%i)", status);
+                }
+
+                CFRelease(sampleBuffer);
+            }
+        }
+
+        block_Release(p_block);
+    }
+
+skip:
+
+    *pp_block = NULL;
+
+    status = VTDecompressionSessionWaitForAsynchronousFrames(p_sys->session);
+    if (status != noErr)
+        msg_Dbg(p_dec, "waiting for async frames failed (%i)", status);
+
+    if ([p_sys->storageObject.outputFrames count] && [p_sys->storageObject.presentationTimes count]) {
+        CVImageBufferRef imageBuffer = NULL;
+        NSNumber *framePTS = nil;
+        id imageBufferObject = nil;
+        picture_t *p_pic = NULL;
+
+        @synchronized(p_sys->storageObject) {
+            framePTS = [p_sys->storageObject.presentationTimes firstObject];
+            imageBufferObject = [p_sys->storageObject.outputFrames firstObject];
+            imageBuffer = (__bridge CVImageBufferRef)imageBufferObject;
+
+            if (imageBuffer != NULL) {
+                if (CVPixelBufferGetDataSize(imageBuffer) > 0) {
+                    p_pic = decoder_NewPicture(p_dec);
+
+                    if (!p_pic)
+                        return NULL;
+
+                    copy422YpCbCr8(p_pic, imageBuffer);
+
+                    p_pic->date = framePTS.longLongValue;
+
+                    if (imageBufferObject)
+                        [p_sys->storageObject.outputFrames removeObjectAtIndex:0];
+
+                    if (framePTS)
+                        [p_sys->storageObject.presentationTimes removeObjectAtIndex:0];
+                }
+            }
+        }
+        return p_pic;
+    }
+
+    return NULL;
+}
+
+static void DecoderCallback(void *decompressionOutputRefCon,
+                             void *sourceFrameRefCon,
+                             OSStatus status,
+                             VTDecodeInfoFlags infoFlags,
+                             CVImageBufferRef imageBuffer,
+                             CMTime pts,
+                             CMTime duration)
+{
+    VLC_UNUSED(sourceFrameRefCon);
+    VLC_UNUSED(duration);
+    decoder_t *p_dec = (decoder_t *)decompressionOutputRefCon;
+    decoder_sys_t *p_sys = p_dec->p_sys;
+
+    if (status != noErr) {
+        msg_Warn(p_dec, "decoding of a frame failed (%i, %u)", status, (unsigned int) infoFlags);
+        return;
+    }
+
+    if (imageBuffer == NULL)
+        return;
+
+    if (infoFlags & kVTDecodeInfo_FrameDropped) {
+        msg_Dbg(p_dec, "dropping frame");
+        CFRelease(imageBuffer);
+        return;
+    }
+
+    NSNumber *framePTS = nil;
+
+    if (CMTIME_IS_VALID(pts))
+        framePTS = [NSNumber numberWithLongLong:pts.value];
+    else {
+        msg_Dbg(p_dec, "invalid timestamp, dropping frame");
+        CFRelease(imageBuffer);
+        return;
+    }
+
+    if (framePTS) {
+        @synchronized(p_sys->storageObject) {
+            id imageBufferObject = (__bridge id)imageBuffer;
+            BOOL shouldStop = YES;
+            NSInteger insertionIndex = [p_sys->storageObject.presentationTimes count] - 1;
+            while (insertionIndex >= 0 && shouldStop == NO) {
+                NSNumber *aNumber = p_sys->storageObject.presentationTimes[insertionIndex];
+                if ([aNumber longLongValue] <= [framePTS longLongValue]) {
+                    shouldStop = YES;
+                    break;
+                }
+                insertionIndex--;
+            }
+            if (insertionIndex + 1 == [p_sys->storageObject.presentationTimes count]) {
+                [p_sys->storageObject.presentationTimes addObject:framePTS];
+                [p_sys->storageObject.outputFrames addObject:imageBufferObject];
+            } else {
+                [p_sys->storageObject.presentationTimes insertObject:framePTS atIndex:insertionIndex + 1];
+                [p_sys->storageObject.outputFrames insertObject:framePTS atIndex:insertionIndex + 1];
+            }
+        }
+    }
+}
-- 
1.8.5.2 (Apple Git-48)




More information about the vlc-devel mailing list