Android NDK 直播推流与引流,2024年最新androidstudio创建项目教程

x264_picture_alloc(&pic_in, param.i_csp, param.i_width, param.i_height);

pic_in.i_pts = 0;

//打开编码器

video_encode_handle = x264_encoder_open(&param);

if (video_encode_handle) {

LOGI(“打开视频编码器成功”);

} else {

throwNativeError(env, INIT_FAILED);

}

}

faac 初始化:

JNIEXPORT void JNICALL

Java_com_byteflow_live_jni_NativePush_setAudioOptions(JNIEnv *env, jobject instance,

jint sampleRateInHz, jint channel) {

audio_encode_handle = faacEncOpen(sampleRateInHz, channel, &nInputSamples,

&nMaxOutputBytes);

if (!audio_encode_handle) {

LOGE(“音频编码器打开失败”);

return;

}

//设置音频编码参数

faacEncConfigurationPtr p_config = faacEncGetCurrentConfiguration(audio_encode_handle);

p_config->mpegVersion = MPEG4;

p_config->allowMidside = 1;

p_config->aacObjectType = LOW;

p_config->outputFormat = 0; //输出是否包含ADTS头

p_config->useTns = 1; //时域噪音控制,大概就是消爆音

p_config->useLfe = 0;

// p_config->inputFormat = FAAC_INPUT_16BIT;

p_config->quantqual = 100;

p_config->bandWidth = 0; //频宽

p_config->shortctl = SHORTCTL_NORMAL;

if (!faacEncSetConfiguration(audio_encode_handle, p_config)) {

LOGE(“%s”, “音频编码器配置失败…”);

throwNativeError(env, INIT_FAILED);

return;

}

LOGI(“%s”, “音频编码器配置成功”);

}

对视频数据进行编码打包,通过 add_rtmp_packet 放入链表:

JNIEXPORT void JNICALL

Java_com_byteflow_live_jni_NativePush_fireVideo(JNIEnv *env, jobject instance, jbyteArray buffer_) {

//视频数据转为YUV420P

//NV21->YUV420P

jbyte *nv21_buffer = (*env)->GetByteArrayElements(env, buffer_, NULL);

jbyte *u = pic_in.img.plane[1];

jbyte *v = pic_in.img.plane[2];

//nv21 4:2:0 Formats, 12 Bits per Pixel

//nv21与yuv420p,y个数一致,uv位置对调

//nv21转yuv420p y = wh,u/v=wh/4

//nv21 = yvu yuv420p=yuv y=y u=y+1+1 v=y+1

//如果要进行图像处理(美颜),可以再转换为RGB

//还可以结合OpenCV识别人脸等等

memcpy(pic_in.img.plane[0], nv21_buffer, y_len);

int i;

for (i = 0; i < u_len; i++) {

*(u + i) = *(nv21_buffer + y_len + i * 2 + 1);

*(v + i) = *(nv21_buffer + y_len + i * 2);

}

//h264编码得到NALU数组

x264_nal_t *nal = NULL; //NAL

int n_nal = -1; //NALU的个数

//进行h264编码

if (x264_encoder_encode(video_encode_handle, &nal, &n_nal, &pic_in, &pic_out) < 0) {

LOGE(“%s”, “编码失败”);

return;

}

//使用rtmp协议将h264编码的视频数据发送给流媒体服务器

//帧分为关键帧和普通帧,为了提高画面的纠错率,关键帧应包含SPS和PPS数据

int sps_len, pps_len;

unsigned char sps[100];

unsigned char pps[100];

memset(sps, 0, 100);

memset(pps, 0, 100);

pic_in.i_pts += 1; //顺序累加

//遍历NALU数组,根据NALU的类型判断

for (i = 0; i < n_nal; i++) {

if (nal[i].i_type == NAL_SPS) {

//复制SPS数据,序列参数集(Sequence parameter set)

sps_len = nal[i].i_payload - 4;

memcpy(sps, nal[i].p_payload + 4, sps_len); //不复制四字节起始码

} else if (nal[i].i_type == NAL_PPS) {

//复制PPS数据,图像参数集(Picture parameter set)

pps_len = nal[i].i_payload - 4;

memcpy(pps, nal[i].p_payload + 4, pps_len); //不复制四字节起始码

//发送序列信息

//h264关键帧会包含SPS和PPS数据

add_264_sequence_header(pps, sps, pps_len, sps_len);

} else {

//发送帧信息

add_264_body(nal[i].p_payload, nal[i].i_payload);

}

}

(*env)->ReleaseByteArrayElements(env, buffer_, nv21_buffer, 0);

}

