当前位置: 首页 > news >正文

FFmpeg 自定义IO CONTEXT实现音频解码,以及seek函数

 对于从音频流buffer中解码的场景中,我们需要实现自己的io context 去从buffer中解码,参考ffmepg官方实例:doc/examples/avio_reading.c

关于是否要实现avio context中的seek函数,需要看需要解码什么格式,大部分格式不需要seek,但是有些格式需要,比如apple开发的ALAC格式,这个格式的音频有的时候它的头文件moov信息是在文件的结尾,这就很坑,一般都是在开头,所以在获取音频的时候需要先seek到文件的结尾,获取moov的信息,然后再seek回来继续解析格式并解码。

关于moov格式的坑:【开发笔记】终于,我们解决了iOS播放器的一个Bug... - 哔哩哔哩

如果你不想实现seek,有没有办法直接把音频文件的moov信息从结尾提到开头呢?也是有的

ffmpeg -i ./old.mp4 -movflags faststart -c copy new.mp4

通过这个命令转换后再去解码,文件信息就在开头,就可以不用seek了。

可以通过以下命令去查看头文件信息:

ffprobe -v trace filename

直接贴代码:

#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavformat/avio.h>
#include <libavutil/file.h>
#include <libavutil/frame.h>
#include <libavutil/mem.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>#define BUF_SIZE 20480FILE *in_file = NULL;struct buffer_data {uint8_t *ptr;uint8_t *ori_ptr;  // for seek file streamsize_t size;       ///< size left in the buffersize_t file_size;  ///< size of the file to decode
};static char *av_get_err(int errnum) {static char err_buf[128] = {0};av_strerror(errnum, err_buf, 128);return err_buf;
}static void print_sample_format(const AVFrame *frame) {printf("ar-samplerate: %uHz\n", frame->sample_rate);printf("ac-channel: %u\n", frame->channels);printf("f-format: %u\n",frame->format);  // 格式需要注意,实际存储到本地文件时已经改成交错模式
}
/*
int read_size;static int read_packet(void *opaque, uint8_t *buf, int buf_size) {//    FILE *in_file = (FILE *)opaque;read_size = fread(buf, 1, buf_size, in_file);printf("read_packet read_size:%d, buf_size:%d\n", read_size, buf_size);if (read_size <= 0) {return AVERROR_EOF;  // 数据读取完毕}return read_size;
}
*/
static int read_packet(void *opaque, uint8_t *buf, int buf_size) {struct buffer_data *bd = (struct buffer_data *)opaque;buf_size = FFMIN(buf_size, bd->size);if (!buf_size) return AVERROR_EOF;//    printf("ptr:%p size:%zu buf_size: %d\n", bd->ptr, bd->size, buf_size);/* copy internal buffer data to buf */memcpy(buf, bd->ptr, buf_size);bd->ptr += buf_size;bd->size -= buf_size;return buf_size;
}// for some format like ALAC (apple format) , which moov partten is located at
// the end of file so we need to implement seek function during demux to seek to
// the end of file for paring the moov info and then seek back to the front
static int64_t seek_packet(void *opaque, int64_t offset, int whence) {//    FILE *in_file = (FILE *)opaque;struct buffer_data *bd = (struct buffer_data *)opaque;int64_t ret = -1;printf("whence=%d , offset=%lld \n", whence, offset);switch (whence) {case AVSEEK_SIZE:printf("AVSEEK_SIZE \n");ret = bd->file_size;break;case SEEK_SET:printf("SEEK_SET \n");bd->ptr = bd->ori_ptr + offset;bd->size = bd->file_size - offset;ret = bd->ptr;break;case SEEK_CUR:printf("SEEK_cur \n");break;case SEEK_END:printf("SEEK_end \n");break;}return ret;
}static void decode(AVCodecContext *dec_ctx, AVPacket *packet, AVFrame *frame,FILE *outfile) {int ret = 0;ret = avcodec_send_packet(dec_ctx, packet);if (ret == AVERROR(EAGAIN)) {printf("Receive_frame and send_packet both returned EAGAIN, which is an API ""violation.\n");} else if (ret < 0) {printf("Error submitting the packet to the decoder, err:%s\n",av_get_err(ret));return;}while (ret >= 0) {ret = avcodec_receive_frame(dec_ctx, frame);if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {return;} else if (ret < 0) {printf("Error during decoding\n");exit(1);}if (!packet) {printf("get flush frame\n");}int out_sample_bytes = av_get_bytes_per_sample(dec_ctx->sample_fmt);int out_sample_is_plannar = av_sample_fmt_is_planar(dec_ctx->sample_fmt);// printf("debug %d is out_sample_is_plannar : %d \n", __LINE__,//       out_sample_is_plannar);// print_sample_format(frame);if (out_sample_bytes < 0) {/* This should not occur, checking just for paranoia */fprintf(stderr, "Failed to calculate data size\n");exit(1);}//    printf("debug %d out_sample_bytes: %d samples: %d ch:%d\n", __LINE__,//           out_sample_bytes, frame->nb_samples,//           dec_ctx->ch_layout.nb_channels);if (out_sample_is_plannar) {  // plannar frames/**P表示Planar(平面),其数据格式排列方式为 :LLLLLLRRRRRRLLLLLLRRRRRRLLLLLLRRRRRRL...(每个LLLLLLRRRRRR为一个音频帧)而不带P的数据格式(即交错排列)排列方式为:LRLRLRLRLRLRLRLRLRLRLRLRLRLRLRLRLRLRL...(每个LR为一个音频样本)播放范例:   ffplay -ar 48000 -ac 2 -f f32le believe.pcm并不是每一种都是这样的格式*/// 这里的写法不是通用,通用要调用重采样的函数去实现// 这里只是针对解码出来是planar格式的转换for (int i = 0; i < frame->nb_samples; i++) {for (int ch = 0; ch < frame->channels; ch++) {// for(int ch = 0; ch < 1; ch++) {fwrite(frame->data[ch] + out_sample_bytes * i, 1, out_sample_bytes,outfile);}}} else  // packed framefwrite(frame->data[0],frame->nb_samples * out_sample_bytes * frame->channels, 1,outfile);}
}int main(int argc, char **argv) {if (argc != 3) {printf("usage: %s <intput file> <out file>\n", argv[0]);return -1;}av_log_set_level(AV_LOG_TRACE);const char *in_file_name = argv[1];const char *out_file_name = argv[2];//    FILE *in_file = NULL;FILE *out_file = NULL;// 1. 打开参数文件in_file = fopen(in_file_name, "rb");if (!in_file) {printf("open file %s failed\n", in_file_name);return -1;}out_file = fopen(out_file_name, "wb+");if (!out_file) {printf("open file %s failed\n", out_file_name);return -1;}struct buffer_data bd = {0};uint8_t *buffer = NULL;size_t buffer_size;int ret = av_file_map(in_file_name, &buffer, &buffer_size, 0, NULL);printf("file size: %d\n", buffer_size);bd.ptr = buffer;bd.ori_ptr = buffer;bd.file_size = buffer_size;bd.size = buffer_size;//    AVInputFormat* in_fmt = av_find_input_format("flac");// 2自定义 iouint8_t *io_buffer = av_malloc(BUF_SIZE);// AVIOContext *avio_ctx = avio_alloc_context(io_buffer, BUF_SIZE, 0, (void// *)in_file,AVIOContext *avio_ctx = avio_alloc_context(io_buffer, BUF_SIZE, 0, &bd,read_packet, NULL, seek_packet);// avio_alloc_context(io_buffer, BUF_SIZE, 0, &bd, read_packet, NULL, NULL);AVFormatContext *format_ctx = avformat_alloc_context();format_ctx->pb = avio_ctx;format_ctx->flags = AVFMT_FLAG_CUSTOM_IO;// int ret = avformat_open_input(&format_ctx, NULL, in_fmt, NULL);// 从输入源读取封装格式文件头ret = avformat_open_input(&format_ctx, NULL, NULL, NULL);if (ret < 0) {printf("avformat_open_input failed:%s\n", av_err2str(ret));return -1;}// 从输入源读取一段数据,尝试解码,以获取流信息if ((ret = avformat_find_stream_info(format_ctx, NULL)) < 0) {av_log(NULL, AV_LOG_ERROR, "Cannot find stream information\n");return ret;}av_dump_format(format_ctx, 0, NULL, 0);int audioStreamIndex =av_find_best_stream(format_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);AVStream *st = format_ctx->streams[audioStreamIndex];// 编码器查找// AVCodec *codec = avcodec_find_decoder(AV_CODEC_ID_ALAC);// set codec id from famat paramsAVCodec *codec = avcodec_find_decoder(st->codecpar->codec_id);if (!codec) {printf("avcodec_find_decoder failed\n");return -1;}AVCodecContext *codec_ctx = avcodec_alloc_context3(codec);if (!codec_ctx) {printf("avcodec_alloc_context3 failed\n");return -1;}// copy params from format to codecret = avcodec_parameters_to_context(codec_ctx, format_ctx->streams[audioStreamIndex]->codecpar);if (ret < 0) {printf("Failed to copy in_stream codecpar to codec context\n");}ret = avcodec_open2(codec_ctx, codec, NULL);if (ret < 0) {printf("avcodec_open2 failed:%s\n", av_err2str(ret));return -1;}printf("%d debug codec_ctx->sample_rate: %d\n", __LINE__,codec_ctx->sample_rate);AVPacket *packet = av_packet_alloc();AVFrame *frame = av_frame_alloc();while (1) {ret = av_read_frame(format_ctx, packet);if (ret < 0) {printf("av_read_frame failed:%s\n", av_err2str(ret));break;}decode(codec_ctx, packet, frame, out_file);}printf("read file finish\n");decode(codec_ctx, NULL, frame, out_file);fclose(in_file);fclose(out_file);av_frame_free(frame);av_packet_free(packet);avformat_close_input(&format_ctx);avcodec_free_context(&codec_ctx);printf("main finish\n");return 0;
}




m4a moov 格式解析:MP4格式解析---M4A是MP4中的音频部分_m4a格式解析_一个专研技术的小蜜蜂的博客-CSDN博客
mp4文件格式解析 - 知乎
参考:ffmpeg 利用AVIOContext自定义IO 输出结果写buffer - 知乎

Creating Custom FFmpeg IO-Context - CodeProject

ffmpeg AVIOContext 自定义 IO 及 seek

相关文章:

FFmpeg 自定义IO CONTEXT实现音频解码,以及seek函数

对于从音频流buffer中解码的场景中&#xff0c;我们需要实现自己的io context 去从buffer中解码&#xff0c;参考ffmepg官方实例&#xff1a;doc/examples/avio_reading.c 关于是否要实现avio context中的seek函数&#xff0c;需要看需要解码什么格式&#xff0c;大部分格式不…...

技能升级(2023寒假每日一题 13)

小蓝最近正在玩一款 RPG 游戏。 他的角色一共有 N N N 个可以加攻击力的技能。 其中第 i i i 个技能首次升级可以提升 A i A_i Ai​ 点攻击力&#xff0c;以后每次升级增加的点数都会减少 B i B_i Bi​。 ⌈ A i / B i ⌉ ⌈A_i/B_i⌉ ⌈Ai​/Bi​⌉&#xff08;上取整&a…...

低频量化之 可转债 配债数据及策略 - 全网独家

目录 历史文章可转债配债数据 待发转债&#xff08;进展统计&#xff09;待发转债&#xff08;行业统计&#xff09;待发转债&#xff08;5证监会通过&#xff0c;PE排序&#xff09;待发转债&#xff08;5证监会通过&#xff0c;安全垫排序&#xff09;待发转债&#xff08;5证…...

Code area 和Data area的区别

Code Area FLASH &#xff1a;程序在这个flash运行时&#xff0c;几乎没有延时&#xff0c; 运行速度以时钟设置为准。 Data Area FLASH&#xff1a; 程序在这段flash运行时&#xff0c;每条语句都有延时&#xff0c; 最后的速度可能是以10M为时钟&#xff08;举例&#xff09;…...

Oracle LiveLabs DB Security (数据库安全)实验汇总

在Oracle LiveLabs中&#xff0c;和数据库安全相关的实验分为2个系列&#xff0c;共12个实验。 Oracle数据库安全架构如下图&#xff1a; 这些实验涉及了Oracle安全相关的特性&#xff0c;企业版选件&#xff0c;独立产品和服务。 关于Oracle安全产品的中文主页可见&#…...

PAT A1012 The Best Rank

1012 The Best Rank 分数 25 作者 CHEN, Yue 单位 浙江大学 To evaluate the performance of our first year CS majored students, we consider their grades of three courses only: C - C Programming Language, M - Mathematics (Calculus or Linear Algrbra), and E -…...

“我和AI抠图网站的秘密情缘“

在浏览器里面意外发现了一个AI抠图工&#xff0c;了解了一下&#xff0c;AI抠图基于深度学习框架&#xff0c;结合智能检测识别技术&#xff0c;目前已能够实现高精视&#xff0c;秒级全自动主体、场景像素级识别等的分割能力。 一款好的抠图工具&#xff0c;可以把照片变得更加…...

最多能打多少场比赛呢

凌乱的yyy / 线段覆盖 题目背景 快 noip 了&#xff0c;yyy 很紧张&#xff01; 题目描述 现在各大 oj 上有 n n n 个比赛&#xff0c;每个比赛的开始、结束的时间点是知道的。 yyy 认为&#xff0c;参加越多的比赛&#xff0c;noip 就能考的越好&#xff08;假的&#x…...

鸿蒙Hi3861学习二-程序烧录与日志输出

一、准备事项 开发板&#xff1a;BearPi-Hm Nano windows工具&#xff1a;HiBurn.exe https://pan.baidu.com/s/18OQD1_BvjNKD_J2e2iX3qg?pwdadrs 提取码&#xff1a;adrs windows工具&#xff1a;MobaXterm和RaiDrive 把ubuntu文件夹映射到windows本地。可以参考如下链接&am…...

typescript Awaited<Type>教程用法

typescript Awaited教程用法 文章目录 typescript Awaited<Type>教程用法 ts4.5发布了Awaited&#xff0c;但是很多人不明白Awaited的用法。 首先看一下官方的说明&#xff1a;这种类型旨在模拟函数await中的操作async&#xff0c;或 s.then()上的方法——特别是它们递归…...

AES硬件运算单元

功能描述 AES单元主要功能如下: 支持解密密钥扩展 支持128bit/192bit/256bit的密钥长度支持ECBCBCCTRM支持DMA进行自动数据传输 支持GF(2^128)域下的乘法&#xff0c;支持GMAC 工作模式 AES有4种工作模式&#xff0c;通过配置MODE1:0]寄存器设置。 模式1:用存储在AES KEYRx寄存…...

mulesoft MCIA 破釜沉舟备考 2023.04.28.26 (易错题)

mulesoft MCIA 破釜沉舟备考 2023.04.28.26 (易错题) 1. According to MuleSoft, what is a major distinguishing characteristic of an application network in relation to the integration of systems, data, and devices?2. An integration team follows MuleSoft’s r…...

k210单片机定时器的应用

定时器应该是一个单片机的标准配置&#xff0c;所以k210也是有的&#xff0c;拥有3个定时器&#xff0c;具体的使用方法我们往下看&#xff1a; 分步介绍&#xff1a; 首先是相关模块的使用 构造函数&#xff1a; machine.Timer(id,channel,modeTimer.MODE_ONE_SHOT,period100…...

linux0.12-7-1

[272页] 第7章 初始化程序 1、main.c主要内核初始化工作。 2、如果能完全理解这里调用的所有程序&#xff0c;那么看完这张内容后应该对Linux内核有了大致的了解。 3、 有一定的C语言知识 4、 需要GNU gcc手册在身边作为参考&#xff0c;因为在内核代码很多地方使用gcc的扩展…...

设置 文本框 自动填充背景颜色 为白色

关于autofill伪类的 兼容性&#xff1a; 在现代浏览器中&#xff0c;包括Chrome、Safari、Firefox等&#xff0c;都支持:autofill伪类&#xff0c;但在使用时必须加上浏览器前缀-webkit-&#xff0c;即:-webkit-autofill。 在旧版的浏览器中&#xff0c;可能不支持:autofill伪…...

Bitmap引起的OOM问题

作者&#xff1a;向阳逐梦 1.什么是OOM&#xff1f;为什么会引起OOM&#xff1f; 答&#xff1a;Out Of Memory(内存溢出)&#xff0c;我们都知道Android系统会为每个APP分配一个独立的工作空间&#xff0c;或者说分配一个单独的Dalvik虚拟机&#xff0c;这样每个APP都可以独立…...

【JavaEE初阶】认识线程(Thread)

目录 &#x1f33e; 前言 &#x1f33e; 了解线程 &#x1f308;1.1 线程是什么&#xff1f; &#x1f308;1.2 一些基本问题 &#x1f33e;2、创建线程的方式 &#x1f308; 2.1 继承Thread类 &#x1f308; 2.2 实现Runnable接口并重写run()方法 &#x1f308; 注意…...

自动化运维工具一Ansible Roles实战

目录 一、Ansible Roles概述 1.1.roles官方的目录结构 1.2.Ansible Roles依赖关系 二、Ansible Roles案例实战 2.1.Ansible Roles NFS服务 2.2 Roles Memcached 2.3 Roles-rsync服务 一、Ansible Roles概述 之前介绍了 Playbook 的使用方法&#xff0c;对于批量任务的部…...

json 中有递归parentId节点转 c#实体类时如何处理

如果您有一个具有递归parentId节点的JSON数据&#xff0c;并且您需要将其转换为C#实体类&#xff0c;则可以使用以下方法&#xff1a; 创建一个类来表示JSON对象的节点&#xff0c;包括它的属性和子节点。 public class Node {public int Id { get; set; }public string Name …...

给大家介绍几个手机冷门但好用的小技巧

技巧一&#xff1a;拍照识别植物 手机的拍照识别植物功能是指在使用手机相机时&#xff0c;可以通过对植物进行拍照&#xff0c;并通过植物识别技术&#xff0c;获取植物的相关信息和资料。其主要优点如下&#xff1a; 方便实用&#xff1a;使用拍照识别植物功能&#xff0c;…...

日语AI面试高效通关秘籍:专业解读与青柚面试智能助攻

在如今就业市场竞争日益激烈的背景下&#xff0c;越来越多的求职者将目光投向了日本及中日双语岗位。但是&#xff0c;一场日语面试往往让许多人感到步履维艰。你是否也曾因为面试官抛出的“刁钻问题”而心生畏惧&#xff1f;面对生疏的日语交流环境&#xff0c;即便提前恶补了…...

设计模式和设计原则回顾

设计模式和设计原则回顾 23种设计模式是设计原则的完美体现,设计原则设计原则是设计模式的理论基石, 设计模式 在经典的设计模式分类中(如《设计模式:可复用面向对象软件的基础》一书中),总共有23种设计模式,分为三大类: 一、创建型模式(5种) 1. 单例模式(Sing…...

SkyWalking 10.2.0 SWCK 配置过程

SkyWalking 10.2.0 & SWCK 配置过程 skywalking oap-server & ui 使用Docker安装在K8S集群以外&#xff0c;K8S集群中的微服务使用initContainer按命名空间将skywalking-java-agent注入到业务容器中。 SWCK有整套的解决方案&#xff0c;全安装在K8S群集中。 具体可参…...

论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(二)

HoST框架核心实现方法详解 - 论文深度解读(第二部分) 《Learning Humanoid Standing-up Control across Diverse Postures》 系列文章: 论文深度解读 + 算法与代码分析(二) 作者机构: 上海AI Lab, 上海交通大学, 香港大学, 浙江大学, 香港中文大学 论文主题: 人形机器人…...

【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)

服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...

【机器视觉】单目测距——运动结构恢复

ps&#xff1a;图是随便找的&#xff0c;为了凑个封面 前言 在前面对光流法进行进一步改进&#xff0c;希望将2D光流推广至3D场景流时&#xff0c;发现2D转3D过程中存在尺度歧义问题&#xff0c;需要补全摄像头拍摄图像中缺失的深度信息&#xff0c;否则解空间不收敛&#xf…...

376. Wiggle Subsequence

376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...

成都鼎讯硬核科技!雷达目标与干扰模拟器,以卓越性能制胜电磁频谱战

在现代战争中&#xff0c;电磁频谱已成为继陆、海、空、天之后的 “第五维战场”&#xff0c;雷达作为电磁频谱领域的关键装备&#xff0c;其干扰与抗干扰能力的较量&#xff0c;直接影响着战争的胜负走向。由成都鼎讯科技匠心打造的雷达目标与干扰模拟器&#xff0c;凭借数字射…...

什么?连接服务器也能可视化显示界面?:基于X11 Forwarding + CentOS + MobaXterm实战指南

文章目录 什么是X11?环境准备实战步骤1️⃣ 服务器端配置(CentOS)2️⃣ 客户端配置(MobaXterm)3️⃣ 验证X11 Forwarding4️⃣ 运行自定义GUI程序(Python示例)5️⃣ 成功效果![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/55aefaea8a9f477e86d065227851fe3d.pn…...

图表类系列各种样式PPT模版分享

图标图表系列PPT模版&#xff0c;柱状图PPT模版&#xff0c;线状图PPT模版&#xff0c;折线图PPT模版&#xff0c;饼状图PPT模版&#xff0c;雷达图PPT模版&#xff0c;树状图PPT模版 图表类系列各种样式PPT模版分享&#xff1a;图表系列PPT模板https://pan.quark.cn/s/20d40aa…...