הטמעת הזרמת וידאו HLS ו-DASH

האם שירות הווידאו שלך סובל מפיגורים, ומשתמשים ניידים תקועים על חוצץ אינסופי? אנחנו מיישמים סטרימינג אדפטיבי של HLS/DASH שמבטיח השמעה חלקה בכל מכשיר. הצוות שלנו מטפל בכל המחזור—מטרנסקודינג ועד אינטגרציית נגן—ומספק פתרון turnkey אמין עם תמיכה מתמשכת.

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

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

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

השירותים שאנו מציעים
מציג 1 מתוך 1כל 2062 השירותים
הטמעת הזרמת וידאו HLS ו-DASH
מורכב
~1-2 שבועות

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

שאלות נפוצות

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

  • פיתוח אתר חברה B2B ADVANCE
    פיתוח אתר חברה B2B ADVANCE
    1504
  • פיתוח אפליקציית ווב עבור FEEDME
    פיתוח אפליקציית ווב עבור FEEDME
    1344
  • פיתוח אתר עבור BELFINGROUP
    פיתוח אתר עבור BELFINGROUP
    1052
  • פיתוח חנות מקוונת לחברת FURNORO
    פיתוח חנות מקוונת לחברת FURNORO
    1307
  • פיתוח אפליקציית ווב עבור Enviok
    פיתוח אפליקציית ווב עבור Enviok
    1050
  • פיתוח אתר לחברת FIXPER
    פיתוח אתר לחברת FIXPER
    1033

הטמעת סטרימינג וידאו HLS ו-DASH

אתם משיקים שירות וידאו, ואחרי העלאת הסרטונים הראשונים אתם מגלים — הנגן מגמגם, הטעינה לא נגמרת, ומשתמשי מובייל רואים רק ספינר. זה מצב מוכר — נתקלנו בו פעמים רבות. הניסיון שלנו: 7+ שנים בסטרימינג וידאו. הפתרון הוא סטרימינג אדפטיבי עם פילוח נכון, אינטגרציית CDN ותמיכה במספר רזולוציות. במאמר זה נסביר כיצד אנו מיישמים צינור HLS/DASH במפתח-סוהר, ואת המלכודות שנתקלנו בהן.

HLS לעומת DASH: באיזה לבחור?

HLS (HTTP Live Streaming) הוא תקן של אפל, נתמך על ידי כל הדפדפנים דרך hls.js (למעט Safari, שיש לו תמיכה מובנית). DASH (Dynamic Adaptive Streaming over HTTP) הוא תקן פתוח המשמש לתאימות DRM. בפועל, HLS הוא הבחירה המועדפת: חביון נמוך יותר, תמיכת CDN רחבה יותר. DASH נדרש כאשר נדרשים מספר מערכות DRM (Widevine, PlayReady).

בפרויקטים שלנו, אנו משתמשים ב-HLS כבסיס ומוסיפים DASH לפי דרישה. השוואה בטבלה:

מאפיין HLS DASH
תמיכה כל הדפדפנים (hls.js עבור שאינם Safari) כל הדפדפנים (dash.js)
מיכל MPEG-TS או fMP4 fMP4
DRM FairPlay (אפל), Widevine Widevine, PlayReady
חביון 6–30 שניות (LL-HLS: <2 שניות) 2–10 שניות
תאימות CDN מצוינת מצוינת

בבדיקות שלנו, HLS הראה חביון נמוך פי 2 מ-DASH.

למה FFmpeg הוא לא האפשרות היחידה?

לקידוד וידאו, אנו משתמשים ב-FFmpeg יחד עם תורים (Laravel Horizon), אך לנפחים גבוהים אנו משלבים AWS MediaConvert. נפרק את שתי הגישות.

FFmpeg + תור (באירוח עצמי)

הסקריפט transcode-hls.sh חותך את הווידאו לשלוש איכויות: 1080p, 720p, 360p. מקטעים של 6 שניות (fMP4). פלט: master.m3u8 ורשימות השמעה משניות.

