On 15/10/14 12:06, Martin Storsjö wrote:
> On Tue, 14 Oct 2014, Uwe L. Korn wrote:
> 
>> In (non-live) streams with no metadata, the duration of a stream can
> 
> I assume it doesn't hurt in case this is sent for a live stream, i.e.
> it's ignored and/or returns 0 in those cases as well?
> 

I had no live stream to test with, but with invalid playpaths, I always
got a duration of 0 replied from the server. Not sure if different
implementations will answer something else but I don't expect any server
to reply with a positive number where the stream has no duration.

But sending it for a live stream just sounds like unnecessary overhead
as we already know that we'll get no valid duration and thus just spent
a bit more time for initiating the stream which we do not need.

>> be retrieved by calling the RTMP function getStreamLength with the
>> playpath. The server will return a positive duration upon the request if
>> the duration is known, otherwise either no response or a duration of 0
>> will be returned.
>> ---
>> libavformat/rtmpproto.c | 106
>> ++++++++++++++++++++++++++++++++++++++++++++++++
>> 1 file changed, 106 insertions(+)
>>
>> diff --git a/libavformat/rtmpproto.c b/libavformat/rtmpproto.c
>> index 2b4c173..6e90a9f 100644
>> --- a/libavformat/rtmpproto.c
>> +++ b/libavformat/rtmpproto.c
>> @@ -123,6 +123,7 @@ typedef struct RTMPContext {
>>     int           listen;                     ///< listen mode flag
>>     int           listen_timeout;             ///< listen timeout to
>> wait for new connections
>>     int           nb_streamid;                ///< The next stream id
>> to return on createStream calls
>> +    double        duration;                   ///< Duration of the
>> stream in seconds as returned by the server (only valid if non-zero)
>>     char          username[50];
>>     char          password[50];
>>     char          auth_params[500];
>> @@ -679,6 +680,30 @@ static int gen_delete_stream(URLContext *s,
>> RTMPContext *rt)
>> }
>>
>> /**
>> + * Generate 'getStreamLength' call and send it to the server. If the
>> server
>> + * knows the duration of the selected stream, it will reply with the
>> duration
>> + * in seconds.
>> + */
>> +static int gen_get_stream_length(URLContext *s, RTMPContext *rt)
>> +{
>> +    RTMPPacket pkt;
>> +    uint8_t *p;
>> +    int ret;
>> +
>> +    if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SOURCE_CHANNEL,
>> RTMP_PT_INVOKE,
>> +                                     0, 31 + strlen(rt->playpath))) < 0)
>> +        return ret;
>> +
>> +    p = pkt.data;
>> +    ff_amf_write_string(&p, "getStreamLength");
>> +    ff_amf_write_number(&p, ++rt->nb_invokes);
>> +    ff_amf_write_null(&p);
>> +    ff_amf_write_string(&p, rt->playpath);
>> +
>> +    return rtmp_send_packet(rt, &pkt, 1);
>> +}
>> +
>> +/**
>>  * Generate client buffer time and send it to the server.
>>  */
>> static int gen_buffer_time(URLContext *s, RTMPContext *rt)
>> @@ -2044,11 +2069,19 @@ static int handle_invoke_result(URLContext *s,
>> RTMPPacket *pkt)
>>             if ((ret = gen_publish(s, rt)) < 0)
>>                 goto fail;
>>         } else {
>> +            if (rt->live != -1) {
>> +                if ((ret = gen_get_stream_length(s, rt)) < 0)
>> +                    goto fail;
>> +            }
>>             if ((ret = gen_play(s, rt)) < 0)
>>                 goto fail;
>>             if ((ret = gen_buffer_time(s, rt)) < 0)
>>                 goto fail;
>>         }
>> +    } else if (!strcmp(tracked_method, "getStreamLength")) {
>> +        if (read_number_result(pkt, &rt->duration)) {
>> +            av_log(s, AV_LOG_WARNING, "Unexpected reply on
>> getStreamLength()\n");
>> +        }
>>     }
>>
>> fail:
>> @@ -2450,6 +2483,70 @@ static int rtmp_close(URLContext *h)
>> }
>>
>> /**
>> + * Insert a fake onMetadata packet into the FLV stream to notify the FLV
>> + * demuxer about the duration of the stream.
>> + *
>> + * This should only be done if there was no real onMetadata packet
>> sent by the
>> + * server at the start of the stream and if we were able to rezrieve
>> a valid
> 
> Typo, but can be fixed before pushing
> 
>> + * duration via a getStreamLength call.
>> + *
>> + * @return 0 for successful operation, negative value in case of error
>> + */
>> +static int inject_fake_duration_metadata(RTMPContext *rt)
>> +{
>> +    // We need to insert the metdata packet directly after the FLV
>> +    // header, i.e. we need to move all other already read data by the
>> +    // size of our fake metadata packet.
>> +
>> +    uint8_t* p;
>> +    // Keep old flv_data pointer
>> +    uint8_t* old_flv_data = rt->flv_data;
>> +    // Allocate a new flv_data pointer with enough space for the
>> additional package
>> +    if (!(rt->flv_data = av_malloc(rt->flv_size + 55))) {
>> +        rt->flv_data = old_flv_data;
>> +        return AVERROR(ENOMEM);
>> +    }
>> +
>> +    // Copy FLV header
>> +    memcpy(rt->flv_data, old_flv_data, 13);
>> +    // Copy remaining packets
>> +    memcpy(rt->flv_data + 13 + 55, old_flv_data + 13, rt->flv_size -
>> 13);
>> +    // Increase the size by the injected packet
>> +    rt->flv_size += 55;
>> +    // Delete the old FLV data
>> +    av_free(old_flv_data);
>> +
>> +    p = rt->flv_data + 13;
>> +    bytestream_put_byte(&p, FLV_TAG_TYPE_META);
>> +    bytestream_put_be24(&p, 40); // size of data part (sum of all
>> parts below)
>> +    bytestream_put_be24(&p, 0);  // timestamp
>> +    bytestream_put_be32(&p, 0);  // reserved
>> +
>> +    // first event name as a string
>> +    bytestream_put_byte(&p, AMF_DATA_TYPE_STRING);
>> +    // "onMetaData" as AMF string
>> +    bytestream_put_be16(&p, 10);
>> +    bytestream_put_buffer(&p, "onMetaData", 10);
>> +
>> +    // mixed array (hash) with size and string/type/data tuples
>> +    bytestream_put_byte(&p, AMF_DATA_TYPE_MIXEDARRAY);
>> +    bytestream_put_be32(&p, 1); // metadata_count
>> +
>> +    // "duration" as AMF string
>> +    bytestream_put_be16(&p, 8);
>> +    bytestream_put_buffer(&p, "duration", 8);
>> +    bytestream_put_byte(&p, AMF_DATA_TYPE_NUMBER);
>> +    bytestream_put_be64(&p, av_double2int(rt->duration));
>> +
>> +    // Finalise object
>> +    bytestream_put_be16(&p, 0); // Empty string
>> +    bytestream_put_byte(&p, AMF_END_OF_OBJECT);
>> +    bytestream_put_be32(&p, 40); // size of data part (sum of all
>> parts below)
>> +
>> +    return 0;
>> +}
> 
> The size calculations here feel quite brittle, but OTOH that's pretty
> much the norm for this file. First I was tempted to add a check at the
> end to make sure we actually wrote exactly 55 bytes, but if something
> changes, the "40" size all over would also need to be fixed.
> 
> Karma++ for fixing all of that separately though :-)
> 
>> +
>> +/**
>>  * Open RTMP connection and verify that the stream can be played.
>>  *
>>  * URL syntax: rtmp://server[:port][/app][/playpath]
>> @@ -2656,6 +2753,7 @@ reconnect:
>>     rt->received_metadata = 0;
>>     rt->last_bytes_read = 0;
>>     rt->server_bw = 2500000;
>> +    rt->duration = 0;
>>
>>     av_log(s, AV_LOG_DEBUG, "Proto = %s, path = %s, app = %s, fname =
>> %s\n",
>>            proto, path, rt->app, rt->playpath);
>> @@ -2714,6 +2812,14 @@ reconnect:
>>         if (rt->has_video) {
>>             rt->flv_data[4] |= FLV_HEADER_FLAG_HASVIDEO;
>>         }
>> +
>> +        // If we received the first packet of an A/V stream and no
>> metadata but
>> +        // the server returned a valid duration, create a fake
>> metadata packet
>> +        // to inform the FLV decoder about the duration.
>> +        if (!rt->received_metadata && rt->duration > 0) {
>> +            if ((ret = inject_fake_duration_metadata(rt)) < 0)
>> +                goto fail;
>> +        }
>>     } else {
>>         rt->flv_size = 0;
>>         rt->flv_data = NULL;
>> -- 
>> 2.1.2
> 
> Looks mostly good I think. Do you have a sample url that you can share?
> (Either publicly or privately.)

Contact me on IRC for that ;)

Uwe

> 
> // Martin
> _______________________________________________
> libav-devel mailing list
> [email protected]
> https://lists.libav.org/mailman/listinfo/libav-devel


Attachment: signature.asc
Description: OpenPGP digital signature

_______________________________________________
libav-devel mailing list
[email protected]
https://lists.libav.org/mailman/listinfo/libav-devel

Reply via email to