X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=gst%2Faudioparsers%2Fgstaacparse.c;h=cd15f01f9e1ecf80983f600c807f7db7809a758b;hb=976ce2135bb039d6e8e5b8aa65471ca48c65bb78;hp=982d2428eca2125f563a91b00fcb1a7d17fb9e2a;hpb=3b970e9b5ece77dd9633234cfd4e0249c03310ee;p=platform%2Fupstream%2Fgst-plugins-good.git diff --git a/gst/audioparsers/gstaacparse.c b/gst/audioparsers/gstaacparse.c index 982d242..cd15f01 100644 --- a/gst/audioparsers/gstaacparse.c +++ b/gst/audioparsers/gstaacparse.c @@ -68,19 +68,29 @@ GST_DEBUG_CATEGORY_STATIC (aacparse_debug); #define ADIF_MAX_SIZE 40 /* Should be enough */ #define ADTS_MAX_SIZE 10 /* Should be enough */ #define LOAS_MAX_SIZE 3 /* Should be enough */ +#define RAW_MAX_SIZE 1 /* Correct framing is required */ #define ADTS_HEADERS_LENGTH 7UL /* Total byte-length of fixed and variable headers prepended during raw to ADTS conversion */ +#ifdef TIZEN_FEATURE_AACPARSE_MODIFICATION /* to get more accurate duration */ +#define AAC_MAX_ESTIMATE_DURATION_BUF (1024 * 1024) /* use first 1 Mbyte */ +#define AAC_SAMPLE_PER_FRAME 1024 + +#define AAC_MAX_PULL_RANGE_BUF (1 * 1024 * 1024) /* 1 MByte */ +#define AAC_LARGE_FILE_SIZE (2 * 1024 * 1024) /* 2 MByte */ +#define gst_aac_parse_parent_class parent_class +#endif + #define AAC_FRAME_DURATION(parse) (GST_SECOND/parse->frames_per_sec) -static const gint loas_sample_rate_table[32] = { +static const gint loas_sample_rate_table[16] = { 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350, 0, 0, 0 }; -static const gint loas_channels_table[32] = { +static const gint loas_channels_table[16] = { 0, 1, 2, 3, 4, 5, 6, 8, 0, 0, 0, 7, 8, 0, 8, 0 }; @@ -97,9 +107,41 @@ static GstFlowReturn gst_aac_parse_handle_frame (GstBaseParse * parse, GstBaseParseFrame * frame, gint * skipsize); static GstFlowReturn gst_aac_parse_pre_push_frame (GstBaseParse * parse, GstBaseParseFrame * frame); +static gboolean gst_aac_parse_src_event (GstBaseParse * parse, + GstEvent * event); + +static gboolean gst_aac_parse_read_audio_specific_config (GstAacParse * + aacparse, GstBitReader * br, gint * object_type, gint * sample_rate, + gint * channels, gint * frame_samples); + +#ifdef TIZEN_FEATURE_AACPARSE_MODIFICATION +static guint gst_aac_parse_adts_get_fast_frame_len (const guint8 * data); +/* make full aac(adts) index table when seek */ +static gboolean gst_aac_parse_adts_src_eventfunc (GstBaseParse * parse, + GstEvent * event); +int get_aac_parse_get_adts_frame_length (const unsigned char *data, + gint64 offset); +static gboolean gst_aac_parse_estimate_duration (GstBaseParse * parse); +static void gst_aac_parse_check_byte_seekability (GstBaseParse * parse); +#endif +#define gst_aac_parse_parent_class parent_class G_DEFINE_TYPE (GstAacParse, gst_aac_parse, GST_TYPE_BASE_PARSE); +#ifdef TIZEN_FEATURE_AACPARSE_MODIFICATION +static inline gint +gst_aac_parse_get_sample_rate_from_index (guint sr_idx) +{ + static const guint aac_sample_rates[] = { 96000, 88200, 64000, 48000, 44100, + 32000, 24000, 22050, 16000, 12000, 11025, 8000 + }; + + if (sr_idx < G_N_ELEMENTS (aac_sample_rates)) + return aac_sample_rates[sr_idx]; + GST_WARNING ("Invalid sample rate index %u", sr_idx); + return 0; +} +#endif /** * gst_aac_parse_class_init: * @klass: #GstAacParseClass. @@ -114,10 +156,8 @@ gst_aac_parse_class_init (GstAacParseClass * klass) GST_DEBUG_CATEGORY_INIT (aacparse_debug, "aacparse", 0, "AAC audio stream parser"); - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&sink_template)); - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&src_template)); + gst_element_class_add_static_pad_template (element_class, &sink_template); + gst_element_class_add_static_pad_template (element_class, &src_template); gst_element_class_set_static_metadata (element_class, "AAC audio stream parser", "Codec/Parser/Audio", @@ -130,6 +170,7 @@ gst_aac_parse_class_init (GstAacParseClass * klass) parse_class->handle_frame = GST_DEBUG_FUNCPTR (gst_aac_parse_handle_frame); parse_class->pre_push_frame = GST_DEBUG_FUNCPTR (gst_aac_parse_pre_push_frame); + parse_class->src_event = GST_DEBUG_FUNCPTR (gst_aac_parse_src_event); } @@ -145,6 +186,13 @@ gst_aac_parse_init (GstAacParse * aacparse) GST_DEBUG ("initialized"); GST_PAD_SET_ACCEPT_INTERSECT (GST_BASE_PARSE_SINK_PAD (aacparse)); GST_PAD_SET_ACCEPT_TEMPLATE (GST_BASE_PARSE_SINK_PAD (aacparse)); + + aacparse->last_parsed_sample_rate = 0; + aacparse->last_parsed_channels = 0; +#ifdef TIZEN_FEATURE_AACPARSE_MODIFICATION + /* to get more correct duration */ + aacparse->first_frame = TRUE; +#endif } @@ -215,6 +263,19 @@ gst_aac_parse_set_src_caps (GstAacParse * aacparse, GstCaps * sink_caps) if (stream_format) gst_structure_set (s, "stream-format", G_TYPE_STRING, stream_format, NULL); +#ifdef TIZEN_FEATURE_AACPARSE_MODIFICATION + if (!gst_structure_get_value (s, "codec_data")) { + GstBuffer *codec_data_buffer; + GST_WARNING("Insert codec_data to src_caps"); + /* The codec_data data is according to AudioSpecificConfig, + ISO/IEC 14496-3, 1.6.2.1 */ + codec_data_buffer = gst_buffer_new_and_alloc (2); + gst_buffer_fill (codec_data_buffer, 0, codec_data, 2); + gst_caps_set_simple (src_caps, "codec_data", GST_TYPE_BUFFER, codec_data_buffer, NULL); + gst_buffer_unref (codec_data_buffer); + } +#endif + allowed = gst_pad_get_allowed_caps (GST_BASE_PARSE (aacparse)->srcpad); if (allowed && !gst_caps_can_intersect (src_caps, allowed)) { GST_DEBUG_OBJECT (GST_BASE_PARSE (aacparse)->srcpad, @@ -237,6 +298,7 @@ gst_aac_parse_set_src_caps (GstAacParse * aacparse, GstCaps * sink_caps) gst_buffer_fill (codec_data_buffer, 0, codec_data, 2); gst_caps_set_simple (src_caps, "codec_data", GST_TYPE_BUFFER, codec_data_buffer, NULL); + gst_buffer_unref (codec_data_buffer); } } else if (aacparse->header_type == DSPAAC_HEADER_NONE) { GST_DEBUG_OBJECT (GST_BASE_PARSE (aacparse)->srcpad, @@ -253,6 +315,9 @@ gst_aac_parse_set_src_caps (GstAacParse * aacparse, GstCaps * sink_caps) if (allowed) gst_caps_unref (allowed); + aacparse->last_parsed_channels = 0; + aacparse->last_parsed_sample_rate = 0; + GST_DEBUG_OBJECT (aacparse, "setting src caps: %" GST_PTR_FORMAT, src_caps); res = gst_pad_set_caps (GST_BASE_PARSE (aacparse)->srcpad, src_caps); @@ -298,26 +363,19 @@ gst_aac_parse_sink_setcaps (GstBaseParse * parse, GstCaps * caps) if (value) { GstBuffer *buf = gst_value_get_buffer (value); - if (buf) { + if (buf && gst_buffer_get_size (buf) >= 2) { GstMapInfo map; - guint sr_idx; - - gst_buffer_map (buf, &map, GST_MAP_READ); - - sr_idx = ((map.data[0] & 0x07) << 1) | ((map.data[1] & 0x80) >> 7); - aacparse->object_type = (map.data[0] & 0xf8) >> 3; - aacparse->sample_rate = - gst_codec_utils_aac_get_sample_rate_from_index (sr_idx); - aacparse->channels = (map.data[1] & 0x78) >> 3; - if (aacparse->channels == 7) - aacparse->channels = 8; - else if (aacparse->channels == 11) - aacparse->channels = 7; - else if (aacparse->channels == 12 || aacparse->channels == 14) - aacparse->channels = 8; + GstBitReader br; + + if (!gst_buffer_map (buf, &map, GST_MAP_READ)) + return FALSE; + gst_bit_reader_init (&br, map.data, map.size); + gst_aac_parse_read_audio_specific_config (aacparse, &br, + &aacparse->object_type, &aacparse->sample_rate, &aacparse->channels, + &aacparse->frame_samples); + aacparse->header_type = DSPAAC_HEADER_NONE; aacparse->mpegversion = 4; - aacparse->frame_samples = (map.data[1] & 4) ? 960 : 1024; gst_buffer_unmap (buf, &map); GST_DEBUG ("codec_data: object_type=%d, sample_rate=%d, channels=%d, " @@ -328,19 +386,30 @@ gst_aac_parse_sink_setcaps (GstBaseParse * parse, GstCaps * caps) gst_aac_parse_set_src_caps (aacparse, caps); if (aacparse->header_type == aacparse->output_header_type) gst_base_parse_set_passthrough (parse, TRUE); - } else + + /* input is already correctly framed */ + gst_base_parse_set_min_frame_size (parse, RAW_MAX_SIZE); + } else { return FALSE; + } /* caps info overrides */ gst_structure_get_int (structure, "rate", &aacparse->sample_rate); gst_structure_get_int (structure, "channels", &aacparse->channels); } else { - aacparse->sample_rate = 0; - aacparse->channels = 0; - aacparse->header_type = DSPAAC_HEADER_NOT_PARSED; - gst_base_parse_set_passthrough (parse, FALSE); - } + const gchar *stream_format = + gst_structure_get_string (structure, "stream-format"); + if (g_strcmp0 (stream_format, "raw") == 0) { + GST_ERROR_OBJECT (parse, "Need codec_data for raw AAC"); + return FALSE; + } else { + aacparse->sample_rate = 0; + aacparse->channels = 0; + aacparse->header_type = DSPAAC_HEADER_NOT_PARSED; + gst_base_parse_set_passthrough (parse, FALSE); + } + } return TRUE; } @@ -474,7 +543,7 @@ gst_aac_parse_latm_get_value (GstAacParse * aacparse, GstBitReader * br, *value = 0; if (!gst_bit_reader_get_bits_uint8 (br, &bytes, 2)) return FALSE; - for (i = 0; i < bytes; ++i) { + for (i = 0; i <= bytes; ++i) { *value <<= 8; if (!gst_bit_reader_get_bits_uint8 (br, &byte, 8)) return FALSE; @@ -517,45 +586,89 @@ gst_aac_parse_get_audio_sample_rate (GstAacParse * aacparse, GstBitReader * br, if (!*sample_rate) return FALSE; } + aacparse->last_parsed_sample_rate = *sample_rate; return TRUE; } /* See table 1.13 in ISO/IEC 14496-3 */ static gboolean -gst_aac_parse_read_loas_audio_specific_config (GstAacParse * aacparse, - GstBitReader * br, gint * sample_rate, gint * channels, guint32 * bits) +gst_aac_parse_read_audio_specific_config (GstAacParse * aacparse, + GstBitReader * br, gint * object_type, gint * sample_rate, gint * channels, + gint * frame_samples) { - guint8 audio_object_type, channel_configuration; + guint8 audio_object_type; + guint8 G_GNUC_UNUSED extension_audio_object_type; + guint8 channel_configuration, extension_channel_configuration; + gboolean G_GNUC_UNUSED sbr = FALSE, ps = FALSE; if (!gst_aac_parse_get_audio_object_type (aacparse, br, &audio_object_type)) return FALSE; + if (object_type) + *object_type = audio_object_type; if (!gst_aac_parse_get_audio_sample_rate (aacparse, br, sample_rate)) return FALSE; if (!gst_bit_reader_get_bits_uint8 (br, &channel_configuration, 4)) return FALSE; - GST_LOG_OBJECT (aacparse, "channel_configuration: %d", channel_configuration); *channels = loas_channels_table[channel_configuration]; + GST_LOG_OBJECT (aacparse, "channel_configuration: %d", channel_configuration); if (!*channels) return FALSE; - if (audio_object_type == 5) { + if (audio_object_type == 5 || audio_object_type == 29) { + extension_audio_object_type = 5; + sbr = TRUE; + if (audio_object_type == 29) { + ps = TRUE; + /* Parametric stereo. If we have a one-channel configuration, we can + * override it to stereo */ + if (*channels == 1) + *channels = 2; + } + GST_LOG_OBJECT (aacparse, - "Audio object type 5, so rereading sampling rate..."); + "Audio object type 5 or 29, so rereading sampling rate (was %d)...", + *sample_rate); if (!gst_aac_parse_get_audio_sample_rate (aacparse, br, sample_rate)) return FALSE; + + if (!gst_aac_parse_get_audio_object_type (aacparse, br, &audio_object_type)) + return FALSE; + + if (audio_object_type == 22) { + /* extension channel configuration */ + if (!gst_bit_reader_get_bits_uint8 (br, &extension_channel_configuration, + 4)) + return FALSE; + GST_LOG_OBJECT (aacparse, "extension channel_configuration: %d", + extension_channel_configuration); + *channels = loas_channels_table[extension_channel_configuration]; + if (!*channels) + return FALSE; + } + } else { + extension_audio_object_type = 0; } - GST_INFO_OBJECT (aacparse, "Found LOAS config: %d Hz, %d channels", + GST_INFO_OBJECT (aacparse, "Parsed AudioSpecificConfig: %d Hz, %d channels", *sample_rate, *channels); + if (frame_samples && audio_object_type == 23) { + guint8 frame_flag; + /* Read the Decoder Configuration (GASpecificConfig) if present */ + /* We only care about the first bit to know what the number of samples + * in a frame is */ + if (!gst_bit_reader_get_bits_uint8 (br, &frame_flag, 1)) + return FALSE; + *frame_samples = frame_flag ? 960 : 1024; + } + /* There's LOTS of stuff next, but we ignore it for now as we have what we want (sample rate and number of channels */ GST_DEBUG_OBJECT (aacparse, "Need more code to parse humongous LOAS data, currently ignored"); - if (bits) - *bits = 0; + aacparse->last_parsed_channels = *channels; return TRUE; } @@ -582,21 +695,23 @@ gst_aac_parse_read_loas_config (GstAacParse * aacparse, const guint8 * data, return FALSE; if (u8) { GST_LOG_OBJECT (aacparse, "Frame uses previous config"); - if (!aacparse->sample_rate || !aacparse->channels) { + if (!aacparse->last_parsed_sample_rate || !aacparse->last_parsed_channels) { GST_DEBUG_OBJECT (aacparse, "No previous config to use. We'll look for more data."); return FALSE; } - *sample_rate = aacparse->sample_rate; - *channels = aacparse->channels; + *sample_rate = aacparse->last_parsed_sample_rate; + *channels = aacparse->last_parsed_channels; return TRUE; } GST_DEBUG_OBJECT (aacparse, "Frame contains new config"); + /* audioMuxVersion */ if (!gst_bit_reader_get_bits_uint8 (&br, &v, 1)) return FALSE; if (v) { + /* audioMuxVersionA */ if (!gst_bit_reader_get_bits_uint8 (&br, &vA, 1)) return FALSE; } else @@ -607,6 +722,7 @@ gst_aac_parse_read_loas_config (GstAacParse * aacparse, const guint8 * data, guint8 same_time, subframes, num_program, prog; if (v == 1) { guint32 value; + /* taraBufferFullness */ if (!gst_aac_parse_latm_get_value (aacparse, &br, &value)) return FALSE; } @@ -635,17 +751,16 @@ gst_aac_parse_read_loas_config (GstAacParse * aacparse, const guint8 * data, } if (!use_same_config) { if (v == 0) { - if (!gst_aac_parse_read_loas_audio_specific_config (aacparse, &br, + if (!gst_aac_parse_read_audio_specific_config (aacparse, &br, NULL, sample_rate, channels, NULL)) return FALSE; } else { - guint32 bits, asc_len; + guint32 asc_len; if (!gst_aac_parse_latm_get_value (aacparse, &br, &asc_len)) return FALSE; - if (!gst_aac_parse_read_loas_audio_specific_config (aacparse, &br, - sample_rate, channels, &bits)) + if (!gst_aac_parse_read_audio_specific_config (aacparse, &br, NULL, + sample_rate, channels, NULL)) return FALSE; - asc_len -= bits; if (!gst_bit_reader_skip (&br, asc_len)) return FALSE; } @@ -724,7 +839,8 @@ gst_aac_parse_check_loas_frame (GstAacParse * aacparse, if ((data[0] == 0x56) && ((data[1] & 0xe0) == 0xe0)) { *framesize = gst_aac_parse_loas_get_frame_len (data); - GST_DEBUG_OBJECT (aacparse, "Found %u byte LOAS frame", *framesize); + GST_DEBUG_OBJECT (aacparse, "Found possible %u byte LOAS frame", + *framesize); /* In EOS mode this is enough. No need to examine the data further. We also relax the check when we have sync, on the assumption that @@ -753,6 +869,8 @@ gst_aac_parse_check_loas_frame (GstAacParse * aacparse, gst_base_parse_set_min_frame_size (GST_BASE_PARSE (aacparse), nextlen + LOAS_MAX_SIZE); return TRUE; + } else { + GST_DEBUG_OBJECT (aacparse, "That was a false positive"); } } return FALSE; @@ -1169,7 +1287,7 @@ gst_aac_parse_prepend_adts_headers (GstAacParse * aacparse, adts_headers[0] = 0xFFU; adts_headers[1] = 0xF0U | (id << 3) | 0x1U; adts_headers[2] = (profile << 6) | (sampling_frequency_index << 2) | 0x2U | - (channel_configuration & 0x4U); + ((channel_configuration & 0x4U) >> 2); adts_headers[3] = ((channel_configuration & 0x3U) << 6) | 0x30U | (guint8) (frame_size >> 11); adts_headers[4] = (guint8) ((frame_size >> 3) & 0x00FF); @@ -1295,6 +1413,19 @@ gst_aac_parse_handle_frame (GstBaseParse * parse, gst_base_parse_set_frame_rate (GST_BASE_PARSE (aacparse), aacparse->sample_rate, aacparse->frame_samples, 2, 2); } +#ifdef TIZEN_FEATURE_AACPARSE_MODIFICATION + if (aacparse->first_frame == TRUE) { + gboolean ret = FALSE; + aacparse->first_frame = FALSE; + + ret = gst_aac_parse_estimate_duration (parse); + if (!ret) { + GST_WARNING_OBJECT (aacparse, "can not estimate total duration"); + ret = GST_FLOW_NOT_SUPPORTED; + } + gst_aac_parse_check_byte_seekability (parse); + } +#endif } else if (aacparse->header_type == DSPAAC_HEADER_LOAS) { gboolean setcaps = FALSE; @@ -1336,6 +1467,19 @@ gst_aac_parse_handle_frame (GstBaseParse * parse, aacparse->sample_rate, aacparse->frame_samples, 2, 2); } } +#ifdef TIZEN_FEATURE_AACPARSE_MODIFICATION + else if (aacparse->header_type == DSPAAC_HEADER_ADIF) { + /* to get more correct duration */ + float estimated_duration = 0; + gint64 total_file_size; + gst_base_parse_get_upstream_size (parse, &total_file_size); + estimated_duration = + ((total_file_size * 8) / (float) (aacparse->bitrate * 1000)) * + GST_SECOND; + gst_base_parse_set_duration (parse, GST_FORMAT_TIME, + estimated_duration * 1000, 0); + } +#endif if (aacparse->header_type == DSPAAC_HEADER_NONE && aacparse->output_header_type == DSPAAC_HEADER_ADTS) { @@ -1403,11 +1547,13 @@ gst_aac_parse_pre_push_frame (GstBaseParse * parse, GstBaseParseFrame * frame) && aacparse->output_header_type == DSPAAC_HEADER_NONE) { guint header_size; GstMapInfo map; - gst_buffer_map (frame->buffer, &map, GST_MAP_READ); + frame->out_buffer = gst_buffer_make_writable (frame->buffer); + frame->buffer = NULL; + gst_buffer_map (frame->out_buffer, &map, GST_MAP_READ); header_size = (map.data[1] & 1) ? 7 : 9; /* optional CRC */ - gst_buffer_unmap (frame->buffer, &map); - gst_buffer_resize (frame->buffer, header_size, - gst_buffer_get_size (frame->buffer) - header_size); + gst_buffer_unmap (frame->out_buffer, &map); + gst_buffer_resize (frame->out_buffer, header_size, + gst_buffer_get_size (frame->out_buffer) - header_size); } return GST_FLOW_OK; @@ -1432,6 +1578,14 @@ gst_aac_parse_start (GstBaseParse * parse) aacparse->frame_samples = 1024; gst_base_parse_set_min_frame_size (GST_BASE_PARSE (aacparse), ADTS_MAX_SIZE); aacparse->sent_codec_tag = FALSE; + aacparse->last_parsed_channels = 0; + aacparse->last_parsed_sample_rate = 0; + aacparse->object_type = 0; + aacparse->bitrate = 0; + aacparse->header_type = DSPAAC_HEADER_NOT_PARSED; + aacparse->output_header_type = DSPAAC_HEADER_NOT_PARSED; + aacparse->channels = 0; + aacparse->sample_rate = 0; return TRUE; } @@ -1579,3 +1733,629 @@ gst_aac_parse_sink_getcaps (GstBaseParse * parse, GstCaps * filter) return res; } + +static gboolean +gst_aac_parse_src_event (GstBaseParse * parse, GstEvent * event) +{ + GstAacParse *aacparse = GST_AAC_PARSE (parse); + + if (GST_EVENT_TYPE (event) == GST_EVENT_FLUSH_STOP) { + aacparse->last_parsed_channels = 0; + aacparse->last_parsed_sample_rate = 0; + } +#ifdef TIZEN_FEATURE_AACPARSE_MODIFICATION + GST_DEBUG ("Entering gst_aac_parse_src_event header type = %d", + aacparse->header_type); + if (aacparse->header_type == DSPAAC_HEADER_ADTS) + return gst_aac_parse_adts_src_eventfunc (parse, event); +#endif + return GST_BASE_PARSE_CLASS (parent_class)->src_event (parse, event); + +} + +#ifdef TIZEN_FEATURE_AACPARSE_MODIFICATION +/** + * get_aac_parse_get_adts_framelength: + * @data: #GstBufferData. + * @offset: #GstBufferData offset + * + * Implementation to get adts framelength by using first some frame. + * + * Returns: frame size + */ +int +get_aac_parse_get_adts_frame_length (const unsigned char *data, gint64 offset) +{ + const gint adts_header_length_no_crc = 7; + const gint adts_header_length_with_crc = 9; + gint frame_size = 0; + gint protection_absent; + gint head_size; + + /* check of syncword */ + if ((data[offset + 0] != 0xff) || ((data[offset + 1] & 0xf6) != 0xf0)) { + GST_ERROR ("check sync word is fail\n"); + return -1; + } + + /* check of protection absent */ + protection_absent = (data[offset + 1] & 0x01); + + /*check of frame length */ + frame_size = + (data[offset + 3] & 0x3) << 11 | data[offset + 4] << 3 | data[offset + + 5] >> 5; + + /* check of header size */ + /* protectionAbsent is 0 if there is CRC */ + head_size = + protection_absent ? adts_header_length_no_crc : + adts_header_length_with_crc; + if (head_size > frame_size) { + GST_ERROR ("return frame length as 0 (frameSize %u < headSize %u)", + frame_size, head_size); + return 0; + } + + return frame_size; +} + +/** + * gst_aac_parse_estimate_duration: + * @parse: #GstBaseParse. + * + * Implementation to get estimated total duration by using first some frame. + * + * Returns: TRUE if we can get estimated total duraion + */ +static gboolean +gst_aac_parse_estimate_duration (GstBaseParse * parse) +{ + gboolean ret = FALSE; + GstFlowReturn res = GST_FLOW_OK; + gint64 pull_size = 0, file_size = 0, offset = 0, num_frames = 0, duration = 0; + guint sample_rate_index = 0, sample_rate = 0, channel = 0; + guint frame_size = 0, frame_duration_us = 0, estimated_bitrate = 0; + guint lost_sync_count = 0; + GstClockTime estimated_duration = GST_CLOCK_TIME_NONE; + GstBuffer *buffer = NULL; + guint8 *buf = NULL; + gint i = 0; + GstPadMode pad_mode = GST_PAD_MODE_NONE; + GstAacParse *aacparse; + gint64 buffer_size = 0; + GstMapInfo map; + + aacparse = GST_AAC_PARSE (parse); + GST_LOG_OBJECT (aacparse, "gst_aac_parse_estimate_duration enter"); + + /* check baseparse define these fuction */ + gst_base_parse_get_pad_mode (parse, &pad_mode); + if (pad_mode != GST_PAD_MODE_PULL) { + GST_INFO_OBJECT (aacparse, + "aac parser is not pull mode. can not estimate duration"); + return FALSE; + } + + gst_base_parse_get_upstream_size (parse, &file_size); + + if (file_size < ADIF_MAX_SIZE) { + GST_ERROR_OBJECT (aacparse, "file size is too short"); + return FALSE; + } + + pull_size = MIN (file_size, AAC_MAX_ESTIMATE_DURATION_BUF); + + res = gst_pad_pull_range (parse->sinkpad, 0, pull_size, &buffer); + if (res != GST_FLOW_OK) { + GST_ERROR_OBJECT (aacparse, "gst_pad_pull_range failed!"); + return FALSE; + } + + gst_buffer_map (buffer, &map, GST_MAP_READ); + buf = map.data; + buffer_size = map.size; + if (buffer_size != pull_size) { + GST_ERROR_OBJECT (aacparse, + "We got different buffer_size(%" G_GINT64_FORMAT ") with pull_size(%" + G_GINT64_FORMAT ").", buffer_size, pull_size); + } + + /* MODIFICATION : add defence codes for real buffer_size is different with pull_size */ + for (i = 0; i < buffer_size; i++) { + if ((buf[i] == 0xff) && ((buf[i + 1] & 0xf6) == 0xf0)) { /* aac sync word */ + //guint profile = (buf[i+2] >> 6) & 0x3; + sample_rate_index = (buf[i + 2] >> 2) & 0xf; + sample_rate = + gst_aac_parse_get_sample_rate_from_index (sample_rate_index); + if (sample_rate == 0) { + GST_WARNING_OBJECT (aacparse, "Invalid sample rate index (0)"); + goto EXIT; + } + channel = (buf[i + 2] & 0x1) << 2 | (buf[i + 3] >> 6); + + GST_INFO_OBJECT (aacparse, "found sync. aac fs=%d, ch=%d", sample_rate, + channel); + + /* count number of frames */ + /* MODIFICATION : add defence codes for real buffer_size is different with pull_size */ + //while (offset < pull_size) { + while (offset < buffer_size) { + frame_size = get_aac_parse_get_adts_frame_length (buf, i + offset); + if (frame_size == 0) { + GST_ERROR_OBJECT (aacparse, + "framesize error at offset %" G_GINT64_FORMAT, offset); + break; + } else if (frame_size == -1) { + offset++; + lost_sync_count++; // lost sync count limmitation 2K Bytes + if (lost_sync_count > (1024 * 2)) { + GST_WARNING_OBJECT (aacparse, + "lost_sync_count is larger than 2048"); + goto EXIT; + } + } else { + offset += frame_size; + num_frames++; + lost_sync_count = 0; + } + } /* while */ + + /* if we can got full file, we can calculate the accurate duration */ + /* MODIFICATION : add defence codes for real buffer_size is different with pull_size */ + //if (pull_size == file_size) { + if (buffer_size == file_size) { + gfloat duration_for_one_frame = 0; + GstClockTime calculated_duration = GST_CLOCK_TIME_NONE; + + GST_INFO_OBJECT (aacparse, + "we got total file (%" G_GINT64_FORMAT + " bytes). do not estimate but make Accurate total duration.", + pull_size); + + duration_for_one_frame = + (gfloat) AAC_SAMPLE_PER_FRAME / (gfloat) sample_rate; + calculated_duration = + num_frames * duration_for_one_frame * 1000 * 1000 * 1000; + + GST_INFO_OBJECT (aacparse, "duration_for_one_frame %f ms", + duration_for_one_frame); + GST_INFO_OBJECT (aacparse, "calculated duration = %" GST_TIME_FORMAT, + GST_TIME_ARGS (calculated_duration)); + /* 0 means disable estimate */ + gst_base_parse_set_duration (parse, GST_FORMAT_TIME, + calculated_duration, 0); + + } else { + GST_INFO_OBJECT (aacparse, + "we got %" G_GUINT64_FORMAT " bytes in total file (%" + G_GINT64_FORMAT "). can not make accurate duration but Estimate.", + pull_size, file_size); + frame_duration_us = + (1024 * 1000000ll + (sample_rate - 1)) / sample_rate; + duration = num_frames * frame_duration_us; + + if (duration == 0) { + GST_WARNING_OBJECT (aacparse, "Invalid duration"); + goto EXIT; + } + estimated_bitrate = + (gint) ((gfloat) (offset * 8) / (gfloat) (duration / 1000)); + + if (estimated_bitrate == 0) { + GST_WARNING_OBJECT (aacparse, "Invalid estimated_bitrate"); + goto EXIT; + } + estimated_duration = + (GstClockTime) ((file_size * 8) / (estimated_bitrate * 1000)) * + GST_SECOND; + + GST_INFO_OBJECT (aacparse, "number of frame = %" G_GINT64_FORMAT, + num_frames); + GST_INFO_OBJECT (aacparse, "duration = %" G_GINT64_FORMAT, + duration / 1000000); + GST_INFO_OBJECT (aacparse, "byte = %" G_GINT64_FORMAT, offset); + GST_INFO_OBJECT (aacparse, "estimated bitrate = %d bps", + estimated_bitrate); + GST_INFO_OBJECT (aacparse, "estimated duration = %" GST_TIME_FORMAT, + GST_TIME_ARGS (estimated_duration)); + + gst_base_parse_set_average_bitrate (parse, estimated_bitrate * 1000); + /* set update_interval as duration(sec)/2 */ + gst_base_parse_set_duration (parse, GST_FORMAT_TIME, estimated_duration, + (gint) (duration / 2)); + } + + break; + } + } + ret = TRUE; + +EXIT: + gst_buffer_unmap (buffer, &map); + gst_buffer_unref (buffer); + return ret; +} + + +/* perform seek in push based mode: + find BYTE position to move to based on time and delegate to upstream +*/ +static gboolean +gst_aac_audio_parse_do_push_seek (GstBaseParse * parse, + GstPad * pad, GstEvent * event) +{ + GstAacParse *aacparse = GST_AAC_PARSE (parse); + gdouble rate; + GstFormat format; + GstSeekFlags flags; + GstSeekType cur_type, stop_type; + gint64 cur, stop; + gboolean res; + gint64 byte_cur; + gint64 esimate_byte; + gint32 frame_dur; + gint64 upstream_total_bytes = 0; + GstFormat fmt = GST_FORMAT_BYTES; + + GST_INFO_OBJECT (parse, "doing aac push-based seek"); + + gst_event_parse_seek (event, &rate, &format, &flags, &cur_type, &cur, + &stop_type, &stop); + + /* FIXME, always play to the end */ + stop = -1; + + /* only forward streaming and seeking is possible */ + if (rate <= 0) + goto unsupported_seek; + + if (cur == 0) { + /* handle rewind only */ + cur_type = GST_SEEK_TYPE_SET; + byte_cur = 0; + stop_type = GST_SEEK_TYPE_NONE; + stop = -1; + flags |= GST_SEEK_FLAG_FLUSH; + } else { + /* handle normal seek */ + cur_type = GST_SEEK_TYPE_SET; + stop_type = GST_SEEK_TYPE_NONE; + stop = -1; + flags |= GST_SEEK_FLAG_FLUSH; + + esimate_byte = (cur / (1000 * 1000)) * aacparse->frame_byte; + if (aacparse->sample_rate > 0) + frame_dur = (aacparse->spf * 1000) / aacparse->sample_rate; + else + goto unsupported_seek; + if (frame_dur > 0) + byte_cur = esimate_byte / (frame_dur); + else + goto unsupported_seek; + + GST_INFO_OBJECT (parse, "frame_byte(%d) spf(%d) rate (%d) ", + aacparse->frame_byte, aacparse->spf, aacparse->sample_rate); + GST_INFO_OBJECT (parse, + "seek cur (%" G_GINT64_FORMAT ") = (%" GST_TIME_FORMAT ") ", cur, + GST_TIME_ARGS (cur)); + GST_INFO_OBJECT (parse, + "esimate_byte(%" G_GINT64_FORMAT ") esimate_byte (%d)", esimate_byte, + frame_dur); + } + + /* obtain real upstream total bytes */ + if (!gst_pad_peer_query_duration (parse->sinkpad, fmt, &upstream_total_bytes)) + upstream_total_bytes = 0; + GST_INFO_OBJECT (aacparse, + "gst_pad_query_peer_duration -upstream_total_bytes (%" G_GUINT64_FORMAT + ")", upstream_total_bytes); + aacparse->file_size = upstream_total_bytes; + + if ((byte_cur == -1) || (byte_cur > aacparse->file_size)) { + GST_INFO_OBJECT (parse, + "[WEB-ERROR] seek cur (%" G_GINT64_FORMAT ") > file_size (%" + G_GINT64_FORMAT ") ", cur, aacparse->file_size); + goto abort_seek; + } + + GST_INFO_OBJECT (parse, + "Pushing BYTE seek rate %g, " "start %" G_GINT64_FORMAT ", stop %" + G_GINT64_FORMAT, rate, byte_cur, stop); + + if (!(flags & GST_SEEK_FLAG_KEY_UNIT)) { + GST_INFO_OBJECT (parse, + "Requested seek time: %" GST_TIME_FORMAT ", calculated seek offset: %" + G_GUINT64_FORMAT, GST_TIME_ARGS (cur), byte_cur); + } + + /* BYTE seek event */ + event = + gst_event_new_seek (rate, GST_FORMAT_BYTES, flags, cur_type, byte_cur, + stop_type, stop); + res = gst_pad_push_event (parse->sinkpad, event); + + return res; + + /* ERRORS */ + +abort_seek: + { + GST_DEBUG_OBJECT (parse, + "could not determine byte position to seek to, " "seek aborted."); + return FALSE; + } + +unsupported_seek: + { + GST_DEBUG_OBJECT (parse, "unsupported seek, seek aborted."); + return FALSE; + } +} + + +static guint +gst_aac_parse_adts_get_fast_frame_len (const guint8 * data) +{ + int length; + if ((data[0] == 0xff) && ((data[1] & 0xf6) == 0xf0)) { + length = + ((data[3] & 0x03) << 11) | (data[4] << 3) | ((data[5] & 0xe0) >> 5); + } else { + length = 0; + } + return length; +} + +/** + * gst_aac_parse_adts_src_eventfunc: + * @parse: #GstBaseParse. #event + * + * before baseparse handles seek event, make full amr index table. + * + * Returns: TRUE on success. + */ +static gboolean +gst_aac_parse_adts_src_eventfunc (GstBaseParse * parse, GstEvent * event) +{ + gboolean handled = FALSE; + GstAacParse *aacparse = GST_AAC_PARSE (parse); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_SEEK: + { + GstFlowReturn res = GST_FLOW_OK; + gint64 base_offset = 0, cur = 0; + gint32 frame_count = 1; /* do not add first frame because it is already in index table */ + gint64 second_count = 0; /* initial 1 second */ + gint64 total_file_size = 0, start_offset = 0; + GstClockTime current_ts = GST_CLOCK_TIME_NONE; + GstPadMode pad_mode = GST_PAD_MODE_NONE; + + /* check baseparse define these fuction */ + gst_base_parse_get_pad_mode (parse, &pad_mode); + if (pad_mode != GST_PAD_MODE_PULL) { + gboolean ret = FALSE; + GstPad *srcpad = parse->srcpad; + GST_INFO_OBJECT (aacparse, "aac parser is PUSH MODE."); + /* check NULL */ + if (aacparse->byte_seekable) { + ret = gst_aac_audio_parse_do_push_seek (parse, srcpad, event); + if (!ret) { + GST_INFO_OBJECT (aacparse, "PUSH mode seek() failed, Trying base seek()"); + goto aac_seek_null_exit; + } + return ret; + } + GST_INFO_OBJECT (aacparse, "not support byte seek"); + goto aac_seek_null_exit; + } + + gst_base_parse_get_upstream_size (parse, &total_file_size); + gst_base_parse_get_index_last_offset (parse, &start_offset); + gst_base_parse_get_index_last_ts (parse, ¤t_ts); + + if (total_file_size > AAC_LARGE_FILE_SIZE) { + gst_base_parse_set_seek_mode (parse, 0); + GST_INFO_OBJECT (aacparse, "larger than big size (2MB)."); + goto aac_seek_null_exit; + } + + GST_DEBUG ("gst_aac_parse_adts_src_eventfunc GST_EVENT_SEEK enter"); + + if (total_file_size == 0 || start_offset >= total_file_size) { + GST_ERROR ("last index offset %" G_GINT64_FORMAT + " is larger than file size %" G_GINT64_FORMAT, start_offset, + total_file_size); + break; + } + + gst_event_parse_seek (event, NULL, NULL, NULL, NULL, &cur, NULL, NULL); + if (cur <= current_ts) { + GST_INFO ("seek to %" GST_TIME_FORMAT " within index table %" + GST_TIME_FORMAT ". do not make index table", GST_TIME_ARGS (cur), + GST_TIME_ARGS (current_ts)); + break; + } else { + GST_INFO ("seek to %" GST_TIME_FORMAT " without index table %" + GST_TIME_FORMAT ". make index table", GST_TIME_ARGS (cur), + GST_TIME_ARGS (current_ts)); + } + + GST_INFO ("make AAC(ADTS) Index Table. file_size = %" G_GINT64_FORMAT + " last idx offset=%" G_GINT64_FORMAT ", last idx ts=%" + GST_TIME_FORMAT, total_file_size, start_offset, + GST_TIME_ARGS (current_ts)); + + base_offset = start_offset; /* set base by start offset */ + second_count = current_ts + GST_SECOND; /* 1sec */ + + /************************************/ + /* STEP 0: Setting parse information */ + /************************************/ + aacparse->spf = aacparse->frame_samples; + aacparse->frame_duration = (aacparse->spf * 1000 * 100) / aacparse->sample_rate; /* duration per frame (msec) */ + aacparse->frame_per_sec = (aacparse->sample_rate) / aacparse->spf; /* frames per second (ea) */ + + /************************************/ + /* STEP 1: MAX_PULL_RANGE_BUF cycle */ + /************************************/ + while (total_file_size - base_offset >= AAC_MAX_PULL_RANGE_BUF) { + gint64 offset = 0; + GstBuffer *buffer = NULL; + guint8 *buf = NULL; + GstMapInfo map; + GST_INFO ("gst_pad_pull_range %d bytes (from %" G_GINT64_FORMAT + ") use max size", AAC_MAX_PULL_RANGE_BUF, base_offset); + res = + gst_pad_pull_range (parse->sinkpad, base_offset, + base_offset + AAC_MAX_PULL_RANGE_BUF, &buffer); + if (res != GST_FLOW_OK) { + GST_ERROR ("gst_pad_pull_range failed!"); + break; + } + + gst_buffer_map (buffer, &map, GST_MAP_READ); + buf = map.data; + if (buf == NULL) { + gst_buffer_unmap (buffer, &map); + GST_WARNING ("buffer is NULL in make aac seek table's STEP1"); + gst_buffer_unref (buffer); + goto aac_seek_null_exit; + } + + while (offset <= AAC_MAX_PULL_RANGE_BUF) { + gint frame_size = 0; + + /* make sure the values in the frame header look sane */ + frame_size = gst_aac_parse_adts_get_fast_frame_len (buf); + + if ((frame_size > 0) + && (frame_size < (AAC_MAX_PULL_RANGE_BUF - offset))) { + if (current_ts > second_count) { /* 1 sec == xx frames. we make idx per sec */ + gst_base_parse_add_index_entry (parse, base_offset + offset, current_ts, TRUE, TRUE); /* force */ + GST_DEBUG ("Adding index ts=%" GST_TIME_FORMAT " offset %" + G_GINT64_FORMAT, GST_TIME_ARGS (current_ts), + base_offset + offset); + second_count += GST_SECOND; /* 1sec */ + } + + current_ts += (aacparse->frame_duration * GST_MSECOND) / 100; /* each frame is (frame_duration) ms */ + offset += frame_size; + buf += frame_size; + frame_count++; + } else if (frame_size >= (AAC_MAX_PULL_RANGE_BUF - offset)) { + GST_DEBUG ("we need refill buffer"); + break; + } else { + GST_WARNING ("we lost sync"); + buf++; + offset++; + } + } /* while */ + + base_offset = base_offset + offset; + + gst_buffer_unmap (buffer, &map); + gst_buffer_unref (buffer); + } /* end MAX buffer cycle */ + + /*******************************/ + /* STEP 2: Remain Buffer cycle */ + /*******************************/ + if (total_file_size - base_offset > 0) { + gint64 offset = 0; + GstBuffer *buffer = NULL; + guint8 *buf = NULL; + GstMapInfo map; + + GST_INFO ("gst_pad_pull_range %" G_GINT64_FORMAT " bytes (from %" + G_GINT64_FORMAT ") use remain_buf size", + total_file_size - base_offset, base_offset); + res = + gst_pad_pull_range (parse->sinkpad, base_offset, total_file_size, + &buffer); + if (res != GST_FLOW_OK) { + GST_ERROR ("gst_pad_pull_range failed!"); + break; + } + + gst_buffer_map (buffer, &map, GST_MAP_READ); + buf = map.data; + if (buf == NULL) { + gst_buffer_unmap (buffer, &map); + GST_WARNING ("buffer is NULL in make aac seek table's STEP2"); + gst_buffer_unref (buffer); + goto aac_seek_null_exit; + } + + while (base_offset + offset < total_file_size) { + gint frame_size = 0; + + /* make sure the values in the frame header look sane */ + frame_size = gst_aac_parse_adts_get_fast_frame_len (buf); + + if ((frame_size > 0) + && (frame_size <= (total_file_size - (base_offset + offset)))) { + if (current_ts > second_count) { /* 1 sec == xx frames. we make idx per sec */ + gst_base_parse_add_index_entry (parse, base_offset + offset, current_ts, TRUE, TRUE); /* force */ + GST_DEBUG ("Adding index ts=%" GST_TIME_FORMAT " offset %" + G_GINT64_FORMAT, GST_TIME_ARGS (current_ts), + base_offset + offset); + second_count += GST_SECOND; /* 1sec */ + } + + current_ts += (aacparse->frame_duration * GST_MSECOND) / 100; /* each frame is (frame_duration) ms */ + offset += frame_size; + buf += frame_size; + frame_count++; + } else if (frame_size == 0) { + GST_DEBUG ("Frame size is 0 so, Decoding end.."); + break; + } else { + GST_WARNING ("we lost sync"); + buf++; + offset++; + } + } /* while */ + + gst_buffer_unmap (buffer, &map); + gst_buffer_unref (buffer); + } + /* end remain_buf buffer cycle */ + GST_DEBUG ("gst_aac_parse_adts_src_eventfunc GST_EVENT_SEEK leave"); + } + break; + + default: + break; + } + +aac_seek_null_exit: + + /* call baseparse src_event function to handle event */ + handled = GST_BASE_PARSE_CLASS (parent_class)->src_event (parse, event); + return handled; +} + +static void +gst_aac_parse_check_byte_seekability (GstBaseParse * parse) +{ + GstQuery *query; + gboolean seekable = FALSE; + GstAacParse *aacparse = GST_AAC_PARSE (parse); + GST_LOG_OBJECT (aacparse, "gst_aac_parse_check_byte_seekability enter"); + + query = gst_query_new_seeking (GST_FORMAT_BYTES); + if (gst_pad_peer_query (parse->sinkpad, query)) + gst_query_parse_seeking (query, NULL, &seekable, NULL, NULL); + else + GST_DEBUG_OBJECT (aacparse, "seeking query failed"); + + gst_query_unref (query); + + GST_INFO_OBJECT (aacparse, "byte seekable: %d", seekable); + + aacparse->byte_seekable = seekable; +} +#endif /* TIZEN_FEATURE_AACPARSE_MODIFICATION */