该文章首发于微信大众号:字节活动

FFmpeg 开发系列连载:

  • FFmpeg 开发(01):FFmpeg 编译和集成

  • FFmpeg 开发(02):FFmpeg + ANativeWindow 结束视ios8备忘录频解码播映

  • FFmpegjava面试题 开发(03):androidstudio装置教程FFmpeg + OpenSLES 结束音频解码播映

  • FFmpeg 开发(04):FFmpeffmpeg.dll是什么g + OpenGLES 结束音频可ios模拟器视化播映

  • FFmpeg 开发(05):FFmpeg + OpenGLES 结束视频解码播java游戏放和视频滤镜

  • FFmpeg 开发(06):FFmpeg 播映器结束音视频同步的三种方法

  • FFmpeg 开发(07):java编译器FFmpeg + OpenGLES 结束 3D 全景播映器

  • FFmpeg 开发(08):FFmpeg 播映器视频FFmpeg烘托优化

  • FFmpeg 开发(09):FFmpeg、x264 以及 fdk-aac 编译整合

  • FFmpeg 开发(10):FFmpeg 视频录制 – 视频添加滤镜和编码

前文运用 FFmpeg 对 Android Camera2 收集的预览帧先进行烘托,然后FFmpeg运用 OpenGL 添加OpenGL滤镜,最终将烘托成果进行编码opengl和directx生成 mp4 文件。java游戏

本文将运用 Android AudioRecorder 收集opengles3.1扩展包 PCM 音频,然后运用 Fjava模拟器Fmpeg 对其opengl形式编码生成 aac 文件。

提早预告下,在该系列的下一篇文章将介绍 FFmpeg 一起对 Android Camera 收集的预览帧和 AudioRecorder 收集的音频数据进行编码,生成一个 mp4 文件。

FFmpeg 开发(11):FFmpeg + Android AudioRecorder 音频录制编码

AudioRecorder 运用

这儿运用 Android AudioRecorder API 收集音opengl版别过低android下载装置 PCM 裸数据,然后经过 jni 传递到 Native 层供 FFmffmpeg装置peg 编码运用。

下面代码将 AudioRecoder 封装到线程里,经过接口回调的方法将 PCM 数据传出来,默许android平板电脑价格采样率为 44.1kHz,双通道立体声android什么意思,采样格局为 PCM 16 bit 。

public class AudioRecorder extends Thread {
private static final String TAG = "AudioRecorder";
private AudiojavahdxxRecord mAudioRecorffmpeg装置d = null;
private static final int DEFios下载AULT_SAMjavahdxxPLopenglskiaE_RATE = 44100;
private static final int DEFAULT_CHANNEL_LAYOUTios是什么意思 = AudioFormat.CHANNEL_IN_STEREO;
private sopengl是什么意思taios8备忘录tic final int DEFAULT_SAMPLE_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
private final AudioRecorderCallback mRecorderCallback;
popengl和directxublic AudioRecorder(AudioReopengl版别过低corderCallback callback) {
this.mRecorderCallback = callback;
}
@Override
public void run() {
final int mMinBufferSize = AudioRecord.getMinBufferSize(DEFopengl-legacyAULT_SAMPLE_RATE, DEFios14.4.1更新了什么AULT_Cjava名优馆在线六区HANNEL_LAYOUT, DEFAULopengl烘托gpu怎样设置T_SAMPLE_FORMAT);
Log.d(java游戏TAG, "run() called mMinBuffeopengl形式rSize=" + mffmpeg从入门到通晓MinBufffmpeg指令详解ferSize);
mAudioRecord = new AudioRecord(android.media.Mjava名优馆在线六区ediaRecorder.AudioSource.MIC, DEFAULT_SAMPLE_Rjava游戏ATE, DEFAULT_CHOpenGLANNEL_LAYOUT, DEFAULT_SAMPLE_FORMAT, mMinBufferSize);
try {
mAudioRecord.startRecording();
} catch (IllegalStateExceptioandroid下载装置n e) {
mRecorderCallback.onError(e.getMessage() + " [startRecording failed]");
return;
}
byte[android体系] sampleBuffer = new byte[4096];
try {
whiios14.4.1更新了什么le (!Thread.candroid下载装置urrentThread().isInterruptedios14.4值得晋级吗()) {
int rejava面试题sult = mAudioRecord.read(sampleBuffer, 0, 4096);
if (result > 0) {
mRecorderCallback.onAudioopengl是什么意思Data(sampleBuffer, result);
}
}
} catch (Exception e) {
mRecorderCallback.onError(e.getMessage())ffmpeg播映器;
}
mAudijava模拟器oRecord.release();java模拟器
mAudioRecord = null;
}ios14.4怎样样
public interface AudioRecorderCallback {
void onAudioData(byte[] data, inandroid平板电脑价格t datajava编译器Size);
void onError(String msg);
}
}

音频编码流程

音频的编码流程与视频编码流程基本上共同,为了更加清楚地展现流程android什么意思,也画了一opengles3.1扩展包张流程图如下图所示。

FFmpeg 开发(11):FFmpeg + Android AudioRecorder 音频录制编码

AudioRecjava怎样读oder 收集的 PCM 音频放入音频部队中,子线程音频编android手机码循环不断从部队中取数据进行编码,最终将编码数据写入媒体文件。

FFmpeg 两种采样格局

由于新的opengl-legacy FFmpeg 版别不再支撑对 AV_SAMPLE_FMT_S16 采样格局的音频数据进行编码,需java名优馆在线六区要运用 swr_convert 将格局转化为 AV_SAMPLE_FMT_FLTP ,而 AV_SAMPLE_FMT_S16 便是 Audiandroid什么意思oRecorder 的 AudioFormat.ENCODING_PCM_16BIT 。

两种采样格局的特色:

  • AV_SAMPLE_FMT_S16 位宽 16 bit , shorffmpeg是什么意思t 型,取值规划 [-32767, 32767];
  • AV_SAMPLE_FMT_FLTP 位宽 32 bit , float 型,取值规划 [-1.0, 1.0];

能够看出单声道两者仅仅取值规划上的不ffmpeg装置同,ffmpeg.dll找不到是什么状况双声道的话两者还有存储结构上的差异。

双声道 AV_SAMPLE_FMT_S16 和 AV_SAMPLE_FMT_FLTandroid的drawable类P 格局结构

FFmpeg 开发(11):FFmpeg + Android AudioRecorder 音频录制编码

从图中能够看出,双声道 AV_SAMPLE_FMT_S16 类型左右声道数据交叉存储,而双声道 AV_SAMPLE_FMT_FLTP 类型左右声道各存储在一个 planer ,了解 YUV 格局的同学能够看出这种排布办opengl和directx法有点像 YUV420SP 和 YUV420P 。

swffmpeg合并视频resample 库的 swr_convert 函数,网上看到常常有人用错,这儿简单介绍下:

/** Converandroid体系t audio.
* @param s         allocated Swr contexffmpeg播映器t, with parameters set
* @param out       output buffers, only the first one need be set in case of packed audio
* @param out_ffmpeg装置count amount of space avJavaailable for output in sampopengl和directxles per channel
* @param in        input buffers, only the first one need to be set in case of packed audio
* @param in_count  number of input samples availableopengl是什么意思 in one chanJavanel
*
* @retandroid的drawable类urn number of samples output per channel, negative valopengles3.1扩展包ue on error
*/
int swr_convert(struct SwrCojava游戏ntext *s, uint8_t **out, int out_coandroid的drawable类unt,
const uint8_t **in , int in_count);

其间 in_count 和 out_count 标明的是输入和输出每个声道的样本数量,而不是 2 个声道总的样本数,比如收集一坨ffmpeg.dll找不到是什么状况 4096 字节的双声道 AV_SAMPLE_FMT_S16 数据,那么它每个通道的样本数量是 4096 / 2(双声道) / 2(16 bits) = 1024 。

其他 AVFrame 中的 nb_samples 标明的也是每个声道的样本数量。

swr_convert 的运用:

// audioFrame->data 标明双声道 AV_SAMPLE_FMT_S16 数据 
int result = swr_convert(recorder->m_swrCtx, pFramjava面试题e->data, pFrame->nb_samples, (const uopengl是什么意思int8_t **) &(audioFrame->data), audioFrame->daios模拟器taSize / 4);ffmpeg是什么意思
// result 数量一般等于 in_count , 小于 0 时标明转化失利。

代码结束

FFmpeg 编码音频数据跟编码视频数据结束相同,Android AudioRecordjavascripter 经过 jni 将 PCM 数据传递到 Native 层部队中,供 FFmpeg 编码运用。

extern "C"
JNIEXPORT void JNICALL
Java_com_byteflow_leffmpeg指令详解arnffmpeg_media_MediaRecorderContext_native_1OnAudioData(JNIEnv *env,
jobject thiz,
jbyteArray data,
jiios14.4.1更新了什么nt size) {
int len = env->GetArrayLenandroid下载装置gth (data);
unsigned char* buf = new unsigned char[lenjava名优馆在线六区];
envopengl版别过低-&android是什么手机牌子gtffmpeg指令详解;GetByteArrayRffmpeg教程egion(data, 0, len, rjavahdxxeinterpret_cast<jbyte*>(buf));
MediaRecorderContext *pContext = MediaRecorderContext::GetContext(env, thiz);
if(pContext) pContext->OnAudioData(buf, len);
delete[] bandroid手机uf;
}

为了便当读者 demo ,现在把 FFmpeg 编码音频的结束也放到一个独自的类中来结束。

class SingleAudioRecorder {
public:
SingleAudioRecorder(const char *outUrl, int sampleRate, int channelLayouOpenGLt, int sampleFormat);
~SingleOpenGLAudioRecorder();
//初步录制
int StartRecord();
//接收音频数据
int OnFrame2Encode(AudioFrame *inpffmpeg从入门到通晓utFrame);
/ffmpeg.dll是什么/连续录制
int StopRecord();
private:
//编码循环
static void StartAACEncoderThread(SingleAudioRecorder *context);
//编码一帧的函数
int EncodeFrame(AVFrame *pFrame);
private:
ThrFFmpegeadSafeQueue<ios是什么意思AudioFrame *android体系> m_frameQueue;
char m_outUrl[1024] = {0};
int m_frameIndex = 0;
int m_sjava游戏ampleRate;
int m_channios模拟器elLayout;
int m_sampleFormat;
AiOSVPacket m_avPacket;
Aopengl怎样晋级VFrame  *m_pFrame = nullptr;
uint8_t *m_pFrameBuffer = nullandroid什么意思ptr;
int m_frameBufferSize;
AVCodec  *m_pCodec = nulandroid是什么手机牌子lptr;
AVStream *mopengl三重缓冲_pStream = nullptr;
AVCodecCojava环境变量装备ntext *m_opengl怎样晋级pCodecCtx = nullptr;
AVFormatContext *m_pFormatCtx = nullptr;
SwrContext *m_swrCtx = nujavascriptllptr;
thread *m_encodeThreffmpeg.dll是什么ad = nullptr;
volatile int m_exit = 0;
};

SingleAudioRecoropenglskiader 在一android手机个线程里敞开编码循环,不断地从音频部队中取数据进行编码。ffmpeg教程

/android平板电脑价格/编码循环
void SingleAudioRecorder::StartAACjava名优馆在线六区EncoderThread(SingleAudioRecorder *recorder) {
LOGJavaCATE("SingleAudioRecorder::StartAACEncoderThread sjava编译器tart"java游戏);
while (!recorandroid/yunosder->m_exit || !recorder-&gjavascriptt;m_frameQueue.Empty())
{
if(recorder->m_frameQueue.Empty()) {
//部队为空,休眠等候
usleffmpeg指令详解ep(10 * 1000);
continue;
}
AudioFrame *audioffmpeg.dll是什么Frame = recorder->m_frameQueue.Pop();
Aandroid的drawable类VFrame *pFrame = recorder->m_pFrame;
//音频采样格局转化
int result = swr_convert(ffmpeg播映器recorder->m_swrCtx, pFrame->data, pFrame->nb_samples, (const uint8_t **) &(audiAndroidoFrame->Androiddata), audioFraandroid平板电脑价格me->dataSize / 4);
LOGCATE("SingleAudioRecorder::StartAACEncoderThread result=%d", result);
if(result >= 0) {
pFrame->pjava环境变量装备ts = recorder-&ffmpeg从入门到通晓gt;m_frameIndex++;
recorder-&gtffmpeg安卓版别下载;EncodeFrame(pFrame);ios8备忘录
}
delete audioFrameandroid的drawable类;
}
LOGCJavaATE("SingleAudioRecJavaorder::android什么意思StartAACEncoderThread end"ios8备忘录);
}
//编码一帧的函数
int SingleAudioRecoopengl-legacyrder::EncodeFrame(AVFrame *pFrame) {
LOGCATE("SingleAudioRecorder::EncodeFrame pFrame->nb_samples=%d", pFrame != nullptr ? pFrame->nb_samples : 0);
int result = 0;
rios卖肉直播不收费下载esult = avcodec_send_frame(m_pCodecCtx, pFrffmpeg教程ame);
if(result < 0)
{
LOGCATE("SingleAudioRecorder::EncodeFrame avcodec_send_frame fail. retFFmpeg=%d", resultopengl怎样晋级);
retios14.4.1更新了什么urn resuopengl形式lt;
}
while(!rios14.4.1更新了什么esult) {
result = avcodec_receive_pajava基础知识点cket(m_pCodecCtx, &m_avPacffmpeg合并视频ket);
if (result == AVERROR(EAGAIN) || result == AVERROR_EOF) {
returios卖肉直播不收费下载n 0;
} else if (result < 0) {
LOGCATE(android的drawable类"SingleAudioRecoandroid手机rder::EncodeFrame avcodec_receivandroid什么意思e_packet fail. ret=%d", result);
randroid是什么手机牌子eturn result;
}
LOGCATE("SingleAudioRecorder::EncodeFrame frame pandroid下载ts=ios体系%ld, size=%d", m_avPacket.pts, m_avPacket.size);
m_avPacket.stream_index = m_pStream->index;
av_interleaved_write_frame(m_pFormatCtx, &m_avPacket);
av_packet_unref(&m_avPacket);
}
return 0;
}

无缺的结束代码能够参看项目:
LearnFFmpejava环境变量装备g

技能android是什么手机牌子交流

技能交流/获取视频教FFmpeg程能够添加我的微信:Byte-Flow