|
1 | 1 | package main |
2 | 2 |
|
3 | 3 | import ( |
| 4 | + "bufio" |
| 5 | + "encoding/binary" |
4 | 6 | "fmt" |
5 | 7 | "io" |
6 | 8 | "log" |
@@ -326,48 +328,75 @@ func (c *rtspClient) run(wg sync.WaitGroup) { |
326 | 328 |
|
327 | 329 | // record |
328 | 330 | case "ANNOUNCE": |
329 | | - if _, ok := transports["RTP/AVP/UDP"]; !ok { |
330 | | - c.log("ERR: transport header does not contain RTP/AVP/UDP") |
331 | | - return |
332 | | - } |
333 | | - |
334 | 331 | if _, ok := transports["mode=record"]; !ok { |
335 | 332 | c.log("ERR: transport header does not contain mode=record") |
336 | 333 | return |
337 | 334 | } |
338 | 335 |
|
339 | | - clientPort1, clientPort2 := getPorts() |
340 | | - if clientPort1 == 0 || clientPort2 == 0 { |
341 | | - c.log("ERR: transport header does not have valid client ports (%s)", transport) |
342 | | - return |
343 | | - } |
| 336 | + if _, ok := transports["RTP/AVP/UDP"]; ok { |
| 337 | + clientPort1, clientPort2 := getPorts() |
| 338 | + if clientPort1 == 0 || clientPort2 == 0 { |
| 339 | + c.log("ERR: transport header does not have valid client ports (%s)", transport) |
| 340 | + return |
| 341 | + } |
| 342 | + |
| 343 | + err = rconn.WriteResponse(&rtsp.Response{ |
| 344 | + StatusCode: 200, |
| 345 | + Status: "OK", |
| 346 | + Headers: map[string]string{ |
| 347 | + "CSeq": cseq, |
| 348 | + "Transport": strings.Join([]string{ |
| 349 | + "RTP/AVP", |
| 350 | + "unicast", |
| 351 | + fmt.Sprintf("client_port=%d-%d", clientPort1, clientPort2), |
| 352 | + fmt.Sprintf("server_port=%d-%d", c.p.rtpPort, c.p.rtcpPort), |
| 353 | + "ssrc=1234ABCD", |
| 354 | + }, ";"), |
| 355 | + "Session": "12345678", |
| 356 | + }, |
| 357 | + }) |
| 358 | + if err != nil { |
| 359 | + c.log("ERR: %s", err) |
| 360 | + return |
| 361 | + } |
| 362 | + |
| 363 | + c.p.mutex.Lock() |
| 364 | + c.rtpProto = "udp" |
| 365 | + c.rtpPort = clientPort1 |
| 366 | + c.rtcpPort = clientPort2 |
| 367 | + c.state = "PRE_RECORD" |
| 368 | + c.p.mutex.Unlock() |
| 369 | + |
| 370 | + } else if _, ok := transports["RTP/AVP/TCP"]; ok { |
| 371 | + err = rconn.WriteResponse(&rtsp.Response{ |
| 372 | + StatusCode: 200, |
| 373 | + Status: "OK", |
| 374 | + Headers: map[string]string{ |
| 375 | + "CSeq": cseq, |
| 376 | + "Transport": strings.Join([]string{ |
| 377 | + "RTP/AVP/TCP", |
| 378 | + "unicast", |
| 379 | + "destionation=127.0.0.1", |
| 380 | + "source=127.0.0.1", |
| 381 | + }, ";"), |
| 382 | + "Session": "12345678", |
| 383 | + }, |
| 384 | + }) |
| 385 | + if err != nil { |
| 386 | + c.log("ERR: %s", err) |
| 387 | + return |
| 388 | + } |
| 389 | + |
| 390 | + c.p.mutex.Lock() |
| 391 | + c.rtpProto = "tcp" |
| 392 | + c.state = "PRE_RECORD" |
| 393 | + c.p.mutex.Unlock() |
344 | 394 |
|
345 | | - err = rconn.WriteResponse(&rtsp.Response{ |
346 | | - StatusCode: 200, |
347 | | - Status: "OK", |
348 | | - Headers: map[string]string{ |
349 | | - "CSeq": cseq, |
350 | | - "Transport": strings.Join([]string{ |
351 | | - "RTP/AVP", |
352 | | - "unicast", |
353 | | - fmt.Sprintf("client_port=%d-%d", clientPort1, clientPort2), |
354 | | - fmt.Sprintf("server_port=%d-%d", c.p.rtpPort, c.p.rtcpPort), |
355 | | - "ssrc=1234ABCD", |
356 | | - }, ";"), |
357 | | - "Session": "12345678", |
358 | | - }, |
359 | | - }) |
360 | | - if err != nil { |
361 | | - c.log("ERR: %s", err) |
| 395 | + } else { |
| 396 | + c.log("ERR: transport header does not contain a valid protocol (RTP/AVP or RTP/AVP/TCP) (%s)", transport) |
362 | 397 | return |
363 | 398 | } |
364 | 399 |
|
365 | | - c.p.mutex.Lock() |
366 | | - c.rtpPort = clientPort1 |
367 | | - c.rtcpPort = clientPort2 |
368 | | - c.state = "PRE_RECORD" |
369 | | - c.p.mutex.Unlock() |
370 | | - |
371 | 400 | default: |
372 | 401 | c.log("ERR: client is in state '%s'", c.state) |
373 | 402 | return |
@@ -398,12 +427,10 @@ func (c *rtspClient) run(wg sync.WaitGroup) { |
398 | 427 | c.state = "PLAY" |
399 | 428 | c.p.mutex.Unlock() |
400 | 429 |
|
401 | | - // when rtp protocol is TCP, the RTSP connection |
402 | | - // becomes a RTP connection. |
403 | | - // receive RTP feedback, do not parse it, wait until |
404 | | - // connection closes. |
| 430 | + // when rtp protocol is TCP, the RTSP connection becomes a RTP connection. |
| 431 | + // receive RTP feedback, do not parse it, wait until connection closes. |
405 | 432 | if c.rtpProto == "tcp" { |
406 | | - buf := make([]byte, 10249) |
| 433 | + buf := make([]byte, 1024) |
407 | 434 | for { |
408 | 435 | _, err := c.nconn.Read(buf) |
409 | 436 | if err != nil { |
@@ -456,12 +483,49 @@ func (c *rtspClient) run(wg sync.WaitGroup) { |
456 | 483 | return |
457 | 484 | } |
458 | 485 |
|
459 | | - c.log("is publishing (via udp)") |
| 486 | + c.log("is publishing (via %s)", c.rtpProto) |
460 | 487 |
|
461 | 488 | c.p.mutex.Lock() |
462 | 489 | c.state = "RECORD" |
463 | 490 | c.p.mutex.Unlock() |
464 | 491 |
|
| 492 | + // when rtp protocol is TCP, the RTSP connection becomes a RTP connection. |
| 493 | + // receive RTP feedback, do not parse it, wait until connection closes. |
| 494 | + if c.rtpProto == "tcp" { |
| 495 | + packet := make([]byte, 2048) |
| 496 | + bconn := bufio.NewReader(c.nconn) |
| 497 | + for { |
| 498 | + byts, err := bconn.Peek(4) |
| 499 | + if err != nil { |
| 500 | + return |
| 501 | + } |
| 502 | + bconn.Discard(4) |
| 503 | + |
| 504 | + if byts[0] != 0x24 { |
| 505 | + c.log("ERR: wrong magic byte") |
| 506 | + return |
| 507 | + } |
| 508 | + |
| 509 | + if byts[1] != 0x00 { |
| 510 | + c.log("ERR: wrong channel") |
| 511 | + return |
| 512 | + } |
| 513 | + |
| 514 | + plen := binary.BigEndian.Uint16(byts[2:]) |
| 515 | + if plen > 2048 { |
| 516 | + c.log("ERR: packet len > 2048") |
| 517 | + return |
| 518 | + } |
| 519 | + |
| 520 | + _, err = io.ReadFull(bconn, packet[:plen]) |
| 521 | + if err != nil { |
| 522 | + return |
| 523 | + } |
| 524 | + |
| 525 | + c.p.handleRtp(packet[:plen]) |
| 526 | + } |
| 527 | + } |
| 528 | + |
465 | 529 | case "TEARDOWN": |
466 | 530 | return |
467 | 531 |
|
|
0 commit comments