summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWim Taymans <wim.taymans@collabora.co.uk>2009-09-22 22:12:58 +0200
committerWim Taymans <wim@metal.(none)>2009-09-28 22:16:57 +0200
commitceb7d66e2582090317aa0cf33007885985ad12cb (patch)
treea2437c56239c0b6e040562883987de6c6c185563
parent8aa38308527301b48803823cb9c4db0404ac9cb5 (diff)
avi: implement index scanning
Implement scanning of the file when we can parse the index. Some refactoring of common code. Cleanups and comments. Remove some reimplemented code. Remove index massage code and put a FIXME where we should do something equivalent later.
-rw-r--r--gst/avi/gstavidemux.c1037
-rw-r--r--gst/avi/gstavidemux.h8
2 files changed, 297 insertions, 748 deletions
diff --git a/gst/avi/gstavidemux.c b/gst/avi/gstavidemux.c
index 94917c59d..43a8a16cb 100644
--- a/gst/avi/gstavidemux.c
+++ b/gst/avi/gstavidemux.c
@@ -220,31 +220,37 @@ gst_avi_demux_finalize (GObject * object)
220} 220}
221 221
222static void 222static void
223gst_avi_demux_reset_stream (GstAviDemux * avi, GstAviStream * stream)
224{
225 g_free (stream->strh);
226 g_free (stream->strf.data);
227 g_free (stream->name);
228 g_free (stream->index);
229 g_free (stream->indexes);
230 if (stream->initdata)
231 gst_buffer_unref (stream->initdata);
232 if (stream->extradata)
233 gst_buffer_unref (stream->extradata);
234 if (stream->pad) {
235 gst_pad_set_active (stream->pad, FALSE);
236 gst_element_remove_pad (GST_ELEMENT (avi), stream->pad);
237 }
238 if (stream->taglist) {
239 gst_tag_list_free (stream->taglist);
240 stream->taglist = NULL;
241 }
242 memset (stream, 0, sizeof (GstAviStream));
243}
244
245static void
223gst_avi_demux_reset (GstAviDemux * avi) 246gst_avi_demux_reset (GstAviDemux * avi)
224{ 247{
225 gint i; 248 gint i;
226 249
227 GST_DEBUG ("AVI: reset"); 250 GST_DEBUG ("AVI: reset");
228 251
229 for (i = 0; i < avi->num_streams; i++) { 252 for (i = 0; i < avi->num_streams; i++)
230 g_free (avi->stream[i].strh); 253 gst_avi_demux_reset_stream (avi, &avi->stream[i]);
231 g_free (avi->stream[i].strf.data);
232 if (avi->stream[i].name)
233 g_free (avi->stream[i].name);
234 if (avi->stream[i].initdata)
235 gst_buffer_unref (avi->stream[i].initdata);
236 if (avi->stream[i].extradata)
237 gst_buffer_unref (avi->stream[i].extradata);
238 if (avi->stream[i].pad) {
239 gst_pad_set_active (avi->stream[i].pad, FALSE);
240 gst_element_remove_pad (GST_ELEMENT (avi), avi->stream[i].pad);
241 }
242 if (avi->stream[i].taglist) {
243 gst_tag_list_free (avi->stream[i].taglist);
244 avi->stream[i].taglist = NULL;
245 }
246 }
247 memset (&avi->stream, 0, sizeof (avi->stream));
248 254
249 avi->header_state = GST_AVI_DEMUX_HEADER_TAG_LIST; 255 avi->header_state = GST_AVI_DEMUX_HEADER_TAG_LIST;
250 avi->num_streams = 0; 256 avi->num_streams = 0;
@@ -255,9 +261,6 @@ gst_avi_demux_reset (GstAviDemux * avi)
255 avi->state = GST_AVI_DEMUX_START; 261 avi->state = GST_AVI_DEMUX_START;
256 avi->offset = 0; 262 avi->offset = 0;
257 263
258 //g_free (avi->index_entries);
259 //avi->index_entries = NULL;
260 //avi->index_size = 0;
261 avi->index_offset = 0; 264 avi->index_offset = 0;
262 g_free (avi->avih); 265 g_free (avi->avih);
263 avi->avih = NULL; 266 avi->avih = NULL;
@@ -310,14 +313,16 @@ static inline GstClockTime
310avi_stream_convert_bytes_to_time_unchecked (GstAviStream * stream, 313avi_stream_convert_bytes_to_time_unchecked (GstAviStream * stream,
311 guint64 bytes) 314 guint64 bytes)
312{ 315{
313 return gst_util_uint64_scale (bytes, GST_SECOND, stream->strf.auds->av_bps); 316 return gst_util_uint64_scale_int (bytes, GST_SECOND,
317 stream->strf.auds->av_bps);
314} 318}
315 319
316static inline guint64 320static inline guint64
317avi_stream_convert_time_to_bytes_unchecked (GstAviStream * stream, 321avi_stream_convert_time_to_bytes_unchecked (GstAviStream * stream,
318 GstClockTime time) 322 GstClockTime time)
319{ 323{
320 return gst_util_uint64_scale (time, stream->strf.auds->av_bps, GST_SECOND); 324 return gst_util_uint64_scale_int (time, stream->strf.auds->av_bps,
325 GST_SECOND);
321} 326}
322 327
323/* assumes stream->strh->rate != 0 */ 328/* assumes stream->strh->rate != 0 */
@@ -369,8 +374,8 @@ gst_avi_demux_src_convert (GstPad * pad,
369 case GST_FORMAT_TIME: 374 case GST_FORMAT_TIME:
370 switch (*dest_format) { 375 switch (*dest_format) {
371 case GST_FORMAT_BYTES: 376 case GST_FORMAT_BYTES:
372 *dest_value = gst_util_uint64_scale (src_value, 377 *dest_value = gst_util_uint64_scale_int (src_value,
373 (guint64) stream->strf.auds->av_bps, GST_SECOND); 378 stream->strf.auds->av_bps, GST_SECOND);
374 break; 379 break;
375 case GST_FORMAT_DEFAULT: 380 case GST_FORMAT_DEFAULT:
376 *dest_value = 381 *dest_value =
@@ -1729,8 +1734,6 @@ gst_avi_demux_parse_stream (GstAviDemux * avi, GstBuffer * buf)
1729 if (stream->pad) 1734 if (stream->pad)
1730 gst_object_unref (stream->pad); 1735 gst_object_unref (stream->pad);
1731 pad = stream->pad = gst_pad_new_from_template (templ, padname); 1736 pad = stream->pad = gst_pad_new_from_template (templ, padname);
1732 stream->last_flow = GST_FLOW_OK;
1733 stream->discont = TRUE;
1734 g_free (padname); 1737 g_free (padname);
1735 1738
1736 gst_pad_use_fixed_caps (pad); 1739 gst_pad_use_fixed_caps (pad);
@@ -1752,16 +1755,31 @@ gst_avi_demux_parse_stream (GstAviDemux * avi, GstBuffer * buf)
1752#endif 1755#endif
1753 1756
1754 stream->num = avi->num_streams; 1757 stream->num = avi->num_streams;
1758
1759 stream->start_entry = 0;
1760 stream->step_entry = 0;
1761 stream->stop_entry = 0;
1762
1763 stream->current_entry = -1;
1764 stream->current_total = 0;
1765
1766 stream->last_flow = GST_FLOW_OK;
1767 stream->discont = TRUE;
1768
1755 stream->total_bytes = 0; 1769 stream->total_bytes = 0;
1756 stream->idx_n = 0;
1757 stream->total_blocks = 0; 1770 stream->total_blocks = 0;
1758 stream->current_entry = 0; 1771 stream->n_keyframes = 0;
1759 stream->current_total = 0; 1772
1773 stream->idx_n = 0;
1774 stream->idx_max = 0;
1775
1760 gst_pad_set_element_private (pad, stream); 1776 gst_pad_set_element_private (pad, stream);
1761 avi->num_streams++; 1777 avi->num_streams++;
1778
1762 gst_pad_set_caps (pad, caps); 1779 gst_pad_set_caps (pad, caps);
1763 gst_pad_set_active (pad, TRUE); 1780 gst_pad_set_active (pad, TRUE);
1764 gst_element_add_pad (GST_ELEMENT (avi), pad); 1781 gst_element_add_pad (GST_ELEMENT (avi), pad);
1782
1765 GST_LOG_OBJECT (element, "Added pad %s with caps %" GST_PTR_FORMAT, 1783 GST_LOG_OBJECT (element, "Added pad %s with caps %" GST_PTR_FORMAT,
1766 GST_PAD_NAME (pad), caps); 1784 GST_PAD_NAME (pad), caps);
1767 gst_caps_unref (caps); 1785 gst_caps_unref (caps);
@@ -1792,15 +1810,7 @@ fail:
1792 gst_buffer_unref (sub); 1810 gst_buffer_unref (sub);
1793 g_free (vprp); 1811 g_free (vprp);
1794 g_free (codec_name); 1812 g_free (codec_name);
1795 g_free (stream->strh); 1813 gst_avi_demux_reset_stream (avi, stream);
1796 g_free (stream->strf.data);
1797 g_free (stream->name);
1798 g_free (stream->indexes);
1799 if (stream->initdata)
1800 gst_buffer_unref (stream->initdata);
1801 if (stream->extradata)
1802 gst_buffer_unref (stream->extradata);
1803 memset (stream, 0, sizeof (GstAviStream));
1804 avi->num_streams++; 1814 avi->num_streams++;
1805 return FALSE; 1815 return FALSE;
1806 } 1816 }
@@ -1918,12 +1928,14 @@ gst_avi_demux_index_entry_search (GstAviIndexEntry * entry, guint64 * total)
1918} 1928}
1919 1929
1920/* 1930/*
1921 * gst_avi_index_entry: 1931 * gst_avi_demux_index_for_time:
1922 * @avi: Avi object 1932 * @avi: Avi object
1923 * @stream: the stream 1933 * @stream: the stream
1924 * @time: seek time position 1934 * @time: a time position
1925 * 1935 *
1926 * Finds the index entry which time is less or equal than the requested time. 1936 * Finds the index entry which time is less or equal than the requested time.
1937 * Try to avoid binary search when we can convert the time to an index
1938 * position directly (for example for video frames with a fixed duration).
1927 * 1939 *
1928 * Returns: the found position in the index. 1940 * Returns: the found position in the index.
1929 */ 1941 */
@@ -1982,6 +1994,8 @@ gst_avi_demux_index_for_time (GstAviDemux * avi,
1982 return index; 1994 return index;
1983} 1995}
1984 1996
1997/* given @entry_n in @stream, calculate info such as timestamps and
1998 * offsets for the entry. */
1985static void 1999static void
1986gst_avi_demux_get_buffer_info (GstAviDemux * avi, GstAviStream * stream, 2000gst_avi_demux_get_buffer_info (GstAviDemux * avi, GstAviStream * stream,
1987 guint entry_n, GstClockTime * timestamp, GstClockTime * ts_end, 2001 guint entry_n, GstClockTime * timestamp, GstClockTime * ts_end,
@@ -2032,6 +2046,136 @@ gst_avi_demux_get_buffer_info (GstAviDemux * avi, GstAviStream * stream,
2032 } 2046 }
2033} 2047}
2034 2048
2049/* collect and debug stats about the indexes for all streams.
2050 * This method is also responsible for filling in the stream duration
2051 * as measured by the amount of index entries. */
2052static void
2053gst_avi_demux_do_index_stats (GstAviDemux * avi)
2054{
2055 guint i;
2056#ifndef GST_DISABLE_GST_DEBUG
2057 guint total_idx = 0, total_max = 0;
2058#endif
2059
2060 /* get stream stats now */
2061 for (i = 0; i < avi->num_streams; i++) {
2062 GstAviIndexEntry *entry;
2063 GstAviStream *stream;
2064 guint64 total;
2065
2066 if (G_UNLIKELY (!(stream = &avi->stream[i])))
2067 continue;
2068 if (G_UNLIKELY (!stream->strh))
2069 continue;
2070 if (G_UNLIKELY (!stream->index || stream->idx_n == 0))
2071 continue;
2072
2073 entry = &stream->index[stream->idx_n - 1];
2074 total = entry->total + entry->size;
2075
2076 /* calculate duration */
2077 if (stream->is_vbr) {
2078 /* VBR stream next timestamp */
2079 if (stream->strh->type == GST_RIFF_FCC_auds) {
2080 stream->idx_duration =
2081 avi_stream_convert_frames_to_time_unchecked (stream, total);
2082 } else {
2083 stream->idx_duration =
2084 avi_stream_convert_frames_to_time_unchecked (stream, stream->idx_n);
2085 }
2086 } else {
2087 /* constant rate stream */
2088 stream->idx_duration = avi_stream_convert_bytes_to_time_unchecked (stream,
2089 total);
2090 }
2091#ifndef GST_DISABLE_GST_DEBUG
2092 total_idx += stream->idx_n;
2093 total_max += stream->idx_max;
2094#endif
2095 GST_INFO_OBJECT (avi, "Stream %d, dur %" GST_TIME_FORMAT ", %6u entries, "
2096 "%5u keyframes, entry size = %2u, total size = %10u, allocated %10u",
2097 i, GST_TIME_ARGS (stream->idx_duration), stream->idx_n,
2098 stream->n_keyframes, (guint) sizeof (GstAviIndexEntry),
2099 (guint) (stream->idx_n * sizeof (GstAviIndexEntry)),
2100 (guint) (stream->idx_max * sizeof (GstAviIndexEntry)));
2101 }
2102#ifndef GST_DISABLE_GST_DEBUG
2103 total_idx *= sizeof (GstAviIndexEntry);
2104 total_max *= sizeof (GstAviIndexEntry);
2105#endif
2106 GST_INFO_OBJECT (avi, "%u bytes for index vs %u ideally, %u wasted",
2107 total_max, total_idx, total_max - total_idx);
2108}
2109
2110/* add an entry to the index of a stream. @num should be an estimate of the
2111 * total amount of index entries for all streams and is used to dynamically
2112 * allocate memory for the index entries. */
2113static inline gboolean
2114gst_avi_demux_add_index (GstAviDemux * avi, GstAviStream * stream,
2115 guint num, GstAviIndexEntry * entry)
2116{
2117 /* ensure index memory */
2118 if (G_UNLIKELY (stream->idx_n >= stream->idx_max)) {
2119 /* we need to make some more room */
2120 if (stream->idx_max == 0) {
2121 /* initial size guess, assume each stream has an equal amount of entries,
2122 * overshoot with at least 8K */
2123 stream->idx_max =
2124 (num / avi->num_streams) + (8192 / sizeof (GstAviIndexEntry));
2125 } else {
2126 stream->idx_max += 8192 / sizeof (GstAviIndexEntry);
2127 GST_CAT_DEBUG (GST_CAT_PERFORMANCE, "expanded index to %u",
2128 stream->idx_max);
2129 }
2130 stream->index = g_try_renew (GstAviIndexEntry, stream->index,
2131 stream->idx_max);
2132 if (G_UNLIKELY (!stream->index))
2133 return FALSE;
2134 }
2135
2136 /* update stream stats and total size */
2137 entry->total = stream->total_bytes;
2138 stream->total_bytes += entry->size;
2139 if (stream->strh->type == GST_RIFF_FCC_auds) {
2140 gint blockalign = stream->strf.auds->blockalign;
2141 if (blockalign > 0)
2142 stream->total_blocks += DIV_ROUND_UP (entry->size, blockalign);
2143 else
2144 stream->total_blocks++;
2145 }
2146 if (ENTRY_IS_KEYFRAME (entry))
2147 stream->n_keyframes++;
2148
2149 /* and add */
2150 GST_LOG_OBJECT (avi,
2151 "Adding stream %u, index entry %d, kf %d, size %u "
2152 ", offset %" G_GUINT64_FORMAT ", total %" G_GUINT64_FORMAT, stream->num,
2153 stream->idx_n, ENTRY_IS_KEYFRAME (entry), entry->size, entry->offset,
2154 entry->total);
2155 stream->index[stream->idx_n++] = *entry;
2156
2157 return TRUE;
2158}
2159
2160static inline GstAviStream *
2161gst_avi_demux_stream_for_id (GstAviDemux * avi, guint32 id)
2162{
2163 guint stream_nr;
2164 GstAviStream *stream;
2165
2166 /* get the stream for this entry */
2167 stream_nr = CHUNKID_TO_STREAMNR (id);
2168 if (G_UNLIKELY (stream_nr >= avi->num_streams)) {
2169 GST_WARNING_OBJECT (avi, "invalid stream nr %d", stream_nr);
2170 return NULL;
2171 }
2172 stream = &avi->stream[stream_nr];
2173 if (G_UNLIKELY (!stream->strh)) {
2174 GST_WARNING_OBJECT (avi, "Unhandled stream %d, skipping", stream_nr);
2175 return NULL;
2176 }
2177 return stream;
2178}
2035 2179
2036/* 2180/*
2037 * gst_avi_demux_parse_index: 2181 * gst_avi_demux_parse_index:
@@ -2041,7 +2185,7 @@ gst_avi_demux_get_buffer_info (GstAviDemux * avi, GstAviStream * stream,
2041 * Read index entries from the provided buffer. 2185 * Read index entries from the provided buffer.
2042 * The buffer should contain a GST_RIFF_TAG_idx1 chunk. 2186 * The buffer should contain a GST_RIFF_TAG_idx1 chunk.
2043 */ 2187 */
2044static void 2188static gboolean
2045gst_avi_demux_parse_index (GstAviDemux * avi, GstBuffer * buf) 2189gst_avi_demux_parse_index (GstAviDemux * avi, GstBuffer * buf)
2046{ 2190{
2047 guint64 pos_before; 2191 guint64 pos_before;
@@ -2051,9 +2195,8 @@ gst_avi_demux_parse_index (GstAviDemux * avi, GstBuffer * buf)
2051 gst_riff_index_entry *index; 2195 gst_riff_index_entry *index;
2052 GstClockTime stamp; 2196 GstClockTime stamp;
2053 GstAviStream *stream; 2197 GstAviStream *stream;
2054#ifndef GST_DISABLE_GST_DEBUG 2198 GstAviIndexEntry entry;
2055 guint total_idx = 0, total_max = 0; 2199 guint32 id;
2056#endif
2057 2200
2058 if (!buf) 2201 if (!buf)
2059 goto empty_list; 2202 goto empty_list;
@@ -2063,20 +2206,27 @@ gst_avi_demux_parse_index (GstAviDemux * avi, GstBuffer * buf)
2063 2206
2064 stamp = gst_util_get_timestamp (); 2207 stamp = gst_util_get_timestamp ();
2065 2208
2209 /* see how many items in the index */
2066 num = size / sizeof (gst_riff_index_entry); 2210 num = size / sizeof (gst_riff_index_entry);
2067 if (num == 0) 2211 if (num == 0)
2068 goto empty_list; 2212 goto empty_list;
2069 2213
2070 index = (gst_riff_index_entry *) data; 2214 GST_INFO_OBJECT (avi, "Parsing index, nr_entries = %6d", num);
2071 2215
2216 index = (gst_riff_index_entry *) data;
2072 pos_before = avi->offset; 2217 pos_before = avi->offset;
2073 GST_INFO_OBJECT (avi, "Parsing index, nr_entries = %6d", num);
2074 2218
2075 for (i = 0, n = 0; i < num; i++) { 2219 /* figure out if the index is 0 based or relative to the MOVI start */
2076 GstAviIndexEntry entry; 2220 entry.offset = GST_READ_UINT32_LE (&index[0].offset);
2077 guint32 id; 2221 if (entry.offset < avi->offset) {
2078 guint stream_nr; 2222 avi->index_offset = avi->offset + 8;
2223 GST_DEBUG ("index_offset = %" G_GUINT64_FORMAT, avi->index_offset);
2224 } else {
2225 avi->index_offset = 0;
2226 GST_DEBUG ("index is 0 based");
2227 }
2079 2228
2229 for (i = 0, n = 0; i < num; i++) {
2080 id = GST_READ_UINT32_LE (&index[i].id); 2230 id = GST_READ_UINT32_LE (&index[i].id);
2081 entry.offset = GST_READ_UINT32_LE (&index[i].offset); 2231 entry.offset = GST_READ_UINT32_LE (&index[i].offset);
2082 2232
@@ -2085,148 +2235,53 @@ gst_avi_demux_parse_index (GstAviDemux * avi, GstBuffer * buf)
2085 (entry.offset == 0 && n > 0))) 2235 (entry.offset == 0 && n > 0)))
2086 continue; 2236 continue;
2087 2237
2088 /* figure out if the index is 0 based or relative to the MOVI start */
2089 if (G_UNLIKELY (n == 0)) {
2090 if (entry.offset < pos_before)
2091 avi->index_offset = pos_before + 8;
2092 else
2093 avi->index_offset = 0;
2094 GST_DEBUG ("index_offset = %" G_GUINT64_FORMAT, avi->index_offset);
2095 }
2096
2097 /* get the stream for this entry */ 2238 /* get the stream for this entry */
2098 stream_nr = CHUNKID_TO_STREAMNR (id); 2239 stream = gst_avi_demux_stream_for_id (avi, id);
2099 if (G_UNLIKELY (stream_nr >= avi->num_streams)) { 2240 if (G_UNLIKELY (!stream))
2100 GST_WARNING_OBJECT (avi,
2101 "Index entry %d has invalid stream nr %d", i, stream_nr);
2102 continue; 2241 continue;
2103 } 2242
2104 stream = &avi->stream[stream_nr]; 2243 /* handle offset and size */
2105 if (G_UNLIKELY (!stream->strh)) { 2244 entry.offset += avi->index_offset + 8;
2106 GST_WARNING_OBJECT (avi, "Unhandled stream %d, skipping", stream_nr); 2245 entry.size = GST_READ_UINT32_LE (&index[i].size);
2107 continue;
2108 }
2109 2246
2110 /* handle flags */ 2247 /* handle flags */
2111 if (stream->strh->type == GST_RIFF_FCC_auds) { 2248 if (stream->strh->type == GST_RIFF_FCC_auds) {
2112 /* all audio frames are keyframes */ 2249 /* all audio frames are keyframes */
2113 ENTRY_SET_KEYFRAME (&entry); 2250 ENTRY_SET_KEYFRAME (&entry);
2114 stream->n_keyframes++;
2115 } else { 2251 } else {
2116 guint32 flags; 2252 guint32 flags;
2117 /* else read flags */ 2253 /* else read flags */
2118 flags = GST_READ_UINT32_LE (&index[i].flags); 2254 flags = GST_READ_UINT32_LE (&index[i].flags);
2119 if (flags & GST_RIFF_IF_KEYFRAME) { 2255 if (flags & GST_RIFF_IF_KEYFRAME) {
2120 ENTRY_SET_KEYFRAME (&entry); 2256 ENTRY_SET_KEYFRAME (&entry);
2121 stream->n_keyframes++;
2122 } else { 2257 } else {
2123 ENTRY_UNSET_KEYFRAME (&entry); 2258 ENTRY_UNSET_KEYFRAME (&entry);
2124 } 2259 }
2125 } 2260 }
2126 2261
2127 /* handle size */ 2262 /* and add */
2128 entry.size = GST_READ_UINT32_LE (&index[i].size); 2263 if (G_UNLIKELY (!gst_avi_demux_add_index (avi, stream, num, &entry)))
2129 2264 goto out_of_mem;
2130 /* update stats */
2131 entry.total = stream->total_bytes;
2132 stream->total_bytes += entry.size;
2133 if (stream->strh->type == GST_RIFF_FCC_auds) {
2134 gint blockalign = stream->strf.auds->blockalign;
2135 if (blockalign > 0)
2136 stream->total_blocks += DIV_ROUND_UP (entry.size, blockalign);
2137 else
2138 stream->total_blocks++;
2139 }
2140
2141 /* add to the index */
2142 if (G_UNLIKELY (stream->idx_n >= stream->idx_max)) {
2143 /* we need to make some more room */
2144 if (stream->idx_max == 0) {
2145 /* initial size guess, assume each stream has an equal amount of entries,
2146 * overshoot with at least 8K */
2147 stream->idx_max =
2148 (num / avi->num_streams) + (8192 / sizeof (GstAviIndexEntry));
2149 } else {
2150 stream->idx_max += 8192 / sizeof (GstAviIndexEntry);
2151 GST_CAT_DEBUG (GST_CAT_PERFORMANCE, "expanded index to %u",
2152 stream->idx_max);
2153 }
2154 stream->index = g_try_renew (GstAviIndexEntry, stream->index,
2155 stream->idx_max);
2156 if (G_UNLIKELY (!stream->index))
2157 goto out_of_mem;
2158 }
2159
2160 GST_LOG_OBJECT (avi,
2161 "Adding stream %u, index entry %d, kf %d, size %u "
2162 ", offset %" G_GUINT64_FORMAT ", total %" G_GUINT64_FORMAT, stream_nr,
2163 stream->idx_n, ENTRY_IS_KEYFRAME (&entry), entry.size, entry.offset,
2164 entry.total);
2165
2166 /* and copy */
2167 stream->index[stream->idx_n++] = entry;
2168 2265
2169 n++; 2266 n++;
2170 } 2267 }
2171 /* get stream stats now */ 2268 /* get stream stats now */
2172 for (i = 0; i < avi->num_streams; i++) { 2269 gst_avi_demux_do_index_stats (avi);
2173 GstAviIndexEntry *entry;
2174 guint64 total;
2175
2176 if (G_UNLIKELY (!(stream = &avi->stream[i])))
2177 continue;
2178 if (G_UNLIKELY (!stream->strh))
2179 continue;
2180 if (G_UNLIKELY (!stream->index || stream->idx_n == 0))
2181 continue;
2182
2183 entry = &stream->index[stream->idx_n - 1];
2184 total = entry->total + entry->size;
2185
2186 /* calculate duration */
2187 if (stream->is_vbr) {
2188 /* VBR stream next timestamp */
2189 if (stream->strh->type == GST_RIFF_FCC_auds) {
2190 stream->idx_duration =
2191 avi_stream_convert_frames_to_time_unchecked (stream, total);
2192 } else {
2193 stream->idx_duration =
2194 avi_stream_convert_frames_to_time_unchecked (stream, stream->idx_n);
2195 }
2196 } else {
2197 /* constant rate stream */
2198 stream->idx_duration = avi_stream_convert_bytes_to_time_unchecked (stream,
2199 total);
2200 }
2201#ifndef GST_DISABLE_GST_DEBUG
2202 total_idx += stream->idx_n;
2203 total_max += stream->idx_max;
2204#endif
2205 GST_INFO_OBJECT (avi, "Stream %d, dur %" GST_TIME_FORMAT ", %6u entries, "
2206 "%5u keyframes, entry size = %2u, total size = %10u, allocated %10u",
2207 i, GST_TIME_ARGS (stream->idx_duration), stream->idx_n,
2208 stream->n_keyframes, (guint) sizeof (GstAviIndexEntry),
2209 (guint) (stream->idx_n * sizeof (GstAviIndexEntry)),
2210 (guint) (stream->idx_max * sizeof (GstAviIndexEntry)));
2211 }
2212#ifndef GST_DISABLE_GST_DEBUG
2213 total_idx *= sizeof (GstAviIndexEntry);
2214 total_max *= sizeof (GstAviIndexEntry);
2215#endif
2216 GST_INFO_OBJECT (avi, "%u bytes for index vs %u ideally, %u wasted",
2217 total_max, total_idx, total_max - total_idx);
2218 2270
2219 stamp = gst_util_get_timestamp () - stamp; 2271 stamp = gst_util_get_timestamp () - stamp;
2220 GST_CAT_DEBUG (GST_CAT_PERFORMANCE, "parsing index %" GST_TIME_FORMAT, 2272 GST_CAT_DEBUG (GST_CAT_PERFORMANCE, "parsing index %" GST_TIME_FORMAT,
2221 GST_TIME_ARGS (stamp)); 2273 GST_TIME_ARGS (stamp));
2222 2274
2223 return; 2275 /* we have an index now */
2276 avi->have_index = TRUE;
2277
2278 return TRUE;
2224 2279
2225 /* ERRORS */ 2280 /* ERRORS */
2226empty_list: 2281empty_list:
2227 { 2282 {
2228 GST_DEBUG_OBJECT (avi, "empty index"); 2283 GST_DEBUG_OBJECT (avi, "empty index");
2229 return; 2284 return FALSE;
2230 } 2285 }
2231out_of_mem: 2286out_of_mem:
2232 { 2287 {
@@ -2234,7 +2289,7 @@ out_of_mem:
2234 ("Cannot allocate memory for %u*%u=%u bytes", 2289 ("Cannot allocate memory for %u*%u=%u bytes",
2235 (guint) sizeof (GstAviIndexEntry), num, 2290 (guint) sizeof (GstAviIndexEntry), num,
2236 (guint) sizeof (GstAviIndexEntry) * num)); 2291 (guint) sizeof (GstAviIndexEntry) * num));
2237 return; 2292 return FALSE;
2238 } 2293 }
2239} 2294}
2240 2295
@@ -2344,148 +2399,6 @@ zero_index:
2344 } 2399 }
2345} 2400}
2346 2401
2347#if 0
2348/*
2349 * Sync to next data chunk.
2350 */
2351static gboolean
2352gst_avi_demux_skip (GstAviDemux * avi, gboolean prevent_eos)
2353{
2354 GstRiffRead *riff = GST_RIFF_READ (avi);
2355
2356 if (prevent_eos) {
2357 guint64 pos, length;
2358 guint size;
2359 guint8 *data;
2360
2361 pos = gst_bytestream_tell (riff->bs);
2362 length = gst_bytestream_length (riff->bs);
2363
2364 if (pos + 8 > length)
2365 return FALSE;
2366
2367 if (gst_bytestream_peek_bytes (riff->bs, &data, 8) != 8)
2368 return FALSE;
2369
2370 size = GST_READ_UINT32_LE (&data[4]);
2371 if (size & 1)
2372 size++;
2373
2374 /* Note, we're going to skip which might involve seeks. Therefore,
2375 * we need 1 byte more! */
2376 if (pos + 8 + size >= length)
2377 return FALSE;
2378 }
2379
2380 return gst_riff_read_skip (riff);
2381}
2382
2383static gboolean
2384gst_avi_demux_sync (GstAviDemux * avi, guint32 * ret_tag, gboolean prevent_eos)
2385{
2386 GstRiffRead *riff = GST_RIFF_READ (avi);
2387 guint32 tag;
2388 guint64 length = gst_bytestream_length (riff->bs);
2389
2390 if (prevent_eos && gst_bytestream_tell (riff->bs) + 12 >= length)
2391 return FALSE;
2392
2393 /* peek first (for the end of this 'list/movi' section) */
2394 if (!(tag = gst_riff_peek_tag (riff, &avi->level_up)))
2395 return FALSE;
2396
2397 /* if we're at top-level, we didn't read the 'movi'
2398 * list tag yet. This can also be 'AVIX' in case of
2399 * openDML-2.0 AVI files. Lastly, it might be idx1,
2400 * in which case we skip it so we come at EOS. */
2401 while (1) {
2402 if (prevent_eos && gst_bytestream_tell (riff->bs) + 12 >= length)
2403 return FALSE;
2404
2405 if (!(tag = gst_riff_peek_tag (riff, NULL)))
2406 return FALSE;
2407
2408 switch (tag) {
2409 case GST_RIFF_TAG_LIST:
2410 if (!(tag = gst_riff_peek_list (riff)))
2411 return FALSE;
2412
2413 switch (tag) {
2414 case GST_RIFF_LIST_AVIX:
2415 if (!gst_riff_read_list (riff, &tag))
2416 return FALSE;
2417 break;
2418
2419 case GST_RIFF_LIST_movi:
2420 if (!gst_riff_read_list (riff, &tag))
2421 return FALSE;
2422 /* fall-through */
2423
2424 case GST_RIFF_rec:
2425 goto done;
2426
2427 default:
2428 GST_WARNING ("Unknown list %" GST_FOURCC_FORMAT " before AVI data",
2429 GST_FOURCC_ARGS (tag));
2430 /* fall-through */
2431
2432 case GST_RIFF_TAG_JUNK:
2433 if (!gst_avi_demux_skip (avi, prevent_eos))
2434 return FALSE;
2435 break;
2436 }
2437 break;
2438
2439 default:
2440 if ((tag & 0xff) >= '0' && (tag & 0xff) <= '9' &&
2441 ((tag >> 8) & 0xff) >= '0' && ((tag >> 8) & 0xff) <= '9') {
2442 goto done;
2443 }
2444 /* pass-through */
2445
2446 case GST_RIFF_TAG_idx1:
2447 case GST_RIFF_TAG_JUNK:
2448 if (!gst_avi_demux_skip (avi, prevent_eos)) {
2449 return FALSE;
2450 }
2451 break;
2452 }
2453 }
2454done:
2455 /* And then, we get the data */
2456 if (prevent_eos && gst_bytestream_tell (riff->bs) + 12 >= length)
2457 return FALSE;
2458
2459 if (!(tag = gst_riff_peek_tag (riff, NULL)))
2460 return FALSE;
2461
2462 /* Support for rec-list files */
2463 switch (tag) {
2464 case GST_RIFF_TAG_LIST:
2465 if (!(tag = gst_riff_peek_list (riff)))
2466 return FALSE;
2467 if (tag == GST_RIFF_rec) {
2468 /* Simply skip the list */
2469 if (!gst_riff_read_list (riff, &tag))
2470 return FALSE;
2471 if (!(tag = gst_riff_peek_tag (riff, NULL)))
2472 return FALSE;
2473 }
2474 break;
2475
2476 case GST_RIFF_TAG_JUNK:
2477 gst_avi_demux_skip (avi, prevent_eos);
2478 return FALSE;
2479 }
2480
2481 if (ret_tag)
2482 *ret_tag = tag;
2483
2484 return TRUE;
2485}
2486#endif
2487
2488#if 0
2489/* 2402/*
2490 * gst_avi_demux_peek_tag: 2403 * gst_avi_demux_peek_tag:
2491 * 2404 *
@@ -2498,6 +2411,7 @@ gst_avi_demux_peek_tag (GstAviDemux * avi, guint64 offset, guint32 * tag,
2498 GstFlowReturn res = GST_FLOW_OK; 2411 GstFlowReturn res = GST_FLOW_OK;
2499 GstBuffer *buf = NULL; 2412 GstBuffer *buf = NULL;
2500 guint bufsize; 2413 guint bufsize;
2414 guint8 *bufdata;
2501 2415
2502 res = gst_pad_pull_range (avi->sinkpad, offset, 8, &buf); 2416 res = gst_pad_pull_range (avi->sinkpad, offset, 8, &buf);
2503 if (res != GST_FLOW_OK) 2417 if (res != GST_FLOW_OK)
@@ -2507,12 +2421,15 @@ gst_avi_demux_peek_tag (GstAviDemux * avi, guint64 offset, guint32 * tag,
2507 if (bufsize != 8) 2421 if (bufsize != 8)
2508 goto wrong_size; 2422 goto wrong_size;
2509 2423
2510 *tag = GST_READ_UINT32_LE (GST_BUFFER_DATA (buf)); 2424 bufdata = GST_BUFFER_DATA (buf);
2511 *size = GST_READ_UINT32_LE (GST_BUFFER_DATA (buf) + 4); 2425
2426 *tag = GST_READ_UINT32_LE (bufdata);
2427 *size = GST_READ_UINT32_LE (bufdata + 4);
2512 2428
2513 GST_LOG_OBJECT (avi, "Tag[%" GST_FOURCC_FORMAT "] (size:%d) %" 2429 GST_LOG_OBJECT (avi, "Tag[%" GST_FOURCC_FORMAT "] (size:%d) %"
2514 G_GINT64_FORMAT " -- %" G_GINT64_FORMAT, GST_FOURCC_ARGS (*tag), 2430 G_GINT64_FORMAT " -- %" G_GINT64_FORMAT, GST_FOURCC_ARGS (*tag),
2515 *size, offset + 8, offset + 8 + (gint64) * size); 2431 *size, offset + 8, offset + 8 + (gint64) * size);
2432
2516done: 2433done:
2517 gst_buffer_unref (buf); 2434 gst_buffer_unref (buf);
2518 2435
@@ -2531,9 +2448,7 @@ wrong_size:
2531 goto done; 2448 goto done;
2532 } 2449 }
2533} 2450}
2534#endif
2535 2451
2536#if 0
2537/* 2452/*
2538 * gst_avi_demux_next_data_buffer: 2453 * gst_avi_demux_next_data_buffer:
2539 * 2454 *
@@ -2563,132 +2478,63 @@ gst_avi_demux_next_data_buffer (GstAviDemux * avi, guint64 * offset,
2563 2478
2564 return res; 2479 return res;
2565} 2480}
2566#endif
2567 2481
2568#if 0
2569/* 2482/*
2570 * gst_avi_demux_stream_scan: 2483 * gst_avi_demux_stream_scan:
2571 * @avi: calling element (used for debugging/errors). 2484 * @avi: calling element (used for debugging/errors).
2572 * @index: list of index entries, returned by this function.
2573 * @alloc_list: list of allocated data, returned by this function.
2574 * 2485 *
2575 * Scan the file for all chunks to "create" a new index. 2486 * Scan the file for all chunks to "create" a new index.
2576 * Return value indicates if we can continue reading the stream. It
2577 * does not say anything about whether we created an index.
2578 *
2579 * pull-range based 2487 * pull-range based
2580 */ 2488 */
2581static gboolean 2489static gboolean
2582gst_avi_demux_stream_scan (GstAviDemux * avi, 2490gst_avi_demux_stream_scan (GstAviDemux * avi)
2583 GList ** index, GList ** alloc_list)
2584{ 2491{
2585 GstFlowReturn res; 2492 GstFlowReturn res;
2586 gst_avi_index_entry *entry, *entries = NULL;
2587 GstAviStream *stream; 2493 GstAviStream *stream;
2588 GstFormat format; 2494 GstFormat format;
2589 guint64 pos = avi->offset; 2495 guint64 pos;
2590 guint64 length; 2496 guint64 length;
2591 gint64 tmplength; 2497 gint64 tmplength;
2592 guint32 tag = 0; 2498 guint32 tag = 0;
2593 GList *list = NULL; 2499 guint num;
2594 guint index_size = 0;
2595 2500
2596 /* FIXME: 2501 /* FIXME:
2597 * - implement non-seekable source support. 2502 * - implement non-seekable source support.
2598 */ 2503 */
2599 GST_DEBUG_OBJECT (avi, 2504 GST_DEBUG_OBJECT (avi,
2600 "Creating index %s existing index, starting at offset %" G_GUINT64_FORMAT, 2505 "Creating index starting at offset %" G_GUINT64_FORMAT, pos);
2601 ((*index) ? "with" : "without"), pos);
2602 2506
2507 /* get the size of the file */
2603 format = GST_FORMAT_BYTES; 2508 format = GST_FORMAT_BYTES;
2604 if (!gst_pad_query_peer_duration (avi->sinkpad, &format, &tmplength)) 2509 if (!gst_pad_query_peer_duration (avi->sinkpad, &format, &tmplength))
2605 return FALSE; 2510 return FALSE;
2606
2607 length = tmplength; 2511 length = tmplength;
2608 2512
2609 if (*index) { 2513 /* guess the total amount of entries we expect */
2610 entry = g_list_last (*index)->data; 2514 num = 16000;
2611 pos = entry->offset + avi->index_offset + entry->size;
2612 if (entry->size & 1)
2613 pos++;
2614
2615 if (pos >= length) {
2616 GST_LOG_OBJECT (avi, "Complete index, we're done");
2617 return TRUE;
2618 }
2619
2620 GST_LOG_OBJECT (avi, "Incomplete index, seeking to last valid entry @ %"
2621 G_GUINT64_FORMAT " of %" G_GUINT64_FORMAT " (%"
2622 G_GUINT64_FORMAT "+%u)", pos, length, entry->offset, entry->size);
2623 }
2624 2515
2625 while (TRUE) { 2516 while (TRUE) {
2626 guint stream_nr; 2517 GstAviIndexEntry entry;
2627 guint size = 0; 2518 guint size = 0;
2628 2519
2520 /* start reading data buffers to find the id and offset */
2629 res = gst_avi_demux_next_data_buffer (avi, &pos, &tag, &size); 2521 res = gst_avi_demux_next_data_buffer (avi, &pos, &tag, &size);
2630 if (G_UNLIKELY (res != GST_FLOW_OK)) 2522 if (G_UNLIKELY (res != GST_FLOW_OK))
2631 break; 2523 break;
2632 2524
2633 /* check valid stream */ 2525 /* get stream */
2634 stream_nr = CHUNKID_TO_STREAMNR (tag); 2526 stream = gst_avi_demux_stream_for_id (avi, tag);
2635 if (G_UNLIKELY (stream_nr >= avi->num_streams)) { 2527 if (G_UNLIKELY (!stream))
2636 GST_WARNING_OBJECT (avi,
2637 "Index entry has invalid stream nr %d", stream_nr);
2638 goto next;
2639 }
2640
2641 stream = &avi->stream[stream_nr];
2642 if (G_UNLIKELY (stream->pad == NULL)) {
2643 GST_WARNING_OBJECT (avi,
2644 "Stream %d does not have an output pad, can't create new index",
2645 stream_nr);
2646 goto next; 2528 goto next;
2647 }
2648
2649 /* pre-allocate */
2650 if (G_UNLIKELY (index_size % 1024 == 0)) {
2651 entries = g_new (gst_avi_index_entry, 1024);
2652 *alloc_list = g_list_prepend (*alloc_list, entries);
2653 }
2654 entry = &entries[index_size % 1024];
2655 2529
2656 entry->index_nr = index_size++; 2530 /* we can't figure out the keyframes, assume they all are */
2657 entry->stream_nr = stream_nr; 2531 entry.flags = GST_AVI_KEYFRAME;
2658 entry->flags = GST_AVI_INDEX_ENTRY_FLAG_KEYFRAME; 2532 entry.offset = pos;
2659 entry->offset = pos - avi->index_offset; 2533 entry.size = size;
2660 entry->size = size;
2661 2534
2662 /* timestamps, get timestamps of two consecutive frames to calculate 2535 /* and add to the index of this stream */
2663 * timestamp and duration. */ 2536 if (G_UNLIKELY (!gst_avi_demux_add_index (avi, stream, num, &entry)))
2664 format = GST_FORMAT_TIME; 2537 goto out_of_mem;
2665 if (stream->is_vbr) {
2666 /* VBR stream */
2667 entry->ts = avi_stream_convert_frames_to_time_unchecked (stream,
2668 stream->idx_n);
2669 entry->dur = avi_stream_convert_frames_to_time_unchecked (stream,
2670 stream->idx_n + 1);
2671 } else {
2672 /* constant rate stream */
2673 entry->ts = avi_stream_convert_bytes_to_time_unchecked (stream,
2674 stream->total_bytes);
2675 entry->dur = avi_stream_convert_bytes_to_time_unchecked (stream,
2676 stream->total_bytes + entry->size);
2677 }
2678 entry->dur -= entry->ts;
2679
2680 /* stream position */
2681 entry->bytes_before = stream->total_bytes;
2682 stream->total_bytes += entry->size;
2683 entry->frames_before = stream->idx_n;
2684 stream->idx_n++;
2685 stream->idx_duration = entry->ts + entry->dur;
2686
2687 list = g_list_prepend (list, entry);
2688 GST_DEBUG_OBJECT (avi, "Added index entry %d (in stream: %d), offset %"
2689 G_GUINT64_FORMAT ", time %" GST_TIME_FORMAT " for stream %d",
2690 index_size - 1, entry->frames_before, entry->offset,
2691 GST_TIME_ARGS (entry->ts), entry->stream_nr);
2692 2538
2693 next: 2539 next:
2694 /* update position */ 2540 /* update position */
@@ -2699,314 +2545,47 @@ gst_avi_demux_stream_scan (GstAviDemux * avi,
2699 break; 2545 break;
2700 } 2546 }
2701 } 2547 }
2548 /* collect stats */
2549 gst_avi_demux_do_index_stats (avi);
2702 2550
2703 /* FIXME: why is this disabled */ 2551 /* we have an index now */
2704#if 0 2552 avi->have_index = TRUE;
2705 while (gst_avi_demux_sync (avi, &tag, TRUE)) {
2706 guint stream_nr = CHUNKID_TO_STREAMNR (tag);
2707 guint8 *data;
2708 GstFormat format = GST_FORMAT_TIME;
2709
2710 if (stream_nr >= avi->num_streams)
2711 goto next;
2712 stream = &avi->stream[stream_nr];
2713
2714 /* get chunk size */
2715 if (gst_bytestream_peek_bytes (riff->bs, &data, 8) != 8)
2716 goto next;
2717
2718 /* fill in */
2719 entry->index_nr = index_size++;
2720 entry->stream_nr = stream_nr;
2721 entry->flags = GST_AVI_INDEX_ENTRY_FLAG_KEYFRAME;
2722 entry->offset = gst_bytestream_tell (riff->bs) + 8 - avi->index_offset;
2723 entry->size = GST_READ_UINT32_LE (&data[4]);
2724
2725 /* timestamps */
2726 if (stream->is_vbr) {
2727 /* VBR stream */
2728 entry->ts = avi_stream_convert_frames_to_time_unchecked (stream,
2729 stream->idx_n);
2730 entry->dur = avi_stream_convert_frames_to_time_unchecked (stream,
2731 stream->idx_n + 1);
2732 } else {
2733 /* constant rate stream */
2734 entry->ts = avi_stream_convert_bytes_to_time_unchecked (stream,
2735 stream->total_bytes);
2736 entry->dur = avi_stream_convert_bytes_to_time_unchecked (stream,
2737 stream->total_bytes + entry->size);
2738 }
2739 entry->dur -= entry->ts;
2740
2741 /* stream position */
2742 entry->bytes_before = stream->total_bytes;
2743 stream->total_bytes += entry->size;
2744 entry->frames_before = stream->idx_n;
2745 stream->idx_n++;
2746
2747 list = g_list_prepend (list, entry);
2748 GST_DEBUG_OBJECT (avi, "Added index entry %d (in stream: %d), offset %"
2749 G_GUINT64_FORMAT ", time %" GST_TIME_FORMAT " for stream %d",
2750 index_size - 1, entry->frames_before, entry->offset,
2751 GST_TIME_ARGS (entry->ts), entry->stream_nr);
2752
2753 next:
2754 if (!gst_avi_demux_skip (avi, TRUE))
2755 break;
2756 }
2757 /* seek back */
2758 if (!(event = gst_riff_read_seek (riff, pos))) {
2759 g_list_free (list);
2760 return FALSE;
2761 }
2762 gst_event_unref (event);
2763
2764#endif
2765
2766 GST_DEBUG_OBJECT (avi, "index created, %d items", index_size);
2767
2768 *index = g_list_concat (*index, g_list_reverse (list));
2769
2770 return TRUE;
2771}
2772#endif
2773
2774#if 0
2775/*
2776 * gst_avi_demux_massage_index:
2777 * @avi: calling element (used for debugging/errors).
2778 *
2779 * We're going to go over each entry in the index and finetune
2780 * some things we don't like about AVI. For example, a single
2781 * chunk might be too long. Also, individual streams might be
2782 * out-of-sync. In the first case, we cut the chunk in several
2783 * smaller pieces. In the second case, we re-order chunk reading
2784 * order. The end result should be a smoother playing AVI.
2785 */
2786static gboolean
2787gst_avi_demux_massage_index (GstAviDemux * avi,
2788 GList * list, GList * alloc_list)
2789{
2790 gst_avi_index_entry *entry;
2791 GstAviStream *stream;
2792 guint i;
2793 GList *node;
2794 gint64 delay = G_GINT64_CONSTANT (0);
2795 GstClockTime stamp;
2796
2797 stamp = gst_util_get_timestamp ();
2798
2799 GST_LOG_OBJECT (avi, "Starting index massage, nr_entries = %d",
2800 list ? g_list_length (list) : 0);
2801
2802 if (list) {
2803#ifndef GST_DISABLE_GST_DEBUG
2804 guint num_added_total = 0;
2805 guint num_per_stream[GST_AVI_DEMUX_MAX_STREAMS] = { 0, };
2806#endif
2807 GST_LOG_OBJECT (avi,
2808 "I'm now going to cut large chunks into smaller pieces");
2809
2810 /* cut chunks in small (seekable) pieces
2811 * FIXME: this should be a property where a value of
2812 * GST_CLOCK_TIME_NONE would disable the chunking
2813 */
2814#define MAX_DURATION (GST_SECOND / 2)
2815 for (i = 0; i < avi->num_streams; i++) {
2816 /* only chop streams that have exactly *one* chunk */
2817 if (avi->stream[i].idx_n != 1)
2818 continue;
2819
2820 for (node = list; node != NULL; node = node->next) {
2821 entry = node->data;
2822
2823 if (entry->stream_nr != i)
2824 continue;
2825
2826 /* check for max duration of a single buffer. I suppose that
2827 * the allocation of index entries could be improved. */
2828 stream = &avi->stream[entry->stream_nr];
2829 if (entry->dur > MAX_DURATION
2830 && stream->strh->type == GST_RIFF_FCC_auds) {
2831 guint32 ideal_size;
2832 gst_avi_index_entry *entries;
2833 guint old_size, num_added;
2834 GList *node2;
2835
2836 /* cut in 1/10th of a second */
2837 ideal_size = stream->strf.auds->av_bps / 10;
2838
2839 /* ensure chunk size is multiple of blockalign */
2840 if (stream->strf.auds->blockalign > 1)
2841 ideal_size -= ideal_size % stream->strf.auds->blockalign;
2842
2843 /* copy index */
2844 old_size = entry->size;
2845 num_added = (entry->size - 1) / ideal_size;
2846 avi->index_size += num_added;
2847 entries = g_malloc (sizeof (gst_avi_index_entry) * num_added);
2848 alloc_list = g_list_prepend (alloc_list, entries);
2849 for (node2 = node->next; node2 != NULL; node2 = node2->next) {
2850 gst_avi_index_entry *entry2 = node2->data;
2851
2852 entry2->index_nr += num_added;
2853 if (entry2->stream_nr == entry->stream_nr)
2854 entry2->frames_before += num_added;
2855 }
2856
2857 /* new sized index chunks */
2858 for (i = 0; i < num_added + 1; i++) {
2859 gst_avi_index_entry *entry2;
2860
2861 if (i == 0) {
2862 entry2 = entry;
2863 } else {
2864 entry2 = &entries[i - 1];
2865 list = g_list_insert_before (list, node->next, entry2);
2866 entry = node->data;
2867 node = node->next;
2868 memcpy (entry2, entry, sizeof (gst_avi_index_entry));
2869 }
2870
2871 if (old_size >= ideal_size) {
2872 entry2->size = ideal_size;
2873 old_size -= ideal_size;
2874 } else {
2875 entry2->size = old_size;
2876 }
2877
2878 entry2->dur = GST_SECOND * entry2->size / stream->strf.auds->av_bps;
2879 if (i != 0) {
2880 entry2->index_nr++;
2881 entry2->ts += entry->dur;
2882 entry2->offset += entry->size;
2883 entry2->bytes_before += entry->size;
2884 entry2->frames_before++;
2885 }
2886 }
2887#ifndef GST_DISABLE_GST_DEBUG
2888 num_added_total += num_added;
2889#endif
2890 }
2891 }
2892 }
2893#ifndef GST_DISABLE_GST_DEBUG
2894 if (num_added_total)
2895 GST_LOG ("added %u new index entries", num_added_total);
2896#endif
2897
2898 GST_LOG_OBJECT (avi, "I'm now going to reorder the index entries for time");
2899
2900 /* re-order for time */
2901 list = g_list_sort (list, (GCompareFunc) sort);
2902
2903 /* make a continous array out of the list */
2904 avi->index_size = g_list_length (list);
2905 avi->index_entries = g_try_new (gst_avi_index_entry, avi->index_size);
2906 if (!avi->index_entries)
2907 goto out_of_mem;
2908
2909 entry = (gst_avi_index_entry *) (list->data);
2910 delay = entry->ts;
2911
2912 GST_LOG_OBJECT (avi,
2913 "Building index array, nr_entries = %d (time offset = %"
2914 GST_TIME_FORMAT, avi->index_size, GST_TIME_ARGS (delay));
2915
2916 for (i = 0, node = list; node != NULL; node = node->next, i++) {
2917 entry = node->data;
2918 entry->index_nr = i;
2919 entry->ts -= delay;
2920 memcpy (&avi->index_entries[i], entry, sizeof (gst_avi_index_entry));
2921#ifndef GST_DISABLE_GST_DEBUG
2922 num_per_stream[entry->stream_nr]++;
2923#endif
2924
2925 GST_LOG_OBJECT (avi, "Sorted index entry %3d for stream %d of size %6u"
2926 " at offset %7" G_GUINT64_FORMAT ", time %" GST_TIME_FORMAT
2927 " dur %" GST_TIME_FORMAT,
2928 avi->index_entries[i].index_nr, entry->stream_nr, entry->size,
2929 entry->offset, GST_TIME_ARGS (entry->ts), GST_TIME_ARGS (entry->dur));
2930 }
2931 if (delay) {
2932 for (i = 0; i < avi->num_streams; i++) {
2933 stream = &avi->stream[i];
2934 stream->idx_duration -= delay;
2935 }
2936 }
2937#ifndef GST_DISABLE_GST_DEBUG
2938 {
2939 gchar str[GST_AVI_DEMUX_MAX_STREAMS * (1 + 6 + 2)];
2940 gchar *pad_name;
2941
2942 for (i = 0; i < avi->num_streams; i++) {
2943 if (!avi->stream[i].pad)
2944 continue;
2945 pad_name = GST_OBJECT_NAME (avi->stream[i].pad);
2946 sprintf (&str[i * (1 + 6 + 2)], " %6u %c", num_per_stream[i],
2947 pad_name[0]);
2948 }
2949 GST_LOG_OBJECT (avi, "indizies per stream:%20s", str);
2950 }
2951#endif
2952
2953 GST_LOG_OBJECT (avi, "Freeing original index list");
2954 /* all the node->data in list point to alloc_list chunks */
2955
2956 g_list_free (list);
2957 }
2958 if (alloc_list) {
2959 g_list_foreach (alloc_list, (GFunc) g_free, NULL);
2960 g_list_free (alloc_list);
2961 }
2962#ifndef GST_DISABLE_GST_DEBUG
2963 for (i = 0; i < avi->num_streams; i++) {
2964 GST_LOG_OBJECT (avi, "Stream %d, %d frames, %8" G_GUINT64_FORMAT " bytes",
2965 i, avi->stream[i].idx_n, avi->stream[i].total_bytes);
2966 }
2967#endif
2968
2969 GST_LOG_OBJECT (avi, "Index massaging done");
2970
2971 stamp = gst_util_get_timestamp () - stamp;
2972
2973 GST_CAT_DEBUG (GST_CAT_PERFORMANCE, "massaging index %" GST_TIME_FORMAT,
2974 GST_TIME_ARGS (stamp));
2975 2553
2976 return TRUE; 2554 return TRUE;
2977 2555
2978 /* ERRORS */ 2556 /* ERRORS */
2979out_of_mem: 2557out_of_mem:
2980 { 2558 {
2981 GST_WARNING_OBJECT (avi, "Out of memory for %" G_GSIZE_FORMAT " bytes", 2559 GST_ELEMENT_ERROR (avi, RESOURCE, NO_SPACE_LEFT, (NULL),
2982 sizeof (gst_avi_index_entry) * avi->index_size); 2560 ("Cannot allocate memory for %u*%u=%u bytes",
2561 (guint) sizeof (GstAviIndexEntry), num,
2562 (guint) sizeof (GstAviIndexEntry) * num));
2983 return FALSE; 2563 return FALSE;
2984 } 2564 }
2985} 2565}
2986#endif
2987 2566
2988static void 2567static void
2989gst_avi_demux_calculate_durations_from_index (GstAviDemux * avi) 2568gst_avi_demux_calculate_durations_from_index (GstAviDemux * avi)
2990{ 2569{
2991 gint stream; 2570 guint i;
2992 GstClockTime total; 2571 GstClockTime total;
2572 GstAviStream *stream;
2993 2573
2994 total = GST_CLOCK_TIME_NONE; 2574 total = GST_CLOCK_TIME_NONE;
2995 2575
2996 /* all streams start at a timestamp 0 */ 2576 /* all streams start at a timestamp 0 */
2997 for (stream = 0; stream < avi->num_streams; stream++) { 2577 for (i = 0; i < avi->num_streams; i++) {
2998 GstClockTime duration, hduration; 2578 GstClockTime duration, hduration;
2999 GstAviStream *streamc = &avi->stream[stream]; 2579 gst_riff_strh *strh;
3000 gst_riff_strh *strh = streamc->strh;
3001 2580
3002 if (!strh) 2581 stream = &avi->stream[i];
2582 if (G_UNLIKELY (!stream || !(strh = stream->strh)))
3003 continue; 2583 continue;
3004 2584
3005 /* get header duration for the stream */ 2585 /* get header duration for the stream */
3006 hduration = streamc->hdr_duration; 2586 hduration = stream->hdr_duration;
3007 2587 /* index duration calculated during parsing */
3008 /* index duration calculated during parsing, invariant under massage */ 2588 duration = stream->idx_duration;
3009 duration = streamc->idx_duration;
3010 2589
3011 /* now pick a good duration */ 2590 /* now pick a good duration */
3012 if (GST_CLOCK_TIME_IS_VALID (duration)) { 2591 if (GST_CLOCK_TIME_IS_VALID (duration)) {
@@ -3018,7 +2597,7 @@ gst_avi_demux_calculate_durations_from_index (GstAviDemux * avi)
3018 duration = hduration; 2597 duration = hduration;
3019 } 2598 }
3020 /* set duration for the stream */ 2599 /* set duration for the stream */
3021 streamc->duration = duration; 2600 stream->duration = duration;
3022 2601
3023 /* find total duration */ 2602 /* find total duration */
3024 if (total == GST_CLOCK_TIME_NONE || duration > total) 2603 if (total == GST_CLOCK_TIME_NONE || duration > total)
@@ -3027,12 +2606,12 @@ gst_avi_demux_calculate_durations_from_index (GstAviDemux * avi)
3027 2606
3028 if (GST_CLOCK_TIME_IS_VALID (total) && (total > 0)) { 2607 if (GST_CLOCK_TIME_IS_VALID (total) && (total > 0)) {
3029 /* now update the duration for those streams where we had none */ 2608 /* now update the duration for those streams where we had none */
3030 for (stream = 0; stream < avi->num_streams; stream++) { 2609 for (i = 0; i < avi->num_streams; i++) {
3031 GstAviStream *streamc = &avi->stream[stream]; 2610 stream = &avi->stream[i];
3032 2611
3033 if (!GST_CLOCK_TIME_IS_VALID (streamc->duration) 2612 if (!GST_CLOCK_TIME_IS_VALID (stream->duration)
3034 || streamc->duration == 0) { 2613 || stream->duration == 0) {
3035 streamc->duration = total; 2614 stream->duration = total;
3036 2615
3037 GST_INFO ("Stream %d duration according to total: %" GST_TIME_FORMAT, 2616 GST_INFO ("Stream %d duration according to total: %" GST_TIME_FORMAT,
3038 stream, GST_TIME_ARGS (total)); 2617 stream, GST_TIME_ARGS (total));
@@ -3265,34 +2844,6 @@ skipping_done:
3265 GST_DEBUG ("Found movi chunk. Starting to stream data"); 2844 GST_DEBUG ("Found movi chunk. Starting to stream data");
3266 avi->state = GST_AVI_DEMUX_MOVI; 2845 avi->state = GST_AVI_DEMUX_MOVI;
3267 2846
3268#if 0
3269 /*GList *index = NULL, *alloc = NULL; */
3270
3271 /* ######################## this need to be integrated with the state */
3272 /* create or read stream index (for seeking) */
3273 if (avi->stream[0].indexes != NULL) {
3274 gst_avi_demux_read_subindexes_push (avi, &index, &alloc);
3275 }
3276 if (!index) {
3277 if (avi->avih->flags & GST_RIFF_AVIH_HASINDEX) {
3278 gst_avi_demux_stream_index (avi, &index, &alloc);
3279 }
3280 /* some indexes are incomplete, continue streaming from there */
3281 if (!index)
3282 gst_avi_demux_stream_scan (avi, &index, &alloc);
3283 }
3284
3285 /* this is a fatal error */
3286 if (!index)
3287 goto no_index;
3288
3289 if (!gst_avi_demux_massage_index (avi, index, alloc))
3290 goto no_index;
3291
3292 gst_avi_demux_calculate_durations_from_index (avi);
3293 /* ######################## */
3294#endif
3295
3296 /* create initial NEWSEGMENT event */ 2847 /* create initial NEWSEGMENT event */
3297 if ((stop = avi->segment.stop) == GST_CLOCK_TIME_NONE) 2848 if ((stop = avi->segment.stop) == GST_CLOCK_TIME_NONE)
3298 stop = avi->segment.duration; 2849 stop = avi->segment.duration;
@@ -3360,9 +2911,6 @@ gst_avi_demux_stream_header_pull (GstAviDemux * avi)
3360 GstFlowReturn res; 2911 GstFlowReturn res;
3361 GstBuffer *buf, *sub = NULL; 2912 GstBuffer *buf, *sub = NULL;
3362 guint32 tag; 2913 guint32 tag;
3363#if 0
3364 GList *index = NULL, *alloc = NULL;
3365#endif
3366 guint offset = 4; 2914 guint offset = 4;
3367 gint64 stop; 2915 gint64 stop;
3368 GstElement *element = GST_ELEMENT_CAST (avi); 2916 GstElement *element = GST_ELEMENT_CAST (avi);
@@ -3487,6 +3035,7 @@ gst_avi_demux_stream_header_pull (GstAviDemux * avi)
3487 /* Now, find the data (i.e. skip all junk between header and data) */ 3035 /* Now, find the data (i.e. skip all junk between header and data) */
3488 do { 3036 do {
3489 guint size; 3037 guint size;
3038 guint8 *data;
3490 guint32 tag, ltag; 3039 guint32 tag, ltag;
3491 3040
3492 res = gst_pad_pull_range (avi->sinkpad, avi->offset, 12, &buf); 3041 res = gst_pad_pull_range (avi->sinkpad, avi->offset, 12, &buf);
@@ -3500,13 +3049,15 @@ gst_avi_demux_stream_header_pull (GstAviDemux * avi)
3500 return GST_FLOW_ERROR; 3049 return GST_FLOW_ERROR;
3501 } 3050 }
3502 3051
3503 tag = GST_READ_UINT32_LE (GST_BUFFER_DATA (buf)); 3052 data = GST_BUFFER_DATA (buf);
3504 size = GST_READ_UINT32_LE (GST_BUFFER_DATA (buf) + 4); 3053
3505 ltag = GST_READ_UINT32_LE (GST_BUFFER_DATA (buf) + 8); 3054 tag = GST_READ_UINT32_LE (data);
3055 size = GST_READ_UINT32_LE (data + 4);
3056 ltag = GST_READ_UINT32_LE (data + 8);
3506 3057
3507 GST_DEBUG ("tag %" GST_FOURCC_FORMAT ", size %u", 3058 GST_DEBUG ("tag %" GST_FOURCC_FORMAT ", size %u",
3508 GST_FOURCC_ARGS (tag), size); 3059 GST_FOURCC_ARGS (tag), size);
3509 GST_MEMDUMP ("Tag content", GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf)); 3060 GST_MEMDUMP ("Tag content", data, GST_BUFFER_SIZE (buf));
3510 gst_buffer_unref (buf); 3061 gst_buffer_unref (buf);
3511 3062
3512 switch (tag) { 3063 switch (tag) {
@@ -3571,22 +3122,23 @@ skipping_done:
3571 /* we read a super index already (gst_avi_demux_parse_superindex() ) */ 3122 /* we read a super index already (gst_avi_demux_parse_superindex() ) */
3572 gst_avi_demux_read_subindexes_pull (avi, &index, &alloc); 3123 gst_avi_demux_read_subindexes_pull (avi, &index, &alloc);
3573 } 3124 }
3574 if (!index) {
3575#endif 3125#endif
3576 if (avi->avih->flags & GST_RIFF_AVIH_HASINDEX) { 3126 if (!avi->have_index) {
3127 if (avi->avih->flags & GST_RIFF_AVIH_HASINDEX)
3577 gst_avi_demux_stream_index (avi); 3128 gst_avi_demux_stream_index (avi);
3578 }
3579#if 0
3580 /* some indexes are incomplete, continue streaming from there */
3581 if (!index)
3582 gst_avi_demux_stream_scan (avi, &index, &alloc);
3583 }
3584 3129
3585 /* this is a fatal error */ 3130 /* still no index, scan */
3586 if (!index) 3131 if (!avi->have_index) {
3587 goto no_index; 3132 gst_avi_demux_stream_scan (avi);
3588#endif
3589 3133
3134 /* still no index.. this is a fatal error for now.
3135 * FIXME, we should switch to plain push mode without seeking
3136 * instead of failing. */
3137 if (!avi->have_index)
3138 goto no_index;
3139 }
3140 }
3141 /* use the indexes now to construct nice durations */
3590 gst_avi_demux_calculate_durations_from_index (avi); 3142 gst_avi_demux_calculate_durations_from_index (avi);
3591 3143
3592 /* create initial NEWSEGMENT event */ 3144 /* create initial NEWSEGMENT event */
@@ -3595,9 +3147,9 @@ skipping_done:
3595 3147
3596 GST_DEBUG_OBJECT (avi, "segment stop %" G_GINT64_FORMAT, stop); 3148 GST_DEBUG_OBJECT (avi, "segment stop %" G_GINT64_FORMAT, stop);
3597 3149
3598 /* do initial seek to the configured segment values */ 3150 /* do initial seek to the default segment values */
3599 gst_avi_demux_do_seek (avi, &avi->segment); 3151 gst_avi_demux_do_seek (avi, &avi->segment);
3600 3152 /* prepare initial segment */
3601 if (avi->seek_event) 3153 if (avi->seek_event)
3602 gst_event_unref (avi->seek_event); 3154 gst_event_unref (avi->seek_event);
3603 avi->seek_event = gst_event_new_new_segment 3155 avi->seek_event = gst_event_new_new_segment
@@ -3654,19 +3206,13 @@ no_streams:
3654 GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL), ("No streams found")); 3206 GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL), ("No streams found"));
3655 return GST_FLOW_ERROR; 3207 return GST_FLOW_ERROR;
3656 } 3208 }
3657#if 0
3658no_index: 3209no_index:
3659 { 3210 {
3660 GST_WARNING ("file without or too big index"); 3211 GST_WARNING ("file without or too big index");
3661 g_list_free (index);
3662 g_list_foreach (alloc, (GFunc) g_free, NULL);
3663 g_list_free (alloc);
3664
3665 GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL), 3212 GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL),
3666 ("Could not get/create index")); 3213 ("Could not get/create index"));
3667 return GST_FLOW_ERROR; 3214 return GST_FLOW_ERROR;
3668 } 3215 }
3669#endif
3670pull_range_failed: 3216pull_range_failed:
3671 { 3217 {
3672 GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL), 3218 GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL),
@@ -3786,6 +3332,7 @@ gst_avi_demux_do_seek (GstAviDemux * avi, GstSegment * segment)
3786 /* get the entry index for the requested position */ 3332 /* get the entry index for the requested position */
3787 index = gst_avi_demux_index_for_time (avi, ostream, seek_time); 3333 index = gst_avi_demux_index_for_time (avi, ostream, seek_time);
3788 3334
3335 /* move to previous keyframe */
3789 if (!ENTRY_IS_KEYFRAME (&ostream->index[index])) 3336 if (!ENTRY_IS_KEYFRAME (&ostream->index[index]))
3790 index = gst_avi_demux_index_prev (avi, ostream, index, TRUE); 3337 index = gst_avi_demux_index_prev (avi, ostream, index, TRUE);
3791 3338
@@ -4226,13 +3773,12 @@ gst_avi_demux_loop_data (GstAviDemux * avi)
4226 } 3773 }
4227 } 3774 }
4228 3775
4229 /* correct for index offset */
4230 offset += avi->index_offset + 8;
4231
4232 GST_LOG ("reading buffer (size=%d), stream %d, pos %" 3776 GST_LOG ("reading buffer (size=%d), stream %d, pos %"
4233 G_GUINT64_FORMAT " (%llx), kf %d", size, stream_num, offset, 3777 G_GUINT64_FORMAT " (%llx), kf %d", size, stream_num, offset,
4234 offset, keyframe); 3778 offset, keyframe);
4235 3779
3780 /* FIXME, check large chunks and cut them up */
3781
4236 /* pull in the data */ 3782 /* pull in the data */
4237 ret = gst_pad_pull_range (avi->sinkpad, offset, size, &buf); 3783 ret = gst_pad_pull_range (avi->sinkpad, offset, size, &buf);
4238 if (ret != GST_FLOW_OK) 3784 if (ret != GST_FLOW_OK)
@@ -4457,6 +4003,7 @@ gst_avi_demux_stream_data (GstAviDemux * avi)
4457 if (G_UNLIKELY (format != GST_FORMAT_TIME)) 4003 if (G_UNLIKELY (format != GST_FORMAT_TIME))
4458 goto wrong_format; 4004 goto wrong_format;
4459 4005
4006 /* increment our positions */
4460 stream->current_entry++; 4007 stream->current_entry++;
4461 stream->current_total += size; 4008 stream->current_total += size;
4462 4009
diff --git a/gst/avi/gstavidemux.h b/gst/avi/gstavidemux.h
index ff4235ff9..66537a6d7 100644
--- a/gst/avi/gstavidemux.h
+++ b/gst/avi/gstavidemux.h
@@ -57,9 +57,9 @@ typedef struct {
57} GstAviIndexEntry; 57} GstAviIndexEntry;
58 58
59#define GST_AVI_KEYFRAME 1 59#define GST_AVI_KEYFRAME 1
60#define ENTRY_IS_KEYFRAME(e) (((e)->flags & GST_AVI_KEYFRAME) == GST_AVI_KEYFRAME) 60#define ENTRY_IS_KEYFRAME(e) ((e)->flags == GST_AVI_KEYFRAME)
61#define ENTRY_SET_KEYFRAME(e) ((e)->flags |= GST_AVI_KEYFRAME) 61#define ENTRY_SET_KEYFRAME(e) ((e)->flags = GST_AVI_KEYFRAME)
62#define ENTRY_UNSET_KEYFRAME(e) ((e)->flags &= ~(GST_AVI_KEYFRAME)) 62#define ENTRY_UNSET_KEYFRAME(e) ((e)->flags = 0)
63 63
64typedef struct { 64typedef struct {
65 /* index of this streamcontext */ 65 /* index of this streamcontext */
@@ -149,6 +149,8 @@ typedef struct _GstAviDemux {
149 guint64 offset; 149 guint64 offset;
150 gboolean abort_buffering; 150 gboolean abort_buffering;
151 151
152 /* when we loaded the indexes */
153 gboolean have_index;
152 /* index offset in the file */ 154 /* index offset in the file */
153 guint64 index_offset; 155 guint64 index_offset;
154 156