Merge commit '8583b14252deac71136f1dec231910abab0ba503'
authorMichael Niedermayer <michaelni@gmx.at>
Wed, 18 Sep 2013 10:08:37 +0000 (12:08 +0200)
committerMichael Niedermayer <michaelni@gmx.at>
Wed, 18 Sep 2013 10:09:06 +0000 (12:09 +0200)
* commit '8583b14252deac71136f1dec231910abab0ba503':
  rtmp: Support reading interleaved chunks.

Merged-by: Michael Niedermayer <michaelni@gmx.at>
1  2 
libavformat/rtmppkt.c
libavformat/rtmppkt.h
libavformat/rtmpproto.c

diff --combined libavformat/rtmppkt.c
@@@ -2,20 -2,20 +2,20 @@@
   * RTMP input format
   * Copyright (c) 2009 Konstantin Shishkov
   *
 - * This file is part of Libav.
 + * This file is part of FFmpeg.
   *
 - * Libav is free software; you can redistribute it and/or
 + * FFmpeg is free software; you can redistribute it and/or
   * modify it under the terms of the GNU Lesser General Public
   * License as published by the Free Software Foundation; either
   * version 2.1 of the License, or (at your option) any later version.
   *
 - * Libav is distributed in the hope that it will be useful,
 + * FFmpeg is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   * Lesser General Public License for more details.
   *
   * You should have received a copy of the GNU Lesser General Public
 - * License along with Libav; if not, write to the Free Software
 + * License along with FFmpeg; if not, write to the Free Software
   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
   */
  
@@@ -140,16 -140,17 +140,17 @@@ int ff_rtmp_packet_read(URLContext *h, 
      return ff_rtmp_packet_read_internal(h, p, chunk_size, prev_pkt, hdr);
  }
  
