Skip to content

FreeStreamer FAQ

Matias Muhonen edited this page Aug 12, 2016 · 55 revisions

Is it possible to control volume programmatically?

As far as I know, controlling the device volume is not fully available for iOS application developers (probably for safety reasons, think about the possible hearing damage by programming errors).

So you can either stick to MPVolumeView and let the user control the volume, or, you can directly set the volume using the following method in FSAudioStream / FSAudioController:

- (void)setVolume:(float)volume;

The volume parameter is between 0.0 to 1.0. Note that with this approach, though, the maximum volume is constrained by the system volume.

Is it possible to customize the stream buffer sizes?

Yes. Use FSStreamConfiguration and use initWithConfiguration to create the stream:

FSStreamConfiguration *config = [[FSStreamConfiguration alloc] init];
config.httpConnectionBufferSize /= 2;
    
FSAudioStream *stream = [[FSAudioStream alloc] initWithConfiguration:config];

I get a build error when installed from CocoaPods

This can be fixed by the following steps:

  1. Go to the Build Settings of pods
  2. Change the target to Pods-FreeStreamer (in the top left corner)
  3. Check these two parameters:
C++ Language Dialect
C++ Standard Library

The both parameters need to match:

C++ Language Dialect = GNU++11 ...
C++ Standard Library = libstdc++ (GNU C++ ...

What does strict content-type checking means?

With strict content-type checking, if the server responds a non-audio type as the MIME type of the stream, the stream won't play. But if the strict content-type checking is disabled, FreeStreamer will try to play the stream regardless of the MIME type.

You can change the default behavior by supplying your own stream configuration and changing the requireStrictContentTypeChecking property. It is also possible to change the property during runtime with the FSAudioStream property strictContentTypeChecking.

How can I record a stream?

Set the FSAudioStream.outputFile property and the stream is stored to the provided location. In this way the stored audio is the original compressed audio. The following code should work on iOS:

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = paths[0];
NSString *fileName = [documentsDirectory stringByAppendingPathComponent:@"test.mp3"];
NSURL *url = [NSURL fileURLWithPath:fileName];

stream.outputFile = url;

The other possibility is to access the PCM audio samples via FSPCMAudioStreamDelegate protocol. There is the FSPCMAudioSampleCapturer delegate which handles the logic.

How can I seek a stream?

Please take a look at the example project: https://github.com/muhku/FreeStreamer/blob/master/FreeStreamerMobile/FSPlayerViewController.m#L511

Basically you need to call the seekToPosition method of audio stream.

Seeking is not supported for all types of files. Also, the server needs to support content range requests. As an example file for seeking, you may try the one used in the unit tests:

https://dl.dropboxusercontent.com/u/995250/FreeStreamer/As%20long%20as%20the%20stars%20shine.mp3

As a further notice, seeking is always supported for locally stored files (using the File_Stream input stream).

Can I change the default user agent?

Yes. Just supply the configuration value.

FSStreamConfiguration *config = [[FSStreamConfiguration alloc] init];
config.userAgent = @"MyUserAgent";

FSAudioStream *stream = [[FSAudioStream alloc] initWithConfiguration:config];

How to start playback from specific position?

Let's say you are streaming a file and want to resume the playback from the middle of the file.

For this, you need to store the current seek byte offset before the stream gets stopped (it must be in the playing state). See:

http://muhku.github.io/FreeStreamer/api/Classes/FSAudioStream.html#//api/name/currentSeekByteOffset

Then, you start the stream playback with the playFromOffset method by providing the offset that was stored: http://muhku.github.io/FreeStreamer/api/Classes/FSAudioStream.html#//api/name/playFromOffset:

Is there a way to play a stream at a faster rate than 1x?

Yes. See setPlayRate in FSAudioStream. Notice that you may have to increase the buffer sizes accordingly using FSStreamConfiguration.

Please notice that the stream must be in the "PLAYING" state for setPlayRate to have effect.

How does the stream buffering / preloading works?

If the stream receives more data from the network than is currently needed for playback, it is cached. The maximum number of cached bytes is determined by the maxPrebufferedByteCount property in the FSStreamConfiguration class:

http://muhku.github.io/FreeStreamer/api/Classes/FSStreamConfiguration.html#//api/name/maxPrebufferedByteCount

By default, 1MB is cached.

To get the preload progress (how many bytes are loaded), use the prebufferedByteCount property in FSAudioStream:

http://muhku.github.io/FreeStreamer/api/Classes/FSAudioStream.html#//api/name/prebufferedByteCount

How can I play local files?

You need to provide a local file URL. For example, if you have a file named test.mp3 located in the application's main bundle, the following should work:

NSBundle* myBundle = [NSBundle mainBundle];
NSString* myAudioFile = [myBundle pathForResource:@"test" ofType:@"mp3"];
NSURL* url = [NSURL fileURLWithPath:myAudioFile];

self.audioController.url = url;

Can I add custom playback sources besides HTTP and local files?

Yes. See the Input_Stream class. You need to implement your custom Input_Stream by implementing all the virtual methods. For the simplest implementation, see the File_Stream class.

To plug the custom Input_Stream in, see the setUrl() function of the Audio_Stream class. You don't need to touch any other part of the streamer. Using a custom URL scheme for your input stream may be a good idea.

Is it safe to use the streamer from multiple threads?

The short answer is no. The streamer hasn't been designed to be thread-safe. By adding synchronization to the code by default all the methods had to be synchronized. This would slower the performance for the users, who are not calling the streamer from multiple threads. The recommended usage is to use the streamer from the main thread and from the other threads, use something like performSelectorOnMainThread.

The streamer itself is designed to be asynchronous so by using some threading scheme there shouldn't be an advantage as the I/O doesn't really block. But, if you really want to do this, you could wrap the streamer in another class which does the synchronization before calling the streamer's own methods.

I was streaming a long podcast and the playback was interrupted by a network error. What happened?

Usually HTTP servers have a configurable timeout for the maximum duration of HTTP connections. If the timeout is reached, then the server will reply with a "connection reset by peer" message. When the streamer is streaming a long file, it could be requesting data from the server for the whole duration of the file, which means that from the server's point of view, the connection could be open for an hour. That is not accepted in some server configurations.

There are some ways to mitigate the problem:

  • If you have access to the HTTP server configuration, it could be possible to increase the timeouts.
  • It is possible to increase the local in-memory cache for the streamer. This can play time, in a sense. If the file fits in the local cache, the connection to the server is closed sooner and the problem disappears. The disadvantage is that for big files, this may not be feasible (think about allocating, say 20MB of cache memory). But it is possible to experiment with the local cache sizes (see the maxPrebufferedByteCount property). By default FreeStreamer caches a maximum of 1MB of data. This works reasonably well for small files.
  • Download the files before starting to stream and then stream them as a local file.

Notice that since release v3.5.2, there is an automatic recovery added for non-continuous HTTP connections, which can transparently reopen the HTTP connection so streaming can continue without an interruption.

Is it possible to cache the streamed files to disk?

Yes. Caching is supported for non-continuous streams. Caching is controlled by the following properties in FSAudioStream:

  • cacheDirectory - This is automatically set to the user's document directory. This is where the cached content is stored.
  • cacheEnabled - Set this to YES to enable caching. Caching is enabled by default.
  • maxDiskCacheSize - Set this to control the maximum cache size. The default is 100MB.

I cannot stream files with a very short duration. How can I fix it?

No changes should be required any more. Fixed in Issue #132

I'm confused with all these buffering properties. How does it work?

FreeStreamer buffers compressed audio packets to the memory in order to avoid playback glitches in bad network conditions.

maxPrebufferedByteCount - determines the maximum avoid of cached data in memory requiredInitialPrebufferedByteCountForContinuousStream - determines how much audio data there must be in order the playback to start for continuous streams requiredInitialPrebufferedByteCountForNonContinuousStream - the same as the previous but non-continuous streams

The requiredInitialPrebufferedByteCountForContinuousStream and requiredInitialPrebufferedByteCountForNonContinuousStream may cause slight details to the playback start in slow networks. If you want to avoid that, you can set the both properties to 0 and no prebuffering will be required. The audio will start to play when there is a minimal amount of audio packets required by the decode queue size property.

How to add custom HTTP headers to the request when streaming?

Use the configuration key predefinedHttpHeaderValues. Example:

    FSStreamConfiguration *config = [[FSStreamConfiguration alloc] init];
    
    config.predefinedHttpHeaderValues = @{@"header1" : @"value1",
                                          @"header2" : @"value2"};
    
    stream = [[FSAudioStream alloc] initWithConfiguration:config];

    [stream playFromURL:[NSURL URLWithString:@"http://www.example.com/"]];

This could be useful for OAuth authenticated streams, for instance. Please notice that any predefined HTTP headers override the existing HTTP headers, so pay attention to the header values set!

How do I preload a stream?

First call preload for the stream you want to preload. Calling play after that will start the stream playback.

Notice that FSAudioController does preloading automatically for the next playlist items, unless you disable it: http://muhku.github.io/FreeStreamer/api/Classes/FSAudioController.html#//api/name/preloadNextPlaylistItemAutomatically

Can I measure the quality of streaming?

Yes. Enable the DO_STATKEEPING define in the demo project: https://github.com/muhku/FreeStreamer/blob/master/FreeStreamerMobile/FreeStreamerMobile/FSPlayerViewController.m#L27

Logs will be produced and you can observe the streaming statistics on the go.