首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Android平台RTMP直播推送模块技术接入说明

Android平台RTMP直播推送模块技术接入说明

原创
作者头像
音视频牛哥
发布于 2024-08-07 07:23:44
发布于 2024-08-07 07:23:44
4550
举报

​技术背景

大牛直播SDK跨平台RTMP直播推送模块,始于2015年,支持WindowsLinux(x64_64架构|aarch64)、AndroidiOS平台,支持采集推送摄像头、屏幕、麦克风、扬声器、编码前、编码后数据对接,功能强大,性能优异,配合大牛直播SDK的SmartPlayer播放器,轻松实现毫秒级的延迟体验,满足大多数行业的使用场景。

RTMP直播推送模块数据源,支持编码前、编码后数据对接:

  • 编码前数据(目前支持的有YV12/NV21/NV12/I420/RGB24/RGBA32/RGB565等数据类型);
  • 编码后数据(如无人机等264/HEVC数据,或者本地解析的MP4音视频数据)。

技术对接

系统要求

  • SDK支持Android5.1及以上版本;
  • 支持的CPU架构:armv7, arm64, x86, x86_64。

准备工作

  • 确保SmartPublisherJniV2.java放到com.daniulive.smartpublisher包名下(可在其他包名下调用);
  • smartavengine.jar加入到工程;
  • 拷贝libSmartPublisher.so到工程;
  • AndroidManifast.xml添加相关权限:
代码语言:java
AI代码解释
复制
<uses-permission android:name="android.permission.CAMERA"/>
<uses-feature android:name="android.hardware.camera.autofocus" />
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
<uses-permission android:name="android.permission.VIBRATE" />

  • Load相关so:
代码语言:java
AI代码解释
复制
static {  
    System.loadLibrary("SmartPublisher");
}

  • build.gradle配置32/64位库:
代码语言:java
AI代码解释
复制
splits {
    abi {
        enable true
        reset()
        // Specifies a list of ABIs that Gradle should create APKs for
        include 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' //select ABIs to build APKs for
        // Specify that we do not want to also generate a universal APK that includes all ABIs
        universalApk true
    }
}

  • 如需集成到自己系统测试,请用大牛直播SDK的app name,授权版按照授权app name正常使用即可;
  • 如何改app-name,strings.xml做以下修改:
代码语言:java
AI代码解释
复制
<string name="app_name">SmartPublisherSDKDemo</string>

接口设计

Android 推送端SDK接口详解

调用描述

接口

接口描述

最先调用,如成功返回推送实例

SmartPublisherOpen

ctx:上下文信息; Audio_opt: 0:不推送音频; 1:推送编码前音频(PCM); 2:对接外部编码后的audio数据(AAC/PCMA/PCMU/SPEEX) video_opt: 0:不推送视频; 1:推送编码前视频(YUV420SP/YUV420P/RGBA/ARGB); 2:推送编码后视频(H.264) 3:层叠加模式 width|height:宽高信息。

Event回调

SetSmartPublisherEventCallbackV2

设置event callback

硬编码设置

SetSmartPublisherVideoHWEncoder

检测是否支持H.264硬编码,如果返回0,则支持,否则自动采用软编码

SetSmartPublisherVideoHevcHWEncoder

检测是否支持H.265(HEVC)硬编码,如果返回0,则支持,否则自动采用软编码

SetNativeMediaNDK

设置视频硬编码是否使用 Native Media NDK, 默认是不使用, 安卓5.0以下设备不支持

SetVideoHWEncoderBitrateMode

设置视频硬编码码率控制模式 hw_bitrate_mode: -1表示使用默认值, 不设置也会使用默认值, 0:CQ, 1:VBR, 2:CBR, 3:CBR_FD

SetVideoHWEncoderComplexity

设置视频硬编码复杂度, 安卓5.0及以上支持

SetVideoHWEncoderQuality

设置视频硬编码质量, 安卓9及以上支持, 仅当硬编码器码率控制模式(BitrateMode)是CQ(constant-quality mode)时才有效

SetAVCHWEncoderProfile

设置H.264硬编码Profile, 安卓7及以上支持

SetAVCHWEncoderLevel

设置H.264硬编码Level, 这个只有在设置了Profile的情况下才有效, 安卓7及以上支持

SetVideoHWEncoderMaxBitrate

设置视频硬编码最大码率, 安卓没有相关文档说明, 所以不建议设置

水印

文字、png水印

PostLayerBitmap

通过层模式设置水印,投递层 Bitmap.Config.ARGB_888图像

视频参数配置

软编码可变码率

SmartPublisherSetSwVBRMode

设置软编码可变码率,可变码率下,相邻帧之间变化不大时码率更低

GOP间隔(关键帧)

SmartPublisherSetGopInterval

设置推送端GOP间隔,一般建议在帧率的1~3倍,如不设置,用底层默认值

软编码码率设置

SmartPublisherSetSWVideoBitRate

设置软编码视频 bit-rate,最大码流一般是平均码流的2倍,如不设置,用底层计算的默认值

帧率

SmartPublisherSetFPS

设置fps,如不设置,用底层默认值

软编码视频Profile

SmartPublisherSetSWVideoEncoderProfile

