bitstream_put_ui(bs, 1, 1); /* vui_parameters_present_flag */
bitstream_put_ui(bs, 0, 1); /* aspect_ratio_info_present_flag */
bitstream_put_ui(bs, 0, 1); /* overscan_info_present_flag */
- bitstream_put_ui(bs, 0, 1); /* video_signal_type_present_flag */
+ bitstream_put_ui(bs, 1, 1); /* video_signal_type_present_flag */
+ {
+ bitstream_put_ui(bs, 5, 3); /* video_format (5 = Unspecified) */
+ bitstream_put_ui(bs, 0, 1); /* video_full_range_flag */
+ bitstream_put_ui(bs, 1, 1); /* colour_description_present_flag */
+ {
+ bitstream_put_ui(bs, 1, 8); /* colour_primaries (1 = BT.709) */
+ bitstream_put_ui(bs, 1, 8); /* transfer_characteristics (1 = BT.709) */
+ bitstream_put_ui(bs, 1, 8); /* matrix_coefficients (1 = BT.709) */
+ }
+ }
bitstream_put_ui(bs, 0, 1); /* chroma_loc_info_present_flag */
bitstream_put_ui(bs, 1, 1); /* timing_info_present_flag */
{
avstream_video->codec->time_base = AVRational{1, TIMEBASE};
avstream_video->codec->ticks_per_frame = 1; // or 2?
+ // Colorspace details. Closely correspond to settings in EffectChain_finalize,
+ // as noted in each comment.
+ // Note that the H.264 stream also contains this information and depending on the
+ // mux, this might simply get ignored. See sps_rbsp().
+ avstream_video->codec->color_primaries = AVCOL_PRI_BT709; // RGB colorspace (inout_format.color_space).
+ avstream_video->codec->color_trc = AVCOL_TRC_BT709; // Gamma curve (inout_format.gamma_curve).
+ avstream_video->codec->colorspace = AVCOL_SPC_BT709; // YUV colorspace (output_ycbcr_format.luma_coefficients).
+ avstream_video->codec->color_range = AVCOL_RANGE_MPEG; // Full vs. limited range (output_ycbcr_format.full_range).
+ avstream_video->codec->chroma_sample_location = AVCHROMA_LOC_LEFT; // Chroma sample location. See chroma_offset_0[] in Mixer::subsample_chroma().
+ avstream_video->codec->field_order = AV_FIELD_PROGRESSIVE;
+
AVCodec *codec_audio = avcodec_find_encoder(AV_CODEC_ID_MP3);
avstream_audio = avformat_new_stream(avctx, codec_audio);
if (avstream_audio == nullptr) {
bool is_main_chain = checkbool(L, 2);
// Add outputs as needed.
+ // NOTE: If you change any details about the output format, you will need to
+ // also update what's given to the muxer (HTTPD::Mux constructor) and
+ // what's put in the H.264 stream (sps_rbsp()).
ImageFormat inout_format;
- inout_format.color_space = COLORSPACE_sRGB;
- inout_format.gamma_curve = GAMMA_sRGB;
+ inout_format.color_space = COLORSPACE_REC_709;
+ inout_format.gamma_curve = GAMMA_REC_709;
if (is_main_chain) {
YCbCrFormat output_ycbcr_format;
+ // We actually output 4:2:0 in the end, but chroma subsampling
+ // happens in a pass not run by Movit (see Mixer::subsample_chroma()).
output_ycbcr_format.chroma_subsampling_x = 1;
output_ycbcr_format.chroma_subsampling_y = 1;
output_ycbcr_format.luma_coefficients = YCBCR_REC_709;
output_ycbcr_format.full_range = false;
+ output_ycbcr_format.num_levels = 256;
chain->add_ycbcr_output(inout_format, OUTPUT_ALPHA_FORMAT_POSTMULTIPLIED, output_ycbcr_format, YCBCR_OUTPUT_SPLIT_Y_AND_CBCR);
chain->set_dither_bits(8);
inout_format.color_space = COLORSPACE_sRGB;
inout_format.gamma_curve = GAMMA_sRGB;
+ // The Blackmagic driver docs claim that the device outputs Y'CbCr
+ // according to Rec. 601, but practical testing indicates it definitely
+ // is Rec. 709 (at least up to errors attributable to rounding errors).
+ // Perhaps 601 was only to indicate the subsampling positions, not the
+ // colorspace itself? Tested with a Lenovo X1 gen 3 as input.
YCbCrFormat input_ycbcr_format;
input_ycbcr_format.chroma_subsampling_x = 2;
input_ycbcr_format.chroma_subsampling_y = 1;
input_ycbcr_format.cr_x_position = 0.0;
input_ycbcr_format.cb_y_position = 0.5;
input_ycbcr_format.cr_y_position = 0.5;
- input_ycbcr_format.luma_coefficients = YCBCR_REC_601;
+ input_ycbcr_format.luma_coefficients = YCBCR_REC_709;
input_ycbcr_format.full_range = false;
if (override_bounce) {