Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] Add AAC Support #1966

Open
wants to merge 11 commits into
base: develop-pre-1.11.0
Choose a base branch
from
8 changes: 5 additions & 3 deletions samples/Samples.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ extern "C" {
#define NUMBER_OF_H264_FRAME_FILES 1500
#define NUMBER_OF_H265_FRAME_FILES 1500
#define NUMBER_OF_OPUS_FRAME_FILES 618
#define NUMBER_OF_AAC_FRAME_FILES 206
#define DEFAULT_FPS_VALUE 25
#define DEFAULT_VIDEO_HEIGHT_PIXELS 720
#define DEFAULT_VIDEO_WIDTH_PIXELS 1280
Expand All @@ -41,9 +42,10 @@ extern "C" {
#define DEFAULT_AUDIO_OPUS_BYTE_RATE (DEFAULT_AUDIO_OPUS_SAMPLE_RATE_HZ * DEFAULT_AUDIO_OPUS_CHANNELS * DEFAULT_AUDIO_OPUS_BITS_PER_SAMPLE) / 8
#define DEFAULT_AUDIO_AAC_BYTE_RATE (DEFAULT_AUDIO_AAC_SAMPLE_RATE_HZ * DEFAULT_AUDIO_AAC_CHANNELS * DEFAULT_AUDIO_AAC_BITS_PER_SAMPLE) / 8

#define SAMPLE_AUDIO_FRAME_DURATION (20 * HUNDREDS_OF_NANOS_IN_A_MILLISECOND)
#define SAMPLE_STATS_DURATION (60 * HUNDREDS_OF_NANOS_IN_A_SECOND)
#define SAMPLE_VIDEO_FRAME_DURATION (HUNDREDS_OF_NANOS_IN_A_SECOND / DEFAULT_FPS_VALUE)
#define SAMPLE_AUDIO_FRAME_DURATION (20 * HUNDREDS_OF_NANOS_IN_A_MILLISECOND)
#define SAMPLE_AUDIO_AAC_FRAME_DURATION (64 * HUNDREDS_OF_NANOS_IN_A_MILLISECOND)
#define SAMPLE_STATS_DURATION (60 * HUNDREDS_OF_NANOS_IN_A_SECOND)
#define SAMPLE_VIDEO_FRAME_DURATION (HUNDREDS_OF_NANOS_IN_A_SECOND / DEFAULT_FPS_VALUE)

#define SAMPLE_PRE_GENERATE_CERT TRUE
#define SAMPLE_PRE_GENERATE_CERT_PERIOD (1000 * HUNDREDS_OF_NANOS_IN_A_MILLISECOND)
Expand Down
1 change: 1 addition & 0 deletions src/source/Include_i.h
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ STATUS generateJSONSafeString(PCHAR, UINT32);
#include "Rtp/Codecs/RtpH264Payloader.h"
#include "Rtp/Codecs/RtpH265Payloader.h"
#include "Rtp/Codecs/RtpOpusPayloader.h"
#include "Rtp/Codecs/RtpAacPayloader.h"
#include "Rtp/Codecs/RtpG711Payloader.h"
#include "Metrics/Metrics.h"

Expand Down
7 changes: 7 additions & 0 deletions src/source/PeerConnection/PeerConnection.c
Original file line number Diff line number Diff line change
Expand Up @@ -1548,6 +1548,11 @@ STATUS addTransceiver(PRtcPeerConnection pPeerConnection, PRtcMediaStreamTrack p
clockRate = OPUS_CLOCKRATE;
break;

case RTC_CODEC_AAC:
depayFunc = depayAacFromRtpPayload;
clockRate = AAC_CLOCKRATE;
break;

case RTC_CODEC_MULAW:
case RTC_CODEC_ALAW:
depayFunc = depayG711FromRtpPayload;
Expand Down Expand Up @@ -1624,6 +1629,8 @@ STATUS addSupportedCodec(PRtcPeerConnection pPeerConnection, RTC_CODEC rtcCodec)
CHK_STATUS(hashTablePut(pKvsPeerConnection->pCodecTable, rtcCodec, DEFAULT_PAYLOAD_MULAW));
} else if (rtcCodec == RTC_CODEC_ALAW) {
CHK_STATUS(hashTablePut(pKvsPeerConnection->pCodecTable, rtcCodec, DEFAULT_PAYLOAD_ALAW));
} else if (rtcCodec == RTC_CODEC_AAC) {
CHK_STATUS(hashTablePut(pKvsPeerConnection->pCodecTable, rtcCodec, DEFAULT_PAYLOAD_AAC));
} else {
CHK_STATUS(hashTablePut(pKvsPeerConnection->pCodecTable, rtcCodec, 0));
}
Expand Down
5 changes: 5 additions & 0 deletions src/source/PeerConnection/Rtp.c
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,11 @@ STATUS writeFrame(PRtcRtpTransceiver pRtcRtpTransceiver, PFrame pFrame)
rtpTimestamp = CONVERT_TIMESTAMP_TO_RTP(OPUS_CLOCKRATE, pFrame->presentationTs);
break;

case RTC_CODEC_AAC:
rtpPayloadFunc = createPayloadForAac;
rtpTimestamp = CONVERT_TIMESTAMP_TO_RTP(AAC_CLOCKRATE, pFrame->presentationTs);
break;

case RTC_CODEC_MULAW:
case RTC_CODEC_ALAW:
rtpPayloadFunc = createPayloadForG711;
Expand Down
18 changes: 17 additions & 1 deletion src/source/PeerConnection/SessionDescription.c
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ STATUS setPayloadTypesForOffer(PHashTable codecTable)
CHK_STATUS(hashTableUpsert(codecTable, RTC_CODEC_ALAW, DEFAULT_PAYLOAD_ALAW));
CHK_STATUS(hashTableUpsert(codecTable, RTC_CODEC_VP8, DEFAULT_PAYLOAD_VP8));
CHK_STATUS(hashTableUpsert(codecTable, RTC_CODEC_OPUS, DEFAULT_PAYLOAD_OPUS));
CHK_STATUS(hashTableUpsert(codecTable, RTC_CODEC_AAC, DEFAULT_PAYLOAD_AAC));
CHK_STATUS(hashTableUpsert(codecTable, RTC_CODEC_H264_PROFILE_42E01F_LEVEL_ASYMMETRY_ALLOWED_PACKETIZATION_MODE, DEFAULT_PAYLOAD_H264));
CHK_STATUS(hashTableUpsert(codecTable, RTC_CODEC_H265, DEFAULT_PAYLOAD_H265));

Expand Down Expand Up @@ -215,6 +216,12 @@ STATUS setPayloadTypesFromOffer(PHashTable codecTable, PHashTable rtxTable, PSes
CHK_STATUS(hashTableUpsert(codecTable, RTC_CODEC_OPUS, parsedPayloadType));
}

CHK_STATUS(hashTableContains(codecTable, RTC_CODEC_AAC, &supportCodec));
if (supportCodec && (end = STRSTR(attributeValue, AAC_VALUE)) != NULL) {
CHK_STATUS(STRTOUI64(attributeValue, end - 1, 10, &parsedPayloadType));
CHK_STATUS(hashTableUpsert(codecTable, RTC_CODEC_AAC, parsedPayloadType));
}

CHK_STATUS(hashTableContains(codecTable, RTC_CODEC_VP8, &supportCodec));
if (supportCodec && (end = STRSTR(attributeValue, VP8_VALUE)) != NULL) {
CHK_STATUS(STRTOUI64(attributeValue, end - 1, 10, &parsedPayloadType));
Expand Down Expand Up @@ -715,6 +722,12 @@ STATUS populateSingleMediaSection(PKvsPeerConnection pKvsPeerConnection, PKvsRtp
CHK_ERR(amountWritten > 0, STATUS_INTERNAL_ERROR, "Full Opus fmtp could not be written");
attributeCount++;
}
} else if (pRtcMediaStreamTrack->codec == RTC_CODEC_AAC) {
STRCPY(pSdpMediaDescription->sdpAttributes[attributeCount].attributeName, "rtpmap");
amountWritten = SNPRINTF(pSdpMediaDescription->sdpAttributes[attributeCount].attributeValue,
SIZEOF(pSdpMediaDescription->sdpAttributes[attributeCount].attributeValue), "%" PRId64 " AAC/16000", payloadType);
CHK_ERR(amountWritten > 0, STATUS_INTERNAL_ERROR, "Full Aac rtpmap could not be written");
attributeCount++;
} else if (pRtcMediaStreamTrack->codec == RTC_CODEC_VP8) {
STRCPY(pSdpMediaDescription->sdpAttributes[attributeCount].attributeName, "rtpmap");
amountWritten = SNPRINTF(pSdpMediaDescription->sdpAttributes[attributeCount].attributeValue,
Expand Down Expand Up @@ -1272,6 +1285,9 @@ STATUS findTransceiversByRemoteDescription(PKvsPeerConnection pKvsPeerConnection
} else if (STRSTR(attributeValue, OPUS_VALUE) != NULL) {
supportCodec = TRUE;
rtcCodec = RTC_CODEC_OPUS;
} else if (STRSTR(attributeValue, AAC_VALUE) != NULL) {
supportCodec = TRUE;
rtcCodec = RTC_CODEC_AAC;
} else if (STRSTR(attributeValue, MULAW_VALUE) != NULL) {
supportCodec = TRUE;
rtcCodec = RTC_CODEC_MULAW;
Expand Down Expand Up @@ -1416,7 +1432,7 @@ STATUS setReceiversSsrc(PSessionDescription pRemoteSessionDescription, PDoubleLi
codec = pKvsRtpTransceiver->sender.track.codec;
isVideoCodec = (codec == RTC_CODEC_VP8 || codec == RTC_CODEC_H264_PROFILE_42E01F_LEVEL_ASYMMETRY_ALLOWED_PACKETIZATION_MODE ||
codec == RTC_CODEC_H265);
isAudioCodec = (codec == RTC_CODEC_MULAW || codec == RTC_CODEC_ALAW || codec == RTC_CODEC_OPUS);
isAudioCodec = (codec == RTC_CODEC_MULAW || codec == RTC_CODEC_ALAW || codec == RTC_CODEC_OPUS || codec == RTC_CODEC_AAC);

if (pKvsRtpTransceiver->jitterBufferSsrc == 0 &&
((isVideoCodec && isVideoMediaSection) || (isAudioCodec && isAudioMediaSection))) {
Expand Down
3 changes: 3 additions & 0 deletions src/source/PeerConnection/SessionDescription.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ extern "C" {
#define H264_VALUE "H264/90000"
#define H265_VALUE "H265/90000"
#define OPUS_VALUE "opus/48000"
#define AAC_VALUE "AAC/16000"
#define VP8_VALUE "VP8/90000"
#define MULAW_VALUE "PCMU/8000"
#define ALAW_VALUE "PCMA/8000"
Expand All @@ -44,6 +45,7 @@ extern "C" {
#define DEFAULT_PAYLOAD_MULAW (UINT64) 0
#define DEFAULT_PAYLOAD_ALAW (UINT64) 8
#define DEFAULT_PAYLOAD_OPUS (UINT64) 111
#define DEFAULT_PAYLOAD_AAC (UINT64) 96
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably remove these hardcoded payload types altogether: https://issues.webrtc.org/issues/42231779
Not necessary for part of this PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're using it as a part of setPayloadTypesForOffer actually

#define DEFAULT_PAYLOAD_VP8 (UINT64) 96
#define DEFAULT_PAYLOAD_H264 (UINT64) 125
#define DEFAULT_PAYLOAD_H265 (UINT64) 127
Expand Down Expand Up @@ -77,6 +79,7 @@ extern "C" {

#define VIDEO_CLOCKRATE (UINT64) 90000
#define OPUS_CLOCKRATE (UINT64) 48000
#define AAC_CLOCKRATE (UINT64) 16000
#define PCM_CLOCKRATE (UINT64) 8000

// https://tools.ietf.org/html/draft-holmer-rmcat-transport-wide-cc-extensions-01
Expand Down
75 changes: 75 additions & 0 deletions src/source/Rtp/Codecs/RtpAacPayloader.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
#define LOG_CLASS "RtpAacPayloader"

#include "../../Include_i.h"

STATUS createPayloadForAac(UINT32 mtu, PBYTE aacFrame, UINT32 aacFrameLength, PBYTE payloadBuffer, PUINT32 pPayloadLength, PUINT32 pPayloadSubLength,
PUINT32 pPayloadSubLenSize)
{
UNUSED_PARAM(mtu);
ENTERS();
STATUS retStatus = STATUS_SUCCESS;
UINT32 payloadLength = 0;
UINT32 payloadSubLenSize = 0;
BOOL sizeCalculationOnly = (payloadBuffer == NULL);

CHK(aacFrame != NULL && pPayloadSubLenSize != NULL && pPayloadLength != NULL && (sizeCalculationOnly || pPayloadSubLength != NULL),
STATUS_NULL_ARG);

payloadLength = aacFrameLength;
payloadSubLenSize = 1;

// Only return size if given buffer is NULL
CHK(!sizeCalculationOnly, retStatus);
CHK(payloadLength <= *pPayloadLength && payloadSubLenSize <= *pPayloadSubLenSize, STATUS_BUFFER_TOO_SMALL);

MEMCPY(payloadBuffer, aacFrame, aacFrameLength);
pPayloadSubLength[0] = aacFrameLength;

CleanUp:
if (STATUS_FAILED(retStatus) && sizeCalculationOnly) {
payloadLength = 0;
payloadSubLenSize = 0;
}

if (pPayloadSubLenSize != NULL && pPayloadLength != NULL) {
*pPayloadLength = payloadLength;
*pPayloadSubLenSize = payloadSubLenSize;
}

LEAVES();
return retStatus;
}

STATUS depayAacFromRtpPayload(PBYTE pRawPacket, UINT32 packetLength, PBYTE pAacData, PUINT32 pAacLength, PBOOL pIsStart)
{
ENTERS();
STATUS retStatus = STATUS_SUCCESS;
UINT32 aacLength = 0;
BOOL sizeCalculationOnly = (pAacData == NULL);

CHK(pRawPacket != NULL && pAacLength != NULL, STATUS_NULL_ARG);
CHK(packetLength > 0, retStatus);

aacLength = packetLength;

CHK(!sizeCalculationOnly, retStatus);
CHK(aacLength <= *pAacLength, STATUS_BUFFER_TOO_SMALL);

MEMCPY(pAacData, pRawPacket, aacLength);

CleanUp:
if (STATUS_FAILED(retStatus) && sizeCalculationOnly) {
aacLength = 0;
}

if (pAacLength != NULL) {
*pAacLength = aacLength;
}

if (pIsStart != NULL) {
*pIsStart = TRUE;
}

LEAVES();
return retStatus;
}
21 changes: 21 additions & 0 deletions src/source/Rtp/Codecs/RtpAacPayloader.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*******************************************
AAC RTP Payloader include file
*******************************************/
#ifndef __KINESIS_VIDEO_WEBRTC_CLIENT_RTPAACPAYLOADER_H
#define __KINESIS_VIDEO_WEBRTC_CLIENT_RTPAACPAYLOADER_H

#pragma once

#ifdef __cplusplus
extern "C" {
#endif

// https://www.rfc-editor.org/rfc/rfc6416

STATUS createPayloadForAac(UINT32, PBYTE, UINT32, PBYTE, PUINT32, PUINT32, PUINT32);
STATUS depayAacFromRtpPayload(PBYTE, UINT32, PBYTE, PUINT32, PBOOL);

#ifdef __cplusplus
}
#endif
#endif //__KINESIS_VIDEO_WEBRTC_CLIENT_RTPAACPAYLOADER_H
55 changes: 55 additions & 0 deletions tst/RtpFunctionalityTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,61 @@ TEST_F(RtpFunctionalityTest, packingUnpackingVerifySameOpusFrame)
MEMFREE(depayload);
}

TEST_F(RtpFunctionalityTest, packingUnpackingVerifySameAacFrame)
{
BYTE payload[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05};
PBYTE depayload = (PBYTE) MEMALLOC(1500); // This is more than max mtu
UINT32 depayloadSize = 1500;
UINT32 payloadLen = 6;
PayloadArray payloadArray;
UINT32 newPayloadSubLen = 0;

payloadArray.maxPayloadLength = 0;
payloadArray.maxPayloadSubLenSize = 0;
payloadArray.payloadBuffer = NULL;
payloadArray.payloadSubLength = NULL;

// First call for payload size and sub payload length size
EXPECT_EQ(STATUS_SUCCESS,
createPayloadForAac(DEFAULT_MTU_SIZE_BYTES, (PBYTE) &payload, payloadLen, NULL, &payloadArray.payloadLength, NULL,
&payloadArray.payloadSubLenSize));

if (payloadArray.payloadLength > payloadArray.maxPayloadLength) {
if (payloadArray.payloadBuffer != NULL) {
MEMFREE(payloadArray.payloadBuffer);
}
payloadArray.payloadBuffer = (PBYTE) MEMALLOC(payloadArray.payloadLength);
payloadArray.maxPayloadLength = payloadArray.payloadLength;
}
if (payloadArray.payloadSubLenSize > payloadArray.maxPayloadSubLenSize) {
if (payloadArray.payloadSubLength != NULL) {
MEMFREE(payloadArray.payloadSubLength);
}
payloadArray.payloadSubLength = (PUINT32) MEMALLOC(payloadArray.payloadSubLenSize * SIZEOF(UINT32));
payloadArray.maxPayloadSubLenSize = payloadArray.payloadSubLenSize;
}

// Second call with actual buffer to fill in data
EXPECT_EQ(STATUS_SUCCESS,
createPayloadForAac(DEFAULT_MTU_SIZE_BYTES, (PBYTE) &payload, payloadLen, payloadArray.payloadBuffer, &payloadArray.payloadLength,
payloadArray.payloadSubLength, &payloadArray.payloadSubLenSize));

EXPECT_EQ(1, payloadArray.payloadSubLenSize);
EXPECT_EQ(6, payloadArray.payloadSubLength[0]);

EXPECT_EQ(STATUS_SUCCESS, depayAacFromRtpPayload(payloadArray.payloadBuffer, payloadArray.payloadSubLength[0], NULL, &newPayloadSubLen, NULL));
EXPECT_EQ(6, newPayloadSubLen);

newPayloadSubLen = depayloadSize;
EXPECT_EQ(STATUS_SUCCESS,
depayAacFromRtpPayload(payloadArray.payloadBuffer, payloadArray.payloadSubLength[0], depayload, &newPayloadSubLen, NULL));
EXPECT_TRUE(MEMCMP(payload, depayload, newPayloadSubLen) == 0);

MEMFREE(payloadArray.payloadBuffer);
MEMFREE(payloadArray.payloadSubLength);
MEMFREE(depayload);
}

TEST_F(RtpFunctionalityTest, packingUnpackingVerifySameShortG711Frame)
{
BYTE payload[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05};
Expand Down
Loading