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"
2014-05-29 17:28:37 +02:00
# include "format.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
2014-06-11 04:46:58 +02:00
# include <boost/algorithm/string/case_conv.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
2014-03-25 17:51:38 +01:00
FFmpegSourceProvider : : FFmpegSourceProvider ( agi : : BackgroundRunner * br )
2016-04-04 20:24:14 +02:00
: br ( br )
2012-02-20 19:22:12 +01:00
{
2018-05-08 01:24:47 +02:00
FFMS_Init ( 0 , 0 ) ;
2012-02-20 19:22:12 +01:00
}
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
2016-04-06 20:24:21 +02:00
FFMS_Index * FFmpegSourceProvider : : DoIndexing ( FFMS_Indexer * Indexer ,
agi : : fs : : path const & CacheName ,
TrackSelection Track ,
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 ;
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
} ;
2016-04-06 20:24:21 +02:00
if ( Track = = TrackSelection : : All )
FFMS_TrackTypeIndexSettings ( Indexer , FFMS_TYPE_AUDIO , 1 , 0 ) ;
else if ( Track ! = TrackSelection : : None )
FFMS_TrackIndexSettings ( Indexer , static_cast < int > ( Track ) , 1 , 0 ) ;
2018-12-30 20:08:21 +01:00
FFMS_TrackTypeIndexSettings ( Indexer , FFMS_TYPE_VIDEO , 1 , 0 ) ;
2016-04-06 20:24:21 +02:00
FFMS_SetProgressCallback ( Indexer , callback , ps ) ;
Index = FFMS_DoIndexing2 ( Indexer , IndexEH , & ErrInfo ) ;
2012-09-25 03:15:20 +02:00
} ) ;
2011-09-28 21:47:40 +02:00
2014-06-11 00:28:45 +02:00
if ( Index = = nullptr )
throw agi : : EnvironmentError ( std : : string ( " Failed to index: " ) + ErrInfo . Buffer ) ;
2008-09-24 01:30:27 +02:00
// 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 ) {
2016-04-06 20:24:21 +02:00
if ( auto CodecName = FFMS_GetCodecNameI ( Indexer , i ) )
TrackList [ i ] = CodecName ;
2009-07-16 16:48:47 +02:00
}
}
return TrackList ;
}
2016-04-06 20:24:21 +02:00
FFmpegSourceProvider : : TrackSelection
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 ) {
2015-01-01 04:41:56 +01:00
Choices . Add ( agi : : wxformat ( _ ( " Track %02d: %s " ) , track . first , 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 )
2016-04-06 20:24:21 +02:00
return TrackSelection : : None ;
return static_cast < TrackSelection > ( TrackNumbers [ Choice ] ) ;
2009-07-16 16:48:47 +02:00
}
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 ( ) {
2014-06-11 04:46:58 +02:00
auto LogLevel = OPT_GET ( " Provider/FFmpegSource/Log Level " ) - > GetString ( ) ;
boost : : to_lower ( LogLevel ) ;
2009-07-24 03:41:16 +02:00
2014-06-11 04:46:58 +02:00
if ( LogLevel = = " panic " )
2009-07-24 03:41:16 +02:00
FFMS_SetLogLevel ( FFMS_LOG_PANIC ) ;
2014-06-11 04:46:58 +02:00
else if ( LogLevel = = " fatal " )
2009-07-24 03:41:16 +02:00
FFMS_SetLogLevel ( FFMS_LOG_FATAL ) ;
2014-06-11 04:46:58 +02:00
else if ( LogLevel = = " error " )
2009-07-24 03:41:16 +02:00
FFMS_SetLogLevel ( FFMS_LOG_ERROR ) ;
2014-06-11 04:46:58 +02:00
else if ( LogLevel = = " warning " )
2009-07-24 03:41:16 +02:00
FFMS_SetLogLevel ( FFMS_LOG_WARNING ) ;
2014-06-11 04:46:58 +02:00
else if ( LogLevel = = " info " )
2009-07-24 03:41:16 +02:00
FFMS_SetLogLevel ( FFMS_LOG_INFO ) ;
2014-06-11 04:46:58 +02:00
else if ( LogLevel = = " verbose " )
2009-07-24 03:41:16 +02:00
FFMS_SetLogLevel ( FFMS_LOG_VERBOSE ) ;
2014-06-11 04:46:58 +02:00
else if ( 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 ( ) {
2014-06-11 04:46:58 +02:00
auto Mode = OPT_GET ( " Provider/Audio/FFmpegSource/Decode Error Handling " ) - > GetString ( ) ;
boost : : to_lower ( Mode ) ;
2009-11-28 22:13:47 +01:00
2014-06-11 04:46:58 +02:00
if ( Mode = = " ignore " )
2009-11-28 22:13:47 +01:00
return FFMS_IEH_IGNORE ;
2014-06-11 04:46:58 +02:00
if ( Mode = = " clear " )
2009-11-28 22:13:47 +01:00
return FFMS_IEH_CLEAR_TRACK ;
2014-06-11 04:46:58 +02:00
if ( Mode = = " stop " )
2009-11-28 22:13:47 +01:00
return FFMS_IEH_STOP_TRACK ;
2014-06-11 04:46:58 +02:00
if ( Mode = = " abort " )
2009-11-28 22:13:47 +01:00
return FFMS_IEH_ABORT ;
2014-06-11 04:46:58 +02:00
return FFMS_IEH_STOP_TRACK ; // questionable default?
2009-11-28 22:13:47 +01:00
}
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
}
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