19 Jun '13 - 21:19 *
Welcome, Guest. Please login or register.
Did you miss your activation email?

Login with username, password and session length
 
   Home   Help Search Login Register  
Pages: [1]
  Reply  |  Print  
Author Topic: iOS m4a streamCreateFileUser problems  (Read 1071 times)
kakosquid
Posts: 4


« on: 7 Nov '11 - 15:48 »
Reply with quoteQuote

Hi Ian, 

First of all, great work with the iOS port, I love using BASS library.
I'm coding an app which plays streams of raw data and am pretty much finished with the code.
Everything works perfectly well with mp3 files, but m4a files just won't budge. What I'm doing is creating a stream of length 0
so that data is read without stopping

BASS_StreamCreateFileUser(STREAMFILE_BUFFER, BASS_STREAM_RESTRATE, &bgFileProcs, NULL)

and then feeding the fileReadProc with raw bytes which I have stored in NSMutableData.
I've seen a previous post where there is a claim that this should work without add ons.
Can you give me a hint to what this problem with playing m4a files could be about?

Thanks in advance!
Logged
Ian @ un4seen
Administrator
Posts: 15366


« Reply #1 on: 7 Nov '11 - 17:38 »
Reply with quoteQuote

Unfortunately, the CoreAudio codec support doesn't currently include support for the buffered file system (STREAMFILE_BUFFER). Can you use the unbuffered system (STREAMFILE_NOBUFFER) instead?
Logged
kakosquid
Posts: 4


« Reply #2 on: 7 Nov '11 - 18:51 »
Reply with quoteQuote

Unfortunately no, the demands for this app are pretty preposterous,
you would be amazed Smiley
Okay then, I will have to think of some workaround for this.

Thanks for the prompt answer!
Logged
Ian @ un4seen
Administrator
Posts: 15366


« Reply #3 on: 8 Nov '11 - 14:57 »
Reply with quoteQuote

Is the reason that you need a buffered stream because there is latency involved in the file data being available, eg. it's arriving from a remote source?

Perhaps it would be possible for you to implement a buffered system by making the unbuffered file stream a "decoding channel" (via the BASS_STREAM_DECODE flag) and using a push stream to play the decoded data. Normally, an unbuffered file stream's FILEREADPROC function shouldn't wait for data, but if it's a decoding channel that is being processed separately (eg. in its own thread), then delays don't really matter as they won't affect anything else. You could have a thread that just requests data from the decoding channel (via BASS_ChannelGetData) and feeds it to the push stream (via BASS_StreamPutData), something like this...

while (1) {
BYTE buf[10000];
DWORD c=BASS_ChannelGetData(decoder, buf, sizeof(buf)); // request data from the decoder
if (c==-1) break; // error, eg. EOF or freed
c=BASS_StreamPutData(output, buf, c); // feed the data to the push stream
if (c) usleep(100000); // queued data means the playback buffer is full, so wait a bit (100ms)
}

The "output" stream would need to be created with the same sample format as the decoder, something like this...

BASS_CHANNELINFO ci;
BASS_ChannelGetInfo(decoder, &ci); // get decoder sample format info
output=BASS_StreamCreate(ci.freq, ci.chans, ci.flags&(BASS_SAMPLE_FLOAT|BASS_SAMPLE_8BITS), STREAMPROC_PUSH, 0); // create push stream with same format

Regarding the file callback functions (BASS_FILEPROCS) for the "decoder" stream... For a stream of indeterminate length, your FILELENPROC function could just return a very large number, eg. LLONG_MAX. As mentioned above, your FILEREADPROC function would wait for the requested amount of data to arrive. You may also be required to do a bit of seeking via your FILESEEKPROC function, which can be done be reading more data if the position is ahead of the current position, or go back in your file buffer if it's behind. Note you may be requested to seek backwards, particularly when within the BASS_StreamCreateFileUser call (it will probably want to seek back to the start), so you will need to keep old data buffered for that. I would suggest keeping all file data buffered until the BASS_StreamCreateFileUser call returns. Depending on the file format, you may also need to keep a bit of old data buffered for backward seek requests after that too, eg. the ADTS frame parsing seems to involve seeking forward a bit and then back to read the frame.
Logged
kakosquid
Posts: 4


« Reply #4 on: 11 Nov '11 - 11:12 »
Reply with quoteQuote

Thank you very much Ian!

I will try this and report back if it is the proper solution for my problem.
Logged
kakosquid
Posts: 4


« Reply #5 on: 14 Nov '11 - 11:55 »
Reply with quoteQuote

Okay, I've tried what you suggested, I started a decoder channel

_decoder = BASS_StreamCreateFileUser(STREAMFILE_NOBUFFER, BASS_STREAM_DECODE, &decoderFileProcs, NULL)

and a playing channel

_channel = BASS_StreamCreate(ci.freq, ci.chans, ci.flags&(BASS_SAMPLE_FLOAT|BASS_SAMPLE_8BITS), STREAMPROC_PUSH, 0)

and then I am feeding the playing channel in a separate thread

 while (1)
    {
        BYTE buf[10000];
        DWORD c = BASS_ChannelGetData(_decoder, buf, sizeof(buf)); // request data from the decoder
        if (c==-1) break; // error, eg. EOF or freed
        c = BASS_StreamPutData(_channel, buf, c); // feed the data to the push stream
        if (c) usleep(100000); // queued data means the playback buffer is full, so wait a bit (100ms)
    }

but it still works just for mp3 files.
If I try mp4 aac files or even lpcm, it just returns BASS_ERROR_FILEFORM.
I thought at least LPCM should work but it doesn't :/

Do you have any other ideas?
I really don't want to ditch BASS for playing filetypes other than mp3...

Also, what I've tried and doesn't work with mp4 files

NSString *fullPath = [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:fileName];
    NSData *data = [[NSData alloc] initWithContentsOfFile:fullPath];
NSData *subData = [data subdataWithRange:NSMakeRange(0, data.length / 2)];
    if(!(_channel=BASS_StreamCreateFile(TRUE,[subData bytes],0,subData.length,BASS_SAMPLE_LOOP|BASS_SAMPLE_FLOAT)))

but it works perfect when the data provided is whole,


if(!(_channel=BASS_StreamCreateFile(TRUE,[data bytes],0,data.length,BASS_SAMPLE_LOOP|BASS_SAMPLE_FLOAT)))

So basically, it comes to having to provide the whole file data in order to play the mp4 file,
which is not acceptable for my app. Imagine loading a 280 minute podcast here Smiley
« Last Edit: 14 Nov '11 - 12:24 by kakosquid » Logged
Ian @ un4seen
Administrator
Posts: 15366


« Reply #6 on: 14 Nov '11 - 16:01 »
Reply with quoteQuote

To get some information on what is happening, please log and post the file callback requests, eg. the FILEREADPROC/FILESEEKPROC parameters. Also, are the callbacks satisfying the requests? From my tests of using this method, there may be some seek requests a long way ahead during the BASS_StreamCreateFileUser call (ie. during stream creation) that it's safe to fail, ie. return FALSE in the FILESEEKPROC.
Logged
Pages: [1]
  Reply  |  Print  
 
Jump to:  

Powered by SMF 1.1.18 | SMF © 2013, Simple Machines