Tips, Tricks and Secrets of the Android Multimedia APIs - Tistory

33 downloads 165 Views 181KB Size Report
Feb 27, 2013 - Helps apps cooperate with audio output when trying to play .... Call release() when done with a MediaPlay
Tips, Tricks and Secrets of the Android Multimedia APIs Doug Stevenson Wednesday, February 27, 2013 Slides gzip: http://goo.gl/pBXS5 Code Samples: http://goo.gl/XYTJu

Overview of Core Android Audio Capabilities Android prescribes native support for: ●

Audio encoding and decoding



Video encoding and decoding



Image encoding and decoding



Streaming audio and video

http://developer.android.com/guide/appendix/media-formats.html 2

Audio Support Decoding: ●

AAC, AMR-WB, AMR-NB, MP3, MIDI, Vorbis, WAVE



Since Android 3.1: FLAC



Since Android 4.1: AAC ELD

Encoding: ●

AAC LC, AMR-WB, AMR-NB



Since Android 4.1: HE-AACv1, AAC-ELD, WAVE

Containers: ●

3GPP, MPEG-4, MP3, FLAC, OGG, WAVE, MPEG-TS (3.0+)

3

Video Support Decoding ●

H.263, H.264 (baseline profile), MPEG-4 SP



Since Android 2.3.3: VP8

Encoding ●

H.263



Since Android 3.0: H.264 (baseline profile)

Containers: ●

3GPP, MPEG-4, WebM, MPEG-TS (3.0+), Matroska (4.0+) 4

Streaming Support ●

RTSP



HTTP progressive



Since Android 3.0: HTTP live (draft)



Since Android 3.1: HTTPS

5

Notable Exceptions Samsung devices have ●

DivX



WMV



AVI



FLV

Recommendation: Don’t count on these 6

AudioManager System Service Provides access to top level audio functions ●

Volume



Routing



Focus

System Service instance: AudioManager am = (AudioManager) Context.getSystemService(Context.AUDIO_SERVICE); 7

AudioManager: System Audio Streams Audio streams types are identified by AudioManager.STREAM_* integer constants: ● STREAM_ALARM ● STREAM_DTMF ● STREAM_MUSIC ● STREAM_NOTIFICATION ● STREAM_RING ● STREAM_VOICE_CALL 8

AudioManager: Get Stream Volume ●

Minimum volume for any stream is 0.



Maximum volume for a stream: –



AudioManager.getStreamMaxVolume(stream_type)

Current volume for a stream is: –

AudioManager.getStreamVolume(stream_type)

9

AudioManager: Set Stream Volume ●



Adjust volume of the most relevant stream –

AudioManager.adjustVolume(direction, flags)



Directions: ADJUST_LOWER, ADJUST_RAISE, ADJUST_SAME

Adjust volume of a specific stream –



Adjust volume of the most relevant stream OR the given fallback –



AudioManager.adjustStreamVolume(stream_type, direction, flags) AudioManager.adjustSuggestedVolume(direction, stream_type, flags)

Directly set the given stream’s volume –

AudioManager.setStreamVolume(stream_type, volume)

10

AudioManager: Stream Volume Flags FLAG_ALLOW_RINGER_MODES Allows system to switch into vibrate mode when attempting to drop ringer volume below 0 FLAG_PLAY_SOUND Allows system to play a sound when changing volume FLAG_REMOVE_SOUND_AND_VIBRATE Removes queued feedback for volume changes FLAG_SHOW_UI Shows system UI for volume adjustment (toast) FLAG_VIBRATE Allows vibrate if going into vibrate ringer mode

11

AudioManager: Audio Mode Audio Mode is the current high-level function of device audio. int AudioManager.getMode() MODE_NORMAL Not in a call MODE_RINGTONE Phone is ringing MODE_IN_CALL Currently in a call MODE_IN_COMMUNICATION Currently in a VOIP session

12

AudioManager: Audio Focus ●

Helps apps cooperate with audio output when trying to play simultaneously



First available with API 8, Froyo



Apps can indicate different kinds of audio focus –

Long term audio (e.g. music, video, games)



Temporary audio (e.g. notifications, feedback, driving directions)

13

AudioManager: Audio Focus API int AudioManager.requestAudioFocus(listener, stream_type, duration_hint) int AudioManager.abandonAudioFocus(listener)

