diff options
author | Michael Niedermayer <michaelni@gmx.at> | 2014-05-12 03:57:02 +0400 |
---|---|---|
committer | Michael Niedermayer <michaelni@gmx.at> | 2014-05-12 03:57:02 +0400 |
commit | 5449239a031e657cfe364521d2b6d58c0ef0e7f0 (patch) | |
tree | c7293a1078cf2840bbe3515e6ab2d56f7f64ee9b /libavcodec/vda_h264.c | |
parent | c6a683bf2291344238cb1fc2bd84c91fa1acd3bc (diff) | |
parent | 67afcefb35932b420998f6f3fda46c7c85848a3f (diff) |
Merge commit '67afcefb35932b420998f6f3fda46c7c85848a3f'
* commit '67afcefb35932b420998f6f3fda46c7c85848a3f':
lavc: Add new VDA hwaccel
Conflicts:
configure
libavcodec/vda.h
libavcodec/vda_h264.c
Merged-by: Michael Niedermayer <michaelni@gmx.at>
Diffstat (limited to 'libavcodec/vda_h264.c')
-rw-r--r-- | libavcodec/vda_h264.c | 257 |
1 files changed, 250 insertions, 7 deletions
diff --git a/libavcodec/vda_h264.c b/libavcodec/vda_h264.c index 745b3da734..2bb45c317e 100644 --- a/libavcodec/vda_h264.c +++ b/libavcodec/vda_h264.c @@ -31,6 +31,8 @@ struct vda_buffer { CVPixelBufferRef cv_buffer; }; +#include "internal.h" +#include "vda_internal.h" typedef struct VDAContext { // The current bitstream buffer. @@ -41,6 +43,8 @@ typedef struct VDAContext { // The reference size used for fast reallocation. int allocated_size; + + CVImageBufferRef frame; } VDAContext; /* Decoder callback that adds the vda frame to the queue in display order. */ @@ -85,7 +89,7 @@ static int vda_sync_decode(VDAContext *ctx, struct vda_context *vda_ctx) } -static int vda_h264_start_frame(AVCodecContext *avctx, +static int vda_old_h264_start_frame(AVCodecContext *avctx, av_unused const uint8_t *buffer, av_unused uint32_t size) { @@ -100,7 +104,7 @@ static int vda_h264_start_frame(AVCodecContext *avctx, return 0; } -static int vda_h264_decode_slice(AVCodecContext *avctx, +static int vda_old_h264_decode_slice(AVCodecContext *avctx, const uint8_t *buffer, uint32_t size) { @@ -134,7 +138,7 @@ static void vda_h264_release_buffer(void *opaque, uint8_t *data) av_free(context); } -static int vda_h264_end_frame(AVCodecContext *avctx) +static int vda_old_h264_end_frame(AVCodecContext *avctx) { H264Context *h = avctx->priv_data; VDAContext *vda = avctx->internal->hwaccel_priv_data; @@ -241,7 +245,7 @@ int ff_vda_create_decoder(struct vda_context *vda_ctx, status = VDADecoderCreate(config_info, buffer_attributes, - vda_decoder_callback, + (VDADecoderOutputCallback *)vda_decoder_callback, vda_ctx, &vda_ctx->decoder); @@ -267,17 +271,256 @@ int ff_vda_destroy_decoder(struct vda_context *vda_ctx) return status; } -static void vda_h264_uninit(AVCodecContext *avctx) +static int vda_h264_uninit(AVCodecContext *avctx) { - VDAContext *vda = avctx->internal->priv_data; + VDAContext *vda = avctx->internal->hwaccel_priv_data; av_freep(&vda->bitstream); + if (vda->frame) + CVPixelBufferRelease(vda->frame); + return 0; } -AVHWAccel ff_h264_vda_hwaccel = { +AVHWAccel ff_h264_vda_old_hwaccel = { .name = "h264_vda", .type = AVMEDIA_TYPE_VIDEO, .id = AV_CODEC_ID_H264, .pix_fmt = AV_PIX_FMT_VDA_VLD, + .start_frame = vda_old_h264_start_frame, + .decode_slice = vda_old_h264_decode_slice, + .end_frame = vda_old_h264_end_frame, + .uninit = vda_h264_uninit, + .priv_data_size = sizeof(VDAContext), +}; + +void ff_vda_output_callback(void *opaque, + CFDictionaryRef user_info, + OSStatus status, + uint32_t infoFlags, + CVImageBufferRef image_buffer) +{ + AVCodecContext *ctx = opaque; + VDAContext *vda = ctx->internal->hwaccel_priv_data; + + + if (vda->frame) { + CVPixelBufferRelease(vda->frame); + vda->frame = NULL; + } + + if (!image_buffer) + return; + + vda->frame = CVPixelBufferRetain(image_buffer); +} + +static int vda_h264_start_frame(AVCodecContext *avctx, + const uint8_t *buffer, + uint32_t size) +{ + VDAContext *vda = avctx->internal->hwaccel_priv_data; + + vda->bitstream_size = 0; + + return 0; +} + +static int vda_h264_decode_slice(AVCodecContext *avctx, + const uint8_t *buffer, + uint32_t size) +{ + VDAContext *vda = avctx->internal->hwaccel_priv_data; + void *tmp; + + tmp = av_fast_realloc(vda->bitstream, + &vda->allocated_size, + vda->bitstream_size + size + 4); + if (!tmp) + return AVERROR(ENOMEM); + + vda->bitstream = tmp; + + AV_WB32(vda->bitstream + vda->bitstream_size, size); + memcpy(vda->bitstream + vda->bitstream_size + 4, buffer, size); + + vda->bitstream_size += size + 4; + + return 0; +} + +static void release_buffer(void *opaque, uint8_t *data) +{ + CVImageBufferRef frame = (CVImageBufferRef)data; + CVPixelBufferRelease(frame); +} + +static int vda_h264_end_frame(AVCodecContext *avctx) +{ + H264Context *h = avctx->priv_data; + VDAContext *vda = avctx->internal->hwaccel_priv_data; + AVVDAContext *vda_ctx = avctx->hwaccel_context; + AVFrame *frame = &h->cur_pic_ptr->f; + uint32_t flush_flags = 1 << 0; ///< kVDADecoderFlush_emitFrames + CFDataRef coded_frame; + OSStatus status; + + if (!vda->bitstream_size) + return AVERROR_INVALIDDATA; + + + coded_frame = CFDataCreate(kCFAllocatorDefault, + vda->bitstream, + vda->bitstream_size); + + status = VDADecoderDecode(vda_ctx->decoder, 0, coded_frame, NULL); + + if (status == kVDADecoderNoErr) + status = VDADecoderFlush(vda_ctx->decoder, flush_flags); + + CFRelease(coded_frame); + + if (status != kVDADecoderNoErr) { + av_log(avctx, AV_LOG_ERROR, "Failed to decode frame (%d)\n", status); + return AVERROR_UNKNOWN; + } + + if (vda->frame) { + av_buffer_unref(&frame->buf[0]); + + frame->buf[0] = av_buffer_create((uint8_t*)vda->frame, + sizeof(vda->frame), + release_buffer, NULL, + AV_BUFFER_FLAG_READONLY); + if (!frame->buf) + return AVERROR(ENOMEM); + + frame->data[3] = (uint8_t*)vda->frame; + vda->frame = NULL; + } + + return 0; +} + +int ff_vda_default_init(AVCodecContext *avctx) +{ + AVVDAContext *vda_ctx = avctx->hwaccel_context; + OSStatus status = kVDADecoderNoErr; + CFNumberRef height; + CFNumberRef width; + CFNumberRef format; + CFDataRef avc_data; + CFMutableDictionaryRef config_info; + CFMutableDictionaryRef buffer_attributes; + CFMutableDictionaryRef io_surface_properties; + CFNumberRef cv_pix_fmt; + int32_t fmt = 'avc1', pix_fmt = kCVPixelFormatType_422YpCbCr8; + + // kCVPixelFormatType_420YpCbCr8Planar; + + /* Each VCL NAL in the bistream sent to the decoder + * is preceded by a 4 bytes length header. + * Change the avcC atom header if needed, to signal headers of 4 bytes. */ + if (avctx->extradata_size >= 4 && (avctx->extradata[4] & 0x03) != 0x03) { + uint8_t *rw_extradata; + + if (!(rw_extradata = av_malloc(avctx->extradata_size))) + return AVERROR(ENOMEM); + + memcpy(rw_extradata, avctx->extradata, avctx->extradata_size); + + rw_extradata[4] |= 0x03; + + avc_data = CFDataCreate(kCFAllocatorDefault, rw_extradata, avctx->extradata_size); + + av_freep(&rw_extradata); + } else { + avc_data = CFDataCreate(kCFAllocatorDefault, + avctx->extradata, avctx->extradata_size); + } + + config_info = CFDictionaryCreateMutable(kCFAllocatorDefault, + 4, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + + height = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &avctx->height); + width = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &avctx->width); + format = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &fmt); + CFDictionarySetValue(config_info, kVDADecoderConfiguration_Height, height); + CFDictionarySetValue(config_info, kVDADecoderConfiguration_Width, width); + CFDictionarySetValue(config_info, kVDADecoderConfiguration_avcCData, avc_data); + CFDictionarySetValue(config_info, kVDADecoderConfiguration_SourceFormat, format); + + buffer_attributes = CFDictionaryCreateMutable(kCFAllocatorDefault, + 2, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + io_surface_properties = CFDictionaryCreateMutable(kCFAllocatorDefault, + 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + cv_pix_fmt = CFNumberCreate(kCFAllocatorDefault, + kCFNumberSInt32Type, + &pix_fmt); + + CFDictionarySetValue(buffer_attributes, + kCVPixelBufferPixelFormatTypeKey, + cv_pix_fmt); + CFDictionarySetValue(buffer_attributes, + kCVPixelBufferIOSurfacePropertiesKey, + io_surface_properties); + + status = VDADecoderCreate(config_info, + buffer_attributes, + (VDADecoderOutputCallback *)ff_vda_output_callback, + avctx, + &vda_ctx->decoder); + + CFRelease(format); + CFRelease(height); + CFRelease(width); + CFRelease(avc_data); + CFRelease(config_info); + CFRelease(cv_pix_fmt); + CFRelease(io_surface_properties); + CFRelease(buffer_attributes); + + if (status != kVDADecoderNoErr) { + av_log(avctx, AV_LOG_ERROR, "Cannot initialize VDA %d\n", status); + } + + switch (status) { + case kVDADecoderHardwareNotSupportedErr: + case kVDADecoderFormatNotSupportedErr: + return AVERROR(ENOSYS); + case kVDADecoderConfigurationError: + return AVERROR(EINVAL); + case kVDADecoderDecoderFailedErr: + return AVERROR_INVALIDDATA; + case kVDADecoderNoErr: + return 0; + default: + return AVERROR_UNKNOWN; + } +} + +static int vda_h264_alloc_frame(AVCodecContext *avctx, AVFrame *frame) +{ + frame->width = avctx->width; + frame->height = avctx->height; + frame->format = avctx->pix_fmt; + frame->buf[0] = av_buffer_alloc(1); + + if (!frame->buf[0]) + return AVERROR(ENOMEM); + return 0; +} + +AVHWAccel ff_h264_vda_hwaccel = { + .name = "h264_vda", + .type = AVMEDIA_TYPE_VIDEO, + .id = AV_CODEC_ID_H264, + .pix_fmt = AV_PIX_FMT_VDA, + .alloc_frame = vda_h264_alloc_frame, .start_frame = vda_h264_start_frame, .decode_slice = vda_h264_decode_slice, .end_frame = vda_h264_end_frame, |