static void video_seek(rtmp_t *r, media_pipe_t *mp, media_buf_t **mbp, int64_t pos, const char *txt) { if(pos < 0) pos = 0; TRACE(TRACE_DEBUG, "Video", "seek %s to %.2f", txt, pos / 1000000.0); RTMP_SendSeek(r->r, pos / 1000); r->seekpos_video = pos; r->seekpos_audio = pos; mp->mp_video.mq_seektarget = pos; mp->mp_audio.mq_seektarget = pos; mp_flush(mp); if(mbp != NULL && *mbp != NULL) { media_buf_free_unlocked(mp, *mbp); *mbp = NULL; } prop_set_float(prop_create(mp->mp_prop_root, "seektime"), pos / 1000000.0); }
static event_t * sendpkt(rtmp_t *r, media_queue_t *mq, media_codec_t *mc, int64_t dts, int64_t pts, const void *data, size_t size, int skip, int dt, int duration, int drive_clock) { event_t *e = NULL; media_buf_t *mb = media_buf_alloc_unlocked(r->mp, size); mb->mb_data_type = dt; mb->mb_duration = duration; mb->mb_cw = media_codec_ref(mc); mb->mb_drive_clock = drive_clock; mb->mb_dts = dts; mb->mb_pts = pts; mb->mb_skip = skip; memcpy(mb->mb_data, data, size); do { if(mb == NULL || (e = mb_enqueue_with_events(r->mp, mq, mb)) == NULL) { mb = NULL; break; } e = rtmp_process_event(r, e, &mb); } while(e == NULL); if(mb != NULL) media_buf_free_unlocked(r->mp, mb); return e; }
static void seekflush(media_pipe_t *mp, media_buf_t **mbp) { mp_flush(mp, 0); if(*mbp != NULL && *mbp != MB_SPECIAL_EOF) media_buf_free_unlocked(mp, *mbp); *mbp = NULL; }
static void seekflush(media_pipe_t *mp, media_buf_t **mbp) { mp_flush(mp, 0); if(*mbp != NULL) { media_buf_free_unlocked(mp, *mbp); *mbp = NULL; } }
static event_t * dvd_media_enqueue(dvd_player_t *dp, media_queue_t *mq, media_codec_t *cw, int data_type, void *data, int datalen, int rate, int64_t dts, int64_t pts) { media_buf_t *mb = media_buf_alloc_unlocked(dp->dp_mp, datalen); event_t *e; AVCodecContext *ctx = cw->codec_ctx; mb->mb_cw = media_codec_ref(cw); mb->mb_data_type = data_type; mb->mb_duration = cw->codec_ctx->ticks_per_frame * 1000000LL * av_q2d(ctx->time_base); mb->mb_aspect_override = dp->dp_aspect_override; mb->mb_disable_deinterlacer = 1; mb->mb_dts = dts; mb->mb_pts = pts; if(pts != AV_NOPTS_VALUE && data_type == MB_VIDEO) { if(dp->dp_time_pts_delta == AV_NOPTS_VALUE) { int64_t t = av_rescale_q(dvdnav_get_current_time(dp->dp_dvdnav), mpeg_tc, AV_TIME_BASE_Q); dp->dp_time_pts_delta = pts - t; } mb->mb_time = pts - dp->dp_time_pts_delta; } else { mb->mb_time = AV_NOPTS_VALUE; } memcpy(mb->mb_data, data, datalen); do { if((e = mb_enqueue_with_events(dp->dp_mp, mq, mb)) == NULL) { mb = NULL; break; } e = dvd_process_event(dp, e); } while(e == NULL); if(mb != NULL) media_buf_free_unlocked(dp->dp_mp, mb); return e; }
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; }
static event_t * fa_gme_playfile_internal(media_pipe_t *mp, const 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->mp_audio.mq_stream = 0; mp_configure(mp, MP_PLAY_CAPS_PAUSE | MP_PLAY_CAPS_SEEK, MP_BUFFER_SHALLOW, 0); 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_pts = gme_tell(emu) * 1000; mb->mb_drive_clock = 1; if(!registered_play && mb->mb_pts > 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_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); return e; }
/** * Video decoder thread */ static void * vd_thread(void *aux) { video_decoder_t *vd = aux; media_pipe_t *mp = vd->vd_mp; media_queue_t *mq = &mp->mp_video; media_buf_t *mb; media_buf_t *cur = NULL; media_codec_t *mc, *mc_current = NULL; int run = 1; int reqsize = -1; int size; int reinit = 0; const media_buf_meta_t *mbm = NULL; vd->vd_frame = av_frame_alloc(); hts_mutex_lock(&mp->mp_mutex); while(run) { if(mbm != vd->vd_reorder_current) { mbm = vd->vd_reorder_current; hts_mutex_unlock(&mp->mp_mutex); vd->vd_estimated_duration = mbm->mbm_duration; video_decoder_set_current_time(vd, mbm->mbm_pts, mbm->mbm_epoch, mbm->mbm_delta); hts_mutex_lock(&mp->mp_mutex); continue; } media_buf_t *ctrl = TAILQ_FIRST(&mq->mq_q_ctrl); media_buf_t *data = TAILQ_FIRST(&mq->mq_q_data); media_buf_t *aux = TAILQ_FIRST(&mq->mq_q_aux); if(ctrl != NULL) { TAILQ_REMOVE(&mq->mq_q_ctrl, ctrl, mb_link); mb = ctrl; } else if(aux != NULL && aux->mb_pts < vd->vd_subpts + 1000000LL) { if(vd->vd_hold) { hts_cond_wait(&mq->mq_avail, &mp->mp_mutex); continue; } TAILQ_REMOVE(&mq->mq_q_aux, aux, mb_link); mb = aux; } else if(cur != NULL) { if(vd->vd_hold) { hts_cond_wait(&mq->mq_avail, &mp->mp_mutex); continue; } mb = cur; goto retry_current; } else if(data != NULL) { if(vd->vd_hold) { hts_cond_wait(&mq->mq_avail, &mp->mp_mutex); continue; } TAILQ_REMOVE(&mq->mq_q_data, data, mb_link); mp_check_underrun(mp); mb = data; } else { hts_cond_wait(&mq->mq_avail, &mp->mp_mutex); continue; } mq->mq_packets_current--; mp->mp_buffer_current -= mb->mb_size; mq_update_stats(mp, mq); hts_cond_signal(&mp->mp_backpressure); retry_current: mc = mb->mb_cw; if(mb->mb_data_type == MB_VIDEO && mc->decode_locked != NULL) { if(mc != mc_current) { if(mc_current != NULL) media_codec_deref(mc_current); mc_current = media_codec_ref(mc); prop_set_int(mq->mq_prop_too_slow, 0); } size = mb->mb_size; mq->mq_no_data_interest = 1; if(mc->decode_locked(mc, vd, mq, mb)) { cur = mb; hts_cond_wait(&mq->mq_avail, &mp->mp_mutex); continue; } mq->mq_no_data_interest = 0; cur = NULL; update_vbitrate(mp, mq, size, vd); media_buf_free_locked(mp, mb); continue; } hts_mutex_unlock(&mp->mp_mutex); switch(mb->mb_data_type) { case MB_CTRL_EXIT: run = 0; break; case MB_CTRL_PAUSE: vd->vd_hold = 1; break; case MB_CTRL_PLAY: vd->vd_hold = 0; break; case MB_CTRL_FLUSH: if(cur != NULL) { media_buf_free_unlocked(mp, cur); mq->mq_no_data_interest = 0; cur = NULL; } vd_init_timings(vd); vd->vd_interlaced = 0; hts_mutex_lock(&mp->mp_overlay_mutex); video_overlay_flush_locked(mp, 1); dvdspu_flush_locked(mp); hts_mutex_unlock(&mp->mp_overlay_mutex); mp->mp_video_frame_deliver(NULL, mp->mp_video_frame_opaque); if(mc_current != NULL) { mc_current->flush(mc_current, vd); media_codec_deref(mc_current); mc_current = NULL; } mp->mp_video_frame_deliver(NULL, mp->mp_video_frame_opaque); if(mp->mp_seek_video_done != NULL) mp->mp_seek_video_done(mp); break; case MB_VIDEO: if(mc != mc_current) { if(mc_current != NULL) media_codec_deref(mc_current); mc_current = media_codec_ref(mc); prop_set_int(mq->mq_prop_too_slow, 0); } if(reinit) { if(mc->reinit != NULL) mc->reinit(mc); reinit = 0; } size = mb->mb_size; mc->decode(mc, vd, mq, mb, reqsize); update_vbitrate(mp, mq, size, vd); reqsize = -1; break; case MB_CTRL_REQ_OUTPUT_SIZE: reqsize = mb->mb_data32; break; case MB_CTRL_REINITIALIZE: reinit = 1; break; case MB_CTRL_RECONFIGURE: mb->mb_cw->reconfigure(mc, mb->mb_frame_info); break; #if ENABLE_DVD case MB_DVD_RESET_SPU: hts_mutex_lock(&mp->mp_overlay_mutex); vd->vd_spu_curbut = 1; dvdspu_flush_locked(mp); hts_mutex_unlock(&mp->mp_overlay_mutex); break; case MB_CTRL_DVD_HILITE: vd->vd_spu_curbut = mb->mb_data32; vd->vd_spu_repaint = 1; break; case MB_DVD_PCI: memcpy(&vd->vd_pci, mb->mb_data, sizeof(pci_t)); vd->vd_spu_repaint = 1; event_payload_t *ep = event_create(EVENT_DVD_PCI, sizeof(event_t) + sizeof(pci_t)); memcpy(ep->payload, mb->mb_data, sizeof(pci_t)); mp_enqueue_event(mp, &ep->h); event_release(&ep->h); break; case MB_DVD_CLUT: dvdspu_decode_clut(vd->vd_dvd_clut, (void *)mb->mb_data); break; case MB_DVD_SPU: dvdspu_enqueue(mp, mb->mb_data, mb->mb_size, vd->vd_dvd_clut, 0, 0, mb->mb_pts); break; #endif case MB_CTRL_DVD_SPU2: dvdspu_enqueue(mp, mb->mb_data+72, mb->mb_size-72, (void *)mb->mb_data, ((const uint32_t *)mb->mb_data)[16], ((const uint32_t *)mb->mb_data)[17], mb->mb_pts); break; case MB_SUBTITLE: if(vd->vd_ext_subtitles == NULL && mb->mb_stream == mq->mq_stream2) video_overlay_decode(mp, mb); break; case MB_CTRL_FLUSH_SUBTITLES: hts_mutex_lock(&mp->mp_overlay_mutex); video_overlay_flush_locked(mp, 1); hts_mutex_unlock(&mp->mp_overlay_mutex); break; case MB_CTRL_EXT_SUBTITLE: if(vd->vd_ext_subtitles != NULL) subtitles_destroy(vd->vd_ext_subtitles); // Steal subtitle from the media_buf vd->vd_ext_subtitles = (void *)mb->mb_data; mb->mb_data = NULL; hts_mutex_lock(&mp->mp_overlay_mutex); video_overlay_flush_locked(mp, 1); hts_mutex_unlock(&mp->mp_overlay_mutex); break; default: abort(); } hts_mutex_lock(&mp->mp_mutex); media_buf_free_locked(mp, mb); } if(cur != NULL) media_buf_free_locked(mp, cur); mq->mq_no_data_interest = 0; hts_mutex_unlock(&mp->mp_mutex); if(mc_current != NULL) media_codec_deref(mc_current); if(vd->vd_ext_subtitles != NULL) subtitles_destroy(vd->vd_ext_subtitles); av_frame_free(&vd->vd_frame); return NULL; }