Last active
January 23, 2019 05:53
-
-
Save maxymania/ff05177458992d5819a3f64b720f7ae1 to your computer and use it in GitHub Desktop.
Ogg Stream segmenter for netty (4.1.32)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Copyright (c) 2019 Simon Schmidt | |
This software is provided 'as-is', without any express or implied | |
warranty. In no event will the authors be held liable for any damages | |
arising from the use of this software. | |
Permission is granted to anyone to use this software for any purpose, | |
including commercial applications, and to alter it and redistribute it | |
freely, subject to the following restrictions: | |
1. The origin of this software must not be misrepresented; you must not | |
claim that you wrote the original software. If you use this software | |
in a product, an acknowledgment in the product documentation would be | |
appreciated but is not required. | |
2. Altered source versions must be plainly marked as such, and must not be | |
misrepresented as being the original software. | |
3. This notice may not be removed or altered from any source distribution. | |
*/ | |
import io.netty.buffer.ByteBuf; | |
import io.netty.channel.ChannelHandlerContext; | |
import io.netty.handler.codec.ReplayingDecoder; | |
import io.netty.util.ReferenceCountUtil; | |
import java.util.List; | |
/** | |
* | |
* @author simon | |
*/ | |
public class OggDecoder extends ReplayingDecoder<OggDecoder.State> { | |
public enum State { | |
ERROR, | |
OK, | |
SO, | |
SggS0, | |
HDR, | |
SEGTABLE, | |
BODY_PREPARE, | |
BODY, | |
} | |
public OggDecoder() { | |
super(State.OK); | |
} | |
private OggPage message; | |
private static final byte[] MZ = { | |
(byte)'g', | |
(byte)'g', | |
(byte)'S', | |
0, | |
}; | |
@Override | |
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { | |
// Discard on error!. | |
if(state()==State.ERROR){ | |
ReferenceCountUtil.release(msg); | |
return; | |
} | |
super.channelRead(ctx, msg); | |
} | |
@Override | |
protected void decode( | |
ChannelHandlerContext ctx, | |
ByteBuf in, | |
List<Object> out | |
) throws Exception { | |
start: | |
for(;;) | |
switch(state()){ | |
case ERROR: | |
for(;;){ | |
in.readByte(); | |
checkpoint(); | |
} | |
case OK: | |
message = new OggPage(); | |
state(State.SO); | |
case SO: | |
int before = in.bytesBefore((byte)'O'); | |
if(before>0) | |
in.skipBytes(before); | |
in.readByte(); | |
checkpoint(State.SggS0); | |
case SggS0: | |
for(byte b:MZ){ | |
byte r = in.readByte(); | |
if(r==b)continue; | |
checkpoint(); | |
if(r!='O') state(State.SO); | |
continue start; | |
} | |
checkpoint(State.HDR); | |
case HDR: | |
message.decodeHeaderNoMZ(in); | |
checkpoint(State.SEGTABLE); | |
case SEGTABLE: | |
message.decodeSegTable(in); | |
checkpoint(State.BODY); | |
case BODY: | |
message.readBody(in); | |
out.add(message); | |
checkpoint(State.OK); | |
message = null; | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Copyright (c) 2019 Simon Schmidt | |
This software is provided 'as-is', without any express or implied | |
warranty. In no event will the authors be held liable for any damages | |
arising from the use of this software. | |
Permission is granted to anyone to use this software for any purpose, | |
including commercial applications, and to alter it and redistribute it | |
freely, subject to the following restrictions: | |
1. The origin of this software must not be misrepresented; you must not | |
claim that you wrote the original software. If you use this software | |
in a product, an acknowledgment in the product documentation would be | |
appreciated but is not required. | |
2. Altered source versions must be plainly marked as such, and must not be | |
misrepresented as being the original software. | |
3. This notice may not be removed or altered from any source distribution. | |
*/ | |
import io.netty.buffer.ByteBuf; | |
import io.netty.buffer.ByteBufAllocator; | |
import io.netty.util.ReferenceCountUtil; | |
import io.netty.util.ReferenceCounted; | |
/** | |
* | |
* @author simon | |
*/ | |
public class OggPage implements ReferenceCounted{ | |
public static int OGGS_LE = 0x5367674f; | |
public int oggs,version,bitflags; | |
public long absoluteGranulatePosition; | |
public int serialNumber,pageNo,crc; | |
public int pageSegments; | |
public int[] segTable; | |
public ByteBuf body; | |
public void decodeHeader(ByteBuf buf){ | |
oggs = buf.readIntLE(); | |
version = buf.readUnsignedByte(); | |
decodeHeaderInternal(buf); | |
} | |
public void decodeHeaderNoMZ(ByteBuf buf){ | |
oggs = OGGS_LE; | |
version = 0; | |
decodeHeaderInternal(buf); | |
} | |
private void decodeHeaderInternal(ByteBuf buf){ | |
bitflags = buf.readUnsignedByte(); | |
absoluteGranulatePosition = buf.readLongLE(); | |
serialNumber = buf.readIntLE(); | |
pageNo = buf.readIntLE(); | |
crc = buf.readIntLE(); | |
pageSegments = buf.readUnsignedByte(); | |
segTable = new int[pageSegments]; | |
} | |
public boolean validateHeader(){ | |
return | |
oggs==OGGS_LE && | |
version==0; | |
} | |
public void decodeSegTable(ByteBuf buf){ | |
for(int i=0;i<pageSegments;++i) | |
segTable[i] = buf.readUnsignedByte(); | |
} | |
public boolean validateSegmentTable(){ | |
/* | |
for(int segment:segTable) | |
if(segment==0) | |
return false; | |
// */ | |
return true; | |
} | |
public void readBody(ByteBuf buf){ | |
int length = 0; | |
for(int segment:segTable) | |
length += segment; | |
body = buf.readBytes(length); | |
} | |
public void encode(ByteBuf buf){ | |
buf.writeIntLE(oggs); | |
buf.writeByte(version); | |
buf.writeByte(bitflags); | |
buf.writeLongLE(absoluteGranulatePosition); | |
buf.writeIntLE(serialNumber); | |
buf.writeIntLE(pageNo); | |
buf.writeIntLE(crc); | |
buf.writeByte(segTable.length); | |
for(int seg:segTable) | |
buf.writeByte(seg); | |
int length = 0; | |
for(int segment:segTable) | |
length += segment; | |
buf.writeBytes(body, body.readerIndex(), length); | |
} | |
public int hintSize(){ | |
return 27+segTable.length+body.readableBytes(); | |
} | |
@Override | |
public String toString() { | |
StringBuilder sb = new StringBuilder("OggPage"); | |
sb.append("{"); | |
sb.append(";validHeader=").append(validateHeader()); | |
sb.append(";validateSegmentTable=").append(validateSegmentTable()); | |
sb.append(";version=").append(version); | |
sb.append(";agp=").append(absoluteGranulatePosition); | |
sb.append(";serialNumber=").append(serialNumber); | |
sb.append(";pageNo=").append(pageNo); | |
sb.append("}"); | |
return sb.toString(); | |
} | |
@Override | |
public int refCnt() { | |
return ReferenceCountUtil.refCnt(body); | |
} | |
@Override | |
public OggPage retain() { | |
ReferenceCountUtil.retain(body); | |
return this; | |
} | |
@Override | |
public OggPage retain(int increment) { | |
ReferenceCountUtil.retain(body,increment); | |
return this; | |
} | |
@Override | |
public OggPage touch() { | |
ReferenceCountUtil.touch(body); | |
return this; | |
} | |
@Override | |
public OggPage touch(Object hint) { | |
ReferenceCountUtil.touch(body,hint); | |
return this; | |
} | |
@Override | |
public boolean release() { | |
return ReferenceCountUtil.release(body); | |
} | |
@Override | |
public boolean release(int decrement) { | |
return ReferenceCountUtil.release(body,decrement); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Copyright (c) 2019 Simon Schmidt | |
This software is provided 'as-is', without any express or implied | |
warranty. In no event will the authors be held liable for any damages | |
arising from the use of this software. | |
Permission is granted to anyone to use this software for any purpose, | |
including commercial applications, and to alter it and redistribute it | |
freely, subject to the following restrictions: | |
1. The origin of this software must not be misrepresented; you must not | |
claim that you wrote the original software. If you use this software | |
in a product, an acknowledgment in the product documentation would be | |
appreciated but is not required. | |
2. Altered source versions must be plainly marked as such, and must not be | |
misrepresented as being the original software. | |
3. This notice may not be removed or altered from any source distribution. | |
*/ | |
import io.netty.buffer.ByteBuf; | |
import java.nio.charset.StandardCharsets; | |
/** | |
* <a href="https://xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-610004.2">here</a> and | |
* <a href="https://xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-820005">here</a> | |
* @author Simon Schmidt | |
*/ | |
public class VorbisHeader { | |
/** | |
* 1 = identification header<br> | |
* 3 = comment header | |
* . | |
*/ | |
public int type; | |
/** | |
* Should be "vorbis". | |
*/ | |
public String vorbis; | |
/** | |
* type = 1, identification header. | |
*/ | |
public int | |
vorbis_version, | |
audio_channels, | |
audio_sample_rate, | |
bitrate_maximum, | |
bitrate_nominal, | |
bitrate_minimum, | |
blocksize_0_and_1; | |
/** | |
* type = 3, comment header. | |
*/ | |
public String vendor,comments[]; | |
public void parse(OggPage page){ | |
ByteBuf body = page.body; | |
body.markReaderIndex(); | |
try{ | |
type = body.readUnsignedByte(); | |
vorbis = body.readSlice(6).toString(StandardCharsets.ISO_8859_1); | |
switch(type){ | |
case 1: | |
vorbis_version = body.readIntLE(); | |
audio_channels = body.readUnsignedByte(); | |
audio_sample_rate = body.readIntLE(); | |
bitrate_maximum = body.readIntLE(); | |
bitrate_nominal = body.readIntLE(); | |
bitrate_minimum = body.readIntLE(); | |
blocksize_0_and_1 = body.readUnsignedByte(); | |
break; | |
case 3: | |
vendor = body.readSlice(body.readIntLE()).toString(StandardCharsets.UTF_8); | |
int n = body.readIntLE(); | |
comments = new String[n]; | |
for(int i=0;i<n;++i) | |
comments[i] = body.readSlice(body.readIntLE()).toString(StandardCharsets.UTF_8); | |
} | |
}finally{ | |
body.resetReaderIndex(); | |
} | |
} | |
@Override | |
public String toString() { | |
StringBuilder sb = new StringBuilder("VorbisHeader"); | |
sb.append("{"); | |
switch(type){ | |
case 1: | |
sb.append("[").append(vorbis).append("]"); | |
sb.append("\n[vorbis_version]").append(vorbis_version); | |
sb.append("\n[audio_channels]").append(audio_channels); | |
sb.append("\n[audio_sample_rate]").append(audio_sample_rate); | |
sb.append("\n[bitrate_maximum]").append(bitrate_maximum); | |
sb.append("\n[bitrate_nominal]").append(bitrate_nominal); | |
sb.append("\n[bitrate_minimum]").append(bitrate_minimum); | |
sb.append("\n[blocksize_0_and_1]").append(blocksize_0_and_1); | |
break; | |
case 3: | |
sb.append("[").append(vorbis).append("]"); | |
sb.append("\n[vendor]").append(vendor); | |
for(String comment:comments) | |
sb.append("\n[comment]").append(comment); | |
default: | |
sb.append("type=").append(type).append("[").append(vorbis).append("]"); | |
} | |
sb.append("}"); | |
return sb.toString(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment