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-2020 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: 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("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* const codec = avcodec_find_encoder(myFormatContext->oformat->video_codec);
90  if (codec == nullptr) {
91  throw ProcessError("Unknown codec!");
92  }
93  //Param that must set
94  myCodecCtx = avcodec_alloc_context3(codec);
95  if (myCodecCtx == nullptr) {
96  throw ProcessError("Could not allocate video codec context!");
97  }
98  //pmyCodecCtx->codec_id =AV_CODEC_ID_HEVC;
99  //pmyCodecCtx->codec_id = pFormatCtx->oformat->video_codec;
100  //pmyCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;
101  myCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P;
102  // @todo maybe warn about one missing line for odd width or height
103  myCodecCtx->width = (width / 2) * 2;
104  myCodecCtx->height = (height / 2) * 2;
105  myCodecCtx->time_base.num = 1;
106  myCodecCtx->time_base.den = framerate;
107  myCodecCtx->framerate.num = framerate;
108  myCodecCtx->framerate.den = 1;
109  myCodecCtx->bit_rate = 4000000; // example has 400000
110  myCodecCtx->gop_size = 10; // example has 10
111  //H264
112  //pmyCodecCtx->me_range = 16;
113  //pmyCodecCtx->max_qdiff = 4;
114  //pmyCodecCtx->qcompress = 0.6;
115  //myCodecCtx->qmin = 10; // example does not set this
116  //myCodecCtx->qmax = 51; // example does not set this
117  myCodecCtx->max_b_frames = 1; // example has 1
118 
119  // Set codec specific options
120  //H.264
121  if (myCodecCtx->codec_id == AV_CODEC_ID_H264) {
122  av_opt_set(myCodecCtx->priv_data, "preset", "slow", 0);
123  //av_opt_set(myCodecCtx->priv_data, "tune", "zerolatency", 0);
124  //av_opt_set(myCodecCtx->priv_data, "profile", "main", 0);
125  }
126  //H.265
127  if (myCodecCtx->codec_id == AV_CODEC_ID_HEVC) {
128  av_opt_set(myCodecCtx->priv_data, "preset", "ultrafast", 0);
129  av_opt_set(myCodecCtx->priv_data, "tune", "zero-latency", 0);
130  }
131  if (avcodec_open2(myCodecCtx, codec, nullptr) < 0) {
132  throw ProcessError("Could not open codec!");
133  }
134 
135  myFrame = av_frame_alloc();
136  if (myFrame == nullptr) {
137  throw ProcessError("Could not allocate video frame!");
138  }
139  myFrame->format = myCodecCtx->pix_fmt;
140  myFrame->width = myCodecCtx->width;
141  myFrame->height = myCodecCtx->height;
142  if (av_frame_get_buffer(myFrame, 32) < 0) {
143  throw ProcessError("Could not allocate the video frame data!");
144  }
145  mySwsContext = sws_getContext(myCodecCtx->width, myCodecCtx->height, AV_PIX_FMT_RGBA,
146  myCodecCtx->width, myCodecCtx->height, AV_PIX_FMT_YUV420P,
147  0, 0, 0, 0);
148  //Open output URL
149  if (avio_open(&myFormatContext->pb, out_file, AVIO_FLAG_WRITE) < 0) {
150  throw ProcessError("Failed to open output file!");
151  }
152 
153  //Write File Header
154  if (avformat_write_header(myFormatContext, nullptr) < 0) {
155  throw ProcessError("Failed to write file header!");
156  }
157  myFrameIndex = 0;
158  myPkt = av_packet_alloc();
159  if (myPkt == nullptr) {
160  throw ProcessError("Could not allocate video packet!");
161  }
162  }
163 
165  int ret = 1;
166  if (!(myCodecCtx->codec->capabilities & AV_CODEC_CAP_DELAY)) {
167  ret = 0;
168  }
169  if (avcodec_send_frame(myCodecCtx, nullptr) < 0) {
170  WRITE_WARNING("Error sending final frame!");
171  ret = -1;
172  }
173  while (ret >= 0) {
174  ret = avcodec_receive_packet(myCodecCtx, myPkt);
175  if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
176  break;
177  } else if (ret < 0) {
178  WRITE_WARNING("Error during final encoding step!");
179  break;
180  }
181  ret = av_write_frame(myFormatContext, myPkt);
182  av_packet_unref(myPkt);
183  }
184 
185  //Write file trailer
186  av_write_trailer(myFormatContext);
187  avio_closep(&myFormatContext->pb);
188 
189  //Clean
190  avcodec_free_context(&myCodecCtx);
191  av_frame_free(&myFrame);
192  av_packet_free(&myPkt);
193  avformat_free_context(myFormatContext);
194  }
195 
196  void writeFrame(uint8_t* buffer) {
197  if (av_frame_make_writable(myFrame) < 0) {
198  throw ProcessError();
199  }
200  uint8_t* inData[1] = { buffer }; // RGBA32 has one plane
201  int inLinesize[1] = { 4 * myCodecCtx->width }; // RGBA stride
202  sws_scale(mySwsContext, inData, inLinesize, 0, myCodecCtx->height,
203  myFrame->data, myFrame->linesize);
204  myFrame->pts = myFrameIndex;
205  int r = avcodec_send_frame(myCodecCtx, myFrame);
206  if (r < 0) {
207  char errbuf[64];
208  av_strerror(r, errbuf, 64);
209  throw ProcessError("Error sending frame for encoding!");
210  }
211  int ret = 0;
212  while (ret >= 0) {
213  ret = avcodec_receive_packet(myCodecCtx, myPkt);
214  if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
215  break;
216  } else if (ret < 0) {
217  throw ProcessError("Error during encoding!");
218  }
219  /* rescale output packet timestamp values from codec to stream timebase */
220  av_packet_rescale_ts(myPkt, myCodecCtx->time_base, myFormatContext->streams[0]->time_base);
221  myPkt->stream_index = 0;
222  ret = av_write_frame(myFormatContext, myPkt);
223  av_packet_unref(myPkt);
224  }
225  myFrameIndex++;
226  }
227 
228 private:
229  AVFormatContext* myFormatContext;
230  SwsContext* mySwsContext;
231  AVCodecContext* myCodecCtx;
232  AVFrame* myFrame;
233  AVPacket* myPkt;
235 
236 };
#define WRITE_WARNING(msg)
Definition: MsgHandler.h:276
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