| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326 |
- package top.kikt.ijkplayer
- /// create 2019/3/7 by cai
- import android.content.Context
- import android.graphics.Bitmap
- import android.media.AudioManager
- import android.net.Uri
- import android.util.Base64
- import io.flutter.plugin.common.MethodChannel
- import io.flutter.plugin.common.PluginRegistry
- import top.kikt.ijkplayer.entity.IjkOption
- import top.kikt.ijkplayer.entity.Info
- import tv.danmaku.ijk.media.player.IjkMediaPlayer
- import tv.danmaku.ijk.media.player.TextureMediaPlayer
- import java.io.ByteArrayOutputStream
- import java.io.File
- class Ijk(private val registry: PluginRegistry.Registrar, private val options: Map<String, Any>) {
- private val textureEntry = registry.textures().createSurfaceTexture()
- val id: Long
- get() = textureEntry.id()
- val mediaPlayer: IjkMediaPlayer = IjkMediaPlayer()
- private val textureMediaPlayer: TextureMediaPlayer
- private val methodChannel: MethodChannel = MethodChannel(registry.messenger(), "top.kikt/ijkplayer/$id")
- private val notifyChannel: NotifyChannel = NotifyChannel(registry, id, this)
- var degree = 0
- var isDisposed = false
- init {
- textureMediaPlayer = TextureMediaPlayer(mediaPlayer)
- configOptions()
- textureMediaPlayer.surfaceTexture = textureEntry.surfaceTexture()
- methodChannel.setMethodCallHandler { call, result ->
- if (isDisposed) {
- return@setMethodCallHandler
- }
- when (call?.method) {
- "setNetworkDataSource" -> {
- val uri = call.argument<String>("uri")
- val params = call.argument<Map<String, String>>("headers")
- if (uri == null) {
- handleSetUriResult(Exception("uri是必传参数"), result)
- return@setMethodCallHandler
- }
- setUri(uri, params) { throwable ->
- handleSetUriResult(throwable, result)
- }
- }
- "setAssetDataSource" -> {
- val name = call.argument<String>("name")
- val `package` = call.argument<String>("package")
- if (name != null) {
- setAssetUri(name, `package`) { throwable ->
- handleSetUriResult(throwable, result)
- }
- } else {
- handleSetUriResult(Exception("没有找到资源"), result)
- }
- }
- "setFileDataSource" -> {
- val path = call.argument<String>("path")
- if (path != null) {
- setUri("file://$path", hashMapOf()) { throwable ->
- handleSetUriResult(throwable, result)
- }
- }
- }
- "play" -> {
- play()
- result?.success(true)
- }
- "pause" -> {
- pause()
- result?.success(true)
- }
- "stop" -> {
- stop()
- result?.success(true)
- }
- "getInfo" -> {
- val info = getInfo()
- result?.success(info.toMap())
- }
- "seekTo" -> {
- val target = call.argument<Double>("target")
- if (target != null) {
- seekTo((target * 1000).toLong())
- }
- result?.success(true)
- }
- "setVolume" -> {
- val volume = call.argument<Int>("volume")
- setVolume(volume)
- result?.success(true)
- }
- "getVolume" -> {
- // result?.success(this.mediaPlayer.setVolume())
- }
- "screenShot" -> {
- val bytes = screenShot()
- result?.success(bytes)
- }
- "setSpeed" -> {
- val speed = call.arguments<Double>()
- mediaPlayer.setSpeed(speed.toFloat())
- }
- else -> {
- result?.notImplemented()
- }
- }
- }
- }
- private val appContext: Context
- get() = registry.activity().application
- private fun configOptions() {
- // see https://www.jianshu.com/p/843c86a9e9ad
- mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "fflags", "fastseek")
- // 以下注释有选项会导致m3u8类型无声音
- // mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "analyzemaxduration", 100L)
- // mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "analyzeduration", 1)
- // mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "probesize", 1024 * 10)
- // mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "flush_packets", 1L)
- mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "reconnect", 5)
- mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "framedrop", 5)
- mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "enable-accurate-seek", 1)
- mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 1) // 开硬解
- mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "packet-buffering", 1)
- // mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "max-buffer-size", maxCacheSize)
- // mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "packet-buffering", if (isBufferCache) 1 else 0)
- fun setOptionToMediaPlayer(option: IjkOption) {
- if (option.isInit.not()) {
- return
- }
- val category = when (option.type) {
- IjkOption.Type.Format -> IjkMediaPlayer.OPT_CATEGORY_FORMAT
- IjkOption.Type.Player -> IjkMediaPlayer.OPT_CATEGORY_PLAYER
- IjkOption.Type.Sws -> IjkMediaPlayer.OPT_CATEGORY_SWS
- IjkOption.Type.Codec -> IjkMediaPlayer.OPT_CATEGORY_CODEC
- else -> -1
- }
- if (category == -1) {
- return
- }
- val value = option.value
- when (value) {
- is Int -> mediaPlayer.setOption(category, option.key, value.toLong())
- is String -> mediaPlayer.setOption(category, option.key, value)
- is Long -> mediaPlayer.setOption(category, option.key, value)
- }
- }
- val options = options["options"]
- if (options is List<*>) {
- for (option in options) {
- if (option is Map<*, *>) {
- val ijkOptions = IjkOption(option)
- setOptionToMediaPlayer(ijkOptions)
- }
- }
- }
- }
- private fun screenShot(): ByteArray? {
- val frameBitmap = mediaPlayer.frameBitmap
- return if (frameBitmap != null) {
- val outputStream = ByteArrayOutputStream()
- frameBitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream)
- frameBitmap.recycle()
- outputStream.toByteArray()
- } else {
- null
- }
- }
- fun getInfo(): Info {
- val duration = mediaPlayer.duration
- val currentPosition = mediaPlayer.currentPosition
- val width = mediaPlayer.videoWidth
- val height = mediaPlayer.videoHeight
- val outputFps = mediaPlayer.videoOutputFramesPerSecond
- // mediaPlayer.mediaInfo.mAudioDecoder
- // mediaPlayer.mediaInfo.mVideoDecoder
- return Info(
- duration = duration.toDouble() / 1000,
- currentPosition = currentPosition.toDouble() / 1000,
- width = width,
- height = height,
- isPlaying = textureMediaPlayer.isPlaying,
- degree = degree,
- tcpSpeed = mediaPlayer.tcpSpeed,
- outputFps = outputFps
- // decodeFps = mediaPlayer.videoDecodeFramesPerSecond
- )
- }
- private fun handleSetUriResult(throwable: Throwable?, result: MethodChannel.Result?) {
- if (throwable == null) {
- result?.success(true)
- } else {
- throwable.printStackTrace()
- result?.error("1", "set resource error", throwable)
- }
- }
- private fun setUri(uriString: String, headers: Map<String, String>?, callback: (Throwable?) -> Unit) {
- try {
- val uri = Uri.parse(uriString)
- // val scheme = uri.scheme
- // if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
- // (TextUtils.isEmpty(scheme) || scheme.equals("file", ignoreCase = true))) {
- // val dataSource = FileMediaDataSource(File(uri.toString()))
- // mediaPlayer.setDataSource(dataSource)
- // } else {
- mediaPlayer.setDataSource(appContext, uri, headers)
- // }
- mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC)
- mediaPlayer.prepareAsync()
- callback(null)
- } catch (e: Exception) {
- e.printStackTrace()
- callback(e)
- }
- }
- private fun setAssetUri(name: String, `package`: String?, callback: (Throwable?) -> Unit) {
- try {
- mediaPlayer.setOnPreparedListener {
- callback(null)
- }
- val asset =
- if (`package` == null) {
- registry.lookupKeyForAsset(name)
- } else {
- registry.lookupKeyForAsset(name, `package`)
- }
- val assetManager = registry.context().assets
- val input = assetManager.open(asset)
- val cacheDir = registry.context().cacheDir.absoluteFile.path
- val fileName = Base64.encodeToString(asset.toByteArray(), Base64.DEFAULT)
- val file = File(cacheDir, fileName)
- file.outputStream().use { outputStream ->
- input.use { inputStream ->
- inputStream.copyTo(outputStream)
- }
- }
- mediaPlayer.setDataSource(FileMediaDataSource(file))
- mediaPlayer.prepareAsync()
- } catch (e: Exception) {
- e.printStackTrace()
- callback(e)
- }
- }
- fun dispose() {
- if (isDisposed) {
- return
- }
- isDisposed = true
- tryCatchError {
- notifyChannel.dispose()
- methodChannel.setMethodCallHandler(null)
- textureMediaPlayer.stop()
- }
- tryCatchError {
- textureMediaPlayer.release()
- }
- tryCatchError {
- textureEntry.release()
- }
- }
- private fun tryCatchError(runnable: () -> Unit) {
- try {
- runnable()
- } catch (e: Exception) {
- e.printStackTrace()
- }
- }
- private fun play() {
- try {
- mediaPlayer.start()
- } catch (e: Exception) {
- e.printStackTrace()
- }
- }
- private fun pause() {
- textureMediaPlayer.pause()
- }
- private fun stop() {
- textureMediaPlayer.stop()
- }
- private fun seekTo(msec: Long) {
- textureMediaPlayer.seekTo(msec)
- }
- private fun setVolume(volume: Int?) {
- if (volume == null) {
- return
- }
- val v = volume.toFloat() / 100
- mediaPlayer.setVolume(v, v)
- }
- }
|