/* * 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; import java.util.Map.Entry; import java.util.concurrent.ExecutorService; import org.apache.log4j.Logger; import com.ttProject.media.Unit; import com.ttProject.transcode.ITrackManager; import com.ttProject.transcode.TranscodeManager; import com.ttProject.transcode.xuggle.packet.IPacketizer; import com.ttProject.transcode.xuggle.track.XuggleTrackManager; import com.xuggle.xuggler.IAudioSamples; import com.xuggle.xuggler.ICodec.Type; import com.xuggle.xuggler.IPacket; import com.xuggle.xuggler.IStreamCoder; import com.xuggle.xuggler.IVideoPicture; /** * 変換動作の中心マネージャー * 1つのマネージャーでは、1つのコンバートだけ実行します。 * 音声と映像の両方をコンバートしたければ2つ変換マネージャーが必要となるとします。 * @author taktod */ public class XuggleTranscodeManager extends TranscodeManager implements IXuggleTranscodeManager { /** 動作ロガー */ @SuppressWarnings("unused") private Logger logger = Logger.getLogger(XuggleTranscodeManager.class); /** 処理thread pool */ private ExecutorService executor = null; /** 元オブジェクトpacket化モジュール */ private IPacketizer packetizer = null; /** デコーダー */ private IStreamCoder decoder = null; /** 動作パケット(使い回します) */ private IPacket packet = null; /** * executorを設定することで動作を向上させます。 * @param executor */ @Override public void setExecutorService(ExecutorService executor) { this.executor = executor; } /** * パケット化モジュールを設定 * @param packetizer */ @Override public void setPacketizer(IPacketizer packetizer) { this.packetizer = packetizer; } /** * 変換実行 * @param unit 変換対象データ */ @Override public void transcode(final Unit unit) throws Exception { // パケットの確認を実行します。 if(!packetizer.check(unit)) { return; } if(executor != null) { executor.execute(new Runnable() { @Override public void run() { try { process(unit); } catch(Exception e) { reportException(e); } } }); } else { try { // threadでない場合はそのまま処理する process(unit); } catch(Exception e) { reportException(e); } } } /** * 停止処理 */ @Override public synchronized void close() { if(packetizer != null) { packetizer.close(); packetizer = null; } if(decoder != null) { decoder.close(); decoder = null; } for(Entry<Integer, ITrackManager> entry : getTrackManagers().entrySet()) { XuggleTrackManager trackManager = (XuggleTrackManager)entry.getValue(); trackManager.close(); } } /** * 処理を実施する。 * @param unit * @throws Exception (なにか問題がでたら例外がでます) */ private void process(Unit unit) throws Exception { // packet化する。 packet = packetizer.getPacket(unit, packet); if(packet == null) { // packet化できない場合は処理しない。 return; } // デコードを処理する。 if(decoder == null) { decoder = packetizer.createDecoder(); if(decoder == null) { throw new Exception("decoderが取得できませんでした"); } if(!decoder.isOpen()) { if(decoder.open(null, null) < 0) { throw new Exception("decoderが開けませんでした"); } } } if(decoder.getCodecType() == Type.CODEC_TYPE_AUDIO) { // audioの場合 processAudio(packet); } else if(decoder.getCodecType() == Type.CODEC_TYPE_VIDEO) { // videoの場合 processVideo(packet); } else { throw new Exception("変換タイプが不明です"); } } /** * 音声処理をすすめる * @param packet * @throws Exception */ private void processAudio(IPacket packet) throws Exception { IAudioSamples samples = IAudioSamples.make(1024, decoder.getChannels()); int offset = 0; while(offset < packet.getSize()) { int bytesDecoded = decoder.decodeAudio(samples, packet, offset); if(bytesDecoded < 0) { throw new Exception("デコード中にエラーが発生"); } offset += bytesDecoded; if(samples.isComplete()) { // こっちも時間がずれるっぽいので、治してみよう。 samples.setPts((long)(packet.getPts() / samples.getTimeBase().getDouble() * packet.getTimeBase().getDouble())); // ここでtrackManagerに処理をまかせる。 for(Entry<Integer, ITrackManager> entry : getTrackManagers().entrySet()) { ITrackManager trackManager = entry.getValue(); if(!(trackManager instanceof XuggleTrackManager)) { throw new Exception("想定外のtrackManagerを検知しました。"); } XuggleTrackManager xuggleTrackManager = (XuggleTrackManager) trackManager; xuggleTrackManager.encode(samples.copyReference()); } } } } /** * 映像処理をすすめる * @param packet * @throws Exception */ private void processVideo(IPacket packet) throws Exception { IVideoPicture picture = IVideoPicture.make(decoder.getPixelType(), decoder.getWidth(), decoder.getHeight()); int offset = 0; while(offset < packet.getSize()) { int bytesDecoded = decoder.decodeVideo(picture, packet, offset); if(bytesDecoded <= 0) { throw new Exception("デコード中にエラーが発生しました。"); } offset += bytesDecoded; if(picture.isComplete()) { // なんか時間が狂うので、時間の設定し直しを実行しておいた。 picture.setPts(packet.getPts() * 1000L); // ここでtrackManagerに処理をまかせる。 for(Entry<Integer, ITrackManager> entry : getTrackManagers().entrySet()) { ITrackManager trackManager = entry.getValue(); if(!(trackManager instanceof XuggleTrackManager)) { throw new Exception("想定外のtrackManagerを検知しました。"); } XuggleTrackManager xuggleTrackManager = (XuggleTrackManager) trackManager; xuggleTrackManager.encode(picture); } } } } /** * 内部動作用のtrackManagerを生成する処理 * (この動作はTranscodeManagerから呼び出されます) * @param newId */ @Override protected ITrackManager makeTrackManager(int newId) { XuggleTrackManager trackManager = new XuggleTrackManager(this, newId); return trackManager; } }