High quality MPG4 Video & Audio encoding. (WIN32)

Headers, Bindings, Libraries for use with FreeBASIC, Please include example of use to help ensure they are tested and usable.
Post Reply
D.J.Peters
Posts: 8586
Joined: May 28, 2005 3:28
Contact:

High quality MPG4 Video & Audio encoding. (WIN32)

Post by D.J.Peters »

Now you can capture your FreeBASIC or OpenGL Screen as MPG4 XVid Video with Audio may be from FBSound ;-).

http://www.xvid.org/

I needed to capture a window in high quality for my job
so i wrote a windows dll with my old VS6.

Download: XVidCapture.zip

I made it easy as posible :-)

I's nice to post FreeBASIC fbGFX or OpenGL demos/games on youtube with audio.

Joshy

test01.bas

Code: Select all

' test01.bas 32bit pixel stream without audio
#include once "xvidcapture.bi"

const as integer iWidth  = 320
const as integer iHeight = 240
const as integer iBPP    = 32

chdir exepath

if OpenVideoStream("test01.avi",iWidth,iHeight)=0 then
  print "error: OpenVideoStream() !"
  beep:sleep:end 1
end if

screenres iWidth,iHeight,iBPP

dim as integer nFrames,nBytes

while nFrames<(iHeight/2)
  nFrames+=1
  windowtitle "only " & nBytes & " written"
  cls
  draw string (nFrames*2 mod iWidth,nFrames*2 mod iHeight), "Frame " & nFrames
  screenlock
    select case iBPP
    case 32 : nBytes+=Write32BitPixels(ScreenPtr())
    case 16 : nBytes+=Write16BitPixels(ScreenPtr())
    end select
  screenunlock
  sleep 100
wend
CloseStream()
test02.bas

Code: Select all

' test02.bas 16bit pixel stream without audio
#include once "xvidcapture.bi"

const as integer iWidth  = 320
const as integer iHeight = 240
const as integer iBPP    = 16

chdir exepath

if OpenVideoStream("test02.avi",iWidth,iHeight)=0 then
  print "error: OpenVideoStream() !"
  beep:sleep:end 1
end if

screenres iWidth,iHeight,iBPP

dim as integer nFrames,nBytes

while nFrames<(iHeight/2)
  nFrames+=1
  windowtitle "only " & nBytes & " written"
  cls
  draw string ((nFrames*2) mod iWidth,(nFrames*2) mod iHeight), "Frame " & nFrames
  screenlock
    select case iBPP
    case 32 : nBytes+=Write32BitPixels(ScreenPtr())
    case 16 : nBytes+=Write16BitPixels(ScreenPtr())
    end select
  screenunlock
  sleep 100
wend
CloseStream()
test03.bas

Code: Select all

' test03.bas 
' video 32bit pixel 24 FPS
' with audio 16bit mono 22KHz.

#include once "xvidcapture.bi"

const as integer iWidth    = 320
const as integer iHeight   = 240
const as integer iBPP      = 32
const as integer iFPS      = 24
const as integer iQuality  = 75
const as integer iChannels =  1
const as integer iBits     = 16
const as integer iRate     = 22050

chdir exepath

if OpenVideoAudioStream("test03.avi",iWidth,iHeight,iFPS,iQuality,iChannels,iBits,iRate)=0 then
  print "error: OpenVideoAudioStream() !"
  beep:sleep:end 1
end if

' how many audio samples per frame ?
const as integer nSamples = iRate\iFPS
' how many bytes per audio buffer
const as integer BufferSize = nSamples*sizeof(short)
' a 16 bit mono audio buffer
dim as short Samples(nSamples-1)

const as single Hz    = 880
const as single PI    = atn(1)*4
const as single wStep = (2*PI)/iRate*Hz

screenres iWidth,iHeight,iBPP
windowtitle "MPG4 Video + Audio encoding"
  
dim as single w
dim as integer nFrames,nBytes

while nFrames<(iHeight/2)
  nFrames+=1

  cls
  draw string ((nFrames*2) mod iWidth,(nFrames*2) mod iHeight), "Frame " & nFrames
  screenlock
    select case iBPP
    case 32 : Write32BitPixels(ScreenPtr())
    case 16 : Write16BitPixels(ScreenPtr())
    end select
  screenunlock
  for i as integer=0 to nSamples-1
    Samples(i)=sin(w)*5000
    w+=wStep*cos(i/50)
  next
  WriteAudioSamples(@Samples(0),BufferSize)
  sleep 100
wend
CloseStream()
Last edited by D.J.Peters on Oct 12, 2022 18:40, edited 3 times in total.
chung
Posts: 648
Joined: Jan 16, 2010 20:52
Location: France
Contact:

Post by chung »

super !! , i shall use it
kiyotewolf
Posts: 1009
Joined: Oct 11, 2008 7:42
Location: ABQ, NM
Contact:

Post by kiyotewolf »

"so i wrote a windows dll with my old VS6. "

Whats VS6?

Do the frames that get written, are they realtime? How do the frames in the video get their timestamps?



~Kiyote!
D.J.Peters
Posts: 8586
Joined: May 28, 2005 3:28
Contact:

Post by D.J.Peters »

kiyotewolf wrote:Whats VS6?
Microsoft Visual Studio 6 Pro
C++, Visual Basic 6, ... 1994-98
kiyotewolf wrote:Do the frames that get written, are they realtime?
High quality video compression needs some CPU power.
If you don't use threads your main process and the encoder must share the CPU power.
It's bad for "realtime".

Here are how you you can do it with thread's.

pseudo code:

Code: Select all

Create_Video_and_Audio_Stream_With_25FPS(...)
Loop_With_100_FPS
  Render_Video_And_Audio(...)
  if (frames mod 4)=0 then '100/4=25 FPS
    Write_Curent_Frame_and_Audio_In_Thread(...)
  end if
  frames+=1
End_loop
Close_Stream()
kiyotewolf wrote:How do the frames in the video get their timestamps?
There are no timestamps.

Your video player (decoder) knows only the playback speed FPS from stream.
(often seeking works with a frame table inside an AVI container)

Joshy
kiyotewolf
Posts: 1009
Joined: Oct 11, 2008 7:42
Location: ABQ, NM
Contact:

Post by kiyotewolf »

@D.J. Peters / Joshy

You are quite good at this stuff.
I can't thank you enough, you keep making great useful code on this forum like eggs on easter.

You are a resource! THANKS a TON, for this tool, and everything you're up to.



~Kiyote!
D.J.Peters
Posts: 8586
Joined: May 28, 2005 3:28
Contact:

Post by D.J.Peters »

Not my first FreeBASIC video on youtube but the first XVid video :-)

http://www.youtube.com/watch?v=l8raklW0FaU

Joshy
chung
Posts: 648
Joined: Jan 16, 2010 20:52
Location: France
Contact:

Re: High quality MPG4 Video & Audio encoding.

Post by chung »

do you plan to release it as open source ?
D.J.Peters
Posts: 8586
Joined: May 28, 2005 3:28
Contact:

Re: High quality MPG4 Video & Audio encoding.

Post by D.J.Peters »

chung wrote:do you plan to release it as open source ?
My file "XvidCapture.cpp" you need this libs "revel.lib" "libxvidcore.lib"

Code: Select all

#include <revel.h>

#define WIN32_LEAN_AND_MEAN
#include <windows.h>

#ifdef XVIDCAPTURE_EXPORTS
#define _API_ __declspec(dllexport)
#else
#define _API_ __declspec(dllimport)
#endif

struct ENCODER {
  int              Allocated;
  int              hEncoder;
  int              Started;
  Revel_Params     Params;
  Revel_VideoFrame Frame;
};

ENCODER gEncoder;

void EncoderReset() {
  if (gEncoder.Allocated!=0) {
    if (gEncoder.Started!=0) {
      Revel_EncodeEnd(gEncoder.hEncoder);
    }   
    Revel_DestroyEncoder(gEncoder.hEncoder);
  }
  memset(&gEncoder, 0, sizeof(ENCODER));
}

BOOL APIENTRY DllMain( HANDLE hModule, 
                       DWORD  dwReason, 
                       LPVOID lpReserved) {
  // Perform actions based on the reason for calling.
  switch( dwReason ) 
  { 
    case DLL_PROCESS_ATTACH:
    // Initialize once for each new process.
    // Return FALSE to fail DLL load.
	  break;

    case DLL_THREAD_ATTACH:
    // Do thread-specific initialization.
    break;

    case DLL_THREAD_DETACH:
    // Do thread-specific cleanup.
    break;

    case DLL_PROCESS_DETACH:
    // Perform any necessary cleanup.
    EncoderReset();
    break;
  }
 
  return TRUE;
}

