[FFmpeg-devel] [V3 PATCH 1/3] Add muxing/demuxing of RMHD

Thilo Borgmann thilo.borgmann at mail.de
Mon Apr 23 00:15:24 EEST 2018


-Thilo
-------------- next part --------------
From a8d3f717d2e35f2b81cd05e5baf4a6dc5900ee34 Mon Sep 17 00:00:00 2001
From: Thilo Borgmann <thilo.borgmann at mail.de>
Date: Tue, 17 Apr 2018 22:53:02 +0200
Subject: [PATCH 1/3] Add muxing/demuxing of RMHD

---
 libavformat/Makefile     |   1 +
 libavformat/allformats.c |   1 +
 libavformat/rm.c         |   1 +
 libavformat/rm.h         |   7 +
 libavformat/rmdec.c      |  50 ++++--
 libavformat/rmenc.c      | 425 ++++++++++++++++++++++++++++++++++++++---------
 libavformat/utils.c      |   5 +-
 7 files changed, 390 insertions(+), 100 deletions(-)

diff --git a/libavformat/Makefile b/libavformat/Makefile
index 3eeca50..a579fc0 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -421,6 +421,7 @@ 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_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 d582778..de31fd9 100644
--- a/libavformat/allformats.c
+++ b/libavformat/allformats.c
@@ -337,6 +337,7 @@ extern AVInputFormat  ff_redspark_demuxer;
 extern AVInputFormat  ff_rl2_demuxer;
 extern AVInputFormat  ff_rm_demuxer;
 extern AVOutputFormat ff_rm_muxer;
+extern AVOutputFormat ff_rmhd_muxer;
 extern AVInputFormat  ff_roq_demuxer;
 extern AVOutputFormat ff_roq_muxer;
 extern AVInputFormat  ff_rpl_demuxer;
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 ac61723..302f12a 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
@@ -420,7 +413,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 {
@@ -429,10 +423,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];
@@ -455,7 +449,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);
@@ -536,6 +530,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];
@@ -548,7 +543,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);
     }
 
@@ -560,7 +556,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'))
@@ -576,7 +572,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 */
@@ -639,7 +635,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;
@@ -647,7 +649,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;
@@ -1076,10 +1082,22 @@ static int rm_probe(AVProbeData *p)
         (p->buf[0] == '.' && p->buf[1] == 'r' &&
          p->buf[2] == 'a' && p->buf[3] == 0xfd))
         return AVPROBE_SCORE_MAX;
+    else if (AV_RL32(&p->buf[0]) == MKTAG('.', 'R', 'M', 'P'))
+        return AVPROBE_SCORE_MAX;
     else
         return 0;
 }
 
