├── CMakeLists.txt ├── README.md ├── base ├── callback.h ├── compiler_specific.h ├── logging.cc ├── logging.h ├── macros.h ├── message_loop │ ├── incoming_task_queue.cc │ ├── incoming_task_queue.h │ ├── message_loop.cc │ └── message_loop.h ├── numerics │ ├── safe_conversions.h │ ├── safe_conversions_impl.h │ ├── safe_math.h │ └── safe_math_impl.h ├── pending_task.cc ├── pending_task.h ├── posix │ ├── eintr_wrapper.h │ ├── safe_strerror.cc │ └── safe_strerror.h ├── ptr_util.h ├── stl_util.h ├── synchronization │ ├── lock.cc │ ├── lock.h │ ├── waitable_event.cc │ └── waitable_event.h ├── threading │ ├── platform_thread.h │ ├── platform_thread_internal_posix.cc │ ├── platform_thread_internal_posix.h │ ├── platform_thread_linux.cc │ ├── platform_thread_posix.cc │ ├── simple_thread.cc │ ├── simple_thread.h │ ├── thread.cc │ └── thread.h ├── time │ ├── time.cc │ ├── time.h │ └── time_posix.cc └── timer │ ├── timer.cc │ └── timer.h ├── build └── build_config.h ├── main.cc ├── main_app.cc ├── main_app.h ├── media ├── audio_decoder.cc ├── audio_decoder.h ├── audio_decoder_thread.cc ├── audio_decoder_thread.h ├── audio_frame_queue.cc ├── audio_frame_queue.h ├── audio_render.cc ├── audio_render.h ├── audio_resampler.cc ├── audio_resampler.h ├── ffmpeg_aac_bitstream_converter.cc ├── ffmpeg_aac_bitstream_converter.h ├── ffmpeg_common.cc ├── ffmpeg_common.h ├── ffmpeg_deleters.h ├── media_constants.h ├── mp4_dataset.cc ├── mp4_dataset.h ├── mpp_decoder.cc ├── mpp_decoder.h ├── packet_queue.cc ├── packet_queue.h ├── rga_utils.cc ├── rga_utils.h ├── video_decoder_thread.cc ├── video_decoder_thread.h ├── video_frame_queue.cc ├── video_frame_queue.h ├── video_player.cc └── video_player.h └── ui ├── main_window.cc ├── main_window.h ├── video_view.cc └── video_view.h /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.8) 2 | project(mp4player) 3 | set(CMAKE_BUILD_TYPE Debug) 4 | 5 | set(CMAKE_CXX_STANDARD 11) 6 | 7 | set(HOME $ENV{HOME}) 8 | message(STATUS ${HOME}) 9 | 10 | set(CMAKE_PREFIX_PATH "${HOME}/rv1109/buildroot/output/rockchip_rv1126_rv1109_facial_gate/host/arm-buildroot-linux-gnueabihf/sysroot/usr/lib/cmake/Qt5") 11 | set(QML2_IMPORT_PATH "${HOME}/rv1109/buildroot/output/rockchip_rv1126_rv1109_facial_gate/host/arm-buildroot-linux-gnueabihf/sysroot/usr/qml") 12 | set(CMAKE_AUTOMOC ON) 13 | set(CMAKE_AUTORCC ON) 14 | set(CMAKE_AUTOUIC ON) 15 | 16 | set(QT_VERSION 5) 17 | set(REQUIRED_LIBS Core Gui Widgets Multimedia) 18 | set(REQUIRED_LIBS_QUALIFIED Qt5::Core Qt5::Gui Qt5::Widgets Qt5::Multimedia) 19 | 20 | SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -fPIC -Wl,--enable-new-dtags -Wextra -Wall") 21 | SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fpermissive -g -fPIC -Wl,--enable-new-dtags -Wextra -Wall") 22 | 23 | set(EXECUTABLE_OUTPUT_PATH ${HOME}/project/linux/build) 24 | 25 | set(THIRD_PARTY_INCLUDE_DIR ${HOME}/jingxi/build) 26 | set(THIRD_PARTY_LIB_DIR ${HOME}/jingxi/build) 27 | 28 | set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fvisibility=hidden -static-libstdc++") 29 | 30 | set(RV1109_ROOT_DIR ${HOME}/rv1109/buildroot/output/rockchip_rv1126_rv1109_facial_gate/host/arm-buildroot-linux-gnueabihf/sysroot/usr) 31 | 32 | set(SDK_ROOT_DIR ${CMAKE_SOURCE_DIR}) 33 | 34 | include_directories( 35 | ${RV1109_ROOT_DIR} 36 | ${SDK_ROOT_DIR} 37 | ${THIRD_PARTY_INCLUDE_DIR} 38 | ${THIRD_PARTY_INCLUDE_DIR}/libevent/include 39 | ${THIRD_PARTY_INCLUDE_DIR}/openssl/include 40 | ${THIRD_PARTY_INCLUDE_DIR}/ffmpeg/include 41 | ) 42 | 43 | set(SRC_MAIN ${SDK_ROOT_DIR}/main.cc 44 | ${SDK_ROOT_DIR}/main_app.cc) 45 | 46 | file(GLOB_RECURSE SRC_BASE ${SDK_ROOT_DIR}/base/*.c*) 47 | 48 | file(GLOB_RECURSE SRC_PLAYER ${SDK_ROOT_DIR}/media/*.c*) 49 | 50 | file(GLOB_RECURSE SRC_UI ${SDK_ROOT_DIR}/ui/*.c*) 51 | 52 | link_directories( 53 | ${EXECUTABLE_OUTPUT_PATH} 54 | ${THIRD_PARTY_LIB_DIR}/libevent/lib 55 | ${THIRD_PARTY_LIB_DIR}/openssl/lib 56 | ${THIRD_PARTY_LIB_DIR}/ffmpeg/lib 57 | ${THIRD_PARTY_LIB_DIR}/x264/lib 58 | ${RV1109_ROOT_DIR}/lib 59 | ) 60 | 61 | if (NOT CMAKE_PREFIX_PATH) 62 | message(WARNING "CMAKE_PREFIX_PATH is not defined, you may need to set it " 63 | "(-DCMAKE_PREFIX_PATH=\"path/to/Qt/lib/cmake\" or -DCMAKE_PREFIX_PATH=/usr/include/{host}/qt{version}/ on Ubuntu)") 64 | endif () 65 | 66 | 67 | find_package(Qt${QT_VERSION} COMPONENTS ${REQUIRED_LIBS} REQUIRED) 68 | 69 | add_executable(${PROJECT_NAME} ${SRC_MAIN} ${SRC_BASE} ${SRC_PLAYER} ${SRC_UI} ) 70 | 71 | target_link_libraries(${PROJECT_NAME} ${REQUIRED_LIBS_QUALIFIED} 72 | -levent 73 | -levent_openssl 74 | -lasound 75 | -lx264 76 | -lavcodec 77 | -lavfilter 78 | -lavformat 79 | -lavutil 80 | -lpostproc 81 | -lswresample 82 | -lswscale 83 | -lcrypto 84 | -lssl 85 | -lrga 86 | -leasymedia 87 | -lrockchip_mpp 88 | -lrkaiq 89 | -lpthread) 90 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mp4player 2 | RV1109平台上实现一个简单的 mp4 播放器,主要是本人使用的开发板QT无法播放mp4,应该是没有编译qst所致,因而想利用rockchip平台自有的 3 | 功能实现一个简单的播放器。 4 | base目录包含一些基础框架实现,包含信号,线程,时间等,线程和消息泵的实现非常非常简单,因而不是很安全。安全的消息泵可以参考chromium base。 5 | 但那个过于庞大,不大适合引入这里。 6 | media目录包含mp4播放的实现 7 | ui目录,简单的QT UI,为了显示视频。 8 | 作者对QT不怎么熟悉,用法比较粗糙。 9 | 10 | # 技术实现 11 | 一般而言,音视频同步有三种方式: 12 | 1.以视频时间戳为基准 13 | 2.以音频时间戳为基准 14 | 3.以外部时钟为基准 15 | 本项目中使用外部时钟,使用一个定时器周期性播放音频和视频。其中比较关键的是,需要对定时器误差进行补偿,否则 16 | 容易造成同步丢失,或者音频播放缓冲区欠载。 17 | 这里没有什么复杂的算法,之所以播放能连续,主要依赖前期的缓冲,再加上及时修正定时器误差,可以保证播放的流畅性, 18 | 音视频同步误差不超过40ms,理论上。 19 | 20 | 本项目实现的功能包含: 21 | 1.pause/resume 22 | 2.seek 23 | 3.快播慢播暂时不支持 24 | 25 | # 依赖 26 | rkmedia:用来播放声音,以及rga的一些东西。 27 | mpp:用来解码 h264/h265 28 | ffmpeg:用来解码音频 29 | libevent:实现一个简单的消息泵 30 | QT:用来显示视频 31 | 以上库均已包含在rv1109固件中 32 | 33 | # 代码 34 | chromium,mini-chromium, webrtc中精简了部分代码,形成了简单的base库。 35 | 部分逻辑参考了 rockchip ffmpeg 解码器部分。 36 | 还有一部分参考 android 模拟器的 video player部分。(https://github.com/gameltb/rnbox) 37 | 38 | # 注意事项 39 | 缓冲的时长可以预先指定(以秒单位),音视频根据这个时长计算缓冲长度。这里需要注意,mpp解码器的缓冲区不能小于视频输出缓冲区, 40 | 否则视频输出缓冲区永远不可能填满。 41 | 有些mp4 包含5.1或者更多的声道,因为这是针对rv1109的,我们直接转换为立体声输出。 42 | 如果不想依赖rkmedia,可以自己实现 audio render,这个也不是很复杂。 chromium/webrtc中都包含了alsa的播放支持。 43 | 很多mp4包含B帧,不缓冲的话也没法正确播放。 44 | 代码中只验证了aac的解码,对于可能存在的其他音频编码方式,因为没找到样本,也没有验证过。是否需要在送入解码器之前将sample特殊处理, 45 | 没有什么特别的概念。 46 | rkmedia中的 AO 要求指定送入播放器的每帧样本数,不是严格意义上的nb_samples。个人理解nb_samples是每通道的样本数。而rkmedia需要传入每帧的总样本数。 47 | 比如立体声,16bit音频。一般,aac解码出来,nb_samples=1024,实际包含2048个samples。 48 | 49 | # 性能测试 50 | 1.不使用QT的话,单核CPU占用不会超过10%。 51 | 2.运行于QT上,CPU0:12-13%, CPU1:13-16% 52 | 3.内存占用略高,主要看缓冲时长。 53 | 4.如果想进一步优化CPU占用,可以用RK VO直接显示。 但用QT测试尚可。 54 | -------------------------------------------------------------------------------- /base/callback.h: -------------------------------------------------------------------------------- 1 | #ifndef BASE_CALLBACK_H_ 2 | #define BASE_CALLBACK_H_ 3 | 4 | #include 5 | #include 6 | #include "base/ptr_util.h" 7 | 8 | namespace base { 9 | 10 | class QueuedTask { 11 | public: 12 | QueuedTask() {} 13 | virtual ~QueuedTask() {} 14 | 15 | virtual void Run() = 0; 16 | }; 17 | 18 | // Simple implementation of QueuedTask for use with rtc::Bind and lambdas. 19 | template 20 | class ClosureTask : public QueuedTask { 21 | public: 22 | explicit ClosureTask(Closure &&closure) 23 | : closure_(std::forward(closure)) {} 24 | 25 | private: 26 | void Run() override { 27 | closure_(); 28 | } 29 | 30 | typename std::remove_const< 31 | typename std::remove_reference::type>::type closure_; 32 | }; 33 | 34 | // Convenience function to construct closures that can be passed directly 35 | // to methods that support std::unique_ptr but not template 36 | // based parameters. 37 | template 38 | static std::unique_ptr NewClosure(Closure &&closure) { 39 | return base::MakeUnique>(std::forward(closure)); 40 | } 41 | } // namespace base 42 | 43 | #endif // BASE_CALLBACK_H_ 44 | -------------------------------------------------------------------------------- /base/compiler_specific.h: -------------------------------------------------------------------------------- 1 | // Copyright 2008 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #ifndef MINI_CHROMIUM_BASE_COMPILER_SPECIFIC_H_ 6 | #define MINI_CHROMIUM_BASE_COMPILER_SPECIFIC_H_ 7 | 8 | #include "build/build_config.h" 9 | 10 | #if defined(COMPILER_MSVC) 11 | 12 | // MSVC_PUSH_DISABLE_WARNING pushes |n| onto a stack of warnings to be disabled. 13 | // The warning remains disabled until popped by MSVC_POP_WARNING. 14 | #define MSVC_PUSH_DISABLE_WARNING(n) __pragma(warning(push)) \ 15 | __pragma(warning(disable:n)) 16 | 17 | // Pop effects of innermost MSVC_PUSH_* macro. 18 | #define MSVC_POP_WARNING() __pragma(warning(pop)) 19 | 20 | #else // Not MSVC 21 | 22 | #define MSVC_PUSH_DISABLE_WARNING(n) 23 | #define MSVC_POP_WARNING() 24 | 25 | #endif // COMPILER_MSVC 26 | 27 | // Annotate a variable indicating it's ok if the variable is not used. 28 | // (Typically used to silence a compiler warning when the assignment 29 | // is important for some other reason.) 30 | // Use like: 31 | // int x = ...; 32 | // ALLOW_UNUSED_LOCAL(x); 33 | #define ALLOW_UNUSED_LOCAL(x) false ? (void)x : (void)0 34 | 35 | // Annotate a typedef or function indicating it's ok if it's not used. 36 | // Use like: 37 | // typedef Foo Bar ALLOW_UNUSED_TYPE; 38 | #if defined(COMPILER_GCC) 39 | #define ALLOW_UNUSED_TYPE __attribute__((unused)) 40 | #else 41 | #define ALLOW_UNUSED_TYPE 42 | #endif 43 | 44 | // Specify memory alignment for structs, classes, etc. 45 | // Use like: 46 | // class ALIGNAS(16) MyClass { ... } 47 | // ALIGNAS(16) int array[4]; 48 | #if defined(COMPILER_MSVC) 49 | #define ALIGNAS(byte_alignment) __declspec(align(byte_alignment)) 50 | #elif defined(COMPILER_GCC) 51 | #define ALIGNAS(byte_alignment) __attribute__((aligned(byte_alignment))) 52 | #endif 53 | 54 | #if defined(COMPILER_MSVC) 55 | #define WARN_UNUSED_RESULT 56 | #else 57 | #define WARN_UNUSED_RESULT __attribute__((warn_unused_result)) 58 | #endif 59 | 60 | #if defined(COMPILER_MSVC) 61 | #define PRINTF_FORMAT(format_param, dots_param) 62 | #else 63 | #define PRINTF_FORMAT(format_param, dots_param) \ 64 | __attribute__((format(printf, format_param, dots_param))) 65 | #endif 66 | 67 | // Compiler feature-detection. 68 | // clang.llvm.org/docs/LanguageExtensions.html#has-feature-and-has-extension 69 | #if defined(__has_feature) 70 | #define HAS_FEATURE(FEATURE) __has_feature(FEATURE) 71 | #else 72 | #define HAS_FEATURE(FEATURE) 0 73 | #endif 74 | 75 | // Macro for telling -Wimplicit-fallthrough that a fallthrough is intentional. 76 | #if __cplusplus >= 201703L // C++17 77 | #define FALLTHROUGH [[fallthrough]] 78 | #elif defined(__clang__) 79 | #define FALLTHROUGH [[clang::fallthrough]] 80 | #else 81 | #define FALLTHROUGH 82 | #endif 83 | 84 | #endif // MINI_CHROMIUM_BASE_COMPILER_SPECIFIC_H_ 85 | -------------------------------------------------------------------------------- /base/logging.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2006-2008 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #include "base/logging.h" 6 | 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "base/posix/safe_strerror.h" 17 | 18 | #include 19 | #include 20 | 21 | namespace logging { 22 | 23 | namespace { 24 | 25 | const char* const log_severity_names[] = { 26 | "INFO", 27 | "WARNING", 28 | "ERROR", 29 | "ERROR_REPORT", 30 | "FATAL" 31 | }; 32 | 33 | LogMessageHandlerFunction g_log_message_handler = nullptr; 34 | 35 | } // namespace 36 | 37 | void SetLogMessageHandler(LogMessageHandlerFunction log_message_handler) { 38 | g_log_message_handler = log_message_handler; 39 | } 40 | 41 | LogMessageHandlerFunction GetLogMessageHandler() { 42 | return g_log_message_handler; 43 | } 44 | 45 | LogMessage::LogMessage(const char* function, 46 | const char* file_path, 47 | int line, 48 | LogSeverity severity) 49 | : stream_(), 50 | file_path_(file_path), 51 | message_start_(0), 52 | line_(line), 53 | severity_(severity) { 54 | Init(function); 55 | } 56 | 57 | LogMessage::LogMessage(const char* function, 58 | const char* file_path, 59 | int line, 60 | std::string* result) 61 | : stream_(), 62 | file_path_(file_path), 63 | message_start_(0), 64 | line_(line), 65 | severity_(LOG_FATAL) { 66 | Init(function); 67 | stream_ << "Check failed: " << *result << ". "; 68 | delete result; 69 | } 70 | 71 | LogMessage::~LogMessage() { 72 | stream_ << std::endl; 73 | std::string str_newline(stream_.str()); 74 | 75 | if (g_log_message_handler && 76 | g_log_message_handler( 77 | severity_, file_path_, line_, message_start_, str_newline)) { 78 | return; 79 | } 80 | 81 | fprintf(stderr, "%s", str_newline.c_str()); 82 | fflush(stderr); 83 | 84 | if (severity_ == LOG_FATAL) { 85 | #if defined(COMPILER_MSVC) 86 | __debugbreak(); 87 | #if defined(ARCH_CPU_X86_FAMILY) 88 | __ud2(); 89 | #elif defined(ARCH_CPU_ARM64) 90 | __hlt(0); 91 | #else 92 | #error Unsupported Windows Arch 93 | #endif 94 | #elif defined(ARCH_CPU_X86_FAMILY) 95 | asm("int3; ud2;"); 96 | #elif defined(ARCH_CPU_ARMEL) 97 | asm("bkpt #0; udf #0;"); 98 | #elif defined(ARCH_CPU_ARM64) 99 | asm("brk #0; hlt #0;"); 100 | #else 101 | __builtin_trap(); 102 | #endif 103 | } 104 | } 105 | 106 | void LogMessage::Init(const char* function) { 107 | std::string file_name(file_path_); 108 | size_t last_slash = file_name.find_last_of('/'); 109 | if (last_slash != std::string::npos) { 110 | file_name.assign(file_name.substr(last_slash + 1)); 111 | } 112 | pid_t pid = getpid(); 113 | pid_t thread = syscall(__NR_gettid); 114 | stream_ << '[' 115 | << pid 116 | << ':' 117 | << thread 118 | << ':' 119 | << std::setfill('0'); 120 | 121 | timeval tv; 122 | gettimeofday(&tv, nullptr); 123 | tm local_time; 124 | localtime_r(&tv.tv_sec, &local_time); 125 | stream_ << std::setw(4) << local_time.tm_year + 1900 126 | << std::setw(2) << local_time.tm_mon + 1 127 | << std::setw(2) << local_time.tm_mday 128 | << ',' 129 | << std::setw(2) << local_time.tm_hour 130 | << std::setw(2) << local_time.tm_min 131 | << std::setw(2) << local_time.tm_sec 132 | << '.' 133 | << std::setw(6) << tv.tv_usec; 134 | 135 | stream_ << ':'; 136 | 137 | if (severity_ >= 0) { 138 | stream_ << log_severity_names[severity_]; 139 | } else { 140 | stream_ << "VERBOSE" << -severity_; 141 | } 142 | 143 | stream_ << ' ' 144 | << file_name 145 | << ':' 146 | << line_ 147 | << "] "; 148 | 149 | message_start_ = stream_.str().size(); 150 | } 151 | 152 | ErrnoLogMessage::ErrnoLogMessage(const char* function, 153 | const char* file_path, 154 | int line, 155 | LogSeverity severity, 156 | int err) 157 | : LogMessage(function, file_path, line, severity), 158 | err_(err) { 159 | } 160 | 161 | ErrnoLogMessage::~ErrnoLogMessage() { 162 | stream() << ": " 163 | << base::safe_strerror(err_) 164 | << " (" 165 | << err_ 166 | << ")"; 167 | } 168 | 169 | } // namespace logging 170 | -------------------------------------------------------------------------------- /base/logging.h: -------------------------------------------------------------------------------- 1 | // Copyright 2006-2008 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #ifndef MINI_CHROMIUM_BASE_LOGGING_H_ 6 | #define MINI_CHROMIUM_BASE_LOGGING_H_ 7 | 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #include "base/macros.h" 16 | #include "build/build_config.h" 17 | 18 | namespace logging { 19 | 20 | typedef int LogSeverity; 21 | const LogSeverity LOG_VERBOSE = -1; 22 | const LogSeverity LOG_INFO = 0; 23 | const LogSeverity LOG_WARNING = 1; 24 | const LogSeverity LOG_ERROR = 2; 25 | const LogSeverity LOG_ERROR_REPORT = 3; 26 | const LogSeverity LOG_FATAL = 4; 27 | const LogSeverity LOG_NUM_SEVERITIES = 5; 28 | 29 | #if defined(NDEBUG) 30 | const LogSeverity LOG_DFATAL = LOG_ERROR; 31 | #else 32 | const LogSeverity LOG_DFATAL = LOG_FATAL; 33 | #endif 34 | 35 | typedef bool (*LogMessageHandlerFunction)(LogSeverity severity, 36 | const char* file_poath, 37 | int line, 38 | size_t message_start, 39 | const std::string& string); 40 | 41 | void SetLogMessageHandler(LogMessageHandlerFunction log_message_handler); 42 | LogMessageHandlerFunction GetLogMessageHandler(); 43 | 44 | static inline int GetMinLogLevel() { 45 | return LOG_INFO; 46 | } 47 | 48 | static inline int GetVlogLevel(const char*) { 49 | return std::numeric_limits::max(); 50 | } 51 | 52 | static inline int GetLastSystemErrorCode() { 53 | return errno; 54 | } 55 | 56 | template 57 | std::string* MakeCheckOpString(const t1& v1, const t2& v2, const char* names) { 58 | std::ostringstream ss; 59 | ss << names << " (" << v1 << " vs. " << v2 << ")"; 60 | std::string* msg = new std::string(ss.str()); 61 | return msg; 62 | } 63 | 64 | #define DEFINE_CHECK_OP_IMPL(name, op) \ 65 | template \ 66 | inline std::string* Check ## name ## Impl(const t1& v1, const t2& v2, \ 67 | const char* names) { \ 68 | if (v1 op v2) { \ 69 | return NULL; \ 70 | } else { \ 71 | return MakeCheckOpString(v1, v2, names); \ 72 | } \ 73 | } \ 74 | inline std::string* Check ## name ## Impl(int v1, int v2, \ 75 | const char* names) { \ 76 | if (v1 op v2) { \ 77 | return NULL; \ 78 | } else { \ 79 | return MakeCheckOpString(v1, v2, names); \ 80 | } \ 81 | } 82 | 83 | DEFINE_CHECK_OP_IMPL(EQ, ==) 84 | DEFINE_CHECK_OP_IMPL(NE, !=) 85 | DEFINE_CHECK_OP_IMPL(LE, <=) 86 | DEFINE_CHECK_OP_IMPL(LT, <) 87 | DEFINE_CHECK_OP_IMPL(GE, >=) 88 | DEFINE_CHECK_OP_IMPL(GT, >) 89 | 90 | #undef DEFINE_CHECK_OP_IMPL 91 | 92 | class LogMessage { 93 | public: 94 | LogMessage(const char* function, 95 | const char* file_path, 96 | int line, 97 | LogSeverity severity); 98 | LogMessage(const char* function, 99 | const char* file_path, 100 | int line, 101 | std::string* result); 102 | ~LogMessage(); 103 | 104 | std::ostream& stream() { return stream_; } 105 | 106 | private: 107 | void Init(const char* function); 108 | 109 | std::ostringstream stream_; 110 | const char* file_path_; 111 | size_t message_start_; 112 | const int line_; 113 | LogSeverity severity_; 114 | 115 | DISALLOW_COPY_AND_ASSIGN(LogMessage); 116 | }; 117 | 118 | class LogMessageVoidify { 119 | public: 120 | LogMessageVoidify() {} 121 | 122 | void operator&(const std::ostream&) const {} 123 | }; 124 | 125 | class ErrnoLogMessage : public LogMessage { 126 | public: 127 | ErrnoLogMessage(const char* function, 128 | const char* file_path, 129 | int line, 130 | LogSeverity severity, 131 | int err); 132 | ~ErrnoLogMessage(); 133 | 134 | private: 135 | int err_; 136 | 137 | DISALLOW_COPY_AND_ASSIGN(ErrnoLogMessage); 138 | }; 139 | 140 | } // namespace logging 141 | 142 | 143 | #define FUNCTION_SIGNATURE __PRETTY_FUNCTION__ 144 | 145 | #define COMPACT_GOOGLE_LOG_EX_INFO(ClassName, ...) \ 146 | logging::ClassName(FUNCTION_SIGNATURE, __FILE__, __LINE__, \ 147 | logging::LOG_INFO, ## __VA_ARGS__) 148 | #define COMPACT_GOOGLE_LOG_EX_WARNING(ClassName, ...) \ 149 | logging::ClassName(FUNCTION_SIGNATURE, __FILE__, __LINE__, \ 150 | logging::LOG_WARNING, ## __VA_ARGS__) 151 | #define COMPACT_GOOGLE_LOG_EX_ERROR(ClassName, ...) \ 152 | logging::ClassName(FUNCTION_SIGNATURE, __FILE__, __LINE__, \ 153 | logging::LOG_ERROR, ## __VA_ARGS__) 154 | #define COMPACT_GOOGLE_LOG_EX_ERROR_REPORT(ClassName, ...) \ 155 | logging::ClassName(FUNCTION_SIGNATURE, __FILE__, __LINE__, \ 156 | logging::LOG_ERROR_REPORT, ## __VA_ARGS__) 157 | #define COMPACT_GOOGLE_LOG_EX_FATAL(ClassName, ...) \ 158 | logging::ClassName(FUNCTION_SIGNATURE, __FILE__, __LINE__, \ 159 | logging::LOG_FATAL, ## __VA_ARGS__) 160 | #define COMPACT_GOOGLE_LOG_EX_DFATAL(ClassName, ...) \ 161 | logging::ClassName(FUNCTION_SIGNATURE, __FILE__, __LINE__, \ 162 | logging::LOG_DFATAL, ## __VA_ARGS__) 163 | 164 | #define COMPACT_GOOGLE_LOG_INFO \ 165 | COMPACT_GOOGLE_LOG_EX_INFO(LogMessage) 166 | #define COMPACT_GOOGLE_LOG_WARNING \ 167 | COMPACT_GOOGLE_LOG_EX_WARNING(LogMessage) 168 | #define COMPACT_GOOGLE_LOG_ERROR \ 169 | COMPACT_GOOGLE_LOG_EX_ERROR(LogMessage) 170 | #define COMPACT_GOOGLE_LOG_ERROR_REPORT \ 171 | COMPACT_GOOGLE_LOG_EX_ERROR_REPORT(LogMessage) 172 | #define COMPACT_GOOGLE_LOG_FATAL \ 173 | COMPACT_GOOGLE_LOG_EX_FATAL(LogMessage) 174 | #define COMPACT_GOOGLE_LOG_DFATAL \ 175 | COMPACT_GOOGLE_LOG_EX_DFATAL(LogMessage) 176 | 177 | #define LAZY_STREAM(stream, condition) \ 178 | !(condition) ? (void) 0 : ::logging::LogMessageVoidify() & (stream) 179 | 180 | #define LOG_IS_ON(severity) \ 181 | ((::logging::LOG_ ## severity) >= ::logging::GetMinLogLevel()) 182 | #define VLOG_IS_ON(verbose_level) \ 183 | ((verbose_level) <= ::logging::GetVlogLevel(__FILE__)) 184 | 185 | #define LOG_STREAM(severity) COMPACT_GOOGLE_LOG_ ## severity.stream() 186 | #define VLOG_STREAM(verbose_level) \ 187 | logging::LogMessage(FUNCTION_SIGNATURE, __FILE__, __LINE__, \ 188 | -verbose_level).stream() 189 | 190 | #define PLOG_STREAM(severity) COMPACT_GOOGLE_LOG_EX_ ## severity( \ 191 | ErrnoLogMessage, ::logging::GetLastSystemErrorCode()).stream() 192 | #define VPLOG_STREAM(verbose_level) \ 193 | logging::ErrnoLogMessage(FUNCTION_SIGNATURE, __FILE__, __LINE__, \ 194 | -verbose_level, \ 195 | ::logging::GetLastSystemErrorCode()).stream() 196 | 197 | #define LOG(severity) LAZY_STREAM(LOG_STREAM(severity), LOG_IS_ON(severity)) 198 | #define LOG_IF(severity, condition) \ 199 | LAZY_STREAM(LOG_STREAM(severity), LOG_IS_ON(severity) && (condition)) 200 | #define LOG_ASSERT(condition) \ 201 | LOG_IF(FATAL, !(condition)) << "Assertion failed: " # condition ". " 202 | 203 | #define VLOG(verbose_level) \ 204 | LAZY_STREAM(VLOG_STREAM(verbose_level), VLOG_IS_ON(verbose_level)) 205 | #define VLOG_IF(verbose_level, condition) \ 206 | LAZY_STREAM(VLOG_STREAM(verbose_level), \ 207 | VLOG_IS_ON(verbose_level) && (condition)) 208 | 209 | #define PLOG(severity) LAZY_STREAM(PLOG_STREAM(severity), LOG_IS_ON(severity)) 210 | #define PLOG_IF(severity, condition) \ 211 | LAZY_STREAM(PLOG_STREAM(severity), LOG_IS_ON(severity) && (condition)) 212 | 213 | #define VPLOG(verbose_level) \ 214 | LAZY_STREAM(VPLOG_STREAM(verbose_level), VLOG_IS_ON(verbose_level)) 215 | #define VPLOG_IF(verbose_level, condition) \ 216 | LAZY_STREAM(VPLOG_STREAM(verbose_level), \ 217 | VLOG_IS_ON(verbose_level) && (condition)) 218 | 219 | #define CHECK(condition) \ 220 | LAZY_STREAM(LOG_STREAM(FATAL), !(condition)) \ 221 | << "Check failed: " # condition << ". " 222 | #define PCHECK(condition) \ 223 | LAZY_STREAM(PLOG_STREAM(FATAL), !(condition)) \ 224 | << "Check failed: " # condition << ". " 225 | 226 | #define CHECK_OP(name, op, val1, val2) \ 227 | if (std::string* _result = \ 228 | logging::Check ## name ## Impl((val1), (val2), \ 229 | # val1 " " # op " " # val2)) \ 230 | logging::LogMessage(FUNCTION_SIGNATURE, __FILE__, __LINE__, \ 231 | _result).stream() 232 | 233 | #define CHECK_EQ(val1, val2) CHECK_OP(EQ, ==, val1, val2) 234 | #define CHECK_NE(val1, val2) CHECK_OP(NE, !=, val1, val2) 235 | #define CHECK_LE(val1, val2) CHECK_OP(LE, <=, val1, val2) 236 | #define CHECK_LT(val1, val2) CHECK_OP(LT, <, val1, val2) 237 | #define CHECK_GE(val1, val2) CHECK_OP(GE, >=, val1, val2) 238 | #define CHECK_GT(val1, val2) CHECK_OP(GT, >, val1, val2) 239 | 240 | #if defined(NDEBUG) 241 | #define DLOG_IS_ON(severity) 0 242 | #define DVLOG_IS_ON(verbose_level) 0 243 | #define DCHECK_IS_ON() 0 244 | #else 245 | #define DLOG_IS_ON(severity) LOG_IS_ON(severity) 246 | #define DVLOG_IS_ON(verbose_level) VLOG_IS_ON(verbose_level) 247 | #define DCHECK_IS_ON() 1 248 | #endif 249 | 250 | #define DLOG(severity) LAZY_STREAM(LOG_STREAM(severity), DLOG_IS_ON(severity)) 251 | #define DLOG_IF(severity, condition) \ 252 | LAZY_STREAM(LOG_STREAM(severity), DLOG_IS_ON(severity) && (condition)) 253 | #define DLOG_ASSERT(condition) \ 254 | DLOG_IF(FATAL, !(condition)) << "Assertion failed: " # condition ". " 255 | 256 | #define DVLOG(verbose_level) \ 257 | LAZY_STREAM(VLOG_STREAM(verbose_level), DVLOG_IS_ON(verbose_level)) 258 | #define DVLOG_IF(verbose_level, condition) \ 259 | LAZY_STREAM(VLOG_STREAM(verbose_level), \ 260 | DVLOG_IS_ON(verbose_level) && (condition)) 261 | 262 | #define DPLOG(severity) LAZY_STREAM(PLOG_STREAM(severity), DLOG_IS_ON(severity)) 263 | #define DPLOG_IF(severity, condition) \ 264 | LAZY_STREAM(PLOG_STREAM(severity), DLOG_IS_ON(severity) && (condition)) 265 | 266 | #define DVPLOG(verbose_level) \ 267 | LAZY_STREAM(VPLOG_STREAM(verbose_level), DVLOG_IS_ON(verbose_level)) 268 | #define DVPLOG_IF(verbose_level, condition) \ 269 | LAZY_STREAM(VPLOG_STREAM(verbose_level), \ 270 | DVLOG_IS_ON(verbose_level) && (condition)) 271 | 272 | #define DCHECK(condition) \ 273 | LAZY_STREAM(LOG_STREAM(FATAL), DCHECK_IS_ON() ? !(condition) : false) \ 274 | << "Check failed: " # condition << ". " 275 | #define DPCHECK(condition) \ 276 | LAZY_STREAM(PLOG_STREAM(FATAL), DCHECK_IS_ON() ? !(condition) : false) \ 277 | << "Check failed: " # condition << ". " 278 | 279 | #define DCHECK_OP(name, op, val1, val2) \ 280 | if (DCHECK_IS_ON()) \ 281 | if (std::string* _result = \ 282 | logging::Check ## name ## Impl((val1), (val2), \ 283 | # val1 " " # op " " # val2)) \ 284 | logging::LogMessage(FUNCTION_SIGNATURE, __FILE__, __LINE__, \ 285 | _result).stream() 286 | 287 | #define DCHECK_EQ(val1, val2) DCHECK_OP(EQ, ==, val1, val2) 288 | #define DCHECK_NE(val1, val2) DCHECK_OP(NE, !=, val1, val2) 289 | #define DCHECK_LE(val1, val2) DCHECK_OP(LE, <=, val1, val2) 290 | #define DCHECK_LT(val1, val2) DCHECK_OP(LT, <, val1, val2) 291 | #define DCHECK_GE(val1, val2) DCHECK_OP(GE, >=, val1, val2) 292 | #define DCHECK_GT(val1, val2) DCHECK_OP(GT, >, val1, val2) 293 | 294 | #define NOTREACHED() DCHECK(false) 295 | 296 | #undef assert 297 | #define assert(condition) DLOG_ASSERT(condition) 298 | 299 | #endif // MINI_CHROMIUM_BASE_LOGGING_H_ 300 | -------------------------------------------------------------------------------- /base/macros.h: -------------------------------------------------------------------------------- 1 | // Copyright 2006-2008 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #ifndef MINI_CHROMIUM_BASE_MACROS_H_ 6 | #define MINI_CHROMIUM_BASE_MACROS_H_ 7 | 8 | #include 9 | #include 10 | 11 | #define DISALLOW_COPY_AND_ASSIGN(TypeName) \ 12 | TypeName(const TypeName&); \ 13 | void operator=(const TypeName&) 14 | 15 | #define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \ 16 | TypeName(); \ 17 | DISALLOW_COPY_AND_ASSIGN(TypeName) 18 | 19 | template 20 | inline void ignore_result(const T &) { 21 | } 22 | 23 | #endif // MINI_CHROMIUM_BASE_MACROS_H_ 24 | -------------------------------------------------------------------------------- /base/message_loop/incoming_task_queue.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #include "base/message_loop/incoming_task_queue.h" 6 | #include "base/message_loop/message_loop.h" 7 | 8 | namespace base { 9 | 10 | IncomingTaskQueue::IncomingTaskQueue(MessageLoop *message_loop) 11 | : message_loop_(message_loop), 12 | message_loop_scheduled_(false), 13 | is_ready_for_scheduling_(false) { 14 | } 15 | 16 | bool IncomingTaskQueue::AddToIncomingQueue( 17 | std::unique_ptr task, 18 | TimeDelta delay) { 19 | AutoLock locked(incoming_queue_lock_); 20 | std::unique_ptr pending_task( 21 | new PendingTask(std::move(task), CalculateDelayedRuntime(delay))); 22 | return PostPendingTask(std::move(pending_task)); 23 | } 24 | 25 | void IncomingTaskQueue::ReloadWorkQueue(TaskQueue *work_queue) { 26 | // Make sure no tasks are lost. 27 | DCHECK(work_queue->empty()); 28 | 29 | // Acquire all we can from the inter-thread queue with one lock acquisition. 30 | AutoLock lock(incoming_queue_lock_); 31 | if (incoming_queue_.empty()) { 32 | // If the loop attempts to reload but there are no tasks in the incoming 33 | // queue, that means it will go to sleep waiting for more work. If the 34 | // incoming queue becomes nonempty we need to schedule it again. 35 | message_loop_scheduled_ = false; 36 | } else { 37 | incoming_queue_.Swap(work_queue); 38 | } 39 | } 40 | 41 | void IncomingTaskQueue::WillDestroyCurrentMessageLoop() { 42 | AutoLock lock(incoming_queue_lock_); 43 | message_loop_ = nullptr; 44 | } 45 | 46 | void IncomingTaskQueue::StartScheduling() { 47 | AutoLock lock(incoming_queue_lock_); 48 | DCHECK(!is_ready_for_scheduling_); 49 | DCHECK(!message_loop_scheduled_); 50 | is_ready_for_scheduling_ = true; 51 | if (!incoming_queue_.empty()) 52 | ScheduleWork(); 53 | } 54 | 55 | IncomingTaskQueue::~IncomingTaskQueue() { 56 | DCHECK(!message_loop_); 57 | } 58 | 59 | TimeTicks IncomingTaskQueue::CalculateDelayedRuntime(TimeDelta delay) { 60 | TimeTicks delayed_run_time; 61 | if (delay > TimeDelta()) 62 | delayed_run_time = TimeTicks::Now() + delay; 63 | else DCHECK_EQ(delay.InMilliseconds(), 0) << "delay should not be negative"; 64 | return delayed_run_time; 65 | } 66 | 67 | bool IncomingTaskQueue::PostPendingTask(std::unique_ptr task) { 68 | if (!message_loop_) { 69 | task.reset(); 70 | return false; 71 | } 72 | 73 | bool was_empty = incoming_queue_.empty(); 74 | incoming_queue_.push(std::move(task)); 75 | 76 | if (is_ready_for_scheduling_ && 77 | !message_loop_scheduled_ && was_empty) { 78 | ScheduleWork(); 79 | } 80 | 81 | return true; 82 | } 83 | 84 | void IncomingTaskQueue::ScheduleWork() { 85 | DCHECK(is_ready_for_scheduling_); 86 | message_loop_->ScheduleWork(); 87 | message_loop_scheduled_ = true; 88 | } 89 | 90 | } // namespace base 91 | -------------------------------------------------------------------------------- /base/message_loop/incoming_task_queue.h: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #ifndef BASE_MESSAGE_LOOP_INCOMING_TASK_QUEUE_H_ 6 | #define BASE_MESSAGE_LOOP_INCOMING_TASK_QUEUE_H_ 7 | 8 | #include "base/macros.h" 9 | #include "base/pending_task.h" 10 | #include "base/synchronization/lock.h" 11 | #include "base/time/time.h" 12 | #include "base/pending_task.h" 13 | 14 | namespace base { 15 | 16 | class MessageLoop; 17 | 18 | class IncomingTaskQueue { 19 | public: 20 | explicit IncomingTaskQueue(MessageLoop *message_loop); 21 | 22 | virtual ~IncomingTaskQueue(); 23 | 24 | bool AddToIncomingQueue(std::unique_ptr task, 25 | TimeDelta delay); 26 | 27 | void ReloadWorkQueue(TaskQueue *work_queue); 28 | 29 | void WillDestroyCurrentMessageLoop(); 30 | 31 | void StartScheduling(); 32 | 33 | private: 34 | 35 | static TimeTicks CalculateDelayedRuntime(TimeDelta delay); 36 | 37 | bool PostPendingTask(std::unique_ptr task); 38 | 39 | void ScheduleWork(); 40 | 41 | MessageLoop *message_loop_; 42 | 43 | bool message_loop_scheduled_; 44 | 45 | bool is_ready_for_scheduling_; 46 | 47 | base::Lock incoming_queue_lock_; 48 | 49 | TaskQueue incoming_queue_; 50 | 51 | DISALLOW_COPY_AND_ASSIGN(IncomingTaskQueue); 52 | }; 53 | 54 | } // namespace base 55 | 56 | #endif // BASE_MESSAGE_LOOP_INCOMING_TASK_QUEUE_H_ 57 | -------------------------------------------------------------------------------- /base/message_loop/message_loop.cc: -------------------------------------------------------------------------------- 1 | #include "base/message_loop/message_loop.h" 2 | #include "base/posix/eintr_wrapper.h" 3 | #include "base/message_loop/incoming_task_queue.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | extern "C" { 11 | #include 12 | #include 13 | } 14 | 15 | namespace base { 16 | namespace { 17 | bool SetNonBlocking(int fd) { 18 | const int flags = fcntl(fd, F_GETFL); 19 | return (flags & O_NONBLOCK) || fcntl(fd, F_SETFL, flags | O_NONBLOCK) != -1; 20 | } 21 | 22 | void IgnoreSigPipeSignalOnCurrentThread() { 23 | sigset_t sigpipe_mask; 24 | sigemptyset(&sigpipe_mask); 25 | sigaddset(&sigpipe_mask, SIGPIPE); 26 | pthread_sigmask(SIG_BLOCK, &sigpipe_mask, nullptr); 27 | } 28 | 29 | pthread_key_t g_queue_ptr_tls = 0; 30 | 31 | void InitializeTls() { 32 | pthread_key_create(&g_queue_ptr_tls, nullptr); 33 | } 34 | 35 | pthread_key_t GetQueuePtrTls() { 36 | static pthread_once_t init_once = PTHREAD_ONCE_INIT; 37 | pthread_once(&init_once, &InitializeTls); 38 | return g_queue_ptr_tls; 39 | } 40 | } 41 | 42 | class MessageLoop::DelayedTask { 43 | public: 44 | explicit DelayedTask(std::unique_ptr task) 45 | : event_(nullptr), task_(std::move(task)) {} 46 | 47 | virtual ~DelayedTask() { 48 | if (event_) { 49 | event_free(event_); 50 | event_ = nullptr; 51 | } 52 | } 53 | 54 | void Run() { 55 | if (task_) { 56 | task_->Run(); 57 | task_.reset(); 58 | } 59 | } 60 | struct event *event_; 61 | private: 62 | std::unique_ptr task_; 63 | DISALLOW_COPY_AND_ASSIGN(DelayedTask); 64 | }; 65 | 66 | MessageLoop::MessageLoop() 67 | : event_base_(event_base_new()), 68 | wakeup_pipe_in_(-1), 69 | wakeup_pipe_out_(-1), 70 | wakeup_event_(nullptr), 71 | keep_running_(true), 72 | incoming_task_queue_(new IncomingTaskQueue(this)) { 73 | if (!Init()) 74 | NOTREACHED(); 75 | } 76 | 77 | MessageLoop::~MessageLoop() { 78 | for (auto &i: delayed_tasks_) { 79 | delete i; 80 | } 81 | delayed_tasks_.clear(); 82 | 83 | event_del(wakeup_event_); 84 | event_free(wakeup_event_); 85 | 86 | IgnoreSigPipeSignalOnCurrentThread(); 87 | 88 | if (wakeup_pipe_in_ >= 0) { 89 | if (IGNORE_EINTR(close(wakeup_pipe_in_)) < 0) 90 | DPLOG(ERROR) << "close"; 91 | } 92 | if (wakeup_pipe_out_ >= 0) { 93 | if (IGNORE_EINTR(close(wakeup_pipe_out_)) < 0) 94 | DPLOG(ERROR) << "close"; 95 | } 96 | incoming_task_queue_->WillDestroyCurrentMessageLoop(); 97 | incoming_task_queue_.reset(); 98 | event_base_free(event_base_); 99 | if (current() == this) 100 | pthread_setspecific(GetQueuePtrTls(), nullptr); 101 | } 102 | 103 | bool MessageLoop::Init() { 104 | evutil_socket_t fds[2]; 105 | if (pipe(fds)) { 106 | close(fds[0]); 107 | close(fds[1]); 108 | DLOG(ERROR) << "pipe() failed, errno: " << errno; 109 | return false; 110 | } 111 | if (!SetNonBlocking(fds[0])) { 112 | DLOG(ERROR) << "SetNonBlocking for pipe fd[0] failed, errno: " << errno; 113 | return false; 114 | } 115 | if (!SetNonBlocking(fds[1])) { 116 | DLOG(ERROR) << "SetNonBlocking for pipe fd[1] failed, errno: " << errno; 117 | return false; 118 | } 119 | wakeup_pipe_out_ = fds[0]; 120 | wakeup_pipe_in_ = fds[1]; 121 | wakeup_event_ = event_new(event_base_, 122 | wakeup_pipe_out_, 123 | EV_READ | EV_PERSIST, 124 | &MessageLoop::OnWakeup, 125 | this); 126 | if (event_add(wakeup_event_, nullptr)) 127 | return false; 128 | return true; 129 | } 130 | 131 | void MessageLoop::BindToCurrentThread() { 132 | pthread_setspecific(GetQueuePtrTls(), this); 133 | incoming_task_queue_->StartScheduling(); 134 | } 135 | 136 | void MessageLoop::Run() { 137 | while (keep_running_) { 138 | event_base_loop(event_base_, EVLOOP_NO_EXIT_ON_EMPTY); 139 | } 140 | } 141 | 142 | void MessageLoop::QuitNow() { 143 | keep_running_ = false; 144 | event_base_loopbreak(event_base_); 145 | } 146 | 147 | void MessageLoop::ScheduleWork() { 148 | char buf = 0; 149 | int nwrite = HANDLE_EINTR(write(wakeup_pipe_in_, &buf, 1)); 150 | DCHECK(nwrite == 1 || errno == EAGAIN) 151 | << "[nwrite:" << nwrite << "] [errno:" << errno << "]"; 152 | } 153 | 154 | MessageLoop *MessageLoop::current() { 155 | return static_cast(pthread_getspecific(GetQueuePtrTls())); 156 | } 157 | 158 | event_base *MessageLoop::base() { 159 | return event_base_; 160 | } 161 | 162 | void MessageLoop::OnWakeup(evutil_socket_t socket, short flags, void *context) { 163 | char buf; 164 | int nread = HANDLE_EINTR(read(socket, &buf, 1)); 165 | DCHECK_EQ(nread, 1); 166 | 167 | auto ptr = reinterpret_cast(context); 168 | for (;;) { 169 | TaskQueue work_queue; 170 | ptr->ReloadWorkQueue(&work_queue); 171 | if (work_queue.empty()) 172 | break; 173 | do { 174 | std::unique_ptr pending_task(std::move(work_queue.front())); 175 | work_queue.pop(); 176 | if (!pending_task->delayed_run_time_.is_null()) { 177 | ptr->AddToDelayedWorkQueue(std::move(pending_task)); 178 | } else { 179 | pending_task->Run(); 180 | } 181 | } while (!work_queue.empty()); 182 | } 183 | } 184 | 185 | void MessageLoop::PostDelayedTask(std::unique_ptr task, 186 | TimeDelta delay) { 187 | incoming_task_queue_->AddToIncomingQueue(std::move(task), delay); 188 | } 189 | 190 | void MessageLoop::ReloadWorkQueue(TaskQueue *work_queue) { 191 | incoming_task_queue_->ReloadWorkQueue(work_queue); 192 | } 193 | 194 | void MessageLoop::AddToDelayedWorkQueue(std::unique_ptr task) { 195 | base::TimeTicks now = base::TimeTicks::Now(); 196 | if (task->delayed_run_time_ <= now) { 197 | task->Run(); 198 | } else { 199 | base::TimeDelta delay = task->delayed_run_time_ - now; 200 | auto delayed_task = new DelayedTask(std::move(task->task_)); 201 | delayed_task->event_ = event_new(event_base_, -1, EV_TIMEOUT, &MessageLoop::RunTimer, delayed_task); 202 | timeval tv = {static_cast<__time_t>(delay.InSeconds()), 203 | static_cast<__suseconds_t>(delay.InMicroseconds() % Time::kMicrosecondsPerSecond)}; 204 | event_add(delayed_task->event_, &tv); 205 | delayed_tasks_.push_back(delayed_task); 206 | } 207 | } 208 | 209 | void MessageLoop::RunTimer(evutil_socket_t socket, short flags, void *context) { 210 | auto task = reinterpret_cast(context); 211 | task->Run(); 212 | auto ptr = MessageLoop::current(); 213 | ptr->delayed_tasks_.remove(task); 214 | delete task; 215 | } 216 | } -------------------------------------------------------------------------------- /base/message_loop/message_loop.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #ifndef BASE_MESSAGE_LOOP_MESSAGE_LOOP_H_ 6 | #define BASE_MESSAGE_LOOP_MESSAGE_LOOP_H_ 7 | 8 | #include 9 | #include 10 | #include "base/time/time.h" 11 | #include "base/callback.h" 12 | #include "base/pending_task.h" 13 | 14 | extern "C" { 15 | #include 16 | } 17 | 18 | struct event_base; 19 | struct event; 20 | 21 | namespace base { 22 | class IncomingTaskQueue; 23 | 24 | class MessageLoop { 25 | public: 26 | MessageLoop(); 27 | virtual ~MessageLoop(); 28 | 29 | void PostDelayedTask(std::unique_ptr task, 30 | TimeDelta delay); 31 | void Run(); 32 | 33 | void QuitNow(); 34 | 35 | void ScheduleWork(); 36 | 37 | static MessageLoop *current(); 38 | 39 | event_base *base(); 40 | 41 | private: 42 | friend class Thread; 43 | 44 | class DelayedTask; 45 | 46 | bool Init(); 47 | 48 | void BindToCurrentThread(); 49 | 50 | void ReloadWorkQueue(TaskQueue *work_queue); 51 | 52 | void AddToDelayedWorkQueue(std::unique_ptr task); 53 | 54 | static void OnWakeup(evutil_socket_t socket, short flags, void *context); 55 | 56 | static void RunTimer(evutil_socket_t socket, short flags, void *context); 57 | 58 | event_base *event_base_; 59 | 60 | evutil_socket_t wakeup_pipe_in_; 61 | 62 | evutil_socket_t wakeup_pipe_out_; 63 | 64 | event *wakeup_event_; 65 | 66 | bool keep_running_; 67 | 68 | std::list delayed_tasks_; 69 | 70 | std::unique_ptr incoming_task_queue_; 71 | 72 | DISALLOW_COPY_AND_ASSIGN(MessageLoop); 73 | }; 74 | 75 | } // namespace base 76 | 77 | #endif // BASE_MESSAGE_LOOP_MESSAGE_LOOP_H_ 78 | -------------------------------------------------------------------------------- /base/numerics/safe_conversions.h: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #ifndef BASE_NUMERICS_SAFE_CONVERSIONS_H_ 6 | #define BASE_NUMERICS_SAFE_CONVERSIONS_H_ 7 | 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | #include "base/logging.h" 14 | #include "base/numerics/safe_conversions_impl.h" 15 | 16 | namespace base { 17 | 18 | // Convenience function that returns true if the supplied value is in range 19 | // for the destination type. 20 | template 21 | inline bool IsValueInRangeForNumericType(Src value) { 22 | return internal::DstRangeRelationToSrcRange(value) == 23 | internal::RANGE_VALID; 24 | } 25 | 26 | // Convenience function for determining if a numeric value is negative without 27 | // throwing compiler warnings on: unsigned(value) < 0. 28 | template 29 | typename std::enable_if::is_signed, bool>::type 30 | IsValueNegative(T value) { 31 | static_assert(std::numeric_limits::is_specialized, 32 | "Argument must be numeric."); 33 | return value < 0; 34 | } 35 | 36 | template 37 | typename std::enable_if::is_signed, bool>::type 38 | IsValueNegative(T) { 39 | static_assert(std::numeric_limits::is_specialized, 40 | "Argument must be numeric."); 41 | return false; 42 | } 43 | 44 | // checked_cast<> is analogous to static_cast<> for numeric types, 45 | // except that it CHECKs that the specified numeric conversion will not 46 | // overflow or underflow. NaN source will always trigger a CHECK. 47 | template 48 | inline Dst checked_cast(Src value) { 49 | CHECK(IsValueInRangeForNumericType(value)); 50 | return static_cast(value); 51 | } 52 | 53 | // HandleNaN will cause this class to CHECK(false). 54 | struct SaturatedCastNaNBehaviorCheck { 55 | template 56 | static T HandleNaN() { 57 | CHECK(false); 58 | return T(); 59 | } 60 | }; 61 | 62 | // HandleNaN will return 0 in this case. 63 | struct SaturatedCastNaNBehaviorReturnZero { 64 | template 65 | static T HandleNaN() { 66 | return T(); 67 | } 68 | }; 69 | 70 | // saturated_cast<> is analogous to static_cast<> for numeric types, except 71 | // that the specified numeric conversion will saturate rather than overflow or 72 | // underflow. NaN assignment to an integral will defer the behavior to a 73 | // specified class. By default, it will return 0. 74 | template 77 | inline Dst saturated_cast(Src value) { 78 | // Optimization for floating point values, which already saturate. 79 | if (std::numeric_limits::is_iec559) 80 | return static_cast(value); 81 | 82 | switch (internal::DstRangeRelationToSrcRange(value)) { 83 | case internal::RANGE_VALID: 84 | return static_cast(value); 85 | 86 | case internal::RANGE_UNDERFLOW: 87 | return std::numeric_limits::min(); 88 | 89 | case internal::RANGE_OVERFLOW: 90 | return std::numeric_limits::max(); 91 | 92 | // Should fail only on attempting to assign NaN to a saturated integer. 93 | case internal::RANGE_INVALID: 94 | return NaNHandler::template HandleNaN(); 95 | } 96 | 97 | NOTREACHED(); 98 | return static_cast(value); 99 | } 100 | 101 | // strict_cast<> is analogous to static_cast<> for numeric types, except that 102 | // it will cause a compile failure if the destination type is not large enough 103 | // to contain any value in the source type. It performs no runtime checking. 104 | template 105 | inline Dst strict_cast(Src value) { 106 | static_assert(std::numeric_limits::is_specialized, 107 | "Argument must be numeric."); 108 | static_assert(std::numeric_limits::is_specialized, 109 | "Result must be numeric."); 110 | static_assert((internal::StaticDstRangeRelationToSrcRange::value == 111 | internal::NUMERIC_RANGE_CONTAINED), 112 | "The numeric conversion is out of range for this type. You " 113 | "should probably use one of the following conversion " 114 | "mechanisms on the value you want to pass:\n" 115 | "- base::checked_cast\n" 116 | "- base::saturated_cast\n" 117 | "- base::CheckedNumeric"); 118 | 119 | return static_cast(value); 120 | } 121 | 122 | // StrictNumeric implements compile time range checking between numeric types by 123 | // wrapping assignment operations in a strict_cast. This class is intended to be 124 | // used for function arguments and return types, to ensure the destination type 125 | // can always contain the source type. This is essentially the same as enforcing 126 | // -Wconversion in gcc and C4302 warnings on MSVC, but it can be applied 127 | // incrementally at API boundaries, making it easier to convert code so that it 128 | // compiles cleanly with truncation warnings enabled. 129 | // This template should introduce no runtime overhead, but it also provides no 130 | // runtime checking of any of the associated mathematical operations. Use 131 | // CheckedNumeric for runtime range checks of tha actual value being assigned. 132 | template 133 | class StrictNumeric { 134 | public: 135 | typedef T type; 136 | 137 | StrictNumeric() : value_(0) {} 138 | 139 | // Copy constructor. 140 | template 141 | StrictNumeric(const StrictNumeric& rhs) 142 | : value_(strict_cast(rhs.value_)) {} 143 | 144 | // This is not an explicit constructor because we implicitly upgrade regular 145 | // numerics to StrictNumerics to make them easier to use. 146 | template 147 | StrictNumeric(Src value) 148 | : value_(strict_cast(value)) {} 149 | 150 | // The numeric cast operator basically handles all the magic. 151 | template 152 | operator Dst() const { 153 | return strict_cast(value_); 154 | } 155 | 156 | private: 157 | T value_; 158 | }; 159 | 160 | // Explicitly make a shorter size_t typedef for convenience. 161 | typedef StrictNumeric SizeT; 162 | 163 | } // namespace base 164 | 165 | #endif // BASE_NUMERICS_SAFE_CONVERSIONS_H_ 166 | -------------------------------------------------------------------------------- /base/pending_task.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #include "base/pending_task.h" 6 | 7 | namespace base { 8 | 9 | PendingTask::PendingTask(std::unique_ptr task, 10 | base::TimeTicks delayed_run_time) 11 | : task_(std::move(task)), 12 | delayed_run_time_(delayed_run_time) {} 13 | 14 | PendingTask::~PendingTask() = default; 15 | 16 | void PendingTask::Run() { 17 | if (task_) { 18 | task_->Run(); 19 | task_.reset(); 20 | } 21 | } 22 | 23 | void TaskQueue::Swap(TaskQueue *queue) { 24 | c.swap(queue->c); 25 | } 26 | } // namespace base 27 | -------------------------------------------------------------------------------- /base/pending_task.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #ifndef BASE_PENDING_TASK_H_ 6 | #define BASE_PENDING_TASK_H_ 7 | 8 | #include 9 | #include "base/time/time.h" 10 | #include "base/callback.h" 11 | 12 | namespace base { 13 | 14 | class PendingTask { 15 | public: 16 | explicit PendingTask(std::unique_ptr task, 17 | base::TimeTicks delayed_run_time); 18 | 19 | virtual ~PendingTask(); 20 | 21 | void Run(); 22 | 23 | std::unique_ptr task_; 24 | base::TimeTicks delayed_run_time_; 25 | private: 26 | DISALLOW_COPY_AND_ASSIGN(PendingTask); 27 | }; 28 | 29 | class TaskQueue : public std::queue> { 30 | public: 31 | void Swap(TaskQueue *queue); 32 | }; 33 | 34 | } // namespace base 35 | 36 | #endif // BASE_PENDING_TASK_H_ 37 | -------------------------------------------------------------------------------- /base/posix/eintr_wrapper.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | // This provides a wrapper around system calls which may be interrupted by a 6 | // signal and return EINTR. See man 7 signal. 7 | // To prevent long-lasting loops (which would likely be a bug, such as a signal 8 | // that should be masked) to go unnoticed, there is a limit after which the 9 | // caller will nonetheless see an EINTR in Debug builds. 10 | // 11 | // On Windows, this wrapper macro does nothing. 12 | // 13 | // Don't wrap close calls in HANDLE_EINTR. Use IGNORE_EINTR if the return 14 | // value of close is significant. See http://crbug.com/269623. 15 | 16 | #ifndef BASE_POSIX_EINTR_WRAPPER_H_ 17 | #define BASE_POSIX_EINTR_WRAPPER_H_ 18 | 19 | #include 20 | 21 | #if defined(NDEBUG) 22 | 23 | #define HANDLE_EINTR(x) ({ \ 24 | decltype(x) eintr_wrapper_result; \ 25 | do { \ 26 | eintr_wrapper_result = (x); \ 27 | } while (eintr_wrapper_result == -1 && errno == EINTR); \ 28 | eintr_wrapper_result; \ 29 | }) 30 | 31 | #else 32 | 33 | #define HANDLE_EINTR(x) ({ \ 34 | int eintr_wrapper_counter = 0; \ 35 | decltype(x) eintr_wrapper_result; \ 36 | do { \ 37 | eintr_wrapper_result = (x); \ 38 | } while (eintr_wrapper_result == -1 && errno == EINTR && \ 39 | eintr_wrapper_counter++ < 100); \ 40 | eintr_wrapper_result; \ 41 | }) 42 | 43 | #endif // NDEBUG 44 | 45 | #define IGNORE_EINTR(x) ({ \ 46 | decltype(x) eintr_wrapper_result; \ 47 | do { \ 48 | eintr_wrapper_result = (x); \ 49 | if (eintr_wrapper_result == -1 && errno == EINTR) { \ 50 | eintr_wrapper_result = 0; \ 51 | } \ 52 | } while (0); \ 53 | eintr_wrapper_result; \ 54 | }) 55 | 56 | #endif // BASE_POSIX_EINTR_WRAPPER_H_ 57 | -------------------------------------------------------------------------------- /base/posix/safe_strerror.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #include "base/posix/safe_strerror.h" 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #include "base/stl_util.h" 12 | 13 | namespace base { 14 | 15 | void safe_strerror_r(int err, char* buf, size_t len) { 16 | #if defined(__GLIBC__) 17 | char* ret = strerror_r(err, buf, len); 18 | if (ret != buf) { 19 | snprintf(buf, len, "%s", ret); 20 | } 21 | #else 22 | int result = strerror_r(err, buf, len); 23 | if (result != 0) { 24 | snprintf(buf, 25 | len, 26 | "Error %d while retrieving error %d", 27 | result > 0 ? result : errno, 28 | err); 29 | } 30 | #endif 31 | } 32 | 33 | std::string safe_strerror(int err) { 34 | char buf[256]; 35 | safe_strerror_r(err, buf, size(buf)); 36 | return std::string(buf); 37 | } 38 | 39 | } // namespace base 40 | -------------------------------------------------------------------------------- /base/posix/safe_strerror.h: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #ifndef BASE_POSIX_SAFE_STRERROR_H_ 6 | #define BASE_POSIX_SAFE_STRERROR_H_ 7 | 8 | #include 9 | 10 | namespace base { 11 | 12 | void safe_strerror_r(int err, char *buf, size_t len); 13 | std::string safe_strerror(int err); 14 | 15 | } // namespace base 16 | 17 | #endif // BASE_POSIX_SAFE_STRERROR_H_ 18 | -------------------------------------------------------------------------------- /base/ptr_util.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 The WebRTC Project Authors. All rights reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. An additional intellectual property rights grant can be found 7 | * in the file PATENTS. All contributing project authors may 8 | * be found in the AUTHORS file in the root of the source tree. 9 | */ 10 | 11 | // This implementation is borrowed from chromium. 12 | 13 | #ifndef BASE_PTR_UTIL_H_ 14 | #define BASE_PTR_UTIL_H_ 15 | 16 | #include 17 | #include 18 | 19 | namespace base { 20 | 21 | // Helper to transfer ownership of a raw pointer to a std::unique_ptr. 22 | // Note that std::unique_ptr has very different semantics from 23 | // std::unique_ptr: do not use this helper for array allocations. 24 | template 25 | std::unique_ptr WrapUnique(T* ptr) { 26 | return std::unique_ptr(ptr); 27 | } 28 | 29 | namespace internal { 30 | 31 | template 32 | struct MakeUniqueResult { 33 | using Scalar = std::unique_ptr; 34 | }; 35 | 36 | template 37 | struct MakeUniqueResult { 38 | using Array = std::unique_ptr; 39 | }; 40 | 41 | template 42 | struct MakeUniqueResult { 43 | using Invalid = void; 44 | }; 45 | 46 | } // namespace internal 47 | 48 | // Helper to construct an object wrapped in a std::unique_ptr. This is an 49 | // implementation of C++14's std::make_unique that can be used in Chrome. 50 | // 51 | // MakeUnique(args) should be preferred over WrapUnique(new T(args)): bare 52 | // calls to `new` should be treated with scrutiny. 53 | // 54 | // Usage: 55 | // // ptr is a std::unique_ptr 56 | // auto ptr = MakeUnique("hello world!"); 57 | // 58 | // // arr is a std::unique_ptr 59 | // auto arr = MakeUnique(5); 60 | 61 | // Overload for non-array types. Arguments are forwarded to T's constructor. 62 | template 63 | typename internal::MakeUniqueResult::Scalar MakeUnique(Args&&... args) { 64 | return std::unique_ptr(new T(std::forward(args)...)); 65 | } 66 | 67 | // Overload for array types of unknown bound, e.g. T[]. The array is allocated 68 | // with `new T[n]()` and value-initialized: note that this is distinct from 69 | // `new T[n]`, which default-initializes. 70 | template 71 | typename internal::MakeUniqueResult::Array MakeUnique(size_t size) { 72 | return std::unique_ptr(new typename std::remove_extent::type[size]()); 73 | } 74 | 75 | // Overload to reject array types of known bound, e.g. T[n]. 76 | template 77 | typename internal::MakeUniqueResult::Invalid MakeUnique(Args&&... args) = 78 | delete; 79 | 80 | } // namespace base 81 | 82 | #endif // BASE_PTR_UTIL_H_ 83 | -------------------------------------------------------------------------------- /base/stl_util.h: -------------------------------------------------------------------------------- 1 | // Copyright 2006-2008 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #ifndef MINI_CHROMIUM_BASE_STL_UTIL_H_ 6 | #define MINI_CHROMIUM_BASE_STL_UTIL_H_ 7 | 8 | #include 9 | 10 | namespace base { 11 | 12 | template 13 | constexpr size_t size(const T (&array)[N]) noexcept { 14 | return N; 15 | } 16 | 17 | } // namespace base 18 | 19 | #endif // MINI_CHROMIUM_BASE_STL_UTIL_H_ 20 | -------------------------------------------------------------------------------- /base/synchronization/lock.cc: -------------------------------------------------------------------------------- 1 | #include "base/synchronization/lock.h" 2 | 3 | namespace base { 4 | Lock::Lock() { 5 | pthread_mutex_init(&native_handle_, nullptr); 6 | } 7 | 8 | Lock::~Lock() { 9 | pthread_mutex_destroy(&native_handle_); 10 | } 11 | 12 | void Lock::Acquire() { 13 | pthread_mutex_lock(&native_handle_); 14 | } 15 | 16 | void Lock::Release() { 17 | pthread_mutex_unlock(&native_handle_); 18 | } 19 | 20 | bool Lock::Try() { 21 | int rv = pthread_mutex_trylock(&native_handle_); 22 | return rv == 0; 23 | } 24 | 25 | } // namespace base 26 | 27 | -------------------------------------------------------------------------------- /base/synchronization/lock.h: -------------------------------------------------------------------------------- 1 | #ifndef BASE_SYNCHRONIZATION_LOCK_H_ 2 | #define BASE_SYNCHRONIZATION_LOCK_H_ 3 | 4 | #include 5 | #include "base/macros.h" 6 | 7 | namespace base { 8 | 9 | class Lock { 10 | public: 11 | Lock(); 12 | ~Lock(); 13 | void Acquire(); 14 | void Release(); 15 | bool Try(); 16 | 17 | private: 18 | pthread_mutex_t native_handle_{}; 19 | DISALLOW_COPY_AND_ASSIGN(Lock); 20 | }; 21 | 22 | class AutoLock { 23 | public: 24 | explicit AutoLock(Lock &lock) : lock_(lock) { 25 | lock_.Acquire(); 26 | } 27 | 28 | ~AutoLock() { 29 | lock_.Release(); 30 | } 31 | 32 | private: 33 | Lock &lock_; 34 | DISALLOW_COPY_AND_ASSIGN(AutoLock); 35 | }; 36 | 37 | class AutoUnlock { 38 | public: 39 | explicit AutoUnlock(Lock &lock) : lock_(lock) { 40 | lock_.Release(); 41 | } 42 | 43 | ~AutoUnlock() { 44 | lock_.Acquire(); 45 | } 46 | 47 | private: 48 | Lock &lock_; 49 | DISALLOW_COPY_AND_ASSIGN(AutoUnlock); 50 | }; 51 | 52 | } // namespace base 53 | 54 | #endif // BASE_SYNCHRONIZATION_LOCK_H_ 55 | -------------------------------------------------------------------------------- /base/synchronization/waitable_event.cc: -------------------------------------------------------------------------------- 1 | #include "base/synchronization/waitable_event.h" 2 | #include 3 | #include 4 | 5 | namespace base { 6 | 7 | namespace { 8 | 9 | timespec GetTimespec(const int64_t milliseconds_from_now) { 10 | timespec ts{}; 11 | clock_gettime(CLOCK_MONOTONIC, &ts); 12 | ts.tv_sec += (milliseconds_from_now / 1000); 13 | ts.tv_nsec += (milliseconds_from_now % 1000) * 1000000; 14 | 15 | if (ts.tv_nsec >= 1000000000) { 16 | ts.tv_sec++; 17 | ts.tv_nsec -= 1000000000; 18 | } 19 | return ts; 20 | } 21 | } // namespace 22 | 23 | WaitableEvent::WaitableEvent(bool manual_reset, bool initially_signaled) 24 | : is_manual_reset_(manual_reset), 25 | event_status_(initially_signaled) { 26 | pthread_mutex_init(&event_mutex_, nullptr); 27 | pthread_condattr_t cond_attr; 28 | pthread_condattr_init(&cond_attr); 29 | pthread_condattr_setclock(&cond_attr, CLOCK_MONOTONIC); 30 | pthread_cond_init(&event_cond_, &cond_attr); 31 | pthread_condattr_destroy(&cond_attr); 32 | } 33 | 34 | WaitableEvent::~WaitableEvent() { 35 | pthread_mutex_destroy(&event_mutex_); 36 | pthread_cond_destroy(&event_cond_); 37 | } 38 | 39 | void WaitableEvent::Reset() { 40 | pthread_mutex_lock(&event_mutex_); 41 | event_status_ = false; 42 | pthread_mutex_unlock(&event_mutex_); 43 | } 44 | 45 | void WaitableEvent::Signal() { 46 | pthread_mutex_lock(&event_mutex_); 47 | event_status_ = true; 48 | pthread_cond_broadcast(&event_cond_); 49 | pthread_mutex_unlock(&event_mutex_); 50 | } 51 | 52 | bool WaitableEvent::IsSignaled() { 53 | pthread_mutex_lock(&event_mutex_); 54 | bool status = event_status_; 55 | pthread_mutex_unlock(&event_mutex_); 56 | return status; 57 | } 58 | 59 | void WaitableEvent::Wait() { 60 | TimedWait(kForever); 61 | } 62 | 63 | bool WaitableEvent::TimedWait(const TimeDelta &wait_delta) { 64 | auto timeout_ms = static_cast(wait_delta.InMilliseconds()); 65 | return TimedWait(timeout_ms); 66 | } 67 | 68 | bool WaitableEvent::TimedWait(int64_t milliseconds) { 69 | int error = 0; 70 | struct timespec ts{}; 71 | if (milliseconds != kForever) { 72 | ts = GetTimespec(milliseconds); 73 | } 74 | pthread_mutex_lock(&event_mutex_); 75 | 76 | if (milliseconds != kForever) { 77 | while (!event_status_ && error == 0) { 78 | error = pthread_cond_timedwait(&event_cond_, &event_mutex_, &ts); 79 | } 80 | } else { 81 | while (!event_status_ && error == 0) 82 | error = pthread_cond_wait(&event_cond_, &event_mutex_); 83 | } 84 | 85 | if (error == 0 && !is_manual_reset_) 86 | event_status_ = false; 87 | 88 | pthread_mutex_unlock(&event_mutex_); 89 | return (error == 0); 90 | } 91 | 92 | } // namespace base 93 | -------------------------------------------------------------------------------- /base/synchronization/waitable_event.h: -------------------------------------------------------------------------------- 1 | #ifndef BASE_SYNCHRONIZATION_WAITABLE_EVENT_H_ 2 | #define BASE_SYNCHRONIZATION_WAITABLE_EVENT_H_ 3 | 4 | #include 5 | #include 6 | #include "base/macros.h" 7 | #include "base/time/time.h" 8 | 9 | namespace base { 10 | 11 | class WaitableEvent { 12 | public: 13 | explicit WaitableEvent(bool manual_reset, bool initially_signaled); 14 | 15 | ~WaitableEvent(); 16 | 17 | void Reset(); 18 | 19 | void Signal(); 20 | 21 | void Wait(); 22 | 23 | bool IsSignaled(); 24 | 25 | bool TimedWait(const TimeDelta &wait_delta); 26 | 27 | bool TimedWait(int64_t milliseconds); 28 | 29 | private: 30 | 31 | static const int kForever = -1; 32 | 33 | pthread_mutex_t event_mutex_{}; 34 | pthread_cond_t event_cond_{}; 35 | const bool is_manual_reset_; 36 | bool event_status_; 37 | DISALLOW_COPY_AND_ASSIGN(WaitableEvent); 38 | }; 39 | } // namespace base 40 | #endif // BASE_SYNCHRONIZATION_WAITABLE_EVENT_H_ -------------------------------------------------------------------------------- /base/threading/platform_thread.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | // WARNING: You should *NOT* be using this class directly. PlatformThread is 6 | // the low-level platform-specific abstraction to the OS's threading interface. 7 | // You should instead be using a message-loop driven Thread, see thread.h. 8 | 9 | #ifndef BASE_THREADING_PLATFORM_THREAD_H_ 10 | #define BASE_THREADING_PLATFORM_THREAD_H_ 11 | 12 | #include 13 | 14 | #include "base/macros.h" 15 | #include "base/time/time.h" 16 | #include "build/build_config.h" 17 | 18 | #include 19 | #include 20 | 21 | namespace base { 22 | 23 | typedef pid_t PlatformThreadId; 24 | 25 | 26 | // Used for thread checking and debugging. 27 | // Meant to be as fast as possible. 28 | // These are produced by PlatformThread::CurrentRef(), and used to later 29 | // check if we are on the same thread or not by using ==. These are safe 30 | // to copy between threads, but can't be copied to another process as they 31 | // have no meaning there. Also, the internal identifier can be re-used 32 | // after a thread dies, so a PlatformThreadRef cannot be reliably used 33 | // to distinguish a new thread from an old, dead thread. 34 | class PlatformThreadRef { 35 | public: 36 | typedef pthread_t RefType; 37 | PlatformThreadRef() 38 | : id_(0) { 39 | } 40 | 41 | explicit PlatformThreadRef(RefType id) 42 | : id_(id) { 43 | } 44 | 45 | bool operator==(PlatformThreadRef other) const { 46 | return id_ == other.id_; 47 | } 48 | 49 | bool is_null() const { 50 | return id_ == 0; 51 | } 52 | private: 53 | RefType id_; 54 | }; 55 | 56 | // Used to operate on threads. 57 | class PlatformThreadHandle { 58 | public: 59 | typedef pthread_t Handle; 60 | 61 | PlatformThreadHandle() : handle_(0) {} 62 | 63 | explicit PlatformThreadHandle(Handle handle) : handle_(handle) {} 64 | 65 | bool is_equal(const PlatformThreadHandle& other) const { 66 | return handle_ == other.handle_; 67 | } 68 | 69 | bool is_null() const { 70 | return !handle_; 71 | } 72 | 73 | Handle platform_handle() const { 74 | return handle_; 75 | } 76 | 77 | private: 78 | Handle handle_; 79 | }; 80 | 81 | const PlatformThreadId kInvalidThreadId(0); 82 | 83 | // Valid values for priority of Thread::Options and SimpleThread::Options, and 84 | // SetCurrentThreadPriority(), listed in increasing order of importance. 85 | enum class ThreadPriority { 86 | // Suitable for threads that shouldn't disrupt high priority work. 87 | BACKGROUND, 88 | // Default priority level. 89 | NORMAL, 90 | // Suitable for threads which generate data for the display (at ~60Hz). 91 | DISPLAY, 92 | // Suitable for low-latency, glitch-resistant audio. 93 | REALTIME_AUDIO, 94 | }; 95 | 96 | // A namespace for low-level thread functions. 97 | class PlatformThread { 98 | public: 99 | // Implement this interface to run code on a background thread. Your 100 | // ThreadMain method will be called on the newly created thread. 101 | class Delegate { 102 | public: 103 | virtual void ThreadMain() = 0; 104 | 105 | protected: 106 | virtual ~Delegate() {} 107 | }; 108 | 109 | // Gets the current thread id, which may be useful for logging purposes. 110 | static PlatformThreadId CurrentId(); 111 | 112 | // Gets the current thread reference, which can be used to check if 113 | // we're on the right thread quickly. 114 | static PlatformThreadRef CurrentRef(); 115 | 116 | // Get the handle representing the current thread. On Windows, this is a 117 | // pseudo handle constant which will always represent the thread using it and 118 | // hence should not be shared with other threads nor be used to differentiate 119 | // the current thread from another. 120 | static PlatformThreadHandle CurrentHandle(); 121 | 122 | // Yield the current thread so another thread can be scheduled. 123 | static void YieldCurrentThread(); 124 | 125 | // Sleeps for the specified duration. 126 | static void Sleep(base::TimeDelta duration); 127 | 128 | // Sets the thread name visible to debuggers/tools. This has no effect 129 | // otherwise. 130 | static void SetName(const std::string& name); 131 | 132 | // Gets the thread name, if previously set by SetName. 133 | static const char* GetName(); 134 | 135 | // Creates a new thread. The |stack_size| parameter can be 0 to indicate 136 | // that the default stack size should be used. Upon success, 137 | // |*thread_handle| will be assigned a handle to the newly created thread, 138 | // and |delegate|'s ThreadMain method will be executed on the newly created 139 | // thread. 140 | // NOTE: When you are done with the thread handle, you must call Join to 141 | // release system resources associated with the thread. You must ensure that 142 | // the Delegate object outlives the thread. 143 | static bool Create(size_t stack_size, 144 | Delegate* delegate, 145 | PlatformThreadHandle* thread_handle) { 146 | return CreateWithPriority(stack_size, delegate, thread_handle, 147 | ThreadPriority::NORMAL); 148 | } 149 | 150 | // CreateWithPriority() does the same thing as Create() except the priority of 151 | // the thread is set based on |priority|. 152 | static bool CreateWithPriority(size_t stack_size, Delegate* delegate, 153 | PlatformThreadHandle* thread_handle, 154 | ThreadPriority priority); 155 | 156 | // CreateNonJoinable() does the same thing as Create() except the thread 157 | // cannot be Join()'d. Therefore, it also does not output a 158 | // PlatformThreadHandle. 159 | static bool CreateNonJoinable(size_t stack_size, Delegate* delegate); 160 | 161 | // Joins with a thread created via the Create function. This function blocks 162 | // the caller until the designated thread exits. This will invalidate 163 | // |thread_handle|. 164 | static void Join(PlatformThreadHandle thread_handle); 165 | 166 | // Toggles the current thread's priority at runtime. A thread may not be able 167 | // to raise its priority back up after lowering it if the process does not 168 | // have a proper permission, e.g. CAP_SYS_NICE on Linux. A thread may not be 169 | // able to lower its priority back down after raising it to REALTIME_AUDIO. 170 | // Since changing other threads' priority is not permitted in favor of 171 | // security, this interface is restricted to change only the current thread 172 | // priority (https://crbug.com/399473). 173 | static void SetCurrentThreadPriority(ThreadPriority priority); 174 | 175 | static ThreadPriority GetCurrentThreadPriority(); 176 | 177 | private: 178 | DISALLOW_IMPLICIT_CONSTRUCTORS(PlatformThread); 179 | }; 180 | 181 | } // namespace base 182 | 183 | #endif // BASE_THREADING_PLATFORM_THREAD_H_ 184 | -------------------------------------------------------------------------------- /base/threading/platform_thread_internal_posix.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #include "base/threading/platform_thread_internal_posix.h" 6 | 7 | #include "base/logging.h" 8 | 9 | namespace base { 10 | 11 | namespace internal { 12 | 13 | int ThreadPriorityToNiceValue(ThreadPriority priority) { 14 | for (const ThreadPriorityToNiceValuePair& pair : 15 | kThreadPriorityToNiceValueMap) { 16 | if (pair.priority == priority) 17 | return pair.nice_value; 18 | } 19 | NOTREACHED() << "Unknown ThreadPriority"; 20 | return 0; 21 | } 22 | 23 | ThreadPriority NiceValueToThreadPriority(int nice_value) { 24 | for (const ThreadPriorityToNiceValuePair& pair : 25 | kThreadPriorityToNiceValueMap) { 26 | if (pair.nice_value == nice_value) 27 | return pair.priority; 28 | } 29 | NOTREACHED() << "Unknown nice value"; 30 | return ThreadPriority::NORMAL; 31 | } 32 | 33 | } // namespace internal 34 | 35 | } // namespace base 36 | -------------------------------------------------------------------------------- /base/threading/platform_thread_internal_posix.h: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #ifndef BASE_THREADING_PLATFORM_THREAD_INTERNAL_POSIX_H_ 6 | #define BASE_THREADING_PLATFORM_THREAD_INTERNAL_POSIX_H_ 7 | 8 | #include "base/threading/platform_thread.h" 9 | 10 | namespace base { 11 | 12 | namespace internal { 13 | 14 | struct ThreadPriorityToNiceValuePair { 15 | ThreadPriority priority; 16 | int nice_value; 17 | }; 18 | extern const ThreadPriorityToNiceValuePair kThreadPriorityToNiceValueMap[4]; 19 | 20 | // Returns the nice value matching |priority| based on the platform-specific 21 | // implementation of kThreadPriorityToNiceValueMap. 22 | int ThreadPriorityToNiceValue(ThreadPriority priority); 23 | 24 | // Returns the ThreadPrioirty matching |nice_value| based on the platform- 25 | // specific implementation of kThreadPriorityToNiceValueMap. 26 | ThreadPriority NiceValueToThreadPriority(int nice_value); 27 | 28 | // Allows platform specific tweaks to the generic POSIX solution for 29 | // SetCurrentThreadPriority. Returns true if the platform-specific 30 | // implementation handled this |priority| change, false if the generic 31 | // implementation should instead proceed. 32 | bool SetCurrentThreadPriorityForPlatform(ThreadPriority priority); 33 | 34 | // Returns true if there is a platform-specific ThreadPriority set on the 35 | // current thread (and returns the actual ThreadPriority via |priority|). 36 | // Returns false otherwise, leaving |priority| untouched. 37 | bool GetCurrentThreadPriorityForPlatform(ThreadPriority* priority); 38 | 39 | } // namespace internal 40 | 41 | } // namespace base 42 | 43 | #endif // BASE_THREADING_PLATFORM_THREAD_INTERNAL_POSIX_H_ 44 | -------------------------------------------------------------------------------- /base/threading/platform_thread_linux.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #include "base/threading/platform_thread.h" 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #include "base/logging.h" 12 | #include "base/threading/platform_thread_internal_posix.h" 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | namespace base { 20 | 21 | namespace internal { 22 | 23 | namespace { 24 | const struct sched_param kRealTimePrio = {8}; 25 | } // namespace 26 | 27 | const ThreadPriorityToNiceValuePair kThreadPriorityToNiceValueMap[4] = { 28 | {ThreadPriority::BACKGROUND, 10}, 29 | {ThreadPriority::NORMAL, 0}, 30 | {ThreadPriority::DISPLAY, -8}, 31 | {ThreadPriority::REALTIME_AUDIO, -10}, 32 | }; 33 | 34 | bool SetCurrentThreadPriorityForPlatform(ThreadPriority priority) { 35 | return priority == ThreadPriority::REALTIME_AUDIO && 36 | pthread_setschedparam(pthread_self(), SCHED_RR, &kRealTimePrio) == 0; 37 | } 38 | 39 | bool GetCurrentThreadPriorityForPlatform(ThreadPriority *priority) { 40 | int maybe_sched_rr = 0; 41 | struct sched_param maybe_realtime_prio = {0}; 42 | if (pthread_getschedparam(pthread_self(), &maybe_sched_rr, 43 | &maybe_realtime_prio) == 0 && 44 | maybe_sched_rr == SCHED_RR && 45 | maybe_realtime_prio.sched_priority == kRealTimePrio.sched_priority) { 46 | *priority = ThreadPriority::REALTIME_AUDIO; 47 | return true; 48 | } 49 | return false; 50 | } 51 | 52 | } // namespace internal 53 | 54 | // static 55 | void PlatformThread::SetName(const std::string &name) { 56 | // On linux we can get the thread names to show up in the debugger by setting 57 | // the process name for the LWP. We don't want to do this for the main 58 | // thread because that would rename the process, causing tools like killall 59 | // to stop working. 60 | if (PlatformThread::CurrentId() == getpid()) 61 | return; 62 | 63 | // http://0pointer.de/blog/projects/name-your-threads.html 64 | // Set the name for the LWP (which gets truncated to 15 characters). 65 | // Note that glibc also has a 'pthread_setname_np' api, but it may not be 66 | // available everywhere and it's only benefit over using prctl directly is 67 | // that it can set the name of threads other than the current thread. 68 | int err = prctl(PR_SET_NAME, name.c_str()); 69 | // We expect EPERM failures in sandboxed processes, just ignore those. 70 | if (err < 0 && errno != EPERM) 71 | DPLOG(ERROR) << "prctl(PR_SET_NAME)"; 72 | } 73 | 74 | size_t GetDefaultThreadStackSize(const pthread_attr_t &attributes) { 75 | // ThreadSanitizer bloats the stack heavily. Evidence has been that the 76 | // default stack size isn't enough for some browser tests. 77 | return 2 * (1 << 23); // 2 times 8192K (the default stack size on Linux). 78 | } 79 | 80 | } // namespace base 81 | -------------------------------------------------------------------------------- /base/threading/platform_thread_posix.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #include "base/threading/platform_thread.h" 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "base/logging.h" 17 | #include "base/threading/platform_thread_internal_posix.h" 18 | 19 | #include 20 | 21 | namespace base { 22 | 23 | size_t GetDefaultThreadStackSize(const pthread_attr_t &attributes); 24 | 25 | namespace { 26 | 27 | struct ThreadParams { 28 | ThreadParams() 29 | : delegate(NULL), joinable(false), priority(ThreadPriority::NORMAL) {} 30 | 31 | PlatformThread::Delegate *delegate; 32 | bool joinable; 33 | ThreadPriority priority; 34 | }; 35 | 36 | void *ThreadFunc(void *params) { 37 | PlatformThread::Delegate *delegate = nullptr; 38 | 39 | { 40 | std::unique_ptr thread_params(static_cast(params)); 41 | 42 | delegate = thread_params->delegate; 43 | 44 | // Threads on linux/android may inherit their priority from the thread 45 | // where they were created. This explicitly sets the priority of all new 46 | // threads. 47 | PlatformThread::SetCurrentThreadPriority(thread_params->priority); 48 | } 49 | 50 | delegate->ThreadMain(); 51 | return NULL; 52 | } 53 | 54 | bool CreateThread(size_t stack_size, 55 | bool joinable, 56 | PlatformThread::Delegate *delegate, 57 | PlatformThreadHandle *thread_handle, 58 | ThreadPriority priority) { 59 | DCHECK(thread_handle); 60 | 61 | pthread_attr_t attributes; 62 | pthread_attr_init(&attributes); 63 | 64 | // Pthreads are joinable by default, so only specify the detached 65 | // attribute if the thread should be non-joinable. 66 | if (!joinable) 67 | pthread_attr_setdetachstate(&attributes, PTHREAD_CREATE_DETACHED); 68 | 69 | // Get a better default if available. 70 | if (stack_size == 0) 71 | stack_size = base::GetDefaultThreadStackSize(attributes); 72 | 73 | if (stack_size > 0) 74 | pthread_attr_setstacksize(&attributes, stack_size); 75 | 76 | std::unique_ptr params(new ThreadParams); 77 | params->delegate = delegate; 78 | params->joinable = joinable; 79 | params->priority = priority; 80 | 81 | pthread_t handle; 82 | int err = pthread_create(&handle, &attributes, ThreadFunc, params.get()); 83 | bool success = !err; 84 | if (success) { 85 | // ThreadParams should be deleted on the created thread after used. 86 | ignore_result(params.release()); 87 | } else { 88 | // Value of |handle| is undefined if pthread_create fails. 89 | handle = 0; 90 | errno = err; 91 | PLOG(ERROR) << "pthread_create"; 92 | } 93 | *thread_handle = PlatformThreadHandle(handle); 94 | 95 | pthread_attr_destroy(&attributes); 96 | 97 | return success; 98 | } 99 | 100 | } // namespace 101 | 102 | // static 103 | PlatformThreadId PlatformThread::CurrentId() { 104 | // Pthreads doesn't have the concept of a thread ID, so we have to reach down 105 | // into the kernel. 106 | return syscall(__NR_gettid); 107 | } 108 | 109 | // static 110 | PlatformThreadRef PlatformThread::CurrentRef() { 111 | return PlatformThreadRef(pthread_self()); 112 | } 113 | 114 | // static 115 | PlatformThreadHandle PlatformThread::CurrentHandle() { 116 | return PlatformThreadHandle(pthread_self()); 117 | } 118 | 119 | // static 120 | void PlatformThread::YieldCurrentThread() { 121 | sched_yield(); 122 | } 123 | 124 | // static 125 | void PlatformThread::Sleep(TimeDelta duration) { 126 | struct timespec sleep_time, remaining; 127 | 128 | // Break the duration into seconds and nanoseconds. 129 | // NOTE: TimeDelta's microseconds are int64s while timespec's 130 | // nanoseconds are longs, so this unpacking must prevent overflow. 131 | sleep_time.tv_sec = duration.InSeconds(); 132 | duration -= TimeDelta::FromSeconds(sleep_time.tv_sec); 133 | sleep_time.tv_nsec = duration.InMicroseconds() * 1000; // nanoseconds 134 | 135 | while (nanosleep(&sleep_time, &remaining) == -1 && errno == EINTR) 136 | sleep_time = remaining; 137 | } 138 | 139 | // static 140 | const char *PlatformThread::GetName() { 141 | return nullptr; 142 | } 143 | 144 | // static 145 | bool PlatformThread::CreateWithPriority(size_t stack_size, Delegate *delegate, 146 | PlatformThreadHandle *thread_handle, 147 | ThreadPriority priority) { 148 | return CreateThread(stack_size, true, // joinable thread 149 | delegate, thread_handle, priority); 150 | } 151 | 152 | // static 153 | bool PlatformThread::CreateNonJoinable(size_t stack_size, Delegate *delegate) { 154 | PlatformThreadHandle unused; 155 | 156 | bool result = CreateThread(stack_size, false /* non-joinable thread */, 157 | delegate, &unused, ThreadPriority::NORMAL); 158 | return result; 159 | } 160 | 161 | // static 162 | void PlatformThread::Join(PlatformThreadHandle thread_handle) { 163 | // Joining another thread may block the current thread for a long time, since 164 | // the thread referred to by |thread_handle| may still be running long-lived / 165 | // blocking tasks. 166 | CHECK_EQ(0, pthread_join(thread_handle.platform_handle(), NULL)); 167 | } 168 | 169 | // static 170 | void PlatformThread::SetCurrentThreadPriority(ThreadPriority priority) { 171 | if (internal::SetCurrentThreadPriorityForPlatform(priority)) 172 | return; 173 | 174 | // setpriority(2) should change the whole thread group's (i.e. process) 175 | // priority. However, as stated in the bugs section of 176 | // http://man7.org/linux/man-pages/man2/getpriority.2.html: "under the current 177 | // Linux/NPTL implementation of POSIX threads, the nice value is a per-thread 178 | // attribute". Also, 0 is prefered to the current thread id since it is 179 | // equivalent but makes sandboxing easier (https://crbug.com/399473). 180 | const int nice_setting = internal::ThreadPriorityToNiceValue(priority); 181 | if (setpriority(PRIO_PROCESS, 0, nice_setting)) { 182 | DVPLOG(1) << "Failed to set nice value of thread (" 183 | << PlatformThread::CurrentId() << ") to " << nice_setting; 184 | } 185 | } 186 | 187 | // static 188 | ThreadPriority PlatformThread::GetCurrentThreadPriority() { 189 | // Mirrors SetCurrentThreadPriority()'s implementation. 190 | ThreadPriority platform_specific_priority; 191 | if (internal::GetCurrentThreadPriorityForPlatform( 192 | &platform_specific_priority)) { 193 | return platform_specific_priority; 194 | } 195 | 196 | // Need to clear errno before calling getpriority(): 197 | // http://man7.org/linux/man-pages/man2/getpriority.2.html 198 | errno = 0; 199 | int nice_value = getpriority(PRIO_PROCESS, 0); 200 | if (errno != 0) { 201 | DVPLOG(1) << "Failed to get nice value of thread (" 202 | << PlatformThread::CurrentId() << ")"; 203 | return ThreadPriority::NORMAL; 204 | } 205 | 206 | return internal::NiceValueToThreadPriority(nice_value); 207 | } 208 | } // namespace base 209 | -------------------------------------------------------------------------------- /base/threading/simple_thread.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2010 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #include "base/threading/simple_thread.h" 6 | 7 | #include "base/logging.h" 8 | #include "base/threading/platform_thread.h" 9 | 10 | namespace base { 11 | 12 | SimpleThread::SimpleThread(const std::string &name) 13 | : name_(name), 14 | thread_(), event_(true, false), tid_(0), joined_(false) { 15 | } 16 | 17 | SimpleThread::SimpleThread(const std::string &name, 18 | const Options &options) 19 | : name_(name), options_(options), 20 | thread_(), event_(true, false), tid_(0), joined_(false) { 21 | } 22 | 23 | SimpleThread::~SimpleThread() { 24 | DCHECK(HasBeenStarted()) << "SimpleThread was never started."; 25 | DCHECK(HasBeenJoined()) << "SimpleThread destroyed without being Join()ed."; 26 | } 27 | 28 | void SimpleThread::Start() { 29 | DCHECK(!HasBeenStarted()) << "Tried to Start a thread multiple times."; 30 | bool success; 31 | if (options_.priority() == ThreadPriority::NORMAL) { 32 | success = PlatformThread::Create(options_.stack_size(), this, &thread_); 33 | } else { 34 | success = PlatformThread::CreateWithPriority(options_.stack_size(), this, 35 | &thread_, options_.priority()); 36 | } 37 | DCHECK(success); 38 | event_.Wait(); // Wait for the thread to complete initialization. 39 | } 40 | 41 | void SimpleThread::Join() { 42 | DCHECK(HasBeenStarted()) << "Tried to Join a never-started thread."; 43 | DCHECK(!HasBeenJoined()) << "Tried to Join a thread multiple times."; 44 | PlatformThread::Join(thread_); 45 | joined_ = true; 46 | } 47 | 48 | bool SimpleThread::HasBeenStarted() { 49 | return event_.IsSignaled(); 50 | } 51 | 52 | void SimpleThread::ThreadMain() { 53 | tid_ = PlatformThread::CurrentId(); 54 | // Construct our full name of the form "name_prefix_/TID". 55 | PlatformThread::SetName(name_); 56 | 57 | // We've initialized our new thread, signal that we're done to Start(). 58 | event_.Signal(); 59 | 60 | Run(); 61 | } 62 | 63 | DelegateSimpleThread::DelegateSimpleThread(Delegate *delegate, 64 | const std::string &name) 65 | : SimpleThread(name), 66 | delegate_(delegate) { 67 | } 68 | 69 | DelegateSimpleThread::DelegateSimpleThread(Delegate *delegate, 70 | const std::string &name, 71 | const Options &options) 72 | : SimpleThread(name, options), 73 | delegate_(delegate) { 74 | } 75 | 76 | DelegateSimpleThread::~DelegateSimpleThread() { 77 | } 78 | 79 | void DelegateSimpleThread::Run() { 80 | DCHECK(delegate_) << "Tried to call Run without a delegate (called twice?)"; 81 | delegate_->Run(); 82 | delegate_ = NULL; 83 | } 84 | } // namespace base 85 | -------------------------------------------------------------------------------- /base/threading/simple_thread.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | // WARNING: You should probably be using Thread (thread.h) instead. Thread is 6 | // Chrome's message-loop based Thread abstraction, and if you are a 7 | // thread running in the browser, there will likely be assumptions 8 | // that your thread will have an associated message loop. 9 | // 10 | // This is a simple thread interface that backs to a native operating system 11 | // thread. You should use this only when you want a thread that does not have 12 | // an associated MessageLoop. Unittesting is the best example of this. 13 | // 14 | // The simplest interface to use is DelegateSimpleThread, which will create 15 | // a new thread, and execute the Delegate's virtual Run() in this new thread 16 | // until it has completed, exiting the thread. 17 | // 18 | // NOTE: You *MUST* call Join on the thread to clean up the underlying thread 19 | // resources. You are also responsible for destructing the SimpleThread object. 20 | // It is invalid to destroy a SimpleThread while it is running, or without 21 | // Start() having been called (and a thread never created). The Delegate 22 | // object should live as long as a DelegateSimpleThread. 23 | // 24 | // Thread Safety: A SimpleThread is not completely thread safe. It is safe to 25 | // access it from the creating thread or from the newly created thread. This 26 | // implies that the creator thread should be the thread that calls Join. 27 | // 28 | // Example: 29 | // class MyThreadRunner : public DelegateSimpleThread::Delegate { ... }; 30 | // MyThreadRunner runner; 31 | // DelegateSimpleThread thread(&runner, "good_name_here"); 32 | // thread.Start(); 33 | // // Start will return after the Thread has been successfully started and 34 | // // initialized. The newly created thread will invoke runner->Run(), and 35 | // // run until it returns. 36 | // thread.Join(); // Wait until the thread has exited. You *MUST* Join! 37 | // // The SimpleThread object is still valid, however you may not call Join 38 | // // or Start again. 39 | 40 | #ifndef BASE_THREADING_SIMPLE_THREAD_H_ 41 | #define BASE_THREADING_SIMPLE_THREAD_H_ 42 | 43 | #include 44 | 45 | #include 46 | #include 47 | #include 48 | 49 | #include "base/synchronization/lock.h" 50 | #include "base/synchronization/waitable_event.h" 51 | #include "base/threading/platform_thread.h" 52 | 53 | namespace base { 54 | 55 | // This is the base SimpleThread. You can derive from it and implement the 56 | // virtual Run method, or you can use the DelegateSimpleThread interface. 57 | class SimpleThread : public PlatformThread::Delegate { 58 | public: 59 | class Options { 60 | public: 61 | Options() : stack_size_(0), priority_(ThreadPriority::NORMAL) {} 62 | explicit Options(ThreadPriority priority) 63 | : stack_size_(0), priority_(priority) {} 64 | ~Options() {} 65 | 66 | // We use the standard compiler-supplied copy constructor. 67 | 68 | // A custom stack size, or 0 for the system default. 69 | void set_stack_size(size_t size) { stack_size_ = size; } 70 | size_t stack_size() const { return stack_size_; } 71 | 72 | // A custom thread priority. 73 | void set_priority(ThreadPriority priority) { priority_ = priority; } 74 | ThreadPriority priority() const { return priority_; } 75 | private: 76 | size_t stack_size_; 77 | ThreadPriority priority_; 78 | }; 79 | 80 | // Create a SimpleThread. |options| should be used to manage any specific 81 | // configuration involving the thread creation and management. 82 | // Every thread has a name, in the form of |name_prefix|/TID, for example 83 | // "my_thread/321". The thread will not be created until Start() is called. 84 | explicit SimpleThread(const std::string &name); 85 | SimpleThread(const std::string &name, const Options &options); 86 | 87 | ~SimpleThread() override; 88 | 89 | virtual void Start(); 90 | virtual void Join(); 91 | 92 | // Subclasses should override the Run method. 93 | virtual void Run() = 0; 94 | 95 | // Return the completed name including TID, only valid after Start(). 96 | std::string name() { return name_; } 97 | 98 | // Return the thread id, only valid after Start(). 99 | PlatformThreadId tid() { return tid_; } 100 | 101 | // Return True if Start() has ever been called. 102 | bool HasBeenStarted(); 103 | 104 | // Return True if Join() has evern been called. 105 | bool HasBeenJoined() { return joined_; } 106 | 107 | // Overridden from PlatformThread::Delegate: 108 | void ThreadMain() override; 109 | 110 | private: 111 | std::string name_; 112 | const Options options_; 113 | PlatformThreadHandle thread_; // PlatformThread handle, invalid after Join! 114 | WaitableEvent event_; // Signaled if Start() was ever called. 115 | PlatformThreadId tid_; // The backing thread's id. 116 | bool joined_; // True if Join has been called. 117 | }; 118 | 119 | class DelegateSimpleThread : public SimpleThread { 120 | public: 121 | class Delegate { 122 | public: 123 | Delegate() {} 124 | virtual ~Delegate() {} 125 | virtual void Run() = 0; 126 | }; 127 | 128 | DelegateSimpleThread(Delegate *delegate, 129 | const std::string &name); 130 | DelegateSimpleThread(Delegate *delegate, 131 | const std::string &name, 132 | const Options &options); 133 | 134 | ~DelegateSimpleThread() override; 135 | void Run() override; 136 | 137 | private: 138 | Delegate *delegate_; 139 | }; 140 | 141 | } // namespace base 142 | 143 | #endif // BASE_THREADING_SIMPLE_THREAD_H_ 144 | -------------------------------------------------------------------------------- /base/threading/thread.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "base/threading/thread.h" 5 | 6 | namespace base { 7 | 8 | void ThreadQuitHelper() { 9 | MessageLoop::current()->QuitNow(); 10 | } 11 | 12 | Thread::Thread(const std::string &name) 13 | : name_(name), 14 | stopping_(false), 15 | running_(false), 16 | thread_(0), 17 | id_(kInvalidThreadId), 18 | id_event_(true, false), 19 | message_loop_(nullptr), 20 | start_event_(true, false) { 21 | } 22 | 23 | Thread::~Thread() { 24 | Stop(); 25 | } 26 | 27 | bool Thread::IsCurrent() const { 28 | return PlatformThread::CurrentId() == GetThreadId(); 29 | } 30 | 31 | bool Thread::Start() { 32 | SimpleThread::Options options; 33 | return StartWithOptions(options); 34 | } 35 | 36 | bool Thread::StartWithOptions(const SimpleThread::Options &options) { 37 | id_event_.Reset(); 38 | id_ = kInvalidThreadId; 39 | 40 | std::unique_ptr message_loop(new MessageLoop()); 41 | message_loop_ = message_loop.get(); 42 | start_event_.Reset(); 43 | 44 | { 45 | AutoLock lock(thread_lock_); 46 | if (!PlatformThread::CreateWithPriority(options.stack_size(), this, &thread_, 47 | options.priority())) { 48 | DLOG(ERROR) << "failed to create thread"; 49 | message_loop_ = nullptr; 50 | return false; 51 | } 52 | } 53 | // The ownership of message_loop is managemed by the newly created thread 54 | // within the ThreadMain. 55 | ignore_result(message_loop.release()); 56 | 57 | DCHECK(message_loop_); 58 | return true; 59 | } 60 | 61 | void Thread::Stop() { 62 | AutoLock l(thread_lock_); 63 | if (thread_.is_null()) 64 | return; 65 | 66 | StopSoon(); 67 | 68 | PlatformThread::Join(thread_); 69 | thread_ = base::PlatformThreadHandle(); 70 | DCHECK(!message_loop_); 71 | stopping_ = false; 72 | } 73 | 74 | void Thread::StopSoon() { 75 | // We should only be called on the same thread that started us. 76 | DCHECK_NE(GetThreadId(), PlatformThread::CurrentId()); 77 | 78 | if (stopping_ || !message_loop_) 79 | return; 80 | 81 | stopping_ = true; 82 | PostTask(std::bind(&ThreadQuitHelper)); 83 | } 84 | 85 | PlatformThreadId Thread::GetThreadId() const { 86 | id_event_.Wait(); 87 | return id_; 88 | } 89 | 90 | bool Thread::IsRunning() const { 91 | if (message_loop_ && !stopping_) 92 | return true; 93 | AutoLock lock(running_lock_); 94 | return running_; 95 | } 96 | 97 | void Thread::ThreadMain() { 98 | id_ = PlatformThread::CurrentId(); 99 | DCHECK_NE(kInvalidThreadId, id_); 100 | id_event_.Signal(); 101 | 102 | PlatformThread::SetName(name_); 103 | 104 | std::unique_ptr message_loop(message_loop_); 105 | message_loop->BindToCurrentThread(); 106 | 107 | { 108 | AutoLock lock(running_lock_); 109 | running_ = true; 110 | } 111 | 112 | start_event_.Signal(); 113 | 114 | message_loop->Run(); 115 | 116 | { 117 | AutoLock lock(running_lock_); 118 | running_ = false; 119 | } 120 | message_loop_ = nullptr; 121 | } 122 | 123 | void Thread::PostTask(std::unique_ptr task) { 124 | PostDelayedTask(std::move(task), TimeDelta()); 125 | } 126 | 127 | void Thread::PostDelayedTask(std::unique_ptr task, 128 | const base::TimeDelta &delay) { 129 | message_loop_->PostDelayedTask(std::move(task), delay); 130 | } 131 | } // namespace base 132 | -------------------------------------------------------------------------------- /base/threading/thread.h: -------------------------------------------------------------------------------- 1 | #ifndef BASE_THREADING_THREAD_H_ 2 | #define BASE_THREADING_THREAD_H_ 3 | 4 | #include 5 | #include "base/synchronization/lock.h" 6 | #include "base/threading/simple_thread.h" 7 | #include "base/callback.h" 8 | #include "base/message_loop/message_loop.h" 9 | 10 | namespace base { 11 | 12 | class Thread : public PlatformThread::Delegate { 13 | public: 14 | explicit Thread(const std::string &name); 15 | 16 | ~Thread() override; 17 | 18 | bool Start(); 19 | 20 | bool StartWithOptions(const SimpleThread::Options &options); 21 | 22 | void Stop(); 23 | 24 | bool IsCurrent() const; 25 | 26 | void PostTask(std::unique_ptr task); 27 | 28 | void PostDelayedTask(std::unique_ptr task, 29 | const base::TimeDelta &delay); 30 | 31 | // std::enable_if is used here to make sure that calls to PostTask() with 32 | // std::unique_ptr would not end up being 33 | // caught by this template. 34 | template>::value>::type * = nullptr> 38 | void PostTask(Closure &&closure) { 39 | PostTask(NewClosure(std::forward(closure))); 40 | } 41 | 42 | // See documentation above for performance expectations. 43 | template>::value>::type * = nullptr> 47 | void PostDelayedTask(Closure &&closure, const base::TimeDelta &delay) { 48 | PostDelayedTask(NewClosure(std::forward(closure)), delay); 49 | } 50 | 51 | // Returns the name of this thread (for display in debugger too). 52 | const std::string &thread_name() const { return name_; } 53 | 54 | // The native thread handle. 55 | PlatformThreadHandle thread_handle() { return thread_; } 56 | 57 | PlatformThreadId GetThreadId() const; 58 | 59 | bool IsRunning() const; 60 | 61 | private: 62 | void ThreadMain() override; 63 | 64 | void StopSoon(); 65 | 66 | std::string name_; 67 | 68 | bool stopping_; 69 | 70 | mutable base::Lock running_lock_; 71 | 72 | bool running_; 73 | 74 | PlatformThreadHandle thread_; 75 | 76 | PlatformThreadId id_; 77 | 78 | mutable WaitableEvent id_event_; 79 | 80 | mutable base::Lock thread_lock_; 81 | 82 | MessageLoop *message_loop_; 83 | 84 | mutable WaitableEvent start_event_; 85 | 86 | DISALLOW_COPY_AND_ASSIGN(Thread); 87 | }; 88 | } // namespace base 89 | 90 | #endif // BASE_THREADING_THREAD_H_ 91 | -------------------------------------------------------------------------------- /base/time/time.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #include "base/time/time.h" 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "base/logging.h" 14 | #include "base/macros.h" 15 | #include "build/build_config.h" 16 | 17 | namespace base { 18 | 19 | // TimeDelta ------------------------------------------------------------------ 20 | 21 | // static 22 | TimeDelta TimeDelta::Max() { 23 | return TimeDelta(std::numeric_limits::max()); 24 | } 25 | 26 | int TimeDelta::InDays() const { 27 | if (is_max()) { 28 | // Preserve max to prevent overflow. 29 | return std::numeric_limits::max(); 30 | } 31 | return static_cast(delta_ / Time::kMicrosecondsPerDay); 32 | } 33 | 34 | int TimeDelta::InHours() const { 35 | if (is_max()) { 36 | // Preserve max to prevent overflow. 37 | return std::numeric_limits::max(); 38 | } 39 | return static_cast(delta_ / Time::kMicrosecondsPerHour); 40 | } 41 | 42 | int TimeDelta::InMinutes() const { 43 | if (is_max()) { 44 | // Preserve max to prevent overflow. 45 | return std::numeric_limits::max(); 46 | } 47 | return static_cast(delta_ / Time::kMicrosecondsPerMinute); 48 | } 49 | 50 | double TimeDelta::InSecondsF() const { 51 | if (is_max()) { 52 | // Preserve max to prevent overflow. 53 | return std::numeric_limits::infinity(); 54 | } 55 | return static_cast(delta_) / Time::kMicrosecondsPerSecond; 56 | } 57 | 58 | int64_t TimeDelta::InSeconds() const { 59 | if (is_max()) { 60 | // Preserve max to prevent overflow. 61 | return std::numeric_limits::max(); 62 | } 63 | return delta_ / Time::kMicrosecondsPerSecond; 64 | } 65 | 66 | double TimeDelta::InMillisecondsF() const { 67 | if (is_max()) { 68 | // Preserve max to prevent overflow. 69 | return std::numeric_limits::infinity(); 70 | } 71 | return static_cast(delta_) / Time::kMicrosecondsPerMillisecond; 72 | } 73 | 74 | int64_t TimeDelta::InMilliseconds() const { 75 | if (is_max()) { 76 | // Preserve max to prevent overflow. 77 | return std::numeric_limits::max(); 78 | } 79 | return delta_ / Time::kMicrosecondsPerMillisecond; 80 | } 81 | 82 | int64_t TimeDelta::InMillisecondsRoundedUp() const { 83 | if (is_max()) { 84 | // Preserve max to prevent overflow. 85 | return std::numeric_limits::max(); 86 | } 87 | return (delta_ + Time::kMicrosecondsPerMillisecond - 1) / 88 | Time::kMicrosecondsPerMillisecond; 89 | } 90 | 91 | int64_t TimeDelta::InMicroseconds() const { 92 | if (is_max()) { 93 | // Preserve max to prevent overflow. 94 | return std::numeric_limits::max(); 95 | } 96 | return delta_; 97 | } 98 | 99 | namespace time_internal { 100 | 101 | int64_t SaturatedAdd(TimeDelta delta, int64_t value) { 102 | CheckedNumeric rv(delta.delta_); 103 | rv += value; 104 | return FromCheckedNumeric(rv); 105 | } 106 | 107 | int64_t SaturatedSub(TimeDelta delta, int64_t value) { 108 | CheckedNumeric rv(delta.delta_); 109 | rv -= value; 110 | return FromCheckedNumeric(rv); 111 | } 112 | 113 | int64_t FromCheckedNumeric(const CheckedNumeric value) { 114 | if (value.IsValid()) 115 | return value.ValueUnsafe(); 116 | 117 | // We could return max/min but we don't really expose what the maximum delta 118 | // is. Instead, return max/(-max), which is something that clients can reason 119 | // about. 120 | // TODO(rvargas) crbug.com/332611: don't use internal values. 121 | int64_t limit = std::numeric_limits::max(); 122 | if (value.validity() == internal::RANGE_UNDERFLOW) 123 | limit = -limit; 124 | return value.ValueOrDefault(limit); 125 | } 126 | 127 | } // namespace time_internal 128 | 129 | std::ostream &operator<<(std::ostream &os, TimeDelta time_delta) { 130 | return os << time_delta.InSecondsF() << "s"; 131 | } 132 | 133 | // Time ----------------------------------------------------------------------- 134 | 135 | // static 136 | Time Time::Max() { 137 | return Time(std::numeric_limits::max()); 138 | } 139 | 140 | // static 141 | Time Time::FromTimeT(time_t tt) { 142 | if (tt == 0) 143 | return Time(); // Preserve 0 so we can tell it doesn't exist. 144 | if (tt == std::numeric_limits::max()) 145 | return Max(); 146 | return Time(kTimeTToMicrosecondsOffset) + TimeDelta::FromSeconds(tt); 147 | } 148 | 149 | time_t Time::ToTimeT() const { 150 | if (is_null()) 151 | return 0; // Preserve 0 so we can tell it doesn't exist. 152 | if (is_max()) { 153 | // Preserve max without offset to prevent overflow. 154 | return std::numeric_limits::max(); 155 | } 156 | if (std::numeric_limits::max() - kTimeTToMicrosecondsOffset <= us_) { 157 | DLOG(WARNING) << "Overflow when converting base::Time with internal " << 158 | "value " << us_ << " to time_t."; 159 | return std::numeric_limits::max(); 160 | } 161 | return (us_ - kTimeTToMicrosecondsOffset) / kMicrosecondsPerSecond; 162 | } 163 | 164 | // static 165 | Time Time::FromDoubleT(double dt) { 166 | if (dt == 0 || std::isnan(dt)) 167 | return Time(); // Preserve 0 so we can tell it doesn't exist. 168 | return Time(kTimeTToMicrosecondsOffset) + TimeDelta::FromSecondsD(dt); 169 | } 170 | 171 | double Time::ToDoubleT() const { 172 | if (is_null()) 173 | return 0; // Preserve 0 so we can tell it doesn't exist. 174 | if (is_max()) { 175 | // Preserve max without offset to prevent overflow. 176 | return std::numeric_limits::infinity(); 177 | } 178 | return (static_cast(us_ - kTimeTToMicrosecondsOffset) / 179 | static_cast(kMicrosecondsPerSecond)); 180 | } 181 | 182 | // static 183 | Time Time::FromTimeSpec(const timespec &ts) { 184 | return FromDoubleT(ts.tv_sec + 185 | static_cast(ts.tv_nsec) / 186 | base::Time::kNanosecondsPerSecond); 187 | } 188 | 189 | // static 190 | Time Time::FromJsTime(double ms_since_epoch) { 191 | // The epoch is a valid time, so this constructor doesn't interpret 192 | // 0 as the null time. 193 | return Time(kTimeTToMicrosecondsOffset) + 194 | TimeDelta::FromMillisecondsD(ms_since_epoch); 195 | } 196 | 197 | double Time::ToJsTime() const { 198 | if (is_null()) { 199 | // Preserve 0 so the invalid result doesn't depend on the platform. 200 | return 0; 201 | } 202 | if (is_max()) { 203 | // Preserve max without offset to prevent overflow. 204 | return std::numeric_limits::infinity(); 205 | } 206 | return (static_cast(us_ - kTimeTToMicrosecondsOffset) / 207 | kMicrosecondsPerMillisecond); 208 | } 209 | 210 | int64_t Time::ToJavaTime() const { 211 | if (is_null()) { 212 | // Preserve 0 so the invalid result doesn't depend on the platform. 213 | return 0; 214 | } 215 | if (is_max()) { 216 | // Preserve max without offset to prevent overflow. 217 | return std::numeric_limits::max(); 218 | } 219 | return ((us_ - kTimeTToMicrosecondsOffset) / 220 | kMicrosecondsPerMillisecond); 221 | } 222 | 223 | // static 224 | Time Time::UnixEpoch() { 225 | Time time; 226 | time.us_ = kTimeTToMicrosecondsOffset; 227 | return time; 228 | } 229 | 230 | Time Time::LocalMidnight() const { 231 | Exploded exploded; 232 | LocalExplode(&exploded); 233 | exploded.hour = 0; 234 | exploded.minute = 0; 235 | exploded.second = 0; 236 | exploded.millisecond = 0; 237 | return FromLocalExploded(exploded); 238 | } 239 | 240 | TimeTicks TimeTicks::SnappedToNextTick(TimeTicks tick_phase, 241 | TimeDelta tick_interval) const { 242 | // |interval_offset| is the offset from |this| to the next multiple of 243 | // |tick_interval| after |tick_phase|, possibly negative if in the past. 244 | TimeDelta interval_offset = (tick_phase - *this) % tick_interval; 245 | // If |this| is exactly on the interval (i.e. offset==0), don't adjust. 246 | // Otherwise, if |tick_phase| was in the past, adjust forward to the next 247 | // tick after |this|. 248 | if (!interval_offset.is_zero() && tick_phase < *this) 249 | interval_offset += tick_interval; 250 | return *this + interval_offset; 251 | } 252 | 253 | // Time::Exploded ------------------------------------------------------------- 254 | 255 | inline bool is_in_range(int value, int lo, int hi) { 256 | return lo <= value && value <= hi; 257 | } 258 | 259 | bool Time::Exploded::HasValidValues() const { 260 | return is_in_range(month, 1, 12) && 261 | is_in_range(day_of_week, 0, 6) && 262 | is_in_range(day_of_month, 1, 31) && 263 | is_in_range(hour, 0, 23) && 264 | is_in_range(minute, 0, 59) && 265 | is_in_range(second, 0, 60) && 266 | is_in_range(millisecond, 0, 999); 267 | } 268 | 269 | } // namespace base 270 | -------------------------------------------------------------------------------- /base/time/time_posix.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #include "base/time/time.h" 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | #include 14 | 15 | #include "base/logging.h" 16 | #include "build/build_config.h" 17 | 18 | namespace { 19 | 20 | typedef time_t SysTime; 21 | 22 | SysTime SysTimeFromTimeStruct(struct tm *timestruct, bool is_local) { 23 | if (is_local) 24 | return mktime(timestruct); 25 | else 26 | return timegm(timestruct); 27 | } 28 | 29 | void SysTimeToTimeStruct(SysTime t, struct tm *timestruct, bool is_local) { 30 | if (is_local) 31 | localtime_r(&t, timestruct); 32 | else 33 | gmtime_r(&t, timestruct); 34 | } 35 | 36 | int64_t ConvertTimespecToMicros(const struct timespec &ts) { 37 | base::CheckedNumeric result(ts.tv_sec); 38 | result *= base::Time::kMicrosecondsPerSecond; 39 | result += (ts.tv_nsec / base::Time::kNanosecondsPerMicrosecond); 40 | return result.ValueOrDie(); 41 | } 42 | 43 | // Helper function to get results from clock_gettime() and convert to a 44 | // microsecond timebase. Minimum requirement is MONOTONIC_CLOCK to be supported 45 | // on the system. FreeBSD 6 has CLOCK_MONOTONIC but defines 46 | // _POSIX_MONOTONIC_CLOCK to -1. 47 | 48 | int64_t ClockNow(clockid_t clk_id) { 49 | struct timespec ts; 50 | if (clock_gettime(clk_id, &ts) != 0) { 51 | NOTREACHED() << "clock_gettime(" << clk_id << ") failed."; 52 | return 0; 53 | } 54 | return ConvertTimespecToMicros(ts); 55 | } 56 | 57 | } // namespace 58 | 59 | namespace base { 60 | 61 | struct timespec TimeDelta::ToTimeSpec() const { 62 | int64_t microseconds = InMicroseconds(); 63 | time_t seconds = 0; 64 | if (microseconds >= Time::kMicrosecondsPerSecond) { 65 | seconds = InSeconds(); 66 | microseconds -= seconds * Time::kMicrosecondsPerSecond; 67 | } 68 | struct timespec result = 69 | {seconds, 70 | static_cast(microseconds * Time::kNanosecondsPerMicrosecond)}; 71 | return result; 72 | } 73 | 74 | // The Time routines in this file use standard POSIX routines, or almost- 75 | // standard routines in the case of timegm. We need to use a Mach-specific 76 | // function for TimeTicks::Now() on Mac OS X. 77 | 78 | // Time ----------------------------------------------------------------------- 79 | 80 | // Windows uses a Gregorian epoch of 1601. We need to match this internally 81 | // so that our time representations match across all platforms. See bug 14734. 82 | // irb(main):010:0> Time.at(0).getutc() 83 | // => Thu Jan 01 00:00:00 UTC 1970 84 | // irb(main):011:0> Time.at(-11644473600).getutc() 85 | // => Mon Jan 01 00:00:00 UTC 1601 86 | static const int64_t kWindowsEpochDeltaSeconds = INT64_C(11644473600); 87 | 88 | // static 89 | const int64_t Time::kWindowsEpochDeltaMicroseconds = 90 | kWindowsEpochDeltaSeconds * Time::kMicrosecondsPerSecond; 91 | 92 | // Some functions in time.cc use time_t directly, so we provide an offset 93 | // to convert from time_t (Unix epoch) and internal (Windows epoch). 94 | // static 95 | const int64_t Time::kTimeTToMicrosecondsOffset = kWindowsEpochDeltaMicroseconds; 96 | 97 | // static 98 | Time Time::Now() { 99 | struct timeval tv; 100 | struct timezone tz = {0, 0}; // UTC 101 | if (gettimeofday(&tv, &tz) != 0) { 102 | DCHECK(0) << "Could not determine time of day"; 103 | PLOG(ERROR) << "Call to gettimeofday failed."; 104 | // Return null instead of uninitialized |tv| value, which contains random 105 | // garbage data. This may result in the crash seen in crbug.com/147570. 106 | return Time(); 107 | } 108 | // Combine seconds and microseconds in a 64-bit field containing microseconds 109 | // since the epoch. That's enough for nearly 600 centuries. Adjust from 110 | // Unix (1970) to Windows (1601) epoch. 111 | return Time((tv.tv_sec * kMicrosecondsPerSecond + tv.tv_usec) + 112 | kWindowsEpochDeltaMicroseconds); 113 | } 114 | 115 | // static 116 | Time Time::NowFromSystemTime() { 117 | // Just use Now() because Now() returns the system time. 118 | return Now(); 119 | } 120 | 121 | void Time::Explode(bool is_local, Exploded *exploded) const { 122 | // Time stores times with microsecond resolution, but Exploded only carries 123 | // millisecond resolution, so begin by being lossy. Adjust from Windows 124 | // epoch (1601) to Unix epoch (1970); 125 | int64_t microseconds = us_ - kWindowsEpochDeltaMicroseconds; 126 | // The following values are all rounded towards -infinity. 127 | int64_t milliseconds; // Milliseconds since epoch. 128 | SysTime seconds; // Seconds since epoch. 129 | int millisecond; // Exploded millisecond value (0-999). 130 | if (microseconds >= 0) { 131 | // Rounding towards -infinity <=> rounding towards 0, in this case. 132 | milliseconds = microseconds / kMicrosecondsPerMillisecond; 133 | seconds = milliseconds / kMillisecondsPerSecond; 134 | millisecond = milliseconds % kMillisecondsPerSecond; 135 | } else { 136 | // Round these *down* (towards -infinity). 137 | milliseconds = (microseconds - kMicrosecondsPerMillisecond + 1) / 138 | kMicrosecondsPerMillisecond; 139 | seconds = (milliseconds - kMillisecondsPerSecond + 1) / 140 | kMillisecondsPerSecond; 141 | // Make this nonnegative (and between 0 and 999 inclusive). 142 | millisecond = milliseconds % kMillisecondsPerSecond; 143 | if (millisecond < 0) 144 | millisecond += kMillisecondsPerSecond; 145 | } 146 | 147 | struct tm timestruct; 148 | SysTimeToTimeStruct(seconds, ×truct, is_local); 149 | 150 | exploded->year = timestruct.tm_year + 1900; 151 | exploded->month = timestruct.tm_mon + 1; 152 | exploded->day_of_week = timestruct.tm_wday; 153 | exploded->day_of_month = timestruct.tm_mday; 154 | exploded->hour = timestruct.tm_hour; 155 | exploded->minute = timestruct.tm_min; 156 | exploded->second = timestruct.tm_sec; 157 | exploded->millisecond = millisecond; 158 | } 159 | 160 | // static 161 | Time Time::FromExploded(bool is_local, const Exploded &exploded) { 162 | struct tm timestruct; 163 | timestruct.tm_sec = exploded.second; 164 | timestruct.tm_min = exploded.minute; 165 | timestruct.tm_hour = exploded.hour; 166 | timestruct.tm_mday = exploded.day_of_month; 167 | timestruct.tm_mon = exploded.month - 1; 168 | timestruct.tm_year = exploded.year - 1900; 169 | timestruct.tm_wday = exploded.day_of_week; // mktime/timegm ignore this 170 | timestruct.tm_yday = 0; // mktime/timegm ignore this 171 | timestruct.tm_isdst = -1; // attempt to figure it out 172 | timestruct.tm_gmtoff = 0; // not a POSIX field, so mktime/timegm ignore 173 | timestruct.tm_zone = NULL; // not a POSIX field, so mktime/timegm ignore 174 | 175 | int64_t milliseconds; 176 | SysTime seconds; 177 | 178 | // Certain exploded dates do not really exist due to daylight saving times, 179 | // and this causes mktime() to return implementation-defined values when 180 | // tm_isdst is set to -1. On Android, the function will return -1, while the 181 | // C libraries of other platforms typically return a liberally-chosen value. 182 | // Handling this requires the special code below. 183 | 184 | // SysTimeFromTimeStruct() modifies the input structure, save current value. 185 | struct tm timestruct0 = timestruct; 186 | 187 | seconds = SysTimeFromTimeStruct(×truct, is_local); 188 | if (seconds == -1) { 189 | // Get the time values with tm_isdst == 0 and 1, then select the closest one 190 | // to UTC 00:00:00 that isn't -1. 191 | timestruct = timestruct0; 192 | timestruct.tm_isdst = 0; 193 | int64_t seconds_isdst0 = SysTimeFromTimeStruct(×truct, is_local); 194 | 195 | timestruct = timestruct0; 196 | timestruct.tm_isdst = 1; 197 | int64_t seconds_isdst1 = SysTimeFromTimeStruct(×truct, is_local); 198 | 199 | // seconds_isdst0 or seconds_isdst1 can be -1 for some timezones. 200 | // E.g. "CLST" (Chile Summer Time) returns -1 for 'tm_isdt == 1'. 201 | if (seconds_isdst0 < 0) 202 | seconds = seconds_isdst1; 203 | else if (seconds_isdst1 < 0) 204 | seconds = seconds_isdst0; 205 | else 206 | seconds = std::min(seconds_isdst0, seconds_isdst1); 207 | } 208 | 209 | // Handle overflow. Clamping the range to what mktime and timegm might 210 | // return is the best that can be done here. It's not ideal, but it's better 211 | // than failing here or ignoring the overflow case and treating each time 212 | // overflow as one second prior to the epoch. 213 | if (seconds == -1 && 214 | (exploded.year < 1969 || exploded.year > 1970)) { 215 | // If exploded.year is 1969 or 1970, take -1 as correct, with the 216 | // time indicating 1 second prior to the epoch. (1970 is allowed to handle 217 | // time zone and DST offsets.) Otherwise, return the most future or past 218 | // time representable. Assumes the time_t epoch is 1970-01-01 00:00:00 UTC. 219 | // 220 | // The minimum and maximum representible times that mktime and timegm could 221 | // return are used here instead of values outside that range to allow for 222 | // proper round-tripping between exploded and counter-type time 223 | // representations in the presence of possible truncation to time_t by 224 | // division and use with other functions that accept time_t. 225 | // 226 | // When representing the most distant time in the future, add in an extra 227 | // 999ms to avoid the time being less than any other possible value that 228 | // this function can return. 229 | 230 | // On Android, SysTime is int64_t, special care must be taken to avoid 231 | // overflows. 232 | const int64_t min_seconds = (sizeof(SysTime) < sizeof(int64_t)) 233 | ? std::numeric_limits::min() 234 | : std::numeric_limits::min(); 235 | const int64_t max_seconds = (sizeof(SysTime) < sizeof(int64_t)) 236 | ? std::numeric_limits::max() 237 | : std::numeric_limits::max(); 238 | if (exploded.year < 1969) { 239 | milliseconds = min_seconds * kMillisecondsPerSecond; 240 | } else { 241 | milliseconds = max_seconds * kMillisecondsPerSecond; 242 | milliseconds += (kMillisecondsPerSecond - 1); 243 | } 244 | } else { 245 | milliseconds = seconds * kMillisecondsPerSecond + exploded.millisecond; 246 | } 247 | 248 | // Adjust from Unix (1970) to Windows (1601) epoch. 249 | return Time((milliseconds * kMicrosecondsPerMillisecond) + 250 | kWindowsEpochDeltaMicroseconds); 251 | } 252 | 253 | // TimeTicks ------------------------------------------------------------------ 254 | // static 255 | TimeTicks TimeTicks::Now() { 256 | return TimeTicks(ClockNow(CLOCK_MONOTONIC)); 257 | } 258 | 259 | // static 260 | bool TimeTicks::IsHighResolution() { 261 | return true; 262 | } 263 | 264 | // static 265 | Time Time::FromTimeVal(struct timeval t) { 266 | DCHECK_LT(t.tv_usec, static_cast(Time::kMicrosecondsPerSecond)); 267 | DCHECK_GE(t.tv_usec, 0); 268 | if (t.tv_usec == 0 && t.tv_sec == 0) 269 | return Time(); 270 | if (t.tv_usec == static_cast(Time::kMicrosecondsPerSecond) - 1 && 271 | t.tv_sec == std::numeric_limits::max()) 272 | return Max(); 273 | return Time((static_cast(t.tv_sec) * Time::kMicrosecondsPerSecond) + 274 | t.tv_usec + kTimeTToMicrosecondsOffset); 275 | } 276 | 277 | struct timeval Time::ToTimeVal() const { 278 | struct timeval result; 279 | if (is_null()) { 280 | result.tv_sec = 0; 281 | result.tv_usec = 0; 282 | return result; 283 | } 284 | if (is_max()) { 285 | result.tv_sec = std::numeric_limits::max(); 286 | result.tv_usec = static_cast(Time::kMicrosecondsPerSecond) - 1; 287 | return result; 288 | } 289 | int64_t us = us_ - kTimeTToMicrosecondsOffset; 290 | result.tv_sec = us / Time::kMicrosecondsPerSecond; 291 | result.tv_usec = us % Time::kMicrosecondsPerSecond; 292 | return result; 293 | } 294 | 295 | } // namespace base 296 | -------------------------------------------------------------------------------- /base/timer/timer.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include "base/timer/timer.h" 3 | #include "base/message_loop/message_loop.h" 4 | #include 5 | 6 | namespace base { 7 | 8 | Timer::Timer(bool is_repeating) 9 | : is_repeating_(is_repeating), 10 | event_(nullptr) { 11 | } 12 | 13 | Timer::~Timer() { 14 | Stop(); 15 | if (event_) { 16 | event_free(event_); 17 | event_ = nullptr; 18 | } 19 | } 20 | 21 | void Timer::Start(std::unique_ptr task, 22 | const TimeDelta &delay) { 23 | Stop(); 24 | 25 | struct event_base *ev = MessageLoop::current()->base(); 26 | if (!ev) return; 27 | 28 | int event_mask = is_repeating_ ? EV_PERSIST : 0; 29 | 30 | if (!event_) { 31 | event_ = event_new(ev, -1, EV_TIMEOUT | event_mask, &Timer::RunTimer, this); 32 | } else { 33 | event_assign(event_, ev, -1, EV_TIMEOUT | event_mask, &Timer::RunTimer, this); 34 | } 35 | timeval tv = {static_cast<__time_t>(delay.InSeconds()), 36 | static_cast<__suseconds_t>(delay.InMicroseconds() % Time::kMicrosecondsPerSecond)}; 37 | event_add(event_, &tv); 38 | task_ = std::move(task); 39 | } 40 | 41 | void Timer::Stop() { 42 | if (event_) { 43 | event_del(event_); 44 | } 45 | task_.reset(); 46 | } 47 | 48 | void Timer::RunTimer(evutil_socket_t listener, short event, void *arg) { 49 | auto timer = reinterpret_cast(arg); 50 | timer->task_->Run(); 51 | } 52 | } // namespace base 53 | -------------------------------------------------------------------------------- /base/timer/timer.h: -------------------------------------------------------------------------------- 1 | #ifndef BASE_TIMER_TIMER_H_ 2 | #define BASE_TIMER_TIMER_H_ 3 | 4 | #include 5 | #include "base/macros.h" 6 | #include "base/time/time.h" 7 | #include "base/callback.h" 8 | 9 | extern "C" { 10 | #include 11 | } 12 | 13 | struct event; 14 | 15 | namespace base { 16 | 17 | class Timer { 18 | public: 19 | explicit Timer(bool is_repeating); 20 | 21 | virtual ~Timer(); 22 | 23 | void Start(std::unique_ptr task, 24 | const TimeDelta &delay); 25 | 26 | template>::value>::type * = nullptr> 30 | void Start(Closure &&closure, const base::TimeDelta &delay) { 31 | Start(NewClosure(std::forward(closure)), delay); 32 | } 33 | 34 | void Stop(); 35 | 36 | private: 37 | 38 | static void RunTimer(evutil_socket_t listener, short event, void *arg); 39 | 40 | bool is_repeating_; 41 | 42 | struct event *event_; 43 | 44 | std::unique_ptr task_; 45 | DISALLOW_COPY_AND_ASSIGN(Timer); 46 | }; 47 | } // namespace base 48 | 49 | #endif // BASE_TIMER_TIMER_H_ 50 | -------------------------------------------------------------------------------- /build/build_config.h: -------------------------------------------------------------------------------- 1 | // Copyright 2008 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #ifndef MINI_CHROMIUM_BUILD_BUILD_CONFIG_H_ 6 | #define MINI_CHROMIUM_BUILD_BUILD_CONFIG_H_ 7 | 8 | #if defined(__APPLE__) 9 | #define OS_MACOSX 1 10 | #elif defined(__ANDROID__) 11 | #define OS_ANDROID 1 12 | #elif defined(__linux__) 13 | #define OS_LINUX 1 14 | #elif defined(_WIN32) 15 | #define OS_WIN 1 16 | #elif defined(__Fuchsia__) 17 | #define OS_FUCHSIA 1 18 | #else 19 | #error Please add support for your platform in build/build_config.h 20 | #endif 21 | 22 | #if defined(OS_MACOSX) 23 | #include 24 | #if defined(TARGET_OS_IOS) 25 | #if TARGET_OS_IOS 26 | #define OS_IOS 1 27 | #endif 28 | #elif defined(TARGET_OS_IPHONE) 29 | #if TARGET_OS_IPHONE 30 | #define OS_IOS 1 31 | #endif 32 | #endif 33 | #endif 34 | 35 | #if defined(OS_MACOSX) || defined(OS_LINUX) || defined(OS_ANDROID) || \ 36 | defined(OS_FUCHSIA) 37 | #define OS_POSIX 1 38 | #endif 39 | 40 | // Compiler detection. 41 | #if defined(__GNUC__) 42 | #define COMPILER_GCC 1 43 | #elif defined(_MSC_VER) 44 | #define COMPILER_MSVC 1 45 | #else 46 | #error Please add support for your compiler in build/build_config.h 47 | #endif 48 | 49 | #if defined(_M_X64) || defined(__x86_64__) 50 | #define ARCH_CPU_X86_FAMILY 1 51 | #define ARCH_CPU_X86_64 1 52 | #define ARCH_CPU_64_BITS 1 53 | #define ARCH_CPU_LITTLE_ENDIAN 1 54 | #elif defined(_M_IX86) || defined(__i386__) 55 | #define ARCH_CPU_X86_FAMILY 1 56 | #define ARCH_CPU_X86 1 57 | #define ARCH_CPU_32_BITS 1 58 | #define ARCH_CPU_LITTLE_ENDIAN 1 59 | #elif defined(__ARMEL__) 60 | #define ARCH_CPU_ARM_FAMILY 1 61 | #define ARCH_CPU_ARMEL 1 62 | #define ARCH_CPU_32_BITS 1 63 | #elif defined(_M_ARM64) || defined(__aarch64__) 64 | #define ARCH_CPU_ARM_FAMILY 1 65 | #define ARCH_CPU_ARM64 1 66 | #define ARCH_CPU_64_BITS 1 67 | #if defined(_M_ARM64) 68 | #define ARCH_CPU_LITTLE_ENDIAN 1 69 | #endif 70 | #elif defined(__MIPSEL__) 71 | #define ARCH_CPU_MIPS_FAMILY 1 72 | #if !defined(__LP64__) 73 | #define ARCH_CPU_MIPSEL 1 74 | #define ARCH_CPU_32_BITS 1 75 | #else 76 | #define ARCH_CPU_MIPS64EL 1 77 | #define ARCH_CPU_64_BITS 1 78 | #endif 79 | #else 80 | #error Please add support for your architecture in build/build_config.h 81 | #endif 82 | 83 | #if !defined(ARCH_CPU_LITTLE_ENDIAN) && !defined(ARCH_CPU_BIG_ENDIAN) 84 | #if defined(__LITTLE_ENDIAN__) || \ 85 | (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) 86 | #define ARCH_CPU_LITTLE_ENDIAN 1 87 | #elif defined(__BIG_ENDIAN__) || \ 88 | (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) 89 | #define ARCH_CPU_BIG_ENDIAN 1 90 | #else 91 | #error Please add support for your architecture in build/build_config.h 92 | #endif 93 | #endif 94 | 95 | #if defined(OS_POSIX) && defined(COMPILER_GCC) && \ 96 | defined(__WCHAR_MAX__) && \ 97 | (__WCHAR_MAX__ == 0x7fffffff || __WCHAR_MAX__ == 0xffffffff) 98 | #define WCHAR_T_IS_UTF32 99 | #elif defined(OS_WIN) 100 | #define WCHAR_T_IS_UTF16 101 | #else 102 | #error Please add support for your compiler in build/build_config.h 103 | #endif 104 | 105 | #endif // MINI_CHROMIUM_BUILD_BUILD_CONFIG_H_ 106 | -------------------------------------------------------------------------------- /main.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include "media/packet_queue.h" 3 | #include "main_app.h" 4 | #include 5 | #include 6 | 7 | int main(int argc, char *argv[]) { 8 | signal(SIGPIPE, SIG_IGN); 9 | RK_MPI_SYS_Init(); 10 | media::PacketQueue::Init(); 11 | app::MainApp app(argc, argv); 12 | app.initMainView(); 13 | app::MainApp::exec(); 14 | 15 | c_RkRgaDeInit(); 16 | return 0; 17 | } 18 | -------------------------------------------------------------------------------- /main_app.cc: -------------------------------------------------------------------------------- 1 | #include "main_app.h" 2 | 3 | namespace app { 4 | 5 | MainApp::MainApp(int &argc, char **argv) 6 | : QApplication(argc, argv), 7 | main_window_(nullptr) { 8 | } 9 | 10 | MainApp::~MainApp() = default; 11 | 12 | bool MainApp::initMainView() { 13 | if (!main_window_) { 14 | main_window_.reset(new ui::MainWindow()); 15 | main_window_->show(); 16 | } 17 | return true; 18 | } 19 | } -------------------------------------------------------------------------------- /main_app.h: -------------------------------------------------------------------------------- 1 | #ifndef APP_MAIN_APP_H 2 | #define APP_MAIN_APP_H 3 | 4 | #include 5 | #include 6 | #include "base/macros.h" 7 | #include "ui/main_window.h" 8 | #include 9 | 10 | namespace app { 11 | 12 | class MainApp : public QApplication { 13 | Q_OBJECT 14 | 15 | public: 16 | explicit MainApp(int &argc, char **argv); 17 | ~MainApp() override; 18 | 19 | bool initMainView(); 20 | 21 | private: 22 | 23 | QScopedPointer main_window_; 24 | 25 | DISALLOW_COPY_AND_ASSIGN(MainApp); 26 | }; 27 | } 28 | #endif // APP_MAIN_APP_H 29 | -------------------------------------------------------------------------------- /media/audio_decoder.cc: -------------------------------------------------------------------------------- 1 | #include "media/audio_decoder.h" 2 | #include "base/logging.h" 3 | 4 | namespace media { 5 | 6 | FFmpegAudioDecoder::FFmpegAudioDecoder() = default; 7 | 8 | FFmpegAudioDecoder::~FFmpegAudioDecoder() { 9 | UnInit(); 10 | } 11 | 12 | bool FFmpegAudioDecoder::Init(AVStream *stream) { 13 | UnInit(); 14 | int ret; 15 | AVCodec *codec = avcodec_find_decoder(stream->codecpar->codec_id); 16 | if (!codec) { 17 | LOG(ERROR) << "avcodec_find_decoder failed: " << stream->codecpar->codec_id; 18 | return false; 19 | } 20 | codec_context_.reset(avcodec_alloc_context3(codec)); 21 | if (!codec_context_) { 22 | LOG(ERROR) << "avcodec_alloc_context3 failed"; 23 | return false; 24 | } 25 | if ((ret = avcodec_parameters_to_context(codec_context_.get(), stream->codecpar)) < 0) { 26 | LOG(ERROR) << "avcodec_parameters_to_context failed:" << ret << ",err:" << AVErrorToString(ret); 27 | return false; 28 | } 29 | if ((ret = avcodec_open2(codec_context_.get(), codec, nullptr)) < 0) { 30 | LOG(ERROR) << "avcodec_open2 failed:" << ret << ",err:" << AVErrorToString(ret); 31 | return false; 32 | } 33 | return true; 34 | } 35 | 36 | void FFmpegAudioDecoder::UnInit() { 37 | codec_context_.reset(); 38 | } 39 | 40 | int FFmpegAudioDecoder::SendInput(const AVPacket *pkt) const { 41 | return avcodec_send_packet(codec_context_.get(), pkt); 42 | } 43 | 44 | int FFmpegAudioDecoder::FetchOutput(AVFrame *frame) const { 45 | return avcodec_receive_frame(codec_context_.get(), frame); 46 | } 47 | 48 | void FFmpegAudioDecoder::Flush() const { 49 | if (codec_context_) { 50 | avcodec_flush_buffers(codec_context_.get()); 51 | } 52 | } 53 | 54 | AVCodecContext *FFmpegAudioDecoder::codec_context() { 55 | return codec_context_.get(); 56 | } 57 | } -------------------------------------------------------------------------------- /media/audio_decoder.h: -------------------------------------------------------------------------------- 1 | #ifndef MEDIA_AUDIO_DECODER_H_ 2 | #define MEDIA_AUDIO_DECODER_H_ 3 | 4 | #include 5 | #include 6 | #include "base/macros.h" 7 | #include "base/time/time.h" 8 | #include "media/ffmpeg_common.h" 9 | 10 | namespace media { 11 | class FFmpegAudioDecoder { 12 | public: 13 | FFmpegAudioDecoder(); 14 | virtual ~FFmpegAudioDecoder(); 15 | 16 | bool Init(AVStream *stream); 17 | 18 | void UnInit(); 19 | 20 | int SendInput(const AVPacket *pkt) const; 21 | 22 | int FetchOutput(AVFrame *frame) const; 23 | 24 | void Flush() const; 25 | 26 | AVCodecContext *codec_context(); 27 | 28 | private: 29 | std::unique_ptr codec_context_; 30 | DISALLOW_COPY_AND_ASSIGN(FFmpegAudioDecoder); 31 | }; 32 | } 33 | #endif // MEDIA_AUDIO_DECODER_H_ 34 | -------------------------------------------------------------------------------- /media/audio_decoder_thread.cc: -------------------------------------------------------------------------------- 1 | #include "base/logging.h" 2 | #include "media/audio_decoder_thread.h" 3 | #include "media/mp4_dataset.h" 4 | #include "media/audio_decoder.h" 5 | #include "media/ffmpeg_aac_bitstream_converter.h" 6 | #include "media/audio_frame_queue.h" 7 | #include "media/packet_queue.h" 8 | #include "media/audio_resampler.h" 9 | #include "media/video_player.h" 10 | #include "media/media_constants.h" 11 | 12 | namespace media { 13 | 14 | AudioDecoderThread::AudioDecoderThread(VideoPlayer *player, 15 | Mp4Dataset *dataset, 16 | PacketQueue *input_queue, 17 | AudioFrameQueue *output_queue) 18 | : player_(player), 19 | dataset_(dataset), 20 | input_queue_(input_queue), 21 | output_queue_(output_queue), 22 | next_pts_(0), 23 | keep_running_(true), 24 | thread_(new base::DelegateSimpleThread(this, "ADThread")) { 25 | thread_->Start(); 26 | } 27 | 28 | AudioDecoderThread::~AudioDecoderThread() { 29 | keep_running_ = false; 30 | if (thread_) { 31 | thread_->Join(); 32 | thread_.reset(); 33 | } 34 | } 35 | 36 | void AudioDecoderThread::Run() { 37 | InitDecoder(); 38 | DecodeLoop(); 39 | UnInitDecoder(); 40 | } 41 | 42 | bool AudioDecoderThread::InitDecoder() { 43 | AVStream *stream = dataset_->getAudioStream(); 44 | 45 | std::unique_ptr decoder(new FFmpegAudioDecoder()); 46 | if (decoder->Init(stream)) { 47 | decoder_ = std::move(decoder); 48 | } else { 49 | LOG(ERROR) << "create audio decoder failed"; 50 | player_->OnMediaError(Error_AudioCodecCreateFailed); 51 | return false; 52 | } 53 | 54 | if (stream->codecpar->codec_id == AV_CODEC_ID_AAC) { 55 | //AAC需要转换一下 56 | bitstream_converter_ = base::WrapUnique(new FFmpegAACBitstreamConverter(stream->codecpar)); 57 | } else { 58 | LOG(WARNING) << "Decode audio codec[" << stream->codecpar->codec_id << "] maybe fail"; 59 | } 60 | 61 | resampler_ = base::WrapUnique(new FFmpegAudioResampler()); 62 | if (!resampler_->Init(decoder_->codec_context(), 0, kAudioChannels)) { 63 | LOG(ERROR) << "Create audio resampler failed"; 64 | resampler_.reset(); 65 | player_->OnMediaError(Error_AudioResamplerCreateFailed); 66 | } 67 | return true; 68 | } 69 | 70 | void AudioDecoderThread::UnInitDecoder() { 71 | output_queue_->flush(); 72 | resampler_.reset(); 73 | bitstream_converter_.reset(); 74 | decoder_.reset(); 75 | } 76 | 77 | void AudioDecoderThread::DecodeLoop() { 78 | while (keep_running_) { 79 | AVPacket *pkt = FetchPacket(); 80 | 81 | if (pkt) { 82 | if (pkt->data == PacketQueue::kFlushPkt.data) { 83 | DLOG(INFO) << "Got audio flush packet"; 84 | //flush decoder 85 | if (decoder_) { 86 | decoder_->Flush(); 87 | } 88 | output_queue_->flush(); 89 | next_pts_ = 0; 90 | player_->OnFlushCompleted(dataset_->getAudioStreamIndex()); 91 | continue; 92 | } 93 | 94 | if (pkt->data) { 95 | if (decoder_) { 96 | if (bitstream_converter_) { //AAC 97 | if (bitstream_converter_->ConvertPacket(pkt)) { 98 | DecodeOnePacket(pkt); 99 | } 100 | } else { 101 | //NOT AAC ? 102 | DecodeOnePacket(pkt); 103 | } 104 | } 105 | } else { 106 | DLOG(INFO) << "Got audio EOS packet"; 107 | // AAC has no dependent frames so we needn't flush the decoder. 108 | } 109 | av_packet_unref(pkt); 110 | av_packet_free(&pkt); 111 | } else { 112 | //应该到文件末尾了,没有数据需要解码,我们要把解码器中的剩余数据读完 113 | if (!ProcessOutputFrame()) { 114 | usleep(5000); 115 | } 116 | } 117 | } 118 | } 119 | 120 | void AudioDecoderThread::DecodeOnePacket(AVPacket *pkt) { 121 | bool sent_packet = false, frames_remaining = true; 122 | while (!sent_packet || frames_remaining) { 123 | if (!sent_packet) { 124 | const int result = decoder_->SendInput(pkt); 125 | if (result < 0 && result != AVERROR(EAGAIN) && result != AVERROR_EOF) { 126 | DLOG(ERROR) << "Failed to send packet for decoding: " << result; 127 | return; 128 | } 129 | sent_packet = result != AVERROR(EAGAIN); 130 | } 131 | 132 | // See if any frames are available. If we receive an EOF or EAGAIN, there 133 | // should be nothing left to do this pass since we've already provided the 134 | // only input packet that we have. 135 | std::unique_ptr frame(av_frame_alloc()); 136 | const int result = decoder_->FetchOutput(frame.get()); 137 | if (result == AVERROR_EOF || result == AVERROR(EAGAIN)) { 138 | frames_remaining = false; 139 | 140 | // TODO(dalecurtis): This should be a DCHECK() or MEDIA_LOG, but since 141 | // this API is new, lets make it a CHECK first and monitor reports. 142 | if (result == AVERROR(EAGAIN)) { 143 | CHECK(sent_packet) << "avcodec_receive_frame() and " 144 | "avcodec_send_packet() both returned EAGAIN, " 145 | "which is an API violation."; 146 | } 147 | continue; 148 | } else if (result < 0) { 149 | DLOG(ERROR) << "Failed to decode frame: " << result; 150 | continue; 151 | } 152 | OnNewFrame(frame.get()); 153 | av_frame_unref(frame.get()); 154 | } 155 | } 156 | 157 | //没有数据可以输入的时候,我们还要继续读取输出 158 | bool AudioDecoderThread::ProcessOutputFrame() { 159 | if (!decoder_) 160 | return false; 161 | 162 | std::unique_ptr frame(av_frame_alloc()); 163 | const int result = decoder_->FetchOutput(frame.get()); 164 | if (result >= 0) { 165 | OnNewFrame(frame.get()); 166 | av_frame_unref(frame.get()); 167 | return true; 168 | } 169 | return false; 170 | } 171 | 172 | //获得一个解码帧,还需要转码 173 | void AudioDecoderThread::OnNewFrame(AVFrame *frame) { 174 | if (!resampler_) 175 | return; 176 | 177 | uint8_t **output_samples = nullptr; 178 | const int out_nb_samples = resampler_->Resample((const uint8_t **) frame->extended_data, 179 | frame->nb_samples, 180 | &output_samples); 181 | if (out_nb_samples > 0) { 182 | int buffer_size = av_samples_get_buffer_size(nullptr, 183 | kAudioChannels, 184 | out_nb_samples, 185 | AV_SAMPLE_FMT_S16, 186 | 1); 187 | if (buffer_size <= 0) 188 | return; 189 | 190 | MEDIA_BUFFER mb = RK_MPI_MB_CreateAudioBuffer(buffer_size, RK_FALSE); 191 | memcpy(RK_MPI_MB_GetPtr(mb), (const uint8_t *) output_samples[0], buffer_size); 192 | RK_MPI_MB_SetSize(mb, buffer_size); 193 | //尝试修复 pts 问题 194 | if (frame->pts == static_cast(AV_NOPTS_VALUE)) { 195 | frame->pts = next_pts_; 196 | } 197 | if (frame->pts != static_cast(AV_NOPTS_VALUE)) { 198 | next_pts_ = frame->pts + av_rescale_q(frame->nb_samples, 199 | decoder_->codec_context()->time_base, 200 | dataset_->getAudioStream()->time_base); 201 | } 202 | RK_MPI_MB_SetTimestamp(mb, frame->pts); 203 | if (!SendFrame(mb)) { 204 | RK_MPI_MB_ReleaseBuffer(mb); 205 | } 206 | } 207 | } 208 | 209 | /* 210 | * 这里需要注意,此处可能是阻塞的,如果 render线程没有及时取走解码后的数据 211 | * 我们需要休眠一会 212 | */ 213 | bool AudioDecoderThread::SendFrame(MEDIA_BUFFER mb) { 214 | while (keep_running_) { 215 | if (!output_queue_->is_writable()) { 216 | usleep(5000); 217 | continue; 218 | } 219 | output_queue_->put(mb); 220 | return true; 221 | } 222 | return false; 223 | } 224 | 225 | //从文件读取一帧用于解码 226 | AVPacket *AudioDecoderThread::FetchPacket() { 227 | AVPacket *pkt = input_queue_->get(); 228 | while (!pkt) { 229 | DemuxResult result = dataset_->demuxNextPacket(); 230 | if (result != DemuxResult::OK) 231 | break; //AV_EOF or UNKNOWN 232 | pkt = input_queue_->get(); 233 | } 234 | return pkt; 235 | } 236 | } -------------------------------------------------------------------------------- /media/audio_decoder_thread.h: -------------------------------------------------------------------------------- 1 | #ifndef MEDIA_AUDIO_DECODER_THREAD_H_ 2 | #define MEDIA_AUDIO_DECODER_THREAD_H_ 3 | 4 | #include 5 | #include "base/macros.h" 6 | #include "base/threading/simple_thread.h" 7 | #include "base/synchronization/lock.h" 8 | #include "media/ffmpeg_common.h" 9 | #include 10 | 11 | namespace media { 12 | class Mp4Dataset; 13 | class PacketQueue; 14 | class AudioFrameQueue; 15 | class FFmpegAudioDecoder; 16 | class FFmpegAudioResampler; 17 | class FFmpegAACBitstreamConverter; 18 | class VideoPlayer; 19 | 20 | class AudioDecoderThread 21 | : public base::DelegateSimpleThread::Delegate { 22 | public: 23 | explicit AudioDecoderThread(VideoPlayer *player, 24 | Mp4Dataset *dataset, 25 | PacketQueue *input_queue, 26 | AudioFrameQueue *output_queue); 27 | 28 | virtual ~AudioDecoderThread() override; 29 | 30 | private: 31 | void Run() override; 32 | 33 | void DecodeLoop(); 34 | 35 | bool InitDecoder(); 36 | 37 | void UnInitDecoder(); 38 | 39 | void DecodeOnePacket(AVPacket *pkt); 40 | 41 | void OnNewFrame(AVFrame *frame); 42 | 43 | bool SendFrame(MEDIA_BUFFER mb); 44 | 45 | AVPacket *FetchPacket(); 46 | 47 | bool ProcessOutputFrame(); 48 | 49 | VideoPlayer *player_; 50 | Mp4Dataset *dataset_; 51 | PacketQueue *input_queue_; 52 | AudioFrameQueue *output_queue_; 53 | int64_t next_pts_; 54 | bool keep_running_; 55 | std::unique_ptr decoder_; 56 | std::unique_ptr resampler_; 57 | std::unique_ptr bitstream_converter_; 58 | std::unique_ptr thread_; 59 | DISALLOW_COPY_AND_ASSIGN(AudioDecoderThread); 60 | }; 61 | } 62 | #endif // MEDIA_AUDIO_DECODER_THREAD_H_ 63 | -------------------------------------------------------------------------------- /media/audio_frame_queue.cc: -------------------------------------------------------------------------------- 1 | #include "media/audio_frame_queue.h" 2 | #include "media/ffmpeg_common.h" 3 | #include "base/logging.h" 4 | 5 | namespace media { 6 | 7 | AudioFrameQueue::AudioFrameQueue(AVStream *stream, size_t max_size) 8 | : stream_(stream), 9 | max_size_(max_size) {} 10 | 11 | AudioFrameQueue::~AudioFrameQueue() { 12 | flush(); 13 | } 14 | 15 | bool AudioFrameQueue::is_writable() { 16 | base::AutoLock l(lock_); 17 | return frame_list_.size() < max_size_; 18 | } 19 | 20 | int64_t AudioFrameQueue::startTimestamp() { 21 | base::AutoLock l(lock_); 22 | if (frame_list_.empty()) 23 | return AV_NOPTS_VALUE; 24 | MEDIA_BUFFER mb = frame_list_.front(); 25 | return static_cast(RK_MPI_MB_GetTimestamp(mb)); 26 | } 27 | 28 | void AudioFrameQueue::put(MEDIA_BUFFER mb) { 29 | base::AutoLock l(lock_); 30 | frame_list_.push(mb); 31 | } 32 | 33 | MEDIA_BUFFER AudioFrameQueue::get(int64_t render_time) { 34 | base::AutoLock l(lock_); 35 | if (frame_list_.empty()) 36 | return nullptr; 37 | 38 | MEDIA_BUFFER mb = frame_list_.front(); 39 | auto pts = static_cast(RK_MPI_MB_GetTimestamp(mb)); 40 | base::TimeDelta timestamp = media::ConvertFromTimeBase(stream_->time_base, pts); 41 | if (timestamp.InMicroseconds() <= render_time) { 42 | frame_list_.pop(); 43 | DLOG(INFO) << "AudioFrameQueue size: " << frame_list_.size(); 44 | return mb; 45 | } 46 | return nullptr; 47 | } 48 | 49 | void AudioFrameQueue::flush() { 50 | base::AutoLock l(lock_); 51 | while (!frame_list_.empty()) { 52 | MEDIA_BUFFER mb = frame_list_.front(); 53 | frame_list_.pop(); 54 | RK_MPI_MB_ReleaseBuffer(mb); 55 | } 56 | } 57 | 58 | size_t AudioFrameQueue::size() { 59 | base::AutoLock l(lock_); 60 | return frame_list_.size(); 61 | } 62 | 63 | size_t AudioFrameQueue::max_size() const { 64 | return max_size_; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /media/audio_frame_queue.h: -------------------------------------------------------------------------------- 1 | #ifndef MEDIA_AUDIO_FRAME_QUEUE_H_ 2 | #define MEDIA_AUDIO_FRAME_QUEUE_H_ 3 | 4 | #include 5 | #include "base/macros.h" 6 | #include "base/synchronization/lock.h" 7 | #include 8 | 9 | struct AVStream; 10 | 11 | namespace media { 12 | 13 | class AudioFrameQueue { 14 | public: 15 | explicit AudioFrameQueue(AVStream *stream, size_t max_size); 16 | 17 | virtual ~AudioFrameQueue(); 18 | 19 | bool is_writable(); 20 | 21 | int64_t startTimestamp(); 22 | 23 | void put(MEDIA_BUFFER mb); 24 | 25 | MEDIA_BUFFER get(int64_t render_time); 26 | 27 | void flush(); 28 | 29 | size_t size(); 30 | 31 | size_t max_size() const; 32 | 33 | private: 34 | AVStream *stream_; 35 | size_t max_size_; 36 | std::queue frame_list_; 37 | base::Lock lock_; 38 | DISALLOW_COPY_AND_ASSIGN(AudioFrameQueue); 39 | }; 40 | } 41 | 42 | #endif //MEDIA_AUDIO_FRAME_QUEUE_H_ 43 | -------------------------------------------------------------------------------- /media/audio_render.cc: -------------------------------------------------------------------------------- 1 | #include "media/audio_render.h" 2 | #include "base/logging.h" 3 | 4 | namespace media { 5 | #define AO_CHANNEL_ID 0 6 | 7 | RKAudioRender::RKAudioRender() 8 | : initialize_(false) {} 9 | 10 | RKAudioRender::~RKAudioRender() { 11 | UnInit(); 12 | } 13 | 14 | bool RKAudioRender::Init(const std::string &device_name, 15 | int sample_rate, 16 | int channels, 17 | int64_t nb_samples) { 18 | if (initialize_) 19 | return true; 20 | 21 | AO_CHN_ATTR_S ao_chn_attr_s; 22 | memset(&ao_chn_attr_s, 0, sizeof(AO_CHN_ATTR_S)); 23 | ao_chn_attr_s.pcAudioNode = (char *) device_name.c_str(); 24 | ao_chn_attr_s.enSampleFormat = RK_SAMPLE_FMT_S16; 25 | ao_chn_attr_s.u32NbSamples = nb_samples; 26 | ao_chn_attr_s.u32Channels = channels; 27 | ao_chn_attr_s.u32SampleRate = sample_rate; 28 | int ret = RK_MPI_AO_SetChnAttr(AO_CHANNEL_ID, &ao_chn_attr_s); 29 | if (ret) { 30 | LOG(ERROR) << "RK_MPI_AO_SetChnAttr failed:" << ret; 31 | return false; 32 | } 33 | ret = RK_MPI_AO_EnableChn(AO_CHANNEL_ID); 34 | if (ret) { 35 | LOG(ERROR) << "RK_MPI_AO_EnableChn failed:" << ret; 36 | return false; 37 | } 38 | initialize_ = true; 39 | return true; 40 | } 41 | 42 | void RKAudioRender::UnInit() { 43 | if (initialize_) { 44 | RK_MPI_AO_ClearChnBuf(AO_CHANNEL_ID); 45 | RK_MPI_AO_DisableChn(AO_CHANNEL_ID); 46 | initialize_ = false; 47 | } 48 | } 49 | 50 | bool RKAudioRender::SetVolume(int volume) const { 51 | if (!initialize_) 52 | return false; 53 | int ret = RK_MPI_AO_SetVolume(AO_CHANNEL_ID, volume); 54 | return ret == 0; 55 | } 56 | 57 | int RKAudioRender::SendInput(MEDIA_BUFFER mb) const { 58 | if (!initialize_) 59 | return -1; 60 | return RK_MPI_SYS_SendMediaBuffer(RK_ID_AO, AO_CHANNEL_ID, mb); 61 | } 62 | 63 | } -------------------------------------------------------------------------------- /media/audio_render.h: -------------------------------------------------------------------------------- 1 | #ifndef MEDIA_AUDIO_RENDER_H_ 2 | #define MEDIA_AUDIO_RENDER_H_ 3 | 4 | #include "base/macros.h" 5 | #include 6 | #include 7 | 8 | namespace media { 9 | class RKAudioRender { 10 | public: 11 | RKAudioRender(); 12 | virtual ~RKAudioRender(); 13 | 14 | bool Init(const std::string &device_name, 15 | int sample_rate, 16 | int channels, 17 | int64_t nb_samples); 18 | 19 | void UnInit(); 20 | 21 | bool SetVolume(int volume) const; 22 | 23 | int SendInput(MEDIA_BUFFER mb) const; 24 | private: 25 | bool initialize_; 26 | DISALLOW_COPY_AND_ASSIGN(RKAudioRender); 27 | }; 28 | } 29 | #endif // MEDIA_AUDIO_RENDER_H_ 30 | -------------------------------------------------------------------------------- /media/audio_resampler.cc: -------------------------------------------------------------------------------- 1 | #include "media/audio_resampler.h" 2 | #include "base/logging.h" 3 | 4 | namespace media { 5 | 6 | FFmpegAudioResampler::FFmpegAudioResampler() 7 | : max_dst_nb_samples_(0), 8 | converted_input_samples_(nullptr), 9 | in_sample_rate_(0), 10 | in_channels_(0), 11 | out_sample_rate_(0), 12 | out_channels_(0), 13 | resampler_context_(nullptr) {} 14 | 15 | FFmpegAudioResampler::~FFmpegAudioResampler() { 16 | UnInit(); 17 | } 18 | 19 | void FFmpegAudioResampler::UnInit() { 20 | ReleaseBuffer(); 21 | if (resampler_context_) { 22 | swr_free(&resampler_context_); 23 | resampler_context_ = nullptr; 24 | } 25 | } 26 | 27 | void FFmpegAudioResampler::ReleaseBuffer() { 28 | if (converted_input_samples_) { 29 | av_freep(&converted_input_samples_[0]); 30 | av_freep(&converted_input_samples_); 31 | converted_input_samples_ = nullptr; 32 | } 33 | } 34 | 35 | bool FFmpegAudioResampler::Init(AVCodecContext *codec_context, 36 | int out_sample_rate, 37 | int out_channels) { 38 | if (resampler_context_) 39 | return true; 40 | in_sample_rate_ = codec_context->sample_rate; 41 | in_channels_ = av_get_channel_layout_nb_channels(codec_context->channel_layout); 42 | 43 | out_sample_rate_ = out_sample_rate; 44 | if (out_sample_rate_ < 1) 45 | out_sample_rate_ = in_sample_rate_; 46 | 47 | out_channels_ = out_channels; 48 | if (out_channels_ < 1) { 49 | out_channels_ = 2; 50 | } 51 | int64_t out_channel_layout = av_get_default_channel_layout(out_channels); 52 | 53 | resampler_context_ = swr_alloc_set_opts(nullptr, 54 | out_channel_layout, 55 | AV_SAMPLE_FMT_S16, 56 | codec_context->sample_rate, 57 | (int64_t) codec_context->channel_layout, 58 | codec_context->sample_fmt, 59 | codec_context->sample_rate, 60 | 0, 61 | nullptr); 62 | if (!resampler_context_) { 63 | LOG(ERROR) << "swr_alloc_set_opts failed"; 64 | return false; 65 | } 66 | int err = swr_init(resampler_context_); 67 | if (err < 0) { 68 | LOG(ERROR) << "swr_init failed:" << err << ",err:" << AVErrorToString(err); 69 | UnInit(); 70 | return false; 71 | } 72 | 73 | err = av_samples_alloc_array_and_samples(&converted_input_samples_, 74 | nullptr, 75 | out_channels_, 76 | 8192, 77 | AV_SAMPLE_FMT_S16, 78 | 1); 79 | if (err < 0) { 80 | LOG(ERROR) << "av_samples_alloc_array_and_samples failed:" << err << ",err:" << AVErrorToString(err); 81 | UnInit(); 82 | return false; 83 | } 84 | max_dst_nb_samples_ = 8192; 85 | 86 | return true; 87 | } 88 | 89 | int FFmpegAudioResampler::Resample(const uint8_t **data, 90 | int src_nb_samples, 91 | uint8_t ***output_samples) { 92 | if (!resampler_context_) 93 | return -1; 94 | /* compute destination number of samples */ 95 | int64_t dst_nb_samples = av_rescale_rnd(swr_get_delay(resampler_context_, in_sample_rate_) + src_nb_samples, 96 | out_sample_rate_, 97 | in_sample_rate_, 98 | AV_ROUND_UP); 99 | if (dst_nb_samples <= 0) 100 | return -1; 101 | 102 | if (dst_nb_samples > max_dst_nb_samples_) { 103 | ReleaseBuffer(); 104 | if (av_samples_alloc_array_and_samples(&converted_input_samples_, 105 | nullptr, 106 | out_channels_, 107 | (int) dst_nb_samples, 108 | AV_SAMPLE_FMT_S16, 109 | 1) < 0) { 110 | return AVERROR(ENOMEM); 111 | } 112 | max_dst_nb_samples_ = dst_nb_samples; 113 | } 114 | 115 | /* convert to destination format */ 116 | int err = swr_convert(resampler_context_, 117 | converted_input_samples_, 118 | (int) dst_nb_samples, 119 | data, 120 | src_nb_samples); 121 | 122 | if (err < 0) { 123 | return err; 124 | } 125 | *output_samples = converted_input_samples_; 126 | return err; 127 | } 128 | 129 | } -------------------------------------------------------------------------------- /media/audio_resampler.h: -------------------------------------------------------------------------------- 1 | #ifndef MEDIA_AUDIO_RESAMPLER_H_ 2 | #define MEDIA_AUDIO_RESAMPLER_H_ 3 | 4 | #include 5 | #include "base/macros.h" 6 | #include "media/ffmpeg_common.h" 7 | 8 | namespace media { 9 | class FFmpegAudioResampler { 10 | public: 11 | FFmpegAudioResampler(); 12 | virtual ~FFmpegAudioResampler(); 13 | 14 | bool Init(AVCodecContext *codec_context, 15 | int out_sample_rate = 0,//use input samplerate 16 | int out_channels = 2); 17 | 18 | void UnInit(); 19 | 20 | int Resample(const uint8_t **data, 21 | int src_nb_samples, 22 | uint8_t ***output_samples); 23 | 24 | private: 25 | void ReleaseBuffer(); 26 | int64_t max_dst_nb_samples_; 27 | uint8_t **converted_input_samples_; 28 | int in_sample_rate_; 29 | int in_channels_; 30 | int out_sample_rate_; 31 | int out_channels_; 32 | SwrContext *resampler_context_; 33 | DISALLOW_COPY_AND_ASSIGN(FFmpegAudioResampler); 34 | }; 35 | } 36 | #endif // MEDIA_AUDIO_RESAMPLER_H_ 37 | -------------------------------------------------------------------------------- /media/ffmpeg_aac_bitstream_converter.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #include "media/ffmpeg_aac_bitstream_converter.h" 6 | 7 | #include "base/logging.h" 8 | #include "media/ffmpeg_common.h" 9 | 10 | namespace media { 11 | 12 | namespace { 13 | 14 | // Creates an ADTS header and stores in |hdr| 15 | // Assumes |hdr| points to an array of length |kAdtsHeaderSize| 16 | // Returns false if parameter values are for an unsupported configuration. 17 | bool GenerateAdtsHeader(int codec, 18 | int layer, 19 | int audio_profile, 20 | int sample_rate_index, 21 | int private_stream, 22 | int channel_configuration, 23 | int originality, 24 | int home, 25 | int copyrighted_stream, 26 | int copyright_start, 27 | int frame_length, 28 | int buffer_fullness, 29 | int number_of_frames_minus_one, 30 | uint8_t *hdr) { 31 | DCHECK_EQ(codec, AV_CODEC_ID_AAC); 32 | 33 | memset(reinterpret_cast(hdr), 0, 34 | FFmpegAACBitstreamConverter::kAdtsHeaderSize); 35 | // Ref: http://wiki.multimedia.cx/index.php?title=ADTS 36 | // ADTS header structure is the following 37 | // AAAAAAAA AAAABCCD EEFFFFGH HHIJKLMM MMMMMMMM MMMOOOOO OOOOOOPP 38 | // 39 | // A Syncword 0xFFF, all bits must be 1 40 | // B MPEG Version: 0 for MPEG-4, 1 for MPEG-2 41 | // C Layer: always 0 42 | // D Protection absent: Set to 1 if no CRC and 0 if there is CRC. 43 | // E Profile: the MPEG-4 Audio Object Type minus 1. 44 | // F MPEG-4 Sampling Frequency Index (15 is forbidden) 45 | // G Private stream: 46 | // H MPEG-4 Channel Configuration 47 | // I Originality 48 | // J Home 49 | // K Copyrighted Stream 50 | // L Copyright_ start 51 | // M Frame length. This must include the ADTS header length. 52 | // O Buffer fullness 53 | // P Number of AAC frames in ADTS frame minus 1. 54 | // For maximum compatibility always use 1 AAC frame per ADTS frame. 55 | 56 | // Syncword 57 | hdr[0] = 0xFF; 58 | hdr[1] = 0xF0; 59 | 60 | // Layer is always 0. No further action required. 61 | 62 | // Protection absent (no CRC) is always 1. 63 | hdr[1] |= 1; 64 | 65 | switch (audio_profile) { 66 | case FF_PROFILE_AAC_MAIN:break; 67 | case FF_PROFILE_AAC_HE: 68 | case FF_PROFILE_AAC_HE_V2: 69 | case FF_PROFILE_AAC_LOW:hdr[2] |= (1 << 6); 70 | break; 71 | case FF_PROFILE_AAC_SSR:hdr[2] |= (2 << 6); 72 | break; 73 | case FF_PROFILE_AAC_LTP:hdr[2] |= (3 << 6); 74 | break; 75 | default:DLOG(ERROR) << "[" << __FUNCTION__ << "] " 76 | << "unsupported audio profile:" 77 | << audio_profile; 78 | return false; 79 | } 80 | 81 | hdr[2] |= ((sample_rate_index & 0xf) << 2); 82 | 83 | if (private_stream) 84 | hdr[2] |= (1 << 1); 85 | 86 | switch (channel_configuration) { 87 | case 1: 88 | // front-center 89 | hdr[3] |= (1 << 6); 90 | break; 91 | case 2: 92 | // front-left, front-right 93 | hdr[3] |= (2 << 6); 94 | break; 95 | case 3: 96 | // front-center, front-left, front-right 97 | hdr[3] |= (3 << 6); 98 | break; 99 | case 4: 100 | // front-center, front-left, front-right, back-center 101 | hdr[2] |= 1; 102 | break; 103 | case 5: 104 | // front-center, front-left, front-right, back-left, back-right 105 | hdr[2] |= 1; 106 | hdr[3] |= (1 << 6); 107 | break; 108 | case 6: 109 | // front-center, front-left, front-right, back-left, back-right, 110 | // LFE-channel 111 | hdr[2] |= 1; 112 | hdr[3] |= (2 << 6); 113 | break; 114 | case 8: 115 | // front-center, front-left, front-right, side-left, side-right, 116 | // back-left, back-right, LFE-channel 117 | hdr[2] |= 1; 118 | hdr[3] |= (3 << 6); 119 | break; 120 | default:DLOG(ERROR) << "[" << __FUNCTION__ << "] " 121 | << "unsupported number of audio channels:" 122 | << channel_configuration; 123 | return false; 124 | } 125 | 126 | if (originality) 127 | hdr[3] |= (1 << 5); 128 | 129 | if (home) 130 | hdr[3] |= (1 << 4); 131 | 132 | if (copyrighted_stream) 133 | hdr[3] |= (1 << 3); 134 | 135 | if (copyright_start) 136 | hdr[3] |= (1 << 2); 137 | 138 | // frame length 139 | hdr[3] |= (frame_length >> 11) & 0x03; 140 | hdr[4] = (frame_length >> 3) & 0xFF; 141 | hdr[5] |= (frame_length & 7) << 5; 142 | 143 | // buffer fullness 144 | hdr[5] |= (buffer_fullness >> 6) & 0x1F; 145 | hdr[6] |= (buffer_fullness & 0x3F) << 2; 146 | 147 | hdr[6] |= number_of_frames_minus_one & 0x3; 148 | 149 | return true; 150 | } 151 | 152 | } 153 | 154 | FFmpegAACBitstreamConverter::FFmpegAACBitstreamConverter( 155 | AVCodecParameters *stream_codec_parameters) 156 | : stream_codec_parameters_(stream_codec_parameters), 157 | header_generated_(false), 158 | codec_(), 159 | audio_profile_(), 160 | sample_rate_index_(), 161 | channel_configuration_(), 162 | frame_length_() { 163 | CHECK(stream_codec_parameters_); 164 | } 165 | 166 | FFmpegAACBitstreamConverter::~FFmpegAACBitstreamConverter() { 167 | } 168 | 169 | bool FFmpegAACBitstreamConverter::ConvertPacket(AVPacket *packet) { 170 | if (packet == NULL || !packet->data) { 171 | return false; 172 | } 173 | 174 | int header_plus_packet_size = 175 | packet->size + kAdtsHeaderSize; 176 | if (!stream_codec_parameters_->extradata) { 177 | DLOG(ERROR) << "extradata is null"; 178 | return false; 179 | } 180 | if (stream_codec_parameters_->extradata_size < 2) { 181 | DLOG(ERROR) << "extradata too small to contain MP4A header"; 182 | return false; 183 | } 184 | int sample_rate_index = 185 | ((stream_codec_parameters_->extradata[0] & 0x07) << 1) | 186 | ((stream_codec_parameters_->extradata[1] & 0x80) >> 7); 187 | if (sample_rate_index > 12) { 188 | sample_rate_index = 4; 189 | } 190 | 191 | if (!header_generated_ || codec_ != stream_codec_parameters_->codec_id || 192 | audio_profile_ != stream_codec_parameters_->profile || 193 | sample_rate_index_ != sample_rate_index || 194 | channel_configuration_ != stream_codec_parameters_->channels || 195 | frame_length_ != header_plus_packet_size) { 196 | header_generated_ = 197 | GenerateAdtsHeader(stream_codec_parameters_->codec_id, 198 | 0, // layer 199 | stream_codec_parameters_->profile, sample_rate_index, 200 | 0, // private stream 201 | stream_codec_parameters_->channels, 202 | 0, // originality 203 | 0, // home 204 | 0, // copyrighted_stream 205 | 0, // copyright_ start 206 | header_plus_packet_size, 207 | 0x7FF, // buffer fullness 208 | 0, // one frame per packet 209 | hdr_); 210 | codec_ = stream_codec_parameters_->codec_id; 211 | audio_profile_ = stream_codec_parameters_->profile; 212 | sample_rate_index_ = sample_rate_index; 213 | channel_configuration_ = stream_codec_parameters_->channels; 214 | frame_length_ = header_plus_packet_size; 215 | } 216 | 217 | // Inform caller if the header generation failed. 218 | if (!header_generated_) 219 | return false; 220 | 221 | // Allocate new packet for the output. 222 | AVPacket dest_packet; 223 | if (av_new_packet(&dest_packet, header_plus_packet_size) != 0) 224 | return false; // Memory allocation failure. 225 | 226 | memcpy(dest_packet.data, hdr_, kAdtsHeaderSize); 227 | memcpy(reinterpret_cast(dest_packet.data + kAdtsHeaderSize), 228 | reinterpret_cast(packet->data), packet->size); 229 | 230 | // This is a bit tricky: since the interface does not allow us to replace 231 | // the pointer of the old packet with a new one, we will initially copy the 232 | // metadata from old packet to new bigger packet. 233 | av_packet_copy_props(&dest_packet, packet); 234 | 235 | // Release the old packet. 236 | av_packet_unref(packet); 237 | // Finally, replace the values in the input packet. 238 | memcpy(packet, &dest_packet, sizeof(*packet)); 239 | return true; 240 | } 241 | 242 | } // namespace media 243 | -------------------------------------------------------------------------------- /media/ffmpeg_aac_bitstream_converter.h: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #ifndef MEDIA_FILTERS_FFMPEG_AAC_BITSTREAM_CONVERTER_H_ 6 | #define MEDIA_FILTERS_FFMPEG_AAC_BITSTREAM_CONVERTER_H_ 7 | 8 | #include 9 | 10 | #include "base/macros.h" 11 | 12 | // Forward declarations for FFmpeg datatypes used. 13 | struct AVCodecParameters; 14 | struct AVPacket; 15 | 16 | namespace media { 17 | 18 | // Bitstream converter that adds ADTS headers to AAC frames. 19 | class FFmpegAACBitstreamConverter { 20 | public: 21 | enum { kAdtsHeaderSize = 7 }; 22 | 23 | // The |stream_codec_parameters| will be used during conversion and should be 24 | // the AVCodecParameters for the stream sourcing these packets. A reference to 25 | // |stream_codec_parameters| is retained, so it must outlive this class. 26 | explicit FFmpegAACBitstreamConverter( 27 | AVCodecParameters *stream_codec_parameters); 28 | ~FFmpegAACBitstreamConverter(); 29 | 30 | // FFmpegBitstreamConverter implementation. 31 | // Uses FFmpeg allocation methods for buffer allocation to ensure 32 | // compatibility with FFmpeg's memory management. 33 | bool ConvertPacket(AVPacket *packet); 34 | 35 | private: 36 | // Variable to hold a pointer to memory where we can access the global 37 | // data from the FFmpeg file format's global headers. 38 | AVCodecParameters *stream_codec_parameters_; 39 | 40 | bool header_generated_; 41 | uint8_t hdr_[kAdtsHeaderSize]; 42 | int codec_; 43 | int audio_profile_; 44 | int sample_rate_index_; 45 | int channel_configuration_; 46 | int frame_length_; 47 | 48 | DISALLOW_COPY_AND_ASSIGN(FFmpegAACBitstreamConverter); 49 | }; 50 | 51 | } // namespace media 52 | 53 | #endif // MEDIA_FILTERS_FFMPEG_AAC_BITSTREAM_CONVERTER_H_ 54 | -------------------------------------------------------------------------------- /media/ffmpeg_common.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #include "media/ffmpeg_common.h" 6 | 7 | namespace media { 8 | 9 | static const AVRational kMicrosBase = {1, base::Time::kMicrosecondsPerSecond}; 10 | 11 | base::TimeDelta ConvertFromTimeBase(const AVRational &time_base, 12 | int64_t timestamp) { 13 | int64_t microseconds = av_rescale_q(timestamp, time_base, kMicrosBase); 14 | return base::TimeDelta::FromMicroseconds(microseconds); 15 | } 16 | 17 | int64_t ConvertToTimeBase(const AVRational &time_base, 18 | const base::TimeDelta ×tamp) { 19 | return av_rescale_q(timestamp.InMicroseconds(), kMicrosBase, time_base); 20 | } 21 | 22 | std::string AVErrorToString(int errnum) { 23 | char errbuf[AV_ERROR_MAX_STRING_SIZE] = {0}; 24 | av_strerror(errnum, errbuf, AV_ERROR_MAX_STRING_SIZE); 25 | return std::string(errbuf); 26 | } 27 | 28 | } // namespace media 29 | -------------------------------------------------------------------------------- /media/ffmpeg_common.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #ifndef MEDIA_FFMPEG_FFMPEG_COMMON_H_ 6 | #define MEDIA_FFMPEG_FFMPEG_COMMON_H_ 7 | 8 | #include 9 | 10 | // Used for FFmpeg error codes. 11 | #include 12 | 13 | #include "base/time/time.h" 14 | #include "media/ffmpeg_deleters.h" 15 | 16 | // Include FFmpeg header files. 17 | extern "C" { 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | } // extern "C" 30 | 31 | namespace media { 32 | 33 | // The following implement the deleters declared in ffmpeg_deleters.h (which 34 | // contains the declarations needed for use with |scoped_ptr| without #include 35 | // "pollution"). 36 | 37 | inline void ScopedPtrAVFree::operator()(void *x) const { 38 | av_free(x); 39 | } 40 | 41 | inline void ScopedPtrAVFreePacket::operator()(void *x) const { 42 | AVPacket *packet = static_cast(x); 43 | av_packet_free(&packet); 44 | } 45 | 46 | inline void ScopedPtrAVFreeContext::operator()(void *x) const { 47 | AVCodecContext *codec_context = static_cast(x); 48 | avcodec_free_context(&codec_context); 49 | } 50 | 51 | inline void ScopedPtrAVFreeFrame::operator()(void *x) const { 52 | AVFrame *frame = static_cast(x); 53 | av_frame_free(&frame); 54 | } 55 | 56 | // Converts an int64_t timestamp in |time_base| units to a base::TimeDelta. 57 | // For example if |timestamp| equals 11025 and |time_base| equals {1, 44100} 58 | // then the return value will be a base::TimeDelta for 0.25 seconds since that 59 | // is how much time 11025/44100ths of a second represents. 60 | base::TimeDelta ConvertFromTimeBase(const AVRational &time_base, 61 | int64_t timestamp); 62 | 63 | // Converts a base::TimeDelta into an int64_t timestamp in |time_base| units. 64 | // For example if |timestamp| is 0.5 seconds and |time_base| is {1, 44100}, then 65 | // the return value will be 22050 since that is how many 1/44100ths of a second 66 | // represent 0.5 seconds. 67 | int64_t ConvertToTimeBase(const AVRational &time_base, 68 | const base::TimeDelta ×tamp); 69 | 70 | std::string AVErrorToString(int errnum); 71 | 72 | } // namespace media 73 | 74 | #endif // MEDIA_FFMPEG_FFMPEG_COMMON_H_ 75 | -------------------------------------------------------------------------------- /media/ffmpeg_deleters.h: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | // This file contains declarations for deleters for use with |scoped_ptr|. To 6 | // avoid requiring additional #includes, the (inline) definitions are in 7 | // ffmpeg_common.h. (Forward declarations of deleters aren't sufficient for 8 | // |scoped_ptr|.) 9 | 10 | #ifndef MEDIA_FFMPEG_FFMPEG_DELETERS_H_ 11 | #define MEDIA_FFMPEG_FFMPEG_DELETERS_H_ 12 | 13 | namespace media { 14 | 15 | // Wraps FFmpeg's av_free() in a class that can be passed as a template argument 16 | // to scoped_ptr_malloc. 17 | struct ScopedPtrAVFree { 18 | void operator()(void* x) const; 19 | }; 20 | 21 | // This assumes that the AVPacket being captured was allocated outside of 22 | // FFmpeg via the new operator. Do not use this with AVPacket instances that 23 | // are allocated via malloc() or av_malloc(). 24 | struct ScopedPtrAVFreePacket { 25 | void operator()(void* x) const; 26 | }; 27 | 28 | // Frees an AVCodecContext object in a class that can be passed as a Deleter 29 | // argument to scoped_ptr_malloc. 30 | struct ScopedPtrAVFreeContext { 31 | void operator()(void* x) const; 32 | }; 33 | 34 | // Frees an AVFrame object in a class that can be passed as a Deleter argument 35 | // to scoped_ptr_malloc. 36 | struct ScopedPtrAVFreeFrame { 37 | void operator()(void* x) const; 38 | }; 39 | 40 | } // namespace media 41 | 42 | #endif // MEDIA_FFMPEG_FFMPEG_DELETERS_H_ 43 | -------------------------------------------------------------------------------- /media/media_constants.h: -------------------------------------------------------------------------------- 1 | #ifndef MEDIA_MEDIA_CONSTANTS_H_ 2 | #define MEDIA_MEDIA_CONSTANTS_H_ 3 | 4 | #include "base/macros.h" 5 | #include "base/time/time.h" 6 | 7 | namespace media { 8 | const int64_t kRenderPollDelay = 20000; 9 | 10 | const int kAudioChannels = 2; 11 | 12 | } 13 | 14 | #endif //MEDIA_MEDIA_CONSTANTS_H_ 15 | -------------------------------------------------------------------------------- /media/mp4_dataset.cc: -------------------------------------------------------------------------------- 1 | #include "media/mp4_dataset.h" 2 | #include "media/packet_queue.h" 3 | #include "base/logging.h" 4 | 5 | namespace media { 6 | namespace { 7 | 8 | AVPacket *make_eos_packet(int stream_idx) { 9 | AVPacket *pkt = av_packet_alloc(); 10 | av_init_packet(pkt); 11 | pkt->data = nullptr; 12 | pkt->size = 0; 13 | pkt->stream_index = stream_idx; 14 | return pkt; 15 | } 16 | } 17 | 18 | std::unique_ptr Mp4Dataset::create(const std::string &file) { 19 | std::unique_ptr dataset(new Mp4Dataset()); 20 | if (dataset->init(file) < 0) { 21 | LOG(ERROR) << "Failed to initialize mp4 dataset"; 22 | return nullptr; 23 | } 24 | return dataset; 25 | } 26 | 27 | Mp4Dataset::Mp4Dataset() 28 | : format_ctx_(nullptr) {} 29 | 30 | Mp4Dataset::~Mp4Dataset() { 31 | clearFormatContext(); 32 | } 33 | 34 | int Mp4Dataset::init(const std::string &file) { 35 | AVFormatContext *input_ctx = nullptr; 36 | int err = avformat_open_input(&input_ctx, file.c_str(), NULL, NULL); 37 | if (err) { 38 | LOG(ERROR) << "avformat_open_input:" << err << ",err:" << AVErrorToString(err); 39 | return -1; 40 | } 41 | format_ctx_ = input_ctx; 42 | err = avformat_find_stream_info(format_ctx_, NULL); 43 | if (err < 0) { 44 | LOG(ERROR) << "avformat_find_stream_info:" << err << ",err:" << AVErrorToString(err); 45 | return -1; 46 | } 47 | av_dump_format(format_ctx_, 0, file.c_str(), false); 48 | 49 | for (int i = 0; i < format_ctx_->nb_streams; i++) { 50 | if (format_ctx_->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { 51 | audio_stream_idx_ = i; 52 | LOG(INFO) << "Found audio stream:" << format_ctx_->streams[i]->codecpar->codec_id; 53 | } else if (format_ctx_->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { 54 | video_stream_idx_ = i; 55 | LOG(INFO) << "Found video stream:" << format_ctx_->streams[i]->codecpar->codec_id; 56 | } 57 | } 58 | // We expect a dataset to at least have one video stream 59 | if (video_stream_idx_ == -1) { 60 | LOG(ERROR) << "No video stream found"; 61 | return -1; 62 | } 63 | 64 | for (int i = 0; i < 100; ++i) { 65 | std::unique_ptr packet(av_packet_alloc()); 66 | int ret = av_read_frame(format_ctx_, packet.get()); 67 | if (ret >= 0) { 68 | if (video_stream_idx_ >= 0 && video_stream_idx_ == packet->stream_index) { 69 | if (packet->dts != static_cast(AV_NOPTS_VALUE) 70 | && packet->pts != static_cast(AV_NOPTS_VALUE)) { 71 | enable_seek_ = true; 72 | av_packet_unref(packet.get()); 73 | break; 74 | } 75 | } 76 | } 77 | av_packet_unref(packet.get()); 78 | } 79 | return avformat_seek_file(format_ctx_, 80 | -1, 81 | INT64_MIN, 82 | 0, 83 | INT64_MAX, 84 | 0); 85 | } 86 | 87 | DemuxResult Mp4Dataset::demuxNextPacket() { 88 | base::AutoLock l(lock_); 89 | std::unique_ptr packet(av_packet_alloc()); 90 | int ret = av_read_frame(format_ctx_, packet.get()); 91 | if (ret >= 0) { 92 | if (audio_queue_ && audio_stream_idx_ >= 0 93 | && audio_stream_idx_ == packet->stream_index) { 94 | audio_queue_->put(packet.release()); 95 | 96 | } else if (video_queue_ && video_stream_idx_ >= 0 97 | && video_stream_idx_ == packet->stream_index) { 98 | video_queue_->put(packet.release()); 99 | } else { 100 | av_packet_unref(packet.get()); 101 | } 102 | return DemuxResult::OK; 103 | } 104 | 105 | if (ret == AVERROR_EOF || avio_feof(format_ctx_->pb)) { 106 | if (audio_queue_ && audio_stream_idx_ >= 0) { 107 | AVPacket *pkt = make_eos_packet(audio_stream_idx_); 108 | audio_queue_->put(pkt); 109 | } 110 | if (video_queue_ && video_stream_idx_ >= 0) { 111 | AVPacket *pkt = make_eos_packet(video_stream_idx_); 112 | video_queue_->put(pkt); 113 | } 114 | return DemuxResult::AV_EOF; 115 | } 116 | return DemuxResult::UNKNOWN; 117 | } 118 | 119 | int Mp4Dataset::seek(double timestamp) { 120 | base::AutoLock l(lock_); 121 | if (!enable_seek_) { 122 | return -1; 123 | } 124 | auto seek_time = static_cast(timestamp * base::Time::kMicrosecondsPerSecond); 125 | int ret = av_seek_frame(format_ctx_, -1, seek_time, AVSEEK_FLAG_BACKWARD); 126 | if (ret < 0) { 127 | LOG(ERROR) << "av_seek_frame:" << ret << ",err:" << AVErrorToString(ret); 128 | return ret; 129 | } 130 | if (audio_queue_ && audio_stream_idx_ >= 0) { 131 | audio_queue_->flush(); 132 | } 133 | if (video_queue_ && video_stream_idx_ >= 0) { 134 | video_queue_->flush(); 135 | } 136 | return 0; 137 | } 138 | 139 | int Mp4Dataset::rewind() { 140 | base::AutoLock l(lock_); 141 | int ret = avformat_seek_file(format_ctx_, 142 | -1, 143 | INT64_MIN, 144 | 0, 145 | INT64_MAX, 146 | 0); 147 | if (ret < 0) { 148 | LOG(ERROR) << "avformat_seek_file:" << ret << ",err:" << AVErrorToString(ret); 149 | return ret; 150 | } 151 | return 0; 152 | } 153 | 154 | void Mp4Dataset::setAudioPacketQueue(PacketQueue *audio_queue) { 155 | audio_queue_ = audio_queue; 156 | } 157 | 158 | void Mp4Dataset::setVideoPacketQueue(PacketQueue *video_queue) { 159 | video_queue_ = video_queue; 160 | } 161 | 162 | int Mp4Dataset::getAudioStreamIndex() const { 163 | return audio_stream_idx_; 164 | } 165 | 166 | int Mp4Dataset::getVideoStreamIndex() const { 167 | return video_stream_idx_; 168 | } 169 | 170 | bool Mp4Dataset::seekable() const { 171 | return enable_seek_; 172 | } 173 | 174 | AVFormatContext *Mp4Dataset::getFormatContext() { 175 | return format_ctx_; 176 | } 177 | 178 | AVStream *Mp4Dataset::getAudioStream() { 179 | if (audio_stream_idx_ < 0) 180 | return nullptr; 181 | return format_ctx_->streams[audio_stream_idx_]; 182 | } 183 | 184 | AVStream *Mp4Dataset::getVideoStream() { 185 | if (video_stream_idx_ < 0) 186 | return nullptr; 187 | return format_ctx_->streams[video_stream_idx_]; 188 | } 189 | 190 | void Mp4Dataset::clearFormatContext() { 191 | if (format_ctx_) { 192 | if (format_ctx_->iformat) { 193 | //input context 194 | avformat_close_input(&format_ctx_); 195 | } else { 196 | //output context 197 | if (format_ctx_->pb) { 198 | avio_closep(&format_ctx_->pb); 199 | } 200 | } 201 | avformat_free_context(format_ctx_); 202 | format_ctx_ = nullptr; 203 | } 204 | } 205 | } -------------------------------------------------------------------------------- /media/mp4_dataset.h: -------------------------------------------------------------------------------- 1 | #ifndef MEDIA_MP4_DATASET_H_ 2 | #define MEDIA_MP4_DATASET_H_ 3 | 4 | #include 5 | #include "base/macros.h" 6 | #include "media/ffmpeg_common.h" 7 | #include "base/synchronization/lock.h" 8 | 9 | namespace media { 10 | //https://github.com/gameltb/rnbox/blob/0d453cf3f325b55cf88e4c77bd78b06263f2aab6/external/android-emu/android-emu/android/mp4/MP4Dataset.h 11 | //mp4 file demuxe 12 | 13 | class PacketQueue; 14 | 15 | enum class DemuxResult { 16 | UNKNOWN, 17 | OK, 18 | AV_EOF, 19 | }; 20 | 21 | class Mp4Dataset { 22 | public: 23 | Mp4Dataset(); 24 | virtual ~Mp4Dataset(); 25 | 26 | static std::unique_ptr 27 | create(const std::string& file); 28 | 29 | DemuxResult demuxNextPacket(); 30 | 31 | int seek(double timestamp); 32 | 33 | int rewind(); 34 | 35 | void setAudioPacketQueue(PacketQueue *audio_queue); 36 | 37 | void setVideoPacketQueue(PacketQueue *video_queue); 38 | 39 | int getAudioStreamIndex() const; 40 | 41 | int getVideoStreamIndex() const; 42 | 43 | bool seekable() const; 44 | 45 | AVStream *getAudioStream(); 46 | 47 | AVStream *getVideoStream(); 48 | 49 | AVFormatContext *getFormatContext(); 50 | 51 | void clearFormatContext(); 52 | private: 53 | int init(const std::string& file); 54 | 55 | AVFormatContext *format_ctx_; 56 | int audio_stream_idx_ = -1; 57 | int video_stream_idx_ = -1; 58 | bool enable_seek_ = false; 59 | PacketQueue *audio_queue_{}; 60 | PacketQueue *video_queue_{}; 61 | base::Lock lock_; 62 | DISALLOW_COPY_AND_ASSIGN(Mp4Dataset); 63 | }; 64 | } 65 | #endif // MEDIA_MP4_DATASET_H_ 66 | -------------------------------------------------------------------------------- /media/mpp_decoder.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "media/mpp_decoder.h" 4 | #include "base/logging.h" 5 | 6 | namespace media { 7 | 8 | RKMppDecoder::RKMppDecoder(MppCodingType coding_type, size_t max_buffer_size) 9 | : coding_type_(coding_type), 10 | max_buffer_size_(max_buffer_size), 11 | ctx_(nullptr), 12 | mpi_(nullptr), 13 | frame_group_(nullptr) {} 14 | 15 | RKMppDecoder::~RKMppDecoder() { 16 | UnInit(); 17 | } 18 | 19 | bool RKMppDecoder::Init() { 20 | if (ctx_) 21 | return true; 22 | 23 | MPP_RET ret = mpp_create(&ctx_, &mpi_); 24 | if (MPP_OK != ret) { 25 | LOG(ERROR) << "mpp_create failed: " << ret; 26 | return false; 27 | } 28 | 29 | ret = mpp_init(ctx_, MPP_CTX_DEC, coding_type_); 30 | if (ret != MPP_OK) { 31 | LOG(ERROR) << "mpp_init failed:" << ret << ",with type:" << coding_type_; 32 | return false; 33 | } 34 | 35 | MppParam param; 36 | int mode = MPP_POLL_NON_BLOCK; 37 | param = &mode; 38 | ret = mpi_->control(ctx_, MPP_SET_OUTPUT_BLOCK, param); 39 | if (ret != MPP_OK) { 40 | LOG(ERROR) << "MPP_SET_OUTPUT_BLOCK failed: " << ret; 41 | return false; 42 | } 43 | 44 | mode = MPP_POLL_NON_BLOCK; 45 | param = &mode; 46 | ret = mpi_->control(ctx_, MPP_SET_INPUT_BLOCK, param); 47 | if (ret != MPP_OK) { 48 | LOG(ERROR) << "MPP_SET_INPUT_BLOCK failed: " << ret; 49 | return false; 50 | } 51 | 52 | ret = mpp_buffer_group_get_internal(&frame_group_, MPP_BUFFER_TYPE_ION); 53 | if (ret != MPP_OK) { 54 | LOG(ERROR) << "mpp_buffer_group_get_internal failed: " << ret; 55 | return false; 56 | } 57 | ret = mpi_->control(ctx_, MPP_DEC_SET_EXT_BUF_GROUP, frame_group_); 58 | if (ret != MPP_OK) { 59 | LOG(ERROR) << "MPP_DEC_SET_EXT_BUF_GROUP failed: " << ret; 60 | return false; 61 | } 62 | ret = mpp_buffer_group_limit_config(frame_group_, 0, max_buffer_size_); 63 | if (ret != MPP_OK) { 64 | LOG(ERROR) << "mpp_buffer_group_limit_config failed: " << ret << ",max buffer size: " << max_buffer_size_; 65 | return false; 66 | } 67 | return true; 68 | } 69 | 70 | void RKMppDecoder::UnInit() { 71 | if (mpi_) { 72 | mpi_->reset(ctx_); 73 | mpp_destroy(ctx_); 74 | } 75 | if (frame_group_) { 76 | mpp_buffer_group_clear(frame_group_); 77 | mpp_buffer_group_put(frame_group_); 78 | frame_group_ = nullptr; 79 | } 80 | } 81 | 82 | int RKMppDecoder::SendInput(MppPacket packet) { 83 | if (!ctx_) 84 | return -EFAULT; 85 | return mpi_->decode_put_packet(ctx_, packet); 86 | } 87 | 88 | MppFrame RKMppDecoder::FetchOutput() { 89 | if (!ctx_) 90 | return nullptr; 91 | 92 | Again: 93 | MppFrame mppframe = nullptr; 94 | MPP_RET ret = mpi_->decode_get_frame(ctx_, &mppframe); 95 | if (ret != MPP_OK) { 96 | if (ret != MPP_ERR_TIMEOUT) { 97 | DLOG(ERROR) << "decode_get_frame failed: " << ret; 98 | return nullptr; 99 | } 100 | } 101 | if (!mppframe) { 102 | return nullptr; 103 | } 104 | if (mpp_frame_get_info_change(mppframe)) { 105 | RK_U32 width = mpp_frame_get_width(mppframe); 106 | RK_U32 height = mpp_frame_get_height(mppframe); 107 | RK_U32 vir_width = mpp_frame_get_hor_stride(mppframe); 108 | RK_U32 vir_height = mpp_frame_get_ver_stride(mppframe); 109 | ret = mpi_->control(ctx_, MPP_DEC_SET_INFO_CHANGE_READY, nullptr); 110 | if (ret != MPP_OK) { 111 | DLOG(ERROR) << "mpp_frame_get_info_change failed: " << ret; 112 | } 113 | DLOG(INFO) << "MppDec info change(" << width << "," << height << "," << vir_width << "," << vir_height << ")"; 114 | mpp_frame_deinit(&mppframe); 115 | goto Again; 116 | } 117 | 118 | if (mpp_frame_get_discard(mppframe)) { 119 | DLOG(ERROR) << "Received a discard frame"; 120 | mpp_frame_deinit(&mppframe); 121 | return nullptr; 122 | } 123 | 124 | if (mpp_frame_get_errinfo(mppframe)) { 125 | DLOG(ERROR) << "Received a errinfo frame"; 126 | mpp_frame_deinit(&mppframe); 127 | return nullptr; 128 | } 129 | return mppframe; 130 | } 131 | 132 | int RKMppDecoder::Flush() { 133 | if (!ctx_) 134 | return -EFAULT; 135 | return mpi_->reset(ctx_); 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /media/mpp_decoder.h: -------------------------------------------------------------------------------- 1 | #ifndef MEDIA_MPP_DECODER_H_ 2 | #define MEDIA_MPP_DECODER_H_ 3 | 4 | #include "base/macros.h" 5 | #include "base/time/time.h" 6 | #include 7 | #include 8 | #include 9 | 10 | namespace media { 11 | 12 | #define FRAMEGROUP_MAX_FRAMES 16 13 | 14 | class RKMppDecoder { 15 | public: 16 | explicit RKMppDecoder(MppCodingType coding_type, size_t max_buffer_size = FRAMEGROUP_MAX_FRAMES); 17 | 18 | virtual ~RKMppDecoder(); 19 | 20 | bool Init(); 21 | 22 | void UnInit(); 23 | 24 | int SendInput(MppPacket packet); 25 | 26 | MppFrame FetchOutput(); 27 | 28 | int Flush(); 29 | 30 | private: 31 | MppCodingType coding_type_; 32 | size_t max_buffer_size_; 33 | MppCtx ctx_; 34 | MppApi *mpi_; 35 | MppBufferGroup frame_group_; 36 | DISALLOW_COPY_AND_ASSIGN(RKMppDecoder); 37 | }; 38 | } 39 | 40 | #endif //MEDIA_MPP_DECODER_H_ 41 | -------------------------------------------------------------------------------- /media/packet_queue.cc: -------------------------------------------------------------------------------- 1 | #include "media/packet_queue.h" 2 | #include "media/ffmpeg_common.h" 3 | #include "base/logging.h" 4 | 5 | namespace media { 6 | 7 | AVPacket PacketQueue::kFlushPkt = {0}; 8 | 9 | void PacketQueue::Init() { 10 | av_init_packet(&kFlushPkt); 11 | kFlushPkt.data = (uint8_t *) &kFlushPkt; 12 | } 13 | 14 | PacketQueue::PacketQueue() = default; 15 | 16 | PacketQueue::~PacketQueue() { 17 | flush(); 18 | } 19 | 20 | void PacketQueue::put(AVPacket *pkt) { 21 | base::AutoLock l(lock_); 22 | incoming_packets_.push(pkt); 23 | } 24 | 25 | AVPacket *PacketQueue::get() { 26 | base::AutoLock l(lock_); 27 | if (incoming_packets_.empty()) 28 | return nullptr; 29 | AVPacket *pkt = incoming_packets_.front(); 30 | incoming_packets_.pop(); 31 | DLOG(INFO) << "PacketQueue size: " << incoming_packets_.size(); 32 | return pkt; 33 | } 34 | 35 | void PacketQueue::flush() { 36 | base::AutoLock l(lock_); 37 | while (!incoming_packets_.empty()) { 38 | AVPacket *pkt = incoming_packets_.front(); 39 | incoming_packets_.pop(); 40 | if (pkt != &kFlushPkt) { 41 | av_packet_unref(pkt); 42 | av_packet_free(&pkt); 43 | } 44 | } 45 | incoming_packets_.push(&kFlushPkt); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /media/packet_queue.h: -------------------------------------------------------------------------------- 1 | #ifndef MEDIA_PACKET_QUEUE_H_ 2 | #define MEDIA_PACKET_QUEUE_H_ 3 | 4 | #include 5 | #include 6 | #include "base/macros.h" 7 | #include "base/time/time.h" 8 | #include "base/synchronization/lock.h" 9 | 10 | struct AVPacket; 11 | 12 | namespace media { 13 | 14 | class PacketQueue { 15 | public: 16 | static void Init(); 17 | 18 | PacketQueue(); 19 | 20 | virtual ~PacketQueue(); 21 | 22 | void put(AVPacket *pkt); 23 | 24 | AVPacket *get(); 25 | 26 | void flush(); 27 | 28 | public: 29 | // this is special packet to mark a flush is needed 30 | // mainly for future use of seeking a video file 31 | static AVPacket kFlushPkt; 32 | 33 | private: 34 | std::queue incoming_packets_; 35 | base::Lock lock_; 36 | DISALLOW_COPY_AND_ASSIGN(PacketQueue); 37 | }; 38 | } 39 | 40 | #endif //MEDIA_PACKET_QUEUE_H_ 41 | -------------------------------------------------------------------------------- /media/rga_utils.cc: -------------------------------------------------------------------------------- 1 | #include "media/rga_utils.h" 2 | 3 | namespace media { 4 | 5 | namespace { 6 | 7 | int rga_supported = 1; 8 | int rga_initialized = 0; 9 | 10 | int rgaPrepareInfo(void *buf, 11 | RgaSURF_FORMAT format, 12 | QRect rect, 13 | int sw, 14 | int sh, 15 | rga_info_t *info) { 16 | memset(info, 0, sizeof(rga_info_t)); 17 | info->fd = -1; 18 | info->virAddr = buf; 19 | info->mmuFlag = 1; 20 | return rga_set_rect(&info->rect, rect.x(), rect.y(), rect.width(), rect.height(), sw, sh, format); 21 | } 22 | } 23 | 24 | int rgaDrawImage(void *src, 25 | RgaSURF_FORMAT src_format, 26 | QRect srcRect, 27 | int src_sw, 28 | int src_sh, 29 | void *dst, 30 | RgaSURF_FORMAT dst_format, 31 | QRect dstRect, 32 | int dst_sw, 33 | int dst_sh, 34 | int rotate, 35 | unsigned int blend) { 36 | rga_info_t srcInfo; 37 | rga_info_t dstInfo; 38 | 39 | memset(&srcInfo, 0, sizeof(rga_info_t)); 40 | memset(&dstInfo, 0, sizeof(rga_info_t)); 41 | 42 | if (!rga_supported) 43 | return -1; 44 | 45 | if (!rga_initialized) { 46 | if (c_RkRgaInit() < 0) { 47 | rga_supported = 0; 48 | return -1; 49 | } 50 | rga_initialized = 1; 51 | } 52 | 53 | if (rgaPrepareInfo(src, src_format, srcRect, src_sw, src_sh, &srcInfo) < 0) 54 | return -1; 55 | 56 | if (rgaPrepareInfo(dst, dst_format, dstRect, dst_sw, dst_sh, &dstInfo) < 0) 57 | return -1; 58 | 59 | srcInfo.rotation = rotate; 60 | if (blend) srcInfo.blend = blend; 61 | 62 | return c_RkRgaBlit(&srcInfo, &dstInfo, nullptr); 63 | } 64 | 65 | RgaSURF_FORMAT mpp_format_to_rga_format(MppFrameFormat fmt) { 66 | if (fmt == MPP_FMT_YUV420P) 67 | return RK_FORMAT_YCbCr_420_P; //YUV420P 68 | if (fmt == MPP_FMT_YUV420SP) 69 | return RK_FORMAT_YCbCr_420_SP; //NV12 70 | if (fmt == MPP_FMT_YUV420SP_VU) 71 | return RK_FORMAT_YCrCb_420_SP; //NV21 72 | return RK_FORMAT_UNKNOWN; 73 | } 74 | } -------------------------------------------------------------------------------- /media/rga_utils.h: -------------------------------------------------------------------------------- 1 | #ifndef MEDIA_RGA_UTILS_H_ 2 | #define MEDIA_RGA_UTILS_H_ 3 | 4 | #include "base/macros.h" 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | namespace media { 12 | int rgaDrawImage(void *src, 13 | RgaSURF_FORMAT src_format, 14 | QRect srcRect, 15 | int src_sw, 16 | int src_sh, 17 | void *dst, 18 | RgaSURF_FORMAT dst_format, 19 | QRect dstRect, 20 | int dst_sw, 21 | int dst_sh, 22 | int rotate, 23 | unsigned int blend); 24 | 25 | RgaSURF_FORMAT mpp_format_to_rga_format(MppFrameFormat fmt); 26 | } 27 | #endif // MEDIA_RGA_UTILS_H_ 28 | -------------------------------------------------------------------------------- /media/video_decoder_thread.cc: -------------------------------------------------------------------------------- 1 | #include "base/logging.h" 2 | #include "media/video_decoder_thread.h" 3 | #include "media/mp4_dataset.h" 4 | #include "media/mpp_decoder.h" 5 | #include "media/video_frame_queue.h" 6 | #include "media/packet_queue.h" 7 | #include "media/video_player.h" 8 | 9 | namespace media { 10 | 11 | VideoDecoderThread::VideoDecoderThread(VideoPlayer *player, 12 | Mp4Dataset *dataset, 13 | PacketQueue *input_queue, 14 | VideoFrameQueue *output_queue) 15 | : player_(player), 16 | dataset_(dataset), 17 | input_queue_(input_queue), 18 | output_queue_(output_queue), 19 | avbsf_(nullptr), 20 | next_pts_(0), 21 | keep_running_(true), 22 | thread_(new base::DelegateSimpleThread(this, "VDThread")) { 23 | thread_->Start(); 24 | } 25 | 26 | VideoDecoderThread::~VideoDecoderThread() { 27 | keep_running_ = false; 28 | if (thread_) { 29 | thread_->Join(); 30 | thread_.reset(); 31 | } 32 | } 33 | 34 | void VideoDecoderThread::Run() { 35 | InitDecoder(); 36 | DecodeLoop(); 37 | UnInitDecoder(); 38 | } 39 | 40 | void VideoDecoderThread::InitDecoder() { 41 | AVStream *stream = dataset_->getVideoStream(); 42 | 43 | MppCodingType coding_type = MPP_VIDEO_CodingUnused; 44 | if (stream->codecpar->codec_id == AV_CODEC_ID_HEVC) { 45 | coding_type = MPP_VIDEO_CodingHEVC; 46 | } else if (stream->codecpar->codec_id == AV_CODEC_ID_H264) { 47 | coding_type = MPP_VIDEO_CodingAVC; 48 | } else { 49 | player_->OnMediaError(Error_VideoCodecUnsupported); 50 | return; 51 | } 52 | 53 | size_t buffer_size = output_queue_->max_size(); 54 | if (buffer_size < FRAMEGROUP_MAX_FRAMES) { 55 | buffer_size = FRAMEGROUP_MAX_FRAMES; 56 | } 57 | std::unique_ptr decoder(new RKMppDecoder(coding_type, buffer_size + 2)); 58 | if (decoder->Init()) { 59 | decoder_ = std::move(decoder); 60 | } else { 61 | LOG(ERROR) << "create video decoder failed"; 62 | player_->OnMediaError(Error_VideoCodecCreateFailed); 63 | } 64 | std::string bsf_name; 65 | if (stream->codecpar->codec_id == AV_CODEC_ID_H264) { 66 | bsf_name = "h264_mp4toannexb"; 67 | } else if (stream->codecpar->codec_id == AV_CODEC_ID_HEVC) { 68 | bsf_name = "hevc_mp4toannexb"; 69 | } 70 | if (!bsf_name.empty()) { 71 | const struct AVBitStreamFilter *bsfptr = av_bsf_get_by_name(bsf_name.c_str()); 72 | av_bsf_alloc(bsfptr, &avbsf_); 73 | avcodec_parameters_copy(avbsf_->par_in, stream->codecpar); 74 | av_bsf_init(avbsf_); 75 | } 76 | 77 | AVRational frame_rate = av_guess_frame_rate(dataset_->getFormatContext(), stream, nullptr); 78 | double fps = frame_rate.num && frame_rate.den ? av_q2d(frame_rate) : 30.0f; 79 | frame_duration_ = base::TimeDelta::FromSecondsD(1.0 / fps); 80 | } 81 | 82 | void VideoDecoderThread::UnInitDecoder() { 83 | output_queue_->flush(); 84 | decoder_.reset(); 85 | if (avbsf_) { 86 | av_bsf_free(&avbsf_); 87 | avbsf_ = nullptr; 88 | } 89 | } 90 | 91 | void VideoDecoderThread::DecodeLoop() { 92 | while (keep_running_) { 93 | AVPacket *pkt = FetchPacket(); 94 | 95 | bool eos_reached = false; 96 | 97 | if (pkt) { 98 | if (pkt->data == PacketQueue::kFlushPkt.data) { 99 | DLOG(INFO) << "Got video flush packet"; 100 | //flush decoder 101 | if (decoder_) { 102 | decoder_->Flush(); 103 | } 104 | output_queue_->flush(); 105 | next_pts_ = 0; 106 | player_->OnFlushCompleted(dataset_->getVideoStreamIndex()); 107 | continue; 108 | } 109 | 110 | if (!pkt->data) { 111 | //读到文件末尾了,我们需要将 end of stream packet 写入解码器 112 | //并等待解码器输出 eos帧,则说明解码器已经输出所有的帧,此刻可以正常关闭解码器 113 | DLOG(INFO) << "Got video EOS packet"; 114 | SendInput(pkt, &eos_reached); 115 | } else { 116 | if (avbsf_) { 117 | //H264,H265要处理之后才能送到解码器 118 | int err = av_bsf_send_packet(avbsf_, pkt); 119 | if (err < 0) { 120 | av_packet_unref(pkt); 121 | av_packet_free(&pkt); 122 | continue; 123 | } 124 | while ((err = av_bsf_receive_packet(avbsf_, pkt)) == 0) { 125 | SendInput(pkt, &eos_reached); 126 | } 127 | } else { 128 | SendInput(pkt, &eos_reached); 129 | } 130 | } 131 | } else { 132 | if (!ProcessOneOutputBuffer(&eos_reached)) { 133 | usleep(5000); 134 | } 135 | } 136 | if (eos_reached) { 137 | decoder_->Flush(); 138 | } 139 | } 140 | } 141 | 142 | void VideoDecoderThread::SendInput(AVPacket *pkt, bool *eos_reached) { 143 | MppPacket mpp_packet = MakeMppPacket(pkt); 144 | if (mpp_packet) { 145 | DecodePacket(mpp_packet, eos_reached); 146 | mpp_packet_deinit(&mpp_packet); 147 | } 148 | av_packet_unref(pkt); 149 | av_packet_free(&pkt); 150 | } 151 | 152 | AVPacket *VideoDecoderThread::FetchPacket() { 153 | AVPacket *pkt = input_queue_->get(); 154 | while (!pkt) { 155 | DemuxResult result = dataset_->demuxNextPacket(); 156 | if (result != DemuxResult::OK) 157 | break; //AV_EOF or UNKNOWN 158 | pkt = input_queue_->get(); 159 | } 160 | return pkt; 161 | } 162 | 163 | bool VideoDecoderThread::ProcessOneOutputBuffer(bool *eos_reached) { 164 | if (!decoder_) 165 | return false; 166 | 167 | MppFrame frame = decoder_->FetchOutput(); 168 | if (!frame) 169 | return false; 170 | 171 | if (mpp_frame_get_eos(frame)) { 172 | *eos_reached = true; 173 | DLOG(INFO) << "Received a EOS frame"; 174 | } 175 | if (!SendFrame(frame)) { 176 | mpp_frame_deinit(&frame); 177 | } 178 | return true; 179 | } 180 | 181 | bool VideoDecoderThread::DecodePacket(MppPacket mpp_packet, bool *eos_reached) { 182 | if (!decoder_) 183 | return false; 184 | 185 | bool sent_packet = false, frames_remaining = true; 186 | while (!sent_packet || frames_remaining) { 187 | if (!sent_packet) { 188 | const int result = decoder_->SendInput(mpp_packet); 189 | if (result < 0 && result != MPP_ERR_BUFFER_FULL) { 190 | return false; 191 | } 192 | sent_packet = (result != MPP_ERR_BUFFER_FULL); 193 | } 194 | 195 | MppFrame frame = decoder_->FetchOutput(); 196 | if (!frame) { 197 | frames_remaining = false; 198 | continue; 199 | } 200 | 201 | if (mpp_frame_get_eos(frame)) { 202 | *eos_reached = true; 203 | DLOG(INFO) << "Received a EOS frame"; 204 | } 205 | if (!SendFrame(frame)) { 206 | mpp_frame_deinit(&frame); 207 | } 208 | } 209 | return true; 210 | } 211 | 212 | bool VideoDecoderThread::SendFrame(MppFrame frame) { 213 | int64_t pts = mpp_frame_get_pts(frame); 214 | if (pts == static_cast(AV_NOPTS_VALUE)) { 215 | pts = next_pts_; 216 | mpp_frame_set_pts(frame, pts); 217 | } 218 | if (pts != static_cast(AV_NOPTS_VALUE)) { 219 | next_pts_ = pts + frame_duration_.InMicroseconds(); 220 | } 221 | 222 | while (keep_running_) { 223 | if (!output_queue_->is_writable()) { 224 | usleep(5000); 225 | continue; 226 | } 227 | output_queue_->put(frame); 228 | return true; 229 | } 230 | return false; 231 | } 232 | 233 | MppPacket VideoDecoderThread::MakeMppPacket(AVPacket *packet) { 234 | MppPacket mpp_packet = nullptr; 235 | MPP_RET ret = mpp_packet_init(&mpp_packet, packet->data, packet->size); 236 | if (ret != MPP_OK) { 237 | LOG(ERROR) << "mpp_packet_init failed, ret: " << ret; 238 | return nullptr; 239 | } 240 | 241 | if (packet->dts != static_cast(AV_NOPTS_VALUE)) { 242 | auto dts = media::ConvertFromTimeBase(dataset_->getVideoStream()->time_base, packet->dts); 243 | mpp_packet_set_dts(mpp_packet, dts.InMicroseconds()); 244 | } else { 245 | mpp_packet_set_dts(mpp_packet, packet->dts); 246 | } 247 | if (packet->pts != static_cast(AV_NOPTS_VALUE)) { 248 | auto pts = media::ConvertFromTimeBase(dataset_->getVideoStream()->time_base, packet->pts); 249 | mpp_packet_set_pts(mpp_packet, pts.InMicroseconds()); 250 | } else { 251 | mpp_packet_set_pts(mpp_packet, packet->pts); 252 | } 253 | 254 | if (!packet->data) { 255 | mpp_packet_set_eos(mpp_packet); 256 | DLOG(INFO) << "Send EOS to decoder"; 257 | } 258 | return mpp_packet; 259 | } 260 | } -------------------------------------------------------------------------------- /media/video_decoder_thread.h: -------------------------------------------------------------------------------- 1 | #ifndef MEDIA_VIDEO_DECODER_THREAD_H_ 2 | #define MEDIA_VIDEO_DECODER_THREAD_H_ 3 | 4 | #include 5 | #include "base/macros.h" 6 | #include "base/threading/simple_thread.h" 7 | #include "base/synchronization/lock.h" 8 | #include "media/ffmpeg_common.h" 9 | #include 10 | #include 11 | 12 | namespace media { 13 | class Mp4Dataset; 14 | class PacketQueue; 15 | class VideoFrameQueue; 16 | class RKMppDecoder; 17 | class VideoPlayer; 18 | 19 | class VideoDecoderThread 20 | : public base::DelegateSimpleThread::Delegate { 21 | public: 22 | explicit VideoDecoderThread(VideoPlayer *player, 23 | Mp4Dataset *dataset, 24 | PacketQueue *input_queue, 25 | VideoFrameQueue *output_queue); 26 | 27 | virtual ~VideoDecoderThread() override; 28 | 29 | private: 30 | void Run() override; 31 | 32 | void InitDecoder(); 33 | 34 | void UnInitDecoder(); 35 | 36 | void DecodeLoop(); 37 | 38 | AVPacket *FetchPacket(); 39 | 40 | bool ProcessOneOutputBuffer(bool *eos_reached); 41 | 42 | bool DecodePacket(MppPacket mpp_packet, bool *eos_reached); 43 | 44 | MppPacket MakeMppPacket(AVPacket *packet); 45 | 46 | bool SendFrame(MppFrame frame); 47 | 48 | void SendInput(AVPacket *pkt, bool *eos_reached); 49 | 50 | VideoPlayer *player_; 51 | Mp4Dataset *dataset_; 52 | PacketQueue *input_queue_; 53 | VideoFrameQueue *output_queue_; 54 | AVBSFContext *avbsf_; 55 | int64_t next_pts_; 56 | base::TimeDelta frame_duration_; 57 | bool keep_running_; 58 | std::unique_ptr decoder_; 59 | std::unique_ptr thread_; 60 | DISALLOW_COPY_AND_ASSIGN(VideoDecoderThread); 61 | }; 62 | } 63 | #endif // MEDIA_VIDEO_DECODER_THREAD_H_ 64 | -------------------------------------------------------------------------------- /media/video_frame_queue.cc: -------------------------------------------------------------------------------- 1 | #include "base/logging.h" 2 | #include "media/video_frame_queue.h" 3 | #include "media/ffmpeg_common.h" 4 | #include "base/logging.h" 5 | 6 | namespace media { 7 | VideoFrameQueue::VideoFrameQueue(AVStream *stream, size_t max_size) 8 | : stream_(stream), 9 | max_size_(max_size) {} 10 | 11 | VideoFrameQueue::~VideoFrameQueue() { 12 | flush(); 13 | } 14 | 15 | bool VideoFrameQueue::is_writable() { 16 | base::AutoLock l(lock_); 17 | return frame_list_.size() < max_size_; 18 | } 19 | 20 | int64_t VideoFrameQueue::startTimestamp() { 21 | base::AutoLock l(lock_); 22 | if (frame_list_.empty()) 23 | return AV_NOPTS_VALUE; 24 | return frame_list_.begin()->first; 25 | } 26 | 27 | void VideoFrameQueue::put(MppFrame frame) { 28 | base::AutoLock l(lock_); 29 | int64_t pts = mpp_frame_get_pts(frame); 30 | auto iter = frame_list_.find(pts); 31 | if (iter != frame_list_.end()) { 32 | //可能存在 PTS 重复,我们把早期的销毁,保存后来的帧 33 | mpp_frame_deinit(&iter->second); 34 | frame_list_.erase(iter); 35 | } 36 | frame_list_.insert(std::make_pair(pts, frame)); 37 | } 38 | 39 | MppFrame VideoFrameQueue::get(int64_t render_time) { 40 | base::AutoLock l(lock_); 41 | if (frame_list_.empty()) 42 | return nullptr; 43 | 44 | if (frame_list_.begin()->first <= render_time) { 45 | MppFrame frame = frame_list_.begin()->second; 46 | frame_list_.erase(frame_list_.begin()); 47 | DLOG(INFO) << "VideoFrameQueue size: " << frame_list_.size(); 48 | return frame; 49 | } 50 | if (frame_list_.size() == 1) { 51 | MppFrame frame = frame_list_.begin()->second; 52 | if (mpp_frame_get_eos(frame)) { 53 | frame_list_.clear(); 54 | return frame; 55 | } 56 | } 57 | return nullptr; 58 | } 59 | 60 | void VideoFrameQueue::flush() { 61 | base::AutoLock l(lock_); 62 | for (auto &i : frame_list_) { 63 | mpp_frame_deinit(&i.second); 64 | } 65 | frame_list_.clear(); 66 | } 67 | 68 | size_t VideoFrameQueue::size() { 69 | base::AutoLock l(lock_); 70 | return frame_list_.size(); 71 | } 72 | 73 | size_t VideoFrameQueue::max_size() const { 74 | return max_size_; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /media/video_frame_queue.h: -------------------------------------------------------------------------------- 1 | #ifndef MEDIA_VIDEO_FRAME_QUEUE_H_ 2 | #define MEDIA_VIDEO_FRAME_QUEUE_H_ 3 | 4 | #include 5 | #include "base/macros.h" 6 | #include "base/synchronization/lock.h" 7 | #include "media/media_constants.h" 8 | #include 9 | 10 | struct AVStream; 11 | 12 | namespace media { 13 | 14 | /* 15 | * 因为可能存在B帧,解码器出来的帧顺序不是真正的显示顺序,所以, 16 | * 我们要按照 PTS 进行排序,std::map可以对主键自动排序 17 | * 正是我们所需 18 | */ 19 | class VideoFrameQueue { 20 | public: 21 | explicit VideoFrameQueue(AVStream *stream, size_t max_size); 22 | 23 | virtual ~VideoFrameQueue(); 24 | 25 | bool is_writable(); 26 | 27 | int64_t startTimestamp(); 28 | 29 | void put(MppFrame frame); 30 | 31 | MppFrame get(int64_t render_time); 32 | 33 | void flush(); 34 | 35 | size_t size(); 36 | 37 | size_t max_size() const; 38 | 39 | private: 40 | AVStream *stream_; 41 | size_t max_size_; 42 | std::map frame_list_; 43 | base::Lock lock_; 44 | DISALLOW_COPY_AND_ASSIGN(VideoFrameQueue); 45 | }; 46 | } 47 | 48 | #endif //MEDIA_VIDEO_FRAME_QUEUE_H_ 49 | -------------------------------------------------------------------------------- /media/video_player.cc: -------------------------------------------------------------------------------- 1 | #include "base/logging.h" 2 | #include "media/video_player.h" 3 | #include "media/mp4_dataset.h" 4 | #include "media/audio_render.h" 5 | #include "media/mpp_decoder.h" 6 | #include "media/audio_frame_queue.h" 7 | #include "media/video_frame_queue.h" 8 | #include "media/packet_queue.h" 9 | #include "media/audio_decoder_thread.h" 10 | #include "media/video_decoder_thread.h" 11 | #include "media/media_constants.h" 12 | #include 13 | 14 | namespace media { 15 | 16 | VideoPlayer::VideoPlayer(Delegate *delegate, 17 | Mp4Dataset *dataset, 18 | bool enable_audio, 19 | int volume, 20 | bool loop, 21 | double buffer_time) 22 | : delegate_(delegate), 23 | dataset_(dataset), 24 | enable_audio_(enable_audio), 25 | volume_(volume), 26 | loop_(loop), 27 | buffer_time_(buffer_time), 28 | mute_(false), 29 | thread_(new base::Thread("VideoPlayer")) { 30 | if (buffer_time_ < 0.2) buffer_time_ = 0.2; 31 | base::SimpleThread::Options options; 32 | options.set_priority(base::ThreadPriority::REALTIME_AUDIO); 33 | thread_->StartWithOptions(options); 34 | thread_->PostTask(std::bind(&VideoPlayer::OnStart, this)); 35 | } 36 | 37 | VideoPlayer::~VideoPlayer() { 38 | if (thread_) { 39 | thread_->PostTask(std::bind(&VideoPlayer::OnStop, this)); 40 | thread_->Stop(); 41 | thread_.reset(); 42 | } 43 | } 44 | 45 | void VideoPlayer::Seek(double timestamp) { 46 | if (!thread_->IsCurrent()) { 47 | thread_->PostTask(std::bind(&VideoPlayer::Seek, this, timestamp)); 48 | } else { 49 | int err = dataset_->seek(timestamp); 50 | if (err >= 0) { 51 | /* 52 | * seek flow: 53 | * 1)stop render timer 54 | * 2)clear all output buffer 55 | * 3)add stream pending counter 56 | * 4)waiting decoder thread flush decoder 57 | * 5)restart render 58 | */ 59 | io_timer_->Stop(); 60 | 61 | if (video_output_queue_) { 62 | //flush很重要,万一decoder thread 阻塞,清空缓冲区就可以立即唤醒 63 | video_output_queue_->flush(); 64 | ++render_state_.stream_seek_pending; 65 | } 66 | if (audio_output_queue_) { 67 | //flush很重要,万一decoder thread 阻塞,清空缓冲区就可以立即唤醒 68 | audio_output_queue_->flush(); 69 | ++render_state_.stream_seek_pending; 70 | } 71 | } else { 72 | OnMediaError(Error_SeekFailed); 73 | } 74 | } 75 | } 76 | 77 | void VideoPlayer::SetVolume(int volume) { 78 | if (!thread_->IsCurrent()) { 79 | thread_->PostTask(std::bind(&VideoPlayer::SetVolume, this, volume)); 80 | } else { 81 | if (audio_render_) { 82 | audio_render_->SetVolume(volume); 83 | } 84 | } 85 | } 86 | 87 | void VideoPlayer::Mute(bool enable) { 88 | if (!thread_->IsCurrent()) { 89 | thread_->PostTask(std::bind(&VideoPlayer::Mute, this, enable)); 90 | } else { 91 | mute_ = enable; 92 | } 93 | } 94 | 95 | void VideoPlayer::Pause() { 96 | if (!thread_->IsCurrent()) { 97 | thread_->PostTask(std::bind(&VideoPlayer::Pause, this)); 98 | } else { 99 | io_timer_->Stop(); 100 | } 101 | } 102 | 103 | void VideoPlayer::Resume() { 104 | if (!thread_->IsCurrent()) { 105 | thread_->PostTask(std::bind(&VideoPlayer::Resume, this)); 106 | } else { 107 | /* 108 | * 这里需要注意,因为我们采用当前时间来修正定时器的误差,所以,在resume之后,基准时间已经不准了 109 | * 所以,需要修正: 用当前时间减去已经播放的时间 110 | */ 111 | render_state_.BasetimeCalibration(); 112 | OnRender(); 113 | } 114 | } 115 | 116 | void VideoPlayer::OnStart() { 117 | InitVideo(); 118 | InitAudio(); 119 | InitAudioRender(); 120 | io_timer_.reset(new base::Timer(false)); 121 | ManageTimer(base::TimeDelta::FromMicroseconds(kRenderPollDelay)); 122 | } 123 | 124 | void VideoPlayer::OnStop() { 125 | io_timer_.reset(); 126 | //在销毁解码线程之前,先让UI线程释放 mppframe,否则会导致RK解码器异常 127 | delegate_->OnMediaFrameArrival(nullptr); 128 | 129 | video_decoder_thread_.reset(); 130 | audio_decoder_thread_.reset(); 131 | audio_render_.reset(); 132 | audio_input_queue_.reset(); 133 | video_input_queue_.reset(); 134 | video_output_queue_.reset(); 135 | audio_output_queue_.reset(); 136 | delegate_->OnMediaStop(); 137 | } 138 | 139 | void VideoPlayer::InitAudio() { 140 | AVStream *stream = dataset_->getAudioStream(); 141 | 142 | if (!enable_audio_ || !stream) { 143 | LOG(WARNING) << "No audio to play or disable play audio"; 144 | return; 145 | } 146 | 147 | //根据帧时长估计缓冲区大小 148 | base::TimeDelta duration = media::ConvertFromTimeBase(stream->time_base, stream->codecpar->frame_size); 149 | int count = static_cast(buffer_time_ / duration.InSecondsF()) + 1; 150 | LOG(INFO) << "audio max buffer count:" << count; 151 | audio_output_queue_ = base::WrapUnique(new AudioFrameQueue(stream, count)); 152 | audio_input_queue_ = base::WrapUnique(new PacketQueue()); 153 | dataset_->setAudioPacketQueue(audio_input_queue_.get()); 154 | audio_decoder_thread_ = base::WrapUnique(new AudioDecoderThread(this, 155 | dataset_, 156 | audio_input_queue_.get(), 157 | audio_output_queue_.get())); 158 | } 159 | 160 | void VideoPlayer::InitVideo() { 161 | AVStream *stream = dataset_->getVideoStream(); 162 | AVRational frame_rate = av_guess_frame_rate(dataset_->getFormatContext(), stream, nullptr); 163 | double fps = frame_rate.num && frame_rate.den ? av_q2d(frame_rate) : 30.0f; 164 | int count = static_cast(fps * buffer_time_); 165 | LOG(INFO) << "video max buffer count:" << count; 166 | video_output_queue_ = base::WrapUnique(new VideoFrameQueue(stream, count)); 167 | video_input_queue_ = base::WrapUnique(new PacketQueue()); 168 | dataset_->setVideoPacketQueue(video_input_queue_.get()); 169 | video_decoder_thread_ = base::WrapUnique(new VideoDecoderThread(this, 170 | dataset_, 171 | video_input_queue_.get(), 172 | video_output_queue_.get())); 173 | } 174 | 175 | void VideoPlayer::InitAudioRender() { 176 | if (!audio_output_queue_) 177 | return; 178 | 179 | std::unique_ptr audio_render; 180 | auto stream = dataset_->getAudioStream(); 181 | int sample_rate = stream->codecpar->sample_rate; 182 | int64_t frame_size = stream->codecpar->frame_size; 183 | if (frame_size < 1) frame_size = 1024; 184 | std::unique_ptr render = base::WrapUnique(new RKAudioRender()); 185 | if (!render->Init("default", 186 | sample_rate, 187 | kAudioChannels, 188 | frame_size * kAudioChannels)) { 189 | render.reset(); 190 | return; 191 | } 192 | if (volume_ >= 0) { 193 | render->SetVolume(volume_); 194 | } 195 | audio_render_ = std::move(render); 196 | } 197 | 198 | void VideoPlayer::OnRender() { 199 | if (!render_state_.started) { 200 | //我们要缓冲指定时间的视频帧,一是为了后面播放更为流畅,二是如果存在B帧,需要缓冲排序 201 | if (video_output_queue_->is_writable()) { 202 | ManageTimer(base::TimeDelta::FromMicroseconds(kRenderPollDelay)); 203 | return; 204 | } 205 | render_state_.started = true; 206 | DLOG(INFO) << "render started"; 207 | } 208 | 209 | if (render_state_.render_time == AV_NOPTS_VALUE) { 210 | /* 211 | * 正常开始的播放,或者重新播放,或者 seek 之后播放 212 | * 我们要重新初始化播放状态 213 | * 我们要根据视频缓冲区第一帧的时间戳来作为当前播放时间戳,在seek之后,第一帧时间戳不一定为 0 214 | * 我们播放时间戳要比第一帧时间戳略大 215 | */ 216 | int64_t timestamp = video_output_queue_->startTimestamp(); 217 | int64_t remainder = timestamp % kRenderPollDelay; 218 | //转换为 kRenderPollDelay 的整数倍 219 | render_state_.render_time = (timestamp / kRenderPollDelay) * kRenderPollDelay; 220 | if (remainder > 0) render_state_.render_time += kRenderPollDelay; 221 | //重新校准基准时间: 当前时间 - 已经播放的时间 (这里指 seek 之后的时间) 222 | render_state_.BasetimeCalibration(); 223 | DLOG(INFO) << "render timer reset"; 224 | } 225 | 226 | if (audio_render_) { 227 | while (true) { 228 | MEDIA_BUFFER audio_buffer = audio_output_queue_->get(render_state_.render_time); 229 | if (!audio_buffer) 230 | break; 231 | DLOG(INFO) << "Render Audio frame PTS:" << RK_MPI_MB_GetTimestamp(audio_buffer); 232 | if (mute_) { 233 | memset(RK_MPI_MB_GetPtr(audio_buffer), 0, RK_MPI_MB_GetSize(audio_buffer)); 234 | } 235 | audio_render_->SendInput(audio_buffer); 236 | RK_MPI_MB_ReleaseBuffer(audio_buffer); 237 | } 238 | } 239 | 240 | bool eos_reached = false; 241 | 242 | while (true) { 243 | MppFrame video_frame = video_output_queue_->get(render_state_.render_time); 244 | if (!video_frame) 245 | break; 246 | int64_t pts = mpp_frame_get_pts(video_frame); 247 | DLOG(INFO) << "Render Video frame PTS:" << pts; 248 | if (mpp_frame_get_eos(video_frame)) { 249 | eos_reached = true; 250 | mpp_frame_deinit(&video_frame); 251 | } else { 252 | delegate_->OnMediaFrameArrival(video_frame); 253 | } 254 | } 255 | //next render time 256 | render_state_.render_time += kRenderPollDelay; 257 | 258 | if (!eos_reached) { 259 | //这里要对定时器进行误差修正,尽力保证实际间隔在 kRenderPollDelay 260 | base::TimeTicks 261 | expire_time = render_state_.base_time + base::TimeDelta::FromMicroseconds(render_state_.render_time); 262 | base::TimeTicks now = base::TimeTicks::Now(); 263 | if (expire_time > now) { 264 | base::TimeDelta delay = expire_time - now; 265 | DLOG(INFO) << "render delay: " << delay.InMicroseconds(); 266 | ManageTimer(delay); 267 | } else { 268 | ManageTimer(base::TimeDelta::FromMicroseconds(1000)); 269 | } 270 | } else { 271 | //播放完成 272 | render_state_.Reset(); 273 | RenderCompleted(); 274 | if (!loop_) { 275 | delegate_->OnMediaStop(); 276 | } else { 277 | RewindRender(); 278 | } 279 | } 280 | } 281 | 282 | void VideoPlayer::RenderCompleted() { 283 | if (audio_input_queue_) { 284 | audio_input_queue_->flush(); 285 | } 286 | if (video_input_queue_) { 287 | video_input_queue_->flush(); 288 | } 289 | if (audio_output_queue_) { 290 | audio_output_queue_->flush(); 291 | } 292 | if (video_output_queue_) { 293 | video_output_queue_->flush(); 294 | } 295 | } 296 | 297 | void VideoPlayer::RewindRender() { 298 | dataset_->rewind(); 299 | ManageTimer(base::TimeDelta::FromMicroseconds(kRenderPollDelay)); 300 | } 301 | 302 | void VideoPlayer::ManageTimer(const base::TimeDelta &delay) { 303 | io_timer_->Start(std::bind(&VideoPlayer::OnRender, this), delay); 304 | } 305 | 306 | void VideoPlayer::OnFlushCompleted(int stream_idx) { 307 | if (!thread_->IsCurrent()) { 308 | thread_->PostTask(std::bind(&VideoPlayer::OnFlushCompleted, this, stream_idx)); 309 | } else { 310 | if (render_state_.stream_seek_pending < 1) 311 | return; //当前没有seek 312 | 313 | --render_state_.stream_seek_pending; 314 | if (render_state_.stream_seek_pending == 0) { 315 | render_state_.Reset(); 316 | OnRender(); 317 | } 318 | } 319 | } 320 | 321 | void VideoPlayer::OnMediaError(int err) { 322 | delegate_->OnMediaError(err); 323 | } 324 | } -------------------------------------------------------------------------------- /media/video_player.h: -------------------------------------------------------------------------------- 1 | #ifndef MEDIA_VIDEO_PLAYER_H_ 2 | #define MEDIA_VIDEO_PLAYER_H_ 3 | 4 | #include 5 | #include "base/macros.h" 6 | #include "base/threading/thread.h" 7 | #include "base/synchronization/lock.h" 8 | #include "base/timer/timer.h" 9 | #include "base/threading/thread.h" 10 | #include "base/timer/timer.h" 11 | #include "media/ffmpeg_common.h" 12 | #include 13 | #include 14 | 15 | namespace media { 16 | class Mp4Dataset; 17 | class PacketQueue; 18 | class VideoFrameQueue; 19 | class AudioFrameQueue; 20 | class RKAudioRender; 21 | class AudioDecoderThread; 22 | class VideoDecoderThread; 23 | 24 | enum MediaError { 25 | Error_VideoCodecUnsupported, 26 | Error_VideoCodecCreateFailed, 27 | Error_AudioResamplerCreateFailed, 28 | Error_AudioCodecCreateFailed, 29 | Error_SeekFailed 30 | }; 31 | 32 | class VideoPlayer{ 33 | public: 34 | class Delegate { 35 | public: 36 | virtual ~Delegate() {} 37 | virtual void OnMediaError(int err) = 0; 38 | virtual void OnMediaStop() = 0; 39 | virtual void OnMediaFrameArrival(MppFrame mb) = 0; 40 | protected: 41 | Delegate() {} 42 | private: 43 | DISALLOW_COPY_AND_ASSIGN(Delegate); 44 | }; 45 | explicit VideoPlayer(Delegate *delegate, 46 | Mp4Dataset *dataset, 47 | bool enable_audio, 48 | int volume, 49 | bool loop, 50 | double buffer_time); 51 | 52 | virtual ~VideoPlayer(); 53 | 54 | void Seek(double timestamp); 55 | 56 | void SetVolume(int volume); 57 | 58 | void Mute(bool enable); 59 | 60 | void Pause(); 61 | 62 | void Resume(); 63 | 64 | protected: 65 | Delegate *delegate() { 66 | return delegate_; 67 | } 68 | 69 | void OnFlushCompleted(int stream_idx); 70 | 71 | void OnMediaError(int err); 72 | 73 | private: 74 | friend class VideoDecoderThread; 75 | friend class AudioDecoderThread; 76 | 77 | void OnStart(); 78 | 79 | void OnStop(); 80 | 81 | void InitAudio(); 82 | 83 | void InitVideo(); 84 | 85 | void InitAudioRender(); 86 | 87 | void OnRender(); 88 | 89 | void ManageTimer(const base::TimeDelta &delay); 90 | 91 | void RenderCompleted(); 92 | 93 | void RewindRender(); 94 | 95 | Delegate *delegate_; 96 | 97 | Mp4Dataset *dataset_; 98 | 99 | bool enable_audio_; 100 | 101 | int volume_; 102 | 103 | bool loop_; 104 | 105 | double buffer_time_; 106 | 107 | bool mute_; 108 | 109 | struct RenderState { 110 | bool started; 111 | int64_t render_time; 112 | base::TimeTicks base_time; //基准时间,用来消除定时器误差 113 | int stream_seek_pending; 114 | RenderState() { 115 | Reset(); 116 | } 117 | void Reset() { 118 | started = false; 119 | render_time = AV_NOPTS_VALUE; 120 | base_time = base::TimeTicks(); 121 | stream_seek_pending = 0; 122 | } 123 | 124 | void BasetimeCalibration() { 125 | base_time = base::TimeTicks::Now() - base::TimeDelta::FromMicroseconds(render_time); 126 | } 127 | }; 128 | 129 | RenderState render_state_; 130 | 131 | std::unique_ptr io_timer_; 132 | 133 | std::unique_ptr audio_decoder_thread_; 134 | 135 | std::unique_ptr video_decoder_thread_; 136 | 137 | std::unique_ptr audio_render_; 138 | 139 | std::unique_ptr video_input_queue_; 140 | 141 | std::unique_ptr audio_input_queue_; 142 | 143 | std::unique_ptr video_output_queue_; 144 | 145 | std::unique_ptr audio_output_queue_; 146 | 147 | std::unique_ptr thread_; 148 | DISALLOW_COPY_AND_ASSIGN(VideoPlayer); 149 | }; 150 | } 151 | #endif // MEDIA_VIDEO_PLAYER_H_ 152 | -------------------------------------------------------------------------------- /ui/main_window.cc: -------------------------------------------------------------------------------- 1 | #include "ui/main_window.h" 2 | #include "ui/video_view.h" 3 | #include 4 | 5 | namespace ui { 6 | static void setButtonFormat(QBoxLayout *layout, QPushButton *btn, QRect rect) { 7 | btn->setFixedSize(rect.width() / 5, rect.width() / 10); 8 | btn->setStyleSheet("QPushButton{font-size:30px}"); 9 | layout->addWidget(btn); 10 | } 11 | 12 | MainWindow::MainWindow(QWidget *parent) : 13 | QGraphicsView(parent), 14 | video_view_(nullptr), 15 | play_button_(nullptr), 16 | stop_button_(nullptr), 17 | pause_button_(nullptr), 18 | resume_button_(nullptr), 19 | seek_button_(nullptr), 20 | control_Widget_(nullptr) { 21 | this->setStyleSheet("background: transparent"); 22 | this->setAttribute(Qt::WA_DeleteOnClose, true); 23 | desktop_rect_ = QApplication::desktop()->availableGeometry(); 24 | resize(desktop_rect_.width(), desktop_rect_.height()); 25 | setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); 26 | setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); 27 | 28 | auto scene = new QGraphicsScene(this); 29 | scene->setItemIndexMethod(QGraphicsScene::NoIndex); 30 | video_view_ = new VideoView(QRect(0, 0, 800, 480), nullptr, this); 31 | video_view_->setVisible(true); 32 | video_view_->setZValue(0); 33 | scene->addItem(video_view_); 34 | 35 | auto hLayout = new QHBoxLayout; 36 | hLayout->setMargin(0); 37 | hLayout->setSpacing(0); 38 | 39 | play_button_ = new QPushButton(tr("play")); 40 | setButtonFormat(hLayout, play_button_, desktop_rect_); 41 | stop_button_ = new QPushButton(tr("stop")); 42 | setButtonFormat(hLayout, stop_button_, desktop_rect_); 43 | 44 | pause_button_ = new QPushButton(tr("pause")); 45 | setButtonFormat(hLayout, pause_button_, desktop_rect_); 46 | 47 | resume_button_ = new QPushButton(tr("resume")); 48 | setButtonFormat(hLayout, resume_button_, desktop_rect_); 49 | 50 | seek_button_ = new QPushButton(tr("seek")); 51 | setButtonFormat(hLayout, seek_button_, desktop_rect_); 52 | 53 | control_Widget_ = new QWidget(); 54 | control_Widget_->setLayout(hLayout); 55 | 56 | control_Widget_->setWindowOpacity(0.8); 57 | control_Widget_->setGeometry(0, desktop_rect_.height() / 2, 0, desktop_rect_.width() / 10); 58 | 59 | scene->addWidget(control_Widget_); 60 | scene->setSceneRect(scene->itemsBoundingRect()); 61 | setScene(scene); 62 | 63 | iniSignalSlots(); 64 | } 65 | 66 | MainWindow::~MainWindow() { 67 | video_view_ = nullptr; 68 | } 69 | 70 | void MainWindow::iniSignalSlots() { 71 | connect(play_button_, SIGNAL(clicked()), this, SLOT(onStart())); 72 | connect(stop_button_, SIGNAL(clicked()), this, SLOT(onStop())); 73 | connect(pause_button_, SIGNAL(clicked()), this, SLOT(onPause())); 74 | connect(resume_button_, SIGNAL(clicked()), this, SLOT(onResume())); 75 | connect(seek_button_, SIGNAL(clicked()), this, SLOT(onSeek())); 76 | } 77 | 78 | void MainWindow::onStart() { 79 | video_view_->start("/data/jingxi/media/upload.mp4", true, 20, true); 80 | } 81 | 82 | void MainWindow::onStop() { 83 | video_view_->stop(); 84 | } 85 | 86 | void MainWindow::onPause() { 87 | video_view_->pause(); 88 | } 89 | 90 | void MainWindow::onResume() { 91 | video_view_->resume(); 92 | } 93 | 94 | void MainWindow::onSeek() { 95 | video_view_->seek(0.6); 96 | } 97 | 98 | void MainWindow::Update() { 99 | scene()->update(0, 0, desktop_rect_.width(), desktop_rect_.height()); 100 | update(0, 0, desktop_rect_.width(), desktop_rect_.height()); 101 | } 102 | 103 | void MainWindow::PaintNow(QRect *rc) { 104 | if (rc) { 105 | repaint(*rc); 106 | } else { 107 | scene()->update(desktop_rect_); 108 | update(desktop_rect_); 109 | } 110 | } 111 | 112 | VideoView *MainWindow::video_view() { 113 | return video_view_; 114 | } 115 | 116 | } -------------------------------------------------------------------------------- /ui/main_window.h: -------------------------------------------------------------------------------- 1 | #ifndef UI_MAIN_WINDOW_H 2 | #define UI_MAIN_WINDOW_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace ui { 10 | class VideoView; 11 | 12 | class MainWindow : public QGraphicsView { 13 | Q_OBJECT 14 | public: 15 | explicit MainWindow(QWidget *parent = 0); 16 | ~MainWindow(); 17 | 18 | VideoView *video_view(); 19 | 20 | void Update(); 21 | 22 | void PaintNow(QRect *rc = nullptr); 23 | 24 | private slots: 25 | void onStart(); 26 | void onStop(); 27 | void onPause(); 28 | void onResume(); 29 | void onSeek(); 30 | private: 31 | void iniSignalSlots(); 32 | QRect desktop_rect_; 33 | VideoView *video_view_; 34 | QPushButton *play_button_; 35 | QPushButton *stop_button_; 36 | QPushButton *pause_button_; 37 | QPushButton *resume_button_; 38 | QPushButton *seek_button_; 39 | QWidget *control_Widget_; 40 | Q_DISABLE_COPY(MainWindow); 41 | }; 42 | } 43 | #endif // UI_MAIN_WINDOW_H 44 | -------------------------------------------------------------------------------- /ui/video_view.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "ui/video_view.h" 6 | #include 7 | #include 8 | #include "ui/main_window.h" 9 | #include "media/rga_utils.h" 10 | #include "media/ffmpeg_common.h" 11 | #include "base/logging.h" 12 | 13 | namespace ui { 14 | 15 | VideoView::VideoView(const QRect &rect, QGraphicsItem *parent, MainWindow *main_window) 16 | : QGraphicsObject(parent), 17 | rect_(rect), 18 | main_window_(main_window), 19 | frame_(nullptr) { 20 | connect(this, &VideoView::signalMediaError, this, &VideoView::InternalMediaError); 21 | connect(this, &VideoView::signalMediaStop, this, &VideoView::InternalMediaStop); 22 | connect(this, &VideoView::signalUpdateUI, this, &VideoView::Update); 23 | } 24 | 25 | VideoView::~VideoView() { 26 | stop(); 27 | } 28 | 29 | QRectF VideoView::boundingRect() const { 30 | return QRectF(rect_.x(), rect_.y(), rect_.width(), rect_.height()); 31 | } 32 | 33 | void VideoView::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { 34 | if (!painter->paintEngine()) 35 | return; 36 | 37 | Q_UNUSED(option); 38 | Q_UNUSED(widget); 39 | 40 | base::TimeTicks t1 = base::TimeTicks::Now(); 41 | 42 | base::AutoLock l(lock_); 43 | 44 | QImage *image = static_cast(painter->paintEngine()->paintDevice()); 45 | if (image->isNull()) { 46 | return; 47 | } 48 | if (!frame_) 49 | return; 50 | RK_U32 width = mpp_frame_get_width(frame_); 51 | RK_U32 height = mpp_frame_get_height(frame_); 52 | RK_U32 h_stride = mpp_frame_get_hor_stride(frame_); 53 | RK_U32 v_stride = mpp_frame_get_ver_stride(frame_); 54 | MppBuffer buffer = mpp_frame_get_buffer(frame_); 55 | MppFrameFormat fmt = mpp_frame_get_fmt(frame_); 56 | RgaSURF_FORMAT rga_fmt = media::mpp_format_to_rga_format(fmt); 57 | int64_t pts = mpp_frame_get_pts(frame_); 58 | 59 | if (buffer && rga_fmt != RK_FORMAT_UNKNOWN) { 60 | QRect src_rect(0, 0, width, height); 61 | media::rgaDrawImage(reinterpret_cast(mpp_buffer_get_ptr(buffer)), 62 | rga_fmt, 63 | src_rect, 64 | h_stride, 65 | v_stride, 66 | image->bits(), 67 | RK_FORMAT_BGRA_8888, 68 | rect_, 69 | image->width(), 70 | image->height(), 71 | 0, 72 | 0); 73 | 74 | LOG(INFO) << "render video pts:" << pts << ",interval: " << (base::TimeTicks::Now() - t1).InMicroseconds(); 75 | } 76 | } 77 | 78 | bool VideoView::start(const std::string &file, bool enable_audio, int volume, bool loop) { 79 | stop(); 80 | dataset_ = media::Mp4Dataset::create(file); 81 | player_.reset(new media::VideoPlayer(this, dataset_.get(), enable_audio, volume, loop, 0.8)); 82 | return true; 83 | } 84 | 85 | void VideoView::stop() { 86 | if (player_) { 87 | player_.reset(); 88 | } 89 | dataset_.reset(); 90 | } 91 | 92 | void VideoView::pause() { 93 | if (player_) { 94 | player_->Pause(); 95 | } 96 | } 97 | 98 | void VideoView::resume() { 99 | if (player_) { 100 | player_->Resume(); 101 | } 102 | } 103 | 104 | void VideoView::mute(bool enable) { 105 | if (player_) { 106 | player_->Mute(enable); 107 | } 108 | } 109 | 110 | bool VideoView::isSeekable() { 111 | if (!dataset_) 112 | return false; 113 | return dataset_->seekable(); 114 | } 115 | 116 | bool VideoView::seek(double percent) { 117 | if (!player_) 118 | return false; 119 | 120 | AVStream *stream = dataset_->getVideoStream(); 121 | base::TimeDelta time = media::ConvertFromTimeBase(stream->time_base, stream->duration); 122 | double value = percent * time.InSecondsF(); 123 | player_->Seek(value); 124 | return true; 125 | } 126 | 127 | void VideoView::setVolume(int volume) { 128 | if (player_) { 129 | player_->SetVolume(volume); 130 | } 131 | } 132 | 133 | void VideoView::OnMediaError(int err) { 134 | emit signalMediaError(err); 135 | } 136 | 137 | void VideoView::OnMediaStop() { 138 | emit signalMediaStop(); 139 | } 140 | 141 | void VideoView::OnMediaFrameArrival(MppFrame frame) { 142 | base::AutoLock l(lock_); 143 | CleanUp(); 144 | frame_ = frame; 145 | emit signalUpdateUI(); 146 | } 147 | 148 | void VideoView::CleanUp() { 149 | if (frame_) { 150 | mpp_frame_deinit(&frame_); 151 | frame_ = nullptr; 152 | } 153 | } 154 | 155 | void VideoView::Update() { 156 | main_window_->PaintNow(&rect_); 157 | } 158 | 159 | void VideoView::InternalMediaError(int err) { 160 | 161 | } 162 | 163 | void VideoView::InternalMediaStop() { 164 | stop(); 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /ui/video_view.h: -------------------------------------------------------------------------------- 1 | #ifndef UI_VIDEO_VIEW_H 2 | #define UI_VIDEO_VIEW_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include "base/macros.h" 8 | #include "base/synchronization/lock.h" 9 | #include "media/video_player.h" 10 | #include "media/mp4_dataset.h" 11 | 12 | namespace ui { 13 | class MainWindow; 14 | 15 | class VideoView : public QGraphicsObject, 16 | public media::VideoPlayer::Delegate { 17 | Q_OBJECT 18 | public: 19 | explicit VideoView(const QRect &rect, 20 | QGraphicsItem *parent = 0, 21 | MainWindow *main_window = nullptr); 22 | ~VideoView(); 23 | 24 | bool start(const std::string &file, bool enable_audio, int volume, bool loop); 25 | 26 | void stop(); 27 | 28 | void pause(); 29 | 30 | void resume(); 31 | 32 | void mute(bool enable); 33 | 34 | bool isSeekable(); 35 | 36 | bool seek(double percent); 37 | 38 | void setVolume(int volume); 39 | 40 | protected: 41 | QRectF boundingRect() const override; 42 | 43 | void paint(QPainter *painter, 44 | const QStyleOptionGraphicsItem *option, 45 | QWidget *widget = 0) override; 46 | private: 47 | signals: 48 | void signalMediaError(int err); 49 | void signalMediaStop(); 50 | void signalUpdateUI(); 51 | 52 | private slots: 53 | void InternalMediaError(int err); 54 | void InternalMediaStop(); 55 | void Update(); 56 | private: 57 | 58 | void OnMediaError(int err) override; 59 | 60 | void OnMediaStop() override; 61 | 62 | void OnMediaFrameArrival(MppFrame mb) override; 63 | 64 | void CleanUp(); 65 | 66 | QRect rect_; 67 | 68 | MainWindow *main_window_; 69 | 70 | MppFrame frame_; 71 | 72 | base::Lock lock_; 73 | 74 | std::unique_ptr dataset_; 75 | std::unique_ptr player_; 76 | 77 | Q_DISABLE_COPY(VideoView); 78 | }; 79 | } 80 | 81 | #endif // UI_VIDEO_VIEW_H 82 | --------------------------------------------------------------------------------