From 01c0fb492fba2aaa2f57b1d80a279b4655ec96cb Mon Sep 17 00:00:00 2001 From: Michael Sevakis Date: Wed, 18 Apr 2007 23:23:18 +0000 Subject: mpegplayer: Better frame dropping code adapted to the using correct timestamps change. Even smoother frames. Higher FPS and smoother when having to drop than before (Around 15fps regulated for Elephants Dream on x5 which is a dismal situation, up from around 12 or so). git-svn-id: svn://svn.rockbox.org/rockbox/trunk@13205 a1c6a512-1295-4272-9138-f99709370657 --- apps/plugins/mpegplayer/mpegplayer.c | 219 ++++++++++++++++++----------------- 1 file changed, 113 insertions(+), 106 deletions(-) (limited to 'apps/plugins') diff --git a/apps/plugins/mpegplayer/mpegplayer.c b/apps/plugins/mpegplayer/mpegplayer.c index 52d33673f4..6bd5abfa45 100644 --- a/apps/plugins/mpegplayer/mpegplayer.c +++ b/apps/plugins/mpegplayer/mpegplayer.c @@ -1256,20 +1256,15 @@ static uint32_t video_stack[VIDEO_STACKSIZE / sizeof(uint32_t)] IBSS_ATTR; static void video_thread(void) { - /* Time dither to maximally spread frame drops in time to best - approximate the appearance of a lower frame rate */ - static const uint32_t time_dither[8] = - { 1, 4, 7, 2, 5, 8, 3, 6 }; const mpeg2_info_t * info; mpeg2_state_t state; char str[80]; - int dither_index = 0; uint32_t curr_time = 0; uint32_t period = 0; /* Frame period in clock ticks */ uint32_t eta_audio = UINT_MAX, eta_video = 0; int32_t eta_early = 0, eta_late = 0; int frame_drop_level = 0; - int drop_frame = 0; + int skip_level = 0; int num_skipped = 0; int num_drawn = 0; /* Used to decide when to display FPS */ @@ -1293,6 +1288,10 @@ static void video_thread(void) /* Request the first packet data */ get_next_data( &video_str ); + + if (video_str.curr_packet == NULL) + goto done; + mpeg2_buffer (mpeg2dec, video_str.curr_packet, video_str.curr_packet_end); total_offset += video_str.curr_packet_end - video_str.curr_packet; @@ -1346,13 +1345,67 @@ static void video_thread(void) case STATE_PICTURE: { - /* A new picture is available - see if we should draw it */ + int skip = 0; /* Assume no skip */ + + if (frame_drop_level >= 1 || skip_level > 0) + { + /* A frame will be dropped in the decoder */ + + /* Frame type: I/P/B/D */ + int type = info->current_picture->flags & PIC_MASK_CODING_TYPE; + + switch (type) + { + case PIC_FLAG_CODING_TYPE_I: + case PIC_FLAG_CODING_TYPE_D: + /* Level 5: Things are extremely late and all frames will be + dropped until the next key frame */ + if (frame_drop_level >= 1) + frame_drop_level = 0; /* Key frame - reset drop level */ + if (skip_level >= 5) + { + frame_drop_level = 1; + skip_level = 0; /* reset */ + } + break; + case PIC_FLAG_CODING_TYPE_P: + /* Level 4: Things are very late and all frames will be + dropped until the next key frame */ + if (skip_level >= 4) + { + frame_drop_level = 1; + skip_level = 0; /* reset */ + } + break; + case PIC_FLAG_CODING_TYPE_B: + /* We want to drop something, so this B frame won't even + be decoded. Drawing can happen on the next frame if so + desired. Bring the level down as skips are done. */ + skip = 1; + if (skip_level > 0) + skip_level--; + } + + skip |= frame_drop_level; + } + + mpeg2_skip(mpeg2dec, skip); + break; + } + + case STATE_SLICE: + case STATE_END: + case STATE_INVALID_END: + { int32_t offset; /* Tick adjustment to keep sync */ - /* No limiting => no dropping so simply make sure skipping is off - in the decoder and the frame will be drawn */ + /* draw current picture */ + if (!info->display_fbuf) + break; + + /* No limiting => no dropping - draw this frame */ if (!settings.limitfps) - goto picture_done; + goto picture_draw; /* Get presentation times in audio samples - quite accurate enough - add previous frame duration if not stamped */ @@ -1383,7 +1436,7 @@ static void video_thread(void) offset = eta_video; eta_video -= offset; - goto picture_done; + goto picture_draw; } /** Possibly skip this frame **/ @@ -1444,115 +1497,67 @@ static void video_thread(void) } } - /* If we are more than 167ms behind schedule and it's - been less than 1/2 second since the last frame, skip frame. */ - dither_index = (dither_index + 1) & 7; - offset += time_dither[dither_index]*period >> 3; - - if (frame_drop_level > 1 || offset > CLOCK_RATE*167/1000) + if (info->display_picture->flags & PIC_FLAG_SKIP) { - /* Frame type: I/P/B/D */ - int type = info->current_picture->flags & PIC_MASK_CODING_TYPE; - - /* Things are running a bit late or all frames are being - dropped until a key frame */ - if (frame_drop_level > 1) - { - switch (type) - { - case PIC_FLAG_CODING_TYPE_I: - case PIC_FLAG_CODING_TYPE_D: - frame_drop_level = 0; /* This frame can be drawn */ - } - } - - if (frame_drop_level <= 1 && offset > CLOCK_RATE*250/1000) - { - /* Things are very, very late. Resort to stronger measures - to keep sync by dropping a I/D/P frame. Drawing cannot - take place again until the next key frame. */ - switch (type) - { - case PIC_FLAG_CODING_TYPE_I: - case PIC_FLAG_CODING_TYPE_P: - case PIC_FLAG_CODING_TYPE_D: - frame_drop_level = 2; - } - } - - drop_frame = 1 << frame_drop_level; - - if (*rb->current_tick - last_render >= HZ/2) - { - /* Timeout has expired and this frame will be drawn if it - is available. This may reverse the decision to drop a - key frame above. */ - switch (type) - { - default: - if (frame_drop_level <= 1) - { - case PIC_FLAG_CODING_TYPE_I: - case PIC_FLAG_CODING_TYPE_D: - drop_frame = 0; - } - } - } - else if (type == PIC_FLAG_CODING_TYPE_B) - { - /* We want to drop something, so this B frame won't even be - decoded. Drawing can happen on the next frame if so - desired. Drop level is not affected. */ - drop_frame |= 1 << 1; - } - - if (drop_frame != 0) - { - num_skipped++; - eta_early = INT32_MIN; /* Indicate a dropped frame */ - } + /* This frame was set to skip so skip it after having updated + timing information */ + num_skipped++; + eta_early = INT32_MIN; + goto picture_skip; } - picture_done: - mpeg2_skip(mpeg2dec, drop_frame >> 1); - break; + if (skip_level == 3 && TIME_BEFORE(*rb->current_tick, last_render + HZ/2)) + { + /* Render drop was set previously but nothing was dropped in the + decoder or it's been to long since drawing the last frame. */ + skip_level = 0; + num_skipped++; + eta_early = INT32_MIN; + goto picture_skip; } - case STATE_SLICE: - case STATE_END: - case STATE_INVALID_END: - /* draw current picture */ - if (!info->display_fbuf) - break; - if (settings.limitfps) - { - /* Framerate regulation is turned on - drop it if STATE_PICTURE said - or wait for it if it's too early to draw it. */ - if (drop_frame != 0) - { - drop_frame = 0; - break; - } + /* At this point a frame _will_ be drawn - a skip may happen on + the next however */ + skip_level = 0; - /* Wait until audio catches up */ - while (eta_video > eta_audio) - { - rb->priority_yield(); + if (offset > CLOCK_RATE*110/1000) + { + /* Decide which skip level is needed in order to catch up */ + + /* TODO: Calculate this rather than if...else - this is rather + exponential though */ + if (offset > CLOCK_RATE*367/1000) + skip_level = 5; /* Decoder skip: I/D */ + if (offset > CLOCK_RATE*233/1000) + skip_level = 4; /* Decoder skip: P */ + else if (offset > CLOCK_RATE*167/1000) + skip_level = 3; /* Render skip */ + else if (offset > CLOCK_RATE*133/1000) + skip_level = 2; /* Decoder skip: B */ + else + skip_level = 1; /* Decoder skip: B */ + } - /* Make sure not to get stuck waiting here forever */ - if (videostatus != STREAM_PLAYING) - goto rendering_finished; + /* Wait until audio catches up */ + while (eta_video > eta_audio) + { + rb->priority_yield(); - eta_audio = get_stream_time(); - } + /* Make sure not to get stuck waiting here forever */ + if (videostatus != STREAM_PLAYING) + goto rendering_finished; - /* Record last frame time */ - last_render = *rb->current_tick; + eta_audio = get_stream_time(); } + picture_draw: + /* Record last frame time */ + last_render = *rb->current_tick; + vo_draw_frame(info->display_fbuf->buf); num_drawn++; + picture_skip: if (!settings.showfps) break; @@ -1574,6 +1579,8 @@ static void video_thread(void) last_showfps = *rb->current_tick; } break; + } + default: break; } -- cgit v1.2.3