ffmpeg解封装解码

文章目录

  • 封装和解封装
    • 封装
    • 解封装
  • 相关接口
    • 解封装的流程图
    • 关于AVPacket的解释
    • 如何区分不同的码流,视频流,音频流?
      • 第一种方式av_find_best_stream
      • 第二种方式 通过遍历流
    • 代码

封装和解封装

封装

是把音频流 ,视频流,字幕流,其他成分 按照一定的规则组合程一个视频文件(mp4/ flv)的

解封装

流程和封装完全相反 是把一个视频文件的音频流,视频流,字幕流,其他成分给分离出来。

相关接口

◼ avformat_alloc_context();负责申请一个AVFormatContext
结构的内存,并进行简单初始化 AVFormatContext是描述一个媒体文件或媒体流的构成和基本信息的结构体

-注意:这个接口 也不是必须调用的 因为在avformat_open_input()的的时候 传入第一个参数 ,如果检测为空的化,这个函数内部会自动进行检测和分配相关的内存
在这里插入图片描述

◼ avformat_free_context();释放该结构里的所有东西以及该结构本身
◼ avformat_close_input();关闭解复用器。关闭后就不再需要使用avformat_free_context 进行释放。
◼ avformat_open_input();打开输入视频文件
◼ avformat_find_stream_info():获取视频文件信息
◼ av_read_frame(); 读取音视频包
◼ avformat_seek_file(); 定位文件
◼ av_seek_frame():定位文件

解封装的流程图

在这里插入图片描述

关于AVPacket的解释

FFMpeg AVPacket 之理解与掌握
这里的讲解是比较好的
总结来说 ,就是用来装一帧数据流的包 ,包里面有包头和包体,
av_packet_alloc()是分配一个包结构
这里的使用是 av_read_frame() 来记录一帧,并进行输出 ,有一点需要注意的是 ,在读取新的帧的时候 要调用av_packet_unref去释放相关的包结构,避免相关包在释放的时候 查看到引用不为0而不去释放,导致的未知的错误事件发生
并且在最后不用的时候 要调用av_packet_free 去释放掉对应的空间
av_packet_ref 可以让一个包去引用另外一个包的内容
注意不能通过赋值直接去引用,这样两者指向的是 同一个包头 ,这样包头释放的时候 会导致两个指针的失效.

如何区分不同的码流,视频流,音频流?

第一种方式av_find_best_stream

在这里插入图片描述
通过接口
举例 获取视频流
video_index = av_find_best_stream(ic, AVMEDIA_TYPE_VIDEO,-1,-1, NULL, 0)
获取音频流
audio_index = av_find_best_stream(ic, AVMEDIA_TYPE_AUDIO,-1,-1, NULL, 0)
可选参数
在这里插入图片描述

第二种方式 通过遍历流

下面AVFormatContext里面有相关流的数量
通过 遍历 streams里面的数据 去获取对应的流
in_stream->codecpar->codec_type 可以表示对应的流
在这里插入图片描述
下面有代码演示具体的用法
注意点:
avformat_open_input和avformat_find_stream_info分别用于打 开一个流和分析流信息。 在初始信息不足的情况下(比如FLV和H264文件), avformat_find_stream_info接口需要在内部调用 read_frame_internal接口读取流数据(音视频帧),然后再分 析后,设置核心数据结构AVFormatContext。 由于需要读取数据包,avformat_find_stream_info接口会带来 很大的延迟。

代码

