View Full Version : Reading file size in AviSynth


Forensic
3rd June 2018, 22:41
Loading a video is a piece of cake. Does anyone know of any method to get that video's file size in bytes into an integer variable in AviSyth? Any solution would be appreciated.

StainlessS
3rd June 2018, 23:19
You would be limited to 2GB max (+ve Int), what is the reason for this ?
MediaInfo (can call via CallCmd) write stuff to a file and the read in the file and extract, (eg on typical VOB might return 'file Size 1024 MiB'),
or some CLI tool providing filesize via same means.

EDIT: https://forum.doom9.org/showthread.php?p=1759514#post1759514

I can knock you up a plug tomorrow, bit knackered at the moment, gotta get some shuteye soon.

Forensic
4th June 2018, 00:23
StainlessS, Any help would be awesome.

My massive script currently uses CALL_25 to convert an unplayable proprietary video via ffmpeg. Depending upon on the originating file, some conversions create a now-playable version, while other times it results in a small non-video file. The problem is that any attempt to open those failed ffmpeg conversion files lock up my script's load engines (Avisource, Directshow, FFVideoSource, etc...) without allowing detection by TRY or HASVIDEO. Thus making the user forcibly close the AvsPmod script shell, and leaving them confused as to what has just occurred (most users are law enforcement detectives, analysts and private businesses).

My solution is to compare the file size of the originating proprietary video to that of the converted result. If the converted file is below a certain file size, or the file size is below a certain percentage of the originating file's file size, then I will know that the conversion failed to produce anything useful. My script can then decide whether to load the converted file, or delete it and show a status message to the end user.

