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 <SDL_thread.h>
#include <iostream> #include <iostream>
#include <chrono> #include <future>
#include <thread>
namespace ff = ffcpp; namespace ff = ffcpp;
@ -23,6 +22,7 @@ private:
SDLWindowPtr _wnd; SDLWindowPtr _wnd;
SDLRendererPtr _renderer; SDLRendererPtr _renderer;
SDLTexturePtr _texture; SDLTexturePtr _texture;
std::packaged_task<void()> _renderTask;
public: public:
SDLWindow(): _wnd(nullptr, SDL_DestroyWindow), _renderer(nullptr, SDL_DestroyRenderer), _texture(nullptr, SDL_DestroyTexture) { 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"); 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: public:
virtual AVPixelFormat getPixelFormat() const noexcept override { virtual AVPixelFormat getPixelFormat() const noexcept override {
return AV_PIX_FMT_YUV420P; 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 { virtual void drawFrame(void* pixelsData, int pitch) override {
std::cout << "drawing frame" << std::endl; std::cout << "drawing frame" << std::endl;
SDL_UpdateTexture(_texture.get(), nullptr, pixelsData, pitch); 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 { virtual void drawPlanarYUVFrame(const void *yPlane, const void *uPlane, const void *vPlane, int yPitch, int uPitch, int vPitch) override {
SDL_UpdateYUVTexture(_texture.get(), nullptr, (const uint8_t*)yPlane, yPitch, (const uint8_t*)uPlane, uPitch, (const uint8_t*)vPlane, vPitch); _renderTask = std::packaged_task<void()>([=]{
SDL_RenderClear(_renderer.get()); SDL_UpdateYUVTexture(_texture.get(), nullptr, (const uint8_t*)yPlane, yPitch, (const uint8_t*)uPlane, uPitch, (const uint8_t*)vPlane, vPitch);
SDL_RenderCopy(_renderer.get(), _texture.get(), nullptr, nullptr); SDL_RenderClear(_renderer.get());
SDL_RenderPresent(_renderer.get()); SDL_RenderCopy(_renderer.get(), _texture.get(), nullptr, nullptr);
std::this_thread::sleep_for(std::chrono::milliseconds(40)); SDL_RenderPresent(_renderer.get());
});
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) { int main(int argc, char** argv) {
auto wnd = std::make_shared<SDLWindow>(); auto wnd = std::make_shared<SDLWindow>();
ff::Player player(wnd); ff::Player player(wnd);
player.setMedia(argv[1]); player.setMedia(argv[1]);
player.setVideoSize(WINDOW_WIDTH, WINDOW_HEIGHT);
player.play(); player.play();
wnd->handleEvents();
return 0; return 0;
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -2,6 +2,7 @@
#include "ffcpp/Stream.h" #include "ffcpp/Stream.h"
#include "ffcpp/Scaler.h" #include "ffcpp/Scaler.h"
#include <iostream> #include <iostream>
#include <chrono>
namespace ffcpp { namespace ffcpp {
@ -9,46 +10,96 @@ namespace ffcpp {
_curMedia(nullptr), _curMedia(nullptr),
_aStream(nullptr), _aStream(nullptr),
_vStream(nullptr), _vStream(nullptr),
_state(PlayerState::Stopped),
_decodeThread(&Player::decode, this), _decodeThread(&Player::decode, this),
_state(PlayerState::Stopped) _vPlayThread(&Player::displayFrames, this),
_decodedFrames(10)
{ {
init(); init();
} }
Player::~Player() { 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) { void Player::setMedia(std::string path) {
std::lock_guard<std::mutex> lock(_mutex);
_curMedia = std::make_unique<MediaFile>(path, Mode::Read); _curMedia = std::make_unique<MediaFile>(path, Mode::Read);
_vStream = _curMedia->videoStream(); _vStream = _curMedia->videoStream();
_aStream = _curMedia->audioStream(); _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() { void Player::play() {
std::lock_guard<std::mutex> lock(_mutex);
if(!_curMedia) if(!_curMedia)
return; return;
auto vDecoder = _vStream->codec(); _state = PlayerState::Playing;
auto aDecoder = _aStream->codec(); _stateCond.notify_all();
}
void Player::decode() {
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;
}
Scaler scaler(vDecoder->width(), vDecoder->height(), vDecoder->pixelFormat(),
_vSink->getWidth(), _vSink->getHeight(), _vSink->getPixelFormat());
while(auto packet = _curMedia->readPacket()) {
AVMediaType packetType = _curMedia->packetType(packet); AVMediaType packetType = _curMedia->packetType(packet);
if(packetType == AVMEDIA_TYPE_VIDEO) { if(packetType == AVMEDIA_TYPE_VIDEO) {
auto frame = vDecoder->decode(packet); auto frame = _vStream->codec()->decode(packet);
frame = scaler.scale(frame); frame = _scaler->scale(frame);
AVFrame* f = frame; lock.unlock();
//_vSink->drawFrame(f->data, f->linesize[0]); _decodedFrames.pushOrWait(frame);
_vSink->drawPlanarYUVFrame(f->data[0], f->data[1], f->data[2],
f->linesize[0], f->linesize[1], f->linesize[2]);
} }
} }
} }
void Player::decode() { void Player::displayFrames() {
std::cout << "decode function started" << std::endl; 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) { FramePtr Scaler::scale(FramePtr inFrame) {
Frame outFrame(_dstWidth, _dstHeight, _dstPixFmt); FramePtr outFrame = std::make_shared<Frame>(_dstWidth, _dstHeight, _dstPixFmt);
AVFrame* fin = inFrame; AVFrame* fin = inFrame->nativePtr();
AVFrame* fout = outFrame; AVFrame* fout = outFrame->nativePtr();
fout->pts = fin->pts; fout->pts = fin->pts;
int res = sws_scale(_swsContext, (uint8_t const * const *)fin->data, fin->linesize, 0, fin->height, fout->data, fout->linesize); 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; _stream->time_base = timeBase;
} }
int Stream::fps() const {
return _stream->avg_frame_rate.num/_stream->avg_frame_rate.den;
}
Stream::Stream(Stream&& stream) noexcept { Stream::Stream(Stream&& stream) noexcept {
*this = std::move(stream); *this = std::move(stream);
} }