Latest changes

This commit is contained in:
Selim Mustafaev 2021-02-04 15:27:55 +03:00
parent 51c7109193
commit 3da12232a3
14 changed files with 119 additions and 56 deletions

View File

@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.5) cmake_minimum_required(VERSION 3.5)
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 "-std=c++14 -g -O2 -pthread") set(CMAKE_CXX_FLAGS "-std=c++17 -g -O2 -pthread")
set(CMAKE_CXX_FLAGS_DEBUG "-ggdb -O0 -pthread") set(CMAKE_CXX_FLAGS_DEBUG "-ggdb -O0 -pthread")
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -pthread") SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -pthread")

View File

@ -28,7 +28,6 @@ void flushEncoder(ff::MediaFile& file, ff::CodecPtr encoder, ff::StreamPtr inStr
} }
int main(int argc, char** argv) { int main(int argc, char** argv) {
ff::init();
ff::MediaFile input(argv[1], ff::Mode::Read); ff::MediaFile input(argv[1], ff::Mode::Read);
ff::MediaFile output(argv[2], ff::Mode::Write); ff::MediaFile output(argv[2], ff::Mode::Write);
@ -40,7 +39,7 @@ int main(int argc, char** argv) {
double aspect = 1.0*vDecoder->width()/vDecoder->height(); double aspect = 1.0*vDecoder->width()/vDecoder->height();
int outHeight = (int)(VIDEO_WIDTH/aspect) & ~1; int outHeight = (int)(VIDEO_WIDTH/aspect) & ~1;
auto outVStream = output.addVideoStream(AV_CODEC_ID_H264, VIDEO_WIDTH, outHeight, vDecoder->timeBase(), AV_PIX_FMT_YUV420P); auto outVStream = output.addVideoStream(AV_CODEC_ID_HEVC, VIDEO_WIDTH, outHeight, vDecoder->timeBase(), AV_PIX_FMT_YUV420P);
auto vEncoder = outVStream->codec(); auto vEncoder = outVStream->codec();
auto outAStream = output.addAudioStream(AV_CODEC_ID_AC3, 2, 44100, AV_SAMPLE_FMT_FLTP); auto outAStream = output.addAudioStream(AV_CODEC_ID_AC3, 2, 44100, AV_SAMPLE_FMT_FLTP);

View File

@ -8,8 +8,8 @@
namespace ff = ffcpp; namespace ff = ffcpp;
#define WINDOW_WIDTH 640 #define WINDOW_WIDTH 1280
#define WINDOW_HEIGHT 480 #define WINDOW_HEIGHT 720
class SDLWindow: public ff::IVideoSink, public ff::IAudioSink { class SDLWindow: public ff::IVideoSink, public ff::IAudioSink {
private: private:

View File

@ -24,6 +24,8 @@ namespace ffcpp {
private: private:
AVCodec* _codec; AVCodec* _codec;
AVCodecContext* _codecCtx; AVCodecContext* _codecCtx;
mutable FramePtr _tmpFrame;
mutable PacketPtr _tmpPacket;
public: public:
Codec(); Codec();
@ -57,7 +59,7 @@ namespace ffcpp {
void setSampleRate(int sampleRate); void setSampleRate(int sampleRate);
void setStdCompliance(int compliance); void setStdCompliance(int compliance);
FramePtr decode(Packet& packet); std::tuple<FramePtr, bool> decode(PacketPtr packet);
Packet encode(FramePtr frame); Packet encode(FramePtr frame);
FramePtr createAudioFrame() const; FramePtr createAudioFrame() const;

View File

@ -40,8 +40,8 @@ namespace ffcpp {
StreamPtr audioStream(size_t index = 0); StreamPtr audioStream(size_t index = 0);
StreamPtr addVideoStream(AVCodecID codecID, int width, int height, AVRational timeBase, AVPixelFormat pixelFormat = AV_PIX_FMT_NONE); StreamPtr addVideoStream(AVCodecID codecID, int width, int height, AVRational timeBase, AVPixelFormat pixelFormat = AV_PIX_FMT_NONE);
StreamPtr addAudioStream(AVCodecID codecID, int channels, int sampleRate, AVSampleFormat sampleFormat = AV_SAMPLE_FMT_NONE); StreamPtr addAudioStream(AVCodecID codecID, int channels, int sampleRate, AVSampleFormat sampleFormat = AV_SAMPLE_FMT_NONE);
Packet readPacket(); PacketPtr readPacket();
AVMediaType packetType(const Packet& packet); AVMediaType packetType(const PacketPtr packet);
void writeHeader(); void writeHeader();
void writeTrailer(); void writeTrailer();

View File

@ -5,8 +5,12 @@ extern "C" {
#include <libavformat/avformat.h> #include <libavformat/avformat.h>
} }
#include <memory>
namespace ffcpp { namespace ffcpp {
typedef std::shared_ptr<class Packet> PacketPtr;
class Packet { class Packet {
private: private:
AVPacket _packet; AVPacket _packet;

View File

@ -79,6 +79,7 @@ namespace ffcpp {
private: private:
void decode(); void decode();
void displayFrames(); void displayFrames();
void processFrame(FramePtr frame, AVMediaType type, FrameQueue* queue);
private: private:
void fillSampleBuffer(uint8_t *data, int length) override; void fillSampleBuffer(uint8_t *data, int length) override;

View File

@ -27,7 +27,7 @@ namespace ffcpp {
AVRational timeBase() const; AVRational timeBase() const;
void setTimeBase(AVRational timeBase); void setTimeBase(AVRational timeBase);
int fps() const; float fps() const;
public: public:
Stream(Stream&& stream) noexcept; Stream(Stream&& stream) noexcept;

View File

@ -1,14 +1,18 @@
#include "ffcpp/Codec.h" #include "ffcpp/Codec.h"
#include "ffcpp/ffcpp.h" #include "ffcpp/ffcpp.h"
#include <stdexcept> #include <stdexcept>
#include <iostream>
namespace ffcpp { namespace ffcpp {
Codec::Codec(): _codecCtx(nullptr), _codec(nullptr) { Codec::Codec(): _codecCtx(nullptr), _codec(nullptr), _tmpFrame(nullptr), _tmpPacket(nullptr) {
} }
Codec::Codec(AVCodecID codecId, CodecType type, AVCodecParameters* params /* = nullptr */) { Codec::Codec(AVCodecID codecId, CodecType type, AVCodecParameters* params /* = nullptr */) {
_tmpFrame = nullptr;
_tmpPacket = nullptr;
if(type == CodecType::Encoder) { if(type == CodecType::Encoder) {
_codec = avcodec_find_encoder(codecId); _codec = avcodec_find_encoder(codecId);
} else { } else {
@ -32,6 +36,8 @@ namespace ffcpp {
Codec::Codec(AVCodecContext *ctx, AVCodec *codec) { Codec::Codec(AVCodecContext *ctx, AVCodec *codec) {
_codecCtx = ctx; _codecCtx = ctx;
_codec = codec; _codec = codec;
_tmpFrame = nullptr;
_tmpPacket = nullptr;
int res = avcodec_open2(_codecCtx, _codec, nullptr); int res = avcodec_open2(_codecCtx, _codec, nullptr);
throwIfError(res, "cannot open codec"); throwIfError(res, "cannot open codec");
@ -158,19 +164,25 @@ namespace ffcpp {
return *this; return *this;
} }
FramePtr Codec::decode(Packet &packet) { std::tuple<FramePtr, bool> Codec::decode(PacketPtr packet) {
FramePtr frame = std::make_shared<Frame>(); FramePtr frame = _tmpFrame ? _tmpFrame : std::make_shared<Frame>();
int res = avcodec_send_packet(_codecCtx, packet); int res = 0;
if(res < 0) throw std::runtime_error("cannot decode packet"); if(packet) {
res = avcodec_send_packet(_codecCtx, *packet.get());
if(res < 0) throw std::runtime_error("cannot decode packet");
}
while (res >= 0) { res = avcodec_receive_frame(_codecCtx, frame->nativePtr());
res = avcodec_receive_frame(_codecCtx, frame->nativePtr()); _tmpFrame = res == AVERROR(EAGAIN) ? frame : nullptr;
if(res == AVERROR(EAGAIN) || res == AVERROR_EOF) {
break; if(res == AVERROR(EAGAIN) || res == AVERROR_EOF) {
} else if(res < 0) { if(res == AVERROR_EOF) {
throw std::runtime_error("cannot decode packet"); std::cout << "================ EOF" << std::endl;
} }
return std::make_tuple(nullptr, true);
} else if(res < 0) {
throw std::runtime_error("cannot decode packet");
} }
if(_codecCtx->codec_type == AVMEDIA_TYPE_VIDEO) { if(_codecCtx->codec_type == AVMEDIA_TYPE_VIDEO) {
@ -179,7 +191,8 @@ namespace ffcpp {
frame->guessChannelLayout(); frame->guessChannelLayout();
} }
return frame; _tmpPacket = packet;
return std::make_tuple(frame, false);
} }
Packet Codec::encode(FramePtr frame) { Packet Codec::encode(FramePtr frame) {

View File

@ -1,5 +1,6 @@
#include "ffcpp/ffcpp.h" #include "ffcpp/ffcpp.h"
#include "ffcpp/Frame.h" #include "ffcpp/Frame.h"
#include <stdexcept> #include <stdexcept>
namespace ffcpp { namespace ffcpp {
@ -63,7 +64,7 @@ namespace ffcpp {
} }
void Frame::guessPts() { void Frame::guessPts() {
_frame->pts = av_frame_get_best_effort_timestamp(_frame); _frame->pts = _frame->best_effort_timestamp;
} }
void Frame::setPictureType(AVPictureType type) { void Frame::setPictureType(AVPictureType type) {
@ -93,7 +94,12 @@ namespace ffcpp {
} }
int Frame::size() const { int Frame::size() const {
return _frame->pkt_size >= 0 ? _frame->pkt_size : _frame->linesize[0]; if(_frame->nb_samples > 0) {
return _frame->nb_samples*_frame->channels*av_get_bytes_per_sample(static_cast<AVSampleFormat>(_frame->format));
} else {
// TODO: Return something meaningful here
return _frame->pkt_size >= 0 ? _frame->pkt_size : _frame->linesize[0];
}
} }
} }

View File

@ -132,16 +132,16 @@ namespace ffcpp {
return sPtr; return sPtr;
} }
Packet MediaFile::readPacket() { PacketPtr MediaFile::readPacket() {
AVPacket packet; AVPacket packet;
packet.data = nullptr; packet.data = nullptr;
packet.size = 0; packet.size = 0;
int res = av_read_frame(_formatCtx, &packet); int res = av_read_frame(_formatCtx, &packet);
return Packet(packet); return std::make_shared<Packet>(packet);
} }
AVMediaType MediaFile::packetType(const Packet &packet) { AVMediaType MediaFile::packetType(const PacketPtr packet) {
return _formatCtx->streams[packet.streamIndex()]->codecpar->codec_type; return _formatCtx->streams[packet->streamIndex()]->codecpar->codec_type;
} }
void MediaFile::writeHeader() { void MediaFile::writeHeader() {

View File

@ -49,6 +49,9 @@ namespace ffcpp {
auto codec = _aStream->codec().get(); auto codec = _aStream->codec().get();
std::cout << "Input sample rate: " << _aStream->codec()->sampleRate() << std::endl;
std::cout << "Input channels: " << _aStream->codec()->channels() << std::endl;
_resampler = std::make_shared<Resampler>(_aStream->codec()->channels(), _resampler = std::make_shared<Resampler>(_aStream->codec()->channels(),
_aStream->codec()->channelLayout(), _aStream->codec()->channelLayout(),
_aStream->codec()->sampleRate(), _aStream->codec()->sampleRate(),
@ -78,41 +81,68 @@ namespace ffcpp {
} }
void Player::decode() { void Player::decode() {
Packet packet; PacketPtr packet;
while(true) { while(true) {
std::unique_lock<std::mutex> lock(_mutex); std::unique_lock<std::mutex> lock(_mutex);
if(_state == PlayerState::Shutdown) if(_state == PlayerState::Shutdown)
break; break;
packet = _curMedia ? _curMedia->readPacket() : Packet(); packet = _curMedia ? _curMedia->readPacket() : nullptr;
if(!packet) { if(!packet) {
_stateCond.wait(lock, [this]{ return _state == PlayerState::Playing || _state == PlayerState::Shutdown; }); _stateCond.wait(lock,
[this] { return _state == PlayerState::Playing || _state == PlayerState::Shutdown; });
continue; continue;
} }
AVMediaType packetType = _curMedia->packetType(packet); AVMediaType packetType = _curMedia->packetType(packet);
if(packetType == AVMEDIA_TYPE_VIDEO) { if(packetType != AVMEDIA_TYPE_VIDEO && packetType != AVMEDIA_TYPE_AUDIO)
auto frame = _vStream->codec()->decode(packet); continue;
frame = _scaler->scale(frame);
CodecPtr codec = packetType == AVMEDIA_TYPE_VIDEO ? _vStream->codec() : _aStream->codec();
FrameQueue* queue = packetType == AVMEDIA_TYPE_VIDEO ? &_videoFrames : &_audioFrames;
auto [frame, packedDecoded] = codec->decode(packet);
if(!frame) {
// Frame partially decoded, but not ready yet
// We need next packet to decode rest of the frame
continue;
} else if(!packedDecoded) {
lock.unlock(); lock.unlock();
while(!_videoFrames.try_enqueue(frame)) { processFrame(frame, packetType, queue);
std::cout << "waiting for enqueue video frame" << std::endl; lock.lock();
std::this_thread::sleep_for(std::chrono::milliseconds(10));
} // Frame is fully decoded, but packet contains more data (at least beginning of the next frame)
} else if(packetType == AVMEDIA_TYPE_AUDIO) { // So, we need to continue decoding current packet
auto frame = _aStream->codec()->decode(packet); while (!packedDecoded) {
frame = _resampler->resample(frame); // Decoding nullptr means "decode previous cached packet"
lock.unlock(); std::tie(frame, packedDecoded) = _vStream->codec()->decode(nullptr);
while(!_audioFrames.try_enqueue(frame)) { if(frame) {
std::cout << "waiting for enqueue audio frame" << std::endl; lock.unlock();
std::this_thread::sleep_for(std::chrono::milliseconds(10)); processFrame(frame, packetType, queue);
lock.lock();
}
} }
} }
} }
} }
void Player::processFrame(FramePtr frame, AVMediaType type, FrameQueue* queue) {
if(type == AVMEDIA_TYPE_VIDEO) {
frame = _scaler->scale(frame);
} else {
frame = _resampler->resample(frame);
}
while(!queue->try_enqueue(frame)) {
//std::cout << "waiting for enqueue video frame" << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
void Player::displayFrames() { void Player::displayFrames() {
int frameCounter = 0;
auto start = std::chrono::system_clock::now();
while(true) { while(true) {
std::unique_lock<std::mutex> lock(_mutex); std::unique_lock<std::mutex> lock(_mutex);
if(_state == PlayerState::Shutdown) if(_state == PlayerState::Shutdown)
@ -125,20 +155,25 @@ namespace ffcpp {
lock.unlock(); lock.unlock();
int fps = _vStream->fps(); float fps = _vStream->fps();
FramePtr frame; FramePtr frame;
if(_videoFrames.try_dequeue(frame)) { if(_videoFrames.try_dequeue(frame)) {
lock.lock(); lock.lock();
AVFrame* f = frame->nativePtr(); AVFrame* f = frame->nativePtr();
_vSink->drawPlanarYUVFrame(f->data[0], f->data[1], f->data[2], _vSink->drawPlanarYUVFrame(f->data[0], f->data[1], f->data[2],
f->linesize[0], f->linesize[1], f->linesize[2]); f->linesize[0], f->linesize[1], f->linesize[2]);
++frameCounter;
if(frameCounter == 2398) {
auto end = std::chrono::system_clock::now();
std::chrono::duration<double> elapsed_seconds = end-start;
std::cout << "Elapsed time: " << elapsed_seconds.count() << std::endl;
}
lock.unlock(); lock.unlock();
} else { } else {
std::cout << "=============== skip video frame" << std::endl; std::cout << "=============== skip video frame" << std::endl;
} }
std::this_thread::sleep_for(std::chrono::milliseconds(1000/fps)); std::this_thread::sleep_for(std::chrono::microseconds (static_cast<int64_t>(1000000/fps)));
} }
} }
@ -159,16 +194,20 @@ namespace ffcpp {
std::this_thread::sleep_for(std::chrono::milliseconds(10)); std::this_thread::sleep_for(std::chrono::milliseconds(10));
} }
// uint64_t curTime = std::chrono::system_clock::now().time_since_epoch().count(); // TODO: Implement correct behaviour for planar audio
// std::cout << "fill samples buffer: " << length << ", " << (curTime - time) << std::endl;
// time = curTime;
AVFrame* f = frame->nativePtr(); AVFrame* f = frame->nativePtr();
int frameSize = frame->size(); int frameSize = frame->size();
// std::cout << "Samples: " << f->nb_samples << std::endl;
// std::cout << "Channels: " << f->channels << std::endl;
// std::cout << "Bytes per sample: " << av_get_bytes_per_sample(_aStream->codec()->sampleFormat()) << std::endl;
// std::cout << "Linesize[0]: " << f->linesize[0] << std::endl;
// std::cout << "Linesize[1]: " << f->linesize[1] << std::endl;
// std::cout << "Frame size: " << frameSize << std::endl;
if(copied + frameSize > length) { if(copied + frameSize > length) {
memcpy(data + copied, f->data[0], length - copied); memcpy(data + copied, f->data[0], length - copied);
memcpy(_aSamplesBuffer.get(), f->data + length - copied, frameSize - length + copied); memcpy(_aSamplesBuffer.get(), f->data[0] + length - copied, frameSize - length + copied);
_samplesInBuffer = frameSize - length + copied; _samplesInBuffer = frameSize - length + copied;
copied = length; copied = length;
} else { } else {

View File

@ -7,7 +7,7 @@ namespace ffcpp {
} }
Stream::Stream(AVStream *stream): _stream(stream) { Stream::Stream(AVStream *stream): _stream(stream) {
_codec = std::make_shared<Codec>(_stream->codecpar->codec_id, CodecType::Decoder); _codec = std::make_shared<Codec>(_stream->codecpar->codec_id, CodecType::Decoder, _stream->codecpar);
} }
Stream::Stream(AVStream *stream, CodecPtr codec): _stream(stream), _codec(codec) { Stream::Stream(AVStream *stream, CodecPtr codec): _stream(stream), _codec(codec) {
@ -29,8 +29,8 @@ namespace ffcpp {
_stream->time_base = timeBase; _stream->time_base = timeBase;
} }
int Stream::fps() const { float Stream::fps() const {
return _stream->avg_frame_rate.num/_stream->avg_frame_rate.den; return 1.0*_stream->avg_frame_rate.num/_stream->avg_frame_rate.den;
} }
Stream::Stream(Stream&& stream) noexcept { Stream::Stream(Stream&& stream) noexcept {

View File

@ -8,7 +8,6 @@ extern "C" {
namespace ffcpp { namespace ffcpp {
void init() { void init() {
av_register_all();
} }
void throwIfError(int result, const std::string& description) { void throwIfError(int result, const std::string& description) {