more C++ wrappers
This commit is contained in:
parent
3959e80e36
commit
2a2ab38bf5
@ -2,8 +2,8 @@ cmake_minimum_required(VERSION 3.5)
|
||||
project(ffConv)
|
||||
|
||||
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_DEBUG "${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 -O0")
|
||||
|
||||
find_package(FFMPEG REQUIRED)
|
||||
include_directories(${FFMPEG_INCLUDE_DIRS})
|
||||
@ -11,6 +11,6 @@ link_directories(${FFMPEG_LIBRARY_DIRS})
|
||||
|
||||
#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})
|
||||
target_link_libraries(ffConv ${FFMPEG_LIBRARIES})
|
||||
@ -83,4 +83,29 @@ namespace ffcpp {
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,6 +1,9 @@
|
||||
#ifndef FFCONV_CODEC_H
|
||||
#define FFCONV_CODEC_H
|
||||
|
||||
#include "Packet.h"
|
||||
#include "Frame.h"
|
||||
|
||||
extern "C" {
|
||||
#include <libavformat/avformat.h>
|
||||
}
|
||||
@ -14,8 +17,8 @@ namespace ffcpp {
|
||||
|
||||
class Codec {
|
||||
private:
|
||||
AVCodecContext* _codecCtx;
|
||||
AVCodec* _codec;
|
||||
AVCodecContext* _codecCtx;
|
||||
|
||||
public:
|
||||
Codec();
|
||||
@ -35,6 +38,9 @@ namespace ffcpp {
|
||||
void setHeight(int height);
|
||||
void setPixelFormat(AVPixelFormat pixelFormat);
|
||||
|
||||
Frame decode(Packet& packet);
|
||||
Packet encode(AVFrame* frame);
|
||||
|
||||
public:
|
||||
Codec(Codec&& c);
|
||||
Codec& operator=(Codec&& c);
|
||||
|
||||
42
ffcpp/Frame.cpp
Normal file
42
ffcpp/Frame.cpp
Normal 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
29
ffcpp/Frame.h
Normal 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
|
||||
@ -5,6 +5,7 @@
|
||||
namespace ffcpp {
|
||||
|
||||
MediaFile::MediaFile(const std::string& src, Mode mode) {
|
||||
_formatCtx = nullptr;
|
||||
_mode = mode;
|
||||
if(mode == Mode::Read) {
|
||||
int res = avformat_open_input(&_formatCtx, src.c_str(), nullptr, nullptr);
|
||||
@ -13,8 +14,9 @@ namespace ffcpp {
|
||||
res = avformat_find_stream_info(_formatCtx, nullptr);
|
||||
throwIfError(res, "cannot find stream info");
|
||||
|
||||
_streams.reserve(_formatCtx->nb_streams);
|
||||
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) {
|
||||
int res = avformat_alloc_output_context2(&_formatCtx, nullptr, nullptr, src.c_str());
|
||||
@ -77,6 +79,8 @@ namespace ffcpp {
|
||||
}
|
||||
|
||||
MediaFile::~MediaFile() {
|
||||
_streams.clear();
|
||||
|
||||
if(_mode == Mode::Write) {
|
||||
avformat_free_context(_formatCtx);
|
||||
} else {
|
||||
@ -100,6 +104,18 @@ namespace ffcpp {
|
||||
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() {
|
||||
int res = avformat_write_header(_formatCtx, nullptr);
|
||||
throwIfError(res, "error writing header");
|
||||
@ -109,4 +125,10 @@ namespace ffcpp {
|
||||
int res = av_write_trailer(_formatCtx);
|
||||
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");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -34,10 +34,12 @@ namespace ffcpp {
|
||||
Stream& videoStream(size_t index = 0);
|
||||
Stream& audioStream(size_t index = 0);
|
||||
Stream& addStream(AVCodecID codecID, int width, int height, AVPixelFormat pixelFormat);
|
||||
|
||||
Packet readPacket();
|
||||
AVMediaType packetType(const Packet& packet);
|
||||
|
||||
void writeHeader();
|
||||
void writeTrailer();
|
||||
void writePacket(Packet& packet);
|
||||
|
||||
~MediaFile();
|
||||
|
||||
|
||||
@ -1,10 +1,53 @@
|
||||
#include "Packet.h"
|
||||
#include <stdexcept>
|
||||
|
||||
namespace ffcpp {
|
||||
|
||||
Packet::Packet() {
|
||||
_packet.data = nullptr;
|
||||
_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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -13,6 +13,18 @@ namespace ffcpp {
|
||||
|
||||
public:
|
||||
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);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
namespace ffcpp {
|
||||
|
||||
Stream::Stream(): _stream(nullptr) {
|
||||
|
||||
int x = 0;
|
||||
}
|
||||
|
||||
Stream::Stream(AVStream *stream): _stream(stream), _codec(_stream->codec, CodecType::Decoder) {
|
||||
|
||||
129
main.cpp
129
main.cpp
@ -1,23 +1,25 @@
|
||||
#include <iostream>
|
||||
#include "ffcpp/ffcpp.h"
|
||||
#include "ffcpp/MediaFile.h"
|
||||
#include "ffcpp/Stream.h"
|
||||
#include "ffcpp/Codec.h"
|
||||
|
||||
extern "C" {
|
||||
#include <libavformat/avformat.h>
|
||||
}
|
||||
|
||||
void checkResult(int res, const char* msg) {
|
||||
if(res < 0) {
|
||||
char errStr[260];
|
||||
av_strerror(res, errStr, 260);
|
||||
std::cerr << msg << ": " << errStr << std::endl;
|
||||
}
|
||||
}
|
||||
constexpr int VIDEO_STREAM_INDEX = 0;
|
||||
constexpr int AUDIO_STREAM_INDEX = 1;
|
||||
|
||||
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) {
|
||||
ff::init();
|
||||
ff::MediaFile input(argv[1], ff::Mode::Read);
|
||||
@ -31,94 +33,39 @@ int main(int argc, char** argv) {
|
||||
ff::Codec& vEncoder = outVStream.codec();
|
||||
output.writeHeader();
|
||||
|
||||
AVFrame* frame = nullptr;
|
||||
int gotPicture = 0, gotPacket = 0, decodedFrames = 1;
|
||||
int64_t oldPts = 0, oldDts = 0;
|
||||
while(true) {
|
||||
AVPacket 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;
|
||||
while(auto packet = input.readPacket()) {
|
||||
AVMediaType packetType = input.packetType(packet);
|
||||
if(packetType == AVMEDIA_TYPE_VIDEO) {
|
||||
res = avcodec_decode_video2(vDecoder, frame, &gotPicture, &packet);
|
||||
if(res < 0) {
|
||||
av_frame_free(&frame);
|
||||
break;
|
||||
auto frame = vDecoder.decode(packet);
|
||||
frame.setPictureType(AV_PICTURE_TYPE_NONE);
|
||||
auto encPacket = vEncoder.encode(frame);
|
||||
if(!encPacket) continue;
|
||||
|
||||
encPacket.setStreamIndex(packetType == AVMEDIA_TYPE_VIDEO ? VIDEO_STREAM_INDEX : AUDIO_STREAM_INDEX);
|
||||
|
||||
/*
|
||||
// try to recover in case of bad pts/dts
|
||||
if(encPacket.pts < encPacket.dts) {
|
||||
encPacket.dts = encPacket.pts;
|
||||
}
|
||||
|
||||
if(gotPicture) {
|
||||
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;
|
||||
if(encPacket.pts < oldPts)
|
||||
encPacket.pts = oldPts;
|
||||
|
||||
AVPacket encPacket;
|
||||
encPacket.data = nullptr;
|
||||
encPacket.size = 0;
|
||||
av_init_packet(&encPacket);
|
||||
encPacket.stream_index = 0;
|
||||
if(encPacket.dts < oldDts)
|
||||
encPacket.dts = oldDts;
|
||||
|
||||
res = avcodec_encode_video2(vEncoder, &encPacket, frame, &gotPacket);
|
||||
av_frame_free(&frame);
|
||||
if(res < 0) break;
|
||||
if(!gotPacket) continue;
|
||||
oldPts = encPacket.pts;
|
||||
oldDts = encPacket.dts;
|
||||
*/
|
||||
|
||||
// try to recover in case of bad pts/dts
|
||||
if(encPacket.pts < encPacket.dts) {
|
||||
encPacket.dts = encPacket.pts;
|
||||
}
|
||||
|
||||
if(encPacket.pts < oldPts)
|
||||
encPacket.pts = oldPts;
|
||||
|
||||
if(encPacket.dts < oldDts)
|
||||
encPacket.dts = oldDts;
|
||||
|
||||
oldPts = encPacket.pts;
|
||||
oldDts = encPacket.dts;
|
||||
|
||||
av_packet_rescale_ts(&encPacket, vStream.timeBase(), outVStream.timeBase());
|
||||
|
||||
res = av_interleaved_write_frame(output, &encPacket);
|
||||
checkResult(res, "cannot write frame to output file");
|
||||
if(res < 0) break;
|
||||
}
|
||||
encPacket.rescaleTimestamps(vStream.timeBase(), outVStream.timeBase());
|
||||
output.writePacket(encPacket);
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
return 0;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user