解决画面”破损“很严重
- 首先怀疑是某个线程处理不够及时,导致丢失某些数据
测试capture线程,每个while循环平均耗时40毫秒,与somagic-capture命令的man手册能对应上,也跟《Ubuntu驱动USB采集卡(一)》中实时查看的mplayer命令输出能对应上,所以应该没有问题。
测试record线程,每个while循环平均耗时40毫秒,如果跳过等待信号量的语句(while中第一句),平均耗时20毫秒,应该也没有问题。
测试show线程,直接关闭该线程,只用mplayer查看videofile(即“test.avi”),问题仍然存在。
所以怀疑被否定。
- 其次怀疑是逐行扫描/隔行扫描的影响
逐帧查看test.avi,发现出现问题的帧,局部区域有明显的横向扫描线,所以立即怀疑是否逐行/隔行的问题。
但是,同样以“sudo somagic-capture -i 1”作为捕获命令,通过管道符给mplayer时,画面就没有问题,给我的程序时,画面就有问题。
所以怀疑被否定。
- 偶然找到关键
将record线程与show线程的同步改为信号量。
查看somagic-capture的man手册,发现参数“–iso-transfers”,可以增加并发传输的性能,降低某些人工影响,默认是5,尝试设定其他数字。
另外注意到一个细节,之前被忽略了,采集卡输出视频720x576@25fps,手册里要求调整为720x540@25fps一遍保持比例,mplayer是改成了768x576@25fps。我遵照手册的建议。
经过尝试,6以上就能在我这里解决问题,为保险起见,我设定为10,解决问题。
- 源代码5
1 #include <iostream>
2 #include <opencv2/opencv.hpp>
3 #include <cstdio>
4 #include <pthread.h>
5 #include <semaphore.h>
6
7 const int BufBlock = 829440;
8 const int BufSize = 5;
9 unsigned char *buf = NULL;
10 unsigned int wbuf = 0;
11 unsigned int rbuf = 0;
12 pthread_mutex_t mutex_buf[BufSize];
13 sem_t sem_buf, sem_show;
14
15 cv::Mat yuv(576, 720, CV_8UC3, cv::Scalar(0));
16 cv::Mat frame(576, 720, CV_8UC3, cv::Scalar(0));
17 cv::Mat image(540, 720, CV_8UC3, cv::Scalar(0));
18
19 int counter;
20 char title[10];
21
22 cv::VideoWriter videofile;
23
24 void* capture(void* parameter) {
25 while (true) {
26 // double start = cv::getTickCount();
27
28 pthread_mutex_lock(mutex_buf + (wbuf % BufSize));
29
30 unsigned char *data = buf + (wbuf % BufSize) * BufSize;
31 for (int i = 0; i < BufBlock; ++i) {
32 data[i] = getchar();
33 }
34
35 pthread_mutex_unlock(mutex_buf + (wbuf % BufSize));
36
37 ++wbuf;
38
39 sem_post(&sem_buf);
40
41 // double stop = cv::getTickCount();
42 // std::cout << (stop - start) * 1000.0 / cv::getTickFrequency() << " ms" << std::endl;
43 }
44 }
45
46 void* record(void* parameter) {
47 while (true) {
48 sem_wait(&sem_buf);
49
50 // double start = cv::getTickCount();
51
52 // int cur = 0;
53 // sem_getvalue(&sem_buf, &cur);
54 // std::cout << "cur = " << cur << ", wbuf = " << wbuf << ", rbuf = " << rbuf << std::endl;
55
56 pthread_mutex_lock(mutex_buf + (rbuf % BufSize));
57
58 sprintf(title, "%d", ++counter);
59 unsigned char *data = buf + (rbuf % BufSize) * BufSize;
60
61 int row = 0;
62 int col = 0;
63 for (int i = 0; i < BufBlock; i = i + 4) {
64 yuv.at<cv::Vec3b>(row, col) = cv::Vec3b(data[i + 1], data[i], data[i + 2]); /// Y1, U1, V1
65 yuv.at<cv::Vec3b>(row, col + 1) = cv::Vec3b(data[i + 3], data[i], data[i + 2]); /// Y2, U2=U1, V2=V1
66
67 col = col + 2;
68 if (col >= 720) {
69 col = 0;
70 row = row + 1;
71 }
72 }
73
74 pthread_mutex_unlock(mutex_buf + (rbuf % BufSize));
75
76 cv::cvtColor(yuv, frame, CV_YUV2BGR);
77 cv::resize(frame, image, cv::Size(720, 540));
78 cv::putText(frame, title, cv::Point(0, 50), cv::FONT_HERSHEY_SIMPLEX, 1.0, cv::Scalar(255, 255 ,0));
79
80 videofile << image;
81
82 sem_post(&sem_show);
83
84 ++rbuf;
85
86 // double stop = cv::getTickCount();
87 // std::cout << (stop - start) * 1000.0 / cv::getTickFrequency() << " ms" << std::endl;
88 }
89 }
90
91 void* show(void* parameter) {
92 while (true) {
93 sem_wait(&sem_show);
94 cv::imshow("video", image);
95 cv::waitKey(1);
96 }
97 }
98
99 int main(int argc, char **argv) {
100 buf = new unsigned char [BufSize * BufBlock];
101 sem_init(&sem_buf, 0, 0);
102 pthread_t captureid, recordid, showid;
103 counter = 0;
104
105 /// 'D', 'I', 'V', 'X'
106 /// 'F', 'M', 'P', '4'
107 videofile = cv::VideoWriter("test.avi", CV_FOURCC('F', 'M', 'P', '4'), 25.0, cv::Size(720, 576));
108
109 int status = pthread_create(&captureid, NULL, capture, NULL);
110 if (status != 0) {
111 std::cerr << "不能创建capture线程: " << strerror(status);
112 return 0;
113 }
114
115 status = pthread_create(&recordid, NULL, record, NULL);
116 if (status != 0) {
117 std::cerr << "不能创建record线程: " << strerror(status);
118 return 0;
119 }
120
121 status = pthread_create(&showid, NULL, show, NULL);
122 if (status != 0) {
123 std::cerr << "不能创建show线程: " << strerror(status);
124 return 0;
125 }
126
127 pthread_join(recordid, (void **)0);
128 pthread_join(captureid, (void **)0);
129 pthread_join(showid, (void **)0);
130
131 return 0;
132 }