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 "mpeg_settings.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"
PLUGIN_HEADER
@ -493,12 +489,13 @@ static void init_mad(void* mad_frame_overlap)
#define SYSTEM_HEADER_START_CODE 0x000001bbul
/* 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) \
((uint32_t)(((p)[b0] >> 1 << 30) | \
((p)[b1] << 22) | \
((p)[b2] >> 1 << 15) | \
((p)[b3] << 7) | \
((p)[b4] >> 1 )))
((uint32_t)(((p)[b0] >> 1 << 29) | \
((p)[b1] << 21) | \
((p)[b2] >> 1 << 14) | \
((p)[b3] << 6) | \
((p)[b4] >> 2 )))
/* This function demuxes the streams and gives the next stream data pointer */
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 */
if (header[7] & 0x80)
{
/* header has a pts */
uint32_t pts = TS_FROM_HEADER(header, 9, 10, 11, 12, 13);
if (stream >= 0xe0)
{
/* video stream - header may have a dts as well */
uint32_t dts = (header[7] & 0x40) == 0 ?
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)
{
/* header has a pts */
uint32_t pts = TS_FROM_HEADER(ptsbuf, -1, 0, 1, 2, 3);
if (stream >= 0xe0)
{
/* video stream - header may have a dts as well */
uint32_t dts = (ptsbuf[-1] & 0xf0) != 0x30 ?
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 */
#define AVERAGE(var, x, count) (((var) * (count-1) + (x)) / (count))
/* Convert 90kHz PTS/DTS ticks to our clock ticks */
#define TS_TO_TICKS(pts) ((uint64_t)CLOCK_RATE*(pts) / 90000)
/* Convert 45kHz PTS/DTS ticks to our clock ticks */
#define TS_TO_TICKS(pts) ((uint64_t)CLOCK_RATE*(pts) / 45000)
/* Convert 27MHz ticks to our clock ticks */
#define TIME_TO_TICKS(stamp) ((uint64_t)CLOCK_RATE*(stamp) / 27000000)
@ -737,14 +738,14 @@ static bool init_mpabuf(void)
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 */
#define PTS_QUEUE_MASK (PTS_QUEUE_LEN-1)
struct pts_queue_slot
{
uint32_t pts; /* Time stamp for 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
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_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 */
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];
}
/* 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
mpa frame is sent out */
{
@ -992,6 +997,21 @@ static void audio_thread(void)
if (audiostatus == PLEASE_STOP)
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 **/
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)
{
/* Get data from next audio packet */
len = audio_str.curr_packet_end - audio_str.curr_packet;
if (audio_str.tagged)
do
{
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();
stamp->pts = TS_TO_TICKS(audio_str.curr_pts);
/* pts->size should have been zeroed when slot was freed */
struct pts_queue_slot *stamp = pts;
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
like more data in the same packet */
stamp->size += len;
audio_str.tagged = 0;
else
{
/* 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;
/* Get data from next audio packet */
get_next_data(&audio_str);
}
else
{
/* 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);
while (audio_str.curr_packet != NULL &&
mpabuf_used < MPA_MAX_FRAME_SIZE + MAD_BUFFER_GUARD);
}
else if (mpabuf_used <= 0)
{
@ -1051,6 +1078,13 @@ static void audio_thread(void)
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 */
mpabuf = (uint8_t *)stream.next_frame;
@ -1059,25 +1093,6 @@ static void audio_thread(void)
mpabuf_used -= 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)
{
DEBUGF("Audio stream error - %d\n", stream.error);
@ -1113,13 +1128,6 @@ static void audio_thread(void)
/* Generate the pcm samples */
mad_synth_frame(&synth, &frame);
if (stream.next_frame == NULL)
{
/* What to do here? */
DEBUGF("/* What to do here? */\n");
break;
}
/** Output **/
/* 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];
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;
@ -1307,6 +1316,8 @@ static void video_thread(void)
while (videostatus == STREAM_PAUSING)
rb->sleep(HZ/10);
continue;
}
state = mpeg2_parse (mpeg2dec);
@ -1336,7 +1347,6 @@ static void video_thread(void)
case STATE_PICTURE:
{
/* 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 */
/* No limiting => no dropping so simply make sure skipping is off
@ -1344,12 +1354,12 @@ static void video_thread(void)
if (!settings.limitfps)
goto picture_done;
period = TIME_TO_TICKS(info->sequence->frame_period);
/* Get presentation times in audio samples - quite accurate
enough */
curr_time = (info->current_picture->flags & PIC_FLAG_TAGS) ?
TS_TO_TICKS(info->current_picture->tag) : (curr_time + period);
enough - add previous frame duration if not stamped */
curr_time = (info->display_picture->flags & PIC_FLAG_TAGS) ?
TS_TO_TICKS(info->display_picture->tag) : (curr_time + period);
period = TIME_TO_TICKS(info->sequence->frame_period);
eta_video = curr_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)
{
/* 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
dropped until a key frame */
if (frame_drop_level > 1 && (type == I_TYPE || type == D_TYPE))
frame_drop_level = 0; /* This frame can be drawn */
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. */
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;
}
}
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
is available. This may reverse the decision to drop a
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
decoded. Drawing can happen on the next frame if so
@ -1538,7 +1565,7 @@ static void video_thread(void)
if (clock_ticks != 0)
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,
info->display_picture->temporal_reference);
rb->lcd_putsxy(0, 0, str);