1. android开发的技术层次是指哪些
开发一个程序,android的系统框架是层层相扣,不能分开的。
应用程序层:
这个层主要指的就是用java语言编写的运行在虚拟机上的程序,Google在最开始时就 在android系统中捆绑了一些核心的应用(核心应用的编写必须使用应用层序框架层的API框架),例如你android手机中SMS发送短消息的程序,通讯录等等。
应用程序框架层:
指的就是开发时所需要的API框架,开发人员是在遵守该框架的原则上,调用他们的,开发自己所需要的程序。
系统运行库层:
用于支持应用框架层的各个组件的。就是说当开发人员使用android应用框架层时,android系统会通过一些C/C++库来支持对我们使用的各个组件,使其能更好地为开发者服务。
linux核心层:
android的核心系统服务如安全性、内存管理、进程管理、网络协议栈和驱动模型等都依赖于Linux2.6内核,Linux内核同时也作为硬件和软件栈之间的抽象层。
因此,你开发的程序是运行在应用程序层,开发中写代码调用的包,是基于应用框架层,而在应用框架中的各个组件是需要系统运行库的支持的,例如,你要登陆查看的信息就必须访问到SQLite数据库,SQLite就位于系统运行库,再有,你登入要输入信息,肯定需要键盘驱动的支持,而各种驱动是依赖Linux内核的。
2. android多媒体开发,怎么改视频分辨率
改分辨率指的是什么?电脑也不能直接改吧?
3. android.media.AsyncPlayer这个类应该怎么用
代码结构:
Open Core 的代码在Android 代码的 External/Opencore 目录中 。这个目录是OpenCore
的根目录,其中包含的子目录如下所示 :
android :这里面是一个上层的库,它实现了一个为Android 使用的音视频采集,播放的接口,和DRM 数字版权管理的接口实现。
baselibs :包含数据结构和线程安全等内容的底层库
codecs_v2 :音视频的编解码器,基于 OpenMAX 实现
engines :核心部分 ,多媒体 引擎的实现
extern_libs_v2 :包含了 khronos 的 OpenMAX 的头文件
fileformats :文件格式的解析( parser )工具
nodes :提供一些PVMF 的NODE ,主要是编解码和文件解析方面的。
oscl :操作系统兼容库
pvmi : 输入输出控制的抽象接口
protocols :主要是与网络相关的 RTSP 、 RTP 、 HTTP 等协议 的相关内容
pvcommon : pvcommon 库文件的 Android.mk 文件,没有源文件。
pvplayer : pvplayer 库文件的 Android.mk 文件,没有源文件。
pvauthor : pvauthor 库文件的 Android.mk 文件,没有源文件。
tools_v2 :编译工具以及一些可注册的模块。
本文主要介绍Android MediaPlayer的架构,主要由OpenCore 里的PV Player来实现的。
1.概述
Android的MediaPlayer包含了Audio和Video的播放功能,Music和Video两个应用程序都是调用MediaPlayer实现的。
代码主要分布在以下的目录中:
JAVA程序的路径:
packages/apps/Music/src/com/android/music/
JAVA类的路径:
frameworks/base/media/java/android/media/MediaPlayer.java
JAVA本地调用部分(JNI):
frameworks/base/media/jni/android_media_MediaPlayer.cpp
编译为 libmedia_jni.so
头文件:
frameworks/base/include/media/
多媒体库:
frameworks/base/media/libmedia/
编译为 libmedia.so
多媒体服务:
frameworks/base/media/libmediaplayerservice/
编译为 libmediaplayerservice.so
具体实现:
external/opencore/
编译为 libopencoreplayer.so
libopencoreplayer.so是主要的实现部分,其他的库基本上都是在其上建立的封装和为建立进程间通讯的机制。
2.框架
运行的时候,大致可以分成Client和Server两个部分,分别在两个进程中运行,使用Binder机制实现IPC通讯。从框架结构上来看,IMediaPlayerService.h、IMediaPlayerClient.h和MediaPlayer.h三个类定义了MeidaPlayer的接口和架构,MediaPlayerService.cpp和mediaplayer.cpp两个文件用于MeidaPlayer架构的实现,MeidaPlayer的具体功能在PVPlayer(库libopencoreplayer.so)中的实现。
2.1 IMediaPlayerClient.h
描述一个MediaPlayer客户端的接口
class IMediaPlayerClient: public IInterface
{
public:
DECLARE_META_INTERFACE(MediaPlayerClient);
virtual void notify(int msg, int ext1, int ext2) = 0;
};
class BnMediaPlayerClient: public BnInterface
{
public:
virtual status_t onTransact( uint32_t code,
const Parcel& data,
Parcel* reply,
uint32_t flags = 0);
};
在定义中,IMediaPlayerClient类继承IInterface,并定义了一个MediaPlayer客户端的接口,BnMediaPlayerClient继承了BnInterface,这是为基于Android的基础类Binder机制实现在进程通讯而构建的。事实上,根据BnInterface类模版的定义,BnInterface类相当于双继承了BnInterface和ImediaPlayerClient,这是Android一种常用的定义方式。
2.2 mediaplayer.h
对外的接口类,它最主要是定义了一个MediaPlayer类:
class MediaPlayer : public BnMediaPlayerClient
{
public:
MediaPlayer();
~MediaPlayer();
void onFirstRef();
void disconnect();
status_t setDataSource(const char *url);
status_t setDataSource(int fd, int64_t offset, int64_t length);
status_t setVideoSurface(const sp& surface);
status_t setListener(const sp& listener);
status_t prepare();
status_t prepareAsync();
status_t start();
status_t stop();
status_t pause();
bool isPlaying();
status_t getVideoWidth(int *w);
status_t getVideoHeight(int *h);
status_t seekTo(int msec);
status_t getCurrentPosition(int *msec);
status_t getDuration(int *msec);
status_t reset();
status_t setAudioStreamType(int type);
status_t setLooping(int loop);
status_t setVolume(float leftVolume, float rightVolume);
void notify(int msg, int ext1, int ext2);
static sp decode(const char* url, uint32_t *pSampleRate, int*
pNumChannels);
static sp decode(int fd, int64_t offset, int64_t length, uint32_t
*pSampleRate, int* pNumChannels);
//……
}
从接口中可以看出MediaPlayer类刚好实现了一个MediaPlayer的基本操作,例如播放(start)、停止(stop)、暂停(pause)等。
另外的一个类DeathNotifier在MediaPlayer类中定义,它继承了IBinder类中的DeathRecipient类:
class DeathNotifier: public IBinder:: DeathRecipient
{
public:
DeathNotifier() {}
virtual ~DeathNotifier();
virtual void binderDied(const wp& who);
};
事实上,MediaPlayer类正是间接地继承了IBinder,而MediaPlayer:: DeathNotifier类继承了IBinder::
DeathRecipient,这都是为了实现进程间通讯而构建的。
2.3 IMediaPlayer.h
主要的的内容是一个实现MediaPlayer功能的接口:
class IMediaPlayer: public IInterface
{
public:
DECLARE_META_INTERFACE(MediaPlayer);
virtual void disconnect() = 0;
virtual status_t setVideoSurface(const sp& surface) = 0;
virtual status_t prepareAsync() = 0;
virtual status_t start() = 0;
virtual status_t stop() = 0;
virtual status_t pause() = 0;
virtual status_t isPlaying(bool* state) = 0;
virtual status_t getVideoSize(int* w, int* h) = 0;
virtual status_t seekTo(int msec) = 0;
virtual status_t getCurrentPosition(int* msec) = 0;
virtual status_t getDuration(int* msec) = 0;
virtual status_t reset() = 0;
virtual status_t setAudioStreamType(int type) = 0;
virtual status_t setLooping(int loop) = 0;
virtual status_t setVolume(float leftVolume, float rightVolume) = 0;
};
class BnMediaPlayer: public BnInterface
{
public:
virtual status_t onTransact( uint32_t code,
const Parcel& data,
Parcel* reply,
uint32_t flags = 0);
};
在IMediaPlayer类中,主要定义MediaPlayer的功能接口,这个类必须被继承才能够使用。值得注意的是,这些接口和MediaPlayer类的接口有些类似,但是它们并没有直接的关系。事实上,在MediaPlayer类的各种实现中,一般都会通过调用IMediaPlayer类的实现类来完成。
2.4 头文件IMediaPlayerService.h
描述一个MediaPlayer的服务,定义方式如下所示:
class IMediaPlayerService: public IInterface
{
public:
DECLARE_META_INTERFACE(MediaPlayerService);
virtual sp create(pid_t pid, const
sp& client, const char* url) = 0;
virtual sp create(pid_t pid, const
sp& client, int fd, int64_t offset, int64_t length) =
0;
virtual sp decode(const char* url, uint32_t *pSampleRate, int*
pNumChannels) = 0;
virtual sp decode(int fd, int64_t offset, int64_t length, uint32_t
*pSampleRate, int* pNumChannels) = 0;
};
class BnMediaPlayerService: public BnInterface
{
public:
virtual status_t onTransact( uint32_t code,
const Parcel& data,
Parcel* reply,
uint32_t flags = 0);
};
由于有纯虚函数,IMediaPlayerService
以及BnMediaPlayerService必须被继承实现才能够使用,在IMediaPlayerService定义的create和decode等接口,事实上是必须被继承者实现的内容。注意,create的返回值的类型是sp,这个IMediaPlayer正是提供实现功能的接口。
3 实现
3.1 App
在packages/apps/Music/src/com/android/music/里的MediaPlaybackService.java文件中,包含了对MediaPlayer的调用。
在MediaPlaybackService.java中包含对包的引用:
import android.media.MediaPlayer;
在MediaPlaybackService类的内部,定义了MultiPlayer类:
private class MultiPlayer {
private MediaPlayer mMediaPlayer = new MediaPlayer();
}
MultiPlayer类中使用了MediaPlayer类,其中有一些对这个MediaPlayer的调用,调用的过程如下所示:
mMediaPlayer.reset();
mMediaPlayer.setDataSource(path);
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
reset,setDataSource和setAudioStreamType等接口就是通过JAVA本地调用(JNI)来实现的。
3.2 Jni
在frameworks/base/media/jni/android_media_MediaPlayer.cpp中实现,其中android_media_MediaPlayer_reset函数的实现如下所示:
static void android_media_MediaPlayer_reset(JNIEnv *env, jobject thiz)
{
sp mp = getMediaPlayer(env, thiz);
if (mp == NULL ) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
return;
}
process_media_player_call( env, thiz, mp->reset(), NULL, NULL );
}
先获取一个MediaPlayer指针,通过对它的调用来实现实际的功能。
register_android_media_MediaPlayer用于将gMethods注册为的类"android/media/MediaPlayer",其实现如下所示。
static int register_android_media_MediaPlayer(JNIEnv *env)
{
jclass clazz;
clazz = env->FindClass("android/media/MediaPlayer");
// ......
return AndroidRuntime::registerNativeMethods(env,
"android/media/MediaPlayer", gMethods, NELEM(gMethods));
}
"android/media/MediaPlayer"对应JAVA的类android.media.MediaPlayer。
3.3 libmedia.so
frameworks/base/media/libmedia/mediaplayer.cpp文件实现mediaplayer.h提供的接口,其中一个重要的片段如下所示:
const sp& MediaPlayer::getMediaPlayerService()
{
Mutex::Autolock _l(mServiceLock);
if (mMediaPlayerService.get() == 0) {
sp sm = defaultServiceManager();
sp binder;
do {
binder = sm->getService(String16("media.player"));
if (binder != 0)
break;
LOGW("MediaPlayerService not published, waiting...");
usleep(500000); // 0.5 s
} while(true);
if (mDeathNotifier == NULL) {
mDeathNotifier = new DeathNotifier();
}
binder->linkToDeath(mDeathNotifier);
mMediaPlayerService = interface_cast(binder);
}
LOGE_IF(mMediaPlayerService==0, "no MediaPlayerService!?");
return mMediaPlayerService;
}
其中最重要的一点是binder =
sm->getService(String16("media.player"));这个调用用来得到一个名称为"media.player"的服务,这个调用返回值的类型为IBinder,根据实现将其转换成类型IMediaPlayerService使用。
一个具体的函数setDataSource如下所示:
status_t MediaPlayer::setDataSource(const char *url)
{
LOGV("setDataSource(%s)", url);
status_t err = UNKNOWN_ERROR;
if (url != NULL) {
const sp& service(getMediaPlayerService());
if (service != 0) {
sp player(service->create(getpid(), this, url));
err = setDataSource(player);
}
}
return err;
}
4. 什么是android的框架开发
Android开发框架介绍
编辑文档
学分 +2
开发框架方面包含基本的应用功能开发、数据存储、网络访问这三大块:
一、应用方面
一般而言一个标准的Android程序由如下4部分组成即Activity、Broadcast Intent Receiver、Service、Content Provider: 1. Activity是最频繁、最基本的模块,在Android中,一个Activity就是手机上一屏,相当于一个网页一样,所不同的是,每个Activity运行结束了,有个返回值,类似一个函数一样。Android系统会自动记录从首页到其他页面的所有跳转记录并且自动将以前的Activity压入系统堆栈,用户可以通过编程的方式删除历史堆栈中的Activity Instance。
Activity类中主要是跟界面资源文件关联起来(res/layout目录下的xml资源,也可以不含任何界面资源),内部包含控件的显示设计、界面交互设计、事件的响应设计以及数据处理设计、导航设计等application设计的方方面面。 2. Broadcast Intent Receiver
Intent提供了各种不同Activity进行跳转的机制,譬如如果从A activity跳转到B activity,使用Intent来实现如下: Intent in = new Intent(A.this, B.class); startActivity(in);
BroadcastReceiver提供了各种不同的Android应用程序进行进行进程间通讯的机制,譬如当电话呼叫来临时,可以通过BroadcastReceiver发布广播消息。对于用户而言,BroadcastReceiver是不透明的,用户无法看到这个事件,BroadcastReceiver通过NotificationManager来通知用户这些事件发生了,它既可以在资源AndroidManifest.xml中注册,也可以在代码中通过Context.registerReceiver()进行注册,只要是注册了,当事件来临的时候,即时程序没有启动,系统也在需要的时候会自动启动此应用程序;另外各应用程序很方便地通过Context.sendBroadcast()将自己的事情广播给其他应用程序;
3. Service,跟Windows当中的Service完全是一个概念,用户可以通过startService(Intent service)启动一个Service,也可通过Context.bindService来绑定一个Service.
4. Content Provider,由于Android应用程序内部的数据都是私有的,Content Provider提供了应用程序之间数据交换的机制,一个程序可以通过实现一个ContentProvider的抽象接口将自己的数据暴露出去,并且隐蔽了具体的数据存储实现,标准的ContentProvider提供了基本的CRUD(Create,Read,Update,Delete)的接口,并且实现了权限机制,保护了数据交互的安全性; 一个标准的Android应用程序的工程文件包含如下几大部分: -> Java源代码部分(包含Activity),都在src目录当中;
-> R.java文件,这个文件是Eclipse自动生成与维护的,开发者不需要修改,提供了Android对的资源全局索引; -> Android Library,这个是应用运行的Android库;
-> assets目录,这个目录里面主要用与放置多媒体等一些文件;
-> res目录,放置的是资源文件,跟VC中的资源目录基本类似,其中的drawable包含的是图片文件,layout里面包含的是布局文件,values目录里面主要包含的是字符串(strings.xml)、颜色(colors.xml)以及数组(arrays.xml)资源;
-> AndroidManifest.xml,这个文件异常重要,是整个应用的配置文件,在这个文件中,需要声明所有用到的Activity、Service、Receiver等。
5. 学习教学多媒体系统看什么书
Android凭借其开源性、优异的用户体验和极为方便的开发方式,赢得了广大用户和开发者的青睐,目前已经发展成为市场占有率很高的智能手机操作系统。 《Android驱动开发与移植实战详解》分为18章,依次讲解了Android系统的基本知识, Linux内核的基本知识,分析了Android系统的源码,深入分析HAL层的基本知识,GoldFish下的驱动、MSM内核和驱动、OMAP内核和驱动、显示系统驱动、输入系统驱动、振动器系统驱动、音频系统驱动和视频输出系统驱动,多媒体框架,传感器系统、照相机系统、Wi-Fi系统、蓝牙系统、GPS系统和电话系统的知识。在每一章中,重点介绍了与Android驱动开发相关的底层知识,并对Android源码进行了详细的分析及驱动开发实现。 《Android驱动开发与移植实战详解》适合Android程序员、研发人员及Android爱好者学习,也可以作为相关培训学校和大专院校相关专业的教学用书。
6. 应聘Android开发工程师,需要掌握哪些专业技能
任职要求:
1.Android/iphone平台手机终端软件开发精通Android开发平台及框架,一年以上实际开发经验;
2.精通Android GUI程序开发;
3.1年以上J2ME开发经验,熟悉J2ME编程;熟悉Linux环境编程优先 ;从事过嵌入式开发工作2年以上。
4.熟悉HTML/WML/HTTP,具有良好的编程思想;
5.熟悉C/C++或者Java开发语言和环境;
6.有一年以上移动终端应用软件开发经验,有Android或者iPhone开发经验尤佳;
7.基础扎实,精通常用数据结构与算法和设计能力;
8.熟悉移动终端特性和开发特点; 熟悉移动终端网络编程,了解3G\WiFi等技术;
9.熟悉C++/C#, MSSQL/MySQL数据库开发;熟悉多线程、Socket或ACE等网络通信编程技术;
10.熟悉java各种编程方法,比如多线程,jni,idl等。 熟悉JVM的运行机制,移植或者扩展过JVM到嵌入式平台者优先,比如phoneme 等。
11.精通Android平台UI开发优先;英语良好,能阅读英文资料;
工作职责:
1.负责Android平台的浏览器开发。
2.在Android手机上设计并开发应用程序或游戏;
3.Android平台框架层的维护以及扩展。
4.移植各种流行的框架体系(多媒体,蓝牙,无线)到android上负责Android项目的开发工作;
5.负责Android项目的架构设计、方案的制定;
6.跟进Android的新技术发展。纂写设计开发及实现文档;
7.根据产品功能需求设计并完成软件实现;
8.参与产品需求分析并制定技术实施方案;
7. 程序包io.vov.vitamio.utils 怎么引入
二、 简介
Vitamio 是一款 Android 平台上的全能多媒体开发框架。Vitamio 凭借其简洁易用的 API 接口赢得了全球众多开发者的青睐。到目前,全球已经有超过 1000 种应用在使用 Vitamio,覆盖用户超过 5000 万。
Vitamio 能够流畅播放720P甚至1080P高清MKV,FLV,MP4,MOV,TS,RMVB等常见格式的视频,还可以在 Android 上支持 MMS, RTSP, RTMP, HLS(m3u8) 等常见的多种视频流媒体协议,包括点播与直播。
支持 ARMv6 和 ARMv7 两种 ARM CPU,同时对 VFP, VFPv3, NEON 等指令集都做相应优化。
支持 Android 2.1+ 系统,支持超过 95% 的 Android 市场。同时 Android 2.1 之前的系统也基本支持,不过没做详细测试。
其他Vitamio的介绍参照这里。
三、使用
1、导入。下载回来后大家可能发现没有.project工程文件,可以通过File -> Import -> Android -> Existing Android Code Into Workspace来导入工程,然后改一下工程名称即可。
2、将VitamioBundle工程作为Android Library引入Demo工程使用即可。
新手无法解决这些问题建议直接使用上面的链接,导入工程即可使用。
8. 安卓Android软件开发用什么语言
Android以Java为编程语言。
操作系统与应用程序的沟通桥梁,并用分为两层:函数层(Library)和虚拟机(Virtual Machine)。 Bionic是 Android 改良libc的版本。
Android 同时包含了Webkit,所谓的Webkit 就是Apple Safari浏览器背后的引擎。Surface flinger 是就2D或3D的内容显示到屏幕上。Android使用工具链(Toolchain)为Google自制的Bionic Libc。
Android采用OpenCORE作为基础多媒体框架。OpenCORE可分7大块:PVPlayer、PVAuthor、Codec、PacketVideo Multimedia Framework(PVMF)、Operating System Compatibility Library(OSCL)、Common、OpenMAX。
(8)android多媒体框架扩展阅读:
安卓软件开发学习内容
Android基础阶段:平台架构特性(JAVA/C) Market/应用程序组件 环境搭建与部署/打包与发布 AVD/DDMS/AAPT 调试与测试 相关资源访问/资源制作 。
Activity/Service/Broadcast Receiver/Content Provider/原理(生命周期)及深层实现。
Android进阶初级:组件Widget/ 菜单Menu/ 布局Layout 详解 Xml解析(Pull/Dom/Sax)/JNI 解析SQL数据库原理, SQLite /SharedPreferences/File详解 多媒体Audio/Video/Camera 详解。
Android进阶高级:蓝牙/WIFI SMS/MMS 应用实现 深层次解析GPS原理,实现。LocationManager/LocationProvider 进行定位/跟踪/查找/趋近警告以及Geocoder正逆向编解码等技术细节。
2D图形库(Graphics/View)详解 SDCARD/传感器/手势 应用实现。
9. android框架师和androidframework工程师的区别
高级Android多媒体工程师
1.负责Android系统多媒体业务特性的需求分析、总体设计及关键模块开发实现及调试定位;
2、负责攻关解决Android AudioSystem/OpenGL/图形系统/多媒体并发等android系统级多媒体问题;
1. 具有2年以上Android多媒体系统问题解决经验;
2. 熟悉AudioSystem/OpenGL/SurfaceFlinger等具体多媒体技术
高级Android Framework工程师
1.负责Android系统Framework及内核等系统框架层的需求优化分析、优化设计及关键模块开发实现及调试定位
1.3年以上嵌入式系统 2.精通C/C++
高级Android浏览器工程师
1. 负责Android系统浏览器等移动互联业务特性的需求分析、总体设计及关键模块开发实现及调试定位;
2. 负责攻关解决Android Webkit问题及进行浏览器性能优化
1. 具有2年以上Android浏览器系统问题解决经验;
2. 熟悉Webkit系统架构及问题定位方法;
3. 熟悉javascript/css等具体技术
ANDROID高级驱动软件工程师
1、 ANDROID MediaFramework、HAL、 KERNEL中关键系统、关键外设的开发、软件优化;
2、 负责ANDROIID关键技术问题的公关,性能优化等技术工作;
3、 协助培养、管理ANDIROID底层软件开发团队
1. 4年以上LINUX嵌入式驱动软件开发经验,以及3年以上Android手机底层软件开发经验
2. 有过在芯片裸板上面进行从零开始的Linux(Android)移植工作经验;
3. 精通LINUX内核,具备多核处理器整机功耗优化的处理经验,具备基于多核的内核异常问题处理经验和有效手段;
4. 在Android系统下,有丰富的外设开发经验,并熟悉音视频多媒体框架
10. android中mediamuxer和mediacodec的区别
Android中MediaMuxer和MediaCodec用例
在Android的多媒体类中,MediaMuxer和MediaCodec算是比较年轻的,它们是JB 4.1和JB 4.3才引入的。前者用于将音频和视频进行混合生成多媒体文件。缺点是目前只能支持一个audio track和一个video track,而且仅支持mp4输出。不过既然是新生事物,相信之后的版本应该会有大的改进。MediaCodec用于将音视频进行压缩编码,它有个比较牛X的地方是可以对Surface内容进行编码,如KK 4.4中屏幕录像功能就是用它实现的。
注意它们和其它一些多媒体相关类的关系和区别:MediaExtractor用于音视频分路,和MediaMuxer正好是反过程。MediaFormat用于描述多媒体数据的格式。MediaRecorder用于录像+压缩编码,生成编码好的文件如mp4, 3gpp,视频主要是用于录制Camera preview。MediaPlayer用于播放压缩编码后的音视频文件。AudioRecord用于录制PCM数据。AudioTrack用于播放PCM数据。PCM即原始音频采样数据,可以用如vlc播放器播放。当然了,通道采样率之类的要自己设,因为原始采样数据是没有文件头的,如:
vlc --demux=rawaud --rawaud-channels 2 --rawaud-samplerate 44100 audio.pcm
回到MediaMuxer和MediaCodec这两个类,它们的参考文档见http://developer.android.com/reference/android/media/MediaMuxer.html和http://developer.android.com/reference/android/media/MediaCodec.html,里边有使用的框架。这个组合可以实现很多功能,比如音视频文件的编辑(结合MediaExtractor),用OpenGL绘制Surface并生成mp4文件,屏幕录像以及类似Camera app里的录像功能(虽然这个用MediaRecorder更合适)等。
这里以一个很无聊的功能为例,就是在一个Surface上画图编码生成视频,同时用MIC录音编码生成音频,然后将音视频混合生成mp4文件。程序本身没什么用,但是示例了MediaMuxer和MediaCodec的基本用法。本程序主要是基于两个测试程序:一个是Grafika中的SoftInputSurfaceActivity和HWEncoderExperiments。它们一个是生成视频,一个生成音频,这里把它们结合一下,同时生成音频和视频。基本框架和流程如下:
首先是录音线程,主要参考HWEncoderExperiments。通过AudioRecord类接收来自麦克风的采样数据,然后丢给Encoder准备编码:
AudioRecord audio_recorder;
audio_recorder = new AudioRecord(MediaRecorder.AudioSource.MIC,
SAMPLE_RATE, CHANNEL_CONFIG, AUDIO_FORMAT, buffer_size);
// ...
audio_recorder.startRecording();
while (is_recording) {
byte[] this_buffer = new byte[frame_buffer_size];
read_result = audio_recorder.read(this_buffer, 0, frame_buffer_size); // read audio raw data
// …
presentationTimeStamp = System.nanoTime() / 1000;
audioEncoder.offerAudioEncoder(this_buffer.clone(), presentationTimeStamp); // feed to audio encoder
}
这里也可以设置AudioRecord的回调(通过())来触发音频数据的读取。offerAudioEncoder()里主要是把audio采样数据送入音频MediaCodec的InputBuffer进行编码:
ByteBuffer[] inputBuffers = mAudioEncoder.getInputBuffers();
int inputBufferIndex = mAudioEncoder.dequeueInputBuffer(-1);
if (inputBufferIndex >= 0) {
ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
inputBuffer.clear();
inputBuffer.put(this_buffer);
...
mAudioEncoder.queueInputBuffer(inputBufferIndex, 0, this_buffer.length, presentationTimeStamp, 0);
}
下面,参考Grafika-SoftInputSurfaceActivity,并加入音频处理。主循环大体分四部分:
try {
// Part 1
prepareEncoder(outputFile);
...
// Part 2
for (int i = 0; i < NUM_FRAMES; i++) {
generateFrame(i);
drainVideoEncoder(false);
drainAudioEncoder(false);
}
// Part 3
...
drainVideoEncoder(true);
drainAudioEncoder(true);
} catch (IOException ioe) {
throw new RuntimeException(ioe);
} finally {
// Part 4
releaseEncoder();
}
第1部分是准备工作,除了video的MediaCodec,这里还初始化了audio的MediaCodec:
MediaFormat audioFormat = new MediaFormat();
audioFormat.setInteger(MediaFormat.KEY_SAMPLE_RATE, 44100);
audioFormat.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1);
...
mAudioEncoder = MediaCodec.createEncoderByType(AUDIO_MIME_TYPE);
mAudioEncoder.configure(audioFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
mAudioEncoder.start();
第2部分进入主循环,app在Surface上直接绘图,由于这个Surface是从MediaCodec中用createInputSurface()申请来的,所以画完后不用显式用queueInputBuffer()交给Encoder。drainVideoEncoder()和drainAudioEncoder()分别将编码好的音视频从buffer中拿出来(通过dequeueOutputBuffer()),然后交由MediaMuxer进行混合(通过writeSampleData())。注意音视频通过PTS(Presentation time stamp,决定了某一帧的音视频数据何时显示或播放)来同步,音频的time stamp需在AudioRecord从MIC采集到数据时获取并放到相应的bufferInfo中,视频由于是在Surface上画,因此直接用dequeueOutputBuffer()出来的bufferInfo中的就行,最后将编码好的数据送去MediaMuxer进行多路混合。
注意这里Muxer要等把audio track和video track都加入了再开始。MediaCodec在一开始调用dequeueOutputBuffer()时会返回一次INFO_OUTPUT_FORMAT_CHANGED消息。我们只需在这里获取该MediaCodec的format,并注册到MediaMuxer里。接着判断当前audio track和video track是否都已就绪,如果是的话就启动Muxer。
总结来说,drainVideoEncoder()的主逻辑大致如下,drainAudioEncoder也是类似的,只是把video的MediaCodec换成audio的MediaCodec即可。
while(true) {
int encoderStatus = mVideoEncoder.dequeueOutputBuffer(mBufferInfo, TIMEOUT_USEC);
if (encoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER) {
...
} else if (encoderStatus == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
encoderOutputBuffers = mVideoEncoder.getOutputBuffers();
} else if (encoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
MediaFormat newFormat = mAudioEncoder.getOutputFormat();
mAudioTrackIndex = mMuxer.addTrack(newFormat);
mNumTracksAdded++;
if (mNumTracksAdded == TOTAL_NUM_TRACKS) {
mMuxer.start();
}
} else if (encoderStatus < 0) {
...
} else {
ByteBuffer encodedData = encoderOutputBuffers[encoderStatus];
...
if (mBufferInfo.size != 0) {
mMuxer.writeSampleData(mVideoTrackIndex, encodedData, mBufferInfo);
}
mVideoEncoder.releaseOutputBuffer(encoderStatus, false);
if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
break;
}
}
}
第3部分是结束录制,发送EOS信息,这样在drainVideoEncoder()和drainAudioEncoder中就可以根据EOS退出内循环。第4部分为清理工作。把audio和video的MediaCodec,MediaCodec用的Surface及MediaMuxer对象释放。
最后几点注意:
1. 在AndroidManifest.xml里加上录音权限,否则创建AudioRecord对象时铁定失败:
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
2. 音视频通过PTS同步,两个的单位要一致。
3. MediaMuxer的使用要按照Constructor -> addTrack -> start -> writeSampleData -> stop 的顺序。如果既有音频又有视频,在stop前两个都要writeSampleData()过。
Code references:
Grafika: https://github.com/google/grafika
Bigflake: http://bigflake.com/mediacodec/
HWEncoderExperiments:https://github.com/OnlyInAmerica/HWEncoderExperiments/tree/audioonly/HWEncoderExperiments/src/main/java/net/openwatch/hwencoderexperiments
Android test:http://androidxref.com/4.4.2_r2/xref/cts/tests/tests/media/src/android/media/cts/
http://androidxref.com/4.4.2_r2/xref/pdk/apps/TestingCamera2/src/com/android/testingcamera2/CameraRecordingStream.java