very basic support of transcoding audio (incomplete and incorrect)
This commit is contained in:
parent
2a2ab38bf5
commit
990527481d
@ -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 ffcpp/Frame.cpp ffcpp/Frame.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 ffcpp/FifoQueue.cpp ffcpp/FifoQueue.h)
|
||||||
add_executable(ffConv ${SOURCE_FILES})
|
add_executable(ffConv ${SOURCE_FILES})
|
||||||
target_link_libraries(ffConv ${FFMPEG_LIBRARIES})
|
target_link_libraries(ffConv ${FFMPEG_LIBRARIES})
|
||||||
@ -59,6 +59,15 @@ namespace ffcpp {
|
|||||||
return _codecCtx->pix_fmt;
|
return _codecCtx->pix_fmt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AVSampleFormat Codec::sampleFormat() const {
|
||||||
|
return _codecCtx->codec->sample_fmts[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
int Codec::frameSize() const {
|
||||||
|
return _codecCtx->frame_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void Codec::setWidth(int width) {
|
void Codec::setWidth(int width) {
|
||||||
_codecCtx->width = width;
|
_codecCtx->width = width;
|
||||||
}
|
}
|
||||||
@ -71,11 +80,11 @@ namespace ffcpp {
|
|||||||
_codecCtx->pix_fmt = pixelFormat;
|
_codecCtx->pix_fmt = pixelFormat;
|
||||||
}
|
}
|
||||||
|
|
||||||
Codec::Codec(Codec&& c) {
|
Codec::Codec(Codec&& c) noexcept {
|
||||||
*this = std::move(c);
|
*this = std::move(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
Codec& Codec::operator=(Codec&& c) {
|
Codec& Codec::operator=(Codec&& c) noexcept {
|
||||||
_codec = c._codec;
|
_codec = c._codec;
|
||||||
_codecCtx = c._codecCtx;
|
_codecCtx = c._codecCtx;
|
||||||
c._codec = nullptr;
|
c._codec = nullptr;
|
||||||
@ -108,4 +117,8 @@ namespace ffcpp {
|
|||||||
return packet;
|
return packet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Frame Codec::createAudioFrame() const {
|
||||||
|
return Frame(_codecCtx->frame_size, _codecCtx->channels, _codecCtx->codec->sample_fmts[0], _codecCtx->sample_rate);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
#ifndef FFCONV_CODEC_H
|
#ifndef FFCONV_CODEC_H
|
||||||
#define FFCONV_CODEC_H
|
#define FFCONV_CODEC_H
|
||||||
|
|
||||||
|
#include "ffcpp.h"
|
||||||
#include "Packet.h"
|
#include "Packet.h"
|
||||||
#include "Frame.h"
|
#include "Frame.h"
|
||||||
|
|
||||||
@ -15,7 +16,7 @@ namespace ffcpp {
|
|||||||
Decoder
|
Decoder
|
||||||
};
|
};
|
||||||
|
|
||||||
class Codec {
|
class Codec: public non_copyable {
|
||||||
private:
|
private:
|
||||||
AVCodec* _codec;
|
AVCodec* _codec;
|
||||||
AVCodecContext* _codecCtx;
|
AVCodecContext* _codecCtx;
|
||||||
@ -33,6 +34,8 @@ namespace ffcpp {
|
|||||||
AVRational timeBase() const;
|
AVRational timeBase() const;
|
||||||
int capabilities() const;
|
int capabilities() const;
|
||||||
AVPixelFormat pixelFormat() const;
|
AVPixelFormat pixelFormat() const;
|
||||||
|
AVSampleFormat sampleFormat() const;
|
||||||
|
int frameSize() const;
|
||||||
|
|
||||||
void setWidth(int width);
|
void setWidth(int width);
|
||||||
void setHeight(int height);
|
void setHeight(int height);
|
||||||
@ -40,10 +43,11 @@ namespace ffcpp {
|
|||||||
|
|
||||||
Frame decode(Packet& packet);
|
Frame decode(Packet& packet);
|
||||||
Packet encode(AVFrame* frame);
|
Packet encode(AVFrame* frame);
|
||||||
|
Frame createAudioFrame() const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Codec(Codec&& c);
|
Codec(Codec&& c) noexcept;
|
||||||
Codec& operator=(Codec&& c);
|
Codec& operator=(Codec&& c) noexcept;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
34
ffcpp/FifoQueue.cpp
Normal file
34
ffcpp/FifoQueue.cpp
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
#include "FifoQueue.h"
|
||||||
|
#include "ffcpp.h"
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
namespace ffcpp {
|
||||||
|
|
||||||
|
FifoQueue::FifoQueue(AVSampleFormat sampleFormat, int channels, int frameSize) {
|
||||||
|
_frameSize = frameSize;
|
||||||
|
_fifo = av_audio_fifo_alloc(sampleFormat, channels, 1);
|
||||||
|
if(!_fifo)
|
||||||
|
throw std::runtime_error("cannot create audio fifo queue");
|
||||||
|
}
|
||||||
|
|
||||||
|
void FifoQueue::addSamples(const Frame &frame) {
|
||||||
|
int res = av_audio_fifo_realloc(_fifo, av_audio_fifo_size(_fifo) + frame.samplesCount());
|
||||||
|
throwIfError(res, "cannot reallocate fifo queue");
|
||||||
|
|
||||||
|
const AVFrame* frameImpl = frame;
|
||||||
|
res = av_audio_fifo_write(_fifo, (void**)frameImpl->data, frame.samplesCount());
|
||||||
|
throwIfError(res, "cannot add data from frame to fifo queue");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FifoQueue::enoughSamples() const {
|
||||||
|
return av_audio_fifo_size(_fifo) >= _frameSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FifoQueue::readFrame(Frame& frame) {
|
||||||
|
AVFrame* nativeFrame = frame;
|
||||||
|
|
||||||
|
int res = av_audio_fifo_read(_fifo, (void**)nativeFrame->data, _frameSize);
|
||||||
|
throwIfError(res, "cannot read data from fifo queue");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
25
ffcpp/FifoQueue.h
Normal file
25
ffcpp/FifoQueue.h
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#ifndef FFCONV_FIFOQUEUE_H
|
||||||
|
#define FFCONV_FIFOQUEUE_H
|
||||||
|
|
||||||
|
#include "Frame.h"
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include "libavutil/audio_fifo.h"
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace ffcpp {
|
||||||
|
|
||||||
|
class FifoQueue {
|
||||||
|
private:
|
||||||
|
AVAudioFifo* _fifo;
|
||||||
|
int _frameSize;
|
||||||
|
public:
|
||||||
|
FifoQueue(AVSampleFormat sampleFormat, int channels, int frameSize);
|
||||||
|
void addSamples(const Frame& frame);
|
||||||
|
bool enoughSamples() const;
|
||||||
|
void readFrame(Frame& frame);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //FFCONV_FIFOQUEUE_H
|
||||||
@ -1,3 +1,4 @@
|
|||||||
|
#include "ffcpp.h"
|
||||||
#include "Frame.h"
|
#include "Frame.h"
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
@ -7,6 +8,17 @@ namespace ffcpp {
|
|||||||
_frame = av_frame_alloc();
|
_frame = av_frame_alloc();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Frame::Frame(int size, int channels, AVSampleFormat sampleFormat, int sampleRate): Frame() {
|
||||||
|
_frame->nb_samples = size;
|
||||||
|
_frame->channels = channels;
|
||||||
|
_frame->channel_layout = (uint64_t)av_get_default_channel_layout(channels);
|
||||||
|
_frame->format = sampleFormat;
|
||||||
|
_frame->sample_rate = sampleRate;
|
||||||
|
|
||||||
|
int res = av_frame_get_buffer(_frame, 0);
|
||||||
|
throwIfError(res, "cannot initialize buffer in audio frame");
|
||||||
|
}
|
||||||
|
|
||||||
Frame::Frame(Frame &&frame) {
|
Frame::Frame(Frame &&frame) {
|
||||||
*this = std::move(frame);
|
*this = std::move(frame);
|
||||||
}
|
}
|
||||||
@ -39,4 +51,8 @@ namespace ffcpp {
|
|||||||
_frame->pict_type = type;
|
_frame->pict_type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int Frame::samplesCount() const {
|
||||||
|
return _frame->nb_samples;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,6 +13,7 @@ namespace ffcpp {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
Frame();
|
Frame();
|
||||||
|
Frame(int size, int channels, AVSampleFormat sampleFormat, int sampleRate);
|
||||||
Frame(Frame&& frame);
|
Frame(Frame&& frame);
|
||||||
~Frame();
|
~Frame();
|
||||||
|
|
||||||
@ -22,6 +23,7 @@ namespace ffcpp {
|
|||||||
|
|
||||||
void guessPts();
|
void guessPts();
|
||||||
void setPictureType(AVPictureType type);
|
void setPictureType(AVPictureType type);
|
||||||
|
int samplesCount() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -28,7 +28,11 @@ namespace ffcpp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(_formatCtx->oformat->flags & AVFMT_GLOBALHEADER)
|
if(_formatCtx->oformat->flags & AVFMT_GLOBALHEADER)
|
||||||
_formatCtx->flags |= CODEC_FLAG_GLOBAL_HEADER;
|
_formatCtx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
|
||||||
|
|
||||||
|
// Allocate enough space, so streams will never be destroyed
|
||||||
|
// due to reallocation of internal buffer of std::vector
|
||||||
|
_streams.reserve(100);
|
||||||
|
|
||||||
av_dump_format(_formatCtx, 0, src.c_str(), 1);
|
av_dump_format(_formatCtx, 0, src.c_str(), 1);
|
||||||
}
|
}
|
||||||
@ -88,7 +92,7 @@ namespace ffcpp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream& MediaFile::addStream(AVCodecID codecID, int width, int height, AVPixelFormat pixelFormat) {
|
Stream& MediaFile::addVideoStream(AVCodecID codecID, int width, int height, AVPixelFormat pixelFormat) {
|
||||||
AVCodec* codec = avcodec_find_encoder(codecID);
|
AVCodec* codec = avcodec_find_encoder(codecID);
|
||||||
if(!codec) throw std::runtime_error("cannot find codec");
|
if(!codec) throw std::runtime_error("cannot find codec");
|
||||||
|
|
||||||
@ -100,7 +104,26 @@ namespace ffcpp {
|
|||||||
ctx->height = height;
|
ctx->height = height;
|
||||||
ctx->pix_fmt = pixelFormat;
|
ctx->pix_fmt = pixelFormat;
|
||||||
|
|
||||||
_streams.push_back(Stream(stream, codec));
|
_streams.emplace_back(stream, codec);
|
||||||
|
return _streams.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
Stream& MediaFile::addAudioStream(AVCodecID codecID) {
|
||||||
|
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");
|
||||||
|
|
||||||
|
// TODO: Here we need adjust encoder parameters
|
||||||
|
AVCodecContext* ctx = stream->codec;
|
||||||
|
ctx->sample_fmt = codec->sample_fmts[0];
|
||||||
|
ctx->global_quality = 10;
|
||||||
|
ctx->channels = 6;
|
||||||
|
ctx->channel_layout = (uint64_t)av_get_default_channel_layout(ctx->channels);
|
||||||
|
ctx->sample_rate = 48000;
|
||||||
|
|
||||||
|
_streams.emplace_back(stream, codec);
|
||||||
return _streams.back();
|
return _streams.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -33,7 +33,8 @@ namespace ffcpp {
|
|||||||
bool hasAudio() const;
|
bool hasAudio() const;
|
||||||
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& addVideoStream(AVCodecID codecID, int width, int height, AVPixelFormat pixelFormat);
|
||||||
|
Stream& addAudioStream(AVCodecID codecID);
|
||||||
Packet readPacket();
|
Packet readPacket();
|
||||||
AVMediaType packetType(const Packet& packet);
|
AVMediaType packetType(const Packet& packet);
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,6 @@
|
|||||||
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) {
|
||||||
@ -31,11 +30,11 @@ namespace ffcpp {
|
|||||||
_stream->time_base = timeBase;
|
_stream->time_base = timeBase;
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream::Stream(Stream&& stream) {
|
Stream::Stream(Stream&& stream) noexcept {
|
||||||
*this = std::move(stream);
|
*this = std::move(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream& Stream::operator=(Stream&& stream) {
|
Stream& Stream::operator=(Stream&& stream) noexcept {
|
||||||
_codec = std::move(stream._codec);
|
_codec = std::move(stream._codec);
|
||||||
_stream = stream._stream;
|
_stream = stream._stream;
|
||||||
stream._stream = nullptr;
|
stream._stream = nullptr;
|
||||||
|
|||||||
@ -25,8 +25,8 @@ namespace ffcpp {
|
|||||||
void setTimeBase(AVRational timeBase);
|
void setTimeBase(AVRational timeBase);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Stream(Stream&& stream);
|
Stream(Stream&& stream) noexcept;
|
||||||
Stream& operator=(Stream&& stream);
|
Stream& operator=(Stream&& stream) noexcept;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,6 +8,16 @@ namespace ffcpp {
|
|||||||
void init();
|
void init();
|
||||||
void throwIfError(int result, const std::string& description);
|
void throwIfError(int result, const std::string& description);
|
||||||
|
|
||||||
|
class non_copyable
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
non_copyable() = default;
|
||||||
|
~non_copyable() = default;
|
||||||
|
|
||||||
|
non_copyable(non_copyable const &) = delete;
|
||||||
|
void operator=(non_copyable const &x) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif //FFCONV_FFCPP_H
|
#endif //FFCONV_FFCPP_H
|
||||||
|
|||||||
49
main.cpp
49
main.cpp
@ -1,9 +1,11 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include "ffcpp/ffcpp.h"
|
#include "ffcpp/ffcpp.h"
|
||||||
#include "ffcpp/MediaFile.h"
|
#include "ffcpp/MediaFile.h"
|
||||||
|
#include "ffcpp/FifoQueue.h"
|
||||||
|
|
||||||
constexpr int VIDEO_STREAM_INDEX = 0;
|
constexpr int VIDEO_STREAM_INDEX = 0;
|
||||||
constexpr int AUDIO_STREAM_INDEX = 1;
|
constexpr int AUDIO_STREAM_INDEX = 1;
|
||||||
|
constexpr int AUDIO_CHANNELS_COUNT = 6;
|
||||||
|
|
||||||
namespace ff = ffcpp;
|
namespace ff = ffcpp;
|
||||||
|
|
||||||
@ -26,40 +28,43 @@ int main(int argc, char** argv) {
|
|||||||
ff::MediaFile output(argv[2], ff::Mode::Write);
|
ff::MediaFile output(argv[2], ff::Mode::Write);
|
||||||
|
|
||||||
ff::Stream& vStream = input.videoStream();
|
ff::Stream& vStream = input.videoStream();
|
||||||
|
ff::Stream& aStream = input.audioStream();
|
||||||
|
|
||||||
ff::Codec& vDecoder = vStream.codec();
|
ff::Codec& vDecoder = vStream.codec();
|
||||||
ff::Stream& outVStream = output.addStream(AV_CODEC_ID_H264, vDecoder.width(), vDecoder.height(), AV_PIX_FMT_YUV420P);
|
ff::Codec& aDecoder = aStream.codec();
|
||||||
|
|
||||||
|
ff::Stream& outVStream = output.addVideoStream(AV_CODEC_ID_H264, vDecoder.width(), vDecoder.height(),
|
||||||
|
AV_PIX_FMT_YUV420P);
|
||||||
outVStream.setTimeBase(vDecoder.timeBase());
|
outVStream.setTimeBase(vDecoder.timeBase());
|
||||||
ff::Codec& vEncoder = outVStream.codec();
|
ff::Codec& vEncoder = outVStream.codec();
|
||||||
|
ff::Stream& outAStream = output.addAudioStream(AV_CODEC_ID_VORBIS);
|
||||||
|
ff::Codec& aEncoder = outAStream.codec();
|
||||||
|
|
||||||
output.writeHeader();
|
output.writeHeader();
|
||||||
|
|
||||||
int64_t oldPts = 0, oldDts = 0;
|
ff::FifoQueue fifo(aEncoder.sampleFormat(), AUDIO_CHANNELS_COUNT, aEncoder.frameSize());
|
||||||
while(auto packet = input.readPacket()) {
|
while(auto packet = input.readPacket()) {
|
||||||
AVMediaType packetType = input.packetType(packet);
|
AVMediaType packetType = input.packetType(packet);
|
||||||
if(packetType == AVMEDIA_TYPE_VIDEO) {
|
if(packetType == AVMEDIA_TYPE_AUDIO) {
|
||||||
|
auto frame = aDecoder.decode(packet);
|
||||||
|
fifo.addSamples(frame);
|
||||||
|
if(!fifo.enoughSamples()) continue;
|
||||||
|
|
||||||
|
while(fifo.enoughSamples()) {
|
||||||
|
auto frame = aEncoder.createAudioFrame();
|
||||||
|
fifo.readFrame(frame);
|
||||||
|
auto encPacket = aEncoder.encode(frame);
|
||||||
|
if(!encPacket) continue;
|
||||||
|
encPacket.setStreamIndex(AUDIO_STREAM_INDEX);
|
||||||
|
encPacket.rescaleTimestamps(vStream.timeBase(), outVStream.timeBase());
|
||||||
|
output.writePacket(encPacket);
|
||||||
|
}
|
||||||
|
} else if(packetType == AVMEDIA_TYPE_VIDEO) {
|
||||||
auto frame = vDecoder.decode(packet);
|
auto frame = vDecoder.decode(packet);
|
||||||
frame.setPictureType(AV_PICTURE_TYPE_NONE);
|
frame.setPictureType(AV_PICTURE_TYPE_NONE);
|
||||||
auto encPacket = vEncoder.encode(frame);
|
auto encPacket = vEncoder.encode(frame);
|
||||||
if(!encPacket) continue;
|
if(!encPacket) continue;
|
||||||
|
encPacket.setStreamIndex(VIDEO_STREAM_INDEX);
|
||||||
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(encPacket.pts < oldPts)
|
|
||||||
encPacket.pts = oldPts;
|
|
||||||
|
|
||||||
if(encPacket.dts < oldDts)
|
|
||||||
encPacket.dts = oldDts;
|
|
||||||
|
|
||||||
oldPts = encPacket.pts;
|
|
||||||
oldDts = encPacket.dts;
|
|
||||||
*/
|
|
||||||
|
|
||||||
encPacket.rescaleTimestamps(vStream.timeBase(), outVStream.timeBase());
|
encPacket.rescaleTimestamps(vStream.timeBase(), outVStream.timeBase());
|
||||||
output.writePacket(encPacket);
|
output.writePacket(encPacket);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user