diff --git a/gst/audioparsers/gstaacparse.c b/gst/audioparsers/gstaacparse.c index 784a2faa2b..ce7f6fe132 100644 --- a/gst/audioparsers/gstaacparse.c +++ b/gst/audioparsers/gstaacparse.c @@ -69,6 +69,9 @@ GST_DEBUG_CATEGORY_STATIC (aacparse_debug); #define ADTS_MAX_SIZE 10 /* Should be enough */ #define LOAS_MAX_SIZE 3 /* Should be enough */ +#define ADTS_HEADERS_LENGTH 7UL /* Total byte-length of fixed and variable + headers prepended during raw to ADTS + conversion */ #define AAC_FRAME_DURATION(parse) (GST_SECOND/parse->frames_per_sec) @@ -233,6 +236,16 @@ gst_aac_parse_set_src_caps (GstAacParse * aacparse, GstCaps * sink_caps) gst_caps_set_simple (src_caps, "codec_data", GST_TYPE_BUFFER, codec_data, NULL); } + } else if (aacparse->header_type == DSPAAC_HEADER_NONE) { + GST_DEBUG_OBJECT (GST_BASE_PARSE (aacparse)->srcpad, + "Input is raw, trying ADTS"); + gst_caps_set_simple (src_caps, "stream-format", G_TYPE_STRING, "adts", + NULL); + if (gst_caps_can_intersect (src_caps, allowed)) { + GST_DEBUG_OBJECT (GST_BASE_PARSE (aacparse)->srcpad, + "Caps can intersect, we will prepend ADTS headers"); + aacparse->output_header_type = DSPAAC_HEADER_ADTS; + } } } gst_caps_unref (allowed); @@ -303,7 +316,8 @@ gst_aac_parse_sink_setcaps (GstBaseParse * parse, GstCaps * caps) /* arrange for metadata and get out of the way */ gst_aac_parse_set_src_caps (aacparse, caps); - gst_base_parse_set_passthrough (parse, TRUE); + if (aacparse->header_type == aacparse->output_header_type) + gst_base_parse_set_passthrough (parse, TRUE); } else return FALSE; @@ -933,6 +947,184 @@ gst_aac_parse_detect_stream (GstAacParse * aacparse, return FALSE; } +/** + * gst_aac_parse_get_audio_profile_object_type + * @aacparse: #GstAacParse. + * + * Gets the MPEG-2 profile or the MPEG-4 object type value corresponding to the + * mpegversion and profile of @aacparse's src pad caps, according to the + * values defined by table 1.A.11 in ISO/IEC 14496-3. + * + * Returns: the profile or object type value corresponding to @aacparse's src + * pad caps, if such a value exists; otherwise G_MAXUINT8. + */ +static guint8 +gst_aac_parse_get_audio_profile_object_type (GstAacParse * aacparse) +{ + GstCaps *srccaps; + GstStructure *srcstruct; + const gchar *profile; + guint8 ret; + + srccaps = gst_pad_get_current_caps (GST_BASE_PARSE_SRC_PAD (aacparse)); + srcstruct = gst_caps_get_structure (srccaps, 0); + profile = gst_structure_get_string (srcstruct, "profile"); + if (G_UNLIKELY (profile == NULL)) { + gst_caps_unref (srccaps); + return G_MAXUINT8; + } + + if (g_strcmp0 (profile, "main") == 0) { + ret = (guint8) 0U; + } else if (g_strcmp0 (profile, "lc") == 0) { + ret = (guint8) 1U; + } else if (g_strcmp0 (profile, "ssr") == 0) { + ret = (guint8) 2U; + } else if (g_strcmp0 (profile, "ltp") == 0) { + if (G_LIKELY (aacparse->mpegversion == 4)) + ret = (guint8) 3U; + else + ret = G_MAXUINT8; /* LTP Object Type allowed only for MPEG-4 */ + } else { + ret = G_MAXUINT8; + } + + gst_caps_unref (srccaps); + return ret; +} + +/** + * gst_aac_parse_get_audio_channel_configuration + * @num_channels: number of audio channels. + * + * Gets the Channel Configuration value, as defined by table 1.19 in ISO/IEC + * 14496-3, for a given number of audio channels. + * + * Returns: the Channel Configuration value corresponding to @num_channels, if + * such a value exists; otherwise G_MAXUINT8. + */ +static guint8 +gst_aac_parse_get_audio_channel_configuration (gint num_channels) +{ + if (num_channels >= 1 && num_channels <= 6) /* Mono up to & including 5.1 */ + return (guint8) num_channels; + else if (num_channels == 8) /* 7.1 */ + return (guint8) 7U; + else + return G_MAXUINT8; +} + +/** + * gst_aac_parse_get_audio_sampling_frequency_index: + * @sample_rate: audio sampling rate. + * + * Gets the Sampling Frequency Index value, as defined by table 1.18 in ISO/IEC + * 14496-3, for a given sampling rate. + * + * Returns: the Sampling Frequency Index value corresponding to @sample_rate, + * if such a value exists; otherwise G_MAXUINT8. + */ +static guint8 +gst_aac_parse_get_audio_sampling_frequency_index (gint sample_rate) +{ + switch (sample_rate) { + case 96000: + return 0x0U; + case 88200: + return 0x1U; + case 64000: + return 0x2U; + case 48000: + return 0x3U; + case 44100: + return 0x4U; + case 32000: + return 0x5U; + case 24000: + return 0x6U; + case 22050: + return 0x7U; + case 16000: + return 0x8U; + case 12000: + return 0x9U; + case 11025: + return 0xAU; + case 8000: + return 0xBU; + case 7350: + return 0xCU; + default: + return G_MAXUINT8; + } +} + +/** + * gst_aac_parse_prepend_adts_headers: + * @aacparse: #GstAacParse. + * @frame: raw AAC frame to which ADTS headers shall be prepended. + * + * Prepends ADTS headers to a raw AAC audio frame. + * + * Returns: TRUE if ADTS headers were successfully prepended; FALSE otherwise. + */ +static gboolean +gst_aac_parse_prepend_adts_headers (GstAacParse * aacparse, + GstBaseParseFrame * frame) +{ + GstMemory *mem; + guint8 *adts_headers; + gsize buf_size; + gsize frame_size; + guint8 id, profile, channel_configuration, sampling_frequency_index; + + id = (aacparse->mpegversion == 4) ? 0x0U : 0x1U; + profile = gst_aac_parse_get_audio_profile_object_type (aacparse); + if (profile == G_MAXUINT8) { + GST_ERROR_OBJECT (aacparse, "Unsupported audio profile or object type"); + return FALSE; + } + channel_configuration = + gst_aac_parse_get_audio_channel_configuration (aacparse->channels); + if (channel_configuration == G_MAXUINT8) { + GST_ERROR_OBJECT (aacparse, "Unsupported number of channels"); + return FALSE; + } + sampling_frequency_index = + gst_aac_parse_get_audio_sampling_frequency_index (aacparse->sample_rate); + if (sampling_frequency_index == G_MAXUINT8) { + GST_ERROR_OBJECT (aacparse, "Unsupported sampling frequency"); + return FALSE; + } + + frame->out_buffer = gst_buffer_copy (frame->buffer); + buf_size = gst_buffer_get_size (frame->out_buffer); + frame_size = buf_size + ADTS_HEADERS_LENGTH; + + if (G_UNLIKELY (frame_size >= 0x4000)) { + GST_ERROR_OBJECT (aacparse, "Frame size is too big for ADTS"); + return FALSE; + } + + adts_headers = (guint8 *) g_malloc0 (ADTS_HEADERS_LENGTH); + + /* Note: no error correction bits are added to the resulting ADTS frames */ + adts_headers[0] = 0xFFU; + adts_headers[1] = 0xF0U | (id << 3) | 0x1U; + adts_headers[2] = (profile << 6) | (sampling_frequency_index << 2) | 0x2U | + (channel_configuration & 0x4U); + adts_headers[3] = ((channel_configuration & 0x3U) << 6) | 0x30U | + (guint8) (frame_size >> 11); + adts_headers[4] = (guint8) ((frame_size >> 3) & 0x00FF); + adts_headers[5] = (guint8) (((frame_size & 0x0007) << 5) + 0x1FU); + adts_headers[6] = 0xFCU; + + mem = gst_memory_new_wrapped (0, adts_headers, ADTS_HEADERS_LENGTH, 0, + ADTS_HEADERS_LENGTH, NULL, NULL); + gst_buffer_prepend_memory (frame->out_buffer, mem); + + return TRUE; +} /** * gst_aac_parse_check_valid_frame: @@ -1077,6 +1269,14 @@ gst_aac_parse_handle_frame (GstBaseParse * parse, } } + if (aacparse->header_type == DSPAAC_HEADER_NONE + && aacparse->output_header_type == DSPAAC_HEADER_ADTS) { + if (!gst_aac_parse_prepend_adts_headers (aacparse, frame)) { + GST_ERROR_OBJECT (aacparse, "Failed to prepend ADTS headers to frame"); + ret = GST_FLOW_ERROR; + } + } + exit: gst_buffer_unmap (buffer, &map);