Libav 0.7.1
|
00001 /* 00002 * RTMP network protocol 00003 * Copyright (c) 2009 Kostya Shishkov 00004 * 00005 * This file is part of Libav. 00006 * 00007 * Libav is free software; you can redistribute it and/or 00008 * modify it under the terms of the GNU Lesser General Public 00009 * License as published by the Free Software Foundation; either 00010 * version 2.1 of the License, or (at your option) any later version. 00011 * 00012 * Libav is distributed in the hope that it will be useful, 00013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00015 * Lesser General Public License for more details. 00016 * 00017 * You should have received a copy of the GNU Lesser General Public 00018 * License along with Libav; if not, write to the Free Software 00019 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 00020 */ 00021 00027 #include "libavcodec/bytestream.h" 00028 #include "libavutil/avstring.h" 00029 #include "libavutil/lfg.h" 00030 #include "libavutil/sha.h" 00031 #include "avformat.h" 00032 #include "internal.h" 00033 00034 #include "network.h" 00035 00036 #include "flv.h" 00037 #include "rtmp.h" 00038 #include "rtmppkt.h" 00039 #include "url.h" 00040 00041 //#define DEBUG 00042 00044 typedef enum { 00045 STATE_START, 00046 STATE_HANDSHAKED, 00047 STATE_RELEASING, 00048 STATE_FCPUBLISH, 00049 STATE_CONNECTING, 00050 STATE_READY, 00051 STATE_PLAYING, 00052 STATE_PUBLISHING, 00053 STATE_STOPPED, 00054 } ClientState; 00055 00057 typedef struct RTMPContext { 00058 URLContext* stream; 00059 RTMPPacket prev_pkt[2][RTMP_CHANNELS]; 00060 int chunk_size; 00061 int is_input; 00062 char playpath[256]; 00063 char app[128]; 00064 ClientState state; 00065 int main_channel_id; 00066 uint8_t* flv_data; 00067 int flv_size; 00068 int flv_off; 00069 RTMPPacket out_pkt; 00070 uint32_t client_report_size; 00071 uint32_t bytes_read; 00072 uint32_t last_bytes_read; 00073 } RTMPContext; 00074 00075 #define PLAYER_KEY_OPEN_PART_LEN 30 ///< length of partial key used for first client digest signing 00076 00077 static const uint8_t rtmp_player_key[] = { 00078 'G', 'e', 'n', 'u', 'i', 'n', 'e', ' ', 'A', 'd', 'o', 'b', 'e', ' ', 00079 'F', 'l', 'a', 's', 'h', ' ', 'P', 'l', 'a', 'y', 'e', 'r', ' ', '0', '0', '1', 00080 00081 0xF0, 0xEE, 0xC2, 0x4A, 0x80, 0x68, 0xBE, 0xE8, 0x2E, 0x00, 0xD0, 0xD1, 0x02, 00082 0x9E, 0x7E, 0x57, 0x6E, 0xEC, 0x5D, 0x2D, 0x29, 0x80, 0x6F, 0xAB, 0x93, 0xB8, 00083 0xE6, 0x36, 0xCF, 0xEB, 0x31, 0xAE 00084 }; 00085 00086 #define SERVER_KEY_OPEN_PART_LEN 36 ///< length of partial key used for first server digest signing 00087 00088 static const uint8_t rtmp_server_key[] = { 00089 'G', 'e', 'n', 'u', 'i', 'n', 'e', ' ', 'A', 'd', 'o', 'b', 'e', ' ', 00090 'F', 'l', 'a', 's', 'h', ' ', 'M', 'e', 'd', 'i', 'a', ' ', 00091 'S', 'e', 'r', 'v', 'e', 'r', ' ', '0', '0', '1', 00092 00093 0xF0, 0xEE, 0xC2, 0x4A, 0x80, 0x68, 0xBE, 0xE8, 0x2E, 0x00, 0xD0, 0xD1, 0x02, 00094 0x9E, 0x7E, 0x57, 0x6E, 0xEC, 0x5D, 0x2D, 0x29, 0x80, 0x6F, 0xAB, 0x93, 0xB8, 00095 0xE6, 0x36, 0xCF, 0xEB, 0x31, 0xAE 00096 }; 00097 00101 static void gen_connect(URLContext *s, RTMPContext *rt, const char *proto, 00102 const char *host, int port) 00103 { 00104 RTMPPacket pkt; 00105 uint8_t ver[64], *p; 00106 char tcurl[512]; 00107 00108 ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE, 0, 4096); 00109 p = pkt.data; 00110 00111 ff_url_join(tcurl, sizeof(tcurl), proto, NULL, host, port, "/%s", rt->app); 00112 ff_amf_write_string(&p, "connect"); 00113 ff_amf_write_number(&p, 1.0); 00114 ff_amf_write_object_start(&p); 00115 ff_amf_write_field_name(&p, "app"); 00116 ff_amf_write_string(&p, rt->app); 00117 00118 if (rt->is_input) { 00119 snprintf(ver, sizeof(ver), "%s %d,%d,%d,%d", RTMP_CLIENT_PLATFORM, RTMP_CLIENT_VER1, 00120 RTMP_CLIENT_VER2, RTMP_CLIENT_VER3, RTMP_CLIENT_VER4); 00121 } else { 00122 snprintf(ver, sizeof(ver), "FMLE/3.0 (compatible; %s)", LIBAVFORMAT_IDENT); 00123 ff_amf_write_field_name(&p, "type"); 00124 ff_amf_write_string(&p, "nonprivate"); 00125 } 00126 ff_amf_write_field_name(&p, "flashVer"); 00127 ff_amf_write_string(&p, ver); 00128 ff_amf_write_field_name(&p, "tcUrl"); 00129 ff_amf_write_string(&p, tcurl); 00130 if (rt->is_input) { 00131 ff_amf_write_field_name(&p, "fpad"); 00132 ff_amf_write_bool(&p, 0); 00133 ff_amf_write_field_name(&p, "capabilities"); 00134 ff_amf_write_number(&p, 15.0); 00135 ff_amf_write_field_name(&p, "audioCodecs"); 00136 ff_amf_write_number(&p, 1639.0); 00137 ff_amf_write_field_name(&p, "videoCodecs"); 00138 ff_amf_write_number(&p, 252.0); 00139 ff_amf_write_field_name(&p, "videoFunction"); 00140 ff_amf_write_number(&p, 1.0); 00141 } 00142 ff_amf_write_object_end(&p); 00143 00144 pkt.data_size = p - pkt.data; 00145 00146 ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size, rt->prev_pkt[1]); 00147 ff_rtmp_packet_destroy(&pkt); 00148 } 00149 00154 static void gen_release_stream(URLContext *s, RTMPContext *rt) 00155 { 00156 RTMPPacket pkt; 00157 uint8_t *p; 00158 00159 ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE, 0, 00160 29 + strlen(rt->playpath)); 00161 00162 av_log(s, AV_LOG_DEBUG, "Releasing stream...\n"); 00163 p = pkt.data; 00164 ff_amf_write_string(&p, "releaseStream"); 00165 ff_amf_write_number(&p, 2.0); 00166 ff_amf_write_null(&p); 00167 ff_amf_write_string(&p, rt->playpath); 00168 00169 ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size, rt->prev_pkt[1]); 00170 ff_rtmp_packet_destroy(&pkt); 00171 } 00172 00177 static void gen_fcpublish_stream(URLContext *s, RTMPContext *rt) 00178 { 00179 RTMPPacket pkt; 00180 uint8_t *p; 00181 00182 ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE, 0, 00183 25 + strlen(rt->playpath)); 00184 00185 av_log(s, AV_LOG_DEBUG, "FCPublish stream...\n"); 00186 p = pkt.data; 00187 ff_amf_write_string(&p, "FCPublish"); 00188 ff_amf_write_number(&p, 3.0); 00189 ff_amf_write_null(&p); 00190 ff_amf_write_string(&p, rt->playpath); 00191 00192 ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size, rt->prev_pkt[1]); 00193 ff_rtmp_packet_destroy(&pkt); 00194 } 00195 00200 static void gen_fcunpublish_stream(URLContext *s, RTMPContext *rt) 00201 { 00202 RTMPPacket pkt; 00203 uint8_t *p; 00204 00205 ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE, 0, 00206 27 + strlen(rt->playpath)); 00207 00208 av_log(s, AV_LOG_DEBUG, "UnPublishing stream...\n"); 00209 p = pkt.data; 00210 ff_amf_write_string(&p, "FCUnpublish"); 00211 ff_amf_write_number(&p, 5.0); 00212 ff_amf_write_null(&p); 00213 ff_amf_write_string(&p, rt->playpath); 00214 00215 ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size, rt->prev_pkt[1]); 00216 ff_rtmp_packet_destroy(&pkt); 00217 } 00218 00223 static void gen_create_stream(URLContext *s, RTMPContext *rt) 00224 { 00225 RTMPPacket pkt; 00226 uint8_t *p; 00227 00228 av_log(s, AV_LOG_DEBUG, "Creating stream...\n"); 00229 ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE, 0, 25); 00230 00231 p = pkt.data; 00232 ff_amf_write_string(&p, "createStream"); 00233 ff_amf_write_number(&p, rt->is_input ? 3.0 : 4.0); 00234 ff_amf_write_null(&p); 00235 00236 ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size, rt->prev_pkt[1]); 00237 ff_rtmp_packet_destroy(&pkt); 00238 } 00239 00240 00245 static void gen_delete_stream(URLContext *s, RTMPContext *rt) 00246 { 00247 RTMPPacket pkt; 00248 uint8_t *p; 00249 00250 av_log(s, AV_LOG_DEBUG, "Deleting stream...\n"); 00251 ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE, 0, 34); 00252 00253 p = pkt.data; 00254 ff_amf_write_string(&p, "deleteStream"); 00255 ff_amf_write_number(&p, 0.0); 00256 ff_amf_write_null(&p); 00257 ff_amf_write_number(&p, rt->main_channel_id); 00258 00259 ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size, rt->prev_pkt[1]); 00260 ff_rtmp_packet_destroy(&pkt); 00261 } 00262 00267 static void gen_play(URLContext *s, RTMPContext *rt) 00268 { 00269 RTMPPacket pkt; 00270 uint8_t *p; 00271 00272 av_log(s, AV_LOG_DEBUG, "Sending play command for '%s'\n", rt->playpath); 00273 ff_rtmp_packet_create(&pkt, RTMP_VIDEO_CHANNEL, RTMP_PT_INVOKE, 0, 00274 20 + strlen(rt->playpath)); 00275 pkt.extra = rt->main_channel_id; 00276 00277 p = pkt.data; 00278 ff_amf_write_string(&p, "play"); 00279 ff_amf_write_number(&p, 0.0); 00280 ff_amf_write_null(&p); 00281 ff_amf_write_string(&p, rt->playpath); 00282 00283 ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size, rt->prev_pkt[1]); 00284 ff_rtmp_packet_destroy(&pkt); 00285 00286 // set client buffer time disguised in ping packet 00287 ff_rtmp_packet_create(&pkt, RTMP_NETWORK_CHANNEL, RTMP_PT_PING, 1, 10); 00288 00289 p = pkt.data; 00290 bytestream_put_be16(&p, 3); 00291 bytestream_put_be32(&p, 1); 00292 bytestream_put_be32(&p, 256); //TODO: what is a good value here? 00293 00294 ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size, rt->prev_pkt[1]); 00295 ff_rtmp_packet_destroy(&pkt); 00296 } 00297 00301 static void gen_publish(URLContext *s, RTMPContext *rt) 00302 { 00303 RTMPPacket pkt; 00304 uint8_t *p; 00305 00306 av_log(s, AV_LOG_DEBUG, "Sending publish command for '%s'\n", rt->playpath); 00307 ff_rtmp_packet_create(&pkt, RTMP_SOURCE_CHANNEL, RTMP_PT_INVOKE, 0, 00308 30 + strlen(rt->playpath)); 00309 pkt.extra = rt->main_channel_id; 00310 00311 p = pkt.data; 00312 ff_amf_write_string(&p, "publish"); 00313 ff_amf_write_number(&p, 0.0); 00314 ff_amf_write_null(&p); 00315 ff_amf_write_string(&p, rt->playpath); 00316 ff_amf_write_string(&p, "live"); 00317 00318 ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size, rt->prev_pkt[1]); 00319 ff_rtmp_packet_destroy(&pkt); 00320 } 00321 00325 static void gen_pong(URLContext *s, RTMPContext *rt, RTMPPacket *ppkt) 00326 { 00327 RTMPPacket pkt; 00328 uint8_t *p; 00329 00330 ff_rtmp_packet_create(&pkt, RTMP_NETWORK_CHANNEL, RTMP_PT_PING, ppkt->timestamp + 1, 6); 00331 p = pkt.data; 00332 bytestream_put_be16(&p, 7); 00333 bytestream_put_be32(&p, AV_RB32(ppkt->data+2)); 00334 ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size, rt->prev_pkt[1]); 00335 ff_rtmp_packet_destroy(&pkt); 00336 } 00337 00341 static void gen_bytes_read(URLContext *s, RTMPContext *rt, uint32_t ts) 00342 { 00343 RTMPPacket pkt; 00344 uint8_t *p; 00345 00346 ff_rtmp_packet_create(&pkt, RTMP_NETWORK_CHANNEL, RTMP_PT_BYTES_READ, ts, 4); 00347 p = pkt.data; 00348 bytestream_put_be32(&p, rt->bytes_read); 00349 ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size, rt->prev_pkt[1]); 00350 ff_rtmp_packet_destroy(&pkt); 00351 } 00352 00353 //TODO: Move HMAC code somewhere. Eventually. 00354 #define HMAC_IPAD_VAL 0x36 00355 #define HMAC_OPAD_VAL 0x5C 00356 00368 static void rtmp_calc_digest(const uint8_t *src, int len, int gap, 00369 const uint8_t *key, int keylen, uint8_t *dst) 00370 { 00371 struct AVSHA *sha; 00372 uint8_t hmac_buf[64+32] = {0}; 00373 int i; 00374 00375 sha = av_mallocz(av_sha_size); 00376 00377 if (keylen < 64) { 00378 memcpy(hmac_buf, key, keylen); 00379 } else { 00380 av_sha_init(sha, 256); 00381 av_sha_update(sha,key, keylen); 00382 av_sha_final(sha, hmac_buf); 00383 } 00384 for (i = 0; i < 64; i++) 00385 hmac_buf[i] ^= HMAC_IPAD_VAL; 00386 00387 av_sha_init(sha, 256); 00388 av_sha_update(sha, hmac_buf, 64); 00389 if (gap <= 0) { 00390 av_sha_update(sha, src, len); 00391 } else { //skip 32 bytes used for storing digest 00392 av_sha_update(sha, src, gap); 00393 av_sha_update(sha, src + gap + 32, len - gap - 32); 00394 } 00395 av_sha_final(sha, hmac_buf + 64); 00396 00397 for (i = 0; i < 64; i++) 00398 hmac_buf[i] ^= HMAC_IPAD_VAL ^ HMAC_OPAD_VAL; //reuse XORed key for opad 00399 av_sha_init(sha, 256); 00400 av_sha_update(sha, hmac_buf, 64+32); 00401 av_sha_final(sha, dst); 00402 00403 av_free(sha); 00404 } 00405 00413 static int rtmp_handshake_imprint_with_digest(uint8_t *buf) 00414 { 00415 int i, digest_pos = 0; 00416 00417 for (i = 8; i < 12; i++) 00418 digest_pos += buf[i]; 00419 digest_pos = (digest_pos % 728) + 12; 00420 00421 rtmp_calc_digest(buf, RTMP_HANDSHAKE_PACKET_SIZE, digest_pos, 00422 rtmp_player_key, PLAYER_KEY_OPEN_PART_LEN, 00423 buf + digest_pos); 00424 return digest_pos; 00425 } 00426 00434 static int rtmp_validate_digest(uint8_t *buf, int off) 00435 { 00436 int i, digest_pos = 0; 00437 uint8_t digest[32]; 00438 00439 for (i = 0; i < 4; i++) 00440 digest_pos += buf[i + off]; 00441 digest_pos = (digest_pos % 728) + off + 4; 00442 00443 rtmp_calc_digest(buf, RTMP_HANDSHAKE_PACKET_SIZE, digest_pos, 00444 rtmp_server_key, SERVER_KEY_OPEN_PART_LEN, 00445 digest); 00446 if (!memcmp(digest, buf + digest_pos, 32)) 00447 return digest_pos; 00448 return 0; 00449 } 00450 00457 static int rtmp_handshake(URLContext *s, RTMPContext *rt) 00458 { 00459 AVLFG rnd; 00460 uint8_t tosend [RTMP_HANDSHAKE_PACKET_SIZE+1] = { 00461 3, // unencrypted data 00462 0, 0, 0, 0, // client uptime 00463 RTMP_CLIENT_VER1, 00464 RTMP_CLIENT_VER2, 00465 RTMP_CLIENT_VER3, 00466 RTMP_CLIENT_VER4, 00467 }; 00468 uint8_t clientdata[RTMP_HANDSHAKE_PACKET_SIZE]; 00469 uint8_t serverdata[RTMP_HANDSHAKE_PACKET_SIZE+1]; 00470 int i; 00471 int server_pos, client_pos; 00472 uint8_t digest[32]; 00473 00474 av_log(s, AV_LOG_DEBUG, "Handshaking...\n"); 00475 00476 av_lfg_init(&rnd, 0xDEADC0DE); 00477 // generate handshake packet - 1536 bytes of pseudorandom data 00478 for (i = 9; i <= RTMP_HANDSHAKE_PACKET_SIZE; i++) 00479 tosend[i] = av_lfg_get(&rnd) >> 24; 00480 client_pos = rtmp_handshake_imprint_with_digest(tosend + 1); 00481 00482 ffurl_write(rt->stream, tosend, RTMP_HANDSHAKE_PACKET_SIZE + 1); 00483 i = ffurl_read_complete(rt->stream, serverdata, RTMP_HANDSHAKE_PACKET_SIZE + 1); 00484 if (i != RTMP_HANDSHAKE_PACKET_SIZE + 1) { 00485 av_log(s, AV_LOG_ERROR, "Cannot read RTMP handshake response\n"); 00486 return -1; 00487 } 00488 i = ffurl_read_complete(rt->stream, clientdata, RTMP_HANDSHAKE_PACKET_SIZE); 00489 if (i != RTMP_HANDSHAKE_PACKET_SIZE) { 00490 av_log(s, AV_LOG_ERROR, "Cannot read RTMP handshake response\n"); 00491 return -1; 00492 } 00493 00494 av_log(s, AV_LOG_DEBUG, "Server version %d.%d.%d.%d\n", 00495 serverdata[5], serverdata[6], serverdata[7], serverdata[8]); 00496 00497 if (rt->is_input && serverdata[5] >= 3) { 00498 server_pos = rtmp_validate_digest(serverdata + 1, 772); 00499 if (!server_pos) { 00500 server_pos = rtmp_validate_digest(serverdata + 1, 8); 00501 if (!server_pos) { 00502 av_log(s, AV_LOG_ERROR, "Server response validating failed\n"); 00503 return -1; 00504 } 00505 } 00506 00507 rtmp_calc_digest(tosend + 1 + client_pos, 32, 0, 00508 rtmp_server_key, sizeof(rtmp_server_key), 00509 digest); 00510 rtmp_calc_digest(clientdata, RTMP_HANDSHAKE_PACKET_SIZE-32, 0, 00511 digest, 32, 00512 digest); 00513 if (memcmp(digest, clientdata + RTMP_HANDSHAKE_PACKET_SIZE - 32, 32)) { 00514 av_log(s, AV_LOG_ERROR, "Signature mismatch\n"); 00515 return -1; 00516 } 00517 00518 for (i = 0; i < RTMP_HANDSHAKE_PACKET_SIZE; i++) 00519 tosend[i] = av_lfg_get(&rnd) >> 24; 00520 rtmp_calc_digest(serverdata + 1 + server_pos, 32, 0, 00521 rtmp_player_key, sizeof(rtmp_player_key), 00522 digest); 00523 rtmp_calc_digest(tosend, RTMP_HANDSHAKE_PACKET_SIZE - 32, 0, 00524 digest, 32, 00525 tosend + RTMP_HANDSHAKE_PACKET_SIZE - 32); 00526 00527 // write reply back to the server 00528 ffurl_write(rt->stream, tosend, RTMP_HANDSHAKE_PACKET_SIZE); 00529 } else { 00530 ffurl_write(rt->stream, serverdata+1, RTMP_HANDSHAKE_PACKET_SIZE); 00531 } 00532 00533 return 0; 00534 } 00535 00542 static int rtmp_parse_result(URLContext *s, RTMPContext *rt, RTMPPacket *pkt) 00543 { 00544 int i, t; 00545 const uint8_t *data_end = pkt->data + pkt->data_size; 00546 00547 #ifdef DEBUG 00548 ff_rtmp_packet_dump(s, pkt); 00549 #endif 00550 00551 switch (pkt->type) { 00552 case RTMP_PT_CHUNK_SIZE: 00553 if (pkt->data_size != 4) { 00554 av_log(s, AV_LOG_ERROR, 00555 "Chunk size change packet is not 4 bytes long (%d)\n", pkt->data_size); 00556 return -1; 00557 } 00558 if (!rt->is_input) 00559 ff_rtmp_packet_write(rt->stream, pkt, rt->chunk_size, rt->prev_pkt[1]); 00560 rt->chunk_size = AV_RB32(pkt->data); 00561 if (rt->chunk_size <= 0) { 00562 av_log(s, AV_LOG_ERROR, "Incorrect chunk size %d\n", rt->chunk_size); 00563 return -1; 00564 } 00565 av_log(s, AV_LOG_DEBUG, "New chunk size = %d\n", rt->chunk_size); 00566 break; 00567 case RTMP_PT_PING: 00568 t = AV_RB16(pkt->data); 00569 if (t == 6) 00570 gen_pong(s, rt, pkt); 00571 break; 00572 case RTMP_PT_CLIENT_BW: 00573 if (pkt->data_size < 4) { 00574 av_log(s, AV_LOG_ERROR, 00575 "Client bandwidth report packet is less than 4 bytes long (%d)\n", 00576 pkt->data_size); 00577 return -1; 00578 } 00579 av_log(s, AV_LOG_DEBUG, "Client bandwidth = %d\n", AV_RB32(pkt->data)); 00580 rt->client_report_size = AV_RB32(pkt->data) >> 1; 00581 break; 00582 case RTMP_PT_INVOKE: 00583 //TODO: check for the messages sent for wrong state? 00584 if (!memcmp(pkt->data, "\002\000\006_error", 9)) { 00585 uint8_t tmpstr[256]; 00586 00587 if (!ff_amf_get_field_value(pkt->data + 9, data_end, 00588 "description", tmpstr, sizeof(tmpstr))) 00589 av_log(s, AV_LOG_ERROR, "Server error: %s\n",tmpstr); 00590 return -1; 00591 } else if (!memcmp(pkt->data, "\002\000\007_result", 10)) { 00592 switch (rt->state) { 00593 case STATE_HANDSHAKED: 00594 if (!rt->is_input) { 00595 gen_release_stream(s, rt); 00596 gen_fcpublish_stream(s, rt); 00597 rt->state = STATE_RELEASING; 00598 } else { 00599 rt->state = STATE_CONNECTING; 00600 } 00601 gen_create_stream(s, rt); 00602 break; 00603 case STATE_FCPUBLISH: 00604 rt->state = STATE_CONNECTING; 00605 break; 00606 case STATE_RELEASING: 00607 rt->state = STATE_FCPUBLISH; 00608 /* hack for Wowza Media Server, it does not send result for 00609 * releaseStream and FCPublish calls */ 00610 if (!pkt->data[10]) { 00611 int pkt_id = (int) av_int2dbl(AV_RB64(pkt->data + 11)); 00612 if (pkt_id == 4) 00613 rt->state = STATE_CONNECTING; 00614 } 00615 if (rt->state != STATE_CONNECTING) 00616 break; 00617 case STATE_CONNECTING: 00618 //extract a number from the result 00619 if (pkt->data[10] || pkt->data[19] != 5 || pkt->data[20]) { 00620 av_log(s, AV_LOG_WARNING, "Unexpected reply on connect()\n"); 00621 } else { 00622 rt->main_channel_id = (int) av_int2dbl(AV_RB64(pkt->data + 21)); 00623 } 00624 if (rt->is_input) { 00625 gen_play(s, rt); 00626 } else { 00627 gen_publish(s, rt); 00628 } 00629 rt->state = STATE_READY; 00630 break; 00631 } 00632 } else if (!memcmp(pkt->data, "\002\000\010onStatus", 11)) { 00633 const uint8_t* ptr = pkt->data + 11; 00634 uint8_t tmpstr[256]; 00635 00636 for (i = 0; i < 2; i++) { 00637 t = ff_amf_tag_size(ptr, data_end); 00638 if (t < 0) 00639 return 1; 00640 ptr += t; 00641 } 00642 t = ff_amf_get_field_value(ptr, data_end, 00643 "level", tmpstr, sizeof(tmpstr)); 00644 if (!t && !strcmp(tmpstr, "error")) { 00645 if (!ff_amf_get_field_value(ptr, data_end, 00646 "description", tmpstr, sizeof(tmpstr))) 00647 av_log(s, AV_LOG_ERROR, "Server error: %s\n",tmpstr); 00648 return -1; 00649 } 00650 t = ff_amf_get_field_value(ptr, data_end, 00651 "code", tmpstr, sizeof(tmpstr)); 00652 if (!t && !strcmp(tmpstr, "NetStream.Play.Start")) rt->state = STATE_PLAYING; 00653 if (!t && !strcmp(tmpstr, "NetStream.Play.Stop")) rt->state = STATE_STOPPED; 00654 if (!t && !strcmp(tmpstr, "NetStream.Play.UnpublishNotify")) rt->state = STATE_STOPPED; 00655 if (!t && !strcmp(tmpstr, "NetStream.Publish.Start")) rt->state = STATE_PUBLISHING; 00656 } 00657 break; 00658 } 00659 return 0; 00660 } 00661 00673 static int get_packet(URLContext *s, int for_header) 00674 { 00675 RTMPContext *rt = s->priv_data; 00676 int ret; 00677 uint8_t *p; 00678 const uint8_t *next; 00679 uint32_t data_size; 00680 uint32_t ts, cts, pts=0; 00681 00682 if (rt->state == STATE_STOPPED) 00683 return AVERROR_EOF; 00684 00685 for (;;) { 00686 RTMPPacket rpkt = { 0 }; 00687 if ((ret = ff_rtmp_packet_read(rt->stream, &rpkt, 00688 rt->chunk_size, rt->prev_pkt[0])) <= 0) { 00689 if (ret == 0) { 00690 return AVERROR(EAGAIN); 00691 } else { 00692 return AVERROR(EIO); 00693 } 00694 } 00695 rt->bytes_read += ret; 00696 if (rt->bytes_read > rt->last_bytes_read + rt->client_report_size) { 00697 av_log(s, AV_LOG_DEBUG, "Sending bytes read report\n"); 00698 gen_bytes_read(s, rt, rpkt.timestamp + 1); 00699 rt->last_bytes_read = rt->bytes_read; 00700 } 00701 00702 ret = rtmp_parse_result(s, rt, &rpkt); 00703 if (ret < 0) {//serious error in current packet 00704 ff_rtmp_packet_destroy(&rpkt); 00705 return -1; 00706 } 00707 if (rt->state == STATE_STOPPED) { 00708 ff_rtmp_packet_destroy(&rpkt); 00709 return AVERROR_EOF; 00710 } 00711 if (for_header && (rt->state == STATE_PLAYING || rt->state == STATE_PUBLISHING)) { 00712 ff_rtmp_packet_destroy(&rpkt); 00713 return 0; 00714 } 00715 if (!rpkt.data_size || !rt->is_input) { 00716 ff_rtmp_packet_destroy(&rpkt); 00717 continue; 00718 } 00719 if (rpkt.type == RTMP_PT_VIDEO || rpkt.type == RTMP_PT_AUDIO || 00720 (rpkt.type == RTMP_PT_NOTIFY && !memcmp("\002\000\012onMetaData", rpkt.data, 13))) { 00721 ts = rpkt.timestamp; 00722 00723 // generate packet header and put data into buffer for FLV demuxer 00724 rt->flv_off = 0; 00725 rt->flv_size = rpkt.data_size + 15; 00726 rt->flv_data = p = av_realloc(rt->flv_data, rt->flv_size); 00727 bytestream_put_byte(&p, rpkt.type); 00728 bytestream_put_be24(&p, rpkt.data_size); 00729 bytestream_put_be24(&p, ts); 00730 bytestream_put_byte(&p, ts >> 24); 00731 bytestream_put_be24(&p, 0); 00732 bytestream_put_buffer(&p, rpkt.data, rpkt.data_size); 00733 bytestream_put_be32(&p, 0); 00734 ff_rtmp_packet_destroy(&rpkt); 00735 return 0; 00736 } else if (rpkt.type == RTMP_PT_METADATA) { 00737 // we got raw FLV data, make it available for FLV demuxer 00738 rt->flv_off = 0; 00739 rt->flv_size = rpkt.data_size; 00740 rt->flv_data = av_realloc(rt->flv_data, rt->flv_size); 00741 /* rewrite timestamps */ 00742 next = rpkt.data; 00743 ts = rpkt.timestamp; 00744 while (next - rpkt.data < rpkt.data_size - 11) { 00745 next++; 00746 data_size = bytestream_get_be24(&next); 00747 p=next; 00748 cts = bytestream_get_be24(&next); 00749 cts |= bytestream_get_byte(&next) << 24; 00750 if (pts==0) 00751 pts=cts; 00752 ts += cts - pts; 00753 pts = cts; 00754 bytestream_put_be24(&p, ts); 00755 bytestream_put_byte(&p, ts >> 24); 00756 next += data_size + 3 + 4; 00757 } 00758 memcpy(rt->flv_data, rpkt.data, rpkt.data_size); 00759 ff_rtmp_packet_destroy(&rpkt); 00760 return 0; 00761 } 00762 ff_rtmp_packet_destroy(&rpkt); 00763 } 00764 return 0; 00765 } 00766 00767 static int rtmp_close(URLContext *h) 00768 { 00769 RTMPContext *rt = h->priv_data; 00770 00771 if (!rt->is_input) { 00772 rt->flv_data = NULL; 00773 if (rt->out_pkt.data_size) 00774 ff_rtmp_packet_destroy(&rt->out_pkt); 00775 if (rt->state > STATE_FCPUBLISH) 00776 gen_fcunpublish_stream(h, rt); 00777 } 00778 if (rt->state > STATE_HANDSHAKED) 00779 gen_delete_stream(h, rt); 00780 00781 av_freep(&rt->flv_data); 00782 ffurl_close(rt->stream); 00783 av_free(rt); 00784 return 0; 00785 } 00786 00796 static int rtmp_open(URLContext *s, const char *uri, int flags) 00797 { 00798 RTMPContext *rt; 00799 char proto[8], hostname[256], path[1024], *fname; 00800 uint8_t buf[2048]; 00801 int port; 00802 int ret; 00803 00804 rt = av_mallocz(sizeof(RTMPContext)); 00805 if (!rt) 00806 return AVERROR(ENOMEM); 00807 s->priv_data = rt; 00808 rt->is_input = !(flags & AVIO_FLAG_WRITE); 00809 00810 av_url_split(proto, sizeof(proto), NULL, 0, hostname, sizeof(hostname), &port, 00811 path, sizeof(path), s->filename); 00812 00813 if (port < 0) 00814 port = RTMP_DEFAULT_PORT; 00815 ff_url_join(buf, sizeof(buf), "tcp", NULL, hostname, port, NULL); 00816 00817 if (ffurl_open(&rt->stream, buf, AVIO_FLAG_READ_WRITE) < 0) { 00818 av_log(s , AV_LOG_ERROR, "Cannot open connection %s\n", buf); 00819 goto fail; 00820 } 00821 00822 rt->state = STATE_START; 00823 if (rtmp_handshake(s, rt)) 00824 return -1; 00825 00826 rt->chunk_size = 128; 00827 rt->state = STATE_HANDSHAKED; 00828 //extract "app" part from path 00829 if (!strncmp(path, "/ondemand/", 10)) { 00830 fname = path + 10; 00831 memcpy(rt->app, "ondemand", 9); 00832 } else { 00833 char *p = strchr(path + 1, '/'); 00834 if (!p) { 00835 fname = path + 1; 00836 rt->app[0] = '\0'; 00837 } else { 00838 char *c = strchr(p + 1, ':'); 00839 fname = strchr(p + 1, '/'); 00840 if (!fname || c < fname) { 00841 fname = p + 1; 00842 av_strlcpy(rt->app, path + 1, p - path); 00843 } else { 00844 fname++; 00845 av_strlcpy(rt->app, path + 1, fname - path - 1); 00846 } 00847 } 00848 } 00849 if (!strchr(fname, ':') && 00850 (!strcmp(fname + strlen(fname) - 4, ".f4v") || 00851 !strcmp(fname + strlen(fname) - 4, ".mp4"))) { 00852 memcpy(rt->playpath, "mp4:", 5); 00853 } else { 00854 rt->playpath[0] = 0; 00855 } 00856 strncat(rt->playpath, fname, sizeof(rt->playpath) - 5); 00857 00858 rt->client_report_size = 1048576; 00859 rt->bytes_read = 0; 00860 rt->last_bytes_read = 0; 00861 00862 av_log(s, AV_LOG_DEBUG, "Proto = %s, path = %s, app = %s, fname = %s\n", 00863 proto, path, rt->app, rt->playpath); 00864 gen_connect(s, rt, proto, hostname, port); 00865 00866 do { 00867 ret = get_packet(s, 1); 00868 } while (ret == EAGAIN); 00869 if (ret < 0) 00870 goto fail; 00871 00872 if (rt->is_input) { 00873 // generate FLV header for demuxer 00874 rt->flv_size = 13; 00875 rt->flv_data = av_realloc(rt->flv_data, rt->flv_size); 00876 rt->flv_off = 0; 00877 memcpy(rt->flv_data, "FLV\1\5\0\0\0\011\0\0\0\0", rt->flv_size); 00878 } else { 00879 rt->flv_size = 0; 00880 rt->flv_data = NULL; 00881 rt->flv_off = 0; 00882 } 00883 00884 s->max_packet_size = rt->stream->max_packet_size; 00885 s->is_streamed = 1; 00886 return 0; 00887 00888 fail: 00889 rtmp_close(s); 00890 return AVERROR(EIO); 00891 } 00892 00893 static int rtmp_read(URLContext *s, uint8_t *buf, int size) 00894 { 00895 RTMPContext *rt = s->priv_data; 00896 int orig_size = size; 00897 int ret; 00898 00899 while (size > 0) { 00900 int data_left = rt->flv_size - rt->flv_off; 00901 00902 if (data_left >= size) { 00903 memcpy(buf, rt->flv_data + rt->flv_off, size); 00904 rt->flv_off += size; 00905 return orig_size; 00906 } 00907 if (data_left > 0) { 00908 memcpy(buf, rt->flv_data + rt->flv_off, data_left); 00909 buf += data_left; 00910 size -= data_left; 00911 rt->flv_off = rt->flv_size; 00912 return data_left; 00913 } 00914 if ((ret = get_packet(s, 0)) < 0) 00915 return ret; 00916 } 00917 return orig_size; 00918 } 00919 00920 static int rtmp_write(URLContext *s, const uint8_t *buf, int size) 00921 { 00922 RTMPContext *rt = s->priv_data; 00923 int size_temp = size; 00924 int pktsize, pkttype; 00925 uint32_t ts; 00926 const uint8_t *buf_temp = buf; 00927 00928 if (size < 11) { 00929 av_log(s, AV_LOG_DEBUG, "FLV packet too small %d\n", size); 00930 return 0; 00931 } 00932 00933 do { 00934 if (!rt->flv_off) { 00935 //skip flv header 00936 if (buf_temp[0] == 'F' && buf_temp[1] == 'L' && buf_temp[2] == 'V') { 00937 buf_temp += 9 + 4; 00938 size_temp -= 9 + 4; 00939 } 00940 00941 pkttype = bytestream_get_byte(&buf_temp); 00942 pktsize = bytestream_get_be24(&buf_temp); 00943 ts = bytestream_get_be24(&buf_temp); 00944 ts |= bytestream_get_byte(&buf_temp) << 24; 00945 bytestream_get_be24(&buf_temp); 00946 size_temp -= 11; 00947 rt->flv_size = pktsize; 00948 00949 //force 12bytes header 00950 if (((pkttype == RTMP_PT_VIDEO || pkttype == RTMP_PT_AUDIO) && ts == 0) || 00951 pkttype == RTMP_PT_NOTIFY) { 00952 if (pkttype == RTMP_PT_NOTIFY) 00953 pktsize += 16; 00954 rt->prev_pkt[1][RTMP_SOURCE_CHANNEL].channel_id = 0; 00955 } 00956 00957 //this can be a big packet, it's better to send it right here 00958 ff_rtmp_packet_create(&rt->out_pkt, RTMP_SOURCE_CHANNEL, pkttype, ts, pktsize); 00959 rt->out_pkt.extra = rt->main_channel_id; 00960 rt->flv_data = rt->out_pkt.data; 00961 00962 if (pkttype == RTMP_PT_NOTIFY) 00963 ff_amf_write_string(&rt->flv_data, "@setDataFrame"); 00964 } 00965 00966 if (rt->flv_size - rt->flv_off > size_temp) { 00967 bytestream_get_buffer(&buf_temp, rt->flv_data + rt->flv_off, size_temp); 00968 rt->flv_off += size_temp; 00969 } else { 00970 bytestream_get_buffer(&buf_temp, rt->flv_data + rt->flv_off, rt->flv_size - rt->flv_off); 00971 rt->flv_off += rt->flv_size - rt->flv_off; 00972 } 00973 00974 if (rt->flv_off == rt->flv_size) { 00975 bytestream_get_be32(&buf_temp); 00976 00977 ff_rtmp_packet_write(rt->stream, &rt->out_pkt, rt->chunk_size, rt->prev_pkt[1]); 00978 ff_rtmp_packet_destroy(&rt->out_pkt); 00979 rt->flv_size = 0; 00980 rt->flv_off = 0; 00981 } 00982 } while (buf_temp - buf < size_temp); 00983 return size; 00984 } 00985 00986 URLProtocol ff_rtmp_protocol = { 00987 .name = "rtmp", 00988 .url_open = rtmp_open, 00989 .url_read = rtmp_read, 00990 .url_write = rtmp_write, 00991 .url_close = rtmp_close, 00992 };