Changeset 2822

Show
Ignore:
Timestamp:
2007-05-31 04:42:27 (2 years ago)
Author:
tahn
Message:

Added timing synchronisation between audio and video and correct timing for streams that don't have any audio.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • liboggplay/trunk/plugin/plugin_gui_mac.c

    r2801 r2822  
    6767 
    6868typedef struct { 
    69   AGLContext      agl_context; 
    70   GLuint          texture; 
    71   PluginOggFrame  frame_data; 
    72   int             port_width; 
    73   int             port_height; 
    74   float           x_scale; 
    75   float           y_scale; 
    76   float           x_shift; 
    77   float           y_shift; 
     69  AGLContext        agl_context; 
     70  GLuint            texture; 
     71  PluginOggFrame    frame_data; 
     72  int               port_width; 
     73  int               port_height; 
     74  float             x_scale; 
     75  float             y_scale; 
     76  float             x_shift; 
     77  float             y_shift; 
     78  sa_stream_t     * audio_handle; 
    7879} ThreadData; 
    7980 
     
    8788#endif 
    8889 
    89 #define AUDIO_MODE        SA_MODE_WRONLY 
    90 #define AUDIO_FORMAT      SA_PCM_FORMAT_S16_LE 
    91 #define AUDIO_RATE        44100 
    92 #define AUDIO_CHANNELS    2 
     90#define VIDEO_RATE            25 
     91#define AUDIO_MODE            SA_MODE_WRONLY 
     92#define AUDIO_FORMAT          SA_PCM_FORMAT_S16_LE 
     93#define AUDIO_RATE            44100 
     94#define AUDIO_CHANNELS        2 
     95#define AUDIO_BYTES_PER_CH    sizeof(short) 
     96 
     97 
     98/* 
     99 * ----------------------------------------------------------------------------- 
     100 * Helper functions 
     101 * ----------------------------------------------------------------------------- 
     102 */ 
     103 
     104static void 
     105millisleep(long ms) { 
     106  struct timespec ts = {0, ms * 1000000}; 
     107  nanosleep(&ts, NULL); 
     108
     109 
     110static int64_t 
     111get_curr_time() { 
     112  struct timeval tv; 
     113  gettimeofday(&tv, NULL); 
     114  return (int64_t)tv.tv_sec * 1000 + (int64_t)tv.tv_usec / 1000; 
     115
    93116 
    94117 
     
    249272 
    250273 
     274static void 
     275init_audio(PluginWindowInfo *info, ThreadData *td) { 
     276 
     277  if ( 
     278    sa_stream_create_pcm(&td->audio_handle, NULL, AUDIO_MODE, 
     279        AUDIO_FORMAT, AUDIO_RATE, AUDIO_CHANNELS) != SA_SUCCESS 
     280    || 
     281    sa_stream_open(td->audio_handle) != SA_SUCCESS 
     282  ) { 
     283    sa_stream_destroy(td->audio_handle); 
     284    td->audio_handle = NULL; 
     285  } 
     286} 
     287 
     288 
     289static void 
     290shutdown_audio(PluginWindowInfo *info, ThreadData *td) { 
     291 
     292  if (td->audio_handle != NULL) { 
     293    sa_stream_destroy(td->audio_handle); 
     294    td->audio_handle = NULL; 
     295  } 
     296} 
     297 
     298 
    251299/* 
    252300 * Entry point for the display thread. Note that since AGL and GL are not 
     
    257305 
    258306  PluginWindowInfo  * info = (PluginWindowInfo *)_info; 
    259   sa_stream_t       * audio_handle; 
    260   int                 audio_result; 
    261307  ThreadData          td; 
     308  int64_t             playback_target = 0; 
     309  int64_t             time_ref = -1; 
     310  int64_t             cur_time; 
     311  int64_t             offset; 
     312  int64_t             bytes; 
    262313 
    263314  /* 
     
    266317   */ 
    267318  init_agl_and_gl(info, &td); 
    268   audio_result = sa_stream_create_pcm(&audio_handle, NULL, AUDIO_MODE, 
    269                      AUDIO_FORMAT, AUDIO_RATE, AUDIO_CHANNELS); 
    270   if (audio_result == SA_SUCCESS) { 
    271     audio_result = sa_stream_open(audio_handle); 
    272   } 
     319  init_audio(info, &td); 
    273320 
    274321  /* 
     
    279326    /* 
    280327     * If the user resizes or scrolls the browser window, we need to update 
    281      * our clipping area and GL transform to match. This is also used to 
    282      * hide our display when the user changes tabs (the clip region is set 
    283      * to zero). 
     328     * our clipping area and GL transform to match. This is also used to hide 
     329     * our display when the user changes tabs (the clip region is set to zero). 
    284330     */ 
    285331    if (info->display_resized) { 
     
    291337 
    292338    /* 
    293      * Swap the media source on the fly. 
     339     * Swap the media source on the fly. Shut down and restart the audio to 
     340     * (a) flush the audio buffer, and (b) handle changes in the audio data 
     341     * format. 
    294342     */ 
    295343    if (info->new_oggplay_handle != NULL) { 
     
    298346      info->oggplay_handle     = info->new_oggplay_handle; 
    299347      info->new_oggplay_handle = NULL; 
     348      shutdown_audio(info, &td); 
     349      init_audio(info, &td); 
     350      playback_target = 0; 
     351      time_ref = -1; 
    300352      SEM_SIGNAL(info->oggplay_replace_sem); 
    301353    } 
    302354 
    303355    /* 
    304      * Grab the next frame of data and feed it to GL and the audio stream. 
    305      * In the unlikely event of a failure in the audio output, please remain 
    306      * seated while we disable audio and keep displaying the video. 
     356     * Grab the next frame's worth of data, and update the GL video display. 
     357     * We want to do this even when there isn't any video data; if we don't, 
     358     * the media area can retain pixels from the previous display (e.g. if you 
     359     * follow a link to get to a page with embedded video, the old page can 
     360     * remain visible in the media area). 
    307361     */ 
    308362    get_oggplay_frame(info->oggplay_handle, &td.frame_data); 
    309363    convert_oggplay_frame(info->oggplay_handle, &td.frame_data, RGB); 
    310364    update_gl_output(info, &td); 
    311     if (audio_result == SA_SUCCESS) { 
    312       audio_result = sa_stream_write(audio_handle, 
    313                         td.frame_data.samples, td.frame_data.size); 
    314     } 
     365 
     366    bool have_video = (td.frame_data.frame != NULL); 
     367    bool have_audio = (td.frame_data.samples != NULL && td.frame_data.size > 0); 
     368 
     369    /* 
     370     * Spin until we have some data (but wait for a bit first so we don't 
     371     * chew too many cycles). 
     372     */ 
     373    if (!have_video && !have_audio) { 
     374      offset = 5; 
     375      goto loop_end; 
     376    } 
     377 
     378    /* 
     379     * If the media source doesn't contain any audio, we shouldn't keep 
     380     * the audio output unit open. 
     381     */ 
     382    if (!have_audio && td.audio_handle != NULL) { 
     383      shutdown_audio(info, &td); 
     384      playback_target = 0; 
     385      time_ref = -1; 
     386    } 
     387 
     388    /* 
     389     * If we have some audio data, send it to the output device and calculate 
     390     * our current playing time using the number of audio bytes consumed. 
     391     */ 
     392    cur_time = -1; 
     393    if (have_audio && td.audio_handle != NULL) { 
     394      if ( 
     395        sa_stream_write(td.audio_handle, td.frame_data.samples, td.frame_data.size) == SA_SUCCESS 
     396        && 
     397        sa_stream_get_position(td.audio_handle, SA_POSITION_WRITE_SOFTWARE, &bytes) == SA_SUCCESS 
     398      ) { 
     399        cur_time = bytes * 1000 / (AUDIO_RATE * AUDIO_BYTES_PER_CH * AUDIO_CHANNELS); 
     400      } else { 
     401        /* 
     402         * Something's gone wrong with the audio; revert to using the system 
     403         * clock for controlling our playback time. 
     404         */ 
     405        shutdown_audio(info, &td); 
     406        playback_target = 0; 
     407        time_ref = -1; 
     408      } 
     409    } 
     410 
     411    /* 
     412     * If we can't use the audio device to determine our playback time, track 
     413     * it with the system clock. Use the time at which the first frame is 
     414     * displayed as a reference. 
     415     */ 
     416    if (cur_time == -1) { 
     417      if (time_ref == -1) { 
     418        time_ref = get_curr_time(); 
     419      } 
     420      cur_time = get_curr_time() - time_ref; 
     421    } 
     422 
     423    /* 
     424     * playback_target is the time at which we want to display the next video 
     425     * frame. 
     426     */ 
     427    offset = playback_target - cur_time; 
     428    if (offset < 0) { 
     429      offset = 0; 
     430    } 
     431    playback_target += 1000 / VIDEO_RATE; 
     432 
     433loop_end: 
    315434    free_oggplay_frame(info->oggplay_handle, &td.frame_data); 
    316  
    317     // temporary timing mechanism 
    318     struct timespec t = {0, 30000000}; 
    319     nanosleep(&t, NULL); 
    320   } 
     435    millisleep(offset); 
     436 
     437  } /* while (!info->shutdown_gui) */ 
    321438 
    322439  /* 
    323440   * Shut down and let the main thread know we're done. 
    324441   */ 
    325   sa_stream_destroy(audio_handle); 
     442  shutdown_audio(info, &td); 
    326443  shutdown_agl_and_gl(info, &td); 
    327  
    328444  info->shutdown_gui = 0; 
    329445  pthread_exit(NULL); 
     
    411527 
    412528void 
     529pause_gui(void *gui_handle) { 
     530//todo 
     531} 
     532 
     533 
     534void 
     535start_gui(void *gui_handle) { 
     536//todo 
     537} 
     538 
     539 
     540void 
    413541shut_gui(void *gui_handle) { 
    414542 
     
    421549  info->shutdown_gui = 1; 
    422550  while (info->shutdown_gui) { 
    423     struct timespec t = {0, 10000}; 
    424     nanosleep(&t, NULL); 
     551    millisleep(1); 
    425552  } 
    426553