added some C++ wrappers

This commit is contained in:
Selim Mustafaev 2016-08-28 23:29:54 +03:00
parent db9d3c39a1
commit d7f8c733ec
10 changed files with 397 additions and 83 deletions

View File

@ -11,6 +11,6 @@ link_directories(${FFMPEG_LIBRARY_DIRS})
#message(FATAL_ERROR ${FFMPEG_LIBRARIES})
set(SOURCE_FILES main.cpp)
set(SOURCE_FILES main.cpp ffcpp/MediaFile.cpp ffcpp/MediaFile.h ffcpp/ffcpp.cpp ffcpp/ffcpp.h ffcpp/Stream.cpp ffcpp/Stream.h ffcpp/Codec.cpp ffcpp/Codec.h)
add_executable(ffConv ${SOURCE_FILES})
target_link_libraries(ffConv ${FFMPEG_LIBRARIES})

72
ffcpp/Codec.cpp Normal file
View File

@ -0,0 +1,72 @@
#include "Codec.h"
#include "ffcpp.h"
#include <stdexcept>
namespace ffcpp {
Codec::Codec(): _codecCtx(nullptr), _codec(nullptr) {
}
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);
}
if(!_codec) throw std::runtime_error("cannot find codec");
int res = avcodec_open2(_codecCtx, _codec, nullptr);
throwIfError(res, "cannot open codec");
}
Codec::Codec(AVCodecContext *ctx, AVCodec *codec) {
_codecCtx = ctx;
_codec = codec;
int res = avcodec_open2(_codecCtx, _codec, nullptr);
throwIfError(res, "cannot open codec");
}
Codec::~Codec() {
//avcodec_close(_codecCtx);
}
Codec::operator AVCodecContext*() const {
return _codecCtx;
}
int Codec::width() const {
return _codecCtx->width;
}
int Codec::height() const {
return _codecCtx->height;
}
AVRational Codec::timeBase() const {
return _codecCtx->time_base;
}
int Codec::capabilities() const {
return _codec->capabilities;
}
AVPixelFormat Codec::pixelFormat() const {
return _codecCtx->pix_fmt;
}
void Codec::setWidth(int width) {
_codecCtx->width = width;
}
void Codec::setHeight(int height) {
_codecCtx->height = height;
}
void Codec::setPixelFormat(AVPixelFormat pixelFormat) {
_codecCtx->pix_fmt = pixelFormat;
}
}

41
ffcpp/Codec.h Normal file
View File

@ -0,0 +1,41 @@
#ifndef FFCONV_CODEC_H
#define FFCONV_CODEC_H
extern "C" {
#include <libavformat/avformat.h>
}
namespace ffcpp {
enum class CodecType {
Encoder,
Decoder
};
class Codec {
private:
AVCodecContext* _codecCtx;
AVCodec* _codec;
public:
Codec();
Codec(AVCodecContext* ctx, CodecType type);
Codec(AVCodecContext* ctx, AVCodec* codec);
~Codec();
operator AVCodecContext*() const;
int width() const;
int height() const;
AVRational timeBase() const;
int capabilities() const;
AVPixelFormat pixelFormat() const;
void setWidth(int width);
void setHeight(int height);
void setPixelFormat(AVPixelFormat pixelFormat);
};
}
#endif //FFCONV_CODEC_H

107
ffcpp/MediaFile.cpp Normal file
View File