#include <libavformat/avformat.h>
#include <stdio.h>int main(int argc, char **argv) {// 打开网络流。这里如果只需要读取本地媒体文件,不需要用到网络功能,可以不用加上这一句//     avformat_network_init();const char *default_filename = "believe.mp4";char *in_filename = NULL;if (argv[ 1 ] == NULL) {in_filename = default_filename;} else {in_filename = argv[ 1 ];}printf("in_filename = %s\n", in_filename);// AVFormatContext是描述一个媒体文件或媒体流的构成和基本信息的结构体AVFormatContext *ifmt_ctx = NULL; // 输入文件的demuxint videoindex = -1; // 视频索引int audioindex = -1; // 音频索引// 打开文件,主要是探测协议类型,如果是网络文件则创建网络链接int ret = avformat_open_input(&ifmt_ctx, in_filename, NULL, NULL);if (ret < 0) // 如果打开媒体文件失败,打印失败原因{char buf[ 1024 ] = {0};av_strerror(ret, buf, sizeof(buf) - 1);printf("open %s failed:%s\n", in_filename, buf);goto failed;}// 读取媒体文件的数据包以获取流的信息ret = avformat_find_stream_info(ifmt_ctx, NULL);if (ret < 0) // 如果打开媒体文件失败,打印失败原因{char buf[ 1024 ] = {0};av_strerror(ret, buf, sizeof(buf) - 1);printf("avformat_find_stream_info %s failed:%s\n", in_filename, buf);goto failed;}// 打开媒体文件成功printf_s("\n==== av_dump_format in_filename:%s ===\n", in_filename);av_dump_format(ifmt_ctx, 0, in_filename, 0);printf_s("\n==== av_dump_format finish =======\n\n");// url: 调用avformat_open_input读取到的媒体文件的路径/名字printf("media name:%s\n", ifmt_ctx->url);// nb_streams: nb_streams媒体流数量printf("stream number:%d\n", ifmt_ctx->nb_streams);// bit_rate: 媒体文件的码率,单位为bpsprintf("media average ratio:%lldkbps\n", ( int64_t )(ifmt_ctx->bit_rate / 1024));// 时间int total_seconds, hour, minute, second;// duration: 媒体文件时长,单位微妙total_seconds = (ifmt_ctx->duration) / AV_TIME_BASE; // 1000us = 1ms, 1000ms = 1秒hour = total_seconds / 3600;minute = (total_seconds % 3600) / 60;second = (total_seconds % 60);// 通过上述运算,可以得到媒体文件的总时长printf("total duration: %02d:%02d:%02d\n", hour, minute, second);printf("\n");/** 老版本通过遍历的方式读取媒体文件视频和音频的信息* 新版本的FFmpeg新增加了函数av_find_best_stream,也可以取得同样的效果*/for (uint32_t i = 0; i < ifmt_ctx->nb_streams; i++) {AVStream *in_stream = ifmt_ctx->streams[ i ]; // 音频流、视频流、字幕流// 如果是音频流,则打印音频的信息if (AVMEDIA_TYPE_AUDIO == in_stream->codecpar->codec_type) {printf("----- Audio info:\n");// index: 每个流成分在ffmpeg解复用分析后都有唯一的index作为标识printf("index:%d\n", in_stream->index);// sample_rate: 音频编解码器的采样率,单位为Hzprintf("samplerate:%dHz\n", in_stream->codecpar->sample_rate);// codecpar->format: 音频采样格式if (AV_SAMPLE_FMT_FLTP == in_stream->codecpar->format) {printf("sampleformat:AV_SAMPLE_FMT_FLTP\n");} else if (AV_SAMPLE_FMT_S16P == in_stream->codecpar->format) {printf("sampleformat:AV_SAMPLE_FMT_S16P\n");}// channels: 音频信道数目printf("channel number:%d\n", in_stream->codecpar->channels);// codec_id: 音频压缩编码格式if (AV_CODEC_ID_AAC == in_stream->codecpar->codec_id) {printf("audio codec:AAC\n");} else if (AV_CODEC_ID_MP3 == in_stream->codecpar->codec_id) {printf("audio codec:MP3\n");} else {printf("audio codec_id:%d\n", in_stream->codecpar->codec_id);}// 音频总时长,单位为秒。注意如果把单位放大为毫秒或者微妙,音频总时长跟视频总时长不一定相等的if (in_stream->duration != AV_NOPTS_VALUE) {int duration_audio = (in_stream->duration) * av_q2d(in_stream->time_base);// 将音频总时长转换为时分秒的格式打印到控制台上printf("audio duration: %02d:%02d:%02d\n",duration_audio / 3600, (duration_audio % 3600) / 60, (duration_audio % 60));} else {printf("audio duration unknown");}printf("\n");audioindex = i; // 获取音频的索引} else if (AVMEDIA_TYPE_VIDEO == in_stream->codecpar->codec_type) // 如果是视频流,则打印视频的信息{printf("----- Video info:\n");printf("index:%d\n", in_stream->index);// avg_frame_rate: 视频帧率,单位为fps,表示每秒出现多少帧printf("fps:%lffps\n", av_q2d(in_stream->avg_frame_rate));if (AV_CODEC_ID_MPEG4 == in_stream->codecpar->codec_id) // 视频压缩编码格式{printf("video codec:MPEG4\n");} else if (AV_CODEC_ID_H264 == in_stream->codecpar->codec_id) // 视频压缩编码格式{printf("video codec:H264\n");} else {printf("video codec_id:%d\n", in_stream->codecpar->codec_id);}// 视频帧宽度和帧高度printf("width:%d height:%d\n", in_stream->codecpar->width,in_stream->codecpar->height);// 视频总时长,单位为秒。注意如果把单位放大为毫秒或者微妙,音频总时长跟视频总时长不一定相等的if (in_stream->duration != AV_NOPTS_VALUE) {int duration_video = (in_stream->duration) * av_q2d(in_stream->time_base);printf("video duration: %02d:%02d:%02d\n",duration_video / 3600,(duration_video % 3600) / 60,(duration_video % 60)); // 将视频总时长转换为时分秒的格式打印到控制台上} else {printf("video duration unknown");}printf("\n");videoindex = i;}}// 解码的packet包 分配一个// 这个相当于一个包 包含包头和包体AVPacket *pkt = av_packet_alloc();int pkt_count = 0;int print_max_count = 10;printf("\n-----av_read_frame start\n");while (1) {// 从流中读取一帧的数据ret = av_read_frame(ifmt_ctx, pkt);if (ret < 0) {printf("av_read_frame end\n");break;}// 这里最大读取10帧的数据if (pkt_count++ < print_max_count) {if (pkt->stream_index == audioindex) {printf("audio pts: %lld\n", pkt->pts);printf("audio dts: %lld\n", pkt->dts);printf("audio size: %d\n", pkt->size);printf("audio pos: %lld\n", pkt->pos);printf("audio duration: %lf\n\n",pkt->duration * av_q2d(ifmt_ctx->streams[ audioindex ]->time_base));} else if (pkt->stream_index == videoindex) {printf("video pts: %lld\n", pkt->pts);printf("video dts: %lld\n", pkt->dts);printf("video size: %d\n", pkt->size);printf("video pos: %lld\n", pkt->pos);printf("video duration: %lf\n\n",pkt->duration * av_q2d(ifmt_ctx->streams[ videoindex ]->time_base));} else {printf("unknown stream_index:\n", pkt->stream_index);}}// 读取的过程就相当于对某一帧的引用 让相关的信息 读取完之后 要调用接口 去取消这个引用// 不能直接去读取新的引用,不然可能会导致内存泄漏的问题 相关的包释放掉之后里面的某些帧的内容还无法释放掉av_packet_unref(pkt);}if (pkt)av_packet_free(&pkt);
failed:if (ifmt_ctx)avformat_close_input(&ifmt_ctx);getchar(); // 加上这一句,防止程序打印完信息马上退出return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

LeetCode题练习与总结:删除链表中的节点--237

一、题目描述 有一个单链表的 head&#xff0c;我们想删除它其中的一个节点 node。 给你一个需要删除的节点 node 。你将 无法访问 第一个节点 head。 链表的所有值都是 唯一的&#xff0c;并且保证给定的节点 node 不是链表中的最后一个节点。 删除给定的节点。注意&…

实例讲解电动汽车驱动扭矩控制策略及Simulink建模方法

电动汽车完成上电后进入Ready状态&#xff0c;此时车辆具备行车条件&#xff0c;处于行车准备状态。驾驶员挂挡&#xff08;D挡或R挡&#xff09;后&#xff0c;踩油门踏板即可控制车辆开始行车。对于电动汽车来说&#xff0c;驱动行车控制过程一般为&#xff0c;VCU接收Ready状…

高侧电流检测电路设计

1 简介 此单电源、高侧、低成本、电流检测解决方案可以检测 50mA 和 1A 之间的负载电流&#xff0c;并将其转换为 0.25V至 5V 的输出电压。高侧检测使系统能够识别接地短路&#xff0c;并且不会对负载造成接地干扰。 2 设计目标 2.1 输入 2.2 输出 ​​​ 2.3 电…

轴承介绍以及使用

轴承&#xff08;Bearing&#xff09;是在机械传动过程中起固定、旋转和减小载荷摩擦系数的部件。也可以说&#xff0c;当其它机件在轴上彼此产生相对运动时&#xff0c;用来降低运动力传递过程中的摩擦系数和保持转轴中心位置固定的机件。 轴承是当代机械设备中一种举足轻重的…

