Skip to content
This repository was archived by the owner on Feb 22, 2024. It is now read-only.

Commit 7deba8f

Browse files
committed
Adding additional unit tests for static overlay, video splitter component and FFmpeg functionality.
1 parent 8b71b68 commit 7deba8f

8 files changed

Lines changed: 314 additions & 132 deletions

File tree

src/MMALSharp.FFmpeg/Handlers/FFmpegCaptureHandler.cs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@ namespace MMALSharp.Handlers
1515
public class FFmpegCaptureHandler : ICaptureHandler
1616
{
1717
private Process _process;
18-
18+
1919
/// <summary>
2020
/// Streams video from the standard output stream via FFmpeg to an RTMP server.
2121
/// </summary>
2222
/// <param name="streamName">The meta name of the stream.</param>
2323
/// <param name="streamUrl">The url of your RTMP server - the url to stream to.</param>
24-
/// <returns></returns>
24+
/// <returns>A new instance of <see cref="FFmpegCaptureHandler"/> with process arguments to push to an RTMP stream.</returns>
2525
public static FFmpegCaptureHandler RTMPStreamer(string streamName, string streamUrl)
2626
{
2727
return new FFmpegCaptureHandler($"-i - -vcodec copy -an -f flv -metadata streamName={streamName} {streamUrl}");
@@ -32,13 +32,18 @@ public static FFmpegCaptureHandler RTMPStreamer(string streamName, string stream
3232
/// </summary>
3333
/// <param name="directory">The directory to store the output video file.</param>
3434
/// <param name="filename">The name of the video file.</param>
35-
/// <returns></returns>
35+
/// <returns>A new instance of <see cref="FFmpegCaptureHandler"/> with process arguments to convert raw video into a compatible AVI container.</returns>
3636
public static FFmpegCaptureHandler RawVideoToAvi(string directory, string filename)
3737
{
3838
System.IO.Directory.CreateDirectory(directory);
39+
3940
return new FFmpegCaptureHandler($"-re -i - -c:v copy -an -f avi {directory.TrimEnd()}/{filename}.avi");
4041
}
4142

43+
/// <summary>
44+
/// Creates a new instance of <see cref="FFmpegCaptureHandler"/> with the specified process arguments.
45+
/// </summary>
46+
/// <param name="argument">The <see cref="ProcessStartInfo"/> argument.</param>
4247
public FFmpegCaptureHandler(string argument)
4348
{
4449
var processStartInfo = new ProcessStartInfo
@@ -105,6 +110,10 @@ public ProcessResult Process(uint allocSize)
105110
throw new NotImplementedException();
106111
}
107112

113+
/// <summary>
114+
/// Writes frame data to the StandardInput stream to be processed by FFmpeg.
115+
/// </summary>
116+
/// <param name="data">The frame data to push to FFmpeg.</param>
108117
public void Process(byte[] data)
109118
{
110119
try
@@ -118,7 +127,7 @@ public void Process(byte[] data)
118127
throw;
119128
}
120129
}
121-
130+
122131
public void Split()
123132
{
124133
throw new NotImplementedException();

src/MMALSharp/MMALCameraConfig.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -525,8 +525,8 @@ public struct Resolution : IComparable<Resolution>
525525
/// <summary>
526526
/// Creates a new instance of the <see cref="Resolution"/> class with the specified width and height.
527527
/// </summary>
528-
/// <param name="width"></param>
529-
/// <param name="height"></param>
528+
/// <param name="width">The width to assign this resolution with.</param>
529+
/// <param name="height">The height to assign this resolution with.</param>
530530
public Resolution(int width, int height)
531531
{
532532
Width = width;
@@ -604,7 +604,7 @@ public Resolution(int width, int height)
604604
/// <summary>
605605
/// Compares this Resolution instance against the Resolution passed in.
606606
/// </summary>
607-
/// <param name="res"></param>
607+
/// <param name="res">The resolution we are comparing to.</param>
608608
/// <returns>0 if width and height are same. 1 if source width is greater than target. -1 if target greater than source.</returns>
609609
public int CompareTo(Resolution res)
610610
{
@@ -634,7 +634,7 @@ public int CompareTo(Resolution res)
634634
/// </summary>
635635
/// <param name="width">The width to be padded to.</param>
636636
/// <param name="height">The height to be padded to.</param>
637-
/// <returns></returns>
637+
/// <returns>A new <see cref="Resolution"/> struct, padded to the required width/height.</returns>
638638
public Resolution Pad(int width = 32, int height = 16)
639639
{
640640
return new Resolution(MMALUtil.VCOS_ALIGN_UP(this.Width, width),

src/MMALSharp/Ports/MMALPortImpl.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ internal override void NativeOutputPortCallback(MMAL_PORT_T* port, MMAL_BUFFER_H
164164
this.ReleaseOutputBuffer(bufferImpl);
165165

166166
// If this buffer signals the end of data stream, allow waiting thread to continue.
167-
if (eos)
167+
if (eos || failed)
168168
{
169169
if (this.Trigger != null && this.Trigger.CurrentCount > 0)
170170
{

tests/MMALSharp.Tests/FFmpegTests.cs

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,88 @@
33
// Licensed under the MIT License. Please see LICENSE.txt for License info.
44
// </copyright>
55

6+
using System;
7+
using System.Threading;
8+
using System.Threading.Tasks;
9+
using MMALSharp.Components;
10+
using MMALSharp.FFmpeg;
11+
using MMALSharp.Handlers;
12+
using MMALSharp.Native;
13+
using Nito.AsyncEx;
14+
using Xunit;
15+
616
namespace MMALSharp.Tests
717
{
18+
[Collection("MMALCollection")]
819
public class FFmpegTests
920
{
21+
private readonly MMALFixture _fixture;
22+
23+
public FFmpegTests(MMALFixture fixture)
24+
{
25+
_fixture = fixture;
26+
TestData.Fixture = fixture;
27+
}
28+
29+
[Fact]
30+
public void RawVideoConvert()
31+
{
32+
TestHelper.BeginTest("RawVideoConvert");
33+
TestHelper.SetConfigurationDefaults();
34+
35+
AsyncContext.Run(async () =>
36+
{
37+
TestHelper.CleanDirectory("/home/pi/videos/tests");
38+
39+
using (var ffCaptureHandler = FFmpegCaptureHandler.RawVideoToAvi("/home/pi/videos/tests", "testing1234"))
40+
using (var vidEncoder = new MMALVideoEncoder(ffCaptureHandler))
41+
using (var renderer = new MMALVideoRenderer())
42+
{
43+
_fixture.MMALCamera.ConfigureCameraSettings();
44+
45+
vidEncoder.ConfigureOutputPort(0, MMALEncoding.H264, MMALEncoding.I420, 0, 25000000);
46+
47+
_fixture.MMALCamera.Camera.VideoPort.ConnectTo(vidEncoder);
48+
_fixture.MMALCamera.Camera.PreviewPort.ConnectTo(renderer);
49+
50+
// Camera warm up time
51+
await Task.Delay(2000);
52+
53+
var cts = new CancellationTokenSource(TimeSpan.FromMinutes(3));
54+
55+
// Take video for 3 minutes.
56+
await _fixture.MMALCamera.ProcessAsync(_fixture.MMALCamera.Camera.VideoPort, cts.Token);
57+
58+
_fixture.CheckAndAssertFilepath("/home/pi/videos/tests/testing1234.avi");
59+
}
60+
});
61+
}
62+
63+
[Fact]
64+
public void ImagesToVideo()
65+
{
66+
TestHelper.BeginTest("ImagesToVideo");
67+
TestHelper.SetConfigurationDefaults();
68+
69+
AsyncContext.Run(async () =>
70+
{
71+
TestHelper.CleanDirectory("/home/pi/videos/tests");
72+
TestHelper.CleanDirectory("/home/pi/images/tests");
73+
74+
// This example will take an image every 5 seconds for 1 minute.
75+
using (var imgCaptureHandler = new ImageStreamCaptureHandler("/home/pi/images/", "jpg"))
76+
{
77+
var cts = new CancellationTokenSource(TimeSpan.FromMinutes(1));
78+
79+
var tl = new Timelapse { Mode = TimelapseMode.Second, CancellationToken = cts.Token, Value = 5 };
80+
await _fixture.MMALCamera.TakePictureTimelapse(imgCaptureHandler, MMALEncoding.JPEG, MMALEncoding.I420, tl);
81+
82+
// Process all images captured into a video at 2fps.
83+
imgCaptureHandler.ImagesToVideo("/home/pi/videos/tests", 2);
84+
85+
_fixture.CheckAndAssertFilepath("/home/pi/videos/tests/out.avi");
86+
}
87+
});
88+
}
1089
}
1190
}

0 commit comments

Comments
 (0)