avformat/riffenc: indicate storage of flipped RGB bitmaps

Some legacy applications such as AVI2MVE expect raw RGB bitmaps
to be stored bottom-up, whereas our RIFF BITMAPINFOHEADER assumes
they are always stored top-down and thus write a negative value
for height. This can prevent reading of these files.

Option flipped_raw_rgb added to AVI and Matroska muxers
which will write positive value for height when enabled.

Note that the user has to flip the bitmaps beforehand using other
means such as the vflip filter.
pull/345/head
Gyan Doshi 4 years ago
parent a54b367c78
commit 1ec2b3de5a
  1. 13
      doc/muxers.texi
  2. 2
      libavformat/asfenc.c
  3. 4
      libavformat/avienc.c
  4. 5
      libavformat/matroskaenc.c
  5. 2
      libavformat/riff.h
  6. 7
      libavformat/riffenc.c
  7. 2
      libavformat/wtvenc.c

@ -89,6 +89,12 @@ specific scenarios, e.g. when merging multiple audio streams into one for
compatibility with software that only supports a single audio stream in AVI compatibility with software that only supports a single audio stream in AVI
(see @ref{amerge,,the "amerge" section in the ffmpeg-filters manual,ffmpeg-filters}). (see @ref{amerge,,the "amerge" section in the ffmpeg-filters manual,ffmpeg-filters}).
@item flipped_raw_rgb
If set to true, store positive height for raw RGB bitmaps, which indicates
bitmap is stored bottom-up. Note that this option does not flip the bitmap
which has to be done manually beforehand, e.g. by using the vflip filter.
Default is @var{false} and indicates bitmap is stored top down.
@end table @end table
@anchor{chromaprint} @anchor{chromaprint}
@ -1413,6 +1419,13 @@ disposition default exists, no subtitle track will be marked as default.
In this mode the FlagDefault is set if and only if the AV_DISPOSITION_DEFAULT In this mode the FlagDefault is set if and only if the AV_DISPOSITION_DEFAULT
flag is set in the disposition of the corresponding stream. flag is set in the disposition of the corresponding stream.
@end table @end table
@item flipped_raw_rgb
If set to true, store positive height for raw RGB bitmaps, which indicates
bitmap is stored bottom-up. Note that this option does not flip the bitmap
which has to be done manually beforehand, e.g. by using the vflip filter.
Default is @var{false} and indicates bitmap is stored top down.
@end table @end table
@anchor{md5} @anchor{md5}

@ -682,7 +682,7 @@ static int asf_write_header1(AVFormatContext *s, int64_t file_size,
avio_wl16(pb, 40 + par->extradata_size); /* size */ avio_wl16(pb, 40 + par->extradata_size); /* size */
/* BITMAPINFOHEADER header */ /* BITMAPINFOHEADER header */
ff_put_bmp_header(pb, par, 1, 0); ff_put_bmp_header(pb, par, 1, 0, 0);
} }
end_header(pb, hpos); end_header(pb, hpos);
} }

