SDL2绘制ffmpeg解析的mp4文件

文章目录

  • 1.FFMPEG利用命令行将mp4转yuv420
  • 2.ffmpeg将mp4解析为yuv数据
    • 2.1 核心api:
  • 3.SDL2进行yuv绘制到屏幕
    • 3.1 核心api
  • 4.完整代码
  • 5.效果展示


本项目采用生产者消费者模型,生产者线程:使用ffmpeg将mp4格式数据解析为yuv的帧,消费者线程:利用sdl2将解析的yuv的帧进行消费,绘制到屏幕上。未完成的部分:1.解析音频数据,并与视频数据同步。2.增加界面:暂停,播放按钮,支持视频前进和后退。
学习音视频的参考资料与项目:
playdemo_github
雷神的csdn博客


1.FFMPEG利用命令行将mp4转yuv420

ffmpeg -i input.mp4 -c:v rawvideo -pix_fmt yuv420p output.yuv

在这里插入图片描述

2.ffmpeg将mp4解析为yuv数据

2.1 核心api:

  • av_read_frame:读取一帧数据
  • avcodec_send_packet:将数据包发送给解码器
  • avcodec_receive_frame:将数据包从解码器中取
  • sws_scale:格式转换,将解码后的帧数据转为yuv数据,存储在data[0],data[1],data[2]中
    在这里插入图片描述