Both return one of: ● AUDIOFOCUS_REQUEST_GRANTED ●

AUDIOFOCUS_REQUEST_FAILED.

14

AudioManager: Audio Focus Duration Hint duration_hint options AUDIOFOCUS_GAIN Long term focus request; e.g. music, video AUDIOFOCUS_GAIN_TRANSIENT Temporary focus request; e.g. notifications, driving directions AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK Like AUDIOFOCUS_GAIN_TRANSIENT, except current focus holder may keep playing if it “ducks” its volume 15

AudioManager: Audio Focus Change Listener Interface AudioManager.OnAudioFocusChangeListener class FocusListener implements AudioManager.OnAudioFocusChangeListener { public void onAudioFocusChange(int focusChange) { // Act on gaining/losing focus } }

The listener you provide to requestAudioFocus will get called with focus change updates as they happen. 16

AudioManager: Audio Focus Change Values AUDIOFOCUS_GAIN You are gaining focus when duration hint was AUDIOFOCUS_GAIN AUDIOFOCUS_LOSS ●



Indicates loss of focus for unknown duration

You should stop playing AUDIOFOCUS_LOSS_TRANSIENT ●



Indicates temporary loss of focus



Focus should be regained again soon



Resume playback when you get AUDIOFOCUS_GAIN again

17

AudioManager: Audio Focus Change Values AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK ●

Temporary loss of focus to another app using duration hint AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK



You may continue to play if you duck volume



You should gain focus again soon (and restore volume)



Consider restoring volume gently

18

AudioManager: “Becoming Noisy” What happens to audio after the headset is disconnected? Register a BroadcastReceiver for the action ACTION_AUDIO_BECOMING_NOISY context.registerReceiver( receiver, new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY) );

19

SoundPool Maintains a simple pool of sounds in memory for low latency playback. Good for: ●

Simple games that play a variety of sounds



Soundboard style apps

20

SoundPool Features ●

Sounds may be loaded from raw resources, assets, or local files



Sounds may be played, paused/resumed, stopped at any time



Individual sounds may be played simultaneously



Looping



Adjustment of playback rate (time and pitch)



Adjustment of volume

21

SoundPool Tips Good to know: ●

Prior to Android 2.2 (API level 8), there was no good way to know when a sound is done loading. –



Now there is OnLoadCompleteListener

Suggest using OGG/Vorbis encoding for samples and using assets to bundle them in your APK.

22

SoundPool Demo See ActivitySoundPool.java

23

MediaPlayer Plays both audio and video files of types supported by the device. Can play media from: ●

Local files (only using FileDescriptor)



Streaming HTTP and RTSP servers (with buffering)



Content URIs

24

MediaPlayer State Machine ●

Strict state machine that must be followed



Wrong call sequence? Throws unchecked exceptions. –



Don't catch them; fix your app

Diagram is in the javadoc

25

MediaPlayer Video Playback For video playback, one of the following is required: ●



a SurfaceHolder obtained from a SurfaceView in your view hierarchy a Surface, usually from a TextureView’s SurfaceTexture (with Android 4.0 or later)

Android VideoView source code is a good place to start to understand how MediaPlayer can show video. –

frameworks/base/core/java/android/widget/VideoView.java 26

MediaPlayer Tips ●





Calls documented as asynchronous may still have observed delays (e.g. streaming media) Consider moving all MediaPlayer methods off the main thread (HandlerThread is handy, queues and serializes work sent to it) MediaPlayer in conjunction with a Service can keep music playback alive in the background.

27

MediaPlayer features in Gingerbread (API 9) Attach audio effects ● ●

getAudioSessionId() / setAudioSessionId() Create an effect using session id (android.media.audiofx)



attachAuxEffect(int effect_id)



setAuxEffectSendLevel(float level)

28

MediaPlayer features in Jellybean (API 16) Select from multiple audio and video tracks (e.g. multiple languages and camera angles) ●

getTrackInfo() to iterate tracks



selectTrack()

Display timed text (subtitles) (SubRip only) ●

addTimedTextSource() to make the text tracks available



getTrackInfo() / selectTrack()



setOnTimedTrackListener()



deselectTrack()

29

MediaPlayer features in Jellybean (API 16) Video scaling modes ● ● ●

setVideoScalingMode(int mode) VIDEO_SCALING_MODE_SCALE_TO_FIT VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING

Seamless transition between sources ●

setNextMediaPlayer() to queue the next MediaPlayer object



Supposed to be seamlessly transitioned from the current



May not work as seamlessly as expected 30

MediaPlayer: Memory Management Call release() when done with a MediaPlayer instance to free backing native resources.

31

MediaRecorder Records audio using recording hardware (microphone) ●

Has a state machine similar to MediaPlayer



Does not work on the emulator



Requires permission android.permission.RECORD_AUDIO –



Implicit feature: android.hardware.microphone

Not all devices have a mic

32

MediaRecorder: Choosing Audio Source Choose audio source using setAudioSource() with a constant from MediaRecorder.AudioSource ● DEFAULT ● MIC ● CAMCORDER ● VOICE_UPLINK, VOICE_DOWNLINK, VOICE_CALL





http://code.google.com/p/android/issues/detail?id=4075



http://code.google.com/p/android/issues/detail?id=2117

VOICE_RECOGNITION, VOICE_COMMUNICATION (API 11) 33

MediaRecorder: Choosing Output Container Choose output container format using setOutputFormat() and a constant from MediaRecorder.OutputFormat ● DEFAULT ● THREE_GPP ● RAW_AMR ● MPEG_4 ●

AMR_WB (API level 10, GB)



AMR_NB (API level 10, GB)



AAC_ADTS (API level 16, JB)

34

MediaRecorder: Choosing Output Codec Choose output audio format using setAudioEncoder() and a constant from MediaRecorder.AudioEncoder ● DEFAULT ● AMR_NB ●

AMR_WB (API level 10, GB)



AAC (API level 10, GB)



AAC_ELD (API level 16, JB)



HE_AAC (API level 16, JB) 35

MediaRecorder: Memory Management Call release() when done with a MediaPlayer instance to free backing native resources.

36

MediaPlayer/MediaRecorder Demo See ●

ActivityMediaPlayerAudio.java



ActivityMediaPlayerVideo.java



ActivityMediaRecorderAudio.java

37

AudioTrack ●

Low level audio playback mechanism



Raw, uncompressed PCM only



Good if doing digital signal processing

Remember: MediaPlayer → compressed AudioTrack → raw 38

AudioTrack Modes ●



Static: for loading an entire sound into memory for low latency playback (similar to SoundPool) Streaming: for writing a continuous stream of audio that may be too big to fit in memory

39

AudioTrack Constructor new AudioTrack( int streamType, int sampleRateInHz, int channelConfig, int audioFormat, int bufferSizeInBytes, int mode );

40

AudioTrack Constructor Args streamType ●

selects the Android audio stream destination (AudioManager STREAM_* constants)

sampleRateInHz ●

selects the sample rate (e.g. 44100 for CD quality = 44.1KHz)

channelConfig ●



selects output channel configuration (AudioFormat CHANNEL_OUT_* constants) typically CHANNEL_OUT_MONO or CHANNEL_OUT_STEREO 41

AudioTrack Constructor Args audioFormat ●

Selects the output audio format (AudioFormat ENCODING_* constants)



ENCODING_PCM_16BIT or ENCODING_PCM_8BIT (only 16 guaranteed)

buffserSizeInBytes ●

● ●

Interpretation depends on mode: –

if MODE_STATIC: max size of entire sound



if MODE_STREAM: buffer for collecting audio before output

All writes to this AudioTrack must be smaller than this. Must be at least AudioTrack.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat) with same values here 42

AudioTrack Constructor Args mode ●

MODE_STATIC or MODE_STREAM

43

AudioTrack: Writing Audio PCM is written to AudioTrack using the following methods: ●

public int write(byte[] audioData, int offsetInBytes, int sizeInBytes)



public int write(short[] audioData, int offsetInShorts, int sizeInShorts)

44

AudioTrack: MODE_STATIC 1. Instantiate AudioTrack 2. write() all the samples 3. Call play() 4. Allow the audio to play as long as you want 5. Call stop() 6. To play again, call reloadStaticData(), goto 3 7. Call release() to free native resources 45

AudioTrack: MODE_STREAM 1. Instantiate AudioTrack 2. Call play() 3. write() samples to play as they become available 4. Call stop() 5. Call release() to free native resources

46

AudioTrack: MODE_STREAM Tips ●

Writing to AudioTrack can be a blocking operation – don't use the main thread.



Use a dedicated thread for ALL AudioTrack calls.



Set a higher priority for the writing thread: ●



android.os.Process.setThreadPriority(android.os.Process. THREAD_PRIORITY_AUDIO);

Write continuously. No data available? Write small zeroed buffers while waiting (or hear a click when audio playback disengages). 47

AudioTrack: Markers and Periods AudioTrack markers and periods track live playback ●





