1 // Copyright (c) 2007-2017 Fredrik Mellbin
2 //
3 // Permission is hereby granted, free of charge, to any person obtaining a copy
4 // of this software and associated documentation files (the "Software"), to deal
5 // in the Software without restriction, including without limitation the rights
6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 // copies of the Software, and to permit persons to whom the Software is
8 // furnished to do so, subject to the following conditions:
9 //
10 // The above copyright notice and this permission notice shall be included in
11 // all copies or substantial portions of the Software.
12 //
13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 // THE SOFTWARE.
20
21 #include "ffms.h"
22
23 #include "audiosource.h"
24 #include "indexing.h"
25 #include "videosource.h"
26 #include "videoutils.h"
27
28 extern "C" {
29 #include <libavutil/avutil.h>
30 #include <libavutil/pixdesc.h>
31 }
32
33 #include <mutex>
34 #include <sstream>
35 #include <iomanip>
36
37 #ifdef FFMS_WIN_DEBUG
38 # include <windows.h>
39 #endif
40
41 static std::once_flag FFmpegOnce;
42 static std::mutex FFmpegNetwork;
43 static bool FFmpegNetworkInited = false;
44
45 #ifdef FFMS_WIN_DEBUG
46
av_log_windebug_callback(void * ptr,int level,const char * fmt,va_list vl)47 static void av_log_windebug_callback(void* ptr, int level, const char* fmt, va_list vl) {
48 if (level > av_log_get_level())
49 return;
50
51 static int print_prefix = 1;
52 static int count;
53 static char line[1024] = {}, prev[1024] = {};
54 auto avc = ptr ? *static_cast<AVClass **>(ptr) : nullptr;
55
56 int written = 0;
57 if (print_prefix && avc) {
58 written = snprintf(line, sizeof(line), "[%s @ %p]", avc->item_name(ptr), ptr);
59 }
60
61 written += vsnprintf(line + written, sizeof(line) - written, fmt, vl);
62
63 print_prefix = line[written - 1] == '\n';
64 line[sizeof(line) - 1] = 0;
65 if (print_prefix && !strcmp(line, prev)) {
66 count++;
67 return;
68 }
69 if (count > 0) {
70 std::stringstream ss;
71 ss << " Last message repeated " << count << " times\n";
72 OutputDebugStringA(ss.str().c_str());
73 count = 0;
74 }
75 OutputDebugStringA(line);
76 strcpy(prev, line);
77 }
78
79 #endif
80
FFMS_Init(int,int)81 FFMS_API(void) FFMS_Init(int, int) {
82 std::call_once(FFmpegOnce, []() {
83 FFMS_REGISTER();
84 #ifdef FFMS_WIN_DEBUG
85 av_log_set_callback(av_log_windebug_callback);
86 av_log_set_level(AV_LOG_INFO);
87 #else
88 av_log_set_level(AV_LOG_QUIET);
89 #endif
90 });
91 FFmpegNetwork.lock();
92 if (!FFmpegNetworkInited) {
93 avformat_network_init();
94 FFmpegNetworkInited = true;
95 }
96 FFmpegNetwork.unlock();
97 }
98
FFMS_Deinit()99 FFMS_API(void) FFMS_Deinit() {
100 FFmpegNetwork.lock();
101 if (FFmpegNetworkInited) {
102 avformat_network_deinit();
103 FFmpegNetworkInited = false;
104 }
105 FFmpegNetwork.unlock();
106 }
107
FFMS_GetVersion()108 FFMS_API(int) FFMS_GetVersion() {
109 return FFMS_VERSION;
110 }
111
FFMS_GetLogLevel()112 FFMS_API(int) FFMS_GetLogLevel() {
113 return av_log_get_level();
114 }
115
FFMS_SetLogLevel(int Level)116 FFMS_API(void) FFMS_SetLogLevel(int Level) {
117 av_log_set_level(Level);
118 }
119
FFMS_CreateVideoSource(const char * SourceFile,int Track,FFMS_Index * Index,int Threads,int SeekMode,FFMS_ErrorInfo * ErrorInfo)120 FFMS_API(FFMS_VideoSource *) FFMS_CreateVideoSource(const char *SourceFile, int Track, FFMS_Index *Index, int Threads, int SeekMode, FFMS_ErrorInfo *ErrorInfo) {
121 try {
122 return new FFMS_VideoSource(SourceFile, *Index, Track, Threads, SeekMode);
123 } catch (FFMS_Exception &e) {
124 e.CopyOut(ErrorInfo);
125 return nullptr;
126 }
127 }
128
FFMS_CreateAudioSource(const char * SourceFile,int Track,FFMS_Index * Index,int DelayMode,FFMS_ErrorInfo * ErrorInfo)129 FFMS_API(FFMS_AudioSource *) FFMS_CreateAudioSource(const char *SourceFile, int Track, FFMS_Index *Index, int DelayMode, FFMS_ErrorInfo *ErrorInfo) {
130 try {
131 return new FFMS_AudioSource(SourceFile, *Index, Track, DelayMode);
132 } catch (FFMS_Exception &e) {
133 e.CopyOut(ErrorInfo);
134 return nullptr;
135 }
136 }
137
FFMS_DestroyVideoSource(FFMS_VideoSource * V)138 FFMS_API(void) FFMS_DestroyVideoSource(FFMS_VideoSource *V) {
139 delete V;
140 }
141
FFMS_DestroyAudioSource(FFMS_AudioSource * A)142 FFMS_API(void) FFMS_DestroyAudioSource(FFMS_AudioSource *A) {
143 delete A;
144 }
145
FFMS_GetVideoProperties(FFMS_VideoSource * V)146 FFMS_API(const FFMS_VideoProperties *) FFMS_GetVideoProperties(FFMS_VideoSource *V) {
147 return &V->GetVideoProperties();
148 }
149
FFMS_GetAudioProperties(FFMS_AudioSource * A)150 FFMS_API(const FFMS_AudioProperties *) FFMS_GetAudioProperties(FFMS_AudioSource *A) {
151 return &A->GetAudioProperties();
152 }
153
FFMS_GetFrame(FFMS_VideoSource * V,int n,FFMS_ErrorInfo * ErrorInfo)154 FFMS_API(const FFMS_Frame *) FFMS_GetFrame(FFMS_VideoSource *V, int n, FFMS_ErrorInfo *ErrorInfo) {
155 ClearErrorInfo(ErrorInfo);
156 try {
157 return V->GetFrame(n);
158 } catch (FFMS_Exception &e) {
159 e.CopyOut(ErrorInfo);
160 return nullptr;
161 }
162 }
163
FFMS_GetFrameByTime(FFMS_VideoSource * V,double Time,FFMS_ErrorInfo * ErrorInfo)164 FFMS_API(const FFMS_Frame *) FFMS_GetFrameByTime(FFMS_VideoSource *V, double Time, FFMS_ErrorInfo *ErrorInfo) {
165 ClearErrorInfo(ErrorInfo);
166 try {
167 return V->GetFrameByTime(Time);
168 } catch (FFMS_Exception &e) {
169 e.CopyOut(ErrorInfo);
170 return nullptr;
171 }
172 }
173
FFMS_GetAudio(FFMS_AudioSource * A,void * Buf,int64_t Start,int64_t Count,FFMS_ErrorInfo * ErrorInfo)174 FFMS_API(int) FFMS_GetAudio(FFMS_AudioSource *A, void *Buf, int64_t Start, int64_t Count, FFMS_ErrorInfo *ErrorInfo) {
175 ClearErrorInfo(ErrorInfo);
176 try {
177 A->GetAudio(Buf, Start, Count);
178 } catch (FFMS_Exception &e) {
179 return e.CopyOut(ErrorInfo);
180 }
181 return FFMS_ERROR_SUCCESS;
182 }
183
FFMS_SetOutputFormatV2(FFMS_VideoSource * V,const int * TargetFormats,int Width,int Height,int Resizer,FFMS_ErrorInfo * ErrorInfo)184 FFMS_API(int) FFMS_SetOutputFormatV2(FFMS_VideoSource *V, const int *TargetFormats, int Width, int Height, int Resizer, FFMS_ErrorInfo *ErrorInfo) {
185 ClearErrorInfo(ErrorInfo);
186 try {
187 V->SetOutputFormat(reinterpret_cast<const AVPixelFormat *>(TargetFormats), Width, Height, Resizer);
188 } catch (FFMS_Exception &e) {
189 return e.CopyOut(ErrorInfo);
190 }
191 return FFMS_ERROR_SUCCESS;
192 }
193
FFMS_ResetOutputFormatV(FFMS_VideoSource * V)194 FFMS_API(void) FFMS_ResetOutputFormatV(FFMS_VideoSource *V) {
195 V->ResetOutputFormat();
196 }
197
FFMS_SetInputFormatV(FFMS_VideoSource * V,int ColorSpace,int ColorRange,int Format,FFMS_ErrorInfo * ErrorInfo)198 FFMS_API(int) FFMS_SetInputFormatV(FFMS_VideoSource *V, int ColorSpace, int ColorRange, int Format, FFMS_ErrorInfo *ErrorInfo) {
199 ClearErrorInfo(ErrorInfo);
200 try {
201 V->SetInputFormat(ColorSpace, ColorRange, static_cast<AVPixelFormat>(Format));
202 } catch (FFMS_Exception &e) {
203 return e.CopyOut(ErrorInfo);
204 }
205 return FFMS_ERROR_SUCCESS;
206 }
207
FFMS_ResetInputFormatV(FFMS_VideoSource * V)208 FFMS_API(void) FFMS_ResetInputFormatV(FFMS_VideoSource *V) {
209 V->ResetInputFormat();
210 }
211
FFMS_CreateResampleOptions(FFMS_AudioSource * A)212 FFMS_API(FFMS_ResampleOptions *) FFMS_CreateResampleOptions(FFMS_AudioSource *A) {
213 return A->CreateResampleOptions().release();
214 }
215
FFMS_DestroyResampleOptions(FFMS_ResampleOptions * options)216 FFMS_API(void) FFMS_DestroyResampleOptions(FFMS_ResampleOptions *options) {
217 delete options;
218 }
219
FFMS_SetOutputFormatA(FFMS_AudioSource * A,const FFMS_ResampleOptions * options,FFMS_ErrorInfo * ErrorInfo)220 FFMS_API(int) FFMS_SetOutputFormatA(FFMS_AudioSource *A, const FFMS_ResampleOptions *options, FFMS_ErrorInfo *ErrorInfo) {
221 ClearErrorInfo(ErrorInfo);
222 try {
223 A->SetOutputFormat(*options);
224 } catch (FFMS_Exception &e) {
225 return e.CopyOut(ErrorInfo);
226 }
227 return FFMS_ERROR_SUCCESS;
228 }
229
FFMS_DestroyIndex(FFMS_Index * Index)230 FFMS_API(void) FFMS_DestroyIndex(FFMS_Index *Index) {
231 delete Index;
232 }
233
FFMS_GetErrorHandling(FFMS_Index * Index)234 FFMS_API(FFMS_IndexErrorHandling) FFMS_GetErrorHandling(FFMS_Index *Index) {
235 return static_cast<FFMS_IndexErrorHandling>(Index->ErrorHandling);
236 }
237
FFMS_GetFirstTrackOfType(FFMS_Index * Index,int TrackType,FFMS_ErrorInfo * ErrorInfo)238 FFMS_API(int) FFMS_GetFirstTrackOfType(FFMS_Index *Index, int TrackType, FFMS_ErrorInfo *ErrorInfo) {
239 ClearErrorInfo(ErrorInfo);
240 for (int i = 0; i < static_cast<int>(Index->size()); i++)
241 if ((*Index)[i].TT == TrackType)
242 return i;
243
244 try {
245 throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_NOT_AVAILABLE,
246 "No suitable, indexed track found");
247 } catch (FFMS_Exception &e) {
248 e.CopyOut(ErrorInfo);
249 return -1;
250 }
251 }
252
FFMS_GetFirstIndexedTrackOfType(FFMS_Index * Index,int TrackType,FFMS_ErrorInfo * ErrorInfo)253 FFMS_API(int) FFMS_GetFirstIndexedTrackOfType(FFMS_Index *Index, int TrackType, FFMS_ErrorInfo *ErrorInfo) {
254 ClearErrorInfo(ErrorInfo);
255 for (int i = 0; i < static_cast<int>(Index->size()); i++)
256 if ((*Index)[i].TT == TrackType && !(*Index)[i].empty())
257 return i;
258 try {
259 throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_NOT_AVAILABLE,
260 "No suitable, indexed track found");
261 } catch (FFMS_Exception &e) {
262 e.CopyOut(ErrorInfo);
263 return -1;
264 }
265 }
266
FFMS_GetNumTracks(FFMS_Index * Index)267 FFMS_API(int) FFMS_GetNumTracks(FFMS_Index *Index) {
268 return static_cast<int>(Index->size());
269 }
270
FFMS_GetNumTracksI(FFMS_Indexer * Indexer)271 FFMS_API(int) FFMS_GetNumTracksI(FFMS_Indexer *Indexer) {
272 return Indexer->GetNumberOfTracks();
273 }
274
FFMS_GetTrackType(FFMS_Track * T)275 FFMS_API(int) FFMS_GetTrackType(FFMS_Track *T) {
276 return T->TT;
277 }
278
FFMS_GetTrackTypeI(FFMS_Indexer * Indexer,int Track)279 FFMS_API(int) FFMS_GetTrackTypeI(FFMS_Indexer *Indexer, int Track) {
280 return Indexer->GetTrackType(Track);
281 }
282
FFMS_GetCodecNameI(FFMS_Indexer * Indexer,int Track)283 FFMS_API(const char *) FFMS_GetCodecNameI(FFMS_Indexer *Indexer, int Track) {
284 return Indexer->GetTrackCodec(Track);
285 }
286
FFMS_GetNumFrames(FFMS_Track * T)287 FFMS_API(int) FFMS_GetNumFrames(FFMS_Track *T) {
288 return T->VisibleFrameCount();
289 }
290
FFMS_GetFrameInfo(FFMS_Track * T,int Frame)291 FFMS_API(const FFMS_FrameInfo *) FFMS_GetFrameInfo(FFMS_Track *T, int Frame) {
292 return T->GetFrameInfo(static_cast<size_t>(Frame));
293 }
294
FFMS_GetTrackFromIndex(FFMS_Index * Index,int Track)295 FFMS_API(FFMS_Track *) FFMS_GetTrackFromIndex(FFMS_Index *Index, int Track) {
296 return &(*Index)[Track];
297 }
298
FFMS_GetTrackFromVideo(FFMS_VideoSource * V)299 FFMS_API(FFMS_Track *) FFMS_GetTrackFromVideo(FFMS_VideoSource *V) {
300 return V->GetTrack();
301 }
302
FFMS_GetTrackFromAudio(FFMS_AudioSource * A)303 FFMS_API(FFMS_Track *) FFMS_GetTrackFromAudio(FFMS_AudioSource *A) {
304 return A->GetTrack();
305 }
306
FFMS_GetTimeBase(FFMS_Track * T)307 FFMS_API(const FFMS_TrackTimeBase *) FFMS_GetTimeBase(FFMS_Track *T) {
308 return &T->TB;
309 }
310
FFMS_WriteTimecodes(FFMS_Track * T,const char * TimecodeFile,FFMS_ErrorInfo * ErrorInfo)311 FFMS_API(int) FFMS_WriteTimecodes(FFMS_Track *T, const char *TimecodeFile, FFMS_ErrorInfo *ErrorInfo) {
312 ClearErrorInfo(ErrorInfo);
313 try {
314 T->WriteTimecodes(TimecodeFile);
315 } catch (FFMS_Exception &e) {
316 return e.CopyOut(ErrorInfo);
317 }
318 return FFMS_ERROR_SUCCESS;
319 }
320
FFMS_CreateIndexer(const char * SourceFile,FFMS_ErrorInfo * ErrorInfo)321 FFMS_API(FFMS_Indexer *) FFMS_CreateIndexer(const char *SourceFile, FFMS_ErrorInfo *ErrorInfo) {
322 ClearErrorInfo(ErrorInfo);
323 try {
324 return new FFMS_Indexer(SourceFile);
325 } catch (FFMS_Exception &e) {
326 e.CopyOut(ErrorInfo);
327 return nullptr;
328 }
329 }
330
FFMS_DoIndexing2(FFMS_Indexer * Indexer,int ErrorHandling,FFMS_ErrorInfo * ErrorInfo)331 FFMS_API(FFMS_Index *) FFMS_DoIndexing2(FFMS_Indexer *Indexer, int ErrorHandling, FFMS_ErrorInfo *ErrorInfo) {
332 ClearErrorInfo(ErrorInfo);
333
334 Indexer->SetErrorHandling(ErrorHandling);
335
336 FFMS_Index *Index = nullptr;
337 try {
338 Index = Indexer->DoIndexing();
339 } catch (FFMS_Exception &e) {
340 e.CopyOut(ErrorInfo);
341 }
342 delete Indexer;
343 return Index;
344 }
345
FFMS_TrackIndexSettings(FFMS_Indexer * Indexer,int Track,int Index,int)346 FFMS_API(void) FFMS_TrackIndexSettings(FFMS_Indexer *Indexer, int Track, int Index, int) {
347 Indexer->SetIndexTrack(Track, !!Index);
348 }
349
FFMS_TrackTypeIndexSettings(FFMS_Indexer * Indexer,int TrackType,int Index,int)350 FFMS_API(void) FFMS_TrackTypeIndexSettings(FFMS_Indexer *Indexer, int TrackType, int Index, int) {
351 Indexer->SetIndexTrackType(TrackType, !!Index);
352 }
353
FFMS_SetProgressCallback(FFMS_Indexer * Indexer,TIndexCallback IC,void * ICPrivate)354 FFMS_API(void) FFMS_SetProgressCallback(FFMS_Indexer *Indexer, TIndexCallback IC, void *ICPrivate) {
355 Indexer->SetProgressCallback(IC, ICPrivate);
356 }
357
FFMS_CancelIndexing(FFMS_Indexer * Indexer)358 FFMS_API(void) FFMS_CancelIndexing(FFMS_Indexer *Indexer) {
359 delete Indexer;
360 }
361
FFMS_ReadIndex(const char * IndexFile,FFMS_ErrorInfo * ErrorInfo)362 FFMS_API(FFMS_Index *) FFMS_ReadIndex(const char *IndexFile, FFMS_ErrorInfo *ErrorInfo) {
363 ClearErrorInfo(ErrorInfo);
364 try {
365 return new FFMS_Index(IndexFile);
366 } catch (FFMS_Exception &e) {
367 e.CopyOut(ErrorInfo);
368 return nullptr;
369 }
370 }
371
FFMS_ReadIndexFromBuffer(const uint8_t * Buffer,size_t Size,FFMS_ErrorInfo * ErrorInfo)372 FFMS_API(FFMS_Index *) FFMS_ReadIndexFromBuffer(const uint8_t *Buffer, size_t Size, FFMS_ErrorInfo *ErrorInfo) {
373 ClearErrorInfo(ErrorInfo);
374 try {
375 return new FFMS_Index(Buffer, Size);
376 } catch (FFMS_Exception &e) {
377 e.CopyOut(ErrorInfo);
378 return nullptr;
379 }
380 }
381
FFMS_IndexBelongsToFile(FFMS_Index * Index,const char * SourceFile,FFMS_ErrorInfo * ErrorInfo)382 FFMS_API(int) FFMS_IndexBelongsToFile(FFMS_Index *Index, const char *SourceFile, FFMS_ErrorInfo *ErrorInfo) {
383 ClearErrorInfo(ErrorInfo);
384 try {
385 if (!Index->CompareFileSignature(SourceFile))
386 throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_FILE_MISMATCH,
387 "The index does not belong to the file");
388 } catch (FFMS_Exception &e) {
389 return e.CopyOut(ErrorInfo);
390 }
391 return FFMS_ERROR_SUCCESS;
392 }
393
FFMS_WriteIndex(const char * IndexFile,FFMS_Index * Index,FFMS_ErrorInfo * ErrorInfo)394 FFMS_API(int) FFMS_WriteIndex(const char *IndexFile, FFMS_Index *Index, FFMS_ErrorInfo *ErrorInfo) {
395 ClearErrorInfo(ErrorInfo);
396 try {
397 Index->WriteIndexFile(IndexFile);
398 } catch (FFMS_Exception &e) {
399 return e.CopyOut(ErrorInfo);
400 }
401 return FFMS_ERROR_SUCCESS;
402 }
403
FFMS_WriteIndexToBuffer(uint8_t ** BufferPtr,size_t * Size,FFMS_Index * Index,FFMS_ErrorInfo * ErrorInfo)404 FFMS_API(int) FFMS_WriteIndexToBuffer(uint8_t **BufferPtr, size_t *Size, FFMS_Index *Index, FFMS_ErrorInfo *ErrorInfo) {
405 ClearErrorInfo(ErrorInfo);
406 uint8_t *buf;
407
408 try {
409 buf = Index->WriteIndexBuffer(Size);
410 } catch (FFMS_Exception &e) {
411 *Size = 0;
412 *BufferPtr = nullptr;
413 return e.CopyOut(ErrorInfo);
414 }
415
416 *BufferPtr = buf;
417
418 return FFMS_ERROR_SUCCESS;
419 }
420
FFMS_FreeIndexBuffer(uint8_t ** BufferPtr)421 FFMS_API(void) FFMS_FreeIndexBuffer(uint8_t **BufferPtr) {
422 av_freep(BufferPtr);
423 }
424
FFMS_GetPixFmt(const char * Name)425 FFMS_API(int) FFMS_GetPixFmt(const char *Name) {
426 return av_get_pix_fmt(Name);
427 }
428
FFMS_GetFormatNameI(FFMS_Indexer * Indexer)429 FFMS_API(const char *) FFMS_GetFormatNameI(FFMS_Indexer *Indexer) {
430 return Indexer->GetFormatName();
431 }
432