]> git.sesse.net Git - nageru/blob - futatabi/frame_on_disk.cpp
Make multitrack export include audio.
[nageru] / futatabi / frame_on_disk.cpp
1 #include "frame_on_disk.h"
2
3 #include "shared/metrics.h"
4
5 #include <atomic>
6 #include <chrono>
7 #include <fcntl.h>
8 #include <mutex>
9 #include <unistd.h>
10
11 using namespace std;
12 using namespace std::chrono;
13
14 namespace {
15
16 // There can be multiple FrameReader classes, so make all the metrics static.
17 once_flag frame_metrics_inited;
18
19 atomic<int64_t> metric_frame_opened_files{ 0 };
20 atomic<int64_t> metric_frame_closed_files{ 0 };
21 atomic<int64_t> metric_frame_read_bytes{ 0 };
22 atomic<int64_t> metric_frame_read_frames{ 0 };
23
24 Summary metric_frame_read_time_seconds;
25
26 }  // namespace
27
28 FrameReader::FrameReader()
29 {
30         call_once(frame_metrics_inited, [] {
31                 global_metrics.add("frame_opened_files", &metric_frame_opened_files);
32                 global_metrics.add("frame_closed_files", &metric_frame_closed_files);
33                 global_metrics.add("frame_read_bytes", &metric_frame_read_bytes);
34                 global_metrics.add("frame_read_frames", &metric_frame_read_frames);
35
36                 vector<double> quantiles{ 0.01, 0.1, 0.25, 0.5, 0.75, 0.9, 0.99 };
37                 metric_frame_read_time_seconds.init(quantiles, 60.0);
38                 global_metrics.add("frame_read_time_seconds", &metric_frame_read_time_seconds);
39         });
40 }
41
42 FrameReader::~FrameReader()
43 {
44         if (fd != -1) {
45                 close(fd);
46                 ++metric_frame_closed_files;
47         }
48 }
49
50 namespace {
51
52 string read_string(int fd, size_t size, off_t offset)
53 {
54         string str;
55         str.resize(size);
56         size_t str_offset = 0;
57         while (str_offset < size) {
58                 int ret = pread(fd, &str[str_offset], size - str_offset, offset + str_offset);
59                 if (ret <= 0) {
60                         perror("pread");
61                         abort();
62                 }
63
64                 str_offset += ret;
65         }
66         return str;
67 }
68
69 }  // namespace
70
71 FrameReader::Frame FrameReader::read_frame(FrameOnDisk frame, bool read_audio)
72 {
73         steady_clock::time_point start = steady_clock::now();
74
75         if (int(frame.filename_idx) != last_filename_idx) {
76                 if (fd != -1) {
77                         close(fd);  // Ignore errors.
78                         ++metric_frame_closed_files;
79                 }
80
81                 string filename;
82                 {
83                         lock_guard<mutex> lock(frame_mu);
84                         filename = frame_filenames[frame.filename_idx];
85                 }
86
87                 fd = open(filename.c_str(), O_RDONLY);
88                 if (fd == -1) {
89                         perror(filename.c_str());
90                         abort();
91                 }
92
93                 // We want readahead. (Ignore errors.)
94                 posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL);
95
96                 last_filename_idx = frame.filename_idx;
97                 ++metric_frame_opened_files;
98         }
99
100         Frame ret;
101         ret.video = read_string(fd, frame.size, frame.offset);
102         if (read_audio) {
103                 ret.audio = read_string(fd, frame.audio_size, frame.offset + frame.size);
104         }
105
106         steady_clock::time_point stop = steady_clock::now();
107         metric_frame_read_time_seconds.count_event(duration<double>(stop - start).count());
108
109         metric_frame_read_bytes += frame.size;
110         ++metric_frame_read_frames;
111
112         return ret;
113 }