├── .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 |
19 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
18 |
19 |
26 |
27 |
35 |
43 |
44 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_mp4_v2.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
16 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_muxer_mp4.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
15 |
22 |
29 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chezi008/mp4muxer/97bbae69d1e0b0d25b883866b60246e1a9c4be10/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chezi008/mp4muxer/97bbae69d1e0b0d25b883866b60246e1a9c4be10/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chezi008/mp4muxer/97bbae69d1e0b0d25b883866b60246e1a9c4be10/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chezi008/mp4muxer/97bbae69d1e0b0d25b883866b60246e1a9c4be10/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chezi008/mp4muxer/97bbae69d1e0b0d25b883866b60246e1a9c4be10/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chezi008/mp4muxer/97bbae69d1e0b0d25b883866b60246e1a9c4be10/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chezi008/mp4muxer/97bbae69d1e0b0d25b883866b60246e1a9c4be10/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chezi008/mp4muxer/97bbae69d1e0b0d25b883866b60246e1a9c4be10/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chezi008/mp4muxer/97bbae69d1e0b0d25b883866b60246e1a9c4be10/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chezi008/mp4muxer/97bbae69d1e0b0d25b883866b60246e1a9c4be10/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Mp4MuxerDemo
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/test/java/com/chezi008/mp4muxerdemo/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.chezi008.mp4muxerdemo;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | jcenter()
6 | google()
7 | }
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:3.2.1'
10 |
11 | // NOTE: Do not place your application dependencies here; they belong
12 | // in the individual module build.gradle files
13 | }
14 | }
15 |
16 | allprojects {
17 | repositories {
18 | jcenter()
19 | google()
20 | }
21 | }
22 |
23 | task clean(type: Delete) {
24 | delete rootProject.buildDir
25 | }
26 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | org.gradle.jvmargs=-Xmx1536m
13 |
14 | # When configured, Gradle will run in incubating parallel mode.
15 | # This option should only be used with decoupled projects. More details, visit
16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
17 | # org.gradle.parallel=true
18 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chezi008/mp4muxer/97bbae69d1e0b0d25b883866b60246e1a9c4be10/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Dec 08 20:07:29 CST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/mp4muxer/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/mp4muxer/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion 26
5 |
6 |
7 |
8 | defaultConfig {
9 | minSdkVersion 18
10 | targetSdkVersion 25
11 | versionCode 1
12 | versionName "1.0"
13 |
14 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
15 |
16 | }
17 |
18 | buildTypes {
19 | release {
20 | minifyEnabled false
21 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
22 | }
23 | }
24 |
25 | }
26 |
27 | dependencies {
28 | implementation fileTree(dir: 'libs', include: ['*.jar'])
29 |
30 | implementation 'com.android.support:appcompat-v7:26.0.2'
31 | testImplementation 'junit:junit:4.12'
32 | androidTestImplementation 'com.android.support.test:runner:1.0.2'
33 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
34 | }
35 |
--------------------------------------------------------------------------------
/mp4muxer/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/mp4muxer/src/androidTest/java/com/chezi008/mp4muxer/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.chezi008.mp4muxer;
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 | * Instrumented 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() {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.chezi008.mp4muxer.test", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/mp4muxer/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/mp4muxer/src/main/java/com/chezi008/mp4muxer/Mp4Muxer.java:
--------------------------------------------------------------------------------
1 | package com.chezi008.mp4muxer;
2 |
3 | import android.media.MediaCodec;
4 | import android.media.MediaFormat;
5 | import android.media.MediaMuxer;
6 | import android.util.Log;
7 |
8 |
9 | import java.io.IOException;
10 | import java.nio.ByteBuffer;
11 |
12 | /**
13 | * 描述:mp4合成器
14 | * 作者:chezi008 on 2017/7/3 16:11
15 | * 邮箱:chezi008@163.com
16 | */
17 |
18 | public class Mp4Muxer {
19 | public static final boolean VERBOSE = true;
20 | private String TAG = getClass().getSimpleName();
21 |
22 | private MediaMuxer mMuxer;
23 | private int mVideoTrackIndex = -1, mAudioTrackIndex = -1;
24 |
25 | public Mp4Muxer(String outPath) {
26 | try {
27 | mMuxer = new MediaMuxer(outPath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
28 | } catch (IOException e) {
29 | e.printStackTrace();
30 | }
31 | }
32 |
33 | public void addVideoTrack(MediaFormat mediaFormat) {
34 | if (mVideoTrackIndex != -1)
35 | throw new RuntimeException("already add video tracks");
36 | mVideoTrackIndex = mMuxer.addTrack(mediaFormat);
37 | }
38 |
39 | public void addAudioTrack(MediaFormat mediaFormat) {
40 | if (mAudioTrackIndex != -1)
41 | throw new RuntimeException("already add audio tracks");
42 | mAudioTrackIndex = mMuxer.addTrack(mediaFormat);
43 | }
44 |
45 | public void start() {
46 | mMuxer.start();
47 | }
48 |
49 | synchronized
50 | public void writeVideoData(ByteBuffer outputBuffer, MediaCodec.BufferInfo bufferInfo) {
51 | if (mVideoTrackIndex == -1) {
52 | Log.i(TAG, String.format("pumpStream [%s] but muxer is not start.ignore..", "video"));
53 | return;
54 | }
55 | writeData(outputBuffer, bufferInfo, mVideoTrackIndex);
56 | }
57 |
58 | synchronized
59 | public void writeAudioData(ByteBuffer outputBuffer, MediaCodec.BufferInfo bufferInfo) {
60 | if (mAudioTrackIndex == -1) {
61 | Log.i(TAG, String.format("pumpStream [%s] but muxer is not start.ignore..", "audio"));
62 | return;
63 | }
64 | writeData(outputBuffer, bufferInfo, mAudioTrackIndex);
65 | }
66 |
67 | void writeData(ByteBuffer outputBuffer, MediaCodec.BufferInfo bufferInfo, int track) {
68 | if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
69 | // The codec config data was pulled out and fed to the muxer when we got
70 | // the INFO_OUTPUT_FORMAT_CHANGED status. Ignore it.
71 | bufferInfo.size = 0;
72 | } else if (bufferInfo.size != 0) {
73 | outputBuffer.position(bufferInfo.offset);
74 | outputBuffer.limit(bufferInfo.offset + bufferInfo.size);
75 | mMuxer.writeSampleData(track, outputBuffer, bufferInfo);
76 | if (VERBOSE)
77 | Log.d(TAG, String.format("send [%d] [" + bufferInfo.size + "] with timestamp:[%d] to muxer", track, bufferInfo.presentationTimeUs));
78 | if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
79 | if (VERBOSE)
80 | Log.i(TAG, "BUFFER_FLAG_END_OF_STREAM received");
81 | }
82 | }
83 | }
84 |
85 | synchronized
86 | public void stop() {
87 | if (mMuxer != null) {
88 | if (mVideoTrackIndex != -1 || mAudioTrackIndex != -1) {//mAudioTrackIndex != -1 &&
89 | if (VERBOSE)
90 | Log.i(TAG, String.format("muxer is started. now it will be stoped."));
91 | try {
92 | mMuxer.stop();
93 | mMuxer.release();
94 | } catch (IllegalStateException ex) {
95 | ex.printStackTrace();
96 | }
97 | }
98 | }
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/mp4muxer/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Mp4Muxer
3 |
4 |
--------------------------------------------------------------------------------
/mp4muxer/src/test/java/com/chezi008/mp4muxer/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.chezi008.mp4muxer;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/mtv.h264:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chezi008/mp4muxer/97bbae69d1e0b0d25b883866b60246e1a9c4be10/mtv.h264
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':mp4muxer'
2 |
--------------------------------------------------------------------------------