more C++ wrappers

This commit is contained in:
Selim Mustafaev 2016-10-18 23:58:06 +03:00
parent 3959e80e36
commit 2a2ab38bf5
11 changed files with 226 additions and 98 deletions

View File

@ -2,8 +2,8 @@ cmake_minimum_required(VERSION 3.5)
project(ffConv) project(ffConv)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/modules/") set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/modules/")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -ggdb -Og") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -ggdb -O2")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS} -std=c++14 -ggdb -Og") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS} -std=c++14 -ggdb -O0")
find_package(FFMPEG REQUIRED) find_package(FFMPEG REQUIRED)
include_directories(${FFMPEG_INCLUDE_DIRS}) include_directories(${FFMPEG_INCLUDE_DIRS})
@ -11,6 +11,6 @@ link_directories(${FFMPEG_LIBRARY_DIRS})
#message(FATAL_ERROR ${FFMPEG_LIBRARIES}) #message(FATAL_ERROR ${FFMPEG_LIBRARIES})
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 ffcpp/Packet.cpp ffcpp/Packet.h) 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 ffcpp/Packet.cpp ffcpp/Packet.h ffcpp/Frame.cpp ffcpp/Frame.h)
add_executable(ffConv ${SOURCE_FILES}) add_executable(ffConv ${SOURCE_FILES})
target_link_libraries(ffConv ${FFMPEG_LIBRARIES}) target_link_libraries(ffConv ${FFMPEG_LIBRARIES})

View File

@ -83,4 +83,29 @@ namespace ffcpp {
return *this; return *this;
} }
Frame Codec::decode(Packet &packet) {
Frame frame;
int gotPicture = 0;
auto decFunc = (_codecCtx->codec_type == AVMEDIA_TYPE_VIDEO ? avcodec_decode_video2 : avcodec_decode_audio4);
while(!gotPicture) {
int res = decFunc(_codecCtx, frame, &gotPicture, packet);
if(res < 0) throw std::runtime_error("cannot decode packet");
}
frame.guessPts();
return frame;
}
Packet Codec::encode(AVFrame* 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, &gotPacket);
if(res < 0) throw std::runtime_error("cannot encode frame");
return packet;
}
} }

View File

