DLNARendererService.java 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. package com.fongmi.android.tv.service;
  2. import android.app.Notification;
  3. import android.content.ComponentName;
  4. import android.content.Context;
  5. import android.content.Intent;
  6. import android.content.ServiceConnection;
  7. import android.os.Build;
  8. import android.os.IBinder;
  9. import androidx.core.app.NotificationCompat;
  10. import androidx.media3.common.Player;
  11. import com.fongmi.android.tv.App;
  12. import com.fongmi.android.tv.R;
  13. import com.fongmi.android.tv.dlna.CastAction;
  14. import com.fongmi.android.tv.dlna.DLNAAvTransportImpl;
  15. import com.fongmi.android.tv.dlna.DLNARenderingControlImpl;
  16. import com.fongmi.android.tv.dlna.DLNAServiceConfiguration;
  17. import com.fongmi.android.tv.dlna.RenderState;
  18. import com.fongmi.android.tv.player.PlayerManager;
  19. import com.fongmi.android.tv.utils.Notify;
  20. import com.fongmi.android.tv.utils.Util;
  21. import org.jupnp.UpnpServiceConfiguration;
  22. import org.jupnp.android.AndroidUpnpServiceImpl;
  23. import org.jupnp.binding.annotations.AnnotationLocalServiceBinder;
  24. import org.jupnp.model.DefaultServiceManager;
  25. import org.jupnp.model.meta.DeviceDetails;
  26. import org.jupnp.model.meta.DeviceIdentity;
  27. import org.jupnp.model.meta.LocalDevice;
  28. import org.jupnp.model.meta.LocalService;
  29. import org.jupnp.model.meta.ManufacturerDetails;
  30. import org.jupnp.model.meta.ModelDetails;
  31. import org.jupnp.model.types.UDADeviceType;
  32. import org.jupnp.model.types.UDN;
  33. import org.jupnp.support.avtransport.lastchange.AVTransportLastChangeParser;
  34. import org.jupnp.support.connectionmanager.ConnectionManagerService;
  35. import org.jupnp.support.lastchange.LastChangeAwareServiceManager;
  36. import org.jupnp.support.renderingcontrol.lastchange.RenderingControlLastChangeParser;
  37. import java.nio.charset.StandardCharsets;
  38. import java.util.UUID;
  39. public class DLNARendererService extends AndroidUpnpServiceImpl implements ServiceConnection {
  40. private final IBinder binder = new LocalBinder();
  41. private volatile PlayerManager player;
  42. private volatile boolean isDlnaActive;
  43. private DLNARenderingControlImpl renderingControlImpl;
  44. private DLNAAvTransportImpl avTransportImpl;
  45. private PlaybackService playbackService;
  46. private Player currentListenerPlayer;
  47. private boolean bound;
  48. public static void start(Context context) {
  49. context.startService(new Intent(context, DLNARendererService.class));
  50. }
  51. public static void stop(Context context) {
  52. context.stopService(new Intent(context, DLNARendererService.class));
  53. }
  54. @Override
  55. protected UpnpServiceConfiguration createConfiguration() {
  56. return new DLNAServiceConfiguration();
  57. }
  58. @Override
  59. public void onCreate() {
  60. super.onCreate();
  61. Notification notification = new NotificationCompat.Builder(this, Notify.DEFAULT).setSmallIcon(R.drawable.ic_notification).setContentTitle(getString(R.string.app_name)).setSilent(true).build();
  62. startForeground(Notify.ID + 1, notification);
  63. upnpService.startup();
  64. registerLocalDevice();
  65. }
  66. private void registerLocalDevice() {
  67. LocalService<DLNAAvTransportImpl> avTransport = createAvTransport();
  68. LocalService<ConnectionManagerService> connManager = createConnectionManager();
  69. LocalService<DLNARenderingControlImpl> renderControl = createRenderingControl();
  70. DeviceIdentity identity = new DeviceIdentity(new UDN(UUID.nameUUIDFromBytes((Build.MANUFACTURER + Build.MODEL + "-MediaRenderer").getBytes(StandardCharsets.UTF_8))));
  71. UDADeviceType type = new UDADeviceType("MediaRenderer", 1);
  72. DeviceDetails details = new DeviceDetails(Util.getDeviceName(), new ManufacturerDetails(Build.MANUFACTURER), new ModelDetails(Build.MODEL, "DLNA Renderer", "1.0"));
  73. try {
  74. LocalDevice device = new LocalDevice(identity, type, details, new LocalService[]{avTransport, connManager, renderControl});
  75. upnpService.getRegistry().addDevice(device);
  76. } catch (Exception ignored) {
  77. }
  78. }
  79. @SuppressWarnings("unchecked")
  80. private LocalService<DLNAAvTransportImpl> createAvTransport() {
  81. avTransportImpl = new DLNAAvTransportImpl(this);
  82. LocalService<DLNAAvTransportImpl> service = new AnnotationLocalServiceBinder().read(DLNAAvTransportImpl.class);
  83. service.setManager(new LastChangeAwareServiceManager<>(service, new AVTransportLastChangeParser()) {
  84. @Override
  85. protected DLNAAvTransportImpl createServiceInstance() {
  86. return avTransportImpl;
  87. }
  88. });
  89. return service;
  90. }
  91. @SuppressWarnings("unchecked")
  92. private LocalService<ConnectionManagerService> createConnectionManager() {
  93. LocalService<ConnectionManagerService> service = new AnnotationLocalServiceBinder().read(ConnectionManagerService.class);
  94. service.setManager(new DefaultServiceManager<>(service, ConnectionManagerService.class));
  95. return service;
  96. }
  97. @SuppressWarnings("unchecked")
  98. private LocalService<DLNARenderingControlImpl> createRenderingControl() {
  99. renderingControlImpl = new DLNARenderingControlImpl(this);
  100. LocalService<DLNARenderingControlImpl> service = new AnnotationLocalServiceBinder().read(DLNARenderingControlImpl.class);
  101. service.setManager(new LastChangeAwareServiceManager<>(service, new RenderingControlLastChangeParser()) {
  102. @Override
  103. protected DLNARenderingControlImpl createServiceInstance() {
  104. return renderingControlImpl;
  105. }
  106. });
  107. return service;
  108. }
  109. @Override
  110. public IBinder onBind(Intent intent) {
  111. return binder;
  112. }
  113. @Override
  114. public void onDestroy() {
  115. unbindPlaybackService();
  116. super.onDestroy();
  117. }
  118. private void bindPlaybackService() {
  119. if (bound) return;
  120. bound = true;
  121. bindService(new Intent(this, PlaybackService.class).setAction(PlaybackService.LOCAL_BIND_ACTION), this, BIND_AUTO_CREATE);
  122. }
  123. private void cleanupPlaybackRefs() {
  124. App.removeCallbacks(positionUpdater);
  125. if (currentListenerPlayer != null) {
  126. currentListenerPlayer.removeListener(listener);
  127. currentListenerPlayer = null;
  128. }
  129. if (playbackService != null) {
  130. playbackService.removePlayerCallback(playerCallback);
  131. playbackService = null;
  132. }
  133. player = null;
  134. if (avTransportImpl != null) avTransportImpl.setPlayerManager(null);
  135. }
  136. private void unbindPlaybackService() {
  137. if (!bound) return;
  138. bound = false;
  139. cleanupPlaybackRefs();
  140. unbindService(this);
  141. }
  142. public void setDlnaActive(boolean active) {
  143. isDlnaActive = active;
  144. if (avTransportImpl != null) avTransportImpl.setDlnaActive(active);
  145. if (active) bindPlaybackService();
  146. else {
  147. if (avTransportImpl != null) avTransportImpl.reset();
  148. unbindPlaybackService();
  149. }
  150. }
  151. public long consumePendingSeekMs() {
  152. return avTransportImpl != null ? avTransportImpl.consumePendingSeekMs() : -1;
  153. }
  154. public CastAction consumeNext() {
  155. return avTransportImpl != null ? avTransportImpl.popNext() : null;
  156. }
  157. public void notifyError() {
  158. if (avTransportImpl != null) avTransportImpl.fireStateChange(RenderState.STOPPED);
  159. }
  160. @Override
  161. public void onServiceConnected(ComponentName name, IBinder binder) {
  162. if (!isDlnaActive) {
  163. unbindPlaybackService();
  164. return;
  165. }
  166. playbackService = ((PlaybackService.LocalBinder) binder).getService();
  167. playbackService.addPlayerCallback(playerCallback);
  168. player = playbackService.player();
  169. avTransportImpl.setPlayerManager(player);
  170. currentListenerPlayer = player.getPlayer();
  171. currentListenerPlayer.addListener(listener);
  172. App.post(positionUpdater, 1000);
  173. }
  174. @Override
  175. public void onServiceDisconnected(ComponentName name) {
  176. bound = false;
  177. cleanupPlaybackRefs();
  178. }
  179. private void notifyState() {
  180. if (avTransportImpl == null || player == null || !isDlnaActive) return;
  181. int state = player.getPlaybackState();
  182. if (state == Player.STATE_IDLE) return;
  183. long duration = player.getDuration();
  184. avTransportImpl.updatePositionCache(player.getPosition(), duration > 0 ? duration : -1);
  185. RenderState renderState = switch (state) {
  186. case Player.STATE_BUFFERING -> RenderState.PREPARING;
  187. case Player.STATE_READY -> player.isPlaying() ? RenderState.PLAYING : RenderState.PAUSED;
  188. case Player.STATE_ENDED -> avTransportImpl.hasNext() ? RenderState.PREPARING : RenderState.STOPPED;
  189. default -> null;
  190. };
  191. if (renderState != null) avTransportImpl.fireStateChange(renderState);
  192. }
  193. private final Runnable positionUpdater = new Runnable() {
  194. @Override
  195. public void run() {
  196. if (player != null && avTransportImpl != null && player.isPlaying()) avTransportImpl.updatePositionCache(player.getPosition(), player.getDuration());
  197. if (player != null) App.post(this, 1000);
  198. }
  199. };
  200. private final Player.Listener listener = new Player.Listener() {
  201. @Override
  202. public void onPlaybackStateChanged(int playbackState) {
  203. notifyState();
  204. }
  205. @Override
  206. public void onIsPlayingChanged(boolean isPlaying) {
  207. notifyState();
  208. }
  209. };
  210. private final PlaybackService.PlayerCallback playerCallback = new PlaybackService.PlayerCallback() {
  211. @Override
  212. public void onPlayerRebuild(Player newPlayer) {
  213. if (currentListenerPlayer != null) currentListenerPlayer.removeListener(listener);
  214. currentListenerPlayer = newPlayer;
  215. newPlayer.addListener(listener);
  216. }
  217. };
  218. public class LocalBinder extends Binder {
  219. public DLNARendererService getService() {
  220. return DLNARendererService.this;
  221. }
  222. }
  223. }