LCOV - code coverage report
Current view: top level - src/utils/gui/div - GUIVideoEncoder.h (source / functions) Hit Total Coverage
Test: lcov.info Lines: 0 96 0.0 %
Date: 2024-05-02 15:31:40 Functions: 0 3 0.0 %

          Line data    Source code
       1             : /****************************************************************************/
       2             : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
       3             : // Copyright (C) 2001-2024 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             : /****************************************************************************/
      14             : /// @file    GUIVideoEncoder.h
      15             : /// @author  Michael Behrisch
      16             : /// @date    Dec 2015
      17             : ///
      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: 4242 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             : 
      57             : #include <utils/common/MsgHandler.h>
      58             : #include <utils/common/ToString.h>
      59             : 
      60             : 
      61             : // ===========================================================================
      62             : // class definitions
      63             : // ===========================================================================
      64             : /**
      65             : * @class GUIVideoEncoder
      66             : * @brief A simple video encoder from RGBA pics to anything ffmpeg can handle.
      67             : */
      68             : class GUIVideoEncoder {
      69             : public:
      70           0 :     GUIVideoEncoder(const char* const out_file, const int width, const int height, double frameDelay) {
      71             :         //av_register_all();
      72           0 :         avformat_alloc_output_context2(&myFormatContext, NULL, NULL, out_file);
      73           0 :         if (myFormatContext == nullptr) {
      74           0 :             throw ProcessError(TL("Unknown format!"));
      75             :         }
      76             : 
      77             :         // @todo maybe warn about default and invalid framerates
      78             :         int framerate = 25;
      79           0 :         if (frameDelay > 0.) {
      80           0 :             framerate = (int)(1000. / frameDelay);
      81             :             if (framerate <= 0) {
      82             :                 framerate = 1;
      83             :             }
      84             :         }
      85           0 :         AVStream* const video_st = avformat_new_stream(myFormatContext, 0);
      86           0 :         video_st->time_base.num = 1;
      87           0 :         video_st->time_base.den = framerate;
      88             : 
      89           0 :         const AVCodec* codec = avcodec_find_encoder(myFormatContext->oformat->video_codec);
      90           0 :         if (codec == nullptr) {
      91           0 :             WRITE_WARNING(TL("Unknown codec, falling back to HEVC!"));
      92           0 :             codec = avcodec_find_encoder_by_name("libx265");
      93             :         }
      94           0 :         if (codec == nullptr) {
      95           0 :             throw ProcessError(TL("Unknown codec!"));
      96             :         }
      97             :         //Param that must set
      98           0 :         myCodecCtx = avcodec_alloc_context3(codec);
      99           0 :         if (myCodecCtx == nullptr) {
     100           0 :             throw ProcessError(TL("Could not allocate video codec context!"));
     101             :         }
     102             :         //pmyCodecCtx->codec_id =AV_CODEC_ID_HEVC;
     103             :         //pmyCodecCtx->codec_id = pFormatCtx->oformat->video_codec;
     104             :         //pmyCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;
     105           0 :         myCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P;
     106             :         // @todo maybe warn about one missing line for odd width or height
     107           0 :         myCodecCtx->width = (width / 2) * 2;
     108           0 :         myCodecCtx->height = (height / 2) * 2;
     109           0 :         myCodecCtx->time_base.num = 1;
     110           0 :         myCodecCtx->time_base.den = framerate;
     111           0 :         myCodecCtx->framerate.num = framerate;
     112           0 :         myCodecCtx->framerate.den = 1;
     113           0 :         myCodecCtx->bit_rate = 4000000; // example has 400000
     114           0 :         myCodecCtx->gop_size = 10; // example has 10
     115             :         //H264
     116             :         //pmyCodecCtx->me_range = 16;
     117             :         //pmyCodecCtx->max_qdiff = 4;
     118             :         //pmyCodecCtx->qcompress = 0.6;
     119             :         //myCodecCtx->qmin = 10; // example does not set this
     120             :         //myCodecCtx->qmax = 51; // example does not set this
     121             :         //myCodecCtx->max_b_frames = 1; // example has 1
     122             : 
     123             :         // Set codec specific options
     124             :         //H.264
     125           0 :         if (myCodecCtx->codec_id == AV_CODEC_ID_H264) {
     126           0 :             av_opt_set(myCodecCtx->priv_data, "preset", "slow", 0);
     127             :             //av_opt_set(myCodecCtx->priv_data, "tune", "zerolatency", 0);
     128             :             //av_opt_set(myCodecCtx->priv_data, "profile", "main", 0);
     129             :         }
     130             :         //H.265
     131           0 :         if (myCodecCtx->codec_id == AV_CODEC_ID_HEVC) {
     132           0 :             av_opt_set(myCodecCtx->priv_data, "preset", "ultrafast", 0);
     133           0 :             av_opt_set(myCodecCtx->priv_data, "tune", "zero-latency", 0);
     134             :         }
     135           0 :         if (avcodec_open2(myCodecCtx, codec, nullptr) < 0) {
     136           0 :             throw ProcessError(TL("Could not open codec!"));
     137             :         }
     138           0 :         avcodec_parameters_from_context(video_st->codecpar, myCodecCtx);
     139             : 
     140           0 :         myFrame = av_frame_alloc();
     141           0 :         if (myFrame == nullptr) {
     142           0 :             throw ProcessError(TL("Could not allocate video frame!"));
     143             :         }
     144           0 :         myFrame->format = myCodecCtx->pix_fmt;
     145           0 :         myFrame->width  = myCodecCtx->width;
     146           0 :         myFrame->height = myCodecCtx->height;
     147           0 :         if (av_frame_get_buffer(myFrame, 32) < 0) {
     148           0 :             throw ProcessError(TL("Could not allocate the video frame data!"));
     149             :         }
     150           0 :         mySwsContext = sws_getContext(myCodecCtx->width, myCodecCtx->height, AV_PIX_FMT_RGBA,
     151           0 :                                       myCodecCtx->width, myCodecCtx->height, AV_PIX_FMT_YUV420P,
     152             :                                       0, 0, 0, 0);
     153             :         //Open output URL
     154           0 :         if (avio_open(&myFormatContext->pb, out_file, AVIO_FLAG_WRITE) < 0) {
     155           0 :             throw ProcessError(TL("Failed to open output file!"));
     156             :         }
     157             : 
     158             :         //Write File Header
     159           0 :         if (avformat_write_header(myFormatContext, nullptr) < 0) {
     160           0 :             throw ProcessError(TL("Failed to write file header!"));
     161             :         }
     162           0 :         myFrameIndex = 0;
     163           0 :         myPkt = av_packet_alloc();
     164           0 :         if (myPkt == nullptr) {
     165           0 :             throw ProcessError(TL("Could not allocate video packet!"));
     166             :         }
     167           0 :     }
     168             : 
     169           0 :     ~GUIVideoEncoder() {
     170             :         int ret = 1;
     171           0 :         if (!(myCodecCtx->codec->capabilities & AV_CODEC_CAP_DELAY)) {
     172             :             ret = 0;
     173             :         }
     174           0 :         if (avcodec_send_frame(myCodecCtx, nullptr) < 0) {
     175           0 :             WRITE_WARNING(TL("Error sending final frame!"));
     176             :             ret = -1;
     177             :         }
     178           0 :         while (ret >= 0) {
     179           0 :             ret = avcodec_receive_packet(myCodecCtx, myPkt);
     180           0 :             if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
     181             :                 break;
     182           0 :             } else if (ret < 0) {
     183           0 :                 WRITE_WARNING(TL("Error during final encoding step!"));
     184           0 :                 break;
     185             :             }
     186           0 :             ret = av_write_frame(myFormatContext, myPkt);
     187           0 :             av_packet_unref(myPkt);
     188             :         }
     189             : 
     190             :         //Write file trailer
     191           0 :         av_write_trailer(myFormatContext);
     192           0 :         avio_closep(&myFormatContext->pb);
     193             : 
     194             :         //Clean
     195           0 :         avcodec_free_context(&myCodecCtx);
     196           0 :         av_frame_free(&myFrame);
     197           0 :         av_packet_free(&myPkt);
     198           0 :         avformat_free_context(myFormatContext);
     199           0 :     }
     200             : 
     201           0 :     void writeFrame(uint8_t* buffer) {
     202           0 :         if (av_frame_make_writable(myFrame) < 0) {
     203           0 :             throw ProcessError();
     204             :         }
     205           0 :         uint8_t* inData[1] = { buffer }; // RGBA32 has one plane
     206           0 :         int inLinesize[1] = { 4 * myCodecCtx->width }; // RGBA stride
     207           0 :         sws_scale(mySwsContext, inData, inLinesize, 0, myCodecCtx->height,
     208           0 :                   myFrame->data, myFrame->linesize);
     209           0 :         myFrame->pts = myFrameIndex;
     210           0 :         int r = avcodec_send_frame(myCodecCtx, myFrame);
     211           0 :         if (r < 0) {
     212             :             char errbuf[64];
     213           0 :             av_strerror(r, errbuf, 64);
     214           0 :             throw ProcessError(TL("Error sending frame for encoding!"));
     215             :         }
     216             :         int ret = 0;
     217           0 :         while (ret >= 0) {
     218           0 :             ret = avcodec_receive_packet(myCodecCtx, myPkt);
     219           0 :             if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
     220             :                 break;
     221           0 :             } else if (ret < 0) {
     222           0 :                 throw ProcessError(TL("Error during encoding!"));
     223             :             }
     224             :             /* rescale output packet timestamp values from codec to stream timebase */
     225           0 :             av_packet_rescale_ts(myPkt, myCodecCtx->time_base, myFormatContext->streams[0]->time_base);
     226           0 :             myPkt->stream_index = 0;
     227           0 :             ret = av_write_frame(myFormatContext, myPkt);
     228           0 :             av_packet_unref(myPkt);
     229             :         }
     230           0 :         myFrameIndex++;
     231           0 :     }
     232             : 
     233             : private:
     234             :     AVFormatContext* myFormatContext;
     235             :     SwsContext* mySwsContext;
     236             :     AVCodecContext* myCodecCtx;
     237             :     AVFrame* myFrame;
     238             :     AVPacket* myPkt;
     239             :     int myFrameIndex;
     240             : 
     241             : };

Generated by: LCOV version 1.14