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中解码的场景中,我们需要实现自己的io context 去从buffer中解码,参考ffmepg官方实例:doc/examples/avio_reading.c 关于是否要实现avio context中的seek函数,需要看需要解码什么格式,大部分格式不…...
技能升级(2023寒假每日一题 13)
小蓝最近正在玩一款 RPG 游戏。 他的角色一共有 N N N 个可以加攻击力的技能。 其中第 i i i 个技能首次升级可以提升 A i A_i Ai 点攻击力,以后每次升级增加的点数都会减少 B i B_i Bi。 ⌈ A i / B i ⌉ ⌈A_i/B_i⌉ ⌈Ai/Bi⌉(上取整&a…...

低频量化之 可转债 配债数据及策略 - 全网独家
目录 历史文章可转债配债数据 待发转债(进展统计)待发转债(行业统计)待发转债(5证监会通过,PE排序)待发转债(5证监会通过,安全垫排序)待发转债(5证…...

Code area 和Data area的区别
Code Area FLASH :程序在这个flash运行时,几乎没有延时, 运行速度以时钟设置为准。 Data Area FLASH: 程序在这段flash运行时,每条语句都有延时, 最后的速度可能是以10M为时钟(举例)…...

Oracle LiveLabs DB Security (数据库安全)实验汇总
在Oracle LiveLabs中,和数据库安全相关的实验分为2个系列,共12个实验。 Oracle数据库安全架构如下图: 这些实验涉及了Oracle安全相关的特性,企业版选件,独立产品和服务。 关于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抠图工,了解了一下,AI抠图基于深度学习框架,结合智能检测识别技术,目前已能够实现高精视,秒级全自动主体、场景像素级识别等的分割能力。 一款好的抠图工具,可以把照片变得更加…...
最多能打多少场比赛呢
凌乱的yyy / 线段覆盖 题目背景 快 noip 了,yyy 很紧张! 题目描述 现在各大 oj 上有 n n n 个比赛,每个比赛的开始、结束的时间点是知道的。 yyy 认为,参加越多的比赛,noip 就能考的越好(假的&#x…...

鸿蒙Hi3861学习二-程序烧录与日志输出
一、准备事项 开发板:BearPi-Hm Nano windows工具:HiBurn.exe https://pan.baidu.com/s/18OQD1_BvjNKD_J2e2iX3qg?pwdadrs 提取码:adrs windows工具:MobaXterm和RaiDrive 把ubuntu文件夹映射到windows本地。可以参考如下链接&am…...
typescript Awaited<Type>教程用法
typescript Awaited教程用法 文章目录 typescript Awaited<Type>教程用法 ts4.5发布了Awaited,但是很多人不明白Awaited的用法。 首先看一下官方的说明:这种类型旨在模拟函数await中的操作async,或 s.then()上的方法——特别是它们递归…...
AES硬件运算单元
功能描述 AES单元主要功能如下: 支持解密密钥扩展 支持128bit/192bit/256bit的密钥长度支持ECBCBCCTRM支持DMA进行自动数据传输 支持GF(2^128)域下的乘法,支持GMAC 工作模式 AES有4种工作模式,通过配置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单片机定时器的应用
定时器应该是一个单片机的标准配置,所以k210也是有的,拥有3个定时器,具体的使用方法我们往下看: 分步介绍: 首先是相关模块的使用 构造函数: machine.Timer(id,channel,modeTimer.MODE_ONE_SHOT,period100…...
linux0.12-7-1
[272页] 第7章 初始化程序 1、main.c主要内核初始化工作。 2、如果能完全理解这里调用的所有程序,那么看完这张内容后应该对Linux内核有了大致的了解。 3、 有一定的C语言知识 4、 需要GNU gcc手册在身边作为参考,因为在内核代码很多地方使用gcc的扩展…...
设置 文本框 自动填充背景颜色 为白色
关于autofill伪类的 兼容性: 在现代浏览器中,包括Chrome、Safari、Firefox等,都支持:autofill伪类,但在使用时必须加上浏览器前缀-webkit-,即:-webkit-autofill。 在旧版的浏览器中,可能不支持:autofill伪…...

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

【JavaEE初阶】认识线程(Thread)
目录 🌾 前言 🌾 了解线程 🌈1.1 线程是什么? 🌈1.2 一些基本问题 🌾2、创建线程的方式 🌈 2.1 继承Thread类 🌈 2.2 实现Runnable接口并重写run()方法 🌈 注意…...

自动化运维工具一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 的使用方法,对于批量任务的部…...
json 中有递归parentId节点转 c#实体类时如何处理
如果您有一个具有递归parentId节点的JSON数据,并且您需要将其转换为C#实体类,则可以使用以下方法: 创建一个类来表示JSON对象的节点,包括它的属性和子节点。 public class Node {public int Id { get; set; }public string Name …...

给大家介绍几个手机冷门但好用的小技巧
技巧一:拍照识别植物 手机的拍照识别植物功能是指在使用手机相机时,可以通过对植物进行拍照,并通过植物识别技术,获取植物的相关信息和资料。其主要优点如下: 方便实用:使用拍照识别植物功能,…...

Xshell远程连接Kali(默认 | 私钥)Note版
前言:xshell远程连接,私钥连接和常规默认连接 任务一 开启ssh服务 service ssh status //查看ssh服务状态 service ssh start //开启ssh服务 update-rc.d ssh enable //开启自启动ssh服务 任务二 修改配置文件 vi /etc/ssh/ssh_config //第一…...

【WiFi帧结构】
文章目录 帧结构MAC头部管理帧 帧结构 Wi-Fi的帧分为三部分组成:MAC头部frame bodyFCS,其中MAC是固定格式的,frame body是可变长度。 MAC头部有frame control,duration,address1,address2,addre…...
SciencePlots——绘制论文中的图片
文章目录 安装一、风格二、1 资源 安装 # 安装最新版 pip install githttps://github.com/garrettj403/SciencePlots.git# 安装稳定版 pip install SciencePlots一、风格 简单好用的深度学习论文绘图专用工具包–Science Plot 二、 1 资源 论文绘图神器来了:一行…...

2.Vue编写一个app
1.src中重要的组成 1.1main.ts // 引入createApp用于创建应用 import { createApp } from "vue"; // 引用App根组件 import App from ./App.vue;createApp(App).mount(#app)1.2 App.vue 其中要写三种标签 <template> <!--html--> </template>…...
Qwen3-Embedding-0.6B深度解析:多语言语义检索的轻量级利器
第一章 引言:语义表示的新时代挑战与Qwen3的破局之路 1.1 文本嵌入的核心价值与技术演进 在人工智能领域,文本嵌入技术如同连接自然语言与机器理解的“神经突触”——它将人类语言转化为计算机可计算的语义向量,支撑着搜索引擎、推荐系统、…...
Python如何给视频添加音频和字幕
在Python中,给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加,包括必要的代码示例和详细解释。 环境准备 在开始之前,需要安装以下Python库:…...
数据库分批入库
今天在工作中,遇到一个问题,就是分批查询的时候,由于批次过大导致出现了一些问题,一下是问题描述和解决方案: 示例: // 假设已有数据列表 dataList 和 PreparedStatement pstmt int batchSize 1000; // …...
【C++从零实现Json-Rpc框架】第六弹 —— 服务端模块划分
一、项目背景回顾 前五弹完成了Json-Rpc协议解析、请求处理、客户端调用等基础模块搭建。 本弹重点聚焦于服务端的模块划分与架构设计,提升代码结构的可维护性与扩展性。 二、服务端模块设计目标 高内聚低耦合:各模块职责清晰,便于独立开发…...

初学 pytest 记录
安装 pip install pytest用例可以是函数也可以是类中的方法 def test_func():print()class TestAdd: # def __init__(self): 在 pytest 中不可以使用__init__方法 # self.cc 12345 pytest.mark.api def test_str(self):res add(1, 2)assert res 12def test_int(self):r…...
Java毕业设计:WML信息查询与后端信息发布系统开发
JAVAWML信息查询与后端信息发布系统实现 一、系统概述 本系统基于Java和WML(无线标记语言)技术开发,实现了移动设备上的信息查询与后端信息发布功能。系统采用B/S架构,服务器端使用Java Servlet处理请求,数据库采用MySQL存储信息࿰…...