On Tue, Apr 28, 2020 at 3:18 AM Nicolas George <[email protected]> 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 <[email protected]>
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 <[email protected]>
---
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 <[email protected]>
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 <[email protected]>
---
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
[email protected]
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
[email protected] with subject "unsubscribe".