53 #define APP_MAX_LENGTH 128
54 #define PLAYPATH_MAX_LENGTH 256
55 #define TCURL_MAX_LENGTH 512
56 #define FLASHVER_MAX_LENGTH 64
57 #define RTMP_PKTDATA_DEFAULT_SIZE 4096
128 #define PLAYER_KEY_OPEN_PART_LEN 30
130 static const uint8_t rtmp_player_key[] = {
131 'G',
'e',
'n',
'u',
'i',
'n',
'e',
' ',
'A',
'd',
'o',
'b',
'e',
' ',
132 'F',
'l',
'a',
's',
'h',
' ',
'P',
'l',
'a',
'y',
'e',
'r',
' ',
'0',
'0',
'1',
134 0xF0, 0xEE, 0xC2, 0x4A, 0x80, 0x68, 0xBE, 0xE8, 0x2E, 0x00, 0xD0, 0xD1, 0x02,
135 0x9E, 0x7E, 0x57, 0x6E, 0xEC, 0x5D, 0x2D, 0x29, 0x80, 0x6F, 0xAB, 0x93, 0xB8,
136 0xE6, 0x36, 0xCF, 0xEB, 0x31, 0xAE
139 #define SERVER_KEY_OPEN_PART_LEN 36
141 static const uint8_t rtmp_server_key[] = {
142 'G',
'e',
'n',
'u',
'i',
'n',
'e',
' ',
'A',
'd',
'o',
'b',
'e',
' ',
143 'F',
'l',
'a',
's',
'h',
' ',
'M',
'e',
'd',
'i',
'a',
' ',
144 'S',
'e',
'r',
'v',
'e',
'r',
' ',
'0',
'0',
'1',
146 0xF0, 0xEE, 0xC2, 0x4A, 0x80, 0x68, 0xBE, 0xE8, 0x2E, 0x00, 0xD0, 0xD1, 0x02,
147 0x9E, 0x7E, 0x57, 0x6E, 0xEC, 0x5D, 0x2D, 0x29, 0x80, 0x6F, 0xAB, 0x93, 0xB8,
148 0xE6, 0x36, 0xCF, 0xEB, 0x31, 0xAE
181 char **tracked_method)
257 if (param[0] && param[1] ==
':') {
260 }
else if (param[0] ==
'N' && param[1] && param[2] ==
':') {
263 value = strchr(field,
':');
269 if (!field || !value)
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);
748 if (ppkt->
size < 6) {
759 bytestream_put_be16(&p, 7);
780 bytestream_put_be16(&p, 27);
846 const char *subscribe)
853 0, 27 + strlen(subscribe))) < 0)
877 memcpy(hmac_buf, key, keylen);
883 for (i = 0; i < 64; i++)
896 for (i = 0; i < 64; i++)
910 int i, digest_pos = 0;
912 for (i = 0; i < 4; i++)
913 digest_pos += buf[i + off];
914 digest_pos = digest_pos % mod_val + add_val;
965 if (!memcmp(digest, buf + digest_pos, 32))
978 "Hash of the decompressed SWF file is not 32 bytes long.\n");
983 bytestream_put_byte(&p, 1);
984 bytestream_put_byte(&p, 1);
985 bytestream_put_be32(&p, rt->
swfsize);
986 bytestream_put_be32(&p, rt->
swfsize);
995 static int rtmp_uncompress_swfplayer(
uint8_t *in_data, int64_t in_size,
996 uint8_t **out_data, int64_t *out_size)
1003 zs.avail_in = in_size;
1004 zs.next_in = in_data;
1005 ret = inflateInit(&zs);
1012 zs.avail_out =
sizeof(tmp_buf);
1013 zs.next_out = tmp_buf;
1015 ret = inflate(&zs, Z_NO_FLUSH);
1016 if (ret != Z_OK && ret != Z_STREAM_END) {
1021 size =
sizeof(tmp_buf) - zs.avail_out;
1022 if (!(ptr =
av_realloc(*out_data, *out_size + size))) {
1028 memcpy(*out_data + *out_size, tmp_buf, size);
1030 }
while (zs.avail_out == 0);
1042 int64_t in_size, out_size;
1073 if (!memcmp(in_data,
"CWS", 3)) {
1080 memcpy(out_data, in_data, 8);
1084 if ((ret = rtmp_uncompress_swfplayer(in_data + 8, in_size - 8,
1085 &out_data, &out_size)) < 0)
1089 "Zlib is required for decompressing the SWF player file.\n");
1102 "Genuine Adobe Flash Player 001", 30,
1137 int server_pos, client_pos;
1188 serverdata[5], serverdata[6], serverdata[7], serverdata[8]);
1190 if (rt->
is_input && serverdata[5] >= 3) {
1222 0, digest, 32, signature);
1230 tosend + 1, type)) < 0)
1252 tosend + RTMP_HANDSHAKE_PACKET_SIZE - 32);
1259 RTMP_HANDSHAKE_PACKET_SIZE - 32, digest,
1265 RTMP_HANDSHAKE_PACKET_SIZE)) < 0)
1278 tosend + 1, 1)) < 0)
1281 if (serverdata[0] == 9) {
1303 uint32_t *second_int,
char *arraydata,
1314 " not following standard\n", (
int)inoutsize);
1318 *first_int =
AV_RB32(arraydata);
1319 *second_int =
AV_RB32(arraydata + 4);
1324 uint32_t second_int,
char *arraydata,
int size)
1328 AV_WB32(arraydata, first_int);
1329 AV_WB32(arraydata + 4, first_int);
1347 uint32_t hs_my_epoch;
1357 if (inoutsize <= 0) {
1362 if (buffer[0] != 3) {
1368 "Unable to write answer - RTMP S0\n");
1382 hs_my_epoch = hs_epoch;
1408 if (temp != hs_my_epoch)
1410 "Erroneous C2 Message epoch does not match up with C1 epoch\n");
1411 if (memcmp(buffer + 8, hs_s1 + 8,
1414 "Erroneous C2 Message random does not match up\n");
1424 if (pkt->
size < 4) {
1426 "Too short chunk size change packet (%d)\n",
1457 if (pkt->
size < 2) {
1465 if ((ret =
gen_pong(s, rt, pkt)) < 0)
1467 }
else if (t == 26) {
1483 if (pkt->
size < 4) {
1485 "Client bandwidth report packet is less than 4 bytes long (%d)\n",
1507 if (pkt->
size < 4) {
1509 "Too short server bandwidth report packet (%d)\n",
1526 const char *opaque,
const char *challenge)
1554 "?authmod=%s&user=%s&challenge=%s&response=%s",
1555 "adobe", user, challenge2, hashstr);
1558 "&opaque=%s", opaque);
1567 char hashstr1[33], hashstr2[33];
1568 const char *realm =
"live";
1569 const char *method =
"publish";
1570 const char *qop =
"auth";
1571 const char *nc =
"00000001";
1587 hashstr1[32] =
'\0';
1595 hashstr2[32] =
'\0';
1614 "?authmod=%s&user=%s&nonce=%s&cnonce=%s&nc=%s&response=%s",
1615 "llnw", user, nonce, cnonce, nc, hashstr1);
1624 char buf[300], *ptr, authmod[15];
1626 const char *user =
"", *salt =
"", *opaque =
NULL,
1629 if (!(cptr = strstr(desc,
"authmod=adobe")) &&
1630 !(cptr = strstr(desc,
"authmod=llnw"))) {
1632 "Unknown connect error (unsupported authentication method?)\n");
1635 cptr += strlen(
"authmod=");
1636 while (*cptr && *cptr !=
' ' && i <
sizeof(authmod) - 1)
1637 authmod[i++] = *cptr++;
1645 if (strstr(desc,
"?reason=authfailed")) {
1648 }
else if (strstr(desc,
"?reason=nosuchuser")) {
1660 if (strstr(desc,
"code=403 need auth")) {
1662 "?authmod=%s&user=%s", authmod, rt->
username);
1666 if (!(cptr = strstr(desc,
"?reason=needauth"))) {
1675 char *next = strchr(ptr,
'&');
1676 char *value = strchr(ptr,
'=');
1681 if (!strcmp(ptr,
"user")) {
1683 }
else if (!strcmp(ptr,
"salt")) {
1685 }
else if (!strcmp(ptr,
"opaque")) {
1687 }
else if (!strcmp(ptr,
"challenge")) {
1689 }
else if (!strcmp(ptr,
"nonce")) {
1695 if (!strcmp(authmod,
"adobe")) {
1696 if ((ret =
do_adobe_auth(rt, user, salt, challenge, opaque)) < 0)
1711 char *tracked_method =
NULL;
1720 "description", tmpstr,
sizeof(tmpstr))) {
1721 if (tracked_method && (!strcmp(tracked_method,
"_checkbw") ||
1722 !strcmp(tracked_method,
"releaseStream") ||
1723 !strcmp(tracked_method,
"FCSubscribe") ||
1724 !strcmp(tracked_method,
"FCPublish"))) {
1728 }
else if (tracked_method && !strcmp(tracked_method,
"connect")) {
1736 av_log(s, level,
"Server error: %s\n", tmpstr);
1749 char statusmsg[128];
1771 if (!strcmp(command,
"FCPublish") ||
1772 !strcmp(command,
"publish")) {
1774 sizeof(filename), &stringlen);
1780 "Unable to find / in url %s, bad format\n",
1785 if (strcmp(pchar, filename))
1787 " %s\n", filename, pchar);
1792 if (!strcmp(command,
"FCPublish")) {
1801 }
else if (!strcmp(command,
"publish")) {
1811 bytestream2_put_be16(&pbc, 0);
1838 snprintf(statusmsg,
sizeof(statusmsg),
1839 "%s is now published", filename);
1859 if (!strcmp(command,
"createStream")) {
1879 char *tracked_method =
NULL;
1885 if (!tracked_method) {
1890 if (!strcmp(tracked_method,
"connect")) {
1911 }
else if (rt->
live == -1) {
1916 }
else if (!strcmp(tracked_method,
"createStream")) {
1918 if (pkt->
data[10] || pkt->
data[19] != 5 || pkt->
data[20]) {
1948 for (i = 0; i < 2; i++) {
1956 if (!t && !strcmp(tmpstr,
"error")) {
1958 "description", tmpstr,
sizeof(tmpstr)))
1966 if (!t && !strcmp(tmpstr,
"NetStream.Play.UnpublishNotify")) rt->
state =
STATE_STOPPED;
2007 char statusmsg[128];
2014 unsigned datatowritelength;
2021 if (!strcmp(commandbuffer,
"@setDataFrame")) {
2022 datatowrite = gbc.
buffer;
2025 sizeof(statusmsg), &stringlen))
2027 if (strcmp(statusmsg,
"onMetaData")) {
2039 rt->
flv_size += datatowritelength + 15;
2042 rt->
flv_size = datatowritelength + 15;
2052 bytestream2_put_byte(&pbc, pkt->
type);
2053 bytestream2_put_be24(&pbc, datatowritelength);
2054 bytestream2_put_be24(&pbc, ts);
2055 bytestream2_put_byte(&pbc, ts >> 24);
2056 bytestream2_put_be24(&pbc, 0);
2058 bytestream2_put_be32(&pbc, 0);
2077 switch (pkt->
type) {
2079 av_dlog(s,
"received bytes read report\n");
2132 uint32_t ts, cts, pts=0;
2187 bytestream_put_byte(&p, rpkt.
type);
2188 bytestream_put_be24(&p, rpkt.
size);
2189 bytestream_put_be24(&p, ts);
2190 bytestream_put_byte(&p, ts >> 24);
2191 bytestream_put_be24(&p, 0);
2193 bytestream_put_be32(&p, 0);
2212 while (next - rpkt.
data < rpkt.
size - 11) {
2214 size = bytestream_get_be24(&next);
2216 cts = bytestream_get_be24(&next);
2217 cts |= bytestream_get_byte(&next) << 24;
2222 bytestream_put_be24(&p, ts);
2223 bytestream_put_byte(&p, ts >> 24);
2224 next += size + 3 + 4;
2267 char proto[8], hostname[256], path[1024], auth[100], *fname;
2280 hostname,
sizeof(hostname), &port,
2284 char *ptr = strchr(auth,
':');
2292 if (rt->
listen && strcmp(proto,
"rtmp")) {
2297 if (!strcmp(proto,
"rtmpt") || !strcmp(proto,
"rtmpts")) {
2298 if (!strcmp(proto,
"rtmpts"))
2303 }
else if (!strcmp(proto,
"rtmps")) {
2308 }
else if (!strcmp(proto,
"rtmpe") || (!strcmp(proto,
"rtmpte"))) {
2309 if (!strcmp(proto,
"rtmpte"))
2310 av_dict_set(&opts,
"ffrtmpcrypt_tunneling",
"1", 1);
2321 "?listen&listen_timeout=%d",
2359 if (!strncmp(path,
"/ondemand/", 10)) {
2361 memcpy(rt->
app,
"ondemand", 9);
2363 char *next = *path ? path + 1 : path;
2364 char *p = strchr(next,
'/');
2370 char *c = strchr(p + 1,
':');
2371 fname = strchr(p + 1,
'/');
2372 if (!fname || (c && c < fname)) {
2389 int len = strlen(fname);
2397 if (!strchr(fname,
':') && len >= 4 &&
2398 (!strcmp(fname + len - 4,
".f4v") ||
2399 !strcmp(fname + len - 4,
".mp4"))) {
2401 }
else if (len >= 4 && !strcmp(fname + len - 4,
".flv")) {
2402 fname[len - 4] =
'\0';
2416 port,
"/%s", rt->
app);
2453 }
while (ret == EAGAIN);
2493 int orig_size =
size;
2499 if (data_left >= size) {
2504 if (data_left > 0) {
2520 int size_temp =
size;
2521 int pktsize, pkttype;
2523 const uint8_t *buf_temp = buf;
2545 pkttype = bytestream_get_byte(&header);
2546 pktsize = bytestream_get_be24(&header);
2547 ts = bytestream_get_be24(&header);
2548 ts |= bytestream_get_byte(&header) << 24;
2549 bytestream_get_be24(&header);
2562 pkttype, ts, pktsize)) < 0)
2592 }
while (buf_temp - buf < size);
2610 }
else if (ret < 0) {
2612 }
else if (ret == 1) {
2629 #define OFFSET(x) offsetof(RTMPContext, x)
2630 #define DEC AV_OPT_FLAG_DECODING_PARAM
2631 #define ENC AV_OPT_FLAG_ENCODING_PARAM
2635 {
"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},
2638 {
"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},
2639 {
"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"},
2643 {
"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},
2645 {
"rtmp_subscribe",
"Name of live stream to subscribe to. Defaults to rtmp_playpath.",
OFFSET(subscribe),
AV_OPT_TYPE_STRING, {.str =
NULL }, 0, 0,
DEC},
2647 {
"rtmp_swfsize",
"Size of the decompressed SWF file, required for SWFVerification.",
OFFSET(swfsize),
AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX,
DEC},
2651 {
"rtmp_listen",
"Listen for incoming rtmp connections",
OFFSET(listen),
AV_OPT_TYPE_INT, {.i64 = 0}, INT_MIN, INT_MAX,
DEC,
"rtmp_listen" },
2652 {
"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" },
2656 #define RTMP_PROTOCOL(flavor) \
2657 static const AVClass flavor##_class = { \
2658 .class_name = #flavor, \
2659 .item_name = av_default_item_name, \
2660 .option = rtmp_options, \
2661 .version = LIBAVUTIL_VERSION_INT, \
2664 URLProtocol ff_##flavor##_protocol = { \
2666 .url_open = rtmp_open, \
2667 .url_read = rtmp_read, \
2668 .url_write = rtmp_write, \
2669 .url_close = rtmp_close, \
2670 .priv_data_size = sizeof(RTMPContext), \
2671 .flags = URL_PROTOCOL_FLAG_NETWORK, \
2672 .priv_data_class= &flavor##_class, \