You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何正确加密视频I帧?为何加密后P帧仍可正常播放?

问题描述

我尝试仅加密视频文件中的I帧以降低加密整体能耗,但加密后仅被加密的I帧显示为噪声,其余P帧仍可正常播放,这与预期的P帧依赖I帧应被扭曲不符。视频格式为AVI,编码为MPEG-4视频(FMP4)。

我使用FFprobe命令识别名为video1的视频的I帧:

ffprobe -select_streams v -show_frames -of csv video1.avi > video_frame_info.csv

得到的结果显示每12帧为一个I帧。随后我使用如下Python程序,通过PyCryptodome库的ChaCha20加密每12帧:

from Crypto.Cipher import ChaCha20
from Crypto.Random import get_random_bytes
import cv2
import numpy as np

def read_frames(video_path):
    # Load the video file
    video = cv2.VideoCapture(video_path)

    # Initialize a list to hold frames
    frames = []

    # Loop until there are frames left in the video file
    while video.isOpened():
        ret, frame = video.read()
        if not ret:
            break

        frames.append(frame)

    video.release()

    return frames

def encrypt_iframes(frames, key):
    # Initialize the cipher
    cipher = ChaCha20.new(key=key)

    # Encrypt every 12th frame (considering the first frame as an I-frame)
    for i in range(0, len(frames), 12):
        frame = frames[i]
        # Flatten and bytes-encode the frame
        flat_frame = frame.flatten()
        bytes_frame = flat_frame.tobytes()

        # Encrypt the frame
        encrypted_frame = cipher.encrypt(bytes_frame)

        # Replace the original frame with the encrypted one
        frames[i] = np.frombuffer(encrypted_frame, dtype=flat_frame.dtype).reshape(frame.shape)

    return frames

def write_frames(frames, output_path):
    # Assume all frames have the same shape
    height, width, _ = frames[0].shape

    # Create a VideoWriter object
    out = cv2.VideoWriter(output_path, cv2.VideoWriter_fourcc(*'mpv4'), 30, (width, height))

    for frame in frames:
        out.write(frame)

    out.release()

def main():
    # Generate a random key
    key = get_random_bytes(32)

    # Read frames from video
    frames = read_frames('video1.avi')

    # Encrypt I-frames
    encrypted_frames = encrypt_iframes(frames, key)

    # Write encrypted frames to a new video file
    write_frames(encrypted_frames, 'reprise.avi')

if __name__ == "__main__":
    main()

播放加密后的视频时,仅每12帧显示噪声,其余P帧完全正常。请问操作中的问题是什么?如何实现加密I帧后使依赖它的P帧也无法正常播放?


问题根源

你的操作核心问题在于处理的是解码后的原始帧,而非编码层面的压缩帧数据:

  • 用OpenCV读取视频时,已经完成了解码过程——P帧会被自动解码成完整的原始帧,不再依赖原始I帧的压缩数据。
  • 你加密的是解码后的I帧原始像素数据,写入新视频时,OpenCV会重新对所有帧进行编码,此时每个P帧都是基于前一个已解码的原始帧生成的,和你加密的原I帧压缩数据没有关联。

简单说:你相当于把视频拆成了一堆独立的原始图片,加密其中几张后,又重新编码成了一个新的、帧间依赖关系完全重置的视频,自然P帧不受影响。


解决方案

要实现加密I帧后让依赖它的P帧失效,必须在编码后的压缩数据流层面操作,而非解码后的原始帧。具体步骤如下:

1. 提取视频的原始压缩码流

用FFmpeg提取视频的H.264/MPEG-4压缩码流:

ffmpeg -i video1.avi -c:v copy -bsf:v h264_mp4toannexb -f rawvideo video1.h264

2. 定位码流中的I帧位置

通过FFprobe获取I帧的起始字节偏移和长度(而非帧序号):

ffprobe -select_streams v -show_frames -show_entries frame=pkt_pos,pkt_size,key_frame -of csv video1.avi > frame_metadata.csv

在输出的CSV中,key_frame=1的行就是I帧,记录对应的pkt_pos(起始字节)和pkt_size(字节长度)。

3. 加密码流中的I帧数据

编写Python代码直接操作H.264码流文件,找到I帧的字节区间并加密:

from Crypto.Cipher import ChaCha20
from Crypto.Random import get_random_bytes

def encrypt_h264_iframes(input_path, output_path, key):
    # 读取帧元数据
    iframe_positions = []
    with open('frame_metadata.csv', 'r') as f:
        next(f)  # 跳过表头
        for line in f:
            parts = line.strip().split(',')
            if len(parts) >= 4 and parts[3] == '1':  # 筛选I帧
                pkt_pos = int(parts[1])
                pkt_size = int(parts[2])
                iframe_positions.append((pkt_pos, pkt_size))
    
    # 读取整个码流文件
    with open(input_path, 'rb') as f:
        stream_data = bytearray(f.read())
    
    # 加密每个I帧的字节区间
    cipher = ChaCha20.new(key=key)
    for pos, size in iframe_positions:
        iframe_data = stream_data[pos:pos+size]
        encrypted_data = cipher.encrypt(iframe_data)
        stream_data[pos:pos+size] = encrypted_data
    
    # 写入加密后的码流
    with open(output_path, 'wb') as f:
        f.write(stream_data)

def main():
    key = get_random_bytes(32)
    encrypt_h264_iframes('video1.h264', 'encrypted_video.h264', key)

if __name__ == "__main__":
    main()

4. 重新封装成视频文件

用FFmpeg把加密后的H.264码流封装回AVI格式:

ffmpeg -i encrypted_video.h264 -c:v copy encrypted_output.avi

这样处理后,播放加密视频时,I帧的压缩数据被破坏,解码器无法正确解码I帧,后续依赖该I帧的P帧也会因为参考数据错误而无法正常显示,全部呈现噪声或扭曲。


内容的提问来源于stack exchange,提问作者user21612887

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 22:44:56