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