]> git.sesse.net Git - nageru/blobdiff - futatabi/main.cpp
Fix a Clang 19 warning.
[nageru] / futatabi / main.cpp
index e6556d37aa91bff568111d4da8cadc4e609f8ac5..00de13f5bc8002cd6fa36456cdaa9925e678c6bf 100644 (file)
@@ -75,7 +75,7 @@ Summary metric_received_frame_size_bytes;
 
 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];
@@ -105,6 +105,7 @@ FrameOnDisk write_frame(int stream_idx, int64_t pts, const uint8_t *data, size_t
        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)) {
@@ -130,6 +131,12 @@ FrameOnDisk write_frame(int stream_idx, int64_t pts, const uint8_t *data, size_t
                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);
 
@@ -138,6 +145,7 @@ FrameOnDisk write_frame(int stream_idx, int64_t pts, const uint8_t *data, size_t
        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);
@@ -193,7 +201,7 @@ int main(int argc, char **argv)
 {
        parse_flags(argc, argv);
        if (optind == argc) {
-               global_flags.stream_source = "multiangle.mp4";
+               global_flags.stream_source = "multicam.mp4";
                global_flags.slow_down_input = true;
        } else if (optind + 1 == argc) {
                global_flags.stream_source = argv[optind];
@@ -305,6 +313,18 @@ void load_frame_file(const char *filename, const string &basename, unsigned file
                abort();
        }
 
+       // Find the actual length of the file, since fseek() past the end of the file
+       // will succeed without an error.
+       if (fseek(fp, 0, SEEK_END) == -1) {
+               perror("fseek(SEEK_END)");
+               abort();
+       }
+       off_t file_len = ftell(fp);
+       if (fseek(fp, 0, SEEK_SET) == -1) {
+               perror("fseek(SEEK_SET)");
+               abort();
+       }
+
        size_t magic_offset = 0;
        size_t skipped_bytes = 0;
        while (!feof(fp) && !ferror(fp)) {
@@ -359,10 +379,12 @@ void load_frame_file(const char *filename, const string &basename, unsigned file
                }
                frame.filename_idx = filename_idx;
                frame.size = hdr.file_size();
+               frame.audio_size = hdr.audio_size();
 
-               if (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);
-                       continue;
+                       break;
                }
 
                if (hdr.stream_idx() >= 0 && hdr.stream_idx() < MAX_STREAMS) {
@@ -487,32 +509,58 @@ void record_thread_func()
                        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(
-                               &pkt, av_packet_unref);
-                       av_init_packet(&pkt);
-                       pkt.data = nullptr;
-                       pkt.size = 0;
+                       AVPacketWithDeleter pkt = av_packet_alloc_unique();
+                       pkt->data = nullptr;
+                       pkt->size = 0;
 
                        // TODO: Make it possible to abort av_read_frame() (use an interrupt callback);
                        // right now, should_quit will be ignored if it's hung on I/O.
-                       if (av_read_frame(format_ctx.get(), &pkt) != 0) {
+                       if (av_read_frame(format_ctx.get(), pkt.get()) != 0) {
                                break;
                        }
-                       if (pkt.stream_index >= MAX_STREAMS ||
-                           format_ctx->streams[pkt.stream_index]->codecpar->codec_type != AVMEDIA_TYPE_VIDEO) {
+
+                       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 ||
+                           stream->codecpar->codec_type != AVMEDIA_TYPE_VIDEO) {
                                continue;
                        }
 
-                       ++metric_received_frames[pkt.stream_index];
-                       metric_received_frame_size_bytes.count_event(pkt.size);
+                       ++metric_received_frames[pkt->stream_index];
+                       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;
-                       int64_t pts = av_rescale_q(pkt.pts, stream_timebase, AVRational{ 1, TIMEBASE });
+                       AVRational stream_timebase = stream->time_base;
+                       int64_t pts = av_rescale_q(pkt->pts, stream_timebase, AVRational{ 1, TIMEBASE });
 
                        // Translate offset into our stream.
                        if (last_pts == -1) {
@@ -521,11 +569,11 @@ void record_thread_func()
                        pts = std::max(pts + pts_offset, start_pts);
 
                        //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);
+                       //      pkt->stream_index, pts, pkt->size);
+                       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);
+                       post_to_main_thread([stream_index{pkt->stream_index}, frame] {
+                               global_mainwindow->display_frame(stream_index, frame);
                        });
 
                        if (last_pts != -1 && global_flags.slow_down_input) {