自动采集推送( 相比较视频编码,音频编码要简单很多(组图))

优采云 发布时间: 2021-10-28 18:15

  自动采集推送(

相比较视频编码,音频编码要简单很多(组图))

  

  与视频编码相比,音频编码要简单得多。主要是将采集音频源数据PCM编码为AAC。

  MediaPlus 中的 FFmpeg 使用 libfdk-aac 编码器。有一个问题需要注意:FFmpeg已经放弃了AV_SAMPLE_FMT_S16格式的PCM编码AAC,这意味着如果你使用FFmpeg自带的AAC编码器,必须做音频重采样(重采样为:AV_SAMPLE_FMT_FLTP),否则AAC编码会失败.

  接下来,我们来看看采集音频和AAC在MediaPlus中是如何编码的。

  从.libmedia.core.streamer.RtmpPushStreamer的AudioThread中的AudioRecord采集中获取音频数据并传递给底层:

   class AudioThread extends Thread {

public volatile boolean m_bExit = false;

@Override

public void run() {

// TODO Auto-generated method stub

super.run();

int[] dataLength;

byte[] audioBuffer;

AudioCaptureInterface.GetAudioDataReturn ret;

dataLength = new int[1];

audioBuffer = new byte[m_aiBufferLength[0]];

while (!m_bExit) {

try {

Thread.sleep(1, 10);

if (m_bExit) {

break;

}

} catch (InterruptedException e) {

e.printStackTrace();

}

try {

ret = mAudioCapture.GetAudioData(audioBuffer,

m_aiBufferLength[0], dataLength);

if (ret == AudioCaptureInterface.GetAudioDataReturn.RET_SUCCESS) {

encodeAudio(audioBuffer, dataLength[0]);

}

} catch (Exception e) {

e.printStackTrace();

stopThread();

}

}

}复制代码

  AudioRecord采集的具体实现在avcapture.jar包中。代码比较简单。 Android音视频采集初始化和API调用的相关Demo网上有,这里不再赘述!

  

  

int AudioCapture::PushAudioData(OriginData *originData) {

if (ExitCapture) {

return 0;

}

originData->pts = av_gettime();

LOG_D(DEBUG,"audio capture pts :%lld",originData->pts);

audioCaputureframeQueue.push(originData);

return 0;

}复制代码

  以上代码和视频处理是同一个过程。调用.libmedia.core.streamer.RtmpPushStreamer>>startPushStream()后,数据已经加入音频队列,然后rtmpStreamer->StartPushStream(),其实就是把两个编码线程和音视频推流上,推流相关代码与视频相同。

  

int RtmpStreamer::StartPushStream() {

videoStreamIndex = AddStream(videoEncoder->videoCodecContext);

audioStreamIndex = AddStream(audioEncoder->audioCodecContext);

pthread_create(&t3, NULL, RtmpStreamer::WriteHead, this);

pthread_join(t3, NULL);

VideoCapture *pVideoCapture = videoEncoder->GetVideoCapture();

AudioCapture *pAudioCapture = audioEncoder->GetAudioCapture();

pVideoCapture->videoCaputureframeQueue.clear();

pAudioCapture->audioCaputureframeQueue.clear();

if(writeHeadFinish) {

pthread_create(&t1, NULL, RtmpStreamer::PushAudioStreamTask, this);

pthread_create(&t2, NULL, RtmpStreamer::PushVideoStreamTask, this);

}else{

return -1;

}

return 0;

}复制代码

  这里解释一下,在音频编码之前获取编码器和一些参数名称:

  libmedia/src/main/cpp/AudioEncoder.cpp 是音频编码的核心类。 int AudioEncoder::InitEncode() 方法封装了音频编码器的初始化。

  int AudioEncoder::InitEncode() {

std::lock_guard lk(mut);

avCodec = avcodec_find_encoder_by_name("libfdk_aac");

int ret = 0;

if (!avCodec) {

LOG_D(DEBUG, "aac encoder not found!")

return -1;

}

audioCodecContext = avcodec_alloc_context3(avCodec);

if (!audioCodecContext) {

LOG_D(DEBUG, "avcodec alloc context3 failed!");

return -1;

}

audioCodecContext->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;

audioCodecContext->sample_fmt = AV_SAMPLE_FMT_S16;

audioCodecContext->sample_rate = audioCapture->GetAudioEncodeArgs()->sampleRate;

audioCodecContext->thread_count = 8;

audioCodecContext->bit_rate = 50*1024*8;

audioCodecContext->channels = audioCapture->GetAudioEncodeArgs()->channels;

audioCodecContext->frame_size = audioCapture->GetAudioEncodeArgs()->nb_samples;

audioCodecContext->time_base = {1, 1000000};//AUDIO VIDEO 两边时间基数要相同

audioCodecContext->channel_layout = av_get_default_channel_layout(audioCodecContext->channels);

outputFrame = av_frame_alloc();

outputFrame->channels = audioCodecContext->channels;

outputFrame->channel_layout = av_get_default_channel_layout(outputFrame->channels);

outputFrame->format = audioCodecContext->sample_fmt;

outputFrame->nb_samples = 1024;

ret = av_frame_get_buffer(outputFrame, 0);

if (ret != 0) {

LOG_D(DEBUG, "av_frame_get_buffer failed!");

return -1;

}

LOG_D(DEBUG, "av_frame_get_buffer success!");

ret = avcodec_open2(audioCodecContext, NULL, NULL);

if (ret != 0) {

char buf[1024] = {0};

av_strerror(ret, buf, sizeof(buf));

LOG_D(DEBUG, "avcodec open failed! info:%s", buf);

return -1;

}

LOG_D(DEBUG, "open audio codec success!");

LOG_D(DEBUG, "Complete init Audio Encode!")

return 0;

}复制代码

  int AudioEncoder::EncodeAAC方法封装了AAC编码:

  int AudioEncoder::EncodeAAC(OriginData **originData) {

int ret = 0;

ret = avcodec_fill_audio_frame(outputFrame,

audioCodecContext->channels,

audioCodecContext->sample_fmt, (*originData)->data,

8192, 0);

outputFrame->pts = (*originData)->pts;

ret = avcodec_send_frame(audioCodecContext, outputFrame);

if (ret != 0) {

#ifdef SHOW_DEBUG_INFO

LOG_D(DEBUG, "send frame failed!");

#endif

}

av_packet_unref(&audioPacket);

ret = avcodec_receive_packet(audioCodecContext, &audioPacket);

if (ret != 0) {

#ifdef SHOW_DEBUG_INFO

LOG_D(DEBUG, "receive packet failed!");

#endif

}

(*originData)->Drop();

(*originData)->avPacket = &audioPacket;

#ifdef SHOW_DEBUG_INFO

LOG_D(DEBUG, "encode audio packet size:%d pts:%lld", (*originData)->avPacket->size,

(*originData)->avPacket->pts);

LOG_D(DEBUG, "Audio frame encode success!");

#endif

(*originData)->avPacket->size;

return audioPacket.size;

}复制代码

  注意:在 int AudioEncoder::InitEncode() 方法中

  avcodec_find_encoder_by_name("libfdk_aac");复制代码

  这里使用的是fdk-aac编码器,前提是必须将libfdk-aac库链接到ffmpeg动态库,否则找不到编码器。 FFmpeg自带AAC编码器,可以通过:

   avcodec_find_encoder(AV_CODEC_ID_AAC);复制代码

  获取 AAC 编码器。当然,如果使用FFmpeg的AAC编码器,就会有问题。文章开头提到,AV_SAMPLE_FMT_S16需要重采样为:AV_SAMPLE_FMT_FLTP。因为FFmpeg放弃了AV_SAMPLE_FMT_S16格式的PCM编码AAC,那么编码前还需要多一步重采样。

  以下AV_SAMPLE_FMT_S16 PCM音频数据重采样相关代码仅供参考:

  以上简单介绍了android采集音频PCM数据和AAC编码,AAC编码涉及的相关初始化,以及FFmpeg AAC编码器的重采样示例。 android*敏*感*词*采集、H264编码和Rtmp流媒体本文介绍了音视频采集、编码过程以及如何完成推送,相关文章待续...

0 个评论

要回复文章请先登录注册


官方客服QQ群

微信人工客服

QQ人工客服


线