在作圖片和視頻編輯時,不可避免的是旋轉角度問題,這裏僅記錄下相關處理策略。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是反序的。