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() ) pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy