예제 #1
0
void
video_decoder_stop(video_decoder_t *vd)
{
  media_pipe_t *mp = vd->vd_mp;

  mp_send_cmd_head(mp, &mp->mp_video, MB_CTRL_EXIT);

  hts_thread_join(&vd->vd_decoder_thread);
  mp_ref_dec(vd->vd_mp);
  vd->vd_mp = NULL;
}
예제 #2
0
/**
 * Destroy an audio decoder pipeline.
 *
 * Called from media.c
 */
void
audio_decoder_destroy(audio_decoder_t *ad)
{
  mp_send_cmd_head(ad->ad_mp, &ad->ad_mp->mp_audio, MB_CTRL_EXIT);

  hts_thread_join(&ad->ad_tid);

  audio_decoder_flush(ad);

  hfree(ad->ad_outbuf, AVCODEC_MAX_AUDIO_FRAME_SIZE * 2);
 
  free(ad);
}
예제 #3
0
파일: fa_audio.c 프로젝트: tommie/showtime
static event_t *
openspc_play(media_pipe_t *mp, AVIOContext *avio, char *errbuf, size_t errlen)
{
  media_queue_t *mq = &mp->mp_audio;
#error fa_fsize can return -1 .. deal with it
  size_t r, siz = fa_fsize(fh);
  uint8_t *buf = malloc(siz);
  media_buf_t *mb = NULL;
  event_t *e;
  int hold = 0, lost_focus = 0;
  int sample = 0;
  unsigned int duration = INT32_MAX;

  mp_set_playstatus_by_hold(mp, hold, NULL);

  mp->mp_audio.mq_stream = 0;

  fa_seek(fh, 0, SEEK_SET);
  r = fa_read(fh, buf, siz);
  fa_close(fh);
  
  if(r != siz) {
    free(buf);
    snprintf(errbuf, errlen, "openspc: Unable to read file");
    return NULL;
  }

  if(OSPC_Init(buf, siz)) {
    free(buf);
    snprintf(errbuf, errlen, "openspc: Unable to initialize file");
    return NULL;
  }

  if(!memcmp("v0.30", buf + 0x1c, 4) && buf[0x23] == 0x1a) {
    char str[4];
    memcpy(str, buf + 0xa9, 3);
    str[3] = 0;
    duration = atoi(str) * 32000;
  }

  mp_set_play_caps(mp, MP_PLAY_CAPS_PAUSE);

  mp_become_primary(mp);

  while(1) {

    if(mb == NULL) {

      if(sample > duration) {
	while((e = mp_wait_for_empty_queues(mp, 0)) != NULL) {
	  if(event_is_type(e, EVENT_PLAYQUEUE_JUMP) ||
	     event_is_action(e, ACTION_PREV_TRACK) ||
	     event_is_action(e, ACTION_NEXT_TRACK) ||
	     event_is_action(e, ACTION_STOP)) {
	    mp_flush(mp, 0);
	    break;
	  }
	  event_release(e);
	}
	if(e == NULL)
	  e = event_create_type(EVENT_EOF);
	break;
       }

      mb = media_buf_alloc();
      mb->mb_data_type = MB_AUDIO;
      mb->mb_size = sizeof(int16_t) * 2048 * 2;
      mb->mb_data = malloc(mb->mb_size);
      mb->mb_size = OSPC_Run(-1, mb->mb_data, mb->mb_size);

      mb->mb_channels = 2;
      mb->mb_rate = 32000;

      mb->mb_time = sample * 1000000LL / mb->mb_rate;
      sample += 2048;
    }

    if((e = mb_enqueue_with_events(mp, mq, mb)) == NULL) {
      mb = NULL; /* Enqueue succeeded */
      continue;
    }

    if(event_is_type(e, EVENT_PLAYQUEUE_JUMP)) {
      mp_flush(mp, 0);
      break;
    } else if(event_is_action(e, ACTION_PLAYPAUSE) ||
	      event_is_action(e, ACTION_PLAY) ||
	      event_is_action(e, ACTION_PAUSE)) {

      hold = action_update_hold_by_event(hold, e);
      mp_send_cmd_head(mp, mq, hold ? MB_CTRL_PAUSE : MB_CTRL_PLAY);
      lost_focus = 0;
      mp_set_playstatus_by_hold(mp, hold, NULL);

    } else if(event_is_type(e, EVENT_MP_NO_LONGER_PRIMARY)) {

      hold = 1;
      lost_focus = 1;
      mp_send_cmd_head(mp, mq, MB_CTRL_PAUSE);
      mp_set_playstatus_by_hold(mp, hold, e->e_payload);

    } else if(event_is_type(e, EVENT_MP_IS_PRIMARY)) {

      if(lost_focus) {
	hold = 0;
	lost_focus = 0;
	mp_send_cmd_head(mp, mq, MB_CTRL_PLAY);
	mp_set_playstatus_by_hold(mp, hold, NULL);
      }

    } else if(event_is_type(e, EVENT_INTERNAL_PAUSE)) {

      hold = 1;
      lost_focus = 0;
      mp_send_cmd_head(mp, mq, MB_CTRL_PAUSE);
      mp_set_playstatus_by_hold(mp, hold, e->e_payload);

    } else if(event_is_action(e, ACTION_PREV_TRACK) ||
	      event_is_action(e, ACTION_NEXT_TRACK) ||
	      event_is_action(e, ACTION_STOP)) {
      mp_flush(mp, 0);
      break;
    }
    event_release(e);
  }

  free(buf);
  return e;
}
예제 #4
0
파일: fa_audio.c 프로젝트: tommie/showtime
event_t *
be_file_playaudio(const char *url, media_pipe_t *mp,
		  char *errbuf, size_t errlen, int hold, const char *mimetype)
{
  AVFormatContext *fctx;
  AVCodecContext *ctx;
  AVPacket pkt;
  media_format_t *fw;
  int i, r, si;
  media_buf_t *mb = NULL;
  media_queue_t *mq;
  event_ts_t *ets;
  int64_t ts, seekbase = 0;
  media_codec_t *cw;
  event_t *e;
  int lost_focus = 0;
  int registered_play = 0;

  mp_set_playstatus_by_hold(mp, hold, NULL);

  fa_handle_t *fh = fa_open_ex(url, errbuf, errlen, FA_BUFFERED_SMALL, NULL);
  if(fh == NULL)
    return NULL;

  // First we need to check for a few other formats
#if ENABLE_LIBOPENSPC || ENABLE_LIBGME

  uint8_t pb[128];
  size_t psiz;
  
  psiz = fa_read(fh, pb, sizeof(pb));
  if(psiz < sizeof(pb)) {
    fa_close(fh);
    snprintf(errbuf, errlen, "Fill too small");
    return NULL;
  }

#if ENABLE_LIBGME
  if(*gme_identify_header(pb))
    return fa_gme_playfile(mp, fh, errbuf, errlen, hold, url);
#endif

#if ENABLE_LIBOPENSPC
  if(!memcmp(pb, "SNES-SPC700 Sound File Data", 27))
    return openspc_play(mp, fh, errbuf, errlen);
#endif

#endif

  
  AVIOContext *avio = fa_libav_reopen(fh);

  if(avio == NULL) {
    fa_close(fh);
    return NULL;
  }

  if((fctx = fa_libav_open_format(avio, url, 
				  errbuf, errlen, mimetype)) == NULL) {
    fa_libav_close(avio);
    return NULL;
  }

  TRACE(TRACE_DEBUG, "Audio", "Starting playback of %s", url);

  mp_configure(mp, MP_PLAY_CAPS_SEEK | MP_PLAY_CAPS_PAUSE,
	       MP_BUFFER_SHALLOW);

  mp->mp_audio.mq_stream = -1;
  mp->mp_video.mq_stream = -1;

  fw = media_format_create(fctx);

  cw = NULL;
  for(i = 0; i < fctx->nb_streams; i++) {
    ctx = fctx->streams[i]->codec;

    if(ctx->codec_type != AVMEDIA_TYPE_AUDIO)
      continue;

    cw = media_codec_create(ctx->codec_id, 0, fw, ctx, NULL, mp);
    mp->mp_audio.mq_stream = i;
    break;
  }
  
  if(cw == NULL) {
    media_format_deref(fw);
    snprintf(errbuf, errlen, "Unable to open codec");
    return NULL;
  }

  mp_become_primary(mp);
  mq = &mp->mp_audio;

  while(1) {

    /**
     * Need to fetch a new packet ?
     */
    if(mb == NULL) {
      
      mp->mp_eof = 0;
      r = av_read_frame(fctx, &pkt);
      if(r == AVERROR(EAGAIN))
	continue;
      
      if(r == AVERROR_EOF || r == AVERROR(EIO)) {
	mb = MB_SPECIAL_EOF;
	mp->mp_eof = 1;
	continue;
      }
      
      if(r != 0) {
	char msg[100];
	fa_ffmpeg_error_to_txt(r, msg, sizeof(msg));
	TRACE(TRACE_ERROR, "Audio", "Playback error: %s", msg);

	while((e = mp_wait_for_empty_queues(mp)) != NULL) {
	  if(event_is_type(e, EVENT_PLAYQUEUE_JUMP) ||
	     event_is_action(e, ACTION_PREV_TRACK) ||
	     event_is_action(e, ACTION_NEXT_TRACK) ||
	     event_is_action(e, ACTION_STOP)) {
	    mp_flush(mp, 0);
	    break;
	  }
	  event_release(e);
	}
	if(e == NULL)
	  e = event_create_type(EVENT_EOF);
	break;
      }

      si = pkt.stream_index;

      if(si != mp->mp_audio.mq_stream) {
	av_free_packet(&pkt);
	continue;
      }



      mb = media_buf_alloc_unlocked(mp, pkt.size);
      mb->mb_data_type = MB_AUDIO;

      mb->mb_pts      = rescale(fctx, pkt.pts,      si);
      mb->mb_dts      = rescale(fctx, pkt.dts,      si);
      mb->mb_duration = rescale(fctx, pkt.duration, si);

      mb->mb_cw = media_codec_ref(cw);

      /* Move the data pointers from ffmpeg's packet */

      mb->mb_stream = pkt.stream_index;

      memcpy(mb->mb_data, pkt.data, pkt.size);

      if(mb->mb_pts != AV_NOPTS_VALUE) {
	if(fctx->start_time == AV_NOPTS_VALUE)
	  mb->mb_time = mb->mb_pts;
	else
	  mb->mb_time = mb->mb_pts - fctx->start_time;
      } else
	mb->mb_time = AV_NOPTS_VALUE;

      mb->mb_send_pts = 1;

      av_free_packet(&pkt);
    }

    /*
     * Try to send the buffer.  If mb_enqueue() returns something we
     * catched an event instead of enqueueing the buffer. In this case
     * 'mb' will be left untouched.
     */

    if(mb == MB_SPECIAL_EOF) {
      // We have reached EOF, drain queues
      e = mp_wait_for_empty_queues(mp);
      
      if(e == NULL) {
	e = event_create_type(EVENT_EOF);
	break;
      }

    } else if((e = mb_enqueue_with_events(mp, mq, mb)) == NULL) {
      mb = NULL; /* Enqueue succeeded */
      continue;
    }      

    if(event_is_type(e, EVENT_PLAYQUEUE_JUMP)) {

      mp_flush(mp, 0);
      break;

    } else if(event_is_type(e, EVENT_CURRENT_PTS)) {

      ets = (event_ts_t *)e;
      seekbase = ets->ts;

      if(registered_play == 0) {
	if(ets->ts - fctx->start_time > METADB_AUDIO_PLAY_THRESHOLD) {
	  registered_play = 1;
	  metadb_register_play(url, 1, CONTENT_AUDIO);
	}
      }

    } else if(event_is_type(e, EVENT_SEEK)) {

      ets = (event_ts_t *)e;
      ts = ets->ts + fctx->start_time;
      if(ts < fctx->start_time)
	ts = fctx->start_time;
      av_seek_frame(fctx, -1, ts, AVSEEK_FLAG_BACKWARD);
      seekflush(mp, &mb);
      
    } else if(event_is_action(e, ACTION_SEEK_FAST_BACKWARD)) {

      av_seek_frame(fctx, -1, seekbase - 60000000, AVSEEK_FLAG_BACKWARD);
      seekflush(mp, &mb);

    } else if(event_is_action(e, ACTION_SEEK_BACKWARD)) {

      av_seek_frame(fctx, -1, seekbase - 15000000, AVSEEK_FLAG_BACKWARD);
      seekflush(mp, &mb);

    } else if(event_is_action(e, ACTION_SEEK_FAST_FORWARD)) {

      av_seek_frame(fctx, -1, seekbase + 60000000, 0);
      seekflush(mp, &mb);

    } else if(event_is_action(e, ACTION_SEEK_FORWARD)) {

      av_seek_frame(fctx, -1, seekbase + 15000000, 0);
      seekflush(mp, &mb);
#if 0
    } else if(event_is_action(e, ACTION_RESTART_TRACK)) {

      av_seek_frame(fctx, -1, 0, AVSEEK_FLAG_BACKWARD);
      seekflush(mp, &mb);
#endif
    } else if(event_is_action(e, ACTION_PLAYPAUSE) ||
	      event_is_action(e, ACTION_PLAY) ||
	      event_is_action(e, ACTION_PAUSE)) {

      hold = action_update_hold_by_event(hold, e);
      mp_send_cmd_head(mp, mq, hold ? MB_CTRL_PAUSE : MB_CTRL_PLAY);
      lost_focus = 0;
      mp_set_playstatus_by_hold(mp, hold, NULL);

    } else if(event_is_type(e, EVENT_MP_NO_LONGER_PRIMARY)) {

      hold = 1;
      lost_focus = 1;
      mp_send_cmd_head(mp, mq, MB_CTRL_PAUSE);
      mp_set_playstatus_by_hold(mp, hold, e->e_payload);

    } else if(event_is_type(e, EVENT_MP_IS_PRIMARY)) {

      if(lost_focus) {
	hold = 0;
	lost_focus = 0;
	mp_send_cmd_head(mp, mq, MB_CTRL_PLAY);
	mp_set_playstatus_by_hold(mp, hold, NULL);
      }

    } else if(event_is_type(e, EVENT_INTERNAL_PAUSE)) {

      hold = 1;
      lost_focus = 0;
      mp_send_cmd_head(mp, mq, MB_CTRL_PAUSE);
      mp_set_playstatus_by_hold(mp, hold, e->e_payload);

    } else if(event_is_action(e, ACTION_PREV_TRACK) ||
	      event_is_action(e, ACTION_NEXT_TRACK) ||
	      event_is_action(e, ACTION_STOP)) {
      mp_flush(mp, 0);
      break;
    }
    event_release(e);
  }

  if(mb != NULL && mb != MB_SPECIAL_EOF)
    media_buf_free_unlocked(mp, mb);

  media_codec_deref(cw);
  media_format_deref(fw);

  if(hold) { 
    // If we were paused, release playback again.
    mp_send_cmd(mp, mq, MB_CTRL_PLAY);
    mp_set_playstatus_by_hold(mp, 0, NULL);
  }

  return e;
}
예제 #5
0
static int64_t
vdpau_newframe(glw_video_t *gv, video_decoder_t *vd0, int flags)
{
  glw_root_t *gr = gv->w.glw_root;
  vdpau_dev_t *vd = gr->gr_be.gbr_vdpau_dev;
  media_pipe_t *mp = gv->gv_mp;
  VdpStatus st;
  glw_video_surface_t *s;
  int64_t pts = AV_NOPTS_VALUE;

  if(flags & GLW_REINITIALIZE_VDPAU) {

    int i;
    for(i = 0; i < GLW_VIDEO_MAX_SURFACES; i++)
      gv->gv_surfaces[i].gvs_vdpau_surface = VDP_INVALID_HANDLE;

    gv->gv_vdpau_pq = VDP_INVALID_HANDLE;
    gv->gv_vdpau_pqt = VDP_INVALID_HANDLE;
    
    gv->gv_cfg_cur.gvc_valid = 0;
    
    mp_send_cmd_head(mp, &mp->mp_video, MB_REINITIALIZE);

    drain(gv, &gv->gv_displaying_queue);
    drain(gv, &gv->gv_decoded_queue);
    hts_cond_signal(&gv->gv_avail_queue_cond);
    
    return AV_NOPTS_VALUE;
  }

  /* Remove frames from displaying queue if they are idle and push
   * back to the decoder 
   */
  while((s = TAILQ_FIRST(&gv->gv_displaying_queue)) != NULL) {
    VdpPresentationQueueStatus qs;
    VdpTime t;

    gv->gv_vdpau_running = 1;

    st = vd->vdp_presentation_queue_query_surface_status(gv->gv_vdpau_pq,
							 s->gvs_vdpau_surface,
							 &qs, &t);
    if(st != VDP_STATUS_OK || qs == VDP_PRESENTATION_QUEUE_STATUS_IDLE) {
      TAILQ_REMOVE(&gv->gv_displaying_queue, s, gvs_link);
      TAILQ_INSERT_TAIL(&gv->gv_avail_queue, s, gvs_link);
      hts_cond_signal(&gv->gv_avail_queue_cond);
    } else {
      break;
    }
  }


  while((s = TAILQ_FIRST(&gv->gv_decoded_queue)) != NULL) {
    int64_t delta = gr->gr_frameduration * 2;
    int64_t aclock, d;
    pts = s->gvs_pts;

    hts_mutex_lock(&mp->mp_clock_mutex);
    aclock = mp->mp_audio_clock + gr->gr_frame_start - 
      mp->mp_audio_clock_realtime + mp->mp_avdelta;
    hts_mutex_unlock(&mp->mp_clock_mutex);

    d = s->gvs_pts - aclock;

    if(s->gvs_pts == AV_NOPTS_VALUE || d < -5000000LL || d > 5000000LL)
      pts = gv->gv_nextpts;

    if(pts != AV_NOPTS_VALUE && (pts - delta) >= aclock)
      break;

    st = vd->vdp_presentation_queue_display(gv->gv_vdpau_pq,
					    s->gvs_vdpau_surface, 
					    0, 0, 0);
    if(pts != AV_NOPTS_VALUE)
      gv->gv_nextpts = pts + s->gvs_duration;

    gv->gv_fwidth  = s->gvs_width;
    gv->gv_fheight = s->gvs_height;

    TAILQ_REMOVE(&gv->gv_decoded_queue, s, gvs_link);
    TAILQ_INSERT_TAIL(&gv->gv_displaying_queue, s, gvs_link);
  }
  return pts;
}
예제 #6
0
파일: sid.c 프로젝트: pvc1/showtime
/**
 * Play given track.
 *
 * We only expect this to be called from the playqueue system.
 */
