Skip to content

Commit

Permalink
example encoding from BGR0 input
Browse files Browse the repository at this point in the history
- for troubleshooting #30
  • Loading branch information
bmegli committed Oct 5, 2023
1 parent b738ccf commit 20ea76d
Show file tree
Hide file tree
Showing 3 changed files with 182 additions and 0 deletions.
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,8 @@ install(FILES hve.h DESTINATION include)
add_executable(hve-encode-raw-h264 examples/hve_encode_raw_h264.c)
target_link_libraries(hve-encode-raw-h264 hve)

add_executable(hve-encode-raw-h264-bgr0 examples/hve_encode_raw_h264_bgr0.c)
target_link_libraries(hve-encode-raw-h264-bgr0 hve)

add_executable(hve-encode-raw-hevc10 examples/hve_encode_raw_hevc10.c)
target_link_libraries(hve-encode-raw-hevc10 hve)
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,17 @@ make
./hve-encode-raw-h264 10 libx264
```

```bash
# ./hve-encode-raw-h264-bgr0 <seconds> [encoder] [device]
## h264_vaapi + default device or specify
./hve-encode-raw-h264-bgr0 10
./hve-encode-raw-h264-bgr0 10 h264_vaapi
./hve-encode-raw-h264-bgr0 10 h264_vaapi /dev/dri/renderD128

## Nvidia NVENC
./hve-encode-raw-h264-bgr0 10 h264_nvenc
```

``` bash
# ./hve-encode-raw-hevc10 <seconds> [encoder] [device]
## hevc_vaapi + default device or specify
Expand Down
168 changes: 168 additions & 0 deletions examples/hve_encode_raw_h264_bgr0.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
/*
* HVE Hardware Video Encoder library example of encoding through VAAPI or NVENC to H.264
*
* Copyright 2019-2023 (C) Bartosz Meglicki <[email protected]>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/

#include <stdio.h> //printf, fprintf
#include <inttypes.h> //uint8_t

#include "../hve.h"

const int WIDTH=1280;
const int HEIGHT=720;
const int INPUT_WIDTH=1280; //optional hardware scaling if different from width
const int INPUT_HEIGHT=720; //optional hardware scaling if different from height
const int FRAMERATE=30;
int SECONDS=10;
const char *DEVICE=NULL; //NULL for default or device e.g. "/dev/dri/renderD128"
const char *ENCODER=NULL;//NULL for default (h264_vaapi) or FFmpeg encoder e.g. "h264_vaapi", "hevc_vaapi", "h264_nvenc", "hevc_nvenc", ...
const char *PIXEL_FORMAT="bgr0"; //NULL for default (NV12) or pixel format e.g. "rgb0" (has to be supported by encoder)
const int PROFILE=FF_PROFILE_H264_HIGH; //or FF_PROFILE_HEVC_MAIN, FF_PROFILE_H264_CONSTRAINED_BASELINE, ...
const int BFRAMES=0; //max_b_frames, set to 0 to minimize latency, non-zero to minimize size
const int BITRATE=0; //average bitrate in VBR mode (bit_rate != 0 and qp == 0)
const int QP=0; //quantization parameter in CQP mode (qp != 0 and bit_rate == 0)
const int GOP_SIZE=0; //group of pictures size, 0 for default (determines keyframe period)
const int COMPRESSION_LEVEL=0; //encoder/codec dependent, 0 for default, for VAAPI 1-7 speed-quality tradeoff, 1 highest quality, 7 fastest
const int VAAPI_LOW_POWER=0; //alternative VAAPI limited low-power encoding path if non-zero
const char *NVENC_PRESET=NULL; //NVENC and codec specific, NULL / "" or like "default", "slow", "medium", "fast", "hp", "hq", "bd", "ll", "llhq", "llhp", "lossless", "losslesshp"
const int NVENC_DELAY=0; //NVENC specific delay of frame output, 0 for default, -1 for 0 or positive value, set -1 to minimize latency
const int NVENC_ZEROLATENCY=0; //NVENC specific no reordering delay if non-zero, enable to minimize latency

int encoding_loop(struct hve *hardware_encoder, FILE *output_file);
int process_user_input(int argc, char* argv[]);
int hint_user_on_failure(char *argv[]);
void hint_user_on_success();

int main(int argc, char* argv[])
{
//get SECONDS and DEVICE from the command line
if( process_user_input(argc, argv) < 0 )
return -1;

//prepare library data
struct hve_config hardware_config = {WIDTH, HEIGHT, INPUT_WIDTH, INPUT_HEIGHT, FRAMERATE,
DEVICE, ENCODER, PIXEL_FORMAT, PROFILE, BFRAMES,
BITRATE, QP, GOP_SIZE, COMPRESSION_LEVEL,
VAAPI_LOW_POWER,
NVENC_PRESET, NVENC_DELAY, NVENC_ZEROLATENCY};

struct hve *hardware_encoder;

//prepare file for raw H.264 output
FILE *output_file = fopen("output.h264", "w+b");
if(output_file == NULL)
return fprintf(stderr, "unable to open file for output\n");

//initialize library with hve_init
if( (hardware_encoder = hve_init(&hardware_config)) == NULL )
{
fclose(output_file);
return hint_user_on_failure(argv);
}

//do the actual encoding
int status = encoding_loop(hardware_encoder, output_file);

hve_close(hardware_encoder);
fclose(output_file);

if(status == 0)
hint_user_on_success();

return 0;
}

int encoding_loop(struct hve *hardware_encoder, FILE *output_file)
{
struct hve_frame frame = { 0 };
int frames=SECONDS*FRAMERATE, f, failed;

//we are working with bgr0 because we specified bgr0 pixel format
//when calling hve_init, in principle we could use other format
//if hardware supported it (e.g. RGB0 is supported on my Intel)
const int BPP = 4; //BGR0 is 4 bpp, alpha channel is ignored
uint8_t BGR0[INPUT_WIDTH*INPUT_HEIGHT*BPP]; //dummy BGR0 input

//fill with your stride (width including padding if any)
frame.linesize[0] = INPUT_WIDTH * BPP;

//encoded data is returned in FFmpeg packet
AVPacket *packet;

for(f=0;f<frames;++f)
{
//prepare dummy image data, normally you would take it from camera or other source
memset(BGR0, f % 255, INPUT_WIDTH*INPUT_HEIGHT * BPP); //BGR0 (ride through greyscale, alpha is ignored)

//fill hve_frame with pointers to your data in BGR0 pixel format
frame.data[0]=BGR0;

//encode this frame
if( hve_send_frame(hardware_encoder, &frame) != HVE_OK)
break; //break on error

while( (packet=hve_receive_packet(hardware_encoder, &failed)) )
{
//packet.data is H.264 encoded frame of packet.size length
//here we are dumping it to raw H.264 file as example
//yes, we ignore the return value of fwrite for simplicty
//it could also fail in harsh real world...
fwrite(packet->data, packet->size, 1, output_file);
}

//NULL packet and non-zero failed indicates failure during encoding
if(failed)
break; //break on error
}

//flush the encoder by sending NULL frame, encode some last frames returned from hardware
hve_send_frame(hardware_encoder, NULL);
while( (packet=hve_receive_packet(hardware_encoder, &failed)) )
fwrite(packet->data, packet->size, 1, output_file);

//did we encode everything we wanted?
//convention 0 on success, negative on failure
return f == frames ? 0 : -1;
}

int process_user_input(int argc, char* argv[])
{
if(argc < 2)
{
fprintf(stderr, "Usage: %s <seconds> [encoder] [device]\n", argv[0]);
fprintf(stderr, "\nexamples:\n");
fprintf(stderr, "%s 10\n", argv[0]);
fprintf(stderr, "%s 10 h264_vaapi\n", argv[0]);
fprintf(stderr, "%s 10 h264_vaapi /dev/dri/renderD128\n", argv[0]);
fprintf(stderr, "%s 10 h264_nvenc\n", argv[0]);
fprintf(stderr, "%s 10 libx264 # (software encoder)\n", argv[0]);

return -1;
}

SECONDS = atoi(argv[1]);
if(argc >= 3) ENCODER = argv[2];
if(argc >= 4) DEVICE = argv[3];

return 0;
}

int hint_user_on_failure(char *argv[])
{
fprintf(stderr, "unable to initalize encoder, try to specify device e.g:\n\n");
fprintf(stderr, "%s 10 h264_vaapi /dev/dri/renderD128\n", argv[0]);
return -1;
}
void hint_user_on_success()
{
printf("finished successfully\n");
printf("output written to \"output.h264\" file\n");
printf("test with:\n\n");
printf("ffplay output.h264\n");
}

0 comments on commit 20ea76d

Please sign in to comment.