Mercurial > mplayer.hg
changeset 25824:eb338d06c638
Add support for dvdnav still frames playback.
Based on various patches from Otvos Attila
and MPlayer'ized by me.
N.B. Always use -vc ffmpeg12 with dvdnav://
author | ben |
---|---|
date | Sat, 26 Jan 2008 11:51:34 +0000 |
parents | 60fda6c0c7dc |
children | 4c17a62383a4 |
files | command.c mp_core.h mplayer.c stream/stream_dvdnav.c stream/stream_dvdnav.h |
diffstat | 5 files changed, 345 insertions(+), 33 deletions(-) [+] |
line wrap: on
line diff
--- a/command.c Fri Jan 25 19:27:42 2008 +0000 +++ b/command.c Sat Jan 26 11:51:34 2008 +0000 @@ -3124,12 +3124,8 @@ if (mpctx->stream->type != STREAMTYPE_DVDNAV) break; - if (mp_dvdnav_handle_input - (mpctx->stream, cmd->args[0].v.i, &button)) { - uninit_player(INITED_ALL - (INITED_STREAM | INITED_INPUT | - (fixed_vo ? INITED_VO : 0))); - brk_cmd = 2; - } else if (button > 0) + mp_dvdnav_handle_input(mpctx->stream,cmd->args[0].v.i,&button); + if (button > 0) set_osd_msg(OSD_MSG_TEXT, 1, osd_duration, "Selected button number %d", button); }
--- a/mp_core.h Fri Jan 25 19:27:42 2008 +0000 +++ b/mp_core.h Sat Jan 26 11:51:34 2008 +0000 @@ -102,6 +102,13 @@ #endif int was_paused; + +#ifdef USE_DVDNAV + mp_image_t *nav_smpi; ///< last decoded dvdnav video image + unsigned char *nav_buffer; ///< last read dvdnav video frame + unsigned char *nav_start; ///< pointer to last read video buffer + int nav_in_size; ///< last read size +#endif } MPContext;
--- a/mplayer.c Fri Jan 25 19:27:42 2008 +0000 +++ b/mplayer.c Sat Jan 26 11:51:34 2008 +0000 @@ -573,6 +573,17 @@ /// step size of mixer changes int volstep = 3; +#ifdef USE_DVDNAV +static void mp_dvdnav_context_free(MPContext *ctx){ + if (ctx->nav_smpi) free_mp_image(ctx->nav_smpi); + ctx->nav_smpi = NULL; + if (ctx->nav_buffer) free(ctx->nav_buffer); + ctx->nav_buffer = NULL; + ctx->nav_start = NULL; + ctx->nav_in_size = 0; +} +#endif + void uninit_player(unsigned int mask){ mask=inited_flags&mask; @@ -622,6 +633,9 @@ current_module="uninit_vo"; mpctx->video_out->uninit(); mpctx->video_out=NULL; +#ifdef USE_DVDNAV + mp_dvdnav_context_free(mpctx); +#endif } // Must be after libvo uninit, as few vo drivers (svgalib) have tty code. @@ -1767,6 +1781,136 @@ return time_frame; } +#ifdef USE_DVDNAV +#ifndef FF_B_TYPE +#define FF_B_TYPE 3 +#endif +/// store decoded video image +static mp_image_t * mp_dvdnav_copy_mpi(mp_image_t *to_mpi, + mp_image_t *from_mpi) { + mp_image_t *mpi; + + /// Do not store B-frames + if (from_mpi->pict_type == FF_B_TYPE) + return to_mpi; + + if (to_mpi && + to_mpi->w == from_mpi->w && + to_mpi->h == from_mpi->h && + to_mpi->imgfmt == from_mpi->imgfmt) + mpi = to_mpi; + else { + if (to_mpi) + free_mp_image(to_mpi); + if (from_mpi->w == 0 || from_mpi->h == 0) + return NULL; + mpi = alloc_mpi(from_mpi->w,from_mpi->h,from_mpi->imgfmt); + } + + copy_mpi(mpi,from_mpi); + return mpi; +} + +static void mp_dvdnav_reset_stream (MPContext *ctx) { + if (ctx->sh_video) { + /// clear video pts + ctx->d_video->pts = 0.0f; + ctx->sh_video->pts = 0.0f; + ctx->sh_video->i_pts = 0.0f; + ctx->sh_video->last_pts = 0.0f; + ctx->sh_video->num_buffered_pts = 0; + ctx->sh_video->num_frames = 0; + ctx->sh_video->num_frames_decoded = 0; + ctx->sh_video->timer = 0.0f; + ctx->sh_video->stream_delay = 0.0f; + ctx->sh_video->timer = 0; + ctx->demuxer->stream_pts = MP_NOPTS_VALUE; + } + + if (ctx->sh_audio) { + /// free audio packets and reset + ds_free_packs(ctx->d_audio); + audio_delay -= ctx->sh_audio->stream_delay; + ctx->delay =- audio_delay; + ctx->audio_out->reset(); + resync_audio_stream(ctx->sh_audio); + } + + if (ctx->d_sub) dvdsub_id = -2; + + audio_delay = 0.0f; + correct_pts = 0; + + /// clear all EOF related flags + ctx->d_video->eof = ctx->d_audio->eof = ctx->stream->eof = 0; +} + +/// Restore last decoded DVDNAV (still frame) +static mp_image_t *mp_dvdnav_restore_smpi(int *in_size, + unsigned char **start, + mp_image_t *decoded_frame) +{ + if (mpctx->stream->type != STREAMTYPE_DVDNAV) + return decoded_frame; + + /// a change occured in dvdnav stream + if (mp_dvdnav_cell_has_changed(mpctx->stream,0)) { + mp_dvdnav_read_wait(mpctx->stream, 1, 1); + mp_dvdnav_context_free(mpctx); + mp_dvdnav_reset_stream(mpctx); + mp_dvdnav_read_wait(mpctx->stream, 0, 1); + mp_dvdnav_cell_has_changed(mpctx->stream,1); + } + + if (*in_size < 0) { + float len; + + /// Display still frame, if any + if (mpctx->nav_smpi && !mpctx->nav_buffer) + decoded_frame = mpctx->nav_smpi; + + /// increment video frame : continue playing after still frame + len = demuxer_get_time_length(mpctx->demuxer); + if (mpctx->sh_video->pts >= len && + mpctx->sh_video->pts > 0.0 && len > 0.0) { + mp_dvdnav_skip_still(mpctx->stream); + mp_dvdnav_skip_wait(mpctx->stream); + } + mpctx->sh_video->pts += 1 / mpctx->sh_video->fps; + + if (mpctx->nav_buffer) { + *start = mpctx->nav_start; + *in_size = mpctx->nav_in_size; + if (mpctx->nav_start) + memcpy(*start,mpctx->nav_buffer,mpctx->nav_in_size); + } + } + + return decoded_frame; +} + +/// Save last decoded DVDNAV (still frame) +static void mp_dvdnav_save_smpi(int in_size, + unsigned char *start, + mp_image_t *decoded_frame) +{ + if (mpctx->stream->type != STREAMTYPE_DVDNAV) + return; + + if (mpctx->nav_buffer) + free(mpctx->nav_buffer); + + mpctx->nav_buffer = malloc(in_size); + mpctx->nav_start = start; + mpctx->nav_in_size = mpctx->nav_buffer ? in_size : -1; + if (mpctx->nav_buffer) + memcpy(mpctx->nav_buffer,start,in_size); + + if (decoded_frame && mpctx->nav_smpi != decoded_frame) + mpctx->nav_smpi = mp_dvdnav_copy_mpi(mpctx->nav_smpi,decoded_frame); +} +#endif /* USE_DVDNAV */ + static void adjust_sync_and_print_status(int between_frames, float timing_error) { current_module="av_sync"; @@ -2079,7 +2223,7 @@ *blit_frame = 0; // Don't blit if we hit EOF if (!correct_pts) { unsigned char* start=NULL; - void *decoded_frame; + void *decoded_frame = NULL; int drop_frame=0; int in_size; @@ -2087,6 +2231,15 @@ frame_time = sh_video->next_frame_time; in_size = video_read_frame(sh_video, &sh_video->next_frame_time, &start, force_fps); +#ifdef USE_DVDNAV + /// wait, still frame or EOF + if (mpctx->stream->type == STREAMTYPE_DVDNAV && in_size < 0) { + if (mp_dvdnav_is_eof(mpctx->stream)) return -1; + if (mpctx->d_video) mpctx->d_video->eof = 0; + if (mpctx->d_audio) mpctx->d_audio->eof = 0; + mpctx->stream->eof = 0; + } else +#endif if (in_size < 0) return -1; if (in_size > max_framesize) @@ -2117,8 +2270,17 @@ update_teletext(sh_video, mpctx->demuxer, 0); update_osd_msg(); current_module = "decode_video"; +#ifdef USE_DVDNAV + decoded_frame = mp_dvdnav_restore_smpi(&in_size,&start,decoded_frame); + /// still frame has been reached, no need to decode + if (in_size > 0 && !decoded_frame) +#endif decoded_frame = decode_video(sh_video, start, in_size, drop_frame, sh_video->pts); +#ifdef USE_DVDNAV + /// save back last still frame for future display + mp_dvdnav_save_smpi(in_size,start,decoded_frame); +#endif current_module = "filter_video"; *blit_frame = (decoded_frame && filter_video(sh_video, decoded_frame, sh_video->pts)); @@ -3463,6 +3625,13 @@ end_at.type = END_AT_NONE; } +#ifdef USE_DVDNAV +mp_dvdnav_context_free(mpctx); +if (mpctx->stream->type == STREAMTYPE_DVDNAV) { + mp_dvdnav_read_wait(mpctx->stream, 0, 1); + mp_dvdnav_cell_has_changed(mpctx->stream,1); +} +#endif while(!mpctx->eof){ float aq_sleep_time=0;
--- a/stream/stream_dvdnav.c Fri Jan 25 19:27:42 2008 +0000 +++ b/stream/stream_dvdnav.c Sat Jan 26 11:51:34 2008 +0000 @@ -18,6 +18,18 @@ #include "m_option.h" #include "m_struct.h" #include "help_mp.h" +#include "stream_dvd_common.h" + +/* state flags */ +typedef enum { + NAV_FLAG_EOF = 1 << 0, /* end of stream has been reached */ + NAV_FLAG_WAIT = 1 << 1, /* wait event */ + NAV_FLAG_WAIT_SKIP = 1 << 2, /* wait skip disable */ + NAV_FLAG_CELL_CHANGED = 1 << 3, /* cell change event */ + NAV_FLAG_WAIT_READ_AUTO = 1 << 4, /* wait read auto mode */ + NAV_FLAG_WAIT_READ = 1 << 5, /* suspend read from stream */ + NAV_FLAG_VTS_DOMAIN = 1 << 6, /* vts domain */ +} dvdnav_state_t; typedef struct { dvdnav_t * dvdnav; /* handle to libdvdnav stuff */ @@ -27,6 +39,8 @@ int title; unsigned int spu_clut[16], spu_set; dvdnav_highlight_event_t hlev; + int still_length; /* still frame duration */ + unsigned int state; } dvdnav_priv_t; extern char *dvd_device; @@ -146,6 +160,10 @@ } } +static inline int dvdnav_get_duration (int length) { + return (length == 255) ? 0 : length * 1000; +} + static int dvdnav_stream_read(dvdnav_priv_t * priv, unsigned char *buf, int *len) { int event = DVDNAV_NOP; @@ -160,7 +178,14 @@ case DVDNAV_NAV_PACKET: return event; case DVDNAV_STILL_FRAME: { - dvdnav_still_skip(priv->dvdnav); // don't let dvdnav stall on this image + dvdnav_still_event_t *still_event = (dvdnav_still_event_t *) buf; + priv->still_length = still_event->length; + /* set still frame duration */ + priv->duration = dvdnav_get_duration (priv->still_length); + if (priv->still_length <= 1) { + pci_t *pnavpci = dvdnav_get_current_nav_pci (priv->dvdnav); + priv->duration = mp_dvdtimetomsec (&pnavpci->pci_gi.e_eltm); + } break; } case DVDNAV_HIGHLIGHT: { @@ -169,8 +194,27 @@ } case DVDNAV_CELL_CHANGE: { dvdnav_cell_change_event_t *ev = (dvdnav_cell_change_event_t*)buf; + uint32_t nextstill; + + priv->state &= ~NAV_FLAG_WAIT_SKIP; if(ev->pgc_length) priv->duration = ev->pgc_length/90; + + if (dvdnav_is_domain_vts(priv->dvdnav)) + priv->state &= ~NAV_FLAG_VTS_DOMAIN; + else + priv->state |= NAV_FLAG_VTS_DOMAIN; + + nextstill = dvdnav_get_next_still_flag (priv->dvdnav); + if (nextstill) { + priv->duration = dvdnav_get_duration (nextstill); + priv->still_length = nextstill; + if (priv->still_length <= 1) { + pci_t *pnavpci = dvdnav_get_current_nav_pci (priv->dvdnav); + priv->duration = mp_dvdtimetomsec (&pnavpci->pci_gi.e_eltm); + } + } + break; } case DVDNAV_SPU_CLUT_CHANGE: { @@ -178,9 +222,18 @@ priv->spu_set = 1; break; } - case DVDNAV_WAIT: - dvdnav_wait_skip(priv->dvdnav); + case DVDNAV_WAIT: { + if ((priv->state & NAV_FLAG_WAIT_SKIP) && + !(priv->state & NAV_FLAG_WAIT)) + dvdnav_wait_skip (priv->dvdnav); + else + priv->state |= NAV_FLAG_WAIT; break; + } + case DVDNAV_VTS_CHANGE: { + priv->state &= ~NAV_FLAG_WAIT_SKIP; + break; + } } *len=0; @@ -237,6 +290,8 @@ int event; dvdnav_priv_t* priv=s->priv; + if (priv->state & NAV_FLAG_WAIT_READ) /* read is suspended */ + return -1; len=0; if(!s->end_pos) update_title_len(s); @@ -249,16 +304,30 @@ return 0; } switch (event) { - case DVDNAV_STOP: + case DVDNAV_STOP: { + priv->state |= NAV_FLAG_EOF; + return len; + } case DVDNAV_BLOCK_OK: case DVDNAV_NAV_PACKET: + case DVDNAV_STILL_FRAME: return len; + case DVDNAV_WAIT: { + if (priv->state & NAV_FLAG_WAIT) + return len; + break; + } case DVDNAV_VTS_CHANGE: { int tit = 0, part = 0; dvdnav_vts_change_event_t *vts_event = (dvdnav_vts_change_event_t *)s->buffer; mp_msg(MSGT_CPLAYER,MSGL_INFO, "DVDNAV, switched to title: %d\r\n", vts_event->new_vtsN); + priv->state |= NAV_FLAG_CELL_CHANGED; + priv->state &= ~NAV_FLAG_WAIT_SKIP; + priv->state &= ~NAV_FLAG_WAIT; s->end_pos = 0; update_title_len(s); + if (priv->state & NAV_FLAG_WAIT_READ_AUTO) + priv->state |= NAV_FLAG_WAIT_READ; if(dvdnav_current_title_info(priv->dvdnav, &tit, &part) == DVDNAV_STATUS_OK) { mp_msg(MSGT_CPLAYER,MSGL_V, "\r\nDVDNAV, NEW TITLE %d\r\n", tit); dvdnav_get_highlight (priv, 0); @@ -268,6 +337,11 @@ break; } case DVDNAV_CELL_CHANGE: { + priv->state |= NAV_FLAG_CELL_CHANGED; + priv->state &= ~NAV_FLAG_WAIT_SKIP; + priv->state &= ~NAV_FLAG_WAIT; + if (priv->state & NAV_FLAG_WAIT_READ_AUTO) + priv->state |= NAV_FLAG_WAIT_READ; if(priv->title > 0 && dvd_last_chapter > 0) { int tit=0, part=0; if(dvdnav_current_title_info(priv->dvdnav, &tit, &part) == DVDNAV_STATUS_OK && part > dvd_last_chapter) @@ -317,7 +391,7 @@ } case STREAM_CTRL_GET_TIME_LENGTH: { - if(priv->duration) + if(priv->duration || priv->still_length) { *((double *)arg) = (double)priv->duration / 1000.0; return 1; @@ -484,11 +558,9 @@ } if(dvd_chapter > 0) dvdnav_part_play(priv->dvdnav, p->track, dvd_chapter); - } else if(p->track == -1) - dvdnav_menu_call(priv->dvdnav, DVD_MENU_Root); - else { - mp_msg(MSGT_OPEN,MSGL_INFO,"dvdnav_stream, you didn't specify a track number (as in dvdnav://1), playing whole disc\n"); - dvdnav_menu_call(priv->dvdnav, DVD_MENU_Title); + } else if (p->track == 0) { + if(dvdnav_menu_call(priv->dvdnav, DVD_MENU_Root) != DVDNAV_STATUS_OK) + dvdnav_menu_call(priv->dvdnav, DVD_MENU_Title); } if(mp_msg_test(MSGT_IDENTIFY, MSGL_INFO)) identify(priv, p); @@ -508,7 +580,7 @@ *file_format = DEMUXER_TYPE_MPEG_PS; update_title_len(stream); - if(!stream->pos) + if(!stream->pos && p->track > 0) mp_msg(MSGT_OPEN,MSGL_ERR, "INIT ERROR: couldn't get init pos %s\r\n", dvdnav_err_to_string(priv->dvdnav)); mp_msg(MSGT_OPEN,MSGL_INFO, "Remember to disable MPlayer's cache when playing dvdnav:// streams (adding -nocache to your command line)\r\n"); @@ -517,15 +589,14 @@ } -int mp_dvdnav_handle_input(stream_t *stream, int cmd, int *button) { +void mp_dvdnav_handle_input(stream_t *stream, int cmd, int *button) { dvdnav_priv_t * priv=(dvdnav_priv_t*)stream->priv; dvdnav_t *nav = priv->dvdnav; dvdnav_status_t status=DVDNAV_STATUS_ERR; pci_t *pci = dvdnav_get_current_nav_pci(nav); - int reset = 0; if(cmd != MP_CMD_DVDNAV_SELECT && !pci) - return 0; + return; switch(cmd) { case MP_CMD_DVDNAV_UP: @@ -542,26 +613,22 @@ break; case MP_CMD_DVDNAV_MENU: status = dvdnav_menu_call(nav,DVD_MENU_Root); - reset = 1; break; case MP_CMD_DVDNAV_PREVMENU: { int title=0, part=0; dvdnav_current_title_info(nav, &title, &part); if(title) { - if(dvdnav_menu_call(nav, DVD_MENU_Part) == DVDNAV_STATUS_OK - || dvdnav_menu_call(nav, DVD_MENU_Title) == DVDNAV_STATUS_OK) { - reset = 1; + if((status=dvdnav_menu_call(nav, DVD_MENU_Part)) == DVDNAV_STATUS_OK) break; - } } - if(dvdnav_menu_call(nav, DVD_MENU_Root) == DVDNAV_STATUS_OK) - reset = 1; + if((status=dvdnav_menu_call(nav, DVD_MENU_Title)) == DVDNAV_STATUS_OK) + break; + status=dvdnav_menu_call(nav, DVD_MENU_Root); } break; case MP_CMD_DVDNAV_SELECT: status = dvdnav_button_activate(nav, pci); - if(status == DVDNAV_STATUS_OK) reset = 1; break; case MP_CMD_DVDNAV_MOUSECLICK: /* @@ -572,7 +639,6 @@ This last call always works well */ status = dvdnav_mouse_activate(nav, pci, priv->mousex, priv->mousey); - if(status == DVDNAV_STATUS_OK) reset = 1; break; default: mp_msg(MSGT_CPLAYER, MSGL_V, "Unknown DVDNAV cmd %d\n", cmd); @@ -581,8 +647,6 @@ if(status == DVDNAV_STATUS_OK) dvdnav_get_current_highlight(nav, button); - - return reset; } void mp_dvdnav_update_mouse_pos(stream_t *stream, int32_t x, int32_t y, int* button) { @@ -722,6 +786,7 @@ dvdnav_priv_t * priv=(dvdnav_priv_t*)stream->priv; uint8_t lg, k, n=0; + if (priv->state & NAV_FLAG_VTS_DOMAIN) return 0; for(k=0; k<32; k++) { lg = dvdnav_get_spu_logical_stream(priv->dvdnav, k); if(lg == 0xff) continue; @@ -765,6 +830,76 @@ dvdnav_title_play (priv->dvdnav, title); } +/** + * \brief Check if end of stream has been reached + * \param stream: - stream pointer + * \return 1 on really eof + */ +int mp_dvdnav_is_eof (stream_t *stream) { + return ((dvdnav_priv_t *) stream->priv)->state & NAV_FLAG_EOF; +} + +/** + * \brief Skip still frame + * \param stream: - stream pointer + * \return 0 on success + */ +int mp_dvdnav_skip_still (stream_t *stream) { + dvdnav_priv_t *priv = stream->priv; + if (priv->still_length == 0xff) + return 1; + dvdnav_still_skip(priv->dvdnav); + return 0; +} + +/** + * \brief Skip wait event + * \param stream: - stream pointer + * \return 0 on success + */ +int mp_dvdnav_skip_wait (stream_t *stream) { + dvdnav_priv_t *priv = stream->priv; + if (!(priv->state & NAV_FLAG_WAIT)) + return 1; + priv->state &= ~NAV_FLAG_WAIT; + dvdnav_wait_skip(priv->dvdnav); + return 0; +} + +/** + * \brief Set wait mode + * \param stream : - stream pointer + * \param mode : - if true, then suspend block read + * \param automode: - if true, then VTS or cell change set wait mode + */ +void mp_dvdnav_read_wait (stream_t *stream, int mode, int automode) { + dvdnav_priv_t *priv = stream->priv; + if (mode == 0) + priv->state &= ~NAV_FLAG_WAIT_READ; + if (mode > 0) + priv->state |= NAV_FLAG_WAIT_READ; + if (automode == 0) + priv->state &= ~NAV_FLAG_WAIT_READ_AUTO; + if (automode > 0) + priv->state |= NAV_FLAG_WAIT_READ_AUTO; +} + +/** + * \brief Check if cell has changed + * \param stream: - stream pointer + * \param clear : - if true, then clear cell change flag + * \return 1 if cell has changed + */ +int mp_dvdnav_cell_has_changed (stream_t *stream, int clear) { + dvdnav_priv_t *priv = stream->priv; + if (!(priv->state & NAV_FLAG_CELL_CHANGED)) + return 0; + if (clear) + priv->state &= ~NAV_FLAG_CELL_CHANGED; + return 1; +} + + const stream_info_t stream_info_dvdnav = { "DVDNAV stream", "null",
--- a/stream/stream_dvdnav.h Fri Jan 25 19:27:42 2008 +0000 +++ b/stream/stream_dvdnav.h Sat Jan 26 11:51:34 2008 +0000 @@ -17,10 +17,15 @@ int dvdnav_lang_from_aid(stream_t *stream, int id, unsigned char *buf); int dvdnav_sid_from_lang(stream_t *stream, unsigned char *language); int dvdnav_lang_from_sid(stream_t *stream, int sid, unsigned char *buf); -int mp_dvdnav_handle_input(stream_t *stream, int cmd, int *button); +void mp_dvdnav_handle_input(stream_t *stream, int cmd, int *button); void mp_dvdnav_update_mouse_pos(stream_t *stream, int32_t x, int32_t y, int* button); void mp_dvdnav_get_highlight (stream_t *stream, nav_highlight_t *hl); unsigned int *mp_dvdnav_get_spu_clut(stream_t *stream); void mp_dvdnav_switch_title(stream_t *stream, int title); +int mp_dvdnav_is_eof (stream_t *stream); +int mp_dvdnav_skip_still (stream_t *stream); +int mp_dvdnav_skip_wait (stream_t *stream); +void mp_dvdnav_read_wait (stream_t *stream, int mode, int automode); +int mp_dvdnav_cell_has_changed (stream_t *stream, int clear); #endif /* MPLAYER_STREAM_DVDNAV_H */