• ffmpeg 播放音频


    播放音频,设置好SDL_AudioSpec播放参数,然后由SDL回调函数进行解码和数据的拷贝,解码播放音频无需设置延迟,因为声卡播放音频是阻塞的

    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循环解码,直到将Packet中的多个音频帧数据解码完成了,再读取下一个包,每次解码一个音频帧数据后饭后解码后的大小,如果一直未解码出数据,则直到将该packet解码完
      //了后都没有解码出数据,则读取下一个包,一直到有数据解码成来后再返回解码成功后的数据大小
      while(audio_pkt_size > 0)
      {
        int got_frame = 0;
        len1 = avcodec_decode_audio4(aCodecCtx, &frame, &got_frame, &pkt);
        if(len1 < 0)
        {
          //解码失败,则跳过该包数据,解码下一包数据
          audio_pkt_size = 0;
          break;
        }
        audio_pkt_data += len1;
        audio_pkt_size -= len1;
        data_size = 0;
        if(got_frame)
        {
          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;
      }
      //静态变量保存Packet的数据
      audio_pkt_data = pkt.data;
      //静态变量保存Packet的大小
      audio_pkt_size = pkt.size;
      }
    }

    SDL回调函数进行音频的播放,每次将解码的数据拷贝到SDL缓冲区,大小为len

    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)
      {

        //如果上次拷贝后,还有数据剩余未拷贝完成,则现先将上次剩余的数据拷贝进去SDL缓冲区后,然后进行下一个包数据的解码和拷贝
        if(audio_buf_index >= audio_buf_size)
        {
          //audio_size 解码的长度,audio_buf解码的缓存
          audio_size = audio_decode_frame(aCodecCtx, audio_buf, sizeof(audio_buf));
          if(audio_size < 0)
          {
            //如果未解码出数据,则设置为静音
            audio_buf_size = 1024; // arbitrary?
            memset(audio_buf, 0, audio_buf_size);
          }
          else
          {
            //解码后的长度
            audio_buf_size = audio_size;
          }
            //每次解码后从第一个位置开始拷贝数据
            audio_buf_index = 0;
        }
        //解码后的缓存区所剩余的长度,如果解码数据大于SDL回调缓冲区的大小,则只需填充慢SDL回调缓冲区
        //如果未能填充满SDL缓冲区,则解码下一个Packet进行填充
        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;
      }
    }

    作者:长风 Email:844064492@qq.com QQ群:607717453 Git:https://github.com/zhaohu19910409Dz 开源项目:https://github.com/OriginMEK/MEK 本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利. 感谢您的阅读。如果觉得有用的就请各位大神高抬贵手“推荐一下”吧!你的精神支持是博主强大的写作动力。 如果觉得我的博客有意思,欢迎点击首页左上角的“+加关注”按钮关注我!
  • 相关阅读:
    视图的作用,视图可以更改么?
    数据库事务的四个特性及含义
    mysql 设置隔离级别
    如何避免事务的并发问题?
    事务控制语言(TCL)
    事务的并发问题有哪些?
    事务的隔离级别: 事务并发问题如何发生?
    DDL 语言
    DML 语言
    TRUNCATE、Drop、Delete 的用法
  • 原文地址:https://www.cnblogs.com/zhaohu/p/7042411.html
Copyright © 2020-2023  润新知