From a0674cc59c6aaad845ed0a48768fb56b90c41d52 Mon Sep 17 00:00:00 2001 From: Daniel Meyerholt Date: Sun, 6 Feb 2011 16:02:09 +0100 Subject: Init'd using http://www.a-land.de/vdr-vdrrip-0.3.0.tgz --- COPYING | 340 +++++ FAQ | 137 +++ HISTORY | 114 ++ INSTALL | 317 +++++ Makefile | 91 ++ README | 118 ++ TODO | 27 + a-tools.c | 111 ++ a-tools.h | 23 + codecs.c | 200 +++ codecs.h | 36 + i18n.c | 1356 ++++++++++++++++++++ i18n.h | 10 + menu-vdrrip.c | 1275 +++++++++++++++++++ menu-vdrrip.h | 209 ++++ movie.c | 985 +++++++++++++++ movie.h | 163 +++ patches/MPlayer_vdrac3.diff | 38 + queue.c | 289 +++++ queue.h | 69 ++ scripts/queuehandler.sh | 1012 +++++++++++++++ scripts/queuehandler.sh.conf | 71 ++ scripts/sleephalt.sh | 83 ++ scripts/vdrshutdown.sh | 24 + scripts/vdrsync.pl | 2806 ++++++++++++++++++++++++++++++++++++++++++ templates.c | 289 +++++ templates.h | 67 + vdrrip.c | 159 +++ vdrriprecordings.c | 87 ++ vdrriprecordings.h | 27 + xaver.jpg | Bin 0 -> 20042 bytes 31 files changed, 10533 insertions(+) create mode 100755 COPYING create mode 100755 FAQ create mode 100755 HISTORY create mode 100755 INSTALL create mode 100755 Makefile create mode 100755 README create mode 100755 TODO create mode 100755 a-tools.c create mode 100755 a-tools.h create mode 100755 codecs.c create mode 100755 codecs.h create mode 100755 i18n.c create mode 100755 i18n.h create mode 100755 menu-vdrrip.c create mode 100755 menu-vdrrip.h create mode 100755 movie.c create mode 100755 movie.h create mode 100755 patches/MPlayer_vdrac3.diff create mode 100755 queue.c create mode 100755 queue.h create mode 100755 scripts/queuehandler.sh create mode 100755 scripts/queuehandler.sh.conf create mode 100755 scripts/sleephalt.sh create mode 100755 scripts/vdrshutdown.sh create mode 100755 scripts/vdrsync.pl create mode 100755 templates.c create mode 100755 templates.h create mode 100755 vdrrip.c create mode 100755 vdrriprecordings.c create mode 100755 vdrriprecordings.h create mode 100755 xaver.jpg diff --git a/COPYING b/COPYING new file mode 100755 index 0000000..5b6e7c6 --- /dev/null +++ b/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 Library 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 Library General +Public License instead of this License. diff --git a/FAQ b/FAQ new file mode 100755 index 0000000..af36672 --- /dev/null +++ b/FAQ @@ -0,0 +1,137 @@ +What is the bpp-value ? + + This is copied from the file DOCS/tech/encoding-tips.txt in the MPlayer- + source directory: + + $videobitrate * 1024 + $bpp = ----------------------- + $width * $height * $fps + + +What bpp-value should I use ? + + This is copied from the file DOCS/tech/encoding-tips.txt in the MPlayer- + source directory: + + < 0.10: don't do it. Please. I beg you! + < 0.15: It will look bad. + < 0.20: You will notice blocks, but it will look ok. + < 0.25: It will look really good. + > 0.25: It won't really improve visually. + > 0.30: Don't do that either - try a bigger resolution instead. + + +What is matroska ? I have heard it is a "container format" ? What's that ? + + A container format allows you to combine different multimedia streams + (most of the time Audio and video) into one single file. Multimedia + containers are for example the well known AVI (.avi), MPEG (.mpg, .mpeg), + OGM (.ogm), MP4 (.mp4) or Realmedia (.rm, .rmvb) + + The matroska project is a free, opensource container format, aiming to be + able to offer a lot of advanced features, which older formats like AVI + can't handle, on an extensible basis. Matroska supports for example the + storage of Variable Bitrate audio content (VBR) without any hassles, + Variable Framerates (VFR), Chapters, attachment of files, Error Detection + (EDC) and modern A/V Codecs like "Advanced Audio Coding" (AAC), "Ogg + Vorbis" or "Realvideo 9" (RV9), all things AVI can't handle. + + (This text is copied from the "Matroska FAQ" + Forum: New AV/Formats - Containers) + + +Which deinterlacing-Filter should i use ? + + I don't know, but there are really interesting informations/pictures at + . + + +I don't like your encoding-options. How can I change them ? + + feel free to change them in the file queuehandler.sh: + + ... + case "$vcodec" in + "lavc") + local vopts="-ovc lavc -lavcopts vcodec=mpeg4:vhq:vbitrate=$br_video\ + :vqmin=$min_q:vqmax=$max_q:vpass=$pass -sws 2" + ;; + "xvid") + local vopts="-ovc xvid -xvidencopts bitrate=$br_video:me_quality=6:\ + pass=$pass -sws 2" + ;; + "divx4") + local vopts="-ovc divx4 -divx4opts br=$br_video:q=5:min_quant=$min_q\ + :max_quant=$max_q:pass=$pass -sws 2" + ;; + ... + + +The resulting file-size is smaller then the requested. What's the reason ? + + I am using the abr-mode of lame. The resulting audio-bitrate should be + smaller then the requested. You can use vbr or cbr mode (see lame & + mplayer manpage), but then you will get less audio quality. + + +I couldn't add the movie to the queue. What's wrong ? + + You can only add the movie to the queuefile, if the cursor isn't on the + FileName dialog. + + +What does the fields in the file queue.vdrrip mean ? + + e.g.: /video/%Irgendwie_und_Sowieso/2003-09-11.21:43.50.50.rec/001.vdr; + Irgendwie_und_Sowieso;350;1;lavc;896;2;15;712;568;0;4;496;360; + lame;96;0;lb;1;avi;1 + + /video/.../001.vdr: path of the first vdr-file + Irgendwie_und_Sowieso: name of the encoded movie + 350: filesize of one file + 1: number of resulting Files + lavc: video codec + 896: video bitrate + 2: min quantizer + 15: max quantizer + 712: crop width + 568: crop height + 0: crop pos x + 4: crop pos y + 496: scale width + 360: scale height + lame: audio codec + 96: audio bitrate + 0: audio id + lb: postprocess-setting(s) + 1: rename vdr-dir after encoding (0: don't rename) + avi: container-format + 1: encode only preview (0: encode all) + + +Is it possible to do the real encoding process on an other machine within +a network ? + + Yes, it is ! + + First you have to mount two directories (e.g. nfs) on the other machine: + + - /video* to the same mountpoint to access the vdr-files. Read-only is + enough, if you didn't set the parameter "rename after encoding" to + "yes" in the setup-menu. + - //plugins to a arbitary mountpoint (read-write), + because there are the nessecary files queue.vdrrip, lock.vdrrip and + encode.vdrrip. + + Then you only have to install mplayer/mencoder with all needed codecs and + container-tools, and copy the queuehandler (/scripts/queuehandler.sh & + /scripts/queuehandler.sh.conf) and perhaps vdrsync.pl to e.g. + /usr/local/bin. See the table in the file INSTALL for further + informations. + + +Why do I get the error "Can't open main ifo from dvd !" or "Can't open +ifo x !" if I try to encode a DVD ? + + Probably the DVD is encrypted. Copying encrypted DVD's is illegal in many + countries. Don't send any questions regarding encrypted DVD's. diff --git a/HISTORY b/HISTORY new file mode 100755 index 0000000..2dd4448 --- /dev/null +++ b/HISTORY @@ -0,0 +1,114 @@ +VDR Plugin 'vdrrip' Revision History +------------------------------------ + +2003-09-14: Version 0.0.7 + +- Initial revision. + +2003-09-15: Version 0.0.7a + +- added -frames 0 to get -identify in MPlayer1.0pre1 running +- some changes in the README and FAQ - file + +2003-09-15: Version 0.0.7b + +- vdr-recordings can only be selected with selectvdrrip.sh, + if the file marks.vdr is available +- menue changes in menu-vdrrip.c: + VDR to 'encode vdr-rec. (Movie-Name)' (it is only + displayed if a recording is selected +- a lot of changes in the error handling in movie.c: + (vdrrip shouldn't crash, even if mplayer/mencoder isn't + available) + +2003-09-18: Version 0.0.8 + +- add pclose(p) in cMovieBase::queryCodecs() to avoid zombie-processes + (reported by Chrillepup@www.vdrportal.de) +- set the movie names in brackets at the #defines in movie.c + (reported by zuse@www.vdrportal.de) +- (first) code-cleanup of menu-vdrrip.c +- don't save/delete save.vdrrip if the video/audio-codec isn't detected + (reported by dbox.network@www.vdrportal.de) +- add (optional) renaming of encoded vdr-recordings + (thx to memed@www.vdrportal.de) +- selectvdrrip.sh works if the video-directory is a subdirectory + (thx to memed@www.vdrportal.de) +- 001.vdr is copied, if a symbolic-link could not created (for fat-partitions) + (reported by memed@www.vdrportal.de) +- add (alternative) usage of svdrpsend.pl in queuehandler.sh +- fixed OSD-bugs (help-keys etc.) +- add experts-menu (with postprocess-filters) +- add handling of postprocess-filters to queuehandler.sh + +2003-10-12: Version 0.0.9 +- add command line arguments for mplayer/mencoder-location, to avoid + problems with $PATH (see ./vdr --help) +- fix a bug in menu-vdrrip.c (with postprocess-filters) +- fix a bug in menu-vdrrip.c (display Audio-ID, if more then one audio-id is available) +- add templates-handling (templates.c) +- moved codecs-handling to codecs.c +- removed the bitrate-line from osd +- some changes in the README and FAQ - file + +2003-11-24: Version 0.1.0 +- code-cleanup of codecs.c +- add ScaleType & Bpp to templates-handling +- write all messages and errors to syslog instead of stdout/stderr +- add Bpp-Value to expert-menu +- add internationalization support +- changed " to ' at the #defines in movie.c + (thx to memed@www.vdrportal.de) +- changed menu-handling (selectvdrrip.sh is now obsolete) +- some changes in the README and FAQ - file + +2003-12-14: Version 0.1.1 +- add italian translation (thx to tinconn@virgilio.it) +- moved queue-handling to queue.c +- add locking-mechanism to allow the editing of the queue +- changed vdrriprecordings.c to display recordings with more than + two subdirectories (reported by frissi@www.vdrportal.de) +- some cosmetic changes of the menus in menu-vdrrip.c + +2004-01-17: Version 0.2.0 +- add ogg-vorbis support +- add ac3-support +- add ogm-support +- add matroska-support +- now display more audio-informations +- display container-type in queuefile-menu +- get movie length from index.vdr instead of marks.vdr + (the template "default-fixed Bitrate" is now obsolete) +- add a ac3 MPlayer-patch to the /patches directory +- avoid VideoBitrates < 150 in cMovieBase::setBitrate +- avoid AudioBitrates < 32 in cMovieBase::setBitrate +- decrease VideoBitrate if AudioBitrate is increased +- some cosmetic changes of the menus in menu-vdrrip.c +- add french translation (thx to mak_addikt@yahoo.fr) +- splitted the install-section from README to INSTALL +- lot's of changes in the queuehandler.sh + +2004-01-20: Version 0.2.0a +- fixed a bug in cMovie::setCodecs (thx to markus.maeder@oase.ch) +- fixed a bug in queuehandler.sh (reported by cosmo@www.vdrportal.de) + +2004-04-22: Version 0.3.0 +- add dvd as input source +- add one line in i18.c to be compatible with vdr-1.3.2 and newer +- changed cMovie::setCropValues() to support dvd's +- switch between 'crop boarders' and 'reset boarders' possible +- now round crop & scale-values to a multiple of 16 (to avoid errors with + xvid and divx) +- add more deinterlacing filters (requested by sebastian.kemper@web.de) +- now colored items are used to display informations +- decide between preview or full mode while adding a movie to the queue +- close the encoding-menue after adding the movie to the queue (requested + by mariom@www.vdrportal.de) +- display the encoding step in the queue menue (requested by + skyalex@www.vdrportal.de) +- switching between preview mode and normal mode is possible in the + queue menu +- moved 2-pass logfiles of the queuehandler to $tempdir. + (requested by mrmoe@www.vdrportal.de) +- check for mplayer&mencoder (requested by hapemo@www.vdrportal.de) +- some cosmetic changes in queue.c diff --git a/INSTALL b/INSTALL new file mode 100755 index 0000000..916a19f --- /dev/null +++ b/INSTALL @@ -0,0 +1,317 @@ +Installation: +============= + +Vdrrip is able to handle some different output-formats/codecs: + +- lavc, XviD and DivX as video-codecs +- lame, ogg-vorbis, mp2, ac3 as audio-codecs +- avi, ogm, matroska as container-format + +The requirements of vdrrip depend on the audio-codecs and container- +formats, which you want to use. See the following table to know what +you have to install. No fear, you need only MPlayer[5] to create an +avi-file with divx-video(lavc) and mp2/ac3-audio(copy). If you want +mp3-audio for example, you have to install LAME[2a], too. + +======================================================================= +Contanier-formats/ | avi | ogm **) | matroska **) | +Audio-codecs: | | | | +======================================================================= +LAME | 2a/5 | 2a/3b/4a/4b/5 | 2a/3c/4a/4b/5 | +----------------------------------------------------------------------- +ogg-vorbis **) | not possible | 2b/3b/4a/4b/5 | 2b/3c/4a/4b/5 | +----------------------------------------------------------------------- +mpg (stream-copy) | 5 | 3b/4a/4b/5 *) | 3c/4b/5 | +----------------------------------------------------------------------- +ac3 (stream-copy) | 5 | 3b/4b/5 | 3c/4b/5 | +----------------------------------------------------------------------- + +*) It is not possible to mux a mpg2-stream into an ogm-container. So the + mpg2-stream is converted to ac3 (with the same bitrate). + +**) Ogg-Vorbis/ac3/ogm/matroska-support isn't automatically detected by + vdrrip. You have to activate them manually in the plugins-setup menu. + + +See 6) how to encode movies from DVD with vdrrip. + + +1) Video-codecs: +================ + +Use only lavc for a minimal configuration, XviD and DivX are optional ! + + 1a) lavc : + ----------------------------------------- + This open source divx-codec is shipped with MPlayer. It is very fast and + has a really good quality. Highly recommend ! + + + 1b) XviD : + ------------------------------- + XviD is an open source divx-codec. If you want to use it as (additional) + video-codec, you have to run the following commands: + + > cd /usr/local/src + > tar xzvf xvidcore-X.X.X.tgz + > cd xvidcore-X.X.X/build/generic + > ./configure + > make + > make install + > ldconfig (/etc/ld.so.conf should include /usr/local/lib) + + + 1c) DivX 4/5: + ----------------------------------- + DivX is a closed source divx-codec. It is free for personal use. If you + want to use it as (additional) video-codec, you have to run the following + commands: + + > cd /usr/local/src + > tar xzvf divx4linux-XXXXXXXX.tgz + > cd divx4linux-XXXXXXXX + > ./install.sh + + If you use a mplayer version lower then 1.0pre, you need the version + 20020418 from + . Else you + can use the version 5.0.5 from . + + + +2) Audio-codecs: +================ + + 2a) LAME : + --------------------------------------- + Lame is an open source mp3 encoder. Using the LAME encoding engine (or + other mp3 encoding technology) may require a patent license in some + countries . + If you want to use it as audio codec, you have to to run the following + commands: + + > cd /usr/local/src + > tar xzvf lame-X.X.X.tgz + > cd lame-X.X.X + > ./configure + > make + > make install + > ldconfig (/etc/ld.so.conf should include /usr/local/lib) + + + 2b) ogg Vorbis : + --------------------------------------- + Ogg Vorbis is an patent free open source audio encoder. + If you want to use it audio codec, you have to download the following + sources from : + + - libogg-X.X.X.tar.gz, install it with + + > cd /usr/local/src + > tar xzvf libogg-X.X.X.tar.gz + > cd libogg-X.X.X + > ./configure + > make + > make install + > ldconfig (/etc/ld.so.conf should include /usr/local/lib) + + - libvorbis-X.X.X.tar.gz, install it with + > cd /usr/local/src + > tar xzvf libvorbis-X.X.X.tar.gz + > cd libvorbis-X.X.X + > ./configure + > make + > make install + > ldconfig (/etc/ld.so.conf should include /usr/local/lib) + + +3) Container-formats: +===================== + + 3a) avi: + -------- + MPlayer/MEncoder supports the avi-format. You don't have to install + something else. + + + 3b) ogm : + --------------------------------------------------------------- + To use ogm as (additional) container format, you have to install the + OGMtools with: + + > cd /usr/local/src + > tar xjvf ogmtools-X.X.tar.bz2 + > cd ogmtools-X.X + > ./configure + > make + > make install + + OGMtools depend on libogg and libvorbis [see 2b)] ! + + + 3c) matroska : + ---------------------------------------------------------------------- + To use matroska as (additional) container format, you have to install the + following libraries/tools (I had to install libexpat on my testsystem !): + + - libebml: + + > cd /usr/local/src + > tar xjvf libebml-X.X.X.tar.bz2 + > cd libebml-X.X.X/make/linux + > make + > make install + > ldconfig (/etc/ld.so.conf should include /usr/local/lib) + + - libmatroska: + + > cd /usr/local/src + > tar xjvf libmatroska-X.X.X.tar.bz2 + > cd libmatroska-X.X.X/make/linux + > make + > make install + > ldconfig (/etc/ld.so.conf should include /usr/local/lib) + + - mkvtoolnix: + + > cd /usr/local/src + > tar xjvf mkvtoolnix-X.X.X.tar.bz2 + > cd mkvtoolnix-X.X.X + > ./configure + > make + > make install + + Mkvtoolnix depend on libogg and libvorbis [see 2b)] ! + + +4) Tools: +========= + + 4a) ffmpeg : + ------------------------------------------- + ffmpeg is an open source tool, to convert between several video and audio + formats. You only need it together with ogm/matroska-containers. You can + install it with the following commands: + + > cd /usr/local/src + > tar xzvf ffmpeg-X.X.X.tar.gz + > cd ffmpeg-X.X.X + > ./configure --disable-ffserver --disable-ffplay --disable-debug \ + --enable-a52 --enable-mp3lame --enable-vorbis + (only use --enable-mp3lame, if LAME [2a] is installed) + (only use --enable-vorbis, if ogg Vorbis [2b] is installed) + > make + > make install + + + 4b) vdrsync : + ------------------------------------------- + The perl-script vdrsync is used to demux the vdr-recordings. You only + need it together with ogm/matroska-containers. There is a copy of it + (version 0.1.2.2 without Documentation) in the directory /scripts. To use + it, type: + + > cp vdrrip/scripts/vdrsync.pl /usr/local/bin + + The version 0.1.2.2dev1 isn't supported at the moment (because of changes + in the output format) ! + + +5) MPlayer : +===================================== + +All Codecs [see 1/2] have to be installed before the MPlayer installation. +All version higher than 0.90 should run. To compile and install mplayer run +the following commands: + + > cd /usr/local/src + > tar xjvf MPlayer-X.X.bz2 + > cd MPlayer-X.X + > ./configure --with-extraincdir=/include \ + --enable-largefiles + > make + > make install (This mplayer/mencoder-version is used to encode + vdr-streams (without ac3) and dvd's !!!) + + + If you want to encode/copy ac3-audio from vdr-recordings (not dvd's), you + have to install a second, patched (form the directory /patches) version + of MPlayer (thx to viking@www.vdrportal.de): + + > cd /usr/local/src + > cp -a MPlayer-X.X MPlayer-X.X_ac3 + > cd MPlayer-X.X_ac3 + > cat MPlayer_vdrac3.diff | patch libmpdemux/demux_mpg.c + > cd libmpdemux + > make clean + > cd .. + + > ./configure --with-extraincdir=/include \ + --enable-largefiles + > make (don't type make install after this !!!) + > cp mplayer /usr/local/bin/mplayer_ac3 + > cp mencoder /usr/local/bin/mencoder_ac3 + + +6) encoding from DVD: +===================== + +To encode movies from DVD you have to install the library libdvdnav from +: + + > cd /usr/local/src + > tar xzvf libdvdnav-X.X.X.tar.gz + > ./configure + > make + > make install + +Then uncomment the line VDRRIP_DVD=1 in the file Makefile. + +The tested version is libdvdnav-0.1.9. This is the same version which is +recommend for the dvd-plugin. + +The default DVD-device is /dev/dvd. If you have a different path, set a +symlink or specify the value -d DEV in your vdr start-script. + +HINTS: If the kernel-module ide-scsi is loaded, your DVD-device is probably +/dev/sr0 or /dev/sr1. + +And don't forget to give read-privileges to the user which starts vdr. + + +7) vdrrip-plugin: +================= + + > cd vdr/PLUGINS/src + > tar xzvf vdr-vdrrip-X.X.X.tgz + > ln -s vdrrip-X.X.X vdrrip + > cp vdrrip/scripts/queuehandler.sh /usr/local/bin (or e.g. /usr/bin) + > cp vdrrip/scripts/queuehandler.sh.conf /usr/local/bin (or e.g. /usr/bin) + > cd ../.. + > make plugins + + Now you have to adjust the settings in the file queuehandler.sh.conf + + See the README and FAQ how to use the queuehandler. + + + optional: + > cp vdrrip/scripts/vdrshutdown.sh /usr/local/bin (or e.g. /usr/bin) + > cp vdrrip/scripts/sleephalt.sh /usr/local/bin (or e.g. /usr/bin) + + + To load the plugin add '-Pvdrrip' to your vdr start-script. If mplayer/ + mencoder aren't installed to /usr/local/bin, you have to add the + following paramter: + + -p LOC, --MPlayer=LOC use LOC as location of MPlayer + (default is /usr/local/bin/mplayer) + -e LOC, --MEncoder=LOC use LOC as location of MEncoder + (default is /usr/local/bin/mencoder) + This parameter could be used to specify you DVD-device: + -d DEV, --DVD=DEV use DEV as your DVD-device + (default is /dev/dvd) + + e.g: -P'vdrrip -p /bin/mplayer -e /bin/mencoder' + + To use the shutdown scripts, add -s . diff --git a/Makefile b/Makefile new file mode 100755 index 0000000..719ab9d --- /dev/null +++ b/Makefile @@ -0,0 +1,91 @@ +# +# Makefile for a Video Disk Recorder plugin +# +# $Id$ + +# 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. +# +PLUGIN = vdrrip + +### uncomment this line, if you want to encode DVD's +#VDRRIP_DVD=1 + +### 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 ?= -O2 -Wall -Woverloaded-virtual + +### The directory environment: + +DVBDIR = ../../../../DVB +VDRDIR = ../../.. +LIBDIR = ../../lib +TMPDIR = /tmp + +### Allow user defined options to overwrite defaults: + +-include $(VDRDIR)/Make.config + +### The version number of VDR (taken from VDR's "config.h"): + +VDRVERSION = $(shell grep 'define VDRVERSION ' $(VDRDIR)/config.h | awk '{ print $$3 }' | sed -e 's/"//g') + +### The name of the distribution archive: + +ARCHIVE = $(PLUGIN)-$(VERSION) +PACKAGE = vdr-$(ARCHIVE) + +### Includes and Defines (add further entries here): + +INCLUDES += -I$(VDRDIR)/include -I$(DVBDIR)/include + +DEFINES += -D_GNU_SOURCE -DPLUGIN_NAME_I18N='"$(PLUGIN)"' + +### The object files (add further files here): + +OBJS = $(PLUGIN).o \ + menu-vdrrip.o movie.o templates.o codecs.o queue.o vdrriprecordings.o a-tools.o i18n.o + +ifdef VDRRIP_DVD + DEFINES += -DVDRRIP_DVD + LIBS += -ldvdnav +endif + +### Implicit rules: + +%.o: %.c + $(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) $< + +# Dependencies: + +MAKEDEP = g++ -MM -MG +DEPFILE = .dependencies +$(DEPFILE): Makefile + @$(MAKEDEP) $(DEFINES) $(INCLUDES) $(OBJS:%.o=%.c) > $@ + +-include $(DEPFILE) + +### Targets: + +all: libvdr-$(PLUGIN).so + +libvdr-$(PLUGIN).so: $(OBJS) + $(CXX) $(CXXFLAGS) -shared $(OBJS) $(LIBS) -o $@ + @cp $@ $(LIBDIR)/$@.$(VDRVERSION) + +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* *~ diff --git a/README b/README new file mode 100755 index 0000000..156694e --- /dev/null +++ b/README @@ -0,0 +1,118 @@ +This is a "plugin" for the Video Disk Recorder (VDR). + +Written by: herbert attenberger + +Project's homepage: + +Latest version available at: + +See the file COPYING for license information. + +See the file INSTALL for the installation instructions. + + +Description: +============ + +Vdrrip is a plugin to encode the vdr-recordings or dvd's into several +formats. The supported output-formats are: + +Containers: avi, ogm, matroska +Video-codecs(divx): lavc, xvid, divx4/5 +Audio-codecs: mp3, ogg-vorbis, stream-copy for ac3 and mpeg2 + +Ogg-vorbis, ac3, ogm and matroska have to be manually activated in the +setup-menu of the plugin. All further codecs are automatically detected by +the plugin. + +You can manually adjust the encoding values, or simply select a +template (read the section 'Templates' in this manual to find out +more about them). If you input the resulting movie size (or number of +files), the video/audio bitrate is calculated. If you change the video/ +audio bitrate, the file size is calculated. If there is more then one +audio-stream available, you can toggle between them. + +You can choose between four scaling mechanism: + +off: Don't scale, use the original resolution +auto: The scale values are calculated on a algorithm which is based on + the bpp-value. Read the file DOCS/tech/encoding-tips.txt in your + MPlayer-source directory for further informations. The aspect ratio + is corrected +dvb: You can only choose values, which are supported by the DVB-Card + without needing a further scaling while playing. This option + (together with streamcopy as audio codec) is very useful for slow + computers (it is possible to play those files on my Celeron 466 + Mhz). +manual: Select the resolution manually. + +In the auto and manual-mode you can automatically detect and crop the black +movie-boarders with the green key. All settings are saved in the file +save.vdrrip in the movie directory. After this you can add the movie with +the red key to the encoding queue (this is the file queue.vdrrip in the +plugins-directory) and select between preview or full encoding-mode. +The script queuehandler.sh reads the data from the queue, joins +or demuxes the vdr-files and encodes the movie. + +Some parameters of vdrrip can be adjusted in the setup menu. + +DVD's: +====== + +See the INSTALL-file how to compile vdrrip with DVD-support. + +Vdrrip automatically takes the longest title of the DVD. It is possible +to switch between them in the menu. If you press on the title- +line, a more comfortable title-menu is opened. The last settings +are saved in the file /tmp/.vdrrip, and will be restored +if you want to encode the same dvd again. + +WARNING ! + +Most DVDs are encrypted with CSS (Contents Scrambling System). Copying +of encrypted DVDs is illegal in many countries. This program is not +meant for those who intend on breaking copyright laws. Every illegal +use of this software is strictly prohibited. In no way I will be held +responsible if you do. Be aware to check your countries law ! + + +Templates: +========== + +Within a template ('Vdrrip - edit template') you can predefine the encoding- +settings (either with a fixed file size or a fixed bitrate). The default +settings are taken from the template 'default'. You can add further +templates (e.g 1/2-CD, 2/3-CD ...). All templates are saved in the file +templates.vdrrip in the dir /PLUGINS. + + +usage of the quehandler-script: +=============================== + +queuehandler.sh queuefile tempdir + +The queuefile is the file queue.vdrrip in the dir /PLUGINS. +This file is only created, if you add a movie to the queue. After the +encoding the file is deleted by the queuehandler-script. If the movie +couldn't be encoded by the queuehandler because of an error, the queue- +data will be moved to the file queue.vdrrip.rejected. Then you have to look +into the syslog, correct the error and move the movie back to the queue +with the commands: "cat queue.vdrrip.rejected >> queue.vdrrip" and +"rm queue.vdrrip.rejected" + +Tempdir is the directory where all temporary files are created and where the +encoded movies are saved. There should be enough space free ! + +There is no audio-stream in the preview-mode, if the container type is ogm +or matroska ! + + +The file /PLUGINS/encode.vdrrip is created while an active +encoding process. You can use it to keep vdr from an automatic shutdown. +I added my sample scripts vdrshutdown.sh and sleephalt.sh in the dir +scripts. Feel free to use them. Read the file FAQ to see how you can add +the queuehandler to the file commands.conf. + + +Please read the files INSTALL, FAQ and TODO for further informations. +And beware of the dangerous xaver.jpg ... diff --git a/TODO b/TODO new file mode 100755 index 0000000..c073c55 --- /dev/null +++ b/TODO @@ -0,0 +1,27 @@ +high priority: + + +low priority: + +- add pp-filters to templates +- support more then one audio-stream in ogm/matroska-containers + +no priority: + +- add music-cd encoding support +- support different output formats (svcd ...) +- add possibility to burn the encoded movies to a cd/dvd + +done: + +- add setup menu (13.09.2003) +- fix the osd-bugs (help-keys, etc.) (25.09.2003) +- (first) code-cleanup (25.09.2003) +- add menu for postprocessing filters (28.09.2003) +- add script-changes from memed@www.vdrportal.de (28.09.2003) +- add templates (12.10.2003) +- add ScaleType, Bpp to templates(19.10.2003) +- add internationalization support(20.10.2003) +- add ogg/ogm/matroska-formats (13.01.2004) +- add status-informations in the encoding-queue (16.04.2004) +- add dvd encoding support (16.04.2004) diff --git a/a-tools.c b/a-tools.c new file mode 100755 index 0000000..e8d36b5 --- /dev/null +++ b/a-tools.c @@ -0,0 +1,111 @@ +/* a-tools.c: + some tools from herbert attenberger +*/ + +#include +#include +#include +#include + +#include "a-tools.h" + +char *strsub(char* s, int p, int n) { + char *s1; + int l; + + // let's do some checks: + if (s == NULL) {return NULL;} + + l = strlen(s); + if (p > l || p < 1 || n < 1) {return NULL;} + if (p + n > l + 1) {n = l - p + 1;} + + // point s to the beginning of the substring + s = s + p - 1; + + // allocate memory and store the substring in s1 + s1 = (char*) malloc(sizeof(char) * (n + 1)); + memmove(s1, s, sizeof(char) * n); + s1[n] = '\0'; + + return s1; +} + +char *strcol(char *s, char *d, int c) { + char *s1, *s2; + int i; + int l, l1; + + // let's do some checks: + + if (s == NULL) {return NULL;} + if (d == NULL) {return NULL;} + if (c < 1) {return NULL;} + + l = strlen(d); + + // point s to the beginning of the column + for (i = 1; i < c; i++) { + s = strstr(s, d); + if (s == NULL) {return NULL;} + s = s + l; + } + + // point s1 to the end of the column + s1 = strstr(s, d); + if (s1 == NULL) { + // check newline or end of string + s1 = strchr(s, '\n'); + if (s1 == NULL) {s1 = strchr(s,'\0');} + } + + l1 = s1 -s; // store length of column in l1 + + // allocate memory and store the column in s2 + s2 = (char*) malloc(sizeof(char) * (l1 + 1)); + memmove(s2, s, sizeof(char) * l1); + s2[l1] = '\0'; + + return s2; +} + +int strnumcol(const char *s, char *d) { + char *s1; + int i, l; + + // let's do some checks: + if (s == NULL) {return 0;} + if (d == NULL) {return 0;} + + i = 1; + l = strlen(d); + + s1 = strstr(s, d); + while (s1 != NULL) { + i++; + s1 = s1 + l; + s1 = strstr(s1, d); + } + + return i; +} + +char *strgrep(char *s, FILE *f) { + char *s1 = ""; + size_t i = 0; + + // let's do some checks + if (s == NULL) {return NULL;} + + // search the line + while (strstr(s1, s) == NULL) { + if (getline(&s1, &i, f) == -1) { + dsyslog("string %s not found !", s); + return NULL; + } + } + + return s1; +} + +int roundValue(int i, int i1) {return i / i1 * i1;} diff --git a/a-tools.h b/a-tools.h new file mode 100755 index 0000000..312c5ca --- /dev/null +++ b/a-tools.h @@ -0,0 +1,23 @@ +/* a-tools.h: + some tools from herbert attenberger +*/ + +#ifndef __A_TOOLS_H +#define __A_TOOLS_H + +#include + +#define FREE(x) if (x) {free(x); x = NULL;} +#define DELETE(x) if (x) {delete x; x = NULL;} + +char *strsub(char *s, int p, int numbers); + +char *strcol(char *s, char *d, int c); + +int strnumcol(const char *s, char *d); + +char *strgrep(char *s, FILE *f); + +int roundValue(int i, int i1); + +#endif //__A_TOOLS_H diff --git a/codecs.c b/codecs.c new file mode 100755 index 0000000..5b159a2 --- /dev/null +++ b/codecs.c @@ -0,0 +1,200 @@ +/* codecs.c */ + +#include +#include +#include +#include + +#include "codecs.h" +#include "menu-vdrrip.h" +#include "a-tools.h" + +#define MENCCMD "%s %s help 2>/dev/null" +#define VCODECS "lavc, xvid, divx4" +#define ACODECS "lame, copy, ogg-vorbis" + +extern const char *MEncoder; + +// --- cCodecs ------------------------------------------------------------ + +cCodecs::cCodecs() { + VCodecs = ACodecs = Containers = NULL; + + queryCodecs(VCODECS, ACODECS); + queryContainers(); +} + +cCodecs::~cCodecs() { + FREE(VCodecs); + FREE(ACodecs); + FREE(Containers); +} + +void cCodecs::queryCodecs(char *v, char *a) { + char *buf = NULL, *cmd = NULL; + size_t i = 0; + int c, c1, nvc, nac; + + // get number of codecs + nvc = strnumcol(v, ", "); + nac = strnumcol(a, ", "); + + // reserve memory for arrays + VCodecs = (char**)malloc(nvc * sizeof(char*)); + ACodecs = (char**)malloc(nac * sizeof(char*)); + + // read codecs in arryas + for (c = 0; c < nvc; c++) { + VCodecs[c] = strcol(v, ", ", c + 1); + } + for (c = 0; c < nac; c++) { + ACodecs[c] = strcol(a, ", ", c + 1); + } + + // detect available video codecs with mencoder, + // move them to the beginning of the array and + // save the number of supported codecs in NumVCodecs + c1 = 0; + + NumVCodecs = 0; + asprintf(&cmd, MENCCMD, MEncoder, "-ovc"); + FILE *p = popen(cmd, "r"); + if (p) { + while (getline(&buf, &i, p) != -1) { + for (c = c1; c < nvc; c++) { + if (strstr(buf, VCodecs[c])) { + // move found video codec to VCodecs[i1] + char *tmp = VCodecs[c1]; + VCodecs[c1] = VCodecs[c]; + VCodecs[c] = tmp; + c1++; + NumVCodecs++; + } + } + } + } + pclose(p); + FREE(cmd); + + // detect available audio codecs with mencoder, + // move them to the beginning of the array and + // save the number of supported codecs in NumACodecs + c1 = 0; + + NumACodecs = 0; + asprintf(&cmd, MENCCMD, MEncoder, "-oac"); + p = popen(cmd, "r"); + if (p) { + while (getline(&buf, &i, p) != -1) { + for (c = c1; c < nac; c++) { + if (strstr(buf, ACodecs[c])) { + // switch found audio codec with ACodecs[i1] + char *tmp = ACodecs[c1]; + ACodecs[c1] = ACodecs[c]; + ACodecs[c] = tmp; + c1++; + NumACodecs++; + } + } + } + } + pclose(p); + FREE(cmd); + + if (VdrripSetup.OggVorbis == 1) { + ACodecs[NumACodecs] = strdup("ogg-vorbis"); + NumACodecs++; + } + + FREE(buf); +} + +void cCodecs::queryContainers() { + NumContainers = 1; + int i = 1; + if (VdrripSetup.Ogm == 1) {NumContainers++;} + if (VdrripSetup.Matroska == 1) {NumContainers++;} + + Containers = (char**)malloc(NumContainers * sizeof(char*)); + + Containers[0] = "avi"; + + if (VdrripSetup.Ogm == 1) { + Containers[i] = "ogm"; + i++;} + + if (VdrripSetup.Matroska == 1) {Containers[i] = "matroska";} +} + + +int cCodecs::getNumVCodecs() {return NumVCodecs;} + +int cCodecs::getNumACodecs() {return NumACodecs;} + +int cCodecs::getNumContainers() {return NumContainers;} + +char **cCodecs::getVCodecs() {return VCodecs;} + +char **cCodecs::getACodecs() {return ACodecs;} + +char **cCodecs::getContainers() {return Containers;} + +char *cCodecs::getVCodec(int i) {return VCodecs[i];} + +char *cCodecs::getACodec(int i) {return ACodecs[i];} + +char *cCodecs::getContainer(int i) {return Containers[i];} + +int cCodecs::getNumVCodec(char *v) { + int i = 0; + + // check if there are video codecs available + if (NumVCodecs == 0) { + dsyslog("[vdrrip] fatal error: no video codec found !"); + return -2; + } + + while (strcmp(v, VCodecs[i]) != 0) { + i++; + if (i == NumVCodecs) { + dsyslog("[vdrrip] video codec %s not found !", v); + return -1; + } + } + + return i; +} + +int cCodecs::getNumACodec(char *a) { + int i = 0; + + // check if there are audio codecs available + if (NumACodecs == 0) { + dsyslog("[vdrrip] fatal error: no audio codec found !"); + return -2; + } + + while (strcmp(a, ACodecs[i]) != 0) { + i++; + if (i == NumACodecs) { + dsyslog("[vdrrip] audio codec %s not found !", a); + return -1; + } + } + + return i; +} + +int cCodecs::getNumContainer(char *c) { + int i = 0; + + while (strcmp(c, Containers[i]) != 0) { + i++; + if (i == NumContainers) { + dsyslog("[vdrrip] container %s not found !", c); + return -1; + } + } + + return i; +} diff --git a/codecs.h b/codecs.h new file mode 100755 index 0000000..d9f1a2c --- /dev/null +++ b/codecs.h @@ -0,0 +1,36 @@ +// codecs.h + +#ifndef __CODECS_H +#define __CODECS_H + +class cCodecs { + protected: + int NumVCodecs; + int NumACodecs; + int NumContainers; + + char **VCodecs; + char **ACodecs; + char **Containers; + + void queryCodecs(char *v, char *a); + void queryContainers(); + public: + cCodecs(); + ~cCodecs(); + + int getNumVCodecs(); + int getNumACodecs(); + int getNumContainers(); + char *getVCodec(int i); + char *getACodec(int i); + char *getContainer(int i); + int getNumVCodec(char *v); + int getNumACodec(char *a); + int getNumContainer(char *c); + char **getACodecs(); + char **getVCodecs(); + char **getContainers(); +}; + +#endif // __CODECS_H diff --git a/i18n.c b/i18n.c new file mode 100755 index 0000000..b25b29a --- /dev/null +++ b/i18n.c @@ -0,0 +1,1356 @@ +// i18n.c + +#include "i18n.h" + +const tI18nPhrase Phrases[] = { + { "encode vdr-recording", + "VDR-Aufzeichnung encodieren", + "",// TODO + "Comprimi registrazioni di vdr", + "",// TODO + "",// TODO + "Encoder un enregistrement", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO +#if VDRVERSNUM>10301 + "",// TODO +#endif + }, + + { "edit encoding queue", + "Warteschlange bearbeiten", + "",// TODO + "Visualizza coda di compressione", + "",// TODO + "",// TODO + "Voir les encodages en attente", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO +#if VDRVERSNUM>10301 + "",// TODO +#endif + }, + + { "edit templates", + "Schablonen bearbeiten", + "",// TODO + "Editare le maschere", + "",// TODO + "",// TODO + "Editer les gabarits", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO +#if VDRVERSNUM>10301 + "",// TODO +#endif + }, + + { "encoding queue", + "Warteschlange", + "",// TODO + "Coda di compressione", + "",// TODO + "",// TODO + "Queue d'encodage", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO +#if VDRVERSNUM>10301 + "",// TODO +#endif + }, + + { "edit template", + "Schablone bearbeiten", + "",// TODO + "Editare la maschera", + "",// TODO + "",// TODO + "Editer les gabarits", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO +#if VDRVERSNUM>10301 + "",// TODO +#endif + }, + + { "reading movie-data...", + "lese Film-Daten...", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO +#if VDRVERSNUM>10301 + "",// TODO +#endif //VDRVERSNUM + }, + + { "edit", + "bearbeiten", + "",// TODO + "Editare", + "",// TODO + "",// TODO + "Editer", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO +#if VDRVERSNUM>10301 + "",// TODO +#endif + }, + + { "new", + "neu", + "",// TODO + "Nuovo", + "",// TODO + "",// TODO + "Nouveau", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO +#if VDRVERSNUM>10301 + "",// TODO +#endif + }, + + { "delete", + "löschen", + "",// TODO + "Cancellare", + "",// TODO + "",// TODO + "Effacer", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO +#if VDRVERSNUM>10301 + "",// TODO +#endif + }, + + { "delete template %s ?", + "Schablone %s löschen ?", + "",// TODO + "Cancellare maschera %s ?", + "",// TODO + "",// TODO + "Effacer le gabarit %s ?", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO +#if VDRVERSNUM>10301 + "",// TODO +#endif + }, + + { "Name", + "Name", + "",// TODO + "Nome", + "",// TODO + "",// TODO + "Nom", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO +#if VDRVERSNUM>10301 + "",// TODO +#endif + }, + + { "FileSize", + "Datei-Grösse", + "",// TODO + "Dimensione file", + "",// TODO + "",// TODO + "TailleFich", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO +#if VDRVERSNUM>10301 + "",// TODO +#endif + }, + + { "FileNumbers", + "Anzahl Dateien", + "",// TODO + "Numero di file", + "",// TODO + "",// TODO + "Nb fichiers", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO +#if VDRVERSNUM>10301 + "",// TODO +#endif + }, + + { "BitrateVideo", + "Bitrate Video", + "",// TODO + "Video bitrate", + "",// TODO + "",// TODO + "Bitrate Video", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO +#if VDRVERSNUM>10301 + "",// TODO +#endif + }, + + { "BitrateAudio", + "Bitrate Audio", + "",// TODO + "Audio bitrate", + "",// TODO + "",// TODO + "Bitrate Audio", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO +#if VDRVERSNUM>10301 + "",// TODO +#endif + }, + + { "Container", + "Container", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO +#if VDRVERSNUM>10301 + "",// TODO +#endif + }, + + { "Video-Codec", + "Video Codec", + "",// TODO + "Codec video", + "",// TODO + "",// TODO + "Codec Video", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO +#if VDRVERSNUM>10301 + "",// TODO +#endif + }, + + { "Audio-Codec", + "Audio Codec", + "",// TODO + "Codec audio", + "",// TODO + "",// TODO + "Codec Audio", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO +#if VDRVERSNUM>10301 + "",// TODO +#endif + }, + + { "Bpp-Value (*100)", + "Bpp-Wert (*100)", + "",// TODO + "Valore Bpp (*100)", + "",// TODO + "",// TODO + "ValeurBpp (*100)", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO +#if VDRVERSNUM>10301 + "",// TODO +#endif + }, + + { "ScaleType", + "Skalierungsart", + "",// TODO + "Tipo scalatura", + "",// TODO + "",// TODO + "Dimensionnement", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO +#if VDRVERSNUM>10301 + "",// TODO +#endif + }, + + { "encode movie", + "Film encodieren", + "",// TODO + "Compressione video", + "",// TODO + "",// TODO + "Encoder le film", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO +#if VDRVERSNUM>10301 + "",// TODO +#endif + }, + + { "add movie to encoding queue ?", + "Film zur Warteschlange hinzufügen ?", + "",// TODO + "Aggiungere un film alla coda di compressione ?", + "",// TODO + "",// TODO + "Ajouter le film ŕ la queue d'encodage ?", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO +#if VDRVERSNUM>10301 + "",// TODO +#endif + }, + + { "crop black movie boarders ?", + "schwarze Filmränder schneiden ?", + "",// TODO + "Tagliare i bordi neri del video ?", + "",// TODO + "",// TODO + "Découper le film entre les limites noires ?", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO +#if VDRVERSNUM>10301 + "",// TODO +#endif + }, + + { "reset black movie boarders ?", + "schwarze Filmränder löschen ?", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO +#if VDRVERSNUM>10301 + "",// TODO +#endif + }, + + { "search for black movie boarders", + "suche nach schwarzen Filmrändern", + "",// TODO + "Ricerca bordi neri nel video", + "",// TODO + "",// TODO + "Rechercher les limites noires dans le film", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO +#if VDRVERSNUM>10301 + "",// TODO +#endif + }, + + { "couldn't detect black movie boarders !", + "konnte keine schwarzen Filmränder erkennen !", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO +#if VDRVERSNUM>10301 + "",// TODO +#endif + }, + + { "add to queue", + "in Warteschl.", + "",// TODO + "Aggiunto in coda", + "",// TODO + "",// TODO + "Ajout. Queue", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO +#if VDRVERSNUM>10301 + "",// TODO +#endif + }, + + { "crop boarders", + "Ränder schn.", + "",// TODO + "Taglio bordi", + "",// TODO + "",// TODO + "Supp. les limites", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO +#if VDRVERSNUM>10301 + "",// TODO +#endif + }, + + { "reset boarders", + "Ränder lösch.", + "",// TODO + "Taglio bordi", + "",// TODO + "",// TODO + "Supp. les limites", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO +#if VDRVERSNUM>10301 + "",// TODO +#endif + }, + + { "expert modus(off)", + "Exp.-Modus(aus)", + "",// TODO + "Modo esperto(off)", + "",// TODO + "",// TODO + "Mode Expert(off)", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO +#if VDRVERSNUM>10301 + "",// TODO +#endif + }, + + { "expert modus(on)", + "Exp.-Modus(ein)", + "",// TODO + "Modo esperto(on)", + "",// TODO + "",// TODO + "Mode Expert(on)", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO +#if VDRVERSNUM>10301 + "",// TODO +#endif + }, + + { "Template", + "Schablone", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "Gabarit", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO +#if VDRVERSNUM>10301 + "",// TODO +#endif + }, + + { "Audio-Str.", + "Audiospur", + "",// TODO + "Stream audio", + "",// TODO + "",// TODO + "Flux Audio", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO +#if VDRVERSNUM>10301 + "",// TODO +#endif + }, + + { "MovieData", + "Film-Daten", + "",// TODO + "Dati del Film", + "",// TODO + "",// TODO + "Film-Infos", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO +#if VDRVERSNUM>10301 + "",// TODO +#endif + }, + + { "CropData", + "Schnitt-Daten", + "",// TODO + "Valori di taglio", + "",// TODO + "",// TODO + "ValeursDécoupage", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO +#if VDRVERSNUM>10301 + "",// TODO +#endif + }, + + { "ScaleData", + "Skalierung", + "",// TODO + "Valore di scalatura", + "",// TODO + "",// TODO + "ValeursRedimensionnement", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO +#if VDRVERSNUM>10301 + "",// TODO +#endif + }, + + { "ScaleWidth", + "Skal.-breite", + "",// TODO + "Scalatura larghezza", + "",// TODO + "",// TODO + "Largeur Redimensionnement", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO +#if VDRVERSNUM>10301 + "",// TODO +#endif + }, + + { "ScaleHeight", + "Skal.-höhe", + "",// TODO + "Scalatura altezza", + "",// TODO + "",// TODO + "Hauteur Redimensionnement", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO +#if VDRVERSNUM>10301 + "",// TODO +#endif + }, + + { "------ expert settings: ------", + "--- Experten-Einstellungen: ---", + "",// TODO + "------ settaggi esperto: ------", + "",// TODO + "",// TODO + "--- Paramčtres Mode Expert: ---", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO +#if VDRVERSNUM>10301 + "",// TODO +#endif + }, + + { "- adjust crop values:", + "- Schnittwerte anpassen:", + "",// TODO + "- aggiustatura valori di taglio:", + "",// TODO + "",// TODO + "- Ajuster les valeurs de découpage:", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO +#if VDRVERSNUM>10301 + "",// TODO +#endif + }, + + { "CropWidth", + "Schnittbreite", + "",// TODO + "Taglio larghezza", + "",// TODO + "",// TODO + "Largeur Découpage", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO +#if VDRVERSNUM>10301 + "",// TODO +#endif + }, + + { "CropHeight", + "Schnitthöhe", + "",// TODO + "Taglio altezza", + "",// TODO + "",// TODO + "Hauteur Découpage", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO +#if VDRVERSNUM>10301 + "",// TODO +#endif + }, + + { "- postprocessing Filters(%s):", + "- Nachbearbeitungsfilter(%s):", + "",// TODO + "- Filtri dopo il processo(%s):", + "",// TODO + "",// TODO + "- Filtres de PostTraitement(%s):", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO +#if VDRVERSNUM>10301 + "",// TODO +#endif + }, + + { "- remaining:", + "- sonstiges:", + "",// TODO + "- rimanenti:", + "",// TODO + "",// TODO + "- restant:", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO +#if VDRVERSNUM>10301 + "",// TODO +#endif + }, + + { "MaxScaleWidth", + "max. Skal.-breite", + "",// TODO + "Massima scalatura larghezza", + "",// TODO + "",// TODO + "LargeurMaxiRedimensionnement", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO +#if VDRVERSNUM>10301 + "",// TODO +#endif + }, + + { "MinScaleWidth", + "min. Skal.-breite", + "",// TODO + "Minima scalatura larghezza", + "",// TODO + "",// TODO + "LargeurMiniRedimensionnement", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO +#if VDRVERSNUM>10301 + "",// TODO +#endif + }, + + { "Crop Mode", + "Schnittmodus", + "",// TODO + "Modo di taglio", + "",// TODO + "",// TODO + "Mode de découpage", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO +#if VDRVERSNUM>10301 + "",// TODO +#endif + }, + + { "Crop DetectLength (s)", + "Schnitt-Suchdauer (s)", + "",// TODO + "Ricerca larghezza di taglio (s)", + "",// TODO + "",// TODO + "Découpage-Longeur de détection (s)", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO +#if VDRVERSNUM>10301 + "",// TODO +#endif + }, + + { "Rename movie after encoding", + "Film nach Enc. umbenennen", + "",// TODO + "Rinnominare il film dopo la compressione", + "",// TODO + "",// TODO + "Renommer le film aprčs l'encodage", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO +#if VDRVERSNUM>10301 + "",// TODO +#endif + }, + + { "up", + "nach oben", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO +#if VDRVERSNUM>10301 + "",// TODO +#endif + }, + + { "down", + "nach unten", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO +#if VDRVERSNUM>10301 + "",// TODO +#endif + }, + + { "switch mode", + "Modus wechseln", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO +#if VDRVERSNUM>10301 + "",// TODO +#endif + }, + + { " for preview-mode", + " fuer Vorschau-Modus", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO +#if VDRVERSNUM>10301 + "",// TODO +#endif + }, + + { "the queuefile is locked by the queuehandler !", + "die Warteschlange wird momentan vom queuehandler gesperrt !", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO +#if VDRVERSNUM>10301 + "",// TODO +#endif + }, + + + { "delete movie %s from queue ?", + "Film %s von Warteschl. löschen ?", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO +#if VDRVERSNUM>10301 + "",// TODO +#endif + }, + + + { "not used", + "keine Nutzung", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO +#if VDRVERSNUM>10301 + "",// TODO +#endif + }, + + { "unknown", + "unbekannt", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO +#if VDRVERSNUM>10301 + "",// TODO +#endif + }, + + { "not found", + "nicht gefunden", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO +#if VDRVERSNUM>10301 + "",// TODO +#endif + }, + +#ifdef VDRRIP_DVD + { "encode dvd", + "DVD encodieren", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO +#if VDRVERSNUM>10301 + "",// TODO +#endif //VDRVERSNUM + }, + + { "back", + "zurueck", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO +#if VDRVERSNUM>10301 + "",// TODO +#endif //VDRVERSNUM + }, + + { "checking dvd...", + "ueberpruefe dvd...", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO +#if VDRVERSNUM>10301 + "",// TODO +#endif //VDRVERSNUM + }, + + { "Title*", + "Titel*", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO +#if VDRVERSNUM>10301 + "",// TODO +#endif //VDRVERSNUM + }, + + { "accept", + "akzeptieren", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO +#if VDRVERSNUM>10301 + "",// TODO +#endif //VDRVERSNUM + }, + + { "reading audio-data from dvd...", + "lese Audio-Daten von DVD...", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO +#if VDRVERSNUM>10301 + "",// TODO +#endif //VDRVERSNUM + }, + +#endif //VDRRIP_DVD + + { NULL } + }; diff --git a/i18n.h b/i18n.h new file mode 100755 index 0000000..dbdeaef --- /dev/null +++ b/i18n.h @@ -0,0 +1,10 @@ +// i18n.h + +#ifndef _I18N__H +#define _I18N__H + +#include + +extern const tI18nPhrase Phrases[]; + +#endif //_I18N__H diff --git a/menu-vdrrip.c b/menu-vdrrip.c new file mode 100755 index 0000000..356ddba --- /dev/null +++ b/menu-vdrrip.c @@ -0,0 +1,1275 @@ +// +// menu-vdrrip.c +// + +#include +#include + +#ifdef VDRRIP_DVD + #include +#endif //VDRRIP_DVD + +#include "menu-vdrrip.h" +#include "movie.h" +#include "a-tools.h" +#include "templates.h" +#include "queue.h" +#include "vdrriprecordings.h" + +#define MINQUANT 2 +#define MAXQUANT 15 + +#define NUMSCALETYPES 4 +#define NUMPPDEINT 6 + +static const char *ScaleTypes[] = { "off", "auto", "dvb", "manual" }; +static const char *DVBScaleWidths[] = { "352", "480", "544", "688", "704", "720" }; +static const char *DVBScaleHeights[] = { "288", "576" }; +static const char *CropModes[] = { "crop width & height", "crop only height"}; +static const char *PPDeint[] = { "off", "fd", "lb", "li", "ci", "md", }; + +// --- cMenuVdrrip --------------------------------------------------------- + +cMenuVdrrip::cMenuVdrrip():cOsdMenu("Vdrrip") {Set();} + +void cMenuVdrrip::Set() { + Clear(); + + Add(new cOsdItem(tr("encode vdr-recording"), osUser1)); + +#ifdef VDRRIP_DVD + Add(new cOsdItem(tr("encode dvd"), osUser2)); +#endif //VDRRIP_DVD + + Add(new cOsdItem(tr("edit encoding queue"), osUser3)); + Add(new cOsdItem(tr("edit templates"), osUser4)); +} + +eOSState cMenuVdrrip::ProcessKey(eKeys Key) { + + eOSState state = cOsdMenu::ProcessKey(Key); + + switch (state) { + case osUser1: { + AddSubMenu(new cMenuVdrripEncode); + break; + } + +#ifdef VDRRIP_DVD + case osUser2: { + const char *s = "Most DVDs are encrypted with CSS (Contents " + "Scrambling System). Copying of encrypted DVDs is illegal in " + "many countries. This program is not meant for those who intend " + "on breaking copyright laws. Every illegal use of this software " + "is strictly prohibited. In no way I will be held responsible " + "if you do. " + "Be aware to check your countries law !"; + + AddSubMenu(new cMenuVdrripWarning("Warning", s)); + break; + } +#endif + + case osUser3: { + AddSubMenu(new cMenuVdrripQueue); + break; + } + + case osUser4: { + AddSubMenu(new cMenuVdrripTemplates); + break; + } + + default: break; + } + + return state; +} + + +// --- cMenuVdrripWarning ---------------------------------------------------------- + +# ifdef VDRRIP_DVD + +cMenuVdrripWarning::cMenuVdrripWarning(const char *Title, const char *Text) +:cOsdMenu(Title) +{ + bool warning; + warning = true; + //warning = false; + + if (warning) { + Add(new cMenuTextItem(Text, 1, 2, Setup.OSDwidth - 2, MAXOSDITEMS, clrWhite, clrBackground, fontOsd)); + SetHelp(tr("back"), tr("accept"), NULL, NULL); + hadsubmenu = false; + } else { + if (CheckDVD()) AddSubMenu(new cMenuVdrripMovie("dvd://", "")); + hadsubmenu = true; + } +} + +eOSState cMenuVdrripWarning::ProcessKey(eKeys Key) +{ + eOSState state = cOsdMenu::ProcessKey(Key); + + if (HasSubMenu()) { + hadsubmenu = true; + return osContinue; + } + + if (hadsubmenu) {return osBack;} + + switch (Key) { + case kRed: return osBack; + + case kGreen: { + if (CheckDVD()) AddSubMenu(new cMenuVdrripMovie("dvd://", "")); + break; + } + + default: break; + } + + return state; +} + +bool cMenuVdrripWarning::CheckDVD() { + dvd_reader_t *dvd = NULL; + ifo_handle_t *ifo_zero = NULL; + ifo_handle_t *ifo_tmp = NULL; + + Interface->Status(tr("checking dvd...")); + Interface->Flush(); + + if (access(DVD, R_OK) == -1) { + char *s = NULL; + asprintf(&s, "No read privileges on %s !", DVD); + Interface->Error(s); + FREE(s); + return false; + } + + dvd = DVDOpen(DVD); + + if (dvd) { + ifo_zero = ifoOpen(dvd, 0); + if (ifo_zero) { + for (int i = 1; i < ifo_zero->vts_atrt->nr_of_vtss; i++) { + ifo_tmp = ifoOpen(dvd, i); + if (ifo_tmp) ifoClose(ifo_tmp); + else { + char *s = NULL; + asprintf(&s, "Can't open ifo %d !", i); + Interface->Error(s); + FREE(s); + DVDClose(dvd); + return false; + } + } + ifoClose(ifo_zero); + DVDClose(dvd); + return true; + } else { + DVDClose(dvd); + Interface->Error("Can't open main ifo from dvd !"); + return false; + } + } + + char *s = NULL; + asprintf(&s, "Can 't open %s !", DVD); + Interface->Error(s); + FREE(s); + return false; +} + +#endif //VDRRIP_DVD + +// --- cMenuVdrripEncode ---------------------------------------------------- + +cMenuVdrripEncode::cMenuVdrripEncode():cOsdMenu(tr("encode vdr-recording")) { + R = NULL; + + Interface->Status(tr("scanning recordings...")); + Interface->Flush(); + + R = new cVdrripRecordings; + Set(); +} + +cMenuVdrripEncode::~cMenuVdrripEncode() { + DELETE(R); +} + +void cMenuVdrripEncode::Set() { + char *s = NULL; + int i; + + for (i = 0; i < R->getNumRec(); i++) { + asprintf(&s, "%s %s", R->getDate(i), R->getName(i)); + Add(new cOsdItem(s)); + FREE(s); + } +} + +eOSState cMenuVdrripEncode::ProcessKey(eKeys Key) { + + eOSState state = cOsdMenu::ProcessKey(Key); + + if (HasSubMenu()) return osContinue; + + if (Key == kOk) AddSubMenu(new cMenuVdrripMovie(R->getPath(Current()), R->getName(Current()))); + + return state; +} + + +// --- cMenuVdrripQueue ---------------------------------------------------- + +cMenuVdrripQueue::cMenuVdrripQueue():cOsdMenu(tr("edit encoding queue")) { + Q = NULL; + Q = new cQueue; + NumMovie = 0; + Set(); +} + +cMenuVdrripQueue::~cMenuVdrripQueue() { + DELETE(Q); +} + +void cMenuVdrripQueue::Set() { + char *s = NULL, *s1 = NULL; + int c; + struct QueueData *q; + + for (c = 0; c < Q->getNumMovies(); c++) { + q = Q->getData(c); + + asprintf(&s, "%s%s - %s:", (strstr(q->Dir, "dvd://")) ? "DVD": "VDR", (q->Preview == 1) ? " (preview)": "", q->Name); + asprintf(&s1, "- %s, %ix%i MB, %s:%i kbit/s, %s:%i kbit/s", q->Container, q->FileNumbers, q->FileSize, q->VCodec, q->BitrateVideo, q->ACodec, q->BitrateAudio); + if (c == 0 && Q->IsEncoding()) { + AddColItem(new cOsdItem(s)); + AddColItem(new cOsdItem(s1)); + AddColItem(new cOsdItem(Q->getQueueStat())); + } else { + Add(new cOsdItem(s)); + Add(new cOsdItem(s1)); + } + FREE(s); + FREE(s1); + } + + if (Q->getLockStat()) {Interface->Error(tr("the queuefile is locked by the queuehandler !"));} + + SetHelpKeys(); +} + +eOSState cMenuVdrripQueue::ProcessKey(eKeys Key) { + + eOSState state = cOsdMenu::ProcessKey(Key); + + if (HasSubMenu()) return osContinue; + + if (Q->IsEncoding()) { + NumMovie = (Current() - 1) / 2; + if (NumMovie < 0) NumMovie = 0; + } else NumMovie = Current() / 2; + + SetHelpKeys(); + + switch (Key) { + + case kRed: { + if (Delete) { + char *buf = NULL; + asprintf(&buf, tr("delete movie %s from queue ?"), Q->getShortname(NumMovie)); + if (Interface->Confirm(buf)) { + Q->Del(NumMovie); + RefreshOSD(); + } + FREE(buf); + } + break; + } + + case kGreen: { + if (Up) { + Q->Up(NumMovie); + RefreshOSD(); + } + break; + } + + case kYellow: { + if (Down) { + Q->Down(NumMovie); + RefreshOSD(); + } + break; + } + + case kBlue: { + if (Switch) { + Q->Switch(NumMovie); + RefreshOSD(); + } + break; + } + + default: break; + } + + return state; +} + +void cMenuVdrripQueue::RefreshOSD() { + int i = Current(); + Clear(); + Set(); + SetCurrent(Get(i)); + SetHelpKeys(); + Display(); +} + +void cMenuVdrripQueue::SetHelpKeys() { + if (Q->getLockStat() || Q->getNumMovies() == 0 || (NumMovie == 0 && Q->IsEncoding())) { + Delete = Up = Down = Switch = false; + } else { + Delete = true; + Switch = true; + + // up-Key: + if (NumMovie >= 1 && NumMovie < Q->getNumMovies()) { + (NumMovie == 1 && Q->IsEncoding()) ? Up = false : Up = true; + } else {Up = false;} + + // down-key + if (NumMovie >= 0 && NumMovie < Q->getNumMovies() - 1) { + (NumMovie == 0 && Q->IsEncoding()) ? Down = false : Down = true; + } else {Down = false;} + } + + SetHelp(Delete ? tr("delete") : NULL, Up ? tr("up") : NULL, Down ? tr("down") : NULL, Switch ? tr("switch mode") : NULL); +} + +void cMenuVdrripQueue::AddColItem(cOsdItem *i) { +#ifdef clrScrolLine + i->SetColor(clrScrolLine, clrBackground); +#else + i->SetColor(clrCyan, clrBackground); +#endif + + Add(i); +} + +// --- cMenuVdrripTemplates ------------------------------------------------- + +cMenuVdrripTemplates::cMenuVdrripTemplates():cOsdMenu(tr("edit templates")) { + + T = new cTemplate(); + + Set(); + SetHelp(tr("edit"), tr("new"), tr("delete"), NULL); +} + +cMenuVdrripTemplates::~cMenuVdrripTemplates() { + DELETE(T); +} + +void cMenuVdrripTemplates::Set() { + for (int i = 0; i < T->getNumTemplates(); i++) { + Add(new cOsdItem(T->getName(i))); + } + + hadsubmenu = false; +} + +eOSState cMenuVdrripTemplates::ProcessKey(eKeys Key) { + + eOSState state = cOsdMenu::ProcessKey(Key); + + if (HasSubMenu()) { + hadsubmenu = true; + return osContinue; + } + + if (hadsubmenu) {RefreshOSD();} + + switch (Key) { + case kOk: { + AddSubMenu(new cMenuVdrripEditTemplate(T, Current())); + break; + } + + case kRed: { + AddSubMenu(new cMenuVdrripEditTemplate(T, Current())); + break; + } + + case kGreen: { + AddSubMenu(new cMenuVdrripEditTemplate(T, T->New("new"))); + break; + } + + case kYellow: { + char *buf; + asprintf(&buf, tr("delete template %s ?"), T->getShortname(Current())); + if (Interface->Confirm(buf)) {T->Del(Current());} + FREE(buf); + RefreshOSD(); + break; + } + + default: break; + } + + return state; +} + +void cMenuVdrripTemplates::RefreshOSD() { + int i = Current(); + Clear(); + Set(); + SetCurrent(Get(i)); + Display(); +} + + + +// --- cMenuVdrripEditTemplate ----------------------------------------------- + +cMenuVdrripEditTemplate::cMenuVdrripEditTemplate(cTemplate *t, int i):cOsdMenu(tr("edit template"), 15) { + T = t; + NumTemplate = i; + TempOSDsaveName = NULL; + OSDupdate = true; + + SetHelp(tr("ABC/abc"), tr("Overwrite"), tr("Delete"), NULL); + Set(); +} + +cMenuVdrripEditTemplate::~cMenuVdrripEditTemplate() { + FREE(TempOSDsaveName); +} + +void cMenuVdrripEditTemplate::Set() { + + // get data + TempOSD.Name = T->getName(NumTemplate); + TempOSD.FileSize = T->getFileSize(NumTemplate); + TempOSD.FileNumbers = T->getFileNumbers(NumTemplate); + TempOSD.BitrateAudio = T->getBitrateAudio(NumTemplate); + TempOSD.BitrateVideo = T->getBitrateVideo(NumTemplate); + TempOSD.Container = T->getContainer(NumTemplate); + TempOSD.VCodec = T->getVCodec(NumTemplate); + TempOSD.ACodec = T->getACodec(NumTemplate); + TempOSD.ScaleType = T->getScaleType(NumTemplate); + TempOSD.Bpp = T->getBpp(NumTemplate); + + // save data + FREE(TempOSDsaveName); + TempOSDsaveName = strdup(TempOSD.Name); + TempOSDsave = TempOSD; + + // rebuild osd + int i = Current(); + Clear(); + OSDCreate(); + SetCurrent(Get(i)); + Display(); +} + +void cMenuVdrripEditTemplate::OSDCreate() { + + Add(new cMenuEditStrItem(tr("Name"), TempOSD.Name, 32, FileNameChars)); + Add(new cMenuEditIntItem(tr("FileSize"), &TempOSD.FileSize, -1, 9999)); + Add(new cMenuEditIntItem(tr("FileNumbers"), &TempOSD.FileNumbers, 1, 99)); + Add(new cMenuEditIntItem(tr("BitrateVideo"), &TempOSD.BitrateVideo, -1, 99999)); + if (strcmp(T->C->getACodec(TempOSD.ACodec), "copy") == 0 ) { + AddColItem(new cMenuEditIntItem(tr("BitrateAudio"), &TempOSD.BitrateAudio, TempOSD.BitrateAudio, TempOSD.BitrateAudio)); + } else { + Add(new cMenuEditIntItem(tr("BitrateAudio"), &TempOSD.BitrateAudio, 1, 9999)); + } + Add(new cMenuEditStraItem(tr("Container"), &TempOSD.Container, + T->C->getNumContainers(), T->C->getContainers())); + Add(new cMenuEditStraItem(tr("Video-Codec"), &TempOSD.VCodec, + T->C->getNumVCodecs(), T->C->getVCodecs())); + Add(new cMenuEditStraItem(tr("Audio-Codec"), &TempOSD.ACodec, + T->C->getNumACodecs(), T->C->getACodecs())); + Add(new cMenuEditIntItem(tr("Bpp-Value (*100)"), &TempOSD.Bpp, 1, 99)); + Add(new cMenuEditStraItem(tr("ScaleType"), &TempOSD.ScaleType, + NUMSCALETYPES, ScaleTypes)); +} + +void cMenuVdrripEditTemplate::OSDChange() { + + if (TempOSD.FileSize != TempOSDsave.FileSize || TempOSD.FileNumbers != TempOSDsave.FileNumbers) { + T->setFileSize(NumTemplate, TempOSD.FileSize, TempOSD.FileNumbers); + T->Save(); + Set(); + + } else if (TempOSD.BitrateVideo != TempOSDsave.BitrateVideo || TempOSD.BitrateAudio != TempOSDsave.BitrateAudio) { + T->setBitrate(NumTemplate, TempOSD.BitrateVideo, TempOSD.BitrateAudio); + T->Save(); + Set(); + + } else if (TempOSD.VCodec != TempOSDsave.VCodec) { + T->setCodecs(NumTemplate, TempOSD.VCodec, TempOSD.ACodec); + T->Save(); + Set(); + + } else if (TempOSD.ACodec != TempOSDsave.ACodec) { + if (strcmp(T->C->getContainer(TempOSD.Container), "avi") == 0 && + strcmp(T->C->getACodec(TempOSD.ACodec), "ogg-vorbis") == 0) { + // avi couldn't contain ogg-vorbis audio + T->setCodecs(NumTemplate, TempOSD.VCodec, TempOSDsave.ACodec); + } else { + T->setCodecs(NumTemplate, TempOSD.VCodec, TempOSD.ACodec); + } + T->Save(); + Set(); + + } else if (TempOSD.Container != TempOSDsave.Container) { + if (strcmp(T->C->getContainer(TempOSD.Container), "avi") == 0 && + strcmp(T->C->getACodec(TempOSD.ACodec), "ogg-vorbis") == 0) { + // avi couldn't contain ogg-vorbis audio + T->setContainer(NumTemplate, TempOSDsave.Container); + } else { + T->setContainer(NumTemplate, TempOSD.Container); + } + T->Save(); + Set(); + + } else if (TempOSD.ScaleType != TempOSDsave.ScaleType) { + T->setScaleType(NumTemplate, TempOSD.ScaleType); + T->Save(); + Set(); + + } else if (TempOSD.Bpp != TempOSDsave.Bpp) { + T->setBpp(NumTemplate, TempOSD.Bpp); + T->Save(); + Set(); + + } else if (strcmp(TempOSD.Name, TempOSDsaveName) != 0) { + T->setName(NumTemplate, TempOSD.Name); + T->Save(); + } +} + +eOSState cMenuVdrripEditTemplate::ProcessKey(eKeys Key) { + + eOSState state = cOsdMenu::ProcessKey(Key); + + if (Current() == 0) { + SetHelp(tr("ABC/abc"), tr("Overwrite"), tr("Delete"), NULL); + } else { + SetHelp(NULL, NULL, NULL, NULL); + + switch (Key) { + case k0 ... k9: { + OSDupdate = false; + break; + } + + default: { + OSDupdate = true; + break; + } + } + } + + if (OSDupdate) {OSDChange();} + + return state; +} + +void cMenuVdrripEditTemplate::AddColItem(cOsdItem *i) { +#ifdef clrScrolLine + i->SetColor(clrScrolLine, clrBackground); +#else + i->SetColor(clrCyan, clrBackground); +#endif + + Add(i); +} + +// --- cMenuVdrripMovie ------------------------------------------------------ + +cMenuVdrripMovie::cMenuVdrripMovie(char *p, char *n):cOsdMenu(tr("encode movie"), 15) { + M = NULL; + MovOSDsaveName = NULL; + FileSize[0] = MovieData[0] = CropData[0] = ScaleData[0] = NULL; + + Interface->Status(tr("reading movie-data...")); + Interface->Flush(); + + M = new cMovie(p, n); + Init(); + Set(); +} + +cMenuVdrripMovie::~cMenuVdrripMovie() { + DELETE(M); + + FREE(MovOSDsaveName); + FREE(FileSize[0]); + FREE(MovieData[0]); + FREE(CropData[0]); + FREE(ScaleData[0]); +} + +void cMenuVdrripMovie::Init() { + + OSDupdate = true; + Expert = false; + Crop = false; + CropReset = false; + + + // set DVBScaleWidth & DVBScaleHeight + MovOSD.DVBScaleWidth = 0; + MovOSD.DVBScaleHeight = 0; + + if (M->getScaleType() == 2) { + for (int i = 0; i < 6; i++) { + if (atoi(DVBScaleWidths[i]) == M->getScaleWidth()) { + MovOSD.DVBScaleWidth = i; + } + } + + for (int i = 0; i < 2; i++) { + if (atoi(DVBScaleHeights[i]) == M->getScaleHeight()) { + MovOSD.DVBScaleHeight = i; + } + } + } + + CropWidthsave = -1; + CropHeightsave = -1; + + MovOSD.PPDeinterlace = 0; + MovOSD.PPDeblock = 0; + + NumStatic = 0; +} + +void cMenuVdrripMovie::Set() { + + //get movie data + MovOSD.Name = M->getName(); + MovOSD.Template = M->getNumTemplate(); +#ifdef VDRRIP_DVD + if (M->isDVD()) MovOSD.Title = M->getDVDTitle(); +#endif //VDRRIP_DVD + MovOSD.FileSize = M->getFileSize(); + MovOSD.FileNumbers = M->getFileNumbers(); + MovOSD.BitrateAudio = M->getBitrateAudio(); + MovOSD.BitrateVideo = M->getBitrateVideo(); + MovOSD.Container = M->getContainer(); + MovOSD.VCodec = M->getVCodec(); + MovOSD.ACodec = M->getACodec(); + MovOSD.AudioID = M->getAudioID(); + MovOSD.ScaleType = M->getScaleType(); + MovOSD.ScaleWidth = M->getScaleWidth(); + MovOSD.ScaleHeight = M->getScaleHeight(); + + MovOSD.CropWidth = M->getCropWidth(); + MovOSD.CropHeight = M->getCropHeight(); + if (M->getPPValues()) { + if (strstr(M->getPPValues(), "fd")) MovOSD.PPDeinterlace = 1; + else if (strstr(M->getPPValues(), "lb")) MovOSD.PPDeinterlace = 2; + else if (strstr(M->getPPValues(), "li")) MovOSD.PPDeinterlace = 3; + else if (strstr(M->getPPValues(), "ci")) MovOSD.PPDeinterlace = 4; + else if (strstr(M->getPPValues(), "md")) MovOSD.PPDeinterlace = 5; + else MovOSD.PPDeinterlace = 0; + + if (strstr(M->getPPValues(), "hb/vb/dr/al")) MovOSD.PPDeblock = 1; + else MovOSD.PPDeblock = 0; + } else { + MovOSD.PPDeinterlace = 0; + MovOSD.PPDeblock = 0; + } + MovOSD.Bpp = (int)M->getBpp(); + + // save data + FREE(MovOSDsaveName); + MovOSDsaveName = strdup(MovOSD.Name); + MovOSDsave = MovOSD; + + // rebuild osd + int current = Current(); + Clear(); + OSDCreate(); + SetCurrent(Get(current)); + Display(); + + SetHelpKeys(); +} + + +void cMenuVdrripMovie::SetHelpKeys() { + if (Current() == 0) { + SetHelp(tr("ABC/abc"), tr("Overwrite"), tr("Delete"), NULL); + } else { + MovOSD.ScaleType == 1 | MovOSD.ScaleType == 3 ? Crop = true : Crop = false; + MovOSD.CropWidth == -1 && MovOSD.CropHeight == -1 ? CropReset = false : + CropReset = true; + + SetHelp(tr("add to queue"), + Crop ? tr(CropReset ? "reset boarders" : "crop boarders" ) : NULL, + tr(Expert ? "expert modus(off)" : "expert modus(on)"), + NULL); + } +} + + + + +void cMenuVdrripMovie::OSDCreate() { + char *s = NULL, *l = NULL; + cOsdItem *i; + + Add(new cMenuEditStrItem(tr("Name"), MovOSD.Name, 32, FileNameChars)); + + // Templates: + i = new cMenuEditStraItem(tr("Template"), &MovOSD.Template, M->T->getNumTemplates(), M->T->getTNames()); + (M->T->getNumTemplates() > 1) ? Add(i) : AddColItem(i); + FREE(s); + +#ifdef VDRRIP_DVD + if (M->isDVD()) { + i = new cMenuEditIntItem(tr("Title*") , &MovOSD.Title, 1, M->getNumDVDTitles()); + (M->getNumDVDTitles() > 1) ? Add(i) : AddColItem(i); + FREE(s); + } +#endif //VDRRIP_DVD + + // FileSize+Numbers + if (M->getLength() == -1) { + FREE(FileSize[0]); + asprintf(&FileSize[0], "%s", tr("unknown")); + asprintf(&s, "%s", tr("FileSize")); + AddColItem(new cMenuEditStraItem(s, &NumStatic, 1, FileSize)); + asprintf(&s, "%s", tr("FileNumbers")); + AddColItem(new cMenuEditIntItem(s, &MovOSD.FileNumbers, 1, 1)); + FREE(s); + l = strdup("Len: ?"); + } else { + Add(new cMenuEditIntItem(tr("FileSize"), &MovOSD.FileSize, 1, 9999)); + asprintf(&l, "Len: %i", M->getLength()); + Add(new cMenuEditIntItem(tr("FileNumbers"), &MovOSD.FileNumbers, 1, 99)); + } + + // MovieData: + asprintf(&MovieData[0], "%i:%i(Asp: %1.2f %s Fps: %1.2f)", M->getWidth(), + M->getHeight(), M->getAspect(), l, M->getFps() / 100); + AddColItem(new cMenuEditStraItem(tr("MovieData"), &NumStatic, 1, + MovieData)); + FREE(l); + + // CropData: + if (M->getCropWidth() == -1 && M->getCropHeight() == -1) { + if (M->getScaleType() == 0 || M->getScaleType() == 2) { + asprintf(&CropData[0], "%s", tr("not used")); + } else { + asprintf(&CropData[0], "%s", tr("unknown")); + } + } else { + asprintf(&CropData[0], "%i:%i(Asp: %1.2f)", M->getCropWidth(), + M->getCropHeight(), M->getCalcAspect()); + } + AddColItem(new cMenuEditStraItem(tr("CropData"), &NumStatic, 1, CropData)); + + + // Bitrate Video + Add(new cMenuEditIntItem(tr("BitrateVideo"), &MovOSD.BitrateVideo, + 150, 99999)); + + // check, if there is a audio-codec available + if (MovOSD.ACodec != -1) { + if (strcmp(M->C->getACodec(MovOSD.ACodec), "copy") == 0 ) { + AddColItem(new cMenuEditIntItem(tr("BitrateAudio"), + &MovOSD.BitrateAudio, MovOSD.BitrateAudio, MovOSD.BitrateAudio)); + FREE(s); + } else { + Add(new cMenuEditIntItem(tr("BitrateAudio"), &MovOSD.BitrateAudio, + 32, 999)); + } + } + + // Container Format + i = new cMenuEditStraItem(tr("Container"), &MovOSD.Container, + M->C->getNumContainers(), M->C->getContainers()); + (M->C->getNumContainers() > 1) ? Add(i) : AddColItem(i); + FREE(s); + + // check, if there is a video-codec available + if (MovOSD.VCodec != -1) { + Add(new cMenuEditStraItem(tr("Video-Codec"), &MovOSD.VCodec, + M->C->getNumVCodecs(), M->C->getVCodecs())); + Add(new cMenuEditStraItem(tr("Audio-Codec"), &MovOSD.ACodec, + M->C->getNumACodecs(), M->C->getACodecs())); + } + + // Audio-Stream + asprintf(&s, "%s %i*", tr("Audio-Str."), M->getAudioID(MovOSD.AudioID)); + i = new cMenuEditStraItem(s, &MovOSD.AudioID, M->getNumAudioID(), + M->getAudioData()); + FREE(s); + (M->getNumAudioID() > 1) ? Add(i) : AddColItem(i); + + // ScaleType + Add(new cMenuEditStraItem(tr("ScaleType"), &MovOSD.ScaleType, + NUMSCALETYPES, ScaleTypes)); + + switch (MovOSD.ScaleType) { + case 0: { //ScaleType off + break; + } + + case 1: { //ScaleType auto + asprintf(&ScaleData[0], "%i:%i(Asp: %1.2f Bpp: %1.3f)", M->getScaleWidth(), M->getScaleHeight(), (double)M->getScaleWidth() / (double)M->getScaleHeight(), M->getResBpp()); + AddColItem(new cMenuEditStraItem(tr("ScaleData"), &NumStatic, 1, + ScaleData)); + Add(new cMenuEditIntItem(tr("Bpp-Value (*100)"), &MovOSD.Bpp, 1, 99)); + break; + } + + case 2: { //ScaleType dvb + Add(new cMenuEditStraItem(tr("ScaleWidth"), &MovOSD.DVBScaleWidth, 6, DVBScaleWidths)); + Add(new cMenuEditStraItem(tr("ScaleHeight"), &MovOSD.DVBScaleHeight, 2, DVBScaleHeights)); + break; + } + + case 3: { //ScaleType manual + Add(new cMenuEditIntItem(tr("ScaleWidth"), &MovOSD.ScaleWidth, 1, 9999)); + Add(new cMenuEditIntItem(tr("ScaleHeight"), &MovOSD.ScaleHeight, 1, 9999)); + break; + } + + default: + break; + } + + //expert menu: + if (Expert) { + AddColItem(new cOsdItem(tr("------ expert settings: ------"))); + if (MovOSD.CropWidth != -1 && MovOSD.CropHeight != -1) { + AddColItem(new cOsdItem(tr("- adjust crop values:"))); + Add(new cMenuEditIntItem(tr("CropWidth"), &MovOSD.CropWidth, 0, M->getWidth())); + Add(new cMenuEditIntItem(tr("CropHeight"), &MovOSD.CropHeight, 0, M->getHeight())); + } + asprintf(&s, tr("- postprocessing Filters(%s):"), M->getPPValues() ? M->getPPValues() : "off"); + AddColItem(new cOsdItem(s)); + FREE(s); + Add(new cMenuEditStraItem("deinterlacing", &MovOSD.PPDeinterlace, NUMPPDEINT, PPDeint)); + Add(new cMenuEditBoolItem("deblocking", &MovOSD.PPDeblock, "off", "on")); + } + + hadsubmenu = false; +} + + +void cMenuVdrripMovie::OSDChange() { + + if (MovOSD.Template != MovOSDsave.Template) { + // save old crop values + if (M->getScaleType() == 1 || M->getScaleType() == 3) { + CropWidthsave = M->getCropWidth(); + CropHeightsave = M->getCropHeight(); + } + M->setNumTemplate(MovOSD.Template); + // restore old crop values + if (M->getScaleType() == 1 || M->getScaleType() == 3) { + M->setCropValues(CropWidthsave, CropHeightsave); + } + M->saveMovieData(); + Set(); + +#ifdef VDRRIP_DVD + } else if (M->isDVD() && MovOSD.Title != MovOSDsave.Title && MovOSD.Title > 0) { + Interface->Status(tr("reading audio-data from dvd...")); + Interface->Flush(); + M->setDVDTitle(MovOSD.Title, true); + M->saveMovieData(); + Set(); +#endif //VDRRIP_DVD + + } else if ((MovOSD.FileSize != MovOSDsave.FileSize || + MovOSD.FileNumbers != MovOSDsave.FileNumbers) && + MovOSD.FileNumbers > 0) { + M->setFileSize(MovOSD.FileSize, MovOSD.FileNumbers); + M->saveMovieData(); + Set(); + + } else if (MovOSD.BitrateVideo != MovOSDsave.BitrateVideo) { + M->setBitrate(MovOSD.BitrateVideo, MovOSD.BitrateAudio); + M->saveMovieData(); + Set(); + + } else if (MovOSD.BitrateAudio != MovOSDsave.BitrateAudio) { + M->setBitrate(-1 , MovOSD.BitrateAudio); + M->saveMovieData(); + Set(); + + } else if (MovOSD.Container != MovOSDsave.Container) { + if (strcmp(M->C->getContainer(MovOSD.Container), "avi") == 0 && + strcmp(M->C->getACodec(MovOSD.ACodec), "ogg-vorbis") == 0) { + // avi couldn't contain ogg-vorbis audio + M->setContainer(MovOSDsave.Container); + } else { + M->setContainer(MovOSD.Container); + } + M->saveMovieData(); + Set(); + + } else if (MovOSD.VCodec != MovOSDsave.VCodec) { + M->setCodecs(MovOSD.VCodec, MovOSD.ACodec); + M->saveMovieData(); + Set(); + + } else if (MovOSD.ACodec != MovOSDsave.ACodec) { + if (strcmp(M->C->getContainer(MovOSD.Container), "avi") == 0 && + strcmp(M->C->getACodec(MovOSD.ACodec), "ogg-vorbis") == 0) { + // avi couldn't contain ogg-vorbis audio + M->setCodecs(MovOSD.VCodec, MovOSDsave.ACodec); + } else { + M->setCodecs(MovOSD.VCodec, MovOSD.ACodec); + } + M->saveMovieData(); + Set(); + + } else if (MovOSD.AudioID != MovOSDsave.AudioID) { + M->setAudioID(MovOSD.AudioID); + M->saveMovieData(); + Set(); + + } else if (MovOSD.ScaleType != MovOSDsave.ScaleType) { + // restore - save old crop values + if (MovOSDsave.ScaleType == 1 || MovOSDsave.ScaleType == 3) { + CropWidthsave = M->getCropWidth(); + CropHeightsave = M->getCropHeight(); + M->setScaleType(MovOSD.ScaleType); + } else { + M->setScaleType(MovOSD.ScaleType); + M->setCropValues(CropWidthsave, CropHeightsave); + } + M->saveMovieData(); + Set(); + + } else if (MovOSD.ScaleWidth != MovOSDsave.ScaleWidth || + MovOSD.ScaleHeight != MovOSDsave.ScaleHeight) { + M->setScale(MovOSD.ScaleWidth, MovOSD.ScaleHeight); + M->saveMovieData(); + Set(); + + } else if (MovOSD.DVBScaleWidth != MovOSDsave.DVBScaleWidth || + MovOSD.DVBScaleHeight != MovOSDsave.DVBScaleHeight) { + M->setScale(atoi(DVBScaleWidths[MovOSD.DVBScaleWidth]), + atoi(DVBScaleHeights[MovOSD.DVBScaleHeight])); + M->saveMovieData(); + Set(); + + } else if (MovOSD.CropWidth != MovOSDsave.CropWidth || MovOSD.CropHeight != MovOSDsave.CropHeight) { + if (MovOSD.CropWidth <= MovOSDsave.CropWidth) { + MovOSD.CropWidth = roundValue(MovOSD.CropWidth, 16); + } else {MovOSD.CropWidth = roundValue(MovOSD.CropWidth, 16) + 16;} + + if (MovOSD.CropHeight <= MovOSDsave.CropHeight) { + MovOSD.CropHeight = roundValue(MovOSD.CropHeight, 16); + } else {MovOSD.CropHeight = roundValue(MovOSD.CropHeight, 16) + 16;} + + M->setCropValues(MovOSD.CropWidth, MovOSD.CropHeight); + M->saveMovieData(); + Set(); + + } else if (MovOSD.PPDeinterlace != MovOSDsave.PPDeinterlace || MovOSD.PPDeblock != MovOSDsave.PPDeblock) { + if (MovOSD.PPDeinterlace == 0 && MovOSD.PPDeblock == 0) M->setPPValues(NULL); + else if (MovOSD.PPDeinterlace == 0 && MovOSD.PPDeblock == 1) M->setPPValues("hb/vb/dr/al"); + else if (MovOSD.PPDeinterlace >= 1 && MovOSD.PPDeblock == 0) M->setPPValues(PPDeint[MovOSD.PPDeinterlace]); + else { + char *pp = NULL; + asprintf(&pp, "%s/hb/vb/dr/al", PPDeint[MovOSD.PPDeinterlace]); + dsyslog(pp); + M->setPPValues(pp); + FREE(pp); + } + M->saveMovieData(); + Set(); + + } else if (MovOSD.Bpp != MovOSDsave.Bpp) { + M->setBpp(MovOSD.Bpp); + M->saveMovieData(); + Set(); + + } else if (strcmp(MovOSD.Name, MovOSDsaveName) != 0) { + M->setName(MovOSD.Name); + M->saveMovieData(); + } +} + +eOSState cMenuVdrripMovie::ProcessKey(eKeys Key) { + + eOSState state = cOsdMenu::ProcessKey(Key); + + if (HasSubMenu()) { + hadsubmenu = true; + return osContinue; + } + + if (hadsubmenu) { + Set(); + return osContinue; + } + + SetHelpKeys(); + + if (Current() != 0) { + + switch (Key) { + case k0 ... k9: { + OSDupdate = false; + break; + } + + case kRed: { + int p; + Interface->Confirm(tr(" for preview-mode")) ? p = 1 : p = 0; + if (Interface->Confirm(tr("add movie to encoding queue ?"))) { + cQueue *Q; + struct QueueData *q; + Q = new cQueue; + q = (struct QueueData*)malloc(sizeof(struct QueueData)); + + q->Dir = M->getDir(); + q->Name = M->getName(); + q->FileSize = M->getFileSize(); + q->FileNumbers = M->getFileNumbers(); + q->VCodec = M->C->getVCodec(M->getVCodec()); + q->BitrateVideo = M->getBitrateVideo(); + q->MinQuant = MINQUANT; + q->MaxQuant = MAXQUANT; + q->CropWidth = M->getCropWidth(); + q->CropHeight = M->getCropHeight(); + q->CropPosX = M->getCropPosX(); + q->CropPosY = M->getCropPosY(); + q->ScaleWidth = M->getScaleWidth(); + q->ScaleHeight = M->getScaleHeight(); + q->ACodec = M->C->getACodec(M->getACodec()); + q->BitrateAudio = M->getBitrateAudio(); + q->AudioID = M->getAudioID(M->getAudioID()); + q->PPValues = M->getPPValues(); + q->Rename = VdrripSetup.Rename; + q->Container = M->C->getContainer(M->getContainer()); + q->Preview = p; + + if(Q->New(q)) { + FREE(q); + DELETE(Q); + return osBack; + } else {Interface->Error(tr("the queuefile is locked by the queuehandler !"));} + + FREE(q); + DELETE(Q); + + Set(); + } + break; + } + + case kGreen: { + if (MovOSD.ScaleType == 1 | MovOSD.ScaleType == 3) { + if (CropReset) { + if (Interface->Confirm(tr("reset black movie boarders ?"))) { + CropReset = false; + M->initCropValues(); + M->setScale(); + M->saveMovieData(); + } + } else { + if (Interface->Confirm(tr("crop black movie boarders ?"))) { + CropReset = true; + Interface->Status(tr("search for black movie boarders")); + Interface->Flush(); + if (! M->setCropValues()) { + CropReset = false; + Interface->Error(tr("couldn't detect black movie boarders !")); + } + M->saveMovieData(); + } + } + Set(); + } + break; + } + + case kYellow: { + Expert ? Expert = false : Expert = true; + Set(); + break; + } + + case kOk: { + const char *l = Get(Current())->Text(); + if (strstr(l, tr("Audio-Str."))) { + AddSubMenu(new cMenuVdrripMovieAudio(M)); +#ifdef VDRRIP_DVD + } else if (strstr(l, tr("Title*"))) { + AddSubMenu(new cMenuVdrripMovieTitles(M)); +#endif //VDRRIP_DVD + } + break; + } + + default: + OSDupdate = true; + break; + } + } + + if (OSDupdate) {OSDChange();} + + return state; +} + +void cMenuVdrripMovie::AddColItem(cOsdItem *i) { +#ifdef clrScrolLine + i->SetColor(clrScrolLine, clrBackground); +#else + i->SetColor(clrCyan, clrBackground); +#endif + + Add(i); +} + + +// --- cMenuVdrripMovieTitles -------------------------------------------- + +#ifdef VDRRIP_DVD +cMenuVdrripMovieTitles::cMenuVdrripMovieTitles(cMovie *m):cOsdMenu(tr("select dvd title")) { + M = m; + char **s = M->getTitleData(); + for (int i = 0; i < M->getNumDVDTitles(); i++) { + Add(new cOsdItem(s[i])); + } + SetCurrent(Get(M->getDVDTitle() - 1)); + SetHelp(NULL, NULL, NULL, NULL); +} + +cMenuVdrripMovieTitles::~cMenuVdrripMovieTitles() {} + +eOSState cMenuVdrripMovieTitles::ProcessKey(eKeys Key) { + + eOSState state = cOsdMenu::ProcessKey(Key); + + if (Key == kOk) { + Interface->Status(tr("reading audio-data from dvd...")); + Interface->Flush(); + M->setDVDTitle(Current() + 1, true); + M->saveMovieData(); + return osBack; + } + + return state; +} +#endif //VDRRIP_DVD + +// --- cMenuVdrripMovieAudio -------------------------------------------- + +cMenuVdrripMovieAudio::cMenuVdrripMovieAudio(cMovie *m):cOsdMenu(tr("select audio stream(s)")) { + M = m; + char **s = M->getAudioData2(); + for (int i = 0; i < M->getNumAudioID(); i++) { + Add(new cOsdItem(s[i])); + } + SetCurrent(Get(M->getAudioID())); + SetHelp(NULL, NULL, NULL, NULL); +} + +cMenuVdrripMovieAudio::~cMenuVdrripMovieAudio() {} + +eOSState cMenuVdrripMovieAudio::ProcessKey(eKeys Key) { + + eOSState state = cOsdMenu::ProcessKey(Key); + + if (Key == kOk) { + M->setAudioID(Current()); + M->saveMovieData(); + return osBack; + } + + return state; +} + + +// --- cVdrripSetup ----------------------------------------------------------- + +cVdrripSetup VdrripSetup; + +cVdrripSetup::cVdrripSetup(void) +{ + MaxScaleWidth = 704; + MinScaleWidth = 480; + CropMode = 0; + CropLength = 5; + Rename = 0; + OggVorbis = 0; + AC3 = 0; + Ogm = 0; + Matroska = 0; +} + +bool cVdrripSetup::SetupParse(const char *Name, const char *Value) +{ + if (!strcasecmp(Name, "MaxScaleWidth")) MaxScaleWidth = atoi(Value); + else if (!strcasecmp(Name, "MinScaleWidth")) MinScaleWidth = atoi(Value); + else if (!strcasecmp(Name, "CropMode")) CropMode = atoi(Value); + else if (!strcasecmp(Name, "CropLength")) CropLength = atoi(Value); + else if (!strcasecmp(Name, "Rename")) Rename = atoi(Value); + else if (!strcasecmp(Name, "OggVorbis")) OggVorbis = atoi(Value); + else if (!strcasecmp(Name, "AC3")) AC3 = atoi(Value); + else if (!strcasecmp(Name, "Ogm")) Ogm = atoi(Value); + else if (!strcasecmp(Name, "Matroska")) Matroska = atoi(Value); + else + return false; + return true; +} + + +// --- cMenuVdrripSetup -------------------------------------------------------- + +cMenuVdrripSetup::cMenuVdrripSetup() +{ + data = VdrripSetup; + + Add(new cMenuEditIntItem(tr("MaxScaleWidth"), &data.MaxScaleWidth, 1, 9999)); + Add(new cMenuEditIntItem(tr("MinScaleWidth"), &data.MinScaleWidth, 1, 9999)); + Add(new cMenuEditStraItem(tr("Crop Mode"), &data.CropMode, 2, CropModes)); + Add(new cMenuEditIntItem(tr("Crop DetectLength (s)"), &data.CropLength, 1, 999)); + Add(new cMenuEditBoolItem(tr("Rename movie after encoding"), &data.Rename, tr("no"), tr("yes"))); + Add(new cMenuEditBoolItem(tr("Ogg-Vorbis support"), &data.OggVorbis, tr("no"), tr("yes"))); + Add(new cMenuEditBoolItem(tr("AC3 support (MPlayer-patch inst.)"), &data.AC3, tr("no"), tr("yes"))); + Add(new cMenuEditBoolItem(tr("Ogm support"), &data.Ogm, tr("no"), tr("yes"))); + Add(new cMenuEditBoolItem(tr("Matroska support"), &data.Matroska, tr("no"), tr("yes"))); +} + +void cMenuVdrripSetup::Store(void) +{ + // delete unused setup data + SetupStore("FileSize"); + SetupStore("FileNumbers"); + SetupStore("LameAudioBitrate"); + SetupStore("Bpp"); + + + VdrripSetup = data; + SetupStore("MaxScaleWidth", VdrripSetup.MaxScaleWidth); + SetupStore("MinScaleWidth", VdrripSetup.MinScaleWidth); + SetupStore("CropMode", VdrripSetup.CropMode); + SetupStore("CropLength", VdrripSetup.CropLength); + SetupStore("Rename", VdrripSetup.Rename); + SetupStore("OggVorbis", VdrripSetup.OggVorbis); + SetupStore("AC3", VdrripSetup.AC3); + SetupStore("Ogm", VdrripSetup.Ogm); + SetupStore("Matroska", VdrripSetup.Matroska); +} diff --git a/menu-vdrrip.h b/menu-vdrrip.h new file mode 100755 index 0000000..476e8d3 --- /dev/null +++ b/menu-vdrrip.h @@ -0,0 +1,209 @@ +// +// menu-vdrrip.h +// + +#ifndef __MENU_VDRRIP_H +#define __MENU_VDRRIP_H + +#include +#include + +#include "movie.h" +#include "vdrriprecordings.h" +#include "templates.h" +#include "queue.h" +#include "codecs.h" + +struct MovieOSDData { + char *Name; + int Title; + int Template; + int FileSize; + int FileNumbers; + int BitrateVideo; + int BitrateAudio; + int Container; + int VCodec; + int ACodec; + int AudioID; + int ScaleWidth; + int ScaleHeight; + int ScaleType; + int DVBScaleWidth; + int DVBScaleHeight; + + //expert menu: + int CropWidth; + int CropHeight; + int PPDeinterlace; + int PPDeblock; + int Bpp; +}; + +class cMenuVdrrip : public cOsdMenu { +private: + virtual void Set(); + +public: + cMenuVdrrip(); + virtual eOSState ProcessKey(eKeys Key); +}; + +#ifdef VDRRIP_DVD + +class cMenuVdrripWarning : public cOsdMenu { +private: + bool hadsubmenu; + + bool CheckDVD(); +public: + cMenuVdrripWarning(const char *Title, const char *Text); + virtual eOSState ProcessKey(eKeys Key); +}; + +#endif //VDRRIP_DVD}; + +class cMenuVdrripEncode : public cOsdMenu { +private: + virtual void Set(); + + cVdrripRecordings *R; +public: + cMenuVdrripEncode(); + ~cMenuVdrripEncode(); + virtual eOSState ProcessKey(eKeys Key); +}; + +class cMenuVdrripQueue : public cOsdMenu { +private: + virtual void Set(); + void RefreshOSD(); + void SetHelpKeys(); + void AddColItem(cOsdItem *i); + + cQueue *Q; + int NumMovie; + bool Delete, Up, Down, Switch; + +public: + cMenuVdrripQueue(); + ~cMenuVdrripQueue(); + virtual eOSState ProcessKey(eKeys Key); +}; + +class cMenuVdrripTemplates : public cOsdMenu { +private: + virtual void Set(); + void RefreshOSD(); + + cTemplate *T; + bool hadsubmenu; +public: + cMenuVdrripTemplates(); + ~cMenuVdrripTemplates(); + virtual eOSState ProcessKey(eKeys Key); +}; + +class cMenuVdrripEditTemplate : public cOsdMenu { +private: + virtual void Set(); + void OSDChange(); + void OSDCreate(); + void AddColItem(cOsdItem *i); + + cTemplate *T; + int NumTemplate; + struct TemplateData TempOSD, TempOSDsave; + char *TempOSDsaveName; + + bool OSDupdate; +public: + cMenuVdrripEditTemplate(cTemplate *t, int i); + ~cMenuVdrripEditTemplate(); + virtual eOSState ProcessKey(eKeys Key); +}; + + +class cMenuVdrripMovie : public cOsdMenu { +private: + virtual void Set(); + void Init(); + void OSDChange(); + void OSDCreate(); + void SetHelpKeys(); + void AddColItem(cOsdItem *i); + + cMovie *M; + struct MovieOSDData MovOSD, MovOSDsave; + char *MovOSDsaveName; + char *Templates; + char *FileSize[1]; + char *MovieData[1]; + char *CropData[1]; + char *ScaleData[1]; + + bool OSDupdate, Crop, CropReset, Expert; + + int CropWidthsave; + int CropHeightsave; + + int NumStatic; + + bool hadsubmenu; + +public: + cMenuVdrripMovie(char *p, char *n); + ~cMenuVdrripMovie(); + virtual eOSState ProcessKey(eKeys Key); +}; + +class cMenuVdrripMovieTitles : public cOsdMenu { +private: + cMovie *M; +public: + cMenuVdrripMovieTitles(cMovie *m); + ~cMenuVdrripMovieTitles(); + virtual eOSState ProcessKey(eKeys Key); +}; + +class cMenuVdrripMovieAudio : public cOsdMenu { +private: + cMovie *M; +public: + cMenuVdrripMovieAudio(cMovie *m); + ~cMenuVdrripMovieAudio(); + virtual eOSState ProcessKey(eKeys Key); +}; + + +class cVdrripSetup { +public: + int MaxScaleWidth; + int MinScaleWidth; + int CropMode; + int CropLength; + int Rename; + int OggVorbis; + int AC3; + int Ogm; + int Matroska; + +public: + cVdrripSetup(); + bool SetupParse(const char *Name, const char *Value); +}; + + +class cMenuVdrripSetup : public cMenuSetupPage { +private: + cVdrripSetup data; +protected: + virtual void Store(void); +public: + cMenuVdrripSetup(); +}; + +extern cVdrripSetup VdrripSetup; + +#endif //__MENU_VDRRIP_H + diff --git a/movie.c b/movie.c new file mode 100755 index 0000000..4664913 --- /dev/null +++ b/movie.c @@ -0,0 +1,985 @@ +// +// movie.c +// + +#include +#include +#include +#include + +#ifdef VDRRIP_DVD + #include +#endif //VDRRIP_DVD + +#include + +#include "movie.h" +#include "menu-vdrrip.h" +#include "a-tools.h" +#include "queue.h" +#include "templates.h" + +#define SAVEFILE "save.vdrrip" + +#define IDENTCMD "%s \'%s\'%s -identify -frames 0 2>/dev/null | sed -e \'s/[`\\!$\"]/\\&/g\'" +#define CROPCMD "%s \'%s\'%s -vo null -ao null -really-quiet -ss %i -frames %i -vop cropdetect 2>/dev/null | grep \"crop=\" | sed \"s/.*crop\\(.*\\)).*/\\1/\" | sort | uniq -c | sort -r" +#define AUDIOCMD "%s \'%s/001.vdr\' -vo null -ao null -frames 0 -aid %i 2>/dev/null | grep AUDIO" +#define AUDIOCMDDVD "%s %s -vo null -ao null -frames 0 -aid %i 2>/dev/null | grep AUDIO" +#define MENCCMD "%s %s help 2>/dev/null" + +// --- cMovie ------------------------------------------------------------ + + +cMovie::cMovie(char *d, char *n) { + C = NULL; + T = NULL; +#ifdef VDRRIP_DVD + D = NULL; + StrTitles = NULL; +#endif //VDRRIP_DVD + + Dir = OrigName = Name = PPValues = NULL; + A = NULL; + StrAudioData = StrAudioData2 = NULL; + + Dir = strdup(d); + Name = strdup(n); + + // detect codecs + C = new cCodecs(); + + // detect templates + T = new cTemplate(); + + // init some values + AudioID = 0; + initCropValues(); + + if (strstr(Dir, "dvd://")) { + Dvd = true; + +#ifdef VDRRIP_DVD + // detect dvd-data + queryDVDName(); + queryDVDData(); + + if (! restoreMovieData()) { + NumTemplate = T->getNumTemplate(TDEFAULT); + + setDVDTitle(Title, true); + // save Movie Data + saveMovieData(); + } +#endif //VDRRIP_DVD + } else { + Dvd = false; + + // detect vdr-data + setLengthVDR(); + queryMpValuesVDR(); + queryAudioDataVDR(); + + if (! restoreMovieData()) { + // set to default template + NumTemplate = T->getNumTemplate(TDEFAULT); + setNumTemplate(NumTemplate); + + // save Movie Data + saveMovieData(); + } + } +} + +cMovie::~cMovie() { + int i; + + DELETE(C); + DELETE(T); + + FREE(Dir); + FREE(OrigName); + FREE(Name); + FREE(PPValues); + + for (i = 0; i < NumAudioID; i++) { + FREE(A[i].Lang); + FREE(A[i].Format); + FREE(StrAudioData[i]); + FREE(StrAudioData2[i]); + } + FREE(A); + FREE(StrAudioData); + FREE(StrAudioData2); + +#ifdef VDRRIP_DVD + if (Dvd) { + for (i = 0; i < NumTitles; i++) { + //TODO: fix this + //for (i1 = 0; i1 < D[i].NumAudio; i1++) { + //FREE(D[i].A[i1].Lang); + //FREE(D[i].A[i1].Format); + //} + FREE(D[i].A); + if (StrTitles) FREE(StrTitles[i]); + } + FREE(D); + FREE(StrTitles); + } +#endif // VDRRIP_DVD +} + +bool cMovie::isDVD() {return Dvd;} + +void cMovie::setFileSize(int s, int n) { + FileNumbers = n; + + if (s == -1 ) { + // calculate FileSize + if (Length < 1) { + FileSize = -1; + } else { + FileSize = Bitrate * Length / FileNumbers / 8 / 1024; + } + } else { + // calculate Bitrate + FileSize = s; + setBitrate(-1, BitrateAudio); + } +} + +void cMovie::setName(char *n) {Name = n;} + +void cMovie::setNumTemplate(int i) { + // init some values + NumTemplate = i; + Bpp = T->getBpp(NumTemplate); + FileNumbers = T->getFileNumbers(NumTemplate); + FileSize = T->getFileSize(NumTemplate); + ScaleType = T->getScaleType(NumTemplate); + + // the rest is done here ... + setContainer(T->getContainer(NumTemplate)); + setCodecs(T->getVCodec(NumTemplate), T->getACodec(NumTemplate)); +} + +int cMovie::getFileSize() {return FileSize;} + +int cMovie::getFileNumbers() {return FileNumbers;} + +void cMovie::setBitrate(int v, int a) { + // avoid BitrateAudio < 32 + if (a < 32 && strcmp(C->getACodec(ACodec), "copy") != 0) {a = 32;} + BitrateAudio = a; + + if (v == -1) { + // calculate BitrateVideo + if (FileSize == -1) { + // fixed Bitrate + Bitrate = BitrateVideo + BitrateAudio; + } else { + // fixed FileSize + Bitrate = FileSize * 1024 * FileNumbers * 8 / Length; + + // avoid BitrateVideo < 150 + if (Bitrate <= BitrateAudio + 150) { + Bitrate = BitrateAudio + 150; + setFileSize(-1, FileNumbers); + } + + // avoid BitrateVideo > 99999 + if (Bitrate > BitrateAudio + 99999) { + Bitrate = BitrateAudio + 99999; + setFileSize(-1, FileNumbers); + } + + BitrateVideo = Bitrate - BitrateAudio; + } + } else { + // calculate FileSize + BitrateVideo = v; + Bitrate = BitrateVideo + BitrateAudio; + setFileSize(-1, FileNumbers); + } + + setScale(); +} + +void cMovie::setContainer(int c) { + if (c >= 0 && c < C->getNumContainers()) {Container = c; + } else { + dsyslog("[vdrrip] unknown container, falling back to avi !"); + Container = C->getNumContainer("avi"); + } +} + +void cMovie::setScaleType(int s) { + ScaleType = s; + setScale(); +} + +void cMovie::setScale() { + switch (ScaleType) { + case 0: { + // off + ScaleWidth = -1; + ScaleHeight = -1; + initCropValues(); + break; + } + + case 1: { + // auto: this is based on encoding-tips.txt from the mplayer-documentation + ScaleWidth = roundValue((int)sqrt(BitrateVideo * 1024 * CalcAspect * 100 / Bpp /Fps), 16); + if (ScaleWidth > VdrripSetup.MaxScaleWidth) {ScaleWidth = VdrripSetup.MaxScaleWidth;} + if (ScaleWidth < VdrripSetup.MinScaleWidth) {ScaleWidth = VdrripSetup.MinScaleWidth;} + ScaleHeight = roundValue((int)(ScaleWidth / CalcAspect), 16); + break; + } + + case 2: { + // dvb: set default dvb-values + ScaleWidth = 352; + ScaleHeight = 288; + initCropValues(); + break; + } + + case 3: { + // manual + ScaleWidth = Width; + ScaleHeight = Height; + break; + } + + } +} + +void cMovie::setScale(int width, int height) { + ScaleWidth = width; + ScaleHeight = height; +} + +void cMovie::setCropValues(int width, int height) { + CropWidth = width; + if (CropWidth > Width) CropWidth = Width; + + CropHeight = height; + if (CropHeight > Height) CropHeight = Height; + + if (CropWidth == -1) CropPosX = -1; + else CropPosX = (Width - CropWidth) / 2; + + if (CropHeight == -1) { + CropPosY = -1; + CalcAspect = Aspect; + } else { + CropPosY = (Height - CropHeight) / 2; + CalcAspect = Height * Aspect / CropHeight; + } + + setScale(); +} + + +bool cMovie::setCropValues() { + char *cmd = NULL, *buf = NULL; + bool ret = true; + + size_t i = 0; + int l = 0; + int l1; + + if (Dvd) {asprintf(&cmd, IDENTCMD, MPlayer, Dir, ""); + } else {asprintf(&cmd, IDENTCMD, MPlayer, Dir, "/001.vdr");} + + FILE *p = popen(cmd, "r"); + if (p) { + char *s = NULL; + s = strcol(strgrep("ID_LENGTH", p), "=", 2); + if (s) {l = atoi(s);} + FREE(s); + } else { + dsyslog("[vdrrip] could not open pipe to %s !", cmd); + } + pclose(p); + FREE(cmd); + + + l1 = VdrripSetup.CropLength * (int)Fps; + if (Dvd) { + asprintf(&cmd, CROPCMD, MPlayer, Dir, "", l/2, l1); + isyslog("[vdrrip] detecting crop values in %s", Dir); + } else { + asprintf(&cmd, CROPCMD, MPlayer, Dir, "/001.vdr", l/2, l1); + isyslog("[vdrrip] detecting crop values in %s/001.vdr", Dir); + } + p = popen(cmd, "r"); + FREE(cmd); + + if (p) { + // get first line + if (getline(&buf, &i, p) != -1) { + char *s = NULL; + + s = strcol(buf, "=", 2); + CropWidth = roundValue(atoi(strcol(s, ":", 1)), 16); + if (CropWidth > Width || CropWidth < 0) { + ret = false; + CropWidth = Width; + } + + CropHeight = roundValue(atoi(strcol(s, ":", 2)), 16); + if (CropHeight > Height || CropHeight < 0) { + ret = false; + CropHeight = Height; + } + + if (VdrripSetup.CropMode == 1) {CropWidth = Width;} + CropPosX = (Width - CropWidth) / 2; + CropPosY = (Height - CropHeight) / 2; + // CalcAspect is changed now: + CalcAspect = Height * Aspect / CropHeight; + setScale(); + + FREE(s); + FREE(buf); + } else { + ret = false; + } + + pclose(p); + } else ret = false; + + if (! ret) initCropValues(); + + return ret; +} + +void cMovie::initCropValues() { + CropWidth = -1; + CropHeight = -1; + CropPosX = -1; + CropPosY = -1; + CalcAspect = Aspect; +} + +void cMovie::setCodecs(int v, int a) { + // validate video codec + if (v >= 0 && v < C->getNumVCodecs()) {VCodec = v; + } else { + dsyslog("[vdrrip] unknown video codec, falling back to %s !", C->getVCodec(0)); + VCodec = 0; + } + + // validate audio codec + if (a >= 0 && a < C->getNumACodecs()) { + if (strcmp(C->getContainer(Container), "avi") == 0 && + strcmp(C->getACodec(a), "ogg-vorbis") == 0) { + dsyslog("[vdrrip] avi couldn't contain ogg-vorbis audio, falling back to copy !"); + ACodec = C->getNumACodec("copy"); + } else {ACodec = a;} + } else { + dsyslog("[vdrrip] unknown audio codec, falling back to copy !"), + ACodec = C->getNumACodec("copy"); + } + + // set audio bitrates + if (ACodec == C->getNumACodec("copy")) { + setBitrate(T->getBitrateVideo(NumTemplate), A[AudioID].Bitrate); + } else { + setBitrate(T->getBitrateVideo(NumTemplate), T->getBitrateAudio(NumTemplate)); + } +} + +void cMovie::setBpp(int i) { + Bpp = i; + setScale(); +} + +int cMovie::getBitrateAudio() {return BitrateAudio;} + +int cMovie::getBitrateVideo() {return BitrateVideo;} + +int cMovie::getLength() {return Length;} + +int cMovie::getWidth() {return Width;} + +int cMovie::getHeight() {return Height;} + +double cMovie::getAspect() {return Aspect;} + +double cMovie::getCalcAspect() {return CalcAspect;} + +double cMovie::getFps() {return Fps;} + +int cMovie::getContainer() {return Container;} + +int cMovie::getScaleType() {return ScaleType;} + +int cMovie::getScaleWidth() {return ScaleWidth;} + +int cMovie::getScaleHeight() {return ScaleHeight;} + +int cMovie::getCropWidth() {return CropWidth;} + +int cMovie::getCropHeight() {return CropHeight;} + +int cMovie::getCropPosX() {return CropPosX;} + +int cMovie::getCropPosY() {return CropPosY;} + +double cMovie::getResBpp() { + return (double)BitrateVideo * 1024. / (double)ScaleWidth / (double)ScaleHeight / Fps;} + +double cMovie::getBpp() {return (double)Bpp;} + +char *cMovie::getName() {return Name;} + +int cMovie::getNumTemplate() {return NumTemplate;} + +char *cMovie::getDir() {return Dir;} + +int cMovie::getNumAudioID() {return NumAudioID;} + +int cMovie::getAudioID() {return AudioID;} + +void cMovie::setAudioID(int i) { + if (i >= 0 && i < NumAudioID) { + AudioID = i; + } else { + dsyslog("[vdrrip] %d is not a valid audio-id, falling back to 0 !", AudioID); + AudioID = 0; + } + setCodecs(VCodec, ACodec); +} + +int cMovie::getVCodec() {return VCodec;} + +int cMovie::getACodec() {return ACodec;} + +void cMovie::setPPValues(const char *pp) { + FREE(PPValues); + if (pp) { + PPValues = strdup(pp); + } else { + PPValues = NULL; + } +} + +const char* cMovie::getPPValues() {return PPValues;} + + +// --- VDR-Movie --------------------------------------------------------- + + +void cMovie::setLengthVDR() { + char *file = NULL; + + asprintf(&file, "%s/index.vdr", Dir); + FILE *f = fopen(file, "r"); + if (f) { + fseek(f, 0, SEEK_END); + Length = ftell(f) / 200; + fclose(f); + } else { + dsyslog("[vdrrip] could not open file %s !", file); + dsyslog("[vdrrip] perhaps you have to create it with genindex.c !"); + Length = -1; + } + + FREE(file); +} + + +void cMovie::queryMpValuesVDR() { + char *cmd = NULL, *s = NULL; + + asprintf(&cmd, IDENTCMD, MPlayer, Dir, "/001.vdr"); + FILE *p = popen(cmd, "r"); + if (p) { + s = strcol(strgrep("ID_VIDEO_WIDTH", p), "=", 2); + if (s) { + Width = atoi(s); + } else {Width = -1;} + FREE(s); + + s = strcol(strgrep("ID_VIDEO_HEIGHT", p), "=", 2); + if (s) { + Height = atoi(s); + } else {Height = -1;} + FREE(s); + + s = strcol(strgrep("ID_VIDEO_FPS", p), "=", 2); + if (s) { + Fps = atof(s); + } else {Fps = -1;} + FREE(s); + + s = strcol(strgrep("ID_VIDEO_ASPECT", p), "=", 2); + if (s) { + Aspect = atof(s); + } else {Aspect = -1;} + + CalcAspect = Aspect; + + pclose(p); + } else {dsyslog("[vdrrip] could not open pipe to %s !", cmd);} + + FREE(s); + FREE(cmd); +} + + +void cMovie::queryAudioDataVDR() { + char *cmd = NULL, *buf = NULL; + size_t i = 0; + int n = 0; + int c = 0; + bool next = true; + + while (next) { + asprintf(&cmd, AUDIOCMD, MPlayer, Dir, c); + FILE *p = popen(cmd, "r"); + if (p) { + if (getline(&buf, &i, p) != -1) { + if (c == 128) {next = false;} + A = (struct AudioData*)realloc(A, (n + 1) * sizeof(struct AudioData)); + + A[n].AudioID = c; + A[n].Lang = strdup(tr("unknown")); + + if (c == 128) {A[n].Format = strdup("ac3"); + } else {A[n].Format = strdup("mp2");} + + char *s = NULL; + s = strcol(buf, " ", 2); + if (s) {A[n].Freq = atoi(s); + } else {A[n].Freq = 0;} + FREE(s); + + s = strcol(buf, " ", 4); + if (s) {A[n].Chan = atoi(s); + } else {A[n].Chan = 0;} + FREE(s); + + s = strcol(buf, " ", 11); + if (s) { + A[n].Bitrate = atoi(s + sizeof(char)); + } else {A[n].Bitrate = 192;} + FREE(s); + + // isyslog("[vdrrip] Audio-ID %i found: lang %s, format %s, %i kbit, %i Hz, %i ch", A[n].AudioID, A[n].Lang, A[n].Format, A[n].Bitrate, A[n].Freq, A[n].Chan); + n++; + c++; + } else { + // nothing found: + if (c < 128 && VdrripSetup.AC3 == 1) {c = 128; + } else {next = false;} + } + pclose(p); + } else {dsyslog("[vdrrip] could not open pipe to %s !", cmd);} + } + + NumAudioID = n; + + // write AudioData to an array + if (NumAudioID > 0) { + StrAudioData = (char **)malloc(NumAudioID * sizeof(char*)); + StrAudioData2 = (char **)malloc(NumAudioID * sizeof(char*)); + for (c = 0; c < NumAudioID; c++) { + asprintf(&StrAudioData[c], "%s, %i kbit, lang: %s", A[c].Format, A[c].Bitrate, A[c].Lang); + + asprintf(&StrAudioData2[c], "%d: %s, %d kbit, %i chan, %d hz, lang: %s", A[c].AudioID, A[c].Format, A[c].Bitrate, A[c].Chan, A[c].Freq, A[c].Lang); + } + } else { + dsyslog("[vdrrip] no Audio ID found !"); + A = (struct AudioData*)malloc(sizeof(struct AudioData)); + A[0].Lang = strdup(tr("unknown")); + A[0].Format = strdup(tr("unknown")); + A[0].Bitrate = 0; + A[0].Freq = 0; + A[0].Chan = 0; + A[0].AudioID = 0; + + StrAudioData = (char **)malloc(sizeof(char*)); + asprintf(&StrAudioData[0], tr("not found")); + NumAudioID = 1; + } + + FREE(buf); + FREE(cmd); +} + +int cMovie::getAudioID(int i) { + if (i >= 0 && i < NumAudioID) { + return A[i].AudioID; + } else {return 0;} +} + +char **cMovie::getAudioData() {return StrAudioData;} + +char **cMovie::getAudioData2() {return StrAudioData2;} + +void cMovie::saveMovieData() { + char *file = NULL; + + if (Dvd) { + if (OrigName) asprintf(&file, "/tmp/%s.vdrrip", OrigName); + else return; + } else asprintf(&file, "%s/%s", Dir, SAVEFILE); + + FILE *f = fopen(file,"w"); + if (f) { + fprintf(f,"%s;%i;%i;%i;%s;%i;%i;%i;%i;%i;%i;%i;%i;%i;%s;%i;%i;%s;%s;%s;%d\n", + Name, FileSize, FileNumbers, Bitrate, C->getVCodec(VCodec), BitrateVideo, + CropWidth, CropHeight, CropPosX, CropPosY, ScaleType, ScaleWidth, + ScaleHeight, Bpp, C->getACodec(ACodec), BitrateAudio, AudioID, PPValues, + T->getName(NumTemplate), C->getContainer(Container), +#ifdef VDRRIP_DVD + Dvd ? Title : 0 +#else + 0 +#endif //VDRRIP_DVD + ); + + fclose(f); + } else {dsyslog("[vdrrip] could not open file %s !", file);} + + FREE(file); +} + +bool cMovie::restoreMovieData() { + char *file = NULL, *vcodec = NULL, *acodec = NULL, *tname = NULL, + *container = NULL, *buf = NULL; + size_t i = 0; + + if (Dvd) { + if (OrigName) asprintf(&file, "/tmp/%s.vdrrip", OrigName); + else return false; + } else asprintf(&file, "%s/%s", Dir, SAVEFILE); + + FILE *f = fopen(file,"r"); + if (f) { + if (getline(&buf, &i, f) != -1) { + Name = strcol(buf, ";", 1); + FileSize = atoi(strcol(buf, ";", 2)); + FileNumbers = atoi(strcol(buf, ";", 3)); + Bitrate = atoi( strcol(buf, ";", 4)); + vcodec = strcol(buf, ";", 5); + BitrateVideo = atoi(strcol(buf, ";", 6)); + CropWidth = atoi(strcol(buf, ";", 7)); + CropHeight = atoi(strcol(buf, ";", 8)); + CropPosX = atoi(strcol(buf, ";", 9)); + CropPosY = atoi(strcol(buf, ";", 10)); + ScaleType = atoi(strcol(buf, ";", 11)); + ScaleWidth = atoi(strcol(buf, ";", 12)); + ScaleHeight = atoi(strcol(buf, ";", 13)); + Bpp = atoi(strcol(buf, ";", 14)); + acodec = strcol(buf, ";", 15); + BitrateAudio = atoi(strcol(buf, ";", 16)); + AudioID = atoi(strcol(buf, ";", 17)); + PPValues = strcol(buf, ";", 18); + if (strcmp(PPValues, "(null)") == 0) PPValues = NULL; + tname = strcol(buf, ";", 19); + // migrate from version 0.1.1 + container = strcol(buf, ";", 20); + if (! container) container = strdup("avi"); +#ifdef VDRRIP_DVD + // migrate from version 0.2.0a + if (Dvd) Title = atoi(strcol(buf, ";", 21)); +#endif //VDRRIP_DVD + + FREE(buf); + + fclose(f); + isyslog("[vdrrip] restored data from file %s !", file); + + // validate some values: + if (! Dvd) setCropValues(CropWidth, CropHeight); + + NumTemplate = T->getNumTemplate(tname); + if (NumTemplate == -1) { + dsyslog("[vdrrip] %s is not a valid template, falling back to default !", tname); + NumTemplate = T->getNumTemplate(TDEFAULT); + } + FREE(tname); + +#ifdef VDRRIP_DVD + if (Dvd) setDVDTitle(Title, false); +#endif //VDRRIP_DVD + + setContainer(C->getNumContainer(container)); + FREE(container); + + setCodecs(C->getNumVCodec(vcodec), C->getNumACodec(acodec)); + FREE(vcodec); + FREE(acodec); + + setAudioID(AudioID); + + } else { + dsyslog("[vdrrip] could not read data from file %s !", file); + FREE(file); + return false; + } + } else { + dsyslog("[vdrrip] could not open file %s, perhaps it is not available !", file); + FREE(file); + return false; + } + FREE(file); + + saveMovieData(); + return true; +} + + +// --- DVD-Movie --------------------------------------------------------- + +#ifdef VDRRIP_DVD +void cMovie::queryDVDName() { + int i; + char name[33]; + + FILE *f = fopen(DVD, "r"); + + if (f) { + if (! fseek(f, 32808, SEEK_SET )) { + i = fread(name, 1, 32, f); + if (i == 32) { + name[32] = '\0'; + while(i-- > 2) {if (name[i] == ' ') name[i] = '\0';} + Name = strdup(name); + OrigName = strdup(name); + } else { + dsyslog("[vdrrip] Couldn't read enough bytes for title !"); + Name = strdup(tr("unknown")); + } + } else { + dsyslog("[vdrrip] Couldn't seek in %s for title", DVD); + Name = strdup(tr("unknown")); + } + + fclose(f); + } else { + dsyslog("[vdrrip] Couldn't open %s for title", DVD); + Name = strdup(tr("unknown")); + } +} + +void cMovie::queryDVDData() { + // + // parts of this code are pasted from the tool lsdvd by chris phillips + // which is hosted at http://sourceforge.net/projects/acidrip/ + // + // thx a lot... + // + dvd_reader_t *dvd = NULL; + ifo_handle_t *ifo_zero = NULL, **ifo = NULL; + pgcit_t *vts_pgcit; + vtsi_mat_t *vtsi_mat; + vmgi_mat_t *vmgi_mat; + audio_attr_t *audio_attr; + video_attr_t *video_attr; + pgc_t *pgc; + dvd_time_t *dt; + + int i, i1, vts_ttn, numifos, numifo; + int l = 0; + + Title = 1; + NumTitles = 0; + + const char *audio_format[] = {"ac3", "?", "mpeg1", "mp2", "lpcm ", "sdds ", "dts"}; + const char *sample_freq[] = {"48000", "48000"}; + const int height[] = {480, 576}; + const int width[] = {720, 704, 352, 352}; + const double aspect[] = {4.0/3.0, 16.0/9.0, 1/1, 16.0/9.0}; + const double fps[] = {-1.0, 25.00, -1.0, 29.97}; + + + dvd = DVDOpen(DVD); + + if (dvd) { + ifo_zero = ifoOpen(dvd, 0); + + if (ifo_zero) { + // read needed data from ifo_zero + numifos = ifo_zero->vts_atrt->nr_of_vtss; + vmgi_mat = ifo_zero->vmgi_mat; + NumTitles = ifo_zero->tt_srpt->nr_of_srpts; + + // reserve memory for numifos ifos + ifo = (ifo_handle_t **)malloc((numifos + 1) * sizeof(ifo_handle_t *)); + + // save ifo data + for (i = 1; i <= numifos; i++) { + ifo[i] = ifoOpen(dvd, i); + if (! ifo[i]) dsyslog("[vdrrip] Can't open ifo %d !", i); + } + + // reserve memory for DVDData + D = (struct DVDData *)malloc((NumTitles + 1) * sizeof(struct DVDData)); + + // read movie data + for (i = 0; i < NumTitles; i++) { + // get ifo number for title i + numifo = ifo_zero->tt_srpt->title[i].title_set_nr; + + if (ifo[numifo]->vtsi_mat) { + vtsi_mat = ifo[numifo]->vtsi_mat; + vts_pgcit = ifo[numifo]->vts_pgcit; + video_attr = &vtsi_mat->vts_video_attr; + vts_ttn = ifo_zero->tt_srpt->title[i].vts_ttn; + vmgi_mat = ifo_zero->vmgi_mat; + pgc = vts_pgcit->pgci_srp[ifo[numifo]->vts_ptt_srpt->title[vts_ttn - 1].ptt[0].pgcn - 1].pgc; + dt = &pgc->playback_time; + + // read the movie-data of the title into the struc DVDData: + D[i].Length = (((dt->hour & 0xf0) >> 3) * 5 + (dt->hour & 0x0f)) * 3600; + D[i].Length += (((dt->minute & 0xf0) >> 3) * 5 + (dt->minute & 0x0f)) * 60; + D[i].Length += (((dt->second & 0xf0) >> 3) * 5 + (dt->second & 0x0f)); + if (D[i].Length == 0) {D[i].Length = -1;} + + D[i].Width = width[video_attr->picture_size]; + D[i].Height = height[video_attr->video_format]; + D[i].Aspect = aspect[video_attr->display_aspect_ratio]; + D[i].Fps = fps[(pgc->playback_time.frame_u & 0xc0) >> 6]; + + D[i].NumAudio = vtsi_mat->nr_of_vts_audio_streams; + // reserve memory for DVDAudioData + D[i].A = (struct AudioData*)malloc(D[i].NumAudio * sizeof(struct AudioData)); + + for (i1 = 0; i1 < D[i].NumAudio; i1++) { + audio_attr = &vtsi_mat->vts_audio_attr[i1]; + + asprintf(&D[i].A[i1].Lang, "%c%c", audio_attr->lang_code >> 8, audio_attr -> lang_code & 0xff); + D[i].A[i1].Format = strdup(audio_format[audio_attr->audio_format]); + D[i].A[i1].Freq = atoi(sample_freq[audio_attr->sample_frequency]); + D[i].A[i1].Chan = audio_attr->channels+1; + D[i].A[i1].AudioID = 128 + i1; + D[i].A[i1].Bitrate = -1; + } + + // save number of the longest Title + if (D[i].Length > l) { + l = D[i].Length; + Title = i + 1; + LongestTitle = Title; + } + } + } + + // close ifos + ifoClose(ifo_zero); + for (i = 1; i <= numifos; i++) { + if(ifo[i]) ifoClose(ifo[i]); + } + FREE(ifo); + + + DVDClose(dvd); + } else {dsyslog("[vdrrip] Can't open main ifo !");} + } else {dsyslog("[vdrrip] Can't open disc %s !", DVD);} + + +} + + +void cMovie::setDVDTitle(int t, bool st) { + + if (t > 0 && t <= NumTitles) Title = t; + else { + dsyslog("[vdrrip] Unknown title %d, setting back to longest Title %d !", t, LongestTitle); + Title = LongestTitle; + st = true; + } + + // set directory-name (for the queuehandler) + FREE(Dir); + asprintf(&Dir, "dvd://%i", Title); + + // set video data: + Length = D[Title - 1].Length; + Width = D[Title - 1].Width; + Height = D[Title - 1].Height; + Fps = D[Title - 1].Fps; + Aspect = D[Title - 1].Aspect; + + if (st) initCropValues(); // no restore + else setCropValues(CropWidth, CropHeight); + + // set audio data: + if (st) AudioID = 0; // no restore + NumAudioID = D[Title - 1].NumAudio; + + if (NumAudioID > 0) { + A = (struct AudioData*)realloc(A, NumAudioID * sizeof(struct AudioData)); + StrAudioData = (char **)realloc(StrAudioData, NumAudioID * sizeof(char*)); + StrAudioData2 = (char **)realloc(StrAudioData2, NumAudioID * sizeof(char*)); + + for (int i = 0; i < NumAudioID; i++) { + + // get audio bitrate + if (D[Title -1].A[i].Bitrate == -1) {D[Title -1].A[i].Bitrate = queryAudioBrDVD(D[Title -1].A[i].AudioID);} + A[i] = D[Title - 1].A[i]; + + asprintf(&StrAudioData[i], "%s, %d kbit, lang: %s", A[i].Format, A[i].Bitrate, A[i].Lang); + + asprintf(&StrAudioData2[i], "%d: %s, %d kbit, %d chan, %d hz, lang: %s", A[i].AudioID, A[i].Format, A[i].Bitrate, A[i].Chan, A[i].Freq, A[i].Lang); + } + } else { + dsyslog("[vdrrip] no Audio ID found !"); + A = (struct AudioData*)realloc(A, sizeof(struct AudioData)); + A[0].Lang = strdup(tr("unknown")); + A[0].Format = strdup(tr("unknown")); + A[0].Bitrate = 0; + A[0].Freq = 0; + A[0].Chan = 0; + A[0].AudioID = 0; + + StrAudioData = (char **)realloc(StrAudioData, sizeof(char*)); + asprintf(&StrAudioData[0], tr("not found")); + NumAudioID = 1; + } + + if (st) setNumTemplate(NumTemplate); // no restore +} + +int cMovie::queryAudioBrDVD(int c) { + char *cmd = NULL, *buf = NULL; + size_t i = 0; + int b = 0; + + asprintf(&cmd, AUDIOCMDDVD, MPlayer, Dir, c); + FILE *p = popen(cmd, "r"); + if (p) { + if (getline(&buf, &i, p) != -1) { + char *s = strcol(buf, " ", 11); + if (s) { + b = atoi(s + sizeof(char)); + FREE(s); + } + } + pclose(p); + } else {dsyslog("[vdrrip] could not open pipe to %s !", cmd);} + + + FREE(buf); + FREE(cmd); + + return b; +} + +int cMovie::getDVDTitle() {return Title;} + +int cMovie::getNumDVDTitles() {return NumTitles;} + +char **cMovie::getTitleData() { + StrTitles = (char **)malloc(NumTitles * sizeof(char*)); + for (int i = 0; i < NumTitles; i++) + asprintf(&StrTitles[i], "Title %d: %i audio channels, length: %d sec.", i + 1, D[i].NumAudio, D[i].Length); + + return StrTitles; +} +#endif //VDRRIP_DVD diff --git a/movie.h b/movie.h new file mode 100755 index 0000000..1b257ff --- /dev/null +++ b/movie.h @@ -0,0 +1,163 @@ +/* movie.h */ + +#ifndef __MOVIE_H +#define __MOVIE_H + +#include + +#include "templates.h" +#include "codecs.h" + + +extern const char *MPlayer; +#ifdef VDRRIP_DVD + extern const char *DVD; +#endif //VDRRIP_DVD + +struct AudioData { + char* Lang; + char* Format; + int Bitrate; + int Freq; + int Chan; + int AudioID; +}; + +#ifdef VDRRIP_DVD + struct DVDData { + int Length; + int Width; + int Height; + double Aspect; + double Fps; + int NumAudio; + struct AudioData *A; + }; +#endif //VDRRIP_DVD + + +class cMovie { + protected: + bool Dvd; + + char *OrigName; + char *Name; + char *Dir; + int Length; + int FileSize; + int FileNumbers; + int NumTemplate; + int Bitrate; + int BitrateVideo; + int BitrateAudio; + int Width; + int Height; + double Aspect; + double CalcAspect; + double Fps; + int ScaleType; + int ScaleWidth; + int ScaleHeight; + int CropWidth; + int CropHeight; + int CropPosX; + int CropPosY; + int Bpp; + int Container; + int VCodec; + int ACodec; + int AudioID; + int NumAudioID; + struct AudioData *A; + char *PPValues; + + char *MarksFile; + char **StrAudioData; + char **StrAudioData2; + +#ifdef VDRRIP_DVD + int Title; + int LongestTitle; + int NumTitles; + char **StrTitles; + struct DVDData* D; +#endif + + public: + cMovie(char *d, char *n); + ~cMovie(); + + cTemplate *T; + cCodecs *C; + + bool isDVD(); + + void setName(char *n); + char *getName(); + char *getDir(); + void setNumTemplate(int i); + int getNumTemplate(); + void setBitrate(int v, int a); + int getBitrateVideo(); + int getBitrateAudio(); + int getLength(); + + void setFileSize(int s, int n); + int getFileSize(); + int getFileNumbers(); + int getHeight(); + int getWidth(); + double getAspect(); + double getCalcAspect(); + double getFps(); + void setScaleType(int s); + void setScale(); + void setScale(int width, int height); + int getScaleType(); + int getScaleWidth(); + int getScaleHeight(); + void setCropValues(int width, int height); + bool setCropValues(); + int getCropHeight(); + int getCropWidth(); + int getCropPosX(); + int getCropPosY(); + void setBpp(int i); + double getBpp(); + double getResBpp(); + void initCropValues(); + void setContainer(int c); + int getContainer(); + void setCodecs(int v, int a); + int getVCodec(); + int getACodec(); + void setAudioID(int i); + int getAudioID(); + int getNumAudioID(); + void setPPValues(const char *pp); + const char* getPPValues(); + char** getAudioData(); + char** getAudioData2(); + int getAudioID(int i); + + // VDR-Movie + void setLengthVDR(); + void queryMpValuesVDR(); + void queryAudioDataVDR(); + + void saveMovieData(); + bool restoreMovieData(); + +#ifdef VDRRIP_DVD + // DVD + void queryDVDName(); + void queryDVDData(); + void setDVDTitle(int t, bool st); + int getDVDTitle(); + int queryAudioBrDVD(int c); + int getNumDVDTitles(); + char** getTitleData(); +#endif //VDRRIP_DVD +}; + +#endif //__MOVIE_H diff --git a/patches/MPlayer_vdrac3.diff b/patches/MPlayer_vdrac3.diff new file mode 100755 index 0000000..6acdd15 --- /dev/null +++ b/patches/MPlayer_vdrac3.diff @@ -0,0 +1,38 @@ +--- libmpdemux/demux_mpg.c.orig Fri Apr 4 17:38:46 2003 ++++ libmpdemux/demux_mpg.c Mon Jun 16 11:54:20 2003 +@@ -124,7 +124,7 @@ + + //============== DVD Audio sub-stream ====================== + if(id==0x1BD){ +- int aid=stream_read_char(demux->stream);--len; ++ int aid=128; //stream_read_char(demux->stream);--len; + if(len<3) return -1; // invalid audio packet + + // AID: +@@ -145,10 +145,11 @@ + ds=demux->sub; + } + +- } else if((aid & 0xC0) == 0x80 || (aid & 0xE0) == 0x00) { ++ } else if((aid & 0xC0) == 0x80) { + + // aid=128+(aid&0x7F); + // aid=0x80..0xBF ++// if(aid<0x20) aid+=0x80; // hack + + if(!demux->a_streams[aid]) new_sh_audio(demux,aid); + if(demux->audio->id==-1) demux->audio->id=aid; +@@ -158,11 +159,13 @@ + ds=demux->audio; + if(!ds->sh) ds->sh=demux->a_streams[aid]; + // READ Packet: Skip additional audio header data: ++ #if 0 + c=stream_read_char(demux->stream);//num of frames + type=stream_read_char(demux->stream);//startpos hi + type=(type<<8)|stream_read_char(demux->stream);//startpos lo + // printf("\r[%02X][%04X]",c,type); + len-=3; ++ #endif + if((aid&0xE0)==0xA0 && len>=3){ + unsigned char* hdr; + // save audio header as codecdata! diff --git a/queue.c b/queue.c new file mode 100755 index 0000000..354ca08 --- /dev/null +++ b/queue.c @@ -0,0 +1,289 @@ +// +// queue.c +// + +#include +#include +#include + +#include "queue.h" +#include "a-tools.h" + +cQueue::cQueue() { + Q = NULL; + + WaitUnlock(); + if (! Locked) {Lock();} + Load(); +} + +cQueue::~cQueue() { + if (! Locked) {Unlock();} + DELETE(Q); +} + +void cQueue::Load() { + char *buf = NULL; + size_t i = 0; + int c = 0; + + FILE *q = fopen(AddDirectory(cPlugin::ConfigDirectory(), QUEUEFILE), "r"); + if (q) { + // read QueueData + while (getline(&buf, &i, q) != -1) { + // reserve memory for QueueData + Q = (struct QueueData*)realloc(Q, (c + 1) * sizeof(struct QueueData)); + Q[c].Dir = strcol(buf, ";", 1); + Q[c].Name = strcol(buf, ";", 2); + Q[c].FileSize = atoi(strcol(buf, ";", 3)); + Q[c].FileNumbers = atoi(strcol(buf, ";", 4)); + Q[c].VCodec = strcol(buf, ";", 5); + Q[c].BitrateVideo = atoi(strcol(buf, ";", 6)); + Q[c].MinQuant = atoi(strcol(buf, ";", 7)); + Q[c].MaxQuant = atoi(strcol(buf, ";", 8)); + Q[c].CropWidth = atoi(strcol(buf, ";", 9)); + Q[c].CropHeight = atoi(strcol(buf, ";", 10)); + Q[c].CropPosX = atoi(strcol(buf, ";", 11)); + Q[c].CropPosY = atoi(strcol(buf, ";", 12)); + Q[c].ScaleWidth = atoi(strcol(buf, ";", 13)); + Q[c].ScaleHeight = atoi(strcol(buf, ";", 14)); + Q[c].ACodec = strcol(buf, ";", 15); + Q[c].BitrateAudio = atoi(strcol(buf, ";", 16)); + Q[c].AudioID = atoi(strcol(buf, ";", 17)); + Q[c].PPValues = strcol(buf, ";", 18); + Q[c].Rename = atoi(strcol(buf, ";", 19)); + // migrate from version 0.1.1 + char *cont = strcol(buf, ";", 20); + cont ? Q[c].Container = cont : Q[c].Container = strdup("avi"); + // migrate from version 0.2.0a + char *prev = strcol(buf, ";", 21); + prev ? Q[c].Preview = atoi(prev) : Q[c].Preview = 0; + + FREE(buf); + c++; + } + + fclose(q); + } else {dsyslog("[vdrrip] could not open file %s, the queue is probably empty !", QUEUEFILE);} + + NumMovies = c; +} + + +bool cQueue::Save() { + FILE *q; + int c; + int n = 0; + + if (Locked) { + Load(); + return false; + } + + q = fopen(AddDirectory(cPlugin::ConfigDirectory(), QUEUEFILE), "w"); + if (q) { + for (c = 0; c < NumMovies; c++) { + if (strcmp(Q[c].Name, "delete") != 0) { + fprintf(q,"%s;%s;%i;%i;%s;%i;%i;%i;%i;%i;%i;%i;%i;%i;%s;%i;%i;%s;%i;%s;%i\n", + Q[c].Dir, Q[c].Name, Q[c].FileSize, Q[c].FileNumbers, Q[c].VCodec, + Q[c].BitrateVideo, Q[c].MinQuant, Q[c].MaxQuant, Q[c].CropWidth, + Q[c].CropHeight, Q[c].CropPosX, Q[c].CropPosY, Q[c].ScaleWidth, + Q[c].ScaleHeight, Q[c].ACodec, Q[c].BitrateAudio, Q[c].AudioID, + Q[c].PPValues, Q[c].Rename, Q[c].Container, Q[c].Preview); + n++; + } + } + NumMovies = n; + + fclose(q); + + // delete queuefile if it's empty + if (NumMovies < 1) { + remove(AddDirectory(cPlugin::ConfigDirectory(), QUEUEFILE)); + } + } else {dsyslog("[vdrrip] could not save %s", QUEUEFILE);} + + Load(); + + return true; +} + +bool cQueue::New(struct QueueData *q) { + if (Locked) {return false;} + + Q = (struct QueueData*)realloc(Q, ((NumMovies + 1) * sizeof(struct QueueData))); + Q[NumMovies] = *q; + + NumMovies++; + Save(); + + return true; +} + + +bool cQueue::Del(int i) { + if (i >= 0 && i < NumMovies) { + + // don't delete the first entry of the queuefile, + // if there is an aktiv encoding + if (i == 0 && IsEncoding()) {return false;} + + isyslog("added delete flag on movie %s", Q[i].Name); + Q[i].Name = strdup("delete"); + Save(); + + return true; + } + return false; +} + +bool cQueue::Up(int i) { + if (i >= 1 && i < NumMovies) { + + // don't move to the first entry of the queuefile, + // if there is an aktiv encoding + if (i == 1 && IsEncoding()) return false; + + struct QueueData q; + q = Q[i]; + Q[i] = Q[i-1]; + Q[i-1] = q; + Save(); + + return true; + } + return false; +} + +bool cQueue::Down(int i) { + if (i >= 0 && i < NumMovies - 1) { + + // don't move the first entry of the queuefile, + // if there is an aktiv encoding + if (i == 0 && IsEncoding()) return false; + + struct QueueData q; + q = Q[i]; + Q[i] = Q[i+1]; + Q[i+1] = q; + Save(); + + return true; + } + return false; +} + +bool cQueue::Switch(int i) { + if (i >= 0 && i < NumMovies) { + + // don't switch the first entry of the queuefile, + // if there is an aktiv encoding + if (i == 0 && IsEncoding()) return false; + + (Q[i].Preview == 0) ? Q[i].Preview = 1 : Q[i].Preview = 0; + Save(); + + return true; + } + return false; +} + + +int cQueue::getNumMovies() {return NumMovies;} + +struct QueueData* cQueue::getData(int i) { + if (i >= 0 && i < NumMovies) {return &Q[i]; + } else {return NULL;} +} + +char *cQueue::getName(int i) { + if (i >= 0 && i < NumMovies) {return Q[i].Name; + } else {return NULL;} +} + +char *cQueue::getShortname(int i) { + if (i >= 0 && i < NumMovies) { + if (strlen(Q[i].Name) > 20) { + char *s, *s1; + s = strsub(Q[i].Name,1 , 17); + asprintf(&s1, "%s...", s); + return s1; + } else {return Q[i].Name;} + } else {return NULL;} +} + +bool cQueue::getLockStat() {return Locked;} + +void cQueue::Lock() { + FILE *l; + l = fopen(AddDirectory(cPlugin::ConfigDirectory(), LOCKFILE), "w"); + if (l) { + fprintf(l,"0"); + isyslog("[vdrrip] queuefile locked"); + fclose(l); + } else { + dsyslog("[vdrrip] could not lock queuefile"); + } +} + +void cQueue::Unlock() { + int r = remove(AddDirectory(cPlugin::ConfigDirectory(), LOCKFILE)); + if (r == -1) {dsyslog("[vdrrip] could not unlock queuefile"); + } else {isyslog("[vdrrip] queuefile unlocked");} +} + +void cQueue::WaitUnlock() { + FILE *l; + int i = 0; + bool loop = true; + + while (loop) { + l = fopen(AddDirectory(cPlugin::ConfigDirectory(), LOCKFILE), "r"); + if (l) { + + // check if the content of the lockfile 0 + int c = fgetc(l); + if (c == 48) { + loop = false; + Locked = false; + break; + } + + i++; + if (i > 2) { + loop = false; + Locked = true; + } + isyslog("[vdrrip] %d. try: queuefile is locked by another process", i); + sleep(1); + fclose(l); + } else { + isyslog("[vdrrip] queuefile is not locked by another process"); + loop = false; + Locked = false; + } + } +} + +bool cQueue::IsEncoding() { + FILE *e; + e = fopen(AddDirectory(cPlugin::ConfigDirectory(), ENCODEFILE), "r"); + if (! e) return false; + + fclose(e); + return true; +} + +char *cQueue::getQueueStat() { + char *buf = NULL; + size_t i = 0; + + FILE *e = fopen(AddDirectory(cPlugin::ConfigDirectory(), ENCODEFILE), "r"); + if (e) { + if (getline(&buf, &i, e) != -1) { + fclose(e); + return buf; + } + } + return NULL; +} diff --git a/queue.h b/queue.h new file mode 100755 index 0000000..e819901 --- /dev/null +++ b/queue.h @@ -0,0 +1,69 @@ +// +// queue.h +// + +#ifndef __QUEUE_H +#define __QUEUE_H + +#define QUEUEFILE "queue.vdrrip" +#define LOCKFILE "lock.vdrrip" +#define ENCODEFILE "encode.vdrrip" + +struct QueueData { + const char *Dir; + char *Name; + int FileSize; + int FileNumbers; + char *VCodec; + int BitrateVideo; + int MinQuant; + int MaxQuant; + int CropWidth; + int CropHeight; + int CropPosX; + int CropPosY; + int ScaleWidth; + int ScaleHeight; + char *ACodec; + int BitrateAudio; + int AudioID; + const char *PPValues; + int Rename; + char *Container; + int Preview; +}; + +class cQueue { + private: + struct QueueData *Q; + int NumMovies; + bool Locked; + + void Load(); + void Set(char *s, int c); + + public: + cQueue(); + ~cQueue(); + + bool Save(); + bool New(struct QueueData *q); + bool Del(int i); + bool Up(int i); + bool Down(int i); + bool Switch(int i); + + void Lock(); + void Unlock(); + void WaitUnlock(); + bool IsEncoding(); + char *getQueueStat(); + + struct QueueData* getData(int i); + char *getName(int i); + char *getShortname(int i); + int getNumMovies(); + bool getLockStat(); +}; + +#endif // __QUEUE_H diff --git a/scripts/queuehandler.sh b/scripts/queuehandler.sh new file mode 100755 index 0000000..759b603 --- /dev/null +++ b/scripts/queuehandler.sh @@ -0,0 +1,1012 @@ +#!/bin/sh + +################################################################# +# # +# this scrip handles the queue which is generated from # +# the vdr-plugin vdrrip. # +# # +# usage: queuehandler.sh queuefile tempdir # +# # +# # +# written by herbert attenberger # +# # +# 15.07.2003: 0.1.0: - initial version # +# 24.07.2003: 0.1.1: - added split function # +# 28.07.2003: 0.1.2: - added video codec "divx4/divx5" # +# 22.08.2003: 0.1.3: - added vdrecho function # +# 07.09.2003: 0.1.4: - added (preview) to the output filename # +# in preview mode # +# 15.09.2003: 0.1.5: - added -frames 0 to get -identify with # +# mplayer1.0pre1 working (thx to ronny) # +# 24.09.2003: 0.1.6: - added (optional) svdrpsend.pl in # +# function vdrecho # +# - added dvd-handling # +# 28.09.2003: 0.1.7: - added handling of postprocess-filters # +# - added rename function (thx to # +# memed@www.vdrportal.de) # +# - copy 001.vdr if a symbolic link couldn't # +# be created (for fat-partitions) # +# (thx to memed@www.vdrportal.de) # +# 30.09.2003: 0.1.8: - added -sws 2 to xvid/divx5 encoding # +# 25.11.2003: 0.1.9: - moved loop function to read_queue # +# - added lock, unlock and wait_unlock # +# functions # +# - moved encode.vdrrip to the plugins-dir # +# 14.12.2003: 0.2.0: - added check_exe function # +# 16.12.2003: 0.2.1: - removed mod_quant from xvid encoding # +# 20.12.2003: 0.2.2: - moved pp-filters to the last position # +# - added log_error function to output the # +# errors to the syslog & stderr # +# - added log_info function to output the # +# infos to the syslog & stdout # +# - added log_debug function to output the # +# debug-infos infos to the syslog & stdout # +# 10.01.2004: 0.2.4: - renamed check function to initialize # +# - added ogg-vorbis, ac3-handling # +# - added ogm, matroska-handling # +# - moved configuration to the file # +# queuehandler.sh.conf # +# - improved error-handling # +# - added execute function # +# - added trap-command & _trap-function # +# 18.01.2004: 0.2.5: - fixed a bug if scaling is off (thx to # +# cosmo@www.vdrportal.de) # +# 21.01.2004: 0.2.6: - added option $useropts # +# - completed dvd-handling # +# 13.02.2004: 0.2.7: - changed some if statements to elif # +# - changed preview-handling # +# 10.03.2004: 0.2.8: - added calc_steps function # +# - write status to encode.vdrrip # +# 22.03.2004: 0.2.9: - moved 2-pass logiles to $tempdir # +# 26.03.2004: 0.3.0: - added evecho function # +# - added $mencoder_ac3 & $mplayer_ac3 # +# - added $qh_ver & qh_conf_ver # +# # +################################################################# +qh_ver="0.3.0" + + +function initialize () { +# +# make initial checks +# + + scriptname=`basename $0` + scriptdir=`dirname $0` + + cfgfile="$scriptname.conf" + + if [ -e "$scriptdir/$cfgfile" ] + then + source "$scriptdir/$cfgfile" + else + log_error "file $cfgfile not found in $scriptdir, aborting !" 1 + fi + + if [ "$1" -a "$2" ] + then + queuefile="$1" + tempdir="$2" + pluginsdir=`dirname "$queuefile"` + else + log_error "usage: $scriptname queuefile tempdir" 1 + fi + + if [ ! -d "$tempdir" ] + then + log_error "directory $tempdir doesn't exist, aborting !" 1 + fi + + if [ "$3" ]; then log_info "the option -preview isn't supported anymore"; fi + + local pids=`pgrep -d" " "$scriptname"` + local pid1=`echo "$pids" | cut -d" " -f1` + local pid2=`echo "$pids" | cut -d" " -f3` + + if [ "$pid1" != "$pid2" ] + then + log_error "$scriptname is already running with pid $pid1, aborting !" 2 + fi + + # now the 2-pass logfiles are created in $tempdir ;-) + # (because -passlogfile doesn't do its job for xvid) + cd "$tempdir" +} + + +function read_queue () { +# +# wait for the queuefile and read the first line +# + + while [ ! -e "$queuefile" ] + do + echo + echo "waiting for queuefile $queuefile" + echo "sleeping $check seconds" + echo + sleep $check + done + + wait_unlock # wait until the queuefile is unlocked + lock # locks the queuefile + + local saveifs="$IFS" + IFS=";" + read dir name filesize filenumbers vcodec br_video min_q max_q \ + crop_w crop_h crop_x crop_y scale_w scale_h acodec br_audio audio_id \ + ppvalues rename container preview < "$queuefile" + IFS=$saveifs + + # there is an active encoding + echo "- reading queuefile..." > "$pluginsdir/encode.vdrrip" + + unlock # unlocks the queuefile + + # correct some data + name=`echo "$name" | sed "s/ /_/g"` + + local name_length=`echo "$name" | wc -c` + if [ $name_length -gt 20 ] + then + shortname=`echo "$name" | cut -c 1-17` + shortname="$shortname..." + else + shortname="$name" + fi + + if [ "$ppvalues" = "(null)" ]; then ppvalues=""; fi + + dvd=`echo "$dir" | grep "^dvd://"` + + if [ "$preview" = "1" ] + then + local mode="preview" + else + local mode="full" + fi + + log_info "### start encoding movie $shortname in $mode mode ###" + + log_debug "version of queuehandler.sh: $qh_ver" + log_debug "version of queuehandler.sh.conf: $qh_conf_ver" + log_debug "dir: $dir" + log_debug "name: $name" + log_debug "filesize: $filesize" + log_debug "filenumbers: $filenumbers" + log_debug "vcodec: $vcodec" + log_debug "br_video: $br_video" + log_debug "min_q: $min_q" + log_debug "max_q: $max_q" + log_debug "crop_w: $crop_w" + log_debug "crop_h: $crop_h" + log_debug "crop_x: $crop_x" + log_debug "crop_y: $crop_y" + log_debug "scale_w: $scale_w" + log_debug "scale_h: $scale_h" + log_debug "acodec: $acodec" + log_debug "br_audio: $br_audio" + log_debug "audio_id: $audio_id" + log_debug "ppvalues: $ppvalues" + log_debug "rename: $rename" + log_debug "container: $container" + log_debug "preview: $preview" +} + + +function pre_check () { +# +# make some checks before the encoding +# + if [ "$error" ]; then return; fi + + # check mplayer/mencoder + if [ ! "$dvd" -a $audio_id -ge 128 ] + then + # encoding a vdr-recording with selected ac3-stream + check_exe "$mencoder_ac3" "mencoder_ac3=" + check_exe "$mplayer_ac3" "mplayer_ac3=" + mc=$mencoder_ac3 + mp=$mplayer_ac3 + else + check_exe "$mencoder" "mencoder=" + check_exe "$mplayer" "mplayer=" + mc=$mencoder + mp=$mplayer + fi + + # check needed tools + case "$container" in + "avi") + if [ "$acodec" = "ogg-vorbis" ] + then + log_error "ogg-vorbis isn't a valid audio-codec for a avi-container" + fi + ;; + "ogm") + check_exe "$vdrsync" "vdrsync=" + check_exe "$ogmmerge" "ogmmerge=" + + if [ "$acodec" = "lame" -o "$acodec" = "ogg-vorbis" ] + then + check_exe "$ffmpeg" "ffmpeg=" + fi + + if [ $filenumbers -gt 1 ] + then + check_exe "$ogmsplit" "ogmsplit=" + fi + ;; + "matroska") + check_exe "$vdrsync" "vdrsync=" + check_exe "$mkvmerge" "mkvmerge=" + + if [ "$acodec" = "lame" -o "$acodec" = "ogg-vorbis" ] + then + check_exe "$ffmpeg" "ffmpeg=" + fi + ;; + *) + log_error "unknown container $container" + ;; + esac +} + + +function calc_steps () { +# +# calculate the nuber of encoding-steps +# + if [ "$error" ]; then return + elif [ "$preview" = "1" ] + then + if [ "$container" = "avi" ]; then steps="2" + else steps="3"; fi + return + fi + + steps="8" + + if [ "$filenumbers" = "1" ]; then let steps=steps-1; fi + + case "$container" in + "avi") + let steps=steps-4 + if [ "$dvd" ]; then let steps=steps-1 + elif [ ! -e "$dir/002.vdr" ]; then let steps=steps-1; fi + ;; + "ogm") + let steps=steps-2 + if [ "$acodec" = "copy" -a $audio_id -ge 128 ] + then + let steps=steps-1 + fi + ;; + "matroska") + let steps=steps-2 + if [ "$acodec" = "copy" ]; then let steps=steps-1; fi + ;; + *) + ;; + esac +} + + +function preview () { +# +# get the preview values +# + if [ "$error" ]; then return + elif [ "$preview" != "1" ]; then return; fi + + name="$name(preview)" + shortname="$shortname(preview)" + + # start the preview in the middle of the movie + local length=`"$mp" -identify -frames 0 "$dir/001.vdr" 2>/dev/null | grep ID_LENGTH | cut -d"=" -f2` + let local ss=length/2 + previewval="-ss $ss -endpos $previewlength" +} + + +function prepare() { +# +# prepares the encoding-process +# + if [ "$error" ]; then return + elif [ "$dvd" ]; then return; fi + + case "$container" in + "avi") + # join all vdr-files to $tempdir/temp.vdr + if [ -e "$dir/002.vdr" -a "$preview" != "1" ] + then + log_info "joining all vdr-files from directory $dir" + evecho "joining vdr-files" + nice -+19 cat $dir/[0-9][0-9][0-9].vdr > "$tempdir/temp.vdr" + else + create_symbolic_link + fi + ;; + "ogm"|"matroska") + # demux vdr-recording with vdrsync + if [ "$preview" = "1" ] + then + create_symbolic_link + else + log_info "demuxing all vdr-files from directory $dir" + evecho "demuxing vdr-files" + execute "$vdrsync $dir -o $tempdir" + fi + ;; + *) + ;; + esac +} + + +function create_symbolic_link() { +# +# creates a symbolic link of 001.vdr to $temdir/temp.vdr +# + if [ "$error" ]; then return + elif [ "$dvd" ]; then return; fi + + log_info "create a symbolic link from $dir/001.vdr to $tempdir/temp.vdr" + ln -s "$dir/001.vdr" "$tempdir/temp.vdr" + + if [ ! -e "$tempdir/temp.vdr" ] + then + log_info "could not create a symolic link" + log_info "try to copy $dir/001.vdr to $tempdir/temp.vdr" + execute "cp $dir/001.vdr $tempdir/temp.vdr" + fi +} + + +function check_exe () { +# +# checks if $1 is a executable and exit the queuehandler with +# rc $3, if this one is set +# + if [ ! -x "$1" ] + then + if [ "$1" ]; then log_error "$1 isn't a executable"; fi + log_error "set a valid path for $2 in the file $scriptdir/$cfgfile." $3 + fi +} + + +function encode () { +# +# encodes the movie +# + if [ "$error" ]; then return; fi + + # set mencoder audio values + case "$container" in + "avi") + case "$acodec" in + "lame") + if [ "$dvd" ] + then + local aopts="-oac mp3lame -lameopts br=$br_audio:abr:q=2:vol=8 -aid $audio_id" + else + local aopts="-oac mp3lame -lameopts br=$br_audio:abr:q=2 -aid $audio_id" + fi + ;; + "copy") + local aopts="-oac copy -aid $audio_id" + ;; + "ogg-vorbis") + # this shouldn't happen + ;; + *) + log_error "unknown audio codec $acodec" + ;; + esac + ;; + "ogm") + local aopts="-nosound" + case "$acodec" in + "lame"|"ogg-vorbis") + if [ "$dvd" ]; then dump_audio_mplayer; fi + encode_ffmpeg + ;; + "copy") + # convert mp2-files to ac3, because + # mp2 isn't supported by ogm + if [ "$dvd" ]; then dump_audio_mplayer; fi + if [ $audio_id -lt 128 ]; then encode_ffmpeg; fi + ;; + *) + log_error "unknown audio codec $acodec" + ;; + esac + ;; + "matroska") + local aopts="-nosound" + case "$acodec" in + "lame"|"ogg-vorbis") + if [ "$dvd" ]; then dump_audio_mplayer; fi + encode_ffmpeg + ;; + "copy") + if [ "$dvd" ]; then dump_audio_mplayer; fi + ;; + *) + log_error "unknown audio codec $acodec" + ;; + esac + ;; + *) + ;; + esac + + # set mencoder -vop values + if [ "$crop_w" = "-1" -a "$crop_h" = "-1" -a "$crop_x" = "-1" -a \ + "$crop_y" = "-1" -a "$scale_w" = "-1" -a "$scale_h" = "-1" ] + then + local vopopts="" + elif [ "$crop_w" = "-1" -a "$crop_h" = "-1" -a "$crop_x" = "-1" -a \ + "$crop_y" = "-1" ] + then + local vopopts="scale=$scale_w:$scale_h" + else + local vopopts="scale=$scale_w:$scale_h,crop=$crop_w:$crop_h:$crop_x:$crop_y" + fi + + if [ "$ppvalues" ] + then + local vopopts="-vop pp=$ppvalues,$vopopts" + elif [ "$vopopts" ] + then + local vopopts="-vop $vopopts" + fi + + # encode in two passes + for pass in 1 2 + do + if [ "$pass" = "1" ] + then + local ofile="-o /dev/null" + else + local ofile="-o $tempdir/$name.avi" + fi + + # set mencoder video values + case "$vcodec" in + "lavc") + local vopts="-ovc lavc -lavcopts vcodec=mpeg4:vhq:vbitrate=$br_video:vqmin=$min_q:vqmax=$max_q:vpass=$pass -sws 2" + ;; + "xvid") + local vopts="-ovc xvid -xvidencopts bitrate=$br_video:me_quality=6:pass=$pass -sws 2" + ;; + "divx4") + local vopts="-ovc divx4 -divx4opts br=$br_video:q=5:min_quant=$min_q:max_quant=$max_q:pass=$pass -sws 2" + ;; + *) + log_error "unknown video codec $vcodec" + ;; + esac + + # set mencoder input file + if [ "$dvd" ] + then + # dvd + local ifile="$dvd" + elif [ "$container" = "avi" -o "$preview" = "1" ] + then + # no dvd, avi container + local ifile="$tempdir/temp.vdr" + else + # no dvd, ogm/matroska container + + # search for the first video-file + local num=0 + while [ ! "$ifile" ] + do + if [ ! -e "$tempdir/e$num.mpv" ] + then + log_info "video-file $tempdir/e$num.mpv not found !" + if [ $num -ge 9 ] + then + log_error "no video-stream found !" + # exit loop + local ifile="dummy" + fi + let num=num+1 + else + log_info "video-file $tempdir/e$num.mpv found !" + local ifile="$tempdir/e$num.mpv" + fi + done + fi + + if [ "$error" ]; then return; fi + + log_info "encoding movie $shortname (pass: $pass)" + + if [ "$aopts" = "-nosound" ] + then + local encstr="video" + else + local encstr="video & audio" + fi + + evecho "enc. $encstr ($pass. pass)" + if [ "$useropts" ]; then log_info "\$useropts are set to \"$useropts\""; fi + execute "$mc $ifile $ofile $useropts $vopts $vopopts $aopts $previewval" + done +} + + +function dump_audio_mplayer() { +# +# dump audio-stream from dvd with mplayer +# + if [ "$error" ]; then return + elif [ "$preview" = "1" ]; then return + elif [ ! "$dvd" ]; then return; fi + + log_info "dumping audio-stream $audio_id from $dvd with $mp" + evecho "dumping audio-stream from dvd" + execute "$mp $dir -vo null -vc dummy -aid $audio_id -aop list=volnorm -dumpaudio -dumpfile $tempdir/bd.ac3" +} + + +function encode_ffmpeg() { +# +# encode the audio-stream with ffmpeg +# + if [ "$error" ]; then return + elif [ "$preview" = "1" ]; then return; fi + + if [ $audio_id -ge 128 ] + then + local ifile="$tempdir/bd.ac3" + else + local ifile="$tempdir/c$audio_id.mpa" + local forceinput="-f mp3" + fi + + case "$acodec" in + "lame") + local filetype="mp3" + ;; + "ogg-vorbis") + local filetype="ogg" + ;; + "copy") + log_info "ogm doesn't support mp2 audio-streams, i will convert it into ac3" + local filetype="ac3" + ;; + *) + ;; + esac + + log_info "converting $ifile into $filetype-format" + evecho "conv. audio into $filetype-format" + execute "$ffmpeg -hq -y $forceinput -i $ifile -ab $br_audio $tempdir/c$audio_id.$filetype" +} + + +function merge_ogm_mkv() { + if [ "$error" ]; then return; fi + if [ "$container" != "ogm" -a "$container" != "matroska" ]; then return; fi + + + case "$container" in + "ogm") + local merge="$ogmmerge" + local filetype="ogm" + ;; + "matroska") + local merge="$mkvmerge" + local filetype="mkv" + ;; + *) + ;; + esac + + + if [ "$preview" != "1" ] + then + case "$acodec" in + "lame") + local afile="$tempdir/c$audio_id.mp3" + ;; + "ogg-vorbis") + local afile="$tempdir/c$audio_id.ogg" + ;; + "copy") + if [ $audio_id -ge 128 ] + then + # ac3 + local afile="$tempdir/bd.ac3" + elif [ "$container" = "ogm" ] + then + # mpg2, container ogm + local afile="$tempdir/c$audio_id.ac3" + else + # mpg2 + local afile="$tempdir/c$audio_id.mpa" + fi + ;; + *) + ;; + esac + fi + + log_info "merging movie $shortname into $filetype-container" + evecho "merging into $filetype-container" + execute "$merge -o $tempdir/$name.$filetype $tempdir/$name.avi $afile" +} + + +function cleanup () { +# +# delete temp-files and reset variables +# + # temp. codec-files + rm -f "$tempdir/divx2pass.log" + rm -f "$tempdir/lavc_stats.txt" + rm -f "$tempdir/analyse.log" + rm -f "$tempdir/c:\\trace_b.txt" + rm -f "$tempdir/xvid-twopass.stats" + + # temp. movie-files + rm -f "$tempdir/temp.vdr" + if [ "$container" = "ogm" -o "$container" = "matroska" ] + then + rm -f "$tempdir/$name.avi" + fi + rm -f "$tempdir"/e[0-9].mpv + rm -f "$tempdir"/c[0-9].mpa + rm -f "$tempdir"/c[0-9].mp3 + rm -f "$tempdir"/c[0-9].ogg + rm -f "$tempdir"/c[0-9].ac3 + rm -f "$tempdir"/c[0-9][0-9][0-9].mp3 + rm -f "$tempdir"/c[0-9][0-9][0-9].ogg + rm -f "$tempdir"/bd.ac3 + + # temp. queuehandler-files + rm -f "$pluginsdir/encode.vdrrip" + rm -f /tmp/queuehandler.err + + + # reset variables + dir="" + name="" + filesize="" + filenumbers="" + vcodec="" + br_video="" + min_q="" + max_q="" + crop_w="" + crop_h="" + crop_x="" + crop_y="" + scale_w="" + scale_h="" + acodec="" + br_audio="" + audio_id="" + ppvalues="" + rename="" + container="" + preview="" + + mc="" + mp="" + + dvd="" + previewval="" + error="" + + step="0" +} + + +function del_queue () { +# +# delete first line from the queuefile +# + # waits until the queuefile is unlocked, and locks it + wait_unlock + lock + + if [ "$error" ] + then + head -n 1 "$queuefile" >> "$queuefile.rejected" + fi + + # delete first entry of the queuefile + local lines=`cat "$queuefile" | wc -l` + if [ "$lines" -le 1 ] + then + rm -f "$queuefile" + else + let lines=lines-1 + tail -n $lines "$queuefile" > /tmp/queuefile.tmp + mv /tmp/queuefile.tmp $queuefile + fi + + if [ "$error" ] + then + log_info "### moved movie $shortname to $queuefile.rejected ###" + else + log_info "### movie $shortname deleted from queuefile ###" + fi + + # unlock queuefile + unlock +} + + +function split () { +# +# splits the encoded movie into $filenumbers pieces +# + if [ "$error" ]; then return + elif [ "$filenumbers" = "1" -o "$preview" = "1" ]; then return; fi + + log_info "splitting $shortname in $filenumbers pieces" + evecho "splitting movie" + + case "$container" in + "avi") + local overlap=3 + + local count=1 + local splitpos=0 + + # workaround to get the correct filesize from mencoder with -endpos + let local splitsize=filesize*99/100 + + log_info "splitting $shortname in $filenumbers pieces" + while [ $count -le $filenumbers ] + do + local ofile="$name-$count.avi" + + if [ $count -eq $filenumbers ] + then + local endpos="" + else + local endpos="-endpos ${splitsize}mb" + fi + + # split file + execute "$mc -ovc copy -oac copy $tempdir/$name.avi \ + -ss $splitpos $endpos -o $tempdir/$ofile" + + # detect length of splitted file and add it to $splitpos + local length=`$mplayer -identify -frames 0 $tempdir/$ofile 2>/dev/null | \ + grep ID_LENGTH | cut -d= -f2` + let splitpos=splitpos+length-overlap + let count=count+1 + done + ;; + "ogm") + execute "$ogmsplit -s $filesize -n $filenumbers $tempdir/$name.ogm" + ;; + "matroska") + execute "$mkvmerge --split d${filesize}m --split-max-files \ + $filenumbers -o $tempdir/$ofile" + ;; + *) + ;; + esac +} + + +function vdrecho () { +# +# echo $1 in the vdr-infobar (or console) +# + + if [ -x "$svdrpsend" ] + then + $svdrpsend -d "$vdrhostname" "MESG $1" 2>/dev/null 1>/dev/null + elif [ -x "$netcat" ] + then + echo "MESG $1" | $netcat -q 1 "$vdrhostname" 2001 2>/dev/null 1>/dev/null + else + log_info "$1" + fi +} + + +function evecho () { +# +# echo $ in the file encode.vdrrip +# + let step=step+1 + local time=`date +"%k:%M h"` + echo "- step $step/$steps (since $time): $1" > "$pluginsdir/encode.vdrrip" +} + + +function log_error () { +# +# echo $1 on stderr and write it to the syslog with priority user.error +# + logger -s -p user.error -t [vdrrip-qh] "$1" + + if [ "$2" ] + then + vdrecho "$1" + exit "$2" + else + error="1" + fi +} + + +function log_info () { +# +# echo $1 on stdout and write it to the syslog with priority user.info +# + logger -s -p user.info -t [vdrrip-qh] "$1" 2>&1 +} + + +function log_debug () { +# +# echo $1 on stdout and write it to the syslog with priority user.debug +# + if [ "$debug" = "1" ] + then + logger -s -p user.debug -t [vdrrip-qh] "$1" + fi +} + + +function execute () { +# +# executes $1 and checks the rc +# + local cmd="$1" + + log_debug "execute command: $cmd" + + # execute $cmd and save the return-code: + if [ "$stdout" = "1" ] + then + nice -+19 $cmd 2>/tmp/queuehandler.err + local rc="$?" + else + nice -+19 $cmd 1>/dev/null 2>/tmp/queuehandler.err + local rc="$?" + fi + + if [ "$rc" != "0" ] + then + # display stderr if the return-code isn't 0 + log_error "an error occured (rc $rc) while processing the command: $cmd" + local err_message=`cat /tmp/queuehandler.err 2>/dev/null` + if [ "$err_message" ] + then + log_error "error message: $err_message" + fi + elif [ "$debug" = "1" ] + then + # display stderr if the debug-mode is set + local dbg_message=`cat /tmp/queuehandler.err 2>/dev/null` + if [ "$dbg_message" ] + then + log_debug "debug message: $dbg_message" + fi + fi +} + + +function rename () { +# +# renames the vdr recording to indicate it was encoded properly +# this function is written by memed@www.vdrportal.de +# + if [ "$error" ]; then return + elif [ "$dvd" ]; then return + elif [ "$preview" = "1" ]; then return + elif [ "$rename" = "0" ]; then return; fi + + new_dir="$(dirname $dir)${append_string}" + mkdir -p "$new_dir" + #move finished subdir to new folder + mv "$dir" "$new_dir" + #if all subdirs are empty, also delete dir one up + [ $(find $(dirname $dir) -type f|wc -l) -eq 0 ] && rm -rf $(dirname $dir) +} + + +function wait_unlock () { +# +# wait until the queuefile is unlocked +# + local lockstat + read lockstat 2>/dev/null < "$pluginsdir/lock.vdrrip" + + while [ -e "$pluginsdir/lock.vdrrip" -a "$lockstat" != "1" ] + do + echo + echo "queuefile is locked !" + echo "sleeping $lock seconds" + echo + sleep $lock + done +} + + +function lock () { +# +# locks the queuefile +# + local lockfile="$pluginsdir/lock.vdrrip" + echo 1 > $lockfile 2>/dev/null + + if [ ! -e "$lockfile" ] + then + log_error "couldn't write to $lockfile" 1 + fi +} + + +function unlock () { +# +# unlocks the queuefile +# + rm -f $pluginsdir/lock.vdrrip + + if [ -e "$lockfile" ] + then + log_error "couldn't delete $lockfile" 1 + fi +} + +function _trap () { +# +# this function is called if the queuehandler is killed +# + if [ "$debug" = "0" ]; then cleanup; fi + log_error "queuehandler was killed !" + vdrecho "queuehandler was killed !" +} + + +# +# this is the main part +# + +trap "_trap; exit" 2 9 15 + +initialize "$1" "$2" "$3" # make initial checks +cleanup # delete old temp-files + +while [ true ] +do + read_queue # read the first line from the queuefile + pre_check # make some checks before the encoding + calc_steps # calc the number of encoding-steps + preview # get the preview values + prepare # prepares the encoding + encode # encodes the movie + merge_ogm_mkv # merge ogm/mkv-files + split # splits the encoded movie + + if [ ! "$error" ] + then + mess="movie $shortname encoded" + log_info "$mess" + vdrecho "$mess" + else + mess="couldn't encode movie $shortname" + log_info "$mess" + vdrecho "$mess" + fi + + rename # rename dir-name of the vdr-recording + del_queue # delete first line from the queuefile + cleanup # delete temp-files +done diff --git a/scripts/queuehandler.sh.conf b/scripts/queuehandler.sh.conf new file mode 100755 index 0000000..37fed90 --- /dev/null +++ b/scripts/queuehandler.sh.conf @@ -0,0 +1,71 @@ +# +# queuehandler.sh.conf: +# this is the configuration-file of queuehandler.sh +# + qh_conf_ver="0.3.0" + + # set this to 1 to get debug-informations in the syslog and + # exit-messages of the queuehandler on the osd: + debug=0 + + # set this to 1 to see stdout while the queuehandler is executing an + # external programm + stdout=0 + + # resulting length (in seconds) of movies if the queuehandler is + # started in the preview-mode + previewlength=60 + + # check for new entries in queuefile (in seconds) + check=30 + + # check for unlocked queuefile (in seconds) + lock=5 + + + + # if you have set the setup-option "rename after encodings" + # to yes, this string will be appended to the recording-name + # after the movie is encoded. + append_string="_-_CONVERTED" + + + + # this pathes are used for the encoding of vdr-recordings & dvd's. + # they _must_ point to an unpatched mencoder & mplayer + mencoder="/usr/local/bin/mencoder" + mplayer="/usr/local/bin/mplayer" + + # this pathes are only used for encoding a vdr-recording with + # selected ac3-stream. + # they _must_ point to an ac3-patched mencoder & mplayer. + # if you don't use this feature you should deactivate the option + # AC3 Support (MPlayer-patch inst.) in the plugins setup-menu. + mencoder_ac3="/usr/local/bin/mencoder_ac3" + mplayer_ac3="/usr/local/bin/mplayer_ac3" + + + # this string is added to the mencoder-command in the + # queuehandler + useropts="" + + + + # optional tools (only needed for ogm/matroska-container) + vdrsync="/usr/local/bin/vdrsync.pl" + ffmpeg="/usr/local/bin/ffmpeg" + ogmmerge="/usr/local/bin/ogmmerge" + ogmsplit="/usr/local/bin/ogmsplit" + mkvmerge="/usr/local/bin/mkvmerge" + + + + # This (optional) command is used to display some informations + # from the queuehandler on your tv. + # You have to specify only $svdrpsend or $netcat. + # $netcat is only recommend, if perl isn't installed. + svdrpsend="/usr/local/bin/svdrpsend.pl" + #netcat="" + + vdrhostname="localhost" + diff --git a/scripts/sleephalt.sh b/scripts/sleephalt.sh new file mode 100755 index 0000000..d154160 --- /dev/null +++ b/scripts/sleephalt.sh @@ -0,0 +1,83 @@ +#!/bin/sh + +# +# This script is called by vdrshutdown.sh +# +# It is written by herbert attenberger +# + +# +# initial definitions +# + +pgrep="/usr/bin/pgrep" +nvramwakeup="/usr/local/bin/nvram-wakeup" +netcat="/usr/bin/netcat" + +pluginsdir="/etc/vdrtmpfs/plugins" + +scriptname=`basename $0` + +# reboot needed for nvram-wakeup (yes/no) ? +nvramreboot="yes" + + +vdrecho () { +# +# echo $1 in the vdr-infobar (or console) +# + +if [ -x "$netcat" ] +then + echo "MESG $1" | $netcat -q 1 localhost 2001 +else + echo $1 +fi + +} + +# +# this is the main part +# + +if [ -e "$pluginsdir/encode.vdrrip" ] +then + # check, if there is already an active shutdown-procedure: + pids=`"$pgrep" -d" " "$scriptname"` + pid1=`echo "$pids" | cut -d" " -f1` + pid2=`echo "$pids" | cut -d" " -f3` + + if [ "$pid1" != "$pid2" ] + then + vdrecho "a shutdown-procedure is already active" + exit + else + vdrecho "shutdown after vdrrip-encoding is finished" + while [ -e "$pluginsdir/encode.vdrrip" ] + do + sleep 60 + done + + # shutdown vdr with the power-key and exit the script + echo "HITK Power" | $netcat -q 1 localhost 2001 + exit + fi +else + if [ -x "$nvramwakeup" -a "$1" != "0" ] + then + if [ "$nvramreboot" = "yes" ] + then + # shutdown with reboot + $nvramwakeup -ls $1 + lilo -R PowerOff + reboot + else + # shutdown without reboot + $nvramwakeup -ls $1 + halt + fi + else + # shutdown without nvram-wakeup + halt + fi +fi diff --git a/scripts/vdrshutdown.sh b/scripts/vdrshutdown.sh new file mode 100755 index 0000000..e63e2ac --- /dev/null +++ b/scripts/vdrshutdown.sh @@ -0,0 +1,24 @@ +#!/bin/sh + +# +# This script executes the script $sleephalt in the backgroud +# an gives the control back to vdr +# +# $sleephalt handles the shutdown with or without nvram-wakeup +# and waits until /encode.vdrrip is deleted +# +# It is written by herbert attenberger +# + +sleephalt="/usr/local/bin/sleephalt.sh" + +if [ -x $sleephalt ] +then + $sleephalt $1 & +else + echo + echo "script $sleephalt not found" + echo +fi + +exit diff --git a/scripts/vdrsync.pl b/scripts/vdrsync.pl new file mode 100755 index 0000000..bae489e --- /dev/null +++ b/scripts/vdrsync.pl @@ -0,0 +1,2806 @@ +#!/usr/bin/perl + +# +# vdrsync (c) 2003 by Dr. Peter Sebbel, a perl script to demux VDR recordings and +# correcting for missing/additional Audio frames to ensure sync of audio and +# video streams. Contact: peter@vdr-portal.de +# +# 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; version 2 of the License +# +# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + + +use strict; +use warnings; + + + +my $debug = 0; +my $path_param = "./"; +my @parameter_list = @ARGV; +my $tcmplex = ""; +my $tcmplex_panteltje= ""; +my $panteltje = 0; #uses tcmplex-panteltje instead of tcmplex +my $mplex = 0; +my $mpeg2 = 0; +my $transcode = 0; +my $info = 0; +my $remuxfilename = ""; +my @ignore_streams; +my $dump_packets = 0; +my $dump_payload = 0; +my $stop_flag = 0; +my $audio_only = 0; +my $script_output = 0; +my $divx = 0; +my $divxac3 = 0; +my $divx_param = " -V -y divx4 -w 1000 "; #enter additional parameters for divx here +my $divx_ac3_param = " -V -y divx4 -b 256 -w 1000 "; #enter additional parameters for divx with ac3 sound here +my $master_dvd = 0; +my $master_dvd_param = "-c 0,10:00,20:00,30:00,40:00,50:00,01:00:00,01:10:00,01:20:00,01:30:00,01:40:00,01:50:00,02:00:00,02:10:00,02:20:00,02:30:00,02:40:00,02:50:00,03:00:00"; +my $show_drift = 0; +my $postexec = 0; +my $dump_buffer = 0; +my $mkiso = 0; + + +$|=1; # prevent line buffering +if (-e "/usr/bin/tcmplex") { + $tcmplex = "/usr/bin/tcmplex"; +} +elsif (-e "/usr/local/bin/tcmplex") { + $tcmplex = "/usr/local/bin/tcmplex"; +} +if (-e "/vdr/bin/tcmplex-panteltje"){ + $tcmplex_panteltje = "/vdr/bin/tcmplex-panteltje"; +} +elsif (-e "/usr/local/bin/tcmplex-panteltje") { + $tcmplex_panteltje = "/usr/local/bin/tcmplex-panteltje"; +} +elsif (-e "/usr/bin/tcmplex-panteltje") { + $tcmplex_panteltje = "/usr/bin/tcmplex-panteltje"; +} + +if (-e "/usr/bin/transcode") { + $transcode = "/usr/bin/transcode"; +} +elsif (-e "/usr/local/bin/transcode") { + $transcode = "/usr/local/bin/transcode"; +} + + +sub parse_parameters { + my $parameter = shift; + if ($parameter eq "-d") { + $debug = 1; + } + elsif ($parameter eq "-o") { + $path_param = shift @parameter_list; + if (!($path_param =~ /\/$/)) { + $path_param .= "/"; + } + } + elsif ($parameter eq "-m") { + $mplex = 1; + if ($parameter_list[0] && ($parameter_list[0] eq "panteltje")){ + $panteltje = (shift @parameter_list); + } + } + elsif ($parameter eq "-mpeg2") { + $mpeg2 = 1; + $master_dvd = 0; + $mplex = 1; + $mkiso = 0; + } + elsif ($parameter eq "-i") { + $info = 1; + } + elsif ($parameter eq "-ignore") { + @ignore_streams = split /,/,(shift @parameter_list); + } + elsif ($parameter eq "-dump-packets"){ + $dump_packets = (shift @parameter_list); + } + elsif ($parameter eq "-show-time-drift"){ + $show_drift = 1; + } + elsif ($parameter eq "-dump-buffer-on-error"){ + $dump_buffer = 1; + } + elsif ($parameter eq "-dump-payload"){ + $dump_payload = (shift @parameter_list); + } + elsif ($parameter eq "-audio-only") { + $audio_only = 1; + } + elsif ($parameter eq "-script-output") { + $script_output = 1; + } + elsif ($parameter eq "-divx") { + $divx = 1; + if ($parameter_list[0] eq "ac3"){ + $divxac3 = (shift @parameter_list); + } + } + elsif ($parameter eq "-master-dvd"){ + $master_dvd = 1; + $mplex = 1; + if ( ! $parameter_list[0] =~/^-/ ){ + $master_dvd_param = (shift @parameter_list); + } + } + elsif ($parameter eq "-mkiso"){ + $mkiso = 1; + $mplex = 1; + $master_dvd = 1; + } + elsif ($parameter eq "-postexec"){ + $postexec = (shift @parameter_list); + } + else { + print "Unknown parameter $parameter\n"; + exit; + } +} + +my @files; +if (scalar(@ARGV) == 0) + { + print "\nVDRsync Version 0.1.2.2\n"; + print "\nUsage: vdrsync.pl /path/to/vdr/recording/\n"; + print "or : vdrsync.pl vdr-file1 .... vdr-fileN\n\n"; + print "Optional -d print strange debug messages\n"; + print "Optional -o output_dir sets the directory where the result files\n"; + print " are written to\n"; + print "Optional -m tries to multiplex the demuxed streams into a dvd compatible\n"; + print " stream (uses tcmplex)\n"; + print "Optional -m panteltje uses tcmplex-panteltje instead of tcmplex\n"; + print "Optional -i just tries to analyse the .vdr file and exits. -m is ignored\n"; + print "Optional -ingore stream1[,stream2]... tells the script to ignore stream1..n \n"; + print " Useful for deliberately omitting streams (like AC3) or if you have \n"; + print " trouble with one stream\n"; + print "Optional -divx tries to transcode the demuxed streams into a divx movie\n"; + print " For DIVX with AC3 (if present in the recording) type -divx ac3\n"; + print " (uses transcode, adjust transcode settings at the beginning of\n"; + print " vdrsync.pl)\n"; + print "Optional -dump-packets NNNN tells the script to dump the first NNNN\n"; + print " full PES-packets (in file STREAM_ID.pes_dump) Useful for debugging \n"; + print " (if you want to extract a stream that does not work and mail it to me)\n"; + print "Optional -dump-payload NNNN tells the script to dump the raw payload of the \n"; + print " first NNNN PES-packets (in file STREAM_ID.dump) Useful for debugging\n"; + print " (if you want to extract a stream-payload that does not work and \n"; + print " test it with other tools)\n"; + print "Optional -postexec executes the command specified after finishing\n"; + #print " some Dummy variables like VSlength are replaced by actual values \n"; + print "Optional -master-dvd creates a DVD structure in a subdirectory of outputpath\n"; + print "Optional -audio-only discards all video streams and just writes audio\n"; + print "Optional -script-output writes a special output format about the movie at\n"; + print " the end of the vdrsync run. Useful for scripting, contains corrected \n"; + print " info about the streams\n"; + print "Optional -show-time-drift prints information about the difference between\n"; + print " timestamps and actual video / audio data found in the recording\n"; + print "Optional -dump-debug-buffer in case of a problem with one of the streams 3 MB\n"; + print " of the recording are dumped to the file debug.buffer\n"; + print "Optional -mkiso creates an ISO Image suitable for burning a DVD\n"; + print "Optional -mpeg2 creates a generic MPEG2 File (tcmplex -m 2)\n"; + exit; + } + + +while (my $param = shift(@parameter_list)) { # check whether all files hat we got as parameters exist + print "Got parameter $param\n"; + if ($param =~ /^-/) {parse_parameters($param); next} + if (-d $param) { + print "got a directory on the command line\n"; + my @dir_files = @{ get_file_list($param) }; + foreach (@dir_files) { + push @files, "$param/$_"; + } + next; + } + push @files, $param; +} + +print "Output files will be stored in $path_param\n" if $debug; + +if (!(-e $path_param)) {print "Output Directory $path_param does not exist\n"; exit 1} + + + +print "Found " . (scalar(@ignore_streams)) ." to ignore\n" if $debug; + +my %ignore_hash; +foreach (@ignore_streams) { + $ignore_hash{$_} = 1; +} + + +my $PES_Obj = MPEGSTREAM->new + ( + streamcode =>"PES_stream", + debug =>$debug, + outputpath =>$path_param, + files =>\@files, + ignore_streams => \%ignore_hash, + dump_packets => $dump_packets, + dump_payload => $dump_payload, + audio_only => $audio_only, + show_drift => $show_drift, + script_output => $script_output, + dump_buffer => $dump_buffer, + ); + +if (!($PES_Obj)) { + print "could not create the PES Stream Obj\n"; + exit 1; +} +if ($info) { + $PES_Obj->print_stats(); + exit; +} + + +$PES_Obj->process_PES_packets(); + + +if ((!$mplex) && (!$divx ) && (! $postexec) && (! $master_dvd)) {exit} + +my @filestomux = @{$PES_Obj->output_files()}; + +print "Got " . scalar(@filestomux) ." files back\n" if $debug; + +my @basename_list; +foreach (@filestomux) { + my $base_name = (split /\//,$_)[-1]; + $base_name =~ s/\.//; + push @basename_list, $base_name; +} + +if ($divx == 1) { + if (scalar(@filestomux) < 2) {print "Need at least two streams for multiplexing\n";exit} + if (($filestomux[1] =~ /.ac3$/) && ($divxac3)) { + #transcode -i e0.mpv -A -N 0x2000 -p bd.ac3 -y divx4 -o test.avi + my $command = "nice -19 $transcode -i $filestomux[0] -A -N 0x2000 -p $filestomux[1] "; + $remuxfilename .= $path_param . "/$basename_list[0]_$basename_list[1]_"; + $command .= " $divx_ac3_param -o $remuxfilename" . "divx.avi"; + print "executeing $command\n"; + system $command; + exit; + } + else { + #transcode -i e0.mpv -p c0.mpa -y divx4 -o test.avi + my $command = "nice -19 $transcode -i $filestomux[0] -p $filestomux[1] "; + $remuxfilename .= $path_param . "/$basename_list[0]_$basename_list[1]_"; + $command .= " $divx_param -o $remuxfilename" . "divx.avi"; + print "executeing $command\n"; + system $command; + exit; + } +} + +my $mplex_command; + +if ($mplex == 1) { + if (scalar(@filestomux) < 2) {print "Need at least two streams for multiplexing\n";exit} + if (! $panteltje){ + $mplex_command = "nice -19 $tcmplex -i $filestomux[0] -p $filestomux[1]"; + } + else { + $mplex_command = "nice -19 $tcmplex_panteltje -i $filestomux[0] -0 $filestomux[1]"; + } + $remuxfilename .= $path_param . "/$basename_list[0]_$basename_list[1]_"; + + if (scalar(@filestomux) > 2) { + if (! $panteltje){ + $mplex_command .= " -s $filestomux[2]"; + $remuxfilename .= "$basename_list[2]_"; + } + else { + print "Mplexing using tcmplex-panteltje\n"; + my $counter = 2; + while (scalar(@filestomux) > $counter) { + print "adding to $counter $filestomux[$counter]\n"; + $mplex_command .= " -" . ($counter-1) . " $filestomux[$counter]"; + $remuxfilename .= "$basename_list[$counter]_"; + $counter++; + } + } + } + $remuxfilename .= "remux.mpg"; + + if ($mpeg2) { + $mplex_command .= " -m 2 "; + } + else { + $mplex_command .= " -m d "; + } + + print "$remuxfilename as outputname\n"; + + if (! $master_dvd) { + $mplex_command .= "-o $remuxfilename"; + print "executeing $mplex_command\n"; + system $mplex_command; + } +} + +if ($master_dvd) { + print "we got a the master_dvd parameter\n"; + create_dvd_image(); +} + + +if ($postexec) { + print "Executing: $postexec\n"; + if ($postexec =~ /VSlength/) { + my $length = $PES_Obj->get_movie_length(); + $postexec =~ s/VSlength/$length/g; + } + exec $postexec; +} + +if ($mplex) { + clean_up(); +} + + +exit; + +sub create_dvd_image { + my $pid; + my $result = system "mkfifo $path_param"."remuxfifo"; + print "the attempt to create FIFO returnd $result\n"; + my $asp_ratio = $PES_Obj->get_aspect_ratio(); + + if ($pid = fork) { + my $command = "dvdauthor -v $asp_ratio $master_dvd_param -o $path_param" . "/DVD $path_param" . "remuxfifo"; + print "starting $command\n"; + system $command; + $command = "dvdauthor -T -o $path_param" . "/DVD"; + print "starting $command\n"; + system $command; + sleep 1; + } + else { + print "mplexing file to fifo\n"; + print "should execute $mplex_command and put it to the fifo\n"; + $mplex_command .= " -o $path_param" . "remuxfifo"; + print "now the command is $mplex_command\n"; + system $mplex_command; + print "finished mplexing\n"; + exit; + } + if (! $mkiso) { + print "***********************************************************\n"; + print "vdrsync finished\n"; + print "***********************************************************\n"; + print "now you can burn the Image to a DVD-R(W) using the command:\n\n"; + print "mkisofs -dvd-video $path_param" . '/DVD | dvdrecord tsize=$(echo "`mkisofs -dvd-video --print-size ' .$path_param . '/DVD 2>/dev/null`*2048" | bc -l ) dev=1,0,0 -v -dao -' . "\n"; + print "NOTE: Of course you have to adjust the dev=N,N,N parameter to match your device settings\n\n"; + print "NOTE: It might be a good idea to check it first with:\n\n"; + print "mplayer -dvd 1 -dvd-device $path_param" . "/DVD\n\n"; + } + else { + my $command = "mkisofs -dvd-video $path_param" . '/DVD > ' . " $path_param/DVDimage.iso\n"; + system $command; + print "***********************************************************\n"; + print "vdrsync finished\n"; + print "***********************************************************\n"; + print "now you can burn the Image to a DVD-R(W) using the command:\n\n"; + print "dvdrecord dev=1,0,0 -v -dao $path_param" . "DVDimage.iso\n"; + print "NOTE: Of course you have to adjust the dev=N,N,N parameter to match your device settings\n\n"; + } +} + +sub clean_up { + print "Deleting temp files\n"; + for my $delfile (@filestomux) { + if (-e $delfile){ + print "$delfile\n"; + unlink($delfile); + } + } + if (-e "$path_param"."remuxfifo") { + unlink ("$path_param"."remuxfifo"); + } + if ($mkiso) { + my $command = "rm -r $path_param" . "DVD"; + print "Deleting DVD Files with: $command\n"; + system $command; + + } +} + +sub get_file_list { + my $indir = shift; + my $DIR; + opendir $DIR, $indir || die "Can not open $indir $!\n"; + print "trying to open $indir\n"; + if (! $DIR){ + die "did not get a handle back\n"; + } + my @allfiles = grep { ! /^\./ } readdir $DIR; + my @vdrfiles = sort (grep { /\d{3}.vdr$/ } @allfiles); + return \@vdrfiles; + #$summary_file = $indir . "summary.vdr" if (-e "$indir/summary.vdr"); + #$marks_file = $indir . "marks.vdr" if (-e "$indir/marks.vdr"); + #$index_file = $indir . "index.vdr" if (-e "$indir/index.vdr"); +} + + + +BEGIN +{ +package MPEGSTREAM; +require Exporter; +# A place available to all instances for storing the cuts in the file... +our @cutlist; +our $kill_me = ""; +our $total_size = 0; +our $bytes_read = 0; +our %bitrates = ( + MPEG1_Layer_1 => { + "0001" => "32", + "0010" => "64", + "0011" => "96", + "0100" => "128", + "0101" => "160", + "0110" => "192", + "0111" => "224", + "1000" => "256", + "1001" => "288", + "1010" => "320", + "1011" => "352", + "1100" => "384", + "1101" => "416", + "1110" => "448", + }, + MPEG1_Layer_2 => { + "0001" => "32", + "0010" => "48", + "0011" => "56", + "0100" => "64", + "0101" => "80", + "0110" => "96", + "0111" => "112", + "1000" => "128", + "1001" => "160", + "1010" => "192", + "1011" => "224", + "1100" => "256", + "1101" => "320", + "1110" => "384", + }, + MPEG1_Layer_3 => { + "0001" => "32", + "0010" => "40", + "0011" => "48", + "0100" => "56", + "0101" => "64", + "0110" => "80", + "0111" => "96", + "1000" => "112", + "1001" => "128", + "1010" => "160", + "1011" => "192", + "1100" => "224", + "1101" => "256", + "1110" => "320", + }, + MPEG2_Layer_1 => { + "0001" => "32", + "0010" => "64", + "0011" => "96", + "0100" => "128", + "0101" => "160", + "0110" => "192", + "0111" => "224", + "1000" => "256", + "1001" => "288", + "1010" => "320", + "1011" => "352", + "1100" => "384", + "1101" => "416", + "1110" => "448", + }, + MPEG2_Layer_2 => { + "0001" => "32", + "0010" => "48", + "0011" => "56", + "0100" => "64", + "0101" => "80", + "0110" => "96", + "0111" => "112", + "1000" => "128", + "1001" => "160", + "1010" => "192", + "1011" => "224", + "1100" => "256", + "1101" => "320", + "1110" => "384", + }, + MPEG2_Layer_3 => { + "0001" => "8", + "0010" => "16", + "0011" => "24", + "0100" => "32", + "0101" => "64", + "0110" => "80", + "0111" => "56", + "1000" => "64", + "1001" => "128", + "1010" => "160", + "1011" => "112", + "1100" => "128", + "1101" => "256", + "1110" => "320", + }, +); +our %freqs =( + MPEG1 => { + "00" => "44100", + "01" => "48000", + "10" => "32000", + }, + MPEG2 => { + "00" => "22050", + "01" => "24000", + "10" => "16000", + }, +); + +our %AC3_frame_info = ( + "32000" => { + "000000" => "192", + "000001" => "192", + "000010" => "240", + "000011" => "240", + "000100" => "288", + "000101" => "288", + "000110" => "336", + "000111" => "336", + "001000" => "384", + "001001" => "384", + "001010" => "480", + "001011" => "480", + "001100" => "576", + "001101" => "576", + "001110" => "672", + "001111" => "672", + "010000" => "768", + "010001" => "768", + "010010" => "960", + "010011" => "960", + "010100" => "1152", + "010101" => "1152", + "010110" => "1344", + "010111" => "1344", + "011000" => "1536", + "011001" => "1536", + "011010" => "1920", + "011011" => "1920", + "011100" => "2304", + "011101" => "2304", + "011110" => "2688", + "011111" => "2688", + "100000" => "3072", + "100001" => "3072", + "100010" => "3456", + "100011" => "3456", + "100100" => "3840", + "100101" => "3840", + }, + "44100" => { + "000000" => "138", + "000001" => "140", + "000010" => "194", + "000011" => "196", + "000100" => "208", + "000101" => "210", + "000110" => "242", + "000111" => "244", + "001000" => "278", + "001001" => "280", + "001010" => "348", + "001011" => "350", + "001100" => "416", + "001101" => "418", + "001110" => "486", + "001111" => "488", + "010000" => "556", + "010001" => "558", + "010010" => "696", + "010011" => "698", + "010100" => "834", + "010101" => "836", + "010110" => "974", + "010111" => "976", + "011000" => "1114", + "011001" => "1116", + "011010" => "1392", + "011011" => "1394", + "011100" => "1670", + "011101" => "1672", + "011110" => "1950", + "011111" => "1952", + "100000" => "2228", + "100001" => "2230", + "100010" => "2506", + "100011" => "2508", + "100100" => "2786", + "100101" => "2788", + }, + "48000" => { + "000000" => "128", + "000001" => "120", + "000010" => "160", + "000011" => "160", + "000100" => "192", + "000101" => "192", + "000110" => "224", + "000111" => "224", + "001000" => "256", + "001001" => "256", + "001010" => "320", + "001011" => "320", + "001100" => "384", + "001101" => "384", + "001110" => "448", + "001111" => "448", + "010000" => "512", + "010001" => "512", + "010010" => "640", + "010011" => "640", + "010100" => "768", + "010101" => "768", + "010110" => "896", + "010111" => "896", + "011000" => "1024", + "011001" => "1024", + "011010" => "1280", + "011011" => "1280", + "011100" => "1336", + "011101" => "1336", + "011110" => "1792", + "011111" => "1792", + "100000" => "2048", + "100001" => "2048", + "100010" => "2304", + "100011" => "2304", + "100100" => "2560", + "100101" => "2560", + }, + "bitrate" => { + "000000" => "32000", + "000001" => "32000", + "000010" => "40000", + "000011" => "40000", + "000100" => "48000", + "000101" => "48000", + "000110" => "56000", + "000111" => "56000", + "001000" => "64000", + "001001" => "64000", + "001010" => "80000", + "001011" => "80000", + "001100" => "96000", + "001101" => "96000", + "001110" => "112000", + "001111" => "112000", + "010000" => "128000", + "010001" => "128000", + "010010" => "160000", + "010011" => "160000", + "010100" => "192000", + "010101" => "192000", + "010110" => "224000", + "010111" => "224000", + "011000" => "256000", + "011001" => "256000", + "011010" => "320000", + "011011" => "320000", + "011100" => "384000", + "011101" => "384000", + "011110" => "448000", + "011111" => "448000", + "100000" => "512000", + "100001" => "512000", + "100010" => "576000", + "100011" => "576000", + "100100" => "640000", + "100101" => "640000", + }, +); + +our $mp2_regex = + #(pack ("B16", "1111111111110000")) . "|" . + #(pack ("B16", "1111111111110001")) . "|" . + #(pack ("B16", "1111111111110010")) . "|" . + #(pack ("B16", "1111111111110011")) . "|" . These are Mpeg 2 Audio Headers, + #(pack ("B16", "1111111111110100")) . "|" . They should not occur + #(pack ("B16", "1111111111110101")) . "|" . + #(pack ("B16", "1111111111110110")) . "|" . + #(pack ("B16", "1111111111110111")) . "|" . + + #(pack ("B16", "1111111111111000")) . "|" . + #(pack ("B16", "1111111111111001")) . "|" . + #(pack ("B16", "1111111111111010")) . "|" . These are Layer I and III headers + #(pack ("B16", "1111111111111011")) . "|" . + #(pack ("B16", "1111111111111110")) . "|" . + #(pack ("B16", "1111111111111111")) . "|" . + + (pack ("B16", "1111111111111100")) . "|" . + (pack ("B16", "1111111111111101")) + ; + +$mp2_regex = qr/$mp2_regex/; + +our $AC3_regex = + (pack("B16", "0000101101110111")); # AC3 Frames always have this 16 Bit Syncword at the beginning + +$AC3_regex = qr/$AC3_regex/; + + +sub get_cutlist { return @cutlist; } + +our @ISA = qw(Exporter); +our @EXPORT = qw( add_PES_packet + bitrate + streamtype + streamcode + print_stats + output_files + process_PES_packets + ); + +our $VERSION = 0.1.2.2; # Version number + + + + +sub new { + my $invocant = shift; + my $class = ref($invocant) || $invocant; + + my $self = { + streamcode => "PES_stream", #will be overridden + streamtype => undef, # + bitrate => undef, # only for Audio + freq => undef, # only for Audio + copyright => undef, # only for Audio + frame_bytes => undef, # only for Audio + frame_ticks => undef, # one tick is 1 / 90000 of a second + frameno => 0, # READ frames counter, not written frames + GOPno => 0, # just for video stats + packet_start => undef, # a signature that is found a the beginning of a frame, should read frame_start + packet_rest => "", # only audio, if a frame does not finish in a PES-Packet, the first part of the frame is stored here + check_frames => undef, # a placeholder for the sub that actually manages the frames of a given type (mp2 / ac3 / mpv) + analyse_frames => undef, # a placeholder for the sub that actually analyses the frames of a given type (mp2 / ac3 ) + masterstream => "", # here the stream_id of the videostream is stored (it is not always e0 as I thought first) + outputpath => "./", # where to store the result files + outfilename => "", # The name of the output file + cutcounter => 0, # used as an index to the cuts list (see below), for sync we need the current and the previous cut + last_ts => 0, # last timestamp that was read + final_desync => [], + GOP_buffer => "", # A GOP is buffered here before analysed and written + GOP_ts => 0, # Here we store the first GOP ts, for the strrange recordings that have no ts at GOP start + frame_buffer => [], # an array that holds the buffered audio frames + frame_times => [], # an array that holds the timestamp for each audio frame + frame_lengths => [], # an array that holds the length for each audio frame + frames_written => 0, # used only for stats + timestamps => [], # all timesstamps are stored here, probably a waste of memory, but maybe used for stricter checks + written_chunks => [], # an array of scalars for debugging, from where to where were Audio STreams written? + final_desync => 0, # how many ticks desync after correction? Used in the next sync attempt + show_drift => 0, # if set to 1, info about timedrift will be printed + time_drift => 0, # The current mismatch between TS and actual Pakets found + audio_only => 0, # if this is set, all video Streams are discarded and audio ist just written out + script_output => 0, + Unit_analysis_counter => 0, + Unit_analysis_hash => {}, # Here the different settings for apsepect_ratio etc are collected for later print + first_flush => 1, # This flag indicates that it is the first time that the audio buffer is flushrd, an a start sync is needed + @_, # Override previous attributes + }; + if ($self->{streamcode} eq "PES_stream") { + + if (scalar(@{$self->{files}}) == 0) { + print "No input files specified, exiting\n"; exit 1 ; + } + + foreach (@{$self->{files}}) {# check whether all files that we got as parameters exist + if (!(-e $_)) {print "Input file $_ not found\n"; exit 1 } + } + $self->{packets_before_sync} = 0; + print "dump only the first $self->{dump_packets} packets\n" if $self->{dump_packets}; + foreach (keys (%{$self->{ignore_streams}})) {print "Ignoring stream $_\n";} + print "Lots of debug stuff will be printed\n" if $self->{debug}; + print "Printing Information about the time drift\n" if $self->{show_drift}; + init_PES_stream($self); + foreach(@{$self->{files}}) { + $total_size += -s $_; + } + print "Total Input Size is $total_size\n";# if $self->{debug}; + } + return bless $self, $class; +} + + +sub init_stream { + my $self = shift; + my $payload = shift; + my %pes_header = %{shift @_}; + my $decimal_code = hex($pes_header{stream_id}); + print "we got the decimal code $decimal_code for $pes_header{stream_id}\n"if $self->{debug}; + if (($decimal_code > 191) && ($decimal_code < 224)) { + print "we got the stream $decimal_code ($pes_header{stream_id}), checking Audio\n"if $self->{debug}; + analyse_audio_stream($self, $payload, \%pes_header); + #$self->{streamtype} = "audio"; + } + elsif (($decimal_code > 223) && ($decimal_code < 240)) { + print "we got the stream $decimal_code ($pes_header{stream_id}), checking Video\n"if $self->{debug}; + analyse_video_stream($self, $payload, \%pes_header); + $self->{streamtype} = "video"; + } + elsif ($decimal_code == 189) { + analyse_ac3_stream($self, $payload, \%pes_header); + $self->{streamtype} = "AC3_audio"; + } + else { + print "unknown stream type found, skipping contents of stream $pes_header{stream_id}\n"; + } +} + + +sub output_files { + my $self = shift; + + if ($self->{streamcode} ne "PES_stream") {return} + + my @audiofiles; + my $moviefile; + my @filelist; + + foreach(keys(%{$self->{streams}})) { + if ($self->{ignore_streams}{$_}){ + next; + } + if ( $_ =~ /(c|b)/ ) { + push @audiofiles, $self->{streams}{$_}{outfilename}; + } + else { + $moviefile = $self->{streams}{$_}{outfilename}; + } + } + @audiofiles = sort(@audiofiles); + @filelist = ($moviefile, @audiofiles); + return \@filelist; +} + +sub analyse_ac3_stream { + my $self = shift; + my $payload = shift; + my %pes_header = %{shift @_}; + $self->{frame_regex} = $AC3_regex; + ($payload, my %frame_info) = analyse_ac3_frame($self, $payload); + if ($payload eq "-1") { + print "AC3 Packet could not be analysed, skipping\n"; + return; + } + + $self->{packet_start} = substr($payload,0,2); + + + $self->{freq} = $frame_info{freq}; + + $self->{frame_bytes} = $frame_info{frame_bytes}; + $self->{bitrate} = $frame_info{bitrate}; + $self->{mode} = $frame_info{mode}; + + ################################################################## + ############ ONE FRAME is 32 ms + ################################################################## + + $self->{frame_ticks} = $frame_info{frame_ticks}; + + + $self->{outfilename} = "$self->{outputpath}/$self->{streamcode}.ac3"; + open $self->{outfile}, ">$self->{outputpath}/$self->{streamcode}.ac3" || die "Can not open file for ac3: $!\n" if (!($self->{outfile})); + binmode $self->{outfile}; + $self->{check_frames} = \&check_audio_frames; + $self->{analyse_frames} = \&analyse_ac3_frame; + $self->{frame_regex} = $AC3_regex; +} + +sub analyse_ac3_frame { + my $self = shift; + my $payload = shift; + my $bits = unpack("B64", substr($payload, 0, 8)); + my %frame_info; + if (!(substr($payload, 0, 2) =~ /^$AC3_regex$/)) { + #if (substr($bits,0,16) ne "0000101101110111") { + print "No Audio syncword found for stream $self->{streamcode}, searching for audio sync\n" if $self->{debug}; + print substr($bits,0,16) . " found, 0000101101110111 expected\n" if $self->{debug}; + if ($payload =~ /$AC3_regex/g) { + print "\n AC3 regex matched at " . pos($payload) . "\n" if $self->{debug}; + $payload = substr($payload, (pos($payload)-2)); + } + else { + print "No audio frame found in this paket\n"; + return -1; + } + $bits = unpack("B64", substr($payload, 0, 8)); + } + + my $fscod = substr($bits,32,2); + + if ($fscod == "00") {$frame_info{freq} = 48000} + elsif ($fscod == "01") {$frame_info{freq} = 44100} + elsif ($fscod == "11") {$frame_info{freq} = 32000} + elsif ($fscod == "10") {print "Illeagal AC3 freq\n"; return -1} + + my $frmsizecod = substr($bits,34,6); + $frame_info{frame_bytes} = $AC3_frame_info{$frame_info{freq}}{$frmsizecod}; + $frame_info{bitrate} = $AC3_frame_info{bitrate}{$frmsizecod}; + + my $acmod = substr($bits, 48, 3); + if ($acmod == "000") {$frame_info{mode} = "1+1"} + elsif ($acmod == "001") {$frame_info{mode} = "1/0"} + elsif ($acmod == "010") {$frame_info{mode} = "2/0"} + elsif ($acmod == "011") {$frame_info{mode} = "3/0"} + elsif ($acmod == "100") {$frame_info{mode} = "2/1"} + elsif ($acmod == "101") {$frame_info{mode} = "3/1"} + elsif ($acmod == "110") {$frame_info{mode} = "2/2"} + elsif ($acmod == "111") {$frame_info{mode} = "3/2"} + + ################################################################## + ############ ONE FRAME is 32 ms + ################################################################## + if ($fscod ne "00") {print "not an 48 KHz stream, exiting\n"; return -1} + $frame_info{frame_ticks} = 8 * $frame_info{frame_bytes} * 90000 / $frame_info{bitrate}; + $frame_info{streamtype} = "AC3_Audio"; + if ($self->{Unit_analysis_counter}++ > 20) { + $self->{Unit_analysis_hash}{mode}{$frame_info{mode}}++; + } + + + return $payload, %frame_info; +} + +sub check_audio_frames { + my $self = shift; + my $payload = shift; + my %pes_header = %{shift @_}; + my $counter = 0; + my $FH = $self->{outfile}; + + + if ($self->{change_message}) {return} + + if ($self->{dump_payload}) { + print "Dumping payload of stream $self->{streamcode}, ($self->{dump_payload} to dump left)\n" if $self->{debug}; + my $DUMPFH; + if (! $self->{"$self->{streamcode}.dump"}) { + print "Trying to open dumpfile\n"; + open $DUMPFH, ">$self->{streamcode}.dump" || die "Can not open dumpfile: $!\n"; + binmode $DUMPFH; + $self->{"$self->{streamcode}.dump"} = $DUMPFH; + print "opened dumpfile\n"; + } + $DUMPFH = $self->{"$self->{streamcode}.dump"}; + print $DUMPFH $payload; + if ($self->{dump_payload}-- == 1) { + exit; + } + return; + } + + $payload = $self->{packet_rest} . $payload; + + ($payload, my %frame_info) = &{$self->{analyse_frames}}($self, $payload) ; + if (!$frame_info{frame_ticks}) { + print "Analysis failed in audio stream $self->{streamcode}, returning from check_audio_frames\n" if $self->{debug}; + return; + } + if ((!($self->{frame_bytes} eq $frame_info{frame_bytes}) || (!($self->{bitrate} eq $frame_info{bitrate}))) && ($self->{streamcode} ne "bd")) { + if ($self->{audio_only}){ + $self->{copyright} = $frame_info{copyright}; + $self->{bitrate} = $frame_info{bitrate}; + $self->{freq} = $frame_info{freq}; + $self->{padding_bit} = $frame_info{padding_bit}; + $self->{streamtype} = $frame_info{streamtype}; + $self->{frame_bytes} = $frame_info{frame_bytes}; + $self->{frame_ticks} = $frame_info{frame_ticks}; + $self->{mode} = $frame_info{mode}; + print "\nAudio Format Changed:\n"; + print "\naudio stream $self->{streamcode} info ($self->{streamtype}):\n"; + print "Sample frequency: $self->{freq}\n"; # only for Audio + print "Bitrate: $self->{bitrate}\n"; # only for Audio + print "Mode: $self->{mode}\n"; # only for Audio + print "Copyright: $self->{copyright}\n" if $self->{copyright}; # only for Audio + print "Frame length (bytes) $self->{frame_bytes}\n"; # only for Audio + print "Frame length (ticks) $self->{frame_ticks} (90000 / sec)\n\n\n"; # one tick is 1 / 90000 of a second + } + + + elsif (!$self->{frames_written}) { + @{$self->{frame_times}} = (); + $self->{frame_times}[0] = 0; + @{$self->{frame_buffer}} = (); + @{$self->{frame_length}} = (); + $self->{frameno} = 0; + $self->{copyright} = $frame_info{copyright}; + $self->{bitrate} = $frame_info{bitrate}; + $self->{freq} = $frame_info{freq}; + $self->{padding_bit} = $frame_info{padding_bit}; + $self->{streamtype} = $frame_info{streamtype}; + $self->{frame_bytes} = $frame_info{frame_bytes}; + $self->{frame_ticks} = $frame_info{frame_ticks}; + $self->{mode} = $frame_info{mode}; + + $self->{single_frame} = get_silent_frame($self, \%frame_info); + if ($self->{single_frame} eq "-1") { + $self->{single_frame} = substr($payload, 0, $frame_info{frame_bytes}); + print "Defined first audio frame as single frame for later sync in stream $self->{streamcode}\n" if $self->{debug}; + print "Frame has a length of " . length($self->{single_frame}) . "\n" if $self->{debug}; + } + + print "\nAUDIO FORMAT CHANGED, new parameters:\n"; + print "\naudio stream $self->{streamcode} info ($self->{streamtype}):\n"; + print "Sample frequency: $self->{freq}\n"; # only for Audio + print "Bitrate: $self->{bitrate}\n"; # only for Audio + print "Mode: $self->{mode}\n"; # only for Audio + print "Copyright: $self->{copyright}\n" if $self->{copyright}; # only for Audio + print "Frame length (bytes) $self->{frame_bytes}\n"; # only for Audio + print "Frame length (ticks) $self->{frame_ticks} (90000 / sec)\n\n\n"; + } + elsif ($total_size - $bytes_read < 10000000) { + if (! $self->{change_message}) { + print "\nAudio Format changed " . int(($total_size - $bytes_read)/1000000) ." MBytes before end, continuing...\n"; + $self->{change_message} = 1; + } + return; + } + else { + print "Change in Audio Mode from $self->{mode} to $frame_info{mode}, this will not work during remux, stopping!\n"; + print "Change in Audio bitrate from $self->{bitrate} to $frame_info{bitrate}, this will not work during remux, stopping!\n"; + print "You should cut the movie at this position and process the pieces independently\n"; + my $frame_number = $self->{frames_written} + scalar(@{$self->{frame_buffer}}); + my $seconds = $frame_number * $self->{frame_ticks} / 90000; #/ + print "Current position: $seconds sec.\n"; + $kill_me = $self->{streamcode}; + } + } + + if ((! $self->{frame_times}[0]) && ( $pes_header{PTS_decimal}) && ($self->{frameno} > 0)){ + print "\nFirst audio timestamp at $self->{frameno} for stream $self->{streamcode}\n" if $self->{debug}; + print "First Audio Timestamp is $pes_header{PTS_decimal}\n" if $self->{debug}; + for (my $i = 1; $i <= $self->{frameno}; $i++) { + $self->{frame_times}[$self->{frameno} - $i] = $pes_header{PTS_decimal} - ($i * $self->{frame_ticks}); + } + print "\nAudio now starting at $self->{frame_times}[0]\n" if $self->{debug}; + } + + my $first_timestamp = 0; + + if ($pes_header{PTS_decimal}) { + $first_timestamp = $pes_header{PTS_decimal}; + } + else { + $first_timestamp = ${$self->{frame_times}}[-1] + $frame_info{frame_ticks} if (${$self->{frame_times}}[-1]); + } + + if (!($self->{single_frame})) { + + $self->{single_frame} = get_silent_frame($self, \%frame_info); + if ($self->{single_frame} eq "-1") { + $self->{single_frame} = substr($payload, 0, $frame_info{frame_bytes}); + print "\nDefined first audio frame as single frame for later sync in stream $self->{streamcode}\n" if $self->{debug}; + print "Frame has a length of " . length($self->{single_frame}) . "\n" if $self->{debug}; + } + } + + my $packet_offset = 0; + while ($packet_offset + $frame_info{frame_bytes} +2 <= (length($payload))) { + + if ((substr($payload, $packet_offset, 2) =~ /^$self->{frame_regex}$/)) { + push @{$self->{frame_buffer}}, substr($payload, $packet_offset, $frame_info{frame_bytes}); + + push @{$self->{frame_times}}, $first_timestamp + $counter * $frame_info{frame_ticks}; + push @{$self->{frame_length}}, $frame_info{frame_ticks}; + $packet_offset += $frame_info{frame_bytes}; + $counter++; + $self->{frameno}++; + } + else { + print "\n$self->{frame_regex} did not match against " . (substr($payload,$packet_offset,2)) . "\n" if $self->{debug}; + if ($counter == 0) { + $payload = search_audio_sync($self, substr($payload, $packet_offset)); + $self->{packet_rest} = ""; + $packet_offset = 0; + } + else { + print "\nAudio Format change within packet, skipping \n" if $self->{debug}; + $self->{packet_rest} =""; + return; + } + } + } + + if ($packet_offset < (length($payload))) { + $self->{packet_rest} = substr($payload, $packet_offset); + } + else { + $self->{packet_rest} = ""; + } + if (scalar(@{$self->{frame_times}}) < 100) { + return; + } + new_flush($self); + return; +} + +sub new_flush { + my $self = shift; + my $FH = $self->{outfile}; + + if ($self->{audio_only}) { + while (scalar(@{$self->{frame_times}}) > 50) { + $self->{last_frame_time} = shift @{$self->{frame_times}}; + shift @{$self->{frame_length}}; + print $FH shift @{$self->{frame_buffer}}; + $self->{frames_written} ++; + } + return; + } + + my ($chunk_start, $chunk_end, $video_frameno) = split /::/,$cutlist[$self->{cutcounter}]; + if ($self->{first_flush}){ + sync_audio_start($self, $chunk_start); + $self->{first_flush} = 0; + } + + # + # Here the new time drift correction is performed. Note that this is performed wthe the beginning of a Aufio flush NOT when it occurs + # This should be ok, since a) The shift builds up very slowly and b) a buffer flush is only 60 Frames (max 1,8 sec) long + # + if (abs($self->{time_drift}) > ($self->{frame_ticks} * 1.5)) { + if (abs($self->{time_drift}) > 900000) { + print "Timedrift bigger than 10 Seconds, killing stream $self->{streamcode}\n"; + $kill_me = $self->{streamcode}; + return; + } + print "Timedrift is too big, trying to correct $self->{time_drift} ticks\n"; + #sleep 1; + if ($self->{time_drift} < 0) { + while ($self->{time_drift} < ($self->{frame_ticks} * -1.5)) { + shift @{$self->{frame_buffer}}; + shift @{$self->{frame_times}}; + $self->{time_drift} += shift @{$self->{frame_length}}; + print "Dropped frame to correct time drift EXPERIMENTAL!!!\n"; + if (@{$self->{frame_buffer}} == 0) { + print "could not drop enough Audio frames in stream $self->{streamcode}to compensate for timedrift, still $self->{time_drift} left\nContinuing anyway...\n"; + # sleep 1; + if ($self->{dump_buffer}) {$kill_me = "all"} + return; + } + } + } + else { + my $ins_frames = insert_frames($self, $self->{time_drift}); + my $ins_time = $ins_frames * $self->{frame_ticks}; + print "Inserted $ins_frames Audio Frames ($ins_time ticks) to correct a drift of $self->{time_drift}. EXPERIMENTAL\n"; + $self->{time_drift} -= $ins_time; + } + } + + # + #If the currenct chunk's end has not been determined yet, then we just flush 60 frames and calculate the timedrift + # + + if (! $chunk_end) { + #my $frame; + #my $time; + #print "No Chunk end, just flushing\n"; + my $counter = 0; + my $bufferstart = ${$self->{frame_times}}[0]; + my $drift; + my $calc_end = $bufferstart; + while (scalar(@{$self->{frame_times}}) > 40) { + $self->{last_frame_time} = shift @{$self->{frame_times}}; + $calc_end += shift @{$self->{frame_length}}; + print $FH shift @{$self->{frame_buffer}}; + $counter++; + $self->{frames_written}++; + } + $drift = $self->{last_frame_time} - $calc_end + $self->{frame_ticks}; + + #This should not be necessary, since a PTS Overflow will be treated as a cut... + # + #if ($drift < -8000000000) { + # print "Time drift indicates a PTS overflow, correcting\n"; + # $drift += 8589934592; + # print "Now time drift will be " . ($self->{time_drift} + $drift) . "\n"; + #} + $self->{time_drift} += $drift; + if ($self->{show_drift} && (abs($drift) > 0)) { + print "\nAUDIO TIMEDRIFT for stream $self->{streamcode}: $self->{time_drift}\n" if (abs($self->{time_drift}) > 100); + } + return; + } + + # + #If the currenct chunk's end has been determined, but is at the end of the buffer, only 20 Frames are flushed to "move" the cut to the middle of the buffer + #Thereby we ensure that enough Audioframes for syncing are in the buffer before AND after the cut + # + my $cut_diff = $chunk_end - $self->{last_frame_time}; + + if (60 * ${$self->{frame_length}}[0] + $self->{last_frame_time} < $chunk_end) { + + print "delaying sync to next run, chunk_end is $chunk_end\n" if $self->{debug}; + my $calc = 60 * ${$self->{frame_length}}[0] + $self->{last_frame_time}; + while (scalar(@{$self->{frame_times}}) > 80) { + $self->{last_frame_time} = shift @{$self->{frame_times}}; + shift @{$self->{frame_length}}; + print $FH shift @{$self->{frame_buffer}}; + $self->{frames_written}++; + } + return; + } + + # Then we test wether the cut is no cut but a PTS overflow .... If yes, we just dump the buffer, increase the cutcount and continue. + # No Timedrift is computed for this buffer + if ($chunk_end > 8589889595) { + print "syncing at PTS Overflow, special care is taken!\n"; + while (scalar(@{$self->{frame_times}}) > 20) { + $self->{last_frame_time} = shift @{$self->{frame_times}}; + shift @{$self->{frame_length}}; + print $FH shift @{$self->{frame_buffer}}; + $self->{frames_written}++; + } + $self->{cutcounter}++; + return; + } + + + # + # Finally we sync the end of the Audio chunk + # + + + sync_audio_end($self, $chunk_end, $video_frameno); + $self->{cutcounter}++; + # Extract the beginning of the next chunk + ($chunk_start, $chunk_end) = split /::/,$cutlist[$self->{cutcounter}]; + if (! $chunk_start) { + print "Could not extract chunk start from $cutlist[$self->{cutcounter}]\n"; + $kill_me = $self->{streamcode}; + } + # And sync the start of the next chunk + sync_audio_start($self, $chunk_start); +} + +sub sync_audio_end { + my $self = shift; + my $chunk_end = shift; + my $video_frameno = shift; + + my $FH = $self->{outfile}; + my $counter = 0; + my $cut_frame = 0; + + for (my $i = 0; $i < scalar(@{$self->{frame_times}}); $i++) { + if (abs(${$self->{frame_times}}[$i + 1] - ${$self->{frame_times}}[$i]) > 90000) { + $cut_frame = $i; + #print "The end of the chunk is at frame $cut_frame of the buffer\n"; + last; + } + } + print "\nsyncing end of chunk in stream $self->{streamcode}...\n" if $self->{debug}; + my $bufferstart = ${$self->{frame_times}}[0]; + my $calc_end = $bufferstart; + while ((${$self->{frame_times}}[0] < $chunk_end - $self->{final_desync} + ($self->{frame_ticks} * 0.5)) + && ($counter < $cut_frame)){ + + $self->{last_frame_time} = shift @{$self->{frame_times}}; + $calc_end += shift @{$self->{frame_length}}; + print $FH shift @{$self->{frame_buffer}}; + $self->{frames_written}++; + $counter++; + if (scalar(@{$self->{frame_times}}) == 0) { + print "syncing failed for stream $self->{streamcode} while trying to write Frames until $chunk_end\n"; + print "time of last frame written was $self->{last_frame_time}\n"; + $kill_me = $self->{streamcode}; + } + } + print "written $self->{frames_written} frames, time $self->{last_frame_time}\n"if $self->{debug}; + # + # We dont need a timedrift calc at the end of a cunk, it is not corrected anyway...At the end the real length correction is performed + # + #my $drift = $self->{last_frame_time} - $calc_end + ${$self->{frame_length}}[0]; + #if ($drift < -8000000000) { + # print "Time drift indicates a PTS overflow, correcting\n"; + # $drift += 8589934592; + # print "Now time drift will be " . ($self->{time_drift} + $drift) . "\n"; + #} + #$self->{time_drift} += $drift; + #if ($self->{show_drift} && (abs($drift) > 0)) { + # print "\nAUDIO TIMEDRIFT for stream $self->{streamcode}: $self->{time_drift}\n" if (abs($self->{time_drift}) > 100); + #} + #my $diff = $chunk_end - ($self->{last_frame_time} + $self->{frame_ticks}); + my $vid_time = $video_frameno * 3600; + my $aud_time = $self->{frames_written} * $self->{frame_ticks}; + my $frame_diff = $vid_time - $aud_time; + print "For the end sync $self->{cutcounter} of $self->{streamcode} frames were printed up to $self->{last_frame_time} to match $chunk_end of chunk $self->{cutcounter} leaving: $frame_diff)\n"if $self->{debug}; + my $ins_frames = insert_frames($self, $frame_diff); + $self->{frames_written} += $ins_frames; + $aud_time = $self->{frames_written} * $self->{frame_ticks}; + $self->{final_desync} = $vid_time - $aud_time; + $self->{time_drift} = 0; + $self->{written_chunks}[$self->{cutcounter}] .= ($self->{last_frame_time} + $self->{frame_ticks} * ($ins_frames+1)); +} + +sub sync_audio_start { + my $self = shift; + my $chunk_start = shift; + print "\nFirst packet ts for stream $self->{streamcode} in buffer $self->{frame_times}[0] last:$self->{frame_times}[-1]\n" if $self->{debug}; + print "\nIn stream $self->{streamcode}: new start $chunk_start\n" if $self->{debug}; + my $counter = 0; + while ((${$self->{frame_times}}[0] < $chunk_start - $self->{frame_ticks} * 0.5) + || ( (${$self->{frame_times}}[0] - $chunk_start) > 90000 )) { # For PTS overflows and "catted" movies + #print "\ndropping frame with time ${$self->{frame_times}}[$counter]\n" if $self->{debug}; + shift @{$self->{frame_times}}; + shift @{$self->{frame_buffer}}; + shift @{$self->{frame_length}}; + $counter++; + if (scalar(@{$self->{frame_times}}) == 0) { + print "\nsyncing failed for stream $self->{streamcode} while trying to drop Frames until $chunk_start\n"; + #print "time of last frame dropped was $time_drop\n"; + $kill_me = $self->{streamcode}; + } + } + my $diff = $chunk_start - ${$self->{frame_times}}[0]; + my $ins_frames = insert_frames($self, $diff); + print "IN SYNC AUDIO START ($self->{streamcode}): diff is now: $diff\n" if $self->{debug}; + $self->{frames_written} += $ins_frames; + $self->{last_frame_time} = ${$self->{frame_times}}[0]; + $self->{written_chunks}[$self->{cutcounter}] = (${$self->{frame_times}}[0] - $self->{frame_ticks} * $ins_frames) . "::"; + print "\nCHUNKSTART: dropped $counter frames at the beginning of $self->{streamcode}, start is now ${$self->{frame_times}}[0] matching $chunk_start leaving $diff\n" if $self->{debug}; +} + + + +sub insert_frames { + my $self = shift; + my $diff = shift; + + $diff += $self->{final_desync}; + my $FH = $self->{outfile}; + my $abs_diff = abs($diff); + + my $counter = 0; + if ($abs_diff > 900000) { + print "more than 10 seconds auf Audio missing, killing stream $self->{streamcode}\n"; + $kill_me = $self->{streamcode}; + return; + } + while ($abs_diff > ($self->{frame_ticks} * 0.5 )) { + print $FH $self->{single_frame}; + $abs_diff -= $self->{frame_ticks}; + $counter++; + } + if ($diff < 0) {$abs_diff *= -1} + print "INSERT FRAMES:wrote additional $counter frames to close $diff ticks, leaving $abs_diff\n" if $self->{debug}; + $self->{final_desync} = $abs_diff; + #print "Final desync is now $self->{final_desync}\n"; + return $counter; +} + +sub analyse_video_stream { + my $self = shift; + my $payload = shift; + my %pes_header = %{shift @_}; + + $self->{packet_start} = pack("H8", "00000100"); + my $seqstart = pack("H8", "000001b3"); + if (!($payload =~ /$seqstart(.{8})/s)) {print "No SeqHeader in the first Frame, exiting\n"; $kill_me = "all"} + + # Here we used to kill the stream if no PTS is found, that changed... We kill the strream if we find a GOP withou ANY ts + $self->{check_frames} = \&check_GOPs; + + my $bitview = unpack("B64", $1); + print "Matched: $& at $-[0] bitview of the header\n$bitview \n" if $self->{debug}; + my $hor_size = "0b" . substr($bitview,0,12); + $self->{horizontal_size} = oct $hor_size; + my $ver_size = "0b" . substr($bitview,12,12); + $self->{vertical_size} = oct $ver_size; + my $asp_ratio = substr($bitview,24,4); + + if ($asp_ratio eq "0001"){ + $self->{aspect_ratio} = "1:1"; + } + elsif ($asp_ratio eq "0010") { + $self->{aspect_ratio} = "4:3"; + } + elsif ($asp_ratio eq "0011") { + $self->{aspect_ratio} = "16:9"; + } + elsif ($asp_ratio eq "0111") { + $self->{aspect_ratio} = "2.21:1"; + } + else { + $self->{aspect_ratio} = "$asp_ratio"; + } + + my $fps = substr($bitview,28,4); + if ($fps eq "0001") { + $self->{fps} = 23.967; + } + elsif ($fps eq "0010") { + $self->{fps} = 24; + } + elsif ($fps eq "0011") { + $self->{fps} = 25; + } + elsif ($fps eq "0100") { + $self->{fps} = 29.97; + } + elsif ($fps eq "0101") { + $self->{fps} = 30.97; + } + + $self->{bitrate_value} = 400 * ( oct ("0b" . substr($bitview,32, 18))); + my $marker_bit = substr($bitview,50, 1); + print "we got for fps: $fps\n" if $self->{debug}; + $self->{frame_ticks} = 90000 / $self->{fps}; + $self->{outfilename} = "$self->{outputpath}/$self->{streamcode}.mpv"; + open $self->{outfile}, ">$self->{outputpath}/$self->{streamcode}.mpv" || die "Can not open file for m2v: $!\n"; + binmode $self->{outfile}; +} + + +sub analyse_GOP { + my $self = shift; + + + my %GOP_info; + my $seqstart = pack("H8", "000001b3"); + if (!($self->{GOP_buffer} =~ /$seqstart(.{8})/s)) { + + if (! $self->{GOPno}) { + print "\nNo Seq Header in the GOP, exiting\n"; + $kill_me = "all"; + } + else { + print "\nA GOP without Sequence Header. Weird recording\n"; + open DFH, ">./strange_gop.mpg"; + print DFH $self->{GOP_buffer}; + close DFH; + return; + } + } + + my $bitview = unpack("B64", $1); + my $hor_size = "0b" . substr($bitview,0,12); + $GOP_info{horizontal_size} = oct $hor_size; + $self->{Unit_analysis_hash}{horizontal_size}{$GOP_info{horizontal_size}}++; + my $ver_size = "0b" . substr($bitview,12,12); + $GOP_info{vertical_size} = oct $ver_size; + $self->{Unit_analysis_hash}{vertical_size}{$GOP_info{vertical_size}}++; + my $asp_ratio = substr($bitview,24,4); + + if ($asp_ratio eq "0001"){ + $GOP_info{aspect_ratio} = "1:1"; + } + elsif ($asp_ratio eq "0010") { + $GOP_info{aspect_ratio} = "4:3"; + } + elsif ($asp_ratio eq "0011") { + $GOP_info{aspect_ratio} = "16:9"; + } + elsif ($asp_ratio eq "0111") { + $GOP_info{aspect_ratio} = "2.21:1"; + } + else { + $GOP_info{aspect_ratio} = "$asp_ratio"; + } + + + my $fps = substr($bitview,28,4); + if ($fps eq "0001") { + $GOP_info{fps} = 23.967; + } + elsif ($fps eq "0010") { + $GOP_info{fps} = 24; + } + elsif ($fps eq "0011") { + $GOP_info{fps} = 25; + } + elsif ($fps eq "0100") { + $GOP_info{fps} = 29.97; + } + elsif ($fps eq "0101") { + $GOP_info{fps} = 30.97; + } + + + $GOP_info{bitrate_value} = 400 * ( oct ("0b" . substr($bitview,32, 18))); + $self->{Unit_analysis_hash}{bitrate_value}{$GOP_info{bitrate_value}}++; + $self->{Unit_analysis_hash}{aspect_ratio}{$GOP_info{aspect_ratio}}++; + $self->{Unit_analysis_hash}{fps}{$GOP_info{fps}}++; + +} + + +sub check_GOPs { + my $self = shift; + my $payload = shift; + my %pes_header = %{shift @_}; + my $FH = $self->{outfile}; + my $gopstart = pack("H8", "000001b8"); + # First we just grep for the GOP start sequence + if ((! $self->{GOPno}) && (! $self->{GOP_buffer} )) { + $cutlist[$self->{cutcounter}] = "$pes_header{PTS_decimal}"; + push @{$self->{timestamps}}, $pes_header{PTS_decimal}; + my $seqstart = pack("H8", "000001b3"); + $payload =~ /$seqstart/g; + if (pos($payload)-4 != 0) { + print "The very first GOP did not start at 0, but at " . (pos($payload)-4) ."\n"; + $payload = substr($payload, (pos($payload)-4)); + } + else { + print "Yes, it did start at 0\n" if $self->{debug}; + } + $self->{GOP_buffer} .= $payload; + return; + } + + if (! ($payload =~ /$gopstart/gm) ){ + $self->{GOP_buffer} .= $payload; + #print "no GOPstart found, returning\n"; + return; + } + + $self->{GOPno}++; + + my @pics = $self->{GOP_buffer} =~ /$self->{packet_start}/gm; + my $frames_in_GOP = scalar(@pics); + + + $self->{frameno} += $frames_in_GOP; + + if ($self->{Unit_analysis_counter}++ > 9) { + $self->{Unit_analysis_counter} = 1; + analyse_GOP($self); + } + + + if (! $pes_header{PTS_decimal}) {die "Found no ts at GOP start\n";} + + push @{$self->{timestamps}}, $pes_header{PTS_decimal}; + + my $pts_diff = ${$self->{timestamps}}[-1] - ${$self->{timestamps}}[-2]; + my $calc = $self->{frame_ticks} * $frames_in_GOP; + #print "Timestamp diff is $pts_diff and frame time is $calc\n"; + + if ($calc != $pts_diff) { + my $ts_shift = $pts_diff - $calc; + #print "TS not as expected, diff is $ts_shift\n"; + if (abs($ts_shift) > 90000) { + my $chunk_end = ${$self->{timestamps}}[-2] + $frames_in_GOP * $self->{frame_ticks}; + if ($ts_shift > 0) { + print "\nCut detected in Video at $chunk_end\n"; + } + else { + print "\nCut detected at $chunk_end, possibly a PTS Overflow\n"; + } + $cutlist[$self->{cutcounter}] .= "::$chunk_end" . "::$self->{frameno}"; + print "Chunk entry is $cutlist[$self->{cutcounter}]\n"if $self->{debug}; + $self->{cutcounter}++; + $cutlist[$self->{cutcounter}] = "$pes_header{PTS_decimal}"; + $self->{time_drift} = 0; + } + else { + #print "shift is too small for a cut, storing the shift for later use\n"; + $self->{time_drift} += $ts_shift; + if (abs($self->{time_drift}) > 1000) { + print "\nTime Drift in VIDEO is now $self->{time_drift}, ts: ${$self->{timestamps}}[-2] and ${$self->{timestamps}}[-1] \n" if $self->{show_drift}; + } + } + } + print $FH $self->{GOP_buffer}; + $self->{GOP_buffer} = $payload; +} + + +sub search_audio_sync { + + my $self = shift; + my $payload = shift; + my $new_offset = -1; + print "Searching for audio sync\n"if $self->{debug}; + while ($payload =~ /$self->{frame_regex}/g) { + my $potential_start = (pos($payload)-2); + print "\tFound a potential audio syncword at $potential_start: " . unpack("H*" ,substr($payload,$potential_start,3)) . "\n" if $self->{debug}; + (my $dummy_payload, my %frame_info) = analyse_audio_frame($self, substr($payload,$potential_start)); + if (($potential_start + $frame_info{frame_bytes}) < length($payload)) { + print "\twe should have enough payload left to verify...\n" if $self->{debug}; + #(my $dummy_payload, my %frame_info) = analyse_audio_frame($self, substr($payload,$potential_start)); + + print "\tchecking " . unpack("H4",substr($payload,($potential_start + $frame_info{frame_bytes}),2)) ."\n" if $self->{debug}; + + if ($frame_info{packet_start} eq substr($payload,($potential_start + $frame_info{frame_bytes}),2)) { + print "\tfound an additional audio sync in the right distance\n\tAll should be fine\n" if $self->{debug}; + $new_offset = $potential_start; + print "\tNew attempt will be started at $new_offset\n" if $self->{debug}; + $self->{packet_rest} =""; + last; + } + else { + print "\tNext audio start not found, continuing to check...\n" if $self->{debug}; + } + } + else { + print "\tnot enough payload left to verify\n\tGood Luck\n\tSkipping to next packet\n" if $self->{debug}; + last; + } + } + + if ($new_offset != -1) { + return (substr($payload,$new_offset)); + # &{$self->{check_frames}} ($self, substr($payload,$new_offset), \%pes_header); + } + else { + print "\tCould not find anything usefull in this packet, contents dropped, maybe next packet?\n" if $self->{debug}; + return (-1); + } +} + +sub print_stats { + my $self = shift; + + foreach (keys(%{$self->{streams}})) { + print "$self->{streams}{$_}{streamtype} for stream $_\n" if $self->{debug}; + if ($self->{ignore_streams}{$_}) { + print "Ignoring stream $_\n"; + next; + } + + if (($self->{streams}{$_}{streamtype} =~ /audio/) || ($self->{streams}{$_}{streamtype} =~ /Layer/)) { + print "\naudio stream $_ info ($self->{streams}{$_}{streamtype}):\n"; + print "Sample frequency: $self->{streams}{$_}->{freq}\n"; # only for Audio + print "Bitrate: $self->{streams}{$_}->{bitrate}\n"; # only for Audio + print "Mode: $self->{streams}{$_}->{mode}\n"; # only for Audio + print "Copyright: $self->{streams}{$_}->{copyright}\n" if $self->{streams}{$_}->{copyright}; # only for Audio + print "Frame length (bytes) $self->{streams}{$_}->{frame_bytes}\n"; # only for Audio + print "Frame length (ticks) $self->{streams}{$_}->{frame_ticks} (90000 / sec)\n\n\n"; # one tick is 1 / 90000 of a second + } + if ($self->{streams}{$_}{streamtype} eq "video") { + print "video stream $_ info:\n"; + print "Frame length (ticks) $self->{streams}{$_}->{frame_ticks} (90000 / sec)\n"; # one tick is 1 / 90000 of a second + print "Aspect ratio $self->{streams}{$_}->{aspect_ratio}\n"; + print "Horizontal size $self->{streams}{$_}->{horizontal_size} \n"; + print "Vertical size $self->{streams}{$_}->{vertical_size} \n"; + print "Frames per Second $self->{streams}{$_}->{fps} \n"; + print "Bitrate: $self->{streams}{$_}{bitrate_value}\n\n\n"; + } + } +} + +sub analyse_audio_stream { + my $self = shift; + my $payload = shift; + my %pes_header = %{shift @_}; + + #$self->{cuts}[0] = "new start: $self->{first_ts} at 0\t"; #{frameno} : $pes_header{PTS_decimal} : -1 : -1 : $self->{streamcode} : "; + + $self->{frame_regex} = $mp2_regex; + + ($payload, my %frame_info) = analyse_audio_frame($self, $payload); + + if ($payload eq "-1") { + print "Audio Packet could not be analysed, skipping\n" if $self->{debug}; + return; + } + $self->{copyright} = $frame_info{copyright}; + $self->{bitrate} = $frame_info{bitrate}; + $self->{freq} = $frame_info{freq}; + $self->{padding_bit} = $frame_info{padding_bit}; + $self->{streamtype} = $frame_info{streamtype}; + $self->{frame_bytes} = $frame_info{frame_bytes}; + $self->{frame_ticks} = $frame_info{frame_ticks}; + $self->{mode} = $frame_info{mode}; + print "streamtype is: $self->{streamtype}\nbitrate is $self->{bitrate}\nfreq is $self->{freq}\ncopyright is $self->{copyright}\nframelength in byte is $self->{frame_bytes}\nin sec: " . ($self->{frame_ticks}/90000) . "\n" if $self->{debug};#/ + + if ($self->{streamtype} eq "MPEG1_Layer_2") { + $self->{check_frames} = \&check_audio_frames; + $self->{analyse_frames} = \&analyse_audio_frame; + $self->{frame_regex} = $mp2_regex; + } + $self->{outfilename} = "$self->{outputpath}/$self->{streamcode}.mpa"; + open $self->{outfile}, ">$self->{outputpath}/$self->{streamcode}.mpa" || die "Can not open file for mpa: $!\n"; + binmode $self->{outfile}; +} + +sub analyse_audio_frame { + my $self = shift; + my $frame = shift; + #my %pes_header = %{shift @_}; + + my %frame_info; + my $streamtype = ""; + if (!(substr($frame, 0, 2) =~ /^$mp2_regex$/)) { + print "No Audio syncword at the beginning of the frame, searching\n" if $self->{debug}; + if ($frame =~ /$mp2_regex/g) { + $frame = substr($frame, (pos($frame) - 2)) + } + else { + print "No Audio Sync found, returning -1\n" if $self->{debug}; + return -1; + } + print "found sync for mp2 stream $self->{streamcode}\n" if $self->{debug}; + } + if (length($frame) < 8) {print "Frame too small to analyse, returning for stream $self->{streamcode}\n"; return;} + my $BITS = unpack("B32", $frame); + if (substr($BITS,12,1) == 1) {$streamtype = "MPEG1"} else {$streamtype = "MPEG2"} + + if (substr($BITS,13,2) eq "01") {$streamtype .= "_Layer_3"} + elsif (substr($BITS,13,2) eq "10") {$streamtype .= "_Layer_2"} + elsif (substr($BITS,13,2) eq "11") {$streamtype .= "_Layer_1"} + else {print "Streamtype invalid, returning -1\n"; return -1} + + $frame_info{copyright} = substr($BITS,15,1); + if ($bitrates{$streamtype}{substr($BITS,16,4)}) { + $frame_info{bitrate} = $bitrates{$streamtype}{substr($BITS,16,4)} . "000" + } + else { + print "\nBitrate of the audio stream $self->{streamcode} could not be determined, skipping packet and trying again later\n"; + print "Bitrate bits are:" . substr($BITS,14,4) . "\n" if $self->{debug}; + return -1; + } + + $frame_info{freq} = $freqs{substr($streamtype,0,5)}{substr($BITS,20,2)}; + if (! $frame_info{freq}) { + print "\nFrequence of the audio stream $self->{streamcode} could not be determined, skipping packet and trying again later\n"; + print "Freq bits are:" . substr($BITS,20,2) . "\n" if $self->{debug}; + return -1; + } + $frame_info{padding_bit} = substr($BITS,22,1); + + $frame_info{streamtype} = $streamtype; + + + if (substr($BITS,24,2) eq "00") {$frame_info{mode} = "stereo"} + elsif (substr($BITS,24,2) eq "01") {$frame_info{mode} = "joint_stereo"} + elsif (substr($BITS,24,2) eq "10") {$frame_info{mode} = "dual_channel"} + elsif (substr($BITS,24,2) eq "11") {$frame_info{mode} = "mono"} + #print "Mode: $frame_info{mode}\nbitrate: $frame_info{bitrate}, freq: $frame_info{freq}" if $self->{debug}; + + + $frame_info{frame_bytes} = ($frame_info{bitrate} * 144 / $frame_info{freq}) + $frame_info{padding_bit}; + $frame_info{frame_ticks} = 8 * $frame_info{frame_bytes} * 90000 / $frame_info{bitrate}; + + #print "Mode: $frame_info{mode}\nbytes: $frame_info{frame_bytes}\nticks: $frame_info{frame_ticks}\nbitrate: $frame_info{bitrate}\n"; + $frame_info{packet_start} = substr($frame,0,2); + #print "The start of the packet is: $frame_info{packet_start}\n"; + return ($frame, %frame_info); + #print "frame analysed\n"; +} + +sub find_next_pes_packet { + my $self = shift; + my $buffer = shift; + my $old_offset = shift; + my $search_buffer; + if ($old_offset > 0) { + $search_buffer = substr($buffer, $old_offset); + } + else { + $search_buffer = $buffer; + } + my $packet_start = pack("H6", "000001"); + print "TRYING TO FIND A NEW PES PACKET START AFTER $old_offset\n"; + print "Got a buffer of length " . length($buffer) . " to match against\n"; + open DOF, ">./debug_buffer.vdr" || die "Can not open File to dump buffer\n"; + binmode DOF; + print DOF $buffer; + close DOF; + my $counter = 0; + while ($search_buffer =~ /$packet_start/g) { + + print "Now we found the new start at " . (pos($search_buffer)-3) . " \n"; + return (pos($search_buffer) - 3 + (length($buffer) - length($search_buffer))); + } + + print "did not find a new start\n"; + return -1; +} + +sub final_flush { + my $self = shift; + my $FH = $self->{outfile}; + print "should do the final flush for stream $self->{streamcode}\n"if $self->{debug}; + if ($self->{streamcode} eq $self->{masterstream}) { + print "Flushing Video Buffer, " . (length($self->{GOP_buffer})) . " bytes left\n"if $self->{debug}; + my @pics = $self->{GOP_buffer} =~ /$self->{packet_start}/gm; + print "The last GOP contains " . (scalar(@pics)) . " pics\n"if $self->{debug}; + print $FH $self->{GOP_buffer}; + $self->{GOP_buffer} = ""; + $self->{frameno} += scalar(@pics); + my $chunk_end = ${$self->{timestamps}}[-1] + (scalar(@pics)) * $self->{frame_ticks}; + $cutlist[$self->{cutcounter}] .= "::$chunk_end" . "::$self->{frameno}"; + if ($self->{debug}) { + foreach(@cutlist) {print "FINAL: $_\n";} + } + $self->{frames_written} = $self->{frameno}; + return; + } + print $FH @{$self->{frame_buffer}}; + $self->{frames_written} += scalar(@{$self->{frame_buffer}}); + @{$self->{frame_buffer}} = ""; + if (! $self->{audio_only}) { + my ($chunk_start, $chunk_end, $video_frameno) = split /::/,$cutlist[-1]; + my $vid_time = $video_frameno * 3600; + my $aud_time = $self->{frames_written} * $self->{frame_ticks}; + my $diff = $vid_time - $aud_time; + $self->{frames_written} += insert_frames($self, $diff); + } +} + + +sub process_PES_packets { + my $self = shift; + my $limit = shift; + my $offset = 0; # stores the position we are at in the current buffer + $self->{IFH} = undef; # Stores the filehandle for the current file + $self->{EOF} = 0; # All files processed ? + $self->{total_input} = 0; + my $lengthcounter = 0; + my $packetcounter = 0; + + my %pes_header; + my $head_tmpl = 'H6 H2 n1 B8 B8 C1 B40'; + my $dts_tmpl = 'B40'; + + my $buffer = ${ readNextChunk($self) }; # First we read the first chunk of data + + ( $pes_header{startcode}, $pes_header{stream_id}, + $pes_header{PES_packet_length}, $pes_header{eflags1}, + $pes_header{eflags2}, $pes_header{header_length}, + $pes_header{PTS_raw_bits}) + = unpack ( $head_tmpl, substr($buffer, 0, 12) ); + + while (($self->{EOF} eq 0 || $offset + $pes_header{PES_packet_length} + 1) < (length($buffer))) { + if ($kill_me) { + my @kill_list; + if ($kill_me eq "all") { @kill_list = keys(% {$self->{streams} })} + else { push @kill_list, $kill_me } + if ($self->{dump_buffer}) { + print "Something nasty happend, dumping debug.buffer and exit\n"; + my $start = 0; + if ($offset < 10000) { + print "Unfortunately the nasty thing happend at the start of a buffer \n"; + print "offset is only $offset; Dumping anyway...\n"; + } + if ((length($buffer) - $offset )< 1000000) { + print "Unfortunately the nasty thing happend at the end of a buffer \n"; + print "offset is already $offset Dumping anyway...\n"; + } + if ($offset > 1000000) {$start = $offset - 1000000} + open DFH, ">./debug.buffer"; + binmode DFH; + print DFH substr($buffer, $start, $start + 2000000); + close DFH; + print "please mail the file and a short description of the recording to \n"; + print "vdrsync\@gmx.net, then I will try to fix the bug\n"; + exit 1;dump_buffer($self) + } + print "got the kill list @kill_list\n"; + foreach (@kill_list) { + $self->{ignore_streams}{$_} = 1; + my $FH = $self->{streams}{$_}->{outfile}; + close $FH; + unlink $self->{streams}{$_}->{outfilename}; + print "Stream $_ was killed due to an error\n"; + } + if ($kill_me eq "all") {die "Skript stopped due to an error\n";} + $kill_me=""; + + } + + + ( $pes_header{startcode}, $pes_header{stream_id}, + $pes_header{PES_packet_length}, $pes_header{eflags1}, + $pes_header{eflags2}, $pes_header{header_length}, + $pes_header{PTS_raw_bits}) + = unpack ( $head_tmpl, substr($buffer, $offset, 14) ); + + if ($pes_header{startcode} ne "000001") { + print "No 0x000001 found at current packet, found $pes_header{startcode} instead\noffset is $offset\n"; + $offset = find_next_pes_packet($self, $buffer, $offset); + + next; + } + my $decimal_code = hex($pes_header{stream_id}); + if ((!(191 < $decimal_code ) && (240 > $decimal_code)) && (! $decimal_code == 189)) { + print "unknown Streamtype $pes_header{stream_id}, decimal $decimal_code ignoring\n" if $self->{debug}; + $offset += $pes_header{PES_packet_length} + 6; + next; + } + # MPEG2 Audio and Video must have an extended Header as well as AC3 + $pes_header{payload_start} = (9 + $pes_header{header_length}); + # We check whether a TimeStamp is present: + $pes_header{PTS_DTS_flags} = substr($pes_header{eflags2}, 0, 2); + + #FIXME: overflow of PTS not checked + if (($pes_header{PTS_DTS_flags} eq "10") || ($pes_header{PTS_DTS_flags} eq "11")) { + $pes_header{PTS_value_bits} = substr($pes_header{PTS_raw_bits},4,3) . substr($pes_header{PTS_raw_bits},8,15) . substr($pes_header{PTS_raw_bits},24,15); + # decode the timestamp + $pes_header{PTS_decimal} = oct("0b" . substr($pes_header{PTS_value_bits},1)); + $pes_header{PTS_decimal} += 4294967296 if (substr($pes_header{PTS_value_bits},0,1) == 1); + } + else { + $pes_header{PTS_decimal} = 0; + } + $pes_header{data_align} = substr($pes_header{eflags1},6,1); + + if ((($offset + $pes_header{PES_packet_length} + 150) > (length($buffer))) && ($self->{EOF} == 0)) { + my $helpbuffer = substr($buffer, $offset); + $buffer = $helpbuffer . ${ readNextChunk($self) }; + $offset = 0; + } + + my $packet = substr($buffer, $offset, $pes_header{PES_packet_length} + 6); + $offset += $pes_header{PES_packet_length} + 6; + $packetcounter++; + $lengthcounter += ($pes_header{PES_packet_length} + 6); + + + if ($self->{ignore_streams}{$pes_header{stream_id}}) { + print "ignoring packet for stream $pes_header{stream_id}\n" if $self->{debug}; + next; + } + if (!($self->{streams}{$pes_header{stream_id}})) { + print "\nNew Stream with id $pes_header{stream_id}. Ignoring the stream\n" if (! $self->{audio_only}); + $self->{ignore_streams}{$pes_header{stream_id}} = 1; + next; + } + if ($self->{dump_packets}) { + print "Dumping packet of stream $pes_header{stream_id} ($self->{dump_packets} to dump left)\n"; + my $DUMPFH; + if (! $self->{"$pes_header{stream_id}.pes_dump"}) { + open $DUMPFH, ">$pes_header{stream_id}.pes_dump" || die "Can not open dumpfile: $!\n"; + binmode $DUMPFH; + $self->{"$pes_header{stream_id}.pes_dump"} = $DUMPFH; + } + $DUMPFH = $self->{"$pes_header{stream_id}.pes_dump"}; + print $DUMPFH $packet; + if ($self->{dump_packets}-- == 1) { + exit; + } + next; + } + &{$self->{streams}{$pes_header{stream_id}}->{check_frames}} ( + $self->{streams}{$pes_header{stream_id}}, + substr($packet,$pes_header{payload_start}), + \%pes_header + ); + } + + print "\n $packetcounter PES packets processed\n"; + + + if (! $self->{audio_only}) { + $self->{streams}{$self->{masterstream}}->final_flush($self->{streams}{$self->{masterstream}}); + } + foreach (keys (%{$self->{streams}})) { + if ($self->{ignore_streams}{$_}) {next} + if (! ($self->{masterstream} eq $_)) { + print "Final flush for stream $_\n"if $self->{debug}; + $self->{streams}{$_}->final_flush($self->{streams}{$_}); + } + my $seconds = $self->{streams}{$_}{frames_written} * $self->{streams}{$_}{frame_ticks} / 90000; #/ + print "$self->{streams}{$_}{frames_written} frames written for stream $_ ($seconds sec) \n"; + next; + } + print_script_output($self); + if (! $self->{script_output}){print_stats($self)}; + + +} + +sub print_script_output { + my $self = shift; + + my %final_properties; + my $master = $self->{streams}{$self->{masterstream}}; + my $max = 0; + + foreach my $movie_property(keys(%{$master->{Unit_analysis_hash}})) { + print "found key $movie_property\n" if $self->{debug}; + foreach my $subkey (keys(%{$master->{Unit_analysis_hash}{$movie_property}})) { + print "found subkey $subkey with value $master->{Unit_analysis_hash}{$movie_property}{$subkey}\n" if $self->{debug}; + if ((!$final_properties{$movie_property}) || $max < $master->{Unit_analysis_hash}{$movie_property}{$subkey}) { + $final_properties{$movie_property} = $subkey; + $max = $master->{Unit_analysis_hash}{$movie_property}{$subkey}; + } + + } + } + $max = 0; + if ($self->{streams}{bd}) { + my $ac3_audio= $self->{streams}{bd}; + foreach my $ac3_property(keys(%{$ac3_audio->{Unit_analysis_hash}})) { + print "found key $ac3_property\n" if $self->{debug}; + foreach my $subkey (keys(%{$ac3_audio->{Unit_analysis_hash}{$ac3_property}})) { + print "found subkey $subkey with value $ac3_audio->{Unit_analysis_hash}{$ac3_property}{$subkey}\n" if $self->{debug}; + if ((!$final_properties{$ac3_property}) || $max < $ac3_audio->{Unit_analysis_hash}{$ac3_property}{$subkey}) { + $final_properties{$ac3_property} = $subkey; + $max = $ac3_audio->{Unit_analysis_hash}{$ac3_property}{$subkey}; + } + } + } + } + + + foreach (keys(%final_properties)) { + print "Property $_ is $final_properties{$_}\n" if $self->{debug}; + $self->{streams}{$self->{masterstream}}->{$_} = $final_properties{$_}; + } + if (! $self->{script_output}) { + return + } + + + print "*" x 45 ."\n"; + foreach (keys(%{$self->{streams}})) { + print "$self->{streams}{$_}{streamtype} for stream $_\n" if $self->{debug}; + if ($self->{ignore_streams}{$_}) { + print "Ignoring stream $_\n" if $self->{debug}; + next; + } + + if (($self->{streams}{$_}{streamtype} =~ /audio/) || ($self->{streams}{$_}{streamtype} =~ /Layer/)) { + + my $seconds = $self->{streams}{$_}->{frames_written} * $self->{streams}{$_}->{frame_ticks} / 90000; #/ + print "$_" . "_Audio_stream=yes\n"; + print "$_" . "_Audio_type=$self->{streams}{$_}{streamtype}\n"; + print "$_" . "_Sample_frequency=$self->{streams}{$_}->{freq}\n"; # only for Audio + print "$_" . "_Bitrate=$self->{streams}{$_}->{bitrate}\n"; # only for Audio + print "$_" . "_Mode=$self->{streams}{$_}->{mode}\n"; # only for Audio + print "$_" . "_Copyright=$self->{streams}{$_}->{copyright}\n" if $self->{streams}{$_}->{copyright}; # only for Audio + print "$_" . "_Bytes_per_frame=$self->{streams}{$_}->{frame_bytes}\n"; # only for Audio + print "$_" . "_Ticks_per_frame=$self->{streams}{$_}->{frame_ticks}\n"; # one tick is 1 / 90000 of a second + print "$_" . "_Total_frames=$self->{streams}{$_}->{frames_written}\n"; + print "$_" . "_Total_time=$seconds\n\n"; + + } + if ($self->{streams}{$_}{streamtype} eq "video") { + + my $seconds = $self->{streams}{$_}->{frames_written} * $self->{streams}{$_}->{frame_ticks} / 90000; #/ + print "$_" . "_Video_stream=yes\n"; + print "$_" . "_Aspect_ratio=$self->{streams}{$_}->{aspect_ratio}\n"; + print "$_" . "_Horizontal_size=$self->{streams}{$_}->{horizontal_size}\n"; + print "$_" . "_Vertical_size=$self->{streams}{$_}->{vertical_size}\n"; + print "$_" . "_Frames_per_Second=$self->{streams}{$_}->{fps}\n"; + print "$_" . "_Bitrate=$self->{streams}{$_}{bitrate_value}\n"; + print "$_" . "_Ticks_per_frame=$self->{streams}{$_}->{frame_ticks}\n"; + print "$_" . "_Total_frames=$self->{streams}{$_}->{frames_written}\n"; + print "$_" . "_Total_time=$seconds\n\n"; + + } + } +} + +sub init_PES_stream { + my $self = shift; + my $limit = shift; + my $offset = 0; # stores the position we are at in the current buffer + my $plength = 0; + $self->{IFH} = undef; # Stores the filehandle for the current file + $self->{EOF} = 0; # All files processed ? + $self->{total_input} = 0; + my %pes_header; + $pes_header{packet_length} = 0; + my $head_tmpl = 'H6 H2 n1 B8 B8 C1 B40'; + + + + my $packetcounter = 0; + + print "Initialising and analysing the streams....\n"; + my @save_file_list = @{$self->{files}}; + + my $buffer = ${ readNextChunk($self) }; # First we read the first chunk of data + + while ((($self->{EOF} eq 0 || $offset + $pes_header{packet_length} + 1) < (length($buffer))) && ($packetcounter < 2000)) { + ( $pes_header{startcode}, $pes_header{stream_id}, + $pes_header{PES_packet_length}, $pes_header{eflags1}, + $pes_header{eflags2}, $pes_header{header_length}, + $pes_header{PTS_raw_bits}) + = unpack ( $head_tmpl, substr($buffer, $offset, 12) ); + + $pes_header{payload_start} = (9 + $pes_header{header_length}); + # there are at leat six bytes of header at the beginning of a PES packet + if ( $pes_header{startcode} ne "000001") { + print "No 0x000001 found at current packet, searching for next Packet start\n"; + $offset = find_next_pes_packet($self, $buffer, $offset); + if ($offset == -1) {$kill_me = "all"} + next; + } + # We check whether a TimeStamp is present: + $pes_header{PTS_DTS_flags} = substr($pes_header{eflags2}, 0, 2); + if (($pes_header{PTS_DTS_flags} eq "10") || ($pes_header{PTS_DTS_flags} eq "11")) { + $pes_header{PTS_value_bits} = substr($pes_header{PTS_raw_bits},4,3) . substr($pes_header{PTS_raw_bits},8,15) . substr($pes_header{PTS_raw_bits},24,15); + # decode the timestamp + $pes_header{PTS_decimal} = unpack("N", (pack ("B32", substr($pes_header{PTS_value_bits},1)))); + $pes_header{PTS_decimal} += 4294967296 if (substr($pes_header{PTS_value_bits},0,1) == 1); + } + else { + $pes_header{PTS_decimal} = 0; + } + if ((($offset + $pes_header{PES_packet_length} + 150) > (length($buffer))) && ($self->{EOF} == 0)) { + my $helpbuffer = substr($buffer, $offset); + $buffer = $helpbuffer . ${ readNextChunk($self) }; + $offset = 0; + } + + my $packet = substr($buffer, $offset, ($pes_header{PES_packet_length} + 6)); + $offset += $pes_header{PES_packet_length} + 6; + $packetcounter++; + + #print "analysed the first $packetcounter packets...\n" if $self->{debug}; + if (!$self->{streams}{$pes_header{stream_id}}) { + if ($self->{ignore_streams}{$pes_header{stream_id}}){ + next; + } + + my $decimal_code = hex($pes_header{stream_id}); + if ((223 < $decimal_code ) && (240 > $decimal_code)){ + if ($self->{audio_only}) { + $self->{ingnore_streams}{$pes_header{stream_id}} = 1; + next; + } + + if ($self->{masterstream}){ + print "Video stream already defined, but found stream $pes_header{stream_id} in addition. Ingnoring stream $pes_header{stream_id}\n"; + $self->{ingnore_streams}{$pes_header{stream_id}} = 1; + next; + } + else { + $self->{streams}{$pes_header{stream_id}} = MPEGSTREAM->new + ( + streamcode => $pes_header{stream_id}, + outputpath => $self->{outputpath}, + masterstream => $pes_header{stream_id}, + debug => $self->{debug}, + dump_payload => $self->{dump_payload}, + show_drift => $self->{show_drift}, + dump_buffer => $self->{dump_buffer}, + ); + $self->{masterstream} = $pes_header{stream_id}; + print "\nCreated new MPEG stream object for stream $pes_header{stream_id}, master video stream\n"; + } + } + else { + $self->{streams}{$pes_header{stream_id}} = MPEGSTREAM->new + ( + streamcode => $pes_header{stream_id}, + masterstream => $self->{masterstream}, + outputpath => $self->{outputpath}, + debug => $self->{debug}, + dump_payload => $self->{dump_payload}, + audio_only => $self->{audio_only}, + show_drift => $self->{show_drift}, + dump_buffer => $self->{dump_buffer}, + ); + print "\nCreated new MPEG stream object for stream $pes_header{stream_id} \n"; + } + + } + + if (!($self->{streams}{$pes_header{stream_id}}{check_frames})) + { + print "sending frame number $packetcounter for analysis of stream $pes_header{stream_id}\n" if $self->{debug}; + init_stream($self->{streams}{$pes_header{stream_id}}, substr($packet, $pes_header{payload_start}), \%pes_header); + } + } + + + @{$self->{files}} = @save_file_list; + print "analysed the first $packetcounter packets...\n"; + my $IFH = $self->{IFH}; + close $IFH; + + if (!($self->{masterstream})) + { + die "No video stream could be found within the first 2000 packets, exiting\n" if (! $self->{ignore_streams}); + } + + foreach (keys(%{$self->{streams}})) + { + $self->{streams}{$_}{masterstream} = $self->{masterstream}; + + if ((!($self->{streams}{$_}{check_frames})) && (!($self->{ignore_streams}{$_}))) + { + print "The contents of stream $_ could not be identified, the stream will be skipped!\n"; + $self->{ignore_streams}{$_} = 1; + } + } + + print_stats($self) if $self->{debug}; + return; +} + + + +sub get_aspect_ratio { + my $self = shift; + return $self->{streams}{$self->{masterstream}}->{aspect_ratio}; +} + +sub readNextChunk { + my $self = shift; + my $IFH = $self->{IFH}; + + + if (!($self->{IFH})) { + my $firstfile = shift @{$self->{files}}; + print "trying to open $firstfile\n" if $self->{debug}; + open $IFH, $firstfile || die "$! happend while opening $firstfile\n"; + binmode $IFH; + $self->{IFH} = $IFH; + } + + my $rbuffer; + my $rbytes = sysread $IFH, $rbuffer, 10000000, 0; + + if ($rbytes != 10000000 ) { + if ((scalar(@{$self->{files}}))== 0) { + print "\nall Input files processed\n"; + print "EOF reached\n"; + $self->{EOF} = 1; + } + else { + my $nextf = shift @{$self->{files}}; + print "$nextf is the next file\n"; + close $IFH; + open $IFH, $nextf || die "$! happend while opening $nextf\n"; + binmode $IFH; + $self->{IFH} = $IFH; + my $helpbuffer; + my $helprbytes = sysread $self->{IFH}, $helpbuffer, (10000000 - length($rbuffer)), 0; + $rbuffer .= $helpbuffer; + $rbytes += $helprbytes; + } + } + $self->{total_input} += $rbytes; + my $status = sprintf ("%4d", ($self->{total_input} / 1000000));#/ + $bytes_read = $self->{total_input}; + print "\r$status Mbytes of " . int($total_size/1000000) . " read"; #if $self->{debug}; f + return \$rbuffer; +} + + + +sub get_silent_frame { + my $self = shift; + my %info = % { shift @_ }; + my $frame; + my $function; + + if ($info{streamtype} eq "MPEG1_Layer_2") { + $function = "get_$info{mode}_" . "$info{bitrate}"; + } + elsif ($info{streamtype} eq "AC3_Audio") { + $info{mode} =~ s/\//_/; + $function = "get_ac3_$info{mode}_" . "$info{bitrate}"; + } + else { + print "Can not understand Format $info{streamtype}, no silent frame available\n" if $self->{debug}; return -1; + } + + my $uu_frame = eval $function ; + if (! $uu_frame) {print "No silent frame available for $function\n" if $self->{debug}; return -1} + foreach (split "\n", $uu_frame) { + last if /^end/; + next if /[a-z]/; + next unless int((((ord() - 32) & 077) + 2) / 3) == int(length() / 4); + $frame .= unpack "u", $_; + } + + return $frame; +} + + + +sub get_mono_32000 { +my $frame = <<'End_FRAME'; +M__T4P!%)I&JJOOOOOOOOFM?/EL?/FM:^?+8^?-:U\^6Q\^:UKY\MCY\UK7SY +M;'SYK6OGRV/GS6M?/EL?/FM:^?+8^?-:U\^6Q\^:UKY\MCY\UK7SY;'SYK6O +&GRV/GS0` +end +End_FRAME +return $frame; +} + +sub get_mono_48000 { +my $frame = <<'End_FRAME'; +M__TDP#-R-NJJOOOOOOOOEL6Q;'=W=UL6Q;%L6Q;'=W=UL6Q;%L6Q;'=W=UL6 +MQ;%L6Q;'=W=UL6Q;%L6Q;'=W=UL6Q;%L6Q;'=W=UL6Q;%L6Q;'=W=UL6Q;%L +M6Q;'=W=UL6Q;%L6Q;'=W=UL6Q;%L6Q;'=W=UL6Q;%L6Q;'=W=UL6Q;%L6Q;' +)=W=UL6Q;```` +end +End_FRAME +return $frame; +} + +sub get_mono_56000 { + +my $frame = <<'End_FRAME'; +M__TTP!(C,R(B$D``````JJJJOOOOOOOOOOOOOFMMMMMMMM\^?/FM:UMMMMMM +MMOGSY\UK6MMMMMMMM\^?/FM:UMMMMMMMOGSY\UK6MMMMMMMM\^?/FM:UMMMM +MMMMOGSY\UK6MMMMMMMM\^?/FM:UMMMMMMMOGSY\UK6MMMMMMMM\^?/FM:UMM +AMMMMMOGSY\UK6MMMMMMMM\^?/FM:UMMMMMMMOGSY\UK0 +end +End_FRAME + +return $frame; +} + +sub get_mono_64000 { +my $frame = <<'End_FRAME'; +M__U$P"(D1$,B)$``````JJJJOOOOOOOOOOOOOFVVVVMBV+8MC;?/GSYK;;;; +M6Q;%L6QMOGSY\UMMMMK8MBV+8VWSY\^:VVVVUL6Q;%L;;Y\^?-;;;;:V+8MB +MV-M\^?/FMMMMM;%L6Q;&V^?/GS6VVVVMBV+8MC;?/GSYK;;;;6Q;%L6QMOGS +MY\UMMMMK8MBV+8VWSY\^:VVVVUL6Q;%L;;Y\^?-;;;;:V+8MBV-M\^?/FMMM +,MM;%L6Q;&V^?/GS0 +end +End_FRAME +return $frame; +} + + +sub get_mono_80000 { +my $frame = <<'End_FRAME'; +M__U4P"(U541#-HD`````JJJJJ^^^^^^^^^^^^^^^;;;=W=W=W=W6Q;%L;;;; +M;YK6VVW=W=W=W=UL6Q;&VVVV^:UMMMW=W=W=W=;%L6QMMMMOFM;;;=W=W=W= +MW6Q;%L;;;;;YK6VVW=W=W=W=UL6Q;&VVVV^:UMMMW=W=W=W=;%L6QMMMMOFM +M;;;=W=W=W=W6Q;%L;;;;;YK6VVW=W=W=W=UL6Q;&VVVV^:UMMMW=W=W=W=;% +ML6QMMMMOFM;;;=W=W=W=W6Q;%L;;;;;YK6VVW=W=W=W=UL6Q;&VVVV^:UMMM +/W=W=W=W=;%L6QMMMMOFM +end +End_FRAME +return $frame; +} + +sub get_mono_96000 { +my $frame = <<'End_FRAME'; +M__UDP#,V9E5$2-$@````JJJJJOOOOOOOOOOOOOOOOG=W=W=WO>][WO>[N[NM +MBV+8MC;?-:[N[N[N][WO>][W=W=UL6Q;%L;;YK7=W=W=WO>][WO>[N[NMBV+ +M8MC;?-:[N[N[N][WO>][W=W=UL6Q;%L;;YK7=W=W=WO>][WO>[N[NMBV+8MC +M;?-:[N[N[N][WO>][W=W=UL6Q;%L;;YK7=W=W=WO>][WO>[N[NMBV+8MC;?- +M:[N[N[N][WO>][W=W=UL6Q;%L;;YK7=W=W=WO>][WO>[N[NMBV+8MC;?-:[N +M[N[N][WO>][W=W=UL6Q;%L;;YK7=W=W=WO>][WO>[N[NMBV+8MC;?-:[N[N[ +2N][WO>][W=W=UL6Q;%L;;YK0 +end +End_FRAME +return $frame; +} + +sub get_mono_112000 { +my $frame = <<'End_FRAME'; +M__UTP#-&=F956R(D````JJJJJK[[[[[[[[[[[[[[[[YW=W=[WO>]]]][WO>] +M[WN[N[N[N[K8MCYK7=W=WO>][WWWWO>][WO>[N[N[N[NMBV/FM=W=W>][WO? +M??>][WO>][N[N[N[NZV+8^:UW=W=[WO>]]]][WO>][WN[N[N[N[K8MCYK7=W +M=WO>][WWWWO>][WO>[N[N[N[NMBV/FM=W=W>][WO???>][WO>][N[N[N[NZV +M+8^:UW=W=[WO>]]]][WO>][WN[N[N[N[K8MCYK7=W=WO>][WWWWO>][WO>[N +M[N[N[NMBV/FM=W=W>][WO???>][WO>][N[N[N[NZV+8^:UW=W=[WO>]]]][W +MO>][WN[N[N[N[K8MCYK7=W=WO>][WWWWO>][WO>[N[N[N[NMBV/FM=W=W>][ +5WO???>][WO>][N[N[N[NZV+8^:T` +end +End_FRAME +return $frame; +} + +sub get_mono_128000 { +my $frame = <<'End_FRAME'; +M__V$P$17=V9F:V-D````JJJJJK[[[[[[[[[[[[[[[[Y[WO>]]]]]]]]]]]]] +M[WO>][WO>][WN[N[K8VVVU[WO>]]]]]]]]]]]]][WO>][WO>][WN[N[K8VVV +MU[WO>]]]]]]]]]]]]][WO>][WO>][WN[N[K8VVVU[WO>]]]]]]]]]]]]][WO +M>][WO>][WN[N[K8VVVU[WO>]]]]]]]]]]]]][WO>][WO>][WN[N[K8VVVU[W +MO>]]]]]]]]]]]]][WO>][WO>][WN[N[K8VVVU[WO>]]]]]]]]]]]]][WO>][ +MWO>][WN[N[K8VVVU[WO>]]]]]]]]]]]]][WO>][WO>][WN[N[K8VVVU[WO>] +M]]]]]]]]]]]][WO>][WO>][WN[N[K8VVVU[WO>]]]]]]]]]]]]][WO>][WO> +M][WN[N[K8VVVU[WO>]]]]]]]]]]]]][WO>][WO>][WN[N[K8VVVU[WO>]]]] +8]]]]]]]]][WO>][WO>][WN[N[K8VVVT` +end +End_FRAME +return $frame; +} + +sub get_mono_160000 { +my $frame = <<'End_FRAME'; +M__V4P%5HF(=W?;60@```JJJJJJ^^^^^^^^^^^^^^^^^^???????OW[]^_?W] +M_?OW[]^_????????????>][WO>][NZV+8U]]]]]]^_?OW[]_?W]^_?OW[]]] +M]]]]]]]]]][WO>][WN[K8MC7WWWWWW[]^_?OW]_?W[]^_?OWWWWWWWWWWWWW +MO>][WO>[NMBV-???????OW[]^_?W]_?OW[]^_????????????>][WO>][NZV +M+8U]]]]]]^_?OW[]_?W]^_?OW[]]]]]]]]]]]]][WO>][WN[K8MC7WWWWWW[ +M]^_?OW]_?W[]^_?OWWWWWWWWWWWWWO>][WO>[NMBV-???????OW[]^_?W]_? +MOW[]^_????????????>][WO>][NZV+8U]]]]]]^_?OW[]_?W]^_?OW[]]]]] +M]]]]]]]][WO>][WN[K8MC7WWWWWW[]^_?OW]_?W[]^_?OWWWWWWWWWWWWWO> +M][WO>[NMBV-???????OW[]^_?W]_?OW[]^_????????????>][WO>][NZV+8 +MU]]]]]]^_?OW[]_?W]^_?OW[]]]]]]]]]]]]][WO>][WN[K8MC7WWWWWW[]^ +>_?OW]_?W[]^_?OWWWWWWWWWWWWWO>][WO>[NMBV- +end +End_FRAME +return $frame; +} + +sub get_mono_192000 { +my $frame = <<'End_FRAME'; +M__VDP%9IF8B(?[:Q$```JJJJJJOOOOOOOOOOOOOOOOOOOGWWW[]^_?OW]_?W +M]_?W]_?W[]^_?OW[]^_?OWWWW__?_]__WO>][WO>[NMCYK[[[]^_?OW[^_O[ +M^_O[^_O[]^_?OW[]^_?OW[[[[__O_^__[WO>][WO=W6Q\U]]]^_?OW[]_?W] +M_?W]_?W]^_?OW[]^_?OW[]]]]__W__?_][WO>][WN[K8^:^^^_?OW[]^_O[^ +M_O[^_O[^_?OW[]^_?OW[]^^^^__[__O_^][WO>][W=UL?-????OW[]^_?W]_ +M?W]_?W]_?OW[]^_?OW[]^_????_]__W__>][WO>][NZV/FOOOOW[]^_?O[^_ +MO[^_O[^_OW[]^_?OW[]^_?OOOO_^__[__O>][WO>]W=;'S7WWW[]^_?OW]_? +MW]_?W]_?W[]^_?OW[]^_?OWWWW__?_]__WO>][WO>[NMCYK[[[]^_?OW[^_O +M[^_O[^_O[]^_?OW[]^_?OW[[[[__O_^__[WO>][WO=W6Q\U]]]^_?OW[]_?W +M]_?W]_?W]^_?OW[]^_?OW[]]]]__W__?_][WO>][WN[K8^:^^^_?OW[]^_O[ +M^_O[^_O[^_?OW[]^_?OW[]^^^^__[__O_^][WO>][W=UL?-????OW[]^_?W] +M_?W]_?W]_?OW[]^_?OW[]^_????_]__W__>][WO>][NZV/FOOOOW[]^_?O[^ +D_O[^_O[^_OW[]^_?OW[]^_?OOOO_^__[__O>][WO>]W=;'S0 +end +End_FRAME +return $frame; +} + + +sub get_stereo_48000 { +my $frame = <<'End_FRAME'; +M__TD```1)22)))JJJJK[[[[[[[[[[[[[YK6M?/GSYK6M:UK6M?/GSYK6M:UK +M6M?/GSYK6M:UK6M?/GSYK6M:UK6M?/GSYK6M:UK6M?/GSYK6M:UK6M?/GSYK +M6M:UK6M?/GSYK6M:UK6M?/GSYK6M:UK6M?/GSYK6M:UK6M?/GSYK6M:UK6M? +)/GSYK6M:T``` +end +End_FRAME +return $frame; +} + +sub get_stereo_56000{ +my $frame = <<'End_FRAME'; +M__TT`!$11222))JJJJJOOOOOOOOOOOOOOOOFM:U\U\^?/GSYK6M:UK7S7SY\ +M^?/FM:UK6M?-?/GSY\^:UK6M:U\U\^?/GSYK6M:UK7S7SY\^?/FM:UK6M?-? +M/GSY\^:UK6M:U\U\^?/GSYK6M:UK7S7SY\^?/FM:UK6M?-?/GSY\^:UK6M:U +A\U\^?/GSYK6M:UK7S7SY\^?/FM:UK6M?-?/GSY\^:UK0 +end +End_FRAME +return $frame; +} + +sub get_stereo_64000 { +my $frame = <<'End_FRAME'; +M__U$`!$12;;21)JJJJJOOOOOOOOOOOOOOOOFM:U\^6Q;%L6Q\^?-:UK6M?/E +ML6Q;%L?/GS6M:UK7SY;%L6Q;'SY\UK6M:U\^6Q;%L6Q\^?-:UK6M?/EL6Q;% +ML?/GS6M:UK7SY;%L6Q;'SY\UK6M:U\^6Q;%L6Q\^?-:UK6M?/EL6Q;%L?/GS +M6M:UK7SY;%L6Q;'SY\UK6M:U\^6Q;%L6Q\^?-:UK6M?/EL6Q;%L?/GS6M:UK +,7SY;%L6Q;'SY\UK0 +end +End_FRAME +return $frame; +} + +sub get_stereo_80000{ +my $frame = <<'End_FRAME'; +M__U4`"(B;;;;;2JJJJJOOOOOOOOOOOOOOOOGSY\^6Q;%L6Q;%L6Q;%L6Q\^? +M/GSY;%L6Q;%L6Q;%L6Q;'SY\^?/EL6Q;%L6Q;%L6Q;%L?/GSY\^6Q;%L6Q;% +ML6Q;%L6Q\^?/GSY;%L6Q;%L6Q;%L6Q;'SY\^?/EL6Q;%L6Q;%L6Q;%L?/GSY +M\^6Q;%L6Q;%L6Q;%L6Q\^?/GSY;%L6Q;%L6Q;%L6Q;'SY\^?/EL6Q;%L6Q;% +ML6Q;%L?/GSY\^6Q;%L6Q;%L6Q;%L6Q\^?/GSY;%L6Q;%L6Q;%L6Q;'SY\^?/ +/EL6Q;%L6Q;%L6Q;%L?/@ +end +End_FRAME +return $frame; +} + +sub get_stereo_96000{ +my $frame = <<'End_FRAME'; +M__UD`#,SCDDD;;JJJJJOOOOOOOOOOOOOOOOEL6Q;%L=W6QW=W=W=W=W=W=;% +ML6Q;%L6Q;%L=W6QW=W=W=W=W=W=;%L6Q;%L6Q;%L=W6QW=W=W=W=W=W=;%L6 +MQ;%L6Q;%L=W6QW=W=W=W=W=W=;%L6Q;%L6Q;%L=W6QW=W=W=W=W=W=;%L6Q; +M%L6Q;%L=W6QW=W=W=W=W=W=;%L6Q;%L6Q;%L=W6QW=W=W=W=W=W=;%L6Q;%L +M6Q;%L=W6QW=W=W=W=W=W=;%L6Q;%L6Q;%L=W6QW=W=W=W=W=W=;%L6Q;%L6Q +M;%L=W6QW=W=W=W=W=W=;%L6Q;%L6Q;%L=W6QW=W=W=W=W=W=;%L6Q;%L6Q;% +2L=W6QW=W=W=W=W=W=;%L6Q;` +end +End_FRAME +return $frame; +} + +sub get_stereo_112000{ +my $frame = <<'End_FRAME'; +M__UT`!$B(C-#,S(B(B(1))````````````"JJJJJJJJOOOOOOOOOOOOOOOOO +MOOOOOOOOOFM;;;;;;;;;6QMMMMMM\^?/GSY\UK6M:UK;;;;;;;;:V-MMMMMO +MGSY\^?/FM:UK6M;;;;;;;;;6QMMMMMM\^?/GSY\UK6M:UK;;;;;;;;:V-MMM +MMMOGSY\^?/FM:UK6M;;;;;;;;;6QMMMMMM\^?/GSY\UK6M:UK;;;;;;;;:V- +MMMMMMOGSY\^?/FM:UK6M;;;;;;;;;6QMMMMMM\^?/GSY\UK6M:UK;;;;;;;; +M:V-MMMMMOGSY\^?/FM:UK6M;;;;;;;;;6QMMMMMM\^?/GSY\UK6M:UK;;;;; +M;;;:V-MMMMMOGSY\^?/FM:UK6M;;;;;;;;;6QMMMMMM\^?/GSY\UK6M:UK;; +5;;;;;;:V-MMMMMOGSY\^?/FM:UK0 +end +End_FRAME +return $frame; +} + +sub get_stereo_128000{ +my $frame = <<'End_FRAME'; +M__V$`"(B(D1$1#,R(B(B)))```````````"JJJJJJJJJ^^^^^^^^^^^^^^^^ +M^^^^^^^^^^^^;;;;;;;;;6Q;%L6Q;%L;;;;;Y\^?/GSYK6M:UMMMMMMMMM;% +ML6Q;%L6QMMMMOGSY\^?/FM:UK6VVVVVVVVUL6Q;%L6Q;&VVVV^?/GSY\^:UK +M6M;;;;;;;;;6Q;%L6Q;%L;;;;;Y\^?/GSYK6M:UMMMMMMMMM;%L6Q;%L6QMM +MMMOGSY\^?/FM:UK6VVVVVVVVUL6Q;%L6Q;&VVVV^?/GSY\^:UK6M;;;;;;;; +M;6Q;%L6Q;%L;;;;;Y\^?/GSYK6M:UMMMMMMMMM;%L6Q;%L6QMMMMOGSY\^?/ +MFM:UK6VVVVVVVVUL6Q;%L6Q;&VVVV^?/GSY\^:UK6M;;;;;;;;;6Q;%L6Q;% +ML;;;;;Y\^?/GSYK6M:UMMMMMMMMM;%L6Q;%L6QMMMMOGSY\^?/FM:UK6VVVV +8VVVVUL6Q;%L6Q;&VVVV^?/GSY\^:UK6M +end +End_FRAME +return $frame; +} + + +sub get_stereo_160000{ +my $frame = <<'End_FRAME'; +M__V4`"(R,U55541$0S,S;2))``````````"JJJJJJJJJK[[[[[[[[[[[[[[[ +M[[[[[[[[[[[[[[YMMMW=MN[N[N[N[N[N[N[N[K8MBV+8MC;;;;;;;;;;?/FM +M:UMMMW=MN[N[N[N[N[N[N[N[K8MBV+8MC;;;;;;;;;;?/FM:UMMMW=MN[N[N +M[N[N[N[N[N[K8MBV+8MC;;;;;;;;;;?/FM:UMMMW=MN[N[N[N[N[N[N[N[K8 +MMBV+8MC;;;;;;;;;;?/FM:UMMMW=MN[N[N[N[N[N[N[N[K8MBV+8MC;;;;;; +M;;;;?/FM:UMMMW=MN[N[N[N[N[N[N[N[K8MBV+8MC;;;;;;;;;;?/FM:UMMM +MW=MN[N[N[N[N[N[N[N[K8MBV+8MC;;;;;;;;;;?/FM:UMMMW=MN[N[N[N[N[ +MN[N[N[K8MBV+8MC;;;;;;;;;;?/FM:UMMMW=MN[N[N[N[N[N[N[N[K8MBV+8 +MMC;;;;;;;;;;?/FM:UMMMW=MN[N[N[N[N[N[N[N[K8MBV+8MC;;;;;;;;;;? +M/FM:UMMMW=MN[N[N[N[N[N[N[N[K8MBV+8MC;;;;;;;;;;?/FM:UMMMW=MN[ +>N[N[N[N[N[N[N[K8MBV+8MC;;;;;;;;;;?/FM:T` +end +End_FRAME +return $frame; +} + +sub get_stereo_192000{ +my $frame = <<'End_FRAME'; +M__VD`#,S,V9F9E551$1$DC21)`````````"JJJJJJJJJJOOOOOOOOOOOOOOO +MOOOOOOOOOOOOOOOOOG=W=W=W=W=W=WO>][WO>][WO>][W=W=W=W=UL6Q;%L6 +MQ;%L6Q;&V^?/FM:[N[N[N[N[N[N][WO>][WO>][WO>[N[N[N[NMBV+8MBV+8 +MMBV+8VWSY\UK7=W=W=W=W=W=WO>][WO>][WO>][W=W=W=W=UL6Q;%L6Q;%L6 +MQ;&V^?/FM:[N[N[N[N[N[N][WO>][WO>][WO>[N[N[N[NMBV+8MBV+8MBV+8 +MVWSY\UK7=W=W=W=W=W=WO>][WO>][WO>][W=W=W=W=UL6Q;%L6Q;%L6Q;&V^ +M?/FM:[N[N[N[N[N[N][WO>][WO>][WO>[N[N[N[NMBV+8MBV+8MBV+8VWSY\ +MUK7=W=W=W=W=W=WO>][WO>][WO>][W=W=W=W=UL6Q;%L6Q;%L6Q;&V^?/FM: +M[N[N[N[N[N[N][WO>][WO>][WO>[N[N[N[NMBV+8MBV+8MBV+8VWSY\UK7=W +M=W=W=W=W=WO>][WO>][WO>][W=W=W=W=UL6Q;%L6Q;%L6Q;&V^?/FM:[N[N[ +MN[N[N[N][WO>][WO>][WO>[N[N[N[NMBV+8MBV+8MBV+8VWSY\UK7=W=W=W= +MW=W=WO>][WO>][WO>][W=W=W=W=UL6Q;%L6Q;%L6Q;&V^?/FM:[N[N[N[N[N +D[N][WO>][WO>][WO>[N[N[N[NMBV+8MBV+8MBV+8VWSY\UK0 +end +End_FRAME +return $frame; +} + + +sub get_stereo_224000{ +my $frame = <<'End_FRAME'; +M__VT`#,S1'=W9F9F5555MD;2))````````"JJJJJJJJJJJ^^^^^^^^^^^^^^ +M^^^^^^^^^^^^^^^^^^^^=W=W=W=W>][WO????????????>][WO>][WO>][WO +M=W=W=W=W=W=W=W=W6Q;&VVWSYK6M=W=W=W=W>][WO????????????>][WO>] +M[WO>][WO=W=W=W=W=W=W=W=W6Q;&VVWSYK6M=W=W=W=W>][WO??????????? +M?>][WO>][WO>][WO=W=W=W=W=W=W=W=W6Q;&VVWSYK6M=W=W=W=W>][WO??? +M?????????>][WO>][WO>][WO=W=W=W=W=W=W=W=W6Q;&VVWSYK6M=W=W=W=W +M>][WO????????????>][WO>][WO>][WO=W=W=W=W=W=W=W=W6Q;&VVWSYK6M +M=W=W=W=W>][WO????????????>][WO>][WO>][WO=W=W=W=W=W=W=W=W6Q;& +MVVWSYK6M=W=W=W=W>][WO????????????>][WO>][WO>][WO=W=W=W=W=W=W +M=W=W6Q;&VVWSYK6M=W=W=W=W>][WO????????????>][WO>][WO>][WO=W=W +M=W=W=W=W=W=W6Q;&VVWSYK6M=W=W=W=W>][WO????????????>][WO>][WO> +M][WO=W=W=W=W=W=W=W=W6Q;&VVWSYK6M=W=W=W=W>][WO????????????>][ +MWO>][WO>][WO=W=W=W=W=W=W=W=W6Q;&VVWSYK6M=W=W=W=W>][WO??????? +M?????>][WO>][WO>][WO=W=W=W=W=W=W=W=W6Q;&VVWSYK6M=W=W=W=W>][W +JO????????????>][WO>][WO>][WO=W=W=W=W=W=W=W=W6Q;&VVWSYK6M +end +End_FRAME +return $frame; +} + +sub get_stereo_256000{ +my $frame = <<'End_FRAME'; +M__W$`$1$57=W=V9F9F9FVMD;:)````````"JJJJJJJJJJJ^^^^^^^^^^^^^^ +M^^^^^^^^^^^^^^^^^^^^>][WO>][WO????????????????????????>][WO> +M][WO>][WO>][WO>][WO>][WO=W=W6Q;&VVVV^:U[WO>][WO>]]]]]]]]]]]] +M]]]]]]]]]]]]][WO>][WO>][WO>][WO>][WO>][WO>]W=W=;%L;;;;;YK7O> +M][WO>][WWWWWWWWWWWWWWWWWWWWWWWWWO>][WO>][WO>][WO>][WO>][WO>] +M[W=W=UL6QMMMMOFM>][WO>][WO????????????????????????>][WO>][WO +M>][WO>][WO>][WO>][WO=W=W6Q;&VVVV^:U[WO>][WO>]]]]]]]]]]]]]]]] +M]]]]]]]]][WO>][WO>][WO>][WO>][WO>][WO>]W=W=;%L;;;;;YK7O>][WO +M>][WWWWWWWWWWWWWWWWWWWWWWWWWO>][WO>][WO>][WO>][WO>][WO>][W=W +M=UL6QMMMMOFM>][WO>][WO????????????????????????>][WO>][WO>][W +MO>][WO>][WO>][WO=W=W6Q;&VVVV^:U[WO>][WO>]]]]]]]]]]]]]]]]]]]] +M]]]]][WO>][WO>][WO>][WO>][WO>][WO>]W=W=;%L;;;;;YK7O>][WO>][W +MWWWWWWWWWWWWWWWWWWWWWWWWO>][WO>][WO>][WO>][WO>][WO>][W=W=UL6 +MQMMMMOFM>][WO>][WO????????????????????????>][WO>][WO>][WO>][ +MWO>][WO>][WO=W=W6Q;&VVVV^:U[WO>][WO>]]]]]]]]]]]]]]]]]]]]]]]] +M][WO>][WO>][WO>][WO>][WO>][WO>]W=W=;%L;;;;;YK7O>][WO>][WWWWW +MWWWWWWWWWWWWWWWWWWWWO>][WO>][WO>][WO>][WO>][WO>][W=W=UL6QMMM +#MOFM +end +End_FRAME +return $frame; +} + +sub get_stereo_320000{ +my $frame = <<'End_FRAME'; +M__W4`%559IB9B(AW=W=WVVVMDC)```````"JJJJJJJJJJJK[[[[[[[[[[[[[ +M[[[[[[[[[[[[[[[[[[[[[[Y]]]]]]]]]]]]^_?OW[]_?W]^_?O[^_O[^_OW[ +M]^_?OW[]^_?OOOOOOOOOOOOOOOOOOOOOOOOO>][WO>][WO>][WN[N[K8MBV- +MMK7WWWWWWWWWWWW[]^_?OW]_?W[]^_O[^_O[^_?OW[]^_?OW[]^^^^^^^^^^ +M^^^^^^^^^^^^^^^][WO>][WO>][WO>[N[NMBV+8VVM?????????????OW[]^ +M_?W]_?OW[^_O[^_O[]^_?OW[]^_?OW[[[[[[[[[[[[[[[[[[[[[[[[[WO>][ +MWO>][WO>][N[NZV+8MC;:U]]]]]]]]]]]]^_?OW[]_?W]^_?O[^_O[^_OW[] +M^_?OW[]^_?OOOOOOOOOOOOOOOOOOOOOOOOO>][WO>][WO>][WN[N[K8MBV-M +MK7WWWWWWWWWWWW[]^_?OW]_?W[]^_O[^_O[^_?OW[]^_?OW[]^^^^^^^^^^^ +M^^^^^^^^^^^^^^][WO>][WO>][WO>[N[NMBV+8VVM?????????????OW[]^_ +M?W]_?OW[^_O[^_O[]^_?OW[]^_?OW[[[[[[[[[[[[[[[[[[[[[[[[[WO>][W +MO>][WO>][N[NZV+8MC;:U]]]]]]]]]]]]^_?OW[]_?W]^_?O[^_O[^_OW[]^ +M_?OW[]^_?OOOOOOOOOOOOOOOOOOOOOOOOO>][WO>][WO>][WN[N[K8MBV-MK +M7WWWWWWWWWWWW[]^_?OW]_?W[]^_O[^_O[^_?OW[]^_?OW[]^^^^^^^^^^^^ +M^^^^^^^^^^^^^][WO>][WO>][WO>[N[NMBV+8VVM?????????????OW[]^_? +MW]_?OW[^_O[^_O[]^_?OW[]^_?OW[[[[[[[[[[[[[[[[[[[[[[[[[WO>][WO +M>][WO>][N[NZV+8MC;:U]]]]]]]]]]]]^_?OW[]_?W]^_?O[^_O[^_OW[]^_ +M?OW[]^_?OOOOOOOOOOOOOOOOOOOOOOOOO>][WO>][WO>][WN[N[K8MBV-MK7 +MWWWWWWWWWWWW[]^_?OW]_?W[]^_O[^_O[^_?OW[]^_?OW[]^^^^^^^^^^^^^ +M^^^^^^^^^^^^][WO>][WO>][WO>[N[NMBV+8VVM?????????????OW[]^_?W +M]_?OW[^_O[^_O[]^_?OW[]^_?OW[[[[[[[[[[[[[[[[[[[[[[[[[WO>][WO> +/][WO>][N[NZV+8MC;:T` +end +End_FRAME +return $frame; +} + + +sub get_stereo_384000 { +my $frame = <<'End_FRAME'; +M__WD`%5F9IF9F8B(AW=W_^VML;2```````"JJJJJJJJJJJK[[[[[[[[[[[[[ +M[[[[[[[[[[[[[[[[[[[[[[Y]]]]]]^_?OW[]^_?OW[]_?W]_?W]_?W]_?W]_ +M?W]_?W]^_?OW[]^_?OW[]^_?OOOOOOOOOOOOOOOO_^__[__O_^__[__O_^__ +M[__O>][WO>]W=W=W=;&VVWSY]]]]]]^_?OW[]^_?OW[]_?W]_?W]_?W]_?W] +M_?W]_?W]^_?OW[]^_?OW[]^_?OOOOOOOOOOOOOOOO_^__[__O_^__[__O_^_ +M_[__O>][WO>]W=W=W=;&VVWSY]]]]]]^_?OW[]^_?OW[]_?W]_?W]_?W]_?W +M]_?W]_?W]^_?OW[]^_?OW[]^_?OOOOOOOOOOOOOOOO_^__[__O_^__[__O_^ +M__[__O>][WO>]W=W=W=;&VVWSY]]]]]]^_?OW[]^_?OW[]_?W]_?W]_?W]_? +MW]_?W]_?W]^_?OW[]^_?OW[]^_?OOOOOOOOOOOOOOOO_^__[__O_^__[__O_ +M^__[__O>][WO>]W=W=W=;&VVWSY]]]]]]^_?OW[]^_?OW[]_?W]_?W]_?W]_ +M?W]_?W]_?W]^_?OW[]^_?OW[]^_?OOOOOOOOOOOOOOOO_^__[__O_^__[__O +M_^__[__O>][WO>]W=W=W=;&VVWSY]]]]]]^_?OW[]^_?OW[]_?W]_?W]_?W] +M_?W]_?W]_?W]^_?OW[]^_?OW[]^_?OOOOOOOOOOOOOOOO_^__[__O_^__[__ +MO_^__[__O>][WO>]W=W=W=;&VVWSY]]]]]]^_?OW[]^_?OW[]_?W]_?W]_?W +M]_?W]_?W]_?W]^_?OW[]^_?OW[]^_?OOOOOOOOOOOOOOOO_^__[__O_^__[_ +M_O_^__[__O>][WO>]W=W=W=;&VVWSY]]]]]]^_?OW[]^_?OW[]_?W]_?W]_? +MW]_?W]_?W]_?W]^_?OW[]^_?OW[]^_?OOOOOOOOOOOOOOOO_^__[__O_^__[ +M__O_^__[__O>][WO>]W=W=W=;&VVWSY]]]]]]^_?OW[]^_?OW[]_?W]_?W]_ +M?W]_?W]_?W]_?W]^_?OW[]^_?OW[]^_?OOOOOOOOOOOOOOOO_^__[__O_^__ +M[__O_^__[__O>][WO>]W=W=W=;&VVWSY]]]]]]^_?OW[]^_?OW[]_?W]_?W] +M_?W]_?W]_?W]_?W]^_?OW[]^_?OW[]^_?OOOOOOOOOOOOOOOO_^__[__O_^_ +M_[__O_^__[__O>][WO>]W=W=W=;&VVWSY]]]]]]^_?OW[]^_?OW[]_?W]_?W +M]_?W]_?W]_?W]_?W]^_?OW[]^_?OW[]^_?OOOOOOOOOOOOOOOO_^__[__O_^ +M__[__O_^__[__O>][WO>]W=W=W=;&VVWSY]]]]]]^_?OW[]^_?OW[]_?W]_? +MW]_?W]_?W]_?W]_?W]^_?OW[]^_?OW[]^_?OOOOOOOOOOOOOOOO_^__[__O_ +;^__[__O_^__[__O>][WO>]W=W=W=;&VVWSX` +end +End_FRAME +return $frame; +} + + +sub get_ac3_2_0_448000 { +my $frame = <<'End_FRAME'; +M"W>KMQY`0W_X2P:@N&'_.KY\^?/GSY\^?/GSY\^?/GSY\^?/GSY\^?/GSY\^ +M?/GSY\^?/GSY\^?/GSY\^?/GSY\^?/GSY\^?/GSY\^??_.KY\^?/GSY\^?/G +MSY\^?/GSY\^?/GSY\^?/GSY\^?/GSY\^?/GSY\^?/GSY\^?/GSY\^?/GSY\^ +M?/GSY\^?>4^D($`````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````````````````````````````````````````````````\>/ +M'CQX\>/'CQX````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````````````````````````````````````````````'CQX\>/ +M'CQX\>/#```````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M````````````````````````````````````````````````````'CQX\>/' +MCQX\>/`````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````````````````````````````````````````\>/'CQX\>/' +MCQX8```````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M````````````````````````````````````````````````\>/'CQX\>/'C +MQX`````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M````````````````````````````````````````````'CQX\>/'CQX\>/#` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````'CQX\>/'CQX\>/``` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M````````````````````````````````````````\>/'CQX\>/'CQX8`0>`7 +M9',I_X0C2.]?[C,4O>`````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M'CQX\>/'CQX\>/`````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````````````````````````````````````````````````\>/ +M'CQX\>/'CQX8`0X`1GEVD"Z"X6#I04H";@OIC#8Z]Q8X)BNY;28^@``````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M``````````````````````````````````!X\>/'CQX\>/'CP``````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +E```````````````````````````````/'CQX\>/'CQX\>``C>@`` +end +End_FRAME +return $frame; +} + +sub get_ac3_3_2_448000 { +my $frame = <<'End_FRAME'; +M"W>9&!Y`X=_^$L`^_UE_P\/X>'\/#^'A_#P55X^?/GSY\^?/GSY\^?/GSY\^ +M??\ZOGSY\^?/GSY\^?/GSY\^?/GSY\^?/GSY\^?/GSY\^?/GSY\^?/GSY\^? +M/GW_SJ^?/GSY\^?/GSY\^?/GSY\^?/GSY\^?/GSY\^?/GSY\^?/GSY\^?/GS +MY\^??_.KY\^?/GSY\^?/GSY\^?/GSY\^?/GSY\^?/GSY\^?/GSY\^?/GSY\^ +M?/GSY]_\ZOGSY\^?/GSY\^?/GSY\^?/GSY\^?/GSY\^?/GSY\^?/GSY\^?/G +MSY\^?/GW_SJ^?/GSY\^?/GSY\^?/GSY\^?/GSY\^?/GSY\^?/GSY\^?/GSY\ +M^?/GSY\^??_.N4^&)$B1(D2)``````&^;;;;;;;;>/'CN[N[N[N[N[N[N[N[ +MN[QX\;;;?/FM:UK6M:UK6M:UK6M:UK0````&VVVVVVVV\>/'=W=W=W=W=W=W +M=W=W=WCQXVVV^?-:UK6M:UK6M:UK6M:UK6@````-MMMMMMMMX\>.[N[N[N[N +M[N[N[N[N[O'CQMMM\^:UK6M:UK6M:UK6M:UK6M`````;YMMMMMMMMX\>.[N[ +MN[N[N[N[N[N[N[O'CQMMM\^:UK6M:UK6M:UK6M:UK6M`````;;;;;;;;;QX\ +M=W=W=W=W=W=W=W=W=W>/'C;;;Y\UK6M:UK6M:UK6M:UK6M:`````V#X````` +M```!OFVVVVVVVWCQX[N[N[N[N[N[N[N[N[N\>/&VVWSYK6M:UK6M:UK6M:UK +M6M:T````!MMMMMMMMO'CQW=W=W=W=W=W=W=W=W=X\>-MMOGS6M:UK6M:UK6M +M:UK6M:UH````#;;;;;;;;>/'CN[N[N[N[N[N[N[N[N[QX\;;;?/FM:UK6M:U +MK6M:UK6M:UK0````&^;;;;;;;;>/'CN[N[N[N[N[N[N[N[N[QX\;;;?/FM:U +MK6M:UK6M:UK6M:UK0````&VVVVVVVV\>/'=W=W=W=W=W=W=W=W=WCQXVVV^? +M-:UK6M:UK6M:UK6M:UK6@````-@^`````````;YMMMMMMMMX\>.[N[N[N[N[ +MN[N[N[N[O'CQMMM\^:UK6M:UK6M:UK6M:UK6M`````;;;;;;;;;QX\=W=W=W +M=W=W=W=W=W=W>/'C;;;Y\UK6M:UK6M:UK6M:UK6M:`````VVVVVVVVWCQX[N +M[N[N[N[N[N[N[N[N\>/&VVWSYK6M:UK6M:UK6M:UK6M:T````!OFVVVVVVVW +MCQX[N[N[N[N[N[N[N[N[N\>/&VVWSYK6M:UK6M:UK6M:UK6M:T````!MMMMM +MMMMO'CQW=W=W=W=W=W=W=W=W=X\>-MMOGS6M:UK6M:UK6M:UK6M:UH````#8 +M/@````````&^;;;;;;;;>/'CN[N[N[N[N[N[N[N[N[QX\;;;?/FM:UK6M:UK +M6M:UK6M:UK0````&VVVVVVVV\>/'=W=W=W=W=W=W=W=W=WCQXVVV^?-:UK6M +M:UK6M:UK6M:UK6@````-MMMMMMMMX\>.[N[N[N[N[N[N[N[N[O'CQMMM\^:U +MK6M:UK6M:UK6M:UK6M`````;YMMMMMMMMX\>.[N[N[N[N[N[N[N[N[O'CQMM +MM\^:UK6M:UK6M:UK6M:UK6M`````;;;;;;;;;QX\=W=W=W=W=W=W=W=W=W>/ +M'C;;;Y\UK6M:UK6M:UK6M:UK6M:`````V#X````````!OFVVVVVVVWCQX[N[ +MN[N[N[N[N[N[N[N\>/&VVWSYK6M:UK6M:UK6M:UK6M:T````!MMMMMMMMO'C +MQW=W=W=W=W=W=W=W=W=X\>-MMOGS6M:UK6M:UK6M:UK6M:UH````#;;;;;;; +M;>/'CN[N[N[N[N[N[N[N[N[QX\;;;?/FM:UK6M:UK6M:UK6M:UK0````&^;; +M;;;;;;>/'CN[N[N[N[N[N[N[N[N[QX\;;;?/FM:UK6M:UK6M:UK6M:UK0``` +M`&VVVVVVVV\>/'=W=W=W=W=W=W=W=W=WCQXVVV^?-:UK6M:UK6M:UK6M:UK6 +M@````-@^```!`@!=D/'C; +M;;Y\UK6M:UK6M:UK6M:UK6M:`````VVVVVVVVWCQX[N[N[N[N[N[N[N[N[N\ +M>/&VVWSYK6M:UK6M:UK6M:UK6M:T````!MMMMMMMMO'CQW=W=W=W=W=W=W=W +M=W=X\>-MMOGS6M:UK6M:UK6M:UK6M:UH````#?-MMMMMMMO'CQW=W=W=W=W= +MW=W=W=W=X\>-MMOGS6M:UK6M:UK6M:UK6M:UH````#;;;;;;;;>/'CN[N[N[ +EN[N[N[N[N[N[QX\;;;?/FM:UK6M:UK6M:UK6M:UK0````&PK$``` +end +End_FRAME +return $frame; +} + +} diff --git a/templates.c b/templates.c new file mode 100755 index 0000000..125ca0c --- /dev/null +++ b/templates.c @@ -0,0 +1,289 @@ +// templates.c + +#include +#include +#include + +#include "templates.h" +#include "codecs.h" +#include "a-tools.h" + + +cTemplate::cTemplate() { + C = NULL; + T = NULL; + TNames = NULL; + + C = new cCodecs(); + Load(); +} + +cTemplate::~cTemplate() { + for (int c = 0; c < NumTemplates; c++) {FREE(T[c].Name);} + FREE(T); + FREE(TNames); + + DELETE(C); +} + +void cTemplate::Load() { + char *buf = NULL; + size_t i = 0; + bool d = false; + int c = 0; + + FILE *f = fopen(AddDirectory(cPlugin::ConfigDirectory(), TEMPLATESFILE), "r"); + if (f) { + // read Template-Data + while (getline(&buf, &i, f) != -1) { + // reserve memory for templates + T = (struct TemplateData*)realloc(T, (c + 1) * sizeof(struct TemplateData)); + T[c].Name = strcol(buf, ";", 1); + T[c].FileSize = atoi(strcol(buf, ";", 2)); + T[c].FileNumbers = atoi(strcol(buf, ";", 3)); + T[c].BitrateVideo = atoi(strcol(buf, ";", 4)); + T[c].BitrateAudio = atoi(strcol(buf, ";", 5)); + T[c].VCodec = C->getNumVCodec(strcol(buf, ";", 6)); + T[c].ACodec = C->getNumACodec(strcol(buf, ";", 7)); + // migrate from version 0.0.9 + char *s = NULL; + s = strcol(buf, ";", 8); + if (s) {T[c].ScaleType = atoi(s); + } else {T[c].ScaleType = 0;} + FREE(s); + s = strcol(buf, ";", 9); + if (s) {T[c].Bpp = atoi(s); + } else {T[c].Bpp = 20;} + FREE(s); + // migrate from version 0.1.1 + s = strcol(buf, ";", 10); + if (s) {T[c].Container = C->getNumContainer(s); + } else {T[c].Container = C->getNumContainer("avi");} + FREE(s); + + // search for default Templates + if (strcmp(T[c].Name, TDEFAULT) == 0) {d = true;} + + FREE(buf); + c++; + } + fclose(f); + } else { + dsyslog("[vdrrip] could not open file %s !", TEMPLATESFILE); + isyslog("[vdrrip] try to create %s with default settings !", TEMPLATESFILE); + } + + NumTemplates = c; + + //create default Templates + if (! d) {New(TDEFAULT);} +} + +void cTemplate::Save() { + FILE *f; + + f = fopen(AddDirectory(cPlugin::ConfigDirectory(), TEMPLATESFILE), "w"); + if (f) { + for (int c = 0; c < NumTemplates; c++) { + if (strcmp(T[c].Name, "delete") != 0) { + fprintf(f, "%s;%i;%i;%i;%i;%s;%s;%i;%i;%s\n", T[c].Name, + T[c].FileSize, T[c].FileNumbers, T[c].BitrateVideo, + T[c].BitrateAudio, C->getVCodec(T[c].VCodec), + C->getACodec(T[c].ACodec), T[c].ScaleType, T[c].Bpp, + C->getContainer(T[c].Container)); + } + } + fclose(f); + Load(); + + } else { + dsyslog("[vdrrip] could not save %s !", TEMPLATESFILE); + } +} + +int cTemplate::getNumTemplates() {return NumTemplates;} + +int cTemplate::getNumTemplate(char *t) { + int c; + + if (t) { + for (c = 0; c < NumTemplates; c++) { + if (strcmp(T[c].Name, t) == 0) return c; + } + } + return -1; +} + +char **cTemplate::getTNames() { + int c; + + TNames = (char **)malloc(NumTemplates * sizeof(char *)); + for (c = 0; c < NumTemplates; c++) { + TNames[c] = T[c].Name; + } + return TNames; +} + +int cTemplate::New(char *n) { + T = (struct TemplateData*)realloc(T, ((NumTemplates + 1) * sizeof(struct TemplateData))); + T[NumTemplates].Name = strdup(n); + T[NumTemplates].FileSize = 700; + T[NumTemplates].FileNumbers = 1; + T[NumTemplates].BitrateVideo = -1; + T[NumTemplates].Container = C->getNumContainer("avi"); + T[NumTemplates].VCodec = C->getNumVCodec("lavc"); + T[NumTemplates].ACodec = C->getNumACodec("copy"); + T[NumTemplates].ScaleType = 0; + T[NumTemplates].Bpp = 20; + T[NumTemplates].BitrateAudio = 96; + + NumTemplates++; + Save(); + + return NumTemplates - 1; +} + +void cTemplate::Del(int i) { + if (i >= 0 && i < NumTemplates) { + isyslog("add delete flag on template %s", T[i].Name); + T[i].Name = strdup("delete"); + Save(); + } +} + +void cTemplate::setName(int i, char *n) { + if (i >= 0 && i < NumTemplates) { + T[i].Name = strdup(n); + Save(); + } +} + +void cTemplate::setFileSize(int i, int fs, int fn) { + if (i >= 0 && i < NumTemplates) { + T[i].FileSize = fs; + T[i].FileNumbers = fn; + T[i].BitrateVideo = -1; + Save(); + } +} + +void cTemplate::setBitrate(int i, int v, int a) { + if (i >= 0 && i < NumTemplates) { + T[i].BitrateVideo = v; + T[i].BitrateAudio = a; + if (! (T[i].BitrateVideo == -1)) {T[i].FileSize = -1;} + Save(); + } +} + +void cTemplate::setContainer(int i, int c) { + if (i >= 0 && i < NumTemplates) { + if (c >= 0 && c < C->getNumContainers()) {T[i].Container = c; + } else { + dsyslog("[vdrrip] unknown container, falling back to avi !"); + T[i].Container = C->getNumContainer("avi"); + } + Save(); + } +} + +void cTemplate::setCodecs(int i, int v, int a) { + if (i >= 0 && i < NumTemplates) { + // validate video codec + if (v >= 0 && v < C->getNumVCodecs()) {T[i].VCodec = v; + } else { + dsyslog("[vdrrip] unknown video codec, falling back to %s !", + C->getVCodec(0)); + T[i].VCodec = 0; + } + + // validate audio codec + if (a >= 0 && a < C->getNumACodecs()) { + if (strcmp(C->getContainer(T[i].Container), "avi") == 0 && strcmp(C->getACodec(a), "ogg-vorbis") == 0) { + dsyslog("[vdrrip] avi couldn't contain ogg-vorbis audio, falling back to copy !"); + T[i].ACodec = C->getNumACodec("copy"); + } else {T[i].ACodec = a;} + } else { + dsyslog("[vdrrip] unknown audio codec, falling back to copy !"), + T[i].ACodec = C->getNumACodec("copy"); + } + + Save(); + } +} + +void cTemplate::setBpp(int i, int b) { + if (i >= 0 && i < NumTemplates) { + T[i].Bpp = b; + Save(); + } +} + +void cTemplate::setScaleType(int i, int t) { + if (i >= 0 && i < NumTemplates) { + T[i].ScaleType = t; + Save(); + } +} + +char *cTemplate::getName(int i) { + if (i >= 0 && i < NumTemplates) {return T[i].Name; + } else {return NULL;} +} + +char *cTemplate::getShortname(int i) { + if (i >= 0 && i < NumTemplates) { + if (strlen(T[i].Name) > 20) { + char *s, *s1; + s = strsub(T[i].Name,1 , 17); + asprintf(&s1, "%s...", s); + return s1; + } else {return T[i].Name;} + } else {return NULL;} +} + +int cTemplate::getFileSize(int i) { + if (i >= 0 && i < NumTemplates) {return T[i].FileSize; + } else {return 0;} +} + +int cTemplate::getFileNumbers(int i) { + if (i >= 0 && i < NumTemplates) {return T[i].FileNumbers; + } else {return 0;} +} + +int cTemplate::getBitrateVideo(int i) { + if (i >= 0 && i < NumTemplates) {return T[i].BitrateVideo; + } else {return 0;} +} + +int cTemplate::getBitrateAudio(int i) { + if (i >= 0 && i < NumTemplates) {return T[i].BitrateAudio; + } else {return 0;} +} + +int cTemplate::getContainer(int i) { + if (i >= 0 && i < NumTemplates) {return T[i].Container; + } else {return 0;} +} + +int cTemplate::getVCodec(int i) { + if (i >= 0 && i < NumTemplates) {return T[i].VCodec; + } else {return 0;} +} + +int cTemplate::getACodec(int i) { + if (i >= 0 && i < NumTemplates) {return T[i].ACodec; + } else {return 0;} +} + +int cTemplate::getScaleType(int i) { + if (i >= 0 && i < NumTemplates) {return T[i].ScaleType; + } else {return 0;} +} + +int cTemplate::getBpp(int i) { + if (i >= 0 && i < NumTemplates) {return T[i].Bpp; + } else {return 1;} +} + diff --git a/templates.h b/templates.h new file mode 100755 index 0000000..616e8e2 --- /dev/null +++ b/templates.h @@ -0,0 +1,67 @@ +// +// templates.h +// + +#ifndef __TEMPLATES_H +#define __TEMPLATES_H + +#include "codecs.h" + +#define TEMPLATESFILE "templates.vdrrip" +#define TDEFAULT "default" + +struct TemplateData { + char *Name; + int FileSize; + int FileNumbers; + int Bitrate; + int BitrateVideo; + int BitrateAudio; + int Container; + int VCodec; + int ACodec; + int ScaleType; + int Bpp; +}; + +class cTemplate { + private: + struct TemplateData *T; + char **TNames; + int NumTemplates; + + void Load(); + + public: + cTemplate(); + ~cTemplate(); + + cCodecs *C; + + void Save(); + int New(char *n); + void Del(int i); + void setName(int i, char *n); + void setFileSize(int i, int fs, int fn); + void setBitrate(int i, int v, int a); + int getContainer(int i); + void setContainer(int i, int c); + void setCodecs(int i, int v, int a); + int getVCodec(int i); + int getACodec(int i); + void setBpp(int i, int b); + void setScaleType(int i, int t); + int getNumTemplate(char *n); + int getNumTemplates(); + char *getName(int i); + char *getShortname(int i); + char **getTNames(); + int getFileSize(int i); + int getFileNumbers(int i); + int getBitrateVideo(int i); + int getBitrateAudio(int i); + int getScaleType(int i); + int getBpp(int i); +}; + +#endif // __TEMPLATES_H diff --git a/vdrrip.c b/vdrrip.c new file mode 100755 index 0000000..69fbfee --- /dev/null +++ b/vdrrip.c @@ -0,0 +1,159 @@ +/* + * vdrrip.c: A plugin for the Video Disk Recorder + * + * See the README file for copyright information and how to reach the author. + * + * $Id$ + */ + +#include + +#include +#include +#include +#include "menu-vdrrip.h" +#include "movie.h" +#include "i18n.h" +#include "a-tools.h" + +static const char *VERSION = "0.3.0"; +static const char *DESCRIPTION = "A MPlayer using movie encoder"; +static const char *MAINMENUENTRY = "Vdrrip"; + +const char *MPlayer = "/usr/local/bin/mplayer"; +const char *MEncoder = "/usr/local/bin/mencoder"; +const char *DVD = "/dev/dvd"; + +class cPluginVdrrip : public cPlugin { +private: + // Add any member variables or functions you may need here. +public: + cPluginVdrrip(void); + virtual ~cPluginVdrrip(); + virtual const char *Version(void) { return VERSION; } + virtual const char *Description(void) { return DESCRIPTION; } + virtual const char *CommandLineHelp(void); + virtual bool ProcessArgs(int argc, char *argv[]); + virtual bool Initialize(void); + virtual bool Start(void); + virtual void Housekeeping(void); + virtual const char *MainMenuEntry(void) { return MAINMENUENTRY; } + virtual cOsdObject *MainMenuAction(void); + virtual cMenuSetupPage *SetupMenu(void); + virtual bool SetupParse(const char *Name, const char *Value); + }; + +cPluginVdrrip::cPluginVdrrip(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! +} + +cPluginVdrrip::~cPluginVdrrip() +{ + // Clean up after yourself! +} + +const char *cPluginVdrrip::CommandLineHelp(void) +{ + // Return a string that describes all known command line options. + char *s = NULL; + asprintf(&s, " -p LOC, --MPlayer=LOC use LOC as location of MPlayer\n" + " (default is %s)\n" + " -e LOC, --MEncoder=LOC use LOC as location of MEncoder\n" + " (default is %s)\n" +#ifdef VDRRIP_DVD + " -d DEV, --DVD=DEV use DEV as your DVD-device\n" + " (default is %s)\n" +#endif // VDRRIP_DVD + , MPlayer, MEncoder +#ifdef VDRRIP_DVD + , DVD +#endif // VDRRIP_DVD + ); + return s; +} + +bool cPluginVdrrip::ProcessArgs(int argc, char *argv[]) +{ + // Implement command line argument processing here if applicable. + static struct option long_options[] = { + { "MPlayer", required_argument, NULL, 'p' }, + { "MEncoder", required_argument, NULL, 'e' }, + { "DVD", required_argument, NULL, 'd' }, + { NULL } + }; + + int c, option_index = 0; + while ((c = getopt_long(argc, argv, "p:e:d:", long_options, &option_index)) != -1) { + switch (c) { + case 'p': + MPlayer = optarg; + break; + + case 'e': + MEncoder = optarg; + break; + + case 'd': + DVD = optarg; + break; + + default: + return false; + } + } + + return true; +} + +bool cPluginVdrrip::Initialize(void) +{ + // Initialize any background activities the plugin shall perform. + return true; +} + +bool cPluginVdrrip::Start(void) +{ + // Start any background activities the plugin shall perform. + RegisterI18n(Phrases); + return true; +} + +void cPluginVdrrip::Housekeeping(void) +{ + // Perform any cleanup or other regular tasks. +} + +cOsdObject *cPluginVdrrip::MainMenuAction(void) +{ + // Perform the action when selected from the main VDR menu. + if (access(MPlayer, X_OK) == -1) { + char *s = NULL; + asprintf(&s, "%s doesn't exist or isn't a executable !", MPlayer); + Interface->Error(s); + FREE(s); + return NULL; + } else if (access(MEncoder, X_OK) == -1) { + char *s = NULL; + asprintf(&s, "%s doesn't exist or isn't a executable !", MEncoder); + Interface->Error(s); + FREE(s); + return NULL; + } else return new cMenuVdrrip(); +} + +cMenuSetupPage *cPluginVdrrip::SetupMenu(void) +{ + // Return a setup menu in case the plugin supports one. + return new cMenuVdrripSetup(); +} + +bool cPluginVdrrip::SetupParse(const char *Name, const char *Value) +{ + // Parse your own setup parameters and store their values. + return VdrripSetup.SetupParse(Name, Value); +} + +VDRPLUGINCREATOR(cPluginVdrrip); // Don't touch this! diff --git a/vdrriprecordings.c b/vdrriprecordings.c new file mode 100755 index 0000000..b63a780 --- /dev/null +++ b/vdrriprecordings.c @@ -0,0 +1,87 @@ +// +// vdrriprecordings.c +// + +#include +#include +#include +#include + +#include "vdrriprecordings.h" +#include "a-tools.h" + +#define FINDRECCMD "find %s -follow -type d -regex '.*rec$'" + +// --- cVdrripRecordings ----------------------- + +cVdrripRecordings::cVdrripRecordings() { + Name = Date = Path = NULL; + NumRec = 0; + ReadRec(); +} + +cVdrripRecordings::~cVdrripRecordings() { + FREE(Name); + FREE(Date); + FREE(Path); +} + +void cVdrripRecordings::ReadRec() { + char *cmd = NULL, *buf = NULL; + size_t i = 0; + + int colv = strnumcol(VideoDirectory, "/"); + + asprintf(&cmd, FINDRECCMD, VideoDirectory); + FILE *p = popen(cmd, "r"); + if (p) { + while (getline(&buf, &i, p) != -1) { + int colg; + + // search the c from *.rec and terminate the string + int l = strlen(buf); + while (buf[l] != 'c') {l--;} + buf[l+1] = '\0'; + + // allocate memory for Name, Date & Path - arrays + Name = (char **)realloc(Name, ((NumRec + 1) * sizeof(char *))); + Date = (char **)realloc(Date, ((NumRec + 1) * sizeof(char *))); + Path = (char **)realloc(Path, ((NumRec + 1) * sizeof(char *))); + + colg = strnumcol(buf, "/"); + if ( colg - colv >= 3) { + // this is recording with a subdir + asprintf(&Name[NumRec], "%s_-_%s", strcol(buf, "/", colg - 2), strcol(buf, "/", colg - 1)); + } else { + Name[NumRec] = strcol(buf, "/", colg - 1); + } + + Date[NumRec] = strcol(strcol(buf, "/", colg), ".", 1); + Path[NumRec] = strdup(buf); + FREE(buf); + + NumRec++; + } + } else { + dsyslog("[vdrrip] could not open pipe to %s !", cmd); + } + pclose(p); + FREE(cmd); +} + +int cVdrripRecordings::getNumRec() {return NumRec;} + +char *cVdrripRecordings::getName(int i) { + if (i >= 0 && i < NumRec) {return Name[i]; + } else {return NULL;} +} + +char *cVdrripRecordings::getDate(int i) { + if (i >= 0 && i < NumRec) {return Date[i]; + } else {return NULL;} +} + +char *cVdrripRecordings::getPath(int i) { + if (i >= 0 && i < NumRec) {return Path[i]; + } else {return NULL;} +} diff --git a/vdrriprecordings.h b/vdrriprecordings.h new file mode 100755 index 0000000..6f22095 --- /dev/null +++ b/vdrriprecordings.h @@ -0,0 +1,27 @@ +// +// vdrriprecordings.h +// + +#ifndef __VDRRIPRECORDINGS_H +#define __VDRRIPRECORDINGS_H + +class cVdrripRecordings { + private: + char **Name; + char **Date; + char **Path; + + int NumRec; + + public: + cVdrripRecordings(); + ~cVdrripRecordings(); + + void ReadRec(); + int getNumRec(); + char *getName(int i); + char *getDate(int i); + char *getPath(int i); +}; + +#endif // __VDRRIPRECORDINGS_H diff --git a/xaver.jpg b/xaver.jpg new file mode 100755 index 0000000..3d1a250 Binary files /dev/null and b/xaver.jpg differ -- cgit v1.2.3