ffmepg 导入, 视频解码初始化逻辑, 视频解码线程逻辑

This commit is contained in:
Jie 2024-09-30 16:11:07 +08:00
parent 1237b164bf
commit 15018f4079
8 changed files with 127 additions and 6 deletions

View File

@ -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}) set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake_modules" ${CMAKE_MODULE_PATH})
include(FetchContent) include(FetchContent)
include(spdlog) include(spdlog)
include(gtest) include(gtest)
enable_testing() enable_testing()
IF(UNIX) IF(UNIX)
include_directories(/usr/include/x86_64-linux-gnu)
find_package(SFML 2.5 COMPONENTS system window graphics network audio REQUIRED) find_package(SFML 2.5 COMPONENTS system window graphics network audio REQUIRED)
add_executable(${PROJECT_N} add_executable(${PROJECT_N}
main.cc main.cc
@ -32,7 +32,10 @@ IF(UNIX)
sfml-graphics sfml-graphics
sfml-network sfml-network
sfml-audio sfml-audio
avcodec
avformat
vorbis
avutil
spdlog spdlog
) )

BIN
img/ocean.mp4 Executable file

Binary file not shown.

View File

@ -3,11 +3,13 @@
#include <string> #include <string>
#include <SFML/Graphics.hpp> #include <SFML/Graphics.hpp>
#include <memory> #include <memory>
#include "videoService.h"
class MediaService class MediaService
{ {
private: private:
ImageService *imageService = nullptr; ImageService *imageService = nullptr;
VideoService *videoService = nullptr;
MediaType type; MediaType type;
std::shared_ptr<sf::Texture> texture; std::shared_ptr<sf::Texture> texture;
std::shared_ptr<sf::Sprite> sprite; std::shared_ptr<sf::Sprite> sprite;
@ -16,7 +18,10 @@ private:
int client_height = 0; int client_height = 0;
public: public:
MediaService(const std::string &filename, int width, int height); MediaService(const std::string &filename, int width, int height);
~MediaService() = default; ~MediaService(){
delete imageService;
delete videoService;
}
std::shared_ptr<sf::Sprite> GetSprite() std::shared_ptr<sf::Sprite> GetSprite()
{ {
return sprite; return sprite;

View File

@ -9,7 +9,8 @@ public:
~ThreadQueue() = default; ~ThreadQueue() = default;
void push(const T& value) { void push(const T& value) {
std::lock_guard<std::mutex> lock(mutex_); std::unique_lock<std::mutex> lock(mutex_);
condition_.wait(lock, [this](){ return queue_.size() <= maxSize; });
queue_.push(value); queue_.push(value);
condition_.notify_one(); condition_.notify_one();
} }
@ -19,6 +20,7 @@ public:
condition_.wait(lock, [this] { return !queue_.empty(); }); condition_.wait(lock, [this] { return !queue_.empty(); });
T value = queue_.front(); T value = queue_.front();
queue_.pop(); queue_.pop();
condition_.notify_one();
return value; return value;
} }
@ -35,4 +37,5 @@ private:
std::queue<T> queue_; std::queue<T> queue_;
mutable std::mutex mutex_; mutable std::mutex mutex_;
std::condition_variable condition_; std::condition_variable condition_;
unsigned int maxSize = 50;
}; };

View File

@ -1,11 +1,48 @@
#ifndef VIDEOSERVICE_H #ifndef VIDEOSERVICE_H
#define VIDEOSERVICE_H #define VIDEOSERVICE_H
#include "thread_queue.h"
#include <SFML/Graphics.hpp> #include <SFML/Graphics.hpp>
#include <functional>
#include <memory> #include <memory>
#include <thread>
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/avutil.h>
}
void AVFormatContextDeleter(AVFormatContext *context);
void AVCodecContextDeleter(AVCodecContext *context);
class VideoService { class VideoService {
private: private:
std::shared_ptr<sf::Texture> texture; std::shared_ptr<sf::Texture> texture;
std::unique_ptr<AVFormatContext, std::function<void(AVFormatContext *)>>
formatContext;
std::unique_ptr<AVCodecContext, std::function<void(AVCodecContext *)>>
codecContext;
ThreadQueue<AVPacket*> packetQueue;
ThreadQueue<AVFrame*> frameQueue;
ThreadQueue<AVPacket*> 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 #endif

View File

@ -16,7 +16,7 @@ constexpr int CLIENT_HEIGHT = 600;
int main(int argc, char** argv){ int main(int argc, char** argv){
spdlog::info("Current WorkDir Is: {}",argv[0]); spdlog::info("Current WorkDir Is: {}",argv[0]);
#ifdef DEBUG #ifdef DEBUG
argv[1] = R"(../img/ocean.jpg)"; argv[1] = R"(../img/ocean.mp4)";
#else #else
if(argc != 2){ if(argc != 2){
spdlog::error("Usage: mp filename "); spdlog::error("Usage: mp filename ");

View File

@ -18,6 +18,8 @@ MediaService::MediaService(const std::string& filename, int width, int height){
sprite->setScale(scale, scale); sprite->setScale(scale, scale);
break; break;
case MediaType::VIDEO: case MediaType::VIDEO:
videoService = new VideoService(filename);
videoService->Decode();
break; break;
default: default:
@ -37,6 +39,8 @@ void MediaService::Play(){
window->draw(*sprite); window->draw(*sprite);
window->display(); window->display();
break; break;
case MediaType::VIDEO:
break;
default: default:
break; break;

View File

@ -1 +1,70 @@
#include "videoService.h" #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<AVFormatContext, decltype(&AVFormatContextDeleter)>(
nullptr, AVFormatContextDeleter);
codecContext =
std::unique_ptr<AVCodecContext, decltype(&AVCodecContextDeleter)>(
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);
}
});
}