X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavformat%2Frtmpproto.c;h=748aab33cc9c765309e732959411461416a69fa4;hb=0cd138340e5d7c668eb392721fa0015e5732cb39;hp=6787e0b48e6e7cc56f99cee8e7e4ad02a2200206;hpb=56e29bf2c9eacf077708e0304ea1592933f95b76;p=ffmpeg diff --git a/libavformat/rtmpproto.c b/libavformat/rtmpproto.c index 6787e0b48e6..748aab33cc9 100644 --- a/libavformat/rtmpproto.c +++ b/libavformat/rtmpproto.c @@ -20,7 +20,7 @@ */ /** - * @file libavformat/rtmpproto.c + * @file * RTMP protocol */ @@ -29,6 +29,7 @@ #include "libavutil/lfg.h" #include "libavutil/sha.h" #include "avformat.h" +#include "internal.h" #include "network.h" @@ -37,10 +38,10 @@ #include "rtmppkt.h" /* we can't use av_log() with URLContext yet... */ -#if LIBAVFORMAT_VERSION_MAJOR < 53 -#define LOG_CONTEXT NULL -#else +#if FF_API_URL_CLASS #define LOG_CONTEXT s +#else +#define LOG_CONTEXT NULL #endif //#define DEBUG @@ -72,6 +73,9 @@ typedef struct RTMPContext { int flv_size; ///< current buffer size int flv_off; ///< number of bytes read from current buffer RTMPPacket out_pkt; ///< rtmp packet, created from flv a/v or metadata (for output) + uint32_t client_report_size; ///< number of bytes after which client should report to server + uint32_t bytes_read; ///< number of bytes read from server + uint32_t last_bytes_read; ///< number of bytes read last reported to server } RTMPContext; #define PLAYER_KEY_OPEN_PART_LEN 30 ///< length of partial key used for first client digest signing @@ -98,7 +102,7 @@ static const uint8_t rtmp_server_key[] = { }; /** - * Generates 'connect' call and sends it to the server. + * Generate 'connect' call and send it to the server. */ static void gen_connect(URLContext *s, RTMPContext *rt, const char *proto, const char *host, int port) @@ -110,7 +114,7 @@ static void gen_connect(URLContext *s, RTMPContext *rt, const char *proto, ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE, 0, 4096); p = pkt.data; - snprintf(tcurl, sizeof(tcurl), "%s://%s:%d/%s", proto, host, port, rt->app); + ff_url_join(tcurl, sizeof(tcurl), proto, NULL, host, port, "/%s", rt->app); ff_amf_write_string(&p, "connect"); ff_amf_write_number(&p, 1.0); ff_amf_write_object_start(&p); @@ -150,7 +154,7 @@ static void gen_connect(URLContext *s, RTMPContext *rt, const char *proto, } /** - * Generates 'releaseStream' call and sends it to the server. It should make + * Generate 'releaseStream' call and send it to the server. It should make * the server release some channel for media streams. */ static void gen_release_stream(URLContext *s, RTMPContext *rt) @@ -173,7 +177,7 @@ static void gen_release_stream(URLContext *s, RTMPContext *rt) } /** - * Generates 'FCPublish' call and sends it to the server. It should make + * Generate 'FCPublish' call and send it to the server. It should make * the server preapare for receiving media streams. */ static void gen_fcpublish_stream(URLContext *s, RTMPContext *rt) @@ -196,7 +200,7 @@ static void gen_fcpublish_stream(URLContext *s, RTMPContext *rt) } /** - * Generates 'FCUnpublish' call and sends it to the server. It should make + * Generate 'FCUnpublish' call and send it to the server. It should make * the server destroy stream. */ static void gen_fcunpublish_stream(URLContext *s, RTMPContext *rt) @@ -219,7 +223,7 @@ static void gen_fcunpublish_stream(URLContext *s, RTMPContext *rt) } /** - * Generates 'createStream' call and sends it to the server. It should make + * Generate 'createStream' call and send it to the server. It should make * the server allocate some channel for media streams. */ static void gen_create_stream(URLContext *s, RTMPContext *rt) @@ -241,7 +245,7 @@ static void gen_create_stream(URLContext *s, RTMPContext *rt) /** - * Generates 'deleteStream' call and sends it to the server. It should make + * Generate 'deleteStream' call and send it to the server. It should make * the server remove some channel for media streams. */ static void gen_delete_stream(URLContext *s, RTMPContext *rt) @@ -263,7 +267,7 @@ static void gen_delete_stream(URLContext *s, RTMPContext *rt) } /** - * Generates 'play' call and sends it to the server, then pings the server + * Generate 'play' call and send it to the server, then ping the server * to start actual playing. */ static void gen_play(URLContext *s, RTMPContext *rt) @@ -298,7 +302,7 @@ static void gen_play(URLContext *s, RTMPContext *rt) } /** - * Generates 'publish' call and sends it to the server. + * Generate 'publish' call and send it to the server. */ static void gen_publish(URLContext *s, RTMPContext *rt) { @@ -322,7 +326,7 @@ static void gen_publish(URLContext *s, RTMPContext *rt) } /** - * Generates ping reply and sends it to the server. + * Generate ping reply and send it to the server. */ static void gen_pong(URLContext *s, RTMPContext *rt, RTMPPacket *ppkt) { @@ -337,12 +341,27 @@ static void gen_pong(URLContext *s, RTMPContext *rt, RTMPPacket *ppkt) ff_rtmp_packet_destroy(&pkt); } +/** + * Generate report on bytes read so far and send it to the server. + */ +static void gen_bytes_read(URLContext *s, RTMPContext *rt, uint32_t ts) +{ + RTMPPacket pkt; + uint8_t *p; + + ff_rtmp_packet_create(&pkt, RTMP_NETWORK_CHANNEL, RTMP_PT_BYTES_READ, ts, 4); + p = pkt.data; + bytestream_put_be32(&p, rt->bytes_read); + ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size, rt->prev_pkt[1]); + ff_rtmp_packet_destroy(&pkt); +} + //TODO: Move HMAC code somewhere. Eventually. #define HMAC_IPAD_VAL 0x36 #define HMAC_OPAD_VAL 0x5C /** - * Calculates HMAC-SHA2 digest for RTMP handshake packets. + * Calculate HMAC-SHA2 digest for RTMP handshake packets. * * @param src input buffer * @param len input buffer length (should be 1536) @@ -391,7 +410,7 @@ static void rtmp_calc_digest(const uint8_t *src, int len, int gap, } /** - * Puts HMAC-SHA2 digest of packet data (except for the bytes where this digest + * Put HMAC-SHA2 digest of packet data (except for the bytes where this digest * will be stored) into that packet. * * @param buf handshake data (1536 bytes) @@ -412,7 +431,7 @@ static int rtmp_handshake_imprint_with_digest(uint8_t *buf) } /** - * Verifies that the received server response has the expected digest value. + * Verify that the received server response has the expected digest value. * * @param buf handshake data received from the server (1536 bytes) * @param off position to search digest offset from @@ -436,7 +455,7 @@ static int rtmp_validate_digest(uint8_t *buf, int off) } /** - * Performs handshake with the server by means of exchanging pseudorandom data + * Perform handshake with the server by means of exchanging pseudorandom data * signed with HMAC-SHA2 digest. * * @return 0 if handshake succeeds, negative value otherwise @@ -521,7 +540,7 @@ static int rtmp_handshake(URLContext *s, RTMPContext *rt) } /** - * Parses received packet and may perform some action depending on + * Parse received packet and possibly perform some action depending on * the packet contents. * @return 0 for no errors, negative values for serious errors which prevent * further communications, positive values for uncritical errors @@ -556,6 +575,16 @@ static int rtmp_parse_result(URLContext *s, RTMPContext *rt, RTMPPacket *pkt) if (t == 6) gen_pong(s, rt, pkt); break; + case RTMP_PT_CLIENT_BW: + if (pkt->data_size < 4) { + av_log(LOG_CONTEXT, AV_LOG_ERROR, + "Client bandwidth report packet is less than 4 bytes long (%d)\n", + pkt->data_size); + return -1; + } + av_log(LOG_CONTEXT, AV_LOG_DEBUG, "Client bandwidth = %d\n", AV_RB32(pkt->data)); + rt->client_report_size = AV_RB32(pkt->data) >> 1; + break; case RTMP_PT_INVOKE: //TODO: check for the messages sent for wrong state? if (!memcmp(pkt->data, "\002\000\006_error", 9)) { @@ -637,7 +666,7 @@ static int rtmp_parse_result(URLContext *s, RTMPContext *rt, RTMPPacket *pkt) } /** - * Interacts with the server by receiving and sending RTMP packets until + * Interact with the server by receiving and sending RTMP packets until * there is some significant data (media data or expected status notification). * * @param s reading context @@ -662,13 +691,19 @@ static int get_packet(URLContext *s, int for_header) for (;;) { RTMPPacket rpkt; if ((ret = ff_rtmp_packet_read(rt->stream, &rpkt, - rt->chunk_size, rt->prev_pkt[0])) != 0) { - if (ret > 0) { + rt->chunk_size, rt->prev_pkt[0])) <= 0) { + if (ret == 0) { return AVERROR(EAGAIN); } else { return AVERROR(EIO); } } + rt->bytes_read += ret; + if (rt->bytes_read > rt->last_bytes_read + rt->client_report_size) { + av_log(LOG_CONTEXT, AV_LOG_DEBUG, "Sending bytes read report\n"); + gen_bytes_read(s, rt, rpkt.timestamp + 1); + rt->last_bytes_read = rt->bytes_read; + } ret = rtmp_parse_result(s, rt, &rpkt); if (ret < 0) {//serious error in current packet @@ -717,7 +752,7 @@ static int get_packet(URLContext *s, int for_header) data_size = bytestream_get_be24(&next); p=next; cts = bytestream_get_be24(&next); - cts |= bytestream_get_byte(&next); + cts |= bytestream_get_byte(&next) << 24; if (pts==0) pts=cts; ts += cts - pts; @@ -756,7 +791,7 @@ static int rtmp_close(URLContext *h) } /** - * Opens RTMP connection and verifies that the stream can be played. + * Open RTMP connection and verify that the stream can be played. * * URL syntax: rtmp://server[:port][/app][/playpath] * where 'app' is first one or two directories in the path @@ -778,12 +813,12 @@ static int rtmp_open(URLContext *s, const char *uri, int flags) s->priv_data = rt; rt->is_input = !(flags & URL_WRONLY); - url_split(proto, sizeof(proto), NULL, 0, hostname, sizeof(hostname), &port, - path, sizeof(path), s->filename); + av_url_split(proto, sizeof(proto), NULL, 0, hostname, sizeof(hostname), &port, + path, sizeof(path), s->filename); if (port < 0) port = RTMP_DEFAULT_PORT; - snprintf(buf, sizeof(buf), "tcp://%s:%d", hostname, port); + ff_url_join(buf, sizeof(buf), "tcp", NULL, hostname, port, NULL); if (url_open(&rt->stream, buf, URL_RDWR) < 0) { av_log(LOG_CONTEXT, AV_LOG_ERROR, "Cannot open connection %s\n", buf); @@ -826,6 +861,10 @@ static int rtmp_open(URLContext *s, const char *uri, int flags) } strncat(rt->playpath, fname, sizeof(rt->playpath) - 5); + rt->client_report_size = 1048576; + rt->bytes_read = 0; + rt->last_bytes_read = 0; + av_log(LOG_CONTEXT, AV_LOG_DEBUG, "Proto = %s, path = %s, app = %s, fname = %s\n", proto, path, rt->app, rt->playpath); gen_connect(s, rt, proto, hostname, port); @@ -876,6 +915,7 @@ static int rtmp_read(URLContext *s, uint8_t *buf, int size) buf += data_left; size -= data_left; rt->flv_off = rt->flv_size; + return data_left; } if ((ret = get_packet(s, 0)) < 0) return ret; @@ -883,9 +923,9 @@ static int rtmp_read(URLContext *s, uint8_t *buf, int size) return orig_size; } -static int rtmp_write(URLContext *h, uint8_t *buf, int size) +static int rtmp_write(URLContext *s, const uint8_t *buf, int size) { - RTMPContext *rt = h->priv_data; + RTMPContext *rt = s->priv_data; int size_temp = size; int pktsize, pkttype; uint32_t ts; @@ -949,7 +989,7 @@ static int rtmp_write(URLContext *h, uint8_t *buf, int size) return size; } -URLProtocol rtmp_protocol = { +URLProtocol ff_rtmp_protocol = { "rtmp", rtmp_open, rtmp_read,