ffmpeg/c++ 使用 ffmpeg 对视频帧的附加信息进行编码

ffmpeg/c++ Encode additional information of video frame with ffmpeg

本文关键字:ffmpeg 信息 编码 c++ 使用 视频      更新时间:2023-10-16

我是ffmpeg和视频编码的新手,在此页面上查找了一些相关问题后,我发现这篇文章对于理解ffmpeg的概述过程非常有用。

但是,我的工作不仅需要使用Mat帧进行操作,在从视频中提取重要信息(提取边缘,边缘块的位置,每个边缘块的类型,块编号,运动矢量)之后,我必须编码并将它们发送给客户端。我试图为这部分找到一个示例代码,但似乎以前没有人做过。

我的问题是如何将这些附加信息与视频帧一起编码,并将两者发送给客户端。我读过关于霍夫曼编码的信息,它可以帮助无损压缩,但是是否可以使用霍夫曼编码对边缘和运动数据进行编码,同时使用ffmpeg编码视频帧?我正在使用 udp 协议进行实验。

我找不到有关此的任何信息。 我在ffmpeg中读取元数据和侧面信息,但这不是我想做的。

我希望你能给我一个建议或方向来研究这个领域,这样我就可以理解并尝试实施它。如果这种情况有任何示例代码,我将非常感谢您的分享。

非常感谢。

以下是服务器端的编码器部分:

int encode(Mat& input_frame, EncodedCallback callback, void* userdata = nullptr) {
AVPacket pkt;
/* encode 1 second of video */
av_init_packet(&pkt);
pkt.data = NULL; // packet data will be allocated by the encoder
pkt.size = 0;
int size = 0;
fflush(stdout);
cvtFrame2AVFrameYUV420(input_frame, &frame);
static int time; 
frame->pts = time++;
/* encode the image */
ret = avcodec_send_frame(c, frame);
if (ret < 0) {
fprintf(stderr, "Error avcodec_send_framen");
exit(1);
}
nbFramesEncoded++;
ret = avcodec_receive_packet(c, &pkt);
if (!isFirstFrameEmmited) {
nbNeededFramesInBuffer++;
printf("nbNeededFramesInBuffer: %dn", nbNeededFramesInBuffer);
}
if (ret < 0) {
if (ret == -EAGAIN) {
//output is not available, we must send more input
} else {
fprintf(stderr, "Error avcodec_receive_packet %dn", ret);
exit(1);
}
} else {
if (callback) {
callback(pkt, userdata);
}
size = pkt.size + 4;
av_packet_unref(&pkt);
}
return size;
}

下面是处理帧处理的代码(目前我们检查并向客户端发送运动块)

void updateFrame(Mat& frame) {
//Get all Streams ready
bool isReady = true;
if (!frameStreamer->encoder->isFirstFrameEmmited) {
frameStreamer->sendFrame(frame);
isReady = false;
}
for (int yidx = 0; yidx < gridSize.height; yidx++) {
for (int xidx = 0; xidx < gridSize.width; xidx++) {
StreamPtr& stream = streamGrid[yidx][xidx];
if (!stream->encoder->isFirstFrameEmmited) {
Mat block = frame(stream->irect);
stream->sendFrame(block);
isReady = false;
}
}
}
if (isReady == false) {
return;
}
if (pGray.empty()) {
frameStreamer->sendFrame(frame);
frameStreamer->sendFrame(frame);
cvtColor(frame, pGray, CV_BGR2GRAY);
return;
}
//Motion Detection
Mat gray;
cvtColor(frame, gray, CV_BGR2GRAY);
Mat diff;
absdiff(gray, pGray, diff);
threshold(diff, diff, NOISE_THRESHOLD, 255, CV_THRESH_BINARY);
if (HEAT_IMAGE) {
gray.copyTo(diff, diff);
imshow("Gray", gray);
threshold(diff, diff, HEAT_THRESH, 255, CV_THRESH_TOZERO);
}
if (USE_MORPH_NOISE) {
Morph_Noise(diff);
}
Mat motionImg = Mat::zeros(frameSize, CV_8UC3);
//Block Classification
int nbModifiedBlocks = 0;
for (int yidx = 0; yidx < gridSize.height; yidx++) {
for (int xidx = 0; xidx < gridSize.width; xidx++) {
Rect irect(xidx * blockSize.width, yidx * blockSize.height,
blockSize.width, blockSize.height);
int blockDiff = sum(diff(irect))[0];
if (blockDiff > BLOCK_THRESHOLD * 255) {
this->blockCls.at<uchar>(yidx, xidx) = MODI_BLOCK;
nbModifiedBlocks++;
} else {
this->blockCls.at<uchar>(yidx, xidx) = SKIP_BLOCK;
}
}
}
//Send
if (nbModifiedBlocks > this->nbBlocksThresh) {
nbSentBytes += this->frameStreamer->sendFrame(frame);
} else {
for (int yidx = 0; yidx < gridSize.height; yidx++) {
for (int xidx = 0; xidx < gridSize.width; xidx++) {
uchar cls = this->blockCls.at<uchar>(yidx, xidx);
StreamPtr& stream = streamGrid[yidx][xidx];
bool send = false;
if (cls == MODI_BLOCK) {
if (DEBUG_NETWORK) {
printf("Normal (%d, %d): ", xidx, yidx);
}
send = true;
stream->encoder->nbFramesBuffered = stream->encoder->nbNeededFramesInBuffer;
rectangle(motionImg, stream->irect, Scalar(0, 0, 255), CV_FILLED);
} else if (stream->encoder->nbFramesBuffered > 0) {
if (DEBUG_NETWORK) {
printf("Extra (%d, %d): ", xidx, yidx);
}
send = true;
stream->encoder->nbFramesBuffered--;
stream->encoder->nbFlushFrames++;
rectangle(motionImg, stream->irect, Scalar(0, 255, 0), CV_FILLED);
}
if (send) {
Mat block = frame(stream->irect);
nbSentBytes += stream->sendFrame(block);
gray(stream->irect).copyTo(pGray(stream->irect));
}
}
}
}

}

我可能不太明白你的要求,我想你要求在你的输出中添加额外的流,这可能有一些自定义数据。这就是您添加data流的方式,请在您add video stream的位置之后添加它。

AVCodecParameters *par;
AVStream *st;
st = avformat_new_stream(ctx->oc, NULL); // ctx->oc is AVFormatContext *oc same you used where you add video stream
if (st == NULL)
{
fprintf(stderr, "Error: avformat_new_stream() failed.n");
exit(EXIT_FAILURE);
}
par = st->codecpar;
par->codec_id = AV_CODEC_ID_TEXT; // raw UTF-8 text
par->codec_type = AVMEDIA_TYPE_DATA; // Opaque data information usually continuous

之后,您可以将"数据"放入pkt.data,并在关联的视频帧输出后av_interleaved_write_frame发送。