public GenericRecodeWRC(IMediaStream srcStream, IMediaStream destStream, int videoTrackID, TracksIncluded audioOrVideoOnly = TracksIncluded.Both, bool cttsOut = false) : base(srcStream, destStream) { audioOrVideoOrBoth = audioOrVideoOnly; CTTSOut = cttsOut; //Common.Logger.Instance.Info("[GenericRecodeWRC::Ctor] srcStream [" + srcStream.GetType().Name + "], destStream [" + destStream.GetType().Name + "], videoTrackId [" + videoTrackID + "]"); // get characteristics of input stream, and set FetchNextBlock callback on each track. TrackInfo = IsochronousTrackInfo.GetTrackCharacteristics(SourceStream, audioOrVideoOrBoth, videoTrackID); if ((!TrackInfo.Any(t => t is RawVideoTrackInfo)) && (audioOrVideoOnly != TracksIncluded.Audio)) { throw new ArgumentOutOfRangeException("Video track specified does not exist"); } AdjustTrackSpecsToDestination(); // adjust recode params according to output // setup destination stream here (initialize headers in output tracks) DestStream.InitializeForWriting(TrackInfo); }
/// <summary> /// AdjustTrackSpecsToDestination /// The purpose of this is to control the recoding by modifying two instances of the class /// BaseTrackInfo under the two subclasses RawAudioTrackInfo and RawVideoTrackInfo. /// By default, these subclass instances take on the characteristics of the input media. /// NOTE: This method should be XML driven or should pick up parameters from a configuration file. /// FIXME: What about other track types? /// </summary> protected void AdjustTrackSpecsToDestination() { foreach (IsochronousTrackInfo trackDef in TrackInfo) { trackDef.TrackID = 0; // reset track ID (destination stream should determine track ID) if (trackDef is RawVideoTrackInfo) { trackDef.CTTSOut = CTTSOut; } } uint oneSecondTicks = (uint)TimeSpan.TicksPerSecond; if (DestStream.GetType().FullName.Equals("Media.Formats.MP4.MP4StreamWriter")) { TrackInfo.ForEach(delegate(IsochronousTrackInfo trk) { // set the movie time scale to 1,000 trk.MovieTimeScale = 1000; // Set the track time scale to 10,000. // QuickTime cannot handle ulong durations, and our mp4 writer automatically switches to ulong if // a movie is more than 2,166,748,000 units long (a value which goes beyond int.MaxValue). // The track duration can get this high if the time scale is 10,000,000 which is what Expression uses. trk.TimeScale = 10000; if (trk is RawVideoTrackInfo) { trk.CTTSOut = CTTSOut; } else if (trk is RawAudioTrackInfo) { // if we are recoding to MP4 from HyperAsset, private codec data should be set as follows if (SourceStream.GetType().FullName.Contains(".AssetMediaStream")) // This needs to encompass the new AssetMediaStream2 class. { trk.CodecPrivateData = "038080220000000480801640150020000001F4000001F4000580800511900000000680800102"; } } }); } else if (DestStream.IsMediaStreamFragmented) { TrackInfo.ForEach(delegate(IsochronousTrackInfo trk) { if (trk is RawVideoTrackInfo) { // modify RawVideoTrackInfo: for fragmented tracks, timescale should be = oneSecondTicks // rvti.MovieDurationIn100NanoSecs = rvti.MovieDurationIn100NanoSecs * (oneSecondTicks / rvti.MovieTimeScale); // FIXME: what if rvti.MovieTimeScale > oneSecondTicks ticks? trk.MovieTimeScale = oneSecondTicks; //rvti.DurationIn100NanoSecs = rvti.DurationIn100NanoSecs * (oneSecondTicks / rvti.TimeScale); trk.TimeScale = oneSecondTicks; trk.IsFragment = true; } }); } if (TrackInfo.Any(t => t is RawAudioTrackInfo)) { RawAudioTrackInfo rati = (RawAudioTrackInfo)TrackInfo.First(t => t is RawAudioTrackInfo); if ((rati != null) && (audioOrVideoOrBoth == TracksIncluded.Video)) { TrackInfo.Remove(rati); rati = null; } } if (audioOrVideoOrBoth == TracksIncluded.Audio) { IsochronousTrackInfo rvti; do { rvti = TrackInfo.FirstOrDefault(t => t is RawVideoTrackInfo); if (rvti != null) { TrackInfo.Remove(rvti); } } while (rvti != null); } }