音视频开发:音频编码原理+采集+编码实战

这篇具有很好参考价值的文章主要介绍了音视频开发:音频编码原理+采集+编码实战。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

原理:

  1. 消除冗余信息,压缩量最大,也叫有损压缩
  • 剔除人耳听觉范围外的音频信号20Hz以下和20000Hz以上;
  • 去除被掩蔽的音频信号,信号的遮蔽可以分为频域遮蔽和时域遮蔽;
  • 频域遮蔽效应
    屏蔽70分贝以下,20HZ以下,20000HZ以上
    屏蔽分贝小,频率小的声音
    两个频率相近发出的声音,去除低强度的,也就是分贝高的会盖住分贝低的

音视频开发:音频编码原理+采集+编码实战,音视频开发,音视频,音视频开发,C++音视频,PCM,AAC,视音频编解码,编解码

  • 时域遮蔽效应:
    根根时间推移,相近频率且同时出现的声音,声音强度高的遮蔽强度低的声音,并且去除同一时间段前后杂音,前遮蔽50毫秒,后遮蔽200毫秒,在这段时间内的声音,强度越接近就越会被屏蔽。

音视频开发:音频编码原理+采集+编码实战,音视频开发,音视频,音视频开发,C++音视频,PCM,AAC,视音频编解码,编解码

  1. 去除冗余信息后,再进行无损压缩;
  • 无损压缩就是压缩后的数据能够解压缩进行还原,有损则不能;
  • 熵编码中有
    哈夫曼编码:用一个很小的二进制数代替一个长的字符串,频率越高,编码越小,频率越低,编码越长
    算术编码:利用小数进行编码,在香农编码的基础改进而来的
    香农编码

音频编码过程

数据先同时通过 时域转频域变换器和心理学模型处理数据,前者将数据转换成多种频段的数据,然后剔除不需要的频段数据,后者会去除非人耳听到的范围声音和一些复合声音,最后将两者合并经过量化编码,无损编码之类的,形成比特流数据,在此之前还会有一些辅助数据,此后数据就会变得非常小;

音视频开发:音频编码原理+采集+编码实战,音视频开发,音视频,音视频开发,C++音视频,PCM,AAC,视音频编解码,编解码

常见的音频编码器

opus、aac、Ogg、Speex、iLBC、AMR、G.711, 最常用的编码器是opus aac。
opus常用于直播,尤其是无延迟的直播,webrtc默认使用opus;
AAC是应用最广泛的编解码;
Ogg收费;
Speex支持回音消除;
G.711一般用于固定电话,声音损耗严重,通话会失真;

本文福利, 免费领取C++音视频学习资料包+学习路线大纲、技术视频/代码,内容包括(音视频开发,面试题,FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,编解码,推拉流,srs),有需要的可以进企鹅裙927239107领取哦~

音视频开发:音频编码原理+采集+编码实战,音视频开发,音视频,音视频开发,C++音视频,PCM,AAC,视音频编解码,编解码

音视频开发:音频编码原理+采集+编码实战,音视频开发,音视频,音视频开发,C++音视频,PCM,AAC,视音频编解码,编解码 

AAC比较适合有一定延迟的直播,AAC-LD属于低延迟编码器

  • AAC编码器:目前应用最广泛,如iOS、安卓和其他嵌入式设备都包含了AAC硬件编解码器,主要学习这个编码器;
    用来取代mp3,比mp3更高的压缩比和保真性更强;

音视频开发:音频编码原理+采集+编码实战,音视频开发,音视频,音视频开发,C++音视频,PCM,AAC,视音频编解码,编解码

常用的规格有AAC LC、AAC HE V1 、AAC HE V2三种;

音视频开发:音频编码原理+采集+编码实战,音视频开发,音视频,音视频开发,C++音视频,PCM,AAC,视音频编解码,编解码

AAC HE V1 = AAC + SBR;
AAV HE V2 = AAC + SBR + PS;
目前AAC HE V1 已经被取代 V2 取代了;

音视频开发:音频编码原理+采集+编码实战,音视频开发,音视频,音视频开发,C++音视频,PCM,AAC,视音频编解码,编解码

V2的码流跟V1的差别不是很大,根据声音的数据变化,如果两个声道的差别很大,码流差别就会越小;

AAC 中header有两种格式:

