UPNP/DLNA Control Point - java

I'm working on an android UPnP/DLNA app. I have a control point working where I can stream files from media server to renderer. I can pause/play and stop the file during playback but I cannot seem to figure out how to integrate a seekbar into the control point to show the progress of the playing file and be able to interact with the seekbar. I am using the Cling Java library to create the app. If anyone has any examples that could help me I would really appreciate it.
Thanks
I've tried to implement the SubscriptionCallback example and subscribe to LastChange
SubscriptionCallback callback = new SubscriptionCallback(service, 600) { // Timeout in seconds
public void established(GENASubscription sub) {
System.out.println("Established: " + sub.getSubscriptionId());
}
#Override
public void failed(GENASubscription sub, UpnpResponse response, Exception ex) {
System.err.println(
createDefaultFailureMessage(response, ex)
);
}
#Override
public void ended(GENASubscription sub, CancelReason reason, UpnpResponse response) {
// Reason should be null, or it didn't end regularly
}
public void eventReceived(GENASubscription sub) {
System.out.println("Event: " + sub.getCurrentSequence().getValue());
try {
lastChange = new LastChange(
new AVTransportLastChangeParser(),
sub.getCurrentValues().get("LastChange").toString()
);
} catch (Exception ex) {
log.warning("Error parsing LastChange event content: " + ex);
return;
}
Map<String, StateVariableValue> values = sub.getCurrentValues();
StateVariableValue status = values.get("Status");
System.out.println("Status is: " + status.toString());
}
public void eventsMissed(GENASubscription sub, int numberOfMissedEvents) {
System.out.println("Missed events: " + numberOfMissedEvents);
}
#Override
protected void failed(GENASubscription arg0,
UpnpResponse arg1, Exception arg2, String arg3) {
}
};
upnpService.getControlPoint().execute(callback);
Then I try to get the duration of the current playing track:
System.out.println("Duration: "+lastChange.getEventedValue(0, AVTransportVariable.CurrentTrackDuration.class).getValue());
but this returns a NullPointerException.
Any ideas???????
************UPDATE***********
I have been trying to implement Seek() but have not had success.
I have my seekbar and listener but it it keeps failing when I drag the seekbar to a new position.
seekbar.setOnSeekBarChangeListener(new OnSeekBarChangeListener()
{
#Override
public void onProgressChanged(SeekBar arg0, int arg1, boolean arg2)
{
Log.i("SEEKTIME", "time:" + arg1);
upnpService.getControlPoint().execute(new Seek(service, SeekMode.REL_TIME, arg0.toString())
{
#Override
public void success(ActionInvocation invocation)
{
//super.success(invocation);
Log.i("SEEKSUCCESS", "success seek");
}
#Override
public void failure(ActionInvocation arg0, UpnpResponse arg1, String arg2)
{
Log.i("SEEKFAIL", "fail seek");
}
});
}
Any suggestions why this would be failing

You must poll the renderer for this kind of information (see AVTransport spec chapter 2.3.1). The spec encourages polling every second, but you can easily jam up a real hardware renderer (for which DLNA is still rather a fashionable pain in the a** than a vital part of the design). Our established practice is to send GetPositionInfo() request every 2-3 seconds and treat the returned RelativeTimePosition value only as an adjustment to locally running timer. For the seekbar sizing you also need the total length of current media. Ideally the renderer will tell you automatically when you subscribe to AVTransport.LastChange. I don't know Cling specifically but a quick look shows promising example in controlpoint.SubscriptionCallback. Unfortunately with real devices, LastChange often doesn't tell you anything much. Either the values are not there at all or have a constant inert value. So you will need to poll the GetMediaInfo() again and use MediaDuration value.
As for interaction, Seek() is your friend, ideally with parameters of Unit = REL_TIME and Target = your desired time offset. Be aware that a real world renderer may not be supporting this unit (mode) of seeking. Perhaps it supports only TRACK_NR in which case the seekbar is essentialy read-only for you. Again Cling should be able to tell you allowed values of A_ARG_TYPE_SeekMode for the particular renderer.

Related

Is LifecycleRegistry's handleLifecycleEvent giving me the wrong event callback? What changed?

Up until a few weeks ago, my LifeCycleOwnerAwareObserver class was working fine.
It was design with the purpose of self detach on Destroy.
#Override
public void onStateChanged(#NonNull LifecycleOwner source, #NonNull Lifecycle.Event event) {
Log.d(TAG, "onStateChanged: event is: " + event.name());
Lifecycle.State state = source.getLifecycle().getCurrentState();
Log.println(Log.WARN, TAG, "onStateChanged: state is: " + state.name());
if (state.isAtLeast(INITIALIZED) && !state.equals(DESTROYED)) {
observer.get().accept(event);
} else if (state.equals(DESTROYED)) {
observer.get().accept(event);
observer.set(() -> null);
source.getLifecycle().removeObserver(this);
}
}
The idea was to build lifeCycle aware components to handle automatic unregistering.
90% of my project relies on this components...
I have not perceived any change, specially on Adapters, which listen to Fragments, the only a weird behavior I saw, where onViewCreated (an ON_START callback attaches an observer to the fragment's LifeCycleOwnerLiveData) was triggering slightly after the real onViewCreated(), but ONLY when coming back from the backStack... This is not good at all, but with some precautions it can be somewhat ignored.
But then this was the weirdest of them all...
I have a custom view (ViewParticle.class) with its own LifeCycle that implements a LifeCycleRegistry.
This code was working a few weeks ago... since I have not been testing everything constantly, I am not sure at which moment this stopped working here is the code:
private final MyLifeCycleOwner mViewLifecycleOwner = new MyLifeCycleOwner();
#Override
public void viewDestroyed() {
Lifecycle.Event event = Lifecycle.Event.ON_DESTROY;
Log.d(TAG, "viewDestroyed: event is: " + event.toString());
mViewLifecycleOwner.handleLifecycleEvent(event);
}
The receiving end:
#Override
public void viewPrepared() {
lifecycleSupplier = () -> mViewLifecycleOwner;
Lifecycle lCRef = mViewLifecycleOwner.getLifecycle();
//The callback HERE!!
lCRef.addObserver(
new LifeCycleOwnerAwareObserver(
event -> {
Log.d(TAG, "viewPrepared: event is: " + event.name());
if (event.equals(Lifecycle.Event.ON_DESTROY)) lastResponseProvider.run();
}
)
);
lifeCycleProvider.run();
mViewLifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
mViewLifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_START);
}
The Logs are showing this when viewDestroyed() is executed:
D/ViewParticle: viewDestroyed: event is: ON_DESTROY
D/MyLifeCycleOwner: handleLifecycleEvent: event is: ON_DESTROY
D/LifeCycleOwnerAwareObse: onStateChanged: event is: ON_STOP
W/LifeCycleOwnerAwareObse: onStateChanged: state is: DESTROYED
D/ViewParticle: viewPrepared: event is: ON_STOP
As you can see the Event.ON_DESTROY enum is translating into:
a) Lifecycle.State.DESTROYED
b) Lifecycle.Event.ON_STOP
Which is impossible because the getStateAfter() method is as:
static State getStateAfter(Event event) {
switch (event) {
case ON_CREATE:
case ON_STOP:
return CREATED;
case ON_START:
case ON_PAUSE:
return STARTED;
case ON_RESUME:
return RESUMED;
case ON_DESTROY:
return DESTROYED;
case ON_ANY:
break;
}
throw new IllegalArgumentException("Unexpected event value " + event);
}
Which means an Event will never differ from an State, because an State IS the product of an Event, AND what triggers/begins the callback is the Event, NOT the STATE.
This means that if the State is Destroyed, the Event MUST be ON_DESTROYED.
I cannot explain what's happening here..
I won't look too much into the issue, but at first glance the answer seems to be here:
while (!isSynced()) {
mNewEventOccurred = false;
// no need to check eldest for nullability, because isSynced does it for us.
if (mState.compareTo(mObserverMap.eldest().getValue().mState) < 0) {
backwardPass(lifecycleOwner);
}
Entry<LifecycleObserver, ObserverWithState> newest = mObserverMap.newest();
if (!mNewEventOccurred && newest != null
&& mState.compareTo(newest.getValue().mState) > 0) {
forwardPass(lifecycleOwner);
}
}
The backwardPass(lifecycleOwner); and forwardPass(lifecycleOwner); methods, seem to work under the assumption that the mState.compareTo(newest.getValue().mState) > 0 and the mState.compareTo(mObserverMap.eldest().getValue().mState) < 0 conditions will never be greater than 1, so even if the answer is "true because it is 2", the forwardPass() method will only advance a single node in the life cycle, starting from the one of its previous value...
This behavior makes the function of the getStateAfter(Event event) method pointless.
BTW I remember this specifically giving me problems on this line:
mViewLifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
mViewLifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_START);
Of my code above, this means that this in fact gave me problems before, So now the weird thing that needs clarification is why was it working fine in the first place? IDK.
The Answer is this:
#Override
public void viewDestroyed() {
Lifecycle.Event event = Lifecycle.Event.ON_STOP;
Log.d(TAG, "viewDestroyed: event is: " + event.toString());
mViewLifecycleOwner.handleLifecycleEvent(event);
Lifecycle.Event event2 = Lifecycle.Event.ON_DESTROY;
Log.d(TAG, "viewDestroyed: event is: " + event.toString());
mViewLifecycleOwner.handleLifecycleEvent(event2);
}
The requirement is that one needs to traverse the enums in order, up to the point of reaching what's desired.
public enum Event {
ON_CREATE,
ON_START,
ON_RESUME,
ON_PAUSE,
ON_STOP,
ON_DESTROY,
ON_ANY
}
It seems, as of now, that the only skippable enums are:
ON_RESUME and ON_PAUSE

Getting LiveStreamManager error -3 in DJI Mobile SDK when trying to stream to custom RTMP?

I'm trying to implement a app that sends live video from drone to my custom rtmp server. When I use de LiveStreamManager from DJI Mobile SDK it gives me error code -3, and the stream do not start. How can I use this API?
My app registers successfully, I can setup missions, and get telemetry from drone. But when I try to use the LiveStreamManeger it won't work no matter what. Even by implementing exactly the way it is implemented in Sample Code, it does not work. Documentation in DJI API reference seems to be missing a few methods as well.
Here is my implementation
private void setupLiveStream() {
DJISDKManager.getInstance().getLiveStreamManager().registerListener(listener);
initListener();
DJISDKManager.getInstance().getLiveStreamManager().setAudioStreamingEnabled(false);
DJISDKManager.getInstance().getLiveStreamManager().setVideoSource(LiveStreamManager.LiveStreamVideoSource.Primary);
liveURL = "rtmp://mycustomrtmp.com/drone/live_testDJI";
}
private void initListener() {
listener = new LiveStreamManager.OnLiveChangeListener() {
#Override
public void onStatusChanged(int i) {
setResultToToast("status changed : " + i);
}
};
}
private void StartStreaming(){
if (!isLiveStreamManagerOn()) {
return;
}
if (DJISDKManager.getInstance().getLiveStreamManager().isStreaming()) {
setResultToToast("already started the Stream!");
return;
}
new Thread() {
#Override
public void run() {
DJISDKManager.getInstance().getLiveStreamManager().setLiveUrl(liveURL);// + vehicleID);
int result = DJISDKManager.getInstance().getLiveStreamManager().startStream();
DJISDKManager.getInstance().getLiveStreamManager().setStartTime();
setResultToToast("LiveStream Start: " + result +
"\n isVideoStreamSpeedConfigurable:" + DJISDKManager.getInstance().getLiveStreamManager().isVideoStreamSpeedConfigurable() +
"\n isLiveAudioEnabled:" + DJISDKManager.getInstance().getLiveStreamManager().isLiveAudioEnabled());
}
}.start();
}
I always get a return code -3. When I use the sample code I can get it to work. The only diference is then I call the function isVideoStreamSpeedConfigurable(), it returns true on my code, and false on sample code. But I did not see where I can set this thing to false. How should I implement LiveStreamingManager?
Answering my own question...
I've managed to solve the issue. Apparently, to be able to use the LiveStreamManager you must first call the function VideoFeeder.getPrimaryVideoFeed() somewhere in your code or it will give error code -3.
Using the Sample Code there is a class in internal.utils.VideoFeedView that can be used to this purpose
I have first declared a private property VideoFeedView.
Then on my class constructor I call the initUI function.
private VideoFeedView primaryVideoFeed;
private void initUI() {
primaryVideoFeed.registerLiveVideo(VideoFeeder.getInstance().getPrimaryVideoFeed(),true);
startStreaming();
}
I don't know if I just was lucky, but for me, the following code solved my problem. I didn't have any need for anything more, like VideoFeedView. What is the reason for using that?
I running on a mavic 2 pro and streaming 30fps 720p to youtube.
private LiveStreamManager l;
public int live_streaming_start(String live_url){
Log.d("MavicMax", "LiveStream:live_streaming_start:" + live_url);
l = DJISDKManager.getInstance().getLiveStreamManager();
l.registerListener((x)->{Log.d("MavicMax", "LiveStream callback:" + x);});
l.setVideoSource(LiveStreamManager.LiveStreamVideoSource.Primary);
l.setVideoEncodingEnabled(true);
l.setLiveUrl(live_url);
int r = 0;
r = l.startStream();
return r;
}

Java code to handle node failure in jgroups cluster

My Jgroups config file contains the protocol/config
<FD timeout="3000" max_tries="3" />
But how do I use this in the Java code. For example, if there is a cluster and when I detect a failure I want to call an external notifier service via a REST call, like /nodeDown/nodeID
I'm not able to find any java code which does this, all I see is message receive and send, is there a way I can implement this?
Thanks
Adding some more info
I have done the step of writing a RecieverAdpater and override the start, stop, send, recieve method. Please find some code here,
public void receive(Message msg) {
JGroupsDataPacket pckt = (JGroupsDataPacket) msg.getObject();
if ( pckt.getCmd().equals("cacheUpdate") ){
int uid = pckt.getAffectedUid();
cacheUpdateRoutine(uid);
}
if ( pckt.getCmd().equals("ack") ){
System.out.println("got the mesaage!");
}
logger.log(LogLevel.ERROR, "received msg from " + msg.getSrc() + ": " + msg.getObject());
}
public void send(JGroupsDataPacket pckt){
Message msg = new Message(null, null, pckt);
msg.setFlag(Message.Flag.RSVP);
try {
channel.send(msg);
} catch (Exception e) {
e.printStackTrace();
}
}
I want to know where should I add code for example to handle the TimeOutException when I'm sending a message with the RSVP flag enabled. Another requirement is to know, which is the Java callback method which is called when SUSPECT(P) is triggered. I want to catch and handle the machine's going down, timout etc.
Is the viewAccepted() the only place where I can handle this? Is there a sample code around this?
Also is http://www.jgroups.org/manual/html/user-channel.html
the section 3. APIs give all java/programmatic things we can do with JGroups.
Thanks again
I found some documentation here, I think this is the class which I'm supposed to override
public interface MembershipListener {
void viewAccepted(View new_view);
void suspect(Object suspected_mbr);
void block();
void unblock();
}
OK, first off, you have a JChannel. You need to use it to register for view callbacks, like this:
JChannel ch;
ch.setReceiver(this);
'this' extends ReceiverAdapter and overrides viewAccepted():
public void viewAccepted(View view) {
// handle new view
}
To determine the members which left between views v1 and v2:
List<Address> left_mbrs=View.leftMembers(v1,v2);

JavaFX events/listeners/handlers

I have 2 listViews and 2 custom controls on my scene. When i press on an item in the listView, i would like to raise the event, and then be able to handle this anywhere else in the application. Ideally i would like my 2 custom controls to listen for this event, and handle it when it is raised.
Not sure if this is right, but so far i have this
Here is my custom event, i would also like to pass in an argument of which Offer is it, not sure how to do this though?
public class OfferChangedEvent extends Event {
private static final long serialVersionUID = 1L;
public static final EventType<OfferChangedEvent> OFFER_CHANGED = new EventType<OfferChangedEvent>(ANY, "OFFER_CHANGED");
public OfferChangedEvent() {
this(OFFER_CHANGED);
}
public OfferChangedEvent(EventType<? extends Event> arg0) {
super(arg0);
}
public OfferChangedEvent(Object arg0, EventTarget arg1, EventType<? extends Event> arg2) {
super(arg0, arg1, arg2);
}
}
Now on one of my custom controls, i raise the event when a button is clicked, like so
#FXML
public void showOffer(ActionEvent event) {
Button btnView = (Button)event.getSource();
// can i pass in parameter of the offer here??
btnView.fireEvent(new OfferChangedEvent());
}
Now i would like a listview to listen for the event and handle it, i have this but it aint working
// when offer is changed, we dont want to see events
this.eventListPane.addEventHandler(OfferChangedEvent.OFFER_CHANGED, new EventHandler<OfferChangedEvent>() {
#Override
public void handle(OfferChangedEvent event) {
Console.Log("Recived");
eventListPane.setVisible(false);
// How can i get the argument of the offer passed???
}
});
Unfortunately there isn't a straightforward answer to this question. For your case there are about 3 different ways to wire up your application so that different parts can react to changes in other parts:
You could bind to properties and listen for changes.
You could setup listeners and then notify them of changes.
You could use a messaging bus.
Which one you use depends on various factors, but considering what you have done above I would go with number three. You can try this https://github.com/bennidi/mbassador

Android/Java and multiple "view states"

Does anyone recognize this pattern and know of a tidy solution?
I've got a view that can be in certain states. Let's call them Neutral, Success, Error, InProgress. In the view I've got multiple elements (Buttons, TextViews and a ProgressBar) that should either be visible/enabled depending on the state the view is in.
Currently I've got methods that represent the states that do the necessary .setEnabled() and .setVisibility() calls. With 4 states and a couple of elements this becomes messy quite fast.
I also feel that the State Pattern is not necessarily a good solution but is something that personally springs to mind.
I would love to hear what any of you think is a simple and tidy solution.
Sample code:
void setIsRegistering() {
isRegistering = true;
isRegistered = false;
progressBar.setVisibility(View.VISIBLE);
successText.setVisibility(View.GONE);
errorText.setVisibility(View.GONE);
setupFooterButton.setEnabled(false);
setupFooterButton.setText("Adding browser");
}
void setIsRegistered() {
isRegistering = false;
isRegistered = true;
progressBar.setVisibility(View.INVISIBLE);
successText.setVisibility(View.VISIBLE);
errorText.setVisibility(View.GONE);
setupFooterButton.setEnabled(true);
setupFooterButton.setText("Next");
}
void setIsNotRegistered() {
isRegistering = false;
isRegistered = false;
progressBar.setVisibility(View.INVISIBLE);
successText.setVisibility(View.INVISIBLE);
errorText.setVisibility(View.GONE);
setupFooterButton.setEnabled(true);
setupFooterButton.setText("Add browser");
}`
You can use a ViewAnimator for this: (http://developer.android.com/reference/android/widget/ViewAnimator.html)
You can then call viewAnimator.setDisplayedChild() to set the selected item.
setDisplayedChild() takes an integer, so I typically create an enum to hold the states I want:
enum ViewStates {
NEUTRAL, SUCCESS, ERROR
}
setDisplayedChild(ViewStates.Neutral.ordinal());
Or if that's too verbose:
enum ViewStates {
NEUTRAL, SUCCESS, ERROR
public static int neutral = NEUTRAL.ordinal();
public static int success = SUCCESS.ordinal();
public static int error = ERROR.ordinal();
}
setDisplayedChild(neutral);

Categories