Expand my Community achievements bar.

Learn about Edge Delivery Services in upcoming GEM session
SOLVED

A couple of observations about WebcamPublisher & WebcamSubscriber

Avatar

Former Community Member

I've recently begun trying to use the WebcamPublisher and WebcamSubscriber components and have run into a few difficulties with them. They seem to be a little difficult to use in a non-default way (ie. the example code posted in the ASdocs online). For instance, it seems that if I declare both the publisher and subscriber within the same MXML file, they play nicely together. But if I declare the publisher at the root level and pass it as a reference to a custom MXML component containing the subscriber, I start running into problems.

(First a tiny bit of backstory, and to address any potential "why would you do that?" type questions...) The UI I've been tasked to build has 2 webcam video components. One in the top left for the remote user and one in the lower right for the local user. My initial thoughts were that I could use a single WebcamPublisher and 2 WebcamSubscribers. I could control which video feeds showed up where by using the WebcamSubscriber's "publisherIDs" filter Array. The UI also needed a bit of chrome around each webcam view (volume controls, etc.) so I decided to wrap the WebcamSubscribers in a custom MXML component that controlled the user-filtering, volume, etc. That's when I started running into troubles.

The first thing that happened was that the WebcamSubscriber runtime errored with a null pointer. Apparently its initialization method calls subscribe() without checking to see if it has been assigned a _connectSession var yet. Seems like my bindings weren't being triggered yet at this point and there's no way to tell the WebcamSubscriber to not auto-subscribe so this approach was out.

Next, I decided to move WebcamSubscriber out of a var declared in MXML and into a private var that would not be added to the display list (and initialized) until I was sure the values for its IConnectSession and WebcamPublisher had been set. This prevents the RTE in initialization. In the interest of making my custom component a bit more robust though I thought it would be good to add a check in to make sure that the WebcamPublisher passed in as a param was actually publishing. The idea was pretty simple...

if ( !webcamPublisher.isPublishing ) {

  webcamPublisher.publish( connectSession.userManager.myUserID );

}

That led to RTE number 2 though. Seems like the getters in some of these classes don't properly check for null conditions. For instance, SessionContainerProxy's "isSynchronized" getter:

public function get isSynchronized():Boolean {

  return _connectSession.isSynchronized;

}

Would be nice if this were re-written to be a little more robust:

public function get isSynchronized():Boolean {

  return _connectSession && _connectSession.isSynchronized;

}

External code has no way to query to see if the IConnectSession is null other than to try to access a getter and catch an error. Anyway, I eventually gave up on that and decided that I'd just have to call publish on the root component and things seem to be working now.

