X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavcodec%2Fvda_h264.c;h=c7f6a7432e9f4eb411598a8f1eb753984ad102ca;hb=c442190a6bfd8036f6c32b78e1e96ff3b830f8f0;hp=2d2d228745102be69e7c992e0b7fcb07c44ec3ca;hpb=dc87ac55ab81b7242228cbfba997f627bbeb7349;p=ffmpeg diff --git a/libavcodec/vda_h264.c b/libavcodec/vda_h264.c index 2d2d2287451..c7f6a7432e9 100644 --- a/libavcodec/vda_h264.c +++ b/libavcodec/vda_h264.c @@ -26,102 +26,22 @@ #include "libavutil/avutil.h" #include "h264.h" +#include "internal.h" #include "vda.h" +#include "vda_internal.h" -#if FF_API_VDA_ASYNC -#include +typedef struct VDAContext { + // The current bitstream buffer. + uint8_t *bitstream; -/* helper to create a dictionary according to the given pts */ -static CFDictionaryRef vda_dictionary_with_pts(int64_t i_pts) -{ - CFStringRef key = CFSTR("FF_VDA_DECODER_PTS_KEY"); - CFNumberRef value = CFNumberCreate(kCFAllocatorDefault, - kCFNumberSInt64Type, &i_pts); - CFDictionaryRef user_info = CFDictionaryCreate(kCFAllocatorDefault, - (const void **)&key, - (const void **)&value, - 1, - &kCFTypeDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks); - CFRelease(value); - return user_info; -} - -/* helper to retrieve the pts from the given dictionary */ -static int64_t vda_pts_from_dictionary(CFDictionaryRef user_info) -{ - CFNumberRef pts; - int64_t outValue = 0; - - if (!user_info) - return 0; - - pts = CFDictionaryGetValue(user_info, CFSTR("FF_VDA_DECODER_PTS_KEY")); - - if (pts) - CFNumberGetValue(pts, kCFNumberSInt64Type, &outValue); - - return outValue; -} - -/* Remove and release all frames from the queue. */ -static void vda_clear_queue(struct vda_context *vda_ctx) -{ - vda_frame *top_frame; - - pthread_mutex_lock(&vda_ctx->queue_mutex); - - while (vda_ctx->queue) { - top_frame = vda_ctx->queue; - vda_ctx->queue = top_frame->next_frame; - ff_vda_release_vda_frame(top_frame); - } - - pthread_mutex_unlock(&vda_ctx->queue_mutex); -} - -static int vda_decoder_decode(struct vda_context *vda_ctx, - uint8_t *bitstream, - int bitstream_size, - int64_t frame_pts) -{ - OSStatus status = kVDADecoderNoErr; - CFDictionaryRef user_info; - CFDataRef coded_frame; - - coded_frame = CFDataCreate(kCFAllocatorDefault, bitstream, bitstream_size); - user_info = vda_dictionary_with_pts(frame_pts); - status = VDADecoderDecode(vda_ctx->decoder, 0, coded_frame, user_info); - - CFRelease(user_info); - CFRelease(coded_frame); - - return status; -} - -vda_frame *ff_vda_queue_pop(struct vda_context *vda_ctx) -{ - vda_frame *top_frame; + // The current size of the bitstream. + int bitstream_size; - if (!vda_ctx->queue) - return NULL; + // The reference size used for fast reallocation. + int allocated_size; - pthread_mutex_lock(&vda_ctx->queue_mutex); - top_frame = vda_ctx->queue; - vda_ctx->queue = top_frame->next_frame; - pthread_mutex_unlock(&vda_ctx->queue_mutex); - - return top_frame; -} - -void ff_vda_release_vda_frame(vda_frame *frame) -{ - if (frame) { - CVPixelBufferRelease(frame->cv_buffer); - av_freep(&frame); - } -} -#endif + CVImageBufferRef frame; +} VDAContext; /* Decoder callback that adds the VDA frame to the queue in display order. */ static void vda_decoder_callback(void *vda_hw_ctx, @@ -138,53 +58,18 @@ static void vda_decoder_callback(void *vda_hw_ctx, if (vda_ctx->cv_pix_fmt_type != CVPixelBufferGetPixelFormatType(image_buffer)) return; - if (vda_ctx->use_sync_decoding) { - vda_ctx->cv_buffer = CVPixelBufferRetain(image_buffer); - } else { - vda_frame *new_frame; - vda_frame *queue_walker; - - if (!(new_frame = av_mallocz(sizeof(vda_frame)))) - return; - new_frame->next_frame = NULL; - new_frame->cv_buffer = CVPixelBufferRetain(image_buffer); - new_frame->pts = vda_pts_from_dictionary(user_info); - - pthread_mutex_lock(&vda_ctx->queue_mutex); - - queue_walker = vda_ctx->queue; - - if (!queue_walker || new_frame->pts < queue_walker->pts) { - /* we have an empty queue, or this frame earlier than the current queue head */ - new_frame->next_frame = queue_walker; - vda_ctx->queue = new_frame; - } else { - /* walk the queue and insert this frame where it belongs in display order */ - vda_frame *next_frame; - while (1) { - next_frame = queue_walker->next_frame; - if (!next_frame || new_frame->pts < next_frame->pts) { - new_frame->next_frame = next_frame; - queue_walker->next_frame = new_frame; - break; - } - queue_walker = next_frame; - } - } - - pthread_mutex_unlock(&vda_ctx->queue_mutex); - } + vda_ctx->cv_buffer = CVPixelBufferRetain(image_buffer); } -static int vda_sync_decode(struct vda_context *vda_ctx) +static int vda_sync_decode(VDAContext *ctx, struct vda_context *vda_ctx) { OSStatus status; CFDataRef coded_frame; uint32_t flush_flags = 1 << 0; ///< kVDADecoderFlush_emitFrames coded_frame = CFDataCreate(kCFAllocatorDefault, - vda_ctx->priv_bitstream, - vda_ctx->priv_bitstream_size); + ctx->bitstream, + ctx->bitstream_size); status = VDADecoderDecode(vda_ctx->decoder, 0, coded_frame, NULL); @@ -197,64 +82,61 @@ static int vda_sync_decode(struct vda_context *vda_ctx) } -static int start_frame(AVCodecContext *avctx, - av_unused const uint8_t *buffer, - av_unused uint32_t size) +static int vda_old_h264_start_frame(AVCodecContext *avctx, + av_unused const uint8_t *buffer, + av_unused uint32_t size) { + VDAContext *vda = avctx->internal->hwaccel_priv_data; struct vda_context *vda_ctx = avctx->hwaccel_context; if (!vda_ctx->decoder) return -1; - vda_ctx->priv_bitstream_size = 0; + vda->bitstream_size = 0; return 0; } -static int decode_slice(AVCodecContext *avctx, - const uint8_t *buffer, - uint32_t size) +static int vda_old_h264_decode_slice(AVCodecContext *avctx, + const uint8_t *buffer, + uint32_t size) { + VDAContext *vda = avctx->internal->hwaccel_priv_data; struct vda_context *vda_ctx = avctx->hwaccel_context; void *tmp; if (!vda_ctx->decoder) return -1; - tmp = av_fast_realloc(vda_ctx->priv_bitstream, - &vda_ctx->priv_allocated_size, - vda_ctx->priv_bitstream_size + size + 4); + tmp = av_fast_realloc(vda->bitstream, + &vda->allocated_size, + vda->bitstream_size + size + 4); if (!tmp) return AVERROR(ENOMEM); - vda_ctx->priv_bitstream = tmp; + vda->bitstream = tmp; - AV_WB32(vda_ctx->priv_bitstream + vda_ctx->priv_bitstream_size, size); - memcpy(vda_ctx->priv_bitstream + vda_ctx->priv_bitstream_size + 4, buffer, size); + AV_WB32(vda->bitstream + vda->bitstream_size, size); + memcpy(vda->bitstream + vda->bitstream_size + 4, buffer, size); - vda_ctx->priv_bitstream_size += size + 4; + vda->bitstream_size += size + 4; return 0; } -static int 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; struct vda_context *vda_ctx = avctx->hwaccel_context; - AVFrame *frame = &h->s.current_picture_ptr->f; + AVFrame *frame = &h->cur_pic_ptr->f; int status; - if (!vda_ctx->decoder || !vda_ctx->priv_bitstream) + if (!vda_ctx->decoder || !vda->bitstream) return -1; - if (vda_ctx->use_sync_decoding) { - status = vda_sync_decode(vda_ctx); - frame->data[3] = (void*)vda_ctx->cv_buffer; - } else { - status = vda_decoder_decode(vda_ctx, vda_ctx->priv_bitstream, - vda_ctx->priv_bitstream_size, - frame->reordered_opaque); - } + status = vda_sync_decode(vda, vda_ctx); + frame->data[3] = (void*)vda_ctx->cv_buffer; if (status) av_log(avctx, AV_LOG_ERROR, "Failed to decode frame (%d)\n", status); @@ -276,12 +158,8 @@ int ff_vda_create_decoder(struct vda_context *vda_ctx, CFMutableDictionaryRef io_surface_properties; CFNumberRef cv_pix_fmt; -#if FF_API_VDA_ASYNC - pthread_mutex_init(&vda_ctx->queue_mutex, NULL); -#endif - /* Each VCL NAL in the bistream sent to the decoder - * is preceeded by a 4 bytes length header. + * is preceded by a 4 bytes length header. * Change the avcC atom header if needed, to signal headers of 4 bytes. */ if (extradata_size >= 4 && (extradata[4] & 0x03) != 0x03) { uint8_t *rw_extradata; @@ -334,7 +212,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); @@ -357,22 +235,262 @@ int ff_vda_destroy_decoder(struct vda_context *vda_ctx) if (vda_ctx->decoder) status = VDADecoderDestroy(vda_ctx->decoder); -#if FF_API_VDA_ASYNC - vda_clear_queue(vda_ctx); - pthread_mutex_destroy(&vda_ctx->queue_mutex); -#endif + return status; +} - av_freep(&vda_ctx->priv_bitstream); +static int vda_h264_uninit(AVCodecContext *avctx) +{ + VDAContext *vda = avctx->internal->hwaccel_priv_data; + av_freep(&vda->bitstream); + if (vda->frame) + CVPixelBufferRelease(vda->frame); + return 0; +} - return status; +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 = PIX_FMT_VDA_VLD, - .start_frame = start_frame, - .decode_slice = decode_slice, - .end_frame = end_frame, + .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, + .uninit = vda_h264_uninit, + .priv_data_size = sizeof(VDAContext), };