在作图片和视频编辑时,不可避免的是旋转角度问题,这里仅记录下相关处理策略。html
通常状况下,Camera拍摄的图片和视频都存在旋转角度问题,真正渲染时,须要进行旋转操做。在Android平台上能够经过ExifInterface
类获取JPEG的EXIF信息,其中就包括了旋转角度,以下所示:java
// 图片旋转角度,该角度表示在正常图片的基础上逆时针旋转的角度
var degree = 0
val exifInterface = ExifInterface(path)
val orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL)
when (orientation) {
ExifInterface.ORIENTATION_ROTATE_90 -> degree = 90
ExifInterface.ORIENTATION_ROTATE_180 -> degree = 180
ExifInterface.ORIENTATION_ROTATE_270 -> degree = 270
}
复制代码
除此以外,简单列举几个经过ExifInterface
获取的元数据:json
更详细的EXIF信息,能够经过如下工具进行查看:canvas
EXIF(EXchangeable Image File Format)是专门为数码相机照片设定的可交换图像文件格式,能够记录数码照片的属性信息和拍摄数据。app
经过Mac的显示检查器(打开图片->工具->显示检查器)也能够查看图片的EXIF信息,以下所示。所以咱们须要顺时针旋转90度,以修正图片,因此最终上屏的Size是3456*4608
。工具
上述获取了图片的逆时针旋转角度degree
,因此咱们须要顺时针旋转相同的角度,以修正图片旋转问题,而Matrix
的旋转API正好就是顺时针旋转,因此处理逻辑以下所示:post
val matrix = Matrix()
val originWidth = originBitmap.width
val originHeight = originBitmap.height
val originRectF = RectF(0f, 0f, originWidth.toFloat(), originHeight.toFloat())
val tempRectF = RectF()
// 首先围绕图片中心点旋转上面获取的degree(由于degree是逆时针旋转角度,因此这里须要顺时针旋转进行修正,而Matrix的setRotate正好是顺时针旋转)
matrix.setRotate(degree, originWidth / 2f, originHeight / 2f)
matrix.mapRect(tempRectF, originRectF)
// 修正旋转致使的位移
matrix.postTranslate(0 - tempRectF.left, 0 - tempRectF.top)
复制代码
所以,经过上述得到的matrix
,去canvas.drawBitmap
,就修正了图片的旋转问题。整个流程,能够经过下面的示意图描述: spa
上述degree表示在正常图片的基础上逆时针旋转的角度,即为了修正图片,咱们须要顺时针旋转相同的角度。code
val mediaPlayerWrapper = MediaMetadataRetriever()
mediaPlayerWrapper.setDataSource(inputPath)
// 视频旋转角度,该角度表示在正常视频的基础上逆时针旋转的角度(与图片旋转角度含义一致)
val rotation = mediaPlayerWrapper.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION)
// 配合上述旋转角度的视频宽度
val width = mediaPlayerWrapper.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH)
// 配合上述旋转角度的视频高度
val height = mediaPlayerWrapper.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT)
// 视频时长
val duration = mediaPlayerWrapper.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)
复制代码
上述经过MediaMetadataRetriever
获取的rotation表示在正常视频的基础上逆时针旋转的角度(与图片旋转角度degree含义一致)。orm
通常状况下,播放器在硬解后拿到的OES Texture就是逆时针旋转rotation以后纹理,因此咱们只要对纹理坐标顺时针旋转相同角度就能够了。
在对纹理坐标进行旋转时,有一个重要的差别点:屏幕纹理坐标系和FBO纹理坐标系的坐标原点(0,0)在Y轴上是相反的,以下所示:
因此,渲染到FBO与屏幕时,须要使用不一样的纹理坐标。
假设顶点坐标是
float pos[] = {
-1.0f, 1.0f,
-1.0f, -1.0f,
1.0f, 1.0f,
1.0f, -1.0f,
};
复制代码
如果渲染到FBO时,旋转角度和纹理坐标的对应关系以下所示:
val rotation = 90
val textureCoord = when (rotation) {
90 -> floatArrayOf(
1.0f, 1.0f,
0.0f, 1.0f,
1.0f, 0.0f,
0.0f, 0.0f
)
180 -> floatArrayOf(
1.0f, 0.0f,
1.0f, 1.0f,
0.0f, 0.0f,
0.0f, 1.0f
)
270 -> floatArrayOf(
0.0f, 0.0f,
1.0f, 0.0f,
0.0f, 1.0f,
1.0f, 1.0f
)
// 0
else -> floatArrayOf(
0.0f, 1.0f,
0.0f, 0.0f,
1.0f, 1.0f,
1.0f, 0.0f
)
}
复制代码
相反的,如果渲染到屏幕时,旋转角度和纹理坐标的对应关系以下所示:
val rotation = 90
val textureCoord = when (rotation) {
90 -> floatArrayOf(
0.0f, 1.0f,
1.0f, 1.0f,
0.0f, 0.0f,
1.0f, 0.0f
)
180 -> floatArrayOf(
1.0f, 1.0f,
1.0f, 0.0f,
0.0f, 1.0f,
0.0f, 0.0f
)
270 -> floatArrayOf(
1.0f, 0.0f,
0.0f, 0.0f,
1.0f, 1.0f,
0.0f, 1.0f
)
// 0
else -> floatArrayOf(
0.0f, 0.0f,
0.0f, 1.0f,
1.0f, 0.0f,
1.0f, 1.0f
)
}
复制代码
整个流程,能够经过下面的示意图描述:
上述rotation表示在正常视频的基础上逆时针旋转的角度,即为了修正视频,咱们须要针对纹理坐标顺时针转相同的角度(与处理图片旋转角度的方式一致)。
众所周知,咱们能够经过GLES20.glReadPixels
从指定的Read Buffer中获取一帧图像。可是实践中发现,当从FBO和屏幕Buffer(FBO0)中分别读取帧数据时,获得的图像是镜像关系,缘由就是咱们上面说起的***屏幕纹理坐标系和FBO纹理坐标系在Y轴上是反序的***。
首先看下如何读取并保存帧数据,以下所示:
// 一帧图像的Size
val buffer = ByteBuffer.allocateDirect(width * height * 4)
buffer.order(ByteOrder.LITTLE_ENDIAN)
GLES20.glReadPixels(0, 0, width, height, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, buffer)
buffer.rewind()
// outputFile表示存储的目标文件
val bos = BufferedOutputStream(FileOutputStream(outputFile))
val bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
// 填充像素数据
bmp.copyPixelsFromBuffer(buffer)
bmp.compress(Bitmap.CompressFormat.JPEG, 100, bos)
bmp.recycle()
bos.close()
复制代码
而后看下真实的案例:
GLES20.glReadPixels
从FBO中读取并保存帧数据,获得图片1GLES20.glReadPixels
从屏幕(FBO0)中读取并保存帧数据,获得图片2最后咱们来看下几张图片的对比: 首先是最终上屏的图片0,以下所示:
而后是从FBO读取并保存的图片1,以下所示:
最后是从屏幕(FBO0)读取并保存的图片2,以下所示:
可见,从FBO中读取的图片1是正常的,而从屏幕(FBO0)读取的图片2是反序的。