diff --git a/CMakeLists.txt b/CMakeLists.txt index efcd09d..cc4360a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,12 +15,12 @@ file(GLOB_RECURSE tests ${PROJECT_SOURCE_DIR}/test/*.cc) set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake_modules" ${CMAKE_MODULE_PATH}) include(FetchContent) - include(spdlog) include(gtest) enable_testing() IF(UNIX) + include_directories(/usr/include/x86_64-linux-gnu) find_package(SFML 2.5 COMPONENTS system window graphics network audio REQUIRED) add_executable(${PROJECT_N} main.cc @@ -32,7 +32,10 @@ IF(UNIX) sfml-graphics sfml-network sfml-audio - + avcodec + avformat + vorbis + avutil spdlog ) diff --git a/img/ocean.mp4 b/img/ocean.mp4 new file mode 100755 index 0000000..8a0c3aa Binary files /dev/null and b/img/ocean.mp4 differ diff --git a/include/mediaService.h b/include/mediaService.h index 4a4b3fa..474ff37 100644 --- a/include/mediaService.h +++ b/include/mediaService.h @@ -3,11 +3,13 @@ #include #include #include +#include "videoService.h" class MediaService { private: ImageService *imageService = nullptr; + VideoService *videoService = nullptr; MediaType type; std::shared_ptr texture; std::shared_ptr sprite; @@ -16,7 +18,10 @@ private: int client_height = 0; public: MediaService(const std::string &filename, int width, int height); - ~MediaService() = default; + ~MediaService(){ + delete imageService; + delete videoService; + } std::shared_ptr GetSprite() { return sprite; diff --git a/include/thread_queue.h b/include/thread_queue.h index 205b7a1..8c360b9 100644 --- a/include/thread_queue.h +++ b/include/thread_queue.h @@ -9,7 +9,8 @@ public: ~ThreadQueue() = default; void push(const T& value) { - std::lock_guard lock(mutex_); + std::unique_lock lock(mutex_); + condition_.wait(lock, [this](){ return queue_.size() <= maxSize; }); queue_.push(value); condition_.notify_one(); } @@ -19,6 +20,7 @@ public: condition_.wait(lock, [this] { return !queue_.empty(); }); T value = queue_.front(); queue_.pop(); + condition_.notify_one(); return value; } @@ -35,4 +37,5 @@ private: std::queue queue_; mutable std::mutex mutex_; std::condition_variable condition_; + unsigned int maxSize = 50; }; diff --git a/include/videoService.h b/include/videoService.h index 35d56f1..bcc8335 100644 --- a/include/videoService.h +++ b/include/videoService.h @@ -1,11 +1,48 @@ #ifndef VIDEOSERVICE_H #define VIDEOSERVICE_H +#include "thread_queue.h" #include +#include #include +#include +extern "C" { +#include +#include +#include +} + +void AVFormatContextDeleter(AVFormatContext *context); + +void AVCodecContextDeleter(AVCodecContext *context); class VideoService { -private: + private: std::shared_ptr texture; + std::unique_ptr> + formatContext; + std::unique_ptr> + codecContext; + ThreadQueue packetQueue; + ThreadQueue frameQueue; + ThreadQueue audioQueue; + std::string_view filename; + std::jthread decodeThread; + int videoStreamIndex; + int audioStreamIndex; + int waitDelay = 50; + unsigned int width; + unsigned int height; + std::stop_source stopSource; + + public: + VideoService(std::string_view filename); + ~VideoService() { + if (decodeThread.joinable()) { + decodeThread.join(); + } + } + void InitContext(); + void Decode(); }; #endif diff --git a/main.cc b/main.cc index 740a3e7..f2d6b23 100644 --- a/main.cc +++ b/main.cc @@ -16,7 +16,7 @@ constexpr int CLIENT_HEIGHT = 600; int main(int argc, char** argv){ spdlog::info("Current WorkDir Is: {}",argv[0]); #ifdef DEBUG - argv[1] = R"(../img/ocean.jpg)"; + argv[1] = R"(../img/ocean.mp4)"; #else if(argc != 2){ spdlog::error("Usage: mp filename "); diff --git a/src/mediaService.cc b/src/mediaService.cc index 753851f..be6821f 100644 --- a/src/mediaService.cc +++ b/src/mediaService.cc @@ -18,6 +18,8 @@ MediaService::MediaService(const std::string& filename, int width, int height){ sprite->setScale(scale, scale); break; case MediaType::VIDEO: + videoService = new VideoService(filename); + videoService->Decode(); break; default: @@ -37,6 +39,8 @@ void MediaService::Play(){ window->draw(*sprite); window->display(); break; + case MediaType::VIDEO: + break; default: break; diff --git a/src/videoService.cc b/src/videoService.cc index 648ebae..51c89d7 100644 --- a/src/videoService.cc +++ b/src/videoService.cc @@ -1 +1,70 @@ #include "videoService.h" + +void AVFormatContextDeleter(AVFormatContext *context) { + if (context) { + avformat_close_input(&context); + } +} + +void AVCodecContextDeleter(AVCodecContext *context) { + if (context) { + avcodec_free_context(&context); + } +} + +VideoService::VideoService(std::string_view filename) { + this->filename = filename; + formatContext = + std::unique_ptr( + nullptr, AVFormatContextDeleter); + codecContext = + std::unique_ptr( + nullptr, AVCodecContextDeleter); + InitContext(); +} + +void VideoService::InitContext() { + AVFormatContext *formatContext = nullptr; + AVCodecContext *codecContext = nullptr; + auto ret = avformat_open_input(&formatContext, filename.data(), NULL, NULL); + avformat_find_stream_info(formatContext, nullptr); + for (int i = 0; i < formatContext->nb_streams; i++) { + const auto stream = formatContext->streams[i]; + const auto codec = avcodec_find_decoder(stream->codecpar->codec_id); + if (codec->type == AVMEDIA_TYPE_VIDEO) { + videoStreamIndex = i; + codecContext = avcodec_alloc_context3(codec); + this->codecContext.reset(codecContext); + avcodec_parameters_to_context(codecContext, stream->codecpar); + avcodec_open2(codecContext, codec, nullptr); + this->height = codecContext->height; + this->width = codecContext->width; + } else if (codec->type == AVMEDIA_TYPE_AUDIO) { + audioStreamIndex = i; + } + } + this->formatContext.reset(formatContext); +} + +void VideoService::Decode() { + const auto &fmtCtx = formatContext.get(); + AVPacket *packet = av_packet_alloc(); + decodeThread = std::jthread([this, fmtCtx, packet]() { + while (!stopSource.get_token().stop_requested()) { + if (auto ret = av_read_frame(fmtCtx, packet); ret == 0) { + if (packet->stream_index == this->videoStreamIndex) { + auto temp = av_packet_alloc(); + av_packet_ref(temp, packet); + this->packetQueue.push(temp); + } else if (packet->stream_index == this->audioStreamIndex) { + auto temp = av_packet_alloc(); + av_packet_ref(temp, packet); + this->packetQueue.push(temp); + } + } else if (ret == AVERROR_EOF) { + return; + } + av_packet_unref(packet); + } + }); +}