• Main Page
  • Related Pages
  • Modules
  • Data Structures
  • Files
  • File List
  • Globals

libavformat/rtmpproto.c

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

Generated on Fri Sep 16 2011 17:17:50 for FFmpeg by  doxygen 1.7.1