diff options
-rw-r--r-- | COPYING | 340 | ||||
-rw-r--r-- | CREDITS | 116 | ||||
-rw-r--r-- | HBAc3Decoder.cpp | 130 | ||||
-rw-r--r-- | HBAc3Decoder.h | 29 | ||||
-rw-r--r-- | HBApp.cpp | 68 | ||||
-rw-r--r-- | HBApp.h | 23 | ||||
-rw-r--r-- | HBAviMuxer.cpp | 332 | ||||
-rw-r--r-- | HBAviMuxer.h | 149 | ||||
-rw-r--r-- | HBCommon.cpp | 566 | ||||
-rw-r--r-- | HBCommon.h | 146 | ||||
-rw-r--r-- | HBDVDReader.cpp | 67 | ||||
-rw-r--r-- | HBDVDReader.h | 23 | ||||
-rw-r--r-- | HBFifo.cpp | 161 | ||||
-rw-r--r-- | HBFifo.h | 61 | ||||
-rw-r--r-- | HBManager.cpp | 319 | ||||
-rw-r--r-- | HBManager.h | 55 | ||||
-rw-r--r-- | HBMp3Encoder.cpp | 115 | ||||
-rw-r--r-- | HBMp3Encoder.h | 30 | ||||
-rw-r--r-- | HBMpeg2Decoder.cpp | 168 | ||||
-rw-r--r-- | HBMpeg2Decoder.h | 27 | ||||
-rw-r--r-- | HBMpeg4Encoder.cpp | 88 | ||||
-rw-r--r-- | HBMpeg4Encoder.h | 27 | ||||
-rw-r--r-- | HBMpegDemux.cpp | 244 | ||||
-rw-r--r-- | HBMpegDemux.h | 38 | ||||
-rw-r--r-- | HBPictureWin.cpp | 288 | ||||
-rw-r--r-- | HBPictureWin.h | 52 | ||||
-rw-r--r-- | HBThread.cpp | 49 | ||||
-rw-r--r-- | HBThread.h | 26 | ||||
-rw-r--r-- | HBWindow.cpp | 630 | ||||
-rw-r--r-- | HBWindow.h | 62 | ||||
-rw-r--r-- | HandBrake.cpp | 18 | ||||
-rw-r--r-- | Jamfile | 14 | ||||
-rw-r--r-- | NEWS | 15 |
33 files changed, 4476 insertions, 0 deletions
diff --git a/COPYING b/COPYING new file mode 100644 index 000000000..d60c31a97 --- /dev/null +++ b/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <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. diff --git a/CREDITS b/CREDITS new file mode 100644 index 000000000..b8dee5f5b --- /dev/null +++ b/CREDITS @@ -0,0 +1,116 @@ +HandBrake uses a lot of cool libraries from the GNU/Linux world. Thank their authors ! + +libdvdcss authors : + +Billy Biggs +Stéphane Borel +Håkan Hjort +Samuel Hocevar +Eugenio Jarosiewicz +Jon Lech Johansen +Markus Kuespert +Pascal Levesque +Steven M. Schultz +David Siebörger +Alex Strelnikov +German Tischler +Gildas Bazin + +libdvdread authors : + +Björn Englund +Hâkan Hjort +Billy Biggs +Christian Wolff + +libdvdplay authors : + +Håkan Hjort +Martin Norbäck +Stéphane Borel + +mpeg2dec authors : + +Aaron Holtzman +Michel Lespinasse +Bruno Barreyra +Gildas Bazin +Alexander W. Chin +Stephen Crowley +Didier Gautheron +Ryan C. Gordon +Peter Gubanov +Hâkan Hjort +Nicolas Joly +Gerd Knorr +David I. Lehn +Olie Lho +Rick Niles +Real Ouellet +Bajusz Peter +Franck Sicard +Brion Vibber +Martin Vogt +Fredrik Vraalsen + +Ffmpeg authors : + +Fabrice Bellard +Alex Beregszaszi +Brian Foley +Arpad Gereoffy +Philip Gladstone +Falk Hueffner +Zdenek Kabelac +Nick Kurshev +Michael Niedermayer +François Revol +Dieter Shirley +Juan J. Sierralta +Lionel Ulmer + +a52dec authors : + +Aaron Holtzman +Michel Lespinasse +Gildas Bazin +Billy Biggs +Eduard Hasenleithner +Håkan Hjort +Charles M. Hannum +Chris Hodges +Michael Holzt +Angelos Keromytis +David I. Lehn +Don Mahurin +Jim Miller +Takefumi Sayo +Shoji Tokunaga + +lame authors : + +Mike Cheng +Robert Hegemann +Frank Klemm +Alexander Leidinger +Naoki Shibata +Mark Taylor +Takehiro Tominiga +Iván Cavero Belaunde +Gabriel Bouvigne +Florian Bomers +CISC +John Dahlstrom +John Dee +Albert Faber +Peter Gubanov +Lars Magne Ingebrigtsen +Yosi Markovich +Zdenek Kabelac +Iwasa Kazmi +Guillaume Lessard +Steve Lhomme +Don Melton +Viral Shah +Acy Stapp +Roel VdB diff --git a/HBAc3Decoder.cpp b/HBAc3Decoder.cpp new file mode 100644 index 000000000..81753d899 --- /dev/null +++ b/HBAc3Decoder.cpp @@ -0,0 +1,130 @@ +/* $Id: HBAc3Decoder.cpp,v 1.6 2003/08/24 15:03:41 titer Exp $ */ + +#include "HBCommon.h" +#include "HBAc3Decoder.h" +#include "HBManager.h" +#include "HBFifo.h" + +extern "C" { +#include <a52dec/a52.h> +} + +HBAc3Decoder::HBAc3Decoder( HBManager * manager, HBAudioInfo * audioInfo ) + : HBThread( "ac3decoder" ) +{ + fManager = manager; + fAudioInfo = audioInfo; + + fAc3Buffer = NULL; + fAc3Frame = NULL; + fPosInAc3Buffer = 0; +} + +void HBAc3Decoder::DoWork() +{ + /* Init liba52 */ + a52_state_t * state = a52_init( 0 ); + int inFlags = 0; + int outFlags = A52_STEREO; + float sampleLevel = 32768; /* lame wants samples from + -32768 to 32768 */ + + /* Max size for a A52 frame is 3840 bytes */ + fAc3Frame = new HBBuffer( 3840 ); + + int frameSize; + HBBuffer * rawBuffer; + sample_t * samples; + + /* Main loop */ + while( !fDie ) + { + fAc3Frame->fSize = 0; + + /* Get a frame header (7 bytes) */ + if( !( GetBytes( 7 ) ) ) + { + continue; + } + + /* Get the size of the current frame */ + frameSize = a52_syncinfo( fAc3Frame->fData, &inFlags, + &fAudioInfo->fInSampleRate, + &fAudioInfo->fInBitrate ); + + if( !frameSize ) + { + Log( "HBAc3Decoder : a52_syncinfo failed" ); + fManager->Error(); + break; + } + + /* Get the whole frame */ + if( !( GetBytes( (uint32_t) frameSize ) ) ) + { + continue; + } + + /* Feed liba52 */ + a52_frame( state, fAc3Frame->fData, &outFlags, &sampleLevel, 0 ); + + /* 6 blocks per frame, 256 samples per block */ + rawBuffer = new HBBuffer( 12 * 256 * sizeof( float ) ); + for( int i = 0; i < 6; i++ ) + { + /* Decode a block */ + a52_block( state ); + + /* Get a pointer to the raw data */ + samples = a52_samples( state ); + + /* Copy left channel data */ + memcpy( (float*) rawBuffer->fData + i * 256, + samples, + 256 * sizeof( float ) ); + + /* Copy right channel data */ + memcpy( (float*) rawBuffer->fData + ( 6 + i ) * 256, + samples + 256, + 256 * sizeof( float ) ); + } + + fAudioInfo->fRawFifo->Push( rawBuffer ); + } + + /* Clean up */ + delete fAc3Frame; +} + +/* GetBytes() : pops buffers from the AC3 fifo until fAc3Frame + contains <size> bytes */ +bool HBAc3Decoder::GetBytes( uint32_t size ) +{ + while( fAc3Frame->fSize < size ) + { + if( !fAc3Buffer ) + { + if( !( fAc3Buffer = fAudioInfo->fAc3Fifo->Pop() ) ) + { + return false; + } + fPosInAc3Buffer = 0; + } + + int willCopy = MIN( size - fAc3Frame->fSize, + fAc3Buffer->fSize - fPosInAc3Buffer ); + memcpy( fAc3Frame->fData + fAc3Frame->fSize, + fAc3Buffer->fData + fPosInAc3Buffer, + willCopy ); + fAc3Frame->fSize += willCopy; + fPosInAc3Buffer += willCopy; + + if( fAc3Buffer->fSize == fPosInAc3Buffer ) + { + delete fAc3Buffer; + fAc3Buffer = NULL; + } + } + + return true; +} diff --git a/HBAc3Decoder.h b/HBAc3Decoder.h new file mode 100644 index 000000000..75e7553da --- /dev/null +++ b/HBAc3Decoder.h @@ -0,0 +1,29 @@ +/* $Id: HBAc3Decoder.h,v 1.5 2003/08/24 15:03:41 titer Exp $ */ + +#ifndef HB_AC3_DECODER_H +#define HB_AC3_DECODER_H + +#include "HBThread.h" +class HBAudioInfo; +class HBBuffer; +class HBManager; + +class HBAc3Decoder : public HBThread +{ + public: + HBAc3Decoder( HBManager * manager, HBAudioInfo * audioInfo ); + + private: + void DoWork(); + bool GetBytes( uint32_t size ); + void PushSilence(); + + HBManager * fManager; + HBAudioInfo * fAudioInfo; + + HBBuffer * fAc3Buffer; + HBBuffer * fAc3Frame; + uint32_t fPosInAc3Buffer; +}; + +#endif diff --git a/HBApp.cpp b/HBApp.cpp new file mode 100644 index 000000000..142b16cb8 --- /dev/null +++ b/HBApp.cpp @@ -0,0 +1,68 @@ +/* $Id: HBApp.cpp,v 1.6 2003/08/24 20:25:49 titer Exp $ */ + +#include "HBCommon.h" +#include "HBApp.h" +#include "HBWindow.h" +#include "HBManager.h" + +/* Constructor */ +HBApp::HBApp() + : BApplication( "application/x-vnd.titer-handbrake" ) +{ + /* Initializations */ + fWindow = new HBWindow(); + fManager = new HBManager( fWindow ); + + /* Tell the interface we now have a manager */ + BMessage * message = new BMessage( MANAGER_CREATED ); + message->AddPointer( "manager", fManager ); + fWindow->PostMessage( message ); + delete message; + + /* Show the main window */ + fWindow->Show(); + + /* Check the available DVDs */ + Status( "Checking DVD volumes...", 0.0, ENABLE_DETECTING ); + fManager->PostMessage( DETECT_VOLUMES ); +} + +void HBApp::MessageReceived( BMessage * message ) +{ + switch( message->what ) + { + case PRINT_MESSAGE: + { + /* See Log() in HBCommon.cpp */ + char * string; + message->FindPointer( "string", (void**) &string ); + fprintf( stderr, string ); + free( string ); + break; + } + + case CHANGE_STATUS: + { + fWindow->PostMessage( message ); + break; + } + + default: + { + BApplication::MessageReceived( message ); + } + } +} + +bool HBApp::QuitRequested() +{ + if( fManager->Cancel() ) + { + /* We have log messages waiting, quit only after having + displayed them */ + PostMessage( B_QUIT_REQUESTED ); + return false; + } + + return true; +} diff --git a/HBApp.h b/HBApp.h new file mode 100644 index 000000000..0e92a9dee --- /dev/null +++ b/HBApp.h @@ -0,0 +1,23 @@ +/* $Id: HBApp.h,v 1.1.1.1 2003/06/24 13:43:48 titer Exp $ */ + +#ifndef _HB_APP_H +#define _HB_APP_H + +#include <Application.h> + +class HBWindow; +class HBManager; + +class HBApp : public BApplication +{ + public: + HBApp(); + virtual void MessageReceived( BMessage * message ); + virtual bool QuitRequested(); + + private: + HBWindow * fWindow; + HBManager * fManager; +}; + +#endif diff --git a/HBAviMuxer.cpp b/HBAviMuxer.cpp new file mode 100644 index 000000000..8c5d42176 --- /dev/null +++ b/HBAviMuxer.cpp @@ -0,0 +1,332 @@ +/* $Id: HBAviMuxer.cpp,v 1.21 2003/08/24 13:34:18 titer Exp $ */ + +#include "HBCommon.h" +#include "HBAviMuxer.h" +#include "HBManager.h" +#include "HBFifo.h" + +#define AVIF_HASINDEX 0x10 +#define AVIIF_KEYFRAME 0x10 + +HBAviMuxer::HBAviMuxer( HBManager * manager, HBTitleInfo * titleInfo, + HBAudioInfo * audio1Info, HBAudioInfo * audio2Info, + char * fileName ) + : HBThread( "avimuxer" ) +{ + fManager = manager; + fTitleInfo = titleInfo; + fAudio1Info = audio1Info; + fAudio2Info = audio2Info; + fFileName = strdup( fileName ); + + fRiffBytesCount = 2040; + fMoviBytesCount = 4; +} + +void HBAviMuxer::DoWork() +{ + /* Open the destination file */ + if( !( fFile = fopen( fFileName, "w" ) ) ) + { + Log( "HBAviMuxer: fopen failed" ); + fManager->Error(); + return; + } + + /* Initializations */ + memset( &fMainHeader, 0, sizeof( AviMainHeader ) ); + memset( &fVideoStreamHeader, 0, sizeof( AviStreamHeader ) ); + memset( &fAudio1StreamHeader, 0, sizeof( AviStreamHeader ) ); + memset( &fAudio2StreamHeader, 0, sizeof( AviStreamHeader ) ); + memset( &fVideoStreamFormat, 0, sizeof( BitmapInfo ) ); + memset( &fAudio1StreamFormat, 0, sizeof( WaveFormatEx ) ); + memset( &fAudio2StreamFormat, 0, sizeof( WaveFormatEx ) ); + + /* Alloc an 1 MB index (to be realloced later if needed) */ + fIndex = new HBBuffer( 1024 * 1024 ); + sprintf( (char*) fIndex->fData, "idx1" ); + fIndex->fSize = 8; + + /* Main loop */ + int video, audio1, audio2; + while( !fDie ) + { + /* Find the most filled fifo */ + video = fTitleInfo->fMpeg4Fifo->Size(); + audio1 = ( !fAudio1Info->fId ) ? 0 : fAudio1Info->fMp3Fifo->Size(); + audio2 = ( !fAudio2Info->fId ) ? 0 : fAudio2Info->fMp3Fifo->Size(); + + /* Nothing to get - wait a bit and try again */ + if( !video && !audio1 && !audio2 ) + { + snooze( 10000 ); + continue; + } + + /* Got something - mux it */ + if( video >= MAX( audio1, audio2 ) ) + { + AddVideoChunk(); + } + else if( audio1 >= audio2 ) + { + AddAudioChunk( 1 ); + } + else + { + AddAudioChunk( 2 ); + } + } + + /* Write the index */ + uint32_t size = fIndex->fSize - 8; + memcpy( fIndex->fData + 4, &size, 4 ); + fseek( fFile, 0, SEEK_END ); + fwrite( fIndex->fData, fIndex->fSize, 1, fFile ); + + /* Update the headers */ + fRiffBytesCount += fIndex->fSize; + fMainHeader.Flags |= AVIF_HASINDEX; + UpdateMainHeader(); + + delete fIndex; + + fclose( fFile ); +} + +bool HBAviMuxer::AddVideoChunk() +{ + HBBuffer * buffer = fTitleInfo->fMpeg4Fifo->Pop(); + if( !buffer ) + { + return false; + } + + fRiffBytesCount += 8 + EVEN( buffer->fSize ); + fMoviBytesCount += 8 + EVEN( buffer->fSize ); + + fMainHeader.MicroSecPerFrame = 1000000 * (uint64_t) fTitleInfo->fScale / + fTitleInfo->fRate; + fMainHeader.TotalFrames++; + fMainHeader.Width = fTitleInfo->fOutWidth; + fMainHeader.Height = fTitleInfo->fOutHeight; + + fVideoStreamHeader.FourCC = FOURCC( "strh" ); + fVideoStreamHeader.BytesCount = sizeof( AviStreamHeader ) - 8; + fVideoStreamHeader.Type = FOURCC( "vids" ); + fVideoStreamHeader.Handler = FOURCC( "DIVX" ); + fVideoStreamHeader.Scale = fTitleInfo->fScale; + fVideoStreamHeader.Rate = fTitleInfo->fRate; + fVideoStreamHeader.Length++; + + fVideoStreamFormat.FourCC = FOURCC( "strf" ); + fVideoStreamFormat.BytesCount = sizeof( BitmapInfo ) - 8; + fVideoStreamFormat.Size = sizeof( BitmapInfo ) - 8; + fVideoStreamFormat.Width = fTitleInfo->fOutWidth; + fVideoStreamFormat.Height = fTitleInfo->fOutHeight; + fVideoStreamFormat.Planes = 1; + fVideoStreamFormat.BitCount = 24; + fVideoStreamFormat.Compression = FOURCC( "DIVX" );; + + UpdateMainHeader(); + + fseek( fFile, 0, SEEK_END ); + + /* Update the index */ + if( fIndex->fSize + 16 > fIndex->fAllocSize ) + { + /* Realloc if needed */ + fIndex->ReAlloc( fIndex->fSize + 1024 * 1024 ); + } + + uint32_t flags = buffer->fKeyFrame ? AVIIF_KEYFRAME : 0; + uint32_t offset = ftell( fFile ) - 2044; + sprintf( (char*)fIndex->fData + fIndex->fSize, "00dc" ); + + memcpy( fIndex->fData + fIndex->fSize + 4, &flags, 4 ); + memcpy( fIndex->fData + fIndex->fSize + 8, &offset, 4 ); + memcpy( fIndex->fData + fIndex->fSize + 12, &buffer->fSize, 4 ); + fIndex->fSize += 16; + + /* Write the chunk */ + fwrite( "00dc", 4, 1, fFile ); + fwrite( &buffer->fSize, 4, 1, fFile ); + fwrite( buffer->fData, buffer->fSize, 1, fFile ); + + /* Chunks must be 2-bytes aligned */ + if( buffer->fSize & 1 ) + { + fputc( 0, fFile ); + } + + delete buffer; + + return true; +} + +bool HBAviMuxer::AddAudioChunk( int which ) +{ + HBAudioInfo * info; + AviStreamHeader * streamHeader; + WaveFormatEx * streamFormat; + + if( which == 1 ) + { + info = fAudio1Info; + streamHeader = &fAudio1StreamHeader; + streamFormat = &fAudio1StreamFormat; + } + else + { + info = fAudio2Info; + streamHeader = &fAudio2StreamHeader; + streamFormat = &fAudio2StreamFormat; + } + + HBBuffer * buffer = info->fMp3Fifo->Pop(); + if( !buffer ) + { + return false; + } + + fRiffBytesCount += 8 + EVEN( buffer->fSize ); + fMoviBytesCount += 8 + EVEN( buffer->fSize ); + + streamHeader->FourCC = FOURCC( "strh" ); + streamHeader->BytesCount = sizeof( AviStreamHeader ) - 8; + streamHeader->Type = FOURCC( "auds" ); + streamHeader->InitialFrames = 1; + streamHeader->Scale = 1152; + streamHeader->Rate = info->fOutSampleRate; + streamHeader->Length++; + streamHeader->Quality = 0xFFFFFFFF; + + + streamFormat->FourCC = FOURCC( "strf" ); + streamFormat->BytesCount = sizeof( WaveFormatEx ) - 8; + streamFormat->FormatTag = 0x55; + streamFormat->Channels = 2; + streamFormat->SamplesPerSec = info->fOutSampleRate; + streamFormat->AvgBytesPerSec = info->fOutBitrate * 1024 / 8; + streamFormat->BlockAlign = 1152; + + /* stolen from libavformat/wav.c */ + streamFormat->Size = 12; + streamFormat->Id = 1; + streamFormat->Flags = 2; + streamFormat->BlockSize = 1152; + streamFormat->FramesPerBlock = 1; + streamFormat->CodecDelay = 1393; + + UpdateMainHeader(); + + fseek( fFile, 0, SEEK_END ); + + /* Update the index */ + if( fIndex->fSize + 16 > fIndex->fAllocSize ) + { + /* Realloc if needed */ + fIndex->ReAlloc( fIndex->fSize + 1024 * 1024 ); + } + + uint32_t flags = buffer->fKeyFrame ? AVIIF_KEYFRAME : 0; + uint32_t offset = ftell( fFile ) - 2044; + sprintf( (char*)fIndex->fData + fIndex->fSize, "%02dwb", which ); + + memcpy( fIndex->fData + fIndex->fSize + 4, &flags, 4 ); + memcpy( fIndex->fData + fIndex->fSize + 8, &offset, 4 ); + memcpy( fIndex->fData + fIndex->fSize + 12, &buffer->fSize, 4 ); + fIndex->fSize += 16; + + /* Write the chunk */ + fprintf( fFile, "%02dwb", which ); + fwrite( &buffer->fSize, 4, 1, fFile ); + fwrite( buffer->fData, buffer->fSize, 1, fFile ); + + /* Chunks must be 2-bytes aligned */ + if( buffer->fSize & 1 ) + { + fputc( 0, fFile ); + } + + delete buffer; + + return true; +} + +void HBAviMuxer::UpdateMainHeader() +{ + fMainHeader.FourCC = FOURCC( "avih" ); + fMainHeader.BytesCount = sizeof( AviMainHeader ) - 8; + fMainHeader.Streams = 2; + + fHdrlBytesCount = 4 + sizeof( AviMainHeader ) + + 12 + sizeof( AviStreamHeader ) + sizeof( BitmapInfo ); + + if( fAudio1Info->fId ) + { + fHdrlBytesCount += 12 + sizeof( AviStreamHeader ) + sizeof( WaveFormatEx ); + } + if( fAudio2Info->fId ) + { + fHdrlBytesCount += 12 + sizeof( AviStreamHeader ) + sizeof( WaveFormatEx ); + } + + fseek( fFile, 0, SEEK_SET ); + fwrite( "RIFF", 4, 1, fFile ); + fwrite( &fRiffBytesCount, 4, 1, fFile ); + fwrite( "AVI ", 4, 1, fFile ); + fwrite( "LIST", 4, 1, fFile ); + fwrite( &fHdrlBytesCount, 4, 1, fFile ); + fwrite( "hdrl", 4, 1, fFile ); + + fwrite( &fMainHeader, sizeof( AviMainHeader ), 1, fFile ); + + int strlSize; + strlSize = 4 + sizeof( AviStreamHeader ) + sizeof( BitmapInfo ); + fwrite( "LIST", 4, 1, fFile ); + fwrite( &strlSize, 4, 1, fFile ); + fwrite( "strl", 4, 1, fFile ); + fwrite( &fVideoStreamHeader, sizeof( AviStreamHeader ), 1, fFile ); + fwrite( &fVideoStreamFormat, sizeof( fVideoStreamFormat ), 1, fFile ); + + if( fAudio1Info->fId ) + { + strlSize = 4 + sizeof( AviStreamHeader ) + sizeof( WaveFormatEx ); + fwrite( "LIST", 4, 1, fFile ); + fwrite( &strlSize, 4, 1, fFile ); + fwrite( "strl", 4, 1, fFile ); + fwrite( &fAudio1StreamHeader, sizeof( AviStreamHeader ), 1, fFile ); + fwrite( &fAudio1StreamFormat, sizeof( WaveFormatEx ), 1, fFile ); + } + + if( fAudio2Info->fId ) + { + strlSize = 4 + sizeof( AviStreamHeader ) + sizeof( WaveFormatEx ); + fwrite( "LIST", 4, 1, fFile ); + fwrite( &strlSize, 4, 1, fFile ); + fwrite( "strl", 4, 1, fFile ); + fwrite( &fAudio2StreamHeader, sizeof( AviStreamHeader ), 1, fFile ); + fwrite( &fAudio2StreamFormat, sizeof( WaveFormatEx ), 1, fFile ); + } + + /* a JUNK chunk to fill the free space. + size = 2048 -/ + 12 ("RIFFxxxxAVI ") - + 8 (hdrl's "LIS1Txxxx") - + fHdrlBytesCount - + 8 ("JUNKxxxx") - + 12 ("LISTxxxxmovi) */ + int junkSize = 2008 - fHdrlBytesCount; + fwrite( "JUNK", 4, 1, fFile ); + fwrite( &junkSize, 4, 1, fFile ); + for( uint32_t i = 0; i < 2008 - fHdrlBytesCount; i++ ) + { + fputc( 0, fFile ); + } + + /* movi list */ + fwrite( "LIST", 4, 1, fFile ); + fwrite( &fMoviBytesCount, 4, 1, fFile ); + fwrite( "movi", 4, 1, fFile ); +} diff --git a/HBAviMuxer.h b/HBAviMuxer.h new file mode 100644 index 000000000..55c2dc248 --- /dev/null +++ b/HBAviMuxer.h @@ -0,0 +1,149 @@ +/* $Id: HBAviMuxer.h,v 1.8 2003/08/23 16:20:59 titer Exp $ */ + +#ifndef HB_AVI_MUXER_H +#define HB_AVI_MUXER_H + +#include "HBThread.h" +class HBManager; +class HBFifo; +class HBBuffer; +class HBAudioInfo; +class HBTitleInfo; + +#define FOURCC(a) \ + ( ( a[3] << 24 ) | ( a[2] << 16 ) | ( a[1] << 8 ) | a[0] ) + +/* Misc structures used in AVI headers */ +typedef struct __attribute__((__packed__)) BitmapInfo +{ + uint32_t FourCC; + uint32_t BytesCount; + uint32_t Size; + uint32_t Width; + uint32_t Height; + uint16_t Planes; + uint16_t BitCount; + uint32_t Compression; + uint32_t SizeImage; + uint32_t XPelsPerMeter; + uint32_t YPelsPerMeter; + uint32_t ClrUsed; + uint32_t ClrImportant; + uint8_t Blue; + uint8_t Green; + uint8_t Red; + uint8_t Reserved; +} BitmapInfo; + +typedef struct __attribute__((__packed__)) WaveFormatEx +{ + uint32_t FourCC; + uint32_t BytesCount; + uint16_t FormatTag; + uint16_t Channels; + uint32_t SamplesPerSec; + uint32_t AvgBytesPerSec; + uint16_t BlockAlign; + uint16_t BitsPerSample; + uint16_t Size; + + /* mp3 specific */ + uint16_t Id; + uint32_t Flags; + uint16_t BlockSize; + uint16_t FramesPerBlock; + uint16_t CodecDelay; +} WaveFormatEx; + +typedef struct __attribute__((__packed__)) AviStreamHeader +{ + uint32_t FourCC; + uint32_t BytesCount; + uint32_t Type; + uint32_t Handler; + uint32_t Flags; + uint16_t Priority; + uint16_t Language; + uint32_t InitialFrames; + uint32_t Scale; + uint32_t Rate; + uint32_t Start; + uint32_t Length; + uint32_t SuggestedBufferSize; + uint32_t Quality; + uint32_t SampleSize; + int16_t Left; + int16_t Top; + int16_t Right; + int16_t Bottom; +} AviStreamHeader; + +typedef struct __attribute__((__packed__)) AviMainHeader +{ + uint32_t FourCC; + uint32_t BytesCount; + uint32_t MicroSecPerFrame; + uint32_t MaxBytesPerSec; + uint32_t PaddingGranularity; + uint32_t Flags; + uint32_t TotalFrames; + uint32_t InitialFrames; + uint32_t Streams; + uint32_t SuggestedBufferSize; + uint32_t Width; + uint32_t Height; + uint32_t Reserved[4]; +} AviMainHeader; + +typedef struct AviOldIndexEntry +{ + char StreamNb[2]; + char Code[2]; + uint32_t Flags; + uint32_t Offset; + uint32_t Size; +} AviOldIndexEntry; + +class HBAviMuxer : public HBThread +{ + public: + HBAviMuxer( HBManager * manager, HBTitleInfo * titleInfo, + HBAudioInfo * audio1Info, HBAudioInfo * audio2Info, + char * fileName ); + + private: + void DoWork(); + bool AddVideoChunk(); + bool AddAudioChunk( int which ); + void UpdateMainHeader(); + + HBManager * fManager; + HBTitleInfo * fTitleInfo; + HBAudioInfo * fAudio1Info; + HBAudioInfo * fAudio2Info; + char * fFileName; + + FILE * fFile; + + /* The main header */ + AviMainHeader fMainHeader; + + /* The video track */ + AviStreamHeader fVideoStreamHeader; + BitmapInfo fVideoStreamFormat; + + /* The audio tracks */ + AviStreamHeader fAudio1StreamHeader; + WaveFormatEx fAudio1StreamFormat; + AviStreamHeader fAudio2StreamHeader; + WaveFormatEx fAudio2StreamFormat; + + uint32_t fRiffBytesCount; + uint32_t fHdrlBytesCount; + uint32_t fMoviBytesCount; + + HBBuffer * fIndex; + +}; + +#endif diff --git a/HBCommon.cpp b/HBCommon.cpp new file mode 100644 index 000000000..e0e17a43b --- /dev/null +++ b/HBCommon.cpp @@ -0,0 +1,566 @@ +/* $Id: HBCommon.cpp,v 1.5 2003/08/24 20:25:49 titer Exp $ */ + +#include "HBCommon.h" +#include "HBFifo.h" +#include "HBMpegDemux.h" +#include "HBPictureWin.h" +#include "HBWindow.h" + +#include <Application.h> + +#include <dvdread/ifo_types.h> +#include <dvdplay/dvdplay.h> +#include <dvdplay/info.h> +#include <dvdplay/state.h> +#include <dvdplay/nav.h> + +extern "C" { +#include <mpeg2dec/mpeg2.h> +} + +void Log( char * log, ... ) +{ + char * string = (char*) malloc( 1024 ); + + /* Show the time */ + time_t _now = time( NULL ); + struct tm * now = localtime( &_now ); + sprintf( string, "[%02d:%02d:%02d] ", + now->tm_hour, now->tm_min, now->tm_sec ); + + /* Convert the message to a string */ + va_list args; + va_start( args, log ); + int ret = vsnprintf( string + 11, 1011, log, args ); + va_end( args ); + + /* Add the end of line */ + string[ret+11] = '\n'; + string[ret+12] = '\0'; + + /* Send this to the be_app */ + /* We do this so we are sure that only one message is printed + at a time */ + BMessage * message = new BMessage( PRINT_MESSAGE ); + message->AddPointer( "string", string ); + be_app->PostMessage( message ); + delete message; +} + +void Status( char * text, float pos, int mode ) +{ + char * textCopy = strdup( text ); + BMessage * message = new BMessage( CHANGE_STATUS ); + message->AddPointer( "text", textCopy ); + message->AddFloat( "pos", pos ); + message->AddInt32( "mode", mode ); + be_app->PostMessage( message ); + delete message; +} + +/* Get a readable language description from the code */ +iso639_lang_t languages[] = +{ { "Afar", "", "aa" }, + { "Abkhazian", "", "ab" }, + { "Afrikaans", "", "af" }, + { "Albanian", "", "sq" }, + { "Amharic", "", "am" }, + { "Arabic", "", "ar" }, + { "Armenian", "", "hy" }, + { "Assamese", "", "as" }, + { "Avestan", "", "ae" }, + { "Aymara", "", "ay" }, + { "Azerbaijani", "", "az" }, + { "Bashkir", "", "ba" }, + { "Basque", "", "eu" }, + { "Belarusian", "", "be" }, + { "Bengali", "", "bn" }, + { "Bihari", "", "bh" }, + { "Bislama", "", "bi" }, + { "Bosnian", "", "bs" }, + { "Breton", "", "br" }, + { "Bulgarian", "", "bg" }, + { "Burmese", "", "my" }, + { "Catalan", "", "ca" }, + { "Chamorro", "", "ch" }, + { "Chechen", "", "ce" }, + { "Chinese", "", "zh" }, + { "Church Slavic", "", "cu" }, + { "Chuvash", "", "cv" }, + { "Cornish", "", "kw" }, + { "Corsican", "", "co" }, + { "Czech", "", "cs" }, + { "Danish", "Dansk", "da" }, + { "Dutch", "Nederlands", "nl" }, + { "Dzongkha", "", "dz" }, + { "English", "English", "en" }, + { "Esperanto", "", "eo" }, + { "Estonian", "", "et" }, + { "Faroese", "", "fo" }, + { "Fijian", "", "fj" }, + { "Finnish", "Suomi", "fi" }, + { "French", "Francais", "fr" }, + { "Frisian", "", "fy" }, + { "Georgian", "", "ka" }, + { "German", "Deutsch", "de" }, + { "Gaelic (Scots)", "", "gd" }, + { "Irish", "", "ga" }, + { "Gallegan", "", "gl" }, + { "Manx", "", "gv" }, + { "Greek, Modern ()", "", "el" }, + { "Guarani", "", "gn" }, + { "Gujarati", "", "gu" }, + { "Hebrew", "", "he" }, + { "Herero", "", "hz" }, + { "Hindi", "", "hi" }, + { "Hiri Motu", "", "ho" }, + { "Hungarian", "Magyar", "hu" }, + { "Icelandic", "Islenska", "is" }, + { "Inuktitut", "", "iu" }, + { "Interlingue", "", "ie" }, + { "Interlingua", "", "ia" }, + { "Indonesian", "", "id" }, + { "Inupiaq", "", "ik" }, + { "Italian", "Italiano", "it" }, + { "Javanese", "", "jv" }, + { "Japanese", "", "ja" }, + { "Kalaallisut (Greenlandic)", "", "kl" }, + { "Kannada", "", "kn" }, + { "Kashmiri", "", "ks" }, + { "Kazakh", "", "kk" }, + { "Khmer", "", "km" }, + { "Kikuyu", "", "ki" }, + { "Kinyarwanda", "", "rw" }, + { "Kirghiz", "", "ky" }, + { "Komi", "", "kv" }, + { "Korean", "", "ko" }, + { "Kuanyama", "", "kj" }, + { "Kurdish", "", "ku" }, + { "Lao", "", "lo" }, + { "Latin", "", "la" }, + { "Latvian", "", "lv" }, + { "Lingala", "", "ln" }, + { "Lithuanian", "", "lt" }, + { "Letzeburgesch", "", "lb" }, + { "Macedonian", "", "mk" }, + { "Marshall", "", "mh" }, + { "Malayalam", "", "ml" }, + { "Maori", "", "mi" }, + { "Marathi", "", "mr" }, + { "Malay", "", "ms" }, + { "Malagasy", "", "mg" }, + { "Maltese", "", "mt" }, + { "Moldavian", "", "mo" }, + { "Mongolian", "", "mn" }, + { "Nauru", "", "na" }, + { "Navajo", "", "nv" }, + { "Ndebele, South", "", "nr" }, + { "Ndebele, North", "", "nd" }, + { "Ndonga", "", "ng" }, + { "Nepali", "", "ne" }, + { "Norwegian", "Norsk", "no" }, + { "Norwegian Nynorsk", "", "nn" }, + { "Norwegian Bokmål", "", "nb" }, + { "Chichewa; Nyanja", "", "ny" }, + { "Occitan (post 1500); Provençal", "", "oc" }, + { "Oriya", "", "or" }, + { "Oromo", "", "om" }, + { "Ossetian; Ossetic", "", "os" }, + { "Panjabi", "", "pa" }, + { "Persian", "", "fa" }, + { "Pali", "", "pi" }, + { "Polish", "", "pl" }, + { "Portuguese", "Portugues", "pt" }, + { "Pushto", "", "ps" }, + { "Quechua", "", "qu" }, + { "Raeto-Romance", "", "rm" }, + { "Romanian", "", "ro" }, + { "Rundi", "", "rn" }, + { "Russian", "", "ru" }, + { "Sango", "", "sg" }, + { "Sanskrit", "", "sa" }, + { "Serbian", "", "sr" }, + { "Croatian", "Hrvatski", "hr" }, + { "Sinhalese", "", "si" }, + { "Slovak", "", "sk" }, + { "Slovenian", "", "sl" }, + { "Northern Sami", "", "se" }, + { "Samoan", "", "sm" }, + { "Shona", "", "sn" }, + { "Sindhi", "", "sd" }, + { "Somali", "", "so" }, + { "Sotho, Southern", "", "st" }, + { "Spanish", "Espanol", "es" }, + { "Sardinian", "", "sc" }, + { "Swati", "", "ss" }, + { "Sundanese", "", "su" }, + { "Swahili", "", "sw" }, + { "Swedish", "Svenska", "sv" }, + { "Tahitian", "", "ty" }, + { "Tamil", "", "ta" }, + { "Tatar", "", "tt" }, + { "Telugu", "", "te" }, + { "Tajik", "", "tg" }, + { "Tagalog", "", "tl" }, + { "Thai", "", "th" }, + { "Tibetan", "", "bo" }, + { "Tigrinya", "", "ti" }, + { "Tonga (Tonga Islands)", "", "to" }, + { "Tswana", "", "tn" }, + { "Tsonga", "", "ts" }, + { "Turkish", "", "tr" }, + { "Turkmen", "", "tk" }, + { "Twi", "", "tw" }, + { "Uighur", "", "ug" }, + { "Ukrainian", "", "uk" }, + { "Urdu", "", "ur" }, + { "Uzbek", "", "uz" }, + { "Vietnamese", "", "vi" }, + { "Volapük", "", "vo" }, + { "Welsh", "", "cy" }, + { "Wolof", "", "wo" }, + { "Xhosa", "", "xh" }, + { "Yiddish", "", "yi" }, + { "Yoruba", "", "yo" }, + { "Zhuang", "", "za" }, + { "Zulu", "", "zu" }, + { NULL, NULL, NULL } }; + +char * LanguageForCode( int code ) +{ + char codeString[2]; + codeString[0] = ( code >> 8 ) & 0xFF; + codeString[1] = code & 0xFF; + + iso639_lang_t * lang; + for( lang = languages; lang->engName; lang++ ) + { + if( !strncmp( lang->iso639_1, codeString, 2 ) ) + { + if( *lang->nativeName ) + return lang->nativeName; + + return lang->engName; + } + } + + return "Unknown"; +} + +HBVolumeInfo::HBVolumeInfo( char * name, char * device ) + : BMenuItem( "", new BMessage( VOLUME_SELECTED ) ) +{ + fInitOK = false; + fName = strdup( name ); + fDevice = strdup( device ); + fTitleList = new BList(); + + SetLabel( fName ); + + /* Open the device */ + dvdplay_ptr vmg; + vmg = dvdplay_open( device, NULL, NULL ); + if( !vmg ) + { + Log( "VolumeInfo::DetectTitles: dvdplay_open() failed" ); + return; + } + + /* Detect titles */ + HBTitleInfo * titleInfo; + for( int i = 0; i < dvdplay_title_nr( vmg ); i++ ) + { + Log( "HBVolumeInfo : new title (%d)", i + 1 ); + + char statusText[128]; memset( statusText, 0, 128 ); + snprintf( statusText, 128, + "Checking DVD volumes (%s, title %d)...", + fName, i + 1 ); + Status( statusText, 0.0, ENABLE_DETECTING ); + + titleInfo = new HBTitleInfo( vmg, i + 1, device ); + + if( !titleInfo->InitCheck() ) + { + delete titleInfo; + continue; + } + + fTitleList->AddItem( titleInfo ); + } + + dvdplay_close( vmg ); + + if( fTitleList->CountItems() > 0 ) + { + fInitOK = true; + } + +} + +HBVolumeInfo::~HBVolumeInfo() +{ + free( fName ); + free( fDevice ); + + HBTitleInfo * titleInfo; + while( ( titleInfo = (HBTitleInfo*) fTitleList->ItemAt( 0 ) ) ) + { + fTitleList->RemoveItem( titleInfo ); + delete titleInfo; + } + delete fTitleList; +} + +bool HBVolumeInfo::InitCheck() +{ + return fInitOK; +} + +HBTitleInfo::HBTitleInfo( dvdplay_ptr vmg, int index, char * device ) + : BMenuItem( "", new BMessage( TITLE_SELECTED ) ) +{ + fInitOK = false; + fDevice = strdup( device ); + fIndex = index; + + fAudioInfoList1 = new BList(); + fAudioInfoList2 = new BList(); + fPSFifo = NULL; + fMpeg2Fifo = NULL; + fRawFifo = NULL; + fMpeg4Fifo = NULL; + + dvdplay_start( vmg, fIndex ); + + /* Length */ + fLength = dvdplay_title_time( vmg ); + + /* Discard titles under 60 seconds */ + if( fLength < 60 ) + { + Log( "HBTitleInfo : skipping title %d (length = %lld sec)", + index, fLength ); + return; + } + + char label[1024]; memset( label, 0, 1024 ); + sprintf( label, "Title %d (%02lld:%02lld:%02lld)", + index, fLength / 3600, ( fLength % 3600 ) / 60, + fLength % 60 ); + SetLabel( label ); + + /* Detect languages */ + int audio_nr, audio; + dvdplay_audio_info( vmg, &audio_nr, &audio ); + + audio_attr_t * attr; + HBAudioInfo * audioInfo; + for( int i = 0; i < audio_nr; i++ ) + { + int id = dvdplay_audio_id( vmg, i ); + if( id > 0 ) + { + Log( "HBTitleInfo : new language (%x)", id ); + attr = dvdplay_audio_attr( vmg, i ); + audioInfo = new HBAudioInfo( id, LanguageForCode( attr->lang_code ) ); + fAudioInfoList1->AddItem( audioInfo ); + } + } + + /* Discard titles with no audio tracks */ + if( !fAudioInfoList1->CountItems() ) + { + Log( "HBTitleInfo : skipping title %d (no audio track)", index ); + return; + } + + /* Add a dummy 'None' language */ + audioInfo = new HBAudioInfo( 0, "None" ); + fAudioInfoList1->AddItem( audioInfo ); + + /* Duplicate the audio list */ + for( int i = 0; i < fAudioInfoList1->CountItems(); i++ ) + { + audioInfo = (HBAudioInfo*) fAudioInfoList1->ItemAt( i ); + fAudioInfoList2->AddItem( new HBAudioInfo( audioInfo ) ); + } + + /* Decode a few pictures so the user can crop/resize it */ + int titleFirst = dvdplay_title_first ( vmg ); + int titleEnd = dvdplay_title_end( vmg ); + + /* Kludge : libdvdplay wants we to read a first block before seeking */ + uint8_t dummyBuf[2048]; + dvdplay_read( vmg, dummyBuf, 1 ); + + for( int i = 0; i < 10; i++ ) + { + dvdplay_seek( vmg, ( i + 1 ) * ( titleEnd - titleFirst ) / 11 ) ; + if( !DecodeFrame( vmg, i ) ) + { + Log( "HBTitleInfo::HBTitleInfo : could not decode frame %d", i ); + return; + } + } + + fPictureWin = new HBPictureWin( this ); + + fInitOK = true; +} + +HBTitleInfo::~HBTitleInfo() +{ + HBAudioInfo * audioInfo; + + while( ( audioInfo = (HBAudioInfo*) fAudioInfoList1->ItemAt( 0 ) ) ) + { + fAudioInfoList1->RemoveItem( audioInfo ); + delete audioInfo; + } + delete fAudioInfoList1; + + while( ( audioInfo = (HBAudioInfo*) fAudioInfoList2->ItemAt( 0 ) ) ) + { + fAudioInfoList2->RemoveItem( audioInfo ); + delete audioInfo; + } + delete fAudioInfoList2; +} + +bool HBTitleInfo::DecodeFrame( dvdplay_ptr vmg, int i ) +{ + /* Init libmpeg2 */ + mpeg2dec_t * handle = mpeg2_init(); + const mpeg2_info_t * info = mpeg2_info( handle ); + mpeg2_state_t state; + + BList * esBufferList = NULL; + HBBuffer * psBuffer = NULL; + HBBuffer * esBuffer = NULL; + + for( ;; ) + { + state = mpeg2_parse( handle ); + + if( state == STATE_BUFFER ) + { + /* Free the previous buffer */ + if( esBuffer ) + { + delete esBuffer; + esBuffer = NULL; + } + + /* Get a new one */ + while( !esBuffer ) + { + while( !esBufferList ) + { + psBuffer = new HBBuffer( DVD_VIDEO_LB_LEN ); + if( dvdplay_read( vmg, psBuffer->fData, 1 ) != 1 ) + { + Log( "dvdplay_read failed" ); + } + esBufferList = PStoES( psBuffer ); + } + + esBuffer = (HBBuffer*) esBufferList->ItemAt( 0 ); + esBufferList->RemoveItem( esBuffer ); + if( !esBufferList->CountItems() ) + { + delete esBufferList; + esBufferList = NULL; + } + + if( esBuffer->fStreamId != 0xE0 ) + { + delete esBuffer; + esBuffer = NULL; + } + } + + /* Feed libmpeg2 */ + mpeg2_buffer( handle, esBuffer->fData, + esBuffer->fData + esBuffer->fSize ); + } + else if( state == STATE_SEQUENCE ) + { + /* Get size & framerate info */ + fInWidth = info->sequence->width; + fInHeight = info->sequence->height; + fPixelWidth = info->sequence->pixel_width; + fPixelHeight = info->sequence->pixel_height; + fPictures[i] = (uint8_t*) malloc( 3 * fInWidth * fInHeight / 2 ); + fRate = 27000000; + fScale = info->sequence->frame_period; + } + else if( ( state == STATE_SLICE || state == STATE_END ) && + ( info->display_fbuf ) && + ( info->display_picture->flags & PIC_MASK_CODING_TYPE ) + == PIC_FLAG_CODING_TYPE_I ) + { + /* Copy it */ + /* TODO : make libmpeg2 write directly in our buffer */ + memcpy( fPictures[i], + info->display_fbuf->buf[0], + fInWidth * fInHeight ); + memcpy( fPictures[i] + fInWidth * fInHeight, + info->display_fbuf->buf[1], + fInWidth * fInHeight / 4 ); + memcpy( fPictures[i] + 5 * fInWidth * fInHeight / 4, + info->display_fbuf->buf[2], + fInWidth * fInHeight / 4 ); + break; + } + else if( state == STATE_INVALID ) + { + /* Reset libmpeg2 */ + mpeg2_close( handle ); + handle = mpeg2_init(); + } + } + + mpeg2_close( handle ); + + return true; +} + +bool HBTitleInfo::InitCheck() +{ + return fInitOK; +} + +/* Audio track */ +HBAudioInfo::HBAudioInfo( int id, char * description ) + : BMenuItem( "", new BMessage( LANGUAGE_SELECTED ) ) +{ + fId = id; + fOutSampleRate = 44100; + + fAc3Fifo = NULL; + fRawFifo = NULL; + fMp3Fifo = NULL; + + SetLabel( description ); +} + +HBAudioInfo::HBAudioInfo( HBAudioInfo * audioInfo ) + : BMenuItem( "", new BMessage( LANGUAGE_SELECTED ) ) +{ + fId = audioInfo->fId; + fInSampleRate = audioInfo->fInSampleRate; + fOutSampleRate = audioInfo->fOutSampleRate; + fInBitrate = audioInfo->fInBitrate; + fOutBitrate = audioInfo->fOutBitrate; + + fAc3Fifo = NULL; + fRawFifo = NULL; + fMp3Fifo = NULL; + + SetLabel( audioInfo->Label() ); +} + +HBAudioInfo::~HBAudioInfo() +{ +} diff --git a/HBCommon.h b/HBCommon.h new file mode 100644 index 000000000..3e84c3dc5 --- /dev/null +++ b/HBCommon.h @@ -0,0 +1,146 @@ +/* $Id: HBCommon.h,v 1.9 2003/08/24 19:28:18 titer Exp $ */ + +#ifndef _HB_COMMON_H +#define _HB_COMMON_H + +/* standard headers */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <inttypes.h> +typedef uint8_t byte_t; + +/* BeOS headers */ +#include <Looper.h> +#include <MenuItem.h> + +/* Internal headers */ +class HBFifo; +class HBPictureWin; + +/* Misc structures */ +typedef struct dvdplay_s * dvdplay_ptr; +typedef struct iso639_lang_t +{ + char * engName; /* Description in English */ + char * nativeName; /* Description in native language */ + char * iso639_1; /* ISO-639-1 (2 characters) code */ +} iso639_lang_t; + +/* BMessages */ +#define MANAGER_CREATED 'macr' +#define PRINT_MESSAGE 'prme' +#define DETECT_VOLUMES 'devo' +#define START_CONVERT 'stac' +#define STOP_CONVERT 'stoc' +#define SUSPEND_CONVERT 'suco' +#define RESUME_CONVERT 'reco' +#define VOLUMES_DETECTED 'vode' +#define REFRESH_VOLUMES 'revo' +#define VIDEO_SLIDER 'visl' +#define AUDIO_SLIDER 'ausl' +#define PICTURE_WIN 'piwi' +#define NOT_IMPLEMENTED 'noim' +#define VOLUME_SELECTED 'vose' +#define TITLE_SELECTED 'tise' +#define LANGUAGE_SELECTED 'lase' +#define CHANGE_STATUS 'chst' + +/* Handy macros */ +#define EVEN( a ) ( ( (a) & 0x1 ) ? ( (a) + 1 ) : (a) ) +#define MULTIPLE_16( a ) ( ( ( (a) % 16 ) < 8 ) ? ( (a) - ( (a) % 16 ) ) \ + : ( (a) - ( (a) % 16 ) + 16 ) ) + +/* Global prototypes */ +void Log( char * log, ... ); +void Status( char * text, float pos, int mode ); +char * LanguageForCode( int code ); + +/* Possible modes in Status() */ +#define ENABLE_DETECTING 0x1 +#define ENABLE_READY 0x2 +#define ENABLE_ENCODING 0x4 + +/* Classes */ + +class HBAudioInfo : public BMenuItem +{ + public: + /* Common methods and members */ + HBAudioInfo( int id, char * description ); + HBAudioInfo( HBAudioInfo * audioInfo ); + ~HBAudioInfo(); + + uint32_t fId; + HBFifo * fAc3Fifo; + HBFifo * fRawFifo; + HBFifo * fMp3Fifo; + + int fInSampleRate; + int fOutSampleRate; + int fInBitrate; + int fOutBitrate; +}; + +class HBTitleInfo : public BMenuItem +{ + public: + HBTitleInfo( dvdplay_ptr vmg, int index, char * device ); + ~HBTitleInfo(); + bool InitCheck(); + + bool fInitOK; + char * fDevice; + int fIndex; + uint64_t fLength; + + /* MPEG2-PS data */ + HBFifo * fPSFifo; + + /* Video info */ + bool DecodeFrame( dvdplay_ptr vmg, int i ); + + HBFifo * fMpeg2Fifo; + HBFifo * fRawFifo; + HBFifo * fMpeg4Fifo; + + /* Video input */ + uint32_t fInWidth; + uint32_t fInHeight; + uint32_t fPixelWidth; + uint32_t fPixelHeight; + uint32_t fRate; + uint32_t fScale; + + /* Video output */ + bool fDeinterlace; + uint32_t fOutWidth; + uint32_t fOutHeight; + uint32_t fTopCrop; + uint32_t fBottomCrop; + uint32_t fLeftCrop; + uint32_t fRightCrop; + uint32_t fBitrate; + + uint8_t * fPictures[10]; + HBPictureWin * fPictureWin; + + /* Audio infos */ + BList * fAudioInfoList1; + BList * fAudioInfoList2; +}; + +class HBVolumeInfo : public BMenuItem +{ + public: + HBVolumeInfo( char * name, char * device ); + ~HBVolumeInfo(); + bool InitCheck(); + + bool fInitOK; + char * fName; + char * fDevice; + BList * fTitleList; +}; + +#endif diff --git a/HBDVDReader.cpp b/HBDVDReader.cpp new file mode 100644 index 000000000..2027da7e6 --- /dev/null +++ b/HBDVDReader.cpp @@ -0,0 +1,67 @@ +/* $Id: HBDVDReader.cpp,v 1.7 2003/08/12 20:10:50 titer Exp $ */ + +#include "HBCommon.h" +#include "HBDVDReader.h" +#include "HBManager.h" +#include "HBFifo.h" + +#include <Application.h> + +#include <dvdread/ifo_types.h> +#include <dvdplay/dvdplay.h> +#include <dvdplay/info.h> +#include <dvdplay/state.h> +#include <dvdplay/nav.h> + +HBDVDReader::HBDVDReader( HBManager * manager, + HBTitleInfo * titleInfo ) + : HBThread( "dvdreader", B_NORMAL_PRIORITY ) +{ + fManager = manager; + fTitleInfo = titleInfo; +} + +void HBDVDReader::DoWork() +{ + /* Open the device */ + dvdplay_ptr vmg; + vmg = dvdplay_open( fTitleInfo->fDevice, NULL, NULL ); + if( !vmg ) + { + Log( "HBDVDReader: dvdplay_open() failed" ); + fManager->Error(); + return; + } + + /* Open the title */ + dvdplay_start( vmg, fTitleInfo->fIndex ); + + /* Read */ + HBBuffer * dvdBuffer; + int beginPosition = dvdplay_position( vmg ); + int endPosition = dvdplay_title_end( vmg ); + while( dvdplay_position( vmg ) < endPosition ) + { + dvdBuffer = new HBBuffer( DVD_VIDEO_LB_LEN ); + dvdBuffer->fPosition = (float) ( dvdplay_position( vmg ) - beginPosition ) / + (float) ( endPosition - beginPosition ) ; + + if( dvdplay_read( vmg, dvdBuffer->fData, 1 ) < 0 ) + { + Log( "HBDVDReader: could not dvdplay_read()" ); + delete dvdBuffer; + fManager->Error(); + break; + } + if( !( fTitleInfo->fPSFifo->Push( dvdBuffer ) ) ) + { + break; + } + } + + if( dvdplay_position( vmg ) == dvdplay_title_end( vmg ) ) + fManager->Done(); + + /* Clean up */ + dvdplay_close( vmg ); +} diff --git a/HBDVDReader.h b/HBDVDReader.h new file mode 100644 index 000000000..55db069a8 --- /dev/null +++ b/HBDVDReader.h @@ -0,0 +1,23 @@ +/* $Id: HBDVDReader.h,v 1.3 2003/08/12 20:10:50 titer Exp $ */ + +#ifndef HB_DVD_READER_H +#define HB_DVD_READER_H + +#include "HBThread.h" +class HBManager; +class HBTitleInfo; +class HBFifo; + +class HBDVDReader : public HBThread +{ + public: + HBDVDReader( HBManager * manager, HBTitleInfo * titleInfo ); + + private: + void DoWork(); + + HBManager * fManager; + HBTitleInfo * fTitleInfo; +}; + +#endif diff --git a/HBFifo.cpp b/HBFifo.cpp new file mode 100644 index 000000000..fd79f734a --- /dev/null +++ b/HBFifo.cpp @@ -0,0 +1,161 @@ +/* $Id: HBFifo.cpp,v 1.10 2003/08/24 13:27:41 titer Exp $ */ + +#include "HBCommon.h" +#include "HBFifo.h" + +#include <Locker.h> + +HBBuffer::HBBuffer( int size ) +{ + fAllocSize = size; + fSize = size; + fKeyFrame = false; + fData = (uint8_t*) malloc( size ); + + if( !fData ) + { + Log( "HBBuffer::HBBuffer() : malloc() failed, gonna crash soon" ); + } +} + +HBBuffer::~HBBuffer() +{ + free( fData ); +} + +void HBBuffer::ReAlloc( int size ) +{ + realloc( fData, size ); + + if( !fData ) + { + Log( "HBBuffer::ReAlloc() : realloc() failed, gonna crash soon" ); + } + + fAllocSize = size; +} + +/* Constructor */ +HBFifo::HBFifo( int capacity ) +{ + fCapacity = capacity; + + fWhereToPush = 0; + fWhereToPop = 0; + fBuffers = (HBBuffer**) malloc( ( fCapacity + 1 ) * sizeof( void* ) ); + fLocker = new BLocker(); + fDie = false; +} + +void HBFifo::Die() +{ + Lock(); + + /* Empty the fifo */ + while( fWhereToPush != fWhereToPop ) + { + HBBuffer * buffer = fBuffers[fWhereToPop]; + fWhereToPop++; + fWhereToPop %= ( fCapacity + 1 ); + delete buffer; + } + + fDie = true; + + Unlock(); +} + +HBFifo::~HBFifo() +{ + /* Empty the fifo */ + while( fWhereToPush != fWhereToPop ) + { + HBBuffer * buffer = fBuffers[fWhereToPop]; + fWhereToPop++; + fWhereToPop %= ( fCapacity + 1 ); + delete buffer; + } + + /* Cleaning */ + free( fBuffers ); + delete fLocker; +} + +/* Size() : returns how much the fifo is currently filled */ +int HBFifo::Size() +{ + return ( fCapacity + 1 + fWhereToPush - fWhereToPop ) % + ( fCapacity + 1 ); +} + +/* Capacity() : simply returns the fifo capacity... */ +int HBFifo::Capacity() +{ + return fCapacity; +} + +/* Push() : add a packet to the fifo. If the fifo is full, it blocks + until the packet can be added. Returns true when it is successful, + or false if the fifo has been destroyed before we could add it */ +bool HBFifo::Push( HBBuffer * buffer ) +{ + bool success = false; + + while( !fDie ) + { + Lock(); + if( Size() < fCapacity ) + { + fBuffers[fWhereToPush] = buffer; + fWhereToPush++; + fWhereToPush %= ( fCapacity + 1 ); + Unlock(); + success = true; + break; + } + Unlock(); + snooze( 10000 ); + } + + if( !success ) + { + delete buffer; + } + + return success; +} + +/* Pop() : get the first packet if the fifo. If the fifo is empty, it + blocks until a packet comes. Returns true when it is successful, + or false if the fifo has been destroyed before we could get a packet */ +HBBuffer * HBFifo::Pop() +{ + while( !fDie ) + { + Lock(); + if( fWhereToPush != fWhereToPop ) + { + HBBuffer * buffer = fBuffers[fWhereToPop]; + fWhereToPop++; + fWhereToPop %= ( fCapacity + 1 ); + Unlock(); + return buffer; + } + Unlock(); + snooze( 10000 ); + } + + return NULL; +} + +/* Lock() : private function */ +void HBFifo::Lock() +{ + fLocker->Lock(); +} + +/* Unlock() : private function */ +void HBFifo::Unlock() +{ + fLocker->Unlock(); +} diff --git a/HBFifo.h b/HBFifo.h new file mode 100644 index 000000000..a46d46158 --- /dev/null +++ b/HBFifo.h @@ -0,0 +1,61 @@ +/* $Id: HBFifo.h,v 1.9 2003/08/24 13:27:41 titer Exp $ */ + +#ifndef _HB_FIFO_H +#define _HB_FIFO_H + +#define DVD_DATA 0x01 +#define MPEG2_VIDEO 0x02 +#define RAW_VIDEO 0x04 +#define RAW2_VIDEO 0x08 +#define MPEG4_VIDEO 0x10 +#define AC3_AUDIO 0x20 +#define RAW_AUDIO 0x40 +#define MP3_AUDIO 0x80 + +class BLocker; + +class HBBuffer +{ + public: + /* Common functions */ + HBBuffer( int size ); + ~HBBuffer(); + void ReAlloc( int size ); + + /* Common members */ + uint32_t fAllocSize; + uint32_t fSize; + uint8_t * fData; + + /* Misc */ + float fPosition; + uint32_t fStreamId; + bool fKeyFrame; + uint64_t fPTS; +}; + +class HBFifo +{ + public: + HBFifo( int capacity ); + void Die(); + ~HBFifo(); + + int Size(); + int Capacity(); + bool Push( HBBuffer * buffer ); + HBBuffer * Pop(); + + private: + void Lock(); + void Unlock(); + + int fCapacity; + int fWhereToPush; + int fWhereToPop; + HBBuffer ** fBuffers; + BLocker * fLocker; + volatile bool fDie; +}; + +#endif diff --git a/HBManager.cpp b/HBManager.cpp new file mode 100644 index 000000000..159d52348 --- /dev/null +++ b/HBManager.cpp @@ -0,0 +1,319 @@ +/* $Id: HBManager.cpp,v 1.27 2003/08/24 20:25:49 titer Exp $ */ + +#include "HBCommon.h" +#include "HBManager.h" +#include "HBWindow.h" +#include "HBFifo.h" +#include "HBDVDReader.h" +#include "HBMpegDemux.h" +#include "HBMpeg2Decoder.h" +#include "HBMpeg4Encoder.h" +#include "HBAc3Decoder.h" +#include "HBMp3Encoder.h" +#include "HBAviMuxer.h" + +#include <Directory.h> +#include <Drivers.h> +#include <Path.h> +#include <Query.h> +#include <String.h> +#include <VolumeRoster.h> + +#include <fs_info.h> +#include <sys/ioctl.h> + +/* Public methods */ + +HBManager::HBManager( HBWindow * window ) + : BLooper( "manager" ) +{ + fWindow = window; + fFifoList = new BList(); + fThreadList = new BList(); + fVolumeList = new BList(); + + Run(); +} + +HBManager::~HBManager() +{ + delete fFifoList; + delete fThreadList; + delete fVolumeList; +} + +void HBManager::MessageReceived( BMessage * message ) +{ + switch( message->what ) + { + case DETECT_VOLUMES: + { + DetectVolumes(); + break; + } + + default: + BLooper::MessageReceived( message ); + } +} + +void HBManager::SetPosition( float position ) +{ + if( position - fPosition < 0.0001 ) + /* No need to be more precise ;) */ + return; + + fPosition = position; + + char statusText[128]; memset( statusText, 0, 128 ); + sprintf( statusText, "Encoding : %.2f %% - %.2f fps (average : %.2f fps)", + 100 * fPosition, fCurrentFrameRate, fAverageFrameRate ); + Status( statusText, fPosition, ENABLE_ENCODING ); +} + +void HBManager::SetFrameRate( float current, float average ) +{ + fCurrentFrameRate = current; + fAverageFrameRate = average; + + char statusText[128]; memset( statusText, 0, 128 ); + sprintf( statusText, "Encoding : %.2f %% - %.2f fps (average : %.2f fps)", + 100 * fPosition, fCurrentFrameRate, fAverageFrameRate ); + Status( statusText, fPosition, ENABLE_ENCODING ); +} + +void HBManager::Start( HBVolumeInfo * volumeInfo, + HBTitleInfo * titleInfo, + HBAudioInfo * audio1Info, + HBAudioInfo * audio2Info, + char * file ) +{ + fPosition = 0; + fCurrentFrameRate = 0; + fAverageFrameRate = 0; + + /* Remember the fifos that should be freezed in Stop() */ + fFifoList->AddItem( ( titleInfo->fPSFifo = new HBFifo( 1024 ) ) ); + fFifoList->AddItem( ( titleInfo->fMpeg2Fifo = new HBFifo( 5 ) ) ); + fFifoList->AddItem( ( titleInfo->fRawFifo = new HBFifo( 5 ) ) ); + fFifoList->AddItem( ( titleInfo->fMpeg4Fifo = new HBFifo( 5 ) ) ); + + /* Create the threads */ + fThreadList->AddItem( new HBDVDReader( this, titleInfo ) ); + fThreadList->AddItem( new HBMpegDemux( this, titleInfo, + audio1Info, audio2Info ) ); + fThreadList->AddItem( new HBMpeg2Decoder( this, titleInfo ) ); + fThreadList->AddItem( new HBMpeg4Encoder( this, titleInfo ) ); + + if( audio1Info->fId ) + { + fFifoList->AddItem( ( audio1Info->fAc3Fifo = new HBFifo( 5 ) ) ); + fFifoList->AddItem( ( audio1Info->fRawFifo = new HBFifo( 5 ) ) ); + fFifoList->AddItem( ( audio1Info->fMp3Fifo = new HBFifo( 5 ) ) ); + fThreadList->AddItem( new HBAc3Decoder( this, audio1Info ) ); + fThreadList->AddItem( new HBMp3Encoder( this, audio1Info ) ); + } + + if( audio2Info->fId ) + { + fFifoList->AddItem( ( audio2Info->fAc3Fifo = new HBFifo( 5 ) ) ); + fFifoList->AddItem( ( audio2Info->fRawFifo = new HBFifo( 5 ) ) ); + fFifoList->AddItem( ( audio2Info->fMp3Fifo = new HBFifo( 5 ) ) ); + fThreadList->AddItem( new HBAc3Decoder( this, audio2Info ) ); + fThreadList->AddItem( new HBMp3Encoder( this, audio2Info ) ); + } + + fThreadList->AddItem( new HBAviMuxer( this, titleInfo, audio1Info, + audio2Info, file ) ); + + /* Run ! */ + HBThread * thread; + for( int i = 0; i < fThreadList->CountItems(); i++ ) + { + thread = (HBThread*) fThreadList->ItemAt( i ); + thread->Run(); + } +} + +void HBManager::Suspend() +{ + HBThread * thread; + for( int i = 0; i < fThreadList->CountItems(); i++ ) + { + thread = (HBThread*) fThreadList->ItemAt( i ); + thread->Suspend(); + } +} + +void HBManager::Resume() +{ + HBThread * thread; + for( int i = 0; i < fThreadList->CountItems(); i++ ) + { + thread = (HBThread*) fThreadList->ItemAt( i ); + thread->Resume(); + } +} + +bool HBManager::Cancel() +{ + if( !( fFifoList->CountItems() ) ) + /* Not running */ + return false; + + Status( "Cancelled.", 0.0, ENABLE_READY ); + Stop(); + + return true; +} + +/* Called by the DVD reader */ +void HBManager::Done() +{ + HBFifo * fifo = NULL; + for( int i = 0; i < fFifoList->CountItems(); i++ ) + { + fifo = (HBFifo*) fFifoList->ItemAt( i ); + + /* Wait until all threads have finished */ + while( fifo->Size() > 0 ) + snooze( 5000 ); + } + + char statusText[128]; memset( statusText, 0, 128 ); + sprintf( statusText, "Done (%.2f fps).", fAverageFrameRate ); + Status( statusText, 1.0, ENABLE_READY ); + Stop(); +} + +void HBManager::Error() +{ + Status( "An error occured.", 0.0, ENABLE_READY ); + Stop(); +} + +/* Private */ + +void HBManager::Stop() +{ + /* Freeze fifos */ + for( int i = 0; i < fFifoList->CountItems(); i++ ) + { + ((HBFifo*) fFifoList->ItemAt( i ))->Die(); + } + + /* Stop threads */ + HBThread * thread; + while( ( thread = (HBThread*) fThreadList->ItemAt( 0 ) ) ) + { + fThreadList->RemoveItem( thread ); + delete thread; + } + + /* Destroy fifos */ + HBFifo * fifo; + while( ( fifo = (HBFifo*) fFifoList->ItemAt( 0 ) ) ) + { + fFifoList->RemoveItem( fifo ); + delete fifo; + } +} + +void HBManager::DetectVolumes() +{ + /* Empty the current list */ + HBVolumeInfo * volumeInfo; + while( ( volumeInfo = (HBVolumeInfo*) fVolumeList->ItemAt( 0 ) ) ) + { + fVolumeList->RemoveItem( volumeInfo ); + delete volumeInfo; + } + + /* Detect the DVD drives by parsing mounted volumes */ + BVolumeRoster * roster = new BVolumeRoster(); + BVolume * volume = new BVolume(); + fs_info info; + int device; + device_geometry geometry; + + while( roster->GetNextVolume( volume ) == B_NO_ERROR ) + { + /* open() and ioctl() for more informations */ + fs_stat_dev( volume->Device(), &info ); + if( ( device = open( info.device_name, O_RDONLY ) ) < 0 ) + continue; + + if( ioctl( device, B_GET_GEOMETRY, &geometry, + sizeof( geometry ) ) < 0 ) + continue; + + /* Get the volume name */ + char volumeName[B_FILE_NAME_LENGTH]; + volume->GetName( volumeName ); + + if( volume->IsReadOnly() && geometry.device_type == B_CD ) + { + /* May be a DVD */ + + /* Try to open the device */ + volumeInfo = new HBVolumeInfo( volumeName, + info.device_name ); + + if( !volumeInfo->InitCheck() ) + { + delete volumeInfo; + continue; + } + + fVolumeList->AddItem( volumeInfo ); + } + else if( geometry.device_type == B_DISK ) + { + /* May be a hard drive. Look for VIDEO_TS folders on it */ + + BQuery * query = new BQuery(); + + if( query->SetVolume( volume ) != B_OK ) + { + delete query; + continue; + } + + if( query->SetPredicate( "name = VIDEO_TS.BUP" ) != B_OK ) + { + delete query; + continue; + } + + query->Fetch(); + + BEntry entry, parentEntry; + BPath path; + while( query->GetNextEntry( &entry ) != B_ENTRY_NOT_FOUND ) + { + entry.GetParent( &parentEntry ); + parentEntry.GetPath( &path ); + + /* Try to open the folder */ + volumeInfo = new HBVolumeInfo( (char*) path.Path(), + (char*) path.Path() ); + + if( !volumeInfo->InitCheck() ) + { + delete volumeInfo; + continue; + } + + fVolumeList->AddItem( volumeInfo ); + } + + delete query; + } + } + + /* Refresh the interface */ + BMessage * message = new BMessage( VOLUMES_DETECTED ); + message->AddPointer( "list", fVolumeList ); + fWindow->PostMessage( message ); + delete message; +} diff --git a/HBManager.h b/HBManager.h new file mode 100644 index 000000000..c1cabca15 --- /dev/null +++ b/HBManager.h @@ -0,0 +1,55 @@ +/* $Id: HBManager.h,v 1.25 2003/08/24 13:55:18 titer Exp $ */ + +#ifndef _HB_MANAGER_H +#define _HB_MANAGER_H + +#include <Looper.h> + +class HBWindow; +class HBPictureWin; +class HBFifo; + +class HBManager : public BLooper +{ + public: + HBManager( HBWindow * window ); + ~HBManager(); + virtual void MessageReceived( BMessage * message ); + + /* Methods called by the interface */ + void Start( HBVolumeInfo * volumeInfo, + HBTitleInfo * titleInfo, + HBAudioInfo * audio1Info, + HBAudioInfo * audio2Info, + char * file ); + void Suspend(); + void Resume(); + bool Cancel(); + + /* Methods called by the working threads */ + void SetPosition( float position ); + void SetFrameRate( float current, float average ); + void Done(); + void Error(); + + private: + void Stop(); + void DetectVolumes(); + + /* Interface */ + HBWindow * fWindow; + + /* Fifos & threads */ + BList * fThreadList; + BList * fFifoList; + + /* DVD infos */ + BList * fVolumeList; + + /* Status infos */ + float fPosition; + float fCurrentFrameRate; + float fAverageFrameRate; +}; + +#endif diff --git a/HBMp3Encoder.cpp b/HBMp3Encoder.cpp new file mode 100644 index 000000000..8cddd7615 --- /dev/null +++ b/HBMp3Encoder.cpp @@ -0,0 +1,115 @@ +/* $Id: HBMp3Encoder.cpp,v 1.5 2003/08/24 20:50:49 titer Exp $ */ + +#include "HBCommon.h" +#include "HBMp3Encoder.h" +#include "HBManager.h" +#include "HBFifo.h" + +#include <lame/lame.h> + +HBMp3Encoder::HBMp3Encoder( HBManager * manager, HBAudioInfo * audioInfo ) + : HBThread( "mp3encoder" ) +{ + fManager = manager; + fAudioInfo = audioInfo; + + fRawBuffer = NULL; +} + +void HBMp3Encoder::DoWork() +{ + while( !fAudioInfo->fRawFifo->Size() ) + { + snooze( 5000 ); + } + + /* The idea is to have exactly one mp3 frame (i.e. 1152 samples) by + output buffer. As we are resampling from fInSampleRate to + fOutSampleRate, we will give ( 1152 * fInSampleRate ) / + ( 2 * fOutSampleRate ) to libmp3lame so we are sure we will + never get more than 1 frame at a time */ + uint32_t count = ( 1152 * fAudioInfo->fInSampleRate ) / + ( 2 * fAudioInfo->fOutSampleRate ); + fLeftSamples = (float*) malloc( count * sizeof( float ) ); + fRightSamples = (float*) malloc( count * sizeof( float ) ); + + /* Init libmp3lame */ + lame_global_flags * globalFlags = lame_init(); + lame_set_in_samplerate( globalFlags, fAudioInfo->fInSampleRate ); + lame_set_out_samplerate( globalFlags, fAudioInfo->fOutSampleRate ); + lame_set_brate( globalFlags, fAudioInfo->fOutBitrate ); + lame_init_params( globalFlags ); + + HBBuffer * mp3Buffer; + int ret; + while( !fDie ) + { + /* Get new samples */ + if( !GetSamples( count ) ) + { + break; + } + + mp3Buffer = new HBBuffer( LAME_MAXMP3BUFFER ); + ret = lame_encode_buffer_float( globalFlags, + fLeftSamples, fRightSamples, + count, mp3Buffer->fData, + mp3Buffer->fSize ); + + if( ret < 0 ) + { + /* Something wrong happened */ + Log( "HBMp3Encoder : lame_encode_buffer_float() failed (%d)", ret ); + delete mp3Buffer; + fManager->Error(); + break; + } + else if( !ret ) + { + delete mp3Buffer; + } + else if( ret > 0 ) + { + /* We got something, send it to the muxer */ + mp3Buffer->fSize = ret; + mp3Buffer->fKeyFrame = true; + fAudioInfo->fMp3Fifo->Push( mp3Buffer ); + } + } +} + +bool HBMp3Encoder::GetSamples( uint32_t count ) +{ + uint32_t samplesNb = 0; + + while( samplesNb < count ) + { + if( !fRawBuffer ) + { + if( !( fRawBuffer = fAudioInfo->fRawFifo->Pop() ) ) + { + return false; + } + fPosInBuffer = 0; + } + + int willCopy = MIN( count - samplesNb, 6 * 256 - fPosInBuffer ); + memcpy( fLeftSamples + samplesNb, + (float*) fRawBuffer->fData + fPosInBuffer, + willCopy * sizeof( float ) ); + memcpy( fRightSamples + samplesNb, + (float*) fRawBuffer->fData + 6 * 256 + fPosInBuffer, + willCopy * sizeof( float ) ); + + samplesNb += willCopy; + fPosInBuffer += willCopy; + + if( fPosInBuffer == 6 * 256 ) + { + delete fRawBuffer; + fRawBuffer = NULL; + } + } + + return true; +} diff --git a/HBMp3Encoder.h b/HBMp3Encoder.h new file mode 100644 index 000000000..02e385224 --- /dev/null +++ b/HBMp3Encoder.h @@ -0,0 +1,30 @@ +/* $Id: HBMp3Encoder.h,v 1.3 2003/08/23 19:22:59 titer Exp $ */ + +#ifndef HB_MP3_ENCODER_H +#define HB_MP3_ENCODER_H + +#include "HBThread.h" +class HBAudioInfo; +class HBManager; +class HBBuffer; +class HBFifo; + +class HBMp3Encoder : public HBThread +{ + public: + HBMp3Encoder( HBManager * manager, HBAudioInfo * audioInfo ); + + private: + void DoWork(); + bool GetSamples( uint32_t count ); + + HBManager * fManager; + HBAudioInfo * fAudioInfo; + + HBBuffer * fRawBuffer; + uint32_t fPosInBuffer; /* in samples */ + float * fLeftSamples; + float * fRightSamples; +}; + +#endif diff --git a/HBMpeg2Decoder.cpp b/HBMpeg2Decoder.cpp new file mode 100644 index 000000000..f65f2df6b --- /dev/null +++ b/HBMpeg2Decoder.cpp @@ -0,0 +1,168 @@ +/* $Id: HBMpeg2Decoder.cpp,v 1.25 2003/08/23 19:38:47 titer Exp $ */ + +#include "HBCommon.h" +#include "HBManager.h" +#include "HBMpeg2Decoder.h" +#include "HBFifo.h" + +extern "C" { +#include <mpeg2dec/mpeg2.h> +} +#include <ffmpeg/avcodec.h> + +HBMpeg2Decoder::HBMpeg2Decoder( HBManager * manager, HBTitleInfo * titleInfo ) + : HBThread( "mpeg2decoder" ) +{ + fManager = manager; + fTitleInfo = titleInfo; +} + +void HBMpeg2Decoder::DoWork() +{ + /* Statistics */ + uint32_t framesSinceLast = 0; + uint32_t framesSinceBegin = 0; + uint64_t lastTime = 0; + uint64_t beginTime = 0; + + /* Init buffers */ + HBBuffer * mpeg2Buffer = NULL; + HBBuffer * rawBuffer = NULL; + HBBuffer * deinterlacedBuffer = NULL; + HBBuffer * resizedBuffer = NULL; + AVPicture * rawPicture = (AVPicture*) malloc( sizeof( AVPicture ) ); + AVPicture * deinterlacedPicture = (AVPicture*) malloc( sizeof( AVPicture ) ); + AVPicture * resizedPicture = (AVPicture*) malloc( sizeof( AVPicture ) ); + + /* Init libmpeg2 */ + mpeg2dec_t * handle = mpeg2_init(); + const mpeg2_info_t * info = mpeg2_info( handle ); + + /* libavcodec */ + ImgReSampleContext * resampleContext = NULL; + + /* NTSC 3:2 pulldown kludge - UGLY ! */ + if( fTitleInfo->fScale == 900900 ) + { + fTitleInfo->fScale = 1125000; + } + + /* Resizing & cropping initializations */ + resampleContext = img_resample_full_init( fTitleInfo->fOutWidth, fTitleInfo->fOutHeight, + fTitleInfo->fInWidth, fTitleInfo->fInHeight, + fTitleInfo->fTopCrop, fTitleInfo->fBottomCrop, + fTitleInfo->fLeftCrop, fTitleInfo->fRightCrop ); + rawBuffer = new HBBuffer( 3 * fTitleInfo->fInWidth * fTitleInfo->fInHeight / 2 ); + deinterlacedBuffer = new HBBuffer( 3 * fTitleInfo->fInWidth * fTitleInfo->fInHeight / 2 ); + avpicture_fill( rawPicture, rawBuffer->fData, + PIX_FMT_YUV420P, fTitleInfo->fInWidth, fTitleInfo->fInHeight ); + avpicture_fill( deinterlacedPicture, deinterlacedBuffer->fData, + PIX_FMT_YUV420P, fTitleInfo->fInWidth, fTitleInfo->fInHeight ); + + /* Init statistics */ + lastTime = system_time(); + beginTime = system_time(); + + Log( "HBMpeg2Decoder : %dx%d -> %dx%d, %.2f fps", + fTitleInfo->fInWidth, fTitleInfo->fInHeight, + fTitleInfo->fOutWidth, fTitleInfo->fOutHeight, + (float) fTitleInfo->fRate / fTitleInfo->fScale ); + + /* Main loop */ + mpeg2_state_t state; + for( ;; ) + { + state = mpeg2_parse( handle ); + + if( state == STATE_BUFFER ) + { + /* Free the previous buffer */ + if( mpeg2Buffer ) + delete mpeg2Buffer; + + /* Get a new one */ + if( !( mpeg2Buffer = fTitleInfo->fMpeg2Fifo->Pop() ) ) + break; + + /* Feed libmpeg2 */ + mpeg2_buffer( handle, mpeg2Buffer->fData, + mpeg2Buffer->fData + mpeg2Buffer->fSize ); + } + else if( ( state == STATE_SLICE || state == STATE_END ) && + info->display_fbuf ) + { + /* Got a raw picture */ + + /* Copy it */ + /* TODO : make libmpeg2 write directly in our buffer */ + memcpy( rawBuffer->fData, + info->display_fbuf->buf[0], + fTitleInfo->fInWidth * fTitleInfo->fInHeight ); + memcpy( rawBuffer->fData + fTitleInfo->fInWidth * fTitleInfo->fInHeight, + info->display_fbuf->buf[1], + fTitleInfo->fInWidth * fTitleInfo->fInHeight / 4 ); + memcpy( rawBuffer->fData + fTitleInfo->fInWidth * fTitleInfo->fInHeight + + fTitleInfo->fInWidth * fTitleInfo->fInHeight / 4, + info->display_fbuf->buf[2], + fTitleInfo->fInWidth * fTitleInfo->fInHeight / 4 ); + + resizedBuffer = new HBBuffer( 3 * fTitleInfo->fOutWidth * fTitleInfo->fOutHeight / 2 ); + avpicture_fill( resizedPicture, resizedBuffer->fData, PIX_FMT_YUV420P, + fTitleInfo->fOutWidth, fTitleInfo->fOutHeight ); + + if( fTitleInfo->fDeinterlace ) + { + avpicture_deinterlace( deinterlacedPicture, rawPicture, PIX_FMT_YUV420P, + fTitleInfo->fInWidth, fTitleInfo->fInHeight ); + img_resample( resampleContext, resizedPicture, + deinterlacedPicture ); + } + else + { + img_resample( resampleContext, resizedPicture, rawPicture ); + } + + /* Send it to the encoder */ + if( !( fTitleInfo->fRawFifo->Push( resizedBuffer ) ) ) + { + break; + } + + /* Update GUI position */ + fManager->SetPosition( mpeg2Buffer->fPosition ); + + /* Statistics every 0.5 second */ + framesSinceLast++; + framesSinceBegin++; + if( system_time() - lastTime > 500000 ) + { + fManager->SetFrameRate( 1000000 * (float) framesSinceLast / + (float) ( system_time() - lastTime ), + 1000000 * (float) framesSinceBegin / + (float) ( system_time() - beginTime ) ); + lastTime = system_time(); + framesSinceLast = 0; + } + + } + else if( state == STATE_INVALID ) + { + /* Shouldn't happen on a DVD */ + Log( "HBMpeg2Decoder : STATE_INVALID" ); + } + } + + /* Close libmpeg2 */ + mpeg2_close( handle ); + + /* Close libavcodec */ + img_resample_close( resampleContext ); + + /* Free structures & buffers */ + if( mpeg2Buffer ) delete mpeg2Buffer; + if( rawBuffer ) delete rawBuffer; + if( deinterlacedBuffer ) delete deinterlacedBuffer; + if( rawPicture ) free( rawPicture ); + if( deinterlacedPicture ) free( deinterlacedPicture ); + if( resizedPicture ) free( resizedPicture ); +} diff --git a/HBMpeg2Decoder.h b/HBMpeg2Decoder.h new file mode 100644 index 000000000..00e05dd57 --- /dev/null +++ b/HBMpeg2Decoder.h @@ -0,0 +1,27 @@ +/* $Id: HBMpeg2Decoder.h,v 1.9 2003/08/16 10:17:38 titer Exp $ */ + +#ifndef _HB_MPEG2_DECODER_H +#define _HB_MPEG2_DECODER_H + +#include "HBThread.h" +class HBManager; +class HBBuffer; +class HBFifo; + +typedef struct mpeg2dec_s mpeg2dec_t; +typedef struct AVPicture AVPicture; +typedef struct ImgReSampleContext ImgReSampleContext; + +class HBMpeg2Decoder : public HBThread +{ + public: + HBMpeg2Decoder( HBManager * manager, HBTitleInfo * titleInfo ); + + private: + void DoWork(); + + HBManager * fManager; + HBTitleInfo * fTitleInfo; +}; + +#endif diff --git a/HBMpeg4Encoder.cpp b/HBMpeg4Encoder.cpp new file mode 100644 index 000000000..f8682330b --- /dev/null +++ b/HBMpeg4Encoder.cpp @@ -0,0 +1,88 @@ +/* $Id: HBMpeg4Encoder.cpp,v 1.18 2003/08/23 19:38:47 titer Exp $ */ + +#include "HBCommon.h" +#include "HBMpeg4Encoder.h" +#include "HBManager.h" +#include "HBFifo.h" + +#include <ffmpeg/avcodec.h> + +HBMpeg4Encoder::HBMpeg4Encoder( HBManager * manager, HBTitleInfo * titleInfo ) + : HBThread( "mpeg4encoder" ) +{ + fManager = manager; + fTitleInfo = titleInfo; +} + +void HBMpeg4Encoder::DoWork() +{ + /* Init libavcodec */ + AVCodec * codec = avcodec_find_encoder( CODEC_ID_MPEG4 ); + if( !codec ) + { + Log( "HBMpeg4Encoder: avcodec_find_encoder() failed" ); + fManager->Error(); + return; + } + +#define WIDTH fTitleInfo->fOutWidth +#define HEIGHT fTitleInfo->fOutHeight +#define RATE fTitleInfo->fRate +#define SCALE fTitleInfo->fScale + + AVCodecContext * context; + context = avcodec_alloc_context(); + context->bit_rate = 1024 * fTitleInfo->fBitrate; + context->bit_rate_tolerance = 1024 * fTitleInfo->fBitrate; + context->flags |= CODEC_FLAG_HQ; + context->width = WIDTH; + context->height = HEIGHT; + context->frame_rate = RATE; + context->frame_rate_base = SCALE; + context->gop_size = 10 * RATE / SCALE; + + if( avcodec_open( context, codec ) < 0 ) + { + Log( "HBMpeg4Encoder: avcodec_open() failed" ); + fManager->Error(); + return; + } + + AVFrame * frame = avcodec_alloc_frame(); + HBBuffer * mpeg4Buffer; + + for( ;; ) + { + /* Get another frame */ + if( !( fRawBuffer = fTitleInfo->fRawFifo->Pop() ) ) + break; + + frame->data[0] = fRawBuffer->fData; + frame->data[1] = frame->data[0] + WIDTH * HEIGHT; + frame->data[2] = frame->data[1] + WIDTH * HEIGHT / 4; + frame->linesize[0] = WIDTH; + frame->linesize[1] = WIDTH / 2; + frame->linesize[2] = WIDTH / 2; + + mpeg4Buffer = new HBBuffer( 3 * WIDTH * HEIGHT / 2 ); + /* Should be too much. It can't be bigger than the raw video ! */ + + mpeg4Buffer->fSize = + avcodec_encode_video( context, mpeg4Buffer->fData, + mpeg4Buffer->fAllocSize, frame ); + mpeg4Buffer->fKeyFrame = ( context->coded_frame->key_frame != 0 ); + +#undef WIDTH +#undef HEIGHT +#undef RATE +#undef SCALE + + delete fRawBuffer; + + /* Mux it */ + if( !fTitleInfo->fMpeg4Fifo->Push( mpeg4Buffer ) ) + { + break; + } + } +} diff --git a/HBMpeg4Encoder.h b/HBMpeg4Encoder.h new file mode 100644 index 000000000..3f5ccacfa --- /dev/null +++ b/HBMpeg4Encoder.h @@ -0,0 +1,27 @@ +/* $Id: HBMpeg4Encoder.h,v 1.4 2003/08/05 16:47:19 titer Exp $ */ + +#ifndef _HB_MPEG4_ENCODER_H +#define _HB_MPEG4_ENCODER_H + +#include "HBThread.h" +class HBManager; +class HBFifo; +class HBAudioInfo; +class HBTitleInfo; +class HBBuffer; + +class HBMpeg4Encoder : public HBThread +{ + public: + HBMpeg4Encoder( HBManager * manager, HBTitleInfo * titleInfo ); + + private: + void DoWork(); + + HBManager * fManager; + HBTitleInfo * fTitleInfo; + + HBBuffer * fRawBuffer; +}; + +#endif diff --git a/HBMpegDemux.cpp b/HBMpegDemux.cpp new file mode 100644 index 000000000..5fda3dde3 --- /dev/null +++ b/HBMpegDemux.cpp @@ -0,0 +1,244 @@ +/* $Id: HBMpegDemux.cpp,v 1.13 2003/08/24 19:28:18 titer Exp $ */ + +#include "HBCommon.h" +#include "HBManager.h" +#include "HBMpegDemux.h" +#include "HBFifo.h" + +extern "C" { +#include <a52dec/a52.h> +} + +HBMpegDemux::HBMpegDemux( HBManager * manager, HBTitleInfo * titleInfo, + HBAudioInfo * audio1Info, HBAudioInfo * audio2Info ) + : HBThread( "mpegdemux" ) +{ + fManager = manager; + fTitleInfo = titleInfo; + fAudio1Info = audio1Info; + fAudio2Info = audio2Info; + + fPSBuffer = NULL; + fESBuffer = NULL; + fESBufferList = NULL; + + fFirstVideoPTS = -1; + fFirstAudio1PTS = -1; + fFirstAudio2PTS = -1; +} + +void HBMpegDemux::DoWork() +{ + while( !fDie ) + { + /* Get a PS packet */ + fPSBuffer = fTitleInfo->fPSFifo->Pop(); + + if( !fPSBuffer ) + { + continue; + } + + /* Get the ES data in it */ + fESBufferList = PStoES( fPSBuffer ); + + if( !fESBufferList ) + { + continue; + } + + while( ( fESBuffer = (HBBuffer*) fESBufferList->ItemAt( 0 ) ) ) + { + fESBufferList->RemoveItem( fESBuffer ); + + /* Look for a decoder for this ES */ + if( fESBuffer->fStreamId == 0xE0 ) + { + if( fFirstVideoPTS < 0 ) + { + fFirstVideoPTS = fESBuffer->fPTS; + } + fTitleInfo->fMpeg2Fifo->Push( fESBuffer ); + } + else if( fESBuffer->fStreamId == fAudio1Info->fId ) + { + /* If the audio track starts later than the video, + repeat the first frame as long as needed */ + if( fFirstAudio1PTS < 0 ) + { + fFirstAudio1PTS = fESBuffer->fPTS; + + if( fFirstAudio1PTS > fFirstVideoPTS ) + { + Log( "HBMpegDemux::DoWork() : audio track %x is late", + fAudio1Info->fId ); + InsertSilence( fFirstAudio1PTS - fFirstVideoPTS, + fAudio1Info->fAc3Fifo, + fESBuffer ); + } + } + fAudio1Info->fAc3Fifo->Push( fESBuffer ); + } + else if( fESBuffer->fStreamId == fAudio2Info->fId ) + { + if( fFirstAudio2PTS < 0 ) + { + fFirstAudio2PTS = fESBuffer->fPTS; + + if( fFirstAudio2PTS > fFirstVideoPTS ) + { + Log( "HBMpegDemux::DoWork() : audio track %x is late", + fAudio2Info->fId ); + InsertSilence( fFirstAudio2PTS - fFirstVideoPTS, + fAudio2Info->fAc3Fifo, + fESBuffer ); + } + } + fAudio2Info->fAc3Fifo->Push( fESBuffer ); + } + else + { + delete fESBuffer; + } + } + delete fESBufferList; + } +} + +void HBMpegDemux::InsertSilence( int64_t time, HBFifo * fifo, + HBBuffer * buffer ) +{ + int flags = 0; + int sampleRate = 0; + int bitrate = 0; + int frameSize = a52_syncinfo( buffer->fData, &flags, + &sampleRate, &bitrate ); + + if( !frameSize ) + { + Log( "HBMpegDemux::InsertSilence() : a52_syncinfo() failed" ); + return; + } + + uint32_t samples = sampleRate * time / 90000; + HBBuffer * buffer2; + + Log( "HBMpegDemux::InsertSilence() : adding %d samples", samples ); + + for( uint32_t i = 0; i < samples / ( 6 * 256 ); i++ ) + { + buffer2 = new HBBuffer( frameSize ); + memcpy( buffer2->fData, buffer->fData, frameSize ); + fifo->Push( buffer2 ); + } +} + +BList * PStoES( HBBuffer * psBuffer ) +{ +#define psData (psBuffer->fData) + + BList * esBufferList = new BList(); + HBBuffer * esBuffer; + uint32_t pos = 0; + + /* pack_header */ + if( psData[pos] != 0 || psData[pos+1] != 0 || + psData[pos+2] != 0x1 || psData[pos+3] != 0xBA ) + { + Log( "PStoES : not a PS packet (%02x%02x%02x%02x)", + psData[pos] << 24, psData[pos+1] << 16, + psData[pos+2] << 8, psData[pos+3] ); + delete psBuffer; + return NULL; + } + pos += 4; /* pack_start_code */ + pos += 9; /* pack_header */ + pos += 1 + ( psData[pos] & 0x7 ); /* stuffing bytes */ + + /* system_header */ + if( psData[pos] == 0 && psData[pos+1] == 0 && + psData[pos+2] == 0x1 && psData[pos+3] == 0xBB ) + { + uint32_t header_length; + + pos += 4; /* system_header_start_code */ + header_length = ( psData[pos] << 8 ) + psData[pos+1]; + pos += 2 + header_length; + } + + /* PES */ + while( pos + 2 < psBuffer->fSize && + psData[pos] == 0 && psData[pos+1] == 0 && psData[pos+2] == 0x1 ) + { + uint32_t streamId; + uint32_t PES_packet_length; + uint32_t PES_packet_end; + uint32_t PES_header_data_length; + uint32_t PES_header_end; + bool hasPTS; + uint64_t PTS = 0; + + pos += 3; /* packet_start_code_prefix */ + streamId = psData[pos++]; + + PES_packet_length = ( psData[pos] << 8 ) + psData[pos+1]; + pos += 2; /* PES_packet_length */ + PES_packet_end = pos + PES_packet_length; + + hasPTS = ( ( psData[pos+1] >> 6 ) & 0x2 ); + pos += 2; /* Required headers */ + + PES_header_data_length = psData[pos]; + pos += 1; + PES_header_end = pos + PES_header_data_length; + + if( hasPTS ) + { + PTS = ( ( ( psData[pos] >> 1 ) & 0x7 ) << 30 ) + + ( psData[pos+1] << 22 ) + + ( ( psData[pos+2] >> 1 ) << 15 ) + + ( psData[pos+3] << 7 ) + + ( psData[pos+4] >> 1 ); + } + + pos = PES_header_end; + + if( streamId != 0xE0 && streamId != 0xBD ) + { + /* Not interesting */ + continue; + } + + if( streamId == 0xBD ) + { + /* A52 : don't ask */ + streamId |= ( psData[pos] << 8 ); + pos += 4; + } + + /* Here we hit we ES payload */ + esBuffer = new HBBuffer( PES_packet_end - pos ); + + esBuffer->fPosition = psBuffer->fPosition; + esBuffer->fStreamId = streamId; + esBuffer->fPTS = PTS; + memcpy( esBuffer->fData, psBuffer->fData + pos, + PES_packet_end - pos ); + + esBufferList->AddItem( esBuffer ); + + pos = PES_packet_end; + } + + delete psBuffer; + + if( esBufferList && !esBufferList->CountItems() ) + { + delete esBufferList; + return NULL; + } + + return esBufferList; + +#undef psData +} diff --git a/HBMpegDemux.h b/HBMpegDemux.h new file mode 100644 index 000000000..f98c0a0ae --- /dev/null +++ b/HBMpegDemux.h @@ -0,0 +1,38 @@ +/* $Id: HBMpegDemux.h,v 1.7 2003/08/24 13:27:41 titer Exp $ */ + +#ifndef _HB_MPEG_DEMUX_H +#define _HB_MPEG_DEMUX_H + +#include "HBThread.h" +class HBManager; +class HBBuffer; +class BList; + +BList * PStoES( HBBuffer * psBuffer ); + +class HBMpegDemux : public HBThread +{ + public: + HBMpegDemux( HBManager * manager, HBTitleInfo * titleInfo, + HBAudioInfo * audio1Info, HBAudioInfo * audio2Info ); + + private: + void DoWork(); + void InsertSilence( int64_t time, HBFifo * fifo, + HBBuffer * buffer ); + + HBManager * fManager; + HBTitleInfo * fTitleInfo; + HBAudioInfo * fAudio1Info; + HBAudioInfo * fAudio2Info; + + HBBuffer * fPSBuffer; + HBBuffer * fESBuffer; + BList * fESBufferList; + + int64_t fFirstVideoPTS; + int64_t fFirstAudio1PTS; + int64_t fFirstAudio2PTS; +}; + +#endif diff --git a/HBPictureWin.cpp b/HBPictureWin.cpp new file mode 100644 index 000000000..9849af010 --- /dev/null +++ b/HBPictureWin.cpp @@ -0,0 +1,288 @@ +/* $Id: HBPictureWin.cpp,v 1.14 2003/08/24 20:50:49 titer Exp $ */ + +#include "HBCommon.h" +#include "HBPictureWin.h" +#include "HBManager.h" + +#include <Bitmap.h> +#include <Box.h> +#include <CheckBox.h> +#include <Slider.h> + +#include <ffmpeg/avcodec.h> + +#define UPDATE_BITMAP 'upbi' + +HBPictureView::HBPictureView( BRect rect, BBitmap * bitmap ) + : BView( rect, NULL, B_FOLLOW_ALL, B_WILL_DRAW ) +{ + fBitmap = bitmap; +} + +void HBPictureView::Draw( BRect rect ) +{ + if( LockLooper() ) + { + DrawBitmap( fBitmap, Bounds() ); + UnlockLooper(); + } + else + { + Log( "HBPictureView::Draw : LockLooper() failed" ); + } + + BView::Draw( rect ); +} + + +/* Constructor */ +HBPictureWin::HBPictureWin( HBTitleInfo * titleInfo ) + : BWindow( BRect( 50, 50, 60, 60 ), "Picture settings", + B_TITLED_WINDOW, B_NOT_RESIZABLE | B_NOT_ZOOMABLE ) +{ + fTitleInfo = titleInfo; + + fMaxOutWidth = fTitleInfo->fInWidth; + fMaxOutHeight = MULTIPLE_16( fTitleInfo->fInHeight * fTitleInfo->fPixelHeight / + fTitleInfo->fPixelWidth ); + fBitmap = new BBitmap( BRect( 0, 0, fMaxOutWidth, fMaxOutHeight ), + 0, B_RGB32 ); + + ResizeTo( fMaxOutWidth + 40, fMaxOutHeight + 330 ); + + BRect r; + + /* Add a background view */ + BView * view; + view = new BView( Bounds(), NULL, B_FOLLOW_ALL, B_WILL_DRAW ); + view->SetViewColor( ui_color( B_PANEL_BACKGROUND_COLOR ) ); + AddChild( view ); + + /* First box : picture + slider */ + r = BRect( 10, 10, fMaxOutWidth + 30, fMaxOutHeight + 65 ); + BBox * pictureBox; + pictureBox = new BBox( r, NULL ); + pictureBox->SetLabel( "Preview" ); + + /* Picture view */ + r = BRect( 10, 15, fMaxOutWidth + 10, fMaxOutHeight + 15 ); + fPictureView = new HBPictureView( r, fBitmap ); + pictureBox->AddChild( fPictureView ); + + /* Slider */ + r = BRect( 10, fMaxOutHeight + 25, fMaxOutWidth + 10, fMaxOutHeight + 55 ); + fPictureSlider = new BSlider( r, NULL, NULL, new BMessage( UPDATE_BITMAP ), 0, 9 ); + pictureBox->AddChild( fPictureSlider ); + + view->AddChild( pictureBox ); + + /* Second box : resize & crop settings */ + r = BRect( 10, fMaxOutHeight + 75, fMaxOutWidth + 30, fMaxOutHeight + 320 ); + BBox * settingsBox; + settingsBox = new BBox( r, NULL ); + settingsBox->SetLabel( "Settings" ); + + r = BRect( 10, 15, fMaxOutWidth + 10, 45 ); + fWidthSlider = new BSlider( r, NULL, "Picture size", + new BMessage( UPDATE_BITMAP ), + 1, fMaxOutWidth / 16, + B_TRIANGLE_THUMB ); + fWidthSlider->SetValue( fMaxOutWidth / 16 ); + settingsBox->AddChild( fWidthSlider ); + + r = BRect( 10, 55, fMaxOutWidth + 10, 85 ); + fTopCropSlider = new BSlider( r, NULL, "Top cropping", + new BMessage( UPDATE_BITMAP ), + 0, fTitleInfo->fInHeight / 4, + B_TRIANGLE_THUMB ); + settingsBox->AddChild( fTopCropSlider ); + + r = BRect( 10, 95, fMaxOutWidth + 10, 125 ); + fBottomCropSlider = new BSlider( r, NULL, "Bottom cropping", + new BMessage( UPDATE_BITMAP ), + 0, fTitleInfo->fInHeight / 4, + B_TRIANGLE_THUMB ); + settingsBox->AddChild( fBottomCropSlider ); + + r = BRect( 10, 135, fMaxOutWidth + 10, 165 ); + fLeftCropSlider = new BSlider( r, NULL, "Left cropping", + new BMessage( UPDATE_BITMAP ), + 0, fTitleInfo->fInWidth / 4, + B_TRIANGLE_THUMB ); + settingsBox->AddChild( fLeftCropSlider ); + + r = BRect( 10, 175, fMaxOutWidth + 10, 205 ); + fRightCropSlider = new BSlider( r, NULL, "Right cropping", + new BMessage( UPDATE_BITMAP ), + 0, fTitleInfo->fInWidth / 4, + B_TRIANGLE_THUMB ); + settingsBox->AddChild( fRightCropSlider ); + + r = BRect( 10, 215, fMaxOutWidth + 10, 235 ); + fDeinterlaceCheck = new BCheckBox( r, NULL, "Deinterlace", + new BMessage( UPDATE_BITMAP ) ); + settingsBox->AddChild( fDeinterlaceCheck ); + + view->AddChild( settingsBox ); + + /* Buttons */ + + + Hide(); + Show(); + + UpdateBitmap( 0 ); +} + +bool HBPictureWin::QuitRequested() +{ + if( Lock() ) + { + Hide(); + Unlock(); + } + else + { + Log( "HBPictureWin::QuitRequested : cannot Lock()" ); + } + return false; +} + +void HBPictureWin::MessageReceived( BMessage * message ) +{ + switch( message->what ) + { + case UPDATE_BITMAP: + UpdateBitmap( fPictureSlider->Value() ); + fPictureView->Draw( fPictureView->Bounds() ); + break; + + default: + BWindow::MessageReceived( message ); + } +} + +void HBPictureWin::UpdateBitmap( int which ) +{ +#define fInWidth fTitleInfo->fInWidth +#define fInHeight fTitleInfo->fInHeight +#define fPixelWidth fTitleInfo->fPixelWidth +#define fPixelHeight fTitleInfo->fPixelHeight +#define fDeinterlace fTitleInfo->fDeinterlace +#define fOutWidth fTitleInfo->fOutWidth +#define fOutHeight fTitleInfo->fOutHeight +#define fTopCrop fTitleInfo->fTopCrop +#define fBottomCrop fTitleInfo->fBottomCrop +#define fLeftCrop fTitleInfo->fLeftCrop +#define fRightCrop fTitleInfo->fRightCrop +#define fPictures fTitleInfo->fPictures + + fTopCrop = 2 * fTopCropSlider->Value(); + fBottomCrop = 2 * fBottomCropSlider->Value(); + fLeftCrop = 2 * fLeftCropSlider->Value(); + fRightCrop = 2 * fRightCropSlider->Value(); + fDeinterlace = ( fDeinterlaceCheck->Value() != 0 ); + + fOutWidth = MULTIPLE_16( 16 * fWidthSlider->Value() - fLeftCrop - fRightCrop ); + fOutHeight = MULTIPLE_16( ( fInHeight - fTopCrop - fBottomCrop ) * + ( 16 * fWidthSlider->Value() * fPixelHeight ) / + ( fInWidth * fPixelWidth ) ); + + AVPicture pic1; /* original YUV picture */ + avpicture_fill( &pic1, fPictures[which], + PIX_FMT_YUV420P, fInWidth, fInHeight ); + + AVPicture pic2; /* deinterlaced YUV picture */ + uint8_t * buf2 = (uint8_t*) malloc( 3 * fInWidth * fInHeight / 2 ); + avpicture_fill( &pic2, buf2, PIX_FMT_YUV420P, fInWidth, fInHeight ); + + AVPicture pic3; /* resized YUV picture */ + uint8_t * buf3 = (uint8_t*) malloc( 3 * fOutWidth * fOutHeight / 2 ); + avpicture_fill( &pic3, buf3, PIX_FMT_YUV420P, fOutWidth, fOutHeight ); + + AVPicture pic4; /* resized RGB picture */ + uint8_t * buf4 = (uint8_t*) malloc( 4 * fOutWidth * fOutHeight ); + avpicture_fill( &pic4, buf4, PIX_FMT_RGBA32, fOutWidth, fOutHeight ); + + ImgReSampleContext * resampleContext; + resampleContext = img_resample_full_init( fOutWidth, fOutHeight, + fInWidth, fInHeight, + fTopCrop, fBottomCrop, + fLeftCrop, fRightCrop ); + + if( fDeinterlace ) + { + avpicture_deinterlace( &pic2, &pic1, PIX_FMT_YUV420P, fInWidth, fInHeight ); + img_resample( resampleContext, &pic3, &pic2 ); + } + else + { + img_resample( resampleContext, &pic3, &pic1 ); + } + img_convert( &pic4, PIX_FMT_RGBA32, &pic3, PIX_FMT_YUV420P, fOutWidth, fOutHeight ); + + /* Blank the bitmap */ + for( uint32_t i = 0; i < fMaxOutHeight; i++ ) + { + memset( (uint8_t*) fBitmap->Bits() + i * fBitmap->BytesPerRow(), + 0, fMaxOutWidth * 4 ); + } + + /* Draw the picture (centered) */ + uint32_t leftOffset = ( fMaxOutWidth - fOutWidth ) / 2; + uint32_t topOffset = ( fMaxOutHeight - fOutHeight ) / 2; + for( uint32_t i = 0; i < fOutHeight; i++ ) + { + memcpy( (uint8_t*) fBitmap->Bits() + + ( i + topOffset ) * fBitmap->BytesPerRow() + + leftOffset * 4, + buf4 + i * fOutWidth * 4, + fOutWidth * 4 ); + } + + /* Draw the cropping zone */ + memset( (uint8_t*) fBitmap->Bits() + + topOffset * fBitmap->BytesPerRow() + + leftOffset * 4, + 0xFF, + fOutWidth * 4 ); + + for( uint32_t i = 0; i < fOutHeight; i++ ) + { + memset( (uint8_t*) fBitmap->Bits() + + ( i + topOffset ) * fBitmap->BytesPerRow() + + leftOffset * 4, + 0xFF, + 4 ); + memset( (uint8_t*) fBitmap->Bits() + + ( i + topOffset ) * fBitmap->BytesPerRow() + + ( leftOffset + fOutWidth - 1 ) * 4, + 0xFF, + 4 ); + } + + memset( (uint8_t*) fBitmap->Bits() + + ( topOffset + fOutHeight - 1 ) * fBitmap->BytesPerRow() + + leftOffset * 4, + 0xFF, + fOutWidth * 4 ); + + /* Clean up */ + free( buf2 ); + free( buf3 ); + free( buf4 ); + + /* Show the output size */ + if( !Lock() ) + { + Log( "HBPictureWin::UpdateBitmap() : cannot Lock()" ); + return; + } + + char label[128]; memset( label, 0, 128 ); + snprintf( label, 128, "Picture size : %d x %d", + fOutWidth, fOutHeight ); + fWidthSlider->SetLabel( label ); + + Unlock(); +} diff --git a/HBPictureWin.h b/HBPictureWin.h new file mode 100644 index 000000000..9f1e37648 --- /dev/null +++ b/HBPictureWin.h @@ -0,0 +1,52 @@ +/* $Id: HBPictureWin.h,v 1.6 2003/08/20 20:30:30 titer Exp $ */ + +#ifndef _HB_PICTURE_WIN_H +#define _HB_PICTURE_WIN_H + +class HBTitleInfo; + +#include <View.h> +#include <Window.h> +class BSlider; +class BCheckBox; + +class HBPictureView : public BView +{ + public: + HBPictureView::HBPictureView( BRect rect, BBitmap * bitmap ); + virtual void Draw( BRect rect ); + + private: + BBitmap * fBitmap; +}; + +class HBPictureWin : public BWindow +{ + public: + HBPictureWin( HBTitleInfo * titleInfo ); + virtual bool QuitRequested(); + virtual void MessageReceived( BMessage * message ); + + void UpdateBitmap( int which ); + + + private: + HBTitleInfo * fTitleInfo; + + /* GUI */ + HBPictureView * fPictureView; + BSlider * fPictureSlider; + BBitmap * fBitmap; + BSlider * fWidthSlider; + BSlider * fTopCropSlider; + BSlider * fBottomCropSlider; + BSlider * fLeftCropSlider; + BSlider * fRightCropSlider; + BCheckBox * fDeinterlaceCheck; + + /* Internal infos */ + uint32_t fMaxOutWidth; + uint32_t fMaxOutHeight; +}; + +#endif diff --git a/HBThread.cpp b/HBThread.cpp new file mode 100644 index 000000000..b36462be0 --- /dev/null +++ b/HBThread.cpp @@ -0,0 +1,49 @@ +/* $Id: HBThread.cpp,v 1.3 2003/08/24 13:27:41 titer Exp $ */ + +#include "HBCommon.h" +#include "HBThread.h" + +#include <OS.h> + +HBThread::HBThread( char * name, int priority = B_LOW_PRIORITY ) +{ + fName = strdup( name ); + fThread = spawn_thread( ThreadFunc, fName, priority, this ); +} + +HBThread::~HBThread() +{ + fDie = true; + Log( "Stopping thread %d (\"%s\")", fThread, fName ); + int32 exit_value; + wait_for_thread( fThread, &exit_value ); + Log( "Thread %d stopped (\"%s\")", fThread, fName ); + free( fName ); +} + +void HBThread::Run() +{ + fDie = false; + resume_thread( fThread ); + Log( "Thread %d started (\"%s\")", fThread, fName ); +} + +void HBThread::Suspend() +{ + suspend_thread( fThread ); +} + +void HBThread::Resume() +{ + resume_thread( fThread ); +} + +long HBThread::ThreadFunc( HBThread * _this ) +{ + _this->DoWork(); + return 0; +} + +void HBThread::DoWork() +{ +} diff --git a/HBThread.h b/HBThread.h new file mode 100644 index 000000000..5356e5c1f --- /dev/null +++ b/HBThread.h @@ -0,0 +1,26 @@ +/* $Id: HBThread.h,v 1.3 2003/08/24 13:27:41 titer Exp $ */ + +#ifndef _HB_THREAD_H +#define _HB_THREAD_H + +class HBThread +{ + public: + HBThread( char * name, int priority = 5 ); + virtual ~HBThread(); + void Run(); + void Suspend(); + void Resume(); + + protected: + volatile bool fDie; + + private: + static long ThreadFunc( HBThread * _this ); + virtual void DoWork(); + + char * fName; + int fThread; +}; + +#endif diff --git a/HBWindow.cpp b/HBWindow.cpp new file mode 100644 index 000000000..0d665ae45 --- /dev/null +++ b/HBWindow.cpp @@ -0,0 +1,630 @@ +/* $Id: HBWindow.cpp,v 1.20 2003/08/24 20:50:49 titer Exp $ */ + +#include "HBCommon.h" +#include "HBWindow.h" +#include "HBManager.h" +#include "HBPictureWin.h" + +#include <Alert.h> +#include <Application.h> +#include <Box.h> +#include <Button.h> +#include <MenuField.h> +#include <PopUpMenu.h> +#include <Slider.h> +#include <StatusBar.h> +#include <StringView.h> +#include <TextControl.h> + +#define WINDOW_RECT BRect( 100,100,500,505 ) + +/* HBBox : almost a simple BBox, unless we draw a horizontal line + before the "Picture" and "Advanced" buttons. There must be a + cleaner way to do this, but I'm not a expert GUI programmer. */ + +/* Constructor */ +HBBox::HBBox( BRect rect ) + : BBox( rect, NULL ) +{ +} + +/* Draw */ +void HBBox::Draw( BRect rect ) +{ + /* Inherited method */ + BBox::Draw( rect ); + + /* Draw the line */ + SetHighColor( 120, 120, 120 ); + SetLowColor( 255, 255, 255 ); + StrokeLine( BPoint( 10, 265 ), + BPoint( Bounds().Width() - 10, 265 ), + B_SOLID_HIGH ); + StrokeLine( BPoint( 11, 266 ), + BPoint( Bounds().Width() - 10, 266 ), + B_SOLID_LOW ); +} + +/* HBWindow : the real interface */ + +/* Constructor */ +HBWindow::HBWindow() + : BWindow( WINDOW_RECT, "HandBrake " VERSION, B_TITLED_WINDOW, + B_NOT_RESIZABLE | B_NOT_ZOOMABLE ) +{ + BRect r; + + /* Add a background view */ + BView * view; + view = new BView( Bounds(), NULL, B_FOLLOW_ALL, B_WILL_DRAW ); + view->SetViewColor( ui_color( B_PANEL_BACKGROUND_COLOR ) ); + AddChild( view ); + + /* Add the settings box */ + r = BRect( 10, 10, view->Bounds().Width() - 10, + view->Bounds().Height() - 85 ); + fBox = new HBBox( r ); + fBox->SetLabel( "Settings" ); + + /* Volume */ + r = BRect( 10, 15, fBox->Bounds().Width() - 10, 35 ); + fVolumePopUp = new BPopUpMenu( "No DVD found" ); + fVolumeField = new BMenuField( r, NULL, "Volume :", + fVolumePopUp, true ); + fBox->AddChild( fVolumeField ); + + /* Title */ + r = BRect( 10, 45, fBox->Bounds().Width() - 10, 65 ); + fTitlePopUp = new BPopUpMenu( "No title found" ); + fTitleField = new BMenuField( r, NULL, "Title :", + fTitlePopUp, true ); + fBox->AddChild( fTitleField ); + + /* Audio 1 */ + r = BRect( 10, 75, fBox->Bounds().Width() - 10, 95 ); + fAudio1PopUp = new BPopUpMenu( "No audio found" ); + fAudio1Field = new BMenuField( r, NULL, "Audio 1 :", + fAudio1PopUp, true ); + fBox->AddChild( fAudio1Field ); + + /* Audio 2 */ + r = BRect( 10, 105, fBox->Bounds().Width() - 10, 125 ); + fAudio2PopUp = new BPopUpMenu( "No audio found" ); + fAudio2Field = new BMenuField( r, NULL, "Audio 2 :", + fAudio2PopUp, true ); + fBox->AddChild( fAudio2Field ); + + /* Video bitrate */ + r = BRect( 10, 135, fBox->Bounds().Width() - 10, 165 ); + fVideoSlider = new BSlider( r, NULL, "Video bitrate : 1024 kbps", + new BMessage( VIDEO_SLIDER ), + 128, 4096, B_TRIANGLE_THUMB ); + fVideoSlider->SetValue( 1024 ); + fBox->AddChild( fVideoSlider ); + + /* Audio bitrate */ + r = BRect( 10, 175, fBox->Bounds().Width() - 10, 205 ); + fAudioSlider = new BSlider( r, NULL, "Audio bitrate : 128 kbps", + new BMessage( AUDIO_SLIDER ), + 64, 384, B_TRIANGLE_THUMB ); + fAudioSlider->SetValue( 128 ); + fBox->AddChild( fAudioSlider ); + + /* Destination file */ + r = BRect( 10, 215, fBox->Bounds().Width() - 10, 230 ); + fFileString = new BStringView( r, NULL, "Destination file :" ); + fBox->AddChild( fFileString ); + r = BRect( 10, 235, fBox->Bounds().Width() - 90, 255 ); + fFileControl = new BTextControl( r, NULL, "", "/boot/home/Desktop/Movie.avi", + new BMessage() ); + fFileControl->SetDivider( 0 ); + fBox->AddChild( fFileControl ); + r = BRect( fBox->Bounds().Width() - 80, 230, + fBox->Bounds().Width() - 10, 255 ); + fFileButton = new BButton( r, NULL, "Browse...", + new BMessage( NOT_IMPLEMENTED ) ); + fBox->AddChild( fFileButton ); + + view->AddChild( fBox ); + + /* Settings buttons */ + r = BRect( fBox->Bounds().Width() - 200, 275, + fBox->Bounds().Width() - 100, 300 ); + fPictureButton = new BButton( r, NULL, "Picture settings...", + new BMessage( PICTURE_WIN ) ); + fBox->AddChild( fPictureButton ); + + r = BRect( fBox->Bounds().Width() - 90, 275, + fBox->Bounds().Width() - 10, 300 ); + fAdvancedButton = new BButton( r, NULL, "Advanced...", + new BMessage( NOT_IMPLEMENTED ) ); + fBox->AddChild( fAdvancedButton ); + + /* Status bar */ + r = BRect( 10, view->Bounds().Height() - 75, + view->Bounds().Width() - 10, view->Bounds().Height() - 45 ); + fStatusBar = new BStatusBar( r, NULL, NULL ); + fStatusBar->SetMaxValue( 1.0 ); + view->AddChild( fStatusBar ); + + /* Buttons */ + r = BRect( view->Bounds().Width() - 320, view->Bounds().Height() - 35, + view->Bounds().Width() - 250, view->Bounds().Height() - 10 ); + BButton * aboutButton; + aboutButton = new BButton( r, NULL, "About...", + new BMessage( B_ABOUT_REQUESTED ) ); + view->AddChild( aboutButton ); + + r = BRect( view->Bounds().Width() - 240, view->Bounds().Height() - 35, + view->Bounds().Width() - 170, view->Bounds().Height() - 10 ); + fRefreshButton = new BButton( r, NULL, "Refresh", + new BMessage( REFRESH_VOLUMES ) ); + view->AddChild( fRefreshButton ); + + r = BRect( view->Bounds().Width() - 160, view->Bounds().Height() - 35, + view->Bounds().Width() - 90, view->Bounds().Height() - 10 ); + fSuspendButton = new BButton( r, NULL, "Suspend", new BMessage( SUSPEND_CONVERT ) ); + view->AddChild( fSuspendButton ); + + r = BRect( view->Bounds().Width() - 80, view->Bounds().Height() - 35, + view->Bounds().Width() - 10, view->Bounds().Height() - 10 ); + fStartButton = new BButton( r, NULL, "Start !", new BMessage( START_CONVERT ) ); + view->AddChild( fStartButton ); +} + +bool HBWindow::QuitRequested() +{ + /* Empty the PopUps - the BMenuItems do not belong to us */ + HBVolumeInfo * volumeInfo; + while( ( volumeInfo = (HBVolumeInfo*) fVolumePopUp->ItemAt( 0 ) ) ) + { + fVolumePopUp->RemoveItem( volumeInfo ); + } + + HBTitleInfo * titleInfo; + while( ( titleInfo = (HBTitleInfo*) fTitlePopUp->ItemAt( 0 ) ) ) + { + fTitlePopUp->RemoveItem( titleInfo ); + } + + HBAudioInfo * audioInfo; + while( ( audioInfo = (HBAudioInfo*) fAudio1PopUp->ItemAt( 0 ) ) ) + { + fAudio1PopUp->RemoveItem( audioInfo ); + } + while( ( audioInfo = (HBAudioInfo*) fAudio2PopUp->ItemAt( 0 ) ) ) + { + fAudio2PopUp->RemoveItem( audioInfo ); + } + + /* Stop the application */ + be_app->PostMessage( B_QUIT_REQUESTED ); + return true; +} + +void HBWindow::MessageReceived( BMessage * message ) +{ + switch( message->what ) + { + case NOT_IMPLEMENTED: + { + /* Warn the user with a BAlert */ + BAlert * alert; + alert = new BAlert( "Not implemented", + "This feature has not yet been implemented.", + "Come back later !" ); + alert->Go( NULL ); + break; + } + + case VOLUME_SELECTED: + case TITLE_SELECTED: + case LANGUAGE_SELECTED: + SelectionChanged(); + break; + + case VIDEO_SLIDER: + { + /* Update the slider label */ + char label[128]; memset( label, 0, 128 ); + snprintf( label, 128, + "Video bitrate : %ld kbps", + fVideoSlider->Value() ); + fVideoSlider->SetLabel( label ); + break; + } + + case AUDIO_SLIDER: + { + /* Update the slider label */ + char label[128]; memset( label, 0, 128 ); + snprintf( label, 128, + "Audio bitrate : %ld kbps", + fAudioSlider->Value() ); + fAudioSlider->SetLabel( label ); + break; + } + + case PICTURE_WIN: + { + HBTitleInfo * titleInfo; + titleInfo = (HBTitleInfo*) fTitlePopUp->FindMarked(); + + if( titleInfo->fPictureWin->Lock() ) + { + titleInfo->fPictureWin->Show(); + titleInfo->fPictureWin->Unlock(); + break; + } + else + { + Log( "Couldn't lock fPictureWin" ); + } + + break; + } + + case SUSPEND_CONVERT: + /* Suspend all threads */ + fManager->Suspend(); + + /* Update the button label */ + if( Lock() ) + { + fSuspendButton->SetLabel( "Resume" ); + fSuspendButton->SetMessage( new BMessage( RESUME_CONVERT ) ); + Unlock(); + } + break; + + case RESUME_CONVERT: + /* Resume all threads */ + fManager->Resume(); + + /* Update the button label */ + if( Lock() ) + { + fSuspendButton->SetLabel( "Suspend" ); + fSuspendButton->SetMessage( new BMessage( SUSPEND_CONVERT ) ); + Unlock(); + } + break; + + case START_CONVERT: + { + /* Shouldn't happen */ + if( !fVolumePopUp->FindMarked() || + !fTitlePopUp->FindMarked() || + !fAudio1PopUp->FindMarked() || + !fAudio2PopUp->FindMarked() ) + break; + + /* Disable the interface */ + Status( "Starting...", 0.0, ENABLE_ENCODING ); + + /* Start the job */ + HBVolumeInfo * volumeInfo = (HBVolumeInfo*) fVolumePopUp->FindMarked(); + HBTitleInfo * titleInfo = (HBTitleInfo*) fTitlePopUp->FindMarked(); + HBAudioInfo * audio1Info = (HBAudioInfo*) fAudio1PopUp->FindMarked(); + HBAudioInfo * audio2Info = (HBAudioInfo*) fAudio2PopUp->FindMarked(); + + titleInfo->fBitrate = fVideoSlider->Value(); + audio1Info->fOutBitrate = fAudioSlider->Value(); + audio2Info->fOutBitrate = fAudioSlider->Value(); + + fManager->Start( volumeInfo, titleInfo, + audio1Info, audio2Info, + (char*) fFileControl->Text() ); + + /* Update the button label */ + if( Lock() ) + { + fStartButton->SetLabel( "Cancel" ); + fStartButton->SetMessage( new BMessage( STOP_CONVERT ) ); + Unlock(); + } + break; + } + + case STOP_CONVERT: + /* Stop the job */ + fManager->Cancel(); + + /* Update the button label */ + if( Lock() ) + { + fStartButton->SetLabel( "Start !" ); + fStartButton->SetMessage( new BMessage( START_CONVERT ) ); + Unlock(); + } + + /* Enable the interface */ + Status( "Cancelled.", 0.0, ENABLE_READY ); + break; + + case REFRESH_VOLUMES: + /* Disable the interface */ + Status( "Checking DVD volumes...", 0.0, ENABLE_DETECTING ); + + /* Ask the manager to start the detection */ + fManager->PostMessage( DETECT_VOLUMES ); + break; + + case VOLUMES_DETECTED: + { + /* Update the popup */ + BList * volumeList; + message->FindPointer( "list", (void**)&volumeList ); + RefreshVolumes( volumeList ); + + /* Enable the interface */ + Status( "Ready.", 0.0, ENABLE_READY ); + break; + } + + case B_ABOUT_REQUESTED: + { + BAlert * alert; + alert = new BAlert( "title", + "HandBrake " VERSION "\n\n" + "by Eric Petit <[email protected]>\n" + "Homepage : <http://beos.titer.org/handbrake/>\n\n" + "No, you don't want to know where this stupid app " + "name comes from.", + "Woot !" ); + alert->Go( NULL ); + break; + } + + case MANAGER_CREATED: + { + message->FindPointer( "manager", (void**)&fManager ); + break; + } + + case CHANGE_STATUS: + { + char * text; + float pos; + int mode; + message->FindPointer( "text", (void**) &text ); + message->FindFloat( "pos", &pos ); + message->FindInt32( "mode", (int32*) &mode ); + + if( !Lock() ) + { + Log( "HBWindow::MessageReceived() : Lock() failed" ); + break; + } + fStatusBar->Update( pos - fStatusBar->CurrentValue(), text ); + Enable( mode ); + Unlock(); + break; + } + + default: + { + BWindow::MessageReceived( message ); + } + } +} + +void HBWindow::Enable( int mode ) +{ + switch( mode ) + { + case ENABLE_DETECTING: + fVolumeField->SetEnabled( false ); + fTitleField->SetEnabled( false ); + fAudio1Field->SetEnabled( false ); + fAudio2Field->SetEnabled( false ); + fVideoSlider->SetEnabled( true ); + fAudioSlider->SetEnabled( true ); + fFileControl->SetEnabled( true ); + fAdvancedButton->SetEnabled( true ); + fFileString->SetHighColor( 0, 0, 0 ); + fFileString->Invalidate(); + fPictureButton->SetEnabled( false ); + fRefreshButton->SetEnabled( false ); + fSuspendButton->SetEnabled( false ); + fStartButton->SetEnabled( false ); + break; + + case ENABLE_READY: + fVolumeField->SetEnabled( true ); + fTitleField->SetEnabled( true ); + fAudio1Field->SetEnabled( true ); + fAudio2Field->SetEnabled( true ); + fVideoSlider->SetEnabled( true ); + fAudioSlider->SetEnabled( true ); + fFileControl->SetEnabled( true ); + fAdvancedButton->SetEnabled( true ); + fFileString->SetHighColor( 0, 0, 0 ); + fFileString->Invalidate(); + fPictureButton->SetEnabled( true ); + fRefreshButton->SetEnabled( true ); + fSuspendButton->SetEnabled( false ); + fStartButton->SetEnabled( true ); + break; + + case ENABLE_ENCODING: + fVolumeField->SetEnabled( false ); + fTitleField->SetEnabled( false ); + fAudio1Field->SetEnabled( false ); + fAudio2Field->SetEnabled( false ); + fVideoSlider->SetEnabled( false ); + fAudioSlider->SetEnabled( false ); + fFileControl->SetEnabled( false ); + fAdvancedButton->SetEnabled( false ); + fFileString->SetHighColor( 156, 156, 156 ); + fFileString->Invalidate(); + fPictureButton->SetEnabled( false ); + fRefreshButton->SetEnabled( false ); + fSuspendButton->SetEnabled( true ); + fStartButton->SetEnabled( true ); + break; + } +} + +void HBWindow::RefreshVolumes( BList * volumeList ) +{ + if( !( Lock() ) ) + { + Log( "HBWindow::RefreshVolumes : Lock() failed" ); + return; + } + + /* Empty the PopUps */ + HBVolumeInfo * volumeInfo; + while( ( volumeInfo = (HBVolumeInfo*) fVolumePopUp->ItemAt( 0 ) ) ) + { + fVolumePopUp->RemoveItem( volumeInfo ); + } + + HBTitleInfo * titleInfo; + while( ( titleInfo = (HBTitleInfo*) fTitlePopUp->ItemAt( 0 ) ) ) + { + fTitlePopUp->RemoveItem( titleInfo ); + } + + HBAudioInfo * audioInfo; + while( ( audioInfo = (HBAudioInfo*) fAudio1PopUp->ItemAt( 0 ) ) ) + { + fAudio1PopUp->RemoveItem( audioInfo ); + } + while( ( audioInfo = (HBAudioInfo*) fAudio2PopUp->ItemAt( 0 ) ) ) + { + fAudio2PopUp->RemoveItem( audioInfo ); + } + + /* Fill the Volumes PopUp */ + for( int i = 0; i < volumeList->CountItems(); i++ ) + { + fVolumePopUp->AddItem( (HBVolumeInfo*) volumeList->ItemAt( i ) ); + } + + /* Select the first volume */ + if( !( volumeInfo = (HBVolumeInfo*) volumeList->ItemAt( 0 ) ) ) + { + Log( "HBWindow::RefreshVolumes : no volume found" ); + Unlock(); + return; + } + volumeInfo->SetMarked( true ); + + /* Fill the Titles PopUp */ + BList * titleList = volumeInfo->fTitleList; + for( int i = 0; i < titleList->CountItems(); i++ ) + { + fTitlePopUp->AddItem( (HBTitleInfo*) titleList->ItemAt( i ) ); + } + + /* Select the first title */ + if( !( titleInfo = (HBTitleInfo*) titleList->ItemAt( 0 ) ) ) + { + Log( "HBWindow::RefreshVolumes : no title found" ); + Unlock(); + return; + } + titleInfo->SetMarked( true ); + + /* Fill the Audios PopUp */ + BList * audioList1 = titleInfo->fAudioInfoList1; + BList * audioList2 = titleInfo->fAudioInfoList2; + for( int i = 0; i < audioList1->CountItems(); i++ ) + { + fAudio1PopUp->AddItem( (HBAudioInfo*) audioList1->ItemAt( i ) ); + fAudio2PopUp->AddItem( (HBAudioInfo*) audioList2->ItemAt( i ) ); + } + + audioInfo = (HBAudioInfo*) fAudio1PopUp->ItemAt( 0 ); + audioInfo->SetMarked( true ); + audioInfo = (HBAudioInfo*) fAudio2PopUp->ItemAt( fAudio2PopUp->CountItems() - 1 ); + audioInfo->SetMarked( true ); + + Unlock(); +} + +void HBWindow::SelectionChanged() +{ + HBVolumeInfo * volumeInfo; + HBTitleInfo * titleInfo; + HBAudioInfo * audioInfo; + + /* Update the Title popup if needed */ + bool updateTitlePopUp = true; + volumeInfo = (HBVolumeInfo*) fVolumePopUp->FindMarked(); + titleInfo = (HBTitleInfo*) fTitlePopUp->FindMarked(); + + for( int i = 0; i < volumeInfo->fTitleList->CountItems(); i++ ) + { + if( titleInfo == volumeInfo->fTitleList->ItemAt( i ) ) + { + /* No need to update titles, we already are on the right + volume */ + updateTitlePopUp = false; + break; + } + } + + if( updateTitlePopUp ) + { + /* Empty the popup */ + while( ( titleInfo = (HBTitleInfo*) fTitlePopUp->ItemAt( 0 ) ) ) + { + fTitlePopUp->RemoveItem( titleInfo ); + } + + /* Fill it */ + for( int i = 0; i < volumeInfo->fTitleList->CountItems(); i++ ) + { + fTitlePopUp->AddItem( (HBTitleInfo*) volumeInfo->fTitleList->ItemAt( i ) ); + } + + /* Select the first title */ + ((HBTitleInfo*) fTitlePopUp->ItemAt( 0 ))->SetMarked( true ); + } + + /* Update the Audio popups if needed */ + bool updateAudioPopUp = true; + titleInfo = (HBTitleInfo*) fTitlePopUp->FindMarked(); + audioInfo = (HBAudioInfo*) fAudio1PopUp->FindMarked(); + + for( int i = 0; i < titleInfo->fAudioInfoList1->CountItems(); i++ ) + { + if( audioInfo == titleInfo->fAudioInfoList1->ItemAt( i ) ) + { + /* No need to update audio, we already are on the right + title */ + updateAudioPopUp = false; + break; + } + } + + if( updateAudioPopUp ) + { + /* Empty the popups */ + while( ( audioInfo = (HBAudioInfo*) fAudio1PopUp->ItemAt( 0 ) ) ) + { + fAudio1PopUp->RemoveItem( audioInfo ); + } + while( ( audioInfo = (HBAudioInfo*) fAudio2PopUp->ItemAt( 0 ) ) ) + { + fAudio2PopUp->RemoveItem( audioInfo ); + } + + /* Fill it */ + for( int i = 0; i < titleInfo->fAudioInfoList1->CountItems(); i++ ) + { + fAudio1PopUp->AddItem( (HBAudioInfo*) titleInfo->fAudioInfoList1->ItemAt( i ) ); + fAudio2PopUp->AddItem( (HBAudioInfo*) titleInfo->fAudioInfoList2->ItemAt( i ) ); + } + + /* Select the first track */ + ((HBAudioInfo*) fAudio1PopUp->ItemAt( 0 ))->SetMarked( true ); + + /* Select "None" */ + ((HBAudioInfo*) fAudio2PopUp->ItemAt( fAudio2PopUp->CountItems() - 1 ))->SetMarked( true ); + } + +} diff --git a/HBWindow.h b/HBWindow.h new file mode 100644 index 000000000..73aa895b3 --- /dev/null +++ b/HBWindow.h @@ -0,0 +1,62 @@ +/* $Id: HBWindow.h,v 1.8 2003/08/24 19:28:18 titer Exp $ */ + +#ifndef _HB_WINDOW_H +#define _HB_WINDOW_H + +#include <Box.h> +#include <Window.h> + +class HBManager; + +class BButton; +class BMenuField; +class BPopUpMenu; +class BSlider; +class BStatusBar; +class BStringView; +class BTextControl; + +class HBBox : public BBox +{ + public: + HBBox( BRect ); + virtual void Draw( BRect ); +}; + +class HBWindow : public BWindow +{ + public: + HBWindow(); + virtual bool QuitRequested(); + virtual void MessageReceived( BMessage * message ); + + void RefreshVolumes( BList * volumeList ); + + private: + void Enable( int mode ); + void SelectionChanged(); + + HBManager * fManager; + HBBox * fBox; + BMenuField * fVolumeField; + BPopUpMenu * fVolumePopUp; + BMenuField * fTitleField; + BPopUpMenu * fTitlePopUp; + BMenuField * fAudio1Field; + BPopUpMenu * fAudio1PopUp; + BMenuField * fAudio2Field; + BPopUpMenu * fAudio2PopUp; + BSlider * fVideoSlider; + BSlider * fAudioSlider; + BStringView * fFileString; + BTextControl * fFileControl; + BButton * fFileButton; + BButton * fPictureButton; + BButton * fAdvancedButton; + BStatusBar * fStatusBar; + BButton * fRefreshButton; + BButton * fSuspendButton; + BButton * fStartButton; +}; + +#endif diff --git a/HandBrake.cpp b/HandBrake.cpp new file mode 100644 index 000000000..91a2628b4 --- /dev/null +++ b/HandBrake.cpp @@ -0,0 +1,18 @@ +/* $Id: HandBrake.cpp,v 1.1.1.1 2003/06/24 13:43:48 titer Exp $ */ + +#include "HBApp.h" + +#include <ffmpeg/avcodec.h> + +int main() +{ + /* libavcodec initializations */ + avcodec_init(); + register_avcodec( &mpeg4_encoder ); + + /* Run the BApplication */ + HBApp * app = new HBApp(); + app->Run(); + delete app; + return 0; +} diff --git a/Jamfile b/Jamfile new file mode 100644 index 000000000..a3eeaeabd --- /dev/null +++ b/Jamfile @@ -0,0 +1,14 @@ +# $Id: Jamfile,v 1.13 2003/08/24 21:04:14 titer Exp $ + +HB_VERSION = 0.1 ; + +C++FLAGS = $(CPPFLAGS) ; +C++FLAGS += -g -Wall -Werror -Wno-multichar -O3 -funroll-loops ; +C++FLAGS += -DVERSION=\\\"$(HB_VERSION)\\\" ; +LINKFLAGS = $(LDFLAGS) ; +LINKLIBS = -lbe -ldvdplay -ldvdread -ldvdcss -lmpeg2 -lavcodec -la52 -lmp3lame ; + +Main HandBrake : + HandBrake.cpp HBAc3Decoder.cpp HBApp.cpp HBAviMuxer.cpp HBCommon.cpp + HBDVDReader.cpp HBFifo.cpp HBManager.cpp HBMp3Encoder.cpp HBMpeg2Decoder.cpp + HBMpeg4Encoder.cpp HBMpegDemux.cpp HBPictureWin.cpp HBThread.cpp HBWindow.cpp ; @@ -0,0 +1,15 @@ +$Id: NEWS,v 1.4 2003/08/24 20:25:49 titer Exp $ + +Changes between 0.1-alpha2 and 0.1 : + - Automatically detect ripped DVDs on BFS volumes + - Allow picture cropping and resizing + - Allow dual-audio encoding + - Created files are quite compliant now (tested with OSX/Quicktime and BSPlayer) + - Better A/V sync with some DVDs + +Changes between 0.1-alpha and 0.1-alpha2 : + - Show length for each title + - Fixed the screwed-audio bug + - Many bugfixes... + +First version is 0.1-alpha.
\ No newline at end of file |