-
-
Notifications
You must be signed in to change notification settings - Fork 791
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
209 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
171 changes: 171 additions & 0 deletions
171
rtmp/src/main/java/com/pedro/rtmp/flv/video/Av1Packet.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,171 @@ | ||
/* | ||
* Copyright (C) 2023 pedroSG94. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package com.pedro.rtmp.flv.video | ||
|
||
import android.media.MediaCodec | ||
import android.util.Log | ||
import com.pedro.common.removeInfo | ||
import com.pedro.common.toByteArray | ||
import com.pedro.rtmp.flv.FlvPacket | ||
import com.pedro.rtmp.flv.FlvType | ||
import java.nio.ByteBuffer | ||
|
||
/** | ||
* Created by pedro on 05/12/23. | ||
* | ||
*/ | ||
class Av1Packet { | ||
|
||
private val TAG = "AV1Packet" | ||
|
||
private val header = ByteArray(8) | ||
private val naluSize = 4 | ||
//first time we need send video config | ||
private var configSend = false | ||
|
||
private var av1ConfigurationRecord: ByteArray? = null | ||
var profileIop = ProfileIop.BASELINE | ||
|
||
fun sendVideoInfo(av1ConfigurationRecord: ByteBuffer) { | ||
val mAv1ConfigurationRecord = removeHeader(av1ConfigurationRecord) | ||
this.av1ConfigurationRecord = mAv1ConfigurationRecord.toByteArray() | ||
} | ||
|
||
fun createFlvVideoPacket( | ||
byteBuffer: ByteBuffer, | ||
info: MediaCodec.BufferInfo, | ||
callback: (FlvPacket) -> Unit | ||
) { | ||
val fixedBuffer = byteBuffer.removeInfo(info) | ||
val ts = info.presentationTimeUs / 1000 | ||
|
||
//header is 8 bytes length: | ||
//mark first byte as extended header (0b10000000) | ||
//4 bits data type, 4 bits packet type | ||
//4 bytes extended codec type (in this case av01) | ||
//3 bytes CompositionTime, the cts. | ||
val codec = VideoFormat.AV1.value // { "a", "v", "0", "1" } | ||
header[1] = (codec shr 24).toByte() | ||
header[2] = (codec shr 16).toByte() | ||
header[3] = (codec shr 8).toByte() | ||
header[4] = codec.toByte() | ||
val cts = 0 | ||
val ctsLength = 3 | ||
header[5] = (cts shr 16).toByte() | ||
header[6] = (cts shr 8).toByte() | ||
header[7] = cts.toByte() | ||
|
||
var buffer: ByteArray | ||
if (!configSend) { | ||
//avoid send cts on sequence start | ||
header[0] = (0b10000000 or (VideoDataType.KEYFRAME.value shl 4) or FourCCPacketType.SEQUENCE_START.value).toByte() | ||
val sps = this.av1ConfigurationRecord | ||
if (sps != null) { | ||
val config = sps | ||
buffer = ByteArray(config.size + header.size - ctsLength) | ||
val b = ByteBuffer.wrap(buffer, header.size - ctsLength, sps.size) | ||
b.put(sps) | ||
} else { | ||
Log.e(TAG, "waiting for a valid sps and pps") | ||
return | ||
} | ||
|
||
System.arraycopy(header, 0, buffer, 0, header.size - ctsLength) | ||
callback(FlvPacket(buffer, ts, buffer.size, FlvType.VIDEO)) | ||
configSend = true | ||
} | ||
val headerSize = getHeaderSize(fixedBuffer) | ||
if (headerSize == 0) return //invalid buffer or waiting for sps/pps | ||
fixedBuffer.rewind() | ||
val validBuffer = removeHeader(fixedBuffer, headerSize) | ||
val size = validBuffer.remaining() | ||
buffer = ByteArray(header.size + size + naluSize) | ||
|
||
val type: Int = validBuffer.get(0).toInt().shr(1 and 0x3f) | ||
var nalType = VideoDataType.INTER_FRAME.value | ||
if (type == VideoNalType.KEY.value || info.flags == MediaCodec.BUFFER_FLAG_KEY_FRAME) { | ||
nalType = VideoDataType.KEYFRAME.value | ||
} else if (type == VideoNalType.CONFIG.value) { | ||
// we don't need send it because we already do it in video config | ||
return | ||
} | ||
header[0] = (0b10000000 or (nalType shl 4) or FourCCPacketType.CODED_FRAMES.value).toByte() | ||
writeNaluSize(buffer, header.size, size) | ||
validBuffer.get(buffer, header.size + naluSize, size) | ||
|
||
System.arraycopy(header, 0, buffer, 0, header.size) | ||
callback(FlvPacket(buffer, ts, buffer.size, FlvType.VIDEO)) | ||
} | ||
|
||
//naluSize = UInt32 | ||
private fun writeNaluSize(buffer: ByteArray, offset: Int, size: Int) { | ||
buffer[offset] = (size ushr 24).toByte() | ||
buffer[offset + 1] = (size ushr 16).toByte() | ||
buffer[offset + 2] = (size ushr 8).toByte() | ||
buffer[offset + 3] = size.toByte() | ||
} | ||
|
||
private fun removeHeader(byteBuffer: ByteBuffer, size: Int = -1): ByteBuffer { | ||
val position = if (size == -1) getStartCodeSize(byteBuffer) else size | ||
byteBuffer.position(position) | ||
return byteBuffer.slice() | ||
} | ||
|
||
private fun getHeaderSize(byteBuffer: ByteBuffer): Int { | ||
if (byteBuffer.remaining() < 4) return 0 | ||
|
||
val av1ConfigurationRecord = this.av1ConfigurationRecord | ||
if (av1ConfigurationRecord != null) { | ||
val startCodeSize = getStartCodeSize(byteBuffer) | ||
if (startCodeSize == 0) return 0 | ||
val startCode = ByteArray(startCodeSize) { 0x00 } | ||
startCode[startCodeSize - 1] = 0x01 | ||
val avHeader = startCode.plus(av1ConfigurationRecord) | ||
if (byteBuffer.remaining() < avHeader.size) return startCodeSize | ||
|
||
val possibleAvcHeader = ByteArray(avHeader.size) | ||
byteBuffer.get(possibleAvcHeader, 0, possibleAvcHeader.size) | ||
return if (avHeader.contentEquals(possibleAvcHeader)) { | ||
avHeader.size | ||
} else { | ||
startCodeSize | ||
} | ||
} | ||
return 0 | ||
} | ||
|
||
private fun getStartCodeSize(byteBuffer: ByteBuffer): Int { | ||
var startCodeSize = 0 | ||
if (byteBuffer.get(0).toInt() == 0x00 && byteBuffer.get(1).toInt() == 0x00 | ||
&& byteBuffer.get(2).toInt() == 0x00 && byteBuffer.get(3).toInt() == 0x01) { | ||
//match 00 00 00 01 | ||
startCodeSize = 4 | ||
} else if (byteBuffer.get(0).toInt() == 0x00 && byteBuffer.get(1).toInt() == 0x00 | ||
&& byteBuffer.get(2).toInt() == 0x01) { | ||
//match 00 00 01 | ||
startCodeSize = 3 | ||
} | ||
return startCodeSize | ||
} | ||
|
||
fun reset(resetInfo: Boolean = true) { | ||
if (resetInfo) { | ||
av1ConfigurationRecord = null | ||
} | ||
configSend = false | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters