音视频开发—FFmpeg处理流数据的基本概念详解

文章目录

    • 多媒体文件的基本概念
    • 相关重要的结构体
    • 操作数据流的基本步骤
      • 1.解复用(Demuxing)
      • 2.获取流(Stream)
      • 3. 读取数据包(Packet)
      • 4. 释放资源(Free Resources)
      • 完整示例

多媒体文件的基本概念

  • 多媒体文件其实是个容器

多媒体文件(如MP4、MKV、AVI等)实际上是一个容器格式。容器的作用是将不同类型的数据(如视频、音频、字幕等)封装在一个文件中,方便管理和播放。每种容器格式都有自己的规范,定义了如何组织和存储这些不同类型的数据。

  • 在容器里有很多流

在多媒体容器文件中,可以包含多个不同的流。每个流代表一种媒体数据,例如视频流、音频流、字幕流等。一个典型的多媒体文件通常至少包含一个视频流和一个音频流,但也可以包含多个视频流、多个音频流和其他类型的流(如字幕、章节信息、元数据等)。

  • 每种流是由不同的编码器编码实现的

每个流的数据在存储之前需要经过编码。编码器(如H.264、AAC、MP3等)将原始的多媒体数据(如未压缩的视频和音频)转换成压缩格式,以减少存储空间和传输带宽。不同的编码器适用于不同类型的数据和使用场景。例如,视频流可能使用H.264编码器,音频流可能使用AAC编码器。

  • 从流中读出的数据叫做包

在流中,数据被分成一个个的数据包(packet)。每个包包含一段编码后的多媒体数据,以及一些元数据(如时间戳、流的标识等)。在解码和播放时,播放器会从容器文件中读取这些数据包,并将其传递给相应的解码器进行解码。

  • 在一个包中包含多个帧

数据包中的内容进一步细分为帧。帧是视频或音频数据的最小单位。例如,在视频流中,每一帧代表一个静止的图像,连续播放这些图像可以形成视频。在音频流中,每一帧代表一段音频采样数据。帧的数量和类型(如关键帧、预测帧等)取决于编码器的工作方式和编码参数。

相关重要的结构体

  • AVFormatContext 结构体

AVFormatContext 是 FFmpeg 中用于描述多媒体文件或流的上下文结构体。它包含了文件格式、输入输出协议、文件信息以及多个流等信息。

  • AVStream 结构体

AVStream 是 FFmpeg 中用于描述多媒体文件中的一个流(如视频流、音频流、字幕流等)的结构体。每个 AVStream 包含了流的编解码信息、时间基准等。

  • AVPacket

AVPacket 是 FFmpeg 中用于描述存储在容器中的多媒体数据包的结构体。数据包是编码后的数据,包含一组帧。

操作数据流的基本步骤

在这里插入图片描述

1.解复用(Demuxing)

解复用是指从多媒体容器中提取出独立的音频、视频和其他流的过程。在FFmpeg中,解复用通过打开文件并解析文件头部信息来实现。

主要步骤

  • 注册所有格式和编解码器: 使用 av_register_all() 注册FFmpeg支持的所有格式和编解码器(FFmpeg 4.x及以前版本需要,FFmpeg 5.0及以后版本不需要)。
  • 打开输入文件: 使用 avformat_open_input() 打开输入文件。
  • 读取文件头部信息: 使用 avformat_find_stream_info() 读取文件头部信息。

示例代码

AVFormatContext *fmt_ctx = NULL;
int ret;

// 注册所有格式和编解码器(FFmpeg 4.x及以前版本需要)
av_register_all();

// 打开输入文件
if ((ret = avformat_open_input(&fmt_ctx, "input.mp4", NULL, NULL)) < 0) {
    av_log(NULL, AV_LOG_ERROR, "Cannot open input file: %s\n", av_err2str(ret));
    return ret;
}

// 读取文件头部信息
if ((ret = avformat_find_stream_info(fmt_ctx, NULL)) < 0) {
    av_log(NULL, AV_LOG_ERROR, "Cannot find stream information: %s\n", av_err2str(ret));
    avformat_close_input(&fmt_ctx);
    return ret;
}

// 打印输入文件的信息
av_dump_format(fmt_ctx, 0, "input.mp4", 0);

2.获取流(Stream)

获取流是指从多媒体文件中提取出各个独立的流,例如音频流和视频流。每个流包含了相关的编解码信息。

