截取android正在播放音乐的audio音频流(后台获取android音频流)
本文是做项目需求,获取android虚拟机正在播放音频,然后截取,保存成文件,获取的数据是PCM码流,可以通过ffplay播放,播放器播放不了,获取的PCM码流是解码后的原始数据。需要改动的文件是AudioTrack.cpp,路径:存放目录/android/frameworks/av/media/libmedia/需要对android源码进行编译,ssize_t Aud
本文是做项目需求,获取android虚拟机正在播放音频,然后截取,保存成文件,获取的数据是PCM码流,可以通过ffplay播放,播放器播放不了,获取的PCM码流是解码后的原始数据。需要改动的文件是AudioTrack.cpp,路径:存放目录/android/frameworks/av/media/libmedia/
需要对android源码进行编译,红色为需要添加代码:
ssize_t AudioTrack::write(const void* buffer, size_t userSize, bool blocking)
{
// 该write函数要求mTransfer必须为TRANSFER_SYNC并且不是timetrack
if (mTransfer != TRANSFER_SYNC || mIsTimed) {
return INVALID_OPERATION;
}
// 判断下flag中是否带有direct flag,如果是flag,则去掉CBLK_UNDERRUN、CBLK_LOOP_CYCLE
// CBLK_LOOP_FINAL、CBLK_BUFFER_END
if (isDirect()) {
AutoMutex lock(mLock);
int32_t flags = android_atomic_and(
~(CBLK_UNDERRUN | CBLK_LOOP_CYCLE | CBLK_LOOP_FINAL | CBLK_BUFFER_END),
&mCblk->mFlags);
// 如果此时flag还含有CBLK_INVALID flag
if (flags & CBLK_INVALID) {
return DEAD_OBJECT;
}
}
if (ssize_t(userSize) < 0 || (buffer == NULL && userSize != 0)) {
// Sanity-check: user is most-likely passing an error code, and it would
// make the return value ambiguous (actualSize vs error).
ALOGE("AudioTrack::write(buffer=%p, size=%zu (%zd)", buffer, userSize, userSize);
return BAD_VALUE;
}
size_t written = 0;
Buffer audioBuffer;
// 进入循环写,直到userSize写完
while (userSize >= mFrameSize) {
audioBuffer.frameCount = userSize / mFrameSize;
// 获取buffer空间,blocking默认为true,所以选择ClientProxy::kForever
status_t err = obtainBuffer(&audioBuffer,
blocking ? &ClientProxy::kForever : &ClientProxy::kNonBlocking);
// 获取失败
if (err < 0) {
// 如果之前已经有写入一定的数据,则跳出,否则返回错误的
if (written > 0) {
break;
}
return ssize_t(err);
}
// audioBuffer.size记录的是获取到的空间的大小,然后进行数据拷贝,变量更新
size_t toWrite = audioBuffer.size;
memcpy(audioBuffer.i8, buffer, toWrite);//copy PCM data to audioflinger,内存复制,buffer即解码后的PCM码流
// --------------------------------------------------------------------------------------------
//test::write PCM data to file
FILE *fp;
if((fp=fopen("/data/audioBuffer.txt","a+"))==NULL) { //a+表示将数据写在之前数据后面,不清除之前数据
ALOGE("failedopen (buffer=%p, size=%zu (%zd)", buffer, userSize, userSize);
}
else {
fwrite(buffer,toWrite,1, fp);
}
fclose(fp);
// --------------------------------------------------------------------------------------------
buffer = ((const char *) buffer) + toWrite;//指针跳过已经写入的数据
userSize -= toWrite;//剩余数据量
written += toWrite;//已经写入的数据量
releaseBuffer(&audioBuffer);// 释放audiobuffer
}
return written;
}
编译源码:
source build/envsetup.sh
lunch 6
make -j8 注释:-j8是跟电脑配置有关
等待编译完成。然后需要用adb命令来完成文件传递。输入命令:
emulator &
然后分如下操作:
1.虚拟机一般没有sdcard,需要自己创建一个sdcard
找到自己mksdcard命令所在目录,我的在/usr/share/adt-bundle-linux-x86_64-20140702/sdk/tools
然后通过命令: ./mksdcard -l mycard 100M /home/bruceking90/Documents/workplace/sdcard.img
然后启动带sdcard虚拟机命令:
emulator -sdcard /home/bruceking90/Documents/workplace/sdcard.img &
2.虚拟机自带播放器没有音乐,需要通过多米音乐下载或者你通过其他渠道下载mp3音乐,
通过命令:push /home/bruceking90/Downloads/Prison_break.mp3 /sdcard/Music
3.在ubuntu下新建一个文件,准备写入数据用的文件,与底层写文件同名audioBuffer.txt。
然后push到写数据到文件的目录下/data/audioBuffer.txt。
push /home/bruceking90/Downloads/audioBuffer.txt /data/
4.打开自带音乐播放器,播放传进去的mp3。数据就写入到audioBuffer.txt,然后通过命令将该文件穿出来,并且将后缀改为mp3:
pull /data/audioBuffer.txt push /home/bruceking90/Downloads/
mv audioBuffer.txt audioBuffer.mp3(可以手动更改)
5.验证获取音频数据对不对:原始数据可以通过一般音频分析软件得到采样率和声道数。该mp3原始采样率44k,双声道。
通过命令 ffplay -f s16le -ar 44k -ac 2 /home/bruceking90/Downloads/audioBuffer.mp3
可以正常播放。
小结遇到的问题:
1.若不能播放,在ubuntu端启动虚拟机目录下输入adb logcat,查看播放失败日志。android5.0以上,一些avc denied 即权限不够
在android目录下输入命令:adb shell
进入android机制,更改写文件限制,然后输入命令:setenforce 0
一般就可以正常播放,写数据到txt文件里。
2.“&”表示在后台运行,这样方便使用adb命令在当前终端下。当不小心关掉终端,输入emulator没反应需要重新加载环境:
source build/envsetup.sh
lunch 6
然后重新启动虚拟机 emulator -sdcard /home/bruceking90/Documents/workplace/sdcard.img &
更多推荐
所有评论(0)