Java 返回一个数组

private ThreadLocal<byte[]> rtpBuffer = new ThreadLocal<>();
public Object GetByteArray(int type,int len)
{
    byte[] bytes = rtpBuffer.get();
    if(bytes == null || (len+30) >bytes.length)
    {
        bytes = new byte[ Math.max(2000,len+30)];
        rtpBuffer.set(bytes);
    }
    return bytes;
}

/*
 * 作者:songxy
 * 日期:2016.04.25
 * 功能:VideoNeteq jni封装
 */

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
#include <android/log.h>
#include <algorithm>
#include <stdio.h>
#include <string.h>
#include <semaphore.h>
#include <arpa/inet.h>
#include <chrono>
/* Header for class com_jiaxun_sdk_scl_media_video_LibVideoNeteq */
#include "./../include/VideoNetOptimization.h"
#include "./../include/VideoNetOptimizationReceiver.h"
#include "./../include/VideoNetOptimizationSender.h"

//#include "libVideoNeteq/include/rw_lock_wrapper.h"

#define LOG_SENDER 		0
#define LOG_RECEIVER 	0

#define	SENDER_CALLBACK_DISABLE 			0
#define	RECEIVER_CALLBACK_DISABLE			0
#define	INTERFACE_SENDER_DISABLE			0
#define	INTERFACE_RECEIVER_DISABLE			0


#define 	LOG_TAG				"Video_NetEq"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)

#define 	MAX_RTP_PAYLOAD_LEN			30000
#define 	MAX_H264_LEN				1920*1080*2
#define 	VIDEO_NAL_NUM_MAX 			1200

//using namespace webrtc;
JavaVM* g_vm = NULL; //全局
jobject g_sender = NULL;
//RWLockWrapper* g_rwLock = NULL; //全局锁
#if 0
// added by gc 20201110
class CGlobalInit
{
public:
    CGlobalInit();
};

CGlobalInit::CGlobalInit()
{
    VNOInitParam param;

    param.ulRcvrWorkThreadNum = 2;

    (void)iVNO_Init(&param);
}

static CGlobalInit gInitVno;
#endif

#define QUOTE(x) #x
#define STR(x) QUOTE(x)
#define GetVersion Java_com_jiaxun_sdk_scl_media_video_LibVideoNeteq_GetVersion_0_0_0_71_20231110

const char* str_version = STR(GetVersion);
extern "C" JNIEXPORT void GetVersion()
{

}
static long long getCurrentMs()
{
	auto time_now = std::chrono::system_clock::now();
	auto duration_in_ms = std::chrono::duration_cast<std::chrono::milliseconds>(time_now.time_since_epoch());

	return duration_in_ms.count();
}

bool FindTag(char* pBuf, int nBufLen, char*& pFind) {
	unsigned char tag[] = { 0x00, 0x00, 0x01 };

	if (pBuf == NULL || nBufLen < 0)
		return false;

	pFind = std::search(pBuf, pBuf + nBufLen, tag, tag + 3);
	if (pFind == (pBuf + nBufLen))
		return false;

	return true;
}

struct FragmentationInfo {
	unsigned long uOffset; // 偏移量
	unsigned long uLen; // 长度
};

#define		MAX_PAYLOAD_LEN		1500

class VideoNeteqSender;

//封装发送端java的回调
class SenderCallback: public VideoNetOptimizationSenderCallback {
public:
	SenderCallback(JNIEnv * env, jclass object, jobject javaSenderCallback, VideoNeteqSender* pSender, int resolutionNum);
	~SenderCallback();

	void OnRTPToSend(unsigned char* pPacket, int iPacketLen);
	void OnRTCPToSend(unsigned char* pPacket, int iPacketLen);
	void OnBitrateChanged(uint32_t uNewBitrate, uint32_t uRefFrameRate, uint32_t uResolutionIdx, uint8_t uFrLost);
	void OnRequestKeyFrame();

	int Init();

private:
	// add by zl 20211230 
	uint32_t CalResolutionIdx(uint32_t uNewBitrate);
	JNIEnv* m_pJavaEnv; //保存java环境指针
	jclass m_pObject; //保存java对象指针
	jobject m_javaSenderCallback; //保存java的回调对象
	bool m_bIsCallInit; //是否调用Init
	bool m_bInit; //调用Init的结果
	jclass m_class; //m_javaSenderCallback对应的java类

	jmethodID m_methodOnRtpToSend;
	jmethodID m_methodOnRtcpToSend;
	jmethodID m_methodOnBitrateChanged;
	jmethodID m_methodOnRequestKeyFrame;
	jmethodID m_methodInit;
    // added by gc 20201017 for rtp and rtcp send
    jmethodID m_methodGetByteArray;

	jbyteArray m_sender_pRtcpBuf;
	jbyteArray m_sender_pRtpBuf;

	sem_t   m_sem_t;

    uint32_t    m_uNewBitrate;
    uint32_t    m_uRefFrameRate;
    uint32_t    m_uResolutionIdx;
    VideoNeteqSender *m_pSender;
	uint32_t m_uResolutionNum;
	uint32_t m_uPreBitrate;
	uint64_t m_uPreTimeMs;
};

SenderCallback::SenderCallback(JNIEnv * env, jclass object,
		jobject javaSenderCallback, VideoNeteqSender* pSender, int resolutionNum) {
	m_pJavaEnv = env;
	m_pObject = object;
	m_javaSenderCallback = m_pJavaEnv->NewGlobalRef(javaSenderCallback);
	m_bIsCallInit = false;
	m_bInit = false;

	m_methodOnRtpToSend = 0;
	m_methodOnRtcpToSend = 0;
	m_methodOnBitrateChanged = 0;
	m_methodOnRequestKeyFrame = 0;
	m_methodInit = 0;
    m_methodGetByteArray = 0;

	m_sender_pRtpBuf = NULL;
	m_sender_pRtcpBuf = NULL;
    m_pSender = pSender;
	m_uResolutionNum = resolutionNum;
	m_uPreBitrate = 2048;
	m_uPreTimeMs = 0;
	//初始化
	Init();
}
uint32_t SenderCallback::CalResolutionIdx(uint32_t uNewBitrate)
{

	//LOGEM(mTrace, "VideoNetOptimizationSenderImpl::CalResolutionIdx uNewBitrate:%d m_oParameter_.m_uResolutionNum:%d  .. \n", uNewBitrate, m_oParameter_.m_uResolutionNum);
	uint32_t ret = m_uResolutionIdx;
	if (2 == m_uResolutionNum)
	{
		// 大分辨率
		if (uNewBitrate > 650)
		{
			ret = 0;
		}
		else if (uNewBitrate < 400)
		{
			// 小分辨率
			ret = 1;
		}

	}
	else
	{
		ret = 0;
	}
	return ret;

}
SenderCallback::~SenderCallback() {

	if (g_vm) {
		bool isAttached = false;
		JNIEnv* env = NULL;
		if (g_vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
			jint res = g_vm->AttachCurrentThread(&env, NULL);

			// Get the JNI env for this thread
			if ((res < 0) || !env) {
				env = NULL;
			} else {
				isAttached = true;
			}
		}

		if (m_javaSenderCallback) {
			env->DeleteGlobalRef(m_javaSenderCallback);
		}

		if (isAttached) {
			if (g_vm->DetachCurrentThread() < 0) {
			}
		}
	}

	sem_destroy(&m_sem_t);

#if LOG_SENDER
	LOGI("native : SenderCallback::~SenderCallback()");
#endif

}

//初始化 0 成功     其他失败
int SenderCallback::Init() {

#if   SENDER_CALLBACK_DISABLE
	return 0;
#endif

#if LOG_SENDER
	LOGI("native : SenderCallback::Init is called");
#endif

    m_uNewBitrate       = 0;
    m_uRefFrameRate     = 0;
    m_uResolutionIdx    = 0;

	//已经调用过直接返回
	if (m_bInit)
		return 0;

	m_class =
			m_pJavaEnv->FindClass(
					"com/jiaxun/sdk/scl/media/video/VideoNetOptimizationSenderCallback");
	if (m_class == NULL) {
		LOGI("native : SenderCallback::Init find class failed");
		m_bInit = false;
		return -1;
	}

	m_methodOnRtpToSend = m_pJavaEnv->GetMethodID(m_class, "OnRTPToSend",
			"([BI)V");
	if (m_methodOnRtpToSend == NULL) {
		LOGI("native : SenderCallback::Init get method  OnRTPToSend failed");
		return -1;
	}

	m_methodOnRtcpToSend = m_pJavaEnv->GetMethodID(m_class, "OnRTCPToSend",
			"([BI)V");
	if (m_methodOnRtcpToSend == NULL) {
		LOGI("native : SenderCallback::Init get method  OnRTCPToSend failed");
		return -1;
	}

	m_methodOnBitrateChanged = m_pJavaEnv->GetMethodID(m_class,
			"OnBitrateChanged", "(IIIIJ)V");
	if (m_methodOnBitrateChanged == NULL) {
		LOGI(
				"native : SenderCallback::Init get method  OnBitrateChanged failed");
		return -1;
	}

	m_methodOnRequestKeyFrame = m_pJavaEnv->GetMethodID(m_class,
			"OnRequestKeyFrame", "(J)V");
	if (m_methodOnRequestKeyFrame == NULL) {
		LOGI(
				"native : SenderCallback::Init get method  OnRequestKeyFrame failed");
		return -1;
	}

    m_methodGetByteArray = m_pJavaEnv->GetMethodID(m_class, "GetByteArray",
            "(II)Ljava/lang/Object;");
    if (m_methodGetByteArray == NULL)
    {
        LOGI("native : SenderCallback::Init find method GetByteArray failed");
        return -1;
    }


	sem_init (&m_sem_t, 0, 1);

	m_bInit = true;

#if LOG_SENDER
	LOGI("native : SenderCallback::Init success");
#endif

	return 0;
}

