1
0
Fork 0
forked from len0rd/rockbox

mpegplayer: Better sync, smoother frames - corrected a few minor flaws. Keep timestamps 32-bit in stead of 33 - a 45kHz clock is good enough for humans. Increase pts queue size since the mpeg buffer is now filled when buffering the audio stream. Cleanup frame drop code a little too.

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@13175 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
Michael Sevakis 2007-04-16 03:34:56 +00:00
parent 7a5d4011f5
commit eb4dcd80b7

View file

@ -108,10 +108,6 @@ FPS | 27Mhz | 100Hz | 44.1KHz | 48KHz
#include "mpeg2.h" #include "mpeg2.h"
#include "mpeg_settings.h" #include "mpeg_settings.h"
#include "video_out.h" #include "video_out.h"
#ifndef ATTR_ALIGN
#define ATTR_ALIGN(a) __attribute__((aligned (a)))
#endif
#include "mpeg2_internal.h"
#include "../../codecs/libmad/mad.h" #include "../../codecs/libmad/mad.h"
PLUGIN_HEADER PLUGIN_HEADER
@ -493,12 +489,13 @@ static void init_mad(void* mad_frame_overlap)
#define SYSTEM_HEADER_START_CODE 0x000001bbul #define SYSTEM_HEADER_START_CODE 0x000001bbul
/* p = base pointer, b0 - b4 = byte offsets from p */ /* p = base pointer, b0 - b4 = byte offsets from p */
/* We only care about the MS 32 bits of the 33 and so the ticks are 45kHz */
#define TS_FROM_HEADER(p, b0, b1, b2, b3, b4) \ #define TS_FROM_HEADER(p, b0, b1, b2, b3, b4) \
((uint32_t)(((p)[b0] >> 1 << 30) | \ ((uint32_t)(((p)[b0] >> 1 << 29) | \
((p)[b1] << 22) | \ ((p)[b1] << 21) | \
((p)[b2] >> 1 << 15) | \ ((p)[b2] >> 1 << 14) | \
((p)[b3] << 7) | \ ((p)[b3] << 6) | \
((p)[b4] >> 1 ))) ((p)[b4] >> 2 )))
/* This function demuxes the streams and gives the next stream data pointer */ /* This function demuxes the streams and gives the next stream data pointer */
static void get_next_data( Stream* str ) static void get_next_data( Stream* str )
@ -612,10 +609,12 @@ static void get_next_data( Stream* str )
/* header points to the mpeg2 pes header */ /* header points to the mpeg2 pes header */
if (header[7] & 0x80) if (header[7] & 0x80)
{ {
/* header has a pts */
uint32_t pts = TS_FROM_HEADER(header, 9, 10, 11, 12, 13); uint32_t pts = TS_FROM_HEADER(header, 9, 10, 11, 12, 13);
if (stream >= 0xe0) if (stream >= 0xe0)
{ {
/* video stream - header may have a dts as well */
uint32_t dts = (header[7] & 0x40) == 0 ? uint32_t dts = (header[7] & 0x40) == 0 ?
pts : TS_FROM_HEADER(header, 14, 15, 16, 17, 18); pts : TS_FROM_HEADER(header, 14, 15, 16, 17, 18);
@ -659,10 +658,12 @@ static void get_next_data( Stream* str )
if ((ptsbuf[-1] & 0xe0) == 0x20) if ((ptsbuf[-1] & 0xe0) == 0x20)
{ {
/* header has a pts */
uint32_t pts = TS_FROM_HEADER(ptsbuf, -1, 0, 1, 2, 3); uint32_t pts = TS_FROM_HEADER(ptsbuf, -1, 0, 1, 2, 3);
if (stream >= 0xe0) if (stream >= 0xe0)
{ {
/* video stream - header may have a dts as well */
uint32_t dts = (ptsbuf[-1] & 0xf0) != 0x30 ? uint32_t dts = (ptsbuf[-1] & 0xf0) != 0x30 ?
pts : TS_FROM_HEADER(ptsbuf, 4, 5, 6, 7, 18); pts : TS_FROM_HEADER(ptsbuf, 4, 5, 6, 7, 18);
@ -723,8 +724,8 @@ static void get_next_data( Stream* str )
/* For simple lowpass filtering of sync variables */ /* For simple lowpass filtering of sync variables */
#define AVERAGE(var, x, count) (((var) * (count-1) + (x)) / (count)) #define AVERAGE(var, x, count) (((var) * (count-1) + (x)) / (count))
/* Convert 90kHz PTS/DTS ticks to our clock ticks */ /* Convert 45kHz PTS/DTS ticks to our clock ticks */
#define TS_TO_TICKS(pts) ((uint64_t)CLOCK_RATE*(pts) / 90000) #define TS_TO_TICKS(pts) ((uint64_t)CLOCK_RATE*(pts) / 45000)
/* Convert 27MHz ticks to our clock ticks */ /* Convert 27MHz ticks to our clock ticks */
#define TIME_TO_TICKS(stamp) ((uint64_t)CLOCK_RATE*(stamp) / 27000000) #define TIME_TO_TICKS(stamp) ((uint64_t)CLOCK_RATE*(stamp) / 27000000)
@ -737,14 +738,14 @@ static bool init_mpabuf(void)
return mpa_buffer != NULL; return mpa_buffer != NULL;
} }
#define PTS_QUEUE_LEN (1 << 4) /* 16 should be way more than sufficient - #define PTS_QUEUE_LEN (1 << 5) /* 32 should be way more than sufficient -
if not, the case is handled */ if not, the case is handled */
#define PTS_QUEUE_MASK (PTS_QUEUE_LEN-1) #define PTS_QUEUE_MASK (PTS_QUEUE_LEN-1)
struct pts_queue_slot struct pts_queue_slot
{ {
uint32_t pts; /* Time stamp for packet */ uint32_t pts; /* Time stamp for packet */
ssize_t size; /* Number of bytes left in packet */ ssize_t size; /* Number of bytes left in packet */
} pts_queue[PTS_QUEUE_MASK+1]; } pts_queue[PTS_QUEUE_LEN];
/* This starts out wr == rd but will never be emptied to zero during /* This starts out wr == rd but will never be emptied to zero during
streaming again in order to support initializing the first packet's streaming again in order to support initializing the first packet's
@ -752,12 +753,6 @@ struct pts_queue_slot
static unsigned pts_queue_rd; static unsigned pts_queue_rd;
static unsigned pts_queue_wr; static unsigned pts_queue_wr;
/* Resets the pts queue - call when starting and seeking */
static void pts_queue_reset(void)
{
pts_queue_rd = pts_queue_wr;
}
/* Increments the queue head postion - should be used to preincrement */ /* Increments the queue head postion - should be used to preincrement */
static bool pts_queue_add_head(void) static bool pts_queue_add_head(void)
{ {
@ -790,6 +785,16 @@ static struct pts_queue_slot * pts_queue_tail(void)
return &pts_queue[pts_queue_rd & PTS_QUEUE_MASK]; return &pts_queue[pts_queue_rd & PTS_QUEUE_MASK];
} }
/* Resets the pts queue - call when starting and seeking */
static void pts_queue_reset(void)
{
struct pts_queue_slot *pts;
pts_queue_rd = pts_queue_wr;
pts = pts_queue_tail();
pts->pts = 0;
pts->size = 0;
}
struct pcm_frame_header /* Header added to pcm data every time a decoded struct pcm_frame_header /* Header added to pcm data every time a decoded
mpa frame is sent out */ mpa frame is sent out */
{ {
@ -992,6 +997,21 @@ static void audio_thread(void)
if (audiostatus == PLEASE_STOP) if (audiostatus == PLEASE_STOP)
goto done; goto done;
if (pts->size <= 0)
{
/* Carry any overshoot to the next size since we're technically
-pts->size bytes into it already. If size is negative an audio
frame was split accross packets. Old has to be saved before
moving the tail. */
if (pts_queue_remove_tail())
{
struct pts_queue_slot *old = pts;
pts = pts_queue_tail();
pts->size += old->size;
old->size = 0;
}
}
/** Buffering **/ /** Buffering **/
if (mpabuf_used >= MPA_MAX_FRAME_SIZE + MAD_BUFFER_GUARD) if (mpabuf_used >= MPA_MAX_FRAME_SIZE + MAD_BUFFER_GUARD)
{ {
@ -999,46 +1019,53 @@ static void audio_thread(void)
} }
else if (audio_str.curr_packet != NULL) else if (audio_str.curr_packet != NULL)
{ {
/* Get data from next audio packet */ do
len = audio_str.curr_packet_end - audio_str.curr_packet;
if (audio_str.tagged)
{ {
struct pts_queue_slot *stamp = pts; /* Get data from next audio packet */
len = audio_str.curr_packet_end - audio_str.curr_packet;
if (pts_queue_add_head()) if (audio_str.tagged)
{ {
stamp = pts_queue_head(); struct pts_queue_slot *stamp = pts;
stamp->pts = TS_TO_TICKS(audio_str.curr_pts);
/* pts->size should have been zeroed when slot was freed */ if (pts_queue_add_head())
{
stamp = pts_queue_head();
stamp->pts = TS_TO_TICKS(audio_str.curr_pts);
/* pts->size should have been zeroed when slot was
freed */
}
/* else queue full - just count up from the last to make
it look like more data in the same packet */
stamp->size += len;
audio_str.tagged = 0;
} }
/* else queue full - just count up from the last to make it look else
like more data in the same packet */ {
stamp->size += len; /* Add to the one just behind the head - this may be the
audio_str.tagged = 0; tail or the previouly added head - whether or not we'll
ever reach this is quite in question since audio always
seems to have every packet timestamped */
pts_queue_head()->size += len;
}
/* Slide any remainder over to beginning - avoid function
call overhead if no data remaining as well */
if (mpabuf > mpa_buffer && mpabuf_used > 0)
rb->memmove(mpa_buffer, mpabuf, mpabuf_used);
/* Splice this packet onto any remainder */
rb->memcpy(mpa_buffer + mpabuf_used, audio_str.curr_packet,
len);
mpabuf_used += len;
mpabuf = mpa_buffer;
/* Get data from next audio packet */
get_next_data(&audio_str);
} }
else while (audio_str.curr_packet != NULL &&
{ mpabuf_used < MPA_MAX_FRAME_SIZE + MAD_BUFFER_GUARD);
/* Add to the one just behind the head - this may be the tail or
the previouly added head - whether or not we'll ever reach this
is quite in question since audio always seems to have every
packet timestamped */
pts_queue_head()->size += len;
}
/* Slide any remainder over to beginning - avoid function call overhead if
no data remaining as well */
if (mpabuf > mpa_buffer && mpabuf_used > 0)
rb->memmove(mpa_buffer, mpabuf, mpabuf_used);
/* Splice this packet onto any remainder */
rb->memcpy(mpa_buffer + mpabuf_used, audio_str.curr_packet, len);
mpabuf_used += len;
mpabuf = mpa_buffer;
/* Move stream position to the next packet */
get_next_data(&audio_str);
} }
else if (mpabuf_used <= 0) else if (mpabuf_used <= 0)
{ {
@ -1051,6 +1078,13 @@ static void audio_thread(void)
mad_stat = mad_frame_decode(&frame, &stream); mad_stat = mad_frame_decode(&frame, &stream);
if (stream.next_frame == NULL)
{
/* What to do here? (This really is fatal) */
DEBUGF("/* What to do here? */\n");
break;
}
/* Next mad stream buffer is the next frame postion */ /* Next mad stream buffer is the next frame postion */
mpabuf = (uint8_t *)stream.next_frame; mpabuf = (uint8_t *)stream.next_frame;
@ -1059,25 +1093,6 @@ static void audio_thread(void)
mpabuf_used -= len; mpabuf_used -= len;
pts->size -= len; pts->size -= len;
if (pts->size <= 0)
{
/* Carry any overshoot to the next size since we're technically
-pts->size bytes into it already. If size is negative an audio
frame was split accross packets. Old has to be saved before
moving the tail. */
struct pts_queue_slot *old = pts;
if (pts_queue_remove_tail())
{
pts = pts_queue_tail();
pts->size += old->size;
}
/* Zero the size of the last slot to simplify stamping code and ensure
that it is never negative if the tail didn't move */
old->size = 0;
}
if (mad_stat != 0) if (mad_stat != 0)
{ {
DEBUGF("Audio stream error - %d\n", stream.error); DEBUGF("Audio stream error - %d\n", stream.error);
@ -1113,13 +1128,6 @@ static void audio_thread(void)
/* Generate the pcm samples */ /* Generate the pcm samples */
mad_synth_frame(&synth, &frame); mad_synth_frame(&synth, &frame);
if (stream.next_frame == NULL)
{
/* What to do here? */
DEBUGF("/* What to do here? */\n");
break;
}
/** Output **/ /** Output **/
/* TODO: Output through core dsp. We'll still use our own PCM buffer /* TODO: Output through core dsp. We'll still use our own PCM buffer
@ -1257,6 +1265,7 @@ static void video_thread(void)
char str[80]; char str[80];
int dither_index = 0; 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 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;
@ -1307,6 +1316,8 @@ static void video_thread(void)
while (videostatus == STREAM_PAUSING) while (videostatus == STREAM_PAUSING)
rb->sleep(HZ/10); rb->sleep(HZ/10);
continue;
} }
state = mpeg2_parse (mpeg2dec); state = mpeg2_parse (mpeg2dec);
@ -1336,7 +1347,6 @@ static void video_thread(void)
case STATE_PICTURE: case STATE_PICTURE:
{ {
/* A new picture is available - see if we should draw it */ /* A new picture is available - see if we should draw it */
uint32_t period; /* Frame period in clock ticks */
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 /* No limiting => no dropping so simply make sure skipping is off
@ -1344,12 +1354,12 @@ static void video_thread(void)
if (!settings.limitfps) if (!settings.limitfps)
goto picture_done; goto picture_done;
period = TIME_TO_TICKS(info->sequence->frame_period);
/* Get presentation times in audio samples - quite accurate /* Get presentation times in audio samples - quite accurate
enough */ enough - add previous frame duration if not stamped */
curr_time = (info->current_picture->flags & PIC_FLAG_TAGS) ? curr_time = (info->display_picture->flags & PIC_FLAG_TAGS) ?
TS_TO_TICKS(info->current_picture->tag) : (curr_time + period); TS_TO_TICKS(info->display_picture->tag) : (curr_time + period);
period = TIME_TO_TICKS(info->sequence->frame_period);
eta_video = curr_time; eta_video = curr_time;
eta_audio = get_stream_time(); eta_audio = get_stream_time();
@ -1442,21 +1452,32 @@ static void video_thread(void)
if (frame_drop_level > 1 || offset > CLOCK_RATE*167/1000) if (frame_drop_level > 1 || offset > CLOCK_RATE*167/1000)
{ {
/* Frame type: I/P/B/D */ /* Frame type: I/P/B/D */
int type = info->current_picture->flags & PIC_MASK_CODING_TYPE; int type = info->display_picture->flags & PIC_MASK_CODING_TYPE;
/* Things are running a bit late or all frames are being /* Things are running a bit late or all frames are being
dropped until a key frame */ dropped until a key frame */
if (frame_drop_level > 1)
if (frame_drop_level > 1 && (type == I_TYPE || type == D_TYPE)) {
frame_drop_level = 0; /* This frame can be drawn */ 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) if (frame_drop_level <= 1 && offset > CLOCK_RATE*250/1000)
{ {
/* Things are very, very late. Resort to stronger measures /* Things are very, very late. Resort to stronger measures
to keep sync by dropping a I/D/P frame. Drawing cannot to keep sync by dropping a I/D/P frame. Drawing cannot
take place again until the next key frame. */ take place again until the next key frame. */
if (type == I_TYPE || type == D_TYPE || type == P_TYPE) switch (type)
{
case PIC_FLAG_CODING_TYPE_I:
case PIC_FLAG_CODING_TYPE_P:
case PIC_FLAG_CODING_TYPE_D:
frame_drop_level = 2; frame_drop_level = 2;
}
} }
drop_frame = 1 << frame_drop_level; drop_frame = 1 << frame_drop_level;
@ -1466,12 +1487,18 @@ static void video_thread(void)
/* Timeout has expired and this frame will be drawn if it /* Timeout has expired and this frame will be drawn if it
is available. This may reverse the decision to drop a is available. This may reverse the decision to drop a
key frame above. */ key frame above. */
if (frame_drop_level <= 1 || type == I_TYPE || type == D_TYPE) switch (type)
{ {
drop_frame = 0; default:
if (frame_drop_level <= 1)
{
case PIC_FLAG_CODING_TYPE_I:
case PIC_FLAG_CODING_TYPE_D:
drop_frame = 0;
}
} }
} }
else if (type == B_TYPE) else if (type == PIC_FLAG_CODING_TYPE_B)
{ {
/* We want to drop something, so this B frame won't even be /* We want to drop something, so this B frame won't even be
decoded. Drawing can happen on the next frame if so decoded. Drawing can happen on the next frame if so
@ -1538,7 +1565,7 @@ static void video_thread(void)
if (clock_ticks != 0) if (clock_ticks != 0)
fps = num_drawn*CLOCK_RATE*10ll / clock_ticks; fps = num_drawn*CLOCK_RATE*10ll / clock_ticks;
rb->snprintf(str, sizeof(str), "%d.%d %d %d", rb->snprintf(str, sizeof(str), "%d.%d %d %d ",
fps / 10, fps % 10, num_skipped, fps / 10, fps % 10, num_skipped,
info->display_picture->temporal_reference); info->display_picture->temporal_reference);
rb->lcd_putsxy(0, 0, str); rb->lcd_putsxy(0, 0, str);