Skip to content

Transcoder: can extract several channels from an input stream, and encode them to one output stream #269

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

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
c53ab7f
StreamTranscoder: create input codec from the profile in case of gene…
Jul 13, 2016
01e5eae
Transcoder: removed add method to rewrap all the streams of a given file
Jul 13, 2016
321ef89
Transocder: removed ICodec parameter to generate streams
Jul 13, 2016
7570203
Transcoder: added InputStreamDesc to describe the source data to extract
Jul 18, 2016
003df71
pyTest: renamed Dummy tests to GenerateStream
Jul 18, 2016
29af8e2
Transcoder: renamed private method addDummyStream to addGeneratedStream
Jul 18, 2016
1b84b88
Transcoder: renamed 'add' methods to generate streams to 'addGenerate…
Jul 18, 2016
068df30
Transcoder: renamed 'add' methods to 'addStream'
Jul 18, 2016
a4b6a28
AudioProperties: added doc
Jul 18, 2016
128e2ce
AudioProperties: improved getChannelLayout method
Jul 18, 2016
b371beb
AudioFrame: added getChannelLayoutDesc method
Jul 18, 2016
ee5f730
Transcoder: can extract several channels in the same streams
Jul 18, 2016
1824094
AudioFrame: added doc
Jul 18, 2016
21506ac
AuAudioProperties: added doc
Jul 18, 2016
8642c80
AudioTransform: added log to get the channel layout info
Jul 18, 2016
76f2b3f
InputStreamDesc: renamed _channelsIndex attribute to _channelIndexArray
Jul 18, 2016
9868167
Format src files
Jul 18, 2016
7756c2c
Merge branch 'develop' into dev_transcoderRewrapToMultiChannels
Jul 19, 2016
b022bfb
pyTest: added a test to check audio demux to one output
Jul 19, 2016
eb1e9af
AudioDecoder: improved perf if all channels of the stream are extracted
Jul 19, 2016
c8bc988
Merge branch 'develop' of https://github.com/avTranscoder/avTranscode…
Jul 19, 2016
5ec2a73
Transcoder: updated doc of addStream methods
Jul 19, 2016
222c48f
StreamTranscoder: updated doc
Jul 19, 2016
1a512f9
Transcoder: refactored private method addTranscodeStream
Jul 19, 2016
33723c8
StreamTranscoder: throw if a stream needs to switch to a generator bu…
Jul 19, 2016
8c95f63
Moved InputStreamDesc struct to a separate file
Jul 19, 2016
bd01298
StreamTranscoder: updated constructor parameters
Jul 19, 2016
3dba20f
pyTest: clean testTranscoderAdd
Jul 19, 2016
1d84193
pyTest: fixed testSetFrame
Jul 19, 2016
584f530
Transcoder: renamed addGeneratedStream methods to addGenerateStream
Jul 19, 2016
d01ccc8
pyTest: fixed doc of testTranscoderTranscodeAudioMov
Jul 19, 2016
58dbd66
pyTest: fixed testOffset
Jul 20, 2016
a9959cc
.gitignore: added Python bytecode files
Jul 20, 2016
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
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,7 @@ CMakeFiles
Makefile
cmake_install.cmake
install_manifest.txt

# Python
*.pyc

50 changes: 9 additions & 41 deletions app/avProcessor/avProcessor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,6 @@
#include <sstream>
#include <cstdlib>

static const size_t dummyWidth = 1920;
static const size_t dummyHeight = 1080;
static const std::string dummyPixelFormat = "yuv420p";
static const std::string dummyVideoCodec = "mpeg2video";

static const size_t dummySampleRate = 48000;
static const size_t dummyChannels = 1;
static const std::string dummySampleFormat = "s16";
static const std::string dummyAudioCodec = "pcm_s16le";

static bool useVideoGenerator = false;

