创建线程解析packet并存入队列
This commit is contained in:
		@@ -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();
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										119
									
								
								src/decoder.cc
									
									
									
									
									
								
							
							
						
						
									
										119
									
								
								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,24 +72,32 @@ 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;
 | 
				
			||||||
@@ -57,3 +105,38 @@ AVFrame* RequestFrame(DecoderParam& param) {
 | 
				
			|||||||
		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);
 | 
				
			||||||
 | 
					//	}
 | 
				
			||||||
 | 
					//}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user