diff options
author | wm4 <nfxjfg@googlemail.com> | 2017-05-15 11:27:24 +0200 |
---|---|---|
committer | wm4 <nfxjfg@googlemail.com> | 2017-05-15 11:30:36 +0200 |
commit | 532b23f079b52f4789be1f20ce232286ce4ffa13 (patch) | |
tree | 2d826fd0046a1c4f2feeb072971292348812b93b /libavcodec | |
parent | 2c6179aa829e6f50eea6faf47b2b6efd7650a41d (diff) | |
download | ffmpeg-532b23f079b52f4789be1f20ce232286ce4ffa13.tar.gz |
videotoolbox: add hwcontext support
This adds tons of code for no other benefit than making VideoToolbox
support conform with the new hwaccel API (using hw_device_ctx and
hw_frames_ctx).
Since VideoToolbox decoding does not actually require the user to
allocate frames, the new code does mostly nothing.
One benefit is that ffmpeg_videotoolbox.c can be dropped once generic
hwaccel support for ffmpeg.c is merged from Libav.
Does not consider VDA or VideoToolbox encoding.
Fun fact: the frame transfer functions are copied from vaapi, as the
mapping makes copying generic boilerplate. Mapping itself is not
exported by the VT code, because I don't know how to test.
Diffstat (limited to 'libavcodec')
-rw-r--r-- | libavcodec/vda_vt_internal.h | 7 | ||||
-rw-r--r-- | libavcodec/version.h | 4 | ||||
-rw-r--r-- | libavcodec/videotoolbox.c | 180 |
3 files changed, 179 insertions, 12 deletions
diff --git a/libavcodec/vda_vt_internal.h b/libavcodec/vda_vt_internal.h index 9ff63ccc52..e55a813899 100644 --- a/libavcodec/vda_vt_internal.h +++ b/libavcodec/vda_vt_internal.h @@ -40,6 +40,13 @@ typedef struct VTContext { // The core video buffer CVImageBufferRef frame; + + // Current dummy frames context (depends on exact CVImageBufferRef params). + struct AVBufferRef *cached_hw_frames_ctx; + + // Non-NULL if the new hwaccel API is used. This is only a separate struct + // to ease compatibility with the old API. + struct AVVideotoolboxContext *vt_ctx; } VTContext; int ff_videotoolbox_alloc_frame(AVCodecContext *avctx, AVFrame *frame); diff --git a/libavcodec/version.h b/libavcodec/version.h index f0b74fc3b4..a26e93974c 100644 --- a/libavcodec/version.h +++ b/libavcodec/version.h @@ -28,8 +28,8 @@ #include "libavutil/version.h" #define LIBAVCODEC_VERSION_MAJOR 57 -#define LIBAVCODEC_VERSION_MINOR 95 -#define LIBAVCODEC_VERSION_MICRO 101 +#define LIBAVCODEC_VERSION_MINOR 96 +#define LIBAVCODEC_VERSION_MICRO 100 #define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \ LIBAVCODEC_VERSION_MINOR, \ diff --git a/libavcodec/videotoolbox.c b/libavcodec/videotoolbox.c index 67adad53ed..d36a33efcd 100644 --- a/libavcodec/videotoolbox.c +++ b/libavcodec/videotoolbox.c @@ -23,11 +23,13 @@ #include "config.h" #if CONFIG_VIDEOTOOLBOX # include "videotoolbox.h" +# include "libavutil/hwcontext_videotoolbox.h" #else # include "vda.h" #endif #include "vda_vt_internal.h" #include "libavutil/avutil.h" +#include "libavutil/hwcontext.h" #include "bytestream.h" #include "h264dec.h" #include "mpegvideo.h" @@ -188,6 +190,73 @@ int ff_videotoolbox_uninit(AVCodecContext *avctx) } #if CONFIG_VIDEOTOOLBOX +// Return the AVVideotoolboxContext that matters currently. Where it comes from +// depends on the API used. +static AVVideotoolboxContext *videotoolbox_get_context(AVCodecContext *avctx) +{ + // Somewhat tricky because the user can call av_videotoolbox_default_free() + // at any time, even when the codec is closed. + if (avctx->internal && avctx->internal->hwaccel_priv_data) { + VTContext *vtctx = avctx->internal->hwaccel_priv_data; + if (vtctx->vt_ctx) + return vtctx->vt_ctx; + } + return avctx->hwaccel_context; +} + +static int videotoolbox_buffer_create(AVCodecContext *avctx, AVFrame *frame) +{ + VTContext *vtctx = avctx->internal->hwaccel_priv_data; + CVPixelBufferRef pixbuf = (CVPixelBufferRef)vtctx->frame; + OSType pixel_format = CVPixelBufferGetPixelFormatType(pixbuf); + enum AVPixelFormat sw_format = av_map_videotoolbox_format_to_pixfmt(pixel_format); + int width = CVPixelBufferGetWidth(pixbuf); + int height = CVPixelBufferGetHeight(pixbuf); + AVHWFramesContext *cached_frames; + int ret; + + ret = ff_videotoolbox_buffer_create(vtctx, frame); + if (ret < 0) + return ret; + + // Old API code path. + if (!vtctx->cached_hw_frames_ctx) + return 0; + + cached_frames = (AVHWFramesContext*)vtctx->cached_hw_frames_ctx->data; + + if (cached_frames->sw_format != sw_format || + cached_frames->width != width || + cached_frames->height != height) { + AVBufferRef *hw_frames_ctx = av_hwframe_ctx_alloc(cached_frames->device_ref); + AVHWFramesContext *hw_frames; + if (!hw_frames_ctx) + return AVERROR(ENOMEM); + + hw_frames = (AVHWFramesContext*)hw_frames_ctx->data; + hw_frames->format = cached_frames->format; + hw_frames->sw_format = sw_format; + hw_frames->width = width; + hw_frames->height = height; + + ret = av_hwframe_ctx_init(hw_frames_ctx); + if (ret < 0) { + av_buffer_unref(&hw_frames_ctx); + return ret; + } + + av_buffer_unref(&vtctx->cached_hw_frames_ctx); + vtctx->cached_hw_frames_ctx = hw_frames_ctx; + } + + av_assert0(!frame->hw_frames_ctx); + frame->hw_frames_ctx = av_buffer_ref(vtctx->cached_hw_frames_ctx); + if (!frame->hw_frames_ctx) + return AVERROR(ENOMEM); + + return 0; +} + static void videotoolbox_write_mp4_descr_length(PutByteContext *pb, int length) { int i; @@ -323,7 +392,7 @@ static OSStatus videotoolbox_session_decode_frame(AVCodecContext *avctx) { OSStatus status; CMSampleBufferRef sample_buf; - AVVideotoolboxContext *videotoolbox = avctx->hwaccel_context; + AVVideotoolboxContext *videotoolbox = videotoolbox_get_context(avctx); VTContext *vtctx = avctx->internal->hwaccel_priv_data; sample_buf = videotoolbox_sample_buffer_create(videotoolbox->cm_fmt_desc, @@ -349,7 +418,7 @@ static OSStatus videotoolbox_session_decode_frame(AVCodecContext *avctx) static int videotoolbox_common_end_frame(AVCodecContext *avctx, AVFrame *frame) { int status; - AVVideotoolboxContext *videotoolbox = avctx->hwaccel_context; + AVVideotoolboxContext *videotoolbox = videotoolbox_get_context(avctx); VTContext *vtctx = avctx->internal->hwaccel_priv_data; if (!videotoolbox->session || !vtctx->bitstream) @@ -365,7 +434,7 @@ static int videotoolbox_common_end_frame(AVCodecContext *avctx, AVFrame *frame) if (!vtctx->frame) return AVERROR_UNKNOWN; - return ff_videotoolbox_buffer_create(vtctx, frame); + return videotoolbox_buffer_create(avctx, frame); } static int videotoolbox_h264_end_frame(AVCodecContext *avctx) @@ -513,7 +582,7 @@ static CMVideoFormatDescriptionRef videotoolbox_format_desc_create(CMVideoCodecT static int videotoolbox_default_init(AVCodecContext *avctx) { - AVVideotoolboxContext *videotoolbox = avctx->hwaccel_context; + AVVideotoolboxContext *videotoolbox = videotoolbox_get_context(avctx); OSStatus status; VTDecompressionOutputCallbackRecord decoder_cb; CFDictionaryRef decoder_spec; @@ -594,7 +663,7 @@ static int videotoolbox_default_init(AVCodecContext *avctx) static void videotoolbox_default_free(AVCodecContext *avctx) { - AVVideotoolboxContext *videotoolbox = avctx->hwaccel_context; + AVVideotoolboxContext *videotoolbox = videotoolbox_get_context(avctx); if (videotoolbox) { if (videotoolbox->cm_fmt_desc) @@ -607,6 +676,92 @@ static void videotoolbox_default_free(AVCodecContext *avctx) } } +static int videotoolbox_uninit(AVCodecContext *avctx) +{ + VTContext *vtctx = avctx->internal->hwaccel_priv_data; + if (!vtctx) + return 0; + + ff_videotoolbox_uninit(avctx); + + if (vtctx->vt_ctx) + videotoolbox_default_free(avctx); + + av_buffer_unref(&vtctx->cached_hw_frames_ctx); + av_freep(&vtctx->vt_ctx); + + return 0; +} + +static int videotoolbox_common_init(AVCodecContext *avctx) +{ + VTContext *vtctx = avctx->internal->hwaccel_priv_data; + AVHWFramesContext *hw_frames; + int err; + + // Old API - do nothing. + if (avctx->hwaccel_context) + return 0; + + if (!avctx->hw_frames_ctx && !avctx->hw_device_ctx) { + av_log(avctx, AV_LOG_ERROR, + "Either hw_frames_ctx or hw_device_ctx must be set.\n"); + return AVERROR(EINVAL); + } + + vtctx->vt_ctx = av_videotoolbox_alloc_context(); + if (!vtctx->vt_ctx) { + err = AVERROR(ENOMEM); + goto fail; + } + + if (avctx->hw_frames_ctx) { + hw_frames = (AVHWFramesContext*)avctx->hw_frames_ctx->data; + } else { + avctx->hw_frames_ctx = av_hwframe_ctx_alloc(avctx->hw_device_ctx); + if (!avctx->hw_frames_ctx) { + err = AVERROR(ENOMEM); + goto fail; + } + + hw_frames = (AVHWFramesContext*)avctx->hw_frames_ctx->data; + hw_frames->format = AV_PIX_FMT_VIDEOTOOLBOX; + hw_frames->sw_format = AV_PIX_FMT_NV12; // same as av_videotoolbox_alloc_context() + hw_frames->width = avctx->width; + hw_frames->height = avctx->height; + + err = av_hwframe_ctx_init(avctx->hw_frames_ctx); + if (err < 0) { + av_buffer_unref(&avctx->hw_frames_ctx); + goto fail; + } + } + + vtctx->cached_hw_frames_ctx = av_buffer_ref(avctx->hw_frames_ctx); + if (!vtctx->cached_hw_frames_ctx) { + err = AVERROR(ENOMEM); + goto fail; + } + + vtctx->vt_ctx->cv_pix_fmt_type = + av_map_videotoolbox_format_from_pixfmt(hw_frames->sw_format); + if (!vtctx->vt_ctx->cv_pix_fmt_type) { + av_log(avctx, AV_LOG_ERROR, "Unknown sw_format.\n"); + err = AVERROR(EINVAL); + goto fail; + } + + err = videotoolbox_default_init(avctx); + if (err < 0) + goto fail; + + return 0; + +fail: + videotoolbox_uninit(avctx); + return err; +} + AVHWAccel ff_h263_videotoolbox_hwaccel = { .name = "h263_videotoolbox", .type = AVMEDIA_TYPE_VIDEO, @@ -616,7 +771,8 @@ AVHWAccel ff_h263_videotoolbox_hwaccel = { .start_frame = videotoolbox_mpeg_start_frame, .decode_slice = videotoolbox_mpeg_decode_slice, .end_frame = videotoolbox_mpeg_end_frame, - .uninit = ff_videotoolbox_uninit, + .init = videotoolbox_common_init, + .uninit = videotoolbox_uninit, .priv_data_size = sizeof(VTContext), }; @@ -629,7 +785,8 @@ AVHWAccel ff_h264_videotoolbox_hwaccel = { .start_frame = ff_videotoolbox_h264_start_frame, .decode_slice = ff_videotoolbox_h264_decode_slice, .end_frame = videotoolbox_h264_end_frame, - .uninit = ff_videotoolbox_uninit, + .init = videotoolbox_common_init, + .uninit = videotoolbox_uninit, .priv_data_size = sizeof(VTContext), }; @@ -642,7 +799,8 @@ AVHWAccel ff_mpeg1_videotoolbox_hwaccel = { .start_frame = videotoolbox_mpeg_start_frame, .decode_slice = videotoolbox_mpeg_decode_slice, .end_frame = videotoolbox_mpeg_end_frame, - .uninit = ff_videotoolbox_uninit, + .init = videotoolbox_common_init, + .uninit = videotoolbox_uninit, .priv_data_size = sizeof(VTContext), }; @@ -655,7 +813,8 @@ AVHWAccel ff_mpeg2_videotoolbox_hwaccel = { .start_frame = videotoolbox_mpeg_start_frame, .decode_slice = videotoolbox_mpeg_decode_slice, .end_frame = videotoolbox_mpeg_end_frame, - .uninit = ff_videotoolbox_uninit, + .init = videotoolbox_common_init, + .uninit = videotoolbox_uninit, .priv_data_size = sizeof(VTContext), }; @@ -668,7 +827,8 @@ AVHWAccel ff_mpeg4_videotoolbox_hwaccel = { .start_frame = videotoolbox_mpeg_start_frame, .decode_slice = videotoolbox_mpeg_decode_slice, .end_frame = videotoolbox_mpeg_end_frame, - .uninit = ff_videotoolbox_uninit, + .init = videotoolbox_common_init, + .uninit = videotoolbox_uninit, .priv_data_size = sizeof(VTContext), }; |