parent
48f8bd8018
commit
d506d87487
@ -1,217 +0,0 @@ |
|||||||
package com.frank.ffmpeg.view; |
|
||||||
|
|
||||||
import android.content.Context; |
|
||||||
import android.graphics.SurfaceTexture; |
|
||||||
import android.text.TextUtils; |
|
||||||
import android.util.AttributeSet; |
|
||||||
import android.util.Log; |
|
||||||
import android.view.LayoutInflater; |
|
||||||
import android.view.Surface; |
|
||||||
import android.view.TextureView; |
|
||||||
import android.view.View; |
|
||||||
import android.widget.RelativeLayout; |
|
||||||
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; |
|
||||||
|
|
||||||
/** |
|
||||||
* the custom view of preview SeekBar |
|
||||||
* Created by frank on 2019/11/16. |
|
||||||
*/ |
|
||||||
|
|
||||||
public class VideoPreviewBar extends RelativeLayout implements HardwareDecode.OnDataCallback { |
|
||||||
|
|
||||||
private final static String TAG = VideoPreviewBar.class.getSimpleName(); |
|
||||||
|
|
||||||
private TextureView texturePreView; |
|
||||||
|
|
||||||
private SeekBar previewBar; |
|
||||||
|
|
||||||
private TextView txtVideoProgress; |
|
||||||
|
|
||||||
private TextView txtVideoDuration; |
|
||||||
|
|
||||||
private HardwareDecode hardwareDecode; |
|
||||||
|
|
||||||
private PreviewBarCallback mPreviewBarCallback; |
|
||||||
|
|
||||||
private int duration; |
|
||||||
|
|
||||||
private int screenWidth; |
|
||||||
|
|
||||||
private int moveEndPos = 0; |
|
||||||
|
|
||||||
private int previewHalfWidth; |
|
||||||
|
|
||||||
public VideoPreviewBar(Context context) { |
|
||||||
super(context); |
|
||||||
initView(context); |
|
||||||
} |
|
||||||
|
|
||||||
public VideoPreviewBar(Context context, AttributeSet attributeSet) { |
|
||||||
super(context, attributeSet); |
|
||||||
initView(context); |
|
||||||
} |
|
||||||
|
|
||||||
private void initView(Context context) { |
|
||||||
View view = LayoutInflater.from(context).inflate(R.layout.preview_video, this); |
|
||||||
previewBar = view.findViewById(R.id.preview_bar); |
|
||||||
texturePreView = view.findViewById(R.id.texture_preview); |
|
||||||
txtVideoProgress = view.findViewById(R.id.txt_video_progress); |
|
||||||
txtVideoDuration = view.findViewById(R.id.txt_video_duration); |
|
||||||
setListener(); |
|
||||||
screenWidth = ScreenUtil.INSTANCE.getScreenWidth(context); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
protected void onLayout(boolean changed, int l, int t, int r, int b) { |
|
||||||
super.onLayout(changed, l, t, r, b); |
|
||||||
if (moveEndPos == 0) { |
|
||||||
int previewWidth = texturePreView.getWidth(); |
|
||||||
previewHalfWidth = previewWidth / 2; |
|
||||||
int marginEnd = 0; |
|
||||||
MarginLayoutParams layoutParams = (MarginLayoutParams) texturePreView.getLayoutParams(); |
|
||||||
if (layoutParams != null) { |
|
||||||
marginEnd = layoutParams.getMarginEnd(); |
|
||||||
} |
|
||||||
moveEndPos = screenWidth - previewWidth - marginEnd; |
|
||||||
Log.i(TAG, "previewWidth=" + previewWidth); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
private void setPreviewCallback(final String filePath, TextureView texturePreView) { |
|
||||||
texturePreView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() { |
|
||||||
@Override |
|
||||||
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { |
|
||||||
doPreview(filePath, new Surface(surface)); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void onSurfaceTextureUpdated(SurfaceTexture surface) { |
|
||||||
|
|
||||||
} |
|
||||||
}); |
|
||||||
} |
|
||||||
|
|
||||||
private void doPreview(String filePath, Surface surface) { |
|
||||||
if (surface == null || TextUtils.isEmpty(filePath)) { |
|
||||||
return; |
|
||||||
} |
|
||||||
release(); |
|
||||||
hardwareDecode = new HardwareDecode(surface, filePath, this); |
|
||||||
hardwareDecode.decode(); |
|
||||||
} |
|
||||||
|
|
||||||
private void setListener() { |
|
||||||
previewBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { |
|
||||||
@Override |
|
||||||
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { |
|
||||||
if (!fromUser) { |
|
||||||
return; |
|
||||||
} |
|
||||||
previewBar.setProgress(progress); |
|
||||||
if (hardwareDecode != null && progress < duration) { |
|
||||||
// us to ms
|
|
||||||
hardwareDecode.seekTo(progress * 1000); |
|
||||||
} |
|
||||||
int percent = progress * screenWidth / duration; |
|
||||||
if (percent > previewHalfWidth && percent < moveEndPos && texturePreView != null) { |
|
||||||
texturePreView.setTranslationX(percent - previewHalfWidth); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void onStartTrackingTouch(SeekBar seekBar) { |
|
||||||
if (texturePreView != null) { |
|
||||||
texturePreView.setVisibility(VISIBLE); |
|
||||||
} |
|
||||||
if (hardwareDecode != null) { |
|
||||||
hardwareDecode.setPreviewing(true); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void onStopTrackingTouch(SeekBar seekBar) { |
|
||||||
if (texturePreView != null) { |
|
||||||
texturePreView.setVisibility(GONE); |
|
||||||
} |
|
||||||
if (mPreviewBarCallback != null) { |
|
||||||
mPreviewBarCallback.onStopTracking(seekBar.getProgress()); |
|
||||||
} |
|
||||||
if (hardwareDecode != null) { |
|
||||||
hardwareDecode.setPreviewing(false); |
|
||||||
} |
|
||||||
} |
|
||||||
}); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void onData(long duration) { |
|
||||||
//us to ms
|
|
||||||
final int durationMs = (int) (duration / 1000); |
|
||||||
Log.i(TAG, "duration=" + duration); |
|
||||||
this.duration = durationMs; |
|
||||||
post(new Runnable() { |
|
||||||
@Override |
|
||||||
public void run() { |
|
||||||
previewBar.setMax(durationMs); |
|
||||||
txtVideoDuration.setText(TimeUtil.INSTANCE.getVideoTime(durationMs)); |
|
||||||
texturePreView.setVisibility(GONE); |
|
||||||
} |
|
||||||
}); |
|
||||||
} |
|
||||||
|
|
||||||
private void checkArgument(String videoPath) { |
|
||||||
if (texturePreView == null) { |
|
||||||
throw new IllegalStateException("Must init TextureView first..."); |
|
||||||
} |
|
||||||
if (videoPath == null || videoPath.isEmpty()) { |
|
||||||
throw new IllegalStateException("videoPath is empty..."); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public void init(String videoPath, PreviewBarCallback previewBarCallback) { |
|
||||||
checkArgument(videoPath); |
|
||||||
this.mPreviewBarCallback = previewBarCallback; |
|
||||||
doPreview(videoPath, new Surface(texturePreView.getSurfaceTexture())); |
|
||||||
} |
|
||||||
|
|
||||||
public void initDefault(String videoPath, PreviewBarCallback previewBarCallback) { |
|
||||||
checkArgument(videoPath); |
|
||||||
this.mPreviewBarCallback = previewBarCallback; |
|
||||||
setPreviewCallback(videoPath, texturePreView); |
|
||||||
} |
|
||||||
|
|
||||||
public void updateProgress(int progress) { |
|
||||||
if (progress >= 0 && progress <= duration) { |
|
||||||
txtVideoProgress.setText(TimeUtil.INSTANCE.getVideoTime(progress)); |
|
||||||
previewBar.setProgress(progress); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public void release() { |
|
||||||
if (hardwareDecode != null) { |
|
||||||
hardwareDecode.release(); |
|
||||||
hardwareDecode = null; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public interface PreviewBarCallback { |
|
||||||
void onStopTracking(long progress); |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -0,0 +1,197 @@ |
|||||||
|
package com.frank.ffmpeg.view |
||||||
|
|
||||||
|
import android.content.Context |
||||||
|
import android.graphics.SurfaceTexture |
||||||
|
import android.text.TextUtils |
||||||
|
import android.util.AttributeSet |
||||||
|
import android.util.Log |
||||||
|
import android.view.* |
||||||
|
import android.widget.RelativeLayout |
||||||
|
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 |
||||||
|
|
||||||
|
/** |
||||||
|
* the custom view of preview SeekBar |
||||||
|
* Created by frank on 2019/11/16. |
||||||
|
*/ |
||||||
|
|
||||||
|
class VideoPreviewBar : RelativeLayout, HardwareDecode.OnDataCallback { |
||||||
|
|
||||||
|
private var texturePreView: TextureView? = null |
||||||
|
|
||||||
|
private var previewBar: SeekBar? = null |
||||||
|
|
||||||
|
private var txtVideoProgress: TextView? = null |
||||||
|
|
||||||
|
private var txtVideoDuration: TextView? = null |
||||||
|
|
||||||
|
private var hardwareDecode: HardwareDecode? = null |
||||||
|
|
||||||
|
private var mPreviewBarCallback: PreviewBarCallback? = null |
||||||
|
|
||||||
|
private var duration: Int = 0 |
||||||
|
|
||||||
|
private var screenWidth: Int = 0 |
||||||
|
|
||||||
|
private var moveEndPos = 0 |
||||||
|
|
||||||
|
private var previewHalfWidth: Int = 0 |
||||||
|
|
||||||
|
constructor(context: Context) : super(context) { |
||||||
|
initView(context) |
||||||
|
} |
||||||
|
|
||||||
|
constructor(context: Context, attributeSet: AttributeSet) : super(context, attributeSet) { |
||||||
|
initView(context) |
||||||
|
} |
||||||
|
|
||||||
|
private fun initView(context: Context) { |
||||||
|
val view = LayoutInflater.from(context).inflate(R.layout.preview_video, this) |
||||||
|
previewBar = view.findViewById(R.id.preview_bar) |
||||||
|
texturePreView = view.findViewById(R.id.texture_preview) |
||||||
|
txtVideoProgress = view.findViewById(R.id.txt_video_progress) |
||||||
|
txtVideoDuration = view.findViewById(R.id.txt_video_duration) |
||||||
|
setListener() |
||||||
|
screenWidth = ScreenUtil.getScreenWidth(context) |
||||||
|
} |
||||||
|
|
||||||
|
override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) { |
||||||
|
super.onLayout(changed, l, t, r, b) |
||||||
|
if (moveEndPos == 0) { |
||||||
|
val previewWidth = texturePreView!!.width |
||||||
|
previewHalfWidth = previewWidth / 2 |
||||||
|
val marginEnd: Int |
||||||
|
val layoutParams = texturePreView!!.layoutParams as MarginLayoutParams |
||||||
|
marginEnd = layoutParams.marginEnd |
||||||
|
moveEndPos = screenWidth - previewWidth - marginEnd |
||||||
|
Log.i(TAG, "previewWidth=$previewWidth") |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private fun setPreviewCallback(filePath: String, texturePreView: TextureView) { |
||||||
|
texturePreView.surfaceTextureListener = object : TextureView.SurfaceTextureListener { |
||||||
|
override fun onSurfaceTextureAvailable(surface: SurfaceTexture, width: Int, height: Int) { |
||||||
|
doPreview(filePath, Surface(surface)) |
||||||
|
} |
||||||
|
|
||||||
|
override fun onSurfaceTextureSizeChanged(surface: SurfaceTexture, width: Int, height: Int) { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
override fun onSurfaceTextureDestroyed(surface: SurfaceTexture): Boolean { |
||||||
|
return false |
||||||
|
} |
||||||
|
|
||||||
|
override fun onSurfaceTextureUpdated(surface: SurfaceTexture) { |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private fun doPreview(filePath: String, surface: Surface?) { |
||||||
|
if (surface == null || TextUtils.isEmpty(filePath)) { |
||||||
|
return |
||||||
|
} |
||||||
|
release() |
||||||
|
hardwareDecode = HardwareDecode(surface, filePath, this) |
||||||
|
hardwareDecode!!.decode() |
||||||
|
} |
||||||
|
|
||||||
|
private fun setListener() { |
||||||
|
previewBar!!.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener { |
||||||
|
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) { |
||||||
|
if (!fromUser) { |
||||||
|
return |
||||||
|
} |
||||||
|
previewBar!!.progress = progress |
||||||
|
if (hardwareDecode != null && progress < duration) { |
||||||
|
// us to ms |
||||||
|
hardwareDecode!!.seekTo((progress * 1000).toLong()) |
||||||
|
} |
||||||
|
val percent = progress * screenWidth / duration |
||||||
|
if (percent in (previewHalfWidth + 1) until moveEndPos && texturePreView != null) { |
||||||
|
texturePreView!!.translationX = (percent - previewHalfWidth).toFloat() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
override fun onStartTrackingTouch(seekBar: SeekBar) { |
||||||
|
if (texturePreView != null) { |
||||||
|
texturePreView!!.visibility = View.VISIBLE |
||||||
|
} |
||||||
|
if (hardwareDecode != null) { |
||||||
|
hardwareDecode!!.setPreviewing(true) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
override fun onStopTrackingTouch(seekBar: SeekBar) { |
||||||
|
if (texturePreView != null) { |
||||||
|
texturePreView!!.visibility = View.GONE |
||||||
|
} |
||||||
|
if (mPreviewBarCallback != null) { |
||||||
|
mPreviewBarCallback!!.onStopTracking(seekBar.progress.toLong()) |
||||||
|
} |
||||||
|
if (hardwareDecode != null) { |
||||||
|
hardwareDecode!!.setPreviewing(false) |
||||||
|
} |
||||||
|
} |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
override fun onData(duration: Long) { |
||||||
|
//us to ms |
||||||
|
val durationMs = (duration / 1000).toInt() |
||||||
|
Log.i(TAG, "duration=$duration") |
||||||
|
this.duration = durationMs |
||||||
|
post { |
||||||
|
previewBar!!.max = durationMs |
||||||
|
txtVideoDuration!!.text = TimeUtil.getVideoTime(durationMs.toLong()) |
||||||
|
texturePreView!!.visibility = View.GONE |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private fun checkArgument(videoPath: String?) { |
||||||
|
checkNotNull(texturePreView) { "Must init TextureView first..." } |
||||||
|
check(!(videoPath == null || videoPath.isEmpty())) { "videoPath is empty..." } |
||||||
|
} |
||||||
|
|
||||||
|
fun init(videoPath: String, previewBarCallback: PreviewBarCallback) { |
||||||
|
checkArgument(videoPath) |
||||||
|
this.mPreviewBarCallback = previewBarCallback |
||||||
|
doPreview(videoPath, Surface(texturePreView!!.surfaceTexture)) |
||||||
|
} |
||||||
|
|
||||||
|
fun initDefault(videoPath: String, previewBarCallback: PreviewBarCallback) { |
||||||
|
checkArgument(videoPath) |
||||||
|
this.mPreviewBarCallback = previewBarCallback |
||||||
|
setPreviewCallback(videoPath, texturePreView!!) |
||||||
|
} |
||||||
|
|
||||||
|
fun updateProgress(progress: Int) { |
||||||
|
if (progress in 0..duration) { |
||||||
|
txtVideoProgress!!.text = TimeUtil.getVideoTime(progress.toLong()) |
||||||
|
previewBar!!.progress = progress |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fun release() { |
||||||
|
if (hardwareDecode != null) { |
||||||
|
hardwareDecode!!.release() |
||||||
|
hardwareDecode = null |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
interface PreviewBarCallback { |
||||||
|
fun onStopTracking(progress: Long) |
||||||
|
} |
||||||
|
|
||||||
|
companion object { |
||||||
|
|
||||||
|
private val TAG = VideoPreviewBar::class.java.simpleName |
||||||
|
} |
||||||
|
|
||||||
|
} |
Loading…
Reference in new issue