void SenderCallback::OnRTPToSend(unsigned char* pPacket, int iPacketLen) {

	sem_wait(&m_sem_t);

	//WriteLockScoped wl(*g_rwLock);
#if   SENDER_CALLBACK_DISABLE
	return ;
#endif

#if LOG_SENDER
	LOGI("native : SenderCallback::OnRTPToSend is called");
#endif

	if (!m_bInit) {
		if (Init() < 0)
		{
			sem_post(&m_sem_t);
			return;
		}
	}
	JNIEnv* env = NULL;
	bool bIsAttached = false;
	int ret = 0;
	ret = g_vm->GetEnv((void**) &env, JNI_VERSION_1_4);
	if (ret != JNI_OK) {
		ret = g_vm->AttachCurrentThread(&env, NULL);
		if (ret < 0 || !env) {
			LOGI(
					"native : SenderCallback::OnRTPToSend AttachCurrentThread failed");
			sem_post(&m_sem_t);
			return;
		} else {
			bIsAttached = true;
		}
	}

//	m_sender_pRtpBuf = env->NewByteArray(MAX_RTP_PAYLOAD_LEN);
    jobject jObj = env->CallObjectMethod(m_javaSenderCallback, m_methodGetByteArray, (jint)0, (jint)iPacketLen);
    jbyteArray rtpArray = (jbyteArray)jObj;

#if 1
    env->SetByteArrayRegion(rtpArray, 0, iPacketLen, (jbyte*) pPacket);
	env->CallVoidMethod(m_javaSenderCallback, m_methodOnRtpToSend,
			rtpArray, iPacketLen);
#else

	m_sender_pRtpBuf = env->NewByteArray(iPacketLen);

	env->SetByteArrayRegion(m_sender_pRtpBuf, 0, iPacketLen, (jbyte*) pPacket);

	env->CallVoidMethod(m_javaSenderCallback, m_methodOnRtpToSend,
			m_sender_pRtpBuf, iPacketLen);
#endif
	//env->DeleteLocalRef(m_sender_pRtpBuf);

	if (bIsAttached)
		g_vm->DetachCurrentThread();

#if LOG_SENDER
	LOGI("native : SenderCallback::OnRTPToSend success");
#endif

	sem_post(&m_sem_t);
}

void SenderCallback::OnRTCPToSend(unsigned char* pPacket, int iPacketLen) {

	sem_wait(&m_sem_t);

	//WriteLockScoped wl(*g_rwLock);
#if   SENDER_CALLBACK_DISABLE
	return ;
#endif

#if LOG_SENDER
	LOGI("native : SenderCallback::OnRTCPToSend is called");
#endif

	if (!m_bInit) {
		if (Init() < 0)
		{
			sem_post(&m_sem_t);
			return;
		}
	}

	JNIEnv* env = NULL;
	bool bIsAttached = false;
	int ret = 0;
	//尝试获取当前的env,如果失败,关联线程
	ret = g_vm->GetEnv((void**) &env, JNI_VERSION_1_4);
	if (ret != JNI_OK) {
		ret = g_vm->AttachCurrentThread(&env, NULL);
		if (ret < 0 || !env) {
			LOGI(
					"native : SenderCallback::OnRTCPToSend AttachCurrentThread failed");
			sem_post(&m_sem_t);
			return;
		} else {
			bIsAttached = true;
		}
	}


	m_sender_pRtcpBuf = env->NewByteArray(iPacketLen + 30);

	env->SetByteArrayRegion(m_sender_pRtcpBuf, 0, iPacketLen, (jbyte*) pPacket);

	env->CallVoidMethod(m_javaSenderCallback, m_methodOnRtcpToSend,
			m_sender_pRtcpBuf, iPacketLen);

	//env->DeleteLocalRef(m_sender_pRtcpBuf);

	if (bIsAttached)
		g_vm->DetachCurrentThread();
#if LOG_SENDER
	LOGI("native : SenderCallback::OnRTCPToSend is success");
#endif

	sem_post(&m_sem_t);
}

void SenderCallback::OnBitrateChanged(uint32_t uNewBitrate, uint32_t uRefFrameRate, uint32_t uResolutionIdx, uint8_t uFrLost) {

#if   SENDER_CALLBACK_DISABLE
	return ;
#endif
    bool bSkip = false;

    if (uNewBitrate == m_uNewBitrate 
        && uRefFrameRate == m_uRefFrameRate 
        && uResolutionIdx == m_uResolutionIdx)
    {
        bSkip = true;
    }

	//WriteLockScoped wl(*g_rwLock);
#if LOG_SENDER
	LOGI("native : SenderCallback::OnBitrateChanged is called uNewBitrate(kbps)=%d uRefFrameRate=%d,uResloution:%d,uLost(255):%d,skip:%d.",uNewBitrate,uRefFrameRate,uResolutionIdx,uFrLost,bSkip);
#endif

    if (bSkip)
    {
        return;
    }

	if (!m_bInit) {
		if (Init() < 0)
			return;
	}

	JNIEnv* env = NULL;
	bool bIsAttached = false;
	//jclass 	cls = NULL;
	int ret = 0;
	//尝试获取当前的env,如果失败,关联线程
	ret = g_vm->GetEnv((void**) &env, JNI_VERSION_1_4);
	if (ret != JNI_OK) {
		ret = g_vm->AttachCurrentThread(&env, NULL);
		if (ret < 0 || !env) {
			LOGI(
					"native : SenderCallback::OnBitrateChanged AttachCurrentThread failed");
			return;
		} else {
			bIsAttached = true;
		}
	}

    int uLost = uFrLost * 100 / 255;

	int32_t diff = (int32_t)std::abs((long)(uNewBitrate - m_uPreBitrate));
	float t_scale = diff * 1.0f / m_uPreBitrate;
	bool t_isUpdate = false; // 不更新

	uint64_t t_curTime = getCurrentMs();
#if 0
// add by zl 底层处理,这里直接回调
	if (t_scale > 0.2f)
	{
		t_isUpdate = true;
	}
	else if (t_scale >= 0.05f && t_scale <= 0.2f)
	{
		// 
		if (t_curTime - m_uPreTimeMs > 2000)
		{
			t_isUpdate = true;
		}
	}
#else
	t_isUpdate = true;
#endif	
	LOGI("native : SenderCallback::OnBitrateChanged uNewBitrate:%d m_uPreBitrate:%d t_scale:%f difTime:%lld t_isUpdate:%d .", uNewBitrate, m_uPreBitrate, t_scale,t_curTime-m_uPreTimeMs, t_isUpdate);
	if (t_isUpdate)
	{
		m_uResolutionIdx = CalResolutionIdx(uNewBitrate);

		int t_uNewBitrate = (int)uNewBitrate * 1000;
		LOGI("native : SenderCallback::OnBitrateChanged is called uNewBitrate(kbps)=%d t_uNewBitrate:%d uRefFrameRate=%d,m_uResolutionIdx:%d,uLost(255):%d,skip:%d.", uNewBitrate, t_uNewBitrate, uRefFrameRate, m_uResolutionIdx, uFrLost, bSkip);
		env->CallVoidMethod(m_javaSenderCallback, m_methodOnBitrateChanged,
			t_uNewBitrate, (int)uRefFrameRate, m_uResolutionIdx, uLost, (jlong)m_pSender);

		m_uNewBitrate = uNewBitrate;
		m_uRefFrameRate = uRefFrameRate;
		//m_uResolutionIdx = uResolutionIdx;
		m_uPreTimeMs = t_curTime;
		m_uPreBitrate = uNewBitrate;

	}
	if (bIsAttached)
		g_vm->DetachCurrentThread();

#if LOG_SENDER
	LOGI("native : SenderCallback::OnBitrateChanged success");
#endif
}