void parseConfigFile(const std::string& configFilename, avtranscoder::Transcoder& transcoder)
{
std::ifstream configFile(configFilename.c_str(), std::ifstream::in);
Expand All @@ -41,37 +29,23 @@ void parseConfigFile(const std::string& configFilename, avtranscoder::Transcoder
std::stringstream ss(streamId);
size_t streamIndex = 0;
char separator;
int subStreamIndex = -1;
std::vector<size_t> channelIndexArray;
ss >> streamIndex;
ss >> separator;
if(separator == '.')
{
int subStreamIndex = -1;
ss >> subStreamIndex;
channelIndexArray.push_back(subStreamIndex);
}

// dummy stream, need a ICodec (audio or video)
// generated stream
if(!filename.length())
{
if(useVideoGenerator)
{
// video
avtranscoder::VideoCodec inputCodec(avtranscoder::eCodecTypeEncoder, dummyVideoCodec);
avtranscoder::VideoFrameDesc imageDesc(dummyWidth, dummyHeight, dummyPixelFormat);
inputCodec.setImageParameters(imageDesc);

transcoder.add(filename, streamIndex, subStreamIndex, transcodeProfile, inputCodec);
}
else
{
// audio
avtranscoder::AudioCodec inputCodec(avtranscoder::eCodecTypeEncoder, dummyAudioCodec);
avtranscoder::AudioFrameDesc audioDesc(dummySampleRate, dummyChannels, dummySampleFormat);
inputCodec.setAudioParameters(audioDesc);

transcoder.add(filename, streamIndex, subStreamIndex, transcodeProfile, inputCodec);
}
}
transcoder.addGenerateStream(transcodeProfile);
else
{
transcoder.add(filename, streamIndex, subStreamIndex, transcodeProfile);
avtranscoder::InputStreamDesc inputDesc(filename, streamIndex, channelIndexArray);
transcoder.addStream(inputDesc, transcodeProfile);
}
}
}
Expand All @@ -93,8 +67,6 @@ int main(int argc, char** argv)
help += "\tNo subStreamId: will process of channels of the stream\n";
help += "\tNo profileName: will rewrap the stream\n";
help += "Command line options\n";
help += "\t--generate-black: stream which not referred to an input, will generate an output video stream with black "
"images (by default generate audio stream with silence)\n";
help += "\t--verbose: set log level to AV_LOG_DEBUG\n";
help += "\t--logFile: put log in 'avtranscoder.log' file\n";
help += "\t--help: display this help\n";
Expand All @@ -116,10 +88,6 @@ int main(int argc, char** argv)
std::cout << help << std::endl;
return 0;
}
else if(arguments.at(argument) == "--generate-black")
{
useVideoGenerator = true;
}
else if(arguments.at(argument) == "--verbose")
{
avtranscoder::Logger::setLogLevel(AV_LOG_DEBUG);
Expand Down
5 changes: 3 additions & 2 deletions app/pyProcessor/pyprocessor.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ def parseConfigFile( inputConfigFile, transcoder ):
streamIndex = int(streamIndexes)
subStreamIndex = -1

transcoder.add( filename, streamIndex, subStreamIndex, profileName )
inputDesc = av.InputStreamDesc(filename, streamIndex, subStreamIndex)
transcoder.addStream(inputDesc, profileName)


# Create command-line interface
Expand All @@ -35,7 +36,7 @@ def parseConfigFile( inputConfigFile, transcoder ):
args = parser.parse_args()

# setup avtranscoder
logger = av.Logger().setLogLevel(av.AV_LOG_QUIET)
av.Logger().setLogLevel(av.AV_LOG_QUIET)
av.preloadCodecsAndFormats()

# create Transcoder
Expand Down
3 changes: 2 additions & 1 deletion app/pyRewrap/pyrewrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@

# create transcoder
transcoder = av.Transcoder( outputFile )
transcoder.add( args.inputFileName )
for streamIndex in range(0, inputFile.getProperties().getNbStreams()):
transcoder.addStream(av.InputStreamDesc(args.inputFileName, streamIndex))

# launch process
progress = av.ConsoleProgress()
Expand Down
2 changes: 1 addition & 1 deletion app/pyThumbnail/pythumbnail.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@

# create transcoder
transcoder = av.Transcoder( outputFile )
transcoder.add( outputStream )
transcoder.addStream( outputStream )

# launch process
outputFile.beginWrap()
Expand Down
7 changes: 7 additions & 0 deletions src/AvTranscoder/data/decoded/AudioFrame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,13 @@ AudioFrame::AudioFrame(const Frame& otherFrame)
{
}

std::string AudioFrame::getChannelLayoutDesc() const
{
char buf[512];
av_get_channel_layout_string(buf, sizeof(buf), getNbChannels(), getChannelLayout());
return std::string(buf);
}

size_t AudioFrame::getSize() const
{
if(getSampleFormat() == AV_SAMPLE_FMT_NONE)
Expand Down
1 change: 1 addition & 0 deletions src/AvTranscoder/data/decoded/AudioFrame.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ class AvExport AudioFrame : public Frame
size_t getSampleRate() const { return av_frame_get_sample_rate(_frame); }
size_t getNbChannels() const { return av_frame_get_channels(_frame); }
size_t getChannelLayout() const { return av_frame_get_channel_layout(_frame); }
std::string getChannelLayoutDesc() const; ///< Get a description of a channel layout (example: '5.1').
AVSampleFormat getSampleFormat() const { return static_cast<AVSampleFormat>(_frame->format); }
size_t getNbSamplesPerChannel() const { return _frame->nb_samples; }
AudioFrameDesc desc() const { return AudioFrameDesc(getSampleRate(), getNbChannels(), getSampleFormat()); }
Expand Down
73 changes: 46 additions & 27 deletions src/AvTranscoder/decoder/AudioDecoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,57 +116,76 @@ bool AudioDecoder::decodeNextFrame(Frame& frameBuffer)
return decodeNextFrame;
}

bool AudioDecoder::decodeNextFrame(Frame& frameBuffer, const size_t channelIndex)
bool AudioDecoder::decodeNextFrame(Frame& frameBuffer, const std::vector<size_t> channelIndexArray)
{
AudioFrame& audioBuffer = static_cast<AudioFrame&>(frameBuffer);

// decode all data of the next frame
AudioFrame allDataOfNextFrame(audioBuffer);
if(!decodeNextFrame(allDataOfNextFrame))
return false;

AVCodecContext& avCodecContext = _inputStream->getAudioCodec().getAVCodecContext();
const size_t srcNbChannels = avCodecContext.channels;
const size_t bytePerSample = av_get_bytes_per_sample((AVSampleFormat)frameBuffer.getAVFrame().format);

// if all channels of the stream are extracted
if(srcNbChannels == channelIndexArray.size())
return decodeNextFrame(frameBuffer);

// else decode all data in an intermediate buffer
AudioFrame allDataOfNextFrame(frameBuffer);
if(!decodeNextFrame(allDataOfNextFrame))
return false;

const int dstNbChannels = 1;
const int noAlignment = 0;
const size_t decodedSize = av_samples_get_buffer_size(NULL, dstNbChannels, frameBuffer.getAVFrame().nb_samples,
avCodecContext.sample_fmt, noAlignment);
if(decodedSize == 0)
return false;

// check if the expected channel exists
if(channelIndex > srcNbChannels - 1)
// check if each expected channel exists
for(std::vector<size_t>::const_iterator channelIndex = channelIndexArray.begin();
channelIndex != channelIndexArray.end(); ++channelIndex)
{
std::stringstream msg;
msg << "The channel at index ";
msg << channelIndex;
msg << " doesn't exist (srcNbChannels = ";
msg << srcNbChannels;
msg << ").";
throw std::runtime_error(msg.str());
if((*channelIndex) > srcNbChannels - 1)
{
std::stringstream msg;
msg << "The channel at index ";
msg << (*channelIndex);
msg << " doesn't exist (srcNbChannels = ";
msg << srcNbChannels;
msg << ").";
throw std::runtime_error(msg.str());
}
}

// copy frame properties of decoded frame
AudioFrame& audioBuffer = static_cast<AudioFrame&>(frameBuffer);
audioBuffer.copyProperties(allDataOfNextFrame);
av_frame_set_channels(&audioBuffer.getAVFrame(), 1);
av_frame_set_channel_layout(&audioBuffer.getAVFrame(), AV_CH_LAYOUT_MONO);
av_frame_set_channels(&audioBuffer.getAVFrame(), channelIndexArray.size());
av_frame_set_channel_layout(&audioBuffer.getAVFrame(), av_get_default_channel_layout(channelIndexArray.size()));
audioBuffer.setNbSamplesPerChannel(allDataOfNextFrame.getNbSamplesPerChannel());

// @todo manage cases with data of frame not only on data[0] (use _frame.linesize)
unsigned char* src = allDataOfNextFrame.getData()[0];
unsigned char* dst = audioBuffer.getData()[0];

// offset
src += channelIndex * bytePerSample;

// extract one channel
for(int sample = 0; sample < allDataOfNextFrame.getAVFrame().nb_samples; ++sample)
// extract one or more channels
for(size_t sample = 0; sample < allDataOfNextFrame.getNbSamplesPerChannel(); ++sample)
{
memcpy(dst, src, bytePerSample);
dst += bytePerSample;
src += bytePerSample * srcNbChannels;
// offset in source buffer
src += channelIndexArray.at(0) * bytePerSample;

for(size_t i = 0; i < channelIndexArray.size(); ++i)
{
memcpy(dst, src, bytePerSample);
dst += bytePerSample;

// shift to the corresponding sample in the next channel of the current layout
if(i < channelIndexArray.size() - 1)
src += (channelIndexArray.at(i + 1) - channelIndexArray.at(i)) * bytePerSample;
// else shift to the next layout
else
{
src += (srcNbChannels - channelIndexArray.at(i)) * bytePerSample;
break;
}
}
}

return true;
Expand Down
2 changes: 1 addition & 1 deletion src/AvTranscoder/decoder/AudioDecoder.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class AvExport AudioDecoder : public IDecoder
void setupDecoder(const ProfileLoader::Profile& profile = ProfileLoader::Profile());

bool decodeNextFrame(Frame& frameBuffer);
bool decodeNextFrame(Frame& frameBuffer, const size_t channelIndex);
bool decodeNextFrame(Frame& frameBuffer, const std::vector<size_t> channelIndexArray);

void flushDecoder();

Expand Down
5 changes: 3 additions & 2 deletions src/AvTranscoder/decoder/AudioGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ bool AudioGenerator::decodeNextFrame(Frame& frameBuffer)
if(frameBuffer.getAVFrame().channel_layout == 0)
{
const size_t channelLayout = av_get_default_channel_layout(frameBuffer.getAVFrame().channels);
LOG_WARN("Channel layout en the audio frame is not set. Set it to '" << channelLayout << "' to be able to copy silence data.")
LOG_WARN("Channel layout en the audio frame is not set. Set it to '" << channelLayout
<< "' to be able to copy silence data.")
av_frame_set_channel_layout(&frameBuffer.getAVFrame(), channelLayout);
}

Expand Down Expand Up @@ -71,7 +72,7 @@ bool AudioGenerator::decodeNextFrame(Frame& frameBuffer)
return true;
}

bool AudioGenerator::decodeNextFrame(Frame& frameBuffer, const size_t subStreamIndex)
bool AudioGenerator::decodeNextFrame(Frame& frameBuffer, const std::vector<size_t> channelIndexArray)
{
return decodeNextFrame(frameBuffer);
}
Expand Down
6 changes: 3 additions & 3 deletions src/AvTranscoder/decoder/AudioGenerator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@ class AvExport AudioGenerator : public IDecoder
~AudioGenerator();

bool decodeNextFrame(Frame& frameBuffer);
bool decodeNextFrame(Frame& frameBuffer, const size_t subStreamIndex);
bool decodeNextFrame(Frame& frameBuffer, const std::vector<size_t> channelIndexArray);

void setNextFrame(Frame& inputFrame) { _inputFrame = &inputFrame; }

private:
Frame* _inputFrame; ///< Has link (no ownership)
AudioFrame* _silent; ///< The generated silent (has ownership)
Frame* _inputFrame; ///< Has link (no ownership)
AudioFrame* _silent; ///< The generated silent (has ownership)
const AudioFrameDesc _frameDesc; ///< The description of the silence (sampleRate, channels...)
};
}
Expand Down
4 changes: 2 additions & 2 deletions src/AvTranscoder/decoder/IDecoder.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,10 @@ class AvExport IDecoder
/**
* @brief Decode substream of next frame
* @param frameBuffer: the frame decoded
* @param channelIndex: index of channel to extract
* @param channelIndexArray: list of channels to extract
* @return status of decoding
*/
virtual bool decodeNextFrame(Frame& frameBuffer, const size_t channelIndex) = 0;
virtual bool decodeNextFrame(Frame& frameBuffer, const std::vector<size_t> channelIndexArray) = 0;