@ -1,6 +1,9 @@
#ifndef FFCONV_CODEC_H #ifndef FFCONV_CODEC_H
#define FFCONV_CODEC_H #define FFCONV_CODEC_H
#include "Packet.h"
#include "Frame.h"
extern "C" { extern "C" {
#include <libavformat/avformat.h> #include <libavformat/avformat.h>
} }
@ -14,8 +17,8 @@ namespace ffcpp {
class Codec { class Codec {
private: private:
AVCodecContext* _codecCtx;
AVCodec* _codec; AVCodec* _codec;
AVCodecContext* _codecCtx;
public: public:
Codec(); Codec();
@ -35,6 +38,9 @@ namespace ffcpp {
void setHeight(int height); void setHeight(int height);
void setPixelFormat(AVPixelFormat pixelFormat); void setPixelFormat(AVPixelFormat pixelFormat);
Frame decode(Packet& packet);
Packet encode(AVFrame* frame);
public: public:
Codec(Codec&& c); Codec(Codec&& c);
Codec& operator=(Codec&& c); Codec& operator=(Codec&& c);

42
ffcpp/Frame.cpp Normal file
View File

@ -0,0 +1,42 @@
#include "Frame.h"
#include <stdexcept>
namespace ffcpp {
Frame::Frame() {
_frame = av_frame_alloc();
}
Frame::Frame(Frame &&frame) {
*this = std::move(frame);
}
Frame::~Frame() {
if(_frame) {
av_frame_free(&_frame);
}
}
Frame& Frame::operator=(Frame&& frame) {
_frame = frame._frame;
frame._frame = nullptr;
return *this;
}
Frame::operator AVFrame*() {
return _frame;
}
Frame::operator const AVFrame*() const {
return _frame;
}
void Frame::guessPts() {
_frame->pts = av_frame_get_best_effort_timestamp(_frame);
}
void Frame::setPictureType(AVPictureType type) {
_frame->pict_type = type;
}
}

29
ffcpp/Frame.h Normal file
View File

@ -0,0 +1,29 @@
#ifndef FFCONV_FRAME_H
#define FFCONV_FRAME_H
extern "C" {
#include <libavformat/avformat.h>
}
namespace ffcpp {
class Frame {
private:
AVFrame* _frame;
public:
Frame();
Frame(Frame&& frame);
~Frame();
Frame& operator=(Frame&& frame);
operator AVFrame*();
operator const AVFrame*() const;
void guessPts();
void setPictureType(AVPictureType type);
};
}
#endif //FFCONV_FRAME_H

View File

@ -5,6 +5,7 @@
namespace ffcpp { namespace ffcpp {
MediaFile::MediaFile(const std::string& src, Mode mode) { MediaFile::MediaFile(const std::string& src, Mode mode) {
_formatCtx = nullptr;
_mode = mode; _mode = mode;
if(mode == Mode::Read) { if(mode == Mode::Read) {
int res = avformat_open_input(&_formatCtx, src.c_str(), nullptr, nullptr); int res = avformat_open_input(&_formatCtx, src.c_str(), nullptr, nullptr);
@ -13,8 +14,9 @@ namespace ffcpp {
res = avformat_find_stream_info(_formatCtx, nullptr); res = avformat_find_stream_info(_formatCtx, nullptr);
throwIfError(res, "cannot find stream info"); throwIfError(res, "cannot find stream info");
_streams.reserve(_formatCtx->nb_streams);
for(size_t i = 0; i < _formatCtx->nb_streams; ++i) { for(size_t i = 0; i < _formatCtx->nb_streams; ++i) {
_streams.push_back(Stream(_formatCtx->streams[i])); _streams.emplace_back(_formatCtx->streams[i]);
} }
} else if(mode == Mode::Write) { } else if(mode == Mode::Write) {
int res = avformat_alloc_output_context2(&_formatCtx, nullptr, nullptr, src.c_str()); int res = avformat_alloc_output_context2(&_formatCtx, nullptr, nullptr, src.c_str());
@ -77,6 +79,8 @@ namespace ffcpp {
} }
MediaFile::~MediaFile() { MediaFile::~MediaFile() {
_streams.clear();
if(_mode == Mode::Write) { if(_mode == Mode::Write) {
avformat_free_context(_formatCtx); avformat_free_context(_formatCtx);
} else { } else {
@ -100,6 +104,18 @@ namespace ffcpp {
return _streams.back(); return _streams.back();
} }
Packet MediaFile::readPacket() {
AVPacket packet;
packet.data = nullptr;
packet.size = 0;
int res = av_read_frame(_formatCtx, &packet);
return Packet(packet);
}
AVMediaType MediaFile::packetType(const Packet &packet) {
return _formatCtx->streams[packet.streamIndex()]->codec->codec_type;
}
void MediaFile::writeHeader() { void MediaFile::writeHeader() {
int res = avformat_write_header(_formatCtx, nullptr); int res = avformat_write_header(_formatCtx, nullptr);
throwIfError(res, "error writing header"); throwIfError(res, "error writing header");
@ -109,4 +125,10 @@ namespace ffcpp {
int res = av_write_trailer(_formatCtx); int res = av_write_trailer(_formatCtx);
throwIfError(res, "error writing trailer"); throwIfError(res, "error writing trailer");
} }
void MediaFile::writePacket(Packet &packet) {
int res = av_interleaved_write_frame(_formatCtx, packet);
throwIfError(res, "cannot write frame to output file");
}
} }

View File

@ -34,10 +34,12 @@ namespace ffcpp {
Stream& videoStream(size_t index = 0); Stream& videoStream(size_t index = 0);
Stream& audioStream(size_t index = 0); Stream& audioStream(size_t index = 0);
Stream& addStream(AVCodecID codecID, int width, int height, AVPixelFormat pixelFormat); Stream& addStream(AVCodecID codecID, int width, int height, AVPixelFormat pixelFormat);
Packet readPacket();
AVMediaType packetType(const Packet& packet);
void writeHeader(); void writeHeader();
void writeTrailer(); void writeTrailer();
void writePacket(Packet& packet);
~MediaFile(); ~MediaFile();

View File

@ -1,10 +1,53 @@
#include "Packet.h" #include "Packet.h"
#include <stdexcept>
namespace ffcpp { namespace ffcpp {
Packet::Packet() { Packet::Packet() {
_packet.data = nullptr; _packet.data = nullptr;
_packet.size = 0; _packet.size = 0;
av_init_packet(&_packet);
}
Packet::Packet(AVPacket packet): _packet(packet) {
}
Packet::Packet(Packet&& packet) {
*this = std::move(packet);
}
Packet& Packet::operator=(Packet&& packet) {
_packet = packet._packet;
packet._packet.size = 0;
packet._packet.data = nullptr;
return *this;
}
Packet::~Packet() {
if(_packet.data) {
av_free_packet(&_packet);
}
}
Packet::operator bool() {
return (_packet.data != nullptr);
}
Packet::operator AVPacket*() {
return &_packet;
}
int Packet::streamIndex() const {
return _packet.stream_index;
}
void Packet::setStreamIndex(int index) {
_packet.stream_index = index;
}
void Packet::rescaleTimestamps(AVRational from, AVRational to) {
av_packet_rescale_ts(&_packet, from, to);
} }
} }

View File

@ -13,6 +13,18 @@ namespace ffcpp {
public: public:
Packet(); Packet();
Packet(AVPacket packet);
Packet(Packet&& packet);
Packet& operator=(Packet&& packet);
~Packet();
operator bool();
operator AVPacket*();
int streamIndex() const;
void setStreamIndex(int index);
void rescaleTimestamps(AVRational from, AVRational to);
}; };
} }

View File

@ -4,7 +4,7 @@
namespace ffcpp { namespace ffcpp {
Stream::Stream(): _stream(nullptr) { Stream::Stream(): _stream(nullptr) {
int x = 0;
} }
Stream::Stream(AVStream *stream): _stream(stream), _codec(_stream->codec, CodecType::Decoder) { Stream::Stream(AVStream *stream): _stream(stream), _codec(_stream->codec, CodecType::Decoder) {

107
main.cpp
View File

@ -1,23 +1,25 @@
#include <iostream> #include <iostream>
#include "ffcpp/ffcpp.h" #include "ffcpp/ffcpp.h"
#include "ffcpp/MediaFile.h" #include "ffcpp/MediaFile.h"
#include "ffcpp/Stream.h"
#include "ffcpp/Codec.h"
extern "C" { constexpr int VIDEO_STREAM_INDEX = 0;
#include <libavformat/avformat.h> constexpr int AUDIO_STREAM_INDEX = 1;
}
void checkResult(int res, const char* msg) {
if(res < 0) {
char errStr[260];
av_strerror(res, errStr, 260);
std::cerr << msg << ": " << errStr << std::endl;
}
}
namespace ff = ffcpp; namespace ff = ffcpp;
void flushEncoder(ff::MediaFile& file, ff::Codec& encoder, const ff::Stream& inStream, const ff::Stream& outStream, int streamIndex) {
if(encoder.capabilities() & AV_CODEC_CAP_DELAY) {
while (1) {
auto packet = encoder.encode(nullptr);
if(!packet) break;
packet.setStreamIndex(streamIndex);
packet.rescaleTimestamps(inStream.timeBase(), outStream.timeBase());
file.writePacket(packet);
}
}
}
int main(int argc, char** argv) { int main(int argc, char** argv) {
ff::init(); ff::init();
ff::MediaFile input(argv[1], ff::Mode::Read); ff::MediaFile input(argv[1], ff::Mode::Read);
@ -31,43 +33,18 @@ int main(int argc, char** argv) {
ff::Codec& vEncoder = outVStream.codec(); ff::Codec& vEncoder = outVStream.codec();
output.writeHeader(); output.writeHeader();
AVFrame* frame = nullptr;
int gotPicture = 0, gotPacket = 0, decodedFrames = 1;
int64_t oldPts = 0, oldDts = 0; int64_t oldPts = 0, oldDts = 0;
while(true) { while(auto packet = input.readPacket()) {
AVPacket packet; AVMediaType packetType = input.packetType(packet);
packet.data = nullptr;
packet.size = 0;
int res = av_read_frame(input, &packet);
if(res < 0) break;
frame = av_frame_alloc();
if(!frame) break;
AVMediaType packetType = ((AVFormatContext*)input)->streams[packet.stream_index]->codec->codec_type;
if(packetType == AVMEDIA_TYPE_VIDEO) { if(packetType == AVMEDIA_TYPE_VIDEO) {
res = avcodec_decode_video2(vDecoder, frame, &gotPicture, &packet); auto frame = vDecoder.decode(packet);
if(res < 0) { frame.setPictureType(AV_PICTURE_TYPE_NONE);
av_frame_free(&frame); auto encPacket = vEncoder.encode(frame);
break; if(!encPacket) continue;
}
if(gotPicture) { encPacket.setStreamIndex(packetType == AVMEDIA_TYPE_VIDEO ? VIDEO_STREAM_INDEX : AUDIO_STREAM_INDEX);
frame->pts = av_frame_get_best_effort_timestamp(frame);
frame->pict_type = AV_PICTURE_TYPE_NONE;
//std::cout << "decoded frame: " << decodedFrames++ << " pts: " << frame->pts << ", dts: " << frame->pkt_dts << std::endl;
AVPacket encPacket;
encPacket.data = nullptr;
encPacket.size = 0;
av_init_packet(&encPacket);
encPacket.stream_index = 0;
res = avcodec_encode_video2(vEncoder, &encPacket, frame, &gotPacket);
av_frame_free(&frame);
if(res < 0) break;
if(!gotPacket) continue;
/*
// try to recover in case of bad pts/dts // try to recover in case of bad pts/dts
if(encPacket.pts < encPacket.dts) { if(encPacket.pts < encPacket.dts) {
encPacket.dts = encPacket.pts; encPacket.dts = encPacket.pts;
@ -81,44 +58,14 @@ int main(int argc, char** argv) {
oldPts = encPacket.pts; oldPts = encPacket.pts;
oldDts = encPacket.dts; oldDts = encPacket.dts;
*/
av_packet_rescale_ts(&encPacket, vStream.timeBase(), outVStream.timeBase()); encPacket.rescaleTimestamps(vStream.timeBase(), outVStream.timeBase());
output.writePacket(encPacket);
res = av_interleaved_write_frame(output, &encPacket);
checkResult(res, "cannot write frame to output file");
if(res < 0) break;
}
}
av_free_packet(&packet);
}
// flush encoder
if(vEncoder.capabilities() & AV_CODEC_CAP_DELAY) {
std::cout << "flushing encoder" << std::endl;
int gotFrame = 0;
while (1) {
AVPacket encPacket;
encPacket.data = nullptr;
encPacket.size = 0;
av_init_packet(&encPacket);
encPacket.stream_index = 0;
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, vStream.timeBase(), outVStream.timeBase());
res = av_interleaved_write_frame(output, &encPacket);
checkResult(res, "[flush encoder] cannot write frame to output file");
} else {
break;
}
} }
} }
flushEncoder(output, vEncoder, vStream, outVStream, VIDEO_STREAM_INDEX);
output.writeTrailer(); output.writeTrailer();
return 0; return 0;