天天看点

http mjpeg 图像读取http mjpeg 图像格式抓包1、python opencv来读 http mjpeg2、java读取3、c++读取

http mjpeg 图像格式

网络摄像头的视频流解析直接使用通过http的Mjpeg是具有边界帧信息的multipart/x-mixed-replace,而jpeg数据只是以二进制形式发送。因此,实际上不需要关心HTTP协议标头。所有jpeg帧均以marker开头,0xff 0xd8并以0xff 0xd9 结尾。因此,代码可以从http流中提取此类帧,解码,编码h264 ,重新发送。

抓包

GET /stream/mjpeg HTTP/1.1

Host: 192.168.0.66:2401

Connection: keep-alive

Upgrade-Insecure-Requests: 1

User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.85 Safari/537.36

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,/;q=0.8,application/signed-exchange;v=b3;q=0.9

Accept-Encoding: gzip, deflate

Accept-Language: zh-CN,zh;q=0.9

HTTP/1.1 200 OK

Content-Type: multipart/x-mixed-replace;boundary=----WebKitFormBoundaryIZDrYHwuf2VJdpHw

Cache-Control: no-cache

------WebKitFormBoundaryIZDrYHwuf2VJdpHw

Content-Type: image/jpg

Content-Length: 61108

…JFIF…

1、python opencv来读 http mjpeg

以下使用python来读http mjpeg,因为python 方便快速,可以做原型方法的测试

import cv2
import requests
import numpy as np
 
r = requests.get('http://192.168.1.xx/mjpeg.cgi', auth=('user', 'password'), stream=True)
if(r.status_code == 200):
    bytes = bytes()
    for chunk in r.iter_content(chunk_size=1024):
        bytes += chunk
        a = bytes.find(b'\xff\xd8')
        b = bytes.find(b'\xff\xd9')
        if a != -1 and b != -1:
            jpg = bytes[a:b+2]
            bytes = bytes[b+2:]
            i = cv2.imdecode(np.fromstring(jpg, dtype=np.uint8), cv2.IMREAD_COLOR)
            cv2.imshow('i', i)
            if cv2.waitKey(1) == 27:
                exit(0)
else:
    print("Received unexpected status code {}".format(r.status_code))
           

2、java读取

java读取也是很方便的

package net.thistleshrub.mechwarfare.mjpeg;

import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.net.URLConnection;

import javax.imageio.ImageIO;


public class MjpegRunner implements Runnable
{
	private static final String CONTENT_LENGTH = "Content-length: ";
	private static final String CONTENT_TYPE = "Content-type: image/jpeg";
	private MJpegViewer viewer;
	private InputStream urlStream;
	private StringWriter stringWriter;
	private boolean processing = true;
	
	public MjpegRunner(MJpegViewer viewer, URL url) throws IOException
	{
		this.viewer = viewer;
		URLConnection urlConn = url.openConnection();
		// change the timeout to taste, I like 1 second
		urlConn.setReadTimeout(1000);
		urlConn.connect();
		urlStream = urlConn.getInputStream();
		stringWriter = new StringWriter(128);
	}

	public synchronized void stop()
	{
		processing = false;
	}
	

	@Override
	public void run()
	{
		while(processing)
		{
			try
			{
				byte[] imageBytes = retrieveNextImage();
				ByteArrayInputStream bais = new ByteArrayInputStream(imageBytes);
				
				BufferedImage image = ImageIO.read(bais);
				viewer.setBufferedImage(image);
				
				viewer.repaint();
			}catch(SocketTimeoutException ste){
				System.err.println("failed stream read: " + ste);
				viewer.setFailedString("Lost Camera connection: " + ste);
				viewer.repaint();
				stop();
			}catch(IOException e){
				System.err.println("failed stream read: " + e);
				stop();
			}
		}
		
		// close streams
		try
		{
			urlStream.close();
		}catch(IOException ioe){
			System.err.println("Failed to close the stream: " + ioe);
		}
	}
	
	/**
	 * Using the urlStream get the next JPEG image as a byte[]
	 * @return byte[] of the JPEG
	 * @throws IOException
	 */
	private byte[] retrieveNextImage() throws IOException
	{
		boolean haveHeader = false; 
		int currByte = -1;
		
		String header = null;
		// build headers
		// the DCS-930L stops it's headers
		while((currByte = urlStream.read()) > -1 && !haveHeader)
		{
			stringWriter.write(currByte);
			
			String tempString = stringWriter.toString(); 
			int indexOf = tempString.indexOf(CONTENT_TYPE);
			if(indexOf > 0)
			{
				haveHeader = true;
				header = tempString;
			}
		}		
		
		// 255 indicates the start of the jpeg image
		while((urlStream.read()) != 255)
		{
			// just skip extras
		}
		
		// rest is the buffer
		int contentLength = contentLength(header);
		byte[] imageBytes = new byte[contentLength + 1];
		// since we ate the original 255 , shove it back in
		imageBytes[0] = (byte)255;
		int offset = 1;
		int numRead = 0;
		while (offset < imageBytes.length
			&& (numRead=urlStream.read(imageBytes, offset, imageBytes.length-offset)) >= 0) 
		{
			offset += numRead;
		}       
		
		stringWriter = new StringWriter(128);
		
		return imageBytes;
	}

	// dirty but it works content-length parsing
	private static int contentLength(String header)
	{
		int indexOfContentLength = header.indexOf(CONTENT_LENGTH);
		int valueStartPos = indexOfContentLength + CONTENT_LENGTH.length();
		int indexOfEOL = header.indexOf('\n', indexOfContentLength);
		
		String lengthValStr = header.substring(valueStartPos, indexOfEOL).trim();
		
		int retValue = Integer.parseInt(lengthValStr);
		
		return retValue;
	}
}
           

3、c++读取

未完待续