[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): 特点: 输出是连续值,通常是实数。任务目标是预测或估计一个数值。典型应用包括房价预测、销售额预测、温度预测等。 目标: 最小化预测值与真实值之间的差异,通常使用…...
基于大模型的 UI 自动化系统
基于大模型的 UI 自动化系统 下面是一个完整的 Python 系统,利用大模型实现智能 UI 自动化,结合计算机视觉和自然语言处理技术,实现"看屏操作"的能力。 系统架构设计 #mermaid-svg-2gn2GRvh5WCP2ktF {font-family:"trebuchet ms",verdana,arial,sans-…...
Linux链表操作全解析
Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表?1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…...
MongoDB学习和应用(高效的非关系型数据库)
一丶 MongoDB简介 对于社交类软件的功能,我们需要对它的功能特点进行分析: 数据量会随着用户数增大而增大读多写少价值较低非好友看不到其动态信息地理位置的查询… 针对以上特点进行分析各大存储工具: mysql:关系型数据库&am…...
相机Camera日志实例分析之二:相机Camx【专业模式开启直方图拍照】单帧流程日志详解
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了: 这一篇我们开始讲: 目录 一、场景操作步骤 二、日志基础关键字分级如下 三、场景日志如下: 一、场景操作步骤 操作步…...
新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案
随着新能源汽车的快速普及,充电桩作为核心配套设施,其安全性与可靠性备受关注。然而,在高温、高负荷运行环境下,充电桩的散热问题与消防安全隐患日益凸显,成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...
稳定币的深度剖析与展望
一、引言 在当今数字化浪潮席卷全球的时代,加密货币作为一种新兴的金融现象,正以前所未有的速度改变着我们对传统货币和金融体系的认知。然而,加密货币市场的高度波动性却成为了其广泛应用和普及的一大障碍。在这样的背景下,稳定…...
鸿蒙DevEco Studio HarmonyOS 5跑酷小游戏实现指南
1. 项目概述 本跑酷小游戏基于鸿蒙HarmonyOS 5开发,使用DevEco Studio作为开发工具,采用Java语言实现,包含角色控制、障碍物生成和分数计算系统。 2. 项目结构 /src/main/java/com/example/runner/├── MainAbilitySlice.java // 主界…...
华为OD机考-机房布局
import java.util.*;public class DemoTest5 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseSystem.out.println(solve(in.nextLine()));}}priv…...
深度剖析 DeepSeek 开源模型部署与应用:策略、权衡与未来走向
在人工智能技术呈指数级发展的当下,大模型已然成为推动各行业变革的核心驱动力。DeepSeek 开源模型以其卓越的性能和灵活的开源特性,吸引了众多企业与开发者的目光。如何高效且合理地部署与运用 DeepSeek 模型,成为释放其巨大潜力的关键所在&…...
海云安高敏捷信创白盒SCAP入选《中国网络安全细分领域产品名录》
近日,嘶吼安全产业研究院发布《中国网络安全细分领域产品名录》,海云安高敏捷信创白盒(SCAP)成功入选软件供应链安全领域产品名录。 在数字化转型加速的今天,网络安全已成为企业生存与发展的核心基石,为了解…...


