diff --git a/singe/frameFile.c b/singe/frameFile.c new file mode 100644 index 000000000..88db5318b --- /dev/null +++ b/singe/frameFile.c @@ -0,0 +1,296 @@ +/* + * + * Singe 2 + * Copyright (C) 2006-2020 Scott Duensing + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + + +#include "thirdparty/uthash.h" + +#include "util.h" +#include "videoPlayer.h" +#include "frameFile.h" + + +typedef struct FrameLineS { + int videoHandle; + int lastFramePlayed; + long frame; + char *filename; +} FrameLineT; + + +typedef struct FrameFileS { + int id; + int count; + char *videoPath; + FrameLineT *files; + UT_hash_handle hh; +} FrameFileT; + + +static FrameFileT *_frameFileHash = NULL; +static int _nextId = 0; + + +void _transferProperties(int oldHandle, int newHandle); + + +void _transferProperties(int oldHandle, int newHandle) { + int l = 0; + int r = 0; + + if ((oldHandle < 0) || (newHandle < 0)) { + // Invalid handle somewhere + return; + } + + // Transfer previous video's properties to this one + videoGetVolume(oldHandle, &l, &r); + videoSetVolume(newHandle, l, r); + if (videoIsPlaying(oldHandle)) { + videoPlay(newHandle); + } else { + videoPause(newHandle); + } + videoPause(oldHandle); +} + + +long frameFileGetFrame(int frameFileIndex, int videoHandle) { + int i = 0; + FrameFileT *f = NULL; + + // Get our framefile structure + HASH_FIND_INT(_frameFileHash, &frameFileIndex, f); + if (!f) utilDie("No framefile at index %d in frameFileSeek.", frameFileIndex); + + // Search through loaded video segments + for (i=0; icount; i++) { + if (f->files[i].videoHandle == videoHandle) { + // Frame is in this video + return videoGetFrame(videoHandle) + f->files[i].frame; + } + } + + // Didn't find it. + return -1; +} + + +int frameFileInit(void) { + return 0; // Nothing to do +} + + +int frameFileLoad(char *filename, char *indexPath, bool stretchVideo, SDL_Renderer *renderer) { + int result = 0; + int count = 0; + long frame = 0; + size_t bytes = 0; + char *audio = NULL; + char *data = NULL; + char *path = NULL; + char *offset = NULL; + char *frameLine = NULL; + char *space = NULL; + char *endptr = NULL; + char *temp = NULL; + FrameLineT *files = NULL; + FrameLineT *newFiles = NULL; + FrameFileT *frameFile = NULL; + + data = utilReadFile(filename, &bytes); + if (!data) utilDie("Unable to open framefile: %s", filename); + + // Get path where video files live + path = utilReadLine(data, bytes, &offset); + if (!path) utilDie("Cannot read video path from framefile!"); + utilFixPathSeparators(&path); + + // If it's not an absolute path, pre-pend the path to the framefile + if (path[0] != utilGetPathSeparator()) { + temp = path; + count = strlen(filename) - strlen(utilGetLastPathComponent(filename)); + path = malloc(sizeof(char) * (count + strlen(temp) + 1)); + memcpy(path, filename, count); + memcpy(&path[count], temp, strlen(temp)); + path[count + strlen(temp)] = 0; + free(temp); + utilFixPathSeparators(&path); + count = 0; + } + + // Read frame offsets and filenames - silently ignore bad lines + while ((frameLine = utilReadLine(data, bytes, &offset)) != NULL) { + // Find first space in this file. + space = strstr(frameLine, " "); + if (space) { + // Write a zero in there to make it two fields. + *space = 0; + // Is the first part an integer? + endptr = NULL; + frame = strtol(frameLine, &endptr, 10); + *space = 32; + if (endptr == space) { + // Got an integer. Point at filename. + space++; + while ((space[0] == 32) || (space[0] == 9)) { + space++; + } + // Copy frame number and filename into array + newFiles = realloc(files, sizeof(FrameLineT) * (count + 1)); + if (!newFiles) utilDie("Unable to allocate new framefile entry!"); + files = newFiles; + files[count].lastFramePlayed = 0; + files[count].frame = frame; + files[count].filename = utilCreateString("%s%s", path, space); + // Is this an old m2v/ogg pair? + if (strncmp(utilGetFileExtension(files[count].filename), "m2v", 3) == 0) { + // Open split video and audio files + audio = strdup(files[count].filename); + audio[strlen(audio) - 3] = 'o'; + audio[strlen(audio) - 2] = 'g'; + audio[strlen(audio) - 1] = 'g'; + files[count].videoHandle = videoLoadWithAudio(files[count].filename, audio, indexPath, stretchVideo, renderer); + free(audio); + audio = NULL; + } else { + // Open combined video/audio file + files[count].videoHandle = videoLoad(files[count].filename, indexPath, stretchVideo, renderer); + } + count++; + } + } + free(frameLine); + } + + // Allocate new framefile + frameFile = malloc(sizeof(FrameFileT)); + frameFile->id = _nextId; + frameFile->count = count; + frameFile->videoPath = path; + frameFile->files = files; + HASH_ADD_INT(_frameFileHash, id, frameFile); + + result = _nextId++; + + return result; +} + + +int frameFileSeek(int frameFileIndex, long seekFrame, int *videoHandle, int *actualFrame) { + int i = 0; + FrameFileT *f = NULL; + + // Get our framefile structure + HASH_FIND_INT(_frameFileHash, &frameFileIndex, f); + if (!f) utilDie("No framefile at index %d in frameFileSeek.", frameFileIndex); + + // Search through loaded video segments + for (i=0; icount; i++) { + if ((seekFrame >= f->files[i].frame) && (seekFrame <= (f->files[i].frame + videoGetFrameCount(f->files[i].videoHandle)))) { + // Frame is in this video + *actualFrame = (int)(seekFrame - f->files[i].frame); + f->files[i].lastFramePlayed = *actualFrame; + videoSeek(f->files[i].videoHandle, *actualFrame); + // Is this a different video from the previous one? + if (*videoHandle != f->files[i].videoHandle) { + // Yes + _transferProperties(*videoHandle, f->files[i].videoHandle); + *videoHandle = f->files[i].videoHandle; + } + return 0; + } + } + + // Didn't find it + *videoHandle = -1; + *actualFrame = -1; + return 1; +} + + +int frameFileQuit(void) { + FrameFileT *f = NULL; + FrameFileT *t = NULL; + + // Unload any remaining frameFiles + HASH_ITER(hh, _frameFileHash, f, t) { + frameFileUnload(f->id); + } + + return 0; +} + + +int frameFileUnload(int frameFileIndex) { + int i = 0; + FrameFileT *f = NULL; + + // Get our framefile structure + HASH_FIND_INT(_frameFileHash, &frameFileIndex, f); + if (!f) utilDie("No framefile at index %d in frameFileUnload.", frameFileIndex); + + // Unload videos + for (i=0; icount; i++) { + free(f->files[i].filename); + videoUnload(f->files[i].videoHandle); + } + + // Free memory + free(f->files); + free(f->videoPath); + + // Remove from hash + HASH_DEL(_frameFileHash, f); + free(f); + + return 0; +} + + +int frameFileUpdate(int frameFileIndex, int *videoHandle) { + int i = 0; + int frame = 0; + FrameFileT *f = NULL; + + // Get our framefile structure + HASH_FIND_INT(_frameFileHash, &frameFileIndex, f); + if (!f) utilDie("No framefile at index %d in frameFileUpdate.", frameFileIndex); + + // Did the current video loop? If so, move to next video. + for (i=0; icount; i++) { + if (f->files[i].videoHandle == *videoHandle) { + frame = videoGetFrame(*videoHandle); + if ((f->files[i].lastFramePlayed > frame) && (frame == 0)) { + // Switch video files + if (i == (f->count - 1)) { + i = 0; + } else { + i++; + } + _transferProperties(*videoHandle, f->files[i].videoHandle); + *videoHandle = f->files[i].videoHandle; + } + break; + } + } + + return 0; +} diff --git a/singe/frameFile.h b/singe/frameFile.h new file mode 100644 index 000000000..34c616b12 --- /dev/null +++ b/singe/frameFile.h @@ -0,0 +1,41 @@ +/* + * + * Singe 2 + * Copyright (C) 2006-2020 Scott Duensing + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + + +#ifndef FRAMEFILE_H +#define FRAMEFILE_H + + +#include + +#include "common.h" + + +long frameFileGetFrame(int frameFileIndex, int videoHandle); +int frameFileInit(void); +int frameFileLoad(char *filename, char *indexPath, bool stretchVideo, SDL_Renderer *renderer); +int frameFileSeek(int frameFileIndex, long seekFrame, int *videoHandle, int *actualFrame); +int frameFileQuit(void); +int frameFileUnload(int frameFileIndex); +int frameFileUpdate(int frameFileIndex, int *videoHandle); + + +#endif // FRAMEFILE_H diff --git a/singe/main.c b/singe/main.c index 858656e88..52f8225c6 100644 --- a/singe/main.c +++ b/singe/main.c @@ -32,6 +32,7 @@ #include "stddclmr.h" #include "common.h" #include "util.h" +#include "frameFile.h" #include "videoPlayer.h" #include "singe.h" #include "extensions.h" @@ -327,8 +328,20 @@ int main(int argc, char *argv[]) { x++; } free(temp); + // If we still don't have one, try a framefile + if (!_confVideoFile) { + _confVideoFile = utilCreateString("%s.txt", temp); + if (!utilFileExists(_confVideoFile)) { + free(_confVideoFile); + _confVideoFile = NULL; + } + } } if (!_confVideoFile) showUsage(argv[0], "Unable to locate video."); + // Is it a framefile? + if (strncmp(utilGetFileExtension(_confVideoFile), "txt", 3) == 0) { + _confIsFrameFile = true; + } // Do we need to generate a data directory name? if (_confDataDir) { @@ -486,6 +499,7 @@ int main(int argc, char *argv[]) { if (err != 0) utilDie("%s", Mix_GetError()); // Start our video playback system + if (frameFileInit()) utilDie("Unable to initialize framefile handler."); if (videoInit()) utilDie("Unable to initialize video player."); // Finish our setup @@ -495,6 +509,7 @@ int main(int argc, char *argv[]) { // Shutdown videoQuit(); + frameFileQuit(); Mix_CloseAudio(); SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); diff --git a/singe/singe.c b/singe/singe.c index 004708bee..fd13d901c 100644 --- a/singe/singe.c +++ b/singe/singe.c @@ -34,6 +34,7 @@ #include "thirdparty/manymouse/manymouse.h" #include "util.h" +#include "frameFile.h" #include "videoPlayer.h" #include "singe.h" #include "font.h" @@ -145,6 +146,7 @@ enum { char *_confVideoFile = NULL; char *_confScriptFile = NULL; char *_confDataDir = NULL; +bool _confIsFrameFile = false; bool _confStretchVideo = false; bool _confNoMouse = false; bool _confNoStats = false; @@ -175,6 +177,7 @@ static int _nextSoundId = 0; static int _nextFontId = 0; static int _effectsVolume = AUDIO_MAX_VOLUME; static int _keyboardMode = KEYBD_NORMAL; +static int _frameFileHandle = -1; static int _videoHandle = -1; static int _fontQuality = 1; static int _mouseMode = MOUSE_SINGLE; @@ -494,7 +497,11 @@ int apiDiscGetFrame(lua_State *L) { int frame = 0; if (!_discStopped) { - frame = videoGetFrame(_videoHandle); + if (_confIsFrameFile) { + frame = frameFileGetFrame(_frameFileHandle, _videoHandle); + } else { + frame = videoGetFrame(_videoHandle); + } } luaTrace(L, "discGetFrame", "%d", frame); @@ -534,6 +541,7 @@ int apiDiscPlay(lua_State *L) { int apiDiscSearch(lua_State *L) { int n = lua_gettop(L); int frame = 0; + int aFrame = 0; bool result = false; double d = 0; @@ -542,7 +550,11 @@ int apiDiscSearch(lua_State *L) { if (n == 1) { if (lua_isnumber(L, 1)) { d = lua_tonumber(L, 1); frame = (int)d; - videoSeek(_videoHandle, frame); + if (_confIsFrameFile) { + frameFileSeek(_frameFileHandle, frame, &_videoHandle, &aFrame); + } else { + videoSeek(_videoHandle, frame); + } videoPause(_videoHandle); _discStopped = false; result = true; @@ -578,6 +590,7 @@ int apiDiscSetFps(lua_State *L) { int apiDiscSkipBackward(lua_State *L) { int n = lua_gettop(L); int frame = 0; + int aFrame = 0; bool result = false; double d = 0; @@ -587,7 +600,11 @@ int apiDiscSkipBackward(lua_State *L) { if (n == 1) { if (lua_isnumber(L, 1)) { d = lua_tonumber(L, 1); frame = videoGetFrame(_videoHandle) - (int)d; - videoSeek(_videoHandle, frame); + if (_confIsFrameFile) { + frameFileSeek(_frameFileHandle, frame, &_videoHandle, &aFrame); + } else { + videoSeek(_videoHandle, frame); + } result = true; } } @@ -617,6 +634,7 @@ int apiDiscSkipBlanking(lua_State *L) { int apiDiscSkipForward(lua_State *L) { int n = lua_gettop(L); int frame = 0; + int aFrame = 0; bool result = false; double d = 0; @@ -626,7 +644,11 @@ int apiDiscSkipForward(lua_State *L) { if (n == 1) { if (lua_isnumber(L, 1)) { d = lua_tonumber(L, 1); frame = videoGetFrame(_videoHandle) + (int)d; - videoSeek(_videoHandle, frame); + if (_confIsFrameFile) { + frameFileSeek(_frameFileHandle, frame, &_videoHandle, &aFrame); + } else { + videoSeek(_videoHandle, frame); + } result = true; } } @@ -648,6 +670,7 @@ int apiDiscSkipForward(lua_State *L) { int apiDiscSkipToFrame(lua_State *L) { int n = lua_gettop(L); int frame = 0; + int aFrame = 0; bool result = false; double d = 0; @@ -656,7 +679,11 @@ int apiDiscSkipToFrame(lua_State *L) { if (n == 1) { if (lua_isnumber(L, 1)) { d = lua_tonumber(L, 1); frame = (int)d; - videoSeek(_videoHandle, frame); + if (_confIsFrameFile) { + frameFileSeek(_frameFileHandle, frame, &_videoHandle, &aFrame); + } else { + videoSeek(_videoHandle, frame); + } videoPlay(_videoHandle); _discStopped = false; result = true; @@ -675,13 +702,18 @@ int apiDiscSkipToFrame(lua_State *L) { int apiDiscStepBackward(lua_State *L) { int frame = 0; + int aFrame = 0; (void)L; // No matter disc state, go back a frame. If playing, pause. frame = videoGetFrame(_videoHandle) - 1; - videoSeek(_videoHandle, frame); + if (_confIsFrameFile) { + frameFileSeek(_frameFileHandle, frame, &_videoHandle, &aFrame); + } else { + videoSeek(_videoHandle, frame); + } videoPause(_videoHandle); luaTrace(L, "discStepBackward", "%d", frame); @@ -691,13 +723,18 @@ int apiDiscStepBackward(lua_State *L) { int apiDiscStepForward(lua_State *L) { int frame = 0; + int aFrame = 0; (void)L; // No matter disc state, go forward a frame. If playing, pause. frame = videoGetFrame(_videoHandle) + 1; - videoSeek(_videoHandle, frame); + if (_confIsFrameFile) { + frameFileSeek(_frameFileHandle, frame, &_videoHandle, &aFrame); + } else { + videoSeek(_videoHandle, frame); + } videoPause(_videoHandle); luaTrace(L, "discStepForward", "%d", frame); @@ -2035,7 +2072,13 @@ void singe(SDL_Window *window, SDL_Renderer *renderer) { lua_register(_luaContext, "singeGetScriptPath", apiSingeGetScriptPath); // Open main video file - _videoHandle = videoLoad(_confVideoFile, _confDataDir, (bool)_confStretchVideo, _renderer); + if (_confIsFrameFile) { + _frameFileHandle = frameFileLoad(_confVideoFile, _confDataDir, (bool)_confStretchVideo, _renderer); + if (_frameFileHandle < 0) utilDie("Unable to load framefile: %s", _confVideoFile); + frameFileSeek(_frameFileHandle, 0, &_videoHandle, &thisFrame); // Fills in _videoHandle + } else { + _videoHandle = videoLoad(_confVideoFile, _confDataDir, (bool)_confStretchVideo, _renderer); + } if (_videoHandle < 0) utilDie("Unable to load video file: %s", _confVideoFile); videoSetVolume(_videoHandle, _confVolumeVldp, _confVolumeVldp); @@ -2239,7 +2282,10 @@ void singe(SDL_Window *window, SDL_Renderer *renderer) { // Update video thisFrame = videoUpdate(_videoHandle, &_videoTexture); - if ((thisFrame != lastFrame) && (thisFrame>= 0)) { + if (_confIsFrameFile) { + frameFileUpdate(_frameFileHandle, &_videoHandle); + } + if ((thisFrame != lastFrame) && (thisFrame >= 0)) { lastFrame = thisFrame; _refreshDisplay = true; } @@ -2306,7 +2352,11 @@ void singe(SDL_Window *window, SDL_Renderer *renderer) { } // Unload video - videoUnload(_videoHandle); + if (_confIsFrameFile) { + frameFileUnload(_frameFileHandle); + } else { + videoUnload(_videoHandle); + } // Stop mice SDL_ShowCursor(SDL_ENABLE); diff --git a/singe/singe.h b/singe/singe.h index 7cc10063e..066b52704 100644 --- a/singe/singe.h +++ b/singe/singe.h @@ -38,6 +38,7 @@ extern char *_confVideoFile; extern char *_confScriptFile; extern char *_confDataDir; +extern bool _confIsFrameFile; extern bool _confStretchVideo; extern bool _confNoMouse; extern bool _confNoStats; diff --git a/singe/singe.pro b/singe/singe.pro index 97e488611..e3ad7c12b 100644 --- a/singe/singe.pro +++ b/singe/singe.pro @@ -81,16 +81,19 @@ QMAKE_CFLAGS += \ HEADERS += \ $$MANYMOUSE_HEADERS \ + frameFile.h \ stddclmr.h \ thirdparty/uthash.h \ common.h \ util.h \ videoPlayer.h \ singe.h \ - extensions.h + extensions.h \ + font.h SOURCES += \ $$MANYMOUSE_SOURCES \ + frameFile.c \ util.c \ videoPlayer.c \ singe.c \ @@ -112,6 +115,10 @@ OTHER_FILES += \ postLink.sh \ buildRelease.sh +#linux:QMAKE_POST_LINK += bash $$PWD/postLink.sh "$$PWD" "$$DESTDIR" "$$TARGET" + +# === Generate some data for our buildRelease script === + FILEINFO = "SOURCES=\"$$SOURCES\"" \ "HEADERS=\"$$HEADERS\"" \ "LINUX_LIBS=\"$$LIBS\"" \ @@ -119,6 +126,3 @@ FILEINFO = "SOURCES=\"$$SOURCES\"" \ "QMAKE_CFLAGS=\"$$QMAKE_CFLAGS\"" write_file("source.inc.sh", FILEINFO) - -# Strip & UPX the final result -#linux:QMAKE_POST_LINK += bash $$PWD/postLink.sh "$$PWD" "$$DESTDIR" "$$TARGET" diff --git a/singe/util.c b/singe/util.c index 12f22c80c..5ce966c4a 100644 --- a/singe/util.c +++ b/singe/util.c @@ -96,6 +96,30 @@ bool utilFileExists(char *filename) { } +void utilFixPathSeparators(char **path) { + + int i = 0; + char *temp = *path; + + // Flip path separators to whatever our OS wants + while (temp[i] != 0) { + if (temp[i] == '\\' || temp[i] == '/') { + temp[i] = utilGetPathSeparator(); + } + i++; + } + + // Does this string end with a path separator? + if (temp[strlen(temp) - 1] != utilGetPathSeparator()) { + // No - append one. + temp = realloc(temp, sizeof(char) * (strlen(temp) + 1)); + temp[strlen(temp) - 1] = utilGetPathSeparator(); + temp[strlen(temp)] = 0; + *path = temp; + } +} + + char *utilGetFileExtension(char *filename) { char *start = filename + strlen(filename); int x; @@ -149,6 +173,75 @@ bool utilPathExists(char *pathname) { } +char *utilReadFile(char *filename, size_t *bytes) { + char *data = NULL; + FILE *in = fopen(filename, "rb"); + + *bytes = 0; + + if (in) { + fseek(in, 0, SEEK_END); + *bytes = ftell(in); + fseek(in, 0, SEEK_SET); + data = malloc(sizeof(char) * (*bytes)); + fread(data, sizeof(char), *bytes, in); + fclose(in); + } + + return data; +} + + +char *utilReadLine(char *haystack, size_t length, char **offset) { + size_t bytes = 0; + char *temp = *offset; + char *tail = temp; + char *result = NULL; + + // They didn't know where to start + if (temp == NULL) { + temp = haystack; + tail = temp; + } + + // Is there still data to read? + while ((size_t)(tail - haystack) < length) { + // Is this the end of a line? + if ((tail[0] == 10) || (tail[0] == 13)) { + // Yep! + bytes = tail - temp + 1; + result = malloc(sizeof(char) * bytes); + memcpy(result, temp, bytes - 1); + result[bytes - 1] = 0; + // Read past any additional CR/LFs + while ((tail[0] == 10) || (tail[0] == 13)) { + tail++; + } + // Return where we left off + temp = tail; + *offset = temp; + return result; + } + // Next character + tail++; + } + + // Was there data at the end of the block with no CR/LF? + if (tail > temp) { + // Yep. Treat it as a line. + bytes = tail - temp + 1; + result = malloc(sizeof(char) * bytes); + memcpy(result, temp, bytes - 1); + result[bytes] = 0; + temp = tail; + *offset = temp; + } + + // Didn't find anything + return result; +} + + void utilRedirectConsole(void) { #ifdef _WIN32 // http://dslweb.nwnexus.com/~ast/dload/guicon.htm diff --git a/singe/util.h b/singe/util.h index 8adaf3e9a..bf00c8cbc 100644 --- a/singe/util.h +++ b/singe/util.h @@ -38,10 +38,13 @@ char *utilCreateString(char *format, ...); char *utilCreateStringVArgs(char *format, va_list args); void utilDie(char *fmt, ...); bool utilFileExists(char *filename); +void utilFixPathSeparators(char **path); char *utilGetFileExtension(char *filename); char *utilGetLastPathComponent(char *pathname); char utilGetPathSeparator(void); bool utilPathExists(char *pathname); +char *utilReadFile(char *filename, size_t *bytes); +char *utilReadLine(char *haystack, size_t length, char **offset); void utilRedirectConsole(void); void utilSay(char *fmt, ...); void utilTrace(char *fmt, ...); diff --git a/singe/videoPlayer.c b/singe/videoPlayer.c index 6319d092e..4e5bd82dd 100644 --- a/singe/videoPlayer.c +++ b/singe/videoPlayer.c @@ -77,8 +77,10 @@ typedef struct VideoPlayerS { #pragma GCC diagnostic pop -void _dequeueVideoAudio(int channel, void *stream, int len, void *udata); -int FFMS_CC _indexCallBack(int64_t Current, int64_t Total, void *ICPrivate); +FFMS_Index *_createIndex(char *filename, char *indexPath, bool hasVideo, bool hasAudio, VideoPlayerT *v); +void _dequeueVideoAudio(int channel, void *stream, int len, void *udata); +int FFMS_CC _indexCallBack(int64_t Current, int64_t Total, void *ICPrivate); +int _loadVideoAndAudio(char *vFilename, char *aFilename, char *indexPath, bool stretchVideo, SDL_Renderer *renderer); static VideoPlayerT *_videoPlayerHash = NULL; @@ -147,104 +149,10 @@ int FFMS_CC _indexCallBack(int64_t current, int64_t total, void *ICPrivate) { return 0; } - -int videoInit(void) { - - int channels = _mixChannels; - - // Start FFMS - FFMS_Init(0, 0); - - // Fetch mixer settings - if (!Mix_QuerySpec(&_mixRate, &_mixFormat, &channels)) utilDie("%s", Mix_GetError()); - _mixChannels = (Uint8)channels; - - // Volume only works with MIX_DEFAULT_FORMAT - if (_mixFormat != MIX_DEFAULT_FORMAT) utilDie("videoInit: Only MIX_DEFAULT_FORMAT audio is supported."); - - return 0; -} - - -int videoIsPlaying(int playerIndex) { - VideoPlayerT *v = NULL; - - // Get our player structure - HASH_FIND_INT(_videoPlayerHash, &playerIndex, v); - if (!v) utilDie("No video player at index %d in videoIsPlaying.", playerIndex); - - return v->playing; -} - - -int videoGetFrame(int playerIndex) { - VideoPlayerT *v = NULL; - - // Get our player structure - HASH_FIND_INT(_videoPlayerHash, &playerIndex, v); - if (!v) utilDie("No video player at index %d in videoGetHeight.", playerIndex); - - return v->frame; -} - - -int videoGetHeight(int playerIndex) { - VideoPlayerT *v = NULL; - - // Get our player structure - HASH_FIND_INT(_videoPlayerHash, &playerIndex, v); - if (!v) utilDie("No video player at index %d in videoGetHeight.", playerIndex); - - return v->propFrame->EncodedHeight; -} - - -int videoGetWidth(int playerIndex) { - VideoPlayerT *v = NULL; - - // Get our player structure - HASH_FIND_INT(_videoPlayerHash, &playerIndex, v); - if (!v) utilDie("No video player at index %d in videoGetWidth.", playerIndex); - - return v->propFrame->EncodedWidth; -} - - -int videoGetVolume(int playerIndex, int *leftPercent, int *rightPercent) { - VideoPlayerT *v = NULL; - - // Get our player structure - HASH_FIND_INT(_videoPlayerHash, &playerIndex, v); - if (!v) utilDie("No video player at index %d in videoGetVolume.", playerIndex); - - if (leftPercent != NULL) *leftPercent = v->volumeLeft; - if (rightPercent != NULL) *rightPercent = v->volumeRight; - - return 0; -} - - -int videoLoad(char *filename, char *indexPath, bool stretchVideo, SDL_Renderer *renderer) { +FFMS_Index *_createIndex(char *filename, char *indexPath, bool hasVideo, bool hasAudio, VideoPlayerT *v) { char indexName[1024]; - int pixelFormats[2]; - int result = -1; FFMS_Index *index = NULL; FFMS_Indexer *indexer = NULL; - VideoPlayerT *v = NULL; - - // Create new videoPlayer - v = calloc(1, sizeof(VideoPlayerT)); - if (!v) utilDie("Unable to allocate new video player."); - - // Set some starting values - v->audioTrack = -1; - v->videoTrack = -1; - v->audioSilenceChannel = -1; - v->playing = false; // Start paused - v->errInfo.Buffer = v->errMsg; - v->errInfo.BufferSize = sizeof(v->errMsg); - v->errInfo.ErrorType = FFMS_ERROR_SUCCESS; - v->errInfo.SubType = FFMS_ERROR_SUCCESS; // Index file snprintf(indexName, 1024, "%s%c%s.index", indexPath, utilGetPathSeparator(), utilGetLastPathComponent(filename)); @@ -264,8 +172,8 @@ int videoLoad(char *filename, char *indexPath, bool stretchVideo, SDL_Renderer * utilSay("Creating new index."); indexer = FFMS_CreateIndexer(filename, &v->errInfo); if (indexer == NULL) utilDie("%s", v->errInfo.Buffer); - FFMS_TrackTypeIndexSettings(indexer, FFMS_TYPE_AUDIO, 1, 0); - FFMS_TrackTypeIndexSettings(indexer, FFMS_TYPE_VIDEO, 1, 0); + if (hasAudio) FFMS_TrackTypeIndexSettings(indexer, FFMS_TYPE_AUDIO, 1, 0); + if (hasVideo) FFMS_TrackTypeIndexSettings(indexer, FFMS_TYPE_VIDEO, 1, 0); _indexCallBack(0, 0, v); FFMS_SetProgressCallback(indexer, _indexCallBack, v); index = FFMS_DoIndexing2(indexer, FFMS_IEH_ABORT, &v->errInfo); @@ -273,10 +181,43 @@ int videoLoad(char *filename, char *indexPath, bool stretchVideo, SDL_Renderer * if (FFMS_WriteIndex(indexName, index, &v->errInfo)) utilDie("%s", v->errInfo.Buffer); } + return index; +} + + +int _loadVideoAndAudio(char *vFilename, char *aFilename, char *indexPath, bool stretchVideo, SDL_Renderer *renderer) { + int pixelFormats[2]; + int result = -1; + FFMS_Index *vIndex = NULL; + FFMS_Index *aIndex = NULL; + VideoPlayerT *v = NULL; + + // Create new videoPlayer + v = calloc(1, sizeof(VideoPlayerT)); + if (!v) utilDie("Unable to allocate new video player."); + + // Set some starting values + v->audioTrack = -1; + v->videoTrack = -1; + v->audioSilenceChannel = -1; + v->playing = false; // Start paused + v->errInfo.Buffer = v->errMsg; + v->errInfo.BufferSize = sizeof(v->errMsg); + v->errInfo.ErrorType = FFMS_ERROR_SUCCESS; + v->errInfo.SubType = FFMS_ERROR_SUCCESS; + + if (aFilename) { + vIndex = _createIndex(vFilename, indexPath, true, false, v); + aIndex = _createIndex(aFilename, indexPath, false, true, v); + } else { + vIndex = _createIndex(vFilename, indexPath, true, true, v); + aIndex = vIndex; + } + // Find video track - v->videoTrack = FFMS_GetFirstTrackOfType(index, FFMS_TYPE_VIDEO, &v->errInfo); + v->videoTrack = FFMS_GetFirstTrackOfType(vIndex, FFMS_TYPE_VIDEO, &v->errInfo); if (v->videoTrack < 0) utilDie("%s", v->errInfo.Buffer); - v->videoSource = FFMS_CreateVideoSource(filename, v->videoTrack, index, 1, FFMS_SEEK_NORMAL, &v->errInfo); + v->videoSource = FFMS_CreateVideoSource(vFilename, v->videoTrack, vIndex, 1, FFMS_SEEK_NORMAL, &v->errInfo); if (v->videoSource == NULL) utilDie("%s", v->errInfo.Buffer); // Get video properties @@ -291,20 +232,24 @@ int videoLoad(char *filename, char *indexPath, bool stretchVideo, SDL_Renderer * if (FFMS_SetOutputFormatV2(v->videoSource, pixelFormats, v->propFrame->EncodedWidth, v->propFrame->EncodedHeight, FFMS_RESIZER_BICUBIC, &v->errInfo)) utilDie("%s", v->errInfo.Buffer); // Find audio track - v->audioTrack = FFMS_GetFirstTrackOfType(index, FFMS_TYPE_AUDIO, &v->errInfo); + v->audioTrack = FFMS_GetFirstTrackOfType(aIndex, FFMS_TYPE_AUDIO, &v->errInfo); if (v->audioTrack < 0) utilDie("%s", v->errInfo.Buffer); - v->audioSource = FFMS_CreateAudioSource(filename, v->audioTrack, index, FFMS_DELAY_FIRST_VIDEO_TRACK, &v->errInfo); + v->audioSource = FFMS_CreateAudioSource(aFilename, v->audioTrack, aIndex, FFMS_DELAY_FIRST_VIDEO_TRACK, &v->errInfo); if (v->audioSource == NULL) utilDie("%s", v->errInfo.Buffer); // Get audio properties v->audioProps = FFMS_GetAudioProperties(v->audioSource); - // Index is now part of audioSource & videoSource, so release this one - FFMS_DestroyIndex(index); + // Indicies are now part of audioSource & videoSource, so release these + FFMS_DestroyIndex(vIndex); + vIndex = NULL; + if (aIndex) { + FFMS_DestroyIndex(aIndex); + aIndex = NULL; + } // Create video texture SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear"); - //v->videoTexture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_BGRA32, SDL_TEXTUREACCESS_STREAMING, v->propFrame->EncodedWidth, v->propFrame->EncodedHeight); v->videoTexture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_BGRA32, SDL_TEXTUREACCESS_TARGET, v->propFrame->EncodedWidth, v->propFrame->EncodedHeight); if (v->videoTexture == NULL) utilDie("%s", SDL_GetError()); if (!stretchVideo) { @@ -387,6 +332,104 @@ int videoLoad(char *filename, char *indexPath, bool stretchVideo, SDL_Renderer * } +int videoInit(void) { + + int channels = _mixChannels; + + // Start FFMS + FFMS_Init(0, 0); + + // Fetch mixer settings + if (!Mix_QuerySpec(&_mixRate, &_mixFormat, &channels)) utilDie("%s", Mix_GetError()); + _mixChannels = (Uint8)channels; + + // Volume only works with MIX_DEFAULT_FORMAT + if (_mixFormat != MIX_DEFAULT_FORMAT) utilDie("videoInit: Only MIX_DEFAULT_FORMAT audio is supported."); + + return 0; +} + + +int videoIsPlaying(int playerIndex) { + VideoPlayerT *v = NULL; + + // Get our player structure + HASH_FIND_INT(_videoPlayerHash, &playerIndex, v); + if (!v) utilDie("No video player at index %d in videoIsPlaying.", playerIndex); + + return v->playing; +} + + +int videoGetFrame(int playerIndex) { + VideoPlayerT *v = NULL; + + // Get our player structure + HASH_FIND_INT(_videoPlayerHash, &playerIndex, v); + if (!v) utilDie("No video player at index %d in videoGetFrame.", playerIndex); + + return v->frame; +} + + +int videoGetFrameCount(int playerIndex) { + VideoPlayerT *v = NULL; + + // Get our player structure + HASH_FIND_INT(_videoPlayerHash, &playerIndex, v); + if (!v) utilDie("No video player at index %d in videoGetFrameCount.", playerIndex); + + return v->videoProps->NumFrames; + +} + + +int videoGetHeight(int playerIndex) { + VideoPlayerT *v = NULL; + + // Get our player structure + HASH_FIND_INT(_videoPlayerHash, &playerIndex, v); + if (!v) utilDie("No video player at index %d in videoGetHeight.", playerIndex); + + return v->propFrame->EncodedHeight; +} + + +int videoGetWidth(int playerIndex) { + VideoPlayerT *v = NULL; + + // Get our player structure + HASH_FIND_INT(_videoPlayerHash, &playerIndex, v); + if (!v) utilDie("No video player at index %d in videoGetWidth.", playerIndex); + + return v->propFrame->EncodedWidth; +} + + +int videoGetVolume(int playerIndex, int *leftPercent, int *rightPercent) { + VideoPlayerT *v = NULL; + + // Get our player structure + HASH_FIND_INT(_videoPlayerHash, &playerIndex, v); + if (!v) utilDie("No video player at index %d in videoGetVolume.", playerIndex); + + if (leftPercent != NULL) *leftPercent = v->volumeLeft; + if (rightPercent != NULL) *rightPercent = v->volumeRight; + + return 0; +} + + +int videoLoad(char *filename, char *indexPath, bool stretchVideo, SDL_Renderer *renderer) { + return _loadVideoAndAudio(filename, NULL, indexPath, stretchVideo, renderer); +} + + +int videoLoadWithAudio(char *vFilename, char *aFilename, char *indexPath, bool stretchVideo, SDL_Renderer *renderer) { + return _loadVideoAndAudio(vFilename, aFilename, indexPath, stretchVideo, renderer); +} + + int videoPause(int playerIndex) { VideoPlayerT *v = NULL; diff --git a/singe/videoPlayer.h b/singe/videoPlayer.h index 9300e9ba8..0b2cace74 100644 --- a/singe/videoPlayer.h +++ b/singe/videoPlayer.h @@ -32,10 +32,12 @@ int videoInit(void); int videoIsPlaying(int playerIndex); int videoGetFrame(int playerIndex); +int videoGetFrameCount(int playerIndex); int videoGetHeight(int playerIndex); int videoGetWidth(int playerIndex); int videoGetVolume(int playerIndex, int *leftPercent, int *rightPercent); int videoLoad(char *filename, char *indexPath, bool stretchVideo, SDL_Renderer *renderer); +int videoLoadWithAudio(char *vFilename, char *aFilename, char *indexPath, bool stretchVideo, SDL_Renderer *renderer); int videoPause(int playerIndex); int videoPlay(int playerIndex); int videoQuit(void);