+/*****************************************************************************
+ * libbluray event handling
+ *****************************************************************************/
+
+static void streamFlush( demux_sys_t *p_sys )
+{
+ /*
+ * MPEG-TS demuxer does not flush last video frame if size of PES packet is unknown.
+ * Packet is flushed only when TS packet with PUSI flag set is received.
+ *
+ * Fix this by emitting (video) ts packet with PUSI flag set.
+ * Add video sequence end code to payload so that also video decoder is flushed.
+ * Set PES packet size in the payload so that it will be sent to decoder immediately.
+ */
+
+ if (p_sys->b_flushed)
+ return;
+
+ block_t *p_block = block_Alloc(192);
+ if (!p_block)
+ return;
+
+ static const uint8_t seq_end_pes[] = {
+ 0x00, 0x00, 0x01, 0xe0, 0x00, 0x07, 0x80, 0x00, 0x00, /* PES header */
+ 0x00, 0x00, 0x01, 0xb7, /* PES payload: sequence end */
+ };
+ static const uint8_t vid_pusi_ts[] = {
+ 0x00, 0x00, 0x00, 0x00, /* TP extra header (ATC) */
+ 0x47, 0x50, 0x11, 0x30, /* TP header */
+ (192 - (4 + 5) - sizeof(seq_end_pes)), /* adaptation field length */
+ 0x80, /* adaptation field: discontinuity indicator */
+ };
+
+ memset(p_block->p_buffer, 0, 192);
+ memcpy(p_block->p_buffer, vid_pusi_ts, sizeof(vid_pusi_ts));
+ memcpy(p_block->p_buffer + 192 - sizeof(seq_end_pes), seq_end_pes, sizeof(seq_end_pes));
+ p_block->i_buffer = 192;
+
+ /* set correct sequence end code */
+ vlc_mutex_lock(&p_sys->pl_info_lock);
+ if (p_sys->p_clip_info != NULL) {
+ if (p_sys->p_clip_info->video_streams[0].coding_type > 2) {
+ /* VC1 / H.264 sequence end */
+ p_block->p_buffer[191] = 0x0a;
+ }
+ }
+ vlc_mutex_unlock(&p_sys->pl_info_lock);
+
+ stream_DemuxSend(p_sys->p_parser, p_block);
+ p_sys->b_flushed = true;
+}
+
+static void blurayResetStillImage( demux_t *p_demux )
+{
+ demux_sys_t *p_sys = p_demux->p_sys;
+
+ if (p_sys->i_still_end_time) {
+ p_sys->i_still_end_time = 0;
+
+ blurayResetParser(p_demux);
+ es_out_Control( p_demux->out, ES_OUT_RESET_PCR );
+ }
+}
+
+static void blurayStillImage( demux_t *p_demux, unsigned i_timeout )
+{
+ demux_sys_t *p_sys = p_demux->p_sys;
+
+ /* time period elapsed ? */
+ if (p_sys->i_still_end_time > 0 && p_sys->i_still_end_time <= mdate()) {
+ msg_Dbg(p_demux, "Still image end");
+ bd_read_skip_still(p_sys->bluray);
+
+ blurayResetStillImage(p_demux);
+ return;
+ }
+
+ /* show last frame as still image */
+ if (!p_sys->i_still_end_time) {
+ if (i_timeout) {
+ msg_Dbg(p_demux, "Still image (%d seconds)", i_timeout);
+ p_sys->i_still_end_time = mdate() + i_timeout * CLOCK_FREQ;
+ } else {
+ msg_Dbg(p_demux, "Still image (infinite)");
+ p_sys->i_still_end_time = -1;
+ }
+
+ /* flush demuxer and decoder (there won't be next video packet starting with ts PUSI) */
+ streamFlush(p_sys);
+
+ /* stop buffering */
+ bool b_empty;
+ es_out_Control( p_demux->out, ES_OUT_GET_EMPTY, &b_empty );
+ }
+
+ /* avoid busy loops (read returns no data) */
+ msleep( 40000 );
+}
+
+static void blurayStreamSelect(demux_t *p_demux, uint32_t i_type, uint32_t i_id)
+{
+ demux_sys_t *p_sys = p_demux->p_sys;
+ int i_pid = -1;
+
+ vlc_mutex_lock(&p_sys->pl_info_lock);
+
+ if (p_sys->p_clip_info) {
+
+ /* The param we get is the real stream id, not an index, ie. it starts from 1 */
+ i_id--;
+ if (i_type == BD_EVENT_AUDIO_STREAM) {
+ if (i_id < p_sys->p_clip_info->audio_stream_count) {
+ i_pid = p_sys->p_clip_info->audio_streams[i_id].pid;
+ p_sys->i_audio_stream = i_pid;
+ }
+ } else if (i_type == BD_EVENT_PG_TEXTST_STREAM) {
+ if (i_id < p_sys->p_clip_info->pg_stream_count) {
+ i_pid = p_sys->p_clip_info->pg_streams[i_id].pid;
+ p_sys->i_spu_stream = i_pid;
+ }
+ }
+ }
+
+ vlc_mutex_unlock(&p_sys->pl_info_lock);
+
+ if (i_pid > 0) {
+ int i_idx = findEsPairIndex(p_sys, i_pid);
+ if (i_idx >= 0) {
+ es_out_id_t *p_es = vlc_array_item_at_index(&p_sys->es, i_idx);
+ es_out_Control(p_demux->out, ES_OUT_SET_ES, p_es);
+ }
+ }
+}
+
+static void blurayUpdatePlaylist(demux_t *p_demux, unsigned i_playlist)
+{
+ demux_sys_t *p_sys = p_demux->p_sys;
+
+ blurayResetParser(p_demux);
+
+ /* read title info and init some values */
+ if (!p_sys->b_menu)
+ p_demux->info.i_title = bd_get_current_title(p_sys->bluray);
+ p_demux->info.i_seekpoint = 0;
+ p_demux->info.i_update |= INPUT_UPDATE_TITLE | INPUT_UPDATE_SEEKPOINT;
+
+ BLURAY_TITLE_INFO *p_title_info = bd_get_playlist_info(p_sys->bluray, i_playlist, 0);
+ if (p_title_info) {
+ blurayUpdateTitleInfo(p_sys->pp_title[p_demux->info.i_title], p_title_info);
+ }
+ setTitleInfo(p_sys, p_title_info);
+
+ blurayResetStillImage(p_demux);
+}
+
+static void blurayUpdateCurrentClip(demux_t *p_demux, uint32_t clip)