/**
* The MIT License ( MIT )
*
* Copyright ( c ) 2013 - 2018 Winlin
*
* Permission is hereby granted , free of charge , to any person obtaining a copy of
* this software and associated documentation files ( the " Software " ) , to deal in
* the Software without restriction , including without limitation the rights to
* use , copy , modify , merge , publish , distribute , sublicense , and / or sell copies of
* the Software , and to permit persons to whom the Software is furnished to do so ,
* subject to the following conditions :
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software .
*
* THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND , EXPRESS OR
* IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY , FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT . IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM , DAMAGES OR OTHER LIABILITY , WHETHER
* IN AN ACTION OF CONTRACT , TORT OR OTHERWISE , ARISING FROM , OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE .
*/
# include <stdio.h>
# include <stdlib.h>
# include <unistd.h>
# include <getopt.h>
# include <assert.h>
# include "../../objs/include/srs_librtmp.h"
void parse_amf0_object ( char * p , srs_amf0_t args )
{
char opvt = 0 ; // object property value type.
const char * opnp = NULL ; // object property name ptr.
const char * opvp = NULL ; // object property value ptr.
while ( * p ) {
switch ( * p + + ) {
case ' O ' :
while ( * p & & * p + + ! = ' : ' ) {
}
if ( * p + + = = ' 1 ' ) {
printf ( " amf0 object start \n " ) ;
} else {
printf ( " amf0 object end \n " ) ;
}
break ;
case ' N ' :
opvt = * p + + ;
if ( * p + + ! = ' : ' ) {
printf ( " object property must split by :. \n " ) ;
exit ( - 1 ) ;
}
opnp = p + + ;
while ( * p & & * p + + ! = ' : ' ) {
}
p [ - 1 ] = 0 ;
opvp = p ;
printf ( " amf0 %c property[%s]=%s \n " , opvt , opnp , opvp ) ;
switch ( opvt ) {
case ' S ' :
srs_amf0_object_property_set ( args , opnp , srs_amf0_create_string ( opvp ) ) ;
break ;
default :
printf ( " unsupported object property. \n " ) ;
exit ( - 1 ) ;
}
* p = 0 ;
break ;
default :
printf ( " only supports an object arg. \n " ) ;
exit ( - 1 ) ;
}
}
}
// srs debug info.
char * ip = NULL ;
char * sig = NULL ;
int pid = 0 , cid = 0 ;
int major = 0 , minor = 0 , revision = 0 , build = 0 ;
// User options.
int complex_handshake = 0 ;
const char * rtmp_url = NULL ;
const char * output_flv = NULL ;
const char * swfUrl = NULL ;
const char * tcUrl = NULL ;
const char * pageUrl = NULL ;
srs_amf0_t args = NULL ;
int do_proxy ( srs_rtmp_t rtmp , srs_flv_t flv )
{
int ret = 0 ;
if ( ( ret = srs_rtmp_dns_resolve ( rtmp ) ) ! = 0 ) {
srs_human_trace ( " dns resolve failed, ret=%d " , ret ) ;
return ret ;
}
if ( ( ret = srs_rtmp_connect_server ( rtmp ) ) ! = 0 ) {
srs_human_trace ( " connect to server failed, ret=%d " , ret ) ;
return ret ;
}
if ( complex_handshake ) {
if ( ( ret = srs_rtmp_do_complex_handshake ( rtmp ) ) ! = 0 ) {
srs_human_trace ( " complex handshake failed, ret=%d " , ret ) ;
return ret ;
}
srs_human_trace ( " do complex handshake success " ) ;
} else {
if ( ( ret = srs_rtmp_do_simple_handshake ( rtmp ) ) ! = 0 ) {
srs_human_trace ( " simple handshake failed, ret=%d " , ret ) ;
return ret ;
}
srs_human_trace ( " do simple handshake success " ) ;
}
if ( ( ret = srs_rtmp_set_connect_args ( rtmp , tcUrl , swfUrl , pageUrl , args ) ) ! = 0 ) {
srs_human_trace ( " set connect args failed, ret=%d " , ret ) ;
return ret ;
}
if ( ( ret = srs_rtmp_connect_app ( rtmp ) ) ! = 0 ) {
srs_human_trace ( " connect vhost/app failed, ret=%d " , ret ) ;
return ret ;
}
if ( ( ret = srs_rtmp_get_server_sig ( rtmp , & sig ) ) ! = 0 ) {
srs_human_trace ( " Retrieve server ID failed, ret=%d " , ret ) ;
return ret ;
}
if ( ( ret = srs_rtmp_get_server_id ( rtmp , & ip , & pid , & cid ) ) ! = 0 ) {
srs_human_trace ( " Retrieve server ID failed, ret=%d " , ret ) ;
return ret ;
}
if ( ( ret = srs_rtmp_get_server_version ( rtmp , & major , & minor , & revision , & build ) ) ! = 0 ) {
srs_human_trace ( " Retrieve server version failed, ret=%d " , ret ) ;
return ret ;
}
srs_human_trace ( " connect ok, ip=%s, server=%s/%d.%d.%d.%d, pid=%d, cid=%d " ,
ip , sig , major , minor , revision , build , pid , cid ) ;
if ( ( ret = srs_rtmp_play_stream ( rtmp ) ) ! = 0 ) {
srs_human_trace ( " play stream failed, ret=%d " , ret ) ;
return ret ;
}
srs_human_trace ( " play stream success " ) ;
if ( flv ) {
// flv header
char header [ 9 ] ;
// 3bytes, signature, "FLV",
header [ 0 ] = ' F ' ;
header [ 1 ] = ' L ' ;
header [ 2 ] = ' V ' ;
// 1bytes, version, 0x01,
header [ 3 ] = 0x01 ;
// 1bytes, flags, UB[5] 0, UB[1] audio present, UB[1] 0, UB[1] video present.
header [ 4 ] = 0x03 ; // audio + video.
// 4bytes, dataoffset
header [ 5 ] = 0x00 ;
header [ 6 ] = 0x00 ;
header [ 7 ] = 0x00 ;
header [ 8 ] = 0x09 ;
if ( ( ret = srs_flv_write_header ( flv , header ) ) ! = 0 ) {
srs_human_trace ( " write flv header failed, ret=%d " , ret ) ;
return ret ;
}
}
int64_t nb_packets = 0 ;
uint32_t pre_timestamp = 0 ;
int64_t pre_now = - 1 ;
int64_t start_time = - 1 ;
char buffer [ 1024 ] ;
for ( ; ; ) {
int size ;
char type ;
char * data ;
uint32_t timestamp ;
if ( ( ret = srs_rtmp_read_packet ( rtmp , & type , & timestamp , & data , & size ) ) ! = 0 ) {
srs_human_trace ( " read rtmp packet failed, ret=%d " , ret ) ;
return ret ;
}
if ( pre_now = = - 1 ) {
pre_now = srs_utils_time_ms ( ) ;
}
if ( start_time = = - 1 ) {
start_time = srs_utils_time_ms ( ) ;
}
if ( ( ret = srs_human_format_rtmp_packet2 ( buffer , sizeof ( buffer ) , type , timestamp , data , size , pre_timestamp , pre_now , start_time , nb_packets + + ) ) ! = 0 ) {
srs_human_trace ( " print rtmp packet failed, ret=%d " , ret ) ;
return ret ;
}
srs_human_trace ( " %s " , buffer ) ;
pre_timestamp = timestamp ;
pre_now = srs_utils_time_ms ( ) ;
// we only write some types of messages to flv file.
int is_flv_msg = type = = SRS_RTMP_TYPE_AUDIO
| | type = = SRS_RTMP_TYPE_VIDEO | | type = = SRS_RTMP_TYPE_SCRIPT ;
// for script data, ignore except onMetaData
if ( type = = SRS_RTMP_TYPE_SCRIPT ) {
if ( ! srs_rtmp_is_onMetaData ( type , data , size ) ) {
is_flv_msg = 0 ;
}
}
if ( flv ) {
if ( is_flv_msg ) {
if ( ( ret = srs_flv_write_tag ( flv , type , timestamp , data , size ) ) ! = 0 ) {
srs_human_trace ( " dump rtmp packet failed, ret=%d " , ret ) ;
return ret ;
}
} else {
srs_human_trace ( " drop message type=%#x, size=%dB " , type , size ) ;
}
}
free ( data ) ;
}
return ret ;
}
int main ( int argc , char * * argv )
{
srs_flv_t flv = NULL ;
srs_rtmp_t rtmp = NULL ;
printf ( " dump rtmp stream to flv file \n " ) ;
printf ( " srs(ossrs) client librtmp library. \n " ) ;
printf ( " version: %d.%d.%d \n " , srs_version_major ( ) , srs_version_minor ( ) , srs_version_revision ( ) ) ;
printf ( " @refer to http://rtmpdump.mplayerhq.hu/rtmpdump.1.html \n " ) ;
int show_help = 0 ;
const char * short_options = " hxr:o:s:t:p:C: " ;
struct option long_options [ ] = {
{ " rtmp " , required_argument , 0 , ' r ' } ,
{ " flv " , required_argument , 0 , ' o ' } ,
{ " swfUrl " , required_argument , 0 , ' s ' } ,
{ " tcUrl " , required_argument , 0 , ' t ' } ,
{ " pageUrl " , required_argument , 0 , ' p ' } ,
{ " conn " , required_argument , 0 , ' C ' } ,
{ " complex " , no_argument , 0 , ' x ' } ,
{ " help " , no_argument , 0 , ' h ' } ,
{ 0 , 0 , 0 , 0 }
} ;
int opt = 0 ;
int option_index = 0 ;
while ( ( opt = getopt_long ( argc , argv , short_options , long_options , & option_index ) ) ! = - 1 ) {
switch ( opt ) {
case ' r ' :
rtmp_url = optarg ;
break ;
case ' o ' :
output_flv = optarg ;
break ;
case ' s ' :
swfUrl = optarg ;
break ;
case ' t ' :
tcUrl = optarg ;
break ;
case ' p ' :
pageUrl = optarg ;
break ;
case ' C ' :
if ( ! args ) {
args = srs_amf0_create_object ( ) ;
}
char * p = ( char * ) optarg ;
parse_amf0_object ( p , args ) ;
break ;
case ' x ' :
complex_handshake = 1 ;
break ;
case ' h ' :
show_help = 1 ;
break ;
default :
printf ( " unsupported opt. \n " ) ;
exit ( - 1 ) ;
}
}
if ( ! rtmp_url | | show_help ) {
printf ( " Usage: %s -r url [-o output] [-s swfUrl] [-t tcUrl] [-p pageUrl] [-C conndata] [--complex] [-h] \n "
" Options: \n "
" --rtmp -r url \n "
" URL of the server and media content. \n "
" --flv -o output \n "
" Specify the output file name. If the name is − or is omitted, the stream is written to stdout. \n "
" --complex \n "
" Whether use complex handshake(srs-librtmp with ssl required). \n "
" --swfUrl -s url \n "
" URL of the SWF player for the media. By default no value will be sent. \n "
" --tcUrl -t url \n "
" URL of the target stream. Defaults to rtmp[e]://host[:port]/app/playpath. \n "
" --pageUrl -p url \n "
" URL of the web page in which the media was embedded. By default no value will be sent. \n "
" − − conn − C type:data \n "
" Append arbitrary AMF data to the Connect message. The type must be B for Boolean, N for number, S for string, O for object, or Z for null. For Booleans the data must be either 0 or 1 for FALSE or TRUE, respectively. Likewise for Objects the data must be 0 or 1 to end or begin an object, respectively. Data items in subobjects may be named, by prefixing the type with 'N' and specifying the name before the value, e.g. NB:myFlag:1. This option may be used multiple times to construct arbitrary AMF sequences. E.g. \n "
" − C B:1 − C S:authMe − C O:1 − C NN:code:1.23 − C NS:flag:ok − C O:0 \n "
" -C O:1 -C NS:CONN: \" -C B:4Rg9vr0 \" -C O:0 \n "
" @remark, support a object args only. \n "
" --help -h \n "
" Print a summary of command options. \n "
" For example: \n "
" %s -r rtmp://127.0.0.1:1935/live/livestream -o output.flv \n "
" %s -h \n " ,
argv [ 0 ] , argv [ 0 ] , argv [ 0 ] ) ;
exit ( - 1 ) ;
}
srs_human_trace ( " rtmp url: %s " , rtmp_url ) ;
srs_human_trace ( " handshake: %s " , ( complex_handshake ? " complex " : " simple " ) ) ;
srs_human_trace ( " swfUrl: %s " , swfUrl ) ;
srs_human_trace ( " pageUrl: %s " , pageUrl ) ;
srs_human_trace ( " tcUrl: %s " , tcUrl ) ;
if ( output_flv ) {
srs_human_trace ( " flv output path: %s " , output_flv ) ;
} else {
srs_human_trace ( " output to console " ) ;
}
rtmp = srs_rtmp_create ( rtmp_url ) ;
if ( output_flv ) {
flv = srs_flv_open_write ( output_flv ) ;
}
int ret = 0 ;
if ( ( ret = do_proxy ( rtmp , flv ) ) ! = 0 ) {
srs_human_trace ( " Dump RTMP failed, ret=%d " , ret ) ;
}
srs_rtmp_destroy ( rtmp ) ;
srs_flv_close ( flv ) ;
srs_human_trace ( " completed " ) ;
return 0 ;
}