Пример #1
0
        public override void Recode(ulong startTime100NanoSec, ulong endTime100NanoSec, ushort videoTrackID)
        {
            var vidTracks     = DestStream.MediaTracks.Where(t => t is GenericVideoTrack);
            int vidTrackCount = (vidTracks == null) ? 0 : vidTracks.Count();

            if (endTime100NanoSec == 0)
            {      // special case when endTime == 0
                // using duration here is ok as it is about the total time of the source
                endTime100NanoSec = SourceStream.DurationIn100NanoSecs;
            }

            if (endTime100NanoSec - startTime100NanoSec < MaxIterateDuration)
            {
                throw new Exception("Desired time interval for output stream too short");
            }

            int outTracks = DestStream.MediaTracks.Count;

            RecodeSet[] trackEnumerators = new RecodeSet[outTracks];
            int         k = 0;
            int         n = 0;

            foreach (IMediaTrack track in SourceStream.MediaTracks)
            {
                if (((track.Codec.CodecType == CodecTypes.Audio) && (audioOrVideoOrBoth != TracksIncluded.Video)) ||
                    ((track.Codec.CodecType == CodecTypes.Video) && ((videoTrackID == 0) || (track.TrackID == videoTrackID)) &&
                     (audioOrVideoOrBoth != TracksIncluded.Audio)))
                {
                    RecodeSet recodeSet = new RecodeSet();
                    recodeSet.sourceTrack = (IMediaTrackSliceEnumerator)track.GetEnumerator();
                    recodeSet.sourceTrack.Reset();
                    recodeSet.pendingChunkSlices = new List <Slice>();

                    IMediaTrack destination = DestStream[recodeSet.sourceTrack.CodecType, 0];

                    if ((track.Codec.CodecType != CodecTypes.Video) || (vidTrackCount == 1))
                    {
                        destination = DestStream[recodeSet.sourceTrack.CodecType, 0];
                    }
                    else if (vidTrackCount > 1)
                    {
                        destination = vidTracks.ElementAt(n);
                        n++;
                    }

                    if (destination == null)
                    {
                        throw new Exception(string.Format("No {0} destination track. Try vo or so option.", recodeSet.sourceTrack.CodecType));
                    }

                    // normally the destination TrackDurationIn100NanoSecs is set to source duration;
                    // here we reset its value back to zero because it may be smaller than source duration
                    // (for example, if the start time is more than zero).
                    destination.TrackDurationIn100NanoSecs = 0UL;
                    recodeSet.destination      = destination;
                    recodeSet.destinationTrack = (IMediaTrackSliceEnumerator)destination.GetEnumerator();
                    recodeSet.destinationTrack.Reset();

                    trackEnumerators[k++] = recodeSet;
                }
            }

            RaiseRecodeProgressUpdate(0.01f, true, null);      // Indicate we have completed a portion of the work.

            // Need to call MoveNext() first for all source track enumerators
            foreach (RecodeSet recodeSet in trackEnumerators)
            {
                while (recodeSet.sourceTrack.MoveNext())
                {
                    if (recodeSet.sourceTrack.Current != null)
                    {
                        break;
                    }
                }
            }

            IVideoTrack videoTrack           = (IVideoTrack)SourceStream[CodecTypes.Video, 0];
            ulong       prevSyncTime         = 0UL;
            bool        validSyncPointsFound = false;

            foreach (ulong syncTime in EnumerateSyncPoints(videoTrack))
            {     // Cycle through all of the sync points in the video...
                Logger.Instance.Info("[GenericRecodeWRC::Recode] [merge] iterating at syncTime [" + syncTime + "].");

                if ((syncTime > endTime100NanoSec) && (prevSyncTime > endTime100NanoSec))
                {
                    break;              // If we are past the requested end time, stop doing work
                }
                // Each source and destinatin track has its own, independent counter (enumerator).
                // The slices are synced with respect to time, and NOT with respect to index.
                // The outer for loop below iterates through each track being recoded;
                // the inner while loop iterates through each slice skipped.
                // .timeStart == time relative to source track at which recoding starts (should be first slice NOT skipped);
                // .indexStart == index of first slice NOT skipped.
                if (startTime100NanoSec > prevSyncTime)
                {          // Skip a portion of slices.
                    for (int i = 0; i < trackEnumerators.Length; i++)
                    {
                        if (trackEnumerators[i].sourceTrack.CurrentTimeStampNew.HasValue == false)
                        {
                            continue;                      // b-frame and we can't use it to compare...
                        }
                        while (trackEnumerators[i].sourceTrack.CurrentTimeStampNew.Value < syncTime)
                        {
                            Slice slice = trackEnumerators[i].sourceTrack.Current;
                            if (slice == null)
                            {
                                break;
                            }
                            if (slice.TimeStampNew.HasValue == false)
                            {
                                continue;                                                            // its a b-frame, thus no time is available
                            }
                            trackEnumerators[i].timeStart  = slice.TimeStampNew.Value;               // at this point its guaranteed to have a value...
                            trackEnumerators[i].indexStart = slice.index;

                            // Find the next valid CurrentTimeStampNew value.
                            bool tmpEnd = false;
                            while (true)
                            {
                                if (!trackEnumerators[i].sourceTrack.MoveNext())
                                {
                                    tmpEnd = true;
                                    break;                              // Ended.
                                }

                                if (trackEnumerators[i].sourceTrack.CurrentTimeStampNew.HasValue == true)
                                {
                                    break;                              // Found it.
                                }
                            }

                            if (tmpEnd == true)
                            {
                                break;
                            }
                        }
                    }

                    prevSyncTime = syncTime;
                    continue;
                }

                // If we never hit this condition there is nothing actually taken in to process and this causes an exception down the road.
                validSyncPointsFound = true;

                // Each source and destinatin track has its own, independent counter (enumerator).
                // The slices are synced with respect to time, and NOT with respect to index.
                // The outer foreach loop below iterates through each track being recoded;
                // the inner while loop iterates through each slice.
                // recodeSet.sourceTrack ==> source track enumerator
                // recodeSet.destinationTrack ==> destination track enumerator
                ulong timeStamp100NanoSec = ulong.MaxValue;

                foreach (RecodeSet recodeSet in trackEnumerators)
                {
                    recodeSet.pendingChunkSlices.Clear();

                    // Start writing the actual data.
                    while (recodeSet.sourceTrack.CurrentTimeStampNew.HasValue == false ||
                           recodeSet.sourceTrack.CurrentTimeStampNew.Value <= syncTime)
                    {
                        Slice slice = recodeSet.sourceTrack.Current;
                        if (slice == null)
                        {
                            break;
                        }

                        //Logger.Instance.Info("[GenericRecodeWRC::Recode] dumping slice [" + slice.TimeStampNew + ", dur " + (int)slice.SliceDuration + "], track type [" + recodeSet.sourceTrack.CodecType + "].");

                        // Prepare the slice; apply position and time compensation, to base it to the start of the extract.
                        slice.index -= recodeSet.indexStart;
                        if (slice.TimeStampNew.HasValue)
                        {                 // TimeStamp == null if we are a bframe, thus we are not here...
                            if (slice.TimeStampNew.Value < recodeSet.timeStart)
                            {
                                throw new Exception("GenericRecodeWRC.Recode: Offset time stamping error");
                            }

                            // adjust time-stamp and index (offset from time start)
                            slice.TimeStampNew -= recodeSet.timeStart;

                            if (timeStamp100NanoSec == ulong.MaxValue || slice.TimeStampNew.Value > timeStamp100NanoSec)
                            {
                                timeStamp100NanoSec = slice.TimeStampNew.Value;                                 // Take the value for the progress report.
                            }
                        }

                        // Put the slices in the pending Chunk buffer for overview and confirmation.
                        recodeSet.pendingChunkSlices.Add(slice);

                        // position to next output slice
                        recodeSet.destinationTrack.MoveNext();

                        // put slice in destination track
                        recodeSet.destinationTrack.SetCurrent(slice);

                        recodeSet.destination.TrackDurationIn100NanoSecs += (ulong)slice.SliceDuration;

                        // move to next input slice, exit if done
                        if (!recodeSet.sourceTrack.MoveNext())
                        {
                            break;
                        }
                    }
                }

                // Report progress.
                if (timeStamp100NanoSec != ulong.MaxValue)
                {
                    float progress = (float)(((double)timeStamp100NanoSec - (double)startTime100NanoSec) / ((double)endTime100NanoSec - (double)startTime100NanoSec));
                    if (progress > 1)
                    {
                        Common.Logger.Instance.Error("[GenericRecodeWRC::Recode] Progress value [" + progress + "] mis-calculated, progress report skipped.");
                    }
                    else
                    {
                        RaiseRecodeProgressUpdate(progress, true, null);
                    }
                }

                prevSyncTime = syncTime;
            }

            if (validSyncPointsFound == false)
            {     // Nothing meaningful found to process, end now.
                // Do not DestStream.FinalizeStream() as this will try to write and cause an exception.
                RaiseRecodeProgressUpdate(1, false, null);
                RaiseRecodeProgressUpdate(2, false, null);
                return;
            }

            RaiseRecodeProgressUpdate(1, true, null);      // All the work is done, but there may be some finalizers left.

            // Assemble all stbl or moof boxes.
            // Write out the mdat slice in the case of MP4 output;
            // in the case of fragmented files (ISMV output), all moof and mdat boxes have already been written out at this point, and we
            // only need to write out the mfra slice, if it is needed.
            DestStream.FinalizeStream();

            RaiseRecodeProgressUpdate(2, true, null);      // Everything is completed.
        }
Пример #2
0
        public override void Recode(ulong startTime100NanoSec, ulong endTime100NanoSec, ushort videoTrackID)
        {
            var vidTracks = DestStream.MediaTracks.Where(t => t is GenericVideoTrack);
             int vidTrackCount = (vidTracks == null) ? 0 : vidTracks.Count();

             if (endTime100NanoSec == 0)
             { // special case when endTime == 0
             // using duration here is ok as it is about the total time of the source
             endTime100NanoSec = SourceStream.DurationIn100NanoSecs;
             }

             if (endTime100NanoSec - startTime100NanoSec < MaxIterateDuration)
             throw new Exception("Desired time interval for output stream too short");

             int outTracks = DestStream.MediaTracks.Count;
             RecodeSet[] trackEnumerators = new RecodeSet[outTracks];
             int k = 0;
             int n = 0;
             foreach (IMediaTrack track in SourceStream.MediaTracks)
             {
             if (((track.Codec.CodecType == CodecTypes.Audio) && (audioOrVideoOrBoth != TracksIncluded.Video)) ||
                ((track.Codec.CodecType == CodecTypes.Video) && ((videoTrackID == 0) || (track.TrackID == videoTrackID)) &&
                (audioOrVideoOrBoth != TracksIncluded.Audio)))
             {
                 RecodeSet recodeSet = new RecodeSet();
                 recodeSet.sourceTrack = (IMediaTrackSliceEnumerator)track.GetEnumerator();
                 recodeSet.sourceTrack.Reset();
                 recodeSet.pendingChunkSlices = new List<Slice>();

                 IMediaTrack destination = DestStream[recodeSet.sourceTrack.CodecType, 0];

                 if ((track.Codec.CodecType != CodecTypes.Video) || (vidTrackCount == 1))
                     destination = DestStream[recodeSet.sourceTrack.CodecType, 0];
                 else if (vidTrackCount > 1)
                 {
                     destination = vidTracks.ElementAt(n);
                     n++;
                 }

             if (destination == null)
             {
               throw new Exception(string.Format("No {0} destination track. Try vo or so option.", recodeSet.sourceTrack.CodecType));
             }

                 // normally the destination TrackDurationIn100NanoSecs is set to source duration;
                 // here we reset its value back to zero because it may be smaller than source duration
                 // (for example, if the start time is more than zero).
                 destination.TrackDurationIn100NanoSecs = 0UL;
                 recodeSet.destination = destination;
                 recodeSet.destinationTrack = (IMediaTrackSliceEnumerator)destination.GetEnumerator();
                 recodeSet.destinationTrack.Reset();

                 trackEnumerators[k++] = recodeSet;
             }
             }

             RaiseRecodeProgressUpdate(0.01f, true, null); // Indicate we have completed a portion of the work.

             // Need to call MoveNext() first for all source track enumerators
             foreach (RecodeSet recodeSet in trackEnumerators)
             {
             while (recodeSet.sourceTrack.MoveNext())
                 if (recodeSet.sourceTrack.Current != null) break;
             }

             IVideoTrack videoTrack = (IVideoTrack)SourceStream[CodecTypes.Video, 0];
             ulong prevSyncTime = 0UL;
             bool validSyncPointsFound = false;

             foreach (ulong syncTime in EnumerateSyncPoints(videoTrack))
             {// Cycle through all of the sync points in the video...
             Logger.Instance.Info("[GenericRecodeWRC::Recode] [merge] iterating at syncTime [" + syncTime + "].");

             if ((syncTime > endTime100NanoSec) && (prevSyncTime > endTime100NanoSec))
                 break; // If we are past the requested end time, stop doing work

             // Each source and destinatin track has its own, independent counter (enumerator).
             // The slices are synced with respect to time, and NOT with respect to index.
             // The outer for loop below iterates through each track being recoded;
             // the inner while loop iterates through each slice skipped.
             // .timeStart == time relative to source track at which recoding starts (should be first slice NOT skipped);
             // .indexStart == index of first slice NOT skipped.
             if (startTime100NanoSec > prevSyncTime)
             { // Skip a portion of slices.

                 for (int i = 0; i < trackEnumerators.Length; i++)
                 {
                     if (trackEnumerators[i].sourceTrack.CurrentTimeStampNew.HasValue == false)
                         continue; // b-frame and we can't use it to compare...

                     while (trackEnumerators[i].sourceTrack.CurrentTimeStampNew.Value < syncTime)
                     {
                         Slice slice = trackEnumerators[i].sourceTrack.Current;
                         if (slice == null) break;
                         if (slice.TimeStampNew.HasValue == false) continue; // its a b-frame, thus no time is available

                         trackEnumerators[i].timeStart = slice.TimeStampNew.Value; // at this point its guaranteed to have a value...
                         trackEnumerators[i].indexStart = slice.index;

                         // Find the next valid CurrentTimeStampNew value.
                         bool tmpEnd = false;
                         while (true)
                         {
                             if (!trackEnumerators[i].sourceTrack.MoveNext())
                             {
                                 tmpEnd = true;
                                 break; // Ended.
                             }

                             if (trackEnumerators[i].sourceTrack.CurrentTimeStampNew.HasValue == true)
                                 break; // Found it.
                         }

                         if (tmpEnd == true)
                             break;
                     }
                 }

                 prevSyncTime = syncTime;
                 continue;
             }

             // If we never hit this condition there is nothing actually taken in to process and this causes an exception down the road.
             validSyncPointsFound = true;

             // Each source and destinatin track has its own, independent counter (enumerator).
             // The slices are synced with respect to time, and NOT with respect to index.
             // The outer foreach loop below iterates through each track being recoded;
             // the inner while loop iterates through each slice.
             // recodeSet.sourceTrack ==> source track enumerator
             // recodeSet.destinationTrack ==> destination track enumerator
             ulong timeStamp100NanoSec = ulong.MaxValue;

             foreach (RecodeSet recodeSet in trackEnumerators)
             {
                 recodeSet.pendingChunkSlices.Clear();

                 // Start writing the actual data.
                 while (recodeSet.sourceTrack.CurrentTimeStampNew.HasValue == false
                     || recodeSet.sourceTrack.CurrentTimeStampNew.Value <= syncTime)
                 {
                     Slice slice = recodeSet.sourceTrack.Current;
                     if (slice == null)
                         break;

                     //Logger.Instance.Info("[GenericRecodeWRC::Recode] dumping slice [" + slice.TimeStampNew + ", dur " + (int)slice.SliceDuration + "], track type [" + recodeSet.sourceTrack.CodecType + "].");

                     // Prepare the slice; apply position and time compensation, to base it to the start of the extract.
                     slice.index -= recodeSet.indexStart;
                     if (slice.TimeStampNew.HasValue)
                     {// TimeStamp == null if we are a bframe, thus we are not here...

                         if (slice.TimeStampNew.Value < recodeSet.timeStart)
                             throw new Exception("GenericRecodeWRC.Recode: Offset time stamping error");

                         // adjust time-stamp and index (offset from time start)
                         slice.TimeStampNew -= recodeSet.timeStart;

                            if (timeStamp100NanoSec == ulong.MaxValue || slice.TimeStampNew.Value > timeStamp100NanoSec)
                                timeStamp100NanoSec = slice.TimeStampNew.Value; // Take the value for the progress report.
                     }

                     // Put the slices in the pending Chunk buffer for overview and confirmation.
                     recodeSet.pendingChunkSlices.Add(slice);

               // position to next output slice
               recodeSet.destinationTrack.MoveNext();

               // put slice in destination track
               recodeSet.destinationTrack.SetCurrent(slice);

               recodeSet.destination.TrackDurationIn100NanoSecs += (ulong)slice.SliceDuration;

                     // move to next input slice, exit if done
                     if (!recodeSet.sourceTrack.MoveNext())
                         break;
                 }
             }

             // Report progress.
             if (timeStamp100NanoSec != ulong.MaxValue)
             {
                 float progress = (float)(((double)timeStamp100NanoSec - (double)startTime100NanoSec) / ((double)endTime100NanoSec - (double)startTime100NanoSec));
                 if (progress > 1)
                     Common.Logger.Instance.Error("[GenericRecodeWRC::Recode] Progress value [" + progress + "] mis-calculated, progress report skipped.");
                 else
                     RaiseRecodeProgressUpdate(progress, true, null);
             }

             prevSyncTime = syncTime;
             }

             if (validSyncPointsFound == false)
             {// Nothing meaningful found to process, end now.
             // Do not DestStream.FinalizeStream() as this will try to write and cause an exception.
             RaiseRecodeProgressUpdate(1, false, null);
             RaiseRecodeProgressUpdate(2, false, null);
             return;
             }

             RaiseRecodeProgressUpdate(1, true, null); // All the work is done, but there may be some finalizers left.

             // Assemble all stbl or moof boxes.
             // Write out the mdat slice in the case of MP4 output;
             // in the case of fragmented files (ISMV output), all moof and mdat boxes have already been written out at this point, and we
             // only need to write out the mfra slice, if it is needed.
             DestStream.FinalizeStream();

             RaiseRecodeProgressUpdate(2, true, null); // Everything is completed.
        }