fix build for --disable-gtk-doc
[swfdec.git] / swfdec / swfdec_audio_flv.c
blob4595f0740b200020a73ed82e220694b83449cb4d
1 /* Swfdec
2 * Copyright (C) 2007 Benjamin Otte <otte@gnome.org>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301 USA
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
24 #include <string.h>
25 #include "swfdec_audio_flv.h"
26 #include "swfdec_debug.h"
27 #include "swfdec_sound.h"
30 G_DEFINE_TYPE (SwfdecAudioFlv, swfdec_audio_flv, SWFDEC_TYPE_AUDIO)
32 static void
33 swfdec_audio_flv_dispose (GObject *object)
35 SwfdecAudioFlv *flv = SWFDEC_AUDIO_FLV (object);
37 if (flv->decoder != NULL) {
38 g_object_unref (flv->decoder);
39 flv->decoder = NULL;
41 g_queue_foreach (flv->playback_queue, (GFunc) swfdec_buffer_unref, NULL);
42 g_queue_free (flv->playback_queue);
43 g_object_unref (flv->flvdecoder);
45 G_OBJECT_CLASS (swfdec_audio_flv_parent_class)->dispose (object);
48 static SwfdecBuffer *
49 swfdec_audio_flv_decode_one (SwfdecAudioFlv *flv)
51 SwfdecBuffer *buffer;
52 guint format;
53 SwfdecAudioFormat in;
54 guint now, soon;
56 if (g_queue_is_empty (flv->playback_queue)) {
57 /* sync */
58 guint last;
59 swfdec_flv_decoder_get_audio (flv->flvdecoder,
60 SWFDEC_TICKS_TO_MSECS (flv->timestamp),
61 NULL, NULL, &last, NULL);
62 flv->playback_skip = SWFDEC_TICKS_TO_SAMPLES (
63 flv->timestamp - SWFDEC_MSECS_TO_TICKS (last));
64 flv->next_timestamp = last;
65 SWFDEC_DEBUG ("syncing to %ums: next timestamp to decode is %ums, skipping %u samples",
66 (guint) SWFDEC_TICKS_TO_MSECS (flv->timestamp),
67 flv->next_timestamp, flv->playback_skip);
69 if (flv->decoder)
70 buffer = swfdec_audio_decoder_pull (flv->decoder);
71 else
72 buffer = NULL;
73 while (buffer == NULL) {
74 if (flv->decoder && flv->next_timestamp == 0)
75 return NULL;
76 buffer = swfdec_flv_decoder_get_audio (flv->flvdecoder, flv->next_timestamp,
77 &format, &in, &now, &soon);
79 if (flv->next_timestamp != now) {
80 /* FIXME: do sync on first frame here */
81 SWFDEC_WARNING ("FIXME: didn't get requested timestamp - still loading?");
83 /* FIXME FIXME FIXME: This avoids decoding the last frame forever, however it ensures sync */
84 if (soon == 0)
85 return NULL;
86 flv->next_timestamp = soon;
87 if (flv->in == 0) {
88 /* init */
89 if (flv->decoder) {
90 g_object_unref (flv->decoder);
91 flv->decoder = NULL;
93 flv->format = format;
94 flv->in = in;
95 flv->decoder = swfdec_audio_decoder_new (flv->format, flv->in);
96 if (flv->decoder == NULL)
97 return NULL;
98 /* This is a hack that ensures AAC codec data is always present, even if
99 * the decoder gets initialized in the middle of the stream */
100 if (format == SWFDEC_AUDIO_CODEC_AAC) {
101 SwfdecBuffer *tmp = swfdec_flv_decoder_get_audio (flv->flvdecoder,
102 0, &format, NULL, NULL, NULL);
103 if (format == SWFDEC_AUDIO_CODEC_AAC && tmp->data[0] == 0 &&
104 tmp->length > 1) {
105 tmp = swfdec_buffer_new_subbuffer (tmp, 1, tmp->length - 1);
106 swfdec_audio_decoder_set_codec_data (flv->decoder, tmp);
107 swfdec_buffer_unref (tmp);
110 } else if (format != flv->format ||
111 in != flv->in) {
112 SWFDEC_ERROR ("FIXME: format change not implemented");
113 return NULL;
114 } else if (flv->decoder == NULL) {
115 return NULL;
117 if (format == SWFDEC_AUDIO_CODEC_AAC) {
118 SwfdecBuffer *data;
119 SwfdecBits bits;
120 guint type;
121 swfdec_bits_init (&bits, buffer);
122 type = swfdec_bits_get_u8 (&bits);
123 switch (type) {
124 case 0:
125 data = swfdec_bits_get_buffer (&bits, -1);
126 if (data) {
127 swfdec_audio_decoder_set_codec_data (flv->decoder, data);
128 swfdec_buffer_unref (data);
130 break;
131 case 1:
132 data = swfdec_bits_get_buffer (&bits, -1);
133 if (data) {
134 swfdec_audio_decoder_push (flv->decoder, data);
135 swfdec_buffer_unref (data);
136 } else {
137 SWFDEC_ERROR ("no data in AAC data buffer?");
139 break;
140 default:
141 SWFDEC_FIXME ("handle AAC type %u", type);
142 break;
144 } else {
145 swfdec_audio_decoder_push (flv->decoder, buffer);
147 if (flv->next_timestamp == 0)
148 swfdec_audio_decoder_push (flv->decoder, NULL);
149 buffer = swfdec_audio_decoder_pull (flv->decoder);
152 g_queue_push_tail (flv->playback_queue, buffer);
153 return buffer;
156 static gsize
157 swfdec_audio_flv_render (SwfdecAudio *audio, gint16* dest,
158 gsize start, gsize n_samples)
160 SwfdecAudioFlv *flv = SWFDEC_AUDIO_FLV (audio);
161 GList *walk;
162 gsize samples, rendered;
163 SwfdecBuffer *buffer;
165 g_assert (start < G_MAXINT);
166 start += flv->playback_skip;
167 SWFDEC_LOG ("flv %p rendering offset %"G_GSIZE_FORMAT", samples %"G_GSIZE_FORMAT,
168 flv, start, n_samples);
169 walk = g_queue_peek_head_link (flv->playback_queue);
170 for (rendered = 0; rendered < n_samples;) {
171 if (walk) {
172 buffer = walk->data;
173 walk = walk->next;
174 } else {
175 buffer = swfdec_audio_flv_decode_one (flv);
176 if (!buffer)
177 break;
179 samples = swfdec_sound_buffer_get_n_samples (buffer,
180 swfdec_audio_format_new (44100, 2, TRUE));
181 if (start) {
182 if (samples <= start) {
183 start -= samples;
184 continue;
186 samples -= start;
187 SWFDEC_LOG ("rendering %"G_GSIZE_FORMAT" samples, skipping %"G_GSIZE_FORMAT,
188 samples, start);
189 } else {
190 SWFDEC_LOG ("rendering %"G_GSIZE_FORMAT" samples", samples);
192 samples = MIN (samples, n_samples - rendered);
193 swfdec_sound_buffer_render (dest, buffer, start, samples);
194 start = 0;
195 rendered += samples;
196 dest += 2 * samples;
198 return rendered;
201 static gsize
202 swfdec_audio_flv_iterate (SwfdecAudio *audio, gsize remove)
204 SwfdecAudioFlv *flv = SWFDEC_AUDIO_FLV (audio);
205 SwfdecBuffer *buffer;
206 guint next;
208 flv->playback_skip += remove;
209 buffer = g_queue_peek_head (flv->playback_queue);
210 while (buffer && flv->playback_skip >=
211 swfdec_sound_buffer_get_n_samples (buffer, swfdec_audio_format_new (44100, 2, TRUE))
212 + swfdec_audio_format_get_granularity (swfdec_audio_format_new (44100, 2, TRUE))) {
213 buffer = g_queue_pop_head (flv->playback_queue);
214 SWFDEC_LOG ("removing buffer with %u samples",
215 swfdec_sound_buffer_get_n_samples (buffer, swfdec_audio_format_new (44100, 2, TRUE)));
216 flv->playback_skip -= swfdec_sound_buffer_get_n_samples (buffer,
217 swfdec_audio_format_new (44100, 2, TRUE));
218 swfdec_buffer_unref (buffer);
219 buffer = g_queue_peek_head (flv->playback_queue);
221 flv->timestamp += SWFDEC_SAMPLES_TO_TICKS (remove);
223 if (!g_queue_is_empty (flv->playback_queue))
224 return G_MAXUINT;
225 swfdec_flv_decoder_get_audio (flv->flvdecoder,
226 SWFDEC_TICKS_TO_MSECS (flv->timestamp),
227 NULL, NULL, NULL, &next);
228 return next ? G_MAXUINT : 0;
231 static void
232 swfdec_audio_flv_class_init (SwfdecAudioFlvClass *klass)
234 GObjectClass *object_class = G_OBJECT_CLASS (klass);
235 SwfdecAudioClass *audio_class = SWFDEC_AUDIO_CLASS (klass);
237 object_class->dispose = swfdec_audio_flv_dispose;
239 audio_class->iterate = swfdec_audio_flv_iterate;
240 audio_class->render = swfdec_audio_flv_render;
243 static void
244 swfdec_audio_flv_init (SwfdecAudioFlv *flv)
246 flv->playback_queue = g_queue_new ();
249 SwfdecAudio *
250 swfdec_audio_flv_new (SwfdecPlayer *player, SwfdecFlvDecoder *decoder, guint timestamp)
252 SwfdecAudioFlv *flv;
254 flv = g_object_new (SWFDEC_TYPE_AUDIO_FLV, NULL);
256 SWFDEC_DEBUG ("new audio flv for decoder %p, starting at %ums",
257 decoder, timestamp);
258 g_object_ref (decoder);
259 flv->flvdecoder = decoder;
260 flv->timestamp = SWFDEC_MSECS_TO_TICKS (timestamp);
261 swfdec_audio_add (SWFDEC_AUDIO (flv), player);
263 return SWFDEC_AUDIO (flv);