Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add releaseStream support and fix FCPublish call order #1391

Closed

Conversation

levs42
Copy link
Contributor

@levs42 levs42 commented Mar 18, 2024

Description & motivation

This PR allows the library users to be able to call code before createStream. For example, to call releaseStream which is supported by Wowza. New state is required because .initialized is the default state and when the connection is successful, changing the ready state here doesn't lead to the delegate notification call. In addition, createStream is wrapped into a lock queue, so library users have time to perform extra calls on the same queue before the create stream execution. It also adds an additional send method to allow connection.call method call from the outside.

Type of change

Please delete options that are not relevant.

  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)

Copy link
Owner

@shogo4405 shogo4405 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Upon reconsideration of FCPublish/FCUnpublish, it seems more natural for them to exist in RTMPConnection rather than RTMPStream. I believe it would be more appropriate to implement them properly on the RTMPConnection side rather than modifying RTMPStream.

Even if it were to be merged, I have no plans to accept it, as the relevant code itself will be removed in the next version.

@@ -389,6 +389,11 @@ open class RTMPStream: IOStream {
}
}

/// Sends a command to the RTMP client or server.
public func send(commandName: String, arguments: Any?...) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[IMO]
RTMPConneciton.call is already public, so I don't feel the need for implementation on the HaishinKit side. We expect it to be defined in the importing side's extension or RTMPStream extension.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good. connection is private, but it's injected into the RTMPStream so it can be used from the outside.

@@ -57,6 +57,8 @@ open class IOStream: NSObject {
}

/// NetStream has been created.
case uninitialized
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the increase of "uninitialized," the number of states in readyState has increased, making it difficult to foresee and maintain. I also think "closed = uninitialized" is synonymous.

Copy link
Contributor Author

@levs42 levs42 Mar 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Considering that FCPublish/FCUnpublish will move into RTMPConnection, what could be a way to call releaseStream before createStream? I pushed 21a7def with an example so the key can be set from the outside. Initial idea is to subscribe to RTMPConnection.Code.connectSuccess but there is no guarantee about the event listeners dispatch order, so createStream might be executed before releaseStream.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you modify createStream as follows, it ensures that the call to createStream will occur after the releaseStream command is received.

 func createStream(_ stream: RTMPStream) {
        let releaseStreamResponder = RTMPResponder(result: { data -> Void in
                let responder = RTMPResponder(result: { data -> Void in
            guard let id = data[0] as? Double else {
                return
            }
            stream.id = UInt32(id)
            stream.readyState = .open
           })
           call("createStream", responder: responder)
        })
        call(“releaseStream”, responder: releaseStreamResponder, arguments: stream.key)
    }

In modern times, it cannot be simply assumed whether various server-side implementations are correctly implemented or not. I think it would be good to call it like this.

func createStream(_ stream: RTMPStream) {
    // Specify an empty responder to ignore the result of releaseStream.
    let releaseStreamResponder = RTMPResponder(result: { data -> Void in })
    call(“releaseStream”, responder: releaseStreamResponder, arguments: stream.key)
    call(“createStream”, responder: …)
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've updated the PR with optional releaseStream call without a responder.

@levs42 levs42 force-pushed the feature/rtmp-uninitialized-state branch from 21a7def to d59b55b Compare March 20, 2024 18:06
@levs42
Copy link
Contributor Author

levs42 commented Mar 20, 2024

In addition to optional releaseStream call, I moved FCPublish to be called before createStream. I can reference Adobe software and other encoders that encode in this order so it's defacto standard for this undocumented command. In addition, Twitter doesn't respond with onFCPublish when FCPublish is after createStream:

Screenshot

Screenshot 2024-03-20 at 3 03 12 PM

Now it's a breaking change because users have to pass name to connect() method in order to FCPublish to work.

@levs42 levs42 changed the title Add extra state to RTMPStream Add releaseStream support and fix FCPublish call order Mar 20, 2024
@@ -282,11 +284,12 @@ public class RTMPConnection: EventDispatcher {
}

/// Creates a two-way connection to an application on RTMP Server.
public func connect(_ command: String, arguments: Any?...) {
public func connect(_ command: String, name: String? = nil, arguments: Any?...) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[MUST]
I don't want to make such disruptive changes for the purpose of supporting releaseStream. Also, considering viewing or multiple streams, I still think this change is unnecessary.

If I was to do it, I think it would be better to prepare an RTMPConnection subclass or wrapper class, such as RTMPConnectionFMLECompatible, that conforms to the FMLE sequence.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should I make RTMPConnection and createStream() open to allow subclassing? My goal is to have any kind of solution for this case being supported. It's common and solves platform specific issues while not causing others because all other popular encoders perform it in the mentioned sequence. FMLE app performs it in the following order:

AMF command 'releaseStream'
AMF command 'FCPublish'
AMF command 'createStream'

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. HaishinKit aims to provide multi-streaming and ingest/playback functionalities. Killing off RTMPConnection functionality or making destructive changes to methods in pursuit of FMLE compatibility would diverge from this objective.

Therefore, I believe it would be appropriate to have a class dedicated to single-stream ingest, inheriting from RTMPConnection, and within that class, call the FMLE-compatible methods.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems natural for the stream name of releaseStream to be in RTMPStream.
To avoid issues with multi-streaming and playback without inheriting RTMPConnection, this implementation seems necessary.
https://github.com/shogo4405/HaishinKit.swift/pull/1393/files

It might be good to handle errors when there is a contradiction between the publish and fcPublishName, but I don't think it's necessary to be that strict.

@shogo4405
Copy link
Owner

Close as #1393

@shogo4405 shogo4405 closed this Mar 24, 2024
@levs42 levs42 deleted the feature/rtmp-uninitialized-state branch May 7, 2024 21:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants