⑴ android系統app frameworks層,hal層,core libs代碼編譯之後在哪個鏡像里
Google提供的Android包含了原始Android的目標機代碼,主機編譯工具、模擬環境,的代碼包經過解壓後(這里是Android2.2的源碼包),源代碼的第一層目錄結構如下:
|-- Makefile
|-- bionic (bionic C庫)
|-- bootable (啟動引導相關代碼)
|-- build (存放系統編譯規則及generic等基礎開發包配置)
|-- cts (Android兼容性測試套件標准)
|-- dalvik (dalvik java虛擬機)
|-- development (應用程序開發相關)
|-- external (android使用的一些開源的模組)
|-- frameworks (核心框架——java及C++語言)
|-- hardware (主要保護硬解適配層HAL代碼)
|-- libcore
|-- ndk
|-- device
|-- out (編譯完成後的代碼輸出與此目錄)
|-- packages (應用程序包)
|-- prebuilt (x86和arm架構下預編譯的一些資源)
|-- sdk (sdk及模擬器)
|-- system (文件系統庫、應用及組件——C語言)
`-- vendor (廠商定製代碼)
bionic 目錄
|-- libc (C庫)
| |-- arch-arm (ARM架構,包含系統調用匯編實現)
| |-- arch-x86 (x86架構,包含系統調用匯編實現)
| |-- bionic (由C實現的功能,架構無關)
| |-- docs (文檔)
| |-- include (頭文件)
| |-- inet
| |-- kernel (Linux內核中的一些頭文件)
| |-- netbsd (?netbsd系統相關,具體作用不明)
| |-- private (?一些私有的頭文件)
| |-- stdio (stdio實現)
| |-- stdlib (stdlib實現)
| |-- string (string函數實現)
| |-- tools (幾個工具)
| |-- tzcode (時區相關代碼)
| |-- unistd (unistd實現)
| `-- zoneinfo (時區信息)
|-- libdl (libdl實現,dl是動態鏈接,提供訪問動態鏈接庫的功能)
|-- libm (libm數學庫的實現,)
| |-- alpha (apaha架構)
| |-- amd64 (amd64架構)
| |-- arm (arm架構)
| |-- bsdsrc (?bsd的源碼)
| |-- i386 (i386架構)
| |-- i387 (i387架構?)
| |-- ia64 (ia64架構)
| |-- include (頭文件)
| |-- man (數學函數,後綴名為.3,一些為freeBSD的庫文件)
| |-- powerpc (powerpc架構)
| |-- sparc64 (sparc64架構)
| `-- src (源代碼)
|-- libstdc++ (libstdc++ C++實現庫)
| |-- include (頭文件)
| `-- src (源碼)
|-- libthread_db (多線程程序的調試器庫)
| `-- include (頭文件)
`-- linker (動態鏈接器)
`-- arch (支持arm和x86兩種架構)
bootable 目錄
|-- bootloader (適合各種bootloader的通用代碼)
| `-- legacy (估計不能直接使用,可以參考)
| |-- arch_armv6 (V6架構,幾個簡單的匯編文件)
| |-- arch_msm7k (高通7k處理器架構的幾個基本驅動)
| |-- include (通用頭文件和高通7k架構頭文件)
| |-- libboot (啟動庫,都寫得很簡單)
| |-- libc (一些常用的c函數)
| |-- nandwrite (nandwirte函數實現)
| `-- usbloader (usbloader實現)
|-- diskinstaller (android鏡像打包器,x86可生產iso)
`-- recovery (系統恢復相關)
|-- edify (升級腳本使用的edify腳本語言)
|-- etc (init.rc恢復腳本)
|-- minui (一個簡單的UI)
|-- minzip (一個簡單的壓縮工具)
|-- mttils (mtd工具)
|-- res (資源)
| `-- images (一些圖片)
|-- tools (工具)
| `-- ota (OTA Over The Air Updates升級工具)
`-- updater (升級器)
build目錄
|-- core (核心編譯規則)
|-- history (歷史記錄)
|-- libs
| `-- host (主機端庫,有android 「cp」功能替換)
|-- target (目標機編譯對象)
| |-- board (開發)
| | |-- emulator (模擬器)
| | |-- generic (通用)
| | |-- idea6410 (自己添加的)
| | `-- sim (最簡單)
| `-- proct (開發對應的編譯規則)
| `-- security (密鑰相關)
`-- tools (編譯中主機使用的工具及腳本)
|-- acp (Android "acp" Command)
|-- apicheck (api檢查工具)
|-- applypatch (補丁工具)
|-- apriori (預鏈接工具)
|-- atree (tree工具)
|-- bin2asm (bin轉換為asm工具)
|-- check_prereq (檢查編譯時間戳工具)
|-- dexpreopt (模擬器相關工具,具體功能不明)
|-- droiddoc (?作用不明,java語言,網上有人說和JDK5有關)
|-- fs_config (This program takes a list of files and directories)
|-- fs_get_stats (獲取文件系統狀態)
|-- iself (判斷是否ELF格式)
|-- isprelinked (判斷是否prelinked)
|-- kcm (按鍵相關)
|-- lsd (List symbol dependencies)
|-- releasetools (生成鏡像的工具及腳本)
|-- rgb2565 (rgb轉換為565)
|-- signapk (apk簽名工具)
|-- soslim (strip工具)
`-- zipalign (zip archive alignment tool)
dalvik目錄 dalvik虛擬機
.
|-- dalvikvm (main.c的目錄)
|-- dexmp (dex反匯編)
|-- dexlist (List all methods in all concrete classes in a DEX file.)
|-- dexopt (預驗證與優化)
|-- docs (文檔)
|-- dvz (和zygote相關的一個命令)
|-- dx (dx工具,將多個java轉換為dex)
|-- hit (?java語言寫成)
|-- libcore (核心庫)
|-- libcore-disabled (?禁用的庫)
|-- libdex (dex的庫)
|-- libnativehelper (Support functions for Android's class libraries)
|-- tests (測試代碼)
|-- tools (工具)
`-- vm (虛擬機實現)
development 目錄 (開發者需要的一些常式及工具)
|-- apps (一些核心應用程序)
| |-- BluetoothDebug (藍牙調試程序)
| |-- CustomLocale (自定義區域設置)
| |-- Development (開發)
| |-- Fallback (和語言相關的一個程序)
| |-- FontLab (字型檔)
| |-- GestureBuilder (手勢動作)
| |-- NinePatchLab (?)
| |-- OBJViewer (OBJ查看器)
| |-- SdkSetup (SDK安裝器)
| |-- SpareParts (高級設置)
| |-- Term (遠程登錄)
| `-- launchperf (?)
|-- build (編譯腳本模板)
|-- cmds (有個monkey工具)
|-- data (配置數據)
|-- docs (文檔)
|-- host (主機端USB驅動等)
|-- ide (集成開發環境)
|-- ndk (本地開發套件——c語言開發套件)
|-- pdk (Plug Development Kit)
|-- samples (演示程序)
| |-- AliasActivity ()
| |-- ApiDemos (API演示程序)
| |-- BluetoothChat (藍牙聊天)
| |-- BrowserPlugin (瀏覽器插件)
| |-- BusinessCard (商業卡)
| |-- Compass (指南針)
| |-- ContactManager (聯系人管理器)
| |-- CubeLiveWall** (動態壁紙的一個簡單常式)
| |-- FixedGridLayout (像是布局)
| |-- GlobalTime (全球時間)
| |-- HelloActivity (Hello)
| |-- Home (Home)
| |-- JetBoy (jetBoy游戲)
| |-- LunarLander (貌似又是一個游戲)
| |-- MailSync (同步)
| |-- MultiResolution (多解析度)
| |-- MySampleRss (RSS)
| |-- NotePad (記事本)
| |-- RSSReader (RSS閱讀器)
| |-- SearchableDictionary (目錄搜索)
| |-- **JNI (JNI常式)
| |-- SkeletonApp (空殼APP)
| |-- Snake (snake程序)
| |-- SoftKeyboard (軟鍵盤)
| |-- Wiktionary (?維基)
| `-- Wiktionary**(?維基常式)
|-- scripts (腳本)
|-- sdk (sdk配置)
|-- simulator (?模擬器)
|-- testrunner (?測試用)
`-- tools (一些工具)
⑵ 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