diff options
Diffstat (limited to 'apps/plugins')
-rw-r--r-- | apps/plugins/video.c | 112 |
1 files changed, 59 insertions, 53 deletions
diff --git a/apps/plugins/video.c b/apps/plugins/video.c index eab4da5c70..6650631c58 100644 --- a/apps/plugins/video.c +++ b/apps/plugins/video.c | |||
@@ -84,7 +84,8 @@ typedef struct // contains whatever might be useful to the player | |||
84 | unsigned long blocksize; // how many bytes per block (=video frame) | 84 | unsigned long blocksize; // how many bytes per block (=video frame) |
85 | unsigned long bps_average; // bits per second of the whole stream | 85 | unsigned long bps_average; // bits per second of the whole stream |
86 | unsigned long bps_peak; // max. of above (audio may be VBR) | 86 | unsigned long bps_peak; // max. of above (audio may be VBR) |
87 | unsigned long reserved[10]; // reserved, should be zero | 87 | unsigned long resume_pos; // file position to resume to |
88 | unsigned long reserved[9]; // reserved, should be zero | ||
88 | 89 | ||
89 | // video info (16 entries = 64 byte) | 90 | // video info (16 entries = 64 byte) |
90 | unsigned long video_format; // one of VIDEOFORMAT_xxx | 91 | unsigned long video_format; // one of VIDEOFORMAT_xxx |
@@ -138,8 +139,8 @@ static struct | |||
138 | { | 139 | { |
139 | enum | 140 | enum |
140 | { | 141 | { |
141 | playing, | ||
142 | paused, | 142 | paused, |
143 | playing, | ||
143 | } state; | 144 | } state; |
144 | bool bAudioUnderrun; | 145 | bool bAudioUnderrun; |
145 | bool bVideoUnderrun; | 146 | bool bVideoUnderrun; |
@@ -198,41 +199,24 @@ int Available(unsigned char* pSnapshot) | |||
198 | // debug function to draw buffer indicators | 199 | // debug function to draw buffer indicators |
199 | void DrawBuf(void) | 200 | void DrawBuf(void) |
200 | { | 201 | { |
201 | static int old_fill = -1; // indicate not initialized | ||
202 | static int old_video; | ||
203 | static int old_audio; | ||
204 | int fill, video, audio; | 202 | int fill, video, audio; |
205 | 203 | ||
206 | // first call? | 204 | rb->memset(gBuf.pOSD, 0x10, LCD_WIDTH); // draw line |
207 | if (old_fill == -1) | 205 | gBuf.pOSD[0] = gBuf.pOSD[LCD_WIDTH-1] = 0xFE; // ends |
208 | { | ||
209 | rb->memset(gBuf.pOSD, 0x10, LCD_WIDTH); // draw line | ||
210 | gBuf.pOSD[0] = gBuf.pOSD[LCD_WIDTH-1] = 0xFE; // ends | ||
211 | old_fill = 1; // do no harm below | ||
212 | } | ||
213 | 206 | ||
214 | // calculate new tick positions | 207 | // calculate new tick positions |
215 | fill = 1 + ((gBuf.pBufFill - gBuf.pBufStart) * (LCD_WIDTH-2)) / gBuf.bufsize; | 208 | fill = 1 + ((gBuf.pBufFill - gBuf.pBufStart) * (LCD_WIDTH-2)) / gBuf.bufsize; |
216 | video = 1 + ((gBuf.pReadVideo - gBuf.pBufStart) * (LCD_WIDTH-2)) / gBuf.bufsize; | 209 | video = 1 + ((gBuf.pReadVideo - gBuf.pBufStart) * (LCD_WIDTH-2)) / gBuf.bufsize; |
217 | audio = 1 + ((gBuf.pReadAudio - gBuf.pBufStart) * (LCD_WIDTH-2)) / gBuf.bufsize; | 210 | audio = 1 + ((gBuf.pReadAudio - gBuf.pBufStart) * (LCD_WIDTH-2)) / gBuf.bufsize; |
218 | 211 | ||
219 | if (fill != old_fill || video != old_video || audio != old_audio) | 212 | gBuf.pOSD[fill] |= 0x20; // below the line, two pixels |
220 | { | 213 | gBuf.pOSD[video] |= 0x08; // one above |
221 | // erase old ticks | 214 | gBuf.pOSD[audio] |= 0x04; // two above |
222 | gBuf.pOSD[old_fill] = 0x10; | ||
223 | gBuf.pOSD[old_video] = 0x10; | ||
224 | gBuf.pOSD[old_audio] = 0x10; | ||
225 | |||
226 | gBuf.pOSD[fill] |= 0x20; // below the line, two pixels | ||
227 | gBuf.pOSD[video] |= 0x08; // one above | ||
228 | gBuf.pOSD[audio] |= 0x04; // two above | ||
229 | |||
230 | old_fill = fill; | ||
231 | old_video = video; | ||
232 | old_audio = audio; | ||
233 | 215 | ||
216 | if (gPlay.state == paused) // we have to draw ourselves | ||
217 | rb->lcd_update_rect(0, LCD_HEIGHT-8, LCD_WIDTH, 8); | ||
218 | else | ||
234 | gPlay.bDirtyOSD = true; // redraw it with next timer IRQ | 219 | gPlay.bDirtyOSD = true; // redraw it with next timer IRQ |
235 | } | ||
236 | } | 220 | } |
237 | 221 | ||
238 | 222 | ||
@@ -469,6 +453,23 @@ int WaitForButton(void) | |||
469 | } | 453 | } |
470 | 454 | ||
471 | 455 | ||
456 | bool WantResume(int fd) | ||
457 | { | ||
458 | int button; | ||
459 | |||
460 | rb->lcd_puts(0, 0, "Resume to this"); | ||
461 | rb->lcd_puts(0, 1, "last position?"); | ||
462 | rb->lcd_puts(0, 2, "PLAY = yes"); | ||
463 | rb->lcd_puts(0, 3, "Any Other = no"); | ||
464 | rb->lcd_puts(0, 4, " (plays from start)"); | ||
465 | DrawPosition(gFileHdr.resume_pos, rb->filesize(fd)); | ||
466 | rb->lcd_update(); | ||
467 | |||
468 | button = WaitForButton(); | ||
469 | return (button == BUTTON_PLAY); | ||
470 | } | ||
471 | |||
472 | |||
472 | int SeekTo(int fd, int nPos) | 473 | int SeekTo(int fd, int nPos) |
473 | { | 474 | { |
474 | int read_now, got_now; | 475 | int read_now, got_now; |
@@ -505,18 +506,18 @@ int SeekTo(int fd, int nPos) | |||
505 | while (((tAudioFrameHeader*)(gBuf.pReadAudio))->magic != AUDIO_MAGIC) | 506 | while (((tAudioFrameHeader*)(gBuf.pReadAudio))->magic != AUDIO_MAGIC) |
506 | gBuf.pReadAudio += gFileHdr.blocksize; | 507 | gBuf.pReadAudio += gFileHdr.blocksize; |
507 | 508 | ||
508 | rb->mp3_play_data(gBuf.pReadAudio + gFileHdr.audio_headersize, | ||
509 | gFileHdr.blocksize - gFileHdr.audio_headersize, GetMoreMp3); | ||
510 | |||
511 | if (gPlay.bHasVideo) | 509 | if (gPlay.bHasVideo) |
512 | SyncVideo(); // pick the right video for that | 510 | SyncVideo(); // pick the right video for that |
513 | } | 511 | } |
514 | } | 512 | } |
515 | 513 | ||
516 | // synchronous start | 514 | // synchronous start |
515 | gPlay.state = playing; | ||
517 | if (gPlay.bHasAudio) | 516 | if (gPlay.bHasAudio) |
518 | { | 517 | { |
519 | gPlay.bAudioUnderrun = false; | 518 | gPlay.bAudioUnderrun = false; |
519 | rb->mp3_play_data(gBuf.pReadAudio + gFileHdr.audio_headersize, | ||
520 | gFileHdr.blocksize - gFileHdr.audio_headersize, GetMoreMp3); | ||
520 | rb->mp3_play_pause(true); // kickoff audio | 521 | rb->mp3_play_pause(true); // kickoff audio |
521 | } | 522 | } |
522 | if (gPlay.bHasVideo) | 523 | if (gPlay.bHasVideo) |
@@ -553,7 +554,15 @@ int PlayTick(int fd) | |||
553 | if ((!gPlay.bHasAudio || gPlay.bAudioUnderrun) | 554 | if ((!gPlay.bHasAudio || gPlay.bAudioUnderrun) |
554 | && (!gPlay.bHasVideo || gPlay.bVideoUnderrun) | 555 | && (!gPlay.bHasVideo || gPlay.bVideoUnderrun) |
555 | && gBuf.bEOF) | 556 | && gBuf.bEOF) |
557 | { | ||
558 | if (gFileHdr.resume_pos) | ||
559 | { // we played til the end, clear resume position | ||
560 | gFileHdr.resume_pos = 0; | ||
561 | rb->lseek(fd, 0, SEEK_SET); // save resume position | ||
562 | rb->write(fd, &gFileHdr, sizeof(gFileHdr)); | ||
563 | } | ||
556 | return 0; // all expired | 564 | return 0; // all expired |
565 | } | ||
557 | 566 | ||
558 | if (!gPlay.bRefilling || gBuf.bEOF) | 567 | if (!gPlay.bRefilling || gBuf.bEOF) |
559 | { // nothing to do | 568 | { // nothing to do |
@@ -611,6 +620,13 @@ int PlayTick(int fd) | |||
611 | switch (button) | 620 | switch (button) |
612 | { // set exit conditions | 621 | { // set exit conditions |
613 | case BUTTON_OFF: | 622 | case BUTTON_OFF: |
623 | if (gFileHdr.magic == HEADER_MAGIC // only if file has header | ||
624 | && !(gFileHdr.flags & FLAG_LOOP)) // not for stills | ||
625 | { | ||
626 | gFileHdr.resume_pos = filepos; | ||
627 | rb->lseek(fd, 0, SEEK_SET); // save resume position | ||
628 | rb->write(fd, &gFileHdr, sizeof(gFileHdr)); | ||
629 | } | ||
614 | retval = 0; // signal "stop" to caller | 630 | retval = 0; // signal "stop" to caller |
615 | break; | 631 | break; |
616 | case SYS_USB_CONNECTED: | 632 | case SYS_USB_CONNECTED: |
@@ -680,6 +696,12 @@ int PlayTick(int fd) | |||
680 | else | 696 | else |
681 | gPlay.nSeekAcc++; | 697 | gPlay.nSeekAcc++; |
682 | break; | 698 | break; |
699 | case BUTTON_F1: // debug key | ||
700 | case BUTTON_F1 | BUTTON_REPEAT: | ||
701 | DrawBuf(); // show buffer status | ||
702 | gPlay.nTimeOSD = 30; | ||
703 | gPlay.bDirtyOSD = true; | ||
704 | break; | ||
683 | } | 705 | } |
684 | } /* if (button != BUTTON_NONE) */ | 706 | } /* if (button != BUTTON_NONE) */ |
685 | 707 | ||
@@ -738,7 +760,7 @@ int main(char* filename) | |||
738 | int retval; | 760 | int retval; |
739 | 761 | ||
740 | // try to open the file | 762 | // try to open the file |
741 | fd = rb->open(filename, O_RDONLY); | 763 | fd = rb->open(filename, O_RDWR); |
742 | if (fd < 0) | 764 | if (fd < 0) |
743 | return PLUGIN_ERROR; | 765 | return PLUGIN_ERROR; |
744 | file_size = rb->filesize(fd); | 766 | file_size = rb->filesize(fd); |
@@ -749,15 +771,13 @@ int main(char* filename) | |||
749 | 771 | ||
750 | // init playback state | 772 | // init playback state |
751 | rb->memset(&gPlay, 0, sizeof(gPlay)); | 773 | rb->memset(&gPlay, 0, sizeof(gPlay)); |
752 | gPlay.state = playing; | ||
753 | 774 | ||
754 | // init buffer | 775 | // init buffer |
755 | rb->memset(&gBuf, 0, sizeof(gBuf)); | 776 | rb->memset(&gBuf, 0, sizeof(gBuf)); |
756 | gBuf.pOSD = rb->lcd_framebuffer + LCD_WIDTH*7; // last screen line | 777 | gBuf.pOSD = rb->lcd_framebuffer + LCD_WIDTH*7; // last screen line |
757 | gBuf.pBufStart = rb->plugin_get_mp3_buffer(&gBuf.bufsize); | 778 | gBuf.pBufStart = rb->plugin_get_mp3_buffer(&gBuf.bufsize); |
758 | //gBuf.bufsize = 1700*1024; // test!!!! | 779 | //gBuf.bufsize = 1700*1024; // test, like 2MB version!!!! |
759 | gBuf.pBufFill = gBuf.pBufStart; // all empty | 780 | gBuf.pBufFill = gBuf.pBufStart; // all empty |
760 | gBuf.pReadVideo = gBuf.pReadAudio = gBuf.pBufStart; | ||
761 | 781 | ||
762 | // load file header | 782 | // load file header |
763 | read_now = sizeof(gFileHdr); | 783 | read_now = sizeof(gFileHdr); |
@@ -800,19 +820,10 @@ int main(char* filename) | |||
800 | gBuf.nSeekChunk += gBuf.granularity - 1; // round up | 820 | gBuf.nSeekChunk += gBuf.granularity - 1; // round up |
801 | gBuf.nSeekChunk -= gBuf.nSeekChunk % gBuf.granularity; // and align | 821 | gBuf.nSeekChunk -= gBuf.nSeekChunk % gBuf.granularity; // and align |
802 | 822 | ||
803 | // precharge buffer with more data | ||
804 | read_now = MAX(gFileHdr.audio_1st_frame, gFileHdr.video_1st_frame); | ||
805 | read_now = (read_now + PRECHARGE + gBuf.granularity - 1); | ||
806 | read_now -= read_now % gBuf.granularity; // round up to granularity | ||
807 | got_now = rb->read(fd, gBuf.pBufFill, read_now); | ||
808 | gBuf.pBufFill += got_now; | ||
809 | |||
810 | // prepare video playback, if contained | 823 | // prepare video playback, if contained |
811 | if (gFileHdr.video_format == VIDEOFORMAT_RAW) | 824 | if (gFileHdr.video_format == VIDEOFORMAT_RAW) |
812 | { | 825 | { |
813 | gBuf.pReadVideo += gFileHdr.video_1st_frame; | ||
814 | gPlay.bHasVideo = true; | 826 | gPlay.bHasVideo = true; |
815 | |||
816 | if (rb->global_settings->backlight_timeout > 0) | 827 | if (rb->global_settings->backlight_timeout > 0) |
817 | rb->backlight_set_timeout(1); // keep the light on | 828 | rb->backlight_set_timeout(1); // keep the light on |
818 | } | 829 | } |
@@ -820,21 +831,16 @@ int main(char* filename) | |||
820 | // prepare audio playback, if contained | 831 | // prepare audio playback, if contained |
821 | if (gFileHdr.audio_format == AUDIOFORMAT_MP3_BITSWAPPED) | 832 | if (gFileHdr.audio_format == AUDIOFORMAT_MP3_BITSWAPPED) |
822 | { | 833 | { |
823 | gBuf.pReadAudio += gFileHdr.audio_1st_frame; | ||
824 | gPlay.bHasAudio = true; | 834 | gPlay.bHasAudio = true; |
825 | |||
826 | rb->mp3_play_init(); | 835 | rb->mp3_play_init(); |
827 | rb->mp3_play_data(gBuf.pReadAudio + gFileHdr.audio_headersize, | ||
828 | gFileHdr.blocksize - gFileHdr.audio_headersize, GetMoreMp3); | ||
829 | rb->mpeg_sound_set(SOUND_VOLUME, rb->global_settings->volume); | 836 | rb->mpeg_sound_set(SOUND_VOLUME, rb->global_settings->volume); |
830 | } | 837 | } |
831 | 838 | ||
832 | // synchronous start | 839 | // start playback by seeking to zero or resume position |
833 | gPlay.state = playing; | 840 | if (gFileHdr.resume_pos && WantResume(fd)) // ask the user |
834 | if (gPlay.bHasAudio) | 841 | SeekTo(fd, gFileHdr.resume_pos); |
835 | rb->mp3_play_pause(true); // kickoff audio | 842 | else |
836 | if (gPlay.bHasVideo) | 843 | SeekTo(fd, 0); |
837 | timer_set(gFileHdr.video_frametime); // start display interrupt | ||
838 | 844 | ||
839 | // all that's left to do is keep the buffer full | 845 | // all that's left to do is keep the buffer full |
840 | do // the main loop | 846 | do // the main loop |