|
|
|
|
@ -255,6 +255,57 @@ private slots:
|
|
|
|
|
#endif |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void modernCodecPlayback() { |
|
|
|
|
#ifdef HAVE_FFMPEG |
|
|
|
|
struct Fixture { uint8_t type; const char* encoder; }; |
|
|
|
|
const Fixture fixtures[] = {{STREAM_MEDIA_H265, "libx265"}, {STREAM_MEDIA_VP9, "libvpx-vp9"}, {STREAM_MEDIA_AV1, "libaom-av1"}}; |
|
|
|
|
for (const auto& fixture : fixtures) { |
|
|
|
|
const AVCodec* encoder = avcodec_find_encoder_by_name(fixture.encoder); QVERIFY2(encoder, fixture.encoder); |
|
|
|
|
AVCodecContext* context = avcodec_alloc_context3(encoder); QVERIFY(context); |
|
|
|
|
context->width = 160; context->height = 90; context->pix_fmt = AV_PIX_FMT_YUV420P; context->bit_rate = 200000; |
|
|
|
|
context->time_base = {1, 30}; context->framerate = {30, 1}; context->max_b_frames = 0; context->thread_count = 1; context->gop_size = 1; |
|
|
|
|
AVDictionary* options = nullptr; |
|
|
|
|
if (fixture.type == STREAM_MEDIA_H265) { |
|
|
|
|
av_dict_set(&options, "preset", "ultrafast", 0); av_dict_set(&options, "tune", "zerolatency", 0); |
|
|
|
|
av_dict_set(&options, "x265-params", "pools=none:frame-threads=1:log-level=error:bframes=0:repeat-headers=1", 0); |
|
|
|
|
} else { |
|
|
|
|
av_dict_set(&options, "lag-in-frames", "0", 0); av_dict_set(&options, "cpu-used", "8", 0); |
|
|
|
|
av_dict_set(&options, fixture.type == STREAM_MEDIA_VP9 ? "deadline" : "usage", "realtime", 0); |
|
|
|
|
} |
|
|
|
|
QCOMPARE(avcodec_open2(context, encoder, &options), 0); av_dict_free(&options); |
|
|
|
|
AVFrame* frame = av_frame_alloc(); QVERIFY(frame); frame->width = 160; frame->height = 90; frame->format = context->pix_fmt; |
|
|
|
|
QCOMPARE(av_frame_get_buffer(frame, 32), 0); |
|
|
|
|
for (int plane = 0; plane < 3; plane++) for (int y = 0; y < (plane ? 45 : 90); y++) |
|
|
|
|
memset(frame->data[plane] + y * frame->linesize[plane], plane ? 128 : 80, plane ? 80 : 160); |
|
|
|
|
frame->pts = 0; QCOMPARE(avcodec_send_frame(context, frame), 0); |
|
|
|
|
AVPacket* packet = av_packet_alloc(); QVERIFY(packet); |
|
|
|
|
int received = avcodec_receive_packet(context, packet); |
|
|
|
|
if (received == AVERROR(EAGAIN)) { QCOMPARE(avcodec_send_frame(context, nullptr), 0); received = avcodec_receive_packet(context, packet); } |
|
|
|
|
QCOMPARE(received, 0); QVERIFY(packet->flags & AV_PKT_FLAG_KEY); |
|
|
|
|
QByteArray video(4, '\0'); |
|
|
|
|
stream_write_be(reinterpret_cast<uint8_t*>(video.data()), context->extradata_size, 4); |
|
|
|
|
if (context->extradata_size) video.append(reinterpret_cast<const char*>(context->extradata), context->extradata_size); |
|
|
|
|
video.append(reinterpret_cast<const char*>(packet->data), packet->size); |
|
|
|
|
av_packet_free(&packet); av_frame_free(&frame); avcodec_free_context(&context); |
|
|
|
|
QCOMPARE(stream_publish_finish(publisher), STREAM_OK); stream_close(publisher); publisher = nullptr; |
|
|
|
|
QCOMPARE(stream_publish_open(inst, group, nullptr, &publisher), STREAM_OK); |
|
|
|
|
stream_info info{}; QCOMPARE(stream_get_info(publisher, &info), STREAM_OK); epoch = info.descriptor.source_epoch; |
|
|
|
|
StreamPlayerWindow window(group, inst->node_id, epoch, fixture.encoder); QSignalSpy shown(&window, &StreamPlayerWindow::framePresented); |
|
|
|
|
window.show(); |
|
|
|
|
stream_media_block media{}; media.format = {160, 90, 0, fixture.type, 0, 1}; media.video_count = 1; |
|
|
|
|
media.video[0] = {1, 0, (uint32_t)video.size(), 0, (uint32_t)video.size(), STREAM_MEDIA_KEY, |
|
|
|
|
reinterpret_cast<const uint8_t*>(video.constData())}; |
|
|
|
|
uint8_t wire[7345]; size_t length = 0; |
|
|
|
|
QCOMPARE(stream_media_encode(&media, wire, sizeof(wire), &length), STREAM_OK); |
|
|
|
|
QCOMPARE(stream_publish_block(publisher, wire, length), STREAM_OK); |
|
|
|
|
QTRY_VERIFY_WITH_TIMEOUT(shown.count() > 0, 2000); |
|
|
|
|
QCOMPARE(stream_publish_finish(publisher), STREAM_OK); |
|
|
|
|
QTRY_VERIFY_WITH_TIMEOUT(stream_get_info(publisher, &info) == STREAM_OK && info.owners == 1, 2000); |
|
|
|
|
window.close(); QTest::qWait(20); |
|
|
|
|
} |
|
|
|
|
#endif |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void finishAndDetachPendingOwner() { |
|
|
|
|
QCOMPARE(stream_publish_finish(publisher), STREAM_OK); QTRY_COMPARE(snapshots.last().mid(9), QByteArray("[]")); |
|
|
|
|
stream_close(publisher); publisher = nullptr; |
|
|
|
|
|