3333import org .fourthline .cling .model .meta .Icon ;
3434import org .fourthline .cling .model .meta .RemoteDevice ;
3535import org .fourthline .cling .model .meta .Service ;
36+ import org .fourthline .cling .model .types .UDAServiceType ;
3637import de .yaacc .upnp .callback .avtransport .GetPositionInfo ;
3738import de .yaacc .upnp .callback .avtransport .GetTransportInfo ;
3839import de .yaacc .upnp .callback .avtransport .Pause ;
3940import de .yaacc .upnp .callback .avtransport .Play ;
4041import de .yaacc .upnp .callback .avtransport .Seek ;
4142import de .yaacc .upnp .callback .avtransport .SetAVTransportURI ;
4243import de .yaacc .upnp .callback .avtransport .Stop ;
44+ import de .yaacc .upnp .callback .connectionmanager .GetProtocolInfo ;
4345import org .fourthline .cling .support .contentdirectory .DIDLParser ;
4446import org .fourthline .cling .support .model .DIDLContent ;
4547import org .fourthline .cling .support .model .DIDLObject ;
4648import org .fourthline .cling .support .model .PositionInfo ;
49+ import org .fourthline .cling .support .model .ProtocolInfo ;
50+ import org .fourthline .cling .support .model .ProtocolInfos ;
4751import org .fourthline .cling .support .model .Res ;
4852import org .fourthline .cling .support .model .TransportInfo ;
4953import org .fourthline .cling .support .model .TransportState ;
5054import org .fourthline .cling .support .model .item .Item ;
5155
56+ import java .util .concurrent .CountDownLatch ;
57+ import java .util .concurrent .TimeUnit ;
58+
5259import java .net .URI ;
5360import java .net .URL ;
5461import java .text .SimpleDateFormat ;
@@ -182,11 +189,15 @@ protected Object loadItem(PlayableItem playableItem) {
182189 protected void startItem (PlayableItem playableItem , Object loadedItem ) {
183190 if (playableItem == null || getDevice () == null )
184191 return ;
185- YaaccLogger .d (getClass ().getName (), "Uri: " + playableItem .getUri ());
186- YaaccLogger .d (getClass ().getName (), "Duration: " + playableItem .getDuration ());
192+
193+ // Try to select best resource for this device
194+ PlayableItem deviceOptimizedItem = selectBestResourceForDevice (playableItem );
195+
196+ YaaccLogger .d (getClass ().getName (), "Uri: " + deviceOptimizedItem .getUri ());
197+ YaaccLogger .d (getClass ().getName (), "Duration: " + deviceOptimizedItem .getDuration ());
187198 YaaccLogger .d (getClass ().getName (),
188- "MimeType: " + playableItem .getMimeType ());
189- YaaccLogger .d (getClass ().getName (), "Title: " + playableItem .getTitle ());
199+ "MimeType: " + deviceOptimizedItem .getMimeType ());
200+ YaaccLogger .d (getClass ().getName (), "Title: " + deviceOptimizedItem .getTitle ());
190201 Service <?, ?> service = getUpnpClient ().getAVTransportService (getDevice ());
191202 if (service == null ) {
192203 YaaccLogger .d (getClass ().getName (),
@@ -196,7 +207,103 @@ protected void startItem(PlayableItem playableItem, Object loadedItem) {
196207 }
197208
198209 // Check transport state first and handle accordingly
199- checkTransportStateForStart (playableItem , service );
210+ checkTransportStateForStart (deviceOptimizedItem , service );
211+ }
212+
213+ /**
214+ * Select the best resource for this specific device based on supported protocols
215+ */
216+ private PlayableItem selectBestResourceForDevice (PlayableItem playableItem ) {
217+ Item item = playableItem .getItem ();
218+ if (item == null || item .getResources ().isEmpty ()) {
219+ return playableItem ;
220+ }
221+
222+ // Get device's supported protocols
223+ Service <?, ?> cmService = getDevice ().findService (new UDAServiceType ("ConnectionManager" ));
224+ if (cmService == null ) {
225+ YaaccLogger .d (getClass ().getName (), "No ConnectionManager service, using default resource" );
226+ return playableItem ;
227+ }
228+
229+ // Query supported protocols synchronously
230+ final ProtocolInfos [] supportedProtocols = new ProtocolInfos [1 ];
231+ final CountDownLatch latch = new CountDownLatch (1 );
232+
233+ executorService .execute (
234+ new GetProtocolInfo (cmService , getHttpRequestSender ()) {
235+ @ Override
236+ public void received (ActionInvocation actionInvocation , ProtocolInfos sinkProtocolInfos , ProtocolInfos sourceProtocolInfos ) {
237+ supportedProtocols [0 ] = sinkProtocolInfos ;
238+ latch .countDown ();
239+ }
240+
241+ @ Override
242+ public void failure (ActionInvocation invocation , UpnpResponse operation , String defaultMsg ) {
243+ YaaccLogger .d (AVTransportPlayer .class .getName (), "GetProtocolInfo failed: " + defaultMsg );
244+ latch .countDown ();
245+ }
246+ }
247+ );
248+
249+ try {
250+ latch .await (2 , TimeUnit .SECONDS );
251+ } catch (InterruptedException e ) {
252+ YaaccLogger .d (getClass ().getName (), "GetProtocolInfo timeout" );
253+ return playableItem ;
254+ }
255+
256+ if (supportedProtocols [0 ] == null || supportedProtocols [0 ].isEmpty ()) {
257+ YaaccLogger .d (getClass ().getName (), "No supported protocols found, using default resource" );
258+ return playableItem ;
259+ }
260+
261+ // Find best matching resource
262+ Res bestMatch = null ;
263+ long bestBitrate = 0 ;
264+
265+ for (Res resource : item .getResources ()) {
266+ if (resource .getProtocolInfo () == null ) continue ;
267+
268+ String contentFormat = resource .getProtocolInfo ().getContentFormat ();
269+ if (contentFormat == null || contentFormat .isEmpty ()) continue ;
270+
271+ // Check if device supports this format
272+ boolean supported = false ;
273+ for (ProtocolInfo deviceProtocol : supportedProtocols [0 ]) {
274+ if (deviceProtocol .getContentFormat ().equals (contentFormat ) ||
275+ deviceProtocol .getContentFormat ().equals ("*" ) ||
276+ deviceProtocol .getContentFormat ().startsWith (contentFormat .split ("/" )[0 ] + "/*" )) {
277+ supported = true ;
278+ break ;
279+ }
280+ }
281+
282+ if (!supported ) {
283+ YaaccLogger .d (getClass ().getName (), "Device doesn't support: " + contentFormat );
284+ continue ;
285+ }
286+
287+ // Among supported formats, prefer higher bitrate
288+ Long bitrate = resource .getBitrate ();
289+ if (bitrate != null && bitrate > bestBitrate ) {
290+ bestBitrate = bitrate ;
291+ bestMatch = resource ;
292+ } else if (bestMatch == null ) {
293+ bestMatch = resource ;
294+ }
295+ }
296+
297+ if (bestMatch != null && !bestMatch .equals (item .getFirstResource ())) {
298+ YaaccLogger .d (getClass ().getName (), "Selected device-optimized resource: " +
299+ bestMatch .getProtocolInfo ().getContentFormat () + " bitrate: " + bestMatch .getBitrate ());
300+ // Create new PlayableItem with selected resource
301+ Item optimizedItem = new Item (item );
302+ optimizedItem .setResources (java .util .Collections .singletonList (bestMatch ));
303+ return new PlayableItem (optimizedItem , (int ) playableItem .getDuration ());
304+ }
305+
306+ return playableItem ;
200307 }
201308
202309 private void checkTransportStateForStart (PlayableItem playableItem , Service <?, ?> service ) {
0 commit comments