我的第一个Filter -- Text Overlay

本文介绍如何使用CTransInPlaceFilter开发DirectShow Filter,重点讲解了项目配置、关键函数实现及Filter注册等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

    指南中使用的是 CTransInPlaceFilter 作为这个Filter的基类(一般情况接收并处理数据,我们选择CTransformFilter,但是这里可以“就地”处理数据,所以我们选择了继承自他的CTransInPlaceFilter),其输入输出Pin上一般使用相同的媒体进行连接,并尽量使用同一个Sample管理器,从而避免了内部从输入Pin到输出Pin的一次内存拷贝。(但如果两端就修改Sample无法达成一致时,Input Pin 和 Output Pin 只有使用各自的Sample)

    CTransInPlaceFilter 与CTransformFilter 类似,都不需要重写输入输出Pin,他将Pin上必须实现的函数都“委托”到了Filter上,但如果我们要重写,则同时还需要重写GetPin函数。

    CTransInPlaceFilter 类最少需要重写以下两个函数。

(1) CTransformFilter::CheckInputType

(2) CTransInPlaceFilter::Transform

项目配置

  • 引用头文件目录 :"$(FrameworkSDKDir)/Samples/Multimedia/DirectShow/BaseClasses"
  • Preprocessor definition : WIN32;_DEBUG;_WINDOWS;_USRDLL;TEXTOVERLAYFILTER_EXPORTS
  • Runtime Library : Multi-threaded Debug (/MTd)
  • Additional Lib Directories : "$(FrameworkSDKDir)Samples/Multimedia/DirectShow/BaseClasses/Debug"
  • Dependency : Strmbasd.lib Msvcrtd.lib Winmm.lib
  • EntryPoint DllEntryPoint@12

首先 需要处理的是def 和 定义 DLL的入口函数.

<1> XXXX.def:

 

EXPORTS

    DllMain                 PRIVATE

    DllGetClassObject       PRIVATE

    DllCanUnloadNow         PRIVATE

    DllRegisterServer       PRIVATE

    DllUnregisterServer     PRIVATE

 

<2> 入口函数 (可以放在任意的cpp中,比如 dllmain.cpp)
//
// DllEntryPoint
//
extern "C" BOOL WINAPI DllEntryPoint(HINSTANCE, ULONG, LPVOID);
BOOL APIENTRY DllMain(HANDLE hModule, 
                      DWORD  dwReason, 
                      LPVOID lpReserved)
{
return DllEntryPoint((HINSTANCE)(hModule), dwReason, lpReserved);
}
<3>Filter 注册信息
<a> // {63B7E7F0-9CC7-4b81-A794-007290AD12DB} 可以放入 Guids.h中, 放入接口、组件标识
DEFINE_GUID(CLSID_TextOverlayFilter,
0x63b7e7f0, 0x9cc7, 0x4b81, 0xa7, 0x94, 0x0, 0x72, 0x90, 0xad, 0x12, 0xdb);
<b> 填好用于初始化Filter 的 CFactoryTemplate。 这里为类厂指定了 FilterName, CreateInstance以及 Pin 之类的初始化信息。
        <c> 定义两个导出函数 DllRegisterServer 和 DllUnregisterServer。
<4> 编译后自动注册

        在PostBuild中写入 regsvr32 /s $(SolutionDir)$(ConfigurationName)/TextOverlayFilter.dll

代码编写部分

<5> 框架函数的实现

  • CreateInstance :创建Filter对象实例,在gTemplates 中设定,被DirectShow 类厂调用。
  • CheckInputType:完成输入Pin上的媒体类型检查。
  • Transform:调用控制类,完成字符叠加函数的调用。
  • NonDelegatingQueryInterface :暴露出 Filter 所支持的接口。
  • CompleteConnect:在输入Pin成功连接后取得媒体类型描述。《指南》代码在这里将 输入数据的格式设置给了应用逻辑控制对象。
  • StartStreaming,StopStreaming函数,可以分别调用应用逻辑控制对象的相应函数,进行初始化和反初始化。

<6> Filter属性页

Filter 的属性页就是无法显示,寻找原因中。

