Mercurial > mplayer.hg
changeset 18917:d9a75b26da6c
Add a new video pts tracking mode, enabled by option -correct-pts.
This mode has the following differences:
- Video timing is correct for streams with B frames, at least with some
demuxers.
- Video filters can modify frame timestamps and insert new frames, and
removing frames is handled better than before.
- Some things are known to break, it's not usable as the default yet.
Things should work as before when the -correct-pts option is not used.
author | uau |
---|---|
date | Thu, 06 Jul 2006 06:58:17 +0000 |
parents | a95ed361d16b |
children | 58b4379d19b0 |
files | cfg-mplayer.h libmpcodecs/dec_video.c libmpcodecs/dec_video.h libmpcodecs/vd.h libmpcodecs/vd_ffmpeg.c libmpcodecs/vf.c libmpcodecs/vf.h libmpcodecs/vf_tfields.c libmpcodecs/vf_vo.c libmpcodecs/vf_yadif.c libmpdemux/demux_mkv.c libmpdemux/demuxer.c libmpdemux/demuxer.h libmpdemux/stheader.h mplayer.c |
diffstat | 15 files changed, 252 insertions(+), 27 deletions(-) [+] |
line wrap: on
line diff
--- a/cfg-mplayer.h Thu Jul 06 05:05:00 2006 +0000 +++ b/cfg-mplayer.h Thu Jul 06 06:58:17 2006 +0000 @@ -349,6 +349,8 @@ {"playlist", NULL, CONF_TYPE_STRING, 0, 0, 0, NULL}, // a-v sync stuff: + {"correct-pts", &correct_pts, CONF_TYPE_FLAG, 0, 0, 1, NULL}, + {"no-correct-pts", &correct_pts, CONF_TYPE_FLAG, 0, 1, 0, NULL}, {"noautosync", &autosync, CONF_TYPE_FLAG, 0, 0, -1, NULL}, {"autosync", &autosync, CONF_TYPE_INT, CONF_RANGE, 0, 10000, NULL}, // {"dapsync", &dapsync, CONF_TYPE_FLAG, 0, 0, 1, NULL},
--- a/libmpcodecs/dec_video.c Thu Jul 06 05:05:00 2006 +0000 +++ b/libmpcodecs/dec_video.c Thu Jul 06 06:58:17 2006 +0000 @@ -137,6 +137,18 @@ if(mpvdec) mpvdec->control(sh_video, VDCTRL_RESYNC_STREAM, NULL); } +int get_current_video_decoder_lag(sh_video_t *sh_video) +{ + int ret; + + if (!mpvdec) + return -1; + ret = mpvdec->control(sh_video, VDCTRL_QUERY_UNSEEN_FRAMES, NULL); + if (ret >= 10) + return ret-10; + return -1; +} + void uninit_video(sh_video_t *sh_video){ if(!sh_video->inited) return; mp_msg(MSGT_DECVIDEO,MSGL_V,MSGTR_UninitVideoStr,sh_video->codec->drv); @@ -311,6 +323,36 @@ double tt; int ret; + if (correct_pts) { + int delay = get_current_video_decoder_lag(sh_video); + if (delay >= 0) { + if (delay > sh_video->num_buffered_pts) +#if 0 + // this is disabled because vd_ffmpeg reports the same lag + // after seek even when there are no buffered frames, + // leading to incorrect error messages + mp_msg(MSGT_DECVIDEO, MSGL_ERR, "Not enough buffered pts\n"); +#else + ; +#endif + else + sh_video->num_buffered_pts = delay; + } + if (sh_video->num_buffered_pts == + sizeof(sh_video->buffered_pts)/sizeof(double)) + mp_msg(MSGT_DECVIDEO, MSGL_ERR, "Too many buffered pts\n"); + else { + int i, j; + for (i = 0; i < sh_video->num_buffered_pts; i++) + if (sh_video->buffered_pts[i] < pts) + break; + for (j = sh_video->num_buffered_pts; j > i; j--) + sh_video->buffered_pts[j] = sh_video->buffered_pts[j-1]; + sh_video->buffered_pts[i] = pts; + sh_video->num_buffered_pts++; + } + } + //if(!(sh_video->ds->flags&1) || sh_video->ds->pack_no<5) mpi=mpvdec->decode(sh_video, start, in_size, drop_frame); @@ -333,6 +375,11 @@ if(!mpi || drop_frame) return 0; // error / skipped frame + if (correct_pts) { + sh_video->num_buffered_pts--; + pts = sh_video->buffered_pts[sh_video->num_buffered_pts]; + } + //vo_draw_image(video_out,mpi); vf=sh_video->vfilter; ret = vf->put_image(vf,mpi, pts); // apply video filters and call the leaf vo/ve
--- a/libmpcodecs/dec_video.h Thu Jul 06 05:05:00 2006 +0000 +++ b/libmpcodecs/dec_video.h Thu Jul 06 06:58:17 2006 +0000 @@ -19,5 +19,6 @@ extern int set_video_colors(sh_video_t *sh_video,char *item,int value); extern int set_rectangle(sh_video_t *sh_video,int param,int value); extern void resync_video_stream(sh_video_t *sh_video); +extern int get_current_video_decoder_lag(sh_video_t *sh_video); extern int divx_quality;
--- a/libmpcodecs/vd.h Thu Jul 06 05:05:00 2006 +0000 +++ b/libmpcodecs/vd.h Thu Jul 06 06:58:17 2006 +0000 @@ -24,6 +24,7 @@ #define VDCTRL_SET_EQUALIZER 6 /* set color options (brightness,contrast etc) */ #define VDCTRL_GET_EQUALIZER 7 /* get color options (brightness,contrast etc) */ #define VDCTRL_RESYNC_STREAM 8 /* seeking */ +#define VDCTRL_QUERY_UNSEEN_FRAMES 9 /* current decoder lag */ // callbacks: int mpcodecs_config_vo(sh_video_t *sh, int w, int h, unsigned int preferred_outfmt);
--- a/libmpcodecs/vd_ffmpeg.c Thu Jul 06 05:05:00 2006 +0000 +++ b/libmpcodecs/vd_ffmpeg.c Thu Jul 06 06:58:17 2006 +0000 @@ -188,7 +188,9 @@ break; case VDCTRL_RESYNC_STREAM: avcodec_flush_buffers(avctx); - return CONTROL_TRUE; + return CONTROL_TRUE; + case VDCTRL_QUERY_UNSEEN_FRAMES: + return avctx->has_b_frames + 10; } return CONTROL_UNKNOWN; }
--- a/libmpcodecs/vf.c Thu Jul 06 05:05:00 2006 +0000 +++ b/libmpcodecs/vf.c Thu Jul 06 06:58:17 2006 +0000 @@ -558,6 +558,38 @@ dst->qscale= src->qscale; } } + +void vf_queue_frame(vf_instance_t *vf, int (*func)(vf_instance_t *)) +{ + vf->continue_buffered_image = func; +} + +// Output the next buffered image (if any) from the filter chain. +// The queue could be kept as a simple stack/list instead avoiding the +// looping here, but there's currently no good context variable where +// that could be stored so this was easier to implement. + +int vf_output_queued_frame(vf_instance_t *vf) +{ + while (1) { + int ret; + vf_instance_t *current; + vf_instance_t *last=NULL; + int (*tmp)(vf_instance_t *); + for (current = vf; current; current = current->next) + if (current->continue_buffered_image) + last = current; + if (!last) + return 0; + tmp = last->continue_buffered_image; + last->continue_buffered_image = NULL; + ret = tmp(last); + if (ret) + return ret; + } +} + + /** * \brief Video config() function wrapper *
--- a/libmpcodecs/vf.h Thu Jul 06 05:05:00 2006 +0000 +++ b/libmpcodecs/vf.h Thu Jul 06 06:58:17 2006 +0000 @@ -43,6 +43,8 @@ void (*draw_slice)(struct vf_instance_s* vf, unsigned char** src, int* stride, int w,int h, int x, int y); void (*uninit)(struct vf_instance_s* vf); + + void (*continue_buffered_image)(struct vf_instance_s* vf); // caps: unsigned int default_caps; // used by default query_format() unsigned int default_reqs; // used by default config() @@ -93,6 +95,8 @@ unsigned int vf_match_csp(vf_instance_t** vfp,unsigned int* list,unsigned int preferred); void vf_clone_mpi_attributes(mp_image_t* dst, mp_image_t* src); +void vf_queue_frame(vf_instance_t *vf, int (*)(vf_instance_t *)); +int vf_output_queued_frame(vf_instance_t *vf); // default wrappers: int vf_next_config(struct vf_instance_s* vf,
--- a/libmpcodecs/vf_tfields.c Thu Jul 06 05:05:00 2006 +0000 +++ b/libmpcodecs/vf_tfields.c Thu Jul 06 06:58:17 2006 +0000 @@ -15,6 +15,9 @@ struct vf_priv_s { int mode; int parity; + int buffered_i; + mp_image_t *buffered_mpi; + double buffered_pts; }; static inline void *my_memcpy_pic(void * dst, void * src, int bytesPerLine, int height, int dstStride, int srcStride) @@ -311,9 +314,27 @@ static void (*qpel_li)(unsigned char *d, unsigned char *s, int w, int h, int ds, int ss, int up); static void (*qpel_4tap)(unsigned char *d, unsigned char *s, int w, int h, int ds, int ss, int up); +static int continue_buffered_image(struct vf_instance_s *); +extern int correct_pts; + static int put_image(struct vf_instance_s* vf, mp_image_t *mpi, double pts) { - int i; + vf->priv->buffered_mpi = mpi; + vf->priv->buffered_pts = pts; + vf->priv->buffered_i = 0; + return continue_buffered_image(vf); +} + +static int continue_buffered_image(struct vf_instance_s *vf) +{ + int i=vf->priv->buffered_i; + double pts = vf->priv->buffered_pts; + mp_image_t *mpi = vf->priv->buffered_mpi; + + if (i == 0) + vf_queue_frame(vf, continue_buffered_image); + pts += i * .02; // XXX not right + int ret=0; mp_image_t *dmpi; void (*qpel)(unsigned char *, unsigned char *, int, int, int, int, int); @@ -344,7 +365,7 @@ switch (vf->priv->mode) { case 0: - for (i=0; i<2; i++) { + for (; i<2; i++) { dmpi = vf_get_image(vf->next, mpi->imgfmt, MP_IMGTYPE_EXPORT, MP_IMGFLAG_ACCEPT_STRIDE, mpi->width, mpi->height/2); @@ -356,12 +377,15 @@ dmpi->stride[1] = 2*mpi->stride[1]; dmpi->stride[2] = 2*mpi->stride[2]; } - ret |= vf_next_put_image(vf, dmpi, MP_NOPTS_VALUE); - if (!i) vf_next_control(vf, VFCTRL_FLIP_PAGE, NULL); + ret |= vf_next_put_image(vf, dmpi, pts); + if (correct_pts) + break; + else + if (!i) vf_next_control(vf, VFCTRL_FLIP_PAGE, NULL); } break; case 1: - for (i=0; i<2; i++) { + for (; i<2; i++) { dmpi = vf_get_image(vf->next, mpi->imgfmt, MP_IMGTYPE_TEMP, MP_IMGFLAG_ACCEPT_STRIDE, mpi->width, mpi->height); @@ -383,14 +407,17 @@ deint(dmpi->planes[2], dmpi->stride[2], mpi->planes[2], mpi->stride[2], mpi->chroma_width, mpi->chroma_height, (i^!tff)); } - ret |= vf_next_put_image(vf, dmpi, MP_NOPTS_VALUE); - if (!i) vf_next_control(vf, VFCTRL_FLIP_PAGE, NULL); + ret |= vf_next_put_image(vf, dmpi, pts); + if (correct_pts) + break; + else + if (!i) vf_next_control(vf, VFCTRL_FLIP_PAGE, NULL); } break; case 2: case 3: case 4: - for (i=0; i<2; i++) { + for (; i<2; i++) { dmpi = vf_get_image(vf->next, mpi->imgfmt, MP_IMGTYPE_TEMP, MP_IMGFLAG_ACCEPT_STRIDE, mpi->width, mpi->height/2); @@ -406,11 +433,15 @@ mpi->chroma_width, mpi->chroma_height/2, dmpi->stride[2], mpi->stride[2]*2, (i^!tff)); } - ret |= vf_next_put_image(vf, dmpi, MP_NOPTS_VALUE); - if (!i) vf_next_control(vf, VFCTRL_FLIP_PAGE, NULL); + ret |= vf_next_put_image(vf, dmpi, pts); + if (correct_pts) + break; + else + if (!i) vf_next_control(vf, VFCTRL_FLIP_PAGE, NULL); } break; } + vf->priv->buffered_i = 1; return ret; }
--- a/libmpcodecs/vf_vo.c Thu Jul 06 05:05:00 2006 +0000 +++ b/libmpcodecs/vf_vo.c Thu Jul 06 06:58:17 2006 +0000 @@ -12,7 +12,8 @@ //===========================================================================// -#define video_out ((vo_functions_t*)(vf->priv)) +struct priv_t {double pts; vo_functions_t *vo;}; +#define video_out (((struct priv_t *)(vf->priv))->vo) static int query_format(struct vf_instance_s* vf, unsigned int fmt); /* forward declaration */ @@ -102,6 +103,8 @@ static int put_image(struct vf_instance_s* vf, mp_image_t *mpi, double pts){ if(!vo_config_count) return 0; // vo not configured? + // record pts (potentially modified by filters) for main loop + ((struct priv_t *)vf->priv)->pts = pts; // first check, maybe the vo/vf plugin implements draw_image using mpi: if(video_out->control(VOCTRL_DRAW_IMAGE,mpi)==VO_TRUE) return 1; // done. // nope, fallback to old draw_frame/draw_slice:
--- a/libmpcodecs/vf_yadif.c Thu Jul 06 05:05:00 2006 +0000 +++ b/libmpcodecs/vf_yadif.c Thu Jul 06 06:58:17 2006 +0000 @@ -47,6 +47,10 @@ struct vf_priv_s { int mode; int parity; + int buffered_i; + int buffered_tff; + double buffered_pts; + mp_image_t *buffered_mpi; int stride[3]; uint8_t *ref[4][3]; }; @@ -150,10 +154,11 @@ return vf_next_config(vf,width,height,d_width,d_height,flags,outfmt); } +static int continue_buffered_image(struct vf_instance_s *vf); +extern int correct_pts; + static int put_image(struct vf_instance_s* vf, mp_image_t *mpi, double pts){ - mp_image_t *dmpi; - int ret=0; - int tff, i; + int tff; if(vf->priv->parity < 0) { if (mpi->fields & MP_IMGFIELD_ORDERED) @@ -165,18 +170,41 @@ store_ref(vf->priv, mpi->planes, mpi->stride, mpi->w, mpi->h); - for(i=0; i<=(vf->priv->mode&1); i++){ + vf->priv->buffered_mpi = mpi; + vf->priv->buffered_tff = tff; + vf->priv->buffered_i = 0; + vf->priv->buffered_pts = pts; + + return continue_buffered_image(vf); +} + +static int continue_buffered_image(struct vf_instance_s *vf) +{ + mp_image_t *mpi = vf->priv->buffered_mpi; + int tff = vf->priv->buffered_tff; + double pts = vf->priv->buffered_pts; + int i; + int ret=0; + mp_image_t *dmpi; + + pts += vf->priv->buffered_i * .02; // XXX not right + + for(i = vf->priv->buffered_i; i<=(vf->priv->mode&1); i++){ dmpi=vf_get_image(vf->next,mpi->imgfmt, MP_IMGTYPE_TEMP, MP_IMGFLAG_ACCEPT_STRIDE|MP_IMGFLAG_PREFER_ALIGNED_STRIDE, mpi->width,mpi->height); vf_clone_mpi_attributes(dmpi, mpi); filter(vf->priv, dmpi->planes, dmpi->stride, mpi->w, mpi->h, i ^ tff ^ 1, tff); + if (correct_pts && i < (vf->priv->mode & 1)) + vf_queue_frame(vf, continue_buffered_image); ret |= vf_next_put_image(vf, dmpi, pts /*FIXME*/); + if (correct_pts) + break; if(i<(vf->priv->mode&1)) vf_next_control(vf, VFCTRL_FLIP_PAGE, NULL); } - + vf->priv->buffered_i = 1; return ret; }
--- a/libmpdemux/demux_mkv.c Thu Jul 06 05:05:00 2006 +0000 +++ b/libmpdemux/demux_mkv.c Thu Jul 06 06:58:17 2006 +0000 @@ -1690,12 +1690,12 @@ else if (!strcmp(track->codec_id, MKV_V_MPEG1)) { bih->biCompression = mmioFOURCC('m', 'p', 'g', '1'); - track->reorder_timecodes = 1; + track->reorder_timecodes = !correct_pts; } else if (!strcmp(track->codec_id, MKV_V_MPEG2)) { bih->biCompression = mmioFOURCC('m', 'p', 'g', '2'); - track->reorder_timecodes = 1; + track->reorder_timecodes = !correct_pts; } else if (!strcmp(track->codec_id, MKV_V_MPEG4_AVC)) { @@ -1706,7 +1706,7 @@ bih = (BITMAPINFOHEADER *) realloc (bih, bih->biSize); memcpy (bih + 1, track->private_data, track->private_size); } - track->reorder_timecodes = 1; + track->reorder_timecodes = !correct_pts; } else {
--- a/libmpdemux/demuxer.c Thu Jul 06 05:05:00 2006 +0000 +++ b/libmpdemux/demuxer.c Thu Jul 06 06:58:17 2006 +0000 @@ -355,7 +355,7 @@ ds->pos=p->pos; ds->dpos+=p->len; // !!! ++ds->pack_no; - if(p->pts){ + if (p->pts != (correct_pts ? MP_NOPTS_VALUE : 0)) { ds->pts=p->pts; ds->pts_bytes=0; } @@ -511,10 +511,11 @@ *start = NULL; return -1; } - // Should use MP_NOPTS_VALUE for "unknown pts" in the packets too - if (ds->current->pts) - *pts = ds->current->pts; } + // Should use MP_NOPTS_VALUE for "unknown pts" in the packets too + // Return pts unless this read starts from the middle of a packet + if (!ds->buffer_pos && (correct_pts || ds->current->pts)) + *pts = ds->current->pts; len=ds->buffer_size-ds->buffer_pos; *start = &ds->buffer[ds->buffer_pos]; ds->buffer_pos+=len; @@ -624,6 +625,8 @@ int extension_parsing=1; // 0=off 1=mixed (used only for unstable formats) +int correct_pts=0; + /* NOTE : Several demuxers may be opened at the same time so demuxers should NEVER rely on an external var to enable them
--- a/libmpdemux/demuxer.h Thu Jul 06 05:05:00 2006 +0000 +++ b/libmpdemux/demuxer.h Thu Jul 06 06:58:17 2006 +0000 @@ -126,6 +126,8 @@ struct demuxer_st; +extern int correct_pts; + /** * Demuxer description structure */ @@ -182,7 +184,9 @@ demux_packet_t* dp=(demux_packet_t*)malloc(sizeof(demux_packet_t)); dp->len=len; dp->next=NULL; - dp->pts=0; + // still using 0 by default in case there is some code that uses 0 for both + // unknown and a valid pts value + dp->pts=correct_pts ? MP_NOPTS_VALUE : 0; dp->pos=0; dp->flags=0; dp->refcount=1;
--- a/libmpdemux/stheader.h Thu Jul 06 05:05:00 2006 +0000 +++ b/libmpdemux/stheader.h Thu Jul 06 06:58:17 2006 +0000 @@ -64,6 +64,8 @@ // timing (mostly for mpeg): double pts; // predicted/interpolated PTS of the current frame double i_pts; // PTS for the _next_ I/P frame + double buffered_pts[20]; + int num_buffered_pts; // output format: (set by demuxer) float fps; // frames per second (set only if constant fps) float frametime; // 1/fps
--- a/mplayer.c Thu Jul 06 05:05:00 2006 +0000 +++ b/mplayer.c Thu Jul 06 06:58:17 2006 +0000 @@ -2466,6 +2466,40 @@ } +static int generate_video_frame(sh_video_t *sh_video, demux_stream_t *d_video) +{ + unsigned char *start; + int in_size; + int hit_eof=0; + double pts; + + while (1) { + current_module = "decode video"; + // XXX Time used in this call is not counted in any performance + // timer now + if (vf_output_queued_frame(sh_video->vfilter)) + break; + current_module = "video_read_frame"; + in_size = ds_get_packet_pts(d_video, &start, &pts); + if (in_size < 0) { + // try to extract last frames in case of decoder lag + in_size = 0; + pts = 1e300; + hit_eof = 1; + } + if (in_size > max_framesize) + max_framesize = in_size; + if (pts == MP_NOPTS_VALUE) + mp_msg(MSGT_CPLAYER, MSGL_ERR, "pts value from demuxer MISSING\n"); + if (decode_video(sh_video, start, in_size, 0, pts)) + break; + if (hit_eof) + return 0; + } + return 1; +} + + int main(int argc,char* argv[]){ @@ -3505,9 +3539,11 @@ inited_flags|=INITED_VO; } +struct {double pts; vo_functions_t *vo;} vf_vo_data; +vf_vo_data.vo = video_out; current_module="init_video_filters"; { - char* vf_arg[] = { "_oldargs_", (char*)video_out , NULL }; + char* vf_arg[] = { "_oldargs_", (char*)&vf_vo_data , NULL }; sh_video->vfilter=(void*)vf_open_filter(NULL,"vo",vf_arg); } #ifdef HAVE_MENU @@ -3594,6 +3630,7 @@ //float v_frame=0; // Video float time_frame=0; // Timer //float num_frames=0; // number of frames played +double last_pts = MP_NOPTS_VALUE; int grab_frames=0; int drop_frame=0; // current dropping status int dropped_frames=0; // how many frames dropped since last non-dropped frame @@ -3768,7 +3805,7 @@ //-------------------- Decode a frame: ----------------------- blit_frame = 0; // Don't blit if we hit EOF vdecode_time=video_time_usage; - while(1) + if (!correct_pts) while(1) { unsigned char* start=NULL; int in_size; // get it! @@ -3803,6 +3840,32 @@ blit_frame=decode_video(sh_video,start,in_size,drop_frame, MP_NOPTS_VALUE); break; } + else while (1) { + if (!generate_video_frame(sh_video, d_video)) { + eof = 1; + break; + } + sh_video->pts = vf_vo_data.pts; + if (sh_video->pts == MP_NOPTS_VALUE) { + mp_msg(MSGT_CPLAYER, MSGL_ERR, "pts after filters MISSING\n"); + sh_video->pts == last_pts; + } + if (last_pts == MP_NOPTS_VALUE) + last_pts = sh_video->pts; + else if (last_pts >= sh_video->pts) { + last_pts = sh_video->pts; + mp_msg(MSGT_CPLAYER, MSGL_WARN, "pts value <= previous"); + } + frame_time = sh_video->pts - last_pts; + last_pts = sh_video->pts; + sh_video->timer += frame_time; + time_frame += frame_time; // for nosound + if(sh_audio) + sh_audio->delay -= frame_time; + blit_frame = 1; + break; + } + vdecode_time=video_time_usage-vdecode_time; //------------------------ frame decoded. -------------------- @@ -4728,6 +4791,8 @@ current_module="seek_video_reset"; resync_video_stream(sh_video); if(vo_config_count) video_out->control(VOCTRL_RESET,NULL); + sh_video->num_buffered_pts = 0; + last_pts = MP_NOPTS_VALUE; } if(sh_audio){