記事のマスターリンクのこのシリーズ:テーマ別のサブディレクトリのAndroid Frameworkクラスオーディオ・サブシステム
この章の要点の概要と説明:
この章では、onに焦点を当てます。上記のマインドマップの左上にあるAudioTrackの部分で十分です。主に2つのテスト手順(C ++レイヤーのshared_mem_testとJavaレイヤーのMediaAudioTrackTest.java)を分析し、JavaレイヤーAudioTrackからC ++レイヤーAudioTrackへの呼び出しプロセス(コンストラクタープロセスと書き込みプロセス)を分析します。
ここでは、C ++レイヤーのshared_mem_testとJavaレイヤーのMediaAudioTrackTest.javaという2つのテストプログラムを使用しています。これら2つのテストプログラムのディレクトリは次のとおりです。
- frameworks / base / media / tests / audiotests / shared_mem_test.cpp
- frameworks / base / media / tests / mediaframeworktest / src / com / android / mediaframeworktest / functional / audio / MediaAudioTrackTest.java
1 shared_mem_testテストプログラム分析
shared_mem_testテストプログラムの入り口の主な機能は次のとおりです。
int main(int argc, char *argv[]) {
return android::main();
}
分析を続けます:android :: main()、コードは次のように実装されます:
int main() {
ProcessState::self()->startThreadPool();
AudioTrackTest *test;
test = new AudioTrackTest();
test->Execute();
delete test;
return 0;
}
ここでは、主に2つの重要なポイントを分析します。AudioTrackTestコンストラクターとそのExecuteメソッドです。
@ 1 AudioTrackTestコンストラクタ
AudioTrackTestコンストラクタコードは次のように実装されます。
AudioTrackTest::AudioTrackTest(void) {
InitSine(); // init sine table
}
InitSineの分析を続けます。コードの実装は次のとおりです。
void AudioTrackTest::InitSine(void) {
double phi = 0;
double dPhi = 2 * M_PI / SIN_SZ;
for(int i0 = 0; i0<SIN_SZ; i0++) {
long d0;
d0 = 32768. * sin(phi);
phi += dPhi;
if(d0 >= 32767) d0 = 32767;
if(d0 <= -32768) d0 = -32768;
sin1024[i0] = (short)d0;
}
}
これは主に、次のステップに備えてデータと波形を作成するためです。
@ 2 AudioTrackTestのExecuteメソッド
AudioTrackTestのExecuteメソッドは次のように実装されます。
void AudioTrackTest::Execute(void) {
if (Test01() == 0) {
ALOGD("01 passed\n");
} else {
ALOGD("01 failed\n");
}
}
Test01の分析を続けます。コードの実装は次のとおりです。
int AudioTrackTest::Test01() {
sp<MemoryDealer> heap;
sp<IMemory> iMem;
uint8_t* p;
short smpBuf[BUF_SZ];
long rate = 44100;
unsigned long phi;
unsigned long dPhi;
long amplitude;
long freq = 1237;
float f0;
f0 = pow(2., 32.) * freq / (float)rate;
dPhi = (unsigned long)f0;
amplitude = 1000;
phi = 0;
Generate(smpBuf, BUF_SZ, amplitude, phi, dPhi); // fill buffer
for (int i = 0; i < 1024; i++) {
heap = new MemoryDealer(1024*1024, "AudioTrack Heap Base");
iMem = heap->allocate(BUF_SZ*sizeof(short));
p = static_cast<uint8_t*>(iMem->pointer());
memcpy(p, smpBuf, BUF_SZ*sizeof(short));
//关键点1:创建一个AudioTrack对象,并传递数据
sp<AudioTrack> track = new AudioTrack(AUDIO_STREAM_MUSIC,// stream type
rate,
AUDIO_FORMAT_PCM_16_BIT,// word length, PCM
AUDIO_CHANNEL_OUT_MONO,
iMem);
status_t status = track->initCheck();
if(status != NO_ERROR) {
track.clear();
ALOGD("Failed for initCheck()");
return -1;
}
//关键点2:开始播放
track->start();
usleep(20000);
ALOGD("stop");
track->stop();
iMem.clear();
heap.clear();
usleep(20000);
}
return 0;
}
上記の分析により、ネイティブレイヤーのAudioTrackの使用プロセスは次のとおりであることがわかります。
- パラメータを作成し、AudioTrackに渡します。ここではiMemにデータ付きのバッファが含まれていることに注意してください
- スタート操作を行って再生を開始
2 MediaAudioTrackTestテストプログラムの分析
MediaAudioTrackTestには多くのテストケースがあり、そのうちの1つをここで分析します。コードの実装は次のとおりです。
public class MediaAudioTrackTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> {
private String TAG = "MediaAudioTrackTest";
//...
//Test case 1: setPlaybackHeadPosition() on playing track
@LargeTest
public void testSetPlaybackHeadPositionPlaying() throws Exception {
// constants for test
final String TEST_NAME = "testSetPlaybackHeadPositionPlaying";
final int TEST_SR = 22050;
final int TEST_CONF = AudioFormat.CHANNEL_OUT_MONO;
final int TEST_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
final int TEST_MODE = AudioTrack.MODE_STREAM;
final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
//-------- initialization --------------
int minBuffSize = AudioTrack.getMinBufferSize(TEST_SR, TEST_CONF, TEST_FORMAT);
//关键点1:构建并传递参数给AudioTrack对象
AudioTrack track = new AudioTrack(TEST_STREAM_TYPE, TEST_SR, TEST_CONF, TEST_FORMAT,
2*minBuffSize, TEST_MODE);
byte data[] = new byte[minBuffSize];
//-------- test --------------
assumeTrue(TEST_NAME, track.getState() == AudioTrack.STATE_INITIALIZED);
track.write(data, 0, data.length);
track.write(data, 0, data.length);
//关键点2:播放
track.play();
assertTrue(TEST_NAME,
track.setPlaybackHeadPosition(10) == AudioTrack.ERROR_INVALID_OPERATION);
//-------- tear down --------------
track.release();
}
//...
}
上記の分析により、JavaレイヤーでのAudioTrackの使用プロセスは次のとおりであることがわかります。
- パラメータを作成し、AudioTrackに渡します。
- wrtieを実行してデータを書き込むことは、shared_mem_testのiMemと同等です。
- スタート操作を行うと、再生が始まります。
次に、JavaレイヤーからC ++レイヤーのオーディオトラックへのAudioTrackの呼び出しプロセスを分析します。1つはコンストラクターで、もう1つは書き込み関数です。
3 JavaレイヤーからC ++レイヤーのAudioTrackへのAudioTrackの分析
3.1 JavaレイヤーAudioTrackからC ++レイヤーAudioTrackへのコンストラクター分析
同時に、JavaレイヤーにAudioTrackオブジェクトを作成すると、最終的にネイティブレイヤーにAudioTrackオブジェクトが作成されます。JavaレイヤーでAudioTrackを分析しますコードの実装は次のとおりです。
public AudioTrack(AudioAttributes attributes, AudioFormat format, int bufferSizeInBytes,
int mode, int sessionId)
throws IllegalArgumentException {
//...
// native initialization
int initResult = native_setup(new WeakReference<AudioTrack>(this), mAttributes,
mSampleRate, mChannels, mAudioFormat,
mNativeBufferSizeInBytes, mDataLoadMode, session);
//...
mSessionId = session[0];
if (mDataLoadMode == MODE_STATIC) {
mState = STATE_NO_STATIC_DATA;
} else {
mState = STATE_INITIALIZED;
}
}
JNIのマッピング関係によると:
{"native_setup", "(Ljava/lang/Object;Ljava/lang/Object;IIIII[I)I",
(void *)android_media_AudioTrack_setup},
android_media_AudioTrack_setupの実装の分析を続けます。コードは次のとおりです。
static jint
android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this,
jobject jaa,
jint sampleRateInHertz, jint javaChannelMask,
jint audioFormat, jint buffSizeInBytes, jint memoryMode, jintArray jSession) {
//...
//关键点1:创建native AudioTrack对象
sp<AudioTrack> lpTrack = new AudioTrack();
//...
switch (memoryMode) {//这里开始针对 两种模式MODE_STREAM 和 MODE_STATIC进行不同参数的设置
case MODE_STREAM:
//关键点2.1:set方法,设置参数
status = lpTrack->set(
AUDIO_STREAM_DEFAULT,// stream type, but more info conveyed in paa (last argument)
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,// shared mem
true,// thread can call Java
sessionId,// audio session ID
AudioTrack::TRANSFER_SYNC,
NULL, // default offloadInfo
-1, -1, // default uid, pid values
paa);
break;
case MODE_STATIC:
//应用端申请共享内存
if (!lpJniStorage->allocSharedMem(buffSizeInBytes)) {
ALOGE("Error creating AudioTrack in static mode: error creating mem heap base");
goto native_init_failure;
}
//关键点2.2:set方法,设置参数
status = lpTrack->set(
AUDIO_STREAM_DEFAULT,// stream type, but more info conveyed in paa (last argument)
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
AudioTrack::TRANSFER_SHARED,
NULL, // default offloadInfo
-1, -1, // default uid, pid values
paa);
break;
//...
default:
ALOGE("Unknown mode %d", memoryMode);
goto native_init_failure;
}
//...
return (jint) AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED;
}
ここでは2つの主要な操作が実行されます。1つはC ++レイヤーのAudioTrackオブジェクトを作成することであり、もう1つはそのsetメソッドを実行することです。C++レイヤーのAudioTrackオブジェクトのコンストラクターコードは次のとおりです。
AudioTrack::AudioTrack()//无参对象,后再调用set方法
: mStatus(NO_INIT),
mIsTimed(false),
mPreviousPriority(ANDROID_PRIORITY_NORMAL),
mPreviousSchedulingGroup(SP_DEFAULT),
mPausedPosition(0)
{
mAttributes.content_type = AUDIO_CONTENT_TYPE_UNKNOWN;
mAttributes.usage = AUDIO_USAGE_UNKNOWN;
mAttributes.flags = 0x0;
strcpy(mAttributes.tags, "");
}
AudioTrack::AudioTrack(//有参对象,不需再调用set方法
audio_stream_type_t streamType,
uint32_t sampleRate,
audio_format_t format,
audio_channel_mask_t channelMask,
size_t frameCount,
audio_output_flags_t flags,
callback_t cbf,
void* user,
uint32_t notificationFrames,
int sessionId,
transfer_type transferType,
const audio_offload_info_t *offloadInfo,
int uid,
pid_t pid,
const audio_attributes_t* pAttributes)
: mStatus(NO_INIT),
mIsTimed(false),
mPreviousPriority(ANDROID_PRIORITY_NORMAL),
mPreviousSchedulingGroup(SP_DEFAULT),
mPausedPosition(0)
{
mStatus = set(streamType, sampleRate, format, channelMask,
frameCount, flags, cbf, user, notificationFrames,
0 /*sharedBuffer*/, false /*threadCanCallJava*/, sessionId, transferType,
offloadInfo, uid, pid, pAttributes);
}
AudioTrackの使用には2つの状況があることがわかります。
- パラメータコンストラクタはありません。後でパラメータを設定するためにsetメソッドを実行する必要があります。
- パラメータを設定するためにsetメソッドを直接呼び出すパラメータコンストラクタがあります。
要約すると、サウンドを再生するときにAudioTrackオブジェクトを作成する必要があります。JavaAudioTrackオブジェクトを使用すると、C ++ AudioTrackオブジェクトが作成されます。そのため、分析のコアはC ++ AudioTrackクラスであり、重要な関数であるsetが含まれています。同時に、AudioTrackはサウンドの属性のみを判別できますが、どのデバイスからサウンドが再生されるかは判別できません。set関数については、第6章で詳しく説明しています。
3.2 C ++レイヤーAudioTrackへの書き込み関数のJavaレイヤーAudioTrack
ここには、次のような多くの書き込み関数があります。
public int write(byte[] audioData, int offsetInBytes, int sizeInBytes) {
//...
int ret = native_write_byte(audioData, offsetInBytes, sizeInBytes, mAudioFormat,
true /*isBlocking*/);
//...
return ret;
}
public int write(short[] audioData, int offsetInShorts, int sizeInShorts) {
//...
int ret = native_write_short(audioData, offsetInShorts, sizeInShorts, mAudioFormat);
//...
return ret;
}
public int write(float[] audioData, int offsetInFloats, int sizeInFloats,
@WriteMode int writeMode) {
//...
int ret = native_write_float(audioData, offsetInFloats, sizeInFloats, mAudioFormat,
writeMode == WRITE_BLOCKING);
//...
return ret;
}
public int write(ByteBuffer audioData, int sizeInBytes,
@WriteMode int writeMode) {
int ret = 0;
if (audioData.isDirect()) {
ret = native_write_native_bytes(audioData,
audioData.position(), sizeInBytes, mAudioFormat,
writeMode == WRITE_BLOCKING);
} else {
ret = native_write_byte(NioUtils.unsafeArray(audioData),
NioUtils.unsafeArrayOffset(audioData) + audioData.position(),
sizeInBytes, mAudioFormat,
writeMode == WRITE_BLOCKING);
}
return ret;
}
これがネイティブレイヤーの呼び出しです。引き続きフォローアップしてください。コードは次のとおりです。
static jint android_media_AudioTrack_write_byte(JNIEnv *env, jobject thiz,
jbyteArray javaAudioData,
jint offsetInBytes, jint sizeInBytes,
jint javaAudioFormat,
jboolean isWriteBlocking) {
sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
//...
jint written = writeToTrack(lpTrack, javaAudioFormat, cAudioData, offsetInBytes, sizeInBytes,
isWriteBlocking == JNI_TRUE /* blocking */);
//...
return written;
}
static jint android_media_AudioTrack_write_native_bytes(JNIEnv *env, jobject thiz,
jbyteArray javaBytes, jint byteOffset, jint sizeInBytes,
jint javaAudioFormat, jboolean isWriteBlocking) {
sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
//...
jint written = writeToTrack(lpTrack, javaAudioFormat, bytes.get(), byteOffset,
sizeInBytes, isWriteBlocking == JNI_TRUE /* blocking */);
return written;
}
static jint android_media_AudioTrack_write_short(JNIEnv *env, jobject thiz,
jshortArray javaAudioData,
jint offsetInShorts, jint sizeInShorts,
jint javaAudioFormat) {
sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
//...
jint written = writeToTrack(lpTrack, javaAudioFormat, (jbyte *)cAudioData,
offsetInShorts * sizeof(short), sizeInShorts * sizeof(short),
true /*blocking write, legacy behavior*/);
//...
return written;
}
static jint android_media_AudioTrack_write_float(JNIEnv *env, jobject thiz,
jfloatArray javaAudioData,
jint offsetInFloats, jint sizeInFloats,
jint javaAudioFormat,
jboolean isWriteBlocking) {
sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
//...
jint written = writeToTrack(lpTrack, javaAudioFormat, (jbyte *)cAudioData,
offsetInFloats * sizeof(float), sizeInFloats * sizeof(float),
isWriteBlocking == JNI_TRUE /* blocking */);
//...
return written;
}
ここでネイティブ関連関数が最後にwriteToTrack関数を呼び出します。コードの実装は次のとおりです。
jint writeToTrack(const sp<AudioTrack>& track, jint audioFormat, const jbyte* data,
jint offsetInBytes, jint sizeInBytes, bool blocking = true) {
ssize_t written = 0;
//playbackthread提供共享内存,调用C++层track的write函数
if (track->sharedBuffer() == 0) {
written = track->write(data + offsetInBytes, sizeInBytes, blocking);
if (written == (ssize_t) WOULD_BLOCK) {
written = 0;
}
} else {//应用端 提供共享内存,直接执行memcpy
const audio_format_t format = audioFormatToNative(audioFormat);
switch (format) {
default:
case AUDIO_FORMAT_PCM_FLOAT:
case AUDIO_FORMAT_PCM_16_BIT: {
if ((size_t)sizeInBytes > track->sharedBuffer()->size()) {
sizeInBytes = track->sharedBuffer()->size();
}
//这里将data数据拷贝给 共享内存
memcpy(track->sharedBuffer()->pointer(), data + offsetInBytes, sizeInBytes);
written = sizeInBytes;
} break;
case AUDIO_FORMAT_PCM_8_BIT: {
//功能同上,只是8位需要中间的数据转换环节
if (((size_t)sizeInBytes)*2 > track->sharedBuffer()->size()) {
sizeInBytes = track->sharedBuffer()->size() / 2;
}
int count = sizeInBytes;
int16_t *dst = (int16_t *)track->sharedBuffer()->pointer();
const uint8_t *src = (const uint8_t *)(data + offsetInBytes);
memcpy_to_i16_from_u8(dst, src, count);
written = sizeInBytes;
} break;
}
}
return written;
}
次に、C ++レイヤートラックのいくつかの基本操作を呼び出しますこのコードは、次のように簡単に解釈されます。
- track-> sharedBuffer()== 0、つまり共有メモリがplaybackthreadによって提供される場合、C ++レイヤートラックの書き込みメソッドが実行されます。
- track-> sharedBuffer()!= 0の場合、つまり、共有メモリはAPP側によって提供され、memcpy操作を直接実行して、track-> sharedBuffer()に値を割り当てます。
第7章では、トラック書き込み方法を詳しく紹介します。