使用ffmpeg API连续播放m3u8流失败

问题描述 投票:0回答:1

我正在开发一个嵌入式Linux系统(5.10.24),我想在其中使用FFMPEG API播放m3u8音频流。

这是我的代码。

#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. openPCM,
    if (0 > snd_pcm_open(playback_handle, "default", SND_PCM_STREAM_PLAYBACK, 0))
    {
        printf("snd_pcm_open err\n");
        return -1;
    }
    //2. snd_pcm_hw_params_t
    if(0 > snd_pcm_hw_params_malloc (&hw_params))
    {
        printf("snd_pcm_hw_params_malloc err\n");
        return -1;
    }
    //3. hw_params
    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_any err\n");
        return -1;
    }

    //5. SND_PCM_FORMAT_U8,8
    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. set hw_params
    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; //for opening multi-media file
    int audioStream = -1;
    AVCodecContext *pCodecCtx = NULL;
    AVCodec *pCodec = NULL; // the codecer
    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 video file!");
        return -1; // Couldn't open file
    }

    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("Din't find a video stream!");
        return -1;// Didn't find a video stream
    }

    av_dump_format(pFormatCtx, audioStream, NULL, false);

    // Find the decoder for the video stream
    pCodec = avcodec_find_decoder(pFormatCtx->streams[audioStream]->codecpar->codec_id);
    if (pCodec == NULL) {
        printf("Unsupported codec!\n");
        return -1; // Codec not found
    }

    // Copy context
    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;
    }
    // Open codec
    if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
        printf("Failed to open decoder!\n");
        return -1; // Could not open codec
    }
    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 handle ***/
    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.m3
u8
[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://...
发生。

如何使其能够连续播放m3u8音频流?

c audio ffmpeg media audio-streaming
1个回答
0
投票

我找到了代码的修复程序,使其能够连续播放直播流。
需要添加以下代码

               rc = snd_pcm_writei(playback_handle, out_buffer2, outframes);
+               if (rc < 0) {
+                  snd_pcm_prepare(playback_handle);
+               }

也就是说,流媒体播放过程中出现错误,需要恢复ALSA错误。

现在可以连续播放直播了,但是我遇到了另一个问题:链接更改时会出现轻微的中断,如下,

[http @ 0x6eaba0] Opening 'http://broadcast.tx.xmcdn.com/live/2730_64_241107_000014_1e5b.aac' for reading
ALSA lib pcm.c:8675:(snd_pcm_recover) underrun occurred

[http @ 0x7665d0] Opening 'http://live.ximalaya.com/radio-first-page-app/live/2730/64.m3u8' for reading
[hls @ 0x6db020] Skip ('#EXT-X-VERSION:3')
[http @ 0x707c40] Opening 'http://broadcast.tx.xmcdn.com/live/2730_64_241107_000014_1e5c.aac' for reading
ALSA lib pcm.c:8675:(snd_pcm_recover) underrun occurred

[http @ 0x7665d0] Opening 'http://live.ximalaya.com/radio-first-page-app/live/2730/64.m3u8' for reading
[hls @ 0x6db020] Skip ('#EXT-X-VERSION:3')
[http @ 0x6eaba0] Opening 'http://broadcast.tx.xmcdn.com/live/2730_64_241107_000014_1e5d.aac' for reading
ALSA lib pcm.c:8675:(snd_pcm_recover) underrun occurred

我的缓冲区/周期设置似乎有问题,设置正确的值是多少???

© www.soinside.com 2019 - 2024. All rights reserved.