最近遇到个问题,需要使用共享内存。之前看audioTrack与audioFlinger部份时就看到共存内存的分配与同步,但是没有细研究这部份。这几天细看了下流程,发现使用起来挺简单的。
ashmem是android封装提供的一种共享内存实现方式。驱动会在/dev/下面注册ashmem字符设备,供上层做文件操作(ashmem驱动后面分析)。
按照一般理解(如camera videobuffer操作), 既然内核给分配了内存,那上层用户空间要使用的话,要么通过read/write文件操作接口,在用户层与内核层作拷贝;要么就是mmap映射,然后直接使用。ashmem就是直接mmap到用户空间的。大概使用原理就是server、client一端打开/dev/ashmem得到fd句柄,并mmap得到共享内存地址;再把fd与size传到对应的client、server,另一端再mmap下也得到了此地址,这样二边就可以直接操作各自mmap得到的地址就行了。当然从server、client传fd到client、server这里面也包含了binder通信。
android对ashemem的封装很简单(/system/core/libcutils/Ashmem-dev.c),提供了以下几个接口。一般我们只取ashmem_create_region,得到fd(既然是fd,那必然有close,但android并没有提供,因为只需要close(fd)就行了。那还有一个问题,既然server与client端都是fd,按理解那这2个fd的值应该是不同的,肯定会dup)。
ashmem_create_region
ashmem_set_prot_region
ashmem_pin_region
ashmem_unpin_region
ashmem_get_size_region
完全可以在这几个封装接口的基础上直接使用。除此之外android framework还封了一个IMemory类给上层操作。
1、直接操作Ashmem-dev.c接口demo
demo目录如下所示:
demo由server与client及接口组成,共享内存可由server产生也可由client去申请。为了验证共享内存的使用及正确性,server与client都可以读写此共享内存。这样就产生了同步问题。为了简化起见,弄了个ICallback类,在server、client操作完后,callback对方,这时对方才操作shareMemory,这样就不需要花太多精力与时间去弄同步部份。(典型同步见audioTrack与audioFlinger之间的cblk同步)
具体代码如下:
ITestBinder.h
#ifndef _ITESTBINDER_H_ #define _ITESTBINDER_H_
#include <binder/IInterface.h> #include <utils/String8.h> #include "ICallback.h"
namespace android { enum { msg_mem = 0x0000f000, msg_mem_client_not_prepare = msg_mem + 0x01, msg_mem_client_prepared = msg_mem + 0x02,
msg_mem_client_req_server_alloc = msg_mem + 0x10,
msg_mem_server_not_prepare = msg_mem + 0x020, msg_mem_server_prepared = msg_mem + 0x021,
msg_data = 0x0000f100, msg_data_client_write = msg_data + 0x01, msg_data_server_read = msg_data + 0x02,
msg_diconnect = 0x0000ff00, };
struct testShareMem { int fd; int size; };
class ITestBinder : public IInterface { public: DECLARE_META_INTERFACE(TestBinder); virtual int setCallback(const sp<ICallback>& callback) = 0; virtual int notify(int msg, struct testShareMem *mem) = 0; };
class BnTestBinder : public BnInterface<ITestBinder> { public: virtual status_t onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0); }; }
#endif //_ITESTBINDER_H_ |
ITestBinder.cpp
#define LOG_NDEBUG 0 #define LOG_TAG "ITestBinder" #include <utils/Log.h>
#include <binder/Parcel.h> #include <binder/IInterface.h> #include "ITestBinder.h"
namespace android { enum { CONNECT = 0, NOTIFY, SET_CALLBACK };
class BpTestBinder : public BpInterface<ITestBinder> { public: BpTestBinder(const sp<IBinder>& impl) : BpInterface<ITestBinder>(impl) { }
virtual int notify(int msg, struct testShareMem *mem) { //ALOGD("%s", __func__); Parcel data,reply; data.writeInt32(msg);
if (msg == msg_mem_client_prepared) { if (mem != NULL) { data.writeFileDescriptor(mem->fd); data.writeInt32(mem->size); } } else if (msg == msg_mem_client_req_server_alloc) { if (mem != NULL) { data.writeInt32(mem->size); } }
remote()->transact(NOTIFY, data, &reply); if (msg == msg_mem_client_req_server_alloc) { int ret = reply.readInt32(); if (!ret) { int fd = reply.readFileDescriptor(); int size = reply.readInt32(); ALOGD("bpTestBinder get: fd=%d, size=%d", fd, size); mem->fd = fd; mem->fd = dup(mem->fd); //why must dup??? mem->size = size; } return ret; } else { return reply.readInt32(); } }
virtual int setCallback(const sp<ICallback>& callback) { //ALOGD("%s", __func__); Parcel data, reply; //data.writeStrongBinder(callback->asBinder()); data.writeStrongBinder(IInterface::asBinder(callback)); remote()->transact(SET_CALLBACK, data, &reply); return reply.readInt32(); } };
IMPLEMENT_META_INTERFACE(TestBinder, "android.test.ITestBinder");
/////////////////////////////// status_t BnTestBinder::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { //ALOGD("%s, code=%d\n", __func__, code); switch (code) { case SET_CALLBACK: { //ALOGD("%s, SET_CALLBACK", __func__); sp<ICallback> callback = interface_cast<ICallback>(data.readStrongBinder()); reply->writeInt32(setCallback(callback)); return NO_ERROR; }
case NOTIFY: { //CHECK_INTERFACE(ITestBinder, data, reply); //ALOGD("%s, NOTIFY", __func__); int msg = data.readInt32();
struct testShareMem *mem = new testShareMem;
if (msg == msg_mem_client_prepared) { if (data.dataAvail() > 0) { mem->fd = data.readFileDescriptor(); mem->size = data.readInt32(); } reply->writeInt32(notify(msg, mem)); } else if (msg == msg_mem_client_req_server_alloc) { mem->size = data.readInt32(); int ret = notify(msg, mem); if (!ret) { reply->writeInt32(0); //success reply->writeFileDescriptor(mem->fd); reply->writeInt32(mem->size); } } else { reply->writeInt32(notify(msg, mem)); } free(mem); return NO_ERROR; }
default: { //ALOGD("%s, default onTransact handle\n", __func__); return BBinder::onTransact(code, data, reply, flags); } } }
} |
ICallback.h
#ifndef _ICALLBACK_H_ #define _ICALLBACK_H_
#include <binder/IInterface.h>
namespace android { class ICallback : public IInterface { public: DECLARE_META_INTERFACE(Callback); virtual int notifyCallback(int msg) = 0; };
class BnCallback : public BnInterface<ICallback> { public: virtual status_t onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0); }; }
#endif //_ICALLBACK_H_ |
ICallback.cpp
#define LOG_NDEBUG 0 #define LOG_TAG "ICallback" #include <utils/Log.h>
#include <binder/Parcel.h> #include <binder/IInterface.h>
#include "ICallback.h"
namespace android{
enum { NOTIFY_CALLBACK = 0, };
class BpCallback : public BpInterface<ICallback> { public: BpCallback(const sp<IBinder>& impl) : BpInterface<ICallback>(impl) { }
virtual int notifyCallback(int msg) { //ALOGD("%s", __func__); Parcel data,reply; data.writeInt32(msg); remote()->transact(NOTIFY_CALLBACK, data, &reply); return reply.readInt32(); } };
IMPLEMENT_META_INTERFACE(Callback, "android.test.ICallback");
//////////////////////////////////////////////// status_t BnCallback::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { //ALOGD("%s, code=%d\n", __func__, code); switch (code) { case NOTIFY_CALLBACK: { //CHECK_INTERFACE(ICallback, data, reply); reply->writeInt32(notifyCallback((int) data.readInt32())); return NO_ERROR; }
default: { return BBinder::onTransact(code, data, reply, flags); } } } } |
Android.mk
include $(call all-subdir-makefiles) |
TestService.cpp
#define LOG_NDEBUG 0 #define LOG_TAG "TestService" #include <utils/Log.h>
#include <sys/mman.h> #include <cutils/ashmem.h>
#include <binder/IPCThreadState.h> #include <binder/IServiceManager.h> #include <binder/Parcel.h> #include <utils/String8.h> #include "TestService.h"
namespace android { TestService::TestService() { mShareClient.mShareMem.fd = -1; mShareClient.mShareMem.size = 0; mShareClient.mMapAddr = MAP_FAILED;
mShareMemHost.mShareMem.fd = -1; mShareMemHost.mShareMem.size = 0; mShareMemHost.mMapAddr = MAP_FAILED; }
TestService::~TestService() { if (mShareClient.mShareMem.fd > 0) { close(mShareClient.mShareMem.fd); } if (mShareClient.mMapAddr != MAP_FAILED) { munmap(mShareClient.mMapAddr, mShareClient.mShareMem.size); }
//free host if (mShareMemHost.mShareMem.fd > 0) { close(mShareMemHost.mShareMem.fd); } if (mShareMemHost.mMapAddr != MAP_FAILED) { munmap(mShareMemHost.mMapAddr, mShareMemHost.mShareMem.size); } }
int TestService::setCallback(const sp<ICallback>& cb) { //ALOGD("%s", __func__); mCallback = cb; return 0; }
int TestService::notify(int msg, /*const*/ struct testShareMem *mem) { //ALOGD("%s", __func__);
switch (msg) { case msg_mem_client_prepared: { ALOGD("get client share mem: fd=%d, size=%d", mem->fd, mem->size); //int fd = dup(mem->fd); int fd = mem->fd; mShareClient.mShareMem.size = mem->size; mShareClient.mMapAddr = mmap(0, mShareClient.mShareMem.size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); if (mShareClient.mMapAddr == MAP_FAILED) { ALOGE("mmap fail!!"); close(fd); //return -1; } else { mShareClient.mShareMem.fd = fd; } ALOGD("mmaped mem: %p", mShareClient.mMapAddr); } break;
case msg_data_client_write: { ALOGD("get client data msg:"); if (mShareClient.mMapAddr != MAP_FAILED) { ALOGD(" %s", (char *)mShareClient.mMapAddr); sprintf((char *)mShareClient.mMapAddr, "%s", "service write: 1234567890"); } else if (mShareMemHost.mMapAddr != MAP_FAILED) { ALOGD(" %s", (char *)mShareMemHost.mMapAddr); sprintf((char *)mShareMemHost.mMapAddr, "%s", "2018 123456789"); }
sp<ICallback> cb = mCallback; if (cb != 0) { cb->notifyCallback(msg_data_server_read); } } break;
case msg_mem_client_req_server_alloc: { if (mem == NULL || mem->size <=0) { ALOGW("client must send right param go get share mem fd!!"); return -1; } ALOGD("client request server to alloc share mem, size:%d!!", mem->size);
int size = mem->size; int fd = ashmem_create_region("test_ash_mem_share_host", size); mShareMemHost.mShareMem.fd = fd; mShareMemHost.mMapAddr = mmap(0, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); if (mShareMemHost.mMapAddr == MAP_FAILED) { ALOGE("mmap(fd=%d, size=%u) failed (%s)", fd, uint32_t(size), strerror(errno)); close(fd); mShareMemHost.mShareMem.fd = -1; return -errno; } mShareMemHost.mShareMem.size = size; ALOGD("malloc share_mem, fd=%d, size=%d", fd, mShareMemHost.mShareMem.size); ALOGD("mmap addr: %p", mShareMemHost.mMapAddr);
/*sp<ICallback> cb = mCallback; if (cb != 0) { cb->notifyCallback(msg_mem_server_prepared); }*/
mem->fd = mShareMemHost.mShareMem.fd; mem->size = mShareMemHost.mShareMem.size; } break;
case msg_diconnect: { ALOGD("client disconnect!!"); } break;
default: break; }
return 0; } } |
TestService.h
#ifndef _TEST_SERVICE_H_ #define _TEST_SERVICE_H_
#include <binder/BinderService.h> #include "../ITestBinder.h" #include "../ICallback.h"
namespace android { typedef struct shareMemory { void *mMapAddr; struct testShareMem mShareMem; }severShareMemory_t;
class TestService : public BinderService<TestService>, public BnTestBinder { friend class BinderService<TestService>; public: TestService(); virtual ~TestService();
static char const* getServiceName() { return "test.ITestService"; }
virtual int notify(int msg, /*const*/ struct testShareMem *mem); virtual int setCallback(const sp<ICallback>& callback);
private: severShareMemory_t mShareClient; sp<ICallback> mCallback; severShareMemory_t mShareMemHost; }; }
#endif //_TEST_SERVICE_H_ |
main.cpp
#define LOG_NDEBUG 0 #define LOG_TAG "server_main" #include <utils/Log.h>
#include <binder/IPCThreadState.h> #include <binder/ProcessState.h> #include <binder/IServiceManager.h>
#include "TestService.h"
using namespace android;
int main(int argc, char** argv) { sp<ProcessState> proc(ProcessState::self()); sp<IServiceManager> sm = defaultServiceManager();
ALOGI("ServiceManager: %p", sm.get()); TestService::instantiate();
ProcessState::self()->startThreadPool(); IPCThreadState::self()->joinThreadPool(); return 0; } |
Android.mk
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \ main.cpp \ TestService.cpp \ ../ITestBinder.cpp \ ../ICallback.cpp
LOCAL_SHARED_LIBRARIES := \ libcutils \ libutils \ liblog \ libbinder
LOCAL_MODULE:= test_mem_server_4 LOCAL_MODULE_TAGS := eng include $(BUILD_EXECUTABLE) |
TestClient.cpp
#define LOG_NDEBUG 0 #define LOG_TAG "TestClient" #include <sys/mman.h> #include <utils/Log.h> #include <binder/IServiceManager.h> #include <cutils/ashmem.h> #include "TestClient.h"
namespace android { int TestClient::Callback::notifyCallback(int msg) { switch (msg) { case msg_data_server_read: ALOGD("get service read echo back:"); ALOGD(" %s", (char *)(tc->getCurShareMemory()->mMapAddr)); break;
default: break; } return 0; }
sp<ITestBinder> TestClient::gTestService = NULL;
const sp<ITestBinder>& TestClient::get_test_service() { if (gTestService == NULL) { sp<IServiceManager> sm = defaultServiceManager(); sp<IBinder> binder; do { binder = sm->getService(String16("test.ITestService")); if (binder != 0) break; ALOGD("testbinder not published, waiting..."); usleep(500000); // 0.5 s } while (1); gTestService = interface_cast<ITestBinder>(binder); }
if(gTestService == NULL) { ALOGE("connetct no testbinder!"); } return gTestService; }
TestClient::TestClient() : mCb(NULL) , curShareMemType(SHAREMEM_CREATE_BY_CLIENT) { mShareMemLocal.mShareMem.fd = -1; mShareMemLocal.mShareMem.size = 0; mShareMemLocal.mMapAddr = MAP_FAILED;
mShareMemServer.mShareMem.fd = -1; mShareMemServer.mShareMem.size = 0; mShareMemServer.mMapAddr = MAP_FAILED; }
TestClient::~TestClient() { if (mCb.get() != NULL) { mCb.clear(); } if (mShareMemLocal.mMapAddr != MAP_FAILED) munmap(mShareMemLocal.mMapAddr, mShareMemLocal.mShareMem.size); if (mShareMemLocal.mShareMem.fd > 0) close(mShareMemLocal.mShareMem.fd);
if (mShareMemServer.mMapAddr != MAP_FAILED) munmap(mShareMemServer.mMapAddr, mShareMemServer.mShareMem.size); if (mShareMemServer.mShareMem.fd > 0) close(mShareMemServer.mShareMem.fd); }
int TestClient::init(int shareMemType) { sp<ITestBinder> ts = get_test_service(); if (ts == NULL) return -1;
mCb = new Callback(this); ts->setCallback(mCb);
if (shareMemType == SHAREMEM_CREATE_BY_CLIENT) curShareMemType = SHAREMEM_CREATE_BY_CLIENT; else curShareMemType = SHAREMEM_CREATE_BY_SERVER;
return allocShareMemory(curShareMemType); }
shareMemory_t* TestClient::getCurShareMemory() { if (curShareMemType == SHAREMEM_CREATE_BY_SERVER) { return &mShareMemServer; } else { return &mShareMemLocal; } }
int TestClient::localAlloc(int size) { ALOGD("%s, local alloc share memory", __func__);
if (size <= 0) return -1;
int fd = ashmem_create_region("test_ash_mem_share", size); mShareMemLocal.mShareMem.fd = fd; mShareMemLocal.mMapAddr = mmap(0, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); if (mShareMemLocal.mMapAddr == MAP_FAILED) { ALOGE("mmap(fd=%d, size=%u) failed (%s)", fd, size, strerror(errno)); close(fd); mShareMemLocal.mShareMem.fd = -1; return -errno; } mShareMemLocal.mShareMem.size = size;
ALOGD("fd: %d, size: %d", mShareMemLocal.mShareMem.fd, mShareMemLocal.mShareMem.size); return 0; }
int TestClient::serverAlloc(int size) { sp<ITestBinder> ts = get_test_service(); ALOGD("%s, server alloc share memory", __func__);
if (ts == 0) return 0;
struct testShareMem mem; mem.size = size; int ret = ts->notify(msg_mem_client_req_server_alloc, &mem); if (!ret) { mShareMemServer.mShareMem = mem; mShareMemServer.mMapAddr = mmap(0, mem.size, PROT_READ|PROT_WRITE, MAP_SHARED, mem.fd, 0); if (mShareMemServer.mMapAddr == MAP_FAILED) { ALOGE("mmap(fd=%d, size=%u) failed (%s)", mem.fd, size, strerror(errno)); close(mem.fd); mShareMemServer.mShareMem.fd = -1; return -errno; } mShareMemServer.mShareMem.size = size; }
ALOGD("fd: %d, size: %d", mShareMemServer.mShareMem.fd, mShareMemServer.mShareMem.size); return ret; }
int TestClient::allocShareMemory(int type) { int ret;
switch(type) { case SHAREMEM_CREATE_BY_BOTH: { ALOGD("not realize yet!!!"); } break;
case SHAREMEM_CREATE_BY_SERVER: { ret = serverAlloc(2018); } break;
case SHAREMEM_CREATE_BY_CLIENT: { ret = localAlloc(2018);
sp<ITestBinder> ts = get_test_service(); ts->notify(msg_mem_client_prepared, &mShareMemLocal.mShareMem); } break;
default: ALOGD("error type!!!"); break; }
return ret; }
int TestClient::write(String8 &str) { //ALOGD("%s", __func__); sp<ITestBinder> ts = get_test_service(); if (ts == NULL) return -1;
char *mem; size_t size; if (curShareMemType == SHAREMEM_CREATE_BY_CLIENT) { mem = (char *)mShareMemLocal.mMapAddr; size = mShareMemLocal.mShareMem.size; } else { mem = (char *)mShareMemServer.mMapAddr; size = mShareMemServer.mShareMem.size; } memset((void *)mem, 0, (unsigned int)size); memcpy((void *)mem, str.string(), str.length());
ts->notify(msg_data_client_write, NULL); return 0; }
int TestClient::exit() { ALOGD("%s", __func__); sp<ITestBinder> ts = get_test_service(); if (ts == NULL) return -1; ts->notify(msg_diconnect, 0);
return 0; } } |
TestClient.h
#ifndef _TEST_CLIENT_H_ #define _TEST_CLIENT_H_
#include "../ITestBinder.h" #include "../ICallback.h"
namespace android { enum{ SHAREMEM_CREATE_BY_CLIENT = 0, SHAREMEM_CREATE_BY_SERVER = 1, SHAREMEM_CREATE_BY_BOTH = 2, //reserve, two share memory for test };
typedef struct shareMemory { void *mMapAddr; struct testShareMem mShareMem; }shareMemory_t;
class TestClient { public: static const sp<ITestBinder>& get_test_service(); static sp<ITestBinder> gTestService;
public: TestClient(); ~TestClient();
int init(int); int write(String8 &str); int exit();
protected: class Callback : public BnCallback { friend class TestClient; public: Callback(TestClient *tclient) : tc(tclient) {}
virtual ~Callback() {}
virtual int notifyCallback(int msg);
private: TestClient *tc; };
public: shareMemory_t* getCurShareMemory();
private: int localAlloc(int size); int serverAlloc(int size); int allocShareMemory(int type);
private: shareMemory_t mShareMemLocal; sp<Callback> mCb; shareMemory_t mShareMemServer; int curShareMemType; //client or server alloc sharemem }; }
#endif //_TEST_CLIENT_H_ |
main.cpp
#define LOG_NDEBUG 0 #define LOG_TAG "client_main" #include <utils/Log.h>
#include "TestClient.h"
using namespace android;
int main(int argc, char* argv[]) { int cnt = 0;
TestClient* myclient = new TestClient(); if(myclient == NULL) { return 0; }
myclient->init(/*SHAREMEM_CREATE_BY_CLIENT*/SHAREMEM_CREATE_BY_SERVER);
while(cnt++ < 5) { String8 str("binder mem test"); myclient->write(str); }
myclient->exit(); delete myclient; return 0; } |
Android.mk
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \ TestClient.cpp \ main.cpp \ ../ITestBinder.cpp \ ../ICallback.cpp
LOCAL_SHARED_LIBRARIES := \ libcutils \ libutils \ liblog \ libbinder
LOCAL_MODULE:= test_mem_client_4 LOCAL_MODULE_TAGS := eng include $(BUILD_EXECUTABLE) |
1.1、由client申请共享内存
fd与size是怎样传到server端的。BpTestBinder直接写fd与size到BnTestBinder。
bn端的处理:
可以说是直接传了fd与size,server端直接拿来就mmap了。
关闭时也是各自关闭各自的fd。
操作起来相当简单。我们看打印2个fd的值。fd=5、fd=7果然是不一致。是否是ashmem驱动帮我们dup了??
1.2、由server申请共享内存
跟前面client端申请内存一样的,注意下面的,传fd、size值。在bp端读取bn端的fd时,必须得dup下,这点有些不懂,其实不dup的话,打印fd值,驱动是有帮dup的,这里为什么要再dup下,实在是想不通。如果这里不dup,那就会出现mmap fail。
在bp那里没有加dup(),mmap fail:
2、IMemory分析
通常我们调用IMemory去分配共享内存时,一般都如下面操作:
sp<MemoryHeapBase> mMemHeap = new MemoryHeapBase(sndDataSize, 0, "AudioTrack Heap Base");
sp<MemoryBase> mMemBase = new MemoryBase(mMemHeap, 0, sndDataSize);
memcpy(mMemHeap->getBase(), sndDataBuffer, sndDataSize);
由BnMemoryHeapBase转换到BnMemory,再通过IMemory类去实现进程间的共享内存通信。
IMemory是framework层在ashmem-dev.c接口上封的一个共享内存类。Bn端是memoryHeapBase与memoryBase。与直接调用ashmem-dev.c接口基本相同,memoryHeapBase也是对open与mmap封装了下。
BpMemopry端,我们看常见的getBase()接口。
fd、size、offset对应Bn端mmap()操作的参数。同样可以看到BpMemory端的dup()操作,这也验证了上面demo只有加dup()才会正常。
所以可以看到,IMemory类的操作与直接调用ashmem-dev.c接口也是大同小异。
用一个简单的audioTrack demo来说明IMemory接口的使用。
test.cpp
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <binder/Parcel.h> #include <binder/IPCThreadState.h> #include <binder/IServiceManager.h> #include <binder/MemoryHeapBase.h> #include <binder/MemoryBase.h> #include <media/AudioTrack.h>
#define uint32_t unsigned int #define uint16_t unsigned short
#define ID_RIFF 0x46464952 #define ID_WAVE 0x45564157 #define ID_FMT 0x20746d66 #define ID_DATA 0x61746164
struct riff_wave_header { uint32_t riff_id; uint32_t riff_sz; uint32_t wave_id; };
struct chunk_header { uint32_t id; uint32_t sz; };
struct chunk_fmt { uint16_t audio_format; uint16_t num_channels; uint32_t sample_rate; uint32_t byte_rate; uint16_t block_align; uint16_t bits_per_sample; };
static char *sndDataBuffer; static uint32_t sndReadSize; static uint32_t sampleRate; static uint32_t audioFormat;
using namespace android;
#define TEST_FILE_PATH "/data/audio/test_ringtone.wav"
int loadSoundFile(const char *path) { struct riff_wave_header riff_wave_header; struct chunk_header chunk_header; struct chunk_fmt chunk_fmt; int more_chunks = 1;
int file_pos_cur,file_pos_end; int buffer_size;
FILE *sndFile;
sndFile = fopen(path, "rb"); if (!sndFile) { ALOGE("Unable to open file '%s'\n", path); return -1; }
fread(&riff_wave_header, sizeof(riff_wave_header), 1, sndFile); if ((riff_wave_header.riff_id != ID_RIFF) || (riff_wave_header.wave_id != ID_WAVE)) { ALOGE("Error: '%s' is not a riff/wave file\n", path); fclose(sndFile); return -1; }
do { fread(&chunk_header, sizeof(chunk_header), 1, sndFile);
switch (chunk_header.id) { case ID_FMT: fread(&chunk_fmt, sizeof(chunk_fmt), 1, sndFile); /* If the format header is larger, skip the rest */ if (chunk_header.sz > sizeof(chunk_fmt)) fseek(sndFile, chunk_header.sz - sizeof(chunk_fmt), SEEK_CUR); break; case ID_DATA: /* Stop looking for chunks */ more_chunks = 0; break; default: /* Unknown chunk, skip bytes */ fseek(sndFile, chunk_header.sz, SEEK_CUR); } } while (more_chunks);
if (chunk_fmt.bits_per_sample == 32) { audioFormat = AUDIO_FORMAT_PCM_32_BIT; } else if (chunk_fmt.bits_per_sample == 16) { audioFormat = AUDIO_FORMAT_PCM_16_BIT; } sampleRate = chunk_fmt.sample_rate; file_pos_cur = ftell(sndFile); fseek(sndFile, 0, SEEK_END); file_pos_end = ftell(sndFile);
buffer_size = file_pos_end - file_pos_cur; if (sndDataBuffer) { free(sndDataBuffer); sndDataBuffer = NULL; } sndDataBuffer = (char*)malloc(buffer_size); if (!sndDataBuffer) { ALOGE("Unable to allocate %d bytes\n", buffer_size); fclose(sndFile); return -1; } fseek(sndFile, file_pos_cur, SEEK_SET); sndReadSize = fread(sndDataBuffer, 1, buffer_size, sndFile); if(sndReadSize <= 0) { ALOGE("Unable to allocate %d bytes\n", buffer_size); fclose(sndFile); return -1; } fclose(sndFile); return 0; }
int playSound(char *sndDataBuffer, unsigned int sndDataSize, uint32_t sampleRate, uint32_t format) { int res; sp<AudioTrack> audioTrack = NULL; if ((sndDataBuffer == NULL) || !sndDataSize) return -1;
ALOGD("samplerate: %d, format: %d\n", sampleRate, format); sp<MemoryHeapBase> mMemHeap = new MemoryHeapBase(sndDataSize, 0, "AudioTrack Heap Base"); sp<MemoryBase> mMemBase = new MemoryBase(mMemHeap, 0, sndDataSize); memcpy(mMemHeap->getBase(), sndDataBuffer, sndDataSize);
audioTrack = new AudioTrack(); res = audioTrack->set(AUDIO_STREAM_MUSIC, sampleRate, (audio_format_t)format, AUDIO_CHANNEL_OUT_STEREO, 0, AUDIO_OUTPUT_FLAG_PRIMARY, NULL, NULL, 0, mMemBase //sp<IMemory>& sharedBuffer );
ALOGD("audioTrack->set() res=%d\n", res);
audioTrack->setVolume(1.0, 1.0); audioTrack->start();
#if 0 while(!audioTrack->stopped()) //怎样获取停止状态的???没用..... { sleep(1); } #endif
sleep(5); audioTrack.clear(); return 0; }
int main(int argc, char **argv) { char file_path[128] = {0}; sndDataBuffer = NULL;
strcpy(file_path, TEST_FILE_PATH); if (argc > 1) strcpy(file_path, (char *)(argv[1])); ALOGD("file: %s", file_path); if (!loadSoundFile(file_path)) { playSound(sndDataBuffer, sndReadSize, sampleRate, audioFormat); }
if (sndDataBuffer != NULL) { free(sndDataBuffer); sndDataBuffer = NULL; } return 0; } |
Android.mk
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS) LOCAL_C_INCLUDES := \ frameworks/av/include/
LOCAL_SRC_FILES:= test.cpp
LOCAL_SHARED_LIBRARIES := \ libcutils \ libutils \ libbinder \ libmedia \ liblog
LOCAL_MODULE_TAGS := eng LOCAL_MODULE:= audiotrack_sharebuffer_test include $(BUILD_EXECUTABLE) |
可以看到,真正的使用接口就是下面的:
从字面上理解是先分配堆内存,再在堆内存上分配实现内存,但是实际代码的操作明显就是一样的操作。为什么这样写接口,而不是一步封出来??而要有个BnMemoryHeap ->BnMemory的转换??