在java中怎么把对象转换成json,可以使用jackson

简述 在Spring Boot应用中&#xff0c;将Java对象转换为JSON字符串通常有两种主要方法&#xff1a;使用Jackson库或使用Gson库。由于Spring Boot默认集成了Jackson库&#xff0c;所以我们将重点介绍如何使用Jackson来进行对象到JSON的转换。 第1步&#xff1a;Maven添加依赖 …

STM32 Modbus主从站实例程序-FreeRTOS

资料下载地址&#xff1a;STM32 Modbus主从站实例程序-FreeRTOS​​​​​​​ 基本设置 启用Freertos,添加任务 设置中断优先级 设置长生成MDK工程 工程里面添加Modbus库 修改main.c 修改freertos.c 编译下载到单片机,完美运行

深入解析 helpTransfer 方法:多线程协作中的哈希表扩容

文章目录 什么是哈希表哈希表的问题&#xff1a;扩容扩容的挑战扩容的原理helpTransfer 方法检查是否正在扩容生成扩容标记并检查条件判断是否需要更多线程帮助加入搬家工作返回新表或旧表 什么是哈希表 哈希表&#xff08;HashMap&#xff09;是一种常用的数据结构&#xff0…

熬夜2月,终成人人可自建的AI网站

一、前言 自小码哥AI上线以来&#xff0c;备受粉丝们的关注&#xff0c;拖更了两个月&#xff0c;每日加班加点研发系统&#xff0c;2.0终于上线了。 作为一名年过三十的程序员&#xff0c;我深刻体会到了职场的残酷和不确定性&#xff0c;特别是这两年&#xff0c;经济不景气…

ROS理论与实践学习笔记——2 ROS通信机制之服务通信

服务通信也是ROS中一种极其常用的通信模式&#xff0c;服务通信是基于请求响应模式的&#xff0c;是一种应答机制。也即: 一个节点A向另一个节点B发送请求&#xff0c;B接收处理请求并产生响应结果返回给A&#xff0c;用于偶然的、对时时性有要求、有一定逻辑处理需求的数据传输…

基于Java语言的桩底层直连协议和云快充协议

‌云快充协议‌是一种标准通信协议&#xff0c;主要用于电动车与充电桩之间的数据交换。该协议包含了充电请求、状态查询、支付等多个功能模块&#xff0c;这些功能的实现不仅需要对协议进行深入理解&#xff0c;还需要编写相应的代码进行封装。云快充协议旨在解决市场上快充标…

【C++前缀和 状态压缩】1177. 构建回文串检测|1848

本文涉及的基础知识点 C算法&#xff1a;前缀和、前缀乘积、前缀异或的原理、源码及测试用例 包括课程视频 位运算、状态压缩、枚举子集汇总 LeetCode 1177. 构建回文串检测 难度分&#xff1a;1848 给你一个字符串 s&#xff0c;请你对 s 的子串进行检测。 每次检测&#x…

