zl程序教程

您现在的位置是:首页 >  其它

当前栏目

FFMPEG + SDL音频播放分析

分析 播放 FFMPEG 音频 SDL
2023-09-27 14:29:33 时间
抽象流程:

设置SDL的音频参数 —- 打开声音设备,播放静音 —- ffmpeg读取音频流中数据放入队列 —- SDL调用用户设置的函数来获取音频数据 —- 播放音频

SDL内部维护了一个buffer来存放解码后的数据,这个buffer中的数据来源是我们注册的回调函数(audio_callback),audio_callback调用audio_decode_frame来做具体的音频解码工作,需要引起注意的是:从流中读取出的一个音频包(avpacket)可能含有多个音频桢(avframe),所以需要多次调用avcodec_decode_audio4来完成整个包的解码,解码出来的数据存放在我们自己的缓冲中(audio_buf2)。SDL每一次回调都会引起数据从audio_buf2拷贝到SDL内部缓冲区,当audio_buf2中的数据大于SDL的缓冲区大小时,需要分多次拷贝。

关键实现: main()函数
         if(ic- streams[i])- codec- codec_type == AVMEDIA_TYPE_AUDIO audio_index == -1){
    int64_t wanted_channel_layout = 0; // 声道布局(SDL中的具体定义见“FFMPEG结构体”部分)
    if(!wanted_channel_layout || wanted_nb_channels != av_get_channel_layout_nb_channels(wanted_channel_layout)) {
        wanted_channel_layout = av_get_default_channel_lauout(wanted_channel_nb_channels);
        fprintf(stderr, "SDL_OpenAudio(%d channels): %s\n", wanted_spec.channels, SDL_GetError());
        wanted_spec.channels = next_nb_channels[FFMIN(7, wanted_spec.channels)]; // FFMIN()由ffmpeg定义的宏,返回较小的数
        fprintf(stderr, "SDL advised audio format %d is not supported\n", spec.format);
                fprintf(stderr, "SDL advised channel count %d is not support\n", spec.channels);
    ic- streams[stream_index]- discard = AVDISCARD_DEFAULT; //具体含义请查看“FFMPEG宏定义”部分
        /*  audio_buf_index 和 audio_buf_size 标示我们自己用来放置解码出来的数据的缓冲区,*/
        /*   这些数据待copy到SDL缓冲区, 当audio_buf_index = audio_buf_size的时候意味着我*/
        /*   们的缓冲为空,没有数据可供copy,这时候需要调用audio_decode_frame来解码出更
        len1 = avcodec_decode_audio4(is- audio_st_codec, is- audio_frame, got_frame, pkt);
        dec_channel_layout = (is- audio_frame- channel_layout is- audio_frame- channels
                   == av_get_channel_layout_nb_channels(is- audio_frame- channel_layout))
                   ? is- audio_frame- channel_layout : av_get_default_channel_layout(is- audio_frame- channels);                      
                  (wanted_nb_samples - is- audio_frame- nb_samples)*is- audio_tgt_freq/is- audio_frame- sample_rate,
                   wanted_nb_samples * is- audio_tgt_freq/is- audio_frame- sample_rate) 0) {
                  sizeof(is- audio_buf2)/is- audio_tgt_channels/av_get_bytes_per_sample(is- audio_tgt_fmt), 
             if(len2 == sizeof(is- audio_buf2)/is- audio_tgt_channels/av_get_bytes_per_sample(is- audio_tgt_fmt)) {
             resampled_data_size = len2*is- audio_tgt_channels*av_get_bytes_per_sample(is- audio_tgt_fmt);
 #define AV_CH_LAYOUT_2_2               (AV_CH_LAYOUT_STEREO|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT)
 #define AV_CH_LAYOUT_QUAD              (AV_CH_LAYOUT_STEREO|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT)
 #define AV_CH_LAYOUT_5POINT0           (AV_CH_LAYOUT_SURROUND|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT)
 #define AV_CH_LAYOUT_5POINT0_BACK      (AV_CH_LAYOUT_SURROUND|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT)
 #define AV_CH_LAYOUT_5POINT1_BACK      (AV_CH_LAYOUT_5POINT0_BACK|AV_CH_LOW_FREQUENCY)
 #define AV_CH_LAYOUT_6POINT0_FRONT     (AV_CH_LAYOUT_2_2|AV_CH_FRONT_LEFT_OF_CENTER|AV_CH_FRONT_RIGHT_OF_CENTER)
 #define AV_CH_LAYOUT_6POINT1_FRONT     (AV_CH_LAYOUT_6POINT0_FRONT|AV_CH_LOW_FREQUENCY)
 #define AV_CH_LAYOUT_7POINT0           (AV_CH_LAYOUT_5POINT0|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT)
 #define AV_CH_LAYOUT_7POINT0_FRONT     (AV_CH_LAYOUT_5POINT0|AV_CH_FRONT_LEFT_OF_CENTER|AV_CH_FRONT_RIGHT_OF_CENTER)
 #define AV_CH_LAYOUT_7POINT1           (AV_CH_LAYOUT_5POINT1|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT)
#define AV_CH_LAYOUT_7POINT1_WIDE      (AV_CH_LAYOUT_5POINT1|AV_CH_FRONT_LEFT_OF_CENTER|AV_CH_FRONT_RIGHT_OF_CENTER)
#define AV_CH_LAYOUT_7POINT1_WIDE_BACK (AV_CH_LAYOUT_5POINT1_BACK|AV_CH_FRONT_LEFT_OF_CENTER|AV_CH_FRONT_RIGHT_OF_CENTER)
#define AV_CH_LAYOUT_OCTAGONAL         (AV_CH_LAYOUT_5POINT0|AV_CH_BACK_LEFT|AV_CH_BACK_CENTER|AV_CH_BACK_RIGHT)
FFmpeg 开发(05):FFmpeg + OpenGLES 实现视频解码播放和视 前面 Android FFmpeg 开发系列文章中,我们已经利用 FFmpeg 的解码功能和 ANativeWindow 的渲染功能,实现了的视频的解码播放。但是,当你想为播放器做一些视频滤镜时,如加水印、旋转缩放等效果,使用 OpenGL ES 实现起来就极为方便。
FFmpeg简易播放器的实现3-音频播放 基于 FFmpeg 和 SDL 实现的简易视频播放器,主要分为读取视频文件解码和调用 SDL 播放两大部分。本实验仅研究音频播放的实现方式,不考虑视频。
FFmpeg 开发(02):FFmpeg + ANativeWindow 实现视频解码播放 本文将利用 FFmpeg 对一个 Mp4 文件的视频流进行解码,然后使用 libswscale 将解码后的 YUV 帧转换为 RGBA 帧,最后使用 ANativeWindow 进行渲染。