就相当于在aac数据前面加了个Header,header里面就会包含aac数据的一些信息,方便进行编解码

  1. ADIF(Audio data interchange format): 特点是只能从头开始解码,可以确定的找到音频数据的开始部分,不能从音频数据中间开始,这种格式常用于磁盘文件中;
  2. ADTS(Audio Data Transport Format):在每一帧的数据里面都会有一个同步字,也就是每帧都有一个header,所以他可以在任意的位置开始进行解码,就像流式数据;
  • ADTS结构: 由7-9个字节组成,通常情况下是7个字节,如果有CRC 就是9个字节,字节中的每一位都有独特的含义;
    • 1~12bit:全部是1也就是0xFFF,表示是同步字;
    • 13:编码规范 0 = MPEG-4 1 = MPEG-2;
    • 14~15:总是0;
    • 16:是否有保护 1 代表 没有 CRC 0 代表有CRC;
    • 17~18:表示的是MPEG-4的音频类型:AAC LC、 AAC HE V1 、AAC HE V2
    • 19~22:表示的是采样率
    • 24~26:通道数
    • 31~33:数据长度,也包括了header的长度
  • 剩余的之后补上

音视频开发:音频编码原理+采集+编码实战,音视频开发,音视频,音视频开发,C++音视频,PCM,AAC,视音频编解码,编解码

其中每一十进制数对应的含义:

Audio Object Type: 在代码中实际获取类型的时候需要进行+1,才是下面的类型
1 == AAC main
2 == AAC LC
5 == SBR == HE V1
29 == ps == HE V2

其中的采样率是通过十进制数表示的一个采样率,有一个表,比如:0 == 96000Hz 1 == 88200HZ 等

音视频开发:音频编码原理+采集+编码实战,音视频开发,音视频,音视频开发,C++音视频,PCM,AAC,视音频编解码,编解码

音频采集实战

每个端音频采集的底层和应用层的库是不一样的,所以使用ffmpeg中间层能够实现跨平台开发;

  • Android端的底层库是AudioRecorder,应用层是MediaRecorder;
  • iOS端的底层库是AudioUnit,应用层是AVFoundation;
  • Windows端的常用的是Directshow OpenAL 还有Windows7之上的AudioCore;

使用ffmpeg有两种采集方式:

  1. 使用命令方式,命令详情查看ffmpeg相关指令的那篇
  2. 使用代码调用api的方式
  • 在mac下的动态库需要对动态库进行签名

获取本地签名证书列表:/usr/bin/security find-identity -v -p codesigning
查看动态库是否签名: codesign -d -vv 动态库文件
签名命令:codesign -fs "iPhone Distribution: 你的签名证书." 动态库文件
xcode环境:13.2.1
签名了如果还是报错,关掉沙盒并且设置 Enable Hardened Runtime 为NO
在项目中设置user header search path的时候,要使用全路径方式,我使用$(PROJECT_NAME)方式,有的头文件在链接的时候会报错;

采集音频的步骤:

  1. 打开输入输出设备,涉及的包是avdevice avformat 注册设备 设置采集方式,根据平台选择,即设置输入 打开音频设备
  2. 获取数据包 包:avcodec 主要使用av_read_frame方法获取数据 将数据放入packet中 在读取的时候注意缓冲区无未准备好的情况
  3. 将数据输出到文件 创建文件--- fopen 将数据写入文件-- fwrite 关闭文件 -- fclose
  • 打开设备 ·
void startRecorder(void) {
    // 上下文
    AVFormatContext *av_context = NULL;
    AVDictionary *options = NULL;
    // 1. 注册设备
    avdevice_register_all();
    
    // 2. 设置采集方式
    //设置采集方式 mac os 下是AVfoundation Windows下是dshow  linux 下是alsa
    AVInputFormat *format = av_find_input_format("avfoundation");
    
    // 3. 打开设备
    //里面的识别格式为[[video device]:[audio device]]  这里写0 是获取第1个音频设备
    char *name = ":0";

    // url 是路径 可以是网络路径也可以是本地路径 本地路径mac下的格式是 video : audio 这里表示获取第一个音频设备
    int result = avformat_open_input(&av_context, name, format, &options);
    
    if (result != 0) {
        
        char errors[1024];
        // 根据返回值生成错误信息
        av_make_error_string(errors, 1024, result);
        printf("打开设备失败:%s\n", errors);
        return;
    }
    
    printf("打开设备成功!\n");
    

    get_audio_packet(av_context,&packet_callback);
    
    // 关闭输入 上下文
    avformat_close_input(&av_context);
    
}
  • 读取数据和存储到文件
