From ab303a42c2c5e7a31af620cad43e009156ab95e4 Mon Sep 17 00:00:00 2001 From: Aaro Altonen Date: Mon, 3 Jun 2019 11:49:07 +0300 Subject: [PATCH] Add example code for receiving HEVC stream --- examples/README.md | 21 +++- examples/receiving/recv_example_1.cc | 155 +++++++++++++++++++++++++++ 2 files changed, 175 insertions(+), 1 deletion(-) create mode 100644 examples/receiving/recv_example_1.cc diff --git a/examples/README.md b/examples/README.md index 467977e..adbca21 100644 --- a/examples/README.md +++ b/examples/README.md @@ -15,7 +15,7 @@ Compile the RTP Library and hevc_sender.cc and start the sender cd ../.. make all -j8 cd examples/sending -g++ -o main hevc_sender.cc -lrtp -L ../.. -lpthread -lopus +g++ -o main hevc_sender.cc -lrtp -L ../.. -lpthread -lkvazaar ./main ``` @@ -37,3 +37,22 @@ cd examples/sending g++ -o main opus_sender.cc -lrtp -L ../.. -lpthread -lopus ./main ``` + +# Receiving + +## HEVC sender/receiver +Extract 8-bit yuv420 raw video from input.mp4 + +``` +ffmpeg -i input.mp4 -f rawvideo -pix_fmt yuv420p video.raw +``` + +Compile the RTP Library and recv_example_1.cc or recv_example_2.cc and start the sender + +``` +cd ../.. +make all -j8 +cd examples/receiving +g++ -o main recv_example_1.cc -lrtp -L ../.. -lpthread -lkvazaar +./main +``` diff --git a/examples/receiving/recv_example_1.cc b/examples/receiving/recv_example_1.cc new file mode 100644 index 0000000..35c2152 --- /dev/null +++ b/examples/receiving/recv_example_1.cc @@ -0,0 +1,155 @@ +#include "../../src/lib.hh" +#include "../../src/util.hh" +#include "../../src/rtp_opus.hh" +#include +#include +#include +#include +#include +#include +#include + +void recv_thread(kvz_rtp::context *ctx) +{ + FILE *fp = fopen("out.hevc", "w"); + + if (!fp) { + fprintf(stderr, "failed to open file\n"); + return; + } + + kvz_rtp::frame::rtp_frame *frame = nullptr; + kvz_rtp::reader *reader = ctx->create_reader("127.0.0.1", 8888, RTP_FORMAT_HEVC); + (void)reader->start(); + int i = 0; + + uint8_t hevc_start_codes[4] = { 0x0, 0x0, 0x0, 0x1 }; + + while ((frame = reader->pull_frame()) != nullptr && i != 30) { + fwrite(hevc_start_codes, sizeof(hevc_start_codes), 1, fp); + fwrite(frame->payload, frame->payload_len, 1, fp); + kvz_rtp::frame::dealloc_frame(frame); + i++; + } + + fclose(fp); + std::cerr << "\n\n\ndone!\n\n\n"; +} + +int main(int arg, char **argv) +{ + kvz_rtp::context rtp_ctx; + + std::thread *t1 = new std::thread(recv_thread, &rtp_ctx); + + kvz_rtp::writer *writer = rtp_ctx.create_writer("127.0.0.1", 8888, RTP_FORMAT_HEVC); + (void)writer->start(); + + uint8_t *buffer = (uint8_t *)malloc(500); + FILE *inputFile = fopen("video.raw", "r"); + size_t r = 0; + + int width = 720; + int height = 720; + + kvz_encoder* enc = NULL; + const kvz_api * const api = kvz_api_get(8); + kvz_config* config = api->config_alloc(); + api->config_init(config); + api->config_parse(config, "preset", "ultrafast"); + config->width = width; + config->height = height; + config->hash = kvz_hash::KVZ_HASH_NONE; + config->intra_period = 5; + config->vps_period = 1; + config->qp = 32; + config->framerate_num = 30; + config->framerate_denom = 1; + + enc = api->encoder_open(config); + if (!enc) { + fprintf(stderr, "Failed to open encoder.\n"); + return EXIT_FAILURE; + } + + kvz_picture *img_in[16]; + for (uint32_t i = 0; i < 16; ++i) { + img_in[i] = api->picture_alloc_csp(KVZ_CSP_420, width, height); + } + + uint8_t inputCounter = 0; + uint8_t outputCounter = 0; + uint32_t frame = 0; + uint32_t frameIn = 0; + bool done = false; + uint8_t *outData = (uint8_t *)malloc(1024 * 1024); + + std::chrono::high_resolution_clock::time_point start = std::chrono::high_resolution_clock::now(); + std::chrono::high_resolution_clock::time_point end = std::chrono::high_resolution_clock::now(); + + + while (!done) { + kvz_data_chunk* chunks_out = NULL; + kvz_picture *img_rec = NULL; + kvz_picture *img_src = NULL; + uint32_t len_out = 0; + kvz_frame_info info_out; + + if (!fread(img_in[inputCounter]->y, width*height, 1, inputFile)) { + done = true; + continue; + } + if (!fread(img_in[inputCounter]->u, width*height>>2, 1, inputFile)) { + done = true; + continue; + } + if (!fread(img_in[inputCounter]->v, width*height>>2, 1, inputFile)) { + done = true; + continue; + } + + if (!api->encoder_encode(enc, + img_in[inputCounter], + &chunks_out, &len_out, &img_rec, &img_src, &info_out)) + { + fprintf(stderr, "Failed to encode image.\n"); + for (uint32_t i = 0; i < 16; i++) { + api->picture_free(img_in[i]); + } + return EXIT_FAILURE; + } + inputCounter = (inputCounter + 1) % 16; + + + if (chunks_out == NULL && img_in == NULL) { + /* We are done since there is no more input and output left. */ + goto cleanup; + } + + if (chunks_out != NULL) { + uint64_t written = 0; + uint32_t dataPos = 0; + + /* Write data into the output file. */ + for (kvz_data_chunk *chunk = chunks_out; chunk != NULL; chunk = chunk->next) { + written += chunk->len; + } + + for (kvz_data_chunk *chunk = chunks_out; chunk != NULL; chunk = chunk->next) { + memcpy(&outData[dataPos], chunk->data, chunk->len); + dataPos += chunk->len; + } + + outputCounter = (outputCounter + 1) % 16; + frame++; + + if (writer->push_frame(outData, written, RTP_FORMAT_HEVC, (90001 / 24) * frame) < 0) { + std::cerr << "RTP push failure" << std::endl; + } + } + } + +cleanup: + t1->join(); + return 0; +}