1 /* libasf - An Advanced Systems Format media file parser
2 * Copyright (C) 2006-2010 Juho Vähä-Herttua
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.
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, Boston, MA 02110-1301 USA
32 asf_fileio_read_cb(void *stream
, void *buffer
, int size
)
36 ret
= fread(buffer
, 1, size
, stream
);
37 if (!ret
&& !feof(stream
))
44 asf_fileio_seek_cb(void *stream
, int64_t offset
)
46 return fseek(stream
, offset
, SEEK_SET
);
50 asf_open_file(const char *filename
)
53 asf_iostream_t stream
;
56 fstream
= fopen(filename
, "rb");
60 stream
.read
= asf_fileio_read_cb
;
62 stream
.seek
= asf_fileio_seek_cb
;
63 stream
.opaque
= fstream
;
65 file
= asf_open_cb(&stream
);
69 file
->filename
= filename
;
75 asf_open_cb(asf_iostream_t
*iostream
)
83 file
= calloc(1, sizeof(asf_file_t
));
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
;
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
;
108 asf_init(asf_file_t
*file
)
113 return ASF_ERROR_INTERNAL
;
115 tmp
= asf_parse_header(file
);
117 debug_printf("error parsing header: %d", tmp
);
120 file
->position
+= tmp
;
121 file
->data_position
= file
->position
;
123 tmp
= asf_parse_data(file
);
125 debug_printf("error parsing data object: %d", tmp
);
128 file
->position
+= tmp
;
130 if (file
->flags
& ASF_FLAG_SEEKABLE
&& file
->iostream
.seek
) {
131 int64_t seek_position
;
133 file
->index_position
= file
->data_position
+
136 seek_position
= file
->iostream
.seek(file
->iostream
.opaque
,
137 file
->index_position
);
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
);
145 debug_printf("Error finding index object! %d", tmp
);
149 /* The object read was something else than index */
151 file
->index_position
+= tmp
;
153 seek_position
= file
->iostream
.seek(file
->iostream
.opaque
,
154 file
->index_position
);
158 debug_printf("Couldn't find an index object");
159 file
->index_position
= 0;
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
;
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
);
181 asf_close(asf_file_t
*file
)
186 asf_free_header(file
->header
);
189 free(file
->index
->entries
);
193 fclose(file
->iostream
.opaque
);
195 for (i
=0; i
< ASF_MAX_STREAMS
; i
++) {
196 free(file
->streams
[i
].properties
);
197 free(file
->streams
[i
].extended_properties
);
209 ret
= malloc(sizeof(asf_packet_t
));
213 asf_data_init_packet(ret
);
219 asf_get_packet(asf_file_t
*file
, asf_packet_t
*packet
)
223 if (!file
|| !packet
)
224 return ASF_ERROR_INTERNAL
;
226 if (file
->packet
>= file
->data_packets_count
) {
230 tmp
= asf_data_get_packet(packet
, file
);
235 file
->position
+= tmp
;
242 asf_packet_destroy(asf_packet_t
*packet
)
244 asf_data_free_packet(packet
);
249 asf_seek_to_msec(asf_file_t
*file
, int64_t msec
)
252 uint64_t new_position
;
254 int64_t seek_position
;
257 return ASF_ERROR_INTERNAL
;
259 if (!(file
->flags
& ASF_FLAG_SEEKABLE
) || !file
->iostream
.seek
) {
260 return ASF_ERROR_SEEKABLE
;
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) {
269 for (i
=0; i
<ASF_MAX_STREAMS
; i
++) {
270 if (file
->streams
[i
].type
== ASF_STREAM_TYPE_NONE
)
273 /* Non-audio files are not seekable without index */
274 if (file
->streams
[i
].type
!= ASF_STREAM_TYPE_AUDIO
)
275 return ASF_ERROR_SEEKABLE
;
280 /* Audio files with more than one audio track are not seekable
283 return ASF_ERROR_SEEKABLE
;
286 if (msec
> (file
->play_duration
/ 10000)) {
287 return ASF_ERROR_SEEK
;
290 if (file
->index
&& file
->index
->entry_count
== 0) {
291 // Some sort of Constant packet size?
293 /* Calculate current packet from index header */
294 packet
= msec
* 10000 / file
->index
->entry_time_interval
;
297 } else if (file
->index
&& file
->index
->entry_count
> 0) {
298 uint32_t index_entry
;
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
;
305 packet
= file
->index
->entries
[index_entry
].packet_index
;
307 /* the correct msec time isn't known before reading the packet */
310 /* convert msec into bytes per second and divide with packet_size */
311 packet
= msec
* file
->max_bitrate
/ 8000 / file
->packet_size
;
313 /* calculate the resulting position in the audio stream */
314 new_msec
= packet
* file
->packet_size
* 8000 / file
->max_bitrate
;
317 /* calculate new position to be in the beginning of the current frame */
318 new_position
= file
->data
->packets_position
+ packet
* file
->packet_size
;
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
;
325 /* update current file position information */
326 file
->position
= new_position
;
327 file
->packet
= packet
;
333 asf_header_get_metadata(asf_file_t
*file
)
335 if (!file
|| !file
->header
)
338 return asf_header_metadata(file
->header
);
342 asf_header_destroy(asf_file_t
*file
)
347 asf_free_header(file
->header
);
352 asf_metadata_destroy(asf_metadata_t
*metadata
)
354 asf_header_free_metadata(metadata
);
358 asf_get_stream_count(asf_file_t
*file
)
363 for (i
= 0; i
< ASF_MAX_STREAMS
; i
++) {
364 if (file
->streams
[i
].type
!= ASF_STREAM_TYPE_NONE
)
372 asf_is_broadcast(asf_file_t
*file
) {
373 return (file
->flags
& ASF_FLAG_BROADCAST
);
377 asf_is_seekable(asf_file_t
*file
) {
378 return (file
->flags
& ASF_FLAG_SEEKABLE
);
382 asf_get_stream(asf_file_t
*file
, uint8_t track
)
384 if (!file
|| track
>= ASF_MAX_STREAMS
)
387 return &file
->streams
[track
];
391 asf_get_file_size(asf_file_t
*file
)
396 return file
->file_size
;
400 asf_get_creation_date(asf_file_t
*file
)
405 return file
->creation_date
;
409 asf_get_data_packets(asf_file_t
*file
)
414 return file
->data_packets_count
;
418 asf_get_duration(asf_file_t
*file
)
423 return file
->play_duration
;
427 asf_get_max_bitrate(asf_file_t
*file
)
432 return file
->max_bitrate
;