/* * myLib - https://github.com/taktod/myLib * Copyright (c) 2014 ttProject. All rights reserved. * * Licensed under GNU AFFERO GENERAL PUBLIC LICENSE Version 3. */ package com.ttProject.humble.frameutil; import java.nio.ByteBuffer; import io.humble.ferry.Buffer; import io.humble.video.Codec; import io.humble.video.Decoder; import io.humble.video.MediaPacket; import io.humble.video.Rational; import org.apache.log4j.Logger; import com.ttProject.frame.IAudioFrame; import com.ttProject.frame.IFrame; import com.ttProject.frame.IVideoFrame; import com.ttProject.frame.extra.AudioMultiFrame; import com.ttProject.frame.extra.VideoMultiFrame; import com.ttProject.frame.vorbis.VorbisFrame; import com.ttProject.frame.vorbis.type.CommentHeaderFrame; import com.ttProject.frame.vorbis.type.IdentificationHeaderFrame; import com.ttProject.frame.vorbis.type.SetupHeaderFrame; /** * frame -> packet * I cannot figure out the way to share packet like xuggle. make packet everytime. * @author taktod */ public class Packetizer { /** logger */ private Logger logger = Logger.getLogger(Packetizer.class); /** * make packet from frame * @param frame * @param packet * @return * @throws Exception */ public MediaPacket getPacket(IFrame frame, MediaPacket packet) throws Exception { // if(packet == null) { // packet = MediaPacket.make(); // } if(frame instanceof AudioMultiFrame) { throw new Exception("multiFrame is not supported."); } else if(frame instanceof VideoMultiFrame) { throw new Exception("multiFrame is not supported."); } else if(frame instanceof IAudioFrame) { return getAudioPacket((IAudioFrame)frame, packet); } else if(frame instanceof IVideoFrame) { return getVideoPacket((IVideoFrame)frame, packet); } return null; } /** * make videoPacket * @param frame * @param packet * @return * @throws Exception */ private MediaPacket getVideoPacket(IVideoFrame frame, MediaPacket packet) throws Exception { ByteBuffer buffer = frame.getPackBuffer(); if(buffer == null) { return null; } int size = buffer.remaining(); Buffer bufData = Buffer.make(null, buffer.array(), 0, size); packet = MediaPacket.make(bufData); packet.setFlags(0); packet.setDts(frame.getDts()); packet.setPts(frame.getPts()); packet.setTimeBase(Rational.make(1, (int)frame.getTimebase())); packet.setKeyPacket(frame.isKeyFrame()); // complete setting is missing, (automatically put complete?) return packet; } /** * make audioPacket * @param frame * @param packet * @return * @throws Exception */ private MediaPacket getAudioPacket(IAudioFrame frame, MediaPacket packet) throws Exception { ByteBuffer buffer = frame.getPackBuffer(); if(buffer == null) { return null; } int size = buffer.remaining(); Buffer bufData = Buffer.make(null, buffer.array(), 0, size); packet = MediaPacket.make(bufData); packet.setPts(frame.getPts()); packet.setTimeBase(Rational.make(1, (int)frame.getTimebase())); // no complete setting. same as video Packet? return packet; } /** * get the decoder corresponds with frame. * @param frame * @param decoder * @return * @throws Exception */ public Decoder getDecoder(IFrame frame, Decoder decoder) throws Exception { switch(frame.getCodecType()) { case AAC: if(decoder == null || decoder.getCodecID() != Codec.ID.CODEC_ID_AAC) { decoder = makeAudioDecoder((IAudioFrame)frame, Codec.ID.CODEC_ID_AAC); decoder.open(null, null); } break; case ADPCM_IMA_WAV: if(decoder == null || decoder.getCodecID() != Codec.ID.CODEC_ID_ADPCM_IMA_WAV) { decoder = makeAudioDecoder((IAudioFrame)frame, Codec.ID.CODEC_ID_ADPCM_IMA_WAV); decoder.open(null, null); } break; case ADPCM_SWF: if(decoder == null || decoder.getCodecID() != Codec.ID.CODEC_ID_ADPCM_SWF) { decoder = makeAudioDecoder((IAudioFrame)frame, Codec.ID.CODEC_ID_ADPCM_SWF); decoder.open(null, null); } break; case MP3: if(decoder == null || decoder.getCodecID() != Codec.ID.CODEC_ID_MP3) { decoder = makeAudioDecoder((IAudioFrame)frame, Codec.ID.CODEC_ID_MP3); decoder.open(null, null); } break; case NELLYMOSER: if(decoder == null || decoder.getCodecID() != Codec.ID.CODEC_ID_NELLYMOSER) { decoder = makeAudioDecoder((IAudioFrame)frame, Codec.ID.CODEC_ID_NELLYMOSER); decoder.open(null, null); } break; case OPUS: // opus will have codecPrivate. throw new RuntimeException("opus is not implements yet."); case PCM_ALAW: if(decoder == null || decoder.getCodecID() != Codec.ID.CODEC_ID_PCM_ALAW) { decoder = makeAudioDecoder((IAudioFrame)frame, Codec.ID.CODEC_ID_PCM_ALAW); decoder.open(null, null); } break; case PCM_MULAW: if(decoder == null || decoder.getCodecID() != Codec.ID.CODEC_ID_PCM_MULAW) { decoder = makeAudioDecoder((IAudioFrame)frame, Codec.ID.CODEC_ID_PCM_MULAW); decoder.open(null, null); } break; case SPEEX: if(decoder == null || decoder.getCodecID() != Codec.ID.CODEC_ID_SPEEX) { decoder = makeAudioDecoder((IAudioFrame)frame, Codec.ID.CODEC_ID_SPEEX); decoder.open(null, null); } break; case VORBIS: if(decoder == null || decoder.getCodecID() != Codec.ID.CODEC_ID_VORBIS) { if(frame instanceof IdentificationHeaderFrame || frame instanceof CommentHeaderFrame || frame instanceof SetupHeaderFrame) { // need to initialize first. return null; } decoder = makeAudioDecoder((IAudioFrame)frame, Codec.ID.CODEC_ID_VORBIS); VorbisFrame vorbisFrame = (VorbisFrame)frame; // need to tell extra data for decoder.... how can I do? ByteBuffer buffer = vorbisFrame.getPrivateData(); int size = buffer.remaining(); @SuppressWarnings("unused") Buffer extraData = Buffer.make(decoder, buffer.array(), 0, size); } break; case FLV1: if(decoder == null || decoder.getCodecID() != Codec.ID.CODEC_ID_FLV1) { Codec codec = Codec.findDecodingCodec(Codec.ID.CODEC_ID_FLV1); decoder = Decoder.make(codec); decoder.setTimeBase(Rational.make(1, (int)frame.getTimebase())); decoder.open(null, null); } break; case H264: if(decoder == null || decoder.getCodecID() != Codec.ID.CODEC_ID_H264) { Codec codec = Codec.findDecodingCodec(Codec.ID.CODEC_ID_H264); decoder = Decoder.make(codec); decoder.setTimeBase(Rational.make(1, (int)frame.getTimebase())); decoder.open(null, null); logger.info(decoder.getPixelFormat()); } break; case MJPEG: case THEORA: case VP6: case VP8: case VP9: case H265: case NONE: default: throw new RuntimeException("humble video doesn't support these codec:" + frame.getCodecType()); } return decoder; } /** * make audioDecoder * @param frame * @param id * @return * @throws Exception */ private Decoder makeAudioDecoder(IAudioFrame frame, Codec.ID id) throws Exception { Decoder decoder = null; if(frame.getSampleRate() == 0 || frame.getTimebase() == 0 || frame.getChannel() == 0) { // if frame doesn't have audio frame body, skip(like meta data.) return null; } Codec codec = Codec.findDecodingCodec(id); decoder = Decoder.make(codec); decoder.setSampleRate(frame.getSampleRate()); // decoder.setTimeBase(Rational.make(1, (int)frame.getTimebase())); decoder.setChannels(frame.getChannel()); // if we put open here, maybe vorbis will complain. return decoder; } }