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 
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  AVFormatContext* const pFormatCtx = myFormatContext = avformat_alloc_context();
75 
76  //Guess Format
77  pFormatCtx->oformat = av_guess_format(NULL, out_file, NULL);
78  if (pFormatCtx->oformat == 0) {
79  throw std::runtime_error("Unknown format!");
80  }
81 
82  //Open output URL
83  if (avio_open(&pFormatCtx->pb, out_file, AVIO_FLAG_READ_WRITE) < 0) {
84  throw std::runtime_error("Failed to open output file!");
85  }
86 
87  // @todo maybe warn about default and invalid framerates
88  int framerate = 25;
89  if (frameDelay > 0.) {
90  framerate = (int)(1000. / frameDelay);
91  if (framerate <= 0) {
92  framerate = 1;
93  }
94  }
95  AVStream* const video_st = avformat_new_stream(pFormatCtx, 0);
96  video_st->time_base.num = 1;
97  video_st->time_base.den = framerate;
98 
99  //Param that must set
100  AVCodecContext* const pCodecCtx = video_st->codec;
101  //pCodecCtx->codec_id =AV_CODEC_ID_HEVC;
102  pCodecCtx->codec_id = pFormatCtx->oformat->video_codec;
103  pCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;
104  pCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P;
105  // @todo maybe warn about one missing line for odd width or height
106  pCodecCtx->width = (width / 2) * 2;
107  pCodecCtx->height = (height / 2) * 2;
108  pCodecCtx->time_base.num = 1;
109  pCodecCtx->time_base.den = framerate;
110  pCodecCtx->bit_rate = 4000000;
111  pCodecCtx->gop_size = 250;
112  //H264
113  //pCodecCtx->me_range = 16;
114  //pCodecCtx->max_qdiff = 4;
115  //pCodecCtx->qcompress = 0.6;
116  pCodecCtx->qmin = 10;
117  pCodecCtx->qmax = 51;
118 
119  //Optional Param
120  pCodecCtx->max_b_frames = 3;
121 
122  // Set Option
123  AVDictionary* param = 0;
124  //H.264
125  if (pCodecCtx->codec_id == AV_CODEC_ID_H264) {
126  av_dict_set(&param, "preset", "slow", 0);
127  av_dict_set(&param, "tune", "zerolatency", 0);
128  //av_dict_set(&param, "profile", "main", 0);
129  }
130  //H.265
131  if (pCodecCtx->codec_id == AV_CODEC_ID_HEVC) {
132  av_dict_set(&param, "preset", "ultrafast", 0);
133  av_dict_set(&param, "tune", "zero-latency", 0);
134  }
135 
136  //Show some Information
137  //av_dump_format(pFormatCtx, 0, out_file, 1);
138 
139  AVCodec* const pCodec = avcodec_find_encoder(pCodecCtx->codec_id);
140  if (!pCodec) {
141  throw std::runtime_error("Can not find encoder!");
142  }
143  const int ret = avcodec_open2(pCodecCtx, pCodec, &param);
144  if (ret < 0) {
145  throw std::runtime_error("Failed to open encoder (" + toString(ret) + ")!");
146  }
147 
148  myFrame = av_frame_alloc();
149  myFrame->format = pCodecCtx->pix_fmt;
150  myFrame->width = pCodecCtx->width;
151  myFrame->height = pCodecCtx->height;
152  av_image_alloc(myFrame->data, myFrame->linesize, pCodecCtx->width, pCodecCtx->height,
153  pCodecCtx->pix_fmt, 32);
154 
155  mySwsContext = sws_getContext(pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_RGBA,
156  pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_YUV420P,
157  0, 0, 0, 0);
158  //Write File Header
159  avformat_write_header(pFormatCtx, NULL);
160  myFrameIndex = 0;
161  }
162 
164  AVFormatContext* fmt_ctx = myFormatContext;
165  int ret = 1;
166  int got_frame;
167  AVPacket enc_pkt;
168  if (!(fmt_ctx->streams[0]->codec->codec->capabilities &
169  AV_CODEC_CAP_DELAY)) {
170  ret = 0;
171  }
172  while (ret > 0) {
173  enc_pkt.data = NULL;
174  enc_pkt.size = 0;
175  av_init_packet(&enc_pkt);
176  ret = avcodec_encode_video2(fmt_ctx->streams[0]->codec, &enc_pkt,
177  NULL, &got_frame);
178  av_frame_free(NULL);
179  if (ret < 0) {
180  break;
181  }
182  if (!got_frame) {
183  ret = 0;
184  break;
185  }
186  /* mux encoded frame */
187  ret = av_write_frame(fmt_ctx, &enc_pkt);
188  }
189 
190  if (ret == 0) {
191  //Write file trailer
192  av_write_trailer(fmt_ctx);
193 
194  //Clean
195  if (fmt_ctx->streams[0]) {
196  avcodec_close(fmt_ctx->streams[0]->codec);
197  av_freep(&myFrame->data[0]);
198  av_frame_free(&myFrame);
199  }
200  avio_close(fmt_ctx->pb);
201  avformat_free_context(fmt_ctx);
202  }
203  }
204 
205  void writeFrame(uint8_t* buffer) {
206  AVStream* const video_st = myFormatContext->streams[0];
207  AVCodecContext* const pCodecCtx = video_st->codec;
208 
209  uint8_t* inData[1] = { buffer }; // RGBA32 have one plane
210  int inLinesize[1] = { 4 * pCodecCtx->width }; // RGBA stride
211  sws_scale(mySwsContext, inData, inLinesize, 0, pCodecCtx->height,
212  myFrame->data, myFrame->linesize);
213 
214  av_init_packet(&myPkt);
215  myPkt.data = NULL;
216  myPkt.size = 0;
217  //PTS
218  myFrame->pts = myFrameIndex;
219  int got_picture = 0;
220  //Encode
221  int ret = avcodec_encode_video2(pCodecCtx, &myPkt, myFrame, &got_picture);
222  if (ret < 0) {
223  throw std::runtime_error("Failed to encode!");
224  }
225  if (got_picture == 1) {
226  myPkt.stream_index = video_st->index;
227  ret = av_write_frame(myFormatContext, &myPkt);
228  av_free_packet(&myPkt);
229  myFrameIndex++;
230  }
231  }
232 
233 private:
234  AVFormatContext* myFormatContext;
235  SwsContext* mySwsContext;
236  AVFrame* myFrame;
237  AVPacket myPkt;
239 
240 };
241 
242 
243 #endif
244 
245 /****************************************************************************/
AVFormatContext * myFormatContext
void writeFrame(uint8_t *buffer)
GUIVideoEncoder(const char *const out_file, const int width, const int height, double frameDelay)
std::string toString(const T &t, std::streamsize accuracy=gPrecision)
Definition: ToString.h:49
A simple video encoder from RGBA pics to anything ffmpeg can handle.
SwsContext * mySwsContext