/* * myLib - https://github.com/taktod/myLib * Copyright (c) 2014 ttProject. All rights reserved. * * Licensed under The MIT license. */ package com.ttProject.rtmp.netty; import org.apache.log4j.Logger; import com.ttProject.container.flv.amf.Amf0Object; import com.ttProject.rtmp.NetConnection; import com.ttProject.rtmp.NetStream; import com.ttProject.rtmp.command.Amf0; import com.ttProject.rtmp.message.IRtmpMessage; import com.ttProject.rtmp.message.type.Amf0Command; import com.ttProject.rtmp.message.type.UserControlMessage; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; /** * NetStreamHandler * @author taktod */ public class NetStreamHandler extends ChannelInboundHandlerAdapter { private Logger logger = Logger.getLogger(NetStreamHandler.class); private final int transactionId; private final NetConnection conn; private final NetStream ns; private int streamId = -1; public NetStreamHandler(NetConnection conn, NetStream ns) { this.conn = conn; this.ns = ns; conn.addLast(this); transactionId = conn.writeAndFlush(Amf0.createStream(0)); } @SuppressWarnings("unchecked") @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { if(!(msg instanceof IRtmpMessage)) { // logger.info("not IRtmpMessage object, invalid."); return; } IRtmpMessage message = (IRtmpMessage)msg; if(streamId != -1 && streamId != message.getHeader().getStreamId()) { logger.info("not for this handler."); super.channelRead(ctx, msg); return; } switch(message.getHeader().getMessageType()) { case AMF0_COMMAND: Amf0Command command = (Amf0Command)message; switch(command.getCommandType()) { case Result: if(command.getTransactionId() == transactionId) { streamId = ((Double)command.getExtra()).intValue(); switch (ns.getType()) { case Play: ctx.write(Amf0.receiveAudio(true)); ctx.write(Amf0.receiveVideo(true)); // bufferLength UserControlMessage setBufferLength = new UserControlMessage(); setBufferLength.setType(UserControlMessage.Type.CLIENT_BUFFER_LENGTH); setBufferLength.setStreamId(streamId); setBufferLength.setBufferLength(conn.getBufferLength()); ctx.write(setBufferLength); ctx.write(Amf0.play(ns.getStreamName(), streamId)); ctx.flush(); break; case Publish: throw new RuntimeException("publish is underconstruct."); default: break; } } break; case OnStatus: { ns.onStatusEvent((Amf0Object<String, Object>)command.getExtra()); } break; default: break; } break; case USER_CONTROL_MESSAGE: UserControlMessage userControlMessage = (UserControlMessage)message; logger.info(userControlMessage.getType()); // userControlMessage switch(userControlMessage.getType()) { case BUFFER_EMPTY: { // TODO this message if not for NetStream.Buffer.Empty? // streamId of rtmpHeader is different. /* Amf0Object<String, Object> obj = new Amf0Object<String, Object>(); obj.put("level", "status"); obj.put("code", "NetStream.Buffer.Empty"); ns.onStatusEvent(obj);*/ } break; case BUFFER_FULL: { /* Amf0Object<String, Object> obj = new Amf0Object<String, Object>(); obj.put("level", "status"); obj.put("code", "NetStream.Buffer.Full"); ns.onStatusEvent(obj);*/ } break; case STREAM_BEGIN: case STREAM_DRY: case STREAM_EOF: default: break; } break; case AUDIO_MESSAGE: case VIDEO_MESSAGE: case AGGREGATE_MESSAGE: { ns.onMediaReceived(message); } break; default: break; } super.channelRead(ctx, msg); } }