הצג סקריפט קידוד מלא ```bash #!/bin/bash # transcode-hls.sh

INPUT="$1" OUTPUT_DIR="$2"

mkdir -p "$OUTPUT_DIR"

ffmpeg -i "$INPUT" -filter_complex "[0:v]split=3[v1][v2][v3]; [v1]scale=1920:1080:force_original_aspect_ratio=decrease[v1080]; [v2]scale=1280:720:force_original_aspect_ratio=decrease[v720]; [v3]scale=640:360:force_original_aspect_ratio=decrease[v360]" -map "[v1080]" -map 0:a -c:v:0 libx264 -crf 22 -preset fast -b:v:0 5000k -maxrate:v:0 5500k -bufsize:v:0 10000k -c:a:0 aac -b:a:0 192k -map "[v720]" -map 0:a -c:v:1 libx264 -crf 23 -preset fast -b:v:1 2500k -maxrate:v:1 2750k -bufsize:v:1 5000k -c:a:1 aac -b:a:1 128k -map "[v360]" -map 0:a -c:v:2 libx264 -crf 24 -preset fast -b:v:2 800k -maxrate:v:2 880k -bufsize:v:2 1600k -c:a:2 aac -b:a:2 96k -f hls -hls_time 6 -hls_playlist_type vod -hls_flags independent_segments -hls_segment_type fmp4 -hls_segment_filename "$OUTPUT_DIR/v%v/seg%06d.m4s" -master_pl_name master.m3u8 -var_stream_map "v:0,a:0,name:1080p v:1,a:1,name:720p v:2,a:2,name:360p" "$OUTPUT_DIR/v%v/index.m3u8"

</details> Результат: 

output/ ├── master.m3u8 ├── 1080p/ │ ├── index.m3u8 │ ├── seg000001.m4s │ └── ... ├── 720p/ │ └── ... └── 360p/ └── ...

#### PHP-джоба для очереди Класс TranscodeToHlsJob — типичная job для Laravel. Таймаут 2 часа, две попытки. Обрабатываем только успешный кейс.
```php
class TranscodeToHlsJob implements ShouldQueue
{
    public int $timeout = 7200;
    public int $tries = 2;

    public function __construct(private Video $video) {}

    public function handle(): void
    {
        $this->video->update(['status' => 'transcoding']);

        $inputUrl = Storage::disk('s3')->temporaryUrl($this->video->original_key, now()->addHours(3));
        $outputDir = sys_get_temp_dir() . '/hls_' . $this->video->id;
        $s3Prefix = "hls/{$this->video->user_id}/{$this->video->id}";

        mkdir($outputDir, 0777, true);

        $process = new \Symfony\Component\Process\Process([
            '/usr/local/bin/transcode-hls.sh',
            $inputUrl,
            $outputDir
        ]);
        $process->setTimeout(7200);
        $process->run();

        if (!$process->isSuccessful()) {
            throw new \RuntimeException('HLS transcoding failed: ' . $process->getErrorOutput());
        }

        // Загрузить все файлы в S3
        $iterator = new \RecursiveIteratorIterator(
            new \RecursiveDirectoryIterator($outputDir)
        );
        foreach ($iterator as $file) {
            if (!$file->isFile()) continue;

            $relativePath = str_replace($outputDir . '/', '', $file->getPathname());
            $s3Key = "{$s3Prefix}/{$relativePath}";
            $contentType = str_ends_with($file->getFilename(), '.m3u8') ? 'application/x-mpegURL' : 'video/mp4';

            Storage::disk('s3')->put($s3Key, file_get_contents($file->getPathname()), [
                'ContentType' => $contentType,
                'CacheControl' => str_ends_with($file->getFilename(), '.m3u8') ? 'no-cache' // плейлист не кэшируем
                    : 'public, max-age=31536000', // сегменты кэшируем навсегда
            ]);
        }

        $this->video->update([
            'status' => 'ready',
            'hls_manifest' => "{$s3Prefix}/master.m3u8",
        ]);

        // Очистить временные файлы
        exec("rm -rf {$outputDir}");
    }
}
```

AWS MediaConvert (מנוהל)

לפרויקטים בעומס גבוה, אנו משתמשים ב-MediaConvert. הגדרה בפייתון:

def create_hls_job(input_key: str, output_prefix: str) -> str:
    client = boto3.client('mediaconvert', endpoint_url=MEDIACONVERT_ENDPOINT)
    job = client.create_job(
        Role=MEDIACONVERT_ROLE,
        Settings={
            'Inputs': [{'FileInput': f's3://bucket/{input_key}', 'VideoSelector': {}, 'AudioSelectors': {'Audio 1': {'DefaultSelection': 'DEFAULT'}}}],
            'OutputGroups': [{
                'Name': 'Apple HLS',
                'OutputGroupSettings': {
                    'Type': 'HLS_GROUP_SETTINGS',
                    'HlsGroupSettings': {
                        'Destination': f's3://bucket/{output_prefix}/',
                        'SegmentLength': 6,
                        'MinSegmentLength': 0,
                        'DirectoryStructure': 'SUBDIRECTORY_PER_STREAM',
                    },
                },
                'Outputs': [
                    {'NameModifier': '_1080p', 'VideoDescription': {'Width': 1920, 'Height': 1080, 'CodecSettings': {'Codec': 'H_264', 'H264Settings': {'Bitrate': 5000000, 'RateControlMode': 'CBR', 'GopSize': 90}}}, 'AudioDescriptions': [{'CodecSettings': {'Codec': 'AAC', 'AacSettings': {'Bitrate': 192000}}}]},
                    {'NameModifier': '_720p', 'VideoDescription': {'Width': 1280, 'Height': 720, 'CodecSettings': {'Codec': 'H_264', 'H264Settings': {'Bitrate': 2500000, 'RateControlMode': 'CBR', 'GopSize': 90}}}, 'AudioDescriptions': [{'CodecSettings': {'Codec': 'AAC', 'AacSettings': {'Bitrate': 128000}}}]},
                    {'NameModifier': '_360p', 'VideoDescription': {'Width': 640, 'Height': 360, 'CodecSettings': {'Codec': 'H_264', 'H264Settings': {'Bitrate': 800000, 'RateControlMode': 'CBR', 'GopSize': 90}}}, 'AudioDescriptions': [{'CodecSettings': {'Codec': 'AAC', 'AacSettings': {'Bitrate': 96000}}}]},
                ],
            }],
        }
    )
    return job['Job']['Id']

MediaConvert מייצר רשימות השמעה HLS בעצמו. אנו משלמים רק עבור דקות קידוד.

CloudFront CDN

כדי להבטיח אספקת וידאו מהירה ברחבי העולם, אנו מגדירים CloudFront עם שתי מדיניות מטמון: עבור רשימות השמעה (TTL 5 שניות) ומקטעים (TTL שנה).

resource "aws_cloudfront_distribution" "video" {
  origin {
    domain_name = aws_s3_bucket.videos.bucket_regional_domain_name
    origin_id = "s3-videos"
    origin_access_control_id = aws_cloudfront_origin_access_control.default.id
  }
  enabled = true
  is_ipv6_enabled = true
  default_cache_behavior {
    allowed_methods = ["GET", "HEAD"]
    cached_methods = ["GET", "HEAD"]
    target_origin_id = "s3-videos"
    viewer_protocol_policy = "redirect-to-https"
    forwarded_values {
      query_string = false
      cookies {
        forward = "none"
      }
      headers = ["Origin", "Access-Control-Request-Method", "Access-Control-Request-Headers"]
    }
    min_ttl = 0
    default_ttl = 86400
    max_ttl = 31536000
  }
  # Плейлисты .m3u8 — короткий кэш
  ordered_cache_behavior {
    path_pattern = "*.m3u8"
    allowed_methods = ["GET", "HEAD"]
    cached_methods = ["GET", "HEAD"]
    target_origin_id = "s3-videos"
    viewer_protocol_policy = "redirect-to-https"
    forwarded_values {
      query_string = false
      cookies {
        forward = "none"
      }
    }
    min_ttl = 0
    default_ttl = 5
    max_ttl = 30
  }
}

React Player עם hls.js

אינטגרציה בצד הלקוח: hls.js לדפדפנים מודרניים, גיבוי ל-HLS מובנה ב-Safari.

import Hls from 'hls.js';
import { useEffect, useRef } from 'react';

interface VideoPlayerProps {
  manifestUrl: string;
  poster?: string;
}

export function VideoPlayer({ manifestUrl, poster }: VideoPlayerProps) {
  const videoRef = useRef<HTMLVideoElement>(null);

  useEffect(() => {
    const video = videoRef.current;
    if (!video) return;

    if (Hls.isSupported()) {
      const hls = new Hls({
        maxBufferLength: 30,
        maxMaxBufferLength: 60,
        lowLatencyMode: false,
      });
      hls.loadSource(manifestUrl);
      hls.attachMedia(video);
      hls.on(Hls.Events.ERROR, (_, data) => {
        if (data.fatal) {
          if (data.type === Hls.ErrorTypes.NETWORK_ERROR) {
            hls.startLoad();
          } else {
            hls.destroy();
          }
        }
      });
      return () => hls.destroy();
    } else if (video.canPlayType('application/vnd.apple.mpegurl')) {
      // Safari: нативная поддержка HLS
      video.src = manifestUrl;
    }
  }, [manifestUrl]);

  return (
    <video
      ref={videoRef}
      poster={poster}
      controls
      playsInline
      style={{ width: '100%', maxHeight: '80vh' }}
    />
  );
}

מה כלול בעבודה

אנו מטפלים במחזור המלא להטמעת סטרימינג HLS/DASH באתר שלכם:

  • קידוד וידאו לשלוש איכויות (1080p, 720p, 360p) באמצעות FFmpeg או AWS MediaConvert.
  • אינטגרציה עם תורים (Laravel Horizon) לעיבוד אסינכרוני.
  • פיתוח נגן React מותאם אישית המבוסס על hls.js עם תמיכה בהתאמת ממשק משתמש.
  • הגדרת CDN (CloudFront או Cloudflare) לאספקת תוכן מהירה.
  • תיעוד מפורט של ארכיטקטורת הצינור.
  • הכשרת הצוות שלכם על המערכת.
  • שבועיים של תמיכה לאחר ההשקה.

טעויות אופייניות

  • קצבי סיביות שגויים: נמוכים מדי עבור 1080p, גבוהים מדי עבור 360p. אנו משתמשים בהמלצות אפל.
  • שמירת רשימות השמעה במטמון: אם master.m3u8 נשמר במטמון, הנגן לא יראה איכויות חדשות אחרי קידוד מחדש. אנו מגדירים no-cache.
  • חוסר CORS: הדפדפן חוסם מקטעים. אנו מגדירים CORS על S3/CloudFront.
  • אי-התאמת הידרציה: אם הנגן מרנדר על השרת, עלולה להיות אי-התאמה עם הלקוח. אנו משתמשים בייבוא דינמי.

תהליך העבודה

  1. ניתוח — לימוד סרטוני המקור, דרישות DRM, קהל היעד.
  2. עיצוב — בחירת מחסנית (FFmpeg באירוח עצמי או MediaConvert), קביעת קצבי סיביות, CDN.
  3. יישום — כתיבת מקודד, עבודה, נגן.
  4. בדיקות — בדיקת LCP, CLS, INP, Core Web Vitals.
  5. פריסה — פריסה לייצור, ניטור לוגים.

לוח זמנים ועלות

שלב לוח זמנים
קידוד HLS עם FFmpeg בתור 4–5 ימים
אינטגרציית AWS MediaConvert 2–3 ימים
נגן hls.js עם React 1–2 ימים
אספקת CloudFront CDN 1–2 ימים
צינור מלא (העלאה → קידוד → נגן) 7–14 ימים

העלות מחושבת באופן אישי לפי נפח הווידאו, קצבי הסיביות וצרכי ה-DRM. צרו קשר להערכת הפרויקט שלכם — נחשב את לוח הזמנים והעלות. הזמינו הטמעת סטרימינג HLS/DASH וקבלו ייעוץ. אנו מבטיחים שהווידאו שלכם יעבוד ללא תקיעות בכל מכשיר.