diff options
-rw-r--r-- | src/dxr3/dxr3_decode_video.c | 139 |
1 files changed, 99 insertions, 40 deletions
diff --git a/src/dxr3/dxr3_decode_video.c b/src/dxr3/dxr3_decode_video.c index 9cdd1644e..d0f99a844 100644 --- a/src/dxr3/dxr3_decode_video.c +++ b/src/dxr3/dxr3_decode_video.c @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * - * $Id: dxr3_decode_video.c,v 1.5 2002/06/01 16:34:47 mroi Exp $ + * $Id: dxr3_decode_video.c,v 1.6 2002/06/03 20:29:16 mroi Exp $ */ /* dxr3 video decoder plugin. @@ -50,6 +50,9 @@ /* we adjust vpts_offset in metronom, when skip_count reaches this value */ #define SKIP_TOLERANCE 200 +/* the number of frames to pass before we stop duration correction */ +#define FORCE_DURATION_WINDOW_SIZE 100 + /* offset for mpeg header parsing */ #define HEADER_OFFSET 0 @@ -91,21 +94,25 @@ typedef struct dxr3_decoder_s { int enhanced_mode; int resync_window; int skip_count; /* syncing parameters */ -#if LOG_PTS - int64_t last_pts; -#endif + + int correct_durations; + int64_t last_vpts; + int force_duration_window; + int avg_duration; /* logic to correct broken frame rates */ + } dxr3_decoder_t; /* helper functions */ static int dxr3_present(xine_t *xine); static int dxr3_mvcommand(int fd_control, int command); static void parse_mpeg_header(dxr3_decoder_t *this, uint8_t *buffer); -static int get_duration(int framecode, int repeat_first_field); +static int get_duration(dxr3_decoder_t *this); /* config callbacks */ static void dxr3_update_priority(void *this_gen, cfg_entry_t *entry); static void dxr3_update_sync_mode(void *this_gen, cfg_entry_t *entry); static void dxr3_update_enhanced_mode(void *this_gen, cfg_entry_t *entry); +static void dxr3_update_correct_durations(void *this_gen, cfg_entry_t *entry); video_decoder_t *init_video_decoder_plugin(int iface_version, xine_t *xine) @@ -114,6 +121,7 @@ video_decoder_t *init_video_decoder_plugin(int iface_version, xine_t *xine) config_values_t *cfg; const char *confstr; int dashpos; + int64_t cur_offset; if (iface_version != 9) { printf( "dxr3_decode_video: plugin doesn't support plugin API version %d.\n" @@ -172,6 +180,14 @@ video_decoder_t *init_video_decoder_plugin(int iface_version, xine_t *xine) "dxr3.alt_play_mode", 1, "Use alternate Play mode", "Enabling this option will utilise a smoother play mode.", dxr3_update_enhanced_mode, this); + this->correct_durations = cfg->register_bool(cfg, + "dxr3.correct_durations", 0, "Correct frame durations in broken streams", + "Enable this for streams with wrong frame durations.", + dxr3_update_correct_durations, this); + + /* set a/v offset to compensate dxr3 internal delay */ + cur_offset = this->xine->metronom->get_option(this->xine->metronom, METRONOM_AV_OFFSET); + this->xine->metronom->set_option(this->xine->metronom, METRONOM_AV_OFFSET, cur_offset - 21600); return &this->video_decoder; } @@ -191,9 +207,7 @@ static int dxr3_can_handle(video_decoder_t *this_gen, int buf_type) static void dxr3_init(video_decoder_t *this_gen, vo_instance_t *video_out) { dxr3_decoder_t *this = (dxr3_decoder_t *)this_gen; - metronom_t *metronom = this->xine->metronom; char tmpstr[128]; - int64_t cur_offset; snprintf(tmpstr, sizeof(tmpstr), "%s%s", this->devname, this->devnum); #if LOG_VID @@ -212,17 +226,13 @@ static void dxr3_init(video_decoder_t *this_gen, vo_instance_t *video_out) video_out->open(video_out); this->video_out = video_out; - this->resync_window = 0; - this->skip_count = 0; + this->resync_window = 0; + this->skip_count = 0; - cur_offset = metronom->get_option(metronom, METRONOM_AV_OFFSET); - metronom->set_option(metronom, METRONOM_AV_OFFSET, cur_offset - 21600); - /* a note on the 21600: this seems to be a necessary offset, - maybe the dxr3 needs some internal time, however it improves sync a lot */ - -#if LOG_PTS - this->last_vpts = metronom->get_current_time(metronom); -#endif + this->force_duration_window = -FORCE_DURATION_WINDOW_SIZE; + this->last_vpts = + this->xine->metronom->get_current_time(this->xine->metronom); + this->avg_duration = 0; } static void dxr3_decode_data(video_decoder_t *this_gen, buf_element_t *buf) @@ -230,7 +240,7 @@ static void dxr3_decode_data(video_decoder_t *this_gen, buf_element_t *buf) dxr3_decoder_t *this = (dxr3_decoder_t *)this_gen; ssize_t written; int64_t vpts; - int i, duration, skip; + int i, skip; vo_frame_t *img; uint8_t *buffer, byte; uint32_t shift; @@ -280,19 +290,27 @@ static void dxr3_decode_data(video_decoder_t *this_gen, buf_element_t *buf) if (buf->decoder_flags & BUF_FLAG_PREVIEW) continue; - duration = get_duration(this->frame_rate_code, this->repeat_first_field); /* pretend like we have decoded a frame */ img = this->video_out->get_frame (this->video_out, this->width, this->height, this->aspect, IMGFMT_MPEG, VO_BOTH_FIELDS); img->pts = buf->pts; img->bad_frame = 0; - img->duration = duration; + img->duration = get_duration(this); skip = img->draw(img); + if (skip <= 0) { /* don't skip */ vpts = img->vpts; /* copy so we can free img */ + if (this->correct_durations) { + /* calculate an average frame duration from metronom's vpts values */ + this->avg_duration = this->avg_duration * 0.9 + (vpts - this->last_vpts) * 0.1; +#if LOG_PTS + printf("dxr3_decode_video: average frame duration %d\n", this->avg_duration); +#endif + } + if (this->skip_count) this->skip_count--; if (this->resync_window == 0 && this->scr && this->enhanced_mode && @@ -306,13 +324,14 @@ static void dxr3_decode_data(video_decoder_t *this_gen, buf_element_t *buf) } if (this->resync_window != 0 && this->resync_window > -RESYNC_WINDOW_SIZE) this->resync_window--; - } - else { /* metronom says skip, so don't set vpts */ + } else { /* metronom says skip, so don't set vpts */ #if LOG_VID printf("dxr3_decode_video: %d frames to skip\n", skip); #endif vpts = 0; + this->avg_duration = 0; + /* handle frame skip conditions */ if (this->scr && !this->scr->scanning) this->skip_count += skip; if (this->skip_count > SKIP_TOLERANCE) { /* we have had enough skipping messages now, let's react */ @@ -337,10 +356,9 @@ static void dxr3_decode_data(video_decoder_t *this_gen, buf_element_t *buf) if (this->resync_window != 0 && this->resync_window < RESYNC_WINDOW_SIZE) this->resync_window++; } + this->last_vpts = img->vpts; img->free(img); -#if LOG_PTS - this->last_vpts += img->duration; /* predict vpts */ -#endif + /* if sync_every_frame was disabled, decrease the counter * for a retry * (it might be due to crappy studio logos and stuff @@ -405,9 +423,7 @@ static void dxr3_decode_data(video_decoder_t *this_gen, buf_element_t *buf) delay = vpts - this->xine->metronom->get_current_time( this->xine->metronom); #if LOG_PTS - printf("dxr3_decode_video: SETPTS got %lld expected = %lld (delta %lld) delay = %lld\n", - vpts, this->last_vpts, vpts - this->last_vpts, delay); - this->last_vpts = vpts; + printf("dxr3_decode_video: SETPTS got %lld\n", vpts); #endif /* SETPTS only if less then one second in the future and * either buffer has pts or sync_every_frame is set */ @@ -427,8 +443,7 @@ static void dxr3_decode_data(video_decoder_t *this_gen, buf_element_t *buf) } #if LOG_PTS else if (buf->pts) { - printf("dxr3_decode_video: skip buf->pts = %lld (no vpts) last_vpts = %lld\n", - buf->pts, this->last_vpts); + printf("dxr3_decode_video: skip buf->pts = %lld (no vpts)\n", buf->pts); } #endif @@ -466,7 +481,6 @@ static void dxr3_close(video_decoder_t *this_gen) { dxr3_decoder_t *this = (dxr3_decoder_t *)this_gen; metronom_t *metronom = this->xine->metronom; - int64_t cur_offset; if (this->scr) { metronom->unregister_scr(metronom, &this->scr->scr_plugin); @@ -474,10 +488,6 @@ static void dxr3_close(video_decoder_t *this_gen) this->scr = NULL; } - /* revert av_offset change done in dxr3_init */ - cur_offset = metronom->get_option(metronom, METRONOM_AV_OFFSET); - metronom->set_option(metronom, METRONOM_AV_OFFSET, cur_offset + 21600); - if (this->fd_video >= 0) close(this->fd_video); this->fd_video = -1; @@ -531,11 +541,11 @@ static void parse_mpeg_header(dxr3_decoder_t *this, uint8_t * buffer) this->have_header_info = 1; } -static int get_duration(int framecode, int repeat_first_field) +static int get_duration(dxr3_decoder_t *this) { int duration; - switch (framecode) { + switch (this->frame_rate_code) { case 1: /* 23.976 */ duration = 3913; break; @@ -543,10 +553,10 @@ static int get_duration(int framecode, int repeat_first_field) duration = 3750; break; case 3: /* 25.000 */ - duration = repeat_first_field ? 5400 : 3600; + duration = this->repeat_first_field ? 5400 : 3600; break; case 4: /* 29.970 */ - duration = repeat_first_field ? 4505 : 3003; + duration = this->repeat_first_field ? 4505 : 3003; break; case 5: /* 30.000 */ duration = 3000; @@ -561,11 +571,53 @@ static int get_duration(int framecode, int repeat_first_field) duration = 1509; break; default: - printf("dxr3_decode_video: warning: unknown frame rate code %d: using PAL\n", framecode); + printf("dxr3_decode_video: WARNING: unknown frame rate code %d: using PAL\n", + this->frame_rate_code); duration = 3600; /* PAL 25fps */ break; } + if (this->correct_durations) { + /* we set an initial average frame duration here */ + if (!this->avg_duration) this->avg_duration = duration; + + /* Apply a correction to the framerate-code if metronom + * insists on a different frame duration. + * The code below is for NTCS streams labeled as PAL streams. + * (I have seen such things even on dvds!) + */ + if (this->avg_duration && this->avg_duration < 3300 && duration == 3600) { + if (this->force_duration_window > 0) { + // we are already in a force_duration window, so we force duration + this->force_duration_window = FORCE_DURATION_WINDOW_SIZE; + return 3000; + } + if (this->force_duration_window <= 0 && (this->force_duration_window += 10) > 0) { + // we just entered a force_duration window, so we start the correction + metronom_t *metronom = this->xine->metronom; + int64_t cur_offset; + printf("dxr3_decode_video: WARNING: correcting frame rate code from PAL to NTSC\n"); + /* those weird streams need an offset, too */ + cur_offset = metronom->get_option(metronom, METRONOM_AV_OFFSET); + metronom->set_option(metronom, METRONOM_AV_OFFSET, cur_offset - 28800); + this->force_duration_window = FORCE_DURATION_WINDOW_SIZE; + return 3000; + } + } + + if (this->force_duration_window == -FORCE_DURATION_WINDOW_SIZE) + // we are far from a force_duration window + return duration; + if (--this->force_duration_window == 0) { + // we have just left a force_duration window + metronom_t *metronom = this->xine->metronom; + int64_t cur_offset; + cur_offset = metronom->get_option(metronom, METRONOM_AV_OFFSET); + metronom->set_option(metronom, METRONOM_AV_OFFSET, cur_offset + 28800); + this->force_duration_window = -FORCE_DURATION_WINDOW_SIZE; + } + } + return duration; } @@ -589,3 +641,10 @@ static void dxr3_update_enhanced_mode(void *this_gen, cfg_entry_t *entry) printf("dxr3_decode_video: setting enhanced mode to %s\n", (entry->num_value ? "on" : "off")); } + +static void dxr3_update_correct_durations(void *this_gen, cfg_entry_t *entry) +{ + ((dxr3_decoder_t *)this_gen)->correct_durations = entry->num_value; + printf("dxr3_decode_video: setting correct_durations mode to %s\n", + (entry->num_value ? "on" : "off")); +} |