/// <summary> /// Opens the video stream with the specified index in the media container. /// </summary> /// <param name="container">The media container.</param> /// <param name="index">The video stream index.</param> /// <param name="options">The media options.</param> /// <returns>The opened <see cref="InputStream{TFrame}"/>.</returns> internal static InputStream <VideoFrame> OpenVideo(InputContainer container, int index, MediaOptions options) { var format = container.Pointer; var stream = format->streams[index]; var codecContext = stream->codec; codecContext->pkt_timebase = stream->time_base; var codec = ffmpeg.avcodec_find_decoder(codecContext->codec_id); if (codec == null) { throw new InvalidOperationException("Cannot find a codec for this stream."); } var dict = new FFDictionary(options.DecoderOptions); var ptr = dict.Pointer; ffmpeg.avcodec_open2(codecContext, codec, &ptr) .ThrowIfError("Cannot open the video codec"); dict.Update(ptr); return(new InputStream <VideoFrame>(stream, container)); }
/// <summary> /// Creates a new video stream for the specified <see cref="OutputContainer"/>. /// </summary> /// <param name="container">The media container.</param> /// <param name="config">The stream settings.</param> /// <returns>The new video stream.</returns> public static OutputStream <VideoFrame> CreateVideo(OutputContainer container, VideoEncoderSettings config) { var codecId = config.Codec ?? container.Pointer->oformat->video_codec; if (codecId == AVCodecID.AV_CODEC_ID_NONE) { throw new InvalidOperationException("The media container doesn't support video!"); } var codec = ffmpeg.avcodec_find_encoder(codecId); if (codec == null) { throw new InvalidOperationException($"Cannot find an encoder with the {codecId}!"); } if (codec->type != AVMediaType.AVMEDIA_TYPE_VIDEO) { throw new InvalidOperationException($"The {codecId} encoder doesn't support video!"); } var videoStream = ffmpeg.avformat_new_stream(container.Pointer, codec); var codecContext = videoStream->codec; codecContext->codec_id = codecId; codecContext->codec_type = AVMediaType.AVMEDIA_TYPE_VIDEO; codecContext->bit_rate = config.Bitrate; codecContext->width = config.VideoWidth; codecContext->height = config.VideoHeight; codecContext->time_base.den = config.Framerate; codecContext->time_base.num = 1; codecContext->gop_size = config.KeyframeRate; codecContext->pix_fmt = (AVPixelFormat)config.VideoFormat; if ((container.Pointer->oformat->flags & ffmpeg.AVFMT_GLOBALHEADER) != 0) { codecContext->flags |= ffmpeg.AV_CODEC_FLAG_GLOBAL_HEADER; } var dict = new FFDictionary(config.CodecOptions); var ptr = dict.Pointer; ffmpeg.avcodec_open2(codecContext, codec, &ptr); dict.Update(ptr); return(new OutputStream <VideoFrame>(videoStream, container)); }
/// <summary> /// Creates a new audio stream for the specified <see cref="OutputContainer"/>. /// </summary> /// <param name="container">The media container.</param> /// <param name="config">The stream settings.</param> /// <returns>The new audio stream.</returns> public static OutputStream <AudioFrame> CreateAudio(OutputContainer container, AudioEncoderSettings config) { var codecId = config.Codec == AudioCodec.Default ? container.Pointer->oformat->audio_codec : (AVCodecID)config.Codec; if (codecId == AVCodecID.AV_CODEC_ID_NONE) { throw new InvalidOperationException("The media container doesn't support audio!"); } var codec = ffmpeg.avcodec_find_encoder(codecId); if (codec == null) { throw new InvalidOperationException($"Cannot find an encoder with the {codecId}!"); } if (codec->type != AVMediaType.AVMEDIA_TYPE_AUDIO) { throw new InvalidOperationException($"The {codecId} encoder doesn't support audio!"); } var audioStream = ffmpeg.avformat_new_stream(container.Pointer, codec); var codecContext = audioStream->codec; codecContext->time_base = config.TimeBase; codecContext->codec_id = codecId; codecContext->codec_type = AVMediaType.AVMEDIA_TYPE_AUDIO; codecContext->bit_rate = config.Bitrate; codecContext->sample_rate = config.SampleRate; codecContext->frame_size = config.SamplesPerFrame; codecContext->sample_fmt = (AVSampleFormat)config.SampleFormat; codecContext->channels = config.Channels; codecContext->channel_layout = (ulong)ffmpeg.av_get_default_channel_layout(config.Channels); if ((container.Pointer->oformat->flags & ffmpeg.AVFMT_GLOBALHEADER) != 0) { codecContext->flags |= ffmpeg.AV_CODEC_FLAG_GLOBAL_HEADER; } var dict = new FFDictionary(config.CodecOptions); var ptr = dict.Pointer; ffmpeg.avcodec_open2(codecContext, codec, &ptr); dict.Update(ptr); return(new OutputStream <AudioFrame>(audioStream, container)); }
/// <summary> /// Opens a media container and stream codecs from given path. /// </summary> /// <param name="path">A path to the multimedia file.</param> /// <param name="options">The media settings.</param> /// <returns>A new instance of the <see cref="InputContainer"/> class.</returns> public static InputContainer LoadFile(string path, MediaOptions options) { MediaToolkit.LoadFFmpeg(); var context = ffmpeg.avformat_alloc_context(); options.DemuxerOptions.ApplyFlags(context); var dict = new FFDictionary(options.DemuxerOptions.PrivateOptions); var ptr = dict.Pointer; ffmpeg.avformat_open_input(&context, path, null, &ptr) .ThrowIfError("An error ocurred while opening the file"); ffmpeg.avformat_find_stream_info(context, null) .ThrowIfError("Cannot find stream info"); dict.Update(ptr); var container = new InputContainer(context); container.OpenStreams(options); return(container); }
/// <summary> /// Creates a new video stream for the specified <see cref="OutputContainer"/>. /// </summary> /// <param name="container">The media container.</param> /// <param name="config">The stream settings.</param> /// <returns>The new video stream.</returns> public static OutputStream <VideoFrame> CreateVideo(OutputContainer container, VideoEncoderSettings config) { var codecId = config.Codec == VideoCodec.Default ? container.Pointer->oformat->video_codec : (AVCodecID)config.Codec; if (codecId == AVCodecID.AV_CODEC_ID_NONE) { throw new InvalidOperationException("The media container doesn't support video!"); } var codec = ffmpeg.avcodec_find_encoder(codecId); if (codec == null) { throw new InvalidOperationException($"Cannot find an encoder with the {codecId}!"); } if (codec->type != AVMediaType.AVMEDIA_TYPE_VIDEO) { throw new InvalidOperationException($"The {codecId} encoder doesn't support video!"); } var videoStream = ffmpeg.avformat_new_stream(container.Pointer, codec); videoStream->time_base = config.TimeBase; videoStream->r_frame_rate = config.FramerateRational; var codecContext = videoStream->codec; codecContext->codec_id = codecId; codecContext->codec_type = AVMediaType.AVMEDIA_TYPE_VIDEO; codecContext->width = config.VideoWidth; codecContext->height = config.VideoHeight; codecContext->time_base = videoStream->time_base; codecContext->framerate = videoStream->r_frame_rate; codecContext->gop_size = config.KeyframeRate; codecContext->pix_fmt = (AVPixelFormat)config.VideoFormat; if ((container.Pointer->oformat->flags & ffmpeg.AVFMT_GLOBALHEADER) != 0) { codecContext->flags |= ffmpeg.AV_CODEC_FLAG_GLOBAL_HEADER; } var dict = new FFDictionary(config.CodecOptions); if (config.CRF.HasValue && config.Codec.IsMatch(VideoCodec.H264, VideoCodec.H265, VideoCodec.VP9, VideoCodec.VP8)) { dict["crf"] = config.CRF.Value.ToString(); } else { codecContext->bit_rate = config.Bitrate; } if (config.Codec.IsMatch(VideoCodec.H264, VideoCodec.H265)) { dict["preset"] = config.EncoderPreset.GetDescription(); } var ptr = dict.Pointer; ffmpeg.avcodec_open2(codecContext, codec, &ptr); dict.Update(ptr); return(new OutputStream <VideoFrame>(videoStream, container)); }