diff --git a/gst/typefind/Makefile.am b/gst/typefind/Makefile.am index 131c4b22cb..b5d57668f4 100644 --- a/gst/typefind/Makefile.am +++ b/gst/typefind/Makefile.am @@ -1,8 +1,9 @@ plugin_LTLIBRARIES = libgsttypefindfunctions.la -libgsttypefindfunctions_la_SOURCES = gsttypefindfunctions.c +libgsttypefindfunctions_la_SOURCES = gsttypefindfunctions.c gstaacutil.c libgsttypefindfunctions_la_CFLAGS = $(GST_CFLAGS) $(GIO_CFLAGS) libgsttypefindfunctions_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) libgsttypefindfunctions_la_LIBADD = $(GST_LIBS) $(GIO_LIBS) libgsttypefindfunctions_la_LIBTOOLFLAGS = --tag=disable-static +noinst_HEADERS = gstaacutil.h diff --git a/gst/typefind/gstaacutil.c b/gst/typefind/gstaacutil.c new file mode 100644 index 0000000000..229b078841 --- /dev/null +++ b/gst/typefind/gstaacutil.c @@ -0,0 +1,176 @@ +/* GStreamer + * Copyright (C) 2010 Nokia Corporation + * Copyright (C) 2010 Collabora Multimedia + * Copyright (C) 2010 Arun Raghavan + * + * gstaacutil.c: collection of AAC helper utilities + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include + +#include "gstaacutil.h" + +/* FIXME: This file is duplicated in gst-plugins-* wherever needed, so if you + * update this file, please find all other instances and update them as well. + * This less-than-optimal setup is being used till there is a standard location + * for such common functionality. + */ + +/* Determines the level of a stream as defined in ISO/IEC 14496-3. The + * sample_frequency_index and channel_configuration must be got from the ESDS + * for MP4 files and the ADTS header for ADTS streams. + * + * For AAC LC streams, we assume that apply the constraints from the AAC audio + * profile. For AAC Main/LTP/SSR/..., we use the Main profile. + * + * FIXME: HE-AAC support is TBD. + * + * Returns -1 if the level could not be determined. + */ +gint +gst_aac_level_from_header (guint profile, guint rate, guint channel_config) +{ + /* Number of single channel elements, channel pair elements, low frequency + * elements, independently switched coupling channel elements, and + * dependently switched coupling channel elements. + * + * Note: The 2 CCE types are ignored for now as they require us to actually + * parse the first frame, and they are rarely found in actual streams. + */ + int num_sce = 0, num_cpe = 0, num_lfe = 0, num_cce_indep = 0, num_cce_dep = 0; + int num_channels; + /* Processor and RAM Complexity Units (calculated and "reference" for single + * channel) */ + int pcu, rcu, pcu_ref, rcu_ref; + + switch (channel_config) { + case 0: + /* Channel config is defined in the AudioObjectType's SpecificConfig, + * which requires some amount of digging through the headers. I only see + * this done in the MPEG conformance streams - FIXME */ + GST_WARNING ("Found a stream with channel configuration in the " + "AudioSpecificConfig. Please file a bug with a link to the media if " + "possible."); + return -1; + case 1: + /* front center */ + num_sce = 1; + break; + case 2: + /* front left and right */ + num_cpe = 1; + break; + case 3: + /* front left, right, and center */ + num_sce = 1; + num_cpe = 1; + break; + case 4: + /* front left, right, and center; rear surround */ + num_sce = 2; + num_cpe = 1; + break; + case 5: + /* front left, right, and center; rear left and right surround */ + num_sce = 1; + num_cpe = 2; + break; + case 6: + /* front left, right, center and LFE; rear left and right surround */ + num_sce = 1; + num_cpe = 2; + break; + case 7: + /* front left, right, center and LFE; outside front left and right; + * rear left and right surround */ + num_sce = 1; + num_cpe = 3; + num_lfe = 1; + break; + default: + GST_WARNING ("Unknown channel config in header: %d", channel_config); + return -1; + } + + switch (profile) { + case 0: /* NULL */ + GST_WARNING ("profile 0 is not a valid profile"); + return -1; + case 2: /* LC */ + pcu_ref = 3; + rcu_ref = 3; + break; + case 3: /* SSR */ + pcu_ref = 4; + rcu_ref = 3; + break; + case 4: /* LTP */ + pcu_ref = 4; + rcu_ref = 4; + break; + case 1: /* Main */ + default: + /* Other than a couple of ER profiles, Main is the worst-case */ + pcu_ref = 5; + rcu_ref = 5; + break; + } + + /* "fs_ref" is 48000 Hz for AAC Main/LC/SSR/LTP. SBR's fs_ref is defined as + * 24000/48000 (in/out), for SBR streams. Actual support is a FIXME */ + + pcu = ((float) rate / 48000) * pcu_ref * + ((2 * num_cpe) + num_sce + num_lfe + num_cce_indep + (0.3 * num_cce_dep)); + + rcu = ((float) rcu_ref) * (num_sce + (0.5 * num_lfe) + (0.5 * num_cce_indep) + + (0.4 * num_cce_dep)); + + if (num_cpe < 2) + rcu += (rcu_ref + (rcu_ref - 1)) * num_cpe; + else + rcu += (rcu_ref + (rcu_ref - 1) * ((2 * num_cpe) - 1)); + + num_channels = num_sce + (2 * num_cpe) + num_lfe; + + if (profile == 2) { + /* AAC LC => return the level as per the 'AAC Profile' */ + if (num_channels <= 2 && rate <= 24000 && pcu <= 3 && rcu <= 5) + return 1; + if (num_channels <= 2 && rate <= 48000 && pcu <= 6 && rcu <= 5) + return 2; + /* There is no level 3 for the AAC Profile */ + if (num_channels <= 5 && rate <= 48000 && pcu <= 19 && rcu <= 15) + return 4; + if (num_channels <= 5 && rate <= 96000 && pcu <= 38 && rcu <= 15) + return 5; + } else { + /* Return the level as per the 'Main Profile' */ + if (pcu < 40 && rcu < 20) + return 1; + if (pcu < 80 && rcu < 64) + return 2; + if (pcu < 160 && rcu < 128) + return 3; + if (pcu < 320 && rcu < 256) + return 4; + } + + GST_WARNING ("couldn't determine level: profile=%u,rate=%u,channel_config=%u," + "pcu=%d,rcu=%d", profile, rate, channel_config, pcu, rcu); + return -1; +} diff --git a/gst/typefind/gstaacutil.h b/gst/typefind/gstaacutil.h new file mode 100644 index 0000000000..4fe5a58cd9 --- /dev/null +++ b/gst/typefind/gstaacutil.h @@ -0,0 +1,43 @@ +/* GStreamer + * Copyright (C) 2010 Nokia Corporation + * Copyright (C) 2010 Collabora Multimedia + * Copyright (C) 2010 Arun Raghavan + * + * gstaacutil.h: collection of AAC helper utilities + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_AAC_UTIL_H__ +#define __GST_AAC_UTIL_H__ + +#include + +/* FIXME: This file is duplicated in gst-plugins-* wherever needed, so if you + * update this file, please find all other instances and update them as well. + * This less-than-optimal setup is being used till there is a standard location + * for such common functionality. + */ + +G_BEGIN_DECLS + +gint gst_aac_level_from_header (guint profile, + guint sample_freq_idx, + guint channel_config); + +G_END_DECLS + +#endif /* __GST_AAC_UTIL_H__*/ diff --git a/gst/typefind/gsttypefindfunctions.c b/gst/typefind/gsttypefindfunctions.c index 887dc2faed..cb30e8db99 100644 --- a/gst/typefind/gsttypefindfunctions.c +++ b/gst/typefind/gsttypefindfunctions.c @@ -26,6 +26,7 @@ #endif #include +#include /* don't want to add gio xdgmime typefinder if gio was disabled via configure */ #ifdef HAVE_GIO @@ -43,6 +44,8 @@ #include #include +#include "gstaacutil.h" + GST_DEBUG_CATEGORY_STATIC (type_find_debug); #define GST_CAT_DEFAULT type_find_debug @@ -652,6 +655,9 @@ aac_type_find (GstTypeFind * tf, gpointer unused) { /* LUT to convert the AudioObjectType from the ADTS header to a string */ static const gchar profile_to_string[][5] = { "main", "lc", "ssr", "ltp" }; + static const guint sample_freq[] = { 96000, 88200, 64000, 48000, 44100, + 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350 + }; DataScanCtx c = { 0, NULL, 0 }; while (c.offset < AAC_AMOUNT) { @@ -682,18 +688,54 @@ aac_type_find (GstTypeFind * tf, gpointer unused) snc = GST_READ_UINT16_BE (c.data + len); if ((snc & 0xfff6) == 0xfff0) { - gint mpegversion, profile; + guint mpegversion, sample_freq_idx, channel_config, profile, rate; + gint level; mpegversion = (c.data[1] & 0x08) ? 2 : 4; profile = c.data[2] >> 6; + sample_freq_idx = ((c.data[2] & 0x3c) >> 2); + channel_config = ((c.data[2] & 0x01) << 2) + (c.data[3] >> 6); + GST_DEBUG ("Found second ADTS-%d syncpoint at offset 0x%" G_GINT64_MODIFIER "x, framelen %u", mpegversion, c.offset, len); - gst_type_find_suggest_simple (tf, GST_TYPE_FIND_LIKELY, "audio/mpeg", - "framed", G_TYPE_BOOLEAN, FALSE, - "mpegversion", G_TYPE_INT, mpegversion, - "base-profile", G_TYPE_STRING, profile_to_string[profile], - "profile", G_TYPE_STRING, profile_to_string[profile], - "stream-type", G_TYPE_STRING, "adts", NULL); + + /* 0xd and 0xe are reserved. 0xf means the sample frequency is directly + * specified in the header, but that's not allowed for ADTS */ + if (sample_freq_idx > 0xc) { + GST_DEBUG ("Unexpected sample frequency index %d or wrong sync", + sample_freq_idx); + goto next; + } + + rate = sample_freq[sample_freq_idx]; + GST_LOG ("ADTS: profile=%u, rate=%u", profile, rate); + + /* ADTS counts profiles from 0 instead of 1 to save bits */ + level = gst_aac_level_from_header (profile + 1, rate, channel_config); + + if (level == -1) { + /* Could not determine the level */ + gst_type_find_suggest_simple (tf, GST_TYPE_FIND_LIKELY, "audio/mpeg", + "framed", G_TYPE_BOOLEAN, FALSE, + "mpegversion", G_TYPE_INT, mpegversion, + "stream-type", G_TYPE_STRING, "adts", + "base-profile", G_TYPE_STRING, profile_to_string[profile], + "profile", G_TYPE_STRING, profile_to_string[profile], NULL); + } else { + gchar level_str[16]; + + /* we use a string here because h.264 levels are also strings and + * there aren't a lot of levels, so it's not too awkward to not use + * and integer here and keep the field type consistent with h.264 */ + g_snprintf (level_str, sizeof (level_str), "%d", level); + gst_type_find_suggest_simple (tf, GST_TYPE_FIND_LIKELY, "audio/mpeg", + "framed", G_TYPE_BOOLEAN, FALSE, + "mpegversion", G_TYPE_INT, mpegversion, + "stream-type", G_TYPE_STRING, "adts", + "base-profile", G_TYPE_STRING, profile_to_string[profile], + "profile", G_TYPE_STRING, profile_to_string[profile], + "level", G_TYPE_STRING, level_str, NULL); + } break; }