/** * Global eventsink (not tied to a specific media_pipe) */ static void media_global_eventsink(void *opaque, prop_event_t event, ...) { event_t *e; va_list ap; va_start(ap, event); if(event != PROP_EXT_EVENT) return; e = va_arg(ap, event_t *); if(event_is_type(e, EVENT_PLAYTRACK)) { #if ENABLE_PLAYQUEUE playqueue_event_handler(e); #endif } else if(media_primary != NULL) { mp_enqueue_event(media_primary, e); } else { #if ENABLE_PLAYQUEUE playqueue_event_handler(e); #endif } }
void mp_become_primary(struct media_pipe *mp) { mp_init_audio(mp); if(media_primary == mp) return; hts_mutex_lock(&media_mutex); assert(mp->mp_flags & MP_PRIMABLE); if(media_primary != NULL) { prop_set_int(media_primary->mp_prop_primary, 0); event_t *e = event_create_action(ACTION_STOP); mp_enqueue_event(media_primary, e); event_release(e); } media_primary = mp_retain(mp); prop_select(mp->mp_prop_root); prop_link(mp->mp_prop_root, media_prop_current); prop_set_int(mp->mp_prop_primary, 1); hts_mutex_unlock(&media_mutex); }
void video_playback_destroy(video_playback_t *vp) { event_t *e = event_create_type(EVENT_EXIT); mp_enqueue_event(vp->vp_mp, e); event_release(e); hts_thread_join(&vp->vp_thread); free(vp); }
void video_deliver_frame(video_decoder_t *vd, frame_buffer_type_t type, void *frame, const frame_info_t *info, int send_pts) { event_ts_t *ets; vd->vd_skip = 0; if(info->pts != AV_NOPTS_VALUE && send_pts) { ets = event_create(EVENT_CURRENT_PTS, sizeof(event_ts_t)); ets->ts = info->pts; mp_enqueue_event(vd->vd_mp, &ets->h); event_release(&ets->h); } vd->vd_frame_deliver(type, frame, info, vd->vd_opaque); video_decoder_scan_ext_sub(vd, info->pts); }
static void enable_test_thread(int on) { if(!generator_tid == !on) return; if(on) { assert(gen_mp == NULL); gen_mp = mp_create("testsignal", MP_PRIMABLE); hts_thread_create_joinable("audiotest", &generator_tid, test_generator_thread, gen_mp, THREAD_PRIO_DEMUXER); } else { event_t *e = event_create_type(EVENT_EXIT); mp_enqueue_event(gen_mp, e); event_release(e); hts_thread_join(&generator_tid); mp_shutdown(gen_mp); mp_ref_dec(gen_mp); gen_mp = NULL; generator_tid = 0; } }
static void ad_decode_buf(audio_decoder_t *ad, media_pipe_t *mp, media_queue_t *mq, media_buf_t *mb) { audio_mode_t *am = audio_mode_current; uint8_t *buf; int size, r, data_size, channels, rate, frames, delay, i; media_codec_t *cw = mb->mb_cw; AVCodecContext *ctx; int64_t pts; if(cw == NULL) { /* Raw native endian PCM */ if(ad->ad_do_flush) { ad->ad_do_flush = 0; if(mp_is_primary(mp)) ad->ad_send_flush = 1; } else if(mb->mb_time != AV_NOPTS_VALUE) mp_set_current_time(mp, mb->mb_time); if(mb->mb_send_pts && mb->mb_pts != AV_NOPTS_VALUE) { event_ts_t *ets = event_create(EVENT_CURRENT_PTS, sizeof(event_ts_t)); ets->ts = mb->mb_pts; mp_enqueue_event(mp, &ets->h); event_release(&ets->h); } frames = mb->mb_size / sizeof(int16_t) / mb->mb_channels; if(mp_is_primary(mp)) { /* Must copy if auto pipeline does multichannel upmixing */ memcpy(ad->ad_outbuf, mb->mb_data, mb->mb_size); audio_mix1(ad, am, mb->mb_channels, mb->mb_rate, 0, ad->ad_outbuf, frames, mb->mb_pts, mb->mb_epoch, mp); } else { /* We are just suppoed to be silent, emulate some kind of delay, this is not accurate, so we also set the clock epoch to zero to avoid AV sync */ mp->mp_audio_clock_epoch = 0; delay = (int64_t)frames * 1000000LL / mb->mb_rate; usleep(delay); /* XXX: Must be better */ /* Flush any packets in the pause pending queue */ audio_fifo_clear_queue(&ad->ad_hold_queue); } return; } ctx = cw->codec_ctx; if(mp_is_primary(mp)) { switch(ctx->codec_id) { case CODEC_ID_AC3: if(am->am_formats & AM_FORMAT_AC3) { audio_deliver_passthru(mb, ad, AM_FORMAT_AC3, mp); return; } break; case CODEC_ID_DTS: if(am->am_formats & AM_FORMAT_DTS) { audio_deliver_passthru(mb, ad, AM_FORMAT_DTS, mp); return; } break; default: break; } } buf = mb->mb_data; size = mb->mb_size; pts = mb->mb_pts; while(size > 0) { if(ad->ad_do_flush) { avcodec_flush_buffers(cw->codec_ctx); ad->ad_do_flush = 0; if(mp_is_primary(mp)) ad->ad_send_flush = 1; } else if(mb->mb_time != AV_NOPTS_VALUE) mp_set_current_time(mp, mb->mb_time); if(mb->mb_send_pts && mb->mb_pts != AV_NOPTS_VALUE) { event_ts_t *ets = event_create(EVENT_CURRENT_PTS, sizeof(event_ts_t)); ets->ts = pts; mp_enqueue_event(mp, &ets->h); event_release(&ets->h); } if(audio_mode_stereo_only(am) && cw->codec_id != CODEC_ID_TRUEHD && cw->codec_id != CODEC_ID_MLP) ctx->request_channels = 2; /* We can only output stereo. Ask codecs to do downmixing for us. */ else ctx->request_channels = 0; data_size = AVCODEC_MAX_AUDIO_FRAME_SIZE; AVPacket avpkt; av_init_packet(&avpkt); avpkt.data = buf; avpkt.size = size; if(am->am_float) ctx->request_sample_fmt = AV_SAMPLE_FMT_FLT; r = avcodec_decode_audio3(ctx, ad->ad_outbuf, &data_size, &avpkt); if(r < 0) break; if(mp->mp_stats) mp_set_mq_meta(mq, cw->codec, cw->codec_ctx); channels = ctx->channels; rate = ctx->sample_rate; /* Convert to signed 16bit */ if(data_size > 0) { frames = data_size / sample_fmt_to_size[ctx->sample_fmt]; if(!mp_is_primary(mp)) { mp->mp_audio_clock_epoch = 0; delay = (int64_t)(frames / channels) * 1000000LL / rate; usleep(delay); /* XXX: Must be better */ /* Flush any packets in the pause pending queue */ audio_fifo_clear_queue(&ad->ad_hold_queue); } else { /* We are the primary audio decoder == we may play, forward to the mixer stages */ /* But first, if we have any pending packets (due to a previous pause), release them */ audio_fifo_reinsert(thefifo, &ad->ad_hold_queue); if(ctx->sample_fmt == SAMPLE_FMT_FLT && am->am_float && (am->am_sample_rates & AM_SR_ANY || audio_rateflag_from_rate(rate) & am->am_sample_rates) && channels_to_format(channels) & am->am_formats) { frames /= channels; audio_deliver(ad, am, ad->ad_outbuf, channels, frames, rate, pts, mb->mb_epoch, mp, 1); } else { switch(ctx->sample_fmt) { default: return; case SAMPLE_FMT_U8: for(i = frames - 1; i >= 0; i--) ad->ad_outbuf[i] = (((uint8_t *)ad->ad_outbuf)[i] - 0x80) << 8; break; case SAMPLE_FMT_S16: break; case SAMPLE_FMT_S32: for(i = 0; i < frames; i++) ad->ad_outbuf[i] = ((int32_t *)ad->ad_outbuf)[i] >> 16; break; case SAMPLE_FMT_FLT: for(i = 0; i < frames; i++) ad->ad_outbuf[i] = rintf(((float *)ad->ad_outbuf)[i] * 32768); break; case SAMPLE_FMT_DBL: for(i = 0; i < frames; i++) ad->ad_outbuf[i] = rint(((double *)ad->ad_outbuf)[i] * 32768); break; } frames /= channels; audio_mix1(ad, am, channels, rate, ctx->channel_layout, ad->ad_outbuf, frames, pts, mb->mb_epoch, mp); } } } pts = AV_NOPTS_VALUE; buf += r; size -= r; }
static int pa_audio_start(audio_mode_t *am, audio_fifo_t *af) { pa_audio_mode_t *pam = (pa_audio_mode_t *)am; audio_buf_t *ab = NULL; size_t l, length; int64_t pts; media_pipe_t *mp; int r = 0; pa_threaded_mainloop_lock(mainloop); #if PA_API_VERSION >= 12 pa_proplist *pl = pa_proplist_new(); pa_proplist_sets(pl, PA_PROP_APPLICATION_ID, "com.lonelycoder.hts.showtime"); pa_proplist_sets(pl, PA_PROP_APPLICATION_NAME, "Showtime"); /* Create a new connection context */ pam->context = pa_context_new_with_proplist(api, "Showtime", pl); pa_proplist_free(pl); #else pam->context = pa_context_new(api, "Showtime"); #endif if(pam->context == NULL) { pa_threaded_mainloop_unlock(mainloop); return -1; } pa_context_set_state_callback(pam->context, context_state_callback, pam); /* Connect the context */ if(pa_context_connect(pam->context, NULL, 0, NULL) < 0) { TRACE(TRACE_ERROR, "PA", "pa_context_connect() failed: %s", pa_strerror(pa_context_errno(pam->context))); pa_threaded_mainloop_unlock(mainloop); return -1; } /* Need at least one packet of audio */ /* Subscribe to updates of master volume */ pam->sub_mvol = prop_subscribe(PROP_SUB_DIRECT_UPDATE, PROP_TAG_CALLBACK_FLOAT, set_mastervol, pam, PROP_TAG_ROOT, prop_mastervol, PROP_TAG_EXTERNAL_LOCK, mainloop, prop_pa_lockmgr, NULL); /* Subscribe to updates of master volume mute */ pam->sub_mute = prop_subscribe(PROP_SUB_DIRECT_UPDATE, PROP_TAG_CALLBACK_INT, set_mastermute, pam, PROP_TAG_ROOT, prop_mastermute, PROP_TAG_EXTERNAL_LOCK, mainloop, prop_pa_lockmgr, NULL); while(1) { if(ab == NULL) { pa_threaded_mainloop_unlock(mainloop); ab = af_deq2(af, 1, am); pa_threaded_mainloop_lock(mainloop); if(ab == AF_EXIT) { ab = NULL; break; } } if(pa_context_get_state(pam->context) == PA_CONTEXT_TERMINATED || pa_context_get_state(pam->context) == PA_CONTEXT_FAILED) { r = -1; break; } if(pam->stream != NULL && (pam->cur_format != ab->ab_format || pam->cur_rate != ab->ab_samplerate || pam->cur_isfloat != ab->ab_isfloat)) { stream_destroy(pam); } if(pam->stream == NULL && pa_context_get_state(pam->context) == PA_CONTEXT_READY) { /* Context is ready, but we don't have a stream yet, set it up */ stream_setup(pam, ab); } if(pam->stream == NULL) { pa_threaded_mainloop_wait(mainloop); continue; } switch(pa_stream_get_state(pam->stream)) { case PA_STREAM_UNCONNECTED: case PA_STREAM_CREATING: pa_threaded_mainloop_wait(mainloop); continue; case PA_STREAM_READY: break; case PA_STREAM_TERMINATED: case PA_STREAM_FAILED: pa_stream_unref(pam->stream); pam->stream = NULL; char msg[100]; snprintf(msg, sizeof(msg), "Audio stream disconnected from " "PulseAudio server -- %s.", pa_strerror(pam->stream_error)); mp_flush(ab->ab_mp, 0); mp_enqueue_event(ab->ab_mp, event_create_str(EVENT_INTERNAL_PAUSE, msg)); audio_fifo_purge(af, NULL, NULL); if(ab != NULL) { ab_free(ab); ab = NULL; } continue; } if(ab->ab_flush) { pa_operation *o; o = pa_stream_flush(pam->stream, NULL, NULL); if(o != NULL) pa_operation_unref(o); ab->ab_flush = 0; } l = pa_stream_writable_size(pam->stream); if(l == 0) { pa_threaded_mainloop_wait(mainloop); continue; } length = ab->ab_frames * pa_frame_size(&pam->ss) - ab->ab_tmp; if(l > length) l = length; if((pts = ab->ab_pts) != AV_NOPTS_VALUE && ab->ab_mp != NULL) { int64_t pts; pa_usec_t delay; pts = ab->ab_pts; ab->ab_pts = AV_NOPTS_VALUE; if(!pa_stream_get_latency(pam->stream, &delay, NULL)) { mp = ab->ab_mp; hts_mutex_lock(&mp->mp_clock_mutex); mp->mp_audio_clock = pts - delay; mp->mp_audio_clock_realtime = showtime_get_ts(); mp->mp_audio_clock_epoch = ab->ab_epoch; hts_mutex_unlock(&mp->mp_clock_mutex); } } pa_stream_write(pam->stream, ab->ab_data + ab->ab_tmp, l, NULL, 0LL, PA_SEEK_RELATIVE); ab->ab_tmp += l; assert(ab->ab_tmp <= ab->ab_frames * pa_frame_size(&pam->ss)); if(ab->ab_frames * pa_frame_size(&pam->ss) == ab->ab_tmp) { ab_free(ab); ab = NULL; } } prop_unsubscribe(pam->sub_mvol); prop_unsubscribe(pam->sub_mute); if(pam->stream != NULL) stream_destroy(pam); pa_threaded_mainloop_unlock(mainloop); pa_context_unref(pam->context); if(ab != NULL) { ab_free(ab); ab = NULL; } return r; }
void video_deliver_frame(video_decoder_t *vd, media_pipe_t *mp, media_queue_t *mq, AVCodecContext *ctx, AVFrame *frame, const media_buf_t *mb, int decode_time) { event_ts_t *ets; frame_info_t fi; if(mb->mb_time != AV_NOPTS_VALUE) mp_set_current_time(mp, mb->mb_time); /* Compute aspect ratio */ switch(mb->mb_aspect_override) { case 0: if(frame->pan_scan != NULL && frame->pan_scan->width != 0) { fi.dar.num = frame->pan_scan->width; fi.dar.den = frame->pan_scan->height; } else { fi.dar.num = ctx->width; fi.dar.den = ctx->height; } if(ctx->sample_aspect_ratio.num) fi.dar = av_mul_q(fi.dar, ctx->sample_aspect_ratio); break; case 1: fi.dar = (AVRational){4,3}; break; case 2: fi.dar = (AVRational){16,9}; break; } int64_t pts = mb->mb_pts; /* Compute duration and PTS of frame */ if(pts == AV_NOPTS_VALUE && mb->mb_dts != AV_NOPTS_VALUE && (ctx->has_b_frames == 0 || frame->pict_type == FF_B_TYPE)) { pts = mb->mb_dts; } int duration = mb->mb_duration; if(!vd_valid_duration(duration)) { /* duration is zero or very invalid, use duration from last output */ duration = vd->vd_estimated_duration; } if(pts == AV_NOPTS_VALUE && vd->vd_nextpts != AV_NOPTS_VALUE) pts = vd->vd_nextpts; /* no pts set, use estimated pts */ if(pts != AV_NOPTS_VALUE && vd->vd_prevpts != AV_NOPTS_VALUE) { /* we know PTS of a prior frame */ int64_t t = (pts - vd->vd_prevpts) / vd->vd_prevpts_cnt; if(vd_valid_duration(t)) { /* inter frame duration seems valid, store it */ vd->vd_estimated_duration = t; if(duration == 0) duration = t; } else if(t < 0 || t > 10000000LL) { /* PTS discontinuity, use estimated PTS from last output instead */ pts = vd->vd_nextpts; } } duration += frame->repeat_pict * duration / 2; if(pts != AV_NOPTS_VALUE) { vd->vd_prevpts = pts; vd->vd_prevpts_cnt = 0; } vd->vd_prevpts_cnt++; if(duration == 0) { TRACE(TRACE_DEBUG, "Video", "Dropping frame with duration = 0"); return; } prop_set_int(mq->mq_prop_too_slow, decode_time > duration); if(pts != AV_NOPTS_VALUE) { vd->vd_nextpts = pts + duration; if(mb->mb_send_pts) { ets = event_create(EVENT_CURRENT_PTS, sizeof(event_ts_t)); ets->ts = pts; mp_enqueue_event(mp, &ets->h); event_release(&ets->h); } } else { vd->vd_nextpts = AV_NOPTS_VALUE; } vd->vd_interlaced |= frame->interlaced_frame && !mb->mb_disable_deinterlacer; fi.width = ctx->width; fi.height = ctx->height; fi.pix_fmt = ctx->pix_fmt; fi.pts = pts; fi.epoch = mb->mb_epoch; fi.duration = duration; fi.interlaced = !!vd->vd_interlaced; fi.tff = !!frame->top_field_first; fi.prescaled = 0; fi.color_space = ctx->colorspace; fi.color_range = ctx->color_range; vd->vd_frame_deliver(frame->data, frame->linesize, &fi, vd->vd_opaque); video_decoder_scan_ext_sub(vd, fi.pts); }
/** * 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_codec_t *mc; int run = 1; int reqsize = -1; int reinit = 0; int size; vd->vd_frame = avcodec_alloc_frame(); hts_mutex_lock(&mp->mp_mutex); while(run) { if((mb = TAILQ_FIRST(&mq->mq_q)) == NULL) { hts_cond_wait(&mq->mq_avail, &mp->mp_mutex); continue; } if(mb->mb_data_type == MB_VIDEO && vd->vd_hold && vd->vd_skip == 0 && mb->mb_skip == 0) { hts_cond_wait(&mq->mq_avail, &mp->mp_mutex); continue; } TAILQ_REMOVE(&mq->mq_q, mb, mb_link); mq->mq_packets_current--; mp->mp_buffer_current -= mb->mb_size; mq_update_stats(mp, mq); hts_cond_signal(&mp->mp_backpressure); hts_mutex_unlock(&mp->mp_mutex); mc = mb->mb_cw; 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_FLUSH: vd_init_timings(vd); vd->vd_do_flush = 1; vd->vd_interlaced = 0; video_overlay_flush(vd, 1); dvdspu_flush(vd); break; case MB_VIDEO: if(reinit) { reinit = 0; if(mc->reinit != NULL) mc->reinit(mc); } if(mb->mb_skip == 2) vd->vd_skip = 1; size = mb->mb_size; if(mc->decode) mc->decode(mc, vd, mq, mb, reqsize); else vd_decode_video(vd, mq, mb); update_vbitrate(mp, mq, size, vd); reqsize = -1; break; case MB_REQ_OUTPUT_SIZE: reqsize = mb->mb_data32; break; case MB_REINITIALIZE: reinit = 1; break; #if ENABLE_DVD case MB_DVD_RESET_SPU: vd->vd_spu_curbut = 1; dvdspu_flush(vd); break; case MB_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_t *e = event_create(EVENT_DVD_PCI, sizeof(event_t) + sizeof(pci_t)); memcpy(e->e_payload, mb->mb_data, sizeof(pci_t)); mp_enqueue_event(mp, e); event_release(e); break; case MB_DVD_CLUT: dvdspu_decode_clut(vd->vd_dvd_clut, mb->mb_data); break; case MB_DVD_SPU: dvdspu_enqueue(vd, mb->mb_data, mb->mb_size, vd->vd_dvd_clut, 0, 0, mb->mb_pts); break; #endif case MB_DVD_SPU2: dvdspu_enqueue(vd, mb->mb_data+72, mb->mb_size-72, 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(vd, mb); break; case MB_END: break; case MB_BLACKOUT: vd->vd_frame_deliver(FRAME_BUFFER_TYPE_BLACKOUT, NULL, NULL, vd->vd_opaque); break; case MB_FLUSH_SUBTITLES: video_overlay_flush(vd, 1); break; case MB_EXT_SUBTITLE: if(vd->vd_ext_subtitles != NULL) subtitles_destroy(vd->vd_ext_subtitles); // Steal subtitle from the media_buf vd->vd_ext_subtitles = mb->mb_data; mb->mb_data = NULL; video_overlay_flush(vd, 1); break; default: abort(); } hts_mutex_lock(&mp->mp_mutex); media_buf_free_locked(mp, mb); } hts_mutex_unlock(&mp->mp_mutex); if(vd->vd_ext_subtitles != NULL) subtitles_destroy(vd->vd_ext_subtitles); /* Free ffmpeg frame */ av_free(vd->vd_frame); return NULL; }
/** * 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; }