To accomplish this, I need a Call_25, CallCmd, or DLL that is passed one text variable (the full path inclusive of a file name) and responds with one integer (that file's file size) that I can store in a variable to then process using my script's logic. It will be a trivial task for my script to call upon your solution once regarding the originating video, and then again for the ffmpeg conversion result, since I already have variables that store the full path plus file name of both files.

Thank you in advance. You have saved the day more than once for me and my project.

EDIT: I am unsure if a variant of your (https://forum.doom9.org/showthread.p...14#post1759514) idea will work since the unplayable files are not valid video files. Simplicity would be appreciated since I am only looking for one variable in with a one value response. If you opt for a DLL or script, a negative return value could be used to denote that the requested file does not exist.

StainlessS
4th June 2018, 00:52
Suggest that you abandon Call_25, has a number of problems and is reason for both RT_Call() and CallCmd(). [RT_Call runtime, CallCmd req clip at least a single frame dummy BlankClip, eg set CallCmd to do command on single frame blankclip in construction, assign to a variable {calling constructor and doing command}, then immediately assign 0, destroying clip, callCmd also has option to Wait command completion, RT_Call does not].

Could maybe optionally return eg filesize in bytes, or -1 file open problem, -2 for filesize too big for int, where if some Prefix is set, then will set eg MyPrefix_KIB and MyPrefix_ModBytes (%1024), so total filesize would be MyPrefix_KIB * 1024 + MyPrefix_ModBytes. (would not fit in Int though).

Forensic
4th June 2018, 01:40
Your idea for MyPrefix_KIB and MyPrefix_ModBytes is great, then no rounding is required. Please be sure to support zero byte files. Thank you.

As for Call_25, I just changed that to RT_CALL and it works perfect. I had completely forgot that you included that in your amazing RT multi-tool. :) I would take advantage of the Wait command in CallCmd, but that won't help since ffmpeg does not return anything (it just creates the converted file...eventually).

StainlessS
4th June 2018, 19:16
OK, Here you go,

Dll's for avs v2.58, avs+2.6 x86, and avs+ v2.6 x64.
Inclusive of VS2008 source and Project files (Need latest headers from Pinterf source).
Requires VS2008 runtimes.

Get Via MediaFire or SendSpace in my Sig beneath this post. (Approx 69KB).


FileSize(), v0.00, By StainlessS @ Doom9
FileSize(String FileName, String "Prefix"="FileSize_", Int "HiSize"=1000) = Int

Returns int, file size of file FileName in bytes, or -1 means Too big for +ve Int (greater than hex $7FFFFFFF, dec 2147483647).
On a file error, will throw and error (eg cannot open FileName).

Args:-

FileName, UnNamed compulsory String, name of file to find size in bytes of. [Use Exist() Prior to make sure file does exist]

Prefix, String, Default "FileSize_", Prefix to Local variable names of type Int.
If Prefix == "" then no Local variables are set on function return.
Returned Local variables will by default be named "FileSize_HI" and "FileSize_LO", ie Prefix + "HI" and "LO".

HiSize, Int, Default 1000 (2 <= HiSize). supplemental argument to Prefix.
Where Bytes=file size of FileName, & Prefix & HiSize both defaults, then will on function return, set Local variables
FileSize_HI = Bytes / HiSize
FileSize_LO = Bytes % HiSize
Could set eg HiSize 512, to set FileSize_HI to the number of whole 512 byte sectors that a file occupies,
and FileSize_LO to the number of odd bytes occupying a further sector (assuming is non zero).
For total sectors without odd bytes, add 1 to FileSize_HI if FileSize_LO is non zero.
Could set eg HiSize 1024, to set FileSize_HI to the number of whole KiB (KibiBytes) that a file occupies,
and FileSize_LO to the number of odd bytes occupying a further KiB (assuming is non zero).
For total KiB without odd bytes, add 1 to FileSize_HI if FileSize_LO is non zero.
Could set eg HiSize to (1024*1024) to get whole number of MiB (MebiBytes),
or to (1000*1000) to get whole number of Millions of bytes (MegaBytes), etc.

I personally dont like the new MiB, KiB, MB, KB, meanings, is more confusing than ever now that you have no idea what
somone means by MB or KB, may the good lord save us from well meaning clowns.

Example:-

LoadPlugin(".\FileSize_x86.dll")
ColorBars.KillAudio

FN="D:\USB.7z" # Some Biggish File (bigger than 2GB)
#FN=".\FileSize.avs" # Some small File (eg this avs file)

Bytes=FileSize(FN, HiSize=1000, Prefix="FileSize_") # With default Prefix("FileSize_", and HiSize(1000))

RT_Subtitle("Bytes=%d FileSize_HI=%d FileSize_LO=%d", Bytes, FileSize_HI, FileSize_LO)

BigString = (FileSize_HI==0?"":String(FileSize_HI)) + String(FileSize_LO)

RT_Subtitle("Bytes=%d BigString=%s %s Bigger than %d", Bytes, BigString,Bytes<0?"Is":"Is Not",$7FFFFFFF, Align=4)

Return Last


Most of Source

AVSValue __cdecl FileSize(AVSValue args, void* user_data, IScriptEnvironment* env) {
char *myName="FileSize: ";
const char *fn = args[0].AsString(); // Filename
const char *Prefix = args[1].AsString("FileSize_"); // Prefix
int hisize = args[2].AsInt(1000); // high block size
int pfixlen = strlen(Prefix);
if(*fn=='\0') env->ThrowError("%sEmpty FileName specified",myName);
if(hisize<2) env->ThrowError("%s2 <= HiSize(%d)",myName,hisize);
if(pfixlen>32) env->ThrowError("%s32 <= Prefix Length(%d)",myName,pfixlen);
FILE * fp=NULL;
__int64 sz64=0;
if(!(fp=fopen(fn, "rb" ))) env->ThrowError("%sCannot Open Filename",myName);
bool err = (_fseeki64(fp,0,SEEK_END)!=0||(sz64=_ftelli64(fp))<0);
fclose(fp); fp=NULL;
if(err) env->ThrowError("%sError getting file size",myName);
int ret = (sz64 > 0x7FFFFFFF) ? -1 : int(sz64);
if(pfixlen>0) {
__int64 hi64 = sz64 / hisize;
if (hi64 > 0x7FFFFFFF) env->ThrowError("%s%sHI exceeds $7FFFFFFF",myName,Prefix);
int hi = int(hi64);
int lo = int(sz64 % hisize);
char bf[32+64];
memcpy(bf,Prefix,pfixlen);
strcpy(bf+pfixlen,"HI"); // concatenate
AVSValue var = GetVar(env,bf); // Only use SaveString if variable does NOT already exist
env->SetVar(var.Defined()?bf:env->SaveString(bf),hi); // Set Local Var Prefix + "HI"
strcpy(bf+pfixlen,"LO"); // concatenate
var = GetVar(env,bf); // Only use SaveString if variable does NOT already exist
env->SetVar(var.Defined()?bf:env->SaveString(bf),lo); // Set Local Var Prefix + "LO"
}
return ret;
}


EDIT: And GetVar() courtesy of Gavino, thanx.


AVSValue __cdecl GetVar(IScriptEnvironment* env, const char* name) {
try {return env->GetVar(name);} catch (IScriptEnvironment::NotFound) {} return AVSValue();}


EDIT:
F, you aint been around for a while, well Check out VirtualDub2 (In VirtualDub forum), its is way better than the old v1.9
VD (Last Official was somewhere about 1.10). Also, Shekh implemented Avisynth Script Editor, similar to in VDMod.
You should also try the new Avs+ by Pinterf, he's done a hell of a good job on it (get Latest stable).

r2664:- https://forum.doom9.org/showthread.php?p=1837784#post1837784

Also, Both MvTools and Masktools2 (+more) much updated, way better, again down the Fantabulous human chain reactor, Pinterf. :)

Forensic
5th June 2018, 07:52
Thank you. I will try this in a few days (crazy busy with work), and truly appreciate your help. I will check out VirtualDub2 and Avs+ to see if I can/should work them into a rewrite of VideoCleaner.

StainlessS
5th June 2018, 14:58
I would take advantage of the Wait command in CallCmd, but that won't help since ffmpeg does not return anything (it just creates the converted file...eventually).

Just noticed the above Quote, well whether or not ffmpeg returns anything or not, does not really matter, the point is that it waits for it to complete what it is doing, so you know that you can then check for file Exist/Length or whatever, otherwise, you may be trying to check length when file is still being written (or still being parsed and not even started writing it yet), not good idea.

EDIT: Call() and RT_Call() [and CallCmd() when not waiting] only return whether command process was started OK or not. (eg did not get a syntax error or path not found or whatever).

EDIT: Also, see TWriteAVI v2.0:- https://forum.doom9.org/showthread.php?t=172837&highlight=TWriteAVI

Forensic
6th June 2018, 05:07
Worked like a charm. Thank you....thank you

ChaosKing
1st February 2019, 12:00
Found also this https://github.com/pureexe/FileSize_Avisynth

StainlessS
1st February 2019, 21:47
Found also this https://github.com/pureexe/FileSize_Avisynth

From the Readme there:
FileSize plugin for Avisynth

Since Avisynth can't get filesize using built-in function, then built FileSize plugin for get file's size by myself.

Usage:

filelegnth = FileSize("C:/Users/pakkapon/Downloads/Video/MyVideo.mp4")

Return:

file size in byte or -1 if file isn't exist.

Please note that return type is Float to avoid integer overflow. but if you want return type in integer you can use Int() to convert Float back to Int
https://github.com/pureexe/FileSize_Avisynth/blob/master/README.md

Groucho2004
1st February 2019, 23:34
From the Readme there:
Please note that return type is Float to avoid integer overflow. but if you want return type in integer you can use Int() to convert Float back to Int
https://www.youtube.com/watch?v=bLlWW1nF2Bw :scared:

Groucho2004
1st February 2019, 23:44
By the way, getting the file size is as simple as this with the good old Win32 API (sans GetLastError() handling):
//returns file size in bytes or -1 in case of error
__int64 FileSize(std::string s_file)
{
WIN32_FIND_DATA fd;
__int64 iSize = -1;
HANDLE hFind = FindFirstFile(s_file.c_str(), &fd);
if (hFind != INVALID_HANDLE_VALUE)
iSize = (((__int64)fd.nFileSizeHigh) << 32) + fd.nFileSizeLow;

FindClose(hFind);

return iSize;
}

StainlessS
2nd February 2019, 14:29
By the way, getting the file size is as simple as this


And simple as this without the plugin stuff, not really much worse/bigger.

FILE * fp=NULL;
__int64 sz64=0;
if(!(fp=fopen(fn, "rb" ))) env->ThrowError("%sCannot Open Filename",myName);
bool err = (_fseeki64(fp,0,SEEK_END)!=0||(sz64=_ftelli64(fp))<0);
fclose(fp); fp=NULL;

Groucho2004
2nd February 2019, 16:00
And simple as this without the plugin stuff, not really much worse/bigger.

FILE * fp=NULL;
__int64 sz64=0;
if(!(fp=fopen(fn, "rb" ))) env->ThrowError("%sCannot Open Filename",myName);
bool err = (_fseeki64(fp,0,SEEK_END)!=0||(sz64=_ftelli64(fp))<0);
fclose(fp); fp=NULL;
Sure. I just like to ask the OS directly if possible. Different strokes...

StainlessS
2nd February 2019, 16:59
I just like to ask the OS directly if possible.
I like to avoid as much contact with the OS as possible, never did like the PC (puttin' it mildly), I once upon a time refused the offer of a free PC, twice in one day [different free sources], just did not want one, also reason I am reluctant to get into X86 asm/intrinsic stuff.
PC shoulda been dumped decades ago.

Different strokes...

Indeed :)

manolito
2nd February 2019, 17:37
@Groucho and StainlessS

Damn, the thread title is "Reading File Size in AviSynth". Are you talking C++ or another obscure language? I certainly do not understand a thing looking at your code. If it is C or C++ then I sure remember the old saying that C is a "Write Only" language. I sure miss my old Turbo Pascal.

Could one of you at least transform this code to an AVS plugin?

Cheers
manolito

Groucho2004
2nd February 2019, 17:51
If it is C or C++ then I sure remember the old saying that C is a "Write Only" language.Huh? Never heard that saying. What does it mean?

mariush
2nd February 2019, 17:59
Or you guys could just use CreateFileW (https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-createfilew) and GetFileSizeEx (https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-getfilesizeex) and be done with it, instead of dicking around with fseek and ftell

This would also work for locked files where seeking would fail,as you can use createfilew to "open" a file just to retrieve metadata ... and you could also use unicode paths for the file name (not sure if the fopen defaults to unicode version and wide strings)

StainlessS
2nd February 2019, 18:02
Could one of you at least transform this code to an AVS plugin?

I refer the honourable gentleman, to the post I made some moments ago (well some months ago, post #6)

Get Via MediaFire or SendSpace in my Sig beneath this post. (Approx 69KB).

Or get it here:- http://www.mediafire.com/file/77no6wo26z8ioon/FileSize_x86_x64_dll_v0-00_20180604.zip/file

The stuff in bold above post with reply], was Maggie Thatcher's favourite reply at Prime Minister's Question Time, in the House of Commons.
[when being badgered for some answer repeatedly, whilst continuously avoiding a proper reply]

Groucho2004
2nd February 2019, 20:48
I like to avoid as much contact with the OS as possible, never did like the PC (puttin' it mildly), I once upon a time refused the offer of a free PC, twice in one day [different free sources], just did not want one, also reason I am reluctant to get into X86 asm/intrinsic stuff.
PC shoulda been dumped decades ago.You seem to be confusing the PC - I presume you are referring to the x86-64 architecture - and the OS's it can host (Windows, DOS, Linux, BSD, macOS, Solaris).

Unless you want to work with a Unix SparcStation or a MAC (:eek:), there is not much choice, is there?

StainlessS
2nd February 2019, 21:14
there is not much choice, is there?

Yep I know, and I hates dat. (me hates the chip, Windows, the whole shabang).
Seems that we are stuck with the x86/64 PC, until the lights all go out.

wonkey_monkey
2nd February 2019, 21:15
The stuff in bold above post with reply], was Maggie Thatcher's favourite reply at Prime Minister's Question Time, in the House of Commons.
[when being badgered for some answer repeatedly, whilst continuously avoiding a proper reply]

It was part of parliamentary protocol/tradition. Historically the PM was only supposed/allowed to answer questions on subjects they had direct responsibility for, which actually isn't all that much (because almost everything can technically be delegated to a minister). So everyone would instead ask about the PM's appointments, to which the PM would - the first time - give a standard answer, and then to every subsequent asking of the question, would give the reply above.

The reason this worked is that once you'd asked the PM the appointments question, you could then ask a follow-up question, which would be your actual question (which would otherwise have been dismissed as not falling under the PM's remit).

So if you'd said "Are you going to sort out the plumbing?" the PM could say "Ask the Minister for Toilets," but if you first asked about appointments, you could then ask something like "Why haven't you got any appointments with plumbers?"

Yes, it's as ridiculous as it sounds - which is why they got rid of that nonsense so now you don't hear the "I refer the honourable..." any more, or at least as much.

StainlessS
2nd February 2019, 21:21
@ Wonkey Donkey,

I thank the honourable gentleman for the reply I read some moments ago :)