文章目录

  • I . FFMPEG 播放视频流程
  • II . OpenSLES 播放音频流程
  • III . OpenSLES 播放参考 Google 官方示例
  • IV . OpenSL ES 播放代码 ( 详细注释 )
  • IV . OpenSLES slAndroidSimpleBufferQueueCallback 回调函数声明及实现代码

I . FFMPEG 播放视频流程


FFMPEG 播放视频流程 : 视频中包含图像和音频 ;

① FFMPEG 初始化 : 参考博客 【Android FFMPEG 开发】FFMPEG 初始化 ( 网络初始化 | 打开音视频 | 查找音视频流 )

② FFMPEG 获取 AVStream 音视频流 : 参考博客 【Android FFMPEG 开发】FFMPEG 获取 AVStream 音视频流 ( AVFormatContext 结构体 | 获取音视频流信息 | 获取音视频流个数 | 获取音视频流 )

③ FFMPEG 获取 AVCodec 编解码器 : 参考博客 【Android FFMPEG 开发】FFMPEG 获取编解码器 ( 获取编解码参数 | 查找编解码器 | 获取编解码器上下文 | 设置上下文参数 | 打开编解码器 )

④ FFMPEG 读取音视频流中的数据到 AVPacket : 参考博客 【Android FFMPEG 开发】FFMPEG 读取音视频流中的数据到 AVPacket ( 初始化 AVPacket 数据 | 读取 AVPacket )

⑤ FFMPEG 解码 AVPacket 数据到 AVFrame ( 音频 / 视频数据解码 ) : 参考博客 【Android FFMPEG 开发】FFMPEG 解码 AVPacket 数据到 AVFrame ( AVPacket->解码器 | 初始化 AVFrame | 解码为 AVFrame 数据 )

⑥ FFMPEG AVFrame 图像格式转换 YUV -> RGBA : 参考博客 【Android FFMPEG 开发】FFMPEG AVFrame 图像格式转换 YUV -> RGBA ( 获取 SwsContext | 初始化图像数据存储内存 | 图像格式转换 )

⑦ FFMPEG ANativeWindow 原生绘制 准备 : 参考博客 【Android FFMPEG 开发】FFMPEG ANativeWindow 原生绘制 ( Java 层获取 Surface | 传递画布到本地 | 创建 ANativeWindow )

⑧ FFMPEG ANativeWindow 原生绘制 : 参考博客 【Android FFMPEG 开发】FFMPEG ANativeWindow 原生绘制 ( 设置 ANativeWindow 缓冲区属性 | 获取绘制缓冲区 | 填充数据到缓冲区 | 启动绘制 )

⑨ FFMPEG 音频重采样 : 参考博客 【Android FFMPEG 开发】FFMPEG 音频重采样 ( 初始化音频重采样上下文 SwrContext | 计算音频延迟 | 计算输出样本个数 | 音频重采样 swr_convert )

II . OpenSLES 播放音频流程


OpenSLES 播放音频流程 :

〇 视频播放操作 : FFMPEG 环境初始化 , 获取 AVStream 音视频流 , 获取 AVCodec 编解码器 , 读取音视频流中的数据到 AVPacket , 解码 AVPacket 数据到 AVFrame , AVFrame 图像格式转换 YUV -> RGBA , ANativeWindow 原生绘制 ;

〇 音频播放操作 : FFMPEG 环境初始化 , 获取 AVStream 音视频流 , 获取 AVCodec 编解码器 , 读取音视频流中的数据到 AVPacket , 解码 AVPacket 数据到 AVFrame , 音频重采样 , 然后使用 OpenSLES 播放重采样后的音频 ;

① 创建引擎 : 先创建引擎对象 , 再实现引擎对象 , 最后从引擎对象中 , 获取引擎接口 ;

SLresult result;// 创建引擎
result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);// 实现引擎
result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);// 获取引擎接口
result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);

② 设置输出混音器 : 创建输出混音器对象 , 实现输出混音器 ;

// 创建输出混音器对象 , 可以指定一个混响效果参数 ( 该混淆参数可选 )
const SLInterfaceID ids_engine[1] = {SL_IID_ENVIRONMENTALREVERB};
const SLboolean req_engine[1] = {SL_BOOLEAN_FALSE};
result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 1, ids_engine, req_engine);// 实现输出混音器
result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);

