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