Android openGL+MediaPlayer播放视频

article/2025/10/1 23:48:54

在Android平台折腾了好多次openGL + MediaPlayer播放视频的事情,openGL本身是套API规范,其身后的计算机图形学还是比较难缠,但只是播放视频用不了太多东西,没有过多的坐标转换、简单2D纹理展示,实现起来还是比较简单,这边记录下开发的代码,后续需要可以直接查看。

openGL环境搭建

在Android中,GLSurfaceView实现了openGL运行依赖的环境,直接使用即可。openGL指定3.0版本,Activity作为Renderer借口实现类。GLES类接口需要在onSurfaceCreated,onSurfaceChanged以及onDrawFrame中调用,因为openGL运行在自己的GLThread中,在其他线程中缺少openGL上下文环境,运行时系统会报错。

在AndroidManifest.xml文件中声明

 <uses-featureandroid:glEsVersion="0x00030000"android:required="true" />
class OpenGLVideoPlayerActivity : Activity(), GLSurfaceView.Renderer, SurfaceTexture.OnFrameAvailableListener {private lateinit var mGLSurfaceView: GLSurfaceViewoverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_opengl_video)mGLSurfaceView = findViewById<GLSurfaceView?>(R.id.surfaceView).apply { setEGLContextClientVersion(3)setRenderer(this@OpenGLVideoPlayerActivity)}}override fun onSurfaceCreated(gl: GL10?, config: EGLConfig?) { }override fun onSurfaceChanged(gl: GL10?, width: Int, height: Int) {  }override fun onDrawFrame(gl: GL10?) { }
}
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"><android.opengl.GLSurfaceViewandroid:id="@+id/surfaceView"android:layout_width="match_parent"android:layout_height="match_parent" />
</FrameLayout>

顶点着色器和片元着色器比较简单,但是要注意openGL播放视频需要用到外部纹理采样。

顶点着色器。vTextureCoord是要输出给片元着色器的纹理坐标,需要对原始纹理坐标处理后输出。gl_Position是输出给管线(自己理解)的顶点坐标,因为播放视频时使用的顶点坐标简单并且已经时标准化坐标,所以这里可以直接输出(一会儿看到顶点坐标就明白了)。

attribute vec4 a_Position;
attribute vec4 a_TextureCoordinate;
uniform mat4 u_TextureMatrix;
varying vec2 vTextureCoord;
void main() {vTextureCoord = (u_TextureMatrix * a_TextureCoordinate).xy;gl_Position =  a_Position;
}

片元着色器。因为播放视频,对比直接显示静态图片需要特殊声明采样的纹理单元。以下声明是全平台通用。

#extension GL_OES_EGL_image_external:require
precision mediump float;
uniform samplerExternalOES u_TextureSampler;
varying vec2 vTextureCoord;
void main() {gl_FragColor = texture2D(u_TextureSampler, vTextureCoord);
}

初始化openGL环境以及加载着色器生成program。此处代码都是模版代码,此处仅仅是记录,没有很完善的做状态错误结果的校验。

private fun newProgram(): Int {val vShaderId = GLES30.glCreateShader(GLES30.GL_VERTEX_SHADER)GLES30.glShaderSource(vShaderId, read(R.raw.vertex_camera))GLES30.glCompileShader(vShaderId)val fShaderId = GLES30.glCreateShader(GLES30.GL_FRAGMENT_SHADER)GLES30.glShaderSource(fShaderId, read(R.raw.fragment_camera))GLES30.glCompileShader(fShaderId)val program = GLES30.glCreateProgram()GLES30.glAttachShader(program, vShaderId)GLES30.glAttachShader(program, fShaderId)GLES30.glLinkProgram(program)return program
}

program创建成功以后即可绑定顶点坐标以及纹理坐标等数据,此处直接使用VAO顶点数据对象方式将数据拷贝到GPU中处理,避免直接使用导致的CPU和GPU之间频繁读写数据。

顶点坐标数据以及纹理坐标

private val VERTEX = floatArrayOf(-1f, 1f, 0f,-1f, -1f, 0f,1f, 1f, 0f,1f, -1f, 0f)
private val TEXTURE = floatArrayOf(0f, 1f,0f, 0f,1f, 1f,1f, 0f)
private val INDICES = intArrayOf(0, 1, 2, 1, 2, 3)

以下图示顶点分布以及顺序情况。渲染时是两个三角形组装成为一个四边形,顶点坐标的定义即两个三角形(左上,右下),纹理坐标与顶点坐标一一对应。采用顶点索引方式绘制,因此(0,1,2,1,2,3)即表示两个三角形顶点顺序。

 

 VAO顶点数据填充绑定代码,需要注意使用GLES30.glBufferSubData方式绑定数据,内存填充结束后,需要及时给顶点绑定属性数据,否则会出现没有数据导致的黑屏问题。

class OpenGLVideoPlayerActivity : Activity(), GLSurfaceView.Renderer,SurfaceTexture.OnFrameAvailableListener {private var mProgramId: Int = 0private var mVao: Int = GLES30.GL_NONEoverride fun onSurfaceCreated(gl: GL10?, config: EGLConfig?) {...val vaoArray = IntArray(1)val vboArray = IntArray(2)GLES30.glGenVertexArrays(vaoArray.size, vaoArray, 0)GLES30.glGenBuffers(vboArray.size, vboArray, 0)GLES30.glBindVertexArray(vaoArray[0])GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, vboArray[0])GLES30.glBufferData(GLES30.GL_ARRAY_BUFFER, getMemorySize(VERTEX) + getMemorySize(TEXTURE), null, GLES30.GL_STATIC_DRAW)GLES30.glBufferSubData(GLES30.GL_ARRAY_BUFFER, 0, getMemorySize(VERTEX), VERTEX.toBuffer())GLES30.glBufferSubData(GLES30.GL_ARRAY_BUFFER, getMemorySize(VERTEX), getMemorySize(TEXTURE), TEXTURE.toBuffer())val aPosition = GLES30.glGetAttribLocation(mProgramId, "a_Position")GLES30.glEnableVertexAttribArray(aPosition)GLES30.glVertexAttribPointer(aPosition, 3, GLES30.GL_FLOAT, false, 3 * 4, 0)val aTextCoordinate = GLES30.glGetAttribLocation(mProgramId, "a_TextureCoordinate")GLES30.glEnableVertexAttribArray(aTextCoordinate)GLES30.glVertexAttribPointer(aTextCoordinate, 2, GLES30.GL_FLOAT, false, 2 * 4, getMemorySize(VERTEX))GLES30.glBindBuffer(GLES30.GL_ELEMENT_ARRAY_BUFFER, vboArray[1])GLES30.glBufferData(GLES30.GL_ELEMENT_ARRAY_BUFFER, getMemorySize(INDICES), INDICES.toBuffer(), GLES30.GL_STATIC_DRAW)mVao = vaoArray[0]...}private fun getMemorySize(array: FloatArray) = array.size * 4private fun getMemorySize(array: IntArray) = array.size * 4private fun FloatArray.toBuffer(): FloatBuffer {val buffer = ByteBuffer.allocateDirect(size * 4).order(ByteOrder.nativeOrder()).asFloatBuffer().put(this)buffer.position(0)return buffer}private fun IntArray.toBuffer(): IntBuffer {val buffer = ByteBuffer.allocateDirect(size * 4).order(ByteOrder.nativeOrder()).asIntBuffer().put(this)buffer.position(0)return buffer}
}

openGL外部纹理

不同与普通图片作为纹理,视频纹理需要生成特殊类型的texture,与MediaPlayer协作,以MediaPlayer作为纹理的生产者,而openGL作为纹理消费者处理视频数据。

openGL纹理生成,注意此处绑定的纹理类型是GLES11Ext.GL_TEXTURE_EXTERNAL_OES。

private fun createOESTextureId(): Int {val textureArray = intArrayOf(1)GLES30.glGenTextures(1, textureArray, 0)GLES30.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureArray[0])GLES30.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES30.GL_TEXTURE_WRAP_S, GLES30.GL_CLAMP_TO_EDGE)GLES30.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES30.GL_TEXTURE_WRAP_T, GLES30.GL_CLAMP_TO_EDGE)GLES30.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES30.GL_TEXTURE_MAG_FILTER, GLES30.GL_LINEAR)GLES30.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES30.GL_TEXTURE_MIN_FILTER, GLES30.GL_LINEAR)return textureArray[0]
}

openGL与MediaPlayer通过SurfaceTexture类进行绑定,当SurfaceTexture.OnFrameAvailableListener#onFrameAvailable回调时表示内存缓冲区有数据,可以刷新画面(个人理解)。

mTextureId = createOESTextureId()
mSurfaceTexture = SurfaceTexture(mTextureId)
mSurfaceTexture.setOnFrameAvailableListener(this)
mediaPlayer.setSurface(Surface(mSurfaceTexture))

在Renderer接口的onDrawFrame方法中,SurfaceTexture刷新缓冲区内存,openGL读取最新的纹理画面推送到窗口中,其中注意SurfaceTexture#getTransformMatrix函数,需要获取最新的变换矩阵与纹理坐标相乘以获取正确的纹理坐标(因为此处视频固定,纹理坐标简单,其实不处理也行)。

override fun onDrawFrame(gl: GL10?) {...if (mAtomicBoolean.get()) {mSurfaceTexture.updateTexImage()mSurfaceTexture.getTransformMatrix(mTextureMatrix)}...GLES30.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mTextureId)GLES30.glActiveTexture(GLES30.GL_TEXTURE0)
}

最后就是MediaPlayer的播放媒体文件。

整体来看,使用openGL播放2D画面视频比较简单,没有顶点坐标的转换等操作,但是openGL的使用是基本功,许多的参数在网上没有详细的教程,需要多查找资料。先写到这里,后面沾上完整的代码。以后有时间,再来详细介绍参数的含义,这部分其实很重要。


