Skip to content

Commit 281b250

Browse files
committed
change options parameter impl
1 parent 8edffee commit 281b250

File tree

7 files changed

+127
-52
lines changed

7 files changed

+127
-52
lines changed

README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,15 @@ ffprobe.getFileInfo('sample.mp4').then(result => {
1818

1919
```
2020

21-
passing in probesize and analyzeduration
21+
## Passing options
22+
23+
Available options are: probeSize, analyzeDuration
2224

2325
```javascript
2426

2527
const ffprobe = require('@tugrul/ffprobe');
2628

27-
ffprobe.getFileInfo({ filePath: 'sample.mp4', options: { probeSize: 1 * 1024 * 1024 * 1024, analyzeDuration: 1000 * 1000000 } }).then(result => {
29+
ffprobe.getFileInfo(sample.mp4, { probeSize: 1 * 1024 * 1024 * 1024, analyzeDuration: 1000 * 1000000 }).then(result => {
2830
console.log(result);
2931
});
3032

index.d.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,5 +177,10 @@ export interface Versions {
177177
readonly avutil: string;
178178
}
179179

180+
export interface FileInfoOptions {
181+
probeSize: number;
182+
analyzeDuration: number;
183+
}
184+
180185
export declare const versions: Versions;
181-
export declare function getFileInfo(filePath: string): Promise<AvFormatContext>;
186+
export declare function getFileInfo(filePath: string, options?: FileInfoOptions): Promise<AvFormatContext>;

src/avdictionary.h

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
2+
#pragma once
3+
4+
extern "C" {
5+
#include <libavutil/dict.h>
6+
}
7+
8+
#include <string>
9+
10+
namespace node_ffprobe {
11+
12+
/**
13+
* RAII wrapper for FFmpeg's AVDictionary
14+
* Provides automatic memory management and convenient methods
15+
* for dictionary operations.
16+
*/
17+
class AvDictionary {
18+
public:
19+
// Default constructor creates an empty dictionary
20+
AvDictionary() : m_dict(nullptr) {}
21+
22+
// Constructor from existing AVDictionary pointer (takes ownership)
23+
explicit AvDictionary(AVDictionary* dict) : m_dict(dict) {}
24+
25+
// Move constructor
26+
AvDictionary(AvDictionary&& other) noexcept : m_dict(other.m_dict) {
27+
other.m_dict = nullptr;
28+
}
29+
30+
// Move assignment operator
31+
AvDictionary& operator=(AvDictionary&& other) noexcept {
32+
if (this != &other) {
33+
free();
34+
m_dict = other.m_dict;
35+
other.m_dict = nullptr;
36+
}
37+
return *this;
38+
}
39+
40+
// Destructor automatically frees the dictionary
41+
~AvDictionary() {
42+
free();
43+
}
44+
45+
// Delete copy constructor and assignment operator
46+
AvDictionary(const AvDictionary&) = delete;
47+
AvDictionary& operator=(const AvDictionary&) = delete;
48+
49+
// Set a key-value pair in the dictionary
50+
int set(const std::string& key, const std::string& value, int flags = 0) {
51+
return av_dict_set(&m_dict, key.c_str(), value.c_str(), flags);
52+
}
53+
54+
// Set a key with integer value in the dictionary
55+
int set(const std::string& key, int64_t value, int flags = 0) {
56+
return av_dict_set_int(&m_dict, key.c_str(), value, flags);
57+
}
58+
59+
// Manually free the dictionary
60+
void free() {
61+
if (m_dict) {
62+
av_dict_free(&m_dict);
63+
m_dict = nullptr;
64+
}
65+
}
66+
67+
// Get the underlying AVDictionary pointer
68+
AVDictionary* getBareInstance() const {
69+
return m_dict;
70+
}
71+
72+
// Allow implicit casting to AVDictionary** for use in FFmpeg functions
73+
operator AVDictionary**() {
74+
return &m_dict;
75+
}
76+
77+
// Allow implicit casting to AVDictionary* for use in FFmpeg functions
78+
operator AVDictionary*() const {
79+
return m_dict;
80+
}
81+
82+
private:
83+
AVDictionary* m_dict;
84+
};
85+
86+
};

src/ffprobe.cc

Lines changed: 16 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ extern "C" {
2121
#include "avchapter.h"
2222
#include "avprogram.h"
2323
#include "avstream.h"
24+
#include "avdictionary.h"
2425

2526

2627
namespace node_ffprobe {
@@ -32,46 +33,34 @@ Napi::Promise GetFileInfo(const Napi::CallbackInfo& info) {
3233

3334
Napi::Promise::Deferred deferred = Napi::Promise::Deferred::New(env);
3435

35-
std::string filePath;
36-
int64_t probeSize = 0;
37-
int64_t analyzeDuration = 0;
3836

39-
if (info[0].IsString()) {
40-
41-
filePath = info[0].As<Napi::String>();
37+
if (!info[0].IsString()) {
38+
deferred.Reject(Napi::TypeError::New(env, Napi::String::New(env,"filePath parameter should be a string value")).Value());
39+
return deferred.Promise();
4240
}
43-
else if (info[0].IsObject()) {
4441

45-
Napi::Object obj = info[0].As<Napi::Object>();
42+
AvDictionary options;
4643

47-
if (!obj.Has("filePath") || !obj.Get("filePath").IsString()) {
48-
deferred.Reject(Napi::TypeError::New(env, "Missing or invalid 'filePath' field").Value());
44+
if (!info[1].IsUndefined()) {
45+
if (!info[1].IsObject()) {
46+
deferred.Reject(Napi::TypeError::New(env, Napi::String::New(env,"options can only be an object")).Value());
4947
return deferred.Promise();
5048
}
5149

52-
filePath = obj.Get("filePath").As<Napi::String>();
53-
54-
if (obj.Has("options") && obj.Get("options").IsObject()) {
55-
56-
Napi::Object opts = obj.Get("options").As<Napi::Object>();
50+
Napi::Object opts = info[1].As<Napi::Object>();
5751

58-
if (opts.Has("probeSize") && opts.Get("probeSize").IsNumber()) {
59-
probeSize = opts.Get("probeSize").As<Napi::Number>().Int64Value();
60-
}
61-
62-
if (opts.Has("analyzeDuration") && opts.Get("analyzeDuration").IsNumber()) {
63-
analyzeDuration = opts.Get("analyzeDuration").As<Napi::Number>().Int64Value();
64-
}
52+
if (opts.Has("probeSize") && opts.Get("probeSize").IsNumber()) {
53+
options.set("probesize", opts.Get("probeSize").As<Napi::Number>().Int64Value());
6554
}
66-
}
67-
else {
6855

69-
deferred.Reject(Napi::TypeError::New(env, "Expected string or object as first argument").Value());
70-
return deferred.Promise();
56+
if (opts.Has("analyzeDuration") && opts.Get("analyzeDuration").IsNumber()) {
57+
options.set("analyzeduration", opts.Get("analyzeDuration").As<Napi::Number>().Int64Value());
58+
}
7159
}
7260

61+
std::string filePath = info[0].As<Napi::String>();
7362

74-
MediaInfoWorker* worker = new MediaInfoWorker(env, filePath, probeSize, analyzeDuration, std::move(deferred));
63+
MediaInfoWorker* worker = new MediaInfoWorker(env, filePath, std::move(options), std::move(deferred));
7564

7665
worker->Queue();
7766

src/media-info-worker.cc

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,10 @@ namespace node_ffprobe {
77
AVFormatContext* MediaInfoWorker::sharedContext = nullptr;
88
std::mutex MediaInfoWorker::sharedContextMutex;
99

10-
MediaInfoWorker::MediaInfoWorker(const Napi::Env& env, const std::string& fileName, const uint64_t probeSize, const uint64_t analyzeDuration, Napi::Promise::Deferred&& def):
10+
MediaInfoWorker::MediaInfoWorker(const Napi::Env& env, const std::string& fileName, AvDictionary&& options, Napi::Promise::Deferred&& def):
1111
Napi::AsyncWorker(env),
12-
fileName(fileName),
13-
probeSize(probeSize),
14-
analyzeDuration(analyzeDuration),
12+
fileName(fileName),
13+
options(std::move(options)),
1514
deferred(std::move(def)) {}
1615

1716
void MediaInfoWorker::Execute() {
@@ -23,21 +22,7 @@ void MediaInfoWorker::Execute() {
2322
return;
2423
}
2524

26-
AVDictionary* options = nullptr;
27-
28-
if (probeSize) {
29-
av_dict_set_int(&options, "probesize", probeSize, 0);
30-
}
31-
32-
if (analyzeDuration) {
33-
av_dict_set_int(&options, "analyzeduration", analyzeDuration, 0);
34-
}
35-
36-
int openStatus = avformat_open_input(&avFormatContext, fileName.c_str(), NULL, &options);
37-
38-
if (options) {
39-
av_dict_free(&options);
40-
}
25+
int openStatus = avformat_open_input(&avFormatContext, fileName.c_str(), NULL, options);
4126

4227
if (openStatus < 0) {
4328
Napi::AsyncWorker::SetError("filePath could not be opened");

src/media-info-worker.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,14 @@ extern "C" {
1010
}
1111

1212
#include "avformat-context.h"
13+
#include "avdictionary.h"
1314

1415
namespace node_ffprobe {
1516

1617

1718
class MediaInfoWorker : public Napi::AsyncWorker {
1819
public:
19-
MediaInfoWorker(const Napi::Env& env, const std::string& fileName, const uint64_t probeSize, const uint64_t analyzeDuration, Napi::Promise::Deferred&& def);
20+
MediaInfoWorker(const Napi::Env& env, const std::string& fileName, AvDictionary&& options, Napi::Promise::Deferred&& def);
2021
void Execute() override;
2122
void OnOK() override;
2223
void OnError(const Napi::Error& error) override;
@@ -26,8 +27,7 @@ class MediaInfoWorker : public Napi::AsyncWorker {
2627

2728
private:
2829
std::string fileName;
29-
int64_t probeSize;
30-
int64_t analyzeDuration;
30+
AvDictionary options;
3131
Napi::Promise::Deferred deferred;
3232
static std::mutex sharedContextMutex;
3333
AVFormatContext* avFormatContext = nullptr;

test.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,14 @@ describe('getFileInfo method', () => {
5353
await expect(getFileInfo('test-assets/test')).rejects.toThrow(msg);
5454
});
5555

56+
test('invalid type of options', async() => {
57+
58+
const msg = 'options can only be an object';
59+
60+
await expect(getFileInfo('test-assets/test.mp4', 123)).rejects.toThrow(msg);
61+
await expect(getFileInfo('test-assets/test.mp4', null)).rejects.toThrow(msg);
62+
await expect(getFileInfo('test-assets/test.mp4', 'asdf')).rejects.toThrow(msg);
63+
});
5664

5765
// test('invalid media contents of a file on the filesystem', async() => {
5866

0 commit comments

Comments
 (0)