③ 获取混响接口并设置混响 : 该步骤不是必须操作 , 另外获取混响接口可能失败 ;

// 获取混响接口
result = (*outputMixObject)->GetInterface(outputMixObject, SL_IID_ENVIRONMENTALREVERB,&outputMixEnvironmentalReverb);
// 设置混响
if (SL_RESULT_SUCCESS == result) {result = (*outputMixEnvironmentalReverb)->SetEnvironmentalReverbProperties(outputMixEnvironmentalReverb, &reverbSettings);(void)result;
}

④ 配置音源输入 : 配置音频数据源缓冲队列 , 和 音源格式 ( 采样率 , 样本位数 , 通道数 , 样本大小端格式 ) ;

//1 . 配置音源输入// 配置要播放的音频输入缓冲队列属性参数 , 缓冲区大小 , 音频格式 , 采样率 , 样本位数 , 通道数 , 样本大小端格式
SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2};// PCM 格式
SLDataFormat_PCM format_pcm = {SL_DATAFORMAT_PCM,           //PCM 格式2,                           //两个声道SL_SAMPLINGRATE_44_1,        //采样率 44100 HzSL_PCMSAMPLEFORMAT_FIXED_16, //采样位数 16位SL_PCMSAMPLEFORMAT_FIXED_16, //容器为 16 位SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT,  //左右双声道SL_BYTEORDER_LITTLEENDIAN};  //小端格式// 设置音频数据源 , 配置缓冲区 ( loc_bufq ) 与 音频格式 (format_pcm)
SLDataSource audioSrc = {&loc_bufq, &format_pcm};

⑤ 配置音频输出 : 装载输出混音器对象 到 SLDataLocator_OutputMix , 在将 SLDataLocator_OutputMix 结构体装载到 SLDataSink 中 ;

// 配置混音器 : 将 outputMixObject 混音器对象装载入 SLDataLocator_OutputMix 结构体中
SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, outputMixObject};
// 将 SLDataLocator_OutputMix 结构体装载到 SLDataSink 中
//  音频输出通过 loc_outmix 输出 , 实际上是通过 outputMixObject 混音器对象输出的
SLDataSink audioSnk = {&loc_outmix, NULL};

⑥ 创建并实现播放器 : 先使用 引擎 , 音源输入 , 音频输出 , 采样率 , 接口队列ID 等参数创建播放器 , 再实现播放器对象 ;

// 操作队列接口 , 如果需要 特效接口 , 添加 SL_IID_EFFECTSEND
const SLInterfaceID ids_player[3] = {SL_IID_BUFFERQUEUE, SL_IID_VOLUME, SL_IID_EFFECTSEND,/*SL_IID_MUTESOLO,*/};
const SLboolean req_player[3] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE,/*SL_BOOLEAN_TRUE,*/ };// 创建播放器
result = (*engineEngine)->CreateAudioPlayer(engineEngine,&bqPlayerObject,&audioSrc, //音频输入&audioSnk, //音频商户处bqPlayerSampleRate? 2 : 3,//ids_player,req_player);// 创建播放器对象
result = (*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE);

⑦ 获取播放器接口 和 缓冲队列接口 : 获取的接口 对应 播放器创建时的接口 ID 数组参数 ;

// 获取播放器 Player 接口 : 该接口用于设置播放器状态 , 开始 暂停 停止 播放 等操作
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_PLAY, &bqPlayerPlay);// 获取播放器 缓冲队列 接口 : 该接口用于控制 音频 缓冲区数据 播放
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_BUFFERQUEUE,&bqPlayerBufferQueue);

⑧ 注册回调函数 : 按照指定的回调函数类型 , 声明并实现该回调函数 , 并将该回调函数注册给播放器缓冲队列接口 ;

// 注册缓冲区队列的回调函数 , 每次播放完数据后 , 会自动回调该函数
//      传入参数 this , 就是 bqPlayerCallback 函数中的 context 参数
result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, this);

回调函数类型 :

