C++ 使用 ffmpeg 解码本地视频并获取每帧的YUV数据
一、简介
FFmpeg 是一个开源的多媒体处理框架,非常适用于处理音视频的录制、转换、流化和播放。
二、代码
示例代码读取一个本地视频文件,解码并将二进制文件保存下来。
注意: 代码中仅展示了 YUV420P 格式,其他 NV12/NV21 等格式可相应修改。
1. yuv_decoder.cpp
#include <iostream>
#include <iomanip>
#include <fstream>extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/imgutils.h>
#include <libavutil/rational.h>
}// 打印媒体信息表格
void print_media_info(AVFormatContext* fmt_ctx, int video_stream) {std::cout << "\n\033[1;36m======= Media Stream Analysis =======\033[0m\n";std::cout << std::left << std::setw(20) << "Container Format:" << fmt_ctx->iformat->long_name << " (" << fmt_ctx->iformat->name << ")\n";std::cout << std::setw(20) << "Duration:" << (fmt_ctx->duration / AV_TIME_BASE) << " seconds\n";std::cout << std::setw(20) << "Bitrate:" << (fmt_ctx->bit_rate / 1000) << " Kbps\n";if (video_stream != -1) {AVStream* stream = fmt_ctx->streams[video_stream];std::cout << "\n\033[1;33m[Video Stream #" << video_stream << "]\033[0m\n";std::cout << std::setw(20) << "Codec:" << avcodec_get_name(stream->codecpar->codec_id) << " (ID:" << stream->codecpar->codec_id << ")\n";std::cout << std::setw(20) << "Resolution:" << stream->codecpar->width << "x" << stream->codecpar->height << "\n";std::cout << std::setw(20) << "Pixel Format:" << av_get_pix_fmt_name((AVPixelFormat)stream->codecpar->format) << "\n";std::cout << std::setw(20) << "Framerate:" << av_q2d(stream->avg_frame_rate) << " fps\n";std::cout << std::setw(20) << "Timebase:" << stream->time_base.num << "/" << stream->time_base.den << "\n";}std::cout << "\033[1;36m=====================================\033[0m\n\n";
}// 验证YUV420P格式
void validate_yuv420p(const AVFrame* frame) {if (frame->format != AV_PIX_FMT_YUV420P || frame->format != AV_PIX_FMT_YUVJ420P) {std::cerr << "\033[1;31mERROR: Unexpected pixel format: " << av_get_pix_fmt_name((AVPixelFormat)frame->format) << "\033[0m" << std::endl;}if (frame->linesize[1] != frame->linesize[2]) {std::cerr << "\033[1;31mERROR: UV plane stride mismatch: U=" << frame->linesize[1] << " V=" << frame->linesize[2]<< "\033[0m" << std::endl;}
}int main(int argc, char** argv) {if (argc < 2) {std::cerr << "Usage: " << argv[0] << " <input_file>" << std::endl;return -1;}AVFormatContext* fmt_ctx = nullptr;if (avformat_open_input(&fmt_ctx, argv[1], nullptr, nullptr) != 0) {std::cerr << "Could not open input file" << std::endl;return -1;}if (avformat_find_stream_info(fmt_ctx, nullptr) < 0) {std::cerr << "Could not find stream information" << std::endl;avformat_close_input(&fmt_ctx);return -1;}int video_stream_index = -1;for (unsigned int i = 0; i < fmt_ctx->nb_streams; i++) {if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {video_stream_index = i;break;}}if (video_stream_index == -1) {std::cerr << "Could not find video stream" << std::endl;avformat_close_input(&fmt_ctx);return -1;}print_media_info(fmt_ctx, video_stream_index); // 打印媒体信息const AVCodec* codec = avcodec_find_decoder(fmt_ctx->streams[video_stream_index]->codecpar->codec_id);if (!codec) {std::cerr << "Unsupported codec" << std::endl;avformat_close_input(&fmt_ctx);return -1;}AVCodecContext* codec_ctx = avcodec_alloc_context3(codec);if (avcodec_parameters_to_context(codec_ctx, fmt_ctx->streams[video_stream_index]->codecpar) < 0) {std::cerr << "Could not initialize codec context" << std::endl;avcodec_free_context(&codec_ctx);avformat_close_input(&fmt_ctx);return -1;}if (avcodec_open2(codec_ctx, codec, nullptr) < 0) {std::cerr << "Could not open codec" << std::endl;avcodec_free_context(&codec_ctx);avformat_close_input(&fmt_ctx);return -1;}std::cout << "\033[1;32mDecoder Initialized Successfully!\033[0m\n";std::cout << "Output Format: " << av_get_pix_fmt_name(codec_ctx->pix_fmt) << "\n\n";AVFrame* frame = av_frame_alloc();AVPacket* pkt = av_packet_alloc();int frame_count = 0;while (av_read_frame(fmt_ctx, pkt) >= 0) {if (pkt->stream_index == video_stream_index) {if (avcodec_send_packet(codec_ctx, pkt) < 0) {std::cerr << "Error sending packet" << std::endl;break;}int ret = 0;while (ret >= 0) {ret = avcodec_receive_frame(codec_ctx, frame);if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) break;std::cout << "Decoded Frame #" << frame_count << " (PTS: " << frame->pts << ", Format: " << av_get_pix_fmt_name((AVPixelFormat)frame->format)<< ")\r" << std::flush;validate_yuv420p(frame); // 格式校验,可略过char filename[20];snprintf(filename, sizeof(filename), "%04d.yuv", frame_count++);std::ofstream file(filename, std::ios::binary);if (file.is_open()) {// 写入Y平面for (int y = 0; y < frame->height; y++) {file.write(reinterpret_cast<char*>(frame->data[0] + y * frame->linesize[0]), frame->width);}// 写入U平面for (int y = 0; y < frame->height / 2; y++) {file.write(reinterpret_cast<char*>(frame->data[1] + y * frame->linesize[1]), frame->width / 2);}// 写入V平面for (int y = 0; y < frame->height / 2; y++) {file.write(reinterpret_cast<char*>(frame->data[2] + y * frame->linesize[2]), frame->width / 2);}}}}av_packet_unref(pkt);}std::cout << "\n\n\033[1;32mDecoding Completed! Total frames: " << frame_count << "\033[0m\n";av_packet_free(&pkt);av_frame_free(&frame);avcodec_free_context(&codec_ctx);avformat_close_input(&fmt_ctx);return 0;
}
2. CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(FFmpegYUVDecoder)set(CMAKE_CXX_STANDARD 11)find_package(PkgConfig REQUIRED)
pkg_check_modules(AVCODEC REQUIRED libavcodec)
pkg_check_modules(AVFORMAT REQUIRED libavformat)
pkg_check_modules(AVUTIL REQUIRED libavutil)add_executable(yuv_decoder yuv_decoder.cpp)target_include_directories(yuv_decoder PRIVATE${AVCODEC_INCLUDE_DIRS}${AVFORMAT_INCLUDE_DIRS}${AVUTIL_INCLUDE_DIRS}
)target_link_libraries(yuv_decoder${AVCODEC_LIBRARIES}${AVFORMAT_LIBRARIES}${AVUTIL_LIBRARIES}
)
依赖的库在安装 ffmpeg 后应该都有,用到的就 libavcodec,libavformat,libavutil三个。
3. 文件目录结构
|—— CMakeLists.txt
|—— yuv_decoder.cpp
三、编译和运行
1. 安装 ffmpeg
如果还没有安装过,可以如下安装
# Ubuntu/Debian
sudo apt update && sudo apt install ffmpeg
# 查看版本号
ffmpeg -version
如果缺少某个库,可以执行
sudo apt install libavcodec-dev libavformat-dev libavutil-dev
2. 编译
mkdir build
cd build
cmake ..
make -j4
3. 运行
只有一个输入参数,及输入视频的文件路径,输出路径默认在当前路径。
./yuv_decoder input.mp4
4. 查看
可以使用 ffplay 命令查看保持的 yuv 数据是否正确。以 1920x1080 大小,yuv420p格式为例:
ffplay -video_size 1920x1080 -pixel_format yuv420p 0001.yuv
相关文章:
C++ 使用 ffmpeg 解码本地视频并获取每帧的YUV数据
一、简介 FFmpeg 是一个开源的多媒体处理框架,非常适用于处理音视频的录制、转换、流化和播放。 二、代码 示例代码读取一个本地视频文件,解码并将二进制文件保存下来。 注意: 代码中仅展示了 YUV420P 格式,其他 NV12/NV2…...
分布式微服务系统架构第143集:pom文件
加群联系作者vx:xiaoda0423 仓库地址:https://webvueblog.github.io/JavaPlusDoc/ https://1024bat.cn/ https://github.com/webVueBlog/fastapi_plus https://webvueblog.github.io/JavaPlusDoc/ ✅ 各字段说明及是否可改 字段名说明是否可修改修改建议…...