void SenderCallback::OnRequestKeyFrame() {

	//WriteLockScoped wl(*g_rwLock);
#if   SENDER_CALLBACK_DISABLE
	return ;
#endif

#if LOG_SENDER
	LOGI("native : SenderCallback::OnRequestKeyFrame is called");
#endif

	if (!m_bInit) {
		if (Init() < 0)
			return;
	}

	JNIEnv* env = NULL;
	bool bIsAttached = false;
	//jclass 	cls = NULL;
	int ret = 0;
	//尝试获取当前的env,如果失败,关联线程
	ret = g_vm->GetEnv((void**) &env, JNI_VERSION_1_4);
	if (ret != JNI_OK) {
		ret = g_vm->AttachCurrentThread(&env, NULL);
		if (ret < 0 || !env) {
			LOGI(
					"native : SenderCallback::OnBitrateChanged AttachCurrentThread failed");
			return;
		} else {
			bIsAttached = true;
		}
	}

	env->CallVoidMethod(m_javaSenderCallback, m_methodOnRequestKeyFrame, (jlong)this);
	if (bIsAttached)
		g_vm->DetachCurrentThread();

#if LOG_SENDER
	LOGI("native : SenderCallback::OnRequestKeyFrame success");
#endif
}

//接收端java回调封装
class ReceiverCallback: public VideoNetOptimizationReceiverCallback {
public:
	ReceiverCallback(JNIEnv * env, jclass object, jobject javaReceiverCallback,
			jint width, jint height);
	~ReceiverCallback();

	void OnVideoFrame(void* uUserData, const unsigned char* pFrame,
			uint32_t uFrameLen, const VideoFrameInfo* pFrameInfo,
			const VNO_RTPFragmentationHeader* pFragmentation);
//	void OnVideoFrame(unsigned long uUserData, unsigned char* pFrame,
//			unsigned long uFrameLen, VideoFrameInfo* pFrameInfo,
//			const VNO_RTPFragmentationHeader* pFragmentation);

	void OnRTCPToSend(void* uUserData, const unsigned char* pPacket,
			int iPacketLen);

	int Init();
private:
	JNIEnv* m_pJavaEnv; //保存java环境指针
	jclass m_pObject; //保存java对象指针
	jobject m_javaReceiverCallback; //保存java的回调对象
	bool m_bIsCallInit; //是否调用Init
	bool m_bInit; //调用Init的结果
	jclass m_class; //m_javaSenderCallback对应的java类

	jmethodID m_methodOnVideoFrame;
	jmethodID m_methodOnRTCPToSend;
    // added by gc 20201017 for rtp and rtcp send
    jmethodID m_methodGetByteArray;

	jbyteArray m_receiver_pRtcpBuf;
	jbyteArray m_nal_pBuf;

	jint bufferSize;

};

ReceiverCallback::ReceiverCallback(JNIEnv * env, jclass object,
		jobject javaReceiverCallback, jint width, jint height) {
	m_pJavaEnv = env;
	m_pObject = object;
	m_javaReceiverCallback = m_pJavaEnv->NewGlobalRef(javaReceiverCallback);
	m_bIsCallInit = false;
	m_bInit = false;
	m_class = 0;

	m_methodOnVideoFrame = 0;
	m_methodOnRTCPToSend = 0;
    m_methodGetByteArray = 0;

	m_receiver_pRtcpBuf = NULL;
	m_nal_pBuf = NULL;

	bufferSize = width * height * 3 / 2;

	Init();
}

ReceiverCallback::~ReceiverCallback() {

	if (g_vm) {
		bool isAttached = false;
		JNIEnv* env = NULL;
		if (g_vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
			// try to attach the thread and get the env
			// Attach this thread to JVM
			jint res = g_vm->AttachCurrentThread(&env, NULL);

			// Get the JNI env for this thread
			if ((res < 0) || !env) {
				env = NULL;
			} else {
				isAttached = true;
			}
		}

		if (m_javaReceiverCallback) {
			env->DeleteGlobalRef(m_javaReceiverCallback);
		}

		if (isAttached) {
			if (g_vm->DetachCurrentThread() < 0) {
			}
		}
	}

#if LOG_RECEIVER
	LOGI("native : ReceiverCallback::~ReceiverCallback() success");
#endif
}

//初始化: 0成功   其他失败
int ReceiverCallback::Init() {
	//WriteLockScoped wl(*g_rwLock);

#if   RECEIVER_CALLBACK_DISABLE
	return 0;
#endif

#if LOG_RECEIVER
	LOGI("native : ReceiverCallback::Init is called");
#endif
	//已经调用过直接返回
	if (m_bInit)
		return 0;

	m_class =
			m_pJavaEnv->FindClass(
					"com/jiaxun/sdk/scl/media/video/VideoNetOptimizationReceiverCallback");

	if (m_class == NULL) {
		LOGI("native : ReceiverCallback::Init FindClass failed");
		return -1;
	}

	m_methodOnVideoFrame = m_pJavaEnv->GetMethodID(m_class, "OnVideoFrame",
			"(J[BJI)V");

	if (m_methodOnVideoFrame == NULL) {
		LOGI(
				"native : ReceiverCallback::Init find method m_methodOnVideoFrame failed");
		return -1;
	}

	m_methodOnRTCPToSend = m_pJavaEnv->GetMethodID(m_class, "OnRTCPToSend",
			"([BI)V");

	if (m_methodOnRTCPToSend == NULL) {
		LOGI("native : ReceiverCallback::Init find method OnRTCPToSend failed");
		return -1;
	}

    m_methodGetByteArray = m_pJavaEnv->GetMethodID(m_class, "GetByteArray",
            "(II)Ljava/lang/Object;");
    if (m_methodGetByteArray == NULL)
    {
        LOGI("native : ReceiverCallback::Init find method GetByteArray failed");
        return -1;
    }

	m_bInit = true;

#if LOG_RECEIVER
	LOGI("native : ReceiverCallback::Init is success");
#endif

	return 0;
}

void ReceiverCallback::OnVideoFrame(void* uUserData, const unsigned char* pFrame,
		uint32_t uFrameLen, const VideoFrameInfo* pFrameInfo,
		const VNO_RTPFragmentationHeader* pFragmentation) {

//	WriteLockScoped wl(*g_rwLock);
#if   RECEIVER_CALLBACK_DISABLE
	return ;
#endif

#if LOG_RECEIVER
	LOGI("native : ReceiverCallback::OnVideoFrame is called");
#endif

#if LOG_RECEIVER
    LOGI("native : ReceiverCallback::OnVideoFrame type:%d, bComplete:%d, bContinue:%d, len:%d.", 
        pFrameInfo->eFrameType, pFrameInfo->bComplete, pFrameInfo->bContinue, uFrameLen);
#endif

	if (!m_bInit) {
		if (Init() < 0)
			return;
	}
    // added by gc 20200930 不向上层回调SEI,以解决某些android设备解码绿屏问题
    if (pFrameInfo->eFrameType == VNO_FRAMETYPE_SEI)
    {
        return;
    }
  /*
  LOGI("**>>[%d]\n\r", pFrameInfo->bComplete);
  if(pFrameInfo->bComplete == false)
  {
  	LOGI("###################################################################>>>\n\r");
    return;
  }
  */
  //LOGI("<<<<<<<[%d]", uFrameLen);
  uUserData = (void *)pFrameInfo->eFrameType;
	JNIEnv* env = NULL;
	bool bIsAttached = false;
	//jclass 	cls = NULL;
	int ret = 0;
	//尝试获取当前的env,如果失败,关联线程
	ret = g_vm->GetEnv((void**) &env, JNI_VERSION_1_4);
	if (ret != JNI_OK) {
		ret = g_vm->AttachCurrentThread(&env, NULL);
		if (ret < 0 || !env) {
			LOGI(
					"native : ReceiverCallback::OnVideoFrame AttachCurrentThread failed");
			return;
		} else {
			bIsAttached = true;
		}
	}

	for(int k = 0; k < pFragmentation->fragmentationVectorSize; k ++)
	{

		//jobject jObj = env->CallObjectMethod(m_javaReceiverCallback, m_methodGetByteArray, (jint)0, (jint)uFrameLen);
		jobject jObj = env->CallObjectMethod(m_javaReceiverCallback, m_methodGetByteArray, (jint)0, (jint)pFragmentation->fragmentationLength[k]);
		jbyteArray rtpArray = (jbyteArray)jObj;

	#if 1
		env->SetByteArrayRegion(rtpArray, 0, pFragmentation->fragmentationLength[k], (jbyte*) pFrame + pFragmentation->fragmentationOffset[k]);
		env->CallVoidMethod(m_javaReceiverCallback, m_methodOnVideoFrame,
				(jlong) uUserData, rtpArray, (jlong) pFragmentation->fragmentationLength[k], (jint)pFrameInfo->eFrameType);
	#else
		m_nal_pBuf = env->NewByteArray(uFrameLen + 1);

		env->SetByteArrayRegion(m_nal_pBuf, 0, uFrameLen, (jbyte*) pFrame);

		env->CallVoidMethod(m_javaReceiverCallback, m_methodOnVideoFrame,
				(jlong) uUserData, m_nal_pBuf, (jlong) uFrameLen, (jint)pFrameInfo->eFrameType);
	#endif
		//env->DeleteLocalRef(m_nal_pBuf);
	}
   

	if (bIsAttached)
		g_vm->DetachCurrentThread();

#if LOG_RECEIVER
	LOGI("native : ReceiverCallback::OnVideoFrame success");
#endif
}