void get_audio_packet(AVFormatContext *context, void (*packet_callback)(AVPacket)) {
    
    // w == 写  b == 二进制  + == 没有就创建文件
    FILE *f = fopen("/Users/cunw/Desktop/learning/音视频学习/音视频文件/code_recorder.pcm", "wb+");
    
    
    AVPacket *packet = av_packet_alloc();
    int result = -1;
    // 循环读取设备信息
    // result == -35 是Resource temporarily unavailable 因为获取太频繁 设备未准备好,还正在处理数据  
  // 因为输入设备准备好需要时间  睡一秒后再读取  
     sleep(1.0);
    while ((result = av_read_frame(·context, packet)) == 0  || result == -35) {
     
        if (packet->size > 0) {
            packet_callback(*packet);
            fwrite(packet->data, packet->size, 1, f);
            // 每读取一次 就清空数据包 不然数据包会一直增大
            av_packet_unref(packet);
          
        }
    }
    
    if (result != 0) {
        char errors[1024];
        av_make_error_string(errors, 1024, result);
        
        printf("get packet occured error is \"%s\" \n", errors);
    }
    
    // 将缓冲区剩余的数据 强制写入文件
    fflush(f);
    fclose(f);
    
    // 释放packet空间
    av_packet_free(&packet);
    
}
// 回调函数
void packet_callback(AVPacket packet) {
    
    printf("packet size is %d\n",packet.size);

}
  • 播放
  • ffplay 播放pcm数据: ffplay -ar(采样率) 44100 -ac(通道数) 2 -f(采样大小)f32le 文件名

音频编解码实战

音频重采样

就是将音频三元组(采样率 采样大小 通道数)的值转成另外一组值

1. 应用场景:

1、从设备采集的音频数据与编码器要求的不一致;
2、扬声器要求的音频数据与要播放的音频数据不一致;
3、方便运算:例如回音消除 将多声道变为单声道;

2. 如何判断是否需要重采样

  • 了解音频设备的参数
  • 查看ffmpeg源码

3. 重采样的步骤

api:需要使用libswresample库

1. 创建重采样上下文

 - swr_alloc_set_opts 通过设置采样参数获取上下文      

2. 设置参数

- 参数大体分为输出的采样率、采样大小、声道和输入的采样率、采样大小、声道;
- out_ch_layout:表示声道也可以是布局(扬声器的布局)AV_CH_LAYOUT_STEREO  立体声;
- out_sample_fmt:输出的采样格式 16 = AV_SAMPLE_FMT_S16 或者 32 =  AV_SAMPLE_FMT_FLT;
-  av_sample_fmt_s16 in_ch_layout:输入的声道布局  ;
-  in_sample_fmt 输入的采样格式 ;
-  in_sample_rate:  输入的采样率;
-  后两位是log相关 0,null

3. 初始化重采样

- swr_init 初始化上下文  

4. 进行重采样

- swr_convert 开始转换 ,目的就是将输入缓冲区的数据写入输出缓冲区
  out:输出结果缓冲区 out_count:每个通道的采样数 
  in:输入的缓冲区 in_count:输入的单个通道的采样数  
- 因为重采样的数据需要重新构造所以需要创建输入缓冲区和输出缓冲区  
  使用av_sample_array_and_samples audio_data创建
  其中的单通道采样数(单位是字节)`nb_samples = pkt.size / (32位 / 8) / 2(通道数)` 
  linessize:缓冲区大小  align:对齐 0 
- 在转换前需要将pkt的data按字节拷贝到输入缓冲区,调用memcpy需要引用string.h

- 将输出数据写入文件
  将输出缓冲区已经转换的数据写入文件

5. 释放资源

- 还有输入输出缓冲区的释放av_freep
- swr_free释放上下文

重采样上下文初始化代码

SwrContext * init_swr_context(void) {
    
    SwrContext *context = NULL;
    
    // 假设已经提前知道输入音频数据的三要素的值 AV_CH_LAYOUT_STEREO, AV_SAMPLE_FMT_FLT, 44100
    context = swr_alloc_set_opts(NULL,
                                 AV_CH_LAYOUT_STEREO,
                                 AV_SAMPLE_FMT_S16,
                                 44100,
                                 AV_CH_LAYOUT_STEREO,
                                 AV_SAMPLE_FMT_FLT,
                                 44100,
                                 0, NULL);
    
    int result = swr_init(context);
    
    if (result != 0) {
        char error[1024];
        av_make_error_string(error, 1024, result);
        printf("初始化重采样上下文失败:%s", error);
    }
    
    return context;
}

将采集的数据重采样后 写入文件代码

void get_audio_packet(AVFormatContext *context, void (*packet_callback)(AVPacket)) {
    
    // w == 写  b == 二进制  + == 没有就创建文件
    FILE *f = fopen("/Users/cunw/Desktop/learning/音视频学习/音视频文件/resample.pcm", "wb+");

    // 初始化重采样上下文
    SwrContext *swr_context = init_swr_context();

    // 初始化转换的输入输出缓冲区
    uint8_t **out_buffer = NULL;
    int linesize_out = 0;
    av_samples_alloc_array_and_samples(&out_buffer, &linesize_out, 2, 512, AV_SAMPLE_FMT_S16, 0);
    uint8_t **in_buffer = NULL;
    int linesize_in = 0;
    // nb_samples 单通道采样数 4096 / (32 / 8) / 2 = 1024
    av_samples_alloc_array_and_samples(&in_buffer, &linesize_in, 2, 512, AV_SAMPLE_FMT_FLT, 0);


    AVPacket *packet = av_packet_alloc();
    int result = -1;
    // 循环读取设备信息
    // result == -35 是Resource temporarily unavailable 因为获取太频繁 设备未准备好,还正在处理数据
    sleep(1);
    while (((result = av_read_frame(context, packet)) == 0  || result == -35) && isRecording == 1) {

        if (packet->size > 0) {


            // 开始转换数据
            // 先将音频数据拷贝到输入缓冲区  只是重采样音频的话  只需要处理数组的第一个
            memcpy(in_buffer[0], packet->data, packet->size);
            // 再进行转换
            swr_convert(swr_context, out_buffer, 512, (const uint8_t **)in_buffer, 512);

            fwrite(out_buffer[0],linesize_out, 1, f);
            // 每读取一次 就清空数据包 不然数据包会一直增大
            av_packet_unref(packet);
        }

    }

    if (result != 0) {
        char errors[1024];
        av_make_error_string(errors, 1024, result);

        printf("get packet occured error is \"%s\" \n", errors);
    }

    // 释放重采样资源
    if (in_buffer) {
        av_freep(&in_buffer[0]);
    }
    if (out_buffer) {
        av_freep(&out_buffer[0]);
    }
    av_freep(&in_buffer);
    av_freep(&out_buffer);
    swr_free(&swr_context);

    // 将缓冲区剩余的数据 强制写入文件
    fflush(f);
    fclose(f);

    // 释放packet空间
    av_packet_free(&packet);
    
    
}

ffmpeg 音频数据编码

本文福利, 免费领取C++音视频学习资料包+学习路线大纲、技术视频/代码,内容包括(音视频开发,面试题,FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,编解码,推拉流,srs),有需要的可以进企鹅裙927239107领取哦~