typedef void (SLAPIENTRY *slAndroidSimpleBufferQueueCallback)(SLAndroidSimpleBufferQueueItf caller,void *pContext
);

回调函数实现 :

//每当缓冲数据播放完毕后 , 会自动回调该回调函数
void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context)
{...//通过播放器队列接口 , 将 PCM 数据加入到该队列缓冲区后 , 就会自动播放这段音频(*bq)->Enqueue(bq, audioChannel->data, data_size);}

⑨ 获取效果器接口 和 音量控制接口 : 这两个接口不是必须的 , 可选选项 ;

    // 获取效果器发送接口 ( get the effect send interface )bqPlayerEffectSend = NULL;if( 0 == bqPlayerSampleRate) {result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_EFFECTSEND,&bqPlayerEffectSend);}// 获取音量控制接口 ( get the volume interface ) [ 如果需要调节音量可以获取该接口 ]result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_VOLUME, &bqPlayerVolume);

⑩ 设置播放状态 : 设置播放状态为 SL_PLAYSTATE_PLAYING ;

// 设置播放器正在播放状态 ( set the player's state to playing )
result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING);

⑪ 手动调用激活回调函数 : 第一次激活回调函数调用 , 需要手动激活 ;

// 手动激活 , 手动调用一次 bqPlayerCallback 回调函数
bqPlayerCallback(bqPlayerBufferQueue, this);

III . OpenSLES 播放参考 Google 官方示例


1 . Google 官方示例 : 关于 OpenSL ES 音频播放 , 在 Google 的官方示例 native-audio 中 , 有现成的代码可供使用 ;

① Google 官方示例 参考地址 : native-audio

② OpenSL ES 播放代码 : native-audio-jni.c

2 . FFMPEG 播放 : 在 FFMPEG 中可以原封不动的拷贝 native-audio 项目中的关于 OpenSL ES 播放相关的代码 , 但是在 slAndroidSimpleBufferQueueCallback 回调函数中播放的音频 , 是 FFMPEG 中音频从 AVPacket 解码成的 AVFrame 重采样后的音频 , 关于音频重采样参考 【Android FFMPEG 开发】FFMPEG 音频重采样 ( 初始化音频重采样上下文 SwrContext | 计算音频延迟 | 计算输出样本个数 | 音频重采样 swr_convert ) ;

IV . OpenSL ES 播放代码 ( 详细注释 )


OpenSL ES 播放部分的代码 : 细节内容看注释吧 , 不再展开一条一条的写了 ;

    // I . 创建 OpenSLES 引擎并获取引擎的接口 ( 相关代码拷贝自 Google 官方示例 native-audio )//      参考 : https://github.com/android/ndk-samples/blob/master/native-audio/app/src/main/cpp/native-audio-jni.c//声明每个方法执行的返回结果 , 一般情况下返回 SL_RESULT_SUCCESS 即执行成功//  该类型本质是 int 类型 , 定义的是各种类型的异常SLresult result;// 创建引擎result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);// 返回 0 成功 , 否则失败 , 一旦失败就中断退出assert(SL_RESULT_SUCCESS == result);(void)result;// 实现引擎result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);assert(SL_RESULT_SUCCESS == result);(void)result;// 获取引擎接口 , 使用该接口创建输出混音器 , 音频播放器等其它对象//      引擎对象不提供任何调用的方法 , 引擎调用的方法都定义在接口中result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);assert(SL_RESULT_SUCCESS == result);(void)result;// II . 设置输出混音器//  输出声音 , 添加各种音效 ( 混响 , 重低音 , 环绕音 , 均衡器 等 ) , 都要通过混音器实现 ;// 创建输出混音器对象 , 可以指定一个混响效果参数 ( 该混淆参数可选 )const SLInterfaceID ids_engine[1] = {SL_IID_ENVIRONMENTALREVERB};const SLboolean req_engine[1] = {SL_BOOLEAN_FALSE};result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 1, ids_engine, req_engine);assert(SL_RESULT_SUCCESS == result);(void)result;// 实现输出混音器result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);assert(SL_RESULT_SUCCESS == result);(void)result;// III . 获取混响接口 并 设置混响 ( 可能会失败 )// 获取环境混响接口// 如果环境混响效果不可用 , 该操作可能失败// either because the feature is not present, excessive CPU load, or// the required MODIFY_AUDIO_SETTINGS permission was not requested and grantedresult = (*outputMixObject)->GetInterface(outputMixObject, SL_IID_ENVIRONMENTALREVERB,&outputMixEnvironmentalReverb);if (SL_RESULT_SUCCESS == result) {result = (*outputMixEnvironmentalReverb)->SetEnvironmentalReverbProperties(outputMixEnvironmentalReverb, &reverbSettings);(void)result;}//IV . 配置音源输入// 配置要播放的音频输入缓冲队列属性参数 , 缓冲区大小 , 音频格式 , 采样率 , 样本位数 , 通道数 , 样本大小端格式SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2};/*typedef struct SLDataFormat_PCM_ {SLuint32       formatType;     //数据格式                 SL_DATAFORMAT_PCMSLuint32        numChannels;    //通道数 , 左右声道 2个     2SLuint32       samplesPerSec;  //采样率 44100Hz           SL_SAMPLINGRATE_44_1SLuint32        bitsPerSample;  //采样位数 16位            SL_PCMSAMPLEFORMAT_FIXED_16SLuint32       containerSize;  //容器大小                 SL_PCMSAMPLEFORMAT_FIXED_16SLuint32      channelMask;    //通道        SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHTSLuint32      endianness;     //小端格式                 SL_BYTEORDER_LITTLEENDIAN} SLDataFormat_PCM;*/SLDataFormat_PCM format_pcm = {SL_DATAFORMAT_PCM,           //PCM 格式2,                           //两个声道SL_SAMPLINGRATE_44_1,        //采样率 44100 HzSL_PCMSAMPLEFORMAT_FIXED_16, //采样位数 16位SL_PCMSAMPLEFORMAT_FIXED_16, //容器为 16 位SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT,  //左右双声道SL_BYTEORDER_LITTLEENDIAN};  //小端格式// 设置音频数据源 , 配置缓冲区 ( loc_bufq ) 与 音频格式 (format_pcm)SLDataSource audioSrc = {&loc_bufq, &format_pcm};// V . 配置音频输出// 配置混音器 : 将 outputMixObject 混音器对象装载入 SLDataLocator_OutputMix 结构体中SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, outputMixObject};// 将 SLDataLocator_OutputMix 结构体装载到 SLDataSink 中//  音频输出通过 loc_outmix 输出 , 实际上是通过 outputMixObject 混音器对象输出的SLDataSink audioSnk = {&loc_outmix, NULL};// VI . 创建并实现播放器/** 创建音频播放器:*      如果需要效果器时 , 不支持高性能音频*     ( fast audio does not support when SL_IID_EFFECTSEND is required, skip it*          for fast audio case )*/// 操作队列接口 , 如果需要 特效接口 , 添加 SL_IID_EFFECTSENDconst SLInterfaceID ids_player[3] = {SL_IID_BUFFERQUEUE, SL_IID_VOLUME, SL_IID_EFFECTSEND,/*SL_IID_MUTESOLO,*/};const SLboolean req_player[3] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE,/*SL_BOOLEAN_TRUE,*/ };// 创建播放器result = (*engineEngine)->CreateAudioPlayer(engineEngine,&bqPlayerObject,&audioSrc, //音频输入&audioSnk, //音频商户处bqPlayerSampleRate? 2 : 3,//ids_player,req_player);assert(SL_RESULT_SUCCESS == result);(void)result;// 创建播放器对象result = (*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE);assert(SL_RESULT_SUCCESS == result);(void)result;// VII . 获取播放器接口 和 缓冲队列接口// 获取播放器 Player 接口 : 该接口用于设置播放器状态 , 开始 暂停 停止 播放 等操作result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_PLAY, &bqPlayerPlay);assert(SL_RESULT_SUCCESS == result);(void)result;// 获取播放器 缓冲队列 接口 : 该接口用于控制 音频 缓冲区数据 播放result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_BUFFERQUEUE,&bqPlayerBufferQueue);assert(SL_RESULT_SUCCESS == result);(void)result;// VIII . 注册回调函数// 注册缓冲区队列的回调函数 , 每次播放完数据后 , 会自动回调该函数//      传入参数 this , 就是 bqPlayerCallback 函数中的 context 参数result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, this);assert(SL_RESULT_SUCCESS == result);(void)result;// IX . 获取效果器接口 和 音量控制接口 ( 不是必须的 )// 获取效果器发送接口 ( get the effect send interface )bqPlayerEffectSend = NULL;if( 0 == bqPlayerSampleRate) {result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_EFFECTSEND,&bqPlayerEffectSend);assert(SL_RESULT_SUCCESS == result);(void)result;}#if 0   // mute/solo is not supported for sources that are known to be mono, as this is// get the mute/solo interfaceresult = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_MUTESOLO, &bqPlayerMuteSolo);assert(SL_RESULT_SUCCESS == result);(void)result;
#endif// 获取音量控制接口// 获取音量控制接口 ( get the volume interface ) [ 如果需要调节音量可以获取该接口 ]result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_VOLUME, &bqPlayerVolume);assert(SL_RESULT_SUCCESS == result);(void)result;// X . 设置播放状态// 设置播放器正在播放状态 ( set the player's state to playing )result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING);assert(SL_RESULT_SUCCESS == result);(void)result;// XI. 手动调用激活回调函数// 手动激活 , 手动调用一次 bqPlayerCallback 回调函数bqPlayerCallback(bqPlayerBufferQueue, this);

IV . OpenSLES slAndroidSimpleBufferQueueCallback 回调函数声明及实现代码


1 . 回调函数原型 :

typedef void (SLAPIENTRY *slAndroidSimpleBufferQueueCallback)(SLAndroidSimpleBufferQueueItf caller,void *pContext
);

2 . 回调函数声明及实现 :

//每当缓冲数据播放完毕后 , 会自动回调该回调函数
// this callback handler is called every time a buffer finishes playing
void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context)
{//获取 PCM 采样数据 , 将重采样的数据放到 data 中int data_size ; //进行 FFMPEG 音频重采样 ... 大块代码参考上一篇博客 //开始播放if ( data_size > 0 ){//通过播放器队列接口 , 将 PCM 数据加入到该队列缓冲区后 , 就会自动播放这段音频//  注意 , 最后一个参数是样本字节数(*bq)->Enqueue(bq, audioChannel->data, data_size);}}

3 . 回调函数注册 :

    // VIII . 注册回调函数// 注册缓冲区队列的回调函数 , 每次播放完数据后 , 会自动回调该函数//      传入参数 this , 就是 bqPlayerCallback 函数中的 context 参数result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, this);assert(SL_RESULT_SUCCESS == result);(void)result;

【Android FFMPEG 开发】OpenSLES 播放音频 ( 创建引擎 | 输出混音设置 | 配置输入输出 | 创建播放器 | 获取播放/队列接口 | 回调函数 | 开始播放 | 激活回调 )相关推荐

  1. Android FFmpeg开发(三),利用OpenSL ES实现音频渲染

    上篇文章我们利用FFmpeg+ANativeWindwo实现了视频的解码和渲染,已经完成视频画面在SurfaceView上显示.还没阅读上一篇文章的同学建议先阅读:Android FFmpeg开发(二 ...

  2. 【Android FFMPEG 开发】FFMPEG 音频重采样 ( 初始化音频重采样上下文 SwrContext | 计算音频延迟 | 计算输出样本个数 | 音频重采样 swr_convert )

    文章目录 I . FFMPEG 播放视频流程 II . FFMPEG 音频重采样流程 III . FFMPEG 音频重采样 IV . FFMPEG 初始化音频重采样上下文 SwrContext V . ...

  3. 【Android FFMPEG 开发】FFMPEG ANativeWindow 原生绘制 ( Java 层获取 Surface | 传递画布到本地 | 创建 ANativeWindow )

    文章目录 I . FFMPEG ANativeWindow 原生绘制 II . FFMPEG 原生绘制流程 III . Java 层获取 Surface 画布 IV . 传递 Surface 画布到 ...

  4. 【Android FFMPEG 开发】Android 中使用 FFMPEG 将 PCM 音频采样转为 MP3 格式

    文章目录 一.前置操作 ( 移植 FFMPEG ) 二.FFMPEG 将 PCM 采样转为 MP3 格式的命令 三.Android FFMPEG 混音源代码完整示例 四.博客源码 一.前置操作 ( 移 ...

  5. 【Android FFMPEG 开发】FFMPEG 读取音视频流中的数据到 AVPacket ( 初始化 AVPacket 数据 | 读取 AVPacket )

    文章目录 I . FFMPEG 获取 AVPacket 数据前置操作 II . FFMPEG 获取 AVPacket 数据流程 III . FFMPEG AVPacket 结构体 IV . AVPac ...

  6. 【Android FFMPEG 开发】FFMPEG 获取编解码器 ( 获取编解码参数 | 查找编解码器 | 获取编解码器上下文 | 设置上下文参数 | 打开编解码器 )

    文章目录 博客简介 . FFMPEG 编解码器获取流程 I . FFMPEG 获取音视频流的编解码参数 AVCodecParameters *codecpar II . FFMPEG 查找解码器 av ...

  7. 【Android FFMPEG 开发】Android 中使用 FFMPEG 对 MP3 文件进行混音操作

    文章目录 一.前置操作 ( 移植 FFMPEG ) 二.FFMPEG 混音命令 三.Android FFMPEG 混音源代码完整示例 四.博客源码 一.前置操作 ( 移植 FFMPEG ) 参考 [A ...

  8. 【Android FFMPEG 开发】FFMPEG ANativeWindow 原生绘制 ( 设置 ANativeWindow 缓冲区属性 | 获取绘制缓冲区 | 填充数据到缓冲区 | 启动绘制 )

    文章目录 I . FFMPEG ANativeWindow 原生绘制 前置操作 II . FFMPEG 原生绘制流程 III . 设置 ANativeWindow 绘制窗口属性 ANativeWind ...

  9. 【Android FFMPEG 开发】FFMPEG 解码 AVPacket 数据到 AVFrame ( AVPacket-解码器 | 初始化 AVFrame | 解码为 AVFrame 数据 )

    文章目录 I . FFMPEG 解码 AVPacket 数据到 AVFrame 前置操作 II . FFMPEG 解码 AVPacket 数据到 AVFrame 流程 III . FFMPEG 发送 ...

最新文章

  1. 《代码大全2》读书笔记 Week 1
  2. django html文本编辑器,django xadmin 集成DjangoUeditor富文本编辑器
  3. Loadrunner性能测试结果分析
  4. ANSYS——对称模型对称边界的确定以及对称边界的约束施加问题
  5. 今天开始学opnet14.5
  6. 2019年新iPhone假机模曝光 越看越不顺眼
  7. NYOJ259 - 茵茵的第一课
  8. 机器学习基石笔记9——机器可以怎样学习(1)
  9. oracle lgt,第 2 章 Fortran 95 内函数
  10. 同一局域网建立ftp服务器实现文件共享
  11. 网站为什么要备案?网站备案流程是什么?
  12. C中kbhit()和getch()以及gets()和getchar()的用法
  13. 计算最高分,最低分,平均分
  14. 使用EasyExcel完成导出导入功能
  15. 2021年中国网络安全硬件市场竞争格局分析:深信服、新华三、启明星辰优势明显[图]
  16. 利用mininet进行链路拥塞造成数据丢包的实验
  17. linux驱动开发之内核线程
  18. 反三角函数在matlab中怎样定义
  19. 计算机一级电子表格计算公式,计算机一级电子表格(23页)-原创力文档
  20. OC 使用AVPlayer 简单的实现一个视频播放器

热门文章

  1. Bit,Byte,Word,Dword,Qword
  2. JavaScript 异常处理
  3. 【BZOJ 1486】 [HNOI2009]最小圈
  4. Arduino学习笔记35
  5. Asp.Net异常:由于代码已经过优化或者本机框架位于调用堆栈之上,无法计算表达式的值的解决方法...
  6. JSF框架在NetBeans下的编码
  7. java实现线程间通信的四种方式
  8. (转)关于Linux核心转储文件 core dump
  9. Android Tcp操作
  10. 20145201李子璇 《网络对抗》恶意代码分析