本系列首篇提出 J2ME 音訊串流的核心架構:雙 Player 交替預載與播放,並討論伺服器端分塊傳輸協定與 WAV/MP3 格式適用性。
Player 實例:Player A、Player B。HttpConnection 向伺服器請求第一塊資料(建議 18 KB)。realize()/prefetch() → start() 播放。realize()/prefetch() 待命。END_OF_MEDIA 事件 → 切換 Player B 播放,同時請求第三塊給 Player A。裝置必須支援兩個 Player 同時處於 prefetched 狀態。 實測 Nokia 7610 (S40) 符合條件:兩執行緒各持一 Player,同步完成 prefetch() 後等待切換信號。
public void playerUpdate(Player player, String event, Object data) {
if (event == PlayerListener.END_OF_MEDIA) {
try {
stopGauge(); // 停止進度條
playForeground(); // 切換到另一 Player 播放
} catch (Exception e) { e.printStackTrace(); }
}
}
Client 端每次請求一塊,Server 依序回應:
// Server 端 Java 伪碼
public void transfer(DataOutputStream out) {
byte[] header = extractHeader(audio); // WAV 44 bytes / MP3 32 bytes (假設)
int chunkSize = 50058; // 含 header 的塊大小
byte[] chunk = new byte[chunkSize];
// 第一塊:header + 前 chunkSize-header 音訊資料
System.arraycopy(audio, 0, chunk, 0, chunkSize);
writeChunk(out, chunk);
// 後續塊:每塊前面加上 header,確保 Player 可獨立解碼
while (hasMoreData) {
buildChunkWithHeader(chunk, header, nextAudioData);
writeChunk(out, chunk);
}
}
void writeChunk(DataOutputStream out, byte[] chunk) {
out.writeInt(chunk.length); // 先寫長度
out.write(chunk); // 再寫資料
}
| 格式 | 支援分段 | 每段需加 Header | 備註 |
|---|---|---|---|
| WAV (PCM) | 是 | 是 (44 bytes 標準 RIFF header) | 原始資料,極易切割 |
| MP3 | 視編碼器而定 | 可能需要 (側資訊/bit reservoir) | CBR 較易切,VBR 需解析幀頭 |
| AMR | 是 | 是 (單幀模式含 header) | 行動裝置首選,框架自動處理 |
| AAC/MP4 | 否 | 複雜 (需 moov/mdat 解析) | 不建議手工分段 |
作者早期 VC++ TTS 系統即用此法:WAV header 固定在前,後續純 PCM 資料;每次取資料前加 header 送聲卡播放,極低延遲。J2ME 移植邏輯完全一致。
此架構為後續 AMR 最佳化、Podcast 章節化、即時轉碼閘道等進階主題奠基。