Commit 5767d653 authored by Alex Ashley's avatar Alex Ashley Committed by Arun Raghavan

codec-utils: support extension audio object type and sample rate

ISO 14496-3 defines that audioObjectType 5 is a special case that
indicates SBR is present and that an additional field has to be
parsed to find the true audioObjectType.

There are two ways of signaling SBR within an AAC stream - implicit
and explicit (see [1] section 4.2). When explicit signaling is used,
the presence of SBR data is signaled by means of the SBR
audioObjectType in the AudioSpecificConfig data.

Normally the sample rate is specified by an index into a
table of common sample rates. However index 0x0f is a special case
that indicates that the next 24 bits contain the real sample rate.

[1] https://www.telosalliance.com/support/A-closer-look-into-MPEG-4-High-Efficiency-AAC

Fixes #39
parent 4d603b00
Pipeline #14662 passed with stages
in 14 minutes and 33 seconds
......@@ -38,6 +38,7 @@
#include "pbutils.h"
#include <gst/base/base.h>
#include <gst/base/gstbitreader.h>
#include <gst/tag/tag.h>
#include <string.h>
......@@ -107,6 +108,83 @@ gst_codec_utils_aac_get_index_from_sample_rate (guint rate)
return -1;
}
static gboolean
gst_codec_utils_aac_get_audio_object_type (GstBitReader * br,
guint8 * audio_object_type)
{
guint8 aot;
if (!gst_bit_reader_get_bits_uint8 (br, &aot, 5))
return FALSE;
if (aot == 31) {
if (!gst_bit_reader_get_bits_uint8 (br, &aot, 6))
return FALSE;
aot += 32;
}
*audio_object_type = aot;
return TRUE;
}
static gboolean
gst_codec_utils_aac_get_audio_sample_rate (GstBitReader * br,
guint * sample_rate)
{
guint8 sampling_freq_index;
guint32 sampling_rate;
if (!gst_bit_reader_get_bits_uint8 (br, &sampling_freq_index, 4))
return FALSE;
if (sampling_freq_index == 0xf) {
if (!gst_bit_reader_get_bits_uint32 (br, &sampling_rate, 24))
return FALSE;
} else {
sampling_rate =
gst_codec_utils_aac_get_sample_rate_from_index (sampling_freq_index);
if (!sampling_rate)
return FALSE;
}
*sample_rate = sampling_rate;
return TRUE;
}
static gboolean
gst_codec_utils_aac_get_audio_object_type_full (GstBitReader * br,
guint8 * audio_object_type, guint8 * channel_config, guint * sample_rate)
{
guint8 aot, channels;
guint rate;
if (!gst_codec_utils_aac_get_audio_object_type (br, &aot))
return FALSE;
if (!gst_codec_utils_aac_get_audio_sample_rate (br, &rate))
return FALSE;
if (!gst_bit_reader_get_bits_uint8 (br, &channels, 4))
return FALSE;
/* 5 indicates SBR extension (i.e. HE-AAC) */
/* 29 indicates PS extension */
if (aot == 5 || aot == 29) {
if (!gst_codec_utils_aac_get_audio_sample_rate (br, &rate))
return FALSE;
if (!gst_codec_utils_aac_get_audio_object_type (br, &aot))
return FALSE;
}
*audio_object_type = aot;
*sample_rate = rate;
*channel_config = channels;
return TRUE;
}
/**
* gst_codec_utils_aac_get_sample_rate:
* @audio_config: (array length=len): a pointer to the AudioSpecificConfig
......@@ -124,13 +202,17 @@ gst_codec_utils_aac_get_index_from_sample_rate (guint rate)
guint
gst_codec_utils_aac_get_sample_rate (const guint8 * audio_config, guint len)
{
guint rate_index;
guint sample_rate = 0;
guint8 audio_object_type = 0, channel_config = 0;
GstBitReader br = GST_BIT_READER_INIT (audio_config, len);
if (len < 2)
return 0;
rate_index = ((audio_config[0] & 0x7) << 1) | ((audio_config[1] & 0x80) >> 7);
return gst_codec_utils_aac_get_sample_rate_from_index (rate_index);
gst_codec_utils_aac_get_audio_object_type_full (&br, &audio_object_type,
&channel_config, &sample_rate);
return sample_rate;
}
/**
......@@ -171,10 +253,8 @@ gst_codec_utils_aac_get_channels (const guint8 * audio_config, guint len)
* @len: Length of @audio_config in bytes
*
* Returns the profile of the given AAC stream as a string. The profile is
* determined using the AudioObjectType field which is in the first 5 bits of
* @audio_config.
*
* > HE-AAC support has not yet been implemented.
* normally determined using the AudioObjectType field which is in the first
* 5 bits of @audio_config
*
* Returns: The profile as a const string and %NULL if the profile could not be
* determined.
......@@ -182,29 +262,40 @@ gst_codec_utils_aac_get_channels (const guint8 * audio_config, guint len)
const gchar *
gst_codec_utils_aac_get_profile (const guint8 * audio_config, guint len)
{
guint profile;
const gchar *profile = NULL;
guint sample_rate;
guint8 audio_object_type, channel_config;
GstBitReader br = GST_BIT_READER_INIT (audio_config, len);
if (len < 1)
return NULL;
GST_MEMDUMP ("audio config", audio_config, len);
profile = audio_config[0] >> 3;
switch (profile) {
if (!gst_codec_utils_aac_get_audio_object_type_full (&br, &audio_object_type,
&channel_config, &sample_rate)) {
return NULL;
}
switch (audio_object_type) {
case 1:
return "main";
profile = "main";
break;
case 2:
return "lc";
profile = "lc";
break;
case 3:
return "ssr";
profile = "ssr";
break;
case 4:
return "ltp";
profile = "ltp";
break;
default:
GST_DEBUG ("Invalid profile idx: %u", audio_object_type);
break;
}
GST_DEBUG ("Invalid profile idx: %u", profile);
return NULL;
return profile;
}
/**
......@@ -221,21 +312,21 @@ gst_codec_utils_aac_get_profile (const guint8 * audio_config, guint len)
* The @audio_config parameter follows the following format, starting from the
* most significant bit of the first byte:
*
* * Bit 0:4 contains the AudioObjectType
* * Bit 0:4 contains the AudioObjectType (if this is 0x5, then the
* real AudioObjectType is carried after the rate and channel data)
* * Bit 5:8 contains the sample frequency index (if this is 0xf, then the
* next 24 bits define the actual sample frequency, and subsequent
* fields are appropriately shifted).
* * Bit 9:12 contains the channel configuration
*
* > HE-AAC support has not yet been implemented.
*
* Returns: The level as a const string and %NULL if the level could not be
* determined.
*/
const gchar *
gst_codec_utils_aac_get_level (const guint8 * audio_config, guint len)
{
int profile, sr_idx, channel_config, rate;
guint8 audio_object_type = 0xFF, channel_config = 0xFF;
guint rate;
/* Number of single channel elements, channel pair elements, low frequency
* elements, independently switched coupling channel elements, and
* dependently switched coupling channel elements.
......@@ -247,8 +338,9 @@ gst_codec_utils_aac_get_level (const guint8 * audio_config, guint len)
int num_channels;
/* Processor and RAM Complexity Units (calculated and "reference" for single
* channel) */
int pcu, rcu, pcu_ref, rcu_ref;
int pcu = -1, rcu = -1, pcu_ref, rcu_ref;
int ret = -1;
GstBitReader br = GST_BIT_READER_INIT (audio_config, len);
g_return_val_if_fail (audio_config != NULL, NULL);
......@@ -257,14 +349,10 @@ gst_codec_utils_aac_get_level (const guint8 * audio_config, guint len)
GST_MEMDUMP ("audio config", audio_config, len);
profile = audio_config[0] >> 3;
/* FIXME: add support for sr_idx = 0xf */
sr_idx = ((audio_config[0] & 0x7) << 1) | ((audio_config[1] & 0x80) >> 7);
rate = gst_codec_utils_aac_get_sample_rate_from_index (sr_idx);
channel_config = (audio_config[1] & 0x7f) >> 3;
if (rate == 0)
if (!gst_codec_utils_aac_get_audio_object_type_full (&br, &audio_object_type,
&channel_config, &rate)) {
return NULL;
}
switch (channel_config) {
case 0:
......@@ -322,7 +410,7 @@ gst_codec_utils_aac_get_level (const guint8 * audio_config, guint len)
return NULL;
}
switch (profile) {
switch (audio_object_type) {
case 0: /* NULL */
GST_WARNING ("profile 0 is not a valid profile");
return NULL;
......@@ -362,7 +450,7 @@ gst_codec_utils_aac_get_level (const guint8 * audio_config, guint len)
num_channels = num_sce + (2 * num_cpe) + num_lfe;
if (profile == 2) {
if (audio_object_type == 2) {
/* AAC LC => return the level as per the 'AAC Profile' */
if (num_channels <= 2 && rate <= 24000 && pcu <= 3 && rcu <= 5)
ret = 1;
......@@ -391,8 +479,8 @@ gst_codec_utils_aac_get_level (const guint8 * audio_config, guint len)
if (ret == -1) {
GST_WARNING ("couldn't determine level: profile=%u, rate=%u, "
"channel_config=%u, pcu=%d,rcu=%d", profile, rate, channel_config, pcu,
rcu);
"channel_config=%u, pcu=%d,rcu=%d", audio_object_type, rate,
channel_config, pcu, rcu);
return NULL;
} else {
return digit_to_string (ret);
......
......@@ -24,6 +24,7 @@
#include <gst/check/gstcheck.h>
#include <gst/pbutils/pbutils.h>
#include <gst/base/gstbitwriter.h>
#include <stdio.h>
#include <glib/gstdio.h>
......@@ -803,6 +804,69 @@ GST_START_TEST (test_pb_utils_versions)
GST_END_TEST;
GST_START_TEST (test_pb_utils_aac_get_profile)
{
const guint8 aac_config[] = { 0x11, 0x90, 0x56, 0xE5, 0x00 };
const guint8 aac_config_sre[] = { 0x17, 0x80, 0x91, 0xA2, 0x82, 0x00 };
const guint8 heaac_config[] = { 0x2B, 0x11, 0x88, 0x00, 0x06, 0x01, 0x02 };
const gchar *profile, *level;
guint sample_rate;
GstBitWriter *wr;
guint8 *buf;
guint buf_len;
profile = gst_codec_utils_aac_get_profile (aac_config, sizeof (aac_config));
fail_unless (profile != NULL);
fail_unless_equals_string (profile, "lc");
level = gst_codec_utils_aac_get_level (aac_config, sizeof (aac_config));
fail_unless_equals_string (level, "2");
sample_rate =
gst_codec_utils_aac_get_sample_rate (aac_config, sizeof (aac_config));
fail_unless_equals_int (sample_rate, 48000);
sample_rate =
gst_codec_utils_aac_get_sample_rate (aac_config_sre,
sizeof (aac_config_sre));
fail_unless_equals_int (sample_rate, 0x12345);
profile =
gst_codec_utils_aac_get_profile (heaac_config, sizeof (heaac_config));
fail_unless (profile != NULL);
fail_unless_equals_string (profile, "lc");
level = gst_codec_utils_aac_get_level (heaac_config, sizeof (heaac_config));
fail_unless_equals_string (level, "2");
sample_rate =
gst_codec_utils_aac_get_sample_rate (heaac_config, sizeof (heaac_config));
fail_unless_equals_int (sample_rate, 48000);
wr = gst_bit_writer_new ();
fail_if (wr == NULL);
gst_bit_writer_put_bits_uint8 (wr, 5, 5); /* object_type = 5 (SBR) */
gst_bit_writer_put_bits_uint8 (wr, 3, 4); /* freq_index = 3 (48KHz) */
gst_bit_writer_put_bits_uint8 (wr, 2, 4); /* channel_config = 2 (L&R) */
gst_bit_writer_put_bits_uint8 (wr, 0x0f, 4); /* freq_index extension */
gst_bit_writer_put_bits_uint32 (wr, 87654, 24); /* freq */
gst_bit_writer_put_bits_uint8 (wr, 2, 5); /* object_type = 2 (LC) */
buf = gst_bit_writer_get_data (wr);
buf_len = gst_bit_writer_get_size (wr);
profile = gst_codec_utils_aac_get_profile (buf, buf_len);
fail_unless (profile != NULL);
fail_unless_equals_string (profile, "lc");
level = gst_codec_utils_aac_get_level (buf, buf_len);
fail_unless (level != NULL);
fail_unless_equals_string (level, "5");
sample_rate = gst_codec_utils_aac_get_sample_rate (buf, buf_len);
fail_unless_equals_int (sample_rate, 87654);
gst_bit_writer_free (wr);
}
GST_END_TEST;
static Suite *
libgstpbutils_suite (void)
{
......@@ -817,6 +881,7 @@ libgstpbutils_suite (void)
tcase_add_test (tc_chain, test_pb_utils_install_plugins);
tcase_add_test (tc_chain, test_pb_utils_installer_details);
tcase_add_test (tc_chain, test_pb_utils_versions);
tcase_add_test (tc_chain, test_pb_utils_aac_get_profile);
return s;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment