X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=futatabi%2Fmain.cpp;h=00de13f5bc8002cd6fa36456cdaa9925e678c6bf;hb=3256213f93385cc56281c616e0347ff5fa2b70ac;hp=e6556d37aa91bff568111d4da8cadc4e609f8ac5;hpb=9ffd4f03f314cc6e0254449593def95c9bc203d6;p=nageru diff --git a/futatabi/main.cpp b/futatabi/main.cpp index e6556d3..00de13f 100644 --- a/futatabi/main.cpp +++ b/futatabi/main.cpp @@ -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 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 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 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 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 pending_audio[MAX_STREAMS]; + int64_t last_pts = -1; while (!should_quit.load()) { - AVPacket pkt; - unique_ptr 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) {