<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>问卷管理系统 - Vue3 + Element Plus</title> <link rel="stylesheet" href="https://unpkg.com/element-plus/dist/index.css"> <script src="https://cdn.jsdelivr.net/npm/vue@3/dist/vue.global.js"></script> <script src="https://unpkg.com/element-plus/dist/index.full.js"></script> <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", Arial, sans-serif; background-color: #f5f7fa; color: #333; padding: 20px; } .container { max-width: 1400px; margin: 0 auto; background: #fff; border-radius: 8px; box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); padding: 25px; } .header { text-align: center; margin-bottom: 30px; padding-bottom: 20px; border-bottom: 1px solid #ebeef5; } .header h1 { color: #409EFF; font-size: 28px; margin-bottom: 10px; } .header p { color: #909399; font-size: 16px; } /* 顶部索引板块 - 桌面端垂直布局 */ .filter-container .el-form-item { margin-bottom: 22px; } .filter-container .el-form-item__label { display: block; text-align: left; padding: 0 0 8px 0; float: none; } .filter-container .el-form-item__content { margin-left: 0 !important; width: 100%; } .filter-container .el-select, .filter-container .el-input { width: 100%; } .search-btn-container { display: flex; justify-content: flex-end; margin-top: 10px; } .table-container { margin-top: 30px; } .pagination-container { margin-top: 20px; display: flex; justify-content: flex-end; } /* 移动端响应式优化 */ @media (max-width: 768px) { .container { padding: 15px; } .el-form-item { margin-bottom: 15px; } .el-form-item__label { float: none; display: block; text-align: left; padding: 0 0 8px 0; } .el-form-item__content { margin-left: 0 !important; } .mobile-full-width { width: 100%; } .el-select { width: 100%; } .search-btn-container { flex-direction: column; gap: 10px; } .search-btn-container .el-button { margin-left: 0 !important; } } .status-tag { padding: 4px 8px; border-radius: 4px; font-size: 12px; } .status-0 { background: #f0f9eb; color: #67c23a; } .status-1 { background: #ecf5ff; color: #409eff; } .status-2 { background: #fdf6ec; color: #e6a23c; } .status-3 { background: #fef0f0; color: #f56c6c; } .loading-overlay { position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: rgba(255, 255, 255, 0.7); display: flex; align-items: center; justify-content: center; z-index: 10; } </style> </head> <body> <div id="app"> <div class="container"> <div class="header"> <h1>问卷管理系统</h1> <p>响应式详情页面 - 顶部索引与分页展示</p> </div> <!-- 顶部索引板块 --> <div class="filter-container"> <el-form :model="searchForm"> <!-- 问卷标题搜索 --> <el-form-item label="问卷标题"> <el-input v-model="searchForm.title" placeholder="输入问卷标题" clearable /> </el-form-item> <!-- 被测评人ID搜索 --> <el-form-item label="被测评人ID"> <el-input v-model="searchForm.userId" placeholder="输入被测评人ID" clearable /> </el-form-item> <!-- 部门选择 --> <el-form-item label="人员部门"> <el-select v-model="searchForm.dept" placeholder="选择部门" clearable > <el-option label="技术部" value="tech"></el-option> <el-option label="市场部" value="market"></el-option> <el-option label="人力资源部" value="hr"></el-option> <el-option label="财务部" value="finance"></el-option> <el-option label="产品部" value="product"></el-option> </el-select> </el-form-item> <!-- 状态选择 --> <el-form-item label="提交状态"> <el-select v-model="searchForm.status" placeholder="选择状态" clearable > <el-option label="未提交" value="0"></el-option> <el-option label="已提交" value="1"></el-option> <el-option label="审核中" value="2"></el-option> <el-option label="已完成" value="3"></el-option> </el-select> </el-form-item> <!-- 搜索按钮 --> <div class="search-btn-container"> <el-button type="primary" @click="handleSearch" icon="el-icon-search" class="mobile-full-width" :loading="loading" >搜索</el-button> <el-button type="info" @click="resetForm" icon="el-icon-refresh" class="mobile-full-width" >重置</el-button> <el-button type="success" @click="exportData" icon="el-icon-download" class="mobile-full-width" >导出数据</el-button> </div> </el-form> </div> <!-- 数据表格 --> <div class="table-container" v-loading="loading"> <el-table :data="tableData" border style="width: 100%"> <el-table-column prop="id" label="ID" width="80" /> <el-table-column prop="title" label="问卷标题" min-width="150" /> <el-table-column prop="userId" label="被测评人ID" width="120" /> <el-table-column prop="dept" label="部门" width="120"> <template #default="scope"> {{ formatDept(scope.row.dept) }} </template> </el-table-column> <el-table-column prop="status" label="状态" width="100"> <template #default="scope"> <span :class="['status-tag', `status-${scope.row.status}`]"> {{ formatStatus(scope.row.status) }} </span> </template> </el-table-column> <el-table-column prop="createTime" label="创建时间" width="180" /> <el-table-column prop="updateTime" label="更新时间" width="180" /> <el-table-column label="操作" width="120"> <template #default="scope"> <el-button size="mini" @click="handleEdit(scope.row)">查看</el-button> </template> </el-table-column> </el-table> <!-- 无数据提示 --> <div v-if="tableData.length === 0 && !loading" class="el-table__empty-block"> <span class="el-table__empty-text">暂无数据</span> </div> </div> <!-- 分页组件 --> <div class="pagination-container"> <el-pagination background layout="total, sizes, prev, pager, next, jumper" :total="total" :page-sizes="[10, 20, 50, 100]" :page-size="pageSize" :current-page="currentPage" @size-change="handleSizeChange" @current-change="handleCurrentChange" /> </div> </div> </div> <script> const { createApp, ref, reactive, computed, onMounted } = Vue; const app = createApp({ setup() { // 搜索表单数据 const searchForm = reactive({ title: '', userId: '', dept: '', status: '' }); // 表格数据 const tableData = ref([]); // 分页数据 const total = ref(0); const pageSize = ref(10); const currentPage = ref(1); // 加载状态 const loading = ref(false); // 部门格式化 const formatDept = (dept) => { const deptMap = { tech: '技术部', market: '市场部', hr: '人力资源部', finance: '财务部', product: '产品部' }; return deptMap[dept] || dept; }; // 状态格式化 const formatStatus = (status) => { const statusMap = { '0': '未提交', '1': '已提交', '2': '审核中', '3': '已完成' }; return statusMap[status] || status; }; // 获取表格数据 const fetchTableData = async () => { try { loading.value = true; // 构造请求参数 const params = { ...searchForm, page: currentPage.value, size: pageSize.value }; // 在实际项目中,这里应该调用真实API // const response = await axios.get('http://172.26.26.43/dev-api/wjdc/wj/listTx', { params }); // 模拟API延迟 await new Promise(resolve => setTimeout(resolve, 800)); // 模拟API响应 const mockData = { total: 85, data: Array.from({ length: pageSize.value }, (_, i) => ({ id: i + 1 + (currentPage.value - 1) * pageSize.value, title: `问卷${i + 1}`, userId: `USER${1000 + i}`, dept: ['tech', 'market', 'hr', 'finance', 'product'][i % 5], status: String(i % 4), createTime: '2023-06-01 10:00:00', updateTime: '2023-06-01 14:30:00' })) }; // 更新数据 tableData.value = mockData.data; total.value = mockData.total; } catch (error) { console.error('获取数据失败:', error); } finally { loading.value = false; } }; // 搜索操作 const handleSearch = () => { currentPage.value = 1; // 重置到第一页 fetchTableData(); }; // 重置表单 const resetForm = () => { Object.keys(searchForm).forEach(key => { searchForm[key] = ''; }); currentPage.value = 1; fetchTableData(); }; // 分页大小改变 const handleSizeChange = (size) => { pageSize.value = size; fetchTableData(); }; // 当前页改变 const handleCurrentChange = (page) => { currentPage.value = page; fetchTableData(); }; // 导出数据 const exportData = () => { ElMessage.success('导出功能已触发,在实际项目中会调用导出API'); console.log('导出参数:', searchForm); }; // 编辑操作 const handleEdit = (row) => { ElMessage.info(`查看问卷: ${row.title}`); console.log('查看问卷数据:', row); }; // 初始化时加载数据 onMounted(() => { fetchTableData(); }); return { searchForm, tableData, total, pageSize, currentPage, loading, formatDept, formatStatus, handleSearch, resetForm, handleSizeChange, handleCurrentChange, exportData, handleEdit }; } }); app.use(ElementPlus); app.mount('#app'); </script> </body> </html> 帮我修改代码,写到.vue文件里
最新发布
07-15
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值