播放音频,设置好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;
  }
}

ffmpeg 播放音频的更多相关文章

  1. FFmpeg学习3:播放音频

    参考dranger tutorial,本文将介绍如何使用FFmpeg解码音频数据,并使用SDL将解码后的数据输出. 本文主要包含以下几方面的内容: 关于播放音频的需要的一些基础知识介绍 使用SDL2播 ...

  2. FFMPEG + SDL音频播放分析

    目录 [hide] 1 抽象流程: 2 关键实现: 2.1 main()函数 2.2 decode_thread()读取文件信息和音频包 2.3 stream_component_open():设置音 ...

  3. FFmpeg 入门(3):播放音频

    本文转自:FFmpeg 入门(3):播放音频 | www.samirchen.com 音频 SDL 提供了播放音频的方法.SDL_OpenAudio 函数用来让设备播放音频,它需要我们传入一个包含了所 ...

  4. [总结]FFMPEG视音频编解码零基础学习方法--转

    ffmpeg编解码学习   目录(?)[-] ffmpeg程序的使用ffmpegexeffplayexeffprobeexe 1 ffmpegexe 2 ffplayexe 3 ffprobeexe ...

  5. FFMPEG视音频编解码零基础学习方法

    在CSDN上的这一段日子,接触到了很多同行业的人,尤其是使用FFMPEG进行视音频编解码的人,有的已经是有多年经验的“大神”,有的是刚开始学习的初学者.在和大家探讨的过程中,我忽然发现了一个问题:在“ ...

  6. FFMPEG视音频编解码零基础学习方法-b

    感谢大神分享,虽然现在还看不懂,留着大家一起看啦 PS:有不少人不清楚“FFmpeg”应该怎么读.它读作“ef ef em peg” 0. 背景知识 本章主要介绍一下FFMPEG都用在了哪里(在这里仅 ...

  7. [总结]FFMPEG视音频编解码零基础学习方法

    在CSDN上的这一段日子,接触到了很多同行业的人,尤其是使用FFMPEG进行视音频编解码的人,有的已经是有多年经验的“大神”,有的是刚开始学习的初学者.在和大家探讨的过程中,我忽然发现了一个问题:在“ ...

  8. 【转】[总结]FFMPEG视音频编解码零基础学习方法

    在CSDN上的这一段日子,接触到了很多同行业的人,尤其是使用FFMPEG进行视音频编解码的人,有的已经是有多年经验的“大神”,有的是刚开始学习的初学者.在和大家探讨的过程中,我忽然发现了一个问题:在“ ...

  9. 浏览器音频兼容和ffmpeg的音频转码使用

    浏览器对各音/视频格式的支持问题 浏览器测试效果图 ffmpeg在音频格式转换,和从视频中提取音频的简单实用 1.百度搜索浏览器对于音频文件的兼容,排在前面的文章大部分是复制粘贴很久以前的文章,容易误 ...

随机推荐

  1. Samsung_tiny4412(驱动笔记09)----alloc_pages,kmalloc,vmalloc,kmem_cache,class

    /*********************************************************************************** * * alloc_pages ...

  2. [LeetCode&Python] Problem 804. Unique Morse Code Words

    International Morse Code defines a standard encoding where each letter is mapped to a series of dots ...

  3. C/S架构和B/S架构

    本文内容整理自http://blog.csdn.net/tennysonsky/article/details/45062079 C/S架构和B/S架构是两种颇具影响力的软件体系结构.C/S是一种历史 ...

  4. egret游戏入门之学习资源篇

    最近因需要,入手H5游戏. 写游戏当然需要有引擎. H5游戏开发:游戏引擎入门推荐 如何选择 H5 游戏引擎 白鹭引擎和layabox哪个好用,哪个技术更成熟 ? LayaBox 与 Egret 选择 ...

  5. hdu 5312 dp(背包)、二分图或其他姿势

    题意:给出一个二分图(不一定连通),问最多能加多少边,使它仍然是二分图 BC周年庆第四题,貌似终判再终判之后数据还是有问题``` 据说貌似可以用bitset搞,而且姿势优美是正解```然而我还是用的d ...

  6. 【MVC】使用MvcPager进行分页

    1.添加引用: mvcPager 版本高的提供的功能也更多. 注:下载了第一个,但是里面的一些字段是只读的.(eg:PagedList<T> .TotalItemCount)这是不符合的. ...

  7. 设置tab标签页 遮挡部分

    效果如下: 主要代码: <div class="need-detail"> <div class="top-title"> <sp ...

  8. memsql 6.7集群安装

    预备环境处理 安装yum 源 yum install -y yum-utils yum-config-manager --add-repo https://release.memsql.com/pro ...

  9. 【VB.NET】——若水归海

    版权声明:本文为博主原创文章.未经博主同意不得转载. https://blog.csdn.net/successA/article/details/34496209 看完台湾微软特约资深讲师的VB.N ...

  10. tyvj1035棋盘覆盖——二分图匹配

    题目:http://www.joyoi.cn/problem/tyvj-1035 把可放的位置作为节点,相邻的连边. 可用天然有的编号作为节点的编号. 果然只用连单向边就行了.也只需记录另一部的对应点 ...