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