package cn.org.hentai.jtt1078.subscriber;
|
|
import cn.org.hentai.jtt1078.codec.MP3Encoder;
|
import cn.org.hentai.jtt1078.flv.AudioTag;
|
import cn.org.hentai.jtt1078.flv.FlvAudioTagEncoder;
|
import cn.org.hentai.jtt1078.flv.FlvEncoder;
|
import cn.org.hentai.jtt1078.util.ByteBufUtils;
|
import cn.org.hentai.jtt1078.util.FLVUtils;
|
import cn.org.hentai.jtt1078.util.HttpChunk;
|
import io.netty.buffer.ByteBuf;
|
import io.netty.channel.ChannelHandlerContext;
|
|
/**
|
* Created by matrixy on 2020/1/13.
|
*/
|
public class VideoSubscriber extends Subscriber
|
{
|
private long videoTimestamp = 0;
|
private long audioTimestamp = 0;
|
private long lastVideoFrameTimeOffset = 0;
|
private long lastAudioFrameTimeOffset = 0;
|
private boolean videoHeaderSent = false;
|
|
public VideoSubscriber(String tag, ChannelHandlerContext ctx)
|
{
|
super(tag, ctx);
|
}
|
|
@Override
|
public void onVideoData(long timeoffset, byte[] data, FlvEncoder flvEncoder)
|
{
|
if (lastVideoFrameTimeOffset == 0) lastVideoFrameTimeOffset = timeoffset;
|
|
// 之前是不是已经发送过了?没有的话,需要补发FLV HEADER的。。。
|
if (videoHeaderSent == false && flvEncoder.videoReady())
|
{
|
enqueue(HttpChunk.make(flvEncoder.getHeader().getBytes()));
|
enqueue(HttpChunk.make(flvEncoder.getVideoHeader().getBytes()));
|
|
// 直接下发第一个I帧
|
byte[] iFrame = flvEncoder.getLastIFrame();
|
if (iFrame != null)
|
{
|
FLVUtils.resetTimestamp(iFrame, (int) videoTimestamp);
|
enqueue(HttpChunk.make(iFrame));
|
}
|
|
videoHeaderSent = true;
|
}
|
|
if (data == null) return;
|
|
// 修改时间戳
|
// System.out.println("Time: " + videoTimestamp + ", current: " + timeoffset);
|
FLVUtils.resetTimestamp(data, (int) videoTimestamp);
|
videoTimestamp += (int)(timeoffset - lastVideoFrameTimeOffset);
|
lastVideoFrameTimeOffset = timeoffset;
|
|
enqueue(HttpChunk.make(data));
|
}
|
|
private FlvAudioTagEncoder audioEncoder = new FlvAudioTagEncoder();
|
MP3Encoder mp3Encoder = new MP3Encoder();
|
|
@Override
|
public void onAudioData(long timeoffset, byte[] data, FlvEncoder flvEncoder)
|
{
|
if (!videoHeaderSent) return;
|
|
byte[] mp3Data = mp3Encoder.encode(data);
|
if (mp3Data == null || mp3Data.length == 0) return;
|
AudioTag audioTag = new AudioTag(0, mp3Data.length + 1, AudioTag.MP3, (byte) 0, (byte)1, (byte) 0, mp3Data);
|
byte[] frameData = null;
|
try
|
{
|
ByteBuf audioBuf = audioEncoder.encode(audioTag);
|
frameData = ByteBufUtils.readReadableBytes(audioBuf);
|
}
|
catch (Exception e)
|
{
|
e.printStackTrace();
|
}
|
|
if (lastAudioFrameTimeOffset == 0) lastAudioFrameTimeOffset = timeoffset;
|
|
if (data == null) return;
|
|
FLVUtils.resetTimestamp(frameData, (int) audioTimestamp);
|
audioTimestamp += (int)(timeoffset - lastAudioFrameTimeOffset);
|
lastAudioFrameTimeOffset = timeoffset;
|
|
enqueue(HttpChunk.make(frameData));
|
}
|
|
@Override
|
public void close()
|
{
|
super.close();
|
mp3Encoder.close();
|
}
|
}
|