diff --git a/.github/workflows/cmake-multi-platform.yml b/.github/workflows/cmake-multi-platform.yml index dbb8fe2d..01f320db 100644 --- a/.github/workflows/cmake-multi-platform.yml +++ b/.github/workflows/cmake-multi-platform.yml @@ -140,6 +140,38 @@ jobs: echo "CLEAN_BUILD is true, removing entire build directory." if [ -d "build" ]; then rm -rf build; fi + # --- FFmpeg (macOS) --- + - name: Install FFmpeg (macOS) + if: runner.os == 'macOS' + run: | + echo "Installing FFmpeg via Homebrew..." + brew update + brew install ffmpeg + + echo "Verifying installation:" + ffmpeg -version + ffprobe -version + + echo "FFmpeg location:" + which ffmpeg + which ffprobe + + # --- FFmpeg (Windows) --- + - name: Install FFmpeg (Windows) + if: runner.os == 'Windows' + shell: powershell + run: | + Write-Host "Installing FFmpeg via Chocolatey..." + choco install ffmpeg -y + + Write-Host "Verifying installation:" + ffmpeg -version + ffprobe -version + + Write-Host "FFmpeg location:" + where.exe ffmpeg + where.exe ffprobe + # --- Formatting (macOS only for now) --- - name: Check Formatting if: runner.os == 'macOS' diff --git a/CMakeLists.txt b/CMakeLists.txt index 6a29fe76..96dea021 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -143,6 +143,20 @@ if(CI_TEST OR INTERNAL_TEST) include(GoogleTest) include("cmake/eclipsa_test.cmake") include("cmake/eclipsa_build_tests.cmake") + + # Check for an FFmpeg install for tests that do additional media verification + include("cmake/find_ffmpeg.cmake") + if (FFMPEG_TOOLS_FOUND) + message(STATUS "FFmpeg found. Enabling tests that require FFmpeg.") + add_compile_definitions(ECLIPSA_FFMPEG_AVAILABLE=1) + else() + message(STATUS "FFmpeg not found. Tests requiring FFmpeg will be disabled.") + endif() + + # FFmpeg is required for CI tests + if(CI_TEST AND NOT FFMPEG_TOOLS_FOUND) + message(FATAL_ERROR "FFmpeg tools not found. Required for CI tests.") + endif() endif() add_subdirectory(third_party) diff --git a/cmake/copy_resources.cmake b/cmake/copy_resources.cmake index 77e7ad57..e0046fff 100644 --- a/cmake/copy_resources.cmake +++ b/cmake/copy_resources.cmake @@ -18,18 +18,22 @@ function(copy_resources target plugin_path) set(LIB_OBR_PATH "${CMAKE_SOURCE_DIR}/third_party/obr/lib/obr.dylib") set(LIB_IAMF_TOOLS_PATH "${CMAKE_SOURCE_DIR}/third_party/iamftools/lib/libiamf_tools.dylib") set(LIB_ZMQ_5_2_6_PATH "${CMAKE_BINARY_DIR}/_deps/zeromq-build/lib/libzmq.5.2.6.dylib") + set(LIB_GPAC_PATH "${CMAKE_SOURCE_DIR}/third_party/gpac/lib/libgpac.dylib") # Set Resources directory and external subdirectories set(RESOURCES_DIR "${plugin_path}/Contents/Resources") set(EXTERNAL_IAMF_DIR "${RESOURCES_DIR}/third_party/iamftools/lib") set(EXTERNAL_OBR_DIR "${RESOURCES_DIR}/third_party/obr/lib") + set(EXTERNAL_GPAC_DIR "${RESOURCES_DIR}/third_party/gpac/lib") # Copy libraries to the appropriate directories add_custom_command(TARGET ${target} POST_BUILD COMMAND ${CMAKE_COMMAND} -E make_directory ${EXTERNAL_IAMF_DIR} COMMAND ${CMAKE_COMMAND} -E make_directory ${EXTERNAL_OBR_DIR} + COMMAND ${CMAKE_COMMAND} -E make_directory ${EXTERNAL_GPAC_DIR} COMMAND ${CMAKE_COMMAND} -E copy ${LIB_OBR_PATH} ${EXTERNAL_OBR_DIR}/obr.dylib COMMAND ${CMAKE_COMMAND} -E copy ${LIB_IAMF_TOOLS_PATH} ${EXTERNAL_IAMF_DIR}/libiamf_tools.dylib + COMMAND ${CMAKE_COMMAND} -E copy ${LIB_GPAC_PATH} ${EXTERNAL_GPAC_DIR}/libgpac.dylib COMMAND ${CMAKE_COMMAND} -E copy ${LIB_ZMQ_5_2_6_PATH} ${RESOURCES_DIR}/libzmq.5.2.6.dylib # Create symbolic links for libzmq diff --git a/cmake/find_ffmpeg.cmake b/cmake/find_ffmpeg.cmake new file mode 100644 index 00000000..a0b1a757 --- /dev/null +++ b/cmake/find_ffmpeg.cmake @@ -0,0 +1,38 @@ +# Copyright 2025 Google LLC +# +# 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. + +# Find FFmpeg package with platform-independent detection +# Supports standard installations and homebrew on macOS +# +# Sets: +# FFMPEG_TOOLS_FOUND - whether FFmpeg was found +# FFMPEG_EXECUTABLE - path to ffmpeg executable +# FFPROBE_EXECUTABLE - path to ffprobe executable + +# Find ffmpeg and ffprobe executables +find_program(FFMPEG_EXECUTABLE ffmpeg) +find_program(FFPROBE_EXECUTABLE ffprobe) + +# Check if executables were found +if(FFMPEG_EXECUTABLE AND FFPROBE_EXECUTABLE) + set(FFMPEG_TOOLS_FOUND TRUE) +else() + set(FFMPEG_TOOLS_FOUND FALSE) + if(NOT FFMPEG_EXECUTABLE) + message(WARNING "ffmpeg executable not found") + endif() + if(NOT FFPROBE_EXECUTABLE) + message(WARNING "ffprobe executable not found") + endif() +endif() diff --git a/common/processors/file_output/FileOutputProcessor.cpp b/common/processors/file_output/FileOutputProcessor.cpp index 1901288d..2d1744f8 100644 --- a/common/processors/file_output/FileOutputProcessor.cpp +++ b/common/processors/file_output/FileOutputProcessor.cpp @@ -165,23 +165,10 @@ void FileOutputProcessor::closeFileExport(FileExport& config) { // video files. const bool kIamfExported = iamfFileWriter_ ? iamfFileWriter_->close() : false; if (kIamfExported && fileExportRepository_.get().getExportVideo()) { - const bool kVSourcePathValid = FileExport::validateFilePath( - fileExportRepository_.get().getVideoSource().toStdString(), true); - const bool kVOutputPathValid = FileExport::validateFilePath( - fileExportRepository_.get().getVideoExportFolder().toStdString(), - false); - - bool muxIamfSuccess = false; - if (kVSourcePathValid && kVOutputPathValid) { - muxIamfSuccess = IAMFExportHelper::muxIAMF(audioElementRepository_, - mixPresentationRepository_, - fileExportRepository_.get()); - } else { - LOG_WARNING(0, - "IAMF Muxing: Invalid video source or output path provided."); - } + const bool kMuxIamfSuccess = + IAMFExportHelper::muxIAMF(fileExportRepository_.get()); - if (!muxIamfSuccess) { + if (!kMuxIamfSuccess) { LOG_WARNING(0, "IAMF Muxing: Failed to mux IAMF file with provided video."); } diff --git a/common/processors/file_output/iamf_export_utils/IAMFExportUtil.cpp b/common/processors/file_output/iamf_export_utils/IAMFExportUtil.cpp index 76633c53..0c0a9679 100644 --- a/common/processors/file_output/iamf_export_utils/IAMFExportUtil.cpp +++ b/common/processors/file_output/iamf_export_utils/IAMFExportUtil.cpp @@ -15,9 +15,12 @@ #include "IAMFExportUtil.h" #include +#include #include #include +#include "gpac/tools.h" + namespace IAMFExportHelper { void writeIASeqHdr(FileProfile profileVersion, @@ -158,102 +161,277 @@ void writeOPUSConfigMD(const int sampleRate, const int bitratePerChannel, opusConfig->set_allocated_opus_encoder_metadata(opusMD); } -bool muxIAMF(const AudioElementRepository& aeRepo, - const MixPresentationRepository& mpRepo, - const FileExport& exportData) { - const juce::String inputAudioFile = exportData.getExportFile(); - const juce::String inputVideoFile = exportData.getVideoSource(); - const juce::String outputMuxdFile = exportData.getVideoExportFolder(); - - // Initialize GPAC library before creating session - GF_Err init_err = gf_sys_init(GF_MemTrackerNone, NULL); - if (init_err != GF_OK) { - LOG_ERROR(0, "IAMF Muxing: Failed to initialize GPAC system."); +static bool validateMuxingPaths(const juce::String& inputAudioFile, + const juce::String& inputVideoFile, + const juce::String& outputMuxdFile) { + if (!FileExport::validateFilePath( + std::filesystem::path(inputAudioFile.toStdString()), true)) { + LOG_ERROR(0, std::string("IAMFMuxing: Invalid input audio file path ") + + inputAudioFile.toStdString()); + return false; + } + if (!FileExport::validateFilePath( + std::filesystem::path(inputVideoFile.toStdString()), true)) { + LOG_ERROR(0, std::string("IAMFMuxing: Invalid input video file path ") + + inputVideoFile.toStdString()); return false; } + if (!FileExport::validateFilePath( + std::filesystem::path(outputMuxdFile.toStdString()), false)) { + LOG_ERROR(0, std::string("IAMFMuxing: Invalid output muxed file path ") + + outputMuxdFile.toStdString()); + return false; + } + return true; +} + +// Writes IAMF audio to an MP4 file using a GPAC filter session +static bool muxIAMFAudio(const juce::String& inputAudioFile, + const juce::String& outputMp4File) { +#ifdef DEBUG + gf_log_set_tool_level(GF_LOG_FILTER, GF_LOG_INFO); + gf_log_set_tool_level(GF_LOG_CONTAINER, GF_LOG_INFO); +#endif GF_Err gf_err = GF_OK; GF_FilterSession* session = gf_fs_new_defaults(GF_FilterSessionFlags(0)); if (session == NULL) { - LOG_INFO(0, "IAMF Muxing: Failed to create gpac session."); + LOG_INFO(0, "IAMF Audio Muxing: Failed to create gpac session."); gf_fs_del(session); return false; } - // Construct a filter for input audio. + // Filter for input audio GF_Filter* src_audio = gf_fs_load_source(session, inputAudioFile.toRawUTF8(), NULL, NULL, &gf_err); if (gf_err != GF_OK) { - std::string errStr = "IAMF Muxing: Failed to load audio file: " + - inputAudioFile.toStdString(); - LOG_ERROR(0, errStr); + LOG_ERROR(0, "IAMF Audio Muxing: Failed to load audio file " + + inputAudioFile.toStdString() + + " with error: " + std::string(gf_error_to_string(gf_err))); + gf_fs_del(session); return false; } - // Filter for input video. - GF_Filter* src_video = gf_fs_load_source(session, inputVideoFile.toRawUTF8(), - NULL, NULL, &gf_err); + // Reframer for audio stream + GF_Filter* reframer_filter = gf_fs_load_filter(session, "rfav1", &gf_err); if (gf_err != GF_OK) { - std::string errStr = "IAMF Muxing: Failed to load video file: " + - inputVideoFile.toStdString(); - LOG_ERROR(0, errStr); + LOG_ERROR(0, "IAMF Audio Muxing: Failed to load reframer filter."); gf_fs_del(session); return false; } - // Filter for output mp4. + // Filter for output mp4 GF_Filter* dest_filter = gf_fs_load_destination( - session, outputMuxdFile.toRawUTF8(), NULL, NULL, &gf_err); + session, outputMp4File.toRawUTF8(), NULL, NULL, &gf_err); if (gf_err != GF_OK) { - std::string errStr = "IAMF Muxing: Failed to load output destination: " + - outputMuxdFile.toStdString(); - LOG_ERROR(0, errStr); + LOG_ERROR(0, "IAMF Audio Muxing: Failed to load output file filter."); gf_fs_del(session); return false; } - // Reframer for audio stream. - GF_Filter* reframer_filter = gf_fs_load_filter(session, "rfav1", &gf_err); + gf_filter_set_source(reframer_filter, src_audio, NULL); + gf_filter_set_source(dest_filter, reframer_filter, NULL); + + gf_err = gf_fs_run(session); + + if (gf_err >= GF_OK) { + gf_err = gf_fs_get_last_connect_error(session); + if (gf_err >= GF_OK) { + gf_err = gf_fs_get_last_process_error(session); + } + } + + gf_fs_del(session); + if (gf_err != GF_OK) { - LOG_ERROR(0, "IAMF Muxing: Failed to load reframer filter."); - gf_fs_del(session); + LOG_ERROR(0, "IAMF Audio Muxing: Failed with error: " + + std::string(gf_error_to_string(gf_err))); + return false; + } + return true; +} + +// Writes video to an existing MP4 file using GPAC ISO media APIs +static bool muxVideo(const juce::String& inputVideoFile, + const juce::String& outputMuxedFile) { +#ifdef DEBUG + gf_log_set_tool_level(GF_LOG_CORE, GF_LOG_INFO); + gf_log_set_tool_level(GF_LOG_CONTAINER, GF_LOG_INFO); +#endif + + GF_Err gf_err = GF_OK; + + // Open input video file for reading + GF_ISOFile* src_video = + gf_isom_open(inputVideoFile.toRawUTF8(), GF_ISOM_OPEN_READ, NULL); + if (!src_video) { + LOG_ERROR(0, "Video Muxing: Failed to open input video file " + + inputVideoFile.toStdString() + " for reading."); return false; } - // Filter for muxing audio and video. - GF_Filter* mux_filter = gf_fs_load_filter(session, "mp4mx", &gf_err); + // Get the first video track from the source file + u32 src_track_count = gf_isom_get_track_count(src_video); + u32 src_video_track = 0; + for (u32 i = 1; i <= src_track_count; i++) { + u32 media_type = gf_isom_get_media_type(src_video, i); + if (media_type == GF_ISOM_MEDIA_VISUAL) { + src_video_track = i; + break; + } + } + + if (src_video_track == 0) { + LOG_ERROR(0, "Video Muxing: No video track found in input file."); + gf_isom_close(src_video); + return false; + } + + // Open the existing MP4 file (with audio) for editing + GF_ISOFile* dst_file = + gf_isom_open(outputMuxedFile.toRawUTF8(), GF_ISOM_OPEN_EDIT, NULL); + if (!dst_file) { + LOG_ERROR(0, "Video Muxing: Failed to open output file " + + outputMuxedFile.toStdString() + " for editing."); + gf_isom_close(src_video); + return false; + } + + // Get track count before adding video for verification later + u32 tracks_before = gf_isom_get_track_count(dst_file); + + // Clone the video track structure (this only clones metadata, not samples) + u32 dst_track = 0; + gf_err = gf_isom_clone_track(src_video, src_video_track, dst_file, + GF_ISOTrackCloneFlags(0), &dst_track); if (gf_err != GF_OK) { - LOG_ERROR(0, "IAMF Muxing: Failed to load muxing filter."); - gf_fs_del(session); + LOG_ERROR(0, "Video Muxing: Failed to clone video track: " + + std::string(gf_error_to_string(gf_err))); + gf_isom_close(dst_file); + gf_isom_close(src_video); return false; } - // Filter for removing audio from video - GF_Filter* audio_remover = - gf_fs_load_filter(session, "mp4dmx:tkid=video", &gf_err); + // Now manually copy all samples from the source track to the destination + // track + u32 sample_count = gf_isom_get_sample_count(src_video, src_video_track); + for (u32 i = 1; i <= sample_count; i++) { + u32 sample_desc_index = 0; + GF_ISOSample* sample = + gf_isom_get_sample(src_video, src_video_track, i, &sample_desc_index); + if (!sample) { + LOG_ERROR(0, "Video Muxing: Failed to get sample " + std::to_string(i) + + " from source track"); + gf_isom_close(dst_file); + gf_isom_close(src_video); + return false; + } + + // Add the sample to the destination track + gf_err = gf_isom_add_sample(dst_file, dst_track, sample_desc_index, sample); + gf_isom_sample_del(&sample); + + if (gf_err != GF_OK) { + LOG_ERROR(0, "Video Muxing: Failed to add sample " + std::to_string(i) + + " to destination track: " + + std::string(gf_error_to_string(gf_err))); + gf_isom_close(dst_file); + gf_isom_close(src_video); + return false; + } + } + + // Close source file + gf_isom_close(src_video); + + // Set storage mode to interleaved to mimic CLI behaviour + gf_err = gf_isom_set_storage_mode(dst_file, GF_ISOM_STORE_INTERLEAVED); if (gf_err != GF_OK) { - LOG_ERROR(0, "IAMF Muxing: Failed to load audio remover filter."); - gf_fs_del(session); + LOG_ERROR(0, "Video Muxing: Failed to set storage mode: " + + std::string(gf_error_to_string(gf_err))); + gf_isom_close(dst_file); return false; } - // Pass the video file through the audio removal filter before muxing - gf_filter_set_source(audio_remover, src_video, NULL); - gf_filter_set_source(mux_filter, audio_remover, NULL); - gf_filter_set_source(reframer_filter, src_audio, NULL); - gf_filter_set_source(mux_filter, reframer_filter, NULL); - gf_filter_set_source(dest_filter, mux_filter, NULL); + // Set final output filename (required for edit mode) + // Create a temporary filename for the edited output + juce::String tempOutputFile = outputMuxedFile + ".muxed"; + gf_err = gf_isom_set_final_name( + dst_file, const_cast(tempOutputFile.toRawUTF8())); + if (gf_err != GF_OK) { + LOG_ERROR(0, "Video Muxing: Failed to set final name: " + + std::string(gf_error_to_string(gf_err))); + gf_isom_close(dst_file); + return false; + } - gf_err = gf_fs_run(session); + // Close destination file (this writes the changes to the new filename) + gf_err = gf_isom_close(dst_file); + if (gf_err != GF_OK) { + LOG_ERROR(0, "Video Muxing: Failed to close output file: " + + std::string(gf_error_to_string(gf_err))); + return false; + } - if (gf_err >= GF_OK) { - gf_err = gf_fs_get_last_connect_error(session); - if (gf_err >= GF_OK) { - gf_err = gf_fs_get_last_process_error(session); - } + // Replace the original file with the new file + std::remove(outputMuxedFile.toRawUTF8()); + if (std::rename(tempOutputFile.toRawUTF8(), outputMuxedFile.toRawUTF8()) != + 0) { + LOG_ERROR(0, "Video Muxing: Failed to rename output file, error: " + + std::string(strerror(errno))); + return false; } - gf_fs_del(session); + // Verify by reopening and checking track count + GF_ISOFile* verify_file = + gf_isom_open(outputMuxedFile.toRawUTF8(), GF_ISOM_OPEN_READ, NULL); + if (!verify_file) { + LOG_ERROR(0, "Video Muxing: Failed to reopen file for verification."); + return false; + } + + u32 tracks_after = gf_isom_get_track_count(verify_file); + gf_isom_close(verify_file); + + if (tracks_after != tracks_before + 1) { + LOG_ERROR(0, "Video Muxing: Track count verification failed. Expected " + + std::to_string(tracks_before + 1) + " tracks, got " + + std::to_string(tracks_after)); + return false; + } + + return true; +} + +bool muxIAMF(const FileExport& exportData) { + const juce::String inputAudioFile = exportData.getExportFile(); + const juce::String inputVideoFile = exportData.getVideoSource(); + const juce::String outputMuxdFile = exportData.getVideoExportFolder(); + + if (!validateMuxingPaths(inputAudioFile, inputVideoFile, outputMuxdFile)) { + LOG_ERROR(0, "IAMF Muxing: One or more file paths are invalid."); + return false; + } + + // Initialize GPAC library before creating session + GF_Err init_err = gf_sys_init(GF_MemTrackerNone, NULL); + if (init_err != GF_OK) { + LOG_ERROR(0, "IAMF Muxing: Failed to initialize GPAC system."); + return false; + } + + if (!muxIAMFAudio(inputAudioFile, outputMuxdFile)) { + LOG_ERROR(0, "IAMF Muxing: Failed to mux IAMF audio into MP4."); + gf_sys_close(); + return false; + } + if (!muxVideo(inputVideoFile, outputMuxdFile)) { + LOG_ERROR(0, "IAMF Muxing: Failed to mux video into MP4."); + gf_sys_close(); + return false; + } + + gf_sys_close(); return true; } } // namespace IAMFExportHelper \ No newline at end of file diff --git a/common/processors/file_output/iamf_export_utils/IAMFExportUtil.h b/common/processors/file_output/iamf_export_utils/IAMFExportUtil.h index 6d948b21..6c909a53 100644 --- a/common/processors/file_output/iamf_export_utils/IAMFExportUtil.h +++ b/common/processors/file_output/iamf_export_utils/IAMFExportUtil.h @@ -17,8 +17,6 @@ #pragma once #include -#include "data_repository/implementation/AudioElementRepository.h" -#include "data_repository/implementation/MixPresentationRepository.h" #include "data_structures/src/FileExport.h" #include "user_metadata.pb.h" @@ -34,7 +32,5 @@ void writeFLACConfigMD(const int samplesPerBlock, const int samplesProcessed, iamf_tools_cli_proto::UserMetadata& user_metadata); void writeOPUSConfigMD(const int sampleRate, const int bitratePerChannel, iamf_tools_cli_proto::UserMetadata& user_metadata); -bool muxIAMF(const AudioElementRepository& aeRepo, - const MixPresentationRepository& mpRepo, - const FileExport& exportData); +bool muxIAMF(const FileExport& exportData); } // namespace IAMFExportHelper \ No newline at end of file diff --git a/common/processors/tests/FileOutputTestFixture.h b/common/processors/tests/FileOutputTestFixture.h index dfd0e63b..c82526c6 100644 --- a/common/processors/tests/FileOutputTestFixture.h +++ b/common/processors/tests/FileOutputTestFixture.h @@ -214,7 +214,7 @@ class FileOutputTests : public ::testing::Test { .append("processors") .append("tests") .append("test_resources") - .append("SilentSampleVideo.mp4")) + .append("SilentSampleVideo_h264.mp4")) .string()); iamfOutPath = std::filesystem::current_path() / "test.iamf"; videoOutPath = std::filesystem::current_path() / "test.mp4"; @@ -309,6 +309,14 @@ class FileOutputTests : public ::testing::Test { std::optional profile = std::nullopt; int sampleRate = kSampleRate; bool exportVideo = false; + std::string videoSource = (std::filesystem::current_path() + .parent_path() + .append("common") + .append("processors") + .append("tests") + .append("test_resources") + .append("SilentSampleVideo_h264.mp4")) + .string(); }; void setTestExportOpts(const ExportTestOpts opts) { @@ -319,19 +327,21 @@ class FileOutputTests : public ::testing::Test { ex.setAudioCodec(opts.codec); ex.setSampleRate(opts.sampleRate); ex.setExportVideo(opts.exportVideo); + ex.setVideoSource(opts.videoSource); fileExportRepository.update(ex); } // Constants - std::vector kAudioElementLayouts = { - Speakers::kMono, Speakers::kStereo, - Speakers::k5Point1, Speakers::k5Point1Point2, - Speakers::k5Point1Point4, Speakers::k7Point1, - Speakers::k7Point1Point2, Speakers::k7Point1Point4, - Speakers::k3Point1Point2, Speakers::kBinaural, - Speakers::kHOA1, Speakers::kHOA2, - Speakers::kHOA3, - }; + const std::vector kAudioElementLayouts = + { + Speakers::kMono, Speakers::kStereo, + Speakers::k5Point1, Speakers::k5Point1Point2, + Speakers::k5Point1Point4, Speakers::k7Point1, + Speakers::k7Point1Point2, Speakers::k7Point1Point4, + Speakers::k3Point1Point2, Speakers::kBinaural, + Speakers::kHOA1, Speakers::kHOA2, + Speakers::kHOA3, + }; std::vector kAudioElementExpandedLayouts = { Speakers::kExplLFE, @@ -349,6 +359,15 @@ class FileOutputTests : public ::testing::Test { Speakers::kExpl9Point1Point6Top, }; + const std::filesystem::path kTestSourceVideo = + std::filesystem::current_path().parent_path() / + "common/processors/tests/test_resources/SilentSampleVideo"; + + const std::vector kTestSourceVideoCodecs = {"h264", "h265", + "av1"}; + + const std::vector kTestSourceVideoContainers = {".mp4", ".mov"}; + // Repositories juce::ValueTree testState{"test_state"}; FileExportRepository fileExportRepository{ diff --git a/common/processors/tests/FileOutputTestUtils.h b/common/processors/tests/FileOutputTestUtils.h index 13c91a5c..6912b2ff 100644 --- a/common/processors/tests/FileOutputTestUtils.h +++ b/common/processors/tests/FileOutputTestUtils.h @@ -408,3 +408,94 @@ class WavFileWriter { std::unique_ptr wavFormat_; std::unique_ptr writer_; }; + +// Helper function to execute a command and capture its output +static std::pair executeCommand( + const juce::String& executable, const juce::StringArray& arguments) { + juce::ChildProcess process; + + // Combine executable and arguments into a single StringArray + juce::StringArray args; + args.add(executable); + args.addArray(arguments); + + if (!process.start(args)) { + return {-1, ""}; + } + + juce::String output = process.readAllProcessOutput(); + int exitCode = process.getExitCode(); + return {exitCode, output}; +} + +// Helper function to check for general file errors using ffmpeg +static bool checkForFFmpegErrors(const juce::String& path) { + juce::StringArray ffmpegArgs; + ffmpegArgs.add("-v"); + ffmpegArgs.add("error"); + ffmpegArgs.add("-i"); + ffmpegArgs.add(path); + ffmpegArgs.add("-f"); + ffmpegArgs.add("null"); +#ifdef _WIN32 + ffmpegArgs.add("NUL"); +#else + ffmpegArgs.add("-"); +#endif + + auto [exitCode, output] = executeCommand("ffmpeg", ffmpegArgs); + if (exitCode < 0) { + LOG_INFO(0, "Failed to execute ffmpeg command"); + return false; + } + if (!output.isEmpty() || exitCode != 0) { + LOG_INFO(0, "FFmpeg validation errors:"); + // Only print the first 1000 characters to avoid excessive output + if (output.length() > 1000) { + output = output.substring(0, 1000) + "... (truncated)"; + } + LOG_INFO(0, std::string(output.toRawUTF8())); + return false; + } + return true; +} + +// Helper function to verify expected streams are present using ffprobe +static bool verifyIamfStreamsPresent(const juce::String& path) { + juce::StringArray ffprobeArgs; + ffprobeArgs.add(path); + + auto [exitCode, output] = executeCommand("ffprobe", ffprobeArgs); + + if (!output.contains("IAMF Audio Element")) { + LOG_INFO(0, "FFmpeg validation: IAMF Audio Element stream group not found"); + return false; + } + + if (!output.contains("IAMF Mix Presentation")) { + LOG_INFO(0, + "FFmpeg validation: IAMF Mix Presentation stream group not " + "found"); + return false; + } + + if (!output.contains("Video:")) { + LOG_INFO(0, "FFmpeg validation: Video stream not found"); + return false; + } + + return true; +} + +// Checks for errors in the muxed file using ffmpeg and verifies presence of an +// IAMF audio stream and video stream +inline bool validateMuxFFmpeg(const juce::String& path) { + if (!checkForFFmpegErrors(path)) { + return false; + } + if (!verifyIamfStreamsPresent(path)) { + return false; + } + + return true; +} diff --git a/common/processors/tests/MP4IAMFDemuxer_test.cpp b/common/processors/tests/MP4IAMFDemuxer_test.cpp index a6effab7..a8de5998 100644 --- a/common/processors/tests/MP4IAMFDemuxer_test.cpp +++ b/common/processors/tests/MP4IAMFDemuxer_test.cpp @@ -17,6 +17,7 @@ #include #include "FileOutputTestFixture.h" +#include "data_structures/src/FileExport.h" #include "processors/tests/FileOutputTestUtils.h" #include "substream_rdr/substream_rdr_utils/Speakers.h" @@ -24,69 +25,132 @@ using Layout = Speakers::AudioElementSpeakerLayout; class MP4IAMFDemuxerTest : public FileOutputTests { public: - MP4IAMFDemuxerTest() : demuxer() {} + MP4IAMFDemuxerTest() : demuxer() { muxSources = genMuxSources(); } + + protected: + bool validateMuxedFile(const juce::String& path) { +#ifdef ECLIPSA_FFMPEG_AVAILABLE + return validateMuxFFmpeg(path); +#else + return true; +#endif + } + + std::vector genMuxSources() { + std::vector sources; + for (const auto& codec : kTestSourceVideoCodecs) { + for (const auto& container : kTestSourceVideoContainers) { + const std::filesystem::path kNewSource = + kTestSourceVideo.string() + "_" + codec + container; + // We only add the source if the file exists. Some combinations cannot + // exist (e.g., av1 in .mov) + if (std::filesystem::exists(kNewSource)) { + sources.push_back(kNewSource); + } + } + } + return sources; + } MP4IAMFDemuxer demuxer; + std::vector muxSources; }; TEST_F(MP4IAMFDemuxerTest, mux_demux_iamf_1ae_cb) { - const juce::Uuid kAE = addAudioElement(Speakers::kStereo); - const juce::Uuid kMP = addMixPresentation(); - addAudioElementsToMix(kMP, {kAE}); - - setTestExportOpts({.codec = AudioCodec::LPCM, .exportVideo = true}); - - ASSERT_FALSE(std::filesystem::exists(iamfOutPath)); - ASSERT_FALSE(std::filesystem::exists(videoOutPath)); - - bounceAudio(fio_proc, audioElementRepository, ex.getSampleRate()); - - ASSERT_TRUE(std::filesystem::exists(iamfOutPath)); - ASSERT_TRUE(std::filesystem::exists(videoOutPath)); - - EXPECT_TRUE(demuxer.verifyIAMFIntegrity( - videoOutPath.string(), iamfOutPath.string(), ex.getSampleRate(), 16, - SOUND_SYSTEM_A, 0.01f)); + for (const auto& source : muxSources) { + for (const auto layout : kAudioElementLayouts) { + const juce::Uuid kAE = addAudioElement(layout); + const juce::Uuid kMP = addMixPresentation(); + addAudioElementsToMix(kMP, {kAE}); + + setTestExportOpts({.codec = AudioCodec::LPCM, + .exportVideo = true, + .videoSource = source.string()}); + + ASSERT_FALSE(std::filesystem::exists(iamfOutPath)); + ASSERT_FALSE(std::filesystem::exists(videoOutPath)); + + bounceAudio(fio_proc, audioElementRepository, ex.getSampleRate()); + + ASSERT_TRUE(std::filesystem::exists(iamfOutPath)); + ASSERT_TRUE(std::filesystem::exists(videoOutPath)); + ASSERT_TRUE(demuxer.verifyIAMFIntegrity( + videoOutPath.string(), iamfOutPath.string(), kSampleRate, 16)); + ASSERT_TRUE(validateMuxedFile(videoOutPath.string())) + << "Muxing validation failed for muxing source: " << source.string() + << ", layout: " << layout.toString(); + + std::filesystem::remove(iamfOutPath); + std::filesystem::remove(videoOutPath); + audioElementRepository.clear(); + mixRepository.clear(); + } + } } -TEST_F(MP4IAMFDemuxerTest, mux_demux_iamf_1ae_sb) { - const juce::Uuid kAE = addAudioElement(Speakers::kHOA1); - const juce::Uuid kMP = addMixPresentation(); - addAudioElementsToMix(kMP, {kAE}); - - setTestExportOpts({.codec = AudioCodec::LPCM, .exportVideo = true}); - - bounceAudio(fio_proc, audioElementRepository, ex.getSampleRate()); - - ASSERT_TRUE(std::filesystem::exists(iamfOutPath)); - ASSERT_TRUE(std::filesystem::exists(videoOutPath)); - - EXPECT_TRUE(demuxer.verifyIAMFIntegrity( - videoOutPath.string(), iamfOutPath.string(), ex.getSampleRate(), 16, - SOUND_SYSTEM_A, 0.01f)); +TEST_F(MP4IAMFDemuxerTest, mux_demux_iamf_1ae_2mp) { + for (const auto& source : muxSources) { + for (const auto layout : kAudioElementLayouts) { + const juce::Uuid kAE = addAudioElement(layout); + const juce::Uuid kMP = addMixPresentation(); + const juce::Uuid kMP2 = addMixPresentation("Second Mix"); + addAudioElementsToMix(kMP, {kAE}); + addAudioElementsToMix(kMP2, {kAE}); + + setTestExportOpts({.codec = AudioCodec::LPCM, + .exportVideo = true, + .videoSource = source.string()}); + + ASSERT_FALSE(std::filesystem::exists(iamfOutPath)); + ASSERT_FALSE(std::filesystem::exists(videoOutPath)); + + bounceAudio(fio_proc, audioElementRepository, ex.getSampleRate()); + + ASSERT_TRUE(std::filesystem::exists(iamfOutPath)); + ASSERT_TRUE(std::filesystem::exists(videoOutPath)); + ASSERT_TRUE(demuxer.verifyIAMFIntegrity( + videoOutPath.string(), iamfOutPath.string(), kSampleRate, 16)); + ASSERT_TRUE(validateMuxedFile(videoOutPath.string())) + << "Muxing validation failed for muxing source: " << source.string() + << ", layout: " << layout.toString(); + std::filesystem::remove(iamfOutPath); + std::filesystem::remove(videoOutPath); + audioElementRepository.clear(); + mixRepository.clear(); + } + } } TEST_F(MP4IAMFDemuxerTest, mux_demux_iamf_2ae_cb) { - const juce::Uuid kAE1 = addAudioElement(Speakers::kStereo); - const juce::Uuid kAE2 = addAudioElement(Speakers::kExpl9Point1Point6Side); - const juce::Uuid kMP = addMixPresentation(); - addAudioElementsToMix(kMP, {kAE1, kAE2}); + for (const auto& source : muxSources) { + const juce::Uuid kAE1 = addAudioElement(Speakers::kStereo); + const juce::Uuid kAE2 = addAudioElement(Speakers::kExpl9Point1Point6Side); + const juce::Uuid kMP = addMixPresentation(); + addAudioElementsToMix(kMP, {kAE1, kAE2}); - setTestExportOpts({.codec = AudioCodec::LPCM, - .profile = FileProfile::BASE_ENHANCED, - .exportVideo = true}); + setTestExportOpts({.codec = AudioCodec::LPCM, + .profile = FileProfile::BASE_ENHANCED, + .exportVideo = true, + .videoSource = source.string()}); - ASSERT_FALSE(std::filesystem::exists(iamfOutPath)); - ASSERT_FALSE(std::filesystem::exists(videoOutPath)); + ASSERT_FALSE(std::filesystem::exists(iamfOutPath)); + ASSERT_FALSE(std::filesystem::exists(videoOutPath)); - bounceAudio(fio_proc, audioElementRepository, ex.getSampleRate()); + bounceAudio(fio_proc, audioElementRepository, ex.getSampleRate()); - ASSERT_TRUE(std::filesystem::exists(iamfOutPath)); - ASSERT_TRUE(std::filesystem::exists(videoOutPath)); + ASSERT_TRUE(std::filesystem::exists(iamfOutPath)); + ASSERT_TRUE(std::filesystem::exists(videoOutPath)); - EXPECT_TRUE(demuxer.verifyIAMFIntegrity( - videoOutPath.string(), iamfOutPath.string(), ex.getSampleRate(), 16, - SOUND_SYSTEM_A, 0.01f)); + EXPECT_TRUE(demuxer.verifyIAMFIntegrity( + videoOutPath.string(), iamfOutPath.string(), ex.getSampleRate(), 16, + SOUND_SYSTEM_A, 0.01f)); + ASSERT_TRUE(validateMuxedFile(videoOutPath.string())) + << "Muxing validation failed for muxing source: " << source.string() + << ", layout: " << Speakers::kStereo.toString() << " + " + << Speakers::kExpl9Point1Point6Side.toString(); + std::filesystem::remove(iamfOutPath); + std::filesystem::remove(videoOutPath); + } } TEST_F(MP4IAMFDemuxerTest, e2e_iamf_1ae_cb) { @@ -118,21 +182,32 @@ TEST_F(MP4IAMFDemuxerTest, e2e_iamf_1ae_sb) { } TEST_F(MP4IAMFDemuxerTest, e2e_iamf_2ae_cb) { - const juce::Uuid kAE1 = addAudioElement(Speakers::kStereo); - const juce::Uuid kAE2 = addAudioElement(Speakers::kExplLFE); - const juce::Uuid kMP = addMixPresentation(); + for (const auto& source : muxSources) { + const juce::Uuid kAE1 = addAudioElement(Speakers::kStereo); + const juce::Uuid kAE2 = addAudioElement(Speakers::kExplLFE); + const juce::Uuid kMP = addMixPresentation(); - addAudioElementsToMix(kMP, {kAE1, kAE2}); + addAudioElementsToMix(kMP, {kAE1, kAE2}); - setTestExportOpts({.codec = AudioCodec::LPCM, - .profile = FileProfile::BASE_ENHANCED, - .exportVideo = true}); + setTestExportOpts({.codec = AudioCodec::LPCM, + .exportVideo = true, + .videoSource = source.string(), + .profile = FileProfile::BASE_ENHANCED}); - bounceAudio(fio_proc, audioElementRepository, ex.getSampleRate()); + ASSERT_FALSE(std::filesystem::exists(iamfOutPath)); + ASSERT_FALSE(std::filesystem::exists(videoOutPath)); - EXPECT_TRUE(demuxer.verifyIAMFIntegrity( - videoOutPath.string(), iamfOutPath.string(), ex.getSampleRate(), 16, - SOUND_SYSTEM_A, 0.01f)); + bounceAudio(fio_proc, audioElementRepository, ex.getSampleRate()); + + ASSERT_TRUE(std::filesystem::exists(iamfOutPath)); + ASSERT_TRUE(std::filesystem::exists(videoOutPath)); + + EXPECT_TRUE(demuxer.verifyIAMFIntegrity( + videoOutPath.string(), iamfOutPath.string(), ex.getSampleRate(), 16, + SOUND_SYSTEM_A, 0.01f)); + std::filesystem::remove(iamfOutPath); + std::filesystem::remove(videoOutPath); + } } TEST_F(MP4IAMFDemuxerTest, e2e_iamf_all_layouts) { @@ -169,72 +244,90 @@ TEST_F(MP4IAMFDemuxerTest, e2e_iamf_all_layouts) { } TEST_F(MP4IAMFDemuxerTest, e2e_iamf_codecs) { - const juce::Uuid kAE = addAudioElement(Speakers::kStereo); - const juce::Uuid kMP = addMixPresentation(); - addAudioElementsToMix(kMP, {kAE}); + for (const auto& source : muxSources) { + const juce::Uuid kAE = addAudioElement(Speakers::kStereo); + const juce::Uuid kMP = addMixPresentation(); + addAudioElementsToMix(kMP, {kAE}); - for (const AudioCodec codec : { - AudioCodec::LPCM, - AudioCodec::FLAC, - AudioCodec::OPUS, - }) { - setTestExportOpts( - {.codec = codec, .sampleRate = (int)48e3, .exportVideo = true}); + for (const AudioCodec codec : { + AudioCodec::LPCM, + AudioCodec::FLAC, + AudioCodec::OPUS, + }) { + setTestExportOpts({.codec = codec, + .exportVideo = true, + .videoSource = source.string(), + .sampleRate = (int)48e3}); - bounceAudio(fio_proc, audioElementRepository, ex.getSampleRate()); + ASSERT_FALSE(std::filesystem::exists(iamfOutPath)); + ASSERT_FALSE(std::filesystem::exists(videoOutPath)); - ASSERT_TRUE(std::filesystem::exists(iamfOutPath)); - ASSERT_TRUE(std::filesystem::exists(videoOutPath)); + bounceAudio(fio_proc, audioElementRepository, ex.getSampleRate()); - EXPECT_TRUE(demuxer.verifyIAMFIntegrity( - videoOutPath.string(), iamfOutPath.string(), ex.getSampleRate(), 16, - SOUND_SYSTEM_A, 0.01f)); + ASSERT_TRUE(std::filesystem::exists(iamfOutPath)); + ASSERT_TRUE(std::filesystem::exists(videoOutPath)); - std::filesystem::remove(iamfOutPath); - std::filesystem::remove(videoOutPath); + EXPECT_TRUE(demuxer.verifyIAMFIntegrity( + videoOutPath.string(), iamfOutPath.string(), ex.getSampleRate(), 16, + SOUND_SYSTEM_A, 0.01f)); + + std::filesystem::remove(iamfOutPath); + std::filesystem::remove(videoOutPath); + } } } TEST_F(MP4IAMFDemuxerTest, e2e_iamf_bit_depths) { - const juce::Uuid kAE = addAudioElement(Speakers::kStereo); - const juce::Uuid kMP = addMixPresentation(); - addAudioElementsToMix(kMP, {kAE}); + for (const auto& source : muxSources) { + const juce::Uuid kAE = addAudioElement(Speakers::kStereo); + const juce::Uuid kMP = addMixPresentation(); + addAudioElementsToMix(kMP, {kAE}); - setTestExportOpts({.codec = AudioCodec::LPCM, .exportVideo = true}); + setTestExportOpts({.codec = AudioCodec::LPCM, + .exportVideo = true, + .videoSource = source.string()}); - for (int bitDepth : {16, 24, 32}) { - bounceAudio(fio_proc, audioElementRepository, ex.getSampleRate()); + for (int bitDepth : {16, 24, 32}) { + bounceAudio(fio_proc, audioElementRepository, ex.getSampleRate()); - ASSERT_TRUE(std::filesystem::exists(iamfOutPath)); - ASSERT_TRUE(std::filesystem::exists(videoOutPath)); + ASSERT_TRUE(std::filesystem::exists(iamfOutPath)); + ASSERT_TRUE(std::filesystem::exists(videoOutPath)); - EXPECT_TRUE(demuxer.verifyIAMFIntegrity( - videoOutPath.string(), iamfOutPath.string(), ex.getSampleRate(), - bitDepth, SOUND_SYSTEM_A, 0.01f)); + EXPECT_TRUE(demuxer.verifyIAMFIntegrity( + videoOutPath.string(), iamfOutPath.string(), ex.getSampleRate(), + bitDepth, SOUND_SYSTEM_A, 0.01f)); - std::filesystem::remove(iamfOutPath); - std::filesystem::remove(videoOutPath); + std::filesystem::remove(iamfOutPath); + std::filesystem::remove(videoOutPath); + } } } TEST_F(MP4IAMFDemuxerTest, e2e_iamf_sample_rates) { - const juce::Uuid kAE = addAudioElement(Speakers::kStereo); - const juce::Uuid kMP = addMixPresentation(); - addAudioElementsToMix(kMP, {kAE}); - for (int sr : {44100, 48000, 96000}) { - setTestExportOpts( - {.codec = AudioCodec::LPCM, .sampleRate = sr, .exportVideo = true}); + for (const auto& source : muxSources) { + const juce::Uuid kAE = addAudioElement(Speakers::kStereo); + const juce::Uuid kMP = addMixPresentation(); + addAudioElementsToMix(kMP, {kAE}); + for (int sr : {44100, 48000, 96000}) { + setTestExportOpts({.codec = AudioCodec::LPCM, + .sampleRate = sr, + .videoSource = source.string(), + .exportVideo = true}); - bounceAudio(fio_proc, audioElementRepository, sr); + ASSERT_FALSE(std::filesystem::exists(iamfOutPath)); + ASSERT_FALSE(std::filesystem::exists(videoOutPath)); - ASSERT_TRUE(std::filesystem::exists(iamfOutPath)); - ASSERT_TRUE(std::filesystem::exists(videoOutPath)); + bounceAudio(fio_proc, audioElementRepository, sr); - EXPECT_TRUE(demuxer.verifyIAMFIntegrity(videoOutPath.string(), - iamfOutPath.string(), sr, 16, - SOUND_SYSTEM_A, 0.01f)); + ASSERT_TRUE(std::filesystem::exists(iamfOutPath)); + ASSERT_TRUE(std::filesystem::exists(videoOutPath)); - std::filesystem::remove(iamfOutPath); - std::filesystem::remove(videoOutPath); + EXPECT_TRUE(demuxer.verifyIAMFIntegrity(videoOutPath.string(), + iamfOutPath.string(), sr, 16, + SOUND_SYSTEM_A, 0.01f)); + + std::filesystem::remove(iamfOutPath); + std::filesystem::remove(videoOutPath); + } } } \ No newline at end of file diff --git a/common/processors/tests/test_resources/SilentSampleVideo_av1.mp4 b/common/processors/tests/test_resources/SilentSampleVideo_av1.mp4 new file mode 100644 index 00000000..1592aef3 Binary files /dev/null and b/common/processors/tests/test_resources/SilentSampleVideo_av1.mp4 differ diff --git a/common/processors/tests/test_resources/SilentSampleVideo_h264.mov b/common/processors/tests/test_resources/SilentSampleVideo_h264.mov new file mode 100644 index 00000000..dd9d0ffa Binary files /dev/null and b/common/processors/tests/test_resources/SilentSampleVideo_h264.mov differ diff --git a/common/processors/tests/test_resources/SilentSampleVideo.mp4 b/common/processors/tests/test_resources/SilentSampleVideo_h264.mp4 similarity index 100% rename from common/processors/tests/test_resources/SilentSampleVideo.mp4 rename to common/processors/tests/test_resources/SilentSampleVideo_h264.mp4 diff --git a/common/processors/tests/test_resources/SilentSampleVideo_h265.mov b/common/processors/tests/test_resources/SilentSampleVideo_h265.mov new file mode 100644 index 00000000..9326d325 Binary files /dev/null and b/common/processors/tests/test_resources/SilentSampleVideo_h265.mov differ diff --git a/common/processors/tests/test_resources/SilentSampleVideo_h265.mp4 b/common/processors/tests/test_resources/SilentSampleVideo_h265.mp4 new file mode 100644 index 00000000..fd26640b Binary files /dev/null and b/common/processors/tests/test_resources/SilentSampleVideo_h265.mp4 differ diff --git a/scripts/package.sh b/scripts/package.sh index edc0e1d6..e2eac247 100755 --- a/scripts/package.sh +++ b/scripts/package.sh @@ -485,6 +485,7 @@ copy_dylibs_to_plugin_resources() { "$build_dir/_deps/zeromq-build/lib/libzmq.5.2.6.dylib:Resources/" "$build_dir/third_party/iamftools/lib/libiamf_tools.dylib:Resources/third_party/iamftools/lib/" "$build_dir/third_party/obr/lib/obr.dylib:Resources/third_party/obr/lib/" + "$build_dir/third_party/gpac/lib/libgpac.dylib:Resources/third_party/gpac/lib/" ) log "Copying dylibs to $plugin_type plugin resources" @@ -495,6 +496,7 @@ copy_dylibs_to_plugin_resources() { # Create resources directory structure run_cmd "sudo mkdir -p \"$resources_path/third_party/iamftools/lib\"" run_cmd "sudo mkdir -p \"$resources_path/third_party/obr/lib\"" + run_cmd "sudo mkdir -p \"$resources_path/third_party/gpac/lib\"" # Copy each dylib if it exists for dylib_entry in "${dylib_sources[@]}"; do @@ -553,6 +555,7 @@ update_plugin_rpath() { "@loader_path/../Resources/" "@loader_path/../Resources/third_party/iamftools/lib/" "@loader_path/../Resources/third_party/obr/lib/" + "@loader_path/../Resources/third_party/gpac/lib/" ) # Add each RPATH if it doesn't already exist diff --git a/third_party/gpac/CMakeLists.txt b/third_party/gpac/CMakeLists.txt index 050fe0b7..963a7142 100644 --- a/third_party/gpac/CMakeLists.txt +++ b/third_party/gpac/CMakeLists.txt @@ -52,15 +52,12 @@ if(WIN32) target_link_libraries(gpac INTERFACE gpac_archive ws2_32 shlwapi winmm) else() - # macOS: static library (.a) - add_library(gpac_archive STATIC IMPORTED) - set_target_properties(gpac_archive PROPERTIES - IMPORTED_LOCATION "${CMAKE_CURRENT_LIST_DIR}/lib/libgpac_static.a" - IMPORTED_LOCATION_DEBUG "${CMAKE_CURRENT_LIST_DIR}/lib/libgpac_static.a" - IMPORTED_LOCATION_RELEASE "${CMAKE_CURRENT_LIST_DIR}/lib/libgpac_static.a" - IMPORTED_LOCATION_RELWITHDEBINFO "${CMAKE_CURRENT_LIST_DIR}/lib/libgpac_static.a" + add_library(gpac_shared SHARED IMPORTED) + set_target_properties(gpac_shared PROPERTIES + IMPORTED_LOCATION "${CMAKE_CURRENT_LIST_DIR}/lib/libgpac.dylib" + IMPORTED_LOCATION_DEBUG "${CMAKE_CURRENT_LIST_DIR}/lib/libgpac.dylib" + IMPORTED_LOCATION_RELEASE "${CMAKE_CURRENT_LIST_DIR}/lib/libgpac.dylib" + IMPORTED_LOCATION_RELWITHDEBINFO "${CMAKE_CURRENT_LIST_DIR}/lib/libgpac.dylib" LINKER_LANGUAGE CXX) - - # z-lib is an OSX system library - target_link_libraries(gpac INTERFACE gpac_archive z) + target_link_libraries(gpac INTERFACE gpac_shared) endif() diff --git a/third_party/gpac/README.md b/third_party/gpac/README.md index adf70117..fb7c7f74 100644 --- a/third_party/gpac/README.md +++ b/third_party/gpac/README.md @@ -1,6 +1,4 @@ -- Compiled from: https://github.com/gpac/gpac/tree/945c0e4b1bbf3b0278e94f8623a14e6bb061ea91 -- Commit: https://github.com/gpac/gpac/commit/945c0e4b1bbf3b0278e94f8623a14e6bb061ea91 -- Version: 1.0.0 (main branch - 945c0e4b1bbf3b0278e94f8623a14e6bb061ea91) +- Compiled from: https://github.com/gpac/gpac/tree/4f33ccde09bce9e6d56a56304250fa49893f5019 ### Compile notes on MacOs - Compiled for ARM OSX (Sonoma 14.4.1, clang 15.0.0). diff --git a/third_party/gpac/include/gpac/Remotery.h b/third_party/gpac/include/gpac/Remotery.h deleted file mode 100644 index 6a3568c9..00000000 --- a/third_party/gpac/include/gpac/Remotery.h +++ /dev/null @@ -1,683 +0,0 @@ - - -/* -Copyright 2014-2018 Celtoys Ltd - -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. -*/ - - -/* - -Compiling ---------- - -* Windows (MSVC) - add lib/Remotery.c and lib/Remotery.h to your program. Set include - directories to add Remotery/lib path. The required library ws2_32.lib should be picked - up through the use of the #pragma comment(lib, "ws2_32.lib") directive in Remotery.c. - -* Mac OS X (XCode) - simply add lib/Remotery.c and lib/Remotery.h to your program. - -* Linux (GCC) - add the source in lib folder. Compilation of the code requires -pthreads for - library linkage. For example to compile the same run: cc lib/Remotery.c sample/sample.c - -I lib -pthread -lm - -You can define some extra macros to modify what features are compiled into Remotery. These are -documented just below this comment. - -*/ - - -#ifndef RMT_INCLUDED_H -#define RMT_INCLUDED_H - -//! @cond Doxygen_Suppress - -// Set to 0 to not include any bits of Remotery in your build -#ifndef RMT_ENABLED -#define RMT_ENABLED 1 -#endif - -// Help performance of the server sending data to the client by marking this machine as little-endian -#ifndef RMT_ASSUME_LITTLE_ENDIAN -#define RMT_ASSUME_LITTLE_ENDIAN 0 -#endif - -// Used by the Celtoys TinyCRT library (not released yet) -#ifndef RMT_USE_TINYCRT -#define RMT_USE_TINYCRT 0 -#endif - -// Assuming CUDA headers/libs are setup, allow CUDA profiling -#ifndef RMT_USE_CUDA -#define RMT_USE_CUDA 0 -#endif - -// Assuming Direct3D 11 headers/libs are setup, allow D3D11 profiling -#ifndef RMT_USE_D3D11 -#define RMT_USE_D3D11 0 -#endif - -// Allow OpenGL profiling -#ifndef RMT_USE_OPENGL -#define RMT_USE_OPENGL 0 -#endif - -// Allow Metal profiling -#ifndef RMT_USE_METAL -#define RMT_USE_METAL 0 -#endif - -// Initially use POSIX thread names to name threads instead of Thread0, 1, ... -#ifndef RMT_USE_POSIX_THREADNAMES -#define RMT_USE_POSIX_THREADNAMES 0 -#endif - -// How many times we spin data back and forth between CPU & GPU -// to calculate average RTT (Roundtrip Time). Cannot be 0. -// Affects OpenGL & D3D11 -#ifndef RMT_GPU_CPU_SYNC_NUM_ITERATIONS -#define RMT_GPU_CPU_SYNC_NUM_ITERATIONS 16 -#endif - -// Time in seconds between each resync to compensate for drifting between GPU & CPU timers, -// effects of power saving, etc. Resyncs can cause stutter, lag spikes, stalls. -// Set to 0 for never. -// Affects OpenGL & D3D11 -#ifndef RMT_GPU_CPU_SYNC_SECONDS -#define RMT_GPU_CPU_SYNC_SECONDS 30 -#endif - -// Whether we should automatically resync if we detect a timer disjoint (e.g. -// changed from AC power to battery, GPU is overheating, or throttling up/down -// due to laptop savings events). Set it to 0 to avoid resync in such events. -// Useful if for some odd reason a driver reports a lot of disjoints. -// Affects D3D11 -#ifndef RMT_D3D11_RESYNC_ON_DISJOINT -#define RMT_D3D11_RESYNC_ON_DISJOINT 1 -#endif - - -/* ------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------- - Compiler/Platform Detection and Preprocessor Utilities ------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------- -*/ - - -// Platform identification -#if defined(_WINDOWS) || defined(_WIN32) - #define RMT_PLATFORM_WINDOWS -#elif defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) - #define RMT_PLATFORM_LINUX - #define RMT_PLATFORM_POSIX -#elif defined(__APPLE__) - #define RMT_PLATFORM_MACOS - #define RMT_PLATFORM_POSIX -#endif - -#ifdef RMT_DLL - #if defined (RMT_PLATFORM_WINDOWS) - #if defined (RMT_IMPL) - #define RMT_API __declspec(dllexport) - #else - #define RMT_API __declspec(dllimport) - #endif - #elif defined (RMT_PLATFORM_POSIX) - #if defined (RMT_IMPL) - #define RMT_API __attribute__((visibility("default"))) - #else - #define RMT_API - #endif - #endif -#else - #define RMT_API -#endif - -// Allows macros to be written that can work around the inability to do: #define(x) #ifdef x -// with the C preprocessor. -#if RMT_ENABLED - #define IFDEF_RMT_ENABLED(t, f) t -#else - #define IFDEF_RMT_ENABLED(t, f) f -#endif -#if RMT_ENABLED && RMT_USE_CUDA - #define IFDEF_RMT_USE_CUDA(t, f) t -#else - #define IFDEF_RMT_USE_CUDA(t, f) f -#endif -#if RMT_ENABLED && RMT_USE_D3D11 - #define IFDEF_RMT_USE_D3D11(t, f) t -#else - #define IFDEF_RMT_USE_D3D11(t, f) f -#endif -#if RMT_ENABLED && RMT_USE_OPENGL - #define IFDEF_RMT_USE_OPENGL(t, f) t -#else - #define IFDEF_RMT_USE_OPENGL(t, f) f -#endif -#if RMT_ENABLED && RMT_USE_METAL - #define IFDEF_RMT_USE_METAL(t, f) t -#else - #define IFDEF_RMT_USE_METAL(t, f) f -#endif - - -// Public interface is written in terms of these macros to easily enable/disable itself -#define RMT_OPTIONAL(macro, x) IFDEF_ ## macro(x, ) -#define RMT_OPTIONAL_RET(macro, x, y) IFDEF_ ## macro(x, (y)) - - - -/* ------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------- - Types ------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------- -*/ - - - -// Boolean -typedef unsigned int rmtBool; -#define RMT_TRUE ((rmtBool)1) -#define RMT_FALSE ((rmtBool)0) - - -// Unsigned integer types -typedef unsigned char rmtU8; -typedef unsigned short rmtU16; -typedef unsigned int rmtU32; -typedef unsigned long long rmtU64; - - -// Signed integer types -typedef char rmtS8; -typedef short rmtS16; -typedef int rmtS32; -typedef long long rmtS64; - - -// Const, null-terminated string pointer -typedef const char* rmtPStr; - - -// Handle to the main remotery instance -typedef struct Remotery Remotery; - - -// All possible error codes -typedef enum rmtError -{ - RMT_ERROR_NONE, - RMT_ERROR_RECURSIVE_SAMPLE, // Not an error but an internal message to calling code - - // System errors - RMT_ERROR_MALLOC_FAIL, // Malloc call within remotery failed - RMT_ERROR_TLS_ALLOC_FAIL, // Attempt to allocate thread local storage failed - RMT_ERROR_VIRTUAL_MEMORY_BUFFER_FAIL, // Failed to create a virtual memory mirror buffer - RMT_ERROR_CREATE_THREAD_FAIL, // Failed to create a thread for the server - - // Network TCP/IP socket errors - RMT_ERROR_SOCKET_INIT_NETWORK_FAIL, // Network initialisation failure (e.g. on Win32, WSAStartup fails) - RMT_ERROR_SOCKET_CREATE_FAIL, // Can't create a socket for connection to the remote viewer - RMT_ERROR_SOCKET_BIND_FAIL, // Can't bind a socket for the server - RMT_ERROR_SOCKET_LISTEN_FAIL, // Created server socket failed to enter a listen state - RMT_ERROR_SOCKET_SET_NON_BLOCKING_FAIL, // Created server socket failed to switch to a non-blocking state - RMT_ERROR_SOCKET_INVALID_POLL, // Poll attempt on an invalid socket - RMT_ERROR_SOCKET_SELECT_FAIL, // Server failed to call select on socket - RMT_ERROR_SOCKET_POLL_ERRORS, // Poll notified that the socket has errors - RMT_ERROR_SOCKET_ACCEPT_FAIL, // Server failed to accept connection from client - RMT_ERROR_SOCKET_SEND_TIMEOUT, // Timed out trying to send data - RMT_ERROR_SOCKET_SEND_FAIL, // Unrecoverable error occured while client/server tried to send data - RMT_ERROR_SOCKET_RECV_NO_DATA, // No data available when attempting a receive - RMT_ERROR_SOCKET_RECV_TIMEOUT, // Timed out trying to receive data - RMT_ERROR_SOCKET_RECV_FAILED, // Unrecoverable error occured while client/server tried to receive data - - // WebSocket errors - RMT_ERROR_WEBSOCKET_HANDSHAKE_NOT_GET, // WebSocket server handshake failed, not HTTP GET - RMT_ERROR_WEBSOCKET_HANDSHAKE_NO_VERSION, // WebSocket server handshake failed, can't locate WebSocket version - RMT_ERROR_WEBSOCKET_HANDSHAKE_BAD_VERSION, // WebSocket server handshake failed, unsupported WebSocket version - RMT_ERROR_WEBSOCKET_HANDSHAKE_NO_HOST, // WebSocket server handshake failed, can't locate host - RMT_ERROR_WEBSOCKET_HANDSHAKE_BAD_HOST, // WebSocket server handshake failed, host is not allowed to connect - RMT_ERROR_WEBSOCKET_HANDSHAKE_NO_KEY, // WebSocket server handshake failed, can't locate WebSocket key - RMT_ERROR_WEBSOCKET_HANDSHAKE_BAD_KEY, // WebSocket server handshake failed, WebSocket key is ill-formed - RMT_ERROR_WEBSOCKET_HANDSHAKE_STRING_FAIL, // WebSocket server handshake failed, internal error, bad string code - RMT_ERROR_WEBSOCKET_DISCONNECTED, // WebSocket server received a disconnect request and closed the socket - RMT_ERROR_WEBSOCKET_BAD_FRAME_HEADER, // Couldn't parse WebSocket frame header - RMT_ERROR_WEBSOCKET_BAD_FRAME_HEADER_SIZE, // Partially received wide frame header size - RMT_ERROR_WEBSOCKET_BAD_FRAME_HEADER_MASK, // Partially received frame header data mask - RMT_ERROR_WEBSOCKET_RECEIVE_TIMEOUT, // Timeout receiving frame header - - RMT_ERROR_REMOTERY_NOT_CREATED, // Remotery object has not been created - RMT_ERROR_SEND_ON_INCOMPLETE_PROFILE, // An attempt was made to send an incomplete profile tree to the client - - // CUDA error messages - RMT_ERROR_CUDA_DEINITIALIZED, // This indicates that the CUDA driver is in the process of shutting down - RMT_ERROR_CUDA_NOT_INITIALIZED, // This indicates that the CUDA driver has not been initialized with cuInit() or that initialization has failed - RMT_ERROR_CUDA_INVALID_CONTEXT, // This most frequently indicates that there is no context bound to the current thread - RMT_ERROR_CUDA_INVALID_VALUE, // This indicates that one or more of the parameters passed to the API call is not within an acceptable range of values - RMT_ERROR_CUDA_INVALID_HANDLE, // This indicates that a resource handle passed to the API call was not valid - RMT_ERROR_CUDA_OUT_OF_MEMORY, // The API call failed because it was unable to allocate enough memory to perform the requested operation - RMT_ERROR_ERROR_NOT_READY, // This indicates that a resource handle passed to the API call was not valid - - // Direct3D 11 error messages - RMT_ERROR_D3D11_FAILED_TO_CREATE_QUERY, // Failed to create query for sample - - // OpenGL error messages - RMT_ERROR_OPENGL_ERROR, // Generic OpenGL error, no need to expose detail since app will need an OpenGL error callback registered - - RMT_ERROR_CUDA_UNKNOWN, -} rmtError; - - -typedef enum rmtSampleFlags -{ - // Default behavior - RMTSF_None = 0, - - // Search parent for same-named samples and merge timing instead of adding a new sample - RMTSF_Aggregate = 1, - - // Merge sample with parent if it's the same sample - RMTSF_Recursive = 2, -} rmtSampleFlags; - - -/* ------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------- - Public Interface ------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------- -*/ - - - -// Can call remotery functions on a null pointer -// TODO: Can embed extern "C" in these macros? - -#define rmt_Settings() \ - RMT_OPTIONAL_RET(RMT_ENABLED, _rmt_Settings(), NULL ) - -#define rmt_CreateGlobalInstance(rmt) \ - RMT_OPTIONAL_RET(RMT_ENABLED, _rmt_CreateGlobalInstance(rmt), RMT_ERROR_NONE) - -#define rmt_DestroyGlobalInstance(rmt) \ - RMT_OPTIONAL(RMT_ENABLED, _rmt_DestroyGlobalInstance(rmt)) - -#define rmt_SetGlobalInstance(rmt) \ - RMT_OPTIONAL(RMT_ENABLED, _rmt_SetGlobalInstance(rmt)) - -#define rmt_GetGlobalInstance() \ - RMT_OPTIONAL_RET(RMT_ENABLED, _rmt_GetGlobalInstance(), NULL) - -#define rmt_SetCurrentThreadName(rmt) \ - RMT_OPTIONAL(RMT_ENABLED, _rmt_SetCurrentThreadName(rmt)) - -#define rmt_LogText(text) \ - RMT_OPTIONAL(RMT_ENABLED, _rmt_LogText(text)) - -#define rmt_SendText(text) \ - RMT_OPTIONAL(RMT_ENABLED, _rmt_SendText(text)) - -#define rmt_EnableSampling(enable) \ - RMT_OPTIONAL(RMT_ENABLED, _rmt_EnableSampling(enable)) - -#define rmt_SamplingEnabled() \ - RMT_OPTIONAL(RMT_ENABLED, _rmt_SamplingEnabled()) - -#define rmt_BeginCPUSample(name, flags) \ - RMT_OPTIONAL(RMT_ENABLED, { \ - static rmtU32 rmt_sample_hash_##name = 0; \ - _rmt_BeginCPUSample(#name, flags, &rmt_sample_hash_##name); \ - }) - -#define rmt_BeginCPUSampleStore(name, flags, hashptr) \ - RMT_OPTIONAL(RMT_ENABLED, { \ - _rmt_BeginCPUSample(name, flags, hashptr); \ - }) - -#define rmt_BeginCPUSampleDynamic(namestr, flags) \ - RMT_OPTIONAL(RMT_ENABLED, _rmt_BeginCPUSample(namestr, flags, NULL)) - -#define rmt_EndCPUSample() \ - RMT_OPTIONAL(RMT_ENABLED, _rmt_EndCPUSample()) - - -// Callback function pointer types -typedef void* (*rmtMallocPtr)(void* mm_context, rmtU32 size); -typedef void* (*rmtReallocPtr)(void* mm_context, void* ptr, rmtU32 size); -typedef void (*rmtFreePtr)(void* mm_context, void* ptr); -typedef void (*rmtInputHandlerPtr)(const char* text, void* context); - - -// Struture to fill in to modify Remotery default settings -typedef struct rmtSettings -{ - // Which port to listen for incoming connections on - rmtU16 port; - - // When this server exits it can leave the port open in TIME_WAIT state for - // a while. This forces subsequent server bind attempts to fail when - // restarting. If you find restarts fail repeatedly with bind attempts, set - // this to true to forcibly reuse the open port. - rmtBool reuse_open_port; - - // Only allow connections on localhost? - // For dev builds you may want to access your game from other devices but if - // you distribute a game to your players with Remotery active, probably best - // to limit connections to localhost. - rmtBool limit_connections_to_localhost; - - // How long to sleep between server updates, hopefully trying to give - // a little CPU back to other threads. - rmtU32 msSleepBetweenServerUpdates; - - // Size of the internal message queues Remotery uses - // Will be rounded to page granularity of 64k - rmtU32 messageQueueSizeInBytes; - - // If the user continuously pushes to the message queue, the server network - // code won't get a chance to update unless there's an upper-limit on how - // many messages can be consumed per loop. - rmtU32 maxNbMessagesPerUpdate; - - // Callback pointers for memory allocation - rmtMallocPtr malloc; - rmtReallocPtr realloc; - rmtFreePtr free; - void* mm_context; - - // Callback pointer for receiving input from the Remotery console - rmtInputHandlerPtr input_handler; - - // Context pointer that gets sent to Remotery console callback function - void* input_handler_context; - - rmtPStr logFilename; -} rmtSettings; - - -// Structure to fill in when binding CUDA to Remotery -typedef struct rmtCUDABind -{ - // The main context that all driver functions apply before each call - void* context; - - // Driver API function pointers that need to be pointed to - // Untyped so that the CUDA headers are not required in this file - // NOTE: These are named differently to the CUDA functions because the CUDA API has a habit of using - // macros to point function calls to different versions, e.g. cuEventDestroy is a macro for - // cuEventDestroy_v2. - void* CtxSetCurrent; - void* CtxGetCurrent; - void* EventCreate; - void* EventDestroy; - void* EventRecord; - void* EventQuery; - void* EventElapsedTime; - -} rmtCUDABind; - - -// Call once after you've initialised CUDA to bind it to Remotery -#define rmt_BindCUDA(bind) \ - RMT_OPTIONAL(RMT_USE_CUDA, _rmt_BindCUDA(bind)) - -// Mark the beginning of a CUDA sample on the specified asynchronous stream -#define rmt_BeginCUDASample(name, stream) \ - RMT_OPTIONAL(RMT_USE_CUDA, { \ - static rmtU32 rmt_sample_hash_##name = 0; \ - _rmt_BeginCUDASample(#name, &rmt_sample_hash_##name, stream); \ - }) - -// Mark the end of a CUDA sample on the specified asynchronous stream -#define rmt_EndCUDASample(stream) \ - RMT_OPTIONAL(RMT_USE_CUDA, _rmt_EndCUDASample(stream)) - - -#define rmt_BindD3D11(device, context) \ - RMT_OPTIONAL(RMT_USE_D3D11, _rmt_BindD3D11(device, context)) - -#define rmt_UnbindD3D11() \ - RMT_OPTIONAL(RMT_USE_D3D11, _rmt_UnbindD3D11()) - -#define rmt_BeginD3D11Sample(name) \ - RMT_OPTIONAL(RMT_USE_D3D11, { \ - static rmtU32 rmt_sample_hash_##name = 0; \ - _rmt_BeginD3D11Sample(#name, &rmt_sample_hash_##name); \ - }) - -#define rmt_BeginD3D11SampleDynamic(namestr) \ - RMT_OPTIONAL(RMT_USE_D3D11, _rmt_BeginD3D11Sample(namestr, NULL)) - -#define rmt_EndD3D11Sample() \ - RMT_OPTIONAL(RMT_USE_D3D11, _rmt_EndD3D11Sample()) - - -#define rmt_BindOpenGL() \ - RMT_OPTIONAL(RMT_USE_OPENGL, _rmt_BindOpenGL()) - -#define rmt_UnbindOpenGL() \ - RMT_OPTIONAL(RMT_USE_OPENGL, _rmt_UnbindOpenGL()) - -#define rmt_BeginOpenGLSample(name) \ - RMT_OPTIONAL(RMT_USE_OPENGL, { \ - static rmtU32 rmt_sample_hash_##name = 0; \ - _rmt_BeginOpenGLSample(#name, &rmt_sample_hash_##name); \ - }) - -#define rmt_BeginOpenGLSampleStore(name, hashptr) \ - RMT_OPTIONAL(RMT_USE_OPENGL, { \ - _rmt_BeginOpenGLSample(name, hashptr); \ - }) - -#define rmt_BeginOpenGLSampleDynamic(namestr) \ - RMT_OPTIONAL(RMT_USE_OPENGL, _rmt_BeginOpenGLSample(namestr, NULL)) - -#define rmt_EndOpenGLSample() \ - RMT_OPTIONAL(RMT_USE_OPENGL, _rmt_EndOpenGLSample()) - - -#define rmt_BindMetal(command_buffer) \ - RMT_OPTIONAL(RMT_USE_METAL, _rmt_BindMetal(command_buffer)); - -#define rmt_UnbindMetal() \ - RMT_OPTIONAL(RMT_USE_METAL, _rmt_UnbindMetal()); - -#define rmt_BeginMetalSample(name) \ - RMT_OPTIONAL(RMT_USE_METAL, { \ - static rmtU32 rmt_sample_hash_##name = 0; \ - _rmt_BeginMetalSample(#name, &rmt_sample_hash_##name); \ - }) - -#define rmt_BeginMetalSampleDynamic(namestr) \ - RMT_OPTIONAL(RMT_USE_METAL, _rmt_BeginMetalSample(namestr, NULL)) - -#define rmt_EndMetalSample() \ - RMT_OPTIONAL(RMT_USE_METAL, _rmt_EndMetalSample()) - - - - -/* ------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------- - C++ Public Interface Extensions ------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------- -*/ - - - -#ifdef __cplusplus - - -#if RMT_ENABLED - -// Types that end samples in their destructors -extern "C" RMT_API void _rmt_EndCPUSample(void); -struct rmt_EndCPUSampleOnScopeExit -{ - ~rmt_EndCPUSampleOnScopeExit() - { - _rmt_EndCPUSample(); - } -}; -#if RMT_USE_CUDA -extern "C" RMT_API void _rmt_EndCUDASample(void* stream); -struct rmt_EndCUDASampleOnScopeExit -{ - rmt_EndCUDASampleOnScopeExit(void* stream) : stream(stream) - { - } - ~rmt_EndCUDASampleOnScopeExit() - { - _rmt_EndCUDASample(stream); - } - void* stream; -}; -#endif -#if RMT_USE_D3D11 -extern "C" RMT_API void _rmt_EndD3D11Sample(void); -struct rmt_EndD3D11SampleOnScopeExit -{ - ~rmt_EndD3D11SampleOnScopeExit() - { - _rmt_EndD3D11Sample(); - } -}; -#endif - -#if RMT_USE_OPENGL -extern "C" RMT_API void _rmt_EndOpenGLSample(void); -struct rmt_EndOpenGLSampleOnScopeExit -{ - ~rmt_EndOpenGLSampleOnScopeExit() - { - _rmt_EndOpenGLSample(); - } -}; -#endif - -#if RMT_USE_METAL -extern "C" RMT_API void _rmt_EndMetalSample(void); -struct rmt_EndMetalSampleOnScopeExit -{ - ~rmt_EndMetalSampleOnScopeExit() - { - _rmt_EndMetalSample(); - } -}; -#endif - -#endif - - - -// Pairs a call to rmt_BeginSample with its call to rmt_EndSample when leaving scope -#define rmt_ScopedCPUSample(name, flags) \ - RMT_OPTIONAL(RMT_ENABLED, rmt_BeginCPUSample(name, flags)); \ - RMT_OPTIONAL(RMT_ENABLED, rmt_EndCPUSampleOnScopeExit rmt_ScopedCPUSample##name); -#define rmt_ScopedCUDASample(name, stream) \ - RMT_OPTIONAL(RMT_USE_CUDA, rmt_BeginCUDASample(name, stream)); \ - RMT_OPTIONAL(RMT_USE_CUDA, rmt_EndCUDASampleOnScopeExit rmt_ScopedCUDASample##name(stream)); -#define rmt_ScopedD3D11Sample(name) \ - RMT_OPTIONAL(RMT_USE_D3D11, rmt_BeginD3D11Sample(name)); \ - RMT_OPTIONAL(RMT_USE_D3D11, rmt_EndD3D11SampleOnScopeExit rmt_ScopedD3D11Sample##name); -#define rmt_ScopedOpenGLSample(name) \ - RMT_OPTIONAL(RMT_USE_OPENGL, rmt_BeginOpenGLSample(name)); \ - RMT_OPTIONAL(RMT_USE_OPENGL, rmt_EndOpenGLSampleOnScopeExit rmt_ScopedOpenGLSample##name); -#define rmt_ScopedMetalSample(name) \ - RMT_OPTIONAL(RMT_USE_METAL, rmt_BeginMetalSample(name)); \ - RMT_OPTIONAL(RMT_USE_METAL, rmt_EndMetalSampleOnScopeExit rmt_ScopedMetalSample##name); - -#endif - - - -/* ------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------- - Private Interface - don't directly call these ------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------- -*/ - - - -#if RMT_ENABLED - -#ifdef __cplusplus -extern "C" { -#endif - -RMT_API rmtSettings* _rmt_Settings( void ); -RMT_API enum rmtError _rmt_CreateGlobalInstance(Remotery** remotery); -RMT_API void _rmt_DestroyGlobalInstance(Remotery* remotery); -RMT_API void _rmt_SetGlobalInstance(Remotery* remotery); -RMT_API Remotery* _rmt_GetGlobalInstance(void); -RMT_API void _rmt_SetCurrentThreadName(rmtPStr thread_name); -RMT_API void _rmt_LogText(rmtPStr text); -RMT_API void _rmt_SendText(rmtPStr text); -RMT_API void _rmt_BeginCPUSample(rmtPStr name, rmtU32 flags, rmtU32* hash_cache); -RMT_API void _rmt_EndCPUSample(void); -RMT_API void _rmt_EnableSampling(rmtBool enable); -RMT_API rmtBool _rmt_SamplingEnabled(); - -#if RMT_USE_CUDA -RMT_API void _rmt_BindCUDA(const rmtCUDABind* bind); -RMT_API void _rmt_BeginCUDASample(rmtPStr name, rmtU32* hash_cache, void* stream); -RMT_API void _rmt_EndCUDASample(void* stream); -#endif - -#if RMT_USE_D3D11 -RMT_API void _rmt_BindD3D11(void* device, void* context); -RMT_API void _rmt_UnbindD3D11(void); -RMT_API void _rmt_BeginD3D11Sample(rmtPStr name, rmtU32* hash_cache); -RMT_API void _rmt_EndD3D11Sample(void); -#endif - -#if RMT_USE_OPENGL -RMT_API void _rmt_BindOpenGL(); -RMT_API void _rmt_UnbindOpenGL(void); -RMT_API void _rmt_BeginOpenGLSample(rmtPStr name, rmtU32* hash_cache); -RMT_API void _rmt_EndOpenGLSample(void); -#endif - -#if RMT_USE_METAL -RMT_API void _rmt_BeginMetalSample(rmtPStr name, rmtU32* hash_cache); -RMT_API void _rmt_EndMetalSample(void); -#endif - -#ifdef __cplusplus - -} -#endif - -#if RMT_USE_METAL -#ifdef __OBJC__ -RMT_API void _rmt_BindMetal(id command_buffer); -RMT_API void _rmt_UnbindMetal(); -#endif -#endif - -#endif // RMT_ENABLED - -//! @endcond - -#endif diff --git a/third_party/gpac/include/gpac/avparse.h b/third_party/gpac/include/gpac/avparse.h index da496396..8a38490d 100644 --- a/third_party/gpac/include/gpac/avparse.h +++ b/third_party/gpac/include/gpac/avparse.h @@ -358,12 +358,12 @@ Bool gf_vorbis_parse_header(GF_VorbisParser *vp, u8 *data, u32 data_len); u32 gf_vorbis_check_frame(GF_VorbisParser *vp, u8 *data, u32 data_len); /*! parses opus header packets - initializes the config on success, leave it to NULL otherwise -\param cfg pointer to a opus config to fill +\param ocfg pointer to a opus config to fill \param data opus header buffer to parse \param data_len size of opus header buffer \return 1 if success, 0 if error */ -Bool gf_opus_parse_header(GF_OpusConfig *cfg, u8 *data, u32 data_len); +Bool gf_opus_parse_header(GF_OpusConfig *ocfg, u8 *data, u32 data_len); /*! checks if an opus frame is valid \param cfg pointer to a opus config to use @@ -641,7 +641,7 @@ const char *gf_m4a_get_profile_name(u8 audio_pl); //! \cond old name typedef struct __ac3_config GF_AC3Header; -//! \endcond +//! \endcond /*! parses an AC-3 header from a buffer \param buffer buffer to parse @@ -706,6 +706,26 @@ u32 gf_ac3_get_surround_channels(u32 acmod); */ u32 gf_ac3_get_bitrate(u32 brcode); +/*! parses an AC-4 header from a buffer +\param buffer buffer to parse +\param buffer_size size of buffer to parse +\param pos set to start offset (in bytes) of the AC4 header parsed +\param out_hdr will be filled by parser +\param full_parse if GF_TRUE, complete parsing of the header will be done +\param start_from_toc if GF_TRUE, parsing starts from the toc +\return GF_TRUE if success +*/ +Bool gf_ac4_parser(u8 *buffer, u32 buffer_size, u32 *pos, GF_AC4Config *out_hdr, Bool full_parse, Bool start_from_toc); + +/*! parses an AC-4 header from a bitstream +\param bs bitstream to parse +\param hdr will be filled by parser +\param full_parse if GF_TRUE, complete parsing of the header and check for next frame/blocks presence will be done +\param start_from_toc if GF_TRUE, parsing starts from the toc +\return GF_TRUE if success +*/ +Bool gf_ac4_parser_bs(GF_BitStream *bs, GF_AC4Config *hdr, Bool full_parse, Bool start_from_toc); + /*! gets basic information from an AVC Sequence Parameter Set \param sps SPS NAL buffer \param sps_size size of buffer @@ -855,7 +875,9 @@ typedef enum { OBU_METADATA_TYPE_HDR_MDCV = 2, OBU_METADATA_TYPE_SCALABILITY = 3, OBU_METADATA_TYPE_ITUT_T35 = 4, - OBU_METADATA_TYPE_TIMECODE = 5 + OBU_METADATA_TYPE_TIMECODE = 5, + OBU_METADATA_TYPE_PRIVATE_TIMECODE_SIMPLE = 7, + OBU_METADATA_TYPE_PRIVATE_TIMECODE_SIMPLE_BIS = 32, //same as 7 } ObuMetadataType; /*! gets the name of a given OBU type @@ -866,38 +888,38 @@ const char *gf_av1_get_obu_name(ObuType obu_type); /*!\brief IAMF OBU types */ typedef enum { - OBU_IA_CODEC_CONFIG = 0, - OBU_IA_AUDIO_ELEMENT = 1, - OBU_IA_MIX_PRESENTATION = 2, - OBU_IA_PARAMETER_BLOCK = 3, - OBU_IA_TEMPORAL_DELIMITER = 4, - OBU_IA_AUDIO_FRAME = 5, - OBU_IA_AUDIO_FRAME_ID0 = 6, - OBU_IA_AUDIO_FRAME_ID1 = 7, - OBU_IA_AUDIO_FRAME_ID2 = 8, - OBU_IA_AUDIO_FRAME_ID3 = 9, - OBU_IA_AUDIO_FRAME_ID4 = 10, - OBU_IA_AUDIO_FRAME_ID5 = 11, - OBU_IA_AUDIO_FRAME_ID6 = 12, - OBU_IA_AUDIO_FRAME_ID7 = 13, - OBU_IA_AUDIO_FRAME_ID8 = 14, - OBU_IA_AUDIO_FRAME_ID9 = 15, - OBU_IA_AUDIO_FRAME_ID10 = 16, - OBU_IA_AUDIO_FRAME_ID11 = 17, - OBU_IA_AUDIO_FRAME_ID12 = 18, - OBU_IA_AUDIO_FRAME_ID13 = 19, - OBU_IA_AUDIO_FRAME_ID14 = 20, - OBU_IA_AUDIO_FRAME_ID15 = 21, - OBU_IA_AUDIO_FRAME_ID16 = 22, - OBU_IA_AUDIO_FRAME_ID17 = 23, - OBU_IA_RESERVED_24 = 24, - OBU_IA_RESERVED_25 = 25, - OBU_IA_RESERVED_26 = 26, - OBU_IA_RESERVED_27 = 27, - OBU_IA_RESERVED_28 = 28, - OBU_IA_RESERVED_29 = 29, - OBU_IA_RESERVED_30 = 30, - OBU_IA_SEQUENCE_HEADER = 31 + OBU_IAMF_CODEC_CONFIG = 0, + OBU_IAMF_AUDIO_ELEMENT = 1, + OBU_IAMF_MIX_PRESENTATION = 2, + OBU_IAMF_PARAMETER_BLOCK = 3, + OBU_IAMF_TEMPORAL_DELIMITER = 4, + OBU_IAMF_AUDIO_FRAME = 5, + OBU_IAMF_AUDIO_FRAME_ID0 = 6, + OBU_IAMF_AUDIO_FRAME_ID1 = 7, + OBU_IAMF_AUDIO_FRAME_ID2 = 8, + OBU_IAMF_AUDIO_FRAME_ID3 = 9, + OBU_IAMF_AUDIO_FRAME_ID4 = 10, + OBU_IAMF_AUDIO_FRAME_ID5 = 11, + OBU_IAMF_AUDIO_FRAME_ID6 = 12, + OBU_IAMF_AUDIO_FRAME_ID7 = 13, + OBU_IAMF_AUDIO_FRAME_ID8 = 14, + OBU_IAMF_AUDIO_FRAME_ID9 = 15, + OBU_IAMF_AUDIO_FRAME_ID10 = 16, + OBU_IAMF_AUDIO_FRAME_ID11 = 17, + OBU_IAMF_AUDIO_FRAME_ID12 = 18, + OBU_IAMF_AUDIO_FRAME_ID13 = 19, + OBU_IAMF_AUDIO_FRAME_ID14 = 20, + OBU_IAMF_AUDIO_FRAME_ID15 = 21, + OBU_IAMF_AUDIO_FRAME_ID16 = 22, + OBU_IAMF_AUDIO_FRAME_ID17 = 23, + OBU_IAMF_RESERVED_24 = 24, + OBU_IAMF_RESERVED_25 = 25, + OBU_IAMF_RESERVED_26 = 26, + OBU_IAMF_RESERVED_27 = 27, + OBU_IAMF_RESERVED_28 = 28, + OBU_IAMF_RESERVED_29 = 29, + OBU_IAMF_RESERVED_30 = 30, + OBU_IAMF_SEQUENCE_HEADER = 31 } IamfObuType; /*! gets the name of a given IAMF OBU type @@ -914,4 +936,3 @@ const char *gf_iamf_get_obu_name(IamfObuType obu_type); #endif /*_GF_PARSERS_AV_H_*/ - diff --git a/third_party/gpac/include/gpac/configuration.h b/third_party/gpac/include/gpac/configuration.h index 0a1b2fef..17d09082 100644 --- a/third_party/gpac/include/gpac/configuration.h +++ b/third_party/gpac/include/gpac/configuration.h @@ -1,16 +1,122 @@ /* Automatically generated by configure */ #ifndef GF_CONFIG_H #define GF_CONFIG_H -#define GPAC_CONFIGURATION "--static-bin --use-curl=no" +#define GPAC_CONFIGURATION "--prefix=/Users/joelm/Desktop/eclipsa-audio-plugin/third_party/gpac/scripts/gpac_temp/install --static-modules --isomedia-only --disable-rmtws --use-zlib=no --use-opensvc=no --use-openhevc=no --use-platinum=no --use-freetype=no --use-ssl=no --use-jpeg=no --use-openjpeg=no --use-png=no --use-mad=no --use-a52=no --use-xvid=no --use-faad=no --use-ffmpeg=no --use-freenect=no --use-vorbis=no --use-theora=no --use-nghttp2=no --use-ngtcp2=no --use-nghttp3=no --use-oss=no --use-dvb4linux=no --use-alsa=no --use-pulseaudio=no --use-jack=no --use-directfb=no --use-hid=no --use-lzma=no --use-tinygl=no --use-vtb=no --use-ogg=no --use-sdl=no --use-caption=no --use-mpeghdec=no --use-libcaca=no --use-curl=no --enable-compositor --enable-fin --enable-fout --enable-mp4mx --enable-mp4dmx --enable-reframer --enable-rfav1" #define GF_STATIC static #define GF_NOT_EXPORTED -#define GPAC_STATIC_BIN -#define GPAC_STATIC_BUILD +#define GPAC_DISABLE_COMPOSITOR +#define GPAC_DISABLE_FONTS +#define GPAC_DISABLE_SMGR +#define GPAC_DISABLE_SVG +#define GPAC_DISABLE_VRML +#define GPAC_DISABLE_X3D +#define GPAC_MINIMAL_ODF +#define GPAC_DISABLE_OD_PARSE +#define GPAC_DISABLE_BIFS +#define GPAC_DISABLE_BIFS_ENC +#define GPAC_DISABLE_LASER +#define GPAC_DISABLE_SAF +#define GPAC_DISABLE_SENG +#define GPAC_DISABLE_QTVR +#define GPAC_DISABLE_AVILIB +#define GPAC_DISABLE_OGG +#define GPAC_DISABLE_MPEG2PS +#define GPAC_DISABLE_MPEG2TS +#define GPAC_DISABLE_MPEG2TS_MUX +#define GPAC_DISABLE_SWF_IMPORT +#define GPAC_DISABLE_SCENEGRAPH +#define GPAC_DISABLE_SCENE_STATS +#define GPAC_DISABLE_SCENE_DUMP +#define GPAC_DISABLE_SCENE_ENCODER +#define GPAC_DISABLE_LOADER_ISOM +#define GPAC_DISABLE_LOADER_BT +#define GPAC_DISABLE_LOADER_XMT +#define GPAC_DISABLE_OD_DUMP +#define GPAC_DISABLE_CRYPTO +#define GPAC_DISABLE_STREAMING +#define GPAC_DISABLE_VOBSUB +#define GPAC_DISABLE_DOC +#define GPAC_DISABLE_EVG +#define GPAC_DISABLE_ROUTE +#define GPAC_DISABLE_CRYPTO +#define GPAC_DISABLE_LOG +#define GPAC_DISABLE_VOUT +#define GPAC_DISABLE_AOUT +#define GPAC_DISABLE_NETCAP #define GPAC_CONFIG_DARWIN -#define GPAC_HAS_QJS #define GPAC_HAS_POLL +#define GPAC_DISABLE_THREADS +#define GPAC_DISABLE_NETWORK +#define GPAC_DISABLE_ZLIB +#define GPAC_DISABLE_RMTWS +#define GPAC_HAS_VTB #define GPAC_HAS_STRLCPY #define GPAC_HAS_SOCK_UN #define GPAC_HAS_IFADDRS +#define GPAC_DISABLE_COMPOSITOR +#define GPAC_DISABLE_VOUT +#define GPAC_DISABLE_AOUT +#define GPAC_DISABLE_BSAGG +#define GPAC_DISABLE_BSSPLIT +#define GPAC_DISABLE_BSRW +#define GPAC_DISABLE_DASHER +#define GPAC_DISABLE_DASHIN +#define GPAC_DISABLE_IMGDEC +#define GPAC_DISABLE_UNCVDEC +#define GPAC_DISABLE_CDCRYPT +#define GPAC_DISABLE_GHIDMX +#define GPAC_DISABLE_GSFDMX +#define GPAC_DISABLE_NHMLR +#define GPAC_DISABLE_NHNTR +#define GPAC_DISABLE_CECRYPT +#define GPAC_DISABLE_FLIST +#define GPAC_DISABLE_HEVCMERGE +#define GPAC_DISABLE_HEVCSPLIT +#define GPAC_DISABLE_PIN +#define GPAC_DISABLE_INSPECT +#define GPAC_DISABLE_CRYPTFILE +#define GPAC_DISABLE_GSFMX +#define GPAC_DISABLE_POUT +#define GPAC_DISABLE_RFAC3 +#define GPAC_DISABLE_RFAC4 +#define GPAC_DISABLE_RFADTS +#define GPAC_DISABLE_RFAMR +#define GPAC_DISABLE_RFFLAC +#define GPAC_DISABLE_RFH263 +#define GPAC_DISABLE_RFIMG +#define GPAC_DISABLE_RFLATM +#define GPAC_DISABLE_RFMHAS +#define GPAC_DISABLE_RFMP3 +#define GPAC_DISABLE_RFMPGVID +#define GPAC_DISABLE_RFNALU +#define GPAC_DISABLE_RFPRORES +#define GPAC_DISABLE_RFQCP +#define GPAC_DISABLE_RFPCM +#define GPAC_DISABLE_RFRAWVID +#define GPAC_DISABLE_RFTRUEHD +#define GPAC_DISABLE_RESAMPLE +#define GPAC_DISABLE_RESTAMP +#define GPAC_DISABLE_REWIND +#define GPAC_DISABLE_UFAAC +#define GPAC_DISABLE_UFMHAS +#define GPAC_DISABLE_UFAC4 +#define GPAC_DISABLE_UFM4V +#define GPAC_DISABLE_UFNALU +#define GPAC_DISABLE_UFOBU +#define GPAC_DISABLE_TILEAGG +#define GPAC_DISABLE_TILESPLIT +#define GPAC_DISABLE_TTMLCONV +#define GPAC_DISABLE_UNFRAMER +#define GPAC_DISABLE_VCROP +#define GPAC_DISABLE_VFLIP +#define GPAC_DISABLE_WRITEGEN +#define GPAC_DISABLE_WEBCODEC +#define GPAC_DISABLE_NHMLW +#define GPAC_DISABLE_NHNTW +#define GPAC_DISABLE_WRITEQCP +#define GPAC_DISABLE_UNITS +#define GPAC_DISABLE_NVDEC +#define GPAC_DISABLE_VTBDEC #define GPAC_DISABLE_3D +#define GPAC_HAS_IPV6 #endif diff --git a/third_party/gpac/include/gpac/constants.h b/third_party/gpac/include/gpac/constants.h index 68b9de01..ed257dd7 100644 --- a/third_party/gpac/include/gpac/constants.h +++ b/third_party/gpac/include/gpac/constants.h @@ -2,7 +2,7 @@ * GPAC - Multimedia Framework C SDK * * Authors: Jean Le Feuvre - * Copyright (c) Telecom ParisTech 2000-2023 + * Copyright (c) Telecom ParisTech 2000-2025 * All rights reserved * * This file is part of GPAC / exported constants @@ -150,6 +150,8 @@ typedef enum GF_PIXEL_ALPHAGREY = GF_4CC('G','R','A','L'), /*!16 bit greyscale, first grey, then alpha*/ GF_PIXEL_GREYALPHA = GF_4CC('A','L','G','R'), + /*!8 bit RGB */ + GF_PIXEL_RGB_332 = GF_4CC('R','3','3','2'), /*!12 bit RGB on 16 bits (4096 colors)*/ GF_PIXEL_RGB_444 = GF_4CC('R','4','4','4'), /*!15 bit RGB*/ @@ -486,6 +488,8 @@ typedef enum GF_CODECID_AC3 = GF_4CC('a','c','-','3'), /*! codecid for enhanced AC-3 audio streams*/ GF_CODECID_EAC3 = GF_4CC('e','c','-','3'), + /*! codecid for AC-4 audio streams*/ + GF_CODECID_AC4 = GF_4CC('a','c','-','4'), /*! codecid for Dolby TrueHS audio streams*/ GF_CODECID_TRUEHD = GF_4CC('m','l','p','a'), /*! codecid for DRA audio streams*/ @@ -536,6 +540,7 @@ typedef enum GF_CODECID_DVB_SUBS = GF_4CC( 'd', 'v', 'b', 's' ), GF_CODECID_DVB_TELETEXT = GF_4CC( 'd', 'v', 'b', 't' ), + /*! codecid for SCTE35 streams (MPEG2-TS Sections payloads as per ANSI/SCTE 67 2017 (13.1.1.3)*/ GF_CODECID_SCTE35 = GF_4CC( 's', 'c', '3', '5' ), /*! @@ -607,6 +612,12 @@ typedef enum GF_CODECID_VP9 = GF_4CC('V','P','0','9'), GF_CODECID_VP10 = GF_4CC('V','P','1','0'), + /*AVS2/3*/ + GF_CODECID_AVS2_VIDEO = GF_4CC('A','V','V','2'), + GF_CODECID_AVS2_AUDIO = GF_4CC('A','V','A','2'), + GF_CODECID_AVS3_VIDEO = GF_4CC('A','V','V','3'), + GF_CODECID_AVS3_AUDIO = GF_4CC('A','V','A','3'), + /*MPEG-H audio*/ GF_CODECID_MPHA = GF_4CC('m','p','h','a'), /*MPEG-H mux audio*/ @@ -622,7 +633,7 @@ typedef enum GF_CODECID_TMCD = GF_4CC('t','m','c','d'), - /*Event Message Track*/ + /*Event Message Track (contains boxes)*/ GF_CODECID_EVTE = GF_4CC('e','v','t','e'), /*! codecid for FFV1*/ @@ -1005,18 +1016,30 @@ u32 gf_audio_fmt_get_cicp_from_layout(u64 chan_layout); */ u32 gf_audio_fmt_get_num_channels_from_layout(u64 chan_layout); -/*! get dloby chanmap value from cicp layout +/*! get dolby chanmap value from cicp layout \param cicp_layout channel CICP layout \return dolby chanmap */ u16 gf_audio_fmt_get_dolby_chanmap(u32 cicp_layout); -/*! get dloby chanmap value from channel layout +/*! get dolby chanmap value from channel layout \param channel_layout channel layout mask \return dolby chanmap */ u16 gf_audio_fmt_get_dolby_chanmap_from_layout(u64 channel_layout); +/*! get dolby AudioChannelConfiguration value from ac4 presentation_channel_mask_v1 +\param mask presentation channel mask v1 +\return dolby AudioChannelConfiguration value +*/ +u32 gf_audio_get_dolby_channel_config_value_from_mask(u32 mask); + +/*! get dolby channel count for HLS from ac4 presentation_channel_mask_v1 +\param mask presentation channel mask v1 +\return dolby channel count +*/ +u32 gf_ac4_dolby_channel_count_from_channel_mask_v1(u32 mask); + /*! enumerates CICP channel layout \param idx index of cicp layout value to query \param short_name set t o CICP name as used in GPAC - may be NULL diff --git a/third_party/gpac/include/gpac/crypt_tools.h b/third_party/gpac/include/gpac/crypt_tools.h index bd0e4211..6dd35c31 100644 --- a/third_party/gpac/include/gpac/crypt_tools.h +++ b/third_party/gpac/include/gpac/crypt_tools.h @@ -138,6 +138,17 @@ typedef enum GF_KEYROLL_PERIODS, } GF_KeyRollType; +/*! non-VCL crypt modes (for avc1 CTR CENC edition 1) */ +typedef enum +{ + /*! all encrypted (except nal header) */ + GF_CRYPT_NONVCL_CLEAR_NONE = 0, + /*! keep SEI and AUD in clear */ + GF_CRYPT_NONVCL_CLEAR_SEI_AUD, + /*! all clear */ + GF_CRYPT_NONVCL_CLEAR_ALL, +} GF_CryptNonVCL; + /*! Crypto information for one media stream*/ typedef struct { @@ -195,8 +206,10 @@ typedef struct /*! forces a minimum clear range for subsamples (ignored otherwise) - the final offset is at least the slice header size*/ u32 crypt_byte_offset; - /* ! for avc1 ctr CENC edition 1 */ + /*! for avc1 CTR CENC edition 1 */ Bool allow_encrypted_slice_header; + /*! encrypt non-VCL NALUs - made to refine avc1 CTR CENC edition 1 support */ + GF_CryptNonVCL allow_encrypted_nonVCLs; /*! force cenc and cbc1: 0: default, 1: no block alignment of encrypted data, 2: always block align even if producing non encrypted samples*/ u32 block_align; diff --git a/third_party/gpac/include/gpac/dash.h b/third_party/gpac/include/gpac/dash.h index 23f0ef6a..1648ca4f 100644 --- a/third_party/gpac/include/gpac/dash.h +++ b/third_party/gpac/include/gpac/dash.h @@ -329,7 +329,7 @@ s32 gf_dash_get_dependent_group_index(GF_DashClient *dash, u32 group_idx, u32 gr */ Bool gf_dash_is_group_selectable(GF_DashClient *dash, u32 group_idx); -/*! selects a group for playback. If group selection is enabled, other groups are alternate to this group (through the group attribute), they are automatically deselected +/*! selects a group for playback. If group selection is enabled, other groups are alternate to this group (through the group attribute), they are automatically deselected Seeking is NOT performed, it is the responsability to call \ref gf_dash_group_seek - this can be called before or after selecting @@ -346,13 +346,13 @@ void gf_dash_group_select(GF_DashClient *dash, u32 group_idx, Bool select); */ s32 gf_dash_group_get_id(GF_DashClient *dash, u32 group_idx); -/*! gets cuirrent period ID +/*! gets current period ID \param dash the target dash client \return ID of the current period or NULL */ const char*gf_dash_get_period_id(GF_DashClient *dash); -/*! enables group selection through the group attribute +/*! enables group selection through the group attribute \param dash the target dash client \param enable if GF_TRUE, group selection will be done whenever selecting a new group */ @@ -398,7 +398,7 @@ const char *gf_dash_group_get_segment_init_keys(GF_DashClient *dash, u32 group_i */ const char *gf_dash_group_get_language(GF_DashClient *dash, u32 group_idx); -/*! returns the number of audio channelsof the group +/*! returns the number of audio channels of the group \param dash the target dash client \param group_idx the 0-based index of the target group \return the number of audio channels, or 0 if not audio or unspecified diff --git a/third_party/gpac/include/gpac/download.h b/third_party/gpac/include/gpac/download.h index 999ac9cb..9fc029b1 100644 --- a/third_party/gpac/include/gpac/download.h +++ b/third_party/gpac/include/gpac/download.h @@ -520,7 +520,7 @@ u32 gf_dm_sess_get_max_rate(GF_DownloadSession *sess); /*! \brief Checks session regulation state -Checks if last session fetch has been skiped due to rate limitation +Checks if last session fetch has been skipped due to rate limitation \param sess the download session object \return GF_TRUE if last call to \ref gf_dm_sess_fetch_data was skipped because of rate regulation */ @@ -615,7 +615,7 @@ Adds a local entry in the cache \param download_time_ms indicates the download time of the associated resource, if known, 0 otherwise. \return a cache entry structure */ -DownloadedCacheEntry gf_dm_add_cache_entry(GF_DownloadManager *dm, const char *szURL, GF_Blob *blob, u64 start_range, u64 end_range, const char *mime, Bool clone_memory, u32 download_time_ms); +DownloadedCacheEntry gf_dm_add_cache_entry(GF_DownloadManager *dm, const char *szURL, GF_Blob *blob, u64 start_range, u64 end_range, const char *mime, Bool clone_memory, u32 download_time_ms); /*! Forces HTTP headers for a given cache entry diff --git a/third_party/gpac/include/gpac/evg.h b/third_party/gpac/include/gpac/evg.h index 9fc121ff..c897c164 100644 --- a/third_party/gpac/include/gpac/evg.h +++ b/third_party/gpac/include/gpac/evg.h @@ -173,7 +173,7 @@ When auto matrix mode is not activated,, the stencil matrix must describe the co Stencils are by default created in auto matrix mode. \param stencil the target stencil -\param auto_on If true, the surface current matrix will be added to the stencil matrix when drawing, otherwise the stencil matrix is in final surface coordinates +\param auto_on If true, the surface current matrix will be added to the stencil matrix when drawing, otherwise the stencil matrix is in final surface coordinates \return error if any */ GF_Err gf_evg_stencil_set_auto_matrix(GF_EVGStencil * stencil, Bool auto_on); @@ -999,7 +999,7 @@ GF_Err gf_evg_surface_disable_early_depth(GF_EVGSurface *surf, Bool disable); /*! disables depth buffer write \note this is only used for 3D rasterizer, and fails 2D mode \param surf the target 3D surface -\param do_write if GF_TRUE, depth values are written to the depth buffer (default when creating the rasterizer) +\param do_write if GF_TRUE, depth values are written to the depth buffer (default when creating the rasterizer) \return error if any */ GF_Err gf_evg_surface_write_depth(GF_EVGSurface *surf, Bool do_write); diff --git a/third_party/gpac/include/gpac/filters.h b/third_party/gpac/include/gpac/filters.h index e0212e77..4f604aba 100644 --- a/third_party/gpac/include/gpac/filters.h +++ b/third_party/gpac/include/gpac/filters.h @@ -54,7 +54,7 @@ This file contains all exported functions for filter management of the GPAC fram API Documentation of the filter managment system of GPAC. The filter management in GPAC is built using the following core objects: -- \ref GF_FilterSession in charge of: +- \ref fs_grp "GF_FilterSession" in charge of: - loading filters from register, managing argument parsing and co - resolving filter graphs to handle PID connection(s) - tracking data packets and properties exchanged on PIDs @@ -62,12 +62,12 @@ The filter management in GPAC is built using the following core objects: - ensuring thread-safe filter state: a filter may be called from any thread in the session (unless explicitly asked not to), but only by a single thread at any time. - \ref __gf_filter_register static structure describing possible entry points of the filter, possible arguments and input output PID capabilities. Each filter share the same API (register definition) regardless of its type: source/sink, mux/demux, encode/decode, raw media processing, encoded media processing, ... -- \ref GF_Filter is an instance of the filter register. A filter implementation typical tasks are: +- \ref fs_filter "GF_Filter" is an instance of the filter register. A filter implementation typical tasks are: - accepting new input PIDs (for non source filters) - defining new output PIDs (for non sink filters), applying any property change due to filter processing - consuming packets on the input PIDs - dispatching packets on the output PIDs -- \ref GF_FilterPid handling the connections between two filters. +- \ref fs_pid "GF_FilterPid" handling the connections between two filters. - PID natively supports fan-out (one filter PID connecting to multiple destinations). - A PID is in charge of dispatching packets to possible destinations and storing PID properties in sync with dispatched packets. - Whenever PID properties change, the next packet sent on that PID is associated with the new state, and the destination filter(s) will be called @@ -78,7 +78,7 @@ The filter management in GPAC is built using the following core objects: for processing. This is a semi-blocking design, which imply that if a filter has one of its PIDs in a non blocking state, it will be scheduled for processing. If a PID has multiple destinations and one of the destination consumes faster than the other one, the filter is currently not blocking (this might change in the near future). - A PID is in charge of managing the packet references across filters, by performing memory management of allocated data packets (avoid alloc/free at each packet but rather recycle the memory) and tracking shared packets references. -- \ref GF_FilterPacket holding data to dispatch from a filter on a given PID. +- \ref fs_pck "GF_FilterPacket" holding data to dispatch from a filter on a given PID. - Packets are always associated to a single output PID, ie it is not possible for a filter to send one packet to multiple PIDs, the data has to be cloned. - Packets have default attributes such as timestamps, size, random access status, start/end frame, etc, as well as optional properties. - All packets are reference counted. @@ -254,7 +254,7 @@ typedef enum GF_FilterSession *gf_fs_new(s32 nb_threads, GF_FilterSchedulerType type, GF_FilterSessionFlags flags, const char *blacklist); /*! Creates a new filter session, loading parameters from gpac config. This will also load all available filter registers not blacklisted. -\param flags set of flags for the session. Only \ref GF_FS_FLAG_LOAD_META, \ref GF_FS_FLAG_NON_BLOCKING , \ref GF_FS_FLAG_NO_GRAPH_CACHE and \ref GF_FS_FLAG_PRINT_CONNECTIONS are used, other flags are set from config file or command line +\param flags set of flags for the session. Only \ref GF_FS_FLAG_LOAD_META, \ref GF_FS_FLAG_NON_BLOCKING , \ref GF_FS_FLAG_NO_GRAPH_CACHE and \ref GF_FS_FLAG_PRINT_CONNECTIONS are used, other flags are set from config file or command line \return the created filter session */ GF_FilterSession *gf_fs_new_defaults(GF_FilterSessionFlags flags); @@ -304,8 +304,8 @@ The format of the filter graph is the same as the one used in the gpac command l \param fsess filter session \param argc number of arguments \param argv list of arguments -\param loaded_filters list of loaded filters, must be freed by the caller -\param links_directive list of link directives, must be freed by the caller +\param out_loaded_filters list of loaded filters, must be freed by the caller +\param out_links_directive list of link directives, must be freed by the caller \return error if any */ GF_Err gf_fs_parse_filter_graph(GF_FilterSession *fsess, int argc, char *argv[], GF_List **out_loaded_filters, GF_List **out_links_directive); @@ -320,8 +320,8 @@ This is a convenience function for command line parsing, and does not support al \param fsess filter session \param graph_str filter graph string -\param loaded_filters list of loaded filters, must be freed by the caller -\param links_directive list of link directives, must be freed by the caller +\param out_loaded_filters list of loaded filters, must be freed by the caller +\param out_links_directive list of link directives, must be freed by the caller \return error if any */ GF_Err gf_fs_parse_filter_graph_str(GF_FilterSession *fsess, char *graph_str, GF_List **out_loaded_filters, GF_List **out_links_directive); @@ -933,7 +933,7 @@ typedef enum GF_PROP_4CC_LIST = 26, /*! string list, memory is duplicated when setting the property - to use only with property assignment functions*/ GF_PROP_STRING_LIST_COPY = 27, - + /*! last non-enum property*/ GF_PROP_LAST_NON_ENUM, @@ -1300,6 +1300,7 @@ enum GF_PROP_PID_PERIOD_DUR = GF_4CC('P','E','D','U'), GF_PROP_PID_REP_ID = GF_4CC('D','R','I','D'), GF_PROP_PID_SSR = GF_4CC('S','S','R','R'), + GF_PROP_PID_AS_QUERY = GF_4CC('A','S','Q','R'), GF_PROP_PID_AS_ID = GF_4CC('D','A','I','D'), GF_PROP_PID_MUX_SRC = GF_4CC('M','S','R','C'), GF_PROP_PID_DASH_MODE = GF_4CC('D','M','O','D'), @@ -1309,6 +1310,7 @@ enum GF_PROP_PID_DASH_MULTI_PID = GF_4CC('D','M','S','D'), GF_PROP_PID_DASH_MULTI_PID_IDX = GF_4CC('D','M','S','I'), GF_PROP_PID_DASH_MULTI_TRACK = GF_4CC('D','M','T','K'), + GF_PROP_PID_DASH_INIT_BASE64 = GF_4CC('I','B','6','4'), GF_PROP_PID_ROLE = GF_4CC('R','O','L','E'), GF_PROP_PID_PERIOD_DESC = GF_4CC('P','D','E','S'), GF_PROP_PID_AS_COND_DESC = GF_4CC('A','C','D','S'), @@ -1465,6 +1467,11 @@ enum GF_PROP_PCK_CONTENT_LIGHT_LEVEL = GF_4CC('C','L','L','P'), GF_PROP_PCK_MASTER_DISPLAY_COLOUR = GF_4CC('M','D','C','P'), + GF_PROP_PCK_ORIGINAL_PTS = GF_4CC('O','P','T','S'), + GF_PROP_PCK_ORIGINAL_DTS = GF_4CC('O','D','T','S'), + GF_PROP_PID_MABR_URLS = GF_4CC('M','A','B','U'), + GF_PROP_PCK_FORCED_SUB = GF_4CC('P','C','F','S'), + }; /*! Block patching requirements for FILE pids, as signaled by GF_PROP_PID_DISABLE_PROGRESSIVE @@ -1648,61 +1655,61 @@ u8 gf_props_4cc_get_flags(u32 prop_4cc); /*! Helper macro to set signed int property */ -#define PROP_SINT(_val) (GF_PropertyValue){.type=GF_PROP_SINT, .value.sint = _val} +#define PROP_SINT(_val) (GF_PropertyValue){GF_PROP_SINT, { .sint = (_val) }} /*! Helper macro to set unsigned int property */ -#define PROP_UINT(_val) (GF_PropertyValue){.type=GF_PROP_UINT, .value.uint = _val} +#define PROP_UINT(_val) (GF_PropertyValue){GF_PROP_UINT, { .uint = (_val) }} /*! Helper macro to set an enum property */ -#define PROP_ENUM(_val, _type) (GF_PropertyValue){.type=_type, .value.uint = _val} +#define PROP_ENUM(_val, _type) (GF_PropertyValue){_type, { .uint = (_val) }} /*! Helper macro to set 4CC unsigned int property */ -#define PROP_4CC(_val) (GF_PropertyValue){.type=GF_PROP_4CC, .value.uint = _val} +#define PROP_4CC(_val) (GF_PropertyValue){GF_PROP_4CC, { .uint = (_val) }} /*! Helper macro to set long signed int property */ -#define PROP_LONGSINT(_val) (GF_PropertyValue){.type=GF_PROP_LSINT, .value.longsint = _val} +#define PROP_LONGSINT(_val) (GF_PropertyValue){GF_PROP_LSINT, { .longsint = (_val) }} /*! Helper macro to set long unsigned int property */ -#define PROP_LONGUINT(_val) (GF_PropertyValue){.type=GF_PROP_LUINT, .value.longuint = _val} +#define PROP_LONGUINT(_val) (GF_PropertyValue){GF_PROP_LUINT, { .longuint = (_val) }} /*! Helper macro to set boolean property */ -#define PROP_BOOL(_val) (GF_PropertyValue){.type=GF_PROP_BOOL, .value.boolean = _val} +#define PROP_BOOL(_val) (GF_PropertyValue){GF_PROP_BOOL, { .boolean = (_val) }} /*! Helper macro to set fixed-point number property */ -#define PROP_FIXED(_val) (GF_PropertyValue){.type=GF_PROP_FLOAT, .value.fnumber = _val} +#define PROP_FIXED(_val) (GF_PropertyValue){GF_PROP_FLOAT, { .fnumber = (_val) }} /*! Helper macro to set float property */ -#define PROP_FLOAT(_val) (GF_PropertyValue){.type=GF_PROP_FLOAT, .value.fnumber = FLT2FIX(_val)} +#define PROP_FLOAT(_val) (GF_PropertyValue){GF_PROP_FLOAT, { .fnumber = FLT2FIX(_val) }} /*! Helper macro to set 32-bit fraction property from integers*/ -#define PROP_FRAC_INT(_num, _den) (GF_PropertyValue){.type=GF_PROP_FRACTION, .value.frac.num = _num, .value.frac.den = _den} +#define PROP_FRAC_INT(_num, _den) (GF_PropertyValue){GF_PROP_FRACTION, { .frac.num = (_num), .frac.den = (_den) }} /*! Helper macro to set 32-bit fraction property*/ -#define PROP_FRAC(_val) (GF_PropertyValue){.type=GF_PROP_FRACTION, .value.frac = _val } +#define PROP_FRAC(_val) (GF_PropertyValue){GF_PROP_FRACTION, { .frac = (_val) }} /*! Helper macro to set 64-bit fraction property from integers*/ -#define PROP_FRAC64(_val) (GF_PropertyValue){.type=GF_PROP_FRACTION64, .value.lfrac = _val} +#define PROP_FRAC64(_val) (GF_PropertyValue){GF_PROP_FRACTION64, { .lfrac = (_val) }} /*! Helper macro to set 64-bit fraction property*/ -#define PROP_FRAC64_INT(_num, _den) (GF_PropertyValue){.type=GF_PROP_FRACTION64, .value.lfrac.num = _num, .value.lfrac.den = _den} +#define PROP_FRAC64_INT(_num, _den) (GF_PropertyValue){GF_PROP_FRACTION64, { .lfrac.num = (_num), .lfrac.den = (_den) }} /*! Helper macro to set double property */ -#define PROP_DOUBLE(_val) (GF_PropertyValue){.type=GF_PROP_DOUBLE, .value.number = _val} +#define PROP_DOUBLE(_val) (GF_PropertyValue){GF_PROP_DOUBLE, { .number = (_val) }} /*! Helper macro to set string property */ -#define PROP_STRING(_val) (GF_PropertyValue){.type=GF_PROP_STRING, .value.string = (char *) _val} +#define PROP_STRING(_val) (GF_PropertyValue){GF_PROP_STRING, { .string = (char *)(_val) }} /*! Helper macro to set string property without string copy (string memory is owned by filter) */ -#define PROP_STRING_NO_COPY(_val) (GF_PropertyValue){.type=GF_PROP_STRING_NO_COPY, .value.string = _val} +#define PROP_STRING_NO_COPY(_val) (GF_PropertyValue){GF_PROP_STRING_NO_COPY, { .string = (char *)(_val) }} /*! Helper macro to set name property */ -#define PROP_NAME(_val) (GF_PropertyValue){.type=GF_PROP_NAME, .value.string = _val} +#define PROP_NAME(_val) (GF_PropertyValue){GF_PROP_NAME, { .string = (char *)(_val) }} /*! Helper macro to set data property */ -#define PROP_DATA(_val, _len) (GF_PropertyValue){.type=GF_PROP_DATA, .value.data.ptr = _val, .value.data.size=_len} +#define PROP_DATA(_val, _len) (GF_PropertyValue){GF_PROP_DATA, { .data.ptr = (_val), .data.size = (_len) }} /*! Helper macro to set data property without data copy ( memory is owned by filter) */ -#define PROP_DATA_NO_COPY(_val, _len) (GF_PropertyValue){.type=GF_PROP_DATA_NO_COPY, .value.data.ptr = _val, .value.data.size =_len} +#define PROP_DATA_NO_COPY(_val, _len) (GF_PropertyValue){GF_PROP_DATA_NO_COPY, { .data.ptr = (_val), .data.size = (_len) }} /*! Helper macro to set const data property */ -#define PROP_CONST_DATA(_val, _len) (GF_PropertyValue){.type=GF_PROP_CONST_DATA, .value.data.ptr = _val, .value.data.size = _len} +#define PROP_CONST_DATA(_val, _len) (GF_PropertyValue){GF_PROP_CONST_DATA, { .data.ptr = (_val), .data.size = (_len) }} /*! Helper macro to set 2D float vector property */ -#define PROP_VEC2(_val) (GF_PropertyValue){.type=GF_PROP_VEC2, .value.vec2 = _val} +#define PROP_VEC2(_val) (GF_PropertyValue){GF_PROP_VEC2, { .vec2 = (_val) }} /*! Helper macro to set 2D integer vector property */ -#define PROP_VEC2I(_val) (GF_PropertyValue){.type=GF_PROP_VEC2I, .value.vec2i = _val} +#define PROP_VEC2I(_val) (GF_PropertyValue){GF_PROP_VEC2I, { .vec2i = (_val) }} /*! Helper macro to set 2D integer vector property from integers*/ -#define PROP_VEC2I_INT(_x, _y) (GF_PropertyValue){.type=GF_PROP_VEC2I, .value.vec2i.x = _x, .value.vec2i.y = _y} +#define PROP_VEC2I_INT(_x, _y) (GF_PropertyValue){GF_PROP_VEC2I, { .vec2i.x = (_x), .vec2i.y = (_y) }} /*! Helper macro to set 3D integer vector property */ -#define PROP_VEC3I(_val) (GF_PropertyValue){.type=GF_PROP_VEC3I, .value.vec3i = _val} +#define PROP_VEC3I(_val) (GF_PropertyValue){GF_PROP_VEC3I, { .vec3i = (_val) }} /*! Helper macro to set 3D integer vector property from integers*/ -#define PROP_VEC3I_INT(_x, _y, _z) (GF_PropertyValue){.type=GF_PROP_VEC3I, .value.vec3i.x = _x, .value.vec3i.y = _y, .value.vec3i.z = _z} +#define PROP_VEC3I_INT(_x, _y, _z) (GF_PropertyValue){GF_PROP_VEC3I, { .vec3i.x = (_x), .vec3i.y = (_y), .vec3i.z = (_z) }} /*! Helper macro to set 4D integer vector property */ -#define PROP_VEC4I(_val) (GF_PropertyValue){.type=GF_PROP_VEC4I, .value.vec4i = _val} +#define PROP_VEC4I(_val) (GF_PropertyValue){GF_PROP_VEC4I, { .vec4i = (_val) }} /*! Helper macro to set 4D integer vector property from integers */ -#define PROP_VEC4I_INT(_x, _y, _z, _w) (GF_PropertyValue){.type=GF_PROP_VEC4I, .value.vec4i.x = _x, .value.vec4i.y = _y, .value.vec4i.z = _z, .value.vec4i.w = _w} +#define PROP_VEC4I_INT(_x, _y, _z, _w) (GF_PropertyValue){GF_PROP_VEC4I, { .vec4i.x = (_x), .vec4i.y = (_y), .vec4i.z = (_z), .vec4i.w = (_w) }} /*! Helper macro to set pointer property */ -#define PROP_POINTER(_val) (GF_PropertyValue){.type=GF_PROP_POINTER, .value.ptr = (void*)_val} +#define PROP_POINTER(_val) (GF_PropertyValue){GF_PROP_POINTER, { .ptr = (void *)(_val) }} /*! @} */ @@ -1795,8 +1802,8 @@ typedef enum /*! DASH fragment (cmaf chunk) size info, sent down from muxers to manifest generators*/ GF_FEVT_FRAGMENT_SIZE, - /*! Encoder hints*/ - GF_FEVT_ENCODE_HINTS, + /*! Transport hints*/ + GF_FEVT_TRANSPORT_HINTS, /*! NTP source clock send by other services (eg from TS to dash using TEMI) */ GF_FEVT_NTP_REF, /*! Event sent by DASH/HLS demux to source to notify a quality change - used for ROUTE/MABR only */ @@ -1893,7 +1900,7 @@ typedef struct u8 is_init_segment; /*!GF_FEVT_SOURCE_SWITCH only, ignore cache expiration directive for HTTP*/ u8 skip_cache_expiration; - /*! GF_FEVT_SOURCE_SEEK only, hint block size for source, might not be respected*/ + /*! GF_FEVT_SOURCE_SEEK only, hint block size for source, might not be respected*/ u32 hint_block_size; } GF_FEVT_SourceSeek; @@ -1903,6 +1910,8 @@ typedef struct FILTER_EVENT_BASE /*! URL of segment this info is for, or NULL if single file*/ const char *seg_url; + /*! base64 of segment payload (for init) or NULL*/ + const char *base64_version; /*! media start range in segment file*/ u64 media_range_start; /*! media end range in segment file*/ @@ -1973,7 +1982,12 @@ typedef struct typedef struct { FILTER_EVENT_BASE - /*! URL to delete, or "__gpac_self__" when asking source filter to delete file */ + /*! URL to delete, or "__gpac_self__" when asking source filter to delete file + + For gfio files, the syntax gfio://PTR@URL is allowed, with: + - PTR: the parent gfio pointer + - URL: the url of the file to delete, relative to the parent gfio + */ const char *url; } GF_FEVT_FileDelete; @@ -2006,17 +2020,30 @@ typedef struct Bool pid_only; } GF_FEVT_BufferRequirement; +typedef enum +{ + /*! no hints */ + GF_TRANSPORT_HINTS_NONE = 0, + /*! event seen by an encoder */ + GF_TRANSPORT_HINTS_SAW_ENCODER = 1<<0, +} GF_TransportHintsFlags; -/*! Event structure for GF_FEVT_ENCODE_HINT*/ +/*! Event structure for GF_FEVT_TRANSPORT_HINT*/ typedef struct { FILTER_EVENT_BASE - /*! duration of intra (IDR, closed GOP) as expected by the dasher */ - GF_Fraction intra_period; + /*! flags for the hints */ + GF_TransportHintsFlags flags; + + /*! segment duration */ + GF_Fraction seg_duration; /*! if TRUE codec should only generate DSI (possibly no input frame, and all output packets will be discarded) */ Bool gen_dsi_only; -} GF_FEVT_EncodeHints; + + /* if TRUE reframer should hold packets until theoretical segment boundary */ + Bool wait_seg_boundary; +} GF_FEVT_TransportHints; /*! Event structure for GF_FEVT_NTP_REF*/ @@ -2085,7 +2112,7 @@ union __gf_filter_event GF_FEVT_SegmentSize seg_size; GF_FEVT_FragmentSize frag_size; GF_FEVT_FileDelete file_del; - GF_FEVT_EncodeHints encode_hints; + GF_FEVT_TransportHints transport_hints; GF_FEVT_NTPRef ntp; GF_FEVT_DASHQualitySelection dash_select; GF_FEVT_NetworkHint net_hint; @@ -2205,33 +2232,33 @@ typedef struct } GF_FilterArgs; /*! Shortcut macro to assign singed integer capability type*/ -#define CAP_SINT(_f, _a, _b) { .code=_a, .val={.type=GF_PROP_SINT, .value.sint = _b}, .flags=(_f) } +#define CAP_SINT(_f, _a, _b) { _a, { GF_PROP_SINT, { .sint = (_b) }}, NULL, _f } /*! Shortcut macro to assign unsigned integer capability type*/ -#define CAP_UINT(_f, _a, _b) { .code=_a, .val={.type=GF_PROP_UINT, .value.uint = _b}, .flags=(_f) } +#define CAP_UINT(_f, _a, _b) { _a, { GF_PROP_UINT, { .uint = (_b) }}, NULL, _f } /*! Shortcut macro to assign unsigned integer capability type*/ -#define CAP_4CC(_f, _a, _b) { .code=_a, .val={.type=GF_PROP_4CC, .value.uint = _b}, .flags=(_f) } +#define CAP_4CC(_f, _a, _b) { _a, { GF_PROP_4CC, { .uint = (_b) }}, NULL, _f } /*! Shortcut macro to assign signed long integer capability type*/ -#define CAP_LSINT(_f, _a, _b) { .code=_a, .val={.type=GF_PROP_LSINT, .value.longsint = _b}, .flags=(_f) } +#define CAP_LSINT(_f, _a, _b) { _a, { GF_PROP_LSINT, { .longsint = (_b) }}, NULL, _f } /*! Shortcut macro to assign unsigned long integer capability type*/ -#define CAP_LUINT(_f, _a, _b) { .code=_a, .val={.type=GF_PROP_LUINT, .value.longuint = _b}, .flags=(_f) } +#define CAP_LUINT(_f, _a, _b) { _a, { GF_PROP_LUINT, { .longuint = (_b) }}, NULL, _f } /*! Shortcut macro to assign boolean capability type*/ -#define CAP_BOOL(_f, _a, _b) { .code=_a, .val={.type=GF_PROP_BOOL, .value.boolean = _b}, .flags=(_f) } +#define CAP_BOOL(_f, _a, _b) { _a, { GF_PROP_BOOL, { .boolean = (_b) }}, NULL, _f } /*! Shortcut macro to assign fixed-point number capability type*/ -#define CAP_FIXED(_f, _a, _b) { .code=_a, .val={.type=GF_PROP_FLOAT, .value.fnumber = _b}, .flags=(_f) } +#define CAP_FIXED(_f, _a, _b) { _a, { GF_PROP_FLOAT, { .fnumber = (_b) }}, NULL, _f } /*! Shortcut macro to assign float capability type*/ -#define CAP_FLOAT(_f, _a, _b) { .code=_a, .val={.type=GF_PROP_FLOAT, .value.fnumber = FLT2FIX(_b)}, .flags=(_f) } +#define CAP_FLOAT(_f, _a, _b) { _a, { GF_PROP_FLOAT, { .fnumber = FLT2FIX(_b) }}, NULL, _f } /*! Shortcut macro to assign 32-bit fraction capability type*/ -#define CAP_FRAC_INT(_f, _a, _b, _c) { .code=_a, .val={.type=GF_PROP_FRACTION, .value.frac.num = _b, .value.frac.den = _c}, .flags=(_f) } +#define CAP_FRAC_INT(_f, _a, _b, _c) { _a, { GF_PROP_FRACTION, { .frac = { .num = (_b), .den = (_c) }}}, NULL, _f } /*! Shortcut macro to assign 32-bit fraction capability type from integers*/ -#define CAP_FRAC(_f, _a, _b) { .code=_a, .val={.type=GF_PROP_FRACTION, .value.frac = _b}, .flags=(_f) } +#define CAP_FRAC(_f, _a, _b) { _a, { GF_PROP_FRACTION, { .frac = (_b) }}, NULL, _f } /*! Shortcut macro to assign double capability type*/ -#define CAP_DOUBLE(_f, _a, _b) { .code=_a, .val={.type=GF_PROP_DOUBLE, .value.number = _b}, .flags=(_f) } +#define CAP_DOUBLE(_f, _a, _b) { _a, { GF_PROP_DOUBLE, { .number = (_b) }}, NULL, _f } /*! Shortcut macro to assign name (const string) capability type*/ -#define CAP_NAME(_f, _a, _b) { .code=_a, .val={.type=GF_PROP_NAME, .value.string = _b}, .flags=(_f) } +#define CAP_NAME(_f, _a, _b) { _a, { GF_PROP_NAME, { .string = (char *)(_b) }}, NULL, _f } /*! Shortcut macro to assign string capability type*/ -#define CAP_STRING(_f, _a, _b) { .code=_a, .val={.type=GF_PROP_STRING, .value.string = _b}, .flags=(_f) } +#define CAP_STRING(_f, _a, _b) { _a, { GF_PROP_STRING, { .string = (char *)(_b) }}, NULL, _f } /*! Shortcut macro to assign unsigned integer capability type with capability priority*/ -#define CAP_UINT_PRIORITY(_f, _a, _b, _p) { .code=_a, .val={.type=GF_PROP_UINT, .value.uint = _b}, .flags=(_f), .priority=_p} +#define CAP_UINT_PRIORITY(_f, _a, _b, _p) { _a, { GF_PROP_UINT, { .uint = (_b) }}, NULL, _f, _p } /*! Flags for filter capabilities*/ enum @@ -2434,7 +2461,7 @@ typedef enum (through get_gl_texture callback) in the main GL thread*/ GF_FS_REG_CONFIGURE_MAIN_THREAD = 1<<2, /*! when set indicates the filter does not take part of dynamic filter chain resolution and can only be used by explicitly loading the filter - A filter with this flag and a \ref reconfigure_output callback set will be checked when loading a chain for PID property adaptation*/ + A filter with this flag and a \ref __gf_filter_register.reconfigure_output callback set will be checked when loading a chain for PID property adaptation*/ GF_FS_REG_EXPLICIT_ONLY = 1<<3, /*! when set ignores the filter weight during link resolution - this is typically needed by decoders requiring a specific reframing so that the weight of the reframer+decoder is the same as the weight of other decoders*/ GF_FS_REG_HIDE_WEIGHT = 1<<4, @@ -2749,6 +2776,18 @@ Bool gf_filter_is_temporary(GF_Filter *filter); */ const char *gf_filter_get_name(GF_Filter *filter); +/*! Gets filter status +\param filter target filter +\return status string of the filter if it exists, else empty string +*/ +const char *gf_filter_get_status(GF_Filter *filter); + +/*! Gets bytes processed by filter +\param filter target filter +\return the nb_bytes_processed field of the filter +*/ +u64 gf_filter_get_bytes_done(GF_Filter *filter); + /*! Makes the filter sticky. A sticky filter is not removed when all its input PIDs are disconnected. Typically used by the player \param filter target filter */ @@ -2775,7 +2814,7 @@ struct _gf_ft_mgr *gf_filter_get_font_manager(GF_Filter *filter); /*! Asks task reschedule for a given delay. There is no guarantee that the task will be recalled at exactly the desired delay The function can be called several times while in process, the smallest reschedule time will be kept. - + \param filter target filter \param us_until_next number of microseconds to wait before recalling this task */ @@ -2799,6 +2838,9 @@ GF_Err gf_filter_post_task(GF_Filter *filter, Bool (*task_execute) (GF_Filter *f /*! Sets callback function on source filter setup failure + + A filter with a non-NULL callback will never get destroyed by internal filter session logic, even if it no longer has valid connections. This ensures that a filter loading another filter can be sure this filter is a valid object or has failed to setup. Setting the callback to NULL may trigger the filter removal if needed, hence access to the filter should not happen after reseting the callback. + \param filter target filter \param source_filter the source filter to monitor \param on_setup_error callback function to call upon source setup error - the callback can return GF_TRUE to cancel error reporting @@ -3676,6 +3718,14 @@ void gf_filter_pid_remove(GF_FilterPid *PID); */ GF_Err gf_filter_pid_raw_new(GF_Filter *filter, const char *url, const char *local_file, const char *mime_type, const char *fext, const u8 *probe_data, u32 probe_size, Bool trust_mime, GF_FilterPid **out_pid); +/*! Creates an output PID for a gmem block, send packet as FILE and set created pid to EOS +\param filter the target filter +\param url gmem URL of the data block +\param out_pid the output PID to create or update. If no referer PID, a new PID will be created otherwise the PID will be updated +\return error code if any +*/ +GF_Err gf_filter_pid_raw_gmem(GF_Filter *filter, const char *url, GF_FilterPid **out_pid); + /*! Sets a new property on an output PID for built-in property names. Setting a new property will trigger a PID reconfigure at the consumption point of the next dispatched packet. Previous properties (ones set before last packet dispatch) will still be valid. You can remove any of them using \ref gf_filter_pid_set_property with NULL property, or reset the properties with \ref gf_filter_pid_reset_properties. @@ -4817,6 +4867,19 @@ GF_Err gf_filter_pck_set_sap(GF_FilterPacket *pck, GF_FilterSAPType sap_type); */ GF_FilterSAPType gf_filter_pck_get_sap(GF_FilterPacket *pck); +/*! Sets packet switch frame flag +\param pck target packet +\param is_switch_frame switch frame flag of the packet +\return error code if any +*/ +GF_Err gf_filter_pck_set_switch_frame(GF_FilterPacket *pck, Bool is_switch_frame); + +/*! Sets packet switch frame flag +\param pck target packet +\return switch frame flag of the packet +*/ +Bool gf_filter_pck_get_switch_frame(GF_FilterPacket *pck); + /*! Sets packet video interlacing flag \param pck target packet @@ -5095,7 +5158,7 @@ The app is responsible for assigning capabilities to the filter, and setting cal Each callback is optional, but a custom filter should at least have a process callback, and a configure_pid callback if not a source filter. Custom filters do not have any arguments exposed, and cannot be selected for sink or source filters. -If your app requires custom I/Os for source or sinks, use \ref GF_FileIO. +If your app requires custom I/Os for source or sinks, use \ref osfile_grp "GF_FileIO". @{ */ @@ -5162,4 +5225,3 @@ GF_Err gf_filter_set_probe_data_cbk(GF_Filter *filter, const char * (*probe_data #endif #endif //_GF_FILTERS_H_ - diff --git a/third_party/gpac/include/gpac/internal/id3.h b/third_party/gpac/include/gpac/id3.h similarity index 100% rename from third_party/gpac/include/gpac/internal/id3.h rename to third_party/gpac/include/gpac/id3.h diff --git a/third_party/gpac/include/gpac/internal/bifs_dev.h b/third_party/gpac/include/gpac/internal/bifs_dev.h index 2adcfa5c..81113782 100644 --- a/third_party/gpac/include/gpac/internal/bifs_dev.h +++ b/third_party/gpac/include/gpac/internal/bifs_dev.h @@ -2,7 +2,7 @@ * GPAC - Multimedia Framework C SDK * * Authors: Jean Le Feuvre - * Copyright (c) Telecom ParisTech 2000-2012 + * Copyright (c) Telecom ParisTech 2000-2025 * All rights reserved * * This file is part of GPAC / BIFS codec sub-project @@ -130,6 +130,7 @@ struct __tag_bifs_dec Bool is_com_dec; Double cts_offset; + u32 tree_depth; }; diff --git a/third_party/gpac/include/gpac/internal/ff_dmx.h b/third_party/gpac/include/gpac/internal/ff_dmx.h new file mode 100644 index 00000000..5f25bafd --- /dev/null +++ b/third_party/gpac/include/gpac/internal/ff_dmx.h @@ -0,0 +1,52 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Authors: Deniz Ugur + * Copyright (c) Motion Spell 2025 + * All rights reserved + * + * This file is part of GPAC / Media Tools sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#ifndef _GF_FF_DMX_H_ +#define _GF_FF_DMX_H_ + +#include + +#ifdef GPAC_HAS_FFMPEG + +#include +#include + +#include + +typedef enum +{ + GF_FFDMX_EOS = -2, + GF_FFDMX_ERR = -1, + GF_FFDMX_OK = 0, + GF_FFDMX_HAS_MORE = 1 +} GF_FFDemuxCallbackRet; + +typedef GF_FFDemuxCallbackRet (*GF_FFDemuxCallbackFn)(void *udta, AVPacket **pkt); + +GF_Err gf_filter_bind_ffdmx_callbacks(GF_Filter *filter, void *udta, GF_FFDemuxCallbackFn on_pkt); + +#endif // GPAC_HAS_FFMPEG + +#endif // _GF_FF_DMX_H_ + diff --git a/third_party/gpac/include/gpac/internal/isomedia_dev.h b/third_party/gpac/include/gpac/internal/isomedia_dev.h index fc96b85d..1561e23a 100644 --- a/third_party/gpac/include/gpac/internal/isomedia_dev.h +++ b/third_party/gpac/include/gpac/internal/isomedia_dev.h @@ -208,6 +208,10 @@ enum GF_ISOM_BOX_TYPE_AV1C = GF_4CC('a', 'v', '1', 'C'), GF_ISOM_BOX_TYPE_AV01 = GF_4CC('a', 'v', '0', '1'), + /*AVS3 Video*/ + GF_ISOM_BOX_TYPE_AV3C = GF_4CC('a', 'v', '3', 'c'), + GF_ISOM_BOX_TYPE_AVS3 = GF_4CC('a', 'v', 's', '3'), + /*WebM*/ GF_ISOM_BOX_TYPE_VPCC = GF_4CC('v', 'p', 'c', 'C'), GF_ISOM_BOX_TYPE_VP08 = GF_4CC('v', 'p', '0', '8'), @@ -250,6 +254,7 @@ enum GF_ISOM_BOX_TYPE_EMIB = GF_4CC( 'e', 'm', 'i', 'b' ), GF_ISOM_BOX_TYPE_EMEB = GF_4CC( 'e', 'm', 'e', 'b' ), GF_ISOM_BOX_TYPE_EVTE = GF_4CC( 'e', 'v', 't', 'e' ), + GF_ISOM_BOX_TYPE_SILB = GF_4CC( 's', 'i', 'l', 'b' ), /*3GPP text / MPEG-4 StreamingText*/ GF_ISOM_BOX_TYPE_FTAB = GF_4CC( 'f', 't', 'a', 'b' ), @@ -397,6 +402,8 @@ enum GF_ISOM_BOX_TYPE_DAC3 = GF_4CC( 'd', 'a', 'c', '3' ), GF_ISOM_BOX_TYPE_EC3 = GF_4CC( 'e', 'c', '-', '3' ), GF_ISOM_BOX_TYPE_DEC3 = GF_4CC( 'd', 'e', 'c', '3' ), + GF_ISOM_BOX_TYPE_AC4 = GF_4CC( 'a', 'c', '-', '4' ), + GF_ISOM_BOX_TYPE_DAC4 = GF_4CC( 'd', 'a', 'c', '4' ), GF_ISOM_BOX_TYPE_DVCC = GF_4CC( 'd', 'v', 'c', 'C' ), GF_ISOM_BOX_TYPE_DVVC = GF_4CC( 'd', 'v', 'v', 'C' ), GF_ISOM_BOX_TYPE_DVH1 = GF_4CC( 'd', 'v', 'h', '1' ), @@ -449,6 +456,8 @@ enum GF_ISOM_BOX_TYPE_IENC = GF_4CC( 'i', 'e', 'n', 'c' ), GF_ISOM_BOX_TYPE_IAUX = GF_4CC('i', 'a', 'u', 'x'), GF_ISOM_BOX_TYPE_ILCE = GF_4CC( 'i', 'l', 'c', 'e' ), + GF_ISOM_BOX_TYPE_TXLO = GF_4CC( 't', 'x', 'l', 'o' ), + GF_ISOM_BOX_TYPE_FNCH = GF_4CC( 'f', 'n', 'c', 'h' ), /* MIAF Boxes */ GF_ISOM_BOX_TYPE_CLLI = GF_4CC('c', 'l', 'l', 'i'), @@ -588,6 +597,7 @@ enum GF_GPAC_BOX_TYPE_SREF = GF_4CC( 'G', 'P', 'S', 'R' ), + GF_ISOM_BOX_TYPE_CDRF = GF_4CC( 'c', 'd', 'r', 'f' ), GF_ISOM_BOX_TYPE_CMOV = GF_4CC( '!', 'm', 'o', 'v' ), GF_ISOM_BOX_TYPE_CMOF = GF_4CC( '!', 'm', 'o', 'f' ), @@ -629,7 +639,10 @@ enum //internal flags (up to 16) //if flag is set, position checking of child boxes is ignored #define GF_ISOM_ORDER_FREEZE 1 +//if flag is set, box uses deflate compression #define GF_ISOM_BOX_COMPRESSED 2 +//if flag is set, box dump will skip size info +#define GF_ISOM_DUMP_SKIP_SIZE 4 /*the default size is 64, cause we need to handle large boxes... @@ -716,6 +729,7 @@ GF_Err gf_isom_box_parse(GF_Box **outBox, GF_BitStream *bs); GF_Err gf_isom_box_array_read(GF_Box *s, GF_BitStream *bs); GF_Err gf_isom_box_parse_ex(GF_Box **outBox, GF_BitStream *bs, u32 parent_type, Bool is_root_box, u64 parent_size); +GF_Err gf_isom_parse_root_box(GF_Box **outBox, GF_BitStream *bs, u32 *boxType, u64 *bytesExpected, Bool progressive_mode); //writes box header - shall be called at the beginning of each xxxx_Write function //this function is not factorized in order to let box serializer modify box type before writing @@ -1372,7 +1386,7 @@ typedef struct u32 avgBitrate; } GF_BitRateBox; -GF_BitRateBox *gf_isom_sample_entry_get_bitrate(GF_SampleEntryBox *ent, Bool create); +GF_BitRateBox *gf_isom_sample_entry_get_bitrate_box(GF_SampleEntryBox *ent, Bool create); typedef struct { @@ -1551,6 +1565,14 @@ typedef struct GF_VPConfig *config; } GF_VPConfigurationBox; + +typedef struct +{ + GF_ISOM_BOX + GF_AVS3VConfig *config; +} GF_AVS3VConfigurationBox; + + typedef struct { GF_ISOM_FULL_BOX @@ -1657,6 +1679,8 @@ typedef struct __full_video_sample_entry GF_AV1ConfigurationBox *av1_config; /*vp8-9 extension*/ GF_VPConfigurationBox *vp_config; + /*avs3 video extension*/ + GF_AVS3VConfigurationBox *avs3v_config; /*jp2k extension*/ GF_J2KHeaderBox *jp2h; /*dolbyvision extension*/ @@ -1730,6 +1754,12 @@ typedef struct GF_AC3Config cfg; } GF_AC3ConfigBox; +typedef struct +{ + GF_ISOM_BOX + GF_AC4Config cfg; +} GF_AC4ConfigBox; + typedef struct { GF_ISOM_FULL_BOX @@ -1797,8 +1827,8 @@ typedef struct typedef struct { - GF_ISOM_BOX - GF_IAConfig *cfg; + GF_ISOM_BOX + GF_IAConfig *cfg; } GF_IAConfigurationBox; @@ -1855,6 +1885,9 @@ typedef struct __full_audio_sample_entry //for AC3/EC3 audio GF_AC3ConfigBox *cfg_ac3; + //for AC4 audio + GF_AC4ConfigBox *cfg_ac4; + //for AC3/EC3 audio GF_TrueHDConfigBox *cfg_mlp; @@ -1867,8 +1900,8 @@ typedef struct __full_audio_sample_entry //for FLAC GF_FLACConfigBox *cfg_flac; - //for IAMF - GF_IAConfigurationBox *cfg_iamf; + //for IAMF + GF_IAConfigurationBox *cfg_iamf; //for generic audio sample entry //box type as specified in the file (not this box's type!!) @@ -2198,6 +2231,9 @@ typedef struct GF_ISOM_FULL_BOX GF_List *entries; u32 id_shift; + + //for cdrf + u32 cdrf_entries, cdrf_cache_size; } GF_SampleReferences; typedef struct @@ -2771,6 +2807,7 @@ typedef struct //temp storage of prft box GF_ISOTrackID reference_track_ID; u64 ntp, timestamp; + u32 prft_at_mux; //emsg to inject before moof, not part of the moof hierarchy ! GF_List *emsgs; @@ -3392,11 +3429,28 @@ typedef struct u32 message_data_size; } GF_EventMessageBox; +typedef struct +{ + char *scheme_id_uri; + char *value; + u8 reserved : 7; + u8 atleast_once_flag : 1; +} GF_SchemeIdListBoxEntry; + +typedef struct +{ + GF_ISOM_FULL_BOX + u32 number_of_schemes; + GF_List *schemes; /*list of GF_SchemeIdListBoxEntry*/ + u8 reserved : 7; + u8 other_schemes_flag : 1; +} GF_SchemeIdListBox; + typedef struct { GF_ISOM_SAMPLE_ENTRY_FIELDS GF_BitRateBox *btrt; - /*GF_SchemeIdListBox*/void *silb; //'silb' box, not implemented yet + GF_SchemeIdListBox *silb; } GF_EventMessageSampleEntryBox; @@ -3548,6 +3602,12 @@ typedef struct u8 *data; } GF_DefaultSampleGroupDescriptionEntry; +/*AV1 Switching Entry - Switching Frames*/ +typedef struct +{ + int unused; // C requires that a struct or union has at least one member +} GF_AV1SwitchingEntry; + /*VisualRandomAccessEntry - 'rap ' type*/ typedef struct { @@ -3979,6 +4039,29 @@ typedef struct { u32 aux_info_parameter; } GF_AuxiliaryInfoPropertyBox; + +typedef struct { + GF_ISOM_FULL_BOX + char* font_family; + char* font_style; + char* font_weight; +} GF_FontCharacteristicsPropertyBox; + + +typedef struct { + GF_ISOM_FULL_BOX + u32 reference_width; + u32 reference_height; + s32 x; + s32 y; + u32 width; + u32 height; + s16 font_size; + char* direction; + char* writing_mode; +} GF_TextLayoutPropertyBox; + + typedef struct { GF_ISOM_FULL_BOX @@ -4377,7 +4460,7 @@ void gf_isom_set_last_error(GF_ISOFile *the_file, GF_Err error); GF_Err gf_isom_parse_movie_boxes(GF_ISOFile *mov, u32 *boxType, u64 *bytesMissing, Bool progressive_mode); GF_ISOFile *gf_isom_new_movie(); /*Movie and Track access functions*/ -GF_TrackBox *gf_isom_get_track_from_file(GF_ISOFile *the_file, u32 trackNumber); +GF_TrackBox *gf_isom_get_track_box(GF_ISOFile *the_file, u32 trackNumber); GF_TrackBox *gf_isom_get_track(GF_MovieBox *moov, u32 trackNumber); GF_TrackBox *gf_isom_get_track_from_id(GF_MovieBox *moov, GF_ISOTrackID trackID); GF_TrackBox *gf_isom_get_track_from_original_id(GF_MovieBox *moov, u32 originalID, u32 originalFile); @@ -4469,7 +4552,6 @@ GF_Err gf_isom_set_sample_group_description_internal(GF_ISOFile *movie, u32 trac GF_Err isom_on_block_out(void *cbk, u8 *data, u32 block_size); GF_Err FlushCaptureMode(GF_ISOFile *movie); -GF_Err CanAccessMovie(GF_ISOFile *movie, GF_ISOOpenMode Mode); GF_ISOFile *gf_isom_create_movie(const char *fileName, GF_ISOOpenMode OpenMode, const char *tmp_dir); GF_Err gf_isom_insert_moov(GF_ISOFile *file); @@ -4543,7 +4625,7 @@ GF_Err gf_isom_flush_sidx(GF_ISOFile *movie, u32 sidx_max_size, Bool force_v1); #endif /*GPAC_DISABLE_ISOM_WRITE*/ Bool gf_isom_is_identical_sgpd(void *ptr1, void *ptr2, u32 grouping_type); -void sgpd_del_entry(u32 grouping_type, void *entry); +void sgpd_del_entry(u32 grouping_type, void *entry, Bool is_opaque); /*return type is either GF_DefaultSampleGroupDescriptionEntry if opaque sample group, or the structure associated with the grouping type*/ void *gf_isom_get_sample_group_info_entry(GF_ISOFile *the_file, GF_TrackBox *trak, u32 grouping_type, u32 sample_description_index, u32 *default_index, GF_SampleGroupDescriptionBox **out_sgdp); @@ -4598,9 +4680,6 @@ void gf_isom_parse_trif_info(const u8 *data, u32 size, u32 *id, u32 *independent Bool gf_isom_is_encrypted_entry(u32 entryType); -//too export in constants -Bool gf_cenc_validate_key_info(const u8 *key_info, u32 key_info_size); - GF_Err gf_isom_add_sample_aux_info_internal(GF_TrackBox *trak, void *_traf, u32 sampleNumber, u32 aux_type, u32 aux_info, u8 *data, u32 size); @@ -4915,7 +4994,8 @@ Bool gf_isom_box_equal(GF_Box *a, GF_Box *b); GF_Box *gf_isom_clone_config_box(GF_Box *box); GF_Err gf_isom_box_dump(void *ptr, FILE * trace); -GF_Err gf_isom_box_array_dump(GF_List *list, FILE * trace); +GF_Err gf_isom_box_dump_ex(void *ptr, FILE * trace, Bool subtree_root); +GF_Err gf_isom_box_array_dump(GF_List *list, FILE * trace, u16 parent_internal_flags); void gf_isom_registry_disable(u32 boxCode, Bool disable); @@ -4932,7 +5012,6 @@ GF_Box *gf_isom_create_meta_extensions(GF_ISOFile *mov, u32 meta_type); #ifndef GPAC_DISABLE_ISOM_DUMP -GF_Err gf_isom_box_dump_ex(void *ptr, FILE * trace, u32 box_4cc); GF_Err gf_isom_box_dump_start(GF_Box *a, const char *name, FILE * trace); GF_Err gf_isom_box_dump_start_ex(GF_Box *a, const char *name, FILE * trace, Bool force_version, const char *spec, const char *container); void gf_isom_box_dump_done(const char *name, GF_Box *ptr, FILE *trace); diff --git a/third_party/gpac/include/gpac/internal/m3u8.h b/third_party/gpac/include/gpac/internal/m3u8.h index 96ebdb76..3ca241ea 100644 --- a/third_party/gpac/include/gpac/internal/m3u8.h +++ b/third_party/gpac/include/gpac/internal/m3u8.h @@ -2,7 +2,7 @@ * GPAC - Multimedia Framework C SDK * * Authors: Pierre Souchay - Jean Le Feuvre - Romain Bouqueau - * Copyright (c) Telecom ParisTech 2010-2021 + * Copyright (c) Telecom ParisTech 2010-2025 * All rights reserved * * This file is part of GPAC @@ -55,7 +55,7 @@ typedef struct s_playList Playlist; typedef enum e_playlistElementType { TYPE_PLAYLIST, TYPE_MEDIA, TYPE_UNKNOWN } PlaylistElementType; -typedef enum e_playlistElementDRMMethod { DRM_NONE, DRM_AES_128, DRM_CENC } PlaylistElementDRMMethod; +typedef enum e_playlistElementDRMMethod { DRM_NONE, DRM_AES_128, DRM_CENC_CBCS, DRM_CENC_CTR } PlaylistElementDRMMethod; typedef enum _e_MediaType { MEDIA_TYPE_UNKNOWN = 0, diff --git a/third_party/gpac/include/gpac/internal/media_dev.h b/third_party/gpac/include/gpac/internal/media_dev.h index eff21eab..1d49e0d3 100644 --- a/third_party/gpac/include/gpac/internal/media_dev.h +++ b/third_party/gpac/include/gpac/internal/media_dev.h @@ -295,7 +295,9 @@ typedef struct s32 extra_filter; //- removes the sei, + keeps the sei } SEI_Filter; - +#ifdef __cplusplus +extern "C" { +#endif /*return sps ID or -1 if error*/ s32 gf_avc_read_sps(const u8 *sps_data, u32 sps_size, AVCState *avc, u32 subseq_sps, u32 *vui_flag_pos); s32 gf_avc_read_sps_bs(GF_BitStream *bs, AVCState *avc, u32 subseq_sps, u32 *vui_flag_pos); @@ -314,6 +316,10 @@ s32 gf_avc_parse_nalu(GF_BitStream *bs, AVCState *avc); /*remove SEI messages not allowed in MP4*/ /*nota: 'buffer' remains unmodified but cannot be set const*/ u32 gf_avc_reformat_sei(u8 *buffer, u32 nal_size, Bool isobmf_rewrite, AVCState *avc, SEI_Filter *sei_filter); +#ifdef __cplusplus +} +#endif + #ifndef GPAC_DISABLE_AV_PARSERS @@ -521,8 +527,13 @@ typedef struct u32 dimension_id[MAX_LHVC_LAYERS][16]; u32 layer_id_in_nuh[MAX_LHVC_LAYERS]; u32 layer_id_in_vps[MAX_LHVC_LAYERS]; - - u8 num_profile_tier_level, num_output_layer_sets; + u8 num_direct_ref_layers[64]; + u8 num_ref_list_layers[64]; + u8 num_profile_tier_level, num_output_layer_sets, default_ref_layers_active_flag; + u8 id_direct_ref_layers[64][MAX_LHVC_LAYERS]; + u8 layer_idx_in_vps[MAX_LHVC_LAYERS]; + u8 sub_layers_vps_max_minus1[MAX_LHVC_LAYERS]; + u8 max_tid_il_ref_pics_plus1[MAX_LHVC_LAYERS][MAX_LHVC_LAYERS]; u32 profile_level_tier_idx[MAX_LHVC_LAYERS]; HEVC_ProfileTierLevel ext_ptl[MAX_LHVC_LAYERS]; @@ -539,11 +550,12 @@ typedef struct Bool necessary_layers_flag[MAX_LHVC_LAYERS][MAX_LHVC_LAYERS]; u8 LayerSetLayerIdList[MAX_LHVC_LAYERS][MAX_LHVC_LAYERS]; u8 LayerSetLayerIdListMax[MAX_LHVC_LAYERS]; //the highest value in LayerSetLayerIdList[i] + u8 max_one_active_ref_layer_flag; } HEVC_VPS; typedef struct { - u8 nal_unit_type; + u8 nal_unit_type, layer_id, temporal_id; u32 frame_num, poc_lsb, slice_type, header_size_with_emulation; s32 redundant_pic_cnt; @@ -869,11 +881,18 @@ typedef struct _vvc_state u32 parse_mode; } VVCState; +#ifdef __cplusplus +extern "C" { +#endif s32 gf_vvc_parse_nalu_bs(GF_BitStream *bs, VVCState *vvc, u8 *nal_unit_type, u8 *temporal_id, u8 *layer_id); void gf_vvc_parse_sei(char* buffer, u32 nal_size, VVCState *vvc); u32 gf_vvc_reformat_sei(char *buffer, u32 nal_size, Bool isobmf_rewrite, SEI_Filter *sei_filter); Bool gf_vvc_slice_is_ref(VVCState *vvc); s32 gf_vvc_parse_nalu(u8 *data, u32 size, VVCState *vvc, u8 *nal_unit_type, u8 *temporal_id, u8 *layer_id); +#ifdef __cplusplus +} +#endif + void gf_vvc_parse_ps(GF_VVCConfig* hevccfg, VVCState* vvc, u32 nal_type); @@ -887,7 +906,10 @@ GF_Err gf_media_parse_ivf_file_header(GF_BitStream *bs, u32 *width, u32*height, GF_Err gf_vp9_parse_sample(GF_BitStream *bs, GF_VPConfig *vp9_cfg, Bool *key_frame, u32 *FrameWidth, u32 *FrameHeight, u32 *renderWidth, u32 *renderHeight); GF_Err gf_vp9_parse_superframe(GF_BitStream *bs, u64 ivf_frame_size, u32 *num_frames_in_superframe, u32 frame_sizes[VP9_MAX_FRAMES_IN_SUPERFRAME], u32 *superframe_index_size); - +typedef struct +{ + GF_AC4Config *config; +} AC4State; #define AV1_MAX_TILE_ROWS 64 #define AV1_MAX_TILE_COLS 64 @@ -910,7 +932,7 @@ typedef struct { Bool is_first_frame; Bool seen_frame_header, seen_seq_header; - Bool key_frame, show_frame; + Bool key_frame, switch_frame, show_frame; AV1FrameType frame_type; GF_List *header_obus, *frame_obus; /*GF_AV1_OBUArrayEntry*/ AV1Tile tiles[AV1_MAX_TILE_ROWS * AV1_MAX_TILE_COLS]; @@ -1064,7 +1086,7 @@ GF_Err gf_av1_parse_obu_header(GF_BitStream *bs, ObuType *obu_type, Bool *obu_ex typedef struct { - Bool seen_valid_ia_seq_header; + Bool seen_valid_iamf_seq_header; Bool seen_first_frame; Bool previous_obu_is_descriptor; @@ -1083,7 +1105,11 @@ typedef struct typedef struct { + // Determined based on Sequence Header OBU. + u8 primary_profile; + u8 additional_profile; // Determined based on Codec Config OBU. + u32 codec_id; int num_samples_per_frame; int sample_size; int sample_rate; @@ -1235,7 +1261,7 @@ typedef struct _webvtt_parser GF_WebVTTParser; typedef struct _webvtt_sample GF_WebVTTSample; GF_WebVTTParser *gf_webvtt_parser_new(); -GF_Err gf_webvtt_parser_init(GF_WebVTTParser *parser, FILE *vtt_file, s32 unicode_type, Bool is_srt, +GF_Err gf_webvtt_parser_init(GF_WebVTTParser *parser, FILE **vtt_file, s32 unicode_type, Bool is_srt, void *user, GF_Err (*report_message)(void *, GF_Err, char *, const char *), void (*on_sample_parsed)(void *, GF_WebVTTSample *), void (*on_header_parsed)(void *, const char *)); diff --git a/third_party/gpac/include/gpac/isomedia.h b/third_party/gpac/include/gpac/isomedia.h index 85bd0028..3766097e 100644 --- a/third_party/gpac/include/gpac/isomedia.h +++ b/third_party/gpac/include/gpac/isomedia.h @@ -169,8 +169,8 @@ enum { /*base media types*/ GF_ISOM_MEDIA_VISUAL = GF_4CC( 'v', 'i', 'd', 'e' ), - GF_ISOM_MEDIA_AUXV = GF_4CC( 'a', 'u', 'x', 'v' ), - GF_ISOM_MEDIA_PICT = GF_4CC( 'p', 'i', 'c', 't' ), + GF_ISOM_MEDIA_AUXV = GF_4CC( 'a', 'u', 'x', 'v' ), + GF_ISOM_MEDIA_PICT = GF_4CC( 'p', 'i', 'c', 't' ), GF_ISOM_MEDIA_AUDIO = GF_4CC( 's', 'o', 'u', 'n' ), GF_ISOM_MEDIA_HINT = GF_4CC( 'h', 'i', 'n', 't' ), GF_ISOM_MEDIA_META = GF_4CC( 'm', 'e', 't', 'a' ), @@ -247,12 +247,15 @@ enum /*AV1 media type*/ GF_ISOM_SUBTYPE_AV01 = GF_4CC('a', 'v', '0', '1'), + /*AVS3 Video media type*/ + GF_ISOM_SUBTYPE_AVS3 = GF_4CC('a', 'v', 's', '3'), + /*Opus media type*/ GF_ISOM_SUBTYPE_OPUS = GF_4CC('O', 'p', 'u', 's'), GF_ISOM_SUBTYPE_FLAC = GF_4CC( 'f', 'L', 'a', 'C' ), - /*IAMF media type*/ - GF_ISOM_SUBTYPE_IAMF = GF_4CC('i', 'a', 'm', 'f'), + /*IAMF media type*/ + GF_ISOM_SUBTYPE_IAMF = GF_4CC('i', 'a', 'm', 'f'), /* VP */ GF_ISOM_SUBTYPE_VP08 = GF_4CC('v', 'p', '0', '8'), @@ -279,6 +282,7 @@ enum GF_ISOM_SUBTYPE_AC3 = GF_4CC( 'a', 'c', '-', '3' ), GF_ISOM_SUBTYPE_EC3 = GF_4CC( 'e', 'c', '-', '3' ), + GF_ISOM_SUBTYPE_AC4 = GF_4CC( 'a', 'c', '-', '4' ), GF_ISOM_SUBTYPE_MP3 = GF_4CC( '.', 'm', 'p', '3' ), GF_ISOM_SUBTYPE_MLPA = GF_4CC( 'm', 'l', 'p', 'a' ), @@ -488,9 +492,11 @@ enum GF_ISOM_BRAND_AV01 = GF_4CC( 'a', 'v', '0', '1'), + GF_ISOM_BRAND_CAV3 = GF_4CC( 'c', 'a', 'v', '3'), // AVS 3 Video + GF_ISOM_BRAND_OPUS = GF_4CC( 'O', 'p', 'u', 's'), - GF_ISOM_BRAND_IAMF = GF_4CC( 'i', 'a', 'm', 'f'), + GF_ISOM_BRAND_IAMF = GF_4CC( 'i', 'a', 'm', 'f'), GF_ISOM_BRAND_ISMA = GF_4CC( 'I', 'S', 'M', 'A' ), @@ -594,7 +600,7 @@ typedef struct GF_ISOSAPType IsRAP; /*! allocated data size - used only when using static sample in \ref gf_isom_get_sample_ex*/ u32 alloc_size; - + /*! number of packed samples in this sample. If 0 or 1, only 1 sample is present only used for constant size and constant duration samples*/ u32 nb_pack; @@ -692,6 +698,13 @@ GF_Err gf_isom_close(GF_ISOFile *isom_file); */ void gf_isom_delete(GF_ISOFile *isom_file); +/*! Checks if an open movie can be accessed in the given mode +\param isom_file the target ISO file +\param mode the desried open mode +\return GF_OK if access is possible, GF_ISOM_INVALID_MODE if access is not possible, GF_BAD_PARAM otherwise +*/ +GF_Err gf_isom_can_access_movie(GF_ISOFile *isom_file, GF_ISOOpenMode Mode); + /*! gets the last fatal error that occured in the file ANY FUNCTION OF THIS API WON'T BE PROCESSED IF THE FILE HAS AN ERROR \note Some function may return an error while the movie has no error @@ -1340,7 +1353,7 @@ GF_Err gf_isom_get_chapter(GF_ISOFile *isom_file, u32 trackNumber, u32 Index, u6 /*! checks if a media has sync points \param isom_file the target ISO file \param trackNumber the target track -\return 0 if the media has no sync point info (eg, all samples are RAPs), 1 if the media has sync points (eg some samples are RAPs), 2 if the media has empty sync point info (no samples are RAPs - this will likely only happen +\return 0 if the media has no sync point info (eg, all samples are RAPs), 1 if the media has sync points (eg some samples are RAPs), 2 if the media has empty sync point info (no samples are RAPs - this will likely only happen in scalable context) */ u8 gf_isom_has_sync_points(GF_ISOFile *isom_file, u32 trackNumber); @@ -1937,19 +1950,19 @@ GF_Err gf_isom_load_extra_boxes(GF_ISOFile *isom_file, u8 *moov_boxes, u32 moov_ u32 gf_isom_new_track(GF_ISOFile *isom_file, GF_ISOTrackID trackID, u32 MediaType, u32 TimeScale); /*! creates a new track from an encoded trak box. -\param isom_file the target ISO file -\param trackID the ID of the track - if 0, the track ID is chosen by the API +\param movie the target ISO file +\param trakID the ID of the track - if 0, the track ID is chosen by the API \param MediaType the handler type (four character code) of the media \param TimeScale the time scale of the media \param tk_box a serialized trak box to use as template \param tk_box_size the size of the serialized trak box \param udta_only only replace/inject udta box and entries \return the track number or 0 if error*/ -u32 gf_isom_new_track_from_template(GF_ISOFile *isom_file, GF_ISOTrackID trackID, u32 MediaType, u32 TimeScale, u8 *tk_box, u32 tk_box_size, Bool udta_only); +u32 gf_isom_new_track_from_template(GF_ISOFile *movie, GF_ISOTrackID trakID, u32 MediaType, u32 TimeScale, u8 *tk_box, u32 tk_box_size, Bool udta_only); /*! creates a new external track -\param isom_file the target ISO file -\param trackID the ID of the track- if 0, the track ID is chosen by the API +\param movie the target ISO file +\param trakID the ID of the track- if 0, the track ID is chosen by the API \param refTrakID the ID of the referenced track (not checked by API) \param MediaType the handler type (four character code) of the media \param TimeScale the time scale of the media @@ -2234,7 +2247,7 @@ GF_Err gf_isom_remove_sample(GF_ISOFile *isom_file, u32 trackNumber, u32 sampleN \param force_rescale_type type fo rescaling, Ignored if new_tsinc is not 0: - if set to 0, rescale timings. - if set to 1, only the media timescale is changed but media times are not updated. - - if set to 2, media timescale is updated if new_timescale is set, and all sample durations are set to new_tsinc + - if set to 2, media timescale is updated if new_timescale is set, and all sample durations are set to new_tsinc \return GF_EOS if no action taken (same config), or error if any */ GF_Err gf_isom_set_media_timescale(GF_ISOFile *isom_file, u32 trackNumber, u32 new_timescale, u32 new_tsinc, u32 force_rescale_type); @@ -2296,9 +2309,9 @@ GF_OPT_ENUM (GF_ISOCompressMode, GF_ISOM_COMP_MOOF, /*! only moof and sidx boxes are compressed*/ GF_ISOM_COMP_MOOF_SIDX, - /*! only moof, sidx and ssix boxes are compressed*/ + /*! only moof, sidx and ssix boxes are compressed*/ GF_ISOM_COMP_MOOF_SSIX, - /*! all (moov, moof, sidx and ssix) boxes are compressed*/ + /*! all (moov, moof, sidx and ssix) boxes are compressed*/ GF_ISOM_COMP_ALL, ); @@ -2383,10 +2396,10 @@ GF_Err gf_isom_set_alternate_group_id(GF_ISOFile *isom_file, u32 trackNumber, u3 */ GF_Err gf_isom_add_chapter(GF_ISOFile *isom_file, u32 trackNumber, u64 timestamp, char *name); -/*! deletes copyright +/*! deletes chapter \param isom_file the target ISO file \param trackNumber the target track -\param index the 1-based index of the copyright notice to remove, or 0 to remove all chapters +\param index the 1-based index of the chapter notice to remove, or 0 to remove all chapters \return error if any */ GF_Err gf_isom_remove_chapter(GF_ISOFile *isom_file, u32 trackNumber, u32 index); @@ -2403,7 +2416,7 @@ the media normal timing. EditTime and EditDuration are expressed in movie timesc \param EditDuration the duration of the edit in movie timecale \param MediaTime the corresponding media time of the start of the edit, in media timescale. -1 for empty edits \param EditMode the edit mode -\return error if any, GF_EOS if empty edit was inserted +\return error if any, GF_EOS if empty edit was inserted */ GF_Err gf_isom_set_edit(GF_ISOFile *isom_file, u32 trackNumber, u64 EditTime, u64 EditDuration, u64 MediaTime, GF_ISOEditType EditMode); @@ -2818,6 +2831,20 @@ if both average_bitrate and max_bitrate are 0, this removes any bitrate informat GF_Err gf_isom_update_bitrate(GF_ISOFile *isom_file, u32 trackNumber, u32 sampleDescriptionIndex, u32 average_bitrate, u32 max_bitrate, u32 decode_buffer_size); +/*! updates average and max bitrate of a sample description +if both average_bitrate and max_bitrate are 0, this removes any bitrate information +\param isom_file the target ISO file +\param trackNumber the target track number +\param sampleDescriptionIndex the target sample description +\param average_bitrate the average bitrate of the media for that sample description +\param max_bitrate the maximum bitrate of the media for that sample description +\param decode_buffer_size the decoder buffer size in bytes for that sample description +\param forced_for_mpeg4 if set to TRUE, the bitrate box will be added/removed even for MPEG-4 systems entries (mp4a, mp4v, mp4s) where the info is usually in the esds +\return error if any +*/ +GF_Err gf_isom_update_bitrate_ex(GF_ISOFile *isom_file, u32 trackNumber, u32 sampleDescriptionIndex, u32 average_bitrate, u32 max_bitrate, u32 decode_buffer_size, Bool forced_for_mpeg4); + + /*! track clone flags*/ typedef enum { @@ -2959,7 +2986,7 @@ Bool gf_isom_is_inplace_rewrite(GF_ISOFile *isom_file); - specifying a storage mode using \ref gf_isom_set_storage_mode - removing or adding tracks or items - removing, adding or updating samples - - using stdout, redirect file "_gpac_isobmff_redirect", memory file " gmem://" + - using stdout, redirect file "_gpac_isobmff_redirect", memory file " gmem://" In-place rewriting is enabled by default on files open in edit mode. @@ -3169,8 +3196,6 @@ GF_Err gf_isom_add_desc_to_description(GF_ISOFile *isom_file, u32 trackNumber, u */ GF_Err gf_isom_clone_pl_indications(GF_ISOFile *orig_file, GF_ISOFile *dest_file); -/*deletes chapter (1-based index, index 0 for all)*/ -GF_Err gf_isom_remove_chapter(GF_ISOFile *the_file, u32 trackNumber, u32 index); /*! associates a given SL config with a given ESD while extracting the OD information This is useful while reading the IOD / OD stream of an MP4 file. Note however that @@ -3313,6 +3338,15 @@ GF_Err gf_isom_set_rvc_config(GF_ISOFile *isom_file, u32 trackNumber, u32 sample */ GF_Err gf_isom_update_video_sample_entry_fields(GF_ISOFile *isom_file, u32 trackNumber, u32 sampleDescriptionIndex, u16 revision, u32 vendor, u32 temporalQ, u32 spatialQ, u32 horiz_res, u32 vert_res, u16 frames_per_sample, const char *compressor_name, s16 color_table_index); +/*! Sets all sample descriptions from a serialized sample description box, removing all child boxes of stsd +\param isom_file the target ISO file +\param trackNumber the target track number +\param stsd_data a serialized sample description box +\param stsd_data_size size of the serialized sample description +\return error if any +*/ +GF_Err gf_isom_set_track_stsd_templates(GF_ISOFile *isom_file, u32 trackNumber, u8 *stsd_data, u32 stsd_data_size); + /*! updates a sample description from a serialized sample description box. Only child boxes are removed in the process \param isom_file the target ISO file \param trackNumber the target track number @@ -3323,7 +3357,6 @@ GF_Err gf_isom_update_video_sample_entry_fields(GF_ISOFile *isom_file, u32 track */ GF_Err gf_isom_update_sample_description_from_template(GF_ISOFile *isom_file, u32 trackNumber, u32 sampleDescriptionIndex, u8 *data, u32 size); - /*! creates a new unknown StreamDescription in the file. \note use this to store media not currently supported by the ISO media format or media types not implemented in this library \param isom_file the target ISO file @@ -3552,6 +3585,14 @@ GF_AV1Config *gf_isom_av1_config_get(GF_ISOFile *isom_file, u32 trackNumber, u32 */ GF_VPConfig *gf_isom_vp_config_get(GF_ISOFile *isom_file, u32 trackNumber, u32 sampleDescriptionIndex); +/*! gets AVS3 Video config for a sample description +\param isom_file the target ISO file +\param trackNumber the target track +\param sampleDescriptionIndex the target sample description index +\return the AVS3 Video config - user is responsible for deleting it +*/ +GF_AVS3VConfig *gf_isom_avs3v_config_get(GF_ISOFile *isom_file, u32 trackNumber, u32 sampleDescriptionIndex); + /*! gets DOVI config for a sample description \param isom_file the target ISO file \param trackNumber the target track @@ -3560,6 +3601,14 @@ GF_VPConfig *gf_isom_vp_config_get(GF_ISOFile *isom_file, u32 trackNumber, u32 s */ GF_DOVIDecoderConfigurationRecord* gf_isom_dovi_config_get(GF_ISOFile* isom_file, u32 trackNumber, u32 sampleDescriptionIndex); +/*! gets IAMF config for a sample description +\param isom_file the target ISO file +\param trackNumber the target track +\param sampleDescriptionIndex the target sample description index +\return the IAMF config - user is responsible for deleting it +*/ +GF_IAConfig* gf_isom_iamf_config_get(GF_ISOFile* isom_file, u32 trackNumber, u32 sampleDescriptionIndex); + /*! checks if some tracks in file needs layer reconstruction \param isom_file the target ISO file \return GF_TRUE if track dependencies implying extractors or implicit reconstruction are found, GF_FALSE otherwise @@ -3814,7 +3863,6 @@ GF_Err gf_isom_vvc_config_update(GF_ISOFile *isom_file, u32 trackNumber, u32 sam */ GF_Err gf_isom_vp_config_new(GF_ISOFile *isom_file, u32 trackNumber, GF_VPConfig *cfg, const char *URLname, const char *URNname, u32 *outDescriptionIndex, u32 vpx_type); - /*! creates new AV1 config \param isom_file the target ISO file \param trackNumber the target track @@ -3826,16 +3874,28 @@ GF_Err gf_isom_vp_config_new(GF_ISOFile *isom_file, u32 trackNumber, GF_VPConfig */ GF_Err gf_isom_av1_config_new(GF_ISOFile *isom_file, u32 trackNumber, GF_AV1Config *cfg, const char *URLname, const char *URNname, u32 *outDescriptionIndex); +/*! creates new AVS 3 Video config +\param isom_file the target ISO file +\param trackNumber the target track +\param cfg the AVS3 Video config for this sample description +\param URLname URL value of the data reference, NULL if no data reference (media in the file) +\param URNname URN value of the data reference, NULL if no data reference (media in the file) +\param outDescriptionIndex set to the index of the created sample description +\return error if any +*/ +GF_Err gf_isom_avs3v_config_new(GF_ISOFile *isom_file, u32 trackNumber, GF_AVS3VConfig *cfg, const char *URLname, const char *URNname, u32 *outDescriptionIndex); + + /*! creates new IAMF config \param isom_file the target ISO file \param trackNumber the target track -\param cfg the IA config for this sample description +\param cfg the IAMF config for this sample description \param URLname URL value of the data reference, NULL if no data reference (media in the file) \param URNname URN value of the data reference, NULL if no data reference (media in the file) \param outDescriptionIndex set to the index of the created sample description \return error if any */ -GF_Err gf_isom_ia_config_new(GF_ISOFile *isom_file, u32 trackNumber, GF_IAConfig *cfg, const char *URLname, const char *URNname, u32 *outDescriptionIndex); +GF_Err gf_isom_iamf_config_new(GF_ISOFile *isom_file, u32 trackNumber, GF_IAConfig *cfg, const char *URLname, const char *URNname, u32 *outDescriptionIndex); #endif /*GPAC_DISABLE_ISOM_WRITE*/ @@ -3932,6 +3992,68 @@ GF_Err gf_isom_ac3_config_update(GF_ISOFile *isom_file, u32 trackNumber, u32 sam #endif /*GPAC_DISABLE_ISOM_WRITE*/ +/*! gets an AC4 sample description +\param isom_file the target ISO file +\param trackNumber the target track +\param sampleDescriptionIndex the target sample description index +\return AC-4 config +*/ +GF_AC4Config *gf_isom_ac4_config_get(GF_ISOFile *isom_file, u32 trackNumber, u32 sampleDescriptionIndex); + +#ifndef GPAC_DISABLE_ISOM_WRITE +/*! creates an AC4 sample description +\param isom_file the target ISO file +\param trackNumber the target track +\param cfg the AC4 config for this sample description +\param URLname URL value of the data reference, NULL if no data reference (media in the file) +\param URNname URN value of the data reference, NULL if no data reference (media in the file) +\param outDescriptionIndex set to the index of the created sample description +\return error if any +*/ +GF_Err gf_isom_ac4_config_new(GF_ISOFile *isom_file, u32 trackNumber, GF_AC4Config *cfg, const char *URLname, const char *URNname, u32 *outDescriptionIndex); + +/*! updates an AC4 sample description +\param isom_file the target ISO file +\param trackNumber the target track +\param sampleDescriptionIndex the target sample description index +\param cfg the AC4 config for this sample description +\return error if any +*/ +GF_Err gf_isom_ac4_config_update(GF_ISOFile *isom_file, u32 trackNumber, u32 sampleDescriptionIndex, GF_AC4Config *cfg); + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +/*! gets an AC4 sample description +\param isom_file the target ISO file +\param trackNumber the target track +\param sampleDescriptionIndex the target sample description index +\return AC-4 config +*/ +GF_AC4Config *gf_isom_ac4_config_get(GF_ISOFile *isom_file, u32 trackNumber, u32 sampleDescriptionIndex); + +#ifndef GPAC_DISABLE_ISOM_WRITE +/*! creates an AC4 sample description +\param isom_file the target ISO file +\param trackNumber the target track +\param cfg the AC4 config for this sample description +\param URLname URL value of the data reference, NULL if no data reference (media in the file) +\param URNname URN value of the data reference, NULL if no data reference (media in the file) +\param outDescriptionIndex set to the index of the created sample description +\return error if any +*/ +GF_Err gf_isom_ac4_config_new(GF_ISOFile *isom_file, u32 trackNumber, GF_AC4Config *cfg, const char *URLname, const char *URNname, u32 *outDescriptionIndex); + +/*! updates an AC4 sample description +\param isom_file the target ISO file +\param trackNumber the target track +\param sampleDescriptionIndex the target sample description index +\param cfg the AC4 config for this sample description +\return error if any +*/ +GF_Err gf_isom_ac4_config_update(GF_ISOFile *isom_file, u32 trackNumber, u32 sampleDescriptionIndex, GF_AC4Config *cfg); + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + /*! gets TrueHD sample description info \param isom_file the target ISO file \param trackNumber the target track @@ -4101,7 +4223,7 @@ GF_Err gf_isom_get_lpcm_config(GF_ISOFile *isom_file, u32 trackNumber, u32 sampl \param URLname URL value of the data reference, NULL if no data reference (media in the file) \param URNname URN value of the data reference, NULL if no data reference (media in the file) \param outDescriptionIndex set to the index of the created sample description -\param dsi the MPEGH audio config (payload of mhaC box): byte[0]=1 (config version) ,byte[1]=ProfileLevel, byte[2]=channel layout, byte[3],byte[4]: the size of what follows the rest being a mpegh3daConfig +\param dsi the MPEGH audio config (payload of mhaC box): byte[0]=1 (config version) ,byte[1]=ProfileLevel, byte[2]=channel layout, byte[3],byte[4]: the size of what follows the rest being a mpegh3daConfig \param dsi_size the size of the MPEGH audio config \param mha_subtype mha1/mha2:/mhm1/mhm2 subtype to use \return error if any @@ -4785,9 +4907,10 @@ GF_Err gf_isom_flush_fragments(GF_ISOFile *isom_file, Bool last_segment); \param reference_track_ID the ID of the track used as a reference for media timestamps \param ntp absolute NTP time \param timestamp media time corresponding to the NTP time, in reference track media timescale +\param at_mux whether the box should also contain the ntp time of when the movie fragment is written \return error if any */ -GF_Err gf_isom_set_fragment_reference_time(GF_ISOFile *isom_file, GF_ISOTrackID reference_track_ID, u64 ntp, u64 timestamp); +GF_Err gf_isom_set_fragment_reference_time(GF_ISOFile *isom_file, GF_ISOTrackID reference_track_ID, u64 ntp, u64 timestamp, Bool at_mux); /*! writes an empty sidx in the current movie. @@ -4972,6 +5095,16 @@ GF_Err gf_isom_fragment_set_sample_roll_group(GF_ISOFile *isom_file, GF_ISOTrack */ GF_Err gf_isom_fragment_set_sample_rap_group(GF_ISOFile *isom_file, GF_ISOTrackID trackID, u32 sample_number_in_frag, Bool is_rap, u32 num_leading_samples); +/*! sets AV1 Switching Frame information for a sample in a track fragment +\param isom_file the target ISO file +\param trackID the ID of the target track +\param sample_number_in_frag the sample number of the sample in the traf +\param is_rap set to GF_TRUE to indicate the sample is a RAP sample (open-GOP), GF_FALSE otherwise +\param num_leading_samples set to the number of leading pictures for a RAP sample +\return error if any +*/ +GF_Err gf_isom_fragment_set_sample_av1_switch_frame_group(GF_ISOFile *isom_file, GF_ISOTrackID trackID, u32 sample_number_in_frag, Bool is_switch_Frame); + /*! sets sample dependency flags in a track fragment - see ISO/IEC 14496-12 and \ref gf_filter_pck_set_dependency_flags \param isom_file the target ISO file \param trackID the ID of the target track @@ -5997,20 +6130,21 @@ GF_Err gf_isom_set_cenc_protection_mkey(GF_ISOFile *isom_file, u32 trackNumber, \param pssh_mode 0: regular PSSH in moov, 1: PIFF PSSH in moov, 2: regular PSSH in meta \return error if any */ -GF_Err gf_cenc_set_pssh(GF_ISOFile *isom_file, bin128 systemID, u32 version, u32 KID_count, bin128 *KID, u8 *data, u32 len, u32 pssh_mode); +GF_Err gf_isom_cenc_set_pssh(GF_ISOFile *isom_file, bin128 systemID, u32 version, u32 KID_count, bin128 *KID, u8 *data, u32 len, u32 pssh_mode); /*! removes CENC senc box info \param isom_file the target ISO file \param trackNumber the target track \return error if any */ -GF_Err gf_isom_remove_samp_enc_box(GF_ISOFile *isom_file, u32 trackNumber); +GF_Err gf_isom_remove_cenc_senc_box(GF_ISOFile *isom_file, u32 trackNumber); + /*! removes all CENC sample groups \param isom_file the target ISO file \param trackNumber the target track \return error if any */ -GF_Err gf_isom_remove_samp_group_box(GF_ISOFile *isom_file, u32 trackNumber); +GF_Err gf_isom_remove_cenc_seig_sample_group(GF_ISOFile *isom_file, u32 trackNumber); #endif //GPAC_DISABLE_ISOM_WRITE @@ -6495,6 +6629,24 @@ GF_Err gf_isom_meta_get_next_item_id(GF_ISOFile *isom_file, Bool root_meta, u32 */ GF_Err gf_isom_add_meta_item(GF_ISOFile *isom_file, Bool root_meta, u32 track_num, Bool self_reference, char *resource_path, const char *item_name, u32 item_id, u32 item_type, const char *mime_type, const char *content_encoding, const char *URL, const char *URN, GF_ImageItemProperties *image_props); +/*! adds an item to a meta box from file (same as \ref gf_isom_add_meta_item but outputs the item's id in io_item_id) +\param isom_file the target ISO file +\param root_meta if GF_TRUE uses meta at the file, otherwise uses meta at the movie level if track number is 0 +\param track_num if GF_TRUE and root_meta is GF_FALSE, uses meta at the track level +\param self_reference if GF_TRUE, indicates that the item is in fact the entire container file +\param resource_path path to the file to add +\param item_name name of the item +\param io_item_id pointer to the item's id. If the pointed value is 0 or the same as an existing item, it is set to the item's newly attributed id +\param item_type four character code of item type +\param mime_type mime type of the item, can be NULL +\param content_encoding content encoding of the item, can be NULL +\param URL URL of the item for external data reference (data is not contained in meta parent file) +\param URN URN of the item for external data reference (data is not contained in meta parent file) +\param image_props image properties information for image items +\return error if any +*/ +GF_Err gf_isom_add_meta_item2(GF_ISOFile *isom_file, Bool root_meta, u32 track_num, Bool self_reference, char *resource_path, const char *item_name, u32 *io_item_id, u32 item_type, const char *mime_type, const char *content_encoding, const char *URL, const char *URN, GF_ImageItemProperties *image_props); + #endif //GPAC_DISABLE_ISOM /*! item extend description*/ @@ -6925,7 +7077,7 @@ GF_Err gf_isom_apple_set_tag(GF_ISOFile *isom_file, GF_ISOiTunesTag tag, const u \param data tag data buffer or string to parse \param data_len size of the tag data buffer. If data is NULL and and data_len not 0, removes the given tag \param int_val value for integer/boolean tags. If data and data_len are set, parse data as string to get the value -\param int_val2 value for fractional tags. If data and data_len are set, parse data as string to get the value +\param int_val2 value for fractional tags. If data and data_len are set, parse data as string to get the value \param name domain name of tag, ignores for_tag if not null \param mean mean of tag, ignores for_tag if not null \param locale locale of tag, ignored if name and mean are null @@ -7135,6 +7287,7 @@ enum { GF_ISOM_SAMPLE_GROUP_VIPR = GF_4CC( 'v', 'i', 'p', 'r'), //p15 GF_ISOM_SAMPLE_GROUP_LBLI = GF_4CC( 'l', 'b', 'l', 'i'), //p15 GF_ISOM_SAMPLE_GROUP_3GAG = GF_4CC( '3', 'g', 'a', 'g'), //3gpp + GF_ISOM_SAMPLE_GROUP_AV1S = GF_4CC( 'a', 'v', '1', 's'), //av1-isobmff GF_ISOM_SAMPLE_GROUP_AVCB = GF_4CC( 'a', 'v', 'c', 'b'), //avif GF_ISOM_SAMPLE_GROUP_SPOR = GF_4CC( 's', 'p', 'o', 'r'), //p15 GF_ISOM_SAMPLE_GROUP_SULM = GF_4CC( 's', 'u', 'l', 'm'), //p15 @@ -7168,7 +7321,7 @@ Bool gf_isom_get_sample_group_info(GF_ISOFile *isom_file, u32 trackNumber, u32 s \param trackNumber the target track \param sample_number sample number to query \param grouping_type four character code of grouping type of sample group description to query -\param grouping_type_parameter grouping type parameter of sample group description to query +\param grouping_type_parameter grouping type parameter of sample group description to query \param sampleGroupDescIndex set to the 1-based sample group description index, or 0 if no sample group of this type is associated \return error if any */ @@ -7239,6 +7392,18 @@ GF_Err gf_isom_enum_sample_aux_data(GF_ISOFile *isom_file, u32 trackNumber, u32 */ GF_Err gf_isom_set_sample_rap_group(GF_ISOFile *isom_file, u32 trackNumber, u32 sampleNumber, Bool is_rap, u32 num_leading_samples); +/*! sets AV1 Switching Frame info for sample_number +\warning Sample group info MUST be added in order (no insertion in the tables) + +\param isom_file the target ISO file +\param trackNumber the target track +\param sampleNumber the target sample number +\param is_rap indicates if the sample is a RAP (open gop) sample +\param num_leading_samples indicates the number of leading samples (samples after this RAP that have dependences on samples before this RAP and hence should be discarded when tuning in) +\return error if any +*/ +GF_Err gf_isom_set_sample_av1_switch_frame_group(GF_ISOFile *isom_file, u32 trackNumber, u32 sampleNumber, Bool is_switch_Frame); + /*! sets roll_distance info for sample_number (number of frames before (<0) or after (>0) this sample to have a complete refresh of the decoded data (used by GDR in AVC) \warning Sample group info MUST be added in order (no insertion in the tables) @@ -7368,5 +7533,3 @@ GF_Err gf_isom_fragment_add_sample_references(GF_ISOFile *isom_file, GF_ISOTrack #endif /*_GF_ISOMEDIA_H_*/ - - diff --git a/third_party/gpac/include/gpac/list.h b/third_party/gpac/include/gpac/list.h index e2142c6e..2e7a9c89 100644 --- a/third_party/gpac/include/gpac/list.h +++ b/third_party/gpac/include/gpac/list.h @@ -149,7 +149,7 @@ void gf_list_reset(GF_List *ptr); /*! \brief gets last item -Gets last item o fthe list +Gets last item of the list \param ptr target list object \return the last item */ diff --git a/third_party/gpac/include/gpac/main.h b/third_party/gpac/include/gpac/main.h index e85afe88..cfe8d4ad 100644 --- a/third_party/gpac/include/gpac/main.h +++ b/third_party/gpac/include/gpac/main.h @@ -140,7 +140,7 @@ typedef struct #define GF_ARG_SUBSYS_AUDIO (1<<10) /*! argument applies to the font and text subsystem*/ #define GF_ARG_SUBSYS_TEXT (1<<11) -/*! argument applies to the remotery subsystem*/ +/*! argument applies to the rmtws subsystem*/ #define GF_ARG_SUBSYS_RMT (1<<12) /*! argument belongs to hack tools, usually never used*/ #define GF_ARG_SUBSYS_HACKS (1<<13) @@ -265,4 +265,3 @@ Bool gf_sys_word_match(const char *orig, const char *dst); #endif #endif //_GF_MAIN_H_ - diff --git a/third_party/gpac/include/gpac/media_tools.h b/third_party/gpac/include/gpac/media_tools.h index 35c55f9f..40997dd2 100644 --- a/third_party/gpac/include/gpac/media_tools.h +++ b/third_party/gpac/include/gpac/media_tools.h @@ -1133,6 +1133,14 @@ GF_Err gf_dasher_print_session_info(GF_DASHSegmenter *dasher, u32 fs_print_flags */ GF_Err gf_dasher_keep_source_utc(GF_DASHSegmenter *dasher, Bool keep_utc); +/*! + Keeps hls info used in -crypt and pass to dasher +\param dasher the DASH segmenter object +\param hls_info use for HLS playlist in -crypt, if NULL no HLS info is set +\return error if any +*/ +GF_Err gf_dasher_set_hls_info(GF_DASHSegmenter *dasher, char *hls_info); + #ifndef GPAC_DISABLE_ISOM_FRAGMENTS /*! save file as fragmented movie diff --git a/third_party/gpac/include/gpac/mediaobject.h b/third_party/gpac/include/gpac/mediaobject.h index 73eb1650..abfbd38f 100644 --- a/third_party/gpac/include/gpac/mediaobject.h +++ b/third_party/gpac/include/gpac/mediaobject.h @@ -168,7 +168,7 @@ typedef enum //always resync the content of the decoded media buffer to the current time (used for video) GF_MO_FETCH_RESYNC, //never resync the content of the decoded media buffer (used fo audio) - //if clock is paused, do fetch (used for audio extraction) + //if clock is paused, do fetch (used for audio extraction) GF_MO_FETCH_PAUSED } GF_MOFetchMode; diff --git a/third_party/gpac/include/gpac/mpd.h b/third_party/gpac/include/gpac/mpd.h index e6093df0..fbe90197 100644 --- a/third_party/gpac/include/gpac/mpd.h +++ b/third_party/gpac/include/gpac/mpd.h @@ -676,6 +676,8 @@ typedef struct { const char *hls_single_file_name; /*! number of audio channels - HLS only*/ u32 nb_chan; + /*! CHANNELS attribute in string for special content - HLS only*/ + char str_chan[20]; /*! video FPS - HLS only*/ Double fps; /*! groupID (for HLS)*/ @@ -691,7 +693,7 @@ typedef struct { /*! temp file for m3u8 generation*/ FILE *m3u8_var_file; - /*! for m3u8: 0: not encrypted, 1: full segment, 2: CENC*/ + /*! for m3u8: 0: not encrypted, 1: full segment, 2: CENC CBC, 2: CENC CTR*/ u8 crypto_type; u8 def_kms_used; @@ -717,6 +719,8 @@ typedef struct { Bool sub_forced; const char *hls_forced; + + const char *init_base64; } GF_MPD_Representation; /*! AdaptationSet*/ @@ -902,9 +906,9 @@ typedef struct { char *profiles; /*! offering type*/ GF_MPD_Type type; - /*! UTC of availability start anchor, expressed in milliseconds, MANDATORY if type=dynamic*/ + /*! UTC of availability start anchor, expressed in milliseconds, MANDATORY if type=dynamic*/ u64 availabilityStartTime; - /*! UTC of availability end anchor, expressed in milliseconds*/ + /*! UTC of availability end anchor, expressed in milliseconds*/ u64 availabilityEndTime; /*! UTC of last publishing of the manifest*/ u64 publishTime; diff --git a/third_party/gpac/include/gpac/mpeg4_odf.h b/third_party/gpac/include/gpac/mpeg4_odf.h index a12efc0c..6863f86a 100644 --- a/third_party/gpac/include/gpac/mpeg4_odf.h +++ b/third_party/gpac/include/gpac/mpeg4_odf.h @@ -2,7 +2,7 @@ * GPAC - Multimedia Framework C SDK * * Authors: Jean Le Feuvre - * Copyright (c) Telecom ParisTech 2000-2024 + * Copyright (c) Telecom ParisTech 2000-2025 * All rights reserved * * This file is part of GPAC / MPEG-4 Object Descriptor sub-project @@ -347,7 +347,7 @@ enum IPMP_CP_CM = 3, /*control point in BIFS tree (???)*/ IPMP_CP_BIFS = 4 - /*the rest is reserved or forbidden(0xFF)*/ + /*the rest is reserved or forbidden(0xFF)*/ }; /*! IPMPX base classe*/ @@ -1123,6 +1123,14 @@ typedef struct { u8 force_dv; } GF_DOVIDecoderConfigurationRecord; +/*! AVS3 Video av3C */ +typedef struct { + u8 configurationVersion; + u16 sequence_header_length; + u8* sequence_header; // 8*sequence_header_length bits + u8 library_dependency_idc; // 6 bits reserved at '1' + 2 bits +} GF_AVS3VConfig; + /*! Media Segment Descriptor used for Media Control Extensions*/ typedef struct { @@ -1565,6 +1573,40 @@ GF_Err gf_odf_dovi_cfg_write_bs(GF_DOVIDecoderConfigurationRecord *cfg, GF_BitSt \param cfg the DolbyVision config to destroy*/ void gf_odf_dovi_cfg_del(GF_DOVIDecoderConfigurationRecord *cfg); +/*! creates a AVS3 Video descriptor +\return a newly allocated descriptor +*/ +GF_AVS3VConfig *gf_odf_avs3v_cfg_new(); +/*! AVS3 Video config constructor +\param cfg the AVS3 Video config to destroy*/ +void gf_odf_avs3v_cfg_del(GF_AVS3VConfig *cfg); +/*! writes AVS3 Video config to bitstream +\param cfg the AVS3 Video config to write +\param bs bitstream containing the encoded AVS3 Video decoder specific info +\param is_v0 if GF_TRUE, this is a version 0 config +\return error code if any +*/ +GF_Err gf_odf_avs3v_cfg_write_bs(GF_AVS3VConfig *cfg, GF_BitStream *bs); +/*! writes AVS3 Video config to buffer +\param cfg the AVS3 Video config to write +\param outData set to an allocated encoded buffer - it is the caller responsibility to free this +\param outSize set to the encoded buffer size +\param is_v0 if GF_TRUE, this is a version 0 config +\return error if any +*/ +GF_Err gf_odf_avs3v_cfg_write(GF_AVS3VConfig *cfg, u8 **outData, u32 *outSize); +/*! gets AVS3 Video config from bitstream +\param bs bitstream containing the encoded AV1 decoder specific info +\return the decoded AVS3 Video config +*/ +GF_AVS3VConfig *gf_odf_avs3v_cfg_read_bs(GF_BitStream *bs); +/*! gets AVS3 Video config from buffer +\param dsi encoded AVS3 Video config +\param dsi_size size of encoded AVS3 Video config +\return the decoded AVS3 Video config +*/ +GF_AVS3VConfig *gf_odf_avs3v_cfg_read(u8 *dsi, u32 dsi_size); + /*! AC-3 and E-AC3 stream info */ typedef struct __ec3_stream @@ -1640,7 +1682,7 @@ GF_Err gf_odf_ac3_cfg_write(GF_AC3Config *cfg, u8 **data, u32 *size); \param cfg the AC3/EC3 config to fill \return Error if any */ -GF_Err gf_odf_ac3_config_parse(u8 *dsi, u32 dsi_len, Bool is_ec3, GF_AC3Config *cfg); +GF_Err gf_odf_ac3_cfg_parse(u8 *dsi, u32 dsi_len, Bool is_ec3, GF_AC3Config *cfg); /*! parses an AC3/EC3 sample description from bitstream \param bs the bitstream object @@ -1648,8 +1690,205 @@ GF_Err gf_odf_ac3_config_parse(u8 *dsi, u32 dsi_len, Bool is_ec3, GF_AC3Config * \param cfg the AC3/EC3 config to fill \return Error if any */ -GF_Err gf_odf_ac3_config_parse_bs(GF_BitStream *bs, Bool is_ec3, GF_AC3Config *cfg); +GF_Err gf_odf_ac3_cfg_parse_bs(GF_BitStream *bs, Bool is_ec3, GF_AC3Config *cfg); + +typedef struct { + u8 b_4_back_channels_present; + u8 b_centre_present; + u8 top_channels_present; + u8 dsi_sf_multiplier; + u8 b_substream_bitrate_indicator; + u8 substream_bitrate_indicator; + u32 dsi_substream_channel_mask; + u8 b_ajoc; + u8 b_static_dmx; + u8 n_dmx_objects_minus1; + u8 n_umx_objects_minus1; + u8 b_substream_contains_bed_objects; + u8 b_substream_contains_dynamic_objects; + u8 b_substream_contains_ISF_objects; + + // auxiliary information, used to parse the frame + u8 b_lfe; + u32 ch_mode; +} GF_AC4SubStream; +typedef struct { + u8 b_substreams_present; + u8 b_hsf_ext; + u8 b_channel_coded; + u8 b_content_type; + u8 content_classifier; + u8 b_language_indicator; + u8 n_language_tag_bytes; + u8 language_tag_bytes[64]; // n_language_tag_bytes is 6 bits + u8 dolby_atmos_indicator; + u8 n_lf_substreams; + GF_List *substreams; // GF_AC4SubStream +} GF_AC4SubStreamGroupV1; + +typedef struct { + u8 bit_rate_mode; + u32 bit_rate; + u32 bit_rate_precision; +} GF_AC4BitrateDsi; + +typedef struct { + u16 name_len; + u8 presentation_name[256]; // restrict to 256 char + u8 n_targets; + u8 target_md_compat[32]; + u8 target_device_category[32]; +} GF_AC4AlternativeInfo; + +typedef struct { + u8 presentation_version; + u8 presentation_config; + u8 mdcompat; + u8 b_presentation_id; + u8 presentation_id; + u8 dsi_frame_rate_multiply_info; + u8 dsi_frame_rate_fraction_info; + u8 presentation_emdf_version; + u16 presentation_key_id; + u8 b_presentation_channel_coded; + u8 dsi_presentation_ch_mode; + u8 pres_b_4_back_channels_present; + u8 pres_top_channel_pairs; + u32 presentation_channel_mask_v1; + u8 b_presentation_core_differs; + u8 b_presentation_core_channel_coded; + u8 dsi_presentation_channel_mode_core; + u8 b_presentation_filter; + u8 b_enable_presentation; + u8 n_filter_bytes; + u8 b_multi_pid; + u8 n_skip_bytes; + u8 b_pre_virtualized; + u8 b_add_emdf_substreams; + u8 n_add_emdf_substreams; + u8 substream_emdf_version[128]; + u16 substream_key_id[128]; + u8 b_presentation_bitrate_info; + GF_AC4BitrateDsi ac4_bitrate_dsi; + u8 b_alternative; + GF_AC4AlternativeInfo alternative_info; + u8 de_indicator; + u8 dolby_atmos_indicator; + u8 b_extended_presentation_id; + u16 extended_presentation_id; + u8 n_substream_groups; + GF_List *substream_groups; // GF_AC4SubStreamGroupV1 + + // auxiliary information, not exist in DSI + GF_List *substream_group_indexs; +} GF_AC4PresentationV1; + +/*! AC-4 stream info */ +typedef struct __ac4_stream +{ + u8 ac4_dsi_version; + u8 bitstream_version; + u8 fs_index; + u8 frame_rate_index; + u8 b_iframe_global; + u8 b_program_id; + u16 short_program_id; + u8 b_uuid; + u8 program_uuid[16]; + GF_AC4BitrateDsi ac4_bitrate_dsi; + u16 n_presentations; + GF_List *presentations; // GF_AC4PresentationV1 +} GF_AC4StreamInfo; + +/*! AC4 config record - see dolby specs ETSI TS 103 190 */ +/* please use gf_odf_ac4_cfg_deep_copy(), gf_odf_ac4_cfg_clean_list() to copy and destroy GF_AC4Config*/ +typedef struct __ac4_config +{ + /*! streams info */ + GF_AC4StreamInfo stream; + /*! sample rate */ + u32 sample_rate; + /*! size of the complete frame*/ + u32 frame_size; + /* channel count */ + u32 channel_count; + /* sample_delta [units of media time scale] */ + u32 sample_duration; + /* media time scale [1/sec]*/ + u32 media_time_scale; + /* sync frame header size */ + u32 header_size; + /* sync frame CRC size */ + u32 crc_size; + /* frame toc size */ + u32 toc_size; +} GF_AC4Config; + +#define GF_AC4_DESCMODE_PARSE 0 +#define GF_AC4_DESCMODE_WRITE 1 +#define GF_AC4_DESCMODE_GETSIZE 2 + +/*! parse/write/get the size of Dolby AC4 DSI V1 +\param dsi the GF_AC4StreamInfo to parse/write/get the size +\param bs the bitstream object in which to write the config +\param size the address of the size +\param desc_mode the mode should be GF_AC4_DESCMODE_PARSE/GF_AC4_DESCMODE_WRITE/GF_AC4_DESCMODE_GETSIZE +\return error if any +*/ +GF_Err gf_odf_ac4_cfg_dsi_v1(GF_AC4StreamInfo *dsi, GF_BitStream *bs, u64 *size, u8 desc_mode); + +/*! writes Dolby AC4 config to buffer +\param cfg the Dolby AC4 config to write +\param bs the bitstream object in which to write the config +\return error if any +*/ +GF_Err gf_odf_ac4_cfg_write_bs(GF_AC4Config *cfg, GF_BitStream *bs); + +/*! writes Dolby AC4 config to buffer +\param cfg the Dolby AC4 config to write +\param data set to created output buffer, must be freed by caller +\param size set to created output buffer size +\return error if any +*/ +GF_Err gf_odf_ac4_cfg_write(GF_AC4Config *cfg, u8 **data, u32 *size); + +/*! parses an AC4 sample description +\param dsi the encoded config +\param dsi_len the encoded config size +\param cfg the AC4 config to fill +\return Error if any +*/ +GF_Err gf_odf_ac4_cfg_parse(u8 *dsi, u32 dsi_len, GF_AC4Config *cfg); + +/*! parses an AC4 sample description from bitstream +\param bs the bitstream object +\param cfg the AC4 config to fill +\return Error if any +*/ +GF_Err gf_odf_ac4_cfg_parse_bs(GF_BitStream *bs, GF_AC4Config *cfg); + +/*! get the size of an AC4 sample description from bitstream +\param cfg the AC4 config to fill +\return 0 if any +*/ +u64 gf_odf_ac4_cfg_size(GF_AC4Config *cfg); + +/*! copy the GF_AC4Config +\param dst the address of AC4 config to fill +\param src the address of source +*/ +void gf_odf_ac4_cfg_deep_copy(GF_AC4Config *dst, GF_AC4Config *src); + +/*! clean the GF_List data in GF_AC4Config +\param hdr the address of AC4 config to clean +*/ +void gf_odf_ac4_cfg_clean_list(GF_AC4Config *hdr); + +/*! destroy the GF_AC4Config +\param cfg the address of AC4 config to destroy +*/ +void gf_odf_ac4_cfg_del(GF_AC4Config *cfg); /*! Opus decoder config*/ @@ -1710,27 +1949,27 @@ GF_Err gf_odf_opus_cfg_parse_bs(GF_BitStream *bs, GF_OpusConfig *cfg); /*! Used for storing IAMF OBUs */ typedef struct { - /* Size of raw_obu_bytes, including the header and payload. - * This is different from `obu_size` in the IAMF spec Section 3.2, - * which includes only the partial header size and the payload. - */ - u64 obu_length; - int obu_type; /* IamfObuType */ - u8* raw_obu_bytes; + /* Size of raw_obu_bytes, including the header and payload. + * This is different from `obu_size` in the IAMF spec Section 3.2, + * which includes only the partial header size and the payload. + */ + u64 obu_length; + int obu_type; /* IamfObuType */ + u8* raw_obu_bytes; } GF_IamfObu; /*! Used for storing the IAMF configuration from the `iacb` box */ typedef struct { - u8 configurationVersion; - u32 configOBUs_size; - GF_List *configOBUs; /* GF_IamfObu */ + u8 configurationVersion; + u32 configOBUs_size; + GF_List *configOBUs; /* GF_IamfObu */ } GF_IAConfig; /*! IAMF config constructor \return the created config */ -GF_IAConfig *gf_odf_ia_cfg_new(); +GF_IAConfig *gf_odf_iamf_cfg_new(); /*! writes IAMF config to buffer \param cfg the IAMF config to write @@ -1738,45 +1977,45 @@ GF_IAConfig *gf_odf_ia_cfg_new(); \param outSize set to the encoded dsi buffer size \return error if any */ -GF_Err gf_odf_ia_cfg_write(GF_IAConfig *cfg, u8 **outData, u32 *outSize); +GF_Err gf_odf_iamf_cfg_write(GF_IAConfig *cfg, u8 **outData, u32 *outSize); /*! Writes the IAMF config to bitstream \param cfg the IAMF config to write \param bs the bitstream object \return error code if any */ -GF_Err gf_odf_ia_cfg_write_bs(GF_IAConfig *cfg, GF_BitStream *bs); +GF_Err gf_odf_iamf_cfg_write_bs(GF_IAConfig *cfg, GF_BitStream *bs); /*! IAMF config destructor \param cfg the IAMF config to destroy */ -void gf_odf_ia_cfg_del(GF_IAConfig *cfg); +void gf_odf_iamf_cfg_del(GF_IAConfig *cfg); /*! gets GF_IAConfig from MPEG-4 DSI -\param dsi encoded IA decoder specific info -\param dsi_size encoded IA decoder specific info size +\param dsi encoded IAMF decoder specific info +\param dsi_size encoded IAMF decoder specific info size \return the decoded IAMF config */ -GF_IAConfig *gf_odf_ia_cfg_read(u8 *dsi, u32 dsi_size); +GF_IAConfig *gf_odf_iamf_cfg_read(u8 *dsi, u32 dsi_size); /*! Reads the IAMF config from the bitstream \param bs bitstream containing the encoded IAMF descriptors \return the IAMF config */ -GF_IAConfig *gf_odf_ia_cfg_read_bs(GF_BitStream *bs); +GF_IAConfig *gf_odf_iamf_cfg_read_bs(GF_BitStream *bs); /*! Reads the IAMF config from the bitstream \param bs bitstream containing the encoded IAMF descriptors - \param size size of the encoded structure in the bitstream. A value of 0 means "until the end", equivalent to gf_odf_ia_cfg_read_bs + \param size size of the encoded structure in the bitstream. A value of 0 means "until the end", equivalent to gf_odf_iamf_cfg_read_bs \return the IAMF config */ -GF_IAConfig *gf_odf_ia_cfg_read_bs_size(GF_BitStream *bs, u32 size); +GF_IAConfig *gf_odf_iamf_cfg_read_bs_size(GF_BitStream *bs, u32 size); /*! Returns the size of the IAMF config \param cfg the IAMF config \return 0 if error, otherwise the IAMF config size */ -u32 gf_odf_ia_cfg_size(GF_IAConfig *cfg); +u32 gf_odf_iamf_cfg_size(GF_IAConfig *cfg); /*! destroy the descriptors in a list but not the list \param descList descriptor list to destroy diff --git a/third_party/gpac/include/gpac/mpegts.h b/third_party/gpac/include/gpac/mpegts.h index 648ec95b..9c09379e 100644 --- a/third_party/gpac/include/gpac/mpegts.h +++ b/third_party/gpac/include/gpac/mpegts.h @@ -282,6 +282,11 @@ typedef enum GF_M2TS_HLS_AAC_CRYPT = 0xcf, GF_M2TS_HLS_AVC_CRYPT = 0xdb, + GF_M2TS_VIDEO_AVS2 = 0xD2, + GF_M2TS_AUDIO_AVS2 = 0xD3, + GF_M2TS_VIDEO_AVS3 = 0xD4, + GF_M2TS_AUDIO_AVS3 = 0xD5, + /*the rest is internal use*/ GF_M2TS_VIDEO_VC1 = 0xEA, @@ -295,6 +300,7 @@ typedef enum GF_M2TS_SUBTITLE_DVB = 0x100, GF_M2TS_AUDIO_OPUS = 0x101, GF_M2TS_VIDEO_AV1 = 0x102, + GF_M2TS_AUDIO_AC4 = 0x103, GF_M2TS_DVB_TELETEXT = 0x152, GF_M2TS_DVB_VBI = 0x153, @@ -319,12 +325,15 @@ enum GF_M2TS_RA_STREAM_DTS3 = GF_4CC('D','T','S','3'), GF_M2TS_RA_STREAM_OPUS = GF_4CC('O','p','u','s'), GF_M2TS_RA_STREAM_DOVI = GF_4CC('D','O','V','I'), + GF_M2TS_RA_STREAM_AVSA = GF_4CC('A','V','S','A'), // AVS2-3 Audio + GF_M2TS_RA_STREAM_AVSV = GF_4CC('A','V','S','V'), // AVS2-3 Video GF_M2TS_RA_STREAM_AV1 = GF_4CC('A','V','0','1'), GF_M2TS_RA_STREAM_SCTE35 = GF_4CC('C','U','E','I'), GF_M2TS_RA_STREAM_GPAC = GF_4CC('G','P','A','C'), GF_M2TS_RA_STREAM_SRT = GF_4CC('S','R','T',' '), - GF_M2TS_RA_STREAM_TXT = GF_4CC('T','E','X','T') + GF_M2TS_RA_STREAM_TXT = GF_4CC('T','E','X','T'), + GF_M2TS_RA_STREAM_AC4 = GF_4CC('A','C','-','4'), }; @@ -359,18 +368,12 @@ typedef struct tag_m2ts_section_es GF_M2TS_SECTION_ES; /*! Maximum number of service in a TS*/ #define GF_M2TS_MAX_SERVICES 65535 -/*! Maximum size of the buffer in UDP */ -#ifdef WIN32 -#define GF_M2TS_UDP_BUFFER_SIZE 0x80000 -#else -//fixme - issues on linux and OSX with large stack size -//we need to change default stack size for TS thread -#define GF_M2TS_UDP_BUFFER_SIZE 0x40000 -#endif - /*! Maximum PCR value */ #define GF_M2TS_MAX_PCR 2576980377811ULL +/*! Maximum PCR value in 90Khz scale */ +#define GF_M2TS_MAX_PCR_90K 8589934592 + /*! gets the stream name for an MPEG-2 stream type \param streamType the target stream type \return name of the stream type*/ @@ -710,6 +713,8 @@ typedef struct /*! continuity counter check for pure PCR PIDs*/ s16 pcr_cc; + s64 pcr_base_offset; + void *user; } GF_M2TS_Program; @@ -795,6 +800,7 @@ typedef struct u64 DTS; /*! size of PES header*/ u8 hdr_data_len; + u8 has_pts; } GF_M2TS_PESHeader; /*! Section elementary stream*/ @@ -905,6 +911,8 @@ typedef struct tag_m2ts_pes GF_M2TS_DVB_Subtitling_Descriptor sub; /*! Metadata descriptor (for ID3)*/ GF_M2TS_MetadataDescriptor *metadata_descriptor; + /*! AVS3 Video descriptors*/ + u8 avs3_video_descriptor[10]; /*! last received TEMI payload*/ u8 *temi_tc_desc; @@ -1539,7 +1547,7 @@ typedef struct __elementary_stream_ifce u32 gpac_meta_dsi_size; /*! GPAC unmapped meta codec decoder config*/ u8 *gpac_meta_dsi; - /*! GPAC unmapped meta codec name if knwon*/ + /*! GPAC unmapped meta codec name if known*/ const char *gpac_meta_name; } GF_ESInterface; @@ -1672,7 +1680,7 @@ typedef struct __m2ts_mux_stream { is available in PES, don't copy from next*/ u32 min_bytes_copy_from_next; /*! process PES or table update/framing - returns the priority of the stream, 0 meaning not scheduled, 1->N highest priority sent first*/ + returns the priority of the stream, 0 meaning not scheduled, 1->N highest priority sent first*/ u32 (*process)(struct __m2ts_mux *muxer, struct __m2ts_mux_stream *stream); /*! stream type*/ @@ -2053,7 +2061,7 @@ GF_Err gf_m2ts_mux_use_single_au_pes_mode(GF_M2TS_Mux *muxer, GF_M2TS_PackMode a /*! sets initial PCR value for all programs in multiplex. If this is not called, each program will use a random initial PCR \param muxer the target MPEG-2 TS multiplexer -\param init_pcr_value initial PCR value in 90kHz +\param init_pcr_value initial PCR value in 27 MHz \return error if any */ GF_Err gf_m2ts_mux_set_initial_pcr(GF_M2TS_Mux *muxer, u64 init_pcr_value); diff --git a/third_party/gpac/include/gpac/network.h b/third_party/gpac/include/gpac/network.h index 46ebd41a..a3b777b2 100644 --- a/third_party/gpac/include/gpac/network.h +++ b/third_party/gpac/include/gpac/network.h @@ -146,7 +146,7 @@ Bool gf_url_is_relative(const char *url); Returns a pointer to the first colon at the end of a filename or URL, if any. If assign_sep is specified, for example '=', the function will make sure that the colon is after the file extension if found and that '=' is not present between colon and file ext. -This is used to parse 'a:b.mp4:c' (expected result ':c...' and not ':b...') vs 'a:b=c.mp4' ' (expected result ':b') +This is used to parse 'a:b.mp4:c' (expected result ':c...' and not ':b...') vs 'a:b=c.mp4' ' (expected result ':b') \param URL path or URL to inspect \param assign_sep value of assignment operand character. If 0, only checks for colon, otherwise chec that no assign sep or colon is present before file extension, if present @@ -795,4 +795,3 @@ Bool gf_sk_group_sock_is_set(GF_SockGroup *sg, GF_Socket *sk, GF_SockSelectMode #endif /*_GF_NET_H_*/ - diff --git a/third_party/gpac/include/gpac/revision.h b/third_party/gpac/include/gpac/revision.h index be7a11bd..0b56748c 100644 --- a/third_party/gpac/include/gpac/revision.h +++ b/third_party/gpac/include/gpac/revision.h @@ -1 +1 @@ -#define GPAC_GIT_REVISION "1502-g945c0e4b1-master" +#define GPAC_GIT_REVISION "2064-g4f33ccde0-master" diff --git a/third_party/gpac/include/gpac/revision.h.new b/third_party/gpac/include/gpac/revision.h.new deleted file mode 100644 index be7a11bd..00000000 --- a/third_party/gpac/include/gpac/revision.h.new +++ /dev/null @@ -1 +0,0 @@ -#define GPAC_GIT_REVISION "1502-g945c0e4b1-master" diff --git a/third_party/gpac/include/gpac/rmt_ws.h b/third_party/gpac/include/gpac/rmt_ws.h new file mode 100644 index 00000000..39663b74 --- /dev/null +++ b/third_party/gpac/include/gpac/rmt_ws.h @@ -0,0 +1,115 @@ +#ifndef RMT_WS_INCLUDED_H +#define RMT_WS_INCLUDED_H + +//! type for callbacks called when a new client connects +//! \param task user data sent back to the callback +//! \param new_client a structure representing the client (of type RMT_ClientCtx) +typedef void (*rmt_on_new_client_cbk)(void *task, void* new_client); + + +//! Handle to the main instance +typedef struct RMT_WS RMT_WS; + +//! creates the main instance +RMT_WS* rmt_ws_new(); + +//! deletes the main instance +void rmt_ws_del(RMT_WS* rmt_ws); + +//! Struture to fill in to modify default settings +typedef struct RMT_Settings { + + //! Which port to listen for incoming connections on + u16 port; + + //! inactivity timeout before closing connections + u32 timeout_secs; + + //! time between websocket ping requests (0 to disable) + u32 ping_secs; + + + //! Only allow connections on localhost? + Bool limit_connections_to_localhost; + + //! How long to sleep between server updates + u32 msSleepBetweenServerUpdates; + + //! function to call when a new websocket connection is accepted + rmt_on_new_client_cbk on_new_client_cbk; + //! context for on_new_client_cbk + void* on_new_client_cbk_task; + + //! server certificate and private key to use for ssl websocket (null to disable wss) + const char* cert; + const char* pkey; + +} RMT_Settings; + +//! gets the current rmtws settings (creates the structure if necessary) +RMT_Settings* gf_rmt_get_settings(); + +//! structure representing the http server +typedef struct __rmt_serverctx RMT_ServerCtx; + +//! structure representing a websocket client +typedef struct __rmt_clientctx RMT_ClientCtx; + +typedef enum { + RMT_CALLBACK_NONE=0x12, + RMT_CALLBACK_JS, + RMT_CALLBACK_NODE, +} RMT_Callback_type; + +//! sets the callback called when new clients connect to the sever +//! \param task user data stored and passed back to the callback +//! \param cbk the callback of type \ref rmt_on_new_client_cbk +void gf_rmt_set_on_new_client_cbk(void *task, rmt_on_new_client_cbk cbk); + +//! gets the userdata associated with the new client callback if defined +void* gf_rmt_get_on_new_client_task(); + +//! gets a string representing the client in the format ip:port +//! \param client the client object +//! \return a "ip:port" string of the given client +const char* gf_rmt_get_peer_address(RMT_ClientCtx* client); + +//! sends data to a client on the websocket +//! \param client the client object +//! \param msg a buffer containing the data to send +//! \param size the size of the data to send +//! \param is_binary false if we're sending a utf8 string, true otherwise +GF_Err gf_rmt_client_send_to_ws(RMT_ClientCtx* client, const char* msg, u64 size, Bool is_binary); + +//! type for callbacks called when a client receives data on the websocket +//! \param task user data passed back to the callback +//! \param payload a buffer containing the data received +//! \param size the size of the data received +//! \param is_binary false the data is a utf8 string, true otherwise +typedef void (*rmt_client_on_data_cbk)(void* task, const u8* payload, u64 size, Bool is_binary); + +//! sets the callback called when a client receives data +//! \param client the client for which we are setting the callback +//! \param task user data stored and passed back to the callback +//! \param cbk the callback of type \ref rmt_client_on_data_cbk +void gf_rmt_client_set_on_data_cbk(RMT_ClientCtx* client, void* task, rmt_client_on_data_cbk cbk); + +//! gets the userdata associated with the client on data callback if defined +void* gf_rmt_client_get_on_data_task(RMT_ClientCtx* client); + +//! type for callbacks called when a client is deleted (e.g. on disconnects) +//! \param task user data passed back to the callback +typedef void (*rmt_client_on_del_cbk)(void* task); + +//! sets the callback called when a client is deleted +//! \param client the client for which we are setting the callback +//! \param task user data stored and passed back to the callback +//! \param cbk the callback of type \ref rmt_client_on_del_cbk +void gf_rmt_client_set_on_del_cbk(RMT_ClientCtx* client, void* task, rmt_client_on_del_cbk cbk); + +//! gets the userdata associated with the client on deleted callback if defined +void* gf_rmt_client_get_on_del_task(RMT_ClientCtx* client); + + + +#endif diff --git a/third_party/gpac/include/gpac/route.h b/third_party/gpac/include/gpac/route.h index 45a3e65c..6c85256a 100644 --- a/third_party/gpac/include/gpac/route.h +++ b/third_party/gpac/include/gpac/route.h @@ -156,7 +156,7 @@ typedef struct Any reallocation of the fragment info SHALL be done using \ref gf_route_dmx_patch_frag_info */ GF_LCTFragInfo *frags; - + /*! offset of late received data, only for GF_ROUTE_EVT_LATE_DATA*/ u32 late_fragment_offset; @@ -177,6 +177,9 @@ typedef struct Only used for GF_ROUTE_EVT_FILE, GF_ROUTE_EVT_DYN_SEG, GF_ROUTE_EVT_DYN_SEG_FRAG and GF_ROUTE_EVT_FILE_DELETE */ void *udta; + + /*! channel hint set by application; 0 if unknown*/ + u32 channel_hint; } GF_ROUTEEventFileInfo; /*! Creates a new ROUTE ATSC3.0 demultiplexer @@ -256,7 +259,6 @@ Bool gf_route_dmx_has_active_multicast(GF_ROUTEDmx *routedmx); /*! Checks for object being timeouts - this should only be called when \ref gf_route_dmx_process returns GF_IP_NETWORK_EMPTY for the first time in a batch \param routedmx the ROUTE demultiplexer -\return GF_TRUE if some multicast sockets are active, GF_FALSE otherwise */ void gf_route_dmx_check_timeouts(GF_ROUTEDmx *routedmx); @@ -280,9 +282,9 @@ typedef enum } GF_RouteProgressiveDispatch; /*! Allow segments to be sent while being downloaded. - + \note Files with a static TOI association are always sent once completely received, other files using TOI templating may be sent while being received if enabled. The data sent is always contiguous data since the beginning of the file in that case. - + \param routedmx the ROUTE demultiplexer \param dispatch_mode set dispatch mode \return error code if any @@ -418,6 +420,17 @@ GF_Err gf_route_dmx_patch_frag_info(GF_ROUTEDmx *routedmx, u32 service_id, GF_RO */ GF_Err gf_route_dmx_patch_blob_size(GF_ROUTEDmx *routedmx, u32 service_id, GF_ROUTEEventFileInfo *finfo, u32 new_size); +/*! Set hint to the identified object - if object has an associated channel (eg HAS representation), set the hint on this channel. The hint is passed back in the \ref GF_ROUTEEventFileInfo and is typically used to store mime type of the object + +\param routedmx the ROUTE demultiplexer +\param service_id the target service +\param tsi tsi of channel +\param toi toi of object +\param hint the new size to set +\return error if any + */ +GF_Err gf_route_dmx_set_object_hint(GF_ROUTEDmx *routedmx, u32 service_id, u32 tsi, u32 toi, u32 hint); + /*! Set active status of a representation \param routedmx the ROUTE demultiplexer \param service_id the target service @@ -434,6 +447,13 @@ GF_Err gf_route_dmx_mark_active_quality(GF_ROUTEDmx *routedmx, u32 service_id, c */ void gf_route_dmx_reset_all(GF_ROUTEDmx *routedmx); +/*! Gets repair info for MABR +\param routedmx the ROUTE demultiplexer +\param service_id the service identifier +\param base_uri set to base URI used in MABR filenames if present or to NULL otherwise - may be NULL +\param repair_server set to repair server address if present or to NULL otherwise - may be NULL + */ +void gf_route_dmx_get_repair_info(GF_ROUTEDmx *routedmx, u32 service_id, const char **base_uri, const char **repair_server);; /*! @} */ #ifdef __cplusplus @@ -443,4 +463,3 @@ void gf_route_dmx_reset_all(GF_ROUTEDmx *routedmx); #endif /* GPAC_DISABLE_ROUTE */ #endif //_GF_ROUTE_H_ - diff --git a/third_party/gpac/include/gpac/rtp_streamer.h b/third_party/gpac/include/gpac/rtp_streamer.h index d74c06d4..cc0a4652 100644 --- a/third_party/gpac/include/gpac/rtp_streamer.h +++ b/third_party/gpac/include/gpac/rtp_streamer.h @@ -200,12 +200,12 @@ Gets the SDP asscoiated with all media in the streaming session (only media part \param tx text window horizontal offset \param ty text window vertical offset \param tl text window z-index -\param nb_chan number of audio channels, 0 if unknown +\param nb_channels number of audio channels, 0 if unknown \param for_rtsp if GF_TRUE, produces the SDP for an RTSP describe (no port info) \param out_sdp_buffer location to the SDP buffer to allocate and fill \return error if any */ -GF_Err gf_rtp_streamer_append_sdp_extended(GF_RTPStreamer *rtp, u16 ESID, const u8 *dsi, u32 dsi_len, const u8 *dsi_enh, u32 dsi_enh_len, char *KMS_URI, u32 width, u32 height, u32 tw, u32 th, s32 tx, s32 ty, s16 tl, u32 nb_chan, Bool for_rtsp, char **out_sdp_buffer); +GF_Err gf_rtp_streamer_append_sdp_extended(GF_RTPStreamer *rtp, u16 ESID, const u8 *dsi, u32 dsi_len, const u8 *dsi_enh, u32 dsi_enh_len, char *KMS_URI, u32 width, u32 height, u32 tw, u32 th, s32 tx, s32 ty, s16 tl, u32 nb_channels, Bool for_rtsp, char **out_sdp_buffer); /*! sends a full Access Unit over RTP \param rtp the target RTP streamer @@ -309,7 +309,7 @@ u16 gf_rtp_streamer_get_next_rtp_sn(GF_RTPStreamer *streamer); GF_Err gf_rtp_streamer_set_interleave_callbacks(GF_RTPStreamer *streamer, GF_Err (*RTP_TCPCallback)(void *cbk1, void *cbk2, Bool is_rtcp, u8 *pck, u32 pck_size), void *cbk1, void *cbk2); -/*! callback function for procesing RTCP receiver reports +/*! callback function for processing RTCP receiver reports \param cbk user data passed to \ref gf_rtp_streamer_read_rtcp \param ssrc ssrc for this report, 0 if same as ssrc of channel \param rtt_ms round-trip time estimate in ms @@ -353,4 +353,3 @@ u32 gf_rtp_streamer_get_codecid(GF_RTPStreamer *streamer); #endif //GPAC_DISABLE_ISOM && GPAC_DISABLE_STREAMING #endif /*_GF_RTPSTREAMER_H_*/ - diff --git a/third_party/gpac/include/gpac/svg_types.h b/third_party/gpac/include/gpac/svg_types.h index 582fe994..b98095f6 100644 --- a/third_party/gpac/include/gpac/svg_types.h +++ b/third_party/gpac/include/gpac/svg_types.h @@ -532,7 +532,7 @@ enum { SVG_COLOR_INACTIVE_CAPTION, /* Inactive window caption. */ SVG_COLOR_INACTIVE_CAPTION_TEXT, /*Color of text in an inactive caption. */ SVG_COLOR_INFO_BACKGROUND, /* Background color for tooltip controls. */ - SVG_COLOR_INFO_TEXT, /*Text color for tooltip controls. */ + SVG_COLOR_INFO_TEXT, /*Text color for tooltip controls. */ SVG_COLOR_MENU, /*Menu background. */ SVG_COLOR_MENU_TEXT, /* Text in menus. */ SVG_COLOR_SCROLLBAR, /* Scroll bar gray area. */ diff --git a/third_party/gpac/include/gpac/sync_layer.h b/third_party/gpac/include/gpac/sync_layer.h index 561656c3..cb89534d 100644 --- a/third_party/gpac/include/gpac/sync_layer.h +++ b/third_party/gpac/include/gpac/sync_layer.h @@ -2,7 +2,7 @@ * GPAC - Multimedia Framework C SDK * * Authors: Jean Le Feuvre - * Copyright (c) Telecom ParisTech 2000-2024 + * Copyright (c) Telecom ParisTech 2000-2025 * All rights reserved * * This file is part of GPAC / SL header file @@ -133,7 +133,7 @@ typedef struct bin128 constant_IV; /*version_number are pushed from m2ts sections to the mpeg4sl layer so as to handle mpeg4 stream dependencies*/ u8 m2ts_version_number_plus_one; - //0: not mpeg-2 TS PCR, 1: MEPG-2 TS PCR, 2: MPEG-2 TS PCR with discontinuity + //0: not mpeg-2 TS PCR, 1: MPEG-2 TS PCR, 2: MPEG-2 TS PCR with discontinuity u8 m2ts_pcr; /* HTML5 MSE Packet info */ s64 timeStampOffset; @@ -169,7 +169,7 @@ u32 gf_sl_get_header_size(GF_SLConfig* slConfig, GF_SLHeader *Header); \param PDULength the size of the SL packet \param HeaderLen set to size of the SL header - payload will start at PDU + *HeaderLen */ -void gf_sl_depacketize(GF_SLConfig *slConfig, GF_SLHeader *Header, const u8 *PDU, u32 PDULength, u32 *HeaderLen); +void gf_odf_sl_depacketize(GF_SLConfig *slConfig, GF_SLHeader *Header, const u8 *PDU, u32 PDULength, u32 *HeaderLen); /*! @} */ diff --git a/third_party/gpac/include/gpac/thread.h b/third_party/gpac/include/gpac/thread.h index 131f026d..387f8d7d 100644 --- a/third_party/gpac/include/gpac/thread.h +++ b/third_party/gpac/include/gpac/thread.h @@ -106,6 +106,8 @@ static inline function. #define safe_int_add(__v, inc_val) InterlockedExchangeAdd((int *) (__v), inc_val) #define safe_int_sub(__v, dec_val) InterlockedExchangeAdd((int *) (__v), -dec_val) +/*! atomic add and gets the value *before* the add */ +#define safe_int_fetch_add(__v, inc_val) InterlockedExchangeAdd((int *) (__v), inc_val) #ifdef GPAC_64_BITS #define safe_int64_add(__v, inc_val) InterlockedExchangeAdd64((LONGLONG *) (__v), inc_val) #define safe_int64_sub(__v, dec_val) InterlockedExchangeAdd64((LONGLONG *) (__v), -dec_val) @@ -129,7 +131,8 @@ static inline function. #define safe_int64_add(__v, inc_val) InterlockedAdd64((LONG64 *) (__v), inc_val) /*! atomic large integer subtraction */ #define safe_int64_sub(__v, dec_val) InterlockedAdd64((LONG64 *) (__v), -dec_val) - +/*! atomic add and gets the value *before* the add */ +#define safe_int_fetch_add(__v, inc_val) InterlockedExchangeAdd((int *) (__v), inc_val) #endif //winxp #else //not windows @@ -148,6 +151,8 @@ static inline function. #define safe_int64_add(__v, inc_val) __atomic_add_fetch((int64_t *) (__v), inc_val, __ATOMIC_SEQ_CST) /*! atomic large integer subtraction */ #define safe_int64_sub(__v, dec_val) __atomic_sub_fetch((int64_t *) (__v), dec_val, __ATOMIC_SEQ_CST) +/*! atomic add and gets the value *before* the add */ +#define safe_int_fetch_add(__v, inc_val) __atomic_fetch_add((int *) (__v), inc_val, __ATOMIC_SEQ_CST) #else @@ -163,6 +168,8 @@ static inline function. #define safe_int64_add(__v, inc_val) __sync_add_and_fetch((int64_t *) (__v), inc_val) /*! atomic large integer subtraction */ #define safe_int64_sub(__v, dec_val) __sync_sub_and_fetch((int64_t *) (__v), dec_val) +/*! atomic add and gets the value *before* the add */ +#define safe_int_fetch_add(__v, inc_val) __sync_fetch_and_add((int *) (__v), inc_val) #endif //GPAC_NEED_LIBATOMIC diff --git a/third_party/gpac/include/gpac/tools.h b/third_party/gpac/include/gpac/tools.h index 01358e30..906bef41 100644 --- a/third_party/gpac/include/gpac/tools.h +++ b/third_party/gpac/include/gpac/tools.h @@ -509,6 +509,30 @@ Compares two timecodes */ Bool gf_timecode_equal(GF_TimeCode *value1, GF_TimeCode *value2); + +/*! +\brief Get CENC IV size + +Get CENC IV size from a key info chunk +\param key_info CENC key info buffer +\param key_info_size CENC key info buffer size +\param key_idx index of key for multi-key cases, 0 otherwise +\param const_iv_size set to const IV size if const IV is used, otherwise set to 0 - can be NULL +\param const_iv set to const IV start in key_info buffer when constant IV is used, otherwise set to NULL - can be NULL +\return IV size in bytes if constant IV is not used, otherwise 0 + */ +u8 gf_cenc_key_info_get_iv_size(const u8 *key_info, u32 key_info_size, u32 key_idx, u8 *const_iv_size, const u8 **const_iv); + +/*! +\brief validate a CENC key info chunk + +Checks whether a CENC key info chunk is valid or not +\param key_info CENC key info buffer +\param key_info_size CENC key info buffer size +\return GF_TRUE if this chunk looks like a CENC key info buffer, GF_FALSE otherwise +*/ +Bool gf_cenc_validate_key_info(const u8 *key_info, u32 key_info_size); + /*! @} */ /*! @@ -730,39 +754,12 @@ const char *gf_sys_features(Bool disabled); */ Bool gf_sys_solve_path(const char *tpl_path, char szPath[GF_MAX_PATH]); -/*! callback function for remotery profiler - \param udta user data passed by \ref gf_sys_profiler_set_callback - \param text string sent by webbrowser client +/*! Enables or disables the rmt websocket monitoring server +\param start If true starts the webserver, if false stops it +\return GF_OK if success, GF_BAD_PARAM if error, GF_NOT_SUPPORTED if ws server not supported */ -typedef void (*gf_rmt_user_callback)(void *udta, const char* text); - -/*! Enables remotery profiler callback. If remotery is enabled, commands sent via webbrowser client will be forwarded to the callback function specified -\param udta user data -\param rmt_usr_cbk callback function -\return GF_OK if success, GF_BAD_PARAM if profiler is not running, GF_NOT_SUPPORTED if profiler not supported -*/ -GF_Err gf_sys_profiler_set_callback(void *udta, gf_rmt_user_callback rmt_usr_cbk); - - -/*! Sends a log message to remotery web client -\param msg text message to send. The message format should be json -\return GF_OK if success, GF_BAD_PARAM if profiler is not running, GF_NOT_SUPPORTED if profiler not supported -*/ -GF_Err gf_sys_profiler_log(const char *msg); - -/*! Sends a message to remotery web client -\param msg text message to send. The message format should be json -\return GF_OK if success, GF_BAD_PARAM if profiler is not running, GF_NOT_SUPPORTED if profiler not supported -*/ -GF_Err gf_sys_profiler_send(const char *msg); - -/*! Enables sampling times in RMT - \param enable if GF_TRUE, sampling will be enabled, otherwise disabled*/ -void gf_sys_profiler_enable_sampling(Bool enable); +GF_Err gf_sys_enable_rmtws(Bool start); -/*! Checks if sampling is enabled in RMT. Sampling is by default enabled when enabling remotery - \return GF_TRUE if sampling is enabled, GF_FALSE otherwise*/ -Bool gf_sys_profiler_sampling_enabled(); /*! GPAC Log tools @@ -923,6 +920,8 @@ typedef enum GF_LOG_CONSOLE, /*! Log for all messages coming the application, not used by libgpac or the modules*/ GF_LOG_APP, + /*! Log for all info regarding the rmt_ws server and bindings*/ + GF_LOG_RMTWS, /*! special value used to set a level for all tools*/ GF_LOG_ALL, @@ -1479,7 +1478,7 @@ typedef enum { GF_LOCKFILE_FAILED=0, /*! lockfile creation succeeded, creating a new lock file*/ GF_LOCKFILE_NEW, - /*! lockfile creation succeeded, lock file was already present and created by this process*/ + /*! lockfile creation succeeded, lock file was already present and created by this process*/ GF_LOCKFILE_REUSE } GF_LockStatus; @@ -1833,12 +1832,13 @@ FILE *gf_fopen(const char *file_name, const char *mode); \brief file opening Opens a file, potentially using file IO if the parent URL is a File IO wrapper + +\note If a parent GFIO file is used and file_name was obtained without using \ref gf_url_concatenate on the parent GFIO, make sur to call \ref gf_fileio_open_url in "url" mode to prevent further concatenations by the GFIO handler. + \param file_name same as fopen \param parent_url URL of parent file. If not a file io wrapper (gfio://), the function is equivalent to gf_fopen \param mode same as fopen - value "mkdir" checks if parent dir(s) need to be created, create them if needed and returns NULL (no file open) \param no_warn if GF_TRUE, do not throw log message if failure -\return stream handle of the file object -\note You only need to call this function if you're suspecting the file to be a large one (usually only media files), otherwise use regular stdio. \return stream habdle of the file or file IO object*/ FILE *gf_fopen_ex(const char *file_name, const char *parent_url, const char *mode, Bool no_warn); @@ -2008,7 +2008,7 @@ typedef struct __gf_file_io GF_FileIO; \param mode opening mode of file, same as fopen mode. The following additional modes are defined: - "ref": indicates this FileIO object is used by some part of the code and must not be destroyed upon closing of the file. Associated URL is null - "unref": indicates this FileIO object is not used by some part of the code and may be destroyed if no more references to this object are set. Associated URL is null - - "url": indicates to create a new FileIO object for the given URL without opening the output file. The resulting FileIO object must be garbage collected by the app in case its is never used by the callers + - "url": indicates to create a new FileIO object for the given URL without opening the output file. The resulting FileIO object must be garbage collected by the app in case it is never used by the callers - "probe": checks if the file exists, but no need to open the file. The function should return NULL in this case. If file does not exist, set out_error to GF_URL_ERROR - "close": indicates the fileIO object is being closed (fclose) \param out_error must be set to error code if any (never NULL) @@ -2211,6 +2211,27 @@ Bool gf_fileio_read_mode(GF_FileIO *fileio); */ Bool gf_fileio_write_mode(GF_FileIO *fileio); +/*! Function callback for GF_FileIO delete events. The callback is NOT thread-safe in GPAC, applications should take care of ensuring safety + +\note Applications must make sure that the underlying gfio objects are defined as GPAC does not track allocated gfio objects. + + \param url a gfio:// url to be deleted or a regular name if parent_gfio_url is NULL. + \param parent_gfio_url the parent GFIO associated with the URL string, or NULL if url is a gfio url + \return error code if any, GF_EOS if this callback is not handling this specific gfio +*/ +typedef GF_Err (*gfio_delete_proc)(const char *url, const char *parent_gfio_url); + +/*! Register a GF_FileIO delete callback. This function is NOT threadsafe, applications should take care of ensuring safety +\param del_proc the calback to register. It MUST be valid until unregistered +\return error if any +*/ +GF_Err gf_fileio_register_delete_proc(gfio_delete_proc del_proc); + +/*! Unregister a GF_FileIO delete callback. This function is NOT threadsafe, applications should take care of ensuring safety +\param del_proc the calback to unregister. +*/ +void gf_fileio_unregister_delete_proc(gfio_delete_proc del_proc); + /*! @} */ /*! @@ -2548,43 +2569,18 @@ Bool gf_creds_check_membership(const char *username, const char *users, const ch //! @cond Doxygen_Suppress -#if defined(GPAC_DISABLE_3D) && !defined(GPAC_DISABLE_REMOTERY) -#define GPAC_DISABLE_REMOTERY 1 -#endif - -#ifdef GPAC_DISABLE_REMOTERY +#ifdef GPAC_DISABLE_RMTWS #define RMT_ENABLED 0 -#else -#define RMT_USE_OPENGL 1 #endif -#include - -#define GF_RMT_AGGREGATE RMTSF_Aggregate -/*! begins remotery CPU sample*/ -#define gf_rmt_begin rmt_BeginCPUSample -/*! begins remotery CPU sample with hash*/ -#define gf_rmt_begin_hash rmt_BeginCPUSampleStore -/*! ends remotery CPU sample*/ -#define gf_rmt_end rmt_EndCPUSample -/*! sets remotery thread name*/ -#define gf_rmt_set_thread_name rmt_SetCurrentThreadName -/*! logs remotery text*/ -#define gf_rmt_log_text rmt_LogText -/*! begins remotery OpenGL sample*/ -#define gf_rmt_begin_gl rmt_BeginOpenGLSample -/*! begins remotery OpenGL sample with hash*/ -#define gf_rmt_begin_gl_hash rmt_BeginOpenGLSampleStore -/*!ends remotery OpenGL sample*/ -#define gf_rmt_end_gl rmt_EndOpenGLSample +#include //! @endcond /* \cond dummy */ -/*to call whenever the OpenGL library is opened - this function is needed to bind OpenGL and remotery, and to load -OpenGL extensions on windows +/*to call whenever the OpenGL library is opened - this function is needed to load OpenGL extensions on windows not exported, and not included in src/compositor/gl_inc.h since it may be needed even when no OpenGL calls are made by the caller*/ void gf_opengl_init(); diff --git a/third_party/gpac/include/gpac/version.h b/third_party/gpac/include/gpac/version.h index 59232746..34db429e 100644 --- a/third_party/gpac/include/gpac/version.h +++ b/third_party/gpac/include/gpac/version.h @@ -46,9 +46,9 @@ // WARNING: when bumping, reflect the changes in share/python/libgpac.py !! /*! ABI Major number of libgpac */ -#define GPAC_VERSION_MAJOR 12 +#define GPAC_VERSION_MAJOR 13 /*! ABI Minor number of libgpac */ -#define GPAC_VERSION_MINOR 23 +#define GPAC_VERSION_MINOR 0 /*! ABI Micro number of libgpac */ #define GPAC_VERSION_MICRO 0 diff --git a/third_party/gpac/include/gpac/xml.h b/third_party/gpac/include/gpac/xml.h index e6cb895e..7573fea0 100644 --- a/third_party/gpac/include/gpac/xml.h +++ b/third_party/gpac/include/gpac/xml.h @@ -303,7 +303,7 @@ u32 gf_xml_dom_get_root_nodes_count(GF_DOMParser *parser); */ GF_XMLNode *gf_xml_dom_get_root_idx(GF_DOMParser *parser, u32 idx); -/*! Gets the root node of the document and assign the parser root to NULL. +/*! Gets the root node of the document and assigns the parser root to NULL. \param parser the DOM parser to use \return the root element at the given index, or NULL if error. The element must be freed by the caller */ diff --git a/third_party/gpac/lib/libgpac.dylib b/third_party/gpac/lib/libgpac.dylib new file mode 100755 index 00000000..9ac98b2b Binary files /dev/null and b/third_party/gpac/lib/libgpac.dylib differ diff --git a/third_party/gpac/lib/libgpac_static.a b/third_party/gpac/lib/libgpac_static.a deleted file mode 100644 index 4bc2dc2b..00000000 Binary files a/third_party/gpac/lib/libgpac_static.a and /dev/null differ diff --git a/third_party/gpac/scripts/update.sh b/third_party/gpac/scripts/update.sh new file mode 100755 index 00000000..2791cc3d --- /dev/null +++ b/third_party/gpac/scripts/update.sh @@ -0,0 +1,91 @@ +#!/bin/bash + +# Copyright 2025 Google LLC +# +# 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. + +# Exit on any error +set -e + +# Configuration +GPAC_REPO="https://github.com/gpac/gpac.git" +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +ROOT_DIR="$(dirname "$SCRIPT_DIR")" +TEMP_DIR="$SCRIPT_DIR/gpac_temp" +DEST_LIB_DIR="$ROOT_DIR/lib" +DEST_INCLUDE_DIR="$ROOT_DIR/include" +INSTALL_DIR="$TEMP_DIR/install" + +# Create temporary directory and clone repository +echo "Cloning gpac repository..." +rm -rf "$TEMP_DIR" +git clone "$GPAC_REPO" "$TEMP_DIR" +cd "$TEMP_DIR" + +# Get the current commit hash +COMMIT_HASH=$(git rev-parse HEAD) + +# Configure the build +# Write a list of extra libs to disable +CFG_CMD="./configure --prefix="$INSTALL_DIR" --static-modules --isomedia-only --disable-rmtws" +EXTRA_LIBS="zlib opensvc openhevc platinum freetype ssl jpeg openjpeg png mad a52 xvid faad ffmpeg freenect vorbis theora nghttp2 ngtcp2 nghttp3 oss dvb4linux alsa pulseaudio jack directfb hid lzma tinygl vtb ogg sdl caption mpeghdec libcaca curl" +LIBS="compositor fin fout mp4mx mp4dmx reframer rfav1" +# For each library in extra libs, append '--use-=no' to the configure command +for lib in $EXTRA_LIBS; do + CFG_CMD+=" --use-$lib=no" +done +# For each library in libs, append '--enable-' to the configure command +for lib in $LIBS; do + CFG_CMD+=" --enable-$lib" +done + +echo "Configuring gpac build..." +$CFG_CMD + +# Build and install the library +echo "Building gpac..." +make -j8 + +echo "Installing gpac library..." +make install-lib + +# Create destination directories if they don't exist +mkdir -p "$DEST_LIB_DIR" "$DEST_INCLUDE_DIR" + +# Copy the built library +echo "Copying library files..." +cp "$INSTALL_DIR/lib/libgpac.dylib" "$DEST_LIB_DIR/" + +# Fix rpaths in the dylib +echo "Fixing rpaths in dylib..." +install_name_tool -add_rpath "@rpath/third_party/gpac/lib/" "$DEST_LIB_DIR/libgpac.dylib" +install_name_tool -add_rpath "@rpath/third_party/gpac/lib/libgpac.dylib" "$DEST_LIB_DIR/libgpac.dylib" +install_name_tool -id "@rpath/third_party/gpac/lib/libgpac.dylib" "$DEST_LIB_DIR/libgpac.dylib" + +# Copy header files +echo "Copying header files..." +rm -rf "$DEST_INCLUDE_DIR/gpac" +cp -r "$INSTALL_DIR/include/gpac" "$DEST_INCLUDE_DIR/" + +# Update README.md with new commit hash +echo "Updating README.md..." +sed -i '' "1s|.*|- Compiled from: https://github.com/gpac/gpac/tree/$COMMIT_HASH|" "$ROOT_DIR/README.md" + +# Go back to the script directory to ensure cleanup paths are correct +cd "$SCRIPT_DIR" + +# Final cleanup +echo "Cleaning up temporary files..." +rm -rf "$TEMP_DIR" + +echo "Update completed successfully!"