PHP4用户手册:函数->fseek (转)

本文详细介绍了 PHP 中的 fseek 函数,该函数用于定位文件指针。文章解释了如何使用 fseek 设置文件指针的新位置,并提供了关于 whence 参数的说明。此外,还提及了 fseek 的返回值及一些注意事项。
PHP4用户手册:函数->fseek (转)[@more@]

fseek

(PHP 3, PHP 4 >= 4.0.0)

fseek -- 定位一个 文件的指针

描述

int fseek (int fp, int offset [, int whence])

为这个文件的引用fp设置文件指示器。这个新的位置,是从文件开始以字节为标准由offset指定的,或由 whence指定的位置开始。 whence的值见下:

SEEK_SET - 从文件开始; SEEK_CUR - 从当前位置; SEEK_END - 从文件结尾开始。

如果whence 没有指定,默认为 SEEK_SET。

成功,返回0;否则,返回-1。注意定位到结束符 EOF 不是一个错误。

如果使用"http://" or "FTP://"格式的服务器脚本类/PHP/PHP用户手册/fancy/function.fopen.html">fopen() 返回的指针不可以使用本函数

注意: whence 参数必须在PHP 4.0 RC1以上的版本中使用。

参见 ftell()rewind().



来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/10752043/viewspace-991070/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/10752043/viewspace-991070/

