kasunk99-livestream-core 0.3.33 → 0.3.34

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"useHostSocket.d.ts","sourceRoot":"","sources":["../../src/hooks/useHostSocket.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAYH,OAAO,EAKL,KAAK,SAAS,EACf,MAAM,mBAAmB,CAAC;AAoB3B,MAAM,MAAM,oBAAoB,GAAG;IACjC,yEAAyE;IACzE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG,SAAS,GAAG;IAC5C,YAAY,EAAE,eAAe,EAAE,CAAC;IAChC,cAAc,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,eAAe,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,YAAY,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAClC,WAAW,EAAE,MAAM,IAAI,CAAC;IACxB,SAAS,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/B,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9B,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,gBAAgB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,eAAe,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACtC,CAAC;AAMF,wBAAgB,aAAa,CAAC,OAAO,GAAE,oBAAyB,GAAG,mBAAmB,CAgrBrF"}
1
+ {"version":3,"file":"useHostSocket.d.ts","sourceRoot":"","sources":["../../src/hooks/useHostSocket.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAYH,OAAO,EAKL,KAAK,SAAS,EACf,MAAM,mBAAmB,CAAC;AAoB3B,MAAM,MAAM,oBAAoB,GAAG;IACjC,yEAAyE;IACzE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG,SAAS,GAAG;IAC5C,YAAY,EAAE,eAAe,EAAE,CAAC;IAChC,cAAc,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,eAAe,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,YAAY,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAClC,WAAW,EAAE,MAAM,IAAI,CAAC;IACxB,SAAS,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/B,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9B,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,gBAAgB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,eAAe,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACtC,CAAC;AAMF,wBAAgB,aAAa,CAAC,OAAO,GAAE,oBAAyB,GAAG,mBAAmB,CAirBrF"}
@@ -310,20 +310,19 @@ export function useHostSocket(options = {}) {
310
310
  patchHostState({ isMicMuted: !newEnabled });
311
311
  }, []);
312
312
  // ── Screen share ───────────────────────────────────────────────────────────
313
- //
314
- // replaceTrack() is unreliable when switching between different capture types
315
- // (camera → screen). Close the existing video producer and publish a new one
316
- // so the server emits new-producer to viewers, who then re-consume the correct
317
- // feed automatically.
318
313
  const replaceVideoProducer = useCallback(async (newTrack) => {
319
314
  const transport = hostSession.sendTransport;
320
315
  if (!transport || transport.closed)
321
316
  return; // preview-only, no transport yet
322
- try {
323
- hostSession.videoProducer?.close();
317
+ // If a producer already exists, swap its track in-place.
318
+ // replaceTrack() keeps the same server-side producer ID so viewers
319
+ // automatically receive the new content without needing to re-consume.
320
+ const existingProducer = hostSession.videoProducer;
321
+ if (existingProducer && !existingProducer.closed) {
322
+ await existingProducer.replaceTrack({ track: newTrack });
323
+ return;
324
324
  }
325
- catch { /* ignore */ }
326
- hostSession.videoProducer = null;
325
+ // No active producer publish a fresh one (first publish or post-cleanup).
327
326
  const producer = await transport.produce({
328
327
  track: newTrack,
329
328
  encodings: [{ maxBitrate: 2500000 }],
@@ -420,28 +419,27 @@ export function useHostSocket(options = {}) {
420
419
  hostSession.screenStream = screenStream;
421
420
  const screenAudioTrack = screenStream.getAudioTracks()[0] ?? null;
422
421
  if (Platform.OS === 'android') {
423
- // Android: stop camera hardware — MediaProjection captures the hardware
424
- // display independently, so stopping the camera is safe and saves battery.
422
+ patchHostState({ streamURL: getStreamURL(screenStream), captureMode: 'screen' });
423
+ }
424
+ else {
425
+ // iOS: keep camera hardware running — RPScreenRecorder captures the app's
426
+ // own screen, so the camera preview must stay alive to provide content.
427
+ patchHostState({ captureMode: 'screen', streamURL: getStreamURL(hostSession.localStream) });
428
+ }
429
+ // Replace the video producer track BEFORE stopping camera hardware.
430
+ // replaceTrack() must locate the existing RTP sender — stopping the camera
431
+ // track first can invalidate the sender reference on some Android devices,
432
+ // causing the replacement to silently fail and viewers to see a black screen.
433
+ await replaceVideoProducer(screenVideoTrack);
434
+ // Android only: safe to stop the camera now that replaceTrack() has
435
+ // redirected the sender to the screen capture track.
436
+ if (Platform.OS === 'android') {
425
437
  hostSession.localStream?.getVideoTracks()
426
438
  ?.forEach((t) => { try {
427
439
  t.stop?.();
428
440
  }
429
441
  catch { /* ignore */ } });
430
- // Point the RTCView preview at the screen capture stream.
431
- patchHostState({ streamURL: getStreamURL(screenStream), captureMode: 'screen' });
432
442
  }
433
- else {
434
- // iOS: keep camera hardware running.
435
- // RPScreenRecorder captures the app's own screen; if we stopped the camera
436
- // first, the app would display black and that black frame would be captured
437
- // → host and viewers both see a black screen. Keeping the camera alive
438
- // means the host's RTCView still shows the camera preview while the
439
- // WebRTC producer (replaced below) delivers screen content to viewers.
440
- patchHostState({ captureMode: 'screen' });
441
- // Update the preview URL so the RTCView keeps showing the local camera stream.
442
- patchHostState({ streamURL: getStreamURL(hostSession.localStream) });
443
- }
444
- await replaceVideoProducer(screenVideoTrack);
445
443
  if (screenAudioTrack && hostSession.sendTransport && !hostSession.sendTransport.closed) {
446
444
  try {
447
445
  const screenAudioProducer = await hostSession.sendTransport.produce({ track: screenAudioTrack });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kasunk99-livestream-core",
3
- "version": "0.3.33",
3
+ "version": "0.3.34",
4
4
  "description": "Reusable livestream viewer/host module for React Native (Expo) — mediasoup + Socket.IO",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",