/* * myLib - https://github.com/taktod/myLib * Copyright (c) 2014 ttProject. All rights reserved. * * Licensed under GNU GENERAL PUBLIC LICENSE Version 3. */ package com.ttProject.transcode.xuggle.packet; import java.nio.ByteBuffer; import com.ttProject.media.Unit; import com.ttProject.media.aac.DecoderSpecificInfo; import com.ttProject.media.aac.frame.Aac; import com.ttProject.media.flv.tag.AudioTag; import com.ttProject.nio.channels.ByteReadChannel; import com.ttProject.transcode.exception.FormatChangeException; import com.ttProject.util.BufferUtil; import com.xuggle.ferry.IBuffer; import com.xuggle.xuggler.ICodec; import com.xuggle.xuggler.IPacket; import com.xuggle.xuggler.IRational; import com.xuggle.xuggler.IStreamCoder; import com.xuggle.xuggler.IStreamCoder.Direction; /** * flvTagからIPacketを作る動作 * @author taktod */ public class FlvAudioPacketizer implements IPacketizer { /** 最終音声タグ */ private AudioTag lastAudioTag = null; /** dsiデータ */ private DecoderSpecificInfo dsi = null; /** * データをあらかじめ確認します。 */ @Override public boolean check(Unit unit) throws FormatChangeException { // 先にデータを確認して、データの整合性を確認します。 // なおtypeが違う場合は、そのままtrueを返します。 // falseになる場合の例:中途でコーデックがかわったなど if(!(unit instanceof AudioTag)) { return false; } if(lastAudioTag == null) { // 始めのデータなので問題なし return true; } AudioTag aTag = (AudioTag) unit; // 一致してたらtrue 違ったらfalse if(aTag.getCodec() == lastAudioTag.getCodec() && aTag.getChannels() == lastAudioTag.getChannels() && aTag.getSampleRate() == lastAudioTag.getSampleRate()) { return true; } throw new FormatChangeException(); } /** * tagからpacketを取り出します。 */ @Override public IPacket getPacket(Unit unit, IPacket packet) throws Exception { if(unit instanceof AudioTag) { lastAudioTag = (AudioTag) unit; switch(lastAudioTag.getCodec()) { case AAC: return getAACPacket(lastAudioTag, packet); case MP3_8: case MP3: dsi = null; return getMp3Packet(lastAudioTag, packet); case NELLY_16: case NELLY_8: case NELLY: dsi = null; return getNellyPacket(lastAudioTag, packet); case SPEEX: dsi = null; return getSpeexPacket(lastAudioTag, packet); case PCM: case LPCM: case ADPCM: case G711_A: case G711_U: case RESERVED: case DEVICE_SPECIFIC: dsi = null; throw new RuntimeException(lastAudioTag.getCodec() + "の変換は未実装です。"); default: break; } } return null; } /** * mp3用のIPacketを生成します * @param tag * @param packet * @return * @throws Exception */ private IPacket getMp3Packet(AudioTag tag, IPacket packet) throws Exception { if(packet == null) { packet = IPacket.make(); } ByteBuffer rawData = tag.getRawData(); int size = rawData.remaining(); IBuffer bufData = IBuffer.make(null, rawData.array(), 0, size); packet.setData(bufData); packet.setDts(tag.getTimestamp()); packet.setPts(tag.getTimestamp()); packet.setTimeBase(IRational.make(1, 1000)); packet.setComplete(true, size); return packet; } /** * nellymoser用のIPacketを生成します * @param tag * @param packet * @return * @throws Exception */ private IPacket getNellyPacket(AudioTag tag, IPacket packet) throws Exception { if(packet == null) { packet = IPacket.make(); } ByteBuffer rawData = tag.getRawData(); int size = rawData.remaining(); IBuffer bufData = IBuffer.make(null, rawData.array(), 0, size); packet.setData(bufData); packet.setDts(tag.getTimestamp()); packet.setPts(tag.getTimestamp()); packet.setTimeBase(IRational.make(1, 1000)); packet.setComplete(true, size); return packet; } /** * speex用のIPacketを生成します * @param tag * @param packet * @return * @throws Exception */ private IPacket getSpeexPacket(AudioTag tag, IPacket packet) throws Exception { if(packet == null) { packet = IPacket.make(); } ByteBuffer rawData = tag.getRawData(); int size = rawData.remaining(); IBuffer bufData = IBuffer.make(null, rawData.array(), 0, size); packet.setData(bufData); packet.setDts(tag.getTimestamp()); packet.setPts(tag.getTimestamp()); packet.setTimeBase(IRational.make(1, 1000)); packet.setComplete(true, size); return packet; } /** * aac用のIPacketを生成します。 * @param tag * @param packet * @return * @throws Exception */ private IPacket getAACPacket(AudioTag tag, IPacket packet) throws Exception { if(tag.isMediaSequenceHeader()) { dsi = new DecoderSpecificInfo(); dsi.analyze(new ByteReadChannel(tag.getRawData())); return null; } if(dsi == null) { throw new RuntimeException("decoderSpecificInfoが決定していません"); } if(packet == null) { packet = IPacket.make(); } ByteBuffer rawData = tag.getRawData(); int size = rawData.remaining(); Aac aac = new Aac(size, dsi); aac.setData(rawData); ByteBuffer buffer = aac.getBuffer(); size = buffer.remaining(); IBuffer bufData = IBuffer.make(null, BufferUtil.toByteArray(buffer), 0, size); packet.setData(bufData); packet.setDts(tag.getTimestamp()); packet.setPts(tag.getTimestamp()); packet.setTimeBase(IRational.make(1, 1000)); packet.setComplete(true, size); return packet; } /** * 現在動作しているデータに対応するデコーダーを取得します */ @Override public IStreamCoder createDecoder() throws Exception { if(lastAudioTag == null) { return null; } IStreamCoder decoder = null; switch(lastAudioTag.getCodec()) { case AAC: // win64bit版のxuggleのaacデコードにはバグがあるみたいです。xuggle-5.4のコンパイルがどうやら32bitになっている感じ? decoder = IStreamCoder.make(Direction.DECODING, ICodec.ID.CODEC_ID_AAC); decoder.setSampleRate(lastAudioTag.getSampleRate()); decoder.setTimeBase(IRational.make(1, lastAudioTag.getSampleRate())); decoder.setChannels(lastAudioTag.getChannels()); break; case MP3: decoder = IStreamCoder.make(Direction.DECODING, ICodec.ID.CODEC_ID_MP3); decoder.setSampleRate(lastAudioTag.getSampleRate()); decoder.setTimeBase(IRational.make(1, lastAudioTag.getSampleRate())); decoder.setChannels(lastAudioTag.getChannels()); break; case MP3_8: decoder = IStreamCoder.make(Direction.DECODING, ICodec.ID.CODEC_ID_MP3); decoder.setSampleRate(8000); decoder.setTimeBase(IRational.make(1, lastAudioTag.getSampleRate())); decoder.setChannels(lastAudioTag.getChannels()); break; case NELLY: decoder = IStreamCoder.make(Direction.DECODING, ICodec.ID.CODEC_ID_NELLYMOSER); decoder.setSampleRate(lastAudioTag.getSampleRate()); decoder.setTimeBase(IRational.make(1, lastAudioTag.getSampleRate())); decoder.setChannels(lastAudioTag.getChannels()); break; case NELLY_8: decoder = IStreamCoder.make(Direction.DECODING, ICodec.ID.CODEC_ID_NELLYMOSER); decoder.setSampleRate(8000); decoder.setTimeBase(IRational.make(1, lastAudioTag.getSampleRate())); decoder.setChannels(lastAudioTag.getChannels()); break; case NELLY_16: // TODO 未チェック decoder = IStreamCoder.make(Direction.DECODING, ICodec.ID.CODEC_ID_NELLYMOSER); decoder.setSampleRate(16000); decoder.setTimeBase(IRational.make(1, lastAudioTag.getSampleRate())); decoder.setChannels(lastAudioTag.getChannels()); break; case SPEEX: // FLVのspeexは16000hz + 1Channelのみになってる。 decoder = IStreamCoder.make(Direction.DECODING, ICodec.ID.CODEC_ID_SPEEX); decoder.setSampleRate(16000); decoder.setTimeBase(IRational.make(1, lastAudioTag.getSampleRate())); decoder.setChannels(1); break; case PCM: case ADPCM: case LPCM: case G711_A: case G711_U: case RESERVED: case DEVICE_SPECIFIC: throw new RuntimeException(lastAudioTag.getCodec() + "の変換は未実装です。"); default: return null; } return decoder; } @Override public void close() { } }