Feature: add MediaDecodeController

pull/209/head
xufuji456 3 years ago
parent aaabe133e8
commit 83b9338386
  1. 79
      AndroidMedia/src/main/java/com/frank/androidmedia/controller/MediaDecodeController.kt
  2. 75
      AndroidMedia/src/main/java/com/frank/androidmedia/controller/MediaPlayController.kt
  3. 18
      app/src/main/java/com/frank/ffmpeg/activity/VideoPreviewActivity.kt
  4. 28
      app/src/main/java/com/frank/ffmpeg/view/VideoPreviewBar.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

@ -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()

@ -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()
}

@ -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
}
}

Loading…
Cancel
Save