@ -72,6 +72,7 @@ typedef struct AVIContext {
int reserve_index_space; int reserve_index_space;
int master_index_max_size; int master_index_max_size;
int write_channel_mask; int write_channel_mask;
int flipped_raw_rgb;
} AVIContext; } AVIContext;
typedef struct AVIStream { typedef struct AVIStream {
@ -449,7 +450,7 @@ static int avi_write_header(AVFormatContext *s)
&& par->bits_per_coded_sample == 15) && par->bits_per_coded_sample == 15)
par->bits_per_coded_sample = 16; par->bits_per_coded_sample = 16;
avist->pal_offset = avio_tell(pb) + 40; avist->pal_offset = avio_tell(pb) + 40;
ff_put_bmp_header(pb, par, 0, 0); ff_put_bmp_header(pb, par, 0, 0, avi->flipped_raw_rgb);
pix_fmt = avpriv_find_pix_fmt(avpriv_pix_fmt_bps_avi, pix_fmt = avpriv_find_pix_fmt(avpriv_pix_fmt_bps_avi,
par->bits_per_coded_sample); par->bits_per_coded_sample);
if ( !par->codec_tag if ( !par->codec_tag
@ -993,6 +994,7 @@ static void avi_deinit(AVFormatContext *s)
static const AVOption options[] = { static const AVOption options[] = {
{ "reserve_index_space", "reserve space (in bytes) at the beginning of the file for each stream index", OFFSET(reserve_index_space), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, ENC }, { "reserve_index_space", "reserve space (in bytes) at the beginning of the file for each stream index", OFFSET(reserve_index_space), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, ENC },
{ "write_channel_mask", "write channel mask into wave format header", OFFSET(write_channel_mask), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, ENC }, { "write_channel_mask", "write channel mask into wave format header", OFFSET(write_channel_mask), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, ENC },
{ "flipped_raw_rgb", "Raw RGB bitmaps are stored bottom-up", OFFSET(flipped_raw_rgb), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, ENC },
{ NULL }, { NULL },
}; };

@ -154,6 +154,7 @@ typedef struct MatroskaMuxContext {
int is_dash; int is_dash;
int dash_track_number; int dash_track_number;
int allow_raw_vfw; int allow_raw_vfw;
int flipped_raw_rgb;
int default_mode; int default_mode;
uint32_t segment_uid[4]; uint32_t segment_uid[4];
@ -764,6 +765,7 @@ static int mkv_write_codecprivate(AVFormatContext *s, AVIOContext *pb,
int native_id, int qt_id) int native_id, int qt_id)
{ {
AVIOContext *dyn_cp; AVIOContext *dyn_cp;
MatroskaMuxContext *mkv = s->priv_data;
uint8_t *codecpriv; uint8_t *codecpriv;
int ret, codecpriv_size; int ret, codecpriv_size;
@ -802,7 +804,7 @@ static int mkv_write_codecprivate(AVFormatContext *s, AVIOContext *pb,
ret = AVERROR(EINVAL); ret = AVERROR(EINVAL);
} }
ff_put_bmp_header(dyn_cp, par, 0, 0); ff_put_bmp_header(dyn_cp, par, 0, 0, mkv->flipped_raw_rgb);
} }
} else if (par->codec_type == AVMEDIA_TYPE_AUDIO) { } else if (par->codec_type == AVMEDIA_TYPE_AUDIO) {
unsigned int tag; unsigned int tag;
@ -2787,6 +2789,7 @@ static const AVOption options[] = {
{ "dash_track_number", "Track number for the DASH stream", OFFSET(dash_track_number), AV_OPT_TYPE_INT, { .i64 = 1 }, 1, INT_MAX, FLAGS }, { "dash_track_number", "Track number for the DASH stream", OFFSET(dash_track_number), AV_OPT_TYPE_INT, { .i64 = 1 }, 1, INT_MAX, FLAGS },
{ "live", "Write files assuming it is a live stream.", OFFSET(is_live), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS }, { "live", "Write files assuming it is a live stream.", OFFSET(is_live), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS },
{ "allow_raw_vfw", "allow RAW VFW mode", OFFSET(allow_raw_vfw), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS }, { "allow_raw_vfw", "allow RAW VFW mode", OFFSET(allow_raw_vfw), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS },
{ "flipped_raw_rgb", "Raw RGB bitmaps in VFW mode are stored bottom-up", OFFSET(flipped_raw_rgb), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS },
{ "write_crc32", "write a CRC32 element inside every Level 1 element", OFFSET(write_crc), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, FLAGS }, { "write_crc32", "write a CRC32 element inside every Level 1 element", OFFSET(write_crc), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, FLAGS },
{ "default_mode", "Controls how a track's FlagDefault is inferred", OFFSET(default_mode), AV_OPT_TYPE_INT, { .i64 = DEFAULT_MODE_INFER }, DEFAULT_MODE_INFER, DEFAULT_MODE_PASSTHROUGH, FLAGS, "default_mode" }, { "default_mode", "Controls how a track's FlagDefault is inferred", OFFSET(default_mode), AV_OPT_TYPE_INT, { .i64 = DEFAULT_MODE_INFER }, DEFAULT_MODE_INFER, DEFAULT_MODE_PASSTHROUGH, FLAGS, "default_mode" },
{ "infer", "For each track type, mark the first track of disposition default as default; if none exists, mark the first track as default.", 0, AV_OPT_TYPE_CONST, { .i64 = DEFAULT_MODE_INFER }, 0, 0, FLAGS, "default_mode" }, { "infer", "For each track type, mark the first track of disposition default as default; if none exists, mark the first track as default.", 0, AV_OPT_TYPE_CONST, { .i64 = DEFAULT_MODE_INFER }, 0, 0, FLAGS, "default_mode" },

@ -46,7 +46,7 @@ void ff_end_tag(AVIOContext *pb, int64_t start);
*/ */
int ff_get_bmp_header(AVIOContext *pb, AVStream *st, uint32_t *size); int ff_get_bmp_header(AVIOContext *pb, AVStream *st, uint32_t *size);
void ff_put_bmp_header(AVIOContext *pb, AVCodecParameters *par, int for_asf, int ignore_extradata); void ff_put_bmp_header(AVIOContext *pb, AVCodecParameters *par, int for_asf, int ignore_extradata, int rgb_frame_is_flipped);
/** /**
* Tell ff_put_wav_header() to use WAVEFORMATEX even for PCM codecs. * Tell ff_put_wav_header() to use WAVEFORMATEX even for PCM codecs.

@ -207,10 +207,11 @@ int ff_put_wav_header(AVFormatContext *s, AVIOContext *pb,
/* BITMAPINFOHEADER header */ /* BITMAPINFOHEADER header */
void ff_put_bmp_header(AVIOContext *pb, AVCodecParameters *par, void ff_put_bmp_header(AVIOContext *pb, AVCodecParameters *par,
int for_asf, int ignore_extradata) int for_asf, int ignore_extradata, int rgb_frame_is_flipped)
{ {
int keep_height = par->extradata_size >= 9 && int keep_height = (par->extradata_size >= 9 &&
!memcmp(par->extradata + par->extradata_size - 9, "BottomUp", 9); !memcmp(par->extradata + par->extradata_size - 9, "BottomUp", 9)) ||
rgb_frame_is_flipped;
int extradata_size = par->extradata_size - 9*keep_height; int extradata_size = par->extradata_size - 9*keep_height;
enum AVPixelFormat pix_fmt = par->format; enum AVPixelFormat pix_fmt = par->format;
int pal_avi; int pal_avi;

@ -241,7 +241,7 @@ static void put_videoinfoheader2(AVIOContext *pb, AVStream *st)
avio_wl32(pb, 0); avio_wl32(pb, 0);
avio_wl32(pb, 0); avio_wl32(pb, 0);
ff_put_bmp_header(pb, st->codecpar, 0, 1); ff_put_bmp_header(pb, st->codecpar, 0, 1, 0);
if (st->codecpar->codec_id == AV_CODEC_ID_MPEG2VIDEO) { if (st->codecpar->codec_id == AV_CODEC_ID_MPEG2VIDEO) {
int padding = (st->codecpar->extradata_size & 3) ? 4 - (st->codecpar->extradata_size & 3) : 0; int padding = (st->codecpar->extradata_size & 3) ? 4 - (st->codecpar->extradata_size & 3) : 0;

Loading…
Cancel
Save