Ijk.kt 7.5 KB

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