diff --git a/src/AvTranscoder/file/InputFile.cpp b/src/AvTranscoder/file/InputFile.cpp index 156fc565..074fc57c 100644 --- a/src/AvTranscoder/file/InputFile.cpp +++ b/src/AvTranscoder/file/InputFile.cpp @@ -68,7 +68,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 44b32d1c..5133e9bd 100644 --- a/src/AvTranscoder/transcoder/StreamTranscoder.cpp +++ b/src/AvTranscoder/transcoder/StreamTranscoder.cpp @@ -327,21 +327,41 @@ void StreamTranscoder::preProcessCodecLatency() return; // set a decoder to preload generated frames + bool wasARewrapCase = false; if(getProcessCase() == eProcessCaseRewrap) + { switchToGeneratorDecoder(); + wasARewrapCase = true; + } while((latency--) > 0) { processFrame(); } - if(getProcessCase() == eProcessCaseRewrap) + if(wasARewrapCase) _currentDecoder = NULL; } bool StreamTranscoder::processFrame() { - if(getProcessCase() == eProcessCaseGenerator) + const EProcessCase processCase = getProcessCase(); + std::string msg = "Current process case of the stream is a "; + switch(processCase) + { + case eProcessCaseTranscode: + msg += "transcode."; + break; + case eProcessCaseRewrap: + msg += "rewrap."; + break; + case eProcessCaseGenerator: + msg += "generator."; + break; + } + LOG_DEBUG(msg) + + if(processCase == eProcessCaseGenerator) return processTranscode(); // Manage offset @@ -363,7 +383,6 @@ bool StreamTranscoder::processFrame() // process generator if(_currentDecoder != _generator) { - LOG_INFO("Switch to generator to process offset") switchToGeneratorDecoder(); } } @@ -381,7 +400,7 @@ bool StreamTranscoder::processFrame() } } - if(getProcessCase() == eProcessCaseRewrap) + if(processCase == eProcessCaseRewrap) return processRewrap(); return processTranscode(_subStreamIndex); @@ -531,9 +550,10 @@ void StreamTranscoder::needToSwitchToGenerator(const bool needToSwitch) if(needToSwitch && !canSwitchToGenerator()) { std::stringstream os; - os << "The stream " << _inputStream->getStreamIndex() << " has a duration of " << getDuration() - << "s. It needs to switch to a generator during the process, but it cannot."; - throw std::runtime_error(os.str()); + LOG_WARN("The stream " << _inputStream->getStreamIndex() << " has a duration of " << getDuration() + << "s. It needs to switch to a generator during the process, but it cannot. " + << "No generator will be used for this stream.") + return; } _needToSwitchToGenerator = needToSwitch; } @@ -547,9 +567,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 bd7cbb4e..1bdc45c8 100644 --- a/src/AvTranscoder/transcoder/StreamTranscoder.hpp +++ b/src/AvTranscoder/transcoder/StreamTranscoder.hpp @@ -77,7 +77,7 @@ class AvExport StreamTranscoder IEncoder* getEncoder() const { return _outputEncoder; } /// Returns a reference to the object which transforms the decoded data - ITransform& getTransform() const { return *_transform; } + ITransform* getTransform() const { return _transform; } /// Returns a reference to the stream which unwraps data IInputStream* getInputStream() const { return _inputStream; } @@ -102,10 +102,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 @@ -117,6 +113,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) @@ -136,7 +136,7 @@ class AvExport StreamTranscoder float _offset; ///< Offset, in seconds, at the beginning of the StreamTranscoder. bool _needToSwitchToGenerator; ///< Set if need to switch to a generator during the process (because, of other streams - ///duration, or an offset) + /// duration, or an offset) }; } diff --git a/src/AvTranscoder/transcoder/Transcoder.cpp b/src/AvTranscoder/transcoder/Transcoder.cpp index db698f7b..5046b990 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(eProcessMethodLongest) , _mainStreamIndex(0) , _outputDuration(0) { @@ -234,16 +234,32 @@ 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) + if(!_streamTranscoders.at(streamIndex)->processFrame()) { - 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 && nbStreamsWithoutGenerator == nbStreamProcessStatusFailed) + { + LOG_INFO("End of process because all streams (except generators) failed to process a new frame.") + return false; + } return true; } @@ -266,33 +282,39 @@ 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("The expected output duration of the program 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(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; @@ -501,7 +523,7 @@ float Transcoder::getMaxTotalDuration() const return maxTotalDuration; } -float Transcoder::getOutputDuration() const +float Transcoder::getExpectedOutputDuration() const { switch(_eProcessMethod) { @@ -520,6 +542,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) @@ -563,9 +599,10 @@ void Transcoder::fillProcessStat(ProcessStat& processStat) for(size_t streamIndex = 0; streamIndex < _streamTranscoders.size(); ++streamIndex) { IOutputStream& stream = _streamTranscoders.at(streamIndex)->getOutputStream(); - const IInputStream* inputStream = _streamTranscoders.at( streamIndex )->getInputStream(); - if(inputStream == NULL) { - LOG_WARN( "Cannot process statistics of generated stream." ) + const IInputStream* inputStream = _streamTranscoders.at(streamIndex)->getInputStream(); + if(inputStream == NULL) + { + LOG_WARN("Cannot process statistics of generated stream.") continue; } const AVMediaType mediaType = inputStream->getProperties().getStreamType(); diff --git a/src/AvTranscoder/transcoder/Transcoder.hpp b/src/AvTranscoder/transcoder/Transcoder.hpp index f54cb3d6..289db061 100644 --- a/src/AvTranscoder/transcoder/Transcoder.hpp +++ b/src/AvTranscoder/transcoder/Transcoder.hpp @@ -17,14 +17,14 @@ 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 + * @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 transcode at the end of an indicated duration, in seconds (@see _outputDuration + * eProcessMethodBasedOnDuration: stop the process at the end of an indicated duration, in seconds (@see _outputDuration * attribute of Transcoder). - * eProcessMethodInfinity: stop transcode by outside of avTranscoder (streaming mode) + * eProcessMethodInfinity: stop the process by outside of avTranscoder (streaming mode) */ enum EProcessMethod { @@ -207,10 +207,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. @@ -231,11 +238,11 @@ 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 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. + /// eProcessMethodBasedOnDuration. }; } diff --git a/test/pyTest/testEProcessMethod.py b/test/pyTest/testEProcessMethod.py index 2a431120..4fee610f 100644 --- a/test/pyTest/testEProcessMethod.py +++ b/test/pyTest/testEProcessMethod.py @@ -41,7 +41,8 @@ def testEProcessMethodShortest(): dst_inputFile = av.InputFile( outputFileName ) dst_properties = dst_inputFile.getProperties() - assert_equals( dst_properties.getDuration(), src_properties_shortest.getDuration() ) + for dst_stream_properties in dst_properties.getStreamProperties(): + assert_almost_equals( dst_stream_properties.getDuration(), src_properties_shortest.getDuration(), delta=0.05 ) def testEProcessMethodLongest(): @@ -70,7 +71,8 @@ def testEProcessMethodLongest(): dst_inputFile = av.InputFile( outputFileName ) dst_properties = dst_inputFile.getProperties() - assert_equals( dst_properties.getDuration(), src_properties_longest.getDuration() ) + for dst_stream_properties in dst_properties.getStreamProperties(): + assert_almost_equals( dst_stream_properties.getDuration(), src_properties_longest.getDuration(), delta=0.05 ) def testEProcessMethodBasedOnStream(): @@ -130,5 +132,6 @@ def testEProcessMethodBasedOnDuration(): dst_inputFile = av.InputFile( outputFileName ) dst_properties = dst_inputFile.getProperties() - assert_equals( dst_properties.getDuration(), outputDuration ) + for dst_stream_properties in dst_properties.getStreamProperties(): + assert_almost_equals( dst_stream_properties.getDuration(), outputDuration, delta=0.05 ) diff --git a/test/pyTest/testTranscoderAdd.py b/test/pyTest/testTranscoderAdd.py index b0ab31db..50a2c9ac 100644 --- a/test/pyTest/testTranscoderAdd.py +++ b/test/pyTest/testTranscoderAdd.py @@ -86,8 +86,5 @@ def testAddAllStreamsOfAGivenFile(): import testTranscoderRewrap # for each stream for src_stream, dst_stream in zip(src_streams_properties, dst_streams_properties): - # FIXME: analyse only the first stream because all the input audio data are not read - if src_stream.getStreamId() != 1: - continue # check properties testTranscoderRewrap.checkStream(src_stream, dst_stream)
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: