Permalink
Browse files

rtmpproto: read metadata to set correct FLV header

In the presence of no metadata, do not set any stream flag in the FLV
header but let the demuxer handle the detection and creation of streams
as data arrives.

Signed-off-by: Martin Storsjö <martin@martin.st>
  • Loading branch information...
1 parent 3b18857 commit 59cb5747ec3c5cd842b94e574c37889521c97cc4 @xhochy xhochy committed with mstorsjo May 31, 2014
Showing with 65 additions and 1 deletion.
  1. +65 −1 libavformat/rtmpproto.c
@@ -97,6 +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
@@ -2109,6 +2112,12 @@ static int append_flv_data(RTMPContext *rt, RTMPPacket *pkt, int skip)
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) {
@@ -2141,6 +2150,38 @@ static int handle_notify(URLContext *s, RTMPPacket *pkt)
&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;
@@ -2571,6 +2612,9 @@ static int rtmp_open(URLContext *s, const char *uri, int flags)
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;
@@ -2610,7 +2654,27 @@ static int rtmp_open(URLContext *s, const char *uri, int flags)
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;

0 comments on commit 59cb574

Please sign in to comment.