[FFmpeg-devel] [PATCH] avformat/rtmppkt: handle extended timestamp field even for one-byte header

Martin Panter vadmium+rtmp at gmail.com
Wed Mar 5 06:33:28 CET 2014


>From 0a1e2beac5b276f74622ab33fe5245e831de516d Mon Sep 17 00:00:00 2001
From: Martin Panter <vadmium à gmail·com>
Date: Wed, 5 Mar 2014 04:04:39 +0000
Subject: [PATCH] avformat/rtmppkt: handle extended timestamp field even for
 one-byte header

Related fix in "rtmpdump":
https://repo.or.cz/w/rtmpdump.git/commitdiff/79459a2

Adobe's RTMP specification (21 Dec 2012), section 5.3.1.3 ("Extended
Timestamp"), says "this field is present in Type 3 chunks". Type 3 chunks are
those with the one-byte header size.

This resolves intermittent hangs and segfaults caused by the read function,
and also includes an untested fix for the write function.

The read function was tested with ABC (Australia) News 24 streams, however
they are probably restricted to only Australian internet addresses. Some of
the packets at the start of these streams seem to contain junk timestamp
fields, often requiring the extended field. Test command:

ffplay rtmp://cp81899.live.edgefcs.net/live/news24-med@28772
---
Original patch: https://github.com/vadmium/FFmpeg/commit/0a1e2be.patch

I fixed the equivalent issue in “rtmpdump” and Martin Storsjö
suggested fixing the internal “libavformat” implementation as well, so
here you are :). I also tried fixing the packet write function,
because it shares the affected “ts_delta” field, but I have not tested
it. (Testing would probably involve generating packets with a
timestamp delta ≥ 0xFFFFFF, or arbitrary junk like I was seeing from
the ABC.)

Also, I would suggest renaming “ts_delta” to “ts_field” or something
in the RTMPPacket structure, because it is not always interpreted as a
delta. But I’m not sure if that is practical; maybe the structure has
a public API scope or is used elsewhere.

 libavformat/rtmppkt.c | 59 ++++++++++++++++++++++++++++-----------------------
 libavformat/rtmppkt.h |  2 +-
 2 files changed, 34 insertions(+), 27 deletions(-)

