- UID
- 1
- 精华
- 积分
- 76361
- 威望
- 点
- 宅币
- 个
- 贡献
- 次
- 宅之契约
- 份
- 最后登录
- 1970-1-1
- 在线时间
- 小时
|
是不是看着就很清晰!确实是很清晰,因为我不仅用了那个半格的字符“▄”来表现两个像素,我还用了“░”和“▒”来表现更丰富的颜色,简单地使用了抖动算法。其实这个抖动算法做得不是特别好,还有改进的空间。
原理很简单,使用VideoIP将视频的每一帧都实时提取出来,然后将其缩小到适合控制台窗口的尺寸,再根据每个像素的颜色选择字符,或者抖动,最后把字符攒起来,在需要调用SetConsoleTextAttribute的时候才将字符用fputs显示出来(注意不是puts)。
下载地址:
BIN:
ConPlay.7z
(328.85 KB, 下载次数: 29)
SRC:http://pan.baidu.com/s/1hqtI4fU(访问密码:t9j6)- //=============================================================================
- //作者:0xAA55
- //论坛:http://www.0xaa55.com/
- //版权所有(C) 2013-2014 技术宅的结界
- //请保留原作者信息,否则视为侵权
- //-----------------------------------------------------------------------------
- #include<VIPc.h>
- #include<math.h>
- #include<stdio.h>
- #include<stdlib.h>
- #include<malloc.h>
- #include<signal.h>
- BOOL g_bCompleted=FALSE;//播放完后这个值为真,退出程序
- HANDLE g_hStdout=NULL;//控制台标准输出句柄
- typedef struct
- {
- DWORD B;
- DWORD G;
- DWORD R;
- DWORD Sum;
- }RGBStretch,*RGBStretchP;//用于收缩图像时计算重合像素的平均颜色
- UINT g_uScrWidth=80;//虚拟的屏幕尺寸
- UINT g_uScrHeight=25;
- size_t g_NbScrBufUnits=0;//虚拟屏幕缓冲区单元数
- size_t g_cbScrBuf=0;//虚拟屏幕缓冲区大小
- RGBStretchP g_pScrBuf=NULL;//虚拟屏幕缓冲区
- //我们把要输出的相同颜色的字符攒起来,等颜色变了再一次性输出,以减少写入stdout的次数而增加流畅度
- size_t g_cbStrBuf=0;
- char *g_pStrBuf=NULL;//字符缓冲区
- BOOL g_bHftnPos=FALSE;//抖动的方式,制造闪烁的效果
- //控制台调色板
- RGBStretch g_Palette[16]=
- {
- {0x00,0x00,0x00,0},
- {0x80,0x00,0x00,0},
- {0x00,0x80,0x00,0},
- {0x80,0x80,0x00,0},
- {0x00,0x00,0x80,0},
- {0x80,0x00,0x80,0},
- {0x00,0x80,0x80,0},
- {0xC0,0xC0,0xC0,0},
- {0x80,0x80,0x80,0},
- {0xFF,0x00,0x00,0},
- {0x00,0xFF,0x00,0},
- {0xFF,0xFF,0x00,0},
- {0x00,0x00,0xFF,0},
- {0xFF,0x00,0xFF,0},
- {0x00,0xFF,0xFF,0},
- {0xFF,0xFF,0xFF,0}
- };
- #define PixelChar 0xDC/*PixelChar这个字符“▄”可以表示两个像素*/
- #define HftnChar1 0xB0/*淡的渐变块*/
- #define HftnChar2 0xB1/*渐变块*/
- #define HftnChar3 0xB2/*深的渐变块*/
- //=============================================================================
- //函数:Usage
- //描述:打印程序的用法
- //-----------------------------------------------------------------------------
- void Usage(wchar_t*argv0)
- {
- fwprintf(stderr,L"Usage:\n"
- L"%s videofile [Width] [Height]\n"
- L"Width and Height were defaulted to 80x25.\n"
- ,
- argv0?argv0:L"ConPlay");
- }
- //=============================================================================
- //函数:SigProc
- //描述:信号处理程序,用于处理用户产生的信号,Ctrl+C等
- //-----------------------------------------------------------------------------
- void SigProc(int Signal)
- {
- switch(Signal)
- {
- case SIGINT:
- g_bCompleted=TRUE;
- break;
- }
- }
- //=============================================================================
- //函数:InitBuffer
- //描述:初始化缓冲区以用于绘图
- //-----------------------------------------------------------------------------
- BOOL InitBuffer()
- {
- g_NbScrBufUnits=g_uScrWidth*g_uScrHeight*2;
- g_pScrBuf=(RGBStretchP)malloc(g_cbScrBuf=g_NbScrBufUnits*sizeof(RGBStretch));//分配屏幕缓冲区内存
- if(!g_pScrBuf)
- return FALSE;
- g_cbStrBuf=g_uScrWidth*g_uScrHeight+1;
- g_pStrBuf=(char*)malloc(g_cbStrBuf);
- if(!g_pStrBuf)
- return FALSE;
- return TRUE;
- }
- //=============================================================================
- //函数:CleanupBuffer
- //描述:清理绘图缓冲区
- //-----------------------------------------------------------------------------
- void CleanupBuffer()
- {
- free(g_pScrBuf);
- g_pScrBuf=NULL;
- g_cbScrBuf=0;
- free(g_pStrBuf);
- }
- //=============================================================================
- //函数:GetColorDistSq
- //描述:取得两颜色之间的距离平方
- //-----------------------------------------------------------------------------
- UINT GetColorDistSq(int X,int Y,int Z)
- {
- return X*X+Y*Y+Z*Z;
- }
- //=============================================================================
- //函数:GetTriArea
- //描述:取得三角形面积
- //-----------------------------------------------------------------------------
- double GetTriArea(double a,double b,double c)
- {
- double p=(a+b+c)/2;
- return sqrt(p*(p-a)*(p-b)*(p-c));
- }
- //=============================================================================
- //函数:GetNearestClr
- //描述:取得最相近的、值不等于wExcept的颜色
- //-----------------------------------------------------------------------------
- WORD GetNearestClr
- (
- RGBStretchP pClr,//颜色
- WORD wExcept,//除外
- UINT*uDistOut//输出距离,可为NULL
- )
- {
- WORD i;
- WORD wNearest=0;//最近颜色索引
- UINT DistSq=0x7FFFFFFF;//颜色距离平方
- UINT NewDistSq;//新的颜色距离平方
- for(i=0;i<16;i++)
- {
- NewDistSq=GetColorDistSq
- (//计算距离的平方
- (int)(pClr->R)-(int)(g_Palette[i].R),//相对坐标X
- (int)(pClr->G)-(int)(g_Palette[i].G),
- (int)(pClr->B)-(int)(g_Palette[i].B)
- );
- if(NewDistSq<DistSq && i!=wExcept)//如果更接近
- {
- DistSq=NewDistSq;
- wNearest=i;
- }
- }
- if(uDistOut)
- *uDistOut=DistSq;
- return wNearest;
- }
- //=============================================================================
- //函数:OnRender
- //描述:VIP回调函数,用于处理图像数据
- //-----------------------------------------------------------------------------
- void VIPCallback OnRender
- (
- size_t Width,//视频宽度
- size_t Height,//视频高度
- size_t Pitch,//视频每行字节数
- BYTE*pBits,//图像数据
- void*pUserData
- )
- {
- size_t x,y;//像素位置
- size_t iSrc,iDest;//像素索引
- size_t lSrc=0,lDest;//像素行开始索引
- COORD dwNewPos={0};
- size_t lSrc1=g_uScrWidth,lSrc2=0;
- size_t iSrc1,iSrc2;
- WORD wCharAttr;
- WORD wOldCharAttr=0xFFFF;//0xFFFF表示这是这一行第一个字符
- size_t LineBufEnd=0;
- char charToAdd=' ';//要添加的字符
- //先缩小图像
- memset(g_pScrBuf,0,g_cbScrBuf);
- for(y=0;y<Height;y++)
- {
- iSrc=lSrc;
- lDest=((Height-y-1)*g_uScrHeight*2/Height)*g_uScrWidth;
- for(x=0;x<Width;x++)
- {
- iDest=lDest+x*g_uScrWidth/Width;
- g_pScrBuf[iDest].B+=pBits[iSrc++];//将合并的像素的颜色混合起来
- g_pScrBuf[iDest].G+=pBits[iSrc++];
- g_pScrBuf[iDest].R+=pBits[iSrc++];
- g_pScrBuf[iDest].Sum++;
- }
- lSrc+=Pitch;
- }
- //然后处理好合并的像素
- for(iDest=0;iDest<g_NbScrBufUnits;iDest++)
- {
- if(g_pScrBuf[iDest].Sum)
- {
- g_pScrBuf[iDest].R/=g_pScrBuf[iDest].Sum;
- g_pScrBuf[iDest].G/=g_pScrBuf[iDest].Sum;
- g_pScrBuf[iDest].B/=g_pScrBuf[iDest].Sum;
- }
- }
- //行缓冲区,用来攒字符
- memset(g_pStrBuf,0,g_cbStrBuf);
- //绘制每一帧
- SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),dwNewPos);
- for(y=0;y<g_uScrHeight;y++)
- {
- iSrc1=lSrc1;//像素行指针
- iSrc2=lSrc2;//第二行的指针
- for(x=0;x<g_uScrWidth-1;x++)
- {
- WORD wCol1,wCol2;
-
- wCol1=GetNearestClr(&g_pScrBuf[iSrc1],0xFFFF,NULL);//取得上半截颜色
- wCol2=GetNearestClr(&g_pScrBuf[iSrc2],0xFFFF,NULL);//取得下半截颜色
- //攒字符
- if(wCol1==wCol2)//如果这个块的颜色上半部分和下半部分相同
- {
- UINT uDist1,uDist2,uDist3;
- double Area;
- double TriHeight;
- double Split1,Split2;
- RGBStretch Avr=//上下两个点的平均色
- {
- (g_pScrBuf[iSrc1].B+g_pScrBuf[iSrc2].B)/2,
- (g_pScrBuf[iSrc1].G+g_pScrBuf[iSrc2].G)/2,
- (g_pScrBuf[iSrc1].R+g_pScrBuf[iSrc2].R)/2,
- 0
- };
- wCol1=GetNearestClr(&Avr,16,&uDist1);//取得最相近颜色
- wCol2=GetNearestClr(&Avr,wCol1,&uDist2);//取得第二接近颜色
- // Avr*_
- // /| ~"-,_
- // uDist1/ | ~"-,_uDist2
- // / | ~"-,_
- //wCol1*---+-----------------*wCol2
- // uDist3
- uDist3=GetColorDistSq
- (
- (int)(g_Palette[wCol1].R)-(int)(g_Palette[wCol2].R),
- (int)(g_Palette[wCol1].G)-(int)(g_Palette[wCol2].G),
- (int)(g_Palette[wCol1].B)-(int)(g_Palette[wCol2].B)
- );
- Area=GetTriArea((double)uDist1,(double)uDist2,(double)uDist3);
- TriHeight=Area*2/(double)uDist3;//三角形的高
- Split1=sqrt((double)uDist1*(double)uDist1-TriHeight*TriHeight);
- Split2=sqrt((double)uDist2*(double)uDist2-TriHeight*TriHeight);
- switch((UINT)(Split1*3/Split2))
- {
- default:
- wCharAttr=wCol1|(wCol1<<4);
- charToAdd=PixelChar;
- break;
- case 1:
- if(g_bHftnPos)
- {
- wCharAttr=wCol2|(wCol1<<4);
- charToAdd=HftnChar1;
- }
- else
- {
- wCharAttr=wCol1|(wCol2<<4);
- charToAdd=HftnChar3;
- }
- break;
- case 2:
- if(g_bHftnPos)
- wCharAttr=wCol1|(wCol2<<4);
- else
- wCharAttr=wCol2|(wCol1<<4);
- charToAdd=HftnChar2;
- break;
- }
- }
- else
- {
- wCharAttr=wCol1|(wCol2<<4);
- charToAdd=PixelChar;
- }
- if(wCharAttr!=wOldCharAttr)//如果颜色发生了改变
- {
- if(wOldCharAttr==0xFFFF)
- SetConsoleTextAttribute(g_hStdout,wCharAttr);//如果没设置过颜色则设置为新颜色
- fputs(g_pStrBuf,stdout);//写入之前攒好的字符串
- memset(g_pStrBuf,0,g_cbStrBuf);//清空字符串缓冲区
- LineBufEnd=0;//重新攒字符串
- SetConsoleTextAttribute(g_hStdout,wCharAttr);//设置为新的颜色
- wOldCharAttr=wCharAttr;//记录新的颜色
- }
- g_pStrBuf[LineBufEnd++]=charToAdd;
- iSrc1++;
- iSrc2++;
- }
- if(y<g_uScrHeight-1)
- g_pStrBuf[LineBufEnd++]='\n';
- lSrc1+=g_uScrWidth+g_uScrWidth;//指针一次跨两行
- lSrc2+=g_uScrWidth+g_uScrWidth;
- }
- fputs(g_pStrBuf,stdout);
- g_bHftnPos=!g_bHftnPos;
- }
- //=============================================================================
- //函数:wmain
- //描述:程序入口点,UNICODE版本
- //-----------------------------------------------------------------------------
- int wmain(int argc,wchar_t**argv)
- {
- VIP VIPlayer;
- HRESULT hr;
- //参数不足打印用法
- if(argc<2)
- {
- Usage(argc?argv[0]:NULL);
- return 1;
- }
- //取得用户输入的可选的屏幕尺寸
- if(argc>=3)
- {
- g_uScrWidth=(UINT)_wtoi(argv[2]);
- if(argc>=4)
- g_uScrHeight=(UINT)_wtoi(argv[3]);
- }
- //安装信号处理程序
- signal(SIGINT,SigProc);
- g_hStdout=GetStdHandle(STD_OUTPUT_HANDLE);
- //必须先初始化COM才能初始化VIP
- if(FAILED(hr=CoInitialize(NULL)))
- goto Cleanup;
- if(FAILED(hr=InitVIP(&VIPlayer,argv[1],NULL,(FnOnRender)OnRender,TRUE,NULL)))
- goto Cleanup;
- //修改代码页,使其能显示DOS字符
- system("chcp 437");
- if(!InitBuffer())
- {
- hr=E_OUTOFMEMORY;
- goto Cleanup;
- }
- //开始播放
- if(FAILED(hr=VIPPlay(&VIPlayer)))
- goto Cleanup;
- //轮询检查是否播放完成
- while(!g_bCompleted)
- {
- VIPCheckCompleted(&VIPlayer,&g_bCompleted);
- }
- VIPStop(&VIPlayer);
- CleanupVIP(&VIPlayer);
- CleanupBuffer();
- system("color 07");
- return 0;
- Cleanup:
- fprintf(stderr,"Error:HRESULT=0x%08X\n",hr);
- VIPStop(&VIPlayer);
- CleanupVIP(&VIPlayer);
- CleanupBuffer();
- system("color 07");
- return 2;
- }
复制代码 |
|