找回密码
 立即注册→加入我们

QQ登录

只需一步,快速开始

搜索
热搜: 下载 VB C 实现 编写
查看: 5224|回复: 4

【C】技术宅教你控制台播放视频

[复制链接]

1110

主题

1651

回帖

7万

积分

用户组: 管理员

一只技术宅

UID
1
精华
244
威望
743 点
宅币
24221 个
贡献
46222 次
宅之契约
0 份
在线时间
2296 小时
注册时间
2014-1-26
发表于 2014-11-12 23:58:40 | 显示全部楼层 |阅读模式

欢迎访问技术宅的结界,请注册或者登录吧。

您需要 登录 才可以下载或查看,没有账号?立即注册→加入我们

×
20141111232757.png 20141112192251.png 20141112225432.png 20141112230740.png
是不是看着就很清晰!确实是很清晰,因为我不仅用了那个半格的字符“▄”来表现两个像素,我还用了“░”和“▒”来表现更丰富的颜色,简单地使用了抖动算法。其实这个抖动算法做得不是特别好,还有改进的空间。
原理很简单,使用VideoIP将视频的每一帧都实时提取出来,然后将其缩小到适合控制台窗口的尺寸,再根据每个像素的颜色选择字符,或者抖动,最后把字符攒起来,在需要调用SetConsoleTextAttribute的时候才将字符用fputs显示出来(注意不是puts)。
下载地址:
BIN: ConPlay.7z (328.85 KB, 下载次数: 29)
SRC:http://pan.baidu.com/s/1hqtI4fU(访问密码:t9j6)
  1. //=============================================================================
  2. //作者:0xAA55
  3. //论坛:http://www.0xaa55.com/
  4. //版权所有(C) 2013-2014 技术宅的结界
  5. //请保留原作者信息,否则视为侵权
  6. //-----------------------------------------------------------------------------

  7. #include<VIPc.h>
  8. #include<math.h>
  9. #include<stdio.h>
  10. #include<stdlib.h>
  11. #include<malloc.h>
  12. #include<signal.h>

  13. BOOL        g_bCompleted=FALSE;//播放完后这个值为真,退出程序
  14. HANDLE        g_hStdout=NULL;//控制台标准输出句柄

  15. typedef struct
  16. {
  17.         DWORD        B;
  18.         DWORD        G;
  19.         DWORD        R;
  20.         DWORD        Sum;
  21. }RGBStretch,*RGBStretchP;//用于收缩图像时计算重合像素的平均颜色

  22. UINT                g_uScrWidth=80;//虚拟的屏幕尺寸
  23. UINT                g_uScrHeight=25;
  24. size_t                g_NbScrBufUnits=0;//虚拟屏幕缓冲区单元数
  25. size_t                g_cbScrBuf=0;//虚拟屏幕缓冲区大小
  26. RGBStretchP        g_pScrBuf=NULL;//虚拟屏幕缓冲区
  27. //我们把要输出的相同颜色的字符攒起来,等颜色变了再一次性输出,以减少写入stdout的次数而增加流畅度
  28. size_t                g_cbStrBuf=0;
  29. char                *g_pStrBuf=NULL;//字符缓冲区

  30. BOOL                g_bHftnPos=FALSE;//抖动的方式,制造闪烁的效果

  31. //控制台调色板
  32. RGBStretch        g_Palette[16]=
  33. {
  34.         {0x00,0x00,0x00,0},
  35.         {0x80,0x00,0x00,0},
  36.         {0x00,0x80,0x00,0},
  37.         {0x80,0x80,0x00,0},
  38.         {0x00,0x00,0x80,0},
  39.         {0x80,0x00,0x80,0},
  40.         {0x00,0x80,0x80,0},
  41.         {0xC0,0xC0,0xC0,0},
  42.         {0x80,0x80,0x80,0},
  43.         {0xFF,0x00,0x00,0},
  44.         {0x00,0xFF,0x00,0},
  45.         {0xFF,0xFF,0x00,0},
  46.         {0x00,0x00,0xFF,0},
  47.         {0xFF,0x00,0xFF,0},
  48.         {0x00,0xFF,0xFF,0},
  49.         {0xFF,0xFF,0xFF,0}
  50. };

  51. #define        PixelChar                0xDC/*PixelChar这个字符“▄”可以表示两个像素*/
  52. #define        HftnChar1                0xB0/*淡的渐变块*/
  53. #define        HftnChar2                0xB1/*渐变块*/
  54. #define        HftnChar3                0xB2/*深的渐变块*/

  55. //=============================================================================
  56. //函数:Usage
  57. //描述:打印程序的用法
  58. //-----------------------------------------------------------------------------
  59. void Usage(wchar_t*argv0)
  60. {
  61.         fwprintf(stderr,L"Usage:\n"
  62.                 L"%s videofile [Width] [Height]\n"
  63.                 L"Width and Height were defaulted to 80x25.\n"
  64.                 ,
  65.                 argv0?argv0:L"ConPlay");
  66. }

  67. //=============================================================================
  68. //函数:SigProc
  69. //描述:信号处理程序,用于处理用户产生的信号,Ctrl+C等
  70. //-----------------------------------------------------------------------------
  71. void SigProc(int Signal)
  72. {
  73.         switch(Signal)
  74.         {
  75.         case SIGINT:
  76.                 g_bCompleted=TRUE;
  77.                 break;
  78.         }
  79. }

  80. //=============================================================================
  81. //函数:InitBuffer
  82. //描述:初始化缓冲区以用于绘图
  83. //-----------------------------------------------------------------------------
  84. BOOL InitBuffer()
  85. {
  86.         g_NbScrBufUnits=g_uScrWidth*g_uScrHeight*2;
  87.         g_pScrBuf=(RGBStretchP)malloc(g_cbScrBuf=g_NbScrBufUnits*sizeof(RGBStretch));//分配屏幕缓冲区内存
  88.         if(!g_pScrBuf)
  89.                 return FALSE;

  90.         g_cbStrBuf=g_uScrWidth*g_uScrHeight+1;
  91.         g_pStrBuf=(char*)malloc(g_cbStrBuf);
  92.         if(!g_pStrBuf)
  93.                 return FALSE;

  94.         return TRUE;
  95. }

  96. //=============================================================================
  97. //函数:CleanupBuffer
  98. //描述:清理绘图缓冲区
  99. //-----------------------------------------------------------------------------
  100. void CleanupBuffer()
  101. {
  102.         free(g_pScrBuf);
  103.         g_pScrBuf=NULL;
  104.         g_cbScrBuf=0;
  105.         free(g_pStrBuf);
  106. }

  107. //=============================================================================
  108. //函数:GetColorDistSq
  109. //描述:取得两颜色之间的距离平方
  110. //-----------------------------------------------------------------------------
  111. UINT GetColorDistSq(int X,int Y,int Z)
  112. {
  113.         return X*X+Y*Y+Z*Z;
  114. }

  115. //=============================================================================
  116. //函数:GetTriArea
  117. //描述:取得三角形面积
  118. //-----------------------------------------------------------------------------
  119. double GetTriArea(double a,double b,double c)
  120. {
  121.         double p=(a+b+c)/2;
  122.         return sqrt(p*(p-a)*(p-b)*(p-c));
  123. }

  124. //=============================================================================
  125. //函数:GetNearestClr
  126. //描述:取得最相近的、值不等于wExcept的颜色
  127. //-----------------------------------------------------------------------------
  128. WORD GetNearestClr
  129. (
  130.         RGBStretchP pClr,//颜色
  131.         WORD wExcept,//除外
  132.         UINT*uDistOut//输出距离,可为NULL
  133. )
  134. {
  135.         WORD        i;
  136.         WORD        wNearest=0;//最近颜色索引
  137.         UINT        DistSq=0x7FFFFFFF;//颜色距离平方
  138.         UINT        NewDistSq;//新的颜色距离平方
  139.         for(i=0;i<16;i++)
  140.         {
  141.                 NewDistSq=GetColorDistSq
  142.                 (//计算距离的平方
  143.                         (int)(pClr->R)-(int)(g_Palette[i].R),//相对坐标X
  144.                         (int)(pClr->G)-(int)(g_Palette[i].G),
  145.                         (int)(pClr->B)-(int)(g_Palette[i].B)
  146.                 );
  147.                 if(NewDistSq<DistSq && i!=wExcept)//如果更接近
  148.                 {
  149.                         DistSq=NewDistSq;
  150.                         wNearest=i;
  151.                 }
  152.         }
  153.         if(uDistOut)
  154.                 *uDistOut=DistSq;
  155.         return wNearest;
  156. }

  157. //=============================================================================
  158. //函数:OnRender
  159. //描述:VIP回调函数,用于处理图像数据
  160. //-----------------------------------------------------------------------------
  161. void VIPCallback OnRender
  162. (
  163.         size_t Width,//视频宽度
  164.         size_t Height,//视频高度
  165.         size_t Pitch,//视频每行字节数
  166.         BYTE*pBits,//图像数据
  167.         void*pUserData
  168. )
  169. {
  170.         size_t x,y;//像素位置
  171.         size_t iSrc,iDest;//像素索引
  172.         size_t lSrc=0,lDest;//像素行开始索引
  173.         COORD dwNewPos={0};
  174.         size_t lSrc1=g_uScrWidth,lSrc2=0;
  175.         size_t iSrc1,iSrc2;
  176.         WORD wCharAttr;
  177.         WORD wOldCharAttr=0xFFFF;//0xFFFF表示这是这一行第一个字符
  178.         size_t LineBufEnd=0;
  179.         char charToAdd=' ';//要添加的字符

  180.         //先缩小图像
  181.         memset(g_pScrBuf,0,g_cbScrBuf);
  182.         for(y=0;y<Height;y++)
  183.         {
  184.                 iSrc=lSrc;
  185.                 lDest=((Height-y-1)*g_uScrHeight*2/Height)*g_uScrWidth;
  186.                 for(x=0;x<Width;x++)
  187.                 {
  188.                         iDest=lDest+x*g_uScrWidth/Width;
  189.                         g_pScrBuf[iDest].B+=pBits[iSrc++];//将合并的像素的颜色混合起来
  190.                         g_pScrBuf[iDest].G+=pBits[iSrc++];
  191.                         g_pScrBuf[iDest].R+=pBits[iSrc++];
  192.                         g_pScrBuf[iDest].Sum++;
  193.                 }
  194.                 lSrc+=Pitch;
  195.         }

  196.         //然后处理好合并的像素
  197.         for(iDest=0;iDest<g_NbScrBufUnits;iDest++)
  198.         {
  199.                 if(g_pScrBuf[iDest].Sum)
  200.                 {
  201.                         g_pScrBuf[iDest].R/=g_pScrBuf[iDest].Sum;
  202.                         g_pScrBuf[iDest].G/=g_pScrBuf[iDest].Sum;
  203.                         g_pScrBuf[iDest].B/=g_pScrBuf[iDest].Sum;
  204.                 }
  205.         }

  206.         //行缓冲区,用来攒字符
  207.         memset(g_pStrBuf,0,g_cbStrBuf);

  208.         //绘制每一帧
  209.         SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),dwNewPos);
  210.         for(y=0;y<g_uScrHeight;y++)
  211.         {
  212.                 iSrc1=lSrc1;//像素行指针
  213.                 iSrc2=lSrc2;//第二行的指针
  214.                 for(x=0;x<g_uScrWidth-1;x++)
  215.                 {
  216.                         WORD wCol1,wCol2;
  217.                        
  218.                         wCol1=GetNearestClr(&g_pScrBuf[iSrc1],0xFFFF,NULL);//取得上半截颜色
  219.                         wCol2=GetNearestClr(&g_pScrBuf[iSrc2],0xFFFF,NULL);//取得下半截颜色

  220.                         //攒字符
  221.                         if(wCol1==wCol2)//如果这个块的颜色上半部分和下半部分相同
  222.                         {
  223.                                 UINT uDist1,uDist2,uDist3;
  224.                                 double Area;
  225.                                 double TriHeight;
  226.                                 double Split1,Split2;
  227.                                 RGBStretch Avr=//上下两个点的平均色
  228.                                 {
  229.                                         (g_pScrBuf[iSrc1].B+g_pScrBuf[iSrc2].B)/2,
  230.                                         (g_pScrBuf[iSrc1].G+g_pScrBuf[iSrc2].G)/2,
  231.                                         (g_pScrBuf[iSrc1].R+g_pScrBuf[iSrc2].R)/2,
  232.                                         0
  233.                                 };
  234.                                 wCol1=GetNearestClr(&Avr,16,&uDist1);//取得最相近颜色
  235.                                 wCol2=GetNearestClr(&Avr,wCol1,&uDist2);//取得第二接近颜色
  236.                                 //      Avr*_
  237.                                 //        /| ~"-,_
  238.                                 // uDist1/ |      ~"-,_uDist2
  239.                                 //      /  |           ~"-,_
  240.                                 //wCol1*---+-----------------*wCol2
  241.                                 //            uDist3
  242.                                 uDist3=GetColorDistSq
  243.                                 (
  244.                                         (int)(g_Palette[wCol1].R)-(int)(g_Palette[wCol2].R),
  245.                                         (int)(g_Palette[wCol1].G)-(int)(g_Palette[wCol2].G),
  246.                                         (int)(g_Palette[wCol1].B)-(int)(g_Palette[wCol2].B)
  247.                                 );
  248.                                 Area=GetTriArea((double)uDist1,(double)uDist2,(double)uDist3);
  249.                                 TriHeight=Area*2/(double)uDist3;//三角形的高
  250.                                 Split1=sqrt((double)uDist1*(double)uDist1-TriHeight*TriHeight);
  251.                                 Split2=sqrt((double)uDist2*(double)uDist2-TriHeight*TriHeight);
  252.                                 switch((UINT)(Split1*3/Split2))
  253.                                 {
  254.                                 default:
  255.                                         wCharAttr=wCol1|(wCol1<<4);
  256.                                         charToAdd=PixelChar;
  257.                                         break;
  258.                                 case 1:
  259.                                         if(g_bHftnPos)
  260.                                         {
  261.                                                 wCharAttr=wCol2|(wCol1<<4);
  262.                                                 charToAdd=HftnChar1;
  263.                                         }
  264.                                         else
  265.                                         {
  266.                                                 wCharAttr=wCol1|(wCol2<<4);
  267.                                                 charToAdd=HftnChar3;
  268.                                         }
  269.                                         break;
  270.                                 case 2:
  271.                                         if(g_bHftnPos)
  272.                                                 wCharAttr=wCol1|(wCol2<<4);
  273.                                         else
  274.                                                 wCharAttr=wCol2|(wCol1<<4);
  275.                                         charToAdd=HftnChar2;
  276.                                         break;
  277.                                 }
  278.                         }
  279.                         else
  280.                         {
  281.                                 wCharAttr=wCol1|(wCol2<<4);
  282.                                 charToAdd=PixelChar;
  283.                         }

  284.                         if(wCharAttr!=wOldCharAttr)//如果颜色发生了改变
  285.                         {
  286.                                 if(wOldCharAttr==0xFFFF)
  287.                                         SetConsoleTextAttribute(g_hStdout,wCharAttr);//如果没设置过颜色则设置为新颜色
  288.                                 fputs(g_pStrBuf,stdout);//写入之前攒好的字符串
  289.                                 memset(g_pStrBuf,0,g_cbStrBuf);//清空字符串缓冲区
  290.                                 LineBufEnd=0;//重新攒字符串
  291.                                 SetConsoleTextAttribute(g_hStdout,wCharAttr);//设置为新的颜色
  292.                                 wOldCharAttr=wCharAttr;//记录新的颜色
  293.                         }
  294.                         g_pStrBuf[LineBufEnd++]=charToAdd;
  295.                         iSrc1++;
  296.                         iSrc2++;
  297.                 }
  298.                 if(y<g_uScrHeight-1)
  299.                         g_pStrBuf[LineBufEnd++]='\n';
  300.                 lSrc1+=g_uScrWidth+g_uScrWidth;//指针一次跨两行
  301.                 lSrc2+=g_uScrWidth+g_uScrWidth;
  302.         }
  303.         fputs(g_pStrBuf,stdout);
  304.         g_bHftnPos=!g_bHftnPos;
  305. }

  306. //=============================================================================
  307. //函数:wmain
  308. //描述:程序入口点,UNICODE版本
  309. //-----------------------------------------------------------------------------
  310. int wmain(int argc,wchar_t**argv)
  311. {
  312.         VIP VIPlayer;
  313.         HRESULT hr;

  314.         //参数不足打印用法
  315.         if(argc<2)
  316.         {
  317.                 Usage(argc?argv[0]:NULL);
  318.                 return 1;
  319.         }

  320.         //取得用户输入的可选的屏幕尺寸
  321.         if(argc>=3)
  322.         {
  323.                 g_uScrWidth=(UINT)_wtoi(argv[2]);
  324.                 if(argc>=4)
  325.                         g_uScrHeight=(UINT)_wtoi(argv[3]);
  326.         }

  327.         //安装信号处理程序
  328.         signal(SIGINT,SigProc);

  329.         g_hStdout=GetStdHandle(STD_OUTPUT_HANDLE);

  330.         //必须先初始化COM才能初始化VIP
  331.         if(FAILED(hr=CoInitialize(NULL)))
  332.                 goto Cleanup;

  333.         if(FAILED(hr=InitVIP(&VIPlayer,argv[1],NULL,(FnOnRender)OnRender,TRUE,NULL)))
  334.                 goto Cleanup;

  335.         //修改代码页,使其能显示DOS字符
  336.         system("chcp 437");

  337.         if(!InitBuffer())
  338.         {
  339.                 hr=E_OUTOFMEMORY;
  340.                 goto Cleanup;
  341.         }

  342.         //开始播放
  343.         if(FAILED(hr=VIPPlay(&VIPlayer)))
  344.                 goto Cleanup;

  345.         //轮询检查是否播放完成
  346.         while(!g_bCompleted)
  347.         {
  348.                 VIPCheckCompleted(&VIPlayer,&g_bCompleted);
  349.         }

  350.         VIPStop(&VIPlayer);
  351.         CleanupVIP(&VIPlayer);
  352.         CleanupBuffer();
  353.         system("color 07");
  354.         return 0;
  355. Cleanup:
  356.         fprintf(stderr,"Error:HRESULT=0x%08X\n",hr);
  357.         VIPStop(&VIPlayer);
  358.         CleanupVIP(&VIPlayer);
  359.         CleanupBuffer();
  360.         system("color 07");
  361.         return 2;
  362. }
