我想在ffplay 视频视频加一些东西比如透明菜单,交互界面等。起初直接把Surface Update,发现每隔大半秒就会闪烁。即使调整double buffer,
Delay 参数也无济于事。通过网上查找,其原因是 Surface 和 Overlay 是两套不同的机制,当Overlay 显示时,就会把相同区域的Surface 覆盖掉,不是真正的“Overlay”(文档里明确的这样说了)。看来Overlay 是绕不过去的了。下面摘录一段原文:
/*file:sdl_yuv.c
direct access yuv color space
author: ludi
quick and dirty, but it is correct:
void drawHLine_(SDL_Overlay *yuv, int sx, int sy, int len)
{
int w = yuv->pitches[0];
memset(yuv->pixels[0]+sy*w + sx, 255, len*1);
memset(yuv->pixels[1]+(sy*w + 2*sx)/4, 255, (len+1)/2);
memset(yuv->pixels[2]+(sy*w + 2*sx)/4, 255, (len+1)/2);
}
todo: add scalability for blitSurface2YUV (really need this??).
history: 2011.11.05 initial startup.
*/
#include<string.h>
#include<SDL.h>
extern void rgb2yuv(int r, int g, int b, int *y, int *u, int *v);
extern void yuv2rgb(int y, int u, int v, int *r, int *g, int *b);
void fillRect(SDL_Overlay *yuv, SDL_Rect *rect, int y0, int u, int v)
{
int y;
int size = rect->w;
int uv_size = (size-1)/2+1;
int uv_off = 0;
for(y = 0; y< rect->h; ++y)
{
memset(yuv->pixels[0] + (rect->y + y) * yuv->pitches[0] + rect->x,
y0, size);
if(y%2 == 0)
{
memset(yuv->pixels[1] + (uv_off + rect->y /2) * yuv->pitches[1] + rect->x/2,
v, uv_size);
memset(yuv->pixels[2] + (uv_off + rect->y /2) * yuv->pitches[2] + rect->x/2,
u, uv_size);
++uv_off;
}
}
}
int blitSurface2YUV(SDL_Surface *src, SDL_Overlay *dst, SDL_Rect *dstrect)
{
Uint8 r, g, b;
int y1,u1,v1;
int y,x;
int height = src->h < dstrect->h ? src->h: dstrect->h;
int width = src->w < dstrect->w ? src->w: dstrect->w;
int uv_off = 0;
Uint32 pixel;
if(dst->format != SDL_YV12_OVERLAY)return 1;
for(y = 0; y < height; ++y)
{
for(x = 0; x < width; ++x)
{
switch(src->format->BitsPerPixel)
{
case 8:
pixel = *((Uint8*)src->pixels + y*src->pitch + x);
break;
case 16:
pixel = *((Uint16*)src->pixels + y*src->pitch/2 + x);
break;
case 32:
pixel = *((Uint32*)src->pixels + y*src->pitch/4 + x);
break;
default:
return -1;
}
SDL_GetRGB(pixel, src->format, &r, &g, &b);
rgb2yuv(r, g, b, &y1, &u1, &v1);
memset(dst->pixels[0] + (dstrect->y + y) * dst->pitches[0] + (dstrect->x + x),
(Uint8)y1, 1);
if((x%2 == 0 ) && (y%2 == 0 ))
{
memset(dst->pixels[1] + (uv_off + dstrect->y /2) * dst->pitches[1] + (dstrect->x/2 + x/2),
(Uint8)v1, 1);
memset(dst->pixels[2] + (uv_off + dstrect->y /2) * dst->pitches[2] + (dstrect->x/2 + x/2),
(Uint8)u1, 1);
}
}
if(y%2 == 0)++uv_off;
}
return 0;
}
void drawHLine(SDL_Overlay *yuv, int sx, int sy, int len)
{
SDL_Rect rect = {sx,sy, len, 1};
fillRect(yuv, &rect, 255,255,255);
}
void drawDot(SDL_Overlay *yuv, int sx, int sy)
{
drawHLine(yuv, sx,sy, 1);
}
int main(int argc, char **argv)
{
int w,h;
SDL_Surface *screen;
SDL_Overlay *yuv;
SDL_Init (SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER);
screen = SDL_SetVideoMode(450,350,0,0);
yuv = SDL_CreateYUVOverlay(w = 400,h = 300,SDL_YV12_OVERLAY, screen);
SDL_Rect rect0 = {0,0, w,h};
fillRect(yuv, &rect0, 50,128,128);//RGB(50,50,50) white color
SDL_Rect rect = {w/2,20,w/5,20};
fillRect(yuv, &rect, 82,90,240);//RGB(255,0,0);
rect.y += 30;
fillRect(yuv, &rect, 144,54,34);//RGB(0,255,0)
rect.y += 30;
fillRect(yuv, &rect, 41,240,110);//RGB(0,0,255);
rect.y +=30;
fillRect(yuv, &rect, 210,16,146);//RGB(255,255,0);
rect.y +=30;
fillRect(yuv, &rect, 107,202,222);//RGB(255,0,255);
rect.y +=30;
fillRect(yuv, &rect, 169,166,16); //RGB(0,255,255);
rect.y +=30;
fillRect(yuv, &rect, 235,128,128);//RGB(255,255,255);
rect.y +=30;
fillRect(yuv, &rect, 16,128,128); //RGB(0,0,0);
{
SDL_Rect rect2 = {0, 0, w, h/4};//a red banner at top
SDL_FillRect(screen, &rect2, 0xffff0000);
rect2.y = h/4; rect2.w /= 2;//move a little down, show the left half.
blitSurface2YUV(screen, yuv, &rect2);
}
#if 1 //FIXME: drawHLine uses a constant color.
drawHLine(yuv, 0,10,w/2);
drawHLine(yuv, 0,20,1);
drawHLine(yuv, w/2,20,1);
drawHLine(yuv, w/2,30,4);
int i;
for(i=0; i<10; i++)
{
drawDot(yuv, i*w/10, 40);
}
#endif
for(;;)
{//event pump, such that we can move the window with proper saving and restoring bg.
SDL_Event event;
if(SDL_PollEvent(&event))
{
if(event.type == SDL_QUIT)break;
}
SDL_Rect tmp = {10,10, w, h};
SDL_DisplayYUVOverlay(yuv, &tmp);
SDL_Flip(screen);
SDL_Delay(100);
}
SDL_FreeSurface(screen);//clean up
SDL_Quit();
return 0;
}
/*****************************************************************************************************/
/*file:rgb2yuv.c
NOTE: Y values are conventionally shifted and scaled to the range [16, 235] rather than using the full range of [0, 255].
This confusing practice derives from the MPEG standards and
explains why 16 is added to Y' and why the Y' coefficients in the basic transform sum to 220 instead of 255.
U and V values, which may be positive or negative,
are summed with 128 to make them always positive.[4]
ref http://en.wikipedia.org/wiki/YUV
*/
#include<stdio.h>
void rgb2yuv(int r, int g, int b, int *y, int *u, int *v)
{
int y0, u0, v0;
y0 = 66*r + 129*g + 25*b;
u0 = -38*r + -74*g + 112*b;
v0 = 112*r + -94*g + -18*b;
y0 = (y0+128)>>8;
u0 = (u0+128)>>8;
v0 = (v0+128)>>8;
*y = y0 + 16;
*u = u0 + 128;
*v = v0 + 128;
}
void yuv2rgb(int y, int u, int v, int *r, int *g, int *b)
{
int r0,g0,b0;
v = v - 128;
u = u - 128;
r0 = y + v + (v>>2) + (v>>3) + (v>>5);
g0 = y - ((u>>2) + (u>>4) + (u>>5)) - ((v>>1) + (v>>3) + (v>>4) + (v>>5));
b0 = y + u + (u>>1) + (u>>2) + (u>>6);
*r = r0 > 255 ? 255: r0;
*g = g0 > 255 ? 255: g0;
*b = b0 > 255 ? 255: b0;
}
#if defined(GEN_EXE)
int main(int argc, char **argv)
{
if(argc < 4)return printf("usage: %s R G B \nor %s r Y U V\n",argv[0],argv[0]);
if(argv[1][0] == 'r')
{
int r,g,b;
yuv2rgb(atoi(argv[2]), atoi(argv[3]), atoi(argv[4]), &r,&g,&b);
printf("%d,%d,%d\n",r,g,b);
}
else
{
int y, u, v;
rgb2yuv(atoi(argv[1]), atoi(argv[2]), atoi(argv[3]), &y, &u, &v);
printf("%d,%d,%d\n", y,u,v);
}
return 0;
}
#endif