void ReceiverCallback::OnRTCPToSend(void* uUserData,
		const unsigned char* pPacket, int iPacketLen) {

	//WriteLockScoped wl(*g_rwLock);
#if   RECEIVER_CALLBACK_DISABLE
	return ;
#endif

#if LOG_RECEIVER
	LOGI("native : ReceiverCallback::OnRTCPToSend is called");
#endif

	if (!m_bInit) {
		if (Init() < 0)
			return;
	}

	JNIEnv* env = NULL;
	bool bIsAttached = false;
	//jclass 	cls = NULL;
	int ret = 0;
	//尝试获取当前的env,如果失败,关联线程
	ret = g_vm->GetEnv((void**) &env, JNI_VERSION_1_4);
	if (ret != JNI_OK) {
		ret = g_vm->AttachCurrentThread(&env, NULL);
		if (ret < 0 || !env) {
			LOGI(
					"native : ReceiverCallback::OnRTCPToSend AttachCurrentThread failed");
			return;
		} else {
			bIsAttached = true;
		}
	}

#if LOG_RECEIVER
	LOGI("ReceiverCallback::OnRTCPToSend iPacketLen : %d", iPacketLen);
#endif

    m_receiver_pRtcpBuf = env->NewByteArray(iPacketLen + 30);

    env->SetByteArrayRegion(m_receiver_pRtcpBuf, 0, iPacketLen,
            (jbyte*) pPacket);
	env->CallVoidMethod(m_javaReceiverCallback, m_methodOnRTCPToSend,
			m_receiver_pRtcpBuf, iPacketLen);

	//env->DeleteLocalRef(m_receiver_pRtcpBuf);

	if (bIsAttached)
		g_vm->DetachCurrentThread();

#if LOG_RECEIVER
	LOGI("native : ReceiverCallback::OnRTCPToSend success");
#endif
}

typedef struct VideoNeteqSender {
	VideoNetOptimizationSender* m_pSender;
	SenderCallback* m_pSenderCallback;
	FragmentationInfo* m_pFragmentationInfos;
	char* m_pH264Buf;
	char* m_pRtcpBuf;

	VideoNeteqSender();
	~VideoNeteqSender();
} VideoNeteqSender;

VideoNeteqSender::VideoNeteqSender() {
	m_pSender = NULL;
	m_pSenderCallback = NULL;
	m_pH264Buf = new char[MAX_H264_LEN];
	m_pRtcpBuf = new char[MAX_RTP_PAYLOAD_LEN];
	m_pFragmentationInfos = (FragmentationInfo*) malloc(
			VIDEO_NAL_NUM_MAX * sizeof(FragmentationInfo));
}

VideoNeteqSender::~VideoNeteqSender() {

	LOGI("VideoNeteqSender : 1");

	if (m_pSender) {
		delete m_pSender;
		m_pSender = NULL;
	}


	LOGI("VideoNeteqSender : 2");

	if (m_pRtcpBuf) {
		delete[] m_pRtcpBuf;
		m_pRtcpBuf = NULL;
	}

	LOGI("VideoNeteqSender : 3 ");

	if (m_pH264Buf) {
			delete[] m_pH264Buf;
			m_pH264Buf = NULL;
		}

	LOGI("VideoNeteqSender : 4 m_pSenderCallback=%p", m_pSenderCallback);

	if (m_pSenderCallback) {
		delete m_pSenderCallback;
		m_pSenderCallback = NULL;
	}


	LOGI("VideoNeteqSender : 5");

	if (m_pFragmentationInfos) {
		free(m_pFragmentationInfos);
		m_pFragmentationInfos = NULL;
	}

	LOGI("VideoNeteqSender : 6");
}

typedef struct VideoNeteqReceiver {
	VideoNetOptimizationReceiver* m_pReceiver;
	ReceiverCallback* m_pReceiverCallback;
	char* m_pRtpBuf;
	char* m_pRtcpBuf;
    bool m_bRecvedFrame;

	VideoNeteqReceiver();
	~VideoNeteqReceiver();
} VideoNeteqReceiver;

VideoNeteqReceiver::VideoNeteqReceiver() {
	m_pReceiver = NULL;
	m_pReceiverCallback = NULL;

	m_pRtpBuf = new char[MAX_RTP_PAYLOAD_LEN];
	m_pRtcpBuf = new char[MAX_RTP_PAYLOAD_LEN];

    m_bRecvedFrame = false;
}

VideoNeteqReceiver::~VideoNeteqReceiver() {

	LOGI("VideoNeteqReceiver 1");
	if (m_pReceiver) {
		delete m_pReceiver;
		//VideoNetOptimizationReceiver::pDestroyVideoNetOptimizationReceiver(m_pReceiver);
		m_pReceiver = NULL;
	}

	LOGI("VideoNeteqReceiver 2");
	if (m_pReceiverCallback) {
		delete m_pReceiverCallback;
		m_pReceiverCallback = NULL;
	}

	LOGI("VideoNeteqReceiver 3");
	if (m_pRtpBuf) {
		delete m_pRtpBuf;
		m_pRtpBuf = NULL;
	}
	LOGI("VideoNeteqReceiver 4");
}

#ifndef _Included_com_jiaxun_sdk_scl_media_video_LibVideoNeteq
#define _Included_com_jiaxun_sdk_scl_media_video_LibVideoNeteq

/*
 * 得到全局的javaVM
 */
#ifdef __cplusplus
extern "C" {
#endif

JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void *reserved) {
	JNIEnv* env = NULL;
	jint result = -1;

	if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK)
		return -1;

	g_vm = vm;

	LOGI("native : JNI_OnLoad VNO2 version:%s \n", str_version);
	return JNI_VERSION_1_4;
}

/*
 * Class:     com_jiaxun_sdk_scl_media_video_LibVideoNeteq
 * Method:    iVNO_Init
 * Signature: (I)I
 */JNIEXPORT jint JNICALL Java_com_jiaxun_sdk_scl_media_video_LibVideoNeteq_iVNO_1Init(
		JNIEnv * env, jclass object, jint nNum) {
	int threadNum = nNum;
	LOGI("native : iVNO_1Init is called i=%d do nothing and return ", threadNum);
	return 0;
    VNOInitParam param;

    param.ulRcvrWorkThreadNum = threadNum;

    (void)iVNO_Init(&param);
    
	//g_rwLock = RWLockWrapper::CreateRWLock();
//	env->GetJavaVM(&g_vm);
	return threadNum;
}

/*
 * Class:     com_jiaxun_sdk_scl_media_video_LibVideoNeteq
 * Method:    vVNO_Unint
 * Signature: ()V
 */JNIEXPORT void JNICALL Java_com_jiaxun_sdk_scl_media_video_LibVideoNeteq_vVNO_1Unint(
		JNIEnv * env, jclass object) {
	LOGI("native : vVNO_1Unint is called");
//	if (g_rwLock)
//		delete g_rwLock;

	return;
}


/*
 * Class:     com_jiaxun_sdk_scl_media_video_LibVideoNeteq
 * Method:    CreateVideoNetOptimizationReceiver
 * Signature: (IIIZZILjava/lang/Object;)J
 * eEstimateMethod 码率估算 0 未知, 1 GCC , 2 BWE
 * iTransportSequenceNumberID 协商得到的NumberID
 * iDuration 计算实时带宽时长默认2000ms,推荐2000整数倍,<=10000 add by zl 20230809
 */JNIEXPORT jlong JNICALL Java_com_jiaxun_sdk_scl_media_video_LibVideoNeteq_CreateVideoNetOptimizationReceiver(
		JNIEnv * env, jclass object, jint nEncType, jint nPayloadType,
		jint nVideoProtectType, jboolean bUseRcvThread,
		jboolean bUseDecodeThread, jint eFrameCbMode, jobject objCallback,
		jint width, jint height, jint nJxredPayloadType, 
		jint eEstimateMethod, jint iTransportSequenceNumberID, jint iDuration) {

#if LOG_RECEIVER
	LOGI("native : CreateVideoNetOptimizationReceiver receiver is called\n");
#endif

	VideoNeteqReceiver* pReceiver = new VideoNeteqReceiver();
	if (pReceiver == NULL) {
		LOGI("native : new VideoNeteqReceiver failed\n");
		return 0;
	}

	pReceiver->m_pReceiverCallback = new ReceiverCallback(env, object,
			objCallback, width, height);
	if (pReceiver->m_pReceiverCallback == NULL) {
		LOGI("native : new receiver callback failed\n");
		delete pReceiver;
		pReceiver = NULL;

		return 0;
	}

	VideoNetOptimizationReceiverParameter oParameter;
	oParameter.eCodecType = (VideoNetOptimizationCodecType) nEncType;
	// add by zl 变量替换 20210909
	oParameter.m_PayloadType = (unsigned char) nPayloadType;
	oParameter.m_eProtectionMethod =
			(VideoNetOptimizationProtectionMethod) nVideoProtectType;
	oParameter.pCallback = pReceiver->m_pReceiverCallback;
	// add by zl 20210909 注释
	//oParameter.bUseRcvThread = bUseRcvThread;
	//oParameter.bUseDecodeThread = bUseDecodeThread;
	//oParameter.eFrameCbMode =
	//		(VideoNetOptimizationFrameCallbackMode) eFrameCbMode;
    if (oParameter.m_eProtectionMethod == VNO_ProtectionMethodJxredOnly
        || oParameter.m_eProtectionMethod == VNO_ProtectionMethodHybridNackAndJxred)
    {
		// add by zl 20210909 替换变量
        oParameter.m_uJxredType = nJxredPayloadType; 
    }
	// add by zl 20210909 新增参数
	oParameter.m_eRemoteBitrateEstimateMethod = (VnoRemoteBitrateEstimateMethod)eEstimateMethod;
    oParameter.m_TransportSequenceNumberID = iTransportSequenceNumberID;
	oParameter.m_bEnableFrameBuffer= true;
	oParameter.m_iDuration = iDuration;
	

    // add by gc 20200428 debug
//    oParameter.eProtectionMethod = VNO_ProtectionMethodHybridNackAndJxred;
//    oParameter.ucJxredType = 103;
//    oParameter.eFrameCbMode = VNO_FrameCbAny;
//    oParameter.bUseDecodeThread = false;        
#if LOG_RECEIVER
    LOGI("native : CreateVideoNetOptimizationReceiver para, method:%d, thread:%d, decode:%d, mode:%d, jxred:%d", oParameter.eProtectionMethod, 
        oParameter.bUseRcvThread, oParameter.bUseDecodeThread, oParameter.eFrameCbMode, nJxredPayloadType);
#endif

	pReceiver->m_pReceiver =
			VideoNetOptimizationReceiver::pCreateVideoNetOptimizationReceiver(
					oParameter);
	if (pReceiver->m_pReceiver == NULL) {
		LOGI("native : CreateVideoNetOptimizationReceiver failed\n");
		delete pReceiver;
		pReceiver = NULL;
		return 0;
	}

#if LOG_RECEIVER
	LOGI("native : CreateVideoNetOptimizationReceiver receiver is success\n");
#endif
	LOGI("native : JNI_OnLoad VNO2 version:%s \n", str_version);
	
	return (jlong) pReceiver;
}

