使用FFmpeg的SDK库实现将H.264流封装进MP4文件时全局SPS、PPS与流中SPS、PPS冲突的问题

本文介绍使用FFmpeg将H.264流封装为MP4文件的方法,探讨了当流中SPS、PPS配置与FFmpeg默认配置冲突时的解决策略。提供了两种解决方案并附带源码示例。
一、问题
1. 使用FFmpeg的SDK库实现将H.264流封装进MP4文件的源码大致如下:

char* filename = "./test.mp4"

AVOutputFormat *fmt;
AVStream* video_st;
AVFormatContext *av_context;

/* 初始化资源 */
av_register_all();
int iret = avformat_alloc_output_context2(&av_context, NULL, NULL, filename);
if (!av_context) {
printf("Could not deduce output format from file extension: using MPEG.\n");
return -1;
}
av_context->oformat->video_codec = CODEC_ID_H264;
fmt = av_context->oformat;

/* Init AVCodecContext */
video_st = avformat_new_stream(av_context, NULL);
if (!video_st) {
fprintf(stderr, "Could not alloc stream\n");
return -1;
}

AVCodecContext *codec_context;
AVCodec *codec;
codec_context = video_st ->codec;
/* find the video encoder */
codec = avcodec_find_encoder(fmt->video_codec);
if (!codec) {
fprintf(stderr, "codec not found\n");
return -1;
}
avcodec_get_context_defaults3(codec_context, codec);
codec_context->codec_id = fmt->video_codec;
codec_context->bit_rate = 500000; /* put sample parameters */
codec_context->width = width; /* resolution must be a multiple of two */
codec_context->height = height;
codec_context->time_base.den = 1000;
codec_context->time_base.num = 1;
codec_contextc->gop_size = 25; /* emit one intra frame every twelve frames at most */
codec_context->pix_fmt = STREAM_PIX_FMT;

if (avcodec_open(video_st->codec, codec) < 0) {
fprintf(stderr, "video could not open codec\n");
ERROR_LOG("video codec not found!\n");
return -1 ;
}

av_dump_format(av_context, 0, filename, 1);

/* open the output file, if needed */
if (!(fmt->flags & AVFMT_NOFILE)) {
iret = avio_open(&av_context->pb, filename, AVIO_FLAG_WRITE);
if (iret < 0) {
fprintf(stderr, "Could not open '%s'\n", filename);
return -1;
}
}

/* write the stream header, if any */
avformat_write_header(av_context, NULL);

/* 将裸数据流写文件,并打上时间戳 */
for (i = 0; i < end ; i++) {
// 取得帧数据enc_data,enc_size,和pts

AVPacket pkt;
av_init_packet(&pkt);

if(is_key)
pkt.flags |= AV_PKT_FLAG_KEY;

pkt.pts = pts;
pkt.dts = pts;
pkt.stream_index = video_st->index;
pkt.data = enc_data;
pkt.size = enc_size;

/* write the compressed frame in the media file */
ret = av_interleaved_write_frame(av_context, &pkt);
}


/* 释放资源 */
av_write_trailer(av_context);
avcodec_close(video_st->codec);
avio_close(av_context->pb);
av_free(av_context);
video_st = NULL;
av_context = NULL;


2. 冲突问题
如果以上述方式进行封装时,FFmpeg默认写到流中的全局H.264 SPS、PPS默认是High profile;
如果流中的是baseline profile, 则会存在有两个SPS、PPS冲突问题,
如下图所示:

这时,对有些播放器,会出现花屏;

二、原因
FFmpeg 的API:
avcodec_open(video_st->codec, codec)
或 avcodec_open2()的定义位于libavcodec\utils.c,
它们将H.264的编码器初始化成了High profile;

具体过程如下:
avcodec_open2所做的工作,如下所列:
(1)为各种结构体分配内存(通过各种av_malloc()实现)。
(2)将输入的AVDictionary形式的选项设置到AVCodecContext。
(3)其他一些零零碎碎的检查,比如说检查编解码器是否处于“实验”阶段。
(4)如果是编码器,检查输入参数是否符合编码器的要求
(5)调用AVCodec的init()初始化具体的解码器。

它最关键的是第5步,即调用了一个关键的函数AVCodec的init()。
这个方法是初始化具体的编码器。
AVCodec的init()是一个函数指针,指向具体编解码器中的初始化函数。
这里我们以libx264为例,看一下它对应的AVCodec的定义。
libx264对应的AVCodec的定义位于libavcodec\libx264.c,
如下所示。

AVCodec ff_libx264_encoder = {
.name = "libx264",
.long_name = NULL_IF_CONFIG_SMALL("libx264 H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10"),
.type = AVMEDIA_TYPE_VIDEO,
.id = AV_CODEC_ID_H264,
.priv_data_size = sizeof(X264Context),
.init = X264_init,
.encode2 = X264_frame,
.close = X264_close,
.capabilities = CODEC_CAP_DELAY | CODEC_CAP_AUTO_THREADS,
.priv_class = &x264_class,
.defaults = x264_defaults,
.init_static_data = X264_init_static,
};

可以看出在ff_libx264_encoder中init()指向X264_init()。
X264_init()的定义同样位于libavcodec\libx264.c,
X264_init()做了两项工作:
(1)设置X264Context的参数。X264Context主要完成了libx264和FFmpeg对接的功能。
可以看出代码主要在设置一个params结构体变量,该变量的类型即是x264中存储参数的结构体x264_param_t。
(2)调用libx264的API进行编码器的初始化工作。
例如调用x264_param_default()设置默认参数,调用x264_param_apply_profile()设置profile,调用x264_encoder_open()打开编码器等等。

X264Context的定义,位于libavcodec\libx264.c,如下所示:
typedef struct X264Context {
AVClass *class;
x264_param_t params;
x264_t *enc;
x264_picture_t pic;
uint8_t *sei;
int sei_size;
char *preset;
char *tune;
char *profile;
char *level;
int fastfirstpass;
char *wpredp;
char *x264opts;
float crf;
float crf_max;
int cqp;
int aq_mode;
float aq_strength;
char *psy_rd;
int psy;
int rc_lookahead;
int weightp;
int weightb;
int ssim;
int intra_refresh;
int bluray_compat;
int b_bias;
int b_pyramid;
int mixed_refs;
int dct8x8;
int fast_pskip;
int aud;
int mbtree;
char *deblock;
float cplxblur;
char *partitions;
int direct_pred;
int slice_max_size;
char *stats;
int nal_hrd;
int avcintra_class;
char *x264_params;
} X264Context;


