以下是使用 FFmpeg.AutoGen 将 h264 视频流通过 RTMP 推流的 C# 示例代码:

using FFmpeg.AutoGen;
using System;
using System.Runtime.InteropServices;

namespace RtmpPusher
{
    class Program
    {
        static void Main(string[] args)
        {
            // 初始化 FFmpeg
            ffmpeg.av_register_all();
            ffmpeg.avformat_network_init();

            // 打开输入文件
            AVInputFormat* inputFormat = ffmpeg.av_find_input_format('video4linux2');
            AVFormatContext* inputContext = null;
            if (ffmpeg.avformat_open_input(&inputContext, '/dev/video0', inputFormat, null) != 0)
            {
                Console.WriteLine('Could not open input file.');
                return;
            }

            // 查找视频流
            AVCodec* videoCodec = null;
            int videoStreamIndex = ffmpeg.av_find_best_stream(inputContext, AVMediaType.AVMEDIA_TYPE_VIDEO, -1, -1, &videoCodec, 0);
            if (videoStreamIndex < 0)
            {
                Console.WriteLine('Could not find video stream.');
                return;
            }

            // 打开视频解码器
            AVCodecContext* videoCodecContext = inputContext->streams[videoStreamIndex]->codec;
            if (ffmpeg.avcodec_open2(videoCodecContext, videoCodec, null) != 0)
            {
                Console.WriteLine('Could not open video codec.');
                return;
            }

            // 初始化输出上下文
            AVFormatContext* outputContext = null;
            if (ffmpeg.avformat_alloc_output_context2(&outputContext, null, 'flv', 'rtmp://localhost/live') != 0)
            {
                Console.WriteLine('Could not allocate output context.');
                return;
            }

            // 添加视频流到输出上下文
            AVStream* videoStream = ffmpeg.avformat_new_stream(outputContext, null);
            if (videoStream == null)
            {
                Console.WriteLine('Could not create video stream.');
                return;
            }
            videoStream->codecpar->codec_type = AVMediaType.AVMEDIA_TYPE_VIDEO;
            videoStream->codecpar->codec_id = videoCodecContext->codec_id;
            videoStream->codecpar->width = videoCodecContext->width;
            videoStream->codecpar->height = videoCodecContext->height;
            videoStream->codecpar->format = (int)AVPixelFormat.AV_PIX_FMT_YUV420P;
            videoStream->time_base = videoCodecContext->time_base;

            // 打开视频编码器
            AVCodec* outputVideoCodec = ffmpeg.avcodec_find_encoder(AVCodecID.AV_CODEC_ID_H264);
            AVCodecContext* outputVideoCodecContext = ffmpeg.avcodec_alloc_context3(outputVideoCodec);
            ffmpeg.avcodec_parameters_to_context(outputVideoCodecContext, videoStream->codecpar);
            if (ffmpeg.avcodec_open2(outputVideoCodecContext, outputVideoCodec, null) != 0)
            {
                Console.WriteLine('Could not open video encoder.');
                return;
            }

            // 初始化输出文件
            if ((outputContext->oformat->flags & ffmpeg.AVFMT_NOFILE) == 0 && ffmpeg.avio_open2(&outputContext->pb, 'rtmp://localhost/live', ffmpeg.AVIO_FLAG_WRITE, null, null) != 0)
            {
                Console.WriteLine('Could not open output file.');
                return;
            }

            // 写入输出文件头
            if (ffmpeg.avformat_write_header(outputContext, null) != 0)
            {
                Console.WriteLine('Could not write output file header.');
                return;
            }

            // 分配 AVPacket 和 AVFrame
            AVPacket* packet = ffmpeg.av_packet_alloc();
            AVFrame* frame = ffmpeg.av_frame_alloc();
            byte[] buffer = new byte[videoCodecContext->width * videoCodecContext->height * 3 / 2];
            GCHandle handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
            IntPtr bufferPtr = handle.AddrOfPinnedObject();
            frame->data[0] = bufferPtr;
            frame->data[1] = bufferPtr + videoCodecContext->width * videoCodecContext->height;
            frame->data[2] = bufferPtr + videoCodecContext->width * videoCodecContext->height * 5 / 4;
            frame->linesize[0] = videoCodecContext->width;
            frame->linesize[1] = videoCodecContext->width / 2;
            frame->linesize[2] = videoCodecContext->width / 2;
            frame->format = (int)AVPixelFormat.AV_PIX_FMT_YUV420P;
            frame->width = videoCodecContext->width;
            frame->height = videoCodecContext->height;

            // 推流
            while (true)
            {
                // 从输入文件读取一帧视频
                AVPacket* inputPacket = ffmpeg.av_packet_alloc();
                if (ffmpeg.av_read_frame(inputContext, inputPacket) != 0)
                {
                    break;
                }
                if (inputPacket->stream_index != videoStreamIndex)
                {
                    ffmpeg.av_packet_unref(inputPacket);
                    continue;
                }
                if (ffmpeg.avcodec_send_packet(videoCodecContext, inputPacket) != 0)
                {
                    ffmpeg.av_packet_unref(inputPacket);
                    continue;
                }
                while (ffmpeg.avcodec_receive_frame(videoCodecContext, frame) == 0)
                {
                    // 编码视频帧
                    AVPacket* outputPacket = ffmpeg.av_packet_alloc();
                    if (ffmpeg.avcodec_send_frame(outputVideoCodecContext, frame) != 0)
                    {
                        ffmpeg.av_packet_unref(outputPacket);
                        continue;
                    }
                    if (ffmpeg.avcodec_receive_packet(outputVideoCodecContext, outputPacket) != 0)
                    {
                        ffmpeg.av_packet_unref(outputPacket);
                        continue;
                    }

                    // 写入输出文件
                    outputPacket->stream_index = videoStreamIndex;
                    outputPacket->pts = ffmpeg.av_rescale_q(outputPacket->pts, outputVideoCodecContext->time_base, videoStream->time_base);
                    outputPacket->dts = ffmpeg.av_rescale_q(outputPacket->dts, outputVideoCodecContext->time_base, videoStream->time_base);
                    outputPacket->duration = ffmpeg.av_rescale_q(outputPacket->duration, outputVideoCodecContext->time_base, videoStream->time_base);
                    if (ffmpeg.av_interleaved_write_frame(outputContext, outputPacket) != 0)
                    {
                        Console.WriteLine('Could not write output packet.');
                    }

                    ffmpeg.av_packet_unref(outputPacket);
                }
                ffmpeg.av_packet_unref(inputPacket);
            }

            // 写入输出文件尾
            if (ffmpeg.av_write_trailer(outputContext) != 0)
            {
                Console.WriteLine('Could not write output file trailer.');
                return;
            }

            // 释放资源
            ffmpeg.avformat_close_input(&inputContext);
            ffmpeg.avcodec_close(videoCodecContext);
            ffmpeg.avcodec_free_context(&outputVideoCodecContext);
            if ((outputContext->oformat->flags & ffmpeg.AVFMT_NOFILE) == 0)
            {
                ffmpeg.avio_close(outputContext->pb);
            }
            ffmpeg.avformat_free_context(outputContext);
            ffmpeg.av_packet_free(&packet);
            ffmpeg.av_frame_free(&frame);
            handle.Free();
        }
    }
}

在这个示例中,我们首先使用 FFmpeg 打开视频输入文件(这里使用的是 video4linux2 设备文件),查找视频流,打开视频解码器。

然后,我们初始化输出上下文,添加视频流到输出上下文,打开视频编码器,并初始化输出文件。

在推流循环中,我们从输入文件读取一帧视频,解码视频帧,编码视频帧,并将编码后的视频帧写入输出文件。

最后,我们写入输出文件尾,释放资源。

使用 FFmpeg.AutoGen 将 h264 视频流通过 RTMP 推流

原文地址: https://www.cveoy.top/t/topic/ndNV 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录