/*
 * Class:     com_jiaxun_sdk_scl_media_video_LibVideoNeteq
 * Method:    DestroyVideoNetOptimizationReceiver
 * Signature: (J)V
 */JNIEXPORT void JNICALL Java_com_jiaxun_sdk_scl_media_video_LibVideoNeteq_DestroyVideoNetOptimizationReceiver(
		JNIEnv * env, jclass object, jlong handle) {

#if LOG_RECEIVER
	LOGI("native : DestroyVideoNetOptimizationReceiver receiver is called");
#endif

	VideoNeteqReceiver* pReceiver = (VideoNeteqReceiver*) handle;
	if (pReceiver) {
		delete pReceiver;
		pReceiver = NULL;
	}

#if LOG_RECEIVER
	LOGI("native : DestroyVideoNetOptimizationReceiver receiver is success");
#endif
	return;
}

// add by gc 20171123
typedef struct RTP_FIXED_HEADER{
    /* byte 0 */
    unsigned char csrc_len : 4;       /* expect 0 */
    unsigned char extension : 1;      /* expect 1 */
    unsigned char padding : 1;        /* expect 0 */
    unsigned char version : 2;        /* expect 2 */
    /* byte 1 */
    unsigned char payload : 7;
    unsigned char marker : 1;        /* expect 1 */
    /* bytes 2, 3 */
    unsigned short seq_no;
    /* bytes 4-7 */
    unsigned  long timestamp;
    /* bytes 8-11 */
    unsigned long ssrc;            /* stream number is used here. */
} RTP_FIXED_HEADER;


typedef struct RTP_EXTENSION
{
    unsigned short byProfile;
    unsigned short len;
}RTP_EXTENSION;


#define DEBUG_INCOMING

/*
 * Class:     com_jiaxun_sdk_scl_media_video_LibVideoNeteq
 * Method:    IncomingRtpPacket
 * Signature: (J[BI)I
 */JNIEXPORT jint JNICALL Java_com_jiaxun_sdk_scl_media_video_LibVideoNeteq_IncomingRtpPacket(
		JNIEnv * env, jclass object, jlong handle, jbyteArray rtpStream,
		jint len, jint offset) {
#if INTERFACE_RECEIVER_DISABLE
	 return 0;
#endif

#if LOG_RECEIVER
	LOGI("native : IncomingRtpPacket is called");
	if(len > 1500)
	{
		LOGI("native : IncomingRtpPacket len=%d",len);
	}
#endif

	VideoNeteqReceiver* pReceiver = (VideoNeteqReceiver*) handle;
    int ret = 0;
	if (pReceiver == NULL) {
		LOGI("native : IncomingRtpPacket   pReceiver == NULL return");
		return -1;
	}
  //LOGI(">>>>>>>>>>>>>>>[%d]", len);
	if(len > MAX_RTP_PAYLOAD_LEN)
	{
		LOGI("##########################################################native : IncomingRtpPacket   len =%d",len);
		return -1;
	}
	if (pReceiver->m_pRtpBuf) {
		env->GetByteArrayRegion(rtpStream, 0, len, (jbyte*) pReceiver->m_pRtpBuf);
        // add by gc 20171123
        RTP_FIXED_HEADER *pRtpHeader = (RTP_FIXED_HEADER *)pReceiver->m_pRtpBuf;
#if LOG_RECEIVER
        LOGI("native : IncomingRtpPacket  GetByteArrayRegion OK, this:%p, ext:%d, len:%d", pReceiver, pRtpHeader->extension, len);
#endif

#if 0
        if (pRtpHeader->extension)
        {
            uint32_t extLen;
			RTP_EXTENSION *pExtHeader;
            uint32_t headLen = sizeof(*pRtpHeader) + pRtpHeader->csrc_len * sizeof(uint32_t);
            
            pExtHeader = (RTP_EXTENSION *)((char *)pRtpHeader + headLen);
            extLen = sizeof(*pExtHeader) + ((pExtHeader->len >> 8) | ((pExtHeader->len & 0xff) << 8)) * 4;
#if LOG_RECEIVER
            LOGI("native : IncomingRtpPacket  GetByteArrayRegion OK, headLen:%d, extLen:%d", headLen, extLen);
#endif
            
//            __android_log_print(ANDROID_LOG_INFO,LOG_TAG,"ext:%d.\n", extLen);
            // 删除EXT部分
            memmove(pExtHeader, (char *)pExtHeader + extLen, len - headLen - extLen);
            len -= extLen;
            // ext清零
            pRtpHeader->extension = 0;            
        }
		
#endif
#if LOG_RECEIVER
		LOGI("native : IncomingRtpPacket  clear rtp ext OK, payload:%d, len:%d, ssrc:0x%08x, seq:%d.", pRtpHeader->payload, len, ntohl(pRtpHeader->ssrc), ntohs(pRtpHeader->seq_no));
#endif        
        bool bRetransmit = false;
		ret = pReceiver->m_pReceiver->IncomingRtpPacket(
				(unsigned char*) pReceiver->m_pRtpBuf, len, bRetransmit);

#if LOG_RECEIVER
        LOGI("native : IncomingRtpPacket  call c++ interface OK, bRetransmit:%d", bRetransmit);
#endif


        if (!pReceiver->m_bRecvedFrame)
        {
            pReceiver->m_pReceiver->RequestKeyFrame();
            pReceiver->m_bRecvedFrame = true;
        }
#if LOG_RECEIVER
        LOGI("native : IncomingRtpPacket  return");
#endif

        return ret;
	}

	LOGI("native : IncomingRtpPacket is failed return -1");

	return -1;
}

/*
 * Class:     com_jiaxun_sdk_scl_media_video_LibVideoNeteq
 * Method:    ReceiverIncomingRtcpPacket
 * Signature: (J[BI)I
 */JNIEXPORT jint JNICALL Java_com_jiaxun_sdk_scl_media_video_LibVideoNeteq_ReceiverIncomingRtcpPacket(
		JNIEnv * env, jclass object, jlong handle, jbyteArray rtcpStream,
		jint len) {

#if INTERFACE_RECEIVER_DISABLE
	 return 0;
#endif

#if LOG_RECEIVER
	LOGI("native : ReceiverIncomingRtcpPacket is called");
#endif

	VideoNeteqReceiver* pReceiver = (VideoNeteqReceiver*) handle;
	if (pReceiver == NULL) {
		LOGI("native : IncomingRtpPacket   pReceiver == NULL return");
		return -1;
	}

	if (pReceiver->m_pRtcpBuf) {
		env->GetByteArrayRegion(rtcpStream, 0, len,
				(jbyte*) pReceiver->m_pRtcpBuf);

#if LOG_RECEIVER
		LOGI("native : ReceiverIncomingRtcpPacket is success");
#endif

		return pReceiver->m_pReceiver->IncomingRtcpPacket(
				(unsigned char*) pReceiver->m_pRtcpBuf, len);
	}

#if LOG_RECEIVER
	LOGI("native : ReceiverIncomingRtcpPacket is failed return -1");
#endif

	return 0;
}

