FFmpegfs Fuse Multi Media Filesystem 2.19
Loading...
Searching...
No Matches
ffmpeg_utils.cc
Go to the documentation of this file.
1/*
2 * Copyright (C) 2017-2026 Norbert Schlia (nschlia@oblivion-software.de)
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
17 *
18 * On Debian systems, the complete text of the GNU General Public License
19 * Version 3 can be found in `/usr/share/common-licenses/GPL-3'.
20 */
21
32#ifdef __cplusplus
33extern "C" {
34#endif
35// Disable annoying warnings outside our code
36#pragma GCC diagnostic push
37#pragma GCC diagnostic ignored "-Wconversion"
38#pragma GCC diagnostic ignored "-Wsign-conversion"
39#include <libswscale/swscale.h>
40#include "libavutil/ffversion.h"
41#include <libavcodec/avcodec.h>
42#pragma GCC diagnostic pop
43#ifdef __cplusplus
44}
45#endif
46
47#include "id3v1tag.h"
48#include "ffmpegfs.h"
49#include "ffmpeg_utils.h"
50
51#include <iostream>
52#include <libgen.h>
53#include <algorithm>
54#include <wordexp.h>
55#include <memory>
56#include <fstream>
57#include <locale>
58#include <vector>
59#include <cstring>
60#include <functional>
61#include <chrono>
62#include <thread>
63
64#include <iconv.h>
65#ifdef HAVE_CONFIG_H
66// This causes problems because it includes defines that collide
67// with out config.h.
68#undef HAVE_CONFIG_H
69#endif // HAVE_CONFIG_H
70#pragma GCC diagnostic push
71#pragma GCC diagnostic ignored "-Wpedantic"
72#include <chardet.h>
73#pragma GCC diagnostic pop
74#include <fnmatch.h>
75#include <unistd.h>
76
77#ifdef __cplusplus
78extern "C" {
79#endif
80// Disable annoying warnings outside our code
81#pragma GCC diagnostic push
82#pragma GCC diagnostic ignored "-Wconversion"
83#pragma GCC diagnostic ignored "-Wsign-conversion"
84#include <libswresample/swresample.h>
85#pragma GCC diagnostic pop
86#ifdef __cplusplus
87}
88#endif
89
90static int is_device(__attribute__((unused)) const AVClass *avclass);
91static std::string ffmpeg_libinfo(bool lib_exists, __attribute__((unused)) unsigned int version, __attribute__((unused)) const char *cfg, int version_minor, int version_major, int version_micro, const char * libname);
92
93#ifndef AV_ERROR_MAX_STRING_SIZE
94#define AV_ERROR_MAX_STRING_SIZE 128
95#endif // AV_ERROR_MAX_STRING_SIZE
96
97typedef std::map<const std::string, const FILETYPE, comp> FILETYPE_MAP;
103{
104 { "mp3", FILETYPE::MP3 },
105 { "mp4", FILETYPE::MP4 },
106 { "wav", FILETYPE::WAV },
107 { "ogg", FILETYPE::OGG },
108 { "webm", FILETYPE::WEBM },
109 { "mov", FILETYPE::MOV },
110 { "aiff", FILETYPE::AIFF },
111 { "opus", FILETYPE::OPUS },
112 { "prores", FILETYPE::PRORES },
113 { "alac", FILETYPE::ALAC },
114 { "png", FILETYPE::PNG },
115 { "jpg", FILETYPE::JPG },
116 { "bmp", FILETYPE::BMP },
117 { "ts", FILETYPE::TS },
118 { "hls", FILETYPE::HLS },
119 { "flac", FILETYPE::FLAC },
120 { "mkv", FILETYPE::MKV },
121};
122
124 : m_format_map{ { SAMPLE_FMT::FMT_DONTCARE, { { AV_CODEC_ID_NONE }, { AV_CODEC_ID_NONE }, { AV_CODEC_ID_NONE }, AV_SAMPLE_FMT_NONE }}}
125 , m_albumart_supported(false)
126{
127}
128
130 std::string format_name,
131 std::string fileext,
132 FORMAT_MAP format,
133 bool albumart_supported
134 )
135 : m_format_name(std::move(format_name))
136 , m_fileext(std::move(fileext))
137 , m_format_map(std::move(format))
138 , m_albumart_supported(albumart_supported)
139{
140
141}
142
144{
145 FORMAT_MAP::const_iterator it = m_format_map.find(params.m_sample_fmt);
146 if (it == m_format_map.cend())
147 {
148 // Output supports no video. End of story.
149 return AV_CODEC_ID_NONE;
150 }
151
152 if (params.m_video_codec == AV_CODEC_ID_NONE)
153 {
154 return (it->second.m_video_codec[0]); // 1st array entry is the predefined codec
155 }
156
157 return params.m_video_codec;
158}
159
160bool Format_Options::is_video_codec_supported(AVCodecID codec_id) const
161{
162 FORMAT_MAP::const_iterator it = m_format_map.find(params.m_sample_fmt);
163 if (it != m_format_map.cend())
164 {
165 for (const AVCodecID & video_codec_id : it->second.m_video_codec)
166 {
167 if (video_codec_id == codec_id)
168 {
169 return true;
170 }
171 }
172 }
173 return false;
174}
175
177{
178 std::string buffer;
179 FORMAT_MAP::const_iterator it = m_format_map.find(params.m_sample_fmt);
180 if (it != m_format_map.cend())
181 {
182 for (const AVCodecID & video_codec_id : it->second.m_video_codec)
183 {
184 if (!buffer.empty())
185 {
186 buffer += ", ";
187 }
188
189 buffer += get_video_codec_text(video_codec_id);
190 }
191 }
192
193 return buffer;
194}
195
197{
198 FORMAT_MAP::const_iterator it = m_format_map.find(params.m_sample_fmt);
199 if (it == m_format_map.cend())
200 {
201 // Output supports no audio??? Well then, end of story.
202 return AV_CODEC_ID_NONE;
203 }
204
205 if (params.m_audio_codec == AV_CODEC_ID_NONE)
206 {
207 return (it->second.m_audio_codec[0]); // 1st array entry is the predefined codec
208 }
209
210 return params.m_audio_codec;
211}
212
213bool Format_Options::is_audio_codec_supported(AVCodecID codec_id) const
214{
215 FORMAT_MAP::const_iterator it = m_format_map.find(params.m_sample_fmt);
216 if (it != m_format_map.cend())
217 {
218 for (const AVCodecID & audio_codec_id : it->second.m_audio_codec)
219 {
220 if (audio_codec_id == codec_id)
221 {
222 return true;
223 }
224 }
225 }
226 return false;
227}
228
230{
231 std::string buffer;
232 FORMAT_MAP::const_iterator it = m_format_map.find(params.m_sample_fmt);
233 if (it != m_format_map.cend())
234 {
235 for (const AVCodecID & audio_codec_id : it->second.m_audio_codec)
236 {
237 if (!buffer.empty())
238 {
239 buffer += ", ";
240 }
241
242 buffer += get_audio_codec_text(audio_codec_id);
243 }
244 }
245
246 return buffer;
247}
248
249AVSampleFormat Format_Options::sample_format() const
250{
251 FORMAT_MAP::const_iterator it = m_format_map.find(params.m_sample_fmt);
252 if (it == m_format_map.cend())
253 {
254 return AV_SAMPLE_FMT_NONE;
255 }
256
257 return (it->second.m_sample_format);
258}
259
261{
262 FORMAT_MAP::const_iterator it = m_format_map.find(params.m_sample_fmt);
263 return (it != m_format_map.cend());
264}
265
267{
268 std::string buffer;
269 for (typename FORMAT_MAP::const_iterator it = m_format_map.cbegin(); it != m_format_map.cend();)
270 {
271 buffer += get_sampleformat_text(it->first);
272
273 if (++it != m_format_map.cend())
274 {
275 buffer += ", ";
276 }
277 }
278
279 return buffer;
280}
281
282AVCodecID Format_Options::subtitle_codec(AVCodecID codec_id) const
283{
284 FORMAT_MAP::const_iterator it = m_format_map.find(params.m_sample_fmt);
285 if (it == m_format_map.cend())
286 {
287 // Output supports no subtitles. End of story.
288 return AV_CODEC_ID_NONE;
289 }
290
291 // Try to find direct match, prefer same as input stream
292 for (const AVCodecID & subtitle_codec_id : it->second.m_subtitle_codec)
293 {
294 // Also match AV_CODEC_ID_DVD_SUBTITLE to AV_CODEC_ID_DVB_SUBTITLE
295 if (subtitle_codec_id == codec_id || (codec_id == AV_CODEC_ID_DVD_SUBTITLE && subtitle_codec_id == AV_CODEC_ID_DVB_SUBTITLE))
296 {
297 return subtitle_codec_id;
298 }
299 }
300
301 // No direct match, try to find a text/text or bitmap/bitmap pair
302 if (is_text_codec(codec_id))
303 {
304 // Find a text based codec in the list
305 for (const AVCodecID & subtitle_codec_id : it->second.m_subtitle_codec)
306 {
307 if (is_text_codec(subtitle_codec_id))
308 {
309 return subtitle_codec_id;
310 }
311 }
312 }
313 else
314 {
315 // Find a bitmap based codec in the list
316 for (const AVCodecID & subtitle_codec_id : it->second.m_subtitle_codec)
317 {
318 if (!is_text_codec(subtitle_codec_id))
319 {
320 return subtitle_codec_id;
321 }
322 }
323 }
324
325 // No matching codec support
326 return AV_CODEC_ID_NONE;
327}
328
330{
331 //{
332 // Descriptive name of the format.
333 // File extension: Mostly, but not always, same as format.
334 // {
335 // {
336 // SAMPLE_FMT enum, or SAMPLE_FMT::FMT_DONTCARE if source format decides
337 // {
338 // List of video codecs
339 // List of audio codec(s)
340 // List of subtitle codec(s)
341 // AVSampleFormat to be used in encoding, if AV_SAMPLE_FMT_NONE will be determined by source
342 // }
343 // }
344 // },
345 // If album arts are supported, true; false if no album arts are supported
346 //}
347 // -----------------------------------------------------------------------------------------------------------------------
348 // MP3
349 // -----------------------------------------------------------------------------------------------------------------------
350 {
351 FILETYPE::MP3,
352 {
353 "mp3",
354 "mp3",
355 {
356 {
358 {
359 { AV_CODEC_ID_NONE },
360 { AV_CODEC_ID_MP3 },
361 { AV_CODEC_ID_NONE },
362 AV_SAMPLE_FMT_NONE,
363 }
364 }
365 },
366 true
367 }
368 },
369 // -----------------------------------------------------------------------------------------------------------------------
370 // MP4
371 // -----------------------------------------------------------------------------------------------------------------------
372 {
373 FILETYPE::MP4,
374 {
375 "mp4",
376 "mp4",
377 {
378 {
380 {
381 { AV_CODEC_ID_H264, AV_CODEC_ID_H265, AV_CODEC_ID_MPEG1VIDEO, AV_CODEC_ID_MPEG2VIDEO },
382 { AV_CODEC_ID_AAC, AV_CODEC_ID_MP3 },
383 { AV_CODEC_ID_MOV_TEXT }, // MOV Text (Apple Text Media Handler): should be AV_CODEC_ID_WEBVTT, but we get "codec not currently supported in container"
384 AV_SAMPLE_FMT_NONE,
385 }
386 }
387 },
388 false
389 }
390 },
391 // -----------------------------------------------------------------------------------------------------------------------
392 // WAV
393 // -----------------------------------------------------------------------------------------------------------------------
394 {
395 FILETYPE::WAV,
396 {
397 "wav",
398 "wav",
399 {
400 {
402 {
403 { AV_CODEC_ID_NONE },
404 { AV_CODEC_ID_PCM_S16LE },
405 { AV_CODEC_ID_NONE },
406 AV_SAMPLE_FMT_NONE,
407 }
408 },
409 {
410 SAMPLE_FMT::FMT_8, // 8 bit
411 {
412 { AV_CODEC_ID_NONE },
413 { AV_CODEC_ID_PCM_U8 },
414 { AV_CODEC_ID_NONE },
415 AV_SAMPLE_FMT_NONE,
416 }
417 },
418 {
419 SAMPLE_FMT::FMT_16, // 32 bit
420 {
421 { AV_CODEC_ID_NONE },
422 { AV_CODEC_ID_PCM_S16LE },
423 { AV_CODEC_ID_NONE },
424 AV_SAMPLE_FMT_NONE,
425 }
426 },
427 {
428 SAMPLE_FMT::FMT_24, // 24 bit
429 {
430 { AV_CODEC_ID_NONE },
431 { AV_CODEC_ID_PCM_S24LE },
432 { AV_CODEC_ID_NONE },
433 AV_SAMPLE_FMT_NONE,
434 }
435 },
436 {
437 SAMPLE_FMT::FMT_32, // 32 bit
438 {
439
440 { AV_CODEC_ID_NONE },
441 { AV_CODEC_ID_PCM_S32LE },
442 { AV_CODEC_ID_NONE },
443 AV_SAMPLE_FMT_NONE,
444 }
445 },
446 {
447 SAMPLE_FMT::FMT_64, // 64 bit
448 {
449
450 { AV_CODEC_ID_NONE },
451 { AV_CODEC_ID_PCM_S64LE },
452 { AV_CODEC_ID_NONE },
453 AV_SAMPLE_FMT_NONE,
454 }
455 },
456 {
457 SAMPLE_FMT::FMT_F16, // 16 bit float
458 {
459 { AV_CODEC_ID_NONE },
460 { AV_CODEC_ID_PCM_F16LE },
461 { AV_CODEC_ID_NONE },
462 AV_SAMPLE_FMT_NONE,
463 }
464 },
465 {
466 SAMPLE_FMT::FMT_F24, // 24 bit float
467 {
468 { AV_CODEC_ID_NONE },
469 { AV_CODEC_ID_PCM_F24LE },
470 { AV_CODEC_ID_NONE },
471 AV_SAMPLE_FMT_NONE,
472 }
473 },
474 {
475 SAMPLE_FMT::FMT_F32, // 32 bit float
476 {
477 { AV_CODEC_ID_NONE },
478 { AV_CODEC_ID_PCM_F32LE },
479 { AV_CODEC_ID_NONE },
480 AV_SAMPLE_FMT_NONE,
481 }
482 },
483 {
484 SAMPLE_FMT::FMT_F64, // 64 bit float
485 {
486 { AV_CODEC_ID_NONE },
487 { AV_CODEC_ID_PCM_F64LE },
488 { AV_CODEC_ID_NONE },
489 AV_SAMPLE_FMT_NONE,
490 }
491 }
492 },
493 false
494 }
495 },
496 // -----------------------------------------------------------------------------------------------------------------------
497 // OGG
498 // -----------------------------------------------------------------------------------------------------------------------
499 {
500 FILETYPE::OGG,
501 {
502 "ogg",
503 "ogg",
504 {
505 {
507 {
508 { AV_CODEC_ID_THEORA },
509 { AV_CODEC_ID_VORBIS },
510 { AV_CODEC_ID_NONE },
511 AV_SAMPLE_FMT_NONE,
512 }
513 }
514 },
515 false
516 }
517 },
518 // -----------------------------------------------------------------------------------------------------------------------
519 // WebM
520 // -----------------------------------------------------------------------------------------------------------------------
521 {
522 FILETYPE::WEBM,
523 {
524 "webm",
525 "webm",
526 {
527 {
529 {
530 { AV_CODEC_ID_VP9, AV_CODEC_ID_VP8, AV_CODEC_ID_AV1 },
531 { AV_CODEC_ID_OPUS, AV_CODEC_ID_VORBIS },
532 { AV_CODEC_ID_WEBVTT },
533 AV_SAMPLE_FMT_NONE,
534 }
535 }
536 },
537 false
538 }
539 },
540 // -----------------------------------------------------------------------------------------------------------------------
541 // MOV
542 // -----------------------------------------------------------------------------------------------------------------------
543 {
544 FILETYPE::MOV,
545 {
546 "mov",
547 "mov",
548 {
549 {
551 {
552 { AV_CODEC_ID_H264, AV_CODEC_ID_H265, AV_CODEC_ID_MPEG1VIDEO, AV_CODEC_ID_MPEG2VIDEO },
553 { AV_CODEC_ID_AAC, AV_CODEC_ID_AC3, AV_CODEC_ID_MP3 },
554 { AV_CODEC_ID_MOV_TEXT }, // MOV Text (Apple Text Media Handler): should be AV_CODEC_ID_WEBVTT, but we get "codec not currently supported in container"
555 AV_SAMPLE_FMT_NONE,
556 }
557 }
558 },
559 false
560 }
561 },
562 // -----------------------------------------------------------------------------------------------------------------------
563 // AIFF
564 // -----------------------------------------------------------------------------------------------------------------------
565 {
566 FILETYPE::AIFF,
567 {
568 "aiff",
569 "aiff",
570 {
571 {
573 {
574 { AV_CODEC_ID_NONE },
575 { AV_CODEC_ID_PCM_S16BE },
576 { AV_CODEC_ID_NONE },
577 AV_SAMPLE_FMT_NONE,
578 }
579 },
580 {
581 SAMPLE_FMT::FMT_16, // 16 bit
582 {
583 { AV_CODEC_ID_NONE },
584 { AV_CODEC_ID_PCM_S16BE },
585 { AV_CODEC_ID_NONE },
586 AV_SAMPLE_FMT_S16, // 16 bit
587 }
588 },
589 {
590 SAMPLE_FMT::FMT_32, // 32 bit
591 {
592 { AV_CODEC_ID_NONE },
593 { AV_CODEC_ID_PCM_S32BE },
594 { AV_CODEC_ID_NONE },
595 AV_SAMPLE_FMT_S32, // 32 bit
596 }
597 },
598 },
599 false
600 }
601 },
602 // -----------------------------------------------------------------------------------------------------------------------
603 // Opus
604 // -----------------------------------------------------------------------------------------------------------------------
605 {
606 FILETYPE::OPUS,
607 {
608 "opus",
609 "opus",
610 {
611 {
613 {
614 { AV_CODEC_ID_NONE },
615 { AV_CODEC_ID_OPUS },
616 { AV_CODEC_ID_NONE },
617 AV_SAMPLE_FMT_NONE,
618 }
619 }
620 },
621 false
622 }
623 },
624 // -----------------------------------------------------------------------------------------------------------------------
625 // Opus
626 // -----------------------------------------------------------------------------------------------------------------------
627 {
628 FILETYPE::PRORES,
629 {
630 "mov",
631 "mov",
632 {
633 {
635 {
636 { AV_CODEC_ID_PRORES },
637 { AV_CODEC_ID_PCM_S16LE },
638 { AV_CODEC_ID_MOV_TEXT }, // MOV Text (Apple Text Media Handler): should be AV_CODEC_ID_WEBVTT, but we get "codec not currently supported in container"
639 AV_SAMPLE_FMT_NONE,
640 }
641 }
642 },
643 false
644 }
645 },
646 // -----------------------------------------------------------------------------------------------------------------------
647 // ALAC
648 // -----------------------------------------------------------------------------------------------------------------------
649 {
650 FILETYPE::ALAC,
651 {
652 "m4a",
653 "m4a",
654 {
655 {
657 {
658 { AV_CODEC_ID_NONE },
659 { AV_CODEC_ID_ALAC },
660 { AV_CODEC_ID_NONE },
661 AV_SAMPLE_FMT_NONE,
662 }
663 },
664 {
665 SAMPLE_FMT::FMT_16, // 16 bit
666 {
667 { AV_CODEC_ID_NONE },
668 { AV_CODEC_ID_ALAC },
669 { AV_CODEC_ID_NONE },
670 AV_SAMPLE_FMT_S16P, // 16 bit planar
671 }
672 },
673 {
674 SAMPLE_FMT::FMT_24, // 24 bit
675 {
676 { AV_CODEC_ID_NONE },
677 { AV_CODEC_ID_ALAC },
678 { AV_CODEC_ID_NONE },
679 AV_SAMPLE_FMT_S32P, // 32 bit planar, creates 24 bit ALAC
680 }
681 }
682 },
683 false
684 }
685 },
686 // -----------------------------------------------------------------------------------------------------------------------
687 // PNG
688 // -----------------------------------------------------------------------------------------------------------------------
689 {
690 FILETYPE::PNG,
691 {
692 "png",
693 "png",
694 {
695 {
697 {
698 { AV_CODEC_ID_PNG },
699 { AV_CODEC_ID_NONE }, // Audio codec(s)
700 { AV_CODEC_ID_NONE },
701 AV_SAMPLE_FMT_NONE,
702 }
703 }
704 },
705 false
706 }
707 },
708 // -----------------------------------------------------------------------------------------------------------------------
709 // JPG
710 // -----------------------------------------------------------------------------------------------------------------------
711 {
712 FILETYPE::JPG,
713 {
714 "jpg",
715 "jpg",
716 {
717 {
719 {
720 { AV_CODEC_ID_MJPEG },
721 { AV_CODEC_ID_NONE },
722 { AV_CODEC_ID_NONE },
723 AV_SAMPLE_FMT_NONE,
724 }
725 }
726 },
727 false
728 }
729 },
730 // -----------------------------------------------------------------------------------------------------------------------
731 // BMP
732 // -----------------------------------------------------------------------------------------------------------------------
733 {
734 FILETYPE::BMP,
735 {
736 "bmp",
737 "bmp",
738 {
739 {
741 {
742 { AV_CODEC_ID_BMP },
743 { AV_CODEC_ID_NONE }, // Audio codec(s)
744 { AV_CODEC_ID_NONE },
745 AV_SAMPLE_FMT_NONE,
746 }
747 }
748 },
749 false
750 }
751 },
752 // -----------------------------------------------------------------------------------------------------------------------
753 // TS
754 // -----------------------------------------------------------------------------------------------------------------------
755 {
756 FILETYPE::TS,
757 {
758 "mpegts",
759 "ts",
760 {
761 {
763 {
764 { AV_CODEC_ID_H264, AV_CODEC_ID_H265, AV_CODEC_ID_MPEG1VIDEO, AV_CODEC_ID_MPEG2VIDEO },
765 { AV_CODEC_ID_AAC, AV_CODEC_ID_AC3, AV_CODEC_ID_MP3 },
766 { AV_CODEC_ID_DVB_SUBTITLE },
767 AV_SAMPLE_FMT_NONE,
768 }
769 }
770 },
771 false
772
773 }
774 },
775 // -----------------------------------------------------------------------------------------------------------------------
776 // HLS, same as TS
777 // -----------------------------------------------------------------------------------------------------------------------
778 {
779 FILETYPE::HLS,
780 {
781 "mpegts",
782 "ts",
783 {
784 {
786 {
787 { AV_CODEC_ID_H264, AV_CODEC_ID_H265, AV_CODEC_ID_MPEG1VIDEO, AV_CODEC_ID_MPEG2VIDEO },
788 { AV_CODEC_ID_AAC, AV_CODEC_ID_AC3, AV_CODEC_ID_MP3 },
789 { AV_CODEC_ID_DVB_SUBTITLE },
790 AV_SAMPLE_FMT_NONE,
791 }
792 }
793 },
794 false
795 }
796 },
797 // -----------------------------------------------------------------------------------------------------------------------
798 // FLAC
799 // -----------------------------------------------------------------------------------------------------------------------
800 {
801 FILETYPE::FLAC,
802 {
803 "flac",
804 "flac",
805 {
806 {
808 {
809 { AV_CODEC_ID_NONE },
810 { AV_CODEC_ID_FLAC },
811 { AV_CODEC_ID_NONE },
812 AV_SAMPLE_FMT_NONE,
813 }
814 },
815 {
816 SAMPLE_FMT::FMT_16, // 16 bit
817 {
818 { AV_CODEC_ID_NONE },
819 { AV_CODEC_ID_FLAC },
820 { AV_CODEC_ID_NONE },
821 AV_SAMPLE_FMT_S16, // Use 16 bit samples
822 }
823 },
824 {
825 SAMPLE_FMT::FMT_24, // 24 bit
826 {
827 { AV_CODEC_ID_NONE },
828 { AV_CODEC_ID_FLAC },
829 { AV_CODEC_ID_NONE },
830 AV_SAMPLE_FMT_S32, // Use 24 bit samples (yes, S32 creates 24 bit samples)
831 }
832 }
833 },
834 true
835 }
836 },
837 // -----------------------------------------------------------------------------------------------------------------------
838 // MKV
839 // -----------------------------------------------------------------------------------------------------------------------
840 {
841 FILETYPE::MKV,
842 {
843 "matroska",
844 "mkv",
845 {
846 {
848 {
849 { AV_CODEC_ID_H264, AV_CODEC_ID_H265, AV_CODEC_ID_MPEG1VIDEO, AV_CODEC_ID_MPEG2VIDEO },
850 { AV_CODEC_ID_AAC, AV_CODEC_ID_AC3, AV_CODEC_ID_MP3 },
851 { AV_CODEC_ID_ASS, AV_CODEC_ID_SUBRIP, AV_CODEC_ID_WEBVTT, AV_CODEC_ID_DVB_SUBTITLE },
852 AV_SAMPLE_FMT_NONE,
853 }
854 }
855 },
856 false
857 }
858 },
859};
860
862 m_cur_opts(&m_empty_options),
863 m_filetype(FILETYPE::UNKNOWN)
864{
865
866}
867
868bool FFmpegfs_Format::init(const std::string & desttype)
869{
870 OPTIONS_MAP::const_iterator it = m_options_map.find(get_filetype(desttype));
871 if (it == m_options_map.cend())
872 {
873 // Not found/invalid desttype
874
875 m_desttype.clear();
876 m_filetype = FILETYPE::UNKNOWN;
878
879 return false;
880 }
881 else
882 {
883 // OK
885 m_filetype = it->first;
886 m_cur_opts = &it->second;
887
888 return true;
889 }
890}
891
892const std::string & FFmpegfs_Format::desttype() const
893{
894 return m_desttype;
895}
896
897const std::string & FFmpegfs_Format::format_name() const
898{
900}
901
902const std::string & FFmpegfs_Format::fileext() const
903{
904 return m_cur_opts->m_fileext;
905}
906
908{
909 return m_filetype;
910}
911
913{
914 return (is_frameset() || is_hls());
915}
916
918{
919 return (m_filetype == FILETYPE::JPG || m_filetype == FILETYPE::PNG || m_filetype == FILETYPE::BMP);
920}
921
923{
924 return (m_filetype == FILETYPE::HLS);
925}
926
931
933{
934 return m_cur_opts->video_codec();
935}
936
937bool FFmpegfs_Format::is_video_codec_supported(AVCodecID codec_id) const
938{
939 return m_cur_opts->is_video_codec_supported(codec_id);
940}
941
943{
945}
946
948{
949 return m_cur_opts->audio_codec();
950}
951
952bool FFmpegfs_Format::is_audio_codec_supported(AVCodecID codec_id) const
953{
954 return m_cur_opts->is_audio_codec_supported(codec_id);
955}
956
958{
960}
961
962AVSampleFormat FFmpegfs_Format::sample_format() const
963{
964 return m_cur_opts->sample_format();
965}
966
971
973{
974 return m_cur_opts->sample_fmt_list();
975}
976
977AVCodecID FFmpegfs_Format::subtitle_codec(AVCodecID codec_id) const
978{
979 return m_cur_opts->subtitle_codec(codec_id);
980}
981
982const std::string & append_sep(std::string * path)
983{
984 if (path->back() != '/')
985 {
986 *path += '/';
987 }
988
989 return *path;
990}
991
992const std::string & append_filename(std::string * path, const std::string & filename)
993{
994 append_sep(path);
995
996 *path += filename;
997
998 return *path;
999}
1000
1001const std::string & remove_sep(std::string * path)
1002{
1003 if (path->back() == '/')
1004 {
1005 (*path).pop_back();
1006 }
1007
1008 return *path;
1009}
1010
1011const std::string & remove_filename(std::string * filepath)
1012{
1013 std::shared_ptr<char[]> p = new_strdup(*filepath);
1014
1015 if (p == nullptr)
1016 {
1017 errno = ENOMEM;
1018 return *filepath;
1019 }
1020
1021 *filepath = dirname(p.get());
1022
1023 append_sep(filepath);
1024 return *filepath;
1025}
1026
1027const std::string & remove_path(std::string *filepath)
1028{
1029 std::shared_ptr<char[]> p = new_strdup(*filepath);
1030
1031 if (p == nullptr)
1032 {
1033 errno = ENOMEM;
1034 return *filepath;
1035 }
1036
1037 *filepath = basename(p.get());
1038
1039 return *filepath;
1040}
1041
1042const std::string & remove_ext(std::string *filepath)
1043{
1044 size_t found;
1045
1046 found = filepath->rfind('.');
1047
1048 if (found != std::string::npos)
1049 {
1050 // Have extension
1051 filepath->resize(found);
1052 }
1053 return *filepath;
1054}
1055
1056
1057bool find_ext(std::string * ext, const std::string & filename)
1058{
1059 size_t found;
1060
1061 found = filename.rfind('.');
1062
1063 if (found == std::string::npos)
1064 {
1065 // No extension
1066 ext->clear();
1067 return false;
1068 }
1069 else
1070 {
1071 // Have extension
1072 *ext = filename.substr(found + 1);
1073 return true;
1074 }
1075}
1076
1077bool check_ext(const std::string & ext, const std::string & filename)
1078{
1079 std::string ext1;
1080 return (find_ext(&ext1, filename) && ext1 == ext);
1081}
1082
1083const std::string & replace_ext(std::string * filepath, const std::string & ext)
1084{
1085 size_t found;
1086
1087 found = filepath->rfind('.');
1088
1089 if (found == std::string::npos)
1090 {
1091 // No extension, just add
1092 *filepath += '.';
1093 }
1094 else
1095 {
1096 // Have extension, so replace
1097 filepath->resize(found + 1);
1098 }
1099
1100 *filepath += ext;
1101
1102 return *filepath;
1103}
1104
1105const std::string & append_ext(std::string * filepath, const std::string & ext)
1106{
1107 size_t found;
1108
1109 found = filepath->rfind('.');
1110
1111 if (found == std::string::npos || strcasecmp(filepath->substr(found + 1), ext) != 0)
1112 {
1113 // No extension or different extension
1114 *filepath += '.' + ext;
1115 }
1116
1117 return *filepath;
1118}
1119
1120std::shared_ptr<char[]> new_strdup(const std::string & str)
1121{
1122 size_t n = str.size() + 1;
1123 std::shared_ptr<char[]> p(new (std::nothrow) char[n]);
1124
1125 if (p == nullptr)
1126 {
1127 errno = ENOMEM;
1128 return nullptr;
1129 }
1130
1131 memcpy(p.get(), str.data(), n - 1); p.get()[n - 1] = '\0';
1132
1133 return p;
1134}
1135
1136std::string ffmpeg_geterror(int errnum)
1137{
1138 if (errnum < 0)
1139 {
1140 std::array<char, AV_ERROR_MAX_STRING_SIZE + 1> error;
1141 av_strerror(errnum, error.data(), error.size() - 1);
1142 return error.data();
1143 }
1144 else
1145 {
1146 // Prefer thread-safe strerror_r where available
1147#if defined(__GLIBC__) || defined(_POSIX_C_SOURCE)
1148 char buf[256];
1149 buf[0] = '\0';
1150# if ((_POSIX_C_SOURCE >= 200112L) && ! _GNU_SOURCE)
1151 if (strerror_r(errnum, buf, sizeof(buf)) == 0)
1152 {
1153 return std::string(buf);
1154 }
1155 else
1156 {
1157 return std::string("Unknown error: ") + std::to_string(errnum);
1158 }
1159# else
1160 // GNU-specific strerror_r returns char*
1161 char *msg = strerror_r(errnum, buf, sizeof(buf));
1162 if (msg) return std::string(msg);
1163 return std::string("Unknown error: ") + std::to_string(errnum);
1164# endif
1165#else
1166 // Fallback (may not be thread-safe on some platforms)
1167 return std::string(strerror(errnum));
1168#endif
1169 }
1170}
1171
1172int64_t ffmpeg_rescale_q(int64_t ts, const AVRational & timebase_in, const AVRational &timebase_out)
1173{
1174 if (ts == AV_NOPTS_VALUE)
1175 {
1176 return AV_NOPTS_VALUE;
1177 }
1178
1179 if (ts == 0)
1180 {
1181 return 0;
1182 }
1183
1184 return av_rescale_q(ts, timebase_in, timebase_out);
1185}
1186
1187int64_t ffmpeg_rescale_q_rnd(int64_t ts, const AVRational & timebase_in, const AVRational &timebase_out)
1188{
1189 if (ts == AV_NOPTS_VALUE)
1190 {
1191 return AV_NOPTS_VALUE;
1192 }
1193
1194 if (ts == 0)
1195 {
1196 return 0;
1197 }
1198
1199 return av_rescale_q_rnd(ts, timebase_in, timebase_out, static_cast<AVRounding>(AV_ROUND_UP | AV_ROUND_PASS_MINMAX));
1200}
1201
1202#if !HAVE_MEDIA_TYPE_STRING
1203const char *get_media_type_string(enum AVMediaType media_type)
1204{
1205 switch (media_type)
1206 {
1207 case AVMEDIA_TYPE_VIDEO:
1208 return "video";
1209 case AVMEDIA_TYPE_AUDIO:
1210 return "audio";
1211 case AVMEDIA_TYPE_DATA:
1212 return "data";
1213 case AVMEDIA_TYPE_SUBTITLE:
1214 return "subtitle";
1215 case AVMEDIA_TYPE_ATTACHMENT:
1216 return "attachment";
1217 default:
1218 return "unknown";
1219 }
1220}
1221#endif
1222
1234static std::string ffmpeg_libinfo(bool lib_exists, __attribute__((unused)) unsigned int version, __attribute__((unused)) const char *cfg, int version_minor, int version_major, int version_micro, const char * libname)
1235{
1236 std::string info;
1237
1238 if (lib_exists)
1239 {
1240 strsprintf(&info, "lib%-17s: %d.%d.%d\n",
1241 libname,
1242 version_minor,
1243 version_major,
1244 version_micro);
1245 }
1246
1247 return info;
1248}
1249
1250#define PRINT_LIB_INFO(libname, LIBNAME) \
1251 ffmpeg_libinfo(true, libname##_version(), libname##_configuration(), \
1252 LIB##LIBNAME##_VERSION_MAJOR, LIB##LIBNAME##_VERSION_MINOR, LIB##LIBNAME##_VERSION_MICRO, #libname)
1254std::string ffmpeg_libinfo()
1255{
1256 std::string info;
1257
1258 info = "FFmpeg Version : " FFMPEG_VERSION "\n";
1259
1260 // cppcheck-suppress ConfigurationNotChecked
1261 info += PRINT_LIB_INFO(avutil, AVUTIL);
1262 info += PRINT_LIB_INFO(avcodec, AVCODEC);
1263 info += PRINT_LIB_INFO(avformat, AVFORMAT);
1264 // info += PRINT_LIB_INFO(avdevice, AVDEVICE);
1265 // info += PRINT_LIB_INFO(avfilter, AVFILTER);
1266 info += PRINT_LIB_INFO(swresample, SWRESAMPLE);
1267 info += PRINT_LIB_INFO(swscale, SWSCALE);
1268 // info += PRINT_LIB_INFO(postproc, POSTPROC);
1269
1270 return info;
1271}
1272
1279static int is_device(__attribute__((unused)) const AVClass *avclass)
1280{
1281 //if (avclass == nullptr)
1282 // return 0;
1283
1284 return 0;
1285 //return AV_IS_INPUT_DEVICE(avclass->category) || AV_IS_OUTPUT_DEVICE(avclass->category);
1286}
1287
1288int show_caps(int device_only)
1289{
1290 const AVInputFormat *ifmt = nullptr;
1291 const AVOutputFormat *ofmt = nullptr;
1292 const char *last_name;
1293 int is_dev;
1294
1295 std::printf("%s\n"
1296 " D. = Demuxing supported\n"
1297 " .E = Muxing supported\n"
1298 " --\n", device_only ? "Devices:" : "File formats:");
1299 last_name = "000";
1300 for (;;)
1301 {
1302 int decode = 0;
1303 int encode = 0;
1304 const char *name = nullptr;
1305 const char *long_name = nullptr;
1306 const char *extensions = nullptr;
1307
1308 void *ofmt_opaque = nullptr;
1309 ofmt_opaque = nullptr;
1310 while ((ofmt = av_muxer_iterate(&ofmt_opaque)))
1311 {
1312 is_dev = is_device(ofmt->priv_class);
1313 if (!is_dev && device_only)
1314 {
1315 continue;
1316 }
1317
1318 if ((!name || strcmp(ofmt->name, name) < 0) &&
1319 strcmp(ofmt->name, last_name) > 0)
1320 {
1321 name = ofmt->name;
1322 long_name = ofmt->long_name;
1323 encode = 1;
1324 }
1325 }
1326
1327 void *ifmt_opaque = nullptr;
1328 ifmt_opaque = nullptr;
1329 while ((ifmt = av_demuxer_iterate(&ifmt_opaque)) != nullptr)
1330 {
1331 is_dev = is_device(ifmt->priv_class);
1332 if (!is_dev && device_only)
1333 {
1334 continue;
1335 }
1336
1337 if ((!name || strcmp(ifmt->name, name) < 0) &&
1338 strcmp(ifmt->name, last_name) > 0)
1339 {
1340 name = ifmt->name;
1341 long_name = ifmt->long_name;
1342 extensions = ifmt->extensions;
1343 encode = 0;
1344 }
1345 if (name && strcmp(ifmt->name, name) == 0)
1346 {
1347 decode = 1;
1348 }
1349 }
1350
1351 if (name == nullptr)
1352 {
1353 break;
1354 }
1355
1356 last_name = name;
1357 if (extensions == nullptr)
1358 {
1359 continue;
1360 }
1361
1362 std::printf(" %s%s %-15s %-15s %s\n",
1363 decode ? "D" : " ",
1364 encode ? "E" : " ",
1365 extensions,
1366 name,
1367 (long_name != nullptr) ? long_name : " ");
1368 }
1369 return 0;
1370}
1371
1372const char * get_codec_name(AVCodecID codec_id, bool long_name)
1373{
1374 const AVCodecDescriptor * pCodecDescriptor;
1375 const char * psz = "unknown";
1376
1377 pCodecDescriptor = avcodec_descriptor_get(codec_id);
1378
1379 if (pCodecDescriptor != nullptr)
1380 {
1381 if (pCodecDescriptor->long_name != nullptr && long_name)
1382 {
1383 psz = pCodecDescriptor->long_name;
1384 }
1385
1386 else
1387 {
1388 psz = pCodecDescriptor->name;
1389 }
1390 }
1391
1392 return psz;
1393}
1394
1395int mktree(const std::string & path, mode_t mode)
1396{
1397 std::shared_ptr<char[]> buffer = new_strdup(path);
1398
1399 if (buffer == nullptr)
1400 {
1401 return ENOMEM;
1402 }
1403
1404 std::string dir;
1405 char *saveptr;
1406 char *p = strtok_r(buffer.get(), "/", &saveptr);
1407 int status = 0;
1408
1409 while (p != nullptr)
1410 {
1411 int newstat;
1412
1413 dir += "/";
1414 dir += p;
1415
1416 errno = 0;
1417
1418 newstat = mkdir(dir.c_str(), mode);
1419
1420 if (!status && newstat && errno != EEXIST)
1421 {
1422 status = -1;
1423 break;
1424 }
1425
1426 status = newstat;
1427
1428 p = strtok_r(nullptr, "/", &saveptr);
1429 }
1430
1431 return status;
1432}
1433
1434void tempdir(std::string & path)
1435{
1436 const char *temp = getenv("TMPDIR");
1437
1438 if (temp != nullptr)
1439 {
1440 path = temp;
1441 return;
1442 }
1443
1444 path = P_tmpdir;
1445
1446 if (!path.empty())
1447 {
1448 return;
1449 }
1450
1451 path = "/tmp";
1452}
1453
1454int supports_albumart(FILETYPE filetype)
1455{
1456 // Could also allow OGG but the format requires special handling for album arts
1457 return (filetype == FILETYPE::MP3 || filetype == FILETYPE::MP4);
1458}
1459
1460FILETYPE get_filetype(const std::string & desttype)
1461{
1462 try
1463 {
1464 return (filetype_map.at(desttype.c_str()));
1465 }
1466 catch (const std::out_of_range& /*oor*/)
1467 {
1468 //std::cerr << "Out of Range error: " << oor.what() << std::endl;
1469 return FILETYPE::UNKNOWN;
1470 }
1471}
1472
1473std::string get_filetype_text(FILETYPE filetype)
1474{
1475 FILETYPE_MAP::const_iterator it = search_by_value(filetype_map, filetype);
1476 if (it != filetype_map.cend())
1477 {
1478 return it->first;
1479 }
1480 return "INVALID";
1481}
1482
1483
1484FILETYPE get_filetype_from_list(const std::string & desttypelist)
1485{
1486 std::vector<std::string> desttype = split(desttypelist, ",");
1487 FILETYPE filetype = FILETYPE::UNKNOWN;
1488
1489 // Find first matching entry
1490 for (size_t n = 0; n < desttype.size() && filetype != FILETYPE::UNKNOWN; n++)
1491 {
1492 filetype = get_filetype(desttype[n]);
1493 }
1494 return filetype;
1495}
1496
1497void init_id3v1(ID3v1 *id3v1)
1498{
1499 // Initialise ID3v1.1 tag structure
1500 std::memset(id3v1, ' ', sizeof(ID3v1));
1501 std::memcpy(&id3v1->m_tag, "TAG", 3);
1502 id3v1->m_padding = '\0';
1503 id3v1->m_title_no = 0;
1504 id3v1->m_genre = 0;
1505}
1506
1507std::string format_number(int64_t value)
1508{
1509 if (!value)
1510 {
1511 return "unlimited";
1512 }
1513
1514 if (value == AV_NOPTS_VALUE)
1515 {
1516 return "unset";
1517 }
1518
1519 std::string buffer;
1520 return strsprintf(&buffer, "%" PRId64, value);
1521}
1522
1523std::string format_bitrate(BITRATE value)
1524{
1525 if (value == static_cast<BITRATE>(AV_NOPTS_VALUE))
1526 {
1527 return "unset";
1528 }
1529
1530 if (value > 1000000)
1531 {
1532 std::string buffer;
1533 return strsprintf(&buffer, "%.2f Mbps", static_cast<double>(value) / 1000000);
1534 }
1535 else if (value > 1000)
1536 {
1537 std::string buffer;
1538 return strsprintf(&buffer, "%.1f kbps", static_cast<double>(value) / 1000);
1539 }
1540 else
1541 {
1542 std::string buffer;
1543 return strsprintf(&buffer, "%" PRId64 " bps", value);
1544 }
1545}
1546
1547std::string format_samplerate(int value)
1548{
1549 if (value == static_cast<int>(AV_NOPTS_VALUE))
1550 {
1551 return "unset";
1552 }
1553
1554 if (value < 1000)
1555 {
1556 std::string buffer;
1557 return strsprintf(&buffer, "%u Hz", value);
1558 }
1559 else
1560 {
1561 std::string buffer;
1562 return strsprintf(&buffer, "%.3f kHz", static_cast<double>(value) / 1000);
1563 }
1564}
1565
1566#define STR_VALUE(arg) #arg
1567#define X(name) STR_VALUE(name)
1569std::string format_duration(int64_t value, uint32_t fracs /*= 3*/)
1570{
1571 if (value == AV_NOPTS_VALUE)
1572 {
1573 return "unset";
1574 }
1575
1576 std::string buffer;
1577 std::string duration;
1578 unsigned hours = static_cast<unsigned>((value / AV_TIME_BASE) / (3600));
1579 unsigned mins = static_cast<unsigned>(((value / AV_TIME_BASE) % 3600) / 60);
1580 unsigned secs = static_cast<unsigned>((value / AV_TIME_BASE) % 60);
1581
1582 if (hours)
1583 {
1584 duration = strsprintf(&buffer, "%02u:", hours);
1585 }
1586
1587 duration += strsprintf(&buffer, "%02u:%02u", mins, secs);
1588 if (fracs)
1589 {
1590 unsigned decimals = static_cast<unsigned>(value % AV_TIME_BASE);
1591 duration += strsprintf(&buffer, ".%0*u", sizeof(X(AV_TIME_BASE)) - 2, decimals).substr(0, fracs + 1);
1592 }
1593 return duration;
1594}
1595
1596std::string format_time(time_t value)
1597{
1598 if (!value)
1599 {
1600 return "unlimited";
1601 }
1602
1603 if (value == static_cast<time_t>(AV_NOPTS_VALUE))
1604 {
1605 return "unset";
1606 }
1607
1608 std::string buffer;
1609 std::string time;
1610 int weeks;
1611 int days;
1612 int hours;
1613 int mins;
1614 int secs;
1615
1616 weeks = static_cast<int>(value / (60*60*24*7));
1617 value -= weeks * (60*60*24*7);
1618 days = static_cast<int>(value / (60*60*24));
1619 value -= days * (60*60*24);
1620 hours = static_cast<int>(value / (60*60));
1621 value -= hours * (60*60);
1622 mins = static_cast<int>(value / (60));
1623 value -= mins * (60);
1624 secs = static_cast<int>(value);
1625
1626 if (weeks)
1627 {
1628 time = strsprintf(&buffer, "%iw ", weeks);
1629 }
1630 if (days)
1631 {
1632 time += strsprintf(&buffer, "%id ", days);
1633 }
1634 if (hours)
1635 {
1636 time += strsprintf(&buffer, "%ih ", hours);
1637 }
1638 if (mins)
1639 {
1640 time += strsprintf(&buffer, "%im ", mins);
1641 }
1642 if (secs)
1643 {
1644 time += strsprintf(&buffer, "%is ", secs);
1645 }
1646 return time;
1647}
1648
1649std::string format_size(uint64_t value)
1650{
1651 if (!value)
1652 {
1653 return "unlimited";
1654 }
1655
1656 if (value == static_cast<uint64_t>(AV_NOPTS_VALUE))
1657 {
1658 return "unset";
1659 }
1660
1661 if (value > 1024*1024*1024*1024LL)
1662 {
1663 std::string buffer;
1664 return strsprintf(&buffer, "%.3f TB", static_cast<double>(value) / (1024*1024*1024*1024LL));
1665 }
1666 else if (value > 1024*1024*1024)
1667 {
1668 std::string buffer;
1669 return strsprintf(&buffer, "%.2f GB", static_cast<double>(value) / (1024*1024*1024));
1670 }
1671 else if (value > 1024*1024)
1672 {
1673 std::string buffer;
1674 return strsprintf(&buffer, "%.1f MB", static_cast<double>(value) / (1024*1024));
1675 }
1676 else if (value > 1024)
1677 {
1678 std::string buffer;
1679 return strsprintf(&buffer, "%.1f KB", static_cast<double>(value) / (1024));
1680 }
1681 else
1682 {
1683 std::string buffer;
1684 return strsprintf(&buffer, "%" PRIu64 " bytes", value);
1685 }
1686}
1687
1688std::string format_size_ex(uint64_t value)
1689{
1690 std::string buffer;
1691 return format_size(value) + strsprintf(&buffer, " (%" PRIu64 " bytes)", value);
1692}
1693
1694std::string format_result_size(size_t size_resulting, size_t size_predicted)
1695{
1696 if (size_resulting >= size_predicted)
1697 {
1698 size_t value = size_resulting - size_predicted;
1699 return format_size(value);
1700 }
1701 else
1702 {
1703 size_t value = size_predicted - size_resulting;
1704 return "-" + format_size(value);
1705 }
1706}
1707
1708std::string format_result_size_ex(size_t size_resulting, size_t size_predicted)
1709{
1710 if (size_resulting >= size_predicted)
1711 {
1712 std::string buffer;
1713 size_t value = size_resulting - size_predicted;
1714 return format_size(value) + strsprintf(&buffer, " (%zu bytes)", value);
1715 }
1716 else
1717 {
1718 std::string buffer;
1719 size_t value = size_predicted - size_resulting;
1720 return "-" + format_size(value) + strsprintf(&buffer, " (-%zu bytes)", value);
1721 }
1722}
1723
1729static void print_fps(double d, const char *postfix)
1730{
1731 long v = lrint(d * 100);
1732 if (!v)
1733 {
1734 std::printf("%1.4f %s\n", d, postfix);
1735 }
1736 else if (v % 100)
1737 {
1738 std::printf("%3.2f %s\n", d, postfix);
1739 }
1740 else if (v % (100 * 1000))
1741 {
1742 std::printf("%1.0f %s\n", d, postfix);
1743 }
1744 else
1745 {
1746 std::printf("%1.0fk %s\n", d / 1000, postfix);
1747 }
1748}
1749
1750int print_stream_info(const AVStream* stream)
1751{
1752 int ret = 0;
1753
1754 AVCodecContext *avctx = avcodec_alloc_context3(nullptr);
1755 if (avctx == nullptr)
1756 {
1757 return AVERROR(ENOMEM);
1758 }
1759
1760 ret = avcodec_parameters_to_context(avctx, stream->codecpar);
1761 if (ret < 0)
1762 {
1763 avcodec_free_context(&avctx);
1764 return ret;
1765 }
1766
1767 // Fields which are missing from AVCodecParameters need to be taken from the AVCodecContext
1768 // avctx->properties = output_stream->codec->properties;
1769 // avctx->codec = output_stream->codec->codec;
1770 // avctx->qmin = output_stream->codec->qmin;
1771 // avctx->qmax = output_stream->codec->qmax;
1772 // avctx->coded_width = output_stream->codec->coded_width;
1773 // avctx->coded_height = output_stream->codec->coded_height;
1774 int fps = stream->avg_frame_rate.den && stream->avg_frame_rate.num;
1775 int tbr = stream->r_frame_rate.den && stream->r_frame_rate.num;
1776 int tbn = stream->time_base.den && stream->time_base.num;
1777 int tbc = avctx->time_base.den && avctx->time_base.num; // Even the currently latest (lavf 58.10.100) refers to AVStream codec->time_base member... (See dump.c dump_stream_format)
1778
1779 if (fps)
1780 print_fps(av_q2d(stream->avg_frame_rate), "avg fps");
1781 if (tbr)
1782 print_fps(av_q2d(stream->r_frame_rate), "Real base framerate (tbr)");
1783 if (tbn)
1784 print_fps(1 / av_q2d(stream->time_base), "stream timebase (tbn)");
1785 if (tbc)
1786 print_fps(1 / av_q2d(avctx->time_base), "codec timebase (tbc)");
1787
1788 avcodec_free_context(&avctx);
1789
1790 return ret;
1791}
1792
1793std::string fourcc_make_string(std::string * buf, uint32_t fourcc)
1794{
1795 std::string fourcc2str(AV_FOURCC_MAX_STRING_SIZE, '\0');
1796 av_fourcc_make_string(&fourcc2str[0], fourcc);
1797 fourcc2str.resize(std::strlen(fourcc2str.c_str()));
1798 *buf = fourcc2str;
1799 return *buf;
1800}
1801
1802void exepath(std::string * path)
1803{
1804 std::array<char, PATH_MAX + 1> result;
1805 ssize_t count = readlink("/proc/self/exe", result.data(), result.size() - 1);
1806 if (count != -1)
1807 {
1808 *path = dirname(result.data());
1809 append_sep(path);
1810 }
1811 else
1812 {
1813 path->clear();
1814 }
1815}
1816
1817std::string &ltrim(std::string &s)
1818{
1819 s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not_fn(std::function<int(int)>(isspace))));
1820 return s;
1821}
1822
1823std::string &rtrim(std::string &s)
1824{
1825 s.erase(std::find_if(s.rbegin(), s.rend(), std::not_fn(std::function<int(int)>(isspace))).base(), s.end());
1826 return s;
1827}
1828
1829std::string &trim(std::string &s)
1830{
1831 return ltrim(rtrim(s));
1832}
1833
1834std::string replace_all(std::string str, const std::string& from, const std::string& to)
1835{
1836 return replace_all(&str, from, to);
1837}
1838
1839std::string replace_all(std::string *str, const std::string& from, const std::string& to)
1840{
1841 size_t start_pos = 0;
1842 while ((start_pos = str->find(from, start_pos)) != std::string::npos)
1843 {
1844 str->replace(start_pos, from.length(), to);
1845 start_pos += to.length(); // Handles case where 'to' is a substring of 'from'
1846 }
1847 return *str;
1848}
1849
1850bool replace_start(std::string *str, const std::string& from, const std::string& to)
1851{
1852#if __cplusplus >= 202002L
1853 // C++20 (and later) code
1854 if (str->starts_with(from) == 0)
1855#else
1856 if (str->find(from, 0) == 0)
1857#endif
1858 {
1859 str->replace(0, from.length(), to);
1860 return true;
1861 }
1862
1863 return false;
1864}
1865
1866int strcasecmp(const std::string & s1, const std::string & s2)
1867{
1868 return ::strcasecmp(s1.c_str(), s2.c_str());
1869}
1870
1871int reg_compare(const std::string & value, const std::string & pattern, std::regex::flag_type flag)
1872{
1873 int reti;
1874
1875 try
1876 {
1877 std::regex rgx(pattern, flag);
1878
1879 reti = (std::regex_search(value, rgx) == true) ? 0 : 1;
1880 }
1881 catch(const std::regex_error& e)
1882 {
1883 std::cerr << "regex_error caught: " << e.what() << std::endl;
1884 if(e.code() == std::regex_constants::error_brack)
1885 std::cerr << "The code was error_brack" << std::endl;
1886
1887 reti = -1;
1888 }
1889
1890 return reti;
1891}
1892
1893const std::string & expand_path(std::string *tgt, const std::string & src)
1894{
1895 wordexp_t exp_result;
1896 if (!wordexp(replace_all(src, " ", "\\ ").c_str(), &exp_result, 0))
1897 {
1898 *tgt = exp_result.we_wordv[0];
1899 wordfree(&exp_result);
1900 }
1901 else
1902 {
1903 *tgt = src;
1904 }
1905
1906 return *tgt;
1907}
1908
1909int is_mount(const std::string & path)
1910{
1911 int ret = 0;
1912
1913 try
1914 {
1915 std::shared_ptr<char[]> orig_name;
1916 struct stat file_stat;
1917 struct stat parent_stat;
1918 char * parent_name = nullptr;
1919
1920 orig_name = new_strdup(path);
1921
1922 if (orig_name == nullptr)
1923 {
1924 std::fprintf(stderr, "is_mount(): Out of memory\n");
1925 errno = ENOMEM;
1926 throw -1;
1927 }
1928
1929 // get the parent directory of the file
1930 parent_name = dirname(orig_name.get());
1931
1932 // get the file's stat info
1933 if (-1 == stat(path.c_str(), &file_stat))
1934 {
1935 std::fprintf(stderr, "is_mount(): (%i) %s\n", errno, strerror(errno));
1936 throw -1;
1937 }
1938
1939 //determine whether the supplied file is a directory
1940 // if it isn't, then it can't be a mountpoint.
1941 if (!(file_stat.st_mode & S_IFDIR))
1942 {
1943 std::fprintf(stderr, "is_mount(): %s is not a directory.\n", path.c_str());
1944 throw -1;
1945 }
1946
1947 // get the parent's stat info
1948 if (-1 == stat(parent_name, &parent_stat))
1949 {
1950 std::fprintf(stderr, "is_mount(): (%i) %s\n", errno, strerror(errno));
1951 throw -1;
1952 }
1953
1954 // if file and parent have different device ids,
1955 // then the file is a mount point
1956 // or, if they refer to the same file,
1957 // then it's probably the root directory
1958 // and therefore a mountpoint
1959 // style: Redundant condition: file_stat.st_dev==parent_stat.st_dev.
1960 // 'A || (!A && B)' is equivalent to 'A || B' [redundantCondition]
1961 //if (file_stat.st_dev != parent_stat.st_dev ||
1962 // (file_stat.st_dev == parent_stat.st_dev &&
1963 // file_stat.st_ino == parent_stat.st_ino))
1964 if (file_stat.st_dev != parent_stat.st_dev || file_stat.st_ino == parent_stat.st_ino)
1965 {
1966 // IS a mountpoint
1967 ret = 1;
1968 }
1969 else
1970 {
1971 // is NOT a mountpoint
1972 ret = 0;
1973 }
1974 }
1975 catch (int _ret)
1976 {
1977 ret = _ret;
1978 }
1979
1980 return ret;
1981}
1982
1983std::vector<std::string> split(const std::string& input, const std::string & regex)
1984{
1985 // passing -1 as the submatch index parameter performs splitting
1986 std::regex re(regex);
1987 std::sregex_token_iterator first{input.cbegin(), input.cend(), re, -1},
1988 last;
1989 return {first, last};
1990}
1991
1992std::string sanitise_filepath(std::string * filepath)
1993{
1994 std::array<char, PATH_MAX + 1> resolved_name;
1995
1996 if (realpath(filepath->c_str(), resolved_name.data()) != nullptr)
1997 {
1998 *filepath = resolved_name.data();
1999 return *filepath;
2000 }
2001
2002 // realpath has the strange feature to remove a trailing slash if there.
2003 // To mimick its behaviour, if realpath fails, at least remove it.
2004 std::string _filepath(*filepath);
2005 remove_sep(&_filepath);
2006 return _filepath;
2007}
2008
2009std::string sanitise_filepath(const std::string & filepath)
2010{
2011 std::string buffer(filepath);
2012 return sanitise_filepath(&buffer);
2013}
2014
2015void append_basepath(std::string *origpath, const char* path)
2016{
2017 *origpath = params.m_basepath;
2018 if (*path == '/')
2019 {
2020 ++path;
2021 }
2022 *origpath += path;
2023
2024 sanitise_filepath(origpath);
2025}
2026
2027bool is_album_art(AVCodecID codec_id, const AVRational * frame_rate)
2028{
2029 if (codec_id == AV_CODEC_ID_PNG || codec_id == AV_CODEC_ID_BMP)
2030 {
2031 // PNG or BMP: must be an album art stream
2032 return true;
2033 }
2034
2035 if (codec_id != AV_CODEC_ID_MJPEG)
2036 {
2037 // Anything else than MJPEG is never an album art stream
2038 return false;
2039 }
2040
2041 if (frame_rate != nullptr && frame_rate->den)
2042 {
2043 double dbFrameRate = static_cast<double>(frame_rate->num) / frame_rate->den;
2044
2045 // If frame rate is < 300 fps this most likely is a video
2046 if (dbFrameRate < 300)
2047 {
2048 // This is a video
2049 return false;
2050 }
2051 }
2052
2053 return true;
2054}
2055
2056int nocasecompare(const std::string & lhs, const std::string &rhs)
2057{
2058 return (strcasecmp(lhs, rhs));
2059}
2060
2061size_t get_disk_free(std::string & path)
2062{
2063 struct statvfs buf;
2064
2065 if (statvfs(path.c_str(), &buf))
2066 {
2067 return 0;
2068 }
2069
2070 return static_cast<size_t>(buf.f_bfree * buf.f_bsize);
2071}
2072
2073bool check_ignore(size_t size, size_t offset)
2074{
2075 std::array<size_t, 3> blocksize_arr = { 0x2000, 0x8000, 0x10000 };
2076 bool ignore = false;
2077
2078 for (const size_t & blocksize: blocksize_arr)
2079 {
2080 size_t rest;
2081 bool match;
2082
2083 match = !(offset % blocksize); // Must be multiple of block size
2084 if (!match)
2085 {
2086 continue;
2087 }
2088
2089 rest = size % offset; // Calculate rest.
2090 ignore = match && (rest < blocksize); // Ignore of rest is less than block size
2091
2092 if (ignore)
2093 {
2094 break;
2095 }
2096 }
2097
2098 return ignore;
2099}
2100
2101std::string make_filename(uint32_t file_no, const std::string & fileext)
2102{
2103 std::string buffer;
2104 return strsprintf(&buffer, "%06u.%s", file_no, fileext.c_str());
2105}
2106
2107bool file_exists(const std::string & filename)
2108{
2109 return (access(filename.c_str(), F_OK) != -1);
2110}
2111
2112void make_upper(std::string * input)
2113{
2114 std::for_each(std::begin(*input), std::end(*input), [](char& c) {
2115 c = static_cast<char>(std::toupper(static_cast<unsigned char>(c)));
2116 });
2117}
2118
2119void make_lower(std::string * input)
2120{
2121 std::for_each(std::begin(*input), std::end(*input), [](char& c) {
2122 c = static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
2123 });
2124}
2125
2126const char * hwdevice_get_type_name(AVHWDeviceType dev_type)
2127{
2128 const char *type_name = av_hwdevice_get_type_name(dev_type);
2129 return (type_name != nullptr ? type_name : "unknown");
2130}
2131
2132int to_utf8(std::string & text, const std::string & encoding)
2133{
2134 iconv_t conv = iconv_open("UTF-8", encoding.c_str());
2135 if (conv == (iconv_t) -1)
2136 {
2137 int open_err = errno;
2138 // Some platforms (e.g. BSD/macOS) use UCS-* names instead of UTF-*
2139 const char *fallback = nullptr;
2140 if (open_err == EINVAL)
2141 {
2142 if (encoding == "UTF-32LE") fallback = "UCS-4LE";
2143 else if (encoding == "UTF-32BE") fallback = "UCS-4BE";
2144 else if (encoding == "UTF-16LE") fallback = "UCS-2LE";
2145 else if (encoding == "UTF-16BE") fallback = "UCS-2BE";
2146 }
2147 if (fallback != nullptr)
2148 {
2149 conv = iconv_open("UTF-8", fallback);
2150 if (conv == (iconv_t) -1)
2151 {
2152 // Fallback also failed
2153 return errno;
2154 }
2155 }
2156 else
2157 {
2158 // Error in iconv_open, errno in return code.
2159 return open_err;
2160 }
2161 }
2162 {
2163 // Error in iconv_open, errno in return code.
2164 return errno;
2165 }
2166
2167 std::vector<char> src;
2168 std::vector<char> dst;
2169 size_t srclen = text.size();
2170 size_t dstlen = 2 * srclen;
2171
2172 src.resize(srclen + 1);
2173 dst.resize(dstlen + 2);
2174
2175 char * pIn = src.data();
2176 char * pOut = dst.data();
2177
2178 memcpy(pIn, text.data(), srclen); pIn[srclen] = '\0';
2179
2180 size_t len = iconv(conv, &pIn, &srclen, &pOut, &dstlen);
2181 if (len != (size_t) -1)
2182 {
2183 *pOut = '\0';
2184
2185 iconv_close(conv);
2186
2187 text = dst.data();
2188
2189 return 0; // Conversion OK
2190 }
2191 else
2192 {
2193 int orgerrno = errno;
2194
2195 iconv_close(conv);
2196
2197 // Error in iconv, errno in return code.
2198 return orgerrno;
2199 }
2200}
2201
2202int get_encoding (const char * str, std::string & encoding)
2203{
2204 DetectObj *obj = detect_obj_init();
2205
2206 if (obj == nullptr)
2207 {
2208 // Memory Allocation failed
2209 return ENOMEM; // CHARDET_MEM_ALLOCATED_FAIL;
2210 }
2211
2212#ifndef CHARDET_BINARY_SAFE
2213 // before 1.0.5. This API is deprecated on 1.0.5
2214 switch (detect (str, &obj))
2215#else
2216 // from 1.0.5
2217 switch (detect_r (str, strlen (str), &obj))
2218#endif
2219 {
2220 case CHARDET_OUT_OF_MEMORY :
2221 // Out of memory on handle processing
2222 detect_obj_free (&obj);
2223 return ENOMEM; // CHARDET_OUT_OF_MEMORY;
2224 case CHARDET_NULL_OBJECT :
2225 // 1st argument of chardet() must be allocated with detect_obj_init API
2226 return EINVAL; // CHARDET_NULL_OBJECT;
2227 }
2228
2229 //#ifndef CHARDET_BOM_CHECK
2230 // printf ("encoding: %s, confidence: %f\n", obj->encoding, obj->confidence);
2231 //#else
2232 // // from 1.0.6 support return whether exists BOM
2233 // printf (
2234 // "encoding: %s, confidence: %f, exist BOM: %d\n",
2235 // obj->encoding, obj->confidence, obj->bom
2236 // );
2237 //#endif
2238 encoding = obj->encoding;
2239 detect_obj_free (&obj);
2240
2241 return 0;
2242}
2243
2244int read_file(const std::string & path, std::string & result)
2245{
2246 constexpr std::array<char, 3> UTF_8_BOM = { '\xEF', '\xBB', '\xBF' };
2247 constexpr std::array<char, 2> UTF_16_BE_BOM = { '\xFE', '\xFF' };
2248 constexpr std::array<char, 2> UTF_16_LE_BOM = { '\xFF', '\xFE' };
2249 constexpr std::array<char, 4> UTF_32_BE_BOM = { '\x00', '\x00', '\xFE', '\xFF' };
2250 constexpr std::array<char, 4> UTF_32_LE_BOM = { '\xFF', '\xFE', '\x00', '\x00' };
2251
2252 std::ifstream ifs;
2253 ENCODING encoding = ENCODING::ASCII;
2254 int res = 0;
2255
2256 try
2257 {
2258 ifs.open(path, std::ios::binary);
2259
2260 if (!ifs.is_open())
2261 {
2262 // Unable to read file
2263 result.clear();
2264 throw errno;
2265 }
2266
2267 if (ifs.eof())
2268 {
2269 // Empty file
2270 result.clear();
2271 throw ENCODING::ASCII;
2272 }
2273
2274 // Read the bottom mark
2275 std::array<char, 4> BOM;
2276 ifs.read(BOM.data(), BOM.size());
2277
2278 // If you feel tempted to reorder these checks please note
2279 // that UTF_32_LE_BOM must be done before UTF_16_LE_BOM to
2280 // avoid misdetection :)
2281 if (!memcmp(BOM.data(), UTF_32_LE_BOM.data(), UTF_32_LE_BOM.size()))
2282 {
2283 // The file contains UTF-32LE BOM
2284 encoding = ENCODING::UTF32LE_BOM;
2285 ifs.seekg(UTF_32_LE_BOM.size());
2286 }
2287 else if (!memcmp(BOM.data(), UTF_32_BE_BOM.data(), UTF_32_BE_BOM.size()))
2288 {
2289 // The file contains UTF-32BE BOM
2290 encoding = ENCODING::UTF32BE_BOM;
2291 ifs.seekg(UTF_32_BE_BOM.size());
2292 }
2293 else if (!memcmp(BOM.data(), UTF_16_LE_BOM.data(), UTF_16_LE_BOM.size()))
2294 {
2295 // The file contains UTF-16LE BOM
2296 encoding = ENCODING::UTF16LE_BOM;
2297 ifs.seekg(UTF_16_LE_BOM.size());
2298 }
2299 else if (!memcmp(BOM.data(), UTF_16_BE_BOM.data(), UTF_16_BE_BOM.size()))
2300 {
2301 // The file contains UTF-16BE BOM
2302 encoding = ENCODING::UTF16BE_BOM;
2303 ifs.seekg(UTF_16_BE_BOM.size());
2304 }
2305 else if (!memcmp(BOM.data(), UTF_8_BOM.data(), UTF_8_BOM.size()))
2306 {
2307 // The file contains UTF-8 BOM
2308 encoding = ENCODING::UTF8_BOM;
2309 ifs.seekg(UTF_8_BOM.size());
2310 }
2311 else
2312 {
2313 // The file does not have BOM
2314 encoding = ENCODING::ASCII;
2315 ifs.seekg(0);
2316 }
2317
2318 switch (encoding)
2319 {
2321 {
2322 std::stringstream ss;
2323 ss << ifs.rdbuf();
2324 result = ss.str(); // raw UTF-16LE bytes (without BOM)
2325 res = to_utf8(result, "UTF-16LE");
2326 if (res) { throw res; }
2327 break;
2328 }
2330 {
2331 std::stringstream ss;
2332 ss << ifs.rdbuf();
2333 result = ss.str(); // raw UTF-16BE bytes (without BOM)
2334 res = to_utf8(result, "UTF-16BE");
2335 if (res) { throw res; }
2336 break;
2337 }
2339 {
2340 std::stringstream ss;
2341 ss << ifs.rdbuf();
2342 result = ss.str(); // raw UTF-32LE bytes (without BOM)
2343 res = to_utf8(result, "UTF-32LE");
2344 if (res) { throw res; }
2345 break;
2346 }
2348 {
2349 std::stringstream ss;
2350 ss << ifs.rdbuf();
2351 result = ss.str(); // raw UTF-32BE bytes (without BOM)
2352 res = to_utf8(result, "UTF-32BE");
2353 if (res) { throw res; }
2354 break;
2355 }
2356 case ENCODING::UTF8_BOM:
2357 {
2358 // Already UTF-8, nothing to do
2359 std::stringstream ss;
2360 ss << ifs.rdbuf();
2361 result = ss.str();
2362 break;
2363 }
2364 default: // ENCODING::ASCII
2365 {
2366 // This is a bit tricky, we have to try to determine the actual encoding.
2367 std::stringstream ss;
2368 ss << ifs.rdbuf();
2369 result = ss.str();
2370
2371 // Using libchardet to guess the encoding
2372 std::string encoding_name;
2373 res = get_encoding(result.c_str(), encoding_name);
2374 if (res)
2375 {
2376 throw res;
2377 }
2378
2379 if (encoding_name != "UTF-8")
2380 {
2381 // If not UTF-8, do the actual conversion
2382 res = to_utf8(result, encoding_name);
2383 if (res)
2384 {
2385 throw res;
2386 }
2387 }
2388 break;
2389 }
2390 }
2391 res = static_cast<int>(encoding);
2392 }
2393 catch (const std::system_error& e)
2394 {
2395 res = errno;
2396 }
2397 catch (int _res)
2398 {
2399 res = _res;
2400 }
2401 return res;
2402}
2403
2404void stat_set_size(struct stat *st, size_t size)
2405{
2406#if defined __x86_64__ || !defined __USE_FILE_OFFSET64
2407 st->st_size = static_cast<__off_t>(size);
2408#else
2409 st->st_size = static_cast<__off64_t>(size);
2410#endif
2411 st->st_blocks = (st->st_size + 512 - 1) / 512;
2412}
2413
2414bool detect_docker()
2415{
2416 try
2417 {
2418 std::ifstream const in_stream("/proc/self/cgroup");
2419 std::stringstream buffer;
2420 buffer << in_stream.rdbuf();
2421 auto const& content_as_string = buffer.str();
2422 return std::string::npos != content_as_string.find("/docker");
2423 }
2424 catch (std::exception const& ex)
2425 {
2426 std::fprintf(stderr, "detect_docker(): Unable check if running in docker or not, exception: %s.", ex.what());
2427 return false;
2428 }
2429}
2430
2431bool is_text_codec(AVCodecID codec_id)
2432{
2433 //AV_CODEC_ID_DVD_SUBTITLE = 0x17000,
2434 //AV_CODEC_ID_DVB_SUBTITLE,
2435 //AV_CODEC_ID_TEXT, ///< raw UTF-8 text
2436 //AV_CODEC_ID_XSUB,
2437 //AV_CODEC_ID_SSA,
2438 //AV_CODEC_ID_MOV_TEXT,
2439 //AV_CODEC_ID_HDMV_PGS_SUBTITLE,
2440 //AV_CODEC_ID_DVB_TELETEXT,
2441 //AV_CODEC_ID_SRT,
2442 //AV_CODEC_ID_MICRODVD,
2443 //AV_CODEC_ID_EIA_608,
2444 //AV_CODEC_ID_JACOSUB,
2445 //AV_CODEC_ID_SAMI,
2446 //AV_CODEC_ID_REALTEXT,
2447 //AV_CODEC_ID_STL,
2448 //AV_CODEC_ID_SUBVIEWER1,
2449 //AV_CODEC_ID_SUBVIEWER,
2450 //AV_CODEC_ID_SUBRIP,
2451 //AV_CODEC_ID_WEBVTT,
2452 //AV_CODEC_ID_MPL2,
2453 //AV_CODEC_ID_VPLAYER,
2454 //AV_CODEC_ID_PJS,
2455 //AV_CODEC_ID_ASS,
2456 //AV_CODEC_ID_HDMV_TEXT_SUBTITLE,
2457 //AV_CODEC_ID_TTML,
2458 //AV_CODEC_ID_ARIB_CAPTION,
2459
2460 return (codec_id != AV_CODEC_ID_DVD_SUBTITLE && codec_id != AV_CODEC_ID_DVB_SUBTITLE && codec_id != AV_CODEC_ID_HDMV_PGS_SUBTITLE);
2461}
2462
2463int get_audio_props(AVFormatContext *format_ctx, int *channels, int *samplerate)
2464{
2465 int ret;
2466
2467 ret = av_find_best_stream(format_ctx, AVMEDIA_TYPE_AUDIO, INVALID_STREAM, INVALID_STREAM, nullptr, 0);
2468 if (ret >= 0)
2469 {
2470#if LAVU_DEP_OLD_CHANNEL_LAYOUT
2471 *channels = format_ctx->streams[ret]->codecpar->ch_layout.nb_channels;
2472#else // !LAVU_DEP_OLD_CHANNEL_LAYOUT
2473 *channels = format_ctx->streams[ret]->codecpar->channels;
2474#endif // !LAVU_DEP_OLD_CHANNEL_LAYOUT
2475 *samplerate = format_ctx->streams[ret]->codecpar->sample_rate;
2476 }
2477
2478 return ret;
2479}
2480
2481const std::string & regex_escape(std::string * str)
2482{
2483 // Escape characters that are meaningful to regexp.
2484 // Note that "\\" must be first so we do not escape our own escapes...
2485 const std::vector<std::string> charlist {"\\", "+", "*", "?", "^", "$", "(", ")", "[", "]", "{", "}", "|"};
2486
2487 for (const std::string & ch : charlist)
2488 {
2489 replace_all(str, ch, "\\" + ch);
2490 }
2491
2492 replace_all(str, ".", "[.]");
2493
2494 return *str;
2495}
2496
2497bool is_selected(const std::string & ext)
2498{
2500 {
2501 // If set is empty, allow all extensions
2502 return true;
2503 }
2504
2505 auto is_match = [ext](const std::string & regex_string) { return (fnmatch(regex_string.c_str(), ext.c_str(), 0) == 0); };
2506
2507 return (find_if(begin(*params.m_include_extensions), end(*params.m_include_extensions), is_match) != end(*params.m_include_extensions));
2508}
2509
2510bool is_blocked(const std::string & filename)
2511{
2512 std::string ext;
2513
2514 if (!find_ext(&ext, filename))
2515 {
2516 return false; // no extension
2517 }
2518
2519 // These are blocked by default, they confuse players like VLC or mpv which
2520 // auto load them. As they get incorporated as subtitle tracks by FFmpegfs
2521 // they would end up as duplicates.
2522 if (!strcasecmp(ext, "srt") || !strcasecmp(ext, "vtt"))
2523 {
2524 return true;
2525 }
2526
2527 auto is_match = [ext](const std::string & regex_string) { return (fnmatch(regex_string.c_str(), ext.c_str(), 0) == 0); };
2528
2529 // Check block list
2530 return (find_if(begin(*params.m_hide_extensions), end(*params.m_hide_extensions), is_match) != end(*params.m_hide_extensions));
2531}
2532
2533void save_free(void **p)
2534{
2535 void * tmp = __atomic_exchange_n(p, nullptr, __ATOMIC_RELEASE);
2536 if (tmp != nullptr)
2537 {
2538 free(tmp);
2539 }
2540}
2541
2542void mssleep(int milliseconds)
2543{
2544 std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
2545}
2546
2547void ussleep(int microseconds)
2548{
2549 std::this_thread::sleep_for(std::chrono::microseconds(microseconds));
2550}
2551
2552void nssleep(int nanoseconds)
2553{
2554 std::this_thread::sleep_for(std::chrono::nanoseconds(nanoseconds));
2555}
const std::string & desttype() const
Get destination type.
bool is_audio_codec_supported(AVCodecID codec_id) const
Check if audio codec/file format combination is supported.
std::map< FILETYPE, const Format_Options > OPTIONS_MAP
Map of options. One entry per supported destination type.
const Format_Options * m_cur_opts
Currently selected options. Will never be nullptr.
AVCodecID subtitle_codec(AVCodecID codec_id) const
Get subtitle codec_id.
FFmpegfs_Format()
Construct FFmpegfs_Format object.
bool init(const std::string &desttype)
Get codecs for the selected destination type.
bool is_video_codec_supported(AVCodecID codec_id) const
Check if video codec/file format combination is supported.
bool albumart_supported() const
Check if album arts are supported.
bool is_hls() const
Check for HLS format.
std::string sample_fmt_list() const
Create a list of supported sample formats for current audio codec.
AVSampleFormat sample_format() const
Get sample format (bit width)
std::string m_desttype
Destination type: mp4, mp3 or other.
FILETYPE filetype() const
Get selected filetype.
bool is_frameset() const
Check for an export frame format.
FILETYPE m_filetype
File type, MP3, MP4, OPUS etc.
bool is_multiformat() const
Check if this is some sort of multi file format (any of the following: is_frameset() or is_hls()).
const Format_Options m_empty_options
Set of empty (invalid) options as default.
AVCodecID video_codec() const
Get video codec_id.
const std::string & fileext() const
Get file extension.
const std::string & format_name() const
Convert destination type to "real" type, i.e., the file extension to be used.
static const OPTIONS_MAP m_options_map
Map of options. One entry per supported destination type.
std::string video_codec_list() const
Create a list of supported audio codecs for current audio codec.
bool is_sample_fmt_supported() const
Check if audio codec/sample format combination is supported.
std::string audio_codec_list() const
Create a list of supported audio codecs for current audio codec.
AVCodecID audio_codec() const
Get audio codec_id.
bool check_ignore(size_t size, size_t offset)
For use with win_smb_fix=1: Check if this an illegal access offset by Windows.
void nssleep(int nanoseconds)
Sleep for specified time.
const char * get_codec_name(AVCodecID codec_id, bool long_name)
Safe way to get the codec name. Function never fails, will return "unknown" on error.
int get_encoding(const char *str, std::string &encoding)
Try to detect the encoding of str. This is relatively realiable, but may be wrong.
const std::string & remove_path(std::string *filepath)
Remove path from filename. Handy basename alternative.
std::string format_duration(int64_t value, uint32_t fracs)
Format a time in format HH:MM:SS.fract.
void exepath(std::string *path)
Path to FFmpegfs binary.
static int is_device(__attribute__((unused)) const AVClass *avclass)
Check if class is a FMmpeg device.
void ussleep(int microseconds)
Sleep for specified time.
int is_mount(const std::string &path)
Check if path is a mount.
std::string & trim(std::string &s)
trim from both ends
int mktree(const std::string &path, mode_t mode)
Make directory tree.
int nocasecompare(const std::string &lhs, const std::string &rhs)
nocasecompare to make std::string find operations case insensitive
void make_upper(std::string *input)
Convert string to upper case.
int read_file(const std::string &path, std::string &result)
Read text file and return in UTF-8 format, no matter in which encoding the input file is....
const std::string & append_filename(std::string *path, const std::string &filename)
Add filename to path, including / after the path if required.
const std::string & remove_filename(std::string *filepath)
Remove filename from path. Handy dirname alternative.
bool check_ext(const std::string &ext, const std::string &filename)
Check if filename has a certain extension. The check is case sensitive.
std::string ffmpeg_libinfo()
Get info about the FFmpeg libraries used.
std::string get_filetype_text(FILETYPE filetype)
Convert FILETYPE enum to human readable text.
bool is_blocked(const std::string &filename)
Check if filename should be hidden from output path.
std::map< const std::string, const FILETYPE, comp > FILETYPE_MAP
Map of file type. One entry per supported type.
std::string sanitise_filepath(std::string *filepath)
Sanitise file name. Calls realpath() to remove duplicate // or resolve ../.. etc. Changes the path in...
int reg_compare(const std::string &value, const std::string &pattern, std::regex::flag_type flag)
Compare value with pattern.
bool is_selected(const std::string &ext)
Find extension in include list, if existing.
int strcasecmp(const std::string &s1, const std::string &s2)
strcasecmp() equivalent for std::string.
void tempdir(std::string &path)
Get temporary directory.
bool is_album_art(AVCodecID codec_id, const AVRational *frame_rate)
Minimal check if codec is an album art. Requires frame_rate to decide whether this is a video stream ...
bool is_text_codec(AVCodecID codec_id)
Check if subtitle codec is a text or graphical codec.
const char * get_media_type_string(enum AVMediaType media_type)
av_get_media_type_string is missing, so we provide our own.
const std::string & regex_escape(std::string *str)
Escape characters that are meaningful to regexp.
std::string format_result_size(size_t size_resulting, size_t size_predicted)
Format size of transcoded file including difference between predicted and resulting size.
std::string & rtrim(std::string &s)
trim from end
const char * hwdevice_get_type_name(AVHWDeviceType dev_type)
int64_t ffmpeg_rescale_q_rnd(int64_t ts, const AVRational &timebase_in, const AVRational &timebase_out)
Convert a FFmpeg time from in timebase to out timebase with rounding.
int supports_albumart(FILETYPE filetype)
Check if file type supports album arts.
FILETYPE get_filetype(const std::string &desttype)
Get the FFmpegfs filetype, desttype must be one of FFmpeg's "official" short names for formats.
#define PRINT_LIB_INFO(libname, LIBNAME)
Print info about a FFmpeg library.
int show_caps(int device_only)
Lists all supported codecs and devices.
size_t get_disk_free(std::string &path)
Get free disk space.
#define X(name)
Convert macro to string.
std::string fourcc_make_string(std::string *buf, uint32_t fourcc)
static const FILETYPE_MAP filetype_map
std::shared_ptr< char[]> new_strdup(const std::string &str)
strdup() variant taking a std::string as input.
std::string format_time(time_t value)
Format a time in format "w d m s".
std::string format_samplerate(int value)
Format a samplerate.
std::string make_filename(uint32_t file_no, const std::string &fileext)
Make a file name from file number and file extension.
std::string format_bitrate(BITRATE value)
Format a bit rate.
std::string & ltrim(std::string &s)
trim from start
const std::string & replace_ext(std::string *filepath, const std::string &ext)
Replace extension in filename, taking into account that there might not be an extension already.
bool detect_docker()
Detect if we are running under Docker.
void make_lower(std::string *input)
Convert string to lower case.
const std::string & remove_sep(std::string *path)
Remove / from the path.
const std::string & remove_ext(std::string *filepath)
Remove extension from filename.
void save_free(void **p)
Savely free memory: Pointer will be set to nullptr before it is actually freed.
const std::string & append_ext(std::string *filepath, const std::string &ext)
Append extension to filename. If ext is the same as.
std::string format_result_size_ex(size_t size_resulting, size_t size_predicted)
Format size of transcoded file including difference between predicted and resulting size.
const std::string & append_sep(std::string *path)
Add / to the path if required.
const std::string & expand_path(std::string *tgt, const std::string &src)
Expand path, e.g., expand ~/ to home directory.
void mssleep(int milliseconds)
Sleep for specified time.
void append_basepath(std::string *origpath, const char *path)
Translate file names from FUSE to the original absolute path.
bool file_exists(const std::string &filename)
Check if file exists.
static void print_fps(double d, const char *postfix)
Print frames per second.
int print_stream_info(const AVStream *stream)
Print info about an AVStream.
std::string format_size(uint64_t value)
Format size.
void stat_set_size(struct stat *st, size_t size)
Properly fill in all size related members in stat struct.
std::vector< std::string > split(const std::string &input, const std::string &regex)
Split string into an array delimited by a regular expression.
void init_id3v1(ID3v1 *id3v1)
Initialise ID3v1 tag.
int get_audio_props(AVFormatContext *format_ctx, int *channels, int *samplerate)
Get first audio stream.
std::string replace_all(std::string str, const std::string &from, const std::string &to)
Same as std::string replace(), but replaces all occurrences.
std::string format_size_ex(uint64_t value)
Format size.
bool replace_start(std::string *str, const std::string &from, const std::string &to)
Replace start of string from "from" to "to".
bool find_ext(std::string *ext, const std::string &filename)
Find extension in filename, if existing.
int to_utf8(std::string &text, const std::string &encoding)
Convert almost any encoding to UTF-8. To get a list of all possible encodings run "iconv --list".
std::string format_number(int64_t value)
Format numeric value.
FILETYPE get_filetype_from_list(const std::string &desttypelist)
Get the FFmpegfs filetype, desttypelist must be a comma separated list of FFmpeg's "official" short n...
int64_t ffmpeg_rescale_q(int64_t ts, const AVRational &timebase_in, const AVRational &timebase_out)
Convert a FFmpeg time from in timebase to outtime base.
std::string ffmpeg_geterror(int errnum)
Get FFmpeg error string for errnum. Internally calls av_strerror().
Various FFmpegfs utility functions.
#define BITRATE
For FFmpeg bit rate is an int.
#define AV_ROUND_PASS_MINMAX
bool is_text_codec(AVCodecID codec_id)
Check if subtitle codec is a text or graphical codec.
ENCODING
@ UTF8_BOM
UTF-8 with bottom mark.
@ UTF32BE_BOM
UTF-16 big-endian with bottom mark.
@ UTF16BE_BOM
UTF-16 big-endian with bottom mark.
@ UTF32LE_BOM
UTF-16 little-endian with bottom mark.
@ ASCII
Some sort of ASCII encoding.
@ UTF16LE_BOM
UTF-16 little-endian with bottom mark.
FILETYPE get_filetype(const std::string &desttype)
Get the FFmpegfs filetype, desttype must be one of FFmpeg's "official" short names for formats.
std::map< conststd::string, constT, comp >::const_iterator search_by_value(const std::map< const std::string, const T, comp > &mapOfWords, T value)
Iterate through all elements in map and search for the passed element.
const std::string & strsprintf(std::string *str, const std::string &format, Args ... args)
Format a std::string sprintf-like.
@ FMT_F16
16 bit floating point
@ FMT_F32
32 bit floating point
@ FMT_F24
24 bit floating point
@ FMT_8
8 bit integer
@ FMT_32
32 bit integer
@ FMT_16
16 bit integer
@ FMT_DONTCARE
Don't care, leave to FFmpegfs to choose.
@ FMT_F64
64 bit floating point
@ FMT_24
24 bit integer
@ FMT_64
64 bit integer
FILETYPE
#define INVALID_STREAM
Denote an invalid stream.
FFMPEGFS_PARAMS params
FFmpegfs command line parameters.
Definition ffmpegfs.cc:81
std::string get_sampleformat_text(SAMPLE_FMT sample_fmt)
Convert SAMPLE_FMT enum to human readable text.
Definition ffmpegfs.cc:872
std::string get_video_codec_text(AVCodecID video_codec)
Convert AVCodecID enum for video codec to human readable text.
Definition ffmpegfs.cc:1257
std::string get_audio_codec_text(AVCodecID audio_codec)
Convert AVCodecID enum for audio codec to human readable text.
Definition ffmpegfs.cc:1247
Main include for FFmpegfs project.
ID3v1 tag structure
SAMPLE_FMT m_sample_fmt
Sample format.
Definition ffmpegfs.h:206
AVCodecID m_video_codec
Either AV_CODEC_ID_NONE for default, or a user selected codec.
Definition ffmpegfs.h:197
std::unique_ptr< MATCHVEC > m_hide_extensions
Set of extensions to block/hide. Must be a pointer as the fuse API cannot handle advanced c++ objects...
Definition ffmpegfs.h:256
AVCodecID m_audio_codec
Either AV_CODEC_ID_NONE for default, or a user selected codec.
Definition ffmpegfs.h:196
std::unique_ptr< MATCHVEC > m_include_extensions
Set of extensions to include. If empty, include all. Must be a pointer as the fuse API cannot handle ...
Definition ffmpegfs.h:255
std::string m_basepath
Base path: Files from this directory (including all sub directories) will be mapped to m_mountpath.
Definition ffmpegfs.h:192
std::string audio_codec_list() const
Create a list of supported audio codecs for current audio codec.
AVCodecID video_codec() const
Get video codec_id.
AVCodecID subtitle_codec(AVCodecID codec_id) const
Get subtitle codec_id.
std::string video_codec_list() const
Create a list of supported audio codecs for current audio codec.
AVSampleFormat sample_format() const
Get sample format (bit width)
bool m_albumart_supported
true if album arts are supported (eg. mp3) or false if not (e.g. wav, aiff)
std::string m_format_name
Descriptive name of the format. Descriptive name of the format, e.g. "opus", "mpegts"....
bool is_sample_fmt_supported() const
Check if audio codec/sample format combination is supported.
Format_Options()
Construct Format_Options object with defaults (empty)
bool is_audio_codec_supported(AVCodecID codec_id) const
Check if audio codec/file format combination is supported.
std::map< SAMPLE_FMT, const FORMAT > FORMAT_MAP
Map of formats. One entry per format derivative.
std::string m_fileext
File extension: mp4, mp3, flac or other. Mostly, but not always, same as m_format_name.
FORMAT_MAP m_format_map
Format definition (audio/videocodec, sample format)
bool is_video_codec_supported(AVCodecID codec_id) const
Check if video codec/file format combination is supported.
AVCodecID audio_codec() const
Get audio codec_id.
std::string sample_fmt_list() const
Create a list of supported sample formats for current audio codec.
ID3 version 1 tag
Definition id3v1tag.h:42
char m_title_no
Title number.
Definition id3v1tag.h:50
char m_padding
Padding byte, must be '\0'.
Definition id3v1tag.h:49
std::array< char, 3 > m_tag
Contains "TAG".
Definition id3v1tag.h:43
char m_genre
Type of music.
Definition id3v1tag.h:51