diff --git a/libavformat/rtmppkt.c b/libavformat/rtmppkt.c
index f99540c..b3a8294 100644
--- a/libavformat/rtmppkt.c
+++ b/libavformat/rtmppkt.c
@@ -169,6 +169,7 @@ static int rtmp_packet_read_one_chunk(URLContext
*h, RTMPPacket *p,

     uint8_t buf[16];
     int channel_id, timestamp, size;
+    uint32_t ts_field; // non-extended timestamp or delta field
     uint32_t extra = 0;
     enum RTMPPacketType type;
     int written = 0;
@@ -193,14 +194,14 @@ static int rtmp_packet_read_one_chunk(URLContext
*h, RTMPPacket *p,
     type  = prev_pkt[channel_id].type;
     extra = prev_pkt[channel_id].extra;

-    hdr >>= 6;
+    hdr >>= 6; // header size indicator
     if (hdr == RTMP_PS_ONEBYTE) {
-        timestamp = prev_pkt[channel_id].ts_delta;
+        ts_field = prev_pkt[channel_id].ts_delta;
     } else {
         if (ffurl_read_complete(h, buf, 3) != 3)
             return AVERROR(EIO);
         written += 3;
-        timestamp = AV_RB24(buf);
+        ts_field = AV_RB24(buf);
         if (hdr != RTMP_PS_FOURBYTES) {
             if (ffurl_read_complete(h, buf, 3) != 3)
                 return AVERROR(EIO);
@@ -217,11 +218,13 @@ static int rtmp_packet_read_one_chunk(URLContext
*h, RTMPPacket *p,
                 extra = AV_RL32(buf);
             }
         }
-        if (timestamp == 0xFFFFFF) {
-            if (ffurl_read_complete(h, buf, 4) != 4)
-                return AVERROR(EIO);
-            timestamp = AV_RB32(buf);
-        }
+    }
+    if (ts_field == 0xFFFFFF) {
+        if (ffurl_read_complete(h, buf, 4) != 4)
+            return AVERROR(EIO);
+        timestamp = AV_RB32(buf);
+    } else {
+        timestamp = ts_field;
     }
     if (hdr != RTMP_PS_TWELVEBYTES)
         timestamp += prev_pkt[channel_id].timestamp;
@@ -232,8 +235,7 @@ static int rtmp_packet_read_one_chunk(URLContext
*h, RTMPPacket *p,
             return ret;
         p->read = written;
         p->offset = 0;
-        prev_pkt[channel_id].ts_delta   = timestamp -
-                                          prev_pkt[channel_id].timestamp;
+        prev_pkt[channel_id].ts_delta   = ts_field;
         prev_pkt[channel_id].timestamp  = timestamp;
     } else {
         // previous packet in this channel hasn't completed reading
@@ -303,18 +305,30 @@ int ff_rtmp_packet_write(URLContext *h, RTMPPacket *pkt,
     int written = 0;
     int ret;
     RTMPPacket *prev_pkt;
+    int use_delta; // flag if using timestamp delta, not RTMP_PS_TWELVEBYTES
+    uint32_t timestamp; // full 32-bit timestamp or delta value

     if ((ret = ff_rtmp_check_alloc_array(prev_pkt_ptr, nb_prev_pkt,
                                          pkt->channel_id)) < 0)
         return ret;
     prev_pkt = *prev_pkt_ptr;
-
-    pkt->ts_delta = pkt->timestamp - prev_pkt[pkt->channel_id].timestamp;
-
+
     //if channel_id = 0, this is first presentation of prev_pkt, send full hdr.
-    if (prev_pkt[pkt->channel_id].channel_id &&
+    use_delta = prev_pkt[pkt->channel_id].channel_id &&
         pkt->extra == prev_pkt[pkt->channel_id].extra &&
-        pkt->timestamp >= prev_pkt[pkt->channel_id].timestamp) {
+        pkt->timestamp >= prev_pkt[pkt->channel_id].timestamp;
+
+    timestamp = pkt->timestamp;
+    if (use_delta) {
+        timestamp -= prev_pkt[pkt->channel_id].timestamp;
+    }
+    if (timestamp >= 0xFFFFFF) {
+        pkt->ts_delta = 0xFFFFFF;
+    } else {
+        pkt->ts_delta = timestamp;
+    }
+
+    if (use_delta) {
         if (pkt->type == prev_pkt[pkt->channel_id].type &&
             pkt->size == prev_pkt[pkt->channel_id].size) {
             mode = RTMP_PS_FOURBYTES;
@@ -334,30 +348,23 @@ int ff_rtmp_packet_write(URLContext *h, RTMPPacket *pkt,
         bytestream_put_byte(&p, 1               | (mode << 6));
         bytestream_put_le16(&p, pkt->channel_id - 64);
     }
+    bytestream_put_be24(&p, pkt->ts_delta);
     if (mode != RTMP_PS_ONEBYTE) {
-        uint32_t timestamp = pkt->timestamp;
-        if (mode != RTMP_PS_TWELVEBYTES)
-            timestamp = pkt->ts_delta;
-        bytestream_put_be24(&p, timestamp >= 0xFFFFFF ? 0xFFFFFF : timestamp);
         if (mode != RTMP_PS_FOURBYTES) {
             bytestream_put_be24(&p, pkt->size);
             bytestream_put_byte(&p, pkt->type);
             if (mode == RTMP_PS_TWELVEBYTES)
                 bytestream_put_le32(&p, pkt->extra);
         }
-        if (timestamp >= 0xFFFFFF)
-            bytestream_put_be32(&p, timestamp);
     }
+    if (pkt->ts_delta == 0xFFFFFF)
+        bytestream_put_be32(&p, timestamp);
     // save history
     prev_pkt[pkt->channel_id].channel_id = pkt->channel_id;
     prev_pkt[pkt->channel_id].type       = pkt->type;
     prev_pkt[pkt->channel_id].size       = pkt->size;
     prev_pkt[pkt->channel_id].timestamp  = pkt->timestamp;
-    if (mode != RTMP_PS_TWELVEBYTES) {
-        prev_pkt[pkt->channel_id].ts_delta   = pkt->ts_delta;
-    } else {
-        prev_pkt[pkt->channel_id].ts_delta   = pkt->timestamp;
-    }
+    prev_pkt[pkt->channel_id].ts_delta   = pkt->ts_delta;
     prev_pkt[pkt->channel_id].extra      = pkt->extra;

     if ((ret = ffurl_write(h, pkt_hdr, p - pkt_hdr)) < 0)
diff --git a/libavformat/rtmppkt.h b/libavformat/rtmppkt.h
index fb79fed..741d893 100644
--- a/libavformat/rtmppkt.h
+++ b/libavformat/rtmppkt.h
@@ -78,7 +78,7 @@ typedef struct RTMPPacket {
     int            channel_id; ///< RTMP channel ID (nothing to do
with audio/video channels though)
     RTMPPacketType type;       ///< packet payload type
     uint32_t       timestamp;  ///< packet full timestamp
-    uint32_t       ts_delta;   ///< timestamp increment to the
previous one in milliseconds (latter only for media packets)
+    uint32_t       ts_delta;   ///< 24-bit timestamp or increment to
the previous one, in milliseconds (latter only for media packets).
Clipped to a maximum of 0xFFFFFF, indicating an extended timestamp
field.
     uint32_t       extra;      ///< probably an additional channel ID
used during streaming data
     uint8_t        *data;      ///< packet payload
     int            size;       ///< packet payload size
-- 
1.8.5.1


More information about the ffmpeg-devel mailing list