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