Skip to content

Commit

Permalink
Merge pull request Streampunk#109 from piercus/set-logging-callback
Browse files Browse the repository at this point in the history
Set logging callback
  • Loading branch information
scriptorian authored Nov 12, 2022
2 parents aee5ff9 + af09d68 commit 7b5d272
Show file tree
Hide file tree
Showing 7 changed files with 235 additions and 68 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,11 @@ To control the level of logging from FFmpeg you can use the `beamcoder.logging()
* `debug` - stuff which is only useful for libav* developers
* `trace` - extremely verbose debugging for libav* developers

To bypass the default ffmpeg logger you can use `beamcoder.setLoggingCallback()`. For example, to log all the messages using `console.log` :
```javascript
beamcoder.setLoggingCallback(msg => console.log(msg))
```

### Demuxing

The process of demuxing (de-multiplexing) extracts time-labelled packets of data contained in a media stream or file. FFmpeg provides a diverse range of demuxing capability with support for a wide range of input formats and protocols (`beamcoder.protocols()`).
Expand Down
5 changes: 3 additions & 2 deletions binding.gyp
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
{
"targets": [{
"target_name" : "beamcoder",
"sources" : [ "src/beamcoder.cc", "src/beamcoder_util.cc",
"sources" : [ "src/beamcoder.cc", "src/beamcoder_util.cc",
"src/log.cc" ,
"src/governor.cc", "src/demux.cc",
"src/decode.cc", "src/filter.cc",
"src/encode.cc", "src/mux.cc",
"src/packet.cc", "src/frame.cc",
"src/codec_par.cc", "src/format.cc",
"src/codec.cc", "src/hwcontext.cc" ],
"src/codec.cc", "src/hwcontext.cc"],
"conditions": [
['OS!="win"', {
"defines": [
Expand Down
55 changes: 2 additions & 53 deletions src/beamcoder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

#include "node_api.h"
#include "beamcoder_util.h"
#include "log.h"
#include "governor.h"
#include "format.h"
#include "demux.h"
Expand Down Expand Up @@ -253,59 +254,6 @@ napi_value licenses(napi_env env, napi_callback_info info) {
return result;
}

napi_value logging(napi_env env, napi_callback_info info) {
napi_status status;
napi_value result;
int logLevel;
char* logLevelStr;
size_t strLen;

napi_value args[1];
size_t argc = 1;
status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
CHECK_STATUS;

if (argc == 0) {
logLevel = av_log_get_level();
status = napi_create_string_utf8(env,
(char*) beam_lookup_name(beam_logging_level->forward, logLevel),
NAPI_AUTO_LENGTH, &result);
CHECK_STATUS;
} else {
if (argc != 1) {
status = napi_throw_error(env, nullptr, "Wrong number of arguments to set log level.");
return nullptr;
}

napi_value params = args[0];
napi_valuetype t;
status = napi_typeof(env, params, &t);
CHECK_STATUS;
if (t != napi_string) {
status = napi_throw_type_error(env, nullptr, "Logging level parameter must be a string.");
return nullptr;
}

status = napi_get_value_string_utf8(env, args[0], nullptr, 0, &strLen);
CHECK_STATUS;
logLevelStr = (char*) malloc(sizeof(char) * (strLen + 1));
CHECK_STATUS;
status = napi_get_value_string_utf8(env, args[0], logLevelStr, strLen + 1, &strLen);
CHECK_STATUS;

logLevel = beam_lookup_enum(beam_logging_level->inverse, logLevelStr);
if (logLevel == BEAM_ENUM_UNKNOWN) {
NAPI_THROW_ERROR("Logging level string unrecognised");
}
av_log_set_level(logLevel);

status = napi_get_undefined(env, &result);
CHECK_STATUS;
}

return result;
}

napi_status fromAVCodec(napi_env env, const AVCodec* codec, napi_value *result) {
napi_status status;
napi_value array, element, subel, value, props, nullval;
Expand Down Expand Up @@ -943,6 +891,7 @@ napi_value Init(napi_env env, napi_value exports) {
DECLARE_NAPI_METHOD("configurations", configurations),
DECLARE_NAPI_METHOD("licenses", licenses),
DECLARE_NAPI_METHOD("logging", logging),
DECLARE_NAPI_METHOD("setLoggingCallback", setLoggingCallback),
DECLARE_NAPI_METHOD("governor", governor),
DECLARE_NAPI_METHOD("format", makeFormat),
DECLARE_NAPI_METHOD("decoder", decoder),
Expand Down
13 changes: 0 additions & 13 deletions src/beamcoder_util.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1127,16 +1127,3 @@ std::unordered_map<int, std::string> beam_frame_side_data_type_fmap = {
{ AV_FRAME_DATA_S12M_TIMECODE, "s12m_timecode" }
};
const beamEnum* beam_frame_side_data_type = new beamEnum(beam_frame_side_data_type_fmap);

std::unordered_map<int, std::string> beam_logging_level_fmap = {
{ AV_LOG_QUIET, "quiet" },
{ AV_LOG_PANIC, "panic" },
{ AV_LOG_FATAL, "fatal" },
{ AV_LOG_ERROR, "error" },
{ AV_LOG_WARNING, "warning" },
{ AV_LOG_INFO, "info" },
{ AV_LOG_VERBOSE, "verbose" },
{ AV_LOG_DEBUG, "debug" },
{ AV_LOG_TRACE, "trace" }
};
const beamEnum* beam_logging_level = new beamEnum(beam_logging_level_fmap);
186 changes: 186 additions & 0 deletions src/log.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
#include "node_api.h"
#include <stdio.h>
#include "log.h"


extern "C" {
#include <libavutil/log.h>
#include <libavutil/bprint.h>
}

std::unordered_map<int, std::string> beam_logging_level_fmap = {
{ AV_LOG_QUIET, "quiet" },
{ AV_LOG_PANIC, "panic" },
{ AV_LOG_FATAL, "fatal" },
{ AV_LOG_ERROR, "error" },
{ AV_LOG_WARNING, "warning" },
{ AV_LOG_INFO, "info" },
{ AV_LOG_VERBOSE, "verbose" },
{ AV_LOG_DEBUG, "debug" },
{ AV_LOG_TRACE, "trace" }
};

const beamEnum* beam_logging_level = new beamEnum(beam_logging_level_fmap);

napi_value logging(napi_env env, napi_callback_info info) {
napi_status status;
napi_value result;
int logLevel;
char* logLevelStr;
size_t strLen;

napi_value args[1];
size_t argc = 1;
status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
CHECK_STATUS;

if (argc == 0) {
logLevel = av_log_get_level();
status = napi_create_string_utf8(env,
(char*) beam_lookup_name(beam_logging_level->forward, logLevel),
NAPI_AUTO_LENGTH, &result);
CHECK_STATUS;
} else {
if (argc != 1) {
status = napi_throw_error(env, nullptr, "Wrong number of arguments to set log level.");
return nullptr;
}

napi_value params = args[0];
napi_valuetype t;
status = napi_typeof(env, params, &t);
CHECK_STATUS;
if (t != napi_string) {
status = napi_throw_type_error(env, nullptr, "Logging level parameter must be a string.");
return nullptr;
}

status = napi_get_value_string_utf8(env, args[0], nullptr, 0, &strLen);
CHECK_STATUS;
logLevelStr = (char*) malloc(sizeof(char) * (strLen + 1));
CHECK_STATUS;
status = napi_get_value_string_utf8(env, args[0], logLevelStr, strLen + 1, &strLen);
CHECK_STATUS;

logLevel = beam_lookup_enum(beam_logging_level->inverse, logLevelStr);
if (logLevel == BEAM_ENUM_UNKNOWN) {
NAPI_THROW_ERROR("Logging level string unrecognised");
}
av_log_set_level(logLevel);

status = napi_get_undefined(env, &result);
CHECK_STATUS;
}

return result;
}

napi_threadsafe_function threadSafeFunction;

// Inspired from https://github.com/FFmpeg/FFmpeg/blob/321a3c244d0a89b2826c38611284cc403a9808fa/libavutil/log.c#L346
#define LINE_SZ 1024
void av_log_custom_callback(void* ptr, int level, const char* fmt, va_list vl)
{
int av_log_level = av_log_get_level();
static int print_prefix = 1;
static int count;
static char prev[LINE_SZ];
AVBPrint part[4];
char line[LINE_SZ];
static int is_atty;
int type[2];
unsigned tint = 0;

if (level >= 0) {
tint = level & 0xff00;
level &= 0xff;
}

if (level > av_log_level)
return;
av_log_format_line(ptr, level, fmt, vl, line, sizeof(line), &print_prefix);

logCarrier* c = new logCarrier;
c->msg = line;
c->level = level;
napi_status status;

status = napi_call_threadsafe_function(threadSafeFunction, c, napi_tsfn_nonblocking);

return;
}

static void callJsCb(
napi_env env,
napi_value jsCallback,
void* context,
void* data
){

logCarrier* c = (logCarrier*) data;

napi_value jsThis;
napi_status status;

status = napi_create_object(env, &jsThis);
CHECK_STATUS_VOID;

napi_value jsStr;
status = napi_create_string_utf8(env, c->msg.c_str(), NAPI_AUTO_LENGTH, &jsStr);
CHECK_STATUS_VOID;

napi_value return_val;
status = napi_call_function(env, jsThis, jsCallback, 1, &jsStr, &return_val);
CHECK_STATUS_VOID;

return;
}

napi_value setLoggingCallback(napi_env env, napi_callback_info info){

napi_status status;

napi_value args[1];
size_t argc = 1;
status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
CHECK_STATUS;

if (argc != 1) {
status = napi_throw_error(env, nullptr, "One argument required to set logging callback.");
return nullptr;
}
napi_value callback = args[0];
napi_valuetype t;
status = napi_typeof(env, callback, &t);
CHECK_STATUS;

if (t != napi_function) {
status = napi_throw_type_error(env, nullptr, "Callback argument should be a function.");
return nullptr;
}

napi_value work_name;
status = napi_create_string_utf8(env, "Thread-safe Function For Libav Custom Logging", NAPI_AUTO_LENGTH, &work_name);
CHECK_STATUS;

status = napi_create_threadsafe_function(
env,
callback,
NULL,
work_name,
0,
1,
nullptr,
nullptr,
nullptr,
callJsCb,
&threadSafeFunction
);
CHECK_STATUS;

status = napi_unref_threadsafe_function(env, threadSafeFunction);
CHECK_STATUS;

av_log_set_callback(av_log_custom_callback);
return nullptr;
}
20 changes: 20 additions & 0 deletions src/log.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#ifndef BEAMCODER_LOG_H
#define BEAMCODER_LOG_H

#include "node_api.h"
#include "beamcoder_util.h"
#include <stdio.h>
#include <unordered_map>

#define CHECK_STATUS_VOID if (checkStatus(env, status, __FILE__, __LINE__ - 1) != napi_ok) return

extern const beamEnum* beam_logging_level;
napi_value logging(napi_env env, napi_callback_info info);
napi_value setLoggingCallback(napi_env env, napi_callback_info info);

struct logCarrier : carrier {
std::string msg;
int level;
};

#endif // BEAMCODER_LOG_H
19 changes: 19 additions & 0 deletions test/introspectionSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,22 @@ test('Muxer information', t => {
t.ok(JSON.stringify(muxers), 'can be converted to JSON.');
t.end();
});

test('Custom Logging', async t => {
let n = 0;
const cb = (msg) => {
n++;
};
beamcoder.setLoggingCallback(cb);

let dm = await beamcoder.demuxer('https://www.elecard.com/storage/video/bbb_1080p_c.ts');
// Expected logs are :
// [hevc @ 0x7f1978017180] Unknown HEVC profile: 0
// [hevc @ 0x7f1978017180] Unknown HEVC profile: 0
// [hevc @ 0x7f1978017180] Unknown HEVC profile: 0
// [hevc @ 0x7f1978017180] Unknown HEVC profile: 0
// [hevc @ 0x7f1978017180] Unknown HEVC profile: 0
// [mpegts @ 0x7f1978000900] PES packet size mismatch
// [mpegts @ 0x7f1978000900] Packet corrupt (stream = 1, dts = 53647096)
t.ok(n > 5);
});

0 comments on commit 7b5d272

Please sign in to comment.