主要步骤

  • 查找音频和视频流: 遍历 AVFormatContext 中的流,查找音频和视频流。
  • 打印流信息: 打印每个流的信息。

示例代码

AVStream *video_stream = NULL;
AVStream *audio_stream = NULL;

for (unsigned int i = 0; i < fmt_ctx->nb_streams; i++) {
    AVStream *stream = fmt_ctx->streams[i];
    if (stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
        video_stream = stream;
    } else if (stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
        audio_stream = stream;
    }
}

if (!video_stream && !audio_stream) {
    av_log(NULL, AV_LOG_ERROR, "No video or audio stream found\n");
    avformat_close_input(&fmt_ctx);
    return -1;
}

3. 读取数据包(Packet)

读取数据包是指从文件中逐个读取编码后的数据包。数据包可以包含音频、视频或其他类型的数据。

主要步骤

  • 初始化数据包: 使用 av_init_packet() 初始化数据包。
  • 读取数据包: 使用 av_read_frame() 读取数据包。
  • 处理数据包: 根据数据包所属的流进行相应处理。
  • 释放数据包: 使用 av_packet_unref() 释放数据包。

示例代码

AVPacket pkt;
av_init_packet(&pkt);
pkt.data = NULL;
pkt.size = 0;

while (av_read_frame(fmt_ctx, &pkt) >= 0) {
    if (pkt.stream_index == video_stream->index) {
        // 处理视频数据包
        av_log(NULL, AV_LOG_INFO, "Video Packet: PTS=%" PRId64 ", DTS=%" PRId64 ", size=%d\n",
               pkt.pts, pkt.dts, pkt.size);
    } else if (pkt.stream_index == audio_stream->index) {
        // 处理音频数据包
        av_log(NULL, AV_LOG_INFO, "Audio Packet: PTS=%" PRId64 ", DTS=%" PRId64 ", size=%d\n",
               pkt.pts, pkt.dts, pkt.size);
    }
    av_packet_unref(&pkt);
}

4. 释放资源(Free Resources)

释放资源是指在完成数据流操作后,释放分配的所有内存和资源,以避免内存泄漏。

主要步骤

  • 释放数据包: 使用 av_packet_unref() 释放每个数据包。
  • 关闭输入文件: 使用 avformat_close_input() 关闭输入文件并释放 AVFormatContext
  • 释放其他资源: 释放任何其他分配的资源。

示例代码

// 释放数据包
av_packet_unref(&pkt);

// 关闭输入文件并释放AVFormatContext
avformat_close_input(&fmt_ctx);

完整示例

#include <libavformat/avformat.h>
#include <libavutil/log.h>

