summaryrefslogtreecommitdiffstats
path: root/mplayer.c
diff options
context:
space:
mode:
Diffstat (limited to 'mplayer.c')
-rw-r--r--mplayer.c330
1 files changed, 311 insertions, 19 deletions
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;
+ }
+ }
// ==========================================================================