summary refs log tree commit diff stats
path: root/director.cpp
diff options
context:
space:
mode:
authorKelly Rauchenberger <fefferburbia@gmail.com>2018-01-18 16:49:54 -0500
committerKelly Rauchenberger <fefferburbia@gmail.com>2018-01-18 16:49:54 -0500
commitda3bc860f66d34f233028e819beee32dd1c43dd8 (patch)
tree8cc7f5ad0a8dad69ea5c3ae0405f803d3ba80051 /director.cpp
parent46db0368fbee4cfba97178837e62f4469c4fa884 (diff)
downloadsap-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.cpp378
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
5extern "C" {
6#include <libavformat/avformat.h>
7#include <libavcodec/avcodec.h>
8#include <libavutil/imgutils.h>
9#include <libswscale/swscale.h>
10}
11
12namespace 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
248director::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
269Magick::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}