From d66ee6ac4898b8af2f9c39bd0893e89e823286ac Mon Sep 17 00:00:00 2001 From: Sebastien Zwickert Date: Wed, 2 Nov 2011 00:55:09 +0100 Subject: HWAccel: adds Video Decoder Acceleration (VDA) module for Mac OS X. Signed-off-by: Michael Niedermayer --- libavcodec/Makefile | 5 +- libavcodec/allcodecs.c | 1 + libavcodec/h264.c | 1 + libavcodec/mpegvideo.c | 1 + libavcodec/vda.c | 271 ++++++++++++++++++++++++++++++++++++++++++++++ libavcodec/vda.h | 140 ++++++++++++++++++++++++ libavcodec/vda_h264.c | 107 ++++++++++++++++++ libavcodec/vda_internal.h | 50 +++++++++ libavcodec/version.h | 2 +- 9 files changed, 576 insertions(+), 2 deletions(-) create mode 100644 libavcodec/vda.c create mode 100644 libavcodec/vda.h create mode 100644 libavcodec/vda_h264.c create mode 100644 libavcodec/vda_internal.h (limited to 'libavcodec') diff --git a/libavcodec/Makefile b/libavcodec/Makefile index b2c0c99214..0178f8fc6e 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -3,7 +3,7 @@ include $(SUBDIR)../config.mak NAME = avcodec FFLIBS = avutil -HEADERS = avcodec.h avfft.h dxva2.h opt.h vaapi.h vdpau.h version.h xvmc.h +HEADERS = avcodec.h avfft.h dxva2.h opt.h vaapi.h vda.h vdpau.h version.h xvmc.h OBJS = allcodecs.o \ audioconvert.o \ @@ -49,6 +49,7 @@ RDFT-OBJS-$(CONFIG_HARDCODED_TABLES) += sin_tables.o OBJS-$(CONFIG_RDFT) += rdft.o $(RDFT-OBJS-yes) OBJS-$(CONFIG_SINEWIN) += sinewin.o OBJS-$(CONFIG_VAAPI) += vaapi.o +OBJS-$(CONFIG_VDA) += vda.o OBJS-$(CONFIG_VDPAU) += vdpau.o # decoders/encoders/hardware accelerators @@ -192,6 +193,7 @@ OBJS-$(CONFIG_H264_DECODER) += h264.o \ mpegvideo.o error_resilience.o OBJS-$(CONFIG_H264_DXVA2_HWACCEL) += dxva2_h264.o OBJS-$(CONFIG_H264_VAAPI_HWACCEL) += vaapi_h264.o +OBJS-$(CONFIG_H264_VDA_HWACCEL) += vda_h264.o OBJS-$(CONFIG_HUFFYUV_DECODER) += huffyuv.o OBJS-$(CONFIG_HUFFYUV_ENCODER) += huffyuv.o OBJS-$(CONFIG_IDCIN_DECODER) += idcinvideo.o @@ -703,6 +705,7 @@ SKIPHEADERS-$(CONFIG_DXVA2) += dxva2.h dxva2_internal.h SKIPHEADERS-$(CONFIG_LIBDIRAC) += libdirac.h SKIPHEADERS-$(CONFIG_LIBSCHROEDINGER) += libschroedinger.h SKIPHEADERS-$(CONFIG_VAAPI) += vaapi_internal.h +SKIPHEADERS-$(CONFIG_VDA) += vda_internal.h SKIPHEADERS-$(CONFIG_VDPAU) += vdpau.h SKIPHEADERS-$(CONFIG_XVMC) += xvmc.h SKIPHEADERS-$(HAVE_W32THREADS) += w32pthreads.h diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c index 1119dd1e7e..7ba2ab3f89 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -57,6 +57,7 @@ void avcodec_register_all(void) REGISTER_HWACCEL (H263_VAAPI, h263_vaapi); REGISTER_HWACCEL (H264_DXVA2, h264_dxva2); REGISTER_HWACCEL (H264_VAAPI, h264_vaapi); + REGISTER_HWACCEL (H264_VDA, h264_vda); REGISTER_HWACCEL (MPEG1_VDPAU, mpeg1_vdpau); REGISTER_HWACCEL (MPEG2_DXVA2, mpeg2_dxva2); REGISTER_HWACCEL (MPEG2_VAAPI, mpeg2_vaapi); diff --git a/libavcodec/h264.c b/libavcodec/h264.c index 3302f71993..3f6bc7290b 100644 --- a/libavcodec/h264.c +++ b/libavcodec/h264.c @@ -56,6 +56,7 @@ static const uint8_t div6[QP_MAX_NUM+1]={ static const enum PixelFormat hwaccel_pixfmt_list_h264_jpeg_420[] = { PIX_FMT_DXVA2_VLD, PIX_FMT_VAAPI_VLD, + PIX_FMT_VDA_VLD, PIX_FMT_YUVJ420P, PIX_FMT_NONE }; diff --git a/libavcodec/mpegvideo.c b/libavcodec/mpegvideo.c index fffa36a456..29500ea7b8 100644 --- a/libavcodec/mpegvideo.c +++ b/libavcodec/mpegvideo.c @@ -118,6 +118,7 @@ const enum PixelFormat ff_pixfmt_list_420[] = { const enum PixelFormat ff_hwaccel_pixfmt_list_420[] = { PIX_FMT_DXVA2_VLD, PIX_FMT_VAAPI_VLD, + PIX_FMT_VDA_VLD, PIX_FMT_YUV420P, PIX_FMT_NONE }; diff --git a/libavcodec/vda.c b/libavcodec/vda.c new file mode 100644 index 0000000000..5fe8a48fa4 --- /dev/null +++ b/libavcodec/vda.c @@ -0,0 +1,271 @@ +/* + * VDA HW acceleration. + * + * copyright (c) 2011 Sebastien Zwickert + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "vda_internal.h" + +/** + * \addtogroup VDA_Decoding + * + * @{ + */ + +/* Mutex manager callback. */ +static int vda_lock_operation(void **mtx, enum AVLockOp op) +{ + switch(op) + { + case AV_LOCK_CREATE: + *mtx = av_malloc(sizeof(pthread_mutex_t)); + if(!*mtx) + return 1; + return !!pthread_mutex_init(*mtx, NULL); + case AV_LOCK_OBTAIN: + return !!pthread_mutex_lock(*mtx); + case AV_LOCK_RELEASE: + return !!pthread_mutex_unlock(*mtx); + case AV_LOCK_DESTROY: + pthread_mutex_destroy(*mtx); + av_freep(mtx); + return 0; + } + return 1; +} + +/* 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 (NULL == user_info) + return 0; + + pts = CFDictionaryGetValue(user_info, CFSTR("FF_VDA_DECODER_PTS_KEY")); + + if (pts) + CFNumberGetValue(pts, kCFNumberSInt64Type, &outValue); + + return outValue; +} + +/* Removes and releases all frames from the queue. */ +static void vda_clear_queue(struct vda_context *vda_ctx) +{ + vda_frame *top_frame; + + vda_lock_operation(&vda_ctx->queue_mutex, AV_LOCK_OBTAIN); + + while (vda_ctx->queue != NULL) + { + top_frame = vda_ctx->queue; + vda_ctx->queue = top_frame->next_frame; + ff_vda_release_vda_frame(top_frame); + } + + vda_lock_operation(&vda_ctx->queue_mutex, AV_LOCK_RELEASE); +} + + +/* Decoder callback that adds the vda frame to the queue in display order. */ +static void vda_decoder_callback (void *vda_hw_ctx, + CFDictionaryRef user_info, + OSStatus status, + uint32_t infoFlags, + CVImageBufferRef image_buffer) +{ + struct vda_context *vda_ctx = (struct vda_context*)vda_hw_ctx; + vda_frame *new_frame; + vda_frame *queue_walker; + + if (NULL == image_buffer) + return; + + if (kCVPixelFormatType_422YpCbCr8 != CVPixelBufferGetPixelFormatType(image_buffer)) + return; + + new_frame = (vda_frame *)av_mallocz(sizeof(vda_frame)); + new_frame->next_frame = NULL; + new_frame->cv_buffer = CVPixelBufferRetain(image_buffer); + new_frame->pts = vda_pts_from_dictionary(user_info); + + vda_lock_operation(&vda_ctx->queue_mutex, AV_LOCK_OBTAIN); + + 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; + } + } + + vda_lock_operation(&vda_ctx->queue_mutex, AV_LOCK_RELEASE); +} + +int ff_vda_create_decoder(struct vda_context *vda_ctx, + uint8_t *extradata, + int extradata_size) +{ + OSStatus status = kVDADecoderNoErr; + CFNumberRef height; + CFNumberRef width; + CFNumberRef format; + CFDataRef avc_data; + CFMutableDictionaryRef config_info; + + if (av_lockmgr_register(vda_lock_operation)) + return -1; + + vda_lock_operation(&vda_ctx->queue_mutex, AV_LOCK_CREATE); + + config_info = (CFDictionaryCreateMutable(kCFAllocatorDefault, + 4, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks)); + + height = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &vda_ctx->height); + width = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &vda_ctx->width); + format = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &vda_ctx->format); + avc_data = CFDataCreate(kCFAllocatorDefault, extradata, extradata_size); + + CFDictionarySetValue(config_info, kVDADecoderConfiguration_Height, height); + CFDictionarySetValue(config_info, kVDADecoderConfiguration_Width, width); + CFDictionarySetValue(config_info, kVDADecoderConfiguration_SourceFormat, format); + CFDictionarySetValue(config_info, kVDADecoderConfiguration_avcCData, avc_data); + + status = VDADecoderCreate( config_info, + NULL, + (VDADecoderOutputCallback *)vda_decoder_callback, + (void *)vda_ctx, + &vda_ctx->decoder ); + + CFRelease(height); + CFRelease(width); + CFRelease(format); + CFRelease(avc_data); + CFRelease(config_info); + + if (kVDADecoderNoErr != status) + return status; + + return 0; +} + +int ff_vda_destroy_decoder(struct vda_context *vda_ctx) +{ + OSStatus status = kVDADecoderNoErr; + + if (vda_ctx->decoder) + status = VDADecoderDestroy(vda_ctx->decoder); + + vda_clear_queue(vda_ctx); + + if (vda_ctx->queue_mutex != NULL) + vda_lock_operation(&vda_ctx->queue_mutex, AV_LOCK_DESTROY); + + if (kVDADecoderNoErr != status) + return status; + + return 0; +} + +vda_frame *ff_vda_queue_pop(struct vda_context *vda_ctx) +{ + vda_frame *top_frame; + + if (!vda_ctx->queue) + return NULL; + + vda_lock_operation(&vda_ctx->queue_mutex, AV_LOCK_OBTAIN); + top_frame = vda_ctx->queue; + vda_ctx->queue = top_frame->next_frame; + vda_lock_operation(&vda_ctx->queue_mutex, AV_LOCK_RELEASE); + + return top_frame; +} + +void ff_vda_release_vda_frame(vda_frame *frame) +{ + if (frame != NULL) + { + CVPixelBufferRelease(frame->cv_buffer); + av_freep(&frame); + } +} + +int ff_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); + + if (kVDADecoderNoErr != status) + return status; + + return 0; +} + +/* @} */ diff --git a/libavcodec/vda.h b/libavcodec/vda.h new file mode 100644 index 0000000000..3f70cf2cf2 --- /dev/null +++ b/libavcodec/vda.h @@ -0,0 +1,140 @@ +/* + * VDA HW acceleration + * + * copyright (c) 2011 Sebastien Zwickert + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_VDA_H +#define AVCODEC_VDA_H + +#include + +// emmintrin.h is unable to compile with -std=c99 -Werror=missing-prototypes +// http://openradar.appspot.com/8026390 +#undef __GNUC_STDC_INLINE__ + +#define Picture QuickdrawPicture + +#include +#include "avcodec.h" +#include + +/** + * This structure is used to store a decoded frame information and data. + */ +typedef struct +{ + /** + * The PTS of the frame. + * + * - encoding: unused + * - decoding: Set/Unset by libavcodec. + */ + int64_t pts; + + /** + * The CoreVideo buffer that contains the decoded data. + * + * - encoding: unused + * - decoding: Set/Unset by libavcodec. + */ + CVPixelBufferRef cv_buffer; + + /** + * A pointer to the next frame. + * + * - encoding: unused + * - decoding: Set/Unset by libavcodec. + */ + struct vda_frame *next_frame; + +} vda_frame; + +/** + * This structure is used to provide the necessary configurations and data + * to the VDA FFmpeg HWAccel implementation. + * + * The application must make it available as AVCodecContext.hwaccel_context. + */ +struct vda_context { + + /** + * VDA decoder object. + * + * - encoding: unused + * - decoding: Set/Unset by libavcodec. + */ + VDADecoder decoder; + + /** + * VDA frames queue ordered by presentation timestamp. + * + * - encoding: unused + * - decoding: Set/Unset by libavcodec. + */ + vda_frame *queue; + + /** + * Mutex for locking queue operations. + * + * - encoding: unused + * - decoding: Set/Unset by libavcodec. + */ + void *queue_mutex; + + /** + * The frame width. + * + * - encoding: unused + * - decoding: Set/Unset by user. + */ + int width; + + /** + * The frame height. + * + * - encoding: unused + * - decoding: Set/Unset by user. + */ + int height; + + /** + * The frame format. + * + * - encoding: unused + * - decoding: Set/Unset by user. + */ + int format; +}; + +/** Creates the video decoder. */ +int ff_vda_create_decoder(struct vda_context *vda_ctx, + uint8_t *extradata, + int extradata_size); + +/** Destroys the video decoder. */ +int ff_vda_destroy_decoder(struct vda_context *vda_ctx); + +/** Returns the top frame of the queue. */ +vda_frame *ff_vda_queue_pop(struct vda_context *vda_ctx); + +/** Releases the given frame. */ +void ff_vda_release_vda_frame(vda_frame *frame); + +#endif /* AVCODEC_VDA_H */ diff --git a/libavcodec/vda_h264.c b/libavcodec/vda_h264.c new file mode 100644 index 0000000000..f4791a168c --- /dev/null +++ b/libavcodec/vda_h264.c @@ -0,0 +1,107 @@ +/* + * VDA H264 HW acceleration. + * + * copyright (c) 2011 Sebastien Zwickert + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "vda_internal.h" + +/* This structure is used to store the bitstream of the current frame. */ +struct vda_picture_context { + uint8_t *bitstream; + int bitstream_size; +}; + +static int start_frame(AVCodecContext *avctx, + av_unused const uint8_t *buffer, + av_unused uint32_t size) +{ + const H264Context *h = avctx->priv_data; + struct vda_context *vda_ctx = avctx->hwaccel_context; + struct vda_picture_context *pic_ctx = h->s.current_picture_ptr->f.hwaccel_picture_private; + + if (!vda_ctx->decoder) + return -1; + + pic_ctx->bitstream = NULL; + pic_ctx->bitstream_size = 0; + + return 0; +} + +static int decode_slice(AVCodecContext *avctx, + const uint8_t *buffer, + uint32_t size) +{ + H264Context *h = avctx->priv_data; + struct vda_context *vda_ctx = avctx->hwaccel_context; + struct vda_picture_context *pic_ctx = h->s.current_picture_ptr->f.hwaccel_picture_private; + void *tmp; + + if (!vda_ctx->decoder) + return -1; + + tmp = av_realloc(pic_ctx->bitstream, pic_ctx->bitstream_size+size+4); + if (!tmp) + return AVERROR(ENOMEM); + + pic_ctx->bitstream = tmp; + + AV_WB32(pic_ctx->bitstream+pic_ctx->bitstream_size, size); + memcpy(pic_ctx->bitstream+pic_ctx->bitstream_size+4, buffer, size); + + pic_ctx->bitstream_size += size + 4; + + return 0; +} + +static int end_frame(AVCodecContext *avctx) +{ + H264Context *h = avctx->priv_data; + struct vda_context *vda_ctx = avctx->hwaccel_context; + struct vda_picture_context *pic_ctx = h->s.current_picture_ptr->f.hwaccel_picture_private; + AVFrame *frame = (AVFrame*)h->s.current_picture_ptr; + int status; + + if (!vda_ctx->decoder || !pic_ctx->bitstream) + return -1; + + status = ff_vda_decoder_decode(vda_ctx, pic_ctx->bitstream, + pic_ctx->bitstream_size, + frame->reordered_opaque); + + if (status) + av_log(avctx, AV_LOG_ERROR, "Failed to decode frame (%d)\n", status); + + av_freep(&pic_ctx->bitstream); + + return status; +} + +AVHWAccel ff_h264_vda_hwaccel = { + .name = "h264_vda", + .type = AVMEDIA_TYPE_VIDEO, + .id = CODEC_ID_H264, + .pix_fmt = PIX_FMT_VDA_VLD, + .capabilities = 0, + .start_frame = start_frame, + .decode_slice = decode_slice, + .end_frame = end_frame, + .priv_data_size = sizeof(struct vda_picture_context), +}; diff --git a/libavcodec/vda_internal.h b/libavcodec/vda_internal.h new file mode 100644 index 0000000000..c6dcaa8ff6 --- /dev/null +++ b/libavcodec/vda_internal.h @@ -0,0 +1,50 @@ +/* + * VDA HW acceleration + * + * copyright (c) 2011 Sebastien Zwickert + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_VDA_INTERNAL_H +#define AVCODEC_VDA_INTERNAL_H + +#include "h264.h" +#include "h264data.h" + +#include "vda.h" + +#include +#include +#include +#include + +/** + * \addtogroup VDA_Decoding + * + * @{ + */ + +/** Send a frame data to the hardware decoder. */ +int ff_vda_decoder_decode(struct vda_context *vda_ctx, + uint8_t *bitstream, + int bitstream_size, + int64_t frame_pts); + +/* @} */ + +#endif /* AVCODEC_VDA_INTERNAL_H */ diff --git a/libavcodec/version.h b/libavcodec/version.h index db9b8ddad6..ad64b6032a 100644 --- a/libavcodec/version.h +++ b/libavcodec/version.h @@ -21,7 +21,7 @@ #define AVCODEC_VERSION_H #define LIBAVCODEC_VERSION_MAJOR 53 -#define LIBAVCODEC_VERSION_MINOR 26 +#define LIBAVCODEC_VERSION_MINOR 27 #define LIBAVCODEC_VERSION_MICRO 0 #define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \ -- cgit v1.2.3