diff options
author | Daniel Meyerholt <dxm523@googlemail.com> | 2011-02-06 16:02:09 +0100 |
---|---|---|
committer | Daniel Meyerholt <dxm523@googlemail.com> | 2011-02-06 16:02:09 +0100 |
commit | a0674cc59c6aaad845ed0a48768fb56b90c41d52 (patch) | |
tree | be828ed0f61ebe85db7f1098e8e8942126b3e7fc | |
download | vdr-plugin-vdrrip-a0674cc59c6aaad845ed0a48768fb56b90c41d52.tar.gz vdr-plugin-vdrrip-a0674cc59c6aaad845ed0a48768fb56b90c41d52.tar.bz2 |
Init'd using http://www.a-land.de/vdr-vdrrip-0.3.0.tgz
-rwxr-xr-x | COPYING | 340 | ||||
-rwxr-xr-x | FAQ | 137 | ||||
-rwxr-xr-x | HISTORY | 114 | ||||
-rwxr-xr-x | INSTALL | 317 | ||||
-rwxr-xr-x | Makefile | 91 | ||||
-rwxr-xr-x | README | 118 | ||||
-rwxr-xr-x | TODO | 27 | ||||
-rwxr-xr-x | a-tools.c | 111 | ||||
-rwxr-xr-x | a-tools.h | 23 | ||||
-rwxr-xr-x | codecs.c | 200 | ||||
-rwxr-xr-x | codecs.h | 36 | ||||
-rwxr-xr-x | i18n.c | 1356 | ||||
-rwxr-xr-x | i18n.h | 10 | ||||
-rwxr-xr-x | menu-vdrrip.c | 1275 | ||||
-rwxr-xr-x | menu-vdrrip.h | 209 | ||||
-rwxr-xr-x | movie.c | 985 | ||||
-rwxr-xr-x | movie.h | 163 | ||||
-rwxr-xr-x | patches/MPlayer_vdrac3.diff | 38 | ||||
-rwxr-xr-x | queue.c | 289 | ||||
-rwxr-xr-x | queue.h | 69 | ||||
-rwxr-xr-x | scripts/queuehandler.sh | 1012 | ||||
-rwxr-xr-x | scripts/queuehandler.sh.conf | 71 | ||||
-rwxr-xr-x | scripts/sleephalt.sh | 83 | ||||
-rwxr-xr-x | scripts/vdrshutdown.sh | 24 | ||||
-rwxr-xr-x | scripts/vdrsync.pl | 2806 | ||||
-rwxr-xr-x | templates.c | 289 | ||||
-rwxr-xr-x | templates.h | 67 | ||||
-rwxr-xr-x | vdrrip.c | 159 | ||||
-rwxr-xr-x | vdrriprecordings.c | 87 | ||||
-rwxr-xr-x | vdrriprecordings.h | 27 | ||||
-rwxr-xr-x | xaver.jpg | bin | 0 -> 20042 bytes |
31 files changed, 10533 insertions, 0 deletions
@@ -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. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + 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. + + <signature of Ty Coon>, 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. @@ -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" <http://forum.doom9.org> + Forum: New AV/Formats - Containers) + + +Which deinterlacing-Filter should i use ? + + I don't know, but there are really interesting informations/pictures at + <http://home.knuut.de/MWieser_/vf_raw_ntv_3/>. + + +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. + - /<vdr-config-dir>/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. @@ -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 @@ -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 <http://ffmpeg.sourceforge.net>: + ----------------------------------------- + This open source divx-codec is shipped with MPlayer. It is very fast and + has a really good quality. Highly recommend ! + + + 1b) XviD <http://www.xvid.org>: + ------------------------------- + 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: <http://www.divx.com> + ----------------------------------- + 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 + <http://sourceforge.net/project/showfiles.php?group_id=11050>. Else you + can use the version 5.0.5 from <http://www.divx.com>. + + + +2) Audio-codecs: +================ + + 2a) LAME <http://lame.sourceforge.net>: + --------------------------------------- + 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 <http://lame.sourceforge.net/links.html#patents>. + 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 <http://www.vorbis.com>: + --------------------------------------- + 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 <http://www.voribs.com/download.psp>: + + - 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 <http://www.bunkus.org/videotools/ogmtools/index.html>: + --------------------------------------------------------------- + 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 <http://www.bunkus.org/videotools/mkvtoolnix/index.html>: + ---------------------------------------------------------------------- + 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 <http://ffmpeg.sourceforge.net>: + ------------------------------------------- + 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 <http://vdrsync.vdr-portal.de>: + ------------------------------------------- + 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 <http://www.mplayerhq.hu>: +===================================== + +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=<your path to the DVB-drivers>/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=<your path to the DVB-drivers>/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 +<http://dvd.sourceforge.net>: + + > 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 <path-to-vdrshutdown.sh>. 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* *~ @@ -0,0 +1,118 @@ +This is a "plugin" for the Video Disk Recorder (VDR). + +Written by: herbert attenberger <herbsl at a-land.de> + +Project's homepage: <http://www.a-land.de> + +Latest version available at: <http://www.a-land.de> + +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 <ok> on the title- +line, a more comfortable title-menu is opened. The last settings +are saved in the file /tmp/<DVD-NAME>.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 <vdr-config-dir>/PLUGINS. + + +usage of the quehandler-script: +=============================== + +queuehandler.sh queuefile tempdir + +The queuefile is the file queue.vdrrip in the dir <vdr-config-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 <vdr-config>/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 ... @@ -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 <herbsl@a-land.de> +*/ + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <vdr/plugin.h> + +#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 <herbsl@a-land.de> +*/ + +#ifndef __A_TOOLS_H +#define __A_TOOLS_H + +#include <stdio.h> + +#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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <vdr/plugin.h> + +#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 @@ -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 + }, + + { "<ok> for preview-mode", + "<ok> 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 } + }; @@ -0,0 +1,10 @@ +// i18n.h + +#ifndef _I18N__H +#define _I18N__H + +#include <vdr/i18n.h> + +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 <vdr/plugin.h> +#include <vdr/videodir.h> + +#ifdef VDRRIP_DVD + #include <dvdnav/ifo_read.h> +#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("<ok> 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 <vdr/osd.h> +#include <vdr/menuitems.h> + +#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 + @@ -0,0 +1,985 @@ +// +// movie.c +// + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <math.h> + +#ifdef VDRRIP_DVD + #include <dvdnav/ifo_read.h> +#endif //VDRRIP_DVD + +#include <vdr/plugin.h> + +#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 @@ -0,0 +1,163 @@ +/* movie.h */ + +#ifndef __MOVIE_H +#define __MOVIE_H + +#include <stdio.h> + +#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! @@ -0,0 +1,289 @@ +// +// queue.c +// + +#include <stdio.h> +#include <stdlib.h> +#include <vdr/plugin.h> + +#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; +} @@ -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 <herbsl@a-land.de> # +# # +# 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 <herbsl@a-land.de> +# + +# +# 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 <plugins-dir>/encode.vdrrip is deleted +# +# It is written by herbert attenberger <herbsl@a-land.de> +# + +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<R`````WS;;;;;;;;QX\=W=W=W=W=W=W=W=W=W>/'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 <stdio.h> +#include <stdlib.h> +#include <vdr/plugin.h> + +#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 <unistd.h> + +#include <getopt.h> +#include <vdr/plugin.h> +#include <vdr/menu.h> +#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 <stdio.h> +#include <stdlib.h> +#include <vdr/videodir.h> +#include <vdr/tools.h> + +#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 Binary files differnew file mode 100755 index 0000000..3d1a250 --- /dev/null +++ b/xaver.jpg |