package com.openglimport android.app.Activity
import android.content.Context
import android.content.res.Resources
import android.graphics.SurfaceTexture
import android.media.MediaPlayer
import android.opengl.GLES11Ext
import android.opengl.GLES30
import android.opengl.GLSurfaceView
import android.os.Bundle
import android.view.Surface
import com.didi.davinci.avm.R
import java.io.BufferedReader
import java.io.IOException
import java.io.InputStreamReader
import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.nio.FloatBuffer
import java.nio.IntBuffer
import java.util.concurrent.atomic.AtomicBoolean
import javax.microedition.khronos.egl.EGLConfig
import javax.microedition.khronos.opengles.GL10private val VERTEX = floatArrayOf(-1f, 1f, 0f, -1f, -1f, 0f, 1f, 1f, 0f, 1f, -1f, 0f
)
private val TEXTURE = floatArrayOf(0f, 1f, 0f, 0f, 1f, 1f, 1f, 0f
)
private val INDICES = intArrayOf(0, 1, 2, 1, 2, 3)class OpenGLVideoPlayerActivity : Activity(), GLSurfaceView.Renderer,SurfaceTexture.OnFrameAvailableListener {private lateinit var mGLSurfaceView: GLSurfaceViewprivate var mProgramId: Int = 0private var mVao: Int = GLES30.GL_NONEprivate val mediaPlayer: MediaPlayer = MediaPlayer().apply {isLooping = true}private lateinit var mSurfaceTexture: SurfaceTextureprivate var mTextureId: Int = 0private val mTextureMatrix = FloatArray(16)private var mTextureMatrixLocation: Int = 0override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_opengl_video)mGLSurfaceView = findViewById<GLSurfaceView?>(R.id.surfaceView).apply {setEGLContextClientVersion(3)setRenderer(this@OpenGLVideoPlayerActivity)}with(mediaPlayer) {setDataSource(assets.openFd("ai.mp4"))prepare()start()}}override fun onSurfaceCreated(gl: GL10?, config: EGLConfig?) {mProgramId = newProgram()val vaoArray = IntArray(1)val vboArray = IntArray(2)GLES30.glGenVertexArrays(vaoArray.size, vaoArray, 0)GLES30.glGenBuffers(vboArray.size, vboArray, 0)GLES30.glBindVertexArray(vaoArray[0])GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, vboArray[0])GLES30.glBufferData(GLES30.GL_ARRAY_BUFFER,getMemorySize(VERTEX) + getMemorySize(TEXTURE),null,GLES30.GL_STATIC_DRAW)GLES30.glBufferSubData(GLES30.GL_ARRAY_BUFFER, 0, getMemorySize(VERTEX), VERTEX.toBuffer())GLES30.glBufferSubData(GLES30.GL_ARRAY_BUFFER,getMemorySize(VERTEX),getMemorySize(TEXTURE),TEXTURE.toBuffer())val aPosition = GLES30.glGetAttribLocation(mProgramId, "a_Position")GLES30.glEnableVertexAttribArray(aPosition)GLES30.glVertexAttribPointer(aPosition, 3, GLES30.GL_FLOAT, false, 3 * 4, 0)val aTextCoordinate = GLES30.glGetAttribLocation(mProgramId, "a_TextureCoordinate")GLES30.glEnableVertexAttribArray(aTextCoordinate)GLES30.glVertexAttribPointer(aTextCoordinate, 2, GLES30.GL_FLOAT, false, 2 * 4, getMemorySize(VERTEX))GLES30.glBindBuffer(GLES30.GL_ELEMENT_ARRAY_BUFFER, vboArray[1])GLES30.glBufferData(GLES30.GL_ELEMENT_ARRAY_BUFFER,getMemorySize(INDICES),INDICES.toBuffer(),GLES30.GL_STATIC_DRAW)mTextureMatrixLocation = GLES30.glGetUniformLocation(mProgramId, "u_TextureMatrix")mVao = vaoArray[0]mTextureId = createOESTextureId()mSurfaceTexture = SurfaceTexture(mTextureId)mSurfaceTexture.setOnFrameAvailableListener(this)mediaPlayer.setSurface(Surface(mSurfaceTexture))}override fun onSurfaceChanged(gl: GL10?, width: Int, height: Int) {GLES30.glViewport(0, 0, width, height)}override fun onDrawFrame(gl: GL10?) {if (mAtomicBoolean.get()) {mSurfaceTexture.updateTexImage()mSurfaceTexture.getTransformMatrix(mTextureMatrix)}GLES30.glClearColor(0f, 1f, 0f, 1f)GLES30.glUseProgram(mProgramId)GLES30.glBindVertexArray(mVao)GLES30.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mTextureId)GLES30.glActiveTexture(GLES30.GL_TEXTURE0)GLES30.glUniformMatrix4fv(mTextureMatrixLocation, 1, false, mTextureMatrix, 0)GLES30.glDrawElements(GLES30.GL_TRIANGLES, INDICES.size, GLES30.GL_UNSIGNED_INT, 0)}private fun newProgram(): Int {val vShaderId = GLES30.glCreateShader(GLES30.GL_VERTEX_SHADER)GLES30.glShaderSource(vShaderId, read(this, R.raw.vertex_camera))GLES30.glCompileShader(vShaderId)val fShaderId = GLES30.glCreateShader(GLES30.GL_FRAGMENT_SHADER)GLES30.glShaderSource(fShaderId, read(this, R.raw.fragment_camera))GLES30.glCompileShader(fShaderId)val program = GLES30.glCreateProgram()GLES30.glAttachShader(program, vShaderId)GLES30.glAttachShader(program, fShaderId)GLES30.glLinkProgram(program)return program}private fun read(context: Context, id: Int): String {val builder = StringBuilder()try {context.resources.openRawResource(id).use {InputStreamReader(it).use { sr ->BufferedReader(sr).use { br ->var textLine: String?while (br.readLine().also { textLine = it } != null) {builder.append(textLine)builder.append("\n")}}}}} catch (e: IOException) {e.printStackTrace()} catch (e: Resources.NotFoundException) {e.printStackTrace()}return builder.toString()}private fun getMemorySize(array: FloatArray) = array.size * 4private fun getMemorySize(array: IntArray) = array.size * 4private fun FloatArray.toBuffer(): FloatBuffer {val buffer =ByteBuffer.allocateDirect(size * 4).order(ByteOrder.nativeOrder()).asFloatBuffer().put(this)buffer.position(0)return buffer}private fun IntArray.toBuffer(): IntBuffer {val buffer =ByteBuffer.allocateDirect(size * 4).order(ByteOrder.nativeOrder()).asIntBuffer().put(this)buffer.position(0)return buffer}private fun createOESTextureId(): Int {val textureArray = intArrayOf(1)GLES30.glGenTextures(1, textureArray, 0)GLES30.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureArray[0])GLES30.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES30.GL_TEXTURE_WRAP_S, GLES30.GL_CLAMP_TO_EDGE)GLES30.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES30.GL_TEXTURE_WRAP_T, GLES30.GL_CLAMP_TO_EDGE)GLES30.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES30.GL_TEXTURE_MAG_FILTER, GLES30.GL_LINEAR)GLES30.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES30.GL_TEXTURE_MIN_FILTER, GLES30.GL_LINEAR)return textureArray[0]}private val mAtomicBoolean = AtomicBoolean(false)override fun onFrameAvailable(surfaceTexture: SurfaceTexture?) {mAtomicBoolean.set(true)}
}
attribute vec4 a_Position;
attribute vec4 a_TextureCoordinate;uniform mat4 u_TextureMatrix;
uniform mat4 u_MvpMatrix;varying vec2 vTextureCoord;void main() {vTextureCoord = (u_TextureMatrix * a_TextureCoordinate).xy;gl_Position =  a_Position;
}
#extension GL_OES_EGL_image_external:require
precision mediump float;uniform samplerExternalOES u_TextureSampler;varying vec2 vTextureCoord;void main() {gl_FragColor = texture2D(u_TextureSampler, vTextureCoord);
}