In general, I think the docs [http://livedocs.adobe.com/labs/acrobatcom/index.html] could be a little more verbose about the limitations or expectations of some of these components (for instance, conditions that will cause runtime errors to be thrown). But I know doc writing can be tedious and not always seem like a good return for your investment, so I won't soap-box about that too much.

PS. Thanks Nigel for prodding me to stop whining at my Twitter and start communicating on the forums here. Sorry you had to read my grumbling tweet.

1 Accepted Solution

Avatar

Correct answer by
Former Community Member

Really, I'm not allowed to say f-r-i-g-g-i-n' ? That's friggin stupid.

View solution in original post

10 Replies

Avatar

Former Community Member

Actually, I may have spoken a bit too soon. The above comment was made while working in a small sandbox test project. When migrating that code over to my actual problem, I have run into yet another issue. Because of some legacy-code (which is probably not worth going into here) the application I'm working in has my RTC components split into the following locations:

MainApplication.mxml

  • ConnectSessionContainer which wraps/contains a ChildView.mxml object (and passes it a reference to the ConnectSessionContainer)

ChildView.mxml

  • Defines WebcamPublisher, AudioPublisher, & AudioSubscriber
  • Contains a WebcamWithAudio.mxml child (and passes it a reference to the publishers & the IConnectSession)

WebcamWithAudio.mxml

  • Defines WebcamSubscriber (and chrome)
  • Delays creation of WebcamSubscriber until the publishers & IConnectSession has been provided (to avoid the null-pointer error in the init method)

My previous working solution relied on the fact that I could subscribe() my audio and webcam publishers as part of my event handler for the IConnectSession's SessionEvent.SYNCHRONIZATION_CHANGE event. Seems like I should be able to do the same type of thing in my ChildView component so long as I pass it a reference to the IConnectSession. Of course I have to also handle the case of the IConnectSession's already having been synchronized. So my code looks something like...

(ChildView.mxml)

public function set connectSession( value:IConnectSession ):void {

._connectSession = value;

.if ( connectSession ) {

..if ( connectSession.isSynchronized ) {

...webcamPublisher.publish();

...audioPublisher.publish();

..} else {

...connectSession.addEventListener(

....SessionEvent.SYNCHRONIZATION_CHANGE,

....function():void {

.....connectSession.removeEventListener( SessionEvent.SYNCHRONIZATION_CHANGE, arguments.callee );

.....webcamPublisher.publish();

.....audioPublisher.publish();

....} );

..}

.}

}

However, what I seem to be seeing now is that no runtime errors (at least no visible ones) but nothing displays in my WebcamSubscriber either.

I would be happy to provide full source for this project if it would be useful to look at.

Avatar

Former Community Member

Hi there,

One of the primary rules that I think would solve a lot of your problems is

that all RTC components in your app need to either be passed an explicit

reference to the session on initialization (myWebCam.connectSession =

mySession), or you simply need to create an IConnectSession before creating

the other components, and everything will self-register (no need to pass

refs around).

I do agree with you that this subject should be expanded upon in the docs.

It's mentioned here and there in the videotutorials and docs, but it would

likely make things easier.. We haven't had many users contact us with this

problem, so that explains the relative scarcity of docs.

thanks for taking the time to bring this to our attention - every bit

helps.

nigel

Avatar

Former Community Member

Nigel,

Thaks for your response. However, I have already been passing the IConnectSession reference to each of the lower level components. I did not paste that portion of the source code because I was trying to be terse. The forum here doesn't really format source code in a very readable way. As I said, I'd be happy to share the full source if anyone would like to take a look. Would you be interested in seeing it, Nigel?

Also, by declaring all of my child components within a ConnectSessionContainer, the above step shouldn't even be necessary since that ConnectSessionContainer's internal ConnectSession will be registered as the PrimarySession. (Unless I'm overlooking something.)

Avatar

Former Community Member

Hmm, well that's kind of what I was asking in the first place.

I've already identified a few conditions under which there can be null-pointer runtime errors. And since such errors occuring within a binding expression are automatically caught and disposed of silently, it's kind of hard to tell if something like that is happening.

I created another iteration of my project which removed all of my binding expressions and replaced them with manual assigned values for things like the IConnectSession but I'm still not seeing video feed. No errors, but no video feed either.

Have you tried this, by chance? The examples all show the WebcamPublisher and WebcamSubscriber living as siblings underneath of a ConnectSessionContainer but I have been assuming that they are intended to still function if they are not declared side by side. Is this assumption incorrect?

My application code, including whitespace, is less than 150 lines so it seems unlikely that I'm making too many silly errors here. I suppose it's still possible.

Avatar

Former Community Member

Could you post some code showing the simplest possible reproduction of the

issue? I've definitely seen apps work spread across multiple

windows/components/etc, so it's likely something pretty fixable.

nigel

Avatar

Former Community Member

Sure thing! Rather than post it here (where it will lose its formatting) I uploaded it to:

http://boynamedbri.com/temp/WebcamSampleCode.html

Thanks for taking a look, Nigel.

Avatar

Former Community Member

Hi Brian,

So, I took a few minutes and stripped out about half of your code =), and everything's working. I've attached the MXML files here.

Observations on what was wrong :

a) The AudioPublisher/Subscriber and WebCamPublisher were in a declarations tag. Even though they're not visible, those are all UIComponents and don't belong in there (similar to ConnectSessionContainer, or a Transitions object). I hate that friggin' declarations tag, because it's so inconsistent as to what belongs in there (like, why the hell don't bindings or states need to go in there? What's the point of it again?), and just "non-visual" as they suggest isn't really the case.

b) Way too much passing around of ConnectSession. In general, this stuff wires itself up, and unless you're planning on having multiple sessions happening at the same time, and you follow the rule of creating your session before the other stuff, you should be able to just not worry about it. In particular, your set connectSession in ChildView was actually broken (I think) because it was setting the connectSession too early (before the components that needed it would exist) - you'd want to make set connectSession something that caches the session and calls invalidateProperties(), handling the actual wiring in commitProperties(), after child instantiation. And that anonymous function in there made me sad, so now we're even .

c) I had no idea what those bindings in WebcamWithChrome were doing, and they frightened me, so I took them out, and just bound the webcampublisher directly onto the subscriber. Happily, this eliminated the initializationBinding method, and allowed me to put the webcamsubscriber back in the MXML tags.

hope that helps,

  nigel

Avatar

Correct answer by
Former Community Member

Really, I'm not allowed to say f-r-i-g-g-i-n' ? That's friggin stupid.

Avatar

Former Community Member

Oh, awesome! Thanks Nigel!

To be honest, I added a lot of the explicit passing around of things like the IConnectSession as part of trying to fix things that could have been possibly broken. (It wasn't that micro-manage-y in my initial couple of iterations.) I'm guessing maybe the damn declarations tag was what got me.

The bindings in the 3rd component were there to prevent the null-pointer RTE that I was getting inside of WebcamSubscriber when I simply declared it inside of the MXML. But that no longer seems to happen now that the Declarations tag isn't involved.

For anyone who may stumble upon this and would like to see the solution:

http://boynamedbri.com/temp/WebcamSampleCodeFixed.html

Thanks again, Nigel.

PS. I can say "damn" why can't you say friggin'

The following has evaluated to null or missing: ==> liqladmin("SELECT id, value FROM metrics WHERE id = 'net_accepted_solutions' and user.id = '${acceptedAnswer.author.id}'").data.items [in template "analytics-container" at line 83, column 41] ---- Tip: It's the step after the last dot that caused this error, not those before it. ---- Tip: If the failing expression is known to be legally refer to something that's sometimes null or missing, either specify a default value like myOptionalVar!myDefault, or use <#if myOptionalVar??>when-present<#else>when-missing. (These only cover the last step of the expression; to cover the whole expression, use parenthesis: (myOptionalVar.foo)!myDefault, (myOptionalVar.foo)?? ---- ---- FTL stack trace ("~" means nesting-related): - Failed at: #assign answerAuthorNetSolutions = li... [in template "analytics-container" at line 83, column 5] ----