6.基于FFMPEG+SDL2播放音频

2024-05-27 09:38
文章标签 音频 ffmpeg 播放 sdl2

本文主要是介绍6.基于FFMPEG+SDL2播放音频,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

参考资料:
1.雷博博客
2. An ffmpeg and SDL Tutorial

前面了解了FFMPEG解码MP3文件为PCM,以及将PCM通过SDL2进行播放,下面就是将这两者进行结合,使之能够边解码边播放。。。。

  • 一、综述
  • 二、代码1(基础代码)
  • 三、代码2(增加链表队列)
    • 1)链表结构
    • 2)放数据到队列中
    • 3)从队列中取数据
    • 4)回调函数
    • 5)解码数据

一、综述

总共有2份代码,第一份是参考雷博的代码,第二份是在雷博的代码基础上进行修改,增加了链表队列控制。

二、代码1(基础代码)

关于FFMPEG解码PCM以及SDL播放音频相关知识在前两篇文章中已经详细描述了,所以不再在这里赘述,参考雷博的代码,将前面两篇文章中的代码进行综合,即可正常解码并播放音频。

大致流程为:
初始化复用器和解复用器—>获取输入文件的一些信息—->查找解码器并打开—>初始化SDL—>播放音频—->读出音频数据并解码—>等待SDL读取音频—>SDL回调读取音频数据—>结束。

其中,比较重要的便是回调函数,当设备需要音频数据时候便会调用此回调函数获取音频数据,因此在这个函数中,我们需要将解码后的音频数据赋值给stream。

代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>#define __STDC_CONSTANT_MACROS#ifdef _WIN32
//Windows
extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswresample/swresample.h"
#include "SDL2/SDL.h"
};
#else
//Linux...
#ifdef __cplusplus
extern "C"
{
#endif
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswresample/swresample.h>
#include <SDL2/SDL.h>
#ifdef __cplusplus
};
#endif
#endif//#define debug_msg(fmt, args...) printf("--->[%s,%d]  " fmt "\n\n", __FUNCTION__, __LINE__, ##args)#define MAX_AUDIO_FRAME_SIZE 192000 // 1 second of 48khz 32bit audio  //48000 * (32/8)unsigned int audioLen = 0;
unsigned char *audioChunk = NULL;
unsigned char *audioPos = NULL;void fill_audio(void * udata, Uint8 * stream, int len)
{SDL_memset(stream, 0, len);if (audioLen == 0)return;len = (len>audioLen?audioLen:len);SDL_MixAudio(stream,audioPos,len,SDL_MIX_MAXVOLUME);audioPos += len;audioLen -= len;
}int test_audio_2_play()
{AVFormatContext *pFortCtx = NULL;AVCodecContext *pCodecCtx = NULL;AVCodec *pCodec = NULL;AVPacket *pPkt = NULL;AVFrame*pFrame = NULL;struct SwrContext *pSwrCtx = NULL;SDL_AudioSpec wantSpec;//FILE* outFile = fopen("output.pcm", "wb");char inFile[] = "skycity1.mp3";int ret = -1;int audioIndex = -1;int i = 0;int got_picture = -1;uint64_t out_chn_layout = AV_CH_LAYOUT_STEREO;  //通道布局 输出双声道enum AVSampleFormat out_sample_fmt=AV_SAMPLE_FMT_S16; //声音格式int out_sample_rate=44100;   //采样率int out_nb_samples = -1;   int out_channels = -1;        //通道数int out_buffer_size = -1;   //输出buffunsigned char *outBuff = NULL;uint64_t in_chn_layout = -1;  //通道布局 struct SwrContext *au_convert_ctx;av_register_all();pFortCtx = avformat_alloc_context();  if (avformat_open_input(&pFortCtx, inFile, NULL, NULL) != 0)   //open input file and read data into buf{printf("avformat_open_input error!\n");ret = -1;goto ERR_1;}if (avformat_find_stream_info(pFortCtx, NULL) < 0)   //find stream some info{printf("avformat_find_stream_info error!\n");ret = -1;goto ERR_1;}/* find audio index */for (i = 0; i < pFortCtx->nb_streams; i++){if (pFortCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO){audioIndex = i;break;}}if (-1 == audioIndex){printf("can not find audio index!\n");ret = -1;goto ERR_1;}printf("------>audioIndex is %d\n", audioIndex);pCodecCtx = pFortCtx->streams[audioIndex]->codec;pCodec = avcodec_find_decoder(pCodecCtx->codec_id);if (NULL == pCodec){printf("can not find decoder!\n");ret = -1;goto ERR_1;}if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0){printf("Could not open codec.\n");ret = -1;goto ERR_1;}if (NULL == (pPkt = (AVPacket *)av_malloc(sizeof(AVPacket)))){printf("AV malloc failure.\n");ret = -1;goto ERR_2;}//out parameterout_nb_samples = pCodecCtx->frame_size;out_channels = av_get_channel_layout_nb_channels(out_chn_layout);out_buffer_size = av_samples_get_buffer_size(NULL, out_channels, out_nb_samples,  out_sample_fmt, 1);outBuff = (unsigned char *)av_malloc(MAX_AUDIO_FRAME_SIZE*2); //双声道printf("-------->out_buffer_size is %d\n",out_buffer_size);in_chn_layout = av_get_default_channel_layout(pCodecCtx->channels);pFrame = av_frame_alloc();//SDLwantSpec.freq = out_sample_rate;wantSpec.format = AUDIO_S16SYS;wantSpec.channels = out_channels;wantSpec.silence = 0;wantSpec.samples = out_nb_samples;wantSpec.callback = fill_audio;wantSpec.userdata = pCodecCtx;if (SDL_OpenAudio(&wantSpec, NULL) < 0){printf("can not open SDL!\n");ret = -1;goto ERR_3;}//Swrau_convert_ctx=swr_alloc_set_opts(NULL,out_chn_layout,                                /*out*/out_sample_fmt,                              /*out*/out_sample_rate,                             /*out*/in_chn_layout,                                  /*in*/pCodecCtx->sample_fmt ,               /*in*/pCodecCtx->sample_rate,               /*in*/0, NULL);swr_init(au_convert_ctx);SDL_PauseAudio(0);while(av_read_frame(pFortCtx, pPkt) >= 0){if (pPkt->stream_index == audioIndex){if (avcodec_decode_audio4(pCodecCtx, pFrame, &got_picture, pPkt) < 0){printf("Error in decoding audio frame.\n");ret = -1;break;}if (got_picture > 0){swr_convert(au_convert_ctx,&outBuff, MAX_AUDIO_FRAME_SIZE,(const uint8_t **)pFrame->data , pFrame->nb_samples);//fwrite(outBuff, 1, out_buffer_size, outFile); while(audioLen > 0)SDL_Delay(1); audioChunk = (unsigned char *)outBuff;audioPos = audioChunk;audioLen = out_buffer_size;}}av_free_packet(pPkt);}SDL_CloseAudio();SDL_Quit();swr_free(&au_convert_ctx);ERR_3:av_free(outBuff);ERR_2:avcodec_close(pCodecCtx);avformat_close_input(&pFortCtx);ERR_1:avformat_free_context(pFortCtx);//fclose(outFile);return ret;
}int main(int argc, char *argv[])
{//test_audio_2_PCM();test_audio_2_play();return 0;
}

三、代码2(增加链表队列)

在这份代码中,主要是增加了链表队列,并将解码工作放在了回调函数中。
和上面的区别是:
主程序中,每读取一包的数据(av_read_frame)将其放入链表中
在回调函数中,从链表中取出每包的数据并将其解码放入stream中

1)链表结构

链表结构如下:

typedef struct PacketQueue {AVPacketList *first_pkt, *last_pkt;int nb_packets;int size;SDL_mutex *mutex;SDL_cond *cond;
} PacketQueue;

其中,first_pkt和last_pkt为两个主要的链表结点,first_pkt用于指向第一个链表结点,当我们取数据的时候,总是取出第一个结点的数据,并将第一个结点的next重新复制给first_pkt,用于下次取数据时使用,last_pkt为最后一个结点,当放数据时候,会将新的数据结点地址赋值给last_pkt的next,并将last_pkt重新指向最后一个结点,依此反复,不断将新的数据结点添加到链表的最后。
其原型为:

typedef struct AVPacketList {AVPacket pkt;struct AVPacketList *next;
} AVPacketList;

nb_packets为当前的总packet数目
size 为当前所有packet中数据的大小
mutex 为互斥锁
cond 为条件变量

因为主线程会不断向链表中放入读取到的数据以及回调函数中会不断从链表中读取数据,所以需要有互斥锁以及条件变量来进行同步操作。

2)放数据到队列中

主函数中将读取到的数据放到队列中,函数原型如下:

int packet_queue_put(PacketQueue *q, AVPacket *pkt) {AVPacketList *pkt1;if(av_dup_packet(pkt) < 0) {return -1;}pkt1 = (AVPacketList *)av_malloc(sizeof(AVPacketList));if (!pkt1)return -1;pkt1->pkt = *pkt;pkt1->next = NULL;SDL_LockMutex(q->mutex);if (!q->last_pkt)    //队列为空q->first_pkt = pkt1;elseq->last_pkt->next = pkt1;  //将当前链表的最后一个结点的next指向pkt1q->last_pkt = pkt1; //将last_pkt指向最后一个结点,即pkt1q->nb_packets++;q->size += pkt1->pkt.size;SDL_CondSignal(q->cond);SDL_UnlockMutex(q->mutex);return 0;
}

注意,在进行链表操作的时候,需要加锁解锁,防止其他地方同时进行操作。

3)从队列中取数据

从链表队列中取出结点数据,如下:

static int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block) {AVPacketList *pkt1;int ret;SDL_LockMutex(q->mutex);for(;;) {if(quit) {ret = -1;break;}pkt1 = q->first_pkt; //指向第一个结点,即取出第一个结点if (pkt1) {q->first_pkt = pkt1->next; //即q->first_pkt = q->first_pkt->next 将第一个结点指向的下一个结点设置为first_pkt,可以理解为取出当前第一个结点if (!q->first_pkt)q->last_pkt = NULL;q->nb_packets--;q->size -= pkt1->pkt.size;*pkt = pkt1->pkt;av_free(pkt1);ret = 1;break;}else if (!block) {ret = 0;break;} else {SDL_CondWait(q->cond, q->mutex);}}SDL_UnlockMutex(q->mutex);return ret;
}

4)回调函数

void audio_callback(void *userdata, Uint8 *stream, int len) {AVCodecContext *aCodecCtx = (AVCodecContext *)userdata;int len1, audio_size;static uint8_t audio_buf[(MAX_AUDIO_FRAME_SIZE * 3) / 2];static unsigned int audio_buf_size = 0;static unsigned int audio_buf_index = 0;while(len > 0) {   if(audio_buf_index >= audio_buf_size)  //表示当前audio_buf中已经没有数据,需要解码数据了{/* We have already sent all our data; get more */audio_size = audio_decode_frame(aCodecCtx, audio_buf,sizeof(audio_buf));if(audio_size < 0) {/* If error, output silence */audio_buf_size = 1024;memset(audio_buf, 0, audio_buf_size);} else {audio_buf_size = audio_size;}audio_buf_index = 0;}len1 = audio_buf_size - audio_buf_index;if(len1 > len)len1 = len;memcpy(stream, (uint8_t *)audio_buf + audio_buf_index, len1);len -= len1;stream += len1;audio_buf_index += len1;}
} 

在上面代码中有三个static 变量:
audio_buf 、audio_buf_size、 audio_buf_index
audio_buf中存放的为解码后的数据
audio_buf_size 为当前audio_buf中数据的长度
audio_buf_index为当前指向audio_buf的index

之所以设置为static,是因为当调用此回调函数时候,SDL期望的数据长度为len,但是可能我们调用audio_decode_frame返回的数据总长度大于len,所以将长度为len的数据赋值给stream,当下一次回调进来的时候,先根据audio_buf_size和audio_buf_index的大小来进行判断是否需要再解码新的数据,如果还有剩余数据,则继续将audio_buf中的剩余数据赋值给stream,此时如果剩余数据长度小于len,则循环解码新的数据进行赋值。

5)解码数据