// 替换 avdm_get_next_block int get_local_video_block(GOPReader *reader, avdm_block_t *block) { // 清空旧数据 // if (reader->gop_buffer) // { // free(reader->gop_buffer); // reader->gop_buffer = NULL; // reader->gop_buffer_size = 0; // STM_INFO("clean old data?"); // } uint8_t nal[1024 * 1024]; // 1MB NAL buffer int nal_size; int found_i = 0; while (read_nal_unit(reader->fp, nal, sizeof(nal), &nal_size)) { if (is_i_frame(nal, nal_size)) { if (found_i) { // 第二个I帧表示上一个GOP结束,回退文件指针 fseek(reader->fp, -nal_size, SEEK_CUR); break; } found_i = 1; // block->unique_info.video.iframe_type = 5; // IDR帧 STM_INFO("is_i_frame(), nal_size=%d", nal_size); } // 扩展缓冲区 reader->gop_buffer = realloc(reader->gop_buffer, reader->gop_buffer_size + nal_size); memcpy(reader->gop_buffer + reader->gop_buffer_size, nal, nal_size); reader->gop_buffer_size += nal_size; } if (!found_i && reader->gop_buffer_size == 0) { return -1; // 没有读取到有效数据 } // 获取文件大小 fseek(fp, 0, SEEK_END); long file_len = ftell(fp); rewind(fp); // 分配内存 block->addr = (U8 *)malloc(file_len); if (!block->addr) { fclose(fp); return -1; } size_t read_len = fread(block->addr, 1, file_len, fp); fclose(fp); if (read_len != file_len) { free(block->addr); return -1; } // 填充 avdm_block_t 字段 block->addr = reader->gop_buffer; block->len = reader->gop_buffer_size; block->start_time = reader->last_pts; // 简单模拟:假设每帧间隔为 3600 ticks(90kHz 时钟) block->unitCnt = 1; // 可以根据实际帧数动态计算 reader->last_pts += 3600 * block->unitCnt; block->end_time = reader->last_pts; return 0; }怎么改
09-18
你的代码目标是从一个视频文件中读取一个完整的 GOP(Group of Pictures),以 I 帧开始,到下一个 I 帧之前结束,并将该 GOP 数据填充到 `avdm_block_t` 结构体中。但当前实现存在几个严重问题: --- ### ❌ 主要问题分析 1. **逻辑混乱:先收集数据,又试图重新加载整个文件** - 你在 `while(read_nal_unit)` 循环中逐步构建 `gop_buffer`。 - 但在循环结束后,你却: ```c fseek(fp, 0, SEEK_END); ... fread(block->addr, 1, file_len, fp); ``` 这完全覆盖了前面辛苦收集的 GOP 数据!这是错误的核心。 2. **重复赋值 `block->addr`** - 先用 `malloc(file_len)` 分配并读取整个文件 → 赋给 `block->addr` - 然后又执行:`block->addr = reader->gop_buffer;` - 导致内存泄漏(前一次 malloc 的内存丢失)且可能使用已释放或非法指针。 3. **文件指针操作冲突** - 在 `read_nal_unit` 中移动了 `fp`,然后在后面 `fseek(fp, 0, SEEK_END)` 又操作 `fp`,但此时 `fp` 已经不是从头开始了。 - 更危险的是:`fclose(fp);` 多次关闭?函数内部没有检查是否已关闭。 4. **未正确初始化和清理资源** - `gop_buffer` 没有初始化为 NULL,可能导致 `realloc` 出错。 - 缺少对 `reader->gop_buffer` 是否为空的判断。 5. **缺少关键字段设置** - 如 `block->unique_info.video.iframe_type` 应设为 IDR 类型(I帧)。 - 时间戳模拟可以接受,但需确保一致性。 --- ## ✅ 正确方案:只读取一个 GOP 并返回其数据 我们应做的是: - 从当前位置读 NAL 单元; - 遇到第一个 I 帧开始记录; - 继续读直到遇到第二个 I 帧,回退指针并停止; - 把这一段 GOP 数据(多个 NAL)打包进 `block->addr`; - 不再去加载整个文件! --- ### ✅ 修改后的正确版本(C语言) ```c int get_local_video_block(GOPReader *reader, avdm_block_t *block) { // 初始化 buffer(如果之前没清空) if (reader->gop_buffer) { free(reader->gop_buffer); reader->gop_buffer = NULL; reader->gop_buffer_size = 0; } uint8_t nal[1024 * 1024]; // 1MB 临时缓冲区 int nal_size; int found_i = 0; while (read_nal_unit(reader->fp, nal, sizeof(nal), &nal_size)) { if (is_i_frame(nal, nal_size)) { if (found_i) { // 第二个 I 帧到来,说明上一个 GOP 到此为止 // 回退当前 NAL 的长度,让下次从这里继续读 fseek(reader->fp, -nal_size, SEEK_CUR); break; } else { found_i = 1; STM_INFO("Found first I-frame, starting GOP capture, nal_size=%d", nal_size); } } // 扩展缓冲区并拷贝 NAL uint8_t *new_buf = realloc(reader->gop_buffer, reader->gop_buffer_size + nal_size); if (!new_buf) { STM_ERROR("Realloc failed"); free(reader->gop_buffer); reader->gop_buffer = NULL; reader->gop_buffer_size = 0; return -1; } reader->gop_buffer = new_buf; memcpy(reader->gop_buffer + reader->gop_buffer_size, nal, nal_size); reader->gop_buffer_size += nal_size; } // 如果没有找到 I 帧,也没有数据,则 EOF 或无效流 if (!found_i || reader->gop_buffer_size == 0) { STM_WARN("No I-frame or no data read"); free(reader->gop_buffer); reader->gop_buffer = NULL; reader->gop_buffer_size = 0; return -1; } // 填充 block 结构 block->addr = reader->gop_buffer; // 指向 GOP 数据 block->len = reader->gop_buffer_size; // 总长度 block->start_time = reader->last_pts; // 当前 PTS block->unitCnt = 1; // 可后续根据实际帧数更新 reader->last_pts += 3600 * block->unitCnt; // 更新时间戳(假设每帧 40ms @90kHz) block->end_time = reader->last_pts; // 设置视频信息(示例) block->unique_info.video.iframe_type = 5; // IDR I-frame // 注意:不要在这里释放 gop_buffer! // 因为 block->addr 指向它,应在外部使用完后由调用方释放 // 清除缓存指针,避免重复释放 reader->gop_buffer = NULL; reader->gop_buffer_size = 0; STM_INFO("GOP captured: size=%zu bytes, start PTS=%llu", block->len, block->start_time); return 0; } ``` --- ### ✅ 关键修改点解释 | 问题 | 修复方式 | |------|---------| | **多次赋值 `block->addr`** | 删除 `malloc(file_len)` 和 `fread` 整个文件的部分,只保留 `gop_buffer` 数据 | | **内存泄漏 / double free** | 使用完 `gop_buffer` 后将其置为 `NULL`,防止误操作 | | **文件指针乱跳** | 不再跳到文件开头,保持顺序读取 | | **未检测分配失败** | 添加 `realloc` 失败处理 | | **未清空旧数据** | 显式释放旧 `gop_buffer` | --- ### 📌 调用注意事项 你需要保证以下几点才能正常工作: 1. `reader->fp` 是打开的二进制文件指针(如 `.h264` 流文件); 2. `read_nal_unit()` 函数能正确解析 Annex B 格式的 NALU(带 `0x000001` 或 `0x00000001` 起始码); 3. `is_i_frame(nal, size)` 能通过 NAL header 和 Slice Header 判断是否为 I 帧; 4. 外部需要负责释放 `block->addr`,例如: ```c free(block->addr); ``` --- ### ✅ 示例:如何释放 block 内存 ```c void free_avdm_block(avdm_block_t *block) { if (block->addr) { free(block->addr); block->addr = NULL; } } ``` --- ### ✅ 推荐改进方向 - 使用环形缓冲区或预分配大 buffer 替代频繁 `realloc`; - 解析出真实的帧数 (`unitCnt`),而不是硬编码为 1; - 支持非 IDR 的 I 帧识别; - 加入 PTS/DTS 提取(从 AUD 或 SEI 中解析时间); --- ### ✅ 示例:简单 `is_i_frame` 实现(H.264) ```c int is_i_frame(uint8_t *nal, int len) { if (len < 1) return 0; uint8_t nal_type = nal[0] & 0x1F; // NAL Unit 类型为 5 表示 IDR 图像 if (nal_type == 5) return 1; // 非 IDR 的 I 帧可能是 SPS/PPS 后的第一个 P/B 帧?更复杂 // 进一步可解析 slice_header 中的 slice_type == 2/7 (I-slice) return 0; } ``` > ⚠️ 更精确的方式需要解析 Slice Header,但用于初步过滤,仅看 NAL type=5 是常见做法。 --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值