mirror of
https://github.com/Rockbox/rockbox.git
synced 2025-10-14 02:27:39 -04:00
MpegPlayer: Add a simple messaging scheme for sending stream commands. Remove the need to disable FIQ when adding to PCM buffer.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@13931 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
parent
9511526410
commit
b8bd1ee557
3 changed files with 298 additions and 151 deletions
|
@ -488,13 +488,12 @@ static const struct plugin_api rockbox_api = {
|
||||||
#endif
|
#endif
|
||||||
/* new stuff at the end, sort into place next time
|
/* new stuff at the end, sort into place next time
|
||||||
the API gets incompatible */
|
the API gets incompatible */
|
||||||
#if NUM_CORES > 1
|
|
||||||
|
#if (CONFIG_CODEC == SWCODEC)
|
||||||
spinlock_init,
|
spinlock_init,
|
||||||
spinlock_lock,
|
spinlock_lock,
|
||||||
spinlock_unlock,
|
spinlock_unlock,
|
||||||
#endif
|
|
||||||
|
|
||||||
#if (CONFIG_CODEC == SWCODEC)
|
|
||||||
codec_load_file,
|
codec_load_file,
|
||||||
get_codec_filename,
|
get_codec_filename,
|
||||||
get_metadata,
|
get_metadata,
|
||||||
|
|
|
@ -610,13 +610,11 @@ struct plugin_api {
|
||||||
/* new stuff at the end, sort into place next time
|
/* new stuff at the end, sort into place next time
|
||||||
the API gets incompatible */
|
the API gets incompatible */
|
||||||
|
|
||||||
#if NUM_CORES > 1
|
#if (CONFIG_CODEC == SWCODEC)
|
||||||
void (*spinlock_init)(struct mutex *m);
|
void (*spinlock_init)(struct mutex *m);
|
||||||
void (*spinlock_lock)(struct mutex *m);
|
void (*spinlock_lock)(struct mutex *m);
|
||||||
void (*spinlock_unlock)(struct mutex *m);
|
void (*spinlock_unlock)(struct mutex *m);
|
||||||
#endif
|
|
||||||
|
|
||||||
#if (CONFIG_CODEC == SWCODEC)
|
|
||||||
int (*codec_load_file)(const char* codec, struct codec_api *api);
|
int (*codec_load_file)(const char* codec, struct codec_api *api);
|
||||||
const char *(*get_codec_filename)(int cod_spec);
|
const char *(*get_codec_filename)(int cod_spec);
|
||||||
bool (*get_metadata)(struct track_info* track, int fd, const char* trackname,
|
bool (*get_metadata)(struct track_info* track, int fd, const char* trackname,
|
||||||
|
|
|
@ -169,38 +169,16 @@ static int total_offset = 0;
|
||||||
static int num_drawn = 0;
|
static int num_drawn = 0;
|
||||||
static int count_start = 0;
|
static int count_start = 0;
|
||||||
|
|
||||||
/* Utility */
|
|
||||||
|
|
||||||
/* Atomically add one long value to another - not core safe atm if ever needed */
|
|
||||||
static inline void locked_add_long(volatile long *value, long amount)
|
|
||||||
{
|
|
||||||
#if defined (CPU_ARM)
|
|
||||||
/* Disable the fiq - this cuts an instruction out over using the
|
|
||||||
system functions */
|
|
||||||
long cpsr, x;
|
|
||||||
asm volatile (
|
|
||||||
"mrs %[sr], cpsr \r\n"
|
|
||||||
"orr %[sr], %[sr], #0x40 \r\n"
|
|
||||||
"msr cpsr_c, %[sr] \r\n"
|
|
||||||
"ldr %[x], [%[value]] \r\n"
|
|
||||||
"add %[x], %[x], %[amount] \r\n"
|
|
||||||
"str %[x], [%[value]] \r\n"
|
|
||||||
"bic %[sr], %[sr], #0x40 \r\n"
|
|
||||||
"msr cpsr_c, %[sr] \r\n"
|
|
||||||
: [sr]"=&r"(cpsr), [x]"=&r"(x)
|
|
||||||
: [value]"r"(value), [amount]"r"(amount)
|
|
||||||
);
|
|
||||||
#elif defined (CPU_COLDFIRE)
|
|
||||||
add_l(amount, value);
|
|
||||||
#else
|
|
||||||
/* Don't know what this is so can't lock it */
|
|
||||||
*value += amount;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Streams */
|
/* Streams */
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
|
struct thread_entry *thread; /* Stream's thread */
|
||||||
|
int status; /* Current stream status */
|
||||||
|
struct event ev; /* Event sent to steam */
|
||||||
|
int have_msg; /* 1=event pending */
|
||||||
|
int replied; /* 1=replied to last event */
|
||||||
|
int reply; /* reply value */
|
||||||
|
struct mutex msg_lock; /* serialization for event senders */
|
||||||
uint8_t* curr_packet; /* Current stream packet beginning */
|
uint8_t* curr_packet; /* Current stream packet beginning */
|
||||||
uint8_t* curr_packet_end; /* Current stream packet end */
|
uint8_t* curr_packet_end; /* Current stream packet end */
|
||||||
|
|
||||||
|
@ -219,6 +197,138 @@ typedef struct
|
||||||
static Stream audio_str IBSS_ATTR;
|
static Stream audio_str IBSS_ATTR;
|
||||||
static Stream video_str IBSS_ATTR;
|
static Stream video_str IBSS_ATTR;
|
||||||
|
|
||||||
|
/* Messages */
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
STREAM_STOP,
|
||||||
|
STREAM_PLAY,
|
||||||
|
STREAM_PAUSE,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Status */
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
STREAM_ERROR = -4,
|
||||||
|
STREAM_STOPPED = -3,
|
||||||
|
STREAM_TERMINATED = -2,
|
||||||
|
STREAM_DONE = -1,
|
||||||
|
STREAM_PLAYING = 0,
|
||||||
|
STREAM_PAUSED,
|
||||||
|
STREAM_BUFFERING
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Returns true if a message is waiting */
|
||||||
|
static inline bool str_have_msg(Stream *str)
|
||||||
|
{
|
||||||
|
return str->have_msg != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Waits until a message is sent */
|
||||||
|
static void str_wait_msg(Stream *str)
|
||||||
|
{
|
||||||
|
/* NOTE: sleep(0) caused a prefectch abort at C0EDBABE on e200 -
|
||||||
|
will look into this oddness */
|
||||||
|
#if 0
|
||||||
|
int spin_count = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
while (str->have_msg == 0)
|
||||||
|
{
|
||||||
|
#if 0
|
||||||
|
if (spin_count < 100)
|
||||||
|
{
|
||||||
|
rb->yield();
|
||||||
|
spin_count++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
rb->sleep(0);
|
||||||
|
#endif
|
||||||
|
rb->yield();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Returns a message waiting or blocks until one is available - removes the
|
||||||
|
event */
|
||||||
|
static bool str_get_msg(Stream *str, struct event *ev)
|
||||||
|
{
|
||||||
|
str_wait_msg(str);
|
||||||
|
ev->id = str->ev.id;
|
||||||
|
ev->data = str->ev.data;
|
||||||
|
str->have_msg = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Peeks at the current message without blocking, returns the data but
|
||||||
|
does not remove the event */
|
||||||
|
static bool str_look_msg(Stream *str, struct event *ev)
|
||||||
|
{
|
||||||
|
if (!str_have_msg(str))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ev->id = str->ev.id;
|
||||||
|
ev->data = str->ev.data;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Replies to the last message pulled - has no effect if last message has not
|
||||||
|
been pulled or already replied */
|
||||||
|
static void str_reply_msg(Stream *str, int reply)
|
||||||
|
{
|
||||||
|
if (str->replied == 1 || str->have_msg != 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
str->reply = reply;
|
||||||
|
str->replied = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sends a message to a stream and waits for a reply */
|
||||||
|
static intptr_t str_send_msg(Stream *str, int id, intptr_t data)
|
||||||
|
{
|
||||||
|
/* NOTE: sleep(0) caused a prefectch abort at C0EDBABE on e200 -
|
||||||
|
will look into this oddness */
|
||||||
|
#if 0
|
||||||
|
int spin_count = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
intptr_t reply;
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
if (str->thread == rb->thread_get_current())
|
||||||
|
return str->dispatch_fn(str, msg);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Only one thread at a time, please */
|
||||||
|
rb->spinlock_lock(&str->msg_lock);
|
||||||
|
|
||||||
|
str->ev.id = id;
|
||||||
|
str->ev.data = data;
|
||||||
|
str->reply = 0;
|
||||||
|
str->replied = 0;
|
||||||
|
str->have_msg = 1;
|
||||||
|
|
||||||
|
while (str->replied == 0 && str->status != STREAM_TERMINATED)
|
||||||
|
{
|
||||||
|
#if 0
|
||||||
|
if (spin_count < 100)
|
||||||
|
{
|
||||||
|
rb->yield();
|
||||||
|
spin_count++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
rb->sleep(0);
|
||||||
|
#endif
|
||||||
|
rb->yield();
|
||||||
|
}
|
||||||
|
|
||||||
|
reply = str->reply;
|
||||||
|
|
||||||
|
rb->spinlock_unlock(&str->msg_lock);
|
||||||
|
|
||||||
|
return reply;
|
||||||
|
}
|
||||||
|
|
||||||
/* NOTE: Putting the following variables in IRAM cause audio corruption
|
/* NOTE: Putting the following variables in IRAM cause audio corruption
|
||||||
on the ipod (reason unknown)
|
on the ipod (reason unknown)
|
||||||
*/
|
*/
|
||||||
|
@ -251,26 +361,6 @@ static struct event_queue msg_queue IBSS_ATTR;
|
||||||
#define MSG_BUFFER_NEARLY_EMPTY 1
|
#define MSG_BUFFER_NEARLY_EMPTY 1
|
||||||
#define MSG_EXIT_REQUESTED 2
|
#define MSG_EXIT_REQUESTED 2
|
||||||
|
|
||||||
/* Threads */
|
|
||||||
static struct thread_entry* audiothread_id;
|
|
||||||
static struct thread_entry* videothread_id;
|
|
||||||
|
|
||||||
/* Status */
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
STREAM_PLAYING = 0,
|
|
||||||
STREAM_DONE,
|
|
||||||
STREAM_PAUSING,
|
|
||||||
STREAM_BUFFERING,
|
|
||||||
STREAM_ERROR,
|
|
||||||
PLEASE_STOP,
|
|
||||||
PLEASE_PAUSE,
|
|
||||||
THREAD_TERMINATED,
|
|
||||||
};
|
|
||||||
|
|
||||||
volatile int audiostatus IBSS_ATTR;
|
|
||||||
volatile int videostatus IBSS_ATTR;
|
|
||||||
|
|
||||||
/* Various buffers */
|
/* Various buffers */
|
||||||
/* TODO: Can we reduce the PCM buffer size? */
|
/* TODO: Can we reduce the PCM buffer size? */
|
||||||
#define PCMBUFFER_SIZE ((512*1024)-PCMBUFFER_GUARD_SIZE)
|
#define PCMBUFFER_SIZE ((512*1024)-PCMBUFFER_GUARD_SIZE)
|
||||||
|
@ -700,7 +790,8 @@ struct pcm_frame_header /* Header added to pcm data every time a decoded
|
||||||
|
|
||||||
#define PCMBUF_PLAY_ALL 1l /* Forces buffer to play back all data */
|
#define PCMBUF_PLAY_ALL 1l /* Forces buffer to play back all data */
|
||||||
#define PCMBUF_PLAY_NONE LONG_MAX /* Keeps buffer from playing any data */
|
#define PCMBUF_PLAY_NONE LONG_MAX /* Keeps buffer from playing any data */
|
||||||
static volatile ssize_t pcmbuf_used IBSS_ATTR;
|
static volatile uint64_t pcmbuf_read IBSS_ATTR;
|
||||||
|
static volatile uint64_t pcmbuf_written IBSS_ATTR;
|
||||||
static volatile ssize_t pcmbuf_threshold IBSS_ATTR;
|
static volatile ssize_t pcmbuf_threshold IBSS_ATTR;
|
||||||
static struct pcm_frame_header *pcm_buffer IBSS_ATTR;
|
static struct pcm_frame_header *pcm_buffer IBSS_ATTR;
|
||||||
static struct pcm_frame_header *pcmbuf_end IBSS_ATTR;
|
static struct pcm_frame_header *pcmbuf_end IBSS_ATTR;
|
||||||
|
@ -711,6 +802,11 @@ static volatile uint32_t samplesplayed IBSS_ATTR; /* Our base clock */
|
||||||
static volatile uint32_t samplestart IBSS_ATTR; /* Clock at playback start */
|
static volatile uint32_t samplestart IBSS_ATTR; /* Clock at playback start */
|
||||||
static volatile int32_t sampleadjust IBSS_ATTR; /* Clock drift adjustment */
|
static volatile int32_t sampleadjust IBSS_ATTR; /* Clock drift adjustment */
|
||||||
|
|
||||||
|
static ssize_t pcmbuf_used(void)
|
||||||
|
{
|
||||||
|
return (ssize_t)(pcmbuf_written - pcmbuf_read);
|
||||||
|
}
|
||||||
|
|
||||||
static bool init_pcmbuf(void)
|
static bool init_pcmbuf(void)
|
||||||
{
|
{
|
||||||
pcm_buffer = mpeg2_malloc(PCMBUFFER_SIZE + PCMBUFFER_GUARD_SIZE, -2);
|
pcm_buffer = mpeg2_malloc(PCMBUFFER_SIZE + PCMBUFFER_GUARD_SIZE, -2);
|
||||||
|
@ -721,7 +817,8 @@ static bool init_pcmbuf(void)
|
||||||
pcmbuf_head = pcm_buffer;
|
pcmbuf_head = pcm_buffer;
|
||||||
pcmbuf_tail = pcm_buffer;
|
pcmbuf_tail = pcm_buffer;
|
||||||
pcmbuf_end = SKIPBYTES(pcm_buffer, PCMBUFFER_SIZE);
|
pcmbuf_end = SKIPBYTES(pcm_buffer, PCMBUFFER_SIZE);
|
||||||
pcmbuf_used = 0;
|
pcmbuf_read = 0;
|
||||||
|
pcmbuf_written = 0;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -741,7 +838,7 @@ static void get_more(unsigned char** start, size_t* size)
|
||||||
static unsigned char silence[4412] __attribute__((aligned (4))) = { 0 };
|
static unsigned char silence[4412] __attribute__((aligned (4))) = { 0 };
|
||||||
size_t sz;
|
size_t sz;
|
||||||
|
|
||||||
if (pcmbuf_used >= pcmbuf_threshold)
|
if (pcmbuf_used() >= pcmbuf_threshold)
|
||||||
{
|
{
|
||||||
uint32_t time = pcmbuf_tail->time;
|
uint32_t time = pcmbuf_tail->time;
|
||||||
sz = pcmbuf_tail->size;
|
sz = pcmbuf_tail->size;
|
||||||
|
@ -750,7 +847,7 @@ static void get_more(unsigned char** start, size_t* size)
|
||||||
|
|
||||||
pcm_advance_buffer(&pcmbuf_tail, sz);
|
pcm_advance_buffer(&pcmbuf_tail, sz);
|
||||||
|
|
||||||
pcmbuf_used -= sz;
|
pcmbuf_read += sz;
|
||||||
|
|
||||||
sz -= sizeof (*pcmbuf_tail);
|
sz -= sizeof (*pcmbuf_tail);
|
||||||
|
|
||||||
|
@ -771,8 +868,8 @@ static void get_more(unsigned char** start, size_t* size)
|
||||||
|
|
||||||
samplesplayed += sz >> 2;
|
samplesplayed += sz >> 2;
|
||||||
|
|
||||||
if (pcmbuf_used < 0)
|
if (pcmbuf_read > pcmbuf_written)
|
||||||
pcmbuf_used = 0;
|
pcmbuf_read = pcmbuf_written;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Flushes the buffer - clock keeps counting */
|
/* Flushes the buffer - clock keeps counting */
|
||||||
|
@ -783,7 +880,8 @@ static void pcm_playback_flush(void)
|
||||||
if (was_playing)
|
if (was_playing)
|
||||||
rb->pcm_play_stop();
|
rb->pcm_play_stop();
|
||||||
|
|
||||||
pcmbuf_used = 0;
|
pcmbuf_read = 0;
|
||||||
|
pcmbuf_written = 0;
|
||||||
pcmbuf_head = pcmbuf_tail;
|
pcmbuf_head = pcmbuf_tail;
|
||||||
|
|
||||||
if (was_playing)
|
if (was_playing)
|
||||||
|
@ -855,11 +953,30 @@ static inline int32_t clip_sample(int32_t sample)
|
||||||
return sample;
|
return sample;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void button_loop(void)
|
static int button_loop(void)
|
||||||
{
|
{
|
||||||
bool result;
|
bool result;
|
||||||
int vol, minvol, maxvol;
|
int vol, minvol, maxvol;
|
||||||
int button = rb->button_get(false);
|
int button;
|
||||||
|
|
||||||
|
if (str_have_msg(&audio_str))
|
||||||
|
{
|
||||||
|
struct event ev;
|
||||||
|
str_get_msg(&audio_str, &ev);
|
||||||
|
|
||||||
|
if (ev.id == STREAM_STOP)
|
||||||
|
{
|
||||||
|
audio_str.status = STREAM_STOPPED;
|
||||||
|
str_reply_msg(&audio_str, 1);
|
||||||
|
goto quit;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
str_reply_msg(&audio_str, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
button = rb->button_get(false);
|
||||||
|
|
||||||
switch (button)
|
switch (button)
|
||||||
{
|
{
|
||||||
|
@ -897,13 +1014,8 @@ static void button_loop(void)
|
||||||
|
|
||||||
case MPEG_MENU:
|
case MPEG_MENU:
|
||||||
pcm_playback_play_pause(false);
|
pcm_playback_play_pause(false);
|
||||||
if (videostatus != STREAM_DONE) {
|
audio_str.status = STREAM_PAUSED;
|
||||||
videostatus=PLEASE_PAUSE;
|
str_send_msg(&video_str, STREAM_PAUSE, 0);
|
||||||
|
|
||||||
/* Wait for video thread to stop */
|
|
||||||
while (videostatus == PLEASE_PAUSE) { rb->sleep(HZ/25); }
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef HAVE_LCD_COLOR
|
#ifndef HAVE_LCD_COLOR
|
||||||
gray_show(false);
|
gray_show(false);
|
||||||
#endif
|
#endif
|
||||||
|
@ -919,21 +1031,23 @@ static void button_loop(void)
|
||||||
rb->lcd_setfont(FONT_SYSFIXED);
|
rb->lcd_setfont(FONT_SYSFIXED);
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
audiostatus = PLEASE_STOP;
|
str_send_msg(&video_str, STREAM_STOP, 0);
|
||||||
if (videostatus != STREAM_DONE) videostatus = PLEASE_STOP;
|
audio_str.status = STREAM_STOPPED;
|
||||||
} else {
|
} else {
|
||||||
if (videostatus != STREAM_DONE) videostatus = STREAM_PLAYING;
|
audio_str.status = STREAM_PLAYING;
|
||||||
|
str_send_msg(&video_str, STREAM_PLAY, 0);
|
||||||
pcm_playback_play_pause(true);
|
pcm_playback_play_pause(true);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MPEG_STOP:
|
case MPEG_STOP:
|
||||||
audiostatus = PLEASE_STOP;
|
str_send_msg(&video_str, STREAM_STOP, 0);
|
||||||
if (videostatus != STREAM_DONE) videostatus = PLEASE_STOP;
|
audio_str.status = STREAM_STOPPED;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MPEG_PAUSE:
|
case MPEG_PAUSE:
|
||||||
if (videostatus != STREAM_DONE) videostatus=PLEASE_PAUSE;
|
str_send_msg(&video_str, STREAM_PAUSE, 0);
|
||||||
|
audio_str.status = STREAM_PAUSED;
|
||||||
pcm_playback_play_pause(false);
|
pcm_playback_play_pause(false);
|
||||||
|
|
||||||
button = BUTTON_NONE;
|
button = BUTTON_NONE;
|
||||||
|
@ -943,13 +1057,14 @@ static void button_loop(void)
|
||||||
do {
|
do {
|
||||||
button = rb->button_get(true);
|
button = rb->button_get(true);
|
||||||
if (button == MPEG_STOP) {
|
if (button == MPEG_STOP) {
|
||||||
audiostatus = PLEASE_STOP;
|
str_send_msg(&video_str, STREAM_STOP, 0);
|
||||||
if (videostatus != STREAM_DONE) videostatus = PLEASE_STOP;
|
audio_str.status = STREAM_STOPPED;
|
||||||
return;
|
goto quit;
|
||||||
}
|
}
|
||||||
} while (button != MPEG_PAUSE);
|
} while (button != MPEG_PAUSE);
|
||||||
|
|
||||||
if (videostatus != STREAM_DONE) videostatus = STREAM_PLAYING;
|
str_send_msg(&video_str, STREAM_PLAY, 0);
|
||||||
|
audio_str.status = STREAM_PLAYING;
|
||||||
pcm_playback_play_pause(true);
|
pcm_playback_play_pause(true);
|
||||||
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
|
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
|
||||||
rb->cpu_boost(true);
|
rb->cpu_boost(true);
|
||||||
|
@ -958,10 +1073,13 @@ static void button_loop(void)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if(rb->default_event_handler(button) == SYS_USB_CONNECTED) {
|
if(rb->default_event_handler(button) == SYS_USB_CONNECTED) {
|
||||||
audiostatus = PLEASE_STOP;
|
str_send_msg(&video_str, STREAM_STOP, 0);
|
||||||
if (videostatus != STREAM_DONE) videostatus = PLEASE_STOP;
|
audio_str.status = STREAM_STOPPED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
quit:
|
||||||
|
return audio_str.status;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void audio_thread(void)
|
static void audio_thread(void)
|
||||||
|
@ -996,10 +1114,8 @@ static void audio_thread(void)
|
||||||
int mad_stat;
|
int mad_stat;
|
||||||
size_t len;
|
size_t len;
|
||||||
|
|
||||||
button_loop();
|
if (button_loop() < 0)
|
||||||
|
goto done;
|
||||||
if (audiostatus == PLEASE_STOP)
|
|
||||||
goto done;
|
|
||||||
|
|
||||||
if (pts->size <= 0)
|
if (pts->size <= 0)
|
||||||
{
|
{
|
||||||
|
@ -1146,10 +1262,21 @@ static void audio_thread(void)
|
||||||
|
|
||||||
/* Leave at least 32KB free (this will be the currently
|
/* Leave at least 32KB free (this will be the currently
|
||||||
playing chunk) */
|
playing chunk) */
|
||||||
while (pcmbuf_used + wait_for > PCMBUFFER_SIZE)
|
while (pcmbuf_used() + wait_for > PCMBUFFER_SIZE)
|
||||||
{
|
{
|
||||||
if (audiostatus == PLEASE_STOP)
|
if (str_have_msg(&audio_str))
|
||||||
goto done;
|
{
|
||||||
|
struct event ev;
|
||||||
|
str_look_msg(&audio_str, &ev);
|
||||||
|
|
||||||
|
if (ev.id == STREAM_STOP)
|
||||||
|
{
|
||||||
|
str_get_msg(&audio_str, &ev);
|
||||||
|
str_reply_msg(&audio_str, 1);
|
||||||
|
goto stop_and_wait;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
rb->priority_yield();
|
rb->priority_yield();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1192,56 +1319,56 @@ static void audio_thread(void)
|
||||||
|
|
||||||
pcm_advance_buffer(&pcmbuf_head, size);
|
pcm_advance_buffer(&pcmbuf_head, size);
|
||||||
|
|
||||||
if (pcmbuf_threshold != PCMBUF_PLAY_ALL && pcmbuf_used >= 64*1024)
|
if (pcmbuf_threshold != PCMBUF_PLAY_ALL && pcmbuf_used() >= 64*1024)
|
||||||
{
|
{
|
||||||
/* We've reached our size treshold so start playing back the
|
/* We've reached our size treshold so start playing back the
|
||||||
audio in the buffer and set the buffer to play all data */
|
audio in the buffer and set the buffer to play all data */
|
||||||
audiostatus = STREAM_PLAYING;
|
audio_str.status = STREAM_PLAYING;
|
||||||
pcmbuf_threshold = PCMBUF_PLAY_ALL;
|
pcmbuf_threshold = PCMBUF_PLAY_ALL;
|
||||||
pcm_playback_seek_time(pcmbuf_tail->time);
|
pcm_playback_seek_time(pcmbuf_tail->time);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Make this data available to DMA */
|
/* Make this data available to DMA */
|
||||||
locked_add_long(&pcmbuf_used, size);
|
pcmbuf_written += size;
|
||||||
}
|
}
|
||||||
|
|
||||||
rb->yield();
|
rb->yield();
|
||||||
} /* end decoding loop */
|
} /* end decoding loop */
|
||||||
|
|
||||||
done:
|
done:
|
||||||
if (audiostatus != PLEASE_STOP)
|
if (audio_str.status == STREAM_STOPPED)
|
||||||
{
|
goto stop_and_wait;
|
||||||
/* Force any residue to play if audio ended before reaching the
|
|
||||||
threshold */
|
|
||||||
if (pcmbuf_threshold != PCMBUF_PLAY_ALL && pcmbuf_used > 0)
|
|
||||||
{
|
|
||||||
pcm_playback_play(pcmbuf_tail->time);
|
|
||||||
pcmbuf_threshold = PCMBUF_PLAY_ALL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rb->pcm_is_playing() && !rb->pcm_is_paused())
|
/* Force any residue to play if audio ended before reaching the
|
||||||
|
threshold */
|
||||||
|
if (pcmbuf_threshold != PCMBUF_PLAY_ALL && pcmbuf_used() > 0)
|
||||||
|
{
|
||||||
|
pcm_playback_play(pcmbuf_tail->time);
|
||||||
|
pcmbuf_threshold = PCMBUF_PLAY_ALL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rb->pcm_is_playing() && !rb->pcm_is_paused())
|
||||||
|
{
|
||||||
|
/* Wait for audio to finish */
|
||||||
|
while (pcmbuf_used() > 0)
|
||||||
{
|
{
|
||||||
/* Wait for audio to finish */
|
if (button_loop() == STREAM_STOPPED)
|
||||||
while (pcmbuf_used > 0 && audiostatus != PLEASE_STOP)
|
break;
|
||||||
{
|
rb->sleep(HZ/10);
|
||||||
button_loop();
|
|
||||||
rb->sleep(HZ/10);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
audiostatus = STREAM_DONE;
|
stop_and_wait:
|
||||||
|
|
||||||
|
audio_str.status = STREAM_DONE;
|
||||||
|
|
||||||
/* Process events until finished */
|
/* Process events until finished */
|
||||||
while (audiostatus != PLEASE_STOP)
|
while (button_loop() != STREAM_STOPPED)
|
||||||
{
|
|
||||||
button_loop();
|
|
||||||
rb->sleep(HZ/4);
|
rb->sleep(HZ/4);
|
||||||
}
|
|
||||||
|
|
||||||
pcm_playback_stop();
|
pcm_playback_stop();
|
||||||
|
|
||||||
audiostatus = THREAD_TERMINATED;
|
audio_str.status = STREAM_TERMINATED;
|
||||||
rb->remove_thread(NULL);
|
rb->remove_thread(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1260,6 +1387,7 @@ static uint32_t video_stack[VIDEO_STACKSIZE / sizeof(uint32_t)] IBSS_ATTR;
|
||||||
|
|
||||||
static void video_thread(void)
|
static void video_thread(void)
|
||||||
{
|
{
|
||||||
|
struct event ev;
|
||||||
const mpeg2_info_t * info;
|
const mpeg2_info_t * info;
|
||||||
mpeg2_state_t state;
|
mpeg2_state_t state;
|
||||||
char str[80];
|
char str[80];
|
||||||
|
@ -1280,7 +1408,7 @@ static void video_thread(void)
|
||||||
{
|
{
|
||||||
rb->splash(0, "mpeg2_init failed");
|
rb->splash(0, "mpeg2_init failed");
|
||||||
/* Commit suicide */
|
/* Commit suicide */
|
||||||
videostatus = THREAD_TERMINATED;
|
video_str.status = STREAM_TERMINATED;
|
||||||
rb->remove_thread(NULL);
|
rb->remove_thread(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1302,24 +1430,36 @@ static void video_thread(void)
|
||||||
|
|
||||||
/* Wait if the audio thread is buffering - i.e. before
|
/* Wait if the audio thread is buffering - i.e. before
|
||||||
the first frames are decoded */
|
the first frames are decoded */
|
||||||
while (audiostatus == STREAM_BUFFERING)
|
while (audio_str.status == STREAM_BUFFERING)
|
||||||
rb->priority_yield();
|
rb->priority_yield();
|
||||||
|
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
if (videostatus == PLEASE_STOP)
|
/* quickly check mailbox first */
|
||||||
|
if (str_have_msg(&video_str))
|
||||||
{
|
{
|
||||||
break;
|
while (1)
|
||||||
}
|
{
|
||||||
else if (videostatus == PLEASE_PAUSE)
|
str_get_msg(&video_str, &ev);
|
||||||
{
|
|
||||||
videostatus = STREAM_PAUSING;
|
|
||||||
flush_icache();
|
|
||||||
|
|
||||||
while (videostatus == STREAM_PAUSING)
|
switch (ev.id)
|
||||||
rb->sleep(HZ/10);
|
{
|
||||||
|
case STREAM_STOP:
|
||||||
|
video_str.status = STREAM_STOPPED;
|
||||||
|
str_reply_msg(&video_str, 1);
|
||||||
|
goto done;
|
||||||
|
case STREAM_PAUSE:
|
||||||
|
flush_icache();
|
||||||
|
video_str.status = STREAM_PAUSED;
|
||||||
|
str_reply_msg(&video_str, 1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
continue;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
video_str.status = STREAM_PLAYING;
|
||||||
|
str_reply_msg(&video_str, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
state = mpeg2_parse (mpeg2dec);
|
state = mpeg2_parse (mpeg2dec);
|
||||||
|
@ -1551,8 +1691,16 @@ static void video_thread(void)
|
||||||
rb->priority_yield();
|
rb->priority_yield();
|
||||||
|
|
||||||
/* Make sure not to get stuck waiting here forever */
|
/* Make sure not to get stuck waiting here forever */
|
||||||
if (videostatus != STREAM_PLAYING)
|
if (str_have_msg(&video_str))
|
||||||
goto rendering_finished;
|
{
|
||||||
|
str_look_msg(&video_str, &ev);
|
||||||
|
|
||||||
|
if (ev.id != STREAM_PLAY)
|
||||||
|
goto rendering_finished;
|
||||||
|
|
||||||
|
str_get_msg(&video_str, &ev);
|
||||||
|
str_reply_msg(&video_str, 1);
|
||||||
|
}
|
||||||
|
|
||||||
eta_audio = get_stream_time();
|
eta_audio = get_stream_time();
|
||||||
}
|
}
|
||||||
|
@ -1599,13 +1747,21 @@ static void video_thread(void)
|
||||||
done:
|
done:
|
||||||
flush_icache();
|
flush_icache();
|
||||||
|
|
||||||
videostatus = STREAM_DONE;
|
video_str.status = STREAM_DONE;
|
||||||
|
|
||||||
while (videostatus != PLEASE_STOP)
|
while (1)
|
||||||
rb->sleep(HZ/5);
|
{
|
||||||
|
str_get_msg(&video_str, &ev);
|
||||||
|
|
||||||
|
if (ev.id == STREAM_STOP)
|
||||||
|
break;
|
||||||
|
|
||||||
|
str_reply_msg(&video_str, 0);
|
||||||
|
}
|
||||||
|
|
||||||
videostatus = THREAD_TERMINATED;
|
|
||||||
/* Commit suicide */
|
/* Commit suicide */
|
||||||
|
str_reply_msg(&video_str, 1);
|
||||||
|
video_str.status = STREAM_TERMINATED;
|
||||||
rb->remove_thread(NULL);
|
rb->remove_thread(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1756,8 +1912,10 @@ enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
|
||||||
video_str.buffer_remaining = disk_buf_len;
|
video_str.buffer_remaining = disk_buf_len;
|
||||||
audio_str.buffer_remaining = disk_buf_len;
|
audio_str.buffer_remaining = disk_buf_len;
|
||||||
|
|
||||||
audiostatus = STREAM_BUFFERING;
|
rb->spinlock_init(&audio_str.msg_lock);
|
||||||
videostatus = STREAM_PLAYING;
|
rb->spinlock_init(&video_str.msg_lock);
|
||||||
|
audio_str.status = STREAM_BUFFERING;
|
||||||
|
video_str.status = STREAM_PLAYING;
|
||||||
|
|
||||||
#ifndef HAVE_LCD_COLOR
|
#ifndef HAVE_LCD_COLOR
|
||||||
gray_show(true);
|
gray_show(true);
|
||||||
|
@ -1766,13 +1924,13 @@ enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
|
||||||
init_stream_lock();
|
init_stream_lock();
|
||||||
|
|
||||||
/* We put the video thread on the second processor for multi-core targets. */
|
/* We put the video thread on the second processor for multi-core targets. */
|
||||||
if ((videothread_id = rb->create_thread(video_thread,
|
if ((video_str.thread = rb->create_thread(video_thread,
|
||||||
(uint8_t*)video_stack,VIDEO_STACKSIZE,"mpgvideo" IF_PRIO(,PRIORITY_PLAYBACK)
|
(uint8_t*)video_stack,VIDEO_STACKSIZE,"mpgvideo" IF_PRIO(,PRIORITY_PLAYBACK)
|
||||||
IF_COP(, COP, true))) == NULL)
|
IF_COP(, COP, true))) == NULL)
|
||||||
{
|
{
|
||||||
rb->splash(HZ, "Cannot create video thread!");
|
rb->splash(HZ, "Cannot create video thread!");
|
||||||
}
|
}
|
||||||
else if ((audiothread_id = rb->create_thread(audio_thread,
|
else if ((audio_str.thread = rb->create_thread(audio_thread,
|
||||||
(uint8_t*)audio_stack,AUDIO_STACKSIZE,"mpgaudio" IF_PRIO(,PRIORITY_PLAYBACK)
|
(uint8_t*)audio_stack,AUDIO_STACKSIZE,"mpgaudio" IF_PRIO(,PRIORITY_PLAYBACK)
|
||||||
IF_COP(, CPU, false))) == NULL)
|
IF_COP(, CPU, false))) == NULL)
|
||||||
{
|
{
|
||||||
|
@ -1784,7 +1942,7 @@ enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
|
||||||
rb->lcd_setfont(FONT_SYSFIXED);
|
rb->lcd_setfont(FONT_SYSFIXED);
|
||||||
|
|
||||||
/* Wait until both threads have finished their work */
|
/* Wait until both threads have finished their work */
|
||||||
while ((audiostatus != STREAM_DONE) || (videostatus != STREAM_DONE))
|
while ((audio_str.status >= 0) || (video_str.status >= 0))
|
||||||
{
|
{
|
||||||
size_t audio_remaining = audio_str.buffer_remaining;
|
size_t audio_remaining = audio_str.buffer_remaining;
|
||||||
size_t video_remaining = video_str.buffer_remaining;
|
size_t video_remaining = video_str.buffer_remaining;
|
||||||
|
@ -1797,7 +1955,7 @@ enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
|
||||||
bytes_to_read = MIN(bytes_to_read,(size_t)(disk_buf_end-disk_buf_tail));
|
bytes_to_read = MIN(bytes_to_read,(size_t)(disk_buf_end-disk_buf_tail));
|
||||||
|
|
||||||
while (( bytes_to_read > 0) && (file_remaining > 0) &&
|
while (( bytes_to_read > 0) && (file_remaining > 0) &&
|
||||||
((audiostatus != STREAM_DONE) || (videostatus != STREAM_DONE))) {
|
((audio_str.status >= 0) || (video_str.status >= 0))) {
|
||||||
size_t n = rb->read(in_file, disk_buf_tail, MIN(32*1024,bytes_to_read));
|
size_t n = rb->read(in_file, disk_buf_tail, MIN(32*1024,bytes_to_read));
|
||||||
|
|
||||||
bytes_to_read -= n;
|
bytes_to_read -= n;
|
||||||
|
@ -1825,19 +1983,11 @@ enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Stop the threads and wait for them to terminate */
|
/* Stop the threads and wait for them to terminate */
|
||||||
if (videothread_id != NULL && videostatus != THREAD_TERMINATED)
|
if (video_str.thread != NULL)
|
||||||
{
|
str_send_msg(&video_str, STREAM_STOP, 0);
|
||||||
videostatus = PLEASE_STOP;
|
|
||||||
while (videostatus != THREAD_TERMINATED)
|
|
||||||
rb->yield();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (audiothread_id != NULL && audiostatus != THREAD_TERMINATED)
|
if (audio_str.thread != NULL)
|
||||||
{
|
str_send_msg(&audio_str, STREAM_STOP, 0);
|
||||||
audiostatus = PLEASE_STOP;
|
|
||||||
while (audiostatus != THREAD_TERMINATED)
|
|
||||||
rb->yield();
|
|
||||||
}
|
|
||||||
|
|
||||||
rb->sleep(HZ/10);
|
rb->sleep(HZ/10);
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue