音视频项目—基于FFmpeg和SDL的音视频播放器解析(十五)

介绍

在本系列,我打算花大篇幅讲解我的 gitee 项目音视频播放器,在这个项目,您可以学到音视频解封装,解码,SDL渲染相关的知识。您对源代码感兴趣的话,请查看基于FFmpeg和SDL的音视频播放器

如果您不理解本文,可参考我的前一篇文章音视频项目---基于FFmpeg和SDL的音视频播放器解析(十四)

解析

我们这篇文章接着解析 audiooutput 剩余的函数。

有一个很关键的函数,fill_audio_pcm

cpp 复制代码
FILE* dump_pcm = nullptr;
void fill_audio_pcm(void* udata, uint8_t* stream, int len){
    AudioOutput* is = (AudioOutput*)udata;
    int len1 = 0;
    int audio_size = 0;
    if(!dump_pcm){
        dump_pcm = fopen("dump.pcm", "wb");
    }
    while (len > 0)
    {
        if(is->audio_buf_index == is->audio_buf_size){
            is->audio_buf_index = 0;
            AVFrame* frame = is->frame_queue->Pop(10);
            if(frame){
                is->pts = frame->pts;
                if(frame->format != is->dst_tgt.fmt
                   || frame->sample_rate != is->dst_tgt.freq
                   || frame->channel_layout != is->dst_tgt.channel_layout
                   && !is->swr_ctx){

                    is->swr_ctx = swr_alloc_set_opts(NULL, 
                                                    is->dst_tgt.channel_layout,
                                                    (enum AVSampleFormat)is->dst_tgt.fmt,
                                                    is->dst_tgt.freq,
                                                    frame->channel_layout,
                                                    (enum AVSampleFormat)frame->format,
                                                    frame->sample_rate,
                                                    0, NULL);
                    if(!is->swr_ctx || swr_init(is->swr_ctx) < 0){
                        swr_free((SwrContext**)(&is->swr_ctx));
                        return;
                    }
                }
                if(is->swr_ctx){
                    const uint8_t** in = (const uint8_t**) frame->extended_data;
                    uint8_t** out = &is->audio_buf1;
                    int out_samples = frame->nb_samples * is->dst_tgt.freq / frame->sample_rate + 256;
                    int out_bytes = av_samples_get_buffer_size(NULL, is->dst_tgt.channels, out_samples, is->dst_tgt.fmt, 0);
                    if(out_bytes < 0){
                        return;
                    }
                    av_fast_malloc(&is->audio_buf1, &is->audio_buf1_size, out_bytes);
                    int len2 = swr_convert(is->swr_ctx, out, out_samples, in, frame->nb_samples);
                    if(len2 < 0){
                        return;
                    }
                    is->audio_buf = is->audio_buf1;
                    is->audio_buf_size = av_samples_get_buffer_size(NULL, is->dst_tgt.channels, len2, is->dst_tgt.fmt, 1);
                }else {
                    audio_size = av_samples_get_buffer_size(NULL, is->dst_tgt.channels, frame->nb_samples, (enum AVSampleFormat) frame->format, 1);
                    av_fast_malloc(&is->audio_buf1, &is->audio_buf1_size, audio_size);
                    is->audio_buf = is->audio_buf1;
                    is->audio_buf_size = audio_size;
                    memcpy(is->audio_buf, frame->data[0], audio_size);
                }
                av_frame_free(&frame);
            }else {
                is->audio_buf = nullptr;
                is->audio_buf_size = 512;
            }
        }
        len1 = is->audio_buf_size - is->audio_buf_index;
        if(len1 > len){
            len1 = len;
        }
        if(!is->audio_buf){
            memset(stream, 0, len1);
        }else {
            memcpy(stream, is->audio_buf + is->audio_buf_index, len1);
            fwrite((uint8_t*)is->audio_buf + is->audio_buf_index, 1, len1, dump_pcm);
            fflush(dump_pcm);
        }
        len -= len1;
        stream += len1;
        is->audio_buf_index += len1;
    }
    if(is->pts != AV_NOPTS_VALUE){
        double pts = is->pts * av_q2d(is->time_base);
        is->avsync->SetClock(pts);
    }
}

这个函数有将近 80 行代码,负责将 pcm 数据填入音频。这个函数是用在 Init 函数上的,用于给 SDL_AudioSpec 的变量的 callback 赋值。

cpp 复制代码
wanted_spec.callback = fill_audio_pcm;

接下来,我们逐条解析这个函数。

首先看前五行代码

cpp 复制代码
    AudioOutput* is = (AudioOutput*)udata;
    int len1 = 0;
    int audio_size = 0;
    if(!dump_pcm){
        dump_pcm = fopen("dump.pcm", "wb");
    }

就是一些正常的赋值操作,如果文件不存在,则打开一个名为 "dump.pcm" 的文件,设为二进制可写。

cpp 复制代码
while (len > 0)
    {
        if(is->audio_buf_index == is->audio_buf_size){
            is->audio_buf_index = 0;
            AVFrame* frame = is->frame_queue->Pop(10);

在长度 len 大于 0 的情况下,如果两个变量相等,则将 index 设置为 0,取出帧队列的头部数据,10 是 Pop 里的参数,关于条件变量的,这里不深究。

cpp 复制代码
if(frame){
   is->pts = frame->pts;
   if(frame->format != is->dst_tgt.fmt
          || frame->sample_rate != is->dst_tgt.freq
          || frame->channel_layout != is->dst_tgt.channel_layout
          && !is->swr_ctx){

然后,在帧数据存在的情况下,将 pts(显示时间戳)赋值,然后开始条件判断,满足这些条件后执行。

好了,这篇文章先讲 20 行代码,剩余的后几篇文章再讲。

欲知后事如何,请听下回分解。

相关推荐
归秋14213 小时前
多轨音频智能混音软件怎么选:从 demo 到更完整作品的 AI 后期工具思路
人工智能·音视频
小猴子爱上树15 小时前
跨马翻译:批量图片翻译与视频字幕、智能抠图一站式在线图片翻译工具
大数据·人工智能·python·音视频
每天死循环15 小时前
ComfyUI MiniMax H3文生视频(t2v)提示词教程:四段式写法+5秒768p实测(附模板和参数)
音视频·文生视频·comfyui·ai视频·minimax
企业数字化笔记17 小时前
视频像素分割怎么选?HSV 阈值、传统掩膜、语义分割和实例分割的效果与代价
opencv·音视频
Maynor99618 小时前
6张商品图→17秒带货短视频:开源12个Claude Skills,配音、字幕花字、BGM本地一条命令出片
ffmpeg·ai编程·claude·电商·agent skills
youdexiang18 小时前
AI 生成会议纪要好用吗?多款 APP 功能分析
java·人工智能·音视频
AI创界者18 小时前
【开源实战】MiniMax-H3 本地离线高自由度 ComfyUI 工作流搭建指南(含规则松绑与提示词映射)
人工智能·aigc·音视频
AIGC小尼18 小时前
8G 显存零基础本地部署 AI 漫剧全流程|ComfyUI+Wan2.2+FFmpeg 离线成片完整方案(含代码 / 指令 / 排坑)
人工智能·ffmpeg·php·comfyui·ai漫剧
hai31524754318 小时前
文本矩阵化拆解与视频矩阵化构建
线性代数·矩阵·音视频
昨日之日200619 小时前
Visual Enhancer:一键让模糊照片视频变清晰,升分辨率、插帧、转HDR,一键搞定素材修复
计算机视觉·音视频