This commit is contained in:
JIe 2024-10-08 14:38:36 +08:00
commit 0f94cbaa9f
11 changed files with 126 additions and 113 deletions

3
.gitignore vendored
View File

@ -1,2 +1,5 @@
/build/ /build/
.cache/ .cache/
/enc_temp_folder
/out/build/x64-Debug
/.vs

33
.vscode/launch.json vendored
View File

@ -1,33 +0,0 @@
{
// 使 IntelliSense
//
// 访: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "(gdb) Launch",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/build/mp",
"args": ["${workspaceFolder}/img/ocean.jpg"],
"stopAtEntry": false,
"cwd": "${fileDirname}",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
},
{
"description": "Set Disassembly Flavor to Intel",
"text": "-gdb-set disassembly-flavor intel",
"ignoreFailures": true
}
]
}
]
}

View File

@ -1,4 +1,5 @@
{ {
"C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools",
"files.associations": { "files.associations": {
"*.cpp": "cpp", "*.cpp": "cpp",
"cctype": "cpp", "cctype": "cpp",

28
.vscode/tasks.json vendored
View File

@ -1,28 +0,0 @@
{
"tasks": [
{
"type": "cppbuild",
"label": "C/C++: g++ build active file",
"command": "/usr/bin/g++",
"args": [
"-fdiagnostics-color=always",
"-g",
"${file}",
"-o",
"${fileDirname}/${fileBasenameNoExtension}"
],
"options": {
"cwd": "${fileDirname}"
},
"problemMatcher": [
"$gcc"
],
"group": {
"kind": "build",
"isDefault": true
},
"detail": "Task generated by Debugger."
}
],
"version": "2.0.0"
}

View File

@ -37,6 +37,7 @@ IF(UNIX)
vorbis vorbis
avutil avutil
spdlog spdlog
swscale
) )
add_executable(${PROJECT_N_T} add_executable(${PROJECT_N_T}

View File

@ -1,13 +1,12 @@
#include <imageService.h>
#include <UtilTool.h>
#include <string>
#include <SFML/Graphics.hpp>
#include <memory>
#include "videoService.h" #include "videoService.h"
#include <SFML/Graphics.hpp>
#include <UtilTool.h>
#include <imageService.h>
#include <memory>
#include <string>
class MediaService class MediaService {
{ private:
private:
ImageService *imageService = nullptr; ImageService *imageService = nullptr;
VideoService *videoService = nullptr; VideoService *videoService = nullptr;
MediaType type; MediaType type;
@ -15,18 +14,15 @@ private:
std::shared_ptr<sf::Sprite> sprite; std::shared_ptr<sf::Sprite> sprite;
std::shared_ptr<sf::RenderWindow> window; std::shared_ptr<sf::RenderWindow> window;
int client_width = 0; int client_width = 0;
int client_height = 0; int client_height = 0;
public:
MediaService(const std::string &filename, int width, int height); public:
~MediaService(){ MediaService(std::shared_ptr<sf::RenderWindow> window,
const std::string &filename, int width, int height);
~MediaService() {
delete imageService; delete imageService;
delete videoService; delete videoService;
} }
std::shared_ptr<sf::Sprite> GetSprite() std::shared_ptr<sf::Sprite> GetSprite() { return sprite; }
{
return sprite;
}
void SetWindow(std::shared_ptr<sf::RenderWindow> window);
void Play(); void Play();
}; };

View File

@ -10,6 +10,8 @@ extern "C" {
#include <libavcodec/avcodec.h> #include <libavcodec/avcodec.h>
#include <libavformat/avformat.h> #include <libavformat/avformat.h>
#include <libavutil/avutil.h> #include <libavutil/avutil.h>
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>
} }
void AVFormatContextDeleter(AVFormatContext *context); void AVFormatContextDeleter(AVFormatContext *context);
@ -17,32 +19,41 @@ void AVFormatContextDeleter(AVFormatContext *context);
void AVCodecContextDeleter(AVCodecContext *context); void AVCodecContextDeleter(AVCodecContext *context);
class VideoService { class VideoService {
private: private:
std::shared_ptr<sf::Texture> texture;
std::unique_ptr<AVFormatContext, std::function<void(AVFormatContext *)>> std::unique_ptr<AVFormatContext, std::function<void(AVFormatContext *)>>
formatContext; formatContext;
std::unique_ptr<AVCodecContext, std::function<void(AVCodecContext *)>> std::unique_ptr<AVCodecContext, std::function<void(AVCodecContext *)>>
codecContext; codecContext;
ThreadQueue<AVPacket*> packetQueue; ThreadQueue<AVPacket *> packetQueue;
ThreadQueue<AVFrame*> frameQueue; ThreadQueue<AVFrame *> frameQueue;
ThreadQueue<AVPacket*> audioQueue; ThreadQueue<AVPacket *> audioQueue;
std::string_view filename; std::string_view filename;
std::jthread decodeThread; std::jthread decodePacketThread;
std::jthread decodeFrameThread;
int videoStreamIndex; int videoStreamIndex;
int audioStreamIndex; int audioStreamIndex;
int waitDelay = 50; int waitDelay = 50;
unsigned int width; unsigned int width;
unsigned int height; unsigned int height;
std::stop_source stopSource; std::stop_source stopSource;
std::shared_ptr<sf::RenderWindow> window;
std::unique_ptr<sf::Sprite> sprite;
std::unique_ptr<sf::Texture> texture;
public: public:
VideoService(std::string_view filename); VideoService(std::string_view filename, std::shared_ptr<sf::RenderWindow> window);
~VideoService() { ~VideoService() {
if (decodeThread.joinable()) { if (decodePacketThread.joinable()) {
decodeThread.join(); decodePacketThread.join();
}
if (decodeFrameThread.joinable()) {
decodeFrameThread.join();
} }
} }
void InitContext(); void InitContext();
void Decode(); void Decode();
void PaintFrame();
void Play();
}; };
#endif #endif

View File

@ -1,8 +1,10 @@
#include <SFML/Graphics/RenderWindow.hpp> #include "UtilTool.h"
#include <cstdio> #include "mediaService.h"
#include <SFML/System.hpp>
#include <SFML/Graphics.hpp> #include <SFML/Graphics.hpp>
#include <SFML/Graphics/RenderWindow.hpp>
#include <SFML/System.hpp>
#include <SFML/Window.hpp> #include <SFML/Window.hpp>
#include <cstdio>
#include <spdlog/spdlog.h> #include <spdlog/spdlog.h>
#include "UtilTool.h" #include "UtilTool.h"
#include "mediaService.h" #include "mediaService.h"

View File

@ -2,13 +2,14 @@
#include <memory> #include <memory>
#include <videoService.h> #include <videoService.h>
MediaService::MediaService(const std::string& filename, int width, int height){ MediaService::MediaService(std::shared_ptr<sf::RenderWindow> window,
const std::string &filename, int width, int height) {
type = UtilTool::CheckFileType(filename); type = UtilTool::CheckFileType(filename);
client_width = width; client_width = width;
client_height = height; client_height = height;
float scale = .0f; float scale = .0f;
switch (type) this->window = window;
{ switch (type) {
case MediaType::IMAGE: case MediaType::IMAGE:
imageService = new ImageService(filename); imageService = new ImageService(filename);
texture = imageService->GetTexture(); texture = imageService->GetTexture();
@ -17,31 +18,29 @@ MediaService::MediaService(const std::string& filename, int width, int height){
scale = imageService->GetScale(client_width, client_height); scale = imageService->GetScale(client_width, client_height);
sprite->setScale(scale, scale); sprite->setScale(scale, scale);
break; break;
case MediaType::VIDEO: case MediaType::VIDEO:
videoService = new VideoService(filename); texture = std::make_shared<sf::Texture>();
sprite = std::make_shared<sf::Sprite>();
videoService = new VideoService(filename, window);
videoService->Decode(); videoService->Decode();
break; break;
default: default:
break; break;
} }
} }
void MediaService::SetWindow(std::shared_ptr<sf::RenderWindow> window){ void MediaService::Play() {
this->window = window; switch (type) {
}
void MediaService::Play(){
switch (type)
{
case MediaType::IMAGE: case MediaType::IMAGE:
window->clear(); window->clear();
window->draw(*sprite); window->draw(*sprite);
window->display(); window->display();
break; break;
case MediaType::VIDEO: case MediaType::VIDEO:
videoService->Play();
break; break;
default: default:
break; break;
} }

View File

@ -1,4 +1,7 @@
#include "videoService.h" #include "videoService.h"
#include <chrono>
#include <spdlog/spdlog.h>
using namespace std::literals::chrono_literals;
void AVFormatContextDeleter(AVFormatContext *context) { void AVFormatContextDeleter(AVFormatContext *context) {
if (context) { if (context) {
@ -12,14 +15,18 @@ void AVCodecContextDeleter(AVCodecContext *context) {
} }
} }
VideoService::VideoService(std::string_view filename) { VideoService::VideoService(std::string_view filename,
std::shared_ptr<sf::RenderWindow> window) {
this->filename = filename; this->filename = filename;
this->window = window;
formatContext = formatContext =
std::unique_ptr<AVFormatContext, decltype(&AVFormatContextDeleter)>( std::unique_ptr<AVFormatContext, decltype(&AVFormatContextDeleter)>(
nullptr, AVFormatContextDeleter); nullptr, AVFormatContextDeleter);
codecContext = codecContext =
std::unique_ptr<AVCodecContext, decltype(&AVCodecContextDeleter)>( std::unique_ptr<AVCodecContext, decltype(&AVCodecContextDeleter)>(
nullptr, AVCodecContextDeleter); nullptr, AVCodecContextDeleter);
sprite = std::make_unique<sf::Sprite>();
texture = std::make_unique<sf::Texture>();
InitContext(); InitContext();
} }
@ -49,17 +56,20 @@ void VideoService::InitContext() {
void VideoService::Decode() { void VideoService::Decode() {
const auto &fmtCtx = formatContext.get(); const auto &fmtCtx = formatContext.get();
AVPacket *packet = av_packet_alloc(); AVPacket *packet = av_packet_alloc();
decodeThread = std::jthread([this, fmtCtx, packet]() { decodePacketThread = std::jthread([this, fmtCtx, packet]() {
while (!stopSource.get_token().stop_requested()) { while (!stopSource.get_token().stop_requested()) {
if (auto ret = av_read_frame(fmtCtx, packet); ret == 0) { if (auto ret = av_read_frame(fmtCtx, packet); ret == 0) {
if (packet->stream_index == this->videoStreamIndex) { if (packet->stream_index == this->videoStreamIndex) {
auto temp = av_packet_alloc(); auto temp = av_packet_alloc();
av_packet_ref(temp, packet); av_packet_ref(temp, packet);
this->packetQueue.push(temp); this->packetQueue.push(temp);
spdlog::info("packetQueueSize: {}", packetQueue.size());
} else if (packet->stream_index == this->audioStreamIndex) { } else if (packet->stream_index == this->audioStreamIndex) {
auto temp = av_packet_alloc(); auto temp = av_packet_alloc();
av_packet_ref(temp, packet); av_packet_ref(temp, packet);
this->packetQueue.push(temp); this->audioQueue.push(temp);
spdlog::info("audioQueueSize: {}", audioQueue.size());
} }
} else if (ret == AVERROR_EOF) { } else if (ret == AVERROR_EOF) {
return; return;
@ -67,4 +77,58 @@ void VideoService::Decode() {
av_packet_unref(packet); av_packet_unref(packet);
} }
}); });
decodeFrameThread = std::jthread([this]() {
while (!stopSource.get_token().stop_requested()) {
auto packet = packetQueue.pop();
auto ret = avcodec_send_packet(codecContext.get(), packet);
if (ret < 0 && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF) {
av_packet_unref(packet);
continue;
}
auto frame = av_frame_alloc();
ret = avcodec_receive_frame(codecContext.get(), frame);
if (ret < 0 && ret != AVERROR_EOF) {
av_packet_unref(packet);
continue;
}
frameQueue.push(frame);
spdlog::info("frameQueueSize: {}", frameQueue.size());
av_packet_unref(packet);
}
});
} }
void VideoService::PaintFrame() {
auto frame = frameQueue.pop();
SwsContext *context = sws_getContext(
frame->width, frame->height, static_cast<AVPixelFormat>(frame->format),
frame->width, frame->height, AV_PIX_FMT_RGBA, SWS_BILINEAR, nullptr,
nullptr, nullptr);
if (!context) {
spdlog::error("Failed to create swsContext");
return;
}
std::vector<uint8_t> buffer(frame->height * frame->width * 4);
AVFrame *rgbaFrame = av_frame_alloc();
av_image_fill_arrays(rgbaFrame->data, rgbaFrame->linesize, buffer.data(),
AV_PIX_FMT_RGBA, frame->width, frame->height, 1);
sws_scale(context, frame->data, frame->linesize, 0, frame->height,
rgbaFrame->data, rgbaFrame->linesize);
sf::Image image;
image.create(frame->width, frame->height, buffer.data());
texture->loadFromImage(image);
av_frame_unref(frame);
sws_freeContext(context);
}
void VideoService::Play() {
while (!stopSource.get_token().stop_requested()) {
PaintFrame();
window->clear();
sprite->setTexture(*(texture.get()));
window->draw(*(sprite.get()));
std::this_thread::sleep_for(41ms);
}
}

View File

@ -1,10 +1,7 @@
#include <gtest/gtest.h>
#include "UtilTool.h" #include "UtilTool.h"
#include "gtest/gtest.h"
#include <iostream> #include <iostream>
TEST(UtilTest, CheckIsImage) TEST(UtilTest, CheckIsImage) {
{
EXPECT_TRUE(UtilTool::CheckFileType("file.jpg") == MediaType::IMAGE); EXPECT_TRUE(UtilTool::CheckFileType("file.jpg") == MediaType::IMAGE);
} }