设置软编码模式下的video encoder profile,默认baseline profile

软编码编码速度

SmartPublisherSetSWVideoEncoderSpeed

设置软编码编码速度,设置范围(1,6),1最快,6最慢,默认是6

视频设置

视频镜像

SmartPublisherSetMirror

镜像模式: 播放端和推送端本地回显方向显示一致(前置摄像头)

视频截图

实时快照

CaptureImage

截图接口, 支持JPEG和PNG两种格式

音频配置

音频编码 类型

SmartPublisherSetAudioCodecType

设置编码类型,默认AAC编码,type设置为2时,启用speex编码(码率更低)

AAC编码码率

SmartPublisherSetAudioBitRate

设置音频编码码率, 当前只对AAC编码有效

SPEEX编码质量

SmartPublisherSetSpeexEncoderQuality

设置speex编码质量,数值越大,质量越高,范围(0,10),默认8

音频处理

噪音抑制

SmartPublisherSetNoiseSuppression

噪音抑制开启后,可去除采集端背景杂音

增益控制

SmartPublisherSetAGC

设置自动增益控制,保持声音稳定

回声消除

SmartPublisherSetEchoCancellation

设置音频回音消除

实时静音

SmartPublisherSetMute

设置实时静音、取消静音

设置输入 音量

SmartPublisherSetInputAudioVolume

设置输入音量,默认是1.0,范围是[0.0, 5.0], 设置成0静音, 1音量不变

RTMP推送模式

SetRtmpPublishingType

设置rtmp publisher类型,0:live,1:record,需服务器支持

Enhanced RTMP设置

DisableEnhancedRTMP

disable enhanced RTMP, SDK默认是开启enhanced RTMP的

RTMP推送URL设置

SmartPublisherSetURL

设置RTMP推送url

编码前实时视频数据

camera数据

SmartPublisherOnCaptureVideoData

对接camera回调的数据

YV12数据

SmartPublisherOnYV12Data

YV12数据接口

NV21数据

SmartPublisherOnNV21Data

NV21数据接口

转换接口

SmartPublisherNV21ToI420Rotate

NV21转换到I420并旋转

YUV(I420)

SmartPublisherOnCaptureVideoI420Data

第三方YUV(I420)接口

RGB24数据

SmartPublisherOnCaptureVideoRGB24Data

RGB24接口

RGBA32数据

SmartPublisherOnCaptureVideoRGBA32Data

RGBA32接口

YUV420888数据

SmartPublisherOnImageYUV420888

YUV420888接口

RGBA数据

SmartPublisherOnCaptureVideoRGBAData

第三方RGBA数据

ABGR垂直翻转数据

SmartPublisherOnCaptureVideoABGRFlip VerticalData

ABGR flip vertical(垂直翻转) 数据(Demo中用于传递屏幕数据)

RGBA8888图像

PostLayerImageRGBA8888ByteBuffer

投递层RGBA8888图像,如果不需要Aplpha通道的话, 请使用RGBX8888接口

RGBX8888图像

PostLayerImageRGBX8888ByteBuffer

投递层RGBX8888图像

I420图像

PostLayerImageI420ByteBuffer

投递层I420图像

RGB565数据

SmartPublisherOnCaptureVideoRGB565Data

RGB565 data

裁剪过的RGBA 数据

SmartPublisherOnCaptureVideoClipedRGBAData

投递裁剪过的RGBA数据

PCM数据

SmartPublisherOnPCMData

实时PCM数据

远端PCM数据 (用于回音消除)

SmartPublisherOnFarEndPCMData

实时传递远端PCM数据(可用于互动级的回音消除处理)

音频 混音

混音数据

SmartPublisherOnMixPCMData

传递PCM混音音频数据给SDK, 每10ms音频数据传入一次

编码后数据对接

编码后视频数据

SmartPublisherPostVideoEncodedData

设置编码后视频数据

编码后音频数据

SmartPublisherPostAudioEncodedData

编码后音频数据

编码后音视频数据回调

编码后音频数据回调

SmartPublisherSetAudioEncodedDataCallback

设置编码后音频数据回调

编码后视频数据回调

SmartPublisherSetVideoEncodedDataCallback

设置编码后视频数据回调

层结构设置

启用|停用视频层

EnableLayer

video_opt为3时,启用或者停用视频层, 这个接口必须在StartXXX之后调用.

移除视频层

RemoveLayer

移除视频层, 这个接口必须在StartXXX之后调用.

RTMP推送

开始推送 RTMP

SmartPublisherStartPublisher

启动RTMP推送

停止推送 RTMP

SmartPublisherStopPublisher

停止RTMP推送

关闭推送实例

关闭实例

SmartPublisherClose

关闭推送实例,结束时必须调用close接口释放资源

设置授权

授权license设置

SmartPublisherSetSDKClientKey

设置授权Key,如需设置授权Key, 请确保在SmartPublisherOpen之前调用!