http://chatgpt.dhexx.cn/article/w79yDG9G.shtml

相关文章

Android-MediaPlayer播放网络音频

官方文档&#xff1a;https://developer.android.google.cn/reference/android/media/MediaPlayer 运行截图&#xff1a; 主布局文件&#xff1a; <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas.a…

深入Android MediaPlayer的使用方法详解

1&#xff09;如何获得MediaPlayer实例&#xff1a; 可以使用直接new的方式&#xff1a; MediaPlayer mp new MediaPlayer(); 也可以使用create的方式&#xff0c;如&#xff1a; MediaPlayer mp MediaPlayer.create(this, R.raw.test);//这时就不用调用setDataSource了 2) 如…

Android MediaPlayer状态机

翻译Android Reference Manual的MediaPlayer的状态机 对播放音频/视频文件和流的控制是通过一个状态机来管理的。下图显示一个MediaPlayer对象被支持的播放控制操作驱动的生命周期和状态。椭圆代表MediaPlayer对象可能驻留的状态。弧线表示驱动MediaPlayer在各个状态之间迁移的…

Android提高第二十一篇之MediaPlayer播放网络视频

本文来自http://blog.csdn.net/hellogv/ &#xff0c;引用必须注明出处&#xff01; 上次讲解了MediaPlayer播放网络音频&#xff0c;介绍了MediaPlayer关于网络音频的缓冲和进度条控制的方法&#xff0c;这次再讲解MediaPlayer播放网络视频。播放网络视频比播放网络音频多需要…

Android开发之MediaPlayer详解

Android开发之MdiaPlayer详解 MediaPlayer类可用于控制音频/视频文件或流的播放&#xff0c;我曾在《Android开发之基于Service的音乐播放器》一文中介绍过它的使用。下面让我们看一下MediaPlayer类的详细介绍。 一、类结构&#xff1a; java.lang.Object ↳ android.med…

Android 9.0 MediaPlayer播放流程分析

1.MediaPlayer初始化流程 EventHandler是后面处理数据回调的handler. 在AudioFlinger.cpp中获取nextUniqueId&#xff1a; audio_unique_id_t AudioFlinger::nextUniqueId(audio_unique_id_use_t use) {// This is the internal API, so it is OK to assert on bad parameter.…

Android的MediaPlayer架构介绍

本文主要介绍的是Android中很重要也最为复杂的媒体播放器&#xff08;MediaPlayer&#xff09;部分的架构。对于Android这样一个完整又相对复杂的系统&#xff0c;一个MediaPlayer功能的实现不在其具体的功能&#xff0c;而是具体功能如何适应Android系统Android MediaPlayer的…

Android之MediaPlayer详解

文章转自&#xff1a;http://www.cnblogs.com/gansc23/archive/2011/04/08/2009868.html MediaPlayer类可用于控制音频/视频文件或流的播放。关于如何使用这个类的方法还可以阅读VideoView类的文档。 1&#xff0e;状态图 对播放音频/视频文件和流的控制是通过一个状态机来…

Android MediaPlayer播放视频详细步骤

MediaPlayer类是媒体框架最重要的组成部分之一&#xff0c;此类的对象能够获取&#xff0c;解码以及播放音频和视频&#xff0c;而且只需极少量设置&#xff0c;它支持多种不同的媒体源&#xff0c;例如&#xff1a; 本地资源 内部Url&#xff0c;例如您可能从内容解析器获取U…