- int ff_rtmp_packet_read_internal(URLContext *h, RTMPPacket *p, int chunk_size,
-                                  RTMPPacket *prev_pkt, uint8_t hdr)
+ static int rtmp_packet_read_one_chunk(URLContext *h, RTMPPacket *p,
+                                       int chunk_size, RTMPPacket *prev_pkt,
+                                       uint8_t hdr)
  {
  
-     uint8_t t, buf[16];
-     int channel_id, timestamp, size, offset = 0;
+     uint8_t buf[16];
+     int channel_id, timestamp, size;
      uint32_t extra = 0;
      enum RTMPPacketType type;
      int written = 0;
-     int ret;
+     int ret, toread;
  
      written++;
      channel_id = hdr & 0x3F;
      if (hdr != RTMP_PS_TWELVEBYTES)
          timestamp += prev_pkt[channel_id].timestamp;
  
-     if ((ret = ff_rtmp_packet_create(p, channel_id, type, timestamp,
-                                      size)) < 0)
-         return ret;
+     if (!prev_pkt[channel_id].read) {
+         if ((ret = ff_rtmp_packet_create(p, channel_id, type, timestamp,
+                                          size)) < 0)
+             return ret;
+         p->read = written;
+         p->offset = 0;
+         prev_pkt[channel_id].ts_delta   = timestamp -
+                                           prev_pkt[channel_id].timestamp;
+         prev_pkt[channel_id].timestamp  = timestamp;
+     } else {
+         // previous packet in this channel hasn't completed reading
+         RTMPPacket *prev = &prev_pkt[channel_id];
+         p->data          = prev->data;
+         p->size          = prev->size;
+         p->channel_id    = prev->channel_id;
+         p->type          = prev->type;
+         p->ts_delta      = prev->ts_delta;
+         p->extra         = prev->extra;
+         p->offset        = prev->offset;
+         p->read          = prev->read + written;
+         p->timestamp     = prev->timestamp;
+         prev->data       = NULL;
+     }
      p->extra = extra;
      // save history
      prev_pkt[channel_id].channel_id = channel_id;
      prev_pkt[channel_id].type       = type;
      prev_pkt[channel_id].size       = size;
-     prev_pkt[channel_id].ts_delta   = timestamp - prev_pkt[channel_id].timestamp;
-     prev_pkt[channel_id].timestamp  = timestamp;
      prev_pkt[channel_id].extra      = extra;
-     while (size > 0) {
-         int toread = FFMIN(size, chunk_size);
-         if (ffurl_read_complete(h, p->data + offset, toread) != toread) {
-             ff_rtmp_packet_destroy(p);
+     size = size - p->offset;
+     toread = FFMIN(size, chunk_size);
+     if (ffurl_read_complete(h, p->data + p->offset, toread) != toread) {
+         ff_rtmp_packet_destroy(p);
+         return AVERROR(EIO);
+     }
+     size      -= toread;
+     p->read   += toread;
+     p->offset += toread;
+     if (size > 0) {
+        RTMPPacket *prev = &prev_pkt[channel_id];
+        prev->data = p->data;
+        prev->read = p->read;
+        prev->offset = p->offset;
+        return AVERROR(EAGAIN);
+     }
+     prev_pkt[channel_id].read = 0; // read complete; reset if needed
+     return p->read;
+ }
+ int ff_rtmp_packet_read_internal(URLContext *h, RTMPPacket *p, int chunk_size,
+                                  RTMPPacket *prev_pkt, uint8_t hdr)
+ {
+     while (1) {
+         int ret = rtmp_packet_read_one_chunk(h, p, chunk_size, prev_pkt, hdr);
+         if (ret > 0 || ret != AVERROR(EAGAIN))
+             return ret;
+         if (ffurl_read(h, &hdr, 1) != 1)
              return AVERROR(EIO);
-         }
-         size    -= chunk_size;
-         offset  += chunk_size;
-         written += chunk_size;
-         if (size > 0) {
-             if ((ret = ffurl_read_complete(h, &t, 1)) < 0) { // marker
-                 ff_rtmp_packet_destroy(p);
-                 return ret;
-             }
-             written++;
-             if (t != (0xC0 + channel_id))
-                 return -1;
-         }
      }
-     return written;
  }
  
  int ff_rtmp_packet_write(URLContext *h, RTMPPacket *pkt,
diff --combined libavformat/rtmppkt.h
@@@ -2,20 -2,20 +2,20 @@@
   * RTMP packet utilities
   * Copyright (c) 2009 Konstantin Shishkov
   *
 - * This file is part of Libav.
 + * This file is part of FFmpeg.
   *
 - * Libav is free software; you can redistribute it and/or
 + * FFmpeg is free software; you can redistribute it and/or
   * modify it under the terms of the GNU Lesser General Public
   * License as published by the Free Software Foundation; either
   * version 2.1 of the License, or (at your option) any later version.
   *
 - * Libav is distributed in the hope that it will be useful,
 + * FFmpeg is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   * Lesser General Public License for more details.
   *
   * You should have received a copy of the GNU Lesser General Public
 - * License along with Libav; if not, write to the Free Software
 + * License along with FFmpeg; if not, write to the Free Software
   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
   */
  
@@@ -82,6 -82,8 +82,8 @@@ typedef struct RTMPPacket 
      uint32_t       extra;      ///< probably an additional channel ID used during streaming data
      uint8_t        *data;      ///< packet payload
      int            size;       ///< packet payload size
+     int            offset;     ///< amount of data read so far
+     int            read;       ///< amount read, including headers
  } RTMPPacket;
  
  /**
diff --combined libavformat/rtmpproto.c
@@@ -2,20 -2,20 +2,20 @@@
   * RTMP network protocol
   * Copyright (c) 2009 Konstantin Shishkov
   *
 - * This file is part of Libav.
 + * This file is part of FFmpeg.
   *
 - * Libav is free software; you can redistribute it and/or
 + * FFmpeg is free software; you can redistribute it and/or
   * modify it under the terms of the GNU Lesser General Public
   * License as published by the Free Software Foundation; either
   * version 2.1 of the License, or (at your option) any later version.
   *
 - * Libav is distributed in the hope that it will be useful,
 + * FFmpeg is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   * Lesser General Public License for more details.
   *
   * You should have received a copy of the GNU Lesser General Public
 - * License along with Libav; if not, write to the Free Software
 + * License along with FFmpeg; if not, write to the Free Software
   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
   */
  
@@@ -48,7 -48,7 +48,7 @@@
  #include <zlib.h>
  #endif
  
 -#define APP_MAX_LENGTH 128
 +#define APP_MAX_LENGTH 1024
  #define PLAYPATH_MAX_LENGTH 256
  #define TCURL_MAX_LENGTH 512
  #define FLASHVER_MAX_LENGTH 64
@@@ -266,6 -266,9 +266,6 @@@ static int rtmp_write_amf_data(URLConte
          *value = '\0';
          value++;
  
 -        if (!field || !value)
 -            goto fail;
 -
          ff_amf_write_field_name(p, field);
      } else {
          goto fail;
@@@ -312,7 -315,7 +312,7 @@@ static int gen_connect(URLContext *s, R
      int ret;
  
      if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE,
 -                                     0, 4096)) < 0)
 +                                     0, 4096 + APP_MAX_LENGTH)) < 0)
          return ret;
  
      p = pkt.data;
