UPNP/DLNA Control Point
android, dlna, java, upnp
Solution
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.
Problem
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