同样,对音频数据进行编码打包放入链表:

JNIEXPORT void JNICALL

Java_com_byteflow_live_jni_NativePush_fireAudio(JNIEnv *env, jobject instance, jbyteArray buffer_,

jint length) {

int *pcmbuf;

unsigned char *bitbuf;

jbyte *b_buffer = (*env)->GetByteArrayElements(env, buffer_, 0);

pcmbuf = (short *) malloc(nInputSamples * sizeof(int));

bitbuf = (unsigned char *) malloc(nMaxOutputBytes * sizeof(unsigned char));

int nByteCount = 0;

unsigned int nBufferSize = (unsigned int) length / 2;

unsigned short *buf = (unsigned short *) b_buffer;

while (nByteCount < nBufferSize) {

int audioLength = nInputSamples;

if ((nByteCount + nInputSamples) >= nBufferSize) {

audioLength = nBufferSize - nByteCount;

}

int i;

for (i = 0; i < audioLength; i++) {//每次从实时的pcm音频队列中读出量化位数为8的pcm数据。

int s = ((int16_t *) buf + nByteCount)[i];

pcmbuf[i] = s << 8;//用8个二进制位来表示一个采样量化点(模数转换)

}

nByteCount += nInputSamples;

//利用FAAC进行编码,pcmbuf为转换后的pcm流数据,audioLength为调用faacEncOpen时得到的输入采样数,bitbuf为编码后的数据buff,nMaxOutputBytes为调用faacEncOpen时得到的最大输出字节数

int byteslen = faacEncEncode(audio_encode_handle, pcmbuf, audioLength,

bitbuf, nMaxOutputBytes);

if (byteslen < 1) {

continue;

}

add_aac_body(bitbuf, byteslen);//从bitbuf中得到编码后的aac数据流,放到数据队列

}

if (bitbuf)

free(bitbuf);

if (pcmbuf)

free(pcmbuf);

(*env)->ReleaseByteArrayElements(env, buffer_, b_buffer, 0);

}

消费者线程不断从链表中取 RTMPPacket 发送给服务器:

void *push_thread(void *arg) {

JNIEnv *env;//获取当前线程JNIEnv

(*javaVM)->AttachCurrentThread(javaVM, &env, NULL);

//建立RTMP连接

RTMP *rtmp = RTMP_Alloc();

if (!rtmp) {

LOGE(“rtmp初始化失败”);

goto end;

}

RTMP_Init(rtmp);

rtmp->Link.timeout = 5; //连接超时的时间

//设置流媒体地址

RTMP_SetupURL(rtmp, rtmp_path);

//发布rtmp数据流

RTMP_EnableWrite(rtmp);

//建立连接

if (!RTMP_Connect(rtmp, NULL)) {

LOGE(“%s”, “RTMP 连接失败”);

throwNativeError(env, CONNECT_FAILED);

goto end;

}

//计时

start_time = RTMP_GetTime();

if (!RTMP_ConnectStream(rtmp, 0)) { //连接流

LOGE(“%s”, “RTMP ConnectStream failed”);

throwNativeError(env, CONNECT_FAILED);

goto end;

}

is_pushing = TRUE;

//发送AAC头信息

add_aac_sequence_header();

while (is_pushing) {

//发送

pthread_mutex_lock(&mutex);

pthread_cond_wait(&cond, &mutex);

//取出队列中的RTMPPacket

RTMPPacket *packet = queue_get_first();

if (packet) {

queue_delete_first(); //移除

packet->m_nInfoField2 = rtmp->m_stream_id; //RTMP协议,stream_id数据

int i = RTMP_SendPacket(rtmp, packet, TRUE); //TRUE放入librtmp队列中,并不是立即发送

if (!i) {

LOGE(“RTMP 断开”);

RTMPPacket_Free(packet);

pthread_mutex_unlock(&mutex);

goto end;

} else {

LOGI(“%s”, “rtmp send packet”);

}

RTMPPacket_Free(packet);

}

pthread_mutex_unlock(&mutex);

}

end:

LOGI(“%s”, “释放资源”);

free(rtmp_path);

RTMP_Close(rtmp);

RTMP_Free(rtmp);

(*javaVM)->DetachCurrentThread(javaVM);

return 0;

}

