ff_h264_find_frame_end

本文介绍了一种用于H.264视频流中帧结束位置检测的具体算法实现。该算法通过状态机的方式,根据输入缓冲区中的数据特征来定位帧的结束位置,适用于不同类型的NAL单元,并特别考虑了AVC格式的数据长度标识情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

2 - denote 1 zero found

1 - denote 2 zeros found

0 - denote >= 3 zeros found

3 - no this state

4 - denote 2 zeros and 1 one found, i.e., 001

5 - denote at least 3 zeros and 1 one found, i.e., 0001 or 00001,etc

6. - no this state

7 - init state

8 - state greater and equal 8 denote two slice header found


static int ff_h264_find_frame_end(H264Context *h, const uint8_t *buf, int buf_size)

{
    int i, j;
    uint32_t state;
    ParseContext *pc = &(h->s.parse_context);
    int next_avc= h->is_avc ? 0 : buf_size;


//    mb_addr= pc->mb_addr - 1;
    state= pc->state;
    if(state>13)
        state= 7;      


    if(h->is_avc && !h->nal_length_size)
        av_log(h->s.avctx, AV_LOG_ERROR, "AVC-parser: nal length size invalid\n");


    for(i=0; i<buf_size; i++){
        if(i >= next_avc) {
            int nalsize = 0;
            i = next_avc;
            for(j = 0; j < h->nal_length_size; j++)
                nalsize = (nalsize << 8) | buf[i++];
            if(nalsize <= 0 || nalsize > buf_size - i){
                av_log(h->s.avctx, AV_LOG_ERROR, "AVC-parser: nal size %d remaining %d\n", nalsize, buf_size - i);
                return buf_size;
            }
            next_avc= i + nalsize;
            state= 5;
        }


        if(state==7){
#if HAVE_FAST_UNALIGNED
        /* we check i<buf_size instead of i+3/7 because its simpler
         * and there should be FF_INPUT_BUFFER_PADDING_SIZE bytes at the end
         */

#    if HAVE_FAST_64BIT

           // when no one zero arise in continuous 8 bytes, forward 8 bytes

            while(i<next_avc && !((~*(const uint64_t*)(buf+i) & (*(const uint64_t*)(buf+i) - 0x0101010101010101ULL)) & 0x8080808080808080ULL))
                i+=8;

#    else

           // when no one zero arise in continuous 4 bytes, forward 4 bytes

            while(i<next_avc && !((~*(const uint32_t*)(buf+i) & (*(const uint32_t*)(buf+i) - 0x01010101U)) & 0x80808080U))
                i+=4;
#    endif
#endif
            for(; i<next_avc; i++){
                if(!buf[i]){
                    state=2;
                    break;
                }
            }
        }else if(state<=2){
            if(buf[i]==1)   state^= 5; //2->7, 1->4, 0->5
            else if(buf[i]) state = 7;
            else            state>>=1; //2->1, 1->0, 0->0
        }else if(state<=5){
            int v= buf[i] & 0x1F;
            if(v==6 || v==7 || v==8 || v==9){  //  7 denote sps,  8 denote pps,  9 denote access unit delimiter
                if(pc->frame_start_found){
                    i++;
                    goto found;
                }
            }else if(v==1 || v==2 || v==5){  // 1 denote coded slice of a non-IDR picture,  2 deonte coded slice data partition A,  5 denote coded slice of an IDR picture
                state+=8;
                continue;
            }
            state= 7;
        }else {  // to be continued
            h->parse_history[h->parse_history_count++]= buf[i];
            if(h->parse_history_count>3){
                unsigned int mb, last_mb= h->parse_last_mb;
                GetBitContext gb;


                init_get_bits(&gb, h->parse_history, 8*h->parse_history_count);
                h->parse_history_count=0;
                mb= get_ue_golomb_long(&gb);
                last_mb= h->parse_last_mb;
                h->parse_last_mb= mb;
                if(pc->frame_start_found){
                    if(mb <= last_mb)
                        goto found;
                }else
                    pc->frame_start_found = 1;
                state= 7;
            }
        }
    }
    pc->state= state;
    if(h->is_avc)
        return next_avc;
    return END_NOT_FOUND;


found:
    pc->state=7;
    pc->frame_start_found= 0;
    if(h->is_avc)
        return next_avc;
    return i-(state&5) - 3*(state>7);
}
class SCLMultiProcessor: def __init__(self, root): self.root = root self.root.title("SCL文件处理系统 - 增强版") self.root.geometry("1100x750") # 增加窗口尺寸以适应新控件 # 初始化变量 self.color_detector = EnhancedColorDetector() self.stats_processor = SCLRuleProcessor(self.color_detector) self.empty_cell_detector = EnhancedEmptyCellDetector(self.color_detector) self.progress_var = tk.DoubleVar() # 创建主框架 self.main_frame = ttk.Frame(root, padding="10") self.main_frame.pack(fill=tk.BOTH, expand=True) # 创建UI self.create_ui() # 记录UI初始化完成 logger.info("用户界面初始化完成") def create_ui(self): """创建用户界面""" # 文件选择区域 file_frame = ttk.LabelFrame(self.main_frame, text="文件选择", padding="10") file_frame.pack(fill=tk.X, pady=5) # 输入文件选择 input_frame = ttk.Frame(file_frame) input_frame.pack(fill=tk.X, pady=5) ttk.Label(input_frame, text="主输入文件:").pack(side=tk.LEFT, padx=5) self.input_path_var = tk.StringVar() input_entry = ttk.Entry(input_frame, textvariable=self.input_path_var, width=70) input_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5) ttk.Button(input_frame, text="浏览...", command=self.browse_input_file).pack(side=tk.LEFT, padx=5) # 操作模式选择区域 mode_frame = ttk.LabelFrame(self.main_frame, text="操作模式", padding="10") mode_frame.pack(fill=tk.X, pady=5) # 添加操作模式单选按钮 self.operation_mode = tk.StringVar(value="stats") # 默认选择统计模式 ttk.Radiobutton(mode_frame, text="统计功能", variable=self.operation_mode, value="stats").pack(side=tk.LEFT, padx=10) ttk.Radiobutton(mode_frame, text="SCL格式检查", variable=self.operation_mode, value="empty_check").pack(side=tk.LEFT, padx=10) # 配置区域 config_frame = ttk.LabelFrame(self.main_frame, text="处理配置", padding="10") config_frame.pack(fill=tk.X, pady=5) # 添加SCL文件夹路径输入 scl_folder_frame = ttk.Frame(config_frame) scl_folder_frame.pack(fill=tk.X, pady=5) ttk.Label(scl_folder_frame, text="SCL文件夹路径:").grid(row=0, column=0, padx=5, sticky=tk.W) self.scl_folder_var = tk.StringVar() scl_folder_entry = ttk.Entry(scl_folder_frame, textvariable=self.scl_folder_var, width=60) scl_folder_entry.grid(row=0, column=1, padx=5, sticky=tk.W) ttk.Button(scl_folder_frame, text="浏览...", command=self.browse_scl_folder).grid(row=0, column=2, padx=5, sticky=tk.W) # 搜索选项 search_frame = ttk.Frame(config_frame) search_frame.pack(fill=tk.X, pady=5) ttk.Label(search_frame, text="文件前缀:").grid(row=0, column=0, padx=5, sticky=tk.W) self.prefix_var = tk.StringVar(value="SCL_") ttk.Entry(search_frame, textvariable=self.prefix_var, width=10).grid(row=0, column=1, padx=5, sticky=tk.W) # 添加CheckSheet路径输入 checksheet_frame = ttk.Frame(config_frame) checksheet_frame.pack(fill=tk.X, pady=5) ttk.Label(checksheet_frame, text="CheckSheet路径:").grid(row=0, column=0, padx=5, sticky=tk.W) self.checksheet_path_var = tk.StringVar() checksheet_entry = ttk.Entry(checksheet_frame, textvariable=self.checksheet_path_var, width=60) checksheet_entry.grid(row=0, column=1, padx=5, sticky=tk.W) ttk.Button(checksheet_frame, text="浏览...", command=self.browse_checksheet_path).grid(row=0, column=2, padx=5, sticky=tk.W) # 添加性能提示 ttk.Label(config_frame, text="(表头固定在第3行,数据从第4行开始)").pack(anchor=tk.W, padx=5, pady=2) # 颜色容差设置 tolerance_frame = ttk.Frame(config_frame) tolerance_frame.pack(fill=tk.X, pady=5) ttk.Label(tolerance_frame, text="颜色容差:").grid(row=0, column=0, padx=5, sticky=tk.W) self.tolerance_var = tk.IntVar(value=30) tolerance_spinbox = ttk.Spinbox( tolerance_frame, from_=0, to=100, width=5, textvariable=self.tolerance_var ) tolerance_spinbox.grid(row=0, column=1, padx=5, sticky=tk.W) ttk.Label(tolerance_frame, text="(0-100,值越大颜色匹配越宽松)").grid(row=0, column=2, padx=5, sticky=tk.W) # 日志选项 log_frame = ttk.Frame(config_frame) log_frame.pack(fill=tk.X, pady=5) ttk.Label(log_frame, text="日志级别:").grid(row=0, column=0, padx=5, sticky=tk.W) self.log_level_var = tk.StringVar(value="INFO") log_level_combo = ttk.Combobox( log_frame, textvariable=self.log_level_var, width=10, state="readonly" ) log_level_combo['values'] = ('DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL') log_level_combo.grid(row=0, column=1, padx=5, sticky=tk.W) log_level_combo.bind("<<ComboboxSelected>>", self.change_log_level) # 处理按钮 btn_frame = ttk.Frame(self.main_frame) btn_frame.pack(fill=tk.X, pady=10) ttk.Button(btn_frame, text="开始处理", command=self.process_file).pack(side=tk.LEFT, padx=5) ttk.Button(btn_frame, text="查看日志", command=self.view_log).pack(side=tk.LEFT, padx=5) ttk.Button(btn_frame, text="导出配置", command=self.export_config).pack(side=tk.LEFT, padx=5) ttk.Button(btn_frame, text="加载配置", command=self.load_config).pack(side=tk.LEFT, padx=5) ttk.Button(btn_frame, text="退出", command=self.root.destroy).pack(side=tk.RIGHT, padx=5) # 进度条 progress_frame = ttk.Frame(self.main_frame) progress_frame.pack(fill=tk.X, pady=5) ttk.Label(progress_frame, text="处理进度:").pack(side=tk.LEFT, padx=5) self.progress_bar = ttk.Progressbar( progress_frame, variable=self.progress_var, maximum=100, length=700 ) self.progress_bar.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5) self.progress_label = ttk.Label(progress_frame, text="0%") self.progress_label.pack(side=tk.LEFT, padx=5) # 结果展示区域 result_frame = ttk.LabelFrame(self.main_frame, text="处理结果", padding="10") result_frame.pack(fill=tk.BOTH, expand=True, pady=5) # 结果文本框 self.result_text = scrolledtext.ScrolledText( result_frame, wrap=tk.WORD, height=20 ) self.result_text.pack(fill=tk.BOTH, expand=True) self.result_text.config(state=tk.DISABLED) # 状态栏 self.status_var = tk.StringVar(value="就绪") status_bar = ttk.Label(self.main_frame, textvariable=self.status_var, relief=tk.SUNKEN, anchor=tk.W) status_bar.pack(fill=tk.X, pady=5) logger.info("UI创建完成") def browse_checksheet_path(self): """浏览CheckSheet文件夹""" folder_path = filedialog.askdirectory(title="选择CheckSheet文件夹") if folder_path: self.checksheet_path_var.set(folder_path) logger.info(f"已选择CheckSheet文件夹: {folder_path}") def change_log_level(self, event=None): """更改日志级别""" level = self.log_level_var.get() logger.setLevel(getattr(logging, level)) logger.info(f"日志级别已更改为: {level}") def browse_input_file(self): """浏览输入文件""" file_path = filedialog.askopenfilename( filetypes=[("Excel 文件", "*.xlsx *.xls"), ("所有文件", "*.*")] ) if file_path: self.input_path_var.set(file_path) self.input_file = file_path logger.info(f"已选择输入文件: {file_path}") def browse_scl_folder(self): """浏览SCL文件夹""" folder_path = filedialog.askdirectory(title="选择SCL文件夹") if folder_path: self.scl_folder_var.set(folder_path) logger.info(f"已选择SCL文件夹: {folder_path}") def highlight_cell(self, sheet, row, col, color="FFFF0000"): """为单元格设置背景色""" try: fill = PatternFill(start_color=color, end_color=color, fill_type="solid") sheet.cell(row=row, column=col).fill = fill return True except Exception as e: logger.error(f"设置单元格颜色失败: {str(e)}") return False def process_file(self): """处理文件 - 每次处理保存数据,下次运行重新开始""" if not self.input_path_var.get(): messagebox.showwarning("警告", "请先选择输入文件") logger.warning("未选择输入文件") return try: # 重置结果 self.result_text.config(state=tk.NORMAL) self.result_text.delete(1.0, tk.END) self.result_text.insert(tk.END, "开始处理...\n") self.result_text.see(tk.END) self.result_text.config(state=tk.DISABLED) self.status_var.set("开始处理文件...") self.root.update() # 更新颜色检测器的容差设置 self.color_detector.tolerance = self.tolerance_var.get() logger.info(f"颜色容差设置为: {self.color_detector.tolerance}") # 获取输入文件目录 input_file = self.input_path_var.get() input_dir = os.path.dirname(input_file) logger.info(f"开始处理文件: {input_file}") logger.info(f"文件目录: {input_dir}") # 获取SCL文件夹路径 scl_folder = self.scl_folder_var.get() if not scl_folder: # 如果没有指定SCL文件夹,则使用输入文件所在目录 scl_folder = input_dir logger.info(f"未指定SCL文件夹,使用输入文件目录: {scl_folder}") # 构建文件路径时使用SCL文件夹 file_path = os.path.join(scl_folder, file_name) # 使用openpyxl加载工作簿(保留格式) wb = openpyxl.load_workbook(input_file) sheet = wb.active logger.info(f"工作簿加载成功, 工作表: {sheet.title}") # 获取配置参数 prefix = self.prefix_var.get() operation_mode = self.operation_mode.get() logger.info(f"配置参数: 文件前缀={prefix}, 操作模式={operation_mode}") # 扫描E列(第5列) total_rows = sheet.max_row processed_count = 0 found_files = 0 problem_files = 0 logger.info(f"开始扫描E列, 总行数: {total_rows}") start_time = time.time() for row_idx in range(1, total_rows + 1): # 更新进度 progress = (row_idx / total_rows) * 100 self.progress_var.set(progress) self.progress_label.config(text=f"{progress:.1f}%") self.root.update() cell = sheet.cell(row=row_idx, column=5) cell_value = str(cell.value) if cell.value else "" # 检查是否包含前缀的文件名 if prefix in cell_value: # 提取文件名(可能有多个以空格分隔) file_names = re.findall(fr'{prefix}[^\s]+', cell_value) logger.info(f"行 {row_idx}: 找到文件: {', '.join(file_names)}") result_lines = [] file_has_problems = False # 标记当前行是否有问题文件 for file_name in file_names: file_path = os.path.join(scl_folder, file_name) # 检查文件是否存在 if not os.path.exists(file_path): result_lines.append(f"{file_name}: 文件不存在") logger.warning(f"文件不存在: {file_path}") # 标记文件不存在的单元格为紫色 self.highlight_cell(sheet, row_idx, 5, "FF800080") file_has_problems = True problem_files += 1 continue # 根据操作模式执行不同处理 if operation_mode == "stats": # 统计模式 results, color_report, missing_data = self.stats_processor.process_file(file_path) # 如果有数据缺失 if missing_data: file_has_problems = True problem_files += 1 result_lines.append(f"{file_name}: 数据缺失!") for item in missing_data: result_lines.append(f" - {item['message']}") logger.warning(item['message']) else: result_lines.append(f"{file_name}: 处理完成") # 将结果写入主Excel文件的不同列 for rule_name, result_str in results.items(): target_col = self.stats_processor.RULE_MAPPING.get(rule_name) if target_col: target_cell = sheet.cell(row=row_idx, column=target_col) target_cell.value = result_str elif operation_mode == "empty_check": # SCL格式检查模式 checksheet_path = self.checksheet_path_var.get() missing_data, marked_file_path = self.empty_cell_detector.detect_empty_cells( file_path, checksheet_path ) if missing_data: file_has_problems = True problem_files += 1 result_lines.append(f"{file_name}: 发现空单元格!") for item in missing_data: result_lines.append(f" - {item['message']}") logger.warning(item['message']) # 添加标记文件路径信息 if marked_file_path: result_lines.append(f"已生成标记文件: {marked_file_path}") else: result_lines.append(f"{file_name}: 无空单元格问题") found_files += 1 # 如果该行有文件存在问题,将E列单元格标红 if file_has_problems: self.highlight_cell(sheet, row_idx, 5) logger.info(f"行 {row_idx} E列单元格标记为红色(存在问题)") # 更新结果文本框 self.result_text.config(state=tk.NORMAL) self.result_text.insert( tk.END, f"行 {row_idx} 处理结果:\n" + "\n".join(result_lines) + "\n\n" ) self.result_text.see(tk.END) self.result_text.config(state=tk.DISABLED) processed_count += 1 # 保存修改后的Excel文件 - 每次处理保存数据 output_path = input_file.replace(".xlsx", "_processed.xlsx") wb.save(output_path) logger.info(f"结果已保存到: {output_path}") elapsed_time = time.time() - start_time status_msg = f"处理完成! 处理了 {processed_count} 个文件项, 耗时 {elapsed_time:.2f} 秒" if problem_files > 0: status_msg += f", {problem_files} 个文件存在问题" self.status_var.set(status_msg) logger.info(status_msg) # 更新结果文本框 self.result_text.config(state=tk.NORMAL) self.result_text.insert( tk.END, f"\n{status_msg}\n" f"结果已保存到: {output_path}\n" ) self.result_text.see(tk.END) self.result_text.config(state=tk.DISABLED) messagebox.showinfo("完成", status_msg) except Exception as e: error_msg = f"处理文件时出错: {str(e)}" logger.exception(f"处理文件时出错: {str(e)}") messagebox.showerror("错误", error_msg) self.status_var.set(f"错误: {str(e)}") # 更新结果文本框 self.result_text.config(state=tk.NORMAL) self.result_text.insert(tk.END, f"\n错误: {error_msg}\n") self.result_text.see(tk.END) self.result_text.config(state=tk.DISABLED) def view_log(self): """查看日志""" log_window = tk.Toplevel(self.root) log_window.title("处理日志") log_window.geometry("800x600") log_frame = ttk.Frame(log_window, padding="10") log_frame.pack(fill=tk.BOTH, expand=True) # 日志文本框 log_text = scrolledtext.ScrolledText( log_frame, wrap=tk.WORD, height=30 ) log_text.pack(fill=tk.BOTH, expand=True) # 读取日志文件 log_file = 'scl_processor.log' try: if not os.path.exists(log_file): with open(log_file, 'w', encoding='utf-8') as f: f.write("日志文件已创建,暂无记录\n") with open(log_file, 'r', encoding='utf-8') as f: log_content = f.read() log_text.insert(tk.END, log_content) except Exception as e: log_text.insert(tk.END, f"无法读取日志文件: {str(e)}") # 设置为只读 log_text.config(state=tk.DISABLED) # 添加刷新按钮 refresh_btn = ttk.Button(log_frame, text="刷新日志", command=lambda: self.refresh_log(log_text)) refresh_btn.pack(pady=5) logger.info("日志查看窗口已打开") def refresh_log(self, log_text): """刷新日志内容""" log_text.config(state=tk.NORMAL) log_text.delete(1.0, tk.END) try: with open('scl_processor.log', 'r', encoding='utf-8') as f: log_content = f.read() log_text.insert(tk.END, log_content) except Exception as e: log_text.insert(tk.END, f"刷新日志失败: {str(e)}") log_text.config(state=tk.DISABLED) log_text.see(tk.END) logger.info("日志已刷新") def export_config(self): """导出配置到文件""" config = { "prefix": self.prefix_var.get(), "log_level": self.log_level_var.get(), "operation_mode": self.operation_mode.get(), "tolerance": self.tolerance_var.get(), "checksheet_path": self.checksheet_path_var.get(), "scl_folder": self.scl_folder_var.get() } file_path = filedialog.asksaveasfilename( defaultextension=".json", filetypes=[("JSON 文件", "*.json"), ("所有文件", "*.*")] ) if file_path: try: with open(file_path, 'w', encoding='utf-8') as f: f.write(str(config)) messagebox.showinfo("成功", f"配置已导出到: {file_path}") logger.info(f"配置已导出到: {file_path}") except Exception as e: messagebox.showerror("错误", f"导出配置失败: {str(e)}") logger.error(f"导出配置失败: {str(e)}") def load_config(self): """从文件加载配置""" file_path = filedialog.askopenfilename( filetypes=[("JSON 文件", "*.json"), ("所有文件", "*.*")] ) self.scl_folder_var.set(config.get("scl_folder", "")) if file_path: try: with open(file_path, 'r', encoding='utf-8') as f: config = eval(f.read()) self.prefix_var.set(config.get("prefix", "SCL_")) self.log_level_var.set(config.get("log_level", "INFO")) self.operation_mode.set(config.get("operation_mode", "stats")) self.tolerance_var.set(config.get("tolerance", 30)) self.checksheet_path_var.set(config.get("checksheet_path", "")) self.change_log_level() messagebox.showinfo("成功", "配置已加载") logger.info(f"配置已从 {file_path} 加载") except Exception as e: messagebox.showerror("错误", f"加载配置失败: {str(e)}") logger.error(f"加载配置失败: {str(e)}") 给出完整的修改过后的这个类
最新发布
08-08
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值