1 /*
2  * Copyright (c) 2010, Google, Inc.
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  * AV1 decoder support via libaom
24  */
25 
26 #include <aom/aom_decoder.h>
27 #include <aom/aomdx.h>
28 
29 #include "libavutil/common.h"
30 #include "libavutil/cpu.h"
31 #include "libavutil/imgutils.h"
32 
33 #include "avcodec.h"
34 #include "codec_internal.h"
35 #include "internal.h"
36 #include "profiles.h"
37 
38 typedef struct AV1DecodeContext {
39     struct aom_codec_ctx decoder;
40 } AV1DecodeContext;
41 
aom_init(AVCodecContext *avctx, const struct aom_codec_iface *iface)42 static av_cold int aom_init(AVCodecContext *avctx,
43                             const struct aom_codec_iface *iface)
44 {
45     AV1DecodeContext *ctx           = avctx->priv_data;
46     struct aom_codec_dec_cfg deccfg = {
47         .threads = FFMIN(avctx->thread_count ? avctx->thread_count : av_cpu_count(), 16)
48     };
49 
50     av_log(avctx, AV_LOG_INFO, "%s\n", aom_codec_version_str());
51     av_log(avctx, AV_LOG_VERBOSE, "%s\n", aom_codec_build_config());
52 
53     if (aom_codec_dec_init(&ctx->decoder, iface, &deccfg, 0) != AOM_CODEC_OK) {
54         const char *error = aom_codec_error(&ctx->decoder);
55         av_log(avctx, AV_LOG_ERROR, "Failed to initialize decoder: %s\n",
56                error);
57         return AVERROR(EINVAL);
58     }
59 
60     return 0;
61 }
62 
image_copy_16_to_8(AVFrame *pic, struct aom_image *img)63 static void image_copy_16_to_8(AVFrame *pic, struct aom_image *img)
64 {
65     const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pic->format);
66     int i;
67 
68     for (i = 0; i < desc->nb_components; i++) {
69         int w = img->d_w;
70         int h = img->d_h;
71         int x, y;
72 
73         if (i) {
74             w = (w + img->x_chroma_shift) >> img->x_chroma_shift;
75             h = (h + img->y_chroma_shift) >> img->y_chroma_shift;
76         }
77 
78         for (y = 0; y < h; y++) {
79             uint16_t *src = (uint16_t *)(img->planes[i] + y * img->stride[i]);
80             uint8_t *dst = pic->data[i] + y * pic->linesize[i];
81             for (x = 0; x < w; x++)
82                 *dst++ = *src++;
83         }
84     }
85 }
86 
87 // returns 0 on success, AVERROR_INVALIDDATA otherwise
set_pix_fmt(AVCodecContext *avctx, struct aom_image *img)88 static int set_pix_fmt(AVCodecContext *avctx, struct aom_image *img)
89 {
90     static const enum AVColorRange color_ranges[] = {
91         AVCOL_RANGE_MPEG, AVCOL_RANGE_JPEG
92     };
93     avctx->color_range = color_ranges[img->range];
94     avctx->color_primaries = img->cp;
95     avctx->colorspace  = img->mc;
96     avctx->color_trc   = img->tc;
97 
98     switch (img->fmt) {
99     case AOM_IMG_FMT_I420:
100     case AOM_IMG_FMT_I42016:
101         if (img->bit_depth == 8) {
102             avctx->pix_fmt = img->monochrome ?
103                              AV_PIX_FMT_GRAY8 : AV_PIX_FMT_YUV420P;
104             avctx->profile = FF_PROFILE_AV1_MAIN;
105             return 0;
106         } else if (img->bit_depth == 10) {
107             avctx->pix_fmt = img->monochrome ?
108                              AV_PIX_FMT_GRAY10 : AV_PIX_FMT_YUV420P10;
109             avctx->profile = FF_PROFILE_AV1_MAIN;
110             return 0;
111         } else if (img->bit_depth == 12) {
112             avctx->pix_fmt = img->monochrome ?
113                              AV_PIX_FMT_GRAY12 : AV_PIX_FMT_YUV420P12;
114             avctx->profile = FF_PROFILE_AV1_PROFESSIONAL;
115             return 0;
116         } else {
117             return AVERROR_INVALIDDATA;
118         }
119     case AOM_IMG_FMT_I422:
120     case AOM_IMG_FMT_I42216:
121         if (img->bit_depth == 8) {
122             avctx->pix_fmt = AV_PIX_FMT_YUV422P;
123             avctx->profile = FF_PROFILE_AV1_PROFESSIONAL;
124             return 0;
125         } else if (img->bit_depth == 10) {
126             avctx->pix_fmt = AV_PIX_FMT_YUV422P10;
127             avctx->profile = FF_PROFILE_AV1_PROFESSIONAL;
128             return 0;
129         } else if (img->bit_depth == 12) {
130             avctx->pix_fmt = AV_PIX_FMT_YUV422P12;
131             avctx->profile = FF_PROFILE_AV1_PROFESSIONAL;
132             return 0;
133         } else {
134             return AVERROR_INVALIDDATA;
135         }
136     case AOM_IMG_FMT_I444:
137     case AOM_IMG_FMT_I44416:
138         if (img->bit_depth == 8) {
139             avctx->pix_fmt = AV_PIX_FMT_YUV444P;
140             avctx->profile = FF_PROFILE_AV1_HIGH;
141             return 0;
142         } else if (img->bit_depth == 10) {
143             avctx->pix_fmt = AV_PIX_FMT_YUV444P10;
144             avctx->profile = FF_PROFILE_AV1_HIGH;
145             return 0;
146         } else if (img->bit_depth == 12) {
147             avctx->pix_fmt = AV_PIX_FMT_YUV444P12;
148             avctx->profile = FF_PROFILE_AV1_PROFESSIONAL;
149             return 0;
150         } else {
151             return AVERROR_INVALIDDATA;
152         }
153 
154     default:
155         return AVERROR_INVALIDDATA;
156     }
157 }
158 
aom_decode(AVCodecContext *avctx, AVFrame *picture, int *got_frame, AVPacket *avpkt)159 static int aom_decode(AVCodecContext *avctx, AVFrame *picture,
160                       int *got_frame, AVPacket *avpkt)
161 {
162     AV1DecodeContext *ctx = avctx->priv_data;
163     const void *iter      = NULL;
164     struct aom_image *img;
165     int ret;
166 
167     if (aom_codec_decode(&ctx->decoder, avpkt->data, avpkt->size, NULL) !=
168         AOM_CODEC_OK) {
169         const char *error  = aom_codec_error(&ctx->decoder);
170         const char *detail = aom_codec_error_detail(&ctx->decoder);
171 
172         av_log(avctx, AV_LOG_ERROR, "Failed to decode frame: %s\n", error);
173         if (detail)
174             av_log(avctx, AV_LOG_ERROR, "  Additional information: %s\n",
175                    detail);
176         return AVERROR_INVALIDDATA;
177     }
178 
179     if ((img = aom_codec_get_frame(&ctx->decoder, &iter))) {
180         if (img->d_w > img->w || img->d_h > img->h) {
181             av_log(avctx, AV_LOG_ERROR, "Display dimensions %dx%d exceed storage %dx%d\n",
182                    img->d_w, img->d_h, img->w, img->h);
183             return AVERROR_EXTERNAL;
184         }
185 
186         if ((ret = set_pix_fmt(avctx, img)) < 0) {
187             av_log(avctx, AV_LOG_ERROR, "Unsupported output colorspace (%d) / bit_depth (%d)\n",
188                    img->fmt, img->bit_depth);
189             return ret;
190         }
191 
192         if ((int)img->d_w != avctx->width || (int)img->d_h != avctx->height) {
193             av_log(avctx, AV_LOG_INFO, "dimension change! %dx%d -> %dx%d\n",
194                    avctx->width, avctx->height, img->d_w, img->d_h);
195             ret = ff_set_dimensions(avctx, img->d_w, img->d_h);
196             if (ret < 0)
197                 return ret;
198         }
199         if ((ret = ff_get_buffer(avctx, picture, 0)) < 0)
200             return ret;
201 
202 #ifdef AOM_CTRL_AOMD_GET_FRAME_FLAGS
203         {
204             aom_codec_frame_flags_t flags;
205             ret = aom_codec_control(&ctx->decoder, AOMD_GET_FRAME_FLAGS, &flags);
206             if (ret == AOM_CODEC_OK) {
207                 picture->key_frame = !!(flags & AOM_FRAME_IS_KEY);
208                 if (flags & (AOM_FRAME_IS_KEY | AOM_FRAME_IS_INTRAONLY))
209                     picture->pict_type = AV_PICTURE_TYPE_I;
210                 else if (flags & AOM_FRAME_IS_SWITCH)
211                     picture->pict_type = AV_PICTURE_TYPE_SP;
212                 else
213                     picture->pict_type = AV_PICTURE_TYPE_P;
214             }
215         }
216 #endif
217 
218         av_reduce(&picture->sample_aspect_ratio.num,
219                   &picture->sample_aspect_ratio.den,
220                   picture->height * img->r_w,
221                   picture->width * img->r_h,
222                   INT_MAX);
223         ff_set_sar(avctx, picture->sample_aspect_ratio);
224 
225         if ((img->fmt & AOM_IMG_FMT_HIGHBITDEPTH) && img->bit_depth == 8)
226             image_copy_16_to_8(picture, img);
227         else {
228             const uint8_t *planes[4] = { img->planes[0], img->planes[1], img->planes[2] };
229             const int      stride[4] = { img->stride[0], img->stride[1], img->stride[2] };
230 
231             av_image_copy(picture->data, picture->linesize, planes,
232                           stride, avctx->pix_fmt, img->d_w, img->d_h);
233         }
234         *got_frame = 1;
235     }
236     return avpkt->size;
237 }
238 
aom_free(AVCodecContext *avctx)239 static av_cold int aom_free(AVCodecContext *avctx)
240 {
241     AV1DecodeContext *ctx = avctx->priv_data;
242     aom_codec_destroy(&ctx->decoder);
243     return 0;
244 }
245 
av1_init(AVCodecContext *avctx)246 static av_cold int av1_init(AVCodecContext *avctx)
247 {
248     return aom_init(avctx, aom_codec_av1_dx());
249 }
250 
251 const FFCodec ff_libaom_av1_decoder = {
252     .p.name         = "libaom-av1",
253     .p.long_name    = NULL_IF_CONFIG_SMALL("libaom AV1"),
254     .p.type         = AVMEDIA_TYPE_VIDEO,
255     .p.id           = AV_CODEC_ID_AV1,
256     .priv_data_size = sizeof(AV1DecodeContext),
257     .init           = av1_init,
258     .close          = aom_free,
259     FF_CODEC_DECODE_CB(aom_decode),
260     .p.capabilities = AV_CODEC_CAP_OTHER_THREADS | AV_CODEC_CAP_DR1,
261     .caps_internal  = FF_CODEC_CAP_AUTO_THREADS,
262     .p.profiles     = NULL_IF_CONFIG_SMALL(ff_av1_profiles),
263     .p.wrapper_name = "libaom",
264 };
265