Skip to content

Commit

Permalink
Netint hardware support
Browse files Browse the repository at this point in the history
  • Loading branch information
cyberj0g committed Apr 1, 2022
1 parent 9fc0abf commit ef9c733
Show file tree
Hide file tree
Showing 9 changed files with 170 additions and 75 deletions.
7 changes: 5 additions & 2 deletions cmd/transcoding/transcoding.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,15 @@ func main() {
var err error
args := append([]string{os.Args[0]}, flag.Args()...)
if len(args) <= 3 {
panic("Usage: [-hevc] [-from dur] [-to dur] <input file> <output renditions, comma separated> <sw/nv>")
panic("Usage: [-hevc] [-from dur] [-to dur] <input file> <output renditions, comma separated> <sw/nv/nt>")
}
str2accel := func(inp string) (ffmpeg.Acceleration, string) {
if inp == "nv" {
return ffmpeg.Nvidia, "nv"
}
if inp == "nt" {
return ffmpeg.Netint, "nt"
}
return ffmpeg.Software, "sw"
}
str2profs := func(inp string) []ffmpeg.VideoProfile {
Expand Down Expand Up @@ -72,7 +75,7 @@ func main() {
options := profs2opts(profiles)

var dev string
if accel == ffmpeg.Nvidia {
if accel != ffmpeg.Software {
if len(args) <= 4 {
panic("Expected device number")
}
Expand Down
65 changes: 33 additions & 32 deletions ffmpeg/decoder.c
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ static int send_first_pkt(struct input_ctx *ictx)
if (ictx->flushed) return 0;
if (!ictx->first_pkt) return lpms_ERR_INPUT_NOKF;

//LPMS_WARN("sending flush packet NOW !");

int ret = avcodec_send_packet(ictx->vc, ictx->first_pkt);
ictx->sentinel_count++;
if (ret < 0) {
Expand Down Expand Up @@ -85,28 +87,30 @@ int process_in(struct input_ctx *ictx, AVFrame *frame, AVPacket *pkt)
// with video. If there's a nonzero response type, we know there are no more
// video frames, so continue on to audio.

// Flush video decoder.
// To accommodate CUDA, we feed the decoder sentinel (flush) frames, till we
// Flush video decoder
// To accommodate CUDA, we feed the decoder sentinel (flush) frames, till we
// get back all sent frames, or we've made SENTINEL_MAX attempts to retrieve
// buffered frames with no success.
// TODO this is unnecessary for SW decoding! SW process should match audio
if (ictx->vc && !ictx->flushed && ictx->pkt_diff > 0) {
ictx->flushing = 1;
ret = send_first_pkt(ictx);
if (ret < 0) {
ictx->flushed = 1;
return ret;
}
ret = lpms_receive_frame(ictx, ictx->vc, frame);
pkt->stream_index = ictx->vi;
// Keep flushing if we haven't received all frames back but stop after SENTINEL_MAX tries.
if (ictx->pkt_diff != 0 && ictx->sentinel_count <= SENTINEL_MAX && (!ret || ret == AVERROR(EAGAIN))) {
return 0; // ignore actual return value and keep flushing
} else {
ictx->flushed = 1;
if (!ret) return ret;
}
}
// last stable with Netint:
//if (ictx->hw_type != AV_HWDEVICE_TYPE_MEDIACODEC) {
if (ictx->vc && !ictx->flushed && ictx->pkt_diff > 0) {
ictx->flushing = 1;
ret = send_first_pkt(ictx);
if (ret < 0) {
ictx->flushed = 1;
return ret;
}
ret = lpms_receive_frame(ictx, ictx->vc, frame);
pkt->stream_index = ictx->vi;
// Keep flushing if we haven't received all frames back but stop after SENTINEL_MAX tries.
if (ictx->pkt_diff != 0 && ictx->sentinel_count <= SENTINEL_MAX && (!ret || ret == AVERROR(EAGAIN))) {
return 0; // ignore actual return value and keep flushing
} else {
ictx->flushed = 1;
}
}
//}
// Flush audio decoder.
if (ictx->ac) {
avcodec_send_packet(ictx->ac, NULL);
Expand Down Expand Up @@ -165,16 +169,6 @@ static enum AVPixelFormat get_hw_pixfmt(AVCodecContext *vc, const enum AVPixelFo
ret = av_hwframe_ctx_init(vc->hw_frames_ctx);
if (AVERROR(ENOSYS) == ret) ret = lpms_ERR_INPUT_PIXFMT; // most likely
if (ret < 0) LPMS_ERR(pixfmt_cleanup, "Unable to initialize a hardware frame pool");

/*
fprintf(stderr, "selected format: hw %s sw %s\n",
av_get_pix_fmt_name(frames->format), av_get_pix_fmt_name(frames->sw_format));
const enum AVPixelFormat *p;
for (p = pix_fmts; *p != -1; p++) {
fprintf(stderr,"possible format: %s\n", av_get_pix_fmt_name(*p));
}
*/

return frames->format;

pixfmt_cleanup:
Expand Down Expand Up @@ -231,6 +225,7 @@ int open_video_decoder(input_params *params, struct input_ctx *ctx)
{
int ret = 0;
AVCodec *codec = NULL;
AVDictionary **opts = NULL;
AVFormatContext *ic = ctx->ic;
// open video decoder
ctx->vi = av_find_best_stream(ic, AVMEDIA_TYPE_VIDEO, -1, -1, &codec, 0);
Expand All @@ -253,6 +248,11 @@ int open_video_decoder(input_params *params, struct input_ctx *ctx)
ret = lpms_ERR_INPUT_PIXFMT;
LPMS_ERR(open_decoder_err, "Non 4:2:0 pixel format detected in input");
}
} else if (params->video.name && strlen(params->video.name) != 0) {
// Try to find user specified decoder by name
AVCodec *c = avcodec_find_decoder_by_name(params->video.name);
if (c) codec = c;
if (params->video.opts) opts = &params->video.opts;
}
AVCodecContext *vc = avcodec_alloc_context3(codec);
if (!vc) LPMS_ERR(open_decoder_err, "Unable to alloc video codec");
Expand All @@ -261,16 +261,17 @@ int open_video_decoder(input_params *params, struct input_ctx *ctx)
if (ret < 0) LPMS_ERR(open_decoder_err, "Unable to assign video params");
vc->opaque = (void*)ctx;
// XXX Could this break if the original device falls out of scope in golang?
if (params->hw_type != AV_HWDEVICE_TYPE_NONE) {
if (params->hw_type == AV_HWDEVICE_TYPE_CUDA) {
// First set the hw device then set the hw frame
ret = av_hwdevice_ctx_create(&ctx->hw_device_ctx, params->hw_type, params->device, NULL, 0);
if (ret < 0) LPMS_ERR(open_decoder_err, "Unable to open hardware context for decoding")
ctx->hw_type = params->hw_type;
vc->hw_device_ctx = av_buffer_ref(ctx->hw_device_ctx);
vc->get_format = get_hw_pixfmt;
}
ctx->hw_type = params->hw_type;
vc->pkt_timebase = ic->streams[ctx->vi]->time_base;
ret = avcodec_open2(vc, codec, NULL);
av_opt_set(vc->priv_data, "xcoder-params", ctx->xcoderParams, 0);
ret = avcodec_open2(vc, codec, opts);
if (ret < 0) LPMS_ERR(open_decoder_err, "Unable to open video decoder");
}

Expand Down
2 changes: 2 additions & 0 deletions ffmpeg/decoder.h
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavutil/opt.h>
#include "transcoder.h"

struct input_ctx {
Expand All @@ -16,6 +17,7 @@ struct input_ctx {
AVBufferRef *hw_device_ctx;
enum AVHWDeviceType hw_type;
char *device;
char *xcoderParams;

// Decoder flush
AVPacket *first_pkt;
Expand Down
18 changes: 13 additions & 5 deletions ffmpeg/encoder.c
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ void close_output(struct output_ctx *octx)
avformat_free_context(octx->oc);
octx->oc = NULL;
}
if (octx->vc && AV_HWDEVICE_TYPE_NONE == octx->hw_type) avcodec_free_context(&octx->vc);
if (octx->vc && (octx->hw_type == AV_HWDEVICE_TYPE_NONE)) avcodec_free_context(&octx->vc);
if (octx->ac) avcodec_free_context(&octx->ac);
octx->af.flushed = octx->vf.flushed = 0;
octx->af.flushing = octx->vf.flushing = 0;
Expand Down Expand Up @@ -252,6 +252,11 @@ int open_output(struct output_ctx *octx, struct input_ctx *ictx)
}
vc->pix_fmt = av_buffersink_get_format(octx->vf.sink_ctx); // XXX select based on encoder + input support
if (fmt->flags & AVFMT_GLOBALHEADER) vc->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
if(strcmp(ictx->xcoderParams,"")!=0){
av_opt_set(vc->priv_data, "xcoder-params", ictx->xcoderParams, 0);
//printf("xcoder-params %s\n", ictx->xcoderParams);
}

ret = avcodec_open2(vc, codec, &octx->video->opts);
if (ret < 0) LPMS_ERR(open_output_err, "Error opening video encoder");
octx->hw_type = ictx->hw_type;
Expand Down Expand Up @@ -345,14 +350,15 @@ static int encode(AVCodecContext* encoder, AVFrame *frame, struct output_ctx* oc

// We don't want to send NULL frames for HW encoding
// because that closes the encoder: not something we want
if (AV_HWDEVICE_TYPE_NONE == octx->hw_type || AVMEDIA_TYPE_AUDIO == ost->codecpar->codec_type || frame) {
if (AV_HWDEVICE_TYPE_NONE == octx->hw_type || AV_HWDEVICE_TYPE_MEDIACODEC == octx->hw_type ||
AVMEDIA_TYPE_AUDIO == ost->codecpar->codec_type || frame) {
ret = avcodec_send_frame(encoder, frame);
if (AVERROR_EOF == ret) ; // continue ; drain encoder
else if (ret < 0) LPMS_ERR(encode_cleanup, "Error sending frame to encoder");
}

if (AVMEDIA_TYPE_VIDEO == ost->codecpar->codec_type &&
AV_HWDEVICE_TYPE_CUDA == octx->hw_type && !frame) {
(AV_HWDEVICE_TYPE_CUDA == octx->hw_type) && !frame) {
avcodec_flush_buffers(encoder);
}

Expand Down Expand Up @@ -527,6 +533,7 @@ int process_out(struct input_ctx *ictx, struct output_ctx *octx, AVCodecContext
frame->pict_type = AV_PICTURE_TYPE_I;
octx->next_kf_pts = frame->pts + octx->gop_pts_len;
}

if(octx->is_dnn_profile) {
ret = getmetadatainf(frame, octx);
} else {
Expand All @@ -540,8 +547,9 @@ int process_out(struct input_ctx *ictx, struct output_ctx *octx, AVCodecContext
av_frame_unref(frame);
// For HW we keep the encoder open so will only get EAGAIN.
// Return EOF in place of EAGAIN for to terminate the flush
if (frame == NULL && AV_HWDEVICE_TYPE_NONE != octx->hw_type &&
AVERROR(EAGAIN) == ret && !inf) return AVERROR_EOF;
if (frame == NULL && octx->hw_type > AV_HWDEVICE_TYPE_NONE &&
AV_HWDEVICE_TYPE_MEDIACODEC != octx->hw_type &&
AVERROR(EAGAIN) == ret && !inf) return AVERROR_EOF;
if (frame == NULL) return ret;
}

Expand Down
34 changes: 17 additions & 17 deletions ffmpeg/extras.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ struct match_info {
int height;
uint64_t bit_rate;
int packetcount; //video total packet count
uint64_t timestamp; //XOR sum of avpacket pts
uint64_t timestamp; //XOR sum of avpacket pts
int audiosum[4]; //XOR sum of audio data's md5(16 bytes)
};

Expand Down Expand Up @@ -183,10 +183,10 @@ int lpms_get_codec_info(char *fname, pcodec_info out)
return ret;
}

// compare two signature files whether those matches or not.
// @param signpath1 full path of the first signature file.
// @param signpath2 full path of the second signature file.
// @return <0: error 0: no matchiing 1: partial matching 2: whole matching.
//// compare two signature files whether those matches or not.
//// @param signpath1 full path of the first signature file.
//// @param signpath2 full path of the second signature file.
//// @return <0: error 0: no matchiing 1: partial matching 2: whole matching.

int lpms_compare_sign_bypath(char *signpath1, char *signpath2)
{
Expand Down Expand Up @@ -268,7 +268,7 @@ static int read_packet(void *opaque, uint8_t *buf, int buf_size)
return buf_size;
}

static int get_matchinfo(void *buffer, int len, struct match_info* info)
static int get_matchinfo(void *buffer, int len, struct match_info* info)
{
int ret = 0;
AVFormatContext* ifmt_ctx = NULL;
Expand All @@ -292,7 +292,7 @@ static int get_matchinfo(void *buffer, int len, struct match_info* info)
LPMS_ERR(clean, "Error allocating buffer");
}

avio_in = avio_alloc_context(avio_ctx_buffer, avio_ctx_buffer_size, 0, &bd, &read_packet, NULL, NULL);
avio_in = avio_alloc_context(avio_ctx_buffer, avio_ctx_buffer_size, 0, &bd, &read_packet, NULL, NULL);
if (!avio_ctx_buffer) {
ret = AVERROR(ENOMEM);
LPMS_ERR(clean, "Error allocating context");
Expand All @@ -304,15 +304,15 @@ static int get_matchinfo(void *buffer, int len, struct match_info* info)
}
ifmt_ctx->pb = avio_in;
ifmt_ctx->flags = AVFMT_FLAG_CUSTOM_IO;

if ((ret = avformat_open_input(&ifmt_ctx, "", NULL, NULL)) < 0) {
LPMS_ERR(clean, "Cannot open input video file\n");
}

if ((ret = avformat_find_stream_info(ifmt_ctx, NULL)) < 0) {
LPMS_ERR(clean, "Cannot find stream information\n");
}

for (int i = 0; i < ifmt_ctx->nb_streams; i++) {
AVStream *stream;
stream = ifmt_ctx->streams[i];
Expand All @@ -323,18 +323,18 @@ static int get_matchinfo(void *buffer, int len, struct match_info* info)
info->bit_rate = in_codecpar->bit_rate;
}
else if (in_codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
audioid = i;
audioid = i;
}
}
packet = av_packet_alloc();
packet = av_packet_alloc();
if (!packet) LPMS_ERR(clean, "Error allocating packet");
while (1) {
ret = av_read_frame(ifmt_ctx, packet);
if (ret == AVERROR_EOF) {
ret = 0;
break;
}
else if (ret < 0) {
else if (ret < 0) {
LPMS_ERR(clean, "Unable to read input");
}
info->packetcount++;
Expand All @@ -345,8 +345,8 @@ static int get_matchinfo(void *buffer, int len, struct match_info* info)
}
av_packet_unref(packet);
}
clean:

clean:
if(packet)
av_packet_free(&packet);
/* note: the internal buffer could have changed, and be != avio_ctx_buffer */
Expand All @@ -365,7 +365,7 @@ static int get_matchinfo(void *buffer, int len, struct match_info* info)
// @return <0: error =0: matching 1: no matching
int lpms_compare_video_bybuffer(void *buffer1, int len1, void *buffer2, int len2)
{
int ret = 0;
int ret = 0;
struct match_info info1, info2;

ret = get_matchinfo(buffer1,len1,&info1);
Expand All @@ -374,7 +374,7 @@ int lpms_compare_video_bybuffer(void *buffer1, int len1, void *buffer2, int len2
ret = get_matchinfo(buffer2,len2,&info2);
if(ret < 0) return ret;
//compare two matching information
if (info1.width != info2.width || info1.height != info2.height ||
if (info1.width != info2.width || info1.height != info2.height ||
info1.bit_rate != info2.bit_rate || info1.packetcount != info2.packetcount ||
info1.timestamp != info2.timestamp || memcmp(info1.audiosum, info2.audiosum, 16)) {
ret = 1;
Expand Down
Loading

0 comments on commit ef9c733

Please sign in to comment.