/* * 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.message; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; import nliveroid.nlr.main.LiveSettings; import org.jboss.netty.buffer.ChannelBuffer; import com.flazr.rtmp.RtmpHeader; import com.flazr.rtmp.RtmpMessage; import com.flazr.rtmp.client.Amf0Object; import com.flazr.rtmp.client.PublishTypeSubscriber.PublishType; import com.flazr.util.Utils; import com.flazr.util.Utils.Pair; public abstract class Command implements RtmpMessage { protected String name; protected int transactionId; protected Amf0Object object; protected Object[] args; protected final RtmpHeader header; public Command(RtmpHeader header, ChannelBuffer in) { this.header = header; decode(in); } public Command(int transactionId, String name, Amf0Object object, Object ... args) { header = new RtmpHeader(getMessageType()); this.transactionId = transactionId; this.name = name; this.object = object; this.args = args; } public Command(String name, Amf0Object object, Object ... args) { this(0, name, object, args); } public Amf0Object getObject() { return object; } public Object getArg(int index) { return args[index]; } public int getArgCount() { if(args == null) { return 0; } return args.length; } //========================================================================== public static enum OnStatus { ERROR, STATUS, WARNING; public static OnStatus parse(final String raw) { return OnStatus.valueOf(raw.substring(1).toUpperCase()); } public String asString() { return "_" + this.name().toLowerCase(); } } private static Amf0Object onStatus(final OnStatus level, final String code, final String description, final String details, final Pair ... pairs) { final Amf0Object object = Utils.createAmfObject(new Amf0Object(), Utils.createPair("level", level.asString()), Utils.createPair("code", code)); if(description != null) { object.put("description", description); } if(details != null) { object.put("details", details); } return Utils.createAmfObject(object, pairs); } private static Amf0Object onStatus(final OnStatus level, final String code, final String description, final Pair ... pairs) { return onStatus(level, code, description, null, pairs); } public static Amf0Object onStatus(final OnStatus level, final String code, final Pair ... pairs) { return onStatus(level, code, null, null, pairs); } //========================================================================== public static Command connect(LiveSettings options) { Amf0Object object = Utils.createAmfObject(new Amf0Object(), Utils.createPair("app", options.getAppName()), Utils.createPair("flashVer", "WIN 9,0,124,2"), Utils.createPair("tcUrl", options.getTcUrl()), Utils.createPair("fpad", false), Utils.createPair("audioCodecs", 1639.0), Utils.createPair("videoCodecs", 252.0), Utils.createPair("objectEncoding", 0.0), Utils.createPair("capabilities", 15.0), Utils.createPair("videoFunction", 1.0)); if(options.getParams() != null) { object.putAll(options.getParams()); } return new CommandAmf0("connect", object, options.getArgs()); } public static Command connectSuccess(int transactionId) { Map<String, Object> object = onStatus(OnStatus.STATUS, "NetConnection.Connect.Success", "Connection succeeded.", Utils.createPair("fmsVer", "FMS/3,5,1,516"), Utils.createPair("capabilities", 31.0), Utils.createPair("mode", 1.0), Utils.createPair("objectEncoding", 0.0)); return new CommandAmf0(transactionId, "_result", null, object); } public static Command createStream() { return new CommandAmf0("createStream", null); } public static Command onBWDone() { return new CommandAmf0("onBWDone", null); } public static Command createStreamSuccess(int transactionId, int streamId) { return new CommandAmf0(transactionId, "_result", null, streamId); } public static Command play(int streamId, LiveSettings options) { final List playArgs = new ArrayList(); playArgs.add(options.getStreamName()); if(options.getStart() != -2 || options.getArgs() != null) { playArgs.add(options.getStart()); } if(options.getLength() != -1 || options.getArgs() != null) { playArgs.add(options.getLength()); } if(options.getArgs() != null) { playArgs.addAll(Arrays.asList(options.getArgs())); } Command command = new CommandAmf0("play", null, playArgs.toArray()); command.header.setChannelId(8); command.header.setStreamId(streamId); return command; } private static Command playStatus(String code, String description, String playName, String clientId, Pair ... pairs) { Amf0Object status = onStatus(OnStatus.STATUS, "NetStream.Play." + code, description + " " + playName + ".", Utils.createPair("details", playName), Utils.createPair("clientid", clientId)); Utils.createAmfObject(status, pairs); Command command = new CommandAmf0("onStatus", null, status); command.header.setChannelId(5); return command; } public static Command playReset(String playName, String clientId) { Command command = playStatus("Reset", "Playing and resetting", playName, clientId); command.header.setChannelId(4); // ? return command; } public static Command playStart(String playName, String clientId) { Command play = playStatus("Start", "Started playing", playName, clientId); return play; } public static Command playStop(String playName, String clientId) { return playStatus("Stop", "Stopped playing", playName, clientId); } public static Command playFailed(String playName, String clientId) { Amf0Object status = onStatus(OnStatus.ERROR, "NetStream.Play.Failed", "Stream not found"); Command command = new CommandAmf0("onStatus", null, status); command.header.setChannelId(8); return command; } public static Command seekNotify(int streamId, int seekTime, String playName, String clientId) { Amf0Object status = onStatus(OnStatus.STATUS, "NetStream.Seek.Notify", "Seeking " + seekTime + " (stream ID: " + streamId + ").", Utils.createPair("details", playName), Utils.createPair("clientid", clientId)); Command command = new CommandAmf0("onStatus", null, status); command.header.setChannelId(5); command.header.setStreamId(streamId); command.header.setTime(seekTime); return command; } public static Command pauseNotify(String playName, String clientId) { Amf0Object status = onStatus(OnStatus.STATUS, "NetStream.Pause.Notify", "Pausing " + playName, Utils.createPair("details", playName), Utils.createPair("clientid", clientId)); Command command = new CommandAmf0("onStatus", null, status); command.header.setChannelId(5); return command; } public static Command unpauseNotify(String playName, String clientId) { Amf0Object status = onStatus(OnStatus.STATUS, "NetStream.Unpause.Notify", "Unpausing " + playName, Utils.createPair("details", playName), Utils.createPair("clientid", clientId)); Command command = new CommandAmf0("onStatus", null, status); command.header.setChannelId(5); return command; } public static Command publish(int streamId, LiveSettings options) { // TODO Command command = new CommandAmf0("publish", null, options.getStreamName(), PublishType.LIVE.asString()); command.header.setChannelId(8); command.header.setStreamId(streamId); return command; } private static Command publishStatus(String code, String streamName, String clientId, Pair ... pairs) { Amf0Object status = onStatus(OnStatus.STATUS, code, null, streamName, Utils.createPair("details", streamName), Utils.createPair("clientid", clientId)); Utils.createAmfObject(status, pairs); Command command = new CommandAmf0("onStatus", null, status); command.header.setChannelId(8); return command; } public static Command publishStart(String streamName, String clientId, int streamId) { return publishStatus("NetStream.Publish.Start", streamName, clientId); } public static Command unpublishSuccess(String streamName, String clientId, int streamId) { return publishStatus("NetStream.Unpublish.Success", streamName, clientId); } public static Command unpublish(int streamId) { Command command = new CommandAmf0("publish", null, false); command.header.setChannelId(8); command.header.setStreamId(streamId); return command; } public static Command publishBadName(int streamId) { Command command = new CommandAmf0("onStatus", null, onStatus(OnStatus.ERROR, "NetStream.Publish.BadName", "Stream already exists.")); command.header.setChannelId(8); command.header.setStreamId(streamId); return command; } public static Command publishNotify(int streamId) { Command command = new CommandAmf0("onStatus", null, onStatus(OnStatus.STATUS, "NetStream.Play.PublishNotify")); command.header.setChannelId(8); command.header.setStreamId(streamId); return command; } public static Command unpublishNotify(int streamId) { Command command = new CommandAmf0("onStatus", null, onStatus(OnStatus.STATUS, "NetStream.Play.UnpublishNotify")); command.header.setChannelId(8); command.header.setStreamId(streamId); return command; } public static Command closeStream(int streamId) { Command command = new CommandAmf0("closeStream", null); command.header.setChannelId(8); command.header.setStreamId(streamId); return command; } //========================================================================== public String getName() { return name; } public int getTransactionId() { return transactionId; } public void setTransactionId(int transactionId) { this.transactionId = transactionId; } @Override public String toString() { final StringBuilder sb = new StringBuilder(); sb.append(super.toString()); sb.append("name: ").append(name); sb.append(", transactionId: ").append(transactionId); sb.append(", object: ").append(object); sb.append(", args: ").append(Arrays.toString(args)); return sb.toString(); } }