51 #define APP_MAX_LENGTH 1024
52 #define PLAYPATH_MAX_LENGTH 256
53 #define TCURL_MAX_LENGTH 512
54 #define FLASHVER_MAX_LENGTH 64
55 #define RTMP_PKTDATA_DEFAULT_SIZE 4096
56 #define RTMP_HEADER 11
130 #define PLAYER_KEY_OPEN_PART_LEN 30
133 'G',
'e',
'n',
'u',
'i',
'n',
'e',
' ',
'A',
'd',
'o',
'b',
'e',
' ',
134 'F',
'l',
'a',
's',
'h',
' ',
'P',
'l',
'a',
'y',
'e',
'r',
' ',
'0',
'0',
'1',
136 0xF0, 0xEE, 0xC2, 0x4A, 0x80, 0x68, 0xBE, 0xE8, 0x2E, 0x00, 0xD0, 0xD1, 0x02,
137 0x9E, 0x7E, 0x57, 0x6E, 0xEC, 0x5D, 0x2D, 0x29, 0x80, 0x6F, 0xAB, 0x93, 0xB8,
138 0xE6, 0x36, 0xCF, 0xEB, 0x31, 0xAE
141 #define SERVER_KEY_OPEN_PART_LEN 36
144 'G',
'e',
'n',
'u',
'i',
'n',
'e',
' ',
'A',
'd',
'o',
'b',
'e',
' ',
145 'F',
'l',
'a',
's',
'h',
' ',
'M',
'e',
'd',
'i',
'a',
' ',
146 'S',
'e',
'r',
'v',
'e',
'r',
' ',
'0',
'0',
'1',
148 0xF0, 0xEE, 0xC2, 0x4A, 0x80, 0x68, 0xBE, 0xE8, 0x2E, 0x00, 0xD0, 0xD1, 0x02,
149 0x9E, 0x7E, 0x57, 0x6E, 0xEC, 0x5D, 0x2D, 0x29, 0x80, 0x6F, 0xAB, 0x93, 0xB8,
150 0xE6, 0x36, 0xCF, 0xEB, 0x31, 0xAE
184 char **tracked_method)
260 if (param[0] && param[1] ==
':') {
263 }
else if (param[0] ==
'N' && param[1] && param[2] ==
':') {
266 value = strchr(field,
':');
367 char *param = rt->
conn;
370 while (param != NULL) {
372 param += strspn(param,
" ");
375 sep = strchr(param,
' ');
418 if (strcmp(command,
"connect")) {
429 "app", tmpstr,
sizeof(tmpstr));
432 if (!ret && strcmp(tmpstr, rt->
app))
455 bytestream_put_byte(&p, 2);
469 bytestream_put_be16(&p, 0);
470 bytestream_put_be32(&p, 0);
677 bytestream_put_be16(&p, 3);
771 if (ppkt->
size < 6) {
782 bytestream_put_be16(&p, 7);
803 bytestream_put_be16(&p, 27);
869 const char *subscribe)
876 0, 27 + strlen(subscribe))) < 0)
900 memcpy(hmac_buf, key, keylen);
906 for (i = 0; i < 64; i++)
919 for (i = 0; i < 64; i++)
933 int i, digest_pos = 0;
935 for (i = 0; i < 4; i++)
936 digest_pos += buf[i + off];
937 digest_pos = digest_pos % mod_val + add_val;
988 if (!memcmp(digest, buf + digest_pos, 32))
1001 "Hash of the decompressed SWF file is not 32 bytes long.\n");
1006 bytestream_put_byte(&p, 1);
1007 bytestream_put_byte(&p, 1);
1008 bytestream_put_be32(&p, rt->
swfsize);
1009 bytestream_put_be32(&p, rt->
swfsize);
1018 static int rtmp_uncompress_swfplayer(
uint8_t *in_data, int64_t in_size,
1019 uint8_t **out_data, int64_t *out_size)
1021 z_stream zs = { 0 };
1026 zs.avail_in = in_size;
1027 zs.next_in = in_data;
1028 ret = inflateInit(&zs);
1035 zs.avail_out =
sizeof(tmp_buf);
1036 zs.next_out = tmp_buf;
1038 ret = inflate(&zs, Z_NO_FLUSH);
1039 if (ret != Z_OK && ret != Z_STREAM_END) {
1044 size =
sizeof(tmp_buf) - zs.avail_out;
1045 if (!(ptr =
av_realloc(*out_data, *out_size + size))) {
1051 memcpy(*out_data + *out_size, tmp_buf, size);
1053 }
while (zs.avail_out == 0);
1064 uint8_t *in_data = NULL, *out_data = NULL, *swfdata;
1065 int64_t in_size, out_size;
1096 if (!memcmp(in_data,
"CWS", 3)) {
1103 memcpy(out_data, in_data, 8);
1107 if ((ret = rtmp_uncompress_swfplayer(in_data + 8, in_size - 8,
1108 &out_data, &out_size)) < 0)
1112 "Zlib is required for decompressing the SWF player file.\n");
1125 "Genuine Adobe Flash Player 001", 30,
1160 int server_pos, client_pos;
1171 if (CONFIG_FFRTMPCRYPT_PROTOCOL && rt->
encrypted) {
1211 serverdata[5], serverdata[6], serverdata[7], serverdata[8]);
1213 if (rt->
is_input && serverdata[5] >= 3) {
1245 0, digest, 32, signature);
1249 if (CONFIG_FFRTMPCRYPT_PROTOCOL && rt->
encrypted) {
1253 tosend + 1, type)) < 0)
1275 tosend + RTMP_HANDSHAKE_PACKET_SIZE - 32);
1279 if (CONFIG_FFRTMPCRYPT_PROTOCOL && rt->
encrypted) {
1282 RTMP_HANDSHAKE_PACKET_SIZE - 32, digest,
1288 RTMP_HANDSHAKE_PACKET_SIZE)) < 0)
1291 if (CONFIG_FFRTMPCRYPT_PROTOCOL && rt->
encrypted) {
1297 if (CONFIG_FFRTMPCRYPT_PROTOCOL && rt->
encrypted) {
1301 tosend + 1, 1)) < 0)
1304 if (serverdata[0] == 9) {
1315 if (CONFIG_FFRTMPCRYPT_PROTOCOL && rt->
encrypted) {
1326 uint32_t *second_int,
char *arraydata,
1337 " not following standard\n", (
int)inoutsize);
1341 *first_int =
AV_RB32(arraydata);
1342 *second_int =
AV_RB32(arraydata + 4);
1347 uint32_t second_int,
char *arraydata,
int size)
1351 AV_WB32(arraydata, first_int);
1352 AV_WB32(arraydata + 4, second_int);
1370 uint32_t hs_my_epoch;
1380 if (inoutsize <= 0) {
1385 if (buffer[0] != 3) {
1391 "Unable to write answer - RTMP S0\n");
1403 hs_my_epoch = hs_epoch;
1429 if (temp != hs_my_epoch)
1431 "Erroneous C2 Message epoch does not match up with C1 epoch\n");
1432 if (memcmp(buffer + 8, hs_s1 + 8,
1435 "Erroneous C2 Message random does not match up\n");
1445 if (pkt->
size < 4) {
1447 "Too short chunk size change packet (%d)\n",
1478 if (pkt->
size < 2) {
1486 if ((ret =
gen_pong(s, rt, pkt)) < 0)
1488 }
else if (t == 26) {
1504 if (pkt->
size < 4) {
1506 "Client bandwidth report packet is less than 4 bytes long (%d)\n",
1528 if (pkt->
size < 4) {
1530 "Too short server bandwidth report packet (%d)\n",
1547 const char *opaque,
const char *challenge)
1575 "?authmod=%s&user=%s&challenge=%s&response=%s",
1576 "adobe", user, challenge2, hashstr);
1579 "&opaque=%s", opaque);
1588 char hashstr1[33], hashstr2[33];
1589 const char *realm =
"live";
1590 const char *method =
"publish";
1591 const char *qop =
"auth";
1592 const char *nc =
"00000001";
1608 hashstr1[32] =
'\0';
1614 if (!strchr(rt->
app,
'/'))
1618 hashstr2[32] =
'\0';
1637 "?authmod=%s&user=%s&nonce=%s&cnonce=%s&nc=%s&response=%s",
1638 "llnw", user, nonce, cnonce, nc, hashstr1);
1647 char buf[300], *ptr, authmod[15];
1649 const char *user =
"", *salt =
"", *opaque = NULL,
1650 *challenge = NULL, *cptr = NULL, *nonce = NULL;
1652 if (!(cptr = strstr(desc,
"authmod=adobe")) &&
1653 !(cptr = strstr(desc,
"authmod=llnw"))) {
1655 "Unknown connect error (unsupported authentication method?)\n");
1658 cptr += strlen(
"authmod=");
1659 while (*cptr && *cptr !=
' ' && i <
sizeof(authmod) - 1)
1660 authmod[i++] = *cptr++;
1668 if (strstr(desc,
"?reason=authfailed")) {
1671 }
else if (strstr(desc,
"?reason=nosuchuser")) {
1683 if (strstr(desc,
"code=403 need auth")) {
1685 "?authmod=%s&user=%s", authmod, rt->
username);
1689 if (!(cptr = strstr(desc,
"?reason=needauth"))) {
1698 char *next = strchr(ptr,
'&');
1699 char *
value = strchr(ptr,
'=');
1704 if (!strcmp(ptr,
"user")) {
1706 }
else if (!strcmp(ptr,
"salt")) {
1708 }
else if (!strcmp(ptr,
"opaque")) {
1710 }
else if (!strcmp(ptr,
"challenge")) {
1712 }
else if (!strcmp(ptr,
"nonce")) {
1718 if (!strcmp(authmod,
"adobe")) {
1719 if ((ret =
do_adobe_auth(rt, user, salt, opaque, challenge)) < 0)
1734 char *tracked_method = NULL;
1743 "description", tmpstr,
sizeof(tmpstr))) {
1744 if (tracked_method && (!strcmp(tracked_method,
"_checkbw") ||
1745 !strcmp(tracked_method,
"releaseStream") ||
1746 !strcmp(tracked_method,
"FCSubscribe") ||
1747 !strcmp(tracked_method,
"FCPublish"))) {
1751 }
else if (tracked_method && !strcmp(tracked_method,
"connect")) {
1759 av_log(s, level,
"Server error: %s\n", tmpstr);
1781 bytestream2_put_be16(&pbc, 0);
1793 const char *status,
const char *filename)
1797 char statusmsg[128];
1820 snprintf(statusmsg,
sizeof(statusmsg),
1821 "%s is now published", filename);
1865 if (!strcmp(command,
"FCPublish") ||
1866 !strcmp(command,
"publish")) {
1868 sizeof(filename), &stringlen);
1874 "Unable to find / in url %s, bad format\n",
1879 if (strcmp(pchar, filename))
1881 " %s\n", filename, pchar);
1886 if (!strcmp(command,
"FCPublish")) {
1895 }
else if (!strcmp(command,
"publish")) {
1903 }
else if (!strcmp(command,
"play")) {
1921 if (!strcmp(command,
"createStream")) {
1941 char *tracked_method = NULL;
1947 if (!tracked_method) {
1952 if (!strcmp(tracked_method,
"connect")) {
1973 }
else if (rt->
live == -1) {
1978 }
else if (!strcmp(tracked_method,
"createStream")) {
1980 if (pkt->
data[10] || pkt->
data[19] != 5 || pkt->
data[20]) {
2010 for (i = 0; i < 2; i++) {
2018 if (!t && !strcmp(tmpstr,
"error")) {
2020 "description", tmpstr,
sizeof(tmpstr));
2021 if (t || !tmpstr[0])
2023 tmpstr,
sizeof(tmpstr));
2032 if (!t && !strcmp(tmpstr,
"NetStream.Play.UnpublishNotify")) rt->
state =
STATE_STOPPED;
2086 return old_flv_size;
2091 int old_flv_size,
ret;
2094 const int size = pkt->
size - skip;
2105 bytestream2_put_byte(&pbc, pkt->
type);
2106 bytestream2_put_be24(&pbc, size);
2107 bytestream2_put_be24(&pbc, ts);
2108 bytestream2_put_byte(&pbc, ts >> 24);
2109 bytestream2_put_be24(&pbc, 0);
2111 bytestream2_put_be32(&pbc, 0);
2120 char statusmsg[128];
2121 int stringlen,
ret, skip = 0;
2130 if (!strcmp(commandbuffer,
"@setDataFrame")) {
2133 sizeof(statusmsg), &stringlen);
2155 switch (pkt->
type) {
2157 av_dlog(s,
"received bytes read report\n");
2198 uint32_t ts, cts, pts = 0;
2214 type = bytestream_get_byte(&next);
2215 size = bytestream_get_be24(&next);
2216 cts = bytestream_get_be24(&next);
2217 cts |= bytestream_get_byte(&next) << 24;
2222 if (size + 3 + 4 > pkt->
data + pkt->
size - next)
2224 bytestream_put_byte(&p, type);
2225 bytestream_put_be24(&p, size);
2226 bytestream_put_be24(&p, ts);
2227 bytestream_put_byte(&p, ts >> 24);
2228 memcpy(p, next, size + 3 + 4);
2229 next += size + 3 + 4;
2234 "RTMP_PT_METADATA packet\n");
2345 for (i = 0; i < 2; i++) {
2369 char proto[8], hostname[256], path[1024], auth[100], *fname;
2382 hostname,
sizeof(hostname), &port,
2385 if (strchr(path,
' ')) {
2387 "Detected librtmp style URL parameters, these aren't supported "
2388 "by the libavformat internal RTMP handler currently enabled. "
2389 "See the documentation for the correct way to pass parameters.\n");
2393 char *ptr = strchr(auth,
':');
2401 if (rt->
listen && strcmp(proto,
"rtmp")) {
2406 if (!strcmp(proto,
"rtmpt") || !strcmp(proto,
"rtmpts")) {
2407 if (!strcmp(proto,
"rtmpts"))
2411 ff_url_join(buf,
sizeof(buf),
"ffrtmphttp", NULL, hostname, port, NULL);
2412 }
else if (!strcmp(proto,
"rtmps")) {
2416 ff_url_join(buf,
sizeof(buf),
"tls", NULL, hostname, port, NULL);
2417 }
else if (!strcmp(proto,
"rtmpe") || (!strcmp(proto,
"rtmpte"))) {
2418 if (!strcmp(proto,
"rtmpte"))
2419 av_dict_set(&opts,
"ffrtmpcrypt_tunneling",
"1", 1);
2422 ff_url_join(buf,
sizeof(buf),
"ffrtmpcrypt", NULL, hostname, port, NULL);
2429 ff_url_join(buf,
sizeof(buf),
"tcp", NULL, hostname, port,
2430 "?listen&listen_timeout=%d",
2433 ff_url_join(buf,
sizeof(buf),
"tcp", NULL, hostname, port, NULL);
2468 if (!strncmp(path,
"/ondemand/", 10)) {
2470 memcpy(rt->
app,
"ondemand", 9);
2472 char *next = *path ? path + 1 : path;
2473 char *p = strchr(next,
'/');
2479 char *
c = strchr(p + 1,
':');
2480 fname = strchr(p + 1,
'/');
2481 if (!fname || (c && c < fname)) {
2502 int len = strlen(fname);
2510 if (!strchr(fname,
':') && len >= 4 &&
2511 (!strcmp(fname + len - 4,
".f4v") ||
2512 !strcmp(fname + len - 4,
".mp4"))) {
2514 }
else if (len >= 4 && !strcmp(fname + len - 4,
".flv")) {
2515 fname[len - 4] =
'\0';
2529 port,
"/%s", rt->
app);
2565 }
while (ret ==
AVERROR(EAGAIN));
2575 for (i = 0; i < 2; i++)
2610 int orig_size =
size;
2616 if (data_left >= size) {
2621 if (data_left > 0) {
2640 "Seek on stream index %d at timestamp %"PRId64
" with flags %08x\n",
2641 stream_index, timestamp, flags);
2642 if ((ret =
gen_seek(s, rt, timestamp)) < 0) {
2644 "Unable to send seek command on stream index %d at timestamp "
2645 "%"PRId64
" with flags %08x\n",
2646 stream_index, timestamp, flags);
2657 int size_temp =
size;
2658 int pktsize, pkttype;
2683 pkttype = bytestream_get_byte(&header);
2684 pktsize = bytestream_get_be24(&header);
2685 ts = bytestream_get_be24(&header);
2686 ts |= bytestream_get_byte(&header) << 24;
2687 bytestream_get_be24(&header);
2707 pkttype, ts, pktsize)) < 0)
2737 }
while (buf_temp - buf < size);
2755 }
else if (ret < 0) {
2757 }
else if (ret == 1) {
2775 #define OFFSET(x) offsetof(RTMPContext, x)
2776 #define DEC AV_OPT_FLAG_DECODING_PARAM
2777 #define ENC AV_OPT_FLAG_ENCODING_PARAM
2781 {
"rtmp_buffer",
"Set buffer time in milliseconds. The default is 3000.",
OFFSET(client_buffer_time),
AV_OPT_TYPE_INT, {.i64 = 3000}, 0, INT_MAX,
DEC|
ENC},
2783 {
"rtmp_flashver",
"Version of the Flash plugin used to run the SWF player.",
OFFSET(flashver),
AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0,
DEC|
ENC},
2784 {
"rtmp_flush_interval",
"Number of packets flushed in the same request (RTMPT only).",
OFFSET(flush_interval),
AV_OPT_TYPE_INT, {.i64 = 10}, 0, INT_MAX,
ENC},
2785 {
"rtmp_live",
"Specify that the media is a live stream.",
OFFSET(live),
AV_OPT_TYPE_INT, {.i64 = -2}, INT_MIN, INT_MAX,
DEC,
"rtmp_live"},
2789 {
"rtmp_pageurl",
"URL of the web page in which the media was embedded. By default no value will be sent.",
OFFSET(pageurl),
AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0,
DEC},
2791 {
"rtmp_subscribe",
"Name of live stream to subscribe to. Defaults to rtmp_playpath.",
OFFSET(subscribe),
AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0,
DEC},
2793 {
"rtmp_swfsize",
"Size of the decompressed SWF file, required for SWFVerification.",
OFFSET(swfsize),
AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX,
DEC},
2795 {
"rtmp_swfverify",
"URL to player swf file, compute hash/size automatically.",
OFFSET(swfverify),
AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0,
DEC},
2796 {
"rtmp_tcurl",
"URL of the target stream. Defaults to proto://host[:port]/app.",
OFFSET(tcurl),
AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0,
DEC|
ENC},
2797 {
"rtmp_listen",
"Listen for incoming rtmp connections",
OFFSET(listen),
AV_OPT_TYPE_INT, {.i64 = 0}, INT_MIN, INT_MAX,
DEC,
"rtmp_listen" },
2798 {
"listen",
"Listen for incoming rtmp connections",
OFFSET(listen),
AV_OPT_TYPE_INT, {.i64 = 0}, INT_MIN, INT_MAX,
DEC,
"rtmp_listen" },
2799 {
"timeout",
"Maximum timeout (in seconds) to wait for incoming connections. -1 is infinite. Implies -rtmp_listen 1",
OFFSET(listen_timeout),
AV_OPT_TYPE_INT, {.i64 = -1}, INT_MIN, INT_MAX,
DEC,
"rtmp_listen" },
2803 #define RTMP_PROTOCOL(flavor) \
2804 static const AVClass flavor##_class = { \
2805 .class_name = #flavor, \
2806 .item_name = av_default_item_name, \
2807 .option = rtmp_options, \
2808 .version = LIBAVUTIL_VERSION_INT, \
2811 URLProtocol ff_##flavor##_protocol = { \
2813 .url_open = rtmp_open, \
2814 .url_read = rtmp_read, \
2815 .url_read_seek = rtmp_seek, \
2816 .url_write = rtmp_write, \
2817 .url_close = rtmp_close, \
2818 .priv_data_size = sizeof(RTMPContext), \
2819 .flags = URL_PROTOCOL_FLAG_NETWORK, \
2820 .priv_data_class= &flavor##_class, \