159 lines
6.5 KiB
C++
159 lines
6.5 KiB
C++
// RFC6184 RTP Payload Format for H.264 Video
|
|
|
|
#include "mpeg4-aac.h"
|
|
#include "sdp-payload.h"
|
|
#include "base64.h"
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
|
|
// RFC6416 RTP Payload Format for MPEG-4 Audio/Visual Streams
|
|
int sdp_aac_latm(uint8_t *data, int bytes, const char* proto, unsigned short port, int payload, int sample_rate, int channel_count, const void* extra, int extra_size)
|
|
{
|
|
// In the presence of SBR, the sampling rates for the core encoder/
|
|
// decoder and the SBR tool are different in most cases. Therefore,
|
|
// this parameter SHALL NOT be considered as the definitive sampling rate.
|
|
// profile-level-id --> ISO/IEC 14496-3:2009 audioProfileLevelIndication values
|
|
static const char* pattern =
|
|
"m=audio %hu %s %d\r\n"
|
|
"a=rtpmap:%d MP4A-LATM/%d/%d\r\n"
|
|
"a=fmtp:%d profile-level-id=%d;object=%d;cpresent=0;config=";
|
|
|
|
int r, n;
|
|
uint8_t config[6];
|
|
struct mpeg4_aac_t aac;
|
|
|
|
//aac.profile = MPEG4_AAC_LC;
|
|
//aac.channel_configuration = (uint8_t)channel_count;
|
|
//aac.sampling_frequency_index = (uint8_t)mpeg4_aac_audio_frequency_from(sample_rate);
|
|
r = mpeg4_aac_audio_specific_config_load((const uint8_t*)extra, extra_size, &aac);
|
|
if (r < 0) return r;
|
|
|
|
sample_rate = 0 == sample_rate ? mpeg4_aac_audio_frequency_to(aac.sampling_frequency_index) : sample_rate;
|
|
channel_count = 0 == channel_count ? aac.channel_configuration : channel_count;
|
|
assert(aac.sampling_frequency_index == (uint8_t)mpeg4_aac_audio_frequency_from(sample_rate));
|
|
assert(aac.channel_configuration == channel_count);
|
|
|
|
r = mpeg4_aac_stream_mux_config_save(&aac, config, sizeof(config));
|
|
if (r < 0) return r;
|
|
|
|
// the "rate" parameter indicates the RTP timestamp "clock rate".
|
|
// The default value is 90000. Other rates MAY be indicated
|
|
// only if they are set to the same value as the audio sampling rate
|
|
sample_rate = 0 == sample_rate ? 90000 : sample_rate;
|
|
|
|
n = snprintf((char*)data, bytes, pattern, port,
|
|
proto && *proto ? proto : "RTP/AVP",
|
|
payload, payload, sample_rate, channel_count,
|
|
payload, mpeg4_aac_profile_level(&aac), aac.profile);
|
|
|
|
if (n + r * 2 + 1 > bytes)
|
|
return -ENOMEM; // don't have enough memory
|
|
n += (int)base16_encode((char*)data + n, config, r);
|
|
|
|
if (n + 2 > bytes)
|
|
return -ENOMEM; // don't have enough memory
|
|
|
|
data[n++] = '\r';
|
|
data[n++] = '\n';
|
|
return n;
|
|
}
|
|
|
|
// RFC 3640 3.3.1. General (p21)
|
|
int sdp_aac_generic(uint8_t *data, int bytes, const char* proto, unsigned short port, int payload, int sample_rate, int channel_count, const void* extra, int extra_size)
|
|
{
|
|
// a=rtpmap:<payload type> <encoding name>/<clock rate>[/<encoding parameters > ]
|
|
// For audio streams, <encoding parameters> specifies the number of audio channels, default value is 1.
|
|
|
|
// streamType: AudioStream --> ISO/IEC 14496-1:2010 streamType Values
|
|
// profile-level-id --> ISO/IEC 14496-3:2009 audioProfileLevelIndication values
|
|
// When using SDP, the clock rate of the RTP time stamp MUST be expressed using the "rtpmap" attribute.
|
|
// If an MPEG-4 audio stream is transported, the rate SHOULD be set to the same value as the sampling rate of the audio stream.
|
|
// If an MPEG-4 video stream transported, it is RECOMMENDED that the rate be set to 90 kHz.
|
|
static const char* pattern =
|
|
"m=audio %hu %s %d\r\n"
|
|
"a=rtpmap:%d mpeg4-generic/%d/%d\r\n"
|
|
"a=fmtp:%d streamtype=5;profile-level-id=%d;mode=AAC-hbr;sizeLength=13;indexLength=3;indexDeltaLength=3;config=";
|
|
|
|
int r, n;
|
|
struct mpeg4_aac_t aac;
|
|
|
|
r = mpeg4_aac_audio_specific_config_load((const uint8_t*)extra, extra_size, &aac);
|
|
if (r < 0) return r;
|
|
|
|
sample_rate = 0 == sample_rate ? mpeg4_aac_audio_frequency_to(aac.sampling_frequency_index) : sample_rate;
|
|
channel_count = 0 == channel_count ? aac.channel_configuration : channel_count;
|
|
assert(aac.sampling_frequency_index == (uint8_t)mpeg4_aac_audio_frequency_from(sample_rate));
|
|
assert(aac.channel_configuration == channel_count);
|
|
|
|
n = snprintf((char*)data, bytes, pattern, port, proto && *proto ? proto : "RTP/AVP", payload, payload, sample_rate, channel_count, payload, mpeg4_aac_profile_level(&aac));
|
|
|
|
if (n + extra_size * 2 + 1 > bytes)
|
|
return -ENOMEM; // don't have enough memory
|
|
|
|
// For MPEG-4 Audio streams, config is the audio object type specific
|
|
// decoder configuration data AudioSpecificConfig()
|
|
n += (int)base16_encode((char*)data + n, extra, extra_size);
|
|
|
|
if (n + 2 > bytes)
|
|
return -ENOMEM; // don't have enough memory
|
|
|
|
data[n++] = '\r';
|
|
data[n++] = '\n';
|
|
return n;
|
|
}
|
|
|
|
/// @return >0-ok, <=0-error
|
|
int sdp_aac_latm_load(uint8_t* data, int bytes, const char* config)
|
|
{
|
|
int n;
|
|
uint8_t buf[128];
|
|
struct mpeg4_aac_t aac;
|
|
|
|
n = (int)strlen(config);
|
|
if (n / 2 > sizeof(buf))
|
|
return -E2BIG;
|
|
|
|
n = (int)base16_decode(buf, config, n);
|
|
n = mpeg4_aac_stream_mux_config_load(buf, n, &aac);
|
|
return n <= 0 ? n : mpeg4_aac_audio_specific_config_save(&aac, data, bytes);
|
|
}
|
|
|
|
/// @return >0-ok, <=0-error
|
|
int sdp_aac_mpeg4_load(uint8_t* data, int bytes, const char* config)
|
|
{
|
|
int n;
|
|
n = (int)strlen(config);
|
|
if (n / 2 > bytes)
|
|
return -E2BIG;
|
|
|
|
return (int)base16_decode(data, config, n);
|
|
}
|
|
|
|
#if defined(_DEBUG) || defined(DEBUG)
|
|
void sdp_aac_test(void)
|
|
{
|
|
// rfc3640
|
|
//const unsigned char celpcbr[] = { "440E00" }; // 16000/1 streamtype=5; profile-level-id=14
|
|
//const unsigned char celpvbr[] = { "440F20" }; // 16000/1 streamtype=5; profile-level-id=14
|
|
//const unsigned char aaclbr[] = { "1388" }; // 22050/1 streamtype=5; profile-level-id=14
|
|
//const unsigned char aachbr[] = { "11B0" }; // 48000/6 streamtype=5; profile-level-id=16
|
|
|
|
const uint8_t config[] = { 0x11, 0x90, };
|
|
//const char* mpeg4_generic_sdp = "m=audio 0 RTP/AVP 96\r\na=rtpmap:96 mpeg4-generic/48000/2\r\na=fmtp:96 streamtype=5; profile-level-id=15; mode=AAC-hbr; config=1190; SizeLength=13; IndexLength=3; IndexDeltaLength=3; Profile=1\r\n";
|
|
const char* mpeg4_generic_sdp = "m=audio 0 RTP/AVP 96\r\na=rtpmap:96 mpeg4-generic/48000/2\r\na=fmtp:96 streamtype=5;profile-level-id=41;mode=AAC-hbr;sizeLength=13;indexLength=3;indexDeltaLength=3;config=1190\r\n";
|
|
//const char* mp4a_latm_sdp = "m=audio 0 RTP/AVP 96\r\na=rtpmap:96 MP4A-LATM/48000/2\r\na=fmtp:96 profile-level-id=9;object=8;cpresent=0;config=9128B1071070\r\n";
|
|
uint8_t buffer[256];
|
|
//struct mpeg4_aac_t aac;
|
|
//int n;
|
|
|
|
assert((int)strlen(mpeg4_generic_sdp) == sdp_aac_generic(buffer, sizeof(buffer), "RTP/AVP", 0, 96, 48000, 2, config, sizeof(config)));
|
|
assert(0 == memcmp(mpeg4_generic_sdp, buffer, strlen(mpeg4_generic_sdp)));
|
|
assert(sizeof(config) == sdp_aac_mpeg4_load(buffer, sizeof(buffer), "1190"));
|
|
assert(0 == memcmp(config, buffer, sizeof(config)));
|
|
}
|
|
#endif
|