[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