]> git.sesse.net Git - vlc/blobdiff - modules/access/bluray.c
bluray: improve readability by using local variables
[vlc] / modules / access / bluray.c
index 1d34060bacb20a8c50e820c278c583e6056c68ce..df3c9225d2d22862409d9650790852df9673efba 100644 (file)
@@ -125,10 +125,12 @@ struct  demux_sys_t
     /* Titles */
     unsigned int        i_title;
     unsigned int        i_longest_title;
-    int                 i_playlist;          /* -1 = no playlist playing */
-    unsigned int        i_current_clip;
     input_title_t       **pp_title;
 
+    vlc_mutex_t             pl_info_lock;
+    BLURAY_TITLE_INFO      *p_pl_info;
+    const BLURAY_CLIP_INFO *p_clip_info;
+
     /* Meta information */
     const META_DL       *p_meta;
 
@@ -136,6 +138,9 @@ struct  demux_sys_t
     bluray_overlay_t    *p_overlays[MAX_OVERLAY];
     int                 current_overlay; // -1 if no current overlay;
     bool                b_menu;
+    bool                b_menu_open;
+    bool                b_popup_available;
+    mtime_t             i_still_end_time;
 
     /* */
     input_thread_t      *p_input;
@@ -148,6 +153,7 @@ struct  demux_sys_t
     int                 i_spu_stream;   /* Selected subtitle stream. -1 if default */
     int                 i_video_stream;
     stream_t            *p_parser;
+    bool                b_flushed;
 
     /* Used to store bluray disc path */
     char                *psz_bd_path;
@@ -264,6 +270,27 @@ static void FindMountPoint(char **file)
 #endif
 }
 
+/*****************************************************************************
+ * cache current playlist (title) information
+ *****************************************************************************/
+
+static void setTitleInfo(demux_sys_t *p_sys, BLURAY_TITLE_INFO *info)
+{
+    vlc_mutex_lock(&p_sys->pl_info_lock);
+
+    if (p_sys->p_pl_info) {
+        bd_free_title_info(p_sys->p_pl_info);
+    }
+    p_sys->p_pl_info   = info;
+    p_sys->p_clip_info = NULL;
+
+    if (p_sys->p_pl_info && p_sys->p_pl_info->clip_count) {
+        p_sys->p_clip_info = &p_sys->p_pl_info->clips[0];
+    }
+
+    vlc_mutex_unlock(&p_sys->pl_info_lock);
+}
+
 /*****************************************************************************
  * blurayOpen: module init function
  *****************************************************************************/
@@ -289,6 +316,7 @@ static int blurayOpen(vlc_object_t *object)
     p_sys->i_audio_stream = -1;
     p_sys->i_spu_stream = -1;
     p_sys->i_video_stream = -1;
+    p_sys->i_still_end_time = 0;
 
     /* init demux info fields */
     p_demux->info.i_update    = 0;
@@ -391,13 +419,13 @@ static int blurayOpen(vlc_object_t *object)
 
     /* Registering overlay event handler */
     bd_register_overlay_proc(p_sys->bluray, p_demux, blurayOverlayProc);
+    p_sys->p_input = demux_GetParentInput(p_demux);
+    if (unlikely(!p_sys->p_input)) {
+        msg_Err(p_demux, "Could not get parent input");
+        goto error;
+    }
 
     if (p_sys->b_menu) {
-        p_sys->p_input = demux_GetParentInput(p_demux);
-        if (unlikely(!p_sys->p_input)) {
-            msg_Err(p_demux, "Could not get parent input");
-            goto error;
-        }
 
         /* Register ARGB overlay handler for BD-J */
         if (disc_info->num_bdj_titles)
@@ -426,6 +454,8 @@ static int blurayOpen(vlc_object_t *object)
         goto error;
     }
 
+    vlc_mutex_init(&p_sys->pl_info_lock);
+
     p_demux->pf_control = blurayControl;
     p_demux->pf_demux   = blurayDemux;
 
@@ -448,6 +478,8 @@ static void blurayClose(vlc_object_t *object)
     demux_t *p_demux = (demux_t*)object;
     demux_sys_t *p_sys = p_demux->p_sys;
 
