-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
acaf8c4
commit 94329bc
Showing
11 changed files
with
433 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
<?xml version="1.0" encoding="utf-8" ?> | ||
<configuration> | ||
<startup> | ||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" /> | ||
</startup> | ||
</configuration> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
using System; | ||
using System.Diagnostics; | ||
using System.IO; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
|
||
namespace StreamLapse | ||
{ | ||
public class FFMPEG | ||
{ | ||
public static bool CaptureStreamFrame(string streamURL, string filenameFullPath) | ||
{ | ||
Console.WriteLine("\nRequesting new frame..."); | ||
|
||
using (Process p = new Process()) | ||
{ | ||
p.StartInfo.UseShellExecute = false; | ||
p.StartInfo.CreateNoWindow = true; | ||
p.StartInfo.RedirectStandardOutput = true; | ||
p.StartInfo.RedirectStandardError = true; | ||
p.StartInfo.FileName = "ffmpeg.exe"; | ||
p.StartInfo.Arguments = $"-rtsp_transport tcp -i {streamURL} -vframes 1 {filenameFullPath}"; | ||
p.Start(); | ||
p.WaitForExit(); | ||
|
||
return File.Exists(filenameFullPath); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
using CommandLine; | ||
|
||
namespace StreamLapse | ||
{ | ||
public class Options | ||
{ | ||
[Option(Required = true, HelpText = "RTSP url to your IP camera.")] | ||
public string Stream { get; set; } | ||
|
||
[Option("interval", Default = 5000, HelpText = "Time im milliseconds to wait before save another frame.")] | ||
public int CaptureInterval { get; set; } | ||
|
||
[Option(HelpText = "Path to store the frames, will be created if not exists.")] | ||
public string Output { get; set; } | ||
|
||
[Option(Default = "IMG_", HelpText = "Prefix to use with filename, default results in IMG_1, IMG_2")] | ||
public string Prefix { get; set; } | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
using CommandLine; | ||
using System; | ||
using System.IO; | ||
|
||
namespace StreamLapse | ||
{ | ||
internal class Program | ||
{ | ||
static void Main(string[] args) | ||
{ | ||
Console.Title = "..:: StreamLapse ::.."; | ||
|
||
try | ||
{ | ||
Parser.Default.ParseArguments<Options>(args).WithParsed(RunOptions); | ||
} | ||
|
||
catch (Exception ex) when (ex is FormatException || ex is IOException) | ||
{ | ||
Console.WriteLine(ex.Message); | ||
} | ||
} | ||
|
||
static void RunOptions(Options options) | ||
{ | ||
if (!Validation.IsValidRtspUrl(options.Stream)) throw new FormatException("Stream url is not valid."); | ||
|
||
StreamHandler streamHandler = new StreamHandler(options.Stream); | ||
|
||
streamHandler.SetCaptureInterval(options.CaptureInterval); | ||
|
||
streamHandler.SetOutputDirectory(options.Output); | ||
|
||
streamHandler.SetImagePrefix(options.Prefix); | ||
|
||
while (true) streamHandler.SaveFrameToJPGFile(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
using System.Reflection; | ||
using System.Runtime.InteropServices; | ||
|
||
// General Information about an assembly is controlled through the following | ||
// set of attributes. Change these attribute values to modify the information | ||
// associated with an assembly. | ||
[assembly: AssemblyTitle("StreamLapse")] | ||
[assembly: AssemblyDescription("Connect to your IP camera and create a timelapse defining an interval between each frame capture.")] | ||
[assembly: AssemblyConfiguration("")] | ||
[assembly: AssemblyCompany("fellypsantos2011@gmail.com")] | ||
[assembly: AssemblyProduct("StreamLapse")] | ||
[assembly: AssemblyCopyright("Copyright © 2023")] | ||
[assembly: AssemblyTrademark("")] | ||
[assembly: AssemblyCulture("")] | ||
|
||
// Setting ComVisible to false makes the types in this assembly not visible | ||
// to COM components. If you need to access a type in this assembly from | ||
// COM, set the ComVisible attribute to true on that type. | ||
[assembly: ComVisible(false)] | ||
|
||
// The following GUID is for the ID of the typelib if this project is exposed to COM | ||
[assembly: Guid("e2b26aa8-f6c8-4182-b18e-d3a5e8d325f5")] | ||
|
||
// Version information for an assembly consists of the following four values: | ||
// | ||
// Major Version | ||
// Minor Version | ||
// Build Number | ||
// Revision | ||
// | ||
// You can specify all the values or you can default the Build and Revision Numbers | ||
// by using the '*' as shown below: | ||
// [assembly: AssemblyVersion("1.0.*")] | ||
[assembly: AssemblyVersion("1.0.0.0")] | ||
[assembly: AssemblyFileVersion("1.0.0.0")] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
using System; | ||
using System.Diagnostics; | ||
using System.IO; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
|
||
namespace StreamLapse | ||
{ | ||
public class StreamHandler | ||
{ | ||
private readonly string _streamURL; | ||
private string _outputDir; | ||
private string _prefixImageName; | ||
private int _frameCount; | ||
private int _captureIntervalInMilliseconds; | ||
private long _totalBytesWrited; | ||
|
||
/// <summary> | ||
/// Handle connection to a camera stream. | ||
/// </summary> | ||
/// <param name="rtsp">Real Time Streaming Protocol something like this: rtsp://user:pass@192.168.0.5/live/mpeg4</param> | ||
public StreamHandler(string rtsp) | ||
{ | ||
_streamURL = rtsp; | ||
_frameCount = 0; | ||
_totalBytesWrited = 0; | ||
} | ||
|
||
/// <summary> | ||
/// Setup the capture interval to wait before save each frame. | ||
/// </summary> | ||
/// <param name="captureInverval">Time to wait before save another frame.</param> | ||
public void SetCaptureInterval(int captureInverval) | ||
{ | ||
_captureIntervalInMilliseconds = captureInverval; | ||
} | ||
|
||
/// <summary> | ||
/// Folder to store the saved images. | ||
/// </summary> | ||
/// <param name="outputDirectory">Path to store the frames, will be created if not exists.</param> | ||
public void SetOutputDirectory(string outputDirectory) | ||
{ | ||
_outputDir = outputDirectory; | ||
|
||
if (string.IsNullOrEmpty(outputDirectory)) return; | ||
|
||
if (!Directory.Exists(_outputDir)) Directory.CreateDirectory(_outputDir); | ||
} | ||
|
||
/// <summary> | ||
/// Set prefix to be used as filename with frame count eg IMG_1 | ||
/// </summary> | ||
/// <param name="prefix">The image prefix</param> | ||
public void SetImagePrefix(string prefix) | ||
{ | ||
_prefixImageName = prefix; | ||
} | ||
|
||
/// <summary> | ||
/// Takes the filename and concat with output directory to create the full exporting path. | ||
/// </summary> | ||
/// <param name="filename">Filename to merge with output path</param> | ||
/// <returns>Full path to store the image.</returns> | ||
private string GenerateImageNameOutputPath(string filename) | ||
{ | ||
if (string.IsNullOrEmpty(_outputDir)) return filename; | ||
|
||
return Path.Combine(_outputDir, filename); | ||
} | ||
|
||
/// <summary> | ||
/// Connects to stream and save current frame to file. | ||
/// </summary> | ||
/// <exception cref="IOException">In case of the image already exists.</exception> | ||
public void SaveFrameToJPGFile() | ||
{ | ||
Stopwatch stopwatch = Stopwatch.StartNew(); | ||
|
||
string filename = $"{_prefixImageName}{++_frameCount}.jpg"; | ||
|
||
string filenameFullPath = GenerateImageNameOutputPath(filename); | ||
|
||
if (File.Exists(filenameFullPath)) throw new IOException($"Failed to save image {filenameFullPath} because it already exists."); | ||
|
||
stopwatch.Start(); | ||
|
||
bool frameWasSaved = false; | ||
|
||
while(!frameWasSaved) | ||
{ | ||
frameWasSaved = FFMPEG.CaptureStreamFrame(_streamURL, filenameFullPath); | ||
|
||
if (!frameWasSaved) Console.WriteLine($"Failed to save the frame: {filename}. Retrying..."); | ||
} | ||
|
||
stopwatch.Stop(); | ||
|
||
RegisterFileSizeInBytes(filenameFullPath); | ||
|
||
double timeSpentToSaveFile = TimeSpan.FromMilliseconds(stopwatch.ElapsedMilliseconds).TotalSeconds; | ||
|
||
PrintStatus(filenameFullPath, timeSpentToSaveFile); | ||
|
||
Thread.Sleep(_captureIntervalInMilliseconds); | ||
} | ||
|
||
/// <summary> | ||
/// Accumulate the bytes of all images writed to show while processing. | ||
/// </summary> | ||
/// <param name="filename">File to get size and add to previews bytes.</param> | ||
private void RegisterFileSizeInBytes(string filename) | ||
{ | ||
FileInfo fileInfo = new FileInfo(filename); | ||
|
||
_totalBytesWrited += fileInfo.Length; | ||
} | ||
|
||
/// <summary> | ||
/// Show the main text on screen updating the information on each connection. | ||
/// </summary> | ||
/// <param name="filename">Last file saved.</param> | ||
/// <param name="timeSpent">Time spent in milliseconds to save the last file.</param> | ||
private void PrintStatus(string filename, double timeSpent) | ||
{ | ||
Console.Clear(); | ||
|
||
Console.WriteLine("..:: StreamLapse ::..\n\n"); | ||
|
||
Console.WriteLine("See the world from different perspective.\n"); | ||
|
||
Console.WriteLine("created by\n"); | ||
|
||
Console.WriteLine("fellypsantos2011@gmail.com\n\n\n"); | ||
|
||
Console.WriteLine($"File {filename} saved in {timeSpent} seconds.\n"); | ||
|
||
Console.WriteLine("Total frames saved at now: " + _frameCount + '\n'); | ||
|
||
Console.WriteLine("Total filesize of images: " + FormatFileSize(_totalBytesWrited)); | ||
} | ||
|
||
/// <summary> | ||
/// Convert bytes to a more user friendly metric like Bytes, KiloBytes, MegaBytes etc. | ||
/// </summary> | ||
/// <param name="fileSize">File size in bytes to be converted.</param> | ||
/// <returns>Converted unit with two decimal places.</returns> | ||
private string FormatFileSize(long fileSize) | ||
{ | ||
string[] sizeUnits = { "B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" }; | ||
|
||
int unitIndex = 0; | ||
double size = fileSize; | ||
|
||
while (size >= 1024 && unitIndex < sizeUnits.Length - 1) | ||
{ | ||
size /= 1024; | ||
unitIndex++; | ||
} | ||
|
||
return $"{size:0.##} {sizeUnits[unitIndex]}"; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | ||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> | ||
<PropertyGroup> | ||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> | ||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> | ||
<ProjectGuid>{E2B26AA8-F6C8-4182-B18E-D3A5E8D325F5}</ProjectGuid> | ||
<OutputType>Exe</OutputType> | ||
<RootNamespace>StreamLapse</RootNamespace> | ||
<AssemblyName>StreamLapse</AssemblyName> | ||
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion> | ||
<FileAlignment>512</FileAlignment> | ||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects> | ||
<Deterministic>true</Deterministic> | ||
<IsWebBootstrapper>false</IsWebBootstrapper> | ||
<PublishUrl>publish\</PublishUrl> | ||
<Install>true</Install> | ||
<InstallFrom>Disk</InstallFrom> | ||
<UpdateEnabled>false</UpdateEnabled> | ||
<UpdateMode>Foreground</UpdateMode> | ||
<UpdateInterval>7</UpdateInterval> | ||
<UpdateIntervalUnits>Days</UpdateIntervalUnits> | ||
<UpdatePeriodically>false</UpdatePeriodically> | ||
<UpdateRequired>false</UpdateRequired> | ||
<MapFileExtensions>true</MapFileExtensions> | ||
<ApplicationRevision>0</ApplicationRevision> | ||
<ApplicationVersion>1.0.0.%2a</ApplicationVersion> | ||
<UseApplicationTrust>false</UseApplicationTrust> | ||
<BootstrapperEnabled>true</BootstrapperEnabled> | ||
</PropertyGroup> | ||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> | ||
<PlatformTarget>AnyCPU</PlatformTarget> | ||
<DebugSymbols>true</DebugSymbols> | ||
<DebugType>full</DebugType> | ||
<Optimize>false</Optimize> | ||
<OutputPath>bin\Debug\</OutputPath> | ||
<DefineConstants>DEBUG;TRACE</DefineConstants> | ||
<ErrorReport>prompt</ErrorReport> | ||
<WarningLevel>4</WarningLevel> | ||
</PropertyGroup> | ||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> | ||
<PlatformTarget>AnyCPU</PlatformTarget> | ||
<DebugType>pdbonly</DebugType> | ||
<Optimize>true</Optimize> | ||
<OutputPath>bin\Release\</OutputPath> | ||
<DefineConstants>TRACE</DefineConstants> | ||
<ErrorReport>prompt</ErrorReport> | ||
<WarningLevel>4</WarningLevel> | ||
</PropertyGroup> | ||
<PropertyGroup> | ||
<ApplicationIcon>time-lapse.ico</ApplicationIcon> | ||
</PropertyGroup> | ||
<ItemGroup> | ||
<Reference Include="CommandLine, Version=2.9.1.0, Culture=neutral, PublicKeyToken=5a870481e358d379, processorArchitecture=MSIL"> | ||
<HintPath>packages\CommandLineParser.2.9.1\lib\net461\CommandLine.dll</HintPath> | ||
</Reference> | ||
<Reference Include="System" /> | ||
<Reference Include="System.Core" /> | ||
<Reference Include="System.Numerics" /> | ||
<Reference Include="System.Xml.Linq" /> | ||
<Reference Include="System.Data.DataSetExtensions" /> | ||
<Reference Include="Microsoft.CSharp" /> | ||
<Reference Include="System.Data" /> | ||
<Reference Include="System.Net.Http" /> | ||
<Reference Include="System.Xml" /> | ||
</ItemGroup> | ||
<ItemGroup> | ||
<Compile Include="FFMPEG.cs" /> | ||
<Compile Include="Options.cs" /> | ||
<Compile Include="Program.cs" /> | ||
<Compile Include="Properties\AssemblyInfo.cs" /> | ||
<Compile Include="StreamHandler.cs" /> | ||
<Compile Include="Validation.cs" /> | ||
</ItemGroup> | ||
<ItemGroup> | ||
<None Include="App.config" /> | ||
<None Include="packages.config" /> | ||
</ItemGroup> | ||
<ItemGroup> | ||
<Content Include="time-lapse.ico" /> | ||
</ItemGroup> | ||
<ItemGroup> | ||
<BootstrapperPackage Include=".NETFramework,Version=v4.7.2"> | ||
<Visible>False</Visible> | ||
<ProductName>Microsoft .NET Framework 4.7.2 %28x86 and x64%29</ProductName> | ||
<Install>true</Install> | ||
</BootstrapperPackage> | ||
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1"> | ||
<Visible>False</Visible> | ||
<ProductName>.NET Framework 3.5 SP1</ProductName> | ||
<Install>false</Install> | ||
</BootstrapperPackage> | ||
</ItemGroup> | ||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> | ||
</Project> |
Oops, something went wrong.