mirror of
https://github.com/Rockbox/rockbox.git
synced 2025-10-14 02:27:39 -04:00
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
This commit is contained in:
parent
0b11d983e7
commit
01c0fb492f
1 changed files with 118 additions and 111 deletions
|
@ -1256,20 +1256,15 @@ static uint32_t video_stack[VIDEO_STACKSIZE / sizeof(uint32_t)] IBSS_ATTR;
|
||||||
|
|
||||||
static void video_thread(void)
|
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;
|
const mpeg2_info_t * info;
|
||||||
mpeg2_state_t state;
|
mpeg2_state_t state;
|
||||||
char str[80];
|
char str[80];
|
||||||
int dither_index = 0;
|
|
||||||
uint32_t curr_time = 0;
|
uint32_t curr_time = 0;
|
||||||
uint32_t period = 0; /* Frame period in clock ticks */
|
uint32_t period = 0; /* Frame period in clock ticks */
|
||||||
uint32_t eta_audio = UINT_MAX, eta_video = 0;
|
uint32_t eta_audio = UINT_MAX, eta_video = 0;
|
||||||
int32_t eta_early = 0, eta_late = 0;
|
int32_t eta_early = 0, eta_late = 0;
|
||||||
int frame_drop_level = 0;
|
int frame_drop_level = 0;
|
||||||
int drop_frame = 0;
|
int skip_level = 0;
|
||||||
int num_skipped = 0;
|
int num_skipped = 0;
|
||||||
int num_drawn = 0;
|
int num_drawn = 0;
|
||||||
/* Used to decide when to display FPS */
|
/* Used to decide when to display FPS */
|
||||||
|
@ -1293,6 +1288,10 @@ static void video_thread(void)
|
||||||
|
|
||||||
/* Request the first packet data */
|
/* Request the first packet data */
|
||||||
get_next_data( &video_str );
|
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);
|
mpeg2_buffer (mpeg2dec, video_str.curr_packet, video_str.curr_packet_end);
|
||||||
total_offset += video_str.curr_packet_end - video_str.curr_packet;
|
total_offset += video_str.curr_packet_end - video_str.curr_packet;
|
||||||
|
|
||||||
|
@ -1346,13 +1345,67 @@ static void video_thread(void)
|
||||||
|
|
||||||
case STATE_PICTURE:
|
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 */
|
int32_t offset; /* Tick adjustment to keep sync */
|
||||||
|
|
||||||
/* No limiting => no dropping so simply make sure skipping is off
|
/* draw current picture */
|
||||||
in the decoder and the frame will be drawn */
|
if (!info->display_fbuf)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* No limiting => no dropping - draw this frame */
|
||||||
if (!settings.limitfps)
|
if (!settings.limitfps)
|
||||||
goto picture_done;
|
goto picture_draw;
|
||||||
|
|
||||||
/* Get presentation times in audio samples - quite accurate
|
/* Get presentation times in audio samples - quite accurate
|
||||||
enough - add previous frame duration if not stamped */
|
enough - add previous frame duration if not stamped */
|
||||||
|
@ -1383,7 +1436,7 @@ static void video_thread(void)
|
||||||
offset = eta_video;
|
offset = eta_video;
|
||||||
|
|
||||||
eta_video -= offset;
|
eta_video -= offset;
|
||||||
goto picture_done;
|
goto picture_draw;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Possibly skip this frame **/
|
/** Possibly skip this frame **/
|
||||||
|
@ -1444,115 +1497,67 @@ static void video_thread(void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If we are more than 167ms behind schedule and it's
|
if (info->display_picture->flags & PIC_FLAG_SKIP)
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
/* Frame type: I/P/B/D */
|
/* This frame was set to skip so skip it after having updated
|
||||||
int type = info->current_picture->flags & PIC_MASK_CODING_TYPE;
|
timing information */
|
||||||
|
num_skipped++;
|
||||||
/* Things are running a bit late or all frames are being
|
eta_early = INT32_MIN;
|
||||||
dropped until a key frame */
|
goto picture_skip;
|
||||||
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 */
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
picture_done:
|
if (skip_level == 3 && TIME_BEFORE(*rb->current_tick, last_render + HZ/2))
|
||||||
mpeg2_skip(mpeg2dec, drop_frame >> 1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
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
|
/* Render drop was set previously but nothing was dropped in the
|
||||||
or wait for it if it's too early to draw it. */
|
decoder or it's been to long since drawing the last frame. */
|
||||||
if (drop_frame != 0)
|
skip_level = 0;
|
||||||
{
|
num_skipped++;
|
||||||
drop_frame = 0;
|
eta_early = INT32_MIN;
|
||||||
break;
|
goto picture_skip;
|
||||||
}
|
|
||||||
|
|
||||||
/* Wait until audio catches up */
|
|
||||||
while (eta_video > eta_audio)
|
|
||||||
{
|
|
||||||
rb->priority_yield();
|
|
||||||
|
|
||||||
/* Make sure not to get stuck waiting here forever */
|
|
||||||
if (videostatus != STREAM_PLAYING)
|
|
||||||
goto rendering_finished;
|
|
||||||
|
|
||||||
eta_audio = get_stream_time();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Record last frame time */
|
|
||||||
last_render = *rb->current_tick;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* At this point a frame _will_ be drawn - a skip may happen on
|
||||||
|
the next however */
|
||||||
|
skip_level = 0;
|
||||||
|
|
||||||
|
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 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Wait until audio catches up */
|
||||||
|
while (eta_video > eta_audio)
|
||||||
|
{
|
||||||
|
rb->priority_yield();
|
||||||
|
|
||||||
|
/* Make sure not to get stuck waiting here forever */
|
||||||
|
if (videostatus != STREAM_PLAYING)
|
||||||
|
goto rendering_finished;
|
||||||
|
|
||||||
|
eta_audio = get_stream_time();
|
||||||
|
}
|
||||||
|
|
||||||
|
picture_draw:
|
||||||
|
/* Record last frame time */
|
||||||
|
last_render = *rb->current_tick;
|
||||||
|
|
||||||
vo_draw_frame(info->display_fbuf->buf);
|
vo_draw_frame(info->display_fbuf->buf);
|
||||||
num_drawn++;
|
num_drawn++;
|
||||||
|
|
||||||
|
picture_skip:
|
||||||
if (!settings.showfps)
|
if (!settings.showfps)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -1574,6 +1579,8 @@ static void video_thread(void)
|
||||||
last_showfps = *rb->current_tick;
|
last_showfps = *rb->current_tick;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue