Search in sources :

Example 1 with Output

use of org.red5.io.amf.Output in project red5-server-common by Red5.

the class FileConsumer method setupOutputPath.

/**
 * Sets up the output file path for writing.
 *
 * @param name
 *            output filename to use
 */
public void setupOutputPath(String name) {
    // get stream filename generator
    IStreamFilenameGenerator generator = (IStreamFilenameGenerator) ScopeUtils.getScopeService(scope, IStreamFilenameGenerator.class, DefaultStreamFilenameGenerator.class);
    // generate file path
    String filePath = generator.generateFilename(scope, name, ".flv", GenerationType.RECORD);
    this.path = generator.resolvesToAbsolutePath() ? Paths.get(filePath) : Paths.get(System.getProperty("red5.root"), "webapps", scope.getContextPath(), filePath);
    // if append was requested, ensure the file we want to append exists (append==record)
    File appendee = getFile();
    if (IClientStream.MODE_APPEND.equals(mode) && !appendee.exists()) {
        try {
            if (appendee.createNewFile()) {
                log.debug("New file created for appending");
            } else {
                log.debug("Failure to create new file for appending");
            }
        } catch (IOException e) {
            log.warn("Exception creating replacement file for append", e);
        }
    }
}
Also used : IOException(java.io.IOException) DefaultStreamFilenameGenerator(org.red5.server.stream.DefaultStreamFilenameGenerator) File(java.io.File) IStreamFilenameGenerator(org.red5.server.api.stream.IStreamFilenameGenerator)

Example 2 with Output

use of org.red5.io.amf.Output in project red5-io by Red5.

the class FLVReader method createFileMeta.

/**
 * Create tag for metadata event.
 *
 * @return Metadata event tag
 */
private ITag createFileMeta() {
    // Create tag for onMetaData event
    IoBuffer buf = IoBuffer.allocate(192);
    buf.setAutoExpand(true);
    Output out = new Output(buf);
    // Duration property
    out.writeString("onMetaData");
    Map<Object, Object> props = new HashMap<Object, Object>();
    props.put("duration", duration / 1000.0);
    if (firstVideoTag != -1) {
        long old = getCurrentPosition();
        setCurrentPosition(firstVideoTag);
        try {
            readTagHeader();
            fillBuffer(1);
            int codecId = in.get() & MASK_VIDEO_CODEC;
            // Video codec id
            props.put("videocodecid", codecId);
        } catch (UnsupportedDataTypeException e) {
            log.warn("createFileMeta for video", e);
        }
        setCurrentPosition(old);
    }
    if (firstAudioTag != -1) {
        long old = getCurrentPosition();
        setCurrentPosition(firstAudioTag);
        try {
            readTagHeader();
            fillBuffer(1);
            int codecId = (in.get() & MASK_SOUND_FORMAT) >> 4;
            // Audio codec id
            props.put("audiocodecid", codecId);
        } catch (UnsupportedDataTypeException e) {
            log.warn("createFileMeta for audio", e);
        }
        setCurrentPosition(old);
    }
    props.put("canSeekToEnd", true);
    out.writeMap(props);
    buf.flip();
    ITag result = new Tag(IoConstants.TYPE_METADATA, 0, buf.limit(), null, 0);
    result.setBody(buf);
    out = null;
    return result;
}
Also used : ITag(org.red5.io.ITag) HashMap(java.util.HashMap) Output(org.red5.io.amf.Output) ITag(org.red5.io.ITag) IoBuffer(org.apache.mina.core.buffer.IoBuffer)

Example 3 with Output

use of org.red5.io.amf.Output in project red5-io by Red5.

the class FLVWriter method writeMetadataTag.

/**
 * Write "onMetaData" tag to the file.
 *
 * @param duration
 *            Duration to write in milliseconds.
 * @param videoCodecId
 *            Id of the video codec used while recording.
 * @param audioCodecId
 *            Id of the audio codec used while recording.
 * @throws IOException
 *             if the tag could not be written
 * @throws ExecutionException
 * @throws InterruptedException
 */
