/* * myLib - https://github.com/taktod/myLib * Copyright (c) 2014 ttProject. All rights reserved. * * Licensed under The MIT license. */ package com.ttProject.media.h264; import java.nio.ByteBuffer; import com.ttProject.nio.channels.ByteReadChannel; import com.ttProject.nio.channels.IReadChannel; import com.ttProject.util.BufferUtil; /** * nalデータの解析を実行します。 * ただし、nalデータの印の部分00 00 00 01 or 00 00 01の部分には興味がないので、 * データとしては、その部分を省いたデータを応答することにします。 * * なおこれはまだ作成する必要はなさそう。(mpegtsの読み込みくらいしか使いどころなさそうだから、 * 他のデータmp4やflvはnalではない方法をつかっているみたいです。) * @author taktod * */ public class NalAnalyzer extends FrameAnalyzer { @Override public Frame analyze(IReadChannel ch) throws Exception { // cacheBufferを利用した動作だと、IReadChannelの位置が移動してしまってあとでやり直しが効かないので、調整が必要。 // 00 00 01で切って、00 00 01になるまで読み込んでおく。 // 読み込みはじめに00 00 01以外がくる場合は、そこからframeがはじまると解釈すればよさそう。 Short lastData = null; ByteBuffer buf = ByteBuffer.allocate(ch.size() - ch.position()); // データを読み込んでいく。 while(ch.size() - ch.position() > 1) { // shortで確認した方がいい可能性もある。 short data = BufferUtil.safeRead(ch, 2).getShort(); // 00 00 00 01もしくは 00 00 01がnalの分岐点 // よってshort = 0になった場合に注意して2バイト先までbyteでデータを取得すればよさそう。 if(data == 0) { // 前のデータを確認した、下の部分が00だったら追加するデータがアレになる。 byte firstByte, secondByte; firstByte = BufferUtil.safeRead(ch, 1).get(); if(firstByte == 1) { checkLastData(buf, lastData); buf.flip(); if(buf.remaining() == 0) { // データがない場合は解析する必要なし。 buf = ByteBuffer.allocate(ch.size() - ch.position()); continue; } return setupFrame(buf); } else if(firstByte == 0) { secondByte = BufferUtil.safeRead(ch, 1).get(); if(secondByte == 1) { checkLastData(buf, lastData); buf.flip(); if(buf.remaining() == 0) { // データがない場合は解析する必要なし。 buf = ByteBuffer.allocate(ch.size() - ch.position()); continue; } return setupFrame(buf); } else { // nal分岐ではなかったその1 if(lastData != null) { buf.putShort(lastData); } buf.putShort(data); buf.put(firstByte); buf.put(secondByte); } } else { // nal分岐ではなかった、その2 if(lastData != null) { buf.putShort(lastData); } buf.putShort(data); buf.put(firstByte); } lastData = null; } else { if(lastData != null && data == 1 && (lastData & 0x00FF) == 0) { checkLastData(buf, lastData); buf.flip(); if(buf.remaining() == 0) { // データがない場合は解析する必要なし。 buf = ByteBuffer.allocate(ch.size() - ch.position()); continue; } return setupFrame(buf); } setLastData(buf, lastData); lastData = data; } } // 最後まで読み込めた setLastData(buf, lastData); if(ch.size() - ch.position() == 1) { buf.put(BufferUtil.safeRead(ch, 1).get()); } buf.flip(); if(buf.remaining() == 0) { return null; } return setupFrame(buf); } private Frame setupFrame(ByteBuffer buffer) throws Exception { int size = buffer.remaining(); IReadChannel ch = new ByteReadChannel(buffer); Frame frame = super.analyze(ch); frame.setSize(size); frame.analyze(ch); return frame; } private void setLastData(ByteBuffer buf, Short lastData) { if(lastData != null) { buf.putShort(lastData); } } /** * 終端データを調整する。 * @param buf * @param lastData */ private void checkLastData(ByteBuffer buf, Short lastData) { if(lastData != null) { if((lastData & 0x00FF) == 0) { //lastData >>> 8; // 前のnalデータに追記すべきデータ buf.put((byte)(lastData >>> 8)); } else { buf.putShort(lastData); } } } }