On Sat, 31 May 2014, Uwe L. Korn wrote:
In the presence of no metadata, do not set any stream flag in the FLV
header but let the decoder handle the detection and creation of streams
as data arrives.
---
libavformat/rtmpproto.c | 73 ++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 72 insertions(+), 1 deletion(-)
diff --git a/libavformat/rtmpproto.c b/libavformat/rtmpproto.c
index 0cc702a..c69ff84 100644
--- a/libavformat/rtmpproto.c
+++ b/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 @@ reconnect:
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,7 @@ reconnect:
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);
} else {
rt->flv_size = 0;
rt->flv_data = NULL;
The parts above seem ok to me
@@ -2634,6 +2678,33 @@ static int rtmp_read(URLContext *s, uint8_t *buf, int
size)
int orig_size = size;
int ret;
+ // We have not yet read any data to the buffer and we did not receive an
+ // onMetadata packet yet.
The "and we did not receive an onMetadata packet yet" part feels out of
place in this comment here - we haven't checked this fact until a few
if statements further below.
Also, would it make sense to have this at the end of rtmp_open instead of
here in rtmp_read? That would make it even clearer that it's only done
once, at startup.
+ // Because of this lack of information we are not able
+ // to built the correct FLV header. Read packets until we have received the
s/built/build/
+ // first audio or video packet and then set a dummy FLV header. If we
+ // recevive metadata before that, we built the FLV header from the
s/recevive/receive/
And also maybe s/built/build/?
+ // information in the metadata.
+ if (rt->flv_off == 0) {
+ // 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 FLV header from this.
+ while (rt->has_audio == 0 && rt->has_video == 0 &&
+ rt->received_metadata == 0) {
The common style for these conditions would be (!rt->has_audio &&
!rt->has_video ...)
+ if ((ret = get_packet(s, 0)) < 0)
+ return ret;
+ }
+
+ // After we have read the first packet with non-zero timestamp, we know
+ // if the stream contains audio and/or video data. Write this
information
+ // into the flv header before we return it.
+ if (rt->has_audio) {
+ rt->flv_data[4] |= FLV_HEADER_FLAG_HASAUDIO;
+ }
+ if (rt->has_video) {
+ rt->flv_data[4] |= FLV_HEADER_FLAG_HASVIDEO;
+ }
+ }
+
Other than these minor details I think this looks good. It won't be
foolproof for metadata-less streams, but it won't wait for too long
either, so I think this is better than what we have now.
// Martin
_______________________________________________
libav-devel mailing list
[email protected]
https://lists.libav.org/mailman/listinfo/libav-devel