ffplay源码分析3-代码框架 (4)

音频播放线程是SDL内建的线程,通过回调的方式调用用户提供的回调函数。
回调函数在SDL_OpenAudio()时指定。
暂停/继续回调过程由SDL_PauseAudio()控制。

3.6.1 sdl_audio_callback()

音频回调函数如下:

// 音频处理回调函数。读队列获取音频包,解码,播放 // 此函数被SDL按需调用,此函数不在用户主线程中,因此数据需要保护 // \param[in] opaque 用户在注册回调函数时指定的参数 // \param[out] stream 音频数据缓冲区地址,将解码后的音频数据填入此缓冲区 // \param[out] len 音频数据缓冲区大小,单位字节 // 回调函数返回后,stream指向的音频缓冲区将变为无效 // 双声道采样点的顺序为LRLRLR /* prepare a new audio buffer */ static void sdl_audio_callback(void *opaque, Uint8 *stream, int len) { VideoState *is = opaque; int audio_size, len1; audio_callback_time = av_gettime_relative(); while (len > 0) { // 输入参数len等于is->audio_hw_buf_size,是audio_open()中申请到的SDL音频缓冲区大小 if (is->audio_buf_index >= is->audio_buf_size) { // 1. 从音频frame队列中取出一个frame,转换为音频设备支持的格式,返回值是重采样音频帧的大小 audio_size = audio_decode_frame(is); if (audio_size < 0) { /* if error, just output silence */ is->audio_buf = NULL; is->audio_buf_size = SDL_AUDIO_MIN_BUFFER_SIZE / is->audio_tgt.frame_size * is->audio_tgt.frame_size; } else { if (is->show_mode != SHOW_MODE_VIDEO) update_sample_display(is, (int16_t *)is->audio_buf, audio_size); is->audio_buf_size = audio_size; } is->audio_buf_index = 0; } // 引入is->audio_buf_index的作用:防止一帧音频数据大小超过SDL音频缓冲区大小,这样一帧数据需要经过多次拷贝 // 用is->audio_buf_index标识重采样帧中已拷入SDL音频缓冲区的数据位置索引,len1表示本次拷贝的数据量 len1 = is->audio_buf_size - is->audio_buf_index; if (len1 > len) len1 = len; // 2. 将转换后的音频数据拷贝到音频缓冲区stream中,之后的播放就是音频设备驱动程序的工作了 if (!is->muted && is->audio_buf && is->audio_volume == SDL_MIX_MAXVOLUME) memcpy(stream, (uint8_t *)is->audio_buf + is->audio_buf_index, len1); else { memset(stream, 0, len1); if (!is->muted && is->audio_buf) SDL_MixAudioFormat(stream, (uint8_t *)is->audio_buf + is->audio_buf_index, AUDIO_S16SYS, len1, is->audio_volume); } len -= len1; stream += len1; is->audio_buf_index += len1; } // is->audio_write_buf_size是本帧中尚未拷入SDL音频缓冲区的数据量 is->audio_write_buf_size = is->audio_buf_size - is->audio_buf_index; /* Let's assume the audio driver that is used by SDL has two periods. */ // 3. 更新时钟 if (!isnan(is->audio_clock)) { // 更新音频时钟,更新时刻:每次往声卡缓冲区拷入数据后 // 前面audio_decode_frame中更新的is->audio_clock是以音频帧为单位,所以此处第二个参数要减去未拷贝数据量占用的时间 set_clock_at(&is->audclk, is->audio_clock - (double)(2 * is->audio_hw_buf_size + is->audio_write_buf_size) / is->audio_tgt.bytes_per_sec, is->audio_clock_serial, audio_callback_time / 1000000.0); // 使用音频时钟更新外部时钟 sync_clock_to_slave(&is->extclk, &is->audclk); } } 3.6.2 audio_decode_frame()

audio_decode_frame()主要是进行音频重采样,从音频frame队列中取出一个frame,此frame的格式是输入文件中的音频格式,音频设备不一定支持这些参数,所以要将frame转换为音频设备支持的格式。
audio_decode_frame()的实现在后面第5节讲述。

3.7 字幕解码线程

实现细节略。以后有机会研究字幕时,再作补充。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpxwjj.html