创建线程解析packet并存入队列
This commit is contained in:
parent
39697786c9
commit
659ca4772d
@ -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
26
main.cc
@ -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();
|
||||||
|
121
src/decoder.cc
121
src/decoder.cc
@ -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);
|
||||||
|
// }
|
||||||
|
//}
|
Loading…
Reference in New Issue
Block a user