Player now uses queue to buffering decoded frames

This commit is contained in:
selim mustafaev 2017-01-07 23:47:59 +03:00
parent 23fd1db633
commit 36d3ab2a6e
12 changed files with 155 additions and 44 deletions

View File

@ -4,8 +4,7 @@
#include <SDL_thread.h>
#include <iostream>
#include <chrono>
#include <thread>
#include <future>
namespace ff = ffcpp;
@ -23,6 +22,7 @@ private:
SDLWindowPtr _wnd;
SDLRendererPtr _renderer;
SDLTexturePtr _texture;
std::packaged_task<void()> _renderTask;
public:
SDLWindow(): _wnd(nullptr, SDL_DestroyWindow), _renderer(nullptr, SDL_DestroyRenderer), _texture(nullptr, SDL_DestroyTexture) {
@ -39,19 +39,27 @@ public:
if(!_texture) throw std::runtime_error("Error creating SDL texture");
}
void handleEvents() {
SDL_Event event;
while(true) {
SDL_WaitEvent(&event);
switch(event.type) {
case SDL_QUIT:
return;
case SDL_USEREVENT: {
_renderTask();
break;
}
}
}
}
public:
virtual AVPixelFormat getPixelFormat() const noexcept override {
return AV_PIX_FMT_YUV420P;
}
virtual int getWidth() const noexcept override {
return WINDOW_WIDTH;
}
virtual int getHeight() const noexcept override {
return WINDOW_HEIGHT;
}
virtual void drawFrame(void* pixelsData, int pitch) override {
std::cout << "drawing frame" << std::endl;
SDL_UpdateTexture(_texture.get(), nullptr, pixelsData, pitch);
@ -61,19 +69,31 @@ public:
}
virtual void drawPlanarYUVFrame(const void *yPlane, const void *uPlane, const void *vPlane, int yPitch, int uPitch, int vPitch) override {
_renderTask = std::packaged_task<void()>([=]{
SDL_UpdateYUVTexture(_texture.get(), nullptr, (const uint8_t*)yPlane, yPitch, (const uint8_t*)uPlane, uPitch, (const uint8_t*)vPlane, vPitch);
SDL_RenderClear(_renderer.get());
SDL_RenderCopy(_renderer.get(), _texture.get(), nullptr, nullptr);
SDL_RenderPresent(_renderer.get());
std::this_thread::sleep_for(std::chrono::milliseconds(40));
});
auto future = _renderTask.get_future();
SDL_Event event;
event.type = SDL_USEREVENT;
int res = SDL_PushEvent(&event);
future.get();
}
};
int main(int argc, char** argv) {
auto wnd = std::make_shared<SDLWindow>();
ff::Player player(wnd);
player.setMedia(argv[1]);
player.setVideoSize(WINDOW_WIDTH, WINDOW_HEIGHT);
player.play();
wnd->handleEvents();
return 0;
}

View File

