相比力视频编码,音频编码要简单很多,首要就是将收集到的音频源数据PCM编码AAC. MediaPlus中FFmpeg利用的是libfdk-aac编码器,这里有个题目需要留意下:FFmpeg已经烧毁了AV_SAMPLE_FMT_S16格式PCM编码AAC,也就是说假如利用FFmpeg自带的AAC编码器,必须做音频的重采样(重采样为:AV_SAMPLE_FMT_FLTP),否则AAC编码是失利的。 接下来看下 1、MediaPlus中是若何收集音频与AAC编码的.在APP.mobile.nativeapp.com.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,这里不再赘述!
* 收集的PCM音频数据 * * @param audioBuffer * @param length */ public void encodeAudio(byte[] audioBuffer, int length) { try { LiveJniMediaManager.EncodeAAC(audioBuffer, length); } catch (Exception e) { e.printStackTrace(); } }
Java_app_mobile_nativeapp_com_libmedia_core_jni_LiveJniMediaManager_EncodeAAC(JNIEnv *env, jclass type, jbyteArray audioBuffer_, jint length) { if (audioCaptureInit && !isClose) { jbyte *audioSrc = env->GetByteArrayElements(audioBuffer_, 0); uint8_t *audioDstData = (uint8_t *) malloc(length); memcpy(audioDstData, audioSrc, length); OriginData *audioOriginData = new OriginData(); audioOriginData->size = length; audioOriginData->data = audioDstData; audioCapture->PushAudioData(audioOriginData); env->ReleaseByteArrayElements(audioBuffer_, audioSrc, 0); } return 0; } 以上代码,是在挪用app.mobile.nativeapp.com.libmedia.core.streamer.RtmpPushStreamer>>startPushStream()开启推流前的相关挪用:首要就是初始化音频收集,并将数据传入底层。
* 开启推流 * @param pushUrl * @return */ private boolean startPushStream(String pushUrl) { if (nativeInt) { int ret = 0; ret = LiveJniMediaManager.StartPush(pushUrl); if (ret < 0) { Log.d("initNative", "native push failed!"); return false; } return true; } return false; } 以下图:
if (ExitCapture) { return 0; } originData->pts = av_gettime(); LOG_D(DEBUG,"audio capture pts :%lld",originData->pts); audioCaputureframeQueue.push(originData); return 0; } 上面这些代码与视频的处置方式都是一样的流程,在挪用app.mobile.nativeapp.com.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; }
rtmpStreamer->audioEncoder->EncodeAAC(&pAudioData);AAC编码. rtmpStreamer->SendFrame(pAudioData, rtmpStreamer->audioStreamIndex);推流(与视频推流分歧) 【进修地址】:FFmpeg/WebRTC/RTMP/NDK/Android音视频流媒体高级开辟 这里说明下,音频编码前获得编码器及一些参数的指定: libmedia/src/main/cpp/AudioEncoder.cpp是音频编码的焦点类,int AudioEncoder::InitEncode() 方式封装了音频编码器的初始化。 int AudioEncoder::InitEncode() {std::lock_guard<std::mutex> 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; }
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!");
以上是编码前必必要完成的初始化. 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; }
ret = avcodec_receive_packet(audioCodecContext, &audioPacket); audioPacket就是编码后的数据了,data是编码后的数据,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,那末在编码前就需要多一步重采样的处置. 2、以下AV_SAMPLE_FMT_S16 PCM音频数据重采样相关代码仅供参考:
AV_SAMPLE_FMT_FLTP,//输出格式 48000,//输出采样率 av_get_default_channel_layout(CHANNELS),//输入通道Layout AV_SAMPLE_FMT_S16,//输入格式 48000,//输入采样率 NULL,//NULL NULL);//NULL ret = swr_init(swrContext);//初始化SwrContext if (ret != 0) { LOG_D(DEBUG, "swr_init failed!"); return -1; }
if (encodeAAC->exit) { break; } if (encodeAAC->frame_queue.empty()) { continue; } const uint8_t *indata[AV_NUM_DATA_POINTERS] = {0}; //PCM s16 uint8_t *buf = *encodeAAC->frame_queue.wait_and_pop().get();//PCM 16bit #ifdef FDK_CODEC //fdk-aac无需重采样 ret = avcodec_fill_audio_frame(encodeAAC->outputFrame, encodeAAC->avCodecContext->channels, encodeAAC->avCodecContext->sample_fmt, buf, BUFFER_SIZE, 0); if (ret < 0) { LOG_D(DEBUG, "fill frame failed!"); continue; } #else //重采样AM_SAMPLE_FMT_FLTP indata[0] = buf; swr_convert(encodeAAC->swrContext, encodeAAC->outputFrame->data, encodeAAC->outputFrame->nb_samples, indata, encodeAAC->outputFrame->nb_samples); #endif 3、以上代码便可以实现音频重采样,这样便可以再利用FFMPEG AAC编码器完成编码.以上简述了android 收集音频PCM数据及AAC编码、AAC编码触及的相关初始化、FFmpeg AAC编码器的重采样示例.android camera收集、H264编码与Rtmp推流与本文描写了音视频收集、编码进程及若何完成推流,相关文章待续...... 代码地址:github.com/javandcpp/M… 原文链接:https://juejin.cn/post/6844903516838871047 |
0.引言本篇文章主要讲解RTSP推流实战,整体推流流程与RTMP推流流程类似。如果对于RTSP
随着视频号直播的不断完善,美颜、抽奖、推流直播等功能纷纷上线,【附近的直播和人】
很多人直播间没有人,就是因为搞不懂抖音直播间推流机制。不明白推流机制就找不到正确
思路:opencv读取视频 — 将视频分割为帧 — 将每一帧进行需求加工后 — 将此帧写入pi
500强直播策划,策划过多起直播。经常会遇到需要全平台推流的情况,但是OBS的原生软件
#头条文章养成计划#根据我400多天创作的亲身体验,文章推荐的时间比较长,而微头条的
#头条创作挑战赛#头条推流的机制是怎样的?在头条写作了31天,小编一直被这个问题困扰
Open Broadcaster Software(简称 OBS)是一款好用的第三方开源程序直播流媒体内容制
写在前面本文将介绍以下内容:什么是推流?将介绍推流常见的协议RTMP,HLS等。怎么用f
头条直播换到西瓜后台啦! 推流还是一样方便快捷! 功能更多更
最近有很多同学问我视频号推流直播怎么做,这种三两句话回答不清楚,今天特意写了详
推流:将直播内容推送至服务器的过程拉流:为服务器已有直播内容,用指定地址进行拉取
我是依伊,一个全职写作的创作人,点击右上角关注,为你分享【新媒体写作变现】和【个
用OBS作为电脑直播推流,是很多人使用的一款开源软件。我们使用OBS时,它默认的是只能
你是否也遇到过辛辛苦苦拍了视频,但结果却还不如跳舞的小姐姐?为什么别人随便拍的视
专业直播操盘手必须掌握的OBS推流直播技能私域直播母東東,业绩增长分分钟Hello,各位
想在直播间中直播游戏,那就需要用到2个东西,分别是推流码和obs软件。那抖音直播推流
缘起: 最近工作比较忙、文章也没怎么更新,不过最近 一段时间过来问我视频号问题的朋
随着近几年互联网技术高速发展,人们对社交形式多样化的需求不断增加,从一开始的文字
直播目前处于一个风口期,很多直播开始跨平台跨地域直播,如何实现异地直播,跨平台直
声明:本站内容由网友分享或转载自互联网公开发布的内容,如有侵权请反馈到邮箱 1415941@qq.com,我们会在3个工作日内删除,加急删除请添加站长微信:15314649589
Copyright @ 2022-2044 杭州共生网络 www.gongshengyun.cn Powered by Discuz!