Skip to content

v0.6.3: Fixed process in case of several streams bis #206

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/AvTranscoder/file/InputFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
4 changes: 2 additions & 2 deletions src/AvTranscoder/transcoder/StreamTranscoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
8 changes: 4 additions & 4 deletions src/AvTranscoder/transcoder/StreamTranscoder.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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)
Expand Down
62 changes: 49 additions & 13 deletions src/AvTranscoder/transcoder/Transcoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Transcoder::Transcoder( IOutputFile& outputFile )
, _streamTranscoders()
, _streamTranscodersAllocated()
, _profileLoader( true )
, _eProcessMethod ( eProcessMethodBasedOnStream )
, _eProcessMethod ( eProcessMethodProcessAll )
, _mainStreamIndex( 0 )
, _outputDuration( 0 )
{}
Expand Down Expand Up @@ -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;
}

Expand All @@ -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;
Expand Down Expand Up @@ -460,7 +482,7 @@ float Transcoder::getMaxTotalDuration() const
return maxTotalDuration;
}

float Transcoder::getOutputDuration() const
float Transcoder::getExpectedOutputDuration() const
{
switch( _eProcessMethod )
{
Expand All @@ -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 )
Expand Down
27 changes: 18 additions & 9 deletions src/AvTranscoder/transcoder/Transcoder.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,21 @@ 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
{
eProcessMethodShortest = 0,
eProcessMethodLongest,
eProcessMethodBasedOnStream,
eProcessMethodBasedOnDuration,
eProcessMethodProcessAll,
eProcessMethodInfinity,
};

Expand Down Expand Up @@ -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.
Expand All @@ -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.
};
Expand Down
38 changes: 34 additions & 4 deletions test/pyTest/testEProcessMethod.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")

Expand Down Expand Up @@ -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():
Expand Down Expand Up @@ -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():
Expand Down Expand Up @@ -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.
Expand All @@ -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 )