# HG changeset patch # User koda # Date 1458457731 14400 # Node ID b7d5d75469ee92e6f1c5f68986784cbb6751e3d3 # Parent b53c3134d55ad580620e60187a20fd4c26898b08 Move pixel format conversion from uVideoRec to AVWrapper This has several benefits, being in C-land allows us to better use libav API and avoid mixing memory allocated from Pascal. Also the C code for the conversion loop generated by GCC or Clang is probably more optimized than by Freepascal. Finally it will simplify code in the future if we are going to enable any other pixel format than yuv420p. Change the coefficients to improve color accuracy during conversion. diff -r b53c3134d55a -r b7d5d75469ee hedgewars/avwrapper/avwrapper.c --- a/hedgewars/avwrapper/avwrapper.c Sun Mar 20 01:16:11 2016 -0400 +++ b/hedgewars/avwrapper/avwrapper.c Sun Mar 20 03:08:51 2016 -0400 @@ -333,15 +333,13 @@ g_pVFrame = av_frame_alloc(); if (!g_pVFrame) return FatalError("Could not allocate frame"); + av_frame_unref(g_pVFrame); g_pVFrame->width = g_Width; g_pVFrame->height = g_Height; g_pVFrame->format = AV_PIX_FMT_YUV420P; - g_pVFrame->linesize[0] = g_Width; - g_pVFrame->linesize[1] = g_Width/2; - g_pVFrame->linesize[2] = g_Width/2; - g_pVFrame->linesize[3] = 0; - return 0; + + return avcodec_default_get_buffer2(g_pVideo, g_pVFrame, 0); } static int WriteFrame(AVFrame* pFrame) @@ -418,11 +416,47 @@ } } -AVWRAP_DECL int AVWrapper_WriteFrame(uint8_t* pY, uint8_t* pCb, uint8_t* pCr) +AVWRAP_DECL int AVWrapper_WriteFrame(uint8_t *buf) { - g_pVFrame->data[0] = pY; - g_pVFrame->data[1] = pCb; - g_pVFrame->data[2] = pCr; + int x, y, stride = g_Width * 4; + uint8_t *data[3]; + + // copy pointers, prepare source + memcpy(data, g_pVFrame->data, sizeof(data)); + buf += (g_Height - 1) * stride; + + // convert to YUV 4:2:0 + for (y = 0; y < g_Height; y++) { + for (x = 0; x < g_Width; x++) { + int r = buf[x * 4 + 0]; + int g = buf[x * 4 + 1]; + int b = buf[x * 4 + 2]; + + int luma = (int)(0.299f * r + 0.587f * g + 0.114f * b); + data[0][x] = av_clip_uint8(luma); + + if (!(x & 1) && !(y & 1)) { + int r = (buf[x * 4 + 0] + buf[(x + 1) * 4 + 0] + + buf[x * 4 + 0 + stride] + buf[(x + 1) * 4 + 0 + stride]) / 4; + int g = (buf[x * 4 + 1] + buf[(x + 1) * 4 + 1] + + buf[x * 4 + 1 + stride] + buf[(x + 1) * 4 + 1 + stride]) / 4; + int b = (buf[x * 4 + 2] + buf[(x + 1) * 4 + 2] + + buf[x * 4 + 2 + stride] + buf[(x + 1) * 4 + 2 + stride]) / 4; + + int cr = (int)(-0.14713f * r - 0.28886f * g + 0.436f * b); + int cb = (int)( 0.615f * r - 0.51499f * g - 0.10001f * b); + data[1][x / 2] = av_clip_uint8(128 + cr); + data[2][x / 2] = av_clip_uint8(128 + cb); + } + } + buf += -stride; + data[0] += g_pVFrame->linesize[0]; + if (y & 1) { + data[1] += g_pVFrame->linesize[1]; + data[2] += g_pVFrame->linesize[2]; + } + } + return WriteFrame(g_pVFrame); } diff -r b53c3134d55a -r b7d5d75469ee hedgewars/uVideoRec.pas --- a/hedgewars/uVideoRec.pas Sun Mar 20 01:16:11 2016 -0400 +++ b/hedgewars/uVideoRec.pas Sun Mar 20 03:08:51 2016 -0400 @@ -58,7 +58,7 @@ filename, desc, soundFile, format, vcodec, acodec: PChar; width, height, framerateNum, framerateDen, vquality: LongInt): LongInt; cdecl; external AvwrapperLibName; function AVWrapper_Close: LongInt; cdecl; external AvwrapperLibName; -function AVWrapper_WriteFrame( pY, pCb, pCr: PByte ): LongInt; cdecl; external AvwrapperLibName; +function AVWrapper_WriteFrame(rgb: PByte): LongInt; cdecl; external AvwrapperLibName; type TFrame = record realTicks: LongWord; @@ -121,15 +121,6 @@ true) then exit(false); numPixels:= cScreenWidth*cScreenHeight; - YCbCr_Planes[0]:= GetMem(numPixels); - YCbCr_Planes[1]:= GetMem(numPixels div 4); - YCbCr_Planes[2]:= GetMem(numPixels div 4); - - if (YCbCr_Planes[0] = nil) or (YCbCr_Planes[1] = nil) or (YCbCr_Planes[2] = nil) then - begin - AddFileLog('Error: Could not allocate memory for video recording (YCbCr buffer).'); - exit(false); - end; RGB_Buffer:= GetMem(4*numPixels); if RGB_Buffer = nil then @@ -147,9 +138,6 @@ procedure StopVideoRecording; begin AddFileLog('StopVideoRecording'); - FreeMem(YCbCr_Planes[0], numPixels); - FreeMem(YCbCr_Planes[1], numPixels div 4); - FreeMem(YCbCr_Planes[2], numPixels div 4); FreeMem(RGB_Buffer, 4*numPixels); Close(cameraFile); if AVWrapper_Close() < 0 then @@ -159,36 +147,13 @@ SendIPC(_S'v'); // inform frontend that we finished end; -function pixel(x, y, color: LongInt): LongInt; -begin - pixel:= RGB_Buffer[(cScreenHeight-y-1)*cScreenWidth*4 + x*4 + color]; -end; - procedure EncodeFrame; -var x, y, r, g, b: LongInt; - s: shortstring; +var s: shortstring; begin // read pixels from OpenGL glReadPixels(0, 0, cScreenWidth, cScreenHeight, GL_RGBA, GL_UNSIGNED_BYTE, RGB_Buffer); - // convert to YCbCr 4:2:0 format - // Y - for y := 0 to cScreenHeight-1 do - for x := 0 to cScreenWidth-1 do - YCbCr_Planes[0][y*cScreenWidth + x]:= Byte(16 + ((16828*pixel(x,y,0) + 33038*pixel(x,y,1) + 6416*pixel(x,y,2)) shr 16)); - - // Cb and Cr - for y := 0 to cScreenHeight div 2 - 1 do - for x := 0 to cScreenWidth div 2 - 1 do - begin - r:= pixel(2*x,2*y,0) + pixel(2*x+1,2*y,0) + pixel(2*x,2*y+1,0) + pixel(2*x+1,2*y+1,0); - g:= pixel(2*x,2*y,1) + pixel(2*x+1,2*y,1) + pixel(2*x,2*y+1,1) + pixel(2*x+1,2*y+1,1); - b:= pixel(2*x,2*y,2) + pixel(2*x+1,2*y,2) + pixel(2*x,2*y+1,2) + pixel(2*x+1,2*y+1,2); - YCbCr_Planes[1][y*(cScreenWidth div 2) + x]:= Byte(128 + ((-2428*r - 4768*g + 7196*b) shr 16)); - YCbCr_Planes[2][y*(cScreenWidth div 2) + x]:= Byte(128 + (( 7196*r - 6026*g - 1170*b) shr 16)); - end; - - if AVWrapper_WriteFrame(YCbCr_Planes[0], YCbCr_Planes[1], YCbCr_Planes[2]) < 0 then + if AVWrapper_WriteFrame(RGB_Buffer) < 0 then halt(-1); // inform frontend that we have encoded new frame