+ if (open_frame_files.count(stream_idx) == 0) {
+ char filename[256];
+ snprintf(filename, sizeof(filename), "%s/frames/cam%d-pts%09ld.frames",
+ global_flags.working_directory.c_str(), stream_idx, pts);
+ FILE *fp = fopen(filename, "wb");
+ if (fp == nullptr) {
+ perror(filename);
+ exit(1);
+ }
+
+ lock_guard<mutex> lock(frame_mu);
+ int filename_idx = frame_filenames.size();
+ frame_filenames.push_back(filename);
+ open_frame_files[stream_idx] = FrameFile{ fp, filename_idx, 0 };
+ }
+
+ FrameFile &file = open_frame_files[stream_idx];
+ string filename;
+ {
+ lock_guard<mutex> lock(frame_mu);
+ filename = frame_filenames[file.filename_idx];
+ }
+
+ FrameHeaderProto hdr;
+ hdr.set_stream_idx(stream_idx);
+ hdr.set_pts(pts);
+ hdr.set_file_size(size);
+
+ string serialized;
+ if (!hdr.SerializeToString(&serialized)) {
+ fprintf(stderr, "Frame header serialization failed.\n");
+ exit(1);
+ }
+ uint32_t len = htonl(serialized.size());
+
+ if (fwrite(frame_magic, frame_magic_len, 1, file.fp) != 1) {
+ perror("fwrite");
+ exit(1);
+ }
+ if (fwrite(&len, sizeof(len), 1, file.fp) != 1) {
+ perror("fwrite");
+ exit(1);
+ }
+ if (fwrite(serialized.data(), serialized.size(), 1, file.fp) != 1) {
+ perror("fwrite");
+ exit(1);
+ }
+ off_t offset = ftell(file.fp);
+ if (fwrite(data, size, 1, file.fp) != 1) {
+ perror("fwrite");
+ exit(1);
+ }
+ fflush(file.fp); // No fsync(), though. We can accept losing a few frames.
+ global_disk_space_estimator->report_write(filename, 8 + sizeof(len) + serialized.size() + size, pts);
+
+ if (++file.frames_written_so_far >= 1000) {
+ // Start a new file next time.
+ if (fclose(file.fp) != 0) {
+ perror("fclose");
+ exit(1);
+ }
+ open_frame_files.erase(stream_idx);
+
+ // TODO: Write to SQLite.
+ }
+
+ FrameOnDisk frame;
+ frame.pts = pts;
+ frame.filename_idx = file.filename_idx;
+ frame.offset = offset;
+ frame.size = size;
+
+ {
+ lock_guard<mutex> lock(frame_mu);
+ assert(stream_idx < MAX_STREAMS);
+ frames[stream_idx].push_back(frame);
+ }
+
+ return frame;