/*
 * Class:     com_jiaxun_sdk_scl_media_video_LibVideoNeteq
 * Method:    GetReceiverStatistics
 * Signature: (JLjava/lang/Object;)I
 */JNIEXPORT jint JNICALL Java_com_jiaxun_sdk_scl_media_video_LibVideoNeteq_GetReceiverStatistics(
		JNIEnv * env, jclass object, jlong handle, jobject infoObject) {

#if INTERFACE_RECEIVER_DISABLE
	 return 0;
#endif

#if LOG_RECEIVER
	LOGI("native : GetReceiverStatistics is called");
#endif

	VideoNeteqReceiver* pReceiver = (VideoNeteqReceiver*) handle;
	if (pReceiver == NULL) {
		LOGI("native : IncomingRtpPacket   pReceiver == NULL return");
		return -1;
	}

	jclass recInfo = env->FindClass(
			"com/jiaxun/sdk/scl/media/video/VideoReceiverStatistics");
	if (recInfo == 0) {
		LOGI("native : find class VideoReceiverStatistics failed return ");
		return -1;
	}

	jfieldID uPeerSsrcID = env->GetFieldID(recInfo, "uPeerSsrc", "J");
	jfieldID uFractionLostID = env->GetFieldID(recInfo, "uFractionLost", "J");
	jfieldID uCumulativeLostID = env->GetFieldID(recInfo, "uCumulativeLost",
			"J");
	jfieldID uJitterID = env->GetFieldID(recInfo, "uJitter", "J");
	jfieldID uBpsID = env->GetFieldID(recInfo, "uBps", "J");

	jfieldID uFractionOutOfOrderID = env->GetFieldID(recInfo,
			"uFractionOutOfOrder", "J");
	jfieldID uOutOfOrderDiffMaxID = env->GetFieldID(recInfo,
			"uOutOfOrderDiffMax", "J");
	jfieldID uRBEUpdateEstimateID = env->GetFieldID(recInfo,
			"uRBEUpdateEstimate", "J");
	jfieldID uKeyFrameRequestCtrID = env->GetFieldID(recInfo,
			"uKeyFrameRequestCtr", "J");
	VideoReceiverStatistics* pStatistics =
			pReceiver->m_pReceiver->GetStatistics();

	env->SetLongField(infoObject, uPeerSsrcID, pStatistics->uPeerSsrc);
	env->SetLongField(infoObject, uFractionLostID, pStatistics->uFractionLost);
	env->SetLongField(infoObject, uCumulativeLostID,
			pStatistics->uCumulativeLost);
	env->SetLongField(infoObject, uJitterID, pStatistics->uJitter);
	env->SetLongField(infoObject, uBpsID, pStatistics->uBps);
	env->SetLongField(infoObject, uFractionOutOfOrderID,
			pStatistics->uFractionOutOfOrder);
	env->SetLongField(infoObject, uOutOfOrderDiffMaxID,
			pStatistics->uOutOfOrderDiffMax);
	env->SetLongField(infoObject, uRBEUpdateEstimateID,
			pStatistics->uRBEUpdateEstimate);
	env->SetLongField(infoObject, uKeyFrameRequestCtrID,
			pStatistics->uKeyFrameRequestCtr);
			
	jfieldID uFrameRateID = env->GetFieldID(recInfo,
			"uFrameRate", "J");
	jfieldID uRttID = env->GetFieldID(recInfo,
			"uRtt", "J");
			
	env->SetLongField(infoObject, uFrameRateID,
			pStatistics->uFrameRate);
	env->SetLongField(infoObject, uRttID,
			pStatistics->uRtt);

			
#if LOG_RECEIVER
	LOGI("native : GetReceiverStatistics is success");
#endif

	return 0;
}
//passRateArray 输出的数组,上层定义一个byte 数组,大小为2, passRateArray [0] 代表远端终端上行网络通过率取值范围0-100, passRateArray [1] 代表本地下行网络通过率取值范围0-100
//oInfoArray上层定义一个int 数组,大小为1, oInfoArray[0] 表示下行带宽,单位Kbps add by zl 20230809
//注意每个vnoReceive 都会返回一个本地下行网络通过率,安卓层展示时候可以取多路平均值进行展示。
// return 0 成功,其他错误
JNIEXPORT jint JNICALL Java_com_jiaxun_sdk_scl_media_video_LibVideoNeteq_GetReceiverNetworkStatus(
		JNIEnv * env, jclass object, jlong handle, jbyteArray passRateArray, jintArray oInfoArray) {

	VideoNeteqReceiver* pReceiver = (VideoNeteqReceiver*) handle;
	if (pReceiver == NULL) {
		LOGI("native : GetReceiverNetworkStatus   pReceiver == NULL return");
		return -1;
	}
	jint length = env->GetArrayLength(passRateArray);
	if(length < 2)
	{
		return -2;
	}
	int32_t iPassRateRemote = 0;
	int32_t iPassRateLocal = 0;
	int32_t iDownstreamBandWidth = 0;
	pReceiver->m_pReceiver->GetNetworkStatus(iPassRateRemote, iPassRateLocal, iDownstreamBandWidth);
	jbyte c_array[2];
	
	jint c_array_size=2;
	c_array[0] = iPassRateRemote; // 计算得出,代表预估的远端的上行网络通过率
	c_array[1] = iPassRateLocal; // 计算得出, 代表预估本地下行网络通过率
	env->SetByteArrayRegion(passRateArray,0,c_array_size,c_array);
	
	env->SetIntArrayRegion(oInfoArray,0,1, &iDownstreamBandWidth);
	return 0;
}

// add by zl 20230912 返回预估视频延迟时间
JNIEXPORT jint JNICALL Java_com_jiaxun_sdk_scl_media_video_LibVideoNeteq_GetExtraDelay(
		JNIEnv * env, jclass object, jlong handle) {

	VideoNeteqReceiver* pReceiver = (VideoNeteqReceiver*) handle;
	if (pReceiver == NULL) {
		LOGI("native : GetExtraDelay   pReceiver == NULL return");
		return -1;
	}
	
	VideoReceiverStatistics* pStatistics =
		pReceiver->m_pReceiver->GetStatistics();
			
	int32_t tuFractionLost = pStatistics->uFractionLost;
    int32_t tuRtt = pStatistics->uRtt;
    tuRtt = tuRtt < 30 ? 30 : tuRtt;
    int32_t tDelay = 0.133f * tuFractionLost * tuRtt; 
    return tDelay;
}


/*
 * Class:     com_jiaxun_sdk_scl_media_video_LibVideoNeteq
 * Method:    CreateVideoNetOptimizationSender
 * Signature: (IBJLjava/lang/Object;)J
 * eEstimateMethod 码率估算方法 0 未知, 1 GCC , 2 BWE
 * iTransportSequenceNumberID 协商得到的NumberID
 * iStreamType 判断主子码流 0 主码流 1 子码流 其他未定义行为目前统统按主码流处理  jint iStreamType
 * nEncType 区分264 和265  0 表示264, 1 表示 265
 * iDuration 计算实时带宽时长默认2000ms,推荐2000整数倍,<=10000 add by zl 20230809
 */JNIEXPORT jlong JNICALL Java_com_jiaxun_sdk_scl_media_video_LibVideoNeteq_CreateVideoNetOptimizationSender(
		JNIEnv * env, jclass object, jint nType, jbyte uPaylaodType,
		jlong uStartBitrate, jint uLowestPercentage, jint uResolutionNum, jobject senderCallback, jbyte uJxredPayloadType,
		jint eEstimateMethod, jint iTransportSequenceNumberID,jint iStreamType, jint nEncType,  jint iDuration) {

#if LOG_SENDER
	LOGI("native : CreateVideoNetOptimizationSender is called");
#endif

	VideoNeteqSender* pSender = new VideoNeteqSender();

	pSender->m_pSenderCallback = new SenderCallback(env, object,
			senderCallback, pSender, uResolutionNum);
	if (pSender->m_pSenderCallback == NULL) {
		LOGI("native : Create Sender:: new SenderCallback failed");
		delete pSender;
		return 0;
	}

	LOGI("CreateVideoNetOptimizationSender callback: %p, bitrate:%lld, per:%d, resolution:%d iStreamType: %d", 
	    pSender->m_pSenderCallback, uStartBitrate, uLowestPercentage, uResolutionNum, iStreamType);

    VideoNetOptimizationCodecType peCodecType[1];
    uint8_t puPaylaodType[1];
    peCodecType[0] = (VideoNetOptimizationCodecType)nEncType;
    puPaylaodType[0] = uPaylaodType;

	VideoNetOptimizationSenderParameter oParameter;
	// add by zl 20210909 替换变量
	oParameter.m_eProtectionMethod = (VideoNetOptimizationProtectionMethod) nType;
	//oParameter.peCodecType = peCodecType;
	oParameter.peCodecType[0] = peCodecType[0];
	// add by zl 20210909 
    oParameter.m_uLowestBitratePercentage = uLowestPercentage;
    oParameter.m_uResolutionNum = uResolutionNum;
	oParameter.m_uStartBitrate = uStartBitrate/1000;
	oParameter.pCallback = pSender->m_pSenderCallback;
    //oParameter.puPaylaodType = puPaylaodType;
	oParameter.m_PaylaodType[0] = puPaylaodType[0];
    oParameter.m_uPayloadNum = 1;
	// add by zl 20210909 新增参数
	oParameter.m_eRemoteBitrateEstimateMethod = (VnoRemoteBitrateEstimateMethod)eEstimateMethod;
    oParameter.m_TransportSequenceNumberID = iTransportSequenceNumberID;
	oParameter.m_iUpdateOnBitRateType = 2; // add by zl 2023/2/13 非立即更新
	
	oParameter.m_iDuration = iDuration;
	if (1 == iStreamType)//子码流提高冗余比例 add by zl 20211214
	{
		oParameter.m_uRscodeM[RsMLowLost] = 12;
		oParameter.m_uRscodeM[RsMMidLost] = 12;
		oParameter.m_uRscodeM[RsMHighLost] = 12;
	}
	
    if (oParameter.m_eProtectionMethod == VNO_ProtectionMethodJxredOnly
        || oParameter.m_eProtectionMethod == VNO_ProtectionMethodHybridNackAndJxred)
    {
        oParameter.m_uJxredType = uJxredPayloadType;
    }

	pSender->m_pSender =
			VideoNetOptimizationSender::pCreateVideoNetOptimizationSender(
					oParameter);

	LOGI("CreateVideoNetOptimizationSender : %p", pSender->m_pSender);

	if (pSender->m_pSender == NULL) {
		LOGI("native : Create Sender:: new m_pSender failed");
		delete pSender;
		pSender = NULL;
		return 0;
	}

#if LOG_SENDER
	LOGI("native : create sender success");
#endif

	LOGI("native : JNI_OnLoad VNO2 version:%s \n", str_version);
	return (jlong) pSender;
}

