ffmepg 导入, 视频解码初始化逻辑, 视频解码线程逻辑
This commit is contained in:
parent
1237b164bf
commit
15018f4079
@ -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
BIN
img/ocean.mp4
Executable file
Binary file not shown.
@ -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;
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
|
@ -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
|
||||||
|
2
main.cc
2
main.cc
@ -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 ");
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user