@@ -6,11 +6,8 @@ import (
66 "fmt"
77 "io"
88 "net/url"
9- "os"
109 "path"
11- "path/filepath"
1210 "sort"
13- "strconv"
1411 "strings"
1512 "sync"
1613 "time"
@@ -357,124 +354,18 @@ func ClipInputManifest(requestID, sourceURL, clipTargetUrl string, startTimeUnix
357354 return nil , fmt .Errorf ("error clipping: failed to download original manifest: %w" , err )
358355 }
359356
360- // Generate the absolute path URLS for segmens from the manifest's relative path
361- // TODO: optimize later and only get absolute path URLs for the start/end segments
362- sourceSegmentURLs , err := GetSourceSegmentURLs (sourceURL , origManifest )
363- if err != nil {
364- return nil , fmt .Errorf ("error clipping: failed to get segment urls: %w" , err )
365- }
366-
367357 // Convert start/end time specified in UNIX time (milliseconds) to seconds wrt the first segment
368358 startTime , endTime , err := video .ConvertUnixMillisToSeconds (requestID , origManifest .Segments [0 ], startTimeUnixMillis , endTimeUnixMillis )
369359 if err != nil {
370360 return nil , fmt .Errorf ("error clipping: failed to get start/end time offsets in seconds: %w" , err )
371361 }
372362
373363 // Find the segments at the clipping start/end timestamp boundaries
374- segs , clipsegs , err := video .ClipManifest (requestID , & origManifest , startTime , endTime )
364+ segs , _ , err := video .ClipManifest (requestID , & origManifest , startTime , endTime )
375365 if err != nil {
376366 return nil , fmt .Errorf ("error clipping: failed to get start/end segments: %w" , err )
377367 }
378368
379- // Only the first and last segments should be clipped.
380- // And segs can be a single segment (if start/end times fall within the same segment)
381- // or it can span several segments startng from start-time and spanning to end-time
382- var segsToClip []* m3u8.MediaSegment
383- if len (segs ) == 1 {
384- segsToClip = []* m3u8.MediaSegment {segs [0 ]}
385- } else {
386- segsToClip = []* m3u8.MediaSegment {segs [0 ], segs [len (segs )- 1 ]}
387- }
388- // Create temp local storage dir to hold all clipping related files to upload later
389- clipStorageDir , err := os .MkdirTemp (os .TempDir (), "clip_stage_" )
390- if err != nil {
391- return nil , fmt .Errorf ("error clipping: failed to create temp clipping storage dir: %w" , err )
392- }
393- defer os .RemoveAll (clipStorageDir ) // nolint:errcheck
394-
395- // Download start/end segments and clip
396- for i , v := range segsToClip {
397- // Create temp local file to store the segments:
398- clipSegmentFileName := filepath .Join (clipStorageDir , requestID + "_" + strconv .FormatUint (v .SeqId , 10 )+ ".ts" )
399- defer os .Remove (clipSegmentFileName )
400- clipSegmentFile , err := os .Create (clipSegmentFileName )
401- if err != nil {
402- return nil , err
403- }
404- defer clipSegmentFile .Close ()
405-
406- // Download the segment from OS and write to the temp local file
407- segmentURL := sourceSegmentURLs [v .SeqId ].URL
408- dStorage := NewDStorageDownload ()
409- err = backoff .Retry (func () error {
410- rc , err := GetFile (context .Background (), requestID , segmentURL .String (), dStorage )
411- if err != nil {
412- return fmt .Errorf ("error clipping: failed to download segment %d: %w" , v .SeqId , err )
413- }
414- defer rc .Close ()
415-
416- // Write the segment data to the temp local file
417- _ , err = io .Copy (clipSegmentFile , rc )
418- if err != nil {
419- return fmt .Errorf ("error clipping: failed to write segment %d: %w" , v .SeqId , err )
420- }
421- return nil
422- }, DownloadRetryBackoff ())
423- if err != nil {
424- return nil , fmt .Errorf ("error clipping: failed to download or write segments to local temp storage: %w" , err )
425- }
426-
427- // Locally clip (i.e re-encode + clip) those relevant segments at the specified start/end timestamps
428- clippedSegmentFileName := filepath .Join (clipStorageDir , requestID + "_" + strconv .FormatUint (v .SeqId , 10 )+ "_clip.ts" )
429- if len (segs ) == 1 {
430- // If start/end times fall within same segment, then clip just that single segment
431- duration := endTime - startTime
432- err = video .ClipSegment (requestID , clipSegmentFileName , clippedSegmentFileName , clipsegs [0 ].ClipOffsetSecs , clipsegs [0 ].ClipOffsetSecs + duration )
433- if err != nil {
434- return nil , fmt .Errorf ("error clipping: failed to clip segment %d: %w" , v .SeqId , err )
435- }
436- } else {
437- // If start/end times fall within different segments, then clip segment from start-time to end of segment
438- // or clip from beginning of segment to end-time.
439- if i == 0 {
440- err = video .ClipSegment (requestID , clipSegmentFileName , clippedSegmentFileName , clipsegs [0 ].ClipOffsetSecs , - 1 )
441- } else {
442- err = video .ClipSegment (requestID , clipSegmentFileName , clippedSegmentFileName , - 1 , clipsegs [1 ].ClipOffsetSecs )
443- }
444- if err != nil {
445- return nil , fmt .Errorf ("error clipping: failed to clip segment %d: %w" , v .SeqId , err )
446- }
447- }
448-
449- // Upload clipped segment to OS
450- clippedSegmentFile , err := os .Open (clippedSegmentFileName )
451- if err != nil {
452- return nil , fmt .Errorf ("error clipping: failed to open clipped segment %d: %w" , v .SeqId , err )
453- }
454- defer clippedSegmentFile .Close () // nolint:errcheck
455-
456- clippedSegmentOSFilename := "clip_" + strconv .FormatUint (v .SeqId , 10 ) + ".ts"
457- err = UploadToOSURL (clipTargetUrl , clippedSegmentOSFilename , clippedSegmentFile , MaxCopyFileDuration )
458- if err != nil {
459- return nil , fmt .Errorf ("error clipping: failed to upload clipped segment %d: %w" , v .SeqId , err )
460- }
461-
462- // Get duration of clipped segment(s) to use in the clipped manifest
463- p := video.Probe {}
464- clipSegProbe , err := p .ProbeFile (requestID , clippedSegmentFileName )
465- if err != nil {
466- return nil , fmt .Errorf ("error clipping: failed to probe file: %w" , err )
467- }
468- vidTrack , err := clipSegProbe .GetTrack (video .TrackTypeVideo )
469- if err != nil {
470- return nil , fmt .Errorf ("error clipping: unknown duration of clipped segment: %w" , err )
471- }
472- // Overwrite segs with new uri/duration. Note that these are pointers
473- // so the start/end segments in original segs slice are directly modified
474- v .Duration = vidTrack .DurationSec
475- v .URI = clippedSegmentOSFilename
476- }
477-
478369 // Generate the new clipped manifest
479370 clippedPlaylist , err := CreateClippedPlaylist (origManifest , segs )
480371 if err != nil {
0 commit comments