zoukankan      html  css  js  c++  java
  • ffplay源码分析05 ---- 音频重采样

    =====================================================

    ffplay源码分析01 ---- 框架

    ffplay源码分析02 ---- 数据读取线程

    ffplay源码分析03 ---- 视频解码线程

    ffplay源码分析03 ---- 音频解码线程

    ffplay源码分析04 ---- 音频输出

    ffplay源码分析05 ---- 音频重采样

    ffplay源码分析06 ---- 视频输出

    ffplay源码分析07 ---- 音视频同步

    =====================================================

    FFmpeg解码得到的⾳频帧的格式未必能被SDL⽀持,在这种情况下,需要进⾏⾳频重采样,即将⾳频帧
    格式转换为SDL⽀持的⾳频格式,否则是⽆法正常播放的。

    这里重采样主要在回调读取里做的

    /**
     * @brief sdl_audio_callback
     * @param opaque    指向user的数据
     * @param stream    拷贝PCM的地址
     * @param len       需要拷贝的长度
     */
    static void sdl_audio_callback(void *opaque, Uint8 *stream, int len)
    {
        VideoState *is = opaque;
        int audio_size, len1;
    
        audio_callback_time = av_gettime_relative();
    
        while (len > 0) {   // 循环读取,直到读取到足够的数据
            /* (1)如果is->audio_buf_index < is->audio_buf_size则说明上次拷贝还剩余一些数据,
             * 先拷贝到stream再调用audio_decode_frame
             * (2)如果audio_buf消耗完了,则调用audio_decode_frame重新填充audio_buf
             */
            if (is->audio_buf_index >= is->audio_buf_size) {
                audio_size = audio_decode_frame(is);
                if (audio_size < 0) {
                    /* if error, just output silence */
                    is->audio_buf = NULL;
                    is->audio_buf_size = SDL_AUDIO_MIN_BUFFER_SIZE / is->audio_tgt.frame_size
                            * is->audio_tgt.frame_size;
                } else {
                    if (is->show_mode != SHOW_MODE_VIDEO)
                        update_sample_display(is, (int16_t *)is->audio_buf, audio_size);
                    is->audio_buf_size = audio_size; // 讲字节 多少字节
                }
                is->audio_buf_index = 0;
            }
            //根据缓冲区剩余大小量力而行
            len1 = is->audio_buf_size - is->audio_buf_index;
            if (len1 > len)  // len = 3000 < len1 4096
                len1 = len;
            //根据audio_volume决定如何输出audio_buf
            /* 判断是否为静音,以及当前音量的大小,如果音量为最大则直接拷贝数据 */
            if (!is->muted && is->audio_buf && is->audio_volume == SDL_MIX_MAXVOLUME)
                memcpy(stream, (uint8_t *)is->audio_buf + is->audio_buf_index, len1);
            else {
                memset(stream, 0, len1);
                // 3.调整音量
                /* 如果处于mute状态则直接使用stream填0数据, 暂停时is->audio_buf = NULL */
                if (!is->muted && is->audio_buf)
                    SDL_MixAudioFormat(stream, (uint8_t *)is->audio_buf + is->audio_buf_index,
                                       AUDIO_S16SYS, len1, is->audio_volume);
            }
            len -= len1;
            stream += len1;
            /* 更新is->audio_buf_index,指向audio_buf中未被拷贝到stream的数据(剩余数据)的起始位置 */
            is->audio_buf_index += len1;
        }
        is->audio_write_buf_size = is->audio_buf_size - is->audio_buf_index;
        /* Let's assume the audio driver that is used by SDL has two periods. */
        if (!isnan(is->audio_clock)) {
            set_clock_at(&is->audclk, is->audio_clock -
                         (double)(2 * is->audio_hw_buf_size + is->audio_write_buf_size)
                         / is->audio_tgt.bytes_per_sec,
                         is->audio_clock_serial,
                         audio_callback_time / 1000000.0);
            sync_clock_to_slave(&is->extclk, &is->audclk);
        }
    }
    /**
     * Decode one audio frame and return its uncompressed size.
     *
     * The processed audio frame is decoded, converted if required, and
     * stored in is->audio_buf, with size in bytes given by the return
     * value.
     */
    static int audio_decode_frame(VideoState *is)
    {
        int data_size, resampled_data_size;
        int64_t dec_channel_layout;
        av_unused double audio_clock0;
        int wanted_nb_samples;
        Frame *af;
    
        if (is->paused)
            return -1;
    
        do {
    #if defined(_WIN32)
            while (frame_queue_nb_remaining(&is->sampq) == 0) {
                if ((av_gettime_relative() - audio_callback_time) > 1000000LL * is->audio_hw_buf_size / is->audio_tgt.bytes_per_sec / 2)
                    return -1;
                av_usleep (1000);
            }
    #endif
            // 若队列头部可读,则由af指向可读帧
            if (!(af = frame_queue_peek_readable(&is->sampq)))
                return -1;
            frame_queue_next(&is->sampq);
        } while (af->serial != is->audioq.serial);
    
        // 根据frame中指定的音频参数获取缓冲区的大小 af->frame->channels * af->frame->nb_samples * 2
        data_size = av_samples_get_buffer_size(NULL,
                                               af->frame->channels,
                                               af->frame->nb_samples,
                                               af->frame->format, 1);
        // 获取声道布局
        dec_channel_layout =
                (af->frame->channel_layout &&
                 af->frame->channels == av_get_channel_layout_nb_channels(af->frame->channel_layout)) ?
                    af->frame->channel_layout : av_get_default_channel_layout(af->frame->channels);
        // 获取样本数校正值:若同步时钟是音频,则不调整样本数;否则根据同步需要调整样本数
        wanted_nb_samples = synchronize_audio(is, af->frame->nb_samples);
        // is->audio_tgt是SDL可接受的音频帧数,是audio_open()中取得的参数
        // 在audio_open()函数中又有"is->audio_src = is->audio_tgt""
        // 此处表示:如果frame中的音频参数 == is->audio_src == is->audio_tgt,
        // 那音频重采样的过程就免了(因此时is->swr_ctr是NULL)
        // 否则使用frame(源)和is->audio_tgt(目标)中的音频参数来设置is->swr_ctx,
        // 并使用frame中的音频参数来赋值is->audio_src
        if (af->frame->format           != is->audio_src.fmt            || // 采样格式
                dec_channel_layout      != is->audio_src.channel_layout || // 通道布局
                af->frame->sample_rate  != is->audio_src.freq           || // 采样率
                // 第4个条件, 要改变样本数量, 那就是需要初始化重采样
                (wanted_nb_samples      != af->frame->nb_samples && !is->swr_ctx) // samples不同且swr_ctx没有初始化
                ) {
            swr_free(&is->swr_ctx);
            is->swr_ctx = swr_alloc_set_opts(NULL,
                                             is->audio_tgt.channel_layout,  // 目标输出
                                             is->audio_tgt.fmt,
                                             is->audio_tgt.freq,
                                             dec_channel_layout,            // 数据源
                                             af->frame->format,
                                             af->frame->sample_rate,
                                             0, NULL);
            if (!is->swr_ctx || swr_init(is->swr_ctx) < 0) {
                av_log(NULL, AV_LOG_ERROR,
                       "Cannot create sample rate converter for conversion of %d Hz %s %d channels to %d Hz %s %d channels!
    ",
                       af->frame->sample_rate, av_get_sample_fmt_name(af->frame->format), af->frame->channels,
                       is->audio_tgt.freq, av_get_sample_fmt_name(is->audio_tgt.fmt), is->audio_tgt.channels);
                swr_free(&is->swr_ctx);
                return -1;
            }
            is->audio_src.channel_layout = dec_channel_layout;
            is->audio_src.channels       = af->frame->channels;
            is->audio_src.freq = af->frame->sample_rate;
            is->audio_src.fmt = af->frame->format;
        }
    
        if (is->swr_ctx) {
            // 重采样输入参数1:输入音频样本数是af->frame->nb_samples
            // 重采样输入参数2:输入音频缓冲区
            const uint8_t **in = (const uint8_t **)af->frame->extended_data; // data[0] data[1]
    
            // 重采样输出参数1:输出音频缓冲区尺寸
            uint8_t **out = &is->audio_buf1; //真正分配缓存audio_buf1,指向是用audio_buf
            // 重采样输出参数2:输出音频缓冲区
            int out_count = (int64_t)wanted_nb_samples * is->audio_tgt.freq / af->frame->sample_rate
                            + 256;
    
            int out_size  = av_samples_get_buffer_size(NULL, is->audio_tgt.channels,
                                                       out_count, is->audio_tgt.fmt, 0);
            int len2;
            if (out_size < 0) {
                av_log(NULL, AV_LOG_ERROR, "av_samples_get_buffer_size() failed
    ");
                return -1;
            }
            // 如果frame中的样本数经过校正,则条件成立
            if (wanted_nb_samples != af->frame->nb_samples) {
                if (swr_set_compensation(is->swr_ctx,
                                         (wanted_nb_samples - af->frame->nb_samples) * is->audio_tgt.freq / af->frame->sample_rate,
                                         wanted_nb_samples * is->audio_tgt.freq / af->frame->sample_rate) < 0) {
                    av_log(NULL, AV_LOG_ERROR, "swr_set_compensation() failed
    ");
                    return -1;
                }
            }
            av_fast_malloc(&is->audio_buf1, &is->audio_buf1_size, out_size);
            if (!is->audio_buf1)
                return AVERROR(ENOMEM);
            // 音频重采样:返回值是重采样后得到的音频数据中单个声道的样本数
            len2 = swr_convert(is->swr_ctx, out, out_count, in, af->frame->nb_samples);
            if (len2 < 0) {
                av_log(NULL, AV_LOG_ERROR, "swr_convert() failed
    ");
                return -1;
            }
            if (len2 == out_count) {
                av_log(NULL, AV_LOG_WARNING, "audio buffer is probably too small
    ");
                if (swr_init(is->swr_ctx) < 0)
                    swr_free(&is->swr_ctx);
            }
            // 重采样返回的一帧音频数据大小(以字节为单位)
            is->audio_buf = is->audio_buf1;
            resampled_data_size = len2 * is->audio_tgt.channels * av_get_bytes_per_sample(is->audio_tgt.fmt);
        } else {
            // 未经重采样,则将指针指向frame中的音频数据
            is->audio_buf = af->frame->data[0]; // s16交错模式data[0], fltp data[0] data[1]
            resampled_data_size = data_size;
        }
    
        audio_clock0 = is->audio_clock;
        /* update the audio clock with the pts */
        if (!isnan(af->pts))
            is->audio_clock = af->pts + (double) af->frame->nb_samples / af->frame->sample_rate;
        else
            is->audio_clock = NAN;
        is->audio_clock_serial = af->serial;
    #ifdef DEBUG
        {
            static double last_clock;
            printf("audio: delay=%0.3f clock=%0.3f clock0=%0.3f
    ",
                   is->audio_clock - last_clock,
                   is->audio_clock, audio_clock0);
            last_clock = is->audio_clock;
        }
    #endif
        return resampled_data_size;
    }
  • 相关阅读:
    Addrinfo and Getaddrinfo
    网络编程原始套接字
    《福布斯》:IT人最痛苦!?
    Git安装使用笔记 [转]
    Linux下Sniffer程序的实现
    HttpWebRequest post提交XMl参数请求,
    Flex学习记录(一)——MXML基本知识
    利用System.Net.Mail 的SmtpClient发送邮件
    Flex开源开发框架
    C# 手动/自动保存图片
  • 原文地址:https://www.cnblogs.com/vczf/p/14147779.html
Copyright © 2011-2022 走看看