[ffmpeg系列 03] 文件、流地址(视频)解码为YUV
一 代码
ffmpeg版本5.1.2,dll是:ffmpeg-5.1.2-full_build-shared。x64的。
文件、流地址对使用者来说是一样。
流地址(RTMP、HTTP-FLV、RTSP等):信令完成后,才进行音视频传输。信令包括音视频格式、参数等协商。
接流的在实际中的应用:1 展示,播放。2 给算法用,一般是需要RGB格式的。
#ifndef _DECODE_H264_H_
#define _DECODE_H264_H_
#include <string>extern "C"
{
#include "libavformat/avformat.h"
#include "libavcodec/avcodec.h"
#include "libswscale/swscale.h"
#include "libavutil/avutil.h"
#include "libavutil/mathematics.h"
#include "libavutil/time.h"
#include "libavutil/pixdesc.h"
#include "libavutil/display.h"
};#pragma comment(lib, "avformat.lib")
#pragma comment(lib, "avutil.lib")
#pragma comment(lib, "avcodec.lib")
#pragma comment(lib, "swscale.lib")class CDecodeH264
{
public:CDecodeH264();~CDecodeH264();public:public:int DecodeH264();int Start();int Close();int DecodeH264File_Init();int ReleaseDecode();void H264Decode_Thread_Fun();std::string dup_wchar_to_utf8(const wchar_t* wstr);double get_rotation(AVStream *st);public:AVFormatContext* m_pInputFormatCtx = nullptr;AVCodecContext* m_pVideoDecodeCodecCtx = nullptr;const AVCodec* m_pCodec = nullptr; SwsContext* m_pSwsContext = nullptr;AVFrame* m_pFrameScale = nullptr;AVFrame* m_pFrameYUV = nullptr;AVPacket* m_pAVPacket = nullptr;enum AVMediaType m_CodecType;int m_output_pix_fmt;int m_nVideoStream = -1;int m_nFrameHeight = 0;int m_nFrameWidth = 0;int m_nFPS;int m_nVideoSeconds;FILE* m_pfOutYUV = nullptr;FILE* m_pfOutYUV2 = nullptr;
};
#endif
#include "DecodeH264.h"
#include <thread>
#include <functional>
#include <codecvt>
#include <locale>char av_error2[AV_ERROR_MAX_STRING_SIZE] = { 0 };
#define av_err2str2(errnum) av_make_error_string(av_error2, AV_ERROR_MAX_STRING_SIZE, errnum)CDecodeH264::CDecodeH264()
{
}CDecodeH264::~CDecodeH264()
{ReleaseDecode();
}std::string CDecodeH264::dup_wchar_to_utf8(const wchar_t* wstr)
{std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;return converter.to_bytes(wstr);
}//Side data :
//displaymatrix: rotation of - 90.00 degrees
double CDecodeH264::get_rotation(AVStream *st)
{uint8_t* displaymatrix = av_stream_get_side_data(st,AV_PKT_DATA_DISPLAYMATRIX, NULL);double theta = 0;if (displaymatrix)theta = -av_display_rotation_get((int32_t*)displaymatrix);theta -= 360 * floor(theta / 360 + 0.9 / 360);if (fabs(theta - 90 * round(theta / 90)) > 2)av_log(NULL, AV_LOG_WARNING, "Odd rotation angle.\n""If you want to help, upload a sample ""of this file to https://streams.videolan.org/upload/ ""and contact the ffmpeg-devel mailing list. (ffmpeg-devel@ffmpeg.org)");return theta;
}int CDecodeH264::DecodeH264File_Init()
{avformat_network_init(); //流地址需要m_pInputFormatCtx = avformat_alloc_context();std::string strFilename = dup_wchar_to_utf8(L"测试.h264");//std::string strFilename = dup_wchar_to_utf8(L"rtmp://127.0.0.1/live/now"); int ret = avformat_open_input(&m_pInputFormatCtx, strFilename.c_str(), nullptr, nullptr);if (ret != 0) {char* err_str = av_err2str2(ret);printf("fail to open filename: %s, return value: %d, %s\n", strFilename.c_str(), ret, err_str);return -1;}ret = avformat_find_stream_info(m_pInputFormatCtx, nullptr);if (ret < 0) {char* err_str = av_err2str2(ret);printf("fail to get stream information: %d, %s\n", ret, err_str); return -1;}for (int i = 0; i < m_pInputFormatCtx->nb_streams; ++i) {const AVStream* stream = m_pInputFormatCtx->streams[i];if (stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {m_nVideoStream = i;printf("type of the encoded data: %d, dimensions of the video frame in pixels: width: %d, height: %d, pixel format: %d\n",stream->codecpar->codec_id, stream->codecpar->width, stream->codecpar->height, stream->codecpar->format);}}if (m_nVideoStream == -1) {printf("no video stream\n");return -1;}printf("m_nVideoStream=%d\n", m_nVideoStream);//获取旋转角度double theta = get_rotation(m_pInputFormatCtx->streams[m_nVideoStream]);m_pVideoDecodeCodecCtx = avcodec_alloc_context3(nullptr);avcodec_parameters_to_context(m_pVideoDecodeCodecCtx,\m_pInputFormatCtx->streams[m_nVideoStream]->codecpar);m_pCodec = avcodec_find_decoder(m_pVideoDecodeCodecCtx->codec_id);if (m_pCodec == nullptr){return -1;}m_nFrameHeight = m_pVideoDecodeCodecCtx->height;m_nFrameWidth = m_pVideoDecodeCodecCtx->width;printf("w=%d h=%d\n", m_pVideoDecodeCodecCtx->width, m_pVideoDecodeCodecCtx->height);if (avcodec_open2(m_pVideoDecodeCodecCtx, m_pCodec, nullptr) < 0){return -1;}//读文件知道视频宽高m_output_pix_fmt = AV_PIX_FMT_YUV420P; //AV_PIX_FMT_NV12;m_pSwsContext = sws_getContext(m_pVideoDecodeCodecCtx->width, m_pVideoDecodeCodecCtx->height,m_pVideoDecodeCodecCtx->pix_fmt, m_pVideoDecodeCodecCtx->width, m_pVideoDecodeCodecCtx->height,(AVPixelFormat)m_output_pix_fmt, SWS_FAST_BILINEAR, nullptr, nullptr, nullptr);//解码后的视频数据m_pFrameScale = av_frame_alloc();m_pFrameScale->format = m_output_pix_fmt;m_pFrameYUV = av_frame_alloc();m_pFrameYUV->format = m_output_pix_fmt; //mAVFrame.format is not setm_pFrameYUV->width = m_pVideoDecodeCodecCtx->width;m_pFrameYUV->height = m_pVideoDecodeCodecCtx->height;printf("m_pFrameYUV pix_fmt=%d\n", m_pVideoDecodeCodecCtx->pix_fmt);av_frame_get_buffer(m_pFrameYUV, 64);char cYUVName[256];sprintf_s(cYUVName, "%d_%d_%s.yuv", m_nFrameWidth, m_nFrameHeight, av_get_pix_fmt_name(m_pVideoDecodeCodecCtx->pix_fmt));fopen_s(&m_pfOutYUV, cYUVName, "wb");char cYUVName2[256];sprintf_s(cYUVName2, "%d_%d_%s_2.yuv", m_nFrameWidth, m_nFrameHeight, av_get_pix_fmt_name(m_pVideoDecodeCodecCtx->pix_fmt));fopen_s(&m_pfOutYUV2, cYUVName2, "wb");printf("leave init\n");return 0;
}void CDecodeH264::H264Decode_Thread_Fun()
{int nFrameFinished = 0;int i = 0;int ret;m_pAVPacket = av_packet_alloc();while (true) {ret = av_read_frame(m_pInputFormatCtx, m_pAVPacket);if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {av_packet_unref(m_pAVPacket);printf("read_frame break");break;}if (m_pAVPacket->stream_index == m_nVideoStream){int send_packet_ret = avcodec_send_packet(m_pVideoDecodeCodecCtx, m_pAVPacket);printf("encode video send_packet_ret %d\n", send_packet_ret);int receive_frame_ret = avcodec_receive_frame(m_pVideoDecodeCodecCtx, m_pFrameScale);char* err_str = av_err2str2(receive_frame_ret);printf("frame w=%d, h=%d, linesize[0]=%d, linesize[1]=%d\n", m_pFrameScale->width, m_pFrameScale->height, m_pFrameScale->linesize[0], m_pFrameScale->linesize[1]);if (receive_frame_ret == 0){++i;int iReturn = sws_scale(m_pSwsContext, m_pFrameScale->data,m_pFrameScale->linesize, 0, m_nFrameHeight,m_pFrameYUV->data, m_pFrameYUV->linesize);printf("frame w=%d, h=%d, linesize[0]=%d, linesize[1]=%d\n", m_pFrameYUV->width, m_pFrameYUV->height, m_pFrameYUV->linesize[0], m_pFrameYUV->linesize[1]);/*if (0 != iReturn){fwrite(m_pFrameYUV->data[0], 1, m_nFrameWidth * m_nFrameHeight, m_pfOutYUV);fwrite(m_pFrameYUV->data[1], 1, m_nFrameWidth * m_nFrameHeight /4, m_pfOutYUV);fwrite(m_pFrameYUV->data[2], 1, m_nFrameWidth * m_nFrameHeight /4, m_pfOutYUV);}*///用linesize更能兼容特殊的宽if (0 != iReturn){ for (int i = 0; i < m_nFrameHeight; ++i) {fwrite(m_pFrameYUV->data[0] + i * m_pFrameYUV->linesize[0], 1, m_nFrameWidth, m_pfOutYUV2);}for (int i = 0; i < m_nFrameHeight / 2; ++i) {fwrite(m_pFrameYUV->data[1] + i * m_pFrameYUV->linesize[1], 1, m_nFrameWidth / 2, m_pfOutYUV2);}for (int i = 0; i < m_nFrameHeight / 2; ++i) {fwrite(m_pFrameYUV->data[2] + i * m_pFrameYUV->linesize[2], 1, m_nFrameWidth / 2, m_pfOutYUV2);}} }}av_packet_unref(m_pAVPacket);}
}int CDecodeH264::DecodeH264()
{if (DecodeH264File_Init() != 0){return -1;}auto video_func = std::bind(&CDecodeH264::H264Decode_Thread_Fun, this);std::thread video_thread(video_func);video_thread.join(); return 0;
}int CDecodeH264::Start()
{DecodeH264();return 1;
}int CDecodeH264::Close()
{return 0;
}int CDecodeH264::ReleaseDecode()
{if (m_pSwsContext){sws_freeContext(m_pSwsContext);m_pSwsContext = nullptr;}if (m_pFrameScale){av_frame_free(&m_pFrameScale);//av_frame_alloc()对应}if (m_pFrameYUV){av_frame_free(&m_pFrameYUV);}avcodec_close(m_pVideoDecodeCodecCtx);avformat_close_input(&m_pInputFormatCtx);return 0;}
#include <iostream>
#include <Windows.h>#include "1__DecodeH264/DecodeH264.h"int main()
{CDecodeH264* m_pDecodeVideo = new CDecodeH264();m_pDecodeVideo->Start();return 0;
}
图是雷神博客的:注册函数废弃了,解码函数变了。
二 相关的结构体,方便记忆
1 AVFrame是未压缩的,解码后的数据。AVPacket是压缩的,解码前的数据。知道了这个,编码的send_frame、receive_packet,解码的send_packet、receive_frame,容易记住了。2 2个Context(上下文):Format(混合文件、流地址)、Codec(单个编码格式,比如H264、AAC,编解码实现)AVFormatContext* m_pInputFormatCtx;AVCodecContext* m_pVideoDecodeCodecCtx;m_pInputFormatCtx会用到的函数:avformat_open_input、avformat_find_stream_info、 av_read_frame、avformat_close_input。m_pOutputFormatCtx会用到的函数:avcodec_find_decoder、avcodec_open2、avcodec_send_packet、 avcodec_receive_frame。3 AVCodec结构体
const AVCodec ff_h264_decoder = {.name = "h264",.long_name = NULL_IF_CONFIG_SMALL("H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10"),.type = AVMEDIA_TYPE_VIDEO,.id = AV_CODEC_ID_H264,.priv_data_size = sizeof(H264Context),.init = h264_decode_init,.close = h264_decode_end,.decode = h264_decode_frame,……
}static const AVCodec * const codec_list[] = {
...&ff_h264_decoder,
...
};
三 兼容性问题
1 文件名带中文,需要转换。
2 播放竖屏视频(手机录的那种),获取旋转角度。

截图是ffmpeg做法:获取角度后,使用filter调整。

3 宽比较特殊,不是16,32的整数。(比如544x960,544是32的倍数)。用linesize[i]代替宽。
linesize跟cpu有关,是cpu 16、32的倍数。
其它,待更新。
四 为什么需要sws_scale?转换到统一格式I420
sws_scale作用:1 分辨率缩放、 2 不同YUV、RGB格式转换。
H264有记录编码前的YUV采样格式,chroma_format_idc,在sps里。如果没有这个字段,说明该字段用的默认值1,即yuv 4:2:0。

如果YUV的采样格式是yuv 4:2:0,也不要求缩放,不需要sws_scale。
avcodec_receive_frame(AVCodecContext *avctx, AVFrame *frame);
frame->format记录了yuv的类型。
ffmpeg默认解码成:编码前的yuv格式。即m_pVideoDecodeCodecCtx->pix_fmt。
int ff_decode_frame_props(AVCodecContext *avctx, AVFrame *frame)
{...frame->format = avctx->pix_fmt;...
}
五 不同格式的time_base
H264的time_base:1/1200000。

flv:音视频都是1/1000。

mp4:视频1/12800(帧率25,怎么算出来的?),音频:1/48000(1/采样频率)。
相关文章:
[ffmpeg系列 03] 文件、流地址(视频)解码为YUV
一 代码 ffmpeg版本5.1.2,dll是:ffmpeg-5.1.2-full_build-shared。x64的。 文件、流地址对使用者来说是一样。 流地址(RTMP、HTTP-FLV、RTSP等):信令完成后,才进行音视频传输。信令包括音视频格式、参数等协商。 接流的在实际…...
python算法每日一练:连续子数组的最大和
这是一道关于动态规划的算法题: 题目描述: 给定一个整数数组 nums,请找出该数组中连续子数组的最大和,并返回这个最大和。 示例: 输入:[-2, 1, -3, 4, -1, 2, 1, -5, 4] 输出:6 解释ÿ…...
一个vue3的tree组件
https://download.csdn.net/download/weixin_41012767/88709466...
新手练习项目 4:简易2048游戏的实现(C++)
名人说:莫听穿林打叶声,何妨吟啸且徐行。—— 苏轼《定风波莫听穿林打叶声》 Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder) 目录 一、效果图二、代码(带注释)三、说明 一、效果图 二、代码(带…...
2023年度总结:技术沉淀、持续学习
2023年度总结:技术沉淀、持续学习 一、引言 今年是我毕业的第二个年头,也是完整的一年,到了做年终总结的时候了 这一年谈了女朋友,学习了不少技术,是充实且美好的一年! 首先先看年初定的小目标…...
Unity 利用UGUI之Slider制作进度条
在Unity中使用Slider和Text组件可以制作简单的进度条。 首先在场景中右键->UI->Slider,新建一个Slider组件: 同样方法新建一个Text组件,最终如图: 创建一个进度模拟脚本,Slider_Progressbar.cs using System.C…...
OCS2 入门教程(四)- 机器人示例
系列文章目录 前言 OCS2 包含多个机器人示例。我们在此简要讨论每个示例的主要特点。 System State Dim. Input Dim. Constrained Caching Double Integrator 2 1 No No Cartpole 4 1 Yes No Ballbot 10 3 No No Quadrotor 12 4 No No Mobile Manipul…...
FreeRTOS学习第6篇–任务状态挂起恢复删除等操作
目录 FreeRTOS学习第6篇--任务状态挂起恢复删除等操作任务的状态设计实验IRReceiver_Task任务相关代码片段实验现象本文中使用的测试工程 FreeRTOS学习第6篇–任务状态挂起恢复删除等操作 本文目标:学习与使用FreeRTOS中的几项操作,有挂起恢复删除等操作…...
BLE Mesh蓝牙组网技术详细解析之Access Layer访问层(六)
目录 一、什么是BLE Mesh Access Layer访问层? 二、Access payload 2.1 Opcode 三、Access layer behavior 3.1 Access layer发送消息的流程 3.2 Access layer接收消息的流程 3.3 Unacknowledged and acknowledged messages 3.3.1 Unacknowledged message …...
Netlink 通信机制
文章目录 前言一、Netlink 介绍二、示例代码参考资料 前言 一、Netlink 介绍 Netlink套接字是用以实现用户进程与内核进程通信的一种特殊的进程间通信(IPC) ,也是网络应用程序与内核通信的最常用的接口。 在Linux 内核中,使用netlink 进行应用与内核通信的应用有…...
2024.1.8每日一题
LeetCode 回旋镖的数量 447. 回旋镖的数量 - 力扣(LeetCode) 题目描述 给定平面上 n 对 互不相同 的点 points ,其中 points[i] [xi, yi] 。回旋镖 是由点 (i, j, k) 表示的元组 ,其中 i 和 j 之间的距离和 i 和 k 之间的欧式…...
看了致远OA的表单设计后的思考
更多ruoyi-nbcio功能请看演示系统 gitee源代码地址 前后端代码: https://gitee.com/nbacheng/ruoyi-nbcio 演示地址:RuoYi-Nbcio后台管理系统 更多nbcio-boot功能请看演示系统 gitee源代码地址 后端代码: https://gitee.com/nbacheng/n…...
mmdetection训练自己的数据集
mmdetection训练自己的数据集 这里写目录标题 mmdetection训练自己的数据集一: 环境搭建二:数据集格式转换(yolo转coco格式)yolo数据集格式coco数据集格式yolo转coco数据集格式yolo转coco数据集格式的代码 三: 训练dataset数据文件配置config…...
MySQL取出N列里最大or最小的一个数据
如题,现在有3列,都是数字类型,要取出这3列里最大或最小的的一个数字 -- N列取最小 SELECT LEAST(temperature_a,temperature_b,temperature_c) min FROM infrared_heat-- N列取最大 SELECT GREATEST(temperature_a,temperature_b,temperat…...
编写.NET的Dockerfile文件构建镜像
创建一个WebApi项目,并且创建一个Dockerfile空文件,添加以下代码,7.0代表的你项目使用的SDK的版本,构建的时候也需要选择好指定的镜像tag FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base WORKDIR /app EXPOSE 80 EXPOSE 443F…...
【C语言】浙大版C语言程序设计(第三版) 练习7-4 找出不是两个数组共有的元素
前言 最近在学习浙大版的《C语言程序设计》(第三版)教材,同步在PTA平台上做对应的练习题。这道练习题花了比较长的时间,于是就写篇博文记录一下我的算法和代码。 2024.01.03 题目 练习7-4 找出不是两个数组共有的元素 作者 张彤…...
7.27 SpringBoot项目实战 之 整合Swagger
文章目录 前言一、Maven依赖二、编写Swagger配置类三、编写接口配置3.1 控制器Controller 配置描述3.2 接口API 配置描述3.3 参数配置描述3.4 忽略API四、全局参数配置五、启用增强功能六、调试前言 在我们实现了那么多API以后,进入前后端联调阶段,需要给前端同学提供接口文…...
创建第一个SpringMVC项目,入手必看!
文章目录 创建第一个SpringMVC项目,入手必看!1、新建一个maven空项目,在pom.xml中设置打包为war之前,右击项目添加web框架2、如果点击右键没有添加框架或者右击进去后没有web框架,点击左上角file然后进入项目结构在模块…...
go 切片长度与容量的区别
切片的声明 切片可以看成是数组的引用(实际上切片的底层数据结构确实是数组)。在 Go 中,每个数组的大小是固定的,不能随意改变大小,切片可以为数组提供动态增长和缩小的需求,但其本身并不存储任何数据。 …...
回归和分类区别
回归任务(Regression): 特点: 输出是连续值,通常是实数。任务目标是预测或估计一个数值。典型应用包括房价预测、销售额预测、温度预测等。 目标: 最小化预测值与真实值之间的差异,通常使用…...
ESP32 RMT驱动WS2812灯条:从官方例程到彩虹跑马灯,一份避坑指南
ESP32 RMT驱动WS2812灯条:从基础到高级特效的实战指南 当你在深夜的工作室里,看着一排WS2812灯条随着代码的节奏流淌出绚丽的色彩,那种将数字信号转化为视觉艺术的成就感,正是嵌入式开发的魅力所在。ESP32的RMT外设与WS2812的结合…...
如何用 JavaScript 实现单选式盒子颜色切换(点击高亮,其余复原)
本文详解如何使用原生 JavaScript 实现“单选高亮”效果:点击任一 .box 元素时,仅该元素变为指定颜色,其余同组元素自动恢复默认背景色。 本文详解如何使用原生 javascript 实现“单选高亮”效果:点击任一 .box 元素时&#…...
从握手协议到流水线优化:一个真实案例讲透前向/后向插流水在AXI总线设计中的应用
从握手协议到流水线优化:一个真实案例讲透前向/后向插流水在AXI总线设计中的应用 在图像处理IP的设计中,数据接收接口的性能往往成为整个系统的瓶颈。想象这样一个场景:上游摄像头模块以突发方式发送高分辨率图像数据,而下游处理单…...
你的数字记忆银行:用WeChatMsg永久保存微信聊天记录
你的数字记忆银行:用WeChatMsg永久保存微信聊天记录 【免费下载链接】WeChatMsg 提取微信聊天记录,将其导出成HTML、Word、CSV文档永久保存,对聊天记录进行分析生成年度聊天报告 项目地址: https://gitcode.com/GitHub_Trending/we/WeChatM…...
InstantSearch 高级技巧:10个提升搜索性能的实用方法
InstantSearch 高级技巧:10个提升搜索性能的实用方法 【免费下载链接】instantsearch ⚡️ Libraries for building performant and instant search and recommend experiences with Algolia. Compatible with JavaScript, TypeScript, React and Vue. 项目地址: …...
告别手动编译!用ODBC桥接让QT5.14.2轻松操作MySQL8数据库
告别手动编译!用ODBC桥接让QT5.14.2轻松操作MySQL8数据库 在QT开发中连接MySQL数据库时,许多开发者都会遇到一个令人头疼的问题:需要手动编译MySQL驱动。这不仅耗时耗力,还容易因版本不匹配导致各种兼容性问题。本文将介绍一种更…...
如何快速使用RPGMakerDecrypter:解密RPG Maker加密资源的完整指南
如何快速使用RPGMakerDecrypter:解密RPG Maker加密资源的完整指南 【免费下载链接】RPGMakerDecrypter Tool for decrypting and extracting RPG Maker XP, VX and VX Ace encrypted archives and MV and MZ encrypted files. 项目地址: https://gitcode.com/gh_m…...
别再死磕COE文件了!Vivado里用$readmemb/h给RAM上电初始化的正确姿势(附避坑指南)
Vivado中RAM初始化的终极方案:告别COE文件,掌握$readmemb/h的正确用法 在FPGA开发中,RAM初始化是个看似简单却暗藏玄机的环节。许多开发者都曾陷入这样的困境:按照官方文档和主流教程使用COE文件初始化IP核,结果上电后…...
代码随想录算法训练营 Day40 | 动态规划 part13
647. 回文子串 给你一个字符串 s ,请你统计并返回这个字符串中 回文子串 的数目。 回文字符串 是正着读和倒过来读一样的字符串。 子字符串 是字符串中的由连续字符组成的一个序列。 class Solution { public:int countSubstrings(string s) {int n s.size();vecto…...
IIR滤波器设计实战:从Butterworth到参数调优的完整指南
IIR滤波器设计实战:从Butterworth到参数调优的完整指南 在数字信号处理领域,IIR(无限冲激响应)滤波器因其高效的频率选择特性而广受欢迎。与FIR滤波器相比,IIR滤波器能够在相同性能要求下使用更少的计算资源ÿ…...


