/// <summary> /// Seeks to the specified target second. /// </summary> /// <param name="targetSecond">The target second.</param> public void Seek(decimal targetSecond) { if (IsLiveStream) { return; } if (AudioRenderer.HasInitialized) { AudioRenderer.Stop(); } MediaFramesExtractedDone.Wait(Constants.FrameExtractorWaitMs); RealtimeClock.Seek(targetSecond); this.m_Position = targetSecond; NotifyPlayStateChanged(); }
/// <summary> /// Rewinds and pauses media playback /// </summary> public void Stop() { if (IsLiveStream) { return; } if (AudioRenderer.HasInitialized) { AudioRenderer.Stop(); } MediaFramesExtractedDone.Wait(Constants.FrameExtractorWaitMs); this.HasMediaEnded = false; if (Position > StartTime) { RealtimeClock.Seek(StartTime); } }
/// <summary> /// Initializes the internal transcoder -- This create the input, processing, and output blocks that make /// up the video and audio decoding stream. /// </summary> /// <param name="filePath">The file path.</param> /// <param name="inputFormatName">Name of the input format. Leave null or empty to detect automatically</param> /// <param name="referer">The referer. Leave null or empty to skip setting it</param> /// <param name="userAgent">The user agent. Leave null or empty to skip setting it.</param> /// <exception cref="FileFormatException"></exception> /// <exception cref="Exception">Could not find stream info /// or /// Media must contain at least a video or and audio stream</exception> /// <exception cref="System.Exception">Could not open file /// or /// Could not find stream info /// or /// Media must contain a video stream /// or /// Media must contain an audio stream /// or /// Unsupported codec /// or /// Could not initialize the output conversion context /// or /// Could not create output codec context from input /// or /// Could not open codec</exception> private void InitializeMedia(string filePath, string inputFormatName, string referer, string userAgent) { // Create the input format context by opening the file InputFormatContext = ffmpeg.avformat_alloc_context(); AVDictionary *optionsDict = null; if (string.IsNullOrWhiteSpace(userAgent) == false) { ffmpeg.av_dict_set(&optionsDict, "user-agent", userAgent, 0); } if (string.IsNullOrWhiteSpace(referer) == false) { ffmpeg.av_dict_set(&optionsDict, "headers", $"Referer:{referer}", 0); } ffmpeg.av_dict_set_int(&optionsDict, "usetoc", 1, 0); { // for m3u8 (HLS) streaming // TODO: maybe detect here if it is streaming? I need to test if this negatively affects filesystem files or network files as opposed to RTSP streams and HLS streams ffmpeg.av_dict_set_int(&optionsDict, "multiple_requests", 1, 0); ffmpeg.av_dict_set_int(&optionsDict, "reconnect", 1, 0); ffmpeg.av_dict_set_int(&optionsDict, "reconnect_at_eof", 1, 0); ffmpeg.av_dict_set_int(&optionsDict, "reconnect_streamed", 1, 0); ffmpeg.av_dict_set_int(&optionsDict, "reconnect_delay_max", (int)Constants.WaitForPlaybackReadyStateTimeout.TotalMilliseconds, 0); } AVInputFormat *inputFormat = null; if (string.IsNullOrWhiteSpace(inputFormatName) == false) { inputFormat = ffmpeg.av_find_input_format(inputFormatName); fixed(AVFormatContext **inputFormatContextRef = &InputFormatContext) { if (ffmpeg.avformat_open_input(inputFormatContextRef, filePath, inputFormat, &optionsDict) != 0) { throw new FileFormatException(string.Format("Could not open stream or file '{0}'", filePath)); } } InputFormatContext->iformat->flags |= ffmpeg.AVFMT_FLAG_NOBUFFER; InputFormatContext->iformat->flags |= ffmpeg.AVFMT_FLAG_NOFILLIN; ffmpeg.av_dict_free(&optionsDict); // Extract the stream info headers from the file if (ffmpeg.avformat_find_stream_info(InputFormatContext, null) != 0) { throw new Exception("Could not find stream info"); } // search for the audio and video streams for (int i = 0; i < InputFormatContext->nb_streams; i++) { var codecType = InputFormatContext->streams[i]->codec->codec_type; if (codecType == AVMediaType.AVMEDIA_TYPE_VIDEO && InputVideoStream == null) { InputVideoStream = InputFormatContext->streams[i]; continue; } if (codecType == AVMediaType.AVMEDIA_TYPE_AUDIO && InputAudioStream == null) { InputAudioStream = InputFormatContext->streams[i]; continue; } } if (InputVideoStream != null) { this.InitializeVideo(); this.HasVideo = VideoBitrate > 0 || VideoFrameRate > 0M || VideoFrameWidth > 0 || VideoFrameHeight > 0; } if (InputAudioStream != null) { this.InitializeAudio(); this.HasAudio = AudioBytesPerSample > 0; } if (HasAudio == false && HasVideo == false) { throw new Exception("Media must contain at least a video or and audio stream"); } else { // General Properties here NaturalDuration = Convert.ToDecimal(Convert.ToDouble(InputFormatContext->duration) / Convert.ToDouble(ffmpeg.AV_TIME_BASE)); IsLiveStream = Helper.IsNoPtsValue(InputFormatContext->duration); StartTime = Convert.ToDecimal(Convert.ToDouble(InputFormatContext->start_time) / Convert.ToDouble(ffmpeg.AV_TIME_BASE)); EndTime = StartTime + NaturalDuration; RealtimeClock.Seek(StartTime); } }
/// <summary> /// Initializes the internal transcoder -- This create the input, processing, and output blocks that make /// up the video and audio decoding stream. /// </summary> /// <param name="filePath">The file path.</param> /// <exception cref="System.Exception"> /// Could not open file /// or /// Could not find stream info /// or /// Media must contain a video stream /// or /// Media must contain an audio stream /// or /// Unsupported codec /// or /// Could not initialize the output conversion context /// or /// Could not create output codec context from input /// or /// Could not open codec /// </exception> private void InitializeMedia(string filePath) { // Create the input format context by opening the file InputFormatContext = ffmpeg.avformat_alloc_context(); AVDictionary *optionsDict = null; ffmpeg.av_dict_set_int(&optionsDict, "usetoc", 1, 0); fixed(AVFormatContext **inputFormatContextRef = &InputFormatContext) { if (ffmpeg.avformat_open_input(inputFormatContextRef, filePath, null, &optionsDict) != 0) { throw new Exception(string.Format("Could not open file '{0}'", filePath)); } } //InputFormatContext->iformat->flags = InputFormatContext->iformat->flags | FFmpegInvoke.AVFMT_SEEK_TO_PTS; ffmpeg.av_dict_free(&optionsDict); // Extract the stream info headers from the file if (ffmpeg.avformat_find_stream_info(InputFormatContext, null) != 0) { throw new Exception("Could not find stream info"); } // search for the audio and video streams for (int i = 0; i < InputFormatContext->nb_streams; i++) { var codecType = InputFormatContext->streams[i]->codec->codec_type; if (codecType == AVMediaType.AVMEDIA_TYPE_VIDEO && InputVideoStream == null) { InputVideoStream = InputFormatContext->streams[i]; continue; } if (codecType == AVMediaType.AVMEDIA_TYPE_AUDIO && InputAudioStream == null) { InputAudioStream = InputFormatContext->streams[i]; continue; } } if (InputVideoStream != null) { this.InitializeVideo(); this.HasVideo = VideoBitrate > 0 || VideoFrameRate > 0M || VideoFrameWidth > 0 || VideoFrameHeight > 0; } if (InputAudioStream != null) { this.InitializeAudio(); this.HasAudio = AudioBytesPerSample > 0; } if (HasAudio == false && HasVideo == false) { throw new Exception("Media must contain at least a video or and audio stream"); } else { // General Properties here NaturalDuration = Convert.ToDecimal(Convert.ToDouble(InputFormatContext->duration) / Convert.ToDouble(ffmpeg.AV_TIME_BASE)); IsLiveStream = Helper.IsNoPtsValue(InputFormatContext->duration); StartTime = Convert.ToDecimal(Convert.ToDouble(InputFormatContext->start_time) / Convert.ToDouble(ffmpeg.AV_TIME_BASE)); EndTime = StartTime + NaturalDuration; RealtimeClock.Seek(StartTime); } }