├── .gitignore ├── Mp4MuxerDemo.md ├── README.md ├── app ├── .gitignore ├── CMakeLists.txt ├── build.gradle ├── libs │ ├── arm64-v8a │ │ └── libmp4v2.so │ ├── armeabi-v7a │ │ └── libmp4v2.so │ ├── include │ │ └── mp4v2 │ │ │ ├── chapter.h │ │ │ ├── file.h │ │ │ ├── file_prop.h │ │ │ ├── general.h │ │ │ ├── isma.h │ │ │ ├── itmf_generic.h │ │ │ ├── itmf_tags.h │ │ │ ├── mp4v2.h │ │ │ ├── platform.h │ │ │ ├── project.h │ │ │ ├── project.h.in │ │ │ ├── sample.h │ │ │ ├── streaming.h │ │ │ ├── track.h │ │ │ └── track_prop.h │ ├── x86 │ │ └── libmp4v2.so │ └── x86_64 │ │ └── libmp4v2.so ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── chezi008 │ │ └── mp4muxerdemo │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── cpp │ │ ├── MP4Encoder.cpp │ │ ├── MP4Encoder.h │ │ ├── MP4EncoderHelper.cpp │ │ └── native-lib.cpp │ ├── java │ │ └── com │ │ │ └── chezi008 │ │ │ └── mp4muxerdemo │ │ │ ├── MP4V2Activity.java │ │ │ ├── MainActivity.java │ │ │ ├── MuxerMp4Activity.java │ │ │ ├── decode │ │ │ └── H264Decoder.java │ │ │ ├── encode │ │ │ └── AACEncoder.java │ │ │ ├── file │ │ │ ├── FileConstant.java │ │ │ └── H264ReadRunable.java │ │ │ ├── helper │ │ │ └── MP4EncoderHelper.java │ │ │ ├── hw │ │ │ ├── AudioEncoder.java │ │ │ ├── AudioSoftwarePoller.java │ │ │ ├── FileUtils.java │ │ │ └── HWRecorderActivity.java │ │ │ └── utils │ │ │ └── SPUtils.java │ └── res │ │ ├── layout │ │ ├── activity_hwrecorder.xml │ │ ├── activity_main.xml │ │ ├── activity_mp4_v2.xml │ │ └── activity_muxer_mp4.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── chezi008 │ └── mp4muxerdemo │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── mp4muxer ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── chezi008 │ │ └── mp4muxer │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── chezi008 │ │ │ └── mp4muxer │ │ │ └── Mp4Muxer.java │ └── res │ │ └── values │ │ └── strings.xml │ └── test │ └── java │ └── com │ └── chezi008 │ └── mp4muxer │ └── ExampleUnitTest.java ├── mtv.h264 └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | -------------------------------------------------------------------------------- /Mp4MuxerDemo.md: -------------------------------------------------------------------------------- 1 | ## Android使用Jni mp4v2库将h264裸流合成mp4文件 2 | 3 | ### 建议使用场景 4 | #### 一般视频流有如下两种途径获取: 5 | 1. Android摄像头采集 6 | 2. 服务端传输过来的视频流 7 | 8 | 如果数据由本机摄像头直接采集,建议使用MediaMuxer类去实现mp4的合成。如果是服务端传输过来的视频流可以使用mp4v2的方法实现mp4的合成。我在项目里面也简单的利用MediaMuxer编写了一个Demo。可能写的不是很详细,功能也不是很完善。所以有什么问题还是多多希望指出,一起改进。 9 | 10 | ### mp4v2的So文件 11 | 1. 我demo里面只导入了armeabi 指令集的so包,需要其他类型的可以自己去官网下载进行打包。 12 | 2. 将mp4v2的头文件放在libs里面的include文件夹下面,所以再cmake文件里面需要增加`include_directories(libs/include)`命令。 13 | ### 使用c++将mp4v2的使用封装了一层 14 | 封装了如下几个方法: 15 | 16 | // open or creat a mp4 file. 17 | MP4FileHandle CreateMP4File(const char *fileName,int width,int height,int timeScale = 90000,int frameRate = 25); 18 | // wirte 264 metadata in mp4 file. 19 | bool Write264Metadata(MP4FileHandle hMp4File,LPMP4ENC_Metadata lpMetadata); 20 | // wirte 264 data, data can contain multiple frame. 21 | int WriteH264Data(MP4FileHandle hMp4File,const unsigned char* pData,int size); 22 | // close mp4 file. 23 | void CloseMP4File(MP4FileHandle hMp4File); 24 | // convert H264 file to mp4 file. 25 | // no need to call CreateMP4File and CloseMP4File,it will create/close mp4 file automaticly. 26 | bool WriteH264File(const char* pFile264,const char* pFileMp4); 27 | 28 | 方法说明: 29 | 30 | 1. CreateMP4File:创建需要写入的mp4文件,可以设置视频的分辨率。默认的时间刻度是90000,帧率是25帧 31 | 2. WriteH264Data:写入h264数据,可以写入byte[]类型的数据流,也可是LPMP4ENC_Metadata(sps pps)类型数据。 32 | 3. CloseMP4File:转换完成后记得要释放内存,调用次方法 33 | 4. WriteH264File:直接将本地h264文件转换成mp4。实现方法是一样的,只是在C++代码里面实现了,将h264数据分割成一帧一帧,再写入至输入文件中。 34 | 35 | ### 一、mp4v2 方法的使用 36 | 同样,我在代码中封装了三个本地方法: 37 | 38 | public static native void init(String mp4FilePath, int widht, int height); 39 | 40 | public static native int writeH264Data(byte[] data, int size); 41 | 42 | public static native void close(); 43 | 从方法名,可以看得出怎么使用,所以在这里就不多赘述了。项目里面提供了标准的h264测试文件mtv.h264。 44 | 45 | ### 二、MediaMuxer合成Mp4 46 | - 官方文档介绍:http://www.loverobots.cn/android-api/reference/android/media/MediaMuxer.html 47 | ``` 48 | MediaMuxer muxer = new MediaMuxer("temp.mp4", OutputFormat.MUXER_OUTPUT_MPEG_4); 49 | // More often, the MediaFormat will be retrieved from MediaCodec.getOutputFormat() 50 | // or MediaExtractor.getTrackFormat(). 51 | MediaFormat audioFormat = new MediaFormat(...); 52 | MediaFormat videoFormat = new MediaFormat(...); 53 | int audioTrackIndex = muxer.addTrack(audioFormat); 54 | int videoTrackIndex = muxer.addTrack(videoFormat); 55 | ByteBuffer inputBuffer = ByteBuffer.allocate(bufferSize); 56 | boolean finished = false; 57 | BufferInfo bufferInfo = new BufferInfo(); 58 | 59 | muxer.start(); 60 | while(!finished) { 61 | // getInputBuffer() will fill the inputBuffer with one frame of encoded 62 | // sample from either MediaCodec or MediaExtractor, set isAudioSample to 63 | // true when the sample is audio data, set up all the fields of bufferInfo, 64 | // and return true if there are no more samples. 65 | finished = getInputBuffer(inputBuffer, isAudioSample, bufferInfo); 66 | if (!finished) { 67 | int currentTrackIndex = isAudioSample ? audioTrackIndex : videoTrackIndex; 68 | muxer.writeSampleData(currentTrackIndex, inputBuffer, bufferInfo); 69 | } 70 | }; 71 | muxer.stop(); 72 | muxer.release(); 73 | ``` 74 | #### 使用中一些需要注意的地方 75 | 76 | 1. MediaFormat 可以在初始化编码器的时候获取 77 | ``` 78 | mediaformat = MediaFormat.createVideoFormat("video/avc", VIDEO_WIDTH, VIDEO_HEIGHT); 79 | ``` 80 | 2. 写入数据时候的inputBuffer 和 bufferInfo 需要自己构造 81 | 82 | ### 三、注意 83 | 1. 不熟悉cmake编译的同学,可以去查查资料,反正我在项目里面使用so文件遇到过比较大的坑,因为一直不熟悉cmake的语法,链接库都不会。 84 | 2. 还有就是对视频流数据结构的理解,这玩意是一帧一帧组成的。不同类型的帧需要不同的处理。比如第一帧需要传入I帧,你得先去将视频流分割成一帧一帧的,再去判断帧的类型。不太熟悉的同学可以[传送门](http://blog.csdn.net/dittychen/article/details/55509718)一下。**切记传入数据以帧为单位** 85 | 86 | 87 | ### 四、Q&S 88 | 1. 录制的视频或快或慢。 89 | 在 MP4Encoder::CreateMP4File本地方法中有一个m_nFrameRate变量,控制帧率的,也就是每分钟多少帧,这里可以自己去控制,和录制视频的帧率一致就行了,这里默认的是25帧。 90 | 2. 录制的本地mp4视频预览画面是黑色或者是绿色的 91 | 造成的原因是录制视频流的时候第一帧不是关键帧(I帧),所以在使用writeH264Data方法的时候,记得第一帧传入关键帧。 92 | 3. download下载的项目无法运行 93 | 。。。这个,你就自己去配置编译环境了,代码都在这了。 94 | 4. 关于音频写入的问题 95 | 未完待续。。。 96 | 5. 关于音视频同步的问题 97 | 未完待续。。。 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## H264/AAC实时流 录制成MP4格式的本地视频 2 | GITHUB: 3 | https://github.com/chezi008/mp4muxer 4 | ### 建议使用场景 5 | #### 一般视频流有如下两种途径获取: 6 | 1. Android摄像头采集 7 | 2. 服务端传输过来的视频流 8 | 9 | 如果数据由本机摄像头直接采集,建议使用MediaMuxer类去实现mp4的合成。如果是服务端传输过来的视频流可以使用mp4v2的方法实现mp4的合成。我在项目里面也简单的利用MediaMuxer编写了一个Demo。可能写的不是很详细,功能也不是很完善。所以有什么问题还是多多希望指出,一起改进。 10 | 11 | ### mp4v2的So文件 12 | 1. 我demo里面只导入了armeabi 指令集的so包,需要其他类型的可以自己去官网下载进行打包。 13 | 2. 将mp4v2的头文件放在libs里面的include文件夹下面,所以再cmake文件里面需要增加`include_directories(libs/include)`命令。 14 | ### 使用c++将mp4v2的使用封装了一层 15 | 封装了如下几个方法: 16 | 17 | // open or creat a mp4 file. 18 | MP4FileHandle CreateMP4File(const char *fileName,int width,int height,int timeScale = 90000,int frameRate = 25); 19 | // wirte 264 metadata in mp4 file. 20 | bool Write264Metadata(MP4FileHandle hMp4File,LPMP4ENC_Metadata lpMetadata); 21 | // wirte 264 data, data can contain multiple frame. 22 | int WriteH264Data(MP4FileHandle hMp4File,const unsigned char* pData,int size); 23 | // close mp4 file. 24 | void CloseMP4File(MP4FileHandle hMp4File); 25 | // convert H264 file to mp4 file. 26 | // no need to call CreateMP4File and CloseMP4File,it will create/close mp4 file automaticly. 27 | bool WriteH264File(const char* pFile264,const char* pFileMp4); 28 | 29 | 方法说明: 30 | 31 | 1. CreateMP4File:创建需要写入的mp4文件,可以设置视频的分辨率。默认的时间刻度是90000,帧率是25帧 32 | 2. WriteH264Data:写入h264数据,可以写入byte[]类型的数据流,也可是LPMP4ENC_Metadata(sps pps)类型数据。 33 | 3. CloseMP4File:转换完成后记得要释放内存,调用次方法 34 | 4. WriteH264File:直接将本地h264文件转换成mp4。实现方法是一样的,只是在C++代码里面实现了,将h264数据分割成一帧一帧,再写入至输入文件中。 35 | 36 | ### 一、~~mp4v2 方法的使用~~ 37 | **这个库太久没有维护了,还是建议使用官方API,即第二种方法。** 38 | 同样,我在代码中封装了三个本地方法: 39 | 40 | public static native void init(String mp4FilePath, int widht, int height); 41 | 42 | public static native int writeH264Data(byte[] data, int size); 43 | 44 | public static native void close(); 45 | 从方法名,可以看得出怎么使用,所以在这里就不多赘述了。项目里面提供了标准的h264测试文件mtv.h264。 46 | 47 | ### 二、MediaMuxer合成Mp4 48 | ####官方文档介绍:http://www.loverobots.cn/android-api/reference/android/media/MediaMuxer.html 49 | ``` 50 | MediaMuxer muxer = new MediaMuxer("temp.mp4", OutputFormat.MUXER_OUTPUT_MPEG_4); 51 | // More often, the MediaFormat will be retrieved from MediaCodec.getOutputFormat() 52 | // or MediaExtractor.getTrackFormat(). 53 | MediaFormat audioFormat = new MediaFormat(...); 54 | MediaFormat videoFormat = new MediaFormat(...); 55 | int audioTrackIndex = muxer.addTrack(audioFormat); 56 | int videoTrackIndex = muxer.addTrack(videoFormat); 57 | ByteBuffer inputBuffer = ByteBuffer.allocate(bufferSize); 58 | boolean finished = false; 59 | BufferInfo bufferInfo = new BufferInfo(); 60 | 61 | muxer.start(); 62 | while(!finished) { 63 | // getInputBuffer() will fill the inputBuffer with one frame of encoded 64 | // sample from either MediaCodec or MediaExtractor, set isAudioSample to 65 | // true when the sample is audio data, set up all the fields of bufferInfo, 66 | // and return true if there are no more samples. 67 | finished = getInputBuffer(inputBuffer, isAudioSample, bufferInfo); 68 | if (!finished) { 69 | int currentTrackIndex = isAudioSample ? audioTrackIndex : videoTrackIndex; 70 | muxer.writeSampleData(currentTrackIndex, inputBuffer, bufferInfo); 71 | } 72 | }; 73 | muxer.stop(); 74 | muxer.release(); 75 | ``` 76 | #### MediaMuxer的使用 77 | 1. 初始化: 78 | ``` 79 | mMuxer = new MediaMuxer(outPath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4); 80 | ``` 81 | 2. 根据自己情况添加track,不要方法添加,此操作必须在mMuxer start方法之前调用: 82 | ``` 83 | mVideoTrackIndex = mMuxer.addTrack(mediaFormat); 84 | mAudioTrackIndex = mMuxer.addTrack(mediaFormat); 85 | ``` 86 | 3. 写入数据 87 | ``` 88 | outputBuffer.position(bufferInfo.offset); 89 | outputBuffer.limit(bufferInfo.offset + bufferInfo.size); 90 | mMuxer.writeSampleData(track, outputBuffer, bufferInfo); 91 | ``` 92 | 4. 结束 93 | ``` 94 | mMuxer.stop(); 95 | mMuxer.release(); 96 | ``` 97 | **在结束的时候你可能会遇到几种异常:** 98 | - E/MPEG4Writer: Missing codec specific data: 99 | 这是因为在写入数据的时候没有写入编码参数,h264的编码参数包含SPS和PPS。所以当你视频流遇到这些参数帧的时候,请设置好对应的参数。 100 | ``` 101 | //设置sps和pps 如果设置不正确会导致合成的mp4视频作为文件预览的时候,预览图片是黑色的 102 | //视频进度条拖拽画面会出现绿色,以及块状现象 103 | mediaformat.setByteBuffer("csd-0", mCSD0); 104 | mediaformat.setByteBuffer("csd-1", mCSD1); 105 | 106 | ``` 107 | AAC参数:PCM在用编码器编码成AAC格式音频的时候,编码器会在自动设置参数。 108 | ``` 109 | 110 | 当outIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED 111 | 我们可以获取到,MediaFormat format = mEnc.getOutputFormat(), 112 | format就包含了CODEC_CONFIG。此时的format可直接作为addTrack()的参数使用。 113 | ``` 114 | - There are no sync frames for video track 115 | 这是因为没有设置关键帧的flag。 116 | ``` 117 | switch (nalType & 0x1f) { 118 | case H264Decoder.NAL_TYPE_I: 119 | bufferInfo.flags = MediaCodec.BUFFER_FLAG_KEY_FRAME; 120 | break; 121 | } 122 | ``` 123 | 124 | - 音视频同步 125 | 方法里面传入的时间参数PTS的单位时:microseconds,微妙。 126 | ``` 127 | long pts = System.nanoTime() / 1000; 128 | ``` 129 | 直接使用当前时间戳会有问题,录制成的MP4总时间会很大。我使用一个相对时间,当前时间相对于开始的时间差。 130 | ``` 131 | bufferInfo.presentationTimeUs = System.nanoTime()/1000-startPts; 132 | ``` 133 | 134 | 135 | #### 使用中一些需要注意的地方 136 | 137 | 1. MediaFormat 可以在初始化编码器的时候获取 138 | ``` 139 | mediaformat = MediaFormat.createVideoFormat("video/avc", VIDEO_WIDTH, VIDEO_HEIGHT); 140 | ``` 141 | 2. 写入数据时候的inputBuffer 和 bufferInfo 需要自己构造 142 | 143 | ### 三、注意 144 | 1. 不熟悉cmake编译的同学,可以去查查资料,反正我在项目里面使用so文件遇到过比较大的坑,因为一直不熟悉cmake的语法,链接库都不会。 145 | 2. 还有就是对视频流数据结构的理解,这玩意是一帧一帧组成的。不同类型的帧需要不同的处理。比如第一帧需要传入I帧,你得先去将视频流分割成一帧一帧的,再去判断帧的类型。不太熟悉的同学可以[传送门](http://blog.csdn.net/dittychen/article/details/55509718)一下。**切记传入数据以帧为单位** 146 | 147 | 148 | ### 四、Q&S 149 | 1. 录制的视频或快或慢。 150 | 在 MP4Encoder::CreateMP4File本地方法中有一个m_nFrameRate变量,控制帧率的,也就是每分钟多少帧,这里可以自己去控制,和录制视频的帧率一致就行了,这里默认的是25帧。 151 | 2. 录制的本地mp4视频预览画面是黑色或者是绿色的 152 | 造成的原因是录制视频流的时候第一帧不是关键帧(I帧),所以在使用writeH264Data方法的时候,记得第一帧传入关键帧。 153 | 3. download下载的项目无法运行 154 | 。。。这个,你就自己去配置编译环境了,代码都在这了。 155 | 4. 录制成的MP4第一帧模糊 156 | 这是因为写数据的时候没有进行关键帧的判断,第一帧写入关键帧就不会有这个问题了。 157 | 158 | ### 五、引用 159 | Android在MediaMuxer和MediaCodec用例:https://www.cnblogs.com/hrhguanli/p/5043610.html 160 | Grafika: https://github.com/google/grafika 161 | HWEncoderExperiments:https://github.com/OnlyInAmerica/HWEncoderExperiments/tree/audioonly/HWEncoderExperiments/src/main/java/net/openwatch/hwencoderexperiments -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # For more information about using CMake with Android Studio, read the 2 | # documentation: https://d.android.com/studio/projects/add-native-code.html 3 | 4 | # Sets the minimum version of CMake required to build the native library. 5 | 6 | cmake_minimum_required(VERSION 3.4.1) 7 | 8 | set(NATIVE_LIBS ${CMAKE_SOURCE_DIR}/libs) 9 | 10 | add_library(mp4v2 11 | SHARED 12 | IMPORTED) 13 | set_target_properties(mp4v2 14 | PROPERTIES 15 | IMPORTED_LOCATION ${NATIVE_LIBS}/${ANDROID_ABI}/libmp4v2.so) 16 | include_directories(libs/include) 17 | 18 | # Creates and names a library, sets it as either STATIC 19 | # or SHARED, and provides the relative paths to its source code. 20 | # You can define multiple libraries, and CMake builds them for you. 21 | # Gradle automatically packages shared libraries with your APK. 22 | 23 | add_library( # Sets the name of the library. 24 | native-lib 25 | 26 | # Sets the library as a shared library. 27 | SHARED 28 | 29 | # Provides a relative path to your source file(s). 30 | src/main/cpp/native-lib.cpp 31 | src/main/cpp/MP4Encoder.cpp 32 | src/main/cpp/MP4EncoderHelper.cpp) 33 | 34 | # Searches for a specified prebuilt library and stores the path as a 35 | # variable. Because CMake includes system libraries in the search path by 36 | # default, you only need to specify the name of the public NDK library 37 | # you want to add. CMake verifies that the library exists before 38 | # completing its build. 39 | 40 | find_library( # Sets the name of the path variable. 41 | log-lib 42 | 43 | # Specifies the name of the NDK library that 44 | # you want CMake to locate. 45 | log) 46 | 47 | # Specifies libraries CMake should link to your target library. You 48 | # can link multiple libraries, such as libraries you define in this 49 | # build script, prebuilt third-party libraries, or system libraries. 50 | 51 | target_link_libraries( # Specifies the target library. 52 | native-lib 53 | 54 | mp4v2 55 | 56 | # Links the target library to the log library 57 | # included in the NDK. 58 | ${log-lib}) -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 26 5 | buildToolsVersion '28.0.3' 6 | defaultConfig { 7 | applicationId "com.chezi008.mp4muxerdemo" 8 | minSdkVersion 18 9 | targetSdkVersion 25 10 | versionCode 1 11 | versionName "1.0" 12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 13 | externalNativeBuild { 14 | cmake { 15 | cppFlags "-std=c++11 -frtti -fexceptions" 16 | } 17 | } 18 | // ndk { 19 | // abiFilters "armeabi" 20 | // } 21 | } 22 | buildTypes { 23 | release { 24 | minifyEnabled false 25 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 26 | } 27 | } 28 | sourceSets { 29 | main { 30 | jniLibs.srcDir 'libs' 31 | } 32 | } 33 | externalNativeBuild { 34 | cmake { 35 | path "CMakeLists.txt" 36 | } 37 | } 38 | } 39 | 40 | dependencies { 41 | implementation fileTree(include: ['*.jar'], dir: 'libs') 42 | androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', { 43 | exclude group: 'com.android.support', module: 'support-annotations' 44 | }) 45 | implementation 'com.android.support:appcompat-v7:26.0.2' 46 | implementation 'com.android.support.constraint:constraint-layout:1.0.2' 47 | androidTestImplementation 'junit:junit:4.12' 48 | implementation project(':mp4muxer') 49 | } 50 | -------------------------------------------------------------------------------- /app/libs/arm64-v8a/libmp4v2.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chezi008/mp4muxer/97bbae69d1e0b0d25b883866b60246e1a9c4be10/app/libs/arm64-v8a/libmp4v2.so -------------------------------------------------------------------------------- /app/libs/armeabi-v7a/libmp4v2.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chezi008/mp4muxer/97bbae69d1e0b0d25b883866b60246e1a9c4be10/app/libs/armeabi-v7a/libmp4v2.so -------------------------------------------------------------------------------- /app/libs/include/mp4v2/chapter.h: -------------------------------------------------------------------------------- 1 | #ifndef MP4V2_CHAPTER_H 2 | #define MP4V2_CHAPTER_H 3 | 4 | /**************************************************************************//** 5 | * 6 | * @defgroup mp4_chapter MP4v2 Chapter 7 | * @{ 8 | * 9 | *****************************************************************************/ 10 | 11 | /** The maximum length of a QuickTime chapter title (in 8-bit chars) 12 | */ 13 | #define MP4V2_CHAPTER_TITLE_MAX 1023 14 | 15 | /** Chapter item. 16 | * This item defines various attributes for a chapter. 17 | * @ingroup mp4_chapter 18 | */ 19 | typedef struct MP4Chapter_s { 20 | MP4Duration duration; /**< duration of chapter in milliseconds */ 21 | char title[MP4V2_CHAPTER_TITLE_MAX+1]; /**< title of chapter */ 22 | } MP4Chapter_t; 23 | 24 | /** Known chapter types. 25 | * @ingroup mp4_chapter 26 | */ 27 | typedef enum { 28 | MP4ChapterTypeNone = 0, /**< no chapters found return value */ 29 | MP4ChapterTypeAny = 1, /**< any or all known chapter types */ 30 | MP4ChapterTypeQt = 2, /**< QuickTime chapter type */ 31 | MP4ChapterTypeNero = 4 /**< Nero chapter type */ 32 | } MP4ChapterType; 33 | 34 | /** Add a QuickTime chapter. 35 | * 36 | * This function adds a QuickTime chapter to file hFile. 37 | * 38 | * @param hFile handle of file to add chapter. 39 | * @param chapterTrackId ID of chapter track or #MP4_INVALID_TRACK_ID 40 | * if unknown. 41 | * @param chapterDuration duration (in the timescale of the chapter track). 42 | * @param chapterTitle title text for the chapter or NULL to use default 43 | * title format ("Chapter %03d", n) where n is the chapter number. 44 | */ 45 | MP4V2_EXPORT 46 | void MP4AddChapter( 47 | MP4FileHandle hFile, 48 | MP4TrackId chapterTrackId, 49 | MP4Duration chapterDuration, 50 | const char* chapterTitle DEFAULT(0)); 51 | 52 | /** Add a QuickTime chapter track. 53 | * 54 | * This function adds a chapter (text) track to file hFile. 55 | * The optional parameter timescale may be supplied to give the new 56 | * chapter a specific timescale. Otherwise the chapter track will have 57 | * the same timescale as the reference track defined in parameter refTrackId. 58 | * 59 | * @param hFile handle of file to add chapter track. 60 | * @param refTrackId ID of the track that will reference the chapter track. 61 | * @param timescale the timescale of the chapter track or 0 to use the 62 | * timescale of track specified by refTrackId. 63 | * 64 | * @return ID of the created chapter track. 65 | */ 66 | MP4V2_EXPORT 67 | MP4TrackId MP4AddChapterTextTrack( 68 | MP4FileHandle hFile, 69 | MP4TrackId refTrackId, 70 | uint32_t timescale DEFAULT(0) ); 71 | 72 | /** Add a Nero chapter. 73 | * 74 | * This function adds a Nero chapter to file hFile. 75 | * 76 | * @param hFile handle of file to add chapter. 77 | * @param chapterStart the start time of the chapter in 100 nanosecond units 78 | * @param chapterTitle title text for the chapter or NULL to use default 79 | * title format ("Chapter %03d", n) where n is the chapter number. 80 | */ 81 | MP4V2_EXPORT 82 | void MP4AddNeroChapter( 83 | MP4FileHandle hFile, 84 | MP4Timestamp chapterStart, 85 | const char* chapterTitle DEFAULT(0)); 86 | 87 | /** Convert chapters to another type. 88 | * 89 | * This function converts existing chapters in file hFile 90 | * from one type to another type. 91 | * Conversion from Nero to QuickTime or QuickTime to Nero is supported. 92 | * 93 | * @param hFile handle of file to convert. 94 | * @param toChapterType the chapter type to convert to: 95 | * @li #MP4ChapterTypeQt (convert from Nero to Qt) 96 | * @li #MP4ChapterTypeNero (convert from Qt to Nero) 97 | * 98 | * @return the chapter type before conversion or #MP4ChapterTypeNone 99 | * if the source chapters do not exist 100 | * or invalid toChapterType was specified. 101 | */ 102 | MP4V2_EXPORT 103 | MP4ChapterType MP4ConvertChapters( 104 | MP4FileHandle hFile, 105 | MP4ChapterType toChapterType DEFAULT(MP4ChapterTypeQt)); 106 | 107 | /** Delete chapters. 108 | * 109 | * This function deletes existing chapters in file hFile. 110 | * 111 | * @param hFile handle of file to delete chapters. 112 | * @param chapterType the type of chapters to delete: 113 | * @li #MP4ChapterTypeAny (delete all known chapter types) 114 | * @li #MP4ChapterTypeQt 115 | * @li #MP4ChapterTypeNero 116 | * @param chapterTrackId ID of the chapter track if known, 117 | * or #MP4_INVALID_TRACK_ID. 118 | * Only applies when chapterType=#MP4ChapterTypeQt. 119 | * 120 | * @return the type of deleted chapters 121 | */ 122 | MP4V2_EXPORT 123 | MP4ChapterType MP4DeleteChapters( 124 | MP4FileHandle hFile, 125 | MP4ChapterType chapterType DEFAULT(MP4ChapterTypeQt), 126 | MP4TrackId chapterTrackId DEFAULT(MP4_INVALID_TRACK_ID) ); 127 | 128 | /** Get list of chapters. 129 | * 130 | * This function gets a chpter list from file hFile. 131 | * 132 | * @param hFile handle of file to read. 133 | * @param chapterList address receiving array of chapter items. 134 | * If a non-NULL is received the caller is responsible for freeing the 135 | * memory with MP4Free(). 136 | * @param chapterCount address receiving count of items in array. 137 | * @param chapterType the type of chapters to read: 138 | * @li #MP4ChapterTypeAny (any chapters, searched in order of Qt, Nero) 139 | * @li #MP4ChapterTypeQt 140 | * @li #MP4ChapterTypeNero 141 | * 142 | * @result the first type of chapters found. 143 | */ 144 | MP4V2_EXPORT 145 | MP4ChapterType MP4GetChapters( 146 | MP4FileHandle hFile, 147 | MP4Chapter_t** chapterList, 148 | uint32_t* chapterCount, 149 | MP4ChapterType chapterType DEFAULT(MP4ChapterTypeQt)); 150 | 151 | /** Set list of chapters OKOK. 152 | * 153 | * This functions sets the complete chapter list in file hFile. 154 | * If any chapters of the same type already exist they will first 155 | * be deleted. 156 | * 157 | * @param hFile handle of file to modify. 158 | * @param chapterList array of chapters items. 159 | * @param chapterCount count of items in array. 160 | * @param chapterType type of chapters to write: 161 | * @li #MP4ChapterTypeAny (chapters of all types are written) 162 | * @li #MP4ChapterTypeQt 163 | * @li #MP4ChapterTypeNero 164 | * 165 | * @return the type of chapters written. 166 | */ 167 | MP4V2_EXPORT 168 | MP4ChapterType MP4SetChapters( 169 | MP4FileHandle hFile, 170 | MP4Chapter_t* chapterList, 171 | uint32_t chapterCount, 172 | MP4ChapterType chapterType DEFAULT(MP4ChapterTypeQt)); 173 | 174 | /** @} ***********************************************************************/ 175 | 176 | #endif /* MP4V2_CHAPTER_H */ 177 | -------------------------------------------------------------------------------- /app/libs/include/mp4v2/file.h: -------------------------------------------------------------------------------- 1 | #ifndef MP4V2_FILE_H 2 | #define MP4V2_FILE_H 3 | 4 | /**************************************************************************//** 5 | * 6 | * @defgroup mp4_file MP4v2 File I/O 7 | * @{ 8 | * 9 | *****************************************************************************/ 10 | 11 | /** Bit: enable 64-bit data-atoms. */ 12 | #define MP4_CREATE_64BIT_DATA 0x01 13 | /** Bit: enable 64-bit time-atoms. @note Incompatible with QuickTime. */ 14 | #define MP4_CREATE_64BIT_TIME 0x02 15 | /** Bit: do not recompute avg/max bitrates on file close. @note See http://code.google.com/p/mp4v2/issues/detail?id=66 */ 16 | #define MP4_CLOSE_DO_NOT_COMPUTE_BITRATE 0x01 17 | 18 | /** Enumeration of file modes for custom file provider. */ 19 | typedef enum MP4FileMode_e 20 | { 21 | FILEMODE_UNDEFINED, /**< undefined */ 22 | FILEMODE_READ, /**< file may be read */ 23 | FILEMODE_MODIFY, /**< file may be read/written */ 24 | FILEMODE_CREATE /**< file will be created/truncated for read/write */ 25 | } MP4FileMode; 26 | 27 | /** Structure of functions implementing custom file provider. 28 | * 29 | * Except for open, all the functions must return a true value 30 | * to indicate failure or false on success. The open function must return 31 | * a pointer or handle which represents the open file, otherwise NULL. 32 | * 33 | * maxChunkSize is a hint suggesting what the max size of data should be read 34 | * as in underlying read/write operations. A value of 0 indicates there is no hint. 35 | */ 36 | typedef struct MP4FileProvider_s 37 | { 38 | void* ( *open )( const char* name, MP4FileMode mode ); 39 | int ( *seek )( void* handle, int64_t pos ); 40 | int ( *read )( void* handle, void* buffer, int64_t size, int64_t* nin, int64_t maxChunkSize ); 41 | int ( *write )( void* handle, const void* buffer, int64_t size, int64_t* nout, int64_t maxChunkSize ); 42 | int ( *close )( void* handle ); 43 | } MP4FileProvider; 44 | 45 | /** Close an mp4 file. 46 | * MP4Close closes a previously opened mp4 file. If the file was opened 47 | * writable with MP4Create() or MP4Modify(), then MP4Close() will write 48 | * out all pending information to disk. 49 | * 50 | * @param hFile handle of file to close. 51 | * @param flags bitmask that allows the user to set extra options for the 52 | * close commands. Valid options include: 53 | * @li #MP4_CLOSE_DO_NOT_COMPUTE_BITRATE 54 | */ 55 | MP4V2_EXPORT 56 | void MP4Close( 57 | MP4FileHandle hFile, 58 | uint32_t flags DEFAULT(0) ); 59 | 60 | /** Create a new mp4 file. 61 | * 62 | * MP4Create is the first call that should be used when you want to create 63 | * a new, empty mp4 file. It is equivalent to opening a file for writing, 64 | * but also involved with creation of necessary mp4 framework structures. 65 | * ie. invoking MP4Create() followed by MP4Close() will result in a file 66 | * with a non-zero size. 67 | * 68 | * @param fileName pathname of the file to be created. 69 | * On Windows, this should be a UTF-8 encoded string. 70 | * On other platforms, it should be an 8-bit encoding that is 71 | * appropriate for the platform, locale, file system, etc. 72 | * (prefer to use UTF-8 when possible). 73 | * @param flags bitmask that allows the user to set 64-bit values for 74 | * data or time atoms. Valid bits may be any combination of: 75 | * @li #MP4_CREATE_64BIT_DATA 76 | * @li #MP4_CREATE_64BIT_TIME 77 | * 78 | * @return On success a handle of the newly created file for use in 79 | * subsequent calls to the library. 80 | * On error, #MP4_INVALID_FILE_HANDLE. 81 | */ 82 | MP4V2_EXPORT 83 | MP4FileHandle MP4Create( 84 | const char* fileName, 85 | uint32_t flags DEFAULT(0) ); 86 | 87 | /** Create a new mp4 file with extended options. 88 | * 89 | * MP4CreateEx is an extended version of MP4Create(). 90 | * 91 | * @param fileName pathname of the file to be created. 92 | * On Windows, this should be a UTF-8 encoded string. 93 | * On other platforms, it should be an 8-bit encoding that is 94 | * appropriate for the platform, locale, file system, etc. 95 | * (prefer to use UTF-8 when possible). 96 | * @param flags bitmask that allows the user to set 64-bit values for 97 | * data or time atoms. Valid bits may be any combination of: 98 | * @li #MP4_CREATE_64BIT_DATA 99 | * @li #MP4_CREATE_64BIT_TIME 100 | * @param add_ftyp if true an ftyp atom is automatically created. 101 | * @param add_iods if true an iods atom is automatically created. 102 | * @param majorBrand ftyp brand identifier. 103 | * @param minorVersion ftyp informative integer for the minor version 104 | * of the major brand. 105 | * @param compatibleBrands ftyp list of compatible brands. 106 | * @param compatibleBrandsCount is the count of items specified in 107 | * compatibleBrands. 108 | * 109 | * @return On success a handle of the newly created file for use in 110 | * subsequent calls to the library. 111 | * On error, #MP4_INVALID_FILE_HANDLE. 112 | */ 113 | MP4V2_EXPORT 114 | MP4FileHandle MP4CreateEx( 115 | const char* fileName, 116 | uint32_t flags DEFAULT(0), 117 | int add_ftyp DEFAULT(1), 118 | int add_iods DEFAULT(1), 119 | char* majorBrand DEFAULT(0), 120 | uint32_t minorVersion DEFAULT(0), 121 | char** compatibleBrands DEFAULT(0), 122 | uint32_t compatibleBrandsCount DEFAULT(0) ); 123 | 124 | /** Dump mp4 file contents as ASCII either to stdout or the 125 | * log callback (@p see MP4SetLogCallback) 126 | * 127 | * Dump is an invaluable debugging tool in that in can reveal all the details 128 | * of the mp4 control structures. However, the output will not make much sense 129 | * until you familiarize yourself with the mp4 specification (or the Quicktime 130 | * File Format specification). 131 | * 132 | 133 | * Note that MP4Dump() will not print the individual values of control tables, 134 | * such as the size of each sample, unless the current log level is at least 135 | * #MP4_LOG_VERBOSE2. @p see MP4LogSetLevel() for how to set this. 136 | * 137 | * @param hFile handle of file to dump. 138 | * @param dumpImplicits prints properties which would not actually be 139 | * written to the mp4 file, but still exist in mp4 control structures. 140 | * ie. they are implicit given the current values of other controlling 141 | * properties. 142 | * 143 | * @return true on success, false on failure. 144 | */ 145 | MP4V2_EXPORT 146 | bool MP4Dump( 147 | MP4FileHandle hFile, 148 | bool dumpImplicits DEFAULT(0) ); 149 | 150 | /** Return a textual summary of an mp4 file. 151 | * 152 | * MP4FileInfo provides a string that contains a textual summary of the 153 | * contents of an mp4 file. This includes the track id's, the track type, 154 | * and track specific information. For example, for a video track, media 155 | * encoding, image size, frame rate, and bitrate are summarized. 156 | * 157 | * Note that the returned string is malloc'ed, so it is the caller's 158 | * responsibility to free() the string. Also note that the returned string 159 | * contains newlines and tabs which may or may not be desirable. 160 | * 161 | * The following is an example of the output of MP4Info(): 162 | @verbatim 163 | Track Type Info 164 | 1 video MPEG-4 Simple @ L3, 119.625 secs, 1008 kbps, 352x288 @ 24.00 fps 165 | 2 audio MPEG-4, 119.327 secs, 128 kbps, 44100 Hz 166 | 3 hint Payload MP4V-ES for track 1 167 | 4 hint Payload mpeg4-generic for track 2 168 | 5 od Object Descriptors 169 | 6 scene BIFS 170 | @endverbatim 171 | * 172 | * @param fileName pathname to mp4 file to summarize. 173 | * On Windows, this should be a UTF-8 encoded string. 174 | * On other platforms, it should be an 8-bit encoding that is 175 | * appropriate for the platform, locale, file system, etc. 176 | * (prefer to use UTF-8 when possible). 177 | * @param trackId specifies track to summarize. If the value is 178 | * #MP4_INVALID_TRACK_ID, the summary info is created for all 179 | * tracks in the file. 180 | * 181 | * @return On success a malloc'd string containing summary information. 182 | * On failure, NULL. 183 | * 184 | * @see MP4Info(). 185 | */ 186 | MP4V2_EXPORT 187 | char* MP4FileInfo( 188 | const char* fileName, 189 | MP4TrackId trackId DEFAULT(MP4_INVALID_TRACK_ID) ); 190 | 191 | /** Accessor for the filename associated with a file handle 192 | * 193 | * @param hFile a file handle 194 | * 195 | * @return the NUL-terminated, UTF-8 encoded filename 196 | * associated with @p hFile 197 | */ 198 | MP4V2_EXPORT 199 | const char* MP4GetFilename( 200 | MP4FileHandle hFile ); 201 | 202 | /** Return a textual summary of an mp4 file. 203 | * 204 | * MP4FileInfo provides a string that contains a textual summary of the 205 | * contents of an mp4 file. This includes the track id's, the track type, 206 | * and track specific information. For example, for a video track, media 207 | * encoding, image size, frame rate, and bitrate are summarized. 208 | * 209 | * Note that the returned string is malloc'ed, so it is the caller's 210 | * responsibility to free() the string. Also note that the returned string 211 | * contains newlines and tabs which may or may not be desirable. 212 | * 213 | * The following is an example of the output of MP4Info(): 214 | @verbatim 215 | Track Type Info 216 | 1 video MPEG-4 Simple @ L3, 119.625 secs, 1008 kbps, 352x288 @ 24.00 fps 217 | 2 audio MPEG-4, 119.327 secs, 128 kbps, 44100 Hz 218 | 3 hint Payload MP4V-ES for track 1 219 | 4 hint Payload mpeg4-generic for track 2 220 | 5 od Object Descriptors 221 | 6 scene BIFS 222 | @endverbatim 223 | * 224 | * @param hFile handle of file to summarize. 225 | * @param trackId specifies track to summarize. If the value is 226 | * #MP4_INVALID_TRACK_ID, the summary info is created for all 227 | * tracks in the file. 228 | * 229 | * @return On success a malloc'd string containing summary information. 230 | * On failure, NULL. 231 | * 232 | * @see MP4FileInfo(). 233 | */ 234 | MP4V2_EXPORT 235 | char* MP4Info( 236 | MP4FileHandle hFile, 237 | MP4TrackId trackId DEFAULT(MP4_INVALID_TRACK_ID) ); 238 | 239 | /** Modify an existing mp4 file. 240 | * 241 | * MP4Modify is the first call that should be used when you want to modify 242 | * an existing mp4 file. It is roughly equivalent to opening a file in 243 | * read/write mode. 244 | * 245 | * Since modifications to an existing mp4 file can result in a sub-optimal 246 | * file layout, you may want to use MP4Optimize() after you have modified 247 | * and closed the mp4 file. 248 | * 249 | * @param fileName pathname of the file to be modified. 250 | * On Windows, this should be a UTF-8 encoded string. 251 | * On other platforms, it should be an 8-bit encoding that is 252 | * appropriate for the platform, locale, file system, etc. 253 | * (prefer to use UTF-8 when possible). 254 | * @param flags currently ignored. 255 | * 256 | * @return On success a handle of the target file for use in subsequent calls 257 | * to the library. 258 | * On error, #MP4_INVALID_FILE_HANDLE. 259 | */ 260 | MP4V2_EXPORT 261 | MP4FileHandle MP4Modify( 262 | const char* fileName, 263 | uint32_t flags DEFAULT(0) ); 264 | 265 | /** Optimize the layout of an mp4 file. 266 | * 267 | * MP4Optimize reads an existing mp4 file and writes a new version of the 268 | * file with the two important changes: 269 | * 270 | * First, the mp4 control information is moved to the beginning of the file. 271 | * (Frequenty it is at the end of the file due to it being constantly 272 | * modified as track samples are added to an mp4 file). This optimization 273 | * is useful in that in allows the mp4 file to be HTTP streamed. 274 | * 275 | * Second, the track samples are interleaved so that the samples for a 276 | * particular instant in time are colocated within the file. This 277 | * eliminates disk seeks during playback of the file which results in 278 | * better performance. 279 | * 280 | * There are also two important side effects of MP4Optimize(): 281 | * 282 | * First, any free blocks within the mp4 file are eliminated. 283 | * 284 | * Second, as a side effect of the sample interleaving process any media 285 | * data chunks that are not actually referenced by the mp4 control 286 | * structures are deleted. This is useful if you have called MP4DeleteTrack() 287 | * which only deletes the control information for a track, and not the 288 | * actual media data. 289 | * 290 | * @param fileName pathname of (existing) file to be optimized. 291 | * On Windows, this should be a UTF-8 encoded string. 292 | * On other platforms, it should be an 8-bit encoding that is 293 | * appropriate for the platform, locale, file system, etc. 294 | * (prefer to use UTF-8 when possible). 295 | * @param newFileName pathname of the new optimized file. 296 | * On Windows, this should be a UTF-8 encoded string. 297 | * On other platforms, it should be an 8-bit encoding that is 298 | * appropriate for the platform, locale, file system, etc. 299 | * (prefer to use UTF-8 when possible). 300 | * If NULL a temporary file in the same directory as the 301 | * fileName will be used and fileName 302 | * will be over-written upon successful completion. 303 | * 304 | * @return true on success, false on failure. 305 | */ 306 | MP4V2_EXPORT 307 | bool MP4Optimize( 308 | const char* fileName, 309 | const char* newFileName DEFAULT(NULL) ); 310 | 311 | 312 | /** Read an existing mp4 file. 313 | * 314 | * MP4Read is the first call that should be used when you want to just 315 | * read an existing mp4 file. It is equivalent to opening a file for 316 | * reading, but in addition the mp4 file is parsed and the control 317 | * information is loaded into memory. Note that actual track samples are not 318 | * read into memory until MP4ReadSample() is called. 319 | * 320 | * @param fileName pathname of the file to be read. 321 | * On Windows, this should be a UTF-8 encoded string. 322 | * On other platforms, it should be an 8-bit encoding that is 323 | * appropriate for the platform, locale, file system, etc. 324 | * (prefer to use UTF-8 when possible). 325 | ( 326 | * @return On success a handle of the file for use in subsequent calls to 327 | * the library. 328 | * On error, #MP4_INVALID_FILE_HANDLE. 329 | */ 330 | MP4V2_EXPORT 331 | MP4FileHandle MP4Read( 332 | const char* fileName ); 333 | 334 | /** Read an existing mp4 file. 335 | * 336 | * MP4ReadProvider is the first call that should be used when you want to just 337 | * read an existing mp4 file. It is equivalent to opening a file for 338 | * reading, but in addition the mp4 file is parsed and the control 339 | * information is loaded into memory. Note that actual track samples are not 340 | * read into memory until MP4ReadSample() is called. 341 | * 342 | * @param fileName pathname of the file to be read. 343 | * On Windows, this should be a UTF-8 encoded string. 344 | * On other platforms, it should be an 8-bit encoding that is 345 | * appropriate for the platform, locale, file system, etc. 346 | * (prefer to use UTF-8 when possible). 347 | * @param fileProvider custom implementation of file I/O operations. 348 | * All functions in structure must be implemented. 349 | * The structure is immediately copied internally. 350 | * 351 | * @return On success a handle of the file for use in subsequent calls to 352 | * the library. 353 | * On error, #MP4_INVALID_FILE_HANDLE. 354 | */ 355 | MP4V2_EXPORT 356 | MP4FileHandle MP4ReadProvider( 357 | const char* fileName, 358 | const MP4FileProvider* fileProvider DEFAULT(NULL) ); 359 | 360 | /** @} ***********************************************************************/ 361 | 362 | #endif /* MP4V2_FILE_H */ 363 | -------------------------------------------------------------------------------- /app/libs/include/mp4v2/file_prop.h: -------------------------------------------------------------------------------- 1 | #ifndef MP4V2_FILE_PROP_H 2 | #define MP4V2_FILE_PROP_H 3 | 4 | /**************************************************************************//** 5 | * 6 | * @defgroup mp4_file_prop MP4v2 File Property 7 | * @{ 8 | * 9 | *****************************************************************************/ 10 | 11 | /* generic props */ 12 | 13 | MP4V2_EXPORT 14 | bool MP4HaveAtom( 15 | MP4FileHandle hFile, 16 | const char* atomName ); 17 | 18 | MP4V2_EXPORT 19 | bool MP4GetIntegerProperty( 20 | MP4FileHandle hFile, 21 | const char* propName, 22 | uint64_t* retval ); 23 | 24 | MP4V2_EXPORT 25 | bool MP4GetFloatProperty( 26 | MP4FileHandle hFile, 27 | const char* propName, 28 | float* retvalue ); 29 | 30 | MP4V2_EXPORT 31 | bool MP4GetStringProperty( 32 | MP4FileHandle hFile, 33 | const char* propName, 34 | const char** retvalue ); 35 | 36 | MP4V2_EXPORT 37 | bool MP4GetBytesProperty( 38 | MP4FileHandle hFile, 39 | const char* propName, 40 | uint8_t** ppValue, 41 | uint32_t* pValueSize ); 42 | 43 | MP4V2_EXPORT 44 | bool MP4SetIntegerProperty( 45 | MP4FileHandle hFile, 46 | const char* propName, 47 | int64_t value ); 48 | 49 | MP4V2_EXPORT 50 | bool MP4SetFloatProperty( 51 | MP4FileHandle hFile, 52 | const char* propName, 53 | float value ); 54 | 55 | MP4V2_EXPORT 56 | bool MP4SetStringProperty( 57 | MP4FileHandle hFile, 58 | const char* propName, 59 | const char* value ); 60 | 61 | MP4V2_EXPORT 62 | bool MP4SetBytesProperty( 63 | MP4FileHandle hFile, 64 | const char* propName, 65 | const uint8_t* pValue, 66 | uint32_t valueSize ); 67 | 68 | /* specific props */ 69 | 70 | MP4V2_EXPORT 71 | MP4Duration MP4GetDuration( MP4FileHandle hFile ); 72 | 73 | /** Get the time scale of the movie (file). 74 | * 75 | * MP4GetTimeScale returns the time scale in units of ticks per second for 76 | * the mp4 file. Caveat: tracks may use the same time scale as the movie 77 | * or may use their own time scale. 78 | * 79 | * @param hFile handle of file for operation. 80 | * 81 | * @return timescale (ticks per second) of the mp4 file. 82 | */ 83 | MP4V2_EXPORT 84 | uint32_t MP4GetTimeScale( MP4FileHandle hFile ); 85 | 86 | /** Set the time scale of the movie (file). 87 | * 88 | * MP4SetTimeScale sets the time scale of the mp4 file. The time scale is 89 | * in the number of clock ticks per second. Caveat: tracks may use the 90 | * same time scale as the movie or may use their own time scale. 91 | * 92 | * @param hFile handle of file for operation. 93 | * @param value desired timescale for the movie. 94 | * 95 | * @return On success, true. On failure, false. 96 | */ 97 | MP4V2_EXPORT 98 | bool MP4SetTimeScale( MP4FileHandle hFile, uint32_t value ); 99 | 100 | /** Change the general timescale of file hFile. 101 | * 102 | * This function changes the general timescale of the file hFile 103 | * to the new timescale value by recalculating all values that depend 104 | * on the timescale in "moov.mvhd". 105 | * 106 | * If the timescale is already equal to value nothing is done. 107 | * 108 | * @param hFile handle of file to change. 109 | * @param value the new timescale. 110 | */ 111 | MP4V2_EXPORT 112 | void MP4ChangeMovieTimeScale( MP4FileHandle hFile, uint32_t value ); 113 | 114 | MP4V2_EXPORT 115 | uint8_t MP4GetODProfileLevel( MP4FileHandle hFile ); 116 | 117 | MP4V2_EXPORT 118 | bool MP4SetODProfileLevel( MP4FileHandle hFile, uint8_t value ); 119 | 120 | MP4V2_EXPORT 121 | uint8_t MP4GetSceneProfileLevel( MP4FileHandle hFile ); 122 | 123 | MP4V2_EXPORT 124 | bool MP4SetSceneProfileLevel( MP4FileHandle hFile, uint8_t value ); 125 | 126 | MP4V2_EXPORT 127 | uint8_t MP4GetVideoProfileLevel( 128 | MP4FileHandle hFile, 129 | MP4TrackId trackId DEFAULT(MP4_INVALID_TRACK_ID) ); 130 | 131 | MP4V2_EXPORT 132 | void MP4SetVideoProfileLevel( MP4FileHandle hFile, uint8_t value ); 133 | 134 | MP4V2_EXPORT 135 | uint8_t MP4GetAudioProfileLevel( MP4FileHandle hFile ); 136 | 137 | MP4V2_EXPORT 138 | void MP4SetAudioProfileLevel( MP4FileHandle hFile, uint8_t value ); 139 | 140 | MP4V2_EXPORT 141 | uint8_t MP4GetGraphicsProfileLevel( MP4FileHandle hFile ); 142 | 143 | MP4V2_EXPORT 144 | bool MP4SetGraphicsProfileLevel( MP4FileHandle hFile, uint8_t value ); 145 | 146 | /** @} ***********************************************************************/ 147 | 148 | #endif /* MP4V2_FILE_PROP_H */ 149 | -------------------------------------------------------------------------------- /app/libs/include/mp4v2/isma.h: -------------------------------------------------------------------------------- 1 | #ifndef MP4V2_ISMA_H 2 | #define MP4V2_ISMA_H 3 | 4 | /**************************************************************************//** 5 | * 6 | * @defgroup mp4_isma MP4v2 ISMA (Internet Streaming Media Alliance) 7 | * @{ 8 | * 9 | *****************************************************************************/ 10 | 11 | /** something */ 12 | typedef struct mp4v2_ismacryp_session_params { 13 | uint32_t scheme_type; 14 | uint16_t scheme_version; 15 | uint8_t key_ind_len; 16 | uint8_t iv_len; 17 | uint8_t selective_enc; 18 | const char* kms_uri; 19 | } mp4v2_ismacrypParams; 20 | 21 | /* 22 | * API to initialize ismacryp properties to sensible defaults 23 | * if input param is null then mallocs a params struct 24 | */ 25 | 26 | MP4V2_EXPORT 27 | mp4v2_ismacrypParams* MP4DefaultISMACrypParams( mp4v2_ismacrypParams* ptr ); 28 | 29 | MP4V2_EXPORT 30 | MP4TrackId MP4AddEncAudioTrack( 31 | MP4FileHandle hFile, 32 | uint32_t timeScale, 33 | MP4Duration sampleDuration, 34 | mp4v2_ismacrypParams* icPp, 35 | uint8_t audioType DEFAULT(MP4_MPEG4_AUDIO_TYPE) ); 36 | 37 | MP4V2_EXPORT 38 | MP4TrackId MP4AddEncVideoTrack( 39 | MP4FileHandle hFile, 40 | uint32_t timeScale, 41 | MP4Duration sampleDuration, 42 | uint16_t width, 43 | uint16_t height, 44 | mp4v2_ismacrypParams* icPp, 45 | uint8_t videoType DEFAULT(MP4_MPEG4_VIDEO_TYPE), 46 | const char* oFormat DEFAULT(NULL) ); 47 | 48 | MP4V2_EXPORT 49 | MP4TrackId MP4AddEncH264VideoTrack( 50 | MP4FileHandle dstFile, 51 | uint32_t timeScale, 52 | MP4Duration sampleDuration, 53 | uint16_t width, 54 | uint16_t height, 55 | MP4FileHandle srcFile, 56 | MP4TrackId srcTrackId, 57 | mp4v2_ismacrypParams* icPp ); 58 | 59 | MP4V2_EXPORT 60 | MP4TrackId MP4EncAndCloneTrack( 61 | MP4FileHandle srcFile, 62 | MP4TrackId srcTrackId, 63 | mp4v2_ismacrypParams* icPp, 64 | MP4FileHandle dstFile DEFAULT(MP4_INVALID_FILE_HANDLE), 65 | MP4TrackId dstHintTrackReferenceTrack DEFAULT(MP4_INVALID_TRACK_ID) ); 66 | 67 | MP4V2_EXPORT 68 | MP4TrackId MP4EncAndCopyTrack( 69 | MP4FileHandle srcFile, 70 | MP4TrackId srcTrackId, 71 | mp4v2_ismacrypParams* icPp, 72 | encryptFunc_t encfcnp, 73 | uint32_t encfcnparam1, 74 | MP4FileHandle dstFile DEFAULT(MP4_INVALID_FILE_HANDLE), 75 | bool applyEdits DEFAULT(false), 76 | MP4TrackId dstHintTrackReferenceTrack DEFAULT(MP4_INVALID_TRACK_ID) ); 77 | 78 | MP4V2_EXPORT 79 | bool MP4MakeIsmaCompliant( 80 | const char* fileName, 81 | bool addIsmaComplianceSdp DEFAULT(true) ); 82 | 83 | MP4V2_EXPORT 84 | char* MP4MakeIsmaSdpIod( 85 | uint8_t videoProfile, 86 | uint32_t videoBitrate, 87 | uint8_t* videoConfig, 88 | uint32_t videoConfigLength, 89 | uint8_t audioProfile, 90 | uint32_t audioBitrate, 91 | uint8_t* audioConfig, 92 | uint32_t audioConfigLength ); 93 | 94 | /** @} ***********************************************************************/ 95 | 96 | #endif /* MP4V2_ISMA_H */ 97 | -------------------------------------------------------------------------------- /app/libs/include/mp4v2/itmf_generic.h: -------------------------------------------------------------------------------- 1 | #ifndef MP4V2_ITMF_GENERIC_H 2 | #define MP4V2_ITMF_GENERIC_H 3 | 4 | /**************************************************************************//** 5 | * 6 | * @defgroup mp4_itmf_generic MP4v2 iTMF (iTunes Metadata Format) Generic 7 | * @{ 8 | * 9 | * This is a low-level API used to manage iTMF metadata. 10 | * 11 | * It provides support for virtually any kind of iTMF metadata item, 12 | * including meaning atoms, sometimes referred to as reverse-DNS meanings. 13 | * Structures are directly modified; ie: there are no fuctions which 14 | * modify values for you. There is little type-safety, logic checks, or 15 | * specifications compliance checks. For these reasons it is recommended 16 | * to use iTMF Tags API when possible. 17 | * 18 | * At the heart of this API is an #MP4ItmfItem which corresponds to an 19 | * iTMF metadata item atom. The item, and any recursive data structures 20 | * contained within require manual memory management. The general 21 | * rule to follow is that you must always check/free a ptr if you intend 22 | * to resize data. In cases where you know the existing data size is 23 | * exactly what is needed, you may overwrite the buffer contents. 24 | * 25 | * Each item always has at least 1 data elements which corresponds to 26 | * a data atom. Additionally, each item has optional mean and 27 | * name values which correspond to mean and name atoms. 28 | * 29 | * Each #MP4ItmfItem has a list of #MP4ItmfData. Similarily, care must 30 | * be taken to manage memory with one key difference; these structures 31 | * also have a valueSize field. If value is NULL then set valueSize=0. 32 | * Otherwise, set valueSize to the size (in bytes) of value buffer. 33 | * 34 | * In rare cases where the number of data elements in a single item 35 | * is > 1, the user must manually free/alloc/copy the elements 36 | * buffer and update size accordingly. 37 | * 38 | * The mp4 file structure is modified only when MP4AddItem(), 39 | * MP4SetItem() and MP4RemoveItem() are used. Simply free'ing 40 | * the item list does not modify the mp4 file. 41 | * 42 | * iTMF Generic read workflow: 43 | * 44 | * @li MP4ItmfGetItems() 45 | * @li inspect each item... 46 | * @li MP4ItmfItemListFree() 47 | * 48 | * iTMF Generic read/modify/remove workflow: 49 | * 50 | * @li MP4ItmfGetItems() 51 | * @li inspect/modify item... 52 | * @li MP4ItmfSetItem() each modified item... 53 | * @li MP4ItmfRemoveItem()... 54 | * @li MP4ItmfItemListFree() 55 | * 56 | * iTMF Generic add workflow: 57 | * 58 | * @li MP4ItmfItemAlloc() 59 | * @li MP4ItmfAddItem() 60 | * @li MP4ItmfItemFree() 61 | * 62 | * @par Warning: 63 | * Care must be taken when using multiple mechanisms to modify an open mp4 64 | * file as it is not thread-safe, nor does it permit overlapping different 65 | * API workflows which have a begin/end to their workflow. That is to say 66 | * do not interleave an iTMF Generic workflow with an iTMF Tags workflow. 67 | * 68 | *****************************************************************************/ 69 | 70 | /** Basic types of value data as enumerated in spec. */ 71 | typedef enum MP4ItmfBasicType_e 72 | { 73 | MP4_ITMF_BT_IMPLICIT = 0, /**< for use with tags for which no type needs to be indicated */ 74 | MP4_ITMF_BT_UTF8 = 1, /**< without any count or null terminator */ 75 | MP4_ITMF_BT_UTF16 = 2, /**< also known as UTF-16BE */ 76 | MP4_ITMF_BT_SJIS = 3, /**< deprecated unless it is needed for special Japanese characters */ 77 | MP4_ITMF_BT_HTML = 6, /**< the HTML file header specifies which HTML version */ 78 | MP4_ITMF_BT_XML = 7, /**< the XML header must identify the DTD or schemas */ 79 | MP4_ITMF_BT_UUID = 8, /**< also known as GUID; stored as 16 bytes in binary (valid as an ID) */ 80 | MP4_ITMF_BT_ISRC = 9, /**< stored as UTF-8 text (valid as an ID) */ 81 | MP4_ITMF_BT_MI3P = 10, /**< stored as UTF-8 text (valid as an ID) */ 82 | MP4_ITMF_BT_GIF = 12, /**< (deprecated) a GIF image */ 83 | MP4_ITMF_BT_JPEG = 13, /**< a JPEG image */ 84 | MP4_ITMF_BT_PNG = 14, /**< a PNG image */ 85 | MP4_ITMF_BT_URL = 15, /**< absolute, in UTF-8 characters */ 86 | MP4_ITMF_BT_DURATION = 16, /**< in milliseconds, 32-bit integer */ 87 | MP4_ITMF_BT_DATETIME = 17, /**< in UTC, counting seconds since midnight, January 1, 1904; 32 or 64-bits */ 88 | MP4_ITMF_BT_GENRES = 18, /**< a list of enumerated values */ 89 | MP4_ITMF_BT_INTEGER = 21, /**< a signed big-endian integer with length one of { 1,2,3,4,8 } bytes */ 90 | MP4_ITMF_BT_RIAA_PA = 24, /**< RIAA parental advisory; { -1=no, 1=yes, 0=unspecified }, 8-bit ingteger */ 91 | MP4_ITMF_BT_UPC = 25, /**< Universal Product Code, in text UTF-8 format (valid as an ID) */ 92 | MP4_ITMF_BT_BMP = 27, /**< Windows bitmap image */ 93 | 94 | MP4_ITMF_BT_UNDEFINED = 255 /**< undefined */ 95 | } MP4ItmfBasicType; 96 | 97 | /** Data structure. 98 | * Models an iTMF data atom contained in an iTMF metadata item atom. 99 | */ 100 | typedef struct MP4ItmfData_s 101 | { 102 | uint8_t typeSetIdentifier; /**< always zero. */ 103 | MP4ItmfBasicType typeCode; /**< iTMF basic type. */ 104 | uint32_t locale; /**< always zero. */ 105 | uint8_t* value; /**< may be NULL. */ 106 | uint32_t valueSize; /**< value size in bytes. */ 107 | } MP4ItmfData; 108 | 109 | /** List of data. */ 110 | typedef struct MP4ItmfDataList_s 111 | { 112 | MP4ItmfData* elements; /**< flat array. NULL when size is zero. */ 113 | uint32_t size; /**< number of elements. */ 114 | } MP4ItmfDataList; 115 | 116 | /** Item structure. 117 | * Models an iTMF metadata item atom contained in an ilst atom. 118 | */ 119 | typedef struct MP4ItmfItem_s 120 | { 121 | void* __handle; /**< internal use only. */ 122 | 123 | char* code; /**< four-char code identifing atom type. NULL-terminated. */ 124 | char* mean; /**< may be NULL. UTF-8 meaning. NULL-terminated. */ 125 | char* name; /**< may be NULL. UTF-8 name. NULL-terminated. */ 126 | MP4ItmfDataList dataList; /**< list of data. can be zero length. */ 127 | } MP4ItmfItem; 128 | 129 | /** List of items. */ 130 | typedef struct MP4ItmfItemList_s 131 | { 132 | MP4ItmfItem* elements; /**< flat array. NULL when size is zero. */ 133 | uint32_t size; /**< number of elements. */ 134 | } MP4ItmfItemList; 135 | 136 | /** Allocate an item on the heap. 137 | * @param code four-char code identifying atom type. NULL-terminated. 138 | * @param numData number of data elements to allocate. Must be >= 1. 139 | * @return newly allocated item. 140 | */ 141 | MP4V2_EXPORT MP4ItmfItem* 142 | MP4ItmfItemAlloc( const char* code, uint32_t numData ); 143 | 144 | /** Free an item (deep free). 145 | * @param item to be free'd. 146 | */ 147 | MP4V2_EXPORT void 148 | MP4ItmfItemFree( MP4ItmfItem* item ); 149 | 150 | /** Free an item list (deep free). 151 | * @param itemList to be free'd. 152 | */ 153 | MP4V2_EXPORT void 154 | MP4ItmfItemListFree( MP4ItmfItemList* itemList ); 155 | 156 | /** Get list of all items from file. 157 | * @param hFile handle of file to operate on. 158 | * @return On succes, list of items, which must be free'd. On failure, NULL. 159 | */ 160 | MP4V2_EXPORT MP4ItmfItemList* 161 | MP4ItmfGetItems( MP4FileHandle hFile ); 162 | 163 | /** Get list of items by code from file. 164 | * @param hFile handle of file to operate on. 165 | * @param code four-char code identifying atom type. NULL-terminated. 166 | * @return On succes, list of items, which must be free'd. On failure, NULL. 167 | */ 168 | MP4V2_EXPORT MP4ItmfItemList* 169 | MP4ItmfGetItemsByCode( MP4FileHandle hFile, const char* code ); 170 | 171 | /** Get list of items by meaning from file. 172 | * Implicitly only returns atoms of code @b{----}. 173 | * @param hFile handle of file to operate on. 174 | * @param meaning UTF-8 meaning. NULL-terminated. 175 | * @param name may be NULL. UTF-8 name. NULL-terminated. 176 | * @return On succes, list of items, which must be free'd. On failure, NULL. 177 | */ 178 | MP4V2_EXPORT MP4ItmfItemList* 179 | MP4ItmfGetItemsByMeaning( MP4FileHandle hFile, const char* meaning, const char* name ); 180 | 181 | /** Add an item to file. 182 | * @param hFile handle of file to operate on. 183 | * @param item object to add. 184 | * @return true on success, false on failure. 185 | */ 186 | MP4V2_EXPORT bool 187 | MP4ItmfAddItem( MP4FileHandle hFile, const MP4ItmfItem* item ); 188 | 189 | /** Overwrite an existing item in file. 190 | * @param hFile handle of file to operate on. 191 | * @param item object to overwrite. Must have a valid index obtained from prior get. 192 | * @return true on success, false on failure. 193 | */ 194 | MP4V2_EXPORT bool 195 | MP4ItmfSetItem( MP4FileHandle hFile, const MP4ItmfItem* item ); 196 | 197 | /** Remove an existing item from file. 198 | * @param hFile handle of file to operate on. 199 | * @param item object to remove. Must have a valid index obtained from prior get. 200 | * @return true on success, false on failure. 201 | */ 202 | MP4V2_EXPORT bool 203 | MP4ItmfRemoveItem( MP4FileHandle hFile, const MP4ItmfItem* item ); 204 | 205 | /** @} ***********************************************************************/ 206 | 207 | #endif /* MP4V2_ITMF_GENERIC_H */ 208 | -------------------------------------------------------------------------------- /app/libs/include/mp4v2/itmf_tags.h: -------------------------------------------------------------------------------- 1 | #ifndef MP4V2_ITMF_TAGS_H 2 | #define MP4V2_ITMF_TAGS_H 3 | 4 | /**************************************************************************//** 5 | * 6 | * @defgroup mp4_itmf_tags MP4v2 iTMF (iTunes Metadata Format) Tags 7 | * @{ 8 | * 9 | * This is a high-level API used to manage iTMF metadata. 10 | * 11 | * It provides more type-safety and simplified memory management as compared 12 | * to iTMF Generic API. 13 | * 14 | * At the heart of this API is a read-only structure that holds all known 15 | * items and their current values. The value is always a pointer which if 16 | * NULL indicates its corresponding atom does not exist. Thus, one must 17 | * always check if the pointer is non-NULL before attempting to extract 18 | * its value. 19 | * 20 | * The structure may not be directly modified. Instead, set functions 21 | * corresponding to each item are used to modify the backing-store of 22 | * the read-only structure. Setting the value ptr to NULL will effectively 23 | * remove it. Setting the value ptr to real data will immediately make a 24 | * copy of the value in the backing-store and the read-only structure 25 | * will correctly reflect the change. 26 | * 27 | * The hidden data cache memory is automatically managed. Thus the user need 28 | * only guarantee the data is available during the lifetime of the set-function 29 | * call. 30 | * 31 | * iTMF Tags read workflow: 32 | * 33 | * @li MP4TagsAlloc() 34 | * @li MP4TagsFetch() 35 | * @li inspect each tag of interest... 36 | * @li MP4TagsStore() (if modified) 37 | * @li MP4TagsFree() 38 | * 39 | * iTMF Tags read/modify/add/remove workflow: 40 | * 41 | * @li MP4TagsAlloc() 42 | * @li MP4TagsFetch() 43 | * @li inspect each tag of interest... 44 | * @li MP4TagsSetName(), MP4TagsSetArtist()... 45 | * @li MP4TagsStore() 46 | * @li MP4TagsFree() 47 | * 48 | * @par Warning: 49 | * Care must be taken when using multiple mechanisms to modify an open mp4 50 | * file as it is not thread-safe, nor does it permit overlapping different 51 | * API workflows which have a begin/end to their workflow. That is to say 52 | * do not interleave an iTMF Generic workflow with an iTMF Tags workflow. 53 | * 54 | *****************************************************************************/ 55 | 56 | /** Enumeration of possible MP4TagArtwork::type values. */ 57 | typedef enum MP4TagArtworkType_e 58 | { 59 | MP4_ART_UNDEFINED = 0, 60 | MP4_ART_BMP = 1, 61 | MP4_ART_GIF = 2, 62 | MP4_ART_JPEG = 3, 63 | MP4_ART_PNG = 4 64 | } MP4TagArtworkType; 65 | 66 | /** Data object representing a single piece of artwork. */ 67 | typedef struct MP4TagArtwork_s 68 | { 69 | void* data; /**< raw picture data */ 70 | uint32_t size; /**< data size in bytes */ 71 | MP4TagArtworkType type; /**< data type */ 72 | } MP4TagArtwork; 73 | 74 | typedef struct MP4TagTrack_s 75 | { 76 | uint16_t index; 77 | uint16_t total; 78 | } MP4TagTrack; 79 | 80 | typedef struct MP4TagDisk_s 81 | { 82 | uint16_t index; 83 | uint16_t total; 84 | } MP4TagDisk; 85 | 86 | /** Tags convenience structure. 87 | * 88 | * This structure is used in the tags convenience API which allows for 89 | * simplified retrieval and modification of the majority of known tags. 90 | * 91 | * This is a read-only structure and each tag is present if and only if the 92 | * pointer is a non-NULL value. The actual data is backed by a hidden 93 | * data cache which is only updated when the appropriate metadata set 94 | * function is used, or if MP4TagsFetch() is invoked. Thus, if other API 95 | * is used to manipulate relevent atom structure of the MP4 file, the user 96 | * is responsible for re-fetching the data in this structure. 97 | */ 98 | typedef struct MP4Tags_s 99 | { 100 | void* __handle; /* internal use only */ 101 | 102 | const char* name; 103 | const char* artist; 104 | const char* albumArtist; 105 | const char* album; 106 | const char* grouping; 107 | const char* composer; 108 | const char* comments; 109 | const char* genre; 110 | const uint16_t* genreType; 111 | const char* releaseDate; 112 | const MP4TagTrack* track; 113 | const MP4TagDisk* disk; 114 | const uint16_t* tempo; 115 | const uint8_t* compilation; 116 | 117 | const char* tvShow; 118 | const char* tvNetwork; 119 | const char* tvEpisodeID; 120 | const uint32_t* tvSeason; 121 | const uint32_t* tvEpisode; 122 | 123 | const char* description; 124 | const char* longDescription; 125 | const char* lyrics; 126 | 127 | const char* sortName; 128 | const char* sortArtist; 129 | const char* sortAlbumArtist; 130 | const char* sortAlbum; 131 | const char* sortComposer; 132 | const char* sortTVShow; 133 | 134 | const MP4TagArtwork* artwork; 135 | uint32_t artworkCount; 136 | 137 | const char* copyright; 138 | const char* encodingTool; 139 | const char* encodedBy; 140 | const char* purchaseDate; 141 | 142 | const uint8_t* podcast; 143 | const char* keywords; /* TODO: Needs testing */ 144 | const char* category; 145 | 146 | const uint8_t* hdVideo; 147 | const uint8_t* mediaType; 148 | const uint8_t* contentRating; 149 | const uint8_t* gapless; 150 | 151 | const char* iTunesAccount; 152 | const uint8_t* iTunesAccountType; 153 | const uint32_t* iTunesCountry; 154 | const uint32_t* contentID; 155 | const uint32_t* artistID; 156 | const uint64_t* playlistID; 157 | const uint32_t* genreID; 158 | const uint32_t* composerID; 159 | const char* xid; 160 | } MP4Tags; 161 | 162 | /** Allocate tags convenience structure for reading and settings tags. 163 | * 164 | * This function allocates a new structure which represents a snapshot 165 | * of all the tags therein, tracking if the tag is missing, 166 | * or present and with value. It is the caller's responsibility to free 167 | * the structure with MP4TagsFree(). 168 | * 169 | * @return structure with all tags missing. 170 | */ 171 | MP4V2_EXPORT 172 | const MP4Tags* MP4TagsAlloc( void ); 173 | 174 | /** Fetch data from mp4 file and populate structure. 175 | * 176 | * The tags structure and its hidden data-cache is updated to 177 | * reflect the actual tags values found in the hFile. 178 | * 179 | * @param tags structure to fetch (write) into. 180 | * @param hFile handle of file to fetch data from. 181 | * 182 | * @return true on success, false on failure. 183 | */ 184 | MP4V2_EXPORT 185 | bool MP4TagsFetch( const MP4Tags* tags, MP4FileHandle hFile ); 186 | 187 | /** Store data to mp4 file from structure. 188 | * 189 | * The tags structure is pushed out to the mp4 file, 190 | * adding tags if needed, removing tags if needed, and updating 191 | * the values to modified tags. 192 | * 193 | * @param tags structure to store (read) from. 194 | * @param hFile handle of file to store data to. 195 | * 196 | * @return true on success, false on failure. 197 | */ 198 | MP4V2_EXPORT 199 | bool MP4TagsStore( const MP4Tags* tags, MP4FileHandle hFile ); 200 | 201 | /** Free tags convenience structure. 202 | * 203 | * This function frees memory associated with the structure. 204 | * 205 | * @param tags structure to destroy. 206 | */ 207 | MP4V2_EXPORT 208 | void MP4TagsFree( const MP4Tags* tags ); 209 | 210 | /** Accessor that indicates whether a tags structure 211 | * contains any metadata 212 | * 213 | * @param tags the structure to inspect 214 | * 215 | * @param hasMetadata populated with false if @p tags 216 | * contains no metadata, true if @p tags contains metadata 217 | * 218 | * @retval false error determining if @p tags contains 219 | * metadata 220 | * 221 | * @retval true successfully determined if @p tags contains 222 | * metadata 223 | */ 224 | MP4V2_EXPORT 225 | bool MP4TagsHasMetadata ( const MP4Tags* tags, bool *hasMetadata ); 226 | 227 | MP4V2_EXPORT bool MP4TagsSetName ( const MP4Tags*, const char* ); 228 | MP4V2_EXPORT bool MP4TagsSetArtist ( const MP4Tags*, const char* ); 229 | MP4V2_EXPORT bool MP4TagsSetAlbumArtist ( const MP4Tags*, const char* ); 230 | MP4V2_EXPORT bool MP4TagsSetAlbum ( const MP4Tags*, const char* ); 231 | MP4V2_EXPORT bool MP4TagsSetGrouping ( const MP4Tags*, const char* ); 232 | MP4V2_EXPORT bool MP4TagsSetComposer ( const MP4Tags*, const char* ); 233 | MP4V2_EXPORT bool MP4TagsSetComments ( const MP4Tags*, const char* ); 234 | MP4V2_EXPORT bool MP4TagsSetGenre ( const MP4Tags*, const char* ); 235 | MP4V2_EXPORT bool MP4TagsSetGenreType ( const MP4Tags*, const uint16_t* ); 236 | MP4V2_EXPORT bool MP4TagsSetReleaseDate ( const MP4Tags*, const char* ); 237 | MP4V2_EXPORT bool MP4TagsSetTrack ( const MP4Tags*, const MP4TagTrack* ); 238 | MP4V2_EXPORT bool MP4TagsSetDisk ( const MP4Tags*, const MP4TagDisk* ); 239 | MP4V2_EXPORT bool MP4TagsSetTempo ( const MP4Tags*, const uint16_t* ); 240 | MP4V2_EXPORT bool MP4TagsSetCompilation ( const MP4Tags*, const uint8_t* ); 241 | 242 | MP4V2_EXPORT bool MP4TagsSetTVShow ( const MP4Tags*, const char* ); 243 | MP4V2_EXPORT bool MP4TagsSetTVNetwork ( const MP4Tags*, const char* ); 244 | MP4V2_EXPORT bool MP4TagsSetTVEpisodeID ( const MP4Tags*, const char* ); 245 | MP4V2_EXPORT bool MP4TagsSetTVSeason ( const MP4Tags*, const uint32_t* ); 246 | MP4V2_EXPORT bool MP4TagsSetTVEpisode ( const MP4Tags*, const uint32_t* ); 247 | 248 | MP4V2_EXPORT bool MP4TagsSetDescription ( const MP4Tags*, const char* ); 249 | MP4V2_EXPORT bool MP4TagsSetLongDescription ( const MP4Tags*, const char* ); 250 | MP4V2_EXPORT bool MP4TagsSetLyrics ( const MP4Tags*, const char* ); 251 | 252 | MP4V2_EXPORT bool MP4TagsSetSortName ( const MP4Tags*, const char* ); 253 | MP4V2_EXPORT bool MP4TagsSetSortArtist ( const MP4Tags*, const char* ); 254 | MP4V2_EXPORT bool MP4TagsSetSortAlbumArtist ( const MP4Tags*, const char* ); 255 | MP4V2_EXPORT bool MP4TagsSetSortAlbum ( const MP4Tags*, const char* ); 256 | MP4V2_EXPORT bool MP4TagsSetSortComposer ( const MP4Tags*, const char* ); 257 | MP4V2_EXPORT bool MP4TagsSetSortTVShow ( const MP4Tags*, const char* ); 258 | 259 | MP4V2_EXPORT bool MP4TagsAddArtwork ( const MP4Tags*, MP4TagArtwork* ); 260 | MP4V2_EXPORT bool MP4TagsSetArtwork ( const MP4Tags*, uint32_t, MP4TagArtwork* ); 261 | MP4V2_EXPORT bool MP4TagsRemoveArtwork ( const MP4Tags*, uint32_t ); 262 | 263 | MP4V2_EXPORT bool MP4TagsSetCopyright ( const MP4Tags*, const char* ); 264 | MP4V2_EXPORT bool MP4TagsSetEncodingTool ( const MP4Tags*, const char* ); 265 | MP4V2_EXPORT bool MP4TagsSetEncodedBy ( const MP4Tags*, const char* ); 266 | MP4V2_EXPORT bool MP4TagsSetPurchaseDate ( const MP4Tags*, const char* ); 267 | 268 | MP4V2_EXPORT bool MP4TagsSetPodcast ( const MP4Tags*, const uint8_t* ); 269 | MP4V2_EXPORT bool MP4TagsSetKeywords ( const MP4Tags*, const char* ); 270 | MP4V2_EXPORT bool MP4TagsSetCategory ( const MP4Tags*, const char* ); 271 | 272 | MP4V2_EXPORT bool MP4TagsSetHDVideo ( const MP4Tags*, const uint8_t* ); 273 | MP4V2_EXPORT bool MP4TagsSetMediaType ( const MP4Tags*, const uint8_t* ); 274 | MP4V2_EXPORT bool MP4TagsSetContentRating ( const MP4Tags*, const uint8_t* ); 275 | MP4V2_EXPORT bool MP4TagsSetGapless ( const MP4Tags*, const uint8_t* ); 276 | 277 | MP4V2_EXPORT bool MP4TagsSetITunesAccount ( const MP4Tags*, const char* ); 278 | MP4V2_EXPORT bool MP4TagsSetITunesAccountType ( const MP4Tags*, const uint8_t* ); 279 | MP4V2_EXPORT bool MP4TagsSetITunesCountry ( const MP4Tags*, const uint32_t* ); 280 | MP4V2_EXPORT bool MP4TagsSetContentID ( const MP4Tags*, const uint32_t* ); 281 | MP4V2_EXPORT bool MP4TagsSetArtistID ( const MP4Tags*, const uint32_t* ); 282 | MP4V2_EXPORT bool MP4TagsSetPlaylistID ( const MP4Tags*, const uint64_t* ); 283 | MP4V2_EXPORT bool MP4TagsSetGenreID ( const MP4Tags*, const uint32_t* ); 284 | MP4V2_EXPORT bool MP4TagsSetComposerID ( const MP4Tags*, const uint32_t* ); 285 | MP4V2_EXPORT bool MP4TagsSetXID ( const MP4Tags*, const char* ); 286 | 287 | /** @} ***********************************************************************/ 288 | 289 | #endif /* MP4V2_ITMF_TAGS_H */ 290 | -------------------------------------------------------------------------------- /app/libs/include/mp4v2/mp4v2.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The contents of this file are subject to the Mozilla Public 3 | * License Version 1.1 (the "License"); you may not use this file 4 | * except in compliance with the License. You may obtain a copy of 5 | * the License at http://www.mozilla.org/MPL/ 6 | * 7 | * Software distributed under the License is distributed on an "AS 8 | * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or 9 | * implied. See the License for the specific language governing 10 | * rights and limitations under the License. 11 | * 12 | * The Original Code is MPEG4IP. 13 | * 14 | * The Initial Developer of the Original Code is Cisco Systems Inc. 15 | * Portions created by Cisco Systems Inc. are 16 | * Copyright (C) Cisco Systems Inc. 2001 - 2005. All Rights Reserved. 17 | * 18 | * 3GPP features implementation is based on 3GPP's TS26.234-v5.60, 19 | * and was contributed by Ximpo Group Ltd. 20 | * 21 | * Portions created by Ximpo Group Ltd. are 22 | * Copyright (C) Ximpo Group Ltd. 2003, 2004. All Rights Reserved. 23 | * 24 | * Contributor(s): 25 | * Dave Mackie dmackie@cisco.com 26 | * Alix Marchandise-Franquet alix@cisco.com 27 | * Ximpo Group Ltd. mp4v2@ximpo.com 28 | * Bill May wmay@cisco.com 29 | */ 30 | #ifndef MP4V2_MP4V2_H 31 | #define MP4V2_MP4V2_H 32 | 33 | /*****************************************************************************/ 34 | 35 | #include 36 | #include 37 | 38 | /*****************************************************************************/ 39 | 40 | /* exploit C++ ability of default values for function parameters */ 41 | #if defined( DEFAULT ) 42 | # define __MP4V2_SAVE_DEFAULT DEFAULT 43 | #endif 44 | #undef DEFAULT 45 | #if defined( __cplusplus ) 46 | # define DEFAULT(x) =x 47 | #else 48 | # define DEFAULT(x) 49 | #endif 50 | 51 | #ifdef __cplusplus 52 | extern "C" { 53 | #endif 54 | 55 | /*****************************************************************************/ 56 | 57 | #include 58 | #include 59 | #include 60 | #include 61 | #include 62 | #include 63 | #include 64 | #include 65 | #include 66 | #include 67 | #include 68 | 69 | /*****************************************************************************/ 70 | 71 | /* restore macro DEFAULT to state prior to mp4v2 headers */ 72 | #undef DEFAULT 73 | #if defined( __MP4V2_SAVE_DEFAULT ) 74 | # define DEFAULT __MP4V2_SAVE_DEFAULT 75 | #endif 76 | 77 | #ifdef __cplusplus 78 | } // extern "C" 79 | #endif 80 | 81 | /*****************************************************************************/ 82 | 83 | #endif /* MP4V2_MP4V2_H */ 84 | -------------------------------------------------------------------------------- /app/libs/include/mp4v2/platform.h: -------------------------------------------------------------------------------- 1 | #ifndef MP4V2_PLATFORM_H 2 | #define MP4V2_PLATFORM_H 3 | 4 | /*****************************************************************************/ 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | // Thanks, MSFT, for making C99 a total PITA. Declare this not to define any stdint stuff; this is useful 11 | // if you're going to be using mp4v2 on windows with some other library that defines its own stdint. 12 | // TODO msft has finally re-included stdint in vs2010, so maybe at some point in the future this won't be needed. 13 | #ifndef MP4V2_NO_STDINT_DEFS 14 | #if defined( _WIN32 ) && !defined( __MINGW32__ ) 15 | typedef char int8_t; 16 | typedef short int16_t; 17 | typedef int int32_t; 18 | typedef long long int64_t; 19 | 20 | typedef unsigned char uint8_t; 21 | typedef unsigned short uint16_t; 22 | typedef unsigned int uint32_t; 23 | typedef unsigned long long uint64_t; 24 | #else 25 | #include 26 | #endif 27 | #endif 28 | 29 | #if defined( _WIN32 ) || defined( __MINGW32__ ) 30 | # if defined( MP4V2_EXPORTS ) 31 | # define MP4V2_EXPORT __declspec(dllexport) 32 | # elif defined( MP4V2_USE_DLL_IMPORT ) || !defined( MP4V2_USE_STATIC_LIB ) 33 | # define MP4V2_EXPORT __declspec(dllimport) 34 | # else 35 | # define MP4V2_EXPORT 36 | # endif 37 | #else 38 | # define MP4V2_EXPORT __attribute__((visibility("default"))) 39 | #endif 40 | 41 | #if defined( __GNUC__ ) 42 | # define MP4V2_DEPRECATED __attribute__((deprecated)) 43 | #else 44 | # define MP4V2_DEPRECATED 45 | #endif 46 | 47 | /****************************************************************************** 48 | * 49 | * TODO-KB: cleanup -- absolutely no need for a C-API to fuss with reserved 50 | * C++ keywords. This will involve changing the public interface and current 51 | * plan of action: 52 | * 53 | * typdef enum { 54 | * mp4_false, 55 | * mp4_true, 56 | * } mp4_bool_t; 57 | * 58 | * followed by updating all public signatures and implementation. 59 | */ 60 | 61 | #ifndef FALSE 62 | #define FALSE 0 63 | #endif 64 | 65 | #ifndef TRUE 66 | #define TRUE 1 67 | #endif 68 | 69 | #if !defined( __cplusplus ) 70 | #ifndef bool 71 | #if SIZEOF_BOOL == 8 72 | typedef uint64_t bool; 73 | #else 74 | #if SIZEOF_BOOL == 4 75 | typedef uint32_t bool; 76 | #else 77 | #if SIZEOF_BOOL == 2 78 | typedef uint16_t bool; 79 | #else 80 | typedef unsigned char bool; 81 | #endif 82 | #endif 83 | #endif 84 | #ifndef false 85 | #define false FALSE 86 | #endif 87 | #ifndef true 88 | #define true TRUE 89 | #endif 90 | #endif 91 | #endif 92 | 93 | /*****************************************************************************/ 94 | 95 | #endif /* MP4V2_PLATFORM_H */ 96 | -------------------------------------------------------------------------------- /app/libs/include/mp4v2/project.h: -------------------------------------------------------------------------------- 1 | #ifndef MP4V2_PROJECT_H 2 | #define MP4V2_PROJECT_H 3 | 4 | /*****************************************************************************/ 5 | 6 | #define MP4V2_PROJECT_name "MP4v2" 7 | #define MP4V2_PROJECT_name_lower "mp4v2" 8 | #define MP4V2_PROJECT_name_upper "MP4V2" 9 | #define MP4V2_PROJECT_name_formal "MP4v2 2.0.0" 10 | #define MP4V2_PROJECT_url_website "http://code.google.com/p/mp4v2" 11 | #define MP4V2_PROJECT_url_downloads "http://code.google.com/p/mp4v2/downloads/list" 12 | #define MP4V2_PROJECT_url_discussion "http://groups.google.com/group/mp4v2" 13 | #define MP4V2_PROJECT_irc "irc://irc.freenode.net/handbrake" 14 | #define MP4V2_PROJECT_bugreport "" 15 | #define MP4V2_PROJECT_version "2.0.0" 16 | #define MP4V2_PROJECT_version_hex 0x00020000 17 | #define MP4V2_PROJECT_version_major 2 18 | #define MP4V2_PROJECT_version_minor 0 19 | #define MP4V2_PROJECT_version_point 0 20 | #define MP4V2_PROJECT_repo_url "https://mp4v2.googlecode.com/svn/releases/2.0.0" 21 | #define MP4V2_PROJECT_repo_branch "2.0.0" 22 | #define MP4V2_PROJECT_repo_root "https://mp4v2.googlecode.com/svn" 23 | #define MP4V2_PROJECT_repo_uuid "6e6572fa-98a6-11dd-ad9f-f77439c74b79" 24 | #define MP4V2_PROJECT_repo_rev 493 25 | #define MP4V2_PROJECT_repo_date "2012-05-20 15:16:54 -0700 (Sun, 20 May 2012)" 26 | #define MP4V2_PROJECT_repo_type "stable" 27 | #define MP4V2_PROJECT_build "Sun May 20 15:18:53 PDT 2012" 28 | 29 | /*****************************************************************************/ 30 | 31 | #endif /* MP4V2_PROJECT_H */ 32 | -------------------------------------------------------------------------------- /app/libs/include/mp4v2/project.h.in: -------------------------------------------------------------------------------- 1 | #ifndef MP4V2_PROJECT_H 2 | #define MP4V2_PROJECT_H 3 | 4 | /*****************************************************************************/ 5 | 6 | #define MP4V2_PROJECT_name "@PROJECT_name@" 7 | #define MP4V2_PROJECT_name_lower "@PROJECT_name_lower@" 8 | #define MP4V2_PROJECT_name_upper "@PROJECT_name_upper@" 9 | #define MP4V2_PROJECT_name_formal "@PROJECT_name_formal@" 10 | #define MP4V2_PROJECT_url_website "@PROJECT_url_website@" 11 | #define MP4V2_PROJECT_url_downloads "@PROJECT_url_downloads@" 12 | #define MP4V2_PROJECT_url_discussion "@PROJECT_url_discussion@" 13 | #define MP4V2_PROJECT_irc "@PROJECT_irc@" 14 | #define MP4V2_PROJECT_bugreport "@PROJECT_bugreport@" 15 | #define MP4V2_PROJECT_version "@PROJECT_version@" 16 | #define MP4V2_PROJECT_version_hex @PROJECT_version_hex@ 17 | #define MP4V2_PROJECT_version_major @PROJECT_version_major@ 18 | #define MP4V2_PROJECT_version_minor @PROJECT_version_minor@ 19 | #define MP4V2_PROJECT_version_point @PROJECT_version_point@ 20 | #define MP4V2_PROJECT_repo_url "@PROJECT_repo_url@" 21 | #define MP4V2_PROJECT_repo_branch "@PROJECT_repo_branch@" 22 | #define MP4V2_PROJECT_repo_root "@PROJECT_repo_root@" 23 | #define MP4V2_PROJECT_repo_uuid "@PROJECT_repo_uuid@" 24 | #define MP4V2_PROJECT_repo_rev @PROJECT_repo_rev@ 25 | #define MP4V2_PROJECT_repo_date "@PROJECT_repo_date@" 26 | #define MP4V2_PROJECT_repo_type "@PROJECT_repo_type@" 27 | #define MP4V2_PROJECT_build "@PROJECT_build@" 28 | 29 | /*****************************************************************************/ 30 | 31 | #endif /* MP4V2_PROJECT_H */ 32 | -------------------------------------------------------------------------------- /app/libs/include/mp4v2/streaming.h: -------------------------------------------------------------------------------- 1 | #ifndef MP4V2_STREAMING_H 2 | #define MP4V2_STREAMING_H 3 | 4 | /**************************************************************************//** 5 | * 6 | * @defgroup mp4_hint MP4v2 Streaming 7 | * @{ 8 | * 9 | *****************************************************************************/ 10 | 11 | MP4V2_EXPORT 12 | bool MP4GetHintTrackRtpPayload( 13 | MP4FileHandle hFile, 14 | MP4TrackId hintTrackId, 15 | char** ppPayloadName DEFAULT(NULL), 16 | uint8_t* pPayloadNumber DEFAULT(NULL), 17 | uint16_t* pMaxPayloadSize DEFAULT(NULL), 18 | char** ppEncodingParams DEFAULT(NULL) ); 19 | 20 | #define MP4_SET_DYNAMIC_PAYLOAD 0xff 21 | 22 | MP4V2_EXPORT 23 | bool MP4SetHintTrackRtpPayload( 24 | MP4FileHandle hFile, 25 | MP4TrackId hintTrackId, 26 | const char* pPayloadName, 27 | uint8_t* pPayloadNumber, 28 | uint16_t maxPayloadSize DEFAULT(0), 29 | const char * encode_params DEFAULT(NULL), 30 | bool include_rtp_map DEFAULT(true), 31 | bool include_mpeg4_esid DEFAULT(true) ); 32 | 33 | MP4V2_EXPORT 34 | const char* MP4GetSessionSdp( 35 | MP4FileHandle hFile ); 36 | 37 | MP4V2_EXPORT 38 | bool MP4SetSessionSdp( 39 | MP4FileHandle hFile, 40 | const char* sdpString ); 41 | 42 | MP4V2_EXPORT 43 | bool MP4AppendSessionSdp( 44 | MP4FileHandle hFile, 45 | const char* sdpString ); 46 | 47 | MP4V2_EXPORT 48 | const char* MP4GetHintTrackSdp( 49 | MP4FileHandle hFile, 50 | MP4TrackId hintTrackId ); 51 | 52 | MP4V2_EXPORT 53 | bool MP4SetHintTrackSdp( 54 | MP4FileHandle hFile, 55 | MP4TrackId hintTrackId, 56 | const char* sdpString ); 57 | 58 | MP4V2_EXPORT 59 | bool MP4AppendHintTrackSdp( 60 | MP4FileHandle hFile, 61 | MP4TrackId hintTrackId, 62 | const char* sdpString ); 63 | 64 | MP4V2_EXPORT 65 | MP4TrackId MP4GetHintTrackReferenceTrackId( 66 | MP4FileHandle hFile, 67 | MP4TrackId hintTrackId ); 68 | 69 | MP4V2_EXPORT 70 | bool MP4ReadRtpHint( 71 | MP4FileHandle hFile, 72 | MP4TrackId hintTrackId, 73 | MP4SampleId hintSampleId, 74 | uint16_t* pNumPackets DEFAULT(NULL) ); 75 | 76 | MP4V2_EXPORT 77 | uint16_t MP4GetRtpHintNumberOfPackets( 78 | MP4FileHandle hFile, 79 | MP4TrackId hintTrackId ); 80 | 81 | MP4V2_EXPORT 82 | int8_t MP4GetRtpPacketBFrame( 83 | MP4FileHandle hFile, 84 | MP4TrackId hintTrackId, 85 | uint16_t packetIndex ); 86 | 87 | MP4V2_EXPORT 88 | int32_t MP4GetRtpPacketTransmitOffset( 89 | MP4FileHandle hFile, 90 | MP4TrackId hintTrackId, 91 | uint16_t packetIndex ); 92 | 93 | MP4V2_EXPORT 94 | bool MP4ReadRtpPacket( 95 | MP4FileHandle hFile, 96 | MP4TrackId hintTrackId, 97 | uint16_t packetIndex, 98 | uint8_t** ppBytes, 99 | uint32_t* pNumBytes, 100 | uint32_t ssrc DEFAULT(0), 101 | bool includeHeader DEFAULT(true), 102 | bool includePayload DEFAULT(true) ); 103 | 104 | MP4V2_EXPORT 105 | MP4Timestamp MP4GetRtpTimestampStart( 106 | MP4FileHandle hFile, 107 | MP4TrackId hintTrackId ); 108 | 109 | MP4V2_EXPORT 110 | bool MP4SetRtpTimestampStart( 111 | MP4FileHandle hFile, 112 | MP4TrackId hintTrackId, 113 | MP4Timestamp rtpStart ); 114 | 115 | MP4V2_EXPORT 116 | bool MP4AddRtpHint( 117 | MP4FileHandle hFile, 118 | MP4TrackId hintTrackId ); 119 | 120 | MP4V2_EXPORT 121 | bool MP4AddRtpVideoHint( 122 | MP4FileHandle hFile, 123 | MP4TrackId hintTrackId, 124 | bool isBframe DEFAULT(false), 125 | uint32_t timestampOffset DEFAULT(0) ); 126 | 127 | MP4V2_EXPORT 128 | bool MP4AddRtpPacket( 129 | MP4FileHandle hFile, 130 | MP4TrackId hintTrackId, 131 | bool setMbit DEFAULT(false), 132 | int32_t transmitOffset DEFAULT(0) ); 133 | 134 | MP4V2_EXPORT 135 | bool MP4AddRtpImmediateData( 136 | MP4FileHandle hFile, 137 | MP4TrackId hintTrackId, 138 | const uint8_t* pBytes, 139 | uint32_t numBytes ); 140 | 141 | MP4V2_EXPORT 142 | bool MP4AddRtpSampleData( 143 | MP4FileHandle hFile, 144 | MP4TrackId hintTrackId, 145 | MP4SampleId sampleId, 146 | uint32_t dataOffset, 147 | uint32_t dataLength ); 148 | 149 | MP4V2_EXPORT 150 | bool MP4AddRtpESConfigurationPacket( 151 | MP4FileHandle hFile, 152 | MP4TrackId hintTrackId ); 153 | 154 | MP4V2_EXPORT 155 | bool MP4WriteRtpHint( 156 | MP4FileHandle hFile, 157 | MP4TrackId hintTrackId, 158 | MP4Duration duration, 159 | bool isSyncSample DEFAULT(true) ); 160 | 161 | /** @} ***********************************************************************/ 162 | 163 | #endif /* MP4V2_STREAMING_H */ 164 | -------------------------------------------------------------------------------- /app/libs/include/mp4v2/track.h: -------------------------------------------------------------------------------- 1 | #ifndef MP4V2_TRACK_H 2 | #define MP4V2_TRACK_H 3 | 4 | /**************************************************************************//** 5 | * 6 | * @defgroup mp4_track MP4v2 Track 7 | * @{ 8 | * 9 | *****************************************************************************/ 10 | 11 | /** Add a user defined track. 12 | * 13 | * MP4AddTrack adds a user defined track to the mp4 file. Care should be 14 | * taken to avoid any of the standardized track type names. A useful 15 | * convention is use only uppercase characters for user defined track types. 16 | * The string should be exactly four characters in length, e.g. "MINE". 17 | * 18 | * Note this should not be used to add any of the known track types defined 19 | * in the MP4 standard (ISO/IEC 14496-1:2001). 20 | * 21 | * @param hFile handle of file for operation. 22 | * @param type specifies the type of track to be added. 23 | * @param timeScale the time scale in ticks per second of the track. Default is 1000. 24 | * 25 | * @return On success, the track-id of new track. 26 | * On failure, #MP4_INVALID_TRACK_ID. 27 | */ 28 | MP4V2_EXPORT 29 | MP4TrackId MP4AddTrack( 30 | MP4FileHandle hFile, 31 | const char* type, 32 | uint32_t timeScale DEFAULT(MP4_MSECS_TIME_SCALE) ); 33 | 34 | /** Add an MPEG-4 systems track. 35 | * 36 | * MP4AddSystemsTrack adds an MPEG-4 Systems track to the mp4 file. Note 37 | * this should not be used to add OD or scene tracks, MP4AddODTrack() and 38 | * MP4AddSceneTrack() should be used for those purposes. Other known 39 | * MPEG-4 System track types are: 40 | * @li #MP4_CLOCK_TRACK_TYPE 41 | * @li #MP4_MPEG7_TRACK_TYPE 42 | * @li #MP4_OCI_TRACK_TYPE 43 | * @li #MP4_IPMP_TRACK_TYPE 44 | * @li #MP4_MPEGJ_TRACK_TYPE 45 | * 46 | * @param hFile handle of file for operation. 47 | * @param type specifies the type of track to be added. 48 | * 49 | * @return On success, the track-id of new track. 50 | * On failure, #MP4_INVALID_TRACK_ID. 51 | */ 52 | MP4V2_EXPORT 53 | MP4TrackId MP4AddSystemsTrack( 54 | MP4FileHandle hFile, 55 | const char* type ); 56 | 57 | /** Add a object descriptor (OD) track. 58 | * 59 | * MP4AddODTrack adds an object descriptor (aka OD) track to the mp4 file. 60 | * MP4WriteSample() can then be used to add the desired OD commands to the 61 | * track. The burden is currently on the calling application to understand 62 | * OD. 63 | * 64 | * Those wishing to have a simple audio/video scene without understanding 65 | * OD may wish to use MP4MakeIsmaCompliant() to create the minimal OD and 66 | * BIFS information. 67 | * 68 | * @param hFile handle of file for operation. 69 | * 70 | * @return On success, the track-id of new track. 71 | * On failure, #MP4_INVALID_TRACK_ID. 72 | */ 73 | MP4V2_EXPORT 74 | MP4TrackId MP4AddODTrack( 75 | MP4FileHandle hFile ); 76 | 77 | /** Add a scene (BIFS) track. 78 | * 79 | * MP4AddSceneTrack adds a scene (aka BIFS) track to the mp4 file. 80 | * MP4WriteSample() can then be used to add the desired BIFS commands to 81 | * the track. The burden is currently on the calling application to 82 | * understand BIFS. 83 | * 84 | * Those wishing to have a simple audio/video scene without understanding 85 | * BIFS may wish to use MP4MakeIsmaCompliant() to create the minimal OD 86 | * and BIFS information. 87 | * 88 | * @param hFile handle of file for operation. 89 | * 90 | * @return On success, the track-id of new track. 91 | * On failure, #MP4_INVALID_TRACK_ID. 92 | */ 93 | MP4V2_EXPORT 94 | MP4TrackId MP4AddSceneTrack( 95 | MP4FileHandle hFile ); 96 | 97 | /** Add audio track to mp4 file. 98 | * 99 | * MP4AddAudioTrack adds an audio track to the mp4 file. MP4WriteSample() 100 | * can then be used to add the desired audio samples. 101 | * 102 | * It is recommended that the time scale be set to the sampling frequency 103 | * (eg. 44100 Hz) of the audio so as to preserve the timing information 104 | * accurately. 105 | * 106 | * If the audio encoding uses a fixed duration for each sample that should 107 | * be specified here. If not then the value #MP4_INVALID_DURATION 108 | * should be given for the sampleDuration argument. 109 | * 110 | * @param hFile handle of file for operation. 111 | * @param timeScale the time scale in ticks per second of the track. 112 | * @param sampleDuration the fixed duration for all track samples. 113 | * Caveat: the value should be in track-timescale units. 114 | * @param audioType the audio encoding type. 115 | * See MP4GetTrackEsdsObjectTypeId() for known values. 116 | * 117 | * @return On success, the track-id of the new track. 118 | * On error, #MP4_INVALID_TRACK_ID. 119 | */ 120 | MP4V2_EXPORT 121 | MP4TrackId MP4AddAudioTrack( 122 | MP4FileHandle hFile, 123 | uint32_t timeScale, 124 | MP4Duration sampleDuration, 125 | uint8_t audioType DEFAULT(MP4_MPEG4_AUDIO_TYPE) ); 126 | 127 | /** Add ulaw track to mp4 file. 128 | * 129 | * MP4AddULawAudioTrack adds a ulaw track to the mp4 file. MP4WriteSample() 130 | * can then be used to add the desired audio samples. 131 | * 132 | * @param hFile handle of file for operation. 133 | * @param timeScale the time scale in ticks per second of the track. 134 | * 135 | * @return On success, the track-id of the new track. 136 | * On error, #MP4_INVALID_TRACK_ID. 137 | */ 138 | MP4V2_EXPORT 139 | MP4TrackId MP4AddULawAudioTrack( 140 | MP4FileHandle hFile, 141 | uint32_t timeScale); 142 | 143 | /** Add alaw track to mp4 file. 144 | * 145 | * MP4AddALawAudioTrack adds a alaw track to the mp4 file. MP4WriteSample() 146 | * can then be used to add the desired audio samples. 147 | * 148 | * @param hFile handle of file for operation. 149 | * @param timeScale the time scale in ticks per second of the track. 150 | * 151 | * @return On success, the track-id of the new track. 152 | * On error, #MP4_INVALID_TRACK_ID. 153 | */ 154 | MP4V2_EXPORT 155 | MP4TrackId MP4AddALawAudioTrack( 156 | MP4FileHandle hFile, 157 | uint32_t timeScale); 158 | 159 | MP4V2_EXPORT 160 | MP4TrackId MP4AddAC3AudioTrack( 161 | MP4FileHandle hFile, 162 | uint32_t samplingRate, 163 | uint8_t fscod, 164 | uint8_t bsid, 165 | uint8_t bsmod, 166 | uint8_t acmod, 167 | uint8_t lfeon, 168 | uint8_t bit_rate_code ); 169 | 170 | MP4V2_EXPORT 171 | MP4TrackId MP4AddAmrAudioTrack( 172 | MP4FileHandle hFile, 173 | uint32_t timeScale, 174 | uint16_t modeSet, 175 | uint8_t modeChangePeriod, 176 | uint8_t framesPerSample, 177 | bool isAmrWB ); 178 | 179 | MP4V2_EXPORT 180 | void MP4SetAmrVendor( 181 | MP4FileHandle hFile, 182 | MP4TrackId trackId, 183 | uint32_t vendor ); 184 | 185 | MP4V2_EXPORT 186 | void MP4SetAmrDecoderVersion( 187 | MP4FileHandle hFile, 188 | MP4TrackId trackId, 189 | uint8_t decoderVersion ); 190 | 191 | MP4V2_EXPORT 192 | void MP4SetAmrModeSet( 193 | MP4FileHandle hFile, 194 | MP4TrackId trakId, 195 | uint16_t modeSet ); 196 | 197 | MP4V2_EXPORT 198 | uint16_t MP4GetAmrModeSet( 199 | MP4FileHandle hFile, 200 | MP4TrackId trackId ); 201 | 202 | MP4V2_EXPORT 203 | MP4TrackId MP4AddHrefTrack( 204 | MP4FileHandle hFile, 205 | uint32_t timeScale, 206 | MP4Duration sampleDuration, 207 | const char* base_url DEFAULT(NULL) ); 208 | 209 | MP4V2_EXPORT 210 | const char* MP4GetHrefTrackBaseUrl( 211 | MP4FileHandle hFile, 212 | MP4TrackId trackId ); 213 | 214 | /** Add a video track. 215 | * 216 | * MP4AddVideoTrack adds a video track to the mp4 file. MP4WriteSample() 217 | * can then be used to add the desired video samples. 218 | * 219 | * It is recommended that the time scale be set to 90000 so as to preserve 220 | * the timing information accurately for the range of video frame rates 221 | * commonly in use. 222 | * 223 | * If the video frame rate is to be fixed then the sampleDuration argument 224 | * should be give the appropriate fixed value. If the video frame rate is 225 | * to be variable then the value #MP4_INVALID_DURATION should be 226 | * given for the sampleDuration argument. 227 | * 228 | * @param hFile handle of file for operation. 229 | * @param timeScale the timescale in ticks per second of the track. 230 | * @param sampleDuration specifies fixed sample duration for all track 231 | * samples. Caveat: the value should be in track timescale units. 232 | * @param width specifies the video frame width in pixels. 233 | * @param height specifies the video frame height in pixels. 234 | * @param videoType specifies the video encoding type. 235 | * See MP4GetTrackVideoType() for known values. 236 | * 237 | * @return On success, the track-id of the new track. 238 | * On error, #MP4_INVALID_TRACK_ID. 239 | */ 240 | MP4V2_EXPORT 241 | MP4TrackId MP4AddVideoTrack( 242 | MP4FileHandle hFile, 243 | uint32_t timeScale, 244 | MP4Duration sampleDuration, 245 | uint16_t width, 246 | uint16_t height, 247 | uint8_t videoType DEFAULT(MP4_MPEG4_VIDEO_TYPE) ); 248 | 249 | MP4V2_EXPORT 250 | MP4TrackId MP4AddH264VideoTrack( 251 | MP4FileHandle hFile, 252 | uint32_t timeScale, 253 | MP4Duration sampleDuration, 254 | uint16_t width, 255 | uint16_t height, 256 | uint8_t AVCProfileIndication, 257 | uint8_t profile_compat, 258 | uint8_t AVCLevelIndication, 259 | uint8_t sampleLenFieldSizeMinusOne ); 260 | 261 | MP4V2_EXPORT 262 | void MP4AddH264SequenceParameterSet( 263 | MP4FileHandle hFile, 264 | MP4TrackId trackId, 265 | const uint8_t* pSequence, 266 | uint16_t sequenceLen ); 267 | 268 | MP4V2_EXPORT 269 | void MP4AddH264PictureParameterSet( 270 | MP4FileHandle hFile, 271 | MP4TrackId trackId, 272 | const uint8_t* pPict, 273 | uint16_t pictLen ); 274 | 275 | MP4V2_EXPORT 276 | void MP4SetH263Vendor( 277 | MP4FileHandle hFile, 278 | MP4TrackId trackId, 279 | uint32_t vendor ); 280 | 281 | MP4V2_EXPORT 282 | void MP4SetH263DecoderVersion( 283 | MP4FileHandle hFile, 284 | MP4TrackId trackId, 285 | uint8_t decoderVersion ); 286 | 287 | MP4V2_EXPORT 288 | void MP4SetH263Bitrates( 289 | MP4FileHandle hFile, 290 | MP4TrackId trackId, 291 | uint32_t avgBitrate, 292 | uint32_t maxBitrate ); 293 | 294 | MP4V2_EXPORT 295 | MP4TrackId MP4AddH263VideoTrack( 296 | MP4FileHandle hFile, 297 | uint32_t timeScale, 298 | MP4Duration sampleDuration, 299 | uint16_t width, 300 | uint16_t height, 301 | uint8_t h263Level, 302 | uint8_t h263Profile, 303 | uint32_t avgBitrate, 304 | uint32_t maxBitrate ); 305 | 306 | /** Add a hint track. 307 | * 308 | * MP4AddHintTrack adds a hint track to the mp4 file. A hint track is used 309 | * to describe how to send the reference media track over a particular 310 | * network transport. In the case of the IETF RTP protocol, the hint track 311 | * describes how the media data should be placed into packets and any 312 | * media specific protocol headers that should be added. 313 | * 314 | * Typically there is a one to one correspondence between reference media 315 | * track samples and hint track samples. The start time, duration, and 316 | * sync flags are typically the same, however provisions are made for 317 | * deviations from this rule. 318 | * 319 | * The MP4 library provides extensive support for RTP hint tracks. This 320 | * includes a easy to use API to create RTP hint tracks, and read out 321 | * fully constructed RTP packets based on the hint track. 322 | * 323 | * @param hFile handle of file for operation. 324 | * @param refTrackId specifies the reference media track for this hint track. 325 | * 326 | * @return On success, the track-id of the new track. 327 | * On error, #MP4_INVALID_TRACK_ID. 328 | */ 329 | MP4V2_EXPORT 330 | MP4TrackId MP4AddHintTrack( 331 | MP4FileHandle hFile, 332 | MP4TrackId refTrackId ); 333 | 334 | MP4V2_EXPORT 335 | MP4TrackId MP4AddTextTrack( 336 | MP4FileHandle hFile, 337 | MP4TrackId refTrackId ); 338 | 339 | MP4V2_EXPORT 340 | MP4TrackId MP4AddSubtitleTrack( 341 | MP4FileHandle hFile, 342 | uint32_t timescale, 343 | uint16_t width, 344 | uint16_t height ); 345 | 346 | MP4V2_EXPORT 347 | MP4TrackId MP4AddSubpicTrack( 348 | MP4FileHandle hFile, 349 | uint32_t timescale, 350 | uint16_t width, 351 | uint16_t height ); 352 | 353 | MP4V2_EXPORT 354 | MP4TrackId MP4AddPixelAspectRatio( 355 | MP4FileHandle hFile, 356 | MP4TrackId refTrackId, 357 | uint32_t hSpacing, 358 | uint32_t vSpacing ); 359 | 360 | MP4V2_EXPORT 361 | MP4TrackId MP4AddColr( 362 | MP4FileHandle hFile, 363 | MP4TrackId refTrackId, 364 | uint16_t primary, 365 | uint16_t transfer, 366 | uint16_t matrix ); 367 | 368 | MP4V2_EXPORT 369 | MP4TrackId MP4CloneTrack( 370 | MP4FileHandle srcFile, 371 | MP4TrackId srcTrackId, 372 | MP4FileHandle dstFile DEFAULT(MP4_INVALID_FILE_HANDLE), 373 | MP4TrackId dstHintTrackReferenceTrack DEFAULT(MP4_INVALID_TRACK_ID) ); 374 | 375 | MP4V2_EXPORT 376 | MP4TrackId MP4CopyTrack( 377 | MP4FileHandle srcFile, 378 | MP4TrackId srcTrackId, 379 | MP4FileHandle dstFile DEFAULT(MP4_INVALID_FILE_HANDLE), 380 | bool applyEdits DEFAULT(false), 381 | MP4TrackId dstHintTrackReferenceTrack DEFAULT(MP4_INVALID_TRACK_ID) ); 382 | 383 | MP4V2_EXPORT 384 | bool MP4DeleteTrack( 385 | MP4FileHandle hFile, 386 | MP4TrackId trackId ); 387 | 388 | MP4V2_EXPORT 389 | uint32_t MP4GetNumberOfTracks( 390 | MP4FileHandle hFile, 391 | const char* type DEFAULT(NULL), 392 | uint8_t subType DEFAULT(0) ); 393 | 394 | MP4V2_EXPORT 395 | MP4TrackId MP4FindTrackId( 396 | MP4FileHandle hFile, 397 | uint16_t index, 398 | const char* type DEFAULT(NULL), 399 | uint8_t subType DEFAULT(0) ); 400 | 401 | MP4V2_EXPORT 402 | uint16_t MP4FindTrackIndex( 403 | MP4FileHandle hFile, 404 | MP4TrackId trackId ); 405 | 406 | /** Get maximum duration of chunk. 407 | * 408 | * MP4GetTrackDurationPerChunk gets the maximum duration for each chunk. 409 | * 410 | * @param hFile handle of file for operation. 411 | * @param trackId id of track for operation. 412 | * @param duration out value of duration in track timescale units. 413 | * 414 | * return true on success, false on failure. 415 | */ 416 | MP4V2_EXPORT 417 | bool MP4GetTrackDurationPerChunk( 418 | MP4FileHandle hFile, 419 | MP4TrackId trackId, 420 | MP4Duration* duration ); 421 | 422 | /** Set maximum duration of chunk. 423 | * 424 | * MP4SetTrackDurationPerChunk sets the maximum duration for each chunk. 425 | * 426 | * @param hFile handle of file for operation. 427 | * @param trackId id of track for operation. 428 | * @param duration in timescale units. 429 | * 430 | * @return true on success, false on failure. 431 | */ 432 | MP4V2_EXPORT 433 | bool MP4SetTrackDurationPerChunk( 434 | MP4FileHandle hFile, 435 | MP4TrackId trackId, 436 | MP4Duration duration ); 437 | 438 | /** 439 | * @param hFile handle of file for operation. 440 | * @param trackId id of track for operation. 441 | * 442 | * @return true on success, false on failure. 443 | */ 444 | MP4V2_EXPORT 445 | bool MP4AddIPodUUID( 446 | MP4FileHandle hFile, 447 | MP4TrackId trackId ); 448 | 449 | /** @} ***********************************************************************/ 450 | 451 | #endif /* MP4V2_TRACK_H */ 452 | -------------------------------------------------------------------------------- /app/libs/include/mp4v2/track_prop.h: -------------------------------------------------------------------------------- 1 | #ifndef MP4V2_TRACK_PROP_H 2 | #define MP4V2_TRACK_PROP_H 3 | 4 | /**************************************************************************//** 5 | * 6 | * @defgroup mp4_track_prop MP4v2 Track Property 7 | * @{ 8 | * 9 | *****************************************************************************/ 10 | 11 | /* specific track properties */ 12 | 13 | MP4V2_EXPORT 14 | bool MP4HaveTrackAtom( 15 | MP4FileHandle hFile, 16 | MP4TrackId trackId, 17 | const char* atomname ); 18 | 19 | /** Get the track type. 20 | * 21 | * MP4GetTrackType gets the type of the track with the specified track id. 22 | * 23 | * Note: the library does not provide a MP4SetTrackType function, the 24 | * track type needs to be specified when the track is created, e.g. 25 | * MP4AddSystemsTrack(MP4_OCI_TRACK_TYPE). 26 | * 27 | * Known track types are: 28 | * @li #MP4_OD_TRACK_TYPE 29 | * @li #MP4_SCENE_TRACK_TYPE 30 | * @li #MP4_AUDIO_TRACK_TYPE 31 | * @li #MP4_VIDEO_TRACK_TYPE 32 | * @li #MP4_HINT_TRACK_TYPE 33 | * @li #MP4_CNTL_TRACK_TYPE 34 | * @li #MP4_TEXT_TRACK_TYPE 35 | * @li #MP4_CLOCK_TRACK_TYPE 36 | * @li #MP4_MPEG7_TRACK_TYPE 37 | * @li #MP4_OCI_TRACK_TYPE 38 | * @li #MP4_IPMP_TRACK_TYPE 39 | * @li #MP4_MPEGJ_TRACK_TYPE 40 | * 41 | * @param hFile handle of file for operation. 42 | * @param trackId id of track for operation. 43 | * 44 | * @return On success, a string indicating track type. On failure, NULL. 45 | */ 46 | MP4V2_EXPORT 47 | const char* MP4GetTrackType( 48 | MP4FileHandle hFile, 49 | MP4TrackId trackId ); 50 | 51 | MP4V2_EXPORT 52 | const char* MP4GetTrackMediaDataName( 53 | MP4FileHandle hFile, 54 | MP4TrackId trackId ); 55 | 56 | /* 57 | * MP4GetTrackMediaDataOriginalFormat is to be used to get the original 58 | * MediaDataName if a track has been encrypted. 59 | */ 60 | 61 | MP4V2_EXPORT 62 | bool MP4GetTrackMediaDataOriginalFormat( 63 | MP4FileHandle hFile, 64 | MP4TrackId trackId, 65 | char* originalFormat, 66 | uint32_t buflen ); 67 | 68 | MP4V2_EXPORT 69 | MP4Duration MP4GetTrackDuration( 70 | MP4FileHandle hFile, 71 | MP4TrackId trackId ); 72 | 73 | /** Get the time scale of a track. 74 | * 75 | * MP4GetTrackTimeScale returns the time scale of the specified track in 76 | * the mp4 file. The time scale determines the number of clock ticks per 77 | * second for this track. 78 | * 79 | * @param hFile handle of file for operation. 80 | * @param trackId id of track for operation. 81 | * 82 | * @return timescale (ticks per second) of the track in the mp4 file. 83 | */ 84 | MP4V2_EXPORT 85 | uint32_t MP4GetTrackTimeScale( 86 | MP4FileHandle hFile, 87 | MP4TrackId trackId ); 88 | 89 | /** Set the time scale of a track. 90 | * 91 | * MP4SetTrackTimeScale sets the time scale of the specified track in the 92 | * mp4 file. The time scale determines the number of clock ticks per 93 | * second for this track. 94 | * 95 | * Typically this value is set once when the track is created. However 96 | * this call can be used to modify the value if that is desired. Since 97 | * track sample durations are expressed in units of the track time scale, 98 | * any change to the time scale value will effect the real time duration 99 | * of the samples. 100 | * 101 | * @param hFile handle of file for operation. 102 | * @param trackId id of track for operation. 103 | * @param timeScale desired time scale for the track. 104 | * 105 | * @return true on success, false on failure. 106 | */ 107 | MP4V2_EXPORT 108 | bool MP4SetTrackTimeScale( 109 | MP4FileHandle hFile, 110 | MP4TrackId trackId, 111 | uint32_t value ); 112 | 113 | /** Get ISO-639-2/T language code of a track. 114 | * The language code is a 3-char alpha code consisting of lower-case letters. 115 | * 116 | * @param hFile handle of file for operation. 117 | * @param trackId id of track for operation. 118 | * @param code buffer to hold 3-char+null (4-bytes total). 119 | * 120 | * @return true on success, false on failure. 121 | */ 122 | MP4V2_EXPORT 123 | bool MP4GetTrackLanguage( 124 | MP4FileHandle hFile, 125 | MP4TrackId trackId, 126 | char* code ); 127 | 128 | /** Set ISO-639-2/T language code of a track. 129 | * The language code is a 3-char alpha code consisting of lower-case letters. 130 | * 131 | * @param hFile handle of file for operation. 132 | * @param trackId id of track for operation. 133 | * @param code 3-char language code. 134 | * 135 | * @return true on success, false on failure. 136 | */ 137 | MP4V2_EXPORT 138 | bool MP4SetTrackLanguage( 139 | MP4FileHandle hFile, 140 | MP4TrackId trackId, 141 | const char* code ); 142 | 143 | /** Get track name. 144 | * 145 | * MP4GetTrackName gets the name of the track via udta.name property. 146 | * 147 | * @param hFile handle of file for operation. 148 | * @param trackId id of track for operation. 149 | * 150 | * @return true on success, false on failure. 151 | */ 152 | MP4V2_EXPORT 153 | bool MP4GetTrackName( 154 | MP4FileHandle hFile, 155 | MP4TrackId trackId, 156 | char** name ); 157 | 158 | /** Set track name. 159 | * 160 | * MP4SetTrackName sets the name of the track via udta.name property. 161 | * The udta atom is created if needed. 162 | * 163 | * @param hFile handle of file for operation. 164 | * @param trackId id of track for operation. 165 | * 166 | * @return true on success, false on failure. 167 | */ 168 | MP4V2_EXPORT 169 | bool MP4SetTrackName( 170 | MP4FileHandle hFile, 171 | MP4TrackId trackId, 172 | const char* name ); 173 | 174 | MP4V2_EXPORT 175 | uint8_t MP4GetTrackAudioMpeg4Type( 176 | MP4FileHandle hFile, 177 | MP4TrackId trackId ); 178 | 179 | MP4V2_EXPORT 180 | uint8_t MP4GetTrackEsdsObjectTypeId( 181 | MP4FileHandle hFile, 182 | MP4TrackId trackId ); 183 | 184 | /* returns MP4_INVALID_DURATION if track samples do not have a fixed duration */ 185 | MP4V2_EXPORT 186 | MP4Duration MP4GetTrackFixedSampleDuration( 187 | MP4FileHandle hFile, 188 | MP4TrackId trackId ); 189 | 190 | MP4V2_EXPORT 191 | uint32_t MP4GetTrackBitRate( 192 | MP4FileHandle hFile, 193 | MP4TrackId trackId ); 194 | 195 | MP4V2_EXPORT 196 | bool MP4GetTrackVideoMetadata( 197 | MP4FileHandle hFile, 198 | MP4TrackId trackId, 199 | uint8_t** ppConfig, 200 | uint32_t* pConfigSize ); 201 | 202 | MP4V2_EXPORT 203 | bool MP4GetTrackESConfiguration( 204 | MP4FileHandle hFile, 205 | MP4TrackId trackId, 206 | uint8_t** ppConfig, 207 | uint32_t* pConfigSize ); 208 | 209 | MP4V2_EXPORT 210 | bool MP4SetTrackESConfiguration( 211 | MP4FileHandle hFile, 212 | MP4TrackId trackId, 213 | const uint8_t* pConfig, 214 | uint32_t configSize ); 215 | 216 | /* h264 information routines */ 217 | MP4V2_EXPORT 218 | bool MP4GetTrackH264ProfileLevel( 219 | MP4FileHandle hFile, 220 | MP4TrackId trackId, 221 | uint8_t* pProfile, 222 | uint8_t* pLevel ); 223 | 224 | MP4V2_EXPORT 225 | bool MP4GetTrackH264SeqPictHeaders( 226 | MP4FileHandle hFile, 227 | MP4TrackId trackId, 228 | uint8_t*** pSeqHeaders, 229 | uint32_t** pSeqHeaderSize, 230 | uint8_t*** pPictHeader, 231 | uint32_t** pPictHeaderSize ); 232 | 233 | MP4V2_EXPORT 234 | bool MP4GetTrackH264LengthSize( 235 | MP4FileHandle hFile, 236 | MP4TrackId trackId, 237 | uint32_t* pLength ); 238 | 239 | MP4V2_EXPORT 240 | MP4SampleId MP4GetTrackNumberOfSamples( 241 | MP4FileHandle hFile, 242 | MP4TrackId trackId ); 243 | 244 | MP4V2_EXPORT 245 | uint16_t MP4GetTrackVideoWidth( 246 | MP4FileHandle hFile, 247 | MP4TrackId trackId ); 248 | 249 | MP4V2_EXPORT 250 | uint16_t MP4GetTrackVideoHeight( 251 | MP4FileHandle hFile, 252 | MP4TrackId trackId ); 253 | 254 | MP4V2_EXPORT 255 | double MP4GetTrackVideoFrameRate( 256 | MP4FileHandle hFile, 257 | MP4TrackId trackId ); 258 | 259 | MP4V2_EXPORT 260 | int MP4GetTrackAudioChannels( 261 | MP4FileHandle hFile, 262 | MP4TrackId trackId ); 263 | 264 | MP4V2_EXPORT 265 | bool MP4IsIsmaCrypMediaTrack( 266 | MP4FileHandle hFile, 267 | MP4TrackId trackId ); 268 | 269 | /* generic track properties */ 270 | 271 | MP4V2_EXPORT 272 | bool MP4HaveTrackAtom( 273 | MP4FileHandle hFile, 274 | MP4TrackId trackId, 275 | const char* atomName ); 276 | 277 | MP4V2_EXPORT 278 | bool MP4GetTrackIntegerProperty( 279 | MP4FileHandle hFile, 280 | MP4TrackId trackId, 281 | const char* propName, 282 | uint64_t* retvalue ); 283 | 284 | MP4V2_EXPORT 285 | bool MP4GetTrackFloatProperty( 286 | MP4FileHandle hFile, 287 | MP4TrackId trackId, 288 | const char* propName, 289 | float* ret_value ); 290 | 291 | MP4V2_EXPORT 292 | bool MP4GetTrackStringProperty( 293 | MP4FileHandle hFile, 294 | MP4TrackId trackId, 295 | const char* propName, 296 | const char** retvalue ); 297 | 298 | MP4V2_EXPORT 299 | bool MP4GetTrackBytesProperty( 300 | MP4FileHandle hFile, 301 | MP4TrackId trackId, 302 | const char* propName, 303 | uint8_t** ppValue, 304 | uint32_t* pValueSize ); 305 | 306 | MP4V2_EXPORT 307 | bool MP4SetTrackIntegerProperty( 308 | MP4FileHandle hFile, 309 | MP4TrackId trackId, 310 | const char* propName, 311 | int64_t value ); 312 | 313 | MP4V2_EXPORT 314 | bool MP4SetTrackFloatProperty( 315 | MP4FileHandle hFile, 316 | MP4TrackId trackId, 317 | const char* propName, 318 | float value ); 319 | 320 | MP4V2_EXPORT 321 | bool MP4SetTrackStringProperty( 322 | MP4FileHandle hFile, 323 | MP4TrackId trackId, 324 | const char* propName, 325 | const char* value ); 326 | 327 | MP4V2_EXPORT 328 | bool MP4SetTrackBytesProperty( 329 | MP4FileHandle hFile, 330 | MP4TrackId trackId, 331 | const char* propName, 332 | const uint8_t* pValue, 333 | uint32_t valueSize); 334 | 335 | /** @} ***********************************************************************/ 336 | 337 | #endif /* MP4V2_TRACK_PROP_H */ 338 | -------------------------------------------------------------------------------- /app/libs/x86/libmp4v2.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chezi008/mp4muxer/97bbae69d1e0b0d25b883866b60246e1a9c4be10/app/libs/x86/libmp4v2.so -------------------------------------------------------------------------------- /app/libs/x86_64/libmp4v2.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chezi008/mp4muxer/97bbae69d1e0b0d25b883866b60246e1a9c4be10/app/libs/x86_64/libmp4v2.so -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in D:\Android\sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/chezi008/mp4muxerdemo/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.chezi008.mp4muxerdemo; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumentation test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.chezi008.mp4muxerdemo", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/cpp/MP4Encoder.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | filename: MP4Encoder.cpp 3 | created: 2013-04-16 4 | author: firehood 5 | purpose: MP4编码器,基于开源库mp4v2实现(https://code.google.com/p/mp4v2/)。 6 | *********************************************************************/ 7 | #include "MP4Encoder.h" 8 | #include 9 | #include "android/log.h" 10 | 11 | #define LOG_TAG "MP4Encoder.cpp" 12 | #define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__); 13 | 14 | #define BUFFER_SIZE (1024*1024) 15 | 16 | MP4Encoder::MP4Encoder(void) : 17 | m_videoId(NULL), 18 | m_nWidth(0), 19 | m_nHeight(0), 20 | m_nTimeScale(0), 21 | m_nFrameRate(0) { 22 | } 23 | 24 | MP4Encoder::~MP4Encoder(void) { 25 | } 26 | 27 | MP4FileHandle 28 | MP4Encoder::CreateMP4File(const char *pFileName, int width, int height, int timeScale/* = 90000*/, 29 | int frameRate/* = 25*/) { 30 | if (pFileName == NULL) { 31 | return 0; 32 | } 33 | // create mp4 file 34 | MP4FileHandle hMp4file = MP4Create(pFileName); 35 | if (hMp4file == MP4_INVALID_FILE_HANDLE) { 36 | printf("ERROR:Open file fialed.\n"); 37 | return 0; 38 | } 39 | m_nWidth = width; 40 | m_nHeight = height; 41 | m_nTimeScale = 90000; 42 | m_nFrameRate = 25; 43 | MP4SetTimeScale(hMp4file, m_nTimeScale); 44 | return hMp4file; 45 | } 46 | 47 | bool MP4Encoder::Write264Metadata(MP4FileHandle hMp4File, LPMP4ENC_Metadata lpMetadata) { 48 | m_videoId = MP4AddH264VideoTrack 49 | (hMp4File, 50 | m_nTimeScale, 51 | m_nTimeScale / m_nFrameRate, 52 | m_nWidth, // width 53 | m_nHeight,// height 54 | lpMetadata->Sps[1], // sps[1] AVCProfileIndication 55 | lpMetadata->Sps[2], // sps[2] profile_compat 56 | lpMetadata->Sps[3], // sps[3] AVCLevelIndication 57 | 3); // 4 bytes length before each NAL unit 58 | if (m_videoId == MP4_INVALID_TRACK_ID) { 59 | printf("add video track failed.\n"); 60 | return false; 61 | } 62 | MP4SetVideoProfileLevel(hMp4File, 0x01); // Simple Profile @ Level 3 63 | 64 | // write sps 65 | MP4AddH264SequenceParameterSet(hMp4File, m_videoId, lpMetadata->Sps, lpMetadata->nSpsLen); 66 | 67 | // write pps 68 | MP4AddH264PictureParameterSet(hMp4File, m_videoId, lpMetadata->Pps, lpMetadata->nPpsLen); 69 | 70 | return true; 71 | } 72 | 73 | int MP4Encoder::WriteH264Data(MP4FileHandle hMp4File, const unsigned char *pData, int size) { 74 | if (hMp4File == NULL) { 75 | return -1; 76 | } 77 | if (pData == NULL) { 78 | return -1; 79 | } 80 | MP4ENC_NaluUnit nalu; 81 | int pos = 0, len = 0; 82 | while (len = ReadOneNaluFromBuf(pData, size, pos, nalu)) { 83 | LOGI("nalu.type: %d", nalu.type); 84 | if (nalu.type == 0x07) // sps 85 | { 86 | if (m_videoId == MP4_INVALID_TRACK_ID) { 87 | // 添加h264 track 88 | m_videoId = MP4AddH264VideoTrack 89 | (hMp4File, 90 | m_nTimeScale, 91 | m_nTimeScale / m_nFrameRate, 92 | m_nWidth, // width 93 | m_nHeight, // height 94 | nalu.data[1], // sps[1] AVCProfileIndication 95 | nalu.data[2], // sps[2] profile_compat 96 | nalu.data[3], // sps[3] AVCLevelIndication 97 | 3); // 4 bytes length before each NAL unit 98 | 99 | MP4SetVideoProfileLevel(hMp4File, 0x01); // Simple Profile @ Level 3 1 100 | } 101 | if (m_videoId == MP4_INVALID_TRACK_ID) { 102 | printf("add video track failed.\n"); 103 | return 0; 104 | } 105 | 106 | MP4AddH264SequenceParameterSet(hMp4File, m_videoId, nalu.data, nalu.size); 107 | } else if (nalu.type == 0x08) // pps 108 | { 109 | MP4AddH264PictureParameterSet(hMp4File, m_videoId, nalu.data, nalu.size); 110 | } else if (nalu.type == 0x05 || nalu.type == 0x01) { 111 | int datalen = nalu.size + 4; 112 | unsigned char *data = new unsigned char[datalen]; 113 | // MP4 Nalu前四个字节表示Nalu长度 114 | data[0] = nalu.size >> 24; 115 | data[1] = nalu.size >> 16; 116 | data[2] = nalu.size >> 8; 117 | data[3] = nalu.size & 0xff; 118 | memcpy(data + 4, nalu.data, nalu.size); 119 | if (!MP4WriteSample(hMp4File, m_videoId, data, datalen, MP4_INVALID_DURATION, 0, 120 | nalu.type == 0x05 ? 1 : 0)) { 121 | return 0; 122 | } 123 | delete[] data; 124 | } 125 | 126 | pos += len; 127 | } 128 | return pos; 129 | } 130 | 131 | int MP4Encoder::ReadOneNaluFromBuf(const unsigned char *buffer, unsigned int nBufferSize, 132 | unsigned int offSet, MP4ENC_NaluUnit &nalu) { 133 | int i = offSet; 134 | while (i < nBufferSize) { 135 | if (buffer[i++] == 0x00 && 136 | buffer[i++] == 0x00 && 137 | buffer[i++] == 0x00 && 138 | buffer[i++] == 0x01 139 | ) { 140 | int pos = i; 141 | while (pos < nBufferSize) { 142 | if (buffer[pos++] == 0x00 && 143 | buffer[pos++] == 0x00 && 144 | buffer[pos++] == 0x00 && 145 | buffer[pos++] == 0x01 146 | ) { 147 | break; 148 | } 149 | } 150 | if (pos == nBufferSize) { 151 | nalu.size = pos - i; 152 | } else { 153 | nalu.size = (pos - 4) - i; 154 | } 155 | 156 | nalu.type = buffer[i] & 0x1f; 157 | nalu.data = (unsigned char *) &buffer[i]; 158 | return (nalu.size + i - offSet); 159 | } 160 | } 161 | return 0; 162 | } 163 | 164 | void MP4Encoder::CloseMP4File(MP4FileHandle hMp4File) { 165 | if (hMp4File) { 166 | MP4Close(hMp4File); 167 | hMp4File = NULL; 168 | } 169 | } 170 | 171 | bool MP4Encoder::WriteH264File(const char *pFile264, const char *pFileMp4) { 172 | if (pFile264 == NULL || pFileMp4 == NULL) { 173 | return false; 174 | } 175 | 176 | MP4FileHandle hMp4File = CreateMP4File(pFileMp4, 352, 288); 177 | 178 | if (hMp4File == NULL) { 179 | printf("ERROR:Create file failed!"); 180 | return false; 181 | } 182 | 183 | FILE *fp = fopen(pFile264, "rb"); 184 | if (!fp) { 185 | printf("ERROR:open file failed!"); 186 | return false; 187 | } 188 | fseek(fp, 0, SEEK_SET); 189 | 190 | unsigned char *buffer = new unsigned char[BUFFER_SIZE]; 191 | int pos = 0; 192 | while (1) { 193 | int readlen = fread(buffer + pos, sizeof(unsigned char), BUFFER_SIZE - pos, fp); 194 | 195 | 196 | if (readlen <= 0) { 197 | break; 198 | } 199 | 200 | readlen += pos; 201 | 202 | int writelen = 0; 203 | for (int i = readlen - 1; i >= 0; i--) { 204 | if (buffer[i--] == 0x01 && 205 | buffer[i--] == 0x00 && 206 | buffer[i--] == 0x00 && 207 | buffer[i--] == 0x00 208 | ) { 209 | writelen = i + 5; 210 | break; 211 | } 212 | } 213 | 214 | writelen = WriteH264Data(hMp4File, buffer, writelen); 215 | if (writelen <= 0) { 216 | break; 217 | } 218 | memcpy(buffer, buffer + writelen, readlen - writelen + 1); 219 | pos = readlen - writelen + 1; 220 | } 221 | fclose(fp); 222 | 223 | delete[] buffer; 224 | CloseMP4File(hMp4File); 225 | 226 | return true; 227 | } 228 | 229 | bool MP4Encoder::PraseMetadata(const unsigned char *pData, int size, MP4ENC_Metadata &metadata) { 230 | if (pData == NULL || size < 4) { 231 | return false; 232 | } 233 | MP4ENC_NaluUnit nalu; 234 | int pos = 0; 235 | bool bRet1 = false, bRet2 = false; 236 | while (int len = ReadOneNaluFromBuf(pData, size, pos, nalu)) { 237 | if (nalu.type == 0x07) { 238 | memcpy(metadata.Sps, nalu.data, nalu.size); 239 | metadata.nSpsLen = nalu.size; 240 | bRet1 = true; 241 | } else if ((nalu.type == 0x08)) { 242 | memcpy(metadata.Pps, nalu.data, nalu.size); 243 | metadata.nPpsLen = nalu.size; 244 | bRet2 = true; 245 | } 246 | pos += len; 247 | } 248 | if (bRet1 && bRet2) { 249 | return true; 250 | } 251 | return false; 252 | } -------------------------------------------------------------------------------- /app/src/main/cpp/MP4Encoder.h: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | filename: MP4Encoder.h 3 | created: 2013-04-16 4 | author: firehood 5 | purpose: MP4编码器,基于开源库mp4v2实现(https://code.google.com/p/mp4v2/)。 6 | *********************************************************************/ 7 | #pragma once 8 | #include "mp4v2/mp4v2.h" 9 | 10 | // NALU单元 11 | typedef struct _MP4ENC_NaluUnit 12 | { 13 | int type; 14 | int size; 15 | unsigned char *data; 16 | }MP4ENC_NaluUnit; 17 | 18 | typedef struct _MP4ENC_Metadata 19 | { 20 | // video, must be h264 type 21 | unsigned int nSpsLen; 22 | unsigned char Sps[1024]; 23 | unsigned int nPpsLen; 24 | unsigned char Pps[1024]; 25 | 26 | } MP4ENC_Metadata,*LPMP4ENC_Metadata; 27 | 28 | class MP4Encoder 29 | { 30 | public: 31 | MP4Encoder(void); 32 | ~MP4Encoder(void); 33 | public: 34 | // open or creat a mp4 file. 35 | MP4FileHandle CreateMP4File(const char *fileName,int width,int height,int timeScale = 90000,int frameRate = 25); 36 | // wirte 264 metadata in mp4 file. 37 | bool Write264Metadata(MP4FileHandle hMp4File,LPMP4ENC_Metadata lpMetadata); 38 | // wirte 264 data, data can contain multiple frame. 39 | int WriteH264Data(MP4FileHandle hMp4File,const unsigned char* pData,int size); 40 | // close mp4 file. 41 | void CloseMP4File(MP4FileHandle hMp4File); 42 | // convert H264 file to mp4 file. 43 | // no need to call CreateMP4File and CloseMP4File,it will create/close mp4 file automaticly. 44 | bool WriteH264File(const char* pFile264,const char* pFileMp4); 45 | // Prase H264 metamata from H264 data frame 46 | static bool PraseMetadata(const unsigned char* pData,int size,MP4ENC_Metadata &metadata); 47 | private: 48 | // read one nalu from H264 data buffer 49 | static int ReadOneNaluFromBuf(const unsigned char *buffer,unsigned int nBufferSize,unsigned int offSet,MP4ENC_NaluUnit &nalu); 50 | private: 51 | int m_nWidth; 52 | int m_nHeight; 53 | int m_nFrameRate; 54 | int m_nTimeScale; 55 | MP4TrackId m_videoId; 56 | }; 57 | -------------------------------------------------------------------------------- /app/src/main/cpp/MP4EncoderHelper.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by chezi on 2017/9/13. 3 | // 4 | #include 5 | #include 6 | #include "MP4Encoder.h" 7 | #include "android/log.h" 8 | 9 | #define LOG_TAG "MP4EncoderHelper.cpp" 10 | #define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__); 11 | 12 | MP4Encoder mp4Encoder; 13 | MP4FileHandle mp4FileHandle; 14 | 15 | extern "C" 16 | JNIEXPORT void JNICALL Java_com_chezi008_mp4muxerdemo_helper_MP4EncoderHelper_init 17 | (JNIEnv *env, jclass jclass, jstring path, jint width, jint height) { 18 | const char *local_title = env->GetStringUTFChars(path, NULL); 19 | int m_width = width; 20 | int m_height = height; 21 | mp4FileHandle = mp4Encoder.CreateMP4File(local_title, m_width, m_height); 22 | LOGI("MP4Encoder----->初始化成功"); 23 | } 24 | 25 | extern "C" 26 | JNIEXPORT jint JNICALL Java_com_chezi008_mp4muxerdemo_helper_MP4EncoderHelper_writeH264Data 27 | (JNIEnv *env, jclass clz, jbyteArray data, jint size) { 28 | jbyte *jb_data = env->GetByteArrayElements(data, JNI_FALSE); 29 | unsigned char *h264_data = (unsigned char *) jb_data; 30 | int result = mp4Encoder.WriteH264Data(mp4FileHandle, h264_data, size); 31 | LOGI("MP4Encoder----->添加数据 result:%d", result); 32 | return result; 33 | } 34 | /** 35 | * 释放 36 | */ 37 | extern "C" 38 | JNIEXPORT void JNICALL Java_com_chezi008_mp4muxerdemo_helper_MP4EncoderHelper_close 39 | (JNIEnv *env, jclass clz) { 40 | mp4Encoder.CloseMP4File(mp4FileHandle); 41 | LOGI("MP4Encoder----->close"); 42 | } -------------------------------------------------------------------------------- /app/src/main/cpp/native-lib.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | extern "C" 5 | JNIEXPORT jstring JNICALL 6 | Java_com_chezi008_mp4muxerdemo_MainActivity_stringFromJNI( 7 | JNIEnv *env, 8 | jobject /* this */) { 9 | std::string hello = "Hello from C++"; 10 | return env->NewStringUTF(hello.c_str()); 11 | } 12 | -------------------------------------------------------------------------------- /app/src/main/java/com/chezi008/mp4muxerdemo/MP4V2Activity.java: -------------------------------------------------------------------------------- 1 | package com.chezi008.mp4muxerdemo; 2 | 3 | import android.graphics.SurfaceTexture; 4 | import android.support.v7.app.AppCompatActivity; 5 | import android.os.Bundle; 6 | import android.util.Log; 7 | import android.view.Surface; 8 | import android.view.TextureView; 9 | import android.view.View; 10 | import android.widget.Button; 11 | 12 | import com.chezi008.mp4muxerdemo.decode.H264Decoder; 13 | import com.chezi008.mp4muxerdemo.file.FileConstant; 14 | import com.chezi008.mp4muxerdemo.file.H264ReadRunable; 15 | import com.chezi008.mp4muxerdemo.helper.MP4EncoderHelper; 16 | import com.chezi008.mp4muxerdemo.utils.SPUtils; 17 | 18 | public class MP4V2Activity extends AppCompatActivity { 19 | String TAG = getClass().getSimpleName(); 20 | 21 | public static final String VIDEO_KEY_SPS = "video_sps"; 22 | public static final String VIDEO_KEY_PPS = "video_pps"; 23 | 24 | private H264Decoder mVideoDecode; 25 | private TextureView mTextureView; 26 | private boolean haveGetSpsInfo; 27 | 28 | @Override 29 | protected void onCreate(Bundle savedInstanceState) { 30 | super.onCreate(savedInstanceState); 31 | setContentView(R.layout.activity_mp4_v2); 32 | initVariable(); 33 | initView(); 34 | } 35 | 36 | private void initVariable() { 37 | MP4EncoderHelper.init(FileConstant.mp4FilePath, 1280, 720); 38 | } 39 | 40 | private void initView() { 41 | Button btn_play = (Button) findViewById(R.id.btn_play); 42 | btn_play.setOnClickListener(new View.OnClickListener() { 43 | @Override 44 | public void onClick(View v) { 45 | readLocalFile(); 46 | } 47 | }); 48 | mTextureView = (TextureView) findViewById(R.id.tv_paly_view); 49 | mTextureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() { 50 | @Override 51 | public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { 52 | Log.d(TAG, "onSurfaceTextureAvailable: "); 53 | initVideoCodec(surface); 54 | } 55 | 56 | @Override 57 | public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { 58 | 59 | } 60 | 61 | @Override 62 | public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { 63 | Log.d(TAG, "onSurfaceTextureDestroyed: "); 64 | mVideoDecode.release(); 65 | return false; 66 | } 67 | 68 | @Override 69 | public void onSurfaceTextureUpdated(SurfaceTexture surface) { 70 | 71 | } 72 | }); 73 | } 74 | 75 | private void initVideoCodec(SurfaceTexture surface) { 76 | mVideoDecode = new H264Decoder(this); 77 | mVideoDecode.initCodec(new Surface(surface)); 78 | mVideoDecode.start(); 79 | } 80 | 81 | private void readLocalFile() { 82 | H264ReadRunable h264ReadRunable = new H264ReadRunable(); 83 | h264ReadRunable.setH264ReadListener(new H264ReadRunable.H264ReadListener() { 84 | @Override 85 | public void onFrameData(byte[] datas) { 86 | // mVideoDecode.decodeFrame(datas); 87 | if (haveGetSpsInfo) { 88 | Log.d(TAG, "onFrameData: -->datas[4]:" + datas[4]); 89 | MP4EncoderHelper.writeH264Data(datas, datas.length); 90 | return; 91 | } 92 | //找sps和pps 93 | if ((datas[4] & 0x1f) == 7) {//sps 94 | MP4EncoderHelper.writeH264Data(datas, datas.length); 95 | SPUtils.saveObject(MP4V2Activity.this, VIDEO_KEY_SPS, datas); 96 | Log.d(TAG, "onFrameData: "); 97 | } else if ((datas[4] & 0x1f) == 8) {//pps 98 | MP4EncoderHelper.writeH264Data(datas, datas.length); 99 | SPUtils.saveObject(MP4V2Activity.this, VIDEO_KEY_PPS, datas); 100 | }else if((datas[4] & 0x1f) == 5){ 101 | //第一帧为I帧 102 | haveGetSpsInfo = true; 103 | MP4EncoderHelper.writeH264Data(datas, datas.length); 104 | } 105 | } 106 | @Override 107 | public void onStopRead() { 108 | mVideoDecode.release(); 109 | MP4EncoderHelper.close(); 110 | } 111 | }); 112 | Thread readFileThread = new Thread(h264ReadRunable); 113 | readFileThread.start(); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /app/src/main/java/com/chezi008/mp4muxerdemo/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.chezi008.mp4muxerdemo; 2 | 3 | import android.Manifest; 4 | import android.content.Intent; 5 | import android.content.pm.PackageManager; 6 | import android.support.v4.app.ActivityCompat; 7 | import android.support.v4.content.ContextCompat; 8 | import android.support.v7.app.AppCompatActivity; 9 | import android.os.Bundle; 10 | import android.util.Log; 11 | import android.view.View; 12 | import android.widget.Button; 13 | import android.widget.TextView; 14 | 15 | import com.chezi008.mp4muxerdemo.hw.HWRecorderActivity; 16 | 17 | /** 18 | * 描述: 19 | * 作者:chezi008 on 2017/6/29 16:32 20 | * 邮箱:chezi008@qq.com 21 | */ 22 | public class MainActivity extends AppCompatActivity { 23 | String TAG = getClass().getSimpleName(); 24 | private static final int MY_PERMISSION_REQUEST_CODE = 10000; 25 | private String[] requestPermissions = new String[]{ 26 | Manifest.permission.RECORD_AUDIO, 27 | // Manifest.permission.READ_CONTACTS, 28 | Manifest.permission.READ_EXTERNAL_STORAGE, 29 | Manifest.permission.WRITE_EXTERNAL_STORAGE 30 | }; 31 | 32 | // Used to load the 'native-lib' library on application startup. 33 | static { 34 | System.loadLibrary("native-lib"); 35 | } 36 | 37 | @Override 38 | protected void onCreate(Bundle savedInstanceState) { 39 | super.onCreate(savedInstanceState); 40 | setContentView(R.layout.activity_main); 41 | 42 | initView(); 43 | getPermission(); 44 | } 45 | 46 | private void getPermission() { 47 | /** 48 | * 第 1 步: 检查是否有相应的权限 49 | */ 50 | boolean isAllGranted = checkPermissionAllGranted( 51 | requestPermissions 52 | ); 53 | // 如果这3个权限全都拥有, 则直接执行备份代码 54 | if (isAllGranted) { 55 | return; 56 | } 57 | // 一次请求多个权限, 如果其他有权限是已经授予的将会自动忽略掉 58 | ActivityCompat.requestPermissions( 59 | this, 60 | requestPermissions, 61 | MY_PERMISSION_REQUEST_CODE 62 | ); 63 | } 64 | 65 | /** 66 | * 检查是否拥有指定的所有权限 67 | */ 68 | private boolean checkPermissionAllGranted(String[] permissions) { 69 | for (String permission : permissions) { 70 | if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) { 71 | // 只要有一个权限没有被授予, 则直接返回 false 72 | Log.d(TAG, "checkPermissionAllGranted: -------->permission权限没有获取成功:"+permission); 73 | return false; 74 | } 75 | } 76 | return true; 77 | } 78 | 79 | private void initView() { 80 | // Example of a call to a native method 81 | TextView tv = (TextView) findViewById(R.id.sample_text); 82 | tv.setText(stringFromJNI()); 83 | //android muxer shengcheng mp4 84 | Button btn_muxer = (Button) findViewById(R.id.btn_muxer); 85 | btn_muxer.setOnClickListener(new View.OnClickListener() { 86 | @Override 87 | public void onClick(View v) { 88 | Intent intent = new Intent(MainActivity.this, MuxerMp4Activity.class); 89 | startActivity(intent); 90 | } 91 | }); 92 | 93 | Button btn_mp4v2 = (Button) findViewById(R.id.btn_mp4v2); 94 | btn_mp4v2.setOnClickListener(new View.OnClickListener() { 95 | @Override 96 | public void onClick(View v) { 97 | Intent intent = new Intent(MainActivity.this, MP4V2Activity.class); 98 | startActivity(intent); 99 | } 100 | }); 101 | 102 | findViewById(R.id.btnHw).setOnClickListener(new View.OnClickListener() { 103 | @Override 104 | public void onClick(View v) { 105 | Intent intent = new Intent(MainActivity.this, HWRecorderActivity.class); 106 | startActivity(intent); 107 | } 108 | }); 109 | } 110 | 111 | 112 | /** 113 | * A native method that is implemented by the 'native-lib' native library, 114 | * which is packaged with this application. 115 | */ 116 | public native String stringFromJNI(); 117 | } 118 | -------------------------------------------------------------------------------- /app/src/main/java/com/chezi008/mp4muxerdemo/MuxerMp4Activity.java: -------------------------------------------------------------------------------- 1 | package com.chezi008.mp4muxerdemo; 2 | 3 | import android.graphics.SurfaceTexture; 4 | import android.media.MediaCodec; 5 | import android.media.MediaFormat; 6 | import android.os.Bundle; 7 | import android.support.v7.app.AppCompatActivity; 8 | import android.util.Log; 9 | import android.view.Surface; 10 | import android.view.TextureView; 11 | import android.view.View; 12 | import android.widget.Button; 13 | 14 | import com.chezi008.mp4muxer.Mp4Muxer; 15 | import com.chezi008.mp4muxerdemo.decode.H264Decoder; 16 | import com.chezi008.mp4muxerdemo.encode.AACEncoder; 17 | import com.chezi008.mp4muxerdemo.file.FileConstant; 18 | import com.chezi008.mp4muxerdemo.file.H264ReadRunable; 19 | import com.chezi008.mp4muxerdemo.utils.SPUtils; 20 | 21 | import java.io.IOException; 22 | import java.nio.ByteBuffer; 23 | 24 | /** 25 | * 描述:muxer合成Mp4界面 26 | * 作者:chezi008 on 2017/6/29 16:33 27 | * 邮箱:chezi008@qq.com 28 | */ 29 | public class MuxerMp4Activity extends AppCompatActivity { 30 | String TAG = getClass().getSimpleName(); 31 | public static final String VIDEO_KEY_SPS = "video_sps"; 32 | public static final String VIDEO_KEY_PPS = "video_pps"; 33 | 34 | private H264Decoder mVideoDecode; 35 | private AACEncoder mAACEncoder; 36 | private Mp4Muxer mMp4Muxer=new Mp4Muxer(FileConstant.mp4FilePath); 37 | private TextureView mTextureView; 38 | 39 | 40 | @Override 41 | protected void onCreate(Bundle savedInstanceState) { 42 | super.onCreate(savedInstanceState); 43 | setContentView(R.layout.activity_muxer_mp4); 44 | initView(); 45 | 46 | } 47 | 48 | private void initVideoCodec(SurfaceTexture surface) { 49 | mVideoDecode = new H264Decoder(this); 50 | mVideoDecode.initCodec(new Surface(surface)); 51 | mVideoDecode.start(); 52 | 53 | mVideoDecode.setH264DecoderListener(new H264Decoder.H264DecoderListener() { 54 | @Override 55 | public void ondecode(byte[] out, MediaCodec.BufferInfo info) { 56 | 57 | } 58 | 59 | @Override 60 | public void outputFormat(MediaFormat outputFormat) { 61 | // vFormat = outputFormat; 62 | // vFormat.setString(MediaFormat.KEY_MIME, "video/avc"); 63 | vFormat = mVideoDecode.getMediaformat(); 64 | startMuxer(); 65 | } 66 | }); 67 | } 68 | 69 | private MediaFormat vFormat, aFormat; 70 | 71 | private void startMuxer() { 72 | if (startRecord) { 73 | return; 74 | } 75 | if (vFormat != null && aFormat != null) { 76 | mMp4Muxer.addVideoTrack(vFormat); 77 | mMp4Muxer.addAudioTrack(aFormat); 78 | mMp4Muxer.start(); 79 | startRecord = true; 80 | } 81 | } 82 | 83 | private long startPts; 84 | 85 | private void initView() { 86 | Button btn_play = (Button) findViewById(R.id.btn_play); 87 | btn_play.setOnClickListener(new View.OnClickListener() { 88 | @Override 89 | public void onClick(View v) { 90 | initVideoCodec(mTextureView.getSurfaceTexture()); 91 | initAudioCoder(); 92 | readLocalFile(); 93 | } 94 | }); 95 | findViewById(R.id.btnRecord).setOnClickListener(new View.OnClickListener() { 96 | @Override 97 | public void onClick(View v) { 98 | startMuxer(); 99 | } 100 | }); 101 | findViewById(R.id.btnStopRecord).setOnClickListener(new View.OnClickListener() { 102 | @Override 103 | public void onClick(View v) { 104 | mMp4Muxer.stop(); 105 | startRecord = false; 106 | } 107 | }); 108 | mTextureView = findViewById(R.id.tv_paly_view); 109 | mTextureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() { 110 | @Override 111 | public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { 112 | Log.d(TAG, "onSurfaceTextureAvailable: "); 113 | } 114 | 115 | @Override 116 | public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { 117 | 118 | } 119 | 120 | @Override 121 | public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { 122 | Log.d(TAG, "onSurfaceTextureDestroyed: "); 123 | mVideoDecode.release(); 124 | return false; 125 | } 126 | 127 | @Override 128 | public void onSurfaceTextureUpdated(SurfaceTexture surface) { 129 | 130 | } 131 | }); 132 | } 133 | 134 | private void initAudioCoder() { 135 | mAACEncoder = new AACEncoder(); 136 | try { 137 | mAACEncoder.setSavePath(FileConstant.aacFilePath); 138 | mAACEncoder.setAudioEnncoderListener(new AACEncoder.AudioEnncoderListener() { 139 | @Override 140 | public void getAudioBuffer(ByteBuffer byteBuffer, MediaCodec.BufferInfo bufferInfo) { 141 | if (startRecord && startPts != 0) { 142 | bufferInfo.presentationTimeUs = System.nanoTime() / 1000 - startPts; 143 | mMp4Muxer.writeAudioData(byteBuffer, bufferInfo); 144 | } 145 | } 146 | 147 | @Override 148 | public void getOutputFormat(MediaFormat format) { 149 | aFormat = format; 150 | startMuxer(); 151 | } 152 | }); 153 | mAACEncoder.prepare(); 154 | mAACEncoder.start(); 155 | } catch (IOException e) { 156 | e.printStackTrace(); 157 | } catch (InterruptedException e) { 158 | e.printStackTrace(); 159 | } 160 | 161 | } 162 | 163 | private boolean haveGetSpsInfo; 164 | private boolean startRecord; 165 | 166 | private void readLocalFile() { 167 | H264ReadRunable h264ReadRunable = new H264ReadRunable(); 168 | h264ReadRunable.setH264ReadListener(new H264ReadRunable.H264ReadListener() { 169 | @Override 170 | public void onFrameData(byte[] datas) { 171 | mVideoDecode.decodeFrame(datas); 172 | //找sps和pps 173 | if ((datas[4] & 0x1f) == 7) {//sps 174 | mVideoDecode.getMediaformat().setByteBuffer("csd-0", ByteBuffer.wrap(datas)); 175 | Log.d(TAG, "onFrameData:sps "); 176 | } else if ((datas[4] & 0x1f) == 8) {//pps 177 | mVideoDecode.getMediaformat().setByteBuffer("csd-1", ByteBuffer.wrap(datas)); 178 | haveGetSpsInfo = true; 179 | } else if ((datas[4] & 0x1f) == 5) { 180 | //第一帧为I帧 181 | // haveGetSpsInfo = true; 182 | // addMuxerVideoData(datas); 183 | } 184 | if (!startRecord) { 185 | return; 186 | } 187 | if (haveGetSpsInfo) { 188 | Log.d(TAG, "onFrameData: -->datas[4]:" + datas[4]); 189 | addMuxerVideoData(datas); 190 | return; 191 | } 192 | 193 | 194 | } 195 | 196 | @Override 197 | public void onStopRead() { 198 | mVideoDecode.release(); 199 | mAACEncoder.stop(); 200 | } 201 | }); 202 | Thread readFileThread = new Thread(h264ReadRunable); 203 | readFileThread.start(); 204 | } 205 | 206 | 207 | private void addMuxerVideoData(byte[] datas) { 208 | MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo(); 209 | if (mMp4Muxer == null) return; 210 | bufferInfo.offset = 0; 211 | bufferInfo.size = datas.length; 212 | if ((datas[4] & 0x1f) == 5) { 213 | Log.d(TAG, "onDecodeVideoFrame: -->I帧"); 214 | bufferInfo.flags = MediaCodec.BUFFER_FLAG_KEY_FRAME; 215 | } else if ((datas[4] & 0x1f) == 7 || (datas[4] & 0x1f) == 8) { 216 | Log.d(TAG, "addMuxerVideoData: -->sps or pps"); 217 | bufferInfo.flags = MediaCodec.BUFFER_FLAG_CODEC_CONFIG; 218 | } else { 219 | bufferInfo.flags = 0; 220 | } 221 | ByteBuffer buffer = ByteBuffer.wrap(datas, bufferInfo.offset, bufferInfo.size); 222 | long pts = System.nanoTime() / 1000; 223 | if (startPts == 0) { 224 | startPts = pts; 225 | } 226 | bufferInfo.presentationTimeUs = System.nanoTime() / 1000 - startPts; 227 | mMp4Muxer.writeVideoData(buffer, bufferInfo); 228 | } 229 | 230 | } 231 | -------------------------------------------------------------------------------- /app/src/main/java/com/chezi008/mp4muxerdemo/decode/H264Decoder.java: -------------------------------------------------------------------------------- 1 | package com.chezi008.mp4muxerdemo.decode; 2 | 3 | import android.content.Context; 4 | import android.media.MediaCodec; 5 | import android.media.MediaExtractor; 6 | import android.media.MediaFormat; 7 | import android.util.Log; 8 | import android.view.Surface; 9 | 10 | import com.chezi008.mp4muxerdemo.MuxerMp4Activity; 11 | import com.chezi008.mp4muxerdemo.utils.SPUtils; 12 | 13 | import java.io.IOException; 14 | import java.nio.ByteBuffer; 15 | 16 | /** 17 | * 描述: 18 | * 作者:chezi008 on 2017/6/30 10:03 19 | * 邮箱:chezi008@163.com 20 | */ 21 | 22 | public class H264Decoder { 23 | private String TAG = getClass().getSimpleName(); 24 | 25 | private static final int VIDEO_WIDTH = 1280; 26 | private static final int VIDEO_HEIGHT = 720; 27 | 28 | private MediaCodec mCodec; 29 | private MediaFormat mediaformat; 30 | private int mFrameRate = 30; 31 | private Boolean UseSPSandPPS = false; 32 | 33 | // private MediaMuxer mMuxer; 34 | // private int mTrackIndex; 35 | 36 | private ByteBuffer mCSD0; 37 | private ByteBuffer mCSD1; 38 | private Context mContext; 39 | private long timeoutUs = 10000; 40 | 41 | private ByteBuffer[] inputBuffers; 42 | 43 | public H264Decoder(Context context) { 44 | this.mContext = context; 45 | } 46 | 47 | public void initCodec(Surface surface) { 48 | try { 49 | //通过多媒体格式名创建一个可用的解码器 50 | mCodec = MediaCodec.createDecoderByType("video/avc"); 51 | } catch (IOException e) { 52 | e.printStackTrace(); 53 | } 54 | //初始化编码器 55 | mediaformat = MediaFormat.createVideoFormat("video/avc", VIDEO_WIDTH, VIDEO_HEIGHT); 56 | //获取h264中的pps及sps数据 57 | if (UseSPSandPPS) { 58 | byte[] header_default_sps = {0, 0, 0, 1, 103, 100, 0, 31, -84, -76, 2, -128, 45, -56}; 59 | byte[] header_default_pps = {0, 0, 0, 1, 104, -18, 60, 97, 15, -1, -16, -121, -1, -8, 67, -1, -4, 33, -1, -2, 16, -1, -1, 8, 127, -1, -64}; 60 | 61 | SPUtils spUtils = new SPUtils(); 62 | //读取之前保存的sps 和pps 63 | byte[] header_sps = (byte[]) spUtils.readObject(mContext, MuxerMp4Activity.VIDEO_KEY_SPS); 64 | byte[] header_pps = (byte[]) spUtils.readObject(mContext, MuxerMp4Activity.VIDEO_KEY_PPS); 65 | 66 | if (header_sps != null) { 67 | mCSD0 = ByteBuffer.wrap(header_sps); 68 | mCSD1 = ByteBuffer.wrap(header_pps); 69 | } else { 70 | mCSD0 = ByteBuffer.wrap(header_default_sps); 71 | mCSD1 = ByteBuffer.wrap(header_default_pps); 72 | mCSD0.clear(); 73 | mCSD1.clear(); 74 | } 75 | //设置sps和pps 如果设置不正确会导致合成的mp4视频作为文件预览的时候,预览图片是黑色的 76 | //视频进度条拖拽画面会出现绿色,以及块状现象 77 | mediaformat.setByteBuffer("csd-0", mCSD0); 78 | mediaformat.setByteBuffer("csd-1", mCSD1); 79 | 80 | } 81 | // mediaformat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface); 82 | // mediaformat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 1920 * 1080); 83 | //设置帧率 84 | mediaformat.setInteger(MediaFormat.KEY_FRAME_RATE, mFrameRate); 85 | mCodec.configure(mediaformat, surface, null, 0); 86 | 87 | 88 | } 89 | 90 | public MediaFormat getMediaformat() { 91 | return mediaformat; 92 | } 93 | 94 | public void release() { 95 | Log.d(TAG, "releasing encoder objects"); 96 | if (mCodec != null) { 97 | mCodec.stop(); 98 | mCodec.release(); 99 | mCodec = null; 100 | } 101 | } 102 | 103 | public void start() { 104 | if (mCodec != null) { 105 | mCodec.start(); 106 | 107 | inputBuffers = mCodec.getInputBuffers(); 108 | 109 | } 110 | } 111 | MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo(); 112 | public void decodeFrame(byte[] frame) { 113 | long pts = System.nanoTime() / 1000; 114 | int inIndex = mCodec.dequeueInputBuffer(-1); 115 | if (inIndex >= 0) { 116 | ByteBuffer byteBuffer = inputBuffers[inIndex]; 117 | byteBuffer.clear(); 118 | byteBuffer.put(frame); 119 | mCodec.queueInputBuffer(inIndex, 0, frame.length, pts, 0); 120 | } 121 | int outIndex = mCodec.dequeueOutputBuffer(bufferInfo, 0); 122 | if (outIndex >= 0) { 123 | boolean doRender = (bufferInfo.size != 0); 124 | //对outputbuffer的处理完后,调用这个函数把buffer重新返回给codec类。 125 | mCodec.releaseOutputBuffer(outIndex, doRender); 126 | // Log.d(TAG, "video: pts:"+bufferInfo.presentationTimeUs+",rPts:"+pts); 127 | } else if (outIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { 128 | MediaFormat outputFormat = mCodec.getOutputFormat(); 129 | if (h264DecoderListener != null) { 130 | h264DecoderListener.outputFormat(outputFormat); 131 | } 132 | } 133 | } 134 | 135 | private H264DecoderListener h264DecoderListener; 136 | 137 | public void setH264DecoderListener(H264DecoderListener h264DecoderListener) { 138 | this.h264DecoderListener = h264DecoderListener; 139 | } 140 | 141 | public interface H264DecoderListener{ 142 | void ondecode(byte[] out,MediaCodec.BufferInfo info); 143 | void outputFormat(MediaFormat outputFormat); 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /app/src/main/java/com/chezi008/mp4muxerdemo/encode/AACEncoder.java: -------------------------------------------------------------------------------- 1 | package com.chezi008.mp4muxerdemo.encode; 2 | 3 | import android.media.AudioFormat; 4 | import android.media.AudioRecord; 5 | import android.media.MediaCodec; 6 | import android.media.MediaCodecInfo; 7 | import android.media.MediaFormat; 8 | import android.media.MediaRecorder; 9 | import android.os.Build; 10 | import android.util.Log; 11 | 12 | import java.io.FileOutputStream; 13 | import java.io.IOException; 14 | import java.nio.ByteBuffer; 15 | 16 | /** 17 | * 描述: 18 | * 19 | * 作者:chezi008 on 2017/6/26 10:14 20 | * 邮箱:chezi008@qq.com 21 | */ 22 | 23 | public class AACEncoder implements Runnable { 24 | private String TAG = getClass().getSimpleName(); 25 | private String mime = "audio/mp4a-latm"; 26 | private AudioRecord mRecorder; 27 | private MediaCodec mEnc; 28 | private MediaFormat mMediaFormat; 29 | private int rate = 128000;//9600 30 | 31 | //录音设置 32 | private int sampleRate = 8000; //采样率,默认44.1k 33 | private int channelCount = 1; //音频采样通道,默认2通道 34 | private int channelConfig = AudioFormat.CHANNEL_IN_MONO; //通道设置,默认立体声 35 | private int audioFormat = AudioFormat.ENCODING_PCM_16BIT; //设置采样数据格式,默认16比特PCM 36 | private FileOutputStream fos; 37 | 38 | private boolean isRecording; 39 | private Thread mThread; 40 | private int bufferSize; 41 | 42 | private String mSavePath; 43 | private AudioEnncoderListener audioEnncoderListener; 44 | 45 | public AACEncoder() { 46 | } 47 | 48 | public void setAudioEnncoderListener(AudioEnncoderListener audioEnncoderListener) { 49 | this.audioEnncoderListener = audioEnncoderListener; 50 | } 51 | public void setSavePath(String path) { 52 | this.mSavePath = path; 53 | } 54 | 55 | 56 | public void prepare() throws IOException { 57 | if (isWriteLocaAAC){ 58 | fos = new FileOutputStream(mSavePath); 59 | } 60 | //音频编码相关 61 | mMediaFormat = MediaFormat.createAudioFormat(mime, sampleRate, channelCount); 62 | mMediaFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC); 63 | mMediaFormat.setInteger(MediaFormat.KEY_CHANNEL_MASK, AudioFormat.CHANNEL_IN_MONO); 64 | mMediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, rate); 65 | byte[] data = new byte[]{(byte) 0x11, (byte) 0x90}; 66 | 67 | ByteBuffer mCSD0 = ByteBuffer.wrap(data); 68 | // mCSD0.clear(); 69 | 70 | // mCSD1 = ByteBuffer.wrap(header_pps); 71 | // mCSD1.clear(); 72 | mMediaFormat.setByteBuffer("csd-0", mCSD0); 73 | // mMediaFormat.setByteBuffer("csd-1", mCSD1); 74 | 75 | mEnc = MediaCodec.createEncoderByType(mime); 76 | mEnc.configure(mMediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); 77 | 78 | //音频录制相关 79 | bufferSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat) * 2; 80 | mRecorder = new AudioRecord(MediaRecorder.AudioSource.MIC, sampleRate, channelConfig, 81 | audioFormat, bufferSize); 82 | } 83 | 84 | public MediaFormat getMediaFormat() { 85 | return mMediaFormat; 86 | } 87 | 88 | public void start() throws InterruptedException { 89 | mEnc.start(); 90 | mRecorder.startRecording(); 91 | if (mThread != null && mThread.isAlive()) { 92 | isRecording = false; 93 | mThread.join(); 94 | } 95 | isRecording = true; 96 | mThread = new Thread(this); 97 | mThread.start(); 98 | } 99 | 100 | private ByteBuffer getInputBuffer(int index) { 101 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 102 | return mEnc.getInputBuffer(index); 103 | } else { 104 | return mEnc.getInputBuffers()[index]; 105 | } 106 | } 107 | 108 | private ByteBuffer getOutputBuffer(int index) { 109 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 110 | return mEnc.getOutputBuffer(index); 111 | } else { 112 | return mEnc.getOutputBuffers()[index]; 113 | } 114 | } 115 | 116 | private boolean isWriteLocaAAC; 117 | 118 | private void readOutputData() throws IOException { 119 | int index = mEnc.dequeueInputBuffer(-1); 120 | long pts = System.nanoTime() / 1000; 121 | if (index >= 0) { 122 | final ByteBuffer buffer = getInputBuffer(index); 123 | buffer.clear(); 124 | int length = mRecorder.read(buffer, bufferSize); 125 | if (length > 0) { 126 | mEnc.queueInputBuffer(index, 0, length, pts, 0);//System.nanoTime() / 1000 127 | // Log.d(TAG, "queueInputBuffer: pts:"+pts); 128 | } else { 129 | Log.e(TAG, "length-->" + length); 130 | } 131 | } 132 | MediaCodec.BufferInfo mInfo = new MediaCodec.BufferInfo(); 133 | int outIndex; 134 | do { 135 | outIndex = mEnc.dequeueOutputBuffer(mInfo, 0); 136 | // Log.e(TAG, "audio flag---->" + mInfo.flags + "/" + outIndex); 137 | if (outIndex >= 0) { 138 | ByteBuffer buffer = getOutputBuffer(outIndex); 139 | if (isWriteLocaAAC) { 140 | buffer.position(mInfo.offset); 141 | byte[] temp = new byte[mInfo.size + 7]; 142 | buffer.get(temp, 7, mInfo.size); 143 | addADTStoPacket(temp, temp.length); 144 | Log.d(TAG, "readOutputData: temp.length-->" + temp.length); 145 | fos.write(temp); 146 | } 147 | if (audioEnncoderListener != null) { 148 | audioEnncoderListener.getAudioBuffer(buffer, mInfo); 149 | } 150 | // Log.d(TAG, "audio: pts"+mInfo.presentationTimeUs); 151 | mEnc.releaseOutputBuffer(outIndex, false); 152 | } else if (outIndex == MediaCodec.INFO_TRY_AGAIN_LATER) { 153 | 154 | } else if (outIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { 155 | Log.d(TAG, "readOutputData: " + mEnc.getOutputFormat()); 156 | if (audioEnncoderListener != null) { 157 | audioEnncoderListener.getOutputFormat(mEnc.getOutputFormat()); 158 | } 159 | 160 | } 161 | } while (outIndex >= 0); 162 | } 163 | 164 | /** 165 | * 给编码出的aac裸流添加adts头字段 166 | * 167 | * @param packet 要空出前7个字节,否则会搞乱数据 168 | * @param packetLen 169 | */ 170 | private void addADTStoPacket(byte[] packet, int packetLen) { 171 | int profile = 2; //AAC LC 172 | int freqIdx = 11; //44.1KHz--4 这个参数跟采样率有关sampleRate,8000-->11 16000-->8 44100-->4 173 | int chanCfg = channelCount; //CPE 这个参数跟通道数有关channelCount chanCfg = 这个参数跟通道数有关channelCount 174 | packet[0] = (byte) 0xFF; 175 | packet[1] = (byte) 0xF9; 176 | packet[2] = (byte) (((profile - 1) << 6) + (freqIdx << 2) + (chanCfg >> 2)); 177 | packet[3] = (byte) (((chanCfg & 3) << 6) + (packetLen >> 11)); 178 | packet[4] = (byte) ((packetLen & 0x7FF) >> 3); 179 | packet[5] = (byte) (((packetLen & 7) << 5) + 0x1F); 180 | packet[6] = (byte) 0xFC; 181 | } 182 | 183 | /** 184 | * 停止录制 185 | */ 186 | public void stop() { 187 | try { 188 | isRecording = false; 189 | mThread.join(); 190 | mRecorder.stop(); 191 | mEnc.stop(); 192 | mEnc.release(); 193 | fos.flush(); 194 | fos.close(); 195 | } catch (Exception e) { 196 | e.printStackTrace(); 197 | } 198 | } 199 | 200 | @Override 201 | public void run() { 202 | while (isRecording) { 203 | try { 204 | readOutputData(); 205 | } catch (IOException e) { 206 | e.printStackTrace(); 207 | } 208 | } 209 | } 210 | 211 | public interface AudioEnncoderListener { 212 | void getAudioBuffer(ByteBuffer byteBuffer, MediaCodec.BufferInfo bufferInfo); 213 | 214 | void getOutputFormat(MediaFormat format); 215 | } 216 | 217 | } 218 | -------------------------------------------------------------------------------- /app/src/main/java/com/chezi008/mp4muxerdemo/file/FileConstant.java: -------------------------------------------------------------------------------- 1 | package com.chezi008.mp4muxerdemo.file; 2 | 3 | import android.os.Environment; 4 | 5 | import java.io.File; 6 | 7 | /** 8 | * 描述: 9 | * 作者:chezi008 on 2017/6/29 16:44 10 | * 邮箱:chezi008@163.com 11 | */ 12 | 13 | public class FileConstant { 14 | public static final String baseFile = Environment.getExternalStorageDirectory().getPath()+"/mp4muxer"; 15 | public static final String h264FileName = "mtv.h264"; 16 | public static final String mp4FileName = "mtv.mp4"; 17 | public static final String aacFileName = "test.aac"; 18 | 19 | public static final String mp4FilePath = baseFile + File.separator + mp4FileName; 20 | public static final String h264FilePath = baseFile + File.separator + h264FileName; 21 | public static final String aacFilePath = baseFile + File.separator + aacFileName; 22 | } 23 | -------------------------------------------------------------------------------- /app/src/main/java/com/chezi008/mp4muxerdemo/file/H264ReadRunable.java: -------------------------------------------------------------------------------- 1 | package com.chezi008.mp4muxerdemo.file; 2 | 3 | import android.util.Log; 4 | 5 | import java.io.DataInputStream; 6 | import java.io.FileInputStream; 7 | import java.io.FileNotFoundException; 8 | import java.io.IOException; 9 | 10 | /** 11 | * 描述:用来将本地h264数据分割成一帧一帧的数据 12 | * 作者:chezi008 on 2017/6/29 16:50 13 | * 邮箱:chezi008@163.com 14 | */ 15 | 16 | public class H264ReadRunable implements Runnable { 17 | private static final int READ_BUFFER_SIZE = 1024 * 5; 18 | private static final int BUFFER_SIZE = 1024 * 1024; 19 | 20 | private String TAG = getClass().getSimpleName(); 21 | private H264ReadListener h264ReadListener; 22 | private DataInputStream mInputStream; 23 | 24 | public void setH264ReadListener(H264ReadListener h264ReadListener) { 25 | this.h264ReadListener = h264ReadListener; 26 | } 27 | 28 | private byte[] buffer; 29 | 30 | @Override 31 | public void run() { 32 | try { 33 | Log.d(TAG, "run: " + FileConstant.h264FilePath); 34 | mInputStream = new DataInputStream(new FileInputStream(FileConstant.h264FilePath)); 35 | buffer = new byte[BUFFER_SIZE]; 36 | 37 | int readLength; 38 | int naluIndex = 0; 39 | int offset = 0; 40 | int bufferLength = 0; 41 | 42 | while ((readLength = mInputStream.read(buffer, offset, READ_BUFFER_SIZE)) > 0) { 43 | 44 | bufferLength += readLength; 45 | offset = bufferLength; 46 | for (int i = 5; i < bufferLength - 4; i++) { 47 | if (buffer[i] == 0x00 && 48 | buffer[i + 1] == 0x00 && 49 | buffer[i + 2] == 0x00 && 50 | buffer[i + 3] == 0x01) { 51 | naluIndex = i; 52 | // Log.d(TAG, "run: naluIndex:"+naluIndex); 53 | byte[] naluBuffer = new byte[naluIndex]; 54 | System.arraycopy(buffer,0,naluBuffer,0,naluIndex); 55 | h264ReadListener.onFrameData(naluBuffer); 56 | offset = bufferLength-naluIndex; 57 | bufferLength -=naluIndex; 58 | System.arraycopy(buffer,naluIndex,buffer,0,offset); 59 | i = 5; 60 | Thread.sleep(40); 61 | } 62 | } 63 | 64 | } 65 | h264ReadListener.onStopRead(); 66 | } catch (FileNotFoundException e) { 67 | e.printStackTrace(); 68 | } catch (IOException e) { 69 | e.printStackTrace(); 70 | } catch (InterruptedException e) { 71 | e.printStackTrace(); 72 | } 73 | } 74 | 75 | public interface H264ReadListener { 76 | void onFrameData(byte[] datas); 77 | 78 | void onStopRead(); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /app/src/main/java/com/chezi008/mp4muxerdemo/helper/MP4EncoderHelper.java: -------------------------------------------------------------------------------- 1 | package com.chezi008.mp4muxerdemo.helper; 2 | 3 | /** 4 | * 描述: 5 | * 作者:chezi008 on 2017/9/13 9:07 6 | * 邮箱:chezi008@163.com 7 | */ 8 | 9 | public class MP4EncoderHelper { 10 | 11 | public static native void init(String mp4FilePath, int widht, int height); 12 | 13 | public static native int writeH264Data(byte[] data, int size); 14 | 15 | public static native void close(); 16 | 17 | static { 18 | System.loadLibrary("native-lib"); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /app/src/main/java/com/chezi008/mp4muxerdemo/hw/AudioEncoder.java: -------------------------------------------------------------------------------- 1 | package com.chezi008.mp4muxerdemo.hw; 2 | 3 | import android.content.Context; 4 | import android.media.MediaCodec; 5 | import android.media.MediaCodecInfo; 6 | import android.media.MediaFormat; 7 | import android.media.MediaMuxer; 8 | import android.util.Log; 9 | import android.widget.Toast; 10 | 11 | import java.io.File; 12 | import java.io.IOException; 13 | import java.nio.ByteBuffer; 14 | import java.util.Date; 15 | import java.util.concurrent.ExecutorService; 16 | import java.util.concurrent.Executors; 17 | 18 | /** 19 | * Created by davidbrodsky on 9/12/13. 20 | * Enormous thanks to Andrew McFadden for his MediaCodec examples! 21 | * Adapted from http://bigflake.com/mediacodec/CameraToMpegTest.java.txt 22 | */ 23 | public class AudioEncoder { 24 | private static final String TAG = "AudioEncoder"; 25 | private static final String AUDIO_MIME_TYPE = "audio/mp4a-latm"; 26 | private static final boolean VERBOSE = false; 27 | // Muxer state 28 | private static final int TOTAL_NUM_TRACKS = 1; 29 | // Audio state 30 | private static long audioBytesReceived = 0; 31 | private static int numTracksAdded = 0; 32 | boolean eosReceived = false; 33 | boolean eosSentToAudioEncoder = false; 34 | boolean stopReceived = false; 35 | long audioStartTime = 0; 36 | int frameCount = 0; 37 | int totalInputAudioFrameCount = 0; // testing 38 | int totalOutputAudioFrameCount = 0; 39 | Context c; 40 | int encodingServiceQueueLength = 0; 41 | private MediaFormat audioFormat; 42 | private MediaCodec mAudioEncoder; 43 | private TrackIndex mAudioTrackIndex = new TrackIndex(); 44 | private MediaMuxer mMuxer; 45 | private boolean mMuxerStarted; 46 | private MediaCodec.BufferInfo mAudioBufferInfo; 47 | private ExecutorService encodingService = Executors.newSingleThreadExecutor(); // re-use encodingService 48 | 49 | AudioSoftwarePoller audioSoftwarePoller; 50 | 51 | public AudioEncoder(Context c) { 52 | this.c = c; 53 | prepare(); 54 | } 55 | 56 | public void setAudioSoftwarePoller(AudioSoftwarePoller audioSoftwarePoller){ 57 | this.audioSoftwarePoller = audioSoftwarePoller; 58 | } 59 | 60 | 61 | private void prepare() { 62 | audioBytesReceived = 0; 63 | numTracksAdded = 0; 64 | frameCount = 0; 65 | eosReceived = false; 66 | eosSentToAudioEncoder = false; 67 | stopReceived = false; 68 | File f = FileUtils.createTempFileInRootAppStorage("test_" + new Date().getTime() + ".m4a"); 69 | Toast.makeText(c, "Saving audio to: " + f.getAbsolutePath(), Toast.LENGTH_LONG).show(); 70 | 71 | mAudioBufferInfo = new MediaCodec.BufferInfo(); 72 | 73 | audioFormat = new MediaFormat(); 74 | audioFormat.setString(MediaFormat.KEY_MIME, AUDIO_MIME_TYPE); 75 | audioFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC); 76 | audioFormat.setInteger(MediaFormat.KEY_SAMPLE_RATE, 44100); 77 | audioFormat.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1); 78 | audioFormat.setInteger(MediaFormat.KEY_BIT_RATE, 128000); 79 | audioFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 16384); 80 | 81 | try { 82 | mAudioEncoder = MediaCodec.createEncoderByType(AUDIO_MIME_TYPE); 83 | } catch (IOException e) { 84 | e.printStackTrace(); 85 | } 86 | mAudioEncoder.configure(audioFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); 87 | mAudioEncoder.start(); 88 | 89 | try { 90 | mMuxer = new MediaMuxer(f.getAbsolutePath(), MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4); 91 | } catch (IOException ioe) { 92 | throw new RuntimeException("MediaMuxer creation failed", ioe); 93 | } 94 | } 95 | 96 | public void stop() { 97 | if (!encodingService.isShutdown()) 98 | encodingService.submit(new EncoderTask(this, EncoderTaskType.FINALIZE_ENCODER)); 99 | } 100 | 101 | /** 102 | * Called from encodingService 103 | */ 104 | public void _stop() { 105 | stopReceived = true; 106 | eosReceived = true; 107 | logStatistics(); 108 | } 109 | 110 | public void closeEncoderAndMuxer(MediaCodec encoder, MediaCodec.BufferInfo bufferInfo, TrackIndex trackIndex) { 111 | drainEncoder(encoder, bufferInfo, trackIndex, true); 112 | try { 113 | encoder.stop(); 114 | encoder.release(); 115 | encoder = null; 116 | closeMuxer(); 117 | } catch (Exception e) { 118 | e.printStackTrace(); 119 | } 120 | } 121 | 122 | public void closeEncoder(MediaCodec encoder, MediaCodec.BufferInfo bufferInfo, TrackIndex trackIndex) { 123 | drainEncoder(encoder, bufferInfo, trackIndex, true); 124 | try { 125 | encoder.stop(); 126 | encoder.release(); 127 | encoder = null; 128 | } catch (Exception e) { 129 | e.printStackTrace(); 130 | } 131 | } 132 | 133 | public void closeMuxer() { 134 | mMuxer.stop(); 135 | mMuxer.release(); 136 | mMuxer = null; 137 | mMuxerStarted = false; 138 | } 139 | 140 | /** 141 | * temp restriction: Always call after offerVideoEncoder 142 | * 143 | * @param input 144 | */ 145 | public void offerAudioEncoder(byte[] input, long presentationTimeStampNs) { 146 | if (!encodingService.isShutdown()) { 147 | //long thisFrameTime = (presentationTimeNs == 0) ? System.nanoTime() : presentationTimeNs; 148 | encodingService.submit(new EncoderTask(this, input, presentationTimeStampNs)); 149 | encodingServiceQueueLength++; 150 | } 151 | 152 | } 153 | 154 | /** 155 | * temp restriction: Always call after _offerVideoEncoder 156 | * 157 | * @param input 158 | * @param presentationTimeNs 159 | */ 160 | private void _offerAudioEncoder(byte[] input, long presentationTimeNs) { 161 | if (audioBytesReceived == 0) { 162 | audioStartTime = presentationTimeNs; 163 | } 164 | totalInputAudioFrameCount++; 165 | audioBytesReceived += input.length; 166 | if (eosSentToAudioEncoder && stopReceived || input == null) { 167 | logStatistics(); 168 | if (eosReceived) { 169 | Log.i(TAG, "EOS received in offerAudioEncoder"); 170 | closeEncoderAndMuxer(mAudioEncoder, mAudioBufferInfo, mAudioTrackIndex); 171 | eosSentToAudioEncoder = true; 172 | if (!stopReceived) { 173 | // swap encoder 174 | prepare(); 175 | } else { 176 | Log.i(TAG, "Stopping Encoding Service"); 177 | encodingService.shutdown(); 178 | } 179 | } 180 | return; 181 | } 182 | // transfer previously encoded data to muxer 183 | drainEncoder(mAudioEncoder, mAudioBufferInfo, mAudioTrackIndex, false); 184 | // send current frame data to encoder 185 | try { 186 | ByteBuffer[] inputBuffers = mAudioEncoder.getInputBuffers(); 187 | int inputBufferIndex = mAudioEncoder.dequeueInputBuffer(-1); 188 | if (inputBufferIndex >= 0) { 189 | ByteBuffer inputBuffer = inputBuffers[inputBufferIndex]; 190 | inputBuffer.clear(); 191 | inputBuffer.put(input); 192 | if(audioSoftwarePoller != null){ 193 | audioSoftwarePoller.recycleInputBuffer(input); 194 | } 195 | long presentationTimeUs = (presentationTimeNs - audioStartTime) / 1000; 196 | if (eosReceived) { 197 | Log.i(TAG, "EOS received in offerEncoder"); 198 | mAudioEncoder.queueInputBuffer(inputBufferIndex, 0, input.length, presentationTimeUs, MediaCodec.BUFFER_FLAG_END_OF_STREAM); 199 | closeEncoderAndMuxer(mAudioEncoder, mAudioBufferInfo, mAudioTrackIndex); // always called after video, so safe to close muxer 200 | eosSentToAudioEncoder = true; 201 | if (stopReceived) { 202 | Log.i(TAG, "Stopping Encoding Service"); 203 | encodingService.shutdown(); 204 | } 205 | } else { 206 | mAudioEncoder.queueInputBuffer(inputBufferIndex, 0, input.length, presentationTimeUs, 0); 207 | } 208 | } 209 | } catch (Throwable t) { 210 | Log.e(TAG, "_offerAudioEncoder exception"); 211 | t.printStackTrace(); 212 | } 213 | } 214 | 215 | /** 216 | * Extracts all pending data from the encoder and forwards it to the muxer. 217 | *

