-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathH264VideoStreamEncoder.cs
86 lines (72 loc) · 3.47 KB
/
H264VideoStreamEncoder.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
using System;
using System.Drawing;
using System.IO;
using FFmpeg.AutoGen;
namespace cavapa
{
public sealed unsafe class H264VideoStreamEncoder : IDisposable
{
private readonly Size _frameSize;
private readonly int _linesizeU;
private readonly int _linesizeV;
private readonly int _linesizeY;
private readonly AVCodec* _pCodec;
private readonly AVCodecContext* _pCodecContext;
private readonly Stream _stream;
private readonly int _uSize;
private readonly int _ySize;
public H264VideoStreamEncoder(Stream stream, int fps, Size frameSize)
{
_stream = stream;
_frameSize = frameSize;
var codecId = AVCodecID.AV_CODEC_ID_H264;
_pCodec = ffmpeg.avcodec_find_encoder(codecId);
if (_pCodec == null) throw new InvalidOperationException("Codec not found.");
_pCodecContext = ffmpeg.avcodec_alloc_context3(_pCodec);
_pCodecContext->width = frameSize.Width;
_pCodecContext->height = frameSize.Height;
_pCodecContext->time_base = new AVRational {num = 1, den = fps};
_pCodecContext->pix_fmt = AVPixelFormat.AV_PIX_FMT_YUV420P;
ffmpeg.av_opt_set(_pCodecContext->priv_data, "preset", "veryslow", 0);
ffmpeg.avcodec_open2(_pCodecContext, _pCodec, null).ThrowExceptionIfError();
_linesizeY = frameSize.Width;
_linesizeU = frameSize.Width / 2;
_linesizeV = frameSize.Width / 2;
_ySize = _linesizeY * frameSize.Height;
_uSize = _linesizeU * frameSize.Height / 2;
}
public void Dispose()
{
ffmpeg.avcodec_close(_pCodecContext);
ffmpeg.av_free(_pCodecContext);
ffmpeg.av_free(_pCodec);
}
public void Encode(AVFrame frame)
{
if (frame.format != (int) _pCodecContext->pix_fmt) throw new ArgumentException("Invalid pixel format.", nameof(frame));
if (frame.width != _frameSize.Width) throw new ArgumentException("Invalid width.", nameof(frame));
if (frame.height != _frameSize.Height) throw new ArgumentException("Invalid height.", nameof(frame));
if (frame.linesize[0] < _linesizeY) throw new ArgumentException("Invalid Y linesize.", nameof(frame));
if (frame.linesize[1] < _linesizeU) throw new ArgumentException("Invalid U linesize.", nameof(frame));
if (frame.linesize[2] < _linesizeV) throw new ArgumentException("Invalid V linesize.", nameof(frame));
if (frame.data[1] - frame.data[0] < _ySize) throw new ArgumentException("Invalid Y data size.", nameof(frame));
if (frame.data[2] - frame.data[1] < _uSize) throw new ArgumentException("Invalid U data size.", nameof(frame));
var pPacket = ffmpeg.av_packet_alloc();
try
{
int error;
do
{
ffmpeg.avcodec_send_frame(_pCodecContext, &frame).ThrowExceptionIfError();
error = ffmpeg.avcodec_receive_packet(_pCodecContext, pPacket);
} while (error == ffmpeg.AVERROR(ffmpeg.EAGAIN));
error.ThrowExceptionIfError();
using (var packetStream = new UnmanagedMemoryStream(pPacket->data, pPacket->size)) packetStream.CopyTo(_stream);
}
finally
{
ffmpeg.av_packet_unref(pPacket);
}
}
}
}