From 83b933838656b8a6ffac4ff54dbaae1044bfafc0 Mon Sep 17 00:00:00 2001 From: xufuji456 Date: Fri, 18 Mar 2022 16:36:38 +0800 Subject: [PATCH] Feature: add MediaDecodeController --- .../controller/MediaDecodeController.kt | 81 ++++++++++++++++--- ...erController.kt => MediaPlayController.kt} | 75 ++++++++++++----- .../ffmpeg/activity/VideoPreviewActivity.kt | 18 ++--- .../com/frank/ffmpeg/view/VideoPreviewBar.kt | 28 +++---- 4 files changed, 152 insertions(+), 50 deletions(-) rename app/src/main/java/com/frank/ffmpeg/hardware/HardwareDecode.kt => AndroidMedia/src/main/java/com/frank/androidmedia/controller/MediaDecodeController.kt (73%) rename AndroidMedia/src/main/java/com/frank/androidmedia/controller/{MediaPlayerController.kt => MediaPlayController.kt} (52%) diff --git a/app/src/main/java/com/frank/ffmpeg/hardware/HardwareDecode.kt b/AndroidMedia/src/main/java/com/frank/androidmedia/controller/MediaDecodeController.kt similarity index 73% rename from app/src/main/java/com/frank/ffmpeg/hardware/HardwareDecode.kt rename to AndroidMedia/src/main/java/com/frank/androidmedia/controller/MediaDecodeController.kt index d6991d5..b194c2d 100644 --- a/app/src/main/java/com/frank/ffmpeg/hardware/HardwareDecode.kt +++ b/AndroidMedia/src/main/java/com/frank/androidmedia/controller/MediaDecodeController.kt @@ -1,4 +1,4 @@ -package com.frank.ffmpeg.hardware +package com.frank.androidmedia.controller import android.media.MediaCodec import android.media.MediaExtractor @@ -8,11 +8,74 @@ import android.util.Log import android.view.Surface /** - * Extract by MediaExtractor, decode by MediaCodec, and render to Surface - * Created by frank on 2019/11/16. + * The controller of MediaExtractor and MediaCodec. + * Use MediaExtractor to extract data of media tracks. + * And use MediaCodec to decode video frame from extractor. + * + * @author frank + * @date 2022/3/18 */ -class HardwareDecode(private val mSurface: Surface, private val mFilePath: String, private val mCallback: OnDataCallback?) { +open class MediaDecodeController(val mSurface: Surface, val mFilePath: String, val mCallback: OnDataCallback?) { + + /** + * + * MediaExtractor extractor = new MediaExtractor(); + * extractor.setDataSource(...); + * int numTracks = extractor.getTrackCount(); + * for (int i = 0; i < numTracks; ++i) { + * MediaFormat format = extractor.getTrackFormat(i); + * String mime = format.getString(MediaFormat.KEY_MIME); + * if (weAreInterestedInThisTrack) { + * extractor.selectTrack(i); + * } + * } + * ByteBuffer inputBuffer = ByteBuffer.allocate(...) + * while (extractor.readSampleData(inputBuffer, ...) != 0) { + * int trackIndex = extractor.getSampleTrackIndex(); + * long presentationTimeUs = extractor.getSampleTime(); + * ... + * extractor.advance(); + * } + * extractor.release(); + * extractor = null; + */ + +/* + // MediaCodec is typically used like this in asynchronous mode: + MediaCodec codec = MediaCodec.createByCodecName(name); + MediaFormat mOutputFormat; + codec.setCallback(new MediaCodec.Callback() { + @Override + void onInputBufferAvailable(MediaCodec mc, int inputBufferId) { + ByteBuffer inputBuffer = codec.getInputBuffer(inputBufferId); + codec.queueInputBuffer(inputBufferId, …); + } + + @Override + void onOutputBufferAvailable(MediaCodec mc, int outputBufferId, …) { + ByteBuffer outputBuffer = codec.getOutputBuffer(outputBufferId); + MediaFormat bufferFormat = codec.getOutputFormat(outputBufferId); + codec.releaseOutputBuffer(outputBufferId, …); + } + + @Override + void onOutputFormatChanged(MediaCodec mc, MediaFormat format) { + mOutputFormat = format; + } + + @Override + void onError(…) { + + } + }); + codec.configure(format, …); + mOutputFormat = codec.getOutputFormat(); + codec.start(); + // wait for processing to complete + codec.stop(); + codec.release(); +*/ private var videoDecodeThread: VideoDecodeThread? = null @@ -53,11 +116,11 @@ class HardwareDecode(private val mSurface: Surface, private val mFilePath: Strin private var isPreviewing: Boolean = false - internal fun setPreviewing(previewing: Boolean) { + fun setPreviewing(previewing: Boolean) { this.isPreviewing = previewing } - internal fun seekTo(seekPosition: Long) { + fun seekTo(seekPosition: Long) { try { if (mediaExtractor != null) { mediaExtractor!!.seekTo(seekPosition, MediaExtractor.SEEK_TO_CLOSEST_SYNC) @@ -68,7 +131,7 @@ class HardwareDecode(private val mSurface: Surface, private val mFilePath: Strin } - internal fun release() { + fun release() { try { if (mediaCodec != null) { mediaCodec!!.stop() @@ -187,7 +250,7 @@ class HardwareDecode(private val mSurface: Surface, private val mFilePath: Strin companion object { - private val TAG = HardwareDecode::class.java.simpleName + private val TAG = MediaPlayController::class.java.simpleName private const val DEQUEUE_TIME = (10 * 1000).toLong() private const val SLEEP_TIME = 10 @@ -197,4 +260,4 @@ class HardwareDecode(private val mSurface: Surface, private val mFilePath: Strin private const val RATIO_240 = 240 } -} +} \ No newline at end of file diff --git a/AndroidMedia/src/main/java/com/frank/androidmedia/controller/MediaPlayerController.kt b/AndroidMedia/src/main/java/com/frank/androidmedia/controller/MediaPlayController.kt similarity index 52% rename from AndroidMedia/src/main/java/com/frank/androidmedia/controller/MediaPlayerController.kt rename to AndroidMedia/src/main/java/com/frank/androidmedia/controller/MediaPlayController.kt index b384f85..938e54f 100644 --- a/AndroidMedia/src/main/java/com/frank/androidmedia/controller/MediaPlayerController.kt +++ b/AndroidMedia/src/main/java/com/frank/androidmedia/controller/MediaPlayController.kt @@ -1,16 +1,18 @@ package com.frank.androidmedia.controller import android.media.MediaPlayer +import android.media.PlaybackParams import android.view.Surface import com.frank.androidmedia.listener.PlayerCallback import java.io.IOException /** - * @author xufulong - * @date 3/18/22 1:53 PM - * @desc The controller of MediaPlayer + * The controller of MediaPlayer + * + * @author frank + * @date 2022/3/18 */ -open class MediaPlayerController(playerCallback: PlayerCallback) { +open class MediaPlayController(playerCallback: PlayerCallback) { private var mediaPlayer: MediaPlayer? = null private var renderFirstFrame: Boolean = false @@ -20,7 +22,7 @@ open class MediaPlayerController(playerCallback: PlayerCallback) { this.playerCallback = playerCallback } - fun initPlayer(filePath :String, surface :Surface) { + fun initPlayer(filePath: String, surface: Surface) { if (mediaPlayer != null) { releasePlayer() } @@ -31,21 +33,20 @@ open class MediaPlayerController(playerCallback: PlayerCallback) { mediaPlayer!!.start() playerCallback?.onPrepare() } - mediaPlayer!!.setOnErrorListener { - mp: MediaPlayer?, what: Int, extra: Int -> + mediaPlayer!!.setOnErrorListener { mp: MediaPlayer?, what: Int, extra: Int -> return@setOnErrorListener playerCallback?.onError(what, extra)!! } mediaPlayer!!.setOnCompletionListener { playerCallback?.onCompleteListener() } - mediaPlayer!!.setOnInfoListener { - mp, what, extra -> ( - if (what == MediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START) { - if (!renderFirstFrame) { - renderFirstFrame = true - playerCallback?.onRenderFirstFrame() - } - }) + mediaPlayer!!.setOnInfoListener { mp, what, extra -> + ( + if (what == MediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START) { + if (!renderFirstFrame) { + renderFirstFrame = true + playerCallback?.onRenderFirstFrame() + } + }) return@setOnInfoListener true } mediaPlayer!!.setDataSource(filePath) @@ -56,19 +57,19 @@ open class MediaPlayerController(playerCallback: PlayerCallback) { } } - fun currentPosition() : Int { + fun currentPosition(): Int { if (mediaPlayer == null) return 0 return mediaPlayer!!.currentPosition } - fun duration() : Int { + fun duration(): Int { if (mediaPlayer == null) return 0 return mediaPlayer!!.duration } - fun seekTo(position :Int) { + fun seekTo(position: Int) { mediaPlayer?.seekTo(position) } @@ -83,6 +84,44 @@ open class MediaPlayerController(playerCallback: PlayerCallback) { } } + fun getVideoWidth(): Int { + return mediaPlayer!!.videoWidth + } + + fun getVideoHeight(): Int { + return mediaPlayer!!.videoHeight + } + + fun mute() { + mediaPlayer?.setVolume(0.0f, 0.0f) + } + + fun setVolume(volume: Float) { + if (volume < 0 || volume > 1) + return + mediaPlayer?.setVolume(volume, volume) + } + + /** + * Set playback rate + */ + fun setSpeed(speed: Float) { + if (speed <= 0 || speed > 8) + return + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { + val params = PlaybackParams() + params.speed = speed + mediaPlayer?.playbackParams = params + } + } + + /** + * Select audio or subtitle track, when there are multi tracks + */ + fun selectTrack(trackId: Int) { + mediaPlayer?.selectTrack(trackId) + } + fun releasePlayer() { if (mediaPlayer != null) { mediaPlayer!!.stop() diff --git a/app/src/main/java/com/frank/ffmpeg/activity/VideoPreviewActivity.kt b/app/src/main/java/com/frank/ffmpeg/activity/VideoPreviewActivity.kt index aa7559f..151242f 100644 --- a/app/src/main/java/com/frank/ffmpeg/activity/VideoPreviewActivity.kt +++ b/app/src/main/java/com/frank/ffmpeg/activity/VideoPreviewActivity.kt @@ -10,7 +10,7 @@ import android.view.Surface import android.view.SurfaceHolder import android.view.SurfaceView import android.view.View -import com.frank.androidmedia.controller.MediaPlayerController +import com.frank.androidmedia.controller.MediaPlayController import com.frank.androidmedia.listener.PlayerCallback import com.frank.ffmpeg.R @@ -25,7 +25,7 @@ import com.frank.ffmpeg.handler.FFmpegHandler.MSG_TOAST class VideoPreviewActivity : BaseActivity(), VideoPreviewBar.PreviewBarCallback, PlayerCallback { - private var playerController: MediaPlayerController? = null + private var playController: MediaPlayController? = null private var surfaceVideo: SurfaceView? = null private var videoPreviewBar: VideoPreviewBar? = null @@ -34,8 +34,8 @@ class VideoPreviewActivity : BaseActivity(), VideoPreviewBar.PreviewBarCallback, override fun handleMessage(msg: Message) { super.handleMessage(msg) if (msg.what == MSG_UPDATE) { - if (videoPreviewBar != null && playerController != null) { - videoPreviewBar!!.updateProgress(playerController!!.currentPosition()) + if (videoPreviewBar != null && playController != null) { + videoPreviewBar!!.updateProgress(playController!!.currentPosition()) } this.sendEmptyMessageDelayed(MSG_UPDATE, TIME_UPDATE.toLong()) } else if (msg.what == MSG_TOAST) { @@ -52,7 +52,7 @@ class VideoPreviewActivity : BaseActivity(), VideoPreviewBar.PreviewBarCallback, initView() mHandler.sendEmptyMessageDelayed(MSG_TOAST, 500) - playerController = MediaPlayerController(this) + playController = MediaPlayController(this) } private fun initView() { @@ -80,7 +80,7 @@ class VideoPreviewActivity : BaseActivity(), VideoPreviewBar.PreviewBarCallback, if (surface == null || TextUtils.isEmpty(filePath)) { return } - playerController?.initPlayer(filePath, surface) + playController?.initPlayer(filePath, surface) } override fun onViewClick(view: View) { @@ -93,14 +93,14 @@ class VideoPreviewActivity : BaseActivity(), VideoPreviewBar.PreviewBarCallback, } override fun onStopTracking(progress: Long) { - if (playerController != null) { - playerController!!.seekTo(progress.toInt()) + if (playController != null) { + playController!!.seekTo(progress.toInt()) } } override fun onDestroy() { super.onDestroy() - playerController?.releasePlayer() + playController?.releasePlayer() if (videoPreviewBar != null) { videoPreviewBar!!.release() } diff --git a/app/src/main/java/com/frank/ffmpeg/view/VideoPreviewBar.kt b/app/src/main/java/com/frank/ffmpeg/view/VideoPreviewBar.kt index d648ce6..f33317c 100644 --- a/app/src/main/java/com/frank/ffmpeg/view/VideoPreviewBar.kt +++ b/app/src/main/java/com/frank/ffmpeg/view/VideoPreviewBar.kt @@ -11,16 +11,16 @@ import android.widget.SeekBar import android.widget.TextView import com.frank.ffmpeg.R -import com.frank.ffmpeg.hardware.HardwareDecode import com.frank.ffmpeg.util.ScreenUtil import com.frank.ffmpeg.util.TimeUtil +import com.frank.androidmedia.controller.MediaDecodeController /** * the custom view of preview SeekBar * Created by frank on 2019/11/16. */ -class VideoPreviewBar : RelativeLayout, HardwareDecode.OnDataCallback { +class VideoPreviewBar : RelativeLayout, MediaDecodeController.OnDataCallback { private var texturePreView: TextureView? = null @@ -30,7 +30,7 @@ class VideoPreviewBar : RelativeLayout, HardwareDecode.OnDataCallback { private var txtVideoDuration: TextView? = null - private var hardwareDecode: HardwareDecode? = null + private var decodeController: MediaDecodeController? = null private var mPreviewBarCallback: PreviewBarCallback? = null @@ -98,8 +98,8 @@ class VideoPreviewBar : RelativeLayout, HardwareDecode.OnDataCallback { return } release() - hardwareDecode = HardwareDecode(surface, filePath, this) - hardwareDecode!!.decode() + decodeController = MediaDecodeController(surface, filePath, this) + decodeController!!.decode() } private fun setListener() { @@ -109,9 +109,9 @@ class VideoPreviewBar : RelativeLayout, HardwareDecode.OnDataCallback { return } previewBar!!.progress = progress - if (hardwareDecode != null && progress < duration) { + if (decodeController != null && progress < duration) { // us to ms - hardwareDecode!!.seekTo((progress * 1000).toLong()) + decodeController!!.seekTo((progress * 1000).toLong()) } val percent = progress * screenWidth / duration if (percent in (previewHalfWidth + 1) until moveEndPos && texturePreView != null) { @@ -123,8 +123,8 @@ class VideoPreviewBar : RelativeLayout, HardwareDecode.OnDataCallback { if (texturePreView != null) { texturePreView!!.visibility = View.VISIBLE } - if (hardwareDecode != null) { - hardwareDecode!!.setPreviewing(true) + if (decodeController != null) { + decodeController!!.setPreviewing(true) } } @@ -135,8 +135,8 @@ class VideoPreviewBar : RelativeLayout, HardwareDecode.OnDataCallback { if (mPreviewBarCallback != null) { mPreviewBarCallback!!.onStopTracking(seekBar.progress.toLong()) } - if (hardwareDecode != null) { - hardwareDecode!!.setPreviewing(false) + if (decodeController != null) { + decodeController!!.setPreviewing(false) } } }) @@ -179,9 +179,9 @@ class VideoPreviewBar : RelativeLayout, HardwareDecode.OnDataCallback { } fun release() { - if (hardwareDecode != null) { - hardwareDecode!!.release() - hardwareDecode = null + if (decodeController != null) { + decodeController!!.release() + decodeController = null } }