A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 2651 lines 79 kB view raw
1/*************************************************************************** 2 * __________ __ ___. 3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___ 4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / 5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < 6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 7 * \/ \/ \/ \/ \/ 8 * $Id$ 9 * 10 * mpegplayer main entrypoint and UI implementation 11 * 12 * Copyright (c) 2007 Michael Sevakis 13 * 14 * This program is free software; you can redistribute it and/or 15 * modify it under the terms of the GNU General Public License 16 * as published by the Free Software Foundation; either version 2 17 * of the License, or (at your option) any later version. 18 * 19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 20 * KIND, either express or implied. 21 * 22 ****************************************************************************/ 23 24/**************************************************************************** 25 * NOTES: 26 * 27 * mpegplayer is structured as follows: 28 * 29 * +-->Video Thread-->Video Output-->LCD 30 * | 31 * UI-->Stream Manager-->+-->Audio Thread-->PCM buffer--Audio Device 32 * | | | | (ref. clock) 33 * | | +-->Buffer Thread | 34 * Stream Data | | (clock intf./ 35 * Requests | File Cache drift adj.) 36 * | Disk I/O 37 * Stream services 38 * (timing, etc.) 39 * 40 * Thread list: 41 * 1) The main thread - Handles user input, settings, basic playback control 42 * and USB connect. 43 * 44 * 2) Stream Manager thread - Handles playback state, events from streams 45 * such as when a stream is finished, stream commands, PCM state. The 46 * layer in which this thread run also handles arbitration of data 47 * requests between the streams and the disk buffer. The actual specific 48 * transport layer code may get moved out to support multiple container 49 * formats. 50 * 51 * 3) Buffer thread - Buffers data in the background, generates notifications 52 * to streams when their data has been buffered, and watches streams' 53 * progress to keep data available during playback. Handles synchronous 54 * random access requests when the file cache is missed. 55 * 56 * 4) Video thread (running on the COP for PortalPlayer targets) - Decodes 57 * the video stream and renders video frames to the LCD. Handles 58 * miscellaneous video tasks like frame and thumbnail printing. 59 * 60 * 5) Audio thread (running on the main CPU to maintain consistency with the 61 * audio FIQ hander on PP) - Decodes audio frames and places them into 62 * the PCM buffer for rendering by the audio device. 63 * 64 * Streams are neither aware of one another nor care about one another. All 65 * streams shall have their own thread (unless it is _really_ efficient to 66 * have a single thread handle a couple minor streams). All coordination of 67 * the streams is done through the stream manager. The clocking is controlled 68 * by and exposed by the stream manager to other streams and implemented at 69 * the PCM level. 70 * 71 * Notes about MPEG files: 72 * 73 * MPEG System Clock is 27MHz - i.e. 27000000 ticks/second. 74 * 75 * FPS is represented in terms of a frame period - this is always an 76 * integer number of 27MHz ticks. 77 * 78 * e.g. 29.97fps (30000/1001) NTSC video has an exact frame period of 79 * 900900 27MHz ticks. 80 * 81 * In libmpeg2, info->sequence->frame_period contains the frame_period. 82 * 83 * Working with Rockbox's 100Hz tick, the common frame rates would need 84 * to be as follows (1): 85 * 86 * FPS | 27Mhz | 100Hz | 44.1KHz | 48KHz 87 * --------|----------------------------------------------------------- 88 * 10* | 2700000 | 10 | 4410 | 4800 89 * 12* | 2250000 | 8.3333 | 3675 | 4000 90 * 15* | 1800000 | 6.6667 | 2940 | 3200 91 * 23.9760 | 1126125 | 4.170833333 | 1839.3375 | 2002 92 * 24 | 1125000 | 4.166667 | 1837.5 | 2000 93 * 25 | 1080000 | 4 | 1764 | 1920 94 * 29.9700 | 900900 | 3.336667 | 1471,47 | 1601.6 95 * 30 | 900000 | 3.333333 | 1470 | 1600 96 * 97 * *Unofficial framerates 98 * 99 * (1) But we don't really care since the audio clock is used anyway and has 100 * very fine resolution ;-) 101 *****************************************************************************/ 102#include "plugin.h" 103#include "mpegplayer.h" 104#include "lib/helper.h" 105#include "mpeg_settings.h" 106#include "video_out.h" 107#include "stream_thread.h" 108#include "stream_mgr.h" 109 110 111/* button definitions */ 112#if (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD) 113#define MPEG_MENU BUTTON_MODE 114#define MPEG_STOP BUTTON_OFF 115#define MPEG_PAUSE BUTTON_ON 116#define MPEG_VOLDOWN BUTTON_DOWN 117#define MPEG_VOLUP BUTTON_UP 118#define MPEG_RW BUTTON_LEFT 119#define MPEG_FF BUTTON_RIGHT 120 121#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || (CONFIG_KEYPAD == IPOD_3G_PAD) || \ 122 (CONFIG_KEYPAD == IPOD_1G2G_PAD) 123#define MPEG_MENU BUTTON_MENU 124#define MPEG_PAUSE (BUTTON_PLAY | BUTTON_REL) 125#define MPEG_STOP (BUTTON_PLAY | BUTTON_REPEAT) 126#define MPEG_VOLDOWN BUTTON_SCROLL_BACK 127#define MPEG_VOLUP BUTTON_SCROLL_FWD 128#define MPEG_RW BUTTON_LEFT 129#define MPEG_FF BUTTON_RIGHT 130 131#elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD 132#define MPEG_MENU (BUTTON_REC | BUTTON_REL) 133#define MPEG_STOP BUTTON_POWER 134#define MPEG_PAUSE BUTTON_PLAY 135#define MPEG_VOLDOWN BUTTON_DOWN 136#define MPEG_VOLUP BUTTON_UP 137#define MPEG_RW BUTTON_LEFT 138#define MPEG_FF BUTTON_RIGHT 139 140#elif CONFIG_KEYPAD == GIGABEAT_PAD 141#define MPEG_MENU BUTTON_MENU 142#define MPEG_STOP BUTTON_POWER 143#define MPEG_PAUSE BUTTON_SELECT 144#define MPEG_PAUSE2 BUTTON_A 145#define MPEG_VOLDOWN BUTTON_LEFT 146#define MPEG_VOLUP BUTTON_RIGHT 147#define MPEG_VOLDOWN2 BUTTON_VOL_DOWN 148#define MPEG_VOLUP2 BUTTON_VOL_UP 149#define MPEG_RW BUTTON_UP 150#define MPEG_FF BUTTON_DOWN 151 152#define MPEG_RC_MENU BUTTON_RC_DSP 153#define MPEG_RC_STOP (BUTTON_RC_PLAY | BUTTON_REPEAT) 154#define MPEG_RC_PAUSE (BUTTON_RC_PLAY | BUTTON_REL) 155#define MPEG_RC_VOLDOWN BUTTON_RC_VOL_DOWN 156#define MPEG_RC_VOLUP BUTTON_RC_VOL_UP 157#define MPEG_RC_RW BUTTON_RC_REW 158#define MPEG_RC_FF BUTTON_RC_FF 159 160#elif CONFIG_KEYPAD == GIGABEAT_S_PAD 161#define MPEG_MENU BUTTON_MENU 162#define MPEG_STOP BUTTON_POWER 163#define MPEG_PAUSE BUTTON_SELECT 164#define MPEG_PAUSE2 BUTTON_PLAY 165#define MPEG_VOLDOWN BUTTON_LEFT 166#define MPEG_VOLUP BUTTON_RIGHT 167#define MPEG_VOLDOWN2 BUTTON_VOL_DOWN 168#define MPEG_VOLUP2 BUTTON_VOL_UP 169#define MPEG_RW BUTTON_UP 170#define MPEG_RW2 BUTTON_PREV 171#define MPEG_FF BUTTON_DOWN 172#define MPEG_FF2 BUTTON_NEXT 173#define MPEG_SHOW_OSD BUTTON_BACK 174 175#define MPEG_RC_MENU BUTTON_RC_DSP 176#define MPEG_RC_STOP (BUTTON_RC_PLAY | BUTTON_REPEAT) 177#define MPEG_RC_PAUSE (BUTTON_RC_PLAY | BUTTON_REL) 178#define MPEG_RC_VOLDOWN BUTTON_RC_VOL_DOWN 179#define MPEG_RC_VOLUP BUTTON_RC_VOL_UP 180#define MPEG_RC_RW BUTTON_RC_REW 181#define MPEG_RC_FF BUTTON_RC_FF 182 183#elif CONFIG_KEYPAD == IRIVER_H10_PAD 184#define MPEG_MENU BUTTON_LEFT 185#define MPEG_STOP BUTTON_POWER 186#define MPEG_PAUSE BUTTON_PLAY 187#define MPEG_VOLDOWN BUTTON_SCROLL_DOWN 188#define MPEG_VOLUP BUTTON_SCROLL_UP 189#define MPEG_RW BUTTON_REW 190#define MPEG_FF BUTTON_FF 191 192#elif CONFIG_KEYPAD == SANSA_E200_PAD 193#define MPEG_MENU BUTTON_SELECT 194#define MPEG_STOP BUTTON_POWER 195#define MPEG_PAUSE BUTTON_RIGHT 196#define MPEG_VOLDOWN BUTTON_SCROLL_BACK 197#define MPEG_VOLUP BUTTON_SCROLL_FWD 198#define MPEG_RW BUTTON_UP 199#define MPEG_FF BUTTON_DOWN 200 201#elif CONFIG_KEYPAD == SANSA_FUZE_PAD 202#define MPEG_MENU BUTTON_SELECT 203#define MPEG_STOP (BUTTON_HOME|BUTTON_REPEAT) 204#define MPEG_PAUSE BUTTON_UP 205#define MPEG_VOLDOWN BUTTON_SCROLL_BACK 206#define MPEG_VOLUP BUTTON_SCROLL_FWD 207#define MPEG_RW BUTTON_LEFT 208#define MPEG_FF BUTTON_RIGHT 209 210 211#elif CONFIG_KEYPAD == SANSA_C200_PAD || \ 212CONFIG_KEYPAD == SANSA_CLIP_PAD || \ 213CONFIG_KEYPAD == SANSA_M200_PAD 214#define MPEG_MENU BUTTON_SELECT 215#define MPEG_STOP BUTTON_POWER 216#define MPEG_PAUSE BUTTON_UP 217#define MPEG_VOLDOWN BUTTON_VOL_DOWN 218#define MPEG_VOLUP BUTTON_VOL_UP 219#define MPEG_RW BUTTON_LEFT 220#define MPEG_FF BUTTON_RIGHT 221 222#elif CONFIG_KEYPAD == MROBE500_PAD 223#define MPEG_STOP BUTTON_POWER 224 225#define MPEG_RC_MENU BUTTON_RC_HEART 226#define MPEG_RC_STOP BUTTON_RC_DOWN 227#define MPEG_RC_PAUSE BUTTON_RC_PLAY 228#define MPEG_RC_VOLDOWN BUTTON_RC_VOL_DOWN 229#define MPEG_RC_VOLUP BUTTON_RC_VOL_UP 230#define MPEG_RC_RW BUTTON_RC_REW 231#define MPEG_RC_FF BUTTON_RC_FF 232 233#elif CONFIG_KEYPAD == MROBE100_PAD 234#define MPEG_MENU BUTTON_MENU 235#define MPEG_STOP BUTTON_POWER 236#define MPEG_PAUSE BUTTON_PLAY 237#define MPEG_VOLDOWN BUTTON_DOWN 238#define MPEG_VOLUP BUTTON_UP 239#define MPEG_RW BUTTON_LEFT 240#define MPEG_FF BUTTON_RIGHT 241 242#elif CONFIG_KEYPAD == IAUDIO_M3_PAD 243#define MPEG_MENU BUTTON_RC_MENU 244#define MPEG_STOP BUTTON_RC_REC 245#define MPEG_PAUSE BUTTON_RC_PLAY 246#define MPEG_VOLDOWN BUTTON_RC_VOL_DOWN 247#define MPEG_VOLUP BUTTON_RC_VOL_UP 248#define MPEG_RW BUTTON_RC_REW 249#define MPEG_FF BUTTON_RC_FF 250 251#elif CONFIG_KEYPAD == COWON_D2_PAD 252#define MPEG_MENU (BUTTON_MENU|BUTTON_REL) 253//#define MPEG_STOP BUTTON_POWER 254#define MPEG_VOLDOWN BUTTON_MINUS 255#define MPEG_VOLUP BUTTON_PLUS 256 257#elif CONFIG_KEYPAD == CREATIVEZVM_PAD 258#define MPEG_MENU BUTTON_MENU 259#define MPEG_STOP BUTTON_BACK 260#define MPEG_PAUSE BUTTON_PLAY 261#define MPEG_VOLDOWN BUTTON_UP 262#define MPEG_VOLUP BUTTON_DOWN 263#define MPEG_RW BUTTON_LEFT 264#define MPEG_FF BUTTON_RIGHT 265 266#elif CONFIG_KEYPAD == CREATIVE_ZENXFI3_PAD 267#define MPEG_MENU BUTTON_MENU 268#define MPEG_STOP (BUTTON_PLAY|BUTTON_REPEAT) 269#define MPEG_PAUSE (BUTTON_PLAY|BUTTON_REL) 270#define MPEG_VOLDOWN BUTTON_VOL_DOWN 271#define MPEG_VOLUP BUTTON_VOL_UP 272#define MPEG_RW BUTTON_DOWN 273#define MPEG_FF BUTTON_UP 274 275#elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD 276#define MPEG_MENU BUTTON_MENU 277#define MPEG_STOP BUTTON_POWER 278#define MPEG_PAUSE BUTTON_SELECT 279#define MPEG_VOLDOWN BUTTON_VOL_DOWN 280#define MPEG_VOLUP BUTTON_VOL_UP 281#define MPEG_RW BUTTON_LEFT 282#define MPEG_FF BUTTON_RIGHT 283 284#elif CONFIG_KEYPAD == PHILIPS_HDD6330_PAD 285#define MPEG_MENU BUTTON_MENU 286#define MPEG_STOP BUTTON_POWER 287#define MPEG_PAUSE BUTTON_PLAY 288#define MPEG_VOLDOWN BUTTON_VOL_DOWN 289#define MPEG_VOLUP BUTTON_VOL_UP 290#define MPEG_RW BUTTON_PREV 291#define MPEG_FF BUTTON_NEXT 292 293#elif CONFIG_KEYPAD == PHILIPS_SA9200_PAD 294#define MPEG_MENU BUTTON_MENU 295#define MPEG_STOP BUTTON_POWER 296#define MPEG_PAUSE BUTTON_PLAY 297#define MPEG_VOLDOWN BUTTON_VOL_DOWN 298#define MPEG_VOLUP BUTTON_VOL_UP 299#define MPEG_RW BUTTON_UP 300#define MPEG_FF BUTTON_DOWN 301 302#elif CONFIG_KEYPAD == ONDAVX747_PAD 303#define MPEG_MENU (BUTTON_MENU|BUTTON_REL) 304//#define MPEG_STOP BUTTON_POWER 305#define MPEG_VOLDOWN BUTTON_VOL_DOWN 306#define MPEG_VOLUP BUTTON_VOL_UP 307 308#elif CONFIG_KEYPAD == ONDAVX777_PAD 309#define MPEG_MENU BUTTON_POWER 310 311#elif (CONFIG_KEYPAD == SAMSUNG_YH820_PAD) || \ 312 (CONFIG_KEYPAD == SAMSUNG_YH92X_PAD) 313#define MPEG_MENU BUTTON_REW 314#define MPEG_STOP (BUTTON_PLAY | BUTTON_REPEAT) 315#define MPEG_PAUSE (BUTTON_PLAY | BUTTON_REL) 316#define MPEG_VOLDOWN BUTTON_DOWN 317#define MPEG_VOLUP BUTTON_UP 318#define MPEG_RW BUTTON_LEFT 319#define MPEG_FF BUTTON_RIGHT 320#define MPEG_SHOW_OSD BUTTON_FFWD 321 322#elif CONFIG_KEYPAD == PBELL_VIBE500_PAD 323#define MPEG_MENU BUTTON_MENU 324#define MPEG_STOP BUTTON_REC 325#define MPEG_PAUSE BUTTON_PLAY 326#define MPEG_VOLDOWN BUTTON_DOWN 327#define MPEG_VOLUP BUTTON_UP 328#define MPEG_RW BUTTON_PREV 329#define MPEG_FF BUTTON_NEXT 330 331#elif CONFIG_KEYPAD == MPIO_HD200_PAD 332#define MPEG_MENU BUTTON_FUNC 333#define MPEG_PAUSE (BUTTON_PLAY | BUTTON_REL) 334#define MPEG_STOP (BUTTON_PLAY | BUTTON_REPEAT) 335#define MPEG_VOLDOWN BUTTON_VOL_DOWN 336#define MPEG_VOLUP BUTTON_VOL_UP 337#define MPEG_RW BUTTON_REW 338#define MPEG_FF BUTTON_FF 339 340#elif CONFIG_KEYPAD == MPIO_HD300_PAD 341#define MPEG_MENU BUTTON_MENU 342#define MPEG_PAUSE (BUTTON_PLAY | BUTTON_REL) 343#define MPEG_STOP (BUTTON_PLAY | BUTTON_REPEAT) 344#define MPEG_VOLDOWN BUTTON_DOWN 345#define MPEG_VOLUP BUTTON_UP 346#define MPEG_RW BUTTON_REW 347#define MPEG_FF BUTTON_FF 348 349#elif CONFIG_KEYPAD == SANSA_FUZEPLUS_PAD 350#define MPEG_MENU BUTTON_POWER 351#define MPEG_PAUSE (BUTTON_PLAYPAUSE | BUTTON_REL) 352#define MPEG_STOP (BUTTON_PLAYPAUSE | BUTTON_REPEAT) 353#define MPEG_VOLDOWN BUTTON_VOL_DOWN 354#define MPEG_VOLUP BUTTON_VOL_UP 355#define MPEG_RW BUTTON_LEFT 356#define MPEG_FF BUTTON_RIGHT 357 358#elif CONFIG_KEYPAD == SANSA_CONNECT_PAD 359#define MPEG_MENU BUTTON_POWER 360#define MPEG_PAUSE (BUTTON_SELECT | BUTTON_REL) 361#define MPEG_STOP (BUTTON_SELECT | BUTTON_REPEAT) 362#define MPEG_VOLDOWN BUTTON_VOL_DOWN 363#define MPEG_VOLUP BUTTON_VOL_UP 364#define MPEG_RW BUTTON_LEFT 365#define MPEG_FF BUTTON_RIGHT 366 367#elif CONFIG_KEYPAD == SAMSUNG_YPR0_PAD 368#define MPEG_MENU BUTTON_MENU 369#define MPEG_PAUSE BUTTON_SELECT 370#define MPEG_STOP BUTTON_POWER 371#define MPEG_VOLDOWN BUTTON_DOWN 372#define MPEG_VOLUP BUTTON_UP 373#define MPEG_RW BUTTON_LEFT 374#define MPEG_FF BUTTON_RIGHT 375 376#elif CONFIG_KEYPAD == HM60X_PAD 377#define MPEG_MENU BUTTON_POWER 378#define MPEG_PAUSE BUTTON_SELECT 379#define MPEG_STOP (BUTTON_SELECT | BUTTON_POWER) 380#define MPEG_VOLDOWN (BUTTON_POWER | BUTTON_DOWN) 381#define MPEG_VOLUP (BUTTON_POWER | BUTTON_UP) 382#define MPEG_RW BUTTON_LEFT 383#define MPEG_FF BUTTON_RIGHT 384 385#elif CONFIG_KEYPAD == HM801_PAD 386#define MPEG_MENU BUTTON_POWER 387#define MPEG_PAUSE BUTTON_PLAY 388#define MPEG_STOP (BUTTON_POWER | BUTTON_PLAY) 389#define MPEG_VOLDOWN (BUTTON_POWER | BUTTON_DOWN) 390#define MPEG_VOLUP (BUTTON_POWER | BUTTON_UP) 391#define MPEG_RW BUTTON_PREV 392#define MPEG_FF BUTTON_NEXT 393 394#elif CONFIG_KEYPAD == SONY_NWZ_PAD 395#define MPEG_MENU BUTTON_BACK 396#define MPEG_PAUSE BUTTON_PLAY 397#define MPEG_STOP BUTTON_POWER 398#define MPEG_VOLDOWN BUTTON_UP 399#define MPEG_VOLUP BUTTON_DOWN 400#define MPEG_RW BUTTON_LEFT 401#define MPEG_FF BUTTON_RIGHT 402 403#elif CONFIG_KEYPAD == CREATIVE_ZEN_PAD 404#define MPEG_MENU BUTTON_MENU 405#define MPEG_PAUSE BUTTON_PLAYPAUSE 406#define MPEG_STOP BUTTON_BACK 407#define MPEG_VOLDOWN BUTTON_DOWN 408#define MPEG_VOLUP BUTTON_UP 409#define MPEG_RW BUTTON_LEFT 410#define MPEG_FF BUTTON_RIGHT 411 412#elif CONFIG_KEYPAD == DX50_PAD 413#define MPEG_MENU BUTTON_POWER 414#define MPEG_VOLDOWN BUTTON_VOL_DOWN 415#define MPEG_VOLUP BUTTON_VOL_UP 416#define MPEG_RW BUTTON_LEFT 417#define MPEG_FF BUTTON_RIGHT 418#define MPEG_PAUSE BUTTON_PLAY 419#define MPEG_STOP (BUTTON_PLAY|BUTTON_REPEAT) 420 421#elif CONFIG_KEYPAD == CREATIVE_ZENXFI2_PAD 422#define MPEG_MENU BUTTON_POWER 423#define MPEG_PAUSE BUTTON_MENU 424#define MPEG_STOP (BUTTON_MENU|BUTTON_REPEAT) 425 426#elif CONFIG_KEYPAD == AGPTEK_ROCKER_PAD 427#define MPEG_MENU BUTTON_POWER 428#define MPEG_PAUSE BUTTON_SELECT 429#define MPEG_STOP BUTTON_DOWN 430#define MPEG_VOLDOWN BUTTON_VOLDOWN 431#define MPEG_VOLUP BUTTON_VOLUP 432#define MPEG_RW BUTTON_LEFT 433#define MPEG_FF BUTTON_RIGHT 434 435#elif CONFIG_KEYPAD == XDUOO_X3_PAD 436#define MPEG_MENU BUTTON_PLAY 437#define MPEG_STOP BUTTON_POWER 438#define MPEG_PAUSE BUTTON_HOME 439#define MPEG_VOLDOWN BUTTON_VOL_DOWN 440#define MPEG_VOLUP BUTTON_VOL_UP 441#define MPEG_RW BUTTON_PREV 442#define MPEG_FF BUTTON_NEXT 443 444#elif CONFIG_KEYPAD == XDUOO_X3II_PAD || CONFIG_KEYPAD == XDUOO_X20_PAD 445#define MPEG_MENU BUTTON_PLAY 446#define MPEG_STOP BUTTON_POWER 447#define MPEG_PAUSE BUTTON_HOME 448#define MPEG_VOLDOWN BUTTON_VOL_DOWN 449#define MPEG_VOLUP BUTTON_VOL_UP 450#define MPEG_RW BUTTON_PREV 451#define MPEG_FF BUTTON_NEXT 452 453#elif CONFIG_KEYPAD == FIIO_M3K_LINUX_PAD 454#define MPEG_MENU BUTTON_PLAY 455#define MPEG_STOP BUTTON_POWER 456#define MPEG_PAUSE BUTTON_HOME 457#define MPEG_VOLDOWN BUTTON_VOL_DOWN 458#define MPEG_VOLUP BUTTON_VOL_UP 459#define MPEG_RW BUTTON_PREV 460#define MPEG_FF BUTTON_NEXT 461 462#elif CONFIG_KEYPAD == IHIFI_770_PAD || CONFIG_KEYPAD == IHIFI_800_PAD 463#define MPEG_MENU BUTTON_PLAY 464#define MPEG_STOP BUTTON_POWER 465#define MPEG_PAUSE BUTTON_HOME 466#define MPEG_VOLDOWN BUTTON_VOL_DOWN 467#define MPEG_VOLUP BUTTON_VOL_UP 468#define MPEG_RW BUTTON_PREV 469#define MPEG_FF BUTTON_NEXT 470 471#elif CONFIG_KEYPAD == EROSQ_PAD 472#define MPEG_MENU BUTTON_MENU 473#define MPEG_STOP BUTTON_POWER 474#define MPEG_PAUSE BUTTON_PLAY 475#define MPEG_VOLDOWN BUTTON_VOL_DOWN 476#define MPEG_VOLUP BUTTON_VOL_UP 477#define MPEG_RW BUTTON_PREV 478#define MPEG_FF BUTTON_NEXT 479 480#elif CONFIG_KEYPAD == FIIO_M3K_PAD 481#define MPEG_MENU BUTTON_MENU 482#define MPEG_STOP BUTTON_POWER 483#define MPEG_PAUSE BUTTON_PLAY 484#define MPEG_VOLDOWN BUTTON_VOL_DOWN 485#define MPEG_VOLUP BUTTON_VOL_UP 486#define MPEG_RW BUTTON_LEFT 487#define MPEG_FF BUTTON_RIGHT 488 489#elif CONFIG_KEYPAD == MA_PAD 490#define MPEG_MENU BUTTON_MENU 491#define MPEG_STOP BUTTON_BACK 492#define MPEG_PAUSE BUTTON_PLAY 493#define MPEG_VOLDOWN BUTTON_DOWN 494#define MPEG_VOLUP BUTTON_UP 495#define MPEG_RW BUTTON_LEFT 496#define MPEG_FF BUTTON_RIGHT 497 498#elif CONFIG_KEYPAD == SHANLING_Q1_PAD 499/* use touchscreen */ 500 501#elif CONFIG_KEYPAD == RG_NANO_PAD 502#define MPEG_MENU BUTTON_START 503#define MPEG_STOP BUTTON_X 504#define MPEG_PAUSE BUTTON_A 505#define MPEG_VOLDOWN BUTTON_DOWN 506#define MPEG_VOLUP BUTTON_UP 507#define MPEG_RW BUTTON_LEFT 508#define MPEG_FF BUTTON_RIGHT 509 510#else 511#error No keymap defined! 512#endif 513 514#ifdef HAVE_TOUCHSCREEN 515#ifndef MPEG_MENU 516#define MPEG_MENU (BUTTON_TOPRIGHT|BUTTON_REL) 517#endif 518#ifndef MPEG_STOP 519#define MPEG_STOP BUTTON_TOPLEFT 520#endif 521#ifndef MPEG_PAUSE 522#define MPEG_PAUSE BUTTON_CENTER 523#endif 524#ifndef MPEG_VOLDOWN 525#define MPEG_VOLDOWN BUTTON_BOTTOMMIDDLE 526#endif 527#ifndef MPEG_VOLUP 528#define MPEG_VOLUP BUTTON_TOPMIDDLE 529#endif 530#ifndef MPEG_RW 531#define MPEG_RW BUTTON_MIDLEFT 532#endif 533#ifndef MPEG_FF 534#define MPEG_FF BUTTON_MIDRIGHT 535#endif 536#endif 537 538/* One thing we can do here for targets with remotes is having a display 539 * always on the remote instead of always forcing a popup on the main display */ 540 541#define FF_REWIND_MAX_PERCENT 3 /* cap ff/rewind step size at max % of file */ 542 /* 3% of 30min file == 54s step size */ 543#define MIN_FF_REWIND_STEP (TS_SECOND/2) 544#define OSD_MIN_UPDATE_INTERVAL (HZ/2) 545#define FPS_UPDATE_INTERVAL (HZ) /* Get new FPS reading each second */ 546 547enum video_action 548{ 549 VIDEO_STOP = 0, 550 VIDEO_PREV, 551 VIDEO_NEXT, 552 VIDEO_ACTION_MANUAL = 0x8000, /* Flag that says user did it */ 553}; 554 555/* OSD status - same order as icon array */ 556enum osd_status_enum 557{ 558 OSD_STATUS_STOPPED = 0, 559 OSD_STATUS_PAUSED, 560 OSD_STATUS_PLAYING, 561 OSD_STATUS_FF, 562 OSD_STATUS_RW, 563 OSD_STATUS_COUNT, 564 OSD_STATUS_MASK = 0x7 565}; 566 567enum osd_bits 568{ 569 OSD_REFRESH_DEFAULT = 0x0000, /* Only refresh elements when due */ 570 /* Refresh the... */ 571 OSD_REFRESH_VOLUME = 0x0001, /* ...volume display */ 572 OSD_REFRESH_TIME = 0x0002, /* ...time display+progress */ 573 OSD_REFRESH_STATUS = 0x0004, /* ...playback status icon */ 574 OSD_REFRESH_BACKGROUND = 0x0008, /* ...background (implies ALL) */ 575 OSD_REFRESH_VIDEO = 0x0010, /* ...video image upon timeout */ 576 OSD_REFRESH_RESUME = 0x0020, /* Resume playback upon timeout */ 577 OSD_NODRAW = 0x8000, /* OR bitflag - don't draw anything */ 578 OSD_SHOW = 0x4000, /* OR bitflag - show the OSD */ 579#ifdef HAVE_HEADPHONE_DETECTION 580 OSD_HP_PAUSE = 0x2000, /* OR bitflag - headphones caused pause */ 581#endif 582 OSD_HIDE = 0x0000, /* hide the OSD (aid readability) */ 583 OSD_REFRESH_ALL = 0x000f, /* Only immediate graphical elements */ 584}; 585 586/* Status icons selected according to font height */ 587extern const unsigned char mpegplayer_status_icons_8x8x1[]; 588extern const unsigned char mpegplayer_status_icons_12x12x1[]; 589extern const unsigned char mpegplayer_status_icons_16x16x1[]; 590 591/* Main border areas that contain OSD elements */ 592#define OSD_BDR_L 2 593#define OSD_BDR_T 2 594#define OSD_BDR_R 2 595#define OSD_BDR_B 2 596 597struct osd 598{ 599 long hide_tick; 600 long show_for; 601 long print_tick; 602 long print_delay; 603 long resume_tick; 604 long resume_delay; 605 long next_auto_refresh; 606 int x; 607 int y; 608 int width; 609 int height; 610 unsigned fgcolor; 611 unsigned bgcolor; 612 unsigned prog_fillcolor; 613 struct vo_rect update_rect; 614 struct vo_rect prog_rect; 615 struct vo_rect time_rect; 616 struct vo_rect dur_rect; 617 struct vo_rect vol_rect; 618 const unsigned char *icons; 619 struct vo_rect stat_rect; 620 int status; 621 uint32_t curr_time; 622 unsigned auto_refresh; 623 unsigned flags; 624 int font; 625}; 626 627struct fps 628{ 629 /* FPS Display */ 630 struct vo_rect rect; /* OSD coordinates */ 631 int pf_x; /* Screen coordinates */ 632 int pf_y; 633 int pf_width; 634 int pf_height; 635 long update_tick; /* When to next update FPS reading */ 636 #define FPS_FORMAT "%d.%02d" 637 #define FPS_DIMSTR "999.99" /* For establishing rect size */ 638 #define FPS_BUFSIZE sizeof("999.99") 639}; 640 641static struct osd osd; 642static struct fps fps NOCACHEBSS_ATTR; /* Accessed on other processor */ 643 644#ifdef LCD_PORTRAIT 645static fb_data* get_framebuffer(void) 646{ 647 struct viewport *vp_main = *(rb->screens[SCREEN_MAIN]->current_viewport); 648 return vp_main->buffer->fb_ptr; 649} 650#endif 651 652static void osd_show(unsigned show); 653 654#ifdef LCD_LANDSCAPE 655 #define __X (x + osd.x) 656 #define __Y (y + osd.y) 657 #define __W width 658 #define __H height 659#else 660 #define __X (LCD_WIDTH - (y + osd.y) - height) 661 #define __Y (x + osd.x) 662 #define __W height 663 #define __H width 664#endif 665 666#ifdef HAVE_LCD_COLOR 667/* Blend two colors in 0-100% (0-255) mix of c2 into c1 */ 668static unsigned draw_blendcolor(unsigned c1, unsigned c2, unsigned char amount) 669{ 670 int r1 = RGB_UNPACK_RED(c1); 671 int g1 = RGB_UNPACK_GREEN(c1); 672 int b1 = RGB_UNPACK_BLUE(c1); 673 674 int r2 = RGB_UNPACK_RED(c2); 675 int g2 = RGB_UNPACK_GREEN(c2); 676 int b2 = RGB_UNPACK_BLUE(c2); 677 678 return LCD_RGBPACK(amount*(r2 - r1) / 255 + r1, 679 amount*(g2 - g1) / 255 + g1, 680 amount*(b2 - b1) / 255 + b1); 681} 682#endif 683 684#ifdef PLUGIN_USE_IRAM 685/* IRAM preserving mechanism to enable talking menus */ 686static char *iram_saved_copy; 687extern char iramstart[], iramend[]; 688 689static void iram_saving_init(void) 690{ 691#ifndef SIMULATOR 692 size_t size; 693 iram_saved_copy = (char *)rb->plugin_get_buffer(&size); 694 695 if (size >= (size_t)(iramend-iramstart)) 696 iram_saved_copy += size - (size_t)(iramend - iramstart); 697 else 698#endif 699 iram_saved_copy = NULL; 700 701 return; 702} 703 704void mpegplayer_iram_preserve(void) 705{ 706 if (iram_saved_copy) 707 { 708 rb->memcpy(iram_saved_copy, iramstart, iramend-iramstart); 709#ifdef HAVE_CPUCACHE_INVALIDATE 710 /* make the icache (if it exists) up to date with the new code */ 711 rb->cpucache_invalidate(); 712#endif /* HAVE_CPUCACHE_INVALIDATE */ 713 } 714 return; 715} 716 717void mpegplayer_iram_restore(void) 718{ 719 if (iram_saved_copy) 720 { 721 rb->audio_hard_stop(); 722 rb->memcpy(iramstart, iram_saved_copy, iramend-iramstart); 723#ifdef HAVE_CPUCACHE_INVALIDATE 724 /* make the icache (if it exists) up to date with the new code */ 725 rb->cpucache_invalidate(); 726#endif /* HAVE_CPUCACHE_INVALIDATE */ 727 } 728 return; 729} 730#endif 731 732/* Drawing functions that operate rotated on LCD_PORTRAIT displays - 733 * most are just wrappers of lcd_* functions with transforms applied. 734 * The origin is the upper-left corner of the OSD area */ 735static void draw_update_rect(int x, int y, int width, int height) 736{ 737 mylcd_update_rect(__X, __Y, __W, __H); 738} 739 740static void draw_clear_area(int x, int y, int width, int height) 741{ 742#ifdef HAVE_LCD_COLOR 743 rb->screen_clear_area(rb->screens[SCREEN_MAIN], __X, __Y, __W, __H); 744#else 745 int oldmode = grey_get_drawmode(); 746 grey_set_drawmode(DRMODE_SOLID | DRMODE_INVERSEVID); 747 grey_fillrect(__X, __Y, __W, __H); 748 grey_set_drawmode(oldmode); 749#endif 750} 751 752static void draw_clear_area_rect(const struct vo_rect *rc) 753{ 754 draw_clear_area(rc->l, rc->t, rc->r - rc->l, rc->b - rc->t); 755} 756 757static void draw_fillrect(int x, int y, int width, int height) 758{ 759 mylcd_fillrect(__X, __Y, __W, __H); 760} 761 762static void draw_hline(int x1, int x2, int y) 763{ 764#ifdef LCD_LANDSCAPE 765 mylcd_hline(x1 + osd.x, x2 + osd.x, y + osd.y); 766#else 767 y = LCD_WIDTH - (y + osd.y) - 1; 768 mylcd_vline(y, x1 + osd.x, x2 + osd.x); 769#endif 770} 771 772static void draw_vline(int x, int y1, int y2) 773{ 774#ifdef LCD_LANDSCAPE 775 mylcd_vline(x + osd.x, y1 + osd.y, y2 + osd.y); 776#else 777 y1 = LCD_WIDTH - (y1 + osd.y) - 1; 778 y2 = LCD_WIDTH - (y2 + osd.y) - 1; 779 mylcd_hline(y1, y2, x + osd.x); 780#endif 781} 782 783static void draw_scrollbar_draw(int x, int y, int width, int height, 784 uint32_t min, uint32_t max, uint32_t val) 785{ 786 unsigned oldfg = mylcd_get_foreground(); 787 788 draw_hline(x + 1, x + width - 2, y); 789 draw_hline(x + 1, x + width - 2, y + height - 1); 790 draw_vline(x, y + 1, y + height - 2); 791 draw_vline(x + width - 1, y + 1, y + height - 2); 792 793 val = muldiv_uint32(width - 2, val, max - min); 794 val = MIN(val, (uint32_t)(width - 2)); 795 796 draw_fillrect(x + 1, y + 1, val, height - 2); 797 798 mylcd_set_foreground(osd.prog_fillcolor); 799 800 draw_fillrect(x + 1 + val, y + 1, width - 2 - val, height - 2); 801 802 mylcd_set_foreground(oldfg); 803} 804 805static void draw_scrollbar_draw_rect(const struct vo_rect *rc, int min, 806 int max, int val) 807{ 808 draw_scrollbar_draw(rc->l, rc->t, rc->r - rc->l, rc->b - rc->t, 809 min, max, val); 810} 811 812static void draw_setfont(int font) 813{ 814 osd.font = font; 815 mylcd_setfont(font); 816} 817 818#ifdef LCD_PORTRAIT 819/* Portrait displays need rotated text rendering */ 820 821/* Limited function that only renders in DRMODE_FG and uses absolute screen 822 * coordinates */ 823static void draw_oriented_mono_bitmap_part(const unsigned char *src, 824 int src_x, int src_y, 825 int stride, int x, int y, 826 int width, int height) 827{ 828 const unsigned char *src_end; 829 fb_data *dst, *dst_end; 830 unsigned fg_pattern; 831 832 if (x + width > SCREEN_WIDTH) 833 width = SCREEN_WIDTH - x; /* Clip right */ 834 if (x < 0) 835 width += x, x = 0; /* Clip left */ 836 if (width <= 0) 837 return; /* nothing left to do */ 838 839 if (y + height > SCREEN_HEIGHT) 840 height = SCREEN_HEIGHT - y; /* Clip bottom */ 841 if (y < 0) 842 height += y, y = 0; /* Clip top */ 843 if (height <= 0) 844 return; /* nothing left to do */ 845 846 fg_pattern = rb->lcd_get_foreground(); 847 /*bg_pattern =*/ rb->lcd_get_background(); 848 849 src += stride * (src_y >> 3) + src_x; /* move starting point */ 850 src_y &= 7; 851 src_end = src + width; 852 853 dst = get_framebuffer() + (LCD_WIDTH - y) + x*LCD_WIDTH; 854 do 855 { 856 const unsigned char *src_col = src++; 857 unsigned data = *src_col >> src_y; 858 int numbits = 8 - src_y; 859 860 fb_data *dst_col = dst; 861 dst_end = dst_col - height; 862 dst += LCD_WIDTH; 863 864 do 865 { 866 dst_col--; 867 868 if (data & 1) 869 *dst_col = FB_SCALARPACK(fg_pattern); 870#if 0 871 else 872 *dst_col = bg_pattern; 873#endif 874 data >>= 1; 875 if (--numbits == 0) { 876 src_col += stride; 877 data = *src_col; 878 numbits = 8; 879 } 880 } 881 while (dst_col > dst_end); 882 } 883 while (src < src_end); 884} 885 886 887#define ALPHA_COLOR_FONT_DEPTH 2 888#define ALPHA_COLOR_LOOKUP_SHIFT (1 << ALPHA_COLOR_FONT_DEPTH) 889#define ALPHA_COLOR_LOOKUP_SIZE ((1 << ALPHA_COLOR_LOOKUP_SHIFT) - 1) 890#define ALPHA_COLOR_PIXEL_PER_BYTE (8 >> ALPHA_COLOR_FONT_DEPTH) 891#define ALPHA_COLOR_PIXEL_PER_WORD (32 >> ALPHA_COLOR_FONT_DEPTH) 892#ifdef CPU_ARM 893#define BLEND_INIT do {} while (0) 894#define BLEND_FINISH do {} while(0) 895#define BLEND_START(acc, color, alpha) \ 896 asm volatile("mul %0, %1, %2" : "=&r" (acc) : "r" (color), "r" (alpha)) 897#define BLEND_CONT(acc, color, alpha) \ 898 asm volatile("mla %0, %1, %2, %0" : "+&r" (acc) : "r" (color), "r" (alpha)) 899#define BLEND_OUT(acc) do {} while (0) 900#elif defined(CPU_COLDFIRE) 901#define ALPHA_BITMAP_READ_WORDS 902#define BLEND_INIT \ 903 unsigned long _macsr = coldfire_get_macsr(); \ 904 coldfire_set_macsr(EMAC_UNSIGNED) 905#define BLEND_FINISH \ 906 coldfire_set_macsr(_macsr) 907#define BLEND_START(acc, color, alpha) \ 908 asm volatile("mac.l %0, %1, %%acc0" :: "%d" (color), "d" (alpha)) 909#define BLEND_CONT BLEND_START 910#define BLEND_OUT(acc) asm volatile("movclr.l %%acc0, %0" : "=d" (acc)) 911#else 912#define BLEND_INIT do {} while (0) 913#define BLEND_FINISH do {} while(0) 914#define BLEND_START(acc, color, alpha) ((acc) = (color) * (alpha)) 915#define BLEND_CONT(acc, color, alpha) ((acc) += (color) * (alpha)) 916#define BLEND_OUT(acc) do {} while (0) 917#endif 918 919/* Blend the given two colors */ 920static inline unsigned blend_two_colors(unsigned c1, unsigned c2, unsigned a) 921{ 922#if LCD_DEPTH == 16 923 a += a >> (ALPHA_COLOR_LOOKUP_SHIFT - 1); 924#if (LCD_PIXELFORMAT == RGB565SWAPPED) 925 c1 = swap16(c1); 926 c2 = swap16(c2); 927#endif 928 unsigned c1l = (c1 | (c1 << 16)) & 0x07e0f81f; 929 unsigned c2l = (c2 | (c2 << 16)) & 0x07e0f81f; 930 unsigned p; 931 BLEND_START(p, c1l, a); 932 BLEND_CONT(p, c2l, ALPHA_COLOR_LOOKUP_SIZE + 1 - a); 933 BLEND_OUT(p); 934 p = (p >> ALPHA_COLOR_LOOKUP_SHIFT) & 0x07e0f81f; 935 p |= (p >> 16); 936#if (LCD_PIXELFORMAT == RGB565SWAPPED) 937 return swap16(p); 938#else 939 return p; 940#endif 941 942#else /* LCD_DEPTH == 24 */ 943 unsigned s = c1; 944 unsigned d = c2; 945 unsigned s1 = s & 0xff00ff; 946 unsigned d1 = d & 0xff00ff; 947 a += a >> (ALPHA_COLOR_LOOKUP_SHIFT - 1); 948 d1 = (d1 + ((s1 - d1) * a >> ALPHA_COLOR_LOOKUP_SHIFT)) & 0xff00ff; 949 s &= 0xff00; 950 d &= 0xff00; 951 d = (d + ((s - d) * a >> ALPHA_COLOR_LOOKUP_SHIFT)) & 0xff00; 952 953 return d1 | d; 954#endif 955} 956 957static void draw_oriented_alpha_bitmap_part(const unsigned char *src, 958 int src_x, int src_y, 959 int stride, int x, int y, 960 int width, int height) 961{ 962 fb_data *dst, *dst_start; 963 unsigned fg_pattern; 964 965 if (x + width > SCREEN_WIDTH) 966 width = SCREEN_WIDTH - x; /* Clip right */ 967 if (x < 0) 968 width += x, x = 0; /* Clip left */ 969 if (width <= 0) 970 return; /* nothing left to do */ 971 972 if (y + height > SCREEN_HEIGHT) 973 height = SCREEN_HEIGHT - y; /* Clip bottom */ 974 if (y < 0) 975 height += y, y = 0; /* Clip top */ 976 if (height <= 0) 977 return; /* nothing left to do */ 978 979 /* initialize blending */ 980 BLEND_INIT; 981 982 fg_pattern = rb->lcd_get_foreground(); 983 /*bg_pattern=*/ rb->lcd_get_background(); 984 985 dst_start = get_framebuffer() + (LCD_WIDTH - y - 1) + x*LCD_WIDTH; 986 int col, row = height; 987 unsigned data, pixels; 988 unsigned skip_end = (stride - width); 989 unsigned skip_start = src_y * stride + src_x; 990 991#ifdef ALPHA_BITMAP_READ_WORDS 992 uint32_t *src_w = (uint32_t *)((uintptr_t)src & ~3); 993 skip_start += ALPHA_COLOR_PIXEL_PER_BYTE * ((uintptr_t)src & 3); 994 src_w += skip_start / ALPHA_COLOR_PIXEL_PER_WORD; 995 data = letoh32(*src_w++); 996#else 997 src += skip_start / ALPHA_COLOR_PIXEL_PER_BYTE; 998 data = *src; 999#endif 1000 pixels = skip_start % ALPHA_COLOR_PIXEL_PER_WORD; 1001 data >>= pixels * ALPHA_COLOR_LOOKUP_SHIFT; 1002#ifdef ALPHA_BITMAP_READ_WORDS 1003 pixels = 8 - pixels; 1004#endif 1005 1006 do 1007 { 1008 col = width; 1009 dst = dst_start--; 1010#ifdef ALPHA_BITMAP_READ_WORDS 1011#define UPDATE_SRC_ALPHA do { \ 1012 if (--pixels) \ 1013 data >>= ALPHA_COLOR_LOOKUP_SHIFT; \ 1014 else \ 1015 { \ 1016 data = letoh32(*src_w++); \ 1017 pixels = ALPHA_COLOR_PIXEL_PER_WORD; \ 1018 } \ 1019 } while (0) 1020#elif ALPHA_COLOR_PIXEL_PER_BYTE == 2 1021#define UPDATE_SRC_ALPHA do { \ 1022 if (pixels ^= 1) \ 1023 data >>= ALPHA_COLOR_LOOKUP_SHIFT; \ 1024 else \ 1025 data = *(++src); \ 1026 } while (0) 1027#else 1028#define UPDATE_SRC_ALPHA do { \ 1029 if (pixels = (++pixels % ALPHA_COLOR_PIXEL_PER_BYTE)) \ 1030 data >>= ALPHA_COLOR_LOOKUP_SHIFT; \ 1031 else \ 1032 data = *(++src); \ 1033 } while (0) 1034#endif 1035 do 1036 { 1037 unsigned color = blend_two_colors(FB_UNPACK_SCALAR_LCD(*dst), fg_pattern, 1038 data & ALPHA_COLOR_LOOKUP_SIZE ); 1039 *dst= FB_SCALARPACK(color); 1040 dst += LCD_WIDTH; 1041 UPDATE_SRC_ALPHA; 1042 } 1043 while (--col); 1044#ifdef ALPHA_BITMAP_READ_WORDS 1045 if (skip_end < pixels) 1046 { 1047 pixels -= skip_end; 1048 data >>= skip_end * ALPHA_COLOR_LOOKUP_SHIFT; 1049 } else { 1050 pixels = skip_end - pixels; 1051 src_w += pixels / ALPHA_COLOR_PIXEL_PER_WORD; 1052 pixels %= ALPHA_COLOR_PIXEL_PER_WORD; 1053 data = letoh32(*src_w++); 1054 data >>= pixels * ALPHA_COLOR_LOOKUP_SHIFT; 1055 pixels = 8 - pixels; 1056 } 1057#else 1058 if (skip_end) 1059 { 1060 pixels += skip_end; 1061 if (pixels >= ALPHA_COLOR_PIXEL_PER_BYTE) 1062 { 1063 src += pixels / ALPHA_COLOR_PIXEL_PER_BYTE; 1064 pixels %= ALPHA_COLOR_PIXEL_PER_BYTE; 1065 data = *src; 1066 data >>= pixels * ALPHA_COLOR_LOOKUP_SHIFT; 1067 } else 1068 data >>= skip_end * ALPHA_COLOR_LOOKUP_SHIFT; 1069 } 1070#endif 1071 } while (--row); 1072} 1073 1074static void draw_putsxy_oriented(int x, int y, const char *str) 1075{ 1076 ucschar_t ch; 1077 ucschar_t *ucs; 1078 int ofs = MIN(x, 0); 1079 struct font* pf = rb->font_get(osd.font); 1080 1081 ucs = rb->bidi_l2v(str, 1); 1082 1083 x += osd.x; 1084 y += osd.y; 1085 1086 while ((ch = *ucs++) != 0 && x < SCREEN_WIDTH) 1087 { 1088 int width; 1089 const unsigned char *bits; 1090 1091 /* get proportional width and glyph bits */ 1092 width = rb->font_get_width(pf, ch); 1093 1094 if (ofs > width) { 1095 ofs -= width; 1096 continue; 1097 } 1098 1099 bits = rb->font_get_bits(pf, ch); 1100 1101 if (pf->depth) 1102 draw_oriented_alpha_bitmap_part(bits, ofs, 0, width, x, y, 1103 width - ofs, pf->height); 1104 else 1105 draw_oriented_mono_bitmap_part(bits, ofs, 0, width, x, y, 1106 width - ofs, pf->height); 1107 1108 x += width - ofs; 1109 ofs = 0; 1110 } 1111} 1112#else 1113static void draw_oriented_mono_bitmap_part(const unsigned char *src, 1114 int src_x, int src_y, 1115 int stride, int x, int y, 1116 int width, int height) 1117{ 1118 int mode = mylcd_get_drawmode(); 1119 mylcd_set_drawmode(DRMODE_FG); 1120 mylcd_mono_bitmap_part(src, src_x, src_y, stride, x, y, width, height); 1121 mylcd_set_drawmode(mode); 1122} 1123 1124static void draw_putsxy_oriented(int x, int y, const char *str) 1125{ 1126 int mode = mylcd_get_drawmode(); 1127 mylcd_set_drawmode(DRMODE_FG); 1128 mylcd_putsxy(x + osd.x, y + osd.y, str); 1129 mylcd_set_drawmode(mode); 1130} 1131#endif /* LCD_PORTRAIT */ 1132 1133/** FPS Display **/ 1134 1135/* Post-frame callback (on video thread) - update the FPS rectangle from the 1136 * framebuffer */ 1137static void fps_post_frame_callback(void) 1138{ 1139 vo_lock(); 1140 mylcd_update_rect(fps.pf_x, fps.pf_y, 1141 fps.pf_width, fps.pf_height); 1142 vo_unlock(); 1143} 1144 1145/* Set up to have the callback only update the intersection of the video 1146 * rectangle and the FPS text rectangle - if they don't intersect, then 1147 * the callback is set to NULL */ 1148static void fps_update_post_frame_callback(void) 1149{ 1150 void (*cb)(void) = NULL; 1151 1152 if (settings.showfps) { 1153 struct vo_rect cliprect; 1154 1155 if (stream_vo_get_clip(&cliprect)) { 1156 /* Oriented screen coordinates -> OSD coordinates */ 1157 vo_rect_offset(&cliprect, -osd.x, -osd.y); 1158 1159 if (vo_rect_intersect(&cliprect, &cliprect, &fps.rect)) { 1160 int x = cliprect.l; 1161 int y = cliprect.t; 1162 int width = cliprect.r - cliprect.l; 1163 int height = cliprect.b - cliprect.t; 1164 1165 /* OSD coordinates -> framebuffer coordinates */ 1166 fps.pf_x = __X; 1167 fps.pf_y = __Y; 1168 fps.pf_width = __W; 1169 fps.pf_height = __H; 1170 1171 cb = fps_post_frame_callback; 1172 } 1173 } 1174 } 1175 1176 stream_set_callback(VIDEO_SET_POST_FRAME_CALLBACK, cb); 1177} 1178 1179/* Refresh the FPS display */ 1180static void fps_refresh(void) 1181{ 1182 char str[FPS_BUFSIZE]; 1183 struct video_output_stats stats; 1184 int w, h, sw; 1185 long tick; 1186 1187 tick = *rb->current_tick; 1188 1189 if (TIME_BEFORE(tick, fps.update_tick)) 1190 return; 1191 1192 fps.update_tick = tick + FPS_UPDATE_INTERVAL; 1193 1194 stream_video_stats(&stats); 1195 1196 rb->snprintf(str, FPS_BUFSIZE, FPS_FORMAT, 1197 stats.fps / 100, stats.fps % 100); 1198 1199 w = fps.rect.r - fps.rect.l; 1200 h = fps.rect.b - fps.rect.t; 1201 1202 draw_clear_area(fps.rect.l, fps.rect.t, w, h); 1203 mylcd_getstringsize(str, &sw, NULL); 1204 draw_putsxy_oriented(fps.rect.r - sw, fps.rect.t, str); 1205 1206 vo_lock(); 1207 draw_update_rect(fps.rect.l, fps.rect.t, w, h); 1208 vo_unlock(); 1209} 1210 1211/* Initialize the FPS display */ 1212static void fps_init(void) 1213{ 1214 fps.update_tick = *rb->current_tick; 1215 fps.rect.l = fps.rect.t = 0; 1216 mylcd_getstringsize(FPS_DIMSTR, &fps.rect.r, &fps.rect.b); 1217 vo_rect_offset(&fps.rect, -osd.x, -osd.y); 1218 fps_update_post_frame_callback(); 1219} 1220 1221/** OSD **/ 1222 1223#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP) 1224/* So we can refresh the overlay */ 1225static void osd_lcd_enable_hook(unsigned short id, void* param) 1226{ 1227 (void)id; 1228 (void)param; 1229 rb->button_queue_post(LCD_ENABLE_EVENT_1, 0); 1230} 1231#endif 1232 1233static void osdbacklight_hw_on_video_mode(bool video_on) 1234{ 1235 if (video_on) { 1236 /* Turn off backlight timeout */ 1237 backlight_ignore_timeout(); 1238 1239#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP) 1240 rb->remove_event(LCD_EVENT_ACTIVATION, osd_lcd_enable_hook); 1241#endif 1242 } else { 1243#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP) 1244 rb->add_event(LCD_EVENT_ACTIVATION, osd_lcd_enable_hook); 1245#endif 1246 /* Revert to user's backlight settings */ 1247 backlight_use_settings(); 1248 } 1249} 1250 1251#ifdef HAVE_BACKLIGHT_BRIGHTNESS 1252static void osd_backlight_brightness_video_mode(bool video_on) 1253{ 1254 if (settings.backlight_brightness < 0) 1255 return; 1256 1257 mpeg_backlight_update_brightness( 1258 video_on ? settings.backlight_brightness : -1); 1259} 1260#else 1261#define osd_backlight_brightness_video_mode(video_on) 1262#endif /* HAVE_BACKLIGHT_BRIGHTNESS */ 1263 1264static void osd_text_init(void) 1265{ 1266 struct hms hms; 1267 char buf[32]; 1268 int phys; 1269 int spc_width; 1270 1271 draw_setfont(FONT_UI); 1272 1273 osd.x = 0; 1274 osd.width = SCREEN_WIDTH; 1275 1276 vo_rect_clear(&osd.time_rect); 1277 vo_rect_clear(&osd.stat_rect); 1278 vo_rect_clear(&osd.prog_rect); 1279 vo_rect_clear(&osd.vol_rect); 1280 1281 ts_to_hms(stream_get_duration(), &hms); 1282 hms_format(buf, sizeof (buf), &hms); 1283 mylcd_getstringsize(buf, &osd.time_rect.r, &osd.time_rect.b); 1284 1285 /* Choose well-sized bitmap images relative to font height */ 1286 if (osd.time_rect.b < 12) { 1287 osd.icons = mpegplayer_status_icons_8x8x1; 1288 osd.stat_rect.r = osd.stat_rect.b = 8; 1289 } else if (osd.time_rect.b < 16) { 1290 osd.icons = mpegplayer_status_icons_12x12x1; 1291 osd.stat_rect.r = osd.stat_rect.b = 12; 1292 } else { 1293 osd.icons = mpegplayer_status_icons_16x16x1; 1294 osd.stat_rect.r = osd.stat_rect.b = 16; 1295 } 1296 1297 if (osd.stat_rect.b < osd.time_rect.b) { 1298 vo_rect_offset(&osd.stat_rect, 0, 1299 (osd.time_rect.b - osd.stat_rect.b) / 2 + OSD_BDR_T); 1300 vo_rect_offset(&osd.time_rect, OSD_BDR_L, OSD_BDR_T); 1301 } else { 1302 vo_rect_offset(&osd.time_rect, OSD_BDR_L, 1303 osd.stat_rect.b - osd.time_rect.b + OSD_BDR_T); 1304 vo_rect_offset(&osd.stat_rect, 0, OSD_BDR_T); 1305 } 1306 1307 osd.dur_rect = osd.time_rect; 1308 1309 phys = rb->sound_val2phys(SOUND_VOLUME, rb->sound_min(SOUND_VOLUME)); 1310 rb->snprintf(buf, sizeof(buf), "%d%s", phys, 1311 rb->sound_unit(SOUND_VOLUME)); 1312 1313 mylcd_getstringsize(" ", &spc_width, NULL); 1314 mylcd_getstringsize(buf, &osd.vol_rect.r, &osd.vol_rect.b); 1315 1316 osd.prog_rect.r = SCREEN_WIDTH - OSD_BDR_L - spc_width - 1317 osd.vol_rect.r - OSD_BDR_R; 1318 osd.prog_rect.b = 3*osd.stat_rect.b / 4; 1319 vo_rect_offset(&osd.prog_rect, osd.time_rect.l, 1320 osd.time_rect.b); 1321 1322 vo_rect_offset(&osd.stat_rect, 1323 (osd.prog_rect.r + osd.prog_rect.l - osd.stat_rect.r) / 2, 1324 0); 1325 1326 vo_rect_offset(&osd.dur_rect, 1327 osd.prog_rect.r - osd.dur_rect.r, 0); 1328 1329 vo_rect_offset(&osd.vol_rect, osd.prog_rect.r + spc_width, 1330 (osd.prog_rect.b + osd.prog_rect.t - osd.vol_rect.b) / 2); 1331 1332 osd.height = OSD_BDR_T + MAX(osd.prog_rect.b, osd.vol_rect.b) - 1333 MIN(osd.time_rect.t, osd.stat_rect.t) + OSD_BDR_B; 1334 1335#ifdef HAVE_LCD_COLOR 1336 osd.height = ALIGN_UP(osd.height, 2); 1337#endif 1338 osd.y = SCREEN_HEIGHT - osd.height; 1339 1340 draw_setfont(FONT_SYSFIXED); 1341} 1342 1343static void osd_init(void) 1344{ 1345 osd.flags = 0; 1346 osd.show_for = HZ*4; 1347 osd.print_delay = 75*HZ/100; 1348 osd.resume_delay = HZ/2; 1349#ifdef HAVE_LCD_COLOR 1350 osd.bgcolor = LCD_RGBPACK(0x73, 0x75, 0xbd); 1351 osd.fgcolor = LCD_WHITE; 1352 osd.prog_fillcolor = LCD_BLACK; 1353#else 1354 osd.bgcolor = GREY_LIGHTGRAY; 1355 osd.fgcolor = GREY_BLACK; 1356 osd.prog_fillcolor = GREY_WHITE; 1357#endif 1358 osd.curr_time = 0; 1359 osd.status = OSD_STATUS_STOPPED; 1360 osd.auto_refresh = OSD_REFRESH_TIME; 1361 osd.next_auto_refresh = *rb->current_tick; 1362 osd_text_init(); 1363 fps_init(); 1364} 1365 1366#ifdef HAVE_HEADPHONE_DETECTION 1367static void osd_set_hp_pause_flag(bool set) 1368{ 1369 if (set) 1370 osd.flags |= OSD_HP_PAUSE; 1371 else 1372 osd.flags &= ~OSD_HP_PAUSE; 1373} 1374#else 1375#define osd_set_hp_pause_flag(set) 1376#endif /* HAVE_HEADPHONE_DETECTION */ 1377 1378static void osd_schedule_refresh(unsigned refresh) 1379{ 1380 long tick = *rb->current_tick; 1381 1382 if (refresh & OSD_REFRESH_VIDEO) 1383 osd.print_tick = tick + osd.print_delay; 1384 1385 if (refresh & OSD_REFRESH_RESUME) 1386 osd.resume_tick = tick + osd.resume_delay; 1387 1388 osd.auto_refresh |= refresh; 1389} 1390 1391static void osd_cancel_refresh(unsigned refresh) 1392{ 1393 osd.auto_refresh &= ~refresh; 1394} 1395 1396/* Refresh the background area */ 1397static void osd_refresh_background(void) 1398{ 1399 char buf[32]; 1400 struct hms hms; 1401 1402 unsigned bg = mylcd_get_background(); 1403 mylcd_set_drawmode(DRMODE_SOLID | DRMODE_INVERSEVID); 1404 1405#ifdef HAVE_LCD_COLOR 1406 /* Draw a "raised" area for our graphics */ 1407 mylcd_set_background(draw_blendcolor(bg, MYLCD_WHITE, 192)); 1408 draw_hline(0, osd.width, 0); 1409 1410 mylcd_set_background(draw_blendcolor(bg, MYLCD_WHITE, 80)); 1411 draw_hline(0, osd.width, 1); 1412 1413 mylcd_set_background(draw_blendcolor(bg, MYLCD_BLACK, 48)); 1414 draw_hline(0, osd.width, osd.height-2); 1415 1416 mylcd_set_background(draw_blendcolor(bg, MYLCD_BLACK, 128)); 1417 draw_hline(0, osd.width, osd.height-1); 1418 1419 mylcd_set_background(bg); 1420 draw_clear_area(0, 2, osd.width, osd.height - 4); 1421#else 1422 /* Give contrast with the main background */ 1423 mylcd_set_background(MYLCD_WHITE); 1424 draw_hline(0, osd.width, 0); 1425 1426 mylcd_set_background(MYLCD_DARKGRAY); 1427 draw_hline(0, osd.width, osd.height-1); 1428 1429 mylcd_set_background(bg); 1430 draw_clear_area(0, 1, osd.width, osd.height - 2); 1431#endif 1432 1433 vo_rect_set_ext(&osd.update_rect, 0, 0, osd.width, osd.height); 1434 mylcd_set_drawmode(DRMODE_SOLID); 1435 1436 if (stream_get_duration() != INVALID_TIMESTAMP) { 1437 /* Draw the movie duration */ 1438 ts_to_hms(stream_get_duration(), &hms); 1439 hms_format(buf, sizeof (buf), &hms); 1440 draw_putsxy_oriented(osd.dur_rect.l, osd.dur_rect.t, buf); 1441 } 1442 /* else don't know the duration */ 1443} 1444 1445/* Refresh the current time display + the progress bar */ 1446static void osd_refresh_time(void) 1447{ 1448 char buf[32]; 1449 struct hms hms; 1450 1451 uint32_t duration = stream_get_duration(); 1452 1453 draw_scrollbar_draw_rect(&osd.prog_rect, 0, duration, 1454 osd.curr_time); 1455 1456 ts_to_hms(osd.curr_time, &hms); 1457 hms_format(buf, sizeof (buf), &hms); 1458 1459 draw_clear_area_rect(&osd.time_rect); 1460 draw_putsxy_oriented(osd.time_rect.l, osd.time_rect.t, buf); 1461 1462 vo_rect_union(&osd.update_rect, &osd.update_rect, 1463 &osd.prog_rect); 1464 vo_rect_union(&osd.update_rect, &osd.update_rect, 1465 &osd.time_rect); 1466} 1467 1468/* Refresh the volume display area */ 1469static void osd_refresh_volume(void) 1470{ 1471 char buf[32]; 1472 int width; 1473 1474 int volume = rb->global_status->volume; 1475 rb->snprintf(buf, sizeof (buf), "%d%s", 1476 rb->sound_val2phys(SOUND_VOLUME, volume), 1477 rb->sound_unit(SOUND_VOLUME)); 1478 mylcd_getstringsize(buf, &width, NULL); 1479 1480 /* Right-justified */ 1481 draw_clear_area_rect(&osd.vol_rect); 1482 draw_putsxy_oriented(osd.vol_rect.r - width, osd.vol_rect.t, buf); 1483 1484 vo_rect_union(&osd.update_rect, &osd.update_rect, &osd.vol_rect); 1485} 1486 1487/* Refresh the status icon */ 1488static void osd_refresh_status(void) 1489{ 1490 int icon_size = osd.stat_rect.r - osd.stat_rect.l; 1491 1492 draw_clear_area_rect(&osd.stat_rect); 1493 1494#ifdef HAVE_LCD_COLOR 1495 /* Draw status icon with a drop shadow */ 1496 unsigned oldfg = mylcd_get_foreground(); 1497 int i = 1; 1498 1499 mylcd_set_foreground(draw_blendcolor(mylcd_get_background(), 1500 MYLCD_BLACK, 96)); 1501 1502 while (1) 1503 { 1504 draw_oriented_mono_bitmap_part(osd.icons, 1505 icon_size*osd.status, 1506 0, 1507 icon_size*OSD_STATUS_COUNT, 1508 osd.stat_rect.l + osd.x + i, 1509 osd.stat_rect.t + osd.y + i, 1510 icon_size, icon_size); 1511 1512 if (--i < 0) 1513 break; 1514 1515 mylcd_set_foreground(oldfg); 1516 } 1517 1518 vo_rect_union(&osd.update_rect, &osd.update_rect, &osd.stat_rect); 1519#else 1520 draw_oriented_mono_bitmap_part(osd.icons, 1521 icon_size*osd.status, 1522 0, 1523 icon_size*OSD_STATUS_COUNT, 1524 osd.stat_rect.l + osd.x, 1525 osd.stat_rect.t + osd.y, 1526 icon_size, icon_size); 1527 vo_rect_union(&osd.update_rect, &osd.update_rect, &osd.stat_rect); 1528#endif 1529} 1530 1531/* Update the current status which determines which icon is displayed */ 1532static bool osd_update_status(void) 1533{ 1534 int status; 1535 1536 switch (stream_status()) 1537 { 1538 default: 1539 status = OSD_STATUS_STOPPED; 1540 break; 1541 case STREAM_PAUSED: 1542 /* If paused with a pending resume, coerce it to OSD_STATUS_PLAYING */ 1543 status = (osd.auto_refresh & OSD_REFRESH_RESUME) ? 1544 OSD_STATUS_PLAYING : OSD_STATUS_PAUSED; 1545 break; 1546 case STREAM_PLAYING: 1547 status = OSD_STATUS_PLAYING; 1548 break; 1549 } 1550 1551 if (status != osd.status) { 1552 /* A refresh is needed */ 1553 osd.status = status; 1554 return true; 1555 } 1556 1557 return false; 1558} 1559 1560/* Update the current time that will be displayed */ 1561static void osd_update_time(void) 1562{ 1563 uint32_t start; 1564 osd.curr_time = stream_get_seek_time(&start); 1565 osd.curr_time -= start; 1566} 1567 1568/* Refresh various parts of the OSD - showing it if it is hidden */ 1569static void osd_refresh(int hint) 1570{ 1571 long tick; 1572 unsigned oldbg, oldfg; 1573 1574 tick = *rb->current_tick; 1575 1576 if (settings.showfps) 1577 fps_refresh(); 1578 1579 if (hint == OSD_REFRESH_DEFAULT) { 1580 /* The default which forces no updates */ 1581 1582 /* Make sure Rockbox doesn't turn off the player because of 1583 too little activity */ 1584 if (osd.status == OSD_STATUS_PLAYING) 1585 rb->reset_poweroff_timer(); 1586 1587 /* Redraw the current or possibly extract a new video frame */ 1588 if ((osd.auto_refresh & OSD_REFRESH_VIDEO) && 1589 TIME_AFTER(tick, osd.print_tick)) { 1590 osd.auto_refresh &= ~OSD_REFRESH_VIDEO; 1591 stream_draw_frame(false); 1592 } 1593 1594 /* Restart playback if the timout was reached */ 1595 if ((osd.auto_refresh & OSD_REFRESH_RESUME) && 1596 TIME_AFTER(tick, osd.resume_tick)) { 1597 osd.auto_refresh &= ~(OSD_REFRESH_RESUME | OSD_REFRESH_VIDEO); 1598 stream_resume(); 1599 } 1600 1601 /* If not visible, return */ 1602 if (!(osd.flags & OSD_SHOW)) 1603 return; 1604 1605 /* Hide if the visibility duration was reached */ 1606 if (TIME_AFTER(tick, osd.hide_tick)) { 1607 osd_show(OSD_HIDE); 1608 return; 1609 } 1610 } else { 1611 /* A forced update of some region */ 1612 1613 /* Show if currently invisible */ 1614 if (!(osd.flags & OSD_SHOW)) { 1615 /* Avoid call back into this function - it will be drawn */ 1616 osd_show(OSD_SHOW | OSD_NODRAW); 1617 hint = OSD_REFRESH_ALL; 1618 } 1619 1620 /* Move back timeouts for frame print and hide */ 1621 osd.print_tick = tick + osd.print_delay; 1622 osd.hide_tick = tick + osd.show_for; 1623 } 1624 1625 if (TIME_AFTER(tick, osd.next_auto_refresh)) { 1626 /* Refresh whatever graphical elements are due automatically */ 1627 osd.next_auto_refresh = tick + OSD_MIN_UPDATE_INTERVAL; 1628 1629 if (osd.auto_refresh & OSD_REFRESH_STATUS) { 1630 if (osd_update_status()) 1631 hint |= OSD_REFRESH_STATUS; 1632 } 1633 1634 if (osd.auto_refresh & OSD_REFRESH_TIME) { 1635 osd_update_time(); 1636 hint |= OSD_REFRESH_TIME; 1637 } 1638 } 1639 1640 if (hint == 0) 1641 return; /* No drawing needed */ 1642 1643 /* Set basic drawing params that are used. Elements that perform variations 1644 * will restore them. */ 1645 oldfg = mylcd_get_foreground(); 1646 oldbg = mylcd_get_background(); 1647 1648 draw_setfont(FONT_UI); 1649 mylcd_set_foreground(osd.fgcolor); 1650 mylcd_set_background(osd.bgcolor); 1651 1652 vo_rect_clear(&osd.update_rect); 1653 1654 if (hint & OSD_REFRESH_BACKGROUND) { 1655 osd_refresh_background(); 1656 hint |= OSD_REFRESH_ALL; /* Requires a redraw of everything */ 1657 } 1658 1659 if (hint & OSD_REFRESH_TIME) { 1660 osd_refresh_time(); 1661 } 1662 1663 if (hint & OSD_REFRESH_VOLUME) { 1664 osd_refresh_volume(); 1665 } 1666 1667 if (hint & OSD_REFRESH_STATUS) { 1668 osd_refresh_status(); 1669 } 1670 1671 /* Go back to defaults */ 1672 draw_setfont(FONT_SYSFIXED); 1673 mylcd_set_foreground(oldfg); 1674 mylcd_set_background(oldbg); 1675 1676 /* Update the dirty rectangle */ 1677 vo_lock(); 1678 1679 draw_update_rect(osd.update_rect.l, 1680 osd.update_rect.t, 1681 osd.update_rect.r - osd.update_rect.l, 1682 osd.update_rect.b - osd.update_rect.t); 1683 1684 vo_unlock(); 1685} 1686 1687/* Show/Hide the OSD */ 1688static void osd_show(unsigned show) 1689{ 1690 if (((show ^ osd.flags) & OSD_SHOW) == 0) 1691 { 1692 if (show & OSD_SHOW) { 1693 osd.hide_tick = *rb->current_tick + osd.show_for; 1694 } 1695 return; 1696 } 1697 1698 if (show & OSD_SHOW) { 1699 /* Clip away the part of video that is covered */ 1700 struct vo_rect rc = { 0, 0, SCREEN_WIDTH, osd.y }; 1701 1702 osd.flags |= OSD_SHOW; 1703 1704 if (osd.status != OSD_STATUS_PLAYING) { 1705 /* Not playing - set brightness to mpegplayer setting */ 1706 osd_backlight_brightness_video_mode(true); 1707 } 1708 1709 stream_vo_set_clip(&rc); 1710 1711 if (!(show & OSD_NODRAW)) 1712 osd_refresh(OSD_REFRESH_ALL); 1713 } else { 1714 /* Uncover clipped video area and redraw it */ 1715 osd.flags &= ~OSD_SHOW; 1716 1717 draw_clear_area(0, 0, osd.width, osd.height); 1718 1719 if (!(show & OSD_NODRAW)) { 1720 vo_lock(); 1721 draw_update_rect(0, 0, osd.width, osd.height); 1722 vo_unlock(); 1723 1724 stream_vo_set_clip(NULL); 1725 stream_draw_frame(false); 1726 } else { 1727 stream_vo_set_clip(NULL); 1728 } 1729 1730 if (osd.status != OSD_STATUS_PLAYING) { 1731 /* Not playing - restore backlight brightness */ 1732 osd_backlight_brightness_video_mode(false); 1733 } 1734 } 1735} 1736 1737/* Set the current status - update screen if specified */ 1738static void osd_set_status(int status) 1739{ 1740 bool draw = (status & OSD_NODRAW) == 0; 1741 1742 status &= OSD_STATUS_MASK; 1743 1744 if (osd.status != status) { 1745 1746 osd.status = status; 1747 1748 if (draw) 1749 osd_refresh(OSD_REFRESH_STATUS); 1750 } 1751} 1752 1753/* Get the current status value */ 1754static int osd_get_status(void) 1755{ 1756 return osd.status & OSD_STATUS_MASK; 1757} 1758 1759/* Handle Fast-forward/Rewind keys using WPS settings (and some nicked code ;) 1760 * Returns last button code 1761 */ 1762static int osd_ff_rw(int btn, unsigned refresh, uint32_t *new_time) 1763{ 1764 unsigned int step = TS_SECOND*rb->global_settings->ff_rewind_min_step; 1765 const long ff_rw_accel = (rb->global_settings->ff_rewind_accel + 3); 1766 uint32_t start; 1767 uint32_t time = stream_get_seek_time(&start); 1768 const uint32_t duration = stream_get_duration(); 1769 unsigned int max_step = 0; 1770 uint32_t ff_rw_count = 0; 1771 unsigned status = osd.status; 1772 int new_btn; 1773 1774 osd_cancel_refresh(OSD_REFRESH_VIDEO | OSD_REFRESH_RESUME | 1775 OSD_REFRESH_TIME); 1776 1777 time -= start; /* Absolute clock => stream-relative */ 1778 1779 switch (btn) 1780 { 1781 case MPEG_FF: 1782#ifdef MPEG_FF2 1783 case MPEG_FF2: 1784#endif 1785#ifdef MPEG_RC_FF 1786 case MPEG_RC_FF: 1787#endif 1788 osd_set_status(OSD_STATUS_FF); 1789 new_btn = btn | BUTTON_REPEAT; /* simplify code below */ 1790 break; 1791 case MPEG_RW: 1792#ifdef MPEG_RW2 1793 case MPEG_RW2: 1794#endif 1795#ifdef MPEG_RC_RW 1796 case MPEG_RC_RW: 1797#endif 1798 osd_set_status(OSD_STATUS_RW); 1799 new_btn = btn | BUTTON_REPEAT; /* simplify code below */ 1800 break; 1801 default: 1802 new_btn = BUTTON_NONE; /* Fail tests below but still do proper exit */ 1803 } 1804 1805 while (1) 1806 { 1807 stream_keep_disk_active(); 1808 1809 if (new_btn == (btn | BUTTON_REPEAT)) { 1810 if (osd.status == OSD_STATUS_FF) { 1811 /* fast forwarding, calc max step relative to end */ 1812 max_step = muldiv_uint32(duration - (time + ff_rw_count), 1813 FF_REWIND_MAX_PERCENT, 100); 1814 } else { 1815 /* rewinding, calc max step relative to start */ 1816 max_step = muldiv_uint32(time - ff_rw_count, 1817 FF_REWIND_MAX_PERCENT, 100); 1818 } 1819 1820 max_step = MAX(max_step, MIN_FF_REWIND_STEP); 1821 1822 if (step > max_step) 1823 step = max_step; 1824 1825 ff_rw_count += step; 1826 1827 /* smooth seeking by multiplying step by: 1 + (2 ^ -accel) */ 1828 step += step >> ff_rw_accel; 1829 1830 if (osd.status == OSD_STATUS_FF) { 1831 if (duration - time <= ff_rw_count) 1832 ff_rw_count = duration - time; 1833 1834 osd.curr_time = time + ff_rw_count; 1835 } else { 1836 if (time <= ff_rw_count) 1837 ff_rw_count = time; 1838 1839 osd.curr_time = time - ff_rw_count; 1840 } 1841 1842 osd_refresh(OSD_REFRESH_TIME); 1843 1844 new_btn = mpeg_button_get(TIMEOUT_BLOCK); 1845 } 1846 else { 1847 if (new_btn == (btn | BUTTON_REL)) { 1848 if (osd.status == OSD_STATUS_FF) 1849 time += ff_rw_count; 1850 else if (osd.status == OSD_STATUS_RW) 1851 time -= ff_rw_count; 1852 } 1853 1854 *new_time = time; 1855 1856 osd_schedule_refresh(refresh); 1857 osd_set_status(status); 1858 osd_schedule_refresh(OSD_REFRESH_TIME); 1859 1860 return new_btn; 1861 } 1862 } 1863} 1864 1865/* Return adjusted STREAM_* status */ 1866static int osd_stream_status(void) 1867{ 1868 int status = stream_status(); 1869 1870 /* Coerce to STREAM_PLAYING if paused with a pending resume */ 1871 if (status == STREAM_PAUSED) { 1872 if (osd.auto_refresh & OSD_REFRESH_RESUME) 1873 status = STREAM_PLAYING; 1874 } 1875 1876 return status; 1877} 1878 1879/* Change the current audio volume by a specified amount */ 1880static void osd_set_volume(int delta) 1881{ 1882 int vol = rb->global_status->volume; 1883 int limit; 1884 1885 vol += delta; 1886 1887 if (delta < 0) { 1888 /* Volume down - clip to lower limit */ 1889 limit = rb->sound_min(SOUND_VOLUME); 1890 if (vol < limit) 1891 vol = limit; 1892 } else { 1893 /* Volume up - clip to upper limit */ 1894 limit = rb->sound_max(SOUND_VOLUME); 1895 if (vol > limit) 1896 vol = limit; 1897 } 1898 1899 /* Sync the global settings */ 1900 if (vol != rb->global_status->volume) 1901 rb->sound_set(SOUND_VOLUME, vol); 1902 1903 /* Update the volume display */ 1904 osd_refresh(OSD_REFRESH_VOLUME); 1905} 1906 1907/* Begin playback at the specified time */ 1908static int osd_play(uint32_t time) 1909{ 1910 int retval; 1911 1912 osd_set_hp_pause_flag(false); 1913 osd_cancel_refresh(OSD_REFRESH_VIDEO | OSD_REFRESH_RESUME); 1914 1915 retval = stream_seek(time, SEEK_SET); 1916 1917 if (retval >= STREAM_OK) { 1918 osdbacklight_hw_on_video_mode(true); 1919 osd_backlight_brightness_video_mode(true); 1920 stream_show_vo(true); 1921 1922 retval = stream_play(); 1923 1924 if (retval >= STREAM_OK) 1925 osd_set_status(OSD_STATUS_PLAYING | OSD_NODRAW); 1926 } 1927 1928 return retval; 1929} 1930 1931/* Halt playback - pause engine and return logical state */ 1932static int osd_halt(void) 1933{ 1934 int status = stream_pause(); 1935 1936 /* Coerce to STREAM_PLAYING if paused with a pending resume */ 1937 if (status == STREAM_PAUSED) { 1938 if (osd_get_status() == OSD_STATUS_PLAYING) 1939 status = STREAM_PLAYING; 1940 } 1941 1942 /* Cancel some auto refreshes - caller will restart them if desired */ 1943 osd_cancel_refresh(OSD_REFRESH_VIDEO | OSD_REFRESH_RESUME); 1944 1945 /* No backlight fiddling here - callers does the right thing */ 1946 1947 return status; 1948} 1949 1950/* Pause playback if playing */ 1951static int osd_pause(void) 1952{ 1953 unsigned refresh = osd.auto_refresh; 1954 int status = osd_halt(); 1955 1956 osd_set_hp_pause_flag(false); 1957 1958 if (status == STREAM_PLAYING && (refresh & OSD_REFRESH_RESUME)) { 1959 /* Resume pending - change to a still video frame update */ 1960 osd_schedule_refresh(OSD_REFRESH_VIDEO); 1961 } 1962 1963 osd_set_status(OSD_STATUS_PAUSED); 1964 1965 osdbacklight_hw_on_video_mode(false); 1966 /* Leave brightness alone and restore it when OSD is hidden */ 1967 1968 if (stream_can_seek() && rb->global_settings->pause_rewind) { 1969 stream_seek(-rb->global_settings->pause_rewind*TS_SECOND, 1970 SEEK_CUR); 1971 osd_schedule_refresh(OSD_REFRESH_VIDEO); 1972 /* Update time display now */ 1973 osd_update_time(); 1974 osd_refresh(OSD_REFRESH_TIME); 1975 } 1976 1977 return status; 1978} 1979 1980/* Resume playback if halted or paused */ 1981static void osd_resume(void) 1982{ 1983 /* Cancel video and resume auto refresh - the resyc when starting 1984 * playback will perform those tasks */ 1985 osd_set_hp_pause_flag(false); 1986 osdbacklight_hw_on_video_mode(true); 1987 osd_backlight_brightness_video_mode(true); 1988 osd_cancel_refresh(OSD_REFRESH_VIDEO | OSD_REFRESH_RESUME); 1989 osd_set_status(OSD_STATUS_PLAYING); 1990 stream_resume(); 1991} 1992 1993/* Stop playback - remember the resume point if not closed */ 1994static void osd_stop(void) 1995{ 1996 uint32_t resume_time; 1997 1998 osd_set_hp_pause_flag(false); 1999 osd_cancel_refresh(OSD_REFRESH_VIDEO | OSD_REFRESH_RESUME); 2000 osd_set_status(OSD_STATUS_STOPPED | OSD_NODRAW); 2001 osd_show(OSD_HIDE); 2002 2003 stream_stop(); 2004 2005 resume_time = stream_get_resume_time(); 2006 2007 if (resume_time != INVALID_TIMESTAMP) 2008 settings.resume_time = resume_time; 2009 2010 osdbacklight_hw_on_video_mode(false); 2011 osd_backlight_brightness_video_mode(false); 2012} 2013 2014/* Perform a seek by button if seeking is possible for this stream. 2015 * 2016 * A delay will be inserted before restarting in case the user decides to 2017 * seek again soon after. 2018 * 2019 * Returns last button code 2020 */ 2021static int osd_seek_btn(int btn) 2022{ 2023 int status; 2024 unsigned refresh = 0; 2025 uint32_t time; 2026 2027 if (!stream_can_seek()) 2028 return true; 2029 2030 /* Halt playback - not strictly necessary but nice when doing 2031 * buttons */ 2032 status = osd_halt(); 2033 2034 if (status == STREAM_STOPPED) 2035 return true; 2036 2037 osd_show(OSD_SHOW); 2038 2039 /* Obtain a new playback point according to the buttons */ 2040 if (status == STREAM_PLAYING) 2041 refresh = OSD_REFRESH_RESUME; /* delay resume if playing */ 2042 else 2043 refresh = OSD_REFRESH_VIDEO; /* refresh if paused */ 2044 2045 btn = osd_ff_rw(btn, refresh, &time); 2046 2047 /* Tell engine to resume at that time */ 2048 stream_seek(time, SEEK_SET); 2049 2050 return btn; 2051} 2052 2053/* Perform a seek by time if seeking is possible for this stream 2054 * 2055 * If playing, the seeking is immediate, otherise a delay is added to showing 2056 * a still if paused in case the user does another seek soon after. 2057 * 2058 * If seeking isn't possible, a time of zero performs a skip to the 2059 * beginning. 2060 */ 2061static void osd_seek_time(uint32_t time) 2062{ 2063 int status; 2064 unsigned refresh = 0; 2065 2066 if (!stream_can_seek() && time != 0) 2067 return; 2068 2069 stream_wait_status(); 2070 status = osd_stream_status(); 2071 2072 if (status == STREAM_STOPPED) 2073 return; 2074 2075 if (status == STREAM_PLAYING) /* merely preserve resume */ 2076 refresh = osd.auto_refresh & OSD_REFRESH_RESUME; 2077 else 2078 refresh = OSD_REFRESH_VIDEO; /* refresh if paused */ 2079 2080 /* Cancel print or resume if pending */ 2081 osd_cancel_refresh(OSD_REFRESH_VIDEO | OSD_REFRESH_RESUME); 2082 2083 /* Tell engine to seek to the given time - no state change */ 2084 stream_seek(time, SEEK_SET); 2085 2086 osd_update_time(); 2087 osd_refresh(OSD_REFRESH_TIME); 2088 osd_schedule_refresh(refresh); 2089} 2090 2091/* Has this file one of the supported extensions? */ 2092static bool is_videofile(const char* file) 2093{ 2094 static const char * const extensions[] = 2095 { 2096 /* Should match apps/plugins/viewers.config */ 2097 "mpg", "mpeg", "mpv", "m2v" 2098 }; 2099 2100 const char* ext = rb->strrchr(file, '.'); 2101 int i; 2102 2103 if (!ext) 2104 return false; 2105 2106 for (i = ARRAYLEN(extensions) - 1; i >= 0; i--) 2107 { 2108 if (!rb->strcasecmp(ext + 1, extensions[i])) 2109 break; 2110 } 2111 2112 return i >= 0; 2113} 2114 2115/* deliver the next/previous video file in the current directory. 2116 returns false if there is none. */ 2117static bool get_videofile(int direction, char* videofile, size_t bufsize) 2118{ 2119 struct tree_context *tree = rb->tree_get_context(); 2120 struct entry *dircache = rb->tree_get_entries(tree); 2121 int i, step, end, found = 0; 2122 char *videoname = rb->strrchr(videofile, '/') + 1; 2123 size_t rest = bufsize - (videoname - videofile) - 1; 2124 2125 if (direction == VIDEO_NEXT) { 2126 i = 0; 2127 step = 1; 2128 end = tree->filesindir; 2129 } else { 2130 i = tree->filesindir-1; 2131 step = -1; 2132 end = -1; 2133 } 2134 for (; i != end; i += step) 2135 { 2136 const char* name = dircache[i].name; 2137 if (!rb->strcmp(name, videoname)) { 2138 found = 1; 2139 continue; 2140 } 2141 if (found && rb->strlen(name) <= rest && 2142 !(dircache[i].attr & ATTR_DIRECTORY) && is_videofile(name)) 2143 { 2144 rb->strcpy(videoname, name); 2145 return true; 2146 } 2147 } 2148 2149 return false; 2150} 2151 2152#ifdef HAVE_HEADPHONE_DETECTION 2153/* Handle SYS_PHONE_PLUGGED/UNPLUGGED */ 2154static void osd_handle_phone_plug(bool inserted) 2155{ 2156 if (rb->global_settings->unplug_mode == 0) 2157 return; 2158 2159 /* Wait for any incomplete state transition to complete first */ 2160 stream_wait_status(); 2161 2162 int status = osd_stream_status(); 2163 2164 if (inserted) { 2165 if (rb->global_settings->unplug_mode > 1) { 2166 if (status == STREAM_PAUSED && 2167 (osd.flags & OSD_HP_PAUSE)) { 2168 osd_resume(); 2169 } 2170 } 2171 } else { 2172 if (status == STREAM_PLAYING) { 2173 osd_pause(); 2174 2175 osd_set_hp_pause_flag(true); 2176 } 2177 } 2178} 2179#endif 2180 2181static int button_loop(void) 2182{ 2183 int next_action = (settings.play_mode == 0) ? VIDEO_STOP : VIDEO_NEXT; 2184 2185 rb->lcd_setfont(FONT_SYSFIXED); 2186#ifdef HAVE_LCD_COLOR 2187 rb->lcd_set_foreground(LCD_WHITE); 2188 rb->lcd_set_background(LCD_BLACK); 2189#endif 2190 rb->lcd_clear_display(); 2191 rb->lcd_update(); 2192 2193#if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_YUV) 2194 rb->lcd_set_mode(LCD_MODE_YUV); 2195#endif 2196 2197 osd_init(); 2198 2199 /* Start playback at the specified starting time */ 2200 if (osd_play(settings.resume_time) < STREAM_OK) { 2201 rb->splash(HZ*2, "Playback failed"); 2202 return VIDEO_STOP; 2203 } 2204 2205 /* Gently poll the video player for EOS and handle UI */ 2206 while (stream_status() != STREAM_STOPPED) 2207 { 2208 int button = mpeg_button_get(OSD_MIN_UPDATE_INTERVAL/2); 2209 2210 switch (button) 2211 { 2212 case BUTTON_NONE: 2213 { 2214 osd_refresh(OSD_REFRESH_DEFAULT); 2215 continue; 2216 } /* BUTTON_NONE: */ 2217 2218#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP) 2219 case LCD_ENABLE_EVENT_1: 2220 { 2221 /* Draw the current frame if prepared already */ 2222 stream_draw_frame(true); 2223 break; 2224 } /* LCD_ENABLE_EVENT_1: */ 2225#endif 2226 2227 case MPEG_VOLUP: 2228 case MPEG_VOLUP|BUTTON_REPEAT: 2229#ifdef MPEG_VOLUP2 2230 case MPEG_VOLUP2: 2231 case MPEG_VOLUP2|BUTTON_REPEAT: 2232#endif 2233#ifdef MPEG_RC_VOLUP 2234 case MPEG_RC_VOLUP: 2235 case MPEG_RC_VOLUP|BUTTON_REPEAT: 2236#endif 2237 { 2238 osd_set_volume(+1); 2239 break; 2240 } /* MPEG_VOLUP*: */ 2241 2242 case MPEG_VOLDOWN: 2243 case MPEG_VOLDOWN|BUTTON_REPEAT: 2244#ifdef MPEG_VOLDOWN2 2245 case MPEG_VOLDOWN2: 2246 case MPEG_VOLDOWN2|BUTTON_REPEAT: 2247#endif 2248#ifdef MPEG_RC_VOLDOWN 2249 case MPEG_RC_VOLDOWN: 2250 case MPEG_RC_VOLDOWN|BUTTON_REPEAT: 2251#endif 2252 { 2253 osd_set_volume(-1); 2254 break; 2255 } /* MPEG_VOLDOWN*: */ 2256 2257 case MPEG_MENU: 2258#ifdef MPEG_RC_MENU 2259 case MPEG_RC_MENU: 2260#endif 2261 { 2262 int state = osd_halt(); /* save previous state */ 2263 int result; 2264 2265 /* Hide video output */ 2266 osd_show(OSD_HIDE | OSD_NODRAW); 2267 stream_show_vo(false); 2268 osd_backlight_brightness_video_mode(false); 2269 2270#if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_YUV) 2271 rb->lcd_set_mode(LCD_MODE_RGB565); 2272#endif 2273 2274 result = mpeg_menu(); 2275 2276 next_action = (settings.play_mode == 0) ? VIDEO_STOP : VIDEO_NEXT; 2277 2278 fps_update_post_frame_callback(); 2279 2280 /* The menu can change the font, so restore */ 2281 rb->lcd_setfont(FONT_SYSFIXED); 2282#ifdef HAVE_LCD_COLOR 2283 rb->lcd_set_foreground(LCD_WHITE); 2284 rb->lcd_set_background(LCD_BLACK); 2285#endif 2286 rb->lcd_clear_display(); 2287 rb->lcd_update(); 2288 2289 switch (result) 2290 { 2291 case MPEG_MENU_QUIT: 2292 next_action = VIDEO_STOP; 2293 osd_stop(); 2294 break; 2295 2296 default: 2297#if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_YUV) 2298 rb->lcd_set_mode(LCD_MODE_YUV); 2299#endif 2300 /* If not stopped, show video again */ 2301 if (state != STREAM_STOPPED) { 2302 osd_show(OSD_SHOW); 2303 stream_show_vo(true); 2304 } 2305 2306 /* If stream was playing, restart it */ 2307 if (state == STREAM_PLAYING) { 2308 osd_resume(); 2309 } 2310 break; 2311 } 2312 break; 2313 } /* MPEG_MENU: */ 2314 2315#ifdef MPEG_SHOW_OSD 2316 case MPEG_SHOW_OSD: 2317 case MPEG_SHOW_OSD | BUTTON_REPEAT: 2318 /* Show if not visible */ 2319 osd_show(OSD_SHOW); 2320 /* Make sure it refreshes */ 2321 osd_refresh(OSD_REFRESH_DEFAULT); 2322 break; 2323#endif 2324 2325 case MPEG_STOP: 2326#ifdef MPEG_RC_STOP 2327 case MPEG_RC_STOP: 2328#endif 2329 case ACTION_STD_CANCEL: 2330 { 2331 cancel_playback: 2332 next_action = VIDEO_STOP; 2333 osd_stop(); 2334 break; 2335 } /* MPEG_STOP: */ 2336 2337 case MPEG_PAUSE: 2338#ifdef MPEG_PAUSE2 2339 case MPEG_PAUSE2: 2340#endif 2341#ifdef MPEG_RC_PAUSE 2342 case MPEG_RC_PAUSE: 2343#endif 2344 { 2345 int status = osd_stream_status(); 2346 2347 if (status == STREAM_PLAYING) { 2348 /* Playing => Paused */ 2349 osd_pause(); 2350 } 2351 else if (status == STREAM_PAUSED) { 2352 /* Paused => Playing */ 2353 osd_resume(); 2354 } 2355 2356 break; 2357 } /* MPEG_PAUSE*: */ 2358 2359 case MPEG_RW: 2360#ifdef MPEG_RW2 2361 case MPEG_RW2: 2362#endif 2363#ifdef MPEG_RC_RW 2364 case MPEG_RC_RW: 2365#endif 2366 { 2367 int old_button = button; 2368 2369 /* If button has been released: skip to next/previous file */ 2370 button = mpeg_button_get(OSD_MIN_UPDATE_INTERVAL); 2371 2372 if ((old_button | BUTTON_REL) == button) { 2373 /* Check current playback position */ 2374 osd_update_time(); 2375 2376 if (settings.play_mode == 0 || osd.curr_time >= 3*TS_SECOND) { 2377 /* Start the current video from the beginning */ 2378 osd_seek_time(0*TS_SECOND); 2379 } 2380 else { 2381 /* Release within 3 seconds of start: skip to previous 2382 * file */ 2383 osd_stop(); 2384 next_action = VIDEO_PREV | VIDEO_ACTION_MANUAL; 2385 } 2386 } 2387 else if ((button & ~BUTTON_REPEAT) == old_button) { 2388 button = osd_seek_btn(old_button); 2389 } 2390 2391 if (button == ACTION_STD_CANCEL) 2392 goto cancel_playback; /* jump to stop handling above */ 2393 2394 rb->default_event_handler(button); 2395 break; 2396 } /* MPEG_RW: */ 2397 2398 case MPEG_FF: 2399#ifdef MPEG_FF2 2400 case MPEG_FF2: 2401#endif 2402#ifdef MPEG_RC_FF 2403 case MPEG_RC_FF: 2404#endif 2405 { 2406 int old_button = button; 2407 2408 if (settings.play_mode != 0) 2409 button = mpeg_button_get(OSD_MIN_UPDATE_INTERVAL); 2410 2411 if ((old_button | BUTTON_REL) == button) { 2412 /* If button has been released: skip to next file */ 2413 osd_stop(); 2414 next_action = VIDEO_NEXT | VIDEO_ACTION_MANUAL; 2415 } 2416 else if ((button & ~BUTTON_REPEAT) == old_button) { 2417 button = osd_seek_btn(old_button); 2418 } 2419 2420 if (button == ACTION_STD_CANCEL) 2421 goto cancel_playback; /* jump to stop handling above */ 2422 2423 rb->default_event_handler(button); 2424 break; 2425 } /* MPEG_FF: */ 2426 2427#ifdef HAVE_HEADPHONE_DETECTION 2428 case SYS_PHONE_PLUGGED: 2429 case SYS_PHONE_UNPLUGGED: 2430 { 2431 osd_handle_phone_plug(button == SYS_PHONE_PLUGGED); 2432 break; 2433 } /* SYS_PHONE_*: */ 2434#endif 2435 2436 default: 2437 { 2438 osd_refresh(OSD_REFRESH_DEFAULT); 2439 rb->default_event_handler(button); 2440 break; 2441 } /* default: */ 2442 } 2443 2444 rb->yield(); 2445 } /* end while */ 2446 2447 osd_stop(); 2448 2449#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP) 2450 /* Be sure hook is removed before exiting since the stop will put it 2451 * back because of the backlight restore. */ 2452 rb->remove_event(LCD_EVENT_ACTIVATION, osd_lcd_enable_hook); 2453#endif 2454 2455 rb->lcd_setfont(FONT_UI); 2456 2457 return next_action; 2458} 2459 2460enum plugin_status plugin_start(const void* parameter) 2461{ 2462 static char videofile[MAX_PATH]; 2463 int status = PLUGIN_OK; /* assume success */ 2464 bool quit = false; 2465 2466#if defined(PLUGIN_USE_IRAM) && !defined(SIMULATOR) 2467 bool preserved_talk_state; 2468#endif 2469 2470 if (parameter == NULL) { 2471 /* No file = GTFO */ 2472 rb->splash(HZ*2, "No File"); 2473 return PLUGIN_ERROR; 2474 } 2475 2476 /* Disable all talking before initializing IRAM */ 2477 rb->talk_disable(true); 2478 2479#ifdef PLUGIN_USE_IRAM 2480 iram_saving_init(); 2481 2482#ifndef SIMULATOR 2483 preserved_talk_state = rb->global_settings->talk_menu; 2484 if (!iram_saved_copy) 2485 rb->global_settings->talk_menu = false; 2486#endif 2487#endif 2488 2489#ifdef HAVE_LCD_COLOR 2490 rb->lcd_set_backdrop(NULL); 2491 rb->lcd_set_foreground(LCD_WHITE); 2492 rb->lcd_set_background(LCD_BLACK); 2493#endif 2494 2495 rb->lcd_clear_display(); 2496 rb->lcd_update(); 2497 2498 rb->strcpy(videofile, (const char*) parameter); 2499 2500 if (stream_init() < STREAM_OK) { 2501 /* Fatal because this should not fail */ 2502 DEBUGF("Could not initialize streams\n"); 2503 status = PLUGIN_ERROR; 2504 } else { 2505 int next_action = VIDEO_STOP; 2506 bool get_videofile_says = true; 2507 2508 while (!quit) 2509 { 2510 init_settings(videofile); 2511 2512 int result = stream_open(videofile); 2513 bool manual_skip = false; 2514 2515 if (result >= STREAM_OK) { 2516 /* start menu */ 2517 rb->lcd_clear_display(); 2518 rb->lcd_update(); 2519 result = mpeg_start_menu(stream_get_duration()); 2520 2521 next_action = VIDEO_STOP; 2522 if (result != MPEG_START_QUIT) { 2523 /* Enter button loop and process UI */ 2524 next_action = button_loop(); 2525 manual_skip = next_action & VIDEO_ACTION_MANUAL; 2526 next_action &= ~VIDEO_ACTION_MANUAL; 2527 } 2528 2529 stream_close(); 2530 2531 rb->lcd_clear_display(); 2532 rb->lcd_update(); 2533 2534 save_settings(); 2535 } else { 2536 /* Problem with file; display message about it - not 2537 * considered a plugin error */ 2538 long tick; 2539 const char *errstring; 2540 2541 DEBUGF("Could not open %s\n", videofile); 2542 switch (result) 2543 { 2544 case STREAM_UNSUPPORTED: 2545 errstring = "Unsupported format"; 2546 break; 2547 default: 2548 errstring = "Error opening file: %d"; 2549 } 2550 2551 tick = *rb->current_tick + HZ*2; 2552 2553 rb->splashf(0, errstring, result); 2554 2555 /* Be sure it doesn't get stuck in an unbreakable loop of bad 2556 * files, just in case! Otherwise, keep searching in the 2557 * chosen direction until a good one is found. */ 2558 while (!quit && TIME_BEFORE(*rb->current_tick, tick)) 2559 { 2560 int button = mpeg_button_get(HZ*2); 2561 2562 switch (button) 2563 { 2564 case MPEG_STOP: 2565 case ACTION_STD_CANCEL: 2566 /* Abort the search and exit */ 2567 next_action = VIDEO_STOP; 2568 quit = true; 2569 break; 2570 2571 case BUTTON_NONE: 2572 if (settings.play_mode != 0) { 2573 if (next_action == VIDEO_STOP) { 2574 /* Default to next file */ 2575 next_action = VIDEO_NEXT; 2576 } 2577 else if (next_action == VIDEO_PREV && 2578 !get_videofile_says) { 2579 /* Was first file already; avoid endlessly 2580 * retrying it */ 2581 next_action = VIDEO_STOP; 2582 } 2583 } 2584 break; 2585 2586 default: 2587 rb->default_event_handler(button); 2588 } /* switch */ 2589 } /* while */ 2590 } 2591 2592 /* return value of button_loop says, what's next */ 2593 switch (next_action) 2594 { 2595 case VIDEO_NEXT: 2596 { 2597 get_videofile_says = get_videofile(VIDEO_NEXT, videofile, 2598 sizeof(videofile)); 2599 /* quit after finished the last videofile */ 2600 quit = !get_videofile_says; 2601 2602 if (manual_skip) 2603 { 2604 rb->system_sound_play(get_videofile_says ? 2605 SOUND_TRACK_SKIP : SOUND_TRACK_NO_MORE); 2606 } 2607 2608 break; 2609 } 2610 case VIDEO_PREV: 2611 { 2612 get_videofile_says = get_videofile(VIDEO_PREV, videofile, 2613 sizeof(videofile)); 2614 /* if there is no previous file, play the same videofile */ 2615 2616 if (manual_skip) 2617 { 2618 rb->system_sound_play(get_videofile_says ? 2619 SOUND_TRACK_SKIP : SOUND_TRACK_NO_MORE); 2620 } 2621 2622 break; 2623 } 2624 case VIDEO_STOP: 2625 { 2626 quit = true; 2627 break; 2628 } 2629 } 2630 } /* while */ 2631 } 2632 2633#if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_YUV) 2634 rb->lcd_set_mode(LCD_MODE_RGB565); 2635#endif 2636 2637 stream_exit(); 2638 2639#if defined(PLUGIN_USE_IRAM) && !defined(SIMULATOR) 2640 if (!iram_saved_copy) 2641 rb->global_settings->talk_menu = preserved_talk_state; 2642#endif 2643 2644 rb->talk_disable(false); 2645 2646 /* Actually handle delayed processing of system events of interest 2647 * that were captured in other button loops */ 2648 mpeg_sysevent_handle(); 2649 2650 return status; 2651}