You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用FFmpeg API无法连续播放M3U8音频流的问题求助

使用FFmpeg API无法连续播放M3U8音频流的问题求助

我现在在做一个基于Linux 5.10.24的嵌入式系统项目,想用FFmpeg API来播放M3U8格式的音频流。目前写了结合ALSA的播放代码,一开始能正常出声,但当FFmpeg开始加载下一个分片(终端输出Opening http://...这类日志)的时候,音频就断了,没法连续播放下去,希望大家能帮忙排查下问题。

环境与实现思路

  • 系统:嵌入式Linux 5.10.24
  • 方案:用FFmpeg完成M3U8流的解封装、音频解码,通过SwResample做格式转换,最后调用ALSA API输出到音频设备

完整代码实现

#include <stdio.h>
#include <stdbool.h>
#include <alsa/asoundlib.h>

#include <libswresample/swresample.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>

int init_pcm_play(snd_pcm_t **playback_handle,snd_pcm_uframes_t chunk_size,unsigned int rate,int bits_per_sample,int channels)
{
    snd_pcm_hw_params_t *hw_params;
    snd_pcm_format_t format;

    //1. 打开PCM设备
    if (0 > snd_pcm_open(playback_handle, "default", SND_PCM_STREAM_PLAYBACK, 0))
    {
        printf("snd_pcm_open err\n");
        return -1;
    }
    //2. 分配硬件参数结构体
    if(0 > snd_pcm_hw_params_malloc (&hw_params))
    {
        printf("snd_pcm_hw_params_malloc err\n");
        return -1;
    }
    //3. 初始化硬件参数为默认配置
    if(0 > snd_pcm_hw_params_any (*playback_handle, hw_params))
    {
        printf("snd_pcm_hw_params_any err\n");
        return -1;
    }
    //4. 设置访问模式为交错模式
    if (0 > snd_pcm_hw_params_set_access (*playback_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED))
    {
        printf("snd_pcm_hw_params_set_access err\n");
        return -1;
    }

    //5. 根据采样位深设置PCM格式
    if(8 == bits_per_sample) {
        format = SND_PCM_FORMAT_U8;
    }
    if(16 == bits_per_sample) {
        format = SND_PCM_FORMAT_S16_LE;
    }
    if (0 > snd_pcm_hw_params_set_format (*playback_handle, hw_params, format))
    {
        printf("snd_pcm_hw_params_set_format err\n");
        return -1;
    }

    //6. 设置采样率
    if (0 > snd_pcm_hw_params_set_rate_near (*playback_handle, hw_params, &rate, 0))
    {
        printf("snd_pcm_hw_params_set_rate_near err\n");
        return -1;
    }
    //7. 设置声道数
    if (0 > snd_pcm_hw_params_set_channels(*playback_handle, hw_params, 2))
    {
        printf("snd_pcm_hw_params_set_channels err\n");
        return -1;
    }

    //8. 应用硬件参数
    if (0 > snd_pcm_hw_params (*playback_handle, hw_params))
    {
        printf("snd_pcm_hw_params err\n");
        return -1;
    }

    snd_pcm_hw_params_get_period_size(hw_params, &chunk_size, 0);

    snd_pcm_hw_params_free (hw_params);

    return 0;
}

int main(int argc, char *argv[])
{
    AVFormatContext *pFormatCtx = NULL; // 多媒体流上下文
    int audioStream = -1;
    AVCodecContext *pCodecCtx = NULL;
    AVCodec *pCodec = NULL; // 音频解码器
    AVFrame *pFrame = NULL;
    AVPacket *packet;
    uint8_t *out_buffer;
    struct SwrContext *au_convert_ctx;
    snd_pcm_t *playback_handle;
    int bits_per_sample = 0;

    if (avformat_open_input(&pFormatCtx, argv[1], NULL, NULL) != 0) {
        printf("Failed to open stream!");
        return -1; // 无法打开M3U8流
    }

    if(avformat_find_stream_info(pFormatCtx,NULL)<0)
    {
        printf("Failed to find stream info.\n");
        return -1;
    }

    audioStream = av_find_best_stream(pFormatCtx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
    if (audioStream == -1) {
        printf("Didn't find an audio stream!");
        return -1;// 未找到音频流
    }

    av_dump_format(pFormatCtx, audioStream, NULL, false);

    // 查找对应音频解码器
    pCodec = avcodec_find_decoder(pFormatCtx->streams[audioStream]->codecpar->codec_id);
    if (pCodec == NULL) {
        printf("Unsupported codec!\n");
        return -1; // 找不到匹配的解码器
    }

    // 分配并初始化解码器上下文
    pCodecCtx = avcodec_alloc_context3(pCodec);
    AVCodecParameters *pCodecParam = pFormatCtx->streams[audioStream]->codecpar;

     if (avcodec_parameters_to_context(pCodecCtx, pCodecParam) < 0) {
        printf("Failed to set codec params\n");
        return -1;
    }
    // 打开解码器
    if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
        printf("Failed to open decoder!\n");
        return -1; // 无法打开解码器
    }
    packet = av_packet_alloc();
    pFrame = av_frame_alloc();

    uint64_t iInputLayout                    = av_get_default_channel_layout(pCodecCtx->channels);
    enum AVSampleFormat eInputSampleFormat   = pCodecCtx->sample_fmt;
    int         iInputSampleRate             = pCodecCtx->sample_rate;


    uint64_t iOutputLayout                   = av_get_default_channel_layout(pCodecCtx->channels);
    int      iOutputChans                    = pCodecCtx->channels;
    enum AVSampleFormat eOutputSampleFormat  = AV_SAMPLE_FMT_S16;
    int         iOutputSampleRate            = pCodecCtx->sample_rate;

    au_convert_ctx = swr_alloc_set_opts(NULL,iOutputLayout, eOutputSampleFormat, iOutputSampleRate,
        iInputLayout,eInputSampleFormat, iInputSampleRate, 0, NULL);
    swr_init(au_convert_ctx);
    int iConvertLineSize = 0;
    int iConvertBuffSize  = av_samples_get_buffer_size(&iConvertLineSize, iOutputChans, pCodecCtx->frame_size, eOutputSampleFormat, 0);
    printf("ochans: %d, ifrmsmp: %d, osfmt: %d, cbufsz: %d\n", iOutputChans, pCodecCtx->frame_size, eOutputSampleFormat, iConvertBuffSize);
    out_buffer = (uint8_t *) av_malloc(iConvertBuffSize);

    if(eOutputSampleFormat == AV_SAMPLE_FMT_S16 )
    {
        bits_per_sample = 16;
    }
    /*** 初始化ALSA播放句柄 ***/
    init_pcm_play(&playback_handle,256, iOutputSampleRate,bits_per_sample,2);

    if (0 > snd_pcm_prepare (playback_handle))
    {
        printf("snd_pcm_prepare err\n");
        return -1;
    }

    while (av_read_frame(pFormatCtx, packet) >= 0) {
        if (packet->stream_index == audioStream) {
            avcodec_send_packet(pCodecCtx, packet);
            while (avcodec_receive_frame(pCodecCtx, pFrame) == 0) {
                int outframes = swr_convert(au_convert_ctx, &out_buffer, pCodecCtx->frame_size, (const uint8_t **) pFrame->data, pFrame->nb_samples); // 音频格式转换
                snd_pcm_writei(playback_handle, out_buffer, outframes);
                av_frame_unref(pFrame);
            }
        }
        av_packet_unref(packet);
    }
    swr_free(&au_convert_ctx);
    snd_pcm_close(playback_handle);
    av_freep(&out_buffer);

    return 0;
}

运行时输出日志

./ffmpeg_test http://live.ximalaya.com/radio-first-page-app/live/2730/64.m3u8
[hls @ 0x21a8020] Skip ('#EXT-X-VERSION:3')
[hls @ 0x21a8020] Opening 'http://broadcast.tx.xmcdn.com/live/2730_64_241104_000015_2186.aac' for reading
[hls @ 0x21a8020] Opening 'http://broadcast.tx.xmcdn.com/live/2730_64_241104_000015_2187.aac' for reading
Input #0, hls, from '(null)':
  Duration: N/A, bitrate: N/A
  Program 0
    Metadata:
      variant_bitrate : 0
  Stream #0:0: Audio: aac (HE-AAC), 44100 Hz, stereo, fltp
    Metadata:
      variant_bitrate : 0
[http @ 0x21b7ba0] Opening 'http://broadcast.tx.xmcdn.com/live/2730_64_241104_000015_2188.aac' for reading
[hls @ 0x21a8020] Skip ('#EXT-X-VERSION:3')
[http @ 0x21d4c20] Opening 'http://broadcast.tx.xmcdn.com/live/2730_64_241104_000015_2189.aac' for reading

具体问题

程序启动后前几秒能正常播放音频,但当终端输出新的Opening http://...日志(也就是FFmpeg开始加载M3U8的下一个分片)时,音频突然中断,无法继续播放后续内容。想请教各位,怎么修改代码才能让M3U8音频流连续播放,不会在分片切换时中断?


备注:内容来源于stack exchange,提问作者wangt13

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.15 08:53:01