/*!
* 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 2010
*/
// minivideo headers
#include "import.h"
#include "minitraces.h"
#include "minivideo_typedef.h"
#include "minivideo_containers.h"
// minivideo headers
#include "minivideo_mediafile.h"
// C standard libraries
#include
#include
#include
#include
// C POSIX library
#ifdef _MSC_VER
#include
#define getcwd _getcwd
#else
#include
#endif
/* ************************************************************************** */
/*!
* \brief Get various from a media filepath.
* \param[in] *media: A pointer to a MediaFile_t structure, containing every informations available about the current media file.
*
* Get absolute file path, file directory, file name and extension.
* This function will only work with Unix-style file systems.
*/
static void getInfosFromPath(MediaFile_t *media)
{
TRACE_2(IO, "getInfosFromPath()");
// Check if mediaFile->filepath is an absolute path
char *pos_first_slash_p = strchr(media->file_path, '/');
if ((pos_first_slash_p != NULL) && ((pos_first_slash_p - media->file_path) == 0))
{
TRACE_2(IO, "* mediaFile->file_path seems to be an absolute path already (first caracter is /)");
}
else
{
char cwd[4096];
char absolute_filepath[4096];
FILE *temp = NULL;
// First attempt
if (getcwd(cwd, sizeof(cwd)) != NULL)
{
strncpy(absolute_filepath, strncat(cwd, media->file_path, sizeof(cwd) - 1), sizeof(absolute_filepath) - 1);
temp = fopen(absolute_filepath, "rb");
}
if (temp != NULL)
{
TRACE_2(IO, "* New absolute file path found, new using method 1: '%s'", absolute_filepath);
strncpy(media->file_path, absolute_filepath, sizeof(media->file_path) - 1);
fclose(temp);
}
else
{
// Second attempt
if (getcwd(cwd, sizeof(cwd)) != NULL)
{
strncpy(absolute_filepath, strncat(cwd, "/", 1), sizeof(absolute_filepath) - 1);
strncat(absolute_filepath, media->file_path, sizeof(absolute_filepath) - 1);
temp = fopen(absolute_filepath, "rb");
}
if (temp != NULL)
{
TRACE_2(IO, "* New absolute file path found, new using method 2");
strncpy(media->file_path, absolute_filepath, sizeof(media->file_path) - 1);
fclose(temp);
}
else
{
TRACE_2(IO, "* mediaFile->file_path seems to be an absolute path already");
}
}
}
// Get directory
char *pos_last_slash_p = strrchr(media->file_path, '/');
if (pos_last_slash_p != NULL)
{
unsigned int pos_last_slash_i = pos_last_slash_p - media->file_path + 1;
if (pos_last_slash_i > sizeof(media->file_directory) - 1)
{
pos_last_slash_i = sizeof(media->file_directory) - 1;
}
// Set directory
strncpy(media->file_directory, media->file_path, pos_last_slash_i);
// Get file name
char *pos_last_dot_p = strrchr(media->file_path, '.');
if (pos_last_dot_p != NULL)
{
unsigned int pos_last_dot_i = pos_last_dot_p - media->file_path - pos_last_slash_i;
if (pos_last_dot_i > sizeof(media->file_name) - 1)
{
pos_last_dot_i = sizeof(media->file_name) - 1;
}
// Set file name
strncpy(media->file_name, pos_last_slash_p + 1, pos_last_dot_i);
// Set file extension (without the dot)
strncpy(media->file_extension, pos_last_dot_p + 1, sizeof(media->file_extension) - 1);
}
else
{
TRACE_WARNING(IO, "* Cannot find file extension!");
// Set file name (without the extension)
strncpy(media->file_name, pos_last_slash_p + 1, 254);
}
}
else
{
TRACE_WARNING(IO, "* Cannot find file directory, name and extension!");
}
// Print results
TRACE_1(IO, "* File path : '%s'", media->file_path);
TRACE_1(IO, "* File directory : '%s'", media->file_directory);
TRACE_1(IO, "* File name : '%s'", media->file_name);
TRACE_1(IO, "* File extension : '%s'", media->file_extension);
}
/* ************************************************************************** */
/*!
* \brief Get media file size.
* \param[in] *media: A pointer to a MediaFile_t structure containing various informations about the file.
*/
static void getSize(MediaFile_t *media)
{
TRACE_2(IO, "getSize()");
fseek(media->file_pointer, 0, SEEK_END);
media->file_size = (int64_t)ftell(media->file_pointer);
rewind(media->file_pointer);
if (media->file_size < 1024) // < 1 KiB
{
TRACE_1(IO, "* File size : %i bytes", media->file_size);
}
else if (media->file_size < 1048576) // < 1 MiB
{
TRACE_1(IO, "* File size : %.2f KiB (%.2f KB)", (double)media->file_size / 1024.0, (double)media->file_size / 1000.0);
}
else // >= 1 MiB
{
TRACE_1(IO, "* File size : %.2f MiB (%.2f MB)", (double)media->file_size / 1024.0 / 1024.0, (double)media->file_size / 1000.0 / 1000.0);
}
}
/* ************************************************************************** */
/*!
* \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.
*/
static void getContainer(MediaFile_t *media)
{
media->container = CONTAINER_UNKNOWN;
// Detect container format using start codes, by readind the first bytes of the file
rewind(media->file_pointer);
uint8_t buffer[16];
if (fread(buffer, sizeof(uint8_t), sizeof(buffer), media->file_pointer) == sizeof(buffer))
{
media->container = getContainerUsingStartcodes(buffer);
}
if (media->container == CONTAINER_UNKNOWN)
{
TRACE_WARNING(IO, "* Unknown container format: startcodes detection failed...");
// Fallback: detect container format using file extension
std::string ext = media->file_extension;
media->container = getContainerUsingExtension(ext);
if (media->container == CONTAINER_UNKNOWN)
{
TRACE_ERROR(IO, "* Unknown container format: file extension detection failed...");
}
}
}
/* ************************************************************************** */
/* ************************************************************************** */
/*!
* \brief Open a file and check what's inside it.
* \param[in] *filepath: The path of the file to load.
* \param[in,out] **media_ptr: A pointer to a MediaFile_t structure, containing every informations available about the current media file.
*
* Some more informations about supported files:
* Size and offset are coded on int64_t (signed long long), so this library should
* be able to handle file of 1073741824 GiB if Large File Support is enabled during
* compilation, 2 GiB otherwise.
* The filename is limited to 255 caracters (including file extension) and the
* complete filepath is limited to 4096 caracters.
*
* These parameters will only work on POSIX compliant operating system.
*/
int import_fileOpen(const char *filepath, MediaFile_t **media_ptr)
{
TRACE_INFO(IO, BLD_GREEN "import_fileOpen()" CLR_RESET);
int retcode = FAILURE;
if (filepath == NULL)
{
TRACE_ERROR(IO, "* File path is invalid");
}
else
{
// Allocate media structure and create a shortcut
*media_ptr = (MediaFile_t*)calloc(1, sizeof(MediaFile_t));
if (*media_ptr == NULL)
{
TRACE_ERROR(IO, "* Unable to allocate a MediaFile_t structure!");
}
else
{
MediaFile_t *media = (*media_ptr);
// Set filepath in MediaFile_t
strncpy(media->file_path, filepath, sizeof(media->file_path) - 1);
TRACE_INFO(IO, "* File path (raw): '%s'", filepath);
// Open file, read only
media->file_pointer = fopen(filepath, "rb");
if (media->file_pointer == NULL)
{
TRACE_ERROR(IO, "Unable to open the media file: '%s'!", filepath);
free(*media_ptr);
*media_ptr = NULL;
}
else
{
TRACE_1(IO, "* File successfully opened");
// Extract some informations from the media file
getInfosFromPath(media);
getSize(media);
getContainer(media);
retcode = SUCCESS;
}
}
}
return retcode;
}
/* ************************************************************************** */
/*!
* \brief Close a file.
* \param[in,out] **media_ptr: A pointer of pointer to a MediaFile_t structure.
* \return 1 if success, 0 otherwise.
*/
int import_fileClose(MediaFile_t **media_ptr)
{
TRACE_INFO(IO, BLD_GREEN "import_fileClose()" CLR_RESET);
int retcode = SUCCESS;
int i = 0;
if ((*media_ptr) != NULL)
{
if ((*media_ptr)->file_pointer != NULL)
{
if (fclose((*media_ptr)->file_pointer) == 0)
{
TRACE_1(IO, "* File successfully closed");
retcode = SUCCESS;
}
else
{
TRACE_ERROR(IO, "Unable to close that file!");
retcode = FAILURE;
}
}
{
free(*media_ptr);
*media_ptr = NULL;
TRACE_1(IO, ">> MediaFile freed");
}
}
return retcode;
}
/* ************************************************************************** */
/*!
* \brief Print various informations about a file.
* \param[in] *media: A pointer to a MediaFile_t structure, containing every informations available about the current media file.
*/
void import_fileStatus(MediaFile_t *media)
{
TRACE_INFO(IO, BLD_GREEN "import_fileStatus()" CLR_RESET);
unsigned i = 0;
// File
if (media->file_pointer)
{
TRACE_1(IO, "file_pointer is " BLD_GREEN "open" CLR_RESET);
}
else
{
TRACE_1(IO, "file_pointer is " BLD_RED "closed" CLR_RESET);
}
// File info
TRACE_1(IO, "* File path : '%s'", media->file_path);
TRACE_1(IO, "* File directory : '%s'", media->file_directory);
TRACE_1(IO, "* File name : '%s'", media->file_name);
TRACE_1(IO, "* File extension : '%s'", media->file_extension);
TRACE_1(IO, "* File size : %i MiB / %i MB",
media->file_size / 1024 / 1024,
media->file_size / 1000 / 1000);
// File format
TRACE_1(IO, "* File container : '%s'", getContainerString(media->container, 1));
}
/* ************************************************************************** */