summary refs log tree commit diff stats
path: root/director.cpp
diff options
context:
space:
mode:
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}