Ubuntu驱动USB采集卡(五)

解决画面”破损“很严重

测试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,解决问题。

  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 }