/** * Must be called with mp locked */ static void mq_flush_locked(media_pipe_t *mp, media_queue_t *mq, int full) { mq->mq_last_deq_dts = PTS_UNSET; mq_flush_q(mp, mq, &mq->mq_q_data, full); mq_flush_q(mp, mq, &mq->mq_q_ctrl, full); mq_flush_q(mp, mq, &mq->mq_q_aux, full); mq_update_stats(mp, mq, 1); }
/** * 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); 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; #ifdef CONFIG_DVD case MB_DVD_HILITE: case MB_DVD_RESET_SPU: case MB_DVD_CLUT: case MB_DVD_PCI: case MB_DVD_SPU: dvdspu_decoder_dispatch(vd, mb, mp); break; #endif 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; }
static void * dummy_audio_thread(void *aux) { audio_decoder_t *ad = aux; media_pipe_t *mp = ad->ad_mp; media_queue_t *mq = &mp->mp_audio; media_buf_t *mb; int hold = 0; int run = 1; int64_t rt = 0; int64_t base = AV_NOPTS_VALUE; 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_AUDIO && hold && 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); switch(mb->mb_data_type) { case MB_CTRL_EXIT: run = 0; break; case MB_CTRL_PAUSE: hold = 1; break; case MB_CTRL_PLAY: hold = 0; base = AV_NOPTS_VALUE; break; case MB_FLUSH: base = AV_NOPTS_VALUE; break; case MB_AUDIO: if(mb->mb_skip || mb->mb_stream != mq->mq_stream) break; if(mb->mb_pts != AV_NOPTS_VALUE) { audio_set_clock(mp, mb->mb_pts, 0, mb->mb_epoch); if(base == AV_NOPTS_VALUE) { base = mb->mb_pts; rt = showtime_get_ts(); } else { int64_t d = mb->mb_pts - base; if(d > 0) { int sleeptime = rt + d - showtime_get_ts(); if(sleeptime > 0) usleep(sleeptime); } } } break; default: abort(); } hts_mutex_lock(&mp->mp_mutex); media_buf_free_locked(mp, mb); } hts_mutex_unlock(&mp->mp_mutex); return NULL; }
void * audio_decode_thread(void *aux) { audio_decoder_t *ad = aux; const audio_class_t *ac = ad->ad_ac; int run = 1; media_pipe_t *mp = ad->ad_mp; media_queue_t *mq = &mp->mp_audio; media_buf_t *mb; int blocked = 0; if(ac->ac_init != NULL) ac->ac_init(ad); ad->ad_discontinuity = 1; hts_mutex_lock(&mp->mp_mutex); while(run) { int avail; if(ad->ad_spdif_muxer != NULL) { avail = ad->ad_spdif_frame_size; } else { avail = ad->ad_avr != NULL ? avresample_available(ad->ad_avr) : 0; } media_buf_t *data = TAILQ_FIRST(&mq->mq_q_data); media_buf_t *ctrl = TAILQ_FIRST(&mq->mq_q_ctrl); if(avail >= ad->ad_tile_size && blocked == 0 && !ad->ad_paused && !ctrl) { assert(avail != 0); int samples = MIN(ad->ad_tile_size, avail); int r; if(ac->ac_deliver_locked != NULL) { r = ac->ac_deliver_locked(ad, samples, ad->ad_pts, ad->ad_epoch); if(r) { hts_cond_wait(&mq->mq_avail, &mp->mp_mutex); continue; } } else { hts_mutex_unlock(&mp->mp_mutex); r = ac->ac_deliver_unlocked(ad, samples, ad->ad_pts, ad->ad_epoch); hts_mutex_lock(&mp->mp_mutex); } if(r) { blocked = 1; } else { ad->ad_pts = AV_NOPTS_VALUE; } continue; } if(ctrl != NULL) { TAILQ_REMOVE(&mq->mq_q_ctrl, ctrl, mb_link); mb = ctrl; } else if(data != NULL && avail < ad->ad_tile_size) { TAILQ_REMOVE(&mq->mq_q_data, data, mb_link); 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); if(mb->mb_data_type == MB_CTRL_UNBLOCK) { assert(blocked); blocked = 0; } else if(ad->ad_mode == AUDIO_MODE_CODED && ac->ac_deliver_coded_locked != NULL && mb->mb_data_type == MB_AUDIO) { ac->ac_deliver_coded_locked(ad, mb->mb_data, mb->mb_size, mb->mb_pts, mb->mb_epoch); } else { hts_mutex_unlock(&mp->mp_mutex); switch(mb->mb_data_type) { case MB_AUDIO: audio_process_audio(ad, mb); break; case MB_SET_PROP_STRING: prop_set_string(mb->mb_prop, (void *)mb->mb_data); break; case MB_CTRL_SET_VOLUME_MULTIPLIER: ad->ad_vol_scale = mb->mb_float; if(ac->ac_set_volume != NULL) ac->ac_set_volume(ad, ad->ad_vol_scale); break; case MB_CTRL_PAUSE: ad->ad_paused = 1; if(ac->ac_pause) ac->ac_pause(ad); break; case MB_CTRL_PLAY: ad->ad_paused = 0; if(ac->ac_play) ac->ac_play(ad); break; case MB_CTRL_FLUSH: // Reset some error reporting filters ad->ad_channel_layout_fail = 0; ad->ad_sample_rate_fail = 0; if(ac->ac_flush) ac->ac_flush(ad); ad->ad_pts = AV_NOPTS_VALUE; if(mp->mp_seek_audio_done != NULL) mp->mp_seek_audio_done(mp); ad->ad_discontinuity = 1; if(ad->ad_avr != NULL) { avresample_read(ad->ad_avr, NULL, avresample_available(ad->ad_avr)); assert(avresample_available(ad->ad_avr) == 0); } break; case MB_CTRL_EXIT: run = 0; break; default: abort(); } hts_mutex_lock(&mp->mp_mutex); } media_buf_free_locked(mp, mb); } hts_mutex_unlock(&mp->mp_mutex); if(ac->ac_fini != NULL) ac->ac_fini(ad); return NULL; }
void * audio_decode_thread(void *aux) { audio_decoder_t *ad = aux; const audio_class_t *ac = ad->ad_ac; int run = 1; media_pipe_t *mp = ad->ad_mp; media_queue_t *mq = &mp->mp_audio; media_buf_t *mb; int blocked = 0; if(ac->ac_init != NULL) ac->ac_init(ad); hts_mutex_lock(&mp->mp_mutex); while(run) { int avail = ad->ad_avr != NULL ? avresample_available(ad->ad_avr) : 0; media_buf_t *data = TAILQ_FIRST(&mq->mq_q_data); media_buf_t *ctrl = TAILQ_FIRST(&mq->mq_q_ctrl); if(avail >= ad->ad_tile_size && blocked == 0 && !ad->ad_paused) { assert(avail != 0); assert(ad->ad_avr != NULL); int samples = MIN(ad->ad_tile_size, avail); int r; if(ac->ac_deliver_locked != NULL) { r = ac->ac_deliver_locked(ad, samples, ad->ad_pts, ad->ad_epoch); if(r) { hts_cond_wait(&mq->mq_avail, &mp->mp_mutex); continue; } ad->ad_pts = AV_NOPTS_VALUE; continue; } else { hts_mutex_unlock(&mp->mp_mutex); r = ac->ac_deliver_unlocked(ad, samples, ad->ad_pts, ad->ad_epoch); hts_mutex_lock(&mp->mp_mutex); } if(r) { blocked = 1; continue; } else { ad->ad_pts = AV_NOPTS_VALUE; } continue; } if(ctrl != NULL) { TAILQ_REMOVE(&mq->mq_q_ctrl, ctrl, mb_link); mb = ctrl; } else if(data != NULL && avail < 16384) { TAILQ_REMOVE(&mq->mq_q_data, data, mb_link); 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); if(mb->mb_data_type == MB_CTRL_UNBLOCK) { assert(blocked); blocked = 0; } else { hts_mutex_unlock(&mp->mp_mutex); switch(mb->mb_data_type) { case MB_AUDIO: audio_process_audio(ad, mb); break; case MB_CTRL_PAUSE: ad->ad_paused = 1; if(ac->ac_pause) ac->ac_pause(ad); break; case MB_CTRL_PLAY: ad->ad_paused = 0; if(ac->ac_play) ac->ac_play(ad); break; case MB_CTRL_FLUSH: if(ac->ac_flush) ac->ac_flush(ad); ad->ad_pts = AV_NOPTS_VALUE; break; case MB_CTRL_EXIT: run = 0; break; default: abort(); } hts_mutex_lock(&mp->mp_mutex); } media_buf_free_locked(mp, mb); } hts_mutex_unlock(&mp->mp_mutex); if(ac->ac_fini != NULL) ac->ac_fini(ad); return NULL; }
static void * ad_thread(void *aux) { audio_decoder_t *ad = aux; media_pipe_t *mp = ad->ad_mp; media_queue_t *mq = &mp->mp_audio; media_buf_t *mb; int hold = 0; int run = 1; int64_t silence_start_pts = AV_NOPTS_VALUE; uint64_t silence_start_realtime = 0; 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_AUDIO && hold && 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); switch(mb->mb_data_type) { case MB_CTRL_EXIT: run = 0; break; case MB_CTRL_PAUSE: /* Copy back any pending audio in the output fifo */ audio_fifo_purge(thefifo, ad, &ad->ad_hold_queue); hold = 1; break; case MB_CTRL_PLAY: hold = 0; break; case MB_FLUSH: ad->ad_do_flush = 1; /* Flush any pending audio in the output fifo */ audio_fifo_purge(thefifo, ad, NULL); audio_decoder_flush(ad); break; case MB_AUDIO: if(mb->mb_skip != 0) break; if(mq->mq_stream == -1) { if(mb->mb_pts == AV_NOPTS_VALUE) break; if(silence_start_pts == AV_NOPTS_VALUE) { silence_start_pts = mb->mb_pts; silence_start_realtime = showtime_get_ts(); } else { int64_t d = mb->mb_pts - silence_start_pts; if(d > 0) { int64_t sleeptime = silence_start_realtime + d - showtime_get_ts(); if(sleeptime > 0) usleep(sleeptime); } } break; } if(mb->mb_stream != mq->mq_stream) break; ad_decode_buf(ad, mp, mq, mb); silence_start_pts = AV_NOPTS_VALUE; break; case MB_END: mp_set_current_time(mp, AV_NOPTS_VALUE); break; default: abort(); } hts_mutex_lock(&mp->mp_mutex); media_buf_free_locked(mp, mb); } hts_mutex_unlock(&mp->mp_mutex); audio_fifo_purge(thefifo, ad, NULL); 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; }