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

View File

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

View File

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

View File

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

View File

@ -1,4 +1,7 @@
#include "videoService.h"
#include <chrono>
#include <spdlog/spdlog.h>
using namespace std::literals::chrono_literals;
void AVFormatContextDeleter(AVFormatContext *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->window = window;
formatContext =
std::unique_ptr<AVFormatContext, decltype(&AVFormatContextDeleter)>(
nullptr, AVFormatContextDeleter);
codecContext =
std::unique_ptr<AVCodecContext, decltype(&AVCodecContextDeleter)>(
nullptr, AVCodecContextDeleter);
sprite = std::make_unique<sf::Sprite>();
texture = std::make_unique<sf::Texture>();
InitContext();
}
@ -49,17 +56,20 @@ void VideoService::InitContext() {
void VideoService::Decode() {
const auto &fmtCtx = formatContext.get();
AVPacket *packet = av_packet_alloc();
decodeThread = std::jthread([this, fmtCtx, packet]() {
decodePacketThread = 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);
spdlog::info("packetQueueSize: {}", packetQueue.size());
} else if (packet->stream_index == this->audioStreamIndex) {
auto temp = av_packet_alloc();
av_packet_ref(temp, packet);
this->packetQueue.push(temp);
this->audioQueue.push(temp);
spdlog::info("audioQueueSize: {}", audioQueue.size());
}
} else if (ret == AVERROR_EOF) {
return;
@ -67,4 +77,58 @@ void VideoService::Decode() {
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 "gtest/gtest.h"
#include <iostream>
TEST(UtilTest, CheckIsImage)
{
TEST(UtilTest, CheckIsImage) {
EXPECT_TRUE(UtilTool::CheckFileType("file.jpg") == MediaType::IMAGE);
}