功能支持

  • 音频编码:AAC/SPEEX;
  • 视频编码:H.264、H.265;
  • 推流协议:RTMP;
  • [音视频]支持纯音频/纯视频/音视频推送;
  • [摄像头]支持采集过程中,前后摄像头实时切换;
  • 支持帧率、关键帧间隔(GOP)、码率(bit-rate)设置;
  • 支持RTMP推送 live|record模式设置;
  • 支持前置摄像头镜像设置;
  • 支持软编码、特定机型硬编码;
  • 支持横屏、竖屏推送;
  • 支持Android屏幕采集推送;
  • 支持自建标准RTMP服务器或CDN;
  • 支持断网自动重连、网络状态回调;
  • 支持实时动态水印;
  • 支持实时快照;
  • 支持降噪处理、自动增益控制;
  • 支持外部编码前音视频数据对接;
  • 支持外部编码后音视频数据对接;
  • 支持RTMP扩展H.265(需设备支持H.265特定机型硬编码)和Enhanced RTMP;
  • 支持实时音量调节;
  • 支持扩展录像模块;
  • 支持Unity接口;
  • 支持H.264扩展SEI发送模块;
  • 支持Android 5.1及以上版本。

接口调用详解

本文以大牛直播SDK Android平台Camera2Demo为例,推送RTMP之前,可以先选择视频分辨率、软编还是硬编码,音频是AAC、SPEEX还是PCMA编码等基础设置,其他参数的设置,可以参考下面InitAndSetConfig()。

以Android平台Camera2对接为例,onCreate()时,想new SmartPublisherJniV2():

代码语言:java
AI代码解释
复制
/*
 * MainActivity.java
 * Author: daniusdk.com
 */
@Override
protected void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	setContentView(R.layout.activity_main);
	
	...

	context_ = this.getApplicationContext();
	
	libPublisher = new SmartPublisherJniV2();
}

推送RTMP:

代码语言:java
AI代码解释
复制
class ButtonStartPushListener implements View.OnClickListener {
	public void onClick(View v) {
		if (stream_publisher_.is_rtmp_publishing()) {
			stopPush();

			btnRTMPPusher.setText("推送RTMP");
			return;
		}

		Log.i(TAG, "onClick start push rtmp..");
		
        InitAndSetConfig();

		String rtmp_pusher_url ="rtmp://192.168.0.101:1935/hls/stream123";;


		if (!stream_publisher_.SetURL(rtmp_pusher_url))
			Log.e(TAG, "Failed to set publish stream URL..");

		boolean start_ret = stream_publisher_.StartPublisher();
		if (!start_ret) {
			stream_publisher_.try_release();
			Log.e(TAG, "Failed to start push stream..");
			return;
		}

		startAudioRecorder();
		startLayerPostThread();

		btnRTMPPusher.setText("停止推送 ");

	}
}

stopPush()实现如下:

代码语言:java
AI代码解释
复制
//停止rtmp推送
private void stopPush() {
	stream_publisher_.StopPublisher();
	stream_publisher_.try_release();

	if (!stream_publisher_.is_publishing())
		stopAudioRecorder();
}

其中,InitAndSetConfig()实现如下,通过调SmartPublisherOpen()接口,生成推送实例句柄。

代码语言:java
AI代码解释
复制
/*
 * MainActivity.java
 * Author: daniusdk.com
 */
private void InitAndSetConfig() {
	if (null == libPublisher)
		return;

	if (!stream_publisher_.empty())
		return;

	Log.i(TAG, "InitAndSetConfig video width: " + video_width_ + ", height" + video_height_ + " imageRotationDegree:" + cameraImageRotationDegree_);

	int audio_opt = 1;
	long handle = libPublisher.SmartPublisherOpen(context_, audio_opt, 3,  video_width_, video_height_);
	if (0==handle) {
		Log.e(TAG, "sdk open failed!");
		return;
	}

	Log.i(TAG, "publisherHandle=" + handle);

	int fps = 25;
	int gop = fps * 3;

	initialize_publisher(libPublisher, handle, video_width_, video_height_, fps, gop);

	stream_publisher_.set(libPublisher, handle);
}

对应的initialize_publisher()实现如下,设置软硬编码、帧率、关键帧间隔等。

