Simplify the structure of example directory
Only include code relevant to usage of kvzRTP and put everything in the same directory
This commit is contained in:
parent
90e8caff73
commit
fbe35d8d92
38
README.md
38
README.md
|
@ -48,43 +48,7 @@ If you want SRTP/ZRTP support, you must compile [Crypto++](https://www.cryptopp.
|
|||
|
||||
## Examples
|
||||
|
||||
We provide several simple and thoroughly commented examples on how to use kvzRTP, please see:
|
||||
|
||||
[How to create a simple RTP sender](examples/simple/rtp/sending.cc)
|
||||
|
||||
[How to use fragmented input with kvzRTP \(HEVC slices\)](examples/simple/rtp/sending_fragmented.cc)
|
||||
|
||||
[How to create a simple RTP receiver (hooking)](examples/simple/rtp/receiving_hook.cc)
|
||||
|
||||
NOTE: The hook should **not** be used for media processing. It should be rather used as interface between application and library where the frame handout happens.
|
||||
|
||||
[How to create a simple RTP receiver (polling)](examples/simple/rtp/receiving_poll.cc)
|
||||
|
||||
[How to create an RTCP instance (polling)](examples/simple/rtcp/rtcp_poll.cc)
|
||||
|
||||
[How to create an RTCP instance (hoooking)](examples/simple/rtcp/rtcp_hook.cc)
|
||||
|
||||
## Configuration
|
||||
|
||||
By default, kvzRTP does not require any configuration but if the participants are sending high-quality video, some things must be configured
|
||||
|
||||
[How to configure RTP sender for high-quality video](examples/simple/rtp/send_hq.cc)
|
||||
|
||||
[How to configure RTP receiver for high-quality video](examples/simple/rtp/recv_hq.cc)
|
||||
|
||||
[How to configure SRTP with ZRTP](examples/simple/rtp/srtp_zrtp.cc)
|
||||
|
||||
[How to configure SRTP with user-managed keys](examples/simple/rtp/srtp_user.cc)
|
||||
|
||||
### Memory ownership/deallocation
|
||||
|
||||
If you have not enabled the system call dispatcher, you don't need to worry about these
|
||||
|
||||
[Method 1, unique_ptr](examples/simple/rtp/deallocation_1.cc)
|
||||
|
||||
[Method 2, copying](examples/simple/rtp/deallocation_2.cc)
|
||||
|
||||
[Method 3, deallocation hook](examples/simple/rtp/deallocation_3.cc)
|
||||
Please see [examples](examples/) directory for different kvzRTP examples
|
||||
|
||||
## Defines
|
||||
|
||||
|
|
|
@ -1,8 +1,28 @@
|
|||
# kvzRTP example codes
|
||||
|
||||
This directory contains directories full and simple. Simple directory contains code snippets that demonstrate how the library should be used. These miminal examples also show how the library can be used, but don't themselves do anything.
|
||||
We provide several simple and thoroughly commented examples on how to use kvzRTP.
|
||||
|
||||
Full directory on the other hand shows the complete flow: these snippts encode HEVC/Opus from raw video, send this encoded data using the library and receive and reconstruct the received frames.
|
||||
These code snippets contain a lot of code unrelated to kvzRTP.
|
||||
[How to create a simple RTP sender](sending.cc)
|
||||
|
||||
[How to use fragmented input with kvzRTP \(HEVC slices\)](sending_fragmented.cc)
|
||||
|
||||
[How to create a simple RTP receiver (hooking)](receiving_hook.cc)
|
||||
|
||||
NOTE: The hook should **not** be used for media processing. It should be rather used as interface between application and library where the frame handout happens.
|
||||
|
||||
[How to create a simple RTP receiver (polling)](receiving_poll.cc)
|
||||
|
||||
[How to create an RTCP instance (polling)](rtcp_poll.cc)
|
||||
|
||||
[How to create an RTCP instance (hoooking)](rtcp_hook.cc)
|
||||
|
||||
### Memory ownership/deallocation
|
||||
|
||||
If you have not enabled the system call dispatcher, you don't need to worry about these
|
||||
|
||||
[Method 1, unique_ptr](deallocation_1.cc)
|
||||
|
||||
[Method 2, copying](deallocation_2.cc)
|
||||
|
||||
[Method 3, deallocation hook](deallocation_3.cc)
|
||||
|
||||
You can also use [Kvazzup](https://github.com/ultravideo/Kvazzup) to see a real-world example.
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
int main(void)
|
||||
{
|
||||
/* See sending.cc for more details */
|
||||
kvz_rtp::context rtp_ctx;
|
||||
|
||||
/* start session with remote at ip 10.21.25.2
|
|
@ -1,58 +0,0 @@
|
|||
# Sending
|
||||
|
||||
## HEVC sender
|
||||
|
||||
Extract 8-bit yuv420 raw video from input.mp4 and start ffplay
|
||||
|
||||
```
|
||||
ffmpeg -i input.mp4 -f rawvideo -pix_fmt yuv420p video.raw
|
||||
ffplay -protocol_whitelist "file,rtp,udp" ../sdp/hevc.sdp
|
||||
```
|
||||
|
||||
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 -lkvazaar
|
||||
./main
|
||||
```
|
||||
|
||||
## Opus sender
|
||||
|
||||
Extract signed 16-bit little endian PCM from input.mp4 and start ffplay
|
||||
|
||||
```
|
||||
ffmpeg -i input.mp4 -f s16le -acodec pcm_s16le -ac 2 -ar 48000 output.raw
|
||||
ffplay -acodec libopus -protocol_whitelist "file,rtp,udp" ../sdp/opus.sdp
|
||||
```
|
||||
|
||||
Compile the RTP Library and opus_sender.cc and start the sender
|
||||
|
||||
```
|
||||
cd ../..
|
||||
make all -j8
|
||||
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
|
||||
```
|
|
@ -1,153 +0,0 @@
|
|||
#include "../../src/lib.hh"
|
||||
#include "../../src/util.hh"
|
||||
#include "../../src/rtp_opus.hh"
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <kvazaar.h>
|
||||
#include <opus/opus.h>
|
||||
#include <stdint.h>
|
||||
|
||||
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 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;
|
||||
|
||||
if (writer->push_frame(outData, written, RTP_FORMAT_HEVC) < 0) {
|
||||
std::cerr << "RTP push failure" << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cleanup:
|
||||
t1->join();
|
||||
return 0;
|
||||
}
|
|
@ -1,141 +0,0 @@
|
|||
#include "../../src/lib.hh"
|
||||
#include "../../src/util.hh"
|
||||
#include "../../src/rtp_opus.hh"
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <kvazaar.h>
|
||||
#include <opus/opus.h>
|
||||
#include <stdint.h>
|
||||
|
||||
void receive_hook(void *arg, kvz_rtp::frame::rtp_frame *frame)
|
||||
{
|
||||
if (!frame || !arg) {
|
||||
fprintf(stderr, "invalid param\n");
|
||||
return;
|
||||
}
|
||||
|
||||
fwrite(frame->payload, frame->payload_len, 1, (FILE *)arg);
|
||||
kvz_rtp::frame::dealloc_frame(frame);
|
||||
}
|
||||
|
||||
int main(int arg, char **argv)
|
||||
{
|
||||
FILE *fp = fopen("out.hevc", "w");
|
||||
|
||||
kvz_rtp::context rtp_ctx;
|
||||
|
||||
kvz_rtp::reader *reader = rtp_ctx.create_reader("127.0.0.1", 8888, RTP_FORMAT_HEVC);
|
||||
kvz_rtp::writer *writer = rtp_ctx.create_writer("127.0.0.1", 8888, RTP_FORMAT_HEVC);
|
||||
|
||||
reader->install_recv_hook(fp, receive_hook);
|
||||
|
||||
(void)writer->start();
|
||||
(void)reader->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 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;
|
||||
|
||||
if (writer->push_frame(outData, written, RTP_FORMAT_HEVC) < 0) {
|
||||
std::cerr << "RTP push failure" << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cleanup:
|
||||
return 0;
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
v=0
|
||||
o=user 0 0 IN IP4 127.0.0.1
|
||||
s=No Name
|
||||
c=IN IP4 127.0.0.1
|
||||
t=0 0
|
||||
m=video 8888 RTP/AVP 96
|
||||
a=rtpmap:96 H265/90000
|
||||
a=recvonly
|
|
@ -1,9 +0,0 @@
|
|||
o=User 0 0 IN IP4 127.0.0.1
|
||||
s=No Name
|
||||
c=IN IP4 127.0.0.1
|
||||
t=0 0
|
||||
m=audio 8890 RTP/AVP 97
|
||||
a=rtpmap:97 opus/48000/2
|
||||
a=fmtp:97 stereo=1; useinbandfec=1; usedtx=0
|
||||
a=ptime:20
|
||||
a=maxptime:20
|
|
@ -1,130 +0,0 @@
|
|||
#include <kvzrtp/lib.hh>
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <kvazaar.h>
|
||||
#include <opus/opus.h>
|
||||
#include <stdint.h>
|
||||
#include <memory>
|
||||
|
||||
int main(void)
|
||||
{
|
||||
kvz_rtp::context 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;
|
||||
int kzzz = 0;
|
||||
|
||||
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) {
|
||||
std::unique_ptr<uint8_t[]> outData = std::unique_ptr<uint8_t[]>(new uint8_t[1024 * 200]);
|
||||
|
||||
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.get()[dataPos], chunk->data, chunk->len);
|
||||
dataPos += chunk->len;
|
||||
}
|
||||
|
||||
outputCounter = (outputCounter + 1) % 16;
|
||||
frame++;
|
||||
|
||||
if (writer->push_frame(std::move(outData), written, RTP_FORMAT_HEVC) < 0) {
|
||||
std::cerr << "RTP push failure" << std::endl;
|
||||
}
|
||||
|
||||
/* if (frame > 31) */
|
||||
/* goto cleanup; */
|
||||
}
|
||||
}
|
||||
|
||||
rtp_ctx.destroy_writer(writer);
|
||||
cleanup:
|
||||
return 0;
|
||||
}
|
|
@ -1,90 +0,0 @@
|
|||
#include "../../src/debug.hh"
|
||||
#include "../../src/lib.hh"
|
||||
#include "../../src/util.hh"
|
||||
#include "../../src/rtp_opus.hh"
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <kvazaar.h>
|
||||
#include <opus/opus.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define OPUS_BITRATE 64000
|
||||
|
||||
// 20 ms for 48 000Hz
|
||||
#define FRAME_SIZE 960
|
||||
|
||||
bool done = false;
|
||||
|
||||
void audioSender(RTPContext *ctx, int samplerate, int channels)
|
||||
{
|
||||
/* input has to PCM audio in signed 16-bit little endian format */
|
||||
FILE *inFile = fopen("output.raw", "r");
|
||||
|
||||
int error = 0;
|
||||
OpusEncoder* opusEnc = opus_encoder_create(samplerate, channels, OPUS_APPLICATION_VOIP, &error);
|
||||
opus_encoder_ctl(opusEnc, OPUS_SET_BANDWIDTH(OPUS_BANDWIDTH_FULLBAND));
|
||||
opus_encoder_ctl(opusEnc, OPUS_SET_EXPERT_FRAME_DURATION(OPUS_FRAMESIZE_20_MS));
|
||||
|
||||
if (!opusEnc) {
|
||||
std::cerr << "opus_encoder_create failed: " << error << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
RTPOpus::OpusConfig *config = new RTPOpus::OpusConfig;
|
||||
config->channels = channels;
|
||||
config->samplerate = samplerate;
|
||||
config->configurationNumber = 15; // Hydrib | FB | 20 ms
|
||||
|
||||
RTPWriter *writer = ctx->createWriter("127.0.0.1", 8890, RTP_FORMAT_OPUS);
|
||||
writer->start();
|
||||
writer->setConfig(config);
|
||||
|
||||
uint32_t dataLenPerFrame = FRAME_SIZE * channels * sizeof(uint16_t);
|
||||
uint8_t *inFrame = (uint8_t *)malloc(dataLenPerFrame);
|
||||
uint32_t outputSize = 25000;
|
||||
uint8_t *outData = (uint8_t *)malloc(outputSize);
|
||||
int frame = 0;
|
||||
|
||||
opus_int16 in[FRAME_SIZE * channels];
|
||||
|
||||
std::chrono::high_resolution_clock::time_point start = std::chrono::high_resolution_clock::now();
|
||||
std::chrono::high_resolution_clock::time_point end;
|
||||
|
||||
while (!done) {
|
||||
if (!fread(inFrame, dataLenPerFrame, 1, inFile)) {
|
||||
done = true;
|
||||
} else {
|
||||
/* convert from little endian ordering */
|
||||
for (int i = 0; i < channels * FRAME_SIZE; ++i) {
|
||||
in[i] = inFrame[2 * i + 1] << 8 | inFrame[2 * i];
|
||||
}
|
||||
|
||||
int32_t len = opus_encode(opusEnc, in, FRAME_SIZE, outData, outputSize);
|
||||
|
||||
// 20 ms per frame
|
||||
if (writer->pushFrame(outData, len, RTP_FORMAT_OPUS, 960 * frame) < 0) {
|
||||
std::cerr << "Failed to push Opus frame!" << std::endl;
|
||||
}
|
||||
|
||||
frame++;
|
||||
end = std::chrono::high_resolution_clock::now();
|
||||
auto elapsed_time = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
|
||||
double diff = 20.0 - (elapsed_time / (double)frame);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds((int)diff));
|
||||
}
|
||||
}
|
||||
|
||||
opus_encoder_destroy(opusEnc);
|
||||
return;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
RTPContext rtp_ctx;
|
||||
std::thread *t = new std::thread(audioSender, &rtp_ctx, 48000, 2);
|
||||
|
||||
t->join();
|
||||
std::cerr << "here" << std::endl;
|
||||
}
|
|
@ -1,8 +1,6 @@
|
|||
#include <kvzrtp/lib.hh>
|
||||
#include <thread>
|
||||
|
||||
#define USE_RECV_HOOK
|
||||
|
||||
void receive_hook(void *arg, kvz_rtp::frame::rtp_frame *frame)
|
||||
{
|
||||
if (!frame) {
|
|
@ -1,6 +0,0 @@
|
|||
# Simple examples
|
||||
|
||||
rtp directory contains examples showing how the RTP interface of the library should be used.
|
||||
The examples are very simple and all they do is demonstrate how the library can be used.
|
||||
|
||||
rtcp directory contains RTCP examples
|
|
@ -1,31 +0,0 @@
|
|||
#include <kvzrtp/lib.hh>
|
||||
|
||||
void receive_hook(void *arg, kvz_rtp::frame::rtp_frame *frame)
|
||||
{
|
||||
(void)kvz_rtp::frame::dealloc_frame(frame);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
/* Enable system call dispatcher to improve sending speed */
|
||||
kvz_rtp::context ctx;
|
||||
|
||||
kvz_rtp::reader *reader = ctx.create_reader("127.0.0.1", 5566, RTP_FORMAT_GENERIC);
|
||||
|
||||
/* Enable optimistic fragment receiver */
|
||||
reader->configure(RCE_OPTIMISTIC_RECEIVER);
|
||||
|
||||
/* Increase the send UDP buffer size to 40 MB */
|
||||
reader->configure(RCC_UDP_BUF_SIZE, 40 * 1024 * 1024);
|
||||
|
||||
/* Allocate space for 30 frames at the beginning of frame before they're
|
||||
* spilled to temporary frames */
|
||||
reader->configure(RCC_PROBATION_ZONE_SIZE, 30);
|
||||
|
||||
reader->install_recv_hook(NULL, receive_hook);
|
||||
|
||||
(void)reader->start();
|
||||
(void)ctx.destroy_reader(reader);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
#include <kvzrtp/lib.hh>
|
||||
|
||||
#define PAYLOAD_MAXLEN 1 * 1000 * 1000
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
kvz_rtp::context ctx;
|
||||
|
||||
kvz_rtp::writer *writer = ctx.create_writer("127.0.0.1", 5566, 8888, RTP_FORMAT_HEVC);
|
||||
|
||||
/* Enable system call dispatcher to improve sending speed */
|
||||
writer->configure(RCE_SYSTEM_CALL_DISPATCHER);
|
||||
|
||||
/* Increase the send UDP buffer size to 40 MB */
|
||||
writer->configure(RCC_UDP_BUF_SIZE, 40 * 1024 * 1024);
|
||||
|
||||
/* Cache 30 transactions to prevent constant (de)allocation */
|
||||
writer->configure(RCC_MAX_TRANSACTIONS, 30);
|
||||
|
||||
/* Set max size for one input frame to 1.4 MB (1441 * 1000)
|
||||
* (1.4 MB frame would equal 43 MB bitrate for a 30 FPS video which is very large) */
|
||||
writer->configure(RCC_MAX_MESSAGES, 1000);
|
||||
|
||||
/* Before the writer can be used, it must be started.
|
||||
* This initializes the underlying socket and all needed data structures */
|
||||
(void)writer->start();
|
||||
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
|
||||
/* We're using System Call Dispatcher so we must adhere to the memory ownership/deallocation
|
||||
* rules defined in README.md
|
||||
*
|
||||
* Easiest way is to use smart pointers (as done here). If this memory was, however, received
|
||||
* from f.ex. HEVC encoder directly and was not wrapped in a smart pointer, we could either
|
||||
* install a deallocation hook for the memory or pass RTP_COPY to push_frame() to force kvzRTP
|
||||
* to make a copy of the memory */
|
||||
auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[PAYLOAD_MAXLEN]);
|
||||
|
||||
/* We're using */
|
||||
if (writer->push_frame(std::move(buffer), PAYLOAD_MAXLEN, RTP_NO_FLAGS) != RTP_OK) {
|
||||
fprintf(stderr, "Failed to send RTP frame!");
|
||||
}
|
||||
}
|
||||
|
||||
/* Writer must be destroyed manually */
|
||||
ctx.destroy_writer(writer);
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue