package com.flazr.rtmp.reader; import nliveroid.nlr.main.LiveSettings; import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.buffer.ChannelBuffers; import org.jboss.netty.util.internal.LinkedTransferQueue; import android.os.AsyncTask; import android.util.Log; import com.flazr.rtmp.RtmpHeader; import com.flazr.rtmp.RtmpMessage; import com.flazr.rtmp.client.RealTimeMic; import com.flazr.rtmp.message.MessageType; import com.flazr.rtmp.message.MetadataAmf0; import com.flazr.util.Utils; public class SnapReader implements RtmpReader { private MetadataAmf0 metadata; private boolean isStoped = false; private RealTimeMic mAudio; private long startTime; private QuePool qTask; private byte[] encodedPicture; private LinkedTransferQueue<RtmpMessage> globalQueue; private LiveSettings liveSetting; private RealTimeMic mic; //カメラプレビューとりあえず固定取得する public SnapReader(LiveSettings liveSettings) { this.liveSetting = liveSettings; globalQueue = new LinkedTransferQueue<RtmpMessage>(); } @Override public int init(final String path) { //エラーチェック等 //FLVのプレフィックス(46 4C 56 01 05 00 00 00 09 00 00 00 00) //は必ず同じで読み取るより定数としておいてセットしたo方が速いので飛ばす final RtmpMessage metadataAtom = new CamAtom(); //メッセージタイプに渡されて、メタデータがデコードされる metadata = (MetadataAmf0) MessageType.decode(metadataAtom.getHeader(), metadataAtom.encode()); if(liveSetting == null ||mic == null)return -1; return 0; } public int startQueueTask(){ Log.d("PictureReader","startQueueTask"); if(liveSetting.isUseMic()&&mic != null&&!mic.isRecording()){ mic.startRecording();//エラーはRealTimeMicで一応している }else{ mic.setReader(SnapReader.this); } qTask = new QuePool(); qTask.execute(); return 0; } @Override public MetadataAmf0 getMetadata() { return metadata; } @Override public RtmpMessage[] getStartMessages() { return new RtmpMessage[] { metadata }; } public void setPicture(byte[] encodedPicture){ this.encodedPicture = encodedPicture; } @Override public boolean hasNext() { //とりあえずtrueを返しておく return !isStoped; } protected boolean hasPrev() { //とりあえずfalseを返しておく return false; } protected RtmpMessage prev() {//dataQueの1個前は取っておく? return null; } class QuePool extends AsyncTask<Void,Void,Void>{ private boolean ENDFLAG = true; @Override public void onCancelled(){ super.onCancelled(); ENDFLAG = false; } @Override protected Void doInBackground(Void... args){ byte[] aData = null; return null; } } @Override public RtmpMessage next() { Log.d("CamPreviewReader","next"+liveSetting.isStreamStarted()); RtmpMessage tmp; while(true){ try { tmp = globalQueue.take(); if(tmp != null){ return tmp; }else{ Thread.sleep(2); } } catch (InterruptedException e) { e.printStackTrace(); } } } @Override public void close() { if(qTask != null && qTask.getStatus() != AsyncTask.Status.FINISHED){ qTask.cancel(true); } } class CamAtom implements RtmpMessage { private final RtmpHeader header; private ChannelBuffer data; //メタデータを作成するときに呼ぶ public CamAtom() { MetadataAmf0 meta = MetadataAmf0.createMetaData(liveSetting); //タグタイプ1バイト、サイズ3バイト、タイムスタンプ3バイト、タイムスタンプ拡張1バイト、ストリームID3バイト header = meta.getHeader();//ここでヘッダーは正しく返るはず //ここでタグ内のデータがChannelBufferとして取得される→native化はここを代替してやればいい //ここでinはヘッダを読んだ後、4バイト(拡張タイムスタンプ+ストリームID)進んだ状態になって返ってくる //ここのreadでサイズ分new byte[]アロケートされている!!!!これ重いんでないか? data = meta.encode();//メタデータのデータを突っ込む byte[] ba = data.copy().toByteBuffer().array(); Log.d("PictureReader METADATA)",""+Utils.toHex(ba,0,ba.length,true)); //ファイル呼んでないので、スキップとか無し } //カメラプレビューのメインで呼ばれる public CamAtom(final byte[] av_frame) { //とりあえずここでサイズに合わせてタグのヘッダーを生成しておく //タグタイプ1バイト、サイズ3バイト、タイムスタンプ3バイト、タイムスタンプ拡張1バイト、ストリームID3バイト int size = av_frame.length; byte[] headerBytes = new byte[11]; System.arraycopy(av_frame, 0, headerBytes, 0, 11); Log.d("PictureReader","Header "+Utils.toHex(headerBytes, 0, headerBytes.length, true)); header = readHeader(headerBytes); //ここでタグ内のデータがChannelBufferとして取得される→native化はここを代替してやればいい //ここでinはヘッダを読んだ後、4バイト(拡張タイムスタンプ+ストリームID)進んだ状態になって返ってくる //ここのreadでサイズ分new byte[]アロケートされている!!!!これ重いんでないか?でもしょうがないのか? byte[] dataAlloc = new byte[size-11];//ヘッダー+Previousを引いたデータのみのサイズ System.arraycopy(av_frame, 11, dataAlloc, 0, size-11);//av_frameを11からコピーする時、サイズ-11以降はav_frame側の要素がない data = ChannelBuffers.wrappedBuffer(ChannelBuffers.BIG_ENDIAN,dataAlloc);//タグのデータを突っ込む wrapped,copied,dynamicとあるが、wrappedでいいのかわからん byte[] ba = data.copy().toByteBuffer().array(); Log.d("PictureReader","CamAtomDATA"+Utils.toHex(ba,0,ba.length,true)); } //カメラプレビューで呼ばれる public RtmpHeader readHeader(final byte[] in) { // byte[] ba = in; byte type= in[0]; // Log.d("CamPreviewAtom","readHeader"+Utils.toHex(ba,0,ba.length,true)); final MessageType messageType = MessageType.valueToEnum(type); // Log.d("CamPreviewAtom","TYPE:"+messageType.toString()+" VAL:"+new String(Utils.toHexChars(type))); //次の3バイト final int size = (in[1] & 0xff) << 16 | (in[2] & 0xff) << 8 | (in[3] & 0xff) << 0; //次の3バイト final int timestamp = (in[4] & 0xff) << 16 | (in[5] & 0xff) << 8 | (in[6] & 0xff) << 0; // 次の拡張タイムスタンプと、ストリームIDは非対応を意味する return new RtmpHeader(messageType, timestamp, size); } /** * F4vReader#next * FlvReader#next * FlvWriter#write * から呼ばれる * @return */ public ChannelBuffer write() { final ChannelBuffer out = ChannelBuffers.buffer(ChannelBuffers.BIG_ENDIAN,15 + header.getSize()); out.writeByte((byte) header.getMessageType().intValue()); out.writeMedium(header.getSize()); out.writeMedium(header.getTime()); out.writeInt(0); // 4 bytes of zeros (reserved)#next out.writeBytes(data,data.readableBytes()); out.writeInt(header.getSize() + 11); // previous tag size return out; } //FileChannelがChannelBufferとして確保されたもの //ヘッダーとして読み出すべき11バイトが入ってくる public RtmpHeader readHeader(final ChannelBuffer in) { byte[] ba = in.copy().array(); byte type= in.readByte(); final MessageType messageType = MessageType.valueToEnum(type); //次の3バイト final int size = in.readMedium(); //次の3バイト final int timestamp = in.readMedium(); in.skipBytes(4); // 拡張タイムスタンプと、ストリームIDは非対応を意味する return new RtmpHeader(messageType, timestamp, size); } @Override public RtmpHeader getHeader() { return header; } @Override public ChannelBuffer encode() { return data; } @Override public void decode(final ChannelBuffer in) { data = in; } @Override public String toString() { final StringBuilder sb = new StringBuilder(); sb.append(header); sb.append(" data: ").append(data); return sb.toString(); } @Override public MessageType getMessageType() {//AbstractMessage無くす為に新たに追加 return null; } } class MicAtom implements RtmpMessage { private final RtmpHeader header; private ChannelBuffer data; //メタデータを作成するときに呼ぶ public MicAtom() { MetadataAmf0 meta = MetadataAmf0.createMetaData(liveSetting); //タグタイプ1バイト、サイズ3バイト、タイムスタンプ3バイト、タイムスタンプ拡張1バイト、ストリームID3バイト header = meta.getHeader();//ここでヘッダーは正しく返るはず //ここでタグ内のデータがChannelBufferとして取得される→native化はここを代替してやればいい //ここでinはヘッダを読んだ後、4バイト(拡張タイムスタンプ+ストリームID)進んだ状態になって返ってくる //ここのreadでサイズ分new byte[]アロケートされている!!!!これ重いんでないか? data = meta.encode();//メタデータのデータを突っ込む byte[] ba = data.copy().toByteBuffer().array(); Log.d("PictureReaderMicAtom","METADATA:"+Utils.toHex(ba,0,ba.length,true)); //ファイル呼んでないので、スキップとか無し } // public MicAtom(final byte[] av_frame) { //タグタイプ1バイト、サイズ3バイト、タイムスタンプ3バイト、タイムスタンプ拡張1バイト、ストリームID3バイト int size = av_frame.length; Log.d("PictureReaderAudioAtom","SIZE ------------ " + size); byte[] headerBytes = new byte[11]; System.arraycopy(av_frame, 0, headerBytes, 0, 11); header = readHeader(headerBytes); Log.d("PictureReaderMicAtom","Header "+Utils.toHex(headerBytes, 0, headerBytes.length, true)); //ここでinはヘッダを読んだ後、4バイト(拡張タイムスタンプ+ストリームID)進んだ状態になって返ってくる //ここのreadでサイズ分new byte[]アロケートされている!!!!これ重いんでないか?でもしょうがないのか? byte[] dataAlloc = new byte[size-11]; System.arraycopy(av_frame, 11, dataAlloc, 0,size-11); data = ChannelBuffers.wrappedBuffer(ChannelBuffers.BIG_ENDIAN,dataAlloc);//タグのデータを突っ込む wrapped,copied,dynamicとあるが、wrappedでいいのかわからん byte[] ba = data.copy().toByteBuffer().array(); Log.d("PictureReaderMicAtom","DATA:"+Utils.toHex(ba,0,ba.length,true)); //0: Uncompressed, 1: ADPCM, 2: MP3, 5: Nellymoser 8kHz mono, 6: Nellymoser,10:AAC, 11: Speex //0: 5.5 kHz (or speex 16kHz), 1: 11 kHz, 2: 22 kHz, 3: 44 kHz //0: 8-bit, 1: 16-bit //0: mono, 1: stereo //StaticDebug.setTime(header.getTime()); } //単にヘッダをビーン化する→将来的にはいらない public RtmpHeader readHeader(final byte[] in) { byte type= in[0]; final MessageType messageType = MessageType.valueToEnum(type); //次の3バイト final int size = (in[1] & 0xff) << 16 | (in[2] & 0xff) << 8 | (in[3] & 0xff) << 0; //次の3バイト final int timestamp = (in[4] & 0xff) << 16 | (in[5] & 0xff) << 8 | (in[6] & 0xff) << 0; // 次の拡張タイムスタンプと、ストリームIDは非対応を意味する return new RtmpHeader(messageType, timestamp, size); } /** * F4vReader#next * FlvReader#next * FlvWriter#write * から呼ばれる * @return */ public ChannelBuffer write() { final ChannelBuffer out = ChannelBuffers.buffer(ChannelBuffers.BIG_ENDIAN,15 + header.getSize()); out.writeByte((byte) header.getMessageType().intValue()); out.writeMedium(header.getSize()); out.writeMedium(header.getTime()); out.writeInt(0); // 4 bytes of zeros (reserved)#next out.writeBytes(data,data.readableBytes()); out.writeInt(header.getSize() + 11); // previous tag size return out; } //FileChannelがChannelBufferとして確保されたもの //ヘッダーとして読み出すべき11バイトが入ってくる // public RtmpHeader readHeader(final ChannelBuffer in) { // byte[] ba = in.copy().array(); // byte type= in.readByte(); // Log.d("AudioAtom","readHeader"+Utils.toHex(ba,0,ba.length,true)); // final MessageType messageType = MessageType.valueToEnum(type); // Log.d("AudioAtom","TYPE:"+messageType.toString()+" VAL:"+new String(Utils.toHexChars(type))); // //次の3バイト // final int size = in.readMedium(); // //次の3バイト // final int timestamp = in.readMedium(); // in.skipBytes(4); // 拡張タイムスタンプと、ストリームIDは非対応を意味する // return new RtmpHeader(messageType, timestamp, size); // } @Override public RtmpHeader getHeader() { return header; } @Override public ChannelBuffer encode() { return data; } @Override public void decode(final ChannelBuffer in) { data = in; } @Override public String toString() { final StringBuilder sb = new StringBuilder(); sb.append(header); sb.append(" data: ").append(data); return sb.toString(); } @Override public MessageType getMessageType() {//AbstractMessage無くす為に新たに追加 return null; } } @Override public LinkedTransferQueue<RtmpMessage> getGrobalQueue() { return globalQueue; } public boolean isStartedPreview() { // TODO 自動生成されたメソッド・スタブ return false; } public void stopPreview() { // TODO 自動生成されたメソッド・スタブ } }