全部博文(187)
分类: LINUX
2009-09-14 12:23:26
EffecTV 是日本人设计的程序, 也是经由 video4linux 做影像撷取, 在 mmap 的程序码方面, EffecTV 会比较容易懂, 同时也可以借由 EffecTV 来学习一些影像处理的技巧。EffecTV 是一个可以支援特效功能的视讯软件, 是颇有趣的程序。
int video_grab_check(int palette);这些函数定义在 video.h 里。我们不再重覆介绍 video4linux 初始化的地方, 在 frame grab 方面, 呼叫 video_grabstart() 开始进行影像撷取的工作, 程序码如下:
int video_set_grabformat();
int video_grabstart();
int video_grabstop();
int video_syncframe();
int video_grabframe();
/* Start the continuous grabbing */其中主角是 v4lgrabstart() 函数, 这个函数被实作在 v4lutils/v4lutils.c 里, 程序码如下:
int video_grabstart()
{
vd.frame = 0;
if(v4lgrabstart(&vd, 0) < 0)
return -1;
if(v4lgrabstart(&vd, 1) < 0)
return -1;
return 0;
}
/*v4lgrabstart() 是利用 mmap 的方式来取得影像。v4lgrabstart() 也是利用 ioctl() 来完成这个低阶的动作, 与第本文第二篇实作 video4linux 时一样, 写法为:
* v4lgrabstart - activate mmap capturing
*
* vd: v4l device object
* frame: frame number for storing captured image
*/
int v4lgrabstart(v4ldevice *vd, int frame)
{
if(v4l_debug) fprintf(stderr, "v4lgrabstart: grab frame %d.\n",frame);
if(vd->framestat[frame]) {
fprintf(stderr, "v4lgrabstart: frame %d is already used to grab.\n", frame);
}
vd->mmap.frame = frame;
if(ioctl(vd->fd, VIDIOCMCAPTURE, &(vd->mmap)) < 0) {
v4lperror("v4lgrabstart:VIDIOCMCAPTURE");
return -1;
}
vd->framestat[frame] = 1;
return 0;
}
ioctl(vd->fd, VIDIOCMCAPTURE, &(vd->mmap));vd 里的 framestat 栏位主要是纪录目前的 frame 状态:
vd->framestat[frame]这个栏位定义在 v4lutils.h 里, 而利用 mmap 的方式我们需要两个 frame 来存放影像资料, 所以 framestat 宣告成二个元素的数组, 我们将 EffecTV 的 v4l 结构定义完整列出如下:
struct _v4ldevice请读者回头对应一下本文第二篇文章所实作的内容, EffecTV 的实作更为完整。当我们开始 grab 影像到其中一个 frame 时, 我们就把 frame 的状态设成 1:
{
int fd;
struct video_capability capability;
struct video_channel channel[10];
struct video_picture picture;
struct video_clip clip;
struct video_window window;
struct video_capture capture;
struct video_buffer buffer;
struct video_mmap mmap;
struct video_mbuf mbuf;
struct video_unit unit;
unsigned char *map;
pthread_mutex_t mutex;
int frame;
int framestat[2];
int overlay;
};
vd->framestat[frame] = 1;然后利用 v4lsync() 等待 frame 撷取完成, 利用 ioctl() 传入 VIDIOCSYNC 可以检查 frame 是否已经撷取完成:
if(ioctl(vd->fd, VIDIOCSYNC, &frame) < 0) {如果 frame 已撷取完成, 那么我们就将 frame 的状态设成 0, 表示目前这个 frame 并没有在做撷取的动作, 也因此在 v4lsync() 一开始的地方我们会先做这部份的检查:
v4lperror("v4lsync:VIDIOCSYNC");
return -1;
}
vd->framestat[frame] = 0;
return 0;
if(vd->framestat[frame] == 0) {v4lsync() 函数也是一个重要的函数, 程序码如下:
fprintf(stderr, "v4lsync: grabbing to frame %d is not started.\n", frame);
}
/*在 EffecTV 里则是要呼叫 video_syncframe() 函数来做 frame 等待的动作, 而 video_syncframe() 则会去呼叫 v4lsync() 函数。video_syncframe() 函数的原始码如下:
* v4lsync - wait until mmap capturing of the frame is finished
*
* vd: v4l device object
* frame: frame number
*/
int v4lsync(v4ldevice *vd, int frame)
{
if(v4l_debug) fprintf(stderr, "v4lsync: sync frame %d.\n",frame);
if(vd->framestat[frame] == 0) {
fprintf(stderr, "v4lsync: grabbing to frame %d is not started.\n", frame);
}
if(ioctl(vd->fd, VIDIOCSYNC, &frame) < 0) {
v4lperror("v4lsync:VIDIOCSYNC");
return -1;
}
vd->framestat[frame] = 0;
return 0;
}
int video_syncframe()frame 撷取实作
{
return v4lsyncf(&vd);
}
int lifeStart()lifeStart() 在完成一些初始化的设定工作后, 会呼叫 video_grabstart() 函数开始进行影像撷取。在 lifeDraw() 函数里, 则是呼叫 video_syncframe() 等待 frame 撷取完成后再做输出的动作。
{
screen_clear(0);
image_stretching_buffer_clear(0);
image_set_threshold_y(40);
field1 = field;
field2 = field + video_area;
clear_field();
if(video_grabstart())
return -1;
stat = 1;
return 0;
}
Y = 0.299R + 0.587G + 0.114BRGB 是由 R, G, B 三原色组成, 同理 YUV 是由 Y, U, V 三个元素组成。在 PAL 实作 U, V 我们使用的转换公式为:
U = B ━ Y
V = R ━ Y
I = 0.877(R-Y)cos33 ━ 0.492(B-Y)sin33
Q = 0.877(R-Y)sin33 + 0.492(B-Y)cos33
U = 0.492(B-Y)YIQ 则可以简化成转换矩阵:
V = 0.877(R-Y)
(手稿)JRTPLIB 的使用方法
RTP: A Transport Protocol for Real-Time Applications而在 RFC 1890 里, 对 RTP 的描述则是:
RTP Profile for Audio and Video Conferences with Minimal Control即然我们要利用 Video Streaming 来设计视讯会议方面的软件, 对于 RTP的讨论与研究则是必修功课之一。对视讯会议软件而言,RTP 也提供 Audio 部份的支援, 事实上, 任何与 real-time (即时)相关的话题都与 RTP 脱不了关系。
使用前请务必先阅读一下 JRTPLIB 的版权宣告。
#include "rtpsession.h"第一步我们要先把 rtpsession.h 给 include 进来:
int main(void)
{
RTPSession sess;
sess.Create(5000);
return 0;
}
#include "rtpsession.h"接下来再产生 RTPSession 类别的物件:
RTPSession sess;最后再建立 session 就完成最简单的初始动作了:
sess.Create(5000);Create() 成员函数接收一个 portbase 的参数, 指定 session 的 port, 接着开始初始化 timestamp 与 packet sequence number。RTPSession::Create() 程序码如下:
int RTPSession::Create(int localportbase,unsigned long localip)Create() 接着会再建立一个 SSRC:
{
int status;
if (initialized)
return ERR_RTP_SESSIONALREADYINITIALIZED;
if ((status = rtpconn.Create(localportbase,localip)) < 0)
return status;
if ((status = contribsources.CreateLocalCNAME()) < 0)
{
rtpconn.Destroy();
return status;
}
CreateNewSSRC();
localinf.CreateSessionParameters();
rtcpmodule.Initialize();
initialized = true;
return 0;
}
CreateNewSSRC();SSRC 为 local synchronization source identifier。
unsigned long addr = ntohl(inet_addr("127.0.0.1"));这里表示我们要将封包传送到 127.0.0.1 (本地端) 的 port 5000, 只要照着套用即可。
sess.AddDestination(addr,5000);
图 1 是 RTP 的封包档头格式, 整个档头分为 10 个栏位 (field)。
Payload type
编码标准
支援Audio或Video
Clock Rate (Hz)
2
G.721
A
8000
4
G.723
A
8000
7
LPC
A
8000
9
G.722
A
8000
15
G.728
A
8000
26
JPEG
V
90000
31
H.261
V
90000
34
H.263
V
90000
Linux IP Stacks Commentary, Stephen T. Satchell & H.B.J. Clifford, CoriolisOpen Press, ISBN 1-57610-470-2Linux 是网络操作系统, 而且 Linux 对于网络的支援也相当的完整, 包括2.4 系列 kernel 已经加入对 IPv6。 Linux kernel 与 module 提供的通讯层功能包括: