diff --git a/src/AvTranscoder/file/InputFile.cpp b/src/AvTranscoder/file/InputFile.cpp index 38086a24..72e95f77 100644 --- a/src/AvTranscoder/file/InputFile.cpp +++ b/src/AvTranscoder/file/InputFile.cpp @@ -63,7 +63,7 @@ bool InputFile::readNextPacket( CodedData& data, const size_t streamIndex ) const int ret = av_read_frame( &_formatContext.getAVFormatContext(), &data.getAVPacket() ); if( ret < 0 ) // error or end of file { - LOG_INFO( "No more data to read on file '" << _filename << "'" ) + LOG_INFO( "No more data to read on file '" << _filename << "' for stream " << streamIndex ) return false; } diff --git a/src/AvTranscoder/transcoder/StreamTranscoder.cpp b/src/AvTranscoder/transcoder/StreamTranscoder.cpp index 4e90eead..7acb815b 100644 --- a/src/AvTranscoder/transcoder/StreamTranscoder.cpp +++ b/src/AvTranscoder/transcoder/StreamTranscoder.cpp @@ -556,9 +556,9 @@ void StreamTranscoder::setOffset( const float offset ) StreamTranscoder::EProcessCase StreamTranscoder::getProcessCase() const { - if( _inputStream && _inputDecoder ) + if( _inputStream && _inputDecoder && _currentDecoder == _inputDecoder ) return eProcessCaseTranscode; - else if( _inputStream && ! _inputDecoder ) + else if( _inputStream && ! _inputDecoder && ! _currentDecoder ) return eProcessCaseRewrap; else return eProcessCaseGenerator; diff --git a/src/AvTranscoder/transcoder/StreamTranscoder.hpp b/src/AvTranscoder/transcoder/StreamTranscoder.hpp index 912af924..181a90e3 100644 --- a/src/AvTranscoder/transcoder/StreamTranscoder.hpp +++ b/src/AvTranscoder/transcoder/StreamTranscoder.hpp @@ -101,10 +101,6 @@ class AvExport StreamTranscoder */ void setOffset( const float offset ); -private: - bool processRewrap(); - bool processTranscode( const int subStreamIndex = -1 ); ///< By default transcode all channels - //@{ // Get the current process case. enum EProcessCase { @@ -115,6 +111,10 @@ class AvExport StreamTranscoder EProcessCase getProcessCase() const; //@} +private: + bool processRewrap(); + bool processTranscode( const int subStreamIndex = -1 ); ///< By default transcode all channels + private: IInputStream* _inputStream; ///< Input stream to read next packet (has link, no ownership) IOutputStream* _outputStream; ///< Output stream to wrap next packet (has link, no ownership) diff --git a/src/AvTranscoder/transcoder/Transcoder.cpp b/src/AvTranscoder/transcoder/Transcoder.cpp index 984bf34d..49bf3403 100644 --- a/src/AvTranscoder/transcoder/Transcoder.cpp +++ b/src/AvTranscoder/transcoder/Transcoder.cpp @@ -16,7 +16,7 @@ Transcoder::Transcoder( IOutputFile& outputFile ) , _streamTranscoders() , _streamTranscodersAllocated() , _profileLoader( true ) - , _eProcessMethod ( eProcessMethodBasedOnStream ) + , _eProcessMethod ( eProcessMethodProcessAll ) , _mainStreamIndex( 0 ) , _outputDuration( 0 ) {} @@ -209,16 +209,33 @@ bool Transcoder::processFrame() if( _streamTranscoders.size() == 0 ) return false; + // For each stream, process a frame + size_t nbStreamProcessStatusFailed = 0; for( size_t streamIndex = 0; streamIndex < _streamTranscoders.size(); ++streamIndex ) { LOG_DEBUG( "Process stream " << streamIndex << "/" << ( _streamTranscoders.size() - 1 ) ) - - bool streamProcessStatus = _streamTranscoders.at( streamIndex )->processFrame(); - if( ! streamProcessStatus ) + const bool currentStreamProcessStatus = _streamTranscoders.at( streamIndex )->processFrame(); + if( ! currentStreamProcessStatus ) { - return false; + LOG_WARN( "Failed to process stream " << streamIndex ) + ++nbStreamProcessStatusFailed; } } + + // Get the number of streams without the generators (they always succeed) + size_t nbStreamsWithoutGenerator = _streamTranscoders.size(); + for( size_t streamIndex = 0; streamIndex < _streamTranscoders.size(); ++streamIndex ) + { + if( _streamTranscoders.at( streamIndex )->getProcessCase() == StreamTranscoder::eProcessCaseGenerator ) + --nbStreamsWithoutGenerator; + } + + // If all streams failed to process a new frame + if( nbStreamsWithoutGenerator != 0 && nbStreamProcessStatusFailed == nbStreamsWithoutGenerator ) + { + LOG_INFO( "End of process because all streams (except generators) failed to process a new frame." ) + return false; + } return true; } @@ -241,32 +258,37 @@ ProcessStat Transcoder::process( IProgress& progress ) preProcessCodecLatency(); - const float outputDuration = getOutputDuration(); - LOG_INFO( "Output duration of the process will be " << outputDuration << "s." ) + const float expectedOutputDuration = getExpectedOutputDuration(); + LOG_INFO( "Output duration of the process will be " << expectedOutputDuration << "s." ) size_t frame = 0; bool frameProcessed = true; while( frameProcessed ) { - const float progressDuration = _outputFile.getStream( 0 ).getStreamDuration(); + const float progressDuration = getCurrentOutputDuration(); // check if JobStatusCancel - if( progress.progress( ( progressDuration > outputDuration ) ? outputDuration : progressDuration, outputDuration ) == eJobStatusCancel ) + if( progress.progress( ( progressDuration > expectedOutputDuration ) ? expectedOutputDuration : progressDuration, expectedOutputDuration ) == eJobStatusCancel ) + { + LOG_INFO( "End of process because the job was canceled." ) break; + } // check progressDuration - if( progressDuration >= outputDuration ) + if( _eProcessMethod != eProcessMethodProcessAll && progressDuration >= expectedOutputDuration ) + { + LOG_INFO( "End of process because the output program duration (" << progressDuration << "s) is equal or upper than " << expectedOutputDuration << "s." ) break; + } LOG_DEBUG( "Process frame " << frame ) frameProcessed = processFrame(); - ++frame; } _outputFile.endWrap(); - LOG_INFO( "End of process" ) + LOG_INFO( "End of process: " << frame << " frames processed" ) LOG_INFO( "Get process statistics" ) ProcessStat processStat; @@ -460,7 +482,7 @@ float Transcoder::getMaxTotalDuration() const return maxTotalDuration; } -float Transcoder::getOutputDuration() const +float Transcoder::getExpectedOutputDuration() const { switch( _eProcessMethod ) { @@ -479,6 +501,20 @@ float Transcoder::getOutputDuration() const } } +float Transcoder::getCurrentOutputDuration() const +{ + float currentOutputDuration = -1; + for( size_t streamIndex = 0; streamIndex < _streamTranscoders.size(); ++streamIndex ) + { + const float currentStreamDuration = _outputFile.getStream( streamIndex ).getStreamDuration(); + if( currentOutputDuration == -1 ) + currentOutputDuration = currentStreamDuration; + else if( currentStreamDuration < currentOutputDuration ) + currentOutputDuration = currentStreamDuration; + } + return currentOutputDuration; +} + void Transcoder::manageSwitchToGenerator() { for( size_t i = 0; i < _streamTranscoders.size(); ++i ) diff --git a/src/AvTranscoder/transcoder/Transcoder.hpp b/src/AvTranscoder/transcoder/Transcoder.hpp index b0407859..6023d18a 100644 --- a/src/AvTranscoder/transcoder/Transcoder.hpp +++ b/src/AvTranscoder/transcoder/Transcoder.hpp @@ -17,12 +17,13 @@ namespace avtranscoder { /** - * @brief Enum to set a policy of how we manage the transcode in case of several streams. - * eProcessMethodShortest: stop transcode at the end of the shortest stream. - * eProcessMethodLongest: stop transcode at the end of the longest stream. - * eProcessMethodBasedOnStream: stop transcode at the end of an indicated stream (@see _indexBasedStream attribute of Transcoder). - * eProcessMethodBasedOnDuration: stop transcode at the end of an indicated duration, in seconds (@see _outputDuration attribute of Transcoder). - * eProcessMethodInfinity: stop transcode by outside of avTranscoder (streaming mode) + * @brief Enum to set a policy of how we manage the process in case of several streams. + * eProcessMethodShortest: stop the process at the end of the shortest stream. + * eProcessMethodLongest: stop the process at the end of the longest stream. + * eProcessMethodBasedOnStream: stop the process at the end of an indicated stream (@see _indexBasedStream attribute of Transcoder). + * eProcessMethodBasedOnDuration: stop the process at the end of an indicated duration, in seconds (@see _outputDuration attribute of Transcoder). + * eProcessMethodProcessAll: stop the process when all the input data are read. + * eProcessMethodInfinity: stop the process by outside of avTranscoder (streaming mode) */ enum EProcessMethod { @@ -30,6 +31,7 @@ enum EProcessMethod eProcessMethodLongest, eProcessMethodBasedOnStream, eProcessMethodBasedOnDuration, + eProcessMethodProcessAll, eProcessMethodInfinity, }; @@ -180,10 +182,17 @@ class AvExport Transcoder float getMaxTotalDuration() const; /** - * @brief Get the duration of the output program + * @brief Get the expected duration of the output program * @note Depends on the streams, the process method, and the main stream index. */ - float getOutputDuration() const; + float getExpectedOutputDuration() const; + + /** + * @brief Get the current duration of the output program + * @note Returns the duration of the smallest stream. + * @return -1 if there is no output stream. + */ + float getCurrentOutputDuration() const; /** * @brief Set for each StreamTranscoder if it can switch to generator at the end. @@ -204,7 +213,7 @@ class AvExport Transcoder ProfileLoader _profileLoader; ///< Objet to get existing profiles, and add new ones for the Transcoder. - EProcessMethod _eProcessMethod; ///< Transcoding policy + EProcessMethod _eProcessMethod; ///< Processing policy (eProcessMethodProcessAll by default) size_t _mainStreamIndex; ///< Index of stream used to stop the process of transcode in case of eProcessMethodBasedOnStream. float _outputDuration; ///< Duration of output media used to stop the process of transcode in case of eProcessMethodBasedOnDuration. }; diff --git a/test/pyTest/testEProcessMethod.py b/test/pyTest/testEProcessMethod.py index 2a431120..62b0d637 100644 --- a/test/pyTest/testEProcessMethod.py +++ b/test/pyTest/testEProcessMethod.py @@ -2,11 +2,13 @@ # Check if environment is setup to run the tests if os.environ.get('AVTRANSCODER_TEST_VIDEO_AVI_FILE') is None or \ + os.environ.get('AVTRANSCODER_TEST_VIDEO_MOV_FILE') is None or \ os.environ.get('AVTRANSCODER_TEST_AUDIO_MOV_FILE') is None or \ os.environ.get('AVTRANSCODER_TEST_AUDIO_WAVE_FILE') is None: from nose.plugins.skip import SkipTest raise SkipTest("Need to define environment variables " "AVTRANSCODER_TEST_VIDEO_AVI_FILE and " + "AVTRANSCODER_TEST_VIDEO_MOV_FILE and " "AVTRANSCODER_TEST_AUDIO_MOV_FILE and " "AVTRANSCODER_TEST_AUDIO_WAVE_FILE") @@ -41,7 +43,7 @@ def testEProcessMethodShortest(): dst_inputFile = av.InputFile( outputFileName ) dst_properties = dst_inputFile.getProperties() - assert_equals( dst_properties.getDuration(), src_properties_shortest.getDuration() ) + assert_equals( src_properties_shortest.getStreamProperties()[0].getDuration(), dst_properties.getStreamProperties()[1].getDuration() ) def testEProcessMethodLongest(): @@ -70,7 +72,7 @@ def testEProcessMethodLongest(): dst_inputFile = av.InputFile( outputFileName ) dst_properties = dst_inputFile.getProperties() - assert_equals( dst_properties.getDuration(), src_properties_longest.getDuration() ) + assert_equals( src_properties_longest.getStreamProperties()[0].getDuration(), dst_properties.getStreamProperties()[0].getDuration() ) def testEProcessMethodBasedOnStream(): @@ -105,6 +107,35 @@ def testEProcessMethodBasedOnStream(): assert_almost_equals( dst_stream_properties.getDuration(), src_properties_second.getDuration(), delta=0.05 ) +def testEProcessMethodProcessAll(): + """ + Process with method eProcessMethodProcessAll, check the duration of each output stream (which could be differentes). + """ + inputFileName = os.environ['AVTRANSCODER_TEST_VIDEO_MOV_FILE'] + outputFileName = "testEProcessMethodProcessAll.mov" + + ouputFile = av.OutputFile( outputFileName ) + transcoder = av.Transcoder( ouputFile ) + transcoder.setProcessMethod( av.eProcessMethodProcessAll ) + + transcoder.add( inputFileName, 0 ) + transcoder.add( inputFileName, 1 ) + + progress = av.ConsoleProgress() + transcoder.process( progress ) + + # get src file + src_inputFile = av.InputFile( inputFileName ) + src_properties = src_inputFile.getProperties() + + # get dst file + dst_inputFile = av.InputFile( outputFileName ) + dst_properties = dst_inputFile.getProperties() + + assert_equals( src_properties.getStreamProperties()[0].getDuration(), dst_properties.getStreamProperties()[0].getDuration() ) + assert_equals( src_properties.getStreamProperties()[1].getDuration(), dst_properties.getStreamProperties()[1].getDuration() ) + + def testEProcessMethodBasedOnDuration(): """ Process with method eProcessMethodBasedOnDuration, check output duration. @@ -130,5 +161,4 @@ def testEProcessMethodBasedOnDuration(): dst_inputFile = av.InputFile( outputFileName ) dst_properties = dst_inputFile.getProperties() - assert_equals( dst_properties.getDuration(), outputDuration ) - + assert_equals( dst_properties.getStreamProperties()[0].getDuration(), outputDuration ) pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy