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