1+ package com.tencent.iot.explorer.link.demo.video.preview
2+
3+ import android.graphics.SurfaceTexture
4+ import android.os.Bundle
5+ import android.os.Handler
6+ import android.os.Message
7+ import android.text.TextUtils
8+ import android.util.DisplayMetrics
9+ import android.util.Log
10+ import android.view.Surface
11+ import android.view.TextureView
12+ import android.view.WindowManager
13+ import android.widget.Toast
14+ import com.tencent.iot.explorer.link.demo.R
15+ import com.tencent.iot.explorer.link.demo.VideoBaseActivity
16+ import com.tencent.iot.explorer.link.demo.common.log.L
17+ import com.tencent.iot.explorer.link.demo.common.util.CommonUtils
18+ import com.tencent.iot.explorer.link.demo.video.Command
19+ import com.tencent.iot.explorer.link.demo.video.utils.TipToastDialog
20+ import com.tencent.xnet.XP2P
21+ import com.tencent.xnet.XP2PCallback
22+ import kotlinx.android.synthetic.main.activity_video_preview.layout_video_preview
23+ import kotlinx.android.synthetic.main.activity_video_preview.tv_video_quality
24+ import kotlinx.android.synthetic.main.activity_video_preview.v_preview
25+ import kotlinx.android.synthetic.main.activity_video_test.btn_connect
26+ import kotlinx.android.synthetic.main.activity_video_test.et_device_name
27+ import kotlinx.android.synthetic.main.activity_video_test.et_p2p_info
28+ import kotlinx.android.synthetic.main.activity_video_test.et_product_id
29+ import kotlinx.android.synthetic.main.dash_board_layout.tv_a_cache
30+ import kotlinx.android.synthetic.main.dash_board_layout.tv_tcp_speed
31+ import kotlinx.android.synthetic.main.dash_board_layout.tv_v_cache
32+ import kotlinx.android.synthetic.main.dash_board_layout.tv_video_w_h
33+ import kotlinx.coroutines.CoroutineScope
34+ import kotlinx.coroutines.Dispatchers
35+ import kotlinx.coroutines.MainScope
36+ import kotlinx.coroutines.cancel
37+ import kotlinx.coroutines.delay
38+ import kotlinx.coroutines.launch
39+ import org.json.JSONObject
40+ import tv.danmaku.ijk.media.player.IMediaPlayer
41+ import tv.danmaku.ijk.media.player.IjkMediaPlayer
42+ import java.lang.ref.WeakReference
43+ import java.util.Locale
44+
45+ open class VideoTestActivity : VideoBaseActivity (), XP2PCallback, CoroutineScope by MainScope(),
46+ TextureView .SurfaceTextureListener ,
47+ IMediaPlayer .OnInfoListener {
48+ private val player = IjkMediaPlayer ()
49+ lateinit var surface: Surface
50+ private var productId = " "
51+ private var deviceName = " "
52+ private var xp2pInfo = " "
53+ var urlPrefix = " "
54+ var screenWidth = 0
55+ var screenHeight = 0
56+ var startShowVideoTime = 0L
57+ var showVideoTime = 0L
58+ var connectTime = 0L
59+ var showTip = false
60+ var firstIn = true
61+ val MSG_UPDATE_HUD = 1
62+
63+ override fun onCreate (savedInstanceState : Bundle ? ) {
64+ super .onCreate(savedInstanceState)
65+ XP2P .setCallback(this )
66+ }
67+
68+ override fun getContentView (): Int {
69+ return R .layout.activity_video_test
70+ }
71+
72+ override fun initView () {
73+ tv_video_quality.text = " 高清"
74+ v_preview.surfaceTextureListener = this
75+ et_product_id.setText(productId)
76+ et_device_name.setText(deviceName)
77+ et_p2p_info.setText(xp2pInfo)
78+ val wm = this .getSystemService(WINDOW_SERVICE ) as WindowManager
79+ val dm = DisplayMetrics ()
80+ wm.defaultDisplay.getMetrics(dm)
81+ val width = dm.widthPixels // 屏幕宽度(像素)
82+ val height = dm.heightPixels // 屏幕高度(像素)
83+ val density = dm.density // 屏幕密度(0.75 / 1.0 / 1.5)
84+ screenWidth = (width / density).toInt() // 屏幕宽度(dp)
85+ screenHeight = (height / density).toInt() // 屏幕高度(dp)
86+ }
87+
88+ override fun setListener () {
89+ btn_connect.setOnClickListener {
90+ if (et_product_id.text.isNullOrEmpty()) {
91+ show(" 请输入productId" )
92+ return @setOnClickListener
93+ }
94+ if (et_device_name.text.isNullOrEmpty()) {
95+ show(" 请输入deviceName" )
96+ return @setOnClickListener
97+ }
98+ if (et_p2p_info.text.isNullOrEmpty()) {
99+ show(" 请输入P2PInfo" )
100+ return @setOnClickListener
101+ }
102+ productId = et_product_id.text.toString()
103+ deviceName = et_device_name.text.toString()
104+ xp2pInfo = et_p2p_info.text.toString()
105+ XP2P .startService(
106+ this @VideoTestActivity, " ${productId} /${deviceName} " , productId, deviceName
107+ )
108+ val ret = XP2P .setParamsForXp2pInfo(
109+ " ${productId} /${deviceName} " , " " , " " , xp2pInfo
110+ )
111+ if (ret != 0 ) {
112+ launch(Dispatchers .Main ) {
113+ val errInfo: String
114+ if (ret.toString() == " -1007" ) {
115+ errInfo = getString(R .string.xp2p_err_version)
116+ } else {
117+ errInfo = getString(
118+ R .string.error_with_code,
119+ " ${productId} /${deviceName} " ,
120+ ret.toString()
121+ )
122+ }
123+ Toast .makeText(this @VideoTestActivity, errInfo, Toast .LENGTH_SHORT ).show()
124+ }
125+ }
126+ }
127+ }
128+
129+ private fun resetPlayer () {
130+ when (tv_video_quality.text.toString()) {
131+ getString(R .string.video_quality_high_str) -> setPlayerUrl(
132+ Command .getVideoSuperQualityUrlSuffix(
133+ 0
134+ )
135+ )
136+
137+ getString(R .string.video_quality_medium_str) -> setPlayerUrl(
138+ Command .getVideoHightQualityUrlSuffix(
139+ 0
140+ )
141+ )
142+
143+ getString(R .string.video_quality_low_str) -> setPlayerUrl(
144+ Command .getVideoStandardQualityUrlSuffix(
145+ 0
146+ )
147+ )
148+ }
149+ }
150+
151+ open fun setPlayerUrl (suffix : String ) {
152+ mHandler.sendEmptyMessageDelayed(MSG_UPDATE_HUD , 500 )
153+ showTip = false
154+ startShowVideoTime = System .currentTimeMillis()
155+ launch(Dispatchers .Main ) {
156+ layout_video_preview?.removeView(v_preview)
157+ layout_video_preview?.addView(v_preview, 0 )
158+ player.setOnInfoListener(this @VideoTestActivity)
159+ player.let {
160+ val url = urlPrefix + suffix
161+ it.setOption(IjkMediaPlayer .OPT_CATEGORY_FORMAT , " probesize" , 50 * 1024 )
162+ it.setOption(IjkMediaPlayer .OPT_CATEGORY_PLAYER , " packet-buffering" , 0 )
163+ it.setOption(IjkMediaPlayer .OPT_CATEGORY_PLAYER , " start-on-prepared" , 1 )
164+ it.setOption(IjkMediaPlayer .OPT_CATEGORY_CODEC , " threads" , 1 )
165+ it.setOption(IjkMediaPlayer .OPT_CATEGORY_PLAYER , " sync-av-start" , 0 )
166+ it.setOption(IjkMediaPlayer .OPT_CATEGORY_PLAYER , " mediacodec" , 1 )
167+ it.setOption(IjkMediaPlayer .OPT_CATEGORY_PLAYER , " mediacodec-auto-rotate" , 1 )
168+ it.setOption(
169+ IjkMediaPlayer .OPT_CATEGORY_PLAYER , " mediacodec-handle-resolution-change" , 1
170+ )
171+
172+ it.setFrameSpeed(1.5f )
173+ while (! ::surface.isInitialized) {
174+ delay(50 )
175+ L .e(" delay for waiting surface." )
176+ }
177+ it.setSurface(surface)
178+ it.dataSource = url
179+
180+ it.prepareAsync()
181+ it.start()
182+ }
183+ }
184+ }
185+
186+ override fun fail (msg : String? , errorCode : Int ) {
187+
188+ }
189+
190+ override fun commandRequest (id : String? , msg : String? ) {
191+ Log .e(" VideoTestActivity" , " xp2pEventNotify id:$id msg:$msg " )
192+ }
193+
194+ override fun xp2pEventNotify (id : String? , msg : String? , event : Int ) {
195+ Log .e(" VideoTestActivity" , " xp2pEventNotify id:$id msg:$msg event:$event " )
196+ if (event == 1004 || event == 1005 ) {
197+ if (event == 1004 ) {
198+ Log .e(" VideoTestActivity" , " ====event === 1004" )
199+ XP2P .delegateHttpFlv(id)?.let {
200+ urlPrefix = it
201+ if (! TextUtils .isEmpty(urlPrefix)) {
202+ resetPlayer()
203+ }
204+ }
205+ }
206+ }
207+ }
208+
209+ override fun avDataRecvHandle (id : String? , data : ByteArray? , len : Int ) {
210+ Log .e(" VideoTestActivity" , " avDataRecvHandle id:$id data:$data len:$data " )
211+ }
212+
213+ override fun avDataCloseHandle (id : String? , msg : String? , errorCode : Int ) {
214+ Log .e(" VideoTestActivity" , " avDataCloseHandle id:$id msg:$msg errorCode:$errorCode " )
215+ }
216+
217+ override fun onDeviceMsgArrived (id : String? , data : ByteArray? , len : Int ): String {
218+ Log .e(" VideoTestActivity" , " onDeviceMsgArrived id:$id data:$data len:$len " )
219+ val reply = JSONObject ()
220+ reply.put(" code" , " 0" )
221+ reply.put(" msg" , " test command reply" )
222+ return reply.toString()
223+ }
224+
225+ override fun onSurfaceTextureAvailable (surface : SurfaceTexture ? , width : Int , height : Int ) {
226+ surface?.let {
227+ this .surface = Surface (surface)
228+ player.setSurface(this .surface)
229+ }
230+ }
231+
232+ override fun onSurfaceTextureSizeChanged (surface : SurfaceTexture ? , width : Int , height : Int ) {
233+ val layoutParams = v_preview.layoutParams
234+ layoutParams.width = (player.videoWidth * (screenWidth * 16 / 9 )) / player.videoHeight
235+ layoutParams.height = layoutParams.height
236+ v_preview.layoutParams = layoutParams
237+ }
238+
239+ override fun onSurfaceTextureDestroyed (surface : SurfaceTexture ? ): Boolean {
240+ return false
241+ }
242+
243+ override fun onSurfaceTextureUpdated (surface : SurfaceTexture ? ) {
244+ if (! showTip && startShowVideoTime > 0 ) {
245+ showVideoTime = System .currentTimeMillis() - startShowVideoTime
246+ val content =
247+ getString(R .string.time_2_show, connectTime.toString(), showVideoTime.toString())
248+ TipToastDialog (this , content, 10000 ).show()
249+ showTip = true
250+ }
251+ if (firstIn) {
252+ val layoutParams = v_preview.layoutParams
253+ layoutParams.width = (player.videoWidth * (screenWidth * 16 / 9 )) / player.videoHeight
254+ layoutParams.height = layoutParams.height
255+ v_preview.layoutParams = layoutParams
256+ firstIn = false
257+ }
258+ }
259+
260+ override fun onInfo (p0 : IMediaPlayer ? , p1 : Int , p2 : Int ): Boolean {
261+ return true
262+ }
263+
264+ override fun onInfoSEI (p0 : IMediaPlayer ? , p1 : Int , p2 : Int , p3 : String? ): Boolean {
265+ return false
266+ }
267+
268+ override fun onInfoAudioPcmData (p0 : IMediaPlayer ? , p1 : ByteArray? , p2 : Int ) {
269+ }
270+
271+ private val mHandler = MyHandler (this )
272+
273+ private class MyHandler (activity : VideoTestActivity ) : Handler() {
274+ private val mActivity: WeakReference <VideoTestActivity > = WeakReference (activity)
275+ override fun handleMessage (msg : Message ) {
276+ if (mActivity.get() == null ) {
277+ return
278+ }
279+ val activity = mActivity.get()
280+ when (msg.what) {
281+ activity?.MSG_UPDATE_HUD -> {
282+ activity.updateDashboard()
283+ removeMessages(activity.MSG_UPDATE_HUD )
284+ sendEmptyMessageDelayed(activity.MSG_UPDATE_HUD , 500 )
285+ }
286+ }
287+ }
288+ }
289+
290+ private fun updateDashboard () {
291+ val videoCachedDuration = player.videoCachedDuration
292+ val audioCachedDuration = player.audioCachedDuration
293+ val videoCachedBytes = player.videoCachedBytes
294+ val audioCachedBytes = player.audioCachedBytes
295+ val tcpSpeed = player.tcpSpeed
296+
297+ tv_a_cache?.text = String .format(
298+ Locale .US , " %s, %s" ,
299+ CommonUtils .formatedDurationMilli(audioCachedDuration),
300+ CommonUtils .formatedSize(audioCachedBytes)
301+ )
302+ tv_v_cache?.text = String .format(
303+ Locale .US , " %s, %s" ,
304+ CommonUtils .formatedDurationMilli(videoCachedDuration),
305+ CommonUtils .formatedSize(videoCachedBytes)
306+ )
307+ tv_tcp_speed?.text = String .format(
308+ Locale .US , " %s" ,
309+ CommonUtils .formatedSpeed(tcpSpeed, 1000 )
310+ )
311+ tv_video_w_h?.text = " ${player.videoWidth} x ${player.videoHeight} "
312+ }
313+
314+ override fun onDestroy () {
315+ super .onDestroy()
316+ finishPlayer()
317+ XP2P .stopService(" ${productId} /${deviceName} " )
318+ XP2P .setCallback(null )
319+ cancel()
320+ }
321+
322+ private fun finishPlayer () {
323+ mHandler.removeMessages(MSG_UPDATE_HUD )
324+ player.release()
325+ }
326+ }
0 commit comments