+static int rmhd_probe(AVProbeData *p)
+{
+    /* check file header */
+    if (AV_RL32(&p->buf[0]) == MKTAG('.', 'R', 'M', '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)
 {
diff --git a/libavformat/rmenc.c b/libavformat/rmenc.c
index 3bff4da..8fb8980 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,18 @@ 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 mode;
 } RMMuxContext;
 
 /* in ms */
@@ -67,26 +73,38 @@ static void put_str8(AVIOContext *s, const char *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;
     StreamInfo *stream;
     const char *desc, *mimetype;
-    int nb_packets, packet_total_size, packet_max_size, size, packet_avg_size, i;
+    int nb_packets, packet_total_size, packet_max_size, size, packet_avg_size, i, j;
     int bit_rate, v, duration, flags;
     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_wb16(s, 0x10);
+                avio_wb16(s, stream->par->channels);
+                avio_wb32(s, 0x76627266);
+                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(j = 0; j< stream->par->extradata_size; j++) {
+                        avio_w8(s, stream->par->extradata[j]); /* 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,44 @@ 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);
+            } 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 (j = 0; j< stream->par->extradata_size; j++) {
+                        avio_w8(s, stream->par->extradata[j]); /* 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,19 +398,29 @@ 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;
 }
 
 static void write_packet_header(AVFormatContext *ctx, StreamInfo *stream,
-                                int length, int key_frame)
+                                int length, int key_frame, AVPacket *pkt)
 {
     int timestamp;
     AVIOContext *s = ctx->pb;
+    RMMuxContext *rm = ctx->priv_data;
 
     stream->nb_packets++;
     stream->packet_total_size += length;
@@ -304,7 +430,11 @@ static void write_packet_header(AVFormatContext *ctx, StreamInfo *stream,
     avio_wb16(s,0); /* version */
     avio_wb16(s,length + 12);
     avio_wb16(s, stream->num); /* stream number */
-    timestamp = av_rescale_q_rnd(stream->nb_frames, (AVRational){1000, 1}, stream->frame_rate, AV_ROUND_ZERO);
+    if(pkt && rm->mode & MODE_RMHD) {
+        timestamp = pkt->pts;
+    } else {
+        timestamp = av_rescale_q_rnd(stream->nb_frames, (AVRational){1000, 1}, stream->frame_rate, AV_ROUND_ZERO);
+    }
     avio_wb32(s, timestamp); /* timestamp */
     avio_w8(s, 0); /* reserved */
     avio_w8(s, key_frame ? 2 : 0); /* flags */
@@ -317,6 +447,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;
@@ -334,6 +470,10 @@ static int rm_write_header(AVFormatContext *s)
         stream->bit_rate = par->bit_rate;
         stream->par = par;
 
+        if (rm->mode & MODE_RMHD) {
+            avpriv_set_pts_info(st, 32, 1, 1000);
+        }
+
         switch (par->codec_type) {
         case AVMEDIA_TYPE_AUDIO:
             rm->audio_stream = stream;
@@ -346,8 +486,12 @@ static int rm_write_header(AVFormatContext *s)
             break;
         case AVMEDIA_TYPE_VIDEO:
             rm->video_stream = stream;
-            // TODO: should be avg_frame_rate
-            stream->frame_rate = av_inv_q(st->time_base);
+            if (st->codecpar->codec_id == AV_CODEC_ID_RV40 ||
+                st->codecpar->codec_id == AV_CODEC_ID_RV60) {
+                stream->frame_rate = st->avg_frame_rate;
+            } else {
+                stream->frame_rate = av_inv_q(st->time_base);
+            }
             /* XXX: dummy values */
             stream->packet_max_size = 4096;
             stream->nb_packets = 0;
@@ -364,29 +508,37 @@ static int rm_write_header(AVFormatContext *s)
     return 0;
 }
 
-static int rm_write_audio(AVFormatContext *s, const uint8_t *buf, int size, int flags)
+static int rm_write_audio(AVFormatContext *s, const uint8_t *buf,
+                          int size, int flags, AVPacket *pkt)
 {
     RMMuxContext *rm = s->priv_data;
     AVIOContext *pb = s->pb;
     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), pkt);
         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), pkt);
+        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), pkt);
         avio_write(pb, buf, size);
     }
     stream->nb_frames++;
     return 0;
 }
 
-static int rm_write_video(AVFormatContext *s, const uint8_t *buf, int size, int flags)
+static int rm_write_video(AVFormatContext *s, const uint8_t *buf,
+                          int size, int flags, AVPacket *pkt)
 {
     RMMuxContext *rm = s->priv_data;
     AVIOContext *pb = s->pb;
@@ -395,32 +547,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, pkt);
+            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, pkt);
+        // 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;
@@ -430,23 +628,72 @@ static int rm_write_packet(AVFormatContext *s, AVPacket *pkt)
 {
     if (s->streams[pkt->stream_index]->codecpar->codec_type ==
         AVMEDIA_TYPE_AUDIO)
-        return rm_write_audio(s, pkt->data, pkt->size, pkt->flags);
+        return rm_write_audio(s, pkt->data, pkt->size, pkt->flags, pkt);
     else
-        return rm_write_video(s, pkt->data, pkt->size, pkt->flags);
+        return rm_write_video(s, pkt->data, pkt->size, pkt->flags, 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 +726,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 f2f2cc4..e5f1620 100644
--- a/libavformat/utils.c
+++ b/libavformat/utils.c
@@ -2981,9 +2981,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