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

C++读取.mp4文件为字节数组时读取循环仅执行一次问题

C++二进制读取MP4文件异常修复方案

问题现象

需要在C++中读取.mp4文件为字节数组,通过UDP套接字发送,发送逻辑无异常,但文件读取环节出错:目标文件大小208KB,读取循环仅运行1次。
原有问题代码:

#include <iostream>
#include <bits/stdc++.h>
#include "VideoPacket.h"
#include <winsock2.h>
#include <stdlib.h>
#include <stdio.h>

using namespace std;

class VideoPacket {
public:
    bool flag;
    string filename;
    int packetNum;
    int index;
    char *data;
    time_t timestamp{};

    explicit VideoPacket(bool p_flag, string &p_filename, int p_packetNum, char *p_data, int p_index) {
        flag = p_flag;
        packetNum = p_packetNum;
        index = p_index;
        data = p_data;
        filename = p_filename;
    }
};

vector<VideoPacket> readFile(string &filename) {
    ifstream fin(filename);
    vector<VideoPacket> videoData;
    char buff[4096];

    int counter = 0;
    do {
        fin.read(buff, sizeof(buff));
        cout << buff << endl;
        VideoPacket newPacket(true, filename, -1, buff, counter);
        counter++;
        videoData.push_back(newPacket);
    } while (!fin.eof());
    return videoData;
}

int main() {
    string filename = R"(videoSrc)";
    vector<VideoPacket> videoData = readFile(filename);
    return 0;
}

根因分析

  • 未以二进制模式打开文件:ifstream默认以文本模式打开文件,MP4是二进制格式文件,Windows平台下文本模式读取时遇到0x1A字节会被判定为文件结束符,直接终止读取,这是循环仅执行一次的核心原因。
  • 未校验文件打开状态:代码中传入的文件名为videoSrc,未加.mp4后缀,也没有判断文件是否成功打开,如果文件路径/后缀不匹配,文件打开失败后续读取逻辑直接失效。
  • 内存指针悬空+数据覆盖:定义的buff是readFile函数内的栈上局部数组,所有VideoPacket实例的data指针都指向这同一块内存,每次循环读取都会覆盖buff原有内容,且函数返回后栈内存被回收,data指针直接成为野指针,后续UDP发送会读到无效数据。
  • 循环逻辑错误:使用do-while搭配eof()判断会多构造一个无效数据包——哪怕最后一次读取已经失败、没有读到有效数据,也会先执行循环体创建无效包;同时没有调用gcount()获取每次实际读取的字节长度,默认每次读满4096字节,最后一个不足4096字节的包会携带脏数据。
  • 用cout输出二进制buff:cout打印char*时遇到\0就会停止输出,MP4二进制数据中存在大量\0字节,打印结果完全不具备参考性。

修复方案

  • 打开文件时追加ios::binary标记指定二进制读取模式
  • 打开文件后立即校验是否打开成功,失败直接返回错误
  • 每个数据包单独分配堆内存存储读取到的字节,避免指针悬空和数据覆盖,发送完成后要手动释放内存避免泄漏
  • 替换错误的循环逻辑,每次读取后先判断读取状态,通过gcount()拿到实际读取的字节数,再构造有效数据包
  • 补全正确的文件路径/后缀,不要用cout直接打印二进制缓冲区

修复后的参考代码:

#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <winsock2.h>
#include <ctime>
#include <cstring>

using namespace std;

class VideoPacket {
public:
    bool flag;
    string filename;
    int packetNum;
    int index;
    char *data;
    int dataLen;
    time_t timestamp;

    VideoPacket(bool p_flag, string p_filename, int p_packetNum, char *p_data, int p_len, int p_index) {
        flag = p_flag;
        packetNum = p_packetNum;
        index = p_index;
        dataLen = p_len;
        data = new char[p_len];
        memcpy(data, p_data, p_len);
        filename = p_filename;
        timestamp = time(nullptr);
    }

    ~VideoPacket() {
        delete[] data;
    }
};

vector<VideoPacket> readFile(string &filename) {
    ifstream fin(filename, ios::binary);
    vector<VideoPacket> videoData;
    const int PACKET_SIZE = 1472; // 适配公网UDP MTU,避免分片丢包
    char buff[PACKET_SIZE];

    if (!fin.is_open()) {
        cerr << "打开文件失败,检查路径是否正确: " << filename << endl;
        return videoData;
    }

    int counter = 0;
    while (true) {
        fin.read(buff, PACKET_SIZE);
        int readLen = fin.gcount();
        if (readLen <= 0) {
            break;
        }
        VideoPacket newPacket(true, filename, -1, buff, readLen, counter);
        counter++;
        videoData.push_back(newPacket);

        if (fin.eof()) {
            break;
        }
    }
    fin.close();
    return videoData;
}

int main() {
    string filename = R"(test.mp4)"; // 替换为实际文件路径
    vector<VideoPacket> videoData = readFile(filename);
    cout << "共读取到数据包数量: " << videoData.size() << endl;
    // 后续UDP发送逻辑
    return 0;
}

注意:UDP单次发送的数据包大小建议不超过1472字节,超过会触发IP分片,大幅提升公网传输的丢包概率。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 11:27:17