复制代码

本帖被以下淘专辑推荐:

回复

使用道具 举报

3

主题

42

回帖

156

积分

用户组: 小·技术宅

UID
70
精华
0
威望
1 点
宅币
109 个
贡献
0 次
宅之契约
0 份
在线时间
6 小时
注册时间
2014-2-20
发表于 2014-11-13 13:27:35 | 显示全部楼层
果断给跪。。。
回复

使用道具 举报

1110

主题

1651

回帖

7万

积分

用户组: 管理员

一只技术宅

UID
1
精华
244
威望
743 点
宅币
24221 个
贡献
46222 次
宅之契约
0 份
在线时间
2296 小时
注册时间
2014-1-26
 楼主| 发表于 2014-11-13 23:36:41 | 显示全部楼层
如果出现了一个单独的窗口显示视频,那说明解码器有问题,需要修复。
http://support.microsoft.com/?sc ... id=1139&sid=460
回复 赞! 靠!

使用道具 举报

3

主题

7

回帖

56

积分

用户组: 小·技术宅

UID
165
精华
0
威望
1 点
宅币
44 个
贡献
0 次
宅之契约
0 份
在线时间
3 小时
注册时间
2014-3-30
发表于 2015-7-31 01:00:25 | 显示全部楼层
这个好帅!技术宅赛高!
回复 赞! 靠!

使用道具 举报

1

主题

21

回帖

116

积分

用户组: 小·技术宅

UID
2198
精华
0
威望
2 点
宅币
67 个
贡献
23 次
宅之契约
0 份
在线时间
11 小时
注册时间
2017-1-26
发表于 2017-1-26 19:07:53 | 显示全部楼层
Bad_Apple在召唤
回复 赞! 靠!

使用道具 举报

QQ|Archiver|小黑屋|技术宅的结界 ( 滇ICP备16008837号 )|网站地图

GMT+8, 2024-4-19 07:10 , Processed in 0.045623 second(s), 37 queries , Gzip On.

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表