1 /*****************************************************************************
2 * vpx.c: libvpx decoder (VP8/VP9) module
3 *****************************************************************************
4 * Copyright (C) 2013 Rafaël Carré
6 * Authors: Rafaël Carré <funman@videolanorg>
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.
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.
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 *****************************************************************************/
23 /*****************************************************************************
25 *****************************************************************************/
30 #include <vlc_common.h>
31 #include <vlc_plugin.h>
32 #include <vlc_codec.h>
34 #include <vpx/vpx_decoder.h>
35 #include <vpx/vp8dx.h>
36 #include <vpx/vpx_image.h>
39 # include <vpx/vpx_encoder.h>
40 # include <vpx/vp8cx.h>
43 /****************************************************************************
45 ****************************************************************************/
46 static int OpenDecoder(vlc_object_t
*);
47 static void CloseDecoder(vlc_object_t
*);
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
);
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"\
61 /*****************************************************************************
63 *****************************************************************************/
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
)
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 )
85 static void vpx_err_msg(vlc_object_t
*this, struct vpx_codec_ctx
*ctx
,
88 const char *error
= vpx_codec_error(ctx
);
89 const char *detail
= vpx_codec_error_detail(ctx
);
91 detail
= "no specific information";
92 msg_Err(this, msg
, error
, detail
);
95 #define VPX_ERR(this, ctx, msg) vpx_err_msg(VLC_OBJECT(this), ctx, msg ": %s (%s)")
97 /*****************************************************************************
98 * decoder_sys_t: libvpx decoder descriptor
99 *****************************************************************************/
102 struct vpx_codec_ctx ctx
;
107 vlc_fourcc_t i_chroma
;
108 enum vpx_img_fmt i_chroma_id
;
110 uint8_t i_needs_hack
;
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 },
119 { VLC_CODEC_YV12
, VPX_IMG_FMT_YV12
, 8, 0 },
121 { VLC_CODEC_GBR_PLANAR
, VPX_IMG_FMT_I444
, 8, 1 },
122 { VLC_CODEC_GBR_PLANAR_10L
, VPX_IMG_FMT_I44416
, 10, 1 },
124 { VLC_CODEC_I420_10L
, VPX_IMG_FMT_I42016
, 10, 0 },
125 { VLC_CODEC_I422_10L
, VPX_IMG_FMT_I42216
, 10, 0 },
126 { VLC_CODEC_I444_10L
, VPX_IMG_FMT_I44416
, 10, 0 },
128 { VLC_CODEC_I420_12L
, VPX_IMG_FMT_I42016
, 12, 0 },
129 { VLC_CODEC_I422_12L
, VPX_IMG_FMT_I42216
, 12, 0 },
130 { VLC_CODEC_I444_12L
, VPX_IMG_FMT_I44416
, 12, 0 },
132 { VLC_CODEC_I444_16L
, VPX_IMG_FMT_I44416
, 16, 0 },
137 video_color_primaries_t primaries
;
138 video_transfer_func_t transfer
;
139 video_color_space_t space
;
142 const struct video_color vpx_color_mapping_table
[] =
144 [VPX_CS_UNKNOWN
] = { COLOR_PRIMARIES_UNDEF
,
147 [VPX_CS_BT_601
] = { COLOR_PRIMARIES_BT601_525
,
150 [VPX_CS_BT_709
] = { COLOR_PRIMARIES_BT709
,
153 [VPX_CS_SMPTE_170
] = { COLOR_PRIMARIES_SMTPE_170
,
156 [VPX_CS_SMPTE_240
] = { COLOR_PRIMARIES_SMTPE_240
,
157 TRANSFER_FUNC_SMPTE_240
,
159 [VPX_CS_BT_2020
] = { COLOR_PRIMARIES_BT2020
,
160 TRANSFER_FUNC_BT2020
,
161 COLOR_SPACE_BT2020
},
162 [VPX_CS_RESERVED
] = { COLOR_PRIMARIES_UNDEF
,
165 [VPX_CS_SRGB
] = { COLOR_PRIMARIES_SRGB
,
170 static vlc_fourcc_t
FindVlcChroma( struct vpx_image
*img
)
172 uint8_t hack
= (img
->fmt
& VPX_IMG_FMT_I444
) && (img
->cs
== VPX_CS_SRGB
);
174 for( unsigned int i
= 0; i
< ARRAY_SIZE(chroma_table
); i
++ )
175 if( chroma_table
[i
].i_chroma_id
== img
->fmt
&&
176 chroma_table
[i
].i_bitdepth
== img
->bit_depth
&&
177 chroma_table
[i
].i_needs_hack
== hack
)
178 return chroma_table
[i
].i_chroma
;
183 /****************************************************************************
184 * Decode: the whole thing
185 ****************************************************************************/
186 static int Decode(decoder_t
*dec
, block_t
*block
)
188 decoder_sys_t
*p_sys
= dec
->p_sys
;
189 struct vpx_codec_ctx
*ctx
= &p_sys
->ctx
;
191 if (block
== NULL
) /* No Drain */
192 return VLCDEC_SUCCESS
;
194 if (block
->i_flags
& (BLOCK_FLAG_CORRUPTED
)) {
195 block_Release(block
);
196 return VLCDEC_SUCCESS
;
199 /* Associate packet PTS with decoded frame */
200 vlc_tick_t
*pkt_pts
= malloc(sizeof(*pkt_pts
));
202 block_Release(block
);
203 return VLCDEC_SUCCESS
;
206 *pkt_pts
= (block
->i_pts
!= VLC_TICK_INVALID
) ? block
->i_pts
: block
->i_dts
;
209 err
= vpx_codec_decode(ctx
, block
->p_buffer
, block
->i_buffer
, pkt_pts
, 0);
211 block_Release(block
);
213 if (err
!= VPX_CODEC_OK
) {
215 VPX_ERR(dec
, ctx
, "Failed to decode frame");
216 if (err
== VPX_CODEC_UNSUP_BITSTREAM
)
217 return VLCDEC_ECRITICAL
;
219 return VLCDEC_SUCCESS
;
222 const void *iter
= NULL
;
223 struct vpx_image
*img
= vpx_codec_get_frame(ctx
, &iter
);
226 return VLCDEC_SUCCESS
;
229 /* fetches back the PTS */
230 pkt_pts
= img
->user_priv
;
231 vlc_tick_t pts
= *pkt_pts
;
234 dec
->fmt_out
.i_codec
= FindVlcChroma(img
);
236 if( dec
->fmt_out
.i_codec
== 0 ) {
237 msg_Err(dec
, "Unsupported output colorspace %d", img
->fmt
);
238 return VLCDEC_SUCCESS
;
241 video_format_t
*v
= &dec
->fmt_out
.video
;
243 if (img
->d_w
!= v
->i_visible_width
|| img
->d_h
!= v
->i_visible_height
) {
244 v
->i_visible_width
= dec
->fmt_out
.video
.i_width
= img
->d_w
;
245 v
->i_visible_height
= dec
->fmt_out
.video
.i_height
= img
->d_h
;
248 if( !dec
->fmt_out
.video
.i_sar_num
|| !dec
->fmt_out
.video
.i_sar_den
)
250 dec
->fmt_out
.video
.i_sar_num
= 1;
251 dec
->fmt_out
.video
.i_sar_den
= 1;
254 if(dec
->fmt_in
.video
.primaries
== COLOR_PRIMARIES_UNDEF
&&
255 img
->cs
>= 0 && img
->cs
< ARRAY_SIZE(vpx_color_mapping_table
))
257 v
->primaries
= vpx_color_mapping_table
[img
->cs
].primaries
;
258 v
->transfer
= vpx_color_mapping_table
[img
->cs
].transfer
;
259 v
->space
= vpx_color_mapping_table
[img
->cs
].space
;
260 v
->color_range
= img
->range
== VPX_CR_FULL_RANGE
? COLOR_RANGE_FULL
: COLOR_RANGE_LIMITED
;
263 dec
->fmt_out
.video
.projection_mode
= dec
->fmt_in
.video
.projection_mode
;
264 dec
->fmt_out
.video
.multiview_mode
= dec
->fmt_in
.video
.multiview_mode
;
265 dec
->fmt_out
.video
.pose
= dec
->fmt_in
.video
.pose
;
267 if (decoder_UpdateVideoFormat(dec
))
268 return VLCDEC_SUCCESS
;
269 picture_t
*pic
= decoder_NewPicture(dec
);
271 return VLCDEC_SUCCESS
;
273 for (int plane
= 0; plane
< pic
->i_planes
; plane
++ ) {
274 uint8_t *src
= img
->planes
[plane
];
275 uint8_t *dst
= pic
->p
[plane
].p_pixels
;
276 int src_stride
= img
->stride
[plane
];
277 int dst_stride
= pic
->p
[plane
].i_pitch
;
279 int size
= __MIN( src_stride
, dst_stride
);
280 for( int line
= 0; line
< pic
->p
[plane
].i_visible_lines
; line
++ ) {
281 memcpy( dst
, src
, size
);
287 pic
->b_progressive
= true; /* codec does not support interlacing */
290 decoder_QueueVideo(dec
, pic
);
291 return VLCDEC_SUCCESS
;
294 /*****************************************************************************
295 * OpenDecoder: probe the decoder
296 *****************************************************************************/
297 static int OpenDecoder(vlc_object_t
*p_this
)
299 decoder_t
*dec
= (decoder_t
*)p_this
;
300 const struct vpx_codec_iface
*iface
;
303 switch (dec
->fmt_in
.i_codec
)
305 #ifdef ENABLE_VP8_DECODER
308 iface
= &vpx_codec_vp8_dx_algo
;
312 #ifdef ENABLE_VP9_DECODER
314 iface
= &vpx_codec_vp9_dx_algo
;
322 decoder_sys_t
*sys
= malloc(sizeof(*sys
));
327 struct vpx_codec_dec_cfg deccfg
= {
328 .threads
= __MIN(vlc_GetCPUCount(), 16)
331 msg_Dbg(p_this
, "VP%d: using libvpx version %s (build options %s)",
332 vp_version
, vpx_codec_version_str(), vpx_codec_build_config());
334 if (vpx_codec_dec_init(&sys
->ctx
, iface
, &deccfg
, 0) != VPX_CODEC_OK
) {
335 VPX_ERR(p_this
, &sys
->ctx
, "Failed to initialize decoder");
337 return VLC_EGENERIC
;;
340 dec
->pf_decode
= Decode
;
342 dec
->fmt_out
.video
.i_width
= dec
->fmt_in
.video
.i_width
;
343 dec
->fmt_out
.video
.i_height
= dec
->fmt_in
.video
.i_height
;
345 if (dec
->fmt_in
.video
.i_sar_num
> 0 && dec
->fmt_in
.video
.i_sar_den
> 0) {
346 dec
->fmt_out
.video
.i_sar_num
= dec
->fmt_in
.video
.i_sar_num
;
347 dec
->fmt_out
.video
.i_sar_den
= dec
->fmt_in
.video
.i_sar_den
;
353 /*****************************************************************************
354 * CloseDecoder: decoder destruction
355 *****************************************************************************/
356 static void CloseDecoder(vlc_object_t
*p_this
)
358 decoder_t
*dec
= (decoder_t
*)p_this
;
359 decoder_sys_t
*sys
= dec
->p_sys
;
362 const void *iter
= NULL
;
364 struct vpx_image
*img
= vpx_codec_get_frame(&sys
->ctx
, &iter
);
367 free(img
->user_priv
);
370 vpx_codec_destroy(&sys
->ctx
);
377 /*****************************************************************************
378 * encoder_sys_t: libvpx encoder descriptor
379 *****************************************************************************/
382 struct vpx_codec_ctx ctx
;
383 unsigned long quality
;
386 /*****************************************************************************
387 * OpenEncoder: probe the encoder
388 *****************************************************************************/
389 static int OpenEncoder(vlc_object_t
*p_this
)
391 encoder_t
*p_enc
= (encoder_t
*)p_this
;
392 encoder_sys_t
*p_sys
;
394 /* Allocate the memory needed to store the encoder's structure */
395 p_sys
= malloc(sizeof(*p_sys
));
398 p_enc
->p_sys
= p_sys
;
400 const struct vpx_codec_iface
*iface
;
403 switch (p_enc
->fmt_out
.i_codec
)
405 #ifdef ENABLE_VP8_ENCODER
407 iface
= &vpx_codec_vp8_cx_algo
;
411 #ifdef ENABLE_VP9_ENCODER
413 iface
= &vpx_codec_vp9_cx_algo
;
422 struct vpx_codec_enc_cfg enccfg
= {0};
423 vpx_codec_enc_config_default(iface
, &enccfg
, 0);
424 enccfg
.g_threads
= __MIN(vlc_GetCPUCount(), 4);
425 enccfg
.g_w
= p_enc
->fmt_in
.video
.i_visible_width
;
426 enccfg
.g_h
= p_enc
->fmt_in
.video
.i_visible_height
;
428 msg_Dbg(p_this
, "VP%d: using libvpx version %s (build options %s)",
429 vp_version
, vpx_codec_version_str(), vpx_codec_build_config());
431 struct vpx_codec_ctx
*ctx
= &p_sys
->ctx
;
432 if (vpx_codec_enc_init(ctx
, iface
, &enccfg
, 0) != VPX_CODEC_OK
) {
433 VPX_ERR(p_this
, ctx
, "Failed to initialize encoder");
438 p_enc
->pf_encode_video
= Encode
;
439 p_enc
->fmt_in
.i_codec
= VLC_CODEC_I420
;
440 config_ChainParse(p_enc
, ENC_CFG_PREFIX
, ppsz_sout_options
, p_enc
->p_cfg
);
442 /* Deadline (in ms) to spend in encoder */
443 switch (var_GetInteger(p_enc
, ENC_CFG_PREFIX
"quality-mode")) {
445 p_sys
->quality
= VPX_DL_REALTIME
;
448 p_sys
->quality
= VPX_DL_BEST_QUALITY
;
451 p_sys
->quality
= VPX_DL_GOOD_QUALITY
;
458 /****************************************************************************
459 * Encode: the whole thing
460 ****************************************************************************/
461 static block_t
*Encode(encoder_t
*p_enc
, picture_t
*p_pict
)
463 encoder_sys_t
*p_sys
= p_enc
->p_sys
;
464 struct vpx_codec_ctx
*ctx
= &p_sys
->ctx
;
466 if (!p_pict
) return NULL
;
468 vpx_image_t img
= {0};
469 unsigned i_w
= p_enc
->fmt_in
.video
.i_visible_width
;
470 unsigned i_h
= p_enc
->fmt_in
.video
.i_visible_height
;
472 /* Create and initialize the vpx_image */
473 if (!vpx_img_wrap(&img
, VPX_IMG_FMT_I420
, i_w
, i_h
, 32, p_pict
->p
[0].p_pixels
)) {
474 VPX_ERR(p_enc
, ctx
, "Failed to wrap image");
478 /* Correct chroma plane offsets. */
479 for (int plane
= 1; plane
< p_pict
->i_planes
; plane
++) {
480 img
.planes
[plane
] = p_pict
->p
[plane
].p_pixels
;
481 img
.stride
[plane
] = p_pict
->p
[plane
].i_pitch
;
486 vpx_codec_err_t res
= vpx_codec_encode(ctx
, &img
, p_pict
->date
, 1,
487 flags
, p_sys
->quality
);
488 if (res
!= VPX_CODEC_OK
) {
489 VPX_ERR(p_enc
, ctx
, "Failed to encode frame");
494 const vpx_codec_cx_pkt_t
*pkt
= NULL
;
495 vpx_codec_iter_t iter
= NULL
;
496 block_t
*p_out
= NULL
;
497 while ((pkt
= vpx_codec_get_cx_data(ctx
, &iter
)) != NULL
)
499 if (pkt
->kind
== VPX_CODEC_CX_FRAME_PKT
)
501 int keyframe
= pkt
->data
.frame
.flags
& VPX_FRAME_IS_KEY
;
502 block_t
*p_block
= block_Alloc(pkt
->data
.frame
.sz
);
503 if (unlikely(p_block
== NULL
))
505 block_ChainRelease(p_out
);
510 memcpy(p_block
->p_buffer
, pkt
->data
.frame
.buf
, pkt
->data
.frame
.sz
);
511 p_block
->i_dts
= p_block
->i_pts
= pkt
->data
.frame
.pts
;
513 p_block
->i_flags
|= BLOCK_FLAG_TYPE_I
;
514 block_ChainAppend(&p_out
, p_block
);
521 /*****************************************************************************
522 * CloseEncoder: encoder destruction
523 *****************************************************************************/
524 static void CloseEncoder(vlc_object_t
*p_this
)
526 encoder_t
*p_enc
= (encoder_t
*)p_this
;
527 encoder_sys_t
*p_sys
= p_enc
->p_sys
;
528 if (vpx_codec_destroy(&p_sys
->ctx
))
529 VPX_ERR(p_this
, &p_sys
->ctx
, "Failed to destroy codec");
533 #endif /* ENABLE_SOUT */