From 659ca4772dd2509b2345ef1214501242e3a77c0f Mon Sep 17 00:00:00 2001 From: jie Date: Wed, 21 Feb 2024 22:34:00 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=9B=E5=BB=BA=E7=BA=BF=E7=A8=8B=E8=A7=A3?= =?UTF-8?q?=E6=9E=90packet=E5=B9=B6=E5=AD=98=E5=85=A5=E9=98=9F=E5=88=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/decoder.h | 38 ++++++++++----- main.cc | 26 +++++----- src/decoder.cc | 121 ++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 141 insertions(+), 44 deletions(-) diff --git a/include/decoder.h b/include/decoder.h index 1d8881b..acf4266 100644 --- a/include/decoder.h +++ b/include/decoder.h @@ -6,23 +6,15 @@ extern "C" { #include "libavutil/imgutils.h" } #include -#include #include #include -struct VideoParam -{ - AVFormatContext* fmtCtx; - AVCodecContext* codecCtx; - int width; - int height; - int videoStreamIndex; -}; - template requires std::is_same_v || std::is_same_v struct MediaQueue { + static constexpr int MAX_SIZE = 500; + bool full = false; std::queue queue; std::condition_variable cv; std::mutex mut; @@ -31,6 +23,7 @@ struct MediaQueue uint32_t count; MediaQueue() = default; + bool isFill() const { return full; } bool push(const T* item); bool pop(T* item, bool block = false, bool quit = false); }; @@ -38,18 +31,22 @@ struct MediaQueue template requires std::is_same_v || std::is_same_v bool MediaQueue::push(const T* item) { if (item == nullptr) return false; + if (count >= MAX_SIZE) + return false; std::unique_lock lock(mut); if constexpr (std::is_same_v) { auto temp = av_packet_alloc(); av_packet_ref(temp, item); - queue.push(*temp); size += temp->size; - count++; + queue.push(*temp); }else if(std::is_same_v) { auto temp = av_frame_alloc(); av_frame_ref(temp, item); queue.push(*temp); - count++; + } + count++; + if(count >= MAX_SIZE) { + full = true; } cv.notify_all(); return true; @@ -75,6 +72,7 @@ bool MediaQueue::pop(T* item, bool block, bool quit) { } queue.pop(); count--; + full = false; return true; } else if (block) { @@ -88,8 +86,22 @@ bool MediaQueue::pop(T* item, bool block, bool quit) { } +struct VideoParam +{ + MediaQueue 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 RequestPacket(VideoParam& param); AVFrame* RequestFrame(VideoParam& param); #endif \ No newline at end of file diff --git a/main.cc b/main.cc index 5018c6e..b346a77 100644 --- a/main.cc +++ b/main.cc @@ -17,11 +17,13 @@ struct OpenglVideoParam 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); - const int client_width = decoderParam.width / 2; - const int client_height = decoderParam.height / 2; + InitDecoder(targetFilepath, videoParam); + //FIX: when app exited, the fmtCtx was freed, so need notify decode thread to stop decode and exit. + std::jthread(RequestPacket, std::ref(videoParam)).detach(); + const int client_width = videoParam.width / 2; + const int client_height = videoParam.height / 2; window = SDL_CreateWindow( "MP", SDL_WINDOWPOS_UNDEFINED, @@ -121,9 +123,9 @@ void InitImg(SDL_Window*& window, const char* filepath, SDL_Renderer*& renderer, 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) return; // TODO: TIMER @@ -169,7 +171,7 @@ int main(int argc, char** const argv) int client_width, client_height; SDL_Window* window = nullptr; - DecoderParam decoderParam{}; + VideoParam videoParam{}; OpenglVideoParam openglVideoParam{}; ShaderService* shaderService = nullptr; SDL_Surface* surface = nullptr; @@ -185,8 +187,8 @@ int main(int argc, char** const argv) { case FileType::VIDEO: { - InitVideo(window, targetFilepath, decoderParam, openglVideoParam, shaderService); - const auto stream_frame_rate = decoderParam.fmtCtx->streams[decoderParam.videoStreamIndex]->avg_frame_rate; + InitVideo(window, targetFilepath, videoParam, openglVideoParam, shaderService); + const auto stream_frame_rate = videoParam.fmtCtx->streams[videoParam.videoStreamIndex]->avg_frame_rate; framerate = static_cast(stream_frame_rate.den) / stream_frame_rate.num; break; } @@ -234,7 +236,7 @@ int main(int argc, char** const argv) switch (fileType) { case FileType::VIDEO: - OpenglRenderVideo(decoderParam, openglVideoParam, shaderService); + OpenglRenderVideo(videoParam, openglVideoParam, shaderService); SDL_GL_SwapWindow(window); std::this_thread::sleep_until(current_time + std::chrono::milliseconds(static_cast(framerate * 1000))); current_time = std::chrono::system_clock::now(); @@ -247,8 +249,8 @@ int main(int argc, char** const argv) } } - avcodec_close(decoderParam.codecCtx); - avformat_close_input(&(decoderParam.fmtCtx)); + avcodec_close(videoParam.codecCtx); + avformat_close_input(&(videoParam.fmtCtx)); SDL_GL_DeleteContext(openglVideoParam.glContext); SDL_DestroyWindow(window); SDL_Quit(); diff --git a/src/decoder.cc b/src/decoder.cc index 3966c2d..f44213b 100644 --- a/src/decoder.cc +++ b/src/decoder.cc @@ -1,6 +1,9 @@ #include "decoder.h" +#include +#include +using namespace std::literals::chrono_literals; -void InitDecoder(const char* filepath, DecoderParam& param) { +void InitDecoder(const char* filepath, VideoParam& param) { AVFormatContext* fmtCtx = nullptr; AVCodecContext* codecFmt = nullptr; auto ret = avformat_open_input(&fmtCtx, filepath, NULL, NULL); @@ -23,7 +26,44 @@ void InitDecoder(const char* filepath, DecoderParam& param) { 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& codecCtx = param.codecCtx; const auto& videoStreamIndex = param.videoStreamIndex; @@ -32,28 +72,71 @@ AVFrame* RequestFrame(DecoderParam& param) { 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); + if (!param.queue.pop(packet, true, param.quit)) { + if(param.quit) + //NOTE:if no need do something, just 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); return nullptr; } av_packet_unref(packet); } -} \ No newline at end of file +} + +//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); +// } +//} \ No newline at end of file