public void writeHead() throws IOException { if (null == streams) { throw new IllegalArgumentException("No Streams Found"); } ChannelBuffer buffer = ChannelBuffers.dynamicBuffer(1024); int dataSize = 0; // flv header buffer.writeByte('F'); buffer.writeByte('L'); buffer.writeByte('V'); buffer.writeByte(0x01); // version buffer.writeByte(0x05); // audio & video buffer.writeInt(0x09); // header size buffer.writeInt(0x00); // first tag size // metadata Map<String, Object> map = new LinkedHashMap<String, Object>(); map.put("duration", 0); for (int i = 0; i < streams.length; i++) { AVStream av = (AVStream) streams[i]; if (av.getFormat() instanceof VideoFormat) { if (av.getWidth() > 0) { map.put("width", av.getWidth()); } if (av.getHeight() > 0) { map.put("heigth", av.getHeight()); } if (av.getFrameRate() > 0) { map.put("videoframerate", av.getFrameRate()); } } else if (av.getFormat() instanceof AudioFormat) { if (av.getSampleRate() > 0) { map.put("audiosamplerate", av.getSampleRate()); } if (av.getNumChannels() > 0) { map.put("audiochannels", av.getNumChannels()); } } } ChannelBuffer onMetaData = ChannelBuffers.dynamicBuffer(); Amf0Value.encode(onMetaData, "onMetaData"); Amf0Value.encode(onMetaData, map); dataSize = onMetaData.readableBytes(); buffer.writeByte(0x18); // script type buffer.writeMedium(dataSize); buffer.writeInt(0); // timestamp buffer.writeMedium(0); // stream id buffer.writeBytes(onMetaData); buffer.writeInt(dataSize + 11); // pre tag size buffer.readBytes(out, buffer.readableBytes()); // stream config(s) for (AVStream stream : streams) { tryWriteHeader(stream); } }
private void tryWriteHeader(AVStream stream) throws IOException { if (stream.getStreamIndex() >= writeHeaders.length) { logger.error("Fail Add Stream#{}", stream.getStreamIndex()); return; } else if (writeHeaders[stream.getStreamIndex()]) { logger.debug("has writen headers"); return; } int dataSize = 0; ChannelBuffer buffer = ChannelBuffers.dynamicBuffer(1024); AVStreamExtra extra = stream.getExtra(); if (extra instanceof H264Extra) { H264Extra h264 = (H264Extra) extra; ByteBuffer profile = h264.readProfile(); if (null == profile || profile.remaining() != 3) { logger.warn("H264.profile is NOT FOUND"); return; } ChannelBuffer avc = ChannelBuffers.buffer(128); avc.writeByte(0x17); // key frame + avc avc.writeByte(0x00); // avc sequence header avc.writeMedium(0x00); // Composition time offset avc.writeByte(0x01); avc.writeBytes(profile); avc.writeByte(0xFF); // 4 byte for nal header length // sps(s) ByteBuffer sps = h264.readSps(); avc.writeByte(0xE0 | 0x01); avc.writeShort(sps.remaining()); avc.writeBytes(sps); // pps(s) ByteBuffer pps = h264.readPps(); avc.writeByte(0x01); avc.writeShort(pps.remaining()); avc.writeBytes(pps); dataSize = avc.readableBytes(); buffer.writeByte(0x09); // video type buffer.writeMedium(dataSize); buffer.writeInt(0); // timestamp buffer.writeMedium(0); // stream id buffer.writeBytes(avc); buffer.writeInt(11 + dataSize); buffer.readBytes(out, buffer.readableBytes()); writeHeaders[stream.getStreamIndex()] = true; } else if (extra instanceof AACExtra) { AACExtra aac = (AACExtra) extra; ByteBuffer aacHeader = ByteBuffer.allocate(4); aacHeader.put((byte) 0xAF); // aac, 44100, 2 channels, stereo aacHeader.put((byte) 0x00); // aac header int objectType = aac.getObjectType(); int sampleRateIndex = aac.getSampleRateIndex(); int numChannels = aac.getNumChannels(); BitWriter w = new BitWriter(aacHeader); w.writeNBit(objectType, 5); w.writeNBit(sampleRateIndex, 4); w.writeNBit(numChannels, 4); w.writeNBit(0, 1); // frame length - 1024 samples w.writeNBit(0, 1); // does not depend on core coder w.writeNBit(0, 1); // is not extension w.flush(); aacHeader.flip(); dataSize = aacHeader.remaining(); buffer.writeByte(0x08); buffer.writeMedium(dataSize); // data size buffer.writeInt(0x00); // timestamp buffer.writeMedium(0x00); // stream id buffer.writeBytes(aacHeader); // data buffer.writeInt(11 + dataSize); // pre tag size buffer.readBytes(out, buffer.readableBytes()); writeHeaders[stream.getStreamIndex()] = true; } else if (null == stream.getExtra()) { logger.warn("no stream extra found {}", stream.getFormat()); } else { logger.warn("unsupported {}", stream); } }