From d607265f695510e357c67d78e43d0c8138f8c588 Mon Sep 17 00:00:00 2001 From: Klaus Schmidinger Date: Sun, 13 Jan 2008 11:44:38 +0100 Subject: Added the 'pictures' plugin --- PLUGINS/src/pictures/COPYING | 340 +++++++++++++++++++++++++++++++++++++++ PLUGINS/src/pictures/HISTORY | 6 + PLUGINS/src/pictures/Makefile | 110 +++++++++++++ PLUGINS/src/pictures/README | 119 ++++++++++++++ PLUGINS/src/pictures/entry.c | 144 +++++++++++++++++ PLUGINS/src/pictures/entry.h | 36 +++++ PLUGINS/src/pictures/menu.c | 129 +++++++++++++++ PLUGINS/src/pictures/menu.h | 31 ++++ PLUGINS/src/pictures/pic2mpg | 176 ++++++++++++++++++++ PLUGINS/src/pictures/pictures.c | 124 ++++++++++++++ PLUGINS/src/pictures/player.c | 249 ++++++++++++++++++++++++++++ PLUGINS/src/pictures/player.h | 47 ++++++ PLUGINS/src/pictures/po/de_DE.po | 32 ++++ 13 files changed, 1543 insertions(+) create mode 100644 PLUGINS/src/pictures/COPYING create mode 100644 PLUGINS/src/pictures/HISTORY create mode 100644 PLUGINS/src/pictures/Makefile create mode 100644 PLUGINS/src/pictures/README create mode 100644 PLUGINS/src/pictures/entry.c create mode 100644 PLUGINS/src/pictures/entry.h create mode 100644 PLUGINS/src/pictures/menu.c create mode 100644 PLUGINS/src/pictures/menu.h create mode 100755 PLUGINS/src/pictures/pic2mpg create mode 100644 PLUGINS/src/pictures/pictures.c create mode 100644 PLUGINS/src/pictures/player.c create mode 100644 PLUGINS/src/pictures/player.h create mode 100644 PLUGINS/src/pictures/po/de_DE.po (limited to 'PLUGINS/src') diff --git a/PLUGINS/src/pictures/COPYING b/PLUGINS/src/pictures/COPYING new file mode 100644 index 00000000..f90922ee --- /dev/null +++ b/PLUGINS/src/pictures/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/PLUGINS/src/pictures/HISTORY b/PLUGINS/src/pictures/HISTORY new file mode 100644 index 00000000..c04a6280 --- /dev/null +++ b/PLUGINS/src/pictures/HISTORY @@ -0,0 +1,6 @@ +VDR Plugin 'pictures' Revision History +-------------------------------------- + +2008-01-13: Version 0.0.1 + +- Initial revision. diff --git a/PLUGINS/src/pictures/Makefile b/PLUGINS/src/pictures/Makefile new file mode 100644 index 00000000..29f4f4b4 --- /dev/null +++ b/PLUGINS/src/pictures/Makefile @@ -0,0 +1,110 @@ +# +# Makefile for a Video Disk Recorder plugin +# +# $Id: Makefile 1.1 2008/01/13 11:29:27 kls Exp $ + +# The official name of this plugin. +# This name will be used in the '-P...' option of VDR to load the plugin. +# By default the main source file also carries this name. +# IMPORTANT: the presence of this macro is important for the Make.config +# file. So it must be defined, even if it is not used here! +# +PLUGIN = pictures + +### The version number of this plugin (taken from the main source file): + +VERSION = $(shell grep 'static const char \*VERSION *=' $(PLUGIN).c | awk '{ print $$6 }' | sed -e 's/[";]//g') + +### The C++ compiler and options: + +CXX ?= g++ +CXXFLAGS ?= -fPIC -g -O2 -Wall -Woverloaded-virtual + +### The directory environment: + +VDRDIR = ../../.. +LIBDIR = ../../lib +TMPDIR = /tmp + +### Allow user defined options to overwrite defaults: + +-include $(VDRDIR)/Make.config + +### The version number of VDR's plugin API (taken from VDR's "config.h"): + +APIVERSION = $(shell sed -ne '/define APIVERSION/s/^.*"\(.*\)".*$$/\1/p' $(VDRDIR)/config.h) + +### The name of the distribution archive: + +ARCHIVE = $(PLUGIN)-$(VERSION) +PACKAGE = vdr-$(ARCHIVE) + +### Includes and Defines (add further entries here): + +INCLUDES += -I$(VDRDIR)/include + +DEFINES += -D_GNU_SOURCE -DPLUGIN_NAME_I18N='"$(PLUGIN)"' + +### The object files (add further files here): + +OBJS = $(PLUGIN).o entry.o menu.o player.o + +### The main target: + +all: libvdr-$(PLUGIN).so i18n + +### Implicit rules: + +%.o: %.c + $(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) $< + +### Dependencies: + +MAKEDEP = $(CXX) -MM -MG +DEPFILE = .dependencies +$(DEPFILE): Makefile + @$(MAKEDEP) $(DEFINES) $(INCLUDES) $(OBJS:%.o=%.c) > $@ + +-include $(DEPFILE) + +### Internationalization (I18N): + +PODIR = po +LOCALEDIR = $(VDRDIR)/locale +I18Npo = $(wildcard $(PODIR)/*.po) +I18Nmsgs = $(addprefix $(LOCALEDIR)/, $(addsuffix /LC_MESSAGES/vdr-$(PLUGIN).mo, $(notdir $(foreach file, $(I18Npo), $(basename $(file)))))) +I18Npot = $(PODIR)/$(PLUGIN).pot + +%.mo: %.po + msgfmt -c -o $@ $< + +$(I18Npot): $(wildcard *.c) + xgettext -C -cTRANSLATORS --no-wrap --no-location -k -ktr -ktrNOOP --msgid-bugs-address='' -o $@ $^ + +%.po: $(I18Npot) + msgmerge -U --no-wrap --no-location --backup=none -q $@ $< + @touch $@ + +$(I18Nmsgs): $(LOCALEDIR)/%/LC_MESSAGES/vdr-$(PLUGIN).mo: $(PODIR)/%.mo + @mkdir -p $(dir $@) + cp $< $@ + +.PHONY: i18n +i18n: $(I18Nmsgs) $(I18Npot) + +### Targets: + +libvdr-$(PLUGIN).so: $(OBJS) + $(CXX) $(CXXFLAGS) -shared $(OBJS) -o $@ + @cp --remove-destination $@ $(LIBDIR)/$@.$(APIVERSION) + +dist: clean + @-rm -rf $(TMPDIR)/$(ARCHIVE) + @mkdir $(TMPDIR)/$(ARCHIVE) + @cp -a * $(TMPDIR)/$(ARCHIVE) + @tar czf $(PACKAGE).tgz -C $(TMPDIR) $(ARCHIVE) + @-rm -rf $(TMPDIR)/$(ARCHIVE) + @echo Distribution package created as $(PACKAGE).tgz + +clean: + @-rm -f $(OBJS) $(DEPFILE) *.so *.tgz core* *~ $(PODIR)/*.mo $(PODIR)/*.pot diff --git a/PLUGINS/src/pictures/README b/PLUGINS/src/pictures/README new file mode 100644 index 00000000..bd6bb7d2 --- /dev/null +++ b/PLUGINS/src/pictures/README @@ -0,0 +1,119 @@ +This is a "plugin" for the Video Disk Recorder (VDR). + +Written by: Klaus Schmidinger + +Project's homepage: www.cadsoft.de/vdr + +Latest version available at: www.cadsoft.de/vdr + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. +See the file COPYING for more information. + +Description: +============ + +The 'pictures' plugin implements a simple picture viewer. + +There is already an 'image' plugin out there which has a lot more +functionality than this one, but it's way too complex for my taste, +and also converts the image files on-the-fly, which makes it slow +on slow hardware. + +This plugin assumes that the pictures have already been converted to +MPEG frames (with the 'pic2mpg' script that comes with this archive), +and doesn't implement any fancy features like zooming, panning or +tiled previews. It's just a very simple viewer. + +It also assumes a rather particular directory structure (see below), +which I have found to fit well for my needs. If you use a different +structure, the plugin should still work, but the information displayed +in the caption may not be as expected. + +Remote control key functions while in the Pictures menu: +======================================================== + +- Left/right/Up/Down: navigate in the menu as usual. +- Ok: opens a directory or displays the selected + picture. +- Red/Play: start a slide show at the first picture in the + selected directory (or at the selected picture). + +Remote control key functions while displaying pictures: +======================================================= + +- Left/Right: advances to the previous/next picture. At the end of a directory + it automatically advances to the first picture of the next + directory and displays the caption. +- Up/Play: start the slide show. +- Down/Pause: stop the slide show. +- Blue/Stop: exit from the picture viewer. +- Green/Prev: skip to the first picture in the previous directory. +- Yellow/Next: skip to the first picture in the next directory. +- Ok: toggle the caption display. +- Back: return to the picture selection menu. + +The slide show starts with the picture immediately following the one that +is currently visible, and continues until the last available picture, +automatically crossing directories as necessary. + +Pressing any of the Left/Right, Green/Prev, Yellow/Next or Back keys +automatically stops an ongoing slide show. + +Directory structure: +==================== + +The 'pictures' plugin requires a particular directory structure in order to +work properly. Starting at some base directory (e.g. "/path/to/pictures"), +there is one directory level marking the year in which the pictures were +taken, followed by a level with a properly sortable combination of month +and description, and finally the plain picture files, as in + +/path/to/pictures/YEAR/MONTH_DESCRIPTION/*.jpg + +For example + +/path/to/pictures/2007/06a_Vacation_in_Colorado/dsc01234.jpg +/path/to/pictures/2007/06a_Vacation_in_Colorado/dsc01235.jpg +/path/to/pictures/2007/06b_Our_cat/dsc01236.jpg + +The MONTH is "01" for January through "12" for December. If there is more +than one entry for one month, it should be followed by a letter to make them +sort correctly. Note that this is just a suggestion on how to make the +directories sort in the correct chronological order. Instead of the two digit +month number, optionally followed by a letter, you can use anything else you +like. There must be an underscore between the MONTH and the DESCRIPTION part, +and any underscores within the DESCRIPTION will be converted to blanks when +displaying the description (this is done so that blanks can be avoided in +the directory names). + +Preparing the files: +==================== + +In order to display the pictures as "still pictures" on the DVB device, they +need to be converted into MPEG frames. This procedure takes some time, and +would make viewing pictures sluggish, especially on slow hardware. Therefore +the script "pic2mpg" must be used to convert all picture files before +using this plugin, as in + +pic2mpg /path/to/pictures /path/to/pictures.MPG + +This will recursively walk through all pictures in the source directory and +convert them into MPEG frames, stored in the destination directory. The +extension ".MPG" is just a suggestion, you can name the directory anything +you like, and it doesn't have to start with the same path as the source +directory. Just don't make the destination directory a subdirectory of +the source directory, because this would surely lead to problems. +See "pic2mpg -h" for a list of available options. +The pic2mpg conversion can be run any time, even if converted pictures +already exist. It will only convert pictures that haven't been converted +yet. Any changes to the source directory will be reflected in the destination +directory accordingly after pic2mpg has finished (note that this especially +means that any files or directories in the destination tree that have no +corresponding entry in the source tree will be deleted!). So you can run +pic2mpg, for instance, after copying new pictures into your picture +directory, or after making any other kind of changes to your existing +pictures. It might even be a good idea to run pic2mpg in a (nightly) +cron job. diff --git a/PLUGINS/src/pictures/entry.c b/PLUGINS/src/pictures/entry.c new file mode 100644 index 00000000..85156558 --- /dev/null +++ b/PLUGINS/src/pictures/entry.c @@ -0,0 +1,144 @@ +/* + * entry.c: Data structure to handle still pictures + * + * See the README file for copyright information and how to reach the author. + * + * $Id: entry.c 1.1 2008/01/13 11:29:27 kls Exp $ + */ + +#include "entry.h" + +cPictureEntry::cPictureEntry(const char *Name, const cPictureEntry *Parent, bool IsDirectory) +{ + name = strdup(Name); + parent = Parent; + isDirectory = IsDirectory; + entries = NULL; +} + +cPictureEntry::~cPictureEntry() +{ + free(name); + delete entries; +} + +int cPictureEntry::Compare(const cListObject &ListObject) const +{ + cPictureEntry *p = (cPictureEntry *)&ListObject; + if (IsDirectory() && !p->IsDirectory()) + return -1; + if (!IsDirectory() && p->IsDirectory()) + return +1; + if (IsDirectory()) + return strcoll(name, p->name); + else + return strcmp(name, p->name); // correctly sorts dsc01234.jpg and dsc01234a.jpg in case pictures have been "squeezed in" +} + +cString cPictureEntry::Path(void) const +{ + return parent ? AddDirectory(parent->Path(), name) : name; +} + +void cPictureEntry::Load(void) const +{ + if (isDirectory && !entries) { + cString Directory = Path(); + cReadDir d(Directory); + if (d.Ok()) { + struct dirent *e; + while ((e = d.Next()) != NULL) { + if (strcmp(e->d_name, ".") && strcmp(e->d_name, "..")) { + struct stat ds; + if (stat(AddDirectory(Directory, e->d_name), &ds) == 0) { + if (!entries) + entries = new cList; + entries->Add(new cPictureEntry(e->d_name, this, S_ISDIR(ds.st_mode))); + } + } + } + if (entries) + entries->Sort(); + } + else + LOG_ERROR_STR(*Directory); + } +} + +const cList *cPictureEntry::Entries(void) const +{ + Load(); + return entries; +} + +const cPictureEntry *cPictureEntry::FirstPicture(void) const +{ + Load(); + if (entries) { + for (cPictureEntry *pe = entries->First(); pe; pe = entries->Next(pe)) { + if (pe->IsDirectory()) { + const cPictureEntry *p = pe->FirstPicture(); + if (p) + return p; + } + else + return pe; + } + } + return NULL; +} + +const cPictureEntry *cPictureEntry::LastPicture(void) const +{ + Load(); + if (entries) { + for (cPictureEntry *pe = entries->Last(); pe; pe = entries->Prev(pe)) { + if (pe->IsDirectory()) { + const cPictureEntry *p = pe->LastPicture(); + if (p) + return p; + } + else + return pe; + } + } + return NULL; +} + +const cPictureEntry *cPictureEntry::PrevPicture(const cPictureEntry *This) const +{ + if (This) { + const cPictureEntry *pe = (cPictureEntry *)entries->Prev(This); + if (pe) { + if (pe->IsDirectory()) { + const cPictureEntry *p = pe->LastPicture(); + if (p) + return p; + return PrevPicture(pe); + } + return pe; + } + } + if (parent) + return parent->PrevPicture(this); + return NULL; +} + +const cPictureEntry *cPictureEntry::NextPicture(const cPictureEntry *This) const +{ + if (This) { + cPictureEntry *pe = (cPictureEntry *)entries->Next(This); + if (pe) { + if (pe->IsDirectory()) { + const cPictureEntry *p = pe->FirstPicture(); + if (p) + return p; + return NextPicture(pe); + } + return pe; + } + } + if (parent) + return parent->NextPicture(this); + return NULL; +} diff --git a/PLUGINS/src/pictures/entry.h b/PLUGINS/src/pictures/entry.h new file mode 100644 index 00000000..adb84f38 --- /dev/null +++ b/PLUGINS/src/pictures/entry.h @@ -0,0 +1,36 @@ +/* + * entry.h: Data structure to handle still pictures + * + * See the README file for copyright information and how to reach the author. + * + * $Id: entry.h 1.1 2008/01/13 11:29:27 kls Exp $ + */ + +#ifndef _ENTRY_H +#define _ENTRY_H + +#include + +class cPictureEntry : public cListObject { +private: + char *name; + const cPictureEntry *parent; + bool isDirectory; + mutable cList *entries; + void Load(void) const; +public: + cPictureEntry(const char *Name, const cPictureEntry *Parent, bool IsDirectory); + virtual ~cPictureEntry(); + virtual int Compare(const cListObject &ListObject) const; + const char *Name(void) const { return name; } + const cPictureEntry *Parent(void) const { return parent; } + bool IsDirectory(void) const { return isDirectory; } + cString Path(void) const; + const cList *Entries(void) const; + const cPictureEntry *FirstPicture(void) const; + const cPictureEntry *LastPicture(void) const; + const cPictureEntry *PrevPicture(const cPictureEntry *This = NULL) const; + const cPictureEntry *NextPicture(const cPictureEntry *This = NULL) const; + }; + +#endif //_ENTRY_H diff --git a/PLUGINS/src/pictures/menu.c b/PLUGINS/src/pictures/menu.c new file mode 100644 index 00000000..6a64d480 --- /dev/null +++ b/PLUGINS/src/pictures/menu.c @@ -0,0 +1,129 @@ +/* + * menu.c: A menu for still pictures + * + * See the README file for copyright information and how to reach the author. + * + * $Id: menu.c 1.1 2008/01/13 11:35:18 kls Exp $ + */ + +#include "menu.h" +#include +#include "entry.h" +#include "player.h" + +char PictureDirectory[PATH_MAX] = ""; + +static bool PathStartsWith(const char *Path, const char *Name) +{ + if (Path && Name) { + while (*Name) { + if (*Path++ != *Name++) + return false; + } + if (*Path && *Path != '/') + return false; + return true; + } + return false; +} + +static const char *NextLevel(const char *Path) +{ + if (Path) { + const char *p = strchr(Path, '/'); + return p ? p + 1 : NULL; + } + return Path; +} + +cPictureEntry *cPictureMenu::pictures = NULL; + +cPictureMenu::cPictureMenu(const cPictureEntry *PictureEntry, const char *Path) +:cOsdMenu(tr("Pictures")) +{ + pictureEntry = PictureEntry; + if (!pictureEntry) + pictureEntry = pictures = new cPictureEntry(PictureDirectory, NULL, true); + if (pictureEntry->Parent()) { + if (!pictureEntry->Parent()->Parent()) + SetTitle(pictureEntry->Name()); // Year + else + SetTitle(cString::sprintf("%s: %s", pictureEntry->Parent()->Name(), *HandleUnderscores(pictureEntry->Name()))); // Year/Description + } + Set(Path); +} + +cPictureMenu::~cPictureMenu() +{ + if (pictures && pictureEntry && !pictureEntry->Parent()) + DELETENULL(pictures); +} + +void cPictureMenu::Set(const char *Path) +{ + Clear(); + const cList *l = pictureEntry->Entries(); + if (l) { + for (const cPictureEntry *e = l->First(); e; e = l->Next(e)) { + cString Name = HandleUnderscores(e->Name()); + if (!e->IsDirectory()) + Name.Truncate(-4); // don't display the ".mpg" extension + Add(new cOsdItem(HandleUnderscores(Name)), PathStartsWith(Path, e->Name())); + } + } + SetHelp(Count() ? trVDR("Button$Play") : NULL, NULL, NULL, cPictureControl::Active() ? trVDR("Button$Stop") : NULL); + if (Current() >= 0) { + const char *p = NextLevel(Path); + if (p) + SelectItem(p); + } +} + +eOSState cPictureMenu::SelectItem(const char *Path, bool SlideShow) +{ + cOsdItem *Item = Get(Current()); + if (Item) { + const cList *l = pictureEntry->Entries(); + if (l) { + cPictureEntry *pe = l->Get(Current()); + if (pe) { + if (SlideShow) { + cControl::Launch(new cPictureControl(pictures, pe, true)); + pictures = NULL; // cPictureControl takes ownership + return osEnd; + } + if (pe->IsDirectory()) + return AddSubMenu(new cPictureMenu(pe, Path)); + else if (!Path) { + cControl::Launch(new cPictureControl(pictures, pe)); + pictures = NULL; // cPictureControl takes ownership + return osEnd; + } + } + } + } + return osContinue; +} + +eOSState cPictureMenu::ProcessKey(eKeys Key) +{ + eOSState state = cOsdMenu::ProcessKey(Key); + if (state == osUnknown) { + switch (Key) { + case kRed: + case kPlay: return SelectItem(NULL, true); + case kBlue: + case kStop: if (cPictureControl::Active()) + return osStopReplay; + break; + case kOk: return SelectItem(); + default: break; + } + } + return state; +} + +cPictureMenu *cPictureMenu::CreatePictureMenu(void) +{ + return new cPictureMenu(NULL, cPictureControl::LastDisplayed()); +} diff --git a/PLUGINS/src/pictures/menu.h b/PLUGINS/src/pictures/menu.h new file mode 100644 index 00000000..eb85cb33 --- /dev/null +++ b/PLUGINS/src/pictures/menu.h @@ -0,0 +1,31 @@ +/* + * menu.h: A menu for still pictures + * + * See the README file for copyright information and how to reach the author. + * + * $Id: menu.h 1.1 2008/01/13 11:32:52 kls Exp $ + */ + +#ifndef _MENU_H +#define _MENU_H + +#include +#include +#include "entry.h" + +extern char PictureDirectory[PATH_MAX]; + +class cPictureMenu : public cOsdMenu { +private: + static cPictureEntry *pictures; + const cPictureEntry *pictureEntry; + void Set(const char *Path); + eOSState SelectItem(const char *Path = NULL, bool SlideShow = false); +public: + cPictureMenu(const cPictureEntry *PictureEntry, const char *Path = NULL); + ~cPictureMenu(); + virtual eOSState ProcessKey(eKeys Key); + static cPictureMenu *CreatePictureMenu(void); + }; + +#endif //_MENU_H diff --git a/PLUGINS/src/pictures/pic2mpg b/PLUGINS/src/pictures/pic2mpg new file mode 100755 index 00000000..c62dee92 --- /dev/null +++ b/PLUGINS/src/pictures/pic2mpg @@ -0,0 +1,176 @@ +#!/usr/bin/perl + +# pic2mpg: Convert picture files to MPEG still frames +# +# Converts either a single picture file or all files in a +# given directory (recursively) to MPEG still frames. +# +# See the README file for copyright information and how to reach the author. +# +# $Id: pic2mpg 1.1 2008/01/13 11:39:12 kls Exp $ + +## TODO implement HDTV (1920 x 1080) + +use File::Path; +use Getopt::Std; +use Image::Size; + +$Usage = qq{ +Usage: $0 [options] picture-dir mpeg-dir + $0 [options] picture-file mpeg-file + +Options: -a Aspect ratio 4:3 (default is 16:9) + -h print Help + -f Force conversion + -n NTSC (default is PAL) + -v num Verbose (0=none, 1=list files, 2=detailed) + -x percent X overscan in percent + -y percent Y overscan in percent +}; + +getopts("ahfnv:x:y:") || die $Usage; + +die $Usage if $opt_h; + +$Aspect = $opt_a; +$Force = $opt_f; +$NTSC = $opt_n; +$Verbose = $opt_v; +$OverscanX = $opt_x; +$OverscanY = $opt_y; + +$ListFiles = $Verbose >= 1; +$Detailed = $Verbose >= 2; + +# Screen size: + +$SW = $NTSC ? 720 : 720; +$SH = $NTSC ? 480 : 576; + +$ScreenRatio = $Aspect ? 4 / 3 : 16 / 9; + +# Converter programs: + +%PNMCONV = ( + bmp => "bmptopnm", + gif => "giftopnm", + jpeg => "jpegtopnm", + jpg => "jpegtopnm", + png => "pngtopnm", + pnm => "cat", + tif => "tifftopnm", + tiff => "tifftopnm", + ); + +# Command options: + +die "$0: missing parameter\n" unless $ARGV[0] && $ARGV[1]; +die "$0: file or directory not found: $ARGV[0]\n" unless -e $ARGV[0]; +die "$0: source and destination must be different\n" if $ARGV[0] eq $ARGV[1]; + +$verbose1 = $Detailed ? "--verbose" : ""; +$verbose2 = $Detailed ? "-v 2" : "-v 0"; +$system1 = $NTSC ? "" : "--pal"; +$system2 = $NTSC ? "n" : "p"; +$framerate = $NTSC ? "30000:1001" : "25:1"; +$aspect = $Aspect ? "2" : "3"; + +# Convert a single file: + +if (-f $ARGV[0]) { + die "$0: mixed file and directory ('$ARGV[0]' <-> '$ARGV[1]')\n" unless !-e $ARGV[1] || -f $ARGV[1]; + ConvertFile($ARGV[0], $ARGV[1]); + exit; + } + +die "$0: mixed directory and file ('$ARGV[0]' <-> '$ARGV[1]')\n" unless !-e $ARGV[1] || -d $ARGV[1]; + +$PICDIR = $ARGV[0]; +$MPGDIR = $ARGV[1]; + +# Convert pictures to mpegs: + +chdir($PICDIR) || die "$PICDIR: $!\n"; + +@Pictures = `find -type f`; +chomp(@Pictures); + +for $pic (@Pictures) { + my $mpg = "$MPGDIR/$pic.mpg"; + if ($Force || !-e $mpg || -M $mpg > -M $pic) { + (my $dir = $mpg) =~ s/\/[^\/]*$//; + mkpath($dir); + ConvertFile($pic, $mpg); + } + } + +# Remove mpegs without pictures: + +chdir($MPGDIR) || die "$MPGDIR: $!\n"; + +@Mpegs = `find -type f`; +chomp(@Mpegs); + +for $mpg (@Mpegs) { + my $pic = "$PICDIR/$mpg"; + $pic =~ s/\.mpg$//; + if (!-e $pic) { + print "removing $mpg\n"; + unlink($mpg); + } + } + +# Remove empty directories: + +chdir($MPGDIR) || die "$MPGDIR: $!\n"; + +for ($i = 0; $i < 10; $i++) { # dirs might become empty when removing empty subdirs + @Dirs = `find -type d -empty`; + chomp(@Dirs); + last unless @Dirs; + + for $dir (@Dirs) { + $dir = EscapeMeta($dir); + print "removing $dir\n"; + !system("rm -rf $dir") || die "$dir: $!\n"; + } + } + +# Actual file conversion: + +sub ConvertFile +{ + my ($Pict, $Mpeg) = @_; + (my $Type) = $Pict =~ /\.([^\.]*)$/; + die "unknown file type '$Type': '$Pict'\n" unless defined $PNMCONV{$Type}; + my ($w, $h) = imgsize($Pict); + print "image size is $w x $h\n" if ($Detailed); + if ($w / $h <= $ScreenRatio) { + $w = $h * $ScreenRatio; + } + else { + $h = $w / $ScreenRatio; + } + my $ScaleW = $SW / $w * (100 - 2 * $OverscanX) / 100; + my $ScaleH = $SH / $h * (100 - 2 * $OverscanY) / 100; + $Pict = EscapeMeta($Pict); + $Mpeg = EscapeMeta($Mpeg); + print "$Pict -> $Mpeg\n" if $ListFiles; + my $Cmd = "$PNMCONV{$Type} $Pict 2> /dev/null |" + . "pnmscale $verbose1 --xscale=$ScaleW --yscale=$ScaleH |" + . "pnmpad $verbose1 --black --width $SW --height $SH |" + . "ppmntsc $verbose1 $system1 |" + . "ppmtoy4m $verbose2 -F $framerate -I p -S 420mpeg2 |" + . "mpeg2enc $verbose2 -f 3 -b 12500 -a $aspect -q 1 -n $system2 -o $Mpeg"; + !system($Cmd) || die "$Cmd: $!\n"; + $Cmd = "touch -r $Pict $Mpeg"; + !system($Cmd) || die "$Cmd: $!\n"; +} + +sub EscapeMeta +{ + my $META = ' !"#$%&\'()*;<>?[\\]`{|}~'; + my $s = shift; + $s =~ s/([$META])/\\$1/g; + return $s; +} diff --git a/PLUGINS/src/pictures/pictures.c b/PLUGINS/src/pictures/pictures.c new file mode 100644 index 00000000..8b517476 --- /dev/null +++ b/PLUGINS/src/pictures/pictures.c @@ -0,0 +1,124 @@ +/* + * pictures.c: A plugin for the Video Disk Recorder + * + * See the README file for copyright information and how to reach the author. + * + * $Id: pictures.c 1.1 2008/01/13 11:29:27 kls Exp $ + */ + +#include +#include +#include "menu.h" +#include "player.h" + +static const char *VERSION = "0.0.1"; +static const char *DESCRIPTION = trNOOP("A simple picture viewer"); +static const char *MAINMENUENTRY = trNOOP("Pictures"); + +// --- cMenuSetupPictures ---------------------------------------------------- + +class cMenuSetupPictures : public cMenuSetupPage { +private: + char newPictureDirectory[PATH_MAX]; + int newSlideShowDelay; +protected: + virtual void Store(void); +public: + cMenuSetupPictures(void); + }; + +cMenuSetupPictures::cMenuSetupPictures(void) +{ + strn0cpy(newPictureDirectory, PictureDirectory, sizeof(newPictureDirectory)); + newSlideShowDelay = SlideShowDelay; + Add(new cMenuEditStrItem(tr("Picture directory"), newPictureDirectory, sizeof(newPictureDirectory))); + Add(new cMenuEditIntItem(tr("Slide show delay (s)"), &newSlideShowDelay)); +} + +void cMenuSetupPictures::Store(void) +{ + SetupStore("PictureDirectory", strn0cpy(PictureDirectory, newPictureDirectory, sizeof(PictureDirectory))); + SetupStore("SlideShowDelay", SlideShowDelay = newSlideShowDelay); +} + +// --- cPluginPictures ------------------------------------------------------- + +class cPluginPictures : public cPlugin { +private: + // Add any member variables or functions you may need here. +public: + cPluginPictures(void); + virtual ~cPluginPictures(); + virtual const char *Version(void) { return VERSION; } + virtual const char *Description(void) { return tr(DESCRIPTION); } + virtual const char *CommandLineHelp(void); + virtual bool ProcessArgs(int argc, char *argv[]); + virtual const char *MainMenuEntry(void) { return tr(MAINMENUENTRY); } + virtual cOsdObject *MainMenuAction(void); + virtual cMenuSetupPage *SetupMenu(void); + virtual bool SetupParse(const char *Name, const char *Value); + }; + +cPluginPictures::cPluginPictures(void) +{ + // Initialize any member variables here. + // DON'T DO ANYTHING ELSE THAT MAY HAVE SIDE EFFECTS, REQUIRE GLOBAL + // VDR OBJECTS TO EXIST OR PRODUCE ANY OUTPUT! +} + +cPluginPictures::~cPluginPictures() +{ + // Clean up after yourself! +} + +const char *cPluginPictures::CommandLineHelp(void) +{ + // Return a string that describes all known command line options. + return " -d DIR, --dir=DIR set the picture directory to DIR\n"; +} + +bool cPluginPictures::ProcessArgs(int argc, char *argv[]) +{ + // Implement command line argument processing here if applicable. + static struct option long_options[] = { + { "dir", required_argument, NULL, 'd' }, + { NULL } + }; + + int c; + while ((c = getopt_long(argc, argv, "d:s:", long_options, NULL)) != -1) { + switch (c) { + case 'd': strn0cpy(PictureDirectory, optarg, sizeof(PictureDirectory)); + break; + default: return false; + } + } + return true; +} + +cOsdObject *cPluginPictures::MainMenuAction(void) +{ + // Perform the action when selected from the main VDR menu. + if (*PictureDirectory) + return cPictureMenu::CreatePictureMenu(); + Skins.Message(mtWarning, tr("No picture directory has been defined!")); + return NULL; +} + +cMenuSetupPage *cPluginPictures::SetupMenu(void) +{ + // Return a setup menu in case the plugin supports one. + return new cMenuSetupPictures; +} + +bool cPluginPictures::SetupParse(const char *Name, const char *Value) +{ + // Parse your own setup parameters and store their values. + if (!strcasecmp(Name, "PictureDirectory")) strn0cpy(PictureDirectory, Value, sizeof(PictureDirectory)); + else if (!strcasecmp(Name, "SlideShowDelay")) SlideShowDelay = atoi(Value); + else + return false; + return true; +} + +VDRPLUGINCREATOR(cPluginPictures); // Don't touch this! diff --git a/PLUGINS/src/pictures/player.c b/PLUGINS/src/pictures/player.c new file mode 100644 index 00000000..9e07fc53 --- /dev/null +++ b/PLUGINS/src/pictures/player.c @@ -0,0 +1,249 @@ +/* + * player.c: A player for still pictures + * + * See the README file for copyright information and how to reach the author. + * + * $Id: player.c 1.1 2008/01/13 11:29:27 kls Exp $ + */ + +#include "player.h" +#include +#include + +int SlideShowDelay = 3; // seconds + +cString HandleUnderscores(const char *s) +{ + // Skip anything before and including the first '_' and replace + // any remaining '_' with blanks: + const char *p = strchr(s, '_'); + if (p) { + p++; + char buf[strlen(p) + 1]; + strcpy(buf, p); + return strreplace(buf, '_', ' '); + } + return s; +} + +// --- cPicturePlayer -------------------------------------------------------- + +class cPicturePlayer : public cPlayer { +private: + int size; + int length; + uchar *buffer; + virtual void Activate(bool On); +public: + cPicturePlayer(void); + ~cPicturePlayer(); + void SetPicture(const char *FileName); + }; + +cPicturePlayer::cPicturePlayer(void) +{ + size = KILOBYTE(100); // will be adjusted automatically if files are larger + length = 0; + buffer = MALLOC(uchar, size); +} + +cPicturePlayer::~cPicturePlayer() +{ + free(buffer); +} + +void cPicturePlayer::Activate(bool On) +{ + if (length > 0) + DeviceStillPicture(buffer, length); +} + +void cPicturePlayer::SetPicture(const char *FileName) +{ + int f = open(FileName, O_RDONLY); + if (f >= 0) { + for (;;) { + length = read(f, buffer, size); + if (length > 0) { + if (length >= size) { + size = size * 3 / 2; + buffer = (uchar *)realloc(buffer, size); + lseek(f, 0, SEEK_SET); + continue; + } + DeviceStillPicture(buffer, length); + } + else + LOG_ERROR_STR(FileName); + break; + } + close(f); + } + else + LOG_ERROR_STR(FileName); +} + +// --- cPictureControl ------------------------------------------------------- + +int cPictureControl::active = 0; +cString cPictureControl::lastDisplayed; + +cPictureControl::cPictureControl(cPictureEntry *Pictures, const cPictureEntry *PictureEntry, bool SlideShow) +:cControl(player = new cPicturePlayer) +{ + pictures = Pictures; + pictureEntry = PictureEntry; + osd = NULL; + lastPath = "/"; + slideShow = SlideShow; + alwaysDisplayCaption = false; + NextPicture(slideShow && pictureEntry->IsDirectory() ? 1 : 0); + active++; +} + +cPictureControl::~cPictureControl() +{ + active--; + delete osd; + delete player; + delete pictures; +} + +void cPictureControl::NextPicture(int Direction) +{ + if (Direction) { + const cPictureEntry *pe = Direction > 0 ? pictureEntry->NextPicture() : pictureEntry->PrevPicture(); + if (pe) + pictureEntry = pe; + } + if (pictureEntry) { + player->SetPicture(pictureEntry->Path()); + DisplayCaption(); + } +} + +void cPictureControl::NextDirectory(int Direction) +{ + // This only works reliable if a directory contains only subdirectories or pictures, not both! + if (Direction) { + const cPictureEntry *pe = Direction > 0 ? pictureEntry->Parent()->Entries()->Last()->NextPicture() : pictureEntry->Parent()->Entries()->First()->PrevPicture(); + if (pe && Direction < 0) + pe = pe->Parent()->Entries()->First(); + if (pe && pe != pictureEntry) { + pictureEntry = pe; + player->SetPicture(pictureEntry->Path()); + DisplayCaption(); + } + } +} + +static void DrawTextOutlined(cOsd *Osd, int x, int y, const char *s, tColor ColorFg, tColor ColorBg, const cFont *Font) +{ + for (int dx = -1; dx <= 1; dx++) { + for (int dy = -1; dy <= 1; dy++) { + if (dx || dy) + Osd->DrawText(x + dx, y + dy, s, ColorBg, clrTransparent, Font); + } + } + Osd->DrawText(x, y, s, ColorFg, clrTransparent, Font); +} + +void cPictureControl::DisplayCaption(void) +{ + bool Force = false; + cString Path = pictureEntry->Path(); + lastDisplayed = Path + strlen(pictures->Name()) + 1; + const char *p = strrchr(Path, '/'); + const char *q = strrchr(lastPath, '/'); + if (p && q) { + int lp = p - Path; + int lq = q - lastPath; + if (lp != lq || strncmp(lastPath, Path, lp)) { + lastPath = Path; + Force = true; + } + } + if (!alwaysDisplayCaption && !Force) { + DELETENULL(osd); + return; + } + const cFont *Font = cFont::GetFont(fontOsd); + int w = cOsd::OsdWidth(); + int h = 2 * Font->Height(); + if (!osd) { + osd = cOsdProvider::NewOsd(cOsd::OsdLeft(), cOsd::OsdTop() + cOsd::OsdHeight() - h, OSD_LEVEL_SUBTITLES); + tArea Areas[] = { { 0, 0, w, h, 8 } }; + if (Setup.AntiAlias && osd->CanHandleAreas(Areas, sizeof(Areas) / sizeof(tArea)) == oeOk) + osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea)); + else { + tArea Areas[] = { { 0, 0, w, h, 4 } }; + osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea)); + } + } + const char *Year = pictureEntry->Parent()->Parent() ? pictureEntry->Parent()->Parent()->Name() : ""; + cString Description = HandleUnderscores(pictureEntry->Parent()->Name()); + osd->DrawRectangle(0, 0, w - 1, h - 1, clrTransparent); + DrawTextOutlined(osd, 0, 0, Description, clrWhite, clrBlack, Font); + DrawTextOutlined(osd, 0, h / 2, Year, clrWhite, clrBlack, Font); + struct stat sb; + if (stat(Path, &sb) == 0) { + cString Time = DayDateTime(sb.st_mtime); + DrawTextOutlined(osd, w - Font->Width(Time), h / 2, Time, clrWhite, clrBlack, Font); + } + p++; + Path.Truncate(-4); // don't display the ".mpg" extension + DrawTextOutlined(osd, w - Font->Width(p), 0, p, clrWhite, clrBlack, Font); + osd->Flush(); +} + +eOSState cPictureControl::ProcessKey(eKeys Key) +{ + switch (Key) { + case kUp: + case kPlay: slideShowDelay.Set(); + slideShow = true; + break; + case kDown: + case kPause: slideShow = false; + break; + case kLeft|k_Repeat: + case kLeft: NextPicture(-1); + slideShow = false; + break; + case kRight|k_Repeat: + case kRight: NextPicture(+1); + slideShow = false; + break; + case kOk: if (osd && !alwaysDisplayCaption) + DELETENULL(osd); + else { + alwaysDisplayCaption = !alwaysDisplayCaption; + DisplayCaption(); + } + break; + case kGreen: + case kPrev: NextDirectory(-1); + slideShow = false; + break; + case kYellow: + case kNext: NextDirectory(+1); + slideShow = false; + break; + case kBlue: + case kStop: return osEnd; + case kBack: slideShow = false; + cRemote::CallPlugin(PLUGIN_NAME_I18N); + break; + default: break; + } + if (slideShow && slideShowDelay.TimedOut()) { + NextPicture(+1); + slideShowDelay.Set(SlideShowDelay * 1000); + } + return osContinue; +} + +const char *cPictureControl::LastDisplayed(void) +{ + return lastDisplayed; +} diff --git a/PLUGINS/src/pictures/player.h b/PLUGINS/src/pictures/player.h new file mode 100644 index 00000000..ab201bd8 --- /dev/null +++ b/PLUGINS/src/pictures/player.h @@ -0,0 +1,47 @@ +/* + * player.h: A player for still pictures + * + * See the README file for copyright information and how to reach the author. + * + * $Id: player.h 1.1 2008/01/13 11:29:27 kls Exp $ + */ + +#ifndef _PLAYER_H +#define _PLAYER_H + +#include +#include +#include +#include "entry.h" + +extern int SlideShowDelay; + +cString HandleUnderscores(const char *s); + +class cPicturePlayer; + +class cPictureControl : public cControl { +private: + static int active; + static cString lastDisplayed; + cPictureEntry *pictures; + const cPictureEntry *pictureEntry; + cPicturePlayer *player; + cOsd *osd; + cString lastPath; + cTimeMs slideShowDelay; + bool slideShow; + bool alwaysDisplayCaption; + void NextPicture(int Direction); + void NextDirectory(int Direction); + void DisplayCaption(void); + virtual void Hide(void) {} +public: + cPictureControl(cPictureEntry *Pictures, const cPictureEntry *PictureEntry, bool SlideShow = false); + virtual ~cPictureControl(); + virtual eOSState ProcessKey(eKeys Key); + static bool Active(void) { return active > 0; } + static const char *LastDisplayed(void); + }; + +#endif //_PLAYER_H diff --git a/PLUGINS/src/pictures/po/de_DE.po b/PLUGINS/src/pictures/po/de_DE.po new file mode 100644 index 00000000..48f942e5 --- /dev/null +++ b/PLUGINS/src/pictures/po/de_DE.po @@ -0,0 +1,32 @@ +# VDR plugin language source file. +# Copyright (C) 2008 Klaus Schmidinger +# This file is distributed under the same license as the VDR package. +# Klaus Schmidinger , 2008. +# +msgid "" +msgstr "" +"Project-Id-Version: pictures 0.0.1\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2008-01-12 17:38+0100\n" +"PO-Revision-Date: 2008-01-12 17:41+0100\n" +"Last-Translator: Klaus Schmidinger \n" +"Language-Team: German\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=ISO-8859-15\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "Pictures" +msgstr "Bilder" + +msgid "A simple picture viewer" +msgstr "Ein einfacher Bildbetrachter" + +msgid "Picture directory" +msgstr "Bilder-Verzeichnis" + +msgid "Slide show delay (s)" +msgstr "Dia-Schau Wartezeit" + +msgid "No picture directory has been defined!" +msgstr "Es wurde kein Bilder-Verzeichnis angegeben!" -- cgit v1.2.3