ijkplayer源码分析 音频解码流程

前言

本文是流程分析的第四篇,分析ijkPlayer中的音频解码流程,在audio_thread中,如下流程图中所示。
在这里插入图片描述

音频帧是如何解码的、如何入队的

音频帧的解码操作是在audio_thread线程中,audio_thread从packet_queue中读取了音频packet,并软解码成音频帧,放入frame_queue中。
音频帧只有软解码,无硬解码。

static int stream_component_open(FFPlayer *ffp, int stream_index) {
    
    
    // 调用SDL_AoutOpenAudio打开音频设备
    audio_open(ffp, channel_layout, nb_channels, sample_rate, &is->audio_tgt);
    decoder_init(&is->auddec, avctx, &is->audioq, is->continue_read_thread);
    decoder_start(&is->auddec, audio_thread, ffp, "ff_audio_dec")}

static int audio_thread(void *arg) {
    
    
    int ret = 0;
    int got_frame = 0;
    AVRational tb;
    AVFrame *frame = av_frame_alloc();
    Frame *af;
    

    do {
    
    
        // 软解码获取一帧
        got_frame = decoder_decode_frame(ffp, &is->auddec, frame, NULL);

        if (!got_frame) {
    
    
            continue;
        }
        tb = (AVRational) {
    
    1, frame->sample_rate}; // 时间基

        af = frame_queue_peek_writable(&is->sampq); // 获取一个可写的结点
        af->pts = (frame->pts == AV_NOPTS_VALUE) ? NAN : frame->pts * av_q2d(tb); // 赋值pts,用于音视频同步
        af->pos = frame->pkt_pos;
        af->serial = is->auddec.pkt_serial;
        af->duration = av_q2d((AVRational) {
    
    frame->nb_samples, frame->sample_rate});

        av_frame_move_ref(af->frame, frame); // 将frame完全赋值给af->frame
        frame_queue_push(&is->sampq); // 存入队列

    } while (ret >= 0 || ret == AVERROR(EAGAIN) || ret == AVERROR_EOF);

    return ret;
}

// 解码器解码一帧
static int decoder_decode_frame(FFPlayer *ffp, Decoder *d, AVFrame *frame, AVSubtitle *sub) {
    
    
    int ret = AVERROR(EAGAIN);
    for (;;) {
    
    
        AVPacket pkt;

        // avcodec_receive_frame软解码,读取一帧解码后的数据frame
        if (d->queue->serial == d->pkt_serial) {
    
      // 流连续的情况
            do {
    
    
                ret = avcodec_receive_frame(d->avctx, frame); // 获取到解码后的AVFrame数据,(即使还没送入新的Packet,这是为了兼容一个Packet可以解出多个Frame的情况)
               
                if (ret == AVERROR_EOF) {
    
    
                    return 0;
                }
                if (ret >= 0)
                    return 1;
            } while (ret != AVERROR(EAGAIN));
        }

        do {
    
    
            if (d->queue->nb_packets == 0)
                SDL_CondSignal(d->empty_queue_cond);

            if (d->packet_pending) {
    
    
                // packet_pending,用于在send失败时重新发送,将d->pkt赋值给当前pkt
                av_packet_move_ref(&pkt, &d->pkt);
                d->packet_pending = 0;
            } else {
    
    
                // 阻塞等待直到退出或者有AVPacket数据,等待read_thread存数据进来
                if (packet_queue_get(d->queue, &pkt, &d->pkt_serial, &d->finished) < 0){
    
    
                    return -1;
                }
            }
        } while (d->queue->serial != d->pkt_serial); // 跳过不同的serial,读取相同serial的packet

        if (pkt.data == flush_pkt.data) {
    
    
            // serial变动,Reset the internal decoder state / flush internal buffers. 
            avcodec_flush_buffers(d->avctx);
            d->finished = 0;
            d->next_pts = d->start_pts;
            d->next_pts_tb = d->start_pts_tb;
        } else {
    
    
            // 将packet送入解码器进行解码
            if (avcodec_send_packet(d->avctx, &pkt) == AVERROR(EAGAIN)) {
    
    
           // eagain,需要重新发送,设置packet_pending为true
            d->packet_pending = 1;
            av_packet_move_ref(&d->pkt, &pkt);  // 把pkt赋值给d->pk

            av_packet_unref(&pkt);
        }

    }
}

音频帧如何被读取的、如何被播放的

播放流程前面一篇已经讲过了,此处只写音频队列frame_queue取数据的过程。
在stream_open函数中调用了audio_open打开了音频设备,并设置了sdl_audio_callback回调函数,播放线程aout_thread在播放时通过回调从frame_queue中获取数据,进行播放。

// ff_ffplay.c,  stream是buffer,向里填充数据,write到AudioTrack里去
static void sdl_audio_callback(void *opaque, Uint8 *stream, int len) {
    
    
    int audio_size, len1;
    while (len > 0) {
    
    
          if (is->audio_buf_index >= is->audio_buf_size) {
    
    
            // audio_buf消耗完了,调用audio_decode_frame重新填充audio_buf
            audio_size = audio_decode_frame(ffp);
            is->audio_buf_size = audio_size;
            is->audio_buf_index = 0;
        }

        len1 = is->audio_buf_size - is->audio_buf_index;
        if (len1 > len)
            len1 = len;

        // 读取数据到stream中
        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);
            if (!is->muted && is->audio_buf)
                SDL_MixAudio(stream, (uint8_t *) is->audio_buf + is->audio_buf_index, len1,
                             is->audio_volume);
        }

        // 向后移动,len>0则继续读取数据
        len -= len1;
        stream += len1;
        is->audio_buf_index += len1;
    }
}

static int audio_decode_frame(FFPlayer *ffp) {
    
    
    Frame *af;
    do {
    
    
        // 获取一个可读结点,读取新的音频帧
        if (!(af = frame_queue_peek_readable(&is->sampq))) {
    
    
            return -1;
        }
        // 移动指针指向下一个
        frame_queue_next(&is->sampq);
    } while (af->serial != is->audioq.serial);

    if () {
    
    
      // 重采样
    } else {
    
    
        // 把这一帧数据赋值给audio_buf
        is->audio_buf = af->frame->data[0];
        resampled_data_size = data_size;
    }

     // 更新audio_clock
     is->audio_clock = af->pts + (double) af->frame->nb_samples / af->frame->sample_rate;
     return resampled_data_size;
}

参考:
ffplay解码线程分析

猜你喜欢

转载自blog.csdn.net/u014099894/article/details/112970520