diff options
author | Kelly Rauchenberger <fefferburbia@gmail.com> | 2018-01-18 16:49:54 -0500 |
---|---|---|
committer | Kelly Rauchenberger <fefferburbia@gmail.com> | 2018-01-18 16:49:54 -0500 |
commit | da3bc860f66d34f233028e819beee32dd1c43dd8 (patch) | |
tree | 8cc7f5ad0a8dad69ea5c3ae0405f803d3ba80051 /director.cpp | |
parent | 46db0368fbee4cfba97178837e62f4469c4fa884 (diff) | |
download | sap-da3bc860f66d34f233028e819beee32dd1c43dd8.tar.gz sap-da3bc860f66d34f233028e819beee32dd1c43dd8.tar.bz2 sap-da3bc860f66d34f233028e819beee32dd1c43dd8.zip |
Modernized project
This rewrite brings the codebase of this project more in line with the format of the other bots, including things like C++ randomization, better abstraction, use of exceptions, etc. Notably, any FFMPEG objects that get allocated are wrapped in simple objects so that they get properly destroyed if an exception is thrown. Some more error detection and cleanliness stuff can probably be done but my wrists really hurt. Also updated librawr, and thus also removed the yaml-cpp submodule.
Diffstat (limited to 'director.cpp')
-rw-r--r-- | director.cpp | 378 |
1 files changed, 378 insertions, 0 deletions
diff --git a/director.cpp b/director.cpp new file mode 100644 index 0000000..24335da --- /dev/null +++ b/director.cpp | |||
@@ -0,0 +1,378 @@ | |||
1 | #include "director.h" | ||
2 | #include <dirent.h> | ||
3 | #include <iostream> | ||
4 | |||
5 | extern "C" { | ||
6 | #include <libavformat/avformat.h> | ||
7 | #include <libavcodec/avcodec.h> | ||
8 | #include <libavutil/imgutils.h> | ||
9 | #include <libswscale/swscale.h> | ||
10 | } | ||
11 | |||
12 | namespace ffmpeg { | ||
13 | |||
14 | class format { | ||
15 | public: | ||
16 | |||
17 | format(std::string videoPath) | ||
18 | { | ||
19 | if (avformat_open_input(&ptr_, videoPath.c_str(), nullptr, nullptr)) | ||
20 | { | ||
21 | throw ffmpeg_error("Could not open video " + videoPath); | ||
22 | } | ||
23 | |||
24 | if (avformat_find_stream_info(ptr_, nullptr)) | ||
25 | { | ||
26 | throw ffmpeg_error("Could not read stream"); | ||
27 | } | ||
28 | } | ||
29 | |||
30 | ~format() | ||
31 | { | ||
32 | avformat_close_input(&ptr_); | ||
33 | } | ||
34 | |||
35 | format(const format& other) = delete; | ||
36 | |||
37 | AVFormatContext* ptr() | ||
38 | { | ||
39 | return ptr_; | ||
40 | } | ||
41 | |||
42 | private: | ||
43 | |||
44 | AVFormatContext* ptr_ = nullptr; | ||
45 | }; | ||
46 | |||
47 | class codec { | ||
48 | public: | ||
49 | |||
50 | codec( | ||
51 | format& fmt, | ||
52 | AVStream* st) | ||
53 | { | ||
54 | AVCodec* dec = avcodec_find_decoder(st->codecpar->codec_id); | ||
55 | if (!dec) | ||
56 | { | ||
57 | throw ffmpeg_error("Failed to find codec"); | ||
58 | } | ||
59 | |||
60 | ptr_ = avcodec_alloc_context3(dec); | ||
61 | if (!ptr_) | ||
62 | { | ||
63 | throw ffmpeg_error("Failed to allocate codec context"); | ||
64 | } | ||
65 | |||
66 | if (avcodec_parameters_to_context(ptr_, st->codecpar) < 0) | ||
67 | { | ||
68 | throw ffmpeg_error("Failed to copy codec parameters to decoder"); | ||
69 | } | ||
70 | |||
71 | // Init the decoders, with or without reference counting | ||
72 | AVDictionary* opts = nullptr; | ||
73 | av_dict_set(&opts, "refcounted_frames", "0", 0); | ||
74 | |||
75 | if (avcodec_open2(ptr_, dec, &opts) < 0) | ||
76 | { | ||
77 | throw ffmpeg_error("Failed to open codec"); | ||
78 | } | ||
79 | } | ||
80 | |||
81 | codec(const codec& other) = delete; | ||
82 | |||
83 | ~codec() | ||
84 | { | ||
85 | avcodec_free_context(&ptr_); | ||
86 | } | ||
87 | |||
88 | int getWidth() const | ||
89 | { | ||
90 | return ptr_->width; | ||
91 | } | ||
92 | |||
93 | int getHeight() const | ||
94 | { | ||
95 | return ptr_->height; | ||
96 | } | ||
97 | |||
98 | enum AVPixelFormat getPixelFormat() const | ||
99 | { | ||
100 | return ptr_->pix_fmt; | ||
101 | } | ||
102 | |||
103 | AVCodecContext* ptr() | ||
104 | { | ||
105 | return ptr_; | ||
106 | } | ||
107 | |||
108 | private: | ||
109 | |||
110 | AVCodecContext* ptr_ = nullptr; | ||
111 | }; | ||
112 | |||
113 | class packet { | ||
114 | public: | ||
115 | |||
116 | packet() | ||
117 | { | ||
118 | ptr_ = av_packet_alloc(); | ||
119 | } | ||
120 | |||
121 | ~packet() | ||
122 | { | ||
123 | av_packet_free(&ptr_); | ||
124 | } | ||
125 | |||
126 | packet(const packet& other) = delete; | ||
127 | |||
128 | int getStreamIndex() const | ||
129 | { | ||
130 | return ptr_->stream_index; | ||
131 | } | ||
132 | |||
133 | AVPacket* ptr() | ||
134 | { | ||
135 | return ptr_; | ||
136 | } | ||
137 | |||
138 | int getFlags() const | ||
139 | { | ||
140 | return ptr_->flags; | ||
141 | } | ||
142 | |||
143 | private: | ||
144 | |||
145 | AVPacket* ptr_ = nullptr; | ||
146 | }; | ||
147 | |||
148 | class frame { | ||
149 | public: | ||
150 | |||
151 | frame() | ||
152 | { | ||
153 | ptr_ = av_frame_alloc(); | ||
154 | } | ||
155 | |||
156 | ~frame() | ||
157 | { | ||
158 | av_frame_free(&ptr_); | ||
159 | } | ||
160 | |||
161 | frame(const frame& other) = delete; | ||
162 | |||
163 | uint8_t** getData() | ||
164 | { | ||
165 | return ptr_->data; | ||
166 | } | ||
167 | |||
168 | int* getLinesize() | ||
169 | { | ||
170 | return ptr_->linesize; | ||
171 | } | ||
172 | |||
173 | AVFrame* ptr() | ||
174 | { | ||
175 | return ptr_; | ||
176 | } | ||
177 | |||
178 | private: | ||
179 | |||
180 | AVFrame* ptr_ = nullptr; | ||
181 | }; | ||
182 | |||
183 | class sws { | ||
184 | public: | ||
185 | |||
186 | sws( | ||
187 | int srcW, | ||
188 | int srcH, | ||
189 | enum AVPixelFormat srcFormat, | ||
190 | int dstW, | ||
191 | int dstH, | ||
192 | enum AVPixelFormat dstFormat, | ||
193 | int flags, | ||
194 | SwsFilter* srcFilter, | ||
195 | SwsFilter* dstFilter, | ||
196 | const double* param) | ||
197 | { | ||
198 | ptr_ = sws_getContext( | ||
199 | srcW, | ||
200 | srcH, | ||
201 | srcFormat, | ||
202 | dstW, | ||
203 | dstH, | ||
204 | dstFormat, | ||
205 | flags, | ||
206 | srcFilter, | ||
207 | dstFilter, | ||
208 | param); | ||
209 | |||
210 | if (ptr_ == NULL) | ||
211 | { | ||
212 | throw ffmpeg_error("Could not allocate sws context"); | ||
213 | } | ||
214 | } | ||
215 | |||
216 | ~sws() | ||
217 | { | ||
218 | sws_freeContext(ptr_); | ||
219 | } | ||
220 | |||
221 | sws(const sws& other) = delete; | ||
222 | |||
223 | void scale( | ||
224 | const uint8_t* const srcSlice[], | ||
225 | const int srcStride[], | ||
226 | int srcSliceY, | ||
227 | int srcSliceH, | ||
228 | uint8_t* const dst[], | ||
229 | const int dstStride[]) | ||
230 | { | ||
231 | sws_scale( | ||
232 | ptr_, | ||
233 | srcSlice, | ||
234 | srcStride, | ||
235 | srcSliceY, | ||
236 | srcSliceH, | ||
237 | dst, | ||
238 | dstStride); | ||
239 | } | ||
240 | |||
241 | private: | ||
242 | |||
243 | SwsContext* ptr_; | ||
244 | }; | ||
245 | |||
246 | } | ||
247 | |||
248 | director::director(std::string videosPath) : videosPath_(videosPath) | ||
249 | { | ||
250 | DIR* videodir; | ||
251 | struct dirent* ent; | ||
252 | if ((videodir = opendir(videosPath.c_str())) == nullptr) | ||
253 | { | ||
254 | throw std::invalid_argument("Couldn't find videos"); | ||
255 | } | ||
256 | |||
257 | while ((ent = readdir(videodir)) != nullptr) | ||
258 | { | ||
259 | std::string dname(ent->d_name); | ||
260 | if (dname.find(".mp4") != std::string::npos) | ||
261 | { | ||
262 | videos_.push_back(dname); | ||
263 | } | ||
264 | } | ||
265 | |||
266 | closedir(videodir); | ||
267 | } | ||
268 | |||
269 | Magick::Image director::generate(std::mt19937& rng) const | ||
270 | { | ||
271 | std::uniform_int_distribution<size_t> videoDist(0, videos_.size() - 1); | ||
272 | std::string video = videosPath_ + "/" + videos_[videoDist(rng)]; | ||
273 | |||
274 | ffmpeg::format fmt(video); | ||
275 | |||
276 | int streamIdx = | ||
277 | av_find_best_stream(fmt.ptr(), AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0); | ||
278 | |||
279 | if (streamIdx < 0) | ||
280 | { | ||
281 | throw ffmpeg_error("Could not find stream"); | ||
282 | } | ||
283 | |||
284 | AVStream* stream = fmt.ptr()->streams[streamIdx]; | ||
285 | |||
286 | ffmpeg::codec cdc(fmt, stream); | ||
287 | |||
288 | size_t codecw = cdc.getWidth(); | ||
289 | size_t codech = cdc.getHeight(); | ||
290 | |||
291 | std::uniform_int_distribution<int64_t> frameDist(0, stream->duration - 1); | ||
292 | int64_t seek = frameDist(rng); | ||
293 | |||
294 | std::cout << seek << std::endl; | ||
295 | |||
296 | if (av_seek_frame(fmt.ptr(), streamIdx, seek, 0)) | ||
297 | { | ||
298 | throw ffmpeg_error("Could not seek"); | ||
299 | } | ||
300 | |||
301 | ffmpeg::frame frame; | ||
302 | ffmpeg::frame converted; | ||
303 | |||
304 | av_image_alloc( | ||
305 | converted.getData(), | ||
306 | converted.getLinesize(), | ||
307 | codecw, | ||
308 | codech, | ||
309 | AV_PIX_FMT_RGB24, | ||
310 | 1); | ||
311 | |||
312 | ffmpeg::packet pkt; | ||
313 | |||
314 | do | ||
315 | { | ||
316 | if (av_read_frame(fmt.ptr(), pkt.ptr()) < 0) | ||
317 | { | ||
318 | throw ffmpeg_error("Could not read frame"); | ||
319 | } | ||
320 | } while ((pkt.getStreamIndex() != streamIdx) | ||
321 | || !(pkt.getFlags() & AV_PKT_FLAG_KEY)); | ||
322 | |||
323 | int ret; | ||
324 | do { | ||
325 | if (avcodec_send_packet(cdc.ptr(), pkt.ptr()) < 0) | ||
326 | { | ||
327 | throw ffmpeg_error("Could not send packet"); | ||
328 | } | ||
329 | |||
330 | ret = avcodec_receive_frame(cdc.ptr(), frame.ptr()); | ||
331 | } while (ret == AVERROR(EAGAIN)); | ||
332 | |||
333 | if (ret < 0) | ||
334 | { | ||
335 | throw ffmpeg_error("Could not decode frame"); | ||
336 | } | ||
337 | |||
338 | ffmpeg::sws scaler( | ||
339 | cdc.getWidth(), | ||
340 | cdc.getHeight(), | ||
341 | cdc.getPixelFormat(), | ||
342 | cdc.getWidth(), | ||
343 | cdc.getHeight(), | ||
344 | AV_PIX_FMT_RGB24, | ||
345 | 0, nullptr, nullptr, 0); | ||
346 | |||
347 | scaler.scale( | ||
348 | frame.getData(), | ||
349 | frame.getLinesize(), | ||
350 | 0, | ||
351 | codech, | ||
352 | converted.getData(), | ||
353 | converted.getLinesize()); | ||
354 | |||
355 | size_t buffer_size = av_image_get_buffer_size( | ||
356 | AV_PIX_FMT_RGB24, cdc.getWidth(), cdc.getHeight(), 1); | ||
357 | |||
358 | std::vector<uint8_t> buffer(buffer_size); | ||
359 | |||
360 | av_image_copy_to_buffer( | ||
361 | buffer.data(), | ||
362 | buffer_size, | ||
363 | converted.getData(), | ||
364 | converted.getLinesize(), | ||
365 | AV_PIX_FMT_RGB24, | ||
366 | cdc.getWidth(), | ||
367 | cdc.getHeight(), | ||
368 | 1); | ||
369 | |||
370 | size_t width = 1024; | ||
371 | size_t height = codech * width / codecw; | ||
372 | |||
373 | Magick::Image image; | ||
374 | image.read(codecw, codech, "RGB", Magick::CharPixel, buffer.data()); | ||
375 | image.zoom(Magick::Geometry(width, height)); | ||
376 | |||
377 | return image; | ||
378 | } | ||