[FFmpeg-devel] [PATCH 1/3] Add muxing/demuxing of RMHD
Thilo Borgmann
thilo.borgmann at mail.de
Thu Jan 18 20:39:07 EET 2018
This time including the patch...
-------------- next part --------------
From aaeaf7721ba5e9afd79ee13dbb9cc7c3226027b3 Mon Sep 17 00:00:00 2001
From: Thilo Borgmann <thilo.borgmann at mail.de>
Date: Wed, 17 Jan 2018 23:09:03 +0100
Subject: [PATCH 1/3] Add muxing/demuxing of RMHD
---
fftools/ffmpeg.c | 28 +++-
fftools/ffmpeg_opt.c | 2 +-
libavformat/Makefile | 2 +
libavformat/allformats.c | 1 +
libavformat/rm.c | 1 +
libavformat/rm.h | 7 +
libavformat/rmdec.c | 96 +++++++++---
libavformat/rmenc.c | 395 ++++++++++++++++++++++++++++++++++++++---------
libavformat/utils.c | 5 +-
9 files changed, 438 insertions(+), 99 deletions(-)
diff --git a/fftools/ffmpeg.c b/fftools/ffmpeg.c
index 528849a..9bd5724 100644
--- a/fftools/ffmpeg.c
+++ b/fftools/ffmpeg.c
@@ -1296,7 +1296,18 @@ static void do_video_out(OutputFile *of,
if (pkt.pts == AV_NOPTS_VALUE && !(enc->codec->capabilities & AV_CODEC_CAP_DELAY))
pkt.pts = ost->sync_opts;
- av_packet_rescale_ts(&pkt, enc->time_base, ost->mux_timebase);
+ if(enc->codec->id == AV_CODEC_ID_RV60) {
+ AVRational rm_time_base = {1, 1000};
+ if (pkt.pts != AV_NOPTS_VALUE)
+ pkt.pts = av_rescale_q(pkt.pts, enc->time_base, ost->mux_timebase);
+ if (pkt.dts != AV_NOPTS_VALUE && av_cmp_q(rm_time_base, ost->mux_timebase)) {
+ pkt.dts = av_rescale_q(pkt.dts, enc->time_base, ost->mux_timebase);
+ pkt.dts = av_rescale_q(pkt.dts, rm_time_base, enc->time_base);
+ }
+ pkt.pts = pkt.dts;
+ } else {
+ av_packet_rescale_ts(&pkt, enc->time_base, ost->mux_timebase);
+ }
if (debug_ts) {
av_log(NULL, AV_LOG_INFO, "encoder -> type:video "
@@ -1940,7 +1951,20 @@ static void flush_encoders(void)
av_packet_unref(&pkt);
continue;
}
- av_packet_rescale_ts(&pkt, enc->time_base, ost->mux_timebase);
+
+ if(enc->codec->id == AV_CODEC_ID_RV60) {
+ AVRational rm_time_base = {1, 1000};
+ if (pkt.pts != AV_NOPTS_VALUE)
+ pkt.pts = av_rescale_q(pkt.pts, enc->time_base, ost->mux_timebase);
+ if (pkt.dts != AV_NOPTS_VALUE && av_cmp_q(rm_time_base, ost->mux_timebase)) {
+ pkt.dts = av_rescale_q(pkt.dts, enc->time_base, ost->mux_timebase);
+ pkt.dts = av_rescale_q(pkt.dts, rm_time_base, enc->time_base);
+ }
+ pkt.pts = pkt.dts;
+ } else {
+ av_packet_rescale_ts(&pkt, enc->time_base, ost->mux_timebase);
+ }
+
pkt_size = pkt.size;
output_packet(of, &pkt, ost, 0);
if (ost->enc_ctx->codec_type == AVMEDIA_TYPE_VIDEO && vstats_filename) {
diff --git a/fftools/ffmpeg_opt.c b/fftools/ffmpeg_opt.c
index 92199b3..0036468 100644
--- a/fftools/ffmpeg_opt.c
+++ b/fftools/ffmpeg_opt.c
@@ -1747,7 +1747,7 @@ static OutputStream *new_video_stream(OptionsContext *o, AVFormatContext *oc, in
ost->logfile_prefix ? ost->logfile_prefix :
DEFAULT_PASS_LOGFILENAME_PREFIX,
i);
- if (!strcmp(ost->enc->name, "libx264")) {
+ if (!strcmp(ost->enc->name, "libx264") || !strcmp(ost->enc->name, "librv11enc")) {
av_dict_set(&ost->encoder_opts, "stats", logfilename, AV_DICT_DONT_OVERWRITE);
} else {
if (video_enc->flags & AV_CODEC_FLAG_PASS2) {
diff --git a/libavformat/Makefile b/libavformat/Makefile
index de0de92..3a8d0a2 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -414,6 +414,8 @@ OBJS-$(CONFIG_REDSPARK_DEMUXER) += redspark.o
OBJS-$(CONFIG_RL2_DEMUXER) += rl2.o
OBJS-$(CONFIG_RM_DEMUXER) += rmdec.o rm.o rmsipr.o
OBJS-$(CONFIG_RM_MUXER) += rmenc.o rm.o
+OBJS-$(CONFIG_RMHD_DEMUXER) += rmdec.o rm.o rmsipr.o
+OBJS-$(CONFIG_RMHD_MUXER) += rmenc.o rm.o
OBJS-$(CONFIG_ROQ_DEMUXER) += idroqdec.o
OBJS-$(CONFIG_ROQ_MUXER) += idroqenc.o rawenc.o
OBJS-$(CONFIG_RSD_DEMUXER) += rsd.o
diff --git a/libavformat/allformats.c b/libavformat/allformats.c
index ec84096..141ccd2 100644
--- a/libavformat/allformats.c
+++ b/libavformat/allformats.c
@@ -266,6 +266,7 @@ static void register_all(void)
REGISTER_DEMUXER (REDSPARK, redspark);
REGISTER_DEMUXER (RL2, rl2);
REGISTER_MUXDEMUX(RM, rm);
+ REGISTER_MUXDEMUX(RMHD, rmhd);
REGISTER_MUXDEMUX(ROQ, roq);
REGISTER_DEMUXER (RPL, rpl);
REGISTER_DEMUXER (RSD, rsd);
diff --git a/libavformat/rm.c b/libavformat/rm.c
index 52c7ccc..a66c9b6 100644
--- a/libavformat/rm.c
+++ b/libavformat/rm.c
@@ -34,6 +34,7 @@ const AVCodecTag ff_rm_codec_tags[] = {
{ AV_CODEC_ID_RV20, MKTAG('R','V','T','R') },
{ AV_CODEC_ID_RV30, MKTAG('R','V','3','0') },
{ AV_CODEC_ID_RV40, MKTAG('R','V','4','0') },
+ { AV_CODEC_ID_RV60, MKTAG('R','V','6','0') },
{ AV_CODEC_ID_AC3, MKTAG('d','n','e','t') },
{ AV_CODEC_ID_RA_144, MKTAG('l','p','c','J') },
{ AV_CODEC_ID_RA_288, MKTAG('2','8','_','8') },
diff --git a/libavformat/rm.h b/libavformat/rm.h
index 7b080e2..eeb9862 100644
--- a/libavformat/rm.h
+++ b/libavformat/rm.h
@@ -25,6 +25,13 @@
#include "avformat.h"
#include "internal.h"
+#define DEINT_ID_GENR MKTAG('g', 'e', 'n', 'r') ///< interleaving for Cooker/ATRAC
+#define DEINT_ID_INT0 MKTAG('I', 'n', 't', '0') ///< no interleaving needed
+#define DEINT_ID_INT4 MKTAG('I', 'n', 't', '4') ///< interleaving for 28.8
+#define DEINT_ID_SIPR MKTAG('s', 'i', 'p', 'r') ///< interleaving for Sipro
+#define DEINT_ID_VBRF MKTAG('v', 'b', 'r', 'f') ///< VBR case for AAC
+#define DEINT_ID_VBRS MKTAG('v', 'b', 'r', 's') ///< VBR case for AAC
+
extern const char * const ff_rm_metadata[4];
extern const AVCodecTag ff_rm_codec_tags[];
diff --git a/libavformat/rmdec.c b/libavformat/rmdec.c
index fea71a2..1709cd2 100644
--- a/libavformat/rmdec.c
+++ b/libavformat/rmdec.c
@@ -33,13 +33,6 @@
#include "rmsipr.h"
#include "rm.h"
-#define DEINT_ID_GENR MKTAG('g', 'e', 'n', 'r') ///< interleaving for Cooker/ATRAC
-#define DEINT_ID_INT0 MKTAG('I', 'n', 't', '0') ///< no interleaving needed
-#define DEINT_ID_INT4 MKTAG('I', 'n', 't', '4') ///< interleaving for 28.8
-#define DEINT_ID_SIPR MKTAG('s', 'i', 'p', 'r') ///< interleaving for Sipro
-#define DEINT_ID_VBRF MKTAG('v', 'b', 'r', 'f') ///< VBR case for AAC
-#define DEINT_ID_VBRS MKTAG('v', 'b', 'r', 's') ///< VBR case for AAC
-
struct RMStream {
AVPacket pkt; ///< place to store merged video frame / reordered audio data
int videobufsize; ///< current assembled frame size
@@ -330,7 +323,19 @@ int ff_rm_read_mdpr_codecdata(AVFormatContext *s, AVIOContext *pb,
codec_pos = avio_tell(pb);
v = avio_rb32(pb);
- if (v == MKTAG(0xfd, 'a', 'r', '.')) {
+ if (v == MKBETAG('M', 'L', 'T', 'I')) {
+ int number_of_streams = avio_rb16(pb);
+ int number_of_mdpr;
+ int i;
+ for (i = 0; i<number_of_streams; i++)
+ avio_rb16(pb);
+ number_of_mdpr = avio_rb16(pb);
+ if (number_of_mdpr != 1) {
+ avpriv_request_sample(s, "MLTI with multiple MDPR");
+ }
+ avio_rb32(pb);
+ v = avio_rb32(pb);
+ } else if (v == MKTAG(0xfd, 'a', 'r', '.')) {
/* ra type header */
if (rm_read_audio_stream_info(s, pb, st, rst, 0))
return -1;
@@ -424,7 +429,8 @@ skip:
static int rm_read_index(AVFormatContext *s)
{
AVIOContext *pb = s->pb;
- unsigned int size, n_pkts, str_id, next_off, n, pos, pts;
+ unsigned int size, n_pkts, str_id, n, pts, version;
+ uint64_t next_off, pos;
AVStream *st;
do {
@@ -433,10 +439,10 @@ static int rm_read_index(AVFormatContext *s)
size = avio_rb32(pb);
if (size < 20)
return -1;
- avio_skip(pb, 2);
+ version = avio_rb16(pb);
n_pkts = avio_rb32(pb);
str_id = avio_rb16(pb);
- next_off = avio_rb32(pb);
+ next_off = (version == 2) ? avio_rb64(pb) : avio_rb32(pb);
for (n = 0; n < s->nb_streams; n++)
if (s->streams[n]->id == str_id) {
st = s->streams[n];
@@ -459,7 +465,7 @@ static int rm_read_index(AVFormatContext *s)
for (n = 0; n < n_pkts; n++) {
avio_skip(pb, 2);
pts = avio_rb32(pb);
- pos = avio_rb32(pb);
+ pos = (version == 2) ? avio_rb64(pb) : avio_rb32(pb);
avio_skip(pb, 4); /* packet no. */
av_add_index_entry(st, pos, pts, 0, 0, AVINDEX_KEYFRAME);
@@ -540,6 +546,7 @@ static int rm_read_header(AVFormatContext *s)
AVIOContext *pb = s->pb;
unsigned int tag;
int tag_size;
+ unsigned int version;
unsigned int start_time, duration;
unsigned int data_off = 0, indx_off = 0;
char buf[128], mime[128];
@@ -552,7 +559,8 @@ static int rm_read_header(AVFormatContext *s)
if (tag == MKTAG('.', 'r', 'a', 0xfd)) {
/* very old .ra format */
return rm_read_header_old(s);
- } else if (tag != MKTAG('.', 'R', 'M', 'F')) {
+ } else if (tag != MKTAG('.', 'R', 'M', 'F') &&
+ (tag != MKTAG('.', 'R', 'M', 'P'))) {
return AVERROR(EIO);
}
@@ -564,7 +572,7 @@ static int rm_read_header(AVFormatContext *s)
goto fail;
tag = avio_rl32(pb);
tag_size = avio_rb32(pb);
- avio_rb16(pb);
+ version = avio_rb16(pb);
av_log(s, AV_LOG_TRACE, "tag=%s size=%d\n",
av_fourcc2str(tag), tag_size);
if (tag_size < 10 && tag != MKTAG('D', 'A', 'T', 'A'))
@@ -580,7 +588,7 @@ static int rm_read_header(AVFormatContext *s)
duration = avio_rb32(pb); /* duration */
s->duration = av_rescale(duration, AV_TIME_BASE, 1000);
avio_rb32(pb); /* preroll */
- indx_off = avio_rb32(pb); /* index offset */
+ indx_off = (version == 2) ? avio_rb64(pb) : avio_rb32(pb); /* index offset */
data_off = avio_rb32(pb); /* data offset */
avio_rb16(pb); /* nb streams */
flags = avio_rb16(pb); /* flags */
@@ -643,7 +651,13 @@ static int rm_read_header(AVFormatContext *s)
rm->nb_packets = avio_rb32(pb); /* number of packets */
if (!rm->nb_packets && (flags & 4))
rm->nb_packets = 3600 * 25;
- avio_rb32(pb); /* next data header */
+
+ if (version == 2) {
+ avio_rb64(pb);
+ avio_rb64(pb); /* updated size */
+ } else {
+ avio_rb32(pb); /* next data header */
+ }
if (!data_off)
data_off = avio_tell(pb) - 18;
@@ -651,7 +665,11 @@ static int rm_read_header(AVFormatContext *s)
!(s->flags & AVFMT_FLAG_IGNIDX) &&
avio_seek(pb, indx_off, SEEK_SET) >= 0) {
rm_read_index(s);
- avio_seek(pb, data_off + 18, SEEK_SET);
+ if (version == 2) {
+ avio_seek(pb, data_off + 30, SEEK_SET);
+ } else {
+ avio_seek(pb, data_off + 18, SEEK_SET);
+ }
}
return 0;
@@ -1084,12 +1102,25 @@ static int rm_probe(AVProbeData *p)
return 0;
}
+static int rmhd_probe(AVProbeData *p)
+{
+ /* check file header */
+ if ((p->buf[0] == '.' && p->buf[1] == 'R' &&
+ p->buf[2] == 'M' && p->buf[3] == 'P')) {
+ return AVPROBE_SCORE_MAX;
+ } else {
+ return 0;
+ }
+}
+
static int64_t rm_read_dts(AVFormatContext *s, int stream_index,
int64_t *ppos, int64_t pos_limit)
{
RMDemuxContext *rm = s->priv_data;
int64_t pos, dts;
+ int64_t pos_audio, dts_audio;
int stream_index2, flags, len, h;
+ int audio_override = 0;
pos = *ppos;
@@ -1114,20 +1145,32 @@ static int64_t rm_read_dts(AVFormatContext *s, int stream_index,
if(!(h & 0x40)){
seq = avio_r8(s->pb); len--;
}
+ if (!(flags & 2) ||
+ (seq & 0x7F) != 1 ||
+ stream_index2 != stream_index) {
+ audio_override = 0;
+ }
}
if((flags&2) && (seq&0x7F) == 1){
av_log(s, AV_LOG_TRACE, "%d %d-%d %"PRId64" %d\n",
flags, stream_index2, stream_index, dts, seq);
av_add_index_entry(st, pos, dts, 0, 0, AVINDEX_KEYFRAME);
- if(stream_index2 == stream_index)
+ if(stream_index2 == stream_index) {
+ if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
+ dts_audio = dts;
+ pos_audio = pos;
+ audio_override = 1;
+ }
+
break;
+ }
}
avio_skip(s->pb, len);
}
- *ppos = pos;
- return dts;
+ *ppos = (audio_override ? pos_audio : pos);
+ return (audio_override ? dts_audio : dts);
}
static int rm_read_seek(AVFormatContext *s, int stream_index,
@@ -1154,6 +1197,19 @@ AVInputFormat ff_rm_demuxer = {
.read_seek = rm_read_seek,
};
+AVInputFormat ff_rmhd_demuxer = {
+ .name = "rmhd",
+ .long_name = NULL_IF_CONFIG_SMALL("RealMedia HD"),
+ .priv_data_size = sizeof(RMDemuxContext),
+ .read_probe = rmhd_probe,
+ .read_header = rm_read_header,
+ .read_packet = rm_read_packet,
+ .read_close = rm_read_close,
+ .read_timestamp = rm_read_dts,
+ .read_seek = rm_read_seek,
+ .extensions = "rmhd",
+};
+
AVInputFormat ff_rdt_demuxer = {
.name = "rdt",
.long_name = NULL_IF_CONFIG_SMALL("RDT demuxer"),
diff --git a/libavformat/rmenc.c b/libavformat/rmenc.c
index 3bff4da..6170bb2 100644
--- a/libavformat/rmenc.c
+++ b/libavformat/rmenc.c
@@ -22,6 +22,7 @@
#include "avio_internal.h"
#include "rm.h"
#include "libavutil/dict.h"
+#include "libavutil/intreadwrite.h"
typedef struct StreamInfo {
int nb_packets;
@@ -33,13 +34,19 @@ typedef struct StreamInfo {
int nb_frames; /* current frame number */
int total_frames; /* total number of frames */
int num;
+ int32_t deint_id;
AVCodecParameters *par;
} StreamInfo;
+#define MODE_RMVB 0x01
+#define MODE_RMHD 0x02
+
typedef struct RMMuxContext {
StreamInfo streams[2];
StreamInfo *audio_stream, *video_stream;
int data_pos; /* position of the data after the header */
+ int reset_timestamps;
+ int mode;
} RMMuxContext;
/* in ms */
@@ -65,9 +72,8 @@ static void put_str8(AVIOContext *s, const char *tag)
avio_w8(s, *tag++);
}
}
-
static int rv10_write_header(AVFormatContext *ctx,
- int data_size, int index_pos)
+ int64_t data_size, int64_t index_pos)
{
RMMuxContext *rm = ctx->priv_data;
AVIOContext *s = ctx->pb;
@@ -78,15 +84,27 @@ static int rv10_write_header(AVFormatContext *ctx,
int data_offset;
AVDictionaryEntry *tag;
- ffio_wfourcc(s, ".RMF");
- avio_wb32(s,18); /* header size */
- avio_wb16(s,0);
- avio_wb32(s,0);
- avio_wb32(s,4 + ctx->nb_streams); /* num headers */
+ // RMF represent rmvb, RMP represent rmhd
+ if(rm->mode & MODE_RMVB) {
+ ffio_wfourcc(s,".RMF");
+ avio_wb32(s,18); /* header size */
+ avio_wb16(s,0); /* object version */
+ avio_wb32(s,0); /* file version */
+ avio_wb32(s,4 + ctx->nb_streams); /* num headers */
+ ffio_wfourcc(s,"PROP");
+ avio_wb32(s, 50);
+ avio_wb16(s, 0);
+ } else {
+ ffio_wfourcc(s, ".RMP");
+ avio_wb32(s,18); /* header size */
+ avio_wb16(s,1); /* object version */
+ avio_wb32(s,0); /* file version */
+ avio_wb32(s,5 + ctx->nb_streams); /* num headers */
+ ffio_wfourcc(s,"PROP");
+ avio_wb32(s, 54);
+ avio_wb16(s, 2);
+ }
- ffio_wfourcc(s,"PROP");
- avio_wb32(s, 50);
- avio_wb16(s, 0);
packet_max_size = 0;
packet_total_size = 0;
nb_packets = 0;
@@ -115,7 +133,11 @@ static int rv10_write_header(AVFormatContext *ctx,
avio_wb32(s, nb_packets); /* num packets */
avio_wb32(s, duration); /* duration */
avio_wb32(s, BUFFER_DURATION); /* preroll */
- avio_wb32(s, index_pos); /* index offset */
+ if(rm->mode & MODE_RMVB) {
+ avio_wb32(s, index_pos); /* index offset */
+ } else {
+ avio_wb64(s, index_pos); /* index offset */
+ }
/* computation of data the data offset */
data_offset = avio_tell(s);
avio_wb32(s, 0); /* data offset : will be patched after */
@@ -148,11 +170,27 @@ static int rv10_write_header(AVFormatContext *ctx,
if (stream->par->codec_type == AVMEDIA_TYPE_AUDIO) {
desc = "The Audio Stream";
mimetype = "audio/x-pn-realaudio";
- codec_data_size = 73;
+ if (stream->par->codec_id == AV_CODEC_ID_AAC) {
+ codec_data_size = 78;
+ if(stream->par->extradata_size > 0){
+ codec_data_size += stream->par->extradata_size + 1;
+ }
+ } else {
+ codec_data_size = 73;
+ }
} else {
desc = "The Video Stream";
mimetype = "video/x-pn-realvideo";
- codec_data_size = 34;
+ if(stream->par->codec_id == AV_CODEC_ID_RV10 ||
+ stream->par->codec_id == AV_CODEC_ID_RV20 ||
+ stream->par->codec_id == AV_CODEC_ID_RV40) {
+ codec_data_size = 34;
+ } else if(!stream->par->extradata_size &&
+ stream->par->codec_id == AV_CODEC_ID_RV60) {
+ codec_data_size = 40;
+ } else {
+ codec_data_size = 26 + stream->par->extradata_size;
+ }
}
ffio_wfourcc(s,"MDPR");
@@ -184,16 +222,25 @@ static int rv10_write_header(AVFormatContext *ctx,
if (stream->par->codec_type == AVMEDIA_TYPE_AUDIO) {
int coded_frame_size, fscode, sample_rate;
int frame_size = av_get_audio_frame_duration2(stream->par, 0);
+ int version = stream->par->codec_id == AV_CODEC_ID_AAC ? 5 : 4;
sample_rate = stream->par->sample_rate;
coded_frame_size = (stream->par->bit_rate *
frame_size) / (8 * sample_rate);
/* audio codec info */
avio_write(s, ".ra", 3);
avio_w8(s, 0xfd);
- avio_wb32(s, 0x00040000); /* version */
+ if (version == 5) {
+ avio_wb32(s, 0x00050000); /* version */
+ } else {
+ avio_wb32(s, 0x00040000); /* version */
+ }
ffio_wfourcc(s, ".ra4");
avio_wb32(s, 0x01b53530); /* stream length */
- avio_wb16(s, 4); /* unknown */
+ if (version == 5) { /* write version */
+ avio_wb16(s, 5);
+ } else {
+ avio_wb16(s, 4);
+ }
avio_wb32(s, 0x39); /* header size */
switch(sample_rate) {
@@ -225,30 +272,72 @@ static int rv10_write_header(AVFormatContext *ctx,
avio_wb16(s, 0x01);
/* frame length : seems to be very important */
avio_wb16(s, coded_frame_size);
- avio_wb32(s, 0); /* unknown */
- avio_wb16(s, stream->par->sample_rate); /* sample rate */
- avio_wb32(s, 0x10); /* unknown */
- avio_wb16(s, stream->par->channels);
- put_str8(s, "Int0"); /* codec name */
- if (stream->par->codec_tag) {
- avio_w8(s, 4); /* tag length */
- avio_wl32(s, stream->par->codec_tag);
+ if (version == 5) {
+ avio_wb16(s, coded_frame_size);
+ avio_wb32(s, 0);
+ avio_wb32(s, stream->par->sample_rate<<16); /* sample rate */
+ avio_wb32(s, stream->par->sample_rate<<16); /* sample rate */
+ avio_wb32(s, 0x10);
+ avio_wb16(s, stream->par->channels);
+ avio_wb32(s, 0x76627266); /*frbv unknown */
+ stream->deint_id = DEINT_ID_VBRF;
+ if (stream->par->codec_tag) {
+ avio_wl32(s, stream->par->codec_tag);
+ } else {
+ av_log(ctx, AV_LOG_ERROR, "Invalid codec tag\n");
+ return AVERROR(EINVAL);
+ }
+ avio_wb16(s, 0x0100); /* title length set interleaved flag to 1*/
+ avio_wb16(s, 0); /* author length */
+ if (stream->par->extradata_size > 0) {
+ avio_wb32(s, stream->par->extradata_size + 1); /* tag length */
+ avio_w8(s, 2); /* tag length */
+ for(i = 0; i< stream->par->extradata_size; i++) {
+ avio_w8(s, stream->par->extradata[i]); /* end of header */
+ }
+ } else {
+ avio_wb32(s, 0);
+ }
} else {
- av_log(ctx, AV_LOG_ERROR, "Invalid codec tag\n");
- return -1;
+ avio_wb32(s, 0);
+ avio_wb16(s, stream->par->sample_rate); /* sample rate */
+ avio_wb32(s, 0x10);
+ avio_wb16(s, stream->par->channels);
+ put_str8(s, "Int0"); /* codec name */
+ if (stream->par->codec_tag) {
+ avio_w8(s, 4); /* tag length */
+ avio_wl32(s, stream->par->codec_tag);
+ } else {
+ av_log(ctx, AV_LOG_ERROR, "Invalid codec tag\n");
+ return -1;
+ }
+ avio_wb16(s, 0); /* title length */
+ avio_wb16(s, 0); /* author length */
+ avio_wb16(s, 0); /* copyright length */
+ avio_w8(s, 0); /* end of header */
}
- avio_wb16(s, 0); /* title length */
- avio_wb16(s, 0); /* author length */
- avio_wb16(s, 0); /* copyright length */
- avio_w8(s, 0); /* end of header */
} else {
/* video codec info */
avio_wb32(s,34); /* size */
ffio_wfourcc(s, "VIDO");
- if(stream->par->codec_id == AV_CODEC_ID_RV10)
+ switch (stream->par->codec_id) {
+ case AV_CODEC_ID_RV10:
ffio_wfourcc(s,"RV10");
- else
+ break;
+ case AV_CODEC_ID_RV20:
ffio_wfourcc(s,"RV20");
+ break;
+ case AV_CODEC_ID_RV30:
+ ffio_wfourcc(s,"RV30");
+ break;
+ case AV_CODEC_ID_RV40:
+ ffio_wfourcc(s,"RV40");
+ break;
+ case AV_CODEC_ID_RV60:
+ ffio_wfourcc(s,"RV60");
+ break;
+ }
+
avio_wb16(s, stream->par->width);
avio_wb16(s, stream->par->height);
@@ -259,17 +348,45 @@ static int rv10_write_header(AVFormatContext *ctx,
avio_wb16(s, stream->frame_rate.num / stream->frame_rate.den); /* frames per seconds ? */
avio_wb32(s,0); /* unknown meaning */
- avio_wb16(s, stream->frame_rate.num / stream->frame_rate.den); /* unknown meaning */
- avio_wb32(s,0); /* unknown meaning */
- avio_wb16(s, 8); /* unknown meaning */
- /* Seems to be the codec version: only use basic H.263. The next
- versions seems to add a differential DC coding as in
- MPEG... nothing new under the sun. */
- if(stream->par->codec_id == AV_CODEC_ID_RV10)
- avio_wb32(s,0x10000000);
- else
- avio_wb32(s,0x20103001);
- //avio_wb32(s,0x10003000);
+
+ if(rm->mode & MODE_RMVB) {
+ avio_wb16(s, stream->frame_rate.num / stream->frame_rate.den); /* unknown meaning */
+ avio_wb32(s,0); /* unknown meaning */
+ avio_wb16(s, 8); /* unknown meaning */
+ /* Seems to be the codec version: only use basic H.263. The next
+ versions seems to add a differential DC coding as in
+ MPEG... nothing new under the sun. */
+ if(stream->par->codec_id == AV_CODEC_ID_RV10)
+ avio_wb32(s,0x10000000);
+ else
+ avio_wb32(s,0x20103001);
+ //avio_wb32(s,0x10003000);
+ } else {
+ avio_wb32(s, ((int)av_q2d(stream->frame_rate)*(1<<16))); /* unknown meaning */
+ if (stream->par->codec_id == AV_CODEC_ID_RV10 ||
+ stream->par->codec_id == AV_CODEC_ID_RV20 ||
+ stream->par->codec_id == AV_CODEC_ID_RV40) {
+ avio_wb32(s, 8); /* unknown meaning */
+ if (stream->par->codec_id == AV_CODEC_ID_RV10) {
+ avio_wb32(s,0x10000000);
+ } else if (stream->par->codec_id == AV_CODEC_ID_RV20) {
+ avio_wb32(s,0x20103001);
+ } else { // AV_CODEC_ID_RV40
+ avio_wb32(s,0x40008000);
+ }
+ } else if (stream->par->extradata_size >0) {
+ for (i = 0; i< stream->par->extradata_size; i++) {
+ avio_w8(s, stream->par->extradata[i]); /* end of header */
+ }
+ } else if (!stream->par->extradata_size &&
+ stream->par->codec_id == AV_CODEC_ID_RV60) {
+ avio_wb32(s, 0x01081000);
+ avio_wb32(s, 0x40008000);
+ avio_wb16(s, 0x0);
+ avio_wb16(s, stream->par->width);
+ avio_wb16(s, stream->par->height);
+ }
+ }
}
}
@@ -282,11 +399,20 @@ static int rv10_write_header(AVFormatContext *ctx,
/* data stream */
ffio_wfourcc(s, "DATA");
- avio_wb32(s,data_size + 10 + 8);
- avio_wb16(s,0);
- avio_wb32(s, nb_packets); /* number of packets */
- avio_wb32(s,0); /* next data header */
+ if (rm->mode & MODE_RMVB) {
+ avio_wb32(s, data_size + 10 + 8);
+ avio_wb16(s, 0);
+ avio_wb32(s, nb_packets); /* number of packets */
+ avio_wb32(s, 0); /* next data header */
+ } else {
+ avio_wb32(s, 0);
+ avio_wb16(s, 2);
+ avio_wb32(s, nb_packets); /* number of packets */
+ avio_wb64(s, 0); /* next data header */
+ avio_wb64(s, data_size + 30); /* updated_size */
+ }
+
return 0;
}
@@ -317,6 +443,12 @@ static int rm_write_header(AVFormatContext *s)
int n;
AVCodecParameters *par;
+ rm->mode = MODE_RMVB;
+
+ if (s->oformat && !strcmp("rmhd", s->oformat->name)) {
+ rm->mode = MODE_RMHD;
+ }
+
if (s->nb_streams > 2) {
av_log(s, AV_LOG_ERROR, "At most 2 streams are currently supported for muxing in RM\n");
return AVERROR_PATCHWELCOME;
@@ -371,15 +503,21 @@ static int rm_write_audio(AVFormatContext *s, const uint8_t *buf, int size, int
StreamInfo *stream = rm->audio_stream;
int i;
- write_packet_header(s, stream, size, !!(flags & AV_PKT_FLAG_KEY));
-
if (stream->par->codec_id == AV_CODEC_ID_AC3) {
/* for AC-3, the words seem to be reversed */
+ write_packet_header(s, stream, size, !!(flags & AV_PKT_FLAG_KEY));
for (i = 0; i < size; i += 2) {
avio_w8(pb, buf[i + 1]);
avio_w8(pb, buf[i]);
}
+ } else if (stream->deint_id == DEINT_ID_VBRF &&
+ stream->par->codec_id == AV_CODEC_ID_AAC) {
+ write_packet_header(s, stream, size + 4, !!(flags & AV_PKT_FLAG_KEY));
+ avio_wb16(pb, 0x10); // total frame size
+ avio_wb16(pb, size & 0xFFFF); // total frame size
+ avio_write(pb, buf, size);
} else {
+ write_packet_header(s, stream, size, !!(flags & AV_PKT_FLAG_KEY));
avio_write(pb, buf, size);
}
stream->nb_frames++;
@@ -395,32 +533,78 @@ static int rm_write_video(AVFormatContext *s, const uint8_t *buf, int size, int
/* XXX: this is incorrect: should be a parameter */
- /* Well, I spent some time finding the meaning of these bits. I am
- not sure I understood everything, but it works !! */
- if (size > MAX_PACKET_SIZE) {
- av_log(s, AV_LOG_ERROR, "Muxing packets larger than 64 kB (%d) is not supported\n", size);
- return AVERROR_PATCHWELCOME;
- }
- write_packet_header(s, stream, size + 7 + (size >= 0x4000)*4, key_frame);
- /* bit 7: '1' if final packet of a frame converted in several packets */
- avio_w8(pb, 0x81);
- /* bit 7: '1' if I-frame. bits 6..0 : sequence number in current
- frame starting from 1 */
- if (key_frame) {
- avio_w8(pb, 0x81);
- } else {
- avio_w8(pb, 0x01);
- }
- if(size >= 0x4000){
- avio_wb32(pb, size); /* total frame size */
- avio_wb32(pb, size); /* offset from the start or the end */
+ if (stream->par->codec_id == AV_CODEC_ID_RV30 ||
+ stream->par->codec_id == AV_CODEC_ID_RV40 ||
+ stream->par->codec_id == AV_CODEC_ID_RV60) {
+ const uint8_t* seg_buf = NULL;
+ int seg_offset = 0;
+ int segments = buf[0] + 1;
+ int total_size = size - (1 + segments * 8);
+ int seg_size;
+ uint8_t byte1;
+ uint8_t byte2;
+ int part_val;
+ int i;
+
+ for (i = 0; i < segments; i++) {
+ seg_buf = buf + 1 + segments * 8 + AV_RL32(buf + 5 + i * 8);
+ if (segments - i > 1) {
+ seg_size = AV_RL32(buf + 13 + 8 * i) - AV_RL32(buf + 5 + 8 * i);
+ } else {
+ seg_size = buf + size - seg_buf;
+ }
+
+ if ((i + 1) == segments) {
+ byte1 = 0x80 | (0x3F & (segments >> 1));
+ part_val = seg_size;
+ } else {
+ byte1 = 0x3F & (segments >> 1);
+ part_val = seg_offset;
+ }
+ byte2 = (0x80 & (segments << 7)) | (0x7f & (i + 1));
+ write_packet_header(s, stream, seg_size + 7 + (total_size >= 0x4000) * 2 + (part_val >= 0x4000) * 2, key_frame);
+ avio_w8(pb, byte1);
+ avio_w8(pb, byte2);
+ if (total_size >= 0x4000) {
+ avio_wb32(pb, total_size); // total frame size
+ } else {
+ avio_wb16(pb, 0x4000 | total_size); // total frame size
+ }
+ if (part_val >= 0x4000) {
+ avio_wb32(pb, part_val); // offset from the start or the end
+ } else {
+ avio_wb16(pb, 0x4000 | part_val); // offset from the start or the end
+ }
+ seg_offset += seg_size;
+ avio_w8(pb, stream->nb_frames & 0xff);
+ avio_write(pb, seg_buf, seg_size);
+ }
}else{
- avio_wb16(pb, 0x4000 | size); /* total frame size */
- avio_wb16(pb, 0x4000 | size); /* offset from the start or the end */
- }
- avio_w8(pb, stream->nb_frames & 0xff);
+ if (size > MAX_PACKET_SIZE) {
+ av_log(s, AV_LOG_ERROR, "Muxing packets larger than 64 kB (%d) is not supported\n", size);
+ return AVERROR_PATCHWELCOME;
+ }
+ write_packet_header(s, stream, size + 7 + (size >= 0x4000)*4, key_frame);
+ // bit 7: '1' if final packet of a frame converted in several packets
+ avio_w8(pb, 0x81);
+ // bit 7: '1' if I frame. bits 6..0 : sequence number in current
+ // frame starting from 1
+ if (key_frame) {
+ avio_w8(pb, 0x81);
+ } else {
+ avio_w8(pb, 0x01);
+ }
+ if (size >= 0x4000) {
+ avio_wb32(pb, size); /* total frame size */
+ avio_wb32(pb, size); /* offset from the start or the end */
+ } else {
+ avio_wb16(pb, 0x4000 | size); /* total frame size */
+ avio_wb16(pb, 0x4000 | size); /* offset from the start or the end */
+ }
+ avio_w8(pb, stream->nb_frames & 0xff);
- avio_write(pb, buf, size);
+ avio_write(pb, buf, size);
+ }
stream->nb_frames++;
return 0;
@@ -438,15 +622,64 @@ static int rm_write_packet(AVFormatContext *s, AVPacket *pkt)
static int rm_write_trailer(AVFormatContext *s)
{
RMMuxContext *rm = s->priv_data;
- int data_size, index_pos, i;
+ int data_size, index_pos, i, j;
AVIOContext *pb = s->pb;
+ AVIndexEntry *entries = NULL;
+ unsigned char *data_offset_ptr = NULL;
+ unsigned int index_data_pos = 0;
+ unsigned int timestamp, n_pkts;
+ int64_t offset;
if (s->pb->seekable & AVIO_SEEKABLE_NORMAL) {
/* end of file: finish to write header */
index_pos = avio_tell(pb);
data_size = index_pos - rm->data_pos;
- /* FIXME: write index */
+ /* write index */
+ for (i = 0; i < s->nb_streams; i++) {
+ AVStream *st = s->streams[i];
+ entries = st->index_entries;
+ n_pkts = st->nb_index_entries;
+ if (n_pkts < 1) {
+ break;
+ }
+ if (data_offset_ptr) {
+ /* patch data offset field */
+ index_data_pos = avio_tell(pb);
+ data_offset_ptr[0] = index_data_pos >> 24;
+ data_offset_ptr[1] = index_data_pos >> 16;
+ data_offset_ptr[2] = index_data_pos >> 8;
+ data_offset_ptr[3] = index_data_pos;
+ }
+ ffio_wfourcc(pb,"INDX");
+ if (rm->mode & MODE_RMVB) {
+ avio_wb32(pb, n_pkts * 14 + 20);
+ } else {
+ avio_wb32(pb, n_pkts * 18 + 24);
+ }
+ avio_wb16(pb, 2);
+ avio_wb32(pb, n_pkts);
+ avio_wb16(pb, st->id);
+ data_offset_ptr = pb->buf_ptr;
+ if (rm->mode & MODE_RMVB) {
+ avio_wb32(pb,0);
+ } else {
+ avio_wb64(pb, 0); /* data offset gets written later */
+ }
+
+ for (j = 0; j < n_pkts; j++) {
+ timestamp = entries[j].timestamp;
+ offset = entries[j].pos;
+ avio_wb16(pb, 2);
+ avio_wb32(pb, timestamp);
+ if (rm->mode & MODE_RMVB) {
+ avio_wb32(pb, (int32_t)offset);
+ } else {
+ avio_wb64(pb, offset);
+ }
+ avio_wb32(pb, 0);
+ }
+ }
/* undocumented end header */
avio_wb32(pb, 0);
@@ -479,3 +712,17 @@ AVOutputFormat ff_rm_muxer = {
.write_trailer = rm_write_trailer,
.codec_tag = (const AVCodecTag* const []){ ff_rm_codec_tags, 0 },
};
+
+AVOutputFormat ff_rmhd_muxer = {
+ .name = "rmhd",
+ .long_name = NULL_IF_CONFIG_SMALL("RealMedia HD"),
+ .mime_type = "application/vnd.rn-realmedia",
+ .extensions = "rmhd",
+ .priv_data_size = sizeof(RMMuxContext),
+ .audio_codec = AV_CODEC_ID_AAC,
+ .video_codec = AV_CODEC_ID_RV60,
+ .write_header = rm_write_header,
+ .write_packet = rm_write_packet,
+ .write_trailer = rm_write_trailer,
+ .codec_tag = (const AVCodecTag* const []){ ff_rm_codec_tags, 0 },
+};
diff --git a/libavformat/utils.c b/libavformat/utils.c
index 3d73341..3d18f6b 100644
--- a/libavformat/utils.c
+++ b/libavformat/utils.c
@@ -2942,9 +2942,10 @@ static int has_codec_parameters(AVStream *st, const char **errmsg_ptr)
FAIL("unspecified size");
if (st->info->found_decoder >= 0 && avctx->pix_fmt == AV_PIX_FMT_NONE)
FAIL("unspecified pixel format");
- if (st->codecpar->codec_id == AV_CODEC_ID_RV30 || st->codecpar->codec_id == AV_CODEC_ID_RV40)
+ if (st->codecpar->codec_id == AV_CODEC_ID_RV30 || st->codecpar->codec_id == AV_CODEC_ID_RV40 ||
+ st->codecpar->codec_id == AV_CODEC_ID_RV60)
if (!st->sample_aspect_ratio.num && !st->codecpar->sample_aspect_ratio.num && !st->codec_info_nb_frames)
- FAIL("no frame in rv30/40 and no sar");
+ FAIL("no frame in rv30/40/60 and no sar");
break;
case AVMEDIA_TYPE_SUBTITLE:
if (avctx->codec_id == AV_CODEC_ID_HDMV_PGS_SUBTITLE && !avctx->width)
--
2.9.3
More information about the ffmpeg-devel
mailing list