http mjpeg 图像读取

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++读取

以下这段代码我在其他文章里面写过,现在把他加上,省的其他人找了

void capture_start(s_param *param)
{
	//开始视频采集
	if (param != NULL)
	{
		asio::io_context io_service;
		tcp::resolver resolver(io_service);
		tcp::resolver::query query(param->host, param->port);
		tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
		tcp::resolver::iterator end;
		tcp::socket socket(io_service);
		//socket.io_control(asio::ip::tcp::socket::non_blocking(true));
		std::error_code error = asio::error::host_not_found;
		while (error && endpoint_iterator != end) {
			socket.close();
			//int timeout = 3000;
			//int nRet = setsockopt(socket.native_handle(), SOL_SOCKET, SO_CONNECT_TIME, (const char*)&timeout, sizeof(timeout));
			socket.connect(*endpoint_iterator++, error);
		}
		if (error) {
			cout << "can not connect the camera!" << endl;
			return;
			//throw asio::system_error(error);
		}

		// Form the request. We specify the "Connection: close" header so that the
		// server will close the socket after transmitting the response. This will
		// allow us to treat all data up until the EOF as the content.
		asio::streambuf request;
		ostream request_stream(&request);
		request_stream << "GET " <<param->route << " HTTP/1.0\r\n";
		request_stream << "Host: " << param->host << "\r\n";
		request_stream << "Accept: */*\r\n";
		request_stream << "Connection: close\r\n\r\n";

		// Send the request.
		asio::write(socket, request);

		// Read the response status line. The response streambuf will automatically
		// grow to accommodate the entire line. The growth may be limited by passing
		// a maximum size to the streambuf constructor.
		asio::streambuf response;
		asio::read_until(socket, response, "\r\n");

		// Check that response is OK.
		istream response_stream(&response);
		string http_version;
		response_stream >> http_version;
		unsigned int status_code;
		response_stream >> status_code;
		string status_message;
		getline(response_stream, status_message);
		if (!response_stream || http_version.substr(0, 5) != "HTTP/") {
			cout << "Invalid response\n";
			return;
		}
		if (status_code != 200) {
			cout << "Response returned with status code " << status_code << "\n";
			return;
		}

		// Read the response headers, which are terminated by a blank line.
		asio::read_until(socket, response, "\r\n\r\n");

		// Get the MIME multipart boundary from the headers.
		regex rx_content_type("Content-Type:.*boundary=(.*)");
		regex rx_content_length("Content-Length: (.*)");

		smatch match;
		string header;
		string boundary;

		while (getline(response_stream, header) && header != "\r") {
			//cout << "HTTP HEADER: " << header << endl;
			if (regex_search(header, match, rx_content_type)) {
				boundary = match[1];
				//cout << "BOUNDARY SELECTED: " << boundary << endl;
			}
		}

		// Abort if a boundary was not found.
		if (boundary == "") {
			cout << "Not a MJPEG stream" << endl;
			return;
		}


		std::vector<uint8_t> buff;
		while (1) {
			asio::read_until(socket, response, boundary);
			while (getline(response_stream, header)) {
				if (header.find(boundary + "\r") != string::npos)
				{
					break;
				}
			}
			uint32_t content_length;
			while (getline(response_stream, header) && header != "\r") {
				if (regex_search(header, match, rx_content_length)) {
					std::ssub_match base_sub_match = match[1];
					content_length = std::atoi(base_sub_match.str().c_str());
				}
			}

			if (response.size() < content_length) {
				asio::read(socket, response, asio::transfer_at_least(
					content_length - response.size()));
			}
			if (buff.size() < content_length) {
				buff.resize(content_length);
			}

			response.sgetn((char*)&buff[0], content_length);
			cv::Mat mat(buff);
			cv::Mat dec = imdecode(Mat(buff), IMREAD_COLOR);
			v_mut_src.lock();
			cv::resize(dec, v_srcImg, cv::Size(CANVAS_WIDTH, CANVAS_HEIGHT));
			v_mut_src.unlock();
			//cout << "RESPONSE SIZE, BEFORE JPEG CONSUME: " << response.size() << endl;
			cv::imshow("windows", v_srcImg);
			cv::waitKey(2);
			response.consume(content_length);
		}
	}
	
	std::cout << "capture break...." << std::endl;
}

你可能感兴趣的:(音视频和c++,java,物联网,网络,http协议,mjpeg,java,python)