-
Notifications
You must be signed in to change notification settings - Fork 19
Expand file tree
/
Copy pathVaapiEncode.cs
More file actions
115 lines (99 loc) · 4.54 KB
/
Copy pathVaapiEncode.cs
File metadata and controls
115 lines (99 loc) · 4.54 KB
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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
using System;
using System.IO;
using FFmpeg.AutoGen;
namespace FFmpeg.Sharp.Example
{
/// <summary>
/// Maps to FFmpeg example: vaapi_encode.c
/// VAAPI-accelerated H.264 encoding: read raw NV12 frames from a file and
/// write the encoded H.264 bitstream to an output file.
/// Usage: args[0] = width, args[1] = height, args[2] = input.nv12, args[3] = output.h264
/// </summary>
public unsafe class VaapiEncode : ExampleBase
{
public VaapiEncode() { Index = 23; Enable = false; }
public override void Execute()
{
if (args.Length < 4)
{
Console.Error.WriteLine("Usage: VaapiEncode <width> <height> <input.nv12> <output.h264>");
return;
}
int width = int.Parse(args[0]);
int height = int.Parse(args[1]);
var inFile = args[2];
var outFile = args[3];
int size = width * height;
// ── Encoder (UseHardware creates the VAAPI device plus an NV12-backed
// hw_frames_ctx and wires both into the codec context) ───────────
using var encoder = MediaEncoder.Video()
.Codec("h264_vaapi")
.Size(width, height)
.Fps(new AVRational { num = 25, den = 1 })
.UseHardware(AVPixelFormat.AV_PIX_FMT_VAAPI, AVPixelFormat.AV_PIX_FMT_NV12,
AVHWDeviceType.AV_HWDEVICE_TYPE_VAAPI)
.Configure(c => c.Ref.sample_aspect_ratio = new AVRational { num = 1, den = 1 })
.Build();
// ── Encode loop ───────────────────────────────────────────────────
using (var fin = new FileStream(inFile, FileMode.Open, FileAccess.Read))
using (var fout = new FileStream(outFile, FileMode.Create, FileAccess.Write))
{
using var swFrame = new MediaFrame();
using var hwFrame = new MediaFrame();
using var hwFramesCtx = encoder.GetHWFrames(); // surface pool created by UseHardware
int err;
while (true)
{
// Allocate a software frame for NV12 data.
swFrame.Ref.width = width;
swFrame.Ref.height = height;
swFrame.Ref.format = (int)AVPixelFormat.AV_PIX_FMT_NV12;
swFrame.AllocateBuffer();
// Read Y plane (width * height bytes).
var yBuf = new byte[size];
if (fin.Read(yBuf, 0, size) < size) break;
fixed (byte* pY = yBuf)
Buffer.MemoryCopy(pY, swFrame.Ref.data[0u], size, size);
// Read UV plane (width * height / 2 bytes).
var uvBuf = new byte[size / 2];
if (fin.Read(uvBuf, 0, size / 2) < size / 2) break;
fixed (byte* pUV = uvBuf)
Buffer.MemoryCopy(pUV, swFrame.Ref.data[1u], size / 2, size / 2);
// Allocate a HW frame and transfer SW → HW.
hwFrame.AllocateOnHWFrames(hwFramesCtx);
MediaCodecContext.HWFrameTransferData(hwFrame, swFrame);
err = EncodeWrite(encoder, hwFrame, fout);
if (err != 0 && err != ffmpeg.AVERROR(ffmpeg.EAGAIN))
{
Console.Error.WriteLine("Failed to encode.");
break;
}
hwFrame.Unref();
swFrame.Unref();
}
// Flush encoder.
err = EncodeWrite(encoder, null, fout);
if (err == ffmpeg.AVERROR_EOF) err = 0;
}
}
private static int EncodeWrite(MediaEncoder encoder, MediaFrame frame, Stream fout)
{
using var encPkt = new MediaPacket();
int ret = encoder.SendFrame(frame);
if (ret < 0)
{
Console.Error.WriteLine($"Error code: {FFmpegException.GetErrorString(ret)}");
return ret;
}
while (true)
{
ret = encoder.ReceivePacket(encPkt);
if (ret != 0) break;
encPkt.Ref.stream_index = 0;
fout.Write(encPkt.Data);
encPkt.Unref();
}
return ret;
}
}
}