如何将 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 保存到磁盘?

ASP.NET C# - 在服务器上保存 FileStream

将音频流保存到磁盘的推荐方法

ffmpeg 将 rtsp 重新流式传输到 mjpeg

有效地将图像文件保存到磁盘 c#

C# 流总结