2.0 阅读方法论与知识总结
引言 本文将详细分析考研英语阅读做题步骤,并对方法论进行总结,最后通过真题练习巩固方法。 一、做题步骤 所有技巧都建立在精读真题的基础上!建议按以下节奏复习: 1️⃣ 做题 先看题干了解文章大致主旨(看看有没有…...

5. Qt中.pro文件(1)
本节主要讲.pro文件的作用和一些相关基础知识与操作。 本文部分ppt、视频截图原链接:[萌马工作室的个人空间-萌马工作室个人主页-哔哩哔哩视频] 1 PRO文件 1.1 pro文件作用 添加需要用到的QT模块,如通过QT module_name来添加需要用到的Qt模块。指定生…...
第八部分:第三节 - 事件处理:响应顾客的操作
用户与界面的互动是通过事件触发的,比如点击按钮、在输入框中输入文本、提交表单等。React 提供了一套跨浏览器的事件系统,让我们可以在组件中方便地处理这些事件。这就像点餐系统需要能够识别顾客的各种操作(按键、滑动屏幕)并作…...
共识机制全景图:PoW、PoS 与 DAG 的技术对比
目录 共识机制全景图:PoW、PoS 与 DAG 的技术对比 🧱 一、工作量证明(PoW) 原理概述 优点 缺点 示例代码(Python) 💰 二、权益证明(PoS) 原理概述 优点 缺点 …...
学习笔记085——Spring Data JPA笔记
1、什么是Spring Data JPA? Spring Data JPA 是 Spring 框架的一个子项目,它简化了基于 JPA (Java Persistence API) 的数据访问层的实现。它通过减少样板代码和提供默认实现,让开发者能够更快速地构建数据访问层。 1.1、主要特点 减少样板…...
可视化大屏工具对比:GoView、DataRoom、积木JimuBI、Metabase、DataEase、Apache Superset 与 Grafana
可视化大屏工具对比:GoView、DataRoom、积木JimuBI、Metabase、DataEase、Apache Superset 与 Grafana 在当今数据驱动的业务环境中,可视化大屏已成为企业展示数据洞察的重要工具。本文将从功能、部署、分享、参数化大屏四个维度对主流可视化大屏工具进…...
内网穿透:打破网络限制的利器!深入探索和简单实现方案
在如今这个数字化时代,网络已经成为我们生活和工作中不可或缺的一部分。但你是否遇到过这样的困扰:在家办公时,想要访问公司内部的文件服务器,却因为网络限制无法连接;搭建了一个炫酷的个人网站,却只能在自…...
如何选择合适的哈希算法以确保数据安全?
在当今数据爆炸的时代,从个人身份信息到企业核心商业数据,从金融交易记录到医疗健康档案,数据已然成为数字世界的核心资产。而哈希算法作为数据安全领域的基石,犹如为数据资产配备的坚固锁具,其重要性不言而喻。然而&a…...

简数采集技巧之快速获取特殊链接网址URL方法
简数采集器列表页提取器的默认配置规则:获取a标签的href属性值作为采集的链接网址,对于大部分网站都是适用的; 但有些网站不使用a标签作为链接跳转,而用javascript的onclick事件替代,那列表页提取器的默认规则将无法获…...
React 性能监控与错误上报
核心问题与技术挑战 现代 React 应用随着业务复杂度增加,性能问题和运行时错误日益成为影响用户体验的关键因素。没有可靠的监控与错误上报机制,我们将陷入被动修复而非主动预防的困境。 性能指标体系与错误分类 关键性能指标定义 // performance-me…...

AI 如何改变软件文档生产方式?
现代软件工程中的文档革命:从附属品到核心组件的范式升级 在数字化转型浪潮席卷全球的当下,软件系统的复杂度与规模呈现指数级增长。据Gartner最新研究显示,超过67%的企业软件项目延期或超预算的根本原因可追溯至文档系统的缺陷。这一现象在…...

激光干涉仪:解锁协作机器人DD马达的精度密码
在工业4.0的浪潮中,协作机器人正以惊人的灵活性重塑生产线——它们与工人并肩作业,精准搬运零件,完成精密装配。还能协同医生完成手术,甚至制作咖啡。 标准的协作机器人关节模组由角度编码器、直驱电机(DD马达)、驱动器、谐波减速…...
Windows如何定制键盘按键
Windows如何定制键盘按键 https://blog.csdn.net/qq_33204709/article/details/129010351...
go语言学习 第1章:走进Golang
第1章:走进Golang 一、Golang简介 Go语言(又称Golang)是由Google的Robert Griesemer、Rob Pike及Ken Thompson开发的一种开源编程语言。它诞生于2007年,2009年11月正式开源。Go语言的设计初衷是为了在不损失应用程序性能的情况下…...
使用Prometheus+Grafana+Alertmanager+Webhook-dingtalk搭建监控平台
一、监控平台介绍 1.监控平台简述普罗米修斯四件套,分别为Prometheus、Grafana、Alertmanager、Webhook-DingTalk。Prometheus一套开源的监控&报警&时间序列数据库的组合,由SoundCloud公司开发,广泛用于云原生环境和容器化应用的监控和性能分析。其提供了通用的数据…...

HOPE800系列变频器安装到快速调试的详细操作说明
以下是HOPE800系列变频器从安装到调试的详细操作说明及重要参数设置,适用于工程技术人员或具备电气基础的操作人员。请严格遵循安全规范操作。 以下面电机铭牌为例: HOPE800变频器安装与调试指南** (安全第一!操作前务必断电并确…...

vCenter与ESXi主机每分钟周期性断连修复
问题概述 最近我的测试服务器借给客户用作临时中转,仅更改了ESXi的管理IP,设备拿回来改回原来IP,vCenter开启后重新接收证书,主机和所有VM管理运行正常,跑着跑着发现主机和vCenter会频繁断开连接后又马上自动恢复&…...
JMeter函数整理
"_csvRead"函数 csvRead函数是从外部读取参数,csvRead函数可以从一个文件中读取多个参数。 下面具体讲一下如何使用csvread函数: 1.新建一个csv或者text文件,里面保存要读取的参数,每个参数间用逗号相隔。每行表示每一组…...
Sql Server 中常用语句
1.创建用户数据库 --创建数据库 use master --切换到master数据库 go-- 终止所有与SaleManagerDB数据库的连接 alter database SaleManagerDB set single_user with rollback immediate goif exists (select * from sysdatabases where nameSaleManagerDB) drop database Sal…...

web3-区块链困境破解指南:从数字化签名到Rollup 到分片
web3-区块链三难困境破解指南:从数字化签名到Rollup 到分片 数字化签名 实体的签名:将交易和签名者绑定在一起 在数字世界的问题是: 任何人都可以从任一文档复制Bob的签名放到自己想放的地方。 解决方案:让签名由文件来决定 b…...

李飞飞World Labs开源革命性Web端3D渲染器Forge!3D高斯溅射技术首次实现全平台流畅运行
在AI与3D技术深度融合的今天,李飞飞领衔的World Labs团队再次成为行业焦点。今日,他们正式开源了Forge——一款专为Web端设计的3D高斯溅射(3D Gaussian Splatting)渲染器,不仅支持THREE.js生态,更能在手机、…...

小鹏汽车5月交付新车33525台 同比增长230%
6月1日,小鹏汽车公布5月交付数据,5月小鹏交付新车33,525台,同比增长230%,与4月交付35,045台相比下降4.3%,已连续7个月交付量突破30,000台。2025年1-5月,小鹏汽车累计交付新车162,578台,同比增长…...

OpenCV——Mat类及常用数据结构
Mat类及常用数据结构 一、Mat类简介1.1、矩阵头1.2、矩阵的数据类型1.3、Mat的子类 二、矩阵数据的存储2.1、单通道2.2、多通道 三、创建矩阵的方法3.1、静态方法创建3.2、构造方法创建3.3、读取图像文件创建3.4、克隆创建 四、获取矩阵信息五、矩阵相关操作5.1、获取/修改像素…...
深入解析FutureTask:原理与实战
我们来深入解析 FutureTask。下面将从它们的用法开始,逐步深入到底层实现、方法和接口设计,并探讨它们对于我们自己设计实现以及其他编程实践的学习意义。 主要就是放入等待队列(CAS操作一个链表头),完成任务的线程唤…...

每天总结一个html标签——Audio音频标签
Audio标签 文章目录 Audio标签一、audio标签的定义与介绍1. 定义介绍2. 语法3. 支持的格式4.文本提示 二、audio标签的HTML属性1. autoplay2. loop3. muted4. preload 三、audio标签的常用DOM属性四、audio标签的常用事件四、默认样式五、自定义样式1. 示例2. 代码 六、播放 m3…...
使用 React Native 开发鸿蒙(HarmonyOS)运动健康类应用的系统化准备工作
⚙️ 一、环境与工具准备 双环境搭建 React Native 环境:安装 Node.js(≥18.x)、JDK(≥11)、Yarn。鸿蒙开发环境: 下载 DevEco Studio 4.0 及 HarmonyOS SDK;配…...

web3-Remix部署智能合约到“荷兰式”拍卖及以太坊gas费机制细讲
web3-Remix部署智能合约到“荷兰式”拍卖及以太坊gas费机制细讲 一、使用Remix演示智能合约部署 智能合约的代码编写一般都是在Remix上,Remix的好处的话就是可以在浏览器中快速开发和部署合约,无需在本地安装任何程序,十分适合新手。 对应…...

网络编程及原理(一)
目录 一 . 独立模式与网络互联 二 . 局域网 —— LAN (1)基于网线直连 (2)基于集线器组建 (3)基于交换机组建 (4)基于交换机和路由器组建 三 . 广域网 —— WAN 四 …...