好多开发者问我们,有没有针对Android平台RTMP直播推送、轻量级RTSP服务模块的进一步封装,可以更便捷的调用大牛直播SDK接口。
为此,我们分享下我们针对Android平台SmartPublisher做的二次封装代码:
package com.daniulive.smartpublisher;
import android.util.Log;
import java.nio.ByteBuffer;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class LibPublisherWrapper {
private static String TAG = "NTLogLibPBW";
private static final int OK = 0;
private final ReadWriteLock rw_lock_ = new ReentrantReadWriteLock(true);
private final java.util.concurrent.locks.Lock write_lock_ = rw_lock_.writeLock();
private final java.util.concurrent.locks.Lock read_lock_ = rw_lock_.readLock();
private SmartPublisherJniV2 lib_publisher_;
private volatile long native_handle_;
private volatile boolean is_rtmp_publishing_;
private volatile boolean is_recording_;
private volatile boolean is_rtsp_publishing_;
private volatile boolean is_gb_stream_publishing_;
private void clear_all_publishing_flags() {
this.is_rtmp_publishing_ = false;
this.is_recording_ = false;
this.is_rtsp_publishing_ = false;
this.is_gb_stream_publishing_ = false;
}
public void set(SmartPublisherJniV2 lib_publisher, long handle) {
if (!empty())
throw new IllegalStateException("it is not empty");
if (handle != 0) {
if (null == lib_publisher)
throw new NullPointerException("lib_publisher is null");
}
write_lock_.lock();
try {
clear_all_publishing_flags();
this.lib_publisher_ = lib_publisher;
this.native_handle_ = handle;
} finally {
write_lock_.unlock();
}
Log.i(TAG, "set native_handle:" + handle);
}
@Override
protected void finalize() throws Throwable {
try {
if (check_native_handle()) {
if(is_rtmp_publishing()) {
lib_publisher_.SmartPublisherStopPublisher(get());
this.is_rtmp_publishing_ = false;
}
if(is_recording()) {
lib_publisher_.SmartPublisherStopRecorder(get());
this.is_recording_ = false;
}
if (is_rtsp_publishing()) {
lib_publisher_.StopRtspStream(get());
this.is_rtsp_publishing_ = false;
}
if (is_gb_stream_publishing()) {
lib_publisher_.StopGB28181MediaStream(get());
this.is_gb_stream_publishing_ = false;
}
lib_publisher_.SmartPublisherClose(this.native_handle_);
Log.i(TAG, "finalize close handle:" + this.native_handle_);
this.native_handle_ = 0;
}
}catch (Exception e) {
}
super.finalize();
}
public void release() {
if (empty())
return;
if(is_rtmp_publishing())
StopPublisher();
if (is_recording())
StopRecorder();
if (is_rtsp_publishing())
StopRtspStream();
if (is_gb_stream_publishing())
StopGB28181MediaStream();
long handle;
write_lock_.lock();
try {
handle = this.native_handle_;
this.native_handle_ = 0;
clear_all_publishing_flags();
} finally {
write_lock_.unlock();
}
if (lib_publisher_ != null && handle != 0)
lib_publisher_.SmartPublisherClose(handle);
}
public boolean try_release() {
if (empty())
return false;
if (is_publishing()) {
Log.i(TAG, "try_release it is publishing, native_handle:" + get());
return false;
}
long handle;
write_lock_.lock();
try {
if (is_publishing())
return false;
handle = this.native_handle_;
this.native_handle_ = 0;
} finally {
write_lock_.unlock();
}
if (lib_publisher_ != null && handle != 0)
lib_publisher_.SmartPublisherClose(handle);
return true;
}
public final boolean empty() { return 0 == this.native_handle_; }
private final long get() { return this.native_handle_; }
private final boolean check_native_handle() {
return this.lib_publisher_ != null && this.native_handle_ != 0;
}
public final boolean is_rtmp_publishing() { return is_rtmp_publishing_; }
public final boolean is_recording() { return is_recording_; }
public final boolean is_rtsp_publishing() { return is_rtsp_publishing_; }
public final boolean is_gb_stream_publishing() { return is_gb_stream_publishing_; }
public final boolean is_publishing() { return is_gb_stream_publishing_ || is_rtmp_publishing_ || is_rtsp_publishing_ || is_recording_; }
public boolean SetMute(boolean is_mute) {
if (!check_native_handle())
return false;
return OK == lib_publisher_.SmartPublisherSetMute(get(), is_mute? 1 : 0);
}
public boolean SetInputAudioVolume(int index, float volume) {
if (!check_native_handle())
return false;
return OK == lib_publisher_.SmartPublisherSetInputAudioVolume(get(), index , volume);
}
public boolean SetMirror(boolean is_mirror) {
if (!check_native_handle())
return false;
return OK == lib_publisher_.SmartPublisherSetMirror(get(), is_mirror?1:0);
}
public boolean SetRecorderDirectory(String path) {
if (!check_native_handle())
return false;
return OK == lib_publisher_.SmartPublisherSetRecorderDirectory(get(), path);
}
public boolean SetRecorderFileMaxSize(int size) {
if (!check_native_handle())
return false;
return OK == lib_publisher_.SmartPublisherSetRecorderFileMaxSize(get(), size);
}
public boolean CaptureImage(int compress_format, int quality, String file_name, String user_data_string) {
if (!check_native_handle())
return false;
return OK == lib_publisher_.CaptureImage(get(), compress_format, quality, file_name, user_data_string);
}
public boolean SetURL(String url) {
if (!check_native_handle())
return false;
return OK == lib_publisher_.SmartPublisherSetURL(get(), url);
}
public boolean StartPublisher() {
if (!check_native_handle())
return false;
if (is_rtmp_publishing()) {
Log.e(TAG, "already publishing rtmp, native_handle:" + get());
return false;
}
int ret = lib_publisher_.SmartPublisherStartPublisher(get());
if (ret != OK) {
Log.e(TAG, "call SmartPublisherStartPublisher failed, native_handle:" + get() + ", ret:" + ret);
return false;
}
write_lock_.lock();
try {
this.is_rtmp_publishing_ = true;
} finally {
write_lock_.unlock();
}
Log.i(TAG, "call SmartPublisherStartPublisher OK, native_handle:" + get());
return true;
}
public boolean StopPublisher() {
if (!check_native_handle())
return false;
if (!is_rtmp_publishing()) {
Log.w(TAG, "it's not publishing rtmp, native_handle:" + get());
return false;
}
boolean is_need_call = false;
write_lock_.lock();
try {
if (this.is_rtmp_publishing_) {
this.is_rtmp_publishing_ = false;
is_need_call = true;
}
} finally {
write_lock_.unlock();
}
if (is_need_call)
lib_publisher_.SmartPublisherStopPublisher(get());
return true;
}
public boolean StartRecorder() {
if (!check_native_handle())
return false;
if (is_recording()) {
Log.e(TAG, "already recording, native_handle:" + get());
return false;
}
int ret = lib_publisher_.SmartPublisherStartRecorder(get());
if (ret != OK) {
Log.e(TAG, "call SmartPublisherStartRecorder failed, native_handle:" + get() + ", ret:" + ret);
return false;
}
write_lock_.lock();
try {
this.is_recording_ = true;
} finally {
write_lock_.unlock();
}
Log.i(TAG, "call SmartPublisherStartRecorder OK, native_handle:" + get());
return true;
}
public boolean PauseRecorder(boolean is_pause) {
if (!check_native_handle())
return false;
if (!is_recording()) {
Log.e(TAG, "it's not recording, native_handle:" + get());
return false;
}
return OK == lib_publisher_.SmartPublisherPauseRecorder(get(), is_pause?1:0);
}
public boolean StopRecorder() {
if (!check_native_handle())
return false;
if (!is_recording()) {
Log.w(TAG, "it's not recording, native_handle:" + get());
return false;
}
boolean is_need_call = false;
write_lock_.lock();
try {
if (this.is_recording_) {
this.is_recording_ = false;
is_need_call = true;
}
} finally {
write_lock_.unlock();
}
if (is_need_call)
lib_publisher_.SmartPublisherStopRecorder(get());
return true;
}
public boolean SetRtspStreamName(String stream_name) {
if (is_null_or_empty(stream_name))
return false;
if (!check_native_handle())
return false;
return OK == lib_publisher_.SetRtspStreamName(get(), stream_name);
}
public boolean AddRtspStreamServer(long rtsp_server_handle) {
if (!check_native_handle())
return false;
return OK == lib_publisher_.AddRtspStreamServer(get(), rtsp_server_handle, 0);
}
public boolean ClearRtspStreamServer() {
if (!check_native_handle())
return false;
return OK == lib_publisher_.ClearRtspStreamServer(get());
}
public boolean StartRtspStream() {
if (!check_native_handle())
return false;
if (is_rtsp_publishing()) {
Log.e(TAG, "already publishing rtsp stream, native_handle:" + get());
return false;
}
int ret = lib_publisher_.StartRtspStream(get(), 0);
if (ret != OK) {
Log.e(TAG, "call StartRtspStream failed, native_handle:" + get() + ", ret:" + ret);
return false;
}
write_lock_.lock();
try {
this.is_rtsp_publishing_ = true;
} finally {
write_lock_.unlock();
}
Log.i(TAG, "call StartRtspStream OK, native_handle:" + get());
return true;
}
public boolean StopRtspStream() {
if (!check_native_handle())
return false;
if (!is_rtsp_publishing()) {
Log.w(TAG, "it's not publishing rtsp stream, native_handle:" + get());
return false;
}
boolean is_need_call = false;
write_lock_.lock();
try {
if (this.is_rtsp_publishing_) {
this.is_rtsp_publishing_ = false;
is_need_call = true;
}
} finally {
write_lock_.unlock();
}
if (is_need_call)
lib_publisher_.StopRtspStream(get());
return true;
}
public boolean SetGB28181RTPSender(long rtp_sender_handle, int rtp_payload_type, String encoding_name) {
if (!check_native_handle())
return false;
return OK == lib_publisher_.SetGB28181RTPSender(get(), rtp_sender_handle, rtp_payload_type, encoding_name);
}
public boolean StartGB28181MediaStream() {
if (!check_native_handle())
return false;
if (is_gb_stream_publishing()) {
Log.e(TAG, "already publishing gb media stream, native_handle:" + get());
return false;
}
int ret = lib_publisher_.StartGB28181MediaStream(get());
if (ret != OK) {
Log.e(TAG, "call StartGB28181MediaStream failed, native_handle:" + get() + ", ret:" + ret);
return false;
}
write_lock_.lock();
try {
this.is_gb_stream_publishing_ = true;
} finally {
write_lock_.unlock();
}
Log.i(TAG, "call StartGB28181MediaStream OK,native_handle:" + get());
return true;
}
public boolean StopGB28181MediaStream() {
if (!check_native_handle())
return false;
if (!is_gb_stream_publishing()) {
Log.w(TAG, "it's not publishing gb stream, native_handle:" + get());
return false;
}
boolean is_need_call = false;
write_lock_.lock();
try {
if (this.is_gb_stream_publishing_) {
this.is_gb_stream_publishing_ = false;
is_need_call = true;
}
} finally {
write_lock_.unlock();
}
if (is_need_call)
lib_publisher_.StopGB28181MediaStream(get());
return true;
}
public boolean OnPCMData(ByteBuffer pcm_data, int size, int sample_rate, int channel, int per_channel_sample_number) {
if (!check_native_handle() || !is_publishing())
return false;
if (!read_lock_.tryLock())
return false;
try {
if (!check_native_handle() || !is_publishing())
return false;
return OK == lib_publisher_.SmartPublisherOnPCMData(get(), pcm_data, size, sample_rate, channel, per_channel_sample_number);
} catch (Exception e) {
Log.e(TAG, "OnPCMData Exception:", e);
return false;
} finally {
read_lock_.unlock();
}
}
public boolean OnFarEndPCMData(ByteBuffer pcm_data, int sample_rate, int channel, int per_channel_sample_number, int is_low_latency) {
if (!check_native_handle() || !is_publishing())
return false;
if (!read_lock_.tryLock())
return false;
try {
if (!check_native_handle() || !is_publishing())
return false;
return OK == lib_publisher_.SmartPublisherOnFarEndPCMData(get(), pcm_data, sample_rate, channel, per_channel_sample_number, is_low_latency);
} catch (Exception e) {
Log.e(TAG, "OnFarEndPCMData Exception:", e);
return false;
} finally {
read_lock_.unlock();
}
}
public boolean PostUserUTF8StringData(String utf8_string) {
if (is_null_or_empty(utf8_string))
return false;
if (!check_native_handle() || !is_publishing())
return false;
if (!read_lock_.tryLock())
return false;
try {
if (!check_native_handle() || !is_publishing())
return false;
return OK == lib_publisher_.SmartPublisherPostUserUTF8StringData(get(), utf8_string, 0);
} catch (Exception e) {
Log.e(TAG, "PostUserUTF8StringData Exception:", e);
return false;
} finally {
read_lock_.unlock();
}
}
public boolean OnCaptureVideoData(byte[] data, int len, int cameraType, int curOrg) {
if (!check_native_handle())
return false;
if (!read_lock_.tryLock())
return false;
try {
if (!check_native_handle())
return false;
return OK == lib_publisher_.SmartPublisherOnCaptureVideoData(get(), data, len, cameraType, curOrg);
} catch (Exception e) {
Log.e(TAG, "OnCaptureVideoData Exception:", e);
return false;
} finally {
read_lock_.unlock();
}
}
public boolean EnableLayer(int index, boolean is_enable) {
if (index < 1)
return false;
if (!check_native_handle())
return false;
read_lock_.lock();
try {
if (!check_native_handle())
return false;
return OK == lib_publisher_.EnableLayer(get(), index, is_enable?1:0);
} catch (Exception e) {
Log.e(TAG, "EnableLayer Exception:", e);
return false;
} finally {
read_lock_.unlock();
}
}
public boolean RemoveLayer(int index) {
if (index < 1)
return false;
if (!check_native_handle())
return false;
read_lock_.lock();
try {
if (!check_native_handle())
return false;
return OK == lib_publisher_.RemoveLayer(get(), index);
} catch (Exception e) {
Log.e(TAG, "RemoveLayer Exception:", e);
return false;
} finally {
read_lock_.unlock();
}
}
public boolean PostLayerImageNV21ByteArray(int index, int left, int top,
byte[] y_plane, int y_offset, int y_row_stride,
byte[] uv_plane, int uv_offset, int uv_row_stride,
int width, int height, int is_vertical_flip, int is_horizontal_flip,
int scale_width, int scale_height, int scale_filter_mode,
int rotation_degree) {
if (!check_native_handle())
return false;
if (!read_lock_.tryLock())
return false;
try {
if (!check_native_handle())
return false;
return OK == lib_publisher_.PostLayerImageNV21ByteArray(get(), index, left, top, y_plane, y_offset, y_row_stride,
uv_plane, uv_offset, uv_row_stride, width, height, is_vertical_flip, is_horizontal_flip,
scale_width, scale_height, scale_filter_mode, rotation_degree);
} catch (Exception e) {
Log.e(TAG, "PostLayerImageNV21ByteArray Exception:", e);
return false;
} finally {
read_lock_.unlock();
}
}
public boolean PostLayerImageRGBA8888ByteBuffer(int index, int left, int top,
ByteBuffer rgba_plane, int offset, int row_stride, int width, int height,
int is_vertical_flip, int is_horizontal_flip,
int scale_width, int scale_height, int scale_filter_mode,
int rotation_degree) {
if (!check_native_handle())
return false;
if (!read_lock_.tryLock())
return false;
try {
if (!check_native_handle())
return false;
return OK == lib_publisher_.PostLayerImageRGBA8888ByteBuffer(get(), index, left, top,
rgba_plane, offset, row_stride, width, height, is_vertical_flip, is_horizontal_flip,
scale_width, scale_height, scale_filter_mode, rotation_degree);
} catch (Exception e) {
Log.e(TAG, "PostLayerImageRGBA8888ByteBuffer Exception:", e);
return false;
} finally {
read_lock_.unlock();
}
}
public boolean PostLayerImageI420ByteBuffer(int index, int left, int top,
ByteBuffer y_plane, int y_offset, int y_row_stride,
ByteBuffer u_plane, int u_offset, int u_row_stride,
ByteBuffer v_plane, int v_offset, int v_row_stride,
int width, int height, int is_vertical_flip, int is_horizontal_flip,
int scale_width, int scale_height, int scale_filter_mode,
int rotation_degree) {
if (!check_native_handle())
return false;
if (!read_lock_.tryLock())
return false;
try {
if (!check_native_handle())
return false;
return OK == lib_publisher_.PostLayerImageI420ByteBuffer(get(), index, left, top,
y_plane, y_offset, y_row_stride,
u_plane, u_offset, u_row_stride,
v_plane, v_offset, v_row_stride,
width, height, is_vertical_flip, is_horizontal_flip,
scale_width, scale_height, scale_filter_mode,
rotation_degree);
} catch (Exception e) {
Log.e(TAG, "PostLayerImageI420ByteBuffer Exception:", e);
return false;
} finally {
read_lock_.unlock();
}
}
public boolean PostLayerImageYUV420888ByteBuffer(int index, int left, int top,
ByteBuffer y_plane, int y_offset, int y_row_stride,
ByteBuffer u_plane, int u_offset, int u_row_stride,
ByteBuffer v_plane, int v_offset, int v_row_stride, int uv_pixel_stride,
int width, int height, int is_vertical_flip, int is_horizontal_flip,
int scale_width, int scale_height, int scale_filter_mode,
int rotation_degree) {
if (!check_native_handle())
return false;
if (!read_lock_.tryLock())
return false;
try {
if (!check_native_handle())
return false;
return OK == lib_publisher_.PostLayerImageYUV420888ByteBuffer(get(), index, left, top, y_plane, y_offset, y_row_stride,
u_plane, u_offset, u_row_stride, v_plane, v_offset, v_row_stride, uv_pixel_stride,
width, height, is_vertical_flip, is_horizontal_flip, scale_width, scale_height, scale_filter_mode, rotation_degree);
} catch (Exception e) {
Log.e(TAG, "PostLayerImageYUV420888ByteBuffer Exception:", e);
return false;
} finally {
read_lock_.unlock();
}
}
public boolean PostAudioEncodedData(int codec_id, ByteBuffer data, int size, int is_key_frame,
long timestamp,ByteBuffer parameter_info, int parameter_info_size) {
if (!check_native_handle())
return false;
if (!read_lock_.tryLock())
return false;
try {
if (!check_native_handle())
return false;
return OK == lib_publisher_.SmartPublisherPostAudioEncodedData(get(), codec_id, data, size, is_key_frame, timestamp, parameter_info, parameter_info_size);
} catch (Exception e) {
Log.e(TAG, "PostLayerImageYUV420888ByteBuffer Exception:", e);
return false;
} finally {
read_lock_.unlock();
}
}
private static boolean is_null_or_empty(String val) {
return null == val || val.isEmpty();
}
//估计硬编码码率, 可以根据实际机型调整
public static int estimate_video_hardware_kbps(int width, int height, int fps, boolean is_h264) {
int kbps;
int area = width * height;
if (area <= (320 * 300))
kbps = is_h264?350:280;
else if (area <= (370 * 320))
kbps = is_h264?470:400;
else if (area <= (640 * 360))
kbps = is_h264?850:650;
else if (area <= (640 * 480))
kbps = is_h264?1200:800;
else if (area <= (800 * 600))
kbps = is_h264?1300:950;
else if (area <= (900 * 700))
kbps = is_h264?1600:1100;
else if (area <= (1280 * 720))
kbps = is_h264?2100:1500;
else if (area <= (1366 * 768))
kbps = is_h264?2300:1900;
else if (area <= (1600 * 900))
kbps = is_h264?2800:2300;
else if (area <= (1600 * 1050))
kbps =is_h264?3100:2500;
else if (area <= (1920 * 1088))
kbps = is_h264?4200:2800;
else
kbps = is_h264?4500:3500;
kbps = (int)(kbps*fps*1.0/25.0 + 0.5);
return kbps;
}
public static int estimate_video_software_quality(int width, int height, boolean is_h264) {
int area = width*height;
int quality;
if ( area <= (320 * 240) )
quality = is_h264? 23 : 27;
else if ( area <= (640 * 360) )
quality = is_h264? 25 : 28;
else if ( area <= (640 * 480) )
quality = is_h264? 26 : 28;
else if ( area <= (960 * 600) )
quality = is_h264? 26 : 28;
else if ( area <= (1280 * 720) )
quality = is_h264? 27 : 29;
else if ( area <= (1600 * 900) )
quality = is_h264 ? 28 : 30;
else if ( area <= (1920 * 1088) )
quality = is_h264 ? 29 : 31;
else
quality = is_h264 ? 30 : 32;
return quality;
}
public static int estimate_video_vbr_max_kbps(int width, int height, int fps) {
int kbps;
int area = width*height;
if (area <= (320 * 300))
kbps = 320;
else if (area <= (360 * 320))
kbps = 400;
else if (area <= (640 * 360))
kbps = 700;
else if (area <= (640 * 480))
kbps = 800;
else if (area <= (800 * 600))
kbps = 900;
else if (area <= (900 * 700))
kbps = 1100;
else if (area <= (1280 * 720))
kbps = 1700;
else if (area <= (1366 * 768))
kbps = 1800;
else if (area <= (1600 * 900))
kbps = 2500;
else if (area <= (1600 * 1050))
kbps = 2700;
else if (area <= (1920 * 1088))
kbps = 3100;
else
kbps = 3500;
kbps = (int)(kbps*fps*1.0/25.0 + 0.5);
return kbps;
}
}
封装后的LibPublisherWrapper类,逻辑分离更彻底,调用更方便,几乎不要花心思了解接口用法,就可以非常高效的实现RTMP推送或轻量级RTSP服务技术诉求。