51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

Linux下使用ffmpeg播放mp3/aac/wav文件的音乐播放器应用

使用ffmpeg实现一个播放器?是不是没什么新意,不过一直使用ffmpeg程序,还没有用ffmpeg代码接口实现播放器,并且还需要使用linux的alsa接口播放出声音,所以做出来还是觉得有点意思;





需求:实现一个嵌入式linux上支持mp3/aac/wav文件的播放器


实现:所以考虑基于ffmpeg 实现一个嵌入式linux的播放器,这里主要应用ffmpeg的协议处理和音频解码能力,虽然网上的代码很多,不过由于版本的差异,例子程序接口存在差异,实现起来还是花了两天调试的时间;


0、几点总结

---多看官方的例子程序,官方例子路径:\ffmpeg-4.1.9\tmp\share\ffmpeg\examples

---avcodec_open2失败,怎么处理?

关键函数:avcodec_parameters_to_context 将avcodec_find_decoder找到的音频解码器复制decoder;

---av_read_frame存在内存泄漏,怎么处理?

关键函数:av_packet_unref(&input_packet);

---alsa播放设备如何枚举?

关键函数:snd_device_name_get_hint

avcodec_receive_frame接收解码完的frame只用申请一次内存;

AVFrame *pframeSRC = av_frame_alloc();


这里ffmpeg使用版本:ffmpeg-4.1.9,编译选项:

//fdk-aacm
root@lyz-VirtualBox:/home/lyz/work/broadcast_app/app/thirds_libs_src/fdk-aac/build# vim arm-gcc-cxx11.cmake
root@lyz-VirtualBox:/home/lyz/work/broadcast_app/app/thirds_libs_src/fdk-aac/build# cmake -DCMAKE_TOOLCHAIN_FILE=/home/lyz/work/broadcast_app/app/thirds_libs_src/fdk-aac/build/arm-gcc-cxx11.cmake ../
-- The C compiler identification is GNU 6.4.1
root@lyz-VirtualBox:/home/lyz/work/broadcast_app/app_linux# cat /home/lyz/work/broadcast_app/app/thirds_libs_src/fdk-aac/build/arm-gcc-cxx11.cmake
# Sample toolchain file for building with gcc compiler

Typical usage:

*) cmake -H. -B_build -DCMAKE_TOOLCHAIN_FILE="${PWD}/toolchains/gcc.cmake"

SET(CMAKE_SYSTEM_NAME Linux) set(CMAKE_SYSTEM_PROCESSOR arm)

set compiler

set(CMAKE_C_COMPILER arm-openwrt-linux-gnueabi-gcc) set(CMAKE_CXX_COMPILER arm-openwrt-linux-gnueabi-g++) set(CONFIGURE_OPTS --enable-static=yes --enable-shared=no --disable-shared)

set c++ standard

set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) //mp3 /home/lyz/work/broadcast_app/app/thirds_libs_src/lame-3.100 ./configure --host=arm-openwrt-linux-gnueabi --prefix=${PWD}/build/ ./configure --target-os=linux --prefix=/home/lyz/work/broadcast_app/app_linux/thirds_libs_src/ffmpeg-4.1.9/tmp --disable-shared --disable-muxers --enable-pic --enable-static --enable-gpl --enable-nonfree --enable-ffmpeg --disable-debug --disable-filters --disable-encoders --disable-hwaccels --enable-static --enable-libmp3lame --enable-demuxers --enable-parsers --enable-protocols --disable-x86asm --disable-stripping --extra-cflags='-I/home/lyz/work/broadcast_app/app_linux/libs/include/ -I/home/lyz/work/broadcast_app/app_linux/libs/include/lame -Os -fpic ' --extra-ldflags='-ldl -lm -L/home/lyz/work/broadcast_app/app_linux/libs/' --enable-decoder=aac --enable-swresample --enable-decoder=ac3



