前兩章介紹了音視頻的採集,這章主要內容是適配5.0以下手機的視頻採集,首先自然是老Api的基類代碼
//老相機id
private var oldCameraId = Camera.CameraInfo.CAMERA_FACING_BACK
//老相機SurfaceTexture
private var oldSurfaceTexture:SurfaceTexture? = null
//老相機數據存儲數組
private var oldBuffers:ByteArray? = null
//老相機
private var oldCamera:Camera? = null
/**
* 老相機API開始預覽
*/
fun startPreview(surfaceTexture:SurfaceTexture){
if (oldSurfaceTexture == null){
oldSurfaceTexture = surfaceTexture;
}
// 打開攝像頭並將展示方向旋轉90度
oldCamera = Camera.open(oldCameraId)
oldCamera!!.setDisplayOrientation(90)
val parameters = oldCamera!!.parameters
// 選擇合適的預覽尺寸
val sizeList = parameters.supportedPreviewSizes
val oldSize = getOldSize(sizeList)
parameters.previewFormat = ImageFormat.NV21
//設置預覽圖像參數
parameters.setPictureSize(oldSize.width,oldSize.height)
parameters.setPreviewSize(oldSize.width,oldSize.height)
oldCamera!!.parameters = parameters
oldCamera!!.setPreviewTexture(oldSurfaceTexture)
//獲取預覽數據
oldBuffers = ByteArray(oldSize.width * oldSize.height * 4)
oldCamera!!.addCallbackBuffer(oldBuffers)
oldCamera!!.setPreviewCallbackWithBuffer(previewCallback)
oldCamera!!.startPreview()
}
/**
* 獲取老相機預覽數據回調
*/
private val previewCallback = Camera.PreviewCallback { data, camera ->
camera?.addCallbackBuffer(oldBuffers)
oldSendVideoData(data)
}
/**
* 老相機API預覽數據回調
*/
abstract fun oldSendVideoData(data:ByteArray)
/**
* 停止預覽
*/
fun stopPreview(){
if (oldCamera != null){
oldCamera!!.stopPreview()
oldCamera!!.release()
oldCamera = null
}
}
/**
* 老camera切換攝像頭
*/
fun oldSwitchCamera(){
if (oldSurfaceTexture != null){
oldCameraId = if (oldCameraId == Camera.CameraInfo.CAMERA_FACING_BACK){
Camera.CameraInfo.CAMERA_FACING_FRONT
}else{
Camera.CameraInfo.CAMERA_FACING_BACK
}
stopPreview()
startPreview(oldSurfaceTexture!!)
}
}
/**
* 獲取老相機API的預覽大小
*/
private fun getOldSize(sizeList:List<Camera.Size>):Camera.Size{
val aspectRatios = ArrayList<Float>()
aspectRatios.add(16.toFloat() / 9)
aspectRatios.add(4.toFloat() / 3)
aspectRatios.add(18.toFloat() / 9)
for (aspectRatio in aspectRatios){
for (size in sizeList){
if (size.width.toFloat() / size.height == aspectRatio) {
return size
}
}
}
return sizeList[0]
}
使用方式很簡單,在onSurfaceTextureAvailable中調用startPreview(surfaceTexture);中onSurfaceTextureDestroyed中調用stopPreview()
/**
* 獲取Surface的回調
*/
private val surfaceTextureListener = object : TextureView.SurfaceTextureListener {
//SurfaceTexture大小發生變化時調用
override fun onSurfaceTextureSizeChanged(
surfaceTexture: SurfaceTexture,
width: Int,
height: Int
) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
//獲取相機屬性類
val cameraCharacteristics = getCameraCharacteristics()
//設置預覽尺寸
size = setPreviewSize(surfaceTexture, cameraCharacteristics)
surface = Surface(surfaceTexture)
}
}
override fun onSurfaceTextureUpdated(surface: SurfaceTexture?) {
}
override fun onSurfaceTextureDestroyed(surfaceTexture: SurfaceTexture?): Boolean {
//關閉老相機API預覽
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
stopPreview()
}
return true
}
override fun onSurfaceTextureAvailable(
surfaceTexture: SurfaceTexture,
width: Int,
height: Int
) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
//獲取相機屬性類
val cameraCharacteristics = getCameraCharacteristics()
//設置預覽尺寸
size = setPreviewSize(surfaceTexture, cameraCharacteristics)
surface = Surface(surfaceTexture)
//開啓攝像頭
openCamera()
}else{
//開啓老相機API預覽
startPreview(surfaceTexture)
}
}
}
繼承這個基類需要實現oldSendVideoData,視頻編碼是不是很方便
/**
* 老相機API數據回調接口
*/
override fun oldSendVideoData(data: ByteArray) {
}
使用示例
這裏集成了前面兩章內容
class LiveBroadcastActivity : BaseVideoActivity() {
/**
* 老相機API數據回調接口
*/
override fun oldSendVideoData(data: ByteArray) {
}
//預覽Surface
private lateinit var surface: Surface
//相機預覽分辨率
private lateinit var size: Size
//預覽CaptureRequest.Builder
private lateinit var previewCaptureRequest: CaptureRequest.Builder
override fun getLayoutId(): Int {
return R.layout.activity_live_broadcast;
}
override fun init() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
//初始化Camera2
initCamera2()
//初始化AudioRecord
initAudioRecord()
switchCamera.setOnClickListener {
switchCamera()
}
}
//開啓surfaceTextureListener監聽,獲取Surface
textureView.surfaceTextureListener = surfaceTextureListener
switchCamera.setOnClickListener {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
switchCamera()
}else{
oldSwitchCamera()
}
}
}
/**
* 獲取Surface的回調
*/
private val surfaceTextureListener = object : TextureView.SurfaceTextureListener {
//SurfaceTexture大小發生變化時調用
override fun onSurfaceTextureSizeChanged(
surfaceTexture: SurfaceTexture,
width: Int,
height: Int
) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
//獲取相機屬性類
val cameraCharacteristics = getCameraCharacteristics()
//設置預覽尺寸
size = setPreviewSize(surfaceTexture, cameraCharacteristics)
surface = Surface(surfaceTexture)
}
}
override fun onSurfaceTextureUpdated(surface: SurfaceTexture?) {
}
override fun onSurfaceTextureDestroyed(surfaceTexture: SurfaceTexture?): Boolean {
//關閉老相機API預覽
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
stopPreview()
}
return true
}
override fun onSurfaceTextureAvailable(
surfaceTexture: SurfaceTexture,
width: Int,
height: Int
) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
//獲取相機屬性類
val cameraCharacteristics = getCameraCharacteristics()
//設置預覽尺寸
size = setPreviewSize(surfaceTexture, cameraCharacteristics)
surface = Surface(surfaceTexture)
//開啓攝像頭
openCamera()
}else{
//開啓老相機API預覽
startPreview(surfaceTexture)
}
}
}
/**
* 創建一個Session
*/
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
override fun createCaptureSession(camera: CameraDevice):List<Surface>{
//創建一個預覽的CaptureRequest
previewCaptureRequest = camera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW)
// 設置預覽輸出的 Surface
previewCaptureRequest.addTarget(surface)
val outputs = ArrayList<Surface>()
outputs.add(surface)
return outputs
}
/**
* 返回預覽的CaptureRequest
*/
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
override fun setRepeatingRequest(session: CameraCaptureSession): CaptureRequest{
//開始錄音
startRecording()
return previewCaptureRequest.build()
}
/**
* 音頻編碼,注意這裏是在子線程中的
*/
override fun audioCoding(buffer: ByteArray, len: Int) {
}
/**
* 自動修改textureView寬高以適應不同預覽比例
*/
override fun onWindowFocusChanged(hasFocus: Boolean) {
super.onWindowFocusChanged(hasFocus)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
val width = textureView.width
val height = textureView.height
val proportion1 = size.width.toFloat() / size.height
val proportion2 = height.toFloat() / width
if (proportion1 > proportion2) {
val layoutParams = textureView.layoutParams
layoutParams.width = (height * proportion1 + .5).toInt()
textureView.layoutParams = layoutParams
} else if (proportion1 < proportion2) {
val layoutParams = textureView.layoutParams
layoutParams.height = (width * proportion1 + .5).toInt()
textureView.layoutParams = layoutParams
}
}
}
}