@ -0,0 +1,107 @@
#include "MediaFile.h"
#include "ffcpp.h"
#include <stdexcept>
namespace ffcpp {
MediaFile::MediaFile(const std::string& src, Mode mode) {
_mode = mode;
if(mode == Mode::Read) {
int res = avformat_open_input(&_formatCtx, src.c_str(), nullptr, nullptr);
throwIfError(res, "cannot open input file");
res = avformat_find_stream_info(_formatCtx, nullptr);
throwIfError(res, "cannot find stream info");
} else if(mode == Mode::Write) {
int res = avformat_alloc_output_context2(&_formatCtx, nullptr, nullptr, src.c_str());
throwIfError(res, "cannot allocate format context");
if (!(_formatCtx->oformat->flags & AVFMT_NOFILE)) {
res = avio_open(&_formatCtx->pb, src.c_str(), AVIO_FLAG_WRITE);
throwIfError(res, "cannot init avio context");
}
if(_formatCtx->oformat->flags & AVFMT_GLOBALHEADER)
_formatCtx->flags |= CODEC_FLAG_GLOBAL_HEADER;
av_dump_format(_formatCtx, 0, src.c_str(), 1);
}
}
bool MediaFile::hasStream(AVMediaType type) const {
for(size_t i = 0; i < _formatCtx->nb_streams; ++i) {
if(_formatCtx->streams[i]->codec->codec_type == type) {
return true;
}
}
return false;
}
bool MediaFile::hasVideo() const {
return hasStream(AVMEDIA_TYPE_VIDEO);
}
bool MediaFile::hasAudio() const {
return hasStream(AVMEDIA_TYPE_AUDIO);
}
Stream MediaFile::getStream(AVMediaType type, size_t index) const {
for(size_t i = 0, curIndex = 0; i < _formatCtx->nb_streams; ++i) {
if(_formatCtx->streams[i]->codec->codec_type == type) {
if(curIndex == index) {
return Stream(_formatCtx->streams[i]);
} else {
curIndex++;
}
}
}
throw std::runtime_error("cannot find stream");
}
Stream MediaFile::videoStream(size_t index /* = 0 */) const {
return getStream(AVMEDIA_TYPE_VIDEO, index);
}
Stream MediaFile::audioStream(size_t index /* = 0 */) const {
return getStream(AVMEDIA_TYPE_AUDIO, index);
}
MediaFile::operator AVFormatContext*() const {
return _formatCtx;
}
MediaFile::~MediaFile() {
if(_mode == Mode::Write) {
avformat_free_context(_formatCtx);
} else {
avformat_close_input(&_formatCtx);
}
}
Stream MediaFile::addStream(AVCodecID codecID, int width, int height, AVPixelFormat pixelFormat) {
AVCodec* codec = avcodec_find_encoder(codecID);
if(!codec) throw std::runtime_error("cannot find codec");
AVStream* stream = avformat_new_stream(_formatCtx, codec);
if(!stream) throw std::runtime_error("cannot create stream");
AVCodecContext* ctx = stream->codec;
ctx->width = width;
ctx->height = height;
ctx->pix_fmt = pixelFormat;
return Stream(stream, codec);
}
void MediaFile::writeHeader() {
int res = avformat_write_header(_formatCtx, nullptr);
throwIfError(res, "error writing header");
}
void MediaFile::writeTrailer() {
int res = av_write_trailer(_formatCtx);
throwIfError(res, "error writing trailer");
}
}

47
ffcpp/MediaFile.h Normal file
View File

@ -0,0 +1,47 @@
#ifndef FFCONV_MEDIAFILE_H
#define FFCONV_MEDIAFILE_H
#include "Stream.h"
extern "C" {
#include <libavformat/avformat.h>
}
#include <string>
namespace ffcpp {
enum class Mode {
Read,
Write
};
class MediaFile {
private:
AVFormatContext* _formatCtx;
Mode _mode;
public:
MediaFile(const std::string& src, Mode mode);
operator AVFormatContext*() const;
bool hasVideo() const;
bool hasAudio() const;
Stream videoStream(size_t index = 0) const;
Stream audioStream(size_t index = 0) const;
Stream addStream(AVCodecID codecID, int width, int height, AVPixelFormat pixelFormat);
void writeHeader();
void writeTrailer();
~MediaFile();
private:
bool hasStream(AVMediaType type) const;
Stream getStream(AVMediaType type, size_t index) const;
};
}
#endif //FFCONV_MEDIAFILE_H

36
ffcpp/Stream.cpp Normal file
View File

@ -0,0 +1,36 @@
#include "Stream.h"
#include <stdexcept>
namespace ffcpp {
Stream::Stream() {
_stream = nullptr;
}
Stream::Stream(AVStream *stream) {
_stream = stream;
_codec = Codec(_stream->codec, CodecType::Decoder);
}
Stream::Stream(AVStream *stream, AVCodec* encoder) {
_stream = stream;
_codec = Codec(stream->codec, encoder);
}
Stream::operator AVStream*() const {
return _stream;
}
Codec Stream::codec() const {
return _codec;
}
AVRational Stream::timeBase() const {
return _stream->time_base;
}
void Stream::setTimeBase(AVRational timeBase) {
_stream->time_base = timeBase;
}
}

30
ffcpp/Stream.h Normal file
View File

