2003-12-15 07:44:43 +00:00
|
|
|
|
/***************************************************************************
|
|
|
|
|
* __________ __ ___.
|
|
|
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
|
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
|
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
|
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
|
|
|
* \/ \/ \/ \/ \/
|
|
|
|
|
* $Id$
|
|
|
|
|
*
|
2004-01-27 07:21:29 +00:00
|
|
|
|
* Plugin for video playback
|
|
|
|
|
* Reads raw image data + audio data from a file
|
2004-02-05 23:45:46 +00:00
|
|
|
|
* !!!!!!!!!! Code Police free zone !!!!!!!!!!
|
2003-12-15 07:44:43 +00:00
|
|
|
|
*
|
2006-11-19 14:39:02 +00:00
|
|
|
|
* Copyright (C) 2003-2004 J<EFBFBD>g Hohensohn aka [IDC]Dragon
|
2003-12-15 07:44:43 +00:00
|
|
|
|
*
|
|
|
|
|
* All files in this archive are subject to the GNU General Public License.
|
|
|
|
|
* See the file COPYING in the source tree root for full license agreement.
|
|
|
|
|
*
|
|
|
|
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
|
|
|
* KIND, either express or implied.
|
|
|
|
|
*
|
|
|
|
|
****************************************************************************/
|
2004-02-05 23:45:46 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/****************** imports ******************/
|
|
|
|
|
|
2003-12-15 07:44:43 +00:00
|
|
|
|
#include "plugin.h"
|
2004-01-27 07:21:29 +00:00
|
|
|
|
#include "sh7034.h"
|
|
|
|
|
#include "system.h"
|
2007-08-15 12:42:09 +00:00
|
|
|
|
#include "helper.h"
|
2004-02-05 23:45:46 +00:00
|
|
|
|
|
2006-03-03 08:14:44 +00:00
|
|
|
|
#ifndef SIMULATOR /* not for simulator by now */
|
|
|
|
|
#ifdef HAVE_LCD_BITMAP /* and definitely not for the Player, haha */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
|
2006-01-15 18:20:18 +00:00
|
|
|
|
PLUGIN_HEADER
|
|
|
|
|
|
2004-10-12 22:49:51 +00:00
|
|
|
|
/* variable button definitions */
|
|
|
|
|
#if CONFIG_KEYPAD == RECORDER_PAD
|
|
|
|
|
#define VIDEO_STOP_SEEK BUTTON_PLAY
|
|
|
|
|
#define VIDEO_RESUME BUTTON_PLAY
|
2004-10-16 00:07:43 +00:00
|
|
|
|
#define VIDEO_DEBUG BUTTON_F1
|
|
|
|
|
#define VIDEO_CONTRAST_DOWN BUTTON_F2
|
|
|
|
|
#define VIDEO_CONTRAST_UP BUTTON_F3
|
|
|
|
|
|
2004-10-12 22:49:51 +00:00
|
|
|
|
#elif CONFIG_KEYPAD == ONDIO_PAD
|
2004-10-16 00:07:43 +00:00
|
|
|
|
#define VIDEO_STOP_SEEK_PRE BUTTON_MENU
|
|
|
|
|
#define VIDEO_STOP_SEEK (BUTTON_MENU | BUTTON_REL)
|
2004-10-12 22:49:51 +00:00
|
|
|
|
#define VIDEO_RESUME BUTTON_RIGHT
|
2004-10-16 00:07:43 +00:00
|
|
|
|
#define VIDEO_CONTRAST_DOWN (BUTTON_MENU | BUTTON_DOWN)
|
|
|
|
|
#define VIDEO_CONTRAST_UP (BUTTON_MENU | BUTTON_UP)
|
|
|
|
|
|
2004-10-12 22:49:51 +00:00
|
|
|
|
#endif
|
2004-02-05 23:45:46 +00:00
|
|
|
|
/****************** constants ******************/
|
|
|
|
|
|
2006-03-03 08:14:44 +00:00
|
|
|
|
#define SCREENSIZE (LCD_WIDTH*LCD_HEIGHT/8) /* in bytes */
|
|
|
|
|
#define FPS 68 /* default fps for headerless (old video-only) file */
|
|
|
|
|
#define MAX_ACC 20 /* maximum FF/FR speedup */
|
|
|
|
|
#define FF_TICKS 3000; /* experimentally found nice */
|
2003-12-15 07:44:43 +00:00
|
|
|
|
|
2006-03-03 08:14:44 +00:00
|
|
|
|
/* trigger levels, we need about 80 kB/sec */
|
|
|
|
|
#define SPINUP_INIT 5000 /* from what level on to refill, in milliseconds */
|
|
|
|
|
#define SPINUP_SAFETY 700 /* how much on top of the measured spinup time */
|
|
|
|
|
#define CHUNK (1024*32) /* read size */
|
2003-12-15 08:00:45 +00:00
|
|
|
|
|
2003-12-15 07:44:43 +00:00
|
|
|
|
|
2004-02-05 23:45:46 +00:00
|
|
|
|
/****************** prototypes ******************/
|
2006-03-03 08:14:44 +00:00
|
|
|
|
void timer4_isr(void); /* IMIA4 ISR */
|
|
|
|
|
int check_button(void); /* determine next relative frame */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/****************** data types ******************/
|
|
|
|
|
|
2006-03-03 08:14:44 +00:00
|
|
|
|
/* plugins don't introduce headers, so structs are repeated from rvf_format.h */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
|
2006-03-03 08:14:44 +00:00
|
|
|
|
#define HEADER_MAGIC 0x52564668 /* "RVFh" at file start */
|
|
|
|
|
#define AUDIO_MAGIC 0x41756446 /* "AudF" for each audio block */
|
|
|
|
|
#define FILEVERSION 100 /* 1.00 */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
|
2006-03-03 08:14:44 +00:00
|
|
|
|
/* format type definitions */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
#define VIDEOFORMAT_NO_VIDEO 0
|
|
|
|
|
#define VIDEOFORMAT_RAW 1
|
|
|
|
|
#define AUDIOFORMAT_NO_AUDIO 0
|
|
|
|
|
#define AUDIOFORMAT_MP3 1
|
|
|
|
|
#define AUDIOFORMAT_MP3_BITSWAPPED 2
|
|
|
|
|
|
2006-03-03 08:14:44 +00:00
|
|
|
|
/* bit flags */
|
|
|
|
|
#define FLAG_LOOP 0x00000001 /* loop the playback, e.g. for stills */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
|
2006-03-03 08:14:44 +00:00
|
|
|
|
typedef struct /* contains whatever might be useful to the player */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
{
|
2006-03-03 08:14:44 +00:00
|
|
|
|
/* general info (16 entries = 64 byte) */
|
|
|
|
|
unsigned long magic; /* HEADER_MAGIC */
|
|
|
|
|
unsigned long version; /* file version */
|
|
|
|
|
unsigned long flags; /* combination of FLAG_xx */
|
|
|
|
|
unsigned long blocksize; /* how many bytes per block (=video frame) */
|
|
|
|
|
unsigned long bps_average; /* bits per second of the whole stream */
|
|
|
|
|
unsigned long bps_peak; /* max. of above (audio may be VBR) */
|
|
|
|
|
unsigned long resume_pos; /* file position to resume to */
|
|
|
|
|
unsigned long reserved[9]; /* reserved, should be zero */
|
|
|
|
|
|
|
|
|
|
/* video info (16 entries = 64 byte) */
|
|
|
|
|
unsigned long video_format; /* one of VIDEOFORMAT_xxx */
|
|
|
|
|
unsigned long video_1st_frame; /* byte position of first video frame */
|
|
|
|
|
unsigned long video_duration; /* total length of video part, in ms */
|
|
|
|
|
unsigned long video_payload_size; /* total amount of video data, in bytes */
|
|
|
|
|
unsigned long video_bitrate; /* derived from resolution and frame time, in bps */
|
|
|
|
|
unsigned long video_frametime; /* frame interval in 11.0592 MHz clocks */
|
|
|
|
|
long video_preroll; /* video is how much ahead, in 11.0592 MHz clocks */
|
|
|
|
|
unsigned long video_width; /* in pixels */
|
|
|
|
|
unsigned long video_height; /* in pixels */
|
|
|
|
|
unsigned long video_reserved[7]; /* reserved, should be zero */
|
|
|
|
|
|
|
|
|
|
/* audio info (16 entries = 64 byte) */
|
|
|
|
|
unsigned long audio_format; /* one of AUDIOFORMAT_xxx */
|
|
|
|
|
unsigned long audio_1st_frame; /* byte position of first video frame */
|
|
|
|
|
unsigned long audio_duration; /* total length of audio part, in ms */
|
|
|
|
|
unsigned long audio_payload_size; /* total amount of audio data, in bytes */
|
|
|
|
|
unsigned long audio_avg_bitrate; /* average audio bitrate, in bits per second */
|
|
|
|
|
unsigned long audio_peak_bitrate; /* maximum bitrate */
|
|
|
|
|
unsigned long audio_headersize; /* offset to payload in audio frames */
|
|
|
|
|
long audio_min_associated; /* minimum offset to video frame, in bytes */
|
|
|
|
|
long audio_max_associated; /* maximum offset to video frame, in bytes */
|
|
|
|
|
unsigned long audio_reserved[7]; /* reserved, should be zero */
|
|
|
|
|
|
|
|
|
|
/* more to come... ? */
|
|
|
|
|
|
|
|
|
|
/* Note: padding up to 'blocksize' with zero following this header */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
} tFileHeader;
|
|
|
|
|
|
2006-03-03 08:14:44 +00:00
|
|
|
|
typedef struct /* the little header for all audio blocks */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
{
|
2006-03-03 08:14:44 +00:00
|
|
|
|
unsigned long magic; /* AUDIO_MAGIC indicates an audio block */
|
|
|
|
|
unsigned char previous_block; /* previous how many blocks backwards */
|
|
|
|
|
unsigned char next_block; /* next how many blocks forward */
|
|
|
|
|
short associated_video; /* offset to block with corresponding video */
|
|
|
|
|
unsigned short frame_start; /* offset to first frame starting in this block */
|
|
|
|
|
unsigned short frame_end; /* offset to behind last frame ending in this block */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
} tAudioFrameHeader;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/****************** globals ******************/
|
2003-12-18 22:36:35 +00:00
|
|
|
|
|
2003-12-15 07:44:43 +00:00
|
|
|
|
static struct plugin_api* rb; /* here is a global api struct pointer */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
static char gPrint[32]; /* a global printf buffer, saves stack */
|
|
|
|
|
|
2003-12-15 07:44:43 +00:00
|
|
|
|
|
2006-03-03 08:14:44 +00:00
|
|
|
|
/* playstate */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
static struct
|
|
|
|
|
{
|
|
|
|
|
enum
|
|
|
|
|
{
|
|
|
|
|
paused,
|
2004-02-07 16:53:29 +00:00
|
|
|
|
playing,
|
2004-02-05 23:45:46 +00:00
|
|
|
|
} state;
|
|
|
|
|
bool bAudioUnderrun;
|
|
|
|
|
bool bVideoUnderrun;
|
|
|
|
|
bool bHasAudio;
|
|
|
|
|
bool bHasVideo;
|
2006-03-03 08:14:44 +00:00
|
|
|
|
int nTimeOSD; /* OSD should stay for this many frames */
|
|
|
|
|
bool bDirtyOSD; /* OSD needs redraw */
|
|
|
|
|
bool bRefilling; /* set if refilling buffer */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
bool bSeeking;
|
2006-03-03 08:14:44 +00:00
|
|
|
|
int nSeekAcc; /* accelleration value for seek */
|
|
|
|
|
int nSeekPos; /* current file position for seek */
|
|
|
|
|
bool bDiskSleep; /* disk is suspended */
|
2004-10-13 23:59:15 +00:00
|
|
|
|
#if FREQ == 12000000 /* Ondio speed kludge */
|
|
|
|
|
int nFrameTimeAdjusted;
|
|
|
|
|
#endif
|
2004-02-05 23:45:46 +00:00
|
|
|
|
} gPlay;
|
|
|
|
|
|
2006-03-03 08:14:44 +00:00
|
|
|
|
/* buffer information */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
static struct
|
|
|
|
|
{
|
2007-04-21 19:07:15 +00:00
|
|
|
|
ssize_t bufsize;
|
2006-03-03 08:14:44 +00:00
|
|
|
|
int granularity; /* common multiple of block and sector size */
|
|
|
|
|
unsigned char* pBufStart; /* start of ring buffer */
|
|
|
|
|
unsigned char* pBufEnd; /* end of ring buffer */
|
|
|
|
|
unsigned char* pOSD; /* OSD memory (112 bytes for 112*8 pixels) */
|
|
|
|
|
|
|
|
|
|
int vidcount; /* how many video blocks are known in a row */
|
|
|
|
|
unsigned char* pBufFill; /* write pointer for disk, owned by main task */
|
|
|
|
|
unsigned char* pReadVideo; /* video readout, maintained by timer ISR */
|
|
|
|
|
unsigned char* pReadAudio; /* audio readout, maintained by demand ISR */
|
|
|
|
|
bool bEOF; /* flag for end of file */
|
|
|
|
|
int low_water; /* reload threshold */
|
|
|
|
|
int high_water; /* end of reload threshold */
|
|
|
|
|
int spinup_safety; /* safety margin when recalculating low_water */
|
|
|
|
|
int nReadChunk; /* how much data for normal buffer fill */
|
|
|
|
|
int nSeekChunk; /* how much data while seeking */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
} gBuf;
|
|
|
|
|
|
2006-03-03 08:14:44 +00:00
|
|
|
|
/* statistics */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
static struct
|
2003-12-18 22:36:35 +00:00
|
|
|
|
{
|
2004-02-05 23:45:46 +00:00
|
|
|
|
int minAudioAvail;
|
|
|
|
|
int minVideoAvail;
|
|
|
|
|
int nAudioUnderruns;
|
|
|
|
|
int nVideoUnderruns;
|
2004-05-09 18:18:05 +00:00
|
|
|
|
long minSpinup;
|
|
|
|
|
long maxSpinup;
|
2004-02-05 23:45:46 +00:00
|
|
|
|
} gStats;
|
2003-12-18 22:36:35 +00:00
|
|
|
|
|
2006-03-03 08:14:44 +00:00
|
|
|
|
tFileHeader gFileHdr; /* file header */
|
2003-12-18 22:36:35 +00:00
|
|
|
|
|
2004-02-05 23:45:46 +00:00
|
|
|
|
/****************** implementation ******************/
|
2003-12-18 22:36:35 +00:00
|
|
|
|
|
2006-03-03 08:14:44 +00:00
|
|
|
|
/* tool function: return how much playable audio/video is left */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
int Available(unsigned char* pSnapshot)
|
2003-12-18 22:36:35 +00:00
|
|
|
|
{
|
2004-02-05 23:45:46 +00:00
|
|
|
|
if (pSnapshot <= gBuf.pBufFill)
|
|
|
|
|
return gBuf.pBufFill - pSnapshot;
|
|
|
|
|
else
|
|
|
|
|
return gBuf.bufsize - (pSnapshot - gBuf.pBufFill);
|
|
|
|
|
}
|
2003-12-18 22:36:35 +00:00
|
|
|
|
|
2006-03-03 08:14:44 +00:00
|
|
|
|
/* debug function to draw buffer indicators */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
void DrawBuf(void)
|
|
|
|
|
{
|
|
|
|
|
int fill, video, audio;
|
|
|
|
|
|
2006-03-03 08:14:44 +00:00
|
|
|
|
rb->memset(gBuf.pOSD, 0x10, LCD_WIDTH); /* draw line */
|
|
|
|
|
gBuf.pOSD[0] = gBuf.pOSD[LCD_WIDTH-1] = 0xFE; /* ends */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
|
2006-03-03 08:14:44 +00:00
|
|
|
|
/* calculate new tick positions */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
fill = 1 + ((gBuf.pBufFill - gBuf.pBufStart) * (LCD_WIDTH-2)) / gBuf.bufsize;
|
|
|
|
|
video = 1 + ((gBuf.pReadVideo - gBuf.pBufStart) * (LCD_WIDTH-2)) / gBuf.bufsize;
|
|
|
|
|
audio = 1 + ((gBuf.pReadAudio - gBuf.pBufStart) * (LCD_WIDTH-2)) / gBuf.bufsize;
|
|
|
|
|
|
2006-03-03 08:14:44 +00:00
|
|
|
|
gBuf.pOSD[fill] |= 0x20; /* below the line, two pixels */
|
|
|
|
|
gBuf.pOSD[video] |= 0x08; /* one above */
|
|
|
|
|
gBuf.pOSD[audio] |= 0x04; /* two above */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
|
2006-03-03 08:14:44 +00:00
|
|
|
|
if (gPlay.state == paused) /* we have to draw ourselves */
|
2004-02-07 16:53:29 +00:00
|
|
|
|
rb->lcd_update_rect(0, LCD_HEIGHT-8, LCD_WIDTH, 8);
|
|
|
|
|
else
|
2006-03-03 08:14:44 +00:00
|
|
|
|
gPlay.bDirtyOSD = true; /* redraw it with next timer IRQ */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2006-03-03 08:14:44 +00:00
|
|
|
|
/* helper function to draw a position indicator */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
void DrawPosition(int pos, int total)
|
|
|
|
|
{
|
|
|
|
|
int w,h;
|
2006-03-03 08:14:44 +00:00
|
|
|
|
int sec; /* estimated seconds */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* print the estimated position */
|
|
|
|
|
sec = pos / (gFileHdr.bps_average/8);
|
|
|
|
|
if (sec < 100*60) /* fits into mm:ss format */
|
|
|
|
|
rb->snprintf(gPrint, sizeof(gPrint), "%02d:%02dm", sec/60, sec%60);
|
|
|
|
|
else /* a very long clip, hh:mm format */
|
|
|
|
|
rb->snprintf(gPrint, sizeof(gPrint), "%02d:%02dh", sec/3600, (sec/60)%60);
|
|
|
|
|
rb->lcd_puts(0, 7, gPrint);
|
|
|
|
|
|
|
|
|
|
/* draw a slider over the rest of the line */
|
|
|
|
|
rb->lcd_getstringsize(gPrint, &w, &h);
|
|
|
|
|
w++;
|
2006-11-19 14:39:02 +00:00
|
|
|
|
rb->gui_scrollbar_draw(rb->screens[SCREEN_MAIN],w, LCD_HEIGHT-7, LCD_WIDTH-w,
|
2006-11-19 14:11:42 +00:00
|
|
|
|
7, total, 0, pos, HORIZONTAL);
|
2004-02-05 23:45:46 +00:00
|
|
|
|
|
2006-03-03 08:14:44 +00:00
|
|
|
|
if (gPlay.state == paused) /* we have to draw ourselves */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
rb->lcd_update_rect(0, LCD_HEIGHT-8, LCD_WIDTH, 8);
|
2006-03-03 08:14:44 +00:00
|
|
|
|
else /* let the display time do it */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
{
|
|
|
|
|
gPlay.nTimeOSD = 70;
|
2006-03-03 08:14:44 +00:00
|
|
|
|
gPlay.bDirtyOSD = true; /* redraw it with next timer IRQ */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2006-03-03 08:14:44 +00:00
|
|
|
|
/* helper function to change the volume by a certain amount, +/- */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
void ChangeVolume(int delta)
|
|
|
|
|
{
|
2005-12-17 21:13:30 +00:00
|
|
|
|
int minvol = rb->sound_min(SOUND_VOLUME);
|
|
|
|
|
int maxvol = rb->sound_max(SOUND_VOLUME);
|
2004-02-05 23:45:46 +00:00
|
|
|
|
int vol = rb->global_settings->volume + delta;
|
|
|
|
|
|
2005-12-17 21:13:30 +00:00
|
|
|
|
if (vol > maxvol) vol = maxvol;
|
|
|
|
|
else if (vol < minvol) vol = minvol;
|
2004-02-05 23:45:46 +00:00
|
|
|
|
if (vol != rb->global_settings->volume)
|
|
|
|
|
{
|
2005-04-01 13:41:03 +00:00
|
|
|
|
rb->sound_set(SOUND_VOLUME, vol);
|
2004-02-05 23:45:46 +00:00
|
|
|
|
rb->global_settings->volume = vol;
|
2005-12-17 21:13:30 +00:00
|
|
|
|
rb->snprintf(gPrint, sizeof(gPrint), "Vol: %d dB", vol);
|
2004-02-05 23:45:46 +00:00
|
|
|
|
rb->lcd_puts(0, 7, gPrint);
|
2006-03-03 08:14:44 +00:00
|
|
|
|
if (gPlay.state == paused) /* we have to draw ourselves */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
rb->lcd_update_rect(0, LCD_HEIGHT-8, LCD_WIDTH, 8);
|
2006-03-03 08:14:44 +00:00
|
|
|
|
else /* let the display time do it */
|
2003-12-18 22:36:35 +00:00
|
|
|
|
{
|
2006-03-03 08:14:44 +00:00
|
|
|
|
gPlay.nTimeOSD = 50; /* display it for 50 frames */
|
|
|
|
|
gPlay.bDirtyOSD = true; /* let the refresh copy it to LCD */
|
2003-12-18 22:36:35 +00:00
|
|
|
|
}
|
2004-02-05 23:45:46 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2003-12-18 22:36:35 +00:00
|
|
|
|
|
|
|
|
|
|
2006-03-03 08:14:44 +00:00
|
|
|
|
/* helper function to change the LCD contrast by a certain amount, +/- */
|
2004-04-30 21:38:24 +00:00
|
|
|
|
void ChangeContrast(int delta)
|
|
|
|
|
{
|
|
|
|
|
static int mycontrast = -1; /* the "permanent" value while running */
|
|
|
|
|
int contrast; /* updated value */
|
|
|
|
|
|
|
|
|
|
if (mycontrast == -1)
|
|
|
|
|
mycontrast = rb->global_settings->contrast;
|
|
|
|
|
|
|
|
|
|
contrast = mycontrast + delta;
|
|
|
|
|
if (contrast > 63) contrast = 63;
|
|
|
|
|
else if (contrast < 5) contrast = 5;
|
|
|
|
|
if (contrast != mycontrast)
|
|
|
|
|
{
|
|
|
|
|
rb->lcd_set_contrast(contrast);
|
|
|
|
|
mycontrast = contrast;
|
|
|
|
|
rb->snprintf(gPrint, sizeof(gPrint), "Contrast: %d", contrast);
|
|
|
|
|
rb->lcd_puts(0, 7, gPrint);
|
2006-03-03 08:14:44 +00:00
|
|
|
|
if (gPlay.state == paused) /* we have to draw ourselves */
|
2004-04-30 21:38:24 +00:00
|
|
|
|
rb->lcd_update_rect(0, LCD_HEIGHT-8, LCD_WIDTH, 8);
|
2006-03-03 08:14:44 +00:00
|
|
|
|
else /* let the display time do it */
|
2004-04-30 21:38:24 +00:00
|
|
|
|
{
|
2006-03-03 08:14:44 +00:00
|
|
|
|
gPlay.nTimeOSD = 50; /* display it for 50 frames */
|
|
|
|
|
gPlay.bDirtyOSD = true; /* let the refresh copy it to LCD */
|
2004-04-30 21:38:24 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2006-03-03 08:14:44 +00:00
|
|
|
|
/* sync the video to the current audio */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
void SyncVideo(void)
|
|
|
|
|
{
|
|
|
|
|
tAudioFrameHeader* pAudioBuf;
|
2003-12-18 22:36:35 +00:00
|
|
|
|
|
2004-02-05 23:45:46 +00:00
|
|
|
|
pAudioBuf = (tAudioFrameHeader*)(gBuf.pReadAudio);
|
|
|
|
|
if (pAudioBuf->magic == AUDIO_MAGIC)
|
|
|
|
|
{
|
2006-03-03 08:14:44 +00:00
|
|
|
|
gBuf.vidcount = 0; /* nothing known */
|
|
|
|
|
/* sync the video position */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
gBuf.pReadVideo = gBuf.pReadAudio +
|
|
|
|
|
(long)pAudioBuf->associated_video * (long)gFileHdr.blocksize;
|
|
|
|
|
|
2006-03-03 08:14:44 +00:00
|
|
|
|
/* handle possible wrap */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
if (gBuf.pReadVideo >= gBuf.pBufEnd)
|
|
|
|
|
gBuf.pReadVideo -= gBuf.bufsize;
|
|
|
|
|
else if (gBuf.pReadVideo < gBuf.pBufStart)
|
|
|
|
|
gBuf.pReadVideo += gBuf.bufsize;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2006-03-03 08:14:44 +00:00
|
|
|
|
/* timer interrupt handler to display a frame */
|
2004-04-30 20:49:00 +00:00
|
|
|
|
void timer4_isr(void)
|
2004-02-05 23:45:46 +00:00
|
|
|
|
{
|
|
|
|
|
int available;
|
|
|
|
|
tAudioFrameHeader* pAudioBuf;
|
2006-03-03 08:14:44 +00:00
|
|
|
|
int height; /* height to display */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
|
2006-03-03 08:14:44 +00:00
|
|
|
|
/* reduce height if we have OSD on */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
height = gFileHdr.video_height/8;
|
|
|
|
|
if (gPlay.nTimeOSD > 0)
|
|
|
|
|
{
|
|
|
|
|
gPlay.nTimeOSD--;
|
2006-03-03 08:14:44 +00:00
|
|
|
|
height = MIN(LCD_HEIGHT/8-1, height); /* reserve bottom line */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
if (gPlay.bDirtyOSD)
|
2006-03-03 08:14:44 +00:00
|
|
|
|
{ /* OSD to bottom line */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
rb->lcd_blit(gBuf.pOSD, 0, LCD_HEIGHT/8-1,
|
|
|
|
|
LCD_WIDTH, 1, LCD_WIDTH);
|
|
|
|
|
gPlay.bDirtyOSD = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rb->lcd_blit(gBuf.pReadVideo, 0, 0,
|
|
|
|
|
gFileHdr.video_width, height, gFileHdr.video_width);
|
|
|
|
|
|
|
|
|
|
available = Available(gBuf.pReadVideo);
|
|
|
|
|
|
2006-03-03 08:14:44 +00:00
|
|
|
|
/* loop to skip audio frame(s) */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
while(1)
|
|
|
|
|
{
|
2006-03-03 08:14:44 +00:00
|
|
|
|
/* just for the statistics */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
if (!gBuf.bEOF && available < gStats.minVideoAvail)
|
|
|
|
|
gStats.minVideoAvail = available;
|
|
|
|
|
|
2004-03-08 08:29:11 +00:00
|
|
|
|
if (available <= (int)gFileHdr.blocksize)
|
2006-03-03 08:14:44 +00:00
|
|
|
|
{ /* no data for next frame */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
|
|
|
|
|
if (gBuf.bEOF && (gFileHdr.flags & FLAG_LOOP))
|
2006-03-03 08:14:44 +00:00
|
|
|
|
{ /* loop now, assuming the looped clip fits in memory */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
gBuf.pReadVideo = gBuf.pBufStart + gFileHdr.video_1st_frame;
|
2006-03-03 08:14:44 +00:00
|
|
|
|
/* FixMe: pReadVideo is incremented below */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
2004-01-15 07:48:02 +00:00
|
|
|
|
{
|
2004-02-05 23:45:46 +00:00
|
|
|
|
gPlay.bVideoUnderrun = true;
|
2006-03-03 08:14:44 +00:00
|
|
|
|
rb->timer_unregister(); /* disable ourselves */
|
|
|
|
|
return; /* no data available */
|
2004-01-15 07:48:02 +00:00
|
|
|
|
}
|
2004-02-05 23:45:46 +00:00
|
|
|
|
}
|
2006-03-03 08:14:44 +00:00
|
|
|
|
else /* normal advance for next time */
|
2004-03-08 20:14:04 +00:00
|
|
|
|
{
|
|
|
|
|
gBuf.pReadVideo += gFileHdr.blocksize;
|
|
|
|
|
if (gBuf.pReadVideo >= gBuf.pBufEnd)
|
2006-03-03 08:14:44 +00:00
|
|
|
|
gBuf.pReadVideo -= gBuf.bufsize; /* wraparound */
|
2004-03-08 20:14:04 +00:00
|
|
|
|
available -= gFileHdr.blocksize;
|
|
|
|
|
}
|
2003-12-18 22:36:35 +00:00
|
|
|
|
|
2004-02-05 23:45:46 +00:00
|
|
|
|
if (!gPlay.bHasAudio)
|
2006-03-03 08:14:44 +00:00
|
|
|
|
break; /* no need to skip any audio */
|
2003-12-18 22:36:35 +00:00
|
|
|
|
|
2004-02-05 23:45:46 +00:00
|
|
|
|
if (gBuf.vidcount)
|
|
|
|
|
{
|
2006-03-03 08:14:44 +00:00
|
|
|
|
/* we know the next is a video frame */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
gBuf.vidcount--;
|
2006-03-03 08:14:44 +00:00
|
|
|
|
break; /* exit the loop */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pAudioBuf = (tAudioFrameHeader*)(gBuf.pReadVideo);
|
|
|
|
|
if (pAudioBuf->magic == AUDIO_MAGIC)
|
2006-03-03 08:14:44 +00:00
|
|
|
|
{ /* we ran into audio, can happen after seek */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
gBuf.vidcount = pAudioBuf->next_block;
|
|
|
|
|
if (gBuf.vidcount)
|
2006-03-03 08:14:44 +00:00
|
|
|
|
gBuf.vidcount--; /* minus the audio block */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
}
|
2006-03-03 08:14:44 +00:00
|
|
|
|
} /* while */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
}
|
2003-12-18 22:36:35 +00:00
|
|
|
|
|
|
|
|
|
|
2006-03-03 08:14:44 +00:00
|
|
|
|
/* ISR function to get more mp3 data */
|
2007-06-13 15:35:07 +00:00
|
|
|
|
void GetMoreMp3(unsigned char** start, size_t* size)
|
2004-02-05 23:45:46 +00:00
|
|
|
|
{
|
|
|
|
|
int available;
|
|
|
|
|
int advance;
|
2003-12-18 22:36:35 +00:00
|
|
|
|
|
2004-02-05 23:45:46 +00:00
|
|
|
|
tAudioFrameHeader* pAudioBuf = (tAudioFrameHeader*)(gBuf.pReadAudio);
|
|
|
|
|
|
|
|
|
|
advance = pAudioBuf->next_block * gFileHdr.blocksize;
|
|
|
|
|
|
|
|
|
|
available = Available(gBuf.pReadAudio);
|
|
|
|
|
|
2006-03-03 08:14:44 +00:00
|
|
|
|
/* just for the statistics */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
if (!gBuf.bEOF && available < gStats.minAudioAvail)
|
|
|
|
|
gStats.minAudioAvail = available;
|
|
|
|
|
|
2004-03-08 20:14:04 +00:00
|
|
|
|
if (available < advance + (int)gFileHdr.blocksize || advance == 0)
|
2004-02-05 23:45:46 +00:00
|
|
|
|
{
|
|
|
|
|
gPlay.bAudioUnderrun = true;
|
2006-03-03 08:14:44 +00:00
|
|
|
|
return; /* no data available */
|
2003-12-18 22:36:35 +00:00
|
|
|
|
}
|
|
|
|
|
|
2004-02-05 23:45:46 +00:00
|
|
|
|
gBuf.pReadAudio += advance;
|
|
|
|
|
if (gBuf.pReadAudio >= gBuf.pBufEnd)
|
2006-03-03 08:14:44 +00:00
|
|
|
|
gBuf.pReadAudio -= gBuf.bufsize; /* wraparound */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
|
|
|
|
|
*start = gBuf.pReadAudio + gFileHdr.audio_headersize;
|
|
|
|
|
*size = gFileHdr.blocksize - gFileHdr.audio_headersize;
|
2003-12-18 22:36:35 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2003-12-15 07:44:43 +00:00
|
|
|
|
int WaitForButton(void)
|
|
|
|
|
{
|
|
|
|
|
int button;
|
|
|
|
|
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
button = rb->button_get(true);
|
2004-10-12 22:49:51 +00:00
|
|
|
|
rb->default_event_handler(button);
|
2003-12-15 07:44:43 +00:00
|
|
|
|
} while ((button & BUTTON_REL) && button != SYS_USB_CONNECTED);
|
|
|
|
|
|
|
|
|
|
return button;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2004-02-07 16:53:29 +00:00
|
|
|
|
bool WantResume(int fd)
|
|
|
|
|
{
|
|
|
|
|
int button;
|
|
|
|
|
|
|
|
|
|
rb->lcd_puts(0, 0, "Resume to this");
|
|
|
|
|
rb->lcd_puts(0, 1, "last position?");
|
|
|
|
|
rb->lcd_puts(0, 2, "PLAY = yes");
|
|
|
|
|
rb->lcd_puts(0, 3, "Any Other = no");
|
|
|
|
|
rb->lcd_puts(0, 4, " (plays from start)");
|
|
|
|
|
DrawPosition(gFileHdr.resume_pos, rb->filesize(fd));
|
|
|
|
|
rb->lcd_update();
|
|
|
|
|
|
|
|
|
|
button = WaitForButton();
|
2004-10-12 22:49:51 +00:00
|
|
|
|
return (button == VIDEO_RESUME);
|
2004-02-07 16:53:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2004-02-05 23:45:46 +00:00
|
|
|
|
int SeekTo(int fd, int nPos)
|
2003-12-15 07:44:43 +00:00
|
|
|
|
{
|
2004-02-05 23:45:46 +00:00
|
|
|
|
int read_now, got_now;
|
2003-12-15 07:44:43 +00:00
|
|
|
|
|
2004-02-05 23:45:46 +00:00
|
|
|
|
if (gPlay.bHasAudio)
|
2006-03-03 08:14:44 +00:00
|
|
|
|
rb->mp3_play_stop(); /* stop audio ISR */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
if (gPlay.bHasVideo)
|
2006-03-03 08:14:44 +00:00
|
|
|
|
rb->timer_unregister(); /* stop the timer */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
|
|
|
|
|
rb->lseek(fd, nPos, SEEK_SET);
|
2003-12-15 07:44:43 +00:00
|
|
|
|
|
2006-03-03 08:14:44 +00:00
|
|
|
|
gBuf.pBufFill = gBuf.pBufStart; /* all empty */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
gBuf.pReadVideo = gBuf.pReadAudio = gBuf.pBufStart;
|
2003-12-15 07:44:43 +00:00
|
|
|
|
|
2006-03-03 08:14:44 +00:00
|
|
|
|
read_now = gBuf.low_water - 1; /* less than low water, so loading will continue */
|
|
|
|
|
read_now -= read_now % gBuf.granularity; /* round down to granularity */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
got_now = rb->read(fd, gBuf.pBufFill, read_now);
|
|
|
|
|
gBuf.bEOF = (read_now != got_now);
|
|
|
|
|
gBuf.pBufFill += got_now;
|
2003-12-15 07:44:43 +00:00
|
|
|
|
|
2004-02-05 23:45:46 +00:00
|
|
|
|
if (nPos == 0)
|
2006-03-03 08:14:44 +00:00
|
|
|
|
{ /* we seeked to the start */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
if (gPlay.bHasVideo)
|
|
|
|
|
gBuf.pReadVideo += gFileHdr.video_1st_frame;
|
|
|
|
|
|
|
|
|
|
if (gPlay.bHasAudio)
|
|
|
|
|
gBuf.pReadAudio += gFileHdr.audio_1st_frame;
|
|
|
|
|
}
|
|
|
|
|
else
|
2006-03-03 08:14:44 +00:00
|
|
|
|
{ /* we have to search for the positions */
|
|
|
|
|
if (gPlay.bHasAudio) /* prepare audio playback, if contained */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
{
|
2006-03-03 08:14:44 +00:00
|
|
|
|
/* search for audio frame */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
while (((tAudioFrameHeader*)(gBuf.pReadAudio))->magic != AUDIO_MAGIC)
|
|
|
|
|
gBuf.pReadAudio += gFileHdr.blocksize;
|
|
|
|
|
|
|
|
|
|
if (gPlay.bHasVideo)
|
2006-03-03 08:14:44 +00:00
|
|
|
|
SyncVideo(); /* pick the right video for that */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2006-03-03 08:14:44 +00:00
|
|
|
|
/* synchronous start */
|
2004-02-07 16:53:29 +00:00
|
|
|
|
gPlay.state = playing;
|
2004-02-05 23:45:46 +00:00
|
|
|
|
if (gPlay.bHasAudio)
|
|
|
|
|
{
|
|
|
|
|
gPlay.bAudioUnderrun = false;
|
2004-02-07 16:53:29 +00:00
|
|
|
|
rb->mp3_play_data(gBuf.pReadAudio + gFileHdr.audio_headersize,
|
|
|
|
|
gFileHdr.blocksize - gFileHdr.audio_headersize, GetMoreMp3);
|
2006-03-03 08:14:44 +00:00
|
|
|
|
rb->mp3_play_pause(true); /* kickoff audio */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
}
|
|
|
|
|
if (gPlay.bHasVideo)
|
|
|
|
|
{
|
|
|
|
|
gPlay.bVideoUnderrun = false;
|
2006-03-03 08:14:44 +00:00
|
|
|
|
/* start display interrupt */
|
2004-10-13 23:59:15 +00:00
|
|
|
|
#if FREQ == 12000000 /* Ondio speed kludge */
|
2005-07-26 20:01:11 +00:00
|
|
|
|
rb->timer_register(1, NULL, gPlay.nFrameTimeAdjusted, 1, timer4_isr);
|
2004-10-13 23:59:15 +00:00
|
|
|
|
#else
|
2005-07-26 20:01:11 +00:00
|
|
|
|
rb->timer_register(1, NULL, gFileHdr.video_frametime, 1, timer4_isr);
|
2004-10-13 23:59:15 +00:00
|
|
|
|
#endif
|
2004-02-05 23:45:46 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
2003-12-15 07:44:43 +00:00
|
|
|
|
}
|
|
|
|
|
|
2006-03-03 08:14:44 +00:00
|
|
|
|
/* called from default_event_handler_ex() or at end of playback */
|
2004-10-12 22:49:51 +00:00
|
|
|
|
void Cleanup(void *fd)
|
|
|
|
|
{
|
2006-03-03 08:14:44 +00:00
|
|
|
|
rb->close(*(int*)fd); /* close the file */
|
2004-10-12 22:49:51 +00:00
|
|
|
|
|
|
|
|
|
if (gPlay.bHasVideo)
|
2006-03-03 08:14:44 +00:00
|
|
|
|
rb->timer_unregister(); /* stop video ISR, now I can use the display again */
|
2004-10-12 22:49:51 +00:00
|
|
|
|
|
|
|
|
|
if (gPlay.bHasAudio)
|
2006-03-03 08:14:44 +00:00
|
|
|
|
rb->mp3_play_stop(); /* stop audio ISR */
|
2004-10-12 22:49:51 +00:00
|
|
|
|
|
2007-08-15 12:42:09 +00:00
|
|
|
|
/* Turn on backlight timeout (revert to settings) */
|
2007-08-16 23:01:18 +00:00
|
|
|
|
backlight_use_settings(rb); /* backlight control in lib/helper.c */
|
2004-10-12 22:49:51 +00:00
|
|
|
|
|
2006-03-03 08:14:44 +00:00
|
|
|
|
/* restore normal contrast */
|
2004-10-12 22:49:51 +00:00
|
|
|
|
rb->lcd_set_contrast(rb->global_settings->contrast);
|
|
|
|
|
}
|
2003-12-15 07:44:43 +00:00
|
|
|
|
|
2006-03-03 08:14:44 +00:00
|
|
|
|
/* returns >0 if continue, =0 to stop, <0 to abort (USB) */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
int PlayTick(int fd)
|
2003-12-15 07:44:43 +00:00
|
|
|
|
{
|
2004-02-05 23:45:46 +00:00
|
|
|
|
int button;
|
2004-10-16 00:07:43 +00:00
|
|
|
|
static int lastbutton = 0;
|
2004-02-05 23:45:46 +00:00
|
|
|
|
int avail_audio = -1, avail_video = -1;
|
|
|
|
|
int retval = 1;
|
|
|
|
|
int filepos;
|
|
|
|
|
|
2006-03-03 08:14:44 +00:00
|
|
|
|
/* check buffer level */
|
2004-02-06 21:19:05 +00:00
|
|
|
|
|
2004-02-05 23:45:46 +00:00
|
|
|
|
if (gPlay.bHasAudio)
|
|
|
|
|
avail_audio = Available(gBuf.pReadAudio);
|
|
|
|
|
if (gPlay.bHasVideo)
|
|
|
|
|
avail_video = Available(gBuf.pReadVideo);
|
|
|
|
|
|
|
|
|
|
if ((gPlay.bHasAudio && avail_audio < gBuf.low_water)
|
|
|
|
|
|| (gPlay.bHasVideo && avail_video < gBuf.low_water))
|
|
|
|
|
{
|
|
|
|
|
gPlay.bRefilling = true; /* go to refill mode */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((!gPlay.bHasAudio || gPlay.bAudioUnderrun)
|
2004-02-06 21:19:05 +00:00
|
|
|
|
&& (!gPlay.bHasVideo || gPlay.bVideoUnderrun)
|
|
|
|
|
&& gBuf.bEOF)
|
2004-02-07 16:53:29 +00:00
|
|
|
|
{
|
|
|
|
|
if (gFileHdr.resume_pos)
|
2006-03-03 08:14:44 +00:00
|
|
|
|
{ /* we played til the end, clear resume position */
|
2004-02-07 16:53:29 +00:00
|
|
|
|
gFileHdr.resume_pos = 0;
|
2006-03-03 08:14:44 +00:00
|
|
|
|
rb->lseek(fd, 0, SEEK_SET); /* save resume position */
|
2004-02-07 16:53:29 +00:00
|
|
|
|
rb->write(fd, &gFileHdr, sizeof(gFileHdr));
|
|
|
|
|
}
|
2005-01-16 00:10:57 +00:00
|
|
|
|
Cleanup(&fd);
|
2006-03-03 08:14:44 +00:00
|
|
|
|
return 0; /* all expired */
|
2004-02-07 16:53:29 +00:00
|
|
|
|
}
|
2004-02-05 23:45:46 +00:00
|
|
|
|
|
|
|
|
|
if (!gPlay.bRefilling || gBuf.bEOF)
|
2006-03-03 08:14:44 +00:00
|
|
|
|
{ /* nothing to do */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
button = rb->button_get_w_tmo(HZ/10);
|
|
|
|
|
}
|
|
|
|
|
else
|
2006-03-03 08:14:44 +00:00
|
|
|
|
{ /* refill buffer */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
int read_now, got_now;
|
|
|
|
|
int buf_free;
|
2006-03-03 08:14:44 +00:00
|
|
|
|
long spinup; /* measure the spinup time */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
|
2006-03-03 08:14:44 +00:00
|
|
|
|
/* how much can we reload, don't fill completely, would appear empty */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
buf_free = gBuf.bufsize - MAX(avail_audio, avail_video) - gBuf.high_water;
|
|
|
|
|
if (buf_free < 0)
|
2006-03-03 08:14:44 +00:00
|
|
|
|
buf_free = 0; /* just for safety */
|
|
|
|
|
buf_free -= buf_free % gBuf.granularity; /* round down to granularity */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
|
2006-03-03 08:14:44 +00:00
|
|
|
|
/* in one piece max. up to buffer end (wrap after that) */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
read_now = MIN(buf_free, gBuf.pBufEnd - gBuf.pBufFill);
|
|
|
|
|
|
2006-03-03 08:14:44 +00:00
|
|
|
|
/* load piecewise, to stay responsive */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
read_now = MIN(read_now, gBuf.nReadChunk);
|
|
|
|
|
|
|
|
|
|
if (read_now == buf_free)
|
2006-03-03 08:14:44 +00:00
|
|
|
|
gPlay.bRefilling = false; /* last piece requested */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
|
2006-03-03 08:14:44 +00:00
|
|
|
|
spinup = *rb->current_tick; /* in case this is interesting below */
|
2004-05-09 18:18:05 +00:00
|
|
|
|
|
2004-02-05 23:45:46 +00:00
|
|
|
|
got_now = rb->read(fd, gBuf.pBufFill, read_now);
|
|
|
|
|
if (got_now != read_now || read_now == 0)
|
2004-01-15 07:48:02 +00:00
|
|
|
|
{
|
2004-02-05 23:45:46 +00:00
|
|
|
|
gBuf.bEOF = true;
|
|
|
|
|
gPlay.bRefilling = false;
|
2004-01-15 07:48:02 +00:00
|
|
|
|
}
|
2003-12-15 07:44:43 +00:00
|
|
|
|
|
2006-03-03 08:14:44 +00:00
|
|
|
|
if (gPlay.bDiskSleep) /* statistics about the spinup time */
|
2004-05-09 18:18:05 +00:00
|
|
|
|
{
|
|
|
|
|
spinup = *rb->current_tick - spinup;
|
|
|
|
|
gPlay.bDiskSleep = false;
|
|
|
|
|
if (spinup > gStats.maxSpinup)
|
|
|
|
|
gStats.maxSpinup = spinup;
|
|
|
|
|
if (spinup < gStats.minSpinup)
|
|
|
|
|
gStats.minSpinup = spinup;
|
|
|
|
|
|
2006-03-03 08:14:44 +00:00
|
|
|
|
/* recalculate the low water mark from real measurements */
|
2004-05-09 18:18:05 +00:00
|
|
|
|
gBuf.low_water = (gStats.maxSpinup + gBuf.spinup_safety)
|
|
|
|
|
* gFileHdr.bps_peak / 8 / HZ;
|
|
|
|
|
}
|
|
|
|
|
|
2007-08-01 08:50:44 +00:00
|
|
|
|
if (!gPlay.bRefilling
|
|
|
|
|
#ifndef HAVE_FLASH_STORAGE
|
|
|
|
|
&& rb->global_settings->disk_spindown < 20 /* condition for test only */
|
|
|
|
|
#endif
|
|
|
|
|
)
|
2004-02-21 22:05:37 +00:00
|
|
|
|
{
|
2006-03-03 08:14:44 +00:00
|
|
|
|
rb->ata_sleep(); /* no point in leaving the disk run til timeout */
|
2004-05-09 18:18:05 +00:00
|
|
|
|
gPlay.bDiskSleep = true;
|
2004-02-21 22:05:37 +00:00
|
|
|
|
}
|
2003-12-15 07:44:43 +00:00
|
|
|
|
|
2004-02-05 23:45:46 +00:00
|
|
|
|
gBuf.pBufFill += got_now;
|
|
|
|
|
if (gBuf.pBufFill >= gBuf.pBufEnd)
|
2006-03-03 08:14:44 +00:00
|
|
|
|
gBuf.pBufFill = gBuf.pBufStart; /* wrap */
|
2003-12-18 22:36:35 +00:00
|
|
|
|
|
2006-03-03 08:14:44 +00:00
|
|
|
|
rb->yield(); /* have mercy with the other threads */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
button = rb->button_get(false);
|
|
|
|
|
}
|
2003-12-18 22:36:35 +00:00
|
|
|
|
|
2006-03-03 08:14:44 +00:00
|
|
|
|
/* check keypresses */
|
2004-02-06 21:19:05 +00:00
|
|
|
|
|
2004-02-05 23:45:46 +00:00
|
|
|
|
if (button != BUTTON_NONE)
|
|
|
|
|
{
|
|
|
|
|
filepos = rb->lseek(fd, 0, SEEK_CUR);
|
|
|
|
|
|
2006-03-03 08:14:44 +00:00
|
|
|
|
if (gPlay.bHasVideo) /* video position is more accurate */
|
|
|
|
|
filepos -= Available(gBuf.pReadVideo); /* take video position */
|
2004-01-15 07:48:02 +00:00
|
|
|
|
else
|
2006-03-03 08:14:44 +00:00
|
|
|
|
filepos -= Available(gBuf.pReadAudio); /* else audio */
|
2004-10-12 22:49:51 +00:00
|
|
|
|
|
2006-03-03 08:14:44 +00:00
|
|
|
|
switch (button) { /* set exit conditions */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
case BUTTON_OFF:
|
2006-03-03 08:14:44 +00:00
|
|
|
|
if (gFileHdr.magic == HEADER_MAGIC /* only if file has header */
|
|
|
|
|
&& !(gFileHdr.flags & FLAG_LOOP)) /* not for stills */
|
2004-02-07 16:53:29 +00:00
|
|
|
|
{
|
|
|
|
|
gFileHdr.resume_pos = filepos;
|
2006-03-03 08:14:44 +00:00
|
|
|
|
rb->lseek(fd, 0, SEEK_SET); /* save resume position */
|
2004-02-07 16:53:29 +00:00
|
|
|
|
rb->write(fd, &gFileHdr, sizeof(gFileHdr));
|
|
|
|
|
}
|
2004-10-12 22:49:51 +00:00
|
|
|
|
Cleanup(&fd);
|
2006-03-03 08:14:44 +00:00
|
|
|
|
retval = 0; /* signal "stopped" to caller */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
break;
|
2004-10-12 22:49:51 +00:00
|
|
|
|
case VIDEO_STOP_SEEK:
|
2004-10-16 00:07:43 +00:00
|
|
|
|
#ifdef VIDEO_STOP_SEEK_PRE
|
|
|
|
|
if (lastbutton != VIDEO_STOP_SEEK_PRE)
|
|
|
|
|
break;
|
|
|
|
|
#endif
|
2004-02-05 23:45:46 +00:00
|
|
|
|
if (gPlay.bSeeking)
|
|
|
|
|
{
|
|
|
|
|
gPlay.bSeeking = false;
|
|
|
|
|
gPlay.state = playing;
|
|
|
|
|
SeekTo(fd, gPlay.nSeekPos);
|
|
|
|
|
}
|
|
|
|
|
else if (gPlay.state == playing)
|
2003-12-18 22:36:35 +00:00
|
|
|
|
{
|
2004-02-05 23:45:46 +00:00
|
|
|
|
gPlay.state = paused;
|
|
|
|
|
if (gPlay.bHasAudio)
|
2006-03-03 08:14:44 +00:00
|
|
|
|
rb->mp3_play_pause(false); /* pause audio */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
if (gPlay.bHasVideo)
|
2006-03-03 08:14:44 +00:00
|
|
|
|
rb->timer_unregister(); /* stop the timer */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
}
|
|
|
|
|
else if (gPlay.state == paused)
|
|
|
|
|
{
|
|
|
|
|
gPlay.state = playing;
|
|
|
|
|
if (gPlay.bHasAudio)
|
2003-12-18 22:36:35 +00:00
|
|
|
|
{
|
2004-02-05 23:45:46 +00:00
|
|
|
|
if (gPlay.bHasVideo)
|
|
|
|
|
SyncVideo();
|
2006-03-03 08:14:44 +00:00
|
|
|
|
rb->mp3_play_pause(true); /* play audio */
|
2003-12-18 22:36:35 +00:00
|
|
|
|
}
|
2004-02-05 23:45:46 +00:00
|
|
|
|
if (gPlay.bHasVideo)
|
2006-03-03 08:14:44 +00:00
|
|
|
|
{ /* start the video */
|
2004-10-13 23:59:15 +00:00
|
|
|
|
#if FREQ == 12000000 /* Ondio speed kludge */
|
2005-07-26 20:01:11 +00:00
|
|
|
|
rb->timer_register(1, NULL,
|
2004-10-13 23:59:15 +00:00
|
|
|
|
gPlay.nFrameTimeAdjusted, 1, timer4_isr);
|
|
|
|
|
#else
|
2005-07-26 20:01:11 +00:00
|
|
|
|
rb->timer_register(1, NULL,
|
2004-04-30 21:16:35 +00:00
|
|
|
|
gFileHdr.video_frametime, 1, timer4_isr);
|
2004-10-13 23:59:15 +00:00
|
|
|
|
#endif
|
2004-04-30 21:16:35 +00:00
|
|
|
|
}
|
2003-12-18 22:36:35 +00:00
|
|
|
|
}
|
2004-02-05 23:45:46 +00:00
|
|
|
|
break;
|
|
|
|
|
case BUTTON_UP:
|
|
|
|
|
case BUTTON_UP | BUTTON_REPEAT:
|
|
|
|
|
if (gPlay.bHasAudio)
|
|
|
|
|
ChangeVolume(1);
|
|
|
|
|
break;
|
|
|
|
|
case BUTTON_DOWN:
|
|
|
|
|
case BUTTON_DOWN | BUTTON_REPEAT:
|
|
|
|
|
if (gPlay.bHasAudio)
|
|
|
|
|
ChangeVolume(-1);
|
|
|
|
|
break;
|
|
|
|
|
case BUTTON_LEFT:
|
|
|
|
|
case BUTTON_LEFT | BUTTON_REPEAT:
|
2006-03-03 08:14:44 +00:00
|
|
|
|
if (!gPlay.bSeeking) /* prepare seek */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
{
|
|
|
|
|
gPlay.nSeekPos = filepos;
|
|
|
|
|
gPlay.bSeeking = true;
|
|
|
|
|
gPlay.nSeekAcc = 0;
|
|
|
|
|
}
|
2006-03-03 08:14:44 +00:00
|
|
|
|
else if (gPlay.nSeekAcc > 0) /* other direction, stop sliding */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
gPlay.nSeekAcc = 0;
|
|
|
|
|
else
|
|
|
|
|
gPlay.nSeekAcc--;
|
|
|
|
|
break;
|
|
|
|
|
case BUTTON_RIGHT:
|
|
|
|
|
case BUTTON_RIGHT | BUTTON_REPEAT:
|
2006-03-03 08:14:44 +00:00
|
|
|
|
if (!gPlay.bSeeking) /* prepare seek */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
{
|
|
|
|
|
gPlay.nSeekPos = filepos;
|
|
|
|
|
gPlay.bSeeking = true;
|
|
|
|
|
gPlay.nSeekAcc = 0;
|
|
|
|
|
}
|
2006-03-03 08:14:44 +00:00
|
|
|
|
else if (gPlay.nSeekAcc < 0) /* other direction, stop sliding */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
gPlay.nSeekAcc = 0;
|
|
|
|
|
else
|
|
|
|
|
gPlay.nSeekAcc++;
|
|
|
|
|
break;
|
2004-10-16 00:07:43 +00:00
|
|
|
|
#ifdef VIDEO_DEBUG
|
2006-03-03 08:14:44 +00:00
|
|
|
|
case VIDEO_DEBUG: /* debug key */
|
2004-10-16 00:07:43 +00:00
|
|
|
|
case VIDEO_DEBUG | BUTTON_REPEAT:
|
2006-03-03 08:14:44 +00:00
|
|
|
|
DrawBuf(); /* show buffer status */
|
2004-02-07 16:53:29 +00:00
|
|
|
|
gPlay.nTimeOSD = 30;
|
|
|
|
|
gPlay.bDirtyOSD = true;
|
|
|
|
|
break;
|
2004-10-16 00:07:43 +00:00
|
|
|
|
#endif
|
2006-03-03 08:14:44 +00:00
|
|
|
|
case VIDEO_CONTRAST_DOWN: /* contrast down */
|
2004-10-16 00:07:43 +00:00
|
|
|
|
case VIDEO_CONTRAST_DOWN | BUTTON_REPEAT:
|
2004-04-30 21:38:24 +00:00
|
|
|
|
if (gPlay.bHasVideo)
|
|
|
|
|
ChangeContrast(-1);
|
|
|
|
|
break;
|
2006-03-03 08:14:44 +00:00
|
|
|
|
case VIDEO_CONTRAST_UP: /* contrast up */
|
2004-10-16 00:07:43 +00:00
|
|
|
|
case VIDEO_CONTRAST_UP | BUTTON_REPEAT:
|
2004-04-30 21:38:24 +00:00
|
|
|
|
if (gPlay.bHasVideo)
|
|
|
|
|
ChangeContrast(1);
|
|
|
|
|
break;
|
2004-10-16 00:07:43 +00:00
|
|
|
|
default:
|
|
|
|
|
if (rb->default_event_handler_ex(button, Cleanup, &fd)
|
|
|
|
|
== SYS_USB_CONNECTED)
|
2006-03-03 08:14:44 +00:00
|
|
|
|
retval = -1; /* signal "aborted" to caller */
|
2004-10-16 00:07:43 +00:00
|
|
|
|
break;
|
2003-12-15 07:44:43 +00:00
|
|
|
|
}
|
2004-10-16 00:07:43 +00:00
|
|
|
|
|
|
|
|
|
lastbutton = button;
|
2004-02-05 23:45:46 +00:00
|
|
|
|
} /* if (button != BUTTON_NONE) */
|
2003-12-15 07:44:43 +00:00
|
|
|
|
|
2004-02-06 21:19:05 +00:00
|
|
|
|
|
2006-03-03 08:14:44 +00:00
|
|
|
|
/* handle seeking */
|
2004-02-06 21:19:05 +00:00
|
|
|
|
|
2006-03-03 08:14:44 +00:00
|
|
|
|
if (gPlay.bSeeking) /* seeking? */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
{
|
|
|
|
|
if (gPlay.nSeekAcc < -MAX_ACC)
|
|
|
|
|
gPlay.nSeekAcc = -MAX_ACC;
|
|
|
|
|
else if (gPlay.nSeekAcc > MAX_ACC)
|
|
|
|
|
gPlay.nSeekAcc = MAX_ACC;
|
|
|
|
|
|
|
|
|
|
gPlay.nSeekPos += gPlay.nSeekAcc * gBuf.nSeekChunk;
|
|
|
|
|
if (gPlay.nSeekPos < 0)
|
|
|
|
|
gPlay.nSeekPos = 0;
|
|
|
|
|
if (gPlay.nSeekPos > rb->filesize(fd) - gBuf.granularity)
|
|
|
|
|
{
|
|
|
|
|
gPlay.nSeekPos = rb->filesize(fd);
|
|
|
|
|
gPlay.nSeekPos -= gPlay.nSeekPos % gBuf.granularity;
|
|
|
|
|
}
|
|
|
|
|
DrawPosition(gPlay.nSeekPos, rb->filesize(fd));
|
|
|
|
|
}
|
2003-12-15 07:44:43 +00:00
|
|
|
|
|
2004-02-06 21:19:05 +00:00
|
|
|
|
|
2006-03-03 08:14:44 +00:00
|
|
|
|
/* check + recover underruns */
|
2004-02-06 21:19:05 +00:00
|
|
|
|
|
|
|
|
|
if ((gPlay.bAudioUnderrun || gPlay.bVideoUnderrun) && !gBuf.bEOF)
|
|
|
|
|
{
|
2006-03-03 08:14:44 +00:00
|
|
|
|
gBuf.spinup_safety += HZ/2; /* add extra spinup time for the future */
|
2004-02-06 21:19:05 +00:00
|
|
|
|
filepos = rb->lseek(fd, 0, SEEK_CUR);
|
|
|
|
|
|
|
|
|
|
if (gPlay.bHasVideo && gPlay.bVideoUnderrun)
|
|
|
|
|
{
|
|
|
|
|
gStats.nVideoUnderruns++;
|
2006-03-03 08:14:44 +00:00
|
|
|
|
filepos -= Available(gBuf.pReadVideo); /* take video position */
|
2004-02-06 21:19:05 +00:00
|
|
|
|
SeekTo(fd, filepos);
|
|
|
|
|
}
|
|
|
|
|
else if (gPlay.bHasAudio && gPlay.bAudioUnderrun)
|
|
|
|
|
{
|
|
|
|
|
gStats.nAudioUnderruns++;
|
2006-03-03 08:14:44 +00:00
|
|
|
|
filepos -= Available(gBuf.pReadAudio); /* else audio */
|
2004-02-06 21:19:05 +00:00
|
|
|
|
SeekTo(fd, filepos);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2004-02-05 23:45:46 +00:00
|
|
|
|
return retval;
|
2003-12-15 07:44:43 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int main(char* filename)
|
|
|
|
|
{
|
2004-02-05 23:45:46 +00:00
|
|
|
|
int file_size;
|
2003-12-15 07:44:43 +00:00
|
|
|
|
int fd; /* file descriptor handle */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
int read_now, got_now;
|
2004-03-05 07:11:38 +00:00
|
|
|
|
int button = 0;
|
2004-02-05 23:45:46 +00:00
|
|
|
|
int retval;
|
2003-12-15 07:44:43 +00:00
|
|
|
|
|
2006-03-03 08:14:44 +00:00
|
|
|
|
/* try to open the file */
|
2004-02-07 16:53:29 +00:00
|
|
|
|
fd = rb->open(filename, O_RDWR);
|
2003-12-15 07:44:43 +00:00
|
|
|
|
if (fd < 0)
|
|
|
|
|
return PLUGIN_ERROR;
|
|
|
|
|
file_size = rb->filesize(fd);
|
2005-05-12 19:53:11 +00:00
|
|
|
|
|
2006-03-03 08:14:44 +00:00
|
|
|
|
/* reset pitch value to ensure synchronous playback */
|
2005-05-12 19:53:11 +00:00
|
|
|
|
rb->sound_set_pitch(1000);
|
2004-02-05 23:45:46 +00:00
|
|
|
|
|
2006-03-03 08:14:44 +00:00
|
|
|
|
/* init statistics */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
rb->memset(&gStats, 0, sizeof(gStats));
|
|
|
|
|
gStats.minAudioAvail = gStats.minVideoAvail = INT_MAX;
|
2004-05-09 18:18:05 +00:00
|
|
|
|
gStats.minSpinup = INT_MAX;
|
2004-02-05 23:45:46 +00:00
|
|
|
|
|
2006-03-03 08:14:44 +00:00
|
|
|
|
/* init playback state */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
rb->memset(&gPlay, 0, sizeof(gPlay));
|
|
|
|
|
|
2006-03-03 08:14:44 +00:00
|
|
|
|
/* init buffer */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
rb->memset(&gBuf, 0, sizeof(gBuf));
|
2006-03-03 08:14:44 +00:00
|
|
|
|
gBuf.pOSD = rb->lcd_framebuffer + LCD_WIDTH*7; /* last screen line */
|
2007-04-21 19:07:15 +00:00
|
|
|
|
gBuf.pBufStart = rb->plugin_get_audio_buffer((size_t *)&gBuf.bufsize);
|
2006-03-03 08:14:44 +00:00
|
|
|
|
/*gBuf.bufsize = 1700*1024; // test, like 2MB version!!!! */
|
|
|
|
|
gBuf.pBufFill = gBuf.pBufStart; /* all empty */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
|
2006-03-03 08:14:44 +00:00
|
|
|
|
/* load file header */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
read_now = sizeof(gFileHdr);
|
|
|
|
|
got_now = rb->read(fd, &gFileHdr, read_now);
|
2006-03-03 08:14:44 +00:00
|
|
|
|
rb->lseek(fd, 0, SEEK_SET); /* rewind to restart sector-aligned */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
if (got_now != read_now)
|
|
|
|
|
{
|
2003-12-15 07:44:43 +00:00
|
|
|
|
rb->close(fd);
|
2004-02-05 23:45:46 +00:00
|
|
|
|
return (PLUGIN_ERROR);
|
|
|
|
|
}
|
|
|
|
|
|
2006-03-03 08:14:44 +00:00
|
|
|
|
/* check header */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
if (gFileHdr.magic != HEADER_MAGIC)
|
2006-03-03 08:14:44 +00:00
|
|
|
|
{ /* old file, use default info */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
rb->memset(&gFileHdr, 0, sizeof(gFileHdr));
|
|
|
|
|
gFileHdr.blocksize = SCREENSIZE;
|
2006-03-03 08:14:44 +00:00
|
|
|
|
if (file_size < SCREENSIZE * FPS) /* less than a second */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
gFileHdr.flags |= FLAG_LOOP;
|
|
|
|
|
gFileHdr.video_format = VIDEOFORMAT_RAW;
|
|
|
|
|
gFileHdr.video_width = LCD_WIDTH;
|
|
|
|
|
gFileHdr.video_height = LCD_HEIGHT;
|
2004-10-13 23:59:15 +00:00
|
|
|
|
gFileHdr.video_frametime = 11059200 / FPS;
|
2004-02-05 23:45:46 +00:00
|
|
|
|
gFileHdr.bps_peak = gFileHdr.bps_average = LCD_WIDTH * LCD_HEIGHT * FPS;
|
2003-12-15 07:44:43 +00:00
|
|
|
|
}
|
2004-10-13 23:59:15 +00:00
|
|
|
|
|
|
|
|
|
#if FREQ == 12000000 /* Ondio speed kludge, 625 / 576 == 12000000 / 11059200 */
|
|
|
|
|
gPlay.nFrameTimeAdjusted = (gFileHdr.video_frametime * 625) / 576;
|
2004-10-12 22:49:51 +00:00
|
|
|
|
#endif
|
|
|
|
|
|
2006-03-03 08:14:44 +00:00
|
|
|
|
/* continue buffer init: align the end, calc low water, read sizes */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
gBuf.granularity = gFileHdr.blocksize;
|
2006-03-03 08:14:44 +00:00
|
|
|
|
while (gBuf.granularity % 512) /* common multiple of sector size */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
gBuf.granularity *= 2;
|
2006-03-03 08:14:44 +00:00
|
|
|
|
gBuf.bufsize -= gBuf.bufsize % gBuf.granularity; /* round down */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
gBuf.pBufEnd = gBuf.pBufStart + gBuf.bufsize;
|
2004-05-09 18:18:05 +00:00
|
|
|
|
gBuf.low_water = SPINUP_INIT * gFileHdr.bps_peak / 8000;
|
2006-03-03 08:14:44 +00:00
|
|
|
|
gBuf.spinup_safety = SPINUP_SAFETY * HZ / 1000; /* in time ticks */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
if (gFileHdr.audio_min_associated < 0)
|
|
|
|
|
gBuf.high_water = 0 - gFileHdr.audio_min_associated;
|
2003-12-15 07:44:43 +00:00
|
|
|
|
else
|
2006-03-03 08:14:44 +00:00
|
|
|
|
gBuf.high_water = 1; /* never fill buffer completely, would appear empty */
|
|
|
|
|
gBuf.nReadChunk = (CHUNK + gBuf.granularity - 1); /* round up */
|
|
|
|
|
gBuf.nReadChunk -= gBuf.nReadChunk % gBuf.granularity;/* and align */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
gBuf.nSeekChunk = rb->filesize(fd) / FF_TICKS;
|
2006-03-03 08:14:44 +00:00
|
|
|
|
gBuf.nSeekChunk += gBuf.granularity - 1; /* round up */
|
|
|
|
|
gBuf.nSeekChunk -= gBuf.nSeekChunk % gBuf.granularity; /* and align */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
|
2006-03-03 08:14:44 +00:00
|
|
|
|
/* prepare video playback, if contained */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
if (gFileHdr.video_format == VIDEOFORMAT_RAW)
|
|
|
|
|
{
|
|
|
|
|
gPlay.bHasVideo = true;
|
2007-08-15 12:42:09 +00:00
|
|
|
|
/* Turn off backlight timeout */
|
2007-08-16 23:01:18 +00:00
|
|
|
|
backlight_force_on(rb); /* backlight control in lib/helper.c */
|
2003-12-15 07:44:43 +00:00
|
|
|
|
}
|
|
|
|
|
|
2006-03-03 08:14:44 +00:00
|
|
|
|
/* prepare audio playback, if contained */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
if (gFileHdr.audio_format == AUDIOFORMAT_MP3_BITSWAPPED)
|
|
|
|
|
{
|
|
|
|
|
gPlay.bHasAudio = true;
|
|
|
|
|
}
|
|
|
|
|
|
2006-03-03 08:14:44 +00:00
|
|
|
|
/* start playback by seeking to zero or resume position */
|
|
|
|
|
if (gFileHdr.resume_pos && WantResume(fd)) /* ask the user */
|
2004-02-07 16:53:29 +00:00
|
|
|
|
SeekTo(fd, gFileHdr.resume_pos);
|
|
|
|
|
else
|
|
|
|
|
SeekTo(fd, 0);
|
2004-02-05 23:45:46 +00:00
|
|
|
|
|
2006-03-03 08:14:44 +00:00
|
|
|
|
/* all that's left to do is keep the buffer full */
|
|
|
|
|
do /* the main loop */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
{
|
|
|
|
|
retval = PlayTick(fd);
|
|
|
|
|
} while (retval > 0);
|
|
|
|
|
|
2006-03-03 08:14:44 +00:00
|
|
|
|
if (retval < 0) /* aborted? */
|
2004-02-05 23:45:46 +00:00
|
|
|
|
{
|
|
|
|
|
return PLUGIN_USB_CONNECTED;
|
|
|
|
|
}
|
|
|
|
|
|
2006-03-03 08:14:44 +00:00
|
|
|
|
#ifndef DEBUG /* for release compilations, only display the stats in case of error */
|
2004-03-05 07:11:38 +00:00
|
|
|
|
if (gStats.nAudioUnderruns || gStats.nVideoUnderruns)
|
|
|
|
|
#endif
|
|
|
|
|
{
|
2006-03-03 08:14:44 +00:00
|
|
|
|
/* display statistics */
|
2004-03-05 07:11:38 +00:00
|
|
|
|
rb->lcd_clear_display();
|
|
|
|
|
rb->snprintf(gPrint, sizeof(gPrint), "%d Audio Underruns", gStats.nAudioUnderruns);
|
|
|
|
|
rb->lcd_puts(0, 0, gPrint);
|
|
|
|
|
rb->snprintf(gPrint, sizeof(gPrint), "%d Video Underruns", gStats.nVideoUnderruns);
|
|
|
|
|
rb->lcd_puts(0, 1, gPrint);
|
|
|
|
|
rb->snprintf(gPrint, sizeof(gPrint), "%d MinAudio bytes", gStats.minAudioAvail);
|
|
|
|
|
rb->lcd_puts(0, 2, gPrint);
|
|
|
|
|
rb->snprintf(gPrint, sizeof(gPrint), "%d MinVideo bytes", gStats.minVideoAvail);
|
|
|
|
|
rb->lcd_puts(0, 3, gPrint);
|
2007-03-17 09:02:53 +00:00
|
|
|
|
rb->snprintf(gPrint, sizeof(gPrint), "MinSpinup %ld.%02ld", gStats.minSpinup/HZ, gStats.minSpinup%HZ);
|
2004-03-05 07:11:38 +00:00
|
|
|
|
rb->lcd_puts(0, 4, gPrint);
|
2007-03-17 09:02:53 +00:00
|
|
|
|
rb->snprintf(gPrint, sizeof(gPrint), "MaxSpinup %ld.%02ld", gStats.maxSpinup/HZ, gStats.maxSpinup%HZ);
|
2004-03-05 07:11:38 +00:00
|
|
|
|
rb->lcd_puts(0, 5, gPrint);
|
|
|
|
|
rb->snprintf(gPrint, sizeof(gPrint), "LowWater: %d", gBuf.low_water);
|
|
|
|
|
rb->lcd_puts(0, 6, gPrint);
|
|
|
|
|
rb->snprintf(gPrint, sizeof(gPrint), "HighWater: %d", gBuf.high_water);
|
|
|
|
|
rb->lcd_puts(0, 7, gPrint);
|
2004-02-05 23:45:46 +00:00
|
|
|
|
|
2004-03-05 07:11:38 +00:00
|
|
|
|
rb->lcd_update();
|
|
|
|
|
button = WaitForButton();
|
|
|
|
|
}
|
2003-12-15 07:44:43 +00:00
|
|
|
|
return (button == SYS_USB_CONNECTED) ? PLUGIN_USB_CONNECTED : PLUGIN_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/***************** Plugin Entry Point *****************/
|
|
|
|
|
|
|
|
|
|
enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
|
|
|
|
|
{
|
2006-03-03 08:14:44 +00:00
|
|
|
|
rb = api; /* copy to global api pointer */
|
2003-12-15 07:44:43 +00:00
|
|
|
|
|
2003-12-18 22:36:35 +00:00
|
|
|
|
if (parameter == NULL)
|
|
|
|
|
{
|
2007-03-16 21:56:08 +00:00
|
|
|
|
rb->splash(HZ*2, "Play .rvf file!");
|
2003-12-18 22:36:35 +00:00
|
|
|
|
return PLUGIN_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
2006-03-03 08:14:44 +00:00
|
|
|
|
/* now go ahead and have fun! */
|
2004-10-12 22:49:51 +00:00
|
|
|
|
return main((char*) parameter);
|
2003-12-15 07:44:43 +00:00
|
|
|
|
}
|
|
|
|
|
|
2006-03-03 08:14:44 +00:00
|
|
|
|
#endif /* #ifdef HAVE_LCD_BITMAP */
|
|
|
|
|
#endif /* #ifndef SIMULATOR */
|
2004-01-15 07:48:02 +00:00
|
|
|
|
|