我正在开发一个嵌入式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音频流?
我找到了代码的修复程序,使其能够连续播放直播流。
需要添加以下代码
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
我的缓冲区/周期设置似乎有问题,设置正确的值是多少???