1 /*****************************************************************************
2 * aom.c: libaom encoder and decoder (AV1) module
3 *****************************************************************************
4 * Copyright (C) 2016 VLC authors and VideoLAN
6 * Authors: Tristan Matthews <tmatth@videolan.org>
7 * Based on vpx.c by: Rafaël Carré <funman@videolan.org>
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU Lesser General Public License as published by
11 * the Free Software Foundation; either version 2.1 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this program; if not, write to the Free Software Foundation,
21 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
31 #include <vlc_common.h>
32 #include <vlc_plugin.h>
33 #include <vlc_codec.h>
35 #include <aom/aom_decoder.h>
36 #include <aom/aomdx.h>
39 # include <aom/aom_encoder.h>
40 # include <aom/aomcx.h>
41 # include <aom/aom_image.h>
42 # define SOUT_CFG_PREFIX "sout-aom-"
45 #include "../packetizer/iso_color_tables.h"
47 /****************************************************************************
49 ****************************************************************************/
50 static int OpenDecoder(vlc_object_t
*);
51 static void CloseDecoder(vlc_object_t
*);
53 static int OpenEncoder(vlc_object_t
*);
54 static void CloseEncoder(vlc_object_t
*);
55 static block_t
*Encode(encoder_t
*p_enc
, picture_t
*p_pict
);
57 static const int pi_enc_bitdepth_values_list
[] =
59 static const char *const ppsz_enc_bitdepth_text
[] =
60 { N_("8 bpp"), N_("10 bpp"), N_("12 bpp") };
63 /*****************************************************************************
65 *****************************************************************************/
69 set_description(N_("AOM video decoder"))
70 set_capability("video decoder", 100)
71 set_callbacks(OpenDecoder
, CloseDecoder
)
72 set_category(CAT_INPUT
)
73 set_subcategory(SUBCAT_INPUT_VCODEC
)
77 set_capability("encoder", 101)
78 set_description(N_("AOM video encoder"))
79 set_callbacks(OpenEncoder
, CloseEncoder
)
80 add_integer( SOUT_CFG_PREFIX
"profile", 0, "Profile", NULL
, true )
81 change_integer_range( 0, 3 )
82 add_integer( SOUT_CFG_PREFIX
"bitdepth", 8, "Bit Depth", NULL
, true )
83 change_integer_list( pi_enc_bitdepth_values_list
, ppsz_enc_bitdepth_text
)
84 add_integer( SOUT_CFG_PREFIX
"tile-rows", 0, "Tile Rows (in log2 units)", NULL
, true )
85 change_integer_range( 0, 6 ) /* 1 << 6 == MAX_TILE_ROWS */
86 add_integer( SOUT_CFG_PREFIX
"tile-columns", 0, "Tile Columns (in log2 units)", NULL
, true )
87 change_integer_range( 0, 6 ) /* 1 << 6 == MAX_TILE_COLS */
88 add_integer( SOUT_CFG_PREFIX
"cpu-used", 1, "Speed setting", NULL
, true )
89 change_integer_range( 0, 8 ) /* good: 0-5, realtime: 6-8 */
90 add_integer( SOUT_CFG_PREFIX
"lag-in-frames", 16, "Maximum number of lookahead frames", NULL
, true )
91 change_integer_range(0, 70 /* MAX_LAG_BUFFERS + MAX_LAP_BUFFERS */ )
92 add_integer( SOUT_CFG_PREFIX
"usage", 0, "Usage (0: good, 1: realtime)", NULL
, true )
93 change_integer_range( 0, 1 )
94 add_integer( SOUT_CFG_PREFIX
"rc-end-usage", 1, "Usage (0: VBR, 1: CBR, 2: CQ, 3: Q)", NULL
, true )
95 change_integer_range( 0, 4 )
96 #ifdef AOM_CTRL_AV1E_SET_ROW_MT
97 add_bool( SOUT_CFG_PREFIX
"row-mt", false, "Row Multithreading", NULL
, true )
102 static void aom_err_msg(vlc_object_t
*this, aom_codec_ctx_t
*ctx
,
105 const char *error
= aom_codec_error(ctx
);
106 const char *detail
= aom_codec_error_detail(ctx
);
108 detail
= "no specific information";
109 msg_Err(this, msg
, error
, detail
);
112 #define AOM_ERR(this, ctx, msg) aom_err_msg(VLC_OBJECT(this), ctx, msg ": %s (%s)")
113 #define AOM_MAX_FRAMES_DEPTH 64
115 /*****************************************************************************
116 * decoder_sys_t: libaom decoder descriptor
117 *****************************************************************************/
126 struct frame_priv_s frame_priv
[AOM_MAX_FRAMES_DEPTH
];
127 unsigned i_next_frame_priv
;
132 vlc_fourcc_t i_chroma
;
133 enum aom_img_fmt i_chroma_id
;
135 uint8_t i_needs_hack
;
139 { VLC_CODEC_I420
, AOM_IMG_FMT_I420
, 8, 0 },
140 { VLC_CODEC_I422
, AOM_IMG_FMT_I422
, 8, 0 },
141 { VLC_CODEC_I444
, AOM_IMG_FMT_I444
, 8, 0 },
143 { VLC_CODEC_YV12
, AOM_IMG_FMT_YV12
, 8, 0 },
145 { VLC_CODEC_GBR_PLANAR
, AOM_IMG_FMT_I444
, 8, 1 },
146 { VLC_CODEC_GBR_PLANAR_10L
, AOM_IMG_FMT_I44416
, 10, 1 },
148 { VLC_CODEC_I420_10L
, AOM_IMG_FMT_I42016
, 10, 0 },
149 { VLC_CODEC_I422_10L
, AOM_IMG_FMT_I42216
, 10, 0 },
150 { VLC_CODEC_I444_10L
, AOM_IMG_FMT_I44416
, 10, 0 },
152 { VLC_CODEC_I420_12L
, AOM_IMG_FMT_I42016
, 12, 0 },
153 { VLC_CODEC_I422_12L
, AOM_IMG_FMT_I42216
, 12, 0 },
154 { VLC_CODEC_I444_12L
, AOM_IMG_FMT_I44416
, 12, 0 },
156 { VLC_CODEC_I444_16L
, AOM_IMG_FMT_I44416
, 16, 0 },
159 static vlc_fourcc_t
FindVlcChroma( struct aom_image
*img
)
161 uint8_t hack
= (img
->fmt
& AOM_IMG_FMT_I444
) && (img
->tc
== AOM_CICP_TC_SRGB
);
163 for( unsigned int i
= 0; i
< ARRAY_SIZE(chroma_table
); i
++ )
164 if( chroma_table
[i
].i_chroma_id
== img
->fmt
&&
165 chroma_table
[i
].i_bitdepth
== img
->bit_depth
&&
166 chroma_table
[i
].i_needs_hack
== hack
)
167 return chroma_table
[i
].i_chroma
;
172 static void CopyPicture(const struct aom_image
*img
, picture_t
*pic
)
174 for (int plane
= 0; plane
< pic
->i_planes
; plane
++ ) {
175 plane_t src_plane
= pic
->p
[plane
];
176 src_plane
.p_pixels
= img
->planes
[plane
];
177 src_plane
.i_pitch
= img
->stride
[plane
];
178 plane_CopyPixels(&pic
->p
[plane
], &src_plane
);
182 static int PushFrame(decoder_t
*dec
, block_t
*block
)
184 decoder_sys_t
*p_sys
= dec
->p_sys
;
185 aom_codec_ctx_t
*ctx
= &p_sys
->ctx
;
186 const uint8_t *p_buffer
;
189 /* Associate packet PTS with decoded frame */
190 uintptr_t priv_index
= p_sys
->i_next_frame_priv
++ % AOM_MAX_FRAMES_DEPTH
;
194 p_buffer
= block
->p_buffer
;
195 i_buffer
= block
->i_buffer
;
196 p_sys
->frame_priv
[priv_index
].pts
= (block
->i_pts
!= VLC_TICK_INVALID
) ? block
->i_pts
: block
->i_dts
;
205 err
= aom_codec_decode(ctx
, p_buffer
, i_buffer
, (void*)priv_index
);
208 block_Release(block
);
210 if (err
!= AOM_CODEC_OK
) {
211 AOM_ERR(dec
, ctx
, "Failed to decode frame");
212 if (err
== AOM_CODEC_UNSUP_BITSTREAM
)
213 return VLCDEC_ECRITICAL
;
215 return VLCDEC_SUCCESS
;
218 static void OutputFrame(decoder_t
*dec
, const struct aom_image
*img
)
220 video_format_t
*v
= &dec
->fmt_out
.video
;
222 if (img
->d_w
!= v
->i_visible_width
|| img
->d_h
!= v
->i_visible_height
)
224 v
->i_visible_width
= dec
->fmt_out
.video
.i_width
= img
->d_w
;
225 v
->i_visible_height
= dec
->fmt_out
.video
.i_height
= img
->d_h
;
228 if( !dec
->fmt_out
.video
.i_sar_num
|| !dec
->fmt_out
.video
.i_sar_den
)
230 dec
->fmt_out
.video
.i_sar_num
= 1;
231 dec
->fmt_out
.video
.i_sar_den
= 1;
234 if(dec
->fmt_in
.video
.primaries
== COLOR_PRIMARIES_UNDEF
)
236 v
->primaries
= iso_23001_8_cp_to_vlc_primaries(img
->cp
);
237 v
->transfer
= iso_23001_8_tc_to_vlc_xfer(img
->tc
);
238 v
->space
= iso_23001_8_mc_to_vlc_coeffs(img
->mc
);
239 v
->color_range
= img
->range
== AOM_CR_FULL_RANGE
? COLOR_RANGE_FULL
: COLOR_RANGE_LIMITED
;
242 dec
->fmt_out
.video
.projection_mode
= dec
->fmt_in
.video
.projection_mode
;
243 dec
->fmt_out
.video
.multiview_mode
= dec
->fmt_in
.video
.multiview_mode
;
244 dec
->fmt_out
.video
.pose
= dec
->fmt_in
.video
.pose
;
246 if (decoder_UpdateVideoFormat(dec
) == 0)
248 picture_t
*pic
= decoder_NewPicture(dec
);
251 decoder_sys_t
*p_sys
= dec
->p_sys
;
252 CopyPicture(img
, pic
);
254 /* fetches back the PTS */
256 pic
->b_progressive
= true; /* codec does not support interlacing */
257 pic
->date
= p_sys
->frame_priv
[(uintptr_t)img
->user_priv
].pts
;
259 decoder_QueueVideo(dec
, pic
);
264 static int PopFrames(decoder_t
*dec
,
265 void(*pf_output
)(decoder_t
*, const struct aom_image
*))
267 decoder_sys_t
*p_sys
= dec
->p_sys
;
268 aom_codec_ctx_t
*ctx
= &p_sys
->ctx
;
270 for(const void *iter
= NULL
;; )
272 struct aom_image
*img
= aom_codec_get_frame(ctx
, &iter
);
276 dec
->fmt_out
.i_codec
= FindVlcChroma(img
);
277 if (dec
->fmt_out
.i_codec
== 0) {
278 msg_Err(dec
, "Unsupported output colorspace %d", img
->fmt
);
285 return VLCDEC_SUCCESS
;
288 /****************************************************************************
289 * Flush: clears decoder between seeks
290 ****************************************************************************/
291 static void DropFrame(decoder_t
*dec
, const struct aom_image
*img
)
295 /* do nothing for now */
298 static void FlushDecoder(decoder_t
*dec
)
300 decoder_sys_t
*p_sys
= dec
->p_sys
;
301 aom_codec_ctx_t
*ctx
= &p_sys
->ctx
;
303 if(PushFrame(dec
, NULL
) != VLCDEC_SUCCESS
)
304 AOM_ERR(dec
, ctx
, "Failed to flush decoder");
306 PopFrames(dec
, DropFrame
);
309 /****************************************************************************
310 * Decode: the whole thing
311 ****************************************************************************/
312 static int Decode(decoder_t
*dec
, block_t
*block
)
314 if (block
&& block
->i_flags
& (BLOCK_FLAG_CORRUPTED
))
316 block_Release(block
);
317 return VLCDEC_SUCCESS
;
320 int i_ret
= PushFrame(dec
, block
);
322 PopFrames(dec
, OutputFrame
);
327 /*****************************************************************************
328 * OpenDecoder: probe the decoder
329 *****************************************************************************/
330 static int OpenDecoder(vlc_object_t
*p_this
)
332 decoder_t
*dec
= (decoder_t
*)p_this
;
333 const aom_codec_iface_t
*iface
;
336 if (dec
->fmt_in
.i_codec
!= VLC_CODEC_AV1
)
339 iface
= &aom_codec_av1_dx_algo
;
342 decoder_sys_t
*sys
= malloc(sizeof(*sys
));
347 sys
->i_next_frame_priv
= 0;
349 struct aom_codec_dec_cfg deccfg
= {
350 .threads
= __MIN(vlc_GetCPUCount(), 16),
351 .allow_lowbitdepth
= 1
354 msg_Dbg(p_this
, "AV%d: using libaom version %s (build options %s)",
355 av_version
, aom_codec_version_str(), aom_codec_build_config());
357 if (aom_codec_dec_init(&sys
->ctx
, iface
, &deccfg
, 0) != AOM_CODEC_OK
) {
358 AOM_ERR(p_this
, &sys
->ctx
, "Failed to initialize decoder");
360 return VLC_EGENERIC
;;
363 dec
->pf_decode
= Decode
;
364 dec
->pf_flush
= FlushDecoder
;
366 dec
->fmt_out
.video
.i_width
= dec
->fmt_in
.video
.i_width
;
367 dec
->fmt_out
.video
.i_height
= dec
->fmt_in
.video
.i_height
;
368 dec
->fmt_out
.i_codec
= VLC_CODEC_I420
;
370 if (dec
->fmt_in
.video
.i_sar_num
> 0 && dec
->fmt_in
.video
.i_sar_den
> 0) {
371 dec
->fmt_out
.video
.i_sar_num
= dec
->fmt_in
.video
.i_sar_num
;
372 dec
->fmt_out
.video
.i_sar_den
= dec
->fmt_in
.video
.i_sar_den
;
374 dec
->fmt_out
.video
.primaries
= dec
->fmt_in
.video
.primaries
;
375 dec
->fmt_out
.video
.transfer
= dec
->fmt_in
.video
.transfer
;
376 dec
->fmt_out
.video
.space
= dec
->fmt_in
.video
.space
;
377 dec
->fmt_out
.video
.color_range
= dec
->fmt_in
.video
.color_range
;
382 static void destroy_context(vlc_object_t
*p_this
, aom_codec_ctx_t
*context
)
384 if (aom_codec_destroy(context
))
385 AOM_ERR(p_this
, context
, "Failed to destroy codec context");
388 /*****************************************************************************
389 * CloseDecoder: decoder destruction
390 *****************************************************************************/
391 static void CloseDecoder(vlc_object_t
*p_this
)
393 decoder_t
*dec
= (decoder_t
*)p_this
;
394 decoder_sys_t
*sys
= dec
->p_sys
;
399 destroy_context(p_this
, &sys
->ctx
);
406 #ifndef AOM_USAGE_REALTIME
407 # define AOM_USAGE_REALTIME 1
410 /*****************************************************************************
411 * encoder_sys_t: libaom encoder descriptor
412 *****************************************************************************/
415 struct aom_codec_ctx ctx
;
418 /*****************************************************************************
419 * OpenEncoder: probe the encoder
420 *****************************************************************************/
421 static int OpenEncoder(vlc_object_t
*p_this
)
423 encoder_t
*p_enc
= (encoder_t
*)p_this
;
424 encoder_sys_t
*p_sys
;
426 if (p_enc
->fmt_out
.i_codec
!= VLC_CODEC_AV1
)
429 /* Allocate the memory needed to store the encoder's structure */
430 p_sys
= malloc(sizeof(*p_sys
));
434 p_enc
->p_sys
= p_sys
;
436 const struct aom_codec_iface
*iface
= &aom_codec_av1_cx_algo
;
438 struct aom_codec_enc_cfg enccfg
= { 0 };
439 aom_codec_enc_config_default(iface
, &enccfg
, 0);
440 /* TODO: implement 2-pass */
441 enccfg
.g_pass
= AOM_RC_ONE_PASS
;
442 enccfg
.g_timebase
.num
= p_enc
->fmt_in
.video
.i_frame_rate_base
;
443 enccfg
.g_timebase
.den
= p_enc
->fmt_in
.video
.i_frame_rate
;
444 enccfg
.g_threads
= __MIN(vlc_GetCPUCount(), 4);
445 enccfg
.g_w
= p_enc
->fmt_in
.video
.i_visible_width
;
446 enccfg
.g_h
= p_enc
->fmt_in
.video
.i_visible_height
;
447 enccfg
.rc_end_usage
= var_InheritInteger( p_enc
, SOUT_CFG_PREFIX
"rc-end-usage" );
448 enccfg
.g_usage
= var_InheritInteger( p_enc
, SOUT_CFG_PREFIX
"usage" );
449 /* we have no pcr on sout, hence this defaulting to 16 */
450 enccfg
.g_lag_in_frames
= var_InheritInteger( p_enc
, SOUT_CFG_PREFIX
"lag-in-frames" );
451 if( enccfg
.g_usage
== AOM_USAGE_REALTIME
&& enccfg
.g_lag_in_frames
!= 0 )
453 msg_Warn( p_enc
, "Non-zero lag in frames is not supported for realtime, forcing 0" );
454 enccfg
.g_lag_in_frames
= 0;
458 int i_profile
= var_InheritInteger( p_enc
, SOUT_CFG_PREFIX
"profile" );
459 int i_bit_depth
= var_InheritInteger( p_enc
, SOUT_CFG_PREFIX
"bitdepth" );
460 int i_tile_rows
= var_InheritInteger( p_enc
, SOUT_CFG_PREFIX
"tile-rows" );
461 int i_tile_columns
= var_InheritInteger( p_enc
, SOUT_CFG_PREFIX
"tile-columns" );
462 #ifdef AOM_CTRL_AV1E_SET_ROW_MT
463 bool b_row_mt
= var_GetBool( p_enc
, SOUT_CFG_PREFIX
"row-mt" );
466 /* TODO: implement higher profiles, bit depths and other pixformats. */
470 /* Main Profile: 8 and 10-bit 4:2:0. */
471 enccfg
.g_profile
= 0;
472 switch( i_bit_depth
)
475 p_enc
->fmt_in
.i_codec
= VLC_CODEC_I420_10L
;
476 enc_flags
= AOM_CODEC_USE_HIGHBITDEPTH
;
479 p_enc
->fmt_in
.i_codec
= VLC_CODEC_I420
;
483 msg_Err( p_enc
, "%d bit is unsupported for profile %d", i_bit_depth
, i_profile
);
487 enccfg
.g_bit_depth
= i_bit_depth
;
491 /* High Profile: 8 and 10-bit 4:4:4 */
494 /* Professional Profile: 8, 10 and 12-bit for 4:2:2, otherwise 12-bit. */
497 msg_Err( p_enc
, "Unsupported profile %d", i_profile
);
502 msg_Dbg(p_this
, "AV1: using libaom version %s (build options %s)",
503 aom_codec_version_str(), aom_codec_build_config());
505 struct aom_codec_ctx
*ctx
= &p_sys
->ctx
;
506 if (aom_codec_enc_init(ctx
, iface
, &enccfg
, enc_flags
) != AOM_CODEC_OK
)
508 AOM_ERR(p_this
, ctx
, "Failed to initialize encoder");
513 if (i_tile_rows
>= 0 &&
514 aom_codec_control(ctx
, AV1E_SET_TILE_ROWS
, i_tile_rows
))
516 AOM_ERR(p_this
, ctx
, "Failed to set tile rows");
517 destroy_context(p_this
, ctx
);
522 if (i_tile_columns
>= 0 &&
523 aom_codec_control(ctx
, AV1E_SET_TILE_COLUMNS
, i_tile_columns
))
525 AOM_ERR(p_this
, ctx
, "Failed to set tile columns");
526 destroy_context(p_this
, ctx
);
531 #ifdef AOM_CTRL_AV1E_SET_ROW_MT
533 aom_codec_control(ctx
, AV1E_SET_ROW_MT
, b_row_mt
))
535 AOM_ERR(p_this
, ctx
, "Failed to set row-multithreading");
536 destroy_context(p_this
, ctx
);
542 int i_cpu_used
= var_InheritInteger( p_enc
, SOUT_CFG_PREFIX
"cpu-used" );
543 if (aom_codec_control(ctx
, AOME_SET_CPUUSED
, i_cpu_used
))
545 AOM_ERR(p_this
, ctx
, "Failed to set cpu-used");
546 destroy_context(p_this
, ctx
);
551 p_enc
->pf_encode_video
= Encode
;
556 /****************************************************************************
557 * Encode: the whole thing
558 ****************************************************************************/
559 static block_t
*Encode(encoder_t
*p_enc
, picture_t
*p_pict
)
561 encoder_sys_t
*p_sys
= p_enc
->p_sys
;
562 struct aom_codec_ctx
*ctx
= &p_sys
->ctx
;
564 if (!p_pict
) return NULL
;
566 aom_image_t img
= { 0 };
567 unsigned i_w
= p_enc
->fmt_in
.video
.i_visible_width
;
568 unsigned i_h
= p_enc
->fmt_in
.video
.i_visible_height
;
569 const aom_img_fmt_t img_fmt
= p_enc
->fmt_in
.i_codec
== VLC_CODEC_I420_10L
?
570 AOM_IMG_FMT_I42016
: AOM_IMG_FMT_I420
;
572 /* Create and initialize the aom_image */
573 if (!aom_img_wrap(&img
, img_fmt
, i_w
, i_h
, 32, p_pict
->p
[0].p_pixels
))
575 AOM_ERR(p_enc
, ctx
, "Failed to wrap image");
579 /* Correct chroma plane offsets. */
580 for (int plane
= 1; plane
< p_pict
->i_planes
; plane
++) {
581 img
.planes
[plane
] = p_pict
->p
[plane
].p_pixels
;
582 img
.stride
[plane
] = p_pict
->p
[plane
].i_pitch
;
585 aom_codec_err_t res
= aom_codec_encode(ctx
, &img
, US_FROM_VLC_TICK(p_pict
->date
), 1, 0);
586 if (res
!= AOM_CODEC_OK
) {
587 AOM_ERR(p_enc
, ctx
, "Failed to encode frame");
592 const aom_codec_cx_pkt_t
*pkt
= NULL
;
593 aom_codec_iter_t iter
= NULL
;
594 block_t
*p_out
= NULL
;
595 while ((pkt
= aom_codec_get_cx_data(ctx
, &iter
)) != NULL
)
597 if (pkt
->kind
== AOM_CODEC_CX_FRAME_PKT
)
599 int keyframe
= pkt
->data
.frame
.flags
& AOM_FRAME_IS_KEY
;
600 block_t
*p_block
= block_Alloc(pkt
->data
.frame
.sz
);
601 if (unlikely(p_block
== NULL
)) {
602 block_ChainRelease(p_out
);
607 /* FIXME: do this in-place */
608 memcpy(p_block
->p_buffer
, pkt
->data
.frame
.buf
, pkt
->data
.frame
.sz
);
609 p_block
->i_dts
= p_block
->i_pts
= VLC_TICK_FROM_US(pkt
->data
.frame
.pts
);
611 p_block
->i_flags
|= BLOCK_FLAG_TYPE_I
;
612 block_ChainAppend(&p_out
, p_block
);
619 /*****************************************************************************
620 * CloseEncoder: encoder destruction
621 *****************************************************************************/
622 static void CloseEncoder(vlc_object_t
*p_this
)
624 encoder_t
*p_enc
= (encoder_t
*)p_this
;
625 encoder_sys_t
*p_sys
= p_enc
->p_sys
;
626 destroy_context(p_this
, &p_sys
->ctx
);
630 #endif /* ENABLE_SOUT */