代码语言:java
AI代码解释
复制
private boolean initialize_publisher(SmartPublisherJniV2 lib_publisher, long handle, int width, int height, int fps, int gop) {
	if (null == lib_publisher) {
		Log.e(TAG, "initialize_publisher lib_publisher is null");
		return false;
	}

	if (0 == handle) {
		Log.e(TAG, "initialize_publisher handle is 0");
		return false;
	}

	if (videoEncodeType == 1) {
		int kbps = LibPublisherWrapper.estimate_video_hardware_kbps(width, height, fps, true);
		Log.i(TAG, "h264HWKbps: " + kbps);
		int isSupportH264HWEncoder = lib_publisher.SetSmartPublisherVideoHWEncoder(handle, kbps);
		if (isSupportH264HWEncoder == 0) {
			lib_publisher.SetNativeMediaNDK(handle, 0);
			lib_publisher.SetVideoHWEncoderBitrateMode(handle, 1); // 0:CQ, 1:VBR, 2:CBR
			lib_publisher.SetVideoHWEncoderQuality(handle, 39);
			lib_publisher.SetAVCHWEncoderProfile(handle, 0x08); // 0x01: Baseline, 0x02: Main, 0x08: High

			// lib_publisher.SetAVCHWEncoderLevel(handle, 0x200); // Level 3.1
			// lib_publisher.SetAVCHWEncoderLevel(handle, 0x400); // Level 3.2
			// lib_publisher.SetAVCHWEncoderLevel(handle, 0x800); // Level 4
			lib_publisher.SetAVCHWEncoderLevel(handle, 0x1000); // Level 4.1 多数情况下,这个够用了
			//lib_publisher.SetAVCHWEncoderLevel(handle, 0x2000); // Level 4.2

			// lib_publisher.SetVideoHWEncoderMaxBitrate(handle, ((long)h264HWKbps)*1300);

			Log.i(TAG, "Great, it supports h.264 hardware encoder!");
		}
	} else if (videoEncodeType == 2) {
		int kbps = LibPublisherWrapper.estimate_video_hardware_kbps(width, height, fps, false);
		Log.i(TAG, "hevcHWKbps: " + kbps);
		int isSupportHevcHWEncoder = lib_publisher.SetSmartPublisherVideoHevcHWEncoder(handle, kbps);
		if (isSupportHevcHWEncoder == 0) {
			lib_publisher.SetNativeMediaNDK(handle, 0);
			lib_publisher.SetVideoHWEncoderBitrateMode(handle, 1); // 0:CQ, 1:VBR, 2:CBR
			lib_publisher.SetVideoHWEncoderQuality(handle, 39);

			// libPublisher.SetVideoHWEncoderMaxBitrate(handle, ((long)hevcHWKbps)*1200);

			Log.i(TAG, "Great, it supports hevc hardware encoder!");
		}
	}

	boolean is_sw_vbr_mode = true;
	//H.264 software encoder
	if (is_sw_vbr_mode) {
		int is_enable_vbr = 1;
		int video_quality = LibPublisherWrapper.estimate_video_software_quality(width, height, true);
		int vbr_max_kbps = LibPublisherWrapper.estimate_video_vbr_max_kbps(width, height, fps);
		lib_publisher.SmartPublisherSetSwVBRMode(handle, is_enable_vbr, video_quality, vbr_max_kbps);
	}

	if (is_pcma_) {
		lib_publisher.SmartPublisherSetAudioCodecType(handle, 3);
	} else {
		lib_publisher.SmartPublisherSetAudioCodecType(handle, 1);
	}

	lib_publisher.SetSmartPublisherEventCallbackV2(handle, new EventHandlerPublisherV2().set(handler_, record_executor_));

	lib_publisher.SmartPublisherSetSWVideoEncoderProfile(handle, 3);

	lib_publisher.SmartPublisherSetSWVideoEncoderSpeed(handle, 2);

	lib_publisher.SmartPublisherSetGopInterval(handle, gop);

	lib_publisher.SmartPublisherSetFPS(handle, fps);

	// lib_publisher.SmartPublisherSetSWVideoBitRate(handle, 600, 1200);

	boolean is_noise_suppression = true;
	lib_publisher.SmartPublisherSetNoiseSuppression(handle, is_noise_suppression ? 1 : 0);

	boolean is_agc = false;
	lib_publisher.SmartPublisherSetAGC(handle, is_agc ? 1 : 0);

	int echo_cancel_delay = 0;
	lib_publisher.SmartPublisherSetEchoCancellation(handle, 1, echo_cancel_delay);

	return true;
}

数据投递如下(以Camera2采集为例,如果是其他视频格式,也可以正常对接):

代码语言:java
AI代码解释
复制
@Override
public void onCameraImageData(Image image) {
	....
	for (LibPublisherWrapper i : publisher_array_)
		i.PostLayerImageYUV420888ByteBuffer(0, 0, 0,
			planes[0].getBuffer(), y_offset, planes[0].getRowStride(),
			planes[1].getBuffer(), u_offset, planes[1].getRowStride(),
			planes[2].getBuffer(), v_offset, planes[2].getRowStride(), planes[1].getPixelStride(),
			w, h, 0, 0,
			scale_w, scale_h, scale_filter_mode, rotation_degree);

}

音频采集投递设计如下:

代码语言:java
AI代码解释
复制
void startAudioRecorder() {
	if (audio_recorder_ != null)
		return;

	audio_recorder_ = new NTAudioRecordV2(this);

	Log.i(TAG, "startAudioRecorder call audio_recorder_.start()+++...");

	audio_recorder_callback_ = new NTAudioRecordV2CallbackImpl(stream_publisher_, null);

	audio_recorder_.AddCallback(audio_recorder_callback_);

	if (!audio_recorder_.Start(is_pcma_ ? 8000 : 44100, 1) ) {
		audio_recorder_.RemoveCallback(audio_recorder_callback_);
		audio_recorder_callback_ = null;

		audio_recorder_ = null;

		Log.e(TAG, "startAudioRecorder start failed.");
	}
	else {
		Log.i(TAG, "startAudioRecorder call audio_recorder_.start() OK---...");
	}
}

void stopAudioRecorder() {
	if (null == audio_recorder_)
		return;

	Log.i(TAG, "stopAudioRecorder+++");

	audio_recorder_.Stop();

	if (audio_recorder_callback_ != null) {
		audio_recorder_.RemoveCallback(audio_recorder_callback_);
		audio_recorder_callback_ = null;
	}

	audio_recorder_ = null;

	Log.i(TAG, "stopAudioRecorder---");
}

回调Audio数据的地方,直接投递出去:

代码语言:java
AI代码解释
复制
private static class NTAudioRecordV2CallbackImpl implements NTAudioRecordV2Callback {
	private WeakReference<LibPublisherWrapper> publisher_0_;

	public NTAudioRecordV2CallbackImpl(LibPublisherWrapper publisher_0) {
		if (publisher_0 != null)
			publisher_0_ = new WeakReference<>(publisher_0);
	}j

	private final LibPublisherWrapper get_publisher_0() {
		if (publisher_0_ !=null)
			return publisher_0_.get();

		return null;
	}

	@Override
	public void onNTAudioRecordV2Frame(ByteBuffer data, int size, int sampleRate, int channel, int per_channel_sample_number) {

		LibPublisherWrapper publisher_0 = get_publisher_0();
		if (publisher_0 != null)
			publisher_0.OnPCMData(data, size, sampleRate, channel, per_channel_sample_number);
	}
}

图层投递设计如下,图层投递的时候,可设置是否添加文字、图片动态水印:

代码语言:java
AI代码解释
复制
private void startLayerPostThread() {
	if (layer_post_thread_ != null)
		return;

	layer_post_thread_ = new LayerPostThread(this.context_, publisher_array_);
	layer_post_thread_.start_post();
	update_layer_post_video_size();
	layer_post_thread_.enableText(isHasTextWatermark());
	layer_post_thread_.enablePicture(isHasPictureWatermark());
}

private void update_layer_post_video_size() {
	if (null == layer_post_thread_)
		return;

	int w, h;
	int degree = cameraImageRotationDegree_;
	if (degree < 0 ) {
		w = 0;
		h = 0;
	} else if (90 == degree || 270 == degree) {
		w = video_height_;
		h = video_width_;
	}else {
		w = video_width_;
		h = video_height_;
	}

	layer_post_thread_.update_video_size(w, h);
}

private void stopLayerPostThread() {
	if (layer_post_thread_ != null) {
		layer_post_thread_.stop_post();
		layer_post_thread_ = null;
	}
}

如需摄像头快照,调用以下逻辑实现即可:

代码语言:java
AI代码解释
复制
class ButtonCaptureImageListener implements View.OnClickListener {
	public void onClick(View v) {
		if (null == snap_shot_impl_) {
			snap_shot_impl_ = new SnapShotImpl(image_path_, context_, handler_, libPublisher, snap_shot_publisher_);
			snap_shot_impl_.start();
		}

		startLayerPostThread();
		snap_shot_impl_.set_layer_post_thread(layer_post_thread_);

		snap_shot_impl_.capture();
	}
}

如需集成录像模块,开始录像、停止录像设计如下:

代码语言:java
AI代码解释
复制
class ButtonStartRecorderListener implements View.OnClickListener {
	public void onClick(View v) {
		if (layer_post_thread_ != null)
			layer_post_thread_.update_layers();

		if (stream_publisher_.is_recording()) {
			stopRecorder();

			if (stream_publisher_.empty())
				ConfigControlEnable(true);

			btnStartRecorder.setText("实时录像");
			btnPauseRecorder.setText("暂停录像");
			btnPauseRecorder.setEnabled(false);
			isPauseRecording = true;
			return;
		}

		Log.i(TAG, "onClick start recorder..");

		InitAndSetConfig();

		ConfigRecorderParam();

		boolean start_ret = stream_publisher_.StartRecorder();
		if (!start_ret) {
			stream_publisher_.try_release();
			Log.e(TAG, "Failed to start recorder.");
			return;
		}

		startAudioRecorder();
		ConfigControlEnable(false);

		startLayerPostThread();

		btnStartRecorder.setText("停止录像");
		btnPauseRecorder.setEnabled(true);
		isPauseRecording = true;
	}
}

录像参数配置实现如下:

代码语言:java
AI代码解释
复制
void ConfigRecorderParam() {
	if (null == libPublisher)
		return;

	if (null == recDir || recDir.isEmpty())
		return;

	int ret = libPublisher.SmartPublisherCreateFileDirectory(recDir);
	if (ret != 0) {
		Log.e(TAG, "Create record dir failed, path:" + recDir);
		return;
	}

	if (!stream_publisher_.SetRecorderDirectory(recDir)) {
		Log.e(TAG, "Set record dir failed , path:" + recDir);
		return;
	}

	// 更细粒度控制录像的, 一般情况无需调用
	//libPublisher.SmartPublisherSetRecorderAudio(publisherHandle, 0);
	//libPublisher.SmartPublisherSetRecorderVideo(publisherHandle, 0);

	if (!stream_publisher_.SetRecorderFileMaxSize(200)) {
		Log.e(TAG, "SmartPublisherSetRecorderFileMaxSize failed.");
		return;
	}
}

暂停录像、恢复录像设计如下:

