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