FFMPEG Stream网络图片系列

时间:2015-09-07 18:16:46

标签: ffmpeg video-streaming

我想在一台计算机上生成视频帧并将它们流式传输到另一台计算机进行操作和显示。我想以编程方式执行此操作,因为源图像是从第三方c ++库生成的,并且在接收端我想在显示它们之前操纵图像。我一直在尝试FFMPEG。我得到了编码示例,但我不确定在那之后该怎么做。我只是用Google搜索了我能想到的一切,但是一旦我有一个填充的AVPacket,就无法弄清楚要调用哪个FFMPEG库/函数。似乎我需要使用AVIO或AVFormat或多路复用器,但不清楚如何初始化并让它们工作。我很感激您提供的任何帮助。

为了提供一些上下文,我有一个示例'decoding_encoding.c',​​它随ffmpeg一起提供。这是我正在努力的地方:

        /* encode the image */
        ret = avcodec_encode_video2(c, &pkt, frame, &got_output);
        if (ret < 0) {
            fprintf(stderr, "Error encoding frame\n");
            exit(1);
        }

        if (got_output) {
            printf("Write frame %3d (size=%5d)\n", i, pkt.size);
            fwrite(pkt.data, 1, pkt.size, f);

            //
            // instead of writing to a file, i want to stream to a network
            // What do I need to do with pkt to do that?
            //
            av_free_packet(&pkt);
        }

大多数文档(和Dranger教程)都专注于读写文件。我不需要使用文件。我想通过网络流式传输视频。好像我需要AVFormat和AVio,但我不知道它们是如何组合在一起的。

1 个答案:

答案 0 :(得分:2)

关键是使用avio_open2()打开套接字连接,然后将pkt的数据和大小字段传递给avio_write()。他是一个示例工作程序(派生自ffmpeg的doc / examples目录中的decode_encode.c):

#include "stdafx.h"
/*
* Copyright (c) 2001 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

/**
* @file
* libavcodec API use example.
*
* @example decoding_encoding.c
* Note that libavcodec only handles codecs (mpeg, mpeg4, etc...),
* not file formats (avi, vob, mp4, mov, mkv, mxf, flv, mpegts, mpegps, etc...). See library 'libavformat' for the
* format handling
*/

#include <math.h>
#include <stdio.h>
#include <cstdio>
#include <winsock.h>

extern "C"
{
#include <libavutil/opt.h>
#include <libavcodec/avcodec.h>
#include <libavutil/channel_layout.h>
#include <libavutil/common.h>
#include <libavutil/imgutils.h>
#include <libavutil/mathematics.h>
#include <libavutil/samplefmt.h>
#include <libavformat\avio.h>
#include <libavformat\avformat.h>
}

#define INBUF_SIZE 4096
#define AUDIO_INBUF_SIZE 20480
#define AUDIO_REFILL_THRESH 4096