Android MediaPlayer

最近在做游戏状态的保存时&#xff0c;需要存储背景音乐是否静音了&#xff0c;一直不成功&#xff0c;并且总是报出如下错误&#xff1a; ERROR/MediaPlayer(9974): start called in state 64 ERROR/MediaPlayer(9974): error (-38, 0) ERROR/MediaPlayer(9974): Error (-38…

MediaPlayer类播放音频

一、MediaPlayer类 1、常用方法 方法名称功能setDataSource()设置要播放的音频文件prepare()在开始播放前。调用该方法准备播放start()开始播放或者继续播放音频pause()暂停播放reset()重置MediaPlayer对象seekTo()从指定位置播放stop()停止播放&#xff0c;调用后MediaPlaye…

Android提高第一篇之MediaPlayer

本文来自http://blog.csdn.net/hellogv/ &#xff0c;引用必须注明出处&#xff01; 前面写了十四篇关于界面的入门文章&#xff0c;大家都看完和跟着练习之后&#xff0c;对于常用的Layout和View都会有一定的了解了,接下来的文章就不再强调介绍界面了&#xff0c;而是针对具体…

Android中的MediaPlayer的使用详解

今天本文介绍的是Andriod系统自带的Mediaplayer,和VideoView实现简单的音乐和视频的播放&#xff0c;至于想做出如酷狗音乐这样的APP的话&#xff0c;只要想做&#xff0c;应该也不难&#xff0c;都是基于此实现了功能的扩展。 Android的MediaPlayer包含了Audio和Video的播放功…

MediaPlayer详解和使用

Android多媒体相关的API&#xff0c;网上基本都能找到很多相关的文章&#xff0c;使用起来也很简单&#xff0c;一直在犹豫要不要写这方面的内容&#xff0c;后来决定还是写一写&#xff0c;一方面算是一个归纳总结&#xff0c;另一方面&#xff0c;也方便以后查阅。这一篇就写…

MediaPlayer的使用

MediaPlayer的使用 MediaPlayer的使用&#xff08;2019.07.16&#xff09;1.视频播放器的原理2.Android系统自带的MediaPlay状态机详解&#xff08;MediaPlay的生命周期&#xff09;3.如何使用MediaPlayer播放音频与视频&#xff08;一 &#xff09;播放音频&#xff08;二&…

MediaPlayer使用以及常见问题

前面已经写过一篇类似的文章&#xff0c;但是还不够细致&#xff1a; 采用Android的MediaPlayerSurfaceView设计视频播放器 这里我们重新理一下&#xff0c;并记录一点实际运用时遇到的问题。 MediaPlayer特性 MediaPlayer类用于控制音频文件、视频文件和流的播放。 Media…

Android MediaPlayer类

1. MediaPlayer方法 MediaPlayer创建 可以直接调用构造函数&#xff0c;利用setDataSource()方法设置资源。MediaPlayer mp new MediaPlayer(); // path可以是本地路径&#xff0c;也可以是网络地址 mp.setDataSource(String path);也可以调用create()方法&#xff0c;create…

MediaPlayer状态图及生命周期

MediaPlayer状态图及生命周期 MediaPlayer是Android中的uoge多媒体播放类&#xff0c;我们能通过它控制音视频流或本地音视频资源的播放过程。 这一片博客主要介绍MediaPlayer状态图及生命周期。先看一张官网很经典的MediaPlayer状态机的图片。 其中椭圆代表MediaPlayer驻留…

Android 使用MediaPlayer播放音频详解

目录 一、官方资料 二、简单介绍 三、MediaPlayer使用 1.创建MediaPlayer实例 2.重要API 3.状态图 4.代码 5.常用API 6.辅助效果 总结 一、官方资料 MediaPlayer 概览https://developer.android.google.cn/guide/topics/media/mediaplayer?hlzh_cn MediaPlayer 文…

Android -- 多媒体播放之MediaPlayer基础简介

Android -- MediaPlayer基础简介 MediaPlayer是Android中的一个多媒体播放类&#xff0c;我们能通过它控制音视频流或本地音视频资源的播放过程。 这段时间查阅了Android官方文档对MediaPlayer的介绍&#xff0c;同时也看了一些书籍介绍。现在把MediaPlayer的基础内容在此做一…