/*
* myLib - https://github.com/taktod/myLib
* Copyright (c) 2014 ttProject. All rights reserved.
*
* Licensed under The MIT license.
*/
package com.ttProject.packet.mpegts.nw;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import org.apache.log4j.Logger;
import com.ttProject.media.IAudioData;
import com.ttProject.media.aac.Frame;
import com.ttProject.media.aac.FrameAnalyzer;
import com.ttProject.media.aac.IFrameAnalyzer;
import com.ttProject.media.aac.frame.Aac;
import com.ttProject.media.mpegts.packet.Pes;
import com.ttProject.nio.channels.ByteReadChannel;
import com.ttProject.nio.channels.IReadChannel;
/**
* 音声データ保持オブジェクト
* 音声データは、映像データと完全に同期させるのが難しいので、いったんpesからaacもしくはmp3のframeに分解後再構築させることにします。
* @author taktod
*/
public class AudioData extends MediaData {
/** ロガー */
@SuppressWarnings("unused")
private final Logger logger = Logger.getLogger(AudioData.class);
/** 分解前のpesデータリスト(1パケット分だけ保持する形) */
private final List<Pes> pesList = new ArrayList<Pes>();
/** 分解後のIAudioDataリスト */
private final List<IAudioData> audioDataList = new ArrayList<IAudioData>();
/** 経過サンプル数 */
private long counter = 0;
/** 転送済みのサンプル数 */
private long sendedCounter = 0;
/** データ開始位置のpts */
private long startPos = -1;
/** データのサンプルレート */
private int sampleRate = 44100;
/**
* 保持データリストのサイズを参照して確認したかった。
* @return
*/
public int getListCount() {
return audioDataList.size();
}
/**
* pesの中身を解析します
*/
@Override
public void analyzePes(Pes pes) {
if(!checkPes(pes)) {
return;
}
if(startPos == -1) {
// 開始位置のpts値を保存しておく。(ずれ分)
startPos = pes.getPts().getPts();
}
// 自分用のデータなので対処しておく。
// 音声データのpesは分解して、aacもしくはmp3に変更してしまっておく。
// adaptationFieldのrandomAccessIndicator
if(pes.isPayloadUnitStart()) {
ByteBuffer buffer = ByteBuffer.allocate(188 * pesList.size());
while(pesList.size() > 0) {
Pes p = pesList.remove(0);
buffer.put(p.getRawData());
}
buffer.flip(); // aacもしくはmp3として分解できそうなデータはそろった。あとは分解して、配列に突っ込んでおくだけ。
switch(getCodecType()) {
case AUDIO_AAC: // aacの場合
IReadChannel bufferChannel = null;
try {
bufferChannel = new ByteReadChannel(buffer);
IFrameAnalyzer analyzer = new FrameAnalyzer();
Frame frame = null;
while((frame = analyzer.analyze(bufferChannel)) != null) {
if(frame instanceof Aac) {
Aac aac = (Aac)frame;
counter += aac.getSampleNum();
audioDataList.add(aac);
}
}
}
catch(Exception e) {
e.printStackTrace();
}
finally {
if(bufferChannel != null) {
try {
bufferChannel.close();
}
catch(Exception e) {
}
bufferChannel = null;
}
}
break;
case AUDIO_MPEG1: // mp3の場合
/* try {
IFrameAnalyzer analyzer = new FrameAnalyzer();
Frame frame = null;
while((frame = analyzer.analyze(bufferChannel)) != null) {
if(frame instanceof Aac) {
Aac aac = (Aac)frame;
counter += aac.getSampleNum();
audioDataList.add(aac);
}
}
}
catch(Exception e) {
e.printStackTrace();
}*/
default:
throw new RuntimeException("扱うことができないmpegtsの音声コーデックです。");
}
}
pesList.add(pes);
}
/**
* 先頭データを取り出す。
* @return
*/
public IAudioData shift() {
if(audioDataList.size() == 0)
return null;
IAudioData audioData = audioDataList.remove(0);
sendedCounter += audioData.getSampleNum();
return audioData;
}
/**
* 先頭にデータを追加
* @param audioData
*/
public void unshift(IAudioData audioData) {
if(audioData == null) {
return;
}
sendedCounter -= audioData.getSampleNum(); // 戻すサンプル数
audioDataList.add(0, audioData);
}
/**
* 現在保持しているデータの終端pts値
* @return
*/
@Override
public long getLastDataPts() {
return startPos + (long)(counter * (90000D / sampleRate));
}
/**
* 現在保持しているデータの先頭pts値
* @return
*/
@Override
public long getFirstDataPts() {
return startPos + (long)(sendedCounter * (90000D / sampleRate));
}
/**
* ptsにしたときの現在転送済みのaudioデータ料を応答します。
* @return
*/
public long getSendedDataPts() {
return startPos + (long)(sendedCounter * (90000D / 44100D));
}
}