require "opentok/client" require "opentok/broadcast" require "opentok/broadcast_list" module OpenTok # A class for working with OpenTok live streaming broadcasts. # See {https://tokbox.com/developer/guides/broadcast/live-streaming/ Live streaming broadcasts}. class Broadcasts # @private def initialize(client) @client = client end # Starts a live streaming broadcast of an OpenTok session. # # Clients must be actively connected to the OpenTok session for you to successfully start # a broadcast. # # This broadcasts the session to an HLS (HTTP live streaming) or to RTMP streams. # # @param [String] session_id The session ID of the OpenTok session to broadcast. # # @param [Hash] options A hash defining options for the broadcast. # # @option options [Hash] :layout Specify this to assign the initial layout type for # the broadcast. This is a hash containing three keys: # :type, :stylesheet and :screenshare_type. # Valid values for :type are "bestFit" (best fit), "custom" (custom), # "horizontalPresentation" (horizontal presentation), # "pip" (picture-in-picture), and "verticalPresentation" (vertical presentation)). # If you specify a "custom" layout type, set the :stylesheet key to the # stylesheet (CSS). (For other layout types, do not set the :stylesheet key.) # Valid values for :screenshare_type are "bestFit", "pip", # "verticalPresentation", "horizontalPresentation". This property is optional. # If it is specified, then the :type property **must** be set to "bestFit". # If you do not specify an initial layout type, the broadcast uses the best fit # layout type. # # @option options [String] :multiBroadcastTag (Optional) Set this to support multiple broadcasts for the same session simultaneously. # Set this to a unique string for each simultaneous broadcast of an ongoing session. Note that the `multiBroadcastTag` value is *not* included # in the response for the methods to {https://tokbox.com/developer/rest/#list_broadcasts list live streaming broadcasts} and # {https://tokbox.com/developer/rest/#get_info_broadcast get information about a live streaming broadcast}. # {https://tokbox.com/developer/guides/broadcast/live-streaming#simultaneous-broadcasts See Simultaneous broadcasts}. # # @option options [int] maxBitRate # The maximum bitrate for the broadcast stream(s), in bits per second. # The minimum value is 100,000 and the maximum is 6,000,000. # # @option options [int] maxDuration # The maximum duration for the broadcast, in seconds. The broadcast will automatically stop when # the maximum duration is reached. You can set the maximum duration to a value from 60 (60 seconds) to 36000 (10 hours). # The default maximum duration is 4 hours (14,400 seconds). # # @option options [Hash] outputs (Required) # This object defines the types of broadcast streams you want to start (both HLS and RTMP). # You can include HLS, RTMP, or both as broadcast streams. If you include RTMP streaming, # you can specify up to five target RTMP streams (or just one). # # For multiple RTMP streams, the (:rtmp) property is set to an [Array] of Rtmp [Hash] objects. # For each RTMP hash, specify (:serverUrl) for the RTMP server URL, # (:streamName) such as the YouTube Live stream name or the Facebook stream key), # and (optionally) (:id), a unique ID for the stream. If you specify an ID, it will be # included in the (broadcast_json) response from the Client#start_broadcast method call, # and is also available in the (broadcast_json) response from the Broadcasts#find method. # Vonage streams the session to each RTMP URL you specify. Note that OpenTok live streaming # supports RTMP and RTMPS. # If you need to support only one RTMP URL, you can set a Rtmp [Hash] object (instead of an array of # objects) for the (:rtmp) property value in the (:outputs) [Hash]. # # For HLS, the (:hls) property in the (:outputs) [Hash] is set to a HLS [Hash] # object. This object includes the following optional properties: # - (:dvr) (Boolean). Whether to enable # {https://tokbox.com/developer/guides/broadcast/live-streaming/#dvr DVR functionality} # (rewinding, pausing, and resuming) # in players that support it (true), or not (false, the default). With DVR enabled, the HLS URL will # include a ?DVR query string appended to the end. # - (:low_latency) (Boolean). Whether to enable # {https://tokbox.com/developer/guides/broadcast/live-streaming/#low-latency low-latency mode} # for the HLSstream. # Some HLS players do not support low-latency mode. This feature is incompatible with DVR mode HLS # broadcasts (both can't be set to true). This is a beta feature. # The HLS URL is included in the (broadcast_json) response from the Client#start_broadcast # method call, and is also available in the (broadcast_json) response from the # Broadcasts#find method. # # @option options [string] resolution # The resolution of the broadcast: either "640x480" (SD landscape, the default), "1280x720" (HD landscape), # "1920x1080" (FHD landscape), "480x640" (SD portrait), "720x1280" (HD portrait), or "1080x1920" # (FHD portrait). # # @option options [String] :streamMode (Optional) Whether streams included in the broadcast are selected # automatically ("auto", the default) or manually ("manual"). When streams are selected automatically ("auto"), # all streams in the session can be included in the broadcast. When streams are selected manually ("manual"), # you specify streams to be included based on calls to the # {Broadcasts#add_stream} method. You can specify whether a # stream's audio, video, or both are included in the broadcast. # For both automatic and manual modes, the broadcast composer includes streams based # on {https://tokbox.com/developer/guides/archive-broadcast-layout/#stream-prioritization-rules stream prioritization rules}. # Important: this feature is currently available in the Standard environment only. # # @option options [Boolean] :hasAudio Whether the broadcast has audio (default `true`) # # @option options [Boolean] :hasVideo Whether the broadcast has video (default `true`) # # @return [Broadcast] The broadcast object, which includes properties defining the broadcast, # including the broadcast ID. # # @raise [OpenTokBroadcastError] The broadcast could not be started. The request was invalid or broadcast already started # @raise [OpenTokAuthenticationError] Authentication failed while starting an broadcast. # Invalid API key. # @raise [OpenTokError] OpenTok server error. def create(session_id, options = {}) raise ArgumentError, "session_id not provided" if session_id.to_s.empty? raise ArgumentError, "options cannot be empty" if options.empty? raise ArgumentError, "outputs property is required in options" unless options.has_key?(:outputs) raise ArgumentError, "outputs must be a Hash" unless options[:outputs].is_a? Hash if options[:outputs].has_key?(:hls) dvr = options[:outputs][:hls][:dvr] low_latency = options[:outputs][:hls][:low_latency] raise ArgumentError, "dvr and low_latency can't both be true for HLS" if hls_dvr_and_low_latency_options_both_true?(dvr, low_latency) end broadcast_json = @client.start_broadcast(session_id, options) Broadcast.new self, broadcast_json end # Gets a Broadcast object for the given broadcast ID. # # @param [String] broadcast_id The broadcast ID. # # @return [Broadcast] The broadcast object, which includes properties defining the broadcast. # # @raise [OpenTokBroadcastError] No matching broadcast found. # @raise [OpenTokAuthenticationError] Authentication failed. # Invalid API key. # @raise [OpenTokError] OpenTok server error. def find(broadcast_id) raise ArgumentError, "broadcast_id not provided" if broadcast_id.to_s.empty? broadcast_json = @client.get_broadcast(broadcast_id.to_s) Broadcast.new self, broadcast_json end # Returns a BroadcastList, which is an array of broadcasts that are completed and in-progress, # for your API key. # # @param [Hash] options A hash with keys defining which range of broadcasts to retrieve. # @option options [integer] :offset Optional. The index offset of the first broadcast. 0 is offset # of the most recently started broadcast. 1 is the offset of the broadcast that started prior to # the most recent broadcast. If you do not specify an offset, 0 is used. # @option options [integer] :count Optional. The number of broadcasts to be returned. The maximum # number of broadcasts returned is 1000. # @option options [String] :session_id Optional. The session ID that broadcasts belong to. # https://tokbox.com/developer/rest/#list_broadcasts # # @return [BroadcastList] An BroadcastList object, which is an array of Broadcast objects. def all(options = {}) raise ArgumentError, "Limit is invalid" unless options[:count].nil? || (0..1000).include?(options[:count]) broadcast_list_json = @client.list_broadcasts(options[:offset], options[:count], options[:sessionId]) BroadcastList.new self, broadcast_list_json end # Stops an OpenTok broadcast # # Note that broadcasts automatically stop after 120 minute # # @param [String] broadcast_id The broadcast ID. # # @return [Broadcast] The broadcast object, which includes properties defining the broadcast. # # @raise [OpenTokBroadcastError] The broadcast could not be stopped. The request was invalid. # @raise [OpenTokAuthenticationError] Authentication failed. # Invalid API key. # @raise [OpenTokError] OpenTok server error. def stop(broadcast_id) raise ArgumentError, "broadcast_id not provided" if broadcast_id.to_s.empty? broadcast_json = @client.stop_broadcast(broadcast_id) Broadcast.new self, broadcast_json end # Dynamically alters the layout an OpenTok broadcast. For more information, see # For more information, see # {https://tokbox.com/developer/guides/broadcast/live-streaming/#configuring-video-layout-for-opentok-live-streaming-broadcasts Configuring video layout for OpenTok live streaming broadcasts}. # # @param [String] broadcast_id # The broadcast ID. # # @option options [String] :type # The layout type. Set this to "bestFit", "pip", "verticalPresentation", # "horizontalPresentation", "focus", or "custom". # # @option options [String] :stylesheet # The stylesheet for a custom layout. Set this parameter # if you set type to "custom". Otherwise, leave it undefined. # # @option options [String] :screenshare_type # The screenshare layout type. Set this to "bestFit", "pip", "verticalPresentation" or # "horizonalPresentation". If this is defined, then the type parameter # must be set to "bestFit". # # @raise [OpenTokBroadcastError] # The broadcast layout could not be updated. # # @raise [OpenTokAuthenticationError] # Authentication failed. Invalid API key or secret. # # @raise [OpenTokError] # OpenTok server error. # # @raise [ArgumentError] # The broadcast_id or options parameter is empty. # # @raise [ArgumentError] # The "custom" type was specified without a stylesheet option. # # @raise [ArgumentError] # A stylesheet was passed in for a type other than custom. Or an invalid type was passed in. # # @raise [ArgumentError] # An invalid layout type was passed in. def layout(broadcast_id, options = {}) raise ArgumentError, "option parameter is empty" if options.empty? raise ArgumentError, "broadcast_id not provided" if broadcast_id.to_s.empty? type = options[:type] raise ArgumentError, "custom type must have a stylesheet" if (type.eql? "custom") && (!options.key? :stylesheet) valid_non_custom_layouts = ["bestFit","horizontalPresentation","pip", "verticalPresentation", ""] valid_non_custom_type = valid_non_custom_layouts.include? type raise ArgumentError, "type is not valid" if !valid_non_custom_type && !(type.eql? "custom") raise ArgumentError, "stylesheet not needed" if valid_non_custom_type and options.key? :stylesheet raise ArgumentError, "screenshare_type is not valid" if options[:screenshare_type] && !valid_non_custom_layouts.include?(options[:screenshare_type]) raise ArgumentError, "type must be set to 'bestFit' if screenshare_type is defined" if options[:screenshare_type] && type != 'bestFit' response = @client.layout_broadcast(broadcast_id, options) (200..300).include? response.code end # Adds a stream to currently running broadcast that was started with the # streamMode set to "manual". For a description of the feature, see # {https://tokbox.com/developer/rest/#selecting-broadcast-streams}. # # @param [String] broadcast_id # The broadcast ID. # # @param [String] stream_id # The ID for the stream to be added to the broadcast # # @option opts [true, false] :has_audio # (Boolean, optional) — Whether the broadcast should include the stream's # audio (true, the default) or not (false). # # @option opts [true, false] :has_video # (Boolean, optional) — Whether the broadcast should include the stream's # video (true, the default) or not (false). # # You can call the method repeatedly with add_stream set to the same stream ID, to # toggle the stream's audio or video in the broadcast. If you set both has_audio and # has_video to false, you will get error response. # # @raise [ArgumentError] # The broadcast_id parameter is empty. # # @raise [ArgumentError] # The stream_id parameter is empty. # # @raise [ArgumentError] # The has_audio and has_video properties of the options parameter are both set to "false" # def add_stream(broadcast_id, stream_id, options) raise ArgumentError, "broadcast_id not provided" if broadcast_id.to_s.empty? raise ArgumentError, "stream_id not provided" if stream_id.to_s.empty? if options.has_key?(:has_audio) && options.has_key?(:has_video) has_audio = options[:has_audio] has_video = options[:has_video] raise ArgumentError, "has_audio and has_video can't both be false" if audio_and_video_options_both_false?(has_audio, has_video) end options['add_stream'] = stream_id @client.select_streams_for_broadcast(broadcast_id, options) end # Removes a stream from a currently running broadcast that was started with the # streamMode set to "manual". For a description of the feature, see # {https://tokbox.com/developer/rest/#selecting-broadcast-streams}. # # @param [String] broadcast_id # The broadcast ID. # # @param [String] stream_id # The ID for the stream to be removed from the broadcast # # @raise [ArgumentError] # The broadcast_id parameter id is empty. # # @raise [ArgumentError] # The stream_id parameter is empty. # def remove_stream(broadcast_id, stream_id) raise ArgumentError, "broadcast_id not provided" if broadcast_id.to_s.empty? raise ArgumentError, "stream_id not provided" if stream_id.to_s.empty? options = {} options['remove_stream'] = stream_id @client.select_streams_for_broadcast(broadcast_id, options) end private def audio_and_video_options_both_false?(has_audio, has_video) has_audio == false && has_video == false end def hls_dvr_and_low_latency_options_both_true?(dvr, low_latency) dvr == true && low_latency == true end end end