[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): 特点: 输出是连续值,通常是实数。任务目标是预测或估计一个数值。典型应用包括房价预测、销售额预测、温度预测等。 目标: 最小化预测值与真实值之间的差异,通常使用…...
如何利用Blender MMD Tools实现跨平台3D模型与动画工作流
如何利用Blender MMD Tools实现跨平台3D模型与动画工作流 【免费下载链接】blender_mmd_tools MMD Tools is a blender addon for importing/exporting Models and Motions of MikuMikuDance. 项目地址: https://gitcode.com/gh_mirrors/bl/blender_mmd_tools 副标题&am…...
C#实战:基于WebAPI与Modbus构建EMS核心采集服务
1. 为什么需要EMS核心采集服务? 在工业现场,我们经常会遇到几十台甚至上百台智能电表、传感器等设备需要监控。这些设备可能来自不同厂家,使用不同的通信协议,数据格式也各不相同。想象一下,如果每个设备都需要单独开发…...
深度解析ConcurrentHashMap设计演进:从分段锁到无锁化的并发之路
在Java并发编程领域,ConcurrentHashMap绝对是“并发容器扛鼎之作”——它既解决了HashMap并发环境下的数据不一致(死循环、数据丢失)问题,又突破了Hashtable全表锁的性能瓶颈,成为高并发场景下K-V存储的首选。自JDK1.5…...
从零掌握ComfyUI-WanVideoWrapper:AI视频制作工具实战指南
从零掌握ComfyUI-WanVideoWrapper:AI视频制作工具实战指南 【免费下载链接】ComfyUI-WanVideoWrapper 项目地址: https://gitcode.com/GitHub_Trending/co/ComfyUI-WanVideoWrapper 在数字内容创作领域,视频生成工具正经历着前所未有的技术革新。…...
OpenClaw内容创作流:nanobot辅助生成技术文章草稿
OpenClaw内容创作流:nanobot辅助生成技术文章草稿 1. 从灵感到初稿的自动化尝试 去年冬天,当我面对第五篇技术博客的空白文档时,突然意识到一个残酷事实:写作最耗时的不是码字本身,而是前期资料搜集和结构搭建。就像…...
OpenClaw技能市场盘点:10个适配Qwen3.5-4B-Claude的实用工具
OpenClaw技能市场盘点:10个适配Qwen3.5-4B-Claude的实用工具 1. 为什么需要关注技能适配性 当我第一次在OpenClaw上尝试安装第三方技能时,遇到了一个典型问题:技能安装成功了,但执行时模型总是输出"我不明白这个请求"…...
SAP FICO财务账期管理实战:关键配置与月结操作指南
1. SAP FICO财务账期管理基础概念 财务账期管理是SAP FICO模块中最基础也最重要的功能之一。简单来说,它就像财务部门的"门禁系统",控制着哪些会计凭证能在特定时间段被录入系统。想象一下,如果超市收银台没有营业时间限制…...
这份榜单够用!盘点2026年用户挚爱的一键生成论文工具
一天写完毕业论文在2026年已不再是天方夜谭。以下是2026年最炸裂、实测能大幅提速的一键生成论文工具,覆盖选题构思、文献综述、数据整理、格式排版等核心场景,高效搞定论文不再只是梦想。 一、全流程王者:一站式搞定论文全链路(一…...
Kali Linux 2026.1 发布 (2026 主题 BackTrack 模式) - 领先的渗透测试发行版
Kali Linux 2026.1 发布 (2026 主题 & BackTrack 模式) - 领先的渗透测试发行版 The most advanced Penetration Testing Distribution 请访问原文链接:https://sysin.org/blog/kali-linux/ 查看最新版。原创作品,转载请保留出处。 作者主页&…...
Qwen3-ASR-1.7B部署案例:AI初创公司低成本构建ASR SaaS服务
Qwen3-ASR-1.7B部署案例:AI初创公司低成本构建ASR SaaS服务 想象一下,你是一家AI初创公司的技术负责人,老板给你下了个任务:两周内,为公司的新产品上线一个语音转文字(ASR)功能。要求是识别要准…...


