好多开发者问我们,有没有针对Android平台RTMP直播推送、轻量级RTSP服务模块的进一步封装,可以更便捷的调用大牛直播SDK接口。

Android平台RTMP推送|轻量级RTSP服务能力封装代码实现_Android RTMP推流

为此,我们分享下我们针对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服务技术诉求。