From 55a4baba86900419f8cfcfb9afa127a989cc066f Mon Sep 17 00:00:00 2001 From: Valentin NOEL Date: Wed, 20 Sep 2017 18:05:49 +0200 Subject: [PATCH 1/8] StreamTranscoder: add null input stream to generate output channel If the InputStreamDesc filename is empty, a generator is set up as input --- .../transcoder/StreamTranscoder.cpp | 89 +++++++++++++++++-- .../transcoder/StreamTranscoder.hpp | 3 + src/AvTranscoder/transcoder/Transcoder.cpp | 31 ++++++- 3 files changed, 113 insertions(+), 10 deletions(-) diff --git a/src/AvTranscoder/transcoder/StreamTranscoder.cpp b/src/AvTranscoder/transcoder/StreamTranscoder.cpp index 6c79e4d5..cf4fcb05 100644 --- a/src/AvTranscoder/transcoder/StreamTranscoder.cpp +++ b/src/AvTranscoder/transcoder/StreamTranscoder.cpp @@ -147,12 +147,22 @@ StreamTranscoder::StreamTranscoder(const std::vector& inputStre size_t nbOutputChannels = 0; for(size_t index = 0; index < inputStreams.size(); ++index) { - addDecoder(_inputStreamDesc.at(index), *_inputStreams.at(index)); - nbOutputChannels += _inputStreamDesc.at(index)._channelIndexArray.size(); + if(_inputStreams.at(index) != NULL) + { + LOG_INFO("add decoder for input stream " << index); + addDecoder(_inputStreamDesc.at(index), *_inputStreams.at(index)); + nbOutputChannels += _inputStreamDesc.at(index)._channelIndexArray.size(); + } + else + { + LOG_INFO("add generator for empty input " << index); + addGenerator(_inputStreamDesc.at(index), profile); + } } - IInputStream& inputStream = *_inputStreams.at(0); - const InputStreamDesc& inputStreamDesc = inputStreamsDesc.at(0); + const size_t firstInputStreamIndex = getFirstInputStreamIndex(); + IInputStream& inputStream = *_inputStreams.at(firstInputStreamIndex); + const InputStreamDesc& inputStreamDesc = inputStreamsDesc.at(firstInputStreamIndex); // create a transcode case switch(inputStream.getProperties().getStreamType()) @@ -229,6 +239,16 @@ StreamTranscoder::StreamTranscoder(const std::vector& inputStre setOffset(offset); } +size_t StreamTranscoder::getFirstInputStreamIndex() +{ + for(size_t index = 0; index < _inputStreams.size(); ++index) + { + if(_inputStreams.at(index) != NULL) + return index; + } + throw std::runtime_error("Cannot handle only null input streams"); +} + void StreamTranscoder::addDecoder(const InputStreamDesc& inputStreamDesc, IInputStream& inputStream) { // create a transcode case @@ -278,6 +298,53 @@ void StreamTranscoder::addDecoder(const InputStreamDesc& inputStreamDesc, IInput } } +void StreamTranscoder::addGenerator(const InputStreamDesc& inputStreamDesc, const ProfileLoader::Profile& profile) +{ + // create a transcode case + if(profile.find(constants::avProfileType)->second == constants::avProfileTypeVideo) + { + VideoCodec inputVideoCodec(eCodecTypeEncoder, profile.find(constants::avProfileCodec)->second); + VideoFrameDesc inputFrameDesc(profile); + inputVideoCodec.setImageParameters(inputFrameDesc); + + // generator decoder + VideoGenerator* generator = new VideoGenerator(inputFrameDesc); + _generators.push_back(generator); + _currentDecoder = generator; + + // buffers to process + VideoFrameDesc outputFrameDesc = inputFrameDesc; + outputFrameDesc.setParameters(profile); + _decodedData.push_back(new VideoFrame(inputFrameDesc)); + + // no decoder for this input + _inputDecoders.push_back(NULL); + + } + else if(profile.find(constants::avProfileType)->second == constants::avProfileTypeAudio) + { + // corresponding input codec + AudioCodec inputAudioCodec(eCodecTypeEncoder, profile.find(constants::avProfileCodec)->second); + AudioFrameDesc inputFrameDesc(profile); + inputFrameDesc._nbChannels = 1; + inputAudioCodec.setAudioParameters(inputFrameDesc); + + // generator decoder + AudioGenerator* generator = new AudioGenerator(inputFrameDesc); + _generators.push_back(generator); + _currentDecoder = generator; + // buffers to get the decoded data + _decodedData.push_back(new AudioFrame(inputFrameDesc)); + + // no decoder for this input + _inputDecoders.push_back(NULL); + } + else + { + throw std::runtime_error("unupported stream type"); + } +} + StreamTranscoder::StreamTranscoder(IOutputFile& outputFile, const ProfileLoader::Profile& profile) : _inputStreamDesc() , _inputStreams() @@ -375,7 +442,8 @@ StreamTranscoder::~StreamTranscoder() for(std::vector::iterator it = _inputDecoders.begin(); it != _inputDecoders.end(); ++it) { - delete(*it); + if(*it != NULL) + delete(*it); } for(std::vector::iterator it = _generators.begin(); it != _generators.end(); ++it) { @@ -548,9 +616,10 @@ bool StreamTranscoder::processTranscode() } // check the next data buffers in case of audio frames - if(_decodedData.at(0)->isAudioFrame()) + const size_t firstInputStreamIndex = getFirstInputStreamIndex(); + if(_decodedData.at(firstInputStreamIndex)->isAudioFrame()) { - const int nbInputSamplesPerChannel = _decodedData.at(0)->getAVFrame().nb_samples; + const int nbInputSamplesPerChannel = _decodedData.at(firstInputStreamIndex)->getAVFrame().nb_samples; // Reallocate output frame if(nbInputSamplesPerChannel > _filteredData->getAVFrame().nb_samples) @@ -670,7 +739,11 @@ float StreamTranscoder::getDuration() const float minStreamDuration = -1; for(size_t index = 0; index < _inputStreams.size(); ++index) { - const StreamProperties& streamProperties = _inputStreams.at(index)->getProperties(); + IInputStream* inputStream = _inputStreams.at(index); + if(inputStream == NULL) + continue; + + const StreamProperties& streamProperties = inputStream->getProperties(); if(minStreamDuration == -1 || streamProperties.getDuration() < minStreamDuration) minStreamDuration = streamProperties.getDuration(); } diff --git a/src/AvTranscoder/transcoder/StreamTranscoder.hpp b/src/AvTranscoder/transcoder/StreamTranscoder.hpp index c916b8cc..862b4cd6 100644 --- a/src/AvTranscoder/transcoder/StreamTranscoder.hpp +++ b/src/AvTranscoder/transcoder/StreamTranscoder.hpp @@ -125,10 +125,13 @@ class AvExport StreamTranscoder * @param inputStream */ void addDecoder(const InputStreamDesc& inputStreamDesc, IInputStream& inputStream); + void addGenerator(const InputStreamDesc& inputStreamDesc, const ProfileLoader::Profile& profile); bool processRewrap(); bool processTranscode(); + size_t getFirstInputStreamIndex(); + private: std::vector _inputStreamDesc; ///< Description of the data to extract from the input stream. std::vector _inputStreams; ///< List of input stream to read next packet (has link, no ownership) diff --git a/src/AvTranscoder/transcoder/Transcoder.cpp b/src/AvTranscoder/transcoder/Transcoder.cpp index 3e35ad4e..fbc10e80 100644 --- a/src/AvTranscoder/transcoder/Transcoder.cpp +++ b/src/AvTranscoder/transcoder/Transcoder.cpp @@ -260,6 +260,12 @@ void Transcoder::addTranscodeStream(const std::vector& inputStr AVMediaType commonStreamType = AVMEDIA_TYPE_UNKNOWN; for(std::vector::const_iterator it = inputStreamDescArray.begin(); it != inputStreamDescArray.end(); ++it) { + if(it->_filename.empty()) + { + inputStreams.push_back(NULL); + continue; + } + InputFile* referenceFile = addInputFile(it->_filename, it->_streamIndex, offset); inputStreams.push_back(&referenceFile->getStream(it->_streamIndex)); @@ -329,7 +335,16 @@ ProfileLoader::Profile Transcoder::getProfileFromInputs(const std::vector= 1); // Get properties from the first input - const InputStreamDesc& inputStreamDesc = inputStreamDescArray.at(0); + size_t nonEmptyFileName = 0; + for(size_t i = 0; i < inputStreamDescArray.size(); ++i) + { + if(!inputStreamDescArray.at(i)._filename.empty()) + { + nonEmptyFileName = i; + break; + } + } + const InputStreamDesc& inputStreamDesc = inputStreamDescArray.at(nonEmptyFileName); InputFile inputFile(inputStreamDesc._filename); const StreamProperties* streamProperties = &inputFile.getProperties().getStreamPropertiesWithIndex(inputStreamDesc._streamIndex); @@ -519,7 +534,19 @@ void Transcoder::fillProcessStat(ProcessStat& processStat) LOG_WARN("Cannot process statistics of generated stream.") continue; } - const IInputStream* inputStream = _streamTranscoders.at(streamIndex)->getInputStreams().at(0); + + size_t nonNullInputStreamIndex = 0; + std::vector inputStreams = _streamTranscoders.at(streamIndex)->getInputStreams(); + for(size_t i = 0; i < inputStreams.size(); ++i) + { + if(inputStreams.at(i) != NULL) + { + nonNullInputStreamIndex = i; + break; + } + } + + const IInputStream* inputStream = inputStreams.at(nonNullInputStreamIndex); const AVMediaType mediaType = inputStream->getProperties().getStreamType(); switch(mediaType) { From dd89b56b03adbbd2d852974fd123dfdfa91d6512 Mon Sep 17 00:00:00 2001 From: Valentin NOEL Date: Wed, 20 Sep 2017 18:06:57 +0200 Subject: [PATCH 2/8] StreamTranscoder: manage process case according to the input index --- .../transcoder/StreamTranscoder.cpp | 30 +++++++++++++++---- .../transcoder/StreamTranscoder.hpp | 2 +- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/AvTranscoder/transcoder/StreamTranscoder.cpp b/src/AvTranscoder/transcoder/StreamTranscoder.cpp index cf4fcb05..cc7ff102 100644 --- a/src/AvTranscoder/transcoder/StreamTranscoder.cpp +++ b/src/AvTranscoder/transcoder/StreamTranscoder.cpp @@ -604,7 +604,8 @@ bool StreamTranscoder::processTranscode() std::vector decodingStatus(_generators.size(), true); for(size_t index = 0; index < _generators.size(); ++index) { - if(getProcessCase() == eProcessCaseTranscode) + EProcessCase processCase = getProcessCase(index); + if(processCase == eProcessCaseTranscode) _currentDecoder = _inputDecoders.at(index); else _currentDecoder = _generators.at(index); @@ -795,13 +796,30 @@ void StreamTranscoder::setOffset(const float offset) } } -StreamTranscoder::EProcessCase StreamTranscoder::getProcessCase() const +StreamTranscoder::EProcessCase StreamTranscoder::getProcessCase(const size_t decoderIndex) const { - if(! _inputStreams.empty() && ! _inputDecoders.empty() && std::find(_inputDecoders.begin(), _inputDecoders.end(), _currentDecoder) != _inputDecoders.end() ) - return eProcessCaseTranscode; - else if(! _inputStreams.empty() && _inputDecoders.empty() && !_currentDecoder) - return eProcessCaseRewrap; + if(_inputStreamDesc.size() <= 1) + { + if(! _inputStreams.empty() && ! _inputDecoders.empty() && std::find(_inputDecoders.begin(), _inputDecoders.end(), _currentDecoder) != _inputDecoders.end() ) + return eProcessCaseTranscode; + else if(! _inputStreams.empty() && _inputDecoders.empty() && !_currentDecoder) + return eProcessCaseRewrap; + else + return eProcessCaseGenerator; + } else + { + if(! _inputStreams.empty() && _currentDecoder != NULL) + { + if( _inputStreams.at(decoderIndex) != NULL) + return eProcessCaseTranscode; + return eProcessCaseGenerator; + } + else if(! _inputStreams.empty() && _inputDecoders.empty() && !_currentDecoder) + { + return eProcessCaseRewrap; + } return eProcessCaseGenerator; + } } } diff --git a/src/AvTranscoder/transcoder/StreamTranscoder.hpp b/src/AvTranscoder/transcoder/StreamTranscoder.hpp index 862b4cd6..56206091 100644 --- a/src/AvTranscoder/transcoder/StreamTranscoder.hpp +++ b/src/AvTranscoder/transcoder/StreamTranscoder.hpp @@ -115,7 +115,7 @@ class AvExport StreamTranscoder eProcessCaseRewrap, eProcessCaseGenerator }; - EProcessCase getProcessCase() const; + EProcessCase getProcessCase(const size_t decoderIndex = 0) const; //@} private: From 4d4ca1ef32928990137ecbfdbdda3edb06acf986 Mon Sep 17 00:00:00 2001 From: Valentin NOEL Date: Wed, 20 Sep 2017 18:35:39 +0200 Subject: [PATCH 3/8] Transcoder: handle empty filnames only case --- src/AvTranscoder/transcoder/Transcoder.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/AvTranscoder/transcoder/Transcoder.cpp b/src/AvTranscoder/transcoder/Transcoder.cpp index fbc10e80..e278c9db 100644 --- a/src/AvTranscoder/transcoder/Transcoder.cpp +++ b/src/AvTranscoder/transcoder/Transcoder.cpp @@ -335,7 +335,7 @@ ProfileLoader::Profile Transcoder::getProfileFromInputs(const std::vector= 1); // Get properties from the first input - size_t nonEmptyFileName = 0; + size_t nonEmptyFileName = -1; for(size_t i = 0; i < inputStreamDescArray.size(); ++i) { if(!inputStreamDescArray.at(i)._filename.empty()) @@ -344,6 +344,9 @@ ProfileLoader::Profile Transcoder::getProfileFromInputs(const std::vector Date: Thu, 21 Sep 2017 16:38:32 +0200 Subject: [PATCH 4/8] StreamTranscoder: store the index of the first non-null input stream Instead of using the getFirstInputStreamIndex() private method. --- .../transcoder/StreamTranscoder.cpp | 26 +++++++------------ .../transcoder/StreamTranscoder.hpp | 4 +-- 2 files changed, 12 insertions(+), 18 deletions(-) diff --git a/src/AvTranscoder/transcoder/StreamTranscoder.cpp b/src/AvTranscoder/transcoder/StreamTranscoder.cpp index cc7ff102..7e6fca46 100644 --- a/src/AvTranscoder/transcoder/StreamTranscoder.cpp +++ b/src/AvTranscoder/transcoder/StreamTranscoder.cpp @@ -33,6 +33,7 @@ StreamTranscoder::StreamTranscoder(IInputStream& inputStream, IOutputFile& outpu , _outputEncoder(NULL) , _transform(NULL) , _filterGraph(NULL) + , _firstInputStreamIndex(0) , _offset(offset) , _needToSwitchToGenerator(false) { @@ -140,6 +141,7 @@ StreamTranscoder::StreamTranscoder(const std::vector& inputStre , _outputEncoder(NULL) , _transform(NULL) , _filterGraph(NULL) + , _firstInputStreamIndex(std::numeric_limits::max()) , _offset(offset) , _needToSwitchToGenerator(false) { @@ -152,17 +154,19 @@ StreamTranscoder::StreamTranscoder(const std::vector& inputStre LOG_INFO("add decoder for input stream " << index); addDecoder(_inputStreamDesc.at(index), *_inputStreams.at(index)); nbOutputChannels += _inputStreamDesc.at(index)._channelIndexArray.size(); + if(_firstInputStreamIndex == std::numeric_limits::max()) + _firstInputStreamIndex = index; } else { LOG_INFO("add generator for empty input " << index); addGenerator(_inputStreamDesc.at(index), profile); + nbOutputChannels++; } } - const size_t firstInputStreamIndex = getFirstInputStreamIndex(); - IInputStream& inputStream = *_inputStreams.at(firstInputStreamIndex); - const InputStreamDesc& inputStreamDesc = inputStreamsDesc.at(firstInputStreamIndex); + IInputStream& inputStream = *_inputStreams.at(_firstInputStreamIndex); + const InputStreamDesc& inputStreamDesc = inputStreamsDesc.at(_firstInputStreamIndex); // create a transcode case switch(inputStream.getProperties().getStreamType()) @@ -239,16 +243,6 @@ StreamTranscoder::StreamTranscoder(const std::vector& inputStre setOffset(offset); } -size_t StreamTranscoder::getFirstInputStreamIndex() -{ - for(size_t index = 0; index < _inputStreams.size(); ++index) - { - if(_inputStreams.at(index) != NULL) - return index; - } - throw std::runtime_error("Cannot handle only null input streams"); -} - void StreamTranscoder::addDecoder(const InputStreamDesc& inputStreamDesc, IInputStream& inputStream) { // create a transcode case @@ -358,6 +352,7 @@ StreamTranscoder::StreamTranscoder(IOutputFile& outputFile, const ProfileLoader: , _outputEncoder(NULL) , _transform(NULL) , _filterGraph(NULL) + , _firstInputStreamIndex(0) , _offset(0) , _needToSwitchToGenerator(false) { @@ -617,10 +612,9 @@ bool StreamTranscoder::processTranscode() } // check the next data buffers in case of audio frames - const size_t firstInputStreamIndex = getFirstInputStreamIndex(); - if(_decodedData.at(firstInputStreamIndex)->isAudioFrame()) + if(_decodedData.at(_firstInputStreamIndex)->isAudioFrame()) { - const int nbInputSamplesPerChannel = _decodedData.at(firstInputStreamIndex)->getAVFrame().nb_samples; + const int nbInputSamplesPerChannel = _decodedData.at(_firstInputStreamIndex)->getAVFrame().nb_samples; // Reallocate output frame if(nbInputSamplesPerChannel > _filteredData->getAVFrame().nb_samples) diff --git a/src/AvTranscoder/transcoder/StreamTranscoder.hpp b/src/AvTranscoder/transcoder/StreamTranscoder.hpp index 56206091..c72db0ea 100644 --- a/src/AvTranscoder/transcoder/StreamTranscoder.hpp +++ b/src/AvTranscoder/transcoder/StreamTranscoder.hpp @@ -130,8 +130,6 @@ class AvExport StreamTranscoder bool processRewrap(); bool processTranscode(); - size_t getFirstInputStreamIndex(); - private: std::vector _inputStreamDesc; ///< Description of the data to extract from the input stream. std::vector _inputStreams; ///< List of input stream to read next packet (has link, no ownership) @@ -150,6 +148,8 @@ class AvExport StreamTranscoder FilterGraph* _filterGraph; ///< Filter graph (has ownership) + size_t _firstInputStreamIndex; ///< Index of the first non-null input stream. + 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 From a85f86780fb829aff947448f5573f1cc89ee06ac Mon Sep 17 00:00:00 2001 From: Valentin Noel Date: Thu, 21 Sep 2017 16:39:15 +0200 Subject: [PATCH 5/8] StreamTranscoder: throw an exception if no non-null InputStream is set as input User should use the StreamTranscoder(IOutputFile&, const ProfileLoader::Profile&) constructor to handle this case. --- src/AvTranscoder/transcoder/StreamTranscoder.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/AvTranscoder/transcoder/StreamTranscoder.cpp b/src/AvTranscoder/transcoder/StreamTranscoder.cpp index 7e6fca46..391f5ddd 100644 --- a/src/AvTranscoder/transcoder/StreamTranscoder.cpp +++ b/src/AvTranscoder/transcoder/StreamTranscoder.cpp @@ -165,6 +165,9 @@ StreamTranscoder::StreamTranscoder(const std::vector& inputStre } } + if(_firstInputStreamIndex == std::numeric_limits::max()) + throw std::runtime_error("Cannot handle empty only input streams"); + IInputStream& inputStream = *_inputStreams.at(_firstInputStreamIndex); const InputStreamDesc& inputStreamDesc = inputStreamsDesc.at(_firstInputStreamIndex); From cc755babed250a45b8752e5bf0dd1746ca6f5586 Mon Sep 17 00:00:00 2001 From: Valentin Noel Date: Thu, 21 Sep 2017 16:41:00 +0200 Subject: [PATCH 6/8] Transcoder: throw an exception if no non-empty filename is set as input Since the input profile cannot be guessed from such inputs. --- src/AvTranscoder/transcoder/Transcoder.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/AvTranscoder/transcoder/Transcoder.cpp b/src/AvTranscoder/transcoder/Transcoder.cpp index e278c9db..5249f330 100644 --- a/src/AvTranscoder/transcoder/Transcoder.cpp +++ b/src/AvTranscoder/transcoder/Transcoder.cpp @@ -335,7 +335,7 @@ ProfileLoader::Profile Transcoder::getProfileFromInputs(const std::vector= 1); // Get properties from the first input - size_t nonEmptyFileName = -1; + size_t nonEmptyFileName = std::numeric_limits::max(); for(size_t i = 0; i < inputStreamDescArray.size(); ++i) { if(!inputStreamDescArray.at(i)._filename.empty()) @@ -344,8 +344,8 @@ ProfileLoader::Profile Transcoder::getProfileFromInputs(const std::vector::max()) + throw std::runtime_error("Cannot get profile from empty input streams"); const InputStreamDesc& inputStreamDesc = inputStreamDescArray.at(nonEmptyFileName); InputFile inputFile(inputStreamDesc._filename); From c609ac8db4f3ce1c84dcac79e059e48a9e2acaa2 Mon Sep 17 00:00:00 2001 From: Valentin Noel Date: Thu, 21 Sep 2017 16:42:06 +0200 Subject: [PATCH 7/8] Tests: add silent audio channels muxing tests --- test/pyTest/testMuxAudioChannels.py | 154 +++++++++++++++++++++++++++- 1 file changed, 153 insertions(+), 1 deletion(-) diff --git a/test/pyTest/testMuxAudioChannels.py b/test/pyTest/testMuxAudioChannels.py index 01ab6720..6850e934 100644 --- a/test/pyTest/testMuxAudioChannels.py +++ b/test/pyTest/testMuxAudioChannels.py @@ -60,7 +60,7 @@ def testMuxAudioChannelsFromDifferentFormatInputs_20(): def testMuxAudioChannelsFromDifferentFormatInputs_51(): """ - Mux audio channels from different formats files, and generate one audio stereo stream + Mux audio channels from different formats files, and generate one audio 5.1 stream """ # inputs inputFileName1 = os.environ['AVTRANSCODER_TEST_AUDIO_MOV_FILE'] @@ -107,3 +107,155 @@ def testMuxAudioChannelsFromDifferentFormatInputs_51(): dst_audioProperties = dst_inputFile.getProperties().getAudioProperties() assert_equals(1, len(dst_audioProperties)) assert_equals(6, dst_audioProperties[0].getNbChannels()) + + +def testMuxAudioChannelsWithSilence_20(): + """ + Mux audio channels with generated silence, and generate one audio stereo stream + """ + # input + inputFileName = os.environ['AVTRANSCODER_TEST_AUDIO_WAVE_FILE'] + + inputs = av.InputStreamDescVector() + inputs.append(av.InputStreamDesc(inputFileName, 0, 0)) + inputs.append(av.InputStreamDesc("", 0, 0)) # empty filename to generate silence + + # output + outputFileName = "testMuxAudioChannelsWithSilence_20.wav" + ouputFile = av.OutputFile(outputFileName) + + transcoder = av.Transcoder(ouputFile) + transcoder.addStream(inputs, "wave24b48kstereo") + + progress = av.ConsoleProgress() + processStat = transcoder.process( progress ) + + # check process stat returned + audioStat = processStat.getAudioStat(0) + + inputFile = av.InputFile(inputFileName) + + src_audioStream = inputFile.getProperties().getAudioProperties()[0] + + assert_equals(src_audioStream.getDuration(), audioStat.getDuration()) + + # check dst file properties + dst_inputFile = av.InputFile(outputFileName) + dst_fileProperties = dst_inputFile.getProperties() + assert_equals(src_audioStream.getDuration(), dst_fileProperties.getDuration()) + + # check dst audio streams + dst_audioProperties = dst_fileProperties.getAudioProperties() + assert_equals(1, len(dst_audioProperties)) + assert_equals(2, dst_audioProperties[0].getNbChannels()) + +def testMuxAudioChannelsWithSilenceProfileFromInput_20(): + """ + Mux audio channels with generated silence, and generate one audio stereo stream + """ + # input + inputFileName = os.environ['AVTRANSCODER_TEST_AUDIO_WAVE_FILE'] + + inputs = av.InputStreamDescVector() + inputs.append(av.InputStreamDesc(inputFileName, 0, 0)) + inputs.append(av.InputStreamDesc("", 0, 0)) # empty filename to generate silence + + # output + outputFileName = "testMuxAudioChannelsWithSilenceNoProfile_20.wav" + ouputFile = av.OutputFile(outputFileName) + + transcoder = av.Transcoder(ouputFile) + transcoder.addStream(inputs) + + progress = av.ConsoleProgress() + processStat = transcoder.process( progress ) + + # check process stat returned + audioStat = processStat.getAudioStat(0) + + inputFile = av.InputFile(inputFileName) + + src_audioStream = inputFile.getProperties().getAudioProperties()[0] + + assert_equals(src_audioStream.getDuration(), audioStat.getDuration()) + + # check dst file properties + dst_inputFile = av.InputFile(outputFileName) + dst_fileProperties = dst_inputFile.getProperties() + assert_equals(src_audioStream.getDuration(), dst_fileProperties.getDuration()) + + # check dst audio streams + dst_audioProperties = dst_fileProperties.getAudioProperties() + assert_equals(1, len(dst_audioProperties)) + assert_equals(2, dst_audioProperties[0].getNbChannels()) + +def testMuxAudioChannelsWithSilenceProfileFromInput_51(): + """ + Mux audio channels with generated silence, and generate one audio 5.1 stream + """ + # inputs + inputFileName1 = os.environ['AVTRANSCODER_TEST_AUDIO_MOV_FILE'] + inputFileName2 = os.environ['AVTRANSCODER_TEST_AUDIO_WAVE_FILE'] + assert_not_equals(inputFileName1, inputFileName2) + + inputs = av.InputStreamDescVector() + inputs.append(av.InputStreamDesc("", 0, 0)) # empty filename to generate silence + inputs.append(av.InputStreamDesc(inputFileName1, 1, 0)) + inputs.append(av.InputStreamDesc(inputFileName2, 0, 2)) + inputs.append(av.InputStreamDesc("", 0, 0)) # empty filename to generate silence + inputs.append(av.InputStreamDesc(inputFileName2, 0, 1)) + inputs.append(av.InputStreamDesc("", 0, 0)) # empty filename to generate silence + + # output + outputFileName = "testMuxAudioChannelsWithSilenceProfileFromInput_51.wav" + ouputFile = av.OutputFile(outputFileName) + + transcoder = av.Transcoder(ouputFile) + transcoder.addStream(inputs) + + progress = av.ConsoleProgress() + processStat = transcoder.process( progress ) + + # check process stat returned + audioStat = processStat.getAudioStat(0) + + inputFile1 = av.InputFile(inputFileName1) + inputFile2 = av.InputFile(inputFileName2) + + src_audioStream1 = inputFile1.getProperties().getAudioProperties()[0] + src_audioStream2 = inputFile2.getProperties().getAudioProperties()[0] + + min_src_duration = min(src_audioStream1.getDuration(), src_audioStream2.getDuration()) + + assert_equals(min_src_duration, audioStat.getDuration()) + + # check dst file properties + dst_inputFile = av.InputFile(outputFileName) + dst_fileProperties = dst_inputFile.getProperties() + assert_equals(min_src_duration, dst_fileProperties.getDuration()) + + # check dst audio streams + dst_audioProperties = dst_inputFile.getProperties().getAudioProperties() + assert_equals(1, len(dst_audioProperties)) + assert_equals(6, dst_audioProperties[0].getNbChannels()) + +@raises(RuntimeError) +def testMuxAudioChannelsWithSilenceOnly_20(): + """ + Mux audio channels with generated silence, and generate one audio stereo stream + """ + # input + inputs = av.InputStreamDescVector() + inputs.append(av.InputStreamDesc("", 0, 0)) # empty filename to generate silence + inputs.append(av.InputStreamDesc("", 0, 0)) # empty filename to generate silence + + # output + outputFileName = "testMuxAudioChannelsWithSilenceOnly_20.wav" + ouputFile = av.OutputFile(outputFileName) + + transcoder = av.Transcoder(ouputFile) + transcoder.addStream(inputs, "wave24b48kstereo") + + progress = av.ConsoleProgress() + processStat = transcoder.process( progress ) + From d834fe3707795f43faded774cbfc7cab90c89863 Mon Sep 17 00:00:00 2001 From: Valentin Noel Date: Fri, 22 Sep 2017 11:18:14 +0200 Subject: [PATCH 8/8] InputStreamDesc: add a few documentation --- src/AvTranscoder/transcoder/InputStreamDesc.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AvTranscoder/transcoder/InputStreamDesc.hpp b/src/AvTranscoder/transcoder/InputStreamDesc.hpp index a3083e5d..e50d0aed 100644 --- a/src/AvTranscoder/transcoder/InputStreamDesc.hpp +++ b/src/AvTranscoder/transcoder/InputStreamDesc.hpp @@ -63,7 +63,7 @@ struct InputStreamDesc bool demultiplexing() const { return !_channelIndexArray.empty(); } public: - std::string _filename; ///< Source file path. + std::string _filename; ///< Source file path. If empty, a generator is used as source. size_t _streamIndex; ///< Source stream to extract. std::vector _channelIndexArray; ///< List of source channels to extract from the stream };