"Fossies" - the Fresh Open Source Software Archive

Member "ssr-0.4.2/src/GUI/PageOutput.cpp" (18 May 2020, 33457 Bytes) of package /linux/privat/ssr-0.4.2.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "PageOutput.cpp" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 0.4.1_vs_0.4.2.

    1 /*
    2 Copyright (c) 2012-2020 Maarten Baert <maarten-baert@hotmail.com>
    3 
    4 This file is part of SimpleScreenRecorder.
    5 
    6 SimpleScreenRecorder is free software: you can redistribute it and/or modify
    7 it under the terms of the GNU General Public License as published by
    8 the Free Software Foundation, either version 3 of the License, or
    9 (at your option) any later version.
   10 
   11 SimpleScreenRecorder is distributed in the hope that it will be useful,
   12 but WITHOUT ANY WARRANTY; without even the implied warranty of
   13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   14 GNU General Public License for more details.
   15 
   16 You should have received a copy of the GNU General Public License
   17 along with SimpleScreenRecorder.  If not, see <http://www.gnu.org/licenses/>.
   18 */
   19 
   20 #include "PageOutput.h"
   21 
   22 #include "Dialogs.h"
   23 #include "EnumStrings.h"
   24 #include "HiddenScrollArea.h"
   25 #include "Icons.h"
   26 #include "Logger.h"
   27 #include "MainWindow.h"
   28 #include "PageInput.h"
   29 
   30 #include "AVWrapper.h"
   31 #include "VideoEncoder.h"
   32 #include "AudioEncoder.h"
   33 
   34 ENUMSTRINGS(PageOutput::enum_container) = {
   35     {PageOutput::CONTAINER_MKV, "mkv"},
   36     {PageOutput::CONTAINER_MP4, "mp4"},
   37     {PageOutput::CONTAINER_WEBM, "webm"},
   38     {PageOutput::CONTAINER_OGG, "ogg"},
   39     {PageOutput::CONTAINER_OTHER, "other"},
   40 };
   41 ENUMSTRINGS(PageOutput::enum_video_codec) = {
   42     {PageOutput::VIDEO_CODEC_H264, "h264"},
   43     {PageOutput::VIDEO_CODEC_VP8, "vp8"},
   44     {PageOutput::VIDEO_CODEC_THEORA, "theora"},
   45     {PageOutput::VIDEO_CODEC_OTHER, "other"},
   46 };
   47 ENUMSTRINGS(PageOutput::enum_audio_codec) = {
   48     {PageOutput::AUDIO_CODEC_VORBIS, "vorbis"},
   49     {PageOutput::AUDIO_CODEC_MP3, "mp3"},
   50     {PageOutput::AUDIO_CODEC_AAC, "aac"},
   51     {PageOutput::AUDIO_CODEC_UNCOMPRESSED, "uncompressed"},
   52     {PageOutput::AUDIO_CODEC_OTHER, "other"},
   53 };
   54 ENUMSTRINGS(PageOutput::enum_h264_preset) = {
   55     {PageOutput::H264_PRESET_ULTRAFAST, "ultrafast"},
   56     {PageOutput::H264_PRESET_SUPERFAST, "superfast"},
   57     {PageOutput::H264_PRESET_VERYFAST, "veryfast"},
   58     {PageOutput::H264_PRESET_FASTER, "faster"},
   59     {PageOutput::H264_PRESET_FAST, "fast"},
   60     {PageOutput::H264_PRESET_MEDIUM, "medium"},
   61     {PageOutput::H264_PRESET_SLOW, "slow"},
   62     {PageOutput::H264_PRESET_SLOWER, "slower"},
   63     {PageOutput::H264_PRESET_VERYSLOW, "veryslow"},
   64     {PageOutput::H264_PRESET_PLACEBO, "placebo"},
   65 };
   66 
   67 static bool MatchSuffix(const QString& suffix, const QStringList& suffixes) {
   68     return ((suffix.isEmpty() && suffixes.isEmpty()) || suffixes.contains(suffix, Qt::CaseInsensitive));
   69 }
   70 
   71 PageOutput::PageOutput(MainWindow* main_window)
   72     : QWidget(main_window->centralWidget()) {
   73 
   74     m_main_window = main_window;
   75 
   76     m_old_container = (enum_container) 0;
   77     m_old_container_av = 0;
   78 
   79     // main codecs
   80     // (initializer lists should use explicit types for Clang)
   81     m_containers = {
   82         ContainerData({"Matroska (MKV)", "matroska", QStringList({"mkv"}), tr("%1 files", "This appears in the file dialog, e.g. 'MP4 files'").arg("Matroska") + " (*.mkv)",
   83             {VIDEO_CODEC_H264, VIDEO_CODEC_VP8, VIDEO_CODEC_THEORA},
   84             {AUDIO_CODEC_VORBIS, AUDIO_CODEC_MP3, AUDIO_CODEC_AAC, AUDIO_CODEC_UNCOMPRESSED}}),
   85         ContainerData({"MP4", "mp4", QStringList({"mp4"}), tr("%1 files", "This appears in the file dialog, e.g. 'MP4 files'").arg("MP4") + " (*.mp4)",
   86             {VIDEO_CODEC_H264},
   87             {AUDIO_CODEC_VORBIS, AUDIO_CODEC_MP3, AUDIO_CODEC_AAC}}),
   88         ContainerData({"WebM", "webm", QStringList({"webm"}), tr("%1 files", "This appears in the file dialog, e.g. 'MP4 files'").arg("WebM") + " (*.webm)",
   89             {VIDEO_CODEC_VP8},
   90             {AUDIO_CODEC_VORBIS}}),
   91         ContainerData({"OGG", "ogg", QStringList({"ogg"}), tr("%1 files", "This appears in the file dialog, e.g. 'MP4 files'").arg("OGG") + " (*.ogg)",
   92             {VIDEO_CODEC_THEORA},
   93             {AUDIO_CODEC_VORBIS}}),
   94         ContainerData({tr("Other..."), "other", QStringList(), "", std::set<enum_video_codec>({}), std::set<enum_audio_codec>({})}),
   95     };
   96     m_video_codecs = {
   97         {"H.264"       , "libx264"  },
   98         {"VP8"         , "libvpx"   },
   99         {"Theora"      , "libtheora"},
  100         {tr("Other..."), "other"    },
  101     };
  102     m_audio_codecs = {
  103         {"Vorbis"          , "libvorbis"   },
  104         {"MP3"             , "libmp3lame"  },
  105         {"AAC"             , "libvo_aacenc"},
  106         {tr("Uncompressed"), "pcm_s16le"   },
  107         {tr("Other...")    , "other"       },
  108     };
  109 
  110     // alternative aac codec
  111     if(!AVCodecIsInstalled(m_audio_codecs[AUDIO_CODEC_AAC].avname)) {
  112         m_audio_codecs[AUDIO_CODEC_AAC].avname = "aac";
  113     }
  114 
  115     // load AV container list
  116     m_containers_av.clear();
  117 #if SSR_USE_AV_MUXER_ITERATE
  118     const AVOutputFormat *format;
  119     void *format_opaque = NULL;
  120     while((format = av_muxer_iterate(&format_opaque)) != NULL) {
  121 #else
  122     for(AVOutputFormat *format = av_oformat_next(NULL); format != NULL; format = av_oformat_next(format)) {
  123 #endif
  124         if(format->video_codec == AV_CODEC_ID_NONE)
  125             continue;
  126         ContainerData c;
  127         c.name = format->long_name;
  128         c.avname = format->name;
  129         c.suffixes = QString(format->extensions).split(',', QString::SkipEmptyParts);
  130         if(c.suffixes.isEmpty()) {
  131             c.filter = "";
  132         } else {
  133             c.filter = tr("%1 files", "This appears in the file dialog, e.g. 'MP4 files'").arg(c.avname) + " (*." + c.suffixes[0];
  134             for(int i = 1; i < c.suffixes.size(); ++i) {
  135                 c.suffixes[i] = c.suffixes[i].trimmed(); // needed because libav/ffmpeg isn't very consistent when they say 'comma-separated'
  136                 c.filter += " *." + c.suffixes[i];
  137             }
  138             c.filter += ")";
  139         }
  140         m_containers_av.push_back(c);
  141     }
  142     std::sort(m_containers_av.begin(), m_containers_av.end());
  143 
  144     // load AV codec list
  145     m_video_codecs_av.clear();
  146     m_audio_codecs_av.clear();
  147 #if SSR_USE_AV_MUXER_ITERATE
  148     const AVCodec *codec;
  149     void *codec_opaque = NULL;
  150     while((codec = av_codec_iterate(&codec_opaque)) != NULL) {
  151 #else
  152     for(AVCodec *codec = av_codec_next(NULL); codec != NULL; codec = av_codec_next(codec)) {
  153 #endif
  154         if(!av_codec_is_encoder(codec))
  155             continue;
  156         if(codec->type == AVMEDIA_TYPE_VIDEO && VideoEncoder::AVCodecIsSupported(codec->name)) {
  157             VideoCodecData c;
  158             c.name = codec->long_name;
  159             c.avname = codec->name;
  160             m_video_codecs_av.push_back(c);
  161         }
  162         if(codec->type == AVMEDIA_TYPE_AUDIO && AudioEncoder::AVCodecIsSupported(codec->name)) {
  163             AudioCodecData c;
  164             c.name = codec->long_name;
  165             c.avname = codec->name;
  166             m_audio_codecs_av.push_back(c);
  167         }
  168     }
  169     std::sort(m_video_codecs_av.begin(), m_video_codecs_av.end());
  170     std::sort(m_audio_codecs_av.begin(), m_audio_codecs_av.end());
  171 
  172     if(m_containers_av.empty()) {
  173         Logger::LogError("[PageOutput::PageOutput] " + tr("Error: Could not find any suitable container in libavformat!"));
  174         throw LibavException();
  175     }
  176     if(m_video_codecs_av.empty()) {
  177         Logger::LogError("[PageOutput::PageOutput] " + tr("Error: Could not find any suitable video codec in libavcodec!"));
  178         throw LibavException();
  179     }
  180     if(m_audio_codecs_av.empty()) {
  181         Logger::LogError("[PageOutput::PageOutput] " + tr("Error: Could not find any suitable audio codec in libavcodec!"));
  182         throw LibavException();
  183     }
  184 
  185     HiddenScrollArea *scrollarea = new HiddenScrollArea(this);
  186     QWidget *scrollarea_contents = new QWidget(scrollarea);
  187     scrollarea->setWidget(scrollarea_contents);
  188     {
  189         m_profile_box = new ProfileBox(tr("Output profile"), scrollarea_contents, "output-profiles", &LoadProfileSettingsCallback, &SaveProfileSettingsCallback, this);
  190 
  191         QGroupBox *groupbox_file = new QGroupBox(tr("File"), scrollarea_contents);
  192         {
  193             QLabel *label_file = new QLabel(tr("Save as:"), groupbox_file);
  194             m_lineedit_file = new QLineEdit(groupbox_file);
  195             m_lineedit_file->setToolTip(tr("The recording will be saved to this location."));
  196             QPushButton *button_browse = new QPushButton(tr("Browse..."), groupbox_file);
  197             m_checkbox_separate_files = new QCheckBox(tr("Separate file per segment"), groupbox_file);
  198             m_checkbox_separate_files->setToolTip(tr("If checked, a separate video file will be created every time you pause and resume the recording."
  199                                                      "If unchecked, all recorded segments will be combined into a single video file."));
  200             m_checkbox_add_timestamp = new QCheckBox(tr("Add timestamp"), groupbox_file);
  201             m_checkbox_add_timestamp->setToolTip(tr("If checked, the current date and time will be appended to the file name automatically.\n"
  202                                                     "If the original file name is 'test.mkv', the video will be saved as 'test-YYYY-MM-DD_HH.MM.SS.mkv'."));
  203             QLabel *label_container = new QLabel(tr("Container:"), groupbox_file);
  204             m_combobox_container = new QComboBox(groupbox_file);
  205             for(unsigned int i = 0; i < CONTAINER_COUNT; ++i) {
  206                 QString name = "\u200e" + m_containers[i].name + "\u200e";
  207                 if(i != CONTAINER_OTHER && !AVFormatIsInstalled(m_containers[i].avname))
  208                     name += " \u200e" + tr("(not installed)") + "\u200e";
  209                 m_combobox_container->addItem(name);
  210             }
  211             m_combobox_container->setToolTip(tr("The container (file format) that will be used to save the recording.\n"
  212                                                 "Note that not all codecs are supported by all containers, and that not all media players can read all file formats.\n"
  213                                                 "- Matroska (MKV) supports all the codecs, but is less well-known.\n"
  214                                                 "- MP4 is the most well-known format and will play on almost any modern media player, but supports only H.264 video\n"
  215                                                 "   (and many media players only support AAC audio).\n"
  216                                                 "- WebM is intended for embedding video into websites (with the HTML5 <video> tag). The format was created by Google.\n"
  217                                                 "   WebM is supported by default in Firefox, Chrome and Opera, and plugins are available for Internet Explorer and Safari.\n"
  218                                                 "   It supports only VP8 and Vorbis.\n"
  219                                                 "- OGG supports only Theora and Vorbis."));
  220             m_label_container_av = new QLabel(tr("Container name:"), groupbox_file);
  221             m_combobox_container_av = new QComboBox(groupbox_file);
  222             for(unsigned int i = 0; i < m_containers_av.size(); ++i) {
  223                 ContainerData &c = m_containers_av[i];
  224                 m_combobox_container_av->addItem(c.avname);
  225             }
  226             m_combobox_container_av->setToolTip(tr("For advanced users. You can use any libav/ffmpeg format, but many of them are not useful or may not work."));
  227             m_label_container_warning = new QLabel(tr("Warning: This format will produce unreadable files if the recording is interrupted! Consider using MKV instead."), groupbox_file);
  228             m_label_container_warning->setWordWrap(true);
  229 
  230             connect(m_combobox_container, SIGNAL(activated(int)), this, SLOT(OnUpdateSuffixAndContainerFields()));
  231             connect(m_combobox_container_av, SIGNAL(activated(int)), this, SLOT(OnUpdateSuffixAndContainerFields()));
  232             connect(button_browse, SIGNAL(clicked()), this, SLOT(OnBrowse()));
  233 
  234             QGridLayout *layout = new QGridLayout(groupbox_file);
  235             layout->addWidget(label_file, 0, 0);
  236             layout->addWidget(m_lineedit_file, 0, 1);
  237             layout->addWidget(button_browse, 0, 2);
  238             {
  239                 QHBoxLayout *layout2 = new QHBoxLayout();
  240                 layout->addLayout(layout2, 1, 0, 1, 3);
  241                 layout2->addWidget(m_checkbox_separate_files);
  242                 layout2->addWidget(m_checkbox_add_timestamp);
  243             }
  244             layout->addWidget(label_container, 2, 0);
  245             layout->addWidget(m_combobox_container, 2, 1, 1, 2);
  246             layout->addWidget(m_label_container_av, 3, 0);
  247             layout->addWidget(m_combobox_container_av, 3, 1, 1, 2);
  248             layout->addWidget(m_label_container_warning, 4, 0, 1, 3);
  249         }
  250         QGroupBox *groupbox_video = new QGroupBox(tr("Video"), scrollarea_contents);
  251         {
  252             QLabel *label_video_codec = new QLabel(tr("Codec:"), groupbox_video);
  253             m_combobox_video_codec = new QComboBox(groupbox_video);
  254             for(unsigned int i = 0; i < VIDEO_CODEC_COUNT; ++i) {
  255                 m_combobox_video_codec->addItem(m_video_codecs[i].name);
  256             }
  257             m_combobox_video_codec->setToolTip(tr("The codec that will be used to compress the video stream.\n"
  258                                                   "- H.264 (libx264) is by far the best codec - high quality and very fast.\n"
  259                                                   "- VP8 (libvpx) is quite good but also quite slow.\n"
  260                                                   "- Theora (libtheora) isn't really recommended because the quality isn't very good."));
  261             m_label_video_codec_av = new QLabel(tr("Codec name:"), groupbox_video);
  262             m_combobox_video_codec_av = new QComboBox(groupbox_video);
  263             for(unsigned int i = 0; i < m_video_codecs_av.size(); ++i) {
  264                 VideoCodecData &c = m_video_codecs_av[i];
  265                 m_combobox_video_codec_av->addItem(c.avname);
  266             }
  267             m_combobox_video_codec_av->setToolTip(tr("For advanced users. You can use any libav/ffmpeg video codec, but many of them are not useful or may not work."));
  268             m_label_video_kbit_rate = new QLabel(tr("Bit rate (in kbit/s):"), groupbox_video);
  269             m_lineedit_video_kbit_rate = new QLineEdit(groupbox_video);
  270             m_lineedit_video_kbit_rate->setToolTip(tr("The video bit rate (in kilobit per second). A higher value means a higher quality."
  271                                                       "\nIf you have no idea where to start, try 5000 and change it if needed."));
  272             m_label_h264_crf = new QLabel(tr("Constant rate factor:", "libx264 setting: don't translate this unless you can come up with something sensible"), groupbox_video);
  273             m_slider_h264_crf = new QSlider(Qt::Horizontal, groupbox_video);
  274             m_slider_h264_crf->setRange(0, 51);
  275             m_slider_h264_crf->setSingleStep(1);
  276             m_slider_h264_crf->setPageStep(5);
  277             m_slider_h264_crf->setToolTip(tr("This setting changes the video quality. A lower value means a higher quality.\n"
  278                                              "The allowed range is 0-51 (0 means lossless, the default is 23)."));
  279             m_label_h264_crf_value = new QLabel(groupbox_video);
  280             m_label_h264_crf_value->setNum(m_slider_h264_crf->value());
  281             m_label_h264_crf_value->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
  282             m_label_h264_crf_value->setMinimumWidth(QFontMetrics(m_label_h264_crf_value->font()).width("99") + 2);
  283             m_label_h264_preset = new QLabel(tr("Preset:", "libx264 setting: don't translate this unless you can come up with something sensible"), groupbox_video);
  284             m_combobox_h264_preset = new QComboBox(groupbox_video);
  285             for(unsigned int i = 0; i < H264_PRESET_COUNT; ++i) {
  286                 m_combobox_h264_preset->addItem(EnumToString((enum_h264_preset) i));
  287             }
  288             m_combobox_h264_preset->setToolTip(tr("The encoding speed. A higher speed uses less CPU (making higher recording frame rates possible),\n"
  289                                                   "but results in larger files. The quality shouldn't be affected too much."));
  290             m_label_vp8_cpu_used = new QLabel(tr("CPU used:", "libvpx setting: don't translate this unless you can come up with something sensible"), groupbox_video);
  291             m_combobox_vp8_cpu_used = new QComboBox(groupbox_video);
  292             m_combobox_vp8_cpu_used->addItem("5 (" + tr("fastest") + ")");
  293             m_combobox_vp8_cpu_used->addItem("4");
  294             m_combobox_vp8_cpu_used->addItem("3");
  295             m_combobox_vp8_cpu_used->addItem("2");
  296             m_combobox_vp8_cpu_used->addItem("1");
  297             m_combobox_vp8_cpu_used->addItem("0 (" + tr("slowest") + ")");
  298             m_combobox_vp8_cpu_used->setToolTip(tr("The encoding speed. A higher value uses *less* CPU time. (I didn't choose the name, this is the name\n"
  299                                                    "used by the VP8 encoder). Higher values result in lower quality video, unless you increase the bit rate too."));
  300             m_label_video_options = new QLabel(tr("Custom options:"), groupbox_video);
  301             m_lineedit_video_options = new QLineEdit(groupbox_video);
  302             m_lineedit_video_options->setToolTip(tr("Custom codec options separated by commas (e.g. option1=value1,option2=value2,option3=value3)"));
  303             m_checkbox_video_allow_frame_skipping = new QCheckBox(tr("Allow frame skipping"), groupbox_video);
  304             m_checkbox_video_allow_frame_skipping->setToolTip(tr("If checked, the video encoder will be allowed to skip frames if the input frame rate is\n"
  305                                                                  "lower than the output frame rate. If not checked, input frames will be duplicated to fill the holes.\n"
  306                                                                  "This increases the file size and CPU usage, but reduces the latency for live streams in some cases.\n"
  307                                                                  "It shouldn't affect the appearance of the video."));
  308 
  309             connect(m_combobox_video_codec, SIGNAL(activated(int)), this, SLOT(OnUpdateVideoCodecFields()));
  310             connect(m_slider_h264_crf, SIGNAL(valueChanged(int)), m_label_h264_crf_value, SLOT(setNum(int)));
  311 
  312             QGridLayout *layout = new QGridLayout(groupbox_video);
  313             layout->addWidget(label_video_codec, 0, 0);
  314             layout->addWidget(m_combobox_video_codec, 0, 1, 1, 2);
  315             layout->addWidget(m_label_video_codec_av, 1, 0);
  316             layout->addWidget(m_combobox_video_codec_av, 1, 1, 1, 2);
  317             layout->addWidget(m_label_video_kbit_rate, 2, 0);
  318             layout->addWidget(m_lineedit_video_kbit_rate, 2, 1, 1, 2);
  319             layout->addWidget(m_label_h264_crf, 3, 0);
  320             layout->addWidget(m_slider_h264_crf, 3, 1);
  321             layout->addWidget(m_label_h264_crf_value, 3, 2);
  322             layout->addWidget(m_label_h264_preset, 4, 0);
  323             layout->addWidget(m_combobox_h264_preset, 4, 1, 1, 2);
  324             layout->addWidget(m_label_vp8_cpu_used, 5, 0);
  325             layout->addWidget(m_combobox_vp8_cpu_used, 5, 1, 1, 2);
  326             layout->addWidget(m_label_video_options, 6, 0);
  327             layout->addWidget(m_lineedit_video_options, 6, 1, 1, 2);
  328             layout->addWidget(m_checkbox_video_allow_frame_skipping, 7, 0, 1, 3);
  329         }
  330         m_groupbox_audio = new QGroupBox(tr("Audio"), scrollarea_contents);
  331         {
  332             QLabel *label_audio_codec = new QLabel(tr("Codec:"), m_groupbox_audio);
  333             m_combobox_audio_codec = new QComboBox(m_groupbox_audio);
  334             for(unsigned int i = 0; i < AUDIO_CODEC_COUNT; ++i) {
  335                 m_combobox_audio_codec->addItem(m_audio_codecs[i].name);
  336             }
  337             m_combobox_audio_codec->setToolTip(tr("The codec that will be used to compress the audio stream. You shouldn't worry too much about\n"
  338                                                   "this, because the size of the audio data is usually negligible compared to the size of the video data.\n"
  339                                                   "And if you're only recording your own voice (i.e. no music), the quality won't matter that much anyway.\n"
  340                                                   "- Vorbis (libvorbis) is great, this is the recommended codec.\n"
  341                                                   "- MP3 (libmp3lame) is reasonably good.\n"
  342                                                   "- AAC is a good codec, but the implementations used here (libvo_aacenc or the experimental ffmpeg aac encoder)\n"
  343                                                   "   are pretty bad. Only use it if you have no other choice.\n"
  344                                                   "- Uncompressed will simply store the sound data without compressing it. The file will be quite large, but it's very fast."));
  345             m_label_audio_codec_av = new QLabel(tr("Codec name:"), m_groupbox_audio);
  346             m_combobox_audio_codec_av = new QComboBox(m_groupbox_audio);
  347             for(unsigned int i = 0; i < m_audio_codecs_av.size(); ++i) {
  348                 AudioCodecData &c = m_audio_codecs_av[i];
  349                 m_combobox_audio_codec_av->addItem(c.avname);
  350             }
  351             m_combobox_audio_codec_av->setToolTip(tr("For advanced users. You can use any libav/ffmpeg audio codec, but many of them are not useful or may not work."));
  352             m_label_audio_kbit_rate = new QLabel(tr("Bit rate (in kbit/s):"), m_groupbox_audio);
  353             m_lineedit_audio_kbit_rate = new QLineEdit(m_groupbox_audio);
  354             m_lineedit_audio_kbit_rate->setToolTip(tr("The audio bit rate (in kilobit per second). A higher value means a higher quality. The typical value is 128."));
  355             m_label_audio_options = new QLabel(tr("Custom options:"), m_groupbox_audio);
  356             m_lineedit_audio_options = new QLineEdit(m_groupbox_audio);
  357             m_lineedit_audio_options->setToolTip(tr("Custom codec options separated by commas (e.g. option1=value1,option2=value2,option3=value3)"));
  358 
  359             connect(m_combobox_audio_codec, SIGNAL(activated(int)), this, SLOT(OnUpdateAudioCodecFields()));
  360 
  361             QGridLayout *layout = new QGridLayout(m_groupbox_audio);
  362             layout->addWidget(label_audio_codec, 0, 0);
  363             layout->addWidget(m_combobox_audio_codec, 0, 1);
  364             layout->addWidget(m_label_audio_codec_av, 1, 0);
  365             layout->addWidget(m_combobox_audio_codec_av, 1, 1);
  366             layout->addWidget(m_label_audio_kbit_rate, 2, 0);
  367             layout->addWidget(m_lineedit_audio_kbit_rate, 2, 1);
  368             layout->addWidget(m_label_audio_options, 3, 0);
  369             layout->addWidget(m_lineedit_audio_options, 3, 1);
  370         }
  371 
  372         QVBoxLayout *layout = new QVBoxLayout(scrollarea_contents);
  373         layout->addWidget(m_profile_box);
  374         layout->addWidget(groupbox_file);
  375         layout->addWidget(groupbox_video);
  376         layout->addWidget(m_groupbox_audio);
  377         layout->addStretch();
  378     }
  379     QPushButton *button_back = new QPushButton(g_icon_go_previous, tr("Back"), this);
  380     QPushButton *button_continue = new QPushButton(g_icon_go_next, tr("Continue"), this);
  381 
  382     connect(button_back, SIGNAL(clicked()), m_main_window, SLOT(GoPageInput()));
  383     connect(button_continue, SIGNAL(clicked()), this, SLOT(OnContinue()));
  384 
  385     QVBoxLayout *layout = new QVBoxLayout(this);
  386     layout->setContentsMargins(0, 0, 0, 0);
  387     layout->addWidget(scrollarea);
  388     {
  389         QHBoxLayout *layout2 = new QHBoxLayout();
  390         layout->addLayout(layout2);
  391         layout2->addSpacing(style()->pixelMetric(QStyle::PM_LayoutLeftMargin));
  392         layout2->addWidget(button_back);
  393         layout2->addWidget(button_continue);
  394         layout2->addSpacing(style()->pixelMetric(QStyle::PM_LayoutRightMargin));
  395     }
  396     layout->addSpacing(style()->pixelMetric(QStyle::PM_LayoutBottomMargin));
  397 
  398     // temporary settings to calculate the worst-case size
  399     SetContainer(CONTAINER_OTHER);
  400     SetVideoCodec(VIDEO_CODEC_OTHER);
  401     SetAudioCodec(AUDIO_CODEC_OTHER);
  402 
  403     OnUpdateContainerFields();
  404     OnUpdateVideoCodecFields();
  405     OnUpdateAudioCodecFields();
  406 
  407 }
  408 
  409 void PageOutput::LoadSettings(QSettings* settings) {
  410     SetProfile(m_profile_box->FindProfile(settings->value("output/profile", QString()).toString()));
  411     LoadProfileSettings(settings);
  412 }
  413 
  414 void PageOutput::SaveSettings(QSettings* settings) {
  415     settings->setValue("output/profile", m_profile_box->GetProfileName());
  416     SaveProfileSettings(settings);
  417 }
  418 
  419 void PageOutput::LoadProfileSettingsCallback(QSettings* settings, void* userdata) {
  420     PageOutput *page = (PageOutput*) userdata;
  421     page->LoadProfileSettings(settings);
  422 }
  423 
  424 void PageOutput::SaveProfileSettingsCallback(QSettings* settings, void* userdata) {
  425     PageOutput *page = (PageOutput*) userdata;
  426     page->SaveProfileSettings(settings);
  427 }
  428 
  429 void PageOutput::LoadProfileSettings(QSettings* settings) {
  430 
  431     // choose default container and codecs
  432     enum_container default_container = (enum_container) 0;
  433     for(unsigned int i = 0; i < CONTAINER_OTHER; ++i) {
  434         if(AVFormatIsInstalled(m_containers[i].avname)) {
  435             default_container = (enum_container) i;
  436             break;
  437         }
  438     }
  439     enum_video_codec default_video_codec = (enum_video_codec) 0;
  440     for(unsigned int i = 0; i < VIDEO_CODEC_OTHER; ++i) {
  441         if(AVCodecIsInstalled(m_video_codecs[i].avname) && m_containers[default_container].supported_video_codecs.count((enum_video_codec) i)) {
  442             default_video_codec = (enum_video_codec) i;
  443             break;
  444         }
  445     }
  446     enum_audio_codec default_audio_codec = (enum_audio_codec) 0;
  447     for(unsigned int i = 0; i < VIDEO_CODEC_OTHER; ++i) {
  448         if(AVCodecIsInstalled(m_audio_codecs[i].avname) && m_containers[default_container].supported_audio_codecs.count((enum_audio_codec) i)) {
  449             default_audio_codec = (enum_audio_codec) i;
  450             break;
  451         }
  452     }
  453 
  454     // choose default file name
  455 #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
  456     QString dir_videos = QDesktopServices::storageLocation(QDesktopServices::MoviesLocation);
  457     QString dir_documents = QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation);
  458 #else
  459     QString dir_videos = QStandardPaths::writableLocation(QStandardPaths::MoviesLocation);
  460     QString dir_documents = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
  461 #endif
  462     QString dir_home = QDir::homePath();
  463     QString best_dir = (QDir(dir_videos).exists())? dir_videos : (QDir(dir_documents).exists())? dir_documents : dir_home;
  464     QString default_file = best_dir + "/simplescreenrecorder." + m_containers[default_container].suffixes[0];
  465 
  466     // load settings
  467     SetFile(settings->value("output/file", default_file).toString());
  468     SetSeparateFiles(settings->value("output/separate_files", false).toBool());
  469     SetAddTimestamp(settings->value("output/add_timestamp", true).toBool());
  470     SetContainer(StringToEnum(settings->value("output/container", QString()).toString(), default_container));
  471     SetContainerAV(FindContainerAV(settings->value("output/container_av", QString()).toString()));
  472 
  473     SetVideoCodec(StringToEnum(settings->value("output/video_codec", QString()).toString(), default_video_codec));
  474     SetVideoCodecAV(FindVideoCodecAV(settings->value("output/video_codec_av", QString()).toString()));
  475     SetVideoKBitRate(settings->value("output/video_kbit_rate", 5000).toUInt());
  476     SetH264CRF(settings->value("output/video_h264_crf", 23).toUInt());
  477     SetH264Preset((enum_h264_preset) settings->value("output/video_h264_preset", H264_PRESET_SUPERFAST).toUInt());
  478     SetVP8CPUUsed(settings->value("output/video_vp8_cpu_used", 5).toUInt());
  479     SetVideoOptions(settings->value("output/video_options", "").toString());
  480     SetVideoAllowFrameSkipping(settings->value("output/video_allow_frame_skipping", true).toBool());
  481 
  482     SetAudioCodec(StringToEnum(settings->value("output/audio_codec", QString()).toString(), default_audio_codec));
  483     SetAudioCodecAV(FindAudioCodecAV(settings->value("output/audio_codec_av", QString()).toString()));
  484     SetAudioKBitRate(settings->value("output/audio_kbit_rate", 128).toUInt());
  485     SetAudioOptions(settings->value("output/audio_options", "").toString());
  486 
  487     // update things
  488     OnUpdateContainerFields();
  489     OnUpdateVideoCodecFields();
  490     OnUpdateAudioCodecFields();
  491 
  492 }
  493 
  494 void PageOutput::SaveProfileSettings(QSettings* settings) {
  495 
  496     settings->setValue("output/file", GetFile());
  497     settings->setValue("output/separate_files", GetSeparateFiles());
  498     settings->setValue("output/add_timestamp", GetAddTimestamp());
  499     settings->setValue("output/container", EnumToString(GetContainer()));
  500     settings->setValue("output/container_av", m_containers_av[GetContainerAV()].avname);
  501 
  502     settings->setValue("output/video_codec", EnumToString(GetVideoCodec()));
  503     settings->setValue("output/video_codec_av", m_video_codecs_av[GetVideoCodecAV()].avname);
  504     settings->setValue("output/video_kbit_rate", GetVideoKBitRate());
  505     settings->setValue("output/video_h264_crf", GetH264CRF());
  506     settings->setValue("output/video_h264_preset", GetH264Preset());
  507     settings->setValue("output/video_vp8_cpu_used", GetVP8CPUUsed());
  508     settings->setValue("output/video_options", GetVideoOptions());
  509     settings->setValue("output/video_allow_frame_skipping", GetVideoAllowFrameSkipping());
  510 
  511     settings->setValue("output/audio_codec", EnumToString(GetAudioCodec()));
  512     settings->setValue("output/audio_codec_av", m_audio_codecs_av[GetAudioCodecAV()].avname);
  513     settings->setValue("output/audio_kbit_rate", GetAudioKBitRate());
  514     settings->setValue("output/audio_options", GetAudioOptions());
  515 
  516 }
  517 
  518 void PageOutput::StartPage() {
  519 
  520     // only show audio settings if audio is enabled
  521     m_groupbox_audio->setVisible(m_main_window->GetPageInput()->GetAudioEnabled());
  522 
  523 }
  524 
  525 bool PageOutput::Validate() {
  526     QString file = GetFile();
  527     if(file.isEmpty()) {
  528         MessageBox(QMessageBox::Critical, this, MainWindow::WINDOW_CAPTION, tr("You did not select an output file!"), BUTTON_OK, BUTTON_OK);
  529         return false;
  530     }
  531     /*if(GetFileProtocol().isNull() && !GetSeparateFiles() && QFileInfo(file).exists()) {
  532         if(MessageBox(QMessageBox::Warning, this, MainWindow::WINDOW_CAPTION,
  533                       tr("The file '%1' already exists. Are you sure that you want to overwrite it?").arg(QFileInfo(file).fileName()),
  534                       BUTTON_YES | BUTTON_NO, BUTTON_YES) != BUTTON_YES) {
  535             return false;
  536         }
  537     }*/
  538     return true;
  539 }
  540 
  541 QString PageOutput::GetFileProtocol() {
  542     QRegExp protocol_regex("^([a-z0-9]+)://", Qt::CaseInsensitive, QRegExp::RegExp);
  543     if(protocol_regex.indexIn(GetFile()) < 0) {
  544         return QString();
  545     }
  546     return protocol_regex.cap(1);
  547 }
  548 
  549 QString PageOutput::GetContainerAVName() {
  550     enum_container container = GetContainer();
  551     if(container != CONTAINER_OTHER)
  552         return m_containers[container].avname;
  553     else
  554         return m_containers_av[GetContainerAV()].avname;
  555 }
  556 
  557 QString PageOutput::GetVideoCodecAVName() {
  558     enum_video_codec video_codec = GetVideoCodec();
  559     if(video_codec != VIDEO_CODEC_OTHER)
  560         return m_video_codecs[video_codec].avname;
  561     else
  562         return m_video_codecs_av[GetVideoCodecAV()].avname;
  563 }
  564 
  565 QString PageOutput::GetAudioCodecAVName() {
  566     enum_audio_codec audio_codec = GetAudioCodec();
  567     if(audio_codec != AUDIO_CODEC_OTHER)
  568         return m_audio_codecs[audio_codec].avname;
  569     else
  570         return m_audio_codecs_av[GetAudioCodecAV()].avname;
  571 }
  572 
  573 unsigned int PageOutput::FindContainerAV(const QString& name) {
  574     for(unsigned int i = 0; i < m_containers_av.size(); ++i) {
  575         if(m_containers_av[i].avname == name)
  576             return i;
  577     }
  578     return 0;
  579 }
  580 
  581 unsigned int PageOutput::FindVideoCodecAV(const QString& name) {
  582     for(unsigned int i = 0; i < m_video_codecs_av.size(); ++i) {
  583         if(m_video_codecs_av[i].avname == name)
  584             return i;
  585     }
  586     return 0;
  587 }
  588 
  589 unsigned int PageOutput::FindAudioCodecAV(const QString& name) {
  590     for(unsigned int i = 0; i < m_audio_codecs_av.size(); ++i) {
  591         if(m_audio_codecs_av[i].avname == name)
  592             return i;
  593     }
  594     return 0;
  595 }
  596 
  597 void PageOutput::OnUpdateSuffixAndContainerFields() {
  598 
  599     // change file extension
  600     enum_container new_container = GetContainer();
  601     unsigned int new_container_av = GetContainerAV();
  602     if(GetFileProtocol().isNull()) {
  603         QStringList old_suffixes = (m_old_container == CONTAINER_OTHER)? m_containers_av[m_old_container_av].suffixes : m_containers[m_old_container].suffixes;
  604         QStringList new_suffixes = (new_container == CONTAINER_OTHER)? m_containers_av[new_container_av].suffixes : m_containers[new_container].suffixes;
  605         QString file = GetFile();
  606         if(!file.isEmpty()) {
  607             QFileInfo fi(file);
  608             if(MatchSuffix(fi.suffix(), old_suffixes) && !MatchSuffix(fi.suffix(), new_suffixes)) {
  609                 if(new_suffixes.isEmpty())
  610                     m_lineedit_file->setText(fi.path() + "/" + fi.completeBaseName());
  611                 else
  612                     m_lineedit_file->setText(fi.path() + "/" + fi.completeBaseName() + "." + new_suffixes[0]);
  613             }
  614         }
  615     }
  616 
  617     // update fields
  618     OnUpdateContainerFields();
  619 
  620 }
  621 
  622 void PageOutput::OnUpdateContainerFields() {
  623 
  624     enum_container container = GetContainer();
  625     unsigned int container_av = GetContainerAV();
  626 
  627     // show/hide fields
  628     GroupVisible({m_label_container_av, m_combobox_container_av}, (container == CONTAINER_OTHER));
  629 
  630     // show/hide warning
  631     m_label_container_warning->setVisible(GetContainerAVName() == "mp4");
  632 
  633     // mark uninstalled or unsupported codecs
  634     for(unsigned int i = 0; i < VIDEO_CODEC_OTHER; ++i) {
  635         QString name = m_video_codecs[i].name;
  636         if(!AVCodecIsInstalled(m_video_codecs[i].avname))
  637             name += " (" + tr("not installed") + ")";
  638         else if(container != CONTAINER_OTHER && !m_containers[container].supported_video_codecs.count((enum_video_codec) i))
  639             name += " (" + tr("not supported by container") + ")";
  640         m_combobox_video_codec->setItemText(i, name);
  641     }
  642     for(unsigned int i = 0; i < AUDIO_CODEC_OTHER; ++i) {
  643         QString name = m_audio_codecs[i].name;
  644         if(!AVCodecIsInstalled(m_audio_codecs[i].avname))
  645             name += " (" + tr("not installed") + ")";
  646         else if(container != CONTAINER_OTHER && !m_containers[container].supported_audio_codecs.count((enum_audio_codec) i))
  647             name += " (" + tr("not supported by container") + ")";
  648         m_combobox_audio_codec->setItemText(i, name);
  649     }
  650 
  651     m_old_container = container;
  652     m_old_container_av = container_av;
  653 
  654 }
  655 
  656 void PageOutput::OnUpdateVideoCodecFields() {
  657     enum_video_codec codec = GetVideoCodec();
  658     MultiGroupVisible({
  659         {{m_label_video_kbit_rate, m_lineedit_video_kbit_rate}, (codec != VIDEO_CODEC_H264)},
  660         {{m_label_h264_crf, m_slider_h264_crf, m_label_h264_crf_value, m_label_h264_preset, m_combobox_h264_preset}, (codec == VIDEO_CODEC_H264)},
  661         {{m_label_vp8_cpu_used, m_combobox_vp8_cpu_used}, (codec == VIDEO_CODEC_VP8)},
  662         {{m_label_video_codec_av, m_combobox_video_codec_av, m_label_video_options, m_lineedit_video_options}, (codec == VIDEO_CODEC_OTHER)},
  663     });
  664 }
  665 
  666 void PageOutput::OnUpdateAudioCodecFields() {
  667     enum_audio_codec codec = GetAudioCodec();
  668     MultiGroupVisible({
  669         {{m_label_audio_kbit_rate, m_lineedit_audio_kbit_rate}, (codec != AUDIO_CODEC_UNCOMPRESSED)},
  670         {{m_label_audio_codec_av, m_combobox_audio_codec_av, m_label_audio_options, m_lineedit_audio_options}, (codec == AUDIO_CODEC_OTHER)},
  671     });
  672 }
  673 
  674 void PageOutput::OnBrowse() {
  675 
  676     QString filters;
  677     for(int i = 0; i < CONTAINER_OTHER; ++i) {
  678         if(i != 0)
  679             filters += ";;";
  680         filters += m_containers[i].filter;
  681     }
  682     for(unsigned int i = 0; i < m_containers_av.size(); ++i) {
  683         if(!m_containers_av[i].filter.isEmpty())
  684             filters += ";;" + m_containers_av[i].filter;
  685     }
  686 
  687     enum_container container = GetContainer();
  688     unsigned int container_av = GetContainerAV();
  689     QString selected_filter = (container == CONTAINER_OTHER)? m_containers_av[container_av].filter : m_containers[container].filter;
  690     QString selected_file = QFileDialog::getSaveFileName(this, tr("Save recording as"),
  691         GetFile(), filters, &selected_filter, QFileDialog::DontConfirmOverwrite);
  692 
  693     if(selected_file.isNull())
  694         return;
  695 
  696     m_lineedit_file->clear();
  697     QFileInfo fi(selected_file);
  698     if(fi.suffix().isEmpty()) {
  699         QStringList suffixes = (container == CONTAINER_OTHER)? m_containers_av[container_av].suffixes : m_containers[container].suffixes;
  700         if(!suffixes.isEmpty())
  701             selected_file += "." + suffixes[0];
  702     } else {
  703         bool found = false;
  704         for(int i = 0; i < CONTAINER_OTHER; ++i) {
  705             if(m_containers[i].suffixes.contains(fi.suffix(), Qt::CaseInsensitive)) {
  706                 SetContainer((enum_container) i);
  707                 found = true;
  708                 break;
  709             }
  710         }
  711         if(!found) {
  712             for(unsigned int i = 0; i < m_containers_av.size(); ++i) {
  713                 if(m_containers_av[i].suffixes.contains(fi.suffix(), Qt::CaseInsensitive)) {
  714                     SetContainer(CONTAINER_OTHER);
  715                     SetContainerAV(i);
  716                     break;
  717                 }
  718             }
  719         }
  720     }
  721     SetFile(selected_file);
  722 
  723     OnUpdateContainerFields();
  724 
  725 }
  726 
  727 void PageOutput::OnContinue() {
  728     if(!Validate())
  729         return;
  730     m_main_window->GoPageRecord();
  731 }