playback_window.cc
上传用户:psq1974
上传日期:2007-01-06
资源大小:1195k
文件大小:15k
源码类别:

mpeg/mp3

开发平台:

C/C++

  1. /* Copyright (C) 1998, 1999 State University of New York at Stony Brook
  2.    Author: Andrew V. Shuvalov ( andrew@ecsl.cs.sunysb.edu )
  3.    Software license is located in file "COPYING"
  4. */
  5. #include <gtk--.h>
  6. #include <sys/socket.h>
  7. #include <sys/un.h> 
  8. #include <arpa/inet.h>
  9. #include "session.h"
  10. #include "playback_window.h"
  11. #include "play_commands.h"
  12. #include "../../pusher/src/timeval.h"
  13. PlaybackWindow::PlaybackWindow( const char *argv0, Session &ses ) 
  14.   : session( ses ), 
  15.     playerState( STATE_STOPPED ), playerStateMustBe( STATE_STOPPED ),
  16.     currentMovieId( -1 ), playerIsReady( FALSE ), operatingMode( playback ),
  17.     sih_handler_id( 0 ), command_requested_status( nothing ),
  18.     cleanup_flag( false )
  19. {
  20.   // -1 means that player does not exists
  21.   Player.pid = -1;
  22.   init_player( argv0 );
  23.   // create output socket ( to the push server, should be UDP
  24.   serverSocketOut = socket( PF_INET, SOCK_DGRAM, 0 );
  25.   if( -1 == serverSocketOut )
  26.     throw PlayerException( __LINE__, "create socket: %s", sys_errlist[errno] );
  27. }
  28. void PlaybackWindow::prepare_to_die()
  29. {
  30.   cleanup_flag = true;
  31.   stop();
  32.   wait_player_msg_sec( 1 );
  33.   if( sih_handler_id )
  34.     g2a_send_int( &Player, COM_SIH_FREE, 0, sih_handler_id );
  35.   wait_player_msg_sec( 1 );
  36.   sih_handler_id = 0;
  37. }
  38. PlaybackWindow::~PlaybackWindow()
  39. {
  40.   cerr << "PlaybackWindow destructor" << endl;
  41.   cleanup_flag = true;
  42.   if (Player.pid > 0) 
  43.     {
  44.       try 
  45. {
  46.   // first: stop the player
  47.   // g2a_send_int( &Player, COM_STATE, 0, STATE_STOPPED );
  48.   // wait_player_msg_sec( 2 );
  49.   if( sih_handler_id )
  50.     g2a_send_int( &Player, COM_SIH_FREE, 0, sih_handler_id );
  51.   g2a_send_void( &Player, COM_QUIT, 0 );
  52.   // we should wait while player emits A2G_COM_QUIT message...
  53.   for( int i = 0; i < 5 && Player.pid != -1; i++ )
  54.     wait_player_msg_sec( 1 );
  55.   
  56.   g2a_cleanup(&Player);
  57.   // kill it...
  58.   cout << "Player pid " << Player.pid << endl;
  59.   if (Player.pid > 0)
  60.     {
  61.       signal(SIGCHLD, SIG_IGN);
  62.       kill(Player.pid, SIGKILL );
  63.     }
  64.   FILE *_pipe = popen( "ps a|grep mtvp", "r" );
  65.   if( _pipe )
  66.     {
  67.       char buf[200];
  68.       while( true )
  69. {
  70.   if( NULL == fgets( buf, sizeof( buf ), _pipe ))
  71.     break;
  72.   int pid = atoi( buf );
  73.   string s( buf );
  74.   // should not contains "grep"
  75.   if( s.find( "grep" ) == s.npos )
  76.     kill( pid, SIGKILL );
  77. }
  78.       fclose( _pipe );
  79.     }
  80.   else
  81.     perror( "popen" );
  82. } catch ( PlayerException e ) {
  83.   cout << "Exeption: " << e.getText() << endl;
  84. }
  85.     }
  86. }
  87. void PlaybackWindow::init_player( const char *argv0 )
  88. {
  89.   // name, up to 5 arguments and NULL
  90.   const char *mtvp_argv[7];
  91.   mtvp_argv[0] = session.mtvPlayerName.c_str();
  92.   mtvp_argv[1] = NULL;
  93.   string arguments;
  94.   arguments = session.mtvPlayerArguments.str();
  95.   if( arguments.length() > 0 )
  96.     {
  97.       // some arguments exists, up to 5 possible
  98.       // split to different strings
  99.       int pos = 0;
  100.       const string spaces = " tn";
  101.       for( int i = 1; i < 6; i++ )
  102. {
  103.   pos = arguments.find_first_not_of( spaces, pos );
  104.   if( pos == arguments.npos )
  105.     break;
  106.   mtvp_argv[i] = arguments.c_str() + pos;
  107.   mtvp_argv[ i+1 ] = NULL;
  108.   // pos points to first character of argument
  109.   int pos_end = arguments.find_first_of( spaces, pos );
  110.   if( pos_end == arguments.npos )
  111.     break;
  112.   // put zero at the end of argument
  113.   *(char*)(arguments.c_str() + pos_end) = 0;
  114.   pos = pos_end + 1;
  115. }
  116.     }
  117.   if( -1 == g2a_init(&Player, mtvp_argv, argv0 ))
  118.     throw PlayerException( __LINE__, "init: %s", sys_errlist[errno]);
  119.   // clean-up messages stream
  120.   for( int i = 0; i < 10 && playerIsReady == false; i++ )
  121.     wait_player_msg_sec( 2 );
  122.   if( playerIsReady == false )
  123.     throw PlayerException( __LINE__, "Can't get player ready" );
  124.   // add SIH plugin
  125.   session.put_message( 2, "add SIH plugin %s", session.sihPlugin.c_str() );
  126.   g2a_send_string( &Player, COM_SIH_LINK, PMP_FLAG_RETURN_STATUS,
  127.    session.sihPlugin.c_str() );
  128.   // tell which command ask for status:
  129.   command_requested_status = plugin_link;
  130.   // clean-up messages stream
  131.   wait_player_msg_sec( 2 );
  132.   // Next step is skipped:
  133.   // we don't need to open plb and brcst sockets immediately
  134.   g2a_send_int( &Player, COM_AUTO_EXIT, 0, false );
  135.   g2a_send_int( &Player, COM_AUTO_PLAY, 0, false );
  136.   g2a_send_int( &Player, COM_STATE, 0, STATE_STOPPED );
  137.   playerStateMustBe = STATE_STOPPED;
  138.   wait_player_msg_sec( 2 );
  139.   operatingMode = playback;
  140. }
  141. void PlaybackWindow::playback_mode( const MovieFilesT &movieFiles, 
  142.     struct tm t, int movid )
  143. {
  144.   if( movieFiles.size() == 0 )
  145.     return; // nothing to do
  146.   // if plays something at that time - stop it,,
  147.   for( int i = 0; playerState != STATE_STOPPED && i < 5; i++ )
  148.     stop();
  149.   session.put_message( 3, (string)"playback mode" );
  150.   // fetch push server addr from the list:
  151.   string pushserv_addr = movieFiles[0].ipaddress;
  152.   // that's control address to send various commands
  153.   pushServControlAddr.construct( movieFiles[0].ipaddress, 
  154.  movieFiles[0].control_port );
  155.   // construct URL
  156.   string url = plugin_url_prefix;
  157.   // server name
  158.   if( pushserv_addr.length() > 0 )
  159.     {
  160.       url += pushserv_addr.c_str();
  161.       url.c_str();
  162.       // looks like a bug: string cannot attach just char*
  163.       url += ":";
  164.     }
  165.   // port number
  166.   {
  167.     char tmp_buf[10];
  168.     snprintf( tmp_buf, sizeof( tmp_buf ), "%d", 
  169.       (int)session.inputPushSock );
  170.     url += tmp_buf;
  171.   }
  172.   check_player_msgs();
  173.   g2a_send_string( &Player, COM_OPEN_STREAM_URL, 0, url.c_str() );
  174.   
  175.   // after that we must post the "play" command and the list of files to server
  176.   if( session.verbose > 1 )
  177.     for( int i = 0; i < movieFiles.size(); i++ )
  178.       session.put_message( 2, (string)"play " + movieFiles[i].filename );
  179.   // send command to server
  180.   string cmd = string( _play_cmd_ ) + "n";
  181.   // next arg is local socket number to receive data
  182.   char buf[100];
  183.   snprintf( buf, sizeof( buf ), "%dn", (int)session.inputPushSock );
  184.   cmd += buf;
  185.   TIMETOSTR( buf, t );
  186.   cmd += buf;
  187.   cmd += "n";
  188.   for(int i = 0; i < movieFiles.size(); i++ )
  189.     {
  190.       const MovieFile &mf = movieFiles[i];
  191.       cmd += mf.filename + "n";
  192.       TIMETOSTR( buf, mf.start );
  193.       cmd += buf;
  194.       cmd += "n";
  195.       TIMETOSTR( buf, mf.stop );
  196.       cmd += buf;
  197.       cmd += "n";
  198.     }
  199.   if( -1 == sendto( serverSocketOut, (void*) cmd.c_str(), 
  200.     cmd.length(), 0, (const sockaddr *)
  201.     &pushServControlAddr.addr,
  202.     sizeof( pushServControlAddr.addr )))
  203.     throw PlayerException( __LINE__, "send: %s", sys_errlist[ errno ] );
  204.   g2a_send_int( &Player, COM_STATE, 0, STATE_PAUSED );
  205.   // currentMovieId is assigned only here
  206.   currentMovieId = movid;
  207.   
  208.   session.put_message( 1, "Play: %s", url.c_str() );
  209.   // clean-up messages stream
  210.   wait_player_msg_sec( 2 );
  211.   g2a_send_int( &Player, COM_STATE, 0, STATE_PLAYING );
  212.   // set-up modes
  213.   operatingMode = playback;
  214.   playerStateMustBe = STATE_PLAYING;
  215. }
  216. void PlaybackWindow::broadcast_mode( const string &url )
  217. {
  218.   // if plays something at that time - stop it,,
  219.   for( int i = 0; playerState != STATE_STOPPED && i < 5; i++ )
  220.     stop();
  221.   g2a_send_string( &Player, COM_OPEN_STREAM_URL, 0, url.c_str() );
  222.   session.put_message( 1, "Play broadcast: %s", url.c_str() );
  223.   // clean-up messages stream
  224.   wait_player_msg_sec( 2 );
  225.   operatingMode = broadcast;
  226.   g2a_send_int( &Player, COM_STATE, 0, STATE_PLAYING );
  227.   playerStateMustBe = STATE_PLAYING;
  228. }
  229. void PlaybackWindow::stop()
  230. {
  231.   session.put_message( 3, (string)"playback stop" );
  232.   // send stop to player: in fact, close the channel
  233.   g2a_send_int( &Player, COM_STATE, 0, STATE_STOPPED );
  234.   g2a_send_void( &Player, COM_CLOSE_STREAM, 0 );
  235.   wait_player_msg_sec( 1 );
  236.   // only when strem is closed, we may send the stop command to push server
  237.   if( operatingMode == playback )
  238.     {
  239.       // send command to server
  240.       string cmd = string( _stop_cmd_ ) + "n";
  241.       if( -1 == sendto( serverSocketOut, (void*) cmd.c_str(), 
  242. cmd.length(), 0, (const sockaddr *)
  243. &pushServControlAddr.addr,
  244. sizeof( pushServControlAddr.addr ) ))
  245. throw PlayerException( __LINE__, "send: %s", sys_errlist[ errno ] );
  246.     }
  247.   playerStateMustBe = STATE_STOPPED;
  248. }
  249. void PlaybackWindow::timeout()
  250. {
  251.   check_player_msgs();
  252. }
  253. void PlaybackWindow::check_player_msgs()
  254. {
  255.   while( a2g_check_msg( &Player ) )
  256.     {
  257.       PMP_HEADER      msg;            /* to hold message header from Player */
  258.       PMP_DATA_A2G    data;           /* to hold message data from Player */
  259.   
  260.       if (a2g_read_msg(&Player, &msg, &data) == -1 )
  261. {
  262.   if( cleanup_flag == false )
  263.     throw PlayerException( __LINE__, "error reading msg from player" );
  264.   else
  265.     return;
  266. }
  267.   
  268.       switch ((A2G_COM)msg.type) 
  269. {
  270. case A2G_COM_STATE:
  271.   playerState = data.data_generic.data_int;
  272.   switch( playerState )
  273.     {
  274.     case STATE_STOPPED:
  275.       session.put_message( 1, "Player: stopped" );
  276.       break;
  277.     case STATE_PAUSED:
  278.       session.put_message( 1, "Player: paused" );
  279.       if( playerStateMustBe == STATE_PLAYING )
  280. g2a_send_int( &Player, COM_STATE, 0, STATE_PLAYING );
  281.       break;
  282.     case STATE_PLAYING:
  283.       session.put_message( 1, "Player: playing" );
  284.       break;
  285.     default:
  286.       session.put_message( 1, "Player: unknown player state" );
  287.       playerState = STATE_STOPPED;
  288.       break;
  289.     }
  290.   break; // state
  291. case A2G_COM_SIH_FEEDBACK:
  292.   process_feedback( &data, msg.size );
  293.   break;
  294. case A2G_COM_QUIT:
  295.   g2a_cleanup(&Player);
  296.   Player.pid = -1;
  297.   if( cleanup_flag == false )
  298.     throw PlayerException( __LINE__, "player quits" );
  299.   break;
  300. case A2G_COM_READY:
  301.   session.put_message( 1, "Player is ready" );
  302.   playerIsReady = true;
  303.   break;
  304. case A2G_COM_RETURN_SUCCESS:
  305.   session.put_message( 1, "got success to previous command" );
  306.   // who actually requested it?
  307.   switch( command_requested_status )
  308.     {
  309.     case plugin_link:
  310.       sih_handler_id = data.data_return_success.value;
  311.       session.put_message( 1, "got sih plugin ID: %d", sih_handler_id);
  312.       break;
  313.     case nothing:
  314.       break;
  315.     }
  316.   command_requested_status = nothing;
  317.   if( a2g_check_msg( &Player ) )
  318.     {
  319.       PMP_HEADER      original_msg;
  320.       PMP_DATA_G2A    original_data;
  321.       if (a2g_read_msg(&Player, &original_msg, (PMP_DATA_A2G *)
  322.        &original_data) == -1)
  323. throw PlayerException( __LINE__, 
  324.        "a2g_read_msg: error reading msg");
  325.       char buf[PMP_PRINT_MSG_BUF_SIZE];
  326.       session.put_message
  327. ( 1, "received msg: %s", pmp_print_msg
  328.   (&original_msg, (const char *)&original_data, buf));
  329.     }
  330.   break;
  331. case A2G_COM_RETURN_FAILURE:
  332.   session.put_message( 1, "got failure to previous command" );
  333.   break;
  334. case A2G_COM_NUMBER_OF_FRAMES_PLAYED:
  335.   session.put_message( 1, "%d frames played",
  336.        data.data_generic.data_int );
  337.   break;
  338. case A2G_COM_ERROR_IN_STREAM:
  339.   session.put_message( 0, "found serious error in stream that "
  340.        "triggered concealment, %d",
  341.        data.data_generic.data_int );
  342.   break;
  343.   
  344. case A2G_COM_AUDIO_ENABLED:
  345.   session.put_message( 1, "audio enabled" );
  346.   break;
  347. case A2G_COM_AUDIO_VOLUME:
  348.   session.put_message( 4, "got audio volume" );
  349.   break;
  350. case A2G_COM_AUDIO_BALANCE:
  351.   session.put_message( 2, "got audio balance" );
  352.   break;
  353. case A2G_COM_AUDIO_PORTS_AVAILABLE:
  354.   session.put_message( 2, "available audio output ports %d",
  355.        data.data_generic.data_int );
  356.   break;
  357. case A2G_COM_AUDIO_PORTS_SELECTED:
  358.   session.put_message( 2, "selected audio output ports %d",
  359.        data.data_generic.data_int );
  360.   break;
  361. case A2G_COM_INPUT_FILE_SIZE:
  362.   session.put_message( 1, "file size %d",
  363.        data.data_generic.data_int );
  364.   break;
  365. case A2G_COM_INPUT_FD:
  366.   session.put_message( 1, "file descriptor read %d",
  367.        data.data_generic.data_int );
  368.   break;
  369. case A2G_COM_DISPLAY_READY:
  370.   session.put_message( 2, "display ready (e.g. after seek, etc)" );
  371.   break;
  372. case A2G_COM_PACK_INFOS:
  373.   session.put_message( 2, "(PMP_DATA_PACK_INFOS) new mux bitrate"
  374.        "in bit/sec or 0 if VBR" );
  375.   break;
  376. case A2G_COM_FRAME_INFOS:
  377.   session.put_message( 2, "frame infos for new frame displayed. "
  378.        "This message is received only when state "
  379.        "!= STATE_PLAYING." );
  380.   break;
  381. case A2G_COM_VIDEO_DITHER_TYPE:
  382.   session.put_message( 2, "got dither type" );
  383.   break;
  384. case A2G_COM_VIDEO_DITHER_ENABLE:
  385.   session.put_message( 2, "dither enabled" );
  386.   break;
  387. case A2G_COM_AUDIO_INFOS:
  388.   session.put_message( 2, "got new audio infos" );
  389.   break;
  390. case A2G_COM_VIDEO_INFOS:
  391.   session.put_message( 2, "got new video infos" );
  392.   break;
  393. case A2G_COM_GOP_INFOS:
  394.   session.put_message( 2, "gop infos" );
  395.   break;
  396. case A2G_COM_AUDIO_PID:
  397.   session.put_message( 2, "got audio pid" );
  398.   break;
  399. case A2G_COM_AUDIO_NO_CONTROLS_SELECT:
  400.   session.put_message( 2, "audio mixer not available" );
  401.   break;
  402. case A2G_COM_CHALLENGE:
  403.   session.put_message( 2, "authentification request" );
  404.   break;
  405. case A2G_COM_X_EVENT:
  406.   session.put_message( 2, "get X event" );
  407.   break;
  408. case A2G_COM_SYSTEMS_END_CODE:
  409.   session.put_message( 1, "found a systems-end-code" );
  410.   break;
  411. case A2G_COM_STREAM_END:
  412.   session.put_message( 1, "video decoder reached end of stream" );
  413.   break;
  414. default:
  415.   session.put_message( 1, "unknown message %d from player", 
  416.        (A2G_COM)msg.type );
  417.   break;
  418. }
  419.     }
  420. }
  421. void PlaybackWindow::process_feedback( const PMP_DATA_A2G *data, int size )
  422. {
  423.   mtv_msg *msg = (mtv_msg *)( (char *)data + sizeof(SIH_HANDLER_ID) );
  424.   int handler_id = data->data_sih_handler_id;
  425.   switch( msg->type )
  426.     {
  427.     case mtv_msg::debug:
  428.       {
  429. session.put_message( msg->int_param, (const char *)&msg->data );
  430.       }
  431.       break;
  432.     case mtv_msg::timestamp:
  433.       {
  434. // scroll the widow to reflect current text position
  435. session.select_text_line_by_time( currentMovieId, 
  436.   (( struct timeval * )msg->data)
  437.   ->tv_sec );
  438.       }
  439.       break;
  440.     default:
  441.       session.put_message( 1, "received A2G_COM_SIH_FEEDBACK, "
  442.    "SIH id %d, size = %u, type %d, data = "%s"n", 
  443.    handler_id, size, msg->type, data);
  444.     }
  445. }
  446. void PlaybackWindow::wait_player_msg_sec( int sec )
  447. {
  448.   fd_set set;
  449.   FD_ZERO( &set );
  450.   if( Player.receive_channel.msg_pipe[PMP_FD_READ] != -1 )
  451.     FD_SET( Player.receive_channel.msg_pipe[PMP_FD_READ], &set );
  452.   else
  453.     throw PlayerException( __LINE__, "player pipe is -1" );
  454.     
  455.   Timeval diff( sec, 0 );
  456.   int ret = select( FD_SETSIZE, &set, NULL, NULL, &diff.val() );
  457.   if( ret == -1 )
  458.     throw PlayerException( __LINE__, "select: %s", sys_errlist[ errno ] );
  459.   if( !ret )  // timeval expires
  460.     {
  461.       session.put_message( 1, "time expired, no messages" ); 
  462.       return;
  463.     }
  464.   // we got it
  465.   check_player_msgs();
  466. }
  467. // --------------------------------------------------------------------------
  468. PlayerException::PlayerException( int line, const char *format, ... )
  469. {
  470.   va_list ap;
  471.   va_start( ap, format );
  472.   init( format, ap );
  473.   // report this problem...
  474.   fprintf( stderr, "Throws PlayerException, line %d:", line );
  475.   vfprintf( stderr, format, ap );
  476.   fprintf( stderr, "n" );
  477.   va_end( ap );
  478. }