/**
* @brief Set the next frame of the input stream (which bypass the work of decoding)
Expand Down
2 changes: 1 addition & 1 deletion src/AvTranscoder/decoder/VideoDecoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ bool VideoDecoder::decodeNextFrame(Frame& frameBuffer)
return decodeNextFrame;
}

bool VideoDecoder::decodeNextFrame(Frame& frameBuffer, const size_t subStreamIndex)
bool VideoDecoder::decodeNextFrame(Frame& frameBuffer, const std::vector<size_t> channelIndexArray)
{
return false;
}
Expand Down
2 changes: 1 addition & 1 deletion src/AvTranscoder/decoder/VideoDecoder.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class AvExport VideoDecoder : public IDecoder
void setupDecoder(const ProfileLoader::Profile& profile = ProfileLoader::Profile());

bool decodeNextFrame(Frame& frameBuffer);
bool decodeNextFrame(Frame& frameBuffer, const size_t subStreamIndex);
bool decodeNextFrame(Frame& frameBuffer, const std::vector<size_t> channelIndexArray);

void flushDecoder();

Expand Down
2 changes: 1 addition & 1 deletion src/AvTranscoder/decoder/VideoGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ bool VideoGenerator::decodeNextFrame(Frame& frameBuffer)
return true;
}

bool VideoGenerator::decodeNextFrame(Frame& frameBuffer, const size_t channelIndex)
bool VideoGenerator::decodeNextFrame(Frame& frameBuffer, const std::vector<size_t> channelIndexArray)
{
return false;
}
Expand Down
6 changes: 3 additions & 3 deletions src/AvTranscoder/decoder/VideoGenerator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@ class AvExport VideoGenerator : public IDecoder
~VideoGenerator();

bool decodeNextFrame(Frame& frameBuffer);
bool decodeNextFrame(Frame& frameBuffer, const size_t channelIndex);
bool decodeNextFrame(Frame& frameBuffer, const std::vector<size_t> channelIndexArray);

void setNextFrame(Frame& inputFrame) { _inputFrame = &inputFrame; }

private:
Frame* _inputFrame; ///< A frame given from outside (has link, no ownership)
VideoFrame* _blackImage; ///< The generated black image (has ownership)
Frame* _inputFrame; ///< A frame given from outside (has link, no ownership)
VideoFrame* _blackImage; ///< The generated black image (has ownership)
const VideoFrameDesc _frameDesc; ///< The description of the black image (width, height...)
};
}
Expand Down
1 change: 0 additions & 1 deletion src/AvTranscoder/encoder/AudioEncoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -139,5 +139,4 @@ bool AudioEncoder::encode(const AVFrame* decodedData, AVPacket& encodedData)
return true;
#endif
}

}
1 change: 0 additions & 1 deletion src/AvTranscoder/encoder/VideoEncoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,5 +152,4 @@ bool VideoEncoder::encode(const AVFrame* decodedData, AVPacket& encodedData)
return true;
#endif
}

}
2 changes: 1 addition & 1 deletion src/AvTranscoder/file/FormatContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ AVStream& FormatContext::addAVStream(const AVCodec& avCodec)

bool FormatContext::seek(const uint64_t position, const int flag)
{
LOG_INFO("Seek in '" << _avFormatContext->filename << "' at " << position << " with flag '"<< flag << "'")
LOG_INFO("Seek in '" << _avFormatContext->filename << "' at " << position << " with flag '" << flag << "'")
const int err = av_seek_frame(_avFormatContext, -1, position, flag);
if(err < 0)
{
Expand Down
3 changes: 2 additions & 1 deletion src/AvTranscoder/file/FormatContext.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ class AvExport FormatContext
* @param position: can be in AV_TIME_BASE units, in frames... depending on the flag value
* @param flag: seeking mode (AVSEEK_FLAG_xxx)
* @return seek status
* @warn seeking on a raw bitstreams (without any container) could produce an error (because of a lack of timing information)
* @warn seeking on a raw bitstreams (without any container) could produce an error (because of a lack of timing
* information)
* @see flushDecoder
*/
bool seek(const uint64_t position, const int flag);
Expand Down
Loading