Merge commit '59cb5747ec3c5cd842b94e574c37889521c97cc4'
authorMichael Niedermayer <michaelni@gmx.at>
Sun, 1 Jun 2014 22:32:06 +0000 (00:32 +0200)
committerMichael Niedermayer <michaelni@gmx.at>
Sun, 1 Jun 2014 22:32:30 +0000 (00:32 +0200)
* commit '59cb5747ec3c5cd842b94e574c37889521c97cc4':
  rtmpproto: read metadata to set correct FLV header

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

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
@@@ -97,6 -97,9 +97,9 @@@ typedef struct RTMPContext 
      uint32_t      bytes_read;                 ///< number of bytes read from server
      uint32_t      last_bytes_read;            ///< number of bytes read last reported to server
      int           skip_bytes;                 ///< number of bytes to skip from the input FLV stream in the next write call
+     int           has_audio;                  ///< presence of audio data
+     int           has_video;                  ///< presence of video data
+     int           received_metadata;          ///< Indicates if we have received metadata about the streams
      uint8_t       flv_header[RTMP_HEADER];    ///< partial incoming flv packet header
      int           flv_header_bytes;           ///< number of initialized bytes in flv_header
      int           nb_invokes;                 ///< keeps track of invoke messages
@@@ -271,6 -274,9 +274,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;
@@@ -317,7 -323,7 +320,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;
@@@ -414,7 -420,6 +417,7 @@@ static int read_connect(URLContext *s, 
      if (pkt.type == RTMP_PT_CHUNK_SIZE) {
          if ((ret = handle_chunk_size(s, &pkt)) < 0)
              return ret;
 +
          ff_rtmp_packet_destroy(&pkt);
          if ((ret = ff_rtmp_packet_read(rt->stream, &pkt, rt->in_chunk_size,
                                         &rt->prev_pkt[0], &rt->nb_prev_pkt[0])) < 0)
@@@ -1712,23 -1717,18 +1715,23 @@@ static int handle_connect_error(URLCont
          char *value = strchr(ptr, '=');
          if (next)
              *next++ = '\0';
 -        if (value)
 +        if (value) {
              *value++ = '\0';
 -        if (!strcmp(ptr, "user")) {
 -            user = value;
 -        } else if (!strcmp(ptr, "salt")) {
 -            salt = value;
 -        } else if (!strcmp(ptr, "opaque")) {
 -            opaque = value;
 -        } else if (!strcmp(ptr, "challenge")) {
 -            challenge = value;
 -        } else if (!strcmp(ptr, "nonce")) {
 -            nonce = value;
 +            if (!strcmp(ptr, "user")) {
 +                user = value;
 +            } else if (!strcmp(ptr, "salt")) {
 +                salt = value;
 +            } else if (!strcmp(ptr, "opaque")) {
 +                opaque = value;
 +            } else if (!strcmp(ptr, "challenge")) {
 +                challenge = value;
 +            } else if (!strcmp(ptr, "nonce")) {
 +                nonce = value;
 +            } else {
 +                av_log(s, AV_LOG_INFO, "Ignoring unsupported var %s\n", ptr);
 +            }
 +        } else {
 +            av_log(s, AV_LOG_WARNING, "Variable %s has NULL value\n", ptr);
          }
          ptr = next;
      }
@@@ -2112,6 -2112,12 +2115,12 @@@ static int append_flv_data(RTMPContext 
      const int size      = pkt->size - skip;
      uint32_t ts         = pkt->timestamp;
  
+     if (pkt->type == RTMP_PT_AUDIO) {
+         rt->has_audio = 1;
+     } else if (pkt->type == RTMP_PT_VIDEO) {
+         rt->has_video = 1;
+     }
      old_flv_size = update_offset(rt, size + 15);
  
      if ((ret = av_reallocp(&rt->flv_data, rt->flv_size)) < 0) {
@@@ -2144,6 -2150,38 +2153,38 @@@ static int handle_notify(URLContext *s
                             &stringlen))
          return AVERROR_INVALIDDATA;
  
+     if (!strcmp(commandbuffer, "onMetaData")) {
+         // metadata properties should be stored in a mixed array
+         if (bytestream2_get_byte(&gbc) == AMF_DATA_TYPE_MIXEDARRAY) {
+             // We have found a metaData Array so flv can determine the streams
+             // from this.
+             rt->received_metadata = 1;
+             // skip 32-bit max array index
+             bytestream2_skip(&gbc, 4);
+             while (bytestream2_get_bytes_left(&gbc) > 3) {
+                 if (ff_amf_get_string(&gbc, statusmsg, sizeof(statusmsg),
+                                       &stringlen))
+                     return AVERROR_INVALIDDATA;
+                 // We do not care about the content of the property (yet).
+                 stringlen = ff_amf_tag_size(gbc.buffer, gbc.buffer_end);
+                 if (stringlen < 0)
+                     return AVERROR_INVALIDDATA;
+                 bytestream2_skip(&gbc, stringlen);
+                 // The presence of the following properties indicates that the
+                 // respective streams are present.
+                 if (!strcmp(statusmsg, "videocodecid")) {
+                     rt->has_video = 1;
+                 }
+                 if (!strcmp(statusmsg, "audiocodecid")) {
+                     rt->has_audio = 1;
+                 }
+             }
+             if (bytestream2_get_be24(&gbc) != AMF_END_OF_OBJECT)
+                 return AVERROR_INVALIDDATA;
+         }
+     }
      // Skip the @setDataFrame string and validate it is a notification
      if (!strcmp(commandbuffer, "@setDataFrame")) {
          skip = gbc.buffer - pkt->data;
@@@ -2287,7 -2325,7 +2328,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;
@@@ -2521,10 -2559,6 +2562,10 @@@ reconnect
  
      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;
      }
  
      rt->client_report_size = 1048576;
      rt->bytes_read = 0;
+     rt->has_audio = 0;
+     rt->has_video = 0;
+     rt->received_metadata = 0;
      rt->last_bytes_read = 0;
      rt->server_bw = 2500000;
  
          if ((err = av_reallocp(&rt->flv_data, rt->flv_size)) < 0)
              return err;
          rt->flv_off  = 0;
-         memcpy(rt->flv_data, "FLV\1\5\0\0\0\011\0\0\0\0", rt->flv_size);
+         memcpy(rt->flv_data, "FLV\1\0\0\0\0\011\0\0\0\0", rt->flv_size);
+         // Read packets until we reach the first A/V packet or read metadata.
+         // If there was a metadata package in front of the A/V packets, we can
+         // build the FLV header from this. If we do not receive any metadata,
+         // the FLV decoder will allocate the needed streams when their first
+         // audio or video packet arrives.
+         while (!rt->has_audio && !rt->has_video && !rt->received_metadata) {
+             if ((ret = get_packet(s, 0)) < 0)
+                return ret;
+         }
+         // Either after we have read the metadata or (if there is none) the
+         // first packet of an A/V stream, we have a better knowledge about the
+         // streams, so set the FLV header accordingly.
+         if (rt->has_audio) {
+             rt->flv_data[4] |= FLV_HEADER_FLAG_HASAUDIO;
+         }
+         if (rt->has_video) {
+             rt->flv_data[4] |= FLV_HEADER_FLAG_HASVIDEO;
+         }
      } else {
          rt->flv_size = 0;
          rt->flv_data = NULL;