diff --git a/examples/ffPlayer.cpp b/examples/ffPlayer.cpp index 16d7d0f..6dde1a8 100644 --- a/examples/ffPlayer.cpp +++ b/examples/ffPlayer.cpp @@ -4,8 +4,7 @@ #include #include -#include -#include +#include namespace ff = ffcpp; @@ -23,6 +22,7 @@ private: SDLWindowPtr _wnd; SDLRendererPtr _renderer; SDLTexturePtr _texture; + std::packaged_task _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([=]{ + 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(); + ff::Player player(wnd); player.setMedia(argv[1]); + player.setVideoSize(WINDOW_WIDTH, WINDOW_HEIGHT); player.play(); + wnd->handleEvents(); + return 0; } \ No newline at end of file diff --git a/include/ffcpp/Codec.h b/include/ffcpp/Codec.h index ea0a0c2..dc3797d 100644 --- a/include/ffcpp/Codec.h +++ b/include/ffcpp/Codec.h @@ -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; diff --git a/include/ffcpp/Frame.h b/include/ffcpp/Frame.h index 1de8b31..c59343d 100644 --- a/include/ffcpp/Frame.h +++ b/include/ffcpp/Frame.h @@ -6,8 +6,12 @@ extern "C" { #include } +#include + namespace ffcpp { + typedef std::shared_ptr 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); diff --git a/include/ffcpp/Player.h b/include/ffcpp/Player.h index ee545d6..ca85cf8 100644 --- a/include/ffcpp/Player.h +++ b/include/ffcpp/Player.h @@ -2,21 +2,24 @@ #define PROJECT_PLAYER_H #include "ffcpp/MediaFile.h" +#include "ffcpp/Scaler.h" +#include "TSQueue.h" #include #include +#include +#include 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 _curMedia; StreamPtr _aStream; StreamPtr _vStream; - std::thread _decodeThread; + ScalerPtr _scaler; PlayerState _state; + TSQueue _decodedFrames; + std::thread _decodeThread; + std::thread _vPlayThread; + std::mutex _mutex; + std::condition_variable _stateCond; + public: Player(std::shared_ptr vSink); ~Player(); void setMedia(std::string path); + void setVideoSize(size_t width, size_t height); void play(); private: void decode(); + void displayFrames(); }; } diff --git a/include/ffcpp/Scaler.h b/include/ffcpp/Scaler.h index b9ac918..5ee66e0 100644 --- a/include/ffcpp/Scaler.h +++ b/include/ffcpp/Scaler.h @@ -10,6 +10,8 @@ extern "C" { namespace ffcpp { + typedef std::shared_ptr 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); }; diff --git a/include/ffcpp/Stream.h b/include/ffcpp/Stream.h index eadb80d..ca14e53 100644 --- a/include/ffcpp/Stream.h +++ b/include/ffcpp/Stream.h @@ -27,6 +27,7 @@ namespace ffcpp { AVRational timeBase() const; void setTimeBase(AVRational timeBase); + int fps() const; public: Stream(Stream&& stream) noexcept; diff --git a/include/ffcpp/TSQueue.h b/include/ffcpp/TSQueue.h index 8a34d4c..f7fb1da 100644 --- a/include/ffcpp/TSQueue.h +++ b/include/ffcpp/TSQueue.h @@ -45,11 +45,22 @@ namespace ffcpp { _readCond.notify_one(); } + + void pushOrWait(std::shared_ptr value) { + std::unique_lock lock(_mutex); + if(_queue.size() == _maxSize) { + _writeCond.wait(lock, [this]{ return _queue.size() < _maxSize; }); + } + _queue.push(value); + _readCond.notify_one(); + } + std::shared_ptr waitAndPop() { std::unique_lock 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(); 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; } }; diff --git a/src/Codec.cpp b/src/Codec.cpp index cf3680d..75e7f1e 100644 --- a/src/Codec.cpp +++ b/src/Codec.cpp @@ -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(); 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; diff --git a/src/Frame.cpp b/src/Frame.cpp index c243d2f..fb11fd5 100644 --- a/src/Frame.cpp +++ b/src/Frame.cpp @@ -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); } diff --git a/src/Player.cpp b/src/Player.cpp index 719f4fa..7d54cb8 100644 --- a/src/Player.cpp +++ b/src/Player.cpp @@ -2,6 +2,7 @@ #include "ffcpp/Stream.h" #include "ffcpp/Scaler.h" #include +#include 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 lock(_mutex); _curMedia = std::make_unique(path, Mode::Read); - _vStream = _curMedia->videoStream(); _aStream = _curMedia->audioStream(); } + void Player::setVideoSize(size_t width, size_t height) { + std::lock_guard lock(_mutex); + _scaler = std::make_shared(_vStream->codec()->width(), _vStream->codec()->height(), _vStream->codec()->pixelFormat(), + width, height, _vSink->getPixelFormat()); + } + void Player::play() { + std::lock_guard 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 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 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)); + } } } \ No newline at end of file diff --git a/src/Scaler.cpp b/src/Scaler.cpp index f2d3b0c..fc815e1 100644 --- a/src/Scaler.cpp +++ b/src/Scaler.cpp @@ -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(_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); diff --git a/src/Stream.cpp b/src/Stream.cpp index 734c856..057d140 100644 --- a/src/Stream.cpp +++ b/src/Stream.cpp @@ -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); }