【问题标题】:ffmpeg/c++ Encode additional information of video frame with ffmpegffmpeg/c++ 用ffmpeg编码视频帧的附加信息
【发布时间】:2018-07-05 11:47:48
【问题描述】:

我是ffmpeg和视频编码的新手,在这个页面上查找了一些相关问题后,我发现this这个帖子对于理解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_frame\n");
        exit(1);
    }
    nbFramesEncoded++;
    ret = avcodec_receive_packet(c, &pkt);
    if (!isFirstFrameEmmited) {
        nbNeededFramesInBuffer++;

        printf("nbNeededFramesInBuffer: %d\n", nbNeededFramesInBuffer);
    }
    if (ret < 0) {
        if (ret == -EAGAIN) {
            //output is not available, we must send more input
        } else {
            fprintf(stderr, "Error avcodec_receive_packet %d\n", 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));
                }
            }
        }
    }

}

【问题讨论】:

  • “将这些附加信息与视频帧一起编码”的意思还不够清楚。视频只是一些压缩图片和一些标题信息。据我现在了解,也许您可​​以将这些边缘/块信息压缩到另一个自定义序列,该序列与视频帧一一对应。
  • 我在一篇论文中发现了这个想法,他们在其中编码(边缘块的位置、每个边缘块的类型、块号、运动矢量)并发送给客户端......我试图理解和实施它。我不明白如何创建额外的流来编码这些信息。

标签: c++ opencv video encoding ffmpeg


【解决方案1】:

我可能不太明白您的要求,我认为您要求向您的输出添加额外的流,其中可能包含一些自定义数据。这就是添加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 发送。

【讨论】:

    猜你喜欢
    • 2023-03-12
    • 2018-02-12
    • 2015-02-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-14
    相关资源
    最近更新 更多