From b70a80fe6870ef328d317bd56a827d45ac7d083b Mon Sep 17 00:00:00 2001 From: neilyhe <228429803@qq.com> Date: Mon, 17 Feb 2025 19:55:00 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0xp2p=20sdk=E7=89=88=E6=9C=AC?= =?UTF-8?q?=E5=88=B02.4.53?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sdk/video-link-android/build.gradle | 4 +- .../video/link/service/VideoBaseService.kt | 15 + sdkdemo/src/main/AndroidManifest.xml | 16 +- .../iot/explorer/link/demo/BaseActivity.kt | 3 + .../link/demo/video/VideoDeviceFragment.kt | 36 +- .../link/demo/video/VideoOptionsActivity.kt | 2 +- .../demo/video/VideoPreviewBaseActivity.kt | 76 + .../demo/video/VideoWlanDetectActivity.kt | 4 +- .../link/demo/video/nvr/VideoNvrActivity.kt | 11 +- .../cloudPlayback/event/EventModel.kt | 4 + .../cloudPlayback/event/EventPresenter.kt | 7 + .../VideoLocalPlaybackFragment.kt | 35 +- .../preview/VideoMultiPreviewActivity.kt | 818 +++++----- .../video/preview/VideoPreviewActivity.kt | 538 ++++--- .../preview/VideoPreviewMJPEGActivity.kt | 371 ++--- .../video/preview/VideoPushStreamActivity.kt | 308 ++-- .../demo/video/preview/VideoTestActivity.kt | 99 +- .../preview/VideoWithoutPropertyActivity.kt | 1384 +++++++++-------- .../video/preview/WlanVideoPreviewActivity.kt | 864 +++++----- .../res/layout/activity_video_preview.xml | 1 + 20 files changed, 2489 insertions(+), 2107 deletions(-) create mode 100644 sdkdemo/src/main/java/com/tencent/iot/explorer/link/demo/video/VideoPreviewBaseActivity.kt diff --git a/sdk/video-link-android/build.gradle b/sdk/video-link-android/build.gradle index b05002afc..5d80b67d5 100644 --- a/sdk/video-link-android/build.gradle +++ b/sdk/video-link-android/build.gradle @@ -10,7 +10,7 @@ android { buildToolsVersion "30.0.2" defaultConfig { - minSdkVersion 24 + minSdkVersion 21 targetSdkVersion 29 versionCode 1 versionName "1.0" @@ -53,7 +53,7 @@ dependencies { // changing = true // } // api 'com.tencent.iot.thirdparty.android:xp2p-sdk:2.4.23' - api 'com.tencent.iot.thirdparty.android:xp2p-sdk:2.4.52' + api 'com.tencent.iot.thirdparty.android:xp2p-sdk:2.4.53' api 'com.tencent.iot.thirdparty.android:media-server:1.0.7' api 'io.github.sundoggynew:iot-soundtouch:1.0.0' api 'io.github.sundoggynew:iot-voice-changer:1.0.0' diff --git a/sdk/video-link-android/src/main/java/com/tencent/iot/video/link/service/VideoBaseService.kt b/sdk/video-link-android/src/main/java/com/tencent/iot/video/link/service/VideoBaseService.kt index ea78d897c..8dba20634 100644 --- a/sdk/video-link-android/src/main/java/com/tencent/iot/video/link/service/VideoBaseService.kt +++ b/sdk/video-link-android/src/main/java/com/tencent/iot/video/link/service/VideoBaseService.kt @@ -160,6 +160,21 @@ open class VideoBaseService(secretId: String, secretKey: String) { param, headerParams, callback, VideoRequestCode.video_describe_product) } + fun getDeviceXp2pInfo(productId: String,deviceName:String, callback: VideoCallback) { + var headerParams = videoCommonHeaderParams("DescribeP2PInfo", "2021-11-25") + val param = TreeMap() + param["ProductId"] = productId + param["DeviceName"] = deviceName + + val authorization = sign(VideoHttpUtil.VIDEO_SERVICE, headerParams, param) + if (authorization != null) { + headerParams["Authorization"] = authorization + } + basePost(VideoHttpUtil.VIDEO_SERVICE + VideoHttpUtil.REST_HOST_URL, + param, headerParams, callback, VideoRequestCode.video_describe_product) + } + + fun getVideoBaseUrl(productId: String, devName: String, callback: VideoCallback) { var headerParams = videoCommonHeaderParams("DescribeCloudStorageVideoUrl") val param = TreeMap() diff --git a/sdkdemo/src/main/AndroidManifest.xml b/sdkdemo/src/main/AndroidManifest.xml index bdf4dd159..eee588ddb 100644 --- a/sdkdemo/src/main/AndroidManifest.xml +++ b/sdkdemo/src/main/AndroidManifest.xml @@ -58,25 +58,25 @@ - + + - + + + - + + + diff --git a/sdkdemo/src/main/java/com/tencent/iot/explorer/link/demo/BaseActivity.kt b/sdkdemo/src/main/java/com/tencent/iot/explorer/link/demo/BaseActivity.kt index 61ccace45..5a371a3d9 100644 --- a/sdkdemo/src/main/java/com/tencent/iot/explorer/link/demo/BaseActivity.kt +++ b/sdkdemo/src/main/java/com/tencent/iot/explorer/link/demo/BaseActivity.kt @@ -42,11 +42,14 @@ abstract class BaseActivity : AppCompatActivity() { abstract fun getContentView(): Int + open fun performInitView(){} + abstract fun initView() abstract fun setListener() open fun startHere() { + performInitView() initView() setListener() } diff --git a/sdkdemo/src/main/java/com/tencent/iot/explorer/link/demo/video/VideoDeviceFragment.kt b/sdkdemo/src/main/java/com/tencent/iot/explorer/link/demo/video/VideoDeviceFragment.kt index 70d1df804..cae26f4c5 100644 --- a/sdkdemo/src/main/java/com/tencent/iot/explorer/link/demo/video/VideoDeviceFragment.kt +++ b/sdkdemo/src/main/java/com/tencent/iot/explorer/link/demo/video/VideoDeviceFragment.kt @@ -111,7 +111,7 @@ class VideoDeviceFragment : BaseFragment(), VideoCallback, DevsAdapter.OnItemCli } } - VideoMultiPreviewActivity.startMultiPreviewActivity(context, allUrl) +// VideoMultiPreviewActivity.startMultiPreviewActivity(context, allUrl) } } @@ -137,24 +137,24 @@ class VideoDeviceFragment : BaseFragment(), VideoCallback, DevsAdapter.OnItemCli 0 -> { VideoPreviewActivity.startPreviewActivity(context, dev) } 1 -> { VideoPlaybackActivity.startPlaybackActivity(context, dev) } 2 -> { VideoPreviewMJPEGActivity.startPreviewActivity(context, dev) } - 3 -> { VideoWithoutPropertyActivity.startPreviewActivity(context, dev) } +// 3 -> { VideoWithoutPropertyActivity.startPreviewActivity(context, dev) } 4 -> { VideoPushStreamActivity.startPreviewActivity(context, dev) } - 5 -> { - val multipleChannelChooseDialog = MultipleChannelChooseDialog(context) - multipleChannelChooseDialog.show() - multipleChannelChooseDialog.setOnDismisListener { selectChannels -> - var allUrl = ArrayList() - for (i in 0 until selectChannels.size) { - var device = DevUrl2Preview() - device.devName = dev.DeviceName - device.Status = 1 - device.channel = selectChannels[i] - allUrl.add(device) - } - - VideoMultiPreviewActivity.startMultiPreviewActivity(context, allUrl) - } - } +// 5 -> { +// val multipleChannelChooseDialog = MultipleChannelChooseDialog(context) +// multipleChannelChooseDialog.show() +// multipleChannelChooseDialog.setOnDismisListener { selectChannels -> +// var allUrl = ArrayList() +// for (i in 0 until selectChannels.size) { +// var device = DevUrl2Preview() +// device.devName = dev.DeviceName +// device.Status = 1 +// device.channel = selectChannels[i] +// allUrl.add(device) +// } +// +// VideoMultiPreviewActivity.startMultiPreviewActivity(context, allUrl) +// } +// } } } } diff --git a/sdkdemo/src/main/java/com/tencent/iot/explorer/link/demo/video/VideoOptionsActivity.kt b/sdkdemo/src/main/java/com/tencent/iot/explorer/link/demo/video/VideoOptionsActivity.kt index a58a73aa9..575733626 100644 --- a/sdkdemo/src/main/java/com/tencent/iot/explorer/link/demo/video/VideoOptionsActivity.kt +++ b/sdkdemo/src/main/java/com/tencent/iot/explorer/link/demo/video/VideoOptionsActivity.kt @@ -15,7 +15,7 @@ class VideoOptionsActivity : VideoBaseActivity() { override fun setListener() { btn_video.setOnClickListener { jumpActivity(VideoInputAuthorizeActivity::class.java) } - btn_video_wlan.setOnClickListener { jumpActivity(VideoWlanDetectActivity::class.java) } +// btn_video_wlan.setOnClickListener { jumpActivity(VideoWlanDetectActivity::class.java) } btn_video_test.setOnClickListener { jumpActivity(VideoTestInputActivity::class.java) } } diff --git a/sdkdemo/src/main/java/com/tencent/iot/explorer/link/demo/video/VideoPreviewBaseActivity.kt b/sdkdemo/src/main/java/com/tencent/iot/explorer/link/demo/video/VideoPreviewBaseActivity.kt new file mode 100644 index 000000000..f04b604c2 --- /dev/null +++ b/sdkdemo/src/main/java/com/tencent/iot/explorer/link/demo/video/VideoPreviewBaseActivity.kt @@ -0,0 +1,76 @@ +package com.tencent.iot.explorer.link.demo.video + +import android.os.Bundle +import android.text.TextUtils +import com.alibaba.fastjson.JSON +import com.tencent.iot.explorer.link.demo.App +import com.tencent.iot.explorer.link.demo.VideoBaseActivity +import com.tencent.iot.explorer.link.demo.common.util.StatusBarUtil +import com.tencent.iot.explorer.link.demo.video.playback.cloudPlayback.event.EventPresenter +import com.tencent.iot.explorer.link.demo.video.playback.cloudPlayback.event.EventView +import com.tencent.iot.explorer.link.demo.video.preview.DevUrl2Preview +import com.tencent.iot.video.link.callback.VideoCallback +import com.tencent.iot.video.link.consts.VideoConst +import com.tencent.iot.video.link.service.VideoBaseService +import org.json.JSONObject +import java.util.Date + +abstract class VideoPreviewBaseActivity : VideoBaseActivity(), EventView, VideoCallback { + + protected lateinit var presenter: EventPresenter + protected var xp2pInfo: String = "" + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + checkStyle() + } + + private fun checkStyle() { + StatusBarUtil.setRootViewFitsSystemWindows(this, false) + StatusBarUtil.setTranslucentStatus(this) + if (!StatusBarUtil.setStatusBarDarkTheme(this, true)) { + StatusBarUtil.setStatusBarColor(this, 0x55000000) + } + } + + override fun performInitView() { + presenter = EventPresenter(this@VideoPreviewBaseActivity) + val bundle = intent.getBundleExtra(VideoConst.VIDEO_CONFIG) + bundle?.let { + val videoConfig = bundle.getString(VideoConst.VIDEO_CONFIG) + if (TextUtils.isEmpty(videoConfig)) return@let + + val devInfo = JSON.parseObject(videoConfig, DevUrl2Preview::class.java) + devInfo?.let { + presenter.setDeviceName(it.devName) + presenter.setChannel(it.channel) + } + } + + App.data.accessInfo?.let { + presenter.setAccessId(it.accessId) + presenter.setAccessToken(it.accessToken) + presenter.setProductId(it.productId) + presenter.getEventsData(Date()) + } + } + + protected fun getDeviceP2PInfo() { + App.data.accessInfo?.let { + VideoBaseService(it.accessId, it.accessToken).getDeviceXp2pInfo( + it.productId, + presenter.getDeviceName(), + this + ) + } + } + + abstract fun updateXp2pInfo(xp2pInfo: String) + + override fun success(response: String?, reqCode: Int) { + response?.let { + val responseObject = JSONObject(it).getJSONObject("Response") + xp2pInfo = responseObject.getString("P2PInfo") + } + updateXp2pInfo(xp2pInfo) + } +} \ No newline at end of file diff --git a/sdkdemo/src/main/java/com/tencent/iot/explorer/link/demo/video/VideoWlanDetectActivity.kt b/sdkdemo/src/main/java/com/tencent/iot/explorer/link/demo/video/VideoWlanDetectActivity.kt index 48a88768d..371a87580 100644 --- a/sdkdemo/src/main/java/com/tencent/iot/explorer/link/demo/video/VideoWlanDetectActivity.kt +++ b/sdkdemo/src/main/java/com/tencent/iot/explorer/link/demo/video/VideoWlanDetectActivity.kt @@ -11,7 +11,7 @@ import com.tencent.iot.explorer.link.core.utils.SharePreferenceUtil import com.tencent.iot.explorer.link.demo.App import com.tencent.iot.explorer.link.demo.R import com.tencent.iot.explorer.link.demo.VideoBaseActivity -import com.tencent.iot.explorer.link.demo.video.preview.WlanVideoPreviewActivity +//import com.tencent.iot.explorer.link.demo.video.preview.WlanVideoPreviewActivity import com.tencent.iot.video.link.callback.OnWlanDevicesDetectedCallback import com.tencent.iot.video.link.consts.VideoConst import com.tencent.iot.video.link.entity.DeviceServerInfo @@ -79,7 +79,7 @@ class VideoWlanDetectActivity : VideoBaseActivity() , CoroutineScope by MainScop dev.DeviceName = datas.get(pos).deviceName dev.Channel = 0 dev.Status = 1 - WlanVideoPreviewActivity.startPreviewActivity(this@VideoWlanDetectActivity, dev) +// WlanVideoPreviewActivity.startPreviewActivity(this@VideoWlanDetectActivity, dev) } } diff --git a/sdkdemo/src/main/java/com/tencent/iot/explorer/link/demo/video/nvr/VideoNvrActivity.kt b/sdkdemo/src/main/java/com/tencent/iot/explorer/link/demo/video/nvr/VideoNvrActivity.kt index d47714b9e..c94dbb377 100644 --- a/sdkdemo/src/main/java/com/tencent/iot/explorer/link/demo/video/nvr/VideoNvrActivity.kt +++ b/sdkdemo/src/main/java/com/tencent/iot/explorer/link/demo/video/nvr/VideoNvrActivity.kt @@ -17,10 +17,11 @@ import com.tencent.iot.explorer.link.demo.video.playback.VideoPlaybackActivity import com.tencent.iot.explorer.link.demo.video.DevInfo import com.tencent.iot.explorer.link.demo.video.preview.DevUrl2Preview import com.tencent.iot.explorer.link.demo.video.VideoProductInfo -import com.tencent.iot.explorer.link.demo.video.preview.VideoMultiPreviewActivity +//import com.tencent.iot.explorer.link.demo.video.preview.VideoMultiPreviewActivity import com.tencent.iot.explorer.link.demo.video.preview.VideoPreviewActivity import com.tencent.iot.video.link.consts.VideoConst import com.tencent.xnet.XP2P +import com.tencent.xnet.XP2PAppConfig import com.tencent.xnet.XP2PCallback import kotlinx.android.synthetic.main.fragment_video_device.* import kotlinx.android.synthetic.main.title_layout.* @@ -87,9 +88,9 @@ class VideoNvrActivity : VideoBaseActivity(), DevsAdapter.OnItemClicked, XP2PCal App.data.accessInfo?.let { countDownLatch = CountDownLatch(1) - var started = XP2P.startServiceWithXp2pInfo(this@VideoNvrActivity, "${it.productId}/${devName}", - it.productId, devName, "") - if (started != 0) return@launch + XP2P.startService(this@VideoNvrActivity, "${it.productId}/${devName}", + it.productId, devName, XP2PAppConfig() + ) countDownLatch.await(5, TimeUnit.SECONDS) queryNvrDev(devName) @@ -112,7 +113,7 @@ class VideoNvrActivity : VideoBaseActivity(), DevsAdapter.OnItemClicked, XP2PCal } } - VideoMultiPreviewActivity.startMultiPreviewActivity(this@VideoNvrActivity, allUrl) +// VideoMultiPreviewActivity.startMultiPreviewActivity(this@VideoNvrActivity, allUrl) } } diff --git a/sdkdemo/src/main/java/com/tencent/iot/explorer/link/demo/video/playback/cloudPlayback/event/EventModel.kt b/sdkdemo/src/main/java/com/tencent/iot/explorer/link/demo/video/playback/cloudPlayback/event/EventModel.kt index 17792822f..ce4cdf30f 100644 --- a/sdkdemo/src/main/java/com/tencent/iot/explorer/link/demo/video/playback/cloudPlayback/event/EventModel.kt +++ b/sdkdemo/src/main/java/com/tencent/iot/explorer/link/demo/video/playback/cloudPlayback/event/EventModel.kt @@ -112,6 +112,10 @@ class EventModel(view: EventView) : ParentModel(view), VideoCallback this.productId = productId } + fun getProductId() : String { + return this.productId + } + fun setDeviceName(deviceName : String) { this.deviceName = deviceName } diff --git a/sdkdemo/src/main/java/com/tencent/iot/explorer/link/demo/video/playback/cloudPlayback/event/EventPresenter.kt b/sdkdemo/src/main/java/com/tencent/iot/explorer/link/demo/video/playback/cloudPlayback/event/EventPresenter.kt index 9360ad98b..d78afaf25 100644 --- a/sdkdemo/src/main/java/com/tencent/iot/explorer/link/demo/video/playback/cloudPlayback/event/EventPresenter.kt +++ b/sdkdemo/src/main/java/com/tencent/iot/explorer/link/demo/video/playback/cloudPlayback/event/EventPresenter.kt @@ -23,6 +23,13 @@ class EventPresenter : ParentPresenter { model?.setProductId(productId) } + fun getProductId() : String { + model?.let { + return it.getProductId() + } + return "" + } + fun setDeviceName(deviceName : String) { model?.setDeviceName(deviceName) } diff --git a/sdkdemo/src/main/java/com/tencent/iot/explorer/link/demo/video/playback/localPlayback/VideoLocalPlaybackFragment.kt b/sdkdemo/src/main/java/com/tencent/iot/explorer/link/demo/video/playback/localPlayback/VideoLocalPlaybackFragment.kt index 47f9162c8..de00fcb32 100644 --- a/sdkdemo/src/main/java/com/tencent/iot/explorer/link/demo/video/playback/localPlayback/VideoLocalPlaybackFragment.kt +++ b/sdkdemo/src/main/java/com/tencent/iot/explorer/link/demo/video/playback/localPlayback/VideoLocalPlaybackFragment.kt @@ -26,6 +26,7 @@ import com.tencent.iot.explorer.link.demo.video.DevInfo import com.tencent.iot.explorer.link.demo.video.playback.* import com.tencent.iot.explorer.link.demo.video.utils.ToastDialog import com.tencent.xnet.XP2P +import com.tencent.xnet.XP2PAppConfig import com.tencent.xnet.XP2PCallback import kotlinx.android.synthetic.main.activity_video_preview.* import kotlinx.android.synthetic.main.fragment_video_cloud_playback.* @@ -623,16 +624,13 @@ class VideoLocalPlaybackFragment : VideoPlaybackBaseFragment(), TextureView.Surf Thread(Runnable { var id = "${App.data.accessInfo?.productId}/${devInfo?.DeviceName}" - var started = XP2P.startServiceWithXp2pInfo(context, + var started = XP2P.startService(context, id, - App.data.accessInfo?.productId, devInfo?.DeviceName, "" + App.data.accessInfo?.productId, devInfo?.DeviceName, XP2PAppConfig() ) - if (started != 0) { launch(Dispatchers.Main) { var errInfo = getString(R.string.error_with_code, id, started.toString()) Toast.makeText(context, errInfo, Toast.LENGTH_SHORT).show() - } - return@Runnable } devInfo?.let { @@ -667,19 +665,20 @@ class VideoLocalPlaybackFragment : VideoPlaybackBaseFragment(), TextureView.Surf // 发现断开尝试恢复视频,每隔一秒尝试一次 XP2P.stopService(id) - while (XP2P.startServiceWithXp2pInfo(context, - id, - App.data.accessInfo!!.productId, - devInfo?.DeviceName, - "" - ) != 0 - ) { - XP2P.stopService(id) - synchronized(objectLock) { - objectLock.wait(1000) - } - Log.d(TAG, "id=${id}, try to call startServiceWithXp2pInfo") - } +// XP2P.startService(context, +// id, +// App.data.accessInfo!!.productId, +// devInfo?.DeviceName, +// XP2PAppConfig() +// ) +// while ( +// ) { +// XP2P.stopService(id) +// synchronized(objectLock) { +// objectLock.wait(1000) +// } +// Log.d(TAG, "id=${id}, try to call startServiceWithXp2pInfo") +// } Log.d(TAG, "id=${id}, call startServiceWithXp2pInfo successed") countDownLatchs.put(id!!, tmpCountDownLatch) diff --git a/sdkdemo/src/main/java/com/tencent/iot/explorer/link/demo/video/preview/VideoMultiPreviewActivity.kt b/sdkdemo/src/main/java/com/tencent/iot/explorer/link/demo/video/preview/VideoMultiPreviewActivity.kt index 1c8ec0db4..5c8350f76 100644 --- a/sdkdemo/src/main/java/com/tencent/iot/explorer/link/demo/video/preview/VideoMultiPreviewActivity.kt +++ b/sdkdemo/src/main/java/com/tencent/iot/explorer/link/demo/video/preview/VideoMultiPreviewActivity.kt @@ -1,370 +1,448 @@ -package com.tencent.iot.explorer.link.demo.video.preview - -import android.content.Context -import android.content.Intent -import android.content.pm.ActivityInfo -import android.content.res.Configuration -import android.graphics.SurfaceTexture -import android.os.Bundle -import android.text.TextUtils -import android.util.Log -import android.view.Surface -import android.view.TextureView -import android.view.View -import android.widget.Toast -import androidx.recyclerview.widget.GridLayoutManager -import androidx.recyclerview.widget.LinearLayoutManager -import com.alibaba.fastjson.JSON -import com.alibaba.fastjson.JSONArray -import com.tencent.iot.explorer.link.demo.App -import com.tencent.iot.explorer.link.demo.BaseActivity -import com.tencent.iot.explorer.link.demo.R -import com.tencent.iot.explorer.link.demo.VideoBaseActivity -import com.tencent.iot.explorer.link.demo.core.activity.* -import com.tencent.iot.explorer.link.demo.video.Command -import com.tencent.iot.video.link.consts.VideoConst -import com.tencent.xnet.XP2P -import com.tencent.xnet.XP2PCallback -import kotlinx.android.synthetic.main.activity_video_multi_preview.* -import kotlinx.coroutines.* -import tv.danmaku.ijk.media.player.IjkMediaPlayer -import java.lang.Runnable -import java.util.* -import java.util.concurrent.ConcurrentHashMap -import java.util.concurrent.CopyOnWriteArrayList -import java.util.concurrent.CountDownLatch -import kotlin.collections.ArrayList - - -private var countDownLatchs : MutableMap = ConcurrentHashMap() -private var allDevUrl: MutableList = CopyOnWriteArrayList() - -class VideoMultiPreviewActivity : VideoBaseActivity(), XP2PCallback, CoroutineScope by MainScope() { - lateinit var gridLayoutManager : GridLayoutManager - lateinit var linearLayoutManager : LinearLayoutManager - private var adapter : DevPreviewAdapter? = null - private var tag = VideoMultiPreviewActivity::class.simpleName - private var orientation = true - - override fun getContentView(): Int { - return R.layout.activity_video_multi_preview - } - - override fun onResume() { - super.onResume() - adapter = DevPreviewAdapter(this@VideoMultiPreviewActivity, allDevUrl) - gl_video.layoutManager = linearLayoutManager - gl_video.adapter = adapter - switchOrientation(orientation) - playAll() - } - - override fun initView() { - App.data.accessInfo?.let { - XP2P.setQcloudApiCred(it.accessId, it.accessToken) - XP2P.setCallback(this) - } - - gridLayoutManager = GridLayoutManager(this@VideoMultiPreviewActivity, 2) - linearLayoutManager = LinearLayoutManager(this@VideoMultiPreviewActivity) - intent.getBundleExtra(VideoConst.VIDEO_URLS)?.let { - it.getString(VideoConst.VIDEO_URLS)?.let { - try { - allDevUrl = JSONArray.parseArray(it, DevUrl2Preview::class.java) - var column = 2 - if (allDevUrl.size <= 1) column = 1 // 当只有一个元素的时候,网格只有一列 - gridLayoutManager = GridLayoutManager(this@VideoMultiPreviewActivity, column) - linearLayoutManager = LinearLayoutManager(this@VideoMultiPreviewActivity) - } catch (e : Exception) { - e.printStackTrace() - } - } - } - switchOrientation(true) - rg_orientation.check(radio_orientation_v.id) - } - - private fun playAll() { - if (App.data.accessInfo == null) return - - for (i in 0 until allDevUrl.size) { - if (allDevUrl[i].Status != 1) continue - - var player = IjkMediaPlayer() - allDevUrl[i].surfaceTextureListener = object : TextureView.SurfaceTextureListener { - override fun onSurfaceTextureAvailable(surface: SurfaceTexture?, width: Int, height: Int) { - surface?.let { - allDevUrl[i].surface = Surface(surface) - player.setSurface(allDevUrl[i].surface) - } - } - - override fun onSurfaceTextureSizeChanged(surface: SurfaceTexture?, width: Int, height: Int) {} - override fun onSurfaceTextureDestroyed(surface: SurfaceTexture?): Boolean { return false } - override fun onSurfaceTextureUpdated(surface: SurfaceTexture?) {} - } - - setPlayerSource(player, allDevUrl[i].devName, allDevUrl[i].channel) - allDevUrl.get(i).player = player - } - } - - private fun setPlayerSource(player: IjkMediaPlayer, devName: String, channel: Int) { - Thread(Runnable { - var id = "${App.data.accessInfo!!.productId}/${devName}" - var started = XP2P.startServiceWithXp2pInfo(this@VideoMultiPreviewActivity, id, - App.data.accessInfo!!.productId, devName, "") - // 已经启动过,或者第一次启动,继续进行 - if (started != 0 && started != -1011) { - launch(Dispatchers.Main) { - var errInfo = getString(R.string.error_with_code, id, started.toString()) - Toast.makeText(this@VideoMultiPreviewActivity, errInfo, Toast.LENGTH_SHORT).show() - } - return@Runnable - } - - var tmpCountDownLatch = CountDownLatch(1) - countDownLatchs.put("${App.data.accessInfo!!.productId}/${devName}/${channel}", tmpCountDownLatch) - tmpCountDownLatch.await() - - val urlPrefix = XP2P.delegateHttpFlv("${App.data.accessInfo!!.productId}/${devName}") - if (!TextUtils.isEmpty(urlPrefix)) { - player?.let { - val url = urlPrefix + Command.getVideoHightQualityUrlSuffix(channel) - playPlayer(it, url) - keepPlayerplay("${App.data.accessInfo!!.productId}/${devName}", channel) - } - } - }).start() - } - - private fun playPlayer(player: IjkMediaPlayer, url: String) { - player.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "analyzemaxduration", 100) - player.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "probesize", 25 * 1024) - player.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "packet-buffering", 0) - player.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "start-on-prepared", 1) - player.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, "threads", 1) - player.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "sync-av-start", 0) - player.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "reconnect", 1) - player.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec",1) - player.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-auto-rotate", 1) - player.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-handle-resolution-change", 1) - - player.dataSource = url - player.prepareAsync() - player.start() - } - - override fun setListener() { - rg_orientation.setOnCheckedChangeListener { group, checkedId -> - when(checkedId) { - radio_orientation_h.id -> switchOrientation(false) - radio_orientation_v.id -> switchOrientation(true) - } - } - } - - private fun switchOrientation(orientationV : Boolean) { - if (orientationV) { - setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) - radio_orientation_v.visibility = View.GONE - radio_orientation_h.visibility = View.VISIBLE - gl_video.layoutManager = linearLayoutManager - } else { - setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) - radio_orientation_v.visibility = View.VISIBLE - radio_orientation_h.visibility = View.GONE - gl_video.layoutManager = gridLayoutManager - } - orientation = orientationV - adapter?.notifyDataSetChanged() - } - - override fun fail(msg: String?, errorCode: Int) {} - override fun commandRequest(id: String?, msg: String?) {} - - override fun xp2pEventNotify(id: String?, msg: String?, event: Int) { - Log.d(tag, "id=${id},event=${event},msg=${msg}") - if (event == 1003) { - XP2P.stopService(id) - var holders = getHolderById(id) - for (i in 0 until holders.size) { - holders.get(i).lock?.let { - synchronized(it) { - it.notify() - } - } // 唤醒守护线程 - } - launch(Dispatchers.Main) { - var content = getString(R.string.disconnected_and_reconnecting, id) - Toast.makeText(this@VideoMultiPreviewActivity, content, Toast.LENGTH_SHORT).show() - } - - } else if (event == 1004) { - launch(Dispatchers.Main) { - var content = getString(R.string.connected, id) - Toast.makeText(this@VideoMultiPreviewActivity, content, Toast.LENGTH_SHORT).show() - } - - tryReleaseLock(id) - } else if (event == 1005) { - - } else if (event == 1010) { - Log.e(tag, "====event === 1010, 校验失败,info撞库防止串流: $msg") - } - } - - private fun tryReleaseLock(id: String?) { - var holders = getHolderById(id) - for (i in 0 until holders.size) { - Thread(Runnable { - Timer().schedule(object : TimerTask() { - override fun run() { - App.data.accessInfo?.let { - var command = Command.getNvrIpcStatus(holders.get(i).channel, 0) - var repStatus = XP2P.postCommandRequestSync("${it.productId}/${holders.get(i).devName}", - command.toByteArray(), command.toByteArray().size.toLong(), 2 * 1000 * 1000) - JSONArray.parseArray(repStatus, DevStatus::class.java)?.let { - if (it.size == 1 && it.get(0).status == 0) { - countDownLatchs.get("${id}/${holders.get(i).channel}")?.let { - it.countDown() - } - } - } - } - } - - }, 500) - }).start() - } - } - - private fun getHolderById(id: String?) : MutableList { - if (TextUtils.isEmpty(id)) return ArrayList() - - var ret = ArrayList() - for (devUrl in allDevUrl) { - if (!TextUtils.isEmpty(devUrl.devName) && id!!.endsWith(devUrl.devName)) { - ret.add(devUrl) - } - } - return ret - } - - private fun getHolderById(id: String?, channel: Int) : DevUrl2Preview? { - if (TextUtils.isEmpty(id)) return null - - var ret: DevUrl2Preview? = null - for (devUrl in allDevUrl) { - if (!TextUtils.isEmpty(devUrl.devName) && - id!!.endsWith(devUrl.devName) && - channel == devUrl.channel) { - ret = devUrl - } - } - return ret - } - - private fun keepPlayerplay(id: String?, channel: Int) { - if (TextUtils.isEmpty(id)) return - - // 开启守护线程 - Thread(Runnable { - var objectLock = Object() - while (true) { - var playerHolder = getHolderById(id, channel) - if (playerHolder == null || TextUtils.isEmpty(playerHolder.devName)) return@Runnable - - var tmpCountDownLatch = CountDownLatch(1) - - Log.d(tag, "index=${id!!}/${channel} lock wait ") - synchronized(playerHolder.lock) { - playerHolder.lock.wait() - } - Log.d(tag, "index=${id!!}/${channel} lock passed ") - if (!playerHolder.keepAliveThreadRuning) break //锁被释放后,检查守护线程是否继续运行 - - // 发现断开尝试恢复视频,每隔一秒尝试一次 - var flag = XP2P.startServiceWithXp2pInfo(this@VideoMultiPreviewActivity, id, App.data.accessInfo!!.productId, playerHolder.devName, "") - while (flag != 0 && flag != -1011) { - // XP2P.stopService(id) - synchronized(objectLock) { - objectLock.wait(1000) - } - flag = XP2P.startServiceWithXp2pInfo(this@VideoMultiPreviewActivity, id, App.data.accessInfo!!.productId, playerHolder.devName, "") - } - - Log.d(tag, "index=${id!!}/${channel} keepPlayerplay countDownLatch wait ") - countDownLatchs.put("${id!!}/${channel}", tmpCountDownLatch) - tmpCountDownLatch.await() - Log.d(tag, "index=${id!!}/${channel} keepPlayerplay countDownLatch passed") - - val urlPrefix = XP2P.delegateHttpFlv(id) - if (!TextUtils.isEmpty(urlPrefix)) { - playerHolder.player?.let { - val url = urlPrefix + Command.getVideoHightQualityUrlSuffix(playerHolder.channel) - it.reset() - it.setSurface(playerHolder.surface) - it.dataSource = url - it.prepareAsync() - it.start() - } - } - } - }).start() - } - - override fun avDataRecvHandle(id: String?, data: ByteArray?, len: Int) {} - override fun avDataCloseHandle(id: String?, msg: String?, errorCode: Int) {} - override fun onDeviceMsgArrived(id: String?, data: ByteArray?, len: Int): String { - return "app reply to device" - } - - override fun onPause() { - super.onPause() - finishAll() - } - - private fun finishAll() { - for (devPlayer in allDevUrl) { - devPlayer.player?.release() - } - - App.data.accessInfo?.let { - for (i in 0 until allDevUrl.size) { - XP2P.stopService("${it.productId}/${allDevUrl.get(i).devName}") - } - } - - countDownLatchs.clear() - - // 关闭所有守护线程 - for (devUrl in allDevUrl) { - devUrl.keepAliveThreadRuning = false - devUrl.lock?.let { - synchronized(it) { - it.notify() - } - } - } - } - - override fun onDestroy() { - super.onDestroy() - finishAll() - XP2P.setCallback(null) - cancel() - } - - companion object { - fun startMultiPreviewActivity(context: Context?, allUrl: ArrayList) { - if (context == null) return - - var intent = Intent(context, VideoMultiPreviewActivity::class.java) - var bundle = Bundle() - intent.putExtra(VideoConst.VIDEO_URLS, bundle) - bundle.putString(VideoConst.VIDEO_URLS, JSON.toJSONString(allUrl)) - context?.startActivity(intent) - } - } -} \ No newline at end of file +//package com.tencent.iot.explorer.link.demo.video.preview +// +//import android.content.Context +//import android.content.Intent +//import android.content.pm.ActivityInfo +//import android.graphics.SurfaceTexture +//import android.os.Bundle +//import android.text.TextUtils +//import android.util.Log +//import android.view.Surface +//import android.view.TextureView +//import android.view.View +//import android.widget.Toast +//import androidx.recyclerview.widget.GridLayoutManager +//import androidx.recyclerview.widget.LinearLayoutManager +//import com.alibaba.fastjson.JSON +//import com.alibaba.fastjson.JSONArray +//import com.tencent.iot.explorer.link.demo.App +//import com.tencent.iot.explorer.link.demo.BuildConfig +//import com.tencent.iot.explorer.link.demo.R +//import com.tencent.iot.explorer.link.demo.core.activity.* +//import com.tencent.iot.explorer.link.demo.video.Command +//import com.tencent.iot.explorer.link.demo.video.VideoPreviewBaseActivity +//import com.tencent.iot.explorer.link.demo.video.playback.cloudPlayback.event.ActionRecord +//import com.tencent.iot.video.link.consts.VideoConst +//import com.tencent.xnet.XP2P +//import com.tencent.xnet.XP2PAppConfig +//import com.tencent.xnet.XP2PCallback +//import com.tencent.xnet.annotations.XP2PProtocolType +//import kotlinx.android.synthetic.main.activity_video_multi_preview.* +//import kotlinx.coroutines.* +//import tv.danmaku.ijk.media.player.IjkMediaPlayer +//import java.lang.Runnable +//import java.util.* +//import java.util.concurrent.ConcurrentHashMap +//import java.util.concurrent.CopyOnWriteArrayList +//import java.util.concurrent.CountDownLatch +// +// +//private var countDownLatchs: MutableMap = ConcurrentHashMap() +//private var allDevUrl: MutableList = CopyOnWriteArrayList() +// +//class VideoMultiPreviewActivity : VideoPreviewBaseActivity(), XP2PCallback, CoroutineScope by MainScope() { +// lateinit var gridLayoutManager: GridLayoutManager +// lateinit var linearLayoutManager: LinearLayoutManager +// private var adapter: DevPreviewAdapter? = null +// private var tag = VideoMultiPreviewActivity::class.simpleName +// private var orientation = true +// private val xP2PAppConfig = XP2PAppConfig().also { appConfig -> +// appConfig.appKey = +// BuildConfig.TencentIotLinkSDKDemoAppkey //为explorer平台注册的应用信息(https://console.cloud.tencent.com/iotexplorer/v2/instance/app/detai) explorer控制台- 应用开发 - 选对应的应用下的 appkey/appsecret +// appConfig.appSecret = +// BuildConfig.TencentIotLinkSDKDemoAppSecret //为explorer平台注册的应用信息(https://console.cloud.tencent.com/iotexplorer/v2/instance/app/detai) explorer控制台- 应用开发 - 选对应的应用下的 appkey/appsecret +// appConfig.userId = +// "" //用户纬度(每个手机区分开)使用用户自有的账号系统userid;若无请配置为[TIoTCoreXP2PBridge sharedInstance].getAppUUID; 查找日志是需提供此userid字段 +// appConfig.autoConfigFromDevice = true +// appConfig.type = XP2PProtocolType.XP2P_PROTOCOL_AUTO +// appConfig.crossStunTurn = false +// } +// +// override fun getContentView(): Int { +// return R.layout.activity_video_multi_preview +// } +// +// override fun onResume() { +// super.onResume() +// adapter = DevPreviewAdapter(this@VideoMultiPreviewActivity, allDevUrl) +// gl_video.layoutManager = linearLayoutManager +// gl_video.adapter = adapter +// switchOrientation(orientation) +// playAll() +// } +// +// override fun initView() { +// getDeviceP2PInfo() +// XP2P.setCallback(this) +// gridLayoutManager = GridLayoutManager(this@VideoMultiPreviewActivity, 2) +// linearLayoutManager = LinearLayoutManager(this@VideoMultiPreviewActivity) +// intent.getBundleExtra(VideoConst.VIDEO_URLS)?.let { +// it.getString(VideoConst.VIDEO_URLS)?.let { +// try { +// allDevUrl = JSONArray.parseArray(it, DevUrl2Preview::class.java) +// var column = 2 +// if (allDevUrl.size <= 1) column = 1 // 当只有一个元素的时候,网格只有一列 +// gridLayoutManager = GridLayoutManager(this@VideoMultiPreviewActivity, column) +// linearLayoutManager = LinearLayoutManager(this@VideoMultiPreviewActivity) +// } catch (e: Exception) { +// e.printStackTrace() +// } +// } +// } +// switchOrientation(true) +// rg_orientation.check(radio_orientation_v.id) +// } +// +// private fun startService() { +// XP2P.startService( +// this, +// presenter.getProductId(), +// presenter.getDeviceName(), +// xp2pInfo, +// xP2PAppConfig +// ) +// } +// +// private fun restartService() { +// val id = "${presenter.getProductId()}/${presenter.getDeviceName()}" +// XP2P.stopService(id) +// getDeviceP2PInfo() +// } +// +// private fun delegateHttpFlv() { +// val id = "${presenter.getProductId()}/${presenter.getDeviceName()}" +//// XP2P.recordstreamPath("/storage/emulated/0/data_video.flv") //自定义采集裸流路径 +//// XP2P.recordstream(id) //开启自定义采集裸流 +// val prefix = XP2P.delegateHttpFlv(id) +// if (prefix.isNotEmpty()) { +// resetPlayer(prefix) +// } else { +// Toast.makeText(this@VideoPreviewActivity, "get urlPrefix is empty", Toast.LENGTH_SHORT) +// .show() +// } +// } +// +// private fun playAll() { +// if (App.data.accessInfo == null) return +// +// for (i in 0 until allDevUrl.size) { +// if (allDevUrl[i].Status != 1) continue +// +// var player = IjkMediaPlayer() +// allDevUrl[i].surfaceTextureListener = object : TextureView.SurfaceTextureListener { +// override fun onSurfaceTextureAvailable( +// surface: SurfaceTexture?, +// width: Int, +// height: Int +// ) { +// surface?.let { +// allDevUrl[i].surface = Surface(surface) +// player.setSurface(allDevUrl[i].surface) +// } +// } +// +// override fun onSurfaceTextureSizeChanged( +// surface: SurfaceTexture?, +// width: Int, +// height: Int +// ) { +// } +// +// override fun onSurfaceTextureDestroyed(surface: SurfaceTexture?): Boolean { +// return false +// } +// +// override fun onSurfaceTextureUpdated(surface: SurfaceTexture?) {} +// } +// +// setPlayerSource(player, allDevUrl[i].devName, allDevUrl[i].channel) +// allDevUrl.get(i).player = player +// } +// } +// +// private fun setPlayerSource(player: IjkMediaPlayer, devName: String, channel: Int) { +// Thread(Runnable { +// val id = "${App.data.accessInfo!!.productId}/${devName}" +// XP2P.startService(this@VideoMultiPreviewActivity, id, presenter.getProductId(), devName, xP2PAppConfig) +// val started = XP2P.setParamsForXp2pInfo(id,"") +// // 已经启动过,或者第一次启动,继续进行 +// if (started != 0 && started != -1011) { +// launch(Dispatchers.Main) { +// var errInfo = getString(R.string.error_with_code, id, started.toString()) +// Toast.makeText(this@VideoMultiPreviewActivity, errInfo, Toast.LENGTH_SHORT) +// .show() +// } +// return@Runnable +// } +// +// var tmpCountDownLatch = CountDownLatch(1) +// countDownLatchs.put( +// "${App.data.accessInfo!!.productId}/${devName}/${channel}", +// tmpCountDownLatch +// ) +// tmpCountDownLatch.await() +// +// val urlPrefix = XP2P.delegateHttpFlv("${App.data.accessInfo!!.productId}/${devName}") +// if (!TextUtils.isEmpty(urlPrefix)) { +// player?.let { +// val url = urlPrefix + Command.getVideoHightQualityUrlSuffix(channel) +// playPlayer(it, url) +// keepPlayerplay("${App.data.accessInfo!!.productId}/${devName}", channel) +// } +// } +// }).start() +// } +// +// private fun playPlayer(player: IjkMediaPlayer, url: String) { +// player.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "analyzemaxduration", 100) +// player.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "probesize", 25 * 1024) +// player.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "packet-buffering", 0) +// player.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "start-on-prepared", 1) +// player.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, "threads", 1) +// player.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "sync-av-start", 0) +// player.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "reconnect", 1) +// player.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 1) +// player.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-auto-rotate", 1) +// player.setOption( +// IjkMediaPlayer.OPT_CATEGORY_PLAYER, +// "mediacodec-handle-resolution-change", +// 1 +// ) +// +// player.dataSource = url +// player.prepareAsync() +// player.start() +// } +// +// override fun setListener() { +// rg_orientation.setOnCheckedChangeListener { group, checkedId -> +// when (checkedId) { +// radio_orientation_h.id -> switchOrientation(false) +// radio_orientation_v.id -> switchOrientation(true) +// } +// } +// } +// +// private fun switchOrientation(orientationV: Boolean) { +// if (orientationV) { +// setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) +// radio_orientation_v.visibility = View.GONE +// radio_orientation_h.visibility = View.VISIBLE +// gl_video.layoutManager = linearLayoutManager +// } else { +// setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) +// radio_orientation_v.visibility = View.VISIBLE +// radio_orientation_h.visibility = View.GONE +// gl_video.layoutManager = gridLayoutManager +// } +// orientation = orientationV +// adapter?.notifyDataSetChanged() +// } +// +// override fun fail(msg: String?, errorCode: Int) {} +// override fun updateXp2pInfo(xp2pInfo: String) { +// +// } +// +// override fun success(response: String?, reqCode: Int) { +// } +// +// override fun commandRequest(id: String?, msg: String?) {} +// +// override fun xp2pEventNotify(id: String?, msg: String?, event: Int) { +// Log.d(tag, "id=${id},event=${event},msg=${msg}") +// if (event == 1003) { +// XP2P.stopService(id) +// var holders = getHolderById(id) +// for (i in 0 until holders.size) { +// holders.get(i).lock?.let { +// synchronized(it) { +// it.notify() +// } +// } // 唤醒守护线程 +// } +// launch(Dispatchers.Main) { +// var content = getString(R.string.disconnected_and_reconnecting, id) +// Toast.makeText(this@VideoMultiPreviewActivity, content, Toast.LENGTH_SHORT).show() +// } +// +// } else if (event == 1004) { +// launch(Dispatchers.Main) { +// var content = getString(R.string.connected, id) +// Toast.makeText(this@VideoMultiPreviewActivity, content, Toast.LENGTH_SHORT).show() +// } +// +// tryReleaseLock(id) +// } else if (event == 1005) { +// +// } else if (event == 1010) { +// Log.e(tag, "====event === 1010, 校验失败,info撞库防止串流: $msg") +// } +// } +// +// private fun tryReleaseLock(id: String?) { +// var holders = getHolderById(id) +// for (i in 0 until holders.size) { +// Thread(Runnable { +// Timer().schedule(object : TimerTask() { +// override fun run() { +// App.data.accessInfo?.let { +// var command = Command.getNvrIpcStatus(holders.get(i).channel, 0) +// var repStatus = XP2P.postCommandRequestSync( +// "${it.productId}/${holders.get(i).devName}", +// command.toByteArray(), +// command.toByteArray().size.toLong(), +// 2 * 1000 * 1000 +// ) +// JSONArray.parseArray(repStatus, DevStatus::class.java)?.let { +// if (it.size == 1 && it.get(0).status == 0) { +// countDownLatchs.get("${id}/${holders.get(i).channel}")?.let { +// it.countDown() +// } +// } +// } +// } +// } +// +// }, 500) +// }).start() +// } +// } +// +// private fun getHolderById(id: String?): MutableList { +// if (TextUtils.isEmpty(id)) return ArrayList() +// +// var ret = ArrayList() +// for (devUrl in allDevUrl) { +// if (!TextUtils.isEmpty(devUrl.devName) && id!!.endsWith(devUrl.devName)) { +// ret.add(devUrl) +// } +// } +// return ret +// } +// +// private fun getHolderById(id: String?, channel: Int): DevUrl2Preview? { +// if (TextUtils.isEmpty(id)) return null +// +// var ret: DevUrl2Preview? = null +// for (devUrl in allDevUrl) { +// if (!TextUtils.isEmpty(devUrl.devName) && +// id!!.endsWith(devUrl.devName) && +// channel == devUrl.channel +// ) { +// ret = devUrl +// } +// } +// return ret +// } +// +// private fun keepPlayerplay(id: String?, channel: Int) { +// if (TextUtils.isEmpty(id)) return +// +// // 开启守护线程 +// Thread(Runnable { +// var objectLock = Object() +// while (true) { +// var playerHolder = getHolderById(id, channel) +// if (playerHolder == null || TextUtils.isEmpty(playerHolder.devName)) return@Runnable +// +// var tmpCountDownLatch = CountDownLatch(1) +// +// Log.d(tag, "index=${id!!}/${channel} lock wait ") +// synchronized(playerHolder.lock) { +// playerHolder.lock.wait() +// } +// Log.d(tag, "index=${id!!}/${channel} lock passed ") +// if (!playerHolder.keepAliveThreadRuning) break //锁被释放后,检查守护线程是否继续运行 +// +// // 发现断开尝试恢复视频,每隔一秒尝试一次 +// XP2P.startService(this@VideoMultiPreviewActivity, id, presenter.getProductId(), presenter.getDeviceName(), xP2PAppConfig) +// var flag = XP2P.setParamsForXp2pInfo(id,"") +// while (flag != 0 && flag != -1011) { +// // XP2P.stopService(id) +// synchronized(objectLock) { +// objectLock.wait(1000) +// } +// XP2P.startService(this@VideoMultiPreviewActivity, id, presenter.getProductId(), presenter.getDeviceName(), xP2PAppConfig) +// flag = XP2P.setParamsForXp2pInfo(id,"") +// } +// +// Log.d(tag, "index=${id}/${channel} keepPlayerplay countDownLatch wait ") +// countDownLatchs.put("${id}/${channel}", tmpCountDownLatch) +// tmpCountDownLatch.await() +// Log.d(tag, "index=${id}/${channel} keepPlayerplay countDownLatch passed") +// +// val urlPrefix = XP2P.delegateHttpFlv(id) +// if (!TextUtils.isEmpty(urlPrefix)) { +// playerHolder.player?.let { +// val url = +// urlPrefix + Command.getVideoHightQualityUrlSuffix(playerHolder.channel) +// it.reset() +// it.setSurface(playerHolder.surface) +// it.dataSource = url +// it.prepareAsync() +// it.start() +// } +// } +// } +// }).start() +// } +// +// override fun avDataRecvHandle(id: String?, data: ByteArray?, len: Int) {} +// override fun avDataCloseHandle(id: String?, msg: String?, errorCode: Int) {} +// override fun onDeviceMsgArrived(id: String?, data: ByteArray?, len: Int): String { +// return "app reply to device" +// } +// +// override fun onPause() { +// super.onPause() +// finishAll() +// } +// +// override fun eventReady(events: MutableList) { +// TODO("Not yet implemented") +// } +// +// private fun finishAll() { +// for (devPlayer in allDevUrl) { +// devPlayer.player?.release() +// } +// +// App.data.accessInfo?.let { +// for (i in 0 until allDevUrl.size) { +// XP2P.stopService("${it.productId}/${allDevUrl.get(i).devName}") +// } +// } +// +// countDownLatchs.clear() +// +// // 关闭所有守护线程 +// for (devUrl in allDevUrl) { +// devUrl.keepAliveThreadRuning = false +// devUrl.lock?.let { +// synchronized(it) { +// it.notify() +// } +// } +// } +// } +// +// override fun onDestroy() { +// super.onDestroy() +// finishAll() +// XP2P.setCallback(null) +// cancel() +// } +// +// companion object { +// fun startMultiPreviewActivity(context: Context?, allUrl: ArrayList) { +// if (context == null) return +// +// var intent = Intent(context, VideoMultiPreviewActivity::class.java) +// var bundle = Bundle() +// intent.putExtra(VideoConst.VIDEO_URLS, bundle) +// bundle.putString(VideoConst.VIDEO_URLS, JSON.toJSONString(allUrl)) +// context?.startActivity(intent) +// } +// } +//} \ No newline at end of file diff --git a/sdkdemo/src/main/java/com/tencent/iot/explorer/link/demo/video/preview/VideoPreviewActivity.kt b/sdkdemo/src/main/java/com/tencent/iot/explorer/link/demo/video/preview/VideoPreviewActivity.kt index 0c750ac1f..faf14a9e3 100644 --- a/sdkdemo/src/main/java/com/tencent/iot/explorer/link/demo/video/preview/VideoPreviewActivity.kt +++ b/sdkdemo/src/main/java/com/tencent/iot/explorer/link/demo/video/preview/VideoPreviewActivity.kt @@ -20,21 +20,20 @@ import android.view.View import android.view.WindowManager import android.widget.Toast import androidx.constraintlayout.widget.ConstraintLayout -import androidx.recyclerview.widget.LinearLayoutManager import com.alibaba.fastjson.JSON import com.alibaba.fastjson.JSONArray import com.tencent.iot.explorer.link.demo.App +import com.tencent.iot.explorer.link.demo.BuildConfig import com.tencent.iot.explorer.link.demo.R -import com.tencent.iot.explorer.link.demo.VideoBaseActivity import com.tencent.iot.explorer.link.demo.common.log.L import com.tencent.iot.explorer.link.demo.common.util.CommonUtils import com.tencent.iot.explorer.link.demo.common.util.ImageSelect import com.tencent.iot.explorer.link.demo.video.Command import com.tencent.iot.explorer.link.demo.video.DevInfo +import com.tencent.iot.explorer.link.demo.video.VideoPreviewBaseActivity import com.tencent.iot.explorer.link.demo.video.playback.VideoPlaybackActivity import com.tencent.iot.explorer.link.demo.video.playback.cloudPlayback.event.ActionListAdapter import com.tencent.iot.explorer.link.demo.video.playback.cloudPlayback.event.ActionRecord -import com.tencent.iot.explorer.link.demo.video.playback.cloudPlayback.event.EventPresenter import com.tencent.iot.explorer.link.demo.video.playback.cloudPlayback.event.EventView import com.tencent.iot.explorer.link.demo.video.utils.ListOptionsDialog import com.tencent.iot.explorer.link.demo.video.utils.TipToastDialog @@ -44,52 +43,77 @@ import com.tencent.iot.video.link.consts.VideoConst import com.tencent.iot.video.link.entity.DeviceStatus import com.tencent.iot.video.link.util.audio.AudioRecordUtil import com.tencent.xnet.XP2P +import com.tencent.xnet.XP2PAppConfig import com.tencent.xnet.XP2PCallback -import kotlinx.android.synthetic.main.activity_video_preview.* -import kotlinx.android.synthetic.main.dash_board_layout.* -import kotlinx.android.synthetic.main.fragment_video_cloud_playback.* -import kotlinx.android.synthetic.main.title_layout.* -import kotlinx.coroutines.* +import com.tencent.xnet.annotations.XP2PProtocolType +import kotlinx.android.synthetic.main.activity_video_preview.btn_layout +import kotlinx.android.synthetic.main.activity_video_preview.iv_audio +import kotlinx.android.synthetic.main.activity_video_preview.iv_down +import kotlinx.android.synthetic.main.activity_video_preview.iv_left +import kotlinx.android.synthetic.main.activity_video_preview.iv_orientation +import kotlinx.android.synthetic.main.activity_video_preview.iv_right +import kotlinx.android.synthetic.main.activity_video_preview.iv_up +import kotlinx.android.synthetic.main.activity_video_preview.layout_content +import kotlinx.android.synthetic.main.activity_video_preview.layout_video_preview +import kotlinx.android.synthetic.main.activity_video_preview.list_event +import kotlinx.android.synthetic.main.activity_video_preview.radio_photo +import kotlinx.android.synthetic.main.activity_video_preview.radio_playback +import kotlinx.android.synthetic.main.activity_video_preview.radio_record +import kotlinx.android.synthetic.main.activity_video_preview.radio_talk +import kotlinx.android.synthetic.main.activity_video_preview.today_tip +import kotlinx.android.synthetic.main.activity_video_preview.tv_event_status +import kotlinx.android.synthetic.main.activity_video_preview.tv_video_quality +import kotlinx.android.synthetic.main.activity_video_preview.v_preview +import kotlinx.android.synthetic.main.activity_video_preview.v_title +import kotlinx.android.synthetic.main.dash_board_layout.tv_a_cache +import kotlinx.android.synthetic.main.dash_board_layout.tv_tcp_speed +import kotlinx.android.synthetic.main.dash_board_layout.tv_v_cache +import kotlinx.android.synthetic.main.dash_board_layout.tv_video_w_h +import kotlinx.android.synthetic.main.title_layout.iv_back +import kotlinx.android.synthetic.main.title_layout.tv_title +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.MainScope +import kotlinx.coroutines.cancel +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch import tv.danmaku.ijk.media.player.IMediaPlayer import tv.danmaku.ijk.media.player.IjkMediaPlayer -import java.lang.Runnable import java.lang.ref.WeakReference -import java.util.* -import java.util.concurrent.ConcurrentHashMap -import java.util.concurrent.CountDownLatch +import java.util.Locale -private var countDownLatchs : MutableMap = ConcurrentHashMap() -private var keepPlayThreadLock = Object() -@Volatile -private var keepAliveThreadRuning = true - -class VideoPreviewActivity : VideoBaseActivity(), EventView, TextureView.SurfaceTextureListener, +class VideoPreviewActivity : VideoPreviewBaseActivity(), EventView, + TextureView.SurfaceTextureListener, XP2PCallback, CoroutineScope by MainScope(), VolumeChangeObserver.VolumeChangeListener, IMediaPlayer.OnInfoListener { open var tag = VideoPreviewActivity::class.simpleName var orientationV = true - private var adapter : ActionListAdapter? = null - private var records : MutableList = ArrayList() - lateinit var presenter: EventPresenter - lateinit var player : IjkMediaPlayer + private var adapter: ActionListAdapter? = null + private var records: MutableList = ArrayList() + lateinit var player: IjkMediaPlayer lateinit var surface: Surface + @Volatile var audioAble = true - @Volatile - var urlPrefix = "" var filePath: String? = null lateinit var audioRecordUtil: AudioRecordUtil var permissions = arrayOf(Manifest.permission.RECORD_AUDIO) + @Volatile var showTip = false + @Volatile var connectStartTime = 0L + @Volatile var connectTime = 0L + @Volatile var startShowVideoTime = 0L + @Volatile var showVideoTime = 0L var volumeChangeObserver: VolumeChangeObserver? = null @@ -98,62 +122,56 @@ class VideoPreviewActivity : VideoBaseActivity(), EventView, TextureView.Surface var screenWidth = 0 var screenHeight = 0 var firstIn = true + @Volatile var speakAble = false + private var isRestart: Boolean = false + + val xP2PAppConfig = XP2PAppConfig().also { appConfig -> + appConfig.appKey = + BuildConfig.TencentIotLinkSDKDemoAppkey //为explorer平台注册的应用信息(https://console.cloud.tencent.com/iotexplorer/v2/instance/app/detai) explorer控制台- 应用开发 - 选对应的应用下的 appkey/appsecret + appConfig.appSecret = + BuildConfig.TencentIotLinkSDKDemoAppSecret //为explorer平台注册的应用信息(https://console.cloud.tencent.com/iotexplorer/v2/instance/app/detai) explorer控制台- 应用开发 - 选对应的应用下的 appkey/appsecret + appConfig.userId = + "" //用户纬度(每个手机区分开)使用用户自有的账号系统userid;查找日志是需提供此userid字段 + appConfig.autoConfigFromDevice = false //是否启动跟随配置,需要控制台配置 + appConfig.type = XP2PProtocolType.XP2P_PROTOCOL_AUTO + } + override fun getContentView(): Int { return R.layout.activity_video_preview } - override fun onResume() { - super.onResume() - keepAliveThreadRuning = true - startPlayer() + override fun updateXp2pInfo(xp2pInfo: String) { + Log.d(tag, "update xp2p2Info:$xp2pInfo") + startService() } override fun initView() { - presenter = EventPresenter(this@VideoPreviewActivity) - var bundle = intent.getBundleExtra(VideoConst.VIDEO_CONFIG) - bundle?.let { - var videoConfig = bundle.getString(VideoConst.VIDEO_CONFIG) - if (TextUtils.isEmpty(videoConfig)) return@let - - var devInfo = JSON.parseObject(videoConfig, DevUrl2Preview::class.java) - devInfo?.let { - tv_title.setText(it.devName) - presenter.setDeviceName(it.devName) - presenter.setChannel(it.channel) - } - } - - var linearLayoutManager = LinearLayoutManager(this@VideoPreviewActivity) + tv_title.text = presenter.getDeviceName() adapter = ActionListAdapter(this@VideoPreviewActivity, records) - list_event.layoutManager = linearLayoutManager list_event.adapter = adapter tv_video_quality.setText(R.string.video_quality_medium_str) today_tip.setText(getString(R.string.today) + " " + CommonUtils.getWeekDay(this@VideoPreviewActivity)) records.clear() - App.data.accessInfo?.let { - presenter.setAccessId(it.accessId) - presenter.setAccessToken(it.accessToken) - presenter.setProductId(it.productId) - presenter.getEventsData(Date()) - tv_event_status.visibility = View.VISIBLE - tv_event_status.setText(R.string.loading) - audioRecordUtil = AudioRecordUtil(this, "${it.productId}/${presenter.getDeviceName()}", 16000, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT) -// //变调可以传入pitch参数 -// audioRecordUtil = AudioRecordUtil(this, "${it.productId}/${presenter.getDeviceName()}", 16000, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, -6) - // 变调可以传入pitch参数 -// audioRecordUtil = AudioRecordUtil(this, "${it.productId}/${presenter.getDeviceName()}", 16000, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, 0, this) -// audioRecordUtil.recordSpeakFlv(true) - } - - XP2P.setCallback(this) - XP2P.startService(this@VideoPreviewActivity, "${App.data.accessInfo!!.productId}/${presenter.getDeviceName()}", - App.data.accessInfo!!.productId, presenter.getDeviceName() + tv_event_status.visibility = View.VISIBLE + tv_event_status.setText(R.string.loading) + audioRecordUtil = AudioRecordUtil( + this, + "${presenter.getProductId()}/${presenter.getDeviceName()}", + 16000, + AudioFormat.CHANNEL_IN_MONO, + AudioFormat.ENCODING_PCM_16BIT ) - +// //变调可以传入pitch参数 +// audioRecordUtil = AudioRecordUtil(this, "${it.productId}/${presenter.getDeviceName()}", 16000, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, -6) +// //变调可以传入pitch参数 +// audioRecordUtil = AudioRecordUtil(this, "${it.productId}/${presenter.getDeviceName()}", 16000, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, 0, this) +// audioRecordUtil.recordSpeakFlv(true) + getDeviceP2PInfo() + XP2P.setCallback(this) //实例化对象并设置监听器 volumeChangeObserver = VolumeChangeObserver(this) volumeChangeObserver?.volumeChangeListener = this @@ -167,89 +185,70 @@ class VideoPreviewActivity : VideoBaseActivity(), EventView, TextureView.Surface val density = dm.density // 屏幕密度(0.75 / 1.0 / 1.5) screenWidth = (width / density).toInt() // 屏幕宽度(dp) screenHeight = (height / density).toInt() // 屏幕高度(dp) + startPlayer() } - open fun startPlayer() { - if (App.data.accessInfo == null || TextUtils.isEmpty(presenter.getDeviceName())) return - player = IjkMediaPlayer() - player.setOnInfoListener(this) - mHandler.sendEmptyMessageDelayed(MSG_UPDATE_HUD, 500) + private fun startService() { + Log.d(tag, "startService") + XP2P.startService( + this, + presenter.getProductId(), + presenter.getDeviceName(), + xp2pInfo, + xP2PAppConfig + ) + } + + private fun checkDeviceState() { + Log.d(tag, "====检测设备状态===") + launch(Dispatchers.IO) { + val command = "action=user_define&channel=${presenter.getChannel()}&cmd=device_state" + val retContent = XP2P.postCommandRequestSync( + "${presenter.getProductId()}/${presenter.getDeviceName()}", + command.toByteArray(), command.toByteArray().size.toLong(), 1 * 1000 * 1000 + ) ?: "" + Log.d(tag, "device_state back content:$retContent") - Thread(Runnable { - val id = "${App.data.accessInfo!!.productId}/${presenter.getDeviceName()}" - connectStartTime = System.currentTimeMillis() - val ret = XP2P.setParamsForXp2pInfo(id, App.data.accessInfo!!.accessId, - App.data.accessInfo!!.accessToken, "") - if (ret != 0) { + if (retContent.isEmpty()) { launch(Dispatchers.Main) { - val errInfo: String - if (ret.toString() == "-1007") { - errInfo = getString(R.string.xp2p_err_version) - } else { - errInfo = getString(R.string.error_with_code, id, ret.toString()) - } - Toast.makeText(this@VideoPreviewActivity, errInfo, Toast.LENGTH_SHORT).show() + restartService() } - return@Runnable - } - XP2P.delegateHttpFlv(id)?.let { - urlPrefix = it - if (!TextUtils.isEmpty(urlPrefix)) { - player.let { - resetPlayer() - keepPlayerplay(id) - } + } else { + launch(Dispatchers.Main) { + isRestart = false + delegateHttpFlv() } } - }).start() - } - - private fun keepPlayerplay(id: String?) { - if (TextUtils.isEmpty(id)) return - val accessId = App.data.accessInfo!!.accessId - val accessToken = App.data.accessInfo!!.accessToken - // 开启守护线程 - Thread{ - val objectLock = Object() - while (true) { - Log.d(tag, "id=${id} keepAliveThread wait disconnected msg") - synchronized(keepPlayThreadLock) { - keepPlayThreadLock.wait() - } - Log.d(tag, "id=${id} keepAliveThread do not wait and keepAliveThreadRuning=${keepAliveThreadRuning}") - if (!keepAliveThreadRuning) break //锁被释放后,检查守护线程是否继续运行 - - // 发现断开尝试恢复视频,每隔一秒尝试一次 - Log.d(tag, "====开始尝试重连...") - XP2P.stopService(id) - while (XP2P.startService(this@VideoPreviewActivity, id, App.data.accessInfo!!.productId, presenter.getDeviceName())!=0 - || XP2P.setParamsForXp2pInfo(id, accessId, accessToken, "") != 0 - || getDeviceStatus(id) != 0) { - XP2P.stopService(id) - synchronized(objectLock) { - objectLock.wait(500) - } - Log.d(tag, "====正在重连...") - } - connectStartTime = System.currentTimeMillis() + } + } - Log.d(tag, "====尝试拉流...") - XP2P.delegateHttpFlv(id)?.let { - urlPrefix = it - if (!TextUtils.isEmpty(urlPrefix)) resetPlayer() - } - } - }.start() + private fun restartService() { + Log.d(tag, "====开始重连===") + XP2P.stopService("${presenter.getProductId()}/${presenter.getDeviceName()}") + getDeviceP2PInfo() } - private fun resetPlayer() { - when (tv_video_quality.text.toString()) { - getString(R.string.video_quality_high_str) -> setPlayerUrl(Command.getVideoSuperQualityUrlSuffix(presenter.getChannel())) - getString(R.string.video_quality_medium_str) -> setPlayerUrl(Command.getVideoHightQualityUrlSuffix(presenter.getChannel())) - getString(R.string.video_quality_low_str) -> setPlayerUrl(Command.getVideoStandardQualityUrlSuffix(presenter.getChannel())) + private fun delegateHttpFlv() { + val id = "${presenter.getProductId()}/${presenter.getDeviceName()}" +// XP2P.recordstreamPath("/storage/emulated/0/data_video.flv") //自定义采集裸流路径 +// XP2P.recordstream(id) //开启自定义采集裸流 + val prefix = XP2P.delegateHttpFlv(id) + if (prefix.isNotEmpty()) { + resetPlayer(prefix) + } else { + Toast.makeText(this@VideoPreviewActivity, "get urlPrefix is empty", Toast.LENGTH_SHORT) + .show() } } + + open fun startPlayer() { + if (App.data.accessInfo == null || TextUtils.isEmpty(presenter.getDeviceName())) return + player = IjkMediaPlayer() + player.setOnInfoListener(this) + mHandler.sendEmptyMessageDelayed(MSG_UPDATE_HUD, 500) + } + override fun onSurfaceTextureAvailable(surface: SurfaceTexture?, width: Int, height: Int) { surface?.let { this.surface = Surface(surface) @@ -261,8 +260,10 @@ class VideoPreviewActivity : VideoBaseActivity(), EventView, TextureView.Surface App.data.accessInfo?.let { accessInfo -> if (able) { var command = Command.getNvrIpcStatus(presenter.getChannel(), 0) - var repStatus = XP2P.postCommandRequestSync("${accessInfo.productId}/${presenter.getDeviceName()}", - command.toByteArray(), command.toByteArray().size.toLong(), 2 * 1000 * 1000) ?:"" + var repStatus = XP2P.postCommandRequestSync( + "${accessInfo.productId}/${presenter.getDeviceName()}", + command.toByteArray(), command.toByteArray().size.toLong(), 2 * 1000 * 1000 + ) ?: "" launch(Dispatchers.Main) { var retContent = StringBuilder(repStatus).toString() @@ -274,13 +275,16 @@ class VideoPreviewActivity : VideoBaseActivity(), EventView, TextureView.Surface JSONArray.parseArray(repStatus, DevStatus::class.java)?.let { if (it.size == 1 && it.get(0).status == 0) { - XP2P.runSendService("${accessInfo.productId}/${presenter.getDeviceName()}", Command.getTwoWayRadio(presenter.getChannel()), true) + XP2P.runSendService( + "${accessInfo.productId}/${presenter.getDeviceName()}", + Command.getTwoWayRadio(presenter.getChannel()), + true + ) audioRecordUtil.start() speakAble = true return true } } - } else { speakAble = false audioRecordUtil.stop() @@ -313,7 +317,12 @@ class VideoPreviewActivity : VideoBaseActivity(), EventView, TextureView.Surface filePath = CommonUtils.generateFileDefaultPath() var ret = player.startRecord(filePath) if (ret != 0) { - ToastDialog(this, ToastDialog.Type.WARNING, getString(R.string.record_failed), 2000).show() + ToastDialog( + this, + ToastDialog.Type.WARNING, + getString(R.string.record_failed), + 2000 + ).show() radio_record.isChecked = false } } else { @@ -329,7 +338,12 @@ class VideoPreviewActivity : VideoBaseActivity(), EventView, TextureView.Surface radio_photo.setOnClickListener { val bitmap = v_preview.getBitmap(player.videoWidth, player.videoHeight) ImageSelect.saveBitmap(this@VideoPreviewActivity, bitmap) - ToastDialog(this, ToastDialog.Type.SUCCESS, getString(R.string.capture_successed), 2000).show() + ToastDialog( + this, + ToastDialog.Type.SUCCESS, + getString(R.string.capture_successed), + 2000 + ).show() } iv_up.setOnClickListener(controlListener) iv_down.setOnClickListener(controlListener) @@ -349,8 +363,8 @@ class VideoPreviewActivity : VideoBaseActivity(), EventView, TextureView.Surface player.setVolume(0F, 0F) } else { iv_audio.setImageResource(R.mipmap.audio) - var audioManager = getSystemService(Service.AUDIO_SERVICE) as AudioManager - var volume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC) + val audioManager = getSystemService(Service.AUDIO_SERVICE) as AudioManager + val volume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC) player.setVolume(volume.toFloat(), volume.toFloat()) } } @@ -358,7 +372,7 @@ class VideoPreviewActivity : VideoBaseActivity(), EventView, TextureView.Surface open var controlListener = object : View.OnClickListener { override fun onClick(v: View?) { var command = "" - when(v) { + when (v) { iv_up -> command = Command.getPtzUpCommand(presenter.getChannel()) iv_down -> command = Command.getPtzDownCommand(presenter.getChannel()) iv_right -> command = Command.getPtzRightCommand(presenter.getChannel()) @@ -368,13 +382,16 @@ class VideoPreviewActivity : VideoBaseActivity(), EventView, TextureView.Surface Thread(Runnable { App.data.accessInfo?.let { if (command.length <= 0) return@Runnable - var retContent = XP2P.postCommandRequestSync("${it.productId}/${presenter.getDeviceName()}", - command.toByteArray(), command.toByteArray().size.toLong(), 2 * 1000 * 1000)?:"" + var retContent = XP2P.postCommandRequestSync( + "${it.productId}/${presenter.getDeviceName()}", + command.toByteArray(), command.toByteArray().size.toLong(), 2 * 1000 * 1000 + ) ?: "" launch(Dispatchers.Main) { if (TextUtils.isEmpty(retContent)) { retContent = getString(R.string.command_with_error, command) } - Toast.makeText(this@VideoPreviewActivity, retContent, Toast.LENGTH_SHORT).show() + Toast.makeText(this@VideoPreviewActivity, retContent, Toast.LENGTH_SHORT) + .show() } } }).start() @@ -387,101 +404,105 @@ class VideoPreviewActivity : VideoBaseActivity(), EventView, TextureView.Surface } } - private var switchVideoQualityListener = object : View.OnClickListener { - override fun onClick(v: View?) { - if (orientationV) { - showVVideoQualityDialog() - } else { - showHVideoQualityDialog() - } + private var switchVideoQualityListener = View.OnClickListener { + if (orientationV) { + showVVideoQualityDialog() + } else { + showHVideoQualityDialog() } } private fun showVVideoQualityDialog() { - var options = arrayListOf(getString(R.string.video_quality_high_str) + " " + getString(R.string.video_quality_high), - getString(R.string.video_quality_medium_str) + " " + getString(R.string.video_quality_medium), - getString(R.string.video_quality_low_str) + " " + getString(R.string.video_quality_low)) - var dlg = ListOptionsDialog(this@VideoPreviewActivity, options) + val options = arrayListOf( + getString(R.string.video_quality_high_str) + " " + getString(R.string.video_quality_high), + getString(R.string.video_quality_medium_str) + " " + getString(R.string.video_quality_medium), + getString(R.string.video_quality_low_str) + " " + getString(R.string.video_quality_low) + ) + val dlg = ListOptionsDialog(this@VideoPreviewActivity, options) dlg.show() dlg.setOnDismisListener { chgTextState(it) } } private fun showHVideoQualityDialog() { var pos = -1 - when(tv_video_quality.text.toString()) { + when (tv_video_quality.text.toString()) { getString(R.string.video_quality_high_str) -> pos = 2 getString(R.string.video_quality_medium_str) -> pos = 1 getString(R.string.video_quality_low_str) -> pos = 0 } - var dlg = VideoQualityDialog(this@VideoPreviewActivity, pos) + val dlg = VideoQualityDialog(this@VideoPreviewActivity, pos) dlg.show() btn_layout.visibility = View.GONE dlg.setOnDismisListener(object : VideoQualityDialog.OnDismisListener { - override fun onItemClicked(pos: Int) { chgTextState(pos) } - override fun onDismiss() { btn_layout.visibility = View.VISIBLE } + override fun onItemClicked(pos: Int) { + chgTextState(pos) + } + + override fun onDismiss() { + btn_layout.visibility = View.VISIBLE + } }) } - open fun chgTextState(value: Int) { - when(value) { + private fun chgTextState(value: Int) { + val url = when (value) { 0 -> { tv_video_quality.setText(R.string.video_quality_high_str) - setPlayerUrl(Command.getVideoSuperQualityUrlSuffix(presenter.getChannel())) + prefix + Command.getVideoSuperQualityUrlSuffix(presenter.getChannel()) } + 1 -> { tv_video_quality.setText(R.string.video_quality_medium_str) - setPlayerUrl(Command.getVideoHightQualityUrlSuffix(presenter.getChannel())) + prefix + Command.getVideoHightQualityUrlSuffix(presenter.getChannel()) } + 2 -> { tv_video_quality.setText(R.string.video_quality_low_str) - setPlayerUrl(Command.getVideoStandardQualityUrlSuffix(presenter.getChannel())) + prefix + Command.getVideoStandardQualityUrlSuffix(presenter.getChannel()) } - } + else -> "" + } + setPlayerUrl(url) chgAudioStatus(audioAble) } - open fun setPlayerUrl(suffix: String) { + private fun setPlayerUrl(url: String) { showTip = false startShowVideoTime = System.currentTimeMillis() - player.release() - launch (Dispatchers.Main) { + launch(Dispatchers.Main) { layout_video_preview?.removeView(v_preview) layout_video_preview?.addView(v_preview, 0) - - player = IjkMediaPlayer() player.setOnInfoListener(this@VideoPreviewActivity) - player?.let { - val url = urlPrefix + suffix - it.reset() - - it.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "analyzemaxduration", 100) - it.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "probesize", 50 * 1024) - it.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "packet-buffering", 0) - it.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "start-on-prepared", 1) - it.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, "threads", 1) - it.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "sync-av-start", 0) - it.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec",1) - it.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-auto-rotate", 1) - it.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-handle-resolution-change", 1) - - it.setFrameSpeed(1.5f) - while (!::surface.isInitialized) { - delay(50) - L.e("delay for waiting surface.") - } - it.setSurface(surface) - it.dataSource = url - - it.prepareAsync() - it.start() + player.reset() + player.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "analyzemaxduration", 100) + player.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "probesize", 50 * 1024) + player.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "packet-buffering", 0) + player.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "start-on-prepared", 1) + player.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, "threads", 1) + player.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "sync-av-start", 0) + player.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 1) + player.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-auto-rotate", 1) + player.setOption( + IjkMediaPlayer.OPT_CATEGORY_PLAYER, + "mediacodec-handle-resolution-change", + 1 + ) + player.setFrameSpeed(1.5f) + while (!::surface.isInitialized) { + delay(50) + L.e("delay for waiting surface.") } + player.setSurface(surface) + player.dataSource = url + player.prepareAsync() + player.start() } } - private fun switchOrientation(orientation : Boolean) { + private fun switchOrientation(orientation: Boolean) { var marginWidth = 0 - var layoutParams = layout_video_preview.layoutParams as ConstraintLayout.LayoutParams + val layoutParams = layout_video_preview.layoutParams as ConstraintLayout.LayoutParams var fitSize = 0 var visibility = View.VISIBLE var moreSpace = 10 @@ -502,18 +523,18 @@ class VideoPreviewActivity : VideoBaseActivity(), EventView, TextureView.Surface layoutParams.width = fitSize layout_video_preview.layoutParams = layoutParams - var videoLayoutParams = v_preview.layoutParams as ConstraintLayout.LayoutParams + val videoLayoutParams = v_preview.layoutParams as ConstraintLayout.LayoutParams videoLayoutParams.marginStart = dp2px(marginWidth) videoLayoutParams.marginEnd = dp2px(marginWidth) v_preview.layoutParams = videoLayoutParams - var btnLayoutParams = btn_layout.layoutParams as ConstraintLayout.LayoutParams + val btnLayoutParams = btn_layout.layoutParams as ConstraintLayout.LayoutParams btnLayoutParams.bottomMargin = dp2px(moreSpace) btn_layout.layoutParams = btnLayoutParams } override fun eventReady(events: MutableList) { - if (events == null || events.size <= 0) { + if (events.size <= 0) { launch(Dispatchers.Main) { tv_event_status.setText(R.string.no_data) } @@ -538,11 +559,16 @@ class VideoPreviewActivity : VideoBaseActivity(), EventView, TextureView.Surface v_preview.layoutParams = layoutParams } - override fun onSurfaceTextureDestroyed(surface: SurfaceTexture?): Boolean { return false } + + override fun onSurfaceTextureDestroyed(surface: SurfaceTexture?): Boolean { + return false + } + override fun onSurfaceTextureUpdated(surface: SurfaceTexture?) { if (!showTip && startShowVideoTime > 0) { showVideoTime = System.currentTimeMillis() - startShowVideoTime - var content = getString(R.string.time_2_show, connectTime.toString(), showVideoTime.toString()) + val content = + getString(R.string.time_2_show, connectTime.toString(), showVideoTime.toString()) TipToastDialog(this, content, 10000).show() showTip = true } @@ -554,6 +580,7 @@ class VideoPreviewActivity : VideoBaseActivity(), EventView, TextureView.Surface firstIn = false } } + override fun fail(msg: String?, errorCode: Int) {} override fun commandRequest(id: String?, msg: String?) {} override fun avDataRecvHandle(id: String?, data: ByteArray?, len: Int) {} @@ -567,26 +594,46 @@ class VideoPreviewActivity : VideoBaseActivity(), EventView, TextureView.Surface if (event == 1003) { Log.e(tag, "====event === 1003") startShowVideoTime = 0L - keepPlayThreadLock?.let { - synchronized(it) { - Log.d(tag, "====p2p链路断开, event=$event.") - it.notify() - } - } // 唤醒守护线程 launch(Dispatchers.Main) { - var content = getString(R.string.disconnected_and_reconnecting, id) + val content = getString(R.string.disconnected_and_reconnecting, id) Toast.makeText(this@VideoPreviewActivity, content, Toast.LENGTH_SHORT).show() + if (!isRestart) { + restartService() + isRestart = true + } } } else if (event == 1004 || event == 1005) { connectTime = System.currentTimeMillis() - connectStartTime if (event == 1004) { Log.e(tag, "====event === 1004") + checkDeviceState() } } else if (event == 1010) { Log.e(tag, "====event === 1010, 校验失败,info撞库防止串流: $msg") } } + private var prefix: String = "" + private fun resetPlayer(prefix: String) { + this.prefix = prefix + val url = when (tv_video_quality.text.toString()) { + getString(R.string.video_quality_high_str) -> prefix + Command.getVideoSuperQualityUrlSuffix( + presenter.getChannel() + ) + + getString(R.string.video_quality_medium_str) -> prefix + Command.getVideoHightQualityUrlSuffix( + presenter.getChannel() + ) + + getString(R.string.video_quality_low_str) -> prefix + Command.getVideoStandardQualityUrlSuffix( + presenter.getChannel() + ) + + else -> "" + } + setPlayerUrl(url) + } + override fun onPause() { super.onPause() finishPlayer() @@ -599,25 +646,13 @@ class VideoPreviewActivity : VideoBaseActivity(), EventView, TextureView.Surface player.stopRecord() CommonUtils.refreshVideoList(this@VideoPreviewActivity, filePath) } - - player?.release() - - countDownLatchs.clear() - // 关闭守护线程 - keepAliveThreadRuning = false - keepPlayThreadLock?.let { - synchronized(it) { - it.notify() - } - } + player.release() } override fun onDestroy() { super.onDestroy() finishPlayer() - App.data.accessInfo?.let { - XP2P.stopService("${it.productId}/${presenter.getDeviceName()}") - } + XP2P.stopService("${presenter.getProductId()}/${presenter.getDeviceName()}") XP2P.setCallback(null) cancel() volumeChangeObserver?.unregisterReceiver(); @@ -625,12 +660,12 @@ class VideoPreviewActivity : VideoBaseActivity(), EventView, TextureView.Surface companion object { fun startPreviewActivity(context: Context?, dev: DevInfo) { - context?:let { return } + context ?: let { return } - var intent = Intent(context, VideoPreviewActivity::class.java) - var bundle = Bundle() + val intent = Intent(context, VideoPreviewActivity::class.java) + val bundle = Bundle() intent.putExtra(VideoConst.VIDEO_CONFIG, bundle) - var devInfo = DevUrl2Preview() + val devInfo = DevUrl2Preview() devInfo.devName = dev.DeviceName devInfo.Status = dev.Status devInfo.channel = dev.Channel @@ -641,7 +676,7 @@ class VideoPreviewActivity : VideoBaseActivity(), EventView, TextureView.Surface override fun onVolumeChanged(volume: Int) { if (audioAble) { - player?.setVolume(volume.toFloat(), volume.toFloat()) + player.setVolume(volume.toFloat(), volume.toFloat()) } } @@ -649,18 +684,25 @@ class VideoPreviewActivity : VideoBaseActivity(), EventView, TextureView.Surface var command: ByteArray? = null when (tv_video_quality.text.toString()) { getString(R.string.video_quality_high_str) -> { - command = "action=inner_define&channel=0&cmd=get_device_st&type=live&quality=super".toByteArray() + command = + "action=inner_define&channel=0&cmd=get_device_st&type=live&quality=super".toByteArray() } + getString(R.string.video_quality_medium_str) -> { - command = "action=inner_define&channel=0&cmd=get_device_st&type=live&quality=high".toByteArray() + command = + "action=inner_define&channel=0&cmd=get_device_st&type=live&quality=high".toByteArray() } + getString(R.string.video_quality_low_str) -> { - command = "action=inner_define&channel=0&cmd=get_device_st&type=live&quality=standard".toByteArray() + command = + "action=inner_define&channel=0&cmd=get_device_st&type=live&quality=standard".toByteArray() } } - val reponse = XP2P.postCommandRequestSync(id, command, command!!.size.toLong(), 2 * 1000 * 1000) + val reponse = + XP2P.postCommandRequestSync(id, command, command!!.size.toLong(), 2 * 1000 * 1000) if (!TextUtils.isEmpty(reponse)) { - val deviceStatuses: List = JSONArray.parseArray(reponse, DeviceStatus::class.java) + val deviceStatuses: List = + JSONArray.parseArray(reponse, DeviceStatus::class.java) // 0 接收请求 // 1 拒绝请求 // 404 error request message @@ -671,22 +713,30 @@ class VideoPreviewActivity : VideoBaseActivity(), EventView, TextureView.Surface runOnUiThread { when (deviceStatuses[0].status) { 0 -> Toast.makeText(this, "设备状态正常", Toast.LENGTH_SHORT).show() - 1 -> Toast.makeText(this, "设备状态异常, 拒绝请求: $reponse", Toast.LENGTH_SHORT).show() + 1 -> Toast.makeText( + this, + "设备状态异常, 拒绝请求: $reponse", + Toast.LENGTH_SHORT + ).show() + 404 -> Toast.makeText( this, "设备状态异常, error request message: $reponse", Toast.LENGTH_SHORT ).show() + 405 -> Toast.makeText( this, "设备状态异常, connect number too many: $reponse", Toast.LENGTH_SHORT ).show() + 406 -> Toast.makeText( this, "设备状态异常, current command don't support: $reponse", Toast.LENGTH_SHORT ).show() + 407 -> Toast.makeText( this, "设备状态异常, device process error: $reponse", @@ -708,7 +758,7 @@ class VideoPreviewActivity : VideoBaseActivity(), EventView, TextureView.Surface private val mHandler = MyHandler(this) private class MyHandler(activity: VideoPreviewActivity) : Handler() { - private val mActivity: WeakReference = WeakReference(activity) + private val mActivity: WeakReference = WeakReference(activity) override fun handleMessage(msg: Message) { if (mActivity.get() == null) { return @@ -731,14 +781,20 @@ class VideoPreviewActivity : VideoBaseActivity(), EventView, TextureView.Surface val audioCachedBytes = player.audioCachedBytes val tcpSpeed = player.tcpSpeed - tv_a_cache?.text = String.format(Locale.US, "%s, %s", + tv_a_cache?.text = String.format( + Locale.US, "%s, %s", CommonUtils.formatedDurationMilli(audioCachedDuration), - CommonUtils.formatedSize(audioCachedBytes)) - tv_v_cache?.text = String.format(Locale.US, "%s, %s", + CommonUtils.formatedSize(audioCachedBytes) + ) + tv_v_cache?.text = String.format( + Locale.US, "%s, %s", CommonUtils.formatedDurationMilli(videoCachedDuration), - CommonUtils.formatedSize(videoCachedBytes)) - tv_tcp_speed?.text = String.format(Locale.US, "%s", - CommonUtils.formatedSpeed(tcpSpeed, 1000)) + CommonUtils.formatedSize(videoCachedBytes) + ) + tv_tcp_speed?.text = String.format( + Locale.US, "%s", + CommonUtils.formatedSpeed(tcpSpeed, 1000) + ) tv_video_w_h?.text = "${player.videoWidth} x ${player.videoHeight}" } diff --git a/sdkdemo/src/main/java/com/tencent/iot/explorer/link/demo/video/preview/VideoPreviewMJPEGActivity.kt b/sdkdemo/src/main/java/com/tencent/iot/explorer/link/demo/video/preview/VideoPreviewMJPEGActivity.kt index 3de07b962..7e9673716 100644 --- a/sdkdemo/src/main/java/com/tencent/iot/explorer/link/demo/video/preview/VideoPreviewMJPEGActivity.kt +++ b/sdkdemo/src/main/java/com/tencent/iot/explorer/link/demo/video/preview/VideoPreviewMJPEGActivity.kt @@ -23,13 +23,14 @@ import androidx.recyclerview.widget.LinearLayoutManager import com.alibaba.fastjson.JSON import com.alibaba.fastjson.JSONArray import com.tencent.iot.explorer.link.demo.App +import com.tencent.iot.explorer.link.demo.BuildConfig import com.tencent.iot.explorer.link.demo.R -import com.tencent.iot.explorer.link.demo.VideoBaseActivity import com.tencent.iot.explorer.link.demo.common.log.L import com.tencent.iot.explorer.link.demo.common.util.CommonUtils import com.tencent.iot.explorer.link.demo.common.util.ImageSelect import com.tencent.iot.explorer.link.demo.video.Command import com.tencent.iot.explorer.link.demo.video.DevInfo +import com.tencent.iot.explorer.link.demo.video.VideoPreviewBaseActivity import com.tencent.iot.explorer.link.demo.video.playback.VideoPlaybackActivity import com.tencent.iot.explorer.link.demo.video.playback.cloudPlayback.event.ActionListAdapter import com.tencent.iot.explorer.link.demo.video.playback.cloudPlayback.event.ActionRecord @@ -43,7 +44,9 @@ import com.tencent.iot.video.link.consts.VideoConst import com.tencent.iot.video.link.entity.DeviceStatus import com.tencent.iot.video.link.util.audio.AudioRecordUtil import com.tencent.xnet.XP2P +import com.tencent.xnet.XP2PAppConfig import com.tencent.xnet.XP2PCallback +import com.tencent.xnet.annotations.XP2PProtocolType import kotlinx.android.synthetic.main.activity_video_preview.* import kotlinx.android.synthetic.main.dash_board_layout.* import kotlinx.android.synthetic.main.fragment_video_cloud_playback.* @@ -53,41 +56,41 @@ import tv.danmaku.ijk.media.player.IjkMediaPlayer import java.lang.Runnable import java.lang.ref.WeakReference import java.util.* -import java.util.concurrent.ConcurrentHashMap -import java.util.concurrent.CountDownLatch -private var countDownLatchs : MutableMap = ConcurrentHashMap() -private var keepPlayThreadLock = Object() -@Volatile -private var keepAliveThreadRuning = true - -class VideoPreviewMJPEGActivity : VideoBaseActivity(), EventView, TextureView.SurfaceTextureListener, +class VideoPreviewMJPEGActivity : VideoPreviewBaseActivity(), EventView, + TextureView.SurfaceTextureListener, XP2PCallback, CoroutineScope by MainScope(), VolumeChangeObserver.VolumeChangeListener { open var tag = VideoPreviewMJPEGActivity::class.simpleName var orientationV = true - private var adapter : ActionListAdapter? = null - private var records : MutableList = ArrayList() - lateinit var presenter: EventPresenter - lateinit var player : IjkMediaPlayer - var audioPlayer : IjkMediaPlayer? = null + private var adapter: ActionListAdapter? = null + private var records: MutableList = ArrayList() + lateinit var player: IjkMediaPlayer + var audioPlayer: IjkMediaPlayer? = null lateinit var surface: Surface + @Volatile var audioAble = true + @Volatile var urlPrefix = "" var filePath: String? = null lateinit var audioRecordUtil: AudioRecordUtil var permissions = arrayOf(Manifest.permission.RECORD_AUDIO) + @Volatile var showTip = false + @Volatile var connectStartTime = 0L + @Volatile var connectTime = 0L + @Volatile var startShowVideoTime = 0L + @Volatile var showVideoTime = 0L var volumeChangeObserver: VolumeChangeObserver? = null @@ -97,54 +100,37 @@ class VideoPreviewMJPEGActivity : VideoBaseActivity(), EventView, TextureView.Su var screenHeight = 0 var firstIn = true - override fun getContentView(): Int { - return R.layout.activity_video_preview + private val xP2PAppConfig = XP2PAppConfig().also { appConfig -> + appConfig.appKey = + BuildConfig.TencentIotLinkSDKDemoAppkey //为explorer平台注册的应用信息(https://console.cloud.tencent.com/iotexplorer/v2/instance/app/detai) explorer控制台- 应用开发 - 选对应的应用下的 appkey/appsecret + appConfig.appSecret = + BuildConfig.TencentIotLinkSDKDemoAppSecret //为explorer平台注册的应用信息(https://console.cloud.tencent.com/iotexplorer/v2/instance/app/detai) explorer控制台- 应用开发 - 选对应的应用下的 appkey/appsecret + appConfig.userId = + "" //用户纬度(每个手机区分开)使用用户自有的账号系统userid;若无请配置为[TIoTCoreXP2PBridge sharedInstance].getAppUUID; 查找日志是需提供此userid字段 + appConfig.autoConfigFromDevice = true + appConfig.type = XP2PProtocolType.XP2P_PROTOCOL_AUTO + appConfig.crossStunTurn = false } - override fun onResume() { - super.onResume() - keepAliveThreadRuning = true - startPlayer() + override fun getContentView(): Int { + return R.layout.activity_video_preview } - override fun initView() { XP2P.setLogEnable(false, false) - presenter = EventPresenter(this@VideoPreviewMJPEGActivity) - var bundle = intent.getBundleExtra(VideoConst.VIDEO_CONFIG) - bundle?.let { - var videoConfig = bundle.getString(VideoConst.VIDEO_CONFIG) - if (TextUtils.isEmpty(videoConfig)) return@let - - var devInfo = JSON.parseObject(videoConfig, DevUrl2Preview::class.java) - devInfo?.let { - tv_title.setText(it.devName) - presenter.setDeviceName(it.devName) - presenter.setChannel(it.channel) - } - } + tv_title.setText(presenter.getDeviceName()) - var linearLayoutManager = LinearLayoutManager(this@VideoPreviewMJPEGActivity) adapter = ActionListAdapter(this@VideoPreviewMJPEGActivity, records) - list_event.layoutManager = linearLayoutManager list_event.adapter = adapter tv_video_quality.setText(R.string.video_quality_medium_str) today_tip.setText(getString(R.string.today) + " " + CommonUtils.getWeekDay(this@VideoPreviewMJPEGActivity)) records.clear() - App.data.accessInfo?.let { - presenter.setAccessId(it.accessId) - presenter.setAccessToken(it.accessToken) - presenter.setProductId(it.productId) - presenter.getEventsData(Date()) - tv_event_status.visibility = View.VISIBLE - tv_event_status.setText(R.string.loading) - audioRecordUtil = AudioRecordUtil(this, "${it.productId}/${presenter.getDeviceName()}", 16000) - } - + tv_event_status.visibility = View.VISIBLE + tv_event_status.setText(R.string.loading) + audioRecordUtil = + AudioRecordUtil(this, "${presenter.getProductId()}/${presenter.getDeviceName()}", 16000) + getDeviceP2PInfo() XP2P.setCallback(this) - XP2P.startService(this@VideoPreviewMJPEGActivity, "${App.data.accessInfo!!.productId}/${presenter.getDeviceName()}", - App.data.accessInfo!!.productId, presenter.getDeviceName() - ) //实例化对象并设置监听器 volumeChangeObserver = VolumeChangeObserver(this) @@ -159,85 +145,62 @@ class VideoPreviewMJPEGActivity : VideoBaseActivity(), EventView, TextureView.Su val density = dm.density // 屏幕密度(0.75 / 1.0 / 1.5) screenWidth = (width / density).toInt() // 屏幕宽度(dp) screenHeight = (height / density).toInt() // 屏幕高度(dp) + startPlayer() + } + + private fun startService() { + XP2P.startService( + this, + presenter.getProductId(), + presenter.getDeviceName(), + xp2pInfo, + xP2PAppConfig + ) + } + + private fun restartService() { + val id = "${presenter.getProductId()}/${presenter.getDeviceName()}" + XP2P.stopService(id) + getDeviceP2PInfo() + } + + private fun delegateHttpFlv() { + val id = "${presenter.getProductId()}/${presenter.getDeviceName()}" + val prefix = XP2P.delegateHttpFlv(id) + if (prefix.isNotEmpty()) { + urlPrefix = prefix + resetPlayer() + } else { + Toast.makeText(this, "get urlPrefix is empty", Toast.LENGTH_SHORT) + .show() + } } open fun startPlayer() { if (App.data.accessInfo == null || TextUtils.isEmpty(presenter.getDeviceName())) return player = IjkMediaPlayer() mHandler.sendEmptyMessageDelayed(MSG_UPDATE_HUD, 500) - - Thread(Runnable { - val id = "${App.data.accessInfo!!.productId}/${presenter.getDeviceName()}" - connectStartTime = System.currentTimeMillis() - val ret = XP2P.setParamsForXp2pInfo(id, App.data.accessInfo!!.accessId, - App.data.accessInfo!!.accessToken, "") - if (ret != 0) { - launch(Dispatchers.Main) { - val errInfo: String - if (ret.toString() == "-1007") { - errInfo = getString(R.string.xp2p_err_version) - } else { - errInfo = getString(R.string.error_with_code, id, ret.toString()) - } - Toast.makeText(this@VideoPreviewMJPEGActivity, errInfo, Toast.LENGTH_SHORT).show() - } - return@Runnable - } - XP2P.delegateHttpFlv(id)?.let { - urlPrefix = it - if (!TextUtils.isEmpty(urlPrefix)) { - player.let { - resetPlayer() - keepPlayerplay(id) - } - } - } - }).start() - } - - private fun keepPlayerplay(id: String?) { - if (TextUtils.isEmpty(id)) return - val accessId = App.data.accessInfo!!.accessId - val accessToken = App.data.accessInfo!!.accessToken - // 开启守护线程 - Thread{ - val objectLock = Object() - while (true) { - Log.d(tag, "id=${id} keepAliveThread wait disconnected msg") - synchronized(keepPlayThreadLock) { - keepPlayThreadLock.wait() - } - Log.d(tag, "id=${id} keepAliveThread do not wait and keepAliveThreadRuning=${keepAliveThreadRuning}") - if (!keepAliveThreadRuning) break //锁被释放后,检查守护线程是否继续运行 - - // 发现断开尝试恢复视频,每隔一秒尝试一次 - Log.d(tag, "====开始尝试重连...") - XP2P.stopService(id) - while (XP2P.startService(this@VideoPreviewMJPEGActivity, id, App.data.accessInfo!!.productId, presenter.getDeviceName())!=0 - || XP2P.setParamsForXp2pInfo(id, accessId, accessToken, "") != 0 - || getDeviceStatus(id) != 0) { - XP2P.stopService(id) - synchronized(objectLock) { - objectLock.wait(500) - } - Log.d(tag, "====正在重连...") - } - connectStartTime = System.currentTimeMillis() - - Log.d(tag, "====尝试拉流...") - XP2P.delegateHttpFlv(id)?.let { - urlPrefix = it - if (!TextUtils.isEmpty(urlPrefix)) resetPlayer() - } - } - }.start() } private fun resetPlayer() { when (tv_video_quality.text.toString()) { - getString(R.string.video_quality_high_str) -> setPlayerUrl(Command.getVideoMJPEGUrlSuffix(presenter.getChannel())) - getString(R.string.video_quality_medium_str) -> setPlayerUrl(Command.getVideoMJPEGUrlSuffix(presenter.getChannel())) - getString(R.string.video_quality_low_str) -> setPlayerUrl(Command.getVideoMJPEGUrlSuffix(presenter.getChannel())) + getString(R.string.video_quality_high_str) -> setPlayerUrl( + Command.getVideoMJPEGUrlSuffix( + presenter.getChannel() + ) + ) + + getString(R.string.video_quality_medium_str) -> setPlayerUrl( + Command.getVideoMJPEGUrlSuffix( + presenter.getChannel() + ) + ) + + getString(R.string.video_quality_low_str) -> setPlayerUrl( + Command.getVideoMJPEGUrlSuffix( + presenter.getChannel() + ) + ) } } @@ -252,20 +215,27 @@ class VideoPreviewMJPEGActivity : VideoBaseActivity(), EventView, TextureView.Su App.data.accessInfo?.let { accessInfo -> if (able) { var command = Command.getNvrIpcStatus(presenter.getChannel(), 0) - var repStatus = XP2P.postCommandRequestSync("${accessInfo.productId}/${presenter.getDeviceName()}", - command.toByteArray(), command.toByteArray().size.toLong(), 2 * 1000 * 1000) ?:"" + var repStatus = XP2P.postCommandRequestSync( + "${accessInfo.productId}/${presenter.getDeviceName()}", + command.toByteArray(), command.toByteArray().size.toLong(), 2 * 1000 * 1000 + ) ?: "" launch(Dispatchers.Main) { var retContent = StringBuilder(repStatus).toString() if (TextUtils.isEmpty(retContent)) { retContent = getString(R.string.command_with_error, command) } - Toast.makeText(this@VideoPreviewMJPEGActivity, retContent, Toast.LENGTH_SHORT).show() + Toast.makeText(this@VideoPreviewMJPEGActivity, retContent, Toast.LENGTH_SHORT) + .show() } JSONArray.parseArray(repStatus, DevStatus::class.java)?.let { if (it.size == 1 && it.get(0).status == 0) { - XP2P.runSendService("${accessInfo.productId}/${presenter.getDeviceName()}", Command.getTwoWayRadio(presenter.getChannel()), true) + XP2P.runSendService( + "${accessInfo.productId}/${presenter.getDeviceName()}", + Command.getTwoWayRadio(presenter.getChannel()), + true + ) audioRecordUtil.start() return true } @@ -301,7 +271,12 @@ class VideoPreviewMJPEGActivity : VideoBaseActivity(), EventView, TextureView.Su filePath = CommonUtils.generateFileDefaultPath() var ret = player.startRecord(filePath) if (ret != 0) { - ToastDialog(this, ToastDialog.Type.WARNING, getString(R.string.record_failed), 2000).show() + ToastDialog( + this, + ToastDialog.Type.WARNING, + getString(R.string.record_failed), + 2000 + ).show() radio_record.isChecked = false } } else { @@ -317,7 +292,12 @@ class VideoPreviewMJPEGActivity : VideoBaseActivity(), EventView, TextureView.Su } radio_photo.setOnClickListener { ImageSelect.saveBitmap(this@VideoPreviewMJPEGActivity, v_preview.bitmap) - ToastDialog(this, ToastDialog.Type.SUCCESS, getString(R.string.capture_successed), 2000).show() + ToastDialog( + this, + ToastDialog.Type.SUCCESS, + getString(R.string.capture_successed), + 2000 + ).show() } iv_up.setOnClickListener(controlListener) iv_down.setOnClickListener(controlListener) @@ -346,7 +326,7 @@ class VideoPreviewMJPEGActivity : VideoBaseActivity(), EventView, TextureView.Su open var controlListener = object : View.OnClickListener { override fun onClick(v: View?) { var command = "" - when(v) { + when (v) { iv_up -> command = Command.getPtzUpCommand(presenter.getChannel()) iv_down -> command = Command.getPtzDownCommand(presenter.getChannel()) iv_right -> command = Command.getPtzRightCommand(presenter.getChannel()) @@ -356,13 +336,19 @@ class VideoPreviewMJPEGActivity : VideoBaseActivity(), EventView, TextureView.Su Thread(Runnable { App.data.accessInfo?.let { if (command.length <= 0) return@Runnable - var retContent = XP2P.postCommandRequestSync("${it.productId}/${presenter.getDeviceName()}", - command.toByteArray(), command.toByteArray().size.toLong(), 2 * 1000 * 1000)?:"" + var retContent = XP2P.postCommandRequestSync( + "${it.productId}/${presenter.getDeviceName()}", + command.toByteArray(), command.toByteArray().size.toLong(), 2 * 1000 * 1000 + ) ?: "" launch(Dispatchers.Main) { if (TextUtils.isEmpty(retContent)) { retContent = getString(R.string.command_with_error, command) } - Toast.makeText(this@VideoPreviewMJPEGActivity, retContent, Toast.LENGTH_SHORT).show() + Toast.makeText( + this@VideoPreviewMJPEGActivity, + retContent, + Toast.LENGTH_SHORT + ).show() } } }).start() @@ -386,9 +372,11 @@ class VideoPreviewMJPEGActivity : VideoBaseActivity(), EventView, TextureView.Su } private fun showVVideoQualityDialog() { - var options = arrayListOf(getString(R.string.video_quality_high_str) + " " + getString(R.string.video_quality_high), - getString(R.string.video_quality_medium_str) + " " + getString(R.string.video_quality_medium), - getString(R.string.video_quality_low_str) + " " + getString(R.string.video_quality_low)) + var options = arrayListOf( + getString(R.string.video_quality_high_str) + " " + getString(R.string.video_quality_high), + getString(R.string.video_quality_medium_str) + " " + getString(R.string.video_quality_medium), + getString(R.string.video_quality_low_str) + " " + getString(R.string.video_quality_low) + ) var dlg = ListOptionsDialog(this@VideoPreviewMJPEGActivity, options) dlg.show() dlg.setOnDismisListener { chgTextState(it) } @@ -396,7 +384,7 @@ class VideoPreviewMJPEGActivity : VideoBaseActivity(), EventView, TextureView.Su private fun showHVideoQualityDialog() { var pos = -1 - when(tv_video_quality.text.toString()) { + when (tv_video_quality.text.toString()) { getString(R.string.video_quality_high_str) -> pos = 2 getString(R.string.video_quality_medium_str) -> pos = 1 getString(R.string.video_quality_low_str) -> pos = 0 @@ -405,21 +393,28 @@ class VideoPreviewMJPEGActivity : VideoBaseActivity(), EventView, TextureView.Su dlg.show() btn_layout.visibility = View.GONE dlg.setOnDismisListener(object : VideoQualityDialog.OnDismisListener { - override fun onItemClicked(pos: Int) { chgTextState(pos) } - override fun onDismiss() { btn_layout.visibility = View.VISIBLE } + override fun onItemClicked(pos: Int) { + chgTextState(pos) + } + + override fun onDismiss() { + btn_layout.visibility = View.VISIBLE + } }) } open fun chgTextState(value: Int) { - when(value) { + when (value) { 0 -> { tv_video_quality.setText(R.string.video_quality_high_str) setPlayerUrl(Command.getVideoMJPEGUrlSuffix(presenter.getChannel())) } + 1 -> { tv_video_quality.setText(R.string.video_quality_medium_str) setPlayerUrl(Command.getVideoMJPEGUrlSuffix(presenter.getChannel())) } + 2 -> { tv_video_quality.setText(R.string.video_quality_low_str) setPlayerUrl(Command.getVideoMJPEGUrlSuffix(presenter.getChannel())) @@ -434,12 +429,12 @@ class VideoPreviewMJPEGActivity : VideoBaseActivity(), EventView, TextureView.Su startShowVideoTime = System.currentTimeMillis() player.release() audioPlayer?.release() - launch (Dispatchers.Main) { + launch(Dispatchers.Main) { layout_video_preview?.removeView(v_preview) layout_video_preview?.addView(v_preview, 0) player = IjkMediaPlayer() - player?.let { + player.let { val url = urlPrefix + suffix it.reset() @@ -449,9 +444,13 @@ class VideoPreviewMJPEGActivity : VideoBaseActivity(), EventView, TextureView.Su it.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "start-on-prepared", 1) it.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, "threads", 1) it.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "sync-av-start", 0) - it.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec",1) + it.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 1) it.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-auto-rotate", 1) - it.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-handle-resolution-change", 1) + it.setOption( + IjkMediaPlayer.OPT_CATEGORY_PLAYER, + "mediacodec-handle-resolution-change", + 1 + ) it.setFrameSpeed(1.5f) while (!::surface.isInitialized) { @@ -477,9 +476,13 @@ class VideoPreviewMJPEGActivity : VideoBaseActivity(), EventView, TextureView.Su it.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "start-on-prepared", 1) it.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, "threads", 1) it.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "sync-av-start", 0) - it.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec",1) + it.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 1) it.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-auto-rotate", 1) - it.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-handle-resolution-change", 1) + it.setOption( + IjkMediaPlayer.OPT_CATEGORY_PLAYER, + "mediacodec-handle-resolution-change", + 1 + ) it.setFrameSpeed(1.5f) while (!::surface.isInitialized) { @@ -495,7 +498,7 @@ class VideoPreviewMJPEGActivity : VideoBaseActivity(), EventView, TextureView.Su } } - private fun switchOrientation(orientation : Boolean) { + private fun switchOrientation(orientation: Boolean) { var marginWidth = 0 var layoutParams = layout_video_preview.layoutParams as ConstraintLayout.LayoutParams var fitSize = 0 @@ -529,7 +532,7 @@ class VideoPreviewMJPEGActivity : VideoBaseActivity(), EventView, TextureView.Su } override fun eventReady(events: MutableList) { - if (events == null || events.size <= 0) { + if (events.size <= 0) { launch(Dispatchers.Main) { tv_event_status.setText(R.string.no_data) } @@ -554,11 +557,16 @@ class VideoPreviewMJPEGActivity : VideoBaseActivity(), EventView, TextureView.Su v_preview.layoutParams = layoutParams } - override fun onSurfaceTextureDestroyed(surface: SurfaceTexture?): Boolean { return false } + + override fun onSurfaceTextureDestroyed(surface: SurfaceTexture?): Boolean { + return false + } + override fun onSurfaceTextureUpdated(surface: SurfaceTexture?) { if (!showTip && startShowVideoTime > 0) { showVideoTime = System.currentTimeMillis() - startShowVideoTime - var content = getString(R.string.time_2_show, connectTime.toString(), showVideoTime.toString()) + var content = + getString(R.string.time_2_show, connectTime.toString(), showVideoTime.toString()) TipToastDialog(this, content, 10000).show() showTip = true } @@ -570,7 +578,11 @@ class VideoPreviewMJPEGActivity : VideoBaseActivity(), EventView, TextureView.Su firstIn = false } } + override fun fail(msg: String?, errorCode: Int) {} + override fun updateXp2pInfo(xp2pInfo: String) { + startService() + } override fun commandRequest(id: String?, msg: String?) {} override fun avDataRecvHandle(id: String?, data: ByteArray?, len: Int) {} override fun avDataCloseHandle(id: String?, msg: String?, errorCode: Int) {} @@ -583,20 +595,16 @@ class VideoPreviewMJPEGActivity : VideoBaseActivity(), EventView, TextureView.Su if (event == 1003) { Log.e(tag, "====event === 1003") startShowVideoTime = 0L - keepPlayThreadLock?.let { - synchronized(it) { - Log.d(tag, "====p2p链路断开, event=$event.") - it.notify() - } - } // 唤醒守护线程 launch(Dispatchers.Main) { - var content = getString(R.string.disconnected_and_reconnecting, id) + val content = getString(R.string.disconnected_and_reconnecting, id) Toast.makeText(this@VideoPreviewMJPEGActivity, content, Toast.LENGTH_SHORT).show() } + resetPlayer() } else if (event == 1004 || event == 1005) { connectTime = System.currentTimeMillis() - connectStartTime if (event == 1004) { Log.e(tag, "====event === 1004") + delegateHttpFlv() } } else if (event == 1010) { Log.e(tag, "====event === 1010, 校验失败,info撞库防止串流: $msg") @@ -610,7 +618,7 @@ class VideoPreviewMJPEGActivity : VideoBaseActivity(), EventView, TextureView.Su private fun finishPlayer() { mHandler.removeMessages(MSG_UPDATE_HUD) - player?.release() + player.release() if (radio_talk.isChecked) speakAble(false) if (radio_record.isChecked) { player.stopRecord() @@ -618,14 +626,6 @@ class VideoPreviewMJPEGActivity : VideoBaseActivity(), EventView, TextureView.Su } audioPlayer?.release() - countDownLatchs.clear() - // 关闭守护线程 - keepAliveThreadRuning = false - keepPlayThreadLock?.let { - synchronized(it) { - it.notify() - } - } } override fun onDestroy() { @@ -641,7 +641,7 @@ class VideoPreviewMJPEGActivity : VideoBaseActivity(), EventView, TextureView.Su companion object { fun startPreviewActivity(context: Context?, dev: DevInfo) { - context?:let { return } + context ?: let { return } var intent = Intent(context, VideoPreviewMJPEGActivity::class.java) var bundle = Bundle() @@ -665,18 +665,25 @@ class VideoPreviewMJPEGActivity : VideoBaseActivity(), EventView, TextureView.Su var command: ByteArray? = null when (tv_video_quality.text.toString()) { getString(R.string.video_quality_high_str) -> { - command = "action=inner_define&channel=0&cmd=get_device_st&type=live&quality=super".toByteArray() + command = + "action=inner_define&channel=0&cmd=get_device_st&type=live&quality=super".toByteArray() } + getString(R.string.video_quality_medium_str) -> { - command = "action=inner_define&channel=0&cmd=get_device_st&type=live&quality=high".toByteArray() + command = + "action=inner_define&channel=0&cmd=get_device_st&type=live&quality=high".toByteArray() } + getString(R.string.video_quality_low_str) -> { - command = "action=inner_define&channel=0&cmd=get_device_st&type=live&quality=standard".toByteArray() + command = + "action=inner_define&channel=0&cmd=get_device_st&type=live&quality=standard".toByteArray() } } - val reponse = XP2P.postCommandRequestSync(id, command, command!!.size.toLong(), 2 * 1000 * 1000) + val reponse = + XP2P.postCommandRequestSync(id, command, command!!.size.toLong(), 2 * 1000 * 1000) if (!TextUtils.isEmpty(reponse)) { - val deviceStatuses: List = JSONArray.parseArray(reponse, DeviceStatus::class.java) + val deviceStatuses: List = + JSONArray.parseArray(reponse, DeviceStatus::class.java) // 0 接收请求 // 1 拒绝请求 // 404 error request message @@ -687,22 +694,30 @@ class VideoPreviewMJPEGActivity : VideoBaseActivity(), EventView, TextureView.Su runOnUiThread { when (deviceStatuses[0].status) { 0 -> Toast.makeText(this, "设备状态正常", Toast.LENGTH_SHORT).show() - 1 -> Toast.makeText(this, "设备状态异常, 拒绝请求: $reponse", Toast.LENGTH_SHORT).show() + 1 -> Toast.makeText( + this, + "设备状态异常, 拒绝请求: $reponse", + Toast.LENGTH_SHORT + ).show() + 404 -> Toast.makeText( this, "设备状态异常, error request message: $reponse", Toast.LENGTH_SHORT ).show() + 405 -> Toast.makeText( this, "设备状态异常, connect number too many: $reponse", Toast.LENGTH_SHORT ).show() + 406 -> Toast.makeText( this, "设备状态异常, current command don't support: $reponse", Toast.LENGTH_SHORT ).show() + 407 -> Toast.makeText( this, "设备状态异常, device process error: $reponse", @@ -724,7 +739,7 @@ class VideoPreviewMJPEGActivity : VideoBaseActivity(), EventView, TextureView.Su private val mHandler = MyHandler(this) private class MyHandler(activity: VideoPreviewMJPEGActivity) : Handler() { - private val mActivity: WeakReference = WeakReference(activity) + private val mActivity: WeakReference = WeakReference(activity) override fun handleMessage(msg: Message) { if (mActivity.get() == null) { return @@ -747,14 +762,20 @@ class VideoPreviewMJPEGActivity : VideoBaseActivity(), EventView, TextureView.Su val audioCachedBytes = player.audioCachedBytes val tcpSpeed = player.tcpSpeed - tv_a_cache?.text = String.format(Locale.US, "%s, %s", + tv_a_cache?.text = String.format( + Locale.US, "%s, %s", CommonUtils.formatedDurationMilli(audioCachedDuration), - CommonUtils.formatedSize(audioCachedBytes)) - tv_v_cache?.text = String.format(Locale.US, "%s, %s", + CommonUtils.formatedSize(audioCachedBytes) + ) + tv_v_cache?.text = String.format( + Locale.US, "%s, %s", CommonUtils.formatedDurationMilli(videoCachedDuration), - CommonUtils.formatedSize(videoCachedBytes)) - tv_tcp_speed?.text = String.format(Locale.US, "%s", - CommonUtils.formatedSpeed(tcpSpeed, 1000)) + CommonUtils.formatedSize(videoCachedBytes) + ) + tv_tcp_speed?.text = String.format( + Locale.US, "%s", + CommonUtils.formatedSpeed(tcpSpeed, 1000) + ) tv_video_w_h?.text = "${player.videoWidth} x ${player.videoHeight}" } } \ No newline at end of file diff --git a/sdkdemo/src/main/java/com/tencent/iot/explorer/link/demo/video/preview/VideoPushStreamActivity.kt b/sdkdemo/src/main/java/com/tencent/iot/explorer/link/demo/video/preview/VideoPushStreamActivity.kt index 9022bb99f..3a345abe1 100644 --- a/sdkdemo/src/main/java/com/tencent/iot/explorer/link/demo/video/preview/VideoPushStreamActivity.kt +++ b/sdkdemo/src/main/java/com/tencent/iot/explorer/link/demo/video/preview/VideoPushStreamActivity.kt @@ -12,18 +12,20 @@ import android.os.Build import android.os.Bundle import android.os.Handler import android.text.TextUtils -import android.util.DisplayMetrics import android.util.Log -import android.view.* +import android.view.Surface +import android.view.SurfaceHolder +import android.view.TextureView import android.widget.Toast import androidx.annotation.RequiresApi import com.alibaba.fastjson.JSON import com.alibaba.fastjson.JSONArray import com.tencent.iot.explorer.link.demo.App +import com.tencent.iot.explorer.link.demo.BuildConfig import com.tencent.iot.explorer.link.demo.R -import com.tencent.iot.explorer.link.demo.VideoBaseActivity import com.tencent.iot.explorer.link.demo.video.Command import com.tencent.iot.explorer.link.demo.video.DevInfo +import com.tencent.iot.explorer.link.demo.video.VideoPreviewBaseActivity import com.tencent.iot.explorer.link.demo.video.playback.cloudPlayback.event.ActionRecord import com.tencent.iot.explorer.link.demo.video.playback.cloudPlayback.event.EventPresenter import com.tencent.iot.explorer.link.demo.video.playback.cloudPlayback.event.EventView @@ -40,33 +42,31 @@ import com.tencent.iot.video.link.param.MicParam import com.tencent.iot.video.link.param.VideoEncodeParam import com.tencent.iot.video.link.util.CameraUtils import com.tencent.xnet.XP2P +import com.tencent.xnet.XP2PAppConfig import com.tencent.xnet.XP2PCallback -import kotlinx.android.synthetic.main.activity_video_push_stream.* -import kotlinx.android.synthetic.main.dash_board_layout.* -import kotlinx.android.synthetic.main.title_layout.* -import kotlinx.coroutines.* +import com.tencent.xnet.annotations.XP2PProtocolType +import kotlinx.android.synthetic.main.activity_video_push_stream.sv_camera_view +import kotlinx.android.synthetic.main.title_layout.iv_back +import kotlinx.android.synthetic.main.title_layout.tv_title +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.MainScope +import kotlinx.coroutines.cancel +import kotlinx.coroutines.launch import java.io.IOException -import java.lang.Runnable -import java.util.* -import java.util.concurrent.ConcurrentHashMap -import java.util.concurrent.CountDownLatch +import java.util.Date import java.util.concurrent.Executors - -private var countDownLatchs : MutableMap = ConcurrentHashMap() -private var keepPlayThreadLock = Object() -@Volatile -private var keepAliveThreadRuning = true - -class VideoPushStreamActivity : VideoBaseActivity(), EventView, TextureView.SurfaceTextureListener, +class VideoPushStreamActivity : VideoPreviewBaseActivity(), EventView, + TextureView.SurfaceTextureListener, XP2PCallback, CoroutineScope by MainScope(), SurfaceHolder.Callback, OnEncodeListener { open var tag = VideoPushStreamActivity::class.simpleName - lateinit var presenter: EventPresenter lateinit var surface: Surface var holder: SurfaceHolder? = null var camera: Camera? = null + // 默认摄像头方向 var facing: Int = CameraConstants.facing.BACK val vw = 320 @@ -74,11 +74,16 @@ class VideoPushStreamActivity : VideoBaseActivity(), EventView, TextureView.Surf val frameRate = 15 val flvListener = FLVListener { data: ByteArray -> // Log.e(TAG, "===== dataLen:" + data.size); - XP2P.dataSend("${App.data.accessInfo!!.productId}/${presenter.getDeviceName()}", data, data.size) + XP2P.dataSend( + "${App.data.accessInfo!!.productId}/${presenter.getDeviceName()}", + data, + data.size + ) } var audioEncoder: AudioEncoder? = null var videoEncoder: VideoEncoder? = null var flvPacker: FLVPacker? = null + @Volatile var startEncodeVideo = false var executor = Executors.newSingleThreadExecutor() @@ -87,11 +92,24 @@ class VideoPushStreamActivity : VideoBaseActivity(), EventView, TextureView.Surf @Volatile var urlPrefix = "" var permissions = arrayOf(Manifest.permission.RECORD_AUDIO) + @Volatile var connectStartTime = 0L + @Volatile var connectTime = 0L val MSG_UPDATE_HUD = 1 + private val xP2PAppConfig = XP2PAppConfig().also { appConfig -> + appConfig.appKey = + BuildConfig.TencentIotLinkSDKDemoAppkey //为explorer平台注册的应用信息(https://console.cloud.tencent.com/iotexplorer/v2/instance/app/detai) explorer控制台- 应用开发 - 选对应的应用下的 appkey/appsecret + appConfig.appSecret = + BuildConfig.TencentIotLinkSDKDemoAppSecret //为explorer平台注册的应用信息(https://console.cloud.tencent.com/iotexplorer/v2/instance/app/detai) explorer控制台- 应用开发 - 选对应的应用下的 appkey/appsecret + appConfig.userId = + "" //用户纬度(每个手机区分开)使用用户自有的账号系统userid;若无请配置为[TIoTCoreXP2PBridge sharedInstance].getAppUUID; 查找日志是需提供此userid字段 + appConfig.autoConfigFromDevice = true + appConfig.type = XP2PProtocolType.XP2P_PROTOCOL_AUTO + appConfig.crossStunTurn = false + } override fun getContentView(): Int { return R.layout.activity_video_push_stream @@ -99,39 +117,41 @@ class VideoPushStreamActivity : VideoBaseActivity(), EventView, TextureView.Surf override fun onResume() { super.onResume() - keepAliveThreadRuning = true - startPlayer() holder?.addCallback(this) } override fun initView() { - presenter = EventPresenter(this@VideoPushStreamActivity) - var bundle = intent.getBundleExtra(VideoConst.VIDEO_CONFIG) - bundle?.let { - var videoConfig = bundle.getString(VideoConst.VIDEO_CONFIG) - if (TextUtils.isEmpty(videoConfig)) return@let - - var devInfo = JSON.parseObject(videoConfig, DevUrl2Preview::class.java) - devInfo?.let { - tv_title.setText(it.devName) - presenter.setDeviceName(it.devName) - presenter.setChannel(it.channel) - } - } + tv_title.setText(presenter.getDeviceName()) + XP2P.setCallback(this) - App.data.accessInfo?.let { - presenter.setAccessId(it.accessId) - presenter.setAccessToken(it.accessToken) - presenter.setProductId(it.productId) - presenter.getEventsData(Date()) - } + holder = sv_camera_view.holder + } - XP2P.setCallback(this) - XP2P.startService(this@VideoPushStreamActivity, "${App.data.accessInfo!!.productId}/${presenter.getDeviceName()}", - App.data.accessInfo!!.productId, presenter.getDeviceName() + private fun startService() { + XP2P.startService( + this, + presenter.getProductId(), + presenter.getDeviceName(), + xp2pInfo, + xP2PAppConfig ) + } - holder = sv_camera_view.holder + private fun restartService() { + val id = "${presenter.getProductId()}/${presenter.getDeviceName()}" + XP2P.stopService(id) + getDeviceP2PInfo() + } + + private fun delegateHttpFlv() { + val id = "${presenter.getProductId()}/${presenter.getDeviceName()}" + val prefix = XP2P.delegateHttpFlv(id) + if (prefix.isNotEmpty()) { + setPlayerUrl(Command.getVideoStandardQualityUrlSuffix(presenter.getChannel())) + } else { + Toast.makeText(this, "get urlPrefix is empty", Toast.LENGTH_SHORT) + .show() + } } private fun initAudioEncoder() { @@ -148,7 +168,8 @@ class VideoPushStreamActivity : VideoBaseActivity(), EventView, TextureView.Surf private fun initVideoEncoder() { val videoEncodeParam: VideoEncodeParam = - VideoEncodeParam.Builder().setSize(vw, vh).setFrameRate(frameRate).setBitRate(vw * vh * 4).build() + VideoEncodeParam.Builder().setSize(vw, vh).setFrameRate(frameRate) + .setBitRate(vw * vh * 4).build() videoEncoder = VideoEncoder(videoEncodeParam) videoEncoder!!.setEncoderListener(this) } @@ -156,7 +177,7 @@ class VideoPushStreamActivity : VideoBaseActivity(), EventView, TextureView.Surf @RequiresApi(Build.VERSION_CODES.LOLLIPOP) private fun startRecord() { // if (callType == CallingType.TYPE_VIDEO_CALL) { - startEncodeVideo = true + startEncodeVideo = true // } audioEncoder!!.start() } @@ -171,74 +192,60 @@ class VideoPushStreamActivity : VideoBaseActivity(), EventView, TextureView.Surf startEncodeVideo = false } - open fun startPlayer() { - if (App.data.accessInfo == null || TextUtils.isEmpty(presenter.getDeviceName())) return - - Thread(Runnable { - val id = "${App.data.accessInfo!!.productId}/${presenter.getDeviceName()}" - connectStartTime = System.currentTimeMillis() - val ret = XP2P.setParamsForXp2pInfo(id, App.data.accessInfo!!.accessId, - App.data.accessInfo!!.accessToken, "") - if (ret != 0) { - launch(Dispatchers.Main) { - val errInfo: String - if (ret.toString() == "-1007") { - errInfo = getString(R.string.xp2p_err_version) - } else { - errInfo = getString(R.string.error_with_code, id, ret.toString()) - } - Toast.makeText(this@VideoPushStreamActivity, errInfo, Toast.LENGTH_SHORT).show() - } - return@Runnable - } - XP2P.delegateHttpFlv(id)?.let { - urlPrefix = it - if (!TextUtils.isEmpty(urlPrefix)) { - setPlayerUrl(Command.getVideoStandardQualityUrlSuffix(presenter.getChannel())) - keepPlayerplay(id) - } - } - }).start() - } - private fun keepPlayerplay(id: String?) { - if (TextUtils.isEmpty(id)) return - val accessId = App.data.accessInfo!!.accessId - val accessToken = App.data.accessInfo!!.accessToken - // 开启守护线程 - Thread{ - val objectLock = Object() - while (true) { - Log.d(tag, "id=${id} keepAliveThread wait disconnected msg") - synchronized(keepPlayThreadLock) { - keepPlayThreadLock.wait() - } - Log.d(tag, "id=${id} keepAliveThread do not wait and keepAliveThreadRuning=${keepAliveThreadRuning}") - if (!keepAliveThreadRuning) break //锁被释放后,检查守护线程是否继续运行 - flvPacker = null - stopRecord() - // 发现断开尝试恢复视频,每隔一秒尝试一次 - Log.d(tag, "====开始尝试重连...") - XP2P.stopService(id) - while (XP2P.startService(this@VideoPushStreamActivity, id, App.data.accessInfo!!.productId, presenter.getDeviceName())!=0 - || XP2P.setParamsForXp2pInfo(id, accessId, accessToken, "") != 0 - || getDeviceStatus(id) != 0) { - XP2P.stopService(id) - synchronized(objectLock) { - objectLock.wait(500) - } - Log.d(tag, "====正在重连...") - } - connectStartTime = System.currentTimeMillis() - - Log.d(tag, "====尝试拉流...") - XP2P.delegateHttpFlv(id)?.let { - urlPrefix = it - if (!TextUtils.isEmpty(urlPrefix)) setPlayerUrl(Command.getVideoStandardQualityUrlSuffix(presenter.getChannel())) - } - } - }.start() - } +// private fun keepPlayerplay(id: String?) { +// if (TextUtils.isEmpty(id)) return +// val accessId = App.data.accessInfo!!.accessId +// val accessToken = App.data.accessInfo!!.accessToken +// // 开启守护线程 +// Thread { +// val objectLock = Object() +// while (true) { +// Log.d(tag, "id=${id} keepAliveThread wait disconnected msg") +// synchronized(keepPlayThreadLock) { +// keepPlayThreadLock.wait() +// } +// Log.d( +// tag, +// "id=${id} keepAliveThread do not wait and keepAliveThreadRuning=${keepAliveThreadRuning}" +// ) +// if (!keepAliveThreadRuning) break //锁被释放后,检查守护线程是否继续运行 +// flvPacker = null +// stopRecord() +// // 发现断开尝试恢复视频,每隔一秒尝试一次 +// Log.d(tag, "====开始尝试重连...") +// XP2P.stopService(id) +// XP2P.startService( +// this@VideoPushStreamActivity, +// id, +// App.data.accessInfo!!.productId, +// presenter.getDeviceName(), +// xP2PAppConfig +// ) +// while (XP2P.setParamsForXp2pInfo(id, "") != 0 +// || getDeviceStatus(id) != 0 +// ) { +// XP2P.stopService(id) +// synchronized(objectLock) { +// objectLock.wait(500) +// } +// Log.d(tag, "====正在重连...") +// } +// connectStartTime = System.currentTimeMillis() +// +// Log.d(tag, "====尝试拉流...") +// XP2P.delegateHttpFlv(id)?.let { +// urlPrefix = it +// if (!TextUtils.isEmpty(urlPrefix)) setPlayerUrl( +// Command.getVideoStandardQualityUrlSuffix( +// presenter.getChannel() +// ) +// ) +// } +// +// } +// }.start() +// } override fun onSurfaceTextureAvailable(surface: SurfaceTexture?, width: Int, height: Int) {} @@ -247,10 +254,12 @@ class VideoPushStreamActivity : VideoBaseActivity(), EventView, TextureView.Surf } open fun setPlayerUrl(suffix: String) { - launch (Dispatchers.Main) { + launch(Dispatchers.Main) { var command = Command.getNvrIpcStatus(presenter.getChannel(), 0) - var repStatus = XP2P.postCommandRequestSync("${App.data.accessInfo?.productId}/${presenter.getDeviceName()}", - command.toByteArray(), command.toByteArray().size.toLong(), 2 * 1000 * 1000) ?:"" + var repStatus = XP2P.postCommandRequestSync( + "${App.data.accessInfo?.productId}/${presenter.getDeviceName()}", + command.toByteArray(), command.toByteArray().size.toLong(), 2 * 1000 * 1000 + ) ?: "" launch(Dispatchers.Main) { var retContent = StringBuilder(repStatus).toString() @@ -261,7 +270,11 @@ class VideoPushStreamActivity : VideoBaseActivity(), EventView, TextureView.Surf } // 开始推流 - XP2P.runSendService("${App.data.accessInfo?.productId}/${presenter.getDeviceName()}", "channel=0", false) + XP2P.runSendService( + "${App.data.accessInfo?.productId}/${presenter.getDeviceName()}", + "channel=0", + false + ) handler.post(Runnable { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { initAudioEncoder() @@ -277,9 +290,19 @@ class VideoPushStreamActivity : VideoBaseActivity(), EventView, TextureView.Surf } override fun onSurfaceTextureSizeChanged(surface: SurfaceTexture?, width: Int, height: Int) {} - override fun onSurfaceTextureDestroyed(surface: SurfaceTexture?): Boolean { return false } + override fun onSurfaceTextureDestroyed(surface: SurfaceTexture?): Boolean { + return false + } + override fun onSurfaceTextureUpdated(surface: SurfaceTexture?) {} override fun fail(msg: String?, errorCode: Int) {} + override fun updateXp2pInfo(xp2pInfo: String) { + startService() + } + + override fun success(response: String?, reqCode: Int) { + } + override fun commandRequest(id: String?, msg: String?) {} override fun avDataRecvHandle(id: String?, data: ByteArray?, len: Int) {} override fun avDataCloseHandle(id: String?, msg: String?, errorCode: Int) {} @@ -291,20 +314,16 @@ class VideoPushStreamActivity : VideoBaseActivity(), EventView, TextureView.Surf Log.e(tag, "id=${id}, event=${event}") if (event == 1003) { Log.e(tag, "====event === 1003") - keepPlayThreadLock?.let { - synchronized(it) { - Log.d(tag, "====p2p链路断开, event=$event.") - it.notify() - } - } // 唤醒守护线程 launch(Dispatchers.Main) { var content = getString(R.string.disconnected_and_reconnecting, id) Toast.makeText(this@VideoPushStreamActivity, content, Toast.LENGTH_SHORT).show() } + restartService() } else if (event == 1004 || event == 1005) { connectTime = System.currentTimeMillis() - connectStartTime if (event == 1004) { Log.e(tag, "====event === 1004") + delegateHttpFlv() } } else if (event == 1010) { Log.e(tag, "====event === 1010, 校验失败,info撞库防止串流: $msg") @@ -313,36 +332,20 @@ class VideoPushStreamActivity : VideoBaseActivity(), EventView, TextureView.Surf override fun onPause() { super.onPause() - finishPlayer() - } - - private fun finishPlayer() { - - countDownLatchs.clear() - // 关闭守护线程 - keepAliveThreadRuning = false - keepPlayThreadLock?.let { - synchronized(it) { - it.notify() - } - } } override fun onDestroy() { super.onDestroy() stopRecord() executor.shutdown() - finishPlayer() - App.data.accessInfo?.let { - XP2P.stopService("${it.productId}/${presenter.getDeviceName()}") - } + XP2P.stopService("${presenter.getProductId()}/${presenter.getDeviceName()}") XP2P.setCallback(null) cancel() } companion object { fun startPreviewActivity(context: Context?, dev: DevInfo) { - context?:let { return } + context ?: let { return } var intent = Intent(context, VideoPushStreamActivity::class.java) var bundle = Bundle() @@ -358,10 +361,13 @@ class VideoPushStreamActivity : VideoBaseActivity(), EventView, TextureView.Surf private fun getDeviceStatus(id: String?): Int { var command: ByteArray? = null - command = "action=inner_define&channel=0&cmd=get_device_st&type=live&quality=high".toByteArray() - val reponse = XP2P.postCommandRequestSync(id, command, command!!.size.toLong(), 2 * 1000 * 1000) + command = + "action=inner_define&channel=0&cmd=get_device_st&type=live&quality=high".toByteArray() + val reponse = + XP2P.postCommandRequestSync(id, command, command!!.size.toLong(), 2 * 1000 * 1000) if (!TextUtils.isEmpty(reponse)) { - val deviceStatuses: List = JSONArray.parseArray(reponse, DeviceStatus::class.java) + val deviceStatuses: List = + JSONArray.parseArray(reponse, DeviceStatus::class.java) // 0 接收请求 // 1 拒绝请求 // 404 error request message @@ -372,22 +378,30 @@ class VideoPushStreamActivity : VideoBaseActivity(), EventView, TextureView.Surf runOnUiThread { when (deviceStatuses[0].status) { 0 -> Toast.makeText(this, "设备状态正常", Toast.LENGTH_SHORT).show() - 1 -> Toast.makeText(this, "设备状态异常, 拒绝请求: $reponse", Toast.LENGTH_SHORT).show() + 1 -> Toast.makeText( + this, + "设备状态异常, 拒绝请求: $reponse", + Toast.LENGTH_SHORT + ).show() + 404 -> Toast.makeText( this, "设备状态异常, error request message: $reponse", Toast.LENGTH_SHORT ).show() + 405 -> Toast.makeText( this, "设备状态异常, connect number too many: $reponse", Toast.LENGTH_SHORT ).show() + 406 -> Toast.makeText( this, "设备状态异常, current command don't support: $reponse", Toast.LENGTH_SHORT ).show() + 407 -> Toast.makeText( this, "设备状态异常, device process error: $reponse", diff --git a/sdkdemo/src/main/java/com/tencent/iot/explorer/link/demo/video/preview/VideoTestActivity.kt b/sdkdemo/src/main/java/com/tencent/iot/explorer/link/demo/video/preview/VideoTestActivity.kt index 7de54d7ed..1ca145e9d 100644 --- a/sdkdemo/src/main/java/com/tencent/iot/explorer/link/demo/video/preview/VideoTestActivity.kt +++ b/sdkdemo/src/main/java/com/tencent/iot/explorer/link/demo/video/preview/VideoTestActivity.kt @@ -3,7 +3,6 @@ package com.tencent.iot.explorer.link.demo.video.preview import android.Manifest import android.graphics.SurfaceTexture import android.media.AudioFormat -import android.os.Bundle import android.os.Handler import android.os.Message import android.text.TextUtils @@ -15,7 +14,7 @@ import android.view.View import android.view.WindowManager import android.widget.Toast import com.alibaba.fastjson.JSONArray -import com.tencent.iot.explorer.link.demo.App +import com.tencent.iot.explorer.link.demo.BuildConfig import com.tencent.iot.explorer.link.demo.R import com.tencent.iot.explorer.link.demo.VideoBaseActivity import com.tencent.iot.explorer.link.demo.common.log.L @@ -28,7 +27,9 @@ import com.tencent.iot.explorer.link.demo.video.utils.TipToastDialog import com.tencent.iot.explorer.link.demo.video.utils.ToastDialog import com.tencent.iot.video.link.util.audio.AudioRecordUtil import com.tencent.xnet.XP2P +import com.tencent.xnet.XP2PAppConfig import com.tencent.xnet.XP2PCallback +import com.tencent.xnet.annotations.XP2PProtocolType import kotlinx.android.synthetic.main.activity_video_preview.iv_down import kotlinx.android.synthetic.main.activity_video_preview.iv_left import kotlinx.android.synthetic.main.activity_video_preview.iv_right @@ -80,6 +81,18 @@ open class VideoTestActivity : VideoBaseActivity(), XP2PCallback, CoroutineScope @Volatile var speakAble = false + private val xP2PAppConfig = XP2PAppConfig().also { appConfig -> + appConfig.appKey = + BuildConfig.TencentIotLinkSDKDemoAppkey //为explorer平台注册的应用信息(https://console.cloud.tencent.com/iotexplorer/v2/instance/app/detai) explorer控制台- 应用开发 - 选对应的应用下的 appkey/appsecret + appConfig.appSecret = + BuildConfig.TencentIotLinkSDKDemoAppSecret //为explorer平台注册的应用信息(https://console.cloud.tencent.com/iotexplorer/v2/instance/app/detai) explorer控制台- 应用开发 - 选对应的应用下的 appkey/appsecret + appConfig.userId = + "" //用户纬度(每个手机区分开)使用用户自有的账号系统userid;若无请配置为[TIoTCoreXP2PBridge sharedInstance].getAppUUID; 查找日志是需提供此userid字段 + appConfig.autoConfigFromDevice = true + appConfig.type = XP2PProtocolType.XP2P_PROTOCOL_AUTO + appConfig.crossStunTurn = false + } + override fun getContentView(): Int { return R.layout.activity_video_test } @@ -96,26 +109,6 @@ open class VideoTestActivity : VideoBaseActivity(), XP2PCallback, CoroutineScope AudioFormat.ENCODING_PCM_16BIT ) XP2P.setCallback(this) - XP2P.startService(this, "${productId}/${deviceName}", productId, deviceName) - - val ret = XP2P.setParamsForXp2pInfo( - "${productId}/${deviceName}", "", "", xp2pInfo - ) - if (ret != 0) { - launch(Dispatchers.Main) { - val errInfo: String - if (ret.toString() == "-1007") { - errInfo = getString(R.string.xp2p_err_version) - } else { - errInfo = getString( - R.string.error_with_code, - "${productId}/${deviceName}", - ret.toString() - ) - } - Toast.makeText(this@VideoTestActivity, errInfo, Toast.LENGTH_SHORT).show() - } - } tv_video_quality.text = "高清" v_preview.surfaceTextureListener = this @@ -129,6 +122,35 @@ open class VideoTestActivity : VideoBaseActivity(), XP2PCallback, CoroutineScope screenHeight = (height / density).toInt() // 屏幕高度(dp) } + private fun startService() { + XP2P.startService( + this, + productId, + deviceName, + xp2pInfo, + xP2PAppConfig + ) + } + + private fun restartService() { + val id = "${productId}/${deviceName}" + XP2P.stopService(id) + } + + private fun delegateHttpFlv() { + val id = "${productId}/${deviceName}" +// XP2P.recordstreamPath("/storage/emulated/0/data_video.flv") //自定义采集裸流路径 +// XP2P.recordstream(id) //开启自定义采集裸流 + val prefix = XP2P.delegateHttpFlv(id) + if (prefix.isNotEmpty()) { + urlPrefix = prefix + resetPlayer() + } else { + Toast.makeText(this, "get urlPrefix is empty", Toast.LENGTH_SHORT) + .show() + } + } + override fun setListener() { radio_talk.setOnCheckedChangeListener { buttonView, isChecked -> if (isChecked && checkPermissions(permissions)) { @@ -312,36 +334,17 @@ open class VideoTestActivity : VideoBaseActivity(), XP2PCallback, CoroutineScope Log.e("VideoTestActivity", "xp2pEventNotify id:$id msg:$msg event:$event") if (event == 1003) { XP2P.stopService("${productId}/${deviceName}") - XP2P.startService(this, "${productId}/${deviceName}", productId, deviceName) - val ret = XP2P.setParamsForXp2pInfo( - "${productId}/${deviceName}", "", "", xp2pInfo + XP2P.startService( + this, + productId, + deviceName, + xp2pInfo, + xP2PAppConfig ) - if (ret != 0) { - launch(Dispatchers.Main) { - val errInfo: String - if (ret.toString() == "-1007") { - errInfo = getString(R.string.xp2p_err_version) - } else { - errInfo = getString( - R.string.error_with_code, - "${productId}/${deviceName}", - ret.toString() - ) - } - Toast.makeText(this@VideoTestActivity, errInfo, Toast.LENGTH_SHORT).show() - } - } - resetPlayer() } else if (event == 1004 || event == 1005) { if (event == 1004) { Log.e("VideoTestActivity", "====event === 1004") - XP2P.delegateHttpFlv(id)?.let { - XP2P.recordstream(id) - urlPrefix = it - if (!TextUtils.isEmpty(urlPrefix)) { - resetPlayer() - } - } + delegateHttpFlv() } } } diff --git a/sdkdemo/src/main/java/com/tencent/iot/explorer/link/demo/video/preview/VideoWithoutPropertyActivity.kt b/sdkdemo/src/main/java/com/tencent/iot/explorer/link/demo/video/preview/VideoWithoutPropertyActivity.kt index 29ff4fc95..3a1874c25 100644 --- a/sdkdemo/src/main/java/com/tencent/iot/explorer/link/demo/video/preview/VideoWithoutPropertyActivity.kt +++ b/sdkdemo/src/main/java/com/tencent/iot/explorer/link/demo/video/preview/VideoWithoutPropertyActivity.kt @@ -1,643 +1,743 @@ -package com.tencent.iot.explorer.link.demo.video.preview - -import android.Manifest -import android.content.Context -import android.content.Intent -import android.content.pm.ActivityInfo -import android.graphics.ImageFormat -import android.graphics.SurfaceTexture -import android.hardware.Camera -import android.media.AudioFormat -import android.media.MediaRecorder -import android.os.Build -import android.os.Bundle -import android.os.Handler -import android.os.Message -import android.text.TextUtils -import android.util.DisplayMetrics -import android.util.Log -import android.view.* -import android.widget.Toast -import androidx.annotation.RequiresApi -import androidx.constraintlayout.widget.ConstraintLayout -import com.alibaba.fastjson.JSON -import com.alibaba.fastjson.JSONArray -import com.tencent.iot.explorer.link.demo.App -import com.tencent.iot.explorer.link.demo.R -import com.tencent.iot.explorer.link.demo.VideoBaseActivity -import com.tencent.iot.explorer.link.demo.common.log.L -import com.tencent.iot.explorer.link.demo.common.util.CommonUtils -import com.tencent.iot.explorer.link.demo.video.Command -import com.tencent.iot.explorer.link.demo.video.DevInfo -import com.tencent.iot.explorer.link.demo.video.playback.cloudPlayback.event.ActionRecord -import com.tencent.iot.explorer.link.demo.video.playback.cloudPlayback.event.EventPresenter -import com.tencent.iot.explorer.link.demo.video.playback.cloudPlayback.event.EventView -import com.tencent.iot.explorer.link.demo.video.utils.TipToastDialog -import com.tencent.iot.thirdparty.flv.FLVListener -import com.tencent.iot.thirdparty.flv.FLVPacker -import com.tencent.iot.video.link.consts.CameraConstants -import com.tencent.iot.video.link.consts.VideoConst -import com.tencent.iot.video.link.encoder.AudioEncoder -import com.tencent.iot.video.link.encoder.VideoEncoder -import com.tencent.iot.video.link.entity.DeviceStatus -import com.tencent.iot.video.link.listener.OnEncodeListener -import com.tencent.iot.video.link.param.AudioEncodeParam -import com.tencent.iot.video.link.param.MicParam -import com.tencent.iot.video.link.param.VideoEncodeParam -import com.tencent.iot.video.link.util.CameraUtils -import com.tencent.iot.video.link.util.audio.AudioRecordUtil -import com.tencent.xnet.XP2P -import com.tencent.xnet.XP2PCallback -import kotlinx.android.synthetic.main.activity_video_without_property.* -import kotlinx.android.synthetic.main.dash_board_layout.* -import kotlinx.android.synthetic.main.title_layout.* -import kotlinx.coroutines.* -import tv.danmaku.ijk.media.player.IjkMediaPlayer -import java.io.IOException -import java.lang.Runnable -import java.lang.ref.WeakReference -import java.util.* -import java.util.concurrent.ConcurrentHashMap -import java.util.concurrent.CountDownLatch -import java.util.concurrent.Executors - - -private var countDownLatchs : MutableMap = ConcurrentHashMap() -private var keepPlayThreadLock = Object() -@Volatile -private var keepAliveThreadRuning = true - -class VideoWithoutPropertyActivity : VideoBaseActivity(), EventView, TextureView.SurfaceTextureListener, - XP2PCallback, CoroutineScope by MainScope(), SurfaceHolder.Callback, OnEncodeListener { - - open var tag = VideoWithoutPropertyActivity::class.simpleName - var orientationV = false - lateinit var presenter: EventPresenter - lateinit var player : IjkMediaPlayer - lateinit var surface: Surface - - var holder: SurfaceHolder? = null - var camera: Camera? = null - // 默认摄像头方向 - var facing: Int = CameraConstants.facing.BACK - val vw = 320 - val vh = 240 - val frameRate = 15 - val flvListener = FLVListener { data: ByteArray -> -// Log.e(TAG, "===== dataLen:" + data.size); - XP2P.dataSend("${App.data.accessInfo!!.productId}/${presenter.getDeviceName()}", data, data.size) - } - var audioEncoder: AudioEncoder? = null - var videoEncoder: VideoEncoder? = null - var flvPacker: FLVPacker? = null - @Volatile - var startEncodeVideo = false - var executor = Executors.newSingleThreadExecutor() - var handler = Handler() - - @Volatile - var audioAble = true - @Volatile - var urlPrefix = "" - var filePath: String? = null - var permissions = arrayOf(Manifest.permission.RECORD_AUDIO) - @Volatile - var showTip = false - @Volatile - var connectStartTime = 0L - @Volatile - var connectTime = 0L - @Volatile - var startShowVideoTime = 0L - @Volatile - var showVideoTime = 0L - val MSG_UPDATE_HUD = 1 - - var screenWidth = 0 - var screenHeight = 0 - var firstIn = true - - override fun getContentView(): Int { - return R.layout.activity_video_without_property - } - - override fun onResume() { - super.onResume() - keepAliveThreadRuning = true - startPlayer() - holder?.addCallback(this) - } - - override fun initView() { - presenter = EventPresenter(this@VideoWithoutPropertyActivity) - var bundle = intent.getBundleExtra(VideoConst.VIDEO_CONFIG) - bundle?.let { - var videoConfig = bundle.getString(VideoConst.VIDEO_CONFIG) - if (TextUtils.isEmpty(videoConfig)) return@let - - var devInfo = JSON.parseObject(videoConfig, DevUrl2Preview::class.java) - devInfo?.let { - tv_title.setText(it.devName) - presenter.setDeviceName(it.devName) - presenter.setChannel(it.channel) - } - } - - App.data.accessInfo?.let { - presenter.setAccessId(it.accessId) - presenter.setAccessToken(it.accessToken) - presenter.setProductId(it.productId) - presenter.getEventsData(Date()) - } - - XP2P.setCallback(this) - XP2P.startService(this@VideoWithoutPropertyActivity, "${App.data.accessInfo!!.productId}/${presenter.getDeviceName()}", - App.data.accessInfo!!.productId, presenter.getDeviceName() - ) - - val wm = this.getSystemService(WINDOW_SERVICE) as WindowManager - val dm = DisplayMetrics() - wm.defaultDisplay.getMetrics(dm) - val width = dm.widthPixels // 屏幕宽度(像素) - val height = dm.heightPixels // 屏幕高度(像素) - val density = dm.density // 屏幕密度(0.75 / 1.0 / 1.5) - screenWidth = (width / density).toInt() // 屏幕宽度(dp) - screenHeight = (height / density).toInt() // 屏幕高度(dp) - - holder = sv_camera_view.holder - } - - private fun initAudioEncoder() { - val micParam: MicParam = MicParam.Builder() - .setAudioSource(MediaRecorder.AudioSource.VOICE_COMMUNICATION) - .setSampleRateInHz(16000) // 采样率 - .setChannelConfig(AudioFormat.CHANNEL_IN_MONO) - .setAudioFormat(AudioFormat.ENCODING_PCM_16BIT) // PCM - .build() - val audioEncodeParam: AudioEncodeParam = AudioEncodeParam.Builder().build() - audioEncoder = AudioEncoder(micParam, audioEncodeParam, true, true) - audioEncoder!!.setOnEncodeListener(this) - } - - private fun initVideoEncoder() { - val videoEncodeParam: VideoEncodeParam = - VideoEncodeParam.Builder().setSize(vw, vh).setFrameRate(frameRate).setBitRate(vw * vh * 4).build() - videoEncoder = VideoEncoder(videoEncodeParam) - videoEncoder!!.setEncoderListener(this) - } - - @RequiresApi(Build.VERSION_CODES.LOLLIPOP) - private fun startRecord() { -// if (callType == CallingType.TYPE_VIDEO_CALL) { - startEncodeVideo = true +//package com.tencent.iot.explorer.link.demo.video.preview +// +//import android.Manifest +//import android.content.Context +//import android.content.Intent +//import android.content.pm.ActivityInfo +//import android.graphics.ImageFormat +//import android.graphics.SurfaceTexture +//import android.hardware.Camera +//import android.media.AudioFormat +//import android.media.MediaRecorder +//import android.os.Build +//import android.os.Bundle +//import android.os.Handler +//import android.os.Message +//import android.text.TextUtils +//import android.util.DisplayMetrics +//import android.util.Log +//import android.view.* +//import android.widget.Toast +//import androidx.annotation.RequiresApi +//import androidx.constraintlayout.widget.ConstraintLayout +//import com.alibaba.fastjson.JSON +//import com.alibaba.fastjson.JSONArray +//import com.tencent.iot.explorer.link.demo.App +//import com.tencent.iot.explorer.link.demo.BuildConfig +//import com.tencent.iot.explorer.link.demo.R +//import com.tencent.iot.explorer.link.demo.VideoBaseActivity +//import com.tencent.iot.explorer.link.demo.common.log.L +//import com.tencent.iot.explorer.link.demo.common.util.CommonUtils +//import com.tencent.iot.explorer.link.demo.video.Command +//import com.tencent.iot.explorer.link.demo.video.DevInfo +//import com.tencent.iot.explorer.link.demo.video.VideoPreviewBaseActivity +//import com.tencent.iot.explorer.link.demo.video.playback.cloudPlayback.event.ActionRecord +//import com.tencent.iot.explorer.link.demo.video.playback.cloudPlayback.event.EventPresenter +//import com.tencent.iot.explorer.link.demo.video.playback.cloudPlayback.event.EventView +//import com.tencent.iot.explorer.link.demo.video.utils.TipToastDialog +//import com.tencent.iot.thirdparty.flv.FLVListener +//import com.tencent.iot.thirdparty.flv.FLVPacker +//import com.tencent.iot.video.link.consts.CameraConstants +//import com.tencent.iot.video.link.consts.VideoConst +//import com.tencent.iot.video.link.encoder.AudioEncoder +//import com.tencent.iot.video.link.encoder.VideoEncoder +//import com.tencent.iot.video.link.entity.DeviceStatus +//import com.tencent.iot.video.link.listener.OnEncodeListener +//import com.tencent.iot.video.link.param.AudioEncodeParam +//import com.tencent.iot.video.link.param.MicParam +//import com.tencent.iot.video.link.param.VideoEncodeParam +//import com.tencent.iot.video.link.util.CameraUtils +//import com.tencent.iot.video.link.util.audio.AudioRecordUtil +//import com.tencent.xnet.XP2P +//import com.tencent.xnet.XP2PAppConfig +//import com.tencent.xnet.XP2PCallback +//import com.tencent.xnet.annotations.XP2PProtocolType +//import kotlinx.android.synthetic.main.activity_video_without_property.* +//import kotlinx.android.synthetic.main.dash_board_layout.* +//import kotlinx.android.synthetic.main.title_layout.* +//import kotlinx.coroutines.* +//import tv.danmaku.ijk.media.player.IjkMediaPlayer +//import java.io.IOException +//import java.lang.Runnable +//import java.lang.ref.WeakReference +//import java.util.* +//import java.util.concurrent.ConcurrentHashMap +//import java.util.concurrent.CountDownLatch +//import java.util.concurrent.Executors +// +// +//private var countDownLatchs: MutableMap = ConcurrentHashMap() +//private var keepPlayThreadLock = Object() +// +//@Volatile +//private var keepAliveThreadRuning = true +// +//class VideoWithoutPropertyActivity : VideoPreviewBaseActivity(), EventView, +// TextureView.SurfaceTextureListener, +// XP2PCallback, CoroutineScope by MainScope(), SurfaceHolder.Callback, OnEncodeListener { +// +// open var tag = VideoWithoutPropertyActivity::class.simpleName +// var orientationV = false +// lateinit var player: IjkMediaPlayer +// lateinit var surface: Surface +// +// var holder: SurfaceHolder? = null +// var camera: Camera? = null +// +// // 默认摄像头方向 +// var facing: Int = CameraConstants.facing.BACK +// val vw = 320 +// val vh = 240 +// val frameRate = 15 +// val flvListener = FLVListener { data: ByteArray -> +//// Log.e(TAG, "===== dataLen:" + data.size); +// XP2P.dataSend( +// "${App.data.accessInfo!!.productId}/${presenter.getDeviceName()}", +// data, +// data.size +// ) +// } +// var audioEncoder: AudioEncoder? = null +// var videoEncoder: VideoEncoder? = null +// var flvPacker: FLVPacker? = null +// +// @Volatile +// var startEncodeVideo = false +// var executor = Executors.newSingleThreadExecutor() +// var handler = Handler() +// +// @Volatile +// var audioAble = true +// +// @Volatile +// var urlPrefix = "" +// var filePath: String? = null +// var permissions = arrayOf(Manifest.permission.RECORD_AUDIO) +// +// @Volatile +// var showTip = false +// +// @Volatile +// var connectStartTime = 0L +// +// @Volatile +// var connectTime = 0L +// +// @Volatile +// var startShowVideoTime = 0L +// +// @Volatile +// var showVideoTime = 0L +// val MSG_UPDATE_HUD = 1 +// +// var screenWidth = 0 +// var screenHeight = 0 +// var firstIn = true +// +// private val xP2PAppConfig = XP2PAppConfig().also { appConfig -> +// appConfig.appKey = +// BuildConfig.TencentIotLinkSDKDemoAppkey //为explorer平台注册的应用信息(https://console.cloud.tencent.com/iotexplorer/v2/instance/app/detai) explorer控制台- 应用开发 - 选对应的应用下的 appkey/appsecret +// appConfig.appSecret = +// BuildConfig.TencentIotLinkSDKDemoAppSecret //为explorer平台注册的应用信息(https://console.cloud.tencent.com/iotexplorer/v2/instance/app/detai) explorer控制台- 应用开发 - 选对应的应用下的 appkey/appsecret +// appConfig.userId = +// "" //用户纬度(每个手机区分开)使用用户自有的账号系统userid;若无请配置为[TIoTCoreXP2PBridge sharedInstance].getAppUUID; 查找日志是需提供此userid字段 +// appConfig.autoConfigFromDevice = true +// appConfig.type = XP2PProtocolType.XP2P_PROTOCOL_AUTO +// appConfig.crossStunTurn = false +// } +// +// override fun getContentView(): Int { +// return R.layout.activity_video_without_property +// } +// +// override fun onResume() { +// super.onResume() +// keepAliveThreadRuning = true +// startPlayer() +// holder?.addCallback(this) +// } +// +// override fun initView() { +// tv_title.setText(presenter.getDeviceName()) +// +// XP2P.setCallback(this) +// +// val wm = this.getSystemService(WINDOW_SERVICE) as WindowManager +// val dm = DisplayMetrics() +// wm.defaultDisplay.getMetrics(dm) +// val width = dm.widthPixels // 屏幕宽度(像素) +// val height = dm.heightPixels // 屏幕高度(像素) +// val density = dm.density // 屏幕密度(0.75 / 1.0 / 1.5) +// screenWidth = (width / density).toInt() // 屏幕宽度(dp) +// screenHeight = (height / density).toInt() // 屏幕高度(dp) +// +// holder = sv_camera_view.holder +// } +// +// private fun startService() { +// XP2P.startService( +// this, +// presenter.getProductId(), +// presenter.getDeviceName(), +// xp2pInfo, +// xP2PAppConfig +// ) +// } +// +// private fun restartService() { +// val id = "${presenter.getProductId()}/${presenter.getDeviceName()}" +// XP2P.stopService(id) +// getDeviceP2PInfo() +// } +// +//// private fun delegateHttpFlv() { +//// val id = "${presenter.getProductId()}/${presenter.getDeviceName()}" +////// XP2P.recordstreamPath("/storage/emulated/0/data_video.flv") //自定义采集裸流路径 +////// XP2P.recordstream(id) //开启自定义采集裸流 +//// val prefix = XP2P.delegateHttpFlv(id) +//// if (prefix.isNotEmpty()) { +//// resetPlayer(prefix) +//// } else { +//// Toast.makeText(this@VideoPreviewActivity, "get urlPrefix is empty", Toast.LENGTH_SHORT) +//// .show() +//// } +//// } +// +// private fun initAudioEncoder() { +// val micParam: MicParam = MicParam.Builder() +// .setAudioSource(MediaRecorder.AudioSource.VOICE_COMMUNICATION) +// .setSampleRateInHz(16000) // 采样率 +// .setChannelConfig(AudioFormat.CHANNEL_IN_MONO) +// .setAudioFormat(AudioFormat.ENCODING_PCM_16BIT) // PCM +// .build() +// val audioEncodeParam: AudioEncodeParam = AudioEncodeParam.Builder().build() +// audioEncoder = AudioEncoder(micParam, audioEncodeParam, true, true) +// audioEncoder!!.setOnEncodeListener(this) +// } +// +// private fun initVideoEncoder() { +// val videoEncodeParam: VideoEncodeParam = +// VideoEncodeParam.Builder().setSize(vw, vh).setFrameRate(frameRate) +// .setBitRate(vw * vh * 4).build() +// videoEncoder = VideoEncoder(videoEncodeParam) +// videoEncoder!!.setEncoderListener(this) +// } +// +// @RequiresApi(Build.VERSION_CODES.LOLLIPOP) +// private fun startRecord() { +//// if (callType == CallingType.TYPE_VIDEO_CALL) { +// startEncodeVideo = true +//// } +// audioEncoder!!.start() +// } +// +// private fun stopRecord() { +// if (audioEncoder != null) { +// audioEncoder!!.stop() // } - audioEncoder!!.start() - } - - private fun stopRecord() { - if (audioEncoder != null) { - audioEncoder!!.stop() - } - if (videoEncoder != null) { - videoEncoder!!.stop() - } - startEncodeVideo = false - } - - open fun startPlayer() { - if (App.data.accessInfo == null || TextUtils.isEmpty(presenter.getDeviceName())) return - player = IjkMediaPlayer() - mHandler.sendEmptyMessageDelayed(MSG_UPDATE_HUD, 500) - - Thread(Runnable { - val id = "${App.data.accessInfo!!.productId}/${presenter.getDeviceName()}" - connectStartTime = System.currentTimeMillis() - val ret = XP2P.setParamsForXp2pInfo(id, App.data.accessInfo!!.accessId, - App.data.accessInfo!!.accessToken, "") - if (ret != 0) { - launch(Dispatchers.Main) { - val errInfo: String - if (ret.toString() == "-1007") { - errInfo = getString(R.string.xp2p_err_version) - } else { - errInfo = getString(R.string.error_with_code, id, ret.toString()) - } - Toast.makeText(this@VideoWithoutPropertyActivity, errInfo, Toast.LENGTH_SHORT).show() - } - return@Runnable - } - XP2P.delegateHttpFlv(id)?.let { - urlPrefix = it - if (!TextUtils.isEmpty(urlPrefix)) { - player.let { - setPlayerUrl(Command.getVideoStandardQualityUrlSuffix(presenter.getChannel())) - keepPlayerplay(id) - } - } - } - }).start() - } - - private fun keepPlayerplay(id: String?) { - if (TextUtils.isEmpty(id)) return - val accessId = App.data.accessInfo!!.accessId - val accessToken = App.data.accessInfo!!.accessToken - // 开启守护线程 - Thread{ - val objectLock = Object() - while (true) { - Log.d(tag, "id=${id} keepAliveThread wait disconnected msg") - synchronized(keepPlayThreadLock) { - keepPlayThreadLock.wait() - } - Log.d(tag, "id=${id} keepAliveThread do not wait and keepAliveThreadRuning=${keepAliveThreadRuning}") - if (!keepAliveThreadRuning) break //锁被释放后,检查守护线程是否继续运行 - flvPacker = null - stopRecord() - // 发现断开尝试恢复视频,每隔一秒尝试一次 - Log.d(tag, "====开始尝试重连...") - XP2P.stopService(id) - while (XP2P.startService(this@VideoWithoutPropertyActivity, id, App.data.accessInfo!!.productId, presenter.getDeviceName())!=0 - || XP2P.setParamsForXp2pInfo(id, accessId, accessToken, "") != 0 - || getDeviceStatus(id) != 0) { - XP2P.stopService(id) - synchronized(objectLock) { - objectLock.wait(500) - } - Log.d(tag, "====正在重连...") - } - connectStartTime = System.currentTimeMillis() - - Log.d(tag, "====尝试拉流...") - XP2P.delegateHttpFlv(id)?.let { - urlPrefix = it - if (!TextUtils.isEmpty(urlPrefix)) setPlayerUrl(Command.getVideoStandardQualityUrlSuffix(presenter.getChannel())) - } - } - }.start() - } - - override fun onSurfaceTextureAvailable(surface: SurfaceTexture?, width: Int, height: Int) { - surface?.let { - this.surface = Surface(surface) - player.setSurface(this.surface) - } - } - - override fun setListener() { - iv_back.setOnClickListener { finish() } - v_preview.surfaceTextureListener = this - } - - open fun setPlayerUrl(suffix: String) { - showTip = false - startShowVideoTime = System.currentTimeMillis() - player.release() - launch (Dispatchers.Main) { - layout_video_preview?.removeView(v_preview) - layout_video_preview?.addView(v_preview, 0) - - player = IjkMediaPlayer() - player?.let { - val url = urlPrefix + suffix - it.reset() - - it.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "analyzemaxduration", 100) - it.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "probesize", 50 * 1024) - it.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "packet-buffering", 0) - it.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "start-on-prepared", 1) - it.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, "threads", 1) - it.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "sync-av-start", 0) - it.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec",1) - it.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-auto-rotate", 1) - it.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-handle-resolution-change", 1) - - while (!::surface.isInitialized) { - delay(50) - L.e("delay for waiting surface.") - } - it.setSurface(surface) - it.dataSource = url - - it.prepareAsync() - it.start() - - var command = Command.getNvrIpcStatus(presenter.getChannel(), 0) - var repStatus = XP2P.postCommandRequestSync("${App.data.accessInfo?.productId}/${presenter.getDeviceName()}", - command.toByteArray(), command.toByteArray().size.toLong(), 2 * 1000 * 1000) ?:"" - - launch(Dispatchers.Main) { - var retContent = StringBuilder(repStatus).toString() - if (TextUtils.isEmpty(retContent)) { - retContent = getString(R.string.command_with_error, command) - } - Toast.makeText(this@VideoWithoutPropertyActivity, retContent, Toast.LENGTH_SHORT).show() - } - - // 开始推流 - XP2P.runSendService("${App.data.accessInfo?.productId}/${presenter.getDeviceName()}", "channel=0", false) - handler.post(Runnable { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - initAudioEncoder() - initVideoEncoder() - startRecord() - } - }) - } - } - } - - override fun eventReady(events: MutableList) { - - } - - override fun onSurfaceTextureSizeChanged(surface: SurfaceTexture?, width: Int, height: Int) { - Log.e(tag, "width=${width}, height=${height}, player.videoWidth=${player.videoWidth}, player.videoHeight=${player.videoHeight}") - val layoutParams = v_preview.layoutParams - if (orientationV) { - layoutParams.width = (player.videoWidth * (screenWidth * 16 / 9)) / player.videoHeight - layoutParams.height = layoutParams.height - } else { - layoutParams.width = (player.videoWidth * height) / player.videoHeight - } - v_preview.layoutParams = layoutParams - - } - override fun onSurfaceTextureDestroyed(surface: SurfaceTexture?): Boolean { return false } - override fun onSurfaceTextureUpdated(surface: SurfaceTexture?) { - if (!showTip && startShowVideoTime > 0) { - showVideoTime = System.currentTimeMillis() - startShowVideoTime - var content = getString(R.string.time_2_show, connectTime.toString(), showVideoTime.toString()) - TipToastDialog(this, content, 10000).show() - showTip = true - } - if (orientationV && firstIn) { - val layoutParams = v_preview.layoutParams - layoutParams.width = (player.videoWidth * (screenWidth * 16 / 9)) / player.videoHeight - layoutParams.height = layoutParams.height - v_preview.layoutParams = layoutParams - firstIn = false - } - } - override fun fail(msg: String?, errorCode: Int) {} - override fun commandRequest(id: String?, msg: String?) {} - override fun avDataRecvHandle(id: String?, data: ByteArray?, len: Int) {} - override fun avDataCloseHandle(id: String?, msg: String?, errorCode: Int) {} - override fun onDeviceMsgArrived(id: String?, data: ByteArray?, len: Int): String { - return "app reply to device" - } - - override fun xp2pEventNotify(id: String?, msg: String?, event: Int) { - Log.e(tag, "id=${id}, event=${event}") - if (event == 1003) { - Log.e(tag, "====event === 1003") - startShowVideoTime = 0L - keepPlayThreadLock?.let { - synchronized(it) { - Log.d(tag, "====p2p链路断开, event=$event.") - it.notify() - } - } // 唤醒守护线程 - launch(Dispatchers.Main) { - var content = getString(R.string.disconnected_and_reconnecting, id) - Toast.makeText(this@VideoWithoutPropertyActivity, content, Toast.LENGTH_SHORT).show() - } - } else if (event == 1004 || event == 1005) { - connectTime = System.currentTimeMillis() - connectStartTime - if (event == 1004) { - Log.e(tag, "====event === 1004") - } - } else if (event == 1010) { - Log.e(tag, "====event === 1010, 校验失败,info撞库防止串流: $msg") - } - } - - override fun onPause() { - super.onPause() - finishPlayer() - } - - private fun finishPlayer() { - mHandler.removeMessages(MSG_UPDATE_HUD) - player?.release() - - countDownLatchs.clear() - // 关闭守护线程 - keepAliveThreadRuning = false - keepPlayThreadLock?.let { - synchronized(it) { - it.notify() - } - } - } - - override fun onDestroy() { - super.onDestroy() - stopRecord() - executor.shutdown() - finishPlayer() - App.data.accessInfo?.let { - XP2P.stopService("${it.productId}/${presenter.getDeviceName()}") - } - XP2P.setCallback(null) - cancel() - } - - companion object { - fun startPreviewActivity(context: Context?, dev: DevInfo) { - context?:let { return } - - var intent = Intent(context, VideoWithoutPropertyActivity::class.java) - var bundle = Bundle() - intent.putExtra(VideoConst.VIDEO_CONFIG, bundle) - var devInfo = DevUrl2Preview() - devInfo.devName = dev.DeviceName - devInfo.Status = dev.Status - devInfo.channel = dev.Channel - bundle.putString(VideoConst.VIDEO_CONFIG, JSON.toJSONString(devInfo)) - context.startActivity(intent) - } - } - - private fun getDeviceStatus(id: String?): Int { - var command: ByteArray? = null - command = "action=inner_define&channel=0&cmd=get_device_st&type=live&quality=high".toByteArray() - val reponse = XP2P.postCommandRequestSync(id, command, command!!.size.toLong(), 2 * 1000 * 1000) - if (!TextUtils.isEmpty(reponse)) { - val deviceStatuses: List = JSONArray.parseArray(reponse, DeviceStatus::class.java) - // 0 接收请求 - // 1 拒绝请求 - // 404 error request message - // 405 connect number too many - // 406 current command don't support - // 407 device process error - if (deviceStatuses.isNotEmpty()) { - runOnUiThread { - when (deviceStatuses[0].status) { - 0 -> Toast.makeText(this, "设备状态正常", Toast.LENGTH_SHORT).show() - 1 -> Toast.makeText(this, "设备状态异常, 拒绝请求: $reponse", Toast.LENGTH_SHORT).show() - 404 -> Toast.makeText( - this, - "设备状态异常, error request message: $reponse", - Toast.LENGTH_SHORT - ).show() - 405 -> Toast.makeText( - this, - "设备状态异常, connect number too many: $reponse", - Toast.LENGTH_SHORT - ).show() - 406 -> Toast.makeText( - this, - "设备状态异常, current command don't support: $reponse", - Toast.LENGTH_SHORT - ).show() - 407 -> Toast.makeText( - this, - "设备状态异常, device process error: $reponse", - Toast.LENGTH_SHORT - ).show() - } - } - return deviceStatuses[0].status - } else { - runOnUiThread { - Toast.makeText(this, "获取设备状态失败", Toast.LENGTH_SHORT).show() - } - return -1 - } - } - return -1 - } - - private val mHandler = MyHandler(this) - - private class MyHandler(activity: VideoWithoutPropertyActivity) : Handler() { - private val mActivity: WeakReference = WeakReference(activity) - override fun handleMessage(msg: Message) { - if (mActivity.get() == null) { - return - } - val activity = mActivity.get() - when (msg.what) { - activity?.MSG_UPDATE_HUD -> { - activity.updateDashboard() - removeMessages(activity.MSG_UPDATE_HUD) - sendEmptyMessageDelayed(activity.MSG_UPDATE_HUD, 500) - } - } - } - } - - private fun updateDashboard() { - val videoCachedDuration = player.videoCachedDuration - val audioCachedDuration = player.audioCachedDuration - val videoCachedBytes = player.videoCachedBytes - val audioCachedBytes = player.audioCachedBytes - val tcpSpeed = player.tcpSpeed - - tv_a_cache?.text = String.format(Locale.US, "%s, %s", - CommonUtils.formatedDurationMilli(audioCachedDuration), - CommonUtils.formatedSize(audioCachedBytes)) - tv_v_cache?.text = String.format(Locale.US, "%s, %s", - CommonUtils.formatedDurationMilli(videoCachedDuration), - CommonUtils.formatedSize(videoCachedBytes)) - tv_tcp_speed?.text = String.format(Locale.US, "%s", - CommonUtils.formatedSpeed(tcpSpeed, 1000)) - tv_video_w_h?.text = "${player.videoWidth} x ${player.videoHeight}" - } - - /** - * 打开相机 - */ - private fun openCamera() { - releaseCamera(camera) - camera = Camera.open(facing) - //获取相机参数 - val parameters = camera?.getParameters() - - //设置预览格式(也就是每一帧的视频格式)YUV420下的NV21 - parameters?.previewFormat = ImageFormat.NV21 - if (this.facing == Camera.CameraInfo.CAMERA_FACING_BACK) { - val focusModes = parameters?.supportedFocusModes - if (focusModes != null && focusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO)) { - parameters.focusMode = Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO - } - } - var cameraIndex = -1 - if (facing == CameraConstants.facing.BACK) { - cameraIndex = Camera.CameraInfo.CAMERA_FACING_BACK - } else if (facing == CameraConstants.facing.FRONT) { - cameraIndex = Camera.CameraInfo.CAMERA_FACING_FRONT - camera?.setDisplayOrientation(180) - } - try { - camera?.setDisplayOrientation(CameraUtils.getDisplayOrientation(this, cameraIndex)) - } catch (e: Exception) { - e.printStackTrace() - } - - //设置预览图像分辨率 - parameters?.setPreviewSize(vw, vh) - //设置帧率 - parameters?.previewFrameRate = frameRate - //配置camera参数 - camera?.setParameters(parameters) - try { - camera?.setPreviewDisplay(holder) - } catch (e: IOException) { - e.printStackTrace() - } - //设置监听获取视频流的每一帧 - camera?.setPreviewCallback(Camera.PreviewCallback { data, camera -> - if (startEncodeVideo && videoEncoder != null) { - videoEncoder?.encoderH264(data, facing == CameraConstants.facing.FRONT) - } - }) - //调用startPreview()用以更新preview的surface - camera?.startPreview() - } - - /** - * 关闭相机 - */ - fun releaseCamera(camera: Camera?) { - var camera = camera - if (camera != null) { - camera.setPreviewCallback(null) - camera.stopPreview() - camera.release() - camera = null - } - } - - override fun surfaceCreated(p0: SurfaceHolder?) { - Log.d(tag, "surfaceCreated") - openCamera() - } - - override fun surfaceChanged(p0: SurfaceHolder?, p1: Int, p2: Int, p3: Int) { - Log.d(tag, "surfaceChanged") - } - - override fun surfaceDestroyed(p0: SurfaceHolder?) { - Log.d(tag, "surfaceDestroyed") - p0?.removeCallback(this) - } - - override fun onAudioEncoded(datas: ByteArray?, pts: Long, seqgigit: Long) { - if (executor.isShutdown) return - executor.submit { - if (flvPacker == null) - flvPacker = FLVPacker(flvListener, true, true) - flvPacker?.encodeFlv(datas, FLVPacker.TYPE_AUDIO, pts) - } - } - - override fun onVideoEncoded(datas: ByteArray?, pts: Long, seq: Long) { - if (executor.isShutdown) return - executor.submit { - if (flvPacker == null) flvPacker = - FLVPacker(flvListener, true, true) - flvPacker!!.encodeFlv(datas, FLVPacker.TYPE_VIDEO, pts) - } - } -} \ No newline at end of file +// if (videoEncoder != null) { +// videoEncoder!!.stop() +// } +// startEncodeVideo = false +// } +// +// open fun startPlayer() { +// if (App.data.accessInfo == null || TextUtils.isEmpty(presenter.getDeviceName())) return +// player = IjkMediaPlayer() +// mHandler.sendEmptyMessageDelayed(MSG_UPDATE_HUD, 500) +// +// Thread(Runnable { +// val id = "${App.data.accessInfo!!.productId}/${presenter.getDeviceName()}" +// connectStartTime = System.currentTimeMillis() +//// val ret = XP2P.setParamsForXp2pInfo( +//// id, App.data.accessInfo!!.accessId, +//// App.data.accessInfo!!.accessToken, "" +//// ) +// if (ret != 0) { +// launch(Dispatchers.Main) { +// val errInfo: String +// if (ret.toString() == "-1007") { +// errInfo = getString(R.string.xp2p_err_version) +// } else { +// errInfo = getString(R.string.error_with_code, id, ret.toString()) +// } +// Toast.makeText(this@VideoWithoutPropertyActivity, errInfo, Toast.LENGTH_SHORT) +// .show() +// } +// return@Runnable +// } +// XP2P.delegateHttpFlv(id)?.let { +// urlPrefix = it +// if (!TextUtils.isEmpty(urlPrefix)) { +// player.let { +// setPlayerUrl(Command.getVideoStandardQualityUrlSuffix(presenter.getChannel())) +// keepPlayerplay(id) +// } +// } +// } +// }).start() +// } +// +// private fun keepPlayerplay(id: String?) { +// if (TextUtils.isEmpty(id)) return +// val accessId = App.data.accessInfo!!.accessId +// val accessToken = App.data.accessInfo!!.accessToken +// // 开启守护线程 +// Thread { +// val objectLock = Object() +// while (true) { +// Log.d(tag, "id=${id} keepAliveThread wait disconnected msg") +// synchronized(keepPlayThreadLock) { +// keepPlayThreadLock.wait() +// } +// Log.d( +// tag, +// "id=${id} keepAliveThread do not wait and keepAliveThreadRuning=${keepAliveThreadRuning}" +// ) +// if (!keepAliveThreadRuning) break //锁被释放后,检查守护线程是否继续运行 +// flvPacker = null +// stopRecord() +// // 发现断开尝试恢复视频,每隔一秒尝试一次 +// Log.d(tag, "====开始尝试重连...") +// XP2P.stopService(id) +// XP2P.startService( +// this@VideoWithoutPropertyActivity, +// id, +// App.data.accessInfo!!.productId, +// presenter.getDeviceName(), +// xP2PAppConfig +// ) +// while (XP2P.setParamsForXp2pInfo(id, accessId, accessToken, "") != 0 +// || getDeviceStatus(id) != 0 +// ) { +// XP2P.stopService(id) +// synchronized(objectLock) { +// objectLock.wait(500) +// } +// Log.d(tag, "====正在重连...") +// } +// connectStartTime = System.currentTimeMillis() +// +// Log.d(tag, "====尝试拉流...") +// XP2P.delegateHttpFlv(id)?.let { +// urlPrefix = it +// if (!TextUtils.isEmpty(urlPrefix)) setPlayerUrl( +// Command.getVideoStandardQualityUrlSuffix( +// presenter.getChannel() +// ) +// ) +// } +// } +// }.start() +// } +// +// override fun onSurfaceTextureAvailable(surface: SurfaceTexture?, width: Int, height: Int) { +// surface?.let { +// this.surface = Surface(surface) +// player.setSurface(this.surface) +// } +// } +// +// override fun setListener() { +// iv_back.setOnClickListener { finish() } +// v_preview.surfaceTextureListener = this +// } +// +// open fun setPlayerUrl(suffix: String) { +// showTip = false +// startShowVideoTime = System.currentTimeMillis() +// player.release() +// launch(Dispatchers.Main) { +// layout_video_preview?.removeView(v_preview) +// layout_video_preview?.addView(v_preview, 0) +// +// player = IjkMediaPlayer() +// player?.let { +// val url = urlPrefix + suffix +// it.reset() +// +// it.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "analyzemaxduration", 100) +// it.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "probesize", 50 * 1024) +// it.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "packet-buffering", 0) +// it.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "start-on-prepared", 1) +// it.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, "threads", 1) +// it.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "sync-av-start", 0) +// it.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 1) +// it.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-auto-rotate", 1) +// it.setOption( +// IjkMediaPlayer.OPT_CATEGORY_PLAYER, +// "mediacodec-handle-resolution-change", +// 1 +// ) +// +// while (!::surface.isInitialized) { +// delay(50) +// L.e("delay for waiting surface.") +// } +// it.setSurface(surface) +// it.dataSource = url +// +// it.prepareAsync() +// it.start() +// +// var command = Command.getNvrIpcStatus(presenter.getChannel(), 0) +// var repStatus = XP2P.postCommandRequestSync( +// "${App.data.accessInfo?.productId}/${presenter.getDeviceName()}", +// command.toByteArray(), command.toByteArray().size.toLong(), 2 * 1000 * 1000 +// ) ?: "" +// +// launch(Dispatchers.Main) { +// var retContent = StringBuilder(repStatus).toString() +// if (TextUtils.isEmpty(retContent)) { +// retContent = getString(R.string.command_with_error, command) +// } +// Toast.makeText( +// this@VideoWithoutPropertyActivity, +// retContent, +// Toast.LENGTH_SHORT +// ).show() +// } +// +// // 开始推流 +// XP2P.runSendService( +// "${App.data.accessInfo?.productId}/${presenter.getDeviceName()}", +// "channel=0", +// false +// ) +// handler.post(Runnable { +// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { +// initAudioEncoder() +// initVideoEncoder() +// startRecord() +// } +// }) +// } +// } +// } +// +// override fun eventReady(events: MutableList) { +// +// } +// +// override fun onSurfaceTextureSizeChanged(surface: SurfaceTexture?, width: Int, height: Int) { +// Log.e( +// tag, +// "width=${width}, height=${height}, player.videoWidth=${player.videoWidth}, player.videoHeight=${player.videoHeight}" +// ) +// val layoutParams = v_preview.layoutParams +// if (orientationV) { +// layoutParams.width = (player.videoWidth * (screenWidth * 16 / 9)) / player.videoHeight +// layoutParams.height = layoutParams.height +// } else { +// layoutParams.width = (player.videoWidth * height) / player.videoHeight +// } +// v_preview.layoutParams = layoutParams +// +// } +// +// override fun onSurfaceTextureDestroyed(surface: SurfaceTexture?): Boolean { +// return false +// } +// +// override fun onSurfaceTextureUpdated(surface: SurfaceTexture?) { +// if (!showTip && startShowVideoTime > 0) { +// showVideoTime = System.currentTimeMillis() - startShowVideoTime +// var content = +// getString(R.string.time_2_show, connectTime.toString(), showVideoTime.toString()) +// TipToastDialog(this, content, 10000).show() +// showTip = true +// } +// if (orientationV && firstIn) { +// val layoutParams = v_preview.layoutParams +// layoutParams.width = (player.videoWidth * (screenWidth * 16 / 9)) / player.videoHeight +// layoutParams.height = layoutParams.height +// v_preview.layoutParams = layoutParams +// firstIn = false +// } +// } +// +// override fun fail(msg: String?, errorCode: Int) {} +// override fun updateXp2pInfo(xp2pInfo: String) { +// startService() +// } +// +// override fun commandRequest(id: String?, msg: String?) {} +// override fun avDataRecvHandle(id: String?, data: ByteArray?, len: Int) {} +// override fun avDataCloseHandle(id: String?, msg: String?, errorCode: Int) {} +// override fun onDeviceMsgArrived(id: String?, data: ByteArray?, len: Int): String { +// return "app reply to device" +// } +// +// override fun xp2pEventNotify(id: String?, msg: String?, event: Int) { +// Log.e(tag, "id=${id}, event=${event}") +// if (event == 1003) { +// Log.e(tag, "====event === 1003") +// startShowVideoTime = 0L +// keepPlayThreadLock?.let { +// synchronized(it) { +// Log.d(tag, "====p2p链路断开, event=$event.") +// it.notify() +// } +// } // 唤醒守护线程 +// launch(Dispatchers.Main) { +// var content = getString(R.string.disconnected_and_reconnecting, id) +// Toast.makeText(this@VideoWithoutPropertyActivity, content, Toast.LENGTH_SHORT) +// .show() +// } +// } else if (event == 1004 || event == 1005) { +// connectTime = System.currentTimeMillis() - connectStartTime +// if (event == 1004) { +// Log.e(tag, "====event === 1004") +// } +// } else if (event == 1010) { +// Log.e(tag, "====event === 1010, 校验失败,info撞库防止串流: $msg") +// } +// } +// +// override fun onPause() { +// super.onPause() +// finishPlayer() +// } +// +// private fun finishPlayer() { +// mHandler.removeMessages(MSG_UPDATE_HUD) +// player?.release() +// +// countDownLatchs.clear() +// // 关闭守护线程 +// keepAliveThreadRuning = false +// keepPlayThreadLock?.let { +// synchronized(it) { +// it.notify() +// } +// } +// } +// +// override fun onDestroy() { +// super.onDestroy() +// stopRecord() +// executor.shutdown() +// finishPlayer() +// App.data.accessInfo?.let { +// XP2P.stopService("${it.productId}/${presenter.getDeviceName()}") +// } +// XP2P.setCallback(null) +// cancel() +// } +// +// companion object { +// fun startPreviewActivity(context: Context?, dev: DevInfo) { +// context ?: let { return } +// +// var intent = Intent(context, VideoWithoutPropertyActivity::class.java) +// var bundle = Bundle() +// intent.putExtra(VideoConst.VIDEO_CONFIG, bundle) +// var devInfo = DevUrl2Preview() +// devInfo.devName = dev.DeviceName +// devInfo.Status = dev.Status +// devInfo.channel = dev.Channel +// bundle.putString(VideoConst.VIDEO_CONFIG, JSON.toJSONString(devInfo)) +// context.startActivity(intent) +// } +// } +// +// private fun getDeviceStatus(id: String?): Int { +// var command: ByteArray? = null +// command = +// "action=inner_define&channel=0&cmd=get_device_st&type=live&quality=high".toByteArray() +// val reponse = +// XP2P.postCommandRequestSync(id, command, command!!.size.toLong(), 2 * 1000 * 1000) +// if (!TextUtils.isEmpty(reponse)) { +// val deviceStatuses: List = +// JSONArray.parseArray(reponse, DeviceStatus::class.java) +// // 0 接收请求 +// // 1 拒绝请求 +// // 404 error request message +// // 405 connect number too many +// // 406 current command don't support +// // 407 device process error +// if (deviceStatuses.isNotEmpty()) { +// runOnUiThread { +// when (deviceStatuses[0].status) { +// 0 -> Toast.makeText(this, "设备状态正常", Toast.LENGTH_SHORT).show() +// 1 -> Toast.makeText( +// this, +// "设备状态异常, 拒绝请求: $reponse", +// Toast.LENGTH_SHORT +// ).show() +// +// 404 -> Toast.makeText( +// this, +// "设备状态异常, error request message: $reponse", +// Toast.LENGTH_SHORT +// ).show() +// +// 405 -> Toast.makeText( +// this, +// "设备状态异常, connect number too many: $reponse", +// Toast.LENGTH_SHORT +// ).show() +// +// 406 -> Toast.makeText( +// this, +// "设备状态异常, current command don't support: $reponse", +// Toast.LENGTH_SHORT +// ).show() +// +// 407 -> Toast.makeText( +// this, +// "设备状态异常, device process error: $reponse", +// Toast.LENGTH_SHORT +// ).show() +// } +// } +// return deviceStatuses[0].status +// } else { +// runOnUiThread { +// Toast.makeText(this, "获取设备状态失败", Toast.LENGTH_SHORT).show() +// } +// return -1 +// } +// } +// return -1 +// } +// +// private val mHandler = MyHandler(this) +// +// private class MyHandler(activity: VideoWithoutPropertyActivity) : Handler() { +// private val mActivity: WeakReference = WeakReference(activity) +// override fun handleMessage(msg: Message) { +// if (mActivity.get() == null) { +// return +// } +// val activity = mActivity.get() +// when (msg.what) { +// activity?.MSG_UPDATE_HUD -> { +// activity.updateDashboard() +// removeMessages(activity.MSG_UPDATE_HUD) +// sendEmptyMessageDelayed(activity.MSG_UPDATE_HUD, 500) +// } +// } +// } +// } +// +// private fun updateDashboard() { +// val videoCachedDuration = player.videoCachedDuration +// val audioCachedDuration = player.audioCachedDuration +// val videoCachedBytes = player.videoCachedBytes +// val audioCachedBytes = player.audioCachedBytes +// val tcpSpeed = player.tcpSpeed +// +// tv_a_cache?.text = String.format( +// Locale.US, "%s, %s", +// CommonUtils.formatedDurationMilli(audioCachedDuration), +// CommonUtils.formatedSize(audioCachedBytes) +// ) +// tv_v_cache?.text = String.format( +// Locale.US, "%s, %s", +// CommonUtils.formatedDurationMilli(videoCachedDuration), +// CommonUtils.formatedSize(videoCachedBytes) +// ) +// tv_tcp_speed?.text = String.format( +// Locale.US, "%s", +// CommonUtils.formatedSpeed(tcpSpeed, 1000) +// ) +// tv_video_w_h?.text = "${player.videoWidth} x ${player.videoHeight}" +// } +// +// /** +// * 打开相机 +// */ +// private fun openCamera() { +// releaseCamera(camera) +// camera = Camera.open(facing) +// //获取相机参数 +// val parameters = camera?.getParameters() +// +// //设置预览格式(也就是每一帧的视频格式)YUV420下的NV21 +// parameters?.previewFormat = ImageFormat.NV21 +// if (this.facing == Camera.CameraInfo.CAMERA_FACING_BACK) { +// val focusModes = parameters?.supportedFocusModes +// if (focusModes != null && focusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO)) { +// parameters.focusMode = Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO +// } +// } +// var cameraIndex = -1 +// if (facing == CameraConstants.facing.BACK) { +// cameraIndex = Camera.CameraInfo.CAMERA_FACING_BACK +// } else if (facing == CameraConstants.facing.FRONT) { +// cameraIndex = Camera.CameraInfo.CAMERA_FACING_FRONT +// camera?.setDisplayOrientation(180) +// } +// try { +// camera?.setDisplayOrientation(CameraUtils.getDisplayOrientation(this, cameraIndex)) +// } catch (e: Exception) { +// e.printStackTrace() +// } +// +// //设置预览图像分辨率 +// parameters?.setPreviewSize(vw, vh) +// //设置帧率 +// parameters?.previewFrameRate = frameRate +// //配置camera参数 +// camera?.setParameters(parameters) +// try { +// camera?.setPreviewDisplay(holder) +// } catch (e: IOException) { +// e.printStackTrace() +// } +// //设置监听获取视频流的每一帧 +// camera?.setPreviewCallback(Camera.PreviewCallback { data, camera -> +// if (startEncodeVideo && videoEncoder != null) { +// videoEncoder?.encoderH264(data, facing == CameraConstants.facing.FRONT) +// } +// }) +// //调用startPreview()用以更新preview的surface +// camera?.startPreview() +// } +// +// /** +// * 关闭相机 +// */ +// fun releaseCamera(camera: Camera?) { +// var camera = camera +// if (camera != null) { +// camera.setPreviewCallback(null) +// camera.stopPreview() +// camera.release() +// camera = null +// } +// } +// +// override fun surfaceCreated(p0: SurfaceHolder?) { +// Log.d(tag, "surfaceCreated") +// openCamera() +// } +// +// override fun surfaceChanged(p0: SurfaceHolder?, p1: Int, p2: Int, p3: Int) { +// Log.d(tag, "surfaceChanged") +// } +// +// override fun surfaceDestroyed(p0: SurfaceHolder?) { +// Log.d(tag, "surfaceDestroyed") +// p0?.removeCallback(this) +// } +// +// override fun onAudioEncoded(datas: ByteArray?, pts: Long, seqgigit: Long) { +// if (executor.isShutdown) return +// executor.submit { +// if (flvPacker == null) +// flvPacker = FLVPacker(flvListener, true, true) +// flvPacker?.encodeFlv(datas, FLVPacker.TYPE_AUDIO, pts) +// } +// } +// +// override fun onVideoEncoded(datas: ByteArray?, pts: Long, seq: Long) { +// if (executor.isShutdown) return +// executor.submit { +// if (flvPacker == null) flvPacker = +// FLVPacker(flvListener, true, true) +// flvPacker!!.encodeFlv(datas, FLVPacker.TYPE_VIDEO, pts) +// } +// } +//} \ No newline at end of file diff --git a/sdkdemo/src/main/java/com/tencent/iot/explorer/link/demo/video/preview/WlanVideoPreviewActivity.kt b/sdkdemo/src/main/java/com/tencent/iot/explorer/link/demo/video/preview/WlanVideoPreviewActivity.kt index 26efe401d..77c18449c 100644 --- a/sdkdemo/src/main/java/com/tencent/iot/explorer/link/demo/video/preview/WlanVideoPreviewActivity.kt +++ b/sdkdemo/src/main/java/com/tencent/iot/explorer/link/demo/video/preview/WlanVideoPreviewActivity.kt @@ -1,430 +1,434 @@ -package com.tencent.iot.explorer.link.demo.video.preview - -import android.Manifest -import android.app.Service -import android.content.Context -import android.content.Intent -import android.graphics.SurfaceTexture -import android.media.AudioManager -import android.os.Bundle -import android.os.Handler -import android.os.Message -import android.text.TextUtils -import android.util.Log -import android.view.Surface -import android.view.TextureView -import android.view.View -import android.widget.Toast -import com.alibaba.fastjson.JSON -import com.tencent.iot.explorer.link.demo.App -import com.tencent.iot.explorer.link.demo.R -import com.tencent.iot.explorer.link.demo.VideoBaseActivity -import com.tencent.iot.explorer.link.demo.common.util.CommonUtils -import com.tencent.iot.explorer.link.demo.video.Command -import com.tencent.iot.explorer.link.demo.video.DevInfo -import com.tencent.iot.explorer.link.demo.video.utils.ListOptionsDialog -import com.tencent.iot.explorer.link.demo.video.utils.TipToastDialog -import com.tencent.iot.video.link.consts.VideoConst -import com.tencent.iot.video.link.util.audio.AudioRecordUtil -import com.tencent.xnet.XP2P -import com.tencent.xnet.XP2PCallback -import kotlinx.android.synthetic.main.activity_video_preview.* -import kotlinx.android.synthetic.main.dash_board_layout.* -import kotlinx.android.synthetic.main.fragment_video_cloud_playback.* -import kotlinx.android.synthetic.main.title_layout.* -import kotlinx.coroutines.* -import tv.danmaku.ijk.media.player.IjkMediaPlayer -import java.lang.Runnable -import java.nio.charset.StandardCharsets -import java.util.* - - -class WlanVideoPreviewActivity : VideoBaseActivity(), TextureView.SurfaceTextureListener, - XP2PCallback, CoroutineScope by MainScope() { - - var tag = WlanVideoPreviewActivity::class.simpleName - lateinit var player: IjkMediaPlayer - lateinit var surface: Surface - - @Volatile - var audioAble = true - lateinit var audioRecordUtil: AudioRecordUtil - var permissions = arrayOf(Manifest.permission.RECORD_AUDIO) - - @Volatile - var showTip = false - - @Volatile - var connectTime = 0L - - @Volatile - var startShowVideoTime = 0L - - @Volatile - var showVideoTime = 0L - - val MSG_UPDATE_HUD = 1 - var productId = "" - var deviceName = "" - var port = 0 - var address = "" - var channel = 0 - - override fun getContentView(): Int { - return R.layout.activity_video_preview - } - - override fun onResume() { - super.onResume() - XP2P.setCallback(this) - startPlayer() - } - - override fun initView() { - var bundle = intent.getBundleExtra(VideoConst.VIDEO_CONFIG) - bundle?.let { - var videoConfig = bundle.getString(VideoConst.VIDEO_CONFIG) - if (TextUtils.isEmpty(videoConfig)) return@let - var devInfo = JSON.parseObject(videoConfig, DevUrl2Preview::class.java) - devInfo?.let { - tv_title.setText(it.devName) - deviceName = it.devName - channel = it.channel - address = it.address - port = it.port - } - } - - tv_video_quality.setText(R.string.video_quality_medium_str) - App.data.accessInfo?.let { - productId = it.productId - audioRecordUtil = AudioRecordUtil(this, "${it.productId}/${deviceName}", 16000) - } - } - - fun startPlayer() { - player = IjkMediaPlayer() - Log.e( - tag, - "start startPlayer productid ${App.data.accessInfo?.productId} devname ${deviceName}" - ) - if (App.data.accessInfo == null || TextUtils.isEmpty(deviceName)) return - mHandler.sendEmptyMessageDelayed(MSG_UPDATE_HUD, 500) - - Thread(Runnable { - val id = "${productId}/${deviceName}" - val started = XP2P.startLanService(id, productId, deviceName, address, port.toString()) - Log.e(tag, "===================== after startLanService =====================") - if (started != 0) { - launch(Dispatchers.Main) { - var errInfo = "" - if (started.toString() == "-1007") { - errInfo = getString(R.string.xp2p_err_version) - } else { - errInfo = getString(R.string.error_with_code, id, started.toString()) - } - Toast.makeText(this@WlanVideoPreviewActivity, errInfo, Toast.LENGTH_SHORT) - .show() - } - return@Runnable - } - }).start() - } - - override fun onSurfaceTextureAvailable(surface: SurfaceTexture?, width: Int, height: Int) { - surface?.let { - this.surface = Surface(surface) - player.setSurface(this.surface) - } - } - - fun speakAble(able: Boolean): Boolean { - val id = "${productId}/${deviceName}" - if (!TextUtils.isEmpty(id)) { - if (able) { - val port = XP2P.getLanProxyPort(id) - val command = - XP2P.getLanUrl(id) + "voice?_protocol=tcp&_port=$port&channel=${channel}" - Log.e(tag, "start radio url $command") - Log.e(tag, "speakAble id $id") - XP2P.runSendService(id, command, true) - audioRecordUtil.start() - return true - } else { - Log.e(tag, "stop radio") - audioRecordUtil.stop() - XP2P.stopSendService(id, null) - return true - } - } - return false - } - - override fun setListener() { - iv_back.setOnClickListener { finish() } - tv_video_quality.setOnClickListener(switchVideoQualityListener) - radio_talk.setOnCheckedChangeListener { buttonView, isChecked -> - if (isChecked && checkPermissions(permissions)) { - if (!speakAble(true)) radio_talk.isChecked = false - } else if (isChecked && !checkPermissions(permissions)) { - requestPermission(permissions) - } else { - speakAble(false) - } - } - iv_up.setOnClickListener(controlListener) - iv_down.setOnClickListener(controlListener) - iv_right.setOnClickListener(controlListener) - iv_left.setOnClickListener(controlListener) - v_preview.surfaceTextureListener = this - iv_audio.setOnClickListener { - audioAble = !audioAble - chgAudioStatus(audioAble) - } - } - - fun chgAudioStatus(audioAble: Boolean) { - if (!audioAble) { - iv_audio.setImageResource(R.mipmap.no_audio) - player.setVolume(0F, 0F) - } else { - iv_audio.setImageResource(R.mipmap.audio) - var audioManager = getSystemService(Service.AUDIO_SERVICE) as AudioManager - var volume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC) - player.setVolume(volume.toFloat(), volume.toFloat()) - } - } - - var controlListener = object : View.OnClickListener { - override fun onClick(v: View?) { - val id = "${productId}/${deviceName}" - var command = XP2P.getLanUrl(id) + "command?_protocol=tcp&" - when (v) { - iv_up -> command += Command.getPtzUpCommand(channel) - iv_down -> command += Command.getPtzDownCommand(channel) - iv_right -> command += Command.getPtzRightCommand(channel) - iv_left -> command += Command.getPtzLeftCommand(channel) - } - Log.e(tag, "command $command") - - Thread(Runnable { - App.data.accessInfo?.let { - if (command.length <= 0) return@Runnable - var retContent = XP2P.postCommandRequestSync( - "${productId}/${deviceName}", - command.toByteArray(), command.toByteArray().size.toLong(), 2 * 1000 * 1000 - ) ?: "" - Log.d(tag, "command result -> $retContent") - launch(Dispatchers.Main) { - if (TextUtils.isEmpty(retContent)) { - retContent = getString(R.string.command_with_error, command) - } - Toast.makeText( - this@WlanVideoPreviewActivity, - retContent, - Toast.LENGTH_SHORT - ).show() - } - } - }).start() - } - } - - fun chgTextState(value: Int) { - val id = "${productId}/${deviceName}" - val port = XP2P.getLanProxyPort(id) - var command = "" - when (value) { - 0 -> { - tv_video_quality.setText(R.string.video_quality_high_str) - command = - "ipc.flv?action=live&_protocol=tcp&quality=super&_crypto=off&_port=$port&channel=${channel}" - } - 1 -> { - tv_video_quality.setText(R.string.video_quality_medium_str) - command = - "ipc.flv?action=live&_protocol=tcp&quality=high&_crypto=off&_port=$port&channel=${channel}" - } - 2 -> { - tv_video_quality.setText(R.string.video_quality_low_str) - command = - "ipc.flv?action=live&_protocol=tcp&quality=standard&_crypto=off&_port=$port&channel=${channel}" - } - } - - setPlayerUrl(command) - chgAudioStatus(audioAble) - } - - private fun setPlayerUrl(suffix: String) { - player.release() - launch(Dispatchers.Main) { - layout_video_preview?.removeView(v_preview) - layout_video_preview?.addView(v_preview, 0) - } - - player = IjkMediaPlayer() - player.let { - val id = "${productId}/${deviceName}" - val url = XP2P.getLanUrl(id) + suffix - it.reset() - - it.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "analyzemaxduration", 100) - it.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "probesize", 25 * 1024) - it.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "packet-buffering", 0) - it.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "start-on-prepared", 1) - it.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, "threads", 1) - it.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "sync-av-start", 0) - it.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 1) - it.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-auto-rotate", 1) - it.setOption( - IjkMediaPlayer.OPT_CATEGORY_PLAYER, - "mediacodec-handle-resolution-change", - 1 - ) - - it.setSurface(this.surface) - Log.e(tag, "switch url $url") - it.dataSource = url - - it.prepareAsync() - it.start() - } - } - - override fun onSurfaceTextureSizeChanged(surface: SurfaceTexture?, width: Int, height: Int) {} - override fun onSurfaceTextureDestroyed(surface: SurfaceTexture?): Boolean { - return false - } - - override fun onSurfaceTextureUpdated(surface: SurfaceTexture?) { - if (!showTip && startShowVideoTime > 0) { - showVideoTime = System.currentTimeMillis() - startShowVideoTime - val content = - getString(R.string.time_2_show, connectTime.toString(), showVideoTime.toString()) - TipToastDialog(this, content, 10000).show() - showTip = true - } - } - - override fun fail(msg: String?, errorCode: Int) {} - override fun commandRequest(id: String?, msg: String?) {} - override fun avDataRecvHandle(id: String?, data: ByteArray?, len: Int) {} - override fun avDataCloseHandle(id: String?, msg: String?, errorCode: Int) {} - override fun onDeviceMsgArrived(id: String?, data: ByteArray?, len: Int): String { - Log.d(tag, "==onDeviceMsgArrived: ${data?.let { String(it, StandardCharsets.UTF_8) }}") - return "app reply to device" - } - - override fun xp2pEventNotify(id: String?, msg: String?, event: Int) { - if (event == 1004) { - player?.let { - var url = XP2P.getLanUrl(id) - val port = XP2P.getLanProxyPort(id) - url += "ipc.flv?action=live&_protocol=tcp&quality=high&_crypto=off&_port=$port&channel=${channel}" - Log.e(tag, "proxy ===================== $url") - it.reset() - it.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "analyzemaxduration", 100) - it.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "probesize", 25 * 1024) - it.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "packet-buffering", 0) - it.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "start-on-prepared", 1) - it.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, "threads", 1) - it.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "sync-av-start", 0) - it.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 1) - it.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-auto-rotate", 1) - it.setOption( - IjkMediaPlayer.OPT_CATEGORY_PLAYER, - "mediacodec-handle-resolution-change", - 1 - ) - it.dataSource = url - it.prepareAsync() - it.start() - } - } - } - - override fun onPause() { - super.onPause() - finishPlayer() - } - - private fun finishPlayer() { - player?.release() - if (radio_talk.isChecked) speakAble(false) - App.data.accessInfo?.let { - XP2P.stopService("${productId}/${deviceName}") - } - } - - override fun onDestroy() { - super.onDestroy() - finishPlayer() - XP2P.setCallback(null) - cancel() - } - - companion object { - fun startPreviewActivity(context: Context?, dev: DevInfo) { - context ?: let { return } - - var intent = Intent(context, WlanVideoPreviewActivity::class.java) - var bundle = Bundle() - intent.putExtra(VideoConst.VIDEO_CONFIG, bundle) - var devInfo = DevUrl2Preview() - devInfo.devName = dev.DeviceName - devInfo.Status = dev.Status - devInfo.channel = dev.Channel - bundle.putString(VideoConst.VIDEO_CONFIG, JSON.toJSONString(devInfo)) - context.startActivity(intent) - } - } - - private var switchVideoQualityListener = object : View.OnClickListener { - override fun onClick(v: View?) { - showVVideoQualityDialog() - } - } - - private fun showVVideoQualityDialog() { - var options = arrayListOf( - getString(R.string.video_quality_high_str) + " " + getString(R.string.video_quality_high), - getString(R.string.video_quality_medium_str) + " " + getString(R.string.video_quality_medium), - getString(R.string.video_quality_low_str) + " " + getString(R.string.video_quality_low) - ) - var dlg = ListOptionsDialog(this@WlanVideoPreviewActivity, options) - dlg.show() - dlg.setOnDismisListener { chgTextState(it) } - } - - private val mHandler: Handler = object : Handler() { - override fun handleMessage(msg: Message) { - when (msg.what) { - MSG_UPDATE_HUD -> { - val videoCachedDuration = player?.videoCachedDuration - val audioCachedDuration = player?.audioCachedDuration - val videoCachedBytes = player?.videoCachedBytes - val audioCachedBytes = player?.audioCachedBytes - val tcpSpeed = player?.tcpSpeed - - tv_a_cache?.text = String.format( - Locale.US, "%s, %s", - CommonUtils.formatedDurationMilli(audioCachedDuration), - CommonUtils.formatedSize(audioCachedBytes) - ) - tv_v_cache?.text = String.format( - Locale.US, "%s, %s", - CommonUtils.formatedDurationMilli(videoCachedDuration), - CommonUtils.formatedSize(videoCachedBytes) - ) - tv_tcp_speed?.text = String.format( - Locale.US, "%s", - CommonUtils.formatedSpeed(tcpSpeed, 1000) - ) - removeMessages(MSG_UPDATE_HUD) - sendEmptyMessageDelayed(MSG_UPDATE_HUD, 500) - } - } - } - } -} \ No newline at end of file +//package com.tencent.iot.explorer.link.demo.video.preview +// +//import android.Manifest +//import android.app.Service +//import android.content.Context +//import android.content.Intent +//import android.graphics.SurfaceTexture +//import android.media.AudioManager +//import android.os.Bundle +//import android.os.Handler +//import android.os.Message +//import android.text.TextUtils +//import android.util.Log +//import android.view.Surface +//import android.view.TextureView +//import android.view.View +//import android.widget.Toast +//import com.alibaba.fastjson.JSON +//import com.tencent.iot.explorer.link.demo.App +//import com.tencent.iot.explorer.link.demo.R +//import com.tencent.iot.explorer.link.demo.VideoBaseActivity +//import com.tencent.iot.explorer.link.demo.common.util.CommonUtils +//import com.tencent.iot.explorer.link.demo.video.Command +//import com.tencent.iot.explorer.link.demo.video.DevInfo +//import com.tencent.iot.explorer.link.demo.video.VideoPreviewBaseActivity +//import com.tencent.iot.explorer.link.demo.video.playback.cloudPlayback.event.ActionRecord +//import com.tencent.iot.explorer.link.demo.video.utils.ListOptionsDialog +//import com.tencent.iot.explorer.link.demo.video.utils.TipToastDialog +//import com.tencent.iot.video.link.consts.VideoConst +//import com.tencent.iot.video.link.util.audio.AudioRecordUtil +//import com.tencent.xnet.XP2P +//import com.tencent.xnet.XP2PCallback +//import kotlinx.android.synthetic.main.activity_video_preview.* +//import kotlinx.android.synthetic.main.dash_board_layout.* +//import kotlinx.android.synthetic.main.fragment_video_cloud_playback.* +//import kotlinx.android.synthetic.main.title_layout.* +//import kotlinx.coroutines.* +//import tv.danmaku.ijk.media.player.IjkMediaPlayer +//import java.lang.Runnable +//import java.nio.charset.StandardCharsets +//import java.util.* +// +// +//class WlanVideoPreviewActivity : VideoPreviewBaseActivity(), TextureView.SurfaceTextureListener, +// XP2PCallback, CoroutineScope by MainScope() { +// +// var tag = WlanVideoPreviewActivity::class.simpleName +// lateinit var player: IjkMediaPlayer +// lateinit var surface: Surface +// +// @Volatile +// var audioAble = true +// lateinit var audioRecordUtil: AudioRecordUtil +// var permissions = arrayOf(Manifest.permission.RECORD_AUDIO) +// +// @Volatile +// var showTip = false +// +// @Volatile +// var connectTime = 0L +// +// @Volatile +// var startShowVideoTime = 0L +// +// @Volatile +// var showVideoTime = 0L +// +// val MSG_UPDATE_HUD = 1 +// var productId = "" +// var deviceName = "" +// var port = 0 +// var address = "" +// var channel = 0 +// override fun updateXp2pInfo(xp2pInfo: String) { +// TODO("Not yet implemented") +// } +// +// override fun getContentView(): Int { +// return R.layout.activity_video_preview +// } +// +// override fun onResume() { +// super.onResume() +// XP2P.setCallback(this) +// startPlayer() +// } +// +// override fun initView() { +// var devInfo = JSON.parseObject(videoConfig, DevUrl2Preview::class.java) +// devInfo?.let { +// tv_title.setText(presenter.getDeviceName()) +// deviceName = presenter.getDeviceName() +// channel = presenter.getChannel() +// address = it.address +// port = it.port +// } +// } +// +// tv_video_quality.setText(R.string.video_quality_medium_str) +// App.data.accessInfo?.let { +// productId = it.productId +// audioRecordUtil = AudioRecordUtil(this, "${it.productId}/${deviceName}", 16000) +// } +// } +// +// fun startPlayer() { +// player = IjkMediaPlayer() +// Log.e( +// tag, +// "start startPlayer productid ${App.data.accessInfo?.productId} devname ${deviceName}" +// ) +// if (App.data.accessInfo == null || TextUtils.isEmpty(deviceName)) return +// mHandler.sendEmptyMessageDelayed(MSG_UPDATE_HUD, 500) +// +// Thread(Runnable { +// val id = "${productId}/${deviceName}" +// val started = XP2P.startLanService(id, productId, deviceName, address, port.toString()) +// Log.e(tag, "===================== after startLanService =====================") +// if (started != 0) { +// launch(Dispatchers.Main) { +// var errInfo = "" +// if (started.toString() == "-1007") { +// errInfo = getString(R.string.xp2p_err_version) +// } else { +// errInfo = getString(R.string.error_with_code, id, started.toString()) +// } +// Toast.makeText(this@WlanVideoPreviewActivity, errInfo, Toast.LENGTH_SHORT) +// .show() +// } +// return@Runnable +// } +// }).start() +// } +// +// override fun onSurfaceTextureAvailable(surface: SurfaceTexture?, width: Int, height: Int) { +// surface?.let { +// this.surface = Surface(surface) +// player.setSurface(this.surface) +// } +// } +// +// fun speakAble(able: Boolean): Boolean { +// val id = "${productId}/${deviceName}" +// if (!TextUtils.isEmpty(id)) { +// if (able) { +// val port = XP2P.getLanProxyPort(id) +// val command = +// XP2P.getLanUrl(id) + "voice?_protocol=tcp&_port=$port&channel=${channel}" +// Log.e(tag, "start radio url $command") +// Log.e(tag, "speakAble id $id") +// XP2P.runSendService(id, command, true) +// audioRecordUtil.start() +// return true +// } else { +// Log.e(tag, "stop radio") +// audioRecordUtil.stop() +// XP2P.stopSendService(id, null) +// return true +// } +// } +// return false +// } +// +// override fun setListener() { +// iv_back.setOnClickListener { finish() } +// tv_video_quality.setOnClickListener(switchVideoQualityListener) +// radio_talk.setOnCheckedChangeListener { buttonView, isChecked -> +// if (isChecked && checkPermissions(permissions)) { +// if (!speakAble(true)) radio_talk.isChecked = false +// } else if (isChecked && !checkPermissions(permissions)) { +// requestPermission(permissions) +// } else { +// speakAble(false) +// } +// } +// iv_up.setOnClickListener(controlListener) +// iv_down.setOnClickListener(controlListener) +// iv_right.setOnClickListener(controlListener) +// iv_left.setOnClickListener(controlListener) +// v_preview.surfaceTextureListener = this +// iv_audio.setOnClickListener { +// audioAble = !audioAble +// chgAudioStatus(audioAble) +// } +// } +// +// fun chgAudioStatus(audioAble: Boolean) { +// if (!audioAble) { +// iv_audio.setImageResource(R.mipmap.no_audio) +// player.setVolume(0F, 0F) +// } else { +// iv_audio.setImageResource(R.mipmap.audio) +// var audioManager = getSystemService(Service.AUDIO_SERVICE) as AudioManager +// var volume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC) +// player.setVolume(volume.toFloat(), volume.toFloat()) +// } +// } +// +// var controlListener = object : View.OnClickListener { +// override fun onClick(v: View?) { +// val id = "${productId}/${deviceName}" +// var command = XP2P.getLanUrl(id) + "command?_protocol=tcp&" +// when (v) { +// iv_up -> command += Command.getPtzUpCommand(channel) +// iv_down -> command += Command.getPtzDownCommand(channel) +// iv_right -> command += Command.getPtzRightCommand(channel) +// iv_left -> command += Command.getPtzLeftCommand(channel) +// } +// Log.e(tag, "command $command") +// +// Thread(Runnable { +// App.data.accessInfo?.let { +// if (command.length <= 0) return@Runnable +// var retContent = XP2P.postCommandRequestSync( +// "${productId}/${deviceName}", +// command.toByteArray(), command.toByteArray().size.toLong(), 2 * 1000 * 1000 +// ) ?: "" +// Log.d(tag, "command result -> $retContent") +// launch(Dispatchers.Main) { +// if (TextUtils.isEmpty(retContent)) { +// retContent = getString(R.string.command_with_error, command) +// } +// Toast.makeText( +// this@WlanVideoPreviewActivity, +// retContent, +// Toast.LENGTH_SHORT +// ).show() +// } +// } +// }).start() +// } +// } +// +// fun chgTextState(value: Int) { +// val id = "${productId}/${deviceName}" +// val port = XP2P.getLanProxyPort(id) +// var command = "" +// when (value) { +// 0 -> { +// tv_video_quality.setText(R.string.video_quality_high_str) +// command = +// "ipc.flv?action=live&_protocol=tcp&quality=super&_crypto=off&_port=$port&channel=${channel}" +// } +// 1 -> { +// tv_video_quality.setText(R.string.video_quality_medium_str) +// command = +// "ipc.flv?action=live&_protocol=tcp&quality=high&_crypto=off&_port=$port&channel=${channel}" +// } +// 2 -> { +// tv_video_quality.setText(R.string.video_quality_low_str) +// command = +// "ipc.flv?action=live&_protocol=tcp&quality=standard&_crypto=off&_port=$port&channel=${channel}" +// } +// } +// +// setPlayerUrl(command) +// chgAudioStatus(audioAble) +// } +// +// private fun setPlayerUrl(suffix: String) { +// player.release() +// launch(Dispatchers.Main) { +// layout_video_preview?.removeView(v_preview) +// layout_video_preview?.addView(v_preview, 0) +// } +// +// player = IjkMediaPlayer() +// player.let { +// val id = "${productId}/${deviceName}" +// val url = XP2P.getLanUrl(id) + suffix +// it.reset() +// +// it.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "analyzemaxduration", 100) +// it.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "probesize", 25 * 1024) +// it.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "packet-buffering", 0) +// it.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "start-on-prepared", 1) +// it.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, "threads", 1) +// it.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "sync-av-start", 0) +// it.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 1) +// it.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-auto-rotate", 1) +// it.setOption( +// IjkMediaPlayer.OPT_CATEGORY_PLAYER, +// "mediacodec-handle-resolution-change", +// 1 +// ) +// +// it.setSurface(this.surface) +// Log.e(tag, "switch url $url") +// it.dataSource = url +// +// it.prepareAsync() +// it.start() +// } +// } +// +// override fun onSurfaceTextureSizeChanged(surface: SurfaceTexture?, width: Int, height: Int) {} +// override fun onSurfaceTextureDestroyed(surface: SurfaceTexture?): Boolean { +// return false +// } +// +// override fun onSurfaceTextureUpdated(surface: SurfaceTexture?) { +// if (!showTip && startShowVideoTime > 0) { +// showVideoTime = System.currentTimeMillis() - startShowVideoTime +// val content = +// getString(R.string.time_2_show, connectTime.toString(), showVideoTime.toString()) +// TipToastDialog(this, content, 10000).show() +// showTip = true +// } +// } +// +// override fun fail(msg: String?, errorCode: Int) {} +// override fun commandRequest(id: String?, msg: String?) {} +// override fun avDataRecvHandle(id: String?, data: ByteArray?, len: Int) {} +// override fun avDataCloseHandle(id: String?, msg: String?, errorCode: Int) {} +// override fun onDeviceMsgArrived(id: String?, data: ByteArray?, len: Int): String { +// Log.d(tag, "==onDeviceMsgArrived: ${data?.let { String(it, StandardCharsets.UTF_8) }}") +// return "app reply to device" +// } +// +// override fun xp2pEventNotify(id: String?, msg: String?, event: Int) { +// if (event == 1004) { +// player?.let { +// var url = XP2P.getLanUrl(id) +// val port = XP2P.getLanProxyPort(id) +// url += "ipc.flv?action=live&_protocol=tcp&quality=high&_crypto=off&_port=$port&channel=${channel}" +// Log.e(tag, "proxy ===================== $url") +// it.reset() +// it.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "analyzemaxduration", 100) +// it.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "probesize", 25 * 1024) +// it.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "packet-buffering", 0) +// it.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "start-on-prepared", 1) +// it.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, "threads", 1) +// it.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "sync-av-start", 0) +// it.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 1) +// it.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-auto-rotate", 1) +// it.setOption( +// IjkMediaPlayer.OPT_CATEGORY_PLAYER, +// "mediacodec-handle-resolution-change", +// 1 +// ) +// it.dataSource = url +// it.prepareAsync() +// it.start() +// } +// } +// } +// +// override fun onPause() { +// super.onPause() +// finishPlayer() +// } +// +// override fun eventReady(events: MutableList) { +// } +// +// private fun finishPlayer() { +// player?.release() +// if (radio_talk.isChecked) speakAble(false) +// App.data.accessInfo?.let { +// XP2P.stopService("${productId}/${deviceName}") +// } +// } +// +// override fun onDestroy() { +// super.onDestroy() +// finishPlayer() +// XP2P.setCallback(null) +// cancel() +// } +// +// companion object { +// fun startPreviewActivity(context: Context?, dev: DevInfo) { +// context ?: let { return } +// +// var intent = Intent(context, WlanVideoPreviewActivity::class.java) +// var bundle = Bundle() +// intent.putExtra(VideoConst.VIDEO_CONFIG, bundle) +// var devInfo = DevUrl2Preview() +// devInfo.devName = dev.DeviceName +// devInfo.Status = dev.Status +// devInfo.channel = dev.Channel +// bundle.putString(VideoConst.VIDEO_CONFIG, JSON.toJSONString(devInfo)) +// context.startActivity(intent) +// } +// } +// +// private var switchVideoQualityListener = object : View.OnClickListener { +// override fun onClick(v: View?) { +// showVVideoQualityDialog() +// } +// } +// +// private fun showVVideoQualityDialog() { +// var options = arrayListOf( +// getString(R.string.video_quality_high_str) + " " + getString(R.string.video_quality_high), +// getString(R.string.video_quality_medium_str) + " " + getString(R.string.video_quality_medium), +// getString(R.string.video_quality_low_str) + " " + getString(R.string.video_quality_low) +// ) +// var dlg = ListOptionsDialog(this@WlanVideoPreviewActivity, options) +// dlg.show() +// dlg.setOnDismisListener { chgTextState(it) } +// } +// +// private val mHandler: Handler = object : Handler() { +// override fun handleMessage(msg: Message) { +// when (msg.what) { +// MSG_UPDATE_HUD -> { +// val videoCachedDuration = player?.videoCachedDuration +// val audioCachedDuration = player?.audioCachedDuration +// val videoCachedBytes = player?.videoCachedBytes +// val audioCachedBytes = player?.audioCachedBytes +// val tcpSpeed = player?.tcpSpeed +// +// tv_a_cache?.text = String.format( +// Locale.US, "%s, %s", +// CommonUtils.formatedDurationMilli(audioCachedDuration), +// CommonUtils.formatedSize(audioCachedBytes) +// ) +// tv_v_cache?.text = String.format( +// Locale.US, "%s, %s", +// CommonUtils.formatedDurationMilli(videoCachedDuration), +// CommonUtils.formatedSize(videoCachedBytes) +// ) +// tv_tcp_speed?.text = String.format( +// Locale.US, "%s", +// CommonUtils.formatedSpeed(tcpSpeed, 1000) +// ) +// removeMessages(MSG_UPDATE_HUD) +// sendEmptyMessageDelayed(MSG_UPDATE_HUD, 500) +// } +// } +// } +// } +//} \ No newline at end of file diff --git a/sdkdemo/src/main/res/layout/activity_video_preview.xml b/sdkdemo/src/main/res/layout/activity_video_preview.xml index 45544753d..01d73d28e 100644 --- a/sdkdemo/src/main/res/layout/activity_video_preview.xml +++ b/sdkdemo/src/main/res/layout/activity_video_preview.xml @@ -291,6 +291,7 @@ android:layout_width="match_parent" android:layout_height="0dp" android:layout_marginTop="4dp" + app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" app:layout_constraintTop_toBottomOf="@id/today_tip" app:layout_constraintBottom_toBottomOf="parent" />