yading@10: /* yading@10: * Copyright (c) 2012, Xidorn Quan yading@10: * yading@10: * This file is part of FFmpeg. yading@10: * yading@10: * FFmpeg is free software; you can redistribute it and/or yading@10: * modify it under the terms of the GNU Lesser General Public yading@10: * License as published by the Free Software Foundation; either yading@10: * version 2.1 of the License, or (at your option) any later version. yading@10: * yading@10: * FFmpeg is distributed in the hope that it will be useful, yading@10: * but WITHOUT ANY WARRANTY; without even the implied warranty of yading@10: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU yading@10: * Lesser General Public License for more details. yading@10: * yading@10: * You should have received a copy of the GNU Lesser General Public yading@10: * License along with FFmpeg; if not, write to the Free Software yading@10: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA yading@10: */ yading@10: yading@10: /** yading@10: * @file yading@10: * H.264 decoder via VDA yading@10: * @author Xidorn Quan yading@10: */ yading@10: yading@10: #include yading@10: #include yading@10: yading@10: #include "vda.h" yading@10: #include "h264.h" yading@10: #include "avcodec.h" yading@10: yading@10: #ifndef kCFCoreFoundationVersionNumber10_7 yading@10: #define kCFCoreFoundationVersionNumber10_7 635.00 yading@10: #endif yading@10: yading@10: extern AVCodec ff_h264_decoder, ff_h264_vda_decoder; yading@10: yading@10: static const enum AVPixelFormat vda_pixfmts_prior_10_7[] = { yading@10: AV_PIX_FMT_UYVY422, yading@10: AV_PIX_FMT_YUV420P, yading@10: AV_PIX_FMT_NONE yading@10: }; yading@10: yading@10: static const enum AVPixelFormat vda_pixfmts[] = { yading@10: AV_PIX_FMT_UYVY422, yading@10: AV_PIX_FMT_YUYV422, yading@10: AV_PIX_FMT_NV12, yading@10: AV_PIX_FMT_YUV420P, yading@10: AV_PIX_FMT_NONE yading@10: }; yading@10: yading@10: typedef struct { yading@10: H264Context h264ctx; yading@10: int h264_initialized; yading@10: struct vda_context vda_ctx; yading@10: enum AVPixelFormat pix_fmt; yading@10: } VDADecoderContext; yading@10: yading@10: static enum AVPixelFormat get_format(struct AVCodecContext *avctx, yading@10: const enum AVPixelFormat *fmt) yading@10: { yading@10: return AV_PIX_FMT_VDA_VLD; yading@10: } yading@10: yading@10: typedef struct { yading@10: CVPixelBufferRef cv_buffer; yading@10: } VDABufferContext; yading@10: yading@10: static void release_buffer(void *opaque, uint8_t *data) yading@10: { yading@10: VDABufferContext *context = opaque; yading@10: CVPixelBufferUnlockBaseAddress(context->cv_buffer, 0); yading@10: CVPixelBufferRelease(context->cv_buffer); yading@10: av_free(context); yading@10: } yading@10: yading@10: static int get_buffer2(AVCodecContext *avctx, AVFrame *pic, int flag) yading@10: { yading@10: VDABufferContext *context = av_mallocz(sizeof(VDABufferContext)); yading@10: AVBufferRef *buffer = av_buffer_create(NULL, 0, release_buffer, context, 0); yading@10: if (!context || !buffer) { yading@10: av_free(context); yading@10: return AVERROR(ENOMEM); yading@10: } yading@10: yading@10: pic->buf[0] = buffer; yading@10: pic->data[0] = (void *)1; yading@10: return 0; yading@10: } yading@10: yading@10: static int vdadec_decode(AVCodecContext *avctx, yading@10: void *data, int *got_frame, AVPacket *avpkt) yading@10: { yading@10: VDADecoderContext *ctx = avctx->priv_data; yading@10: AVFrame *pic = data; yading@10: int ret; yading@10: yading@10: ret = ff_h264_decoder.decode(avctx, data, got_frame, avpkt); yading@10: if (*got_frame) { yading@10: AVBufferRef *buffer = pic->buf[0]; yading@10: VDABufferContext *context = av_buffer_get_opaque(buffer); yading@10: CVPixelBufferRef cv_buffer = (CVPixelBufferRef)pic->data[3]; yading@10: CVPixelBufferLockBaseAddress(cv_buffer, 0); yading@10: context->cv_buffer = cv_buffer; yading@10: pic->format = ctx->pix_fmt; yading@10: if (CVPixelBufferIsPlanar(cv_buffer)) { yading@10: int i, count = CVPixelBufferGetPlaneCount(cv_buffer); yading@10: av_assert0(count < 4); yading@10: for (i = 0; i < count; i++) { yading@10: pic->data[i] = CVPixelBufferGetBaseAddressOfPlane(cv_buffer, i); yading@10: pic->linesize[i] = CVPixelBufferGetBytesPerRowOfPlane(cv_buffer, i); yading@10: } yading@10: } else { yading@10: pic->data[0] = CVPixelBufferGetBaseAddress(cv_buffer); yading@10: pic->linesize[0] = CVPixelBufferGetBytesPerRow(cv_buffer); yading@10: } yading@10: } yading@10: avctx->pix_fmt = ctx->pix_fmt; yading@10: yading@10: return ret; yading@10: } yading@10: yading@10: static av_cold int vdadec_close(AVCodecContext *avctx) yading@10: { yading@10: VDADecoderContext *ctx = avctx->priv_data; yading@10: /* release buffers and decoder */ yading@10: ff_vda_destroy_decoder(&ctx->vda_ctx); yading@10: /* close H.264 decoder */ yading@10: if (ctx->h264_initialized) yading@10: ff_h264_decoder.close(avctx); yading@10: return 0; yading@10: } yading@10: yading@10: static av_cold int check_format(AVCodecContext *avctx) yading@10: { yading@10: AVCodecParserContext *parser; yading@10: uint8_t *pout; yading@10: int psize; yading@10: int index; yading@10: H264Context *h; yading@10: int ret = -1; yading@10: yading@10: /* init parser & parse file */ yading@10: parser = av_parser_init(avctx->codec->id); yading@10: if (!parser) { yading@10: av_log(avctx, AV_LOG_ERROR, "Failed to open H.264 parser.\n"); yading@10: goto final; yading@10: } yading@10: parser->flags = PARSER_FLAG_COMPLETE_FRAMES; yading@10: index = av_parser_parse2(parser, avctx, &pout, &psize, NULL, 0, 0, 0, 0); yading@10: if (index < 0) { yading@10: av_log(avctx, AV_LOG_ERROR, "Failed to parse this file.\n"); yading@10: goto release_parser; yading@10: } yading@10: yading@10: /* check if support */ yading@10: h = parser->priv_data; yading@10: switch (h->sps.bit_depth_luma) { yading@10: case 8: yading@10: if (!CHROMA444(h) && !CHROMA422(h)) { yading@10: // only this will H.264 decoder switch to hwaccel yading@10: ret = 0; yading@10: break; yading@10: } yading@10: default: yading@10: av_log(avctx, AV_LOG_ERROR, "Unsupported file.\n"); yading@10: } yading@10: yading@10: release_parser: yading@10: av_parser_close(parser); yading@10: yading@10: final: yading@10: return ret; yading@10: } yading@10: yading@10: static av_cold int vdadec_init(AVCodecContext *avctx) yading@10: { yading@10: VDADecoderContext *ctx = avctx->priv_data; yading@10: struct vda_context *vda_ctx = &ctx->vda_ctx; yading@10: OSStatus status; yading@10: int ret; yading@10: yading@10: ctx->h264_initialized = 0; yading@10: yading@10: /* init pix_fmts of codec */ yading@10: if (!ff_h264_vda_decoder.pix_fmts) { yading@10: if (kCFCoreFoundationVersionNumber < kCFCoreFoundationVersionNumber10_7) yading@10: ff_h264_vda_decoder.pix_fmts = vda_pixfmts_prior_10_7; yading@10: else yading@10: ff_h264_vda_decoder.pix_fmts = vda_pixfmts; yading@10: } yading@10: yading@10: /* check if VDA supports this file */ yading@10: if (check_format(avctx) < 0) yading@10: goto failed; yading@10: yading@10: /* init vda */ yading@10: memset(vda_ctx, 0, sizeof(struct vda_context)); yading@10: vda_ctx->width = avctx->width; yading@10: vda_ctx->height = avctx->height; yading@10: vda_ctx->format = 'avc1'; yading@10: vda_ctx->use_sync_decoding = 1; yading@10: ctx->pix_fmt = avctx->get_format(avctx, avctx->codec->pix_fmts); yading@10: switch (ctx->pix_fmt) { yading@10: case AV_PIX_FMT_UYVY422: yading@10: vda_ctx->cv_pix_fmt_type = '2vuy'; yading@10: break; yading@10: case AV_PIX_FMT_YUYV422: yading@10: vda_ctx->cv_pix_fmt_type = 'yuvs'; yading@10: break; yading@10: case AV_PIX_FMT_NV12: yading@10: vda_ctx->cv_pix_fmt_type = '420v'; yading@10: break; yading@10: case AV_PIX_FMT_YUV420P: yading@10: vda_ctx->cv_pix_fmt_type = 'y420'; yading@10: break; yading@10: default: yading@10: av_log(avctx, AV_LOG_ERROR, "Unsupported pixel format: %d\n", avctx->pix_fmt); yading@10: goto failed; yading@10: } yading@10: status = ff_vda_create_decoder(vda_ctx, yading@10: avctx->extradata, avctx->extradata_size); yading@10: if (status != kVDADecoderNoErr) { yading@10: av_log(avctx, AV_LOG_ERROR, yading@10: "Failed to init VDA decoder: %d.\n", status); yading@10: goto failed; yading@10: } yading@10: avctx->hwaccel_context = vda_ctx; yading@10: yading@10: /* changes callback functions */ yading@10: avctx->get_format = get_format; yading@10: avctx->get_buffer2 = get_buffer2; yading@10: #if FF_API_GET_BUFFER yading@10: // force the old get_buffer to be empty yading@10: avctx->get_buffer = NULL; yading@10: #endif yading@10: yading@10: /* init H.264 decoder */ yading@10: ret = ff_h264_decoder.init(avctx); yading@10: if (ret < 0) { yading@10: av_log(avctx, AV_LOG_ERROR, "Failed to open H.264 decoder.\n"); yading@10: goto failed; yading@10: } yading@10: ctx->h264_initialized = 1; yading@10: yading@10: return 0; yading@10: yading@10: failed: yading@10: vdadec_close(avctx); yading@10: return -1; yading@10: } yading@10: yading@10: static void vdadec_flush(AVCodecContext *avctx) yading@10: { yading@10: return ff_h264_decoder.flush(avctx); yading@10: } yading@10: yading@10: AVCodec ff_h264_vda_decoder = { yading@10: .name = "h264_vda", yading@10: .type = AVMEDIA_TYPE_VIDEO, yading@10: .id = AV_CODEC_ID_H264, yading@10: .priv_data_size = sizeof(VDADecoderContext), yading@10: .init = vdadec_init, yading@10: .close = vdadec_close, yading@10: .decode = vdadec_decode, yading@10: .capabilities = CODEC_CAP_DELAY, yading@10: .flush = vdadec_flush, yading@10: .long_name = NULL_IF_CONFIG_SMALL("H.264 (VDA acceleration)"), yading@10: };