Framefiles can now contain videos without matching audio tracks.

This commit is contained in:
Scott Duensing 2020-02-04 19:59:57 -06:00
parent c54101b14b
commit 57bb9c7703

View file

@ -235,17 +235,17 @@ int32_t _loadVideoAndAudio(char *vFilename, char *aFilename, char *indexPath, bo
// Find audio track // Find audio track
v->audioTrack = FFMS_GetFirstTrackOfType(aIndex, FFMS_TYPE_AUDIO, &v->errInfo); v->audioTrack = FFMS_GetFirstTrackOfType(aIndex, FFMS_TYPE_AUDIO, &v->errInfo);
if (v->audioTrack < 0) utilDie("%s", v->errInfo.Buffer); if (v->audioTrack >= 0) {
v->audioSource = FFMS_CreateAudioSource(aFilename, v->audioTrack, aIndex, 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); if (v->audioSource == NULL) utilDie("%s", v->errInfo.Buffer);
// Get audio properties
// Get audio properties v->audioProps = FFMS_GetAudioProperties(v->audioSource);
v->audioProps = FFMS_GetAudioProperties(v->audioSource); }
// Indicies are now part of audioSource & videoSource, so release these // Indicies are now part of audioSource & videoSource, so release these
FFMS_DestroyIndex(vIndex); FFMS_DestroyIndex(vIndex);
vIndex = NULL; vIndex = NULL;
if (aFilename != vFilename) { if ((aFilename != vFilename) && (aIndex)) {
FFMS_DestroyIndex(aIndex); FFMS_DestroyIndex(aIndex);
} }
aIndex = NULL; aIndex = NULL;
@ -258,56 +258,59 @@ int32_t _loadVideoAndAudio(char *vFilename, char *aFilename, char *indexPath, bo
SDL_RenderSetLogicalSize(renderer, v->propFrame->EncodedWidth, v->propFrame->EncodedHeight); SDL_RenderSetLogicalSize(renderer, v->propFrame->EncodedWidth, v->propFrame->EncodedHeight);
} }
// Determine audio format // Do we have audio?
switch (v->audioProps->SampleFormat) { if (v->audioSource) {
case FFMS_FMT_U8: // Determine audio format
v->audioFormat = AUDIO_U8; switch (v->audioProps->SampleFormat) {
v->audioSampleBytes = 1; case FFMS_FMT_U8:
break; v->audioFormat = AUDIO_U8;
case FFMS_FMT_S16: v->audioSampleBytes = 1;
v->audioFormat = AUDIO_S16SYS; break;
v->audioSampleBytes = 2; case FFMS_FMT_S16:
break; v->audioFormat = AUDIO_S16SYS;
case FFMS_FMT_S32: v->audioSampleBytes = 2;
v->audioFormat = AUDIO_S32SYS; break;
v->audioSampleBytes = 4; case FFMS_FMT_S32:
break; v->audioFormat = AUDIO_S32SYS;
case FFMS_FMT_FLT: v->audioSampleBytes = 4;
v->audioFormat = AUDIO_F32SYS; break;
v->audioSampleBytes = 4; case FFMS_FMT_FLT:
break; v->audioFormat = AUDIO_F32SYS;
default: v->audioSampleBytes = 4;
utilDie("Unknown audio sample format."); break;
break; default:
utilDie("Unknown audio sample format.");
break;
}
if (v->audioProps->Channels > 2) utilDie("Only mono and stereo audio are supported.");
// Create audio stream to convert audio to our desired format
v->audioStream = SDL_NewAudioStream(v->audioFormat, (Uint8)v->audioProps->Channels, v->audioProps->SampleRate, _mixFormat, _mixChannels, _mixRate);
if (!v->audioStream) utilDie("%s", SDL_GetError());
// Create a buffer to read audio into before conversion
v->mixSampleSize = SDL_AUDIO_BITSIZE(_mixFormat) / 8 * _mixChannels;
v->audioSampleSize = v->audioSampleBytes * v->audioProps->Channels;
v->audioBufferSize = v->audioSampleSize * AUDIO_SAMPLE_PREREAD;
v->audioBuffer = (byte *)malloc((size_t)v->audioBufferSize * sizeof(byte));
if (!v->audioBuffer) utilDie("Unable to allocate %ld byte audio buffer.", (size_t)v->audioBufferSize * sizeof(byte));
// Create a block of silent audio to overlay with video stream audio
v->audioSilenceSize = (Uint32)(_mixRate * SDL_AUDIO_BITSIZE(_mixFormat) / 8 * AUDIO_SILENCE_SECONDS);
v->audioSilenceRaw = (byte *)calloc(1, (size_t)v->audioSilenceSize * sizeof(byte));
if (!v->audioSilenceRaw) utilDie("Unable to allocate %ld silence buffer.", v->audioSilenceSize);
// Load silent audio
v->silenceChunk = Mix_QuickLoad_RAW(v->audioSilenceRaw, v->audioSilenceSize);
if (!v->silenceChunk) utilDie("%s", Mix_GetError());
// Start silent audio playback & immediately pause it
v->audioSilenceChannel = Mix_PlayChannel(-1, v->silenceChunk, -1);
if (v->audioSilenceChannel < 0) utilDie("%s", Mix_GetError());
// Register effect to provide video stream audio on this channel
Mix_RegisterEffect(v->audioSilenceChannel, _dequeueVideoAudio, NULL, v);
} }
if (v->audioProps->Channels > 2) utilDie("Only mono and stereo audio are supported.");
// Create audio stream to convert audio to our desired format
v->audioStream = SDL_NewAudioStream(v->audioFormat, (Uint8)v->audioProps->Channels, v->audioProps->SampleRate, _mixFormat, _mixChannels, _mixRate);
if (!v->audioStream) utilDie("%s", SDL_GetError());
// Create a buffer to read audio into before conversion
v->mixSampleSize = SDL_AUDIO_BITSIZE(_mixFormat) / 8 * _mixChannels;
v->audioSampleSize = v->audioSampleBytes * v->audioProps->Channels;
v->audioBufferSize = v->audioSampleSize * AUDIO_SAMPLE_PREREAD;
v->audioBuffer = (byte *)malloc((size_t)v->audioBufferSize * sizeof(byte));
if (!v->audioBuffer) utilDie("Unable to allocate %ld byte audio buffer.", (size_t)v->audioBufferSize * sizeof(byte));
// Create a block of silent audio to overlay with video stream audio
v->audioSilenceSize = (Uint32)(_mixRate * SDL_AUDIO_BITSIZE(_mixFormat) / 8 * AUDIO_SILENCE_SECONDS);
v->audioSilenceRaw = (byte *)calloc(1, (size_t)v->audioSilenceSize * sizeof(byte));
if (!v->audioSilenceRaw) utilDie("Unable to allocate %ld silence buffer.", v->audioSilenceSize);
// Load silent audio
v->silenceChunk = Mix_QuickLoad_RAW(v->audioSilenceRaw, v->audioSilenceSize);
if (!v->silenceChunk) utilDie("%s", Mix_GetError());
// Start silent audio playback & immediately pause it
v->audioSilenceChannel = Mix_PlayChannel(-1, v->silenceChunk, -1);
if (v->audioSilenceChannel < 0) utilDie("%s", Mix_GetError());
// Register effect to provide video stream audio on this channel
Mix_RegisterEffect(v->audioSilenceChannel, _dequeueVideoAudio, NULL, v);
// Default volume, in percent // Default volume, in percent
v->volumeLeft = 100; v->volumeLeft = 100;
@ -518,16 +521,17 @@ int32_t videoUnload(int32_t playerHandle) {
HASH_FIND_INT(_videoPlayerHash, &playerHandle, v); HASH_FIND_INT(_videoPlayerHash, &playerHandle, v);
if (!v) utilDie("No video player at index %d in videoStop.", playerHandle); if (!v) utilDie("No video player at index %d in videoStop.", playerHandle);
Mix_HaltChannel(v->audioSilenceChannel); if (v->audioSource) {
Mix_UnregisterEffect(v->audioSilenceChannel, _dequeueVideoAudio); Mix_HaltChannel(v->audioSilenceChannel);
Mix_FreeChunk(v->silenceChunk); Mix_UnregisterEffect(v->audioSilenceChannel, _dequeueVideoAudio);
free(v->audioSilenceRaw); Mix_FreeChunk(v->silenceChunk);
SDL_FreeAudioStream(v->audioStream); free(v->audioSilenceRaw);
free(v->audioBuffer); SDL_FreeAudioStream(v->audioStream);
free(v->audioBuffer);
FFMS_DestroyAudioSource(v->audioSource);
}
FFMS_DestroyAudioSource(v->audioSource);
FFMS_DestroyVideoSource(v->videoSource); FFMS_DestroyVideoSource(v->videoSource);
SDL_DestroyTexture(v->videoTexture); SDL_DestroyTexture(v->videoTexture);
#pragma GCC diagnostic push #pragma GCC diagnostic push
@ -552,7 +556,11 @@ int32_t videoUpdate(int32_t playerHandle, SDL_Texture **texture) {
if (!v) utilDie("No video player at index %d in videoUpdate.", playerHandle); if (!v) utilDie("No video player at index %d in videoUpdate.", playerHandle);
// Audio drift limit // Audio drift limit
threshold = (v->audioProps->SampleRate / 2); if (v->audioSource) {
threshold = (v->audioProps->SampleRate / 2);
} else {
threshold = 99999;
}
// Handle video frames (and time) // Handle video frames (and time)
if ((SDL_GetTicks() - v->lastTickTime >= v->frameDeltaTime) || (v->audioDelta > threshold) || v->resetTime) { if ((SDL_GetTicks() - v->lastTickTime >= v->frameDeltaTime) || (v->audioDelta > threshold) || v->resetTime) {
@ -564,22 +572,23 @@ int32_t videoUpdate(int32_t playerHandle, SDL_Texture **texture) {
} }
*texture = v->videoTexture; *texture = v->videoTexture;
result = v->frame; result = v->frame;
v->framesPlayed++; v->framesPlayed++;
// Did we trip the audio sync compensation? if (v->audioSource) {
if (v->audioDelta > threshold) { // Did we trip the audio sync compensation?
// Adjust frame rate to try and match if (v->audioDelta > threshold) {
if (v->audioDelta > 0) { // Adjust frame rate to try and match
v->audioAdjustment += 0.000001; if (v->audioDelta > 0) {
} else { v->audioAdjustment += 0.000001;
v->audioAdjustment -= 0.000001; } else {
v->audioAdjustment -= 0.000001;
}
} }
// Used to determine if we should play two frames rapidly to catch up to audio
v->audioDelta = labs(v->audioPosition - (int64_t)((double)v->timestamp * 0.001 * (double)v->audioProps->SampleRate));
} }
// Used to determine if we should play two frames rapidly to catch up to audio
v->audioDelta = labs(v->audioPosition - (int64_t)((double)v->timestamp * 0.001 * (double)v->audioProps->SampleRate));
//utilSay("D %ld T %ld A %f", v->audioDelta, threshold, v->audioAdjustment); //utilSay("D %ld T %ld A %f", v->audioDelta, threshold, v->audioAdjustment);
v->frameData = FFMS_GetFrame(v->videoSource, v->frame, &v->errInfo); v->frameData = FFMS_GetFrame(v->videoSource, v->frame, &v->errInfo);
@ -598,31 +607,35 @@ int32_t videoUpdate(int32_t playerHandle, SDL_Texture **texture) {
} }
if (v->resetTime) { if (v->resetTime) {
SDL_AudioStreamClear(v->audioStream); if (v->audioSource) {
v->lastTickTime = 0; SDL_AudioStreamClear(v->audioStream);
v->audioPosition = (int64_t)((double)v->timestamp * 0.001 * (double)v->audioProps->SampleRate);
v->audioDelta = 0;
}
v->lastTickTime = 0;
v->frameDeltaTime = 0; v->frameDeltaTime = 0;
v->audioPosition = (int64_t)((double)v->timestamp * 0.001 * (double)v->audioProps->SampleRate); v->resetTime = false;
v->resetTime = false; v->framesPlayed = 0;
v->audioDelta = 0;
v->framesPlayed = 0;
} }
} }
// Handle audio samples // Handle audio samples
if ((v->playing) && (SDL_AudioStreamAvailable(v->audioStream) < AUDIO_STREAM_LOW_WATERMARK) && (v->audioPosition < v->audioProps->NumSamples)) { if (v->audioSource) {
// Maximum samples we can read at a time if ((v->playing) && (SDL_AudioStreamAvailable(v->audioStream) < AUDIO_STREAM_LOW_WATERMARK) && (v->audioPosition < v->audioProps->NumSamples)) {
count = AUDIO_SAMPLE_PREREAD; // Maximum samples we can read at a time
// Don't read past end of audio data count = AUDIO_SAMPLE_PREREAD;
if (v->audioPosition + count >= v->audioProps->NumSamples) { // Don't read past end of audio data
count = v->audioProps->NumSamples - v->audioPosition - 1; if (v->audioPosition + count >= v->audioProps->NumSamples) {
} count = v->audioProps->NumSamples - v->audioPosition - 1;
// Are we reading anything? }
if (count > 0) { // Are we reading anything?
// Get audio from video stream if (count > 0) {
if (FFMS_GetAudio(v->audioSource, v->audioBuffer, v->audioPosition, count, &v->errInfo)) utilDie("%s", v->errInfo.Buffer); // Get audio from video stream
// Feed it to the mixer stream if (FFMS_GetAudio(v->audioSource, v->audioBuffer, v->audioPosition, count, &v->errInfo)) utilDie("%s", v->errInfo.Buffer);
if (SDL_AudioStreamPut(v->audioStream, v->audioBuffer, (int32_t)(count * v->audioSampleSize)) < 0) utilDie("%s", SDL_GetError()); // Feed it to the mixer stream
v->audioPosition += count; if (SDL_AudioStreamPut(v->audioStream, v->audioBuffer, (int32_t)(count * v->audioSampleSize)) < 0) utilDie("%s", SDL_GetError());
v->audioPosition += count;
}
} }
} }