用過QQ影音(或其他類似播放器)的可能都知道,QQ影音有一個功能叫“劇情連拍”,可以對一部影片在不同時段進行截圖,然後把這些截圖放在一張并生成單獨的圖檔。通過劇情圖,可以一目了然的看到整部影片的畫面風格,也就能大緻猜出這部影片的類型,再加上字幕,甚至可以大概猜出影片的故事梗概。本人前些日子針對這個功能做了一些研究,小有成果,先上兩張效果圖:
1.
2. 怎麼樣,效果還可以吧?要實作這種程式需要完成以下幾種功能- 實作從視訊中截圖的功能
- 分析檔案格式,讀取視訊寬高和時長
- 把所有截圖繪制在一張圖像中
下面一一道來:
1. 實作從視訊中截圖的功能
首先第一個,也是最關鍵的一點,實作視訊截圖。對于截圖這種需要解碼視訊的功能,我們最好借助于現有程式,著名的開源項目ffmpeg便可完成此功能,還有鼎鼎大名的開源播放器MPlayer,也提供的截圖的指令接口,隻需要簡單的調用即可,兩個程式生成的截圖品質差不多,效率上卻有些不同。
ffmpeg的調用指令為:
ffmpeg -i input.rmvb -y -f image2 -ss 08.010 -t 0.001 -s 352x240 output.jpg
其中,-ss指定了截圖的起始時間
當然,這隻能截取一張圖檔,我們要的是劇情連拍啊!不用着急,雖然沒有現成的,我們可以自己來做,隻要讀取到影片的時長,我們就能夠計算出需要在哪些時間來截圖,這樣,通過均勻分布截圖時間,多次調用截圖程式,就能生成一連串的截圖,也就是劇情了。那怎麼才能讀取影片時長呢,來看第二步。
2. 分析檔案格式,讀取視訊寬高和時長
讀取視訊寬高和時長,需要解析視訊格式。如今,媒體容器格式繁多複雜,要想準确的判斷一個視訊檔案的格式和編碼,不是件容易的事情。還好,我們有偉大的開源項目,用很多開源軟體都可以讀取出視訊格式,這裡推薦MediaInfo,因為它似乎就是為這個目的而建立的,生成的屬性資訊詳細而準确,正是我們想要的。此外,MediaInfo還提供了一個動态連結庫"MediaInfo.dll"。
很明顯,MediaInfo告訴了你,這個影片的時長(Duration),寬度(Width)和高度(Height),當然還有其他你想要或不想要的資訊,夠了。
3. 把所有截圖繪制在一張圖像中
這個沒什麼好說的,隻要前兩步做出來了,這一步基本不是什麼問題,根據視訊的寬和高,以及最終結果圖檔的寬和高,計算出你需要對截圖進行縮放的比例,然後把圖像繪制在一起,生成新的圖像即可。下面是用 Qt 實作這一步的代碼:
1 int headerheight = 70; // 頂上留一部分高度用了繪制logo和檔案資訊
2 int interval = 4;
3 int column_count = basecolumn_;
4 int total_width = column_count * one_img_width + (column_count+1) * interval;
5 int row_count = (imglist.size() + column_count-1 ) / column_count;
6 int total_height = row_count * one_img_height + (row_count+1) * interval + headerheight;
7
8 int row_no = 0;
9 int col_no = 0;
10 QImage totalImage(total_width, total_height, QImage::Format_ARGB32_Premultiplied);
11 totalImage.fill(qRgb(255, 255, 255));
12 QPainter imagePainter(&totalImage);
13 imagePainter.setRenderHint(QPainter::Antialiasing, true);
14 imagePainter.setPen(QPen(Qt::black, 1, Qt::SolidLine/*, Qt::RoundCap, Qt::RoundJoin*/));
15 imagePainter.setFont(QFont(tr("楷體"), 13, QFont::Bold));
16 QString titlename(tr("影片名:"));
17 titlename.append(QString::fromStdString(cinfo.title.c_str()));
18 titlename.append(tr("\t格式:"));
19 titlename.append(QString::fromStdString(cinfo.format_name));
20 imagePainter.drawText(150, 30, titlename);
21 std::ostringstream oss;
22 oss << "檔案大小:" << cinfo.size / 1000000 << "MB\t視訊尺寸:"
23 << cinfo.width << "x" << cinfo.height << "\t視訊時長:";
24 QString info = QString::fromStdString(oss.str());
25 info.append(TimetoStr(cinfo.duration));
26 imagePainter.drawText(150, 55, info);
27
28
29 for (list<QImage>::const_iterator it = imglist.begin();
30 it != imglist.end(); ++it)
31 {
32 int img_x = col_no * one_img_width + (col_no+1) * interval;
33 int img_y = row_no * one_img_height + (row_no+1) * interval + headerheight;
34
35 imagePainter.drawImage(img_x, img_y, *it);
36
37 col_no++;
38 if ( (col_no % column_count) == 0 )
39 {
40 row_no ++;
41 col_no = 0;
42 }
43 }
44
45 imagePainter.end();
46 totalImage.save(QString::fromStdString(outname));
這樣,模仿QQ影音劇情連拍的功能就實作了,然後呢,就可以在此基礎上做其他的應用了。我是用Qt開發的,當然,很簡單就能轉換成其他的語言和類庫來實作。