static event_t *
be_sid2player_play(const char *url0, media_pipe_t *mp,
                   char *errbuf, size_t errlen, int hold,
                   const char *mimetype)
{
    media_queue_t *mq = &mp->mp_audio;
    char *url, *p;
    int sample = 0, lost_focus = 0;
    media_buf_t *mb = NULL;
    event_t *e;
    int subsong;


    void *player;
    void *data;
    struct fa_stat fs;


    url0 += strlen("sidplayer:");

    url = mystrdupa(url0);
    p = strrchr(url, '/');
    if(p == NULL) {
        snprintf(errbuf, errlen, "Invalid filename");
        return NULL;
    }

    *p++= 0;
    subsong = atoi(p);

    if((data = fa_quickload(url, &fs, NULL, errbuf, errlen)) == NULL)
        return NULL;

    player = sidcxx_load(data, fs.fs_size, subsong, errbuf, errlen);
    free(data);
    if(player == NULL)
        return NULL;

    mp_set_playstatus_by_hold(mp, hold, NULL);
    mp->mp_audio.mq_stream = 0;
    mp_configure(mp, MP_PLAY_CAPS_PAUSE, MP_BUFFER_NONE);
    mp_become_primary(mp);


    while(1) {

        if(mb == NULL) {

            mb = media_buf_alloc();
            mb->mb_data_type = MB_AUDIO;
            mb->mb_channels = 2;
            mb->mb_size = sizeof(int16_t) * CHUNK_SIZE * mb->mb_channels;
            mb->mb_data = malloc(mb->mb_size);
            mb->mb_rate = 44100;

            mb->mb_time = sample * 1000000LL / mb->mb_rate;
            sample += CHUNK_SIZE;

            int16_t *samples = mb->mb_data;

            sidcxx_play(player, samples,
                        CHUNK_SIZE * sizeof(int16_t) * mb->mb_channels);

            // Crossmix 25%

            int i, l, r, L, R;
            for(i = 0; i < CHUNK_SIZE; i++) {
                l = samples[i * 2 + 0];
                r = samples[i * 2 + 1];

                L = 3 * l + r * 2;
                R = 3 * r + l * 2;

                L = L / 4;
                R = R / 4;

                if(L > 32767)
                    L = 32767;
                if(L < -32768)
                    L = -32768;

                if(R > 32767)
                    R = 32767;
                if(R < -32768)
                    R = -32768;

                samples[i * 2 + 0] = L;
                samples[i * 2 + 1] = R;
            }
        }

        if((e = mb_enqueue_with_events(mp, mq, mb)) == NULL) {
            mb = NULL; /* Enqueue succeeded */
            continue;
        }

        if(event_is_type(e, EVENT_PLAYQUEUE_JUMP)) {
            mp_flush(mp, 0);
            break;
        } else if(event_is_action(e, ACTION_PLAYPAUSE) ||
                  event_is_action(e, ACTION_PLAY) ||
                  event_is_action(e, ACTION_PAUSE)) {

            hold = action_update_hold_by_event(hold, e);
            mp_send_cmd_head(mp, mq, hold ? MB_CTRL_PAUSE : MB_CTRL_PLAY);
            lost_focus = 0;
            mp_set_playstatus_by_hold(mp, hold, NULL);

        } else if(event_is_type(e, EVENT_MP_NO_LONGER_PRIMARY)) {

            hold = 1;
            lost_focus = 1;
            mp_send_cmd_head(mp, mq, MB_CTRL_PAUSE);
            mp_set_playstatus_by_hold(mp, hold, e->e_payload);

        } else if(event_is_type(e, EVENT_MP_IS_PRIMARY)) {

            if(lost_focus) {
                hold = 0;
                lost_focus = 0;
                mp_send_cmd_head(mp, mq, MB_CTRL_PLAY);
                mp_set_playstatus_by_hold(mp, hold, NULL);
            }

        } else if(event_is_type(e, EVENT_INTERNAL_PAUSE)) {

            hold = 1;
            lost_focus = 0;
            mp_send_cmd_head(mp, mq, MB_CTRL_PAUSE);
            mp_set_playstatus_by_hold(mp, hold, e->e_payload);

        } else if(event_is_action(e, ACTION_PREV_TRACK) ||
                  event_is_action(e, ACTION_NEXT_TRACK) ||
                  event_is_action(e, ACTION_STOP)) {
            mp_flush(mp, 0);
            break;
        }
        event_release(e);
    }

    sidcxx_stop(player);

    return e;
}
예제 #7
0
event_t *
be_file_playaudio(const char *url, media_pipe_t *mp,
		  char *errbuf, size_t errlen, int hold)
{
  AVFormatContext *fctx;
  AVIOContext *avio;
  AVCodecContext *ctx;
  AVPacket pkt;
  media_format_t *fw;
  int i, r, si;
  media_buf_t *mb = NULL;
  media_queue_t *mq;
  event_ts_t *ets;
  int64_t ts, pts4seek = 0;
  media_codec_t *cw;
  event_t *e;
  int lost_focus = 0;

  mp_set_playstatus_by_hold(mp, hold, NULL);

  if((avio = fa_libav_open(url, 32768, errbuf, errlen)) == NULL)
    return NULL;


  // First we need to check for a few other formats
#if ENABLE_LIBOPENSPC || ENABLE_LIBGME

  uint8_t pb[128];
  size_t psiz;
  
  psiz = avio_read(avio, pb, sizeof(pb));
  if(psiz < sizeof(pb)) {
    fa_libav_close(avio);
    snprintf(errbuf, errlen, "Fill too small");
    return NULL;
  }

#if ENABLE_LIBGME
  if(*gme_identify_header(pb))
    return fa_gme_playfile(mp, avio, errbuf, errlen, hold);
#endif

#if ENABLE_LIBOPENSPC
  if(!memcmp(pb, "SNES-SPC700 Sound File Data", 27))
    return openspc_play(mp, avio, errbuf, errlen);
#endif

#endif

  if((fctx = fa_libav_open_format(avio, url, errbuf, errlen)) == NULL) {
    fa_libav_close(avio);
    return NULL;
  }

  TRACE(TRACE_DEBUG, "Audio", "Starting playback of %s", url);

  mp_set_play_caps(mp, MP_PLAY_CAPS_SEEK | MP_PLAY_CAPS_PAUSE);

  mp->mp_audio.mq_stream = -1;
  mp->mp_video.mq_stream = -1;

  fw = media_format_create(fctx);

  cw = NULL;
  for(i = 0; i < fctx->nb_streams; i++) {
    ctx = fctx->streams[i]->codec;

    if(ctx->codec_type != AVMEDIA_TYPE_AUDIO)
      continue;

    cw = media_codec_create(ctx->codec_id, 0, fw, ctx, NULL, mp);
    mp->mp_audio.mq_stream = i;
    break;
  }
  
  if(cw == NULL) {
    media_format_deref(fw);
    snprintf(errbuf, errlen, "Unable to open codec");
    return NULL;
  }

  mp_become_primary(mp);
  mq = &mp->mp_audio;

  while(1) {

    /**
     * Need to fetch a new packet ?
     */
    if(mb == NULL) {

      if((r = av_read_frame(fctx, &pkt)) < 0) {

	while((e = mp_wait_for_empty_queues(mp, 0)) != NULL) {
	  if(event_is_type(e, EVENT_PLAYQUEUE_JUMP) ||
	     event_is_action(e, ACTION_PREV_TRACK) ||
	     event_is_action(e, ACTION_NEXT_TRACK) ||
	     event_is_action(e, ACTION_STOP)) {
	    mp_flush(mp, 0);
	    break;
	  }
	  event_release(e);
	}
	if(e == NULL)
	  e = event_create_type(EVENT_EOF);
	break;
      }

      si = pkt.stream_index;

      if(si == mp->mp_audio.mq_stream) {
	/* Current audio stream */
	mb = media_buf_alloc();
	mb->mb_data_type = MB_AUDIO;

      } else {
	/* Check event queue ? */
	av_free_packet(&pkt);
	continue;
      }


      mb->mb_pts      = rescale(fctx, pkt.pts,      si);
      mb->mb_dts      = rescale(fctx, pkt.dts,      si);
      mb->mb_duration = rescale(fctx, pkt.duration, si);

      mb->mb_cw = media_codec_ref(cw);

      /* Move the data pointers from ffmpeg's packet */

      mb->mb_stream = pkt.stream_index;

      av_dup_packet(&pkt);

      mb->mb_data = pkt.data;
      pkt.data = NULL;

      mb->mb_size = pkt.size;
      pkt.size = 0;

      if(mb->mb_pts != AV_NOPTS_VALUE) {
	if(fctx->start_time == AV_NOPTS_VALUE)
	  mb->mb_time = mb->mb_pts;
	else
	  mb->mb_time = mb->mb_pts - fctx->start_time;
	pts4seek = mb->mb_time;
      } else
	mb->mb_time = AV_NOPTS_VALUE;


      av_free_packet(&pkt);
    }

    /*
     * Try to send the buffer.  If mb_enqueue() returns something we
     * catched an event instead of enqueueing the buffer. In this case
     * 'mb' will be left untouched.
     */

    if((e = mb_enqueue_with_events(mp, mq, mb)) == NULL) {
      mb = NULL; /* Enqueue succeeded */
      continue;
    }      

    if(event_is_type(e, EVENT_PLAYQUEUE_JUMP)) {

      mp_flush(mp, 0);
      break;

    } else if(event_is_type(e, EVENT_SEEK)) {

      ets = (event_ts_t *)e;
      ts = ets->pts + fctx->start_time;
      if(ts < fctx->start_time)
	ts = fctx->start_time;
      av_seek_frame(fctx, -1, ts, AVSEEK_FLAG_BACKWARD);
      seekflush(mp, &mb);
      
    } else if(event_is_action(e, ACTION_SEEK_FAST_BACKWARD)) {

      av_seek_frame(fctx, -1, pts4seek - 60000000, AVSEEK_FLAG_BACKWARD);
      seekflush(mp, &mb);

    } else if(event_is_action(e, ACTION_SEEK_BACKWARD)) {

      av_seek_frame(fctx, -1, pts4seek - 15000000, AVSEEK_FLAG_BACKWARD);
      seekflush(mp, &mb);

    } else if(event_is_action(e, ACTION_SEEK_FAST_FORWARD)) {

      av_seek_frame(fctx, -1, pts4seek + 60000000, 0);
      seekflush(mp, &mb);

    } else if(event_is_action(e, ACTION_SEEK_FORWARD)) {

      av_seek_frame(fctx, -1, pts4seek + 15000000, 0);
      seekflush(mp, &mb);
#if 0
    } else if(event_is_action(e, ACTION_RESTART_TRACK)) {

      av_seek_frame(fctx, -1, 0, AVSEEK_FLAG_BACKWARD);
      seekflush(mp, &mb);
#endif
    } else if(event_is_action(e, ACTION_PLAYPAUSE) ||
	      event_is_action(e, ACTION_PLAY) ||
	      event_is_action(e, ACTION_PAUSE)) {

      hold = action_update_hold_by_event(hold, e);
      mp_send_cmd_head(mp, mq, hold ? MB_CTRL_PAUSE : MB_CTRL_PLAY);
      lost_focus = 0;
      mp_set_playstatus_by_hold(mp, hold, NULL);

    } else if(event_is_type(e, EVENT_MP_NO_LONGER_PRIMARY)) {

      hold = 1;
      lost_focus = 1;
      mp_send_cmd_head(mp, mq, MB_CTRL_PAUSE);
      mp_set_playstatus_by_hold(mp, hold, e->e_payload);

    } else if(event_is_type(e, EVENT_MP_IS_PRIMARY)) {

      if(lost_focus) {
	hold = 0;
	lost_focus = 0;
	mp_send_cmd_head(mp, mq, MB_CTRL_PLAY);
	mp_set_playstatus_by_hold(mp, hold, NULL);
      }

    } else if(event_is_type(e, EVENT_INTERNAL_PAUSE)) {

      hold = 1;
      lost_focus = 0;
      mp_send_cmd_head(mp, mq, MB_CTRL_PAUSE);
      mp_set_playstatus_by_hold(mp, hold, e->e_payload);

    } else if(event_is_action(e, ACTION_PREV_TRACK) ||
	      event_is_action(e, ACTION_NEXT_TRACK) ||
	      event_is_action(e, ACTION_STOP)) {
      mp_flush(mp, 0);
      break;
    }
    event_release(e);
  }

  if(mb != NULL)
    media_buf_free(mb);

  media_codec_deref(cw);
  media_format_deref(fw);

  if(hold) { 
    // If we were paused, release playback again.
    mp_send_cmd(mp, mq, MB_CTRL_PLAY);
    mp_set_playstatus_by_hold(mp, 0, NULL);
  }

  return e;
}
예제 #8
0
static event_t *
rtmp_process_event(rtmp_t *r, event_t *e, media_buf_t **mbp)
{
  media_pipe_t *mp = r->mp;

  if(event_is_type(e, EVENT_EXIT) ||
     event_is_type(e, EVENT_PLAY_URL) ||
     event_is_action(e, ACTION_SKIP_FORWARD))
    return e;
  
  if(event_is_action(e, ACTION_SKIP_BACKWARD)) {
    if(mp->mp_seek_base < MP_SKIP_LIMIT) {
      return e;
    }
    video_seek(r, mp, mbp, 0, "direct");
  }
  
  if(event_is_action(e, ACTION_PLAYPAUSE) ||
     event_is_action(e, ACTION_PLAY) ||
     event_is_action(e, ACTION_PAUSE)) {
    
    r->hold = action_update_hold_by_event(r->hold, e);
    mp_send_cmd_head(mp, &mp->mp_video, r->hold ? MB_CTRL_PAUSE : MB_CTRL_PLAY);
    mp_send_cmd_head(mp, &mp->mp_audio, r->hold ? MB_CTRL_PAUSE : MB_CTRL_PLAY);
    mp_set_playstatus_by_hold(mp, r->hold, NULL);
    

  } else if(event_is_type(e, EVENT_INTERNAL_PAUSE)) {
    
    r->hold = 1;
    mp_send_cmd_head(mp, &mp->mp_video, MB_CTRL_PAUSE);
    mp_send_cmd_head(mp, &mp->mp_audio, MB_CTRL_PAUSE);
    mp_set_playstatus_by_hold(mp, r->hold, e->e_payload);

  } else if(event_is_type(e, EVENT_CURRENT_TIME)) {
    event_ts_t *ets = (event_ts_t *)e;
    
    int sec = ets->ts / 1000000;

    if(sec != r->restartpos_last && r->can_seek) {
      r->restartpos_last = sec;
      metadb_set_video_restartpos(r->canonical_url, mp->mp_seek_base / 1000);
    }

  } else if(r->can_seek && event_is_type(e, EVENT_SEEK)) {
    event_ts_t *ets = (event_ts_t *)e;

    video_seek(r, mp, mbp, ets->ts, "direct");

  } else if(event_is_action(e, ACTION_STOP)) {
    mp_set_playstatus_stop(mp);
  } else if(event_is_type(e, EVENT_SELECT_SUBTITLE_TRACK)) {
    event_select_track_t *est = (event_select_track_t *)e;
    prop_set_string(mp->mp_prop_subtitle_track_current, est->id);
    if(!strcmp(est->id, "sub:off")) {
      mp_load_ext_sub(mp, NULL);
      } else {
      mp_load_ext_sub(mp, est->id);
    }

  }
  event_release(e);
  return NULL;
}
예제 #9
0
static event_t *
fa_gme_playfile_internal(media_pipe_t *mp, void *fh,
			 char *errbuf, size_t errlen, int hold, int track)
{
  media_queue_t *mq = &mp->mp_audio;
  Music_Emu *emu;
  gme_err_t err;
  char *buf;
  int lost_focus = 0;
  size_t size, r;
  int sample_rate = 48000;
  media_buf_t *mb = NULL;
  event_t *e;

  size = fa_fsize(fh);

  buf = malloc(size);
  r = fa_read(fh, buf, size);

  if(r != size) {
    snprintf(errbuf, errlen, "Unable to read file");
    free(buf);
    return NULL;
  }

  err = gme_open_data(buf, size, &emu, sample_rate);
  free(buf);
  if(err != NULL) {
    snprintf(errbuf, errlen, "Unable to load file -- %s", err);
    return NULL;
  }

  gme_start_track(emu, track);

  mp_set_playstatus_by_hold(mp, hold, NULL);
  mp->mp_audio.mq_stream = 0;
  mp_set_play_caps(mp, MP_PLAY_CAPS_PAUSE | MP_PLAY_CAPS_SEEK);
  mp_become_primary(mp);
  

  while(1) {

    if(gme_track_ended(emu)) {
      e = event_create_type(EVENT_EOF);
      break;
    }

    if(mb == NULL) {
      mb = media_buf_alloc();
      mb->mb_data_type = MB_AUDIO;
      mb->mb_channels = 2;
      mb->mb_size = sizeof(int16_t) * CHUNK_SIZE * mb->mb_channels;
      mb->mb_data = malloc(mb->mb_size);
      mb->mb_rate = sample_rate;
      mb->mb_time = gme_tell(emu) * 1000;
      gme_play(emu, CHUNK_SIZE * mb->mb_channels, mb->mb_data);
    }

    if((e = mb_enqueue_with_events(mp, mq, mb)) == NULL) {
      mb = NULL; /* Enqueue succeeded */
      continue;
    }
    if(event_is_type(e, EVENT_PLAYQUEUE_JUMP)) {
      mp_flush(mp, 0);
      break;


    } else if(event_is_type(e, EVENT_SEEK)) {

      event_ts_t *ets = (event_ts_t *)e;
      gme_seek(emu, ets->pts / 1000);
      seekflush(mp, &mb);
      
    } else if(event_is_action(e, ACTION_SEEK_FAST_BACKWARD)) {

      deltaseek(mp, &mb, emu, -60000);

    } else if(event_is_action(e, ACTION_SEEK_BACKWARD)) {

      deltaseek(mp, &mb, emu, -15000);

    } else if(event_is_action(e, ACTION_SEEK_FAST_FORWARD)) {

      deltaseek(mp, &mb, emu, 60000);

    } else if(event_is_action(e, ACTION_SEEK_FORWARD)) {

      deltaseek(mp, &mb, emu, 15000);

    } else if(event_is_action(e, ACTION_PLAYPAUSE) ||
	      event_is_action(e, ACTION_PLAY) ||
	      event_is_action(e, ACTION_PAUSE)) {

      hold = action_update_hold_by_event(hold, e);
      mp_send_cmd_head(mp, mq, hold ? MB_CTRL_PAUSE : MB_CTRL_PLAY);
      lost_focus = 0;
      mp_set_playstatus_by_hold(mp, hold, NULL);

    } else if(event_is_type(e, EVENT_MP_NO_LONGER_PRIMARY)) {

      hold = 1;
      lost_focus = 1;
      mp_send_cmd_head(mp, mq, MB_CTRL_PAUSE);
      mp_set_playstatus_by_hold(mp, hold, e->e_payload);

    } else if(event_is_type(e, EVENT_MP_IS_PRIMARY)) {

      if(lost_focus) {
	hold = 0;
	lost_focus = 0;
	mp_send_cmd_head(mp, mq, MB_CTRL_PLAY);
	mp_set_playstatus_by_hold(mp, hold, NULL);
      }

    } else if(event_is_type(e, EVENT_INTERNAL_PAUSE)) {

      hold = 1;
      lost_focus = 0;
      mp_send_cmd_head(mp, mq, MB_CTRL_PAUSE);
      mp_set_playstatus_by_hold(mp, hold, e->e_payload);

    } else if(event_is_action(e, ACTION_PREV_TRACK) ||
	      event_is_action(e, ACTION_NEXT_TRACK) ||
	      event_is_action(e, ACTION_STOP)) {
      mp_flush(mp, 0);
      break;
    }
    event_release(e);
  }  

  gme_delete(emu);

  if(mb != NULL)
    media_buf_free(mb);

  if(hold) { 
    // If we were paused, release playback again.
    mp_send_cmd(mp, mq, MB_CTRL_PLAY);
    mp_set_playstatus_by_hold(mp, 0, NULL);
  }
  return e;
}
예제 #10
0
파일: rtmp.c 프로젝트: Daisho/showtime
static event_t *
rtmp_process_event(rtmp_t *r, event_t *e, media_buf_t **mbp)
{
  media_pipe_t *mp = r->mp;

  if(event_is_type(e, EVENT_EXIT) ||
     event_is_type(e, EVENT_PLAY_URL))
    return e;

  if(event_is_action(e, ACTION_PLAYPAUSE) ||
     event_is_action(e, ACTION_PLAY) ||
     event_is_action(e, ACTION_PAUSE)) {
    
    r->hold = action_update_hold_by_event(r->hold, e);
    mp_send_cmd_head(mp, &mp->mp_video, r->hold ? MB_CTRL_PAUSE : MB_CTRL_PLAY);
    mp_send_cmd_head(mp, &mp->mp_audio, r->hold ? MB_CTRL_PAUSE : MB_CTRL_PLAY);
    mp_set_playstatus_by_hold(mp, r->hold, NULL);
    r->lost_focus = 0;
    

  } else if(event_is_type(e, EVENT_MP_NO_LONGER_PRIMARY)) {

    r->hold = 1;
    r->lost_focus = 1;
    mp_send_cmd_head(mp, &mp->mp_video, MB_CTRL_PAUSE);
    mp_send_cmd_head(mp, &mp->mp_audio, MB_CTRL_PAUSE);
    mp_set_playstatus_by_hold(mp, r->hold, e->e_payload);
    
  } else if(event_is_type(e, EVENT_MP_IS_PRIMARY)) {
    
    if(r->lost_focus) {
      r->hold = 0;
      r->lost_focus = 0;
      mp_send_cmd_head(mp, &mp->mp_video, MB_CTRL_PLAY);
      mp_send_cmd_head(mp, &mp->mp_audio, MB_CTRL_PLAY);
      mp_set_playstatus_by_hold(mp, r->hold, NULL);
    }
    
  } else if(event_is_type(e, EVENT_INTERNAL_PAUSE)) {
    
    r->hold = 1;
    r->lost_focus = 0;
    mp_send_cmd_head(mp, &mp->mp_video, MB_CTRL_PAUSE);
    mp_send_cmd_head(mp, &mp->mp_audio, MB_CTRL_PAUSE);
    mp_set_playstatus_by_hold(mp, r->hold, e->e_payload);

  } else if(event_is_type(e, EVENT_CURRENT_PTS)) {
    event_ts_t *ets = (event_ts_t *)e;
    
    r->seekbase = ets->ts;
    
    int sec = r->seekbase / 1000000;

    if(sec != r->restartpos_last && r->can_seek) {
      r->restartpos_last = sec;
      metadb_set_video_restartpos(r->canonical_url, r->seekbase / 1000);
    }

  } else if(r->can_seek && event_is_type(e, EVENT_SEEK)) {
    event_ts_t *ets = (event_ts_t *)e;

    r->epoch++;
      
    r->seekbase = video_seek(r, mp, mbp, ets->ts, 1, "direct");

  } else if(r->can_seek && event_is_action(e, ACTION_SEEK_FAST_BACKWARD)) {

    r->seekbase = video_seek(r, mp, mbp, r->seekbase - 60000000, 1, "-60s");

  } else if(r->can_seek && event_is_action(e, ACTION_SEEK_BACKWARD)) {

    r->seekbase = video_seek(r, mp, mbp, r->seekbase - 15000000, 1, "-15s");

  } else if(r->can_seek && event_is_action(e, ACTION_SEEK_FORWARD)) {

    r->seekbase = video_seek(r, mp, mbp, r->seekbase + 15000000, 1, "+15s");

  } else if(r->can_seek && event_is_action(e, ACTION_SEEK_FAST_FORWARD)) {

    r->seekbase = video_seek(r, mp, mbp, r->seekbase + 60000000, 1, "+60s");

  } else if(event_is_action(e, ACTION_STOP)) {
    mp_set_playstatus_stop(mp);
  } else if(event_is_type(e, EVENT_SELECT_SUBTITLE_TRACK)) {
    event_select_track_t *est = (event_select_track_t *)e;
    prop_set_string(mp->mp_prop_subtitle_track_current, est->id);
    if(!strcmp(est->id, "sub:off")) {
      mp_load_ext_sub(mp, NULL);
      } else {
      mp_load_ext_sub(mp, est->id);
    }

  }
  event_release(e);
  return NULL;
}
예제 #11
0
파일: rtmp.c 프로젝트: aznboi665/showtime
static event_t *
rtmp_process_event(rtmp_t *r, event_t *e, media_buf_t **mbp)
{
  media_pipe_t *mp = r->mp;

  if(event_is_type(e, EVENT_EXIT) ||
     event_is_type(e, EVENT_PLAY_URL))
    return e;

  if(event_is_action(e, ACTION_PLAYPAUSE) ||
     event_is_action(e, ACTION_PLAY) ||
     event_is_action(e, ACTION_PAUSE)) {
    
    r->hold = action_update_hold_by_event(r->hold, e);
    mp_send_cmd_head(mp, &mp->mp_video, r->hold ? MB_CTRL_PAUSE : MB_CTRL_PLAY);
    mp_send_cmd_head(mp, &mp->mp_audio, r->hold ? MB_CTRL_PAUSE : MB_CTRL_PLAY);
    mp_set_playstatus_by_hold(mp, r->hold, NULL);
    r->lost_focus = 0;
    

  } else if(event_is_type(e, EVENT_MP_NO_LONGER_PRIMARY)) {

    r->hold = 1;
    r->lost_focus = 1;
    mp_send_cmd_head(mp, &mp->mp_video, MB_CTRL_PAUSE);
    mp_send_cmd_head(mp, &mp->mp_audio, MB_CTRL_PAUSE);
    mp_set_playstatus_by_hold(mp, r->hold, e->e_payload);
    
  } else if(event_is_type(e, EVENT_MP_IS_PRIMARY)) {
    
    if(r->lost_focus) {
      r->hold = 0;
      r->lost_focus = 0;
      mp_send_cmd_head(mp, &mp->mp_video, MB_CTRL_PLAY);
      mp_send_cmd_head(mp, &mp->mp_audio, MB_CTRL_PLAY);
      mp_set_playstatus_by_hold(mp, r->hold, NULL);
    }
    
  } else if(event_is_type(e, EVENT_INTERNAL_PAUSE)) {
    
    r->hold = 1;
    r->lost_focus = 0;
    mp_send_cmd_head(mp, &mp->mp_video, MB_CTRL_PAUSE);
    mp_send_cmd_head(mp, &mp->mp_audio, MB_CTRL_PAUSE);
    mp_set_playstatus_by_hold(mp, r->hold, e->e_payload);

  } else if(event_is_type(e, EVENT_CURRENT_PTS)) {
    event_ts_t *ets = (event_ts_t *)e;
    
    r->seekbase = ets->pts;
    
  } else if(event_is_type(e, EVENT_SEEK)) {
    event_ts_t *ets = (event_ts_t *)e;

    r->epoch++;
      
    r->seekbase = video_seek(r, mp, mbp, ets->pts, 1, "direct");

  } else if(event_is_action(e, ACTION_SEEK_FAST_BACKWARD)) {

    r->seekbase = video_seek(r, mp, mbp, r->seekbase - 60000000, 1, "-60s");

  } else if(event_is_action(e, ACTION_SEEK_BACKWARD)) {

    r->seekbase = video_seek(r, mp, mbp, r->seekbase - 15000000, 1, "-15s");

  } else if(event_is_action(e, ACTION_SEEK_FORWARD)) {

    r->seekbase = video_seek(r, mp, mbp, r->seekbase + 15000000, 1, "+15s");

  } else if(event_is_action(e, ACTION_SEEK_FAST_FORWARD)) {

    r->seekbase = video_seek(r, mp, mbp, r->seekbase + 60000000, 1, "+60s");

  } else if(event_is_action(e, ACTION_STOP)) {
    mp_set_playstatus_stop(mp);
  } else if(event_is_type(e, EVENT_SELECT_TRACK)) {
    event_select_track_t *est = (event_select_track_t *)e;
    prop_set_string(mp->mp_prop_subtitle_track_current, est->id);

    if(r->sub != NULL)
      subtitles_destroy(r->sub);
    
    r->sub = subtitles_load(est->id);
  }
  event_release(e);
  return NULL;
}
예제 #12
0
static event_t *
fa_gme_playfile_internal(media_pipe_t *mp, void *buf, size_t size,
			 char *errbuf, size_t errlen, int hold, int track,
			 const char *url)
{
  media_queue_t *mq = &mp->mp_audio;
  Music_Emu *emu;
  gme_err_t err;
  int sample_rate = 48000;
  media_buf_t *mb = NULL;
  event_t *e;
  int registered_play = 0;

  err = gme_open_data(buf, size, &emu, sample_rate);
  if(err != NULL) {
    snprintf(errbuf, errlen, "Unable to load file -- %s", err);
    return NULL;
  }

  gme_start_track(emu, track);

  mp_set_playstatus_by_hold(mp, hold, NULL);
  mp->mp_audio.mq_stream = 0;
  mp_configure(mp, MP_PLAY_CAPS_PAUSE | MP_PLAY_CAPS_SEEK,
	       MP_BUFFER_SHALLOW);
  mp_become_primary(mp);
  

  while(1) {

    if(gme_track_ended(emu)) {
      e = event_create_type(EVENT_EOF);
      break;
    }

    if(mb == NULL) {
      mb = media_buf_alloc_unlocked(mp, sizeof(int16_t) * CHUNK_SIZE * 2);
      mb->mb_data_type = MB_AUDIO;
      mb->mb_channels = 2;
      mb->mb_rate = sample_rate;
      mb->mb_time = gme_tell(emu) * 1000;

      if(!registered_play && mb->mb_time > METADB_AUDIO_PLAY_THRESHOLD) {
	registered_play = 1;
	metadb_register_play(url, 1, CONTENT_AUDIO);
      }

      gme_play(emu, CHUNK_SIZE * mb->mb_channels, mb->mb_data);
    }

    if((e = mb_enqueue_with_events(mp, mq, mb)) == NULL) {
      mb = NULL; /* Enqueue succeeded */
      continue;
    }
    if(event_is_type(e, EVENT_PLAYQUEUE_JUMP)) {
      mp_flush(mp, 0);
      break;


    } else if(event_is_type(e, EVENT_SEEK)) {

      event_ts_t *ets = (event_ts_t *)e;
      gme_seek(emu, ets->ts / 1000);
      seekflush(mp, &mb);
      
    } else if(event_is_action(e, ACTION_PLAYPAUSE) ||
	      event_is_action(e, ACTION_PLAY) ||
	      event_is_action(e, ACTION_PAUSE)) {

      hold = action_update_hold_by_event(hold, e);
      mp_send_cmd_head(mp, mq, hold ? MB_CTRL_PAUSE : MB_CTRL_PLAY);
      mp_set_playstatus_by_hold(mp, hold, NULL);

    } else if(event_is_type(e, EVENT_INTERNAL_PAUSE)) {

      hold = 1;
      mp_send_cmd_head(mp, mq, MB_CTRL_PAUSE);
      mp_set_playstatus_by_hold(mp, hold, e->e_payload);

    } else if(event_is_action(e, ACTION_SKIP_BACKWARD) ||
	      event_is_action(e, ACTION_SKIP_FORWARD) ||
	      event_is_action(e, ACTION_STOP)) {
      mp_flush(mp, 0);
      break;
    }
    event_release(e);
  }  

  gme_delete(emu);

  if(mb != NULL)
    media_buf_free_unlocked(mp, mb);

  if(hold) { 
    // If we were paused, release playback again.
    mp_send_cmd(mp, mq, MB_CTRL_PLAY);
    mp_set_playstatus_by_hold(mp, 0, NULL);
  }
  return e;
}