@ -0,0 +1,30 @@
#ifndef FFCONV_STREAM_H
#define FFCONV_STREAM_H
#include "Codec.h"
extern "C" {
#include <libavformat/avformat.h>
}
namespace ffcpp {
class Stream {
private:
AVStream* _stream;
Codec _codec;
public:
Stream();
Stream(AVStream* stream);
Stream(AVStream* stream, AVCodec* encoder);
operator AVStream*() const;
Codec codec() const;
AVRational timeBase() const;
void setTimeBase(AVRational timeBase);
};
}
#endif //FFCONV_STREAM_H

22
ffcpp/ffcpp.cpp Normal file
View File

@ -0,0 +1,22 @@
#include "ffcpp.h"
#include <stdexcept>
extern "C" {
#include <libavformat/avformat.h>
}
namespace ffcpp {
void init() {
av_register_all();
}
void throwIfError(int result, const std::string& description) {
if(result < 0) {
char errStr[260];
av_strerror(result, errStr, 260);
throw std::runtime_error(description + ": " + errStr);
}
}
}

13
ffcpp/ffcpp.h Normal file
View File

@ -0,0 +1,13 @@
#ifndef FFCONV_FFCPP_H
#define FFCONV_FFCPP_H
#include <string>
namespace ffcpp {
void init();
void throwIfError(int result, const std::string& description);
}
#endif //FFCONV_FFCPP_H

110
main.cpp
View File

