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