private void writeMetadataTag(double duration, int videoCodecId, int audioCodecId) throws IOException, InterruptedException, ExecutionException {
    log.debug("writeMetadataTag - duration: {} video codec: {} audio codec: {}", new Object[] { duration, videoCodecId, audioCodecId });
    IoBuffer buf = IoBuffer.allocate(256);
    buf.setAutoExpand(true);
    Output out = new Output(buf);
    out.writeString("onMetaData");
    Map<Object, Object> params = new HashMap<>();
    if (meta != null) {
        params.putAll(meta);
    }
    params.putIfAbsent("server", "Red5");
    params.putIfAbsent("recordeddate", recordedDate);
    params.put("duration", (Number) duration);
    if (log.isDebugEnabled()) {
        log.debug("Stored duration: {}", params.get("duration"));
    }
    if (videoCodecId != -1) {
        params.put("videocodecid", (videoCodecId == 7 ? "avc1" : (videoCodecId == 12 ? "hevc" : videoCodecId)));
        if (videoDataSize > 0) {
            // from bytes to kilobits
            params.put("videodatarate", 8 * videoDataSize / 1024 / duration);
        }
    } else {
        // place holder
        params.put("novideocodec", 0);
    }
    if (audioCodecId != -1) {
        params.put("audiocodecid", (audioCodecId == 10 ? "mp4a" : (audioCodecId == 13 ? "opus" : audioCodecId)));
        if (audioCodecId == AudioCodec.AAC.getId()) {
            params.put("audiosamplerate", 44100);
            params.put("audiosamplesize", 16);
        } else if (audioCodecId == AudioCodec.SPEEX.getId()) {
            params.put("audiosamplerate", 16000);
            params.put("audiosamplesize", 16);
        } else {
            params.put("audiosamplerate", soundRate);
            params.put("audiosamplesize", soundSize);
        }
        params.put("stereo", soundType);
        if (audioDataSize > 0) {
            // from bytes to kilobits
            params.put("audiodatarate", 8 * audioDataSize / 1024 / duration);
        }
    } else {
        // place holder
        params.put("noaudiocodec", 0);
    }
    // this is actual only supposed to be true if the last video frame is a keyframe
    params.put("canSeekToEnd", true);
    out.writeMap(params);
    buf.flip();
    int bodySize = buf.limit();
    log.debug("Metadata size: {}", bodySize);
    // set a var holding the entire tag size including the previous tag length
    int totalTagSize = TAG_HEADER_LENGTH + bodySize + 4;
    // create a buffer for this tag
    ByteBuffer tagBuffer = ByteBuffer.allocate(totalTagSize);
    // get the timestamp
    int timestamp = 0;
    // create an array big enough
    byte[] bodyBuf = new byte[bodySize];
    // put the bytes into the array
    buf.get(bodyBuf);
    // Data Type
    // 1
    IOUtils.writeUnsignedByte(tagBuffer, ITag.TYPE_METADATA);
    // Body Size - Length of the message. Number of bytes after StreamID to end of tag
    // (Equal to length of the tag - 11)
    // 3
    IOUtils.writeMediumInt(tagBuffer, bodySize);
    // Timestamp
    // 4
    IOUtils.writeExtendedMediumInt(tagBuffer, timestamp);
    // Stream id
    // 3
    tagBuffer.put(DEFAULT_STREAM_ID);
    if (log.isTraceEnabled()) {
        log.trace("Tag buffer (after tag header) limit: {} remaining: {}", tagBuffer.limit(), tagBuffer.remaining());
    }
    // get the body
    tagBuffer.put(bodyBuf);
    if (log.isTraceEnabled()) {
        log.trace("Tag buffer (after body) limit: {} remaining: {}", tagBuffer.limit(), tagBuffer.remaining());
    }
    // we add the tag size
    tagBuffer.putInt(TAG_HEADER_LENGTH + bodySize);
    if (log.isTraceEnabled()) {
        log.trace("Tag buffer (after prev tag size) limit: {} remaining: {}", tagBuffer.limit(), tagBuffer.remaining());
    }
    // flip so we can process from the beginning
    tagBuffer.flip();
    // write the tag
    if (log.isTraceEnabled()) {
        log.trace("Writing metadata starting at position: {}", bytesWritten);
    }
    // add to the total bytes written
    bytesWritten += fileChannel.write(tagBuffer);
    if (log.isTraceEnabled()) {
        log.trace("Updated position: {}", bytesWritten);
    }
    tagBuffer.clear();
    buf.clear();
}
Also used : HashMap(java.util.HashMap) Output(org.red5.io.amf.Output) ByteBuffer(java.nio.ByteBuffer) IoBuffer(org.apache.mina.core.buffer.IoBuffer)

Example 4 with Output

use of org.red5.io.amf.Output in project red5-io by Red5.

the class FLVWriter method writeHeader.

/**
 * Writes the header bytes
 *
 * @throws IOException
 *             Any I/O exception
 */
@Override
public void writeHeader() throws IOException {
    // create a buffer
    // FLVHeader (9 bytes) + PreviousTagSize0 (4 bytes)
    ByteBuffer buf = ByteBuffer.allocate(HEADER_LENGTH + 4);
    // instance an flv header
    FLVHeader flvHeader = new FLVHeader();
    flvHeader.setFlagAudio(audioCodecId != -1 ? true : false);
    flvHeader.setFlagVideo(videoCodecId != -1 ? true : false);
    // write the flv header in the buffer
    flvHeader.write(buf);
    // the final version of the file will go here
    createOutputFile();
    // write header to output channel
    bytesWritten = fileChannel.write(buf);
    assert ((HEADER_LENGTH + 4) - bytesWritten == 0);
    log.debug("Header size: {} bytes written: {}", (HEADER_LENGTH + 4), bytesWritten);
    buf.clear();
    buf = null;
}
Also used : FLVHeader(org.red5.io.flv.FLVHeader) ByteBuffer(java.nio.ByteBuffer)

Example 5 with Output

use of org.red5.io.amf.Output in project red5-server-common by Red5.

the class RTMPMinaProtocolEncoder method encode.

/**
 * {@inheritDoc}
 */
public void encode(IoSession session, Object message, ProtocolEncoderOutput out) throws ProtocolCodecException {
    // get the connection from the session
    String sessionId = (String) session.getAttribute(RTMPConnection.RTMP_SESSION_ID);
    log.trace("Session id: {}", sessionId);
    @SuppressWarnings("unchecked") IConnectionManager<RTMPConnection> connManager = (IConnectionManager<RTMPConnection>) ((WeakReference<?>) session.getAttribute(RTMPConnection.RTMP_CONN_MANAGER)).get();
    RTMPConnection conn = (RTMPConnection) connManager.getConnectionBySessionId(sessionId);
    if (conn != null) {
        // look for and compare the connection local; set it from the session
        RTMPConnection localConn = (RTMPConnection) Red5.getConnectionLocal();
        if (!conn.equals(localConn)) {
            if (localConn != null) {
                log.debug("Connection local ({}) didn't match io session ({})", localConn.getSessionId(), sessionId);
            }
            // replace conn with the one from the session id lookup
            Red5.setConnectionLocal(conn);
        }
        Boolean interrupted = false;
        Semaphore lock = conn.getEncoderLock();
        try {
            // acquire the encoder lock
            // log.trace("Encoder lock acquiring.. {}", conn.getSessionId());
            lock.acquire();
            log.trace("Encoder lock acquired {}", conn.getSessionId());
            // get the buffer
            final IoBuffer buf = message instanceof IoBuffer ? (IoBuffer) message : encoder.encode(message);
            if (buf != null) {
                int requestedWriteChunkSize = conn.getState().getWriteChunkSize();
                log.trace("Requested chunk size: {} target chunk size: {}", requestedWriteChunkSize, targetChunkSize);
                if (buf.remaining() <= targetChunkSize * 2) {
                    log.trace("Writing output data");
                    out.write(buf);
                } else {
                    int sentChunks = Chunker.chunkAndWrite(out, buf, requestedWriteChunkSize, targetChunkSize);
                    log.trace("Wrote {} chunks", sentChunks);
                }
            } else {
                log.trace("Response buffer was null after encoding");
            }
        } catch (InterruptedException ex) {
            log.error("InterruptedException during encode", ex);
            interrupted = true;
        } catch (Exception ex) {
            log.error("Exception during encode", ex);
        } finally {
            log.trace("Encoder lock releasing.. {}", conn.getSessionId());
            lock.release();
            if (interrupted && log.isInfoEnabled()) {
                log.info("Released lock after interruption. session {}, permits {}", conn.getSessionId(), lock.availablePermits());
            }
        }
        // set connection local back to previous value
        if (localConn != null) {
            Red5.setConnectionLocal(localConn);
        }
    } else {
        log.debug("Connection is no longer available for encoding, may have been closed already");
    }
}
Also used : IConnectionManager(org.red5.server.net.IConnectionManager) RTMPConnection(org.red5.server.net.rtmp.RTMPConnection) Semaphore(java.util.concurrent.Semaphore) ProtocolCodecException(org.apache.mina.filter.codec.ProtocolCodecException) IoBuffer(org.apache.mina.core.buffer.IoBuffer)

Aggregations

IoBuffer (org.apache.mina.core.buffer.IoBuffer)21 Output (org.red5.io.amf.Output)18 HashMap (java.util.HashMap)9 ITag (org.red5.io.ITag)8 Tag (org.red5.io.flv.impl.Tag)7 IOException (java.io.IOException)5 IMessageOutput (org.red5.server.messaging.IMessageOutput)5 Map (java.util.Map)4 StatusObject (org.red5.server.net.rtmp.status.StatusObject)4 File (java.io.File)3 ByteBuffer (java.nio.ByteBuffer)3 Output (org.red5.io.amf3.Output)3 Output (org.red5.io.object.Output)3 RTMPConnection (org.red5.server.net.rtmp.RTMPConnection)3 Notify (org.red5.server.net.rtmp.event.Notify)3 ArrayList (java.util.ArrayList)2 BeanMap (org.apache.commons.beanutils.BeanMap)2 IAudioStreamCodec (org.red5.codec.IAudioStreamCodec)2 IStreamCodecInfo (org.red5.codec.IStreamCodecInfo)2 IVideoStreamCodec (org.red5.codec.IVideoStreamCodec)2