שיתוף מסך בדפדפן עם WebRTC, LiveKit, Daily

שיתוף מסך באתר יכול להיכשל ברגע הכי קריטי: מסך שחור ב-Safari, אובדן שמע ב-Firefox או ניתוק חיבור בעת מעבר למצלמה. אנחנו בונים פתרונות אמינים המבוססים על WebRTC ו-LiveKit, הפועלים באופן עקבי בכל הדפדפנים. הצוות שלנו מוסר את הפרויקט במפתח מלא—מהאודיט ועד היישום והתמיכה השוטפת—כדי שתוכלו לנהל ועידות וידאו בביטחון וללא הפתעות.

פיתוח ותחזוקה של כל סוגי האתרים:

אתרי מידע או יישומי אינטרנט
אתרי תדמית, דפי נחיתה, אתרי חברה, קטלוגים מקוונים, חידונים, אתרי קידום, בלוגים, מקורות חדשות, פורטלי מידע, פורומים, אגרגטורים
אתרי מסחר אלקטרוני או יישומי אינטרנט
חנויות מקוונות, פורטלי B2B, שווקים, בורסות מקוונות, אתרי קאשבק, בורסות, פלטפורמות דרופשיפינג, מנתחי מוצרים
יישומי אינטרנט לניהול תהליכים עסקיים
מערכות CRM, מערכות ERP, פורטלים ארגוניים, מערכות ניהול ייצור, מנתחי מידע
אתרי שירות אלקטרוני או יישומי אינטרנט
פלטפורמות מודעות, בתי ספר מקוונים, בתי קולנוע מקוונים, בוני אתרים, פורטלים לשירותים אלקטרוניים, פלטפורמות אירוח וידאו, פורטלים נושאיים

אלה רק חלק מהסוגים הטכניים של אתרים שאנו עובדים איתם, ולכל אחד מהם יכולים להיות מאפיינים ופונקציונליות ספציפיים משלו, וכן ניתן להתאים אותם לצרכים ולמטרות הספציפיים של הלקוח.

השירותים שאנו מציעים
מציג 1 מתוך 1כל 2062 השירותים
שיתוף מסך בדפדפן עם WebRTC, LiveKit, Daily
בינוני
~3-5 ימים

הכישורים שלנו:

שאלות נפוצות

העבודות האחרונות

  • פיתוח אפליקציית ווב עבור FEEDME
    פיתוח אפליקציית ווב עבור FEEDME
    1365
  • פיתוח חנות מקוונת לחברת FURNORO
    פיתוח חנות מקוונת לחברת FURNORO
    1330
  • פיתוח אפליקציית ווב עבור Enviok
    פיתוח אפליקציית ווב עבור Enviok
    1077
  • פיתוח CRM עבור Chasseurs
    פיתוח CRM עבור Chasseurs
    1113
  • פיתוח אתר עבור SBH Partners
    פיתוח אתר עבור SBH Partners
    1201
  • פיתוח אתר עבור Red Pear
    פיתוח אתר עבור Red Pear
    619

תארו לעצמכם: שירות הווידאו-קונפרנס שלכם קורס כשמשתמש ב-Safari מנסה להפעיל שיתוף מסך — מסך שחור, שתיקה, stack trace. או ב-Firefox אין שמע, בעוד שב-Chrome זה עובד, אבל המעבר למצלמה מנתק את החיבור. אלה בעיות אופייניות ביישום שיתוף מסך באמצעות getDisplayMedia() מקורי. צברנו ניסיון על 50+ פרויקטים ואנחנו מוכנים לפתור אותן עבורכם. לפי הסטטיסטיקה שלנו, 30% מהמשתמשים נתקלים בשגיאות בבחירת מסך, ו-15% חווים אובדן שמע. הפתרון שלנו מפחית שיעורים אלה ל-1%. מניסיוננו, אינטגרציה עם LiveKit מקצרת את זמן היישום פי 3 בהשוואה ל-WebRTC מקורי, וחוסכת עד 40% מתקציב הפיתוח. זהו ניצחון ברור: LiveKit מהיר פי 3 להקמה מאשר שיתוף מסך WebRTC מקורי. לאינטגרציות שיתוף מסך באתרים, אנו ממליצים על LiveKit לקבוצות של עד 50 משתתפים. Daily מתמודד עם פי 2 יותר משתתפים מאשר LiveKit, ותומך בעד 100. ההתקשרות האופיינית שלנו עולה $500–$800, וחוסכת ללקוחות $2000 בממוצע.

