Player now uses queue to buffering decoded frames
This commit is contained in:
parent
23fd1db633
commit
36d3ab2a6e
@ -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 {
|
||||
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));
|
||||
_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());
|
||||
});
|
||||
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;
|
||||
}
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@ -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);
|
||||
};
|
||||
|
||||
|
||||
@ -27,6 +27,7 @@ namespace ffcpp {
|
||||
|
||||
AVRational timeBase() const;
|
||||
void setTimeBase(AVRational timeBase);
|
||||
int fps() const;
|
||||
|
||||
public:
|
||||
Stream(Stream&& stream) noexcept;
|
||||
|
||||
@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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();
|
||||
_state = PlayerState::Playing;
|
||||
_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);
|
||||
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]);
|
||||
auto frame = _vStream->codec()->decode(packet);
|
||||
frame = _scaler->scale(frame);
|
||||
lock.unlock();
|
||||
_decodedFrames.pushOrWait(frame);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Player::decode() {
|
||||
std::cout << "decode function started" << std::endl;
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user