3.引流


这里引流就不做展开讲,可以通过 QLive 的 SDK 或者 vitamio 等第三方库实现。

基于 vitamio 实现引流:

private void init(){

mVideoView = (VideoView) findViewById(R.id.live_player_view);

mVideoView.setVideoPath(SPUtils.getInstance(this).getString(SPUtils.KEY_NGINX_SER_URI));

mVideoView.setMediaController(new MediaController(this));

mVideoView.requestFocus();

mVideoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {

@Override

public void onPrepared(MediaPlayer mp) {

mp.setPlaybackSpeed(1.0f);

}

});

}

下面,高能的地方来了!有幸从一位字节跳动大神那里得到他本人吐血整理的“582页Android NDK七大模块学习宝典”,从原理到实战,一应俱全!

秉承好东西的当然要共享的原则,今天就来秀一把,试试这“582页Android NDK七大模块学习宝典”是否也能让你事半功倍!这份宝典主要涉及以下几个方面:

  • NDK 模块开发

  • JNI 模块

  • Native 开发工具

  • Linux 编程

  • 底层图片处理

  • 音视频开发

  • 机器学习

笔记内容全部免费分享,有需要完整版笔记的小伙伴【点击我】免费获取哦!

一、NDK 模块开发

主要内容:

  • C++与 C#数据类型总结

  • C 与 C++之内存结构与管理

  • C 与 C++之预处理命令与用 typedef 命名已有类型

  • C 与 C++之结构体、共用体

  • C 与 C++之指针

  • C/C++ 之多线程机制

  • C/C++ 之函数与初始化列表

二、JNI 模块

主要内容:

  • JNI 开发之 静态注册与动态注册

静态注册、动态注册、JNINativeMethod、数据类型映射、jni 函数默认参数

  • JNI 开发之方法签名与 Java 通信

Android NDK 开发 JNI 类型签名和方法签名、JNI 实现 java 与 c/c++相互通讯

  • JNI 开发之局部引用、全局引用和弱全局引用

三、Native 开发工具

主要内容:

  • 编译器、打包工具与分析器

十大最受欢迎的 React Native 应用开发编辑器、react-native 打包流程

  • 静态库与动态库

  • CPU 架构与注意事项

ABI 管理、处理 CPU 功能、NEON 支持

  • 构建脚本与构建工具

环境搭建、NDK 项目、Cmake、Makefile

  • 交叉编译移植

FFmpeg 编译、FFmpeg+LIBX264+FACC 交叉编译 实现 264 流录制、移植 FFmpeg 在 arm 交叉编译时遇到的问题、FFmpeg 交叉编译、X264 FAAC 交叉编译、解决所有移植问题

  • AS 构建 NDK 项目

配置 NDK 环境、建立 app 项目、生成.h 头文件、创建 C 文件,实现 native 方法、jni.h 文件

四、Linux 编程

  • Linux 环境搭建,系统管理,权限系统和工具使用(vim 等)

Linux 环境的搭建、Linux 系统管理操作(25 个命令)

  • Shell 脚本编程

Shell 脚本、编写简单 Shell 脚本、流程控制语句、计划任务服务程序

五、底层图片处理

  • PNG/JPEG/WEBP 图像处理与压缩

四种图片格式、推荐几种图片处理网站、squoosh 在线无损图片压缩工具,JPG/webP/PNG/ 互转

  • 微信图片压缩

计算原始宽高、计算近似宽高、第一次采样获取目标图片、循环逼近目标大小

  • GIF 合成原理与实现

GIF 图片的解析、GIF 图片的合成(序列图像合成 GIF 图像)

六、音视频开发

  • 多媒体系统

