#include "ffcpp/Player.h" #include #include #include #include #include namespace ff = ffcpp; #define WINDOW_WIDTH 1280 #define WINDOW_HEIGHT 720 class SDLWindow: public ff::IVideoSink, public ff::IAudioSink { private: template using SDLUniquePtr = std::unique_ptr; using SDLWindowPtr = SDLUniquePtr; using SDLRendererPtr = SDLUniquePtr; using SDLTexturePtr = SDLUniquePtr; private: SDLWindowPtr _wnd; SDLRendererPtr _renderer; SDLTexturePtr _texture; SDL_AudioSpec _audioSpec; SDL_AudioDeviceID _aDevId; std::packaged_task _renderTask; ff::IAudioSource* _audioSrc; public: SDLWindow(): _wnd(nullptr, SDL_DestroyWindow), _renderer(nullptr, SDL_DestroyRenderer), _texture(nullptr, SDL_DestroyTexture), _audioSrc(nullptr) { int res = SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER); if(res < 0) throw std::runtime_error("Error initializing SDL"); _wnd.reset(SDL_CreateWindow("ffPlayer", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_OPENGL)); if(!_wnd) throw std::runtime_error("Error creating SDL window"); _renderer.reset(SDL_CreateRenderer(_wnd.get(), -1, 0)); if(!_renderer) throw std::runtime_error("Error creating SDL renderer"); _texture.reset(SDL_CreateTexture(_renderer.get(), SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, WINDOW_WIDTH, WINDOW_HEIGHT)); if(!_texture) throw std::runtime_error("Error creating SDL texture"); SDL_AudioSpec want; SDL_zero(want); want.freq = 44100; want.format = AUDIO_F32; want.channels = 2; want.samples = 8192; want.callback = SDLWindow::audioCallback; want.userdata = this; _aDevId = SDL_OpenAudioDevice(nullptr, 0, &want, &_audioSpec, SDL_AUDIO_ALLOW_ANY_CHANGE); if(_aDevId == 0) throw std::runtime_error("Error opening audio device"); SDL_PauseAudioDevice(_aDevId, 0); } void handleEvents() { SDL_Event event; while(true) { SDL_WaitEvent(&event); switch(event.type) { case SDL_QUIT: return; case SDL_USEREVENT: { _renderTask(); break; } } } } private: static void audioCallback(void* userdata, Uint8* stream, int len) { ff::IAudioSource* src = static_cast(userdata)->_audioSrc; if(src) { //std::cout << "fill sample buffer" << std::endl; src->fillSampleBuffer(stream, len); } } AVSampleFormat sdlToFFMpeg(SDL_AudioFormat format) { switch (format) { case AUDIO_S16: return AV_SAMPLE_FMT_S16; case AUDIO_S32: return AV_SAMPLE_FMT_S32; case AUDIO_F32: return AV_SAMPLE_FMT_FLT; default: throw std::runtime_error("unknown audio sample format: " + std::to_string(format)); } } // IVideoSink implementation private: virtual AVPixelFormat getPixelFormat() const noexcept override { return AV_PIX_FMT_YUV420P; } virtual void drawFrame(void* pixelsData, int pitch) override { std::cout << "drawing frame" << std::endl; SDL_UpdateTexture(_texture.get(), nullptr, pixelsData, pitch); SDL_RenderClear(_renderer.get()); SDL_RenderCopy(_renderer.get(), _texture.get(), nullptr, nullptr); SDL_RenderPresent(_renderer.get()); } virtual void drawPlanarYUVFrame(const void *yPlane, const void *uPlane, const void *vPlane, int yPitch, int uPitch, int vPitch) override { _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(); } // IAudioSink implementation private: void setAudioSource(ff::IAudioSource* audioSrc) override { std::cout << "set audio source" << std::endl; _audioSrc = audioSrc; } AVSampleFormat getSampleFormat() override { return sdlToFFMpeg(_audioSpec.format); } int getChannelsCount() override { return _audioSpec.channels; } int getSampleRate() override { return _audioSpec.freq; } }; int main(int argc, char** argv) { try { auto wnd = std::make_shared(); ff::Player player(wnd, wnd); player.setMedia(argv[1]); player.setVideoSize(WINDOW_WIDTH, WINDOW_HEIGHT); player.play(); wnd->handleEvents(); } catch (...) { std::cout << "exception" << std::endl; return 0; } return 0; }