/* * 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.rtmp.client; import java.net.SocketAddress; import nliveroid.nlr.main.BCPlayer; import nliveroid.nlr.main.ClientHandler; import nliveroid.nlr.main.LiveSettings; import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.buffer.ChannelBufferFactory; import org.jboss.netty.buffer.DynamicChannelBuffer; import org.jboss.netty.channel.Channel; import org.jboss.netty.channel.ChannelDownstreamHandler; import org.jboss.netty.channel.ChannelEvent; import org.jboss.netty.channel.ChannelHandlerContext; import org.jboss.netty.channel.ChannelState; import org.jboss.netty.channel.ChannelStateEvent; import org.jboss.netty.channel.ChannelUpstreamHandler; import org.jboss.netty.channel.DownstreamMessageEvent; import org.jboss.netty.channel.MessageEvent; import org.jboss.netty.channel.SucceededChannelFuture; import org.jboss.netty.channel.UpstreamChannelStateEvent; import org.jboss.netty.channel.socket.nio.NioClientSocketPipelineSink; import org.jboss.netty.channel.socket.nio.NioSocketChannel; import android.util.Log; import com.flazr.rtmp.RtmpHandshake; /** * 【重要クラス】 * 最初のRTMPの接続と認証をマネージする、最初のUpstreamHandler * @author Owner * */ public class ClientHandshakeHandler implements ChannelDownstreamHandler,ChannelUpstreamHandler { private final RtmpHandshake handshake; private ClientHandler handler; private NioClientSocketPipelineSink sink; public ClientHandshakeHandler(LiveSettings options, ClientHandler aCT) { this.handler = aCT; //ハンドシェイクインスタンス生成 this.handshake = new RtmpHandshake(options); } /** * コネクトされたら書き込む */ public void channelConnected( ChannelStateEvent e) { Log.d("ClientHandshakeHandler","channelConnected handshake"); try { sink.eventSunkMessageEvent(new DownstreamMessageEvent(e.getChannel(), e.getFuture(), handshake.encodeClient0(), null)); sink.eventSunkMessageEvent(new DownstreamMessageEvent(e.getChannel(), e.getFuture(), handshake.encodeClient1(), null)); } catch (Exception e1) { e1.printStackTrace(); } } protected Object decode( NioSocketChannel channel, ChannelBuffer in) { //RtmpHandshake.HANDSHAKE_SIZEは1536 if(in.readableBytes() < 1 + RtmpHandshake.HANDSHAKE_SIZE * 2) { return null; } handshake.decodeServerAll(in); try { sink.eventSunkMessageEvent(new DownstreamMessageEvent(channel,new SucceededChannelFuture(channel), handshake.encodeClient2(), null)); } catch (Exception e) { e.printStackTrace(); } if(handshake.getSwfvBytes() != null) { handler.setSwfvBytes(handshake.getSwfvBytes()); } //【重要】ここでコンテキストから自分自身(ClientHandshakeHandler)をremoveしている→ここからデータのencode/decodeが始まる(channel.getPipeline().remove(this);又はchannel.getPipeline().removeFirst();) // handler.removeFirst();//少しでも呼び出し元で処理を進めたいのでhandlerで弄る handler.channelConnected( new UpstreamChannelStateEvent( channel, ChannelState.CONNECTED, channel.getRemoteAddress())); return in; } @Override public void handleDownstream(final ChannelHandlerContext ctx, final ChannelEvent ce) { Log.d("ClientHandshakeHandler","handleDownstream"); } /** * handshakeを取得します。 * @return handshake */ public RtmpHandshake getHandshake() { return handshake; } //FrameDecoderと統合 private ChannelBuffer cumulation; @Override public void handleUpstream( ChannelHandlerContext ctx, ChannelEvent e) throws Exception { Log.d("FrameDecoder","handleUpstream"); // if (e instanceof MessageEvent) { // Log.d("FrameDecoder","0"); // messageReceived(ctx, (MessageEvent) e); // } else if (e instanceof WriteCompletionEvent) { // Log.d("FrameDecoder","1"); // WriteCompletionEvent evt = (WriteCompletionEvent) e; // ctx.getPipeline().sendUpstream(ctx,evt); // } else if (e instanceof ChildChannelStateEvent) { // Log.d("FrameDecoder","2"); // ChildChannelStateEvent evt = (ChildChannelStateEvent) e; // ctx.getPipeline().sendUpstream(ctx,evt); // } else if (e instanceof ChannelStateEvent) { // ChannelStateEvent evt = (ChannelStateEvent) e; // Log.d("FrameDecoder","3" + evt.getState()); // switch (evt.getState()) { // case OPEN: // if (Boolean.TRUE.equals(evt.getValue())) { // ctx.getPipeline().sendUpstream(ctx,e); // } else { // channelClosed(ctx, evt); // } // break; // case BOUND: // ctx.getPipeline().sendUpstream(ctx,e); // break; // case CONNECTED: // if (evt.getValue() != null) { // channelConnected( evt); // } else { // channelDisconnected(ctx, evt); // } // break; // case INTEREST_OPS: // ctx.getPipeline().sendUpstream(ctx,e); // break; // default: // ctx.getPipeline().sendUpstream(ctx,e); // } // } else if (e instanceof ExceptionEvent) { // handler.exceptionCaught((ExceptionEvent) e); // } else { // Log.d("FrameDecoder","4"); // ctx.getPipeline().sendUpstream(ctx,e); // } } public void messageReceived( ChannelHandlerContext ctx, MessageEvent e) throws Exception { // Object m = e.getMessage(); // Log.d("FrameDecoder","messageReceived "+(m instanceof ChannelBuffer)); // if (!(m instanceof ChannelBuffer)) { // ctx.getPipeline().sendUpstream(ctx,e); // return; // } // // ChannelBuffer input = (ChannelBuffer) m; // if (!(input.readableBytes() > 0)) { // Log.d("FrameDecoder","!(input.readableBytes()>0)"); // return; // } // // ChannelBuffer cumulation = cumulation(ctx); // if (cumulation.readableBytes() > 0) {//2、3回目のreadでここ // Log.d("FrameDecoder","(cumulation.readableBytes() > 0)"); // cumulation.discardReadBytes(); // cumulation.writeBytes(input,input.readableBytes()); // callDecode(e.getChannel(), cumulation, e.getRemoteAddress()); // } else { // Log.d("FrameDecoder","(cumulation.readableBytes() > 0)ELLSE"); // callDecode(e.getChannel(), input, e.getRemoteAddress()); // if (input.readableBytes() > 0) {//初回のreadを受け取って呼ばれるのがここ // Log.d("FrameDecoder","(cumulation.readableBytes() > 0)&&input.readableBytes() > 0"); // cumulation.writeBytes(input,input.readableBytes()); // } // } } public void handleReadCumulation(Channel channel,MessageEvent e) throws Exception { ChannelBuffer cumulation = cumulation(channel); ChannelBuffer input = (ChannelBuffer) e.getMessage(); if (cumulation.readableBytes() > 0) {//2、3回目のreadでここ Log.d("FrameDecoder","(cumulation.readableBytes() > 0)"); cumulation.discardReadBytes(); cumulation.writeBytes(input,input.readableBytes()); callDecode( e.getChannel(), cumulation, e.getRemoteAddress()); } else { Log.d("FrameDecoder","(cumulation.readableBytes() > 0)ELLSE"); callDecode( e.getChannel(), input, e.getRemoteAddress()); if (input.readableBytes() > 0) {//初回のreadを受け取って呼ばれるのがここ Log.d("FrameDecoder","(cumulation.readableBytes() > 0)&&input.readableBytes() > 0"); cumulation.writeBytes(input,input.readableBytes()); } } } public void channelDisconnected( ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { cleanup( e); } public void channelClosed( ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { cleanup(e); } /** * Decodes the received data so far into a frame when the channel is * disconnected. * * @param ctx the context of this handler * @param channel the current channel * @param buffer the cumulative buffer of received packets so far. * Note that the buffer might be empty, which means you * should not make an assumption that the buffer contains * at least one byte in your decoder implementation. * * @return the decoded frame if a full frame was received and decoded. * {@code null} if there's not enough data in the buffer to decode a frame. */ protected Object decodeLast( ChannelHandlerContext ctx, NioSocketChannel channel, ChannelBuffer buffer) throws Exception { return decode( channel, buffer); } private void callDecode(NioSocketChannel channel, ChannelBuffer cumulation, SocketAddress remoteAddress) throws Exception { Log.d("FrameDecoder","callDecode"); while (cumulation.readableBytes() > 0) { int oldReaderIndex = cumulation.readerIndex(); Object frame = decode( channel, cumulation);//サーバ情報の整理が纏めて呼ばれる if (frame == null) { if (oldReaderIndex == cumulation.readerIndex()) { // Seems like more data is required. // Let us wait for the next notification. break; } else { // Previous data has been discarded. // Probably it is reading on. continue; } } else if (oldReaderIndex == cumulation.readerIndex()) { throw new IllegalStateException( "decode() method must read at least one byte " + "if it returned a frame (caused by: " + getClass() + ")"); } } if (!(cumulation.readableBytes() > 0)) { this.cumulation = null; } Log.d("FrameDecoder","callDecode END ------------- "); } private void cleanup(ChannelStateEvent e) throws Exception { Log.d("FrameDecoder","cleanup"); try { ChannelBuffer cumulation = this.cumulation; if (cumulation == null) { return; } else { this.cumulation = null; } if (cumulation.readableBytes() > 0) { // Make sure all frames are read before notifying a closed channel. callDecode(e.getChannel(), cumulation, null); } } finally { Log.d("FrameDecoder","finally"); // ctx.getPipeline().sendUpstream(ctx,e); } } private ChannelBuffer cumulation(Channel ch) { Log.d("ClientHandshakeHandler","Channelcumulation"); ChannelBuffer c = cumulation; if (c == null) { ChannelBufferFactory factory = ch.getConfig().getBufferFactory(); c = new DynamicChannelBuffer(factory.getDefaultOrder(), 256, factory); cumulation = c; } return c; } public void setSink(NioClientSocketPipelineSink sink) { this.sink = sink; } }