/*!
* COPYRIGHT (C) 2020 Emeric Grange - All Rights Reserved
*
* This file is part of MiniVideo.
*
* MiniVideo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* MiniVideo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with MiniVideo. If not, see .
*
* \author Emeric Grange
* \date 2018
*/
#include "minivideo_containers.h"
#include "minitraces.h"
#ifdef __GNUC__
#pragma GCC diagnostic ignored "-Wswitch"
#endif
#ifdef __clang__
#pragma clang diagnostic ignored "-Wswitch"
#endif
/* ************************************************************************** */
const char *getContainerString(const Containers_e container, const bool long_description)
{
switch (container)
{
case CONTAINER_AVI:
if (long_description)
return "AVI (Audio Video Interleave)";
else
return "AVI";
case CONTAINER_ASF:
if (long_description)
return "ASF (Advanced Systems Format)";
else
return "ASF";
case CONTAINER_MKV:
if (long_description)
return "MKV (Matroska)";
else
return "MKV";
case CONTAINER_MP4:
if (long_description)
return "MP4 (ISO Base Media format)";
else
return "MP4";
case CONTAINER_MPEG_PS:
if (long_description)
return "MPEG 'Program Stream'";
else
return "MPEG-PS";
case CONTAINER_MPEG_TS:
if (long_description)
return "MPEG 'Transport Stream'";
else
return "MPEG-TS";
case CONTAINER_MPEG_MT:
if (long_description)
return "MPEG 'Media Transport'";
else
return "MPEG-MT";
case CONTAINER_MXF:
if (long_description)
return "MXF Material eXchange Format";
else
return "MXF";
case CONTAINER_FLV:
if (long_description)
return "Flash Video file format";
else
return "FLV";
case CONTAINER_SWF:
if (long_description)
return "SWF (Small Web Format)";
else
return "SWF";
case CONTAINER_OGG:
return "OGG";
case CONTAINER_RM:
return "RealMedia";
case CONTAINER_R3D:
return "Redcode RAW";
case CONTAINER_FLAC:
if (long_description)
return "FLAC (Free Lossless Audio Codec)";
else
return "FLAC";
case CONTAINER_WAVE:
if (long_description)
return "WAVE (Waveform Audio File Format)";
else
return "WAVE";
case CONTAINER_CAF:
if (long_description)
return "CAF (Core Audio Format)";
else
return "CAF";
case CONTAINER_AU:
if (long_description)
return "AU (Au file format)";
else
return "AU";
case CONTAINER_ES:
return "Undefined 'Elementary Stream'";
case CONTAINER_ES_AAC:
return "AAC 'Elementary Stream'";
case CONTAINER_ES_AC3:
return "AC3 'Elementary Stream'";
case CONTAINER_ES_MP3:
return "MP3 'Elementary Stream'";
case CONTAINER_JPEG:
return "JPEG";
case CONTAINER_JPEG_XR:
return "JPEG XR";
case CONTAINER_JPEG_2K:
return "JPEG 2000";
case CONTAINER_GIF:
return "GIF";
case CONTAINER_BMP:
return "BMP";
case CONTAINER_TGA:
return "TGA";
case CONTAINER_TIFF:
return "TIFF";
case CONTAINER_ICNS:
return "ICNS";
case CONTAINER_ICO:
return "ICO";
case CONTAINER_PNG:
return "PNG";
case CONTAINER_DNG:
return "DNG";
case CONTAINER_DPX:
return "DPX";
case CONTAINER_CIFF:
return "CIFF";
case CONTAINER_CR2:
return "CR2";
case CONTAINER_WEBP:
return "WebP";
case CONTAINER_BPG:
return "BPG";
case CONTAINER_FLIF:
return "FLIF";
default:
return "";
}
}
/* ************************************************************************** */
const char *getContainerProfileString(const ContainerProfiles_e profile, const bool long_description)
{
switch (profile)
{
case PROF_AVI:
return "AVI v1";
case PROF_AVI_OpenDML:
return "AVI v2 \"OpenDML\"";
case PROF_AVI_DMF:
return "AVI DMF \"DivX Media Format\"";
case PROF_MPEG_PS_1:
return "MPEG-PS v1";
case PROF_MPEG_PS_2:
return "MPEG-PS v2";
case PROF_ISOBMF_MOV:
return "QuickTime File Format";
case PROF_ISOBMF_MP4:
return "ISO BMF MP4";
case PROF_ISOBMF_M4A:
return "QuickTime Audio, AAC Codec";
case PROF_ISOBMF_F4V:
return "Flash Video";
case PROF_ISOBMF_3GP:
return "ISO BMF 3GP";
case PROF_ISOBMF_DASH:
return "MPEG-DASH";
case PROF_ISOBMF_AVIF:
return "AV1 Image File Format";
case PROF_ISOBMF_HEIF:
return "High Efficiency Image File Format";
case PROF_ISOBMF_MJP2:
return "ISO BMF Motion Jpeg 2000";
case PROF_ISOBMF_PIFF:
return "Protected Interoperable File Format";
case PROF_MKV_MATROSKA:
return "Matroska";
case PROF_MKV_WEBM:
return "WebM";
case PROF_WAVE_AMB:
return "WAVE ambisonic";
case PROF_WAVE_RF64:
return "RF64";
case PROF_WAVE_BWFv1:
return "Broadcast Wave Format v1";
case PROF_WAVE_BWFv2:
return "Broadcast Wave Format v2";
case PROF_WAVE_BWF64:
return "Broadcast Wave Format 64";
default:
return "";
}
}
/* ************************************************************************** */
/* ************************************************************************** */
/*!
* \brief Detect the container used by a multimedia file.
* \param[in] *media: A pointer to a MediaFile_t structure, containing every informations available about the current media file.
* \return container: A ContainerFormat_e value.
*
* This function check the first 16 bytes of a media file in order to find
* evidence of a known file container format.
*/
Containers_e getContainerUsingStartcodes(uint8_t buffer[16])
{
TRACE_2(IO, "getContainerUsingStartcodes()");
Containers_e container = CONTAINER_UNKNOWN;
// Read the first bytes of the file
if (buffer)
{
// Parse the file to find evidence of a container format
if (buffer[0] == 0x47)
{
TRACE_1(IO, "* File type : TS (MPEG 'Transport Stream') container detected");
container = CONTAINER_MPEG_TS;
}
else if (buffer[0] == 0x1A && buffer[1] == 0x45 && buffer[2] == 0xDF && buffer[3] == 0xA3)
{
TRACE_1(IO, "* File type : EBML file detected, possibly MKV or WebM container");
container = CONTAINER_MKV;
}
else if (buffer[0] == 0x52 && buffer[1] == 0x49 && buffer[2] == 0x46 && buffer[3] == 0x46)
{
// RIFF header:
// 52 49 46 46 xx xx xx xx // R I F F (size)
// then:
// 41 56 49 20 4C 49 53 54 // A V I L I S T
// 57 41 56 45 66 6D 74 20 // W A V E f m t
if (buffer[8] == 0x41 && buffer[9] == 0x56 && buffer[10] == 0x49 && buffer[11] == 0x20)
{
TRACE_1(IO, "* File type : AVI container detected");
container = CONTAINER_AVI;
}
else if (buffer[8] == 0x57 && buffer[9] == 0x41 && buffer[10] == 0x56 && buffer[11] == 0x45)
{
TRACE_1(IO, "* File type : WAVE container detected");
container = CONTAINER_WAVE;
}
else
{
TRACE_WARNING(IO, "* File type : Unknown RIFF container detected");
}
}
else if (buffer[0] == 0x00 && buffer[1] == 0x00)
{
if (buffer[2] == 0x01)
{
if (buffer[3] == 0xBA)
{
TRACE_1(IO, "* File type : PS (MPEG 'Program Stream') container detected");
container = CONTAINER_MPEG_PS;
}
else if (buffer[3] == 0xB3)
{
TRACE_1(IO, "* File type : MPEG-1/2 / H.262 Elementary Stream detected (raw video data)");
container = CONTAINER_ES;
//media->codec_video = CODEC_MPEG12;
}
else if (buffer[3] == 0x67)
{
TRACE_1(IO, "* File type : H.264 'Annex B' Elementary Stream detected (raw video data)");
container = CONTAINER_ES;
//media->codec_video = CODEC_H264;
}
}
else if (buffer[2] == 0x00 && buffer[3] == 0x01)
{
if (buffer[4] == 0xBA)
{
TRACE_1(IO, "* File type : MPEG-PS (MPEG 'Program Stream') container detected");
container = CONTAINER_MPEG_PS;
}
else if (buffer[4] == 0xB3)
{
TRACE_1(IO, "* File type : MPEG-1/2 / H.262 Elementary Stream detected (raw video data)");
container = CONTAINER_ES;
//media->codec_video = CODEC_MPEG12;
}
else if (buffer[4] == 0x67)
{
TRACE_1(IO, "* File type : H.264 'Annex B' Elementary Stream detected (raw video data)");
container = CONTAINER_ES;
//media->codec_video = CODEC_H264;
}
}
if (buffer[4] == 0x66 && buffer[5] == 0x74 && buffer[6] == 0x79 && buffer[7] == 0x70)
{
// MP4: 00 00 xx xx 66 74 79 70 // (size) f t y p
TRACE_1(IO, "* File type : ISO BMF (MOV,MP4, ...) container detected");
container = CONTAINER_MP4;
}
else if (buffer[4] == 0x73 && buffer[5] == 0x74 && buffer[6] == 0x79 && buffer[7] == 0x70)
{
// MP4: 00 00 xx xx 73 74 79 70 // (size) s t y p
TRACE_1(IO, "* File type : ISO BMF (MOV,MP4, ...) container detected");
container = CONTAINER_MP4;
}
}
else if (buffer[0] == 0x30 && buffer[1] == 0x26 && buffer[2] == 0xB2 && buffer[3] == 0x75)
{
TRACE_1(IO, "* File type : ASF container detected");
container = CONTAINER_ASF;
}
else if (buffer[0] == 0x4F && buffer[1] == 0x67 && buffer[2] == 0x67 && buffer[3] == 0x53)
{
TRACE_1(IO, "* File type : OGG container detected");
container = CONTAINER_OGG;
}
else if (buffer[0] == 0x66 && buffer[1] == 0x61 && buffer[2] == 0x4C && buffer[3] == 0x43)
{
TRACE_1(IO, "* File type : FLAC container detected");
container = CONTAINER_FLAC;
}
else if (buffer[0] == 0x06 && buffer[1] == 0x0E && buffer[2] == 0x2B && buffer[3] == 0x34)
{
TRACE_1(IO, "* File type : MXF container detected");
container = CONTAINER_MXF;
}
else if (buffer[0] == 0x46 && buffer[1] == 0x4C && buffer[2] == 0x56 && buffer[3] == 0x01)
{
TRACE_1(IO, "* File type : FLV container detected");
container = CONTAINER_FLV;
}
else if (buffer[0] == 0x63 && buffer[1] == 0x61 && buffer[2] == 0x66 && buffer[3] == 0x66)
{
TRACE_1(IO, "* File type : CAF container detected");
container = CONTAINER_CAF;
}
else if (buffer[0] == 0x2E && buffer[1] == 0x73 && buffer[2] == 0x6E && buffer[3] == 0x64)
{
// AU magic number: 2E 73 6E 64 // . s n d
TRACE_1(IO, "* File type : AU container detected");
container = CONTAINER_AU;
}
else if (buffer[0] == 0xFF &&
(buffer[1] == 0xFE || buffer[1] == 0xFD || buffer[1] == 0xFB))
{
TRACE_1(IO, "* File type : MP1/2/3 Elementary Stream detected");
container = CONTAINER_ES_MP3;
}
}
else
{
TRACE_ERROR(IO, "Container: no file header data provided...");
}
return container;
}
/*!
* \brief Detect the container used by a multimedia file.
* \param[in] *media: A pointer to a MediaFile_t structure, containing every informations available about the current media file.
* \return container: A ContainerFormat_e value.
*
* This function check the file extension to guess the container. As this method
* is *definitely* not reliable (file extensions are set by users, and users
* make mistakes) this code is here mostly for fun. And to list extensions for
* whoever is interested.
*/
Containers_e getContainerUsingExtension(const std::string &ext)
{
TRACE_2(IO, "getContainerUsingExtension()");
// Set container to unknown
Containers_e container = CONTAINER_UNKNOWN;
if (ext.size() > 0 && ext.size() < 8)
{
if (ext == "mov" || ext == "qt" || // QuickTime file format
ext == "mp4" || // ISO Base Media file format
ext == "m4v" || ext == "m4a" || ext == "m4p" || ext == "m4b" ||
ext == "m4s" || ext == "mp4v" || ext == "mp4a" ||
ext == "3gp" || ext == "3g2" || ext == "3gpp" || // ISO BMF / 3GP profile
ext == "f4v" || ext == "f4p" || ext == "f4a" || ext == "f4b" || // ISO BMF / Flash Video
ext == "mj2" || ext == "mjp2") // ISO BMF / Motion JPEG2000 profile
{
container = CONTAINER_MP4;
}
else if (ext == "webm" ||
ext == "mkv" || ext == "mka" || ext == "mks" || ext == "mk3d")
{
container = CONTAINER_MKV;
}
else if (ext == "avi" || ext == "divx")
{
container = CONTAINER_AVI;
}
else if (ext == "asf" || ext == "wma" || ext == "wmv")
{
container = CONTAINER_ASF;
}
else if (ext == "ps" || ext == "vob" || ext == "evo" ||
ext == "m2p" || ext == "m2v" || ext == "mpg" || ext == "mpeg")
{
container = CONTAINER_MPEG_PS;
}
else if (ext == "ts" || ext == "trp" ||
ext == "mts" || ext == "m2ts")
{
container = CONTAINER_MPEG_TS;
}
else if (ext == "mt" || ext == "mmt")
{
container = CONTAINER_MPEG_MT;
}
else if (ext == "ogg" || ext == "ogv" || ext == "oga" ||
ext == "ogx" || ext == "ogm" || ext == "spx" ||
ext == "opus")
{
container = CONTAINER_OGG;
}
else if (ext == "mxf")
{
container = CONTAINER_MXF;
}
else if (ext == "flv" || ext == "f4v" || ext == "f4p" || ext == "f4a" || ext == "f4b")
{
container = CONTAINER_FLV;
}
else if (ext == "swf")
{
container = CONTAINER_SWF;
}
else if (ext == "rm" || ext == "rmvb")
{
container = CONTAINER_RM;
}
else if (ext == "r3d")
{
container = CONTAINER_R3D;
}
else if (ext == "flac")
{
container = CONTAINER_FLAC;
}
else if (ext == "wav" || ext == "wave" || ext == "amb")
{
container = CONTAINER_WAVE;
}
else if (ext == "aiff" || ext == "aif" || ext == "aifc")
{
container = CONTAINER_AIFF;
}
else if (ext == "caf")
{
container = CONTAINER_CAF;
}
else if (ext == "au" || ext == "snd")
{
container = CONTAINER_AU;
}
else if (ext == "264" || ext == "h264")
{
container = CONTAINER_ES;
//codec = CODEC_H264;
}
else if (ext == "265" || ext == "h265")
{
container = CONTAINER_ES;
//codec = CODEC_H265;
}
else if (ext == "aac")
{
container = CONTAINER_ES_AAC;
//codec = CODEC_AAC;
}
else if (ext == "ac3")
{
container = CONTAINER_ES_AC3;
//codec = CODEC_AC3;
}
else if (ext == "mp1" || ext == "mp2" || ext == "mp3")
{
container = CONTAINER_ES_MP3;
//codec = CODEC_MP3;
}
else if (ext == "jpg" || ext == "jpeg" || ext == "jpe" ||
ext == "jif" || ext == "jfif" || ext == "jfi")
{
container = CONTAINER_JPEG;
//codec = CODEC_JPEG;
}
else if (ext == "jxr" || ext == "hdp" || ext == "wdp")
{
container = CONTAINER_JPEG_XR;
//codec = CODEC_JPEG_XR;
}
else if (ext == "jp2" || ext == "j2k" || ext == "jpf" ||
ext == "jpx" || ext == "jpm" || ext == "mj2")
{
container = CONTAINER_JPEG_2K;
//codec = CODEC_JPEG_2K;
}
else if (ext == "gif")
{
container = CONTAINER_GIF;
//codec = CODEC_GIF;
}
else if (ext == "bmp" || ext == "dib")
{
container = CONTAINER_BMP;
//codec = CODEC_BMP;
}
else if (ext == "tga" || ext == "icb" || ext == "vda" || ext == "vst")
{
container = CONTAINER_TGA;
//codec = CODEC_TGA;
}
else if (ext == "tif" || ext == "tiff")
{
container = CONTAINER_TIFF;
//codec = CODEC_TIFF;
}
else if (ext == "ico")
{
container = CONTAINER_ICO;
}
else if (ext == "icns")
{
container = CONTAINER_ICNS;
}
else if (ext == "png")
{
container = CONTAINER_PNG;
//codec = CODEC_PNG;
}
else if (ext == "dng")
{
container = CONTAINER_DNG;
//codec = CODEC_DNG;
}
else if (ext == "dpx")
{
container = CONTAINER_DPX;
}
else if (ext == "crw")
{
container = CONTAINER_CIFF;
}
else if (ext == "cr2")
{
container = CONTAINER_CR2;
}
else if (ext == "webp")
{
container = CONTAINER_WEBP;
//codec = CODEC_VP8;
}
else if (ext == "bpg")
{
container = CONTAINER_BPG;
//codec = CODEC_H265;
}
else if (ext == "flif")
{
container = CONTAINER_FLIF;
//codec = CODEC_FLIF16;
}
else if (ext == "heif" || ext == "heic" || ext == "avci")
{
container = CONTAINER_MP4;
//codec = CODEC_H265;
}
else if (ext == "avif")
{
container = CONTAINER_MP4;
//codec = CODEC_AV1;
}
TRACE_1(IO, "* File extension : '%s' detected", getContainerString(container));
}
else
{
TRACE_ERROR(IO, "Container: no extension provided...");
}
return container;
}
/* ************************************************************************** */