@ -1,4 +1,8 @@
#include <iostream>
#include "ffcpp/ffcpp.h"
#include "ffcpp/MediaFile.h"
#include "ffcpp/Stream.h"
#include "ffcpp/Codec.h"
extern "C" {
#include <libavformat/avformat.h>
@ -12,69 +16,23 @@ void checkResult(int res, const char* msg) {
}
}
namespace ff = ffcpp;
int main(int argc, char** argv) {
AVFormatContext* inFmtCtx = nullptr;
AVFormatContext* outFmtCtx = nullptr;
ff::init();
ff::MediaFile input(argv[1], ff::Mode::Read);
ff::MediaFile output(argv[2], ff::Mode::Write);
av_register_all();
int res = avformat_open_input(&inFmtCtx, argv[1], nullptr, nullptr);
checkResult(res, "cannot open input");
res = avformat_find_stream_info(inFmtCtx, nullptr);
checkResult(res, "cannot find stream info");
res = avformat_alloc_output_context2(&outFmtCtx, nullptr, nullptr, argv[2]);
checkResult(res, "cannot allocate output context");
AVStream *videoStream = nullptr, *audioStream = nullptr;
for(int i = 0; i < inFmtCtx->nb_streams; ++i) {
AVStream* stream = inFmtCtx->streams[i];
if(stream->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
std::cout << "found video stream " << avcodec_get_name(stream->codec->codec_id) << std::endl;
videoStream = stream;
} else if(stream->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
std::cout << "found audio stream " << avcodec_get_name(stream->codec->codec_id) << std::endl;
audioStream = stream;
}
ff::Stream vStream;
if(input.hasVideo()) {
vStream = input.videoStream();
}
AVCodecContext* codecCtx = videoStream->codec;
AVCodec* codec = avcodec_find_decoder(codecCtx->codec_id);
if(!codec) std::cout << "cannot find decoder" << std::endl;
res = avcodec_open2(codecCtx, codec, nullptr);
checkResult(res, "cannot open codec for decoding");
// coder
AVCodec* dstVCodec = avcodec_find_encoder(AV_CODEC_ID_H264);
if(!dstVCodec) std::cout << "cannot find encoder" << std::endl;
AVStream* outVideoStream = avformat_new_stream(outFmtCtx, dstVCodec);
if(!outVideoStream) std::cout << "cannot create output video stream" << std::endl;
if (!(outFmtCtx->oformat->flags & AVFMT_NOFILE)) {
res = avio_open(&outFmtCtx->pb, argv[2], AVIO_FLAG_WRITE);
checkResult(res, "cannot open output file");
}
av_dump_format(outFmtCtx, 0, argv[2], 1);
AVCodecContext* dstVCodecCtx = outVideoStream->codec;
dstVCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P;
dstVCodecCtx->width = codecCtx->width;
dstVCodecCtx->height = codecCtx->height;
//dstVCodecCtx->time_base = codecCtx->time_base;
outVideoStream->time_base = codecCtx->time_base;
outFmtCtx->duration = inFmtCtx->duration;
if(outFmtCtx->oformat->flags & AVFMT_GLOBALHEADER)
dstVCodecCtx->flags |= CODEC_FLAG_GLOBAL_HEADER;
res = avcodec_open2(dstVCodecCtx, dstVCodec, nullptr);
checkResult(res, "cannot open codec for encoding");
res = avformat_write_header(outFmtCtx, nullptr);
checkResult(res, "error writing header to output file");
ff::Codec vDecoder = vStream.codec();
ff::Stream outVStream = output.addStream(AV_CODEC_ID_H264, vDecoder.width(), vDecoder.height(), AV_PIX_FMT_YUV420P);
outVStream.setTimeBase(vDecoder.timeBase());
ff::Codec vEncoder = outVStream.codec();
output.writeHeader();
AVFrame* frame = nullptr;
int gotPicture = 0, gotPacket = 0, decodedFrames = 1;
@ -83,15 +41,15 @@ int main(int argc, char** argv) {
AVPacket packet;
packet.data = nullptr;
packet.size = 0;
res = av_read_frame(inFmtCtx, &packet);
int res = av_read_frame(input, &packet);
if(res < 0) break;
frame = av_frame_alloc();
if(!frame) break;
AVMediaType packetType = inFmtCtx->streams[packet.stream_index]->codec->codec_type;
AVMediaType packetType = ((AVFormatContext*)input)->streams[packet.stream_index]->codec->codec_type;
if(packetType == AVMEDIA_TYPE_VIDEO) {
res = avcodec_decode_video2(codecCtx, frame, &gotPicture, &packet);
res = avcodec_decode_video2(vDecoder, frame, &gotPicture, &packet);
if(res < 0) {
av_frame_free(&frame);
break;
@ -108,7 +66,7 @@ int main(int argc, char** argv) {
av_init_packet(&encPacket);
encPacket.stream_index = 0;
res = avcodec_encode_video2(dstVCodecCtx, &encPacket, frame, &gotPacket);
res = avcodec_encode_video2(vEncoder, &encPacket, frame, &gotPacket);
av_frame_free(&frame);
if(res < 0) break;
if(!gotPacket) continue;
@ -127,9 +85,9 @@ int main(int argc, char** argv) {
oldPts = encPacket.pts;
oldDts = encPacket.dts;
av_packet_rescale_ts(&encPacket, videoStream->time_base, outVideoStream->time_base);
av_packet_rescale_ts(&encPacket, vStream.timeBase(), outVStream.timeBase());
res = av_interleaved_write_frame(outFmtCtx, &encPacket);
res = av_interleaved_write_frame(output, &encPacket);
checkResult(res, "cannot write frame to output file");
if(res < 0) break;
}
@ -139,7 +97,7 @@ int main(int argc, char** argv) {
}
// flush encoder
if(dstVCodecCtx->codec->capabilities & AV_CODEC_CAP_DELAY) {
if(vEncoder.capabilities() & AV_CODEC_CAP_DELAY) {
std::cout << "flushing encoder" << std::endl;
int gotFrame = 0;
while (1) {
@ -148,15 +106,15 @@ int main(int argc, char** argv) {
encPacket.size = 0;
av_init_packet(&encPacket);
encPacket.stream_index = 0;
res = avcodec_encode_video2(dstVCodecCtx, &encPacket, nullptr, &gotFrame);
int res = avcodec_encode_video2(vEncoder, &encPacket, nullptr, &gotFrame);
if (res < 0) {
std::cout << "avcodec_encode_video2 failed" << std::endl;
break;
}
if (gotFrame) {
//std::cout << "extra frame" << std::endl;
av_packet_rescale_ts(&encPacket, videoStream->time_base, outVideoStream->time_base);
res = av_interleaved_write_frame(outFmtCtx, &encPacket);
av_packet_rescale_ts(&encPacket, vStream.timeBase(), outVStream.timeBase());
res = av_interleaved_write_frame(output, &encPacket);
checkResult(res, "[flush encoder] cannot write frame to output file");
} else {
break;
@ -164,19 +122,7 @@ int main(int argc, char** argv) {
}
}
res = av_write_trailer(outFmtCtx);
checkResult(res, "error writing trailer to output file");
if(codecCtx)
avcodec_close(codecCtx);
if(dstVCodecCtx)
avcodec_close(dstVCodecCtx);
if(inFmtCtx)
avformat_close_input(&inFmtCtx);
avformat_free_context(outFmtCtx);
output.writeTrailer();
return 0;
}