BIK作为在游戏中广泛使用的视频格式,这里就非常有必要普及一下了

直接贴代码,看注释吧。有不懂的地方就留言提问吧

/**
*
* 解码BIK视频文件为像素数据,使用PBO更新OpenGL纹理,绘制纹理完成视频显示
*
* PBO 即 Pixel Buffer Object 如其名 用来保存像素的缓冲区 这种缓冲区也是一种标准的 OpenGL 缓冲区
* 可以用glMapBuffer函数映射内存地址 直接访问该缓冲区的内存
* 在例子里 我们使用glMapBuffer函数来获得缓冲区的内存地址 然后直接把像素数据复制到该地址中
* 比直接调用glTexSubImage2D传递像素数据快上几十倍 特别是数据量大的时候
* 当我们成功把数据复制到PBO里 我们就可以调用glTexSubImage2D (最后一个参数设为NULL)
* 把像素数据从PBO复制到纹理 完成纹理的更新 此过程完全在显卡端实现 所以速度也是非常快的
*
**/ #include <windows.h> #include <stdio.h>
#include <string.h> #include <gl/glut.h>
#include <gl/glext.h> #include "bink.h"
#pragma comment( lib, "binkw32.lib" ) #include "Timer.h" PFNGLBINDBUFFERARBPROC glBindBufferARB;
PFNGLGENBUFFERSARBPROC glGenBuffersARB;
PFNGLBUFFERDATAARBPROC glBufferDataARB;
PFNGLMAPBUFFERARBPROC glMapBufferARB;
PFNGLUNMAPBUFFERARBPROC glUnmapBufferARB; HBINK g_pBink; int IMAGE_WIDTH; // BIK视频的宽度,例如:1280
int IMAGE_HEIGHT; // BIK视频的高度,例如:720 int DATA_PITCH; // 一行像素数据的大小,单位为字节
// 例如:视频宽度为->1280 没有Alpha通道(每个像素3字节,R/G/B分别1个字节)则一行像素数据大小为-> 1280 × 3 = 3840 int DATA_SIZE; // 一帧像素数据的大小,单位为字节
// 例如:视频宽度为->1280 视频高度为->720 没有Alpha通道(每个像素3字节,R/G/B分别1个字节)则一帧像素数据大小为-> 1280 × 720 × 3 = 2764800 int TEXTURE_WIDTH; // 2D纹理宽度。实际上请尽量使用2的N次方的数值,保证硬件兼容性 例如2/4/8/16/32/64/128之类
int TEXTURE_HEIGHT; // 2D纹理高度 // PBO缓冲区大小,最好和纹理大小相同以免复制内存越界什么的,当然如果你保证不会越界,那就设为BIK视频大小
int BUFFER_SIZE; // 用来显示视频的纹理(什么?你不懂?纹理不断变化就成了视频)
GLuint textureId; // 定义两个PBO,我们可以用两种方式通过PBO来更新纹理
// 1. 只使用1个PBO
// 2. 使用两个2PBO
GLuint pboIds[]; /**
* 输出一些显卡信息
**/
void GL_HardwareInfo( void )
{
char *pInfo1 = (char *)glGetString( GL_VENDOR );
char *pInfo2 = (char *)glGetString( GL_RENDERER );
char *pInfo3 = (char *)glGetString( GL_VERSION );
char *pInfo4 = (char *)glGetString( GL_SHADING_LANGUAGE_VERSION ); printf( "Vendor: %s\nRenderer: %s\nVersion: %s\nGLSL: %s\n", pInfo1, pInfo2, pInfo3, pInfo4 );
} /**
* 为了支持所有设备,纹理的宽和高必须为2的N次幂
* 返回值例如2/4/8/16/32/64 ..
**/
int suggestTexSize( int size )
{
int texSize = ; while ( texSize < size )
{
texSize <<= ;
} return texSize;
} /**
* 只是为了看看内存申请而已.. 不用也可以
**/
void *RADLINK BinkMemAlloc( U32 bytes )
{
return malloc( bytes );
} void RADLINK BinkMemFree( void PTR4 *ptr )
{
free( ptr );
} /**
* 打开BIK文件并创建合适大小的纹理和PBO
**/
void initBink( void )
{
BinkSetMemory( BinkMemAlloc, BinkMemFree ); //
// 使用WaveOut接口输出声音,兼容大多数Windows系统
//
BinkSoundUseWaveOut(); //
// 打开BIK文件,加入BINKSNDTRACK标志表示BIK文件包含音轨
//
g_pBink = BinkOpen( "Era.bik", BINKSNDTRACK ); if ( !g_pBink )
{
return;
} //
// 这里假设打开的BIK文件不包含ALPHA通道的,所以纹理格式选择了GL_RGB8,R/G/B通道分别占用1字节
// // 设置视频大小
IMAGE_WIDTH = g_pBink->Width;
IMAGE_HEIGHT = g_pBink->Height; // 设置像素数据大小
DATA_PITCH = IMAGE_WIDTH * ;
DATA_SIZE = DATA_PITCH * IMAGE_HEIGHT; // 设置2D纹理大小
TEXTURE_WIDTH = suggestTexSize( IMAGE_WIDTH );
TEXTURE_HEIGHT = suggestTexSize( IMAGE_HEIGHT ); // 设置PBO大小
BUFFER_SIZE = TEXTURE_WIDTH * TEXTURE_HEIGHT * ; // 创建2D纹理
glGenTextures( , &textureId );
glBindTexture( GL_TEXTURE_2D, textureId );
glTexImage2D( GL_TEXTURE_2D, , GL_RGB8, TEXTURE_WIDTH, TEXTURE_HEIGHT, , GL_RGB, GL_UNSIGNED_BYTE, NULL );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glBindTexture( GL_TEXTURE_2D, ); // 创建2个PBO
glGenBuffersARB( , pboIds );
glBindBufferARB( GL_PIXEL_UNPACK_BUFFER_ARB, pboIds[] );
glBufferDataARB( GL_PIXEL_UNPACK_BUFFER_ARB, BUFFER_SIZE, NULL, GL_STREAM_DRAW_ARB );
glBindBufferARB( GL_PIXEL_UNPACK_BUFFER_ARB, pboIds[] );
glBufferDataARB( GL_PIXEL_UNPACK_BUFFER_ARB, BUFFER_SIZE, NULL, GL_STREAM_DRAW_ARB );
glBindBufferARB( GL_PIXEL_UNPACK_BUFFER_ARB, );
} void init( void )
{
GL_HardwareInfo(); glBindBufferARB = (PFNGLBINDBUFFERARBPROC)wglGetProcAddress( "glBindBufferARB" );
glGenBuffersARB = (PFNGLGENBUFFERSARBPROC)wglGetProcAddress( "glGenBuffersARB" );
glBufferDataARB = (PFNGLBUFFERDATAARBPROC)wglGetProcAddress( "glBufferDataARB" );
glMapBufferARB = (PFNGLMAPBUFFERARBPROC)wglGetProcAddress( "glMapBufferARB" );
glUnmapBufferARB = (PFNGLUNMAPBUFFERARBPROC)wglGetProcAddress( "glUnmapBufferARB" ); if (!glBindBufferARB || !glGenBuffersARB || !glBufferDataARB || !glMapBufferARB || !glUnmapBufferARB)
{
printf("Your video card does not support pixel buffer object extension\n");
return;
} initBink(); glEnable( GL_TEXTURE_2D ); glEnable( GL_BLEND );
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); glColor3f( 1.0f, 1.0f, 1.0f );
} /**
* 使用 glTexSubImage2D 函数更新纹理
**/
void updateTextureWithOutBuffer( void )
{
} /**
* 只用1个PBO来更新纹理
* 流程为:复制像素数据到PBO -> 从PBO复制到纹理 -> 完成更新
**/
void updateTextureSingleBuffer( void )
{
// 复制像素数据(BIK -> PBO)
glBindBufferARB( GL_PIXEL_UNPACK_BUFFER_ARB, pboIds[] );
glBufferDataARB( GL_PIXEL_UNPACK_BUFFER_ARB, BUFFER_SIZE, NULL, GL_STREAM_DRAW_ARB ); // 如果上一次glBufferData还有未处理的数据,直接丢掉
void *ptr = glMapBufferARB( GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB );
if ( ptr )
{
// 这里直接把像素数据复制到PBO中(即显存)速度非常快
BinkCopyToBuffer( g_pBink, ptr, DATA_PITCH, IMAGE_HEIGHT, , , BINKSURFACE24R );
glUnmapBufferARB( GL_PIXEL_UNPACK_BUFFER_ARB );
} // 复制像素数据(PBO -> 纹理)
glBindTexture( GL_TEXTURE_2D, textureId );
glTexSubImage2D( GL_TEXTURE_2D, , , , IMAGE_WIDTH, IMAGE_HEIGHT, GL_RGB, GL_UNSIGNED_BYTE, NULL ); glBindBufferARB( GL_PIXEL_UNPACK_BUFFER_ARB, ); // 复制完成解绑PBO
//glBindTexture( GL_TEXTURE_2D, 0 );
} /**
* 使用2个PBO来更新纹理
* 流程为:复制像素数据到PBO-1 -> 从PBO-2复制到纹理 -> 完成更新 -> 下一帧-> 复制像素数据到PBO-2 -> 从PBO-1复制到纹理 -> 下一帧 -> ..
* 也就是说,实际上程序跑第2帧的时候纹理才更新为第1帧时候的数据,这个有点像屏幕双缓冲
**/
void updateTextureDoubleBuffer( void )
{
static int index = ;
int nextIndex = ; // increment current index first then get the next index
index = (index + ) % ;
nextIndex = (index + ) % ; glBindTexture( GL_TEXTURE_2D, textureId ); // Copy from Buffer(0) to Texture
glBindBufferARB( GL_PIXEL_UNPACK_BUFFER_ARB, pboIds[index] );
glTexSubImage2D( GL_TEXTURE_2D, , , , IMAGE_WIDTH, IMAGE_HEIGHT, GL_RGB, GL_UNSIGNED_BYTE, NULL ); // Copy from Bink to Buffer(1)
glBindBufferARB( GL_PIXEL_UNPACK_BUFFER_ARB, pboIds[nextIndex] );
glBufferDataARB( GL_PIXEL_UNPACK_BUFFER_ARB, BUFFER_SIZE, NULL, GL_STREAM_DRAW_ARB ); // flush data
void *ptr = glMapBufferARB( GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB );
if ( ptr )
{
// 这里直接把像素数据复制到PBO中(即显存)速度非常快
// 注意这里多了个 BINKCOPYALL 这个标志表示复制的时候要复制完整的一帧数据(Bink好像默认是只复制数据变化的部分) 因为交替两个PBO如果只复制变化的部分 那数据可能就重叠了
BinkCopyToBuffer( g_pBink, ptr, DATA_PITCH, IMAGE_HEIGHT, , , BINKCOPYALL | BINKSURFACE24R );
glUnmapBufferARB( GL_PIXEL_UNPACK_BUFFER_ARB );
} glBindBufferARB( GL_PIXEL_UNPACK_BUFFER_ARB, );
//glBindTexture( GL_TEXTURE_2D, 0 );
} /**
* 更新BIK和纹理
**/
void updateBink( void )
{
if ( !g_pBink )
{
return;
} //
// 处理当前帧,Bink内部会处理许多东西,例如更新内部计时器,更新声音播放缓存区等等
//
BinkDoFrame( g_pBink ); // 如果Bink需要等待,例如视频已经暂停,就直接返回吧,不要处理任何东西
if ( BinkWait( g_pBink ) )
{
return;
} //
// 如果当前需要跳过一些帧,这个函数就会返回TRUE,我们调用BinkNextFrame来跳过直到它返回FALSE就行了
// 如果两次更新时间间隔太长(程序太卡),为了保证画面和声音同步,我们就可能要跳过一些帧了
//
while ( BinkShouldSkip( g_pBink ) )
{
BinkNextFrame( g_pBink );
BinkDoFrame( g_pBink );
} //updateTextureWithOutBuffer(); //最慢的更新方式,除非硬件不支持PBO,否则拒绝使用
//updateTextureSingleBuffer();
updateTextureDoubleBuffer(); // 速度比单个PBO快 // 已经是最后一帧了 什么都不干
if ( g_pBink->FrameNum == g_pBink->Frames )
{
return;
} //
// 当前帧已经显示完毕 进入下一帧
//
BinkNextFrame( g_pBink );
} /**
* 统计一下函数1秒钟内运行了多少次.. 粗略的FPS计算吧
**/
void printInfo( void )
{
static Timer timer;
static int count = ;
double elapsedTime; elapsedTime = timer.getElapsedTime(); if ( elapsedTime < 1.0 )
{
count++;
}
else
{
printf( "FPS:%d\n", count );
count = ;
timer.start();
}
} void display( void )
{
static Timer updatelimit; glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); //
// 这里可以稍微降低更新频率以减少CPU负担
// if ( updatelimit.getElapsedTime() > 1.0 / 30.0 )
{
updateBink();
updatelimit.start();
} //
// 因为纹理大小不一定和视频大小一样,所以做个剪裁,以免绘制出纹理多余的部分
//
int X = ;
int Y = ;
float S0 = 0.0f;
float T0 = 0.0f;
float S1 = (float)IMAGE_WIDTH / (float)TEXTURE_WIDTH;
float T1 = (float)IMAGE_HEIGHT / (float)TEXTURE_HEIGHT; //
// 绘制纹理 (显示视频画面)
// glBindTexture( GL_TEXTURE_2D, textureId );
// 绘制一个视频大小的矩形
glColor3f( , , );
glBegin( GL_QUADS );
glTexCoord2f( S0, T0 );
glVertex2f( X, Y );
glTexCoord2f( S1, T0 );
glVertex2f( X + IMAGE_WIDTH, Y );
glTexCoord2f( S1, T1 );
glVertex2f( X + IMAGE_WIDTH, Y + IMAGE_HEIGHT );
glTexCoord2f( S0, T1 );
glVertex2f( X, Y + IMAGE_HEIGHT );
glEnd(); glFlush(); printInfo(); glutSwapBuffers();
glutPostRedisplay();
} void reshape( int width, int height )
{
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
gluOrtho2D( , width, height, );
glMatrixMode( GL_MODELVIEW ); glViewport( , , width, height );
} int main( int argc, char **argv )
{
glutInitWindowSize( , );
glutInit( &argc, argv );
glutInitDisplayMode( GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH );
glutCreateWindow( "Bink OpenGL" );
init();
glutDisplayFunc( display );
glutReshapeFunc( reshape );
glutMainLoop(); if ( g_pBink )
{
BinkClose( g_pBink );
g_pBink = NULL;
} return ;
}