int audio_decode_frame(AVCodecContext *aCodecCtx, uint8_t *audio_buf, int buf_size) {static AVPacket pkt;static uint8_t *audio_pkt_data = NULL;static int audio_pkt_size = 0;static AVFrame frame;int len1, data_size = 0;for(;;) {while(audio_pkt_size > 0) {int got_frame = 0;len1 = avcodec_decode_audio4(aCodecCtx, &frame, &got_frame, &pkt);if(len1 < 0) {/* if error, skip frame */audio_pkt_size = 0;break;}audio_pkt_data += len1;audio_pkt_size -= len1;data_size = 0;if(got_frame) {swr_convert(au_convert_ctx,&audio_buf, MAX_AUDIO_FRAME_SIZE,(const uint8_t **)frame.data , frame.nb_samples);data_size = out_buffer_size;/*data_size = av_samples_get_buffer_size(NULL, aCodecCtx->channels,frame.nb_samples,aCodecCtx->sample_fmt,1);assert(data_size <= buf_size);memcpy(audio_buf, frame.data[0], data_size);*/}if(data_size <= 0) {/* No data yet, get more frames */continue;}/* We have data, return it and come back for more later */return data_size;}if(pkt.data)av_free_packet(&pkt);if(quit) {return -1;}if(packet_queue_get(&audioq, &pkt, 1) < 0) {return -1;}audio_pkt_data = pkt.data;audio_pkt_size = pkt.size;}
}

在这个函数中,仍然有几个static变量,pkt/audio_pkt_data/audio_pkt_size/frame.

首先我们先了解一下解码函数avcodec_decode_audio4中的一些描述:

  • Some decoders may support multiple frames in a single AVPacket. Such
  • decoders would then just decode the first frame and the return value would be
  • less than the packet size. In this case, avcodec_decode_audio4 has to be
  • called again with an AVPacket containing the remaining data in order to
  • decode the second frame, etc… Even if no frames are returned, the packet
  • needs to be fed to the decoder with remaining data until it is completely
  • consumed or an error occurs.

上面大意是,一个AVPacket中可能会有多帧音频数据,但是avcodec_decode_audio4每次只解码一帧的数据,因此需要我们多次调用此接口来解码AVPacket中的数据。

根据以上,所以当audio_decode_frame解码一帧后就返回,当下次进来此函数时候,会继续将pkt里面的剩余数据进行解码,当将pkt里面的剩余数据解码万之后,在下次进来的时候会重新从队列里面读取数据,audio_pkt_size和audio_pkt_data则是与此过程相关。

同时,解码后的数据格式可能不是我们想要的或者SDL2无法播放,则需要调用SwrContext进行重采样,如下:

swr_convert(au_convert_ctx,&audio_buf, MAX_AUDIO_FRAME_SIZE,(const uint8_t **)frame.data , frame.nb_samples);

以上便是几个重要的函数解析,下面是完整的代码:

//采用队列的方式
//linux下编译命令如下:
//gcc test_2_pcm.c -o test -I /usr/local/include -L /usr/local/lib -lavformat -lavcodec -lavutil -lswresample//windows下可以直接套用雷博的工程#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>#define __STDC_CONSTANT_MACROS#ifdef _WIN32
//Windows
extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswresample/swresample.h"
#include "SDL2/SDL.h"
};
#else
//Linux...
#ifdef __cplusplus
extern "C"
{
#endif
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswresample/swresample.h>
#include <SDL2/SDL.h>
#ifdef __cplusplus
};
#endif
#endif//#define debug_msg(fmt, args...) printf("--->[%s,%d]  " fmt "\n\n", __FUNCTION__, __LINE__, ##args)#define MAX_AUDIO_FRAME_SIZE 192000 // 1 second of 48khz 32bit audio  //48000 * (32/8)
#define SDL_AUDIO_BUFFER_SIZE 1024struct SwrContext *au_convert_ctx;
int out_buffer_size = -1;   //输出buff长度typedef struct PacketQueue {AVPacketList *first_pkt, *last_pkt;int nb_packets;int size;SDL_mutex *mutex;SDL_cond *cond;
} PacketQueue;PacketQueue audioq;void packet_queue_init(PacketQueue *q) {memset(q, 0, sizeof(PacketQueue));q->first_pkt = NULL;q->last_pkt = NULL;q->mutex = SDL_CreateMutex();q->cond = SDL_CreateCond();
}int packet_queue_put(PacketQueue *q, AVPacket *pkt) {AVPacketList *pkt1;if(av_dup_packet(pkt) < 0) {return -1;}pkt1 = (AVPacketList *)av_malloc(sizeof(AVPacketList));if (!pkt1)return -1;pkt1->pkt = *pkt;pkt1->next = NULL;SDL_LockMutex(q->mutex);if (!q->last_pkt)    //队列为空q->first_pkt = pkt1;elseq->last_pkt->next = pkt1;  //将当前链表的最后一个结点的next指向pkt1q->last_pkt = pkt1; //将last_pkt指向最后一个结点,即pkt1q->nb_packets++;q->size += pkt1->pkt.size;SDL_CondSignal(q->cond);SDL_UnlockMutex(q->mutex);return 0;
}int quit = 0;static int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block) {AVPacketList *pkt1;int ret;SDL_LockMutex(q->mutex);for(;;) {if(quit) {ret = -1;break;}pkt1 = q->first_pkt; //指向第一个结点,即取出第一个结点if (pkt1) {q->first_pkt = pkt1->next; //即q->first_pkt = q->first_pkt->next 将第一个结点指向的下一个结点设置为first_pkt,可以理解为取出当前第一个结点if (!q->first_pkt)q->last_pkt = NULL;q->nb_packets--;q->size -= pkt1->pkt.size;*pkt = pkt1->pkt;av_free(pkt1);ret = 1;break;}else if (!block) {ret = 0;break;} else {SDL_CondWait(q->cond, q->mutex);}}SDL_UnlockMutex(q->mutex);return ret;
}int audio_decode_frame(AVCodecContext *aCodecCtx, uint8_t *audio_buf, int buf_size) {static AVPacket pkt;static uint8_t *audio_pkt_data = NULL;static int audio_pkt_size = 0;static AVFrame frame;int len1, data_size = 0;for(;;) {while(audio_pkt_size > 0) {int got_frame = 0;len1 = avcodec_decode_audio4(aCodecCtx, &frame, &got_frame, &pkt);if(len1 < 0) {/* if error, skip frame */audio_pkt_size = 0;break;}audio_pkt_data += len1;audio_pkt_size -= len1;data_size = 0;if(got_frame) {swr_convert(au_convert_ctx,&audio_buf, MAX_AUDIO_FRAME_SIZE,(const uint8_t **)frame.data , frame.nb_samples);data_size = out_buffer_size;/*data_size = av_samples_get_buffer_size(NULL, aCodecCtx->channels,frame.nb_samples,aCodecCtx->sample_fmt,1);assert(data_size <= buf_size);memcpy(audio_buf, frame.data[0], data_size);*/}if(data_size <= 0) {/* No data yet, get more frames */continue;}/* We have data, return it and come back for more later */return data_size;}if(pkt.data)av_free_packet(&pkt);if(quit) {return -1;}if(packet_queue_get(&audioq, &pkt, 1) < 0) {return -1;}audio_pkt_data = pkt.data;audio_pkt_size = pkt.size;}
}void audio_callback(void *userdata, Uint8 *stream, int len) {AVCodecContext *aCodecCtx = (AVCodecContext *)userdata;int len1, audio_size;static uint8_t audio_buf[(MAX_AUDIO_FRAME_SIZE * 3) / 2];static unsigned int audio_buf_size = 0;static unsigned int audio_buf_index = 0;while(len > 0) {   if(audio_buf_index >= audio_buf_size) //表示当前audio_buf中已经没有数据,需要解码数据了{/* We have already sent all our data; get more */audio_size = audio_decode_frame(aCodecCtx, audio_buf,sizeof(audio_buf));if(audio_size < 0) {/* If error, output silence */audio_buf_size = 1024;memset(audio_buf, 0, audio_buf_size);} else {audio_buf_size = audio_size;}audio_buf_index = 0;}len1 = audio_buf_size - audio_buf_index;if(len1 > len)len1 = len;memcpy(stream, (uint8_t *)audio_buf + audio_buf_index, len1);len -= len1;stream += len1;audio_buf_index += len1;}
} 
int test_audio_2_play()
{AVFormatContext *pFortCtx = NULL;AVCodecContext *pCodecCtx = NULL;AVCodec *pCodec = NULL;AVPacket *pPkt = NULL;AVFrame*pFrame = NULL;struct SwrContext *pSwrCtx = NULL;SDL_AudioSpec wantSpec;//FILE* outFile = fopen("output.pcm", "wb");char inFile[] = "skycity1.mp3";int ret = -1;int audioIndex = -1;int i = 0;int got_picture = -1;uint64_t out_chn_layout = AV_CH_LAYOUT_STEREO;  //通道布局 输出双声道enum AVSampleFormat out_sample_fmt=AV_SAMPLE_FMT_S16; //声音格式int out_sample_rate=44100;   //采样率int out_nb_samples = -1;   int out_channels = -1;        //通道数unsigned char *outBuff = NULL;uint64_t in_chn_layout = -1;  //通道布局 av_register_all();pFortCtx = avformat_alloc_context();  if (avformat_open_input(&pFortCtx, inFile, NULL, NULL) != 0)   //open input file and read data into buf{printf("avformat_open_input error!\n");ret = -1;goto ERR_1;}if (avformat_find_stream_info(pFortCtx, NULL) < 0)   //find stream some info{printf("avformat_find_stream_info error!\n");ret = -1;goto ERR_1;}/* find audio index */for (i = 0; i < pFortCtx->nb_streams; i++){if (pFortCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO){audioIndex = i;break;}}if (-1 == audioIndex){printf("can not find audio index!\n");ret = -1;goto ERR_1;}printf("------>audioIndex is %d\n", audioIndex);pCodecCtx = pFortCtx->streams[audioIndex]->codec;pCodec = avcodec_find_decoder(pCodecCtx->codec_id);if (NULL == pCodec){printf("can not find decoder!\n");ret = -1;goto ERR_1;}if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0){printf("Could not open codec.\n");ret = -1;goto ERR_1;}if (NULL == (pPkt = (AVPacket *)av_malloc(sizeof(AVPacket)))){printf("AV malloc failure.\n");ret = -1;goto ERR_2;}//out parameterout_nb_samples = pCodecCtx->frame_size;out_channels = av_get_channel_layout_nb_channels(out_chn_layout);out_buffer_size = av_samples_get_buffer_size(NULL, out_channels, out_nb_samples,  out_sample_fmt, 1);outBuff = (unsigned char *)av_malloc(MAX_AUDIO_FRAME_SIZE*2); //双声道printf("-------->out_buffer_size is %d\n",out_buffer_size);in_chn_layout = av_get_default_channel_layout(pCodecCtx->channels);pFrame = av_frame_alloc();//SDLwantSpec.freq = out_sample_rate;wantSpec.format = AUDIO_S16SYS;wantSpec.channels = out_channels;wantSpec.silence = 0;wantSpec.samples = out_nb_samples;wantSpec.callback = audio_callback;wantSpec.userdata = pCodecCtx;if (SDL_OpenAudio(&wantSpec, NULL) < 0){printf("can not open SDL!\n");ret = -1;goto ERR_3;}//Swrau_convert_ctx=swr_alloc_set_opts(NULL,out_chn_layout,                                /*out*/out_sample_fmt,                              /*out*/out_sample_rate,                             /*out*/in_chn_layout,                                  /*in*/pCodecCtx->sample_fmt ,               /*in*/pCodecCtx->sample_rate,               /*in*/0, NULL);swr_init(au_convert_ctx);SDL_PauseAudio(0);while(av_read_frame(pFortCtx, pPkt) >= 0){if (pPkt->stream_index == audioIndex){#if 0if (avcodec_decode_audio4(pCodecCtx, pFrame, &got_picture, pPkt) < 0){printf("Error in decoding audio frame.\n");ret = -1;break;}if (got_picture > 0){swr_convert(au_convert_ctx,&outBuff, MAX_AUDIO_FRAME_SIZE,(const uint8_t **)pFrame->data , pFrame->nb_samples);//fwrite(outBuff, 1, out_buffer_size, outFile); while(audioLen > 0)SDL_Delay(1); audioChunk = (unsigned char *)outBuff;audioPos = audioChunk;audioLen = out_buffer_size;}#elsepacket_queue_put(&audioq, pPkt);#endif}elseav_free_packet(pPkt);}sleep(10);SDL_CloseAudio();SDL_Quit();swr_free(&au_convert_ctx);ERR_3:av_free(outBuff);ERR_2:avcodec_close(pCodecCtx);avformat_close_input(&pFortCtx);ERR_1:avformat_free_context(pFortCtx);//fclose(outFile);return ret;
}int main(int argc, char *argv[])
{packet_queue_init(&audioq);//test_audio_2_PCM();test_audio_2_play();return 0;
}

最后是一个完整的工程,工程“无耻” ( ̄▽ ̄)~*的使用了雷博的工程,在visual studio 2010上跑通了,直接将代码写入工程编译运行。
基于FFMPEG+SDL2播放音频

这篇关于6.基于FFMPEG+SDL2播放音频的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1007108

相关文章

探索蓝牙协议的奥秘:用ESP32实现高质量蓝牙音频传输

蓝牙(Bluetooth)是一种短距离无线通信技术,广泛应用于各种电子设备之间的数据传输。自1994年由爱立信公司首次提出以来,蓝牙技术已经经历了多个版本的更新和改进。本文将详细介绍蓝牙协议,并通过一个具体的项目——使用ESP32实现蓝牙音频传输,来展示蓝牙协议的实际应用及其优点。 蓝牙协议概述 蓝牙协议栈 蓝牙协议栈是蓝牙技术的核心,定义了蓝牙设备之间如何进行通信。蓝牙协议

基于ZYNQ7000的交叉编译工具链Qt+OpenCV+ffmpeg等库支持总结

最近刚刚接触XILINX的ZYNQ板,刚接触没有十天。XILINX定位它为SOC,我也很认同,起码比TI定位MPU为SOC强很多。据说今年TI的最新产品也加入了ZYNQ板。 之前的MIPS处理器设计与实现的项目就算做告一段落,搞了将近7个月,成果显著,收获颇多,最近打算搞搞ZYNQ。 之前MIPS也有一套交叉编译工具,不过是老师提供的,自己也尝试搞了搞,太辛苦了,而且也没什么成果,因为我

FFmpeg源码:ff_ctz / ff_ctz_c函数分析

一、ff_ctz函数的作用 ff_ctz定义在FFmpeg源码目录的libavutil/intmath.h 下: #ifndef ff_ctz#define ff_ctz ff_ctz_c/*** Trailing zero bit count.** @param v input value. If v is 0, the result is undefined.* @return

视频批量剪辑新境界:一键转码MP4至MP3并自动删除原文件,轻松优化存储空间与播放体验

随着数字媒体的飞速发展,视频文件已成为我们生活中不可或缺的一部分。然而,大量视频文件的累积不仅占据了宝贵的存储空间,而且在某些情况下,我们更希望提取视频中的音频内容。为了满足这一需求,我们推出了全新的视频批量剪辑方案,让你轻松实现MP4到MP3的转码,并自动删除原文件,优化存储空间,提升播放体验。 首先,让我们进入“视频剪辑高手”的主页面。简洁明了的界面设计,让您一眼就能找到所需的功能板

ffmpeg将mp4转换为swf

文章目录 ffmpeg安装、配置java运行报错 Cannot run program "ffmpeg" 需要将mp4转换为swf,网上有很多软件,不是收费,就是功能不全,要不就是分辨率比例不满足要求。突然想到实在不行就自己开发个,谁让自己是程序员呢。 ffmpeg安装、配置 不是只写程序就行,需要先安装ffpmeg。 下载地址: https://www.gyan.d

《FFmpeg开发实战:从零基础到短视频上线》资源下载和内容勘误

资源下载 下面是《FFmpeg开发实战:从零基础到短视频上线》一书用到的工具和代码资源: 1、本书使用的FFmpeg版本为FFmpeg 5.1.2,也可在FFmpeg的github主页上下载最新的FFmpeg源码。 2、本书第12章使用的Android Studio版本为Android Studio Dolphin(小海豚版本)。 3、本书提供所有示例源码的demo工程下载,扫描前言末尾的二维码

音频数据集1--LJSpeech单人语音

LJ Speech Dataset 版本号: 1.1 , 文件大小: 2.6GB 1.简介 1. 1 内容简介 LJS是一个语音数据集,包含 13,100 个音频片段,内容为Linda Johnson(欧美女性)朗读的 7 本书籍段落(非小说类)。每个片段都提供文本转录,片段长度从 1 到 10 秒不等,总长度约为 24 小时。 7本书籍内容 发表于 1884 年至 1964 年

【FreeRTOS】任务状态改进播放控制

这里写目录标题 1 任务状态1.1 阻塞状态(Blocked)1.2 暂停状态(Suspended)1.3 就绪状态(Ready)1.4 完整的状态转换图 2 举个例子3 编写代码 参考《FreeRTOS入门与工程实践(基于DshanMCU-103).pdf》 本节课实现音乐任务的创建,音乐播放的暂停与继续播放,删除任务。 代码为:08_task_priority

ffmpeg调整视频角度

最近公司要做一款视频版宠物喂食器,存在一个问题,就是视频sensor是旋转90度放置的,可能是开模影响或者是方案选型问题,这里先不做讨论。         由于视频sensor不是正立放置,那么出来的视频流也是被旋转90度的,这样在app端显示的视频也需要调整。         针对实时流,是比较容易处理的。可以有两种方法进行处理:一种就是将正立着的播放器旋转90度,也就是Vi

不用写一行代码,deepseek结合腾讯云语音识别来批量转录Mp3音频

首先,打开window系统中的cmd命令行工具,或者powershell,安装腾讯云tencentcloud的Python库 pip install -i https://mirrors.tencent.com/pypi/simple/ --upgrade tencentcloud-sdk-python 然后,开通腾讯云的对象存储COS服务, 把要转录成文本的mp3音频文件上