播放声音可以用MediaPlayer和AudioTrack,两者都提供了Java API供应用开发者使用。虽然都可以播放声音,但两者还是有很大的区别的。其中最大的区别是MediaPlayer可以播放多种格式的声音文件,例如MP3,AAC,WAV,OGG,MIDI等。MediaPlayer会在framework层创建对应的音频解码器。而AudioTrack只能播放已经解码的PCM流,如果是文件的话只支持wav格式的音频文件,因为wav格式的音频文件大部分都是PCM流。AudioTrack不创建解码器,所以只能播放不需要解码的wav文件。当然两者之间还是有紧密的联系,MediaPlayer在framework层还是会创建AudioTrack,把解码后的PCM数流传递给AudioTrack,AudioTrack再传递给AudioFlinger进行混音,然后才传递给硬件播放,所以是MediaPlayer包含了AudioTrack。使用AudioTrack播放音乐示例:
- AudioTrack audio = new AudioTrack(
- AudioManager.STREAM_MUSIC, // 指定流的类型
- 32000, // 设置音频数据的采样率 32k,如果是44.1k就是44100
- AudioFormat.CHANNEL_OUT_STEREO, // 设置输出声道为双声道立体声,而CHANNEL_OUT_MONO类型是单声道
- AudioFormat.ENCODING_PCM_16BIT, // 设置音频数据块是8位还是16位,这里设置为16位。好像现在绝大多数的音频都是16位的了
- AudioTrack.MODE_STREAM // 设置模式类型,在这里设置为流类型,另外一种MODE_STATIC貌似没有什么效果
- );
- audio.play(); // 启动音频设备,下面就可以真正开始音频数据的播放了
- // 打开mp3文件,读取数据,解码等操作省略 ...
- byte[] buffer = new buffer[4096];
- int count;
- while(true)
- {
- // 最关键的是将解码后的数据,从缓冲区写入到AudioTrack对象中
- audio.write(buffer, 0, 4096);
- if(文件结束) break;
- }
- //关闭并释放资源
- audio.stop();
- audio.release();
AudioTrack构造过程
每一个音频流对应着一个AudioTrack类的一个实例,每个AudioTrack会在创建时注册到 AudioFlinger中,由AudioFlinger把所有的AudioTrack进行混合(Mixer),然后输送到AudioHardware中进行播放,目前Android同时最多可以创建32个音频流,也就是说,Mixer最多会同时处理32个AudioTrack的数据流。
frameworks\base\media\Java\android\media\AudioTrack.java
- /**
- * streamType:音频流类型
- * sampleRateInHz:采样率
- * channelConfig:音频声道
- * audioFormat:音频格式
- * bufferSizeInBytes缓冲区大小:
- * mode:音频数据加载模式
- * sessionId:会话id
- */
- public AudioTrack(int streamType, int sampleRateInHz, int channelConfig, int audioFormat,
- int bufferSizeInBytes, int mode, int sessionId)
- throws IllegalArgumentException {
- // mState already == STATE_UNINITIALIZED
- // remember which looper is associated with the AudioTrack instantiation
- Looper looper;
- if ((looper = Looper.myLooper()) == null) {
- looper = Looper.getMainLooper();
- }
- mInitializationLooper = looper;
- /**
- * 参数检查
- * 1.检查streamType是否为:STREAM_ALARM、STREAM_MUSIC、STREAM_RING、STREAM_SYSTEM、STREAM_VOICE_CALL、
- * STREAM_NOTIFICATION、STREAM_BLUETOOTH_SCO、STREAM_BLUETOOTH_SCO,并赋值给mStreamType
- * 2.检查sampleRateInHz是否在4000到48000之间,并赋值给mSampleRate
- * 3.设置mChannels:
- * CHANNEL_OUT_DEFAULT、CHANNEL_OUT_MONO、CHANNEL_CONFIGURATION_MONO ---> CHANNEL_OUT_MONO
- * CHANNEL_OUT_STEREO、CHANNEL_CONFIGURATION_STEREO ---> CHANNEL_OUT_STEREO
- * 4.设置mAudioFormat:
- * ENCODING_PCM_16BIT、ENCODING_DEFAULT ---> ENCODING_PCM_16BIT
- * ENCODING_PCM_8BIT ---> ENCODING_PCM_8BIT
- * 5.设置mDataLoadMode:
- * MODE_STREAM
- * MODE_STATIC
- */
- audioParamCheck(streamType, sampleRateInHz, channelConfig, audioFormat, mode);
- /**
- * buffer大小检查,计算每帧字节大小,如果是ENCODING_PCM_16BIT,则为mChannelCount * 2
- * mNativeBufferSizeInFrames为帧数
- */
- audioBuffSizeCheck(bufferSizeInBytes);
- if (sessionId < 0) {
- throw new IllegalArgumentException("Invalid audio session ID: "+sessionId);
- }
- //进入native层初始化
- int[] session = new int[1];
- session[0] = sessionId;
- // native initialization
- int initResult = native_setup(new WeakReference<AudioTrack>(this),
- mStreamType, mSampleRate, mChannels, mAudioFormat,
- mNativeBufferSizeInBytes, mDataLoadMode, session);
- if (initResult != SUCCESS) {
- loge("Error code "+initResult+" when initializing AudioTrack.");
- return; // with mState == STATE_UNINITIALIZED
- }
- mSessionId = session[0];
- if (mDataLoadMode == MODE_STATIC) {
- mState = STATE_NO_STATIC_DATA;
- } else {
- mState = STATE_INITIALIZED;
- }
- }
with audio session. Use this constructor when the AudioTrack must be attached to a particular audio session. The primary use of the audio session ID is to associate audio effects to a particular instance of AudioTrack: if an audio session ID is provided when creating an AudioEffect, this effect will be applied only to audio tracks and media players in the same session and not to the output mix. When an AudioTrack is created without specifying a session, it will create its own session which can be retreived by calling the getAudioSessionId()
method. If a non-zero session ID is provided, this AudioTrack will share effects attached to this session with all other media players or audio tracks in the same session, otherwise a new session will be created for this track if none is supplied.
streamType | the type of the audio stream. See STREAM_VOICE_CALL,STREAM_SYSTEM,STREAM_RING,STREAM_MUSIC,STREAM_ALARM, andSTREAM_NOTIFICATION. |
sampleRateInHz | the sample rate expressed in Hertz. |
channelConfig | describes the configuration of the audio channels. SeeCHANNEL_OUT_MONO andCHANNEL_OUT_STEREO |
audioFormat | the format in which the audio data is represented. SeeENCODING_PCM_16BIT andENCODING_PCM_8BIT |
bufferSizeInBytes | the total size (in bytes) of the buffer where audio data is read from for playback. If using the AudioTrack in streaming mode, you can write data into this buffer in smaller chunks than this size. If using the AudioTrack in static mode, this is the maximum size of the sound that will be played for this instance. SeegetMinBufferSize(int, int, int) to determine the minimum required buffer size for the successful creation of an AudioTrack instance in streaming mode. Using values smaller than getMinBufferSize() will result in an initialization failure. |
mode | streaming or static buffer. See MODE_STATIC andMODE_STREAM |
sessionId | Id of audio session the AudioTrack must be attached to |
AudioTrack有两种数据加载模式:
- MODE_STREAM
在这种模式下,应用程序持续地write音频数据流到AudioTrack中,并且write动作将阻塞直到数据流从Java层传输到native层,同时加入到播放队列中。这种模式适用于播放大音频数据,但该模式也造成了一定的延时;
- MODE_STATIC
在播放之前,先把所有数据一次性write到AudioTrack的内部缓冲区中。适用于播放内存占用小、延时要求较高的音频数据。
frameworks\base\core\jni\android_media_AudioTrack.cpp
- static int android_media_AudioTrack_native_setup(JNIEnv *env, jobject thiz, jobject weak_this,jint streamType, jint sampleRateInHertz, jint javaChannelMask,
- jint audioFormat, jint buffSizeInBytes, jint memoryMode, jintArray jSession)
- {
- ALOGV("sampleRate=%d, audioFormat(from Java)=%d, channel mask=%x, buffSize=%d",
- sampleRateInHertz, audioFormat, javaChannelMask, buffSizeInBytes);
- int afSampleRate;//采样率
- int afFrameCount;//帧数
- //通过AudioSystem从AudioPolicyService中读取对应音频流类型的帧数
- if (AudioSystem::getOutputFrameCount(&afFrameCount, (audio_stream_type_t) streamType) != NO_ERROR) {
- ALOGE("Error creating AudioTrack: Could not get AudioSystem frame count.");
- return AUDIOTRACK_ERROR_SETUP_AUDIOSYSTEM;
- }
- //通过AudioSystem从AudioPolicyService中读取对应音频流类型的采样率
- if (AudioSystem::getOutputSamplingRate(&afSampleRate, (audio_stream_type_t) streamType) != NO_ERROR) {
- ALOGE("Error creating AudioTrack: Could not get AudioSystem sampling rate.");
- return AUDIOTRACK_ERROR_SETUP_AUDIOSYSTEM;
- }
- // Java channel masks don't map directly to the native definition, but it's a simple shift
- // to skip the two deprecated channel configurations "default" and "mono".
- uint32_t nativeChannelMask = ((uint32_t)javaChannelMask) >> 2;
- //判断是否为输出通道
- if (!audio_is_output_channel(nativeChannelMask)) {
- ALOGE("Error creating AudioTrack: invalid channel mask.");
- return AUDIOTRACK_ERROR_SETUP_INVALIDCHANNELMASK;
- }
- //得到通道个数,popcount函数用于统计一个整数中有多少位为1
- int nbChannels = popcount(nativeChannelMask);
- // check the stream type
- audio_stream_type_t atStreamType;
- switch (streamType) {
- case AUDIO_STREAM_VOICE_CALL:
- case AUDIO_STREAM_SYSTEM:
- case AUDIO_STREAM_RING:
- case AUDIO_STREAM_MUSIC:
- case AUDIO_STREAM_ALARM:
- case AUDIO_STREAM_NOTIFICATION:
- case AUDIO_STREAM_BLUETOOTH_SCO:
- case AUDIO_STREAM_DTMF:
- atStreamType = (audio_stream_type_t) streamType;
- break;
- default:
- ALOGE("Error creating AudioTrack: unknown stream type.");
- return AUDIOTRACK_ERROR_SETUP_INVALIDSTREAMTYPE;
- }
- // This function was called from Java, so we compare the format against the Java constants
- if ((audioFormat != javaAudioTrackFields.PCM16) && (audioFormat != javaAudioTrackFields.PCM8)) {
- ALOGE("Error creating AudioTrack: unsupported audio format.");
- return AUDIOTRACK_ERROR_SETUP_INVALIDFORMAT;
- }
- // for the moment 8bitPCM in MODE_STATIC is not supported natively in the AudioTrack C++ class so we declare everything as 16bitPCM, the 8->16bit conversion for MODE_STATIC will be handled in android_media_AudioTrack_native_write_byte()
- if ((audioFormat == javaAudioTrackFields.PCM8)
- && (memoryMode == javaAudioTrackFields.MODE_STATIC)) {
- ALOGV("android_media_AudioTrack_native_setup(): requesting MODE_STATIC for 8bit \
- buff size of %dbytes, switching to 16bit, buff size of %dbytes",
- buffSizeInBytes, 2*buffSizeInBytes);
- audioFormat = javaAudioTrackFields.PCM16;
- // we will need twice the memory to store the data
- buffSizeInBytes *= 2;
- }
- //根据不同的采样方式得到一个采样点的字节数
- int bytesPerSample = audioFormat == javaAudioTrackFields.PCM16 ? 2 : 1;
- audio_format_t format = audioFormat == javaAudioTrackFields.PCM16 ?
- AUDIO_FORMAT_PCM_16_BIT : AUDIO_FORMAT_PCM_8_BIT;
- //根据buffer大小反向计算帧数 , 一帧大小=一个采样点字节数 * 声道数
- int frameCount = buffSizeInBytes / (nbChannels * bytesPerSample);
- //判断参数的合法性
- jclass clazz = env->GetObjectClass(thiz);
- if (clazz == NULL) {
- ALOGE("Can't find %s when setting up callback.", kClassPathName);
- return AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED;
- }
- if (jSession == NULL) {
- ALOGE("Error creating AudioTrack: invalid session ID pointer");
- return AUDIOTRACK_ERROR;
- }
- jint* nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
- if (nSession == NULL) {
- ALOGE("Error creating AudioTrack: Error retrieving session id pointer");
- return AUDIOTRACK_ERROR;
- }
- int sessionId = nSession[0];
- env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
- nSession = NULL;
- // create the native AudioTrack object
- sp<AudioTrack> lpTrack = new AudioTrack();
- if (lpTrack == NULL) {
- ALOGE("Error creating uninitialized AudioTrack");
- return AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED;
- }
- // 创建存储音频数据的容器
- AudioTrackJniStorage* lpJniStorage = new AudioTrackJniStorage();
- lpJniStorage->mStreamType = atStreamType;
- //将Java层的AudioTrack引用保存到AudioTrackJniStorage中
- lpJniStorage->mCallbackData.audioTrack_class = (jclass)env->NewGlobalRef(clazz);
- // we use a weak reference so the AudioTrack object can be garbage collected.
- lpJniStorage->mCallbackData.audioTrack_ref = env->NewGlobalRef(weak_this);
- lpJniStorage->mCallbackData.busy = false;
- //初始化不同模式下的native AudioTrack对象
- if (memoryMode == javaAudioTrackFields.MODE_STREAM) { //stream模式
- lpTrack->set(
- atStreamType,// stream type
- sampleRateInHertz,
- format,// word length, PCM
- nativeChannelMask,
- frameCount,
- AUDIO_OUTPUT_FLAG_NONE,
- audioCallback,
- &(lpJniStorage->mCallbackData),//callback, callback data (user)
- 0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack
- 0,//stream模式下的共享内存在AudioFlinger中创建
- true,// thread can call Java
- sessionId);// audio session ID
- } else if (memoryMode == javaAudioTrackFields.MODE_STATIC) {//static模式
- // 为AudioTrack分配共享内存区域
- if (!lpJniStorage->allocSharedMem(buffSizeInBytes)) {
- ALOGE("Error creating AudioTrack in static mode: error creating mem heap base");
- goto native_init_failure;
- }
- lpTrack->set(
- atStreamType,// stream type
- sampleRateInHertz,
- format,// word length, PCM
- nativeChannelMask,
- frameCount,
- AUDIO_OUTPUT_FLAG_NONE,
- audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user));
- 0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack
- lpJniStorage->mMemBase,// shared mem
- true,// thread can call Java
- sessionId);// audio session ID
- }
- if (lpTrack->initCheck() != NO_ERROR) {
- ALOGE("Error initializing AudioTrack");
- goto native_init_failure;
- }
- nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
- if (nSession == NULL) {
- ALOGE("Error creating AudioTrack: Error retrieving session id pointer");
- goto native_init_failure;
- }
- // read the audio session ID back from AudioTrack in case we create a new session
- nSession[0] = lpTrack->getSessionId();
- env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
- nSession = NULL;
- { // scope for the lock
- Mutex::Autolock l(sLock);
- sAudioTrackCallBackCookies.add(&lpJniStorage->mCallbackData);
- }
- // save our newly created C++ AudioTrack in the "nativeTrackInJavaObj" field
- // of the Java object (in mNativeTrackInJavaObj)
- setAudioTrack(env, thiz, lpTrack);
- // save the JNI resources so we can free them later
- //ALOGV("storing lpJniStorage: %x\n", (int)lpJniStorage);
- env->SetIntField(thiz, javaAudioTrackFields.jniData, (int)lpJniStorage);
- return AUDIOTRACK_SUCCESS;
- // failures:
- native_init_failure:
- if (nSession != NULL) {
- env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
- }
- env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioTrack_class);
- env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioTrack_ref);
- delete lpJniStorage;
- env->SetIntField(thiz, javaAudioTrackFields.jniData, 0);
- return AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED;
- }
1. 检查音频参数;
2. 创建一个AudioTrack(native)对象;
3. 创建一个AudioTrackJniStorage对象;
4. 调用set函数初始化AudioTrack;
buffersize = frameCount * 每帧数据量 = frameCount * (Channel数 * 每个Channel数据量)
构造native AudioTrack
frameworks\av\media\libmedia\AudioTrack.cpp
- AudioTrack::AudioTrack(): mStatus(NO_INIT),
- mIsTimed(false),
- mPreviousPriority(ANDROID_PRIORITY_NORMAL),
- mPreviousSchedulingGroup(SP_DEFAULT),
- mCblk(NULL)
- {
- }
构造AudioTrackJniStorage
AudioTrackJniStorage是音频数据存储的容器,是对匿名共享内存的封装。
- struct audiotrack_callback_cookie {
- jclass audioTrack_class;
- jobject audioTrack_ref;//Java层AudioTrack对象引用
- bool busy;//忙判断
- Condition cond;//互斥量
- };
- class AudioTrackJniStorage {
- public:
- sp<MemoryHeapBase> mMemHeap;
- sp<MemoryBase> mMemBase;
- audiotrack_callback_cookie mCallbackData;
- audio_stream_type_t mStreamType;
- AudioTrackJniStorage() {
- mCallbackData.audioTrack_class = 0;
- mCallbackData.audioTrack_ref = 0;
- mStreamType = AUDIO_STREAM_DEFAULT;
- }
- ~AudioTrackJniStorage() {
- mMemBase.clear();
- mMemHeap.clear();
- }
- /**
- * 分配一块指定大小的匿名共享内存
- * @param sizeInBytes:匿名共享内存大小
- * @return
- */
- bool allocSharedMem(int sizeInBytes) {
- //创建一个匿名共享内存
- mMemHeap = new MemoryHeapBase(sizeInBytes, 0, "AudioTrack Heap Base");
- if (mMemHeap->getHeapID() < 0) {
- return false;
- }
- mMemBase = new MemoryBase(mMemHeap, 0, sizeInBytes);
- return true;
- }
- };
- /**
- * 创建匿名共享内存区域
- * @param size:匿名共享内存大小
- * @param flags:创建标志位
- * @param name:匿名共享内存名称
- */
- MemoryHeapBase::MemoryHeapBase(size_t size, uint32_t flags, char const * name)
- : mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags),
- mDevice(0), mNeedUnmap(false), mOffset(0)
- {
- //获取内存页大小
- const size_t pagesize = getpagesize();
- //字节对齐
- size = ((size + pagesize-1) & ~(pagesize-1));
- /* 创建共享内存,打开/dev/ashmem设备,得到一个文件描述符 */
- int fd = ashmem_create_region(name == NULL ? "MemoryHeapBase" : name, size);
- ALOGE_IF(fd<0, "error creating ashmem region: %s", strerror(errno));
- if (fd >= 0) {
- //通过mmap将匿名共享内存映射到当前进程地址空间
- if (mapfd(fd, size) == NO_ERROR) {
- if (flags & READ_ONLY) {
- ashmem_set_prot_region(fd, PROT_READ);
- }
- }
- }
- }
初始化AudioTrack
为AudioTrack设置音频参数信息,在Android4.4中,增加了一个参数transfer_type用于指定音频数据的传输方式,Android4.4定义了4种音频数据传输方式:
enum transfer_type {
TRANSFER_DEFAULT, // not specified explicitly; determine from the other parameters
TRANSFER_CALLBACK, // callback EVENT_MORE_DATA
TRANSFER_OBTAIN, // FIXME deprecated: call obtainBuffer() and releaseBuffer()
TRANSFER_SYNC, // synchronous write()
TRANSFER_SHARED, // shared memory
};
- /**
- * 初始化AudioTrack
- * @param streamType 音频流类型
- * @param sampleRate 采样率
- * @param format 音频格式
- * @param channelMask 输出声道
- * @param frameCount 帧数
- * @param flags 输出标志位
- * @param cbf Callback function. If not null, this function is called periodically
- * to provide new data and inform of marker, position updates, etc.
- * @param user Context for use by the callback receiver.
- * @param notificationFrames The callback function is called each time notificationFrames * PCM frames have been consumed from track input buffer.
- * @param sharedBuffer 共享内存
- * @param threadCanCallJava
- * @param sessionId
- * @return
- */
- status_t AudioTrack::set(
- audio_stream_type_t streamType,
- uint32_t sampleRate,
- audio_format_t format,
- audio_channel_mask_t channelMask,
- int frameCountInt,
- audio_output_flags_t flags,
- callback_t cbf,