+    setTitleInfo(p_sys, NULL);
+
     /*
      * Close libbluray first.
      * This will close all the overlays before we release p_vout
@@ -475,6 +507,8 @@ static void blurayClose(vlc_object_t *object)
         vlc_input_title_Delete(p_sys->pp_title[i]);
     TAB_CLEAN(p_sys->i_title, p_sys->pp_title);
 
+    vlc_mutex_destroy(&p_sys->pl_info_lock);
+
     free(p_sys->psz_bd_path);
     free(p_sys);
 }
@@ -525,15 +559,12 @@ static void setStreamLang(es_format_t *p_fmt,
 static es_out_id_t *esOutAdd(es_out_t *p_out, const es_format_t *p_fmt)
 {
     demux_sys_t *p_sys = p_out->p_sys->p_demux->p_sys;
-    BLURAY_TITLE_INFO *title_info = bd_get_playlist_info(p_sys->bluray, p_sys->i_playlist, 0);
-    BLURAY_CLIP_INFO *clip_info = NULL;
     es_format_t fmt;
 
-    if (title_info && p_sys->i_current_clip < title_info->clip_count) {
-        clip_info = &title_info->clips[p_sys->i_current_clip];
-    }
-
     es_format_Copy(&fmt, p_fmt);
+
+    vlc_mutex_lock(&p_sys->pl_info_lock);
+
     switch (fmt.i_cat) {
     case VIDEO_ES:
         if (p_sys->i_video_stream != -1 && p_sys->i_video_stream != p_fmt->i_id)
@@ -542,19 +573,18 @@ static es_out_id_t *esOutAdd(es_out_t *p_out, const es_format_t *p_fmt)
     case AUDIO_ES:
         if (p_sys->i_audio_stream != -1 && p_sys->i_audio_stream != p_fmt->i_id)
             fmt.i_priority = ES_PRIORITY_NOT_SELECTABLE;
-        if (clip_info)
-            setStreamLang(&fmt, clip_info->audio_streams, clip_info->audio_stream_count);
+        if (p_sys->p_clip_info)
+            setStreamLang(&fmt, p_sys->p_clip_info->audio_streams, p_sys->p_clip_info->audio_stream_count);
         break ;
     case SPU_ES:
         if (p_sys->i_spu_stream != -1 && p_sys->i_spu_stream != p_fmt->i_id)
             fmt.i_priority = ES_PRIORITY_NOT_SELECTABLE;
-        if (clip_info)
-            setStreamLang(&fmt, clip_info->pg_streams, clip_info->pg_stream_count);
+        if (p_sys->p_clip_info)
+            setStreamLang(&fmt, p_sys->p_clip_info->pg_streams, p_sys->p_clip_info->pg_stream_count);
         break ;
     }
 
-    if (title_info)
-        bd_free_title_info(title_info);
+    vlc_mutex_unlock(&p_sys->pl_info_lock);
 
     es_out_id_t *p_es = es_out_Add(p_out->p_sys->p_demux->out, &fmt);
     if (p_fmt->i_id >= 0) {
@@ -1105,19 +1135,8 @@ static void bluraySendOverlayToVout(demux_t *p_demux)
     p_sys->p_overlays[p_sys->current_overlay]->status = Outdated;
 }
 
-static void blurayUpdateTitleInfo(demux_t *p_demux, input_title_t *t, int i_title_idx, int i_playlist)
+static void blurayUpdateTitleInfo(input_title_t *t, BLURAY_TITLE_INFO *title_info)
 {
-    demux_sys_t *p_sys = p_demux->p_sys;
-    BLURAY_TITLE_INFO *title_info = NULL;
-
-    if (i_playlist >= 0)
-        title_info = bd_get_playlist_info(p_sys->bluray, i_playlist, 0);
-    else if (i_title_idx >= 0)
-        title_info = bd_get_title_info(p_sys->bluray, i_title_idx, 0);
-    if (!title_info) {
-        return;
-    }
-
     t->i_length = FROM_TICKS(title_info->duration);
 
     if (!t->i_seekpoint) {
@@ -1131,20 +1150,24 @@ static void blurayUpdateTitleInfo(demux_t *p_demux, input_title_t *t, int i_titl
             TAB_APPEND(t->i_seekpoint, t->seekpoint, s);
         }
     }
-
-    bd_free_title_info(title_info);
 }
 
 static void blurayInitTitles(demux_t *p_demux, int menu_titles)
 {
     demux_sys_t *p_sys = p_demux->p_sys;
+#if BLURAY_VERSION < BLURAY_VERSION_CODE(0,5,0)
     int64_t duration = 0;
+#endif
 
     /* get and set the titles */
     unsigned i_title = menu_titles;
 
