diff options
Diffstat (limited to 'libavformat/rtmpproto.c')
-rw-r--r-- | libavformat/rtmpproto.c | 167 |
1 files changed, 109 insertions, 58 deletions
diff --git a/libavformat/rtmpproto.c b/libavformat/rtmpproto.c index 0097841e81..aa1cd8f539 100644 --- a/libavformat/rtmpproto.c +++ b/libavformat/rtmpproto.c @@ -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,9 +48,9 @@ #include <zlib.h> #endif -#define APP_MAX_LENGTH 128 -#define PLAYPATH_MAX_LENGTH 256 -#define TCURL_MAX_LENGTH 512 +#define APP_MAX_LENGTH 1024 +#define PLAYPATH_MAX_LENGTH 512 +#define TCURL_MAX_LENGTH 1024 #define FLASHVER_MAX_LENGTH 64 #define RTMP_PKTDATA_DEFAULT_SIZE 4096 #define RTMP_HEADER 11 @@ -94,8 +94,8 @@ typedef struct RTMPContext { int flv_nb_packets; ///< number of flv packets published 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 + uint64_t bytes_read; ///< number of bytes read from server + uint64_t last_bytes_read; ///< number of bytes read last reported to server uint32_t last_timestamp; ///< last timestamp received in a packet 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 @@ -156,6 +156,8 @@ static const uint8_t rtmp_server_key[] = { }; static int handle_chunk_size(URLContext *s, RTMPPacket *pkt); +static int handle_server_bw(URLContext *s, RTMPPacket *pkt); +static int handle_client_bw(URLContext *s, RTMPPacket *pkt); static int add_tracked_method(RTMPContext *rt, const char *name, int id) { @@ -217,9 +219,8 @@ static void free_tracked_methods(RTMPContext *rt) int i; for (i = 0; i < rt->nb_tracked_methods; i ++) - av_free(rt->tracked_methods[i].name); - av_free(rt->tracked_methods); - rt->tracked_methods = NULL; + av_freep(&rt->tracked_methods[i].name); + av_freep(&rt->tracked_methods); rt->tracked_methods_size = 0; rt->nb_tracked_methods = 0; } @@ -322,7 +323,7 @@ static int gen_connect(URLContext *s, RTMPContext *rt) 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; @@ -400,6 +401,9 @@ static int gen_connect(URLContext *s, RTMPContext *rt) return rtmp_send_packet(rt, &pkt, 1); } + +#define RTMP_CTRL_ABORT_MESSAGE (2) + static int read_connect(URLContext *s, RTMPContext *rt) { RTMPPacket pkt = { 0 }; @@ -412,17 +416,42 @@ static int read_connect(URLContext *s, RTMPContext *rt) uint8_t tmpstr[256]; GetByteContext gbc; - if ((ret = ff_rtmp_packet_read(rt->stream, &pkt, rt->in_chunk_size, - &rt->prev_pkt[0], &rt->nb_prev_pkt[0])) < 0) - return ret; - - if (pkt.type == RTMP_PT_CHUNK_SIZE) { - if ((ret = handle_chunk_size(s, &pkt)) < 0) - return ret; - ff_rtmp_packet_destroy(&pkt); + // handle RTMP Protocol Control Messages + for (;;) { if ((ret = ff_rtmp_packet_read(rt->stream, &pkt, rt->in_chunk_size, &rt->prev_pkt[0], &rt->nb_prev_pkt[0])) < 0) return ret; +#ifdef DEBUG + ff_rtmp_packet_dump(s, &pkt); +#endif + if (pkt.type == RTMP_PT_CHUNK_SIZE) { + if ((ret = handle_chunk_size(s, &pkt)) < 0) { + ff_rtmp_packet_destroy(&pkt); + return ret; + } + } else if (pkt.type == RTMP_CTRL_ABORT_MESSAGE) { + av_log(s, AV_LOG_ERROR, "received abort message\n"); + ff_rtmp_packet_destroy(&pkt); + return AVERROR_UNKNOWN; + } else if (pkt.type == RTMP_PT_BYTES_READ) { + av_log(s, AV_LOG_TRACE, "received acknowledgement\n"); + } else if (pkt.type == RTMP_PT_SERVER_BW) { + if ((ret = handle_server_bw(s, &pkt)) < 0) { + ff_rtmp_packet_destroy(&pkt); + return ret; + } + } else if (pkt.type == RTMP_PT_CLIENT_BW) { + if ((ret = handle_client_bw(s, &pkt)) < 0) { + ff_rtmp_packet_destroy(&pkt); + return ret; + } + } else if (pkt.type == RTMP_PT_INVOKE) { + // received RTMP Command Message + break; + } else { + av_log(s, AV_LOG_ERROR, "Unknown control message type (%d)\n", pkt.type); + } + ff_rtmp_packet_destroy(&pkt); } cp = pkt.data; @@ -1118,8 +1147,9 @@ static int rtmp_calc_swfhash(URLContext *s) int ret = 0; /* Get the SWF player file. */ - if ((ret = ffurl_open(&stream, rt->swfverify, AVIO_FLAG_READ, - &s->interrupt_callback, NULL, s->protocols, s)) < 0) { + if ((ret = ffurl_open_whitelist(&stream, rt->swfverify, AVIO_FLAG_READ, + &s->interrupt_callback, NULL, + s->protocol_whitelist, s->protocol_blacklist, s)) < 0) { av_log(s, AV_LOG_ERROR, "Cannot open connection %s.\n", rt->swfverify); goto fail; } @@ -1748,18 +1778,23 @@ static int handle_connect_error(URLContext *s, const char *desc) 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; } @@ -2415,7 +2450,7 @@ static int get_packet(URLContext *s, int for_header) rt->last_timestamp = rpkt.timestamp; 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; @@ -2532,7 +2567,7 @@ static int inject_fake_duration_metadata(RTMPContext *rt) // Increase the size by the injected packet rt->flv_size += 55; // Delete the old FLV data - av_free(old_flv_data); + av_freep(&old_flv_data); p = rt->flv_data + 13; bytestream_put_byte(&p, FLV_TAG_TYPE_META); @@ -2573,14 +2608,13 @@ static int inject_fake_duration_metadata(RTMPContext *rt) * and 'playpath' is a file name (the rest of the path, * may be prefixed with "mp4:") */ -static int rtmp_open(URLContext *s, const char *uri, int flags) +static int rtmp_open(URLContext *s, const char *uri, int flags, AVDictionary **opts) { RTMPContext *rt = s->priv_data; char proto[8], hostname[256], path[1024], auth[100], *fname; - char *old_app, *qmark, fname_buffer[1024]; + char *old_app, *qmark, *n, fname_buffer[1024]; uint8_t buf[2048]; int port; - AVDictionary *opts = NULL; int ret; if (rt->listen_timeout > 0) @@ -2592,11 +2626,13 @@ static int rtmp_open(URLContext *s, const char *uri, int flags) hostname, sizeof(hostname), &port, path, sizeof(path), s->filename); - if (strchr(path, ' ')) { + n = strchr(path, ' '); + if (n) { av_log(s, AV_LOG_WARNING, "Detected librtmp style URL parameters, these aren't supported " "by the libavformat internal RTMP handler currently enabled. " "See the documentation for the correct way to pass parameters.\n"); + *n = '\0'; // Trim not supported part } if (auth[0]) { @@ -2615,7 +2651,7 @@ static int rtmp_open(URLContext *s, const char *uri, int flags) } if (!strcmp(proto, "rtmpt") || !strcmp(proto, "rtmpts")) { if (!strcmp(proto, "rtmpts")) - av_dict_set(&opts, "ffrtmphttp_tls", "1", 1); + av_dict_set(opts, "ffrtmphttp_tls", "1", 1); /* open the http tunneling connection */ ff_url_join(buf, sizeof(buf), "ffrtmphttp", NULL, hostname, port, NULL); @@ -2626,7 +2662,7 @@ static int rtmp_open(URLContext *s, const char *uri, int flags) ff_url_join(buf, sizeof(buf), "tls", NULL, hostname, port, NULL); } else if (!strcmp(proto, "rtmpe") || (!strcmp(proto, "rtmpte"))) { if (!strcmp(proto, "rtmpte")) - av_dict_set(&opts, "ffrtmpcrypt_tunneling", "1", 1); + av_dict_set(opts, "ffrtmpcrypt_tunneling", "1", 1); /* open the encrypted connection */ ff_url_join(buf, sizeof(buf), "ffrtmpcrypt", NULL, hostname, port, NULL); @@ -2644,8 +2680,9 @@ static int rtmp_open(URLContext *s, const char *uri, int flags) } reconnect: - if ((ret = ffurl_open(&rt->stream, buf, AVIO_FLAG_READ_WRITE, - &s->interrupt_callback, &opts, s->protocols, s)) < 0) { + if ((ret = ffurl_open_whitelist(&rt->stream, buf, AVIO_FLAG_READ_WRITE, + &s->interrupt_callback, opts, + s->protocol_whitelist, s->protocol_blacklist, s)) < 0) { av_log(s , AV_LOG_ERROR, "Cannot open connection %s\n", buf); goto fail; } @@ -2695,8 +2732,14 @@ reconnect: char *next = *path ? path + 1 : path; char *p = strchr(next, '/'); if (!p) { - fname = next; - rt->app[0] = '\0'; + if (old_app) { + // If name of application has been defined by the user, assume that + // playpath is provided in the URL + fname = next; + } else { + fname = NULL; + av_strlcpy(rt->app, next, APP_MAX_LENGTH); + } } else { // make sure we do not mismatch a playpath for an application instance char *c = strchr(p + 1, ':'); @@ -2713,29 +2756,36 @@ 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; } if (!rt->playpath) { - int len = strlen(fname); - rt->playpath = av_malloc(PLAYPATH_MAX_LENGTH); if (!rt->playpath) { ret = AVERROR(ENOMEM); goto fail; } - if (!strchr(fname, ':') && len >= 4 && - (!strcmp(fname + len - 4, ".f4v") || - !strcmp(fname + len - 4, ".mp4"))) { - memcpy(rt->playpath, "mp4:", 5); + if (fname) { + int len = strlen(fname); + if (!strchr(fname, ':') && len >= 4 && + (!strcmp(fname + len - 4, ".f4v") || + !strcmp(fname + len - 4, ".mp4"))) { + memcpy(rt->playpath, "mp4:", 5); + } else { + if (len >= 4 && !strcmp(fname + len - 4, ".flv")) + fname[len - 4] = '\0'; + rt->playpath[0] = 0; + } + av_strlcat(rt->playpath, fname, PLAYPATH_MAX_LENGTH); } else { - if (len >= 4 && !strcmp(fname + len - 4, ".flv")) - fname[len - 4] = '\0'; - rt->playpath[0] = 0; + rt->playpath[0] = '\0'; } - av_strlcat(rt->playpath, fname, PLAYPATH_MAX_LENGTH); } if (!rt->tcurl) { @@ -2849,7 +2899,7 @@ reconnect: return 0; fail: - av_dict_free(&opts); + av_dict_free(opts); rtmp_close(s); return ret; } @@ -2937,6 +2987,7 @@ static int rtmp_write(URLContext *s, const uint8_t *buf, int size) if (rt->flv_header_bytes < RTMP_HEADER) { const uint8_t *header = rt->flv_header; int channel = RTMP_AUDIO_CHANNEL; + copy = FFMIN(RTMP_HEADER - rt->flv_header_bytes, size_temp); bytestream_get_buffer(&buf_temp, rt->flv_header + rt->flv_header_bytes, copy); rt->flv_header_bytes += copy; @@ -3093,7 +3144,7 @@ static const AVClass flavor##_class = { \ \ const URLProtocol ff_##flavor##_protocol = { \ .name = #flavor, \ - .url_open = rtmp_open, \ + .url_open2 = rtmp_open, \ .url_read = rtmp_read, \ .url_read_seek = rtmp_seek, \ .url_read_pause = rtmp_pause, \ |