diff --git a/libavformat/movenc.c b/libavformat/movenc.c index e7f4eb1d90..d7ae5f0e90 100644 --- a/libavformat/movenc.c +++ b/libavformat/movenc.c @@ -67,6 +67,7 @@ static const AVOption options[] = { { "dash", "Write DASH compatible fragmented MP4", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_DASH}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, { "frag_discont", "Signal that the next fragment is discontinuous from earlier ones", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_FRAG_DISCONT}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, { "delay_moov", "Delay writing the initial moov until the first fragment is cut, or until the first fragment flush", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_DELAY_MOOV}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, + { "write_colr", "Write colr atom", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_WRITE_COLR}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, FF_RTP_FLAG_OPTS(MOVMuxContext, rtp_flags), { "skip_iods", "Skip writing iods atom.", offsetof(MOVMuxContext, iods_skip), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1, AV_OPT_FLAG_ENCODING_PARAM}, { "iods_audio_profile", "iods audio profile atom.", offsetof(MOVMuxContext, iods_audio_profile), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 255, AV_OPT_FLAG_ENCODING_PARAM}, @@ -1499,6 +1500,66 @@ static int mov_write_pasp_tag(AVIOContext *pb, MOVTrack *track) return 16; } +static int mov_write_colr_tag(AVIOContext *pb, MOVTrack *track) +{ + // Ref: https://developer.apple.com/library/mac/technotes/tn2162/_index.html#//apple_ref/doc/uid/DTS40013070-CH1-TNTAG9 + + if (track->enc->color_primaries == AVCOL_PRI_UNSPECIFIED && + track->enc->color_trc == AVCOL_TRC_UNSPECIFIED && + track->enc->colorspace == AVCOL_SPC_UNSPECIFIED) { + if ((track->enc->width >= 1920 && track->enc->height >= 1080) + || (track->enc->width == 1280 && track->enc->height == 720)) { + av_log(NULL, AV_LOG_WARNING, "color primaries unspecified, assuming bt709\n"); + track->enc->color_primaries = AVCOL_PRI_BT709; + } else if (track->enc->width == 720 && track->height == 576) { + av_log(NULL, AV_LOG_WARNING, "color primaries unspecified, assuming bt470bg\n"); + track->enc->color_primaries = AVCOL_PRI_BT470BG; + } else if (track->enc->width == 720 && + (track->height == 486 || track->height == 480)) { + av_log(NULL, AV_LOG_WARNING, "color primaries unspecified, assuming smpte170\n"); + track->enc->color_primaries = AVCOL_PRI_SMPTE170M; + } else { + av_log(NULL, AV_LOG_WARNING, "color primaries unspecified, unable to assume anything\n"); + } + switch (track->enc->color_primaries) { + case AVCOL_PRI_BT709: + track->enc->color_trc = AVCOL_TRC_BT709; + track->enc->colorspace = AVCOL_SPC_BT709; + break; + case AVCOL_PRI_SMPTE170M: + case AVCOL_PRI_BT470BG: + track->enc->color_trc = AVCOL_TRC_BT709; + track->enc->colorspace = AVCOL_SPC_SMPTE170M; + break; + } + } + + avio_wb32(pb, 18); + ffio_wfourcc(pb, "colr"); + ffio_wfourcc(pb, "nclc"); + switch (track->enc->color_primaries) { + case AVCOL_PRI_BT709: avio_wb16(pb, 1); break; + case AVCOL_PRI_SMPTE170M: + case AVCOL_PRI_SMPTE240M: avio_wb16(pb, 6); break; + case AVCOL_PRI_BT470BG: avio_wb16(pb, 5); break; + default: avio_wb16(pb, 2); + } + switch (track->enc->color_trc) { + case AVCOL_TRC_BT709: avio_wb16(pb, 1); break; + case AVCOL_TRC_SMPTE170M: avio_wb16(pb, 1); break; // remapped + case AVCOL_TRC_SMPTE240M: avio_wb16(pb, 7); break; + default: avio_wb16(pb, 2); + } + switch (track->enc->colorspace) { + case AVCOL_TRC_BT709: avio_wb16(pb, 1); break; + case AVCOL_PRI_SMPTE170M: avio_wb16(pb, 6); break; + case AVCOL_PRI_SMPTE240M: avio_wb16(pb, 7); break; + default: avio_wb16(pb, 2); + } + + return 18; +} + static void find_compressor(char * compressor_name, int len, MOVTrack *track) { AVDictionaryEntry *encoder; @@ -1527,7 +1588,7 @@ static void find_compressor(char * compressor_name, int len, MOVTrack *track) } } -static int mov_write_video_tag(AVIOContext *pb, MOVTrack *track) +static int mov_write_video_tag(AVIOContext *pb, MOVMuxContext *mov, MOVTrack *track) { int64_t pos = avio_tell(pb); char compressor_name[32] = { 0 }; @@ -1605,6 +1666,9 @@ static int mov_write_video_tag(AVIOContext *pb, MOVTrack *track) if (track->enc->field_order != AV_FIELD_UNKNOWN) mov_write_fiel_tag(pb, track); + if (mov->flags & FF_MOV_FLAG_WRITE_COLR) + mov_write_colr_tag(pb, track); + if (track->enc->sample_aspect_ratio.den && track->enc->sample_aspect_ratio.num && track->enc->sample_aspect_ratio.den != track->enc->sample_aspect_ratio.num) { mov_write_pasp_tag(pb, track); @@ -1695,7 +1759,7 @@ static int mov_write_tmcd_tag(AVIOContext *pb, MOVTrack *track) return update_size(pb, pos); } -static int mov_write_stsd_tag(AVIOContext *pb, MOVTrack *track) +static int mov_write_stsd_tag(AVIOContext *pb, MOVMuxContext *mov, MOVTrack *track) { int64_t pos = avio_tell(pb); avio_wb32(pb, 0); /* size */ @@ -1703,7 +1767,7 @@ static int mov_write_stsd_tag(AVIOContext *pb, MOVTrack *track) avio_wb32(pb, 0); /* version & flags */ avio_wb32(pb, 1); /* entry count */ if (track->enc->codec_type == AVMEDIA_TYPE_VIDEO) - mov_write_video_tag(pb, track); + mov_write_video_tag(pb, mov, track); else if (track->enc->codec_type == AVMEDIA_TYPE_AUDIO) mov_write_audio_tag(pb, track); else if (track->enc->codec_type == AVMEDIA_TYPE_SUBTITLE) @@ -1807,14 +1871,14 @@ static int mov_write_dref_tag(AVIOContext *pb) return 28; } -static int mov_write_stbl_tag(AVIOContext *pb, MOVTrack *track) +static int mov_write_stbl_tag(AVIOContext *pb, MOVMuxContext *mov, MOVTrack *track) { int64_t pos = avio_tell(pb); int ret; avio_wb32(pb, 0); /* size */ ffio_wfourcc(pb, "stbl"); - mov_write_stsd_tag(pb, track); + mov_write_stsd_tag(pb, mov, track); mov_write_stts_tag(pb, track); if ((track->enc->codec_type == AVMEDIA_TYPE_VIDEO || track->enc->codec_tag == MKTAG('r','t','p',' ')) && @@ -2032,7 +2096,7 @@ static int mov_write_hmhd_tag(AVIOContext *pb) return 28; } -static int mov_write_minf_tag(AVIOContext *pb, MOVTrack *track) +static int mov_write_minf_tag(AVIOContext *pb, MOVMuxContext *mov, MOVTrack *track) { int64_t pos = avio_tell(pb); int ret; @@ -2057,7 +2121,7 @@ static int mov_write_minf_tag(AVIOContext *pb, MOVTrack *track) if (track->mode == MODE_MOV) /* FIXME: Why do it for MODE_MOV only ? */ mov_write_hdlr_tag(pb, NULL); mov_write_dinf_tag(pb); - if ((ret = mov_write_stbl_tag(pb, track)) < 0) + if ((ret = mov_write_stbl_tag(pb, mov, track)) < 0) return ret; return update_size(pb, pos); } @@ -2111,7 +2175,7 @@ static int mov_write_mdia_tag(AVIOContext *pb, MOVMuxContext *mov, ffio_wfourcc(pb, "mdia"); mov_write_mdhd_tag(pb, mov, track); mov_write_hdlr_tag(pb, track); - if ((ret = mov_write_minf_tag(pb, track)) < 0) + if ((ret = mov_write_minf_tag(pb, mov, track)) < 0) return ret; return update_size(pb, pos); } diff --git a/libavformat/movenc.h b/libavformat/movenc.h index 79027e69f3..3a729374da 100644 --- a/libavformat/movenc.h +++ b/libavformat/movenc.h @@ -201,6 +201,7 @@ typedef struct MOVMuxContext { #define FF_MOV_FLAG_DASH (1 << 11) #define FF_MOV_FLAG_FRAG_DISCONT (1 << 12) #define FF_MOV_FLAG_DELAY_MOOV (1 << 13) +#define FF_MOV_FLAG_WRITE_COLR (1 << 14) int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt); diff --git a/tests/fate/vcodec.mak b/tests/fate/vcodec.mak index 807699ea89..a3aa11c1ad 100644 --- a/tests/fate/vcodec.mak +++ b/tests/fate/vcodec.mak @@ -37,11 +37,15 @@ fate-vsynth%-dnxhd-720p-10bit: ENCOPTS = -s hd720 -b 90M \ -pix_fmt yuv422p10 -frames 5 -qmax 8 fate-vsynth%-dnxhd-720p-10bit: FMT = dnxhd -FATE_VCODEC-$(call ENCDEC, DNXHD, MOV) += dnxhd-1080i +FATE_VCODEC-$(call ENCDEC, DNXHD, MOV) += dnxhd-1080i dnxhd-1080i-colr fate-vsynth%-dnxhd-1080i: ENCOPTS = -s hd1080 -b 120M -flags +ildct \ -pix_fmt yuv422p -frames 5 -qmax 8 fate-vsynth%-dnxhd-1080i: FMT = mov +fate-vsynth%-dnxhd-1080i-colr: ENCOPTS = -s hd1080 -b 120M -flags +ildct -movflags write_colr \ + -pix_fmt yuv422p -frames 5 -qmax 8 +fate-vsynth%-dnxhd-1080i-colr: FMT = mov + FATE_VCODEC-$(call ENCDEC, DVVIDEO, DV) += dv dv-411 dv-50 fate-vsynth%-dv: CODEC = dvvideo fate-vsynth%-dv: ENCOPTS = -dct int -s pal diff --git a/tests/ref/vsynth/vsynth1-dnxhd-1080i-colr b/tests/ref/vsynth/vsynth1-dnxhd-1080i-colr new file mode 100644 index 0000000000..87942cb516 --- /dev/null +++ b/tests/ref/vsynth/vsynth1-dnxhd-1080i-colr @@ -0,0 +1,4 @@ +b6fbfdfe7027fde6853930abad87eaab *tests/data/fate/vsynth1-dnxhd-1080i-colr.mov +3031929 tests/data/fate/vsynth1-dnxhd-1080i-colr.mov +a09132c6db44f415e831dcaa630a351b *tests/data/fate/vsynth1-dnxhd-1080i-colr.out.rawvideo +stddev: 6.29 PSNR: 32.15 MAXDIFF: 64 bytes: 7603200/ 760320 diff --git a/tests/ref/vsynth/vsynth2-dnxhd-1080i-colr b/tests/ref/vsynth/vsynth2-dnxhd-1080i-colr new file mode 100644 index 0000000000..edf3c2cc67 --- /dev/null +++ b/tests/ref/vsynth/vsynth2-dnxhd-1080i-colr @@ -0,0 +1,4 @@ +d510bc0d58c7cae875e3e67023771d6f *tests/data/fate/vsynth2-dnxhd-1080i-colr.mov +3031929 tests/data/fate/vsynth2-dnxhd-1080i-colr.mov +099001db73036eeb9545c463cf90f0ba *tests/data/fate/vsynth2-dnxhd-1080i-colr.out.rawvideo +stddev: 1.53 PSNR: 44.43 MAXDIFF: 31 bytes: 7603200/ 760320 diff --git a/tests/ref/vsynth/vsynth3-dnxhd-1080i-colr b/tests/ref/vsynth/vsynth3-dnxhd-1080i-colr new file mode 100644 index 0000000000..c651c9204c --- /dev/null +++ b/tests/ref/vsynth/vsynth3-dnxhd-1080i-colr @@ -0,0 +1,4 @@ +3b06d8675f9623db77b6a42916663608 *tests/data/fate/vsynth3-dnxhd-1080i-colr.mov +3031929 tests/data/fate/vsynth3-dnxhd-1080i-colr.mov +382fc519604abb5d87071bdce013cef9 *tests/data/fate/vsynth3-dnxhd-1080i-colr.out.rawvideo +stddev: 7.81 PSNR: 30.28 MAXDIFF: 61 bytes: 86700/ 8670 diff --git a/tests/ref/vsynth/vsynth_lena-dnxhd-1080i-colr b/tests/ref/vsynth/vsynth_lena-dnxhd-1080i-colr new file mode 100644 index 0000000000..6c32bc9b3a --- /dev/null +++ b/tests/ref/vsynth/vsynth_lena-dnxhd-1080i-colr @@ -0,0 +1,4 @@ +4deae1b3d9a5c8fbd28075e8dca0034e *tests/data/fate/vsynth_lena-dnxhd-1080i-colr.mov +3031929 tests/data/fate/vsynth_lena-dnxhd-1080i-colr.mov +744ba46da5d4c19a28562ea31061d170 *tests/data/fate/vsynth_lena-dnxhd-1080i-colr.out.rawvideo +stddev: 1.31 PSNR: 45.77 MAXDIFF: 23 bytes: 7603200/ 760320