#ifdef __cplusplus
	extern "C" {
#endif

_API_ int OpenVideoStream(const char *filename,int w,int h,int f,int q){
  EncoderReset();
  
  if (w<1) return 0;
  if (h<1) return 0;

  if (f<  1) f =   1;
  if (f>100) f = 100;
  if (q<0)   q =   0;
  if (q>100) q = 100;

  Revel_Error revError = Revel_CreateEncoder(&gEncoder.hEncoder);
  if (revError != REVEL_ERR_NONE) return 0;
  gEncoder.Allocated = 1;

  Revel_InitializeParams(&gEncoder.Params);
  gEncoder.Params.width     = w;
  gEncoder.Params.height    = h;
  gEncoder.Params.frameRate = (float) f;
  gEncoder.Params.quality   = (float) q;
  gEncoder.Params.quality/=100.0f;
  gEncoder.Params.codec     = REVEL_CD_XVID;

  gEncoder.Params.hasAudio  = 0;
  revError = Revel_EncodeStart(gEncoder.hEncoder, filename, &gEncoder.Params);
  if (revError != REVEL_ERR_NONE) {
    EncoderReset();
    return 0;
  }

  gEncoder.Started = 1;
  return 1;
}


_API_ int OpenVideoAudioStream(const char *filename,int w,int h,int f,int q, int c, int b,int r){
  EncoderReset();
  
  if (w<1) return 0;
  if (h<1) return 0;
  
  if (f<  1) f =   1;
  if (f>100) f = 100;
  if (q<0)   q =   0;
  if (q>100) q = 100;

  if (c<1) c=1;
  if (c>2) c=2;
  
  b>>=3; // bits to bytes
  if (b<1) b=1;
  if (b>4) b=4;
  b<<=3; //bytes to bits

  if (r<22050)
    r=11025;
  else if (r<44100)
    r=22050;
  else
    r=44100;


  Revel_Error revError = Revel_CreateEncoder(&gEncoder.hEncoder);
  if (revError != REVEL_ERR_NONE) return 0;
  gEncoder.Allocated = 1;

  Revel_InitializeParams(&gEncoder.Params);
  gEncoder.Params.width     = w;
  gEncoder.Params.height    = h;
  gEncoder.Params.frameRate = (float) f;
  gEncoder.Params.quality   = (float) q;
  gEncoder.Params.quality/=100.0f;
  gEncoder.Params.codec             = REVEL_CD_XVID;

  gEncoder.Params.hasAudio          = 1;
  gEncoder.Params.audioChannels     = c;
  gEncoder.Params.audioRate         = r;
  gEncoder.Params.audioBits         = b;
  gEncoder.Params.audioSampleFormat = REVEL_ASF_PCM;
  
  revError = Revel_EncodeStart(gEncoder.hEncoder, filename, &gEncoder.Params);
  if (revError != REVEL_ERR_NONE) {
    EncoderReset();
    return 0;
  }

  gEncoder.Started = 1;
  return 1;
}

_API_ void CloseStream(void){
  EncoderReset();
}

_API_ int Write32BitPixels(void * pixels){
  if (!gEncoder.Allocated) return 0;
  if (!gEncoder.Started) return 0;
  if (!pixels) return 0;
  
  gEncoder.Frame.bytesPerPixel = 4;
  gEncoder.Frame.pixelFormat   = REVEL_PF_RGBA;
  gEncoder.Frame.pixels        = pixels;
  gEncoder.Frame.width         = gEncoder.Params.width;
  gEncoder.Frame.height        = gEncoder.Params.height;
  int bytesWritten=0;
  Revel_Error revError = Revel_EncodeFrame(gEncoder.hEncoder, &gEncoder.Frame, &bytesWritten);
  if (revError != REVEL_ERR_NONE) return 0;
  return bytesWritten;
}

_API_ int Write24BitPixels(void * pixels){
  if (!gEncoder.Allocated) return 0;
  if (!gEncoder.Started) return 0;
  if (!pixels) return 0;
  gEncoder.Frame.bytesPerPixel = 3;
  gEncoder.Frame.pixelFormat   = REVEL_PF_BGR;
  gEncoder.Frame.pixels        = pixels;
  gEncoder.Frame.width         = gEncoder.Params.width;
  gEncoder.Frame.height        = gEncoder.Params.height;
  int bytesWritten=0;
  Revel_Error revError = Revel_EncodeFrame(gEncoder.hEncoder, &gEncoder.Frame, &bytesWritten);
  if (revError != REVEL_ERR_NONE) return 0;
  return bytesWritten;
}

_API_ int Write16BitPixels(void * pixels){
  if (!gEncoder.Allocated) return 0;
  if (!gEncoder.Started) return 0;
  if (!pixels) return 0;
  gEncoder.Frame.bytesPerPixel = 2;
  gEncoder.Frame.pixelFormat   = REVEL_PF_RGB565;
  gEncoder.Frame.pixels        = pixels;
  gEncoder.Frame.width         = gEncoder.Params.width;
  gEncoder.Frame.height        = gEncoder.Params.height;
  int bytesWritten=0;
  Revel_Error revError = Revel_EncodeFrame(gEncoder.hEncoder, &gEncoder.Frame, &bytesWritten);
  if (revError != REVEL_ERR_NONE) return 0;
  return bytesWritten;
}


_API_ int WriteAudioSamples(char * samples,int nBytes){
  if (!gEncoder.Allocated) return 0;
  if (!gEncoder.Started) return 0;
  if (!samples) return 0;
  if (nBytes<1) return 0;
  int bytesWritten=0;
  Revel_Error revError = Revel_EncodeAudio(gEncoder.hEncoder, samples, nBytes,  &bytesWritten);
  if (revError != REVEL_ERR_NONE) return 0;
  return bytesWritten;
}

#ifdef __cplusplus
	}
