summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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