Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improved AV Reading/Writing #57

Merged
merged 27 commits into from Jan 22, 2021
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
7260c71
Update FFmpeg setup guide in the readme.md
radek-k Dec 15, 2020
c61b56c
Update README.md
radek-k Dec 15, 2020
9fdd455
Add ReadFrame method overloads to allow writing decoded frame directl…
radek-k Dec 18, 2020
de61d1d
Fix the stream seek method (#50)
radek-k Dec 18, 2020
e5f9dac
Add bitmap stride check.
radek-k Dec 19, 2020
838ebfc
Update FFmpegLoader error messages.
radek-k Dec 19, 2020
1b96e2f
Add a FrameCount property replacement.
radek-k Dec 20, 2020
ee0f1e6
Update sample code in the README (#43).
radek-k Dec 21, 2020
e8d226d
Update package version.
radek-k Dec 21, 2020
e353fba
Added getter FFmpegLicense in FFmpegLoader for the license string
jrz371 Dec 22, 2020
2b58dfe
Add stream wrapper
hey-red Dec 24, 2020
5d422a2
Implement reading through AVIO
hey-red Dec 24, 2020
5875489
Use discard
hey-red Dec 24, 2020
7dffd3d
StyleCop fixes.
radek-k Dec 29, 2020
6c8ddd1
Merge pull request #54 from hey-red/master
radek-k Dec 29, 2020
3684d74
Merge pull request #53 from jrz371/master
radek-k Dec 29, 2020
55de73a
Update version.
radek-k Dec 29, 2020
fd90d2d
Update README
radek-k Dec 30, 2020
3fd2891
Merge branch 'master' into develop
IsaMorphic Jan 2, 2021
0398e20
Finished initial implementation of multi-stream reading support (UNTE…
IsaMorphic Jan 3, 2021
7b6768c
Made a base class for both video and audio streams to avoid repeat co…
IsaMorphic Jan 4, 2021
de0591f
Implemented multi-stream API in MediaFile class
IsaMorphic Jan 4, 2021
7e21004
Restored features requested by the PR reviewer (Tested & Working)
IsaMorphic Jan 5, 2021
76baf33
Finished multi-stream audio/video writing implementation. Also imple…
IsaMorphic Jan 13, 2021
9190952
Fix big bug in frame adding logic
IsaMorphic Jan 14, 2021
3cfdc01
Fixed bug that occurs when trying to get a MediaStream's position bef…
IsaMorphic Jan 14, 2021
ebc0075
Final bug fixes and changes
IsaMorphic Jan 18, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion FFMediaToolkit/Common/ContainerMetadata.cs
@@ -1,7 +1,7 @@
namespace FFMediaToolkit.Common
{
using FFmpeg.AutoGen;
using System.Collections.Generic;
using FFmpeg.AutoGen;

/// <summary>
/// Represents multimedia file metadata info.
Expand Down
5 changes: 3 additions & 2 deletions FFMediaToolkit/Common/Internal/AudioFrame.cs
@@ -1,4 +1,5 @@
namespace FFMediaToolkit.Common.Internal {
namespace FFMediaToolkit.Common.Internal
{
using System;
using FFMediaToolkit.Helpers;
using FFmpeg.AutoGen;
Expand Down Expand Up @@ -40,7 +41,7 @@ public AudioFrame(AVFrame* frame)
/// <summary>
/// Gets the audio sample format.
/// </summary>
public AVSampleFormat SampleFormat => Pointer != null ? (AVSampleFormat) Pointer->format : AVSampleFormat.AV_SAMPLE_FMT_NONE;
public AVSampleFormat SampleFormat => Pointer != null ? (AVSampleFormat)Pointer->format : AVSampleFormat.AV_SAMPLE_FMT_NONE;

/// <summary>
/// Creates an audio frame with given dimensions and allocates a buffer for it.
Expand Down
5 changes: 2 additions & 3 deletions FFMediaToolkit/Common/Internal/MediaPacket.cs
Expand Up @@ -48,12 +48,11 @@ public int StreamIndex
/// <summary>
/// Allocates a new empty packet.
/// </summary>
/// <param name="streamIndex">The packet stream index.</param>
/// <returns>The new <see cref="MediaPacket"/>.</returns>
public static MediaPacket AllocateEmpty(int streamIndex)
public static MediaPacket AllocateEmpty()
{
var packet = ffmpeg.av_packet_alloc();
packet->stream_index = streamIndex;
packet->stream_index = -1;
return new MediaPacket(packet);
}

Expand Down
1 change: 0 additions & 1 deletion FFMediaToolkit/Common/Internal/VideoFrame.cs
Expand Up @@ -91,7 +91,6 @@ public ImageData ToBitmap(ImageConverter converter, ImagePixelFormat targetForma
return bitmap;
}


/// <inheritdoc/>
internal override unsafe void Update(AVFrame* newFrame)
{
Expand Down
@@ -1,9 +1,9 @@
namespace FFMediaToolkit.Common.Internal
namespace FFMediaToolkit.Common
{
/// <summary>
/// Represents the multimedia stream types.
/// </summary>
internal enum MediaType
public enum MediaType
IsaMorphic marked this conversation as resolved.
Show resolved Hide resolved
{
/// <summary>
/// Other media type not supported by the FFMediaToolkit.
Expand Down
129 changes: 48 additions & 81 deletions FFMediaToolkit/Decoding/AudioStream.cs
@@ -1,126 +1,93 @@
namespace FFMediaToolkit.Decoding
{
using System;
using System.IO;
using FFMediaToolkit.Audio;
using FFMediaToolkit.Common.Internal;
using FFMediaToolkit.Decoding.Internal;
using FFMediaToolkit.Helpers;
using FFmpeg.AutoGen;

/// <summary>
/// Represents an audio stream in the <see cref="MediaFile"/>.
/// </summary>
public class AudioStream : IDisposable
public class AudioStream : MediaStream
{
private readonly Decoder<AudioFrame> stream;
private readonly AudioFrame frame;
private readonly MediaOptions mediaOptions;

private readonly object syncLock = new object();

/// <summary>
/// Initializes a new instance of the <see cref="AudioStream"/> class.
/// </summary>
/// <param name="audio">The Audio stream.</param>
/// <param name="stream">The audio stream.</param>
/// <param name="options">The decoder settings.</param>
internal AudioStream(Decoder<AudioFrame> audio, MediaOptions options)
internal AudioStream(Decoder stream, MediaOptions options)
: base(stream, options)
{
stream = audio;
mediaOptions = options;
frame = AudioFrame.CreateEmpty();
}

/// <summary>
/// Gets informations about this stream.
/// </summary>
public AudioStreamInfo Info => (AudioStreamInfo)stream.Info;
public new AudioStreamInfo Info => base.Info as AudioStreamInfo;

/// <summary>
/// Gets the index of the next frame in the Audio stream.
/// Reads the next frame from the audio stream.
/// </summary>
public int FramePosition { get; private set; }

/// <summary>
/// Gets the timestamp of the next frame in the Audio stream.
/// </summary>
public TimeSpan Position => FramePosition.ToTimeSpan(Info.AvgFrameRate);

/// <summary>
/// Reads the specified audio frame (chunk of audio samples sized by ffmedia settings).
/// </summary>
/// <param name="frameNumber">The frame index (zero-based number).</param>
/// <returns>The decoded audio frame.</returns>
public AudioData ReadFrame(int frameNumber)
/// <returns>The decoded audio data.</returns>
public new AudioData GetNextFrame()
{
lock (syncLock)
{
frameNumber = frameNumber.Clamp(0, Info.FrameCount != 0 ? Info.FrameCount - 1 : int.MaxValue);

if (frameNumber == FramePosition)
{
return GetNextFrameData();
}
else if (frameNumber == FramePosition - 1)
{
return new AudioData(stream.RecentlyDecodedFrame);
}
else
{
var frame = SeekToFrame(frameNumber);
FramePosition = frameNumber + 1;

return new AudioData(frame);
}
}
var frame = base.GetNextFrame() as AudioFrame;
return new AudioData(frame);
}

/// <summary>
/// Reads the audio frame found at the specified timestamp.
/// </summary>
/// <param name="targetTime">The frame timestamp.</param>
/// <returns>The decoded audio frame.</returns>
public AudioData ReadFrame(TimeSpan targetTime) => ReadFrame((int)(targetTime.TotalSeconds * Info.AvgFrameRate));

/// <summary>
/// Reads the next frame from this stream.
/// Reads the next frame from the audio stream.
/// A <see langword="false"/> return value indicates that reached end of stream.
/// The method throws exception if another error has occurred.
/// </summary>
/// <returns>The decoded audio frame.</returns>
public AudioData ReadNextFrame()
/// <param name="data">The decoded audio data.</param>
/// <returns><see langword="false"/> if reached end of the stream.</returns>
public bool TryGetNextFrame(out AudioData data)
{
lock (syncLock)
try
{
return GetNextFrameData();
data = GetNextFrame();
return true;
}
}

/// <inheritdoc/>
void IDisposable.Dispose()
{
lock (syncLock)
catch (EndOfStreamException)
{
stream.Dispose();
frame.Dispose();
data = default;
return false;
}
}

private AudioData GetNextFrameData()
/// <summary>
/// Reads the video frame found at the specified timestamp.
/// </summary>
/// <param name="time">The frame timestamp.</param>
/// <returns>The decoded video frame.</returns>
public new AudioData GetFrame(TimeSpan time)
{
var bmp = new AudioData(stream.GetNextFrame());
FramePosition++;
return bmp;
var frame = base.GetFrame(time) as AudioFrame;
return new AudioData(frame);
}

private AudioFrame SeekToFrame(int frameNumber)
/// <summary>
/// Reads the audio data found at the specified timestamp.
/// A <see langword="false"/> return value indicates that reached end of stream.
/// The method throws exception if another error has occurred.
/// </summary>
/// <param name="time">The frame timestamp.</param>
/// <param name="data">The decoded audio data.</param>
/// <returns><see langword="false"/> if reached end of the stream.</returns>
public bool TryGetFrame(TimeSpan time, out AudioData data)
{
var ts = frameNumber * Info.AvgNumSamplesPerFrame;

if (frameNumber < FramePosition || frameNumber > FramePosition + mediaOptions.AudioSeekThreshold)
try
{
stream.OwnerFile.SeekFile(ts, Info.Index);
data = GetFrame(time);
return true;
}
catch (EndOfStreamException)
{
data = default;
return false;
}

stream.SkipFrames(ts);
return stream.RecentlyDecodedFrame;
}
}
}
57 changes: 57 additions & 0 deletions FFMediaToolkit/Decoding/AudioStreamInfo.cs
@@ -0,0 +1,57 @@
namespace FFMediaToolkit.Decoding
{
using System;
using FFMediaToolkit.Common;
using FFMediaToolkit.Decoding.Internal;
using FFMediaToolkit.Helpers;
using FFmpeg.AutoGen;

/// <summary>
/// Represents informations about the audio stream.
/// </summary>
public class AudioStreamInfo : StreamInfo
{
/// <summary>
/// Initializes a new instance of the <see cref="AudioStreamInfo"/> class.
/// </summary>
/// <param name="stream">A generic stream.</param>
/// <param name="container">The input container.</param>
internal unsafe AudioStreamInfo(AVStream* stream, InputContainer container)
: base(stream, MediaType.Audio, container)
{
var codec = stream->codec;
NumChannels = codec->channels;
SampleRate = codec->sample_rate;
long num_samples = stream->duration >= 0 ? stream->duration : container.Pointer->duration;
AvgNumSamplesPerFrame = (int)Math.Round((double)num_samples / NumberOfFrames.Value);
SampleFormat = codec->sample_fmt.FormatEnum(14);
AvSampleFormat = codec->sample_fmt;
}

/// <summary>
/// Gets the number of audio channels stored in the stream.
/// </summary>
public int NumChannels { get; }

/// <summary>
/// Gets the number of samples per second of the audio stream.
/// </summary>
public int SampleRate { get; }

/// <summary>
/// Gets the average number of samples per frame (chunk of samples) calculated from metadata.
/// It is used to calculate timestamps in the internal decoder methods.
/// </summary>
public int AvgNumSamplesPerFrame { get; }

/// <summary>
/// Gets a lowercase string representing the audio sample format.
/// </summary>
public string SampleFormat { get; }

/// <summary>
/// Gets the audio sample format.
/// </summary>
internal AVSampleFormat AvSampleFormat { get; }
}
}