codec: vpx: do proper color mapping
[vlc.git] / modules / codec / vpx.c
1 /*****************************************************************************
2  * vpx.c: libvpx decoder (VP8/VP9) module
3  *****************************************************************************
4  * Copyright (C) 2013 Rafaël Carré
5  *
6  * Authors: Rafaël Carré <funman@videolanorg>
7  *
8  * This program is free software; you can redistribute it and/or modify it
9  * under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation; either version 2.1 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this program; if not, write to the Free Software Foundation,
20  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21  *****************************************************************************/
22
23 /*****************************************************************************
24  * Preamble
25  *****************************************************************************/
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29
30 #include <vlc_common.h>
31 #include <vlc_plugin.h>
32 #include <vlc_codec.h>
33
34 #include <vpx/vpx_decoder.h>
35 #include <vpx/vp8dx.h>
36 #include <vpx/vpx_image.h>
37
38 #ifdef ENABLE_SOUT
39 # include <vpx/vpx_encoder.h>
40 # include <vpx/vp8cx.h>
41 #endif
42
43 /****************************************************************************
44  * Local prototypes
45  ****************************************************************************/
46 static int OpenDecoder(vlc_object_t *);
47 static void CloseDecoder(vlc_object_t *);
48 #ifdef ENABLE_SOUT
49 static const char *const ppsz_sout_options[] = { "quality-mode", NULL };
50 static int OpenEncoder(vlc_object_t *);
51 static void CloseEncoder(vlc_object_t *);
52 static block_t *Encode(encoder_t *p_enc, picture_t *p_pict);
53
54 #define QUALITY_MODE_TEXT N_("Quality mode")
55 #define QUALITY_MODE_LONGTEXT N_("Quality setting which will determine max encoding time\n" \
56         " - 0: Good quality\n"\
57         " - 1: Realtime\n"\
58         " - 2: Best quality")
59 #endif
60
61 /*****************************************************************************
62  * Module descriptor
63  *****************************************************************************/
64
65 vlc_module_begin ()
66     set_shortname("vpx")
67     set_description(N_("WebM video decoder"))
68     set_capability("video decoder", 60)
69     set_callbacks(OpenDecoder, CloseDecoder)
70     set_category(CAT_INPUT)
71     set_subcategory(SUBCAT_INPUT_VCODEC)
72 #ifdef ENABLE_SOUT
73     add_submodule()
74     set_shortname("vpx")
75     set_capability("encoder", 60)
76     set_description(N_("WebM video encoder"))
77     set_callbacks(OpenEncoder, CloseEncoder)
78 #   define ENC_CFG_PREFIX "sout-vpx-"
79     add_integer( ENC_CFG_PREFIX "quality-mode", VPX_DL_GOOD_QUALITY, QUALITY_MODE_TEXT,
80                  QUALITY_MODE_LONGTEXT, true )
81         change_integer_range( 0, 2 )
82 #endif
83 vlc_module_end ()
84
85 static void vpx_err_msg(vlc_object_t *this, struct vpx_codec_ctx *ctx,
86                         const char *msg)
87 {
88     const char *error  = vpx_codec_error(ctx);
89     const char *detail = vpx_codec_error_detail(ctx);
90     if (!detail)
91         detail = "no specific information";
92     msg_Err(this, msg, error, detail);
93 }
94
95 #define VPX_ERR(this, ctx, msg) vpx_err_msg(VLC_OBJECT(this), ctx, msg ": %s (%s)")
96
97 /*****************************************************************************
98  * decoder_sys_t: libvpx decoder descriptor
99  *****************************************************************************/
100 typedef struct
101 {
102     struct vpx_codec_ctx ctx;
103 } decoder_sys_t;
104
105 static const struct
106 {
107     vlc_fourcc_t     i_chroma;
108     enum vpx_img_fmt i_chroma_id;
109     uint8_t          i_bitdepth;
110     uint8_t          i_needs_hack;
111
112 } chroma_table[] =
113 {
114     { VLC_CODEC_I420, VPX_IMG_FMT_I420, 8, 0 },
115     { VLC_CODEC_I422, VPX_IMG_FMT_I422, 8, 0 },
116     { VLC_CODEC_I444, VPX_IMG_FMT_I444, 8, 0 },
117     { VLC_CODEC_I440, VPX_IMG_FMT_I440, 8, 0 },
118
119     { VLC_CODEC_YV12, VPX_IMG_FMT_YV12, 8, 0 },
120     { VLC_CODEC_YUVA, VPX_IMG_FMT_444A, 8, 0 },
121     { VLC_CODEC_YUYV, VPX_IMG_FMT_YUY2, 8, 0 },
122     { VLC_CODEC_UYVY, VPX_IMG_FMT_UYVY, 8, 0 },
123     { VLC_CODEC_YVYU, VPX_IMG_FMT_YVYU, 8, 0 },
124
125     { VLC_CODEC_RGB15, VPX_IMG_FMT_RGB555, 8, 0 },
126     { VLC_CODEC_RGB16, VPX_IMG_FMT_RGB565, 8, 0 },
127     { VLC_CODEC_RGB24, VPX_IMG_FMT_RGB24, 8, 0 },
128     { VLC_CODEC_RGB32, VPX_IMG_FMT_RGB32, 8, 0 },
129
130     { VLC_CODEC_ARGB, VPX_IMG_FMT_ARGB, 8, 0 },
131     { VLC_CODEC_BGRA, VPX_IMG_FMT_ARGB_LE, 8, 0 },
132
133     { VLC_CODEC_GBR_PLANAR, VPX_IMG_FMT_I444, 8, 1 },
134     { VLC_CODEC_GBR_PLANAR_10L, VPX_IMG_FMT_I44416, 10, 1 },
135
136     { VLC_CODEC_I420_10L, VPX_IMG_FMT_I42016, 10, 0 },
137     { VLC_CODEC_I422_10L, VPX_IMG_FMT_I42216, 10, 0 },
138     { VLC_CODEC_I444_10L, VPX_IMG_FMT_I44416, 10, 0 },
139
140     { VLC_CODEC_I420_12L, VPX_IMG_FMT_I42016, 12, 0 },
141     { VLC_CODEC_I422_12L, VPX_IMG_FMT_I42216, 12, 0 },
142     { VLC_CODEC_I444_12L, VPX_IMG_FMT_I44416, 12, 0 },
143
144     { VLC_CODEC_I444_16L, VPX_IMG_FMT_I44416, 16, 0 },
145 };
146
147 struct video_color
148 {
149     video_color_primaries_t primaries;
150     video_transfer_func_t transfer;
151     video_color_space_t space;
152 };
153
154 const struct video_color vpx_color_mapping_table[] =
155 {
156     [VPX_CS_UNKNOWN]   =  { COLOR_PRIMARIES_UNDEF,
157                             TRANSFER_FUNC_UNDEF,
158                             COLOR_SPACE_UNDEF },
159     [VPX_CS_BT_601]    =  { COLOR_PRIMARIES_BT601_525,
160                             TRANSFER_FUNC_BT709,
161                             COLOR_SPACE_BT601 },
162     [VPX_CS_BT_709]    =  { COLOR_PRIMARIES_BT709,
163                             TRANSFER_FUNC_BT709,
164                             COLOR_SPACE_BT709 },
165     [VPX_CS_SMPTE_170] =  { COLOR_PRIMARIES_SMTPE_170,
166                             TRANSFER_FUNC_BT709,
167                             COLOR_SPACE_BT601 },
168     [VPX_CS_SMPTE_240] =  { COLOR_PRIMARIES_SMTPE_240,
169                             TRANSFER_FUNC_SMPTE_240,
170                             COLOR_SPACE_UNDEF },
171     [VPX_CS_BT_2020]   =  { COLOR_PRIMARIES_BT2020,
172                             TRANSFER_FUNC_BT2020,
173                             COLOR_SPACE_BT2020 },
174     [VPX_CS_RESERVED]  =  { COLOR_PRIMARIES_UNDEF,
175                             TRANSFER_FUNC_UNDEF,
176                             COLOR_SPACE_UNDEF },
177     [VPX_CS_SRGB]      =  { COLOR_PRIMARIES_SRGB,
178                             TRANSFER_FUNC_SRGB,
179                             COLOR_SPACE_UNDEF },
180 };
181
182 static vlc_fourcc_t FindVlcChroma( struct vpx_image *img )
183 {
184     uint8_t hack = (img->fmt & VPX_IMG_FMT_I444) && (img->cs == VPX_CS_SRGB);
185
186     for( unsigned int i = 0; i < ARRAY_SIZE(chroma_table); i++ )
187         if( chroma_table[i].i_chroma_id == img->fmt &&
188             chroma_table[i].i_bitdepth == img->bit_depth &&
189             chroma_table[i].i_needs_hack == hack )
190             return chroma_table[i].i_chroma;
191
192     return 0;
193 }
194
195 /****************************************************************************
196  * Decode: the whole thing
197  ****************************************************************************/
198 static int Decode(decoder_t *dec, block_t *block)
199 {
200     decoder_sys_t *p_sys = dec->p_sys;
201     struct vpx_codec_ctx *ctx = &p_sys->ctx;
202
203     if (block == NULL) /* No Drain */
204         return VLCDEC_SUCCESS;
205
206     if (block->i_flags & (BLOCK_FLAG_CORRUPTED)) {
207         block_Release(block);
208         return VLCDEC_SUCCESS;
209     }
210
211     /* Associate packet PTS with decoded frame */
212     vlc_tick_t *pkt_pts = malloc(sizeof(*pkt_pts));
213     if (!pkt_pts) {
214         block_Release(block);
215         return VLCDEC_SUCCESS;
216     }
217
218     *pkt_pts = (block->i_pts != VLC_TICK_INVALID) ? block->i_pts : block->i_dts;
219
220     vpx_codec_err_t err;
221     err = vpx_codec_decode(ctx, block->p_buffer, block->i_buffer, pkt_pts, 0);
222
223     block_Release(block);
224
225     if (err != VPX_CODEC_OK) {
226         free(pkt_pts);
227         VPX_ERR(dec, ctx, "Failed to decode frame");
228         if (err == VPX_CODEC_UNSUP_BITSTREAM)
229             return VLCDEC_ECRITICAL;
230         else
231             return VLCDEC_SUCCESS;
232     }
233
234     const void *iter = NULL;
235     struct vpx_image *img = vpx_codec_get_frame(ctx, &iter);
236     if (!img) {
237         free(pkt_pts);
238         return VLCDEC_SUCCESS;
239     }
240
241     /* fetches back the PTS */
242     pkt_pts = img->user_priv;
243     vlc_tick_t pts = *pkt_pts;
244     free(pkt_pts);
245
246     dec->fmt_out.i_codec = FindVlcChroma(img);
247
248     if( dec->fmt_out.i_codec == 0 ) {
249         msg_Err(dec, "Unsupported output colorspace %d", img->fmt);
250         return VLCDEC_SUCCESS;
251     }
252
253     video_format_t *v = &dec->fmt_out.video;
254
255     if (img->d_w != v->i_visible_width || img->d_h != v->i_visible_height) {
256         v->i_visible_width = dec->fmt_out.video.i_width = img->d_w;
257         v->i_visible_height = dec->fmt_out.video.i_height = img->d_h;
258     }
259
260     if( !dec->fmt_out.video.i_sar_num || !dec->fmt_out.video.i_sar_den )
261     {
262         dec->fmt_out.video.i_sar_num = 1;
263         dec->fmt_out.video.i_sar_den = 1;
264     }
265
266     if(dec->fmt_in.video.primaries == COLOR_PRIMARIES_UNDEF &&
267        img->cs >= 0 && img->cs < ARRAY_SIZE(vpx_color_mapping_table))
268     {
269         v->primaries = vpx_color_mapping_table[img->cs].primaries;
270         v->transfer = vpx_color_mapping_table[img->cs].transfer;
271         v->space = vpx_color_mapping_table[img->cs].space;
272         v->b_color_range_full = img->range == VPX_CR_FULL_RANGE;
273     }
274
275     dec->fmt_out.video.projection_mode = dec->fmt_in.video.projection_mode;
276     dec->fmt_out.video.multiview_mode = dec->fmt_in.video.multiview_mode;
277     dec->fmt_out.video.pose = dec->fmt_in.video.pose;
278
279     if (decoder_UpdateVideoFormat(dec))
280         return VLCDEC_SUCCESS;
281     picture_t *pic = decoder_NewPicture(dec);
282     if (!pic)
283         return VLCDEC_SUCCESS;
284
285     for (int plane = 0; plane < pic->i_planes; plane++ ) {
286         uint8_t *src = img->planes[plane];
287         uint8_t *dst = pic->p[plane].p_pixels;
288         int src_stride = img->stride[plane];
289         int dst_stride = pic->p[plane].i_pitch;
290
291         int size = __MIN( src_stride, dst_stride );
292         for( int line = 0; line < pic->p[plane].i_visible_lines; line++ ) {
293             memcpy( dst, src, size );
294             src += src_stride;
295             dst += dst_stride;
296         }
297     }
298
299     pic->b_progressive = true; /* codec does not support interlacing */
300     pic->date = pts;
301
302     decoder_QueueVideo(dec, pic);
303     return VLCDEC_SUCCESS;
304 }
305
306 /*****************************************************************************
307  * OpenDecoder: probe the decoder
308  *****************************************************************************/
309 static int OpenDecoder(vlc_object_t *p_this)
310 {
311     decoder_t *dec = (decoder_t *)p_this;
312     const struct vpx_codec_iface *iface;
313     int vp_version;
314
315     switch (dec->fmt_in.i_codec)
316     {
317 #ifdef ENABLE_VP8_DECODER
318     case VLC_CODEC_WEBP:
319     case VLC_CODEC_VP8:
320         iface = &vpx_codec_vp8_dx_algo;
321         vp_version = 8;
322         break;
323 #endif
324 #ifdef ENABLE_VP9_DECODER
325     case VLC_CODEC_VP9:
326         iface = &vpx_codec_vp9_dx_algo;
327         vp_version = 9;
328         break;
329 #endif
330     default:
331         return VLC_EGENERIC;
332     }
333
334     decoder_sys_t *sys = malloc(sizeof(*sys));
335     if (!sys)
336         return VLC_ENOMEM;
337     dec->p_sys = sys;
338
339     struct vpx_codec_dec_cfg deccfg = {
340         .threads = __MIN(vlc_GetCPUCount(), 16)
341     };
342
343     msg_Dbg(p_this, "VP%d: using libvpx version %s (build options %s)",
344         vp_version, vpx_codec_version_str(), vpx_codec_build_config());
345
346     if (vpx_codec_dec_init(&sys->ctx, iface, &deccfg, 0) != VPX_CODEC_OK) {
347         VPX_ERR(p_this, &sys->ctx, "Failed to initialize decoder");
348         free(sys);
349         return VLC_EGENERIC;;
350     }
351
352     dec->pf_decode = Decode;
353
354     dec->fmt_out.video.i_width = dec->fmt_in.video.i_width;
355     dec->fmt_out.video.i_height = dec->fmt_in.video.i_height;
356
357     if (dec->fmt_in.video.i_sar_num > 0 && dec->fmt_in.video.i_sar_den > 0) {
358         dec->fmt_out.video.i_sar_num = dec->fmt_in.video.i_sar_num;
359         dec->fmt_out.video.i_sar_den = dec->fmt_in.video.i_sar_den;
360     }
361
362     return VLC_SUCCESS;
363 }
364
365 /*****************************************************************************
366  * CloseDecoder: decoder destruction
367  *****************************************************************************/
368 static void CloseDecoder(vlc_object_t *p_this)
369 {
370     decoder_t *dec = (decoder_t *)p_this;
371     decoder_sys_t *sys = dec->p_sys;
372
373     /* Free our PTS */
374     const void *iter = NULL;
375     for (;;) {
376         struct vpx_image *img = vpx_codec_get_frame(&sys->ctx, &iter);
377         if (!img)
378             break;
379         free(img->user_priv);
380     }
381
382     vpx_codec_destroy(&sys->ctx);
383
384     free(sys);
385 }
386
387 #ifdef ENABLE_SOUT
388
389 /*****************************************************************************
390  * encoder_sys_t: libvpx encoder descriptor
391  *****************************************************************************/
392 typedef struct
393 {
394     struct vpx_codec_ctx ctx;
395     unsigned long quality;
396 } encoder_sys_t;
397
398 /*****************************************************************************
399  * OpenEncoder: probe the encoder
400  *****************************************************************************/
401 static int OpenEncoder(vlc_object_t *p_this)
402 {
403     encoder_t *p_enc = (encoder_t *)p_this;
404     encoder_sys_t *p_sys;
405
406     /* Allocate the memory needed to store the encoder's structure */
407     p_sys = malloc(sizeof(*p_sys));
408     if (p_sys == NULL)
409         return VLC_ENOMEM;
410     p_enc->p_sys = p_sys;
411
412     const struct vpx_codec_iface *iface;
413     int vp_version;
414
415     switch (p_enc->fmt_out.i_codec)
416     {
417 #ifdef ENABLE_VP8_ENCODER
418     case VLC_CODEC_VP8:
419         iface = &vpx_codec_vp8_cx_algo;
420         vp_version = 8;
421         break;
422 #endif
423 #ifdef ENABLE_VP9_ENCODER
424     case VLC_CODEC_VP9:
425         iface = &vpx_codec_vp9_cx_algo;
426         vp_version = 9;
427         break;
428 #endif
429     default:
430         free(p_sys);
431         return VLC_EGENERIC;
432     }
433
434     struct vpx_codec_enc_cfg enccfg = {0};
435     vpx_codec_enc_config_default(iface, &enccfg, 0);
436     enccfg.g_threads = __MIN(vlc_GetCPUCount(), 4);
437     enccfg.g_w = p_enc->fmt_in.video.i_visible_width;
438     enccfg.g_h = p_enc->fmt_in.video.i_visible_height;
439
440     msg_Dbg(p_this, "VP%d: using libvpx version %s (build options %s)",
441         vp_version, vpx_codec_version_str(), vpx_codec_build_config());
442
443     struct vpx_codec_ctx *ctx = &p_sys->ctx;
444     if (vpx_codec_enc_init(ctx, iface, &enccfg, 0) != VPX_CODEC_OK) {
445         VPX_ERR(p_this, ctx, "Failed to initialize encoder");
446         free(p_sys);
447         return VLC_EGENERIC;
448     }
449
450     p_enc->pf_encode_video = Encode;
451     p_enc->fmt_in.i_codec = VLC_CODEC_I420;
452     config_ChainParse(p_enc, ENC_CFG_PREFIX, ppsz_sout_options, p_enc->p_cfg);
453
454     /* Deadline (in ms) to spend in encoder */
455     switch (var_GetInteger(p_enc, ENC_CFG_PREFIX "quality-mode")) {
456         case 1:
457             p_sys->quality = VPX_DL_REALTIME;
458             break;
459         case 2:
460             p_sys->quality = VPX_DL_BEST_QUALITY;
461             break;
462         default:
463             p_sys->quality = VPX_DL_GOOD_QUALITY;
464             break;
465     }
466
467     return VLC_SUCCESS;
468 }
469
470 /****************************************************************************
471  * Encode: the whole thing
472  ****************************************************************************/
473 static block_t *Encode(encoder_t *p_enc, picture_t *p_pict)
474 {
475     encoder_sys_t *p_sys = p_enc->p_sys;
476     struct vpx_codec_ctx *ctx = &p_sys->ctx;
477
478     if (!p_pict) return NULL;
479
480     vpx_image_t img = {0};
481     unsigned i_w = p_enc->fmt_in.video.i_visible_width;
482     unsigned i_h = p_enc->fmt_in.video.i_visible_height;
483
484     /* Create and initialize the vpx_image */
485     if (!vpx_img_wrap(&img, VPX_IMG_FMT_I420, i_w, i_h, 32, p_pict->p[0].p_pixels)) {
486         VPX_ERR(p_enc, ctx, "Failed to wrap image");
487         return NULL;
488     }
489
490     /* Correct chroma plane offsets. */
491     for (int plane = 1; plane < p_pict->i_planes; plane++) {
492         img.planes[plane] = p_pict->p[plane].p_pixels;
493         img.stride[plane] = p_pict->p[plane].i_pitch;
494     }
495
496     int flags = 0;
497
498     vpx_codec_err_t res = vpx_codec_encode(ctx, &img, p_pict->date, 1,
499      flags, p_sys->quality);
500     if (res != VPX_CODEC_OK) {
501         VPX_ERR(p_enc, ctx, "Failed to encode frame");
502         vpx_img_free(&img);
503         return NULL;
504     }
505
506     const vpx_codec_cx_pkt_t *pkt = NULL;
507     vpx_codec_iter_t iter = NULL;
508     block_t *p_out = NULL;
509     while ((pkt = vpx_codec_get_cx_data(ctx, &iter)) != NULL)
510     {
511         if (pkt->kind == VPX_CODEC_CX_FRAME_PKT)
512         {
513             int keyframe = pkt->data.frame.flags & VPX_FRAME_IS_KEY;
514             block_t *p_block = block_Alloc(pkt->data.frame.sz);
515             if (unlikely(p_block == NULL))
516             {
517                 block_ChainRelease(p_out);
518                 p_out = NULL;
519                 break;
520             }
521
522             memcpy(p_block->p_buffer, pkt->data.frame.buf, pkt->data.frame.sz);
523             p_block->i_dts = p_block->i_pts = pkt->data.frame.pts;
524             if (keyframe)
525                 p_block->i_flags |= BLOCK_FLAG_TYPE_I;
526             block_ChainAppend(&p_out, p_block);
527         }
528     }
529     vpx_img_free(&img);
530     return p_out;
531 }
532
533 /*****************************************************************************
534  * CloseEncoder: encoder destruction
535  *****************************************************************************/
536 static void CloseEncoder(vlc_object_t *p_this)
537 {
538     encoder_t *p_enc = (encoder_t *)p_this;
539     encoder_sys_t *p_sys = p_enc->p_sys;
540     if (vpx_codec_destroy(&p_sys->ctx))
541         VPX_ERR(p_this, &p_sys->ctx, "Failed to destroy codec");
542     free(p_sys);
543 }
544
545 #endif  /* ENABLE_SOUT */