Skip to content

Commit 2aa3649

Browse files
committed
Smoke HW encoding sample
Based on the #132
1 parent 89801c9 commit 2aa3649

File tree

2 files changed

+278
-0
lines changed

2 files changed

+278
-0
lines changed

example/api2-samples/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ set(TARGETS
2323
api2-timestamp
2424
api2-demux-seek
2525
api2-remux
26+
api2-hw-encode
2627
)
2728

2829
foreach(target ${TARGETS})
Lines changed: 277 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,277 @@
1+
/**
2+
* Smoke sample for the HW encoding with current AvCpp API state.
3+
*
4+
* Thanks to https://github.com/mojie126, for more-less complete sample.
5+
*
6+
* Known issues: error on the Matroska header writing.
7+
*
8+
*/
9+
10+
11+
#include <iostream>
12+
#include <set>
13+
#include <map>
14+
#include <memory>
15+
#include <functional>
16+
17+
#include "av.h"
18+
#include "ffmpeg.h"
19+
#include "codec.h"
20+
#include "packet.h"
21+
#include "videorescaler.h"
22+
#include "audioresampler.h"
23+
#include "avutils.h"
24+
25+
// API2
26+
#include "format.h"
27+
#include "formatcontext.h"
28+
#include "codec.h"
29+
#include "codeccontext.h"
30+
31+
using namespace std;
32+
using namespace av;
33+
34+
35+
// check CUDA accessibility
36+
bool isCudaAvailable()
37+
{
38+
AVHWDeviceType type = av_hwdevice_find_type_by_name("cuda");
39+
return type != AV_HWDEVICE_TYPE_NONE;
40+
}
41+
42+
// check QSV accessibility
43+
bool isQsvAvailable()
44+
{
45+
AVHWDeviceType type = av_hwdevice_find_type_by_name("qsv");
46+
return type != AV_HWDEVICE_TYPE_NONE;
47+
}
48+
49+
// Get ecnoder
50+
const av::Codec getEncoder(bool use_cuda, bool use_qsv)
51+
{
52+
if (use_cuda) {
53+
auto enc = av::findEncodingCodec("h264_nvenc");
54+
if (!enc.isNull())
55+
return enc;
56+
}
57+
if (use_qsv) {
58+
auto enc = av::findEncodingCodec("h264_qsv");
59+
if (!enc.isNull())
60+
return enc;
61+
}
62+
return av::findEncodingCodec("libx264"); // Fall-back to SW one
63+
}
64+
65+
// HW context
66+
AVHWDeviceType getHwDeviceType(bool use_cuda, bool use_qsv)
67+
{
68+
if (use_cuda)
69+
return av_hwdevice_find_type_by_name("cuda");
70+
if (use_qsv)
71+
return av_hwdevice_find_type_by_name("qsv");
72+
return AV_HWDEVICE_TYPE_NONE;
73+
}
74+
75+
int main(int argc, char **argv) {
76+
if (argc < 3)
77+
return 1;
78+
79+
av::init();
80+
av::setFFmpegLoggingLevel(AV_LOG_DEBUG);
81+
82+
string uri{argv[1]};
83+
string out{argv[2]};
84+
85+
error_code ec;
86+
87+
//
88+
// INPUT
89+
//
90+
FormatContext ictx;
91+
ssize_t videoStream = -1;
92+
// VideoDecoderContext vdec;
93+
Stream vst;
94+
95+
// int count = 0;
96+
97+
ictx.openInput(uri, ec);
98+
if (ec) {
99+
cerr << "Can't open input\n";
100+
return 1;
101+
}
102+
103+
ictx.findStreamInfo();
104+
105+
for (size_t i = 0; i < ictx.streamsCount(); ++i) {
106+
auto st = ictx.stream(i);
107+
if (st.mediaType() == AVMEDIA_TYPE_VIDEO) {
108+
videoStream = i;
109+
vst = st;
110+
break;
111+
}
112+
}
113+
bool use_cuda = isCudaAvailable();
114+
bool use_qsv = isQsvAvailable();
115+
// Get encoder and hardware context
116+
auto _encoder = getEncoder(use_cuda, use_qsv);
117+
AVHWDeviceType hw_type = getHwDeviceType(use_cuda, use_qsv); // --htrd: never use
118+
clog << "test hw: " << av_hwdevice_get_type_name(hw_type) << endl;
119+
//av::VideoDecoderContext vdec(vst, _encoder);
120+
av::VideoDecoderContext vdec;
121+
122+
if (vst.isNull()) {
123+
cerr << "Video stream not found\n";
124+
return 1;
125+
}
126+
127+
if (vst.isValid()) {
128+
vdec = VideoDecoderContext(vst);
129+
vdec.setRefCountedFrames(true);
130+
131+
vdec.open(Codec(), ec);
132+
if (ec) {
133+
cerr << "Can't open codec\n";
134+
return 1;
135+
}
136+
}
137+
138+
//
139+
// OUTPUT
140+
//
141+
OutputFormat ofrmt;
142+
FormatContext octx;
143+
144+
ofrmt.setFormat(string(), out);
145+
octx.setFormat(ofrmt);
146+
147+
//Codec ocodec = findEncodingCodec(ofrmt);
148+
VideoEncoderContext encoder{_encoder};
149+
150+
// Settings
151+
encoder.setWidth(vdec.width());
152+
encoder.setHeight(vdec.height());
153+
if (vdec.pixelFormat() > -1)
154+
encoder.setPixelFormat(vdec.pixelFormat());
155+
encoder.setTimeBase(Rational{1, 1000});
156+
encoder.setBitRate(vdec.bitRate());
157+
158+
encoder.open(Codec(), ec);
159+
if (ec) {
160+
cerr << "Can't opent encodec\n";
161+
return 1;
162+
}
163+
164+
Stream ost = octx.addStream(encoder);
165+
ost.setFrameRate(vst.frameRate());
166+
ost.setTimeBase(vst.timeBase());
167+
ost.setAverageFrameRate(vst.averageFrameRate());
168+
169+
octx.openOutput(out, ec);
170+
if (ec) {
171+
cerr << "Can't open output\n";
172+
return 1;
173+
}
174+
175+
octx.dump();
176+
octx.writeHeader();
177+
octx.flush();
178+
179+
180+
//
181+
// PROCESS
182+
//
183+
for (int64_t opkt_index = 0;;) {
184+
// READING
185+
Packet pkt = ictx.readPacket(ec);
186+
if (ec) {
187+
clog << "Packet reading error: " << ec << ", " << ec.message() << endl;
188+
break;
189+
}
190+
191+
bool flushDecoder = false;
192+
// !EOF
193+
if (pkt) {
194+
if (pkt.streamIndex() != videoStream) {
195+
continue;
196+
}
197+
198+
clog << "Read packet: pts=" << pkt.pts() << ", dts=" << pkt.dts() << " / " << pkt.pts().seconds() << " / " << pkt.timeBase() << " / st: " << pkt.streamIndex() << endl;
199+
} else {
200+
flushDecoder = true;
201+
}
202+
203+
do {
204+
// DECODING
205+
auto frame = vdec.decode(pkt, ec);
206+
207+
//count++;
208+
//if (count > 200)
209+
// break;
210+
211+
bool flushEncoder = false;
212+
213+
if (ec) {
214+
cerr << "Decoding error: " << ec << endl;
215+
return 1;
216+
} else if (!frame) {
217+
//cerr << "Empty frame\n";
218+
//flushDecoder = false;
219+
//continue;
220+
221+
if (flushDecoder) {
222+
flushEncoder = true;
223+
}
224+
}
225+
226+
if (frame) {
227+
clog << "Frame: pts=" << frame.pts() << " / " << frame.pts().seconds() << " / " << frame.timeBase() << ", " << frame.width() << "x" << frame.height() << ", size=" << frame.size() << ", ref=" << frame.isReferenced() << ":" << frame.refCount() << " / type: " << frame.pictureType() << endl;
228+
229+
// Change timebase
230+
frame.setTimeBase(encoder.timeBase());
231+
frame.setStreamIndex(0);
232+
frame.setPictureType();
233+
234+
clog << "Frame: pts=" << frame.pts() << " / " << frame.pts().seconds() << " / " << frame.timeBase() << ", " << frame.width() << "x" << frame.height() << ", size=" << frame.size() << ", ref=" << frame.isReferenced() << ":" << frame.refCount() << " / type: " << frame.pictureType() << endl;
235+
}
236+
237+
if (frame || flushEncoder) {
238+
do {
239+
// Encode
240+
Packet opkt = frame ? encoder.encode(frame, ec) : encoder.encode(ec);
241+
if (ec) {
242+
cerr << "Encoding error: " << ec << endl;
243+
return 1;
244+
} else if (!opkt) {
245+
//cerr << "Empty packet\n";
246+
//continue;
247+
break;
248+
}
249+
250+
// Only one output stream
251+
opkt.setStreamIndex(0);
252+
opkt.setTimeBase(1 / ost.frameRate());
253+
// Trick: when timeBase is a 1/FPS, PTS value - just an index of the packet
254+
// CUDA encoder provides wrong PTS
255+
opkt.setPts({opkt_index++, 1 / ost.frameRate()});
256+
opkt.setDts(opkt.pts());
257+
258+
clog << "Write packet: pts=" << opkt.pts() << ", dts=" << opkt.dts() << " / " << opkt.pts().seconds() << " / " << opkt.timeBase() << " / st: " << opkt.streamIndex() << endl;
259+
260+
octx.writePacket(opkt, ec);
261+
if (ec) {
262+
cerr << "Error write packet: " << ec << ", " << ec.message() << endl;
263+
return 1;
264+
}
265+
} while (flushEncoder);
266+
}
267+
268+
if (flushEncoder)
269+
break;
270+
} while (flushDecoder);
271+
272+
if (flushDecoder)
273+
break;
274+
}
275+
276+
octx.writeTrailer();
277+
}

0 commit comments

Comments
 (0)