• SDL_AudioSpec(转)


    转贴地址:http://www.cnblogs.com/nanguabing/archive/2012/04/12/2444084.html

    ====================================================

    这篇主要讲把视频的声音播放出来

    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 

  • 相关阅读:
    嵌入式框架Zorb Framework搭建三:列表的实现
    嵌入式框架Zorb Framework搭建二:环形缓冲区的实现
    C#比较两个时间大小
    大数据概述
    综合练习:英文词频统计
    熟悉常用的Linux操作
    Python基础画五星红旗
    字符串、组合数据类型练习
    简化版C语言文法
    词法分析实验报告
  • 原文地址:https://www.cnblogs.com/wainiwann/p/4371760.html
Copyright © 2020-2023  润新知