OLD | NEW |
1 /* | 1 /* |
2 * RTMP network protocol | 2 * RTMP network protocol |
3 * Copyright (c) 2009 Kostya Shishkov | 3 * Copyright (c) 2009 Kostya Shishkov |
4 * | 4 * |
5 * This file is part of FFmpeg. | 5 * This file is part of FFmpeg. |
6 * | 6 * |
7 * FFmpeg is free software; you can redistribute it and/or | 7 * FFmpeg is free software; you can redistribute it and/or |
8 * modify it under the terms of the GNU Lesser General Public | 8 * modify it under the terms of the GNU Lesser General Public |
9 * License as published by the Free Software Foundation; either | 9 * License as published by the Free Software Foundation; either |
10 * version 2.1 of the License, or (at your option) any later version. | 10 * version 2.1 of the License, or (at your option) any later version. |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
65 int chunk_size; ///< size of the chunks RTMP packe
ts are divided into | 65 int chunk_size; ///< size of the chunks RTMP packe
ts are divided into |
66 int is_input; ///< input/output flag | 66 int is_input; ///< input/output flag |
67 char playpath[256]; ///< path to filename to play (wit
h possible "mp4:" prefix) | 67 char playpath[256]; ///< path to filename to play (wit
h possible "mp4:" prefix) |
68 char app[128]; ///< application | 68 char app[128]; ///< application |
69 ClientState state; ///< current state | 69 ClientState state; ///< current state |
70 int main_channel_id; ///< an additional channel ID whic
h is used for some invocations | 70 int main_channel_id; ///< an additional channel ID whic
h is used for some invocations |
71 uint8_t* flv_data; ///< buffer with data for demuxer | 71 uint8_t* flv_data; ///< buffer with data for demuxer |
72 int flv_size; ///< current buffer size | 72 int flv_size; ///< current buffer size |
73 int flv_off; ///< number of bytes read from cur
rent buffer | 73 int flv_off; ///< number of bytes read from cur
rent buffer |
74 RTMPPacket out_pkt; ///< rtmp packet, created from flv
a/v or metadata (for output) | 74 RTMPPacket out_pkt; ///< rtmp packet, created from flv
a/v or metadata (for output) |
| 75 uint32_t client_report_size; ///< number of bytes after which c
lient should report to server |
| 76 uint32_t bytes_read; ///< number of bytes read from ser
ver |
| 77 uint32_t last_bytes_read; ///< number of bytes read last rep
orted to server |
75 } RTMPContext; | 78 } RTMPContext; |
76 | 79 |
77 #define PLAYER_KEY_OPEN_PART_LEN 30 ///< length of partial key used for first
client digest signing | 80 #define PLAYER_KEY_OPEN_PART_LEN 30 ///< length of partial key used for first
client digest signing |
78 /** Client key used for digest signing */ | 81 /** Client key used for digest signing */ |
79 static const uint8_t rtmp_player_key[] = { | 82 static const uint8_t rtmp_player_key[] = { |
80 'G', 'e', 'n', 'u', 'i', 'n', 'e', ' ', 'A', 'd', 'o', 'b', 'e', ' ', | 83 'G', 'e', 'n', 'u', 'i', 'n', 'e', ' ', 'A', 'd', 'o', 'b', 'e', ' ', |
81 'F', 'l', 'a', 's', 'h', ' ', 'P', 'l', 'a', 'y', 'e', 'r', ' ', '0', '0', '
1', | 84 'F', 'l', 'a', 's', 'h', ' ', 'P', 'l', 'a', 'y', 'e', 'r', ' ', '0', '0', '
1', |
82 | 85 |
83 0xF0, 0xEE, 0xC2, 0x4A, 0x80, 0x68, 0xBE, 0xE8, 0x2E, 0x00, 0xD0, 0xD1, 0x02
, | 86 0xF0, 0xEE, 0xC2, 0x4A, 0x80, 0x68, 0xBE, 0xE8, 0x2E, 0x00, 0xD0, 0xD1, 0x02
, |
84 0x9E, 0x7E, 0x57, 0x6E, 0xEC, 0x5D, 0x2D, 0x29, 0x80, 0x6F, 0xAB, 0x93, 0xB8
, | 87 0x9E, 0x7E, 0x57, 0x6E, 0xEC, 0x5D, 0x2D, 0x29, 0x80, 0x6F, 0xAB, 0x93, 0xB8
, |
(...skipping 18 matching lines...) Expand all Loading... |
103 static void gen_connect(URLContext *s, RTMPContext *rt, const char *proto, | 106 static void gen_connect(URLContext *s, RTMPContext *rt, const char *proto, |
104 const char *host, int port) | 107 const char *host, int port) |
105 { | 108 { |
106 RTMPPacket pkt; | 109 RTMPPacket pkt; |
107 uint8_t ver[64], *p; | 110 uint8_t ver[64], *p; |
108 char tcurl[512]; | 111 char tcurl[512]; |
109 | 112 |
110 ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE, 0, 4096); | 113 ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE, 0, 4096); |
111 p = pkt.data; | 114 p = pkt.data; |
112 | 115 |
113 snprintf(tcurl, sizeof(tcurl), "%s://%s:%d/%s", proto, host, port, rt->app); | 116 ff_url_join(tcurl, sizeof(tcurl), proto, NULL, host, port, "/%s", rt->app); |
114 ff_amf_write_string(&p, "connect"); | 117 ff_amf_write_string(&p, "connect"); |
115 ff_amf_write_number(&p, 1.0); | 118 ff_amf_write_number(&p, 1.0); |
116 ff_amf_write_object_start(&p); | 119 ff_amf_write_object_start(&p); |
117 ff_amf_write_field_name(&p, "app"); | 120 ff_amf_write_field_name(&p, "app"); |
118 ff_amf_write_string(&p, rt->app); | 121 ff_amf_write_string(&p, rt->app); |
119 | 122 |
120 if (rt->is_input) { | 123 if (rt->is_input) { |
121 snprintf(ver, sizeof(ver), "%s %d,%d,%d,%d", RTMP_CLIENT_PLATFORM, RTMP_
CLIENT_VER1, | 124 snprintf(ver, sizeof(ver), "%s %d,%d,%d,%d", RTMP_CLIENT_PLATFORM, RTMP_
CLIENT_VER1, |
122 RTMP_CLIENT_VER2, RTMP_CLIENT_VER3, RTMP_CLIENT_VER4); | 125 RTMP_CLIENT_VER2, RTMP_CLIENT_VER3, RTMP_CLIENT_VER4); |
123 } else { | 126 } else { |
(...skipping 206 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
330 uint8_t *p; | 333 uint8_t *p; |
331 | 334 |
332 ff_rtmp_packet_create(&pkt, RTMP_NETWORK_CHANNEL, RTMP_PT_PING, ppkt->timest
amp + 1, 6); | 335 ff_rtmp_packet_create(&pkt, RTMP_NETWORK_CHANNEL, RTMP_PT_PING, ppkt->timest
amp + 1, 6); |
333 p = pkt.data; | 336 p = pkt.data; |
334 bytestream_put_be16(&p, 7); | 337 bytestream_put_be16(&p, 7); |
335 bytestream_put_be32(&p, AV_RB32(ppkt->data+2)); | 338 bytestream_put_be32(&p, AV_RB32(ppkt->data+2)); |
336 ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size, rt->prev_pkt[1]); | 339 ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size, rt->prev_pkt[1]); |
337 ff_rtmp_packet_destroy(&pkt); | 340 ff_rtmp_packet_destroy(&pkt); |
338 } | 341 } |
339 | 342 |
| 343 /** |
| 344 * Generates report on bytes read so far and sends it to the server. |
| 345 */ |
| 346 static void gen_bytes_read(URLContext *s, RTMPContext *rt, uint32_t ts) |
| 347 { |
| 348 RTMPPacket pkt; |
| 349 uint8_t *p; |
| 350 |
| 351 ff_rtmp_packet_create(&pkt, RTMP_NETWORK_CHANNEL, RTMP_PT_BYTES_READ, ts, 4)
; |
| 352 p = pkt.data; |
| 353 bytestream_put_be32(&p, rt->bytes_read); |
| 354 ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size, rt->prev_pkt[1]); |
| 355 ff_rtmp_packet_destroy(&pkt); |
| 356 } |
| 357 |
340 //TODO: Move HMAC code somewhere. Eventually. | 358 //TODO: Move HMAC code somewhere. Eventually. |
341 #define HMAC_IPAD_VAL 0x36 | 359 #define HMAC_IPAD_VAL 0x36 |
342 #define HMAC_OPAD_VAL 0x5C | 360 #define HMAC_OPAD_VAL 0x5C |
343 | 361 |
344 /** | 362 /** |
345 * Calculates HMAC-SHA2 digest for RTMP handshake packets. | 363 * Calculates HMAC-SHA2 digest for RTMP handshake packets. |
346 * | 364 * |
347 * @param src input buffer | 365 * @param src input buffer |
348 * @param len input buffer length (should be 1536) | 366 * @param len input buffer length (should be 1536) |
349 * @param gap offset in buffer where 32 bytes should not be taken into accoun
t | 367 * @param gap offset in buffer where 32 bytes should not be taken into accoun
t |
(...skipping 199 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
549 av_log(LOG_CONTEXT, AV_LOG_ERROR, "Incorrect chunk size %d\n", rt->c
hunk_size); | 567 av_log(LOG_CONTEXT, AV_LOG_ERROR, "Incorrect chunk size %d\n", rt->c
hunk_size); |
550 return -1; | 568 return -1; |
551 } | 569 } |
552 av_log(LOG_CONTEXT, AV_LOG_DEBUG, "New chunk size = %d\n", rt->chunk_siz
e); | 570 av_log(LOG_CONTEXT, AV_LOG_DEBUG, "New chunk size = %d\n", rt->chunk_siz
e); |
553 break; | 571 break; |
554 case RTMP_PT_PING: | 572 case RTMP_PT_PING: |
555 t = AV_RB16(pkt->data); | 573 t = AV_RB16(pkt->data); |
556 if (t == 6) | 574 if (t == 6) |
557 gen_pong(s, rt, pkt); | 575 gen_pong(s, rt, pkt); |
558 break; | 576 break; |
| 577 case RTMP_PT_CLIENT_BW: |
| 578 if (pkt->data_size < 4) { |
| 579 av_log(LOG_CONTEXT, AV_LOG_ERROR, |
| 580 "Client bandwidth report packet is less than 4 bytes long (%d
)\n", |
| 581 pkt->data_size); |
| 582 return -1; |
| 583 } |
| 584 av_log(LOG_CONTEXT, AV_LOG_DEBUG, "Client bandwidth = %d\n", AV_RB32(pkt
->data)); |
| 585 rt->client_report_size = AV_RB32(pkt->data) >> 1; |
| 586 break; |
559 case RTMP_PT_INVOKE: | 587 case RTMP_PT_INVOKE: |
560 //TODO: check for the messages sent for wrong state? | 588 //TODO: check for the messages sent for wrong state? |
561 if (!memcmp(pkt->data, "\002\000\006_error", 9)) { | 589 if (!memcmp(pkt->data, "\002\000\006_error", 9)) { |
562 uint8_t tmpstr[256]; | 590 uint8_t tmpstr[256]; |
563 | 591 |
564 if (!ff_amf_get_field_value(pkt->data + 9, data_end, | 592 if (!ff_amf_get_field_value(pkt->data + 9, data_end, |
565 "description", tmpstr, sizeof(tmpstr))) | 593 "description", tmpstr, sizeof(tmpstr))) |
566 av_log(LOG_CONTEXT, AV_LOG_ERROR, "Server error: %s\n",tmpstr); | 594 av_log(LOG_CONTEXT, AV_LOG_ERROR, "Server error: %s\n",tmpstr); |
567 return -1; | 595 return -1; |
568 } else if (!memcmp(pkt->data, "\002\000\007_result", 10)) { | 596 } else if (!memcmp(pkt->data, "\002\000\007_result", 10)) { |
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
662 for (;;) { | 690 for (;;) { |
663 RTMPPacket rpkt; | 691 RTMPPacket rpkt; |
664 if ((ret = ff_rtmp_packet_read(rt->stream, &rpkt, | 692 if ((ret = ff_rtmp_packet_read(rt->stream, &rpkt, |
665 rt->chunk_size, rt->prev_pkt[0])) <= 0) { | 693 rt->chunk_size, rt->prev_pkt[0])) <= 0) { |
666 if (ret == 0) { | 694 if (ret == 0) { |
667 return AVERROR(EAGAIN); | 695 return AVERROR(EAGAIN); |
668 } else { | 696 } else { |
669 return AVERROR(EIO); | 697 return AVERROR(EIO); |
670 } | 698 } |
671 } | 699 } |
| 700 rt->bytes_read += ret; |
| 701 if (rt->bytes_read > rt->last_bytes_read + rt->client_report_size) { |
| 702 av_log(LOG_CONTEXT, AV_LOG_DEBUG, "Sending bytes read report\n"); |
| 703 gen_bytes_read(s, rt, rpkt.timestamp + 1); |
| 704 rt->last_bytes_read = rt->bytes_read; |
| 705 } |
672 | 706 |
673 ret = rtmp_parse_result(s, rt, &rpkt); | 707 ret = rtmp_parse_result(s, rt, &rpkt); |
674 if (ret < 0) {//serious error in current packet | 708 if (ret < 0) {//serious error in current packet |
675 ff_rtmp_packet_destroy(&rpkt); | 709 ff_rtmp_packet_destroy(&rpkt); |
676 return -1; | 710 return -1; |
677 } | 711 } |
678 if (rt->state == STATE_STOPPED) { | 712 if (rt->state == STATE_STOPPED) { |
679 ff_rtmp_packet_destroy(&rpkt); | 713 ff_rtmp_packet_destroy(&rpkt); |
680 return AVERROR_EOF; | 714 return AVERROR_EOF; |
681 } | 715 } |
(...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
771 uint8_t buf[2048]; | 805 uint8_t buf[2048]; |
772 int port; | 806 int port; |
773 int ret; | 807 int ret; |
774 | 808 |
775 rt = av_mallocz(sizeof(RTMPContext)); | 809 rt = av_mallocz(sizeof(RTMPContext)); |
776 if (!rt) | 810 if (!rt) |
777 return AVERROR(ENOMEM); | 811 return AVERROR(ENOMEM); |
778 s->priv_data = rt; | 812 s->priv_data = rt; |
779 rt->is_input = !(flags & URL_WRONLY); | 813 rt->is_input = !(flags & URL_WRONLY); |
780 | 814 |
781 url_split(proto, sizeof(proto), NULL, 0, hostname, sizeof(hostname), &port, | 815 ff_url_split(proto, sizeof(proto), NULL, 0, hostname, sizeof(hostname), &por
t, |
782 path, sizeof(path), s->filename); | 816 path, sizeof(path), s->filename); |
783 | 817 |
784 if (port < 0) | 818 if (port < 0) |
785 port = RTMP_DEFAULT_PORT; | 819 port = RTMP_DEFAULT_PORT; |
786 snprintf(buf, sizeof(buf), "tcp://%s:%d", hostname, port); | 820 ff_url_join(buf, sizeof(buf), "tcp", NULL, hostname, port, NULL); |
787 | 821 |
788 if (url_open(&rt->stream, buf, URL_RDWR) < 0) { | 822 if (url_open(&rt->stream, buf, URL_RDWR) < 0) { |
789 av_log(LOG_CONTEXT, AV_LOG_ERROR, "Cannot open connection %s\n", buf); | 823 av_log(LOG_CONTEXT, AV_LOG_ERROR, "Cannot open connection %s\n", buf); |
790 goto fail; | 824 goto fail; |
791 } | 825 } |
792 | 826 |
793 rt->state = STATE_START; | 827 rt->state = STATE_START; |
794 if (rtmp_handshake(s, rt)) | 828 if (rtmp_handshake(s, rt)) |
795 return -1; | 829 return -1; |
796 | 830 |
(...skipping 22 matching lines...) Expand all Loading... |
819 } | 853 } |
820 if (!strchr(fname, ':') && | 854 if (!strchr(fname, ':') && |
821 (!strcmp(fname + strlen(fname) - 4, ".f4v") || | 855 (!strcmp(fname + strlen(fname) - 4, ".f4v") || |
822 !strcmp(fname + strlen(fname) - 4, ".mp4"))) { | 856 !strcmp(fname + strlen(fname) - 4, ".mp4"))) { |
823 memcpy(rt->playpath, "mp4:", 5); | 857 memcpy(rt->playpath, "mp4:", 5); |
824 } else { | 858 } else { |
825 rt->playpath[0] = 0; | 859 rt->playpath[0] = 0; |
826 } | 860 } |
827 strncat(rt->playpath, fname, sizeof(rt->playpath) - 5); | 861 strncat(rt->playpath, fname, sizeof(rt->playpath) - 5); |
828 | 862 |
| 863 rt->client_report_size = 1048576; |
| 864 rt->bytes_read = 0; |
| 865 rt->last_bytes_read = 0; |
| 866 |
829 av_log(LOG_CONTEXT, AV_LOG_DEBUG, "Proto = %s, path = %s, app = %s, fname =
%s\n", | 867 av_log(LOG_CONTEXT, AV_LOG_DEBUG, "Proto = %s, path = %s, app = %s, fname =
%s\n", |
830 proto, path, rt->app, rt->playpath); | 868 proto, path, rt->app, rt->playpath); |
831 gen_connect(s, rt, proto, hostname, port); | 869 gen_connect(s, rt, proto, hostname, port); |
832 | 870 |
833 do { | 871 do { |
834 ret = get_packet(s, 1); | 872 ret = get_packet(s, 1); |
835 } while (ret == EAGAIN); | 873 } while (ret == EAGAIN); |
836 if (ret < 0) | 874 if (ret < 0) |
837 goto fail; | 875 goto fail; |
838 | 876 |
(...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
950 } | 988 } |
951 | 989 |
952 URLProtocol rtmp_protocol = { | 990 URLProtocol rtmp_protocol = { |
953 "rtmp", | 991 "rtmp", |
954 rtmp_open, | 992 rtmp_open, |
955 rtmp_read, | 993 rtmp_read, |
956 rtmp_write, | 994 rtmp_write, |
957 NULL, /* seek */ | 995 NULL, /* seek */ |
958 rtmp_close, | 996 rtmp_close, |
959 }; | 997 }; |
OLD | NEW |