2009-07-16 16:48:47 +02:00
// Copyright (c) 2008-2009, Karl Blomster
2008-09-23 22:01:11 +02:00
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// * Neither the name of the Aegisub Group nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
2009-07-29 07:43:02 +02:00
// Aegisub Project http://www.aegisub.org/
/// @file ffmpegsource_common.cpp
2009-07-30 08:40:25 +02:00
/// @brief Shared code for ffms video and audio providers
/// @ingroup video_input audio_input ffms
2009-07-29 07:43:02 +02:00
///
2008-09-23 22:01:11 +02:00
2011-12-22 22:25:49 +01:00
# ifdef WITH_FFMS2
2013-01-04 02:31:38 +01:00
# include "ffmpegsource_common.h"
2010-06-09 01:21:39 +02:00
2010-05-21 03:13:36 +02:00
# include "compat.h"
2013-01-07 02:50:09 +01:00
# include "options.h"
2012-04-04 00:44:40 +02:00
# include "utils.h"
2009-05-03 20:05:30 +02:00
2014-03-25 17:51:38 +01:00
# include <libaegisub/background_runner.h>
2013-01-04 16:01:50 +01:00
# include <libaegisub/fs.h>
2013-01-30 04:35:37 +01:00
# include <libaegisub/path.h>
2013-01-04 02:31:38 +01:00
2013-01-04 16:01:50 +01:00
# include <boost/algorithm/string/predicate.hpp>
2013-01-04 02:31:38 +01:00
# include <boost/crc.hpp>
2014-05-23 00:40:16 +02:00
# include <boost/filesystem/path.hpp>
# include <wx/intl.h>
# include <wx/choicdlg.h>
2013-01-04 02:31:38 +01:00
2013-01-04 16:01:50 +01:00
# ifdef _WIN32
# include <objbase.h>
2012-02-20 19:22:12 +01:00
static void deinit_com ( bool ) {
CoUninitialize ( ) ;
}
# else
static void deinit_com ( bool ) { }
# endif
2014-03-25 17:51:38 +01:00
FFmpegSourceProvider : : FFmpegSourceProvider ( agi : : BackgroundRunner * br )
2012-02-20 19:22:12 +01:00
: COMInited ( false , deinit_com )
2014-03-25 17:51:38 +01:00
, br ( br )
2012-02-20 19:22:12 +01:00
{
2013-01-04 16:01:50 +01:00
# ifdef _WIN32
2012-11-13 17:51:01 +01:00
HRESULT res = CoInitializeEx ( nullptr , COINIT_APARTMENTTHREADED ) ;
2012-02-20 19:22:12 +01:00
if ( SUCCEEDED ( res ) )
COMInited = true ;
else if ( res ! = RPC_E_CHANGED_MODE )
throw " COM initialization failure " ;
# endif
// initialize ffmpegsource
FFMS_Init ( 0 , 1 ) ;
}
2009-05-03 20:05:30 +02:00
2009-08-05 13:21:00 +02:00
/// @brief Does indexing of a source file
/// @param Indexer A pointer to the indexer object representing the file to be indexed
/// @param CacheName The filename of the output index file
/// @param Trackmask A binary mask of the track numbers to index
2013-01-04 16:01:50 +01:00
FFMS_Index * FFmpegSourceProvider : : DoIndexing ( FFMS_Indexer * Indexer , agi : : fs : : path const & CacheName , int Trackmask , FFMS_IndexErrorHandling IndexEH ) {
2012-03-25 06:05:06 +02:00
char FFMSErrMsg [ 1024 ] ;
2009-09-26 23:58:00 +02:00
FFMS_ErrorInfo ErrInfo ;
ErrInfo . Buffer = FFMSErrMsg ;
ErrInfo . BufferSize = sizeof ( FFMSErrMsg ) ;
ErrInfo . ErrorType = FFMS_ERROR_SUCCESS ;
ErrInfo . SubType = FFMS_ERROR_SUCCESS ;
2013-01-04 16:01:50 +01:00
std : : string MsgString ;
2008-09-24 01:30:27 +02:00
// index all audio tracks
2011-09-28 21:47:40 +02:00
FFMS_Index * Index ;
2014-03-25 17:51:38 +01:00
br - > Run ( [ & ] ( agi : : ProgressSink * ps ) {
ps - > SetTitle ( from_wx ( _ ( " Indexing " ) ) ) ;
ps - > SetMessage ( from_wx ( _ ( " Reading timecodes and frame/sample data " ) ) ) ;
2013-12-11 21:30:27 +01:00
TIndexCallback callback = [ ] ( int64_t Current , int64_t Total , void * Private ) - > int {
2014-05-04 01:58:45 +02:00
auto ps = static_cast < agi : : ProgressSink * > ( Private ) ;
ps - > SetProgress ( Current , Total ) ;
return ps - > IsCancelled ( ) ;
2013-12-11 21:30:27 +01:00
} ;
Index = FFMS_DoIndexing ( Indexer , Trackmask , FFMS_TRACKMASK_NONE ,
2014-05-04 01:58:45 +02:00
nullptr , nullptr , IndexEH , callback , ps , & ErrInfo ) ;
2012-09-25 03:15:20 +02:00
} ) ;
2011-09-28 21:47:40 +02:00
2012-11-13 17:51:01 +01:00
if ( Index = = nullptr ) {
2013-01-04 16:01:50 +01:00
MsgString + = " Failed to index: " ;
MsgString + = ErrInfo . Buffer ;
2008-09-24 01:30:27 +02:00
throw MsgString ;
}
// write index to disk for later use
2013-01-04 16:01:50 +01:00
FFMS_WriteIndex ( CacheName . string ( ) . c_str ( ) , Index , & ErrInfo ) ;
2008-09-24 01:30:27 +02:00
return Index ;
}
2012-03-25 06:05:06 +02:00
/// @brief Finds all tracks of the given type and return their track numbers and respective codec names
2009-08-05 13:21:00 +02:00
/// @param Indexer The indexer object representing the source file
/// @param Type The track type to look for
/// @return Returns a std::map with the track numbers as keys and the codec names as values.
2013-01-04 16:01:50 +01:00
std : : map < int , std : : string > FFmpegSourceProvider : : GetTracksOfType ( FFMS_Indexer * Indexer , FFMS_TrackType Type ) {
std : : map < int , std : : string > TrackList ;
2009-07-16 16:48:47 +02:00
int NumTracks = FFMS_GetNumTracksI ( Indexer ) ;
for ( int i = 0 ; i < NumTracks ; i + + ) {
if ( FFMS_GetTrackTypeI ( Indexer , i ) = = Type ) {
2013-12-10 21:37:03 +01:00
const char * CodecName = FFMS_GetCodecNameI ( Indexer , i ) ;
if ( CodecName )
TrackList . insert ( std : : pair < int , std : : string > ( i , CodecName ) ) ;
2009-07-16 16:48:47 +02:00
}
}
return TrackList ;
}
2012-03-25 06:05:06 +02:00
/// @brief Ask user for which track he wants to load
2009-08-05 13:21:00 +02:00
/// @param TrackList A std::map with the track numbers as keys and codec names as values
/// @param Type The track type to ask about
/// @return Returns the track number chosen (an integer >= 0) on success, or a negative integer if the user cancelled.
2013-01-04 16:01:50 +01:00
int FFmpegSourceProvider : : AskForTrackSelection ( const std : : map < int , std : : string > & TrackList , FFMS_TrackType Type ) {
2009-07-16 16:48:47 +02:00
std : : vector < int > TrackNumbers ;
wxArrayString Choices ;
2012-03-25 06:05:06 +02:00
2012-11-04 04:53:03 +01:00
for ( auto const & track : TrackList ) {
2013-01-04 16:01:50 +01:00
Choices . Add ( wxString : : Format ( _ ( " Track %02d: %s " ) , track . first , to_wx ( track . second ) ) ) ;
2012-11-04 04:53:03 +01:00
TrackNumbers . push_back ( track . first ) ;
2009-07-16 16:48:47 +02:00
}
2012-03-25 06:05:06 +02:00
2012-01-08 02:05:13 +01:00
int Choice = wxGetSingleChoiceIndex (
Type = = FFMS_TYPE_VIDEO ? _ ( " Multiple video tracks detected, please choose the one you wish to load: " ) : _ ( " Multiple audio tracks detected, please choose the one you wish to load: " ) ,
Type = = FFMS_TYPE_VIDEO ? _ ( " Choose video track " ) : _ ( " Choose audio track " ) ,
Choices ) ;
2009-07-16 16:48:47 +02:00
if ( Choice < 0 )
return Choice ;
else
return TrackNumbers [ Choice ] ;
}
2012-03-25 06:05:06 +02:00
/// @brief Set ffms2 log level according to setting in config.dat
2009-07-24 03:41:16 +02:00
void FFmpegSourceProvider : : SetLogLevel ( ) {
2013-01-04 16:01:50 +01:00
std : : string LogLevel = OPT_GET ( " Provider/FFmpegSource/Log Level " ) - > GetString ( ) ;
2009-07-24 03:41:16 +02:00
2013-01-04 16:01:50 +01:00
if ( boost : : iequals ( LogLevel , " panic " ) )
2009-07-24 03:41:16 +02:00
FFMS_SetLogLevel ( FFMS_LOG_PANIC ) ;
2013-01-04 16:01:50 +01:00
else if ( boost : : iequals ( LogLevel , " fatal " ) )
2009-07-24 03:41:16 +02:00
FFMS_SetLogLevel ( FFMS_LOG_FATAL ) ;
2013-01-04 16:01:50 +01:00
else if ( boost : : iequals ( LogLevel , " error " ) )
2009-07-24 03:41:16 +02:00
FFMS_SetLogLevel ( FFMS_LOG_ERROR ) ;
2013-01-04 16:01:50 +01:00
else if ( boost : : iequals ( LogLevel , " warning " ) )
2009-07-24 03:41:16 +02:00
FFMS_SetLogLevel ( FFMS_LOG_WARNING ) ;
2013-01-04 16:01:50 +01:00
else if ( boost : : iequals ( LogLevel , " info " ) )
2009-07-24 03:41:16 +02:00
FFMS_SetLogLevel ( FFMS_LOG_INFO ) ;
2013-01-04 16:01:50 +01:00
else if ( boost : : iequals ( LogLevel , " verbose " ) )
2009-07-24 03:41:16 +02:00
FFMS_SetLogLevel ( FFMS_LOG_VERBOSE ) ;
2013-01-04 16:01:50 +01:00
else if ( boost : : iequals ( LogLevel , " debug " ) )
2009-07-24 03:41:16 +02:00
FFMS_SetLogLevel ( FFMS_LOG_DEBUG ) ;
else
FFMS_SetLogLevel ( FFMS_LOG_QUIET ) ;
}
2009-11-28 22:13:47 +01:00
FFMS_IndexErrorHandling FFmpegSourceProvider : : GetErrorHandlingMode ( ) {
2013-01-04 16:01:50 +01:00
std : : string Mode = OPT_GET ( " Provider/Audio/FFmpegSource/Decode Error Handling " ) - > GetString ( ) ;
2009-11-28 22:13:47 +01:00
2013-01-04 16:01:50 +01:00
if ( boost : : iequals ( Mode , " ignore " ) )
2009-11-28 22:13:47 +01:00
return FFMS_IEH_IGNORE ;
2013-01-04 16:01:50 +01:00
else if ( boost : : iequals ( Mode , " clear " ) )
2009-11-28 22:13:47 +01:00
return FFMS_IEH_CLEAR_TRACK ;
2013-01-04 16:01:50 +01:00
else if ( boost : : iequals ( Mode , " stop " ) )
2009-11-28 22:13:47 +01:00
return FFMS_IEH_STOP_TRACK ;
2013-01-04 16:01:50 +01:00
else if ( boost : : iequals ( Mode , " abort " ) )
2009-11-28 22:13:47 +01:00
return FFMS_IEH_ABORT ;
else
return FFMS_IEH_STOP_TRACK ; // questionable default?
}
2012-03-25 06:05:06 +02:00
/// @brief Generates an unique name for the ffms2 index file and prepares the cache folder if it doesn't exist
2009-08-05 13:21:00 +02:00
/// @param filename The name of the source file
/// @return Returns the generated filename.
2013-01-04 16:01:50 +01:00
agi : : fs : : path FFmpegSourceProvider : : GetCacheFilename ( agi : : fs : : path const & filename ) {
2008-10-28 05:03:29 +01:00
// Get the size of the file to be hashed
2013-01-04 16:01:50 +01:00
uintmax_t len = agi : : fs : : Size ( filename ) ;
2009-09-27 02:32:19 +02:00
2013-01-04 02:31:38 +01:00
// Get the hash of the filename
boost : : crc_32_type hash ;
2013-01-04 16:01:50 +01:00
hash . process_bytes ( filename . string ( ) . c_str ( ) , filename . string ( ) . size ( ) ) ;
2008-10-28 05:03:29 +01:00
// Generate the filename
2013-01-30 04:35:37 +01:00
auto result = config : : path - > Decode ( " ?local/ffms2cache/ " + std : : to_string ( hash . checksum ( ) ) + " _ " + std : : to_string ( len ) + " _ " + std : : to_string ( agi : : fs : : ModifiedTime ( filename ) ) + " .ffindex " ) ;
2008-10-28 05:03:29 +01:00
// Ensure that folder exists
2013-01-04 16:01:50 +01:00
agi : : fs : : CreateDirectory ( result . parent_path ( ) ) ;
2008-10-28 05:03:29 +01:00
2013-01-04 16:01:50 +01:00
return result ;
2008-10-28 05:03:29 +01:00
}
2009-08-05 13:21:00 +02:00
/// @brief Starts the cache cleaner thread
2012-04-04 00:44:40 +02:00
void FFmpegSourceProvider : : CleanCache ( ) {
2013-01-30 04:35:37 +01:00
: : CleanCache ( config : : path - > Decode ( " ?local/ffms2cache/ " ) ,
2012-04-04 00:44:40 +02:00
" *.ffindex " ,
OPT_GET ( " Provider/FFmpegSource/Cache/Size " ) - > GetInt ( ) ,
OPT_GET ( " Provider/FFmpegSource/Cache/Files " ) - > GetInt ( ) ) ;
2009-05-03 20:05:30 +02:00
}
2011-12-22 22:25:49 +01:00
# endif // WITH_FFMS2