void readFrame()
{AVPacket* avPacket = av_packet_alloc();AVFrame* frame = av_frame_alloc();FILE* fp = fopen("F:/VS_Project/ffmpeg_demo/yuv.data","wb+") ;while (av_read_frame(formatContext, avPacket) >= 0 && fp){if (avPacket->stream_index == videoStreamIndex){if (avcodec_send_packet(codecContext, avPacket) < 0) {std::cerr << "发送数据包到解码器失败" << std::endl;break;}/*解码*/int ret = avcodec_receive_frame(codecContext, frame);printf("ret:%d\n", ret);if (ret >= 0){ret = sws_scale(swsContext, frame->data, frame->linesize, 0, codecContext->height, yuvFrame->data, yuvFrame->linesize);printf("sws_scale ret=%d\n", ret);std::lock_guard<std::mutex>lck(mtx);isFinished = false;memcpy(yuvBuf, yuvFrame->data[0], yuvFrame->width * yuvFrame->height);memcpy(yuvBuf + yuvFrame->width * yuvFrame->height, yuvFrame->data[1], yuvFrame->width * yuvFrame->height / 4);memcpy(yuvBuf + yuvFrame->width * yuvFrame->height*5/4, yuvFrame->data[2], yuvFrame->width * yuvFrame->height / 4);isFinished = true;condvar.notify_one();//保存y分量//fwrite(yuvFrame->data[0], 1, yuvFrame->width * yuvFrame->height, fp);//保存uv分量//fwrite(yuvFrame->data[1], 1, yuvFrame->width * yuvFrame->height/4, fp);//fwrite(yuvFrame->data[2], 1, yuvFrame->width * yuvFrame->height / 4, fp);}}}fclose(fp);av_frame_unref(yuvFrame);av_packet_free(&avPacket);av_frame_unref(frame);
}

3.SDL2进行yuv绘制到屏幕

3.1 核心api

  • SDL_Init
  • SDL_CreateWindow
  • SDL_CreateRenderer
  • SDL_CreateTexture
  • SDL_UpdateTexture
  • SDL_RenderCopy
  • SDL_RenderPresent
  • SDL_Delay:控制帧率
    在这里插入图片描述
int sdl_display()
{if (SDL_Init(SDL_INIT_VIDEO)) {printf("sdl init failed\n");return -1;}SDL_Window* window = SDL_CreateWindow("sdl_demo", 200, 200, codecContext->width, codecContext->height, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);if (!window) {SDL_Quit();return -1;}SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, 0);if (!renderer){SDL_DestroyWindow(window);SDL_Quit();return -1;}SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);SDL_RenderClear(renderer);Uint32 pixformat = SDL_PIXELFORMAT_IYUV;SDL_Texture* sdlTexture = SDL_CreateTexture(renderer, pixformat, SDL_TEXTUREACCESS_STREAMING, codecContext->width, codecContext->height);//FILE* fp = fopen("F:/VS_Project/ffmpeg_demo/yuv.data", "rb+");while (1) {//int ret = fread(yuvBuf, 1, yuvlen, fp);//if (ret <= 0) {//	break;//}std::unique_lock<std::mutex>lck(mtx);if (condvar.wait_for(lck, std::chrono::seconds(1), [] {return isFinished;})){isFinished = false;SDL_UpdateTexture(sdlTexture, NULL, yuvBuf, codecContext->width);SDL_RenderCopy(renderer, sdlTexture, NULL, NULL);SDL_RenderPresent(renderer);//控制帧率25fpsSDL_Delay(40);}else {printf("sdl thread exit!\n");break;}}SDL_Quit();return 0;
}

4.完整代码

-使用两个线程,生产者消费者模型

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdexcept>
#include <iostream>
#include <string>
#include <thread>
#include <fstream>#include <mutex>
#include <condition_variable>
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/avutil.h>
#include <libswscale/swscale.h>#include <SDL.h>
}
#undef main
#pragma warning(disable:4996)
AVFormatContext* formatContext = nullptr;
AVCodecContext* codecContext = nullptr;
SwsContext* swsContext = nullptr;
int videoStreamIndex = -1;
AVFrame* yuvFrame;
unsigned char* yuvBuf;
bool isReady = false;
bool isFinished = false;std::mutex mtx;
std::condition_variable condvar;
void readFrame()
{AVPacket* avPacket = av_packet_alloc();AVFrame* frame = av_frame_alloc();FILE* fp = fopen("F:/VS_Project/ffmpeg_demo/yuv.data","wb+") ;while (av_read_frame(formatContext, avPacket) >= 0 && fp){if (avPacket->stream_index == videoStreamIndex){if (avcodec_send_packet(codecContext, avPacket) < 0) {std::cerr << "发送数据包到解码器失败" << std::endl;break;}/*解码*/int ret = avcodec_receive_frame(codecContext, frame);printf("ret:%d\n", ret);if (ret >= 0){ret = sws_scale(swsContext, frame->data, frame->linesize, 0, codecContext->height, yuvFrame->data, yuvFrame->linesize);printf("sws_scale ret=%d\n", ret);std::lock_guard<std::mutex>lck(mtx);isFinished = false;memcpy(yuvBuf, yuvFrame->data[0], yuvFrame->width * yuvFrame->height);memcpy(yuvBuf + yuvFrame->width * yuvFrame->height, yuvFrame->data[1], yuvFrame->width * yuvFrame->height / 4);memcpy(yuvBuf + yuvFrame->width * yuvFrame->height*5/4, yuvFrame->data[2], yuvFrame->width * yuvFrame->height / 4);isFinished = true;condvar.notify_one();//保存y分量//fwrite(yuvFrame->data[0], 1, yuvFrame->width * yuvFrame->height, fp);//保存uv分量//fwrite(yuvFrame->data[1], 1, yuvFrame->width * yuvFrame->height/4, fp);//fwrite(yuvFrame->data[2], 1, yuvFrame->width * yuvFrame->height / 4, fp);}}}fclose(fp);av_frame_unref(yuvFrame);av_packet_free(&avPacket);av_frame_unref(frame);
}int sdl_display()
{if (SDL_Init(SDL_INIT_VIDEO)) {printf("sdl init failed\n");return -1;}SDL_Window* window = SDL_CreateWindow("sdl_demo", 200, 200, codecContext->width, codecContext->height, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);if (!window) {SDL_Quit();return -1;}SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, 0);if (!renderer){SDL_DestroyWindow(window);SDL_Quit();return -1;}SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);SDL_RenderClear(renderer);Uint32 pixformat = SDL_PIXELFORMAT_IYUV;SDL_Texture* sdlTexture = SDL_CreateTexture(renderer, pixformat, SDL_TEXTUREACCESS_STREAMING, codecContext->width, codecContext->height);//FILE* fp = fopen("F:/VS_Project/ffmpeg_demo/yuv.data", "rb+");while (1) {//int ret = fread(yuvBuf, 1, yuvlen, fp);//if (ret <= 0) {//	break;//}std::unique_lock<std::mutex>lck(mtx);if (condvar.wait_for(lck, std::chrono::seconds(1), [] {return isFinished;})){isFinished = false;SDL_UpdateTexture(sdlTexture, NULL, yuvBuf, codecContext->width);SDL_RenderCopy(renderer, sdlTexture, NULL, NULL);SDL_RenderPresent(renderer);//控制帧率25fpsSDL_Delay(40);}else {printf("sdl thread exit!\n");break;}}SDL_Quit();return 0;
}/*ffmpeg -i input.mp4 -c:v rawvideo -pix_fmt yuv420p output.yuv*/
int main(int argc, char* argv[])
{/*if (argc != 2) {std::cerr << "文件名未指定" << std::endl;return -1;}*/std::string filename = "F:/VS_Project/ffmpeg_demo/1.mkv";if (avformat_open_input(&formatContext, filename.c_str(), nullptr, nullptr) != 0){std::cerr << "无法打开文件" << std::endl;return -1;}if (avformat_find_stream_info(formatContext, nullptr) < 0) {std::cerr << "无法找到视频流" << std::endl;return -1;}for (int i = 0; i < formatContext->nb_streams; i++){enum AVMediaType type = AVMEDIA_TYPE_VIDEO;AVStream* st = formatContext->streams[i];AVCodecParameters* codecpar = st->codecpar;if (codecpar->codec_type == type){videoStreamIndex = i;const AVCodec* codec = avcodec_find_decoder(codecpar->codec_id);codecContext = avcodec_alloc_context3(codec);avcodec_parameters_to_context(codecContext, codecpar);avcodec_open2(codecContext, codec, nullptr);swsContext = sws_getContext(codecContext->width, codecContext->height, codecContext->pix_fmt,codecContext->width, codecContext->height, AV_PIX_FMT_YUV420P,SWS_BILINEAR, nullptr, nullptr, nullptr);std::cout << "w:" << codecpar->width << std::endl;std::cout << "h:" << codecpar->height << std::endl;}}yuvFrame = av_frame_alloc();yuvFrame->width = codecContext->width;yuvFrame->height = codecContext->height;yuvFrame->format = AV_PIX_FMT_YUV420P;int yuvlen = codecContext->width * codecContext->height * 3 / 2;yuvBuf = new unsigned char[yuvlen];int ret = av_frame_get_buffer(yuvFrame, 0);if (ret < 0) {printf("分配缓冲区失败\n");return -1;}//sdl_init();std::thread th1(readFrame);std::thread th2(sdl_display);th1.join();th2.join();delete[]yuvBuf;return 0;
}

5.效果展示

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/123906.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Unity实现设计模式——状态模式

Unity实现设计模式——状态模式 状态模式最核心的设计思路就是将对象的状态抽象出一个接口&#xff0c;然后根据它的不同状态封装其行为&#xff0c;这样就可以实现状态和行为的绑定&#xff0c;最终实现对象和状态的有效解耦。 在实际开发中一般用到FSM有限状态机的实现&…

以太网基础学习(二)——ARP协议

一、什么是MAC地址 MAC地址&#xff08;英语&#xff1a;Media Access Control Address&#xff09;&#xff0c;直译为媒体访问控制位址&#xff0c;也称为局域网地址&#xff08;LAN Address&#xff09;&#xff0c;MAC位址&#xff0c;以太网地址&#xff08;Ethernet Addr…

231003-四步MacOS-iPadOS设置无线竖屏随航SideCar

Step 0&#xff1a;MacOS到iPad无线竖屏随航显示&#xff0c;最终效果 Step 1&#xff1a; 下载 Better Display Step 2&#xff1a;在设置中新建虚拟屏幕&#xff0c;创建虚拟屏幕 Step 3&#xff1a;进行如下设置 Step 4&#xff1a;注意事项 ⚠️ 设置后的虚拟屏幕与Sideca…

【Java 进阶篇】JDBC查询操作详解

在数据库编程中&#xff0c;查询是一项非常常见且重要的操作。JDBC&#xff08;Java Database Connectivity&#xff09;提供了丰富的API来执行各种类型的查询操作。本篇博客将详细介绍如何使用JDBC进行查询操作&#xff0c;包括连接数据库、创建查询语句、执行查询、处理结果集…

辅助驾驶功能开发-测试篇(2)-真值系统介绍

1 真值系统概述 1.1 真值评测系统核心应用 快速构建有效感知真值,快速完成感知性能评估,快速分析感知性能缺陷。 主要应用场景包括: 1. 感知算法开发验证: 在算法开发周期中,评测结果可以作为测试报告的一部分,体现算法性能的提升。 2. 遴选供应…

asp.net core 远程调试

大概说下过程&#xff1a; 1、站点发布使用Debug模式 2、拷贝到远程服务器&#xff0c;以及iis创建站点。 3、本地的VS2022的安装目录&#xff1a;C:\Program Files\Microsoft Visual Studio\2022\Professional\Common7\IDE下找Remote Debugger 你的服务器是64位就拷贝x64的目…

数据挖掘实验(一)数据规范化【最小-最大规范化、零-均值规范化、小数定标规范化】

一、数据规范化的原理 数据规范化处理是数据挖掘的一项基础工作。不同的属性变量往往具有不同的取值范围&#xff0c;数值间的差别可能很大&#xff0c;不进行处理可能会影响到数据分析的结果。为了消除指标之间由于取值范围带来的差异&#xff0c;需要进行标准化处理。将数据…

Autowired和Resource的关系

相同点对于下面的代码来说&#xff0c;如果是Spring容器的话&#xff0c;两个注解的功能基本是等价的&#xff0c;他们都可以将bean注入到对应的field中 不同点但是请注意&#xff0c;这里说的是基本相同&#xff0c;说明还是有一些不同点的&#xff1a; byName和byType匹配顺…

13.(开发工具篇github)如何在GitHub上上传本地项目

一:创建GitHub账户并安装Git 二:创建一个新的仓库(repository) 三、拉取代码 git clone https://github.com/ainier-max/myboot.git git clone git@github.com:ainier-max/myboot.git四、拷贝代码到拉取后的工程 五、上传代码 (1)添加所有文件到暂存

Ubuntu中启动HDFS后没有NameNode解决办法

关闭进程&#xff1a; stop-dfs.sh 格式化&#xff1a; hadoop namenode -format 出现报错信息&#xff1a; 23/10/03 22:27:04 WARN fs.FileUtil: Failed to delete file or dir [/usr/data/hadoop/tmp/dfs/name/current/fsimage_0000000000000000000.md5]: it still exi…

键盘上F1至F12键的作用

多年来&#xff0c;我们习惯了最上排的12个按键&#xff0c;从F1到F12&#xff0c;它们被称为“快速功能键”&#xff0c;可以让你更轻松地操作电脑&#xff1b;但是&#xff0c;很多人可能从未使用过它们&#xff0c;也从来不知道它们的用途。那么今天&#xff0c;就向大家科普…

Sui第五轮资助: 17个项目共获105万美元资助金

近日&#xff0c;Sui基金会宣布17个项目获得了105万美元的资助&#xff0c;用于建设项目以推动Sui的采用和发展。要获得资助&#xff0c;项目必须提交详细的提案&#xff0c;说明构建的项目、预算明细、关键里程碑、团队经验&#xff0c;以及对Sui社区的预期贡献。了解更多Sui资…