int main(int argc, char *argv[]) {
    AVFormatContext *fmt_ctx = NULL;
    AVPacket pkt;
    AVStream *video_stream = NULL;
    AVStream *audio_stream = NULL;
    int ret;

    if (argc < 2) {
        fprintf(stderr, "Usage: %s <input file>\n", argv[0]);
        return 1;
    }

    // 注册所有格式和编解码器(FFmpeg 4.x及以前版本需要)
    av_register_all();

    // 打开输入文件
    if ((ret = avformat_open_input(&fmt_ctx, argv[1], NULL, NULL)) < 0) {
        av_log(NULL, AV_LOG_ERROR, "Cannot open input file: %s\n", av_err2str(ret));
        return ret;
    }

    // 读取文件头部信息
    if ((ret = avformat_find_stream_info(fmt_ctx, NULL)) < 0) {
        av_log(NULL, AV_LOG_ERROR, "Cannot find stream information: %s\n", av_err2str(ret));
        avformat_close_input(&fmt_ctx);
        return ret;
    }

    // 打印输入文件的信息
    av_dump_format(fmt_ctx, 0, argv[1], 0);

    // 查找音频和视频流
    for (unsigned int i = 0; i < fmt_ctx->nb_streams; i++) {
        AVStream *stream = fmt_ctx->streams[i];
        if (stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
            video_stream = stream;
        } else if (stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
            audio_stream = stream;
        }
    }

    if (!video_stream && !audio_stream) {
        av_log(NULL, AV_LOG_ERROR, "No video or audio stream found\n");
        avformat_close_input(&fmt_ctx);
        return -1;
    }

    // 初始化数据包
    av_init_packet(&pkt);
    pkt.data = NULL;
    pkt.size = 0;

    // 读取数据包
    while (av_read_frame(fmt_ctx, &pkt) >= 0) {
        if (pkt.stream_index == video_stream->index) {
            // 处理视频数据包
            av_log(NULL, AV_LOG_INFO, "Video Packet: PTS=%" PRId64 ", DTS=%" PRId64 ", size=%d\n",
                   pkt.pts, pkt.dts, pkt.size);
        } else if (pkt.stream_index == audio_stream->index) {
            // 处理音频数据包
            av_log(NULL, AV_LOG_INFO, "Audio Packet: PTS=%" PRId64 ", DTS=%" PRId64 ", size=%d\n",
                   pkt.pts, pkt.dts, pkt.size);
        }
        av_packet_unref(&pkt);
    }

    // 关闭输入文件并释放AVFormatContext
    avformat_close_input(&fmt_ctx);

    return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

如何写出一篇高质量的新闻稿,纯干货

对于企业宣传来说&#xff0c;新闻稿的发布是常用的一种宣传推广的营销方式&#xff0c;新闻稿可以让消费者及时了解企业的最新发展动态&#xff0c;增加企业品牌的曝光度。 一篇高质量新闻稿不仅能作为品牌背书&#xff0c;还很可能会被各大媒体转载&#xff0c;短时间内提高企…

分类模型的算法性能评价

一、概述 分类模型是机器学习中一种最常见的问题模型&#xff0c;在许多问题场景中有着广泛的运用&#xff0c;是模式识别问题中一种主要的实现手段。分类问题概况起来就是&#xff0c;对一堆高度抽象了的样本&#xff0c;由经验标定了每个样本所属的实际类别&#xff0c;由特定…

【基于R语言群体遗传学】-11-二倍体适应性进化

之前的博客&#xff0c;我们讨论了正选择在单倍体中的情况&#xff0c;可以先学习之前的博客&#xff1a; 群体遗传学_tRNA做科研的博客-CSDN博客 分子适应的经典观点是正选择。对于一个新的突变在进化意义上起作用&#xff0c;它必须在罕见时通过选择增加。之前例子中有选择系…

【深度学习基础】环境搭建 linux系统下安装pytorch

目录 一、anaconda 安装二、创建pytorch1. 创建pytorch环境&#xff1a;2. 激活环境3. 下载安装pytorch包4. 检查是否安装成功 一、anaconda 安装 具体的安装说明可以参考我的另外一篇文章【环境搭建】Linux报错bash: conda: command not found… 二、创建pytorch 1. 创建py…

园区、社区、乡村的智能管理

智慧园区、社区、乡村管理系统是现代信息技术在城市化进程中的重要应用,它们通过集成多种技术手段,实现对园区、社区、乡村的全面、高效、智能化管理。以下是对这三种管理系统的详细阐述: 一、智慧园区管理系统 1. 定义与目的 智慧园区管理系统是运用物联网、云计算、大数…

深入解析【C++ list 容器】:高效数据管理的秘密武器

目录 1. list 的介绍及使用 1.1 list 的介绍 知识点&#xff1a; 小李的理解&#xff1a; 1.2 list 的使用 1.2.1 list 的构造 知识点&#xff1a; 小李的理解&#xff1a; 代码示例&#xff1a; 1.2.2 list 迭代器的使用 知识点&#xff1a; 小李的理解&#xff1…

来自工业界的开源知识库 RAG 项目最全细节对比

背景介绍 之前详细整理过来自工业界的不少开源 RAG 项目&#xff1a; 有道 QAnythingRAGFlowlangchain-chatchat中科院 GoMateDifyFastGPT 群里一直看到有小伙伴询问在实际的业务需求中如何选择合适的 RAG 项目&#xff0c;本文就详细对比一下这些 RAG 项目。考虑到目前实际…

SouthMap:地理信息系统的戏剧性变革

在这喧嚣的城市中&#xff0c;工程师们的生活总是充满了戏剧性的起伏与跌宕。作为一名长期使用SouthMap的地理信息系统&#xff08;GIS&#xff09;工程师&#xff0c;我深知每一个项目背后所蕴含的心血与汗水。今天&#xff0c;我想与大家分享一款令我感触颇深的软件——South…

关于Python的类的一些理解

才发现python的类对象只能调用类方法 我想使用对类对象a使用系统调用的len方法就会报错 2.类对象a是什么&#xff1f; 答&#xff1a;是所有的带有self的成员变量 举例说明&#xff1a;红色的就是a里面的东西 class A:def __init__(self,data):self.datadataself.b1self.d{a…

tableau树形图制作 - 7

树形图制作 1. 树状图绘制-11.1 选择属性1.2 智能选择树状图1.3 颜色设置 2. 树状图绘制-22.1 标签属性选择2.2 树状图绘制2.3 颜色设置2.4 设置标签2.5 设置筛选器 3. 树状图绘制 - 33.1 选择行列3.2 树状图转换3.3 统计转换3.4 颜色设置3.5 标签设置3.6 筛选器设置 1. 树状图…

值得收藏!推荐10个好用的数据血缘工具【送书活动】

目录 前言01 Apache AtlasApache Atlas核心功能优缺点分析适用场景 02 Datahub核心功能优缺点分析适用场景 03 Gudu SQLFlow核心功能优缺点分析 04 FineBI适用场景 05 亿信华辰智能数据治理平台核心功能 06 飞算SoData数据机器人八大特性 07 Informatica的数据平台主要特点 08 …

Studying-代码随想录训练营day33| 动态规划理论基础、509.斐波那契函数、70.爬楼梯、746.使用最小花费爬楼梯

第33天&#xff0c;动态规划开始&#xff0c;新的算法&#x1f4aa;(ง •_•)ง&#xff0c;编程语言&#xff1a;C 目录 动态规划理论基础 动态规划的解题步骤 动态规划包含的问题 动态规划如何debug 509.斐波那契函数 70.爬楼梯 746.使用最小花费爬楼梯 总结 动态…

文华财经红绿多空趋势量化买卖点指标公式源码

LC:REF(CLOSE,1); RSI1:SMA(MAX(CLOSE-LC,0),13,1)/SMA(ABS(CLOSE-LC),13,1)*100; RSIF:90-RSI1,COLOR33DD33; A4:((C-LLV(L,33))/(HHV(H,33)-LLV(L,33)))*67; ABC22:LLV(LOW,10); ABC33:HHV(HIGH,25); 动力线:EMA((CLOSE-ABC22)/(ABC33-ABC22)*4,4); RSV:(C-LLV(L,9))/…

前端入门知识分享:如何在HTML或CSS文件中引用CSS文件。

阅读提示&#xff1a;本文仅仅仅适用于刚刚接触HTML和CSS的小白从业者&#xff0c;新人爱好者。自觉身份不符的老鸟们&#xff0c;尽快绕行吧&#xff01; 什么是CSS&#xff1f;什么是CSS文件。 CSS&#xff0c;全称为Cascading Style Sheets&#xff08;层叠样式表&#xff…

淮北在选择SCADA系统时,哪些因素会影响其稳定性?

关键字:LP-SCADA系统, 传感器可视化, 设备可视化, 独立SPC系统, 智能仪表系统,SPC可视化,独立SPC系统 在选择SCADA系统时&#xff0c;稳定性是一个关键因素&#xff0c;因为它直接影响到生产过程的连续性和安全性。以下是一些影响SCADA系统稳定性的因素&#xff1a; 硬件质量…

2024机器遗忘(Machine Unlearning)技术分类-思维导图

1 介绍 机器遗忘&#xff08;Machine Unlearning&#xff09;是指从机器学习模型中安全地移除或"遗忘"特定的数据点或信息。这个概念源于数据隐私保护的需求&#xff0c;尤其是在欧盟通用数据保护条例&#xff08;GDPR&#xff09;等法规中提出的"被遗忘的权利…

1、课程导学(react+区块链实战)

1、课程导学&#xff08;react区块链实战&#xff09; 1&#xff0c;课程概述&#xff08;1&#xff09;课程安排&#xff08;2&#xff09;学习前提&#xff08;3&#xff09;讲授方式&#xff08;4&#xff09;课程收获 2&#xff0c;ibloackchain&#xff08;1&#xff09;安…

前端Debugger时复制的JS对象字符转JSON对象

前端debugger时&#xff0c;复制的对象在控制台输出时是如下格式&#xff0c;需要转换为对象格式来进行验证操作 bridgeId : 4118 createBy : null createTime : "2023-03-24 10:35:26" createUserId : 1 具体实现代码&#xff1a; // 转换transform (text) {l…

yolov8 人体姿态识别

引言 在计算机视觉的各种应用中&#xff0c;人体姿态检测是一项极具挑战性的任务&#xff0c;它能够帮助我们理解人体各部位的空间位置。本文将详细介绍如何使用 YOLOv8 和 Python 实现一个人体姿态检测系统&#xff0c;涵盖模型加载、图像预处理、姿态预测到结果可视化的全流…