/* mediastreamer2 library - modular sound and video processing and streaming Copyright (C) 2012 Belledonne Communications, Grenoble, France This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifdef HAVE_CONFIG_H #include "mediastreamer-config.h" #endif #include #include "common.h" #include "mediastreamer2/msequalizer.h" #include "mediastreamer2/msfileplayer.h" #include "mediastreamer2/msvolume.h" #include "mediastreamer2/msfilerec.h" #ifdef VIDEO_ENABLED #include "mediastreamer2/msv4l.h" #endif #include #include #include #ifndef WIN32 #include #else #include #endif #include #include #include #include static int cond = 1; typedef struct _MediastreamDatas { int payload; bool_t is_verbose; bool_t avpf; char *playback_card; char *outfile; char *infile; PayloadType *pt; RtpProfile *profile; MSFilter *read; MSFilter *write; MSFilter *decoder; MSTicker *ticker; } MediastreamDatas; // MAIN METHODS /* init default arguments */ static MediastreamDatas *init_default_args(); /* parse args */ static bool_t parse_args(int argc, char **argv, MediastreamDatas *out); /* setup streams */ static void setup_media_streams(MediastreamDatas *args); /* run loop */ static void run_non_interactive_loop(MediastreamDatas *args); /* exit */ static void clear_mediastreams(MediastreamDatas *args); // HELPER METHODS static void stop_handler(int signum); const char *usage = "pcap_playback --infile \n" "--payload \n" "--avpf [assume RTP/AVPF profile]\n" "[ --playback-card ]\n" "[ --outfile ] limited to wav file for now\n" "[ --verbose (most verbose messages) ]\n" ; int main(int argc, char *argv[]) { MediastreamDatas *args; cond = 1; args = init_default_args(); if (!parse_args(argc, argv, args)) { printf("%s", usage); return 0; } setup_media_streams(args); run_non_interactive_loop(args); clear_mediastreams(args); free(args); return 0; } static MediastreamDatas *init_default_args() { MediastreamDatas *args = (MediastreamDatas *) ms_malloc0(sizeof(MediastreamDatas)); args->payload = 0; args->is_verbose = FALSE; args->playback_card = NULL; args->infile = NULL; args->pt = NULL; args->profile = NULL; args->read = NULL; args->write = NULL; args->decoder = NULL; args->ticker = NULL; return args; } static bool_t parse_args(int argc, char **argv, MediastreamDatas *out) { int i; if (argc < 2) { printf("%s", usage); return FALSE; } for (i = 1; i < argc; i++) { if (strcmp(argv[i], "--payload") == 0) { i++; if (isdigit(argv[i][0])) { out->payload = atoi(argv[i]); }else { out->payload=114; out->pt=ms_tools_parse_custom_payload(argv[i]); } } else if (strcmp(argv[i], "--playback-card") == 0) { i++; out->playback_card = argv[i]; } else if (strcmp(argv[i], "--infile") == 0) { i++; out->infile = argv[i]; } else if (strcmp(argv[i], "--outfile") == 0) { i++; out->outfile = argv[i]; } else if (strcmp(argv[i], "--verbose") == 0) { out->is_verbose = TRUE; } else if (strcmp(argv[i], "--help") == 0) { printf("%s", usage); return FALSE; }else if (strcmp(argv[i],"--avpf")==0){ out->avpf=TRUE; } } if (out->playback_card && out->outfile ) { ms_error("Cannot define both playback card and output file, select only one"); return FALSE; } return TRUE; } static void reader_notify_cb(void *user_data, MSFilter *f, unsigned int event, void *eventdata) { if (event == MS_FILE_PLAYER_EOF) { cond = 0; } } static void video_decoder_callback(void *user_data, MSFilter *f, unsigned int event, void *eventdata){ MSVideoCodecRPSI *rpsi; uint16_t picture_id=0; switch(event){ case MS_VIDEO_DECODER_SEND_RPSI: rpsi=(MSVideoCodecRPSI*)eventdata; if (rpsi->bit_string_len == 8) { picture_id = *((uint8_t *)rpsi->bit_string); } else if (rpsi->bit_string_len == 16) { picture_id = ntohs(*((uint16_t *)rpsi->bit_string)); } ms_message("Decoder would like to send RPSI for pic-id=%u",picture_id); break; case MS_VIDEO_DECODER_SEND_SLI: ms_message("Decoder would like to send SLI"); break; } } static void setup_media_streams(MediastreamDatas *args) { MSConnectionHelper h; MSTickerParams params = {0}; /*create the rtp session */ ortp_init(); if (args->is_verbose) { ortp_set_log_level_mask(ORTP_DEBUG | ORTP_MESSAGE | ORTP_WARNING | ORTP_ERROR | ORTP_FATAL); } else { ortp_set_log_level_mask(ORTP_MESSAGE | ORTP_WARNING | ORTP_ERROR | ORTP_FATAL); } rtp_profile_set_payload(&av_profile, 110, &payload_type_speex_nb); rtp_profile_set_payload(&av_profile, 111, &payload_type_speex_wb); rtp_profile_set_payload(&av_profile, 112, &payload_type_ilbc); rtp_profile_set_payload(&av_profile, 113, &payload_type_amr); rtp_profile_set_payload(&av_profile, 115, &payload_type_lpc1015); #ifdef VIDEO_ENABLED rtp_profile_set_payload(&av_profile, 26, &payload_type_jpeg); rtp_profile_set_payload(&av_profile, 98, &payload_type_h263_1998); rtp_profile_set_payload(&av_profile, 97, &payload_type_theora); rtp_profile_set_payload(&av_profile, 99, &payload_type_mp4v); rtp_profile_set_payload(&av_profile, 100, &payload_type_x_snow); rtp_profile_set_payload(&av_profile, 102, &payload_type_h264); rtp_profile_set_payload(&av_profile, 103, &payload_type_vp8); #endif args->profile = rtp_profile_clone_full(&av_profile); ms_init(); ms_filter_enable_statistics(TRUE); ms_filter_reset_statistics(); signal(SIGINT, stop_handler); if (args->pt==NULL) args->pt = rtp_profile_get_payload(args->profile, args->payload); if (args->pt == NULL) { printf("Error: no payload defined with number %i.", args->payload); exit(-1); } if (args->pt->type == PAYLOAD_VIDEO) { #ifdef VIDEO_ENABLED const char *display_name; MSPixFmt format; MSVideoSize disp_size; int tmp = 1; #if defined(HAVE_GL) display_name = "MSGLXVideo"; #elif defined(HAVE_XV) display_name = "MSX11Video"; #elif __APPLE__ && !defined(__ios) display_name ="MSOSXGLDisplay"; #else display_name = "MSVideoOut"; #endif args->read = ms_filter_new(MS_FILE_PLAYER_ID); args->write = ms_filter_new_from_name(display_name); args->decoder = ms_filter_create_decoder(args->pt->mime_type); if (args->decoder==NULL){ fprintf(stderr,"No decoder available for %s.\n",args->pt->mime_type); exit(-1); } if (ms_filter_call_method(args->decoder,MS_VIDEO_DECODER_ENABLE_AVPF,&args->avpf)==0){ ms_filter_add_notify_callback(args->decoder, video_decoder_callback, NULL, TRUE); } ms_filter_call_method_noarg(args->read, MS_FILE_PLAYER_CLOSE); ms_filter_call_method(args->read, MS_FILE_PLAYER_OPEN, args->infile); ms_filter_call_method(args->read, MS_FILTER_SET_SAMPLE_RATE, &args->pt->clock_rate); ms_filter_call_method_noarg(args->read, MS_FILE_PLAYER_START); ms_filter_add_notify_callback(args->read, reader_notify_cb, NULL,FALSE); /*force the decoder to output YUV420P */ format = MS_YUV420P; ms_filter_call_method(args->decoder, MS_FILTER_SET_PIX_FMT, &format); /*configure the display window */ disp_size.width = MS_VIDEO_SIZE_CIF_W; disp_size.height = MS_VIDEO_SIZE_CIF_H; ms_filter_call_method(args->write, MS_FILTER_SET_VIDEO_SIZE, &disp_size); ms_filter_call_method(args->write, MS_VIDEO_DISPLAY_ENABLE_AUTOFIT, &tmp); ms_filter_call_method(args->write, MS_FILTER_SET_PIX_FMT, &format); params.name = "Video MSTicker"; params.prio = MS_TICKER_PRIO_REALTIME; args->ticker = ms_ticker_new_with_params(¶ms); ms_connection_helper_start(&h); ms_connection_helper_link(&h, args->read, -1, 0); ms_connection_helper_link(&h, args->decoder, 0, 0); ms_connection_helper_link(&h, args->write, 0, -1); ms_ticker_attach(args->ticker, args->read); #else printf("Error: video support not compiled.\n"); #endif } else { MSSndCardManager *manager = ms_snd_card_manager_get(); MSSndCard *play =NULL; if (args->outfile) { args->write=ms_filter_new(MS_FILE_REC_ID); if (ms_filter_call_method(args->write, MS_FILE_REC_OPEN, args->outfile)) { ms_error("Cannot open file [%s] in write mode",args->outfile); exit(-1); } } else { play = args->playback_card ==NULL ? ms_snd_card_manager_get_default_playback_card(manager) : ms_snd_card_manager_get_card(manager, args->playback_card); args->write = ms_snd_card_create_writer(play); } args->read = ms_filter_new(MS_FILE_PLAYER_ID); args->decoder = ms_filter_create_decoder(args->pt->mime_type); ms_filter_call_method_noarg(args->read, MS_FILE_PLAYER_CLOSE); ms_filter_call_method(args->read, MS_FILE_PLAYER_OPEN, args->infile); ms_filter_call_method_noarg(args->read, MS_FILE_PLAYER_START); ms_filter_call_method(args->read, MS_FILTER_SET_SAMPLE_RATE, &args->pt->clock_rate); ms_filter_call_method(args->decoder, MS_FILTER_SET_SAMPLE_RATE, &args->pt->clock_rate); ms_filter_call_method(args->decoder, MS_FILTER_SET_NCHANNELS, &args->pt->channels); ms_filter_call_method(args->write, MS_FILTER_SET_SAMPLE_RATE, &args->pt->clock_rate); ms_filter_call_method(args->write, MS_FILTER_SET_NCHANNELS, &args->pt->channels); if (ms_filter_get_id(args->write) == MS_FILE_REC_ID) { ms_filter_call_method_noarg(args->write, MS_FILE_REC_START); } params.name = "Audio MSTicker"; params.prio = MS_TICKER_PRIO_REALTIME; args->ticker = ms_ticker_new_with_params(¶ms); ms_connection_helper_start(&h); ms_connection_helper_link(&h, args->read, -1, 0); ms_connection_helper_link(&h, args->decoder, 0, 0); ms_connection_helper_link(&h, args->write, 0, -1); ms_ticker_attach(args->ticker, args->read); } } static void run_non_interactive_loop(MediastreamDatas *args) { while (cond) { int n; for (n = 0; n < 100; ++n) { #ifdef WIN32 MSG msg; Sleep(10); while (PeekMessage(&msg, NULL, 0, 0, 1)) { TranslateMessage(&msg); DispatchMessage(&msg); } #else struct timespec ts; ts.tv_sec = 0; ts.tv_nsec = 10000000; nanosleep(&ts, NULL); #endif } } } static void clear_mediastreams(MediastreamDatas *args) { MSConnectionHelper h; ms_message("stopping all...\n"); if (args->read) { ms_ticker_detach(args->ticker, args->read); ms_connection_helper_start(&h); ms_connection_helper_unlink(&h, args->read, -1, 0); ms_connection_helper_unlink(&h, args->decoder, 0, 0); ms_connection_helper_unlink(&h, args->write, 0, -1); if (ms_filter_get_id(args->write) == MS_FILE_REC_ID) { ms_filter_call_method_noarg(args->write, MS_FILE_REC_CLOSE); } } rtp_profile_destroy(args->profile); ms_exit(); } // HELPER METHODS static void stop_handler(int signum) { cond--; if (cond < 0) { ms_error("Brutal exit (%d)\n", cond); exit(-1); } }