On Tue, Apr 28, 2020 at 3:18 AM Nicolas George <geo...@nsup.org> wrote:
> Andreas Rheinhardt (12020-04-28): > > That's expected. The patch provided only provides the structure in which > > the values are intended to be exported; it does not add any demuxing or > > muxing capabilities for mov or mkv (as you can see from the fact that > > none of these (de)muxers have been changed in the patch). > > Which is something I intended to comment on: adding code without users > is rarely a good idea. I suggest we do not commit until at least one > demuxer use it, preferably at least two. Otherwise, we may realize that > “oh crap, it doesn't work” because of a tiny unforeseen detail. Thanks for the feedback. I also have patches for the demux (MOV/MKV) and mux (MOV/MKV). As there is still the alternative of using the fields in the AVCodecParameters/AVCodecContext, my intention was to keep the first patch small to resolve discussion on that point. I've included the patches, if you'd like to try test it, Kieren. I see on your test file that there may be some slight rounding error making output crop 704 not 703 (MKV file ends up with pixel_crop_{left,right} = 8). /ffprobe ../testdata/clap.mov 2>&1 | grep -A1 "Side" Side data: Clean aperture:[width 41472/59 height:576/1 h_offset:0/1 v_offset:0/1] ./ffmpeg -i ../testdata/clap.mov -vcodec copy -acodec copy /tmp/clap.mkv ./ffprobe /tmp/clap.mkv 2>&1 | grep -A1 "Side" Side data: Clean aperture:[width 704/1 height:576/1 h_offset:0/1 v_offset:0/1]
From d92371cc2de6a671b44d9f6dd9d28544d05b5287 Mon Sep 17 00:00:00 2001 From: Neil Birkbeck <neil.birkb...@gmail.com> Date: Sun, 26 Apr 2020 20:42:59 -0700 Subject: [PATCH 3/3] libavformat: Adding MOV/MKV muxer support for CLAP side data. Write out stream-level AVCleanAperture side data in the muxer. Signed-off-by: Neil Birkbeck <neil.birkb...@gmail.com> --- libavformat/matroskaenc.c | 37 +++++++++++++++++++++++++++++++++++++ libavformat/movenc.c | 28 ++++++++++++++++++++-------- 2 files changed, 57 insertions(+), 8 deletions(-) diff --git a/libavformat/matroskaenc.c b/libavformat/matroskaenc.c index 784973a951..19ff29853e 100644 --- a/libavformat/matroskaenc.c +++ b/libavformat/matroskaenc.c @@ -901,6 +901,38 @@ static int mkv_write_video_color(AVIOContext *pb, const AVCodecParameters *par, return 0; } +static int mkv_write_video_crop(AVIOContext *pb, AVCodecParameters *par, AVStream *st) +{ + int cropb = 0, cropt = 0, cropl = 0, cropr = 0; + double width, height, h_offset, v_offset; + int side_data_size = 0; + const AVCleanAperture *clap = + (const AVCleanAperture *)av_stream_get_side_data( + st, AV_PKT_DATA_CLEAN_APERTURE, &side_data_size); + if (!clap) + return 0; + + width = av_q2d(clap->width); + height = av_q2d(clap->height); + h_offset = av_q2d(clap->horizontal_offset); + v_offset = av_q2d(clap->vertical_offset); + cropb = (int)(par->height - (par->height / 2. + v_offset + height / 2)); + cropt = (int)(par->height / 2. + v_offset - height / 2); + cropr = (int)(par->width - (par->width / 2. + h_offset + width / 2)); + cropl = (int)(par->width / 2. + h_offset - width / 2); + cropb = FFMAX(cropb, 0); + cropt = FFMAX(cropt, 0); + cropr = FFMAX(cropr, 0); + cropl = FFMAX(cropl, 0); + if (!cropr && !cropl && !cropt && !cropb) + return 0; + put_ebml_uint(pb, MATROSKA_ID_VIDEOPIXELCROPL, cropl); + put_ebml_uint(pb, MATROSKA_ID_VIDEOPIXELCROPR, cropr); + put_ebml_uint(pb, MATROSKA_ID_VIDEOPIXELCROPB, cropb); + put_ebml_uint(pb, MATROSKA_ID_VIDEOPIXELCROPT, cropt); + return 0; +} + static int mkv_write_video_projection(AVFormatContext *s, AVIOContext *pb, const AVStream *st) { @@ -1287,6 +1319,11 @@ static int mkv_write_track(AVFormatContext *s, MatroskaMuxContext *mkv, } else if (mkv->mode != MODE_WEBM) put_ebml_uint(pb, MATROSKA_ID_VIDEODISPLAYUNIT, MATROSKA_VIDEO_DISPLAYUNIT_UNKNOWN); + // Write out pixel crop + ret = mkv_write_video_crop(pb, par, st); + if (ret < 0) + return ret; + if (par->codec_id == AV_CODEC_ID_RAWVIDEO) { uint32_t color_space = av_le2ne32(par->codec_tag); put_ebml_binary(pb, MATROSKA_ID_VIDEOCOLORSPACE, &color_space, sizeof(color_space)); diff --git a/libavformat/movenc.c b/libavformat/movenc.c index 32e8109268..ab2b53cab6 100644 --- a/libavformat/movenc.c +++ b/libavformat/movenc.c @@ -1847,16 +1847,28 @@ static int mov_write_dvcc_dvvc_tag(AVFormatContext *s, AVIOContext *pb, AVDOVIDe static int mov_write_clap_tag(AVIOContext *pb, MOVTrack *track) { + const AVCleanAperture* clap = + (const AVCleanAperture*) av_stream_get_side_data( + track->st, AV_PKT_DATA_CLEAN_APERTURE, NULL); + AVRational width = {.num = clap ? clap->width.num : track->par->width, + .den = clap ? clap->width.den : 1}; + AVRational height = {.num = clap ? clap->height.num : track->par->height, + .den = clap ? clap->height.den : 1}; + AVRational h_offs = {.num = clap ? clap->horizontal_offset.num : 0, + .den = clap ? clap->horizontal_offset.den : 1}; + AVRational v_offs = {.num = clap ? clap->vertical_offset.num : 0, + .den = clap ? clap->vertical_offset.den : 1}; + avio_wb32(pb, 40); ffio_wfourcc(pb, "clap"); - avio_wb32(pb, track->par->width); /* apertureWidth_N */ - avio_wb32(pb, 1); /* apertureWidth_D (= 1) */ - avio_wb32(pb, track->height); /* apertureHeight_N */ - avio_wb32(pb, 1); /* apertureHeight_D (= 1) */ - avio_wb32(pb, 0); /* horizOff_N (= 0) */ - avio_wb32(pb, 1); /* horizOff_D (= 1) */ - avio_wb32(pb, 0); /* vertOff_N (= 0) */ - avio_wb32(pb, 1); /* vertOff_D (= 1) */ + avio_wb32(pb, width.num); /* apertureWidth_N */ + avio_wb32(pb, width.den); /* apertureWidth_D */ + avio_wb32(pb, height.num); /* apertureHeight_N */ + avio_wb32(pb, height.den); /* apertureHeight_D */ + avio_wb32(pb, h_offs.num); /* horizOff_N (= 0) */ + avio_wb32(pb, h_offs.den); /* horizOff_D (= 1) */ + avio_wb32(pb, v_offs.num); /* vertOff_N (= 0) */ + avio_wb32(pb, v_offs.den); /* vertOff_D (= 1) */ return 40; } -- 2.26.2.303.gf8c07b1a785-goog
From 02a0c4caa1cd0be1913aad25d47e351c3166ee35 Mon Sep 17 00:00:00 2001 From: Neil Birkbeck <neil.birkb...@gmail.com> Date: Sun, 26 Apr 2020 20:28:43 -0700 Subject: [PATCH 2/3] libavformat: demuxer support (MKV/MOV) for clean aperture. Exports the container-level cropping as stream-level AVCleanAperture side data. Signed-off-by: Neil Birkbeck <neil.birkb...@gmail.com> --- libavformat/isom.h | 3 +++ libavformat/matroskadec.c | 42 ++++++++++++++++++++++++++++++++------- libavformat/mov.c | 41 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 79 insertions(+), 7 deletions(-) diff --git a/libavformat/isom.h b/libavformat/isom.h index 41a9c64c11..8a0775fd70 100644 --- a/libavformat/isom.h +++ b/libavformat/isom.h @@ -27,6 +27,7 @@ #include <stddef.h> #include <stdint.h> +#include "libavutil/clean_aperture.h" #include "libavutil/encryption_info.h" #include "libavutil/mastering_display_metadata.h" #include "libavutil/spherical.h" @@ -241,6 +242,8 @@ typedef struct MOVStreamContext { AVEncryptionInfo *default_encrypted_sample; MOVEncryptionIndex *encryption_index; } cenc; + + AVCleanAperture *clap; } MOVStreamContext; typedef struct MOVContext { diff --git a/libavformat/matroskadec.c b/libavformat/matroskadec.c index 8e1326abf6..de520be247 100644 --- a/libavformat/matroskadec.c +++ b/libavformat/matroskadec.c @@ -190,6 +190,10 @@ typedef struct MatroskaTrackVideo { uint64_t display_height; uint64_t pixel_width; uint64_t pixel_height; + uint64_t pixel_cropb; + uint64_t pixel_cropt; + uint64_t pixel_cropl; + uint64_t pixel_cropr; EbmlBin color_space; uint64_t display_unit; uint64_t interlaced; @@ -480,10 +484,10 @@ static EbmlSyntax matroska_track_video[] = { { MATROSKA_ID_VIDEOALPHAMODE, EBML_UINT, 0, offsetof(MatroskaTrackVideo, alpha_mode) }, { MATROSKA_ID_VIDEOCOLOR, EBML_NEST, sizeof(MatroskaTrackVideoColor), offsetof(MatroskaTrackVideo, color), { .n = matroska_track_video_color } }, { MATROSKA_ID_VIDEOPROJECTION, EBML_NEST, 0, offsetof(MatroskaTrackVideo, projection), { .n = matroska_track_video_projection } }, - { MATROSKA_ID_VIDEOPIXELCROPB, EBML_NONE }, - { MATROSKA_ID_VIDEOPIXELCROPT, EBML_NONE }, - { MATROSKA_ID_VIDEOPIXELCROPL, EBML_NONE }, - { MATROSKA_ID_VIDEOPIXELCROPR, EBML_NONE }, + { MATROSKA_ID_VIDEOPIXELCROPB, EBML_UINT, 0, offsetof(MatroskaTrackVideo, pixel_cropb) }, + { MATROSKA_ID_VIDEOPIXELCROPT, EBML_UINT, 0, offsetof(MatroskaTrackVideo, pixel_cropt) }, + { MATROSKA_ID_VIDEOPIXELCROPL, EBML_UINT, 0, offsetof(MatroskaTrackVideo, pixel_cropl) }, + { MATROSKA_ID_VIDEOPIXELCROPR, EBML_UINT, 0, offsetof(MatroskaTrackVideo, pixel_cropr) }, { MATROSKA_ID_VIDEODISPLAYUNIT, EBML_UINT, 0, offsetof(MatroskaTrackVideo, display_unit), { .u= MATROSKA_VIDEO_DISPLAYUNIT_PIXELS } }, { MATROSKA_ID_VIDEOFLAGINTERLACED, EBML_UINT, 0, offsetof(MatroskaTrackVideo, interlaced), { .u = MATROSKA_VIDEO_INTERLACE_FLAG_UNDETERMINED } }, { MATROSKA_ID_VIDEOFIELDORDER, EBML_UINT, 0, offsetof(MatroskaTrackVideo, field_order), { .u = MATROSKA_VIDEO_FIELDORDER_UNDETERMINED } }, @@ -2695,7 +2699,8 @@ static int matroska_parse_tracks(AVFormatContext *s) MatroskaTrackPlane *planes = track->operation.combine_planes.elem; int display_width_mul = 1; int display_height_mul = 1; - + const int cropped_width = track->video.pixel_width - track->video.pixel_cropl - track->video.pixel_cropr; + const int cropped_height = track->video.pixel_height - track->video.pixel_cropt - track->video.pixel_cropb; st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; st->codecpar->codec_tag = fourcc; if (bit_depth >= 0) @@ -2703,6 +2708,29 @@ static int matroska_parse_tracks(AVFormatContext *s) st->codecpar->width = track->video.pixel_width; st->codecpar->height = track->video.pixel_height; + if (track->video.pixel_cropt || track->video.pixel_cropb || + track->video.pixel_cropl || track->video.pixel_cropr) { + AVCleanAperture *metadata = av_clean_aperture_alloc(); + if (!metadata) + return AVERROR(ENOMEM); + metadata->width.num = cropped_width; + metadata->height.num = cropped_height; + metadata->width.den = 1; + metadata->height.den = 1; + av_reduce(&metadata->horizontal_offset.num, + &metadata->horizontal_offset.den, + (int)track->video.pixel_cropl - (int)track->video.pixel_cropr, 2, INT_MAX); + av_reduce(&metadata->vertical_offset.num, + &metadata->vertical_offset.den, + (int)track->video.pixel_cropt - (int)track->video.pixel_cropb, 2, INT_MAX); + ret = av_stream_add_side_data(st, AV_PKT_DATA_CLEAN_APERTURE, + (uint8_t *)metadata, sizeof(*metadata)); + if (ret < 0) { + av_freep(&metadata); + return ret; + } + } + if (track->video.interlaced == MATROSKA_VIDEO_INTERLACE_FLAG_INTERLACED) st->codecpar->field_order = mkv_field_order(matroska, track->video.field_order); else if (track->video.interlaced == MATROSKA_VIDEO_INTERLACE_FLAG_PROGRESSIVE) @@ -2714,8 +2742,8 @@ static int matroska_parse_tracks(AVFormatContext *s) if (track->video.display_unit < MATROSKA_VIDEO_DISPLAYUNIT_UNKNOWN) { av_reduce(&st->sample_aspect_ratio.num, &st->sample_aspect_ratio.den, - st->codecpar->height * track->video.display_width * display_width_mul, - st->codecpar->width * track->video.display_height * display_height_mul, + cropped_height * track->video.display_width * display_width_mul, + cropped_width * track->video.display_height * display_height_mul, 255); } if (st->codecpar->codec_id != AV_CODEC_ID_HEVC) diff --git a/libavformat/mov.c b/libavformat/mov.c index 3d6fef685d..27f75020b4 100644 --- a/libavformat/mov.c +++ b/libavformat/mov.c @@ -5495,6 +5495,36 @@ static int mov_read_clli(MOVContext *c, AVIOContext *pb, MOVAtom atom) return 0; } +static int mov_read_clap(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + MOVStreamContext *sc; + + if (c->fc->nb_streams < 1) + return AVERROR_INVALIDDATA; + + sc = c->fc->streams[c->fc->nb_streams - 1]->priv_data; + + if (atom.size < 32) { + av_log(c->fc, AV_LOG_ERROR, "Clean Aperture box is wrong size\n"); + return AVERROR_INVALIDDATA; + } + + sc->clap = av_clean_aperture_alloc(); + if (!sc->clap) + return AVERROR(ENOMEM); + + sc->clap->width.num = avio_rb32(pb); + sc->clap->width.den = avio_rb32(pb); + sc->clap->height.num = avio_rb32(pb); + sc->clap->height.den = avio_rb32(pb); + sc->clap->horizontal_offset.num = avio_rb32(pb); + sc->clap->horizontal_offset.den = avio_rb32(pb); + sc->clap->vertical_offset.num = avio_rb32(pb); + sc->clap->vertical_offset.den = avio_rb32(pb); + + return 0; +} + static int mov_read_st3d(MOVContext *c, AVIOContext *pb, MOVAtom atom) { AVStream *st; @@ -6941,6 +6971,7 @@ static const MOVParseTableEntry mov_default_parse_table[] = { { MKTAG('c','l','l','i'), mov_read_clli }, { MKTAG('d','v','c','C'), mov_read_dvcc_dvvc }, { MKTAG('d','v','v','C'), mov_read_dvcc_dvvc }, +{ MKTAG('c','l','a','p'), mov_read_clap }, { 0, NULL } }; @@ -7380,6 +7411,7 @@ static int mov_read_close(AVFormatContext *s) av_freep(&sc->spherical); av_freep(&sc->mastering); av_freep(&sc->coll); + av_freep(&sc->clap); } if (mov->dv_demux) { @@ -7744,6 +7776,15 @@ static int mov_read_header(AVFormatContext *s) sc->coll = NULL; } + if (sc->clap) { + err = av_stream_add_side_data(st, AV_PKT_DATA_CLEAN_APERTURE, + (uint8_t *)sc->clap, + sizeof(*sc->clap)); + if (err < 0) + return err; + + sc->clap = NULL; + } break; } } -- 2.26.2.303.gf8c07b1a785-goog
_______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-requ...@ffmpeg.org with subject "unsubscribe".