LCOV - code coverage report
Current view: top level - src/utils/gui/div - GUIVideoEncoder.h (source / functions) Coverage Total Hit
Test: lcov.info Lines: 0.0 % 97 0
Test Date: 2024-12-21 15:45:41 Functions: 0.0 % 3 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            0 :             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 2.0-1