在使用fdk_aac编码器的时候,由于默认的ffmpeg有自带的aac,所以通过avcodec_find_encoder_by_name("libfdk_aac")就获取不到。在编译的时候加上--enable-libfdk-aac。注意:重新编译安装ffmpeg之前最好先删掉之前的ffmpeg,然后更新项目中的动态库;
如果还不行,试试单独下载安装[fdk_aac](https://www.linuxfromscratch.org/blfs/view/svn/multimedia/fdk-aac.html),再重新编译ffmpeg

  1. 创建编码器 avcodec
    1. avcodec_find_encoder 一种通过名字查找 一种是通过id查找,id的查找方式只会找默认的编码器,比如aac,如果是fdkaac就需要通过名字查找;
  2. AV_CODEC_ID_AAC | opus 其他编码器
  3. "libfdk_aac", aac默认的规格是AAC LC
  4. 创建上下文 avcodexcontext
    设置音频三要素
  5. avcodec_alloc_context3
    3表示第三个版本
  6. sample_fmt = av_sample_FMT_S16 aac编码器不支持flt 32位
  7. chnnel_layout = AV_CH_LAYOUT_STEREO( 或者chanels = 2)
  8. sample_rate = 44100
  9. bit_rate = 64000; (KB 码率)可选设置
  10. profile = FF_PROFILE_AAC_HE_V2; (只有bit_rate=0 才有用) 可选设置,设置编码器规格
  1. 打开编码器
  2. avcodex_opne2
    2表示第二个版本
    送数据给编码器时,编码器内部有一个缓冲区,缓冲一部分数据后才进行编码
  1. 编码
  2. 用AVFrame包装未编码的数据,相当于是个输入,用AVPacket包装已编码的数据,相当于是个输出;
  3. 调用avcodec_send_frame 将avframe缓冲区的数据发送给编码器,如果返回值大于0,就表示数据成功发送到了编码器,接着就可以通过循环使用 avcodec_receive_packet读取编码好的数据到AVPacket,并写入文件中,如果读取的结果是AVERROR(EAGIN)或者是AVERROR_EOF,就停止读取,如果是其他的负数,就停止编码;
  4. av_frame_alloc 堆区初始化frame
  5. 设置frame的nb_samples 单通道一个数据帧采样数 512
  1. format 每个采样的大小 av_sample_fmt_s16
  2. channel_layout 声道 av_ch_layout_stereo
  3. av_frame_get_buffer 分配frame里面buffer的大小
  4. 还要判断frame的buffer是否分配成功
  5. 将重采样后的数据memcpy到frame->data中
  6. 再将frame中的数据塞到编码器上下文中 avcodec_send_frame,该函数会返回一个int , 当结果>=0的时候表明有数据已经在编码缓冲区了;
  7. avcodec_receive_packet 读取编码好的数据 avpacket
  8. av_packet_alloc 分配编码后的数据空间
  9. 因为编码器上下文中有一个缓冲区,其中会缓存多个frame,因此并不是每塞一个frame就会有一个packet出来,所以需要通过一个while循环判断编码器的数据是否>=0,再通过avcodec_receive_packet获取packet,该函数也会返回一个int,如果返回值>=0表明获取成功,如果失败直接退出编码,这个值返回值还有其他含义,需要判断eagain 表明编码器没有数据了或者是有数据但是不够编码 这个eagain需要用AVERROR包装成一个负数,表明数据还没准备好 averror_eof 表明一点数据都没有了;
  10. 最后将数据编码后的数据写入到文件pkt->data,数据格式就是aac了;
  11. 在停止录制的时候,由于编码的缓存区可能还有数据,在最后关闭之前,再去取一遍编码数据放入文件;
  1. 释放资源

在结束的时候释放frame(av_frame_free) 和packet(av_packet_frame);文章来源地址https://www.toymoban.com/news/detail-745411.html

编码实战代码:

1. 创建fdk_aac编码器及上下文

AVCodecContext* init_codec_context(void) {
    
    // 创建aac编码器
    AVCodec *codec = avcodec_find_encoder_by_name("libfdk_aac");
    
    // 初始化上下文
    AVCodecContext *context = NULL;
    context = avcodec_alloc_context3(codec);
    context->sample_fmt = AV_SAMPLE_FMT_S16;
    context->sample_rate = 44100;
    context->channel_layout = AV_CH_LAYOUT_STEREO;
    context->bit_rate = 0;
    // bitrate == 0 才会生效
    context->profile = FF_PROFILE_AAC_HE_V2;
    
    int result = avcodec_open2(context, codec, NULL);
    if (result < 0) {
        char error[1024];
        av_make_error_string(error, 1024, result);
        av_log(NULL, AV_LOG_DEBUG, "创建AAC编码器失败:%s",error);
    }
    
    return context;
    
}

2. 创建输入缓冲区

AVFrame* create_audio_input_frame(void) {
    
    AVFrame *codec_frame = NULL;
    codec_frame = av_frame_alloc();
    
    codec_frame->nb_samples = 512;
    codec_frame->channel_layout = AV_CH_LAYOUT_STEREO;
    codec_frame->format = AV_SAMPLE_FMT_S16;
    int buffer_result = av_frame_get_buffer(codec_frame, 0);
    if (buffer_result < 0) {
        char error[1024];
        av_make_error_string(error, 1024, buffer_result);
        printf("frame 缓冲区分配失败:%s", error);
    }
    
    return codec_frame;
}

3. 开始编码并写入文件

void audio_encode(AVCodecContext *ctx, AVFrame *frame, AVPacket *packet, FILE *fl) {
    
    
    // 将数据送入编码器
    int codec_result = avcodec_send_frame(ctx, frame);
    while (codec_result >= 0) {
        // 从packet中循环读取编码好的数据
        codec_result = avcodec_receive_packet(ctx, packet);
        if (codec_result == AVERROR(EAGAIN) || codec_result == AVERROR_EOF) {
        
            break;
        } else if (codec_result < 0) {
            char error[1024];
            av_make_error_string(error, 1024, codec_result);
            printf("编码器出错:%s     停止编码", error);
        } else {
            fwrite(packet->data, 1,packet->size, fl);
        }
    }
    if (codec_result < 0) {
        char error[1024];
        av_make_error_string(error, 1024, codec_result);
        printf("将数据送入编码器错误: %s\n",error);
    }
}

4. 调用

  • 先将重采样的数据放入avframe的缓冲区中
memcpy(codec_frame->data[0], out_buffer[0], linesize_out);
  • 再开始编码
audio_encode(codec_context, codec_frame, codec_packet, f);
  • 总览
void get_audio_packet(AVFormatContext *context, void (*packet_callback)(AVPacket)) {
    
    // w == 写  b == 二进制  + == 没有就创建文件
    FILE *f = fopen("/Users/cunw/Desktop/learning/音视频学习/音视频文件/encoder.aac", "wb+");
    
    // 创建编码器上下文
    AVCodecContext *codec_context = init_codec_context();
    // 初始化输入缓冲区  AVframe
    AVFrame *codec_frame = create_audio_input_frame();
    // 初始化编码输出缓冲区
    AVPacket *codec_packet = av_packet_alloc();
    
    // 初始化重采样上下文
    SwrContext *swr_context = init_swr_context();
    // 初始化重采样的缓冲区
    uint8_t **out_buffer = NULL;
    int linesize_out = 0;
    uint8_t **in_buffer = NULL;
    int linesize_in = 0;
    init_resammple_buffer(&in_buffer, &linesize_in, &out_buffer, &linesize_out);
    

    AVPacket *packet = av_packet_alloc();
    int result = -1;
    // 循环读取设备信息
    
    while (isRecording == 1) {
        
        result = av_read_frame(context, packet);
        if (packet->size > 0 && result == 0) {

            packet_callback(*packet);
            // 开始转换数据
            // 先将音频数据拷贝到输入缓冲区  只是重采样音频的话  只需要处理数组的第一个
            memcpy(in_buffer[0], packet->data, packet->size);
            // 再进行转换
            swr_convert(swr_context, out_buffer, 512, (const uint8_t **)in_buffer, 512);
            // 将重采样好的数据按字节拷贝到frame缓冲区
            memcpy(codec_frame->data[0], out_buffer[0], linesize_out);
            audio_encode(codec_context, codec_frame, codec_packet, f);
            // 每读取一次 就清空数据包 不然数据包会一直增大
            av_packet_unref(packet);
        } else if (result == -EAGAIN) {
            // result == -35 是Resource temporarily unavailable 因为设备未准备好,还正在处理数据
            av_usleep(1);
        }
    }
    // 把缓冲区剩余的数据拿出来编码
    audio_encode(codec_context, NULL, codec_packet, f);

    if (result != 0) {
        char errors[1024];
        av_make_error_string(errors, 1024, result);
        printf("get packet occured error is \"%s\" \n", errors);
    }

    // 释放重采样资源
    if (in_buffer) {
        av_freep(&in_buffer[0]);
    }
    if (out_buffer) {
        av_freep(&out_buffer[0]);
    }
    av_freep(&in_buffer);
    av_freep(&out_buffer);
    swr_free(&swr_context);
    
    av_frame_free(&codec_frame);
    av_packet_free(&codec_packet);

    // 将缓冲区剩余的数据 强制写入文件
    fflush(f);
    fclose(f);

    // 释放packet空间
    av_packet_free(&packet);
    
    
}

到了这里,关于音视频开发:音频编码原理+采集+编码实战的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 从原理到实践:音视频编码与解码技术解析

    1.1 引言 音视频编码与解码技术在现代数字媒体领域中扮演着至关重要的角色。随着互联网和移动设备的快速发展,音视频数据的传输和处理变得越来越普遍和重要。理解音视频编码与解码的原理与实践对于开发高质量、高效率的音视频应用程序至关重要。 1.2 音视频编码与解

    2024年02月03日
    浏览(38)
  • 音视频开发 RTMP协议发送H.264编码及AAC编码的音视频(C++实现)

    RTMP(Real Time Messaging Protocol)是专门用来传输音视频数据的流媒体协议,最初由Macromedia 公司创建,后来归Adobe公司所有,是一种私有协议,主要用来联系Flash Player和RtmpServer,如 FMS , Red5 , crtmpserver 等。RTMP协议可用于实现直播、点播应用,通过 FMLE(Flash Media Live Encoder) 推送音

    2023年04月08日
    浏览(42)
  • Android 音视频开发—MediaPlayer音频与视频的播放介绍

    Android多媒体中的——MediaPlayer,我们可以通过这个API来播放音频和视频该类是Androd多媒体框架中的一个重要组件,通过该类,我们可以以最小的步骤来获取,解码和播放音视频。 它支持三种不同的媒体来源: 本地资源 内部的URI,比如你可以通过ContentResolver来获取 外部URL(流

    2024年02月10日
    浏览(43)
  • 音视频编码实战-------pcm+yuv数据转成MP4

    avcodec_find_encoder: 根据编码器ID查找编码器 avcodec_alloc_context3:创建编码器上下文 avcodec_open2:打开编码器 avformat_alloc_output_context2:为输出格式创建复用器上下文 avformat_new_stream:创建音视频流 avcodec_parameters_from_context:将编码器上下文中的参数拷贝到音视频流中的编码器参数中AVCodec

    2024年02月15日
    浏览(43)
  • 音视频处理 ffmpeg中级开发 H264编码

    libavcodec/avcodec.h 常用的数据结构 AVCodec 编码器结构体 AVCodecContext 编码器上下文 AVFrame 解码后的帧 结构体内存的分配和释放 av_frame_alloc 申请 av_frame_free() 释放 avcodec_alloc_context3() 创建编码器上下文 avcodec_free_context() 释放编码器上下文 解码步骤 avcodec_find_decoder 查找解码器 avcod

    2024年02月01日
    浏览(53)
  • Qt音视频开发40-ffmpeg采集桌面并录制

    之前用ffmpeg打通了各种视频文件和视频流以及本地摄像头设备的采集,近期有个客户需求要求将整个桌面屏幕采集下来,并可以录制保存成MP4文件,以前也遇到过类似的需求,由于没有搞过,也没有精力去摸索和测试,所以也就一直耽搁着,近期刚好这个需求又来了,定下心

    2023年04月25日
    浏览(43)
  • 【ESP32音视频传输】②通过I2S采集SPH0645麦克风音频数据并上传到服务端实时播放

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 本文章基于Arduino ESP32 2.07版本,因为2.04版本开始I2S驱动被更改了,所以相同代码可能效果不太同 本文主要参考了:https://atomic14.com/2020/09/12/esp32-audio-input.html ESP32有多种方式从外置麦克风中读取数据:

    2024年02月11日
    浏览(37)
  • 音视频开发系列(10):基于qt的音频推流

    今天分享一下利用qt录制音频,然后再利用ffmpeg推流到nginx服务器,最后再利用vlc进行拉流的demo。 首先介绍一下如何利用qt来进行音频的录制,qt的音频录制主要利用qt的QAudioFormat先进行音频信息的配置。主要需要配置以下的信息: 然后使用QAudioDeviceInfo来获取是否支持改设置

    2024年02月02日
    浏览(39)
  • 安卓音视频开发(3)—— AudioTrack两种方式播放pcm音频

    前言 之前学习了AudioRecord录制pcm音频,与之对应的就是AudioTrack播放pcm音频(MediaPlayer、SoundPool有其他应用场景),它有两种数据加载模式(MODE_STATIC、MODE_STREAM)。 模式 MODE_STATIC :这种模式下,一次将所有的数据放入一个固定的buffer,然后写入到AudioTrack中,后续就不用继续

    2023年04月22日
    浏览(34)
  • RK3568平台开发系列讲解(音视频篇)H264 的编码结构

    🚀返回专栏总目录 沉淀、分享、成长,让自己和他人都能有所收获!😄 📢视频编码的码流结构其实就是指视频经过编码之后得到的二进制数据是怎么组织的,换句话说,就是编码后的码流我们怎么将一帧帧编码后的图像数据分离出来,以及在二进制码流数据中,哪一块数

    2024年02月09日
    浏览(32)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包