SimpleFastOpenAtomicVisualiser
Loading...
Searching...
No Matches
ffmpegRecord.h
Go to the documentation of this file.
1#ifndef FFMPEGRECORD_H
2#define FFMPEGRECORD_H
3
4#include <stdexcept>
5#include <cstdint>
6#include <vector>
7#include <filesystem>
8#include <string>
9
10#include <glm/glm.hpp>
11
12extern "C"
13{
14 #include <libavcodec/avcodec.h>
15 #include <libavformat/avformat.h>
16 #include <libavutil/avutil.h>
17 #include <libavutil/imgutils.h>
18 #include <libavutil/time.h>
19 #include <libavutil/opt.h>
20 #include <libswscale/swscale.h>
21}
22
23#include <record.h>
24
31class FFmpegRecord : public Record
32{
33
34public:
35
45 (
46 std::filesystem::path file,
47 glm::ivec2 resolution,
49 std::string preset = "slow",
50 std::string profile = "high",
51 uint8_t crf = 0,
52 uint8_t cq = 0,
53 uint8_t qp = 0,
54 std::string codecName = "libx264",
57 uint32_t bitrate = 2000000
58 )
59 :
60 Record(file, resolution, fps), bitrate(bitrate)
61 {
62 outputFormat = av_guess_format(nullptr, file.extension().string().c_str(), nullptr);
63 if (!outputFormat)
64 {
65 throw std::runtime_error("Failed to create FFmpeg output format");
66 }
67
69 if (!codec)
70 {
71 throw std::runtime_error("Failed to create FFmpeg codec");
72 }
73
74 outputFormat->video_codec = codec->id;
75
76 if (avformat_alloc_output_context2(&outputContext, outputFormat, nullptr, file.string().c_str()))
77 {
78 throw std::runtime_error("Failed to create FFmpeg output context");
79 }
80
81 AVStream * stream = avformat_new_stream(outputContext, codec);
82 if (!stream)
83 {
84 throw std::runtime_error("Failed to create FFmpeg stream");
85 }
86
87 context = avcodec_alloc_context3(codec);
88 if (!context)
89 {
90 throw std::runtime_error("Failed to create FFmpeg codec context");
91 }
92
93 stream->codecpar->codec_id = outputFormat->video_codec;
94 stream->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
95 stream->codecpar->width = resolution.x;
96 stream->codecpar->height = resolution.y;
97 stream->codecpar->format = AV_PIX_FMT_YUV420P;
98 stream->codecpar->color_primaries = AVCOL_PRI_BT709;
99 stream->codecpar->color_space = AVCOL_SPC_BT709;
100 stream->codecpar->color_trc = AVCOL_TRC_BT709;
101 stream->codecpar->bit_rate = bitrate;
102 stream->time_base = (AVRational){ 1, fps };
103 stream->r_frame_rate = (AVRational){ fps, 1 };
104 stream->avg_frame_rate = (AVRational){ fps, 1 };
105 avcodec_parameters_to_context(context, stream->codecpar);
106 context->time_base = (AVRational){ 1, fps };
107 context->max_b_frames = max_b_frames;
108 context->gop_size = gop_size;
109 context->framerate = (AVRational){ fps, 1 };
110 context->pix_fmt = AV_PIX_FMT_YUV420P;
111
112 av_opt_set(context->priv_data, "preset", preset.c_str(), 0);
113 av_opt_set(context->priv_data, "profile", profile.c_str(), 0);
114 av_opt_set_int(context->priv_data, "cq", cq, 0);
115 av_opt_set_int(context->priv_data, "qp", qp, 0);
116 av_opt_set_int(context->priv_data, "crf", crf, 0);
117 avcodec_parameters_from_context(stream->codecpar, context);
118 }
119
121 {
122 if (videoFrame)
123 {
124 av_frame_free(&videoFrame);
125 }
126 if (context)
127 {
128 avcodec_free_context(&context);
129 }
130 if (outputContext)
131 {
132 avformat_free_context(outputContext);
133 }
134 if (scaleContext)
135 {
136 sws_freeContext(scaleContext);
137 }
138 }
139
145 void open()
146 {
147 if (fileOpen) { return; }
148 if ((avcodec_open2(context, codec, NULL)) < 0)
149 {
150 throw std::runtime_error("Failed to open FFmpeg codec");
151 }
152
153 if (!(outputFormat->flags & AVFMT_NOFILE))
154 {
155 auto code = avio_open(&outputContext->pb, file.string().c_str(), AVIO_FLAG_WRITE);
156 if ((code) < 0)
157 {
158 throw std::runtime_error("Failed to open video file");
159 }
160 }
161
162 if ((avformat_write_header(outputContext, NULL)) < 0)
163 {
164 throw std::runtime_error("Failed to write video header");
165 }
166
167 av_dump_format(outputContext, 0, file.string().c_str(), 1);
168 fileOpen = true;
169 }
170
176 void close()
177 {
178 if (!fileOpen) { return; }
179 fileOpen = false;
181 pkt->data = NULL;
182 pkt->size = 0;
183
184 while(true)
185 {
186 avcodec_send_frame(context, NULL);
187 if (avcodec_receive_packet(context, pkt) == 0)
188 {
189 av_interleaved_write_frame(outputContext, pkt);
191 }
192 else
193 {
194 break;
195 }
196 }
197
198 av_write_trailer(outputContext);
199 if (!(outputFormat->flags & AVFMT_NOFILE))
200 {
201 if (avio_close(outputContext->pb) < 0)
202 {
203 throw std::runtime_error("Failed to close file");
204 }
205 }
206 }
207
208private:
209
210 AVFrame * videoFrame = nullptr;
211 AVCodec * codec = nullptr;
212 AVStream * stream = nullptr;
213 AVCodecContext * context = nullptr;
214 SwsContext * scaleContext = nullptr;
215 AVFormatContext * outputContext = nullptr;
216 AVOutputFormat * outputFormat = nullptr;
217
218 uint32_t bitrate = 2000000;
219 uint16_t frameCounter = 0;
220
221 void write(const uint8_t * frame)
222 {
223 if (!videoFrame)
224 {
225 videoFrame = av_frame_alloc();
226 videoFrame->format = AV_PIX_FMT_YUV420P;
227 videoFrame->width = context->width;
228 videoFrame->height = context->height;
229 if ((av_frame_get_buffer(videoFrame, 0)) < 0)
230 {
231 throw std::runtime_error("Failed to allocate FFmpeg frame");
232 }
233 }
234 if (!scaleContext)
235 {
236 scaleContext = sws_getContext
237 (
238 context->width,
239 context->height,
241 context->width,
242 context->height,
245 0,
246 0,
247 0
248 );
249 }
250
251 // Fixed, stride should be 4x for RBGA data.
252 int stride[1] = {4 * context->width};
253
255 (
256 scaleContext,
257 (const uint8_t * const *)&frame,
258 stride,
259 0,
260 context->height,
261 videoFrame->data,
262 videoFrame->linesize
263 );
264
265 videoFrame->pts = (1.0 / float(fps)) * 15360 * (frameCounter++);
266 if ((avcodec_send_frame(context, videoFrame)) < 0)
267 {
268 throw std::runtime_error("Failed to send frame");
269 }
270
272 pkt->data = NULL;
273 pkt->size = 0;
274 pkt->flags |= AV_PKT_FLAG_KEY;
275 if (avcodec_receive_packet(context, pkt) == 0)
276 {
277 av_interleaved_write_frame(outputContext, pkt);
279 }
280 }
281};
282
283#endif /* FFMPEGRECORD_H */
An FFmpeg based Record.
Definition ffmpegRecord.h:32
void close()
Close the video file.
Definition ffmpegRecord.h:176
FFmpegRecord(std::filesystem::path file, glm::ivec2 resolution, uint8_t fps, std::string preset="slow", std::string profile="high", uint8_t crf=0, uint8_t cq=0, uint8_t qp=0, std::string codecName="libx264", uint32_t max_b_frames=0, uint32_t gop_size=1, uint32_t bitrate=2000000)
Construct a new FFmpeg based Record from a file path, resolution and fps.
Definition ffmpegRecord.h:45
void open()
Open the video file.
Definition ffmpegRecord.h:145
~FFmpegRecord()
Definition ffmpegRecord.h:120
An API for recording video.
Definition record.h:15
uint8_t fps
Definition record.h:135
std::filesystem::path file
Definition record.h:133
glm::ivec2 resolution
Definition record.h:134
bool fileOpen
Definition record.h:136
glm::vec< L, float, glm::qualifier::highp > vec
Definition commandLine.h:214