Python OpenCV精讲系列 - 基于深度学习的目标检测(十二)

&#x1f496;&#x1f496;⚡️⚡️专栏&#xff1a;Python OpenCV精讲⚡️⚡️&#x1f496;&#x1f496; 本专栏聚焦于Python结合OpenCV库进行计算机视觉开发的专业教程。通过系统化的课程设计&#xff0c;从基础概念入手&#xff0c;逐步深入到图像处理、特征检测、物体识…

C++ | Leetcode C++题解之第434题字符串中的单词数

题目&#xff1a; 题解&#xff1a; class Solution { public:int countSegments(string s) {int segmentCount 0;for (int i 0; i < s.size(); i) {if ((i 0 || s[i - 1] ) && s[i] ! ) {segmentCount;}}return segmentCount;} };

await命令使用注意点

第一点&#xff0c;前面已经说过&#xff0c;await 命令后面的 Promise 对象&#xff0c;运行结果可能是 rejected&#xff0c;所以最好把 await 命令放在 try...catch 代码块中 第二点&#xff0c;多个 await 命令后面的异步操作&#xff0c;如果不存在继发关系&#xff0c;最…

最优化理论与自动驾驶(二-补充):求解算法(梯度下降法、牛顿法、高斯牛顿法以及LM法,C++代码)

在之前的章节里面&#xff08;最优化理论与自动驾驶&#xff08;二&#xff09;&#xff1a;求解算法&#xff09;我们展示了最优化理论的基础求解算法&#xff0c;包括高斯-牛顿法&#xff08;Gauss-Newton Method&#xff09;、梯度下降法&#xff08;Gradient Descent Metho…

FileLink跨网文件传输 | 跨越网络边界的利器,文件传输不再受限

在当今数字化时代&#xff0c;企业与个人对文件传输的需求不断增长&#xff0c;尤其是在跨网环境中。传统的文件传输方式常常受到网络带宽、传输速度和安全性的限制&#xff0c;给用户带来了诸多不便。FileLink 的出现&#xff0c;为这一难题提供了完美解决方案&#xff0c;让文…

Python 聊聊有内置函数,又该怎么学习内置函数

前言 python有内置函数的概念&#xff0c;从Python3.x开始&#xff0c;内置函数位于builtins模块&#xff0c;比如我们常用的内置函数len()&#xff0c;其实它是builtins模块下的属性&#xff0c;我们也可以builtins.len&#xff08;&#xff09;去访问&#xff0c;当然因为每个…

鼎曼白茶贡眉:贮留芳香记忆,书写老茶传奇

在茶的世界 每一叶都承载着岁月的印记 每一香都凝聚着时光的韵味 其中 有一种温润如玉、恬淡从容的存在 它便是2017年贡眉 这款经过七年时光沉淀与陈化的白茶 以其独特的韵味与品质 吸引了无数茶客的青睐 今日 让我们一同领略2017年贡眉的魅力 PART 01 FIRST OF ALL …

力扣【118-杨辉三角】【数组-C语言】

题目&#xff1a;力扣-118 杨辉三角&#xff1a;&#xff08;算法思路&#xff09; 1. 每行第一个数和最后一个数都是1 2. 把杨辉三角左端对齐&#xff0c;从第三行开始&#xff0c;非首尾的元素值等于上一行同列的元素与该元素之前的元素之和&#xff0c;即 t [ j ] r e t …

【自动化测试】Appium 生态工具以及Appium Desktop如何安装和使用

引言 Appium 是一个开源的自动化测试框架&#xff0c;用于测试原生、移动 Web 和混合应用程序。它支持 iOS、Android 和 Windows 平台。Appium 生态系统包含多个工具和库&#xff0c;这些工具和库可以与 Appium 一起使用&#xff0c;以提高移动应用的自动化测试效率 文章目录 引…