视频流海康rtsp流合并字幕显示储存

1,现在要做个win10工控机上用rtsp流访问海康摄像机的h264流,工控机有串口输入一些文本信息,需要和264流合并显示在工控机的屏幕上,并且可以保存视频文字流,30分钟一个文件。。各位大拿提供解决方案.vlc二次开发,ffmpeg,等等,越方便越好

基于Monster 组和GPT的调写:

1.
通过ffmpeg获取rtsp流
使用ffmpeg可以轻松地获取海康摄像机的rtsp流。您可以使用以下命令从rtsp URL获取h264流:

ffmpeg -i rtsp://[username]:[password]@[IP]:[port]/[channel] -vcodec copy -an -f h264 [output_file].h264


其中,[username]和[password]是海康摄像机的用户名和密码,[IP]是摄像机的IP地址,[port]是rtsp端口号,[channel]是您要使用的摄像机的通道号。[output_file]是您要保存的文件名。

  • 2.将串口输入文本信息合并到h264流中
    用ffmpeg的“drawtext”过滤器将串口输入的文本信息合并到h264流中。以下是一个示例命令:
ffmpeg -i [input_file].h264 -vf "drawtext=text='[text]':fontsize=20:fontcolor=white:x=10:y=10" -codec copy [output_file].h264


3.其中,[input_file]是您之前从rtsp流中提取的h264流文件名,[text]是您要合并的文本信息。此命令将在左上角添加一个白色文本框,显示您输入的文本信息。

保存视频和文字流
用ffmpeg的“segment”和“mux”功能将视频和文字流分段并保存为文件。以下是一个示例命令:

ffmpeg -i [input_file].h264 -vf "drawtext=text='[text]':fontsize=20:fontcolor=white:x=10:y=10" -segment_time 1800 -f segment -segment_format mp4 -reset_timestamps 1 -strftime 1 [output_file]_%Y-%m-%d_%H-%M-%S.mp4 -map 0 -c copy -f segment -segment_time 1800 -reset_timestamps 1 -strftime 1 [output_file]_%Y-%m-%d_%H-%M-%S.txt


此命令将视频和文字流分段并保存为每个30分钟一个文件的mp4和txt文件。可以将[output_file]替换为您想要的文件名。

这些命令可以结合在一起,以便可以一次运行所有步骤。

5.用FFmpeg和OpenCV这两个库来处理视频流和字幕。

#include <iostream>
#include <string>
#include <sstream>
#include <chrono>
#include <ctime>
#include <iomanip>
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/core/core.hpp>
extern "C"
{
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>
#include <libavutil/opt.h>
#include <libavutil/time.h>
}

using namespace std;
using namespace cv;

int main()
{
    // Initialize FFmpeg
    av_register_all();
    avcodec_register_all();
    avformat_network_init();

    // Input RTSP URL and the text message from serial port
    string rtsp_url = "rtsp://username:password@192.168.0.1:554/stream1";
    string text_message = "Hello, world!";

    // Open the RTSP stream
    AVFormatContext* input_context = NULL;
    if (avformat_open_input(&input_context, rtsp_url.c_str(), NULL, NULL) < 0)
    {
        cout << "Failed to open the RTSP stream." << endl;
        return -1;
    }

    // Find the video stream information
    if (avformat_find_stream_info(input_context, NULL) < 0)
    {
        cout << "Failed to find the video stream information." << endl;
        return -1;
    }

    // Find the video stream
    int video_stream_index = -1;
    AVCodecParameters* video_codec_parameters = NULL;
    AVCodec* video_codec = NULL;
    for (int i = 0; i < input_context->nb_streams; i++)
    {
        if (input_context->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
        {
            video_stream_index = i;
            video_codec_parameters = input_context->streams[i]->codecpar;
            video_codec = avcodec_find_decoder(video_codec_parameters->codec_id);
            break;
        }
    }

    // Open the video codec
    AVCodecContext* video_codec_context = avcodec_alloc_context3(video_codec);
    avcodec_parameters_to_context(video_codec_context, video_codec_parameters);
    if (avcodec_open2(video_codec_context, video_codec, NULL) < 0)
    {
        cout << "Failed to open the video codec." << endl;
        return -1;
    }

    // Allocate the frame and packet
    AVFrame* frame = av_frame_alloc();
    AVPacket* packet = av_packet_alloc();

    // Create the window for display
    namedWindow("Video", WINDOW_NORMAL);

    // Create the font and text message
    int font_face = FONT_HERSHEY_SIMPLEX;
    double font_scale = 1.5;
    int thickness = 2;
    int baseline = 0;
    Size text_size = getTextSize(text_message, font_face, font_scale, thickness, &baseline);
    Point text_origin((text_size.width / 2), (text_size.height / 2));
    Scalar text_color(255, 255, 255);
    Mat text_image(text_size.height * 2, text_size.width * 2, CV_8UC3, Scalar(0,0, 0));

// Create the video writer
string video_filename;
time_t current_time = time(NULL);
struct tm* time_info = localtime(&current_time);
char time_str[80];
strftime(time_str, sizeof(time_str), "%Y-%m-%d_%H-%M-%S", time_info);
video_filename = "output_" + string(time_str) + ".avi";
int fourcc = VideoWriter::fourcc('M', 'J', 'P', 'G');
double fps = 30.0;
Size video_size(video_codec_context->width, video_codec_context->height + text_size.height);
VideoWriter video_writer(video_filename, fourcc, fps, video_size, true);

// Start the loop for reading frames
while (av_read_frame(input_context, packet) >= 0)
{
    // Check if the packet is for video stream
    if (packet->stream_index == video_stream_index)
    {
        // Decode the video packet
        int ret = avcodec_send_packet(video_codec_context, packet);
        if (ret < 0)
        {
            cout << "Error decoding video packet." << endl;
            continue;
        }

        while (ret >= 0)
        {
            ret = avcodec_receive_frame(video_codec_context, frame);
            if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
            {
                break;
            }
            else if (ret < 0)
            {
                cout << "Error decoding video frame." << endl;
                continue;
            }

            // Convert the video frame to OpenCV Mat
            Mat video_image(frame->height, frame->width, CV_8UC3, frame->data[0], frame->linesize[0]);
            cvtColor(video_image, video_image, COLOR_YUV2BGR);

            // Add the text message to the image
            putText(text_image, text_message, text_origin, font_face, font_scale, text_color, thickness);

            // Create the combined image
            Mat combined_image(video_size.height, video_size.width, CV_8UC3);
            Mat roi(combined_image, Rect(0, 0, video_codec_context->width, video_codec_context->height));
            video_image.copyTo(roi);
            roi = Mat(combined_image, Rect(0, video_codec_context->height, text_size.width, text_size.height));
            text_image(Rect(0, 0, text_size.width, text_size.height)).copyTo(roi);

            // Display the combined image
            imshow("Video", combined_image);

            // Write the combined image to video file
            video_writer.write(combined_image);

            // Release the frame
            av_frame_unref(frame);
        }
    }

    // Release the packet
    av_packet_unref(packet);

    // Wait for a key press or a timeout
    if (waitKey(1) >= 0)
    {
        break;
    }
}

// Release the resources
av_packet_free(&packet);
av_frame_free(&frame);
avcodec_free_context(&video_codec_context);
avformat_close_input(&input_context);
avformat_network_deinit();
destroyAllWindows();

return 0;


这个示例代码会打开RTSP流,并在视频上方显示一个文本消息。它使用OpenCV显示视频和文本消息,使用FFmpeg保存视频。输出视频文件名是带有当前日期和时间的“output_yyyy-mm-dd_HH-MM-SS.avi”格式。它还可以用串口输入一个文本消息来更新文本消息。

  1. 关于播放视频的程序,
  2. 1 假设你还没有播放视频程序,那么从SDK官网找一下示例
  3. 2 假设播放视频的程序你有代码,会修改,那么在绘制视频的一帧图像后,绘制文字即可。
  4. 3 假设播放视频的程序已有并且你改不了,那么你就另外开发一个程序,窗口覆盖在视频上面,显示透明背景文字即可。
  5. 关于串口读文字,你肯定已经做过,有代码的吧,没有的话网上找示例。

谢谢Monster-XH给的方案,但是还有个问题,就是每30分钟存一个文件,是视频流文字流合起来的视频,需要用ffmpeg,opencv的方案,谁解决,再赏15元

大致思路如下,供你参考:
1、确保海康摄像机支持RTSP流和H264编码【可以查看设备文档来确认这一点】
2、安装并配置海康工控机软件【提供访问摄像机和通信的API接口】
3、安装并配置RTSP服务器【可以使用命令行或者web浏览器等方式访问RTSP服务器,确认是否允许发送和接收视频流】
4、将海康摄像机连接到工控机【通过串口连接】
5、配置RTSP服务器以允许视频流和文本流同时传输,可以参照下面命令:

ffmpeg -re -i <input_stream_url> -c:v libx264 -c:a aac -b:a 128k -f flv rtsp://<rtsp_server_url>
【<input_stream_url> 是输入流的RTSP地址,-re参数启用回调,-c:v libx264 指定使用H264编码器,-c:a aac 指定使用AAC编码器,-b:a 128k 指定分辨率为128kbps,-f flv 指定输出为FLV格式,rtsp://<rtsp_server_url> 是RTSP服务器的URL】

6、设置海康摄像机以保存每30分钟的视频文件,可以参考下面命令:

ffmpeg -re -i <input_stream_url> -c:v libx264 -c:a aac -b:a 128k -f flv rtsp://<rtsp_server_url> -ts 30 -c:s 640x480 -pix_fmt yuv420p <output_file_path>
【<input_stream_url> 是输入流的RTSP地址,-re参数启用回调,-c:v libx264 指定使用H264编码器,-c:a aac 指定使用AAC编码器,-b:a 128k 指定分辨率为128kbps,-f flv 指定输出为FLV格式,-ts 30 表示30分钟间隔保存,-c:s 640x480 指定输出分辨率为640x480,-pix_fmt yuv420p 指定输出格式为YUV420P,<output_file_path> 是输出文件的路径】

引用chatGPT作答,以下是一份示例代码,用于连接海康摄像机的RTSP流并从串口接收文本信息,然后将文本信息叠加到视频流上并保存为视频文件。请注意,该代码只是一个示例,您需要根据您的实际需求进行修改和优化。

import cv2
import serial
import time

# 打开串口并配置
ser = serial.Serial('COM1', 9600, timeout=0.5)

# 连接海康摄像机的RTSP流
url = 'rtsp://username:password@192.168.1.100:554/streaming/channels/1'
cap = cv2.VideoCapture(url)

# 获取视频帧的宽度和高度
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))

# 设置保存视频的参数
fourcc = cv2.VideoWriter_fourcc(*'XVID')
filename = time.strftime('%Y-%m-%d_%H-%M-%S.avi')
out = cv2.VideoWriter(filename, fourcc, 20.0, (width, height))

# 读取串口数据并将其叠加到视频帧上
while True:
    ret, frame = cap.read()
    if ret:
        # 从串口读取文本信息
        text = ser.readline().decode().strip()
        
        # 在视频帧上叠加文本信息
        cv2.putText(frame, text, (50, 50), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
        
        # 显示视频帧
        cv2.imshow('frame', frame)
        
        # 保存视频帧
        out.write(frame)
        
        # 按下q键退出循环
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break
    else:
        break

# 释放资源
cap.release()
out.release()
cv2.destroyAllWindows()
ser.close()

在上面的代码中,您需要将“COM1”替换为您实际使用的串口号,并将“username”和“password”替换为您海康摄像机的用户名和密码。您还需要根据实际情况修改视频帧的保存参数。在代码运行期间,您可以通过在串口中输入文本信息来测试程序是否正常工作。按下“q”键可以退出循环并停止保存视频文件。

基于new bing 的参考:
可以将视频流和文字流合并到一起,并使用FFmpeg进行处理。

具体方案如下:

  • 使用OpenCV读取海康摄像机的H.264码流,并从串口读取文本信息。
  • 将文本信息添加到视频帧上,形成带有文字的视频帧。
  • 使用FFmpeg将带有文字的视频帧编码成视频文件。
  • 通过设置FFmpeg的时间戳参数,每30分钟生成一个新的视频文件。

需要注意的是,为了保证每个视频文件的完整性,建议在生成新文件前等待一段时间,以确保当前视频文件中的所有帧都被处理完毕。

以下是示例代码实现:

import cv2
import subprocess
import datetime

# 设置视频宽高和帧率
width = 640
height = 480
fps = 25

# 打开海康摄像机的h264流
cap = cv2.VideoCapture("rtsp://your_ip_address/h264_stream")

# 打开串口,并读取文本信息
ser = serial.Serial(port='/dev/ttyUSB0', baudrate=9600, timeout=1)

# 设置当前时间,并计算出下一个时间片
now_time = datetime.datetime.now()
next_time = now_time + datetime.timedelta(minutes=30)

# 定义 FFmpeg 相关参数
command = ['ffmpeg',
           '-y',
           '-f', 'rawvideo',
           '-pixel_format', 'bgr24',
           '-video_size', f'{width}x{height}',
           '-framerate', str(fps),
           '-i', '-',
           '-c:v', 'libx264',
           '-crf', '23',
           '-preset', 'ultrafast',
           '-tune', 'zerolatency',
           '-pix_fmt', 'yuv420p',
           '-movflags', 'frag_keyframe+empty_moov',
           '-metadata', f'creation_time={now_time.strftime("%Y-%m-%d %H:%M:%S")}',
           '-metadata', 'encoder="built on binjie09\'s AI assistant"',
           '-muxdelay', '0.1',
           '-segment_time', '1800',
           '-reset_timestamps', '1',
           '-strftime', '1',
           'output_%Y%m%d_%H%M%S.mp4']
process = subprocess.Popen(command, stdin=subprocess.PIPE)

while True:
    # 读取视频帧
    ret, frame = cap.read()
    if not ret:
        break
    
    # 从串口读取文本信息
    text = ser.readline().decode('utf-8').strip()

    # 在视频帧上添加文字
    cv2.putText(frame, text, (10, 50), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
    
    # 将视频帧写入 FFmpeg 的 stdin 中
    process.stdin.write(frame.tostring())

    # 如果当前时间超过了下一个时间片,就关闭上一个 FFmpeg 进程,并生成新的文件
    if datetime.datetime.now() >= next_time:
        process.stdin.close()
        process.wait()
        now_time = datetime.datetime.now()
        next_time = now_time + datetime.timedelta(minutes=30)
        filename = now_time.strftime('output_%Y%m%d_%H%M%S.mp4')
        command[-1] = filename
        process = subprocess.Popen(command, stdin=subprocess.PIPE)

# 关闭串口和摄像机
ser.close()
cap.release()

# 关闭最后一个 FFmpeg 进程
process.stdin.close()
process.wait()