Markers invoke a callback when the active playback reaches a certain point Periods invoke a callback when the period time reaches a multiple Position is measured in samples (aka frames), NOT bytes (one 16 bit sample = 2 bytes)

48

AudioTrack: Markers and Periods setNotificationMarkerPosition(int pos) getNotificationMarkerPosition() ●

Get/set the marker position, measured in samples



Listener will get called when the marker is crossed

setPositionNotificationPeriod(int period) ● ●

Sets the period for notifications, measured in samples Listener will be called when the playback head reaches a multiple of the period 49

AudioTrack: Markers and Periods setPlaybackPositionUpdateListener(listener) Sets the listener to be called when the marker is crossed or a period elapses during playback class MyListener implements OnPlaybackPositionUpdateListener { void onMarkerReached(AudioTrack track) { } void onPeriodicNotification(AudioTrack track) { } }

50

AudioTrack: Looping int setLoopPoints( int startInFrames, int endInFrames, int loopCount ) ●

Sets up playback looping between a start and end frame.



Only makes sense in static mode.

51

AudioTrack Features in Gingerbread (API 9) Attach audio effects (just like MediaPlayer) ● ●

getAudioSessionId() / setAudioSessionId() Create an effect with session id (android.media.audiofx)



attachAuxEffect(int effect_id)



setAuxEffectSendLevel(float level)

52

AudioRecord Used for capturing raw audio data from the device’s microphone or other audio inputs. ●

No local audio capture

53

AudioRecord Constructor new AudioRecord( int audioSource, int sampleRateInHz, int channelConfig, int audioFormat, int bufferSizeInBytes );

54

AudioRecord Constructor Args audioSource ●

Selects the audio source (MediaRecorder.AudioSource constant, e.g. MIC)

sampleRateInHz ● ●

Selects the sample rate (e.g. 44100 for CD quality = 44.1KHz) All devices are supposed to support 44100, but expect 16000 and 8000 as well

55

AudioRecord Constructor Args channelConfig ●



Selects output channel configuration (AudioFormat CHANNEL_OUT_* constants) Typically CHANNEL_OUT_MONO unless device has stereo recording mics

audioFormat ●



Selects the output audio format (AudioFormat ENCODING_* constants) ENCODING_PCM_16BIT or ENCODING_PCM_8BIT 56

AudioRecord Constructor Args audioSource ●

Selects the audio source (MediaRecorder.AudioSource constant, e.g. MIC)

sampleRateInHz ● ●

Selects the sample rate (e.g. 44100 for CD quality = 44.1KHz) All devices are supposed to support 44100, but expect 16000 and 8000 as well

57

AudioRecord Constructor buffserSizeInBytes ● ●

Internal AudioRecord buffer size Must be at least AudioRecord.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat) with same values here

58

AudioRecord: Reading Audio PCM is read from AudioRecord using the following methods: ●

public int read(byte[] audioData, int offsetInBytes, int sizeInBytes)



public int read(short[] audioData, int offsetInShorts, int sizeInShorts)



public int read(ByteBuffer audioBuffer, int sizeInBytes)



Reads can block, so always read in a dedicated thread.



Read continuously, or lose audio samples 59

