ffmpeg编程(三)把视频的声音播放出来
来源:互联网 发布:阿里云邮箱服务器地址 编辑:程序博客网 时间:2024/05/03 22:45
这篇主要讲把视频的声音播放出来
audioStream = -1; for (i = 0; i < pFormatCtx->nb_streams; i++) { if (pFormatCtx->streams[i]->codec->codec_type == CODEC_TYPE_AUDIO && audioStream < 0) { audioStream = i; } } if (audioStream == -1) return -1;
上面这段代码主要是找到第一个音频流。
aCodecCtx=pFormatCtx->streams[audioStream]->codec;
记录一个音频解码器的上下文信息。
wanted_spec.freq = aCodecCtx->sample_rate;wanted_spec.format = AUDIO_S16SYS;wanted_spec.channels = aCodecCtx->channels;wanted_spec.silence = 0;wanted_spec.samples = SDL_AUDIO_BUFFER_SIZE;wanted_spec.callback = audio_callback;wanted_spec.userdata = aCodecCtx;
wanted_spec是一个SDL_AudioSpec结构体。
SDL_AudioSpec是包含音频输出格式的结构体,同时它也包含当音频设备需要更多数据时调用的回调函数。
int
freq
采样率
SDL_AudioFormat
format
音频数据格式;format 告诉SDL我们将要给的格式。在“S16SYS”中的S表示有符号的signed,16表示每个样本是16位长的,SYS表示大小头的顺序是与使用的系统相同的。这些格式是由avcodec_decode_audio2为我们给出来的输入音频的格式。
Uint8
channels
声音的通道数 1 单声道, 2 立体声;
Uint8
silence
表示静音的值。因为声音采样是有符号的,所以0当然就是这个值。
Uint16
samples
audio buffer size in samples (power of 2); 详情参考“讨论”
Uint32
size
音频缓存区大小(字节数),当我们想要更多声音的时候,我们想让SDL给出来的声音缓冲区的尺寸。一个比较合适的值在512到8192之间;ffplay使用1024。
SDL_AudioCallback
callback
当音频设备需要更多数据时调用的回调函数;
void*
userdata
这个是SDL供给回调函数运行的参数。我们将让回调函数得到整个编解码的上下文信息;
if (SDL_OpenAudio(&wanted_spec, &spec) < 0) { fprintf(stderr, "SDL_OpenAudio: %s/n", SDL_GetError()); return -1; }
如果你的程序能够处理不同的音频格式,把一个SDL_AudioSpec的指针作为SDL_OpenAudio() 的第二个参数可以取得硬件真正的音频格式。如果第二个参数是NULL,音频数据将在运行时被转换成硬件格式。
aCodec = avcodec_find_decoder(aCodecCtx->codec_id); if (!aCodec) { fprintf(stderr, "Unsupported codec!/n"); return -1; }
avcodec_find_decoder(aCodecCtx->codec_id);函数负责找到解码器。
avcodec_open(aCodecCtx, aCodec);
打开音频解码器。
下面要把音频从文件中循环的读出来
typedef struct PacketQueue { AVPacketList *first_pkt, *last_pkt; int nb_packets; int size; SDL_mutex *mutex; SDL_cond *cond;} PacketQueue;
再wanted_spec.callback =audio_callback;我已经解释过了这是一个回调函数,这个回调函数负责不断的播放声音,那么这个函数从那里取出声音呢?这时候我需要申请一块内存用来存放声音,回调函数不断的读取数据从我申请的内存中,这块内存就是队列PacketQueue 。
PacketQueue通过小链表AVPacketList把音频帧AVPacket组成一个顺序队列。
nb_packets为AVPacket的数量
size为AVPacket.size的总大小
void packet_queue_init(PacketQueue *q) { memset(q, 0, sizeof(PacketQueue)); q->mutex = SDL_CreateMutex(); q->cond = SDL_CreateCond();}
初始化队列。
函数memset(q, 0, sizeof(PacketQueue));负责将q中前sizeof(PacketQueue)个字节替换为0并返回q;
SDL_CreateMutex函数用来创建一个互斥体的,返回类型是SDL_mutex。
SDL_CreateCond创建一个条件变量。
int packet_queue_put(PacketQueue *q, AVPacket *pkt) { AVPacketList *pkt1; if (av_dup_packet(pkt) < 0) { return -1; } pkt1 = 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; else q->last_pkt->next = pkt1; q->last_pkt = pkt1; q->nb_packets++; q->size += pkt1->pkt.size; SDL_CondSignal(q->cond); SDL_UnlockMutex(q->mutex); return 0;}
函数SDL_LockMutex()锁定队列的互斥量以便于我们向队列中添加数据,然后函数SDL_CondSignal ()重新启动线程等待一个条件变量(如果它在等待)发出一个信号来告诉它现在已经有数据了,接着就会解锁互斥量并让队列可以自由访问。
int decode_interrupt_cb(void) { return quit;}...main() {... url_set_interrupt_cb(decode_interrupt_cb); ... SDL_PollEvent(&event); switch(event.type) { case SDL_QUIT: quit = 1;...
url_set_interrupt_cb函数是进行回调并检查我们是否需要退出一些被阻塞的函数。
在SDL中的,还必需要设置quit标志为1。
PacketQueue audioq;main() {... avcodec_open(aCodecCtx, aCodec); packet_queue_init(&audioq); SDL_PauseAudio(0);
初始化PacketQueue队列。
函数SDL_PauseAudio()让音频设备最终开始工作。如果没有立即供给足够的数据,它会播放静音。
while(av_read_frame(pFormatCtx, &packet)>=0) { // Is this a packet from the video stream? if(packet.stream_index==videoStream) { // Decode video frame .... } } else if(packet.stream_index==audioStream) { packet_queue_put(&audioq, &packet); } else { av_free_packet(&packet); }
循环读取音频流包的信息。
void audio_callback(void *userdata, Uint8 *stream, int len) { //获得编解码器的上下文信息 AVCodecContext *aCodecCtx = (AVCodecContext *) userdata; int len1, audio_size; static uint8_t audio_buf[(AVCODEC_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_size = audio_decode_frame(aCodecCtx, audio_buf, sizeof(audio_buf)); if (audio_size < 0) { 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 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; int len1, data_size; for (;;) { while (audio_pkt_size > 0) { data_size = buf_size; len1 = avcodec_decode_audio2(aCodecCtx, (int16_t *) audio_buf, &data_size, audio_pkt_data, audio_pkt_size); if (len1 < 0) { audio_pkt_size = 0; break; } audio_pkt_data += len1; audio_pkt_size -= len1; if (data_size <= 0) { continue; } 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; }}
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; 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;}
这段代码大概就是 先执行main函数,开启分线程audio_callback()不断读取音频流,然后用audio_decode_frame()解码,解码的数据从packet_queue_get()中取出来,
继续回到main函数packet_queue_put()负责把音频帧放到队列中去,以便于packet_queue_get()从队列中获取。
c文件下载
http://download.csdn.net/detail/wenwei19861106/4221053
- ffmpeg编程(三)把视频的声音播放出来
- ffmpeg编程四(把视频的声音播放出来)
- ffmpeg编程三(把视频文件播放出来)
- ffmpeg编程(二)把视频文件播放出来
- ffmpeg播放声音(三)
- 把一个音视频文件的视频部分抽取出来播放
- FFmpeg和SDL教程(三)播放声音
- FFmpeg和SDL教程(三)播放声音
- FFmpeg和SDL教程(三)播放声音
- FFmpeg和SDL教程(三)播放声音
- ffmpeg和sdl教程(三) --- 播放声音
- ffmpeg+SDL+windows 视频播放器的开发(三)
- 三、FFMPEG视频解码及播放
- ffmpeg编程基础:视频解码、音频播放
- ffmpeg 音频播放器的播放没声音的问题
- ffmpeg +SDL 视频播放实例(目前只能显示视频,没有声音)
- WPF 播放声音的三种方法
- WPF 播放声音的三种方法
- spring的DriverManagerDataSource与apache的BasicDataSource(转)
- ffmpeg编程(二)把视频文件播放出来
- weblogic无法关闭的处理方式.kill
- Java猜拳小游戏
- C#中virtual 方法和abstract方法的区别
- ffmpeg编程(三)把视频的声音播放出来
- 用GDB调试程序(1)
- C# Heap(ing) Vs Stack(ing) in .NET: Part II
- Darwin Streaming Server 6.0.3 - setup, customization, plugin or module development, performance and
- source VS exec
- 加班
- 在Windows中应用MinGW编译X264
- 工作队列(workqueue) create_workqueue/schedule_work/queue_work
- Flex中画箭头