SQL Server查询速度慢的原因有很,常见的有以下几种:
1、没有索引或者没有用到索引(这是查询慢最常见的问题,是程序设计的缺陷)
2、I/O吞吐量小,形成了瓶颈效应。
3、没有创建计算列导致查询不优化。
4、内存不足
5、网络速度慢
6、查询出的数据量过大(可以采用多次查询,其他的方法降低数据量)
7、锁或者死锁(这也是查询慢最常见的问题,是程序设计的缺陷)
8、sp_lock,sp_who,活动的用户查看,原因是读写竞争资源。
9、返回了不必要的行和列
10、查询语句不好,没有优化
●可以通过以下方法来优化查询 :
1、把数据、日志、索引放到不同的I/O设备上,增加读取速度,以前可以将Tempdb应放在RAID0上,SQL2000不在支持。数据量(尺寸)越大,提高I/O越重要.
2、纵向、横向分割表,减少表的尺寸(sp_spaceuse)
3、升级硬件
4、根据查询条件,建立索引,优化索引、优化访问方式,限制结果集的数据量。注意填充因子要适当(最好是使用默认值0)。索引应该尽量小,使用字节数小的列建索引好(参照索引的创建),不要对有限的几个值的字段建单一索引如性别字段
5、提高网速;
6、扩大服务器的内存,Windows 2000和SQL server 2000能支持4-8G的内存。
配 置虚拟内存:虚拟内存大小应基于计算机上并发运行的服务进行配置。运行 Microsoft SQL Server? 2000 时,可考虑将虚拟内存大小设置为计算机中安装的物理内存的 1.5 倍。如果另外安装了全文检索功能,并打算运行 Microsoft 搜索服务以便执行全文索引和查询,可考虑:将虚拟内存大小配置为至少是计算机中安装的物理内存的 3 倍。将 SQL Server max server memory 服务器配置选项配置为物理内存的 1.5 倍(虚拟内存大小设置的一半)。
7、增加服务器CPU个数;但是必须 明白并行处理串行处理更需要资源例如内存。使用并行还是串行程是MsSQL自动评估选择的。单个任务分解成多个任务,就可以在处理器上运行。例如耽搁查询 的排序、连接、扫描和GROUP BY字句同时执行,SQL SERVER根据系统的负载情况决定最优的并行等级,复杂的需要消耗大量的CPU的查询最适合并行处理。但是更新操作UPDATE,INSERT, DELETE还不能并行处理。
8、如果是使用like进行查询的话,简单的使用index是不行的,但是全文索引,耗空间。 like ''a%'' 使用索引 like ''%a'' 不使用索引用 like ''%a%'' 查询时,查询耗时和字段值总长度成正比,所以不能用CHAR类型,而是VARCHAR。对于字段的值很长的建全文索引。
9、DB Server 和APPLication Server 分离;OLTP和OLAP分离
10、分布式分区视图可用于实现数据库服务器联合体。
联合体是一组分开管理的服务器,但它们相互协作分担系统的处理负荷。这种通过分区数据形成数据库服务器联合体的机制能够扩大一组服务器,以支持大型的多层 Web 站点的处理需要。有关更多信息,参见设计联合数据库服务器。(参照SQL帮助文件''分区视图'')
a、在实现分区视图之前,必须先水平分区表
b、 在创建成员表后,在每个成员服务器上定义一个分布式分区视图,并且每个视图具有相同的名称。这样,引用分布式分区视图名的查询可以在任何一个成员服务器上 运行。系统操作如同每个成员服务器上都有一个原始表的复本一样,但其实每个服务器上只有一个成员表和一个分布式分区视图。数据的位置对应用程序是透明的。
11、重建索引 DBCC REINDEX ,DBCC INDEXDEFRAG,收缩数据和日志 DBCC SHRINKDB,DBCC SHRINKFILE. 设置自动收缩日志.对于大的数据库不要设置数据库自动增长,它会降低服务器的性能。
在T-sql的写法上有很大的讲究,下面列出常见的要点:首先,DBMS处理查询计划的过程是这样的:
1、 查询语句的词法、语法检查
2、 将语句提交给DBMS的查询优化器
3、 优化器做代数优化和存取路径的优化
4、 由预编译模块生成查询规划
5、 然后在合适的时间提交给系统处理执行
6、 最后将执行结果返回给用户。
软件开发网 www.mscto.com
其次,看一下SQL SERVER的数据存放的结构:一个页面的大小为8K(8060)字节,8个页面为一个盘区,按照B树存放。
12、 Commit和rollback的区别 Rollback:回滚所有的事物。 Commit:提交当前的事物. 没有必要在动态SQL里写事物,如果要写请写在外面如: begin tran exec(@s) commit trans 或者将动态SQL 写成函数或者存储过程。
13、在查询Select语句中用Where字句限制返回的行数,避免表扫描,如果返回不必要的数据,浪费了服务器的I/O资源,加重了网络的负担降低性能。如果表很大,在表扫描的期间将表锁住,禁止其他的联接访问表,后果严重。
14、SQL的注释申明对执行没有任何影响
15、 尽可能不使用光标,它占用大量的资源。如果需要row-by-row地执行,尽量采用非光标技术,如:在客户端循环,用临时表,Table变量,用子查 询,用Case语句等等。游标可以按照它所支持的提取选项进行分类: 只进 必须按照从第一行到最后一行的顺序提取行。FETCH NEXT 是唯一允许的提取操作,也是默认方式。可滚动性 可以在游标中任何地方随机提取任意行。游标的技术在SQL2000下变得功能很强大,他的目的是支持循环。有四个并发选项 READ_ONLY:不允许通过游标定位更新(Update),且在组成结果集的行中没有锁。 OPTIMISTIC WITH valueS:乐观并发控制是事务控制理论的一个标准部分。乐观并发控制用于这样的情形,即在打开游标及更新行的间隔中,只有很小的机会让第二个用户更新 某一行。当某个游标以此选项打开时,没有锁控制其中的行,这将有助于最大化其处理能力。如果用户试图修改某一行,则此行的当前值会与最后一次提取此行时获 取的值进行比较。如果任何值发生改变,则服务器就会知道其他人已更新了此行,并会返回一个错误。如果值是一样的,服务器就执行修改。 选择这个并发选项OPTIMISTIC WITH ROW VERSIONING:此乐观并发控制选项基于行版本控制。使用行版本控制,其中的表必须具有某种版本标识符,服务器可用它来确定该行在读入游标后是否有 所更改。在 SQL Server 中,这个性能由 timestamp 数据类型提供,它是一个二进制数字,表示数据库中更改的相对顺序。每个数据库都有一个全局当前时间戳值:@@DBTS。每次以任何方式更改带有 timestamp 列的行时,SQL Server 先在时间戳列中存储当前的 @@DBTS 值,然后增加 @@DBTS 的值。如果某 个表具有 timestamp 列,则时间戳会被记到行级。服务器就可以比较某行的当前时间戳值和上次提取时所存储的时间戳值,从而确定该行是否已更新。服务器不必比较所有列的值,只需 比较 timestamp 列即可。如果应用程序对没有 timestamp 列的表要求基于行版本控制的乐观并发,则游标默认为基于数值的乐观并发控制。 SCROLL LOCKS 这个选项实现悲观并发控制。在悲观并发控制中,在把数据库的行读入游标结果集时,应用程序将试图锁定数据库行。在使用服务器游标时,将行读入游标时会在其 上放置一个更新锁。如果在事务内打开游标,则该事务更新锁将一直保持到事务被提交或回滚;当提取下一行时,将除去游标锁。如果在事务外打开游标,则提取下 一行时,锁就被丢弃。因此,每当用户需要完全的悲观并发控制时,游标都应在事务内打开。更新锁将阻止任何其它任务获取更新锁或排它锁,从而阻止其它任务更 新该行。然而,更新锁并不阻止共享锁,所以它不会阻止其它任务读取行,除非第二个任务也在要求带更新锁的读取。滚动锁根据在游标定义的 SELECT 语句中指定的锁提示,这些游标并发选项可以生成滚动锁。滚动锁在提取时在每行上获取,并保持到下次提取或者游标关闭,以先发生者为准。下次提取时,服务器 为新提取中的行获取滚动锁,并释放上次提取中行的滚动锁。滚动锁独立于事务锁,并可以保持到一个提交或回滚操作之后。如果提交时关闭游标的选项为关,则 COMMIT 语句并不关闭任何打开的游标,而且滚动锁被保留到提交之后,以维护对所提取数据的隔离。所获取滚动锁的类型取决于游标并发选项和游标 SELECT 语句中的锁提示。锁提示 只读 乐观数值 乐观行版本控制 锁定无提示 未锁定 未锁定 未锁定 更新 NOLOCK 未锁定 未锁定 未锁定 未锁定 HOLDLOCK 共享 共享 共享 更新 UPDLOCK 错误 更新 更新 更新 TABLOCKX 错误 未锁定 未锁定 更新其它 未锁定 未锁定 未锁定 更新 *指定 NOLOCK 提示将使指定了该提示的表在游标内是只读的。
16、用Profiler来跟踪查询,得到查询所需的时间,找出SQL的问题所在;用索引优化器优化索引
17、注意UNion和UNion all 的区别。UNION all好
18、注意使用DISTINCT,在没有必要时不要用,它同UNION一样会使查询变慢。重复的记录在查询里是没有问题的
19、查询时不要返回不需要的行、列
20、 用sp_configure ''query governor cost limit''或者SET QUERY_GOVERNOR_COST_LIMIT来限制查询消耗的资源。当评估查询消耗的资源超出限制时,服务器自动取消查询,在查询之前就扼杀掉。 SET LOCKTIME设置锁的时间
21、用select top 100 / 10 Percent 来限制用户返回的行数或者SET ROWCOUNT来限制操作的行
22、 在SQL2000以前,一般不要用如下的字句: "IS NULL", "<>", "!=", "!>", "!<", "NOT", "NOT EXISTS", "NOT IN", "NOT LIKE", and "LIKE ''%500''",因为他们不走索引全是表扫描。也不要在WHere字句中的列名加函数,如Convert,substring等,如果必须用函数的时 候,创建计算列再创建索引来替代.还可以变通写法:WHERE SUBSTRING(firstname,1,1) = ''m''改为WHERE firstname like ''m%''(索引扫描),一定要将函数和列名分开。并且索引不能建得太多和太大。NOT IN会多次扫描表,使用EXISTS、NOT EXISTS ,IN , LEFT OUTER JOIN 来替代,特别是左连接,而Exists比IN更快,最慢的是NOT操作.如果列的值含有空,以前它的索引不起作用,现在2000的优化器能够处理了。相同 的是IS NULL,“NOT", "NOT EXISTS", "NOT IN"能优化她,而”<>”等还是不能优化,用不到索引。
23、使用Query Analyzer,查看SQL语句的查询计划和评估分析是否是优化的SQL。一般的20%的代码占据了80%的资源,我们优化的重点是这些慢的地方。
24、如果使用了IN或者OR等时发现查询没有走索引,使用显示申明指定索引: SELECT * FROM PersonMember (INDEX = IX_Title) WHERE processid IN (‘男’,‘女’)
25、将需要查询的结果预先计算好放在表中,查询的时候再SELECT。这在SQL7.0以前是最重要的手段。例如医院的住院费计算。
26、MIN() 和 MAX()能使用到合适的索引。
27、 数据库有一个原则是代码离数据越近越好,所以优先选择Default,依次为Rules,Triggers, Constraint(约束如外健主健CheckUNIQUE……,数据类型的最大长度等等都是约束),Procedure.这样不仅维护工作小,编写程 序质量高,并且执行的速度快。
28、如果要插入大的二进制值到Image列,使用存储过程,千万不要用内嵌INsert来插入(不知JAVA 是否)。因为这样应用程序首先将二进制值转换成字符串(尺寸是它的两倍),服务器受到字符后又将他转换成二进制值.存储过程就没有这些动作: 方法:Create procedure p_insert as insert into table(Fimage) values (@image), 在前台调用这个存储过程传入二进制参数,这样处理速度明显改善。
posted @
2009-01-05 10:47 石正 阅读(419) |
评论 (0) |
编辑 收藏
Gartner Group公布了IT业界未来四年来的十大新技术.
多核心处理器,虚拟化和社交网络排行前三,同时一些用户端技术也占了榜单的数个位置. Gartner建议CIO建立一个正式的机制,建立虚拟团队,选拔优秀的工作人员来研究新的思路和创新,特别是消费者所关注的新Web2.0技术,并运用这些技术来解决问题.
1. 多核心和混合处理器
2. 虚拟化和构造计算
3. 社交网络和社交软件
4. 云计算/Web平台
5. Web Mashups
6. 用户界面(加速计,无线界面,全息和三维成像等)
7. 普及计算
8. 上下文计算
9. 增强实体
10. 语义学
posted @
2008-06-17 08:52 石正 阅读(264) |
评论 (0) |
编辑 收藏
posted @
2008-06-13 14:00 石正 阅读(863) |
评论 (0) |
编辑 收藏
十大最不可思议的
计算机
http://club2.cat898.com/newbbs/dispbbs.asp?boardid=41&id=2189094
据英国《新科学家》杂志报道,现今的计算机利用
电子脉冲和磁体实现
信息操作和
数据储存,但是随着技术发展,还有其它更为怪异的方法处理这些信息。《新科学家》杂志列出了10大最不可思议的计算方式,包括光计算、量子计算、神经计算以及水波计算等。
1、光计算
光线传输编码信息不足为奇,因为全球通信全部依靠光缆来传输,但用光信号来处理数据和进行计算还是不切实际的。研究光计算机还是值得做的,因为使用光能加快计算机的速度和处理的数据量。
但对光的诱捕、储存以及操作依然非常困难。美国伊利诺伊州立大学保罗·布劳恩等人的研究让我们更接近这一目标。他们已经研制成一款三维光学波导光子晶体,可以诱捕光,使其降低速度,并在锐角转角处让光弯曲,而不必担心光逃逸。同时,美国哈佛大学的米哈伊尔·卢金已经开发出一种光晶体管,可以让单个光子从一个光
信号转换成另外一个光信号。
2、量子计算
如果你不再想打破所有传
统计算的规则,那么量子计算方式将最为现实。量子计算不使用传统计算的基本信息比特,而利用量子
机械效应创造量子比特,可实现平行计算,并且随着量子比特数的增加,它们处理数据的速度将呈指数形式上升,将能完成现代计算机所不能完成的事情,比如快速分解大质数、破解密码锁等。
3、DNA计算
DNA可能是完成计算的最完美材料。DNA计算的创始人是美国南加州大学的莱昂那多·阿德莱曼教授,他于1994年利用DNA计算方法解决了一个著名的数学难题“七顶点哈密尔顿路径”。最近,科学家们开始利用DNA计算来创造生物计算机,放在人体或生物体工作,其计算结果可通过荧光蛋白的活动来读取。
4、可逆计算
可逆计算通过恢复和重新利用丢失数据的这些能量来减少计算机的能耗。美国佛罗里达大学的迈克尔·弗兰克正在开发这样一种方法:每个输入操作都会产生一个输出信号,将输出信号的能量保存起来,为新的信号输入提供能量,从而创造出一种零热量产生的计算方式。
5、撞球计算
传统的计算涉及电子通过电路内部每一个分子时产生的连锁反应,而科学家们正在尝试其它种类的连锁反应,包括多米诺或者大理石的连锁反应。
但是
制作这样的微处理器可能需要很大空间,除非多米诺或者大理石也很小。美国IBM公司的研究人员已经开始实验这种逻辑电路,通过使用原子互相撞击传递信息。尽管这种逻辑门只能使用一次,但是它却比现有的最小晶体管还要小很多。
6、神经元计算
研究神经元通常是指模仿人类大脑的计算方式。芝加哥
西北大学的费尔迪南多·莫沙-伊万迪正在研究如何利用七鳃鳗的大脑细胞控制机器人。
从机器人身上的光敏感元件上输出信号传递给神经元,神经元的反馈再反过来用来控制机器人的动作。此外,英国纽卡斯尔大学的神经学家克莱尔·林德受《星球大战》电影中记录的一只蝗虫的启发,开发出一种非常精确的故障排除系统。美国国防部高级研究计划署近来正在利用飞蛾大脑的电子元件,尝试遥控半机械昆虫间谍——飞蛾。
7、核磁共振计算
如果你知道如何操作水,那么每杯水就是一台电脑。英国约克大学的苏珊·斯特尼及其同事使用核磁共振强磁场来控制和观察分子的交互作用,这种方法能够以三维方式表现信息。如果研究能够成功,只要一杯水就可以模拟大气。然而这个研究小组只完成了原理证明。
8、Glooper计算机
这可能是最不可思议的计算机之一,它使用gloopware当硬件。英国西英格兰大学的安德鲁·亚当马特兹可以在一种化学制剂中制造干涉离子波,其行为要充当逻辑门。此波通过脉冲循环化学反应产生。亚当马特兹安德鲁已经展示其化学逻辑门可用于制造机器手臂。当机器人手指进行模仿动作时,化学反应就被触发,进而控制这只手。
9、霉变计算机
即使像粘菌这样的原始生物也可以被用来解决现今计算机难以处理的问题。日本北海道大学的Toshiyuki Nakagaki及其同事首次证实粘菌能够找到最短路线通过迷宫。
在他们的试验中,一种阿米巴门原生质粘菌起初也是尝试所有路径走出迷宫的。当这种生物找出最短路径通往有食物的出口时就不再找别的路线了,直奔食物去了。对于计算机专家们来说,这是一个非常有趣的实验,可解决行销推销员的问题。
10、水波计算
最不可思议的就是计算机竟然以水波为动力。英国苏塞克斯大学的克莉森西娅·费尔南多等利用波动箱和高架
摄像机,用水波动模式制造出一种逻辑门,称作“异或门(exclusive OR gate)”。人造神经网络感知器可以模仿一些逻辑门,但不能模仿异或门。费尔南多实现了这种逻辑门的工作原理。
posted @
2008-04-17 17:49 石正 阅读(274) |
评论 (0) |
编辑 收藏
驻伊美军机器人叛乱平息 20年内不再考虑机器人
http://club2.cat898.com/newbbs/dispbbs.asp?boardid=41&id=2189098
去年刚刚进驻伊拉克的美军重装遥控机器人系统SWORDS,也许永远都不会有机会上阵杀敌,摧城拔寨了。事实上,这种配备有M249轻机枪的机器人从未放过一枪一弹,便被迅速撤出战场——因为它们做了更可怕的事情:将枪口对向它们的人类指挥官。
在
新闻发布会上,美国陆军地面作战指挥官凯文·法赫称,这些机器人“将它们的机枪指向未受批准的方向”。显然,这意味着这些机器人遭遇了严重
事故,将枪口指向友军部队。而这也导致了这支机器人部队在正式部署前线仅11小时便被召回。
法赫证实机器人并未开火,也没有任何人类在此次事变中伤亡。但这并不意味着零损失,所有关于机器人作战的研究项目都可能因之取消。法赫称:“一旦遇到如此严重的事变,我们必须花上10年乃至20年,推倒重来。”因此,我们必须再等上很久才能看到机器人和人类并肩作战了。
这不仅仅对于机器人工业是重大损失,而且也树立了一个重要先例:关于机器人的任何试验都必须以无人类伤亡为绝对前提。也许对一切事故的极度厌恶,终将导致此领域的任何创新都步履维艰。
posted @
2008-04-17 17:32 石正 阅读(214) |
评论 (0) |
编辑 收藏
http://www.ugia.cn/?p=121
http://hi.baidu.com/momoca/blog/item/422a73f023999fafa40f52b1.html
posted @
2008-04-02 11:03 石正 阅读(590) |
评论 (0) |
编辑 收藏
软件企业技术路线:项目\产品\队伍
项目为企业近期发展发展积累资金;
产品为企业中期发展创造条件;
队伍是企业长期发展的根本;
企业可以根据自身的长期发展计划,制定近期的发展计划;
在有条件时,应该首先建设队伍,其次设计产品,再次做项目;
实际执行时,往往是3者混合进行的;
作为企业的主要负责人,管理人员,乃至对企业发展关心的员工,都应该明确企业发展的基本规律.来指导日常的工作实践。
posted @
2008-03-03 08:30 石正 阅读(1156) |
评论 (1) |
编辑 收藏
杀人的历史评传——刘邦杀韩信
http://club2.cat898.com/newbbs/dispbbs.asp?boardid=2&id=2067439
posted @
2008-02-27 11:18 石正 阅读(355) |
评论 (0) |
编辑 收藏
匈牙利科学家发明狗吠
翻译机 准确率达43%
http://club2.cat898.com/newbbs/dispbbs.asp?boardid=41&id=2040695
为了让狗的主人能够更好的了解自己
爱犬发出的叫声所表达的含义,匈牙利科学家日前发明了一种
计算机程序,该程序可以将狗的叫声“翻译”成人类能够听懂的语言。该程序
分析的准确率高达43%。
据报道,这个特殊的计算机程序是在分析了来自匈牙利14个地区6000多只牧羊犬在6种不同场合的叫声后设计而成的。这六种场合分别为:当只有一条狗时的叫声、狗之间发生冲突时候的叫声、当狗看到球后的叫声、当狗玩耍时发出的叫声和当狗看到陌生者时的叫声以及散步时发出的叫声。
据了解,来自匈牙利埃托沃罗兰德
大学的生态学
研究者查巴和他率领的科研团队通过研究发现,安装这种特殊软件程序的计算机能够准确分析出狗发出叫声所表达的含义。他们将狗的叫声被用录音设备记录下来,随后在计算机上进行数字化处理,然后研究人员使用计算机软件研究这些狗的叫声,以便分析这些叫声的不同之处。
研究人员发现,计算机基本能正确的辨别狗的叫声,计算机在分析狗所表达“打架”和“陌生者”这两种叫声时非常准确,高达43%。尽管辨别的
成功率不是很高,但是这个数字是人类无法比拟的。科学家表示,这个分析软件的识别率还有待于改善。
通过研究发现,狗之所以发出的叫声不同是由于它们的情绪受到相应影响的原因所致。查巴说:“如果这种设备研制成功投入使用的话,将有助于人类了解他们的爱犬日常都在‘说’些什么。”查巴表示,通过研究狗儿们发出不同的叫声可以分析出它们传递着相应的信息,这使得没有养狗经验的人也能知道狗为什么而叫。
据悉,该科研团队还计划研究其它动物的叫声,以便发现它们之间的共同之处。有关该研究的结果已经刊登在近日出版的《动物识别能力》杂志上。
posted @
2008-01-22 13:55 石正 阅读(275) |
评论 (0) |
编辑 收藏
设定access自增字段起始值.
下边修改picInfo表的
自增字段sn的起始值和步进值都为1
ALTER TABLE picInfo
ALTER COLUMN sn COUNTER (1, 1)
posted @
2008-01-10 11:38 石正 阅读(1167) |
评论 (0) |
编辑 收藏
http://www.cat898.com/Infolook.asp?bclass=1&id=57162
posted @
2007-12-14 09:10 石正 阅读(181) |
评论 (0) |
编辑 收藏
// 画实心椭圆形
CBrush Brush;
Brush.CreateSolidBrush( RGB( 111, 134, 233 ) );
pDC->BeginPath();
pDC->Ellipse( m_rcMark );
pDC->EndPath();
pDC->SelectObject(&Brush);
pDC->FillPath();
posted @
2007-11-28 11:34 石正 阅读(297) |
评论 (0) |
编辑 收藏
有三种方法:
1 在CApp类内设定其颜色值
2 OnCtrlColor
2 OnEraserBkColor
BOOL CMy444Dlg::OnEraseBkgnd(CDC* pDC)
{
// TODO: Add your message handler code here and/or call default
CBrush brush (RGB (128 , 0 , 128) );
CBrush* pOldBrush = pDC->SelectObject (&brush);
CRect reClip ;
pDC->GetClipBox (&reClip);
pDC-> PatBlt (reClip.left , reClip.top ,
reClip.Width ( ) , reClip.Height ( ) , PATCOPY );
pDC->SelectObject (pOldBrush );
return TRUE;
}
posted @
2007-11-27 14:37 石正 阅读(847) |
评论 (0) |
编辑 收藏
需要用到以下API:GetWindowRect和GetClientRect。
首先用GetWindowRect得到窗体的宽度与高度,再用GetClientRect得到窗体客户区的宽度与高度,用(窗体宽度-窗体客户区宽度)/2得到窗体边框宽度,之后用窗体高度-窗体客户区高度-窗体边框宽度得到*2得到标题栏的高度。
Windows XP 下窗口标题条高度 = 26
posted @
2007-11-27 14:13 石正 阅读(1117) |
评论 (1) |
编辑 收藏
为了对avi进行读写,微软提供了一套API,总共50个函数,他们的用途主要有两类,一个是avi文件的操作,一类是数据流streams的操作。
1、打开和关闭文件
AVIFileOpen ,AVIFileAddRef, AVIFileRelease
2、从文件中读取文件信息
通过AVIFileInfo可以获取avi文件的一些信息,这个函数返回一个AVIFILEINFO结构,通过AVIFileReadData可以用来获取AVIFileInfo函数得不到的信息。这些信息也许不包含在文件的头部,比如拥有file的公司和个人的名称。
3、写入文件信息
可以通过AVIFileWriteData函数来写入文件的一些额外信息。
4、打开和关闭一个流
打开一个数据流就跟打开文件一样,你可以通过 AVIFileGetStream函数来打开一个数据流,这个函数创建了一个流的接口,然后在该接口中保存了一个句柄。
如果你想操作文件的某一个单独的流,你可以采用AVIStreamOpenFromFile函数,这个函数综合了AVIFileOpen和AVIFileGetStream函数。
如果你想操作文件中的多个数据流,你就要首先AVIFileOpen,然后AVIFileGetStream。
可以通过AVIStreamAddRef来增加stream接口的引用。
通过AVIStreamRelease函数来关闭数据流。这个函数用来减少streams的引用计数,当计数减少为0时,删除。
5、从流中读取数据和信息
AVIStreamInfo函数可以获取数据的一些信息,该函数返回一个AVISTREAMINFO结构,该结构包含了数据的类型压缩方法,建议的buffersize,回放的rate,以及一些description。
如果数据流还有一些其它的额外的信息,你可以通过AVIStreamReadData函数来获取。应用程序分配一个
内存,传递给这个函数,然后这个函数会通过这个内存返回数据流的信息,额外的信息可能包括数据流的压缩和解压缩的方法,你可以通过AVIStreamDataSize宏来回去需要申请内存块的大小。
可以通过AVIStreamReadFormat函数获取数据流的格式信息。这个函数通过指定的内存返回数据流的格式信息,比如对于视频流,这个buffer包含了一个BIMAPINFO结构,对于音频流,内存块包含了WAVEFORMATEX或者PCMAVEFORMAT结构。你可以通过给AVIStreamReadFormat传递一个空buffer就可以获取buffer的大小。也可以通过AVIStreamFormatSize宏。
可以通过AVIStreamRead函数来返回多媒体的数据。这个函数将数据复制到应用程序提供的内存中,对于视频流,这个函数返回图像祯,对于音频流,这个函数返回音频的sample数据。可以通过给AVIStreamRead传递一个NULL的buffer来获取需要的buffer的大小。也可以通过AVIStreamSampleSize宏来获取buffer的大小。
有些AVI数据流句柄可能需要在启动数据流的前要做一下准备工作,此时,我们可以调用AVIStreamBeginStreaming函数来告知AVI数据流handle来申请分配它需要的一些资源。在完毕后,调用AVIStreamEndStreamming函数来释放资源。
6、操作压缩的视频数据
如果你要演示一祯或者几祯压缩视频图像时,你可以调用AVIStreamRead函数,将获取的数据传递给DrawDib函数来显示图像。这些函数可以显示压缩和未压缩的图像。
AVIFile也提供了一个函数AVIStreamGetFrameOpen,来获取未压缩的视频祯,这个函数创建了内存来获取未压缩的数据。也可以通过AVIStreamGetFrame函数来解压缩一个单独的视频祯。这个函数可以解压缩某一祯图像,然后将数据以一个BIMAPINFOHEADER结构返回。当你调用完AVIStreamGetFrame函数后,要调用AVIStreamGetFrameClose函数释放上一个函数申请的资源。
7、根据已存在的数据流创建文件
创建一个包含多个数据流的文件的方法就是整合多个数据流,将其写入一个新文件。这些数据流可以是内存中的数据,也可以是存在于另一个文件中。
我们可以用AVISave这个函数来build一个文件。这个函数可以创建一个文件,并且将指定的多个数据流按照指定的顺序写入文件,你也可以通过AVISaveV函数来创建一个新的文件,这个函数的功能和AVISave的功能一样,主要区别是AVISaveV采用的数据流数组,而AVISave是单个的数据流,多次保存。
我们可以调用AVISaveOptions函数来显示一个对话框,可以让用户来选择压缩方式。
我们可以在调用AVISave和AVISaveV函数时指定一个回调函数,用来显示avi文件的生成进度,可以让用户随时地取消生成avi文件。
我们可以调用GetSaveFileNamePreview函数来显示保存的对话框让用户选择保存的文件名。
通过AVIMakeFileFromStreams函数我们可以创建一个虚拟的文件句柄,其他的avi函数可以通过这个虚拟的文件句柄来操作文件中的数据流,操作完毕要记得调用AVIFileRelease释放。
8、向文件写入一个数据流
我们可以通过AVIFileCreateStream函数来在一个新文件或者已经存在的文件中创建一个数据流。这个函数根据AVISTREAMINFO结构定义了新的数据流,并为新的数据流创建一个接口,返回接口的指针。
在写入新的数据前,一定要指定流的格式信息,通过AVIStreamSetFormat函数,当设置一个视频流的时候,一定要使用BIMAPINFO结构来设置,音频就用WAVEFORMAT。
然后我们就可以通过AVIStreamWrite函数将我们的多媒体数据写入数据流了。这个函数将应用程序提供的内存数据复制到指定的流。缺省的avi handler将数据写入流的最后。
如果你有其他额外的信息需要写入流,你可以调用AVIFileWriteData或者AVIStreamWriteData,最后记得在完成数据写入后,要调用AVIStreamRelease。
9、数据流中的祯的位置
寻找起始祯:
可以通过AVIStreamStart函数来获取第一祯包含的sample number。也可以通过AVIStreamInfo函数来获取这个信息,这个函数的AVISTREAMINFO结构中包含了dwStart,可以通过AVIStreamStartTime宏来获取第一个sample。
可以通过AVIStreamLength函数来获取流的长度。这个函数返回流中的sample的数目。也可以通过AVIStreamInfo函数来获取这些信息,可以通过AVIStreamLengthTime宏来获取流的长度,毫秒。
在视频流中,一个sample对应着一祯图像,所以,有时这些sample中没有视频数据,如果你调用AVIStreamRead函数来数据,可能返回NULL,也可以通过AVIStreamFindSample通过指定FIND_ANY标志来查找指定的sample。
查找关键祯
通过AVIStreamFindSample函数查找符合要寻找的sample,然后可以通过下面的宏判断是否关键祯。
在time和sample间互相切换。
AVIStreamSampleToTime这个函数可以将smaple转换成毫秒。对于视频,这个值代表的是这个祯开始播放的时间。
在了解了上面的知识后,我们对avi的文件结构以及如何操作avi文件心里就明白了,下面我们可以开始我们的编程了。我们要做两件事情:
1、如何将一组静态的bmp位图合成一个avi的视频文件;
2、如何将一个未压缩的avi文件解析成一幅幅位图。
示例程序界面如下:
下面的函数演示了如何将一个文件夹下面的所有bmp文件都保存为一个avi文件,函数的第一个参数是要生成的AVI的文件名,第二个参数是存放bmp文件的文件夹名,这个函数会枚举该文件夹下的所有bmp文件,合成一个AVI文件。
void Cbmp2aviDlg::AVItoBmp(CString strAVIFileName, CString strBmpDir)
{
// TODO: 在此添加控件通知处理程序代码
AVIFileInit();
PAVIFILE avi;
int res=AVIFileOpen(&avi, strAVIFileName, OF_READ, NULL);
int n = GetLastError();
if (res!=AVIERR_OK)
{
//an error occures
if (avi!=NULL)
AVIFileRelease(avi);
return ;
}
AVIFILEINFO avi_info;
AVIFileInfo(avi, &avi_info, sizeof(AVIFILEINFO));
PAVISTREAM pStream;
res=AVIFileGetStream(avi, &pStream, streamtypeVIDEO /*video stream*/,
0 /*first stream*/);
if (res!=AVIERR_OK)
{
if (pStream!=NULL)
AVIStreamRelease(pStream);
AVIFileExit();
return ;
}
//do some task with the stream
int iNumFrames;
int iFirstFrame;
iFirstFrame=AVIStreamStart(pStream);
if (iFirstFrame==-1)
{
//Error getteing the frame inside the stream
if (pStream!=NULL)
AVIStreamRelease(pStream);
AVIFileExit();
return ;
}
iNumFrames=AVIStreamLength(pStream);
if (iNumFrames==-1)
{
//Error getteing the number of frames inside the stream
if (pStream!=NULL)
AVIStreamRelease(pStream);
AVIFileExit();
return ;
}
//getting bitmap from frame
BITMAPINFOHEADER bih;
ZeroMemory(&bih, sizeof(BITMAPINFOHEADER));
bih.biBitCount=24; //24 bit per pixel
bih.biClrImportant=0;
bih.biClrUsed = 0;
bih.biCompression = BI_RGB;
bih.biPlanes = 1;
bih.biSize = 40;
bih.biXPelsPerMeter = 0;
bih.biYPelsPerMeter = 0;
//calculate total size of RGBQUAD scanlines (DWORD aligned)
bih.biSizeImage = (((bih.biWidth * 3) + 3) & 0xFFFC) * bih.biHeight ;
PGETFRAME pFrame;
pFrame=AVIStreamGetFrameOpen(pStream, NULL );
AVISTREAMINFO streaminfo;
AVIStreamInfo(pStream,&streaminfo,sizeof(AVISTREAMINFO));
//Get the first frame
BITMAPINFOHEADER bih2;
long lsize = sizeof(bih2);
int index=0;
for (int i=iFirstFrame; i<iNumFrames; i++)
{
index= i-iFirstFrame;
BYTE* pDIB = (BYTE*) AVIStreamGetFrame(pFrame, index); //
AVIStreamReadFormat(pStream,index,&bih2,&lsize);
BITMAPFILEHEADER stFileHdr;
BYTE* Bits=new BYTE[bih2.biSizeImage];
AVIStreamRead(pStream,index,1,Bits,bih2.biSizeImage,NULL,NULL);
//RtlMoveMemory(Bits, pDIB + sizeof(BITMAPINFOHEADER), bih2.biSizeImage);
bih2.biClrUsed =0;
stFileHdr.bfOffBits=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);
stFileHdr.bfSize=sizeof(BITMAPFILEHEADER);
stFileHdr.bfType=0x4d42;
CString FileName;
FileName.Format("Frame-%05d.bmp", index);
CString strtemp = strBmpDir;
strtemp += "\\";
strtemp += FileName;
FILE* fp=_tfopen(strtemp ,_T("wb"));
fwrite(&stFileHdr,1,sizeof(BITMAPFILEHEADER),fp);
fwrite(&bih2,1,sizeof(BITMAPINFOHEADER),fp);
int ff = fwrite(Bits,1,bih2.biSizeImage,fp);
int e = GetLastError();
fclose(fp);
/////
delete Bits;
//CreateFromPackedDIBPointer(pDIB, index);
}
AVIStreamGetFrameClose(pFrame);
//close the stream after finishing the task
if (pStream!=NULL)
AVIStreamRelease(pStream);
AVIFileExit();
} |
下面的这个函数演示了如何将AVI文件中的每一桢图像单独取出来,保存为bmp文件。函数的头一个参数是avi文件名,第二个参数是存放bmp文件的文件夹。
//生成avi
void Cbmp2aviDlg::BMPtoAVI(CString szAVIName, CString strBmpDir)
{
CFileFind finder;
strBmpDir += _T("\\*.*");
AVIFileInit();
AVISTREAMINFO strhdr;
PAVIFILE pfile;
PAVISTREAM ps;
int nFrames =0;
HRESULT hr;
BOOL bFind = finder.FindFile(strBmpDir);
while(bFind)
{
bFind = finder.FindNextFile();
if(!finder.IsDots() && !finder.IsDirectory())
{
CString str = finder.GetFilePath();
FILE *fp = fopen(str,"rb");
BITMAPFILEHEADER bmpFileHdr;
BITMAPINFOHEADER bmpInfoHdr;
fseek( fp,0,SEEK_SET);
fread(&bmpFileHdr,sizeof(BITMAPFILEHEADER),1, fp);
fread(&bmpInfoHdr,sizeof(BITMAPINFOHEADER),1, fp);
BYTE *tmp_buf = NULL;
if(nFrames ==0 )
{
AVIFileOpen(&pfile,szAviName,OF_WRITE | OF_CREATE,NULL);
_fmemset(&strhdr, 0, sizeof(strhdr));
strhdr.fccType = streamtypeVIDEO;// stream type
strhdr.fccHandler = 0;
strhdr.dwScale = 1;
strhdr.dwRate = 15; // 15 fps
strhdr.dwSuggestedBufferSize = bmpInfoHdr.biSizeImage ;
SetRect(&strhdr.rcFrame, 0, 0, bmpInfoHdr.biWidth, bmpInfoHdr.biHeight);
// And create the stream;
hr = AVIFileCreateStream(pfile,&ps,&strhdr);
// hr = AVIStreamSetFormat(ps,nFrames,&bmpInfoHdr,sizeof(bmpInfoHdr));
}
tmp_buf = new BYTE[bmpInfoHdr.biWidth * bmpInfoHdr.biHeight * 3];
fread(tmp_buf, 1, bmpInfoHdr.biWidth * bmpInfoHdr.biHeight * 3, fp);
hr = AVIStreamSetFormat(ps,nFrames,&bmpInfoHdr,sizeof(bmpInfoHdr));
hr = AVIStreamWrite(ps, // stream pointer
nFrames , // time of this frame
1, // number to write
(LPBYTE) tmp_buf,
bmpInfoHdr.biSizeImage , // size of this frame
AVIIF_KEYFRAME, // flags....
NULL,
NULL);
nFrames ++;
fclose(fp);
}
}
AVIStreamClose(ps);
if(pfile != NULL)
AVIFileRelease(pfile);
AVIFileExit();
} |
结束语:
以上代码在 vc 6.0 和windows xp平台调试通过。这两个函数你可以直接在你的程序中使用,更详细的代码可以参见随着本文附上的示例源码。这里我要指出的是,这个AVI文件和bmp互相转换过程中,avi中的视频数据都是存放的是没有压缩的数据,如果你要分解AVI文件是经过压缩编码,比如,DVSD,MPEG4编码,首先你要采用相应的解码器对视频数据解码,然后将解码过的数据保存为bmp文件。好了,关于avi文件的介绍就到这里结束了。
posted @
2007-10-24 10:30 石正 阅读(7491) |
评论 (16) |
编辑 收藏
摘要:本文详细的解析了AVI文件的存储结构,介绍了微软提供的用来操作AVI文件的一组API使用方法,并通过例子代码,演示了如何将一组静态Bmp图片合成一个avi视频文件以及如何将一个avi视频文件解析保存为一系列的bmp图像文件。
关键词:avi文件 bmp图像 vc
AVI是音频视频交错(Audio Video Interleaved)的英文缩写,它是Microsoft公司开发的一种符合RIFF文件规范的数字音频与视频文件格式,原先用于Microsoft Video for Windows (简称VFW)环境,现在已被Windows 95/98、OS/2等多数操作系统直接支持。AVI格式允许视频和音频交错在一起同步播放,支持256色和RLE压缩,但AVI文件并未限定压缩标准,因此,AVI文件格式只是作为控制界面上的标准,不具有兼容性,用不同压缩算法生成的AVI文件,必须使用相应的解压缩算法才能播放出来。常用的AVI播放驱动程序,主要是Microsoft Video for Windows或Windows 95/98中的Video 1,以及Intel公司的Indeo Video。
在介绍AVI文件前,我们要先来看看RIFF文件结构。AVI文件采用的是RIFF文件结构方式,RIFF(Resource Interchange File Format,资源互换文件格式)是微软公司定义的一种用于管理windows环境中多媒体数据的文件格式,波形音频wave,MIDI和数字视频AVI都采用这种格式存储。构造RIFF文件的基本单元叫做数据块(Chunk),每个数据块包含3个部分,
1 4字节的数据块标记(或者叫做数据块的ID)
2 数据块的大小
3 数据
整个RIFF文件可以看成一个数据块,其数据块ID为RIFF,称为RIFF块。一个RIFF文件中只允许存在一个RIFF块。RIFF块中包含一系列的子块,其中有一种字块的ID为“LIST”,称为LIST,LIST块中可以再包含一系列的子块,但除了LIST块外的其他所有的子块都不能再包含子块。
RIFF和LIST块分别比普通的数据块多一个被称为形式类型(Form Type)和列表类型(List Type)的数据域,其组成如下:
1 4字节的数据块标记(Chunk ID)
2 数据块的大小
3 4字节的形式类型或者列表类型
4 数据
下面我们看看AVI文件的结构。AVI文件是目前使用的最复杂的RIFF文件,它能同时存储同步表现的音频视频数据。AVI的RIFF块的形式类型是AVI,它包含3个子块,如下所述:
1信息块,一个ID为”hdrl”的LIST块,定义AVI文件的数据格式。
2数据块,一个ID为 “movi”的LIST块,包含AVI的音视频序列数据
3索引块,ID为 “idxl”的子块,定义 “movi”LIST块的索引数据,是可选块。
AVI文件的结构如下图所示,下面将具体介绍AVI文件的各子块构造。
1信息块,信息块包含两个子块,即一个ID为 avih 的子块和一个ID 为 strl 的LIST块。
“avih”子块的内容可由如下的结构定义:
typedef struct
{
DWORD dwMicroSecPerFrame ; //显示每桢所需的时间ns,定义avi的显示速率
DWORD dwMaxBytesPerSec; // 最大的数据传输率
DWORD dwPaddingGranularity; //记录块的长度需为此值的倍数,通常是2048
DWORD dwFlages; //AVI文件的特殊属性,如是否包含索引块,音视频数据是否交叉存储
DWORD dwTotalFrame; //文件中的总桢数
DWORD dwInitialFrames; //说明在开始播放前需要多少桢
DWORD dwStreams; //文件中包含的数据流种类
DWORD dwSuggestedBufferSize; //建议使用的缓冲区的大小,
//通常为存储一桢图像以及同步声音所需要的数据之和
DWORD dwWidth; //图像宽
DWORD dwHeight; //图像高
DWORD dwReserved[4]; //保留值
}MainAVIHeader;
“strl” LIST块用于记录AVI数据流,每一种数据流都在该LIST块中占有3个子块,他们的ID分别是”strh”,”strf”, “strd”;
“strh”子块由如下结构定义,
typedef struct
{
FOURCC fccType; //4字节,表示数据流的种类 vids 表示视频数据流
//auds 音频数据流
FOURCC fccHandler;//4字节 ,表示数据流解压缩的驱动程序代号
DWORD dwFlags; //数据流属性
WORD wPriority; //此数据流的播放优先级
WORD wLanguage; //音频的语言代号
DWORD dwInitalFrames;//说明在开始播放前需要多少桢
DWORD dwScale; //数据量,视频每桢的大小或者音频的采样大小
DWORD dwRate; //dwScale /dwRate = 每秒的采样数
DWORD dwStart; //数据流开始播放的位置,以dwScale为单位
DWORD dwLength; //数据流的数据量,以dwScale为单位
DWORD dwSuggestedBufferSize; //建议缓冲区的大小
DWORD dwQuality; //解压缩质量参数,值越大,质量越好
DWORD dwSampleSize; //音频的采样大小
RECT rcFrame; //视频图像所占的矩形
}AVIStreamHeader;
“strf”子块紧跟在”strh”子块之后,其结构视”strh”子块的类型而定,如下所述;
如果 strh子块是视频数据流,则 strf子块的内容是一个与windows设备无关位图的BIMAPINFO结构,如下
typedef struct tagBITMAPINFO
{
BITMAPINFOHEADER bmiHeader;
RGBQUAD bmiColors[1]; //颜色表
}BITMAPINFO;
typedef struct tagBITMAPINFOHEADER
{
DWORD biSize;
LONG biWidth;
LONG biHeight;
WORD biPlanes;
WORD biBitCount;
DWORD biCompression;
DWORD biSizeImage;
LONG biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
}BITMAPINFOHEADER;
如果 strh子块是音频数据流,则strf子块的内容是一个WAVEFORMAT结构,如下
typedef struct
{
WORD wFormatTag;
WORD nChannels; //声道数
DWORD nSamplesPerSec; //采样率
DWORD nAvgBytesPerSec; //WAVE声音中每秒的数据量
WORD nBlockAlign; //数据块的对齐标志
WORD biSize; //此结构的大小
}WAVEFORMAT
“strd”子块紧跟在strf子块后,存储供压缩驱动程序使用的参数,不一定存在,也没有固定的结构。
“strl” LIST块定义的AVI数据流依次将 “hdrl “ LIST 块中的数据流头结构与”movi” LIST块中的数据联系在一起,第一个数据流头结构用于数据流0,第二个用于数据流1,依次类推。
数据块中存储视频和音频数据流,数据可直接存于 “movi” LIST块中。数据块中音视频数据按不同的字块存放,其结构如下所述,
音频字块
“##wb”
Wave 数据流
视频子块中存储DIB数据,又分为压缩或者未压缩DIB,
“##db”
RGB数据流
“##dc”
压缩的图像数据流
看到了吧,avi文件的图像数据可以是压缩的,和非压缩格式的。对于压缩格式来说,也可采用不同的编码,也许你曾经遇到有些avi没法识别,就是因为编码方式不一样,如果没有相应的解码,你就没法识别视频数据。AVI的编码方式有很多种,比较常见的有 mpeg2,mpeg4, divx等。
索引块,索引快包含数据块在文件中的位置索引,能提高avi文件的读写速度,其中存放着一组AVIINDEXENTRY结构数据。如下,这个块并不是必需的,也许不存在。
typedef struct
{
DWORD ckid; //记录数据块中子块的标记
DWORD dwFlags; //表示chid所指子块的属性
DWORD dwChunkOffset; //子块的相对位置
DWORD dwChunkLength; //子块长度
};
Ok,现在我相信你肯定会对AVI的文件结构已经很清楚了,在介绍完了AVI文件结构后,我们就来看看如何对avi文件进行读写了,为了对avi进行读写,微软提供了一套API,总共50个函数,他们的用途主要有两类,一个是avi文件的操作,一类是数据流streams的操作。
1 打开和关闭文件
AVIFileOpen ,AVIFileAddRef, AVIFileRelease
2从文件中读取文件信息
通过AVIFileInfo可以获取avi文件的一些信息,这个函数返回一个AVIFILEINFO结构
通过AVIFileReadData可以用来获取AVIFileInfo函数得不到的信息。这些信息也许不包含在文件的头部,比如拥有file的公司和个人的名称。
3写入文件信息
可以通过AVIFileWriteData函数来写入文件的一些额外信息。
4打开和关闭一个流
打开一个数据流就跟打开文件一样,你可以通过 AVIFileGetStream函数来打开一个数据流,这个函数创建了一个流的接口,然后在该接口中保存了一个句柄。
如果你想操作文件的某一个单独的流,你可以采用AVIStreamOpenFromFile函数,这个函数综合了AVIFileOpen和AVIFileGetStream函数。
如果你想操作文件中的多个数据流,你就要首先AVIFileOpen,然后AVIFileGetStream。
可以通过AVIStreamAddRef来增加stream接口的引用。
通过AVIStreamRelease函数来关闭数据流。这个函数用来减少streams的引用计数,当计数减少为0时,删除。
5从流中读取数据和信息
AVIStreamInfo函数可以获取数据的一些信息,该函数返回一个AVISTREAMINFO结构,该结构包含了数据的类型压缩方法,建议的buffersize,回放的rate,以及一些description。
如果数据流还有一些其它的额外的信息,你可以通过AVIStreamReadData函数来获取。应用程序分配一个内存,传递给这个函数,然后这个函数会通过这个内存返回数据流的信息,额外的信息可能包括数据流的压缩和解压缩的方法,你可以通过AVIStreamDataSize宏来回去需要申请内存块的大小。
可以通过AVIStreamReadFormat函数获取数据流的格式信息。这个函数通过指定的内存返回数据流的格式信息,比如对于视频流,这个buffer包含了一个BIMAPINFO结构,对于音频流,内存块包含了WAVEFORMATEX或者PCMAVEFORMAT结构。你可以通过给AVIStreamReadFormat传递一个空buffer就可以获取buffer的大小。也可以通过AVIStreamFormatSize宏。
可以通过AVIStreamRead函数来返回多媒体的数据。这个函数将数据复制到应用程序提供的内存中,对于视频流,这个函数返回图像祯,对于音频流,这个函数返回音频的sample数据。可以通过给AVIStreamRead传递一个NULL的buffer来获取需要的buffer的大小。也可以通过AVIStreamSampleSize宏来获取buffer的大小。
有些AVI数据流句柄可能需要在启动数据流的前要做一下准备工作,此时,我们可以调用AVIStreamBeginStreaming函数来告知AVI数据流handle来申请分配它需要的一些资源。在完毕后,调用AVIStreamEndStreamming函数来释放资源。
6操作压缩的视频数据
如果你要演示一祯或者几祯压缩视频图像时,你可以调用AVIStreamRead函数,将获取的数据传递给DrawDib函数来显示图像。这些函数可以显示压缩和未压缩的图像。
AVIFile也提供了一个函数AVIStreamGetFrameOpen,来获取未压缩的视频祯,这个函数创建了内存来获取未压缩的数据。也可以通过AVIStreamGetFrame函数来解压缩一个单独的视频祯。这个函数可以解压缩某一祯图像,然后将数据以一个BIMAPINFOHEADER结构返回。当你调用完AVIStreamGetFrame函数后,要调用AVIStreamGetFrameClose函数释放上一个函数申请的资源。
7根据已存在的数据流创建文件
创建一个包含多个数据流的文件的方法就是整合多个数据流,将其写入一个新文件。这些数据流可以是内存中的数据,也可以是存在于另一个文件中。
我们可以用AVISave这个函数来build一个文件。这个函数可以创建一个文件,并且将指定的多个数据流按照指定的顺序写入文件,你也可以通过AVISaveV函数来创建一个新的文件,这个函数的功能和AVISave的功能一样,主要区别是AVISaveV采用的数据流数组,而AVISave是单个的数据流,多次保存。
我们可以调用AVISaveOptions函数来显示一个对话框,可以让用户来选择压缩方式。
我们可以在调用AVISave和AVISaveV函数时指定一个回调函数,用来显示avi文件的生成进度,可以让用户随时地取消生成avi文件。
我们可以调用GetSaveFileNamePreview函数来显示保存的对话框让用户选择保存的文件名。
通过AVIMakeFileFromStreams函数我们可以创建一个虚拟的文件句柄,其他的avi函数可以通过这个虚拟的文件句柄来操作文件中的数据流,操作完毕要记得调用AVIFileRelease释放。
8向文件写入一个数据流
我们可以通过AVIFileCreateStream函数来在一个新文件或者已经存在的文件中创建一个数据流。这个函数根据AVISTREAMINFO结构定义了新的数据流,并为新的数据流创建一个接口,返回接口的指针。
在写入新的数据前,一定要指定流的格式信息,通过AVIStreamSetFormat函数,当设置一个视频流的时候,一定要使用BIMAPINFO结构来设置,音频就用WAVEFORMAT。
然后我们就可以通过AVIStreamWrite函数将我们的多媒体数据写入数据流了。这个函数将应用程序提供的内存数据复制到指定的流。缺省的avi handler将数据写入流的最后。
如果你有其他额外的信息需要写入流,你可以调用AVIFileWriteData或者AVIStreamWriteData,
最后记得在完成数据写入后,要调用AVIStreamRelease。
9数据流中的祯的位置
寻找起始祯:
可以通过AVIStreamStart函数来获取第一祯包含的sample number。也可以通过AVIStreamInfo函数来获取这个信息,这个函数的AVISTREAMINFO结构中包含了dwStart,可以通过AVIStreamStartTime宏来获取第一个sample。
可以通过AVIStreamLength函数来获取流的长度。这个函数返回流中的sample的数目。也可以通过AVIStreamInfo函数来获取这些信息,可以通过AVIStreamLengthTime宏来获取流的长度,毫秒。
在视频流中,一个sample对应着一祯图像,所以,有时这些sample中没有视频数据,如果你调用AVIStreamRead函数来数据,可能返回NULL,也可以通过AVIStreamFindSample通过指定FIND_ANY标志来查找指定的sample。
查找关键祯
通过AVIStreamFindSample函数查找符合要寻找的sample,然后可以通过下面的宏判断是否关键祯。
在time和sample间互相切换
AVIStreamSampleToTime这个函数可以将smaple转换成毫秒。对于视频,这个值代表的是这个祯开始播放的时间。
在了解了上面的知识后,我们对avi的文件结构以及如何操作avi文件心里就明白了,下面我们可以开始我们的编程了。我们要做两件事情,1如何将一组静态的bmp位图合成一个avi的视频文件,2 如何将一个未压缩的avi文件解析成一幅幅位图。
示例程序界面如下:
下面的函数演示了如何将一个文件夹下面的所有bmp文件都保存为一个avi文件,函数的第一个参数是要生成的AVI的文件名,第二个参数是存放bmp文件的文件夹名,这个函数会枚举该文件夹下的所有bmp文件,合成一个AVI文件。
void Cbmp2aviDlg::AVItoBmp(CString strAVIFileName, CString strBmpDir)
{
// TODO: 在此添加控件通知处理程序代码
AVIFileInit();
PAVIFILE avi;
int res=AVIFileOpen(&avi, strAVIFileName, OF_READ, NULL);
int n = GetLastError();
if (res!=AVIERR_OK)
{
//an error occures
if (avi!=NULL)
AVIFileRelease(avi);
return ;
}
AVIFILEINFO avi_info;
AVIFileInfo(avi, &avi_info, sizeof(AVIFILEINFO));
PAVISTREAM pStream;
res=AVIFileGetStream(avi, &pStream, streamtypeVIDEO /*video stream*/,
0 /*first stream*/);
if (res!=AVIERR_OK)
{
if (pStream!=NULL)
AVIStreamRelease(pStream);
AVIFileExit();
return ;
}
//do some task with the stream
int iNumFrames;
int iFirstFrame;
iFirstFrame=AVIStreamStart(pStream);
if (iFirstFrame==-1)
{
//Error getteing the frame inside the stream
if (pStream!=NULL)
AVIStreamRelease(pStream);
AVIFileExit();
return ;
}
iNumFrames=AVIStreamLength(pStream);
if (iNumFrames==-1)
{
//Error getteing the number of frames inside the stream
if (pStream!=NULL)
AVIStreamRelease(pStream);
AVIFileExit();
return ;
}
//getting bitmap from frame
BITMAPINFOHEADER bih;
ZeroMemory(&bih, sizeof(BITMAPINFOHEADER));
bih.biBitCount=24; //24 bit per pixel
bih.biClrImportant=0;
bih.biClrUsed = 0;
bih.biCompression = BI_RGB;
bih.biPlanes = 1;
bih.biSize = 40;
bih.biXPelsPerMeter = 0;
bih.biYPelsPerMeter = 0;
//calculate total size of RGBQUAD scanlines (DWORD aligned)
bih.biSizeImage = (((bih.biWidth * 3) + 3) & 0xFFFC) * bih.biHeight ;
PGETFRAME pFrame;
pFrame=AVIStreamGetFrameOpen(pStream, NULL );
AVISTREAMINFO streaminfo;
AVIStreamInfo(pStream,&streaminfo,sizeof(AVISTREAMINFO));
//Get the first frame
BITMAPINFOHEADER bih2;
long lsize = sizeof(bih2);
int index=0;
for (int i=iFirstFrame; i<iNumFrames; i++)
{
index= i-iFirstFrame;
BYTE* pDIB = (BYTE*) AVIStreamGetFrame(pFrame, index); //
AVIStreamReadFormat(pStream,index,&bih2,&lsize);
BITMAPFILEHEADER stFileHdr;
BYTE* Bits=new BYTE[bih2.biSizeImage];
AVIStreamRead(pStream,index,1,Bits,bih2.biSizeImage,NULL,NULL);
//RtlMoveMemory(Bits, pDIB + sizeof(BITMAPINFOHEADER), bih2.biSizeImage);
bih2.biClrUsed =0;
stFileHdr.bfOffBits=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);
stFileHdr.bfSize=sizeof(BITMAPFILEHEADER);
stFileHdr.bfType=0x4d42;
CString FileName;
FileName.Format("Frame-%05d.bmp", index);
CString strtemp = strBmpDir;
strtemp += "\\";
strtemp += FileName;
FILE* fp=_tfopen(strtemp ,_T("wb"));
fwrite(&stFileHdr,1,sizeof(BITMAPFILEHEADER),fp);
fwrite(&bih2,1,sizeof(BITMAPINFOHEADER),fp);
int ff = fwrite(Bits,1,bih2.biSizeImage,fp);
int e = GetLastError();
fclose(fp);
/////
delete Bits;
//CreateFromPackedDIBPointer(pDIB, index);
}
AVIStreamGetFrameClose(pFrame);
//close the stream after finishing the task
if (pStream!=NULL)
AVIStreamRelease(pStream);
AVIFileExit();
}
下面的这个函数演示了如何将AVI文件中的每一桢图像单独取出来,保存为bmp文件。函数的头一个参数是avi文件名,第二个参数是存放bmp文件的文件夹。
//生成avi
void Cbmp2aviDlg::BMPtoAVI(CString szAVIName, CString strBmpDir)
{
CFileFind finder;
strBmpDir += _T("\\*.*");
AVIFileInit();
AVISTREAMINFO strhdr;
PAVIFILE pfile;
PAVISTREAM ps;
int nFrames =0;
HRESULT hr;
BOOL bFind = finder.FindFile(strBmpDir);
while(bFind)
{
bFind = finder.FindNextFile();
if(!finder.IsDots() && !finder.IsDirectory())
{
CString str = finder.GetFilePath();
FILE *fp = fopen(str,"rb");
BITMAPFILEHEADER bmpFileHdr;
BITMAPINFOHEADER bmpInfoHdr;
fseek( fp,0,SEEK_SET);
fread(&bmpFileHdr,sizeof(BITMAPFILEHEADER),1, fp);
fread(&bmpInfoHdr,sizeof(BITMAPINFOHEADER),1, fp);
BYTE *tmp_buf = NULL;
if(nFrames ==0 )
{
AVIFileOpen(&pfile,szAviName,OF_WRITE | OF_CREATE,NULL);
_fmemset(&strhdr, 0, sizeof(strhdr));
strhdr.fccType = streamtypeVIDEO;// stream type
strhdr.fccHandler = 0;
strhdr.dwScale = 1;
strhdr.dwRate = 15; // 15 fps
strhdr.dwSuggestedBufferSize = bmpInfoHdr.biSizeImage ;
SetRect(&strhdr.rcFrame, 0, 0, bmpInfoHdr.biWidth, bmpInfoHdr.biHeight);
// And create the stream;
hr = AVIFileCreateStream(pfile,&ps,&strhdr);
// hr = AVIStreamSetFormat(ps,nFrames,&bmpInfoHdr,sizeof(bmpInfoHdr));
}
tmp_buf = new BYTE[bmpInfoHdr.biWidth * bmpInfoHdr.biHeight * 3];
fread(tmp_buf, 1, bmpInfoHdr.biWidth * bmpInfoHdr.biHeight * 3, fp);
hr = AVIStreamSetFormat(ps,nFrames,&bmpInfoHdr,sizeof(bmpInfoHdr));
hr = AVIStreamWrite(ps, // stream pointer
nFrames , // time of this frame
1, // number to write
(LPBYTE) tmp_buf,
bmpInfoHdr.biSizeImage , // size of this frame
AVIIF_KEYFRAME, // flags....
NULL,
NULL);
nFrames ++;
fclose(fp);
}
}
AVIStreamClose(ps);
if(pfile != NULL)
AVIFileRelease(pfile);
AVIFileExit();
}
结束语:
以上代码在 vc 6.0 和windows xp平台调试通过。这两个函数你可以直接在你的程序中使用,更详细的代码可以参见随着本文附上的示例源码。这里我要指出的是,这个AVI文件和bmp互相转换过程中,avi中的视频数据都是存放的是没有压缩的数据,如果你要分解AVI文件是经过压缩编码,比如,DVSD,MPEG4编码,首先你要采用相应的解码器对视频数据解码,然后将解码过的数据保存为bmp文件。好了,关于avi文件的介绍就到这里结束了
posted @
2007-10-24 10:22 石正 阅读(683) |
评论 (0) |
编辑 收藏
VC++ 6.0
方法1:
CPen Pen( PS_SOLID, 1, RGB( 255, 0, 0 ) );
CPen* pOldPen;
pOldPen = pDC->SelectObject( &Pen );
方法2:
CPen Pen( PS_SOLID, 1, RGB( 255, 0, 255 ) );
CPen* pOldPen;
pOldPen = (CPen*)SelectObject( hDC, Pen.GetSafeHandle() );
posted @
2007-09-17 09:48 石正 阅读(3781) |
评论 (2) |
编辑 收藏
正确的格式应该是:Copyright [dates] by [author/owner]
通常可以代替Copyright, 但是不可以用(c)。 All Rights Reserved 在某些国家曾经是必须的,但是现在在大多数国家,都不是法律上必须有的字样。
参见下面几个正确的格式:
1995-2004 Macromedia, Inc. All rights reserved.
2004 Microsoft Corporation. All rights reserved.
Copyright 2004 Adobe Systems Incorporated. All rights reserved.
1995-2004 Eric A. and Kathryn S. Meyer. All Rights Reserved.
请注意标点符号和大小写的用法,这也是专业精神的一种体现。
现在流行some rights reserved:creativecommons.org
some rights reserved 和copyright 本身并不矛盾,但是其中的界限更多是一个道德问题,真正的保留一部分权力,是指给浏览者fair use 的权利,fair use的界定也决不是随便乱用,或者抄袭。
posted @
2007-09-10 16:15 石正 阅读(845) |
评论 (0) |
编辑 收藏
测试视频时的笑话:
HJ对LCY说:
你能不能把头发“竖(束)”起来。
posted @
2007-09-05 11:09 石正 阅读(266) |
评论 (0) |
编辑 收藏
使计算机具有感知到人是否存在的视觉功能,这项技术就是人脸检测
posted @
2007-08-28 10:16 石正 阅读(164) |
评论 (0) |
编辑 收藏
3-D histogram 三维直方图
posted @
2007-08-27 15:33 石正 阅读(277) |
评论 (0) |
编辑 收藏
使用电子邮件e_mail的礼节
1、使用电子邮件e_mail最容易被忽略的礼节是:回复邮件时,请把原邮件的内容带上,以方便别人查看;但,原邮件所携带的附件是不需要带上的;
2、写正文时,不需要段落首行缩进;
posted @
2007-07-30 16:34 石正 阅读(243) |
评论 (0) |
编辑 收藏
软件工程第零原则,简称第0原则:所有的软件工作项都有唯一的最佳解决方案。
这里,先定义几个名词:
1、最佳解决方案。比如:在需要使用“懒惰策略”的时候(优化后置),使用“懒惰策略”就是最佳方案;不使用就不是最佳方案。2种方案的区别,关键在于,开发的进度和风险,2种方案的成本是不一样的。
2、团队最佳解决方案:团队最佳解决方案有时也是最佳解决方案,是团队能够提出的最佳方案,虽然不是该问题的最佳方案,但该方案如果可以满足问题的需求,团队又有能力实施,同时,相比向外部请求协助的代价要低的多,此时,基本可以认定该方案是团队最佳方案。这种情况下,采用“团队最佳方案”是最好的解决方法,理由是:满足需求,开发成本低,风险小。
在长期的软件工作实践中,软件小组成员之间经常出现意见不一致的时候。
具体出现的情况分类如下:
1、所有人的意见都是最佳方案的;
2、有一个或者几个人的意见是最佳方案的,有几个或者一个人的意见是不太合适的,或者是错误的;
3、所有人的意见都不是最佳方案,但有一个或者几个人的意见是“团队最佳解决方案”;
4、所有人的意见都不合适,或者都没有解决方案;
出现意见不一致时,容易造成团队内部争执,或者使团队进入到其他低效状态;为了提高团队工作效率,降低开发成本,需要减少这类低效状态的出现,采用合理的方式,是可以减少这类状态的;正面解决问题是其中的一种较好的方式;
在实际工作中发现,正面解决问题是较好的方法;即找到当前的最佳方案,并且让团队成员经过谈论,形成一致意见,或者,在找不到最佳方案时,把该问题记录下来,后续解决,不影响当前工作;
采用这种工作方法的前提条件就是团队所有成员对“问题具有唯一正确的解决方案”的原则形成一致的认识:所有的软件工作项都有唯一正确的解决方案。由于该原则是软件工作的基本原则,所以,命名该原则为:“软件工程第零原则”,简称“第0原则”。
以下对该原则做说明:
1、软件工作中所有工作项都有最佳解决方案:由于,计算机理论基于数学,软件工作中的所有工作项的根本基础是数学,可以说软件工作中的所有问题都可以转化为相应的数学问题,而数学问题是有最佳解决方案的,所以,软件工作中的所有工作项都有最佳解决方案;(这里,还有,成本进度等的量化计算与管理);
2、不同解决方案合适程度是不一样的:有时,几个方案都可行,但,其中是有区别的,区别在于,风险和成本,具体来说在于工作量、工期、运行性能、团队技术熟练程度等;
3、该原则的用途:学习软件知识时,明白所有问题有最佳解决方案,避免一叶障目;团队工作时,减少无效讨论;
该随笔的观点不太成熟,多提宝贵意见。谢谢!
posted @
2007-07-22 19:34 石正 阅读(325) |
评论 (0) |
编辑 收藏
感知 = 感觉 + 知觉
上边的公式今天的收获,很高兴啊。
上边公式说的是:人类获得信息的方式。起关键在于把过程分为2步,可以在人工智能工作中参考。
具体是如下说的:
感觉获得外界的信息,知觉把感觉获得的信息加工成为“概念”,后续的思维,进一步处理。
posted @
2007-07-22 18:52 石正 阅读(208) |
评论 (0) |
编辑 收藏
我希望有个安静祥和的家庭,家和万事兴啊。
posted @
2007-07-20 09:17 石正 阅读(175) |
评论 (0) |
编辑 收藏