├── .idea
├── .gitignore
├── compiler.xml
├── encodings.xml
├── jarRepositories.xml
├── libraries
│ └── io_netty_netty_all_5_0_0_Alpha1.xml
├── misc.xml
├── rtmpServer.iml
├── sbt.xml
├── uiDesigner.xml
├── vcs.xml
└── workspace.xml
├── README.en.md
├── README.md
├── pom.xml
├── rtmpServer.iml
├── src
└── main
│ └── java
│ ├── AMF
│ ├── AMF.java
│ ├── AMFClass.java
│ └── AMFUtil.java
│ ├── Decoder
│ ├── AudioStreamDecoder.java
│ ├── RtmpDecoder.java
│ ├── RtmpDecoderbak.java
│ └── VideoStreamDecoder.java
│ ├── EnCoder
│ └── RtmpEncoder.java
│ ├── Handler
│ └── RtmpHandler.java
│ ├── META-INF
│ └── MANIFEST.MF
│ ├── Rtmp.java
│ ├── Rtmp
│ ├── Amf.java
│ ├── RtmpAudio.java
│ ├── RtmpBytesRead.java
│ ├── RtmpChunk.java
│ ├── RtmpChunkSize.java
│ ├── RtmpControl.java
│ ├── RtmpHandshake.java
│ ├── RtmpMessage.java
│ ├── RtmpNot.java
│ ├── RtmpNotify.java
│ ├── RtmpPacket.java
│ ├── RtmpPing.java
│ ├── RtmpResponse.java
│ ├── RtmpServerWindow.java
│ └── RtmpVideo.java
│ ├── User
│ ├── Publish.java
│ ├── PublishGroup.java
│ ├── Receive.java
│ └── ReceiveGroup.java
│ ├── Util
│ ├── AACDecoderSpecific.java
│ ├── AudioSpecificConfig.java
│ ├── Common.java
│ └── MsgType.java
│ └── test.java
└── target
└── classes
├── AMF
├── AMF.class
├── AMFClass.class
└── AMFUtil.class
├── Decoder
├── AudioStreamDecoder.class
├── RtmpDecoder$1.class
├── RtmpDecoder.class
├── RtmpDecoderbak.class
├── SendData.class
├── SendDecoder.class
└── VideoStreamDecoder.class
├── EnCoder
└── RtmpEncoder.class
├── Handler
└── RtmpHandler.class
├── META-INF
└── rtmpServer.kotlin_module
├── Rtmp$1.class
├── Rtmp.class
├── Rtmp
├── Amf.class
├── RtmpAudio.class
├── RtmpBytesRead.class
├── RtmpChunk.class
├── RtmpChunkSize.class
├── RtmpControl.class
├── RtmpHandshake.class
├── RtmpMessage.class
├── RtmpNot.class
├── RtmpNotify.class
├── RtmpPacket.class
├── RtmpPing.class
├── RtmpResponse.class
├── RtmpServerWindow.class
└── RtmpVideo.class
├── User
├── Publish.class
├── PublishGroup.class
├── Receive.class
└── ReceiveGroup.class
├── Util
├── AACDecoderSpecific.class
├── AudioSpecificConfig.class
├── Common.class
└── MsgType.class
└── test.class
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 | # Datasource local storage ignored files
5 | /dataSources/
6 | /dataSources.local.xml
7 | # Editor-based HTTP Client requests
8 | /httpRequests/
9 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/jarRepositories.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/.idea/libraries/io_netty_netty_all_5_0_0_Alpha1.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/.idea/rtmpServer.iml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/.idea/sbt.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/.idea/uiDesigner.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | -
6 |
7 |
8 | -
9 |
10 |
11 | -
12 |
13 |
14 | -
15 |
16 |
17 | -
18 |
19 |
20 |
21 |
22 |
23 | -
24 |
25 |
26 |
27 |
28 |
29 | -
30 |
31 |
32 |
33 |
34 |
35 | -
36 |
37 |
38 |
39 |
40 |
41 | -
42 |
43 |
44 |
45 |
46 | -
47 |
48 |
49 |
50 |
51 | -
52 |
53 |
54 |
55 |
56 | -
57 |
58 |
59 |
60 |
61 | -
62 |
63 |
64 |
65 |
66 | -
67 |
68 |
69 |
70 |
71 | -
72 |
73 |
74 | -
75 |
76 |
77 |
78 |
79 | -
80 |
81 |
82 |
83 |
84 | -
85 |
86 |
87 |
88 |
89 | -
90 |
91 |
92 |
93 |
94 | -
95 |
96 |
97 |
98 |
99 | -
100 |
101 |
102 | -
103 |
104 |
105 | -
106 |
107 |
108 | -
109 |
110 |
111 | -
112 |
113 |
114 |
115 |
116 | -
117 |
118 |
119 | -
120 |
121 |
122 |
123 |
124 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/workspace.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 | 1570624912476
95 |
96 |
97 | 1570624912476
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 | 1571039624065
138 |
139 |
140 |
141 | 1571039624065
142 |
143 |
144 | 1571132649103
145 |
146 |
147 |
148 | 1571132649103
149 |
150 |
151 | 1571217773128
152 |
153 |
154 |
155 | 1571217773128
156 |
157 |
158 | 1571384083045
159 |
160 |
161 |
162 | 1571384083045
163 |
164 |
165 | 1571648930857
166 |
167 |
168 |
169 | 1571648930857
170 |
171 |
172 | 1572255160271
173 |
174 |
175 |
176 | 1572255160271
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 | No facets are configured
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 | scala-sdk-2.12.8
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 |
291 |
292 |
293 | 1.8
294 |
295 |
296 |
297 |
298 |
299 |
300 |
301 |
302 |
303 |
304 |
305 | rtmpServer
306 |
307 |
308 |
309 |
310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 | 1.8
318 |
319 |
320 |
321 |
322 |
323 |
324 |
325 |
326 |
327 |
328 |
329 | io.netty:netty-all:5.0.0.Alpha1
330 |
331 |
332 |
333 |
334 |
335 |
336 |
337 |
338 |
339 |
340 |
341 |
--------------------------------------------------------------------------------
/README.en.md:
--------------------------------------------------------------------------------
1 | # rtmpServer
2 |
3 | #### Description
4 | {**When you're done, you can delete the content in this README and update the file with details for others getting started with your repository**}
5 |
6 | #### Software Architecture
7 | Software architecture description
8 |
9 | #### Installation
10 |
11 | 1. xxxx
12 | 2. xxxx
13 | 3. xxxx
14 |
15 | #### Instructions
16 |
17 | 1. xxxx
18 | 2. xxxx
19 | 3. xxxx
20 |
21 | #### Contribution
22 |
23 | 1. Fork the repository
24 | 2. Create Feat_xxx branch
25 | 3. Commit your code
26 | 4. Create Pull Request
27 |
28 |
29 | #### Gitee Feature
30 |
31 | 1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md
32 | 2. Gitee blog [blog.gitee.com](https://blog.gitee.com)
33 | 3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore)
34 | 4. The most valuable open source project [GVP](https://gitee.com/gvp)
35 | 5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help)
36 | 6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 基于netty4.0 实现的rtmp 协议demo 推拉流已经实现
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | rtmpServer
8 | rtmpServer
9 | 1.0-SNAPSHOT
10 |
11 |
12 |
13 | org.apache.maven.plugins
14 | maven-compiler-plugin
15 |
16 | 6
17 | 6
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | io.netty
26 | netty-all
27 | 4.1.42.Final
28 |
29 |
30 |
31 |
32 | org.jboss.netty
33 | netty
34 | 3.2.10.Final
35 |
36 |
37 |
38 | org.projectlombok
39 | lombok
40 | 1.16.18
41 | provided
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/rtmpServer.iml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/main/java/AMF/AMF.java:
--------------------------------------------------------------------------------
1 | package AMF;
2 |
3 | public class AMF {
4 | public final static byte
5 | Number = 0x00, // 0
6 | Boolean = 0x01, // 1
7 | String = 0x02, // 2
8 | UntypedObject = 0x03, // 3
9 | MovieClip = 0x04, // 4
10 | Null = 0x05, // 5
11 | Undefined = 0x06, // 6
12 | ReferencedObject = 0x07, // 7
13 | MixedArray = 0x08, // 8
14 | End = 0x09, // 9
15 | Array = 0x10, // 10
16 | Date = 0x11, // 11
17 | LongString = 0x12, // 12
18 | TypeAsObject = 0x13, // 13
19 | Recordset = 0x14, // 14
20 | Xml = 0x15, // 15
21 | TypedObject = 0x16, // 16
22 | AMF3data = 0x17; // 17
23 | }
24 |
--------------------------------------------------------------------------------
/src/main/java/AMF/AMFClass.java:
--------------------------------------------------------------------------------
1 | package AMF;
2 |
3 | public class AMFClass {
4 | public byte[] message;
5 | public int pos;
6 | public int version = 0;
7 | }
8 |
--------------------------------------------------------------------------------
/src/main/java/AMF/AMFUtil.java:
--------------------------------------------------------------------------------
1 | package AMF;
2 |
3 |
4 | import Util.Common;
5 |
6 | import java.lang.reflect.Field;
7 | import java.util.*;
8 |
9 |
10 | public class AMFUtil {
11 | /**
12 | * 加载 amf string
13 | * @param amfClass
14 | * @return
15 | */
16 | public static String load_amf_string(AMFClass amfClass) {
17 | String msg = "";
18 | if(amfClass.message[amfClass.pos] != AMF.String) {
19 | return msg;
20 | }
21 | amfClass.pos += 1;
22 | if(amfClass.pos + 2 > amfClass.message.length){
23 | System.out.println("string len 解析数据长度不足,数据错误");
24 | return msg;
25 | }
26 | byte[] strLenByte = {amfClass.message[amfClass.pos],amfClass.message[amfClass.pos+1]};
27 | int strLen = Common.byteToInt16(strLenByte);
28 | amfClass.pos += 2;
29 | if(amfClass.pos + strLen > amfClass.message.length){
30 | System.out.println("string message 解析数据长度不足,数据错误");
31 | return msg;
32 | }
33 | byte[] str = new byte[strLen];
34 | int index = 0;
35 | for(int i = amfClass.pos;i < amfClass.pos + strLen;i++){ //链接字符串
36 | str[index] = amfClass.message[i];
37 | index++;
38 | }
39 | amfClass.pos += strLen;
40 | msg = new String(str);
41 | return msg;
42 | }
43 |
44 | /**
45 | * amf string 写入
46 | * @param str
47 | * @return
48 | */
49 | public static byte[] writeString(String str) {
50 | List data = new ArrayList();
51 | data.add(AMF.String);
52 | byte[] strByte = str.getBytes();
53 | int len = strByte.length;
54 | byte[] lenByte = Common.intToByte(len); //转换 之后为小端模式,直接拿前两位 就可以了
55 | data.add(lenByte[1]); // rtmp 数据都为 大端模式
56 | data.add(lenByte[0]);
57 | for(byte val: strByte) {
58 | data.add(val);
59 | }
60 | return Common.conversionByteArray(data);
61 | }
62 |
63 | /**
64 | *
65 | * @param key
66 | * @return
67 | */
68 | public static byte[] writeKey(String key) {
69 | List data = new ArrayList();
70 | byte[] strByte = key.getBytes();
71 | int len = strByte.length;
72 | byte[] lenByte = Common.intToByte(len); //转换 之后为小端模式,直接拿前两位 就可以了
73 | data.add(lenByte[1]); // rtmp 数据都为 大端模式
74 | data.add(lenByte[0]);
75 | for(byte val: strByte) {
76 | data.add(val);
77 | }
78 | return Common.conversionByteArray(data);
79 | }
80 |
81 | /**
82 | * 编写boolean
83 | * @param val
84 | * @return
85 | */
86 | public static byte[] writeBoolean(boolean val) {
87 | byte[] data = new byte[2];
88 | data[0] = AMF.Boolean;
89 | data[1] = (byte) (val ? 0x01: 0x00);
90 | return data;
91 | }
92 |
93 | public static byte[] writeObject(Map objectData) {
94 | List data = new ArrayList();
95 | data.add(AMF.UntypedObject);
96 | for (Map.Entry entry : objectData.entrySet()) {
97 | try {
98 | byte[] valueByte = writeValue(entry.getValue()); //如果解析不出 数据,直接跳过去
99 | String key = entry.getKey();
100 | byte[] keyByte = writeKey(key);
101 | for(byte i : keyByte){
102 | data.add(i);
103 | }
104 | for(byte i: valueByte){
105 | data.add(i);
106 | }
107 | } catch (Exception e) {
108 | e.printStackTrace();
109 | }
110 |
111 | }
112 | data.add((byte) 0x00);
113 | data.add((byte) 0x00);
114 | data.add((byte) 0x09);
115 | return Common.conversionByteArray(data);
116 | }
117 |
118 | public static byte[] writeMixedArray(Map objectData) {
119 | List data = new ArrayList();
120 | data.add(AMF.MixedArray);
121 | for(byte i : Common.intToByte(0)){
122 | data.add(i);
123 | }
124 | for (Map.Entry entry : objectData.entrySet()) {
125 | try {
126 | byte[] valueByte = writeValue(entry.getValue()); //如果解析不出 数据,直接跳过去
127 | String key = entry.getKey();
128 | byte[] keyByte = writeKey(key);
129 | for(byte i : keyByte){
130 | data.add(i);
131 | }
132 | for(byte i: valueByte){
133 | data.add(i);
134 | }
135 | } catch (Exception e) {
136 | e.printStackTrace();
137 | }
138 |
139 | }
140 | data.add((byte) 0x00);
141 | data.add((byte) 0x00);
142 | data.add((byte) 0x09);
143 | return Common.conversionByteArray(data);
144 | }
145 |
146 |
147 | /**
148 | * 获取 object value
149 | * @param param
150 | * @return
151 | */
152 | public static byte[] writeValue(Object param) throws Exception {
153 | byte[] data;
154 | if (param instanceof Double) {
155 | double d = ((Double) param).doubleValue();
156 | data = writeNumber(d);
157 | } else if (param instanceof String) {
158 | String s = (String) param;
159 | data = writeString(s);
160 | } else if (param instanceof Boolean) {
161 | boolean b = ((Boolean) param).booleanValue();
162 | data = writeBoolean(b);
163 | } else if(param instanceof Map){
164 | Map map = objectToMap(param);
165 | data = writeObject(map);
166 | } else {
167 | throw new Exception("数据异常,没有找到解析value");
168 | }
169 | return data;
170 | }
171 |
172 | public static Map objectToMap(Object obj) {
173 | Map map = new HashMap();
174 | map = objectToMap(obj, false);
175 | return map;
176 | }
177 |
178 | public static Map objectToMap(Object obj, boolean keepNullVal) {
179 | if (obj == null) {
180 | return null;
181 | }
182 |
183 | Map map = new HashMap();
184 | try {
185 | Field[] declaredFields = obj.getClass().getDeclaredFields();
186 | for (Field field : declaredFields) {
187 | field.setAccessible(true);
188 | if (keepNullVal == true) {
189 | map.put(field.getName(), field.get(obj));
190 | } else {
191 | if (field.get(obj) != null && !"".equals(field.get(obj).toString())) {
192 | map.put(field.getName(), field.get(obj));
193 | }
194 | }
195 | }
196 | } catch (Exception e) {
197 | e.printStackTrace();
198 | }
199 | return map;
200 | }
201 |
202 | /**
203 | * amf number 写法
204 | * @param val
205 | * @return
206 | */
207 | public static byte[] writeNumber(double val){
208 | List data = new ArrayList();
209 | data.add(AMF.Number);
210 | byte[] doubleData = Common.reverseArray(Common.double2Bytes(val));
211 | for(byte i : doubleData){
212 | data.add(i);
213 | }
214 | return Common.conversionByteArray(data);
215 | }
216 |
217 | /**
218 | * amf null 写入方法
219 | * @return
220 | */
221 | public static byte writeNull() {
222 | return AMF.Null;
223 | }
224 |
225 | /**
226 | * 提取 amf 里面的 boolean
227 | * @param amfClass
228 | * @return
229 | */
230 | public static boolean load_amf_boolean(AMFClass amfClass) {
231 | if(amfClass.message[amfClass.pos] != AMF.Boolean) {
232 | System.out.println("boolean获取失败");
233 | return false;
234 | }
235 | amfClass.pos += 1;
236 | boolean flag = amfClass.message[amfClass.pos] != 0;
237 | amfClass.pos += 1;
238 | return flag;
239 | }
240 |
241 | /**
242 | * 加载 key
243 | * @param amfClass
244 | * @return
245 | */
246 | public static String load_amf_key(AMFClass amfClass) {
247 | String msg = "";
248 | if(amfClass.pos + 2 > amfClass.message.length){
249 | System.out.println("key len 解析数据长度不足,数据错误");
250 | return msg;
251 | }
252 | byte[] strLenByte = {amfClass.message[amfClass.pos],amfClass.message[amfClass.pos+1]};
253 | int strLen = Common.byteToInt16(strLenByte);
254 | amfClass.pos += 2;
255 | if(amfClass.pos + strLen > amfClass.message.length){
256 | System.out.println("key message 解析数据长度不足,数据错误");
257 | return msg;
258 | }
259 | byte[] str = new byte[strLen];
260 | int index = 0;
261 | for(int i = amfClass.pos;i < amfClass.pos + strLen;i++){ //链接字符串
262 | str[index] = amfClass.message[i];
263 | index++;
264 | }
265 | amfClass.pos += strLen;
266 | msg = new String(str);
267 | return msg;
268 | }
269 |
270 | /**
271 | * 解析 amf number
272 | * @param amfClass
273 | * @return
274 | */
275 | public static double load_amf_number(AMFClass amfClass){
276 | byte type = amfClass.message[amfClass.pos];
277 | if(type != AMF.Number){
278 | return -1;
279 | }
280 | amfClass.pos += 1;
281 | if(amfClass.pos + Common.AFM_NUMBER_LENGTH > amfClass.message.length) {
282 | return -1;
283 | }
284 | byte[] number = new byte[Common.AFM_NUMBER_LENGTH];
285 | int index = 0;
286 | for(int i = amfClass.pos; i < amfClass.pos + Common.AFM_NUMBER_LENGTH;i++) {
287 | number[index] = amfClass.message[i];
288 | index++;
289 | }
290 | // System.out.println (Common.bytes2hex(Common.reverseArray(number)));
291 | amfClass.pos += Common.AFM_NUMBER_LENGTH;
292 | return Common.bytesToDouble(Common.reverseArray(number));
293 | }
294 |
295 |
296 | // public static Object load_amf_value(AMFClass amfClass){
297 | // byte type = amfClass.message[amfClass.pos];
298 | // switch (type) {
299 | // case AMF.Number:
300 | // return load_amf_number(amfClass);
301 | // case AMF.String:
302 | // return load_amf_string(amfClass);
303 | // case AMF.UntypedObject:
304 | // return load_amf_object(amfClass);
305 | // case AMF.Boolean:
306 | // return load_amf_boolean(amfClass);
307 | // default:
308 | // System.out.println("其他消息" + type);
309 | // return null;
310 | // }
311 | // }
312 | /**
313 | * 解析 object value
314 | * @param amfClass
315 | * @return
316 | */
317 | public static Object load_amf(AMFClass amfClass) {
318 | byte type = amfClass.message[amfClass.pos];
319 | switch (type) {
320 | case AMF.Number:
321 | return load_amf_number(amfClass);
322 | case AMF.String:
323 | return load_amf_string(amfClass);
324 | case AMF.UntypedObject:
325 | return load_amf_object(amfClass);
326 | case AMF.Boolean:
327 | return load_amf_boolean(amfClass);
328 | case AMF.Null:
329 | amfClass.pos++;
330 | return null;
331 | case AMF.MixedArray:
332 | return load_amf_mixedArray(amfClass);
333 | default:
334 | System.out.println("其他消息" + type);
335 | return null;
336 | }
337 | }
338 |
339 | /**
340 | * 解析 amf object 数据
341 | * @param amfClass
342 | * @return
343 | */
344 | public static Map load_amf_object(AMFClass amfClass) {
345 | Map amfData = new HashMap();
346 | byte type = amfClass.message[amfClass.pos];
347 | if(type != AMF.UntypedObject){
348 | return null;
349 | }
350 | amfClass.pos += 1;
351 | while (true){
352 | String key = load_amf_key(amfClass);
353 | if(key.length() == 0){
354 | break;
355 | }
356 | Object value = load_amf(amfClass);
357 | amfData.put(key,value);
358 | // System.out.println(key +"==="+value);
359 | }
360 | //System.out.println(amfClass.pos);
361 | amfClass.pos += 1;// object 最后 结束为 00 00 09 取到最后是00 所以 +1 跳过 09 这个 字节
362 | return amfData;
363 | }
364 |
365 |
366 | /**
367 | * 解析 amf object 数据
368 | * @param amfClass
369 | * @return
370 | */
371 | public static Map load_amf_mixedArray(AMFClass amfClass) {
372 | Map amfData = new HashMap();
373 | byte type = amfClass.message[amfClass.pos];
374 | if(type != AMF.MixedArray){
375 | return null;
376 | }
377 | amfClass.pos += 1;
378 | if(amfClass.pos + 4 > amfClass.message.length) {
379 | return null;
380 | }
381 | amfClass.pos += 4;
382 | while (true){
383 | String key = load_amf_key(amfClass);
384 | if(key.length() == 0){
385 | break;
386 | }
387 | Object value = load_amf(amfClass);
388 | amfData.put(key,value);
389 | // System.out.println(key +"==="+value);
390 | }
391 | System.out.println(amfClass.pos);
392 | amfClass.pos += 1;// object 最后 结束为 00 00 09 取到最后是00 所以 +1 跳过 09 这个 字节
393 | return amfData;
394 | }
395 |
396 |
397 | }
398 |
--------------------------------------------------------------------------------
/src/main/java/Decoder/AudioStreamDecoder.java:
--------------------------------------------------------------------------------
1 | package Decoder;
2 |
3 | import Util.AACDecoderSpecific;
4 | import Util.AudioSpecificConfig;
5 | import Util.Common;
6 |
7 | public class AudioStreamDecoder {
8 | private int index = 0;
9 | AudioSpecificConfig ascAudioSpecificConfig = new AudioSpecificConfig();
10 | AACDecoderSpecific aacDecoderSpecific = new AACDecoderSpecific();
11 |
12 |
13 | public void decode(byte[] data) {
14 | byte type = data[index++];
15 | byte AACPacketType = data[index++];
16 | // System.out.println(Common.bytes2hex(data));
17 | if (AACPacketType == 0x00) {
18 | System.out.println(Common.bytes2hex(data));
19 | aacDecoderSpecific.nAudioFortmatType = (byte) ((type & 0xff & 0xff) >> 4);
20 | aacDecoderSpecific.nAudioSampleType = (byte) ((type & 0x0c & 0xff & 0xff) >> 2);
21 | aacDecoderSpecific.nAudioSizeType = (byte) ((type & 0x02 & 0xff & 0xff) >> 1);
22 | aacDecoderSpecific.nAudioStereo = (byte) (type & 0x01 & 0xff & 0xff);
23 | }
24 | // System.out.println(aacDecoderSpecific.nAudioFortmatType);
25 |
26 | byte[] packet = new byte[data.length - index];
27 | // System.out.println("packet length" + packet.length);
28 | // System.out.println("data length" + data.length);
29 | if (aacDecoderSpecific.nAudioFortmatType == 0x0a) {
30 | for (int i = 0; i < data.length - 2; i++) {
31 | packet[i] = data[index++];
32 | }
33 | if (AACPacketType == 0x00) {
34 | // Common.appendMethodA("D:\\test2.aac",packet);
35 | // unsigned short audioSpecificConfig = 0;
36 | short audioSpecificConfig = (short) ((data[2] & 0xff) << 8);
37 | audioSpecificConfig += 0x00ff & data[3];
38 | ascAudioSpecificConfig.nAudioObjectType = (byte) ((audioSpecificConfig & 0xF800) >> 11);
39 | ascAudioSpecificConfig.nSampleFrequencyIndex = (byte) ((audioSpecificConfig & 0x0780) >> 7);
40 | ascAudioSpecificConfig.nChannels = (byte) ((audioSpecificConfig & 0x78) >> 3);
41 | ascAudioSpecificConfig.nFrameLengthFlag = (byte) ((audioSpecificConfig & 0x04) >> 2);
42 | ascAudioSpecificConfig.nDependOnCoreCoder = (byte) ((audioSpecificConfig & 0x02) >> 1);
43 | ascAudioSpecificConfig.nExtensionFlag = (byte) (audioSpecificConfig & 0x01);
44 | System.out.println("nAudioObjectType" + ascAudioSpecificConfig.nAudioObjectType);
45 | System.out.println("nSampleFrequencyIndex" + ascAudioSpecificConfig.nSampleFrequencyIndex);
46 | System.out.println("nChannels" + ascAudioSpecificConfig.nChannels);
47 | // Common.appendMethodA("D:\\fuweicong.aac",Common.CreateADTS(ascAudioSpecificConfig,packet.length));
48 | // Common.appendMethodA("D:\\fuweicong.aac",packet); // 错误
49 | } else {
50 | Common.appendMethodA("D:\\fuweicong.aac",Common.CreateADTS(ascAudioSpecificConfig,packet.length + 7));
51 | Common.appendMethodA("D:\\fuweicong.aac",packet);
52 | }
53 | }
54 | index = 0;
55 | }
56 |
57 |
58 |
59 |
60 | }
61 |
--------------------------------------------------------------------------------
/src/main/java/Decoder/RtmpDecoder.java:
--------------------------------------------------------------------------------
1 | package Decoder;
2 |
3 | import Rtmp.*;
4 | import User.Receive;
5 | import User.ReceiveGroup;
6 | import Util.Common;
7 | import io.netty.buffer.ByteBuf;
8 | import io.netty.channel.ChannelHandlerContext;
9 | import io.netty.handler.codec.ByteToMessageDecoder;
10 |
11 | import java.util.*;
12 |
13 | public class RtmpDecoder extends ByteToMessageDecoder {
14 |
15 | // chunk message 数据
16 | private ByteBuf byteBuf = null;
17 |
18 | //握手数据
19 | private RtmpHandshake rtmpHandshake = new RtmpHandshake();
20 |
21 | private String path = null;
22 |
23 | AudioStreamDecoder audioStreamDecoder = new AudioStreamDecoder();
24 |
25 | private RtmpPacket rtmpPacket = null;
26 |
27 | private int chunkLength = Common.DEFAULT_CHUNK_MESSAGE_LENGTH;
28 |
29 | @Override
30 | protected void decode(ChannelHandlerContext ctx, ByteBuf in, List