diff --git a/core/src/main/java/io/github/thibaultbee/streampack/data/VideoConfig.kt b/core/src/main/java/io/github/thibaultbee/streampack/data/VideoConfig.kt index 0fe587c1e..324101891 100644 --- a/core/src/main/java/io/github/thibaultbee/streampack/data/VideoConfig.kt +++ b/core/src/main/java/io/github/thibaultbee/streampack/data/VideoConfig.kt @@ -17,6 +17,7 @@ package io.github.thibaultbee.streampack.data import android.content.Context import android.media.MediaCodecInfo.CodecProfileLevel +import android.media.MediaCodecInfo.CodecProfileLevel.AV1ProfileMain8 import android.media.MediaCodecInfo.CodecProfileLevel.AVCProfileBaseline import android.media.MediaCodecInfo.CodecProfileLevel.AVCProfileConstrainedBaseline import android.media.MediaCodecInfo.CodecProfileLevel.AVCProfileConstrainedHigh @@ -48,8 +49,8 @@ import kotlin.math.roundToInt class VideoConfig( /** * Video encoder mime type. - * Only [MediaFormat.MIMETYPE_VIDEO_AVC], [MediaFormat.MIMETYPE_VIDEO_HEVC] and - * [MediaFormat.MIMETYPE_VIDEO_VP9] are supported yet. + * Only [MediaFormat.MIMETYPE_VIDEO_AVC], [MediaFormat.MIMETYPE_VIDEO_HEVC], + * [MediaFormat.MIMETYPE_VIDEO_VP9] and [MediaFormat.MIMETYPE_VIDEO_AV1] are supported yet. * * **See Also:** [MediaFormat MIMETYPE_VIDEO_*](https://developer.android.com/reference/android/media/MediaFormat) */ @@ -223,6 +224,10 @@ class VideoConfig( VP9Profile0 ) + private val av1ProfilePriority = listOf( + AV1ProfileMain8 + ) + /** * Return the higher profile with the higher level */ @@ -231,6 +236,7 @@ class VideoConfig( MediaFormat.MIMETYPE_VIDEO_AVC -> avcProfilePriority MediaFormat.MIMETYPE_VIDEO_HEVC -> hevcProfilePriority MediaFormat.MIMETYPE_VIDEO_VP9 -> vp9ProfilePriority + MediaFormat.MIMETYPE_VIDEO_AV1 -> av1ProfilePriority else -> throw InvalidParameterException("Profile for $mimeType is not supported") } diff --git a/core/src/main/java/io/github/thibaultbee/streampack/internal/muxers/flv/FlvMuxerHelper.kt b/core/src/main/java/io/github/thibaultbee/streampack/internal/muxers/flv/FlvMuxerHelper.kt index 864aada72..fb5106f2e 100644 --- a/core/src/main/java/io/github/thibaultbee/streampack/internal/muxers/flv/FlvMuxerHelper.kt +++ b/core/src/main/java/io/github/thibaultbee/streampack/internal/muxers/flv/FlvMuxerHelper.kt @@ -37,7 +37,8 @@ class VideoFlvMuxerHelper : IVideoMuxerHelper { get() { val extendedSupportedCodec = listOf( MediaFormat.MIMETYPE_VIDEO_HEVC, - MediaFormat.MIMETYPE_VIDEO_VP9 + MediaFormat.MIMETYPE_VIDEO_VP9, + MediaFormat.MIMETYPE_VIDEO_AV1 ) val supportedCodecList = CodecID.entries.mapNotNull { try { diff --git a/core/src/main/java/io/github/thibaultbee/streampack/internal/muxers/flv/tags/AVTagsFactory.kt b/core/src/main/java/io/github/thibaultbee/streampack/internal/muxers/flv/tags/AVTagsFactory.kt index 33fe2e7b8..3580b1575 100644 --- a/core/src/main/java/io/github/thibaultbee/streampack/internal/muxers/flv/tags/AVTagsFactory.kt +++ b/core/src/main/java/io/github/thibaultbee/streampack/internal/muxers/flv/tags/AVTagsFactory.kt @@ -123,6 +123,15 @@ class AVTagsFactory( VPCodecConfigurationRecord.fromMediaFormat(frame.format) } + MediaFormat.MIMETYPE_VIDEO_AV1 -> { + if (frame.extra != null) { + // Extra is AV1CodecConfigurationRecord + PassthroughBufferWriter(frame.extra[0]) + } else { + throw IOException("AV1 sequence header without CSD buffer is not supported") + } + } + else -> { throw IOException("Unsupported video codec: ${config.mimeType}") } diff --git a/core/src/main/java/io/github/thibaultbee/streampack/internal/muxers/mp4/MP4MuxerHelper.kt b/core/src/main/java/io/github/thibaultbee/streampack/internal/muxers/mp4/MP4MuxerHelper.kt index 5677e1cba..4bdc0c66a 100644 --- a/core/src/main/java/io/github/thibaultbee/streampack/internal/muxers/mp4/MP4MuxerHelper.kt +++ b/core/src/main/java/io/github/thibaultbee/streampack/internal/muxers/mp4/MP4MuxerHelper.kt @@ -48,6 +48,7 @@ class VideoMP4MuxerHelper : IVideoMuxerHelper { listOf( MediaFormat.MIMETYPE_VIDEO_AVC, MediaFormat.MIMETYPE_VIDEO_HEVC, - MediaFormat.MIMETYPE_VIDEO_VP9 + MediaFormat.MIMETYPE_VIDEO_VP9, + MediaFormat.MIMETYPE_VIDEO_AV1 ) } diff --git a/core/src/main/java/io/github/thibaultbee/streampack/internal/muxers/mp4/boxes/AV1CodecConfigurationBox.kt b/core/src/main/java/io/github/thibaultbee/streampack/internal/muxers/mp4/boxes/AV1CodecConfigurationBox.kt new file mode 100644 index 000000000..0f98ac80d --- /dev/null +++ b/core/src/main/java/io/github/thibaultbee/streampack/internal/muxers/mp4/boxes/AV1CodecConfigurationBox.kt @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2023 Thibault B. + * + * 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 io.github.thibaultbee.streampack.internal.muxers.mp4.boxes + +import io.github.thibaultbee.streampack.internal.utils.av.video.av1.AV1CodecConfigurationRecord +import java.nio.ByteBuffer + +sealed class AV1CodecConfigurationBox : Box("av1C") + +class AV1CodecConfigurationBox1(private val config: AV1CodecConfigurationRecord) : + AV1CodecConfigurationBox() { + override val size: Int = super.size + config.size + + override fun write(output: ByteBuffer) { + super.write(output) + config.write(output) + } +} + +class AV1CodecConfigurationBox2(private val buffer: ByteBuffer) : AV1CodecConfigurationBox() { + override val size: Int = super.size + buffer.remaining() + + override fun write(output: ByteBuffer) { + super.write(output) + output.put(buffer) + } +} \ No newline at end of file diff --git a/core/src/main/java/io/github/thibaultbee/streampack/internal/muxers/mp4/boxes/SampleEntry.kt b/core/src/main/java/io/github/thibaultbee/streampack/internal/muxers/mp4/boxes/SampleEntry.kt index bf4e79636..cacc78bcb 100644 --- a/core/src/main/java/io/github/thibaultbee/streampack/internal/muxers/mp4/boxes/SampleEntry.kt +++ b/core/src/main/java/io/github/thibaultbee/streampack/internal/muxers/mp4/boxes/SampleEntry.kt @@ -132,7 +132,6 @@ class HEVCSampleEntry( pasp ) - class VP9SampleEntry( resolution: Size, horizontalResolution: Int = 72, @@ -161,6 +160,34 @@ class VP9SampleEntry( pasp ) +class AV1SampleEntry( + resolution: Size, + horizontalResolution: Int = 72, + verticalResolution: Int = 72, + frameCount: Short = 1, + compressorName: String? = "AV1 Coding", + depth: Short = 0x0018, + av1C: AV1CodecConfigurationBox, + btrt: BitRateBox? = null, + extensionDescriptorsBox: List = emptyList(), + clap: CleanApertureBox? = null, + pasp: PixelAspectRatioBox? = null +) : VisualSampleEntry( + "av01", + resolution, + horizontalResolution, + verticalResolution, + frameCount, + compressorName, + depth, + mutableListOf(av1C).apply { + btrt?.let { add(it) } + addAll(extensionDescriptorsBox) + }, + clap, + pasp +) + class OpusSampleEntry( channelCount: Short, dOps: OpusSpecificBox, @@ -174,7 +201,8 @@ class OpusSampleEntry( 48000, mutableListOf(dOps).apply { btrt?.let { add(it) } - }) + } + ) class MP4AudioSampleEntry( channelCount: Short, @@ -191,7 +219,8 @@ class MP4AudioSampleEntry( sampleRate, mutableListOf(esds).apply { btrt?.let { add(it) } - }) + } + ) open class AudioSampleEntry( type: String, diff --git a/core/src/main/java/io/github/thibaultbee/streampack/internal/muxers/mp4/models/TrackChunks.kt b/core/src/main/java/io/github/thibaultbee/streampack/internal/muxers/mp4/models/TrackChunks.kt index d6b899a9f..1913b292e 100644 --- a/core/src/main/java/io/github/thibaultbee/streampack/internal/muxers/mp4/models/TrackChunks.kt +++ b/core/src/main/java/io/github/thibaultbee/streampack/internal/muxers/mp4/models/TrackChunks.kt @@ -22,6 +22,8 @@ import io.github.thibaultbee.streampack.data.Config import io.github.thibaultbee.streampack.data.VideoConfig import io.github.thibaultbee.streampack.internal.data.Frame import io.github.thibaultbee.streampack.internal.interfaces.IOrientationProvider +import io.github.thibaultbee.streampack.internal.muxers.mp4.boxes.AV1CodecConfigurationBox2 +import io.github.thibaultbee.streampack.internal.muxers.mp4.boxes.AV1SampleEntry import io.github.thibaultbee.streampack.internal.muxers.mp4.boxes.AVCConfigurationBox import io.github.thibaultbee.streampack.internal.muxers.mp4.boxes.AVCSampleEntry import io.github.thibaultbee.streampack.internal.muxers.mp4.boxes.ChunkLargeOffsetBox @@ -97,6 +99,10 @@ class TrackChunks( this.format.isNotEmpty() } + MediaFormat.MIMETYPE_VIDEO_AV1 -> { + this.extra.size == 1 + } + MediaFormat.MIMETYPE_AUDIO_AAC -> { this.extra.size == 1 } @@ -343,6 +349,18 @@ class TrackChunks( ) } + MediaFormat.MIMETYPE_VIDEO_AV1 -> { + val extra = this.extra + require(extra.size == 1) { "For AV1, extra must contain 1 extra" } + (track.config as VideoConfig) + AV1SampleEntry( + orientationProvider.orientedSize(track.config.resolution), + av1C = AV1CodecConfigurationBox2( + extra[0][0] + ), + ) + } + MediaFormat.MIMETYPE_AUDIO_AAC -> { (track.config as AudioConfig) MP4AudioSampleEntry( diff --git a/core/src/main/java/io/github/thibaultbee/streampack/internal/utils/av/video/av1/AV1CodecConfigurationRecord.kt b/core/src/main/java/io/github/thibaultbee/streampack/internal/utils/av/video/av1/AV1CodecConfigurationRecord.kt new file mode 100644 index 000000000..3ee7827c7 --- /dev/null +++ b/core/src/main/java/io/github/thibaultbee/streampack/internal/utils/av/video/av1/AV1CodecConfigurationRecord.kt @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2023 Thibault B. + * + * 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 io.github.thibaultbee.streampack.internal.utils.av.video.av1 + +import android.media.MediaFormat +import io.github.thibaultbee.streampack.internal.utils.av.buffer.ByteBufferWriter +import io.github.thibaultbee.streampack.internal.utils.extensions.put +import io.github.thibaultbee.streampack.internal.utils.extensions.putShort +import io.github.thibaultbee.streampack.internal.utils.extensions.shl +import io.github.thibaultbee.streampack.internal.utils.extensions.toInt +import java.nio.ByteBuffer + +class AV1CodecConfigurationRecord( + private val marker: Boolean = true, + private val version: Byte = 1, + private val seqProfile: Byte, + private val seqLevelIdx0: Byte, + private val seqTier0: Boolean, + private val highBitdepth: Boolean, + private val twelveBit: Boolean, + private val monochrome: Boolean, + private val chromaSubsamplingX: Boolean, + private val chromaSubsamplingY: Boolean, + private val chromaSamplePosition: Byte, + private val initialPresentationDelayMinusOne: Int?, + private val configOBUs: ByteBuffer, +) : ByteBufferWriter() { + override val size: Int = AV1_DECODER_CONFIGURATION_RECORD_SIZE + configOBUs.remaining() + + override fun write(output: ByteBuffer) { + output.put((marker.toInt() shl 7) or version.toInt()) + output.put((seqProfile shl 5) or seqLevelIdx0.toInt()) + output.putShort( + (seqTier0 shl 15) or + (highBitdepth shl 14) or + (twelveBit shl 13) or + (monochrome shl 12) or + (chromaSubsamplingX shl 11) or + (chromaSubsamplingY shl 10) or + (chromaSamplePosition shl 8) or + if (initialPresentationDelayMinusOne != null) { + (0b1 shl 4) or (initialPresentationDelayMinusOne) + } else { + 0 + } + ) + + output.put(configOBUs) + } + + companion object { + private const val AV1_DECODER_CONFIGURATION_RECORD_SIZE = 4 + + /** + * {max-bitrate=2000000, crop-right=719, level=32, latency=0, mime=video/av01, profile=1, + * bitrate=2000000, priority=0, color-standard=1, color-transfer=3, + * hdr10-plus-info=java.nio.HeapByteBuffer[pos=0 lim=0 cap=0], crop-bottom=1279, + * video-qp-average=0, crop-left=0, width=720, bitrate-mode=2, color-range=2, crop-top=0, + * frame-rate=30, height=1280, csd-0=java.nio.HeapByteBuffer[pos=0 lim=4 cap=4]} + * + */ + fun fromMediaFormat(mediaFormat: MediaFormat): AV1CodecConfigurationRecord { + throw NotImplementedError("AV1CodecConfigurationRecord.fromMediaFormat() is not implemented") + } + } +} \ No newline at end of file diff --git a/core/src/main/java/io/github/thibaultbee/streampack/streamers/helpers/StreamerConfigurationHelper.kt b/core/src/main/java/io/github/thibaultbee/streampack/streamers/helpers/StreamerConfigurationHelper.kt index da2e5922c..366e5c891 100644 --- a/core/src/main/java/io/github/thibaultbee/streampack/streamers/helpers/StreamerConfigurationHelper.kt +++ b/core/src/main/java/io/github/thibaultbee/streampack/streamers/helpers/StreamerConfigurationHelper.kt @@ -176,21 +176,13 @@ open class VideoStreamerConfigurationHelper(private val videoMuxerHelper: IVideo MediaFormat.MIMETYPE_VIDEO_AVC -> avcProfiles MediaFormat.MIMETYPE_VIDEO_HEVC -> hevcProfiles MediaFormat.MIMETYPE_VIDEO_VP9 -> vp9Profiles + MediaFormat.MIMETYPE_VIDEO_AV1 -> av1Profiles else -> throw InvalidParameterException("Unknown mimetype $mimeType") } val supportedProfiles = MediaCodecHelper.getProfiles(mimeType) return supportedProfiles.filter { profiles.contains(it) } } - private val vp9Profiles = listOf( - VP9Profile0, - VP9Profile1 - ) - - private val hevcProfiles = listOf( - HEVCProfileMain - ) - private val avcProfiles = listOf( AVCProfileBaseline, AVCProfileConstrainedBaseline, @@ -199,4 +191,18 @@ open class VideoStreamerConfigurationHelper(private val videoMuxerHelper: IVideo AVCProfileHigh, AVCProfileMain ) + + private val hevcProfiles = listOf( + HEVCProfileMain + ) + + private val vp9Profiles = listOf( + VP9Profile0, + VP9Profile1 + ) + + private val av1Profiles = listOf( + AV1ProfileMain8 + ) } + diff --git a/demos/camera/src/main/java/io/github/thibaultbee/streampack/app/ui/settings/SettingsFragment.kt b/demos/camera/src/main/java/io/github/thibaultbee/streampack/app/ui/settings/SettingsFragment.kt index c14634ec8..7c0ff4844 100644 --- a/demos/camera/src/main/java/io/github/thibaultbee/streampack/app/ui/settings/SettingsFragment.kt +++ b/demos/camera/src/main/java/io/github/thibaultbee/streampack/app/ui/settings/SettingsFragment.kt @@ -170,7 +170,8 @@ class SettingsFragment : PreferenceFragmentCompat() { MediaFormat.MIMETYPE_VIDEO_AVC to getString(R.string.video_encoder_h264), MediaFormat.MIMETYPE_VIDEO_HEVC to getString(R.string.video_encoder_h265), MediaFormat.MIMETYPE_VIDEO_H263 to getString(R.string.video_encoder_h263), - MediaFormat.MIMETYPE_VIDEO_VP9 to getString(R.string.video_encoder_vp9) + MediaFormat.MIMETYPE_VIDEO_VP9 to getString(R.string.video_encoder_vp9), + MediaFormat.MIMETYPE_VIDEO_AV1 to getString(R.string.video_encoder_av1) ) val supportedVideoEncoder = streamerHelper.video.supportedEncoders diff --git a/demos/camera/src/main/java/io/github/thibaultbee/streampack/app/utils/ProfileLevelDisplay.kt b/demos/camera/src/main/java/io/github/thibaultbee/streampack/app/utils/ProfileLevelDisplay.kt index bbb900ea5..b0b7daf4e 100644 --- a/demos/camera/src/main/java/io/github/thibaultbee/streampack/app/utils/ProfileLevelDisplay.kt +++ b/demos/camera/src/main/java/io/github/thibaultbee/streampack/app/utils/ProfileLevelDisplay.kt @@ -13,6 +13,34 @@ import android.media.MediaCodecInfo.CodecProfileLevel.AACObjectMain import android.media.MediaCodecInfo.CodecProfileLevel.AACObjectSSR import android.media.MediaCodecInfo.CodecProfileLevel.AACObjectScalable import android.media.MediaCodecInfo.CodecProfileLevel.AACObjectXHE +import android.media.MediaCodecInfo.CodecProfileLevel.AV1Level2 +import android.media.MediaCodecInfo.CodecProfileLevel.AV1Level21 +import android.media.MediaCodecInfo.CodecProfileLevel.AV1Level22 +import android.media.MediaCodecInfo.CodecProfileLevel.AV1Level23 +import android.media.MediaCodecInfo.CodecProfileLevel.AV1Level3 +import android.media.MediaCodecInfo.CodecProfileLevel.AV1Level31 +import android.media.MediaCodecInfo.CodecProfileLevel.AV1Level32 +import android.media.MediaCodecInfo.CodecProfileLevel.AV1Level33 +import android.media.MediaCodecInfo.CodecProfileLevel.AV1Level4 +import android.media.MediaCodecInfo.CodecProfileLevel.AV1Level41 +import android.media.MediaCodecInfo.CodecProfileLevel.AV1Level42 +import android.media.MediaCodecInfo.CodecProfileLevel.AV1Level43 +import android.media.MediaCodecInfo.CodecProfileLevel.AV1Level5 +import android.media.MediaCodecInfo.CodecProfileLevel.AV1Level51 +import android.media.MediaCodecInfo.CodecProfileLevel.AV1Level52 +import android.media.MediaCodecInfo.CodecProfileLevel.AV1Level53 +import android.media.MediaCodecInfo.CodecProfileLevel.AV1Level6 +import android.media.MediaCodecInfo.CodecProfileLevel.AV1Level61 +import android.media.MediaCodecInfo.CodecProfileLevel.AV1Level62 +import android.media.MediaCodecInfo.CodecProfileLevel.AV1Level63 +import android.media.MediaCodecInfo.CodecProfileLevel.AV1Level7 +import android.media.MediaCodecInfo.CodecProfileLevel.AV1Level71 +import android.media.MediaCodecInfo.CodecProfileLevel.AV1Level72 +import android.media.MediaCodecInfo.CodecProfileLevel.AV1Level73 +import android.media.MediaCodecInfo.CodecProfileLevel.AV1ProfileMain10 +import android.media.MediaCodecInfo.CodecProfileLevel.AV1ProfileMain10HDR10 +import android.media.MediaCodecInfo.CodecProfileLevel.AV1ProfileMain10HDR10Plus +import android.media.MediaCodecInfo.CodecProfileLevel.AV1ProfileMain8 import android.media.MediaCodecInfo.CodecProfileLevel.AVCLevel1 import android.media.MediaCodecInfo.CodecProfileLevel.AVCLevel11 import android.media.MediaCodecInfo.CodecProfileLevel.AVCLevel12 @@ -178,6 +206,19 @@ class ProfileLevelDisplay(private val context: Context) { } } + private val av1ProfileNameMap = + mutableMapOf().apply { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + put(AV1ProfileMain8, context.getString(R.string.video_profile_main)) + put(AV1ProfileMain10, context.getString(R.string.video_profile_main10)) + put(AV1ProfileMain10HDR10, context.getString(R.string.video_profile_main10_hdr10)) + put( + AV1ProfileMain10HDR10Plus, + context.getString(R.string.video_profile_main10_hdr10_plus) + ) + } + } + private val avcLevelNameMap = mutableMapOf( AVCLevel1 to context.getString(R.string.video_level_1), @@ -263,14 +304,51 @@ class ProfileLevelDisplay(private val context: Context) { } } - fun getProfileName(mimeType: String, profile: Int): String { - val nameMap = when (mimeType) { - MediaFormat.MIMETYPE_AUDIO_AAC -> aacProfileNameMap - MediaFormat.MIMETYPE_VIDEO_AVC -> avcProfileNameMap - MediaFormat.MIMETYPE_VIDEO_HEVC -> hevcProfileNameMap - MediaFormat.MIMETYPE_VIDEO_VP9 -> vp9ProfileNameMap - else -> emptyMap() + private val av1LevelNameMap = + mutableMapOf().apply { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + putAll( + mapOf( + AV1Level2 to context.getString(R.string.video_level_2), + AV1Level21 to context.getString(R.string.video_level_21), + AV1Level22 to context.getString(R.string.video_level_22), + AV1Level23 to context.getString(R.string.video_level_21), + AV1Level3 to context.getString(R.string.video_level_3), + AV1Level31 to context.getString(R.string.video_level_31), + AV1Level32 to context.getString(R.string.video_level_32), + AV1Level33 to context.getString(R.string.video_level_33), + AV1Level4 to context.getString(R.string.video_level_4), + AV1Level41 to context.getString(R.string.video_level_41), + AV1Level42 to context.getString(R.string.video_level_42), + AV1Level43 to context.getString(R.string.video_level_43), + AV1Level5 to context.getString(R.string.video_level_5), + AV1Level51 to context.getString(R.string.video_level_51), + AV1Level52 to context.getString(R.string.video_level_52), + AV1Level53 to context.getString(R.string.video_level_53), + AV1Level6 to context.getString(R.string.video_level_6), + AV1Level61 to context.getString(R.string.video_level_61), + AV1Level62 to context.getString(R.string.video_level_62), + AV1Level63 to context.getString(R.string.video_level_63), + AV1Level7 to context.getString(R.string.video_level_7), + AV1Level71 to context.getString(R.string.video_level_71), + AV1Level72 to context.getString(R.string.video_level_72), + AV1Level73 to context.getString(R.string.video_level_73), + ) + ) + } } + + private fun getProfileMap(mimeType: String) = when (mimeType) { + MediaFormat.MIMETYPE_AUDIO_AAC -> aacProfileNameMap + MediaFormat.MIMETYPE_VIDEO_AVC -> avcProfileNameMap + MediaFormat.MIMETYPE_VIDEO_HEVC -> hevcProfileNameMap + MediaFormat.MIMETYPE_VIDEO_VP9 -> vp9ProfileNameMap + MediaFormat.MIMETYPE_VIDEO_AV1 -> av1ProfileNameMap + else -> emptyMap() + } + + fun getProfileName(mimeType: String, profile: Int): String { + val nameMap = getProfileMap(mimeType) return try { nameMap[profile]!! } catch (_: Exception) { @@ -279,20 +357,15 @@ class ProfileLevelDisplay(private val context: Context) { } fun getProfile(mimeType: String, name: String): Int { - val nameMap = when (mimeType) { - MediaFormat.MIMETYPE_AUDIO_AAC -> aacProfileNameMap - MediaFormat.MIMETYPE_VIDEO_AVC -> avcProfileNameMap - MediaFormat.MIMETYPE_VIDEO_HEVC -> hevcProfileNameMap - MediaFormat.MIMETYPE_VIDEO_VP9 -> vp9ProfileNameMap - else -> emptyMap() - } - return nameMap.entries.find { it.value == name }!!.key + val nameMap = getProfileMap(mimeType) + return nameMap.entries.first { it.value == name }.key } private fun getLevelMap(mimeType: String) = when (mimeType) { MediaFormat.MIMETYPE_VIDEO_AVC -> avcLevelNameMap MediaFormat.MIMETYPE_VIDEO_HEVC -> hevcLevelNameMap MediaFormat.MIMETYPE_VIDEO_VP9 -> vp9LevelNameMap + MediaFormat.MIMETYPE_VIDEO_AV1 -> av1LevelNameMap else -> emptyMap() } @@ -307,7 +380,7 @@ class ProfileLevelDisplay(private val context: Context) { fun getLevel(mimeType: String, name: String): Int { val nameMap = getLevelMap(mimeType) - return nameMap.entries.find { it.value == name }!!.key + return nameMap.entries.first { it.value == name }.key } fun getAllLevelSet(mimeType: String): Set { diff --git a/demos/camera/src/main/java/io/github/thibaultbee/streampack/app/utils/StreamerFactory.kt b/demos/camera/src/main/java/io/github/thibaultbee/streampack/app/utils/StreamerFactory.kt index 54a8fde75..6429c9969 100644 --- a/demos/camera/src/main/java/io/github/thibaultbee/streampack/app/utils/StreamerFactory.kt +++ b/demos/camera/src/main/java/io/github/thibaultbee/streampack/app/utils/StreamerFactory.kt @@ -76,14 +76,17 @@ class StreamerFactory( enableAudio = enableAudio, tsServiceInfo = tsServiceInfo ) + EndpointType.FLV_FILE -> CameraFlvFileStreamer( context, enableAudio = enableAudio ) + EndpointType.MP4_FILE -> CameraMp4FileStreamer( context, enableAudio = enableAudio ) + EndpointType.SRT -> CameraSrtLiveStreamer( context, enableAudio = enableAudio, @@ -91,27 +94,32 @@ class StreamerFactory( bitrateRegulatorFactory = bitrateRegulatorFactory, bitrateRegulatorConfig = bitrateRegulatorConfig ) + EndpointType.RTMP -> CameraRtmpLiveStreamer( context, enableAudio = enableAudio ) } } + configuration.audio.enable -> { when (configuration.endpoint.endpointType) { EndpointType.TS_FILE -> AudioOnlyTsFileStreamer( context, tsServiceInfo = tsServiceInfo ) + EndpointType.FLV_FILE -> AudioOnlyFlvFileStreamer(context) EndpointType.MP4_FILE -> AudioOnlyMp4FileStreamer(context) EndpointType.SRT -> AudioOnlySrtLiveStreamer( context, tsServiceInfo = tsServiceInfo ) + EndpointType.RTMP -> AudioOnlyRtmpLiveStreamer(context) } } + else -> { throw IllegalStateException("StreamerFactory: You must enable at least one of audio or video") } diff --git a/demos/camera/src/main/res/values/strings.xml b/demos/camera/src/main/res/values/strings.xml index 4ce52af54..3f8545550 100644 --- a/demos/camera/src/main/res/values/strings.xml +++ b/demos/camera/src/main/res/values/strings.xml @@ -79,6 +79,7 @@ AVC/H.264 HEVC/H.265 VP9 + AV1 AAC server_stream_id_key Stream ID @@ -148,18 +149,27 @@ Level2 Level21 Level22 + Level23 Level3 Level31 Level32 + Level33 Level4 Level41 Level42 + Level43 Level5 Level51 Level52 + Level53 Level6 Level61 Level62 + Level63 + Level7 + Level71 + Level72 + Level73 MainLevel1 HighLevel1