1、cpp文件引用ffmpeg库,出现链接错误,需要在包括头文件的地方增加两个前缀

//.cpp
#include <alsa/asoundlib.h>
#ifdef __cplusplus
extern "C" {
#endif
#include "libavutil/time.h"
#include "libavformat/avformat.h"
#include "libavcodec/avcodec.h"
#include "libavdevice/avdevice.h"
#include "libswresample/swresample.h"
#include "libswscale/swscale.h"
 
#ifdef __cplusplus
}
#endif

2、上面修改后,还是出现链接错误,与链接库的链接顺序有关系;

错误的a库顺序

LDFLAGS +=  -L ./libs/   -lavcodec -lavfilter -lavformat -lavutil -lpostproc -lswscale -lswresample  -lfdk-aac -lmp3lame

正确的链接库顺序:

LDFLAGS += -Wl,-Bstatic -L./libs -lavformat -lavcodec -lswscale -lswresample -lavutil -lavfilter -lavdevice -lpostproc -lfdk-aac -lmp3lame

注意到动态链接和静态链接:

LDFLAGS += -Wl,-Bstatic -L./libs -lavformat -lavcodec -lswscale -lswresample -lavutil -lavfilter -lavdevice -lpostproc -lfdk-aac -lmp3lame

LDFLAGS += -Wl,-Bdynamic -ldl -lm -lasound -lpthread



3、内存泄漏,用valgrind 检查会有内存泄漏,播放一会就因为内存问题挂掉了

使用valgrind可以很好的定位程序中的内存问题;

root@lyz-VirtualBox:/home/lyz/work/broadcast_app/app_linux# valgrind ./bas ./Test1.wav 0


4、使用alsa接口,完整播放出mp3文件声音的代码;

//static const char *device = "hw:1,0";			/* playback device "hw:0,0" */
static snd_pcm_format_t format = SND_PCM_FORMAT_S16;	/* sample format */
static unsigned int rate = 44100;			/* stream rate */
static unsigned int channels = 2;			/* count of channels */
static unsigned int buffer_time = 500000;		/* ring buffer length in us */
static unsigned int period_time = 100000;		/* period time in us */
static int resample = 1;				/* enable alsa-lib resampling */
static snd_pcm_sframes_t buffer_size;
static snd_pcm_sframes_t period_size;
snd_pcm_access_t mode = SND_PCM_ACCESS_RW_INTERLEAVED;
static snd_output_t *output = NULL;
/配置参数/
static int set_hwparams(snd_pcm_t *handle,snd_pcm_hw_params_t *params,snd_pcm_access_t access)
{
unsigned int rrate;
snd_pcm_uframes_t size;
int err, dir = 0;
`/*&nbsp;choose&nbsp;all&nbsp;parameters&nbsp;*/
err&nbsp;=&nbsp;snd_pcm_hw_params_any(handle,&nbsp;params);
if&nbsp;(err&nbsp;&lt;&nbsp;0)&nbsp;{
	printf("Broken&nbsp;configuration&nbsp;for&nbsp;playback:&nbsp;no&nbsp;configurations&nbsp;available:&nbsp;%s\n",&nbsp;snd_strerror(err));
	return&nbsp;err;
}
/*&nbsp;set&nbsp;hardware&nbsp;resampling&nbsp;*/
err&nbsp;=&nbsp;snd_pcm_hw_params_set_rate_resample(handle,&nbsp;params,&nbsp;resample);
if&nbsp;(err&nbsp;&lt;&nbsp;0)&nbsp;{
	printf("Resampling&nbsp;setup&nbsp;failed&nbsp;for&nbsp;playback:&nbsp;%s\n",&nbsp;snd_strerror(err));
	return&nbsp;err;
}

/&nbsp;set&nbsp;the&nbsp;interleaved&nbsp;read/write&nbsp;format&nbsp;/ /访问格式/ err&nbsp;=&nbsp;snd_pcm_hw_params_set_access(handle,&nbsp;params,&nbsp;mode); if&nbsp;(err&nbsp;&lt;&nbsp;0)&nbsp;{ printf("Access&nbsp;type&nbsp;not&nbsp;available&nbsp;for&nbsp;playback:&nbsp;%s\n",&nbsp;snd_strerror(err)); return&nbsp;err; }

/&nbsp;set&nbsp;the&nbsp;sample&nbsp;format&nbsp;/ /采样格式/ err&nbsp;=&nbsp;snd_pcm_hw_params_set_format(handle,&nbsp;params,&nbsp;format); if&nbsp;(err&nbsp;&lt;&nbsp;0)&nbsp;{ printf("Sample&nbsp;format&nbsp;not&nbsp;available&nbsp;for&nbsp;playback:&nbsp;%s\n",&nbsp;snd_strerror(err)); return&nbsp;err; }

/&nbsp;set&nbsp;the&nbsp;count&nbsp;of&nbsp;channels&nbsp;/ /音频声道/ err&nbsp;=&nbsp;snd_pcm_hw_params_set_channels(handle,&nbsp;params,&nbsp;channels); if&nbsp;(err&nbsp;&lt;&nbsp;0)&nbsp;{ printf("Channels&nbsp;count&nbsp;(%u)&nbsp;not&nbsp;available&nbsp;for&nbsp;playbacks:&nbsp;%s\n",&nbsp;channels,&nbsp;snd_strerror(err)); return&nbsp;err; }

/&nbsp;set&nbsp;the&nbsp;stream&nbsp;rate&nbsp;/ /采样率/ rrate&nbsp;=&nbsp;rate; err&nbsp;=&nbsp;snd_pcm_hw_params_set_rate_near(handle,&nbsp;params,&nbsp;&amp;rrate,&nbsp;0); if&nbsp;(err&nbsp;&lt;&nbsp;0)&nbsp;{ printf("Rate&nbsp;%uHz&nbsp;not&nbsp;available&nbsp;for&nbsp;playback:&nbsp;%s\n",&nbsp;rate,&nbsp;snd_strerror(err)); return&nbsp;err; } if&nbsp;(rrate&nbsp;!=&nbsp;rate)&nbsp;{ printf("Rate&nbsp;doesn't&nbsp;match&nbsp;(requested&nbsp;%uHz,&nbsp;get&nbsp;%iHz)\n",&nbsp;rate,&nbsp;err); return&nbsp;-EINVAL; }

/&nbsp;set&nbsp;the&nbsp;buffer&nbsp;time&nbsp;/ /底层buffer区间,以时间为单位,500000=0.5s/ err&nbsp;=&nbsp;snd_pcm_hw_params_set_buffer_time_near(handle,&nbsp;params,&nbsp;&amp;buffer_time,&nbsp;&amp;dir); if&nbsp;(err&nbsp;&lt;&nbsp;0)&nbsp;{ printf("Unable&nbsp;to&nbsp;set&nbsp;buffer&nbsp;time&nbsp;%u&nbsp;for&nbsp;playback:&nbsp;%s\n",&nbsp;buffer_time,&nbsp;snd_strerror(err)); return&nbsp;err; } err&nbsp;=&nbsp;snd_pcm_hw_params_get_buffer_size(params,&nbsp;&amp;size); if&nbsp;(err&nbsp;&lt;&nbsp;0)&nbsp;{ printf("Unable&nbsp;to&nbsp;get&nbsp;buffer&nbsp;size&nbsp;for&nbsp;playback:&nbsp;%s\n",&nbsp;snd_strerror(err)); return&nbsp;err; }

buffer_size&nbsp;=&nbsp;size; printf("buffer_size=%ld\n",buffer_size); /&nbsp;set&nbsp;the&nbsp;period&nbsp;time&nbsp;/ /底层period区间,以时间为单位,100000=0.1s/ err&nbsp;=&nbsp;snd_pcm_hw_params_set_period_time_near(handle,&nbsp;params,&nbsp;&amp;period_time,&nbsp;&amp;dir); if&nbsp;(err&nbsp;&lt;&nbsp;0)&nbsp;{ printf("Unable&nbsp;to&nbsp;set&nbsp;period&nbsp;time&nbsp;%u&nbsp;for&nbsp;playback:&nbsp;%s\n",&nbsp;period_time,&nbsp;snd_strerror(err)); return&nbsp;err; } /底层period区间,以字节为单位,441000.1=4410*/ err&nbsp;=&nbsp;snd_pcm_hw_params_get_period_size(params,&nbsp;&amp;size,&nbsp;&amp;dir); if&nbsp;(err&nbsp;&lt;&nbsp;0)&nbsp;{ printf("Unable&nbsp;to&nbsp;get&nbsp;period&nbsp;size&nbsp;for&nbsp;playback:&nbsp;%s\n",&nbsp;snd_strerror(err)); return&nbsp;err; } period_size&nbsp;=&nbsp;size; printf("period_size=%ld\n",period_size); /&nbsp;write&nbsp;the&nbsp;parameters&nbsp;to&nbsp;device&nbsp;/ err&nbsp;=&nbsp;snd_pcm_hw_params(handle,&nbsp;params); if&nbsp;(err&nbsp;&lt;&nbsp;0)&nbsp;{ printf("Unable&nbsp;to&nbsp;set&nbsp;hw&nbsp;params&nbsp;for&nbsp;playback:&nbsp;%s\n",&nbsp;snd_strerror(err)); return&nbsp;err; } return&nbsp;0; } /** Initialize one data packet for reading or writing. @param packet Packet to be initialized / static void init_packet(AVPacket *packet) { av_init_packet(packet); /* Set the packet data and size so that it is recognized as being empty./ packet-&gt;data = NULL; packet-&gt;size = 0; } int test_play_mp3(int argc, char *argv[]) { int rc; int size; int got_picture; int nb_data; bool pkt_pending = false; int audio_stream_idx; char **hints, **n; char *alsa_device_name;if&nbsp;(argc&nbsp;&lt;&nbsp;2){ printf("please&nbsp;input&nbsp;filename!\r\n"); return&nbsp;1; } printf(&quot;test_play_file filename :%s\r\n&quot;, argv[1]);snd_pcm_t&nbsp;*handle; snd_pcm_hw_params_t&nbsp;*hwparams;

snd_pcm_hw_params_alloca(&amp;hwparams);

printf("Stream&nbsp;parameters&nbsp;are&nbsp;%uHz,&nbsp;%s,&nbsp;%u&nbsp;channels\n",&nbsp;rate,&nbsp;snd_pcm_format_name(format),&nbsp;channels); ` int err; /Enumerate sound devices/ err = snd_device_name_hint(-1, "pcm", (void***)&hints); if (err != 0){ printf("please snd_device_name_hint:%d\r\n", err); return err; } #if 1 snd_lib_error_set_handler(alsa_error_handler); #else /Set a null error handler prior to enumeration to suppress errors/ snd_lib_error_set_handler(null_alsa_error_handler); #endif n = hints; while (n != NULL) { char name = snd_device_name_get_hint(n, "NAME"); if (name != NULL) { if (0 != strcmp("null", name)){ snd_pcm_t pcm; int pb_result = snd_pcm_open (&amp;pcm, name, SND_PCM_STREAM_PLAYBACK, 0); if (pb_result &gt;= 0) { printf("Try to open the device for playback - success\r\n"); snd_pcm_close (pcm); pcm = NULL; alsa_device_name = name; break; } printf("found device:%s\r\n", alsa_device_name); //break; } } n++; } printf("Playback device is %s\n", alsa_device_name); / Install error handler after enumeration, otherwise we'll get many error messages about invalid card/device ID. / snd_lib_error_set_handler(alsa_error_handler); err = snd_device_name_free_hint((void*)hints);

err = snd_output_stdio_attach(&amp;output, stdout, 0);

if (err < 0) { printf("Output failed: %s\n", snd_strerror(err)); return 0; } /设置播放模式/ err = snd_pcm_open(&handle, alsa_device_name, SND_PCM_STREAM_PLAYBACK, 0); if (err < 0) { printf("Playback open error: %s\n", snd_strerror(err)); return 0; } /设置参数/ err = set_hwparams(handle, hwparams, mode); if (err < 0) { printf("Setting of hwparams failed: %s\n", snd_strerror(err)); return 0; }

//period_size大概是采样点数/帧------4410点/帧 //s16位代表两个字节,再加上双声道 //size公式=period_sizechannels16/8 size = (period_size channels snd_pcm_format_physical_width(format)) / 8; /2 bytes/sample, 1 channels/ printf("size:%d\n",size); char&amp;nbsp;*buffer; buffer&amp;nbsp;=&amp;nbsp;(char&amp;nbsp;*)&amp;nbsp;malloc(size); memset(buffer,0,size); char&amp;nbsp;*in_name=argv[1];//&quot;鄧紫棋&amp;nbsp;-&amp;nbsp;睡公主.wav&quot;; int ret; AVFormatContext* infmt_ctx = NULL; //创建输入封装器 ret=avformat_open_input(&infmt_ctx, in_name, NULL, NULL); if (ret != 0) { printf("failed alloc output context\n"); return -1; } infmt_ctx->max_analyze_duration = 5*AV_TIME_BASE; //读取一部分视音频流并且获得一些相关的信息 ret=avformat_find_stream_info(infmt_ctx, NULL); if (ret < 0) { printf("Can't get stream info\n"); avformat_close_input(&infmt_ctx); return -1; } audio_stream_idx = av_find_best_stream(infmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0); if (audio_stream_idx < 0) { printf( "Can't find video stream in input file\n"); avformat_close_input(&infmt_ctx); return -1; } AVCodecParameters pCodecParameters = infmt_ctx-&gt;streams[audio_stream_idx]-&gt;codecpar; if (pCodecParameters == NULL){ printf("pCodecParameters is NULL\n"); avformat_close_input(&amp;infmt_ctx); return -1; } //找到解码器 const AVCodec decodec = avcodec_find_decoder(pCodecParameters->codec_id); if (!decodec) { printf("not find decoder codec audio_stream_idx:%d codec_id:%d\n", audio_stream_idx, pCodecParameters->codec_id); avformat_close_input(&infmt_ctx); return -1; } AVCodecContext *decodec_ctx = avcodec_alloc_context3(decodec); if (!decodec_ctx) { printf("Can't allocate decoder context\n"); avformat_close_input(&infmt_ctx); return AVERROR(ENOMEM); } if(avcodec_parameters_to_context(decodec_ctx, pCodecParameters)<0){ printf("Cannot alloc codec context.\n"); avformat_close_input(&infmt_ctx); return -1; } decodec_ctx->pkt_timebase = infmt_ctx->streams[audio_stream_idx]->time_base; #if 0 decodec_ctx->sample_rate = pCodecParameters->sample_rate; decodec_ctx->sample_fmt = (AVSampleFormat)pCodecParameters->format ; decodec_ctx->channels = pCodecParameters->channels; decodec_ctx->channel_layout = pCodecParameters->channel_layout; #endif// //打开解码器 ret&amp;nbsp;=&amp;nbsp;avcodec_open2(decodec_ctx,&amp;nbsp;decodec,&amp;nbsp;NULL); if (ret < 0) { printf("Could not open codec: %d\n", ret); avformat_close_input(&infmt_ctx); return -1; } //查看输入封装内容 av_dump_format(infmt_ctx,&amp;nbsp;0,&amp;nbsp;in_name,0);

#if 1 AVFrame *pframePCM = av_frame_alloc(); pframePCM-&amp;gt;format&amp;nbsp;=&amp;nbsp;AV_SAMPLE_FMT_S16; pframePCM->channel_layout = AV_CH_LAYOUT_STEREO; pframePCM->sample_rate = rate; pframePCM->nb_samples = period_size; pframePCM->channels = channels; av_frame_get_buffer(pframePCM, 0); #else uint8_t *converted_input_samples = NULL; int converted_input_samples_size = av_samples_alloc(&converted_input_samples, NULL, channels , period_size, AV_SAMPLE_FMT_S16, 0); #endif struct&amp;nbsp;SwrContext&amp;nbsp;*pcm_convert_ctx&amp;nbsp;&amp;nbsp;=&amp;nbsp;swr_alloc(); if (!pcm_convert_ctx) { printf("Could not allocate resampler context\n"); free(buffer); return -1; }

swr_alloc_set_opts(pcm_convert_ctx, AV_CH_LAYOUT_STEREO, AV_SAMPLE_FMT_S16, pframePCM->sample_rate, av_get_default_channel_layout(decodec_ctx->channels), decodec_ctx->sample_fmt, decodec_ctx->sample_rate, 0, NULL);

ret = swr_init(pcm_convert_ctx); if (ret<0) { printf("Failed to initialize the resampling context\n"); free(buffer); return -1; }

AVPacket input_packet=av_packet_alloc();

init_packet(input_packet); AVFrame pframeSRC = av_frame_alloc(); #if 0 pframeSRC-&gt;format = (AVSampleFormat)pCodecParameters-&gt;format ; pframeSRC-&gt;channel_layout = decodec_ctx-&gt;channel_layout; pframeSRC-&gt;sample_rate = decodec_ctx-&gt;sample_rate; pframeSRC-&gt;nb_samples = (20decodec_ctx-&gt;sample_rate channels * 2) / 8000;; pframeSRC->channels = channels; av_frame_get_buffer(pframeSRC, 0); #endif int&amp;nbsp;finished&amp;nbsp;=&amp;nbsp;0; int&amp;nbsp;decode_ret&amp;nbsp;=&amp;nbsp;0; int&amp;nbsp;data_size&amp;nbsp;=&amp;nbsp;av_get_bytes_per_sample(decodec_ctx-&amp;gt;sample_fmt); printf("data_size:%d, frame_size:%d, dst_samples:%d\n", data_size, pCodecParameters->frame_size, pframePCM->nb_samples); while&amp;nbsp;(!finished)&amp;nbsp; {&amp;nbsp; ret=av_read_frame(infmt_ctx,&amp;nbsp;input_packet); if&amp;nbsp;(ret&amp;nbsp;!=&amp;nbsp;0) {
if (ret == AVERROR_EOF){ finished = 1; break; } printf("fail to read_frame\n"); break; } //avcodec_send_packet/avcodec_receive_frame

        //针对多音轨问题处理
                if(input_packet-&gt;stream_index != audio_stream_idx){
                	av_packet_unref(input_packet);
                	continue;
                }

