/* * Flazr <http://flazr.com> Copyright (C) 2009 Peter Thomas. * * This file is part of Flazr. * * Flazr is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Flazr is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Flazr. If not, see <http://www.gnu.org/licenses/>. */ package com.flazr.io.flv; import java.io.File; import java.io.FileOutputStream; import java.nio.channels.FileChannel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.flazr.rtmp.RtmpHeader; import com.flazr.rtmp.RtmpMessage; import com.flazr.rtmp.RtmpWriter; import org.jboss.netty.buffer.ChannelBuffer; public class FlvWriter implements RtmpWriter { private static final Logger logger = LoggerFactory.getLogger(FlvWriter.class); private final FileChannel out; private final int[] channelTimes = new int[RtmpHeader.MAX_CHANNEL_ID]; private int primaryChannel = -1; private int lastLoggedSeconds; private final int seekTime; private final long startTime; public FlvWriter(final String fileName) { this(0, fileName); } public FlvWriter(final int seekTime, final String fileName) { this.seekTime = seekTime < 0 ? 0 : seekTime; this.startTime = System.currentTimeMillis(); if(fileName == null) { logger.info("save file notspecified, will only consume stream"); out = null; return; } try { File file = new File(fileName); FileOutputStream fos = new FileOutputStream(file); out = fos.getChannel(); out.write(FlvAtom.flvHeader().toByteBuffer()); logger.info("opened file for writing: {}", file.getAbsolutePath()); } catch (Exception e) { throw new RuntimeException(e); } } @Override public void close() { if(out != null) { try { out.close(); } catch (Exception e) { throw new RuntimeException(e); } } if(primaryChannel == -1) { logger.warn("no media was written, closed file"); return; } logger.info("finished in {} seconds, media duration: {} seconds (seek time: {})", new Object[]{(System.currentTimeMillis() - startTime) / 1000, (channelTimes[primaryChannel] - seekTime) / 1000, seekTime / 1000}); } private void logWriteProgress() { final int seconds = (channelTimes[primaryChannel] - seekTime) / 1000; if (seconds >= lastLoggedSeconds + 10) { logger.info("write progress: " + seconds + " seconds"); lastLoggedSeconds = seconds - (seconds % 10); } } @Override public void write(final RtmpMessage message) { final RtmpHeader header = message.getHeader(); if(header.isAggregate()) { final ChannelBuffer in = message.encode(); while (in.readable()) { final FlvAtom flvAtom = new FlvAtom(in); final int absoluteTime = flvAtom.getHeader().getTime(); channelTimes[primaryChannel] = absoluteTime; write(flvAtom); // logger.debug("aggregate atom: {}", flvAtom); logWriteProgress(); } } else { // METADATA / AUDIO / VIDEO final int channelId = header.getChannelId(); channelTimes[channelId] = seekTime + header.getTime(); if(primaryChannel == -1 && (header.isAudio() || header.isVideo())) { logger.info("first media packet for channel: {}", header); primaryChannel = channelId; } if(header.getSize() <= 2) { return; } write(new FlvAtom(header.getMessageType(), channelTimes[channelId], message.encode())); if (channelId == primaryChannel) { logWriteProgress(); } } } private void write(final FlvAtom flvAtom) { if(logger.isDebugEnabled()) { logger.debug("writing: {}", flvAtom); } if(out == null) { return; } try { out.write(flvAtom.write().toByteBuffer()); } catch (Exception e) { throw new RuntimeException(e); } } }