Ijk.kt 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. package top.kikt.ijkplayer
  2. /// create 2019/3/7 by cai
  3. import android.graphics.Bitmap
  4. import android.util.Base64
  5. import io.flutter.plugin.common.MethodCall
  6. import io.flutter.plugin.common.MethodChannel
  7. import io.flutter.plugin.common.PluginRegistry
  8. import top.kikt.ijkplayer.entity.Info
  9. import tv.danmaku.ijk.media.player.IjkMediaPlayer
  10. import tv.danmaku.ijk.media.player.TextureMediaPlayer
  11. import java.io.ByteArrayOutputStream
  12. import java.io.File
  13. class Ijk(private val registry: PluginRegistry.Registrar) : MethodChannel.MethodCallHandler {
  14. private val textureEntry = registry.textures().createSurfaceTexture()
  15. val id: Long
  16. get() = textureEntry.id()
  17. val mediaPlayer: IjkMediaPlayer = IjkMediaPlayer()
  18. private val textureMediaPlayer: TextureMediaPlayer
  19. private val methodChannel: MethodChannel = MethodChannel(registry.messenger(), "top.kikt/ijkplayer/$id")
  20. private val notifyChannel: NotifyChannel = NotifyChannel(registry, id, this)
  21. var degree = 0
  22. init {
  23. textureMediaPlayer = TextureMediaPlayer(mediaPlayer)
  24. configOptions()
  25. textureMediaPlayer.surfaceTexture = textureEntry.surfaceTexture()
  26. methodChannel.setMethodCallHandler(this)
  27. }
  28. private fun configOptions() {
  29. // see https://www.jianshu.com/p/843c86a9e9ad
  30. mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "fflags", "fastseek")
  31. mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "analyzemaxduration", 100L)
  32. mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "analyzeduration", 1)
  33. mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "probesize", 1024 * 10)
  34. mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "flush_packets", 1L)
  35. mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "reconnect", 5)
  36. mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "framedrop", 5)
  37. mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "enable-accurate-seek", 1)
  38. mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 1) // 开硬解
  39. // mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "max-buffer-size", maxCacheSize)
  40. // mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "packet-buffering", if (isBufferCache) 1 else 0)
  41. }
  42. override fun onMethodCall(call: MethodCall?, result: MethodChannel.Result?) {
  43. when (call?.method) {
  44. "setNetworkDataSource" -> {
  45. val uri = call.argument<String>("uri")
  46. val params = call.argument<Map<String, String>>("headers")
  47. if (uri == null) {
  48. handleSetUriResult(Exception("uri是必传参数"), result)
  49. return
  50. }
  51. setUri(uri, params) { throwable ->
  52. handleSetUriResult(throwable, result)
  53. }
  54. }
  55. "setAssetDataSource" -> {
  56. val name = call.argument<String>("name")
  57. val `package` = call.argument<String>("package")
  58. if (name != null) {
  59. setAssetUri(name, `package`) { throwable ->
  60. handleSetUriResult(throwable, result)
  61. }
  62. } else {
  63. handleSetUriResult(Exception("没有找到资源"), result)
  64. }
  65. }
  66. "setFileDataSource" -> {
  67. val path = call.argument<String>("path")
  68. if (path != null) {
  69. setUri("file://$path", hashMapOf()) { throwable ->
  70. handleSetUriResult(throwable, result)
  71. }
  72. }
  73. }
  74. "play" -> {
  75. play()
  76. result?.success(true)
  77. }
  78. "pause" -> {
  79. pause()
  80. result?.success(true)
  81. }
  82. "stop" -> {
  83. stop()
  84. result?.success(true)
  85. }
  86. "getInfo" -> {
  87. val info = getInfo()
  88. result?.success(info.toMap())
  89. }
  90. "seekTo" -> {
  91. val target = call.argument<Double>("target")
  92. if (target != null) {
  93. seekTo((target * 1000).toLong())
  94. }
  95. result?.success(true)
  96. }
  97. "setVolume" -> {
  98. val volume = call.argument<Int>("volume")
  99. setVolume(volume)
  100. result?.success(true)
  101. }
  102. "getVolume" -> {
  103. // result?.success(this.mediaPlayer.setVolume())
  104. }
  105. "screenShot" -> {
  106. val bytes = screenShot()
  107. result?.success(bytes)
  108. }
  109. else -> {
  110. result?.notImplemented()
  111. }
  112. }
  113. }
  114. private fun screenShot(): ByteArray? {
  115. val frameBitmap = mediaPlayer.frameBitmap
  116. return if (frameBitmap != null) {
  117. val outputStream = ByteArrayOutputStream()
  118. frameBitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream)
  119. frameBitmap.recycle()
  120. outputStream.toByteArray()
  121. } else {
  122. null
  123. }
  124. }
  125. fun getInfo(): Info {
  126. val duration = mediaPlayer.duration
  127. val currentPosition = mediaPlayer.currentPosition
  128. val width = mediaPlayer.videoWidth
  129. val height = mediaPlayer.videoHeight
  130. val outputFps = mediaPlayer.videoOutputFramesPerSecond
  131. // mediaPlayer.mediaInfo.mAudioDecoder
  132. // mediaPlayer.mediaInfo.mVideoDecoder
  133. return Info(
  134. duration = duration.toDouble() / 1000,
  135. currentPosition = currentPosition.toDouble() / 1000,
  136. width = width,
  137. height = height,
  138. isPlaying = textureMediaPlayer.isPlaying,
  139. degree = degree,
  140. tcpSpeed = mediaPlayer.tcpSpeed,
  141. outputFps = outputFps
  142. // decodeFps = mediaPlayer.videoDecodeFramesPerSecond
  143. )
  144. }
  145. private fun handleSetUriResult(throwable: Throwable?, result: MethodChannel.Result?) {
  146. if (throwable == null) {
  147. result?.success(true)
  148. } else {
  149. throwable.printStackTrace()
  150. result?.error("1", "设置资源失败", throwable)
  151. }
  152. }
  153. private fun setUri(uri: String, headers: Map<String, String>?, callback: (Throwable?) -> Unit) {
  154. try {
  155. // mediaPlayer.dataSource = uri
  156. mediaPlayer.setDataSource(uri, headers)
  157. mediaPlayer.prepareAsync()
  158. callback(null)
  159. } catch (e: Exception) {
  160. e.printStackTrace()
  161. callback(e)
  162. }
  163. }
  164. private fun setAssetUri(name: String, `package`: String?, callback: (Throwable?) -> Unit) {
  165. try {
  166. mediaPlayer.setOnPreparedListener {
  167. callback(null)
  168. }
  169. val asset =
  170. if (`package` == null) {
  171. registry.lookupKeyForAsset(name)
  172. } else {
  173. registry.lookupKeyForAsset(name, `package`)
  174. }
  175. val assetManager = registry.context().assets
  176. val fd = assetManager.openFd(asset)
  177. val cacheDir = registry.context().cacheDir.absoluteFile.path
  178. val fileName = Base64.encodeToString(asset.toByteArray(), Base64.DEFAULT)
  179. val file = File(cacheDir, fileName)
  180. fd.createInputStream().copyTo(file.outputStream())
  181. mediaPlayer.dataSource = file.path
  182. // ijkPlayer.setDataSource(fd.fileDescriptor) // can't use,
  183. mediaPlayer.prepareAsync()
  184. } catch (e: Exception) {
  185. e.printStackTrace()
  186. callback(e)
  187. }
  188. }
  189. fun dispose() {
  190. notifyChannel.dispose()
  191. methodChannel.setMethodCallHandler(null)
  192. textureMediaPlayer.stop()
  193. textureMediaPlayer.release()
  194. textureEntry.release()
  195. }
  196. private fun play() {
  197. try {
  198. mediaPlayer.start()
  199. } catch (e: Exception) {
  200. e.printStackTrace()
  201. }
  202. }
  203. private fun pause() {
  204. textureMediaPlayer.pause()
  205. }
  206. private fun stop() {
  207. textureMediaPlayer.stop()
  208. }
  209. private fun seekTo(msec: Long) {
  210. textureMediaPlayer.seekTo(msec)
  211. }
  212. private fun setVolume(volume: Int?) {
  213. if (volume == null) {
  214. return
  215. }
  216. val v = volume.toFloat() / 100
  217. mediaPlayer.setVolume(v, v)
  218. }
  219. }