#endif
nimdays
Posts: 236
Joined: May 29, 2014 22:01
Location: West Java, Indonesia

Re: High quality MPG4 Video & Audio encoding.

Post by nimdays »

Do you know where i can find this revel ,they moved ?
https://sourceforge.net/projects/revel
D.J.Peters
Posts: 8586
Joined: May 28, 2005 3:28
Contact:

Re: High quality MPG4 Video & Audio encoding.

Post by D.J.Peters »

I build XVidCapture encoder based on xvidcore-1.3.2 and revel-1.1.0 libs and source codes.

another example: https://www.youtube.com/watch?v=aJvW320mdZg

Joshy

sourcecode:
revel-1.1.0-src.zip

libs:
xvidcore-1.3.2.zip
revel-1.1.0-win32.zip
Last edited by D.J.Peters on Oct 12, 2022 18:40, edited 2 times in total.
nimdays
Posts: 236
Joined: May 29, 2014 22:01
Location: West Java, Indonesia

Re: High quality MPG4 Video & Audio encoding.

Post by nimdays »

Great, thanks a lot :)
I can see xvid from tinycore but no revel.
dafhi
Posts: 1640
Joined: Jun 04, 2005 9:51

Re: High quality MPG4 Video & Audio encoding.

Post by dafhi »

if i'm rendering normal, fb window looks good, but avi is discolored

if i swap red / blue (sub tRenderer.show), avi looks good

Code: Select all

#include "xvidcapture.bi"

sub Main
  
  var w = 320
  var h = 240
  
  dim as single    iFPS = 24
  const as integer ibpp = 32
  const as integer iQuality  = 75
  const as integer iChannels =  0
  const as integer iBits     = 8
  const as integer iRate     = 11025'22050

  screenres w,h,ibpp
 
  chdir exepath
  if OpenVideoAudioStream("test03.avi",w,h,iFPS,iQuality,iChannels,iBits,iRate)=0 then
    print "error: OpenVideoAudioStream() !"
    beep:sleep:end 1
  end if
  

  for frame as long = 1 to 20
    line (0,0)-(w,h),rgb(255,127,0),bf
    ? rnd
    screenlock
      select case ibpp
      case 32 : Write32BitPixels(ScreenPtr)
      case 16 : Write16BitPixels(ScreenPtr)
      end select
    screenunlock
    sleep 100
  next
  
  CloseStream()

end sub

Main
Last edited by dafhi on May 09, 2017 14:37, edited 1 time in total.
D.J.Peters
Posts: 8586
Joined: May 28, 2005 3:28
Contact:

Re: High quality MPG4 Video & Audio encoding.

Post by D.J.Peters »

dafhi you are right it's the the old game real RGB vs. BGR :-)
If I capture OpenGL it's RGBA the FBGFX screen is BGRA.
Yes as a workaround swap Red and Blue triple in the RGB/RGBA macros.

In scope of size/compression It's a bad idea using 32-bit pixels in Videos

It's better using for video capturing an FBGFX screen with 16-bits.

Or create an 16-bit buffer on the fly from your RGBA pixels and write it to the stream.

Joshy
MrSwiss
Posts: 3910
Joined: Jun 02, 2013 9:27
Location: Switzerland

Re: High quality MPG4 Video & Audio encoding.

Post by MrSwiss »

For the old BGR to RGB conversion, a fast Function (from my *Color by Percent* lib):

Code: Select all

Function BGR_RGB( ByVal clr As ULong ) As ULong ' for 24/32bit color only!
    ' FB to WIN: red/blue swap / WIN to FB blue/red swap, works both ways!
    Swap CPtr(UByte Ptr, @clr)[0], CPtr(UByte Ptr, @clr)[2]
    Return clr
End Function
For some more details, see comments in the code. This may just *do the trick*.
D.J.Peters
Posts: 8586
Joined: May 28, 2005 3:28
Contact:

Re: High quality MPG4 Video & Audio encoding.

Post by D.J.Peters »

Because of malware on alice-dsl.net domain I moved all content to my new shiny3d.de server.

Joshy
Post Reply