/*
* myLib - https://github.com/taktod/myLib
* Copyright (c) 2014 ttProject. All rights reserved.
*
* Licensed under GNU GENERAL PUBLIC LICENSE Version 3.
*/
package com.ttProject.xuggle.frameutil;
import org.apache.log4j.Logger;
import com.ttProject.frame.AudioAnalyzer;
import com.ttProject.frame.AudioSelector;
import com.ttProject.frame.CodecType;
import com.ttProject.frame.Frame;
import com.ttProject.frame.IAnalyzer;
import com.ttProject.frame.IAudioFrame;
import com.ttProject.frame.IFrame;
import com.ttProject.frame.IVideoFrame;
import com.ttProject.frame.VideoAnalyzer;
import com.ttProject.frame.VideoSelector;
import com.ttProject.frame.extra.AudioMultiFrame;
import com.ttProject.frame.extra.VideoMultiFrame;
import com.ttProject.frame.speex.SpeexFrameSelector;
import com.ttProject.frame.speex.type.CommentFrame;
import com.ttProject.frame.speex.type.HeaderFrame;
import com.ttProject.frame.vorbis.VorbisFrameAnalyzer;
import com.ttProject.frameutil.AnalyzerChecker;
import com.ttProject.nio.channels.ByteReadChannel;
import com.ttProject.nio.channels.IReadChannel;
import com.xuggle.xuggler.IPacket;
import com.xuggle.xuggler.IStreamCoder;
/**
* packet -> frame
* @author taktod
*/
public class Depacketizer {
/** logger */
@SuppressWarnings("unused")
private Logger logger = Logger.getLogger(Depacketizer.class);
private IAnalyzer analyzer = null;
private AnalyzerChecker analyzerChecker = new AnalyzerChecker();
private CodecTypeChecker codecChecker = new CodecTypeChecker();
/**
* get the frame from packet.
* @param encoder
* @param packet
* @return
* @throws Exception
*/
public IFrame getFrame(IStreamCoder encoder, IPacket packet) throws Exception {
CodecType type = codecChecker.getCodecType(encoder.getCodecID());
if(type.isAudio()) {
return getAudioFrame(encoder, packet, type);
}
else if(type.isVideo()) {
return getVideoFrame(encoder, packet, type);
}
else {
throw new Exception("invalid data type.");
}
}
/**
* video frame
* @param encoder
* @param packet
* @param type
* @return
* @throws Exception
*/
private IFrame getVideoFrame(IStreamCoder encoder, IPacket packet, CodecType type) throws Exception {
if(analyzer == null || analyzer.getCodecType() != type) {
analyzer = analyzerChecker.checkAnalyzer(type);
if(analyzer instanceof AudioAnalyzer) {
throw new Exception("audioAnalyzer is applyed for videoFrame.");
}
VideoAnalyzer vAnalyzer = (VideoAnalyzer)analyzer;
VideoSelector selector = vAnalyzer.getSelector();
selector.setWidth(encoder.getWidth());
selector.setHeight(encoder.getHeight());
}
VideoMultiFrame result = new VideoMultiFrame();
IFrame frame = null;
IReadChannel channel = new ByteReadChannel(packet.getData().getByteArray(0, packet.getSize()));
while((frame = analyzer.analyze(channel)) != null) {
Frame f = (Frame) frame;
f.setPts(packet.getPts());
result.addFrame((IVideoFrame)frame);
}
frame = analyzer.getRemainFrame();
if(frame != null) {
Frame f = (Frame) frame;
f.setPts(packet.getPts());
result.addFrame((IVideoFrame)frame);
}
switch(result.getFrameList().size()) {
case 0:
return null;
case 1:
return result.getFrameList().get(0);
default:
return result;
}
}
/**
* audio frame
* @param encoder
* @param packet
* @param type
* @return
* @throws Exception
*/
private IFrame getAudioFrame(IStreamCoder encoder, IPacket packet, CodecType type) throws Exception {
if(analyzer == null || analyzer.getCodecType() != type) {
analyzer = analyzerChecker.checkAnalyzer(type);
if(analyzer instanceof VideoAnalyzer) {
throw new Exception("videoAnalyzer is applyed for audioFrame.");
}
AudioAnalyzer aAnalyzer = (AudioAnalyzer)analyzer;
AudioSelector selector = aAnalyzer.getSelector();
selector.setChannel(encoder.getChannels());
selector.setSampleRate(encoder.getSampleRate());
// for vorbis need to set the private data.
IFrame extraFrame = null;
switch(type) {
case VORBIS:
{
((VorbisFrameAnalyzer)analyzer).setPrivateData(new ByteReadChannel(encoder.getExtraData().getByteBuffer(0, encoder.getExtraDataSize())));
}
break;
case SPEEX:
{
IReadChannel privateData = new ByteReadChannel(encoder.getExtraData().getByteBuffer(0, encoder.getExtraDataSize()));
// for speex, privateData has only header. no comment frame?
CommentFrame commentFrame = new CommentFrame();
while((extraFrame = analyzer.analyze(privateData)) != null) {
if(extraFrame instanceof HeaderFrame) {
commentFrame.setHeaderFrame((HeaderFrame)extraFrame);
}
}
commentFrame.setVenderName("ttProject");
commentFrame.addElement("Depacketizer");
SpeexFrameSelector sselector = (SpeexFrameSelector)selector;
sselector.setCommentFrame(commentFrame);
}
break;
default:
break;
}
}
AudioMultiFrame result = new AudioMultiFrame();
IFrame frame = null;
IReadChannel channel = new ByteReadChannel(packet.getData().getByteArray(0, packet.getSize()));
while((frame = analyzer.analyze(channel)) != null) {
Frame f = (Frame) frame;
f.setPts(packet.getPts());
f.setTimebase((long)(1 / packet.getTimeBase().getDouble()));
result.addFrame((IAudioFrame)frame);
}
frame = analyzer.getRemainFrame();
if(frame != null) {
Frame f = (Frame) frame;
f.setPts(packet.getPts());
result.addFrame((IAudioFrame)frame);
}
switch(result.getFrameList().size()) {
case 0:
return null;
case 1:
return result.getFrameList().get(0);
default:
return result;
}
}
}