@ -48,7 +48,7 @@ namespace ffcpp {
void setHeight(int height);
void setPixelFormat(AVPixelFormat pixelFormat);
Frame decode(Packet& packet);
FramePtr decode(Packet& packet);
Packet encode(AVFrame* frame);
Frame createAudioFrame() const;

View File

@ -6,8 +6,12 @@ extern "C" {
#include <libavutil/imgutils.h>
}
#include <memory>
namespace ffcpp {
typedef std::shared_ptr<class Frame> FramePtr;
class Frame {
private:
uint8_t* _buffer;
@ -23,6 +27,7 @@ namespace ffcpp {
Frame& operator=(Frame&& frame);
operator AVFrame*();
operator const AVFrame*() const;
AVFrame* nativePtr();
void guessPts();
void setPictureType(AVPictureType type);

View File

@ -2,21 +2,24 @@
#define PROJECT_PLAYER_H
#include "ffcpp/MediaFile.h"
#include "ffcpp/Scaler.h"
#include "TSQueue.h"
#include <memory>
#include <thread>
#include <condition_variable>
#include <mutex>
namespace ffcpp {
struct IVideoSink {
virtual AVPixelFormat getPixelFormat() const noexcept = 0;
virtual int getWidth() const noexcept = 0;
virtual int getHeight() const noexcept = 0;
virtual void drawFrame(void* pixelsData, int pitch) = 0;
virtual void drawPlanarYUVFrame(const void *yPlane, const void *uPlane, const void *vPlane, int yPitch,
int uPitch, int vPitch) = 0;
};
enum class PlayerState {
Shutdown,
Stopped,
Playing,
Paused
@ -28,18 +31,26 @@ namespace ffcpp {
std::unique_ptr<MediaFile> _curMedia;
StreamPtr _aStream;
StreamPtr _vStream;
std::thread _decodeThread;
ScalerPtr _scaler;
PlayerState _state;
TSQueue<Frame> _decodedFrames;
std::thread _decodeThread;
std::thread _vPlayThread;
std::mutex _mutex;
std::condition_variable _stateCond;
public:
Player(std::shared_ptr<IVideoSink> vSink);
~Player();
void setMedia(std::string path);
void setVideoSize(size_t width, size_t height);
void play();
private:
void decode();
void displayFrames();
};
}

View File

@ -10,6 +10,8 @@ extern "C" {
namespace ffcpp {
typedef std::shared_ptr<class Scaler> ScalerPtr;
class Scaler {
private:
SwsContext* _swsContext;
@ -20,7 +22,7 @@ namespace ffcpp {
public:
Scaler(int srcWidth, int srcHeight, AVPixelFormat srcPixFmt, int dstWidth, int dstHeight, AVPixelFormat dstPixFmt);
Scaler(CodecPtr decoder, CodecPtr encoder);
Frame scale(Frame& inFrame);
FramePtr scale(FramePtr inFrame);
static bool needScaling(CodecPtr decoder, CodecPtr encoder);
};

View File

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

View File

@ -45,11 +45,22 @@ namespace ffcpp {
_readCond.notify_one();
}
void pushOrWait(std::shared_ptr<T> value) {
std::unique_lock<std::mutex> lock(_mutex);
if(_queue.size() == _maxSize) {
_writeCond.wait(lock, [this]{ return _queue.size() < _maxSize; });
}
_queue.push(value);
_readCond.notify_one();
}
std::shared_ptr<T> waitAndPop() {
std::unique_lock<std::mutex> lock(_mutex);
_readCond.wait(lock, [this]{ return !_queue.empty(); });
auto res = _queue.front();
_queue.pop();
_writeCond.notify_one();
return res;
}
@ -59,6 +70,7 @@ namespace ffcpp {
return std::shared_ptr<T>();
auto res = _queue.front();
_queue.pop();
_writeCond.notify_one();
return res;
}
@ -69,6 +81,7 @@ namespace ffcpp {
}
auto res = _queue.front();
_queue.pop();
_writeCond.notify_one();
return res;
}
};

View File

@ -103,18 +103,18 @@ namespace ffcpp {
return *this;
}
Frame Codec::decode(Packet &packet) {
Frame frame;
FramePtr Codec::decode(Packet &packet) {
FramePtr frame = std::make_shared<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);
int res = decFunc(_codecCtx, frame->nativePtr(), &gotPicture, packet);
if(res < 0) throw std::runtime_error("cannot decode packet");
}
if(_codecCtx->codec_type == AVMEDIA_TYPE_VIDEO) {
frame.guessPts();
frame->guessPts();
}
return frame;

View File

@ -58,6 +58,10 @@ namespace ffcpp {
return _frame;
}
AVFrame* Frame::nativePtr() {
return _frame;
}
void Frame::guessPts() {
_frame->pts = av_frame_get_best_effort_timestamp(_frame);
}

View File

@ -2,6 +2,7 @@
#include "ffcpp/Stream.h"
#include "ffcpp/Scaler.h"
#include <iostream>
#include <chrono>
namespace ffcpp {
@ -9,46 +10,96 @@ namespace ffcpp {
_curMedia(nullptr),
_aStream(nullptr),
_vStream(nullptr),
_state(PlayerState::Stopped),
_decodeThread(&Player::decode, this),
_state(PlayerState::Stopped)
_vPlayThread(&Player::displayFrames, this),
_decodedFrames(10)
{
init();
}
Player::~Player() {
// _state = PlayerState::Shutdown;
// std::cout << "destructor" << std::endl;
// _stateCond.notify_all();
//
// if(_decodeThread.joinable()) {
// _decodeThread.join();
// }
//
// if(_vPlayThread.joinable()) {
// _vPlayThread.join();
// }
}
void Player::setMedia(std::string path) {
std::lock_guard<std::mutex> lock(_mutex);
_curMedia = std::make_unique<MediaFile>(path, Mode::Read);
_vStream = _curMedia->videoStream();
_aStream = _curMedia->audioStream();
}
void Player::setVideoSize(size_t width, size_t height) {
std::lock_guard<std::mutex> lock(_mutex);
_scaler = std::make_shared<Scaler>(_vStream->codec()->width(), _vStream->codec()->height(), _vStream->codec()->pixelFormat(),
width, height, _vSink->getPixelFormat());
}
void Player::play() {
std::lock_guard<std::mutex> lock(_mutex);
if(!_curMedia)
return;
auto vDecoder = _vStream->codec();
auto aDecoder = _aStream->codec();
Scaler scaler(vDecoder->width(), vDecoder->height(), vDecoder->pixelFormat(),
_vSink->getWidth(), _vSink->getHeight(), _vSink->getPixelFormat());
while(auto packet = _curMedia->readPacket()) {
AVMediaType packetType = _curMedia->packetType(packet);
if(packetType == AVMEDIA_TYPE_VIDEO) {
auto frame = vDecoder->decode(packet);
frame = scaler.scale(frame);
AVFrame* f = frame;
//_vSink->drawFrame(f->data, f->linesize[0]);
_vSink->drawPlanarYUVFrame(f->data[0], f->data[1], f->data[2],
f->linesize[0], f->linesize[1], f->linesize[2]);
}
}
_state = PlayerState::Playing;
_stateCond.notify_all();
}
void Player::decode() {
std::cout << "decode function started" << std::endl;
Packet packet;
while(true) {
std::unique_lock<std::mutex> lock(_mutex);
if(_state == PlayerState::Shutdown)
break;
packet = _curMedia ? _curMedia->readPacket() : Packet();
if(!packet) {
_stateCond.wait(lock, [this]{ return _state == PlayerState::Playing || _state == PlayerState::Shutdown; });
continue;
}
AVMediaType packetType = _curMedia->packetType(packet);
if(packetType == AVMEDIA_TYPE_VIDEO) {
auto frame = _vStream->codec()->decode(packet);
frame = _scaler->scale(frame);
lock.unlock();
_decodedFrames.pushOrWait(frame);
}
}
}
void Player::displayFrames() {
while(true) {
std::unique_lock<std::mutex> lock(_mutex);
if(_state == PlayerState::Shutdown)
break;
if(_state != PlayerState::Playing) {
_stateCond.wait(lock, [this]{ return _state == PlayerState::Playing || _state == PlayerState::Shutdown; });
continue;
}
lock.unlock();
auto frame = _decodedFrames.popOrWait();
lock.lock();
AVFrame* f = frame->nativePtr();
_vSink->drawPlanarYUVFrame(f->data[0], f->data[1], f->data[2],
f->linesize[0], f->linesize[1], f->linesize[2]);
int fps = _vStream->fps();
lock.unlock();
std::this_thread::sleep_for(std::chrono::milliseconds(1000/fps));
}
}
}

View File

@ -21,11 +21,11 @@ namespace ffcpp {
}
Frame Scaler::scale(Frame &inFrame) {
Frame outFrame(_dstWidth, _dstHeight, _dstPixFmt);
FramePtr Scaler::scale(FramePtr inFrame) {
FramePtr outFrame = std::make_shared<Frame>(_dstWidth, _dstHeight, _dstPixFmt);
AVFrame* fin = inFrame;
AVFrame* fout = outFrame;
AVFrame* fin = inFrame->nativePtr();
AVFrame* fout = outFrame->nativePtr();
fout->pts = fin->pts;
int res = sws_scale(_swsContext, (uint8_t const * const *)fin->data, fin->linesize, 0, fin->height, fout->data, fout->linesize);

View File

@ -30,6 +30,10 @@ namespace ffcpp {
_stream->time_base = timeBase;
}
int Stream::fps() const {
return _stream->avg_frame_rate.num/_stream->avg_frame_rate.den;
}
Stream::Stream(Stream&& stream) noexcept {
*this = std::move(stream);
}