Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/publish-pypi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ env:

# nv-codec-headers (Linux/Windows only)
if [[ "$EXTRA_FFMPEG_FLAGS" == *"--enable-nvdec"* ]]; then
git clone -b "$nv_version" --depth 1 https://git.videolan.org/git/ffmpeg/nv-codec-headers
git clone -b "$nv_version" --depth 1 https://github.com/FFmpeg/nv-codec-headers
cd nv-codec-headers
$SUDO make install PREFIX="$PREFIX"
cd .. && rm -rf nv-codec-headers
Expand Down
9 changes: 6 additions & 3 deletions AviSynth/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@
###### LSMASHAudioSource

* `LSMASHAudioSource(string source, int track = 0, bool skip_priming = true, string layout = "", int rate = 0,
string decoder = "", int ff_loglevel = 0, float drc_scale = 1.0, string ff_options = "")`
string decoder = "", int ff_loglevel = 0, float drc_scale = 1.0, string ff_options = "", bool skip_tail = false)`

* This function uses libavcodec as audio decoder and L-SMASH as demuxer.
[Arguments]
Expand All @@ -169,7 +169,7 @@
The value 0 means trying to get the first detected audio stream.
+ skip_priming (default : true)
Whether skip priming samples or not.
Priming samples is detected from iTunSMPB or the first non-empty edit.
Priming samples are detected from iTunSMPB or the first non-empty edit.
If any priming samples, do pre-roll whenever any seek of audio stream occurs.
+ layout (default : "")
Output audio channel layout.
Expand Down Expand Up @@ -257,8 +257,11 @@
0.0 < drc_scale <= 1.0 : DRC enabled. Applies a fraction of the stream DRC value. Audio reproduction is between full range and full compression.
> 1.0 : DRC enabled. Applies drc_scale asymmetrically. Loud sounds are fully compressed. Soft sounds are enhanced.
If `ff_options="drc_scale=x"` is used, `drc_scale` is ignored.
+ ff_options (default: "")
+ ff_options (default : "")
Same as 'ff_options' of LSMASHVideoSource().
* skip_tail (default : false)
Whether skip trailing samples or not.
Trailing samples are detected from iTunSMPB.

###### LWLibavVideoSource

Expand Down
107 changes: 18 additions & 89 deletions AviSynth/libavsmash_source.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -252,32 +252,6 @@ uint32_t LSMASHAudioSource::open_file(const char* source, IScriptEnvironment* en
return movie_param.number_of_tracks;
}

static int64_t get_start_time(lsmash_root_t* root, uint32_t track_ID)
{
/* Consider start time of this media if any non-empty edit is present. */
uint32_t edit_count = lsmash_count_explicit_timeline_map(root, track_ID);
for (uint32_t edit_number = 1; edit_number <= edit_count; edit_number++) {
lsmash_edit_t edit;
if (lsmash_get_explicit_timeline_map(root, track_ID, edit_number, &edit))
return 0;
if (edit.duration == 0)
return 0; /* no edits */
if (edit.start_time >= 0)
return edit.start_time;
}
return 0;
}

static char* duplicate_as_string(void* src, size_t length)
{
char* dst = new char[length + 1];
if (!dst)
return nullptr;
memcpy(dst, src, length);
dst[length] = '\0';
return dst;
}

void LSMASHAudioSource::get_audio_track(const char* source, uint32_t track_number, IScriptEnvironment* env)
{
libavsmash_audio_decode_handler_t* adhp = this->adhp.get();
Expand All @@ -289,70 +263,24 @@ void LSMASHAudioSource::get_audio_track(const char* source, uint32_t track_numbe
}

static void count_output_audio_samples(libavsmash_audio_decode_handler_t* adhp, libavsmash_audio_output_handler_t* aohp, bool skip_priming,
VideoInfo& vi, IScriptEnvironment* env)
bool skip_tail, VideoInfo& vi, IScriptEnvironment* env)
{
lsmash_root_t* root = libavsmash_audio_get_root(adhp);
uint32_t track_id = libavsmash_audio_get_track_id(adhp);
uint64_t start_time = 0;
if (skip_priming) {
uint32_t media_timescale = libavsmash_audio_get_media_timescale(adhp);
uint32_t itunes_metadata_count = lsmash_count_itunes_metadata(root);
for (uint32_t i = 1; i <= itunes_metadata_count; i++) {
lsmash_itunes_metadata_t metadata;
if (lsmash_get_itunes_metadata(root, i, &metadata) < 0)
continue;
if (metadata.item != ITUNES_METADATA_ITEM_CUSTOM
|| (metadata.type != ITUNES_METADATA_TYPE_STRING && metadata.type != ITUNES_METADATA_TYPE_BINARY) || !metadata.meaning
|| !metadata.name || memcmp("com.apple.iTunes", metadata.meaning, strlen(metadata.meaning))
|| memcmp("iTunSMPB", metadata.name, strlen(metadata.name))) {
lsmash_cleanup_itunes_metadata(&metadata);
continue;
}
char* value = nullptr;
if (metadata.type == ITUNES_METADATA_TYPE_STRING) {
size_t length = strlen(metadata.value.string);
if (length >= 116)
value = duplicate_as_string(metadata.value.string, length);
} else /* metadata.type == ITUNES_METADATA_TYPE_BINARY */
{
if (metadata.value.binary.size >= 116)
value = duplicate_as_string(metadata.value.binary.data, metadata.value.binary.size);
}
lsmash_cleanup_itunes_metadata(&metadata);
if (!value)
continue;
uint32_t dummy[9];
uint32_t priming_samples;
uint32_t padding;
uint64_t duration;
if (12
!= sscanf(value, " %x %x %x %" SCNx64 " %x %x %x %x %x %x %x %x", &dummy[0], &priming_samples, &padding, &duration,
&dummy[1], &dummy[2], &dummy[3], &dummy[4], &dummy[5], &dummy[6], &dummy[7], &dummy[8])) {
delete[] value;
continue;
}
delete[] value;
libavsmash_audio_set_implicit_preroll(adhp);
start_time = av_rescale(priming_samples, media_timescale, aohp->output_sample_rate);
aohp->skip_decoded_samples = priming_samples;
// vi.num_audio_samples = duration + priming_samples;
break;
}
if (aohp->skip_decoded_samples == 0) {
uint32_t ctd_shift;
if (lsmash_get_composition_to_decode_shift_from_media_timeline(root, track_id, &ctd_shift))
env->ThrowError("LSMASHAudioSource: failed to get the timeline shift.");
start_time = ctd_shift + get_start_time(root, track_id);
aohp->skip_decoded_samples = av_rescale(start_time, aohp->output_sample_rate, media_timescale);
}
uint64_t final_num_samples = 0;
char error_msg[256] = { 0 };
if (libavsmash_audio_setup_sample_count(
adhp, aohp, skip_priming ? 1 : 0, skip_tail ? 1 : 0, &final_num_samples, error_msg, sizeof(error_msg))
< 0) {
env->ThrowError("LSMASHAudioSource: %s", error_msg);
}
if (final_num_samples > (uint64_t)INT64_MAX) {
env->ThrowError("LSMASHAudioSource: audio sample count exceeds INT64_MAX.");
}
vi.num_audio_samples = libavsmash_audio_count_overall_pcm_samples(adhp, aohp->output_sample_rate, start_time);
if (vi.num_audio_samples == 0)
env->ThrowError("LSMASHAudioSource: no valid audio frame.");
vi.num_audio_samples = (int64_t)final_num_samples;
}

static void prepare_audio_decoding(libavsmash_audio_decode_handler_t* adhp, libavsmash_audio_output_handler_t* aohp,
AVFormatContext* format_ctx, const char* channel_layout, int sample_rate, bool skip_priming, VideoInfo& vi, IScriptEnvironment* env)
AVFormatContext* format_ctx, const char* channel_layout, int sample_rate, bool skip_priming, bool skip_tail, VideoInfo& vi,
IScriptEnvironment* env)
{
/* Initialize the audio decoder configuration. */
if (libavsmash_audio_initialize_decoder_configuration(adhp, format_ctx, 0) < 0)
Expand All @@ -363,13 +291,13 @@ static void prepare_audio_decoding(libavsmash_audio_decode_handler_t* adhp, liba
aohp->output_bits_per_sample = libavsmash_audio_get_best_used_bits_per_sample(adhp);
AVCodecContext* ctx = libavsmash_audio_get_codec_context(adhp);
as_setup_audio_rendering(aohp, ctx, &vi, env, "LSMASHAudioSource", channel_layout, sample_rate);
count_output_audio_samples(adhp, aohp, skip_priming, vi, env);
count_output_audio_samples(adhp, aohp, skip_priming, skip_tail, vi, env);
/* Force seeking at the first reading. */
libavsmash_audio_force_seek(adhp);
}

LSMASHAudioSource::LSMASHAudioSource(const char* source, uint32_t track_number, bool skip_priming, const char* channel_layout,
int sample_rate, const char* preferred_decoder_names, const double drc, const char* ff_options, IScriptEnvironment* env)
int sample_rate, const char* preferred_decoder_names, const double drc, const char* ff_options, bool skip_tail, IScriptEnvironment* env)
: LSMASHAudioSource {}
{
memset(&vi, 0, sizeof(VideoInfo));
Expand All @@ -380,7 +308,7 @@ LSMASHAudioSource::LSMASHAudioSource(const char* source, uint32_t track_number,
libavsmash_audio_set_drc(adhp, drc);
libavsmash_audio_set_decoder_options(adhp, ff_options);
get_audio_track(source, track_number, env);
prepare_audio_decoding(adhp, aohp, format_ctx.get(), channel_layout, sample_rate, skip_priming, vi, env);
prepare_audio_decoding(adhp, aohp, format_ctx.get(), channel_layout, sample_rate, skip_priming, skip_tail, vi, env);
lsmash_discard_boxes(libavsmash_audio_get_root(adhp));
}

Expand Down Expand Up @@ -468,7 +396,8 @@ AVSValue __cdecl CreateLSMASHAudioSource(AVSValue args, void* user_data, IScript
int ff_loglevel = args[6].AsInt(0);
const double drc = args[7].AsFloat(-1.0);
const char* ff_options = args[8].AsString(nullptr);
bool skip_tail = args[9].AsBool(false);
set_av_log_level(ff_loglevel);
return new LSMASHAudioSource(
source, track_number, skip_priming, layout_string, sample_rate, preferred_decoder_names, drc, ff_options, env);
source, track_number, skip_priming, layout_string, sample_rate, preferred_decoder_names, drc, ff_options, skip_tail, env);
}
2 changes: 1 addition & 1 deletion AviSynth/libavsmash_source.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ class LSMASHAudioSource : public LibavSMASHSource {

public:
LSMASHAudioSource(const char* source, uint32_t track_number, bool skip_priming, const char* channel_layout, int sample_rate,
const char* preferred_decoder_names, const double drc, const char* ff_options, IScriptEnvironment* env);
const char* preferred_decoder_names, const double drc, const char* ff_options, bool skip_tail, IScriptEnvironment* env);
~LSMASHAudioSource();
PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* env)
{
Expand Down
2 changes: 1 addition & 1 deletion AviSynth/lsmashsource.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ extern "C" AVS_EXPORT const char* __stdcall AvisynthPluginInit3(IScriptEnvironme
CreateLSMASHVideoSource, 0);
/* LSMASHAudioSource */
env->AddFunction("LSMASHAudioSource",
"[source]s[track]i[skip_priming]b[layout]s[rate]i[decoder]s[ff_loglevel]i[drc_scale]f[ff_options]s", CreateLSMASHAudioSource, 0);
"[source]s[track]i[skip_priming]b[layout]s[rate]i[decoder]s[ff_loglevel]i[drc_scale]f[ff_options]s[skip_tail]b", CreateLSMASHAudioSource, 0);
/* LWLibavVideoSource */
env->AddFunction("LWLibavVideoSource",
"[source]s[stream_index]i[threads]i[cache]b[cachefile]s[seek_mode]i[seek_threshold]i[dr]b[fpsnum]i[fpsden]i[repeat]b[dominance]i["
Expand Down
2 changes: 1 addition & 1 deletion AviSynth/lwlibav_source.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@ LWLibavAudioSource::LWLibavAudioSource(lwlibav_option_t* opt, const char* channe
sequence_pcm_count += info[i].length;
const uint64_t frame_end = prior_sequences_resampled_count
+ count_sequence_output_pcm_samples(sequence_pcm_count, current_sample_rate, aohp->output_sample_rate);
if (info[i].file_offset == -1)
if (lw_audio_is_gap_offset(info[i].file_offset))
gap_info_list.emplace_back(static_cast<int64_t>(frame_start) + lwh.av_gap, static_cast<int>(frame_end - frame_start));
}
const int gap_count = gap_info_list.size();
Expand Down
98 changes: 18 additions & 80 deletions VapourSynth/libavsmash_audio_source.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@

#include <libavformat/avformat.h>
#include <libavutil/log.h>
#include <libavutil/mem.h>
#include <libavutil/mathematics.h>
#include <libavutil/mem.h>

#include "audio_output.h"
#include "lsmashsource.h"
Expand Down Expand Up @@ -63,86 +63,23 @@ static lsmas_audio_handler_t* alloc_handler(void)
return hp;
}

static int64_t get_start_time(lsmash_root_t* root, uint32_t track_id)
{
uint32_t edit_count = lsmash_count_explicit_timeline_map(root, track_id);
for (uint32_t edit_number = 1; edit_number <= edit_count; edit_number++) {
lsmash_edit_t edit;
if (lsmash_get_explicit_timeline_map(root, track_id, edit_number, &edit) || edit.duration == 0)
return 0;
if (edit.start_time >= 0)
return edit.start_time;
}
return 0;
}

static char* duplicate_string(const void* src, size_t length)
{
char* dst = (char*)malloc(length + 1);
if (!dst)
return NULL;
memcpy(dst, src, length);
dst[length] = '\0';
return dst;
}

static int count_output_audio_samples(lsmas_audio_handler_t* hp, int skip_priming, VSMap* out, const VSAPI* vsapi)
static int count_output_audio_samples(lsmas_audio_handler_t* hp, int skip_priming, int skip_tail, VSMap* out, const VSAPI* vsapi)
{
libavsmash_audio_decode_handler_t* adhp = hp->adhp;
libavsmash_audio_output_handler_t* aohp = hp->aohp;
lsmash_root_t* root = libavsmash_audio_get_root(adhp);
uint32_t track_id = libavsmash_audio_get_track_id(adhp);
uint64_t start_time = 0;
if (skip_priming) {
uint32_t media_timescale = libavsmash_audio_get_media_timescale(adhp);
uint32_t metadata_count = lsmash_count_itunes_metadata(root);
for (uint32_t i = 1; i <= metadata_count; i++) {
lsmash_itunes_metadata_t metadata;
if (lsmash_get_itunes_metadata(root, i, &metadata) < 0)
continue;
int matches = metadata.item == ITUNES_METADATA_ITEM_CUSTOM
&& (metadata.type == ITUNES_METADATA_TYPE_STRING || metadata.type == ITUNES_METADATA_TYPE_BINARY) && metadata.meaning
&& metadata.name && !strcmp(metadata.meaning, "com.apple.iTunes") && !strcmp(metadata.name, "iTunSMPB");
char* value = NULL;
if (matches && metadata.type == ITUNES_METADATA_TYPE_STRING) {
size_t length = strlen(metadata.value.string);
if (length >= 116)
value = duplicate_string(metadata.value.string, length);
} else if (matches && metadata.value.binary.size >= 116)
value = duplicate_string(metadata.value.binary.data, metadata.value.binary.size);
lsmash_cleanup_itunes_metadata(&metadata);
if (!value)
continue;
uint32_t dummy[9];
uint32_t priming_samples;
uint32_t padding;
uint64_t duration;
int fields = sscanf(value, " %x %x %x %" SCNx64 " %x %x %x %x %x %x %x %x", &dummy[0], &priming_samples, &padding,
&duration, &dummy[1], &dummy[2], &dummy[3], &dummy[4], &dummy[5], &dummy[6], &dummy[7], &dummy[8]);
lw_free(value);
if (fields != 12)
continue;
libavsmash_audio_set_implicit_preroll(adhp);
start_time = av_rescale(priming_samples, media_timescale, aohp->output_sample_rate);
aohp->skip_decoded_samples = priming_samples;
break;
}
if (aohp->skip_decoded_samples == 0) {
uint32_t ctd_shift;
if (lsmash_get_composition_to_decode_shift_from_media_timeline(root, track_id, &ctd_shift)) {
set_error_on_init(out, vsapi, "lsmas: failed to get the audio timeline shift.");
return -1;
}
start_time = ctd_shift + get_start_time(root, track_id);
aohp->skip_decoded_samples = av_rescale(start_time, aohp->output_sample_rate, media_timescale);
}
uint64_t final_num_samples = 0;
char error_msg[256] = { 0 };
if (libavsmash_audio_setup_sample_count(hp->adhp, hp->aohp, skip_priming, skip_tail, &final_num_samples, error_msg, sizeof(error_msg))
< 0) {
char vs_msg[300];
snprintf(vs_msg, sizeof(vs_msg), "lsmas: %s", error_msg);
set_error_on_init(out, vsapi, vs_msg);
return -1;
}
hp->ai.numSamples = libavsmash_audio_count_overall_pcm_samples(adhp, aohp->output_sample_rate, start_time);
if (hp->ai.numSamples <= 0) {
set_error_on_init(out, vsapi, "lsmas: no valid audio frame.");
if (final_num_samples > (uint64_t)INT64_MAX) {
set_error_on_init(out, vsapi, "lsmas: audio sample count exceeds INT64_MAX.");
return -1;
}
hp->ai.numFrames = (int)((hp->ai.numSamples + VS_AUDIO_FRAME_SAMPLES - 1) / VS_AUDIO_FRAME_SAMPLES);
hp->ai.numSamples = (int64_t)final_num_samples;
hp->ai.numFrames = (int)((final_num_samples + VS_AUDIO_FRAME_SAMPLES - 1) / VS_AUDIO_FRAME_SAMPLES);
return 0;
}

Expand Down Expand Up @@ -208,6 +145,7 @@ void VS_CC vs_libavsmashaudiosource_create(const VSMap* in, VSMap* out, void* us
const char* layout;
const char* decoder;
const char* ff_options;
int64_t skip_tail;
int error;
double drc = vsapi->mapGetFloat(in, "drc_scale", 0, &error);
if (error)
Expand All @@ -219,6 +157,7 @@ void VS_CC vs_libavsmashaudiosource_create(const VSMap* in, VSMap* out, void* us
set_option_string(&layout, NULL, "layout", in, vsapi);
set_option_string(&decoder, NULL, "decoder", in, vsapi);
set_option_string(&ff_options, NULL, "ff_options", in, vsapi);
set_option_int64(&skip_tail, 0, "skip_tail", in, vsapi);
set_av_log_level(ff_loglevel);

lsmas_audio_handler_t* hp = alloc_handler();
Expand Down Expand Up @@ -261,7 +200,7 @@ void VS_CC vs_libavsmashaudiosource_create(const VSMap* in, VSMap* out, void* us
hp->aohp->output_bits_per_sample = libavsmash_audio_get_best_used_bits_per_sample(hp->adhp);
AVCodecContext* ctx = libavsmash_audio_get_codec_context(hp->adhp);
if (vs_setup_audio_rendering(hp->aohp, ctx, layout, (int)MAX(rate, 0), &hp->ai, core, vsapi) < 0
|| count_output_audio_samples(hp, skip_priming != 0, out, vsapi) < 0) {
|| count_output_audio_samples(hp, skip_priming != 0, skip_tail != 0, out, vsapi) < 0) {
free_handler(&hp);
if (!vsapi->mapGetError(out))
set_error_on_init(out, vsapi, "lsmas: failed to configure audio output.");
Expand All @@ -270,8 +209,7 @@ void VS_CC vs_libavsmashaudiosource_create(const VSMap* in, VSMap* out, void* us
libavsmash_audio_force_seek(hp->adhp);
lsmash_discard_boxes(root);

VSNode* node = vsapi->createAudioFilter2(
"LibavSMASHAudioSource", &hp->ai, audio_get_frame, audio_free, fmUnordered, NULL, 0, hp, core);
VSNode* node = vsapi->createAudioFilter2("LibavSMASHAudioSource", &hp->ai, audio_get_frame, audio_free, fmUnordered, NULL, 0, hp, core);
if (!node) {
free_handler(&hp);
vsapi->mapSetError(out, "lsmas: failed to create the LibavSMASH audio node.");
Expand Down
2 changes: 1 addition & 1 deletion VapourSynth/lsmashsource.c
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ VS_EXTERNAL_API(void) VapourSynthPluginInit2(VSPlugin* plugin, const VSPLUGINAPI
"clip:vnode;", vs_libavsmashsource_create, NULL, plugin);
vspapi->registerFunction("LibavSMASHAudioSource",
"source:data;track:int:opt;skip_priming:int:opt;layout:data:opt;rate:int:opt;decoder:data:opt;ff_loglevel:int:opt;drc_scale:"
"float:opt;ff_options:data:opt;",
"float:opt;ff_options:data:opt;skip_tail:int:opt;",
"audio:anode;", vs_libavsmashaudiosource_create, NULL, plugin);
vspapi->registerFunction("LWLibavSource",
"source:data;stream_index:int:opt;cache:int:opt;cachefile:data:opt;" COMMON_OPTS
Expand Down
Loading
Loading