Startseite | Programmieren | C++ | OpenGL Screenshots
RSS Githublogo Youtubelogo higgs.at kovacs.cf

Es gibt unterschiedliche Gründe, um Screenshots mit OpenGL zu erstellen. Will man z.B. automatisiert Screenshots von einem OpenGL-Programm machen lassen zwecks Dokumentation. Ein anderer Grund kann auch sein, dass man von einem Programm ein Video machen will und alle vorgefertigten Anwendungen unzufriedenstellend sind. Um dies zu bewerkstelligen (ein Video von einem OpenGL-Programm) muss zu jedem Frame ein Screenshot gemacht werden. Aus dieser Bildfolge kann dann ein Video erstellt werden.

Erstellt man das Video selber aus Bildern können viele Parameter selbst eingestellt werden, was die Qualität des Videos im Vergleich zu "Screenrecorder" im Allgemeinen verbessert.

Manche Programme können auch nicht "live" aufgenommen werden, weil die Berechnungen zwischen den Frames zu lange und besonders unterschiedlich lange dauert. Eine direkte Aufnahme live ist hier speichertechnisch und programmiertechnisch sehr unvorteilhaft.

Eine Funktion unter C++, welche einen Screenshot des aktuellen Programms macht ist:
bool screenshot_png(const std::string& filename, int width, int height, std::vector<unsigned char>& pixel)
{

   glReadPixels(0, 0, wnd_width, height, GL_RGBA, GL_UNSIGNED_BYTE, &pixel[0]);

   FILE *fp = fopen(filename.c_str(), "wb");

   if(fp == 0)
      return false;

   png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);

   if(png_ptr == 0)
   {
      fclose(fp);
      return false;
   }

   png_infop info_ptr = png_create_info_struct(png_ptr);

   if(info_ptr == 0)
   {
      fclose(fp);
      png_destroy_write_struct(&png_ptr, png_infopp_NULL);
      return false;
   }

   png_init_io(png_ptr, fp);
   png_set_IHDR(png_ptr, info_ptr, wnd_width, height, 8, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
   png_write_info(png_ptr, info_ptr);

   std::vector<png_bytep> row_pointers(height);

   for(int i=0; i < height; ++i)
   {
      int ii = height - i - 1;
      row_pointers[ii] = &pixel[i * wnd_width * 4];
   }

   png_write_image(png_ptr, &row_pointers[0]);

   png_write_end(png_ptr, info_ptr);
   png_destroy_write_struct(&png_ptr, &info_ptr);
   fclose(fp);

   return true;
}

   const size_t format_nchannels = 4;
   std::vector<unsigned char> pixel_store(format_nchannels * wnd_width * wnd_height);
   screenshot_png(filename, wnd_width, wnd_height, pixel_store);

Selbst die beste Qualität mit recordmydesktop war qualitatsmäßig nicht ausreichend.

Im Gegensatz dazu bietet ein selbst erstelltes Video (mittels ffmpeg aus PNGs) eine deutlich bessere Qualität.

Der Befehlt mit dem die Videos erstellt wurden:
ffmpeg -framerate 50 -i png_screen%d.png -c:v libx264 -preset 0 -crf 0 -r 30 -pix_fmt yuv420p output.mp4

Wird die graphische Ausgabe des Programms direkt in das PNG gelenkt und nichts am Bildschirm ausgegeben (offscreen rendering), so ist dies nicht schneller als wenn die graphische Ausgabe ebenso am Bildschirm ausgegeben wird sowie ein Bild erstellt wird (onscreen rendering). Solange nichts parallel gemacht wird, ist die Geschwindigkeit zwischen den beiden rendering-Arten gleich schnell.

Vergleich zwischen recordmydesktop und der PNG-Bildschirm render Methode:

recordmydesktop
PNG render

Hinweis: Beide Videos wurden mit bestem Wissen und Gewissen in Bedacht auf maximale Qualität zum damaligen Zeitpunkt erzeugt. Bessere Qualität sind je nach Wissen und Hardware möglich!

Trotzdem können, sollte man das Video aus Bildern selbst erstellen, die Parameter meiner Meinung nach besser und feiner eingestellt werden und deswegen ist diese Methode (trotz längerer Programmlaufzeit) vorgefertigten Screenrecordern vorzuziehen.