我今天试着做了一些事情,想和大家分享我的结果。你可以从
CImg输出原始RGB视频,然后使用
ffmpeg将其编码成视频,就像这样:
#include <iostream>
#include "CImg.h"
using namespace std;
using namespace cimg_library;
int main()
{
const unsigned int width=1024;
const unsigned int height=768;
CImg<unsigned char> image(width,height,1,3);
unsigned char magenta[] = {255,0,255};
int radius=100;
int cx=100;
int cy=100;
for(int frame=0;frame<300;frame++){
image.fill(0);
image.draw_circle(cx,cy,radius,magenta);
cx+=2; cy++; if(magenta[1]!=255){magenta[1]++;}
char* s=reinterpret_cast<char*>(image.data()+(width*height));
std::cout.write(s,width*height);
s=reinterpret_cast<char*>(image.data()+2*(width*height));
std::cout.write(s,width*height);
s=reinterpret_cast<char*>(image.data());
std::cout.write(s,width*height);
}
}
我猜我不会进入好莱坞,因为这个视频不是很令人兴奋!
按照以下方式运行上述代码以生成视频:
./main | ffmpeg -y -f rawvideo -pixel_format gbrp -video_size 1024x768 -i - -c:v h264 -pix_fmt yuv420p video.mov
注1
需要明白的是,CImg以平面配置存储数据,这意味着所有红色像素首先保存,然后直接保存所有绿色像素,最后紧随其后保存所有蓝色像素-全部没有填充或间隔。
想象一下在CImg中有一个4x4的图像(共16个像素):
RRRRRRRRRRRRRRRR GGGGGGGGGGGGGGGG BBBBBBBBBBBBBBBB
与常规的RGB数据不同,它将存储与以下图像相同的图像:
RGB RGB RGB RGB RGB RGB RGB RGB RGB RGB RGB RGB RGB RGB RGB RGB
因此,您可以将所有数据混合并重新格式化,并将其作为
-pixel_fmt rgb24
传递给
ffmpeg,或者像我一样以
CImg的平面格式输出并选择匹配的
-pixel_fmt gbrp
(其中
p
表示
"平面")。您只需要按正确的B、G、R顺序输出平面即可。另请参见
Note 4。
注2
为了更清晰地展示,我选择使用3个write()
,分别写入每个色彩平面,但实际上使用 writev()
的"gathered write" 会更高效。
char* s=reinterpret_cast<char*>(image.data()+(width*height)); // Get start of G plane
std::cout.write(s,width*height); // Output it
s=reinterpret_cast<char*>(image.data()+2*(width*height)); // Get start of B plane
std::cout.write(s,width*height); // Output it
s=reinterpret_cast<char*>(image.data()); // Get start of R plane
std::cout.write(s,width*height);
会成为类似这样的东西:
struct iovec iov[3];
ssize_t nwritten;
iov[0].iov_base = reinterpret_cast<char*>(image.data()+(width*height))
iov[0].iov_len = width*height;
iov[1].iov_base = reinterpret_cast<char*>(image.data()+2*(width*height));
iov[1].iov_len = width*height;
iov[2].iov_base = reinterpret_cast<char*>(image.data());
iov[2].iov_len = width*height;
nwritten = writev(STDOUT_FILENO,iov,3);
注3
我使用了-c:v h264 -pix_fmt yuv420p
来使视频与我的Mac上的苹果公司的QuickTime兼容,但您可以轻松更改输出 - 更难的部分是正确地处理CImg和fmpeg之间的接口。
注4
如果你想要重新排列数据并将其写入ffmpeg非平面格式(-pixel_fmt rgb
),我最初的代码如下:
unsigned char* BIP = new unsigned char[width*height*3];
unsigned char *d,*r,*g,*b;
...
...
r=image.data();
g=r+(width*height);
b=g+(width*height);
d=BIP;
for(int i=0;i<width*height;i++){
*d++=*r++;
*d++=*g++;
*d++=*b++;
}
std::cout.write(reinterpret_cast<char*>(BIP),width*height*3);
理论上,您可以使用CImg的permute_axes()
方法来实现此操作,但我没有成功。