初始化完成后,FFmpeg 的全局SPS,PPS存储在 AVCodecContext->extradata 和 AVCodecContext->extrdata_size 中,
通过GDB跟踪显示如下:
378 if (avcodec_open(video_st->codec, codec) < 0){
(gdb) p video_st->codec
$11 = (AVCodecContext *) 0x7fffe0006ce0
(gdb) p *video_st->codec
$12 = {av_class = 0xf3e580, bit_rate = 500000, bit_rate_tolerance = 4000000, flags = 4194304, sub_id = 0, me_method = -1, extradata = 0x0, extradata_size = 0,....
(gdb) n
(gdb) p *video_st->codec
$13 = {av_class = 0xf3e580, bit_rate = 500000, bit_rate_tolerance = 4000000, flags = 4194304, sub_id = 0, me_method = -1, extradata = 0x7fffe049c700 "",
extradata_size = 34, time_base = {num = 1, den = 1000}, width = 320, height = 240, gop_size = 1000, pix_fmt = PIX_FMT_YUV420P, draw_horiz_band = 0, sample_rate = 0,...

三、解决方案
1. 方案一:将全局的SPS、PPS设置成和流中同样的baseline属性
代码如下所示:

AVDictionary *opts = NULL;
av_dict_set(&opts, "profile", "baseline", 0);

/* open the codec */
if (avcodec_open2(c, codec, &opts) < 0){
fprintf(stderr, "video could not open codec\n");
return -1;
}

2. 方案二:使用流的SPS、PPS覆盖全局的SPS、PPS;
即,
Step1: 获得流中的SPS,PPS数据,及它们合在一起的size;
Step2:释放原有的 video_st->codec->extradata 数据;
Step3:申请新的 video_st->codec->extradata 空间,并将流的SPS+PPS复制进去,
再置 video_st->codec->extradata_size 为新的size;
具体的的源码不在些处列出。
需要的可以去此下载,
我从FFmpeg中抽取出来,
读取文件中数据流,快速找到buffer中的SPS 和 PPS,并解析它们的代码;

上面的那些问题是下面这个代码引起的,你看看代码有没有问题: #!/usr/bin/env python3 import cv2 import numpy as np import mindx.sdk.base as base import logging import time import sys from pathlib import Path # -------------------------- 全局配置 -------------------------- # 日志配置 logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s', handlers=[ logging.FileHandler("yolov8_video_inference.log"), logging.StreamHandler() ] ) logger = logging.getLogger("YOLOv8_Inference") # 模型参数(单类别YOLOv8n) MODEL_PATH = "best.om" # 替换为你的模型路径 INPUT_SIZE = (640, 640) # 模型输入尺寸 CONF_THRESHOLD = 0.5 # 置信度阈值 NMS_THRESHOLD = 0.45 # NMS阈值 TARGET_CLASS_ID = 0 # 单类别ID(根据模型训练配置调整) # -------------------------- 核心函数 -------------------------- """ def initialize_sdk(): #初始化MindX SDK环境 try: if not base.mx_init(): logger.error("MindX SDK初始化失败") return False logger.info("MindX SDK初始化成功") return True except Exception as e: logger.error(f"SDK初始化异常: {str(e)}") return False """ def initialize_sdk(): try: base.mx_init() # 不判断返回值,直接调用 logger.info("MindX SDK初始化成功") return True except Exception as e: logger.error(f"SDK初始化异常: {str(e)}") return False """ def create_video_decoder(input_path): #创建视频解码器 try: logger.info("开始创建视频解码器...") # 1. 打印输入视频路径,确认路径正确 logger.info(f"输入视频路径: {input_path}") # 获取视频基本信息(用于初始化解码器) cap = cv2.VideoCapture(input_path) if not cap.isOpened(): logger.error(f"无法打开视频文件: {input_path}") return None, None logger.info("成功打开视频文件,开始提取参数...") # 2. 提取并打印视频参数,确认参数有效性 width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) fps = cap.get(cv2.CAP_PROP_FPS) total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) logger.info(f"提取视频参数: 宽={width}, 高={height}, FPS={fps:.2f}, 总帧数={total_frames}") # 释放临视频句柄 cap.release() logger.info("已释放临视频句柄") # 3. 打印配置项,确认枚举值和参数正确性 logger.info(f"支持的图像格式枚举: {[attr for attr in dir(base.image_format) if not attr.startswith('_')]}") logger.info(f"输入视频格式枚举: {base.StreamFormat.h264_main_level}") logger.info(f"输出图像格式枚举: {base.image_format.nv12}") # 创建视频解码配置 decoder_config = base.VideoDecodeConfig( #width=width, #height=height, #input_video_format=base.StreamFormat.h264_main_level, #inputVideoFormat=base.StreamFormat.h264_main_level, #inputVideoFormat=base.h264_main_level, #output_format=base.image_format.nv12 #outputIamgeFormat=base.image_format.nv12 #outputIamgeFormat=base.nv12 ) logger.info("视频解码配置创建完成") # 4. 打印设备ID,确认设备可用 device_id = 0 logger.info(f"使用设备ID: {device_id}") # 创建视频解码器 logger.info("开始初始化视频解码器...") #decoder = base.VideoDecoder(config=decoder_config, device_id=device_id) decoder = base.VideoDecoder(pyVdecConfig=decoder_config,deviceId=device_id) logger.info(f"视频解码器创建成功 | 分辨率: {width}x{height} | FPS: {fps:.2f}") return decoder, (width, height, fps, total_frames) except Exception as e: logger.error(f"解码器创建失败: {str(e)}", exc_info=True) # 打印完整异常堆栈 return None, None """ def create_video_decoder(input_path): """创建视频解码器(符合Vision SDK规范)""" try: logger.info("开始创建视频解码器...") # 1. 打开视频文件并提取参数(用于日志打印,非解码器依赖) cap = cv2.VideoCapture(input_path) if not cap.isOpened(): logger.error(f"无法打开视频文件: {input_path}") return None, None width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) fps = cap.get(cv2.CAP_PROP_FPS) total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) cap.release() logger.info(f"视频参数: 宽={width}, 高={height}, FPS={fps:.2f}, 总帧数={total_frames}") # 2. 定义解码回调函数(文档7.3.5.4.2要求,用于接收解码后的数据) decoded_frames = [] # 存储Image对象 def vdec_callback(decoded_image, channel_id, frame_id): """解码回调函数:处理单帧解码结果""" # 此处可添加帧数据处理逻辑(如缓存、预处理等) decoded_frames.append(decoded_image) # decoded_image是Image类对象 logger.debug(f"解码完成 - 通道{channel_id}, 帧{frame_id}") return base.APP_ERR_OK # 必须返回成功码 # 3. 初始化回调管理器(文档7.3.5.4要求,必填参数) vdec_callbacker = base.VdecCallBacker() vdec_callbacker.registerVdecCallBack(vdec_callback) # 注册回调函数 # 4. 构造解码配置(文档7.3.3.1.36要求,必填参数) decoder_config = base.VideoDecodeConfig() decoder_config.input_video_format = base.StreamFormat.h264_main_level # 输入视频格式 decoder_config.output_format = base.image_format.nv12 # 输出格式(匹配日志支持列表) decoder_config.skip_interval = 0 # 不跳帧(文档可选参数) # 5. 创建视频解码器(文档7.3.5.5要求,传入所有必填参数) device_id = 0 # 设备ID(文档可选,默认0) decoder = base.VideoDecoder( #config=decoder_config, # 解码配置(pyVdecConfig) pyVdecConfig=decoder_config, # 解码配置(pyVdecConfig) #callBacker=vdec_callbacker, # 回调管理器(pyVdecCallBacker) pyVdecCallBacker=vdec_callbacker, # 回调管理器(pyVdecCallBacker) deviceId=device_id # 设备ID ) logger.info(f"视频解码器创建成功 | 设备ID: {device_id}") return decoder, (width, height, fps, total_frames) except Exception as e: logger.error(f"解码器创建失败: {str(e)}", exc_info=True) return None, None def load_om_model(model_path): """加载OM模型(单类别YOLOv8n)""" try: # 模型加载参数(动态批处理优化) model = base.Model( #model_path=model_path, modelPath=model_path, #device_id=0, # 使用0号NPU设备 deviceId=0, # 使用0号NPU设备 #dynamic_batch=1, # 单类别推理建议批大小=1 #infer_mode=base.InferMode.ASCEND # 使用NPU加速 ) logger.info(f"模型加载成功: {model_path}") return model except Exception as e: logger.error(f"模型加载失败: {str(e)}") return None """ def preprocess_frame(frame, target_size): #图像预处理(缩放+格式转换) try: # 转换为MindX Image对象(RGB格式) height, width = frame.shape[:2] mx_image = base.Image( data=frame.tobytes(), width=width, height=height, format=base.image_format.RGB_888 ) # 图像缩放(使用MindX内置处理器) processor = base.ImageProcessor() resized_img = processor.resize( mx_image, base.Size(*target_size), interpolation="huaweiu_high_order_filter" # 高阶滤波 ) return resized_img except Exception as e: logger.error(f"预处理失败: {str(e)}") return None """ """ def preprocess_frame(mx_image, target_size): #图像预处理(格式转换+缩放) try: # 1. 将nv12格式转换为RGB_888(关键步骤) processor = base.ImageProcessor(deviceId=0) rgb_image = processor.convert_format(frame, base.image_format.rgb) # 匹配日志中的枚举值 # 2. 图像缩放 resized_img = processor.resize( rgb_image, base.Size(*target_size), interpolation="huaweiu_high_order_filter" ) return resized_img except Exception as e: logger.error(f"预处理失败: {str(e)}") return None """ """ def preprocess_frame(mx_image, target_size): # mx_image为Image类对象 try: processor = base.ImageProcessor(deviceId=0) # 直接使用Image对象作为参数 rgb_image = processor.convert_format(mx_image, base.image_format.rgb) #resized_img = processor.resize(rgb_image, base.Size(*target_size)) # 3. 缩放至模型输入尺寸 resized_img = processor.resize( rgb_image, base.Size(*target_size), # 目标尺寸 interpolation=base.Interpolation.huaweiu_high_order_filter # 高阶滤波 ) return resized_img except Exception as e: logger.error(f"预处理失败: {str(e)}") return None """ def preprocess_frame(mx_image, target_size): # mx_image为解码器输出的NV12格式Image对象 try: # 1. 验证输入格式为NV12(文档7.3.5.2.2 Image类属性) logger.info(f"输入Image格式: {mx_image.format}") if mx_image.format != base.image_format.nv12: logger.error(f"预期输入格式为NV12,实际为{mx_image.format}") return None # 2. 初始化处理器(文档7.3.5.3.2 ImageProcessor类) processor = base.ImageProcessor(deviceId=0) # 3. 将NV12转换为模型所需的RGB_888(文档7.3.5.3.8 色域转换) # 参考文档7.3.3.1.13,使用image_format枚举值 rgb_image = processor.convert_format( mx_image, base.image_format.RGB_888 # 目标格式为RGB_888(模型常见输入格式) ) logger.info("NV12格式转换为RGB_888成功") # 4. 缩放至模型输入尺寸(文档7.3.5.3.4 缩放接口) resized_img = processor.resize( rgb_image, base.Size(*target_size), # 目标尺寸(640,640) interpolation=base.Interpolation.huaweiu_high_order_filter # 高阶滤波插值 ) logger.info(f"缩放成功,目标尺寸: {target_size}") return resized_img except Exception as e: logger.error(f"预处理失败: {str(e)}", exc_info=True) return None def yolov8_single_class_postprocess(output_tensor): """单类别YOLOv8后处理(解析边界框+置信度)""" try: # 输出张量形状: [1, 4+1, 8400](YOLOv8n输出) # 维度说明: [Batch, (cx, cy, w, h, conf), Anchors] output_data = output_tensor.to_host().reshape(5, 8400) # 单批次 # 提取关键数据 boxes = output_data[0:4, :] # cx, cy, w, h confs = output_data[4, :] # 置信度 # 过滤低置信度检测 valid_indices = np.where(confs >= CONF_THRESHOLD)[0] if len(valid_indices) == 0: return [], [], [] # 解析有效检测 boxes_list = [] confs_list = [] for idx in valid_indices: cx, cy, w, h = boxes[:, idx] conf = confs[idx] # 转换为像素坐标(假设输入尺寸为640x640) x1 = int((cx - w/2) * 640) y1 = int((cy - h/2) * 640) x2 = int((cx + w/2) * 640) y2 = int((cy + h/2) * 640) boxes_list.append([x1, y1, x2, y2]) confs_list.append(float(conf)) return boxes_list, confs_list, [TARGET_CLASS_ID] * len(boxes_list) except Exception as e: logger.error(f"后处理失败: {str(e)}") return [], [], [] def draw_detections(frame, boxes, confs, class_ids): """绘制检测结果(单类别可视化)""" try: for box, conf, cls_id in zip(boxes, confs, class_ids): x1, y1, x2, y2 = box # 绘制边界框(绿色) cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2) # 绘制标签(类别+置信度) label = f"Class {cls_id}: {conf:.2f}" cv2.putText(frame, label, (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2) return frame except Exception as e: logger.warning(f"结果绘制失败: {str(e)}") return frame def process_video(input_path, output_path): """视频处理主函数(遵循Vision SDK 7.1.RC1文档规范)""" # 1. 初始化SDK(文档7.3.4节) if not initialize_sdk(): return False # 2. 创建视频解码器(单一实例,文档7.3.5.5节) decoder, video_info = create_video_decoder(input_path) if not decoder: base.mx_deinit() return False width, height, fps, total_frames = video_info # 3. 加载模型(文档7.3.6节) model = load_om_model(MODEL_PATH) if not model: decoder.destroy() base.mx_deinit() return False # 4. 初始化输出视频(使用解码器获取的视频参数) fourcc = cv2.VideoWriter_fourcc(*'mp4v') writer = cv2.VideoWriter(output_path, fourcc, fps, (width, height)) if not writer.isOpened(): logger.error("无法创建输出输出视频文件") model.destroy() decoder.destroy() base.mx_deinit() return False # 5. 配置解码回调(文档7.3.5.4节) decoded_frames = [] # 存储解码器输出的Image对象 def vdec_callback(decoded_image, channel_id, frame_id): """回调函数:接收解码后的Image对象""" decoded_frames.append(decoded_image) return base.APP_ERR_OK # 注册回调函数 vdec_callbacker = base.VdecCallBacker() vdec_callbacker.registerVdecCallBack(vdec_callback) # 6. 开始解码视频(文档7.3.5.5.2节) try: with open(input_path, 'rb') as f: video_data = f.read() decoder.decode(video_data, 0) # 0为起始帧ID except Exception as e: logger.error(f"解码视频失败: {str(e)}") return False # 7. 处理解码帧(文档6.2.3节媒体数据程) start_time = time.time() processed_frames = 0 inference_time_total = 0.0 max_wait_time = 10 # 最大等待间(秒) start_wait = time.time() logger.info(f"开始处理视频 | 分辨率: {width}x{height} | FPS: {fps:.2f} | 总帧数: {total_frames}") while processed_frames < total_frames: # 超检查 if time.time() - start_wait > max_wait_time: logger.error(f"等待帧超({max_wait_time}秒),可能解码失败") break # 等待帧数据就绪 if len(decoded_frames) > processed_frames: # 获取解码器输出的Image对象 mx_image = decoded_frames[processed_frames] processed_frames += 1 start_wait = time.time() # 重置超 # 8. 预处理(文档7.3.5.3节) preprocessed_img = preprocess_frame(mx_image, INPUT_SIZE) if not preprocessed_img: continue # 9. 模型推理(文档7.3.6节) inference_start = time.time() try: input_tensor = preprocessed_img.to_tensor() outputs = model.infer([input_tensor]) inference_time = time.time() - inference_start inference_time_total += inference_time except Exception as e: logger.error(f"推理失败: {str(e)}") continue # 10. 后处理 boxes, confs, class_ids = yolov8_single_class_postprocess(outputs[0]) # 11. 结果可视化(将Image对象转为OpenCV格式) # 参考文档7.3.5.2.2节Image类属性 frame_data = np.frombuffer(mx_image.data, dtype=np.uint8) # 根据解码器输出格式NV12转换为BGR(文档7.3.5.3.8节) frame = cv2.cvtColor(frame_data.reshape((height * 3 // 2, width)), cv2.COLOR_YUV2BGR_NV12) rendered_frame = draw_detections(frame, boxes, confs, class_ids) # 12. 写入输出视频 writer.write(rendered_frame) # 13. 进度统计 if processed_frames % 10 == 0: elapsed = time.time() - start_time logger.info(f"已处理 {processed_frames}/{total_frames} 帧 | 耗: {elapsed:.2f}s | 平均FPS: {processed_frames/elapsed:.2f}") else: # 未获取到帧短暂等待(文档推荐的异步处理建议) time.sleep(0.01) # 14. 释放资源(文档7.3.7节资源管理) writer.release() model.destroy() decoder.destroy() base.mx_deinit() # 15. 性能总结 total_time = time.time() - start_time avg_fps = processed_frames / total_time if total_time > 0 else 0 logger.info(f"视频处理完成 | 总帧数: {processed_frames} | 总耗: {total_time:.2f}s | 平均FPS: {avg_fps:.2f}") if processed_frames > 0: avg_inference_time = inference_time_total / processed_frames logger.info(f"推理总耗: {inference_time_total:.2f}s | 平均推理间: {avg_inference_time:.4f}s") else: logger.warning("未处理任何有效帧") return processed_frames > 0 if __name__ == "__main__": # 配置参数(根据实际情况修改) INPUT_VIDEO = "2025-07-12-51957.mp4" # 输入视频路径 OUTPUT_VIDEO = "2025-07-12-51957_out.mp4" # 输出视频路径 # 运行推理 success = process_video(INPUT_VIDEO, OUTPUT_VIDEO) if success: logger.info("视频推理成功完成!") sys.exit(0) else: logger.error("视频推理失败!") sys.exit(1)
最新发布
08-02
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

北雨南萍

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值