Skip to content

Commit 37d97d3

Browse files
committed
Merge remote-tracking branch 'origin/master'
2 parents e7f1725 + b4202bc commit 37d97d3

File tree

1 file changed

+68
-1
lines changed

1 file changed

+68
-1
lines changed

README.md

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,68 @@
1-
RTPPlayer-iOS
1+
# FFmpeg-based RTP Video Stream Player for iOS
2+
3+
This is a simple (S)RTP video stream player for iOS with decoding pipeline optionally based on a custom version of FFmpeg ([https://github.com/O2-Czech-Republic/FFmpeg](https://github.com/O2-Czech-Republic/FFmpeg)) that adds very basic error resilience to HEVC decoding.
4+
5+
6+
### Background
7+
As part of the O2 Smart Box project ([https://www.o2.cz/osobni/en/541537-o2_smart_box](https://www.o2.cz/osobni/en/541537-o2_smart_box)) we developed our own framework for RTP stream playback on iOS.
8+
9+
RTP playback implementation on iOS unfortunately involves some complexity as native out-of-the-box playback functionality is mostly limited to HLS for streaming and MP4-containerized H.26x for file playback.
10+
11+
Playing RTP stream on iOS essentially requires the app to parse the payload of each RTP packet, use its own logic to reassemble NAL units from Fragmentation Units, and provide these NAL units along with a `CMVideoFormatDescription` as `CMSampleBuffer`s to VideoToolbox (or AVFoundation).
12+
13+
14+
### Architecture
15+
#### RTP Streaming
16+
The basic component of the (S)RTP session is the `RTPProcessor` (or `SRTPProcessor`) which is responsible for:
17+
- transforming raw data packets (buffers) into `RTPPacket` structs
18+
- and delivering these `RTPPacket`s to its delegate in correct order (i.e. this is where reordering happens)
19+
20+
The `RTPProcessor` accepts in its constructor a `RTPTransport` object, a class responsible for handling the underlying transport channel and its lifecycle.
21+
22+
#### RTP Parsing
23+
The `RTPProcessor` dispatches each `RTPPacket` to its delegate (in this case it's the ViewController) which should pass it to a `VideoDefragmenter` responsible for re-assembling large NAL units that did not fit into a single RTP packet and were split by the producer (e.g. IP camera) into multiple Fragmentation Units.
24+
25+
The `VideoDefragmenter` provides its delegate (usually the decoder, although additional processing might be involved) with complete NAL units ready to be decoded.
26+
27+
#### Video Stream Decoding
28+
Currently, two decoders are provided: `VTDecoder`, which is just a wrapper around the native iOS VideoToolbox, and `FFMpegDecoder` based on custom FFmpeg version ([https://github.com/O2-Czech-Republic/FFmpeg](https://github.com/O2-Czech-Republic/FFmpeg)) that adds very basic error resilience to HEVC decoding (natively not present in FFmpeg's HEVC codec).
29+
30+
Each decoder delivers fully decoded and ready frames (as `CMSampleBuffer`s) that are ready to be enqueued to the player (`AVSampleBufferDisplayLayer`).
31+
32+
33+
### Stream transport
34+
This code currently only supports two methods of delivery (transports) but adding another one is only a matter of impementing a new `RTPTransport`.
35+
#### UDP unicast
36+
In which the client sends a "hello" packet to the server and the server responds back with a stream of RTP packets.
37+
38+
This enables the traversal of NATs along the way but the client must be able to accept incoming packets on the same UDP port it sent the hello packet from.
39+
40+
Due to the connectionless nature of UDP, the server also expects to receive a heartbeat packet every once in a while to know that the client is still there.
41+
42+
#### HTTP progressive download
43+
The app performs an HTTP `GET` request to a remote server that responds with `Transfer-Encoding: chunked` in response header (and `Content-Length` unset) and streams the RTP packets in the response body.
44+
45+
```
46+
GET /streams/1234567 HTTP/1.1
47+
Connection: close
48+
```
49+
```
50+
HTTP/1.1 200 OK
51+
Transfer-Encoding: chunked
52+
Content-Type: video/rtp
53+
54+
AGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIDEzIGxhenkgZG9ncy...
55+
```
56+
57+
Because `Content-Length` is not set, HTTP clients will treat this as an unbounded response of undefined (and potentially infinite) size and return data chunks as long as some data is received within a specified timeout.
58+
59+
Our particular protocol always sends a 4-byte packet size first, followed immediately by the packet itself, followed by the size of the next packet and so on.
60+
61+
| Packet 1 length (4 bytes) | Packet 1 (.....) | Packet 2 length (4 bytes) | Packet 2 (.....) |
62+
| --- | --- | --- | --- |
63+
64+
For more info see [https://en.wikipedia.org/wiki/Progressive_download](https://en.wikipedia.org/wiki/Progressive_download)
65+
66+
### Known limitations
67+
- The decryption of packet payload in SRTP is not implemented. We're using a custom encryption scheme wich is not fully SRTP-compatible.
68+
- This is still a proof-of-concept, so expect that some corner cases might produce unexpected behavior or crashes (think handling sequence resets, byte array slicing etc.).

0 commit comments

Comments
 (0)