代码语言:java
AI代码解释
复制
class ButtonPauseRecorderListener implements View.OnClickListener {
	public void onClick(View v) {
		if (stream_publisher_.is_recording()) {
			if (isPauseRecording) {
				boolean ret = stream_publisher_.PauseRecorder(true);
				if (ret) {
					isPauseRecording = false;
					btnPauseRecorder.setText("恢复录像");
				} else {
					Log.e(TAG, "Pause recorder failed..");
				}
			} else {
				boolean ret = stream_publisher_.PauseRecorder(false);
				if (ret) {
					isPauseRecording = true;
					btnPauseRecorder.setText("暂停录像");
				} else {
					Log.e(TAG, "Resume recorder failed..");
				}
			}
		}
	}
}

Event回调实现如下:

代码语言:java
AI代码解释
复制
private static class EventHandlerPublisherV2 implements NTSmartEventCallbackV2 {
	@Override
	public void onNTSmartEventCallbackV2(long handle, int id, long param1, long param2, String param3, String param4, Object param5) {

		Log.i(TAG, "EventHandeV2: handle=" + handle + " id:" + id);

		String publisher_event = "";

		switch (id) {
			case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_STARTED:
				publisher_event = "开始..";
				break;
			case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_CONNECTING:
				publisher_event = "连接中..";
				break;
			case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_CONNECTION_FAILED:
				publisher_event = "连接失败..";
				break;
			case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_CONNECTED:
				publisher_event = "连接成功..";
				break;
			case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_DISCONNECTED:
				publisher_event = "连接断开..";
				break;
			case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_STOP:
				publisher_event = "关闭..";
				break;
			case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_RECORDER_START_NEW_FILE:
				publisher_event = "开始一个新的录像文件 : " + param3;
				break;
			case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_ONE_RECORDER_FILE_FINISHED:
				if (record_executor_ != null) {
					RecordExecutorService executor = record_executor_.get();
					if (executor != null) {
						RecordFileFinishedHandler file_finished_handler = new RecordFileFinishedHandler().set(handle, param3, param1);
						if (param2 > 0)
							file_finished_handler.set_begin_time(param2);

						executor.execute(file_finished_handler);
					}
				}
				publisher_event = "已生成一个录像文件 : " + param3;
				break;

			case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_SEND_DELAY:
				publisher_event = "发送时延: " + param1 + " 帧数:" + param2;
				break;

			case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_CAPTURE_IMAGE:
				publisher_event = "快照: " + param1 + " 路径:" + param3;
				if (0 == param1)
					publisher_event = publisher_event + "截取快照成功.." + ", 用户数据:" + param4;
				 else
					publisher_event = publisher_event + "截取快照失败..";

				break;
			case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_RTSP_URL:
				publisher_event = "RTSP服务URL: " + param3;
				break;
			case NTSmartEventID.EVENT_DANIULIVE_ERC_PUSH_RTSP_SERVER_RESPONSE_STATUS_CODE:
				publisher_event ="RTSP status code received, codeID: " + param1 + ", RTSP URL: " + param3;
				break;
			case NTSmartEventID.EVENT_DANIULIVE_ERC_PUSH_RTSP_SERVER_NOT_SUPPORT:
				publisher_event ="服务器不支持RTSP推送, 推送的RTSP URL: " + param3;
				break;
		}

		String str = "当前回调状态:" + publisher_event;

		Log.i(TAG, str);

		if (handler_ != null) {
			android.os.Handler handler = handler_.get();
			if (handler != null) {
				Message message = new Message();
				message.what = PUBLISHER_EVENT_MSG;
				message.obj = publisher_event;
				handler.sendMessage(message);
			}
		}
	}

	public NTSmartEventCallbackV2 set(android.os.Handler handler, RecordExecutorService record_executor) {
		this.handler_ = new WeakReference<>(handler);
		this.record_executor_ = new WeakReference<>(record_executor);
		return this;
	}

	private WeakReference<android.os.Handler> handler_;
	private WeakReference<RecordExecutorService> record_executor_;
}

onDestroy() 的时候,调用stopPush()即可,如果有录像和快照,都停掉,此外,停掉图层投递线程,并关闭camera:

代码语言:java
AI代码解释
复制
@Override
protected void onDestroy() {
	Log.i(TAG, "activity destory!");

	record_executor_.cancel_tasks();

	stopAudioRecorder();

	if (snap_shot_impl_ != null) {
		snap_shot_impl_.stop();
		snap_shot_impl_ = null;
	}

	snap_shot_publisher_.release();

	stopPush();
	stopRecorder();

	stream_publisher_.release();

	stopLayerPostThread();

	if (camera2Helper != null) {
		camera2Helper.release();
	}

	if (!record_executor_.shutdown(60, TimeUnit.SECONDS))
		Log.w(TAG, "call record_executor_.shutdown failed");

	super.onDestroy();
}

总结

