From 73fb23c1cfc40a9e0683f0a82cb1a669005eaa67 Mon Sep 17 00:00:00 2001 From: Uoti Urpala Date: Thu, 19 Mar 2009 05:25:12 +0200 Subject: Add improved relative seek mode When the new mode is active relative seeks are converted to absolute ones (current video pts + relative seek amount) and forward/backward flag before being sent to the demuxer. This mode is used if the demuxer has set the accurate_seek field in the demuxer struct and there is a video stream. At the moment the mkv and lavf demuxers enable the flag. This change is useful for later Matroska ordered chapter support (and for more general timelime editing), but also fixes problems in existing functionality. The main problem with the old mode, where relative seeks are passed directly to the demuxer, is that the user wants to seek relative to the currently displayed position but the demuxer does not know what that position is. There can be an arbitrary amount of buffering between the demuxer read position and what is displayed on the screen. In some situations this makes small seeks fail to move backward at all (especially visible at high playback speed, when audio needs to be demuxed and decoded further ahead to fill the output buffers after resampling). Some container formats that can be used with the lavf demuxer do not always have reliable timestamps that could be used for unambiguous absolute seeking. However I made the demuxer always enable the new mode because it already converted all seeks to absolute ones before sending them to libavformat, so cases without reliable absolute seeks were failing already and this should only improve the working cases. --- mplayer.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'mplayer.c') diff --git a/mplayer.c b/mplayer.c index 1690f75ddb..a30fe21899 100644 --- a/mplayer.c +++ b/mplayer.c @@ -2463,6 +2463,16 @@ static void edl_update(MPContext *mpctx) static int seek(MPContext *mpctx, double amount, int style) { current_module = "seek"; + if (mpctx->demuxer->accurate_seek && mpctx->sh_video + && !(style & (SEEK_ABSOLUTE | SEEK_FACTOR))) { + style |= SEEK_ABSOLUTE; + if (amount > 0) + style |= SEEK_FORWARD; + else + style |= SEEK_BACKWARD; + amount += mpctx->sh_video->pts; + } + if (demux_seek(mpctx->demuxer, amount, audio_delay, style) == 0) return -1; -- cgit v1.2.3 From 694c067e19dcbabe8f6121923fe628b9bfa6ac32 Mon Sep 17 00:00:00 2001 From: Uoti Urpala Date: Mon, 30 Mar 2009 02:06:58 +0300 Subject: options: Move osd_level and osd_duration to options struct --- mplayer.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) (limited to 'mplayer.c') diff --git a/mplayer.c b/mplayer.c index a30fe21899..ec91b2a10e 100644 --- a/mplayer.c +++ b/mplayer.c @@ -201,10 +201,8 @@ int enqueue=0; static int list_properties = 0; -int osd_level=1; // if nonzero, hide current OSD contents when GetTimerMS() reaches this unsigned int osd_visible; -int osd_duration = 1000; int term_osd = 1; static char* term_osd_esc = "\x1b[A\r\x1b[K"; @@ -1403,6 +1401,7 @@ static void clear_osd_msgs(void) { static mp_osd_msg_t* get_osd_msg(struct MPContext *mpctx) { + struct MPOpts *opts = &mpctx->opts; mp_osd_msg_t *msg,*prev,*last = NULL; static unsigned last_update = 0; unsigned now = GetTimerMS(); @@ -1428,14 +1427,16 @@ static mp_osd_msg_t* get_osd_msg(struct MPContext *mpctx) // Look for the first message in the stack with high enough level. for(msg = osd_msg_stack ; msg ; last = msg, msg = prev) { prev = msg->prev; - if(msg->level > osd_level && hidden_dec_done) continue; + if (msg->level > opts->osd_level && hidden_dec_done) + continue; // The message has a high enough level or it is the first hidden one // in both cases we decrement the timer or kill it. if(!msg->started || msg->time > diff) { if(msg->started) msg->time -= diff; else msg->started = 1; // display it - if(msg->level <= osd_level) return msg; + if (msg->level <= opts->osd_level) + return msg; hidden_dec_done = 1; continue; } @@ -1461,8 +1462,9 @@ static mp_osd_msg_t* get_osd_msg(struct MPContext *mpctx) */ void set_osd_bar(struct MPContext *mpctx, int type,const char* name,double min,double max,double val) { - - if(osd_level < 1) return; + struct MPOpts *opts = &mpctx->opts; + if (opts->osd_level < 1) + return; if(mpctx->sh_video) { osd_visible = (GetTimerMS() + 1000) | 1; @@ -1472,8 +1474,8 @@ void set_osd_bar(struct MPContext *mpctx, int type,const char* name,double min,d return; } - set_osd_msg(OSD_MSG_BAR,1,osd_duration,"%s: %d %%", - name,ROUND(100*(val-min)/(max-min))); + set_osd_msg(OSD_MSG_BAR, 1, opts->osd_duration, "%s: %d %%", + name, ROUND(100*(val-min)/(max-min))); } @@ -1488,6 +1490,7 @@ void set_osd_bar(struct MPContext *mpctx, int type,const char* name,double min,d static void update_osd_msg(struct MPContext *mpctx) { + struct MPOpts *opts = &mpctx->opts; mp_osd_msg_t *msg; struct osd_state *osd = mpctx->osd; char osd_text_timer[128]; @@ -1504,7 +1507,7 @@ static void update_osd_msg(struct MPContext *mpctx) if(mpctx->sh_video) { // fallback on the timer - if(osd_level>=2) { + if (opts->osd_level >= 2) { int len = demuxer_get_time_length(mpctx->demuxer); int percentage = -1; char percentage_text[10]; @@ -1518,7 +1521,7 @@ static void update_osd_msg(struct MPContext *mpctx) else percentage_text[0] = 0; - if (osd_level == 3) + if (opts->osd_level == 3) snprintf(osd_text_timer, 63, "%c %02d:%02d:%02d / %02d:%02d:%02d%s", mpctx->osd_function,pts/3600,(pts/60)%60,pts%60, -- cgit v1.2.3 From 0f590fce191097887a81d0a6676cb0a943ce54e4 Mon Sep 17 00:00:00 2001 From: Uoti Urpala Date: Mon, 30 Mar 2009 03:13:17 +0300 Subject: core: Clean up OSD seek info logic Clean up the code and make the behavior more consistent. Before bits of the OSD information were triggered in different places, and various property commands that affect playback position only showed the seek bar while the main seek command also triggered showing the percentage in OSD text. Now only the seek and chapter commands trigger all information and others nothing (which is consistent with most property behavior). --- mplayer.c | 43 +++++++++++++++++-------------------------- 1 file changed, 17 insertions(+), 26 deletions(-) (limited to 'mplayer.c') diff --git a/mplayer.c b/mplayer.c index ec91b2a10e..50f1ec4f0a 100644 --- a/mplayer.c +++ b/mplayer.c @@ -201,9 +201,6 @@ int enqueue=0; static int list_properties = 0; -// if nonzero, hide current OSD contents when GetTimerMS() reaches this -unsigned int osd_visible; - int term_osd = 1; static char* term_osd_esc = "\x1b[A\r\x1b[K"; static char* playing_msg = NULL; @@ -211,7 +208,6 @@ static char* playing_msg = NULL; static double seek_to_sec; static off_t seek_to_byte=0; static off_t step_sec=0; -static int loop_seek=0; static m_time_size_t end_at = { .type = END_AT_NONE, .pos = 0 }; @@ -312,7 +308,6 @@ static char* rtc_device; edl_record_ptr edl_records = NULL; ///< EDL entries memory area edl_record_ptr next_edl_record = NULL; ///< only for traversing edl_records -short edl_decision = 0; ///< 1 when an EDL operation has been made. FILE* edl_fd = NULL; ///< fd to write to when in -edlout mode. int use_filedir_conf; @@ -1408,16 +1403,18 @@ static mp_osd_msg_t* get_osd_msg(struct MPContext *mpctx) unsigned diff; char hidden_dec_done = 0; - if (osd_visible) { + if (mpctx->osd_visible) { // 36000000 means max timed visibility is 1 hour into the future, if // the difference is greater assume it's wrapped around from below 0 - if (osd_visible - now > 36000000) { - osd_visible = 0; + if (mpctx->osd_visible - now > 36000000) { + mpctx->osd_visible = 0; vo_osd_progbar_type = -1; // disable vo_osd_changed(OSDTYPE_PROGBAR); mpctx->osd_function = mpctx->paused ? OSD_PAUSE : OSD_PLAY; } } + if (mpctx->osd_show_percentage_until - now > 36000000) + mpctx->osd_show_percentage_until = 0; if(!last_update) last_update = now; diff = now >= last_update ? now - last_update : 0; @@ -1467,7 +1464,7 @@ void set_osd_bar(struct MPContext *mpctx, int type,const char* name,double min,d return; if(mpctx->sh_video) { - osd_visible = (GetTimerMS() + 1000) | 1; + mpctx->osd_visible = (GetTimerMS() + 1000) | 1; vo_osd_progbar_type = type; vo_osd_progbar_value = 256*(val-min)/(max-min); vo_osd_changed(OSDTYPE_PROGBAR); @@ -1494,7 +1491,15 @@ static void update_osd_msg(struct MPContext *mpctx) mp_osd_msg_t *msg; struct osd_state *osd = mpctx->osd; char osd_text_timer[128]; - + + if (mpctx->add_osd_seek_info) { + set_osd_bar(mpctx, 0, "Position", 0, 100, + demuxer_get_percent_pos(mpctx->demuxer)); + if (mpctx->sh_video) + mpctx->osd_show_percentage_until = (GetTimerMS() + 1000) | 1; + mpctx->add_osd_seek_info = false; + } + // Look if we have a msg if((msg = get_osd_msg(mpctx))) { if (strcmp(osd->osd_text, msg->msg)) { @@ -1513,7 +1518,7 @@ static void update_osd_msg(struct MPContext *mpctx) char percentage_text[10]; int pts = demuxer_get_current_time(mpctx->demuxer); - if (mpctx->osd_show_percentage) + if (mpctx->osd_show_percentage_until) percentage = demuxer_get_percent_pos(mpctx->demuxer); if (percentage >= 0) @@ -1533,10 +1538,6 @@ static void update_osd_msg(struct MPContext *mpctx) } else osd_text_timer[0]=0; - // always decrement the percentage timer - if(mpctx->osd_show_percentage) - mpctx->osd_show_percentage--; - if (strcmp(osd->osd_text, osd_text_timer)) { strncpy(osd->osd_text, osd_text_timer, 63); vo_osd_changed(OSDTYPE_OSD); @@ -2446,7 +2447,6 @@ static void edl_update(MPContext *mpctx) mp_msg(MSGT_CPLAYER, MSGL_DBG4, "EDL_SKIP: start [%f], stop " "[%f], length [%f]\n", next_edl_record->start_sec, next_edl_record->stop_sec, next_edl_record->length_sec); - edl_decision = 1; } else if (next_edl_record->action == EDL_MUTE) { mpctx->edl_muted = !mpctx->edl_muted; @@ -3928,22 +3928,13 @@ if (step_sec > 0 && !mpctx->paused) { play_n_frames=play_n_frames_mf; mpctx->stop_play=0; mpctx->abs_seek_pos=SEEK_ABSOLUTE; mpctx->rel_seek_secs=seek_to_sec; - loop_seek = 1; } if(mpctx->rel_seek_secs || mpctx->abs_seek_pos){ - if (seek(mpctx, mpctx->rel_seek_secs, mpctx->abs_seek_pos) >= 0) { - // Set OSD: - if(!loop_seek){ - if( !edl_decision ) - set_osd_bar(mpctx, 0,"Position",0,100,demuxer_get_percent_pos(mpctx->demuxer)); - } - } + seek(mpctx, mpctx->rel_seek_secs, mpctx->abs_seek_pos); mpctx->rel_seek_secs=0; mpctx->abs_seek_pos=0; - loop_seek=0; - edl_decision = 0; } #ifdef CONFIG_GUI -- cgit v1.2.3 From 58497380e5222749892414089d26916095c0485c Mon Sep 17 00:00:00 2001 From: Uoti Urpala Date: Sun, 29 Mar 2009 22:45:06 +0300 Subject: Initial ordered chapters support Add basic support for Matroska ordered chapters. The switching between segments is implemented as a general edit timeline that could also be used for other purposes. Some things still need improvement. In particular the current code does not try to do any proper mapping between audio/video/subtitle streams of different files and there should be options for better control of how MPlayer searches other files for the required content. --- mplayer.c | 330 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 311 insertions(+), 19 deletions(-) (limited to 'mplayer.c') diff --git a/mplayer.c b/mplayer.c index 50f1ec4f0a..84b9d6ad0a 100644 --- a/mplayer.c +++ b/mplayer.c @@ -76,6 +76,7 @@ #include "osdep/getch2.h" #include "osdep/timer.h" +#include "osdep/findfiles.h" #ifdef CONFIG_GUI #include "gui/interface.h" @@ -565,6 +566,20 @@ void uninit_player(struct MPContext *mpctx, unsigned int mask){ if(mask&INITIALIZED_DEMUXER){ mpctx->initialized_flags&=~INITIALIZED_DEMUXER; current_module="free_demuxer"; + if (mpctx->num_sources) { + mpctx->demuxer = mpctx->sources[0].demuxer; + for (int i = 1; i < mpctx->num_sources; i++) { + free_stream(mpctx->sources[i].stream); + free_demuxer(mpctx->sources[i].demuxer); + } + } + talloc_free(mpctx->sources); + mpctx->sources = NULL; + mpctx->num_sources = 0; + talloc_free(mpctx->timeline); + mpctx->timeline = NULL; + mpctx->num_timeline_parts = 0; + mpctx->video_offset = 0; if(mpctx->demuxer){ mpctx->stream=mpctx->demuxer->stream; free_demuxer(mpctx->demuxer); @@ -1493,8 +1508,13 @@ static void update_osd_msg(struct MPContext *mpctx) char osd_text_timer[128]; if (mpctx->add_osd_seek_info) { - set_osd_bar(mpctx, 0, "Position", 0, 100, - demuxer_get_percent_pos(mpctx->demuxer)); + double percentage; + if (mpctx->timeline && mpctx->sh_video) + percentage = mpctx->sh_video->pts * 100 / + mpctx->timeline[mpctx->num_timeline_parts].start; + else + percentage = demuxer_get_percent_pos(mpctx->demuxer); + set_osd_bar(mpctx, 0, "Position", 0, 100, percentage); if (mpctx->sh_video) mpctx->osd_show_percentage_until = (GetTimerMS() + 1000) | 1; mpctx->add_osd_seek_info = false; @@ -1513,13 +1533,21 @@ static void update_osd_msg(struct MPContext *mpctx) if(mpctx->sh_video) { // fallback on the timer if (opts->osd_level >= 2) { - int len = demuxer_get_time_length(mpctx->demuxer); + int len; + if (mpctx->timeline) + len = mpctx->timeline[mpctx->num_timeline_parts].start; + else + len = demuxer_get_time_length(mpctx->demuxer); int percentage = -1; char percentage_text[10]; int pts = demuxer_get_current_time(mpctx->demuxer); if (mpctx->osd_show_percentage_until) - percentage = demuxer_get_percent_pos(mpctx->demuxer); + if (mpctx->timeline) + percentage = mpctx->sh_video->pts * 100 / + mpctx->timeline[mpctx->num_timeline_parts].start; + else + percentage = demuxer_get_percent_pos(mpctx->demuxer); if (percentage >= 0) snprintf(percentage_text, 9, " (%d%%)", percentage); @@ -1682,7 +1710,7 @@ static double written_audio_pts(struct MPContext *mpctx) // to get the length in original units without speedup or slowdown a_pts -= buffered_output * mpctx->opts.playback_speed / ao_data.bps; - return a_pts; + return a_pts + mpctx->video_offset; } // Return pts value corresponding to currently playing audio. @@ -2220,7 +2248,7 @@ static double update_video_nocorrect_pts(struct MPContext *mpctx, #endif if (decoded_frame) { // These updates are done here for vf_expand OSD/subtitles - update_subtitles(sh_video, mpctx->d_sub, 0); + update_subtitles(sh_video, mpctx->d_sub, mpctx->video_offset, 0); update_teletext(sh_video, mpctx->demuxer, 0); update_osd_msg(mpctx); current_module = "filter video"; @@ -2252,6 +2280,8 @@ static double update_video(struct MPContext *mpctx, int *blit_frame) unsigned char *packet = NULL; bool hit_eof = false; int in_size = ds_get_packet_pts(mpctx->d_video, &packet, &pts); + if (pts != MP_NOPTS_VALUE) + pts += mpctx->video_offset; if (in_size < 0) { // try to extract last frames in case of decoder lag in_size = 0; @@ -2266,7 +2296,7 @@ static double update_video(struct MPContext *mpctx, int *blit_frame) framedrop_type, pts); if (decoded_frame) { // These updates are done here for vf_expand OSD/subtitles - update_subtitles(sh_video, mpctx->d_sub, 0); + update_subtitles(sh_video, mpctx->d_sub, mpctx->video_offset, 0); update_teletext(sh_video, mpctx->demuxer, 0); update_osd_msg(mpctx); current_module = "filter video"; @@ -2459,6 +2489,48 @@ static void edl_update(MPContext *mpctx) } } +static void reinit_decoders(struct MPContext *mpctx) +{ + reinit_video_chain(mpctx); + reinit_audio_chain(mpctx); + mp_property_do("sub", M_PROPERTY_SET, &mpctx->global_sub_pos, mpctx); +} + +static bool timeline_set_part(struct MPContext *mpctx, int i) +{ + struct timeline_part *p = mpctx->timeline + mpctx->timeline_part; + struct timeline_part *n = mpctx->timeline + i; + mpctx->timeline_part = i; + mpctx->video_offset = n->start - n->source_start; + if (n->source == p->source) + return false; + uninit_player(mpctx, INITIALIZED_VCODEC | (mpctx->opts.fixed_vo && mpctx->opts.video_id != -2 ? 0 : INITIALIZED_VO) | INITIALIZED_AO | INITIALIZED_ACODEC); + mpctx->demuxer = n->source->demuxer; + mpctx->d_video = mpctx->demuxer->video; + mpctx->d_audio = mpctx->demuxer->audio; + mpctx->d_sub = mpctx->demuxer->sub; + mpctx->sh_video = mpctx->d_video->sh; + mpctx->sh_audio = mpctx->d_audio->sh; + return true; +} + +// Given pts, switch playback to the corresponding part. +// Return offset within that part. +static double timeline_set_from_time(struct MPContext *mpctx, double pts, + bool *need_reset) +{ + if (pts < 0) + pts = 0; + for (int i = 0; i < mpctx->num_timeline_parts; i++) { + struct timeline_part *p = mpctx->timeline + i; + if (pts < (p+1)->start) { + *need_reset = timeline_set_part(mpctx, i); + return pts - p->start + p->source_start; + } + } + return -1; +} + // style & SEEK_ABSOLUTE == 0 means seek relative to current position, == 1 means absolute // style & SEEK_FACTOR == 0 means amount in seconds, == 2 means fraction of file length @@ -2466,7 +2538,13 @@ static void edl_update(MPContext *mpctx) static int seek(MPContext *mpctx, double amount, int style) { current_module = "seek"; - if (mpctx->demuxer->accurate_seek && mpctx->sh_video + if (mpctx->stop_play == AT_END_OF_FILE) + mpctx->stop_play = KEEP_PLAYING; + if (mpctx->timeline && style & SEEK_FACTOR) { + amount *= mpctx->timeline[mpctx->num_timeline_parts].start; + style &= ~SEEK_FACTOR; + } + if ((mpctx->demuxer->accurate_seek || mpctx->timeline) && mpctx->sh_video && !(style & (SEEK_ABSOLUTE | SEEK_FACTOR))) { style |= SEEK_ABSOLUTE; if (amount > 0) @@ -2476,7 +2554,27 @@ static int seek(MPContext *mpctx, double amount, int style) amount += mpctx->sh_video->pts; } - if (demux_seek(mpctx->demuxer, amount, audio_delay, style) == 0) + /* At least the liba52 decoder wants to read from the input stream + * during initialization, so reinit must be done after the demux_seek() + * call that clears possible stream EOF. */ + bool need_reset = false; + if (mpctx->timeline) { + amount = timeline_set_from_time(mpctx, amount, &need_reset); + if (amount == -1) { + mpctx->stop_play = AT_END_OF_FILE; + // Clear audio from current position + if (mpctx->sh_audio) { + mpctx->audio_out->reset(); + mpctx->sh_audio->a_buffer_len = 0; + mpctx->sh_audio->a_out_buffer_len = 0; + } + return -1; + } + } + int seekresult = demux_seek(mpctx->demuxer, amount, audio_delay, style); + if (need_reset) + reinit_decoders(mpctx); + if (seekresult == 0) return -1; if (mpctx->sh_video) { @@ -2493,8 +2591,8 @@ static int seek(MPContext *mpctx, double amount, int style) // Not all demuxers set d_video->pts during seek, so this value // (which is used by at least vobsub and edl code below) may // be completely wrong (probably 0). - mpctx->sh_video->pts = mpctx->d_video->pts; - update_subtitles(mpctx->sh_video, mpctx->d_sub, 1); + mpctx->sh_video->pts = mpctx->d_video->pts + mpctx->video_offset; + update_subtitles(mpctx->sh_video, mpctx->d_sub, mpctx->video_offset, 1); update_teletext(mpctx->sh_video, mpctx->demuxer, 1); } @@ -2520,6 +2618,177 @@ static int seek(MPContext *mpctx, double amount, int style) return 0; } +static int find_ordered_chapter_sources(struct MPContext *mpctx, + struct content_source *sources, + int num_sources, + unsigned char uid_map[][16]) +{ + int num_filenames = 0; + char **filenames = NULL; + if (num_sources > 1) { + mp_msg(MSGT_CPLAYER, MSGL_INFO, "This file references data from " + "other sources.\n"); + if (mpctx->stream->type != STREAMTYPE_FILE) { + mp_msg(MSGT_CPLAYER, MSGL_WARN, "Playback source is not a " + "normal disk file. Will not search for related files.\n"); + } else { + mp_msg(MSGT_CPLAYER, MSGL_INFO, "Will scan other files in the " + "same directory to find referenced sources.\n"); + filenames = find_files(mpctx->demuxer->filename, ".mkv", + &num_filenames); + } + } + + int num_left = num_sources - 1; + for (int i = 0; i < num_filenames && num_left > 0; i++) { + mp_msg(MSGT_CPLAYER, MSGL_INFO, "Checking file %s\n", + filename_recode(filenames[i])); + int format; + struct stream *s = open_stream(filenames[i], &mpctx->opts, &format); + if (!s) + continue; + struct demuxer *d = demux_open(&mpctx->opts, s, DEMUXER_TYPE_MATROSKA, + mpctx->opts.audio_id, + mpctx->opts.video_id, + mpctx->opts.sub_id, filenames[i]); + if (!d) { + free_stream(s); + continue; + } + if (d->file_format == DEMUXER_TYPE_MATROSKA) { + for (int i = 1; i < num_sources; i++) { + if (sources[i].demuxer) + continue; + if (!memcmp(uid_map[i], d->matroska_data.segment_uid, 16)) { + mp_msg(MSGT_CPLAYER, MSGL_INFO,"Match for source %d: %s\n", + i, filename_recode(d->filename)); + sources[i].stream = s; + sources[i].demuxer = d; + num_left--; + goto match; + } + } + } + free_demuxer(d); + free_stream(s); + continue; + match: + ; + } + talloc_free(filenames); + if (num_left) { + mp_msg(MSGT_CPLAYER, MSGL_ERR, "Failed to find ordered chapter part!\n" + "There will be parts MISSING from the video!\n"); + for (int i = 1, j = 1; i < num_sources; i++) + if (sources[i].demuxer) { + sources[j] = sources[i]; + memcpy(uid_map[j], uid_map[i], 16); + j++; + } + } + return num_sources - num_left; +} + +static void build_ordered_chapter_timeline(struct MPContext *mpctx) +{ + mp_msg(MSGT_CPLAYER, MSGL_INFO, "File uses ordered chapters, will build " + "edit timeline.\n"); + + struct demuxer *demuxer = mpctx->demuxer; + struct matroska_data *m = &demuxer->matroska_data; + + struct content_source *sources = talloc_array_ptrtype(NULL, sources, + m->num_ordered_chapters); + sources[0].stream = mpctx->stream; + sources[0].demuxer = mpctx->demuxer; + unsigned char uid_map[m->num_ordered_chapters][16]; + int num_sources = 1; + memcpy(uid_map[0], m->segment_uid, 16); + + for (int i = 0; i < m->num_ordered_chapters; i++) { + struct matroska_chapter *c = m->ordered_chapters + i; + if (!c->has_segment_uid) + memcpy(c->segment_uid, m->segment_uid, 16); + + for (int j = 0; j < num_sources; j++) + if (!memcmp(c->segment_uid, uid_map[j], 16)) + goto found1; + memcpy(uid_map[num_sources], c->segment_uid, 16); + sources[num_sources] = (struct content_source){}; + num_sources++; + found1: + ; + } + + num_sources = find_ordered_chapter_sources(mpctx, sources, num_sources, + uid_map); + + + struct timeline_part *timeline = talloc_array_ptrtype(NULL, timeline, + m->num_ordered_chapters + 1); + uint64_t starttime = 0; + uint64_t missing_time = 0; + int part_count = 0; + for (int i = 0; i < m->num_ordered_chapters; i++) { + struct matroska_chapter *c = m->ordered_chapters + i; + + int j; + for (j = 0; j < num_sources; j++) { + if (!memcmp(c->segment_uid, uid_map[j], 16)) + goto found2; + } + missing_time += c->end - c->start; + continue; + found2:; + // Only add a separate part if the time or file actually changes + uint64_t prev_end = !part_count ? 0 : starttime + - timeline[part_count - 1].start + + timeline[part_count - 1].source_start; + if (part_count == 0 || c->start != prev_end + || sources + j != timeline[part_count - 1].source) { + timeline[part_count].source = sources + j; + timeline[part_count].start = starttime / 1000.; + timeline[part_count].source_start = c->start / 1000.; + part_count++; + } + starttime += c->end - c->start; + } + timeline[part_count].start = starttime / 1000.; + + if (!part_count) { + // None of the parts come from the file itself??? + talloc_free(sources); + talloc_free(timeline); + return; + } + + mp_msg(MSGT_CPLAYER, MSGL_V, "Timeline contains %d parts from %d " + "sources. Total length %.3f seconds.\n", part_count, num_sources, + timeline[part_count].start); + if (missing_time) + mp_msg(MSGT_CPLAYER, MSGL_ERR, "There are %.3f seconds missing " + "from the timeline!\n", missing_time / 1000.); + mp_msg(MSGT_CPLAYER, MSGL_V, "Source files:\n"); + for (int i = 0; i < num_sources; i++) + mp_msg(MSGT_CPLAYER, MSGL_V, "%d: %s\n", i, + filename_recode(sources[i].demuxer->filename)); + mp_msg(MSGT_CPLAYER, MSGL_V, "Timeline parts: (number, start, " + "source_start, source):\n"); + for (int i = 0; i < part_count; i++) { + struct timeline_part *p = timeline + i; + mp_msg(MSGT_CPLAYER, MSGL_V, "%3d %9.3f %9.3f %3td\n", i, p->start, + p->source_start, p->source - sources); + } + mp_msg(MSGT_CPLAYER, MSGL_V, "END %9.3f\n", timeline[part_count].start); + mpctx->sources = sources; + mpctx->num_sources = num_sources; + mpctx->timeline = timeline; + mpctx->num_timeline_parts = part_count; + mpctx->timeline_part = 0; + mpctx->video_offset = timeline[0].source_start; + mpctx->demuxer = timeline[0].source->demuxer; +} + static int read_keys(void *ctx, int fd) { @@ -3311,6 +3580,17 @@ if (mpctx->demuxer && mpctx->demuxer->type==DEMUXER_TYPE_PLAYLIST) if(!mpctx->demuxer) goto goto_next_file; + + if (mpctx->demuxer->matroska_data.ordered_chapters) + build_ordered_chapter_timeline(mpctx); + + if (!mpctx->sources) { + mpctx->sources = talloc_ptrtype(NULL, mpctx->sources); + *mpctx->sources = (struct content_source){.stream = mpctx->stream, + .demuxer = mpctx->demuxer}; + mpctx->num_sources = 1; + } + if(dvd_chapter>1) { float pts; if (demuxer_seek_chapter(mpctx->demuxer, dvd_chapter-1, 1, &pts, NULL, NULL) >= 0 && pts > -1.0) @@ -3335,14 +3615,17 @@ if (mpctx->global_sub_size <= mpctx->global_sub_indices[SUB_SOURCE_DEMUX] + opts #ifdef CONFIG_ASS if (ass_enabled && ass_library) { - for (i = 0; i < mpctx->demuxer->num_attachments; ++i) { - demux_attachment_t* att = mpctx->demuxer->attachments + i; - if (extract_embedded_fonts && - att->name && att->type && att->data && att->data_size && - (strcmp(att->type, "application/x-truetype-font") == 0 || - strcmp(att->type, "application/x-font") == 0)) - ass_add_font(ass_library, att->name, att->data, att->data_size); - } + for (int j = 0; j < mpctx->num_sources; j++) { + struct demuxer *d = mpctx->sources[j].demuxer; + for (int i = 0; i < d->num_attachments; i++) { + struct demux_attachment *att = d->attachments + i; + if (extract_embedded_fonts + && att->name && att->type && att->data && att->data_size + && (strcmp(att->type, "application/x-truetype-font") == 0 + || strcmp(att->type, "application/x-font") == 0)) + ass_add_font(ass_library, att->name, att->data, att->data_size); + } + } } #endif @@ -3775,6 +4058,15 @@ if(!mpctx->sh_video) { } } } + if (mpctx->timeline) { + struct timeline_part *next = mpctx->timeline + mpctx->timeline_part + 1; + if (mpctx->sh_video->pts >= next->start + || mpctx->stop_play == AT_END_OF_FILE + && mpctx->timeline_part + 1 < mpctx->num_timeline_parts) { + seek(mpctx, next->start, SEEK_ABSOLUTE); + continue; + } + } // ========================================================================== -- cgit v1.2.3 From 7db643095995ddf625af670f6e07a61bf25d3a94 Mon Sep 17 00:00:00 2001 From: Uoti Urpala Date: Tue, 31 Mar 2009 02:50:34 +0300 Subject: VO: Don't reset pause status in VO config() functions Many VOs kept track of pause status, but reset the value when their config() function was called. However it can be called while playback stays in pause mode. Modify the VOs to not change anything in config(). Also send the VO either VOCTRL_PAUSE or VOCTRL_RESUME when the playback of a new file is starting to make sure they have the right status. --- mplayer.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'mplayer.c') diff --git a/mplayer.c b/mplayer.c index 84b9d6ad0a..6e49876afe 100644 --- a/mplayer.c +++ b/mplayer.c @@ -3984,6 +3984,10 @@ if (mpctx->stream->type == STREAMTYPE_DVDNAV) { mpctx->drop_message_shown = 0; mpctx->update_video_immediately = true; mpctx->total_avsync_change = 0; + // Make sure VO knows current pause state + if (mpctx->sh_video) + vo_control(mpctx->video_out, mpctx->paused ? VOCTRL_PAUSE : VOCTRL_RESUME, + NULL); while(!mpctx->stop_play){ float aq_sleep_time=0; -- cgit v1.2.3 From f12c83b85b135c1cb9fb34e978eb0c8051450da8 Mon Sep 17 00:00:00 2001 From: Uoti Urpala Date: Wed, 1 Apr 2009 19:55:26 +0300 Subject: Change demuxer_seek_chapter() parameters Remove the "num_chapters" and "mode" parameters that aren't needed by any callers. Change "float *seek_pts" to "double *". Allocate the string returned via "chapter_name" with talloc. --- mplayer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'mplayer.c') diff --git a/mplayer.c b/mplayer.c index 6e49876afe..42d24f03e6 100644 --- a/mplayer.c +++ b/mplayer.c @@ -3592,8 +3592,8 @@ if(!mpctx->demuxer) } if(dvd_chapter>1) { - float pts; - if (demuxer_seek_chapter(mpctx->demuxer, dvd_chapter-1, 1, &pts, NULL, NULL) >= 0 && pts > -1.0) + double pts; + if (demuxer_seek_chapter(mpctx->demuxer, dvd_chapter-1, &pts, NULL) >= 0 && pts > -1.0) seek(mpctx, pts, SEEK_ABSOLUTE); } -- cgit v1.2.3 From 2d91b19956c7d444ba440078e1c2ecd23d207a87 Mon Sep 17 00:00:00 2001 From: Uoti Urpala Date: Thu, 2 Apr 2009 05:00:22 +0300 Subject: Support chapter seeking with ordered chapters --- mplayer.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 52 insertions(+), 3 deletions(-) (limited to 'mplayer.c') diff --git a/mplayer.c b/mplayer.c index 42d24f03e6..bd5957cee0 100644 --- a/mplayer.c +++ b/mplayer.c @@ -579,6 +579,8 @@ void uninit_player(struct MPContext *mpctx, unsigned int mask){ talloc_free(mpctx->timeline); mpctx->timeline = NULL; mpctx->num_timeline_parts = 0; + talloc_free(mpctx->chapters); + mpctx->num_chapters = 0; mpctx->video_offset = 0; if(mpctx->demuxer){ mpctx->stream=mpctx->demuxer->stream; @@ -2618,6 +2620,43 @@ static int seek(MPContext *mpctx, double amount, int style) return 0; } +int get_current_chapter(struct MPContext *mpctx) +{ + if (!mpctx->chapters || !mpctx->sh_video) + return demuxer_get_current_chapter(mpctx->demuxer); + + int i; + double current_pts = mpctx->sh_video->pts; + for (i = 1; i < mpctx->num_chapters; i++) + if (current_pts < mpctx->chapters[i].start) + break; + return i - 1; +} + +// currently returns a string allocated with malloc, not talloc +char *chapter_display_name(struct MPContext *mpctx, int chapter) +{ + if (!mpctx->chapters || !mpctx->sh_video) + return demuxer_chapter_display_name(mpctx->demuxer, chapter); + return strdup(mpctx->chapters[chapter].name); +} + +int seek_chapter(struct MPContext *mpctx, int chapter, double *seek_pts, + char **chapter_name) +{ + if (!mpctx->chapters || !mpctx->sh_video) + return demuxer_seek_chapter(mpctx->demuxer, chapter, seek_pts, + chapter_name); + if (chapter >= mpctx->num_chapters) + return -1; + if (chapter < 0) + chapter = 0; + *seek_pts = mpctx->chapters[chapter].start; + if (chapter_name) + *chapter_name = talloc_strdup(NULL, mpctx->chapters[chapter].name); + return chapter; +} + static int find_ordered_chapter_sources(struct MPContext *mpctx, struct content_source *sources, int num_sources, @@ -2726,9 +2765,12 @@ static void build_ordered_chapter_timeline(struct MPContext *mpctx) struct timeline_part *timeline = talloc_array_ptrtype(NULL, timeline, m->num_ordered_chapters + 1); + struct chapter *chapters = talloc_array_ptrtype(NULL, chapters, + m->num_ordered_chapters); uint64_t starttime = 0; uint64_t missing_time = 0; int part_count = 0; + int num_chapters = 0; for (int i = 0; i < m->num_ordered_chapters; i++) { struct matroska_chapter *c = m->ordered_chapters + i; @@ -2740,6 +2782,8 @@ static void build_ordered_chapter_timeline(struct MPContext *mpctx) missing_time += c->end - c->start; continue; found2:; + chapters[num_chapters].start = starttime / 1000.; + chapters[num_chapters].name = talloc_strdup(chapters, c->name); // Only add a separate part if the time or file actually changes uint64_t prev_end = !part_count ? 0 : starttime - timeline[part_count - 1].start @@ -2747,11 +2791,12 @@ static void build_ordered_chapter_timeline(struct MPContext *mpctx) if (part_count == 0 || c->start != prev_end || sources + j != timeline[part_count - 1].source) { timeline[part_count].source = sources + j; - timeline[part_count].start = starttime / 1000.; + timeline[part_count].start = chapters[num_chapters].start; timeline[part_count].source_start = c->start / 1000.; part_count++; } starttime += c->end - c->start; + num_chapters++; } timeline[part_count].start = starttime / 1000.; @@ -2759,6 +2804,7 @@ static void build_ordered_chapter_timeline(struct MPContext *mpctx) // None of the parts come from the file itself??? talloc_free(sources); talloc_free(timeline); + talloc_free(chapters); return; } @@ -2784,6 +2830,9 @@ static void build_ordered_chapter_timeline(struct MPContext *mpctx) mpctx->num_sources = num_sources; mpctx->timeline = timeline; mpctx->num_timeline_parts = part_count; + mpctx->num_chapters = num_chapters; + mpctx->chapters = chapters; + mpctx->timeline_part = 0; mpctx->video_offset = timeline[0].source_start; mpctx->demuxer = timeline[0].source->demuxer; @@ -3593,7 +3642,7 @@ if(!mpctx->demuxer) if(dvd_chapter>1) { double pts; - if (demuxer_seek_chapter(mpctx->demuxer, dvd_chapter-1, &pts, NULL) >= 0 && pts > -1.0) + if (seek_chapter(mpctx, dvd_chapter-1, &pts, NULL) >= 0 && pts > -1.0) seek(mpctx, pts, SEEK_ABSOLUTE); } @@ -3993,7 +4042,7 @@ while(!mpctx->stop_play){ float aq_sleep_time=0; if(dvd_last_chapter>0) { - int cur_chapter = demuxer_get_current_chapter(mpctx->demuxer); + int cur_chapter = get_current_chapter(mpctx); if(cur_chapter!=-1 && cur_chapter+1>dvd_last_chapter) goto goto_next_file; } -- cgit v1.2.3 From 96daf7ed5ef96b86f2539164c27155bc830aa2a6 Mon Sep 17 00:00:00 2001 From: Uoti Urpala Date: Wed, 8 Apr 2009 02:37:27 +0300 Subject: Add option -noordered-chapters. --- mplayer.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'mplayer.c') diff --git a/mplayer.c b/mplayer.c index bd5957cee0..8bcaa3430b 100644 --- a/mplayer.c +++ b/mplayer.c @@ -2730,6 +2730,12 @@ static int find_ordered_chapter_sources(struct MPContext *mpctx, static void build_ordered_chapter_timeline(struct MPContext *mpctx) { + if (!mpctx->opts.ordered_chapters) { + mp_msg(MSGT_CPLAYER, MSGL_INFO, "File uses ordered chapters, but " + "you have disabled support for them. Ignoring.\n"); + return; + } + mp_msg(MSGT_CPLAYER, MSGL_INFO, "File uses ordered chapters, will build " "edit timeline.\n"); -- cgit v1.2.3