glut.h glext.h 还要问去哪里找吗?

这里偷偷放一个 B**k SDK

https://pan.baidu.com/s/1nv5Pm2H
钥匙:5sx9

还有一个我测试用的视频

链接: https://pan.baidu.com/s/1c23zDx2 钥匙: eh6z

BIK的视频工具:搜索 《Rad Video Tools》即可

使用 OpenGL API 播放 BIK 视频的更多相关文章

  1. 在Android应用程序使用YouTube API来嵌入视频

    在Android版YouTube播放器API使您可以将视频播放功能到你的Android应用程序.该API允许您加载和播放YouTube视频(和播放列表),并自定义和控制视频播放体验. 您可以加载或暗示 ...

  2. Android 视频播放器 (三):使用NBPlayer播放直播视频

    一.前言 在 Android 音视频开发学习思路 中,我们不断的学习和了解音视频相关的知识,随着知识点不断的学习,我们现在应该做的事情,就是将知识点不断的串联起来.这样才能得到更深层次的领悟.通过整理 ...

  3. EasyNVR网页H5无插件播放摄像机视频功能二次开发之直播通道接口保活示例代码

    背景需求 随着雪亮工程.明厨亮灶.手机看店.智慧幼儿园监控等行业开始将传统的安防摄像头进行互联网.微信直播,我们知道摄像头直播的春天了.将安防摄像头或NVR上的视频流转成互联网直播常用的RTMP.HT ...

  4. SRS之播放推流视频

    1. 综述 首先,推流直播的配置文件如下: # rtmp.conf listen 1935; max_connections 1000; daemon off; srs_log_tank consol ...

  5. Android开发 MediaPlayer入门_播放本地视频

    前言 MediaPlayer,可以播放视频/音频,并且它支持本地和网络文件的播放.本片博客作为入门教程,先以最通俗的方式解释播放文件本地视频.(如果你嫌MediaPlayer还是太麻烦可以试试选择Vi ...

  6. [转] Android 4.4中播放HTML5视频<video>的Bug

    近期Nexus 4手机自动升级到Android4.4,本来挺好的一件事儿,结果发现自己的应用中出现一个Bug,应用中使用了Webview播放HTML5视频,代码如下: <video width= ...

  7. Android 播放在线视频

    首先开启电脑上的tomcat,将视频文件放在Tomcat 7.0\webapps\ROOT中 不用修改代码,直接输入地址即可,运行如下: 播放在线视频,必须要求手机支持当前的格式,才可以播放 播放的原 ...

  8. Android Multimedia框架总结(二)MediaPlayer框架及播放网络视频案例

    前言:前面一篇我们介绍MediaPlayer相关方法,有人说,没有实际例子,看得不是很明白,今天在分析MediaPlayer时,顺带一个播放网络视频例子.可以自行试试.今天分析的都是下几篇介绍各个模块 ...

  9. 利用开源jPlayer播放.flv视频文件

    最近工作中用到视频播放,在网上搜索对比了好几款开源播放插件后,觉得 jPlayer 是比较不错的,故作此记录! 接下来先快速的展示一下 利用jPlayer播放.flv视频的效果: <!DOCTY ...

随机推荐

  1. IIS 配置网站

    IIS 配置网站最常见的问题 1 文件夹权限问题 (C盘 windows下temp文件夹权限问题)2 版本问题 (框架版本问题如4.0 在2.0下运行,4.5在4.0下运行) 3如果是局域网,考虑动态 ...

  2. Core - Provide an easy way to store administrator and user model differences in a custom store (e.g., in a database)

    https://www.devexpress.com/Support/Center/Question/Details/S32444/core-provide-an-easy-way-to-store- ...

  3. JavaWeb项目学习教程(2) 系统数据库设计

    最开始本来想写一个管理系统,因为考虑到期末来临,我女朋友就可以看着教程然后学一些东西,然后可以自己慢慢手敲代码.但无奈自己也太懒,两个月过后,我才开始继续写这个博客,而现在我都已经开学了.不过博客还是 ...

  4. Siki_Unity_3-3_背包系统

    Unity 3-3 背包系统(基于UGUI) 任务1&2&3:演示.介绍.类图分析 背包面板.箱子面板.锻造合成面板.装备佩戴面板.商店面板等 面板的显示和隐藏.保存和加载.拾起物品. ...

  5. 【python 3.6】类:访问属性及调用方法

    >>> class price(): //定义1个类,用于计算价格 def __init__(self,name,danjia): //初始化方法,定义商品名称和单价 self.na ...

  6. python之multiprocessing创建进程

    python的multiprocessing模块是用来创建多进程的,下面对multiprocessing总结一下使用记录. multiprocessing创建多进程在windows和linux系统下的 ...

  7. js 基础拓展

    1.关于 try catch 的用法 <body> <div>请输出一个 5 到 10 之间的数字:</div> <input id="demo&q ...

  8. 数据库——SQL数据定义

    数据定义  SQL的数据定义语句 操 作 对 象 操  作  方  式 创  建 删  除 修  改 表 CREATE TABLE DROP TABLE ALTER TABLE 视  图 CREATE ...

  9. MathExam6378

    我的第一个程序 一.预估与实际 PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟) Planning 计划 15 10 • Estima ...

  10. Ubuntu16.04 下虚拟环境的创建与使用

    1. 虚拟环境   虚拟环境(virtual environment),顾名思义是虚拟出来的环境,通俗来讲,可以借助虚拟机,docker来理解虚拟环境,就是把一部分内容独立出来,我们把这部分独立出来的 ...