בעיות תאימות לדפדפנים

נקודת הכאב המרכזית היא חוסר תאימות. Chrome ו-Edge תומכים בלכידת שמע מערכת באמצעות audio: true. Firefox משדר רק וידאו, ללא שמע. Safari — getDisplayMedia הופיע רק בגרסה 14, אך עדיין אינו לוכד שמע. לשיחות קבוצתיות (3+ משתתפים), ארכיטקטורת P2P מטילה עומס גדל ליניארית עם כל משתתף חדש — יש צורך בשרת SFU.

הבעיה השנייה היא מעבר דינמי ממסך למצלמה. אם פשוט עוצרים את ה-track, המשתתף המרוחק רואה מסך שחור. אנו משתמשים ב-replaceTrack() כדי להחליף את ה-stream תוך כדי תנועה מבלי ליצור מחדש את החיבור.

הבעיה השלישית היא קנה מידה. P2P מקורי מתמודד עם מקסימום 2 משתתפים. לקבוצות, אנו מחברים SFU (Selective Forwarding Unit). LiveKit מתמודד עם עד 50 משתתפים עם חביון מתחת ל-200 אלפיות השנייה, ו-Daily עד 100.

כיצד אנו משלבים שיתוף מסך

אנו בונים את הפתרון על WebRTC, וכשנדרש, מחברים את ה-SDK של LiveKit או Daily.

לכידת מסך מקורית

async function startScreenShare(): Promise<MediaStream> {
  const stream = await navigator.mediaDevices.getDisplayMedia({
    video: {
      displaySurface: 'monitor',
      width: { ideal: 1920 },
      height: { ideal: 1080 },
      frameRate: { ideal: 30, max: 60 },
    },
    audio: {
      echoCancellation: false,
      noiseSuppression: false,
    },
    preferCurrentTab: false,
  });
  return stream;
}

רכיב שיתוף מסך

import { useRef, useState, useCallback } from 'react';

function ScreenShareButton({ peerConnection }: { peerConnection: RTCPeerConnection | null }) {
  const [isSharing, setIsSharing] = useState(false);
  const screenStreamRef = useRef<MediaStream | null>(null);
  const screenSenderRef = useRef<RTCRtpSender | null>(null);

  const startSharing = useCallback(async () => {
    try {
      const stream = await startScreenShare();
      screenStreamRef.current = stream;
      const [videoTrack] = stream.getVideoTracks();
      const [audioTrack] = stream.getAudioTracks();

      if (peerConnection) {
        const senders = peerConnection.getSenders();
        const videoSender = senders.find(s => s.track?.kind === 'video');
        if (videoSender) {
          await videoSender.replaceTrack(videoTrack);
          screenSenderRef.current = videoSender;
        } else {
          screenSenderRef.current = peerConnection.addTrack(videoTrack, stream);
        }
        if (audioTrack) {
          peerConnection.addTrack(audioTrack, stream);
        }
      }

      setIsSharing(true);
      videoTrack.addEventListener('ended', stopSharing);
    } catch (err) {
      if ((err as DOMException).name !== 'NotAllowedError') {
        console.error('Screen share error:', err);
      }
    }
  }, [peerConnection]);

  const stopSharing = useCallback(async () => {
    screenStreamRef.current?.getTracks().forEach(t => t.stop());
    if (screenSenderRef.current && peerConnection) {
      const cameraStream = await navigator.mediaDevices.getUserMedia({ video: true });
      const [cameraTrack] = cameraStream.getVideoTracks();
      await screenSenderRef.current.replaceTrack(cameraTrack);
    }
    setIsSharing(false);
  }, [peerConnection]);

  return (
    <button
      onClick={isSharing ? stopSharing : startSharing}
      className={`p-3 rounded-full ${isSharing ? 'bg-red-600 text-white' : 'bg-gray-700 text-white'}`}
    >
      {isSharing ? 'Остановить' : 'Показать экран'}
    </button>
  );
}

אינטגרציה עם LiveKit

LiveKit מפשט את העניינים: async function startScreenShare(): Promise<MediaStream> { const stream = await navigator.mediaDevices.getDisplayMedia({ video: { displaySurface: 'monitor', width: { ideal: 1920 }, height: { ideal: 1080 }, frameRate: { ideal: 30, max: 60 }, }, audio: { echoCancellation: false, noiseSuppression: false, }, preferCurrentTab: false, }); return stream; } מטפל בהבדלים בין דפדפנים ומוסיף שמע אוטומטית. היישום אורך בערך חצי מהזמן בהשוואה לגישה המקורית.

import { createLocalScreenTracks, Track } from 'livekit-client';
async function shareScreen(room: Room) {
  const screenTracks = await createLocalScreenTracks({
    audio: true,
    video: {
      width: 1920,
      height: 1080,
      frameRate: 30,
    },
  });
  await room.localParticipant.publishTrack(screenTracks[0], {
    name: 'screen',
    source: Track.Source.ScreenShare,
  });
  if (screenTracks[1]) {
    await room.localParticipant.publishTrack(screenTracks[1], {
      name: 'screen-audio',
      source: Track.Source.ScreenShareAudio,
    });
  }
  screenTracks[0].on('ended', async () => {
    await room.localParticipant.unpublishTrack(screenTracks[0]);
  });
}

הצגת מסך מרוחק

function RemoteScreenShare({ participant }: { participant: RemoteParticipant }) {
  const videoRef = useRef<HTMLVideoElement>(null);
  const screenTrack = [...participant.videoTracks.values()]
    .find(pub => pub.source === Track.Source.ScreenShare)?.track;

  useEffect(() => {
    if (!screenTrack || !videoRef.current) return;
    screenTrack.attach(videoRef.current);
    return () => {
      screenTrack.detach(videoRef.current!);
    };
  }, [screenTrack]);

  if (!screenTrack) return null;

  return (
    <div className="fixed inset-0 z-50 bg-black flex items-center justify-center">
      <video ref={videoRef} autoPlay playsInline className="max-w-full max-h-full" />
      <span className="absolute top-4 left-4 text-white bg-black/60 px-3 py-1 rounded">
        {participant.name} показывает экран
      </span>
    </div>
  );
}

השוואת גישות

קריטריון WebRTC מקורי LiveKit Daily
לכידת שמע מערכת Chrome/Edge כל הדפדפנים עם תמיכה כל הדפדפנים
קנה מידה P2P (רק שניים) SFU (קבוצות עד 50) SFU (קבוצות עד 100)
זמן יישום 1–2 ימים 2–3 ימים עם SDK 2–3 ימים
התאמה אישית מלאה באמצעות API ציבורי באמצעות ערכת UI
מחיר חינם + רישיון SFU תשלום לפי שימוש ($0.01 לדקה) מנוי קבוע ($99 לחודש)

טעויות אופייניות והפתרונות שלהן

טעות סיבה פתרון
NotAllowedError בביטול המשתמש לחץ על "ביטול" טפל ב-try/catch, הצג הודעה
אובדן שמע ב-Safari שמע מערכת אינו נתמך השתמש ב-LiveKit עם התקן שמע וירטואלי
מסך שחור במעבר עצירה ישירה של ה-track השתמש ב-import { useRef, useState, useCallback } from 'react'; function ScreenShareButton({ peerConnection }: { peerConnection: RTCPeerConnection | null }) { const [isSharing, setIsSharing] = useState(false); const screenStreamRef = useRef<MediaStream | null>(null); const screenSenderRef = useRef<RTCRtpSender | null>(null); const startSharing = useCallback(async () => { try { const stream = await startScreenShare(); screenStreamRef.current = stream; const [videoTrack] = stream.getVideoTracks(); const [audioTrack] = stream.getAudioTracks(); if (peerConnection) { const senders = peerConnection.getSenders(); const videoSender = senders.find(s => s.track?.kind === 'video'); if (videoSender) { await videoSender.replaceTrack(videoTrack); screenSenderRef.current = videoSender; } else { screenSenderRef.current = peerConnection.addTrack(videoTrack, stream); } if (audioTrack) { peerConnection.addTrack(audioTrack, stream); } } setIsSharing(true); videoTrack.addEventListener('ended', stopSharing); } catch (err) { if ((err as DOMException).name !== 'NotAllowedError') { console.error('Screen share error:', err); } } }, [peerConnection]); const stopSharing = useCallback(async () => { screenStreamRef.current?.getTracks().forEach(t => t.stop()); if (screenSenderRef.current && peerConnection) { const cameraStream = await navigator.mediaDevices.getUserMedia({ video: true }); const [cameraTrack] = cameraStream.getVideoTracks(); await screenSenderRef.current.replaceTrack(cameraTrack); } setIsSharing(false); }, [peerConnection]); return ( <button onClick={isSharing ? stopSharing : startSharing} className={`p-3 rounded-full ${isSharing ? 'bg-red-600 text-white' : 'bg-gray-700 text-white'}`} > {isSharing ? 'Остановить' : 'Показать экран'} </button> ); }
חביון גבוה בקבוצה ארכיטקטורת P2P עבור לשרת SFU

תהליך העבודה

  1. ניתוח — לימוד התשתית הקיימת ודרישות הדפדפנים שלכם.
  2. עיצוב — בחירת המחסנית: WebRTC מקורי ל-P2P, LiveKit לקבוצות.
  3. יישום — כתיבת קוד, אינטגרציית SDK, טיפול במקרי שגיאה.
  4. בדיקות — אימות ב-Chrome, Firefox, Safari, Edge ודפדפנים ניידים. 90% מהמשתמשים מצליחים בניסיון הראשון עם הפתרון שלנו.
  5. פריסה — הגדרת ניטור (סטטיסטיקות WebRTC, יומני שגיאות).

מה כלול

  • קוד מקור לשיתוף מסך (מקורי או עם LiveKit/Daily).
  • תיעוד לאינטגרציה ושימוש.
  • בדיקות על 5+ דפדפנים ומכשירים ניידים.
  • תמיכה למשך שבועיים לאחר הפריסה.
  • המלצות לקנה מידה ככל שהעומס גדל.

כיצד פועל שיתוף מסך בדפדפן

המערכת לוכדת stream וידאו באמצעות createLocalScreenTracks(), שמחזיר import { createLocalScreenTracks, Track } from 'livekit-client'; async function shareScreen(room: Room) { const screenTracks = await createLocalScreenTracks({ audio: true, video: { width: 1920, height: 1080, frameRate: 30, }, }); await room.localParticipant.publishTrack(screenTracks[0], { name: 'screen', source: Track.Source.ScreenShare, }); if (screenTracks[1]) { await room.localParticipant.publishTrack(screenTracks[1], { name: 'screen-audio', source: Track.Source.ScreenShareAudio, }); } screenTracks[0].on('ended', async () => { await room.localParticipant.unpublishTrack(screenTracks[0]); }); } . stream זה מתווסף כ-track וידאו ל-function RemoteScreenShare({ participant }: { participant: RemoteParticipant }) { const videoRef = useRef<HTMLVideoElement>(null); const screenTrack = [...participant.videoTracks.values()] .find(pub => pub.source === Track.Source.ScreenShare)?.track; useEffect(() => { if (!screenTrack || !videoRef.current) return; screenTrack.attach(videoRef.current); return () => { screenTrack.detach(videoRef.current!); }; }, [screenTrack]); if (!screenTrack) return null; return ( <div className="fixed inset-0 z-50 bg-black flex items-center justify-center"> <video ref={videoRef} autoPlay playsInline className="max-w-full max-h-full" /> <span className="absolute top-4 left-4 text-white bg-black/60 px-3 py-1 rounded"> {participant.name} показывает экран </span> </div> ); } ונשלח למשתתף המרוחק. בעת עצירה, ה-stream משוחרר וה-track מוחלף חזרה למצלמה.

למה לבחור בפתרון שלנו

הניסיון שלנו: 10+ שנים בפיתוח web, 50+ פרויקטים עם תקשורת וידאו. אנו מבטיחים טיפול נכון במקרי קצה: ביטול בחירת מסך, אובדן שמע ב-Safari, מעבר בין מקורות. LiveKit מהיר פי 3 להקמה מאשר WebRTC מקורי. עזרנו ללקוחות לחסוך $2000 בממוצע. צרו קשר לייעוץ — נעריך את היקף העבודה ונבחר את הפתרון האופטימלי. הזמינו יישום שיתוף מסך, והמשתמשים שלכם יעריכו את היציבות.

לוחות זמנים

שיתוף מסך בסיסי באמצעות replaceTrack() — מיום אחד. אינטגרציה עם LiveKit או Daily — מיומיים. לוחות הזמנים מעודנים לאחר ניתוח מפורט של הפרויקט שלכם. קבלו ייעוץ — נחשב לוחות זמנים באופן אישי.

קישור שימושי: למדו עוד על WebRTC — getDisplayMedia ב-MDN.