diff --git a/include/ffcpp/Codec.h b/include/ffcpp/Codec.h index e5b1ef2..681ab7f 100644 --- a/include/ffcpp/Codec.h +++ b/include/ffcpp/Codec.h @@ -27,12 +27,14 @@ namespace ffcpp { public: Codec(); - Codec(AVCodecContext* ctx, CodecType type); + Codec(AVCodecID codecId, CodecType type, AVCodecParameters* params = nullptr); Codec(AVCodecContext* ctx, AVCodec* codec); ~Codec(); operator AVCodecContext*() const; + const AVCodec* nativeCodecPtr() const; + int width() const; int height() const; AVRational timeBase() const; @@ -47,6 +49,13 @@ namespace ffcpp { void setWidth(int width); void setHeight(int height); void setPixelFormat(AVPixelFormat pixelFormat); + void setTimeBase(AVRational timeBase); + void setSampleFormat(AVSampleFormat sampleFormat); + void setGlobalQuality(int quality); + void setChannelCount(int channels); + void setChannelLayout(uint64_t layout); + void setSampleRate(int sampleRate); + void setStdCompliance(int compliance); FramePtr decode(Packet& packet); Packet encode(FramePtr frame); diff --git a/include/ffcpp/Stream.h b/include/ffcpp/Stream.h index ca14e53..8c838e1 100644 --- a/include/ffcpp/Stream.h +++ b/include/ffcpp/Stream.h @@ -21,7 +21,7 @@ namespace ffcpp { public: Stream(); Stream(AVStream* stream); - Stream(AVStream* stream, AVCodec* encoder); + Stream(AVStream* stream, CodecPtr codec); operator AVStream*() const; CodecPtr codec(); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a04caa8..34c01af 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,8 +1,6 @@ project(ffcpp) find_package(FFMPEG REQUIRED) -# FIXME: FFMPEG_INCLUDE_DIR is incorrect and causes errors -# http://stackoverflow.com/questions/35982639/ctime-std-namespace-conflict include_directories(${FFMPEG_INCLUDE_DIR}) link_directories(${FFMPEG_LIBRARY_DIRS}) diff --git a/src/Codec.cpp b/src/Codec.cpp index 6b5d2f1..1f3d2a1 100644 --- a/src/Codec.cpp +++ b/src/Codec.cpp @@ -8,18 +8,26 @@ namespace ffcpp { } - Codec::Codec(AVCodecContext *ctx, CodecType type): _codecCtx(ctx) { - if(type == CodecType::Encoder) { - _codec = avcodec_find_encoder(ctx->codec_id); - } else { - _codec = avcodec_find_decoder(ctx->codec_id); - } + Codec::Codec(AVCodecID codecId, CodecType type, AVCodecParameters* params /* = nullptr */) { + if(type == CodecType::Encoder) { + _codec = avcodec_find_encoder(codecId); + } else { + _codec = avcodec_find_decoder(codecId); + } - if(!_codec) throw std::runtime_error("cannot find codec"); + if(!_codec) throw std::runtime_error("cannot find codec"); - int res = avcodec_open2(_codecCtx, _codec, nullptr); - throwIfError(res, "cannot open codec"); - } + _codecCtx = avcodec_alloc_context3(_codec); + if(!_codecCtx) throw std::runtime_error("cannot allocate codec context"); + + if(params) { + int res = avcodec_parameters_to_context(_codecCtx, params); + if(res < 0) throwIfError(res, "cannot copy codec papameters from stream"); + } + + int res = avcodec_open2(_codecCtx, _codec, nullptr); + throwIfError(res, "cannot open codec"); + } Codec::Codec(AVCodecContext *ctx, AVCodec *codec) { _codecCtx = ctx; @@ -32,8 +40,13 @@ namespace ffcpp { Codec::~Codec() { if(_codecCtx) { avcodec_close(_codecCtx); - } - } + } + } + + const AVCodec *Codec::nativeCodecPtr() const + { + return _codec; + } Codec::operator AVCodecContext*() const { return _codecCtx; @@ -88,9 +101,51 @@ namespace ffcpp { } void Codec::setPixelFormat(AVPixelFormat pixelFormat) { - _codecCtx->pix_fmt = pixelFormat; + if(pixelFormat == AV_PIX_FMT_NONE) { + _codecCtx->pix_fmt = _codec->pix_fmts[0]; + } else { + _codecCtx->pix_fmt = pixelFormat; + } } + void Codec::setTimeBase(AVRational timeBase) { + _codecCtx->time_base = timeBase; + } + + void Codec::setSampleFormat(AVSampleFormat sampleFormat) + { + if(sampleFormat == AV_SAMPLE_FMT_NONE) { + _codecCtx->sample_fmt = _codec->sample_fmts[0]; + } else { + _codecCtx->sample_fmt = sampleFormat; + } + } + + void Codec::setGlobalQuality(int quality) + { + _codecCtx->global_quality = quality; + } + + void Codec::setChannelCount(int channels) + { + _codecCtx->channels = channels; + } + + void Codec::setChannelLayout(uint64_t layout) + { + _codecCtx->channel_layout = layout; + } + + void Codec::setSampleRate(int sampleRate) + { + _codecCtx->sample_rate = sampleRate; + } + + void Codec::setStdCompliance(int compliance) + { + _codecCtx->strict_std_compliance = compliance; + } + Codec::Codec(Codec&& c) noexcept { *this = std::move(c); } @@ -105,13 +160,18 @@ namespace ffcpp { FramePtr Codec::decode(Packet &packet) { FramePtr frame = std::make_shared(); - int gotPicture = 0; - auto decFunc = (_codecCtx->codec_type == AVMEDIA_TYPE_VIDEO ? avcodec_decode_video2 : avcodec_decode_audio4); - while(!gotPicture) { - int res = decFunc(_codecCtx, frame->nativePtr(), &gotPicture, packet); - if(res < 0) throw std::runtime_error("cannot decode packet"); - } + int res = avcodec_send_packet(_codecCtx, packet); + if(res < 0) throw std::runtime_error("cannot decode packet"); + + while (res >= 0) { + res = avcodec_receive_frame(_codecCtx, frame->nativePtr()); + if(res == AVERROR(EAGAIN) || res == AVERROR_EOF) { + break; + } else if(res < 0) { + throw std::runtime_error("cannot decode packet"); + } + } if(_codecCtx->codec_type == AVMEDIA_TYPE_VIDEO) { frame->guessPts(); @@ -124,11 +184,18 @@ namespace ffcpp { Packet Codec::encode(FramePtr frame) { Packet packet; - int gotPacket = 0; - auto encFunc = (_codecCtx->codec_type == AVMEDIA_TYPE_VIDEO ? avcodec_encode_video2 : avcodec_encode_audio2); - int res = encFunc(_codecCtx, packet, frame ? frame->nativePtr() : nullptr, &gotPacket); - if(res < 0) throw std::runtime_error("cannot encode frame"); + int res = avcodec_send_frame(_codecCtx, frame->nativePtr()); + if(res < 0) throw std::runtime_error("cannot encode frame"); + + while (res >= 0) { + res = avcodec_receive_packet(_codecCtx, packet); + if(res == AVERROR(EAGAIN) || res == AVERROR_EOF) { + break; + } else if(res < 0) { + throw std::runtime_error("cannot encode frame"); + } + } return packet; } diff --git a/src/MediaFile.cpp b/src/MediaFile.cpp index 7fe9737..9f00d6d 100644 --- a/src/MediaFile.cpp +++ b/src/MediaFile.cpp @@ -16,7 +16,7 @@ namespace ffcpp { _streams.reserve(_formatCtx->nb_streams); for(size_t i = 0; i < _formatCtx->nb_streams; ++i) { - auto codecType = _formatCtx->streams[i]->codec->codec_type; + auto codecType = _formatCtx->streams[i]->codecpar->codec_type; if(codecType != AVMEDIA_TYPE_VIDEO && codecType != AVMEDIA_TYPE_AUDIO) continue; @@ -45,7 +45,7 @@ namespace ffcpp { bool MediaFile::hasStream(AVMediaType type) const { for(size_t i = 0; i < _formatCtx->nb_streams; ++i) { - if(_formatCtx->streams[i]->codec->codec_type == type) { + if(_formatCtx->streams[i]->codecpar->codec_type == type) { return true; } } @@ -63,7 +63,7 @@ namespace ffcpp { StreamPtr MediaFile::getStream(AVMediaType type, size_t index) { for(size_t i = 0, curIndex = 0; i < _formatCtx->nb_streams; ++i) { - if(_formatCtx->streams[i]->codec->codec_type == type) { + if(_formatCtx->streams[i]->codecpar->codec_type == type) { if(curIndex == index) { return _streams[i]; } else { @@ -98,22 +98,15 @@ namespace ffcpp { } StreamPtr MediaFile::addVideoStream(AVCodecID codecID, int width, int height, AVRational timeBase, AVPixelFormat pixelFormat) { - AVCodec* codec = avcodec_find_encoder(codecID); - if(!codec) throw std::runtime_error("cannot find codec"); + CodecPtr codec = std::make_shared(codecID, CodecType::Encoder); - AVStream* stream = avformat_new_stream(_formatCtx, codec); + AVStream* stream = avformat_new_stream(_formatCtx, codec->nativeCodecPtr()); if(!stream) throw std::runtime_error("cannot create stream"); - AVCodecContext* ctx = stream->codec; - ctx->width = width; - ctx->height = height; - ctx->time_base = timeBase; - - if(pixelFormat == AV_PIX_FMT_NONE) { - ctx->pix_fmt = codec->pix_fmts[0]; - } else { - ctx->pix_fmt = pixelFormat; - } + codec->setWidth(width); + codec->setHeight(height); + codec->setTimeBase(timeBase); + codec->setPixelFormat(pixelFormat); auto sPtr = std::make_shared(stream, codec); _streams.emplace_back(sPtr); @@ -121,24 +114,18 @@ namespace ffcpp { } StreamPtr MediaFile::addAudioStream(AVCodecID codecID, int channels, int sampleRate, AVSampleFormat sampleFormat) { - AVCodec* codec = avcodec_find_encoder(codecID); - if(!codec) throw std::runtime_error("cannot find codec"); + CodecPtr codec = std::make_shared(codecID, CodecType::Encoder); - AVStream* stream = avformat_new_stream(_formatCtx, codec); + AVStream* stream = avformat_new_stream(_formatCtx, codec->nativeCodecPtr()); if(!stream) throw std::runtime_error("cannot create stream"); - AVCodecContext* ctx = stream->codec; - if(sampleFormat == AV_SAMPLE_FMT_NONE) { - ctx->sample_fmt = codec->sample_fmts[0]; - } else { - ctx->sample_fmt = sampleFormat; - } - ctx->global_quality = 10; - ctx->channels = channels; - ctx->channel_layout = (uint64_t)av_get_default_channel_layout(channels); - ctx->sample_rate = sampleRate; - ctx->time_base = AVRational {1, sampleRate}; - ctx->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL; + codec->setSampleFormat(sampleFormat); + codec->setGlobalQuality(10); + codec->setChannelCount(channels); + codec->setChannelLayout(av_get_default_channel_layout(channels)); + codec->setSampleRate(sampleRate); + codec->setTimeBase(AVRational {1, sampleRate}); + codec->setStdCompliance(FF_COMPLIANCE_EXPERIMENTAL); auto sPtr = std::make_shared(stream, codec); _streams.emplace_back(sPtr); @@ -154,7 +141,7 @@ namespace ffcpp { } AVMediaType MediaFile::packetType(const Packet &packet) { - return _formatCtx->streams[packet.streamIndex()]->codec->codec_type; + return _formatCtx->streams[packet.streamIndex()]->codecpar->codec_type; } void MediaFile::writeHeader() { diff --git a/src/Stream.cpp b/src/Stream.cpp index 057d140..7e1abf0 100644 --- a/src/Stream.cpp +++ b/src/Stream.cpp @@ -7,11 +7,10 @@ namespace ffcpp { } Stream::Stream(AVStream *stream): _stream(stream) { - _codec = std::make_shared(_stream->codec, CodecType::Decoder); + _codec = std::make_shared(_stream->codecpar->codec_id, CodecType::Decoder); } - Stream::Stream(AVStream *stream, AVCodec* encoder): _stream(stream) { - _codec = std::make_shared(_stream->codec, encoder); + Stream::Stream(AVStream *stream, CodecPtr codec): _stream(stream), _codec(codec) { } Stream::operator AVStream*() const {