Camera 与手机屏幕采集、图像原始数据格式 YUV420(NV21 与 YV12 等)、音频采集与播放系统、编解码器 MediaCodec、MediaMuxer 复用与 MediaExtractor

  • FFmpeg

ffmpeg 模块介绍、音视频解码,音视频同步、I 帧,B 帧,P 帧解码原理、x264 视频编码与 faac 音频编码、OpenGL 绘制与 NativeWindow 绘制

  • 流媒体协议

RTMP 协议、、音视频通话 P2P WebRtc

  • OpenGL ES 滤镜开发之美颜效果

高斯模糊、高反差保留、强光处理、融合

  • 抖音视频效果分析与实现

流程列表、视频拍摄、视频编辑、视频导出

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
img

总结

找工作是个很辛苦的事情,而且一般周期都比较长,有时候既看个人技术,也看运气。第一次找工作,最后的结果虽然不尽如人意,不过收获远比offer大。接下来就是针对自己的不足,好好努力了。

最后为了节约大家的时间,我把我学习所用的资料和面试遇到的问题和答案都整理成了PDF文档

喜欢文章的话请关注、点赞、转发 谢谢!

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
img
68)]
[外链图片转存中…(img-0UmbtpXi-1712767937569)]
[外链图片转存中…(img-fsxiJQK5-1712767937569)]
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
[外链图片转存中…(img-wVrcAoVY-1712767937569)]

总结

找工作是个很辛苦的事情,而且一般周期都比较长,有时候既看个人技术,也看运气。第一次找工作,最后的结果虽然不尽如人意,不过收获远比offer大。接下来就是针对自己的不足,好好努力了。

最后为了节约大家的时间,我把我学习所用的资料和面试遇到的问题和答案都整理成了PDF文档

喜欢文章的话请关注、点赞、转发 谢谢!

[外链图片转存中…(img-y5fHITxw-1712767937569)]

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
[外链图片转存中…(img-JFqUusgY-1712767937570)]

  • 17
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
使用Android Studio创建NDK项目的步骤如下: 1. 创建项目:在Android Studio中选择“Start a new Android Studio project”。 2. 添加NDK支持:在“Add an Activity to Mobile”页面中,选择“Native C++”作为Activity类型,然后单击“Next”。 3. 配置C++支持:在“Customize C++ Support”页面中,选择“C++11”作为C++标准库,并选择“None”作为异常支持。单击“Finish”。 4. 配置gradle文件:在项目的build.gradle文件中,添加以下代码: ``` externalNativeBuild { cmake { path "CMakeLists.txt" } } ``` 5. 配置CMakeLists.txt文件:在项目的CMakeLists.txt文件中,添加以下代码: ``` cmake_minimum_required(VERSION 3.4.1) add_library( # Sets the name of the library. native-lib # Sets the library as a shared library. SHARED # Provides a relative path to your source file(s). src/main/cpp/native-lib.cpp ) # Searches for a specified prebuilt library and stores the path as a # variable. Because system libraries are included in the search path by # default, you only need to specify the name of the public NDK library # you want to add. CMake verifies that the library exists before # completing its build. find_library( # Sets the name of the path variable. log-lib # Specifies the name of the NDK library that # you want CMake to locate. log ) # Specifies libraries CMake should link to your target library. You # can link multiple libraries, such as libraries you define in the # build script, prebuilt third-party libraries, or system libraries. target_link_libraries( # Specifies the target library. native-lib # Links the target library to the log library # included in the NDK. ${log-lib} ) ``` 6. 编写C++代码:在src/main/cpp目录下创建native-lib.cpp文件,并添加以下代码: ``` #include <jni.h> #include <string> extern "C" JNIEXPORT jstring JNICALL Java_com_example_myapplication_MainActivity_stringFromJNI( JNIEnv* env, jobject /* this */) { std::string hello = "Hello from C++"; return env->NewStringUTF(hello.c_str()); } ``` 7. 在MainActivity.java文件中添加以下代码: ``` static { System.loadLibrary("native-lib"); } public native String stringFromJNI(); ``` 8. 运行应用程序:构建并运行应用程序,您将看到“Hello from C++”消息。 --相关问题--: 1. 如何在Android Studio中使用OpenCV? 2. 如何在NDK中使用CMake? 3.

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值