#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, 0, 0, 0, 0, 0
+ 0, 0, 0, 7, 8, 0, 8, 0
};
static gboolean gst_aac_parse_start (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.
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",
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);
}
{
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
}
GstCaps *src_caps = NULL, *allowed;
gboolean res = FALSE;
const gchar *stream_format;
- GstBuffer *codec_data;
+ guint8 codec_data[2];
guint16 codec_data_data;
+ gint sample_rate_idx;
GST_DEBUG_OBJECT (aacparse, "sink caps: %" GST_PTR_FORMAT, sink_caps);
if (sink_caps)
stream_format = NULL;
}
+ /* Generate codec data to be able to set profile/level on the caps */
+ sample_rate_idx =
+ gst_codec_utils_aac_get_index_from_sample_rate (aacparse->sample_rate);
+ if (sample_rate_idx < 0)
+ goto not_a_known_rate;
+ codec_data_data =
+ (aacparse->object_type << 11) |
+ (sample_rate_idx << 7) | (aacparse->channels << 3);
+ GST_WRITE_UINT16_BE (codec_data, codec_data_data);
+ gst_codec_utils_aac_caps_set_level_and_profile (src_caps, codec_data, 2);
+
s = gst_caps_get_structure (src_caps, 0);
if (aacparse->sample_rate > 0)
gst_structure_set (s, "rate", G_TYPE_INT, aacparse->sample_rate, NULL);
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 (!gst_caps_can_intersect (src_caps, allowed)) {
+ if (allowed && !gst_caps_can_intersect (src_caps, allowed)) {
GST_DEBUG_OBJECT (GST_BASE_PARSE (aacparse)->srcpad,
"Caps can not intersect");
if (aacparse->header_type == DSPAAC_HEADER_ADTS) {
gst_caps_set_simple (src_caps, "stream-format", G_TYPE_STRING, "raw",
NULL);
if (gst_caps_can_intersect (src_caps, allowed)) {
- GstMapInfo map;
- int idx;
-
- idx =
- gst_codec_utils_aac_get_index_from_sample_rate
- (aacparse->sample_rate);
- if (idx < 0)
- goto not_a_known_rate;
+ GstBuffer *codec_data_buffer;
GST_DEBUG_OBJECT (GST_BASE_PARSE (aacparse)->srcpad,
"Caps can intersect, we will drop the ADTS layer");
/* The codec_data data is according to AudioSpecificConfig,
ISO/IEC 14496-3, 1.6.2.1 */
- codec_data = gst_buffer_new_and_alloc (2);
- gst_buffer_map (codec_data, &map, GST_MAP_WRITE);
- codec_data_data =
- (aacparse->object_type << 11) |
- (idx << 7) | (aacparse->channels << 3);
- GST_WRITE_UINT16_BE (map.data, codec_data_data);
- gst_buffer_unmap (codec_data, &map);
+ 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, NULL);
+ 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,
}
}
}
- gst_caps_unref (allowed);
+ 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);
return res;
not_a_known_rate:
- gst_caps_unref (allowed);
+ GST_ERROR_OBJECT (aacparse, "Not a known sample rate: %d",
+ aacparse->sample_rate);
gst_caps_unref (src_caps);
return FALSE;
}
if (value) {
GstBuffer *buf = gst_value_get_buffer (value);
- if (buf) {
+ if (buf && gst_buffer_get_size (buf) >= 2) {
GstMapInfo map;
- guint sr_idx;
+ GstBitReader br;
- gst_buffer_map (buf, &map, GST_MAP_READ);
+ 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);
- 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;
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, "
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;
}
/* Absolute minimum to perform the ADTS syncword,
layer and sampling frequency tests */
- if (G_UNLIKELY (avail < 3))
+ if (G_UNLIKELY (avail < 3)) {
+ *needed_data = 3;
return FALSE;
+ }
/* Syncword and layer tests */
if ((data[0] == 0xff) && ((data[1] & 0xf6) == 0xf0)) {
crc_size = (data[1] & 0x01) ? 0 : 2;
/* CRC size test */
- if (*framesize < 7 + crc_size)
+ if (*framesize < 7 + crc_size) {
+ *needed_data = 7 + crc_size;
return FALSE;
+ }
/* 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
*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;
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;
}
if (!gst_bit_reader_get_bits_uint8 (&br, &u8, 1))
return FALSE;
if (u8) {
- GST_DEBUG_OBJECT (aacparse, "Frame uses previous config");
- if (!aacparse->sample_rate || !aacparse->channels) {
- GST_WARNING_OBJECT (aacparse, "No previous config to use");
+ GST_LOG_OBJECT (aacparse, "Frame uses previous config");
+ 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
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;
}
}
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;
}
GST_LOG_OBJECT (aacparse, "More data ignored");
} else {
GST_WARNING_OBJECT (aacparse, "Spec says \"TBD\"...");
+ return FALSE;
}
return TRUE;
}
*needed_data = 0;
/* 3 byte header */
- if (G_UNLIKELY (avail < 3))
+ if (G_UNLIKELY (avail < 3)) {
+ *needed_data = 3;
return FALSE;
+ }
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
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;
*rate = gst_codec_utils_aac_get_sample_rate_from_index (sr_idx);
}
- if (channels)
+ if (channels) {
*channels = ((data[2] & 0x01) << 2) | ((data[3] & 0xc0) >> 6);
+ if (*channels == 7)
+ *channels = 8;
+ }
if (version)
*version = (data[1] & 0x08) ? 2 : 4;
for (i = 0; i < avail - 4; i++) {
if (((data[i] == 0xff) && ((data[i + 1] & 0xf6) == 0xf0)) ||
- ((data[0] == 0x56) && ((data[1] & 0xe0) == 0xe0)) ||
+ ((data[i] == 0x56) && ((data[i + 1] & 0xe0) == 0xe0)) ||
strncmp ((char *) data + i, "ADIF", 4) == 0) {
GST_DEBUG_OBJECT (aacparse, "Found signature at offset %u", i);
found = TRUE;
if (gst_aac_parse_check_loas_frame (aacparse, data, avail, drain,
framesize, &need_data_loas)) {
- gint rate, channels;
+ gint rate = 0, channels = 0;
GST_INFO ("LOAS, framesize: %d", *framesize);
if (!gst_aac_parse_read_loas_config (aacparse, data, avail, &rate,
&channels, &aacparse->mpegversion)) {
- GST_WARNING_OBJECT (aacparse, "Error reading LOAS config");
+ /* This is pretty normal when skipping data at the start of
+ * random stream (MPEG-TS capture for example) */
+ GST_LOG_OBJECT (aacparse, "Error reading LOAS config");
return FALSE;
}
gst_base_parse_set_frame_rate (GST_BASE_PARSE (aacparse), rate,
aacparse->frame_samples, 2, 2);
+ /* Don't store the sample rate and channels yet -
+ * this is just format detection. */
GST_DEBUG ("LOAS: samplerate %d, channels %d, objtype %d, version %d",
rate, channels, aacparse->object_type, aacparse->mpegversion);
- aacparse->sample_rate = rate;
- aacparse->channels = channels;
}
gst_base_parse_set_syncable (GST_BASE_PARSE (aacparse), TRUE);
guint8 ret;
srccaps = gst_pad_get_current_caps (GST_BASE_PARSE_SRC_PAD (aacparse));
+ if (G_UNLIKELY (srccaps == NULL)) {
+ return G_MAXUINT8;
+ }
+
srcstruct = gst_caps_get_structure (srccaps, 0);
profile = gst_structure_get_string (srcstruct, "profile");
if (G_UNLIKELY (profile == NULL)) {
return (guint8) 7U;
else
return G_MAXUINT8;
+
+ /* FIXME: Add support for configurations 11, 12 and 14 from
+ * ISO/IEC 14496-3:2009/PDAM 4 based on the actual channel layout
+ */
}
/**
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);
adts_headers[6] = 0xFCU;
mem = gst_memory_new_wrapped (0, adts_headers, ADTS_HEADERS_LENGTH, 0,
- ADTS_HEADERS_LENGTH, NULL, NULL);
+ ADTS_HEADERS_LENGTH, adts_headers, g_free);
gst_buffer_prepend_memory (frame->out_buffer, mem);
return TRUE;
gboolean lost_sync;
GstBuffer *buffer;
guint framesize;
- gint rate, channels;
+ gint rate = 0, channels = 0;
aacparse = GST_AAC_PARSE (parse);
buffer = frame->buffer;
ret = gst_aac_parse_check_adts_frame (aacparse, map.data, map.size,
GST_BASE_PARSE_DRAINING (parse), &framesize, &needed_data);
- if (!ret) {
+ if (!ret && needed_data) {
GST_DEBUG ("buffer didn't contain valid frame");
+ *skipsize = 0;
gst_base_parse_set_min_frame_size (GST_BASE_PARSE (aacparse),
needed_data);
}
ret = gst_aac_parse_check_loas_frame (aacparse, map.data,
map.size, GST_BASE_PARSE_DRAINING (parse), &framesize, &needed_data);
- if (!ret) {
+ if (!ret && needed_data) {
GST_DEBUG ("buffer didn't contain valid frame");
+ *skipsize = 0;
gst_base_parse_set_min_frame_size (GST_BASE_PARSE (aacparse),
needed_data);
}
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;
frame->overhead = 3;
if (!gst_aac_parse_read_loas_config (aacparse, map.data, map.size, &rate,
- &channels, NULL)) {
- GST_WARNING_OBJECT (aacparse, "Error reading LOAS config");
- } else if (G_UNLIKELY (rate != aacparse->sample_rate
+ &channels, NULL) || !rate || !channels) {
+ /* This is pretty normal when skipping data at the start of
+ * random stream (MPEG-TS capture for example) */
+ GST_DEBUG_OBJECT (aacparse, "Error reading LOAS config. Skipping.");
+ /* Since we don't fully parse the LOAS config, we don't know for sure
+ * how much to skip. Just skip 1 to end up to the next marker and
+ * resume parsing from there */
+ *skipsize = 1;
+ goto exit;
+ }
+
+ if (G_UNLIKELY (rate != aacparse->sample_rate
|| channels != aacparse->channels)) {
aacparse->sample_rate = rate;
aacparse->channels = channels;
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) {
GstTagList *taglist;
GstCaps *caps;
- taglist = gst_tag_list_new_empty ();
-
/* codec tag */
caps = gst_pad_get_current_caps (GST_BASE_PARSE_SRC_PAD (parse));
+ if (caps == NULL) {
+ if (GST_PAD_IS_FLUSHING (GST_BASE_PARSE_SRC_PAD (parse))) {
+ GST_INFO_OBJECT (parse, "Src pad is flushing");
+ return GST_FLOW_FLUSHING;
+ } else {
+ GST_INFO_OBJECT (parse, "Src pad is not negotiated!");
+ return GST_FLOW_NOT_NEGOTIATED;
+ }
+ }
+
+ taglist = gst_tag_list_new_empty ();
gst_pb_utils_add_codec_description_to_tag_list (taglist,
GST_TAG_AUDIO_CODEC, caps);
gst_caps_unref (caps);
- gst_pad_push_event (GST_BASE_PARSE_SRC_PAD (aacparse),
- gst_event_new_tag (taglist));
+ gst_base_parse_merge_tags (parse, taglist, GST_TAG_MERGE_REPLACE);
+ gst_tag_list_unref (taglist);
/* also signals the end of first-frame processing */
aacparse->sent_codec_tag = TRUE;
&& 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;
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;
}
}
}
+static void
+add_conversion_fields (GstCaps * caps)
+{
+ guint i, n;
+
+ n = gst_caps_get_size (caps);
+ for (i = 0; i < n; i++) {
+ GstStructure *s = gst_caps_get_structure (caps, i);
+
+ if (gst_structure_has_field (s, "stream-format")) {
+ const GValue *v = gst_structure_get_value (s, "stream-format");
+
+ if (G_VALUE_HOLDS_STRING (v)) {
+ const gchar *str = g_value_get_string (v);
+
+ if (strcmp (str, "adts") == 0 || strcmp (str, "raw") == 0) {
+ GValue va = G_VALUE_INIT;
+ GValue vs = G_VALUE_INIT;
+
+ g_value_init (&va, GST_TYPE_LIST);
+ g_value_init (&vs, G_TYPE_STRING);
+ g_value_set_string (&vs, "adts");
+ gst_value_list_append_value (&va, &vs);
+ g_value_set_string (&vs, "raw");
+ gst_value_list_append_value (&va, &vs);
+ gst_structure_set_value (s, "stream-format", &va);
+ g_value_unset (&va);
+ g_value_unset (&vs);
+ }
+ } else if (GST_VALUE_HOLDS_LIST (v)) {
+ gboolean contains_raw = FALSE;
+ gboolean contains_adts = FALSE;
+ guint m = gst_value_list_get_size (v), j;
+
+ for (j = 0; j < m; j++) {
+ const GValue *ve = gst_value_list_get_value (v, j);
+ const gchar *str;
+
+ if (G_VALUE_HOLDS_STRING (ve) && (str = g_value_get_string (ve))) {
+ if (strcmp (str, "adts") == 0)
+ contains_adts = TRUE;
+ else if (strcmp (str, "raw") == 0)
+ contains_raw = TRUE;
+ }
+ }
+
+ if (contains_adts || contains_raw) {
+ GValue va = G_VALUE_INIT;
+ GValue vs = G_VALUE_INIT;
+
+ g_value_init (&va, GST_TYPE_LIST);
+ g_value_init (&vs, G_TYPE_STRING);
+ g_value_copy (v, &va);
+
+ if (!contains_raw) {
+ g_value_set_string (&vs, "raw");
+ gst_value_list_append_value (&va, &vs);
+ }
+ if (!contains_adts) {
+ g_value_set_string (&vs, "adts");
+ gst_value_list_append_value (&va, &vs);
+ }
+
+ gst_structure_set_value (s, "stream-format", &va);
+
+ g_value_unset (&vs);
+ g_value_unset (&va);
+ }
+ }
+ }
+ }
+}
+
static GstCaps *
gst_aac_parse_sink_getcaps (GstBaseParse * parse, GstCaps * filter)
{
GstCaps *fcopy = gst_caps_copy (filter);
/* Remove the fields we convert */
remove_fields (fcopy);
+ add_conversion_fields (fcopy);
peercaps = gst_pad_peer_query_caps (GST_BASE_PARSE_SRC_PAD (parse), fcopy);
gst_caps_unref (fcopy);
} else
peercaps = gst_caps_make_writable (peercaps);
/* Remove the fields we convert */
remove_fields (peercaps);
+ add_conversion_fields (peercaps);
res = gst_caps_intersect_full (peercaps, templ, GST_CAPS_INTERSECT_FIRST);
gst_caps_unref (peercaps);
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 */