//oInfoArray上层定义一个int 数组,大小为1, oInfoArray[0] 表示上行带宽,单位Kbps add by zl 20230809

JNIEXPORT jint JNICALL Java_com_jiaxun_sdk_scl_media_video_LibVideoNeteq_GetSenderNetworkStatus(
		JNIEnv * env, jclass object, jlong handle,jintArray oInfoArray)
		{

    VideoNeteqSender* pSender = (VideoNeteqSender*) handle;
    if (!(pSender && pSender->m_pSender))
    {

        LOGI("native : LibVideoNeteq_GetSenderNetworkStatus failed.");
        return -1;
    }
	int32_t iUpstreamBandwidth = 0;
    pSender->m_pSender->GetNetworkStatus(iUpstreamBandwidth);
	
    env->SetIntArrayRegion(oInfoArray,0,1, &iUpstreamBandwidth);
    return 0;
			
}
JNIEXPORT jlong JNICALL Java_com_jiaxun_sdk_scl_media_video_LibVideoNeteq_SetMtu(JNIEnv * env, jclass object, jlong handle, jlong uMtu)
{
#if LOG_SENDER
    LOGI("native : LibVideoNeteq_SetMtu is called");
#endif

    VideoNeteqSender* pSender = (VideoNeteqSender*) handle;

    LOGI("DestroyVideoNetOptimizationSender : %p", pSender->m_pSender);

    if (!(pSender && pSender->m_pSender))
    {
#if LOG_SENDER
        LOGI("native : LibVideoNeteq_SetMtu failed.");
#endif
        return -1;
    }

    pSender->m_pSender->SetMtu(uMtu);
    
#if LOG_SENDER
    LOGI("native : LibVideoNeteq_SetMtu success");
#endif


}


/*
 * Class:     com_jiaxun_sdk_scl_media_video_LibVideoNeteq
 * Method:    DestroyVideoNetOptimizationSender
 * Signature: (J)V
 */JNIEXPORT void JNICALL Java_com_jiaxun_sdk_scl_media_video_LibVideoNeteq_DestroyVideoNetOptimizationSender(
		JNIEnv * env, jclass object, jlong handle) {

#if LOG_SENDER
	LOGI("native : DestroyVideoNetOptimizationSender is called");
#endif

	VideoNeteqSender* pSender = (VideoNeteqSender*) handle;

	LOGI("DestroyVideoNetOptimizationSender : %p", pSender->m_pSender);

	if (pSender) {
		delete pSender;
		pSender = NULL;
	}

#if LOG_SENDER
	LOGI("native : delete sender success");
#endif

}

/*
 * Class:     com_jiaxun_sdk_scl_media_video_LibVideoNeteq
 * Method:    AddVideoReceiver
 * Signature: (JJ)V
 */JNIEXPORT void JNICALL Java_com_jiaxun_sdk_scl_media_video_LibVideoNeteq_AddVideoReceiver(
		JNIEnv * env, jclass object, jlong sender, jlong receiver) {

#if INTERFACE_SENDER_DISABLE
	 return ;
#endif

#if LOG_SENDER
	LOGI("native : AddVideoReceiver is called");
#endif

	if (sender != 0 && receiver != 0) {
		VideoNeteqSender* pSender = (VideoNeteqSender*) sender;
		VideoNeteqReceiver* pReceiver = (VideoNeteqReceiver*) receiver;
		// add by zl 20210909 没有这个函数了
		//pSender->m_pSender->AddVideoReceiver(pReceiver->m_pReceiver);

		LOGI("native : AddVideoReceiver is success");
		return;
	}

	if (sender == 0) {
		LOGI("native : AddVideoReceiver : sender is 0 failed");
	}

	if (receiver == 0) {
		LOGI("native : AddVideoReceiver : receiver is 0 failed");
	}
	return;
}

/*
 * Class:     com_jiaxun_sdk_scl_media_video_LibVideoNeteq
 * Method:    RemoveVideoReceiver
 * Signature: (JJ)V
 */JNIEXPORT void JNICALL Java_com_jiaxun_sdk_scl_media_video_LibVideoNeteq_RemoveVideoReceiver(
		JNIEnv * env, jclass object, jlong sender, jlong receiver) {

#if INTERFACE_SENDER_DISABLE
	 return ;
#endif

#if LOG_SENDER
	LOGI("native : RemoveVideoReceiver is called");
#endif

	if (sender != 0 && receiver != 0) {
		VideoNeteqSender* pSender = (VideoNeteqSender*) sender;
		VideoNeteqReceiver* pReceiver = (VideoNeteqReceiver*) receiver;
		// add by zl 20210909 没有这个函数了
		//pSender->m_pSender->RemoveVideoReceiver(pReceiver->m_pReceiver);
		LOGI("native : RemoveVideoReceiver is success");
		return;
	}

	if (sender == 0) {
		LOGI("native : RemoveVideoReceiver : sender is 0");
	}

	if (receiver == 0) {
		LOGI("native : RemoveVideoReceiver : receiver is 0");
	}
	return;
}

/*
 * Class:     com_jiaxun_sdk_scl_media_video_LibVideoNeteq
 * Method:    ClearVideoReceivers
 * Signature: (J)V
 */JNIEXPORT void JNICALL Java_com_jiaxun_sdk_scl_media_video_LibVideoNeteq_ClearVideoReceivers(
		JNIEnv * env, jclass object, jlong handle) {
#if INTERFACE_SENDER_DISABLE
	 return ;
#endif

#if LOG_SENDER
	LOGI("native : ClearVideoReceivers is called");
#endif

	VideoNeteqSender* pSender = (VideoNeteqSender*) handle;

	if (pSender) {
		// add by zl 20210909 没有这个函数了
		//pSender->m_pSender->ClearVideoReceivers();
	}

	LOGI(" ClearVideoReceivers is success");
	return;
}

//这个方法有问题
/**
 * pFragmentation 没有用到
 */

/*
 * Class:     com_jiaxun_sdk_scl_media_video_LibVideoNeteq
 * Method:    IncomingVideoFrame
 * Signature: (JIJ[BILjava/lang/Object;Z)I
 */JNIEXPORT jint JNICALL Java_com_jiaxun_sdk_scl_media_video_LibVideoNeteq_IncomingVideoFrame(
		JNIEnv * env, jclass object, jlong handle, jint nFrameType,
		jlong uTimeStamp, jbyteArray h264Stream, jint len,
		jobject pFragmentation, jboolean bRtp) {

#if INTERFACE_SENDER_DISABLE
	 return 0;
#endif

#if LOG_SENDER
	LOGI("native : IncomingVideoFrame is called");
#endif

	VideoNeteqSender* pSender = (VideoNeteqSender*) handle;
	char* pFind = NULL;
	char* pBegin = NULL;
	char* pSecond = NULL;
	char* pBuf = NULL;
	int nLen = 0;
	int uNalNum = 0;
	int nBufSize = 0;
	int nOffSet = 3;

	//取出h264数据
	if (pSender->m_pH264Buf == NULL)
		return -1;
	env->GetByteArrayRegion(h264Stream, 0, len, (jbyte*) pSender->m_pH264Buf);

	pBuf = pSender->m_pH264Buf;
	pBegin = pSender->m_pH264Buf;
	nLen = len;
	nBufSize = len;
	while (true) {
		if (!FindTag(pBegin, nLen, pFind))
			break;

		if (FindTag(pBegin + nOffSet, nLen - nOffSet, pSecond)) {
			pSender->m_pFragmentationInfos[uNalNum].uOffset = (pFind - pBuf)
					+ nOffSet;
			pSender->m_pFragmentationInfos[uNalNum].uLen = (pSecond - pFind)
					- nOffSet;
			if ((pSecond - pBuf > 1) && (pSecond[-1] == 0)) {
				pSender->m_pFragmentationInfos[uNalNum].uLen -= 1;
			}
			nLen = nLen - (pSecond - pBuf);
			pBegin = pSecond;
			uNalNum++;
		} else {
			pSender->m_pFragmentationInfos[uNalNum].uOffset = (pFind - pBuf)
					+ nOffSet;
			pSender->m_pFragmentationInfos[uNalNum].uLen = nBufSize
					- (pFind - pBuf) - nOffSet;
			uNalNum++;
			break;
		}
	}
	VNO_RTPFragmentationHeader oFragmentations;
	VideoNetOptimizationFrameType eFrameType;

	oFragmentations.VerifyAndAllocateFragmentationHeader(uNalNum);

	for (unsigned long i = 0; i < uNalNum; i++) {
		oFragmentations.fragmentationOffset[i] =
				pSender->m_pFragmentationInfos[i].uOffset;
		oFragmentations.fragmentationLength[i] =
				pSender->m_pFragmentationInfos[i].uLen;
		oFragmentations.fragmentationPlType[i] = 0;
		oFragmentations.fragmentationTimeDiff[i] = 0;
	}
	pSender->m_pSender->IncomingVideoFrame(
			(VideoNetOptimizationFrameType) nFrameType,
			(unsigned long) uTimeStamp, (unsigned char*) pSender->m_pH264Buf,
			len, &oFragmentations, bRtp);

#if LOG_SENDER
	LOGI(" IncomingVideoFrame is success");
#endif
	return 0;
}

/*
 * Class:     com_jiaxun_sdk_scl_media_video_LibVideoNeteq
 * Method:    SenderIncomingRTCPPacket
 * Signature: (J[BI)I
 */JNIEXPORT jint JNICALL Java_com_jiaxun_sdk_scl_media_video_LibVideoNeteq_SenderIncomingRTCPPacket(
		JNIEnv * env, jclass object, jlong handle, jbyteArray rtcpStream,
		jint len) {

#if INTERFACE_SENDER_DISABLE
	 return 0;
#endif

#if LOG_SENDER
	LOGI("native : SenderIncomingRTCPPacket is called");
#endif

	VideoNeteqSender* pSender = (VideoNeteqSender*) handle;
	if (pSender == NULL)
		return -1;

	if (pSender->m_pRtcpBuf) {
		env->GetByteArrayRegion(rtcpStream, 0, len,
				(jbyte*) pSender->m_pRtcpBuf);

//		LOGI("native : SenderIncomingRTCPPacket is success");
		return pSender->m_pSender->IncomingRTCPPacket(
				(unsigned char*) pSender->m_pRtcpBuf, len);
	}

#if LOG_SENDER
	LOGI("native : SenderIncomingRTCPPacket is failed");
#endif

	return -1;
}

/*
 * Class:     com_jiaxun_sdk_scl_media_video_LibVideoNeteq
 * Method:    GetSendertStatistics
 * Signature: (JLjava/lang/Object;)I
 */JNIEXPORT jint JNICALL Java_com_jiaxun_sdk_scl_media_video_LibVideoNeteq_GetSendertStatistics(
		JNIEnv * env, jclass object, jlong handle, jobject pObject) {
#if INTERFACE_SENDER_DISABLE
	 return 0;
#endif

#if LOG_SENDER
	LOGI("native : GetSendertStatistics is called");
#endif

	VideoNeteqSender* pSender = (VideoNeteqSender*) handle;

	if (pSender == NULL) {
		LOGI("sender is NULL return ");
		return -1;
	}
	jclass recInfo = env->FindClass(
			"com/jiaxun/sdk/scl/media/video/VideoSenderStatistics");
	if (recInfo == NULL) {
		LOGI("get sendinfo find class failed");
		return -1;
	}

	jfieldID uSSRCID = env->GetFieldID(recInfo, "uSSRC", "J");
	jfieldID uSelectedPeerSSRCId = env->GetFieldID(recInfo, "uSelectedPeerSSRC",
			"J");
	jfieldID uFrameRateID = env->GetFieldID(recInfo, "uFrameRate", "J");
	jfieldID uSentVideoRateBpsID = env->GetFieldID(recInfo, "uSentVideoRateBps",
			"J");
	jfieldID uSentNackRateBpsID = env->GetFieldID(recInfo, "uSentNackRateBps",
			"J");
	jfieldID uSentFecRateBpsID = env->GetFieldID(recInfo, "uSentFecRateBps",
			"J");
	jfieldID uForceKeyFrameCtrID = env->GetFieldID(recInfo, "uForceKeyFrameCtr",
			"J");
//	jfieldID PeerReceiverFeedbackID = env->GetFieldID(recInfo,
//			"peerReceiverFeedback",
//			"Lcom/jiaxun/sdk/scl/media/video/PeerReceiverFeedback");

//	jfieldID feedBackInfoID = env->GetFieldID(recInfo, "peerReceiverFeedback",
//			"[Lcom/jiaxun/sdk/scl/media/video/PeerReceiverFeedback");

	jfieldID nActualCountID = env->GetFieldID(recInfo, "nActualCount", "I");

	jmethodID getPeerID = env->GetMethodID(recInfo, "getPeerReceiverFeedback",
			"(I)Lcom/jiaxun/sdk/scl/media/video/PeerReceiverFeedback;");

	jmethodID setPeerID = env->GetMethodID(recInfo, "setPeerReceiverFeedback",
			"(IBSJJJ)V");
	jmethodID getPeerSizeID = env->GetMethodID(recInfo,
			"getPeerReceiverFeedbackSize", "()I");

	//env->SetLongField(pObject,uSSRCID,10);
	VideoSenderStatistics* pStatistics = pSender->m_pSender->GetStatistics();

	env->SetLongField(pObject, uSSRCID, (jlong) pStatistics->uSSRC);
	env->SetLongField(pObject, uSelectedPeerSSRCId,
			(jlong) pStatistics->uSelectedPeerSSRC);
	env->SetLongField(pObject, uFrameRateID, (jlong) pStatistics->uFrameRate);
	env->SetLongField(pObject, uSentVideoRateBpsID,
			(jlong) pStatistics->uSentVideoRateBps);
	env->SetLongField(pObject, uSentNackRateBpsID,
			(jlong) pStatistics->uSentNackRateBps);
	env->SetLongField(pObject, uSentFecRateBpsID,
			(jlong) pStatistics->uSentFecRateBps);
	env->SetLongField(pObject, uForceKeyFrameCtrID,
			(jlong) pStatistics->uForceKeyFrameCtr);
	env->SetIntField(pObject, nActualCountID,
			(jlong) pStatistics->mapReceiversInfo.size());

//	jobjectArray feedbackArray = (jobjectArray) env->GetObjectField(pObject,
//			feedBackInfoID);

	//获取class
	jclass jobjClass = env->FindClass(
			"com/jiaxun/sdk/scl/media/video/PeerReceiverFeedback");

	//创建关联的数组

	if (jobjClass == 0) {
		LOGI(" failed to get PeerReceiverFeedback class ");
		return -1;
	}

	jfieldID uFractionLostID = env->GetFieldID(jobjClass, "uFractionLost", "B");
	jfieldID uRTTID = env->GetFieldID(jobjClass, "uRTT", "S");
	jfieldID uCumulativeLostID = env->GetFieldID(jobjClass, "uCumulativeLost",
			"J");
	jfieldID ullRcvTimeID = env->GetFieldID(jobjClass, "ullRcvTime", "J");
	jfieldID uRBEID = env->GetFieldID(jobjClass, "uRBE", "J");

	LOGI("size=%d", pStatistics->mapReceiversInfo.size());
//	LOGI("begin=%p", pStatistics->mapReceiversInfo.begin());
//	LOGI("end=%p", pStatistics->mapReceiversInfo.end());

	PeerReceiverFeedback peer;

	peer.uCumulativeLost = 1;
	peer.uFractionLost = 1;
	peer.uRBE = 1;
	peer.uRTT = 1;
	peer.ullRcvTime = 1;

//	pStatistics->mapReceiversInfo.insert(
//			std::map<unsigned long, PeerReceiverFeedback>::value_type(54090102,
//					peer));
//	;

	PeerReceiverFeedbackMap::iterator it;
	for (it = pStatistics->mapReceiversInfo.begin();
			it != pStatistics->mapReceiversInfo.end(); ++it) {

//		LOGI("20 %d / %d", it, pStatistics->mapReceiversInfo.end());

		env->CallVoidMethod(pObject, setPeerID,
				(jbyte) it->second.uFractionLost, (jshort) it->second.uRTT,
				(jlong) it->second.uCumulativeLost,
				(jlong) it->second.ullRcvTime, (jlong) it->second.uRBE);

		LOGI("21");
	}

	LOGI(" GetSendertStatistics is success");
	return 0;
}

#ifdef __cplusplus
}
#endif

#endif

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值