13

I'm interested in a library(for windows) written in Delphi/Pascal or C++ that allows me to record(to a video format) desktop screen, requirements:

  • must be able to specify the frame rate, or at least be able to record @ 5fps;
  • must be open source or free;
  • the output format could be almost any, but the quality must be good enough to be able to read text from the recording;

pluses, if possible:

  • option to record without colors(grayscale);
  • multiple display aware;
  • cross platform(Windows & Linux, other platforms would be nice as well, but not necessary);

If by any chance, I didn't explain something right, please feel free to ask so I can rephrase or give more details, etc.

  • "without colors" needs clarification. Maybe you mean monochrome? Ideally, you'd want to be able to reduce the color palette to save space. i.e. reduce to 256 colors. – Chris Thornton Nov 29 '11 at 20:50
  • Record to what? A series of .bmp frames? .mpg? Flash? H264? Record mouse/keyboard as well? – Chris Thornton Nov 29 '11 at 20:51
  • @Chris I've edited the question, but in short, any output form as long as the quality is good enough to read text from the recording. –  Nov 29 '11 at 21:00

7 Answers7

12

FFMPEG supports screen capturing(casting) and is cross platform.

Marcus Adams
  • 53,009
  • 9
  • 91
  • 143
  • 3
    +1 This is a good suggestion. You can even find [Delphi headers for ffmpeg](http://www.iversenit.dk/dev/ffmpeg-headers/) out there. – yms Nov 30 '11 at 00:21
  • 3
    @yms, absolutely, and I'm using them. But I don't recommend the ones labeled Delphi FFMPEG headers, as they're outdated and incomplete. Instead, find an open source Delphi project that uses FFMPEG (there are many) and try their headers out for size. – Marcus Adams Nov 30 '11 at 00:51
  • after a close battle between VLC and FFMPEG, I have to chose FFMPEG, I feel like it fits my needs better, thank you @Marcus. –  Dec 03 '11 at 19:48
  • Can you point to me to a Win32 (Windows) c++ tool that uses FFMPEG? – Michael Haephrati Aug 15 '19 at 10:46
4

You could try Windows Media Encoder (freeware, wmv/asf only) or VLC (GPL, Win/OSX/Linux). Be aware that "hardware accelerated" views (Direct3D & OpenGL rendering for example) will not be available, and some quality loss will be experienced due to video compression. How much you lose will depend on your settings (codec, bitrate, resolution, etc)

Example: How to Stream your Desktop using VLC

vlc screen:// :screen-fps=30 :screen-caching=100 --sout '#transcode{vcodec=mp4v,vb=4096,acodec=mpga,ab=256,scale=1,width=1280,height=800}:rtp{dst=192.168.1.2,port=1234,access=udp,mux=ts}'

You can find more options in VLC documentation, for saving your stream as a file for example.

yms
  • 10,361
  • 3
  • 38
  • 68
3

This is the one I use with Delphi, it's called "Professional Screen Camera Component". Admittedly I had to make some changes to support unicode versions (replace PChar with PAnsiChar, replace Char with AnsiChar).

It'll happily record away at whatever framerate I set it to, will encode the video with whatever codec I specify (if I want it to), and allows you to specify the region you wish to record.

Comes with a demo project too!

Oh, and it's free/open source!

LaKraven
  • 5,804
  • 2
  • 23
  • 49
  • thank you, this is a good option also, I'm between VCL, FFMPEG and this. –  Nov 30 '11 at 00:38
  • I actually used the demo built on this component to record my Lua4Delphi demo video... just so you know it actually works (and supports HD). – LaKraven Nov 30 '11 at 00:40
  • I was in the middle of developing a STREAMING component to sit along-side it.... since that (coupled with a keyboard/mouse capture and emulation system) would form a good basis for a Remote Desktop server/client architecture too! – LaKraven Nov 30 '11 at 00:41
  • VLC can do streaming, and for remote desktop, windows RDS can do it quite nicely... I don't see a good reason to reinvent the wheel (: –  Nov 30 '11 at 00:52
  • 1
    @DorinDuminca, you know me well enough to know I live to reinvent the wheel ;) – LaKraven Nov 30 '11 at 01:13
2

FFmpeg can be used to capture the screen.

watch the screen recorded video demo using FFMPEG : https://www.youtube.com/watch?v=a31bBY3HuxE

Container format : MP4

Codec : MPEG4

Follow the steps to record the screen in video using FFmpeg and other libraries.

  1. Initialize required registers

  2. use x11grab(for linux OS) in av_find_input_format

  3. mention the posistion to capture the video in screen (Eg. ":0.0+10,250" in av_format_open_input)

  4. Now go for regular video parameters initialization and memory allocation.

  5. start capturing the frames and store it in a file.

  6. Finally, release the allocated resources once completed !.

below code is written in c++ and uses linux(ubuntu) platform video format is in mp4 format.

// sample code to record the computer screen !

      #ifndef SCREENRECORDER_H
      #define SCREENRECORDER_H

      #include <iostream>
      #include <cstdio>
      #include <cstdlib>
      #include <fstream>
      #include <cstring>
      #include <math.h>
      #include <string.h>

      #define __STDC_CONSTANT_MACROS

      //FFMPEG LIBRARIES
      extern "C"
      {
      #include "libavcodec/avcodec.h"
      #include "libavcodec/avfft.h"

      #include "libavdevice/avdevice.h"

      #include "libavfilter/avfilter.h"
      #include "libavfilter/avfiltergraph.h"
      #include "libavfilter/buffersink.h"
      #include "libavfilter/buffersrc.h"

      #include "libavformat/avformat.h"
      #include "libavformat/avio.h"

      // libav resample

      #include "libavutil/opt.h"
      #include "libavutil/common.h"
      #include "libavutil/channel_layout.h"
      #include "libavutil/imgutils.h"
      #include "libavutil/mathematics.h"
      #include "libavutil/samplefmt.h"
      #include "libavutil/time.h"
      #include "libavutil/opt.h"
      #include "libavutil/pixdesc.h"
      #include "libavutil/file.h"

      // lib swresample

      #include "libswscale/swscale.h"

      }



      class ScreenRecorder
      {
      private:
        AVInputFormat *pAVInputFormat;
        AVOutputFormat *output_format;

        AVCodecContext *pAVCodecContext;

        AVFormatContext *pAVFormatContext;

        AVFrame *pAVFrame;
        AVFrame *outFrame;

        AVCodec *pAVCodec;
        AVCodec *outAVCodec;

        AVPacket *pAVPacket;

        AVDictionary *options;

        AVOutputFormat *outAVOutputFormat;
        AVFormatContext *outAVFormatContext;
        AVCodecContext *outAVCodecContext;

        AVStream *video_st;
        AVFrame *outAVFrame;

        const char *dev_name;
        const char *output_file;

        double video_pts;

        int out_size;
        int codec_id;
        int value;
        int VideoStreamIndx;

      public:

        ScreenRecorder();
        ~ScreenRecorder();

        int openCamera();
        int init_outputfile();
        int collectFrames();

      };

      #endif


      using namespace std;

      ScreenRecorder::ScreenRecorder()
      {
        cout<<"\n\n Registering required functions...";
        av_register_all();
        avcodec_register_all();
        avdevice_register_all();
        cout<<"\n\n Registered successfully...";
      }

      ScreenRecorder::~ScreenRecorder()
      {

        avformat_close_input(&pAVFormatContext);
        if( !pAVFormatContext )
        {
        cout<<"\n\n1.Success : avformat_close_input()";
        }
        else
        {
        cout<<"\n\nError : avformat_close_input()";
        }

        avformat_free_context(pAVFormatContext);
        if( !pAVFormatContext )
        {
        cout<<"\n\n2.Success : avformat_free_context()";
        }
        else
        {
        cout<<"\n\nError : avformat_free_context()";
        }

      cout<<"\n\n---------------Successfully released all resources------------------\n\n\n";
      cout<<endl;
      cout<<endl;
      cout<<endl;
      }

      int ScreenRecorder::collectFrames()
      {
        int flag;
        int frameFinished;
      //when you decode a single packet, you still don't have information enough to have a frame [depending on the type of codec, some of them //you do], when you decode a GROUP of packets that represents a frame, then you have a picture! that's why frameFinished will let //you know you decoded enough to have a frame.

        int frame_index = 0;
        value = 0;

        pAVPacket = (AVPacket *)av_malloc(sizeof(AVPacket));
        av_init_packet(pAVPacket);

        pAVFrame = av_frame_alloc();
        if( !pAVFrame )
        {
         cout<<"\n\nError : av_frame_alloc()";
         return -1;
        }

        outFrame = av_frame_alloc();//Allocate an AVFrame and set its fields to default values.
        if( !outFrame )
        {
         cout<<"\n\nError : av_frame_alloc()";
         return -1;
        }

        int video_outbuf_size;
        int nbytes = av_image_get_buffer_size(outAVCodecContext->pix_fmt,outAVCodecContext->width,outAVCodecContext->height,32);
        uint8_t *video_outbuf = (uint8_t*)av_malloc(nbytes);
        if( video_outbuf == NULL )
        {
        cout<<"\n\nError : av_malloc()";
        }

        // Setup the data pointers and linesizes based on the specified image parameters and the provided array.
        value = av_image_fill_arrays( outFrame->data, outFrame->linesize, video_outbuf , AV_PIX_FMT_YUV420P, outAVCodecContext->width,outAVCodecContext->height,1 ); // returns : the size in bytes required for src
        if(value < 0)
        {
        cout<<"\n\nError : av_image_fill_arrays()";
        }

        SwsContext* swsCtx_ ;

        // Allocate and return swsContext.
        // a pointer to an allocated context, or NULL in case of error
        // Deprecated : Use sws_getCachedContext() instead.
        swsCtx_ = sws_getContext(pAVCodecContext->width,
                            pAVCodecContext->height,
                            pAVCodecContext->pix_fmt,
                            outAVCodecContext->width,
                    outAVCodecContext->height,
                            outAVCodecContext->pix_fmt,
                            SWS_BICUBIC, NULL, NULL, NULL);


      int ii = 0;
      int no_frames = 100;
      cout<<"\n\nEnter No. of Frames to capture : ";
      cin>>no_frames;

        AVPacket outPacket;
        int j = 0;

        int got_picture;

        while( av_read_frame( pAVFormatContext , pAVPacket ) >= 0 )
        {
        if( ii++ == no_frames )break;
            if(pAVPacket->stream_index == VideoStreamIndx)
            {
                value = avcodec_decode_video2( pAVCodecContext , pAVFrame , &frameFinished , pAVPacket );
                if( value < 0)
                {
                    cout<<"Error : avcodec_decode_video2()";
                }

                if(frameFinished)// Frame successfully decoded :)
                {
                    sws_scale(swsCtx_, pAVFrame->data, pAVFrame->linesize,0, pAVCodecContext->height, outFrame->data,outFrame->linesize);
                    av_init_packet(&outPacket);
                    outPacket.data = NULL;    // packet data will be allocated by the encoder
                    outPacket.size = 0;

                    avcodec_encode_video2(outAVCodecContext , &outPacket ,outFrame , &got_picture);

                    if(got_picture)
                    {
                        if(outPacket.pts != AV_NOPTS_VALUE)
                            outPacket.pts = av_rescale_q(outPacket.pts, video_st->codec->time_base, video_st->time_base);
                        if(outPacket.dts != AV_NOPTS_VALUE)
                            outPacket.dts = av_rescale_q(outPacket.dts, video_st->codec->time_base, video_st->time_base);

                        printf("Write frame %3d (size= %2d)\n", j++, outPacket.size/1000);
                        if(av_write_frame(outAVFormatContext , &outPacket) != 0)
                        {
                            cout<<"\n\nError : av_write_frame()";
                        }

                    av_packet_unref(&outPacket);
                    } // got_picture

                av_packet_unref(&outPacket);
                } // frameFinished

            }
        }// End of while-loop

        value = av_write_trailer(outAVFormatContext);
        if( value < 0)
        {
            cout<<"\n\nError : av_write_trailer()";
        }


      //THIS WAS ADDED LATER
      av_free(video_outbuf);

      }

      int ScreenRecorder::openCamera()
      {

        value = 0;
        options = NULL;
        pAVFormatContext = NULL;

        pAVFormatContext = avformat_alloc_context();//Allocate an AVFormatContext.

        pAVInputFormat = av_find_input_format("x11grab");
        value = avformat_open_input(&pAVFormatContext, ":0.0+10,250", pAVInputFormat, NULL);
        if(value != 0)
        {
           cout<<"\n\nError : avformat_open_input\n\nstopped...";
           return -1;
        }

        value = av_dict_set( &options,"framerate","30",0 );
        if(value < 0)
        {
          cout<<"\n\nError : av_dict_set(framerate , 30 , 0)";
          return -1;
        }

        value = av_dict_set( &options, "preset", "medium", 0 );
        if(value < 0)
        {
          cout<<"\n\nError : av_dict_set(preset , medium)";
              return -1;
        }

      //    value = avformat_find_stream_info(pAVFormatContext,NULL);
        if(value < 0)
        {
          cout<<"\n\nError : avformat_find_stream_info\nstopped...";
          return -1;
        }

        VideoStreamIndx = -1;

        for(int i = 0; i < pAVFormatContext->nb_streams; i++ ) // find video stream posistion/index.
        {
          if( pAVFormatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO )
          {
             VideoStreamIndx = i;
             break;
          }

        } // End for-loop

        if( VideoStreamIndx == -1)
        {
          cout<<"\n\nError : VideoStreamIndx = -1";
          return -1;
        }

        // assign pAVFormatContext to VideoStreamIndx
        pAVCodecContext = pAVFormatContext->streams[VideoStreamIndx]->codec;

        pAVCodec = avcodec_find_decoder(pAVCodecContext->codec_id);
        if( pAVCodec == NULL )
        {
          cout<<"\n\nError : avcodec_find_decoder()";
          return -1;
        }

        value = avcodec_open2(pAVCodecContext , pAVCodec , NULL);//Initialize the AVCodecContext to use the given AVCodec.
        if( value < 0 )
        {
          cout<<"\n\nError : avcodec_open2()";
          return -1;
        }
      }

      int ScreenRecorder::init_outputfile()
      {
        outAVFormatContext = NULL;
        value = 0;
        output_file = "output.mp4";

        avformat_alloc_output_context2(&outAVFormatContext, NULL, NULL, output_file);
        if (!outAVFormatContext)
        {
            cout<<"\n\nError : avformat_alloc_output_context2()";
          return -1;
        }

      /*Returns the output format in the list of registered output formats which best matches the provided parameters, or returns NULL if there is no match.
      */
        output_format = av_guess_format(NULL, output_file ,NULL);
        if( !output_format )
        {
         cout<<"\n\nError : av_guess_format()";
         return -1;
        }

        video_st = avformat_new_stream(outAVFormatContext ,NULL);
        if( !video_st )
        {
            cout<<"\n\nError : avformat_new_stream()";
          return -1;
        }

        outAVCodecContext = avcodec_alloc_context3(outAVCodec);
        if( !outAVCodecContext )
        {
          cout<<"\n\nError : avcodec_alloc_context3()";
          return -1;
        }

        outAVCodecContext = video_st->codec;
        outAVCodecContext->codec_id = AV_CODEC_ID_MPEG4;// AV_CODEC_ID_MPEG4; // AV_CODEC_ID_H264 // AV_CODEC_ID_MPEG1VIDEO
        outAVCodecContext->codec_type = AVMEDIA_TYPE_VIDEO;
        outAVCodecContext->pix_fmt  = AV_PIX_FMT_YUV420P;
        outAVCodecContext->bit_rate = 400000; // 2500000
        outAVCodecContext->width = 1920;
        outAVCodecContext->height = 1080;
        outAVCodecContext->gop_size = 3;
        outAVCodecContext->max_b_frames = 2;
        outAVCodecContext->time_base.num = 1;
        outAVCodecContext->time_base.den = 30; // 15fps

        if (codec_id == AV_CODEC_ID_H264)
        {
         av_opt_set(outAVCodecContext->priv_data, "preset", "slow", 0);
        }

        outAVCodec = avcodec_find_encoder(AV_CODEC_ID_MPEG4);
        if( !outAVCodec )
        {
         cout<<"\n\nError : avcodec_find_encoder()";
         return -1;
        }

      // Some container formats (like MP4) require global headers to be present
      // Mark the encoder so that it behaves accordingly.

        if ( outAVFormatContext->oformat->flags & AVFMT_GLOBALHEADER)
        {
            outAVCodecContext->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
        }

        value = avcodec_open2(outAVCodecContext, outAVCodec, NULL);
        if( value < 0)
        {
            cout<<"\n\nError : avcodec_open2()";
            return -1;
        }

        if ( !(outAVFormatContext->flags & AVFMT_NOFILE) )
        {
         if( avio_open2(&outAVFormatContext->pb , output_file , AVIO_FLAG_WRITE ,NULL, NULL) < 0 )
         {
          cout<<"\n\nError : avio_open2()";
         }
        }



        if(!outAVFormatContext->nb_streams)
        {
            cout<<"\n\nError : Output file dose not contain any stream";
          return -1;
        }

        value = avformat_write_header(outAVFormatContext , &options);
        if(value < 0)
        {
            cout<<"\n\nError : avformat_write_header()";
            return -1;
        }

        cout<<"\n\nOutput file information :\n\n";
        av_dump_format(outAVFormatContext , 0 ,output_file ,1);
      }

      int main()
      {
        ScreenRecorder s_record;

        s_record.openCamera();
        s_record.init_outputfile();
        s_record.collectFrames();

        cout<<"\n\n---------EXIT_SUCCESS------------\n\n";

      return 0;
      }


      /* to compile the code : g++ -Wno-format-zero-length -Wno-write-strings -L/home/abdullah/ffmpeg_build/lib/ -L/usr/lib/x86_64-linux-gnu/ -I/home/abdullah/ffmpeg_build/include/ -o ScreenRecorder ScreenRecorder.cpp -lavdevice -lavfilter -lswscale -lavformat -lavcodec -lavutil -lswresample -lm -lva -lpthread -lvorbis -lvpx -lopus -lz -lpostproc -ldl -lfdk-aac -lmp3lame -lvorbisenc -lvorbisfile -lx264 -ltheora -lx265 -ltheoraenc -ltheoradec -ldl -lrt -lbz2 -lasound -lSDL -lSDLmain -lSDL_ttf -lfreetype -lass -llzma -lftgl -lperl -lcrypto -lxcb -lxcb-shm -lxcb-xfixes -lao -lxcb-shape -lfftw3 */

