Kotlin user - You can use MediaPlayer with SurfaceView like following example. Just create VideoLayout
class with following codes.
class VideoLayout(
context: Context, attrs: AttributeSet?
) : FrameLayout(context, attrs), SurfaceTextureListener {
private var isLoop: Boolean
private var isSound: Boolean
private var videoAlign: Int
private var videoScale: Int
private var mVideoWidth = 0f
private var mVideoHeight = 0f
private var fileName: String? = null
private var fileFormat: FileFormat = FILE
private var videoSurface: TextureView? = null
private var mediaPlayer: MediaPlayer? = null
enum class Scale {
ORIGINAL, CROP, STRETCH, FIT_SCREEN
}
enum class FileFormat {
FILE, URI, URL, OTHER
}
enum class Align {
TOP, TOP_LEFT, TOP_RIGHT, CENTER, CENTER_LEFT,
CENTER_RIGHT, BOTTOM, BOTTOM_LEFT, BOTTOM_RIGHT
}
init {
val styledAttributes = context.theme.obtainStyledAttributes(
attrs, R.styleable.VideoLayout, 0, 0
)
fileName = styledAttributes.getString(R.styleable.VideoLayout_path_or_url)
videoScale = styledAttributes.getInteger(R.styleable.VideoLayout_scaleType, 3)
videoAlign = styledAttributes.getInteger(R.styleable.VideoLayout_align, 4)
isLoop = styledAttributes.getBoolean(R.styleable.VideoLayout_loop, true)
isSound = styledAttributes.getBoolean(R.styleable.VideoLayout_sound, false)
styledAttributes.recycle()
fileName?.let {
setupView()
it.setFileFormat()
if (videoScale != 2) {
calculateVideoSize()
surfaceSetup()
}
}
}
private fun setupView() {
videoSurface = TextureView(context)
addView(videoSurface)
videoSurface?.surfaceTextureListener = this
}
private fun String.setFileFormat() {
fileFormat = if (contains("http://") || contains("https://")) {
URL
} else if (contains("content://")) {
URI
} else if(contains("file://")) {
FILE
} else {
OTHER
}
}
private fun calculateVideoSize() {
try {
val metaRetriever = MediaMetadataRetriever()
fileName?.let {
if (fileFormat != OTHER) {
when(fileFormat) {
FILE -> {
val fName = it.substringAfterLast('/')
val file = File(context.getExternalFilesDir(
Environment.DIRECTORY_DOWNLOADS), fName
)
Timber.d("$fName file exists: ${file.exists()}")
val fd = FileInputStream(file).fd
metaRetriever.setDataSource(fd)
}
URI -> {
val uri = it.toUri()
metaRetriever.setDataSource(context, uri)
}
else -> metaRetriever.setDataSource(it, HashMap())
}
} else {
val afd = context.assets.openFd(it)
metaRetriever.setDataSource(
afd.fileDescriptor, afd.startOffset, afd.length
)
}
}
metaRetriever.extractMetadata(METADATA_KEY_VIDEO_HEIGHT)?.let {
mVideoHeight = it.toFloat()
}
metaRetriever.extractMetadata(METADATA_KEY_VIDEO_WIDTH)?.let {
mVideoWidth = it.toFloat()
}
metaRetriever.release()
} catch (e: IOException) {
e.printStackTrace()
} catch (e: NumberFormatException) {
e.printStackTrace()
}
}
private fun updateTextureViewSize(viewWidth: Int, viewHeight: Int) {
var scaleX = 1.0f
var scaleY = 1.0f
val viewWidthFloat = viewWidth.toFloat()
val viewHeightFloat = viewHeight.toFloat()
val viewWidthFactor = viewWidthFloat / viewHeightFloat
val videoWidthFactor = mVideoWidth / mVideoHeight
when (videoScale) {
0 -> { //original
scaleX = mVideoWidth / viewWidthFloat
scaleY = mVideoHeight / viewHeightFloat
}
1 -> { // crop
if (viewWidthFactor > videoWidthFactor) {
scaleY = ((viewWidthFloat / mVideoWidth) * mVideoHeight) / viewHeightFloat
} else {
scaleX = ((viewHeightFloat / mVideoHeight) * mVideoWidth) / viewWidthFloat
}
}
3 -> { // fit-screen
if (viewWidthFactor > videoWidthFactor) {
scaleX = ((viewHeightFloat / mVideoHeight) * mVideoWidth) / viewWidthFloat
} else {
scaleY = ((viewWidthFloat / mVideoWidth) * mVideoHeight) / viewHeightFloat
}
}
}
var pivotPointX = 0f
var pivotPointY = 0f
when (videoAlign) {
0 -> {
pivotPointX = viewWidthFloat / 2f
pivotPointY = 0f
}
1 -> {
pivotPointX = 0f
pivotPointY = 0f
}
2 -> {
pivotPointX = viewWidthFloat
pivotPointY = 0f
}
3 -> {
pivotPointX = viewWidthFloat / 2f
pivotPointY = viewHeightFloat / 2f
}
4 -> {
pivotPointX = 0f
pivotPointY = viewHeightFloat / 2f
}
5 -> {
pivotPointX = viewWidthFloat
pivotPointY = viewHeightFloat / 2f
}
6 -> {
pivotPointX = viewWidthFloat / 2f
pivotPointY = viewHeightFloat
}
7 -> {
pivotPointX = 0f
pivotPointY = viewHeightFloat
}
8 -> {
pivotPointX = viewWidthFloat
pivotPointY = viewHeightFloat
}
}
val matrix = Matrix().apply {
setScale(scaleX, scaleY, pivotPointX, pivotPointY)
}
videoSurface?.setTransform(matrix)
videoSurface?.layoutParams = LayoutParams(viewWidth, viewHeight)
}
private fun surfaceSetup() {
val screenHeight = resources.displayMetrics.heightPixels
val screenWidth = resources.displayMetrics.widthPixels
updateTextureViewSize(screenWidth, screenHeight)
}
private fun surfaceAvailableWorkers(surfaceTexture: SurfaceTexture) {
val surface = Surface(surfaceTexture)
try {
mediaPlayer = MediaPlayer()
mediaPlayer?.let { player ->
fileName?.let {
if (fileFormat != OTHER) {
when(fileFormat) {
FILE -> {
val fName = it.substringAfterLast('/')
val file = File(context.getExternalFilesDir(
Environment.DIRECTORY_DOWNLOADS), fName
)
Timber.d("$fName file exists: ${file.exists()}")
val fd = FileInputStream(file).fd
player.setDataSource(fd)
}
URI -> {
val uri = it.toUri()
player.setDataSource(context, uri)
}
else -> player.setDataSource(fileName)
}
} else {
val afd = context.assets.openFd(it)
player.setDataSource(afd.fileDescriptor, afd.startOffset, afd.length)
}
if (!isSound) player.setVolume(0f, 0f)
player.setSurface(surface)
player.isLooping = isLoop
player.prepareAsync()
player.setOnPreparedListener { obj: MediaPlayer -> obj.start() }
}
}
} catch (ignored: IllegalArgumentException) {
} catch (ignored: SecurityException) {
} catch (ignored: IllegalStateException) {
} catch (ignored: IOException) {
}
}
private fun changeVideo() {
try {
onDestroyVideoLayout()
mediaPlayer = MediaPlayer()
mediaPlayer?.let { player ->
fileName?.let {
if (fileFormat != OTHER) {
when(fileFormat) {
FILE -> {
val fName = it.substringAfterLast('/')
val file = File(context.getExternalFilesDir(
Environment.DIRECTORY_DOWNLOADS), fName
)
Timber.d("$fName file exists: ${file.exists()}")
val fd = FileInputStream(file).fd
player.setDataSource(fd)
}
URI -> {
val uri = it.toUri()
player.setDataSource(context, uri)
}
else -> player.setDataSource(fileName)
}
} else {
val afd = context.assets.openFd(it)
player.setDataSource(afd.fileDescriptor, afd.startOffset, afd.length)
}
if (!isSound) player.setVolume(0f, 0f)
player.isLooping = isLoop
player.setSurface(Surface(videoSurface?.surfaceTexture))
player.prepareAsync()
player.setOnPreparedListener { obj: MediaPlayer -> obj.start() }
}
}
} catch (ignored: IllegalArgumentException) {
} catch (ignored: IOException) {
} catch (ignored: IllegalStateException) {
} catch (ignored: SecurityException) {
}
}
override fun onSurfaceTextureAvailable(surface: SurfaceTexture, width: Int, height: Int) {
surfaceAvailableWorkers(surface)
}
override fun onSurfaceTextureSizeChanged(surface: SurfaceTexture, width: Int, height: Int) {}
override fun onSurfaceTextureDestroyed(surface: SurfaceTexture): Boolean = false
override fun onSurfaceTextureUpdated(surface: SurfaceTexture) {}
fun onDestroyVideoLayout() {
mediaPlayer?.let {
try {
it.stop()
it.release()
mediaPlayer = null
} catch (e: IllegalStateException) {
e.printStackTrace()
}
}
}
fun onResumeVideoLayout() {
mediaPlayer?.let {
if (!it.isPlaying) {
try {
it.start()
} catch (e: IllegalStateException) {
e.printStackTrace()
}
}
}
}
fun onPauseVideoLayout() {
mediaPlayer?.let {
if (it.isPlaying) {
try {
it.pause()
} catch (e: IllegalStateException) {
e.printStackTrace()
}
}
}
}
fun setPathOrUrl(fileName: String) {
this.fileName = fileName
fileName.setFileFormat()
if (videoSurface == null) {
setupView()
}
if (videoScale != 2) {
calculateVideoSize()
surfaceSetup()
}
if (videoSurface != null) {
changeVideo()
}
}
fun setIsLoop(isLoop: Boolean) {
this.isLoop = isLoop
}
fun setScale(scale: Scale) {
videoScale = scale.ordinal
}
fun setAlign(align: Align) {
videoAlign = align.ordinal
}
}
Add following to your layout.xml
<com.your.package.VideoLayout
android:id="@+id/video_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:loop="true"
app:sound="false"
app:scaleType="crop"
app:align="center"/>
Also this supports video scaleType
like center_crop
, fit_screen
, stretch
and original
.