218 | * If endOfStream is not set, this returns when there is no more data to drain. If it 219 | * is set, we send EOS to the encoder, and then iterate until we see EOS on the output. 220 | * Calling this with endOfStream set should be done once, right before stopping the muxer. 221 | *

222 | * We're just using the muxer to get a .mp4 file (instead of a raw H.264 stream). We're 223 | * not recording audio. 224 | */ 225 | private void drainEncoder(MediaCodec encoder, MediaCodec.BufferInfo bufferInfo, TrackIndex trackIndex, boolean endOfStream) { 226 | final int TIMEOUT_USEC = 100; 227 | if (VERBOSE) Log.d(TAG, "drainEncoder(" + endOfStream + ")"); 228 | ByteBuffer[] encoderOutputBuffers = encoder.getOutputBuffers(); 229 | while (true) { 230 | int encoderStatus = encoder.dequeueOutputBuffer(bufferInfo, TIMEOUT_USEC); 231 | if (encoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER) { 232 | // no output available yet 233 | if (!endOfStream) { 234 | break; // out of while 235 | } else { 236 | if (VERBOSE) Log.d(TAG, "no output available, spinning to await EOS"); 237 | } 238 | } else if (encoderStatus == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { 239 | // not expected for an encoder 240 | encoderOutputBuffers = encoder.getOutputBuffers(); 241 | } else if (encoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { 242 | // should happen before receiving buffers, and should only happen once 243 | 244 | if (mMuxerStarted) { 245 | throw new RuntimeException("format changed after muxer start"); 246 | } 247 | MediaFormat newFormat = encoder.getOutputFormat(); 248 | 249 | // now that we have the Magic Goodies, start the muxer 250 | trackIndex.index = mMuxer.addTrack(newFormat); 251 | numTracksAdded++; 252 | Log.d(TAG, "encoder output format changed: " + newFormat + ". Added track index: " + trackIndex.index); 253 | if (numTracksAdded == TOTAL_NUM_TRACKS) { 254 | mMuxer.start(); 255 | mMuxerStarted = true; 256 | Log.i(TAG, "All tracks added. Muxer started"); 257 | } 258 | 259 | } else if (encoderStatus < 0) { 260 | Log.w(TAG, "unexpected result from encoder.dequeueOutputBuffer: " + 261 | encoderStatus); 262 | // let's ignore it 263 | } else { 264 | ByteBuffer encodedData = encoderOutputBuffers[encoderStatus]; 265 | if (encodedData == null) { 266 | throw new RuntimeException("encoderOutputBuffer " + encoderStatus + 267 | " was null"); 268 | } 269 | 270 | 271 | if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) { 272 | // The codec config data was pulled out and fed to the muxer when we got 273 | // the INFO_OUTPUT_FORMAT_CHANGED status. Ignore it. 274 | if (VERBOSE) Log.d(TAG, "ignoring BUFFER_FLAG_CODEC_CONFIG"); 275 | bufferInfo.size = 0; 276 | } 277 | 278 | 279 | if (bufferInfo.size != 0) { 280 | if (!mMuxerStarted) { 281 | throw new RuntimeException("muxer hasn't started"); 282 | } 283 | 284 | // adjust the ByteBuffer values to match BufferInfo (not needed?) 285 | encodedData.position(bufferInfo.offset); 286 | encodedData.limit(bufferInfo.offset + bufferInfo.size); 287 | mMuxer.writeSampleData(trackIndex.index, encodedData, bufferInfo); 288 | } 289 | 290 | encoder.releaseOutputBuffer(encoderStatus, false); 291 | 292 | if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { 293 | if (!endOfStream) { 294 | Log.w(TAG, "reached end of stream unexpectedly"); 295 | } else { 296 | if (VERBOSE) Log.d(TAG, "end of stream reached"); 297 | } 298 | break; // out of while 299 | } 300 | } 301 | } 302 | long endTime = System.nanoTime(); 303 | } 304 | 305 | private void logStatistics() { 306 | Log.i(TAG + "-Stats", "audio frames input: " + totalInputAudioFrameCount + " output: " + totalOutputAudioFrameCount); 307 | } 308 | 309 | enum EncoderTaskType { 310 | ENCODE_FRAME, /*SHIFT_ENCODER,*/ FINALIZE_ENCODER; 311 | } 312 | 313 | // Can't pass an int by reference in Java... 314 | class TrackIndex { 315 | int index = 0; 316 | } 317 | 318 | private class EncoderTask implements Runnable { 319 | private static final String TAG = "encoderTask"; 320 | boolean is_initialized = false; 321 | long presentationTimeNs; 322 | private AudioEncoder encoder; 323 | private EncoderTaskType type; 324 | private byte[] audio_data; 325 | 326 | public EncoderTask(AudioEncoder encoder, EncoderTaskType type) { 327 | setEncoder(encoder); 328 | this.type = type; 329 | switch (type) { 330 | /* 331 | case SHIFT_ENCODER: 332 | setShiftEncoderParams(); 333 | break; 334 | */ 335 | case FINALIZE_ENCODER: 336 | setFinalizeEncoderParams(); 337 | break; 338 | } 339 | } 340 | 341 | public EncoderTask(AudioEncoder encoder, byte[] audio_data, long pts) { 342 | setEncoder(encoder); 343 | setEncodeFrameParams(audio_data, pts); 344 | } 345 | 346 | public EncoderTask(AudioEncoder encoder) { 347 | setEncoder(encoder); 348 | setFinalizeEncoderParams(); 349 | } 350 | 351 | private void setEncoder(AudioEncoder encoder) { 352 | this.encoder = encoder; 353 | } 354 | 355 | private void setFinalizeEncoderParams() { 356 | is_initialized = true; 357 | } 358 | 359 | private void setEncodeFrameParams(byte[] audio_data, long pts) { 360 | this.audio_data = audio_data; 361 | this.presentationTimeNs = pts; 362 | 363 | is_initialized = true; 364 | this.type = EncoderTaskType.ENCODE_FRAME; 365 | } 366 | 367 | private void encodeFrame() { 368 | if (encoder != null && audio_data != null) { 369 | encoder._offerAudioEncoder(audio_data, presentationTimeNs); 370 | audio_data = null; 371 | } 372 | } 373 | 374 | private void finalizeEncoder() { 375 | encoder._stop(); 376 | } 377 | 378 | @Override 379 | public void run() { 380 | if (is_initialized) { 381 | switch (type) { 382 | case ENCODE_FRAME: 383 | encodeFrame(); 384 | break; 385 | /* 386 | case SHIFT_ENCODER: 387 | shiftEncoder(); 388 | break; 389 | */ 390 | case FINALIZE_ENCODER: 391 | finalizeEncoder(); 392 | break; 393 | 394 | } 395 | // prevent multiple execution of same task 396 | is_initialized = false; 397 | encodingServiceQueueLength -= 1; 398 | //Log.i(TAG, "EncodingService Queue length: " + encodingServiceQueueLength); 399 | } else { 400 | Log.e(TAG, "run() called but EncoderTask not initialized"); 401 | } 402 | 403 | } 404 | 405 | } 406 | } -------------------------------------------------------------------------------- /app/src/main/java/com/chezi008/mp4muxerdemo/hw/AudioSoftwarePoller.java: -------------------------------------------------------------------------------- 1 | package com.chezi008.mp4muxerdemo.hw; 2 | 3 | import android.media.AudioFormat; 4 | import android.media.AudioRecord; 5 | import android.media.MediaRecorder; 6 | import android.util.Log; 7 | 8 | import java.util.concurrent.ArrayBlockingQueue; 9 | 10 | /* 11 | * This class polls audio from the microphone and feeds an 12 | * AudioEncoder. Audio buffers are recycled between this class and the AudioEncoder 13 | * 14 | * Usage: 15 | * 16 | * 1. AudioSoftwarePoller recorder = new AudioSoftwarePoller(); 17 | * 1a (optional): recorder.setSamplesPerFrame(NUM_SAMPLES_PER_CODEC_FRAME) 18 | * 2. recorder.setAudioEncoder(myAudioEncoder) 19 | * 2. recorder.startPolling(); 20 | * 3. recorder.stopPolling(); 21 | */ 22 | public class AudioSoftwarePoller { 23 | public static final String TAG = "AudioSoftwarePoller"; 24 | public static final int SAMPLE_RATE = 44100; 25 | public static final int CHANNEL_CONFIG = AudioFormat.CHANNEL_IN_MONO; 26 | public static final int AUDIO_FORMAT = AudioFormat.ENCODING_PCM_16BIT; 27 | public static final int FRAMES_PER_BUFFER = 24; // 1 sec @ 1024 samples/frame (aac) 28 | public static long US_PER_FRAME = 0; 29 | public static boolean is_recording = false; 30 | final boolean VERBOSE = false; 31 | public RecorderTask recorderTask = new RecorderTask(); 32 | 33 | AudioEncoder audioEncoder; 34 | 35 | public AudioSoftwarePoller() { 36 | } 37 | 38 | 39 | public void setAudioEncoder(AudioEncoder avcEncoder) { 40 | this.audioEncoder = avcEncoder; 41 | } 42 | 43 | /** 44 | * Set the number of samples per frame (Default is 1024). Call this before startPolling(). 45 | * The output of emptyBuffer() will be equal to, or a multiple of, this value. 46 | * 47 | * @param samples_per_frame The desired audio frame size in samples. 48 | */ 49 | public void setSamplesPerFrame(int samples_per_frame) { 50 | if (!is_recording) 51 | recorderTask.samples_per_frame = samples_per_frame; 52 | } 53 | 54 | /** 55 | * Return the number of microseconds represented by each audio frame 56 | * calculated with the sampling rate and samples per frame 57 | * @return 58 | */ 59 | public long getMicroSecondsPerFrame(){ 60 | if(US_PER_FRAME == 0){ 61 | US_PER_FRAME = (SAMPLE_RATE / recorderTask.samples_per_frame) * 1000000; 62 | } 63 | return US_PER_FRAME; 64 | } 65 | 66 | public void recycleInputBuffer(byte[] buffer){ 67 | recorderTask.data_buffer.offer(buffer); 68 | } 69 | 70 | /** 71 | * Begin polling audio and transferring it to the buffer. Call this before emptyBuffer(). 72 | */ 73 | public void startPolling() { 74 | new Thread(recorderTask).start(); 75 | } 76 | 77 | /** 78 | * Stop polling audio. 79 | */ 80 | public void stopPolling() { 81 | is_recording = false; // will stop recording after next sample received 82 | // by recorderTask 83 | } 84 | 85 | public class RecorderTask implements Runnable { 86 | public int buffer_size; 87 | //public int samples_per_frame = 1024; // codec-specific 88 | public int samples_per_frame = 2048; // codec-specific 89 | public int buffer_write_index = 0; // last buffer index written to 90 | //public byte[] data_buffer; 91 | public int total_frames_written = 0; 92 | 93 | ArrayBlockingQueue data_buffer = new ArrayBlockingQueue(50); 94 | 95 | int read_result = 0; 96 | 97 | public void run() { 98 | int min_buffer_size = AudioRecord.getMinBufferSize(SAMPLE_RATE, CHANNEL_CONFIG, AUDIO_FORMAT); 99 | 100 | buffer_size = samples_per_frame * FRAMES_PER_BUFFER; 101 | 102 | // Ensure buffer is adequately sized for the AudioRecord 103 | // object to initialize 104 | if (buffer_size < min_buffer_size) 105 | buffer_size = ((min_buffer_size / samples_per_frame) + 1) * samples_per_frame * 2; 106 | 107 | //data_buffer = new byte[samples_per_frame]; // filled directly by hardware 108 | for(int x=0; x < 25; x++) 109 | data_buffer.add(new byte[samples_per_frame]); 110 | 111 | AudioRecord audio_recorder; 112 | audio_recorder = new AudioRecord( 113 | MediaRecorder.AudioSource.MIC, // source 114 | SAMPLE_RATE, // sample rate, hz 115 | CHANNEL_CONFIG, // channels 116 | AUDIO_FORMAT, // audio format 117 | buffer_size); // buffer size (bytes) 118 | 119 | 120 | audio_recorder.startRecording(); 121 | is_recording = true; 122 | Log.i("AudioSoftwarePoller", "SW recording begin"); 123 | long audioPresentationTimeNs; 124 | while (is_recording) { 125 | //read_result = audio_recorder.read(data_buffer, buffer_write_index, samples_per_frame); 126 | audioPresentationTimeNs = System.nanoTime(); 127 | byte[] this_buffer; 128 | if(data_buffer.isEmpty()){ 129 | this_buffer = new byte[samples_per_frame]; 130 | //Log.i(TAG, "Audio buffer empty. added new buffer"); 131 | }else{ 132 | this_buffer = data_buffer.poll(); 133 | } 134 | read_result = audio_recorder.read(this_buffer, 0, samples_per_frame); 135 | if (VERBOSE) 136 | Log.i("AudioSoftwarePoller-FillBuffer", String.valueOf(buffer_write_index) + " - " + String.valueOf(buffer_write_index + samples_per_frame - 1)); 137 | if(read_result == AudioRecord.ERROR_BAD_VALUE || read_result == AudioRecord.ERROR_INVALID_OPERATION) 138 | Log.e("AudioSoftwarePoller", "Read error"); 139 | //buffer_write_index = (buffer_write_index + samples_per_frame) % buffer_size; 140 | total_frames_written++; 141 | if(audioEncoder != null){ 142 | audioEncoder.offerAudioEncoder(this_buffer, audioPresentationTimeNs); 143 | } 144 | } 145 | if (audio_recorder != null) { 146 | audio_recorder.setRecordPositionUpdateListener(null); 147 | audio_recorder.release(); 148 | audio_recorder = null; 149 | Log.i("AudioSoftwarePoller", "stopped"); 150 | } 151 | } 152 | } 153 | 154 | } -------------------------------------------------------------------------------- /app/src/main/java/com/chezi008/mp4muxerdemo/hw/FileUtils.java: -------------------------------------------------------------------------------- 1 | package com.chezi008.mp4muxerdemo.hw; 2 | 3 | import android.content.Context; 4 | import android.os.Environment; 5 | import android.util.Log; 6 | 7 | import com.chezi008.mp4muxerdemo.file.FileConstant; 8 | 9 | import java.io.File; 10 | import java.io.IOException; 11 | 12 | public class FileUtils { 13 | 14 | static final String TAG = "FileUtils"; 15 | 16 | static final String OUTPUT_DIR = "HWEncodingExperiments"; // Directory relative to External or Internal (fallback) Storage 17 | 18 | /** 19 | * Returns a Java File initialized to a directory of given name 20 | * at the root storage location, with preference to external storage. 21 | * If the directory did not exist, it will be created at the conclusion of this call. 22 | * If a file with conflicting name exists, this method returns null; 23 | * 24 | * @param c the context to determine the internal storage location, if external is unavailable 25 | * @param directory_name the name of the directory desired at the storage location 26 | * @return a File pointing to the storage directory, or null if a file with conflicting name 27 | * exists 28 | */ 29 | public static File getRootStorageDirectory(Context c, String directory_name){ 30 | File result; 31 | // First, try getting access to the sdcard partition 32 | if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){ 33 | Log.d(TAG,"Using sdcard"); 34 | result = new File(Environment.getExternalStorageDirectory(), directory_name); 35 | } else { 36 | // Else, use the internal storage directory for this application 37 | Log.d(TAG,"Using internal storage"); 38 | result = new File(c.getApplicationContext().getFilesDir(), directory_name); 39 | } 40 | 41 | if(!result.exists()) 42 | result.mkdir(); 43 | else if(result.isFile()){ 44 | return null; 45 | } 46 | Log.d("getRootStorageDirectory", result.getAbsolutePath()); 47 | return result; 48 | } 49 | 50 | /** 51 | * Returns a Java File initialized to a directory of given name 52 | * within the given location. 53 | * 54 | * @param parent_directory a File representing the directory in which the new child will reside 55 | * @return a File pointing to the desired directory, or null if a file with conflicting name 56 | * exists or if getRootStorageDirectory was not called first 57 | */ 58 | public static File getStorageDirectory(File parent_directory, String new_child_directory_name){ 59 | 60 | File result = new File(parent_directory, new_child_directory_name); 61 | if(!result.exists()) 62 | if(result.mkdir()) 63 | return result; 64 | else{ 65 | Log.e("getStorageDirectory", "Error creating " + result.getAbsolutePath()); 66 | return null; 67 | } 68 | else if(result.isFile()){ 69 | return null; 70 | } 71 | 72 | Log.d("getStorageDirectory", "directory ready: " + result.getAbsolutePath()); 73 | return result; 74 | } 75 | 76 | /** 77 | * Returns a TempFile with given root, filename, and extension. 78 | * The resulting TempFile is safe for use with Android's MediaRecorder 79 | * @param c 80 | * @param root 81 | * @param filename 82 | * @param extension 83 | * @return 84 | */ 85 | public static File createTempFile(Context c, File root, String filename, String extension){ 86 | File output = null; 87 | try { 88 | if(filename != null){ 89 | if(!extension.contains(".")) 90 | extension = "." + extension; 91 | output = new File(root, filename + extension); 92 | output.createNewFile(); 93 | //output = File.createTempFile(filename, extension, root); 94 | Log.i(TAG, "Created temp file: " + output.getAbsolutePath()); 95 | } 96 | return output; 97 | } catch (IOException e) { 98 | e.printStackTrace(); 99 | return null; 100 | } 101 | } 102 | 103 | 104 | public static File createTempFileInRootAppStorage( String filename){ 105 | File recordingDir = new File(FileConstant.baseFile,filename); 106 | return recordingDir; 107 | } 108 | 109 | } -------------------------------------------------------------------------------- /app/src/main/java/com/chezi008/mp4muxerdemo/hw/HWRecorderActivity.java: -------------------------------------------------------------------------------- 1 | package com.chezi008.mp4muxerdemo.hw; 2 | 3 | import android.app.Activity; 4 | import android.os.Bundle; 5 | import android.util.Log; 6 | import android.view.View; 7 | import android.widget.Button; 8 | 9 | import com.chezi008.mp4muxerdemo.R; 10 | 11 | public class HWRecorderActivity extends Activity { 12 | private static final String TAG = "CameraToMpegTest"; 13 | 14 | AudioEncoder mEncoder; 15 | AudioSoftwarePoller audioPoller; 16 | boolean recording = false; 17 | 18 | Button recordButton; 19 | 20 | protected void onCreate (Bundle savedInstanceState){ 21 | super.onCreate(savedInstanceState); 22 | // getActionBar().setTitle(""); 23 | setContentView(R.layout.activity_hwrecorder); 24 | recordButton = (Button) findViewById(R.id.recordButton); 25 | 26 | // test MediaCodec capabilities 27 | /* 28 | for(int i = MediaCodecList.getCodecCount() - 1; i >= 0; i--){ 29 | MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i); 30 | if(codecInfo.isEncoder()){ 31 | for(String t : codecInfo.getSupportedTypes()){ 32 | try{ 33 | Log.i("CodecCapability", t); 34 | Log.i("CodecCapability", codecInfo.getCapabilitiesForType(t).toString()); 35 | } catch(IllegalArgumentException e){ 36 | e.printStackTrace(); 37 | } 38 | } 39 | } 40 | } 41 | */ 42 | } 43 | 44 | 45 | public void onRecordButtonClick(View v){ 46 | recording = !recording; 47 | 48 | Log.i(TAG, "Record button hit. Start: " + String.valueOf(recording)); 49 | 50 | if(recording){ 51 | recordButton.setText("停止录制"); 52 | 53 | mEncoder = new AudioEncoder(getApplicationContext()); 54 | audioPoller = new AudioSoftwarePoller(); 55 | audioPoller.setAudioEncoder(mEncoder); 56 | mEncoder.setAudioSoftwarePoller(audioPoller); 57 | audioPoller.startPolling(); 58 | }else{ 59 | recordButton.setText("开始录制"); 60 | if(mEncoder != null){ 61 | audioPoller.stopPolling(); 62 | mEncoder.stop(); 63 | } 64 | } 65 | 66 | } 67 | 68 | static byte[] audioData; 69 | private static byte[] getSimulatedAudioInput(){ 70 | int magnitude = 10; 71 | if(audioData == null){ 72 | //audioData = new byte[1024]; 73 | audioData = new byte[1470]; // this is roughly equal to the audio expected between 30 fps frames 74 | for(int x=0; x= '0' && hex_char1 <='9') 131 | int_ch1 = (hex_char1-48)*16; //// 0 的Ascll - 48 132 | else if(hex_char1 >= 'A' && hex_char1 <='F') 133 | int_ch1 = (hex_char1-55)*16; //// A 的Ascll - 65 134 | else 135 | return null; 136 | i++; 137 | char hex_char2 = hexString.charAt(i); ///两位16进制数中的第二位(低位) 138 | int int_ch2; 139 | if(hex_char2 >= '0' && hex_char2 <='9') 140 | int_ch2 = (hex_char2-48); //// 0 的Ascll - 48 141 | else if(hex_char2 >= 'A' && hex_char2 <='F') 142 | int_ch2 = hex_char2-55; //// A 的Ascll - 65 143 | else 144 | return null; 145 | int_ch = int_ch1+int_ch2; 146 | retData[i/2]=(byte) int_ch;//将转化后的数放入Byte里 147 | } 148 | return retData; 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_hwrecorder.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 |