十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

openFrameworks 视频播放实战:基于 ofVideoPlayer 的加载、速度控制与像素可视化

openFrameworks 视频播放实战:基于 ofVideoPlayer 的加载、速度控制与像素可视化 openFrameworks 视频播放实战基于 ofVideoPlayer 的加载、速度控制与像素可视化【免费下载链接】openFrameworksopenFrameworks is a community-developed cross platform toolkit for creative coding in C.项目地址: https://gitcode.com/gh_mirrors/op/openFrameworks本篇技术指南以 openFrameworks 官方示例 videoPlayerExample 为主体系统讲解如何在 openFrameworks 中使用ofVideoPlayer完成视频加载、播放、倍速控制、位置跳转与逐帧浏览并演示如何读取视频帧像素做实时可视化。读完本文你将掌握ofVideoPlayer的核心 API 用法并理解其跨平台后端实现原理可直接复用到自己的创意编程项目中。示例概览与学习目标videoPlayerExample位于 examples/video/videoPlayerExample演示了在 openFrameworks 中播放视频的完整流程。根据其 README该示例覆盖以下学习要点如何从data/文件夹加载视频如何播放视频如何改变视频播放速度如何使用鼠标位置控制视频播放速度如何使用左右方向键逐帧前后跳转如何遍历视频帧的像素用像素值控制圆形的大小运行该应用后屏幕左侧显示加载的视频原始画面右侧则显示一个风格化的可视化画面——根据像素亮度生成大小不同的黑色圆形README。示例的核心类是ofVideoPlayer其声明位于 libs/openFrameworks/video/ofVideoPlayer.h实现位于 libs/openFrameworks/video/ofVideoPlayer.cpp。项目结构与视频资源约定示例的源码结构如下examples/video/videoPlayerExample/ ├── src/ │ ├── main.cpp # 窗口初始化与主循环入口 │ ├── ofApp.h # 应用类声明包含 ofVideoPlayer 成员 │ └── ofApp.cpp # 核心逻辑加载、更新、绘制、交互 ├── bin/data/movies/fingers.mp4 # 示例视频资源 ├── videoPlayerExample.png # 运行截图 └── README.mdopenFrameworks 有一条重要的资源路径约定所有数据文件都放在bin/data/目录下代码中以相对路径加载资源时路径是相对于bin/data/的。因此本例中fingerMovie.load(movies/fingers.mp4);实际对应的是bin/data/movies/fingers.mp4视频文件位于examples/video/videoPlayerExample/bin/data/movies/fingers.mp4。这条约定贯穿所有 openFrameworks 项目——图片、字体、着色器、模型等资源都遵循同样的规则。加载视频load() 与像素格式在ofApp.h中应用声明了唯一的视频播放器成员变量ofVideoPlayer fingerMovie; bool frameByframe;setup()中完成视频加载与初始化void ofApp::setup(){ ofBackground(255,255,255); ofSetVerticalSync(true); frameByframe false; // Uncomment this to show movies with alpha channels // fingerMovie.setPixelFormat(OF_PIXELS_RGBA); fingerMovie.load(movies/fingers.mp4); fingerMovie.setLoopState(OF_LOOP_NORMAL); fingerMovie.play(); }几个关键点setPixelFormat(OF_PIXELS_RGBA)默认视频按 RGB 解码若视频带 alpha 通道需在load()之前取消注释该行切换为 RGBA。从 ofVideoPlayer.cpp 的实现看setPixelFormat()对已加载的视频会直接拒绝并输出警告setPixelFormat(): cant set pixel format of a loaded movie因此像素格式必须放在load()之前设置。setLoopState(OF_LOOP_NORMAL)设置循环播放。值得一提的是ofVideoPlayer::load()内部本身就会调用setLoopState(OF_LOOP_NORMAL)见 ofVideoPlayer.cpp示例中显式调用是为了让意图更清晰。play()开始播放。ofVideoPlayer还提供stop()暂停并将播放头重置到第一帧。ofVideoPlayer的循环模式由枚举ofLoopType定义位于 libs/openFrameworks/video/ofVideoBaseTypes.h枚举值值行为OF_LOOP_NONE0x01只播放一次不循环OF_LOOP_PALINDROME0x02正向播放后反向播放往复循环OF_LOOP_NORMAL0x03从头到尾不断循环播放播放循环update() 驱动帧更新ofVideoPlayer是一个拉模式pull-based播放器它不会自己跑线程而是要求你在应用每一帧调用一次update()来推进内部状态见 ofVideoPlayer.h 的文档注释void ofApp::update(){ fingerMovie.update(); }在 ofVideoPlayer.cpp 的update()实现中除了将调用转发给底层播放器外还会检测isFrameNew()——当底层解码出新帧时自动把像素数据上传到ofTexture若播放器实现不提供内部纹理指针保证draw()时能直接绘制纹理。这就是为什么示例中draw()可以无额外处理地直接调用fingerMovie.draw(20,20)。绘制视频与像素可视化draw()中同时完成两件事绘制原始视频以及遍历像素生成点阵可视化void ofApp::draw(){ ofSetHexColor(0xFFFFFF); fingerMovie.draw(20,20); // 左侧原始视频 ofSetHexColor(0x000000); ofPixels pixels fingerMovie.getPixels(); // 获取当前帧像素 int vidWidth pixels.getWidth(); int vidHeight pixels.getHeight(); int nChannels pixels.getNumChannels(); // 以 8 像素为步长遍历 RGB(A) 数组用红色通道控制圆的大小 for (int i 4; i vidWidth; i8){ for (int j 4; j vidHeight; j8){ unsigned char r pixels[(j * 320 i)*nChannels]; float val 1 - ((float)r / 255.0f); ofDrawCircle(400 i,20j,10*val); } } // ... HUD 文字帧号、时长、速度、影片结束提示 }这段代码是像素级视频处理的典型范式值得逐行拆解getPixels()返回当前帧的ofPixels引用getPixelsRef()是已废弃的旧名称新代码统一使用getPixels()。getNumChannels()返回通道数RGB 为 3RGBA 为 4。像素缓冲区是紧凑的RGB(A)字符数组第(j * width i) * nChannels个字节就是坐标(i, j)的红色通道。代码中j * 320的320是视频宽度硬编码值视频实际宽 320 像素更通用的写法是用vidWidth代替即pixels[(j * vidWidth i) * nChannels]。亮度映射val 1 - r/255红色越亮则圆形半径越小10 * val从而把视频帧转化为明暗反相的点阵艺术效果。ofDrawCircle(x, y, radius)是 openFrameworks 的全局绘制函数此处循环绘制约(320/8) × (180/8)个圆形。底部 HUD 还展示了几个常用查询接口的用法ofDrawBitmapString(frame: ofToString(fingerMovie.getCurrentFrame()) / ofToString(fingerMovie.getTotalNumFrames()), 20, 380); ofDrawBitmapString(duration: ofToString(fingerMovie.getPosition() * fingerMovie.getDuration(), 2) / ofToString(fingerMovie.getDuration(), 2), 20, 400); ofDrawBitmapString(speed: ofToString(fingerMovie.getSpeed(), 2), 20, 420); if(fingerMovie.getIsMovieDone()){ // 影片播放结束提示 }其中getPosition()返回归一化的播放头位置0.01.0getDuration()返回视频总时长秒两者相乘即当前播放时间getIsMovieDone()用于检测视频是否已播放完毕。键盘交互逐帧控制与暂停keyPressed()演示了逐帧浏览与暂停切换void ofApp::keyPressed (int key){ switch(key){ case f: frameByframe !frameByframe; fingerMovie.setPaused(frameByframe); // 进入逐帧模式时暂停 break; case OF_KEY_LEFT: fingerMovie.previousFrame(); // 上一帧 break; case OF_KEY_RIGHT: fingerMovie.nextFrame(); // 下一帧 break; case 0: fingerMovie.firstFrame(); // 跳回第一帧 break; } }按f在鼠标速度模式与逐帧模式之间切换进入逐帧模式时调用setPaused(true)冻结播放配合方向键手动浏览每一帧。previousFrame()/nextFrame()分别向后/向前移动一帧。根据 ofVideoBaseTypes.h 的文档它们允许在不调用play()的情况下手动逐帧推进播放头。firstFrame()等价于setFrame(0)或setPosition(0.0)。ofVideoPlayer还提供setFrame(int frame)直接跳转到指定帧号——注意帧号从 0 开始最后一帧为getTotalNumFrames() - 1见 ofVideoBaseTypes.h。鼠标交互速度控制与位置拖拽mouseMoved()演示了如何用鼠标横向位置实时控制播放速度void ofApp::mouseMoved(int x, int y ){ if(!frameByframe){ int width ofGetWidth(); float pct (float)x / (float)width; // 0.0左~ 1.0右 float speed (2 * pct - 1) * 5.0f; // 映射到 -5.0 ~ 5.0 fingerMovie.setSpeed(speed); } }鼠标移到窗口最左侧速度约为-5快速倒放移到最右侧速度约为55 倍速正放居中位置速度约为 0静止。setSpeed()的语义在 ofVideoBaseTypes.h 中有明确说明速度为1.0表示正常速度正放2.0为 2 倍速0.25为 1/4 慢放负数表示倒放速度0.0则相当于停止播放在OF_LOOP_PALINDROME模式下播放方向会随循环自动反转速度的绝对值决定速率。mouseDragged()演示了用鼠标拖拽控制播放位置void ofApp::mouseDragged(int x, int y, int button){ if(!frameByframe){ float pct (float)x / (float)width; fingerMovie.setPosition(pct); // 0.0 首帧 ~ 1.0 末帧 } }setPosition(float pct)接受 0.01.0 的归一化位置0.0 对应首帧、1.0 对应末帧非常适合实现进度条拖拽。mousePressed()/mouseReleased()则演示了点击时暂停、松开时继续播放void ofApp::mousePressed(int x, int y, int button){ if(!frameByframe){ fingerMovie.setPaused(true); } } void ofApp::mouseReleased(int x, int y, int button){ if(!frameByframe){ fingerMovie.setPaused(false); } }源码纵深ofVideoPlayer 的跨平台后端机制ofVideoPlayer本身是一个门面facade类真正的解码工作委托给实现了ofBaseVideoPlayer接口的具体播放器。在 ofVideoPlayer.cpp 中通过编译期宏按平台选择后端平台宏定义后端实现LinuxOF_VIDEO_PLAYER_GSTREAMERofGstVideoPlayer基于 GStreamermacOS 10.8 / iOSOF_VIDEO_PLAYER_AVFOUNDATIONofAVFoundationPlayerWindowsMSVCOF_VIDEO_PLAYER_MEDIA_FOUNDATIONofMediaFoundationPlayerWindowsMinGWOF_VIDEO_PLAYER_DIRECTSHOWofDirectShowPlayerEmscriptenWebOF_VIDEO_PLAYER_EMSCRIPTENofxEmscriptenVideoPlayerAndroidOF_VIDEO_PLAYER_ANDROIDofxAndroidVideoPlayer后端实例采用延迟创建策略getPlayer()首次被调用时才std::make_sharedOF_VID_PLAYER_TYPE()见 ofVideoPlayer.cpp因此未加载视频时调用查询类方法会安全地返回默认值如getDuration()返回 0.0、isLoaded()返回 false。load()的完整流程ofVideoPlayer.cpp若无后端实例则创建默认播放器并应用当前像素格式调用后端的player-load(fileName)加载成功后保存moviePath可通过getMoviePath()查询若启用纹理默认启用且后端不提供内部纹理则按像素平面数分配ofTexture纹理槽将循环状态重置为OF_LOOP_NORMAL。纹理渲染存在两条路径如果后端实现了getTexturePtr()如某些 GStreamer/AVFoundation 后端支持直接纹理上传ofVideoPlayer会直接复用其内部纹理以避免额外像素拷贝否则在update()中手动执行tex[i].loadData(plane)完成 CPU→GPU 上传ofVideoPlayer.cpp。此外还有两个实用 APIsetVolume(float volume)接受 0.01.0 的归一化音量超出 1.0 会被钳制并输出警告ofVideoPlayer.cpploadAsync()提供异步加载配合isLoaded()轮询加载状态适合大体积视频避免阻塞主线程。完整代码清单main.cpp负责创建 1024×768 的窗口并启动应用#include ofMain.h #include ofApp.h int main( ){ ofGLWindowSettings settings; settings.setSize(1024, 768); settings.windowMode OF_WINDOW; //can also be OF_FULLSCREEN auto window ofCreateWindow(settings); ofRunApp(window, std::make_sharedofApp()); ofRunMainLoop(); }完整的ofApp.cpp见 examples/video/videoPlayerExample/src/ofApp.cpp类声明见 ofApp.h。你可以在此基础上自由扩展例如用setFrame()实现进度条滑块跳帧用getIsMovieDone()在视频结束时触发下一段视频或特效用getPixels()结合 OpenCV参考 opencvExample做逐帧图像分析用getTexture()/bind()/unbind()将视频帧作为纹理输入到自定义着色器中。小结videoPlayerExample虽小却完整覆盖了ofVideoPlayer的常用能力资源加载约定、循环模式、播放/暂停/停止、速度与位置控制、逐帧浏览以及最重要的读取帧像素做实时可视化。结合 ofVideoPlayer.h 与 ofVideoBaseTypes.h 的源码注释你可以在不查阅外部资料的情况下掌握每个 API 的精确语义——这正是 openFrameworks 开源代码库的价值所在。【免费下载链接】openFrameworksopenFrameworks is a community-developed cross platform toolkit for creative coding in C.项目地址: https://gitcode.com/gh_mirrors/op/openFrameworks创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表