ffmpeg - 使用 FFmpeg 和 SDL_QueueAudio 播放视频中的声音会产生高音调的音频

标签 ffmpeg sdl sdl-2

我正在尝试使用 SDL2 和 FFmpeg 播放 mp4 文件中的音频,并使用 SDL_QueueAudio似乎比设置回调容易得多。

我找到的所有解决方案,无论是在这里还是在 dranger tutorials , 已弃用或使用回调。我尝试使用 ffmpeg 和 sdl 标签(没有很多)浏览所有问题,但无济于事。我尝试将 dranger 教程转换为使用未弃用的调用,但遇到了同样的问题。我正在使用 C、FFmpeg 4.1 和 SDL 2.0.9。

这是 AVCodecContext 和 AVCodec 的设置:

    int audioStream = -1;
    for (i = 0; i < formatContext->nb_streams; i++) {
        if (audioStream < 0 && formatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
            audioStream = i;
        }
    }

    AVCodecParameters *audioParams = formatContext->streams[audioStream]->codecpar;

    AVCodec *audioCodec = avcodec_find_decoder(audioParams->codec_id);

    AVCodecContext *audioCodecCtx = avcodec_alloc_context3(NULL);
    avcodec_open2(audioCodecCtx, audioCodec, NULL);

    SDL_Init(SDL_INIT_AUDIO)

    SDL_AudioSpec desired, obtained;
    SDL_zero(desired);
    SDL_zero(obtained);
    desired.freq = audioCodecCtx->sample_rate;
    desired.format = AUDIO_F32SYS;
    desired.channels = audioCodecCtx->channels;
    desired.silence = 0;
    desired.samples = AUDIO_BUFFER_SIZE;

    SDL_AudioDeviceID audioDevice = SDL_OpenAudioDevice(NULL, 0, &desired, &obtained, SDL_AUDIO_ALLOW_ANY_CHANGE);

这是主要的数据包解码循环:

    while (av_read_frame(formatContext, &packet) >= 0) {
        if (packet.stream_index == audioStream) {
            if (!avcodec_send_packet(audioCodecCtx, &packet)) {
                avcodec_receive_frame(audioCodecCtx, audioFrame);
                SDL_QueueAudio(audioDevice, audioFrame->data[0], audioFrame->linesize[0]);
            }
        }
    }

音频以正确的速度播放,但音调比实际高得多。我希望它听起来像在任何媒体播放器中一样。
编辑:我刚刚意识到测试视频有立体声音频,但我只是在排队 audioFrame.data[0] ,我认为这意味着我只播放一个 channel 。我试过排队audioFrame.data[1]它也有数据,但它没有解决问题。我是正确的,如果是,我如何播放两个 channel ?

最佳答案

回答这个问题可能为时已晚,但我遇到了同样的问题,现在我找到了适合我的解决方案,所以我发布了这个。
这里的问题可能是,FFmpeg 解码的音频格式是 AV_SAMPLE_FMT_FLTP (float planer) 格式,其中 channel 单独存储,如 frame->data[0]frame->data[1] .
我们需要使用 swr_convert() 将其转换为将这些 channel 打包成一个数组的格式
这是我的解决方案。

  • SwrContext 设置

  • SwrContext *resampler = swr_alloc_set_opts(NULL, 
                                               audioCodecCtx->channel_layout,
                                               AV_SAMPLE_FMT_S16,
                                               44100,
                                               audioCodecCtx->channel_layout,
                                               audioCodecCtx->sample_fmt,
                                               audioCodecCtx->sample_rate,
                                               0, 
                                               NULL);
    swr_init(resampler);
    
  • SDL 音频设置

  • SDL_AudioDeviceID dev;
    SDL_AudioSpec want, have;
    SDL_zero(want);
    SDL_zero(have);
    want.freq = 44100;
    want.channels = audioCodecCtx->channels;
    want.format = AUDIO_S16SYS;
    dev = SDL_OpenAudioDevice(NULL, 0, &want, &have, 0);
    SDL_PauseAudioDevice(dev, 0);
    
    最后是解码循环
    int ret = 0;
    AVPacket *packet = av_packet_alloc();
    AVFrame *frame = av_frame_alloc();
    AVFrame *audioframe = av_frame_alloc();
    while (true){
        ret = av_read_frame(formatContext, packet);
        if (ret < 0) break;
        AVStream *stream = formatContext->streams[packet->stream_index];
        if (stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO){
            ret = avcodec_send_packet(audioCodecCtx, packet);
            while (ret >= 0){
                ret = avcodec_receive_frame(audioCodecCtx, frame);
                if (ret >= 0){
                    int dst_samples = frame->channels * av_rescale_rnd(
                                       swr_get_delay(resampler, frame->sample_rate)
                                       + frame->nb_samples,
                                       44100,
                                       frame->sample_rate,               
                                       AV_ROUND_UP);
                    uint8_t *audiobuf = NULL;
                    ret = av_samples_alloc(&audiobuf, 
                                           NULL, 
                                           1, 
                                           dst_samples,
                                           AV_SAMPLE_FMT_S16, 
                                           1);
                    dst_samples = frame->channels * swr_convert(
                                                     resampler, 
                                                     &audiobuf, 
                                                     dst_samples,
                                                     (const uint8_t**) frame->data, 
                                                     frame->nb_samples);
                    ret = av_samples_fill_arrays(audioframe->data, 
                                                 audioframe->linesize, 
                                                 audiobuf,
                                                 1, 
                                                 dst_samples, 
                                                 AV_SAMPLE_FMT_S16, 
                                                 1);
                    SDL_QueueAudio(dev, 
                                   audioframe->data[0], 
                                   audioframe->linesize[0]); 
                }
            }
        }
    }
    

    关于ffmpeg - 使用 FFmpeg 和 SDL_QueueAudio 播放视频中的声音会产生高音调的音频,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55438697/

    相关文章:

    video - ffmpeg 录制延时摄影机 v4l2

    c++ - SDL libsdl1.2-dev

    c++ - 如何构建用于分发的 SDL C++ 程序?

    node.js - 无法安装 ffmpeg 模块

    encoding - 如何在使用 ffmpeg 进行实时 rtmp 发布期间自适应地将编码比特率更改为带宽?

    ffmpeg - 从 MP4 文件创建 TS 文件 - 不同的持续时间

    c++ - Mac 和 Linux 上的 SDL IMG_Load() 为同一图像返回不同的 BitsPerPixel

    gcc 编译 SDL 的正确命令行参数

    opengl - SDL 2.0/OpenGL 代码无法编译

    c++ - 在 iOS 项目中使用 C++