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

相关文章

SQLite3 在嵌入式C环境中存储音频/视频文件的最优方案

《SQLite3在嵌入式C环境中存储音频/视频文件的最优方案》本文探讨了SQLite3在嵌入式C环境中存储音视频文件的优化方案,推荐采用文件路径存储结合元数据管理,兼顾效率与资源限制,小文件可使用B... 目录SQLite3 在嵌入式C环境中存储音频/视频文件的专业方案一、存储策略选择1. 直接存储 vs

Python如何将OpenCV摄像头视频流通过浏览器播放

《Python如何将OpenCV摄像头视频流通过浏览器播放》:本文主要介绍Python如何将OpenCV摄像头视频流通过浏览器播放的问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完... 目录方法1:使用Flask + MJPEG流实现代码使用方法优点缺点方法2:使用WebSocket传输视

Python中对FFmpeg封装开发库FFmpy详解

《Python中对FFmpeg封装开发库FFmpy详解》:本文主要介绍Python中对FFmpeg封装开发库FFmpy,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录一、FFmpy简介与安装1.1 FFmpy概述1.2 安装方法二、FFmpy核心类与方法2.1 FF

基于Linux的ffmpeg python的关键帧抽取

《基于Linux的ffmpegpython的关键帧抽取》本文主要介绍了基于Linux的ffmpegpython的关键帧抽取,实现以按帧或时间间隔抽取关键帧,文中通过示例代码介绍的非常详细,对大家的学... 目录1.FFmpeg的环境配置1) 创建一个虚拟环境envjavascript2) ffmpeg-py

Python使用FFmpeg实现高效音频格式转换工具

《Python使用FFmpeg实现高效音频格式转换工具》在数字音频处理领域,音频格式转换是一项基础但至关重要的功能,本文主要为大家介绍了Python如何使用FFmpeg实现强大功能的图形化音频转换工具... 目录概述功能详解软件效果展示主界面布局转换过程截图完成提示开发步骤详解1. 环境准备2. 项目功能结

SpringBoot使用ffmpeg实现视频压缩

《SpringBoot使用ffmpeg实现视频压缩》FFmpeg是一个开源的跨平台多媒体处理工具集,用于录制,转换,编辑和流式传输音频和视频,本文将使用ffmpeg实现视频压缩功能,有需要的可以参考... 目录核心功能1.格式转换2.编解码3.音视频处理4.流媒体支持5.滤镜(Filter)安装配置linu

Android NDK版本迭代与FFmpeg交叉编译完全指南

《AndroidNDK版本迭代与FFmpeg交叉编译完全指南》在Android开发中,使用NDK进行原生代码开发是一项常见需求,特别是当我们需要集成FFmpeg这样的多媒体处理库时,本文将深入分析A... 目录一、android NDK版本迭代分界线二、FFmpeg交叉编译关键注意事项三、完整编译脚本示例四

用js控制视频播放进度基本示例代码

《用js控制视频播放进度基本示例代码》写前端的时候,很多的时候是需要支持要网页视频播放的功能,下面这篇文章主要给大家介绍了关于用js控制视频播放进度的相关资料,文中通过代码介绍的非常详细,需要的朋友可... 目录前言html部分:JavaScript部分:注意:总结前言在javascript中控制视频播放

Python基于wxPython和FFmpeg开发一个视频标签工具

《Python基于wxPython和FFmpeg开发一个视频标签工具》在当今数字媒体时代,视频内容的管理和标记变得越来越重要,无论是研究人员需要对实验视频进行时间点标记,还是个人用户希望对家庭视频进行... 目录引言1. 应用概述2. 技术栈分析2.1 核心库和模块2.2 wxpython作为GUI选择的优

Python+PyQt5实现多屏幕协同播放功能

《Python+PyQt5实现多屏幕协同播放功能》在现代会议展示、数字广告、展览展示等场景中,多屏幕协同播放已成为刚需,下面我们就来看看如何利用Python和PyQt5开发一套功能强大的跨屏播控系统吧... 目录一、项目概述:突破传统播放限制二、核心技术解析2.1 多屏管理机制2.2 播放引擎设计2.3 专