AudioRecord: Reading Audio byte[] buffer = new byte[min_buffer_size]; audioRecord.startRecording(); while (!finished) { int bytes_read = audioRecord.read(buffer, 0, min_buffer_size); if (bytes_read > 0) { // Do something with buffer } else { break; } } audioRecord.stop(); 60

AudioTrack / AudioRecord Demo See ActivityAudioTrack.java

61

New Media APIs in JellyBean MediaMetadataRetriever ●

Extract metadata from the media container



Extract still video frames as Bitmaps

MediaExtractor ●

Demuxes and pulls encoded A/V from some input source

MediaCodec ●

Encodes/compresses raw A/V data



Decodes/decompresses A/V to raw format 62

Decoding Audio with JB APIs Use MediaExtractor and MediaCodec in tandem 1. Do all work OFF the main thread 2. Initialize a new MediaExtractor with data source 3. Select desired tracks from it 4. Initialize a MediaCodec decoder using metadata from MediaExtractor 5. Loop: –

Read encoded data from MediaExtractor



Write encoded data to MediaCodec's input buffers



Read decoded data from MediaCodec's output buffers 63

MediaExtractor: Create and Init AssetFileDescriptor fd = getAssets().openFd(asset_name); MediaExtractor extractor = new MediaExtractor(); extractor.setDataSource( fd.getFileDescriptor(), fd.getStartOffset(), fd.getLength() ); fd.close();

64

MediaExtractor: Selecting a Track MediaFormat format; for (int i = 0; i < extractor.getTrackCount(); i++) { format = extractor.getTrackFormat(i); if (track_is_interesting) { extractor.selectTrack(i); } }

65

MediaCodec: Create and Init String mime_type = format.getString(MediaFormat.KEY_MIME); MediaCodec mediaCodec = MediaCodec.createDecoderByType(mime_type); mediaCodec.configure( format, // MediaFormat from selected track null, // Surface, for video only null, // MediaCrypto, for encrypted source only 0 // flag CONFIGURE_FLAG_ENCODE if encoding only ); mediaCodec.start(); ByteBuffer[] codecInputBuffers = mediaCodec.getInputBuffers(); ByteBuffer[] codecOutputBuffers = mediaCodec.getOutputBuffers();

66

Recap MediaExtractor ●

Ready to read encoded audio from source

MediaCodec ●

Ready to decode encoded audio

Input Buffers ●

Used to send encoded audio to MediaCodec

Output Buffers ●

Used to receive decoded audio 67

Reading and Decoding Encoded Data Pt. 1 int inputBufIndex = mediaCodec.dequeueInputBuffer(-1); // obtain buf ByteBuffer dstBuf = codecInputBuffers[inputBufIndex]; int sampleSize = mediaExtractor.readSampleData(dstBuf, 0); long presentationTimeUs = 0; boolean inputEnded = false; if (sampleSize < 0) { inputEnded = true; sampleSize = 0; } else { presentationTimeUs = mediaExtractor.getSampleTime(); }

68

Reading and Decoding Encoded Data Pt. 2 mediaCodec.queueInputBuffer( inputBufIndex, 0,

// offset within input buf

sampleSize, presentationTimeUs, inputEnded ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0 ); if (!inputEnded) { mediaExtractor.advance(); }

69

Receiving Decoded Data BufferInfo bufferInfo = new BufferInfo(); int res = mediaCodec.dequeueOutputBuffer(bufferInfo, 0); if (res >= 0) { ByteBuffer buf = codecOutputBuffers[res]; // receive and process decoded data } else if (res == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { codecOutputBuffers = mediaCodec.getOutputBuffers(); } else if (res == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { MediaFormat oformat = mediaCodec.getOutputFormat(); // Make adjustments based on new format } else if (res == MediaCodec.INFO_TRY_AGAIN_LATER) { // Nothing available now, maybe later }

70

About Output Buffers ●

May be a “direct” ByteBuffer –



You may have to copy it to local byte array to process in java

If you call a get* method on it to copy its data, you have to clear the ByteBuffer before releasing it back to the MediaCodec

ByteBuffer buf = codecOutputBuffers[idx]; byte[] readBuffer = new byte[bufferInfo.size]; buf.get(readBuffer, 0, bufferInfo.size); buf.clear(); mediaCodec.releaseOutputBuffer(outputBufIndex, false); 71

MediaExtractor/MediaCodec Demo See ActivityMediaCodecAudioDecode.java

72

Audio with the NDK ●

OpenSL ES 1.0.1 APIs available with Gingerbread



Portable to other platforms with OpenSL ES



Some Android-specific extentions



Decode to PCM available in ICS (level 14)

73

Thank you!

Slides gzip: http://goo.gl/pBXS5 Code Samples: http://goo.gl/XYTJu

74