Eclipse SUMO - Simulation of Urban MObility
GUIVideoEncoder.h
Go to the documentation of this file.
1 /****************************************************************************/
2 // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 // Copyright (C) 2001-2024 German Aerospace Center (DLR) and others.
4 // This program and the accompanying materials are made available under the
5 // terms of the Eclipse Public License 2.0 which is available at
6 // https://www.eclipse.org/legal/epl-2.0/
7 // This Source Code may also be made available under the following Secondary
8 // Licenses when the conditions for such availability set forth in the Eclipse
9 // Public License 2.0 are satisfied: GNU General Public License, version 2
10 // or later which is available at
11 // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12 // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13 /****************************************************************************/
18 // A simple video encoder from RGBA pics to anything ffmpeg can handle.
19 // Tested with h264 only.
20 // Inspired by Lei Xiaohua, Philip Schneider and Fabrice Bellard, see
21 // https://github.com/leixiaohua1020/simplest_ffmpeg_video_encoder and
22 // https://github.com/codefromabove/FFmpegRGBAToYUV
23 /****************************************************************************/
24 #pragma once
25 #include <config.h>
26 
27 #include <stdio.h>
28 #include <iostream>
29 #include <stdexcept>
30 
31 #define __STDC_CONSTANT_MACROS
32 
33 #ifdef _MSC_VER
34 #pragma warning(push)
35 #pragma warning(disable: 4242 4244) // do not warn about integer conversions
36 #endif
37 #if __GNUC__ > 3
38 #pragma GCC diagnostic push
39 #pragma GCC diagnostic ignored "-Wpedantic"
40 #pragma GCC diagnostic ignored "-Wvariadic-macros"
41 #endif
42 extern "C"
43 {
44 #include <libavutil/opt.h>
45 #include <libavutil/imgutils.h>
46 #include <libavcodec/avcodec.h>
47 #include <libavformat/avformat.h>
48 #include <libswscale/swscale.h>
49 }
50 #ifdef _MSC_VER
51 #pragma warning(pop)
52 #endif
53 #if __GNUC__ > 3
54 #pragma GCC diagnostic pop
55 #endif
56 
58 #include <utils/common/ToString.h>
59 
60 
61 // ===========================================================================
62 // class definitions
63 // ===========================================================================
69 public:
70  GUIVideoEncoder(const char* const out_file, const int width, const int height, double frameDelay) {
71  //av_register_all();
72  avformat_alloc_output_context2(&myFormatContext, NULL, NULL, out_file);
73  if (myFormatContext == nullptr) {
74  throw ProcessError(TL("Unknown format!"));
75  }
76 
77  // @todo maybe warn about default and invalid framerates
78  int framerate = 25;
79  if (frameDelay > 0.) {
80  framerate = (int)(1000. / frameDelay);
81  if (framerate <= 0) {
82  framerate = 1;
83  }
84  }
85  AVStream* const video_st = avformat_new_stream(myFormatContext, 0);
86  video_st->time_base.num = 1;
87  video_st->time_base.den = framerate;
88 
89  const AVCodec* codec = avcodec_find_encoder(myFormatContext->oformat->video_codec);
90  if (codec == nullptr) {
91  WRITE_WARNING(TL("Unknown codec, falling back to HEVC!"));
92  codec = avcodec_find_encoder_by_name("libx265");
93  }
94  if (codec == nullptr) {
95  throw ProcessError(TL("Unknown codec!"));
96  }
97  //Param that must set
98  myCodecCtx = avcodec_alloc_context3(codec);
99  if (myCodecCtx == nullptr) {
100  throw ProcessError(TL("Could not allocate video codec context!"));
101  }
102  //pmyCodecCtx->codec_id =AV_CODEC_ID_HEVC;
103  //pmyCodecCtx->codec_id = pFormatCtx->oformat->video_codec;
104  //pmyCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;
105  myCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P;
106  // @todo maybe warn about one missing line for odd width or height
107  myCodecCtx->width = (width / 2) * 2;
108  myCodecCtx->height = (height / 2) * 2;
109  myCodecCtx->time_base.num = 1;
110  myCodecCtx->time_base.den = framerate;
111  myCodecCtx->framerate.num = framerate;
112  myCodecCtx->framerate.den = 1;
113  myCodecCtx->bit_rate = 4000000; // example has 400000
114  myCodecCtx->gop_size = 10; // example has 10
115  //H264
116  //pmyCodecCtx->me_range = 16;
117  //pmyCodecCtx->max_qdiff = 4;
118  //pmyCodecCtx->qcompress = 0.6;
119  //myCodecCtx->qmin = 10; // example does not set this
120  //myCodecCtx->qmax = 51; // example does not set this
121  //myCodecCtx->max_b_frames = 1; // example has 1
122 
123  // Set codec specific options
124  //H.264
125  if (myCodecCtx->codec_id == AV_CODEC_ID_H264) {
126  av_opt_set(myCodecCtx->priv_data, "preset", "slow", 0);
127  //av_opt_set(myCodecCtx->priv_data, "tune", "zerolatency", 0);
128  //av_opt_set(myCodecCtx->priv_data, "profile", "main", 0);
129  }
130  //H.265
131  if (myCodecCtx->codec_id == AV_CODEC_ID_HEVC) {
132  av_opt_set(myCodecCtx->priv_data, "preset", "ultrafast", 0);
133  av_opt_set(myCodecCtx->priv_data, "tune", "zero-latency", 0);
134  }
135  if (avcodec_open2(myCodecCtx, codec, nullptr) < 0) {
136  throw ProcessError(TL("Could not open codec!"));
137  }
138  avcodec_parameters_from_context(video_st->codecpar, myCodecCtx);
139 
140  myFrame = av_frame_alloc();
141  if (myFrame == nullptr) {
142  throw ProcessError(TL("Could not allocate video frame!"));
143  }
144  myFrame->format = myCodecCtx->pix_fmt;
145  myFrame->width = myCodecCtx->width;
146  myFrame->height = myCodecCtx->height;
147  if (av_frame_get_buffer(myFrame, 32) < 0) {
148  throw ProcessError(TL("Could not allocate the video frame data!"));
149  }
150  mySwsContext = sws_getContext(myCodecCtx->width, myCodecCtx->height, AV_PIX_FMT_RGBA,
151  myCodecCtx->width, myCodecCtx->height, AV_PIX_FMT_YUV420P,
152  0, 0, 0, 0);
153  //Open output URL
154  if (avio_open(&myFormatContext->pb, out_file, AVIO_FLAG_WRITE) < 0) {
155  throw ProcessError(TL("Failed to open output file!"));
156  }
157 
158  //Write File Header
159  if (avformat_write_header(myFormatContext, nullptr) < 0) {
160  throw ProcessError(TL("Failed to write file header!"));
161  }
162  myFrameIndex = 0;
163  myPkt = av_packet_alloc();
164  if (myPkt == nullptr) {
165  throw ProcessError(TL("Could not allocate video packet!"));
166  }
167  }
168 
170  int ret = 1;
171  if (!(myCodecCtx->codec->capabilities & AV_CODEC_CAP_DELAY)) {
172  ret = 0;
173  }
174  if (avcodec_send_frame(myCodecCtx, nullptr) < 0) {
175  WRITE_WARNING(TL("Error sending final frame!"));
176  ret = -1;
177  }
178  while (ret >= 0) {
179  ret = avcodec_receive_packet(myCodecCtx, myPkt);
180  if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
181  break;
182  } else if (ret < 0) {
183  WRITE_WARNING(TL("Error during final encoding step!"));
184  break;
185  }
186  ret = av_write_frame(myFormatContext, myPkt);
187  av_packet_unref(myPkt);
188  }
189 
190  //Write file trailer
191  av_write_trailer(myFormatContext);
192  avio_closep(&myFormatContext->pb);
193 
194  //Clean
195  avcodec_free_context(&myCodecCtx);
196  av_frame_free(&myFrame);
197  av_packet_free(&myPkt);
198  avformat_free_context(myFormatContext);
199  }
200 
201  void writeFrame(uint8_t* buffer) {
202  if (av_frame_make_writable(myFrame) < 0) {
203  throw ProcessError();
204  }
205  uint8_t* inData[1] = { buffer }; // RGBA32 has one plane
206  int inLinesize[1] = { 4 * myCodecCtx->width }; // RGBA stride
207  sws_scale(mySwsContext, inData, inLinesize, 0, myCodecCtx->height,
208  myFrame->data, myFrame->linesize);
209  myFrame->pts = myFrameIndex;
210  int r = avcodec_send_frame(myCodecCtx, myFrame);
211  if (r < 0) {
212  char errbuf[64];
213  av_strerror(r, errbuf, 64);
214  throw ProcessError(TL("Error sending frame for encoding!"));
215  }
216  int ret = 0;
217  while (ret >= 0) {
218  ret = avcodec_receive_packet(myCodecCtx, myPkt);
219  if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
220  break;
221  } else if (ret < 0) {
222  throw ProcessError(TL("Error during encoding!"));
223  }
224  /* rescale output packet timestamp values from codec to stream timebase */
225  av_packet_rescale_ts(myPkt, myCodecCtx->time_base, myFormatContext->streams[0]->time_base);
226  myPkt->stream_index = 0;
227  ret = av_write_frame(myFormatContext, myPkt);
228  av_packet_unref(myPkt);
229  }
230  myFrameIndex++;
231  }
232 
233 private:
234  AVFormatContext* myFormatContext;
235  SwsContext* mySwsContext;
236  AVCodecContext* myCodecCtx;
237  AVFrame* myFrame;
238  AVPacket* myPkt;
240 
241 };
#define WRITE_WARNING(msg)
Definition: MsgHandler.h:295
#define TL(string)
Definition: MsgHandler.h:315
A simple video encoder from RGBA pics to anything ffmpeg can handle.
SwsContext * mySwsContext
AVFormatContext * myFormatContext
void writeFrame(uint8_t *buffer)
GUIVideoEncoder(const char *const out_file, const int width, const int height, double frameDelay)
AVCodecContext * myCodecCtx