@@@ -1165,7 -1168,7 +1165,7 @@@ static int rtmp_handshake(URLContext *s
      for (i = 9; i <= RTMP_HANDSHAKE_PACKET_SIZE; i++)
          tosend[i] = av_lfg_get(&rnd) >> 24;
  
 -    if (rt->encrypted && CONFIG_FFRTMPCRYPT_PROTOCOL) {
 +    if (CONFIG_FFRTMPCRYPT_PROTOCOL && rt->encrypted) {
          /* When the client wants to use RTMPE, we have to change the command
           * byte to 0x06 which means to use encrypted data and we have to set
           * the flash version to at least 9.0.115.0. */
          if (ret < 0)
              return ret;
  
 -        if (rt->encrypted && CONFIG_FFRTMPCRYPT_PROTOCOL) {
 +        if (CONFIG_FFRTMPCRYPT_PROTOCOL && rt->encrypted) {
              /* Compute the shared secret key sent by the server and initialize
               * the RC4 encryption. */
              if ((ret = ff_rtmpe_compute_secret_key(rt->stream, serverdata + 1,
          if (ret < 0)
              return ret;
  
 -        if (rt->encrypted && CONFIG_FFRTMPCRYPT_PROTOCOL) {
 +        if (CONFIG_FFRTMPCRYPT_PROTOCOL && rt->encrypted) {
              /* Encrypt the signature to be send to the server. */
              ff_rtmpe_encrypt_sig(rt->stream, tosend +
                                   RTMP_HANDSHAKE_PACKET_SIZE - 32, digest,
                                 RTMP_HANDSHAKE_PACKET_SIZE)) < 0)
              return ret;
  
 -        if (rt->encrypted && CONFIG_FFRTMPCRYPT_PROTOCOL) {
 +        if (CONFIG_FFRTMPCRYPT_PROTOCOL && rt->encrypted) {
              /* Set RC4 keys for encryption and update the keystreams. */
              if ((ret = ff_rtmpe_update_keystream(rt->stream)) < 0)
                  return ret;
          }
      } else {
 -        if (rt->encrypted && CONFIG_FFRTMPCRYPT_PROTOCOL) {
 +        if (CONFIG_FFRTMPCRYPT_PROTOCOL && rt->encrypted) {
              /* Compute the shared secret key sent by the server and initialize
               * the RC4 encryption. */
              if ((ret = ff_rtmpe_compute_secret_key(rt->stream, serverdata + 1,
                                 RTMP_HANDSHAKE_PACKET_SIZE)) < 0)
              return ret;
  
 -        if (rt->encrypted && CONFIG_FFRTMPCRYPT_PROTOCOL) {
 +        if (CONFIG_FFRTMPCRYPT_PROTOCOL && rt->encrypted) {
              /* Set RC4 keys for encryption and update the keystreams. */
              if ((ret = ff_rtmpe_update_keystream(rt->stream)) < 0)
                  return ret;
@@@ -2205,7 -2208,7 +2205,7 @@@ static int get_packet(URLContext *s, in
              }
          }
          rt->bytes_read += ret;
 -        if (rt->bytes_read > rt->last_bytes_read + rt->client_report_size) {
 +        if (rt->bytes_read - rt->last_bytes_read > rt->client_report_size) {
              av_log(s, AV_LOG_DEBUG, "Sending bytes read report\n");
              if ((ret = gen_bytes_read(s, rt, rpkt.timestamp + 1)) < 0)
                  return ret;
  static int rtmp_close(URLContext *h)
  {
      RTMPContext *rt = h->priv_data;
-     int ret = 0;
+     int ret = 0, i, j;
  
      if (!rt->is_input) {
          rt->flv_data = NULL;
      }
      if (rt->state > STATE_HANDSHAKED)
          ret = gen_delete_stream(h, rt);
+     for (i = 0; i < 2; i++)
+         for (j = 0; j < RTMP_CHANNELS; j++)
+             ff_rtmp_packet_destroy(&rt->prev_pkt[i][j]);
  
      free_tracked_methods(rt);
      av_freep(&rt->flv_data);
@@@ -2450,20 -2456,16 +2453,20 @@@ reconnect
              fname = strchr(p + 1, '/');
              if (!fname || (c && c < fname)) {
                  fname = p + 1;
 -                av_strlcpy(rt->app, path + 1, p - path);
 +                av_strlcpy(rt->app, path + 1, FFMIN(p - path, APP_MAX_LENGTH));
              } else {
                  fname++;
 -                av_strlcpy(rt->app, path + 1, fname - path - 1);
 +                av_strlcpy(rt->app, path + 1, FFMIN(fname - path - 1, APP_MAX_LENGTH));
              }
          }
      }
  
      if (old_app) {
          // The name of application has been defined by the user, override it.
 +        if (strlen(old_app) >= APP_MAX_LENGTH) {
 +            ret = AVERROR(EINVAL);
 +            goto fail;
 +        }
          av_free(rt->app);
          rt->app = old_app;
      }