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.
25 #include "stream/stream.h"
30 #include "ffmpeg_files/intreadwrite.h"
41 typedef struct da_priv
{
46 //! rather arbitrary value for maximum length of wav-format headers
47 #define MAX_WAVHDR_LEN (1 * 1024 * 1024)
49 //! how many valid frames in a row we need before accepting as valid MP3
50 #define MIN_MP3_HDRS 12
52 //! Used to describe a potential (chain of) MP3 headers we found
53 typedef struct mp3_hdr
{
54 off_t frame_pos
; // start of first frame in this "chain" of headers
55 off_t next_frame_pos
; // here we expect the next header with same parameters
61 int cons_hdrs
; // if this reaches MIN_MP3_HDRS we accept as MP3 file
65 void print_wave_header(WAVEFORMATEX
*h
, int verbose_level
);
70 * \brief free a list of MP3 header descriptions
71 * \param list pointer to the head-of-list pointer
73 static void free_mp3_hdrs(mp3_hdr_t
**list
) {
83 * \brief add another potential MP3 header to our list
84 * If it fits into an existing chain this one is expanded otherwise
85 * a new one is created.
86 * All entries that expected a MP3 header before the current position
88 * The list is expected to be and will be kept sorted by next_frame_pos
89 * and when those are equal by frame_pos.
90 * \param list pointer to the head-of-list pointer
91 * \param st_pos stream position where the described header starts
92 * \param mp3_chans number of channels as specified by the header (*)
93 * \param mp3_freq sampling frequency as specified by the header (*)
94 * \param mpa_spf frame size as specified by the header
95 * \param mpa_layer layer type ("version") as specified by the header (*)
96 * \param mpa_br bitrate as specified by the header
97 * \param mp3_flen length of the frame as specified by the header
98 * \return If non-null the current file is accepted as MP3 and the
99 * mp3_hdr struct describing the valid chain is returned. Must be
100 * freed independent of the list.
102 * parameters marked by (*) must be the same for all headers in the same chain
104 static mp3_hdr_t
*add_mp3_hdr(mp3_hdr_t
**list
, off_t st_pos
,
105 int mp3_chans
, int mp3_freq
, int mpa_spf
,
106 int mpa_layer
, int mpa_br
, int mp3_flen
) {
109 while (*list
&& (*list
)->next_frame_pos
<= st_pos
) {
110 if (((*list
)->next_frame_pos
< st_pos
) || ((*list
)->mp3_chans
!= mp3_chans
)
111 || ((*list
)->mp3_freq
!= mp3_freq
) || ((*list
)->mpa_layer
!= mpa_layer
) ) {
117 (*list
)->cons_hdrs
++;
118 (*list
)->next_frame_pos
= st_pos
+ mp3_flen
;
119 (*list
)->mpa_spf
= mpa_spf
;
120 (*list
)->mpa_br
= mpa_br
;
121 if ((*list
)->cons_hdrs
>= MIN_MP3_HDRS
) {
122 // copy the valid entry, so that the list can be easily freed
123 tmp
= malloc(sizeof(mp3_hdr_t
));
124 memcpy(tmp
, *list
, sizeof(mp3_hdr_t
));
129 list
= &((*list
)->next
);
132 if (!in_list
) { // does not belong into an existing chain, insert
133 // find right position to insert to keep sorting
134 while (*list
&& (*list
)->next_frame_pos
<= st_pos
+ mp3_flen
)
135 list
= &((*list
)->next
);
136 tmp
= malloc(sizeof(mp3_hdr_t
));
137 tmp
->frame_pos
= st_pos
;
138 tmp
->next_frame_pos
= st_pos
+ mp3_flen
;
139 tmp
->mp3_chans
= mp3_chans
;
140 tmp
->mp3_freq
= mp3_freq
;
141 tmp
->mpa_spf
= mpa_spf
;
142 tmp
->mpa_layer
= mpa_layer
;
143 tmp
->mpa_br
= mpa_br
;
151 #if 0 /* this code is a mess, clean it up before reenabling */
152 #define FLAC_SIGNATURE_SIZE 4
153 #define FLAC_STREAMINFO_SIZE 34
154 #define FLAC_SEEKPOINT_SIZE 18
166 get_flac_metadata (demuxer_t
* demuxer
)
169 unsigned int blk_len
;
170 stream_t
*s
= demuxer
->stream
;
172 /* file is qualified; skip over the signature bytes in the stream */
175 /* loop through the metadata blocks; use a do-while construct since there
176 * will always be 1 metadata block */
180 r
= stream_read (s
, (char *) preamble
, FLAC_SIGNATURE_SIZE
);
181 if (r
!= FLAC_SIGNATURE_SIZE
)
184 blk_len
= AV_RB24(preamble
+ 1);
186 switch (preamble
[0] & 0x7F)
188 case FLAC_VORBIS_COMMENT
:
190 /* For a description of the format please have a look at */
191 /* http://www.xiph.org/vorbis/doc/v-comment.html */
193 uint32_t length
, comment_list_len
;
194 char comments
[blk_len
];
195 uint8_t *ptr
= comments
;
200 if (stream_read (s
, comments
, blk_len
) == blk_len
)
202 length
= AV_RL32(ptr
);
205 comment_list_len
= AV_RL32(ptr
);
209 for (; cn
< comment_list_len
; cn
++)
211 length
= AV_RL32(ptr
);
215 if (&comment
[length
] < comments
|| &comment
[length
] >= &comments
[blk_len
])
220 if (!strncasecmp ("TITLE=", comment
, 6) && (length
- 6 > 0))
221 demux_info_add (demuxer
, "Title", comment
+ 6);
222 else if (!strncasecmp ("ARTIST=", comment
, 7) && (length
- 7 > 0))
223 demux_info_add (demuxer
, "Artist", comment
+ 7);
224 else if (!strncasecmp ("ALBUM=", comment
, 6) && (length
- 6 > 0))
225 demux_info_add (demuxer
, "Album", comment
+ 6);
226 else if (!strncasecmp ("DATE=", comment
, 5) && (length
- 5 > 0))
227 demux_info_add (demuxer
, "Year", comment
+ 5);
228 else if (!strncasecmp ("GENRE=", comment
, 6) && (length
- 6 > 0))
229 demux_info_add (demuxer
, "Genre", comment
+ 6);
230 else if (!strncasecmp ("Comment=", comment
, 8) && (length
- 8 > 0))
231 demux_info_add (demuxer
, "Comment", comment
+ 8);
232 else if (!strncasecmp ("TRACKNUMBER=", comment
, 12)
233 && (length
- 12 > 0))
237 sprintf (buf
, "%d", atoi (comment
+ 12));
238 demux_info_add(demuxer
, "Track", buf
);
248 case FLAC_STREAMINFO
:
250 case FLAC_APPLICATION
:
254 /* 6-127 are presently reserved */
255 stream_skip (s
, blk_len
);
258 } while ((preamble
[0] & 0x80) == 0);
262 static int demux_audio_open(demuxer_t
* demuxer
) {
264 sh_audio_t
* sh_audio
;
265 uint8_t hdr
[HDR_SIZE
];
266 int frmt
= 0, n
= 0, step
;
267 off_t st_pos
= 0, next_frame_pos
= 0;
268 // mp3_hdrs list is sorted first by next_frame_pos and then by frame_pos
269 mp3_hdr_t
*mp3_hdrs
= NULL
, *mp3_found
= NULL
;
274 stream_read(s
, hdr
, HDR_SIZE
);
275 while(n
< 30000 && !s
->eof
) {
276 int mp3_freq
, mp3_chans
, mp3_flen
, mpa_layer
, mpa_spf
, mpa_br
;
277 st_pos
= stream_tell(s
) - HDR_SIZE
;
280 if( hdr
[0] == 'R' && hdr
[1] == 'I' && hdr
[2] == 'F' && hdr
[3] == 'F' ) {
284 stream_read(s
,hdr
,4);
287 if(hdr
[0] != 'W' || hdr
[1] != 'A' || hdr
[2] != 'V' || hdr
[3] != 'E' )
290 // We found wav header. Now we can have 'fmt ' or a mp3 header
293 } else if( hdr
[0] == 'I' && hdr
[1] == 'D' && hdr
[2] == '3' && (hdr
[3] >= 2)) {
296 stream_read(s
,hdr
,4);
297 len
= (hdr
[0]<<21) | (hdr
[1]<<14) | (hdr
[2]<<7) | hdr
[3];
300 } else if( hdr
[0] == 'f' && hdr
[1] == 'm' && hdr
[2] == 't' && hdr
[3] == ' ' ) {
303 } else if((mp3_flen
= mp_get_mp3_header(hdr
, &mp3_chans
, &mp3_freq
,
304 &mpa_spf
, &mpa_layer
, &mpa_br
)) > 0) {
305 mp3_found
= add_mp3_hdr(&mp3_hdrs
, st_pos
, mp3_chans
, mp3_freq
,
306 mpa_spf
, mpa_layer
, mpa_br
, mp3_flen
);
311 } else if( hdr
[0] == 'f' && hdr
[1] == 'L' && hdr
[2] == 'a' && hdr
[3] == 'C' ) {
313 if (!mp3_hdrs
|| mp3_hdrs
->cons_hdrs
< 3)
316 // Add here some other audio format detection
318 memmove(hdr
,&hdr
[step
],HDR_SIZE
-step
);
319 stream_read(s
, &hdr
[HDR_SIZE
- step
], step
);
323 free_mp3_hdrs(&mp3_hdrs
);
328 sh_audio
= new_sh_audio(demuxer
,0);
332 sh_audio
->format
= (mp3_found
->mpa_layer
< 3 ? 0x50 : 0x55);
333 demuxer
->movi_start
= mp3_found
->frame_pos
;
334 next_frame_pos
= mp3_found
->next_frame_pos
;
335 sh_audio
->audio
.dwSampleSize
= 0;
336 sh_audio
->audio
.dwScale
= mp3_found
->mpa_spf
;
337 sh_audio
->audio
.dwRate
= mp3_found
->mp3_freq
;
338 sh_audio
->wf
= malloc(sizeof(WAVEFORMATEX
));
339 sh_audio
->wf
->wFormatTag
= sh_audio
->format
;
340 sh_audio
->wf
->nChannels
= mp3_found
->mp3_chans
;
341 sh_audio
->wf
->nSamplesPerSec
= mp3_found
->mp3_freq
;
342 sh_audio
->wf
->nAvgBytesPerSec
= mp3_found
->mpa_br
* (1000 / 8);
343 sh_audio
->wf
->nBlockAlign
= mp3_found
->mpa_spf
;
344 sh_audio
->wf
->wBitsPerSample
= 16;
345 sh_audio
->wf
->cbSize
= 0;
346 sh_audio
->i_bps
= sh_audio
->wf
->nAvgBytesPerSec
;
349 if(s
->end_pos
&& (s
->flags
& MP_STREAM_SEEK
) == MP_STREAM_SEEK
) {
351 stream_seek(s
,s
->end_pos
-128);
352 stream_read(s
,tag
,3);
354 if(strcmp(tag
,"TAG"))
355 demuxer
->movi_end
= s
->end_pos
;
359 demuxer
->movi_end
= stream_tell(s
)-3;
360 stream_read(s
,buf
,30);
362 demux_info_add(demuxer
,"Title",buf
);
363 stream_read(s
,buf
,30);
365 demux_info_add(demuxer
,"Artist",buf
);
366 stream_read(s
,buf
,30);
368 demux_info_add(demuxer
,"Album",buf
);
369 stream_read(s
,buf
,4);
371 demux_info_add(demuxer
,"Year",buf
);
372 stream_read(s
,buf
,30);
374 demux_info_add(demuxer
,"Comment",buf
);
375 if(buf
[28] == 0 && buf
[29] != 0) {
376 uint8_t trk
= (uint8_t)buf
[29];
377 sprintf(buf
,"%d",trk
);
378 demux_info_add(demuxer
,"Track",buf
);
380 g
= stream_read_char(s
);
381 demux_info_add(demuxer
,"Genre",genres
[g
]);
386 unsigned int chunk_type
;
387 unsigned int chunk_size
;
390 l
= stream_read_dword_le(s
);
392 mp_msg(MSGT_DEMUX
,MSGL_ERR
,"[demux_audio] Bad wav header length: too short (%d)!!!\n",l
);
395 if(l
> MAX_WAVHDR_LEN
) {
396 mp_msg(MSGT_DEMUX
,MSGL_ERR
,"[demux_audio] Bad wav header length: too long (%d)!!!\n",l
);
399 sh_audio
->wf
= w
= malloc(l
> sizeof(WAVEFORMATEX
) ? l
: sizeof(WAVEFORMATEX
));
400 w
->wFormatTag
= sh_audio
->format
= stream_read_word_le(s
);
401 w
->nChannels
= sh_audio
->channels
= stream_read_word_le(s
);
402 w
->nSamplesPerSec
= sh_audio
->samplerate
= stream_read_dword_le(s
);
403 w
->nAvgBytesPerSec
= stream_read_dword_le(s
);
404 w
->nBlockAlign
= stream_read_word_le(s
);
405 w
->wBitsPerSample
= stream_read_word_le(s
);
406 sh_audio
->samplesize
= (w
->wBitsPerSample
+ 7) / 8;
408 sh_audio
->i_bps
= sh_audio
->wf
->nAvgBytesPerSec
;
411 w
->cbSize
= stream_read_word_le(s
);
414 mp_msg(MSGT_DEMUX
,MSGL_ERR
,"[demux_audio] truncated extradata (%d < %d)\n",
418 stream_read(s
,(char*)((char*)(w
)+sizeof(WAVEFORMATEX
)),w
->cbSize
);
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 mp_msg(MSGT_DEMUX
,MSGL_DBG2
,"[demux_audio] DTS sync offset = %u\n", i
);
475 stream_seek(s
,demuxer
->movi_start
);
478 sh_audio
->format
= mmioFOURCC('f', 'L', 'a', 'C');
479 demuxer
->movi_start
= stream_tell(s
) - 4;
480 demuxer
->movi_end
= s
->end_pos
;
481 if (demuxer
->movi_end
> demuxer
->movi_start
) {
482 // try to find out approx. bitrate
483 int64_t size
= demuxer
->movi_end
- demuxer
->movi_start
;
484 int64_t num_samples
= 0;
487 stream_read(s
, (char *)&srate
, 3);
488 srate
= be2me_32(srate
) >> 12;
489 stream_read(s
, (char *)&num_samples
, 5);
490 num_samples
= (be2me_64(num_samples
) >> 24) & 0xfffffffffULL
;
491 if (num_samples
&& srate
)
492 sh_audio
->i_bps
= size
* srate
/ num_samples
;
494 if (sh_audio
->i_bps
< 1) // guess value to prevent crash
495 sh_audio
->i_bps
= 64 * 1024;
496 // get_flac_metadata (demuxer);
500 priv
= malloc(sizeof(da_priv_t
));
503 demuxer
->priv
= priv
;
504 demuxer
->audio
->id
= 0;
505 demuxer
->audio
->sh
= sh_audio
;
506 sh_audio
->ds
= demuxer
->audio
;
507 sh_audio
->samplerate
= sh_audio
->audio
.dwRate
;
509 if(stream_tell(s
) != demuxer
->movi_start
)
511 mp_msg(MSGT_DEMUX
, MSGL_V
, "demux_audio: seeking from 0x%X to start pos 0x%X\n",
512 (int)stream_tell(s
), (int)demuxer
->movi_start
);
513 stream_seek(s
,demuxer
->movi_start
);
514 if (stream_tell(s
) != demuxer
->movi_start
) {
515 mp_msg(MSGT_DEMUX
, MSGL_V
, "demux_audio: seeking failed, now at 0x%X!\n",
516 (int)stream_tell(s
));
517 if (next_frame_pos
) {
518 mp_msg(MSGT_DEMUX
, MSGL_V
, "demux_audio: seeking to 0x%X instead\n",
519 (int)next_frame_pos
);
520 stream_seek(s
, next_frame_pos
);
525 mp_msg(MSGT_DEMUX
,MSGL_V
,"demux_audio: audio data 0x%X - 0x%X \n",(int)demuxer
->movi_start
,(int)demuxer
->movi_end
);
527 return DEMUXER_TYPE_AUDIO
;
531 static int demux_audio_fill_buffer(demuxer_t
*demux
, demux_stream_t
*ds
) {
534 sh_audio_t
* sh_audio
= ds
->sh
;
535 da_priv_t
* priv
= demux
->priv
;
536 double this_pts
= priv
->next_pts
;
537 stream_t
* s
= demux
->stream
;
546 stream_read(s
,hdr
,4);
549 l
= mp_decode_mp3_header(hdr
);
551 if (demux
->movi_end
&& stream_tell(s
) >= demux
->movi_end
)
552 return 0; // might be ID3 tag, i.e. EOF
555 dp
= new_demux_packet(l
);
556 memcpy(dp
->buffer
,hdr
,4);
557 if (stream_read(s
,dp
->buffer
+ 4,l
-4) != l
-4)
559 free_demux_packet(dp
);
562 priv
->next_pts
+= sh_audio
->audio
.dwScale
/(double)sh_audio
->samplerate
;
567 unsigned align
= sh_audio
->wf
->nBlockAlign
;
568 l
= sh_audio
->wf
->nAvgBytesPerSec
;
569 if (l
<= 0) l
= 65536;
570 if (demux
->movi_end
&& l
> demux
->movi_end
- stream_tell(s
)) {
571 // do not read beyond end, there might be junk after data chunk
572 l
= demux
->movi_end
- stream_tell(s
);
573 if (l
<= 0) return 0;
576 l
= (l
+ align
- 1) / align
* align
;
577 dp
= new_demux_packet(l
);
578 l
= stream_read(s
,dp
->buffer
,l
);
579 priv
->next_pts
+= l
/(double)sh_audio
->i_bps
;
584 dp
= new_demux_packet(l
);
585 l
= stream_read(s
,dp
->buffer
,l
);
586 /* FLAC is not a constant-bitrate codec. These values will be wrong. */
587 priv
->next_pts
+= l
/(double)sh_audio
->i_bps
;
591 mp_tmsg(MSGT_DEMUXER
,MSGL_WARN
,"Audio demuxer: unknown format %d.\n",priv
->frmt
);
595 resize_demux_packet(dp
, l
);
597 ds_add_packet(ds
, dp
);
601 static void high_res_mp3_seek(demuxer_t
*demuxer
,float time
) {
604 da_priv_t
* priv
= demuxer
->priv
;
605 sh_audio_t
* sh
= (sh_audio_t
*)demuxer
->audio
->sh
;
607 nf
= time
*sh
->samplerate
/sh
->audio
.dwScale
;
609 stream_read(demuxer
->stream
,hdr
,4);
610 len
= mp_decode_mp3_header(hdr
);
612 stream_skip(demuxer
->stream
,-3);
615 stream_skip(demuxer
->stream
,len
-4);
616 priv
->next_pts
+= sh
->audio
.dwScale
/(double)sh
->samplerate
;
621 static void demux_audio_seek(demuxer_t
*demuxer
,float rel_seek_secs
,float audio_delay
,int flags
){
622 sh_audio_t
* sh_audio
;
628 if(!(sh_audio
= demuxer
->audio
->sh
))
631 priv
= demuxer
->priv
;
633 if(priv
->frmt
== MP3
&& hr_mp3_seek
&& !(flags
& SEEK_FACTOR
)) {
634 len
= (flags
& SEEK_ABSOLUTE
) ? rel_seek_secs
- priv
->next_pts
: rel_seek_secs
;
636 stream_seek(s
,demuxer
->movi_start
);
637 len
= priv
->next_pts
+ len
;
641 high_res_mp3_seek(demuxer
,len
);
645 base
= flags
&SEEK_ABSOLUTE
? demuxer
->movi_start
: stream_tell(s
);
646 if(flags
&SEEK_FACTOR
)
647 pos
= base
+ ((demuxer
->movi_end
- demuxer
->movi_start
)*rel_seek_secs
);
649 pos
= base
+ (rel_seek_secs
*sh_audio
->i_bps
);
651 if(demuxer
->movi_end
&& pos
>= demuxer
->movi_end
) {
652 pos
= demuxer
->movi_end
;
653 } else if(pos
< demuxer
->movi_start
)
654 pos
= demuxer
->movi_start
;
656 priv
->next_pts
= (pos
-demuxer
->movi_start
)/(double)sh_audio
->i_bps
;
660 pos
-= (pos
- demuxer
->movi_start
) %
661 (sh_audio
->wf
->nBlockAlign
? sh_audio
->wf
->nBlockAlign
:
662 (sh_audio
->channels
* sh_audio
->samplesize
));
669 static void demux_close_audio(demuxer_t
* demuxer
) {
670 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
,