Here's the screenshot. Here's the screenshot

complete working code in github link:

Abdullah Farweez
  • 851
  • 2
  • 11
  • 25
2

It is probably overkill for your needs, but the video grabber component from DataStead can also record screen activity and save the output as video file. See http://www.datastead.com/products/tvideograbber/overview.html. I'm not associated with DataStead, but have been a customer for a few years and it works great.

ByteArts
  • 156
  • 1
  • 7
  • 3
    sorry, I can't give you a +1, "Limitations: the evaluation packages overlay a nag-screen over the video window. Also the preview, recording and playback stop randomly after a few minutes. These limitations are removed in the licensed packages.", I asked for "open source or free", NOT something limited, especially no "random stops" (: –  Nov 29 '11 at 22:15
1

I haven't done this myself before, but when I googled around (as I'm sure you have), I ran into this:

http://www.codeproject.com/KB/GDI/barry_s_screen_capture.aspx

It looks as if it should do what you're asking reasonably easily (for Windows), and it has no license associated with it (as confirmed at the bottom). I don't believe its set up as a library, but I'm sure you could bind the interface to the sample WinCap functions into one with reasonable ease.

John Humphreys
  • 37,047
  • 37
  • 155
  • 255
  • I'm sorry, I think you didn't understand my question, NOT screen-shots, a video of the desktop, but it could be converted to a video I guess, but this involves extra steps... –  Nov 29 '11 at 21:01
  • 1
    A video is a series of screen captures. Ideally with some sort of key-frame + delta compression. So what you need, is a way to take a series of frames, and transcode into flash, .mpg, etc.. – Chris Thornton Nov 29 '11 at 21:13
  • @Chris yes, I understand that, but if the "series of screenshots" would have suffice, I would have NOT posted this question, what I need is a way of capturing into a video format(almost any), in a medium to high quality(or enough to read text) and be able to specify the fps, yms gave a very good answer. –  Nov 29 '11 at 21:32
0

use screen capture lite https://github.com/smasherprog/screen_capture_lite

This is a C++ library and cross-platform

Anil8753
  • 2,663
  • 4
  • 29
  • 40
  • I'm wondering how to save in video format using this library. I run the examples, and I get the screenshots. But my goal is to capture these data as video files without saving them as images. Could you please show me a way? – Tunahan Oct 12 '20 at 08:35