创建线程解析packet并存入队列

This commit is contained in:
jie 2024-02-21 22:34:00 +08:00
parent 39697786c9
commit 659ca4772d
3 changed files with 141 additions and 44 deletions

View File

@ -6,23 +6,15 @@ extern "C" {
#include "libavutil/imgutils.h" #include "libavutil/imgutils.h"
} }
#include <queue> #include <queue>
#include <thread>
#include <condition_variable> #include <condition_variable>
#include <mutex> #include <mutex>
struct VideoParam
{
AVFormatContext* fmtCtx;
AVCodecContext* codecCtx;
int width;
int height;
int videoStreamIndex;
};
template<typename T> template<typename T>
requires std::is_same_v<T, AVPacket> || std::is_same_v<T, AVFrame> requires std::is_same_v<T, AVPacket> || std::is_same_v<T, AVFrame>
struct MediaQueue struct MediaQueue
{ {
static constexpr int MAX_SIZE = 500;
bool full = false;
std::queue<T> queue; std::queue<T> queue;
std::condition_variable cv; std::condition_variable cv;
std::mutex mut; std::mutex mut;
@ -31,6 +23,7 @@ struct MediaQueue
uint32_t count; uint32_t count;
MediaQueue() = default; MediaQueue() = default;
bool isFill() const { return full; }
bool push(const T* item); bool push(const T* item);
bool pop(T* item, bool block = false, bool quit = false); bool pop(T* item, bool block = false, bool quit = false);
}; };
@ -38,18 +31,22 @@ struct MediaQueue
template <typename T> requires std::is_same_v<T, AVPacket> || std::is_same_v<T, AVFrame> template <typename T> requires std::is_same_v<T, AVPacket> || std::is_same_v<T, AVFrame>
bool MediaQueue<T>::push(const T* item) { bool MediaQueue<T>::push(const T* item) {
if (item == nullptr) return false; if (item == nullptr) return false;
if (count >= MAX_SIZE)
return false;
std::unique_lock lock(mut); std::unique_lock lock(mut);
if constexpr (std::is_same_v<T, AVPacket>) { if constexpr (std::is_same_v<T, AVPacket>) {
auto temp = av_packet_alloc(); auto temp = av_packet_alloc();
av_packet_ref(temp, item); av_packet_ref(temp, item);
queue.push(*temp);
size += temp->size; size += temp->size;
count++; queue.push(*temp);
}else if(std::is_same_v<T, AVFrame>) { }else if(std::is_same_v<T, AVFrame>) {
auto temp = av_frame_alloc(); auto temp = av_frame_alloc();
av_frame_ref(temp, item); av_frame_ref(temp, item);
queue.push(*temp); queue.push(*temp);
count++; }
count++;
if(count >= MAX_SIZE) {
full = true;
} }
cv.notify_all(); cv.notify_all();
return true; return true;
@ -75,6 +72,7 @@ bool MediaQueue<T>::pop(T* item, bool block, bool quit) {
} }
queue.pop(); queue.pop();
count--; count--;
full = false;
return true; return true;
} }
else if (block) { else if (block) {
@ -88,8 +86,22 @@ bool MediaQueue<T>::pop(T* item, bool block, bool quit) {
} }
struct VideoParam
{
MediaQueue<AVPacket> queue;
AVFormatContext* fmtCtx;
AVCodecContext* codecCtx;
int width;
int height;
int videoStreamIndex;
bool eof = false;
bool pause = false;
bool quit = false;
};
void InitDecoder(const char* filepath, VideoParam& param); void InitDecoder(const char* filepath, VideoParam& param);
void RequestPacket(VideoParam& param);
AVFrame* RequestFrame(VideoParam& param); AVFrame* RequestFrame(VideoParam& param);
#endif #endif

26
main.cc
View File

@ -17,11 +17,13 @@ struct OpenglVideoParam
unsigned int texs[3]; unsigned int texs[3];
}; };
int InitVideo(SDL_Window*& window, const char* targetFilepath, DecoderParam& decoderParam, OpenglVideoParam& openglVideoParam, ShaderService*& shaderService) int InitVideo(SDL_Window*& window, const char* targetFilepath, VideoParam& videoParam, OpenglVideoParam& openglVideoParam, ShaderService*& shaderService)
{ {
InitDecoder(targetFilepath, decoderParam); InitDecoder(targetFilepath, videoParam);
const int client_width = decoderParam.width / 2; //FIX: when app exited, the fmtCtx was freed, so need notify decode thread to stop decode and exit.
const int client_height = decoderParam.height / 2; std::jthread(RequestPacket, std::ref(videoParam)).detach();
const int client_width = videoParam.width / 2;
const int client_height = videoParam.height / 2;
window = SDL_CreateWindow( window = SDL_CreateWindow(
"MP", "MP",
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
@ -121,9 +123,9 @@ void InitImg(SDL_Window*& window, const char* filepath, SDL_Renderer*& renderer,
texture = SDL_CreateTextureFromSurface(renderer, surface); texture = SDL_CreateTextureFromSurface(renderer, surface);
} }
void OpenglRenderVideo(DecoderParam& decoderParam, const OpenglVideoParam& openglVideoParam, ShaderService* shaderService) void OpenglRenderVideo(VideoParam& videoParam, const OpenglVideoParam& openglVideoParam, ShaderService* shaderService)
{ {
auto frame = RequestFrame(decoderParam); auto frame = RequestFrame(videoParam);
if (frame == nullptr) if (frame == nullptr)
return; return;
// TODO: TIMER // TODO: TIMER
@ -169,7 +171,7 @@ int main(int argc, char** const argv)
int client_width, client_height; int client_width, client_height;
SDL_Window* window = nullptr; SDL_Window* window = nullptr;
DecoderParam decoderParam{}; VideoParam videoParam{};
OpenglVideoParam openglVideoParam{}; OpenglVideoParam openglVideoParam{};
ShaderService* shaderService = nullptr; ShaderService* shaderService = nullptr;
SDL_Surface* surface = nullptr; SDL_Surface* surface = nullptr;
@ -185,8 +187,8 @@ int main(int argc, char** const argv)
{ {
case FileType::VIDEO: case FileType::VIDEO:
{ {
InitVideo(window, targetFilepath, decoderParam, openglVideoParam, shaderService); InitVideo(window, targetFilepath, videoParam, openglVideoParam, shaderService);
const auto stream_frame_rate = decoderParam.fmtCtx->streams[decoderParam.videoStreamIndex]->avg_frame_rate; const auto stream_frame_rate = videoParam.fmtCtx->streams[videoParam.videoStreamIndex]->avg_frame_rate;
framerate = static_cast<double>(stream_frame_rate.den) / stream_frame_rate.num; framerate = static_cast<double>(stream_frame_rate.den) / stream_frame_rate.num;
break; break;
} }
@ -234,7 +236,7 @@ int main(int argc, char** const argv)
switch (fileType) switch (fileType)
{ {
case FileType::VIDEO: case FileType::VIDEO:
OpenglRenderVideo(decoderParam, openglVideoParam, shaderService); OpenglRenderVideo(videoParam, openglVideoParam, shaderService);
SDL_GL_SwapWindow(window); SDL_GL_SwapWindow(window);
std::this_thread::sleep_until(current_time + std::chrono::milliseconds(static_cast<int>(framerate * 1000))); std::this_thread::sleep_until(current_time + std::chrono::milliseconds(static_cast<int>(framerate * 1000)));
current_time = std::chrono::system_clock::now(); current_time = std::chrono::system_clock::now();
@ -247,8 +249,8 @@ int main(int argc, char** const argv)
} }
} }
avcodec_close(decoderParam.codecCtx); avcodec_close(videoParam.codecCtx);
avformat_close_input(&(decoderParam.fmtCtx)); avformat_close_input(&(videoParam.fmtCtx));
SDL_GL_DeleteContext(openglVideoParam.glContext); SDL_GL_DeleteContext(openglVideoParam.glContext);
SDL_DestroyWindow(window); SDL_DestroyWindow(window);
SDL_Quit(); SDL_Quit();

View File

@ -1,6 +1,9 @@
#include "decoder.h" #include "decoder.h"
#include <thread>
#include <chrono>
using namespace std::literals::chrono_literals;
void InitDecoder(const char* filepath, DecoderParam& param) { void InitDecoder(const char* filepath, VideoParam& param) {
AVFormatContext* fmtCtx = nullptr; AVFormatContext* fmtCtx = nullptr;
AVCodecContext* codecFmt = nullptr; AVCodecContext* codecFmt = nullptr;
auto ret = avformat_open_input(&fmtCtx, filepath, NULL, NULL); auto ret = avformat_open_input(&fmtCtx, filepath, NULL, NULL);
@ -23,7 +26,44 @@ void InitDecoder(const char* filepath, DecoderParam& param) {
param.height = codecFmt->height; param.height = codecFmt->height;
} }
AVFrame* RequestFrame(DecoderParam& param) { void RequestPacket(VideoParam& param) {
const auto& fmtCtx = param.fmtCtx;
const auto& videoStreamIndex = param.videoStreamIndex;
AVPacket* packet = av_packet_alloc();
while (true) {
if(param.queue.isFill()) {
std::this_thread::sleep_for(100ms);
continue;
}
const int ret = av_read_frame(fmtCtx, packet);
if (param.eof) {
std::this_thread::sleep_for(100ms);
return;
}
if (ret == 0) {
if (packet->stream_index == videoStreamIndex) {
param.queue.push(packet);
av_packet_unref(packet);
}
else if (ret == AVERROR_EOF)
{
param.eof = true;
av_packet_unref(packet);
break;
}else {
av_packet_unref(packet);
}
}else if(param.fmtCtx->pb->error == 0) {
std::this_thread::sleep_for(100ms);
}
}
av_packet_unref(packet);
}
AVFrame* RequestFrame(VideoParam& param) {
const auto& fmtCtx = param.fmtCtx; const auto& fmtCtx = param.fmtCtx;
const auto& codecCtx = param.codecCtx; const auto& codecCtx = param.codecCtx;
const auto& videoStreamIndex = param.videoStreamIndex; const auto& videoStreamIndex = param.videoStreamIndex;
@ -32,28 +72,71 @@ AVFrame* RequestFrame(DecoderParam& param) {
AVFrame* frame = av_frame_alloc(); AVFrame* frame = av_frame_alloc();
while (true) { while (true) {
int ret = av_read_frame(fmtCtx, packet); if (!param.queue.pop(packet, true, param.quit)) {
if (ret == 0 && packet->stream_index == videoStreamIndex) { if(param.quit)
ret = avcodec_send_packet(codecCtx, packet); //NOTE:if no need do something, just return nullptr
if (ret == 0) {
ret = avcodec_receive_frame(codecCtx, frame);
if (ret == 0) {
av_packet_unref(packet);
return frame;
}
if (ret == AVERROR(EAGAIN)) {
continue;
}
av_frame_free(&frame);
av_packet_free(&packet);
return nullptr; return nullptr;
} continue;
} }
else if (ret == AVERROR_EOF) int ret = avcodec_send_packet(codecCtx, packet);
if(ret < 0) {
av_packet_unref(packet);
av_frame_free(&frame);
return nullptr;
}
if (ret == 0) {
ret = avcodec_receive_frame(codecCtx, frame);
if (ret == 0) {
av_packet_unref(packet);
return frame;
}
if (ret == AVERROR(EAGAIN)) {
continue;
}
av_frame_free(&frame);
av_packet_free(&packet);
return nullptr;
}
if (ret == AVERROR_EOF)
{ {
av_packet_unref(packet); av_packet_unref(packet);
return nullptr; return nullptr;
} }
av_packet_unref(packet); av_packet_unref(packet);
} }
} }
//AVFrame* RequestFrame(VideoParam& param) {
// const auto& fmtCtx = param.fmtCtx;
// const auto& codecCtx = param.codecCtx;
// const auto& videoStreamIndex = param.videoStreamIndex;
//
// AVPacket* packet = av_packet_alloc();
// AVFrame* frame = av_frame_alloc();
//
// while (true) {
// int ret = av_read_frame(fmtCtx, packet);
// if (ret == 0 && packet->stream_index == videoStreamIndex) {
// ret = avcodec_send_packet(codecCtx, packet);
// if (ret == 0) {
// ret = avcodec_receive_frame(codecCtx, frame);
// if (ret == 0) {
// av_packet_unref(packet);
// return frame;
// }
// if (ret == AVERROR(EAGAIN)) {
// continue;
// }
// av_frame_free(&frame);
// av_packet_free(&packet);
// return nullptr;
// }
// }
// else if (ret == AVERROR_EOF)
// {
// av_packet_unref(packet);
// return nullptr;
// }
// av_packet_unref(packet);
// }
//}