Skip to content

Commit

Permalink
Added support for "config" audio codec specific parameter
Browse files Browse the repository at this point in the history
  • Loading branch information
alexeyvasilyev committed Mar 16, 2021
1 parent a4bf001 commit 55c0464
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,17 @@ class AudioDecodeThread (
private val mimeType: String,
private val sampleRate: Int,
private val channelCount: Int,
private val codecConfig: ByteArray?,
private val audioFrameQueue: FrameQueue) : Thread() {

private val TAG: String = AudioDecodeThread::class.java.simpleName
private val DEBUG = true

companion object {
fun getAacDecoderConfigData(sampleRate: Int, channels: Int): ByteArray {
fun getAacDecoderConfigData(audioProfile: Int, sampleRate: Int, channels: Int): ByteArray {
// AOT_LC = 2
// 0001 0000 0000 0000
var extraDataAac = 2 shl 11
var extraDataAac = audioProfile shl 11
// Sample rate
when (sampleRate) {
7350 -> extraDataAac = extraDataAac or (0xC shl 7)
Expand Down Expand Up @@ -50,7 +51,7 @@ class AudioDecodeThread (
val decoder = MediaCodec.createDecoderByType(mimeType)
val format = MediaFormat.createAudioFormat(mimeType, sampleRate, channelCount)

val csd0 = getAacDecoderConfigData(sampleRate, channelCount)
val csd0 = codecConfig ?: getAacDecoderConfigData(MediaCodecInfo.CodecProfileLevel.AACObjectLC, sampleRate, channelCount)
val bb = ByteBuffer.wrap(csd0)
format.setByteBuffer("csd-0", bb)
format.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC)
Expand All @@ -59,16 +60,18 @@ class AudioDecodeThread (
decoder.start()

// Creating audio playback device
val bufferSize = AudioTrack.getMinBufferSize(sampleRate, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT)
val outChannel = if (channelCount > 1) AudioFormat.CHANNEL_OUT_STEREO else AudioFormat.CHANNEL_OUT_MONO
val outAudio = AudioFormat.ENCODING_PCM_16BIT
val bufferSize = AudioTrack.getMinBufferSize(sampleRate, outChannel, outAudio)
// Log.i(TAG, "sampleRate: $sampleRate, bufferSize: $bufferSize".format(sampleRate, bufferSize))
val audioTrack = AudioTrack(
AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_MEDIA)
.setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
.build(),
AudioFormat.Builder()
.setEncoding(AudioFormat.ENCODING_PCM_16BIT)
.setChannelMask(AudioFormat.CHANNEL_OUT_MONO)
.setEncoding(outAudio)
.setChannelMask(outChannel)
.setSampleRate(sampleRate)
.build(),
bufferSize,
Expand Down Expand Up @@ -100,9 +103,11 @@ class AudioDecodeThread (
e.printStackTrace()
}
}
// Log.i(TAG, "inIndex: ${inIndex}")

try {
val outIndex = decoder.dequeueOutputBuffer(bufferInfo, 10000)
// Log.w(TAG, "outIndex: ${outIndex}")
when (outIndex) {
MediaCodec.INFO_OUTPUT_FORMAT_CHANGED -> Log.d(TAG, "Decoder format changed: " + decoder.outputFormat)
MediaCodec.INFO_TRY_AGAIN_LATER -> if (DEBUG) Log.d(TAG, "No output from decoder available")
Expand All @@ -121,7 +126,7 @@ class AudioDecodeThread (
}
}
}
} catch (e: java.lang.Exception) {
} catch (e: Exception) {
e.printStackTrace()
}

Expand Down
10 changes: 7 additions & 3 deletions app/src/main/java/com/alexvas/rtsp/demo/ui/live/LiveFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ class LiveFragment : Fragment(), SurfaceHolder.Callback {
private var audioMimeType: String = ""
private var audioSampleRate: Int = 0
private var audioChannelCount: Int = 0
private var audioCodecConfig: ByteArray? = null

fun onRtspClientStarted() {
if (DEBUG) Log.v(TAG, "onRtspClientStarted()")
Expand All @@ -72,7 +73,7 @@ class LiveFragment : Fragment(), SurfaceHolder.Callback {
}
if (audioMimeType.isNotEmpty() && checkAudio!!.isChecked) {
Log.i(TAG, "Starting audio decoder with mime type \"$audioMimeType\"")
audioDecodeThread = AudioDecodeThread(audioMimeType, audioSampleRate, audioChannelCount, audioFrameQueue)
audioDecodeThread = AudioDecodeThread(audioMimeType, audioSampleRate, audioChannelCount, audioCodecConfig, audioFrameQueue)
audioDecodeThread?.start()
}
}
Expand Down Expand Up @@ -134,6 +135,7 @@ class LiveFragment : Fragment(), SurfaceHolder.Callback {
}
audioSampleRate = sdpInfo.audioTrack?.sampleRateHz!!
audioChannelCount = sdpInfo.audioTrack?.channels!!
audioCodecConfig = sdpInfo.audioTrack?.config
}
onRtspClientConnected()
}
Expand Down Expand Up @@ -163,12 +165,14 @@ class LiveFragment : Fragment(), SurfaceHolder.Callback {
}
Log.i(TAG, "NALs ($numNals): $textNals")
}
videoFrameQueue.push(FrameQueue.Frame(data, offset, length, timestamp))
if (length > 0)
videoFrameQueue.push(FrameQueue.Frame(data, offset, length, timestamp))
}

override fun onRtspAudioSampleReceived(data: ByteArray, offset: Int, length: Int, timestamp: Long) {
if (DEBUG) Log.v(TAG, "onRtspAudioSampleReceived(length=$length, timestamp=$timestamp)")
audioFrameQueue.push(FrameQueue.Frame(data, offset, length, timestamp))
if (length > 0)
audioFrameQueue.push(FrameQueue.Frame(data, offset, length, timestamp))
}

override fun onRtspConnecting() {
Expand Down
18 changes: 15 additions & 3 deletions library-rtsp/src/main/java/com/alexvas/rtsp/RtspClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
Expand Down Expand Up @@ -192,6 +193,7 @@ public static class AudioTrack extends Track {
public int sampleRateHz; // 16000, 8000
public int channels; // 1 - mono, 2 - stereo
public String mode; // AAC-lbr, AAC-hbr
public @Nullable byte[] config; // config=1210fff15081ffdffc
}

private static final String CRLF = "\r\n";
Expand Down Expand Up @@ -859,6 +861,7 @@ private static Track[] getTracksFromDescribeParams(@NonNull List<Pair<String, St
// a=fmtp:97 streamtype=5; profile-level-id=15; mode=AAC-hbr; config=1408; sizeLength=13; indexLength=3; indexDeltaLength=3; profile=1; bitrate=32000;
// a=fmtp:97 streamtype=5;profile-level-id=1;mode=AAC-hbr;sizelength=13;indexlength=3;indexdeltalength=3;config=1408
// a=fmtp:96 streamtype=5; profile-level-id=14; mode=AAC-lbr; config=1388; sizeLength=6; indexLength=2; indexDeltaLength=2; constantDuration=1024; maxDisplacement=5
// a=fmtp:96 profile-level-id=1;mode=AAC-hbr;sizelength=13;indexlength=3;indexdeltalength=3;config=1210fff15081ffdffc
} else if (param.second.startsWith("fmtp:")) {
// Video
if (currentTrack instanceof VideoTrack) {
Expand All @@ -872,6 +875,7 @@ private static Track[] getTracksFromDescribeParams(@NonNull List<Pair<String, St
// a=rtpmap:97 mpeg4-generic/16000/1
// a=rtpmap:97 MPEG4-GENERIC/16000
// a=rtpmap:97 G726-32/8000
// a=rtpmap:96 mpeg4-generic/44100/2
} else if (param.second.startsWith("rtpmap:")) {
// Video
if (currentTrack instanceof VideoTrack) {
Expand Down Expand Up @@ -1021,7 +1025,7 @@ private static void updateVideoTrackFromDescribeParam(@NonNull VideoTrack videoT
List<Pair<String, String>> params = getSdpAParams(param);
if (params != null) {
for (Pair<String, String> pair: params) {
switch (pair.first) {
switch (pair.first.toLowerCase()) {
case "sprop-parameter-sets":
{
String[] paramsSpsPps = TextUtils.split(pair.second, ",");
Expand Down Expand Up @@ -1051,14 +1055,22 @@ private static void updateVideoTrackFromDescribeParam(@NonNull VideoTrack videoT
}
}

@NonNull
private static byte[] getBytesFromHexString(@NonNull String config) {
// "1210fff1" -> [12, 10, ff, f1]
return new BigInteger(config ,16).toByteArray();
}

private static void updateAudioTrackFromDescribeParam(@NonNull AudioTrack audioTrack, @NonNull Pair<String, String> param) {
// a=fmtp:96 streamtype=5; profile-level-id=14; mode=AAC-lbr; config=1388; sizeLength=6; indexLength=2; indexDeltaLength=2; constantDuration=1024; maxDisplacement=5
// a=fmtp:97 streamtype=5;profile-level-id=1;mode=AAC-hbr;sizelength=13;indexlength=3;indexdeltalength=3;config=1408
// a=fmtp:96 profile-level-id=1;mode=AAC-hbr;sizelength=13;indexlength=3;indexdeltalength=3;config=1210fff15081ffdffc
List<Pair<String, String>> params = getSdpAParams(param);
if (params != null) {
if (params != null) {
for (Pair<String, String> pair: params) {
switch (pair.first) {
switch (pair.first.toLowerCase()) {
case "mode": audioTrack.mode = pair.second; break;
case "config": audioTrack.config = getBytesFromHexString(pair.second); break;
}
}
}
Expand Down

0 comments on commit 55c0464

Please sign in to comment.