static void video_encode_example(const char *filename, AVCodecID codec_id)
{
    AVCodec *codec;
    AVCodecContext *c = NULL;
    int i, ret, x, y, got_output;
    FILE *f;
    AVFrame *frame;
    AVPacket pkt;
    uint8_t endcode[] = { 0, 0, 1, 0xb7 };
    errno_t err;
    int errval;
    printf("Encode video file %s\n", filename);

    /* find the mpeg1 video encoder */
    codec = avcodec_find_encoder(codec_id);
    if (!codec) {
        fprintf(stderr, "Codec not found\n");
        exit(1);
    }

    c = avcodec_alloc_context3(codec);
    if (!c) {
        fprintf(stderr, "Could not allocate video codec context\n");
        exit(1);
    }

    /* put sample parameters */
    c->bit_rate = 4000000;
    /* resolution must be a multiple of two */
    c->width = 1024;
    c->height = 768;
    /* frames per second */
    c->time_base.den = 25;
    c->time_base.num = 1;
    /* emit one intra frame every ten frames
    * check frame pict_type before passing frame
    * to encoder, if frame->pict_type is AV_PICTURE_TYPE_I
    * then gop_size is ignored and the output of encoder
    * will always be I frame irrespective to gop_size
    */
    c->gop_size = 12;
    c->max_b_frames = 0;
    c->pix_fmt = AV_PIX_FMT_YUV420P;

    if (codec_id == AV_CODEC_ID_H264)
    {
        av_opt_set(c->priv_data, "preset", "slow", 0);
        av_opt_set(c->priv_data, "tune", "zerolatency", 0);
    }

    /* open it */
    if (avcodec_open2(c, codec, NULL) < 0) {
        fprintf(stderr, "Could not open codec\n");
        exit(1);
    }

    AVFormatContext* format =  avformat_alloc_context();
    AVDictionary *options = NULL;
    av_dict_set(&options, "pkt_size", "1300", 0);
    av_dict_set(&options, "buffer_size", "65535", 0);
    AVIOContext * server = NULL;
    avio_open2(&server, "udp://192.168.0.13:5555", AVIO_FLAG_WRITE, NULL, &options);

    frame = av_frame_alloc();
    if (!frame) {
        fprintf(stderr, "Could not allocate video frame\n");
        exit(1);
    }
    frame->format = c->pix_fmt;
    frame->width = c->width;
    frame->height = c->height;

    /* the image can be allocated by any means and av_image_alloc() is
    * just the most convenient way if av_malloc() is to be used */
    ret = av_image_alloc(frame->data, frame->linesize, c->width, c->height,
        c->pix_fmt, 32);
    if (ret < 0) {
        fprintf(stderr, "Could not allocate raw picture buffer\n");
        exit(1);
    }
    // Cycle through the test pattern for 1 hour
    for (int j = 0; j < 3600; j++)
    {
        /* encode 1 second of video */
        for (i = 0; i < 25; i++) {

            Sleep(40);
            av_init_packet(&pkt);
            pkt.data = NULL;    // packet data will be allocated by the encoder
            pkt.size = 1300;

            fflush(stdout);
            /* prepare a dummy image */
            /* Y */
            for (y = 0; y < c->height; y++) {
                for (x = 0; x < c->width; x++) {
                    frame->data[0][y * frame->linesize[0] + x] = x + y + i * 3;
                }
            }

            /* Cb and Cr */
            for (y = 0; y < c->height / 2; y++) {
                for (x = 0; x < c->width / 2; x++) {
                    frame->data[1][y * frame->linesize[1] + x] = 128 + y + i * 2;
                    frame->data[2][y * frame->linesize[2] + x] = 64 + x + i * 5;
                }
            }

            frame->pts = i+j*25;

            /* encode the image */
            ret = avcodec_encode_video2(c, &pkt, frame, &got_output);
            if (ret < 0) {
                fprintf(stderr, "Error encoding frame\n");
                exit(1);
            }

            if (got_output) {
                printf("Write frame %3d (size=%5d)\n", i, pkt.size);
                //fwrite(pkt.data, 1, pkt.size, f);
                avio_write(server, pkt.data, pkt.size);
                av_free_packet(&pkt);
            }
        }
    }

    /* get the delayed frames */
    for (got_output = 1; got_output; i++) {
        fflush(stdout);

        ret = avcodec_encode_video2(c, &pkt, NULL, &got_output);
        if (ret < 0) {
            fprintf(stderr, "Error encoding frame\n");
            exit(1);
        }

        if (got_output) {
            printf("Write frame %3d (size=%5d)\n", i, pkt.size);
            avio_write(server, pkt.data, pkt.size);
            av_free_packet(&pkt);
        }
    }
    avcodec_close(c);
    av_free(c);
    av_freep(&frame->data[0]);
    av_frame_free(&frame);
    printf("\n");
}

int main(int argc, char **argv)
{
    const char *output_type;

    /* register all the codecs */
    av_register_all();
    avcodec_register_all();
    avformat_network_init();

    video_encode_example("test.h264", AV_CODEC_ID_H264);

    return 0;
}