1/*
2 * Copyright (c) 2012, Xidorn Quan
3 *
4 * This file is part of FFmpeg.
5 *
6 * FFmpeg is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * FFmpeg is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with FFmpeg; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21/**
22 * @file
23 * H.264 decoder via VDA
24 * @author Xidorn Quan <quanxunzhen@gmail.com>
25 */
26
27#include <string.h>
28#include <CoreFoundation/CoreFoundation.h>
29
30#include "vda.h"
31#include "h264.h"
32#include "avcodec.h"
33
34#ifndef kCFCoreFoundationVersionNumber10_7
35#define kCFCoreFoundationVersionNumber10_7      635.00
36#endif
37
38extern AVCodec ff_h264_decoder, ff_h264_vda_decoder;
39
40static const enum AVPixelFormat vda_pixfmts_prior_10_7[] = {
41    AV_PIX_FMT_UYVY422,
42    AV_PIX_FMT_YUV420P,
43    AV_PIX_FMT_NONE
44};
45
46static const enum AVPixelFormat vda_pixfmts[] = {
47    AV_PIX_FMT_UYVY422,
48    AV_PIX_FMT_YUYV422,
49    AV_PIX_FMT_NV12,
50    AV_PIX_FMT_YUV420P,
51    AV_PIX_FMT_NONE
52};
53
54typedef struct {
55    H264Context h264ctx;
56    int h264_initialized;
57    struct vda_context vda_ctx;
58    enum AVPixelFormat pix_fmt;
59
60    /* for backing-up fields set by user.
61     * we have to gain full control of such fields here */
62    void *hwaccel_context;
63    enum AVPixelFormat (*get_format)(struct AVCodecContext *s, const enum AVPixelFormat * fmt);
64    int (*get_buffer2)(struct AVCodecContext *s, AVFrame *frame, int flags);
65#if FF_API_GET_BUFFER
66    int (*get_buffer)(struct AVCodecContext *c, AVFrame *pic);
67#endif
68} VDADecoderContext;
69
70static enum AVPixelFormat get_format(struct AVCodecContext *avctx,
71        const enum AVPixelFormat *fmt)
72{
73    return AV_PIX_FMT_VDA_VLD;
74}
75
76typedef struct {
77    CVPixelBufferRef cv_buffer;
78} VDABufferContext;
79
80static void release_buffer(void *opaque, uint8_t *data)
81{
82    VDABufferContext *context = opaque;
83    CVPixelBufferUnlockBaseAddress(context->cv_buffer, 0);
84    CVPixelBufferRelease(context->cv_buffer);
85    av_free(context);
86}
87
88static int get_buffer2(AVCodecContext *avctx, AVFrame *pic, int flag)
89{
90    VDABufferContext *context = av_mallocz(sizeof(VDABufferContext));
91    AVBufferRef *buffer = av_buffer_create(NULL, 0, release_buffer, context, 0);
92    if (!context || !buffer) {
93        av_free(context);
94        return AVERROR(ENOMEM);
95    }
96
97    pic->buf[0] = buffer;
98    pic->data[0] = (void *)1;
99    return 0;
100}
101
102static inline void set_context(AVCodecContext *avctx)
103{
104    VDADecoderContext *ctx = avctx->priv_data;
105    ctx->hwaccel_context = avctx->hwaccel_context;
106    avctx->hwaccel_context = &ctx->vda_ctx;
107    ctx->get_format = avctx->get_format;
108    avctx->get_format = get_format;
109    ctx->get_buffer2 = avctx->get_buffer2;
110    avctx->get_buffer2 = get_buffer2;
111#if FF_API_GET_BUFFER
112    ctx->get_buffer = avctx->get_buffer;
113    avctx->get_buffer = NULL;
114#endif
115}
116
117static inline void restore_context(AVCodecContext *avctx)
118{
119    VDADecoderContext *ctx = avctx->priv_data;
120    avctx->hwaccel_context = ctx->hwaccel_context;
121    avctx->get_format = ctx->get_format;
122    avctx->get_buffer2 = ctx->get_buffer2;
123#if FF_API_GET_BUFFER
124    avctx->get_buffer = ctx->get_buffer;
125#endif
126}
127
128static int vdadec_decode(AVCodecContext *avctx,
129        void *data, int *got_frame, AVPacket *avpkt)
130{
131    VDADecoderContext *ctx = avctx->priv_data;
132    AVFrame *pic = data;
133    int ret;
134
135    set_context(avctx);
136    ret = ff_h264_decoder.decode(avctx, data, got_frame, avpkt);
137    restore_context(avctx);
138    if (*got_frame) {
139        AVBufferRef *buffer = pic->buf[0];
140        VDABufferContext *context = av_buffer_get_opaque(buffer);
141        CVPixelBufferRef cv_buffer = (CVPixelBufferRef)pic->data[3];
142
143        CVPixelBufferRetain(cv_buffer);
144        CVPixelBufferLockBaseAddress(cv_buffer, 0);
145        context->cv_buffer = cv_buffer;
146        pic->format = ctx->pix_fmt;
147        if (CVPixelBufferIsPlanar(cv_buffer)) {
148            int i, count = CVPixelBufferGetPlaneCount(cv_buffer);
149            av_assert0(count < 4);
150            for (i = 0; i < count; i++) {
151                pic->data[i] = CVPixelBufferGetBaseAddressOfPlane(cv_buffer, i);
152                pic->linesize[i] = CVPixelBufferGetBytesPerRowOfPlane(cv_buffer, i);
153            }
154        } else {
155            pic->data[0] = CVPixelBufferGetBaseAddress(cv_buffer);
156            pic->linesize[0] = CVPixelBufferGetBytesPerRow(cv_buffer);
157        }
158    }
159    avctx->pix_fmt = ctx->pix_fmt;
160
161    return ret;
162}
163
164static av_cold int vdadec_close(AVCodecContext *avctx)
165{
166    VDADecoderContext *ctx = avctx->priv_data;
167    /* release buffers and decoder */
168    ff_vda_destroy_decoder(&ctx->vda_ctx);
169    /* close H.264 decoder */
170    if (ctx->h264_initialized) {
171        set_context(avctx);
172        ff_h264_decoder.close(avctx);
173        restore_context(avctx);
174    }
175    return 0;
176}
177
178static av_cold int vdadec_init(AVCodecContext *avctx)
179{
180    VDADecoderContext *ctx = avctx->priv_data;
181    struct vda_context *vda_ctx = &ctx->vda_ctx;
182    OSStatus status;
183    int ret, i;
184
185    ctx->h264_initialized = 0;
186
187    /* init pix_fmts of codec */
188    if (!ff_h264_vda_decoder.pix_fmts) {
189        if (kCFCoreFoundationVersionNumber < kCFCoreFoundationVersionNumber10_7)
190            ff_h264_vda_decoder.pix_fmts = vda_pixfmts_prior_10_7;
191        else
192            ff_h264_vda_decoder.pix_fmts = vda_pixfmts;
193    }
194
195    /* init vda */
196    memset(vda_ctx, 0, sizeof(struct vda_context));
197    vda_ctx->width = avctx->width;
198    vda_ctx->height = avctx->height;
199    vda_ctx->format = 'avc1';
200    vda_ctx->use_sync_decoding = 1;
201    vda_ctx->use_ref_buffer = 1;
202    ctx->pix_fmt = avctx->get_format(avctx, avctx->codec->pix_fmts);
203    switch (ctx->pix_fmt) {
204    case AV_PIX_FMT_UYVY422:
205        vda_ctx->cv_pix_fmt_type = '2vuy';
206        break;
207    case AV_PIX_FMT_YUYV422:
208        vda_ctx->cv_pix_fmt_type = 'yuvs';
209        break;
210    case AV_PIX_FMT_NV12:
211        vda_ctx->cv_pix_fmt_type = '420v';
212        break;
213    case AV_PIX_FMT_YUV420P:
214        vda_ctx->cv_pix_fmt_type = 'y420';
215        break;
216    default:
217        av_log(avctx, AV_LOG_ERROR, "Unsupported pixel format: %d\n", avctx->pix_fmt);
218        goto failed;
219    }
220    status = ff_vda_create_decoder(vda_ctx,
221                                   avctx->extradata, avctx->extradata_size);
222    if (status != kVDADecoderNoErr) {
223        av_log(avctx, AV_LOG_ERROR,
224                "Failed to init VDA decoder: %d.\n", status);
225        goto failed;
226    }
227
228    /* init H.264 decoder */
229    set_context(avctx);
230    ret = ff_h264_decoder.init(avctx);
231    restore_context(avctx);
232    if (ret < 0) {
233        av_log(avctx, AV_LOG_ERROR, "Failed to open H.264 decoder.\n");
234        goto failed;
235    }
236    ctx->h264_initialized = 1;
237
238    for (i = 0; i < MAX_SPS_COUNT; i++) {
239        SPS *sps = ctx->h264ctx.sps_buffers[i];
240        if (sps && (sps->bit_depth_luma != 8 ||
241                sps->chroma_format_idc == 2 ||
242                sps->chroma_format_idc == 3)) {
243            av_log(avctx, AV_LOG_ERROR, "Format is not supported.\n");
244            goto failed;
245        }
246    }
247
248    return 0;
249
250failed:
251    vdadec_close(avctx);
252    return -1;
253}
254
255static void vdadec_flush(AVCodecContext *avctx)
256{
257    set_context(avctx);
258    ff_h264_decoder.flush(avctx);
259    restore_context(avctx);
260}
261
262AVCodec ff_h264_vda_decoder = {
263    .name           = "h264_vda",
264    .type           = AVMEDIA_TYPE_VIDEO,
265    .id             = AV_CODEC_ID_H264,
266    .priv_data_size = sizeof(VDADecoderContext),
267    .init           = vdadec_init,
268    .close          = vdadec_close,
269    .decode         = vdadec_decode,
270    .capabilities   = CODEC_CAP_DELAY,
271    .flush          = vdadec_flush,
272    .long_name      = NULL_IF_CONFIG_SMALL("H.264 (VDA acceleration)"),
273};
274