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"
31 #include "ffmpeg_files/intreadwrite.h"
42 typedef struct da_priv
{
47 //! rather arbitrary value for maximum length of wav-format headers
48 #define MAX_WAVHDR_LEN (1 * 1024 * 1024)
50 //! how many valid frames in a row we need before accepting as valid MP3
51 #define MIN_MP3_HDRS 12
53 //! Used to describe a potential (chain of) MP3 headers we found
54 typedef struct mp3_hdr
{
55 off_t frame_pos
; // start of first frame in this "chain" of headers
56 off_t next_frame_pos
; // here we expect the next header with same parameters
62 int cons_hdrs
; // if this reaches MIN_MP3_HDRS we accept as MP3 file
67 * \brief free a list of MP3 header descriptions
68 * \param list pointer to the head-of-list pointer
70 static void free_mp3_hdrs(mp3_hdr_t
**list
) {
80 * \brief add another potential MP3 header to our list
81 * If it fits into an existing chain this one is expanded otherwise
82 * a new one is created.
83 * All entries that expected a MP3 header before the current position
85 * The list is expected to be and will be kept sorted by next_frame_pos
86 * and when those are equal by frame_pos.
87 * \param list pointer to the head-of-list pointer
88 * \param st_pos stream position where the described header starts
89 * \param mp3_chans number of channels as specified by the header (*)
90 * \param mp3_freq sampling frequency as specified by the header (*)
91 * \param mpa_spf frame size as specified by the header
92 * \param mpa_layer layer type ("version") as specified by the header (*)
93 * \param mpa_br bitrate as specified by the header
94 * \param mp3_flen length of the frame as specified by the header
95 * \return If non-null the current file is accepted as MP3 and the
96 * mp3_hdr struct describing the valid chain is returned. Must be
97 * freed independent of the list.
99 * parameters marked by (*) must be the same for all headers in the same chain
101 static mp3_hdr_t
*add_mp3_hdr(mp3_hdr_t
**list
, off_t st_pos
,
102 int mp3_chans
, int mp3_freq
, int mpa_spf
,
103 int mpa_layer
, int mpa_br
, int mp3_flen
) {
106 while (*list
&& (*list
)->next_frame_pos
<= st_pos
) {
107 if (((*list
)->next_frame_pos
< st_pos
) || ((*list
)->mp3_chans
!= mp3_chans
)
108 || ((*list
)->mp3_freq
!= mp3_freq
) || ((*list
)->mpa_layer
!= mpa_layer
) ) {
114 (*list
)->cons_hdrs
++;
115 (*list
)->next_frame_pos
= st_pos
+ mp3_flen
;
116 (*list
)->mpa_spf
= mpa_spf
;
117 (*list
)->mpa_br
= mpa_br
;
118 if ((*list
)->cons_hdrs
>= MIN_MP3_HDRS
) {
119 // copy the valid entry, so that the list can be easily freed
120 tmp
= malloc(sizeof(mp3_hdr_t
));
121 memcpy(tmp
, *list
, sizeof(mp3_hdr_t
));
126 list
= &((*list
)->next
);
129 if (!in_list
) { // does not belong into an existing chain, insert
130 // find right position to insert to keep sorting
131 while (*list
&& (*list
)->next_frame_pos
<= st_pos
+ mp3_flen
)
132 list
= &((*list
)->next
);
133 tmp
= malloc(sizeof(mp3_hdr_t
));
134 tmp
->frame_pos
= st_pos
;
135 tmp
->next_frame_pos
= st_pos
+ mp3_flen
;
136 tmp
->mp3_chans
= mp3_chans
;
137 tmp
->mp3_freq
= mp3_freq
;
138 tmp
->mpa_spf
= mpa_spf
;
139 tmp
->mpa_layer
= mpa_layer
;
140 tmp
->mpa_br
= mpa_br
;
148 #if 0 /* this code is a mess, clean it up before reenabling */
149 #define FLAC_SIGNATURE_SIZE 4
150 #define FLAC_STREAMINFO_SIZE 34
151 #define FLAC_SEEKPOINT_SIZE 18
163 get_flac_metadata (demuxer_t
* demuxer
)
166 unsigned int blk_len
;
167 stream_t
*s
= demuxer
->stream
;
169 /* file is qualified; skip over the signature bytes in the stream */
172 /* loop through the metadata blocks; use a do-while construct since there
173 * will always be 1 metadata block */
177 r
= stream_read (s
, (char *) preamble
, FLAC_SIGNATURE_SIZE
);
178 if (r
!= FLAC_SIGNATURE_SIZE
)
181 blk_len
= AV_RB24(preamble
+ 1);
183 switch (preamble
[0] & 0x7F)
185 case FLAC_VORBIS_COMMENT
:
187 /* For a description of the format please have a look at */
188 /* http://www.xiph.org/vorbis/doc/v-comment.html */
190 uint32_t length
, comment_list_len
;
191 char comments
[blk_len
];
192 uint8_t *ptr
= comments
;
197 if (stream_read (s
, comments
, blk_len
) == blk_len
)
199 length
= AV_RL32(ptr
);
202 comment_list_len
= AV_RL32(ptr
);
206 for (; cn
< comment_list_len
; cn
++)
208 length
= AV_RL32(ptr
);
212 if (&comment
[length
] < comments
|| &comment
[length
] >= &comments
[blk_len
])
217 if (!strncasecmp ("TITLE=", comment
, 6) && (length
- 6 > 0))
218 demux_info_add (demuxer
, "Title", comment
+ 6);
219 else if (!strncasecmp ("ARTIST=", comment
, 7) && (length
- 7 > 0))
220 demux_info_add (demuxer
, "Artist", comment
+ 7);
221 else if (!strncasecmp ("ALBUM=", comment
, 6) && (length
- 6 > 0))
222 demux_info_add (demuxer
, "Album", comment
+ 6);
223 else if (!strncasecmp ("DATE=", comment
, 5) && (length
- 5 > 0))
224 demux_info_add (demuxer
, "Year", comment
+ 5);
225 else if (!strncasecmp ("GENRE=", comment
, 6) && (length
- 6 > 0))
226 demux_info_add (demuxer
, "Genre", comment
+ 6);
227 else if (!strncasecmp ("Comment=", comment
, 8) && (length
- 8 > 0))
228 demux_info_add (demuxer
, "Comment", comment
+ 8);
229 else if (!strncasecmp ("TRACKNUMBER=", comment
, 12)
230 && (length
- 12 > 0))
234 sprintf (buf
, "%d", atoi (comment
+ 12));
235 demux_info_add(demuxer
, "Track", buf
);
245 case FLAC_STREAMINFO
:
247 case FLAC_APPLICATION
:
251 /* 6-127 are presently reserved */
252 stream_skip (s
, blk_len
);
255 } while ((preamble
[0] & 0x80) == 0);
259 static int demux_audio_open(demuxer_t
* demuxer
) {
261 sh_audio_t
* sh_audio
;
262 uint8_t hdr
[HDR_SIZE
];
263 int frmt
= 0, n
= 0, step
;
264 off_t st_pos
= 0, next_frame_pos
= 0;
265 // mp3_hdrs list is sorted first by next_frame_pos and then by frame_pos
266 mp3_hdr_t
*mp3_hdrs
= NULL
, *mp3_found
= NULL
;
271 stream_read(s
, hdr
, HDR_SIZE
);
272 while(n
< 30000 && !s
->eof
) {
273 int mp3_freq
, mp3_chans
, mp3_flen
, mpa_layer
, mpa_spf
, mpa_br
;
274 st_pos
= stream_tell(s
) - HDR_SIZE
;
277 if( hdr
[0] == 'R' && hdr
[1] == 'I' && hdr
[2] == 'F' && hdr
[3] == 'F' ) {
281 stream_read(s
,hdr
,4);
284 if(hdr
[0] != 'W' || hdr
[1] != 'A' || hdr
[2] != 'V' || hdr
[3] != 'E' )
287 // We found wav header. Now we can have 'fmt ' or a mp3 header
290 } else if( hdr
[0] == 'I' && hdr
[1] == 'D' && hdr
[2] == '3' && (hdr
[3] >= 2)) {
293 stream_read(s
,hdr
,4);
294 len
= (hdr
[0]<<21) | (hdr
[1]<<14) | (hdr
[2]<<7) | hdr
[3];
297 } else if( hdr
[0] == 'f' && hdr
[1] == 'm' && hdr
[2] == 't' && hdr
[3] == ' ' ) {
300 } else if((mp3_flen
= mp_get_mp3_header(hdr
, &mp3_chans
, &mp3_freq
,
301 &mpa_spf
, &mpa_layer
, &mpa_br
)) > 0) {
302 mp3_found
= add_mp3_hdr(&mp3_hdrs
, st_pos
, mp3_chans
, mp3_freq
,
303 mpa_spf
, mpa_layer
, mpa_br
, mp3_flen
);
308 } else if( hdr
[0] == 'f' && hdr
[1] == 'L' && hdr
[2] == 'a' && hdr
[3] == 'C' ) {
310 if (!mp3_hdrs
|| mp3_hdrs
->cons_hdrs
< 3)
313 // Add here some other audio format detection
315 memmove(hdr
,&hdr
[step
],HDR_SIZE
-step
);
316 stream_read(s
, &hdr
[HDR_SIZE
- step
], step
);
320 free_mp3_hdrs(&mp3_hdrs
);
325 sh_audio
= new_sh_audio(demuxer
,0);
329 sh_audio
->format
= (mp3_found
->mpa_layer
< 3 ? 0x50 : 0x55);
330 demuxer
->movi_start
= mp3_found
->frame_pos
;
331 next_frame_pos
= mp3_found
->next_frame_pos
;
332 sh_audio
->audio
.dwSampleSize
= 0;
333 sh_audio
->audio
.dwScale
= mp3_found
->mpa_spf
;
334 sh_audio
->audio
.dwRate
= mp3_found
->mp3_freq
;
335 sh_audio
->wf
= malloc(sizeof(*sh_audio
->wf
));
336 sh_audio
->wf
->wFormatTag
= sh_audio
->format
;
337 sh_audio
->wf
->nChannels
= mp3_found
->mp3_chans
;
338 sh_audio
->wf
->nSamplesPerSec
= mp3_found
->mp3_freq
;
339 sh_audio
->wf
->nAvgBytesPerSec
= mp3_found
->mpa_br
* (1000 / 8);
340 sh_audio
->wf
->nBlockAlign
= mp3_found
->mpa_spf
;
341 sh_audio
->wf
->wBitsPerSample
= 16;
342 sh_audio
->wf
->cbSize
= 0;
343 sh_audio
->i_bps
= sh_audio
->wf
->nAvgBytesPerSec
;
346 if(s
->end_pos
&& (s
->flags
& MP_STREAM_SEEK
) == MP_STREAM_SEEK
) {
348 stream_seek(s
,s
->end_pos
-128);
349 stream_read(s
,tag
,3);
351 if(strcmp(tag
,"TAG"))
352 demuxer
->movi_end
= s
->end_pos
;
356 demuxer
->movi_end
= stream_tell(s
)-3;
357 stream_read(s
,buf
,30);
359 demux_info_add(demuxer
,"Title",buf
);
360 stream_read(s
,buf
,30);
362 demux_info_add(demuxer
,"Artist",buf
);
363 stream_read(s
,buf
,30);
365 demux_info_add(demuxer
,"Album",buf
);
366 stream_read(s
,buf
,4);
368 demux_info_add(demuxer
,"Year",buf
);
369 stream_read(s
,buf
,30);
371 demux_info_add(demuxer
,"Comment",buf
);
372 if(buf
[28] == 0 && buf
[29] != 0) {
373 uint8_t trk
= (uint8_t)buf
[29];
374 sprintf(buf
,"%d",trk
);
375 demux_info_add(demuxer
,"Track",buf
);
377 g
= stream_read_char(s
);
378 demux_info_add(demuxer
,"Genre",genres
[g
]);
383 unsigned int chunk_type
;
384 unsigned int chunk_size
;
387 l
= stream_read_dword_le(s
);
389 mp_msg(MSGT_DEMUX
,MSGL_ERR
,"[demux_audio] Bad wav header length: too short (%d)!!!\n",l
);
392 if(l
> MAX_WAVHDR_LEN
) {
393 mp_msg(MSGT_DEMUX
,MSGL_ERR
,"[demux_audio] Bad wav header length: too long (%d)!!!\n",l
);
396 sh_audio
->wf
= w
= malloc(l
> sizeof(*w
) ? l
: sizeof(*w
));
397 w
->wFormatTag
= sh_audio
->format
= stream_read_word_le(s
);
398 w
->nChannels
= sh_audio
->channels
= stream_read_word_le(s
);
399 w
->nSamplesPerSec
= sh_audio
->samplerate
= stream_read_dword_le(s
);
400 w
->nAvgBytesPerSec
= stream_read_dword_le(s
);
401 w
->nBlockAlign
= stream_read_word_le(s
);
402 w
->wBitsPerSample
= stream_read_word_le(s
);
403 sh_audio
->samplesize
= (w
->wBitsPerSample
+ 7) / 8;
405 sh_audio
->i_bps
= sh_audio
->wf
->nAvgBytesPerSec
;
408 w
->cbSize
= stream_read_word_le(s
);
411 mp_msg(MSGT_DEMUX
,MSGL_ERR
,"[demux_audio] truncated extradata (%d < %d)\n",
415 stream_read(s
,(char*)(w
+ 1),w
->cbSize
);
417 if (w
->wFormatTag
== 0xfffe && w
->cbSize
>= 22)
418 sh_audio
->format
= le2me_16(((WAVEFORMATEXTENSIBLE
*)w
)->SubFormat
);
421 if( mp_msg_test(MSGT_DEMUX
,MSGL_V
) ) print_wave_header(w
, MSGL_V
);
426 chunk_type
= stream_read_fourcc(demuxer
->stream
);
427 chunk_size
= stream_read_dword_le(demuxer
->stream
);
428 if (chunk_type
!= mmioFOURCC('d', 'a', 't', 'a'))
429 stream_skip(demuxer
->stream
, chunk_size
);
430 } while (!s
->eof
&& chunk_type
!= mmioFOURCC('d', 'a', 't', 'a'));
431 demuxer
->movi_start
= stream_tell(s
);
432 demuxer
->movi_end
= chunk_size
? demuxer
->movi_start
+ chunk_size
: s
->end_pos
;
433 // printf("wav: %X .. %X\n",(int)demuxer->movi_start,(int)demuxer->movi_end);
434 // Check if it contains dts audio
435 if((w
->wFormatTag
== 0x01) && (w
->nChannels
== 2) && (w
->nSamplesPerSec
== 44100)) {
436 unsigned char buf
[16384]; // vlc uses 16384*4 (4 dts frames)
438 memset(buf
, 0, sizeof(buf
));
439 stream_read(s
, buf
, sizeof(buf
));
440 for (i
= 0; i
< sizeof(buf
) - 5; i
+= 2) {
442 if((buf
[i
] == 0xff) && (buf
[i
+1] == 0x1f) && (buf
[i
+2] == 0x00) &&
443 (buf
[i
+3] == 0xe8) && ((buf
[i
+4] & 0xfe) == 0xf0) && (buf
[i
+5] == 0x07)) {
444 sh_audio
->format
= 0x2001;
445 mp_msg(MSGT_DEMUX
,MSGL_V
,"[demux_audio] DTS audio in wav, 14 bit, LE\n");
449 if((buf
[i
] == 0x1f) && (buf
[i
+1] == 0xff) && (buf
[i
+2] == 0xe8) &&
450 (buf
[i
+3] == 0x00) && (buf
[i
+4] == 0x07) && ((buf
[i
+5] & 0xfe) == 0xf0)) {
451 sh_audio
->format
= 0x2001;
452 mp_msg(MSGT_DEMUX
,MSGL_V
,"[demux_audio] DTS audio in wav, 14 bit, BE\n");
456 if((buf
[i
] == 0x7f) && (buf
[i
+1] == 0xfe) && (buf
[i
+2] == 0x80) &&
457 (buf
[i
+3] == 0x01)) {
458 sh_audio
->format
= 0x2001;
459 mp_msg(MSGT_DEMUX
,MSGL_V
,"[demux_audio] DTS audio in wav, 16 bit, BE\n");
463 if((buf
[i
] == 0xfe) && (buf
[i
+1] == 0x7f) && (buf
[i
+2] == 0x01) &&
464 (buf
[i
+3] == 0x80)) {
465 sh_audio
->format
= 0x2001;
466 mp_msg(MSGT_DEMUX
,MSGL_V
,"[demux_audio] DTS audio in wav, 16 bit, LE\n");
470 if (sh_audio
->format
== 0x2001) {
471 sh_audio
->needs_parsing
= 1;
472 mp_msg(MSGT_DEMUX
,MSGL_DBG2
,"[demux_audio] DTS sync offset = %u\n", i
);
476 stream_seek(s
,demuxer
->movi_start
);
479 sh_audio
->format
= mmioFOURCC('f', 'L', 'a', 'C');
480 demuxer
->movi_start
= stream_tell(s
) - 4;
481 demuxer
->movi_end
= s
->end_pos
;
482 if (demuxer
->movi_end
> demuxer
->movi_start
) {
483 // try to find out approx. bitrate
484 int64_t size
= demuxer
->movi_end
- demuxer
->movi_start
;
488 srate
= stream_read_int24(s
) >> 4;
489 num_samples
= stream_read_int24(s
) << 16;
490 num_samples
|= stream_read_word(s
);
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 sh_audio
->needs_parsing
= 1;
497 // get_flac_metadata (demuxer);
501 priv
= malloc(sizeof(da_priv_t
));
504 demuxer
->priv
= priv
;
505 demuxer
->audio
->id
= 0;
506 demuxer
->audio
->sh
= sh_audio
;
507 sh_audio
->ds
= demuxer
->audio
;
508 sh_audio
->samplerate
= sh_audio
->audio
.dwRate
;
510 if(stream_tell(s
) != demuxer
->movi_start
)
512 mp_msg(MSGT_DEMUX
, MSGL_V
, "demux_audio: seeking from 0x%X to start pos 0x%X\n",
513 (int)stream_tell(s
), (int)demuxer
->movi_start
);
514 stream_seek(s
,demuxer
->movi_start
);
515 if (stream_tell(s
) != demuxer
->movi_start
) {
516 mp_msg(MSGT_DEMUX
, MSGL_V
, "demux_audio: seeking failed, now at 0x%X!\n",
517 (int)stream_tell(s
));
518 if (next_frame_pos
) {
519 mp_msg(MSGT_DEMUX
, MSGL_V
, "demux_audio: seeking to 0x%X instead\n",
520 (int)next_frame_pos
);
521 stream_seek(s
, next_frame_pos
);
526 mp_msg(MSGT_DEMUX
,MSGL_V
,"demux_audio: audio data 0x%X - 0x%X \n",(int)demuxer
->movi_start
,(int)demuxer
->movi_end
);
528 return DEMUXER_TYPE_AUDIO
;
532 static int demux_audio_fill_buffer(demuxer_t
*demux
, demux_stream_t
*ds
) {
535 sh_audio_t
* sh_audio
= ds
->sh
;
536 da_priv_t
* priv
= demux
->priv
;
537 double this_pts
= priv
->next_pts
;
538 stream_t
* s
= demux
->stream
;
547 stream_read(s
,hdr
,4);
550 l
= mp_decode_mp3_header(hdr
);
552 if (demux
->movi_end
&& stream_tell(s
) >= demux
->movi_end
)
553 return 0; // might be ID3 tag, i.e. EOF
556 dp
= new_demux_packet(l
);
557 memcpy(dp
->buffer
,hdr
,4);
558 if (stream_read(s
,dp
->buffer
+ 4,l
-4) != l
-4)
560 free_demux_packet(dp
);
563 priv
->next_pts
+= sh_audio
->audio
.dwScale
/(double)sh_audio
->samplerate
;
568 unsigned align
= sh_audio
->wf
->nBlockAlign
;
569 l
= sh_audio
->wf
->nAvgBytesPerSec
;
570 if (l
<= 0) l
= 65536;
571 if (demux
->movi_end
&& l
> demux
->movi_end
- stream_tell(s
)) {
572 // do not read beyond end, there might be junk after data chunk
573 l
= demux
->movi_end
- stream_tell(s
);
574 if (l
<= 0) return 0;
577 l
= (l
+ align
- 1) / align
* align
;
578 dp
= new_demux_packet(l
);
579 l
= stream_read(s
,dp
->buffer
,l
);
580 priv
->next_pts
+= l
/(double)sh_audio
->i_bps
;
585 dp
= new_demux_packet(l
);
586 l
= stream_read(s
,dp
->buffer
,l
);
587 priv
->next_pts
= MP_NOPTS_VALUE
;
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 struct MPOpts
*opts
= demuxer
->opts
;
623 sh_audio_t
* sh_audio
;
629 if(!(sh_audio
= demuxer
->audio
->sh
))
632 priv
= demuxer
->priv
;
634 if(priv
->frmt
== MP3
&& opts
->hr_mp3_seek
&& !(flags
& SEEK_FACTOR
)) {
635 len
= (flags
& SEEK_ABSOLUTE
) ? rel_seek_secs
- priv
->next_pts
: rel_seek_secs
;
637 stream_seek(s
,demuxer
->movi_start
);
638 len
= priv
->next_pts
+ len
;
642 high_res_mp3_seek(demuxer
,len
);
646 base
= flags
&SEEK_ABSOLUTE
? demuxer
->movi_start
: stream_tell(s
);
647 if(flags
&SEEK_FACTOR
)
648 pos
= base
+ ((demuxer
->movi_end
- demuxer
->movi_start
)*rel_seek_secs
);
650 pos
= base
+ (rel_seek_secs
*sh_audio
->i_bps
);
652 if(demuxer
->movi_end
&& pos
>= demuxer
->movi_end
) {
653 pos
= demuxer
->movi_end
;
654 } else if(pos
< demuxer
->movi_start
)
655 pos
= demuxer
->movi_start
;
657 priv
->next_pts
= (pos
-demuxer
->movi_start
)/(double)sh_audio
->i_bps
;
661 pos
-= (pos
- demuxer
->movi_start
) %
662 (sh_audio
->wf
->nBlockAlign
? sh_audio
->wf
->nBlockAlign
:
663 (sh_audio
->channels
* sh_audio
->samplesize
));
670 static void demux_close_audio(demuxer_t
* demuxer
) {
671 da_priv_t
* priv
= demuxer
->priv
;
676 static int demux_audio_control(demuxer_t
*demuxer
,int cmd
, void *arg
){
677 sh_audio_t
*sh_audio
=demuxer
->audio
->sh
;
678 int audio_length
= sh_audio
->i_bps
? demuxer
->movi_end
/ sh_audio
->i_bps
: 0;
679 da_priv_t
* priv
= demuxer
->priv
;
682 case DEMUXER_CTRL_GET_TIME_LENGTH
:
683 if (audio_length
<=0) return DEMUXER_CTRL_DONTKNOW
;
684 *((double *)arg
)=(double)audio_length
;
685 return DEMUXER_CTRL_GUESS
;
687 case DEMUXER_CTRL_GET_PERCENT_POS
:
689 return DEMUXER_CTRL_DONTKNOW
;
690 *((int *)arg
)=(int)( (priv
->next_pts
*100) / audio_length
);
691 return DEMUXER_CTRL_OK
;
694 return DEMUXER_CTRL_NOTIMPL
;
699 const demuxer_desc_t demuxer_desc_audio
= {
706 0, // unsafe autodetect
708 demux_audio_fill_buffer
,