如何将 MJPEG 流保存到磁盘(C# .NET)?
Posted
技术标签:
【中文标题】如何将 MJPEG 流保存到磁盘(C# .NET)?【英文标题】:How to save a MJPEG Stream to disk (C# .NET)? 【发布时间】:2010-12-07 16:21:07 【问题描述】:我有一个应用程序,它从相机 (MJPEG) 读取流并在表单上实时显示(在图片框中)。这是有效的。当用户单击“开始”按钮时,此流读取开始。
我想要做的是,当用户单击“停止”按钮时,“开始”和“停止”按钮之间的流将以 .mpg 格式保存在磁盘上。
现在,它在磁盘上写了一些东西,但我无法在 Windows Media Player 中打开它。
这是编写流的代码
private void ReadWriteStream(byte[] buffer, int start, int lenght, Stream writeStream)
Stream readStream = new MemoryStream(buffer, start, lenght);
int bytesRead = readStream.Read(buffer, 0, m_readSize);
// write the required bytes
while (bytesRead > 0 && !m_bStopLecture)
writeStream.Write(buffer, 0, bytesRead);
bytesRead = readStream.Read(buffer, 0, m_readSize);
readStream.Close();
这里是调用函数的地方。这是一个循环,正如我所说,视频正在 PictureBox 中播放。
// image at stop
Stream towrite = new MemoryStream(buffer, start, stop - start);
Image img = Image.FromStream(towrite);
imgSnapshot.Image = img;
// write to the stream
ReadWriteStream(buffer, start, stop - start, writeStream);
非常感谢!
【问题讨论】:
可能是因为您没有以正确的格式保存文件。所有文件都有文件格式,包括标题和描述文件的数据。如果您仅以字节为单位保存流的一部分,则可能会弄乱文件格式,并且在将其保存到磁盘时可能会丢失重要数据。这是我的猜测。 我试图将整个流从相机复制到文件中,但发生了同样的事情。还有其他想法吗? MJPEG 与 mpeg(或 mpg)不同。两种不同的编解码器。除非您想转码(==质量松散)为 mpeg,否则尽量保持为 mjpeg。如果需要包含它,最好使用 Quicktime 或 AVI,而不是 Mpeg。 【参考方案1】:您需要在流上设置内容类型,并包含帧边界数据。我将首先查看问题MJPG VLC and HTTP Streaming。
【讨论】:
【参考方案2】:有一个实现@https://net7mma.codeplex.com/SourceControl/latest具体https://net7mma.codeplex.com/SourceControl/latest#Rtsp/Server/Streams/MJPEGSourceStream.cs
类似这样的:
// buffer to read stream
byte[] buffer = new byte[bufSize];
// JPEG magic number
byte[] jpegMagic = new byte[] 0xFF, 0xD8, 0xFF ;
int jpegMagicLength = 3;
ASCIIEncoding encoding = new ASCIIEncoding();
while (!stopEvent.WaitOne(0, false))
// reset reload event
reloadEvent.Reset();
// HTTP web request
HttpWebRequest request = null;
// web responce
WebResponse response = null;
// stream for MJPEG downloading
Stream stream = null;
// boundary betweeen images (string and binary versions)
byte[] boundary = null;
string boudaryStr = null;
// length of boundary
int boundaryLen;
// flag signaling if boundary was checked or not
bool boundaryIsChecked = false;
// read amounts and positions
int read, todo = 0, total = 0, pos = 0, align = 1;
int start = 0, stop = 0;
// align
// 1 = searching for image start
// 2 = searching for image end
try
// create request
request = (HttpWebRequest)WebRequest.Create(m_Source);
// set user agent
if (userAgent != null)
request.UserAgent = userAgent;
// set proxy
if (proxy != null)
request.Proxy = proxy;
// set timeout value for the request
request.Timeout = requestTimeout;
// set login and password
if ((login != null) && (password != null) && (login != string.Empty))
request.Credentials = new NetworkCredential(login, password);
// set connection group name
if (useSeparateConnectionGroup)
request.ConnectionGroupName = GetHashCode().ToString();
// force basic authentication through extra headers if required
if (forceBasicAuthentication)
string authInfo = string.Format("0:1", login, password);
authInfo = Convert.ToBase64String(Encoding.Default.GetBytes(authInfo));
request.Headers["Authorization"] = "Basic " + authInfo;
// get response
response = request.GetResponse();
// check content type
string contentType = response.ContentType;
string[] contentTypeArray = contentType.Split('/');
// "application/octet-stream"
if ((contentTypeArray[0] == "application") && (contentTypeArray[1] == "octet-stream"))
boundaryLen = 0;
boundary = new byte[0];
else if ((contentTypeArray[0] == "multipart") && (contentType.Contains("mixed")))
// get boundary
int boundaryIndex = contentType.IndexOf("boundary", 0);
if (boundaryIndex != -1)
boundaryIndex = contentType.IndexOf("=", boundaryIndex + 8);
if (boundaryIndex == -1)
// try same scenario as with octet-stream, i.e. without boundaries
boundaryLen = 0;
boundary = new byte[0];
else
boudaryStr = contentType.Substring(boundaryIndex + 1);
// remove spaces and double quotes, which may be added by some IP cameras
boudaryStr = boudaryStr.Trim(' ', '"');
boundary = encoding.GetBytes(boudaryStr);
boundaryLen = boundary.Length;
boundaryIsChecked = false;
else
throw new Exception("Invalid content type.");
// get response stream
stream = response.GetResponseStream();
stream.ReadTimeout = requestTimeout;
// loop
while ((!stopEvent.WaitOne(0, false)) && (!reloadEvent.WaitOne(0, false)))
// check total read
if (total > bufSize - readSize)
total = pos = todo = 0;
// read next portion from stream
if ((read = stream.Read(buffer, total, readSize)) == 0)
throw new ApplicationException();
total += read;
todo += read;
// increment received bytes counter
bytesReceived += read;
// do we need to check boundary ?
if ((boundaryLen != 0) && (!boundaryIsChecked))
// some IP cameras, like AirLink, claim that boundary is "myboundary",
// when it is really "--myboundary". this needs to be corrected.
pos = Utility.ContainsBytes(buffer, ref start, ref read, boundary, 0, boundary.Length);
// continue reading if boudary was not found
if (pos == -1)
continue;
for (int i = pos - 1; i >= 0; i--)
byte ch = buffer[i];
if ((ch == (byte)'\n') || (ch == (byte)'\r'))
break;
boudaryStr = (char)ch + boudaryStr;
boundary = encoding.GetBytes(boudaryStr);
boundaryLen = boundary.Length;
boundaryIsChecked = true;
// search for image start
if ((align == 1) && (todo >= jpegMagicLength))
start = Utility.ContainsBytes(buffer, ref pos, ref todo, jpegMagic, 0, jpegMagicLength);
if (start != -1)
// found JPEG start
pos = start + jpegMagicLength;
todo = total - pos;
align = 2;
else
// delimiter not found
todo = jpegMagicLength - 1;
pos = total - todo;
// search for image end ( boundaryLen can be 0, so need extra check )
while ((align == 2) && (todo != 0) && (todo >= boundaryLen))
stop = Utility.ContainsBytes(buffer, ref start, ref read,
(boundaryLen != 0) ? boundary : jpegMagic,
pos, todo);
if (stop != -1)
pos = stop;
todo = total - pos;
// increment frames counter
framesReceived++;
// image at stop
using (Bitmap bitmap = (Bitmap)Bitmap.FromStream(new MemoryStream(buffer, start, stop - start)))
// notify client
Packetize(bitmap);
// shift array
pos = stop + boundaryLen;
todo = total - pos;
Array.Copy(buffer, pos, buffer, 0, todo);
total = todo;
pos = 0;
align = 1;
else
// boundary not found
if (boundaryLen != 0)
todo = boundaryLen - 1;
pos = total - todo;
else
todo = 0;
pos = total;
catch (ApplicationException)
// do nothing for Application Exception, which we raised on our own
// wait for a while before the next try
Thread.Sleep(250);
catch (ThreadAbortException)
break;
catch (Exception exception)
// wait for a while before the next try
Thread.Sleep(250);
finally
// abort request
if (request != null)
request.Abort();
request = null;
// close response stream
if (stream != null)
stream.Close();
stream = null;
// close response
if (response != null)
response.Close();
response = null;
// need to stop ?
if (stopEvent.WaitOne(0, false))
break;
【讨论】:
以上是关于如何将 MJPEG 流保存到磁盘(C# .NET)?的主要内容,如果未能解决你的问题,请参考以下文章
如何在张量流中将 TextVectorization 保存到磁盘?