2 * This file is part of MPlayer.
4 * MPlayer is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * MPlayer 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
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with MPlayer; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include <libavutil/intreadwrite.h>
29 #include "stream/stream.h"
43 typedef struct da_priv
{
48 //! rather arbitrary value for maximum length of wav-format headers
49 #define MAX_WAVHDR_LEN (1 * 1024 * 1024)
51 //! how many valid frames in a row we need before accepting as valid MP3
52 #define MIN_MP3_HDRS 12
54 //! Used to describe a potential (chain of) MP3 headers we found
55 typedef struct mp3_hdr
{
56 off_t frame_pos
; // start of first frame in this "chain" of headers
57 off_t next_frame_pos
; // here we expect the next header with same parameters
63 int cons_hdrs
; // if this reaches MIN_MP3_HDRS we accept as MP3 file
68 * \brief free a list of MP3 header descriptions
69 * \param list pointer to the head-of-list pointer
71 static void free_mp3_hdrs(mp3_hdr_t
**list
) {
81 * \brief add another potential MP3 header to our list
82 * If it fits into an existing chain this one is expanded otherwise
83 * a new one is created.
84 * All entries that expected a MP3 header before the current position
86 * The list is expected to be and will be kept sorted by next_frame_pos
87 * and when those are equal by frame_pos.
88 * \param list pointer to the head-of-list pointer
89 * \param st_pos stream position where the described header starts
90 * \param mp3_chans number of channels as specified by the header (*)
91 * \param mp3_freq sampling frequency as specified by the header (*)
92 * \param mpa_spf frame size as specified by the header
93 * \param mpa_layer layer type ("version") as specified by the header (*)
94 * \param mpa_br bitrate as specified by the header
95 * \param mp3_flen length of the frame as specified by the header
96 * \return If non-null the current file is accepted as MP3 and the
97 * mp3_hdr struct describing the valid chain is returned. Must be
98 * freed independent of the list.
100 * parameters marked by (*) must be the same for all headers in the same chain
102 static mp3_hdr_t
*add_mp3_hdr(mp3_hdr_t
**list
, off_t st_pos
,
103 int mp3_chans
, int mp3_freq
, int mpa_spf
,
104 int mpa_layer
, int mpa_br
, int mp3_flen
) {
107 while (*list
&& (*list
)->next_frame_pos
<= st_pos
) {
108 if (((*list
)->next_frame_pos
< st_pos
) || ((*list
)->mp3_chans
!= mp3_chans
)
109 || ((*list
)->mp3_freq
!= mp3_freq
) || ((*list
)->mpa_layer
!= mpa_layer
) ) {
115 (*list
)->cons_hdrs
++;
116 (*list
)->next_frame_pos
= st_pos
+ mp3_flen
;
117 (*list
)->mpa_spf
= mpa_spf
;
118 (*list
)->mpa_br
= mpa_br
;
119 if ((*list
)->cons_hdrs
>= MIN_MP3_HDRS
) {
120 // copy the valid entry, so that the list can be easily freed
121 tmp
= malloc(sizeof(mp3_hdr_t
));
122 memcpy(tmp
, *list
, sizeof(mp3_hdr_t
));
127 list
= &((*list
)->next
);
130 if (!in_list
) { // does not belong into an existing chain, insert
131 // find right position to insert to keep sorting
132 while (*list
&& (*list
)->next_frame_pos
<= st_pos
+ mp3_flen
)
133 list
= &((*list
)->next
);
134 tmp
= malloc(sizeof(mp3_hdr_t
));
135 tmp
->frame_pos
= st_pos
;
136 tmp
->next_frame_pos
= st_pos
+ mp3_flen
;
137 tmp
->mp3_chans
= mp3_chans
;
138 tmp
->mp3_freq
= mp3_freq
;
139 tmp
->mpa_spf
= mpa_spf
;
140 tmp
->mpa_layer
= mpa_layer
;
141 tmp
->mpa_br
= mpa_br
;
149 #if 0 /* this code is a mess, clean it up before reenabling */
150 #define FLAC_SIGNATURE_SIZE 4
151 #define FLAC_STREAMINFO_SIZE 34
152 #define FLAC_SEEKPOINT_SIZE 18
164 get_flac_metadata (demuxer_t
* demuxer
)
167 unsigned int blk_len
;
168 stream_t
*s
= demuxer
->stream
;
170 /* file is qualified; skip over the signature bytes in the stream */
173 /* loop through the metadata blocks; use a do-while construct since there
174 * will always be 1 metadata block */
178 r
= stream_read (s
, (char *) preamble
, FLAC_SIGNATURE_SIZE
);
179 if (r
!= FLAC_SIGNATURE_SIZE
)
182 blk_len
= AV_RB24(preamble
+ 1);
184 switch (preamble
[0] & 0x7F)
186 case FLAC_VORBIS_COMMENT
:
188 /* For a description of the format please have a look at */
189 /* http://www.xiph.org/vorbis/doc/v-comment.html */
191 uint32_t length
, comment_list_len
;
192 char comments
[blk_len
];
193 uint8_t *ptr
= comments
;
198 if (stream_read (s
, comments
, blk_len
) == blk_len
)
200 length
= AV_RL32(ptr
);
203 comment_list_len
= AV_RL32(ptr
);
207 for (; cn
< comment_list_len
; cn
++)
209 length
= AV_RL32(ptr
);
213 if (&comment
[length
] < comments
|| &comment
[length
] >= &comments
[blk_len
])
218 if (!strncasecmp ("TITLE=", comment
, 6) && (length
- 6 > 0))
219 demux_info_add (demuxer
, "Title", comment
+ 6);
220 else if (!strncasecmp ("ARTIST=", comment
, 7) && (length
- 7 > 0))
221 demux_info_add (demuxer
, "Artist", comment
+ 7);
222 else if (!strncasecmp ("ALBUM=", comment
, 6) && (length
- 6 > 0))
223 demux_info_add (demuxer
, "Album", comment
+ 6);
224 else if (!strncasecmp ("DATE=", comment
, 5) && (length
- 5 > 0))
225 demux_info_add (demuxer
, "Year", comment
+ 5);
226 else if (!strncasecmp ("GENRE=", comment
, 6) && (length
- 6 > 0))
227 demux_info_add (demuxer
, "Genre", comment
+ 6);
228 else if (!strncasecmp ("Comment=", comment
, 8) && (length
- 8 > 0))
229 demux_info_add (demuxer
, "Comment", comment
+ 8);
230 else if (!strncasecmp ("TRACKNUMBER=", comment
, 12)
231 && (length
- 12 > 0))
235 sprintf (buf
, "%d", atoi (comment
+ 12));
236 demux_info_add(demuxer
, "Track", buf
);
246 case FLAC_STREAMINFO
:
248 case FLAC_APPLICATION
:
252 /* 6-127 are presently reserved */
253 stream_skip (s
, blk_len
);
256 } while ((preamble
[0] & 0x80) == 0);
260 static int demux_audio_open(demuxer_t
* demuxer
) {
262 sh_audio_t
* sh_audio
;
263 uint8_t hdr
[HDR_SIZE
];
264 int frmt
= 0, n
= 0, step
;
265 off_t st_pos
= 0, next_frame_pos
= 0;
266 // mp3_hdrs list is sorted first by next_frame_pos and then by frame_pos
267 mp3_hdr_t
*mp3_hdrs
= NULL
, *mp3_found
= NULL
;
272 stream_read(s
, hdr
, HDR_SIZE
);
273 while(n
< 30000 && !s
->eof
) {
274 int mp3_freq
, mp3_chans
, mp3_flen
, mpa_layer
, mpa_spf
, mpa_br
;
275 st_pos
= stream_tell(s
) - HDR_SIZE
;
278 if( hdr
[0] == 'R' && hdr
[1] == 'I' && hdr
[2] == 'F' && hdr
[3] == 'F' ) {
282 stream_read(s
,hdr
,4);
285 if(hdr
[0] != 'W' || hdr
[1] != 'A' || hdr
[2] != 'V' || hdr
[3] != 'E' )
288 // We found wav header. Now we can have 'fmt ' or a mp3 header
291 } else if( hdr
[0] == 'I' && hdr
[1] == 'D' && hdr
[2] == '3' && (hdr
[3] >= 2)) {
294 stream_read(s
,hdr
,4);
295 len
= (hdr
[0]<<21) | (hdr
[1]<<14) | (hdr
[2]<<7) | hdr
[3];
298 } else if( hdr
[0] == 'f' && hdr
[1] == 'm' && hdr
[2] == 't' && hdr
[3] == ' ' ) {
301 } else if((mp3_flen
= mp_get_mp3_header(hdr
, &mp3_chans
, &mp3_freq
,
302 &mpa_spf
, &mpa_layer
, &mpa_br
)) > 0) {
303 mp3_found
= add_mp3_hdr(&mp3_hdrs
, st_pos
, mp3_chans
, mp3_freq
,
304 mpa_spf
, mpa_layer
, mpa_br
, mp3_flen
);
309 } else if( hdr
[0] == 'f' && hdr
[1] == 'L' && hdr
[2] == 'a' && hdr
[3] == 'C' ) {
311 if (!mp3_hdrs
|| mp3_hdrs
->cons_hdrs
< 3)
314 // Add here some other audio format detection
316 memmove(hdr
,&hdr
[step
],HDR_SIZE
-step
);
317 stream_read(s
, &hdr
[HDR_SIZE
- step
], step
);
321 free_mp3_hdrs(&mp3_hdrs
);
326 sh_audio
= new_sh_audio(demuxer
,0);
330 sh_audio
->format
= (mp3_found
->mpa_layer
< 3 ? 0x50 : 0x55);
331 demuxer
->movi_start
= mp3_found
->frame_pos
;
332 next_frame_pos
= mp3_found
->next_frame_pos
;
333 sh_audio
->audio
.dwSampleSize
= 0;
334 sh_audio
->audio
.dwScale
= mp3_found
->mpa_spf
;
335 sh_audio
->audio
.dwRate
= mp3_found
->mp3_freq
;
336 sh_audio
->wf
= malloc(sizeof(*sh_audio
->wf
));
337 sh_audio
->wf
->wFormatTag
= sh_audio
->format
;
338 sh_audio
->wf
->nChannels
= mp3_found
->mp3_chans
;
339 sh_audio
->wf
->nSamplesPerSec
= mp3_found
->mp3_freq
;
340 sh_audio
->wf
->nAvgBytesPerSec
= mp3_found
->mpa_br
* (1000 / 8);
341 sh_audio
->wf
->nBlockAlign
= mp3_found
->mpa_spf
;
342 sh_audio
->wf
->wBitsPerSample
= 16;
343 sh_audio
->wf
->cbSize
= 0;
344 sh_audio
->i_bps
= sh_audio
->wf
->nAvgBytesPerSec
;
347 if(s
->end_pos
&& (s
->flags
& MP_STREAM_SEEK
) == MP_STREAM_SEEK
) {
349 stream_seek(s
,s
->end_pos
-128);
350 stream_read(s
,tag
,3);
352 if(strcmp(tag
,"TAG"))
353 demuxer
->movi_end
= s
->end_pos
;
357 demuxer
->movi_end
= stream_tell(s
)-3;
358 stream_read(s
,buf
,30);
360 demux_info_add(demuxer
,"Title",buf
);
361 stream_read(s
,buf
,30);
363 demux_info_add(demuxer
,"Artist",buf
);
364 stream_read(s
,buf
,30);
366 demux_info_add(demuxer
,"Album",buf
);
367 stream_read(s
,buf
,4);
369 demux_info_add(demuxer
,"Year",buf
);
370 stream_read(s
,buf
,30);
372 demux_info_add(demuxer
,"Comment",buf
);
373 if(buf
[28] == 0 && buf
[29] != 0) {
374 uint8_t trk
= (uint8_t)buf
[29];
375 sprintf(buf
,"%d",trk
);
376 demux_info_add(demuxer
,"Track",buf
);
378 g
= stream_read_char(s
);
379 demux_info_add(demuxer
,"Genre",genres
[g
]);
384 unsigned int chunk_type
;
385 unsigned int chunk_size
;
388 l
= stream_read_dword_le(s
);
390 mp_msg(MSGT_DEMUX
,MSGL_ERR
,"[demux_audio] Bad wav header length: too short (%d)!!!\n",l
);
393 if(l
> MAX_WAVHDR_LEN
) {
394 mp_msg(MSGT_DEMUX
,MSGL_ERR
,"[demux_audio] Bad wav header length: too long (%d)!!!\n",l
);
397 sh_audio
->wf
= w
= malloc(l
> sizeof(*w
) ? l
: sizeof(*w
));
398 w
->wFormatTag
= sh_audio
->format
= stream_read_word_le(s
);
399 w
->nChannels
= sh_audio
->channels
= stream_read_word_le(s
);
400 w
->nSamplesPerSec
= sh_audio
->samplerate
= stream_read_dword_le(s
);
401 w
->nAvgBytesPerSec
= stream_read_dword_le(s
);
402 w
->nBlockAlign
= stream_read_word_le(s
);
403 w
->wBitsPerSample
= stream_read_word_le(s
);
404 sh_audio
->samplesize
= (w
->wBitsPerSample
+ 7) / 8;
406 sh_audio
->i_bps
= sh_audio
->wf
->nAvgBytesPerSec
;
409 w
->cbSize
= stream_read_word_le(s
);
412 mp_msg(MSGT_DEMUX
,MSGL_ERR
,"[demux_audio] truncated extradata (%d < %d)\n",
416 stream_read(s
,(char*)(w
+ 1),w
->cbSize
);
418 if (w
->wFormatTag
== 0xfffe && w
->cbSize
>= 22)
419 sh_audio
->format
= le2me_16(((WAVEFORMATEXTENSIBLE
*)w
)->SubFormat
);
422 if( mp_msg_test(MSGT_DEMUX
,MSGL_V
) ) print_wave_header(w
, MSGL_V
);
427 chunk_type
= stream_read_fourcc(demuxer
->stream
);
428 chunk_size
= stream_read_dword_le(demuxer
->stream
);
429 if (chunk_type
!= mmioFOURCC('d', 'a', 't', 'a'))
430 stream_skip(demuxer
->stream
, chunk_size
);
431 } while (!s
->eof
&& chunk_type
!= mmioFOURCC('d', 'a', 't', 'a'));
432 demuxer
->movi_start
= stream_tell(s
);
433 demuxer
->movi_end
= chunk_size
? demuxer
->movi_start
+ chunk_size
: s
->end_pos
;
434 // printf("wav: %X .. %X\n",(int)demuxer->movi_start,(int)demuxer->movi_end);
435 // Check if it contains dts audio
436 if((w
->wFormatTag
== 0x01) && (w
->nChannels
== 2) && (w
->nSamplesPerSec
== 44100)) {
437 unsigned char buf
[16384]; // vlc uses 16384*4 (4 dts frames)
439 memset(buf
, 0, sizeof(buf
));
440 stream_read(s
, buf
, sizeof(buf
));
441 for (i
= 0; i
< sizeof(buf
) - 5; i
+= 2) {
443 if((buf
[i
] == 0xff) && (buf
[i
+1] == 0x1f) && (buf
[i
+2] == 0x00) &&
444 (buf
[i
+3] == 0xe8) && ((buf
[i
+4] & 0xfe) == 0xf0) && (buf
[i
+5] == 0x07)) {
445 sh_audio
->format
= 0x2001;
446 mp_msg(MSGT_DEMUX
,MSGL_V
,"[demux_audio] DTS audio in wav, 14 bit, LE\n");
450 if((buf
[i
] == 0x1f) && (buf
[i
+1] == 0xff) && (buf
[i
+2] == 0xe8) &&
451 (buf
[i
+3] == 0x00) && (buf
[i
+4] == 0x07) && ((buf
[i
+5] & 0xfe) == 0xf0)) {
452 sh_audio
->format
= 0x2001;
453 mp_msg(MSGT_DEMUX
,MSGL_V
,"[demux_audio] DTS audio in wav, 14 bit, BE\n");
457 if((buf
[i
] == 0x7f) && (buf
[i
+1] == 0xfe) && (buf
[i
+2] == 0x80) &&
458 (buf
[i
+3] == 0x01)) {
459 sh_audio
->format
= 0x2001;
460 mp_msg(MSGT_DEMUX
,MSGL_V
,"[demux_audio] DTS audio in wav, 16 bit, BE\n");
464 if((buf
[i
] == 0xfe) && (buf
[i
+1] == 0x7f) && (buf
[i
+2] == 0x01) &&
465 (buf
[i
+3] == 0x80)) {
466 sh_audio
->format
= 0x2001;
467 mp_msg(MSGT_DEMUX
,MSGL_V
,"[demux_audio] DTS audio in wav, 16 bit, LE\n");
471 if (sh_audio
->format
== 0x2001) {
472 sh_audio
->needs_parsing
= 1;
473 mp_msg(MSGT_DEMUX
,MSGL_DBG2
,"[demux_audio] DTS sync offset = %u\n", i
);
477 stream_seek(s
,demuxer
->movi_start
);
480 sh_audio
->format
= mmioFOURCC('f', 'L', 'a', 'C');
481 demuxer
->movi_start
= stream_tell(s
) - 4;
482 demuxer
->movi_end
= s
->end_pos
;
483 if (demuxer
->movi_end
> demuxer
->movi_start
) {
484 // try to find out approx. bitrate
485 int64_t size
= demuxer
->movi_end
- demuxer
->movi_start
;
489 srate
= stream_read_int24(s
) >> 4;
490 num_samples
= stream_read_int24(s
) << 16;
491 num_samples
|= stream_read_word(s
);
492 if (num_samples
&& srate
)
493 sh_audio
->i_bps
= size
* srate
/ num_samples
;
495 if (sh_audio
->i_bps
< 1) // guess value to prevent crash
496 sh_audio
->i_bps
= 64 * 1024;
497 sh_audio
->needs_parsing
= 1;
498 // get_flac_metadata (demuxer);
502 priv
= malloc(sizeof(da_priv_t
));
505 demuxer
->priv
= priv
;
506 demuxer
->audio
->id
= 0;
507 demuxer
->audio
->sh
= sh_audio
;
508 sh_audio
->ds
= demuxer
->audio
;
509 sh_audio
->samplerate
= sh_audio
->audio
.dwRate
;
511 if(stream_tell(s
) != demuxer
->movi_start
)
513 mp_msg(MSGT_DEMUX
, MSGL_V
, "demux_audio: seeking from 0x%X to start pos 0x%X\n",
514 (int)stream_tell(s
), (int)demuxer
->movi_start
);
515 stream_seek(s
,demuxer
->movi_start
);
516 if (stream_tell(s
) != demuxer
->movi_start
) {
517 mp_msg(MSGT_DEMUX
, MSGL_V
, "demux_audio: seeking failed, now at 0x%X!\n",
518 (int)stream_tell(s
));
519 if (next_frame_pos
) {
520 mp_msg(MSGT_DEMUX
, MSGL_V
, "demux_audio: seeking to 0x%X instead\n",
521 (int)next_frame_pos
);
522 stream_seek(s
, next_frame_pos
);
527 mp_msg(MSGT_DEMUX
,MSGL_V
,"demux_audio: audio data 0x%X - 0x%X \n",(int)demuxer
->movi_start
,(int)demuxer
->movi_end
);
529 return DEMUXER_TYPE_AUDIO
;
533 static int demux_audio_fill_buffer(demuxer_t
*demux
, demux_stream_t
*ds
) {
536 sh_audio_t
* sh_audio
= ds
->sh
;
537 da_priv_t
* priv
= demux
->priv
;
538 double this_pts
= priv
->next_pts
;
539 stream_t
* s
= demux
->stream
;
548 stream_read(s
,hdr
,4);
551 l
= mp_decode_mp3_header(hdr
);
553 if (demux
->movi_end
&& stream_tell(s
) >= demux
->movi_end
)
554 return 0; // might be ID3 tag, i.e. EOF
557 dp
= new_demux_packet(l
);
558 memcpy(dp
->buffer
,hdr
,4);
559 if (stream_read(s
,dp
->buffer
+ 4,l
-4) != l
-4)
561 free_demux_packet(dp
);
564 priv
->next_pts
+= sh_audio
->audio
.dwScale
/(double)sh_audio
->samplerate
;
569 unsigned align
= sh_audio
->wf
->nBlockAlign
;
570 l
= sh_audio
->wf
->nAvgBytesPerSec
;
571 if (l
<= 0) l
= 65536;
572 if (demux
->movi_end
&& l
> demux
->movi_end
- stream_tell(s
)) {
573 // do not read beyond end, there might be junk after data chunk
574 l
= demux
->movi_end
- stream_tell(s
);
575 if (l
<= 0) return 0;
578 l
= (l
+ align
- 1) / align
* align
;
579 dp
= new_demux_packet(l
);
580 l
= stream_read(s
,dp
->buffer
,l
);
581 priv
->next_pts
+= l
/(double)sh_audio
->i_bps
;
586 dp
= new_demux_packet(l
);
587 l
= stream_read(s
,dp
->buffer
,l
);
588 priv
->next_pts
= MP_NOPTS_VALUE
;
592 mp_tmsg(MSGT_DEMUXER
,MSGL_WARN
,"Audio demuxer: unknown format %d.\n",priv
->frmt
);
596 resize_demux_packet(dp
, l
);
598 ds_add_packet(ds
, dp
);
602 static void high_res_mp3_seek(demuxer_t
*demuxer
,float time
) {
605 da_priv_t
* priv
= demuxer
->priv
;
606 sh_audio_t
* sh
= (sh_audio_t
*)demuxer
->audio
->sh
;
608 nf
= time
*sh
->samplerate
/sh
->audio
.dwScale
;
610 stream_read(demuxer
->stream
,hdr
,4);
611 len
= mp_decode_mp3_header(hdr
);
613 stream_skip(demuxer
->stream
,-3);
616 stream_skip(demuxer
->stream
,len
-4);
617 priv
->next_pts
+= sh
->audio
.dwScale
/(double)sh
->samplerate
;
622 static void demux_audio_seek(demuxer_t
*demuxer
,float rel_seek_secs
,float audio_delay
,int flags
){
623 struct MPOpts
*opts
= demuxer
->opts
;
624 sh_audio_t
* sh_audio
;
630 if(!(sh_audio
= demuxer
->audio
->sh
))
633 priv
= demuxer
->priv
;
635 if(priv
->frmt
== MP3
&& opts
->hr_mp3_seek
&& !(flags
& SEEK_FACTOR
)) {
636 len
= (flags
& SEEK_ABSOLUTE
) ? rel_seek_secs
- priv
->next_pts
: rel_seek_secs
;
638 stream_seek(s
,demuxer
->movi_start
);
639 len
= priv
->next_pts
+ len
;
643 high_res_mp3_seek(demuxer
,len
);
647 base
= flags
&SEEK_ABSOLUTE
? demuxer
->movi_start
: stream_tell(s
);
648 if(flags
&SEEK_FACTOR
)
649 pos
= base
+ ((demuxer
->movi_end
- demuxer
->movi_start
)*rel_seek_secs
);
651 pos
= base
+ (rel_seek_secs
*sh_audio
->i_bps
);
653 if(demuxer
->movi_end
&& pos
>= demuxer
->movi_end
) {
654 pos
= demuxer
->movi_end
;
655 } else if(pos
< demuxer
->movi_start
)
656 pos
= demuxer
->movi_start
;
658 priv
->next_pts
= (pos
-demuxer
->movi_start
)/(double)sh_audio
->i_bps
;
662 pos
-= (pos
- demuxer
->movi_start
) %
663 (sh_audio
->wf
->nBlockAlign
? sh_audio
->wf
->nBlockAlign
:
664 (sh_audio
->channels
* sh_audio
->samplesize
));
671 static void demux_close_audio(demuxer_t
* demuxer
) {
672 da_priv_t
* priv
= demuxer
->priv
;
677 static int demux_audio_control(demuxer_t
*demuxer
,int cmd
, void *arg
){
678 sh_audio_t
*sh_audio
=demuxer
->audio
->sh
;
679 int audio_length
= sh_audio
->i_bps
? demuxer
->movi_end
/ sh_audio
->i_bps
: 0;
680 da_priv_t
* priv
= demuxer
->priv
;
683 case DEMUXER_CTRL_GET_TIME_LENGTH
:
684 if (audio_length
<=0) return DEMUXER_CTRL_DONTKNOW
;
685 *((double *)arg
)=(double)audio_length
;
686 return DEMUXER_CTRL_GUESS
;
688 case DEMUXER_CTRL_GET_PERCENT_POS
:
690 return DEMUXER_CTRL_DONTKNOW
;
691 *((int *)arg
)=(int)( (priv
->next_pts
*100) / audio_length
);
692 return DEMUXER_CTRL_OK
;
695 return DEMUXER_CTRL_NOTIMPL
;
700 const demuxer_desc_t demuxer_desc_audio
= {
707 0, // unsafe autodetect
709 demux_audio_fill_buffer
,