//解码获取初始音频 ret = avcodec_send_packet(decodec_ctx, input_packet); if (ret == AVERROR(EAGAIN)) { pkt_pending = true; continue; }if (ret < 0){ break; } ` int&nbsp;no_resample&nbsp;=&nbsp;0;

do{&amp;nbsp;

decode_ret = avcodec_receive_frame(decodec_ctx, pframeSRC); if (decode_ret == AVERROR_EOF) {//取完数据帧复位解码器 //avcodec_flush_buffers(decodec_ctx); printf(&quot;avcodec_receive_frame eof\n&quot;); //av_packet_unref(&amp;input_packet); break; } else if (decode_ret &lt; 0){ break; } int source_samples = swr_get_out_samples(pcm_convert_ctx, pframeSRC-&gt;nb_samples); int out_samples = source_samples;// uint8_t *write_2_pcm = NULL; if (out_samples != pframePCM-&gt;nb_samples){ no_resample = 1; //读取到一帧音频或者视频 //MP3-&gt;PCM, ret=swr_convert(pcm_convert_ctx, pframePCM-&gt;data, pframePCM-&gt;nb_samples,(const uint8_t **)pframeSRC-&gt;extended_data, pframeSRC-&gt;nb_samples); if (ret &lt;= 0) { printf(&quot;[0]out_samples:%d, pframeSRC-&gt;nb_samples:%d,ret:%d\n&quot;, source_samples, pframeSRC-&gt;nb_samples, ret); continue; }else{ //printf(&quot;[2]out_samples:%d, pframeSRC-&gt;nb_samples:%d,ret:%d\n&quot;, source_samples, pframeSRC-&gt;nb_samples, ret); } write_2_pcm = pframePCM-&gt;data[0]; nb_data = ret; }else{ printf(&quot;out_samples:%d, pframeSRC-&gt;nb_samples:%d \n&quot;, out_samples, pframeSRC-&gt;nb_samples ); nb_data = out_samples; write_2_pcm&nbsp;=&nbsp;pframeSRC-&gt;data[0];&nbsp; }&nbsp; //向硬件写入音频数据 rc&nbsp;=&nbsp;snd_pcm_writei(handle,&nbsp;write_2_pcm,&nbsp;out_samples); if&nbsp;(rc&nbsp;==&nbsp;-EPIPE)&nbsp;{ printf("underrun&nbsp;occurred\n"); err=snd_pcm_prepare(handle); if(err&lt;0) { printf("can&nbsp;not&nbsp;recover&nbsp;from&nbsp;underrun:&nbsp;%s\n",snd_strerror(err)); }

	}&amp;nbsp;
	else&amp;nbsp;if&amp;nbsp;(rc&amp;nbsp;&amp;lt;&amp;nbsp;0)&amp;nbsp;{
		fprintf(stderr,&quot;error&amp;nbsp;from&amp;nbsp;writei:&amp;nbsp;%s\n&quot;,snd_strerror(rc));
	}&amp;nbsp;&amp;nbsp;
	else&amp;nbsp;if&amp;nbsp;(rc&amp;nbsp;!=&amp;nbsp;(int)nb_data)&amp;nbsp;{
		fprintf(stderr,&quot;short&amp;nbsp;write,&amp;nbsp;write&amp;nbsp;%d&amp;nbsp;frames\n&quot;,&amp;nbsp;rc);
	}
			
}while(no_resample&amp;nbsp;&amp;amp;&amp;amp;&amp;nbsp;decode_ret&amp;nbsp;&amp;gt;&amp;nbsp;0);&amp;nbsp;&amp;nbsp;

`

    av_packet_unref(input_packet);

} if&amp;nbsp;(pcm_convert_ctx)&amp;nbsp;{ swr_free(&amp;amp;pcm_convert_ctx); } av_packet_free(&input_packet); if (pframeSRC) { av_frame_free(&pframeSRC); } #if 1 if (pframePCM) { av_frame_free(&pframePCM); } #endif if(decodec_ctx != NULL){ avcodec_close(decodec_ctx); avcodec_free_context(&decodec_ctx); } if (infmt_ctx != NULL) { avformat_close_input(&infmt_ctx); avformat_free_context(infmt_ctx); } `snd_pcm_drain(handle); snd_pcm_close(handle); //free(converted_input_samples); free(buffer); free(alsa_device_name);

return&nbsp;0; ` }


参考:https://blog.csdn.net/pk296256948/article/details/113695358


下一步:实现对rtsp流的请求;

--

2022/11/28更新:实现rtsp播放器,只需要将播放路径直接给一个rtsp的地址就可以了,是不是很简单!


呱牛笔记





赞(1)
未经允许不得转载:工具盒子 » Linux下使用ffmpeg播放mp3/aac/wav文件的音乐播放器应用