以上是大牛直播SDK的Android平台RTMP直播推送模块详细的对接说明,除了可以对接编码前各种类型的音视频数据外,模块还支持对接编码后音视频数据,并实现本地录像、快照等功能,除支持H.264外,RTMP推送模块还支持扩展H.265和Enhanced RTMP。感兴趣的开发者,可以单独跟我们探讨。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Android平台轻量级RTSP服务模块技术接入说明
为满足内网无纸化/电子教室等内网超低延迟需求,避免让用户配置单独的服务器,大牛直播SDK在推送端发布了轻量级RTSP服务SDK。
音视频牛哥
2024/07/17
2470
Android平台轻量级RTSP服务模块技术接入说明
拉取RTSP流后的几个去向探讨(播放|转RTMP|轻量级RTSP服务|本地录制|GB28181)
写了很多关于RTSP播放和转发的blog了,今天我们做个简单的汇总,以大牛直播SDK的Android平台为例,拉取到RTSP流,除了本地播放,还有几个流向:
音视频牛哥
2024/01/31
3570
拉取RTSP流后的几个去向探讨(播放|转RTMP|轻量级RTSP服务|本地录制|GB28181)
Android平台RTSP流如何添加动态水印后转推RTMP或轻量级RTSP服务
我们在对接外部开发者的时候,遇到这样的技术诉求,客户用于地下管道检测场景,需要把摄像头的数据拉取过来,然后叠加上实时位置、施工单位、施工人员等信息,然后对外输出新的RTSP流,并本地录制一份带动态水印叠加后的数据。整个过程,因为摄像头位置一直在变化,所以需要整体尽可能的低延迟,达到可操控摄像头的目的。
音视频牛哥
2023/12/25
4680
Android平台RTSP流如何添加动态水印后转推RTMP或轻量级RTSP服务
Android平台不需要单独部署流媒体服务如何实现内网环境下一对一音视频互动
我们在做内网环境的一对一音视频互动的时候,遇到这样的技术诉求:如智能硬件场景下(比如操控智能硬件),纯内网环境,如何不要单独部署RTMP或类似流媒体服务,实现一对一音视频互动。
音视频牛哥
2023/05/17
3190
Android平台RTSP转RTMP推送之采集麦克风音频转发
RTSP转RTMP推送,好多开发者第一想到的是采用ffmpeg命令行的形式,如果对ffmpeg比较熟,而且产品不要额外的定制和更高阶的要求,未尝不可,如果对产品稳定性、时延、断网重连等有更高的技术诉求,比较好的办法,还是采用我们的技术实现。
音视频牛哥
2024/10/30
2090
Android平台RTSP转RTMP推送之采集麦克风音频转发
Android平台实现无纸化同屏并推送RTMP或轻量级RTSP服务(毫秒级延迟)
在写这篇文章之前,实际上几年之前,我们就有非常稳定的无纸化同屏的模块,本文借demo更新,算是做个新的总结,废话不多说,先看图,本文以Android平台屏幕实时采集推送,Windows播放为例,和大家做个技术分享。
音视频牛哥
2024/03/10
5030
Android平台实现无纸化同屏并推送RTMP或轻量级RTSP服务(毫秒级延迟)
Android平台摄像头麦克风视音频采集录像之MediaRecorder还是SmartPublisher
在 Android 中录制摄像头采集的数据到 MP4 文件,我们可以用系统自带的MediaRecorder,也可以用第三方成熟的摄像头采集录制库,本文就两种方案,做个大概的梳理。
音视频牛哥
2024/11/21
3230
Android平台摄像头麦克风视音频采集录像之MediaRecorder还是SmartPublisher
Android平台通过RTSP服务实现摄像头麦克风共享
前些年,我们在完成Android平台RTMP直播推送模块后,遇到这样的技术需求,好多开发者希望在Android平台,实现摄像头和麦克风音视频数据采集编码打包后,对外提供RTSP(Real Time Streaming Protocol)服务。
音视频牛哥
2024/09/05
3150
Android平台通过RTSP服务实现摄像头麦克风共享
Android平台GB28181设备接入端实现实时快照
Android平台GB28181设计开发的时候,有个功能必不可少的:实时快照,特别是用于执法记录仪等场景下,用于图像留底或分析等考量。
音视频牛哥
2022/10/04
3120
Android平台RTMP多实例推送的几种情况探讨
1. 多路编码,多个实例分别推送到不同的RTMP URL(如Android采集板卡同时接2路出去);
音视频牛哥
2020/06/01
6420
GB28181设备接入侧录像查询和录像下载技术探究之实时录像
我们在对接GB28181设备接入侧的时候,除了常规实时音视频按需上传外,还有个重要的功能,就是本地实时录像,录像后的数据,在执法记录仪等前端设备留底,然后,到工作站拷贝到专门的平台。
音视频牛哥
2023/07/16
7360
GB28181设备接入侧录像查询和录像下载技术探究之实时录像
Android平台RTMP推送|轻量级RTSP服务|GB28181设备接入模块之实时快照保存JPG还是PNG?
JPG和PNG是两种常见的图片文件格式,在压缩方式、图像质量、透明效果和可编辑性等方面存在显著差异。
音视频牛哥
2024/01/14
2460
Android平台RTMP推送|轻量级RTSP服务|GB28181设备接入模块之实时快照保存JPG还是PNG?
Android平台实现内网无纸化会议|智慧教室|实时同屏功能
1. 组网:无线组网,需要好的AP模块才能撑得住大的并发流量,推送端到AP,最好是有线网链接;
音视频牛哥
2021/05/12
1.8K0
[技术分享]Android平台实时音视频录像模块设计之道
录像有什么难的?无非就是数据过来,编码保存mp4而已,这可能是好多开发者在做录像模块的时候的思考输出。是的,确实不难,但是做好,或者和其他模块有非常好的逻辑配合,确实不容易。
音视频牛哥
2023/05/23
6850
[技术分享]Android平台实时音视频录像模块设计之道
Android平台实现屏幕录制(屏幕投影)|音频播放采集|麦克风采集并推送RTMP或轻量级RTSP服务
好多开发者,希望我们能系统的介绍下无纸化同屏的原理和集成步骤,以Android平台为例,无纸化同屏将Android设备上的屏幕内容实时投射到另一个显示设备(如Windows终端、国产化操作系统或另一台Android设备)上,从而实现多屏互动和内容的无缝共享。
音视频牛哥
2024/08/16
2990
Android平台实现屏幕录制(屏幕投影)|音频播放采集|麦克风采集并推送RTMP或轻量级RTSP服务
Android平台轻量级RTSP服务模块二次封装版调用说明
在前面的blog,我们发布了Android平台轻量级RTSP服务模块的技术对接说明,好多开发者希望,更黑盒的对接轻量级RTSP服务这块,专注于自身业务逻辑。为此,我们针对Android平台轻量级RTSP服务模块,做了更进一步的封装(LibPublisherWrapper.java)。
音视频牛哥
2024/07/25
1780
Android平台轻量级RTSP服务模块二次封装版调用说明
Android平台RTSP|RTMP直播播放器技术接入说明
本文详细介绍了在 Android 平台上集成 RTSP 和 RTMP 直播播放模块的技术背景、系统要求、准备工作、接口设计、功能支持以及接口调用流程。通过合理的架构设计和优化,开发者可以高效地实现直播播放功能,满足不同场景下的应用需求。
音视频牛哥
2025/03/02
3641
Android平台RTSP|RTMP直播播放器技术接入说明
Android平台RTSP|RTMP直播播放器技术接入说明
大牛直播SDK自2015年发布RTSP、RTMP直播播放模块,迭代从未停止,SmartPlayer功能强大、性能强劲、高稳定、超低延迟、超低资源占用。无需赘述,全自研内核,行业内一致认可的跨平台RTSP、RTMP直播播放器。本文以Android平台为例,介绍下如何集成RTSP、RTMP播放模块。
音视频牛哥
2024/07/24
5560
Android平台RTSP|RTMP直播播放器技术接入说明
Android平台实现Unity3D下RTMP推送
像Unity3D下的RTMP或RTSP播放器一样,好多开发者苦于在Unity环境下,如何高效率低延迟的把数据采集并编码实时推送到流媒体服务器,实现Unity场景下的低延迟推拉流方案。
音视频牛哥
2021/06/08
1K0
Android平台实现Unity3D下RTMP推送
Android平台实现VR头显Unity下音视频数据RTMP推送
随着技术发展的日新月异,虚拟现实产业已经从过去的探索期,自2020年起,慢慢过渡到高速发展期,随着5G时代的到来,大带宽高可靠低延迟网络环境,为虚拟现实产业提供了很好的网络保障,虚拟现实在越来越多的场景下有了应用价值,典型场景如工业互联网、虚拟仿真、文旅文博、智慧交通、智慧能源、智慧医疗、智慧校园、智慧农业等。同事,行业也对清晰度、流畅性和交互感也提出了更高的要求。本文从Android平台的采集推送为例,介绍下基于头显或类似终端的低延迟解决方案。
音视频牛哥
2022/04/28
9080
推荐阅读
Android平台轻量级RTSP服务模块技术接入说明
2470
拉取RTSP流后的几个去向探讨(播放|转RTMP|轻量级RTSP服务|本地录制|GB28181)
3570
Android平台RTSP流如何添加动态水印后转推RTMP或轻量级RTSP服务
4680
Android平台不需要单独部署流媒体服务如何实现内网环境下一对一音视频互动
3190
Android平台RTSP转RTMP推送之采集麦克风音频转发
2090
Android平台实现无纸化同屏并推送RTMP或轻量级RTSP服务(毫秒级延迟)
5030
Android平台摄像头麦克风视音频采集录像之MediaRecorder还是SmartPublisher
3230
Android平台通过RTSP服务实现摄像头麦克风共享
3150
Android平台GB28181设备接入端实现实时快照
3120
Android平台RTMP多实例推送的几种情况探讨
6420
GB28181设备接入侧录像查询和录像下载技术探究之实时录像
7360
Android平台RTMP推送|轻量级RTSP服务|GB28181设备接入模块之实时快照保存JPG还是PNG?
2460
Android平台实现内网无纸化会议|智慧教室|实时同屏功能
1.8K0
[技术分享]Android平台实时音视频录像模块设计之道
6850
Android平台实现屏幕录制(屏幕投影)|音频播放采集|麦克风采集并推送RTMP或轻量级RTSP服务
2990
Android平台轻量级RTSP服务模块二次封装版调用说明
1780
Android平台RTSP|RTMP直播播放器技术接入说明
3641
Android平台RTSP|RTMP直播播放器技术接入说明
5560
Android平台实现Unity3D下RTMP推送
1K0
Android平台实现VR头显Unity下音视频数据RTMP推送
9080
相关推荐
Android平台轻量级RTSP服务模块技术接入说明
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档