diff --git a/app/avPlay/Window.cpp b/app/avPlay/Window.cpp index 32e4f2f9..4bb9d006 100644 --- a/app/avPlay/Window.cpp +++ b/app/avPlay/Window.cpp @@ -1,6 +1,7 @@ - #include "Window.hpp" +#include + #ifdef __APPLE__ #include #include @@ -103,8 +104,8 @@ void loadNewTexture(const char* data, GLint internalFormat, size_t width, size_t Window::Window(avtranscoder::VideoReader& reader) { _reader = &reader; - _width = _reader->getWidth(); - _height = _reader->getHeight(); + _width = _reader->getOutputWidth(); + _height = _reader->getOutputHeight(); char* argv[2] = {(char*)"", NULL}; int argc = 1; @@ -439,7 +440,9 @@ void Window::displayInformations() std::cout << textureType << " " << _width << "x" << _height << std::endl; // stream info - _reader->printInfo(); + const avtranscoder::VideoProperties* properties = _reader->getSourceVideoProperties(); + if(properties != NULL) + std::cout << *properties << std::endl; } void Window::move(float x, float y) @@ -547,15 +550,17 @@ void Window::showAlphaChannelTexture() void Window::displayNextFrame() { - const char* buffer = (const char*)_reader->readNextFrame()->getData(); - loadNewTexture(buffer, _reader->getComponents(), _reader->getWidth(), _reader->getHeight(), GL_RGB, GL_UNSIGNED_BYTE); + const char* buffer = (const char*)_reader->readNextFrame()->getData()[0]; + loadNewTexture(buffer, _reader->getOutputNbComponents(), _reader->getOutputWidth(), _reader->getOutputHeight(), GL_RGB, + GL_UNSIGNED_BYTE); display(); } void Window::displayPrevFrame() { - const char* buffer = (const char*)_reader->readPrevFrame()->getData(); - loadNewTexture(buffer, _reader->getComponents(), _reader->getWidth(), _reader->getHeight(), GL_RGB, GL_UNSIGNED_BYTE); + const char* buffer = (const char*)_reader->readPrevFrame()->getData()[0]; + loadNewTexture(buffer, _reader->getOutputNbComponents(), _reader->getOutputWidth(), _reader->getOutputHeight(), GL_RGB, + GL_UNSIGNED_BYTE); display(); } @@ -566,8 +571,9 @@ void Window::displayFirstFrame() void Window::displayAtFrame(const size_t frame) { - const char* buffer = (const char*)_reader->readFrameAt(frame)->getData(); - loadNewTexture(buffer, _reader->getComponents(), _reader->getWidth(), _reader->getHeight(), GL_RGB, GL_UNSIGNED_BYTE); + const char* buffer = (const char*)_reader->readFrameAt(frame)->getData()[0]; + loadNewTexture(buffer, _reader->getOutputNbComponents(), _reader->getOutputWidth(), _reader->getOutputHeight(), GL_RGB, + GL_UNSIGNED_BYTE); display(); } diff --git a/app/avPlay/main.cpp b/app/avPlay/main.cpp index 4dbb0e9a..2dd6ff1b 100644 --- a/app/avPlay/main.cpp +++ b/app/avPlay/main.cpp @@ -3,21 +3,90 @@ #include "Window.hpp" +#include + int main(int argc, char** argv) { - avtranscoder::preloadCodecsAndFormats(); + std::string filename; + size_t streamIndex = 0; + size_t width = 0; + size_t height = 0; + + std::string help; + help += "Usage\n"; + help += "\tavplay filename [streamIndex] [--width width] [--height height] [--help]\n"; + help += "Command line options\n"; + help += "\tstreamIndex: specify the index of the stream to read (by default 0)\n"; + help += "\t--width: specify the output width (by default the same as input)\n"; + help += "\t--height: specify the output height (by default the same as input)\n"; + help += "\t--help: display this help\n"; + // List command line arguments + std::vector arguments; + for(int argument = 1; argument < argc; ++argument) + { + arguments.push_back(argv[argument]); + } + for(size_t argument = 0; argument < arguments.size(); ++argument) + { + if(arguments.at(argument) == "--help") + { + std::cout << help << std::endl; + return 0; + } + else if(arguments.at(argument) == "--width") + { + try + { + width = atoi(arguments.at(++argument).c_str()); + } + catch(...) + { + std::cout << help << std::endl; + return 0; + } + } + else if(arguments.at(argument) == "--height") + { + try + { + height = atoi(arguments.at(++argument).c_str()); + } + catch(...) + { + std::cout << help << std::endl; + return 0; + } + } + // positional arguments + if(argument == 0) + { + filename = arguments.at(argument); + } + else if(argument == 1) + { + streamIndex = atoi(arguments.at(argument).c_str()); + } + } + + // Check required arguments if(argc < 2) { std::cout << "avplay can play the given video media file." << std::endl; - std::cout << "Provide the filename and the streamIndex (0 by default)" << std::endl; + std::cout << "Use option --help to display help" << std::endl; return (-1); } - const std::string filename(argv[1]); - const size_t streamIndex = argc > 2 ? atoi(argv[2]) : 0; + // Setup avtranscoder + avtranscoder::preloadCodecsAndFormats(); + avtranscoder::Logger::setLogLevel(AV_LOG_QUIET); avtranscoder::VideoReader reader(filename, streamIndex); + if(width == 0) + width = reader.getOutputWidth(); + if(height == 0) + height = reader.getOutputHeight(); + reader.updateOutput(width, height, "rgb24"); Window window(reader); window.launch(); } diff --git a/src/AvTranscoder/Library.cpp b/src/AvTranscoder/Library.cpp index 299512ab..80cedf17 100644 --- a/src/AvTranscoder/Library.cpp +++ b/src/AvTranscoder/Library.cpp @@ -77,7 +77,7 @@ Libraries getLibraries() { Libraries libs; - libs.push_back(Library("avtranscoder", "GPL or LGPL version 3", AVTRANSCODER_VERSION_MAJOR, AVTRANSCODER_VERSION_MINOR, + libs.push_back(Library("avtranscoder", "GPL v2 or LGPL v2.1", AVTRANSCODER_VERSION_MAJOR, AVTRANSCODER_VERSION_MINOR, AVTRANSCODER_VERSION_MICRO)); libs.push_back( Library("avutil", avutil_license(), LIBAVUTIL_VERSION_MAJOR, LIBAVUTIL_VERSION_MINOR, LIBAVUTIL_VERSION_MICRO)); diff --git a/src/AvTranscoder/avTranscoder.i b/src/AvTranscoder/avTranscoder.i index 1ec80796..c5578383 100644 --- a/src/AvTranscoder/avTranscoder.i +++ b/src/AvTranscoder/avTranscoder.i @@ -23,7 +23,7 @@ %include "AvTranscoder/progress/progress.i" %include "AvTranscoder/properties/properties.i" -%include "AvTranscoder/frame/frame.i" +%include "AvTranscoder/data/data.i" %include "AvTranscoder/profile/profile.i" %include diff --git a/src/AvTranscoder/codec/AudioCodec.cpp b/src/AvTranscoder/codec/AudioCodec.cpp index 4cbec76d..f5460f5e 100644 --- a/src/AvTranscoder/codec/AudioCodec.cpp +++ b/src/AvTranscoder/codec/AudioCodec.cpp @@ -24,14 +24,13 @@ AudioCodec::AudioCodec(const ECodecType type, AVCodecContext& avCodecContext) AudioFrameDesc AudioCodec::getAudioFrameDesc() const { assert(_avCodecContext != NULL); - AudioFrameDesc audioFrameDesc(_avCodecContext->sample_rate, _avCodecContext->channels, _avCodecContext->sample_fmt); - return audioFrameDesc; + return AudioFrameDesc(_avCodecContext->sample_rate, _avCodecContext->channels, _avCodecContext->sample_fmt); } void AudioCodec::setAudioParameters(const AudioFrameDesc& audioFrameDesc) { - _avCodecContext->sample_rate = audioFrameDesc.getSampleRate(); - _avCodecContext->channels = audioFrameDesc.getChannels(); - _avCodecContext->sample_fmt = audioFrameDesc.getSampleFormat(); + _avCodecContext->sample_rate = audioFrameDesc._sampleRate; + _avCodecContext->channels = audioFrameDesc._nbChannels; + _avCodecContext->sample_fmt = audioFrameDesc._sampleFormat; } } diff --git a/src/AvTranscoder/codec/AudioCodec.hpp b/src/AvTranscoder/codec/AudioCodec.hpp index 2ac1691b..9b380171 100644 --- a/src/AvTranscoder/codec/AudioCodec.hpp +++ b/src/AvTranscoder/codec/AudioCodec.hpp @@ -2,7 +2,7 @@ #define _AV_TRANSCODER_CODEC_AUDIO_CODEC_HPP_ #include "ICodec.hpp" -#include +#include namespace avtranscoder { diff --git a/src/AvTranscoder/codec/VideoCodec.cpp b/src/AvTranscoder/codec/VideoCodec.cpp index f09e5d29..081aba82 100644 --- a/src/AvTranscoder/codec/VideoCodec.cpp +++ b/src/AvTranscoder/codec/VideoCodec.cpp @@ -27,17 +27,17 @@ VideoFrameDesc VideoCodec::getVideoFrameDesc() const VideoFrameDesc videoFrameDesc(_avCodecContext->width, _avCodecContext->height, _avCodecContext->pix_fmt); double fps = 1.0 * _avCodecContext->time_base.den / (_avCodecContext->time_base.num * _avCodecContext->ticks_per_frame); if(!std::isinf(fps)) - videoFrameDesc.setFps(fps); + videoFrameDesc._fps = fps; return videoFrameDesc; } void VideoCodec::setImageParameters(const VideoFrameDesc& videoFrameDesc) { - _avCodecContext->width = videoFrameDesc.getWidth(); - _avCodecContext->height = videoFrameDesc.getHeight(); - _avCodecContext->pix_fmt = videoFrameDesc.getPixelFormat(); + _avCodecContext->width = videoFrameDesc._width; + _avCodecContext->height = videoFrameDesc._height; + _avCodecContext->pix_fmt = videoFrameDesc._pixelFormat; _avCodecContext->time_base.num = 1; - _avCodecContext->time_base.den = videoFrameDesc.getFps(); + _avCodecContext->time_base.den = videoFrameDesc._fps; _avCodecContext->ticks_per_frame = 1; } } diff --git a/src/AvTranscoder/codec/VideoCodec.hpp b/src/AvTranscoder/codec/VideoCodec.hpp index 63470d21..aec23e43 100644 --- a/src/AvTranscoder/codec/VideoCodec.hpp +++ b/src/AvTranscoder/codec/VideoCodec.hpp @@ -2,7 +2,7 @@ #define _AV_TRANSCODER_CODEC_VIDEO_CODEC_HPP_ #include "ICodec.hpp" -#include +#include namespace avtranscoder { diff --git a/src/AvTranscoder/frame/Frame.cpp b/src/AvTranscoder/data/coded/CodedData.cpp similarity index 68% rename from src/AvTranscoder/frame/Frame.cpp rename to src/AvTranscoder/data/coded/CodedData.cpp index 55c4007d..44eeaa6a 100644 --- a/src/AvTranscoder/frame/Frame.cpp +++ b/src/AvTranscoder/data/coded/CodedData.cpp @@ -1,47 +1,47 @@ -#include "Frame.hpp" +#include "CodedData.hpp" #include namespace avtranscoder { -Frame::Frame() +CodedData::CodedData() : _avStream(NULL) { initAVPacket(); } -Frame::Frame(const size_t dataSize) +CodedData::CodedData(const size_t dataSize) : _avStream(NULL) { av_new_packet(&_packet, dataSize); } -Frame::Frame(const AVPacket& avPacket) +CodedData::CodedData(const AVPacket& avPacket) : _avStream(NULL) { copyAVPacket(avPacket); } -Frame::Frame(const Frame& other) +CodedData::CodedData(const CodedData& other) { copyAVPacket(other.getAVPacket()); _avStream = other.getAVStream(); } -Frame& Frame::operator=(const Frame& other) +CodedData& CodedData::operator=(const CodedData& other) { copyAVPacket(other.getAVPacket()); _avStream = other.getAVStream(); return *this; } -Frame::~Frame() +CodedData::~CodedData() { av_free_packet(&_packet); } -void Frame::resize(const size_t newSize) +void CodedData::resize(const size_t newSize) { if((int)newSize < _packet.size) av_shrink_packet(&_packet, newSize); @@ -49,45 +49,45 @@ void Frame::resize(const size_t newSize) av_grow_packet(&_packet, newSize - _packet.size); } -void Frame::refData(unsigned char* buffer, const size_t size) +void CodedData::refData(unsigned char* buffer, const size_t size) { _packet.data = buffer; _packet.size = size; } -void Frame::copyData(unsigned char* buffer, const size_t size) +void CodedData::copyData(unsigned char* buffer, const size_t size) { resize(size); if(size != 0) memcpy(_packet.data, buffer, _packet.size); } -void Frame::refData(Frame& frame) +void CodedData::refData(CodedData& frame) { _packet.data = frame.getData(); _packet.size = frame.getSize(); } -void Frame::clear() +void CodedData::clear() { av_free_packet(&_packet); initAVPacket(); } -void Frame::assign(const size_t size, const int value) +void CodedData::assign(const size_t size, const int value) { resize(size); memset(_packet.data, value, size); } -void Frame::initAVPacket() +void CodedData::initAVPacket() { av_init_packet(&_packet); _packet.data = NULL; _packet.size = 0; } -void Frame::copyAVPacket(const AVPacket& avPacket) +void CodedData::copyAVPacket(const AVPacket& avPacket) { #if AVTRANSCODER_FFMPEG_DEPENDENCY && LIBAVCODEC_VERSION_INT > AV_VERSION_INT(54, 56, 0) // Need const_cast for libav versions from 54.56. to 55.56. diff --git a/src/AvTranscoder/frame/Frame.hpp b/src/AvTranscoder/data/coded/CodedData.hpp similarity index 70% rename from src/AvTranscoder/frame/Frame.hpp rename to src/AvTranscoder/data/coded/CodedData.hpp index cb6169d2..e40da4a2 100644 --- a/src/AvTranscoder/frame/Frame.hpp +++ b/src/AvTranscoder/data/coded/CodedData.hpp @@ -1,5 +1,5 @@ -#ifndef _AV_TRANSCODER_FRAME_FRAME_HPP_ -#define _AV_TRANSCODER_FRAME_FRAME_HPP_ +#ifndef _AV_TRANSCODER_FRAME_CODEDDATA_HPP_ +#define _AV_TRANSCODER_FRAME_CODEDDATA_HPP_ #include @@ -12,36 +12,41 @@ struct AVStream; namespace avtranscoder { -class AvExport Frame +/** + * @brief This class describes coded data. + */ +class AvExport CodedData { public: - /// Create a frame with empty buffer data - Frame(); + /// Create an empty data buffer + CodedData(); - /// Create a frame with a the given buffer size - Frame(const size_t dataSize); + /// Create a data buffer with a the given size + CodedData(const size_t dataSize); #ifndef SWIG - /// Create a frame from the given AVPAcket (copy data of given packet) - Frame(const AVPacket& avPacket); + /// Create a data buffer from the given AVPAcket (copy data of given packet) + CodedData(const AVPacket& avPacket); #endif /// Override copy constructor in order to copy AVPacket data - Frame(const Frame& other); + CodedData(const CodedData& other); /// Override operator = in order to copy AVPacket data - Frame& operator=(const Frame& other); + CodedData& operator=(const CodedData& other); /// Free buffer of data - ~Frame(); + ~CodedData(); +#ifndef SWIG void refAVStream(const AVStream& avStream) { _avStream = &avStream; } +#endif /// Resize data buffer void resize(const size_t newSize); ///@{ /// Ref to external data buffer - void refData(Frame& frame); + void refData(CodedData& frame); void refData(unsigned char* buffer, const size_t size); ///@} @@ -58,6 +63,10 @@ class AvExport Frame void clear(); unsigned char* getData() { return _packet.data; } +#ifndef SWIG + const unsigned char* getData() const { return _packet.data; } +#endif + size_t getSize() const { return _packet.size; } #ifndef SWIG @@ -68,7 +77,6 @@ class AvExport Frame const AVStream* getAVStream() const { return _avStream; } AVPacket& getAVPacket() { return _packet; } const AVPacket& getAVPacket() const { return _packet; } - const unsigned char* getData() const { return _packet.data; } #endif private: @@ -81,11 +89,6 @@ class AvExport Frame // Stream which contains the packet const AVStream* _avStream; //< Has link (no ownership) }; - -// Typedef to represent buffer of coded data. -// Example 1: in case of image, no sense to get size if coded data. -// Example 2: in case of audio, no sense to get number of channels if coded data. -typedef Frame CodedData; } #endif diff --git a/src/AvTranscoder/data/data.i b/src/AvTranscoder/data/data.i new file mode 100644 index 00000000..117c1f74 --- /dev/null +++ b/src/AvTranscoder/data/data.i @@ -0,0 +1,13 @@ +%apply char * { unsigned char * }; + +%{ +#include +#include +#include +#include +%} + +%include +%include +%include +%include diff --git a/src/AvTranscoder/data/decoded/AudioFrame.cpp b/src/AvTranscoder/data/decoded/AudioFrame.cpp new file mode 100644 index 00000000..4da72ccd --- /dev/null +++ b/src/AvTranscoder/data/decoded/AudioFrame.cpp @@ -0,0 +1,124 @@ +#include "AudioFrame.hpp" + +#include + +extern "C" { +#include +#include +} + +#include + +namespace avtranscoder +{ + +AudioFrameDesc::AudioFrameDesc(const size_t sampleRate, const size_t nbChannels, const AVSampleFormat sampleFormat) + : _sampleRate(sampleRate) + , _nbChannels(nbChannels) + , _sampleFormat(sampleFormat) +{ +} + +AudioFrameDesc::AudioFrameDesc(const size_t sampleRate, const size_t nbChannels, const std::string& sampleFormatName) + : _sampleRate(sampleRate) + , _nbChannels(nbChannels) + , _sampleFormat(getAVSampleFormat(sampleFormatName)) +{ +} + +void AudioFrameDesc::setParameters(const ProfileLoader::Profile& profile) +{ + // sample rate + if(profile.count(constants::avProfileSampleRate)) + _sampleRate = atoi(profile.find(constants::avProfileSampleRate)->second.c_str()); + // channel + if(profile.count(constants::avProfileChannel)) + _nbChannels = atoi(profile.find(constants::avProfileChannel)->second.c_str()); + // sample format + if(profile.count(constants::avProfileSampleFormat)) + _sampleFormat = getAVSampleFormat(profile.find(constants::avProfileSampleFormat)->second.c_str()); +} + +AudioFrame::AudioFrame(const AudioFrameDesc& ref) + : Frame() +{ + allocateAVSample(ref); +} + +AudioFrame::AudioFrame(const Frame& otherFrame) + : Frame(otherFrame) +{ +} + +size_t AudioFrame::getSize() const +{ + if(getSampleFormat() == AV_SAMPLE_FMT_NONE) + { + LOG_WARN("Incorrect sample format when get size of audio frame: return a size of 0.") + return 0; + } + + const size_t size = getNbSamplesPerChannel() * getNbChannels() * av_get_bytes_per_sample(getSampleFormat()); + if(size == 0) + { + std::stringstream msg; + msg << "Unable to determine audio buffer size:" << std::endl; + msg << "nb sample per channel = " << getNbSamplesPerChannel() << std::endl; + msg << "channels = " << getNbChannels() << std::endl; + msg << "bytes per sample = " << av_get_bytes_per_sample(getSampleFormat()) << std::endl; + throw std::runtime_error(msg.str()); + } + return size; +} + +void AudioFrame::allocateAVSample(const AudioFrameDesc& desc) +{ + // Set Frame properties + _frame->sample_rate = desc._sampleRate; + _frame->channels = desc._nbChannels; + _frame->channel_layout = av_get_default_channel_layout(desc._nbChannels); + _frame->format = desc._sampleFormat; + _frame->nb_samples = desc._sampleRate / 25.; // cannot be known before calling avcodec_decode_audio4 + + // Allocate data + const int align = 0; + const int ret = + av_samples_alloc(_frame->data, _frame->linesize, _frame->channels, _frame->nb_samples, desc._sampleFormat, align); + if(ret < 0) + { + std::stringstream os; + os << "Unable to allocate an audio frame of "; + os << "sample rate = " << _frame->sample_rate << ", "; + os << "nb channels = " << _frame->channels << ", "; + os << "channel layout = " << av_get_channel_name(_frame->channels) << ", "; + os << "nb samples = " << _frame->nb_samples << ", "; + os << "sample format = " << getSampleFormatName(desc._sampleFormat); + throw std::runtime_error(os.str()); + } +} + +void AudioFrame::assign(const unsigned char value) +{ + // Create the audio buffer + // The buffer will be freed in destructor of based class + const int audioSize = getSize(); + unsigned char* audioBuffer = new unsigned char[audioSize]; + memset(audioBuffer, value, audioSize); + + // Fill the picture + assign(audioBuffer); +} + +void AudioFrame::assign(const unsigned char* ptrValue) +{ + const int align = 0; + const int ret = av_samples_fill_arrays(_frame->data, _frame->linesize, ptrValue, getNbChannels(), + getNbSamplesPerChannel(), getSampleFormat(), align); + if(ret < 0) + { + std::stringstream os; + os << "Unable to assign an audio buffer: " << getDescriptionFromErrorCode(ret); + throw std::runtime_error(os.str()); + } +} +} diff --git a/src/AvTranscoder/data/decoded/AudioFrame.hpp b/src/AvTranscoder/data/decoded/AudioFrame.hpp new file mode 100644 index 00000000..3a68fa16 --- /dev/null +++ b/src/AvTranscoder/data/decoded/AudioFrame.hpp @@ -0,0 +1,72 @@ +#ifndef _AV_TRANSCODER_FRAME_AUDIO_FRAME_HPP_ +#define _AV_TRANSCODER_FRAME_AUDIO_FRAME_HPP_ + +#include "Frame.hpp" +#include + +namespace avtranscoder +{ + +/** + * @brief Description to create an audio frame. + * This corresponds to the number of samples, which corresponds to one video frame. + */ +struct AvExport AudioFrameDesc +{ +public: + AudioFrameDesc(const size_t sampleRate = 0, const size_t channels = 0, + const AVSampleFormat sampleFormat = AV_SAMPLE_FMT_NONE); + AudioFrameDesc(const size_t sampleRate, const size_t channels, const std::string& sampleFormatName); + + /** + * @brief Set the attributes from the given profile. + * @see Profile + */ + void setParameters(const ProfileLoader::Profile& profile); + +public: + size_t _sampleRate; + size_t _nbChannels; + AVSampleFormat _sampleFormat; +}; + +/** + * @brief This class describes decoded audio data. + */ +class AvExport AudioFrame : public Frame +{ +public: + /** + * @note Allocated data will be initialized to silence. + */ + AudioFrame(const AudioFrameDesc& ref); + AudioFrame(const Frame& otherFrame); + + size_t getSampleRate() const { return av_frame_get_sample_rate(_frame); } + size_t getNbChannels() const { return av_frame_get_channels(_frame); } + AVSampleFormat getSampleFormat() const { return static_cast(_frame->format); } + size_t getNbSamplesPerChannel() const { return _frame->nb_samples; } + AudioFrameDesc desc() const { return AudioFrameDesc(getSampleRate(), getNbChannels(), getSampleFormat()); } + + size_t getSize() const; ///< in bytes + + void setNbSamplesPerChannel(const size_t nbSamples) { _frame->nb_samples = nbSamples; } + + /** + * @brief Assign the given value to all the data of the audio frame. + */ + void assign(const unsigned char value); + + /** + * @brief Assign the given ptr of data to the data of the audio frame. + * @warning the given ptr should have the size of the audio frame.. + * @see getSize + */ + void assign(const unsigned char* ptrValue); + +private: + void allocateAVSample(const AudioFrameDesc& ref); +}; +} + +#endif diff --git a/src/AvTranscoder/data/decoded/Frame.cpp b/src/AvTranscoder/data/decoded/Frame.cpp new file mode 100644 index 00000000..9a35e7da --- /dev/null +++ b/src/AvTranscoder/data/decoded/Frame.cpp @@ -0,0 +1,83 @@ +#include "Frame.hpp" + +#include + +namespace avtranscoder +{ + +Frame::Frame() + : _frame(NULL) +{ + allocateAVFrame(); +} + +Frame::Frame(const Frame& otherFrame) + : _frame(NULL) +{ + // allocate frame + allocateAVFrame(); + // check if the frame could be a valid video/audio frame + if(otherFrame.getAVFrame().format == -1) + return; + // reference the other frame + refFrame(otherFrame); +} + +Frame::~Frame() +{ + if(_frame != NULL) + { +#if LIBAVCODEC_VERSION_MAJOR > 54 + av_frame_free(&_frame); +#else +#if LIBAVCODEC_VERSION_MAJOR > 53 + avcodec_free_frame(&_frame); +#else + av_free(_frame); +#endif +#endif + _frame = NULL; + } +} + +void Frame::copyData(const Frame& frameToRef) +{ + const int ret = av_frame_copy(_frame, &frameToRef.getAVFrame()); + if(ret < 0) + { + throw std::ios_base::failure("Unable to copy data of other frame: " + getDescriptionFromErrorCode(ret)); + } +} + +void Frame::copyProperties(const Frame& otherFrame) +{ + av_frame_copy_props(_frame, &otherFrame.getAVFrame()); +} + +void Frame::refFrame(const Frame& otherFrame) +{ + const int ret = av_frame_ref(_frame, &otherFrame.getAVFrame()); + if(ret < 0) + { + throw std::ios_base::failure("Unable to reference other frame: " + getDescriptionFromErrorCode(ret)); + } +} + +void Frame::clear() +{ + av_frame_unref(_frame); +} + +void Frame::allocateAVFrame() +{ +#if LIBAVCODEC_VERSION_MAJOR > 54 + _frame = av_frame_alloc(); +#else + _frame = avcodec_alloc_frame(); +#endif + if(_frame == NULL) + { + throw std::runtime_error("Unable to allocate an empty Frame."); + } +} +} diff --git a/src/AvTranscoder/data/decoded/Frame.hpp b/src/AvTranscoder/data/decoded/Frame.hpp new file mode 100644 index 00000000..505f66a8 --- /dev/null +++ b/src/AvTranscoder/data/decoded/Frame.hpp @@ -0,0 +1,80 @@ +#ifndef _AV_TRANSCODER_FRAME_FRAME_HPP_ +#define _AV_TRANSCODER_FRAME_FRAME_HPP_ + +#include + +extern "C" { +#include +} + +namespace avtranscoder +{ + +/** + * @brief This class describes decoded (raw) audio or video data. + */ +class AvExport Frame +{ +public: + /** + * @brief Allocate an empty frame. + */ + Frame(); + + /** + * @brief Copy properties and reference data of the other frame + */ + Frame(const Frame& otherFrame); + + virtual ~Frame(); + + /** + * @brief Get all the data of the frame. + */ + unsigned char** getData() { return _frame->data; } + + /** + * @brief Returns the size in byte. + * For video, size in bytes of each picture line. + * For audio, size in bytes of each plane. + * @note For audio, only linesize[0] may be set. For planar audio, each channel + * plane must be the same size. + */ + int* getLineSize() const { return _frame->linesize; } + + /** + * @brief Copy the data of the given Frame. + */ + void copyData(const Frame& frameToRef); + + /** + * @brief Copy all the fields that do not affect the data layout in the buffers. + */ + void copyProperties(const Frame& otherFrame); + + /** + * @brief Copy frame properties and create a new reference to data of the given frame. + * @warning This method allocates new data that will be freed only by calling the destructor of the referenced frame. + */ + void refFrame(const Frame& otherFrame); + + /** + * @brief Unreference all the buffers referenced by frame and reset the frame fields. + */ + void clear(); + +#ifndef SWIG + AVFrame& getAVFrame() { return *_frame; } + const AVFrame& getAVFrame() const { return *_frame; } + const unsigned char** getData() const { return const_cast(_frame->data); } +#endif + +private: + void allocateAVFrame(); + +protected: + AVFrame* _frame; +}; +} + +#endif diff --git a/src/AvTranscoder/data/decoded/VideoFrame.cpp b/src/AvTranscoder/data/decoded/VideoFrame.cpp new file mode 100644 index 00000000..c7f8dcf8 --- /dev/null +++ b/src/AvTranscoder/data/decoded/VideoFrame.cpp @@ -0,0 +1,113 @@ +#include "VideoFrame.hpp" + +#include + +extern "C" { +#include +#include +} + +#include +#include + +namespace avtranscoder +{ + +VideoFrameDesc::VideoFrameDesc(const size_t width, const size_t height, const AVPixelFormat pixelFormat) + : _width(width) + , _height(height) + , _pixelFormat(pixelFormat) + , _fps(1.0) +{ +} + +VideoFrameDesc::VideoFrameDesc(const size_t width, const size_t height, const std::string& pixelFormatName) + : _width(width) + , _height(height) + , _pixelFormat(getAVPixelFormat(pixelFormatName)) + , _fps(1.0) +{ +} + +void VideoFrameDesc::setParameters(const ProfileLoader::Profile& profile) +{ + // width + if(profile.count(constants::avProfileWidth)) + _width = atoi(profile.find(constants::avProfileWidth)->second.c_str()); + // height + if(profile.count(constants::avProfileHeight)) + _height = atoi(profile.find(constants::avProfileHeight)->second.c_str()); + // pixel format + if(profile.count(constants::avProfilePixelFormat)) + _pixelFormat = getAVPixelFormat(profile.find(constants::avProfilePixelFormat)->second); + // fps + if(profile.count(constants::avProfileFrameRate)) + _fps = atof(profile.find(constants::avProfileFrameRate)->second.c_str()); +} + +VideoFrame::VideoFrame(const VideoFrameDesc& ref) + : Frame() +{ + allocateAVPicture(ref); +} + +VideoFrame::VideoFrame(const Frame& otherFrame) + : Frame(otherFrame) +{ +} + +size_t VideoFrame::getSize() const +{ + if(getPixelFormat() == AV_PIX_FMT_NONE) + { + LOG_WARN("Incorrect pixel format when get size of video frame: return a size of 0.") + return 0; + } + + const size_t size = avpicture_get_size(getPixelFormat(), getWidth(), getHeight()); + if(size == 0) + throw std::runtime_error("unable to determine image buffer size"); + return size; +} + +void VideoFrame::allocateAVPicture(const VideoFrameDesc& desc) +{ + const int ret = avpicture_alloc(reinterpret_cast(_frame), desc._pixelFormat, desc._width, desc._height); + if(ret < 0) + { + std::stringstream os; + os << "Unable to allocate an image frame of "; + os << "width = " << desc._width << ", "; + os << "height = " << desc._height << ", "; + os << "pixel format = " << desc._pixelFormat; + throw std::runtime_error(os.str()); + } + _frame->width = desc._width; + _frame->height = desc._height; + _frame->format = desc._pixelFormat; +} + +void VideoFrame::assign(const unsigned char value) +{ + // Create the image buffer + // The buffer will be freed in destructor of based class + const int imageSize = getSize(); + unsigned char* imageBuffer = new unsigned char[imageSize]; + memset(imageBuffer, value, imageSize); + + // Fill the picture + assign(imageBuffer); +} + +void VideoFrame::assign(const unsigned char* ptrValue) +{ + const int ret = + avpicture_fill(reinterpret_cast(_frame), ptrValue, getPixelFormat(), getWidth(), getHeight()); + if(ret < 0) + { + std::stringstream os; + os << "Unable to assign an image buffer of " << getSize() << " bytes: " << getDescriptionFromErrorCode(ret); + throw std::runtime_error(os.str()); + } +} +} diff --git a/src/AvTranscoder/data/decoded/VideoFrame.hpp b/src/AvTranscoder/data/decoded/VideoFrame.hpp new file mode 100644 index 00000000..ae9e22f9 --- /dev/null +++ b/src/AvTranscoder/data/decoded/VideoFrame.hpp @@ -0,0 +1,73 @@ +#ifndef _AV_TRANSCODER_FRAME_VIDEO_FRAME_HPP_ +#define _AV_TRANSCODER_FRAME_VIDEO_FRAME_HPP_ + +#include "Frame.hpp" +#include + +extern "C" { +#include +#include +} + +namespace avtranscoder +{ + +/** + * @brief Description to create a video frame. + * @param width + * @param height + * @param pixelFormat + */ +struct AvExport VideoFrameDesc +{ +public: + VideoFrameDesc(const size_t width = 0, const size_t height = 0, const AVPixelFormat pixelFormat = AV_PIX_FMT_NONE); + VideoFrameDesc(const size_t width, const size_t height, const std::string& pixelFormatName); + + /** + * @brief Set the attributes from the given profile. + * @see Profile + */ + void setParameters(const ProfileLoader::Profile& profile); + +public: + size_t _width; + size_t _height; + AVPixelFormat _pixelFormat; + double _fps; +}; + +/** + * @brief This class describes decoded video data. + */ +class AvExport VideoFrame : public Frame +{ +public: + VideoFrame(const VideoFrameDesc& ref); + VideoFrame(const Frame& otherFrame); + + size_t getWidth() const { return _frame->width; } + size_t getHeight() const { return _frame->height; } + AVPixelFormat getPixelFormat() const { return static_cast(_frame->format); } + VideoFrameDesc desc() const { return VideoFrameDesc(getWidth(), getHeight(), getPixelFormat()); } + + size_t getSize() const; ///< in bytes/** + + /** + * @brief Assign the given value to all the data of the picture. + */ + void assign(const unsigned char value); + + /** + * @brief Assign the given ptr of data to the data of the picture. + * @warning the given ptr should have the size of the picture. + * @see getSize + */ + void assign(const unsigned char* ptrValue); + +private: + void allocateAVPicture(const VideoFrameDesc& desc); +}; +} + +#endif diff --git a/src/AvTranscoder/decoder/AudioDecoder.cpp b/src/AvTranscoder/decoder/AudioDecoder.cpp index 776e50ce..fa7da88a 100644 --- a/src/AvTranscoder/decoder/AudioDecoder.cpp +++ b/src/AvTranscoder/decoder/AudioDecoder.cpp @@ -2,7 +2,7 @@ #include #include -#include +#include extern "C" { #include @@ -13,41 +13,19 @@ extern "C" { } #include +#include namespace avtranscoder { AudioDecoder::AudioDecoder(InputStream& inputStream) : _inputStream(&inputStream) - , _frame(NULL) , _isSetup(false) { -#if LIBAVCODEC_VERSION_MAJOR > 54 - _frame = av_frame_alloc(); -#else - _frame = avcodec_alloc_frame(); -#endif - if(_frame == NULL) - { - throw std::runtime_error("unable to setup frame buffer"); - } } AudioDecoder::~AudioDecoder() { - if(_frame != NULL) - { -#if LIBAVCODEC_VERSION_MAJOR > 54 - av_frame_free(&_frame); -#else -#if LIBAVCODEC_VERSION_MAJOR > 53 - avcodec_free_frame(&_frame); -#else - av_free(_frame); -#endif -#endif - _frame = NULL; - } } void AudioDecoder::setupDecoder(const ProfileLoader::Profile& profile) @@ -99,100 +77,97 @@ void AudioDecoder::setupDecoder(const ProfileLoader::Profile& profile) bool AudioDecoder::decodeNextFrame(Frame& frameBuffer) { - if(!decodeNextFrame()) - return false; - - AVCodecContext& avCodecContext = _inputStream->getAudioCodec().getAVCodecContext(); + bool decodeNextFrame = false; - size_t decodedSize = - av_samples_get_buffer_size(NULL, avCodecContext.channels, _frame->nb_samples, avCodecContext.sample_fmt, 1); - if(decodedSize == 0) - return false; + if(!_isSetup) + setupDecoder(); - AudioFrame& audioBuffer = static_cast(frameBuffer); - audioBuffer.setNbSamples(_frame->nb_samples); - audioBuffer.resize(decodedSize); + int got_frame = 0; + while(!got_frame) + { + CodedData data; - // @todo manage cases with data of frame not only on data[0] (use _frame.linesize) - unsigned char* const src = _frame->data[0]; - unsigned char* dst = audioBuffer.getData(); + const bool nextPacketRead = _inputStream->readNextPacket(data); + // if error or end of file + if(!nextPacketRead) + { + data.clear(); + return false; + } - av_samples_copy(&dst, &src, 0, 0, _frame->nb_samples, avCodecContext.channels, avCodecContext.sample_fmt); + // decoding + int ret = avcodec_decode_audio4(&_inputStream->getAudioCodec().getAVCodecContext(), &frameBuffer.getAVFrame(), + &got_frame, &data.getAVPacket()); + if(ret < 0) + { + throw std::runtime_error("an error occured during audio decoding" + getDescriptionFromErrorCode(ret)); + } - return true; + // if no frame could be decompressed + if(!nextPacketRead && ret == 0 && got_frame == 0) + decodeNextFrame = false; + else + decodeNextFrame = true; + } + return decodeNextFrame; } -bool AudioDecoder::decodeNextFrame(Frame& frameBuffer, const size_t subStreamIndex) +bool AudioDecoder::decodeNextFrame(Frame& frameBuffer, const size_t channelIndex) { - if(!decodeNextFrame()) + AudioFrame& audioBuffer = static_cast(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); - const int output_nbChannels = 1; - const int output_align = 1; - size_t decodedSize = - av_samples_get_buffer_size(NULL, output_nbChannels, _frame->nb_samples, avCodecContext.sample_fmt, output_align); - - size_t nbSubStreams = avCodecContext.channels; - size_t bytePerSample = av_get_bytes_per_sample((AVSampleFormat)_frame->format); + 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; - if(subStreamIndex > nbSubStreams - 1) + // check if the expected channel exists + if(channelIndex > srcNbChannels - 1) { - throw std::runtime_error("The subStream doesn't exist"); + 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(decodedSize == 0) - return false; - - AudioFrame& audioBuffer = static_cast(frameBuffer); - audioBuffer.setNbSamples(_frame->nb_samples); - audioBuffer.resize(decodedSize); + // copy frame properties of decoded frame + audioBuffer.copyProperties(allDataOfNextFrame); + av_frame_set_channels(&audioBuffer.getAVFrame(), 1); + av_frame_set_channel_layout(&audioBuffer.getAVFrame(), AV_CH_LAYOUT_MONO); + audioBuffer.setNbSamplesPerChannel(allDataOfNextFrame.getNbSamplesPerChannel()); // @todo manage cases with data of frame not only on data[0] (use _frame.linesize) - unsigned char* src = _frame->data[0]; - unsigned char* dst = audioBuffer.getData(); + unsigned char* src = allDataOfNextFrame.getData()[0]; + unsigned char* dst = audioBuffer.getData()[0]; // offset - src += subStreamIndex * bytePerSample; + src += channelIndex * bytePerSample; - for(int sample = 0; sample < _frame->nb_samples; ++sample) + // extract one channel + for(int sample = 0; sample < allDataOfNextFrame.getAVFrame().nb_samples; ++sample) { memcpy(dst, src, bytePerSample); dst += bytePerSample; - src += bytePerSample * nbSubStreams; + src += bytePerSample * srcNbChannels; } return true; } -bool AudioDecoder::decodeNextFrame() -{ - if(!_isSetup) - setupDecoder(); - - int got_frame = 0; - while(!got_frame) - { - CodedData data; - - bool nextPacketRead = _inputStream->readNextPacket(data); - if(!nextPacketRead) // error or end of file - data.clear(); - - int ret = avcodec_decode_audio4(&_inputStream->getAudioCodec().getAVCodecContext(), _frame, &got_frame, - &data.getAVPacket()); - if(!nextPacketRead && ret == 0 && got_frame == 0) // no frame could be decompressed - return false; - - if(ret < 0) - { - throw std::runtime_error("an error occured during audio decoding" + getDescriptionFromErrorCode(ret)); - } - } - return true; -} - void AudioDecoder::flushDecoder() { avcodec_flush_buffers(&_inputStream->getAudioCodec().getAVCodecContext()); diff --git a/src/AvTranscoder/decoder/AudioDecoder.hpp b/src/AvTranscoder/decoder/AudioDecoder.hpp index d04adac0..a2a6f2b8 100644 --- a/src/AvTranscoder/decoder/AudioDecoder.hpp +++ b/src/AvTranscoder/decoder/AudioDecoder.hpp @@ -3,8 +3,6 @@ #include "IDecoder.hpp" -struct AVFrame; - namespace avtranscoder { @@ -19,16 +17,12 @@ class AvExport AudioDecoder : 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 size_t channelIndex); void flushDecoder(); -private: - bool decodeNextFrame(); - private: InputStream* _inputStream; ///< Stream from which we read next frames (no ownership, has link) - AVFrame* _frame; ///< Libav object to store decoded data (has ownership) bool _isSetup; }; diff --git a/src/AvTranscoder/decoder/AudioGenerator.cpp b/src/AvTranscoder/decoder/AudioGenerator.cpp index 588064aa..33309f10 100644 --- a/src/AvTranscoder/decoder/AudioGenerator.cpp +++ b/src/AvTranscoder/decoder/AudioGenerator.cpp @@ -6,14 +6,12 @@ namespace avtranscoder AudioGenerator::AudioGenerator() : _inputFrame(NULL) , _silent(NULL) - , _frameDesc() { } AudioGenerator::AudioGenerator(const AudioGenerator& audioGenerator) : _inputFrame(NULL) , _silent(NULL) - , _frameDesc(audioGenerator.getAudioFrameDesc()) { } @@ -21,7 +19,6 @@ AudioGenerator& AudioGenerator::operator=(const AudioGenerator& audioGenerator) { _inputFrame = NULL; _silent = NULL; - _frameDesc = audioGenerator.getAudioFrameDesc(); return *this; } @@ -30,13 +27,7 @@ AudioGenerator::~AudioGenerator() delete _silent; } -void AudioGenerator::setAudioFrameDesc(const AudioFrameDesc& frameDesc) -{ - _frameDesc = frameDesc; - _frameDesc.setFps(25.); -} - -void AudioGenerator::setFrame(Frame& inputFrame) +void AudioGenerator::setNextFrame(Frame& inputFrame) { _inputFrame = &inputFrame; } @@ -46,24 +37,19 @@ bool AudioGenerator::decodeNextFrame(Frame& frameBuffer) // Generate silent if(!_inputFrame) { - AudioFrame& audioBuffer = static_cast(frameBuffer); - audioBuffer.setNbSamples(_frameDesc.getSampleRate() / _frameDesc.getFps()); - // Generate the silent only once if(!_silent) { - int fillChar = 0; - + AudioFrame& audioBuffer = static_cast(frameBuffer); _silent = new AudioFrame(audioBuffer.desc()); - _silent->assign(_frameDesc.getDataSize(), fillChar); - _silent->setNbSamples(audioBuffer.getNbSamples()); } - frameBuffer.refData(*_silent); + frameBuffer.getAVFrame().nb_samples = _silent->getAVFrame().nb_samples; + frameBuffer.copyData(*_silent); } // Take audio frame from _inputFrame else { - frameBuffer.refData(_inputFrame->getData(), _inputFrame->getSize()); + frameBuffer.copyData(*_inputFrame); } return true; } diff --git a/src/AvTranscoder/decoder/AudioGenerator.hpp b/src/AvTranscoder/decoder/AudioGenerator.hpp index 2ff09bb6..4f677043 100644 --- a/src/AvTranscoder/decoder/AudioGenerator.hpp +++ b/src/AvTranscoder/decoder/AudioGenerator.hpp @@ -19,14 +19,11 @@ class AvExport AudioGenerator : public IDecoder bool decodeNextFrame(Frame& frameBuffer); bool decodeNextFrame(Frame& frameBuffer, const size_t subStreamIndex); - const AudioFrameDesc& getAudioFrameDesc() const { return _frameDesc; } - void setAudioFrameDesc(const AudioFrameDesc& frameDesc); - void setFrame(Frame& inputFrame); + void setNextFrame(Frame& inputFrame); private: Frame* _inputFrame; ///< Has link (no ownership) AudioFrame* _silent; ///< The generated silent (has ownership) - AudioFrameDesc _frameDesc; ///< The description of the silent (sample rate...) }; } diff --git a/src/AvTranscoder/decoder/IDecoder.hpp b/src/AvTranscoder/decoder/IDecoder.hpp index 243befd1..c9af9148 100644 --- a/src/AvTranscoder/decoder/IDecoder.hpp +++ b/src/AvTranscoder/decoder/IDecoder.hpp @@ -2,7 +2,7 @@ #define _AV_TRANSCODER_ESSENCE_STREAM_IDECODER_HPP_ #include -#include +#include #include namespace avtranscoder @@ -30,10 +30,10 @@ class AvExport IDecoder /** * @brief Decode substream of next frame * @param frameBuffer: the frame decoded - * @param subStreamIndex: index of substream to extract + * @param channelIndex: index of channel to extract * @return status of decoding */ - virtual bool decodeNextFrame(Frame& frameBuffer, const size_t subStreamIndex) = 0; + virtual bool decodeNextFrame(Frame& frameBuffer, const size_t channelIndex) = 0; /** * @brief Set the next frame of the input stream (which bypass the work of decoding) diff --git a/src/AvTranscoder/decoder/VideoDecoder.cpp b/src/AvTranscoder/decoder/VideoDecoder.cpp index 79c18bd9..72e5759e 100644 --- a/src/AvTranscoder/decoder/VideoDecoder.cpp +++ b/src/AvTranscoder/decoder/VideoDecoder.cpp @@ -2,7 +2,7 @@ #include #include -#include +#include extern "C" { #include @@ -18,35 +18,12 @@ namespace avtranscoder VideoDecoder::VideoDecoder(InputStream& inputStream) : _inputStream(&inputStream) - , _frame(NULL) , _isSetup(false) { -#if LIBAVCODEC_VERSION_MAJOR > 54 - _frame = av_frame_alloc(); -#else - _frame = avcodec_alloc_frame(); -#endif - if(_frame == NULL) - { - throw std::runtime_error("unable to setup frame buffer"); - } } VideoDecoder::~VideoDecoder() { - if(_frame != NULL) - { -#if LIBAVCODEC_VERSION_MAJOR > 54 - av_frame_free(&_frame); -#else -#if LIBAVCODEC_VERSION_MAJOR > 53 - avcodec_free_frame(&_frame); -#else - av_free(_frame); -#endif -#endif - _frame = NULL; - } } void VideoDecoder::setupDecoder(const ProfileLoader::Profile& profile) @@ -98,30 +75,8 @@ void VideoDecoder::setupDecoder(const ProfileLoader::Profile& profile) bool VideoDecoder::decodeNextFrame(Frame& frameBuffer) { - if(!decodeNextFrame()) - return false; - - size_t decodedSize = avpicture_get_size((AVPixelFormat)_frame->format, _frame->width, _frame->height); - if(decodedSize == 0) - return false; - - VideoFrame& imageBuffer = static_cast(frameBuffer); - imageBuffer.resize(decodedSize); - - // Copy pixel data from an AVPicture into one contiguous buffer. - avpicture_layout((AVPicture*)_frame, (AVPixelFormat)_frame->format, _frame->width, _frame->height, imageBuffer.getData(), - frameBuffer.getSize()); - - return true; -} - -bool VideoDecoder::decodeNextFrame(Frame& frameBuffer, const size_t subStreamIndex) -{ - return false; -} + bool decodeNextFrame = false; -bool VideoDecoder::decodeNextFrame() -{ if(!_isSetup) setupDecoder(); @@ -130,21 +85,34 @@ bool VideoDecoder::decodeNextFrame() { CodedData data; - bool nextPacketRead = _inputStream->readNextPacket(data); - if(!nextPacketRead) // error or end of file + const bool nextPacketRead = _inputStream->readNextPacket(data); + // if error or end of file + if(!nextPacketRead) + { data.clear(); - - int ret = avcodec_decode_video2(&_inputStream->getVideoCodec().getAVCodecContext(), _frame, &got_frame, - &data.getAVPacket()); - if(!nextPacketRead && ret == 0 && got_frame == 0) // no frame could be decompressed return false; + } + // decoding + const int ret = avcodec_decode_video2(&_inputStream->getVideoCodec().getAVCodecContext(), &frameBuffer.getAVFrame(), + &got_frame, &data.getAVPacket()); if(ret < 0) { throw std::runtime_error("an error occured during video decoding - " + getDescriptionFromErrorCode(ret)); } + + // if no frame could be decompressed + if(!nextPacketRead && ret == 0 && got_frame == 0) + decodeNextFrame = false; + else + decodeNextFrame = true; } - return true; + return decodeNextFrame; +} + +bool VideoDecoder::decodeNextFrame(Frame& frameBuffer, const size_t subStreamIndex) +{ + return false; } void VideoDecoder::flushDecoder() diff --git a/src/AvTranscoder/decoder/VideoDecoder.hpp b/src/AvTranscoder/decoder/VideoDecoder.hpp index c1bdea82..69b8419d 100644 --- a/src/AvTranscoder/decoder/VideoDecoder.hpp +++ b/src/AvTranscoder/decoder/VideoDecoder.hpp @@ -3,8 +3,6 @@ #include "IDecoder.hpp" -struct AVFrame; - namespace avtranscoder { @@ -23,12 +21,8 @@ class AvExport VideoDecoder : public IDecoder void flushDecoder(); -private: - bool decodeNextFrame(); - private: InputStream* _inputStream; ///< Stream from which we read next frames (no ownership, has link) - AVFrame* _frame; ///< Libav object to store decoded data (has ownership) bool _isSetup; }; diff --git a/src/AvTranscoder/decoder/VideoGenerator.cpp b/src/AvTranscoder/decoder/VideoGenerator.cpp index caf677b1..74f16a6f 100644 --- a/src/AvTranscoder/decoder/VideoGenerator.cpp +++ b/src/AvTranscoder/decoder/VideoGenerator.cpp @@ -1,5 +1,6 @@ #include "VideoGenerator.hpp" +#include #include namespace avtranscoder @@ -50,35 +51,34 @@ bool VideoGenerator::decodeNextFrame(Frame& frameBuffer) // Generate the black image only once if(!_blackImage) { - // @todo support PAL (0 to 255) and NTFS (16 to 235) - char fillChar = 0; + VideoFrame& imageBuffer = static_cast(frameBuffer); - // input of convert + // Input of convert + // @todo support PAL (0 to 255) and NTFS (16 to 235) VideoFrameDesc desc(_frameDesc); - desc.setPixelFormat("rgb24"); - + desc._pixelFormat = getAVPixelFormat("rgb24"); VideoFrame intermediateBuffer(desc); - intermediateBuffer.assign(_frameDesc.getDataSize(), fillChar); + const unsigned char fillChar = 0; + intermediateBuffer.assign(fillChar); - // output of convert - VideoFrame& imageBuffer = static_cast(frameBuffer); + // Output of convert _blackImage = new VideoFrame(imageBuffer.desc()); - // convert and store the black image + // Convert and store the black image VideoTransform videoTransform; videoTransform.convert(intermediateBuffer, *_blackImage); } - frameBuffer.refData(*_blackImage); + frameBuffer.copyData(*_blackImage); } // Take image from _inputFrame else { - frameBuffer.refData(_inputFrame->getData(), _inputFrame->getSize()); + frameBuffer.copyData(*_inputFrame); } return true; } -bool VideoGenerator::decodeNextFrame(Frame& frameBuffer, const size_t subStreamIndex) +bool VideoGenerator::decodeNextFrame(Frame& frameBuffer, const size_t channelIndex) { return false; } diff --git a/src/AvTranscoder/decoder/VideoGenerator.hpp b/src/AvTranscoder/decoder/VideoGenerator.hpp index f58a00da..888c2c3c 100644 --- a/src/AvTranscoder/decoder/VideoGenerator.hpp +++ b/src/AvTranscoder/decoder/VideoGenerator.hpp @@ -17,10 +17,11 @@ class AvExport VideoGenerator : public IDecoder ~VideoGenerator(); bool decodeNextFrame(Frame& frameBuffer); - bool decodeNextFrame(Frame& frameBuffer, const size_t subStreamIndex); + bool decodeNextFrame(Frame& frameBuffer, const size_t channelIndex); const VideoFrameDesc& getVideoFrameDesc() const { return _frameDesc; } void setVideoFrameDesc(const VideoFrameDesc& frameDesc); + void setNextFrame(Frame& inputFrame); private: @@ -30,4 +31,4 @@ class AvExport VideoGenerator : public IDecoder }; } -#endif \ No newline at end of file +#endif diff --git a/src/AvTranscoder/encoder/AudioEncoder.cpp b/src/AvTranscoder/encoder/AudioEncoder.cpp index 69f21311..afede6e4 100644 --- a/src/AvTranscoder/encoder/AudioEncoder.cpp +++ b/src/AvTranscoder/encoder/AudioEncoder.cpp @@ -13,26 +13,11 @@ namespace avtranscoder AudioEncoder::AudioEncoder(const std::string& audioCodecName) : _codec(eCodecTypeEncoder, audioCodecName) - , _frame(NULL) { -#if LIBAVCODEC_VERSION_MAJOR > 54 - _frame = av_frame_alloc(); -#else - _frame = avcodec_alloc_frame(); -#endif } AudioEncoder::~AudioEncoder() { -#if LIBAVCODEC_VERSION_MAJOR > 54 - av_frame_free(&_frame); -#else -#if LIBAVCODEC_VERSION_MAJOR > 53 - avcodec_free_frame(&_frame); -#else - av_free(_frame); -#endif -#endif } void AudioEncoder::setupAudioEncoder(const AudioFrameDesc& frameDesc, const ProfileLoader::Profile& profile) @@ -106,39 +91,10 @@ void AudioEncoder::setupEncoder(const ProfileLoader::Profile& profile) } } -bool AudioEncoder::encodeFrame(const Frame& sourceFrame, Frame& codedFrame) +bool AudioEncoder::encodeFrame(const Frame& sourceFrame, CodedData& codedFrame) { AVCodecContext& avCodecContext = _codec.getAVCodecContext(); -// Set default frame parameters -#if LIBAVCODEC_VERSION_MAJOR > 54 - av_frame_unref(_frame); -#else - avcodec_get_frame_defaults(_frame); -#endif - - const AudioFrame& sourceAudioFrame = static_cast(sourceFrame); - - _frame->nb_samples = sourceAudioFrame.getNbSamples(); - _frame->format = avCodecContext.sample_fmt; - _frame->channel_layout = avCodecContext.channel_layout; - - // we calculate the size of the samples buffer in bytes - int bufferSize = - av_samples_get_buffer_size(NULL, avCodecContext.channels, _frame->nb_samples, avCodecContext.sample_fmt, 0); - if(bufferSize < 0) - { - throw std::runtime_error("Encode audio frame error: buffer size < 0 - " + getDescriptionFromErrorCode(bufferSize)); - } - - int retvalue = avcodec_fill_audio_frame(_frame, avCodecContext.channels, avCodecContext.sample_fmt, - sourceAudioFrame.getData(), bufferSize, 0); - if(retvalue < 0) - { - throw std::runtime_error("Encode audio frame error: avcodec fill audio frame - " + - getDescriptionFromErrorCode(retvalue)); - } - AVPacket& packet = codedFrame.getAVPacket(); packet.stream_index = 0; @@ -154,14 +110,14 @@ bool AudioEncoder::encodeFrame(const Frame& sourceFrame, Frame& codedFrame) #if LIBAVCODEC_VERSION_MAJOR > 53 int gotPacket = 0; - int ret = avcodec_encode_audio2(&avCodecContext, &packet, _frame, &gotPacket); + int ret = avcodec_encode_audio2(&avCodecContext, &packet, &sourceFrame.getAVFrame(), &gotPacket); if(ret != 0 && gotPacket == 0) { throw std::runtime_error("Encode audio frame error: avcodec encode audio frame - " + getDescriptionFromErrorCode(ret)); } #else - int ret = avcodec_encode_audio(&avCodecContext, packet.data, packet.size, _frame); + int ret = avcodec_encode_audio(&avCodecContext, packet.data, packet.size, &sourceFrame.getAVFrame()); if(ret < 0) { throw std::runtime_error("Encode audio frame error: avcodec encode audio frame - " + @@ -175,7 +131,7 @@ bool AudioEncoder::encodeFrame(const Frame& sourceFrame, Frame& codedFrame) return ret == 0; } -bool AudioEncoder::encodeFrame(Frame& codedFrame) +bool AudioEncoder::encodeFrame(CodedData& codedFrame) { AVCodecContext& avCodecContext = _codec.getAVCodecContext(); diff --git a/src/AvTranscoder/encoder/AudioEncoder.hpp b/src/AvTranscoder/encoder/AudioEncoder.hpp index c00089db..50f3246d 100644 --- a/src/AvTranscoder/encoder/AudioEncoder.hpp +++ b/src/AvTranscoder/encoder/AudioEncoder.hpp @@ -18,15 +18,14 @@ class AvExport AudioEncoder : public IEncoder const ProfileLoader::Profile& profile = ProfileLoader::Profile()); void setupEncoder(const ProfileLoader::Profile& profile = ProfileLoader::Profile()); - bool encodeFrame(const Frame& sourceFrame, Frame& codedFrame); - bool encodeFrame(Frame& codedFrame); + bool encodeFrame(const Frame& sourceFrame, CodedData& codedFrame); + bool encodeFrame(CodedData& codedFrame); ICodec& getCodec() { return _codec; } AudioCodec& getAudioCodec() { return _codec; } private: AudioCodec _codec; - AVFrame* _frame; ///< Contains the encoded data to pass to the Frame when encodeFrame }; } diff --git a/src/AvTranscoder/encoder/IEncoder.hpp b/src/AvTranscoder/encoder/IEncoder.hpp index 9598579b..55e8d3fa 100644 --- a/src/AvTranscoder/encoder/IEncoder.hpp +++ b/src/AvTranscoder/encoder/IEncoder.hpp @@ -1,7 +1,8 @@ #ifndef _AV_TRANSCODER_ESSENCE_STREAM_IENCODER_HPP_ #define _AV_TRANSCODER_ESSENCE_STREAM_IENCODER_HPP_ -#include +#include +#include #include #include @@ -26,14 +27,14 @@ class AvExport IEncoder * @param codedFrame data of the coded frame if present (first frames can be delayed) * @return status of encoding */ - virtual bool encodeFrame(const Frame& sourceFrame, Frame& codedFrame) = 0; + virtual bool encodeFrame(const Frame& sourceFrame, CodedData& codedFrame) = 0; /** * @brief Get delayed encoded frames * @param codedFrame data of the coded frame if present (first frames can be delayed) * @return status of encoding */ - virtual bool encodeFrame(Frame& codedFrame) = 0; + virtual bool encodeFrame(CodedData& codedFrame) = 0; /** * @brief Get codec used for encoding. diff --git a/src/AvTranscoder/encoder/VideoEncoder.cpp b/src/AvTranscoder/encoder/VideoEncoder.cpp index 0e6b0ae0..8c7f14e8 100644 --- a/src/AvTranscoder/encoder/VideoEncoder.cpp +++ b/src/AvTranscoder/encoder/VideoEncoder.cpp @@ -14,26 +14,11 @@ namespace avtranscoder VideoEncoder::VideoEncoder(const std::string& videoCodecName) : _codec(eCodecTypeEncoder, videoCodecName) - , _frame(NULL) { -#if LIBAVCODEC_VERSION_MAJOR > 54 - _frame = av_frame_alloc(); -#else - _frame = avcodec_alloc_frame(); -#endif } VideoEncoder::~VideoEncoder() { -#if LIBAVCODEC_VERSION_MAJOR > 54 - av_frame_free(&_frame); -#else -#if LIBAVCODEC_VERSION_MAJOR > 53 - avcodec_free_frame(&_frame); -#else - av_free(_frame); -#endif -#endif } void VideoEncoder::setupVideoEncoder(const VideoFrameDesc& frameDesc, const ProfileLoader::Profile& profile) @@ -119,30 +104,10 @@ void VideoEncoder::setupEncoder(const ProfileLoader::Profile& profile) } } -bool VideoEncoder::encodeFrame(const Frame& sourceFrame, Frame& codedFrame) +bool VideoEncoder::encodeFrame(const Frame& sourceFrame, CodedData& codedFrame) { AVCodecContext& avCodecContext = _codec.getAVCodecContext(); -// Set default frame parameters -#if LIBAVCODEC_VERSION_MAJOR > 54 - av_frame_unref(_frame); -#else - avcodec_get_frame_defaults(_frame); -#endif - - const VideoFrame& sourceImageFrame = static_cast(sourceFrame); - - _frame->width = avCodecContext.width; - _frame->height = avCodecContext.height; - _frame->format = avCodecContext.pix_fmt; - - int bufferSize = avpicture_fill((AVPicture*)_frame, const_cast(sourceImageFrame.getData()), - avCodecContext.pix_fmt, avCodecContext.width, avCodecContext.height); - if(bufferSize < 0) - { - throw std::runtime_error("Encode video frame error: buffer size < 0 - " + getDescriptionFromErrorCode(bufferSize)); - } - AVPacket& packet = codedFrame.getAVPacket(); packet.stream_index = 0; @@ -158,14 +123,14 @@ bool VideoEncoder::encodeFrame(const Frame& sourceFrame, Frame& codedFrame) #if LIBAVCODEC_VERSION_MAJOR > 53 int gotPacket = 0; - int ret = avcodec_encode_video2(&avCodecContext, &packet, _frame, &gotPacket); + int ret = avcodec_encode_video2(&avCodecContext, &packet, &sourceFrame.getAVFrame(), &gotPacket); if(ret != 0 && gotPacket == 0) { throw std::runtime_error("Encode video frame error: avcodec encode video frame - " + getDescriptionFromErrorCode(ret)); } #else - int ret = avcodec_encode_video(&avCodecContext, packet.data, packet.size, _frame); + int ret = avcodec_encode_video(&avCodecContext, packet.data, packet.size, &sourceFrame.getAVFrame()); if(ret < 0) { throw std::runtime_error("Encode video frame error: avcodec encode video frame - " + @@ -179,7 +144,7 @@ bool VideoEncoder::encodeFrame(const Frame& sourceFrame, Frame& codedFrame) return ret == 0; } -bool VideoEncoder::encodeFrame(Frame& codedFrame) +bool VideoEncoder::encodeFrame(CodedData& codedFrame) { AVCodecContext& avCodecContext = _codec.getAVCodecContext(); diff --git a/src/AvTranscoder/encoder/VideoEncoder.hpp b/src/AvTranscoder/encoder/VideoEncoder.hpp index 4a27e1da..6f8a910d 100644 --- a/src/AvTranscoder/encoder/VideoEncoder.hpp +++ b/src/AvTranscoder/encoder/VideoEncoder.hpp @@ -18,15 +18,14 @@ class AvExport VideoEncoder : public IEncoder const ProfileLoader::Profile& profile = ProfileLoader::Profile()); void setupEncoder(const ProfileLoader::Profile& profile = ProfileLoader::Profile()); - bool encodeFrame(const Frame& sourceFrame, Frame& codedFrame); - bool encodeFrame(Frame& codedFrame); + bool encodeFrame(const Frame& sourceFrame, CodedData& codedFrame); + bool encodeFrame(CodedData& codedFrame); ICodec& getCodec() { return _codec; } VideoCodec& getVideoCodec() { return _codec; } private: VideoCodec _codec; - AVFrame* _frame; ///< Contains the encoded data to pass to the Frame when encodeFrame }; } diff --git a/src/AvTranscoder/file/OutputFile.cpp b/src/AvTranscoder/file/OutputFile.cpp index 39424de4..8edecbc0 100644 --- a/src/AvTranscoder/file/OutputFile.cpp +++ b/src/AvTranscoder/file/OutputFile.cpp @@ -215,7 +215,9 @@ IOutputStream::EWrappingStatus OutputFile::wrap(const CodedData& data, const siz const double currentStreamDuration = _outputStreams.at(streamIndex)->getStreamDuration(); if(currentStreamDuration < _previousProcessedStreamDuration) { - // if the current stream is strictly shorter than the previous, wait for more data + LOG_DEBUG("The output stream " << streamIndex << " is strictly shorter than the previous duration saved (" + << currentStreamDuration << "s < " << _previousProcessedStreamDuration + << "s): wait for more data.") return IOutputStream::eWrappingWaitingForData; } diff --git a/src/AvTranscoder/frame/AudioFrame.cpp b/src/AvTranscoder/frame/AudioFrame.cpp deleted file mode 100644 index 6252a9a0..00000000 --- a/src/AvTranscoder/frame/AudioFrame.cpp +++ /dev/null @@ -1,63 +0,0 @@ -#include "AudioFrame.hpp" - -#include -#include - -namespace avtranscoder -{ - -AudioFrameDesc::AudioFrameDesc(const size_t sampleRate, const size_t channels, const AVSampleFormat sampleFormat) - : _sampleRate(sampleRate) - , _channels(channels) - , _sampleFormat(sampleFormat) - , _fps(1.) -{ -} - -AudioFrameDesc::AudioFrameDesc(const size_t sampleRate, const size_t channels, const std::string& sampleFormat) - : _sampleRate(sampleRate) - , _channels(channels) - , _sampleFormat(av_get_sample_fmt(sampleFormat.c_str())) - , _fps(1.) -{ -} - -std::string AudioFrameDesc::getSampleFormatName() const -{ - const char* formatName = av_get_sample_fmt_name(_sampleFormat); - return formatName ? std::string(formatName) : "unknown sample format"; -} - -size_t AudioFrameDesc::getDataSize() const -{ - if(_sampleFormat == AV_SAMPLE_FMT_NONE) - throw std::runtime_error("incorrect sample format"); - - size_t size = (_sampleRate / _fps) * _channels * av_get_bytes_per_sample(_sampleFormat); - if(size == 0) - throw std::runtime_error("unable to determine audio buffer size"); - - return size; -} - -void AudioFrameDesc::setSampleFormat(const std::string& sampleFormatName) -{ - _sampleFormat = av_get_sample_fmt(sampleFormatName.c_str()); -} - -void AudioFrameDesc::setParameters(const ProfileLoader::Profile& profile) -{ - // sample rate - if(profile.count(constants::avProfileSampleRate)) - setSampleRate(atoi(profile.find(constants::avProfileSampleRate)->second.c_str())); - // channel - if(profile.count(constants::avProfileChannel)) - setChannels(atoi(profile.find(constants::avProfileChannel)->second.c_str())); - // sample format - if(profile.count(constants::avProfileSampleFormat)) - setSampleFormat(profile.find(constants::avProfileSampleFormat)->second); - // fps - if(profile.count(constants::avProfileFrameRate)) - setFps(atof(profile.find(constants::avProfileFrameRate)->second.c_str())); -} -} diff --git a/src/AvTranscoder/frame/AudioFrame.hpp b/src/AvTranscoder/frame/AudioFrame.hpp deleted file mode 100644 index e39b02ed..00000000 --- a/src/AvTranscoder/frame/AudioFrame.hpp +++ /dev/null @@ -1,67 +0,0 @@ -#ifndef _AV_TRANSCODER_FRAME_AUDIO_FRAME_HPP_ -#define _AV_TRANSCODER_FRAME_AUDIO_FRAME_HPP_ - -#include "Frame.hpp" -#include - -extern "C" { -#include -} - -namespace avtranscoder -{ - -/// @brief Description of a number of samples, which corresponds to one video frame -class AvExport AudioFrameDesc -{ -public: - AudioFrameDesc(const size_t sampleRate = 0, const size_t channels = 0, - const AVSampleFormat sampleFormat = AV_SAMPLE_FMT_NONE); - - AudioFrameDesc(const size_t sampleRate, const size_t channels, const std::string& sampleFormat); - - size_t getSampleRate() const { return _sampleRate; } - size_t getChannels() const { return _channels; } - AVSampleFormat getSampleFormat() const { return _sampleFormat; } - std::string getSampleFormatName() const; - double getFps() const { return _fps; } - - size_t getDataSize() const; - - void setSampleRate(const size_t sampleRate) { _sampleRate = sampleRate; } - void setChannels(const size_t channels) { _channels = channels; } - void setSampleFormat(const std::string& sampleFormatName); - void setSampleFormat(const AVSampleFormat sampleFormat) { _sampleFormat = sampleFormat; } - void setFps(const double fps) { _fps = fps; } - - void setParameters(const ProfileLoader::Profile& profile); - -private: - size_t _sampleRate; - size_t _channels; - AVSampleFormat _sampleFormat; - double _fps; -}; - -class AvExport AudioFrame : public Frame -{ -public: - AudioFrame(const AudioFrameDesc& ref) - : Frame(ref.getDataSize()) - , _audioFrameDesc(ref) - , _nbSamples(0) - { - } - - const AudioFrameDesc& desc() const { return _audioFrameDesc; } - - size_t getNbSamples() const { return _nbSamples; } - void setNbSamples(size_t nbSamples) { _nbSamples = nbSamples; } - -private: - const AudioFrameDesc _audioFrameDesc; - size_t _nbSamples; -}; -} - -#endif diff --git a/src/AvTranscoder/frame/VideoFrame.cpp b/src/AvTranscoder/frame/VideoFrame.cpp deleted file mode 100644 index 32ed2660..00000000 --- a/src/AvTranscoder/frame/VideoFrame.cpp +++ /dev/null @@ -1,67 +0,0 @@ -#include "VideoFrame.hpp" - -extern "C" { -#include -#include -} - -#include -#include - -namespace avtranscoder -{ - -VideoFrameDesc::VideoFrameDesc(const size_t width, const size_t height, const AVPixelFormat pixelFormat) - : _width(width) - , _height(height) - , _pixelFormat(pixelFormat) - , _fps(1.0) -{ -} -VideoFrameDesc::VideoFrameDesc(const size_t width, const size_t height, const std::string& pixelFormat) - : _width(width) - , _height(height) - , _pixelFormat(av_get_pix_fmt(pixelFormat.c_str())) - , _fps(1.0) -{ -} - -std::string VideoFrameDesc::getPixelFormatName() const -{ - const char* formatName = av_get_pix_fmt_name(_pixelFormat); - return formatName ? std::string(formatName) : "unknown pixel format"; -} - -size_t VideoFrameDesc::getDataSize() const -{ - if(_pixelFormat == AV_PIX_FMT_NONE) - throw std::runtime_error("incorrect pixel format"); - - size_t size = avpicture_get_size(_pixelFormat, _width, _height); - if(size == 0) - throw std::runtime_error("unable to determine image buffer size"); - - return size; -} - -void VideoFrameDesc::setPixelFormat(const std::string& pixelFormat) -{ - _pixelFormat = av_get_pix_fmt(pixelFormat.c_str()); -} - -void VideoFrameDesc::setParameters(const ProfileLoader::Profile& profile) -{ - // width - if(profile.count(constants::avProfileWidth)) - setWidth(atoi(profile.find(constants::avProfileWidth)->second.c_str())); - // height - if(profile.count(constants::avProfileHeight)) - setHeight(atoi(profile.find(constants::avProfileHeight)->second.c_str())); - // pixel format - if(profile.count(constants::avProfilePixelFormat)) - setPixelFormat(profile.find(constants::avProfilePixelFormat)->second); - // fps - if(profile.count(constants::avProfileFrameRate)) - setFps(atof(profile.find(constants::avProfileFrameRate)->second.c_str())); -} -} diff --git a/src/AvTranscoder/frame/VideoFrame.hpp b/src/AvTranscoder/frame/VideoFrame.hpp deleted file mode 100644 index 131b183d..00000000 --- a/src/AvTranscoder/frame/VideoFrame.hpp +++ /dev/null @@ -1,63 +0,0 @@ -#ifndef _AV_TRANSCODER_FRAME_VIDEO_FRAME_HPP_ -#define _AV_TRANSCODER_FRAME_VIDEO_FRAME_HPP_ - -#include "Frame.hpp" -#include - -extern "C" { -#include -} - -namespace avtranscoder -{ - -class AvExport VideoFrameDesc -{ -public: - VideoFrameDesc(const size_t width = 0, const size_t height = 0, const AVPixelFormat pixelFormat = AV_PIX_FMT_NONE); - VideoFrameDesc(const size_t width, const size_t height, const std::string& pixelFormat); - - size_t getWidth() const { return _width; } - size_t getHeight() const { return _height; } - AVPixelFormat getPixelFormat() const { return _pixelFormat; } - std::string getPixelFormatName() const; - double getFps() const { return _fps; } - - size_t getDataSize() const; - - void setWidth(const size_t width) { _width = width; } - void setHeight(const size_t height) { _height = height; } - void setPixelFormat(const std::string& pixelFormat); - void setPixelFormat(const AVPixelFormat pixelFormat) { _pixelFormat = pixelFormat; } - void setFps(const double fps) { _fps = fps; } - - void setParameters(const ProfileLoader::Profile& profile); - -private: - size_t _width; - size_t _height; - AVPixelFormat _pixelFormat; - double _fps; -}; - -// template< template Alloc > -// class AvExport ImageBase -class AvExport VideoFrame : public Frame -{ -public: - VideoFrame(const VideoFrameDesc& ref) - : Frame(ref.getDataSize()) - , _videoFrameDesc(ref) - { - } - - const VideoFrameDesc& desc() const { return _videoFrameDesc; } - -private: - const VideoFrameDesc _videoFrameDesc; -}; - -// typedef ImageBase VideoFrame; -} - -#endif diff --git a/src/AvTranscoder/frame/frame.i b/src/AvTranscoder/frame/frame.i deleted file mode 100644 index d3959127..00000000 --- a/src/AvTranscoder/frame/frame.i +++ /dev/null @@ -1,11 +0,0 @@ -%apply char * { unsigned char * }; - -%{ -#include -#include -#include -%} - -%include -%include -%include diff --git a/src/AvTranscoder/properties/AudioProperties.cpp b/src/AvTranscoder/properties/AudioProperties.cpp index 93db9bd3..7ccd34e7 100644 --- a/src/AvTranscoder/properties/AudioProperties.cpp +++ b/src/AvTranscoder/properties/AudioProperties.cpp @@ -109,7 +109,7 @@ size_t AudioProperties::getSampleRate() const return _codecContext->sample_rate; } -size_t AudioProperties::getChannels() const +size_t AudioProperties::getNbChannels() const { if(!_codecContext) throw std::runtime_error("unknown codec context"); @@ -136,7 +136,7 @@ size_t AudioProperties::getNbSamples() const throw std::runtime_error("unknown format context"); size_t nbSamples = _formatContext->streams[_streamIndex]->nb_frames; if(nbSamples == 0) - nbSamples = getSampleRate() * getChannels() * getDuration(); + nbSamples = getSampleRate() * getNbChannels() * getDuration(); return nbSamples; } @@ -160,7 +160,7 @@ PropertyVector AudioProperties::asVector() const addProperty(data, "sampleRate", &AudioProperties::getSampleRate); addProperty(data, "bitRate", &AudioProperties::getBitRate); addProperty(data, "nbSamples", &AudioProperties::getNbSamples); - addProperty(data, "channels", &AudioProperties::getChannels); + addProperty(data, "nbChannels", &AudioProperties::getNbChannels); addProperty(data, "channelLayout", &AudioProperties::getChannelLayout); addProperty(data, "channelName", &AudioProperties::getChannelName); addProperty(data, "channelDescription", &AudioProperties::getChannelDescription); diff --git a/src/AvTranscoder/properties/AudioProperties.hpp b/src/AvTranscoder/properties/AudioProperties.hpp index e8ce9945..0acd0d5f 100644 --- a/src/AvTranscoder/properties/AudioProperties.hpp +++ b/src/AvTranscoder/properties/AudioProperties.hpp @@ -20,7 +20,7 @@ class AvExport AudioProperties : public StreamProperties std::string getChannelDescription() const; size_t getSampleRate() const; - size_t getChannels() const; + size_t getNbChannels() const; size_t getBitRate() const; ///< 0 if unknown size_t getNbSamples() const; diff --git a/src/AvTranscoder/reader/AudioReader.cpp b/src/AvTranscoder/reader/AudioReader.cpp index c1962824..f47bf919 100644 --- a/src/AvTranscoder/reader/AudioReader.cpp +++ b/src/AvTranscoder/reader/AudioReader.cpp @@ -1,32 +1,30 @@ #include "AudioReader.hpp" +#include #include -#include +#include #include #include -#include namespace avtranscoder { -AudioReader::AudioReader(const std::string& filename, const size_t audioStreamIndex, const size_t sampleRate, - const size_t nbChannels, const std::string& sampleFormat) - : IReader(filename, audioStreamIndex) +AudioReader::AudioReader(const std::string& filename, const size_t streamIndex, const int channelIndex) + : IReader(filename, streamIndex, channelIndex) , _audioStreamProperties(NULL) - , _sampleRate(sampleRate) - , _nbChannels(nbChannels) - , _sampleFormat(av_get_sample_fmt(sampleFormat.c_str())) + , _outputSampleRate(0) + , _outputNbChannels(0) + , _outputSampleFormat(AV_SAMPLE_FMT_S16) { init(); } -AudioReader::AudioReader(InputFile& inputFile, const size_t audioStreamIndex, const size_t sampleRate, - const size_t nbChannels, const std::string& sampleFormat) - : IReader(inputFile, audioStreamIndex) +AudioReader::AudioReader(InputFile& inputFile, const size_t streamIndex, const int channelIndex) + : IReader(inputFile, streamIndex, channelIndex) , _audioStreamProperties(NULL) - , _sampleRate(sampleRate) - , _nbChannels(nbChannels) - , _sampleFormat(av_get_sample_fmt(sampleFormat.c_str())) + , _outputSampleRate(0) + , _outputNbChannels(0) + , _outputSampleFormat(AV_SAMPLE_FMT_S16) { init(); } @@ -44,19 +42,16 @@ void AudioReader::init() _decoder = new AudioDecoder(_inputFile->getStream(_streamIndex)); _decoder->setupDecoder(); + // create transform + _transform = new AudioTransform(); + // create src frame _srcFrame = new AudioFrame(_inputFile->getStream(_streamIndex).getAudioCodec().getAudioFrameDesc()); AudioFrame* srcFrame = static_cast(_srcFrame); // create dst frame - if(_sampleRate == 0) - _sampleRate = srcFrame->desc().getSampleRate(); - if(_nbChannels == 0) - _nbChannels = srcFrame->desc().getChannels(); - AudioFrameDesc dstAudioFrame(_sampleRate, _nbChannels, _sampleFormat); - _dstFrame = new AudioFrame(dstAudioFrame); - - // create transform - _transform = new AudioTransform(); + _outputSampleRate = srcFrame->getSampleRate(); + _outputNbChannels = (_channelIndex == -1) ? srcFrame->getNbChannels() : 1; + _dstFrame = new AudioFrame(AudioFrameDesc(_outputSampleRate, _outputNbChannels, _outputSampleFormat)); } AudioReader::~AudioReader() @@ -67,23 +62,13 @@ AudioReader::~AudioReader() delete _transform; } -size_t AudioReader::getSampleRate() -{ - return _sampleRate; -} - -size_t AudioReader::getChannels() -{ - return _nbChannels; -} - -AVSampleFormat AudioReader::getSampleFormat() +void AudioReader::updateOutput(const size_t sampleRate, const size_t nbChannels, const std::string& sampleFormat) { - return _sampleFormat; -} - -void AudioReader::printInfo() -{ - std::cout << *_audioStreamProperties << std::endl; + _outputSampleRate = sampleRate; + _outputNbChannels = nbChannels; + _outputSampleFormat = getAVSampleFormat(sampleFormat); + // update dst frame + delete _dstFrame; + _dstFrame = new AudioFrame(AudioFrameDesc(_outputSampleRate, _outputNbChannels, _outputSampleFormat)); } } diff --git a/src/AvTranscoder/reader/AudioReader.hpp b/src/AvTranscoder/reader/AudioReader.hpp index 9bcfbe9e..edaff72e 100644 --- a/src/AvTranscoder/reader/AudioReader.hpp +++ b/src/AvTranscoder/reader/AudioReader.hpp @@ -13,28 +13,29 @@ class AvExport AudioReader : public IReader { public: //@{ - // @param sampleRate: if 0, get sample rate of source - // @param nbChannels: if 0, get number of channels of source - // @param sampleFormat: pcm_16le by default (to listen) - // - AudioReader(const std::string& filename, const size_t audioStreamIndex, const size_t sampleRate = 0, - const size_t nbChannels = 0, const std::string& sampleFormat = "s16"); - AudioReader(InputFile& inputFile, const size_t audioStreamIndex, const size_t sampleRate = 0, - const size_t nbChannels = 0, const std::string& sampleFormat = "s16"); + // @note Transform the input stream to s16 sample format (to listen). + // @see updateOutput + AudioReader(const std::string& filename, const size_t streamIndex = 0, const int channelIndex = -1); + AudioReader(InputFile& inputFile, const size_t streamIndex = 0, const int channelIndex = -1); + //@} ~AudioReader(); + /** + * @brief Update sample rate, number of channels and sample format of the output. + * @note Will transform the decoded data when read the stream. + */ + void updateOutput(const size_t sampleRate, const size_t nbChannels, const std::string& sampleFormat); + //@{ // @brief Output info - size_t getSampleRate(); - size_t getChannels(); - AVSampleFormat getSampleFormat(); + size_t getOutputSampleRate() const { return _outputSampleRate; } + size_t getOutputNbChannels() const { return _outputNbChannels; } + AVSampleFormat getOutputSampleFormat() const { return _outputSampleFormat; } //@} - // @brief Input info - const AudioProperties* getAudioProperties() const { return _audioStreamProperties; } - - void printInfo(); + // @brief Get source audio properties + const AudioProperties* getSourceAudioProperties() const { return _audioStreamProperties; } private: void init(); @@ -44,9 +45,9 @@ class AvExport AudioReader : public IReader //@{ // @brief Output info - size_t _sampleRate; - size_t _nbChannels; - AVSampleFormat _sampleFormat; + size_t _outputSampleRate; + size_t _outputNbChannels; + AVSampleFormat _outputSampleFormat; //@} }; } diff --git a/src/AvTranscoder/reader/IReader.cpp b/src/AvTranscoder/reader/IReader.cpp index 55bde9b3..8f7e7630 100644 --- a/src/AvTranscoder/reader/IReader.cpp +++ b/src/AvTranscoder/reader/IReader.cpp @@ -1,13 +1,11 @@ #include "IReader.hpp" -#include - #include namespace avtranscoder { -IReader::IReader(const std::string& filename, const size_t streamIndex) +IReader::IReader(const std::string& filename, const size_t streamIndex, const int channelIndex) : _inputFile(NULL) , _streamProperties(NULL) , _decoder(NULL) @@ -15,13 +13,14 @@ IReader::IReader(const std::string& filename, const size_t streamIndex) , _dstFrame(NULL) , _transform(NULL) , _streamIndex(streamIndex) + , _channelIndex(channelIndex) , _currentFrame(-1) , _inputFileAllocated(true) { _inputFile = new InputFile(filename); } -IReader::IReader(InputFile& inputFile, const size_t streamIndex) +IReader::IReader(InputFile& inputFile, const size_t streamIndex, const int channelIndex) : _inputFile(&inputFile) , _streamProperties(NULL) , _decoder(NULL) @@ -29,6 +28,7 @@ IReader::IReader(InputFile& inputFile, const size_t streamIndex) , _dstFrame(NULL) , _transform(NULL) , _streamIndex(streamIndex) + , _channelIndex(channelIndex) , _currentFrame(-1) , _inputFileAllocated(false) { @@ -57,23 +57,26 @@ Frame* IReader::readFrameAt(const size_t frame) assert(_srcFrame != NULL); assert(_dstFrame != NULL); + // seek if((int)frame != _currentFrame + 1) { - // seek _inputFile->seekAtFrame(frame); _decoder->flushDecoder(); } _currentFrame = frame; // decode - _decoder->decodeNextFrame(*_srcFrame); + bool decodingStatus = false; + if(_channelIndex != -1) + decodingStatus = _decoder->decodeNextFrame(*_srcFrame, _channelIndex); + else + decodingStatus = _decoder->decodeNextFrame(*_srcFrame); + if(!decodingStatus) + { + _dstFrame->clear(); + return _dstFrame; + } + // transform _transform->convert(*_srcFrame, *_dstFrame); - // return buffer return _dstFrame; } - -void IReader::printInfo() -{ - assert(_streamProperties != NULL); - std::cout << *_streamProperties << std::endl; -} } diff --git a/src/AvTranscoder/reader/IReader.hpp b/src/AvTranscoder/reader/IReader.hpp index ff33ab5d..51e41083 100644 --- a/src/AvTranscoder/reader/IReader.hpp +++ b/src/AvTranscoder/reader/IReader.hpp @@ -6,7 +6,7 @@ #include #include #include -#include +#include #include namespace avtranscoder @@ -20,14 +20,16 @@ class AvExport IReader public: /** * @brief Create a new InputFile and prepare to read the stream at the given index + * @param streamIndex by default read the first stream + * @param channelIndex by default -1 (all channels of the stream) */ - IReader(const std::string& filename, const size_t streamIndex); + IReader(const std::string& filename, const size_t streamIndex = 0, const int channelIndex = -1); /** * @brief Get the existing InputFile and prepare to read the stream at the given index * @note This constructor can improve performances when you create several readers from one InputFile. */ - IReader(InputFile& inputFile, const size_t streamIndex); + IReader(InputFile& inputFile, const size_t streamIndex = 0, const int channelIndex = -1); virtual ~IReader() = 0; @@ -47,9 +49,9 @@ class AvExport IReader Frame* readFrameAt(const size_t frame); /** - * @brief Print info of the source stream read. + * @brief Get the properties of the source stream read. */ - virtual void printInfo(); + const StreamProperties* getSourceProperties() const { return _streamProperties; } protected: InputFile* _inputFile; @@ -62,6 +64,7 @@ class AvExport IReader ITransform* _transform; size_t _streamIndex; + int _channelIndex; private: int _currentFrame; ///< The current decoded frame. diff --git a/src/AvTranscoder/reader/VideoReader.cpp b/src/AvTranscoder/reader/VideoReader.cpp index 46ea7e65..d5ddd5f7 100644 --- a/src/AvTranscoder/reader/VideoReader.cpp +++ b/src/AvTranscoder/reader/VideoReader.cpp @@ -1,32 +1,29 @@ #include "VideoReader.hpp" #include -#include +#include #include #include -#include namespace avtranscoder { -VideoReader::VideoReader(const std::string& filename, const size_t videoStreamIndex, const size_t width, const size_t height, - const std::string& pixelFormat) +VideoReader::VideoReader(const std::string& filename, const size_t videoStreamIndex) : IReader(filename, videoStreamIndex) , _videoStreamProperties(NULL) - , _width(width) - , _height(height) - , _pixelProperties(pixelFormat) + , _outputWidth(0) + , _outputHeight(0) + , _outputPixelProperties("rgb24") { init(); } -VideoReader::VideoReader(InputFile& inputFile, const size_t videoStreamIndex, const size_t width, const size_t height, - const std::string& pixelFormat) +VideoReader::VideoReader(InputFile& inputFile, const size_t videoStreamIndex) : IReader(inputFile, videoStreamIndex) , _videoStreamProperties(NULL) - , _width(width) - , _height(height) - , _pixelProperties(pixelFormat) + , _outputWidth(0) + , _outputHeight(0) + , _outputPixelProperties("rgb24") { init(); } @@ -44,19 +41,16 @@ void VideoReader::init() _decoder = new VideoDecoder(_inputFile->getStream(_streamIndex)); _decoder->setupDecoder(); + // create transform + _transform = new VideoTransform(); + // create src frame _srcFrame = new VideoFrame(_inputFile->getStream(_streamIndex).getVideoCodec().getVideoFrameDesc()); VideoFrame* srcFrame = static_cast(_srcFrame); // create dst frame - if(_width == 0) - _width = srcFrame->desc().getWidth(); - if(_height == 0) - _height = srcFrame->desc().getHeight(); - VideoFrameDesc videoFrameDescToDisplay(_width, _height, getPixelFormat()); - _dstFrame = new VideoFrame(videoFrameDescToDisplay); - - // create transform - _transform = new VideoTransform(); + _outputWidth = srcFrame->getWidth(); + _outputHeight = srcFrame->getHeight(); + _dstFrame = new VideoFrame(VideoFrameDesc(_outputWidth, _outputHeight, getOutputPixelFormat())); } VideoReader::~VideoReader() @@ -67,33 +61,13 @@ VideoReader::~VideoReader() delete _transform; } -size_t VideoReader::getWidth() -{ - return _width; -}; - -size_t VideoReader::getHeight() -{ - return _height; -} - -size_t VideoReader::getComponents() +void VideoReader::updateOutput(const size_t width, const size_t height, const std::string& pixelFormat) { - return _pixelProperties.getNbComponents(); -} - -size_t VideoReader::getBitDepth() -{ - return _pixelProperties.getBitsPerPixel(); -} - -AVPixelFormat VideoReader::getPixelFormat() -{ - return _pixelProperties.getAVPixelFormat(); -} - -void VideoReader::printInfo() -{ - std::cout << *_videoStreamProperties << std::endl; + _outputWidth = width; + _outputHeight = height; + _outputPixelProperties = PixelProperties(pixelFormat); + // update dst frame + delete _dstFrame; + _dstFrame = new VideoFrame(VideoFrameDesc(_outputWidth, _outputHeight, getOutputPixelFormat())); } } diff --git a/src/AvTranscoder/reader/VideoReader.hpp b/src/AvTranscoder/reader/VideoReader.hpp index 3c65288f..c507c91f 100644 --- a/src/AvTranscoder/reader/VideoReader.hpp +++ b/src/AvTranscoder/reader/VideoReader.hpp @@ -14,31 +14,31 @@ class AvExport VideoReader : public IReader { public: //@{ - // @param width: if 0, get width of source - // @param height: if 0, get height of source - // @param pixelFormat: rgb24 by default (to display) - // - VideoReader(const std::string& filename, const size_t videoStreamIndex, const size_t width = 0, const size_t height = 0, - const std::string& pixelFormat = "rgb24"); - VideoReader(InputFile& inputFile, const size_t videoStreamIndex, const size_t width = 0, const size_t height = 0, - const std::string& pixelFormat = "rgb24"); + // @note Transform the input stream to rgb24 pixel format (to display). + // @see updateOutput + VideoReader(const std::string& filename, const size_t videoStreamIndex = 0); + VideoReader(InputFile& inputFile, const size_t videoStreamIndex = 0); //@} ~VideoReader(); + /** + * @brief Update width, height and pixelFormat of the output. + * @note Will transform the decoded data when read the stream. + */ + void updateOutput(const size_t width, const size_t height, const std::string& pixelFormat); + //@{ // @brief Output info - size_t getWidth(); - size_t getHeight(); - size_t getComponents(); - size_t getBitDepth(); - AVPixelFormat getPixelFormat(); + size_t getOutputWidth() const { return _outputWidth; } + size_t getOutputHeight() const { return _outputHeight; } + size_t getOutputNbComponents() const { return _outputPixelProperties.getNbComponents(); } + size_t getOutputBitDepth() const { return _outputPixelProperties.getBitsPerPixel(); } + AVPixelFormat getOutputPixelFormat() const { return _outputPixelProperties.getAVPixelFormat(); } //@} - // @brief Input info - const VideoProperties* getVideoProperties() const { return _videoStreamProperties; } - - void printInfo(); + // @brief Get source video properties + const VideoProperties* getSourceVideoProperties() const { return _videoStreamProperties; } private: void init(); @@ -48,9 +48,9 @@ class AvExport VideoReader : public IReader //@{ // @brief Output info - size_t _width; - size_t _height; - PixelProperties _pixelProperties; + size_t _outputWidth; + size_t _outputHeight; + PixelProperties _outputPixelProperties; //@} }; } diff --git a/src/AvTranscoder/stream/IInputStream.hpp b/src/AvTranscoder/stream/IInputStream.hpp index 626dc4e6..f1752e2c 100644 --- a/src/AvTranscoder/stream/IInputStream.hpp +++ b/src/AvTranscoder/stream/IInputStream.hpp @@ -7,7 +7,7 @@ #include #include -#include +#include namespace avtranscoder { diff --git a/src/AvTranscoder/stream/IOutputStream.hpp b/src/AvTranscoder/stream/IOutputStream.hpp index 928057cc..3bf279ee 100644 --- a/src/AvTranscoder/stream/IOutputStream.hpp +++ b/src/AvTranscoder/stream/IOutputStream.hpp @@ -3,7 +3,7 @@ #include #include -#include +#include namespace avtranscoder { diff --git a/src/AvTranscoder/transcoder/StreamTranscoder.cpp b/src/AvTranscoder/transcoder/StreamTranscoder.cpp index 5133e9bd..0ae48dba 100644 --- a/src/AvTranscoder/transcoder/StreamTranscoder.cpp +++ b/src/AvTranscoder/transcoder/StreamTranscoder.cpp @@ -82,7 +82,6 @@ StreamTranscoder::StreamTranscoder(IInputStream& inputStream, IOutputFile& outpu // generator decoder AudioGenerator* generatorAudio = new AudioGenerator(); - generatorAudio->setAudioFrameDesc(inputFrameDesc); _generator = generatorAudio; // buffers to process @@ -186,7 +185,7 @@ StreamTranscoder::StreamTranscoder(IInputStream& inputStream, IOutputFile& outpu if(subStreamIndex > -1) { // @todo manage downmix ? - outputFrameDesc.setChannels(1); + outputFrameDesc._nbChannels = 1; } outputAudio->setupAudioEncoder(outputFrameDesc, profile); @@ -196,7 +195,7 @@ StreamTranscoder::StreamTranscoder(IInputStream& inputStream, IOutputFile& outpu // buffers to process AudioFrameDesc inputFrameDesc(_inputStream->getAudioCodec().getAudioFrameDesc()); if(subStreamIndex > -1) - inputFrameDesc.setChannels(1); + inputFrameDesc._nbChannels = 1; _sourceBuffer = new AudioFrame(inputFrameDesc); _frameBuffer = new AudioFrame(outputAudio->getAudioCodec().getAudioFrameDesc()); @@ -206,7 +205,6 @@ StreamTranscoder::StreamTranscoder(IInputStream& inputStream, IOutputFile& outpu // generator decoder AudioGenerator* generatorAudio = new AudioGenerator(); - generatorAudio->setAudioFrameDesc(outputAudio->getAudioCodec().getAudioFrameDesc()); _generator = generatorAudio; break; @@ -266,7 +264,6 @@ StreamTranscoder::StreamTranscoder(const ICodec& inputCodec, IOutputFile& output // generator decoder AudioGenerator* generatorAudio = new AudioGenerator(); const AudioCodec& inputAudioCodec = static_cast(inputCodec); - generatorAudio->setAudioFrameDesc(inputAudioCodec.getAudioFrameDesc()); _generator = generatorAudio; _currentDecoder = _generator; @@ -346,7 +343,7 @@ void StreamTranscoder::preProcessCodecLatency() bool StreamTranscoder::processFrame() { const EProcessCase processCase = getProcessCase(); - std::string msg = "Current process case of the stream is a "; + std::string msg = "Current process case of the stream is a "; switch(processCase) { case eProcessCaseTranscode: @@ -467,10 +464,10 @@ bool StreamTranscoder::processTranscode(const int subStreamIndex) CodedData data; if(decodingStatus) { - LOG_DEBUG("Convert (" << _sourceBuffer->getSize() << " bytes)") + LOG_DEBUG("Convert") _transform->convert(*_sourceBuffer, *_frameBuffer); - LOG_DEBUG("Encode (" << _frameBuffer->getSize() << " bytes)") + LOG_DEBUG("Encode") _outputEncoder->encodeFrame(*_frameBuffer, data); } else diff --git a/src/AvTranscoder/transcoder/Transcoder.cpp b/src/AvTranscoder/transcoder/Transcoder.cpp index 5046b990..47e5f2a7 100644 --- a/src/AvTranscoder/transcoder/Transcoder.cpp +++ b/src/AvTranscoder/transcoder/Transcoder.cpp @@ -491,7 +491,7 @@ ProfileLoader::Profile Transcoder::getProfileFromFile(InputFile& inputFile, cons ss << audioProperties->getSampleRate(); profile[constants::avProfileSampleRate] = ss.str(); ss.str(""); - ss << audioProperties->getChannels(); + ss << audioProperties->getNbChannels(); profile[constants::avProfileChannel] = ss.str(); } diff --git a/src/AvTranscoder/transform/AudioTransform.cpp b/src/AvTranscoder/transform/AudioTransform.cpp index d5e5eb96..593965ea 100644 --- a/src/AvTranscoder/transform/AudioTransform.cpp +++ b/src/AvTranscoder/transform/AudioTransform.cpp @@ -1,6 +1,7 @@ #include "AudioTransform.hpp" -#include +#include +#include extern "C" { #include @@ -26,13 +27,13 @@ extern "C" { } #include +#include namespace avtranscoder { AudioTransform::AudioTransform() : _audioConvertContext(NULL) - , _nbSamplesOfPreviousFrame(0) , _isInit(false) { } @@ -44,6 +45,7 @@ AudioTransform::~AudioTransform() bool AudioTransform::init(const Frame& srcFrame, const Frame& dstFrame) { + // Set convert context _audioConvertContext = AllocResampleContext(); if(!_audioConvertContext) { @@ -53,33 +55,37 @@ bool AudioTransform::init(const Frame& srcFrame, const Frame& dstFrame) const AudioFrame& src = static_cast(srcFrame); const AudioFrame& dst = static_cast(dstFrame); - av_opt_set_int(_audioConvertContext, "in_channel_layout", av_get_default_channel_layout(src.desc().getChannels()), 0); - av_opt_set_int(_audioConvertContext, "out_channel_layout", av_get_default_channel_layout(dst.desc().getChannels()), 0); - av_opt_set_int(_audioConvertContext, "in_sample_rate", src.desc().getSampleRate(), 0); - av_opt_set_int(_audioConvertContext, "out_sample_rate", dst.desc().getSampleRate(), 0); - SetSampleFormat(_audioConvertContext, "in_sample_fmt", src.desc().getSampleFormat(), 0); - SetSampleFormat(_audioConvertContext, "out_sample_fmt", dst.desc().getSampleFormat(), 0); + av_opt_set_int(_audioConvertContext, "in_channel_layout", av_get_default_channel_layout(src.getNbChannels()), 0); + av_opt_set_int(_audioConvertContext, "out_channel_layout", av_get_default_channel_layout(dst.getNbChannels()), 0); + av_opt_set_int(_audioConvertContext, "in_sample_rate", src.getSampleRate(), 0); + av_opt_set_int(_audioConvertContext, "out_sample_rate", dst.getSampleRate(), 0); + SetSampleFormat(_audioConvertContext, "in_sample_fmt", src.getSampleFormat(), 0); + SetSampleFormat(_audioConvertContext, "out_sample_fmt", dst.getSampleFormat(), 0); if(InitResampleContext(_audioConvertContext) < 0) { FreeResampleContext(&_audioConvertContext); - throw std::runtime_error("unable to open audio convert context"); + std::stringstream msg; + msg << "Unable to open audio convert context:" << std::endl; + msg << "in_channel_layout " << av_get_default_channel_layout(src.getNbChannels()) << std::endl; + msg << "out_channel_layout " << av_get_default_channel_layout(dst.getNbChannels()) << std::endl; + msg << "in_sample_rate " << src.getSampleRate() << std::endl; + msg << "out_sample_rate " << dst.getSampleRate() << std::endl; + msg << "in_sample_fmt " << src.getSampleFormat() << std::endl; + msg << "out_sample_fmt " << dst.getSampleFormat() << std::endl; + throw std::runtime_error(msg.str()); } - return true; -} - -void AudioTransform::updateOutputFrame(const size_t nbInputSamples, Frame& dstFrame) const -{ - AudioFrame& dst = static_cast(dstFrame); - - // resize buffer of output frame - const int dstSampleSize = av_get_bytes_per_sample(dst.desc().getSampleFormat()); - const size_t bufferSizeNeeded = nbInputSamples * dst.desc().getChannels() * dstSampleSize; - dstFrame.resize(bufferSizeNeeded); + std::stringstream msg; + msg << "Audio conversion from " << getSampleFormatName(src.getSampleFormat()) << " to " + << getSampleFormatName(dst.getSampleFormat()) << std::endl; + msg << "Source, number of channels = " << src.getNbChannels() << std::endl; + msg << "Source, sample rate = " << src.getSampleRate() << std::endl; + msg << "Destination, number of channels = " << dst.getNbChannels() << std::endl; + msg << "Destination, sample rate = " << dst.getSampleRate() << std::endl; + LOG_INFO(msg.str()) - // set nbSamples of output frame - dst.setNbSamples(nbInputSamples); + return true; } void AudioTransform::convert(const Frame& srcFrame, Frame& dstFrame) @@ -88,28 +94,27 @@ void AudioTransform::convert(const Frame& srcFrame, Frame& dstFrame) _isInit = init(srcFrame, dstFrame); // if number of samples change from previous frame - const size_t nbSamplesOfCurrentFrame = static_cast(srcFrame).getNbSamples(); - if(nbSamplesOfCurrentFrame != _nbSamplesOfPreviousFrame) - { - updateOutputFrame(nbSamplesOfCurrentFrame, dstFrame); - _nbSamplesOfPreviousFrame = nbSamplesOfCurrentFrame; - } + const size_t nbInputSamplesPerChannel = srcFrame.getAVFrame().nb_samples; - const unsigned char* srcData = srcFrame.getData(); - unsigned char* dstData = dstFrame.getData(); + const unsigned char** srcData = srcFrame.getData(); + unsigned char** dstData = dstFrame.getData(); int nbOutputSamplesPerChannel; #ifdef AVTRANSCODER_LIBAV_DEPENDENCY - nbOutputSamplesPerChannel = avresample_convert(_audioConvertContext, (uint8_t**)&dstData, 0, nbSamplesOfCurrentFrame, - (uint8_t**)&srcData, 0, nbSamplesOfCurrentFrame); + nbOutputSamplesPerChannel = + avresample_convert(_audioConvertContext, dstData, 0, nbInputSamplesPerChannel, srcData, 0, nbInputSamplesPerChannel); #else nbOutputSamplesPerChannel = - swr_convert(_audioConvertContext, &dstData, nbSamplesOfCurrentFrame, &srcData, nbSamplesOfCurrentFrame); + swr_convert(_audioConvertContext, dstData, nbInputSamplesPerChannel, srcData, nbInputSamplesPerChannel); #endif if(nbOutputSamplesPerChannel < 0) { throw std::runtime_error("unable to convert audio samples"); } + else + { + dstFrame.getAVFrame().nb_samples = nbOutputSamplesPerChannel; + } } } diff --git a/src/AvTranscoder/transform/AudioTransform.hpp b/src/AvTranscoder/transform/AudioTransform.hpp index 8bc5391f..e670605c 100644 --- a/src/AvTranscoder/transform/AudioTransform.hpp +++ b/src/AvTranscoder/transform/AudioTransform.hpp @@ -4,7 +4,7 @@ #include "ITransform.hpp" #include -#include +#include #ifdef AVTRANSCODER_LIBAV_DEPENDENCY #define ResampleContext AVAudioResampleContext @@ -32,14 +32,9 @@ class AvExport AudioTransform : public ITransform private: bool init(const Frame& srcFrame, const Frame& dstFrame); - /// Update output buffer if source has a different size from the last process - void updateOutputFrame(const size_t nbInputSamples, Frame& dstFrame) const; - private: ResampleContext* _audioConvertContext; - size_t _nbSamplesOfPreviousFrame; ///< To check if the number of samples change between frames - bool _isInit; }; } diff --git a/src/AvTranscoder/transform/ITransform.hpp b/src/AvTranscoder/transform/ITransform.hpp index 7af00fea..0dba77d4 100644 --- a/src/AvTranscoder/transform/ITransform.hpp +++ b/src/AvTranscoder/transform/ITransform.hpp @@ -2,7 +2,7 @@ #define _AV_TRANSCODER_ESSENCE_TRANSFORM_ESSENCE_TRANSFORM_HPP_ #include -#include +#include namespace avtranscoder { diff --git a/src/AvTranscoder/transform/VideoTransform.cpp b/src/AvTranscoder/transform/VideoTransform.cpp index 2ff27fe3..90131b1d 100644 --- a/src/AvTranscoder/transform/VideoTransform.cpp +++ b/src/AvTranscoder/transform/VideoTransform.cpp @@ -1,6 +1,6 @@ #include "VideoTransform.hpp" -#include +#include extern "C" { #include @@ -12,8 +12,6 @@ extern "C" { #endif } -#define MAX_SWS_PLANE 4 - #include #include #include @@ -24,10 +22,6 @@ namespace avtranscoder VideoTransform::VideoTransform() : _imageConvertContext(NULL) - , _srcData((uint8_t)MAX_SWS_PLANE, NULL) - , _dstData((uint8_t)MAX_SWS_PLANE, NULL) - , _srcLineSize(MAX_SWS_PLANE, 0) - , _dstLineSize(MAX_SWS_PLANE, 0) , _isInit(false) { } @@ -39,37 +33,35 @@ VideoTransform::~VideoTransform() bool VideoTransform::init(const Frame& srcFrame, const Frame& dstFrame) { + // Set convert context const VideoFrame& src = static_cast(srcFrame); const VideoFrame& dst = static_cast(dstFrame); - const AVPixelFormat srcPixelFormat = src.desc().getPixelFormat(); - const AVPixelFormat dstPixelFormat = dst.desc().getPixelFormat(); + const AVPixelFormat srcPixelFormat = src.getPixelFormat(); + const AVPixelFormat dstPixelFormat = dst.getPixelFormat(); _imageConvertContext = - sws_getCachedContext(_imageConvertContext, src.desc().getWidth(), src.desc().getHeight(), srcPixelFormat, - dst.desc().getWidth(), dst.desc().getHeight(), dstPixelFormat, SWS_POINT, NULL, NULL, NULL); + sws_getCachedContext(_imageConvertContext, src.getWidth(), src.getHeight(), srcPixelFormat, dst.getWidth(), + dst.getHeight(), dstPixelFormat, SWS_POINT, NULL, NULL, NULL); if(!_imageConvertContext) { throw std::runtime_error("unable to create color convert context"); } - av_image_fill_linesizes(&_srcLineSize[0], srcPixelFormat, src.desc().getWidth()); - av_image_fill_linesizes(&_dstLineSize[0], dstPixelFormat, dst.desc().getWidth()); - const char* srcPixFmt; srcPixFmt = av_get_pix_fmt_name(srcPixelFormat); const char* dstPixFmt; dstPixFmt = av_get_pix_fmt_name(dstPixelFormat); - LOG_DEBUG("Video conversion from " << (srcPixFmt != NULL ? srcPixFmt : "None") << " to " - << (dstPixFmt != NULL ? dstPixFmt : "None")) - - LOG_DEBUG("Source, width = " << src.desc().getWidth()) - LOG_DEBUG("Source, height = " << src.desc().getHeight()) - - LOG_DEBUG("Destination, width = " << dst.desc().getWidth()) - LOG_DEBUG("Destination, height = " << dst.desc().getHeight()) + std::stringstream msg; + msg << "Video conversion from " << (srcPixFmt != NULL ? srcPixFmt : "None") << " to " + << (dstPixFmt != NULL ? dstPixFmt : "None") << std::endl; + msg << "Source, width = " << src.getWidth() << std::endl; + msg << "Source, height = " << src.getHeight() << std::endl; + msg << "Destination, width = " << dst.getWidth() << std::endl; + msg << "Destination, height = " << dst.getHeight() << std::endl; + LOG_INFO(msg.str()) return true; } @@ -79,29 +71,23 @@ void VideoTransform::convert(const Frame& srcFrame, Frame& dstFrame) const VideoFrame& src = static_cast(srcFrame); VideoFrame& dst = static_cast(dstFrame); - assert(src.desc().getWidth() != 0); - assert(src.desc().getHeight() != 0); - assert(src.desc().getPixelFormat() != AV_PIX_FMT_NONE); + assert(src.getWidth() != 0); + assert(src.getHeight() != 0); + assert(src.getPixelFormat() != AV_PIX_FMT_NONE); if(!_isInit) _isInit = init(srcFrame, dstFrame); - const AVPixelFormat srcPixelFormat = src.desc().getPixelFormat(); - const AVPixelFormat dstPixelFormat = dst.desc().getPixelFormat(); - - // Fill plane data pointers - av_image_fill_pointers(&_srcData[0], srcPixelFormat, src.desc().getHeight(), (uint8_t*)src.getData(), &_srcLineSize[0]); - av_image_fill_pointers(&_dstData[0], dstPixelFormat, dst.desc().getHeight(), (uint8_t*)dst.getData(), &_dstLineSize[0]); - if(!_imageConvertContext) { throw std::runtime_error("unknown color convert context"); } - int ret = sws_scale(_imageConvertContext, &_srcData[0], &_srcLineSize[0], 0, src.desc().getHeight(), &_dstData[0], - &_dstLineSize[0]); + // Convert + const int ret = sws_scale(_imageConvertContext, src.getData(), src.getLineSize(), 0, src.getHeight(), dst.getData(), + dst.getLineSize()); - if(ret != (int)dst.desc().getHeight()) + if(ret != (int)dst.getHeight()) throw std::runtime_error("error in color converter"); } } diff --git a/src/AvTranscoder/transform/VideoTransform.hpp b/src/AvTranscoder/transform/VideoTransform.hpp index 0bdcec5c..7d17d0f7 100644 --- a/src/AvTranscoder/transform/VideoTransform.hpp +++ b/src/AvTranscoder/transform/VideoTransform.hpp @@ -5,7 +5,7 @@ #include "ITransform.hpp" -#include +#include class SwsContext; @@ -28,12 +28,6 @@ class AvExport VideoTransform : public ITransform bool init(const Frame& srcFrame, const Frame& dstFrame); SwsContext* _imageConvertContext; - - std::vector _srcData; - std::vector _dstData; - std::vector _srcLineSize; - std::vector _dstLineSize; - bool _isInit; }; } diff --git a/src/AvTranscoder/util.cpp b/src/AvTranscoder/util.cpp index 99380cb3..39166989 100644 --- a/src/AvTranscoder/util.cpp +++ b/src/AvTranscoder/util.cpp @@ -105,6 +105,18 @@ AVSampleFormat getAVSampleFormat(const std::string& sampleFormat) return av_get_sample_fmt(sampleFormat.c_str()); } +std::string getPixelFormatName(const AVPixelFormat pixelFormat) +{ + const char* formatName = av_get_pix_fmt_name(pixelFormat); + return formatName ? std::string(formatName) : ""; +} + +std::string getSampleFormatName(const AVSampleFormat sampleFormat) +{ + const char* formatName = av_get_sample_fmt_name(sampleFormat); + return formatName ? std::string(formatName) : ""; +} + NamesArray getFormatsNames() { NamesArray formatsNames; diff --git a/src/AvTranscoder/util.hpp b/src/AvTranscoder/util.hpp index 1d29f372..97ccd826 100644 --- a/src/AvTranscoder/util.hpp +++ b/src/AvTranscoder/util.hpp @@ -50,6 +50,18 @@ AVPixelFormat AvExport getAVPixelFormat(const std::string& pixelFormat); */ AVSampleFormat AvExport getAVSampleFormat(const std::string& sampleFormat); +/** + * @return The name of the given pixel format. + * @note Returns an empty string if the format is not found. + */ +std::string AvExport getPixelFormatName(const AVPixelFormat pixelFormat); + +/** + * @return The name of the given sample format. + * @note Returns an empty string if the format is not found. + */ +std::string AvExport getSampleFormatName(const AVSampleFormat sampleFormat); + #ifndef SWIG /** * @brief Get array of short/long names of all format supported by FFmpeg / libav. diff --git a/test/pyTest/testOffset.py b/test/pyTest/testOffset.py index d38ae677..8e9128bf 100644 --- a/test/pyTest/testOffset.py +++ b/test/pyTest/testOffset.py @@ -103,7 +103,7 @@ def testRewrapAudioPositiveOffset(): # check output duration assert_equals( src_audioStream.getDuration() + offset, dst_audioStream.getDuration() ) - assert_equals( src_audioStream.getNbSamples() + ( offset * dst_audioStream.getSampleRate() * dst_audioStream.getChannels() ), dst_audioStream.getNbSamples() ) + assert_equals( src_audioStream.getNbSamples() + ( offset * dst_audioStream.getSampleRate() * dst_audioStream.getNbChannels() ), dst_audioStream.getNbSamples() ) def testRewrapAudioNegativeOffset(): @@ -134,7 +134,7 @@ def testRewrapAudioNegativeOffset(): # check output duration assert_equals( src_audioStream.getDuration() + offset, dst_audioStream.getDuration() ) - assert_equals( src_audioStream.getNbSamples() + ( offset * dst_audioStream.getSampleRate() * dst_audioStream.getChannels() ), dst_audioStream.getNbSamples() ) + assert_equals( src_audioStream.getNbSamples() + ( offset * dst_audioStream.getSampleRate() * dst_audioStream.getNbChannels() ), dst_audioStream.getNbSamples() ) def testTranscodeVideoPositiveOffset(): @@ -295,7 +295,7 @@ def testMultipleOffsetFromSameInputFile(): assert_equals( src_videoStream.getDuration() + offset_1, dst_videoStream.getDuration() ) assert_equals( src_videoStream.getNbFrames() + ( offset_1 * dst_videoStream.getFps() ), dst_videoStream.getNbFrames() ) assert_equals( src_audioStream.getDuration() + offset_1, dst_audioStream.getDuration() ) - assert_equals( src_audioStream.getNbSamples() + ( offset_1 * dst_audioStream.getSampleRate() * dst_audioStream.getChannels() ), dst_audioStream.getNbSamples() ) + assert_equals( src_audioStream.getNbSamples() + ( offset_1 * dst_audioStream.getSampleRate() * dst_audioStream.getNbChannels() ), dst_audioStream.getNbSamples() ) def testMultipleOffsetFromSameStream(): diff --git a/test/pyTest/testProperties.py b/test/pyTest/testProperties.py index 5761ebee..358cf22f 100644 --- a/test/pyTest/testProperties.py +++ b/test/pyTest/testProperties.py @@ -124,7 +124,7 @@ def testCheckAudioProperties(): assert_equals( audioStream.getCodecName(), expectedCodecName ) assert_equals( audioStream.getNbSamples(), expectedSamples ) assert_equals( round(audioStream.getDuration(), 2), expectedDuration ) - assert_equals( audioStream.getChannels(), expectedChannels ) + assert_equals( audioStream.getNbChannels(), expectedChannels ) assert_equals( audioStream.getChannelLayout(), expectedChannelLayout ) assert_equals( audioStream.getSampleRate(), expectedSampleRate ) diff --git a/test/pyTest/testReader.py b/test/pyTest/testReader.py new file mode 100644 index 00000000..f78a64f8 --- /dev/null +++ b/test/pyTest/testReader.py @@ -0,0 +1,76 @@ +import os + +# 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_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_AUDIO_WAVE_FILE") + +from nose.tools import * + +from pyAvTranscoder import avtranscoder as av + + +def testVideoReaderCreateNewInputFile(): + """ + Read a video stream with the VideoReader. + The InputFile is created inside the reader. + """ + inputFileName = os.environ['AVTRANSCODER_TEST_VIDEO_AVI_FILE'] + reader = av.VideoReader(inputFileName) + + # read all frames and check their size + for i in xrange(0, reader.getSourceVideoProperties().getNbFrames()): + frame = av.VideoFrame(reader.readNextFrame()) + assert_equals( frame.getSize(), reader.getOutputWidth() * reader.getOutputHeight() * reader.getOutputNbComponents() ) + + # check if the next frame is empty + frame = av.VideoFrame(reader.readNextFrame()) + assert_equals( frame.getSize(), 0 ) + + +def testVideoReaderReferenceInputFile(): + """ + Read a video stream with the VideoReader. + The InputFile is a reference for the reader. + """ + inputFileName = os.environ['AVTRANSCODER_TEST_VIDEO_AVI_FILE'] + inputFile = av.InputFile(inputFileName) + reader = av.VideoReader(inputFile) + + # read all frames and check their size + for i in xrange(0, reader.getSourceVideoProperties().getNbFrames()): + frame = av.VideoFrame(reader.readNextFrame()) + assert_equals( frame.getSize(), reader.getOutputWidth() * reader.getOutputHeight() * reader.getOutputNbComponents() ) + + # check if the next frame is empty + frame = av.VideoFrame(reader.readNextFrame()) + assert_equals( frame.getSize(), 0 ) + + +def testAudioReaderChannelsExtraction(): + """ + Read the same audio stream with several AudioReaders. + Compare decoded frames from reader of all channels, and of one channel. + """ + inputFileName = os.environ['AVTRANSCODER_TEST_AUDIO_WAVE_FILE'] + inputFile = av.InputFile(inputFileName) + streamIndex = inputFile.getProperties().getAudioProperties()[0].getStreamIndex() + channelIndex = 0 + + # create reader to read all channels of the audio stream + readerOfAllChannels = av.AudioReader(inputFile, streamIndex) + nbChannels = readerOfAllChannels.getOutputNbChannels() + # read first frame + frame = av.AudioFrame(readerOfAllChannels.readNextFrame()) + sizeOfFrameWithAllChannels = frame.getSize() + + # create reader to read one channel of the audio stream + readerOfOneChannel = av.AudioReader(inputFile, streamIndex, channelIndex) + # read first frame + frame = av.AudioFrame(readerOfOneChannel.readNextFrame()) + sizeOfFrameWithOneChannels = frame.getSize() + + assert_equals( sizeOfFrameWithAllChannels / nbChannels, sizeOfFrameWithOneChannels ) diff --git a/test/pyTest/testSetFrame.py b/test/pyTest/testSetFrame.py index 5b8c01af..513167d5 100644 --- a/test/pyTest/testSetFrame.py +++ b/test/pyTest/testSetFrame.py @@ -27,15 +27,15 @@ def testSetVideoFrame(): transcoder.preProcessCodecLatency() p = av.ConsoleProgress() - # process 10 frames - nbFrames = 10 - for i in range(0, nbFrames): + # process 51 frames + nbFrames = 255 + for i in range(0, nbFrames, 5): transcoder.processFrame() p.progress( i, nbFrames ) # set video frame frame = av.VideoFrame( imageDesc ) - frame.assign(frame.getSize(), i) + frame.assign(i) videoDecoder.setNextFrame( frame ) # end process @@ -80,15 +80,15 @@ def testSetAudioFrame(): transcoder.preProcessCodecLatency() p = av.ConsoleProgress() - # process 10 frames - nbFrames = 10 + # process 51 frames + nbFrames = 255 for i in range(0, nbFrames): transcoder.processFrame() p.progress( i, nbFrames ) # set video frame frame = av.AudioFrame( audioDesc ) - frame.assign(frame.getSize(), i) + frame.assign(i) audioDecoder.setNextFrame( frame ) # end process @@ -106,5 +106,5 @@ def testSetAudioFrame(): assert_equals( "s32", dst_audioStream.getSampleFormatName() ) assert_equals( "signed 32 bits", dst_audioStream.getSampleFormatLongName() ) assert_equals( 48000, dst_audioStream.getSampleRate() ) - assert_equals( 1, dst_audioStream.getChannels() ) + assert_equals( 1, dst_audioStream.getNbChannels() ) diff --git a/test/pyTest/testTranscoderTranscodeAudioWave.py b/test/pyTest/testTranscoderTranscodeAudioWave.py index 2a189af4..46b80499 100644 --- a/test/pyTest/testTranscoderTranscodeAudioWave.py +++ b/test/pyTest/testTranscoderTranscodeAudioWave.py @@ -42,7 +42,7 @@ def testTranscodeWave24b48k5_1(): assert_equals( "s32", dst_audioStream.getSampleFormatName() ) assert_equals( "signed 32 bits", dst_audioStream.getSampleFormatLongName() ) assert_equals( 48000, dst_audioStream.getSampleRate() ) - assert_equals( 6, dst_audioStream.getChannels() ) + assert_equals( 6, dst_audioStream.getNbChannels() ) def testTranscodeWave24b48kstereo(): """ @@ -76,7 +76,7 @@ def testTranscodeWave24b48kstereo(): assert_equals( "s32", dst_audioStream.getSampleFormatName() ) assert_equals( "signed 32 bits", dst_audioStream.getSampleFormatLongName() ) assert_equals( 48000, dst_audioStream.getSampleRate() ) - assert_equals( 2, dst_audioStream.getChannels() ) + assert_equals( 2, dst_audioStream.getNbChannels() ) def testTranscodeWave24b48kmono(): """ @@ -110,7 +110,7 @@ def testTranscodeWave24b48kmono(): assert_equals( "s32", dst_audioStream.getSampleFormatName() ) assert_equals( "signed 32 bits", dst_audioStream.getSampleFormatLongName() ) assert_equals( 48000, dst_audioStream.getSampleRate() ) - assert_equals( 1, dst_audioStream.getChannels() ) + assert_equals( 1, dst_audioStream.getNbChannels() ) def testTranscodeWave16b48kmono(): """ @@ -144,4 +144,4 @@ def testTranscodeWave16b48kmono(): assert_equals( "s16", dst_audioStream.getSampleFormatName() ) assert_equals( "signed 16 bits", dst_audioStream.getSampleFormatLongName() ) assert_equals( 48000, dst_audioStream.getSampleRate() ) - assert_equals( 1, dst_audioStream.getChannels() ) + assert_equals( 1, dst_audioStream.getNbChannels() )