diff --git a/extern/audaspace/CMakeLists.txt b/extern/audaspace/CMakeLists.txt index 1599c03cbad..552ff749512 100644 --- a/extern/audaspace/CMakeLists.txt +++ b/extern/audaspace/CMakeLists.txt @@ -152,6 +152,7 @@ set(PUBLIC_HDR include/devices/ThreadedDevice.h include/Exception.h include/file/File.h + include/file/FileInfo.h include/file/FileManager.h include/file/FileWriter.h include/file/IFileInput.h @@ -960,7 +961,10 @@ endif() if(BUILD_DEMOS) include_directories(${INCLUDE}) - set(DEMOS audaplay audaconvert audaremap signalgen randsounds dynamicmusic playbackmanager) + set(DEMOS audainfo audaplay audaconvert audaremap signalgen randsounds dynamicmusic playbackmanager) + + add_executable(audainfo demos/audainfo.cpp) + target_link_libraries(audainfo audaspace) add_executable(audaplay demos/audaplay.cpp) target_link_libraries(audaplay audaspace) diff --git a/extern/audaspace/bindings/C/AUD_PlaybackManager.h b/extern/audaspace/bindings/C/AUD_PlaybackManager.h index 0fa8171599d..a2f5134602a 100644 --- a/extern/audaspace/bindings/C/AUD_PlaybackManager.h +++ b/extern/audaspace/bindings/C/AUD_PlaybackManager.h @@ -39,7 +39,7 @@ extern AUD_API void AUD_PlaybackManager_free(AUD_PlaybackManager* manager); * Plays a sound through the playback manager, adding it into a category. * \param manager The PlaybackManager object. * \param sound The sound to be played. -* \param catKey The key of the category into which the sound will be added. If it doesn't exist a new one will be creatd. +* \param catKey The key of the category into which the sound will be added. If it doesn't exist a new one will be created. */ extern AUD_API void AUD_PlaybackManager_play(AUD_PlaybackManager* manager, AUD_Sound* sound, unsigned int catKey); diff --git a/extern/audaspace/bindings/C/AUD_Sound.cpp b/extern/audaspace/bindings/C/AUD_Sound.cpp index 8c99ce2341f..aa246b9a047 100644 --- a/extern/audaspace/bindings/C/AUD_Sound.cpp +++ b/extern/audaspace/bindings/C/AUD_Sound.cpp @@ -94,6 +94,36 @@ AUD_API int AUD_Sound_getLength(AUD_Sound* sound) return (*sound)->createReader()->getLength(); } +AUD_API int AUD_Sound_getFileStreams(AUD_Sound* sound, AUD_StreamInfo **stream_infos) +{ + assert(sound); + + std::shared_ptr file = std::dynamic_pointer_cast(*sound); + + if(file) + { + auto streams = file->queryStreams(); + + size_t size = sizeof(AUD_StreamInfo) * streams.size(); + + if(!size) + { + *stream_infos = nullptr; + return 0; + } + + *stream_infos = reinterpret_cast(std::malloc(size)); + std::memcpy(*stream_infos, streams.data(), size); + + return streams.size(); + } + else + { + *stream_infos = nullptr; + return 0; + } +} + AUD_API sample_t* AUD_Sound_data(AUD_Sound* sound, int* length, AUD_Specs* specs) { assert(sound); @@ -252,6 +282,12 @@ AUD_API AUD_Sound* AUD_Sound_bufferFile(unsigned char* buffer, int size) return new AUD_Sound(new File(buffer, size)); } +AUD_API AUD_Sound* AUD_Sound_bufferFileStream(unsigned char* buffer, int size, int stream) +{ + assert(buffer); + return new AUD_Sound(new File(buffer, size, stream)); +} + AUD_API AUD_Sound* AUD_Sound_cache(AUD_Sound* sound) { assert(sound); @@ -272,6 +308,12 @@ AUD_API AUD_Sound* AUD_Sound_file(const char* filename) return new AUD_Sound(new File(filename)); } +AUD_API AUD_Sound* AUD_Sound_fileStream(const char* filename, int stream) +{ + assert(filename); + return new AUD_Sound(new File(filename, stream)); +} + AUD_API AUD_Sound* AUD_Sound_sawtooth(float frequency, AUD_SampleRate rate) { return new AUD_Sound(new Sawtooth(frequency, rate)); diff --git a/extern/audaspace/bindings/C/AUD_Sound.h b/extern/audaspace/bindings/C/AUD_Sound.h index 53172616781..fc73a31e15c 100644 --- a/extern/audaspace/bindings/C/AUD_Sound.h +++ b/extern/audaspace/bindings/C/AUD_Sound.h @@ -36,7 +36,15 @@ extern AUD_API AUD_Specs AUD_Sound_getSpecs(AUD_Sound* sound); * \return The length of the sound in samples. * \note This function creates a reader from the sound and deletes it again. */ -extern AUD_API int AUD_getLength(AUD_Sound* sound); +extern AUD_API int AUD_Sound_getLength(AUD_Sound* sound); + +/** + * Retrieves the stream infos of a sound file. + * \param sound The sound to retrieve from which must be a file sound. + * \param infos A pointer to a AUD_StreamInfo array that will be allocated and must afterwards be freed by the caller. + * \return The number of items in the infos array. + */ +extern AUD_API int AUD_Sound_getFileStreams(AUD_Sound* sound, AUD_StreamInfo** stream_infos); /** * Reads a sound's samples into memory. @@ -89,6 +97,15 @@ extern AUD_API AUD_Sound* AUD_Sound_buffer(sample_t* data, int length, AUD_Specs */ extern AUD_API AUD_Sound* AUD_Sound_bufferFile(unsigned char* buffer, int size); +/** + * Loads a sound file from a memory buffer. + * \param buffer The buffer which contains the sound file. + * \param size The size of the buffer. + * \param stream The index of the audio stream within the file if it contains multiple audio streams. + * \return A handle of the sound file. + */ +extern AUD_API AUD_Sound* AUD_Sound_bufferFileStream(unsigned char* buffer, int size, int stream); + /** * Caches a sound into a memory buffer. * \param sound The sound to cache. @@ -103,6 +120,14 @@ extern AUD_API AUD_Sound* AUD_Sound_cache(AUD_Sound* sound); */ extern AUD_API AUD_Sound* AUD_Sound_file(const char* filename); +/** + * Loads a sound file. + * \param filename The filename of the sound file. + * \param stream The index of the audio stream within the file if it contains multiple audio streams. + * \return A handle of the sound file. + */ +extern AUD_API AUD_Sound* AUD_Sound_fileStream(const char* filename, int stream); + /** * Creates a sawtooth sound. * \param frequency The frequency of the generated sawtooth sound. diff --git a/extern/audaspace/bindings/C/AUD_Special.cpp b/extern/audaspace/bindings/C/AUD_Special.cpp index 5cc33525d1d..1ce25dcd41c 100644 --- a/extern/audaspace/bindings/C/AUD_Special.cpp +++ b/extern/audaspace/bindings/C/AUD_Special.cpp @@ -86,7 +86,6 @@ AUD_API AUD_SoundInfo AUD_getInfo(AUD_Sound* sound) info.specs.channels = AUD_CHANNELS_INVALID; info.specs.rate = AUD_RATE_INVALID; info.length = 0.0f; - info.start_offset = 0.0f; try { @@ -96,7 +95,6 @@ AUD_API AUD_SoundInfo AUD_getInfo(AUD_Sound* sound) { info.specs = convSpecToC(reader->getSpecs()); info.length = reader->getLength() / (float) info.specs.rate; - info.start_offset = reader->getStartOffset(); } } catch(Exception&) @@ -109,7 +107,7 @@ AUD_API AUD_SoundInfo AUD_getInfo(AUD_Sound* sound) AUD_API float* AUD_readSoundBuffer(const char* filename, float low, float high, float attack, float release, float threshold, int accumulate, int additive, int square, - float sthreshold, double samplerate, int* length) + float sthreshold, double samplerate, int* length, int stream) { Buffer buffer; DeviceSpecs specs; @@ -117,7 +115,7 @@ AUD_API float* AUD_readSoundBuffer(const char* filename, float low, float high, specs.rate = (SampleRate)samplerate; std::shared_ptr sound; - std::shared_ptr file = std::shared_ptr(new File(filename)); + std::shared_ptr file = std::shared_ptr(new File(filename, stream)); int position = 0; @@ -247,7 +245,7 @@ AUD_API int AUD_readSound(AUD_Sound* sound, float* buffer, int length, int sampl buffer[i * 3] = min; buffer[i * 3 + 1] = max; - buffer[i * 3 + 2] = sqrt(power / len); // RMS + buffer[i * 3 + 2] = std::sqrt(power / len); if(overallmax < max) overallmax = max; diff --git a/extern/audaspace/bindings/C/AUD_Special.h b/extern/audaspace/bindings/C/AUD_Special.h index ce51fa2e04e..2f5d13c6fd9 100644 --- a/extern/audaspace/bindings/C/AUD_Special.h +++ b/extern/audaspace/bindings/C/AUD_Special.h @@ -37,7 +37,7 @@ extern AUD_API float* AUD_readSoundBuffer(const char* filename, float low, float float attack, float release, float threshold, int accumulate, int additive, int square, float sthreshold, double samplerate, - int* length); + int* length, int stream); /** * Pauses a playing sound after a specific amount of time. diff --git a/extern/audaspace/bindings/C/AUD_Types.h b/extern/audaspace/bindings/C/AUD_Types.h index c6a96d30d3f..0f95366bc27 100644 --- a/extern/audaspace/bindings/C/AUD_Types.h +++ b/extern/audaspace/bindings/C/AUD_Types.h @@ -176,5 +176,17 @@ typedef struct { AUD_Specs specs; float length; - double start_offset; } AUD_SoundInfo; + +/// Specification of a sound source. +typedef struct +{ + /// Start time in seconds. + double start; + + /// Duration in seconds. May be estimated or 0 if unknown. + double duration; + + /// Audio data parameters. + AUD_DeviceSpecs specs; +} AUD_StreamInfo; diff --git a/extern/audaspace/bindings/python/PySound.cpp b/extern/audaspace/bindings/python/PySound.cpp index 33628307249..2236057e7d2 100644 --- a/extern/audaspace/bindings/python/PySound.cpp +++ b/extern/audaspace/bindings/python/PySound.cpp @@ -89,10 +89,11 @@ Sound_new(PyTypeObject* type, PyObject* args, PyObject* kwds) self = (Sound*)type->tp_alloc(type, 0); if(self != nullptr) { - static const char* kwlist[] = {"filename", nullptr}; + static const char* kwlist[] = {"filename", "stream", nullptr}; const char* filename = nullptr; + int stream = 0; - if(!PyArg_ParseTupleAndKeywords(args, kwds, "s:Sound", const_cast(kwlist), &filename)) + if(!PyArg_ParseTupleAndKeywords(args, kwds, "s|i:Sound", const_cast(kwlist), &filename, &stream)) { Py_DECREF(self); return nullptr; @@ -100,7 +101,7 @@ Sound_new(PyTypeObject* type, PyObject* args, PyObject* kwds) try { - self->sound = new std::shared_ptr(new File(filename)); + self->sound = new std::shared_ptr(new File(filename, stream)); } catch(Exception& e) { @@ -407,8 +408,9 @@ static PyObject * Sound_file(PyTypeObject* type, PyObject* args) { const char* filename = nullptr; + int stream = 0; - if(!PyArg_ParseTuple(args, "s:file", &filename)) + if(!PyArg_ParseTuple(args, "s|i:file", &filename, &stream)) return nullptr; Sound* self; @@ -418,7 +420,7 @@ Sound_file(PyTypeObject* type, PyObject* args) { try { - self->sound = new std::shared_ptr(new File(filename)); + self->sound = new std::shared_ptr(new File(filename, stream)); } catch(Exception& e) { diff --git a/extern/audaspace/include/IReader.h b/extern/audaspace/include/IReader.h index f6070b0f23b..c29900ca579 100644 --- a/extern/audaspace/include/IReader.h +++ b/extern/audaspace/include/IReader.h @@ -70,12 +70,6 @@ public: */ virtual int getPosition() const=0; - /** - * Returns the start offset the sound should have to line up with related sources. - * \return The required start offset in seconds. - */ - virtual double getStartOffset() const { return 0.0;} - /** * Returns the specification of the reader. * \return The Specs structure. diff --git a/extern/audaspace/include/file/File.h b/extern/audaspace/include/file/File.h index 24745a757e8..ac490acba38 100644 --- a/extern/audaspace/include/file/File.h +++ b/extern/audaspace/include/file/File.h @@ -23,9 +23,11 @@ */ #include "ISound.h" +#include "FileInfo.h" #include #include +#include AUD_NAMESPACE_BEGIN @@ -48,6 +50,14 @@ private: */ std::shared_ptr m_buffer; + /** + * The index of the stream within the file if it contains multiple. + * The first audio stream in the file has index 0 and the index increments by one + * for every other audio stream in the file. Other types of streams in the file + * do not count. + */ + int m_stream; + // delete copy constructor and operator= File(const File&) = delete; File& operator=(const File&) = delete; @@ -57,16 +67,25 @@ public: * Creates a new sound. * The file is read from the file system using the given path. * \param filename The sound file path. + * \param stream The index of the audio stream within the file if it contains multiple audio streams. */ - File(std::string filename); + File(std::string filename, int stream = 0); /** * Creates a new sound. * The file is read from memory using the supplied buffer. * \param buffer The buffer to read from. * \param size The size of the buffer. + * \param stream The index of the audio stream within the file if it contains multiple audio streams. */ - File(const data_t* buffer, int size); + File(const data_t* buffer, int size, int stream = 0); + + /** + * Queries the streams of the file. + * \return A vector with as many streams as there are in the file. + * \exception Exception Thrown if the file specified cannot be read. + */ + std::vector queryStreams(); virtual std::shared_ptr createReader(); }; diff --git a/extern/audaspace/include/file/FileInfo.h b/extern/audaspace/include/file/FileInfo.h new file mode 100644 index 00000000000..53ba99a5f67 --- /dev/null +++ b/extern/audaspace/include/file/FileInfo.h @@ -0,0 +1,42 @@ +/******************************************************************************* + * Copyright 2009-2016 Jörg Müller + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ + +#pragma once + +/** + * @file FileInfo.h + * @ingroup file + * The FileInfo data structures. + */ + +#include "respec/Specification.h" + +AUD_NAMESPACE_BEGIN + +/// Specification of a sound source. +struct StreamInfo +{ + /// Start time in seconds. + double start; + + /// Duration in seconds. May be estimated or 0 if unknown. + double duration; + + /// Audio data parameters. + DeviceSpecs specs; +}; + +AUD_NAMESPACE_END diff --git a/extern/audaspace/include/file/FileManager.h b/extern/audaspace/include/file/FileManager.h index 56708607ea6..e19eef65b1c 100644 --- a/extern/audaspace/include/file/FileManager.h +++ b/extern/audaspace/include/file/FileManager.h @@ -22,12 +22,14 @@ * The FileManager class. */ +#include "FileInfo.h" #include "respec/Specification.h" #include "IWriter.h" #include #include #include +#include AUD_NAMESPACE_BEGIN @@ -66,18 +68,36 @@ public: /** * Creates a file reader for the given filename if a registed IFileInput is able to read it. * @param filename The path to the file. + * @param stream The index of the audio stream within the file if it contains multiple audio streams. * @return The reader created. * @exception Exception If no file input can read the file an exception is thrown. */ - static std::shared_ptr createReader(std::string filename); + static std::shared_ptr createReader(std::string filename, int stream = 0); /** * Creates a file reader for the given buffer if a registed IFileInput is able to read it. * @param buffer The buffer to read the file from. + * @param stream The index of the audio stream within the file if it contains multiple audio streams. * @return The reader created. * @exception Exception If no file input can read the file an exception is thrown. */ - static std::shared_ptr createReader(std::shared_ptr buffer); + static std::shared_ptr createReader(std::shared_ptr buffer, int stream = 0); + + /** + * Queries the streams of a sound file. + * \param filename Path to the file to be read. + * \return A vector with as many streams as there are in the file. + * \exception Exception Thrown if the file specified cannot be read. + */ + static std::vector queryStreams(std::string filename); + + /** + * Queries the streams of a sound file. + * \param buffer The in-memory file buffer. + * \return A vector with as many streams as there are in the file. + * \exception Exception Thrown if the file specified cannot be read. + */ + static std::vector queryStreams(std::shared_ptr buffer); /** * Creates a file writer that writes a sound to the given file path. diff --git a/extern/audaspace/include/file/IFileInput.h b/extern/audaspace/include/file/IFileInput.h index 64074910d13..4a3fe446852 100644 --- a/extern/audaspace/include/file/IFileInput.h +++ b/extern/audaspace/include/file/IFileInput.h @@ -23,9 +23,11 @@ */ #include "Audaspace.h" +#include "FileInfo.h" #include #include +#include AUD_NAMESPACE_BEGIN @@ -48,18 +50,36 @@ public: /** * Creates a reader for a file to be read. * \param filename Path to the file to be read. + * \param stream The index of the audio stream within the file if it contains multiple audio streams. * \return The reader that reads the file. * \exception Exception Thrown if the file specified cannot be read. */ - virtual std::shared_ptr createReader(std::string filename)=0; + virtual std::shared_ptr createReader(std::string filename, int stream = 0)=0; /** * Creates a reader for a file to be read from memory. * \param buffer The in-memory file buffer. + * \param stream The index of the audio stream within the file if it contains multiple audio streams. * \return The reader that reads the file. * \exception Exception Thrown if the file specified cannot be read. */ - virtual std::shared_ptr createReader(std::shared_ptr buffer)=0; + virtual std::shared_ptr createReader(std::shared_ptr buffer, int stream = 0)=0; + + /** + * Queries the streams of a sound file. + * \param filename Path to the file to be read. + * \return A vector with as many streams as there are in the file. + * \exception Exception Thrown if the file specified cannot be read. + */ + virtual std::vector queryStreams(std::string filename)=0; + + /** + * Queries the streams of a sound file. + * \param buffer The in-memory file buffer. + * \return A vector with as many streams as there are in the file. + * \exception Exception Thrown if the file specified cannot be read. + */ + virtual std::vector queryStreams(std::shared_ptr buffer)=0; }; AUD_NAMESPACE_END diff --git a/extern/audaspace/include/fx/VolumeReader.h b/extern/audaspace/include/fx/VolumeReader.h index f7169f4c78b..13b6845e931 100644 --- a/extern/audaspace/include/fx/VolumeReader.h +++ b/extern/audaspace/include/fx/VolumeReader.h @@ -67,4 +67,4 @@ public: virtual void read(int& length, bool& eos, sample_t* buffer); }; -AUD_NAMESPACE_END +AUD_NAMESPACE_END \ No newline at end of file diff --git a/extern/audaspace/plugins/ffmpeg/FFMPEG.cpp b/extern/audaspace/plugins/ffmpeg/FFMPEG.cpp index 3ffe963b2b9..07c0fee691a 100644 --- a/extern/audaspace/plugins/ffmpeg/FFMPEG.cpp +++ b/extern/audaspace/plugins/ffmpeg/FFMPEG.cpp @@ -35,14 +35,24 @@ void FFMPEG::registerPlugin() FileManager::registerOutput(plugin); } -std::shared_ptr FFMPEG::createReader(std::string filename) +std::shared_ptr FFMPEG::createReader(std::string filename, int stream) { - return std::shared_ptr(new FFMPEGReader(filename)); + return std::shared_ptr(new FFMPEGReader(filename, stream)); } -std::shared_ptr FFMPEG::createReader(std::shared_ptr buffer) +std::shared_ptr FFMPEG::createReader(std::shared_ptr buffer, int stream) { - return std::shared_ptr(new FFMPEGReader(buffer)); + return std::shared_ptr(new FFMPEGReader(buffer, stream)); +} + +std::vector FFMPEG::queryStreams(std::string filename) +{ + return FFMPEGReader(filename).queryStreams(); +} + +std::vector FFMPEG::queryStreams(std::shared_ptr buffer) +{ + return FFMPEGReader(buffer).queryStreams(); } std::shared_ptr FFMPEG::createWriter(std::string filename, DeviceSpecs specs, Container format, Codec codec, unsigned int bitrate) diff --git a/extern/audaspace/plugins/ffmpeg/FFMPEG.h b/extern/audaspace/plugins/ffmpeg/FFMPEG.h index 108ba547e0f..fb40ba05573 100644 --- a/extern/audaspace/plugins/ffmpeg/FFMPEG.h +++ b/extern/audaspace/plugins/ffmpeg/FFMPEG.h @@ -52,8 +52,10 @@ public: */ static void registerPlugin(); - virtual std::shared_ptr createReader(std::string filename); - virtual std::shared_ptr createReader(std::shared_ptr buffer); + virtual std::shared_ptr createReader(std::string filename, int stream = 0); + virtual std::shared_ptr createReader(std::shared_ptr buffer, int stream = 0); + virtual std::vector queryStreams(std::string filename); + virtual std::vector queryStreams(std::shared_ptr buffer); virtual std::shared_ptr createWriter(std::string filename, DeviceSpecs specs, Container format, Codec codec, unsigned int bitrate); }; diff --git a/extern/audaspace/plugins/ffmpeg/FFMPEGReader.cpp b/extern/audaspace/plugins/ffmpeg/FFMPEGReader.cpp index afdc7fcfcc6..de3ca099696 100644 --- a/extern/audaspace/plugins/ffmpeg/FFMPEGReader.cpp +++ b/extern/audaspace/plugins/ffmpeg/FFMPEGReader.cpp @@ -31,6 +31,25 @@ AUD_NAMESPACE_BEGIN #define FFMPEG_OLD_CODE #endif +SampleFormat FFMPEGReader::convertSampleFormat(AVSampleFormat format) +{ + switch(av_get_packed_sample_fmt(format)) + { + case AV_SAMPLE_FMT_U8: + return FORMAT_U8; + case AV_SAMPLE_FMT_S16: + return FORMAT_S16; + case AV_SAMPLE_FMT_S32: + return FORMAT_S32; + case AV_SAMPLE_FMT_FLT: + return FORMAT_FLOAT32; + case AV_SAMPLE_FMT_DBL: + return FORMAT_FLOAT64; + default: + AUD_THROW(FileException, "FFMPEG sample format unknown."); + } +} + int FFMPEGReader::decode(AVPacket& packet, Buffer& buffer) { int buf_size = buffer.getSize(); @@ -68,7 +87,7 @@ int FFMPEGReader::decode(AVPacket& packet, Buffer& buffer) for(int i = 0; i < m_frame->nb_samples; i++) { std::memcpy(((data_t*)buffer.getBuffer()) + buf_pos + ((m_codecCtx->channels * i) + channel) * single_size, - m_frame->data[channel] + i * single_size, single_size); + m_frame->data[channel] + i * single_size, single_size); } } } @@ -109,7 +128,7 @@ int FFMPEGReader::decode(AVPacket& packet, Buffer& buffer) for(int i = 0; i < m_frame->nb_samples; i++) { std::memcpy(((data_t*)buffer.getBuffer()) + buf_pos + ((m_codecCtx->channels * i) + channel) * single_size, - m_frame->data[channel] + i * single_size, single_size); + m_frame->data[channel] + i * single_size, single_size); } } } @@ -123,13 +142,10 @@ int FFMPEGReader::decode(AVPacket& packet, Buffer& buffer) return buf_pos; } -void FFMPEGReader::init() +void FFMPEGReader::init(int stream) { m_position = 0; - m_start_offset = 0.0f; m_pkgbuf_left = 0; - m_st_time = 0; - m_duration = 0; if(avformat_find_stream_info(m_formatCtx, nullptr) < 0) AUD_THROW(FileException, "File couldn't be read, ffmpeg couldn't find the stream info."); @@ -137,43 +153,22 @@ void FFMPEGReader::init() // find audio stream and codec m_stream = -1; - double dur_sec = 0; - for(unsigned int i = 0; i < m_formatCtx->nb_streams; i++) { #ifdef FFMPEG_OLD_CODE - if(m_formatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) + if((m_formatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) #else - if(m_formatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) + if((m_formatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) #endif + && (m_stream < 0)) { - AVStream *audio_stream = m_formatCtx->streams[i]; - double audio_timebase = av_q2d(audio_stream->time_base); - - if (audio_stream->start_time != AV_NOPTS_VALUE) + if(stream == 0) { - m_st_time = audio_stream->start_time; - } - - int64_t ctx_start_time = 0; - if (m_formatCtx->start_time != AV_NOPTS_VALUE) { - ctx_start_time = m_formatCtx->start_time; - } - - m_start_offset = m_st_time * audio_timebase - (double)ctx_start_time / AV_TIME_BASE; - - if(audio_stream->duration != AV_NOPTS_VALUE) - { - dur_sec = audio_stream->duration * audio_timebase; + m_stream=i; + break; } else - { - /* If the audio starts after the stream start time, subract this from the total duration. */ - dur_sec = (double)m_formatCtx->duration / AV_TIME_BASE - m_start_offset; - } - - m_stream=i; - break; + stream--; } } @@ -242,10 +237,9 @@ void FFMPEGReader::init() } m_specs.rate = (SampleRate) m_codecCtx->sample_rate; - m_duration = lround(dur_sec * m_codecCtx->sample_rate); } -FFMPEGReader::FFMPEGReader(std::string filename) : +FFMPEGReader::FFMPEGReader(std::string filename, int stream) : m_pkgbuf(), m_formatCtx(nullptr), m_codecCtx(nullptr), @@ -259,7 +253,7 @@ FFMPEGReader::FFMPEGReader(std::string filename) : try { - init(); + init(stream); } catch(Exception&) { @@ -268,7 +262,7 @@ FFMPEGReader::FFMPEGReader(std::string filename) : } } -FFMPEGReader::FFMPEGReader(std::shared_ptr buffer) : +FFMPEGReader::FFMPEGReader(std::shared_ptr buffer, int stream) : m_pkgbuf(), m_codecCtx(nullptr), m_frame(nullptr), @@ -295,7 +289,7 @@ FFMPEGReader::FFMPEGReader(std::shared_ptr buffer) : try { - init(); + init(stream); } catch(Exception&) { @@ -318,6 +312,51 @@ FFMPEGReader::~FFMPEGReader() avformat_close_input(&m_formatCtx); } +std::vector FFMPEGReader::queryStreams() +{ + std::vector result; + + for(unsigned int i = 0; i < m_formatCtx->nb_streams; i++) + { +#ifdef FFMPEG_OLD_CODE + if(m_formatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) +#else + if(m_formatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) +#endif + { + StreamInfo info; + + double time_base = av_q2d(m_formatCtx->streams[i]->time_base); + + if(m_formatCtx->streams[i]->start_time != AV_NOPTS_VALUE) + info.start = m_formatCtx->streams[i]->start_time * time_base; + else + info.start = 0; + + if(m_formatCtx->streams[i]->duration != AV_NOPTS_VALUE) + info.duration = m_formatCtx->streams[i]->duration * time_base; + else if(m_formatCtx->duration != AV_NOPTS_VALUE) + info.duration = double(m_formatCtx->duration) / AV_TIME_BASE - info.start; + else + info.duration = 0; + +#ifdef FFMPEG_OLD_CODE + info.specs.channels = Channels(m_formatCtx->streams[i]->codec->channels); + info.specs.rate = m_formatCtx->streams[i]->codec->sample_rate; + info.specs.format = convertSampleFormat(m_formatCtx->streams[i]->codec->sample_fmt); +#else + info.specs.channels = Channels(m_formatCtx->streams[i]->codecpar->channels); + info.specs.rate = m_formatCtx->streams[i]->codecpar->sample_rate; + info.specs.format = convertSampleFormat(AVSampleFormat(m_formatCtx->streams[i]->codecpar->format)); +#endif + + result.emplace_back(info); + } + } + + return result; +} + int FFMPEGReader::read_packet(void* opaque, uint8_t* buf, int buf_size) { FFMPEGReader* reader = reinterpret_cast(opaque); @@ -368,18 +407,16 @@ void FFMPEGReader::seek(int position) { if(position >= 0) { - double pts_time_base = - av_q2d(m_formatCtx->streams[m_stream]->time_base); + double pts_time_base = av_q2d(m_formatCtx->streams[m_stream]->time_base); - uint64_t seek_pts = (((uint64_t)position) / ((uint64_t)m_specs.rate)) / pts_time_base; + uint64_t st_time = m_formatCtx->streams[m_stream]->start_time; + uint64_t seek_pos = (uint64_t)(position / (pts_time_base * m_specs.rate)); - if(m_st_time != AV_NOPTS_VALUE) { - seek_pts += m_st_time; - } + if(st_time != AV_NOPTS_VALUE) + seek_pos += st_time; // a value < 0 tells us that seeking failed - if(av_seek_frame(m_formatCtx, m_stream, seek_pts, - AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_ANY) >= 0) + if(av_seek_frame(m_formatCtx, m_stream, seek_pos, AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_ANY) >= 0) { avcodec_flush_buffers(m_codecCtx); m_position = position; @@ -400,7 +437,7 @@ void FFMPEGReader::seek(int position) if(packet.pts != AV_NOPTS_VALUE) { // calculate real position, and read to frame! - m_position = (packet.pts - m_st_time) * pts_time_base * m_specs.rate; + m_position = (packet.pts - (st_time != AV_NOPTS_VALUE ? st_time : 0)) * pts_time_base * m_specs.rate; if(m_position < position) { @@ -430,8 +467,25 @@ void FFMPEGReader::seek(int position) int FFMPEGReader::getLength() const { + auto stream = m_formatCtx->streams[m_stream]; + + double time_base = av_q2d(stream->time_base); + double duration; + + if(stream->duration != AV_NOPTS_VALUE) + duration = stream->duration * time_base; + else if(m_formatCtx->duration != AV_NOPTS_VALUE) + { + duration = float(m_formatCtx->duration) / AV_TIME_BASE; + + if(stream->start_time != AV_NOPTS_VALUE) + duration -= stream->start_time * time_base; + } + else + duration = -1; + // return approximated remaning size - return m_duration - m_position; + return (int)(duration * m_codecCtx->sample_rate) - m_position; } int FFMPEGReader::getPosition() const @@ -439,11 +493,6 @@ int FFMPEGReader::getPosition() const return m_position; } -double FFMPEGReader::getStartOffset() const -{ - return m_start_offset; -} - Specs FFMPEGReader::getSpecs() const { return m_specs.specs; @@ -480,13 +529,11 @@ void FFMPEGReader::read(int& length, bool& eos, sample_t* buffer) // decode the package pkgbuf_pos = decode(packet, m_pkgbuf); - if (packet.pts >= m_st_time) { - // copy to output buffer - data_size = std::min(pkgbuf_pos, left * sample_size); - m_convert((data_t*) buf, (data_t*) m_pkgbuf.getBuffer(), data_size / AUD_FORMAT_SIZE(m_specs.format)); - buf += data_size / AUD_FORMAT_SIZE(m_specs.format); - left -= data_size / sample_size; - } + // copy to output buffer + data_size = std::min(pkgbuf_pos, left * sample_size); + m_convert((data_t*) buf, (data_t*) m_pkgbuf.getBuffer(), data_size / AUD_FORMAT_SIZE(m_specs.format)); + buf += data_size / AUD_FORMAT_SIZE(m_specs.format); + left -= data_size / sample_size; } av_packet_unref(&packet); } diff --git a/extern/audaspace/plugins/ffmpeg/FFMPEGReader.h b/extern/audaspace/plugins/ffmpeg/FFMPEGReader.h index d613457c220..70f13911eca 100644 --- a/extern/audaspace/plugins/ffmpeg/FFMPEGReader.h +++ b/extern/audaspace/plugins/ffmpeg/FFMPEGReader.h @@ -29,9 +29,11 @@ #include "respec/ConverterFunctions.h" #include "IReader.h" #include "util/Buffer.h" +#include "file/FileInfo.h" #include #include +#include struct AVCodecContext; extern "C" { @@ -54,22 +56,6 @@ private: */ int m_position; - /** - * The start offset in seconds relative to the media container start time. - * IE how much the sound should be delayed to be kept in sync with the rest of the containter streams. - */ - double m_start_offset; - - /** - * The start time pts of the stream. All packets before this timestamp shouldn't be played back (only decoded). - */ - int64_t m_st_time; - - /** - * The duration of the audio stream in samples. - */ - int64_t m_duration; - /** * The specification of the audio data. */ @@ -135,6 +121,13 @@ private: */ bool m_tointerleave; + /** + * Converts an ffmpeg sample format to an audaspace one. + * \param format The AVSampleFormat sample format. + * \return The sample format as SampleFormat. + */ + AUD_LOCAL static SampleFormat convertSampleFormat(AVSampleFormat format); + /** * Decodes a packet into the given buffer. * \param packet The AVPacket to decode. @@ -145,8 +138,9 @@ private: /** * Initializes the object. + * \param stream The index of the audio stream within the file if it contains multiple audio streams. */ - AUD_LOCAL void init(); + AUD_LOCAL void init(int stream); // delete copy constructor and operator= FFMPEGReader(const FFMPEGReader&) = delete; @@ -156,24 +150,33 @@ public: /** * Creates a new reader. * \param filename The path to the file to be read. + * \param stream The index of the audio stream within the file if it contains multiple audio streams. * \exception Exception Thrown if the file specified does not exist or * cannot be read with ffmpeg. */ - FFMPEGReader(std::string filename); + FFMPEGReader(std::string filename, int stream = 0); /** * Creates a new reader. * \param buffer The buffer to read from. + * \param stream The index of the audio stream within the file if it contains multiple audio streams. * \exception Exception Thrown if the buffer specified cannot be read * with ffmpeg. */ - FFMPEGReader(std::shared_ptr buffer); + FFMPEGReader(std::shared_ptr buffer, int stream = 0); /** * Destroys the reader and closes the file. */ virtual ~FFMPEGReader(); + /** + * Queries the streams of a sound file. + * \return A vector with as many streams as there are in the file. + * \exception Exception Thrown if the file specified cannot be read. + */ + virtual std::vector queryStreams(); + /** * Reads data to a memory buffer. * This function is used for avio only. @@ -198,7 +201,6 @@ public: virtual void seek(int position); virtual int getLength() const; virtual int getPosition() const; - virtual double getStartOffset() const; virtual Specs getSpecs() const; virtual void read(int& length, bool& eos, sample_t* buffer); }; diff --git a/extern/audaspace/plugins/libsndfile/SndFile.cpp b/extern/audaspace/plugins/libsndfile/SndFile.cpp index ba4ff24ad68..39335de9a1a 100644 --- a/extern/audaspace/plugins/libsndfile/SndFile.cpp +++ b/extern/audaspace/plugins/libsndfile/SndFile.cpp @@ -32,16 +32,26 @@ void SndFile::registerPlugin() FileManager::registerOutput(plugin); } -std::shared_ptr SndFile::createReader(std::string filename) +std::shared_ptr SndFile::createReader(std::string filename, int stream) { return std::shared_ptr(new SndFileReader(filename)); } -std::shared_ptr SndFile::createReader(std::shared_ptr buffer) +std::shared_ptr SndFile::createReader(std::shared_ptr buffer, int stream) { return std::shared_ptr(new SndFileReader(buffer)); } +std::vector SndFile::queryStreams(std::string filename) +{ + return SndFileReader(filename).queryStreams(); +} + +std::vector SndFile::queryStreams(std::shared_ptr buffer) +{ + return SndFileReader(buffer).queryStreams(); +} + std::shared_ptr SndFile::createWriter(std::string filename, DeviceSpecs specs, Container format, Codec codec, unsigned int bitrate) { return std::shared_ptr(new SndFileWriter(filename, specs, format, codec, bitrate)); diff --git a/extern/audaspace/plugins/libsndfile/SndFile.h b/extern/audaspace/plugins/libsndfile/SndFile.h index 61afed1d564..10a7391180f 100644 --- a/extern/audaspace/plugins/libsndfile/SndFile.h +++ b/extern/audaspace/plugins/libsndfile/SndFile.h @@ -52,8 +52,10 @@ public: */ static void registerPlugin(); - virtual std::shared_ptr createReader(std::string filename); - virtual std::shared_ptr createReader(std::shared_ptr buffer); + virtual std::shared_ptr createReader(std::string filename, int stream = 0); + virtual std::shared_ptr createReader(std::shared_ptr buffer, int stream = 0); + virtual std::vector queryStreams(std::string filename); + virtual std::vector queryStreams(std::shared_ptr buffer); virtual std::shared_ptr createWriter(std::string filename, DeviceSpecs specs, Container format, Codec codec, unsigned int bitrate); }; diff --git a/extern/audaspace/plugins/libsndfile/SndFileReader.cpp b/extern/audaspace/plugins/libsndfile/SndFileReader.cpp index d2d89814c07..21c733d8117 100644 --- a/extern/audaspace/plugins/libsndfile/SndFileReader.cpp +++ b/extern/audaspace/plugins/libsndfile/SndFileReader.cpp @@ -118,6 +118,21 @@ SndFileReader::~SndFileReader() sf_close(m_sndfile); } +std::vector SndFileReader::queryStreams() +{ + std::vector result; + + StreamInfo info; + info.start = 0; + info.duration = double(getLength()) / m_specs.rate; + info.specs.specs = m_specs; + info.specs.format = FORMAT_FLOAT32; + + result.emplace_back(info); + + return result; +} + bool SndFileReader::isSeekable() const { return m_seekable; diff --git a/extern/audaspace/plugins/libsndfile/SndFileReader.h b/extern/audaspace/plugins/libsndfile/SndFileReader.h index 081c29c686c..b4158d9091a 100644 --- a/extern/audaspace/plugins/libsndfile/SndFileReader.h +++ b/extern/audaspace/plugins/libsndfile/SndFileReader.h @@ -28,9 +28,12 @@ * The SndFileReader class. */ +#include "file/FileInfo.h" + #include #include #include +#include AUD_NAMESPACE_BEGIN @@ -96,6 +99,7 @@ public: /** * Creates a new reader. * \param filename The path to the file to be read. + * \param stream The index of the audio stream within the file if it contains multiple audio streams. * \exception Exception Thrown if the file specified does not exist or * cannot be read with libsndfile. */ @@ -104,6 +108,7 @@ public: /** * Creates a new reader. * \param buffer The buffer to read from. + * \param stream The index of the audio stream within the file if it contains multiple audio streams. * \exception Exception Thrown if the buffer specified cannot be read * with libsndfile. */ @@ -114,6 +119,13 @@ public: */ virtual ~SndFileReader(); + /** + * Queries the streams of a sound file. + * \return A vector with as many streams as there are in the file. + * \exception Exception Thrown if the file specified cannot be read. + */ + virtual std::vector queryStreams(); + virtual bool isSeekable() const; virtual void seek(int position); virtual int getLength() const; diff --git a/extern/audaspace/src/file/File.cpp b/extern/audaspace/src/file/File.cpp index 0cdecb03657..5d4bae482d6 100644 --- a/extern/audaspace/src/file/File.cpp +++ b/extern/audaspace/src/file/File.cpp @@ -23,23 +23,31 @@ AUD_NAMESPACE_BEGIN -File::File(std::string filename) : - m_filename(filename) +File::File(std::string filename, int stream) : + m_filename(filename), m_stream(stream) { } -File::File(const data_t* buffer, int size) : - m_buffer(new Buffer(size)) +File::File(const data_t* buffer, int size, int stream) : + m_buffer(new Buffer(size)), m_stream(stream) { std::memcpy(m_buffer->getBuffer(), buffer, size); } +std::vector File::queryStreams() +{ + if(m_buffer.get()) + return FileManager::queryStreams(m_buffer); + else + return FileManager::queryStreams(m_filename); +} + std::shared_ptr File::createReader() { if(m_buffer.get()) - return FileManager::createReader(m_buffer); + return FileManager::createReader(m_buffer, m_stream); else - return FileManager::createReader(m_filename); + return FileManager::createReader(m_filename, m_stream); } AUD_NAMESPACE_END diff --git a/extern/audaspace/src/file/FileManager.cpp b/extern/audaspace/src/file/FileManager.cpp index f8ef8deb409..7cbc0318f8c 100644 --- a/extern/audaspace/src/file/FileManager.cpp +++ b/extern/audaspace/src/file/FileManager.cpp @@ -43,13 +43,13 @@ void FileManager::registerOutput(std::shared_ptr output) outputs().push_back(output); } -std::shared_ptr FileManager::createReader(std::string filename) +std::shared_ptr FileManager::createReader(std::string filename, int stream) { for(std::shared_ptr input : inputs()) { try { - return input->createReader(filename); + return input->createReader(filename, stream); } catch(Exception&) {} } @@ -57,13 +57,41 @@ std::shared_ptr FileManager::createReader(std::string filename) AUD_THROW(FileException, "The file couldn't be read with any installed file reader."); } -std::shared_ptr FileManager::createReader(std::shared_ptr buffer) +std::shared_ptr FileManager::createReader(std::shared_ptr buffer, int stream) { for(std::shared_ptr input : inputs()) { try { - return input->createReader(buffer); + return input->createReader(buffer, stream); + } + catch(Exception&) {} + } + + AUD_THROW(FileException, "The file couldn't be read with any installed file reader."); +} + +std::vector FileManager::queryStreams(std::string filename) +{ + for(std::shared_ptr input : inputs()) + { + try + { + return input->queryStreams(filename); + } + catch(Exception&) {} + } + + AUD_THROW(FileException, "The file couldn't be read with any installed file reader."); +} + +std::vector FileManager::queryStreams(std::shared_ptr buffer) +{ + for(std::shared_ptr input : inputs()) + { + try + { + return input->queryStreams(buffer); } catch(Exception&) {} } diff --git a/extern/audaspace/src/fx/VolumeReader.cpp b/extern/audaspace/src/fx/VolumeReader.cpp index 627acbac9ef..ac1d4882a87 100644 --- a/extern/audaspace/src/fx/VolumeReader.cpp +++ b/extern/audaspace/src/fx/VolumeReader.cpp @@ -57,4 +57,4 @@ void VolumeReader::read(int& length, bool& eos, sample_t* buffer) buffer[i] = buffer[i] * m_volumeStorage->getVolume(); } -AUD_NAMESPACE_END +AUD_NAMESPACE_END \ No newline at end of file diff --git a/source/blender/blenkernel/BKE_sound.h b/source/blender/blenkernel/BKE_sound.h index fa58813c5f8..8796e2c18f3 100644 --- a/source/blender/blenkernel/BKE_sound.h +++ b/source/blender/blenkernel/BKE_sound.h @@ -96,13 +96,24 @@ typedef struct SoundInfo { eSoundChannels channels; } specs; float length; - double start_offset; } SoundInfo; +typedef struct SoundStreamInfo { + double duration; + double start; +} SoundStreamInfo; + /* Get information about given sound. Returns truth on success., false if sound can not be loaded * or if the codes is not supported. */ bool BKE_sound_info_get(struct Main *main, struct bSound *sound, SoundInfo *sound_info); +/* Get information about given sound. Returns truth on success., false if sound can not be loaded + * or if the codes is not supported. */ +bool BKE_sound_stream_info_get(struct Main *main, + const char *filepath, + int stream, + SoundStreamInfo *sound_info); + #if defined(WITH_AUDASPACE) AUD_Device *BKE_sound_mixdown(const struct Scene *scene, AUD_DeviceSpecs specs, diff --git a/source/blender/blenkernel/intern/sound.c b/source/blender/blenkernel/intern/sound.c index c61fa793367..ccb10f080e3 100644 --- a/source/blender/blenkernel/intern/sound.c +++ b/source/blender/blenkernel/intern/sound.c @@ -1213,7 +1213,6 @@ static bool sound_info_from_playback_handle(void *playback_handle, SoundInfo *so AUD_SoundInfo info = AUD_getInfo(playback_handle); sound_info->specs.channels = (eSoundChannels)info.specs.channels; sound_info->length = info.length; - sound_info->start_offset = info.start_offset; return true; } @@ -1231,6 +1230,44 @@ bool BKE_sound_info_get(struct Main *main, struct bSound *sound, SoundInfo *soun return result; } +bool BKE_sound_stream_info_get(struct Main *main, const char *filepath, int stream, SoundStreamInfo *sound_info) +{ + const char *path; + char str[FILE_MAX]; + AUD_Sound *sound; + AUD_StreamInfo *stream_infos; + int stream_count; + + BLI_strncpy(str, filepath, sizeof(str)); + path = BKE_main_blendfile_path(main); + BLI_path_abs(str, path); + + sound = AUD_Sound_file(str); + if (!sound) { + return false; + } + + stream_count = AUD_Sound_getFileStreams(sound, &stream_infos); + + AUD_Sound_free(sound); + + if (!stream_infos) { + return false; + } + + if ((stream < 0) || (stream >= stream_count)) { + free(stream_infos); + return false; + } + + sound_info->start = stream_infos[stream].start; + sound_info->duration = stream_infos[stream].duration; + + free(stream_infos); + + return true; +} + #else /* WITH_AUDASPACE */ # include "BLI_utildefines.h" @@ -1400,6 +1437,14 @@ bool BKE_sound_info_get(struct Main *UNUSED(main), return false; } +bool BKE_sound_stream_info_get(struct Main *UNUSED(main), + const char *UNUSED(filepath), + int UNUSED(stream), + SoundStreamInfo *UNUSED(sound_info)) +{ + return false; +} + #endif /* WITH_AUDASPACE */ void BKE_sound_reset_scene_runtime(Scene *scene) diff --git a/source/blender/editors/space_graph/graph_edit.c b/source/blender/editors/space_graph/graph_edit.c index 2955c4ef7ae..872b17372de 100644 --- a/source/blender/editors/space_graph/graph_edit.c +++ b/source/blender/editors/space_graph/graph_edit.c @@ -1098,7 +1098,8 @@ static int graphkeys_sound_bake_exec(bContext *C, wmOperator *op) RNA_boolean_get(op->ptr, "use_square"), RNA_float_get(op->ptr, "sthreshold"), FPS, - &sbi.length); + &sbi.length, + 0); if (sbi.samples == NULL) { BKE_report(op->reports, RPT_ERROR, "Unsupported audio format"); diff --git a/source/blender/editors/space_sequencer/sequencer_add.c b/source/blender/editors/space_sequencer/sequencer_add.c index 081f0241e94..bdfa639b327 100644 --- a/source/blender/editors/space_sequencer/sequencer_add.c +++ b/source/blender/editors/space_sequencer/sequencer_add.c @@ -47,6 +47,7 @@ #include "BKE_mask.h" #include "BKE_movieclip.h" #include "BKE_report.h" +#include "BKE_sound.h" #include "IMB_imbuf.h" @@ -643,7 +644,15 @@ static void sequencer_add_movie_multiple_strips(bContext *C, BLI_strncpy(load_data->name, file_only, sizeof(load_data->name)); Sequence *seq_movie = NULL; Sequence *seq_sound = NULL; - double video_start_offset; + double video_start_offset = -1; + double audio_start_offset = 0; + + if (RNA_boolean_get(op->ptr, "sound")) { + SoundStreamInfo sound_info; + if (BKE_sound_stream_info_get(bmain, load_data->path, 0, &sound_info)) { + audio_start_offset = video_start_offset = sound_info.start; + } + } load_data->channel++; seq_movie = SEQ_add_movie_strip(bmain, scene, ed->seqbasep, load_data, &video_start_offset); @@ -653,9 +662,30 @@ static void sequencer_add_movie_multiple_strips(bContext *C, } else { if (RNA_boolean_get(op->ptr, "sound")) { - seq_sound = SEQ_add_sound_strip(bmain, scene, ed->seqbasep, load_data, video_start_offset); + int minimum_frame_offset = MIN2(video_start_offset, audio_start_offset) * FPS; + + int video_frame_offset = video_start_offset * FPS; + int audio_frame_offset = audio_start_offset * FPS; + + double video_frame_remainder = video_start_offset * FPS - video_frame_offset; + double audio_frame_remainder = audio_start_offset * FPS - audio_frame_offset; + + double audio_skip = (video_frame_remainder - audio_frame_remainder) / FPS; + + video_frame_offset -= minimum_frame_offset; + audio_frame_offset -= minimum_frame_offset; + + load_data->start_frame += audio_frame_offset; + seq_sound = SEQ_add_sound_strip(bmain, scene, ed->seqbasep, load_data, audio_skip); + + int min_startdisp = MIN2(seq_movie->startdisp, seq_sound->startdisp); + int max_enddisp = MAX2(seq_movie->enddisp, seq_sound->enddisp); + + load_data->start_frame += max_enddisp - min_startdisp - audio_frame_offset; + } + else { + load_data->start_frame += seq_movie->enddisp - seq_movie->startdisp; } - load_data->start_frame += seq_movie->enddisp - seq_movie->startdisp; seq_load_apply_generic_options(C, op, seq_sound); seq_load_apply_generic_options(C, op, seq_movie); seq_build_proxy(C, seq_movie); @@ -672,7 +702,15 @@ static bool sequencer_add_movie_single_strip(bContext *C, wmOperator *op, SeqLoa Sequence *seq_movie = NULL; Sequence *seq_sound = NULL; - double video_start_offset; + double video_start_offset = -1; + double audio_start_offset = 0; + + if (RNA_boolean_get(op->ptr, "sound")) { + SoundStreamInfo sound_info; + if (BKE_sound_stream_info_get(bmain, load_data->path, 0, &sound_info)) { + audio_start_offset = video_start_offset = sound_info.start; + } + } load_data->channel++; seq_movie = SEQ_add_movie_strip(bmain, scene, ed->seqbasep, load_data, &video_start_offset); @@ -683,7 +721,21 @@ static bool sequencer_add_movie_single_strip(bContext *C, wmOperator *op, SeqLoa return false; } if (RNA_boolean_get(op->ptr, "sound")) { - seq_sound = SEQ_add_sound_strip(bmain, scene, ed->seqbasep, load_data, video_start_offset); + int minimum_frame_offset = MIN2(video_start_offset, audio_start_offset) * FPS; + + int video_frame_offset = video_start_offset * FPS; + int audio_frame_offset = audio_start_offset * FPS; + + double video_frame_remainder = video_start_offset * FPS - video_frame_offset; + double audio_frame_remainder = audio_start_offset * FPS - audio_frame_offset; + + double audio_skip = (video_frame_remainder - audio_frame_remainder) / FPS; + + video_frame_offset -= minimum_frame_offset; + audio_frame_offset -= minimum_frame_offset; + + load_data->start_frame += audio_frame_offset; + seq_sound = SEQ_add_sound_strip(bmain, scene, ed->seqbasep, load_data, audio_skip); } seq_load_apply_generic_options(C, op, seq_sound); seq_load_apply_generic_options(C, op, seq_movie); diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index b56ad48cec2..bf817005a08 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -420,6 +420,10 @@ static void draw_seq_waveform_overlay(View2D *v2d, float sample_offset = start_sample + i * samples_per_pix; int p = sample_offset; + if (p < 0) { + continue; + } + if (p >= waveform->length) { break; } diff --git a/source/blender/imbuf/intern/anim_movie.c b/source/blender/imbuf/intern/anim_movie.c index dbca16ca82b..13f9356751e 100644 --- a/source/blender/imbuf/intern/anim_movie.c +++ b/source/blender/imbuf/intern/anim_movie.c @@ -664,11 +664,6 @@ static int startffmpeg(struct anim *anim) anim->duration_in_frames = (int)(stream_dur * av_q2d(frame_rate) + 0.5f); } - double ctx_start = 0; - if (pFormatCtx->start_time != AV_NOPTS_VALUE) { - ctx_start = (double)pFormatCtx->start_time / AV_TIME_BASE; - } - frs_num = frame_rate.num; frs_den = frame_rate.den; @@ -683,7 +678,7 @@ static int startffmpeg(struct anim *anim) anim->frs_sec_base = frs_den; /* Save the relative start time for the video. IE the start time in relation to where playback * starts. */ - anim->start_offset = video_start - ctx_start; + anim->start_offset = video_start; anim->params = 0; diff --git a/source/blender/makesrna/intern/rna_sequencer_api.c b/source/blender/makesrna/intern/rna_sequencer_api.c index a0564d3435b..b43b57a35be 100644 --- a/source/blender/makesrna/intern/rna_sequencer_api.c +++ b/source/blender/makesrna/intern/rna_sequencer_api.c @@ -323,8 +323,8 @@ static Sequence *rna_Sequences_new_movie(ID *id, SEQ_add_load_data_init(&load_data, name, file, frame_start, channel); load_data.fit_method = fit_method; load_data.allow_invalid_file = true; - double video_start_offset; - Sequence *seq = SEQ_add_movie_strip(bmain, scene, seqbase, &load_data, &video_start_offset); + double start_offset = -1; + Sequence *seq = SEQ_add_movie_strip(bmain, scene, seqbase, &load_data, &start_offset); DEG_relations_tag_update(bmain); DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS); diff --git a/source/blender/sequencer/SEQ_add.h b/source/blender/sequencer/SEQ_add.h index 4025f1a4a04..d2a731d9953 100644 --- a/source/blender/sequencer/SEQ_add.h +++ b/source/blender/sequencer/SEQ_add.h @@ -88,7 +88,7 @@ struct Sequence *SEQ_add_movie_strip(struct Main *bmain, struct Scene *scene, struct ListBase *seqbase, struct SeqLoadData *load_data, - double *r_video_start_offset); + double *r_start_offset); struct Sequence *SEQ_add_scene_strip(struct Scene *scene, struct ListBase *seqbase, struct SeqLoadData *load_data); diff --git a/source/blender/sequencer/intern/strip_add.c b/source/blender/sequencer/intern/strip_add.c index 9081c655d2f..3cf7a4ebf4d 100644 --- a/source/blender/sequencer/intern/strip_add.c +++ b/source/blender/sequencer/intern/strip_add.c @@ -403,26 +403,8 @@ Sequence *SEQ_add_sound_strip(Main *bmain, return NULL; } - /* If this sound it part of a video, then the sound might start after the video. - * In this case we need to then offset the start frame of the audio so it syncs up - * properly with the video. - */ - int start_frame_offset = info.start_offset * FPS; - double start_frame_offset_remainer = (info.start_offset * FPS - start_frame_offset) / FPS; - - if (start_frame_offset_remainer > FLT_EPSILON) { - /* We can't represent a fraction of a frame, so skip the first frame fraction of sound so we - * start on a "whole" frame. - */ - start_frame_offset++; - } - - sound->offset_time += start_frame_offset_remainer; - - Sequence *seq = SEQ_sequence_alloc(seqbase, - load_data->start_frame + start_frame_offset, - load_data->channel, - SEQ_TYPE_SOUND_RAM); + Sequence *seq = SEQ_sequence_alloc( + seqbase, load_data->start_frame, load_data->channel, SEQ_TYPE_SOUND_RAM); seq->sound = sound; seq->scene_sound = NULL; @@ -508,7 +490,7 @@ Sequence *SEQ_add_movie_strip(Main *bmain, Scene *scene, ListBase *seqbase, SeqLoadData *load_data, - double *r_video_start_offset) + double *r_start_offset) { char path[sizeof(load_data->path)]; BLI_strncpy(path, load_data->path, sizeof(path)); @@ -554,8 +536,40 @@ Sequence *SEQ_add_movie_strip(Main *bmain, return NULL; } + int video_frame_offset = 0; + float video_fps = 0.0f; + + if (anim_arr[0] != NULL) { + short fps_denom; + float fps_num; + + IMB_anim_get_fps(anim_arr[0], &fps_denom, &fps_num, true); + + video_fps = fps_denom / fps_num; + + /* Adjust scene's frame rate settings to match. */ + if (load_data->flags & SEQ_LOAD_MOVIE_SYNC_FPS) { + scene->r.frs_sec = fps_denom; + scene->r.frs_sec_base = fps_num; + } + + double video_start_offset = IMD_anim_get_offset(anim_arr[0]); + int minimum_frame_offset; + + if (*r_start_offset >= 0) { + minimum_frame_offset = MIN2(video_start_offset, *r_start_offset) * FPS; + } + else { + minimum_frame_offset = video_start_offset * FPS; + } + + video_frame_offset = video_start_offset * FPS - minimum_frame_offset; + + *r_start_offset = video_start_offset; + } + Sequence *seq = SEQ_sequence_alloc( - seqbase, load_data->start_frame, load_data->channel, SEQ_TYPE_MOVIE); + seqbase, load_data->start_frame + video_frame_offset, load_data->channel, SEQ_TYPE_MOVIE); /* Multiview settings. */ if (load_data->use_multiview) { @@ -579,27 +593,11 @@ Sequence *SEQ_add_movie_strip(Main *bmain, seq->blend_mode = SEQ_TYPE_CROSS; /* so alpha adjustment fade to the strip below */ - float video_fps = 0.0f; - if (anim_arr[0] != NULL) { seq->len = IMB_anim_get_duration(anim_arr[0], IMB_TC_RECORD_RUN); - *r_video_start_offset = IMD_anim_get_offset(anim_arr[0]); IMB_anim_load_metadata(anim_arr[0]); - short fps_denom; - float fps_num; - - IMB_anim_get_fps(anim_arr[0], &fps_denom, &fps_num, true); - - video_fps = fps_denom / fps_num; - - /* Adjust scene's frame rate settings to match. */ - if (load_data->flags & SEQ_LOAD_MOVIE_SYNC_FPS) { - scene->r.frs_sec = fps_denom; - scene->r.frs_sec_base = fps_num; - } - /* Set initial scale based on load_data->fit_method. */ orig_width = IMB_anim_get_image_width(anim_arr[0]); orig_height = IMB_anim_get_image_height(anim_arr[0]);