frame.frame.pts = stream.pts(i);
frame.frame.offset = stream.offset(i);
frame.frame.size = stream.file_size(i);
+ if (i < stream.audio_size_size()) {
+ frame.frame.audio_size = stream.audio_size(i);
+ } else {
+ frame.frame.audio_size = 0;
+ }
frames.push_back(frame);
}
}
stream->mutable_pts()->Reserve(frames.size());
stream->mutable_offset()->Reserve(frames.size());
stream->mutable_file_size()->Reserve(frames.size());
+ stream->mutable_audio_size()->Reserve(frames.size());
for (const FrameOnDiskAndStreamIdx &frame : frames) {
if (frame.stream_idx != stream_idx) {
continue;
stream->add_pts(frame.frame.pts);
stream->add_offset(frame.frame.offset);
stream->add_file_size(frame.frame.size);
+ stream->add_audio_size(frame.frame.size);
}
}
string serialized;
namespace {
-FrameOnDisk write_frame(int stream_idx, int64_t pts, const uint8_t *data, size_t size, DB *db)
+FrameOnDisk write_frame(int stream_idx, int64_t pts, const uint8_t *data, size_t size, vector<uint32_t> audio, DB *db)
{
if (open_frame_files.count(stream_idx) == 0) {
char filename[256];
hdr.set_stream_idx(stream_idx);
hdr.set_pts(pts);
hdr.set_file_size(size);
+ hdr.set_audio_size(audio.size() * sizeof(audio[0]));
string serialized;
if (!hdr.SerializeToString(&serialized)) {
perror("fwrite");
abort();
}
+ if (audio.size() > 0) {
+ if (fwrite(audio.data(), hdr.audio_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);
frame.filename_idx = filename_idx;
frame.offset = offset;
frame.size = size;
+ frame.audio_size = audio.size() * sizeof(audio[0]);
{
lock_guard<mutex> lock(frame_mu);
}
frame.filename_idx = filename_idx;
frame.size = hdr.file_size();
+ frame.audio_size = hdr.audio_size();
- if (frame.offset + frame.size > file_len ||
- fseek(fp, frame.offset + frame.size, SEEK_SET) == -1) {
+ if (frame.offset + frame.size + frame.audio_size > file_len ||
+ fseek(fp, frame.offset + frame.size + frame.audio_size, SEEK_SET) == -1) {
fprintf(stderr, "WARNING: %s: Could not seek past frame (probably truncated).\n", filename);
break;
}
continue;
}
- int64_t last_pts = -1;
+ // Match any audio streams to video streams, sequentially.
+ vector<int> video_stream_idx, audio_stream_idx;
+ for (unsigned i = 0; i < format_ctx->nb_streams; ++i) {
+ if (format_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
+ video_stream_idx.push_back(i);
+ } else if (format_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
+ audio_stream_idx.push_back(i);
+ }
+ }
+ unordered_map<int, int> audio_stream_to_video_stream_idx;
+ for (size_t i = 0; i < min(video_stream_idx.size(), audio_stream_idx.size()); ++i) {
+ audio_stream_to_video_stream_idx[audio_stream_idx[i]] = video_stream_idx[i];
+ }
+ vector<uint32_t> pending_audio[MAX_STREAMS];
+ int64_t last_pts = -1;
while (!should_quit.load()) {
AVPacket pkt;
unique_ptr<AVPacket, decltype(av_packet_unref) *> pkt_cleanup(
if (av_read_frame(format_ctx.get(), &pkt) != 0) {
break;
}
+
+ AVStream *stream = format_ctx->streams[pkt.stream_index];
+ if (stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO &&
+ audio_stream_to_video_stream_idx.count(pkt.stream_index)) {
+ if ((pkt.size % (sizeof(uint32_t) * 2)) != 0) {
+ fprintf(stderr, "Audio stream %u had a packet of strange length %d, ignoring.\n",
+ pkt.stream_index, pkt.size);
+ } else {
+ // TODO: Endianness?
+ const uint32_t *begin = (const uint32_t *)pkt.data;
+ const uint32_t *end = (const uint32_t *)(pkt.data + pkt.size);
+ pending_audio[audio_stream_to_video_stream_idx[pkt.stream_index]].assign(begin, end);
+ }
+ }
+
if (pkt.stream_index >= MAX_STREAMS ||
- format_ctx->streams[pkt.stream_index]->codecpar->codec_type != AVMEDIA_TYPE_VIDEO) {
+ stream->codecpar->codec_type != AVMEDIA_TYPE_VIDEO) {
continue;
}
metric_received_frame_size_bytes.count_event(pkt.size);
// Convert pts to our own timebase.
- AVRational stream_timebase = format_ctx->streams[pkt.stream_index]->time_base;
+ AVRational stream_timebase = stream->time_base;
int64_t pts = av_rescale_q(pkt.pts, stream_timebase, AVRational{ 1, TIMEBASE });
// Translate offset into our stream.
//fprintf(stderr, "Got a frame from camera %d, pts = %ld, size = %d\n",
// pkt.stream_index, pts, pkt.size);
- FrameOnDisk frame = write_frame(pkt.stream_index, pts, pkt.data, pkt.size, &db);
+ FrameOnDisk frame = write_frame(pkt.stream_index, pts, pkt.data, pkt.size, move(pending_audio[pkt.stream_index]), &db);
post_to_main_thread([pkt, frame] {
global_mainwindow->display_frame(pkt.stream_index, frame);