1/*  libasf - An Advanced Systems Format media file parser
2 *  Copyright (C) 2006-2010 Juho Vähä-Herttua
3 *
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.
13 *
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, Boston, MA 02110-1301 USA
17 */
18
19#include <stdlib.h>
20#include <stdio.h>
21
22#include "asf.h"
23#include "asfint.h"
24#include "byteio.h"
25#include "header.h"
26#include "parse.h"
27#include "data.h"
28#include "debug.h"
29
30
31static int
32asf_fileio_read_cb(void *stream, void *buffer, int size)
33{
34	int ret;
35
36	ret = fread(buffer, 1, size, stream);
37	if (!ret && !feof(stream))
38		return -1;
39
40	return ret;
41}
42
43static int64_t
44asf_fileio_seek_cb(void *stream, int64_t offset)
45{
46	return fseek(stream, offset, SEEK_SET);
47}
48
49asf_file_t *
50asf_open_file(const char *filename)
51{
52	asf_file_t *file;
53	asf_iostream_t stream;
54	FILE *fstream;
55
56	fstream = fopen(filename, "rb");
57	if (!fstream)
58		return NULL;
59
60	stream.read = asf_fileio_read_cb;
61	stream.write = NULL;
62	stream.seek = asf_fileio_seek_cb;
63	stream.opaque = fstream;
64
65	file = asf_open_cb(&stream);
66	if (!file)
67		return NULL;
68
69	file->filename = filename;
70
71	return file;
72}
73
74asf_file_t *
75asf_open_cb(asf_iostream_t *iostream)
76{
77	asf_file_t *file;
78	int i;
79
80	if (!iostream)
81		return NULL;
82
83	file = calloc(1, sizeof(asf_file_t));
84	if (!file)
85		return NULL;
86
87	file->filename = NULL;
88	file->iostream.read = iostream->read;
89	file->iostream.write = iostream->write;
90	file->iostream.seek = iostream->seek;
91	file->iostream.opaque = iostream->opaque;
92
93	file->header = NULL;
94	file->data = NULL;
95	file->index = NULL;
96
97	for (i=0; i < ASF_MAX_STREAMS; i++) {
98		file->streams[i].type = ASF_STREAM_TYPE_NONE;
99		file->streams[i].flags = ASF_STREAM_FLAG_NONE;
100		file->streams[i].properties = NULL;
101		file->streams[i].extended_properties = NULL;
102	}
103
104	return file;
105}
106
107int
108asf_init(asf_file_t *file)
109{
110	int tmp;
111
112	if (!file)
113		return ASF_ERROR_INTERNAL;
114
115	tmp = asf_parse_header(file);
116	if (tmp < 0) {
117		debug_printf("error parsing header: %d", tmp);
118		return tmp;
119	}
120	file->position += tmp;
121	file->data_position = file->position;
122
123	tmp = asf_parse_data(file);
124	if (tmp < 0) {
125		debug_printf("error parsing data object: %d", tmp);
126		return tmp;
127	}
128	file->position += tmp;
129
130	if (file->flags & ASF_FLAG_SEEKABLE && file->iostream.seek) {
131		int64_t seek_position;
132
133		file->index_position = file->data_position +
134		                       file->data->size;
135
136		seek_position = file->iostream.seek(file->iostream.opaque,
137		                                    file->index_position);
138
139		/* if first seek fails, we can try to recover and just ignore seeking */
140		if (seek_position >= 0) {
141			while (seek_position == file->index_position &&
142			       file->index_position < file->file_size && !file->index) {
143				tmp = asf_parse_index(file);
144				if (tmp < 0) {
145					debug_printf("Error finding index object! %d", tmp);
146					break;
147				}
148
149				/* The object read was something else than index */
150				if (!file->index)
151					file->index_position += tmp;
152
153				seek_position = file->iostream.seek(file->iostream.opaque,
154								  file->index_position);
155			}
156
157			if (!file->index) {
158				debug_printf("Couldn't find an index object");
159				file->index_position = 0;
160			}
161
162			seek_position = file->iostream.seek(file->iostream.opaque,
163							  file->data->packets_position);
164			if (seek_position != file->data->packets_position) {
165				/* Couldn't seek back to packets position, this is fatal! */
166				return ASF_ERROR_SEEK;
167			}
168		}
169	}
170
171	for (tmp = 0; tmp < ASF_MAX_STREAMS; tmp++) {
172		if (file->streams[tmp].type != ASF_STREAM_TYPE_NONE) {
173			debug_printf("stream %d of type %d found!", tmp, file->streams[tmp].type);
174		}
175	}
176
177	return 0;
178}
179
180void
181asf_close(asf_file_t *file)
182{
183	if (file) {
184		int i;
185
186		asf_free_header(file->header);
187		free(file->data);
188		if (file->index)
189			free(file->index->entries);
190		free(file->index);
191
192		if (file->filename)
193			fclose(file->iostream.opaque);
194
195		for (i=0; i < ASF_MAX_STREAMS; i++) {
196			free(file->streams[i].properties);
197			free(file->streams[i].extended_properties);
198		}
199
200		free(file);
201	}
202}
203
204asf_packet_t *
205asf_packet_create()
206{
207	asf_packet_t *ret;
208
209	ret = malloc(sizeof(asf_packet_t));
210	if (!ret)
211		return NULL;
212
213	asf_data_init_packet(ret);
214
215	return ret;
216}
217
218int
219asf_get_packet(asf_file_t *file, asf_packet_t *packet)
220{
221	int tmp;
222
223	if (!file || !packet)
224		return ASF_ERROR_INTERNAL;
225
226	if (file->packet >= file->data_packets_count) {
227		return 0;
228	}
229
230	tmp = asf_data_get_packet(packet, file);
231	if (tmp < 0) {
232		return tmp;
233	}
234
235	file->position += tmp;
236	file->packet++;
237
238	return tmp;
239}
240
241void
242asf_packet_destroy(asf_packet_t *packet)
243{
244	asf_data_free_packet(packet);
245	free(packet);
246}
247
248int64_t
249asf_seek_to_msec(asf_file_t *file, int64_t msec)
250{
251	uint64_t packet;
252	uint64_t new_position;
253	uint64_t new_msec;
254	int64_t seek_position;
255
256	if (!file)
257		return ASF_ERROR_INTERNAL;
258
259	if (!(file->flags & ASF_FLAG_SEEKABLE) || !file->iostream.seek) {
260		return ASF_ERROR_SEEKABLE;
261	}
262
263	/* Index structure is missing, check if we can still seek */
264	// DLM we can always seek to 0
265	if (file->index == NULL && msec > 0) {
266		int i, audiocount;
267
268		audiocount = 0;
269		for (i=0; i<ASF_MAX_STREAMS; i++) {
270			if (file->streams[i].type == ASF_STREAM_TYPE_NONE)
271				continue;
272
273			/* Non-audio files are not seekable without index */
274			if (file->streams[i].type != ASF_STREAM_TYPE_AUDIO)
275				return ASF_ERROR_SEEKABLE;
276			else
277				audiocount++;
278		}
279
280		/* Audio files with more than one audio track are not seekable
281		 * without index */
282		if (audiocount != 1)
283			return ASF_ERROR_SEEKABLE;
284	}
285
286	if (msec > (file->play_duration / 10000)) {
287		return ASF_ERROR_SEEK;
288	}
289
290	if (file->index && file->index->entry_count == 0) {
291		// Some sort of Constant packet size?
292
293		/* Calculate current packet from index header */
294		packet = msec * 10000 / file->index->entry_time_interval;
295		new_msec = msec;
296
297	} else if (file->index && file->index->entry_count > 0) {
298		uint32_t index_entry;
299
300		/* Fetch current packet from index entry structure */
301		index_entry = msec * 10000 / file->index->entry_time_interval;
302		if (index_entry >= file->index->entry_count) {
303			return ASF_ERROR_SEEK;
304		}
305		packet = file->index->entries[index_entry].packet_index;
306
307		/* the correct msec time isn't known before reading the packet */
308		new_msec = msec;
309	} else {
310		/* convert msec into bytes per second and divide with packet_size */
311		packet = msec * file->max_bitrate / 8000 / file->packet_size;
312
313		/* calculate the resulting position in the audio stream */
314		new_msec = packet * file->packet_size * 8000 / file->max_bitrate;
315	}
316
317	/* calculate new position to be in the beginning of the current frame */
318	new_position = file->data->packets_position + packet * file->packet_size;
319
320	seek_position = file->iostream.seek(file->iostream.opaque, new_position);
321	if (seek_position < 0 || seek_position != new_position) {
322		return ASF_ERROR_SEEK;
323	}
324
325	/* update current file position information */
326	file->position = new_position;
327	file->packet = packet;
328
329	return new_msec;
330}
331
332asf_metadata_t *
333asf_header_get_metadata(asf_file_t *file)
334{
335	if (!file || !file->header)
336		return NULL;
337
338	return asf_header_metadata(file->header);
339}
340
341void
342asf_header_destroy(asf_file_t *file)
343{
344	if (!file)
345		return;
346
347	asf_free_header(file->header);
348	file->header = NULL;
349}
350
351void
352asf_metadata_destroy(asf_metadata_t *metadata)
353{
354	asf_header_free_metadata(metadata);
355}
356
357uint8_t
358asf_get_stream_count(asf_file_t *file)
359{
360	uint8_t ret = 0;
361	int i;
362
363	for (i = 0; i < ASF_MAX_STREAMS; i++) {
364		if (file->streams[i].type != ASF_STREAM_TYPE_NONE)
365			ret = i;
366	}
367
368	return ret;
369}
370
371int
372asf_is_broadcast(asf_file_t *file) {
373	return (file->flags & ASF_FLAG_BROADCAST);
374}
375
376int
377asf_is_seekable(asf_file_t *file) {
378	return (file->flags & ASF_FLAG_SEEKABLE);
379}
380
381asf_stream_t *
382asf_get_stream(asf_file_t *file, uint8_t track)
383{
384	if (!file || track >= ASF_MAX_STREAMS)
385		return NULL;
386
387	return &file->streams[track];
388}
389
390uint64_t
391asf_get_file_size(asf_file_t *file)
392{
393	if (!file)
394		return 0;
395
396	return file->file_size;
397}
398
399uint64_t
400asf_get_creation_date(asf_file_t *file)
401{
402	if (!file)
403		return 0;
404
405	return file->creation_date;
406}
407
408uint64_t
409asf_get_data_packets(asf_file_t *file)
410{
411	if (!file)
412		return 0;
413
414	return file->data_packets_count;
415}
416
417uint64_t
418asf_get_duration(asf_file_t *file)
419{
420	if (!file)
421		return 0;
422
423	return file->play_duration;
424}
425
426uint32_t
427asf_get_max_bitrate(asf_file_t *file)
428{
429	if (!file)
430		return 0;
431
432	return file->max_bitrate;
433}
434
435