Mercurial > mplayer.hg
changeset 22021:71d78117fbf0
set has_valid_timestamps and corresponding first and final pts only after
having checked that at the beginning, at the middle and at the end of the
stream timestamps don't reset and that they seem to progress (almost-) linearly;
additionally probe those timestamps only when the stream type is file and it's seekable,
so all other stream types (especially network ones) won't waste time trying to seek
and possibly slowing down detection.
Seeking is not negatively affected by these changes.
Patch by Christian Aistleitner (zaek7q gmx net) reworked by me.
author | nicodvb |
---|---|
date | Sun, 28 Jan 2007 10:45:34 +0000 |
parents | a40f222a31df |
children | 35098f42db9a |
files | libmpdemux/demux_mpg.c |
diffstat | 1 files changed, 122 insertions(+), 46 deletions(-) [+] |
line wrap: on
line diff
--- a/libmpdemux/demux_mpg.c Sun Jan 28 10:34:11 2007 +0000 +++ b/libmpdemux/demux_mpg.c Sun Jan 28 10:45:34 2007 +0000 @@ -29,8 +29,10 @@ typedef struct mpg_demuxer { float last_pts; - float final_pts; - int has_valid_timestamps; + float first_pts; // first pts found in stream + float first_to_final_pts_len; // difference between final pts and first pts + int has_valid_timestamps; // !=0 iff time stamps look linear + // (not necessarily starting with 0) unsigned int es_map[0x40]; //es map of stream types (associated to the pes id) from 0xb0 to 0xef int num_a_streams; int a_stream_ids[MAX_A_STREAMS]; @@ -102,49 +104,123 @@ // 500000 is a wild guess #define TIMESTAMP_PROBE_LEN 500000 +//MAX_PTS_DIFF_FOR_CONSECUTIVE denotes the maximum difference +//between two pts to consider them consecutive +//1.0 is a wild guess +#define MAX_PTS_DIFF_FOR_CONSECUTIVE 1.0 + +//returns the first pts found within TIME_STAMP_PROBE_LEN bytes after stream_pos in demuxer's stream. +//if no pts is found or an error occurs, -1.0 is returned. +//Packs are freed. +static float read_first_mpeg_pts_at_position(demuxer_t* demuxer, off_t stream_pos) +{ + stream_t *s = demuxer->stream; + mpg_demuxer_t *mpg_d = demuxer->priv; + float pts = -1.0; //the pts to return; + float found_pts1; //the most recently found pts + float found_pts2; //the pts found before found_pts1 + float found_pts3; //the pts found before found_pts2 + int found = 0; + + if(!mpg_d || stream_pos < 0) + return pts; + + found_pts3 = found_pts2 = found_pts1 = mpg_d->last_pts; + stream_seek(s, stream_pos); + + //We look for pts. + //However, we do not stop at the first found one, as timestamps may reset + //Therefore, we seek until we found three consecutive + //pts within MAX_PTS_DIFF_FOR_CONSECUTIVE. + + while(found<3 && !s->eof + && (fabsf(found_pts2-found_pts1) < MAX_PTS_DIFF_FOR_CONSECUTIVE) + && (fabsf(found_pts3-found_pts2) < MAX_PTS_DIFF_FOR_CONSECUTIVE) + && (stream_tell(s) < stream_pos + TIMESTAMP_PROBE_LEN) + && ds_fill_buffer(demuxer->video)) + { + if(mpg_d->last_pts != found_pts1) + { + if(!found) + found_pts3 = found_pts2 = found_pts1 = mpg_d->last_pts; //the most recently found pts + else + { + found_pts3 = found_pts2; + found_pts2 = found_pts1; + found_pts1 = mpg_d->last_pts; + } + found++; + } + } + + if(found == 3) pts = found_pts3; + + //clean up from searching of first pts; + ds_free_packs(demuxer->audio); + ds_free_packs(demuxer->video); + ds_free_packs(demuxer->sub); + + return pts; +} + /// Open an mpg physical stream static demuxer_t* demux_mpg_open(demuxer_t* demuxer) { stream_t *s = demuxer->stream; - off_t pos = stream_tell(s); - off_t end_seq_start; - float half_pts = 0.0; mpg_demuxer_t* mpg_d; if (!ds_fill_buffer(demuxer->video)) return 0; - end_seq_start = demuxer->movi_end-TIMESTAMP_PROBE_LEN; mpg_d = calloc(1,sizeof(mpg_demuxer_t)); - demuxer->priv = mpg_d; - mpg_d->final_pts = 0.0; - mpg_d->has_valid_timestamps = 1; - mpg_d->num_a_streams = 0; - if (demuxer->seekable && stream_tell(demuxer->stream) < end_seq_start) { - off_t half_pos = pos + end_seq_start / 2; - stream_seek(s, half_pos); - while ((!s->eof) && ds_fill_buffer(demuxer->video) && half_pts == 0.0) { - half_pts = mpg_d->last_pts; - if (stream_tell(s) > half_pos + TIMESTAMP_PROBE_LEN) - break; - } - stream_seek(s,end_seq_start); - while ((!s->eof) && ds_fill_buffer(demuxer->video)) { - if (mpg_d->final_pts < mpg_d->last_pts) mpg_d->final_pts = mpg_d->last_pts; - if (stream_tell(s) > demuxer->movi_end) - break; - } - // educated guess about validity of timestamps - if (mpg_d->final_pts > 3 * half_pts || mpg_d->final_pts < 1.5 * half_pts) { - mpg_d->has_valid_timestamps = 0; - } - ds_free_packs(demuxer->audio); - ds_free_packs(demuxer->video); - ds_free_packs(demuxer->sub); - demuxer->stream->eof=0; // clear eof flag - demuxer->video->eof=0; - demuxer->audio->eof=0; - - stream_seek(s,pos); - ds_fill_buffer(demuxer->video); - } + if(mpg_d) + { + demuxer->priv = mpg_d; + mpg_d->last_pts = -1.0; + mpg_d->first_pts = -1.0; + + //if seeking is allowed set has_valid_timestamps if appropriate + if(demuxer->seekable + && demuxer->stream->type == STREAMTYPE_FILE + && demuxer->movi_start != demuxer-> movi_end + ) + { + //We seek to the beginning of the stream, to somewhere in the + //middle, and to the end of the stream, while remembering the pts + //at each of the three positions. With these pts, we check whether + //or not the pts are "linear enough" to justify seeking by the pts + //of the stream + + //The position where the stream is now + off_t pos = stream_tell(s); + float first_pts = read_first_mpeg_pts_at_position(demuxer, demuxer->movi_start); + if(first_pts != -1.0) + { + float middle_pts = read_first_mpeg_pts_at_position(demuxer, (demuxer->movi_end - demuxer->movi_start)/2); + if(middle_pts != -1.0) + { + float final_pts = read_first_mpeg_pts_at_position(demuxer, demuxer->movi_end - TIMESTAMP_PROBE_LEN); + if(final_pts != -1.0) + { + // found proper first, middle, and final pts. + float proportion = (middle_pts-first_pts==0) ? -1 : (final_pts-middle_pts)/(middle_pts-first_pts); + // if they are linear enough set has_valid_timestamps + if((0.5 < proportion) && (proportion < 2)) + { + mpg_d->first_pts = first_pts; + mpg_d->first_to_final_pts_len = final_pts - first_pts; + mpg_d->has_valid_timestamps = 1; + } + } + } + } + + //Cleaning up from seeking in stream + demuxer->stream->eof=0; + demuxer->video->eof=0; + demuxer->audio->eof=0; + + stream_seek(s,pos); + ds_fill_buffer(demuxer->video); + } // if ( demuxer->seekable ) + } // if ( mpg_d ) return demuxer; } @@ -770,8 +846,8 @@ //================= seek in MPEG ========================== //calculate the pts to seek to if(flags & 2) { - if (mpg_d && mpg_d->final_pts > 0.0) - newpts += mpg_d->final_pts * rel_seek_secs; + if (mpg_d && mpg_d->first_to_final_pts_len > 0.0) + newpts += mpg_d->first_to_final_pts_len * rel_seek_secs; else newpts += rel_seek_secs * (demuxer->movi_end - demuxer->movi_start) * oldpts / oldpos; } else @@ -784,8 +860,8 @@ } else { // time seek (secs) if (mpg_d && mpg_d->has_valid_timestamps) { - if (mpg_d->final_pts > 0.0) - newpos += rel_seek_secs * (demuxer->movi_end - demuxer->movi_start) / mpg_d->final_pts; + if (mpg_d->first_to_final_pts_len > 0.0) + newpos += rel_seek_secs * (demuxer->movi_end - demuxer->movi_start) / mpg_d->first_to_final_pts_len; else if (oldpts > 0.0) newpos += rel_seek_secs * (oldpos - demuxer->movi_start) / oldpts; } else if(!sh_video || !sh_video->i_bps) // unspecified or VBR @@ -871,14 +947,14 @@ return DEMUXER_CTRL_GUESS; } if (mpg_d && mpg_d->has_valid_timestamps) { - *((double *)arg)=(double)mpg_d->final_pts; - return DEMUXER_CTRL_GUESS; + *((double *)arg)=(double)mpg_d->first_to_final_pts_len; + return DEMUXER_CTRL_OK; } return DEMUXER_CTRL_DONTKNOW; case DEMUXER_CTRL_GET_PERCENT_POS: - if (mpg_d && mpg_d->has_valid_timestamps && mpg_d->final_pts > 0.0) { - *((int *)arg)=(int)(100 * mpg_d->last_pts / mpg_d->final_pts); + if (mpg_d && mpg_d->has_valid_timestamps && mpg_d->first_to_final_pts_len > 0.0) { + *((int *)arg)=(int)(100 * (mpg_d->last_pts-mpg_d->first_pts) / mpg_d->first_to_final_pts_len); return DEMUXER_CTRL_OK; } return DEMUXER_CTRL_DONTKNOW;