-    if (!p_sys->b_menu)
+    if (!p_sys->b_menu) {
         i_title = bd_get_titles(p_sys->bluray, TITLES_RELEVANT, 60);
+#if BLURAY_VERSION >= BLURAY_VERSION_CODE(0,5,0)
+        p_sys->i_longest_title = bd_get_main_title(p_sys->bluray);
+#endif
+    }
 
     for (unsigned int i = 0; i < i_title; i++) {
         input_title_t *t = vlc_input_title_New();
@@ -1152,12 +1175,16 @@ static void blurayInitTitles(demux_t *p_demux, int menu_titles)
             break;
 
         if (!p_sys->b_menu) {
-            blurayUpdateTitleInfo(p_demux, t, i, -1);
+            BLURAY_TITLE_INFO *title_info = bd_get_title_info(p_sys->bluray, i, 0);
+            blurayUpdateTitleInfo(t, title_info);
+            bd_free_title_info(title_info);
 
+#if BLURAY_VERSION < BLURAY_VERSION_CODE(0,5,0)
             if (t->i_length > duration) {
                 duration = t->i_length;
                 p_sys->i_longest_title = i;
             }
+#endif
         } else if (i == 0) {
             t->psz_name = strdup(_("Top Menu"));
         } else if (i == i_title - 1) {
@@ -1185,34 +1212,6 @@ static void blurayResetParser(demux_t *p_demux)
         msg_Err(p_demux, "Failed to create TS demuxer");
 }
 
-static void blurayUpdateTitle(demux_t *p_demux, unsigned i_title)
-{
-    blurayResetParser(p_demux);
-    if (i_title >= p_demux->p_sys->i_title)
-        return;
-
-    /* read title info and init some values */
-    p_demux->info.i_title = i_title;
-    p_demux->info.i_seekpoint = 0;
-    p_demux->info.i_update |= INPUT_UPDATE_TITLE | INPUT_UPDATE_SEEKPOINT;
-}
-
-static void blurayUpdatePlaylist(demux_t *p_demux, unsigned i_playlist)
-{
-    blurayResetParser(p_demux);
-
-    p_demux->p_sys->i_playlist = i_playlist;
-    p_demux->p_sys->i_current_clip = 0;
-
-    /* read title info and init some values */
-    if (!p_demux->p_sys->b_menu)
-        p_demux->info.i_title = bd_get_current_title(p_demux->p_sys->bluray);
-    p_demux->info.i_seekpoint = 0;
-    p_demux->info.i_update |= INPUT_UPDATE_TITLE | INPUT_UPDATE_SEEKPOINT;
-
-    blurayUpdateTitleInfo(p_demux, p_demux->p_sys->pp_title[p_demux->info.i_title], -1, i_playlist);
-}
-
 /*****************************************************************************
  * bluraySetTitle: select new BD title
  *****************************************************************************/
@@ -1230,7 +1229,7 @@ static int bluraySetTitle(demux_t *p_demux, int i_title)
             msg_Dbg(p_demux, "Playing Title %i", i_title);
         }
 
-        if (bd_play_title(p_demux->p_sys->bluray, i_title) == 0) {
+        if (bd_play_title(p_sys->bluray, i_title) == 0) {
             msg_Err(p_demux, "cannot play bd title '%d'", i_title);
             return VLC_EGENERIC;
         }
@@ -1246,11 +1245,12 @@ static int bluraySetTitle(demux_t *p_demux, int i_title)
 
     msg_Dbg(p_demux, "Selecting Title %i", i_title);
 
-    if (bd_select_title(p_demux->p_sys->bluray, i_title) == 0) {
+    if (bd_select_title(p_sys->bluray, i_title) == 0) {
         msg_Err(p_demux, "cannot select bd title '%d'", i_title);
         return VLC_EGENERIC;
     }
-    blurayUpdateTitle(p_demux, i_title);
+
+    blurayResetParser(p_demux);
 
     return VLC_SUCCESS;
 }
@@ -1385,6 +1385,9 @@ static int blurayControl(demux_t *p_demux, int query, va_list args)
     }
 
     case DEMUX_NAV_ACTIVATE:
+        if (p_sys->b_popup_available && !p_sys->b_menu_open) {
+            return sendKeyEvent(p_sys, BD_VK_POPUP);
+        }
         return sendKeyEvent(p_sys, BD_VK_ENTER);
     case DEMUX_NAV_UP:
         return sendKeyEvent(p_sys, BD_VK_UP);
@@ -1408,36 +1411,130 @@ static int blurayControl(demux_t *p_demux, int query, va_list args)
     return VLC_SUCCESS;
 }
 
-static void blurayStreamSelect(demux_t *p_demux, uint32_t i_type, uint32_t i_id)
+/*****************************************************************************
+ * libbluray event handling
+ *****************************************************************************/
+
+static void streamFlush( demux_sys_t *p_sys )
 {
-    demux_sys_t *p_sys = p_demux->p_sys;
-    int i_pid = -1;
+    /*
+     * 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;
 
-    if (p_sys->i_playlist < 0)
+    block_t *p_block = block_Alloc(192);
+    if (!p_block)
         return;
 
-    BLURAY_TITLE_INFO *title_info = bd_get_playlist_info(p_sys->bluray, p_sys->i_playlist, 0);
-    if (title_info == NULL)
+    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;
+        }
 
-    if (p_sys->i_current_clip < title_info->clip_count) {
-        BLURAY_CLIP_INFO *clip_info = &title_info->clips[p_sys->i_current_clip];
+        /* 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 < clip_info->audio_stream_count) {
-                i_pid = clip_info->audio_streams[i_id].pid;
+            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 < clip_info->pg_stream_count) {
-                i_pid = clip_info->pg_streams[i_id].pid;
+            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;
             }
         }
     }
-    bd_free_title_info(title_info);
+
+    vlc_mutex_unlock(&p_sys->pl_info_lock);
 
     if (i_pid > 0) {
         int i_idx = findEsPairIndex(p_sys, i_pid);
@@ -1448,22 +1545,50 @@ static void blurayStreamSelect(demux_t *p_demux, uint32_t i_type, uint32_t i_id)
     }
 }
 
+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)
 {
-    if (clip == 0xFF)
-        return ;
     demux_sys_t *p_sys = p_demux->p_sys;
 
-    p_sys->i_current_clip = clip;
-    BLURAY_TITLE_INFO *info = bd_get_playlist_info(p_sys->bluray, p_sys->i_playlist, 0);
-    if (info == NULL)
-        return ;
+    vlc_mutex_lock(&p_sys->pl_info_lock);
+
+    p_sys->p_clip_info = NULL;
+    p_sys->i_video_stream = -1;
+
+    if (p_sys->p_pl_info && clip < p_sys->p_pl_info->clip_count) {
+
+        p_sys->p_clip_info = &p_sys->p_pl_info->clips[clip];
+
     /* Let's assume a single video track for now.
      * This may brake later, but it's enough for now.
      */
-    assert(info->clips[p_sys->i_current_clip].video_stream_count >= 1);
-    p_sys->i_video_stream = info->clips[p_sys->i_current_clip].video_streams[0].pid;
-    bd_free_title_info(info);
+        assert(p_sys->p_clip_info->video_stream_count >= 1);
+        p_sys->i_video_stream = p_sys->p_clip_info->video_streams[0].pid;
+    }
+
+    vlc_mutex_unlock(&p_sys->pl_info_lock);
+
+    blurayResetStillImage(p_demux);
 }
 
 static void blurayHandleEvent(demux_t *p_demux, const BD_EVENT *e)
@@ -1477,7 +1602,7 @@ static void blurayHandleEvent(demux_t *p_demux, const BD_EVENT *e)
         else
             p_demux->info.i_title = e->param;
         /* this is feature title, we don't know yet which playlist it will play (if any) */
-        p_sys->i_playlist = -1;
+        setTitleInfo(p_sys, NULL);
         /* reset title infos here ? */
         p_demux->info.i_update |= INPUT_UPDATE_TITLE | INPUT_UPDATE_SEEKPOINT; /* might be BD-J title with no video */
         break;
@@ -1494,6 +1619,13 @@ static void blurayHandleEvent(demux_t *p_demux, const BD_EVENT *e)
         break;
     case BD_EVENT_ANGLE:
         break;
+    case BD_EVENT_MENU:
+        p_sys->b_menu_open = e->param;
+        break;
+    case BD_EVENT_POPUP:
+        p_sys->b_popup_available = e->param;
+        /* TODO: show / hide pop-up menu button in gui ? */
+        break;
 
     /*
      * stream selection events
@@ -1505,6 +1637,12 @@ static void blurayHandleEvent(demux_t *p_demux, const BD_EVENT *e)
     case BD_EVENT_IG_STREAM:
         break;
 
+    /*
+     * playback control events
+     */
+    case BD_EVENT_STILL_TIME:
+        blurayStillImage(p_demux, e->param);
+        break;
     case BD_EVENT_DISCONTINUITY:
         /* reset demuxer (partially decoded PES packets must be dropped) */
         blurayResetParser(p_demux);
@@ -1536,7 +1674,7 @@ static int blurayDemux(demux_t *p_demux)
     int nread;
 
     if (p_sys->b_menu == false) {
-        while (bd_get_event(p_demux->p_sys->bluray, &e))
+        while (bd_get_event(p_sys->bluray, &e))
             blurayHandleEvent(p_demux, &e);
 
         nread = bd_read(p_sys->bluray, p_block->p_buffer,
@@ -1577,5 +1715,7 @@ static int blurayDemux(demux_t *p_demux)
 
     stream_DemuxSend(p_sys->p_parser, p_block);
 
+    p_sys->b_flushed = false;
+
     return 1;
 }