blob: e7707bb5fb4ace798fb08ce5419c6c6481273e81
1 | /* |
2 | * RTSP muxer |
3 | * Copyright (c) 2010 Martin Storsjo |
4 | * |
5 | * This file is part of FFmpeg. |
6 | * |
7 | * FFmpeg is free software; you can redistribute it and/or |
8 | * modify it under the terms of the GNU Lesser General Public |
9 | * License as published by the Free Software Foundation; either |
10 | * version 2.1 of the License, or (at your option) any later version. |
11 | * |
12 | * FFmpeg is distributed in the hope that it will be useful, |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 | * Lesser General Public License for more details. |
16 | * |
17 | * You should have received a copy of the GNU Lesser General Public |
18 | * License along with FFmpeg; if not, write to the Free Software |
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
20 | */ |
21 | |
22 | #include "avformat.h" |
23 | |
24 | #if HAVE_POLL_H |
25 | #include <poll.h> |
26 | #endif |
27 | #include "network.h" |
28 | #include "os_support.h" |
29 | #include "rtsp.h" |
30 | #include "internal.h" |
31 | #include "avio_internal.h" |
32 | #include "libavutil/intreadwrite.h" |
33 | #include "libavutil/avstring.h" |
34 | #include "libavutil/time.h" |
35 | #include "url.h" |
36 | |
37 | #define SDP_MAX_SIZE 16384 |
38 | |
39 | static const AVClass rtsp_muxer_class = { |
40 | .class_name = "RTSP muxer", |
41 | .item_name = av_default_item_name, |
42 | .option = ff_rtsp_options, |
43 | .version = LIBAVUTIL_VERSION_INT, |
44 | }; |
45 | |
46 | int ff_rtsp_setup_output_streams(AVFormatContext *s, const char *addr) |
47 | { |
48 | RTSPState *rt = s->priv_data; |
49 | RTSPMessageHeader reply1, *reply = &reply1; |
50 | int i; |
51 | char *sdp; |
52 | AVFormatContext sdp_ctx, *ctx_array[1]; |
53 | |
54 | if (s->start_time_realtime == 0 || s->start_time_realtime == AV_NOPTS_VALUE) |
55 | s->start_time_realtime = av_gettime(); |
56 | |
57 | /* Announce the stream */ |
58 | sdp = av_mallocz(SDP_MAX_SIZE); |
59 | if (!sdp) |
60 | return AVERROR(ENOMEM); |
61 | /* We create the SDP based on the RTSP AVFormatContext where we |
62 | * aren't allowed to change the filename field. (We create the SDP |
63 | * based on the RTSP context since the contexts for the RTP streams |
64 | * don't exist yet.) In order to specify a custom URL with the actual |
65 | * peer IP instead of the originally specified hostname, we create |
66 | * a temporary copy of the AVFormatContext, where the custom URL is set. |
67 | * |
68 | * FIXME: Create the SDP without copying the AVFormatContext. |
69 | * This either requires setting up the RTP stream AVFormatContexts |
70 | * already here (complicating things immensely) or getting a more |
71 | * flexible SDP creation interface. |
72 | */ |
73 | sdp_ctx = *s; |
74 | ff_url_join(sdp_ctx.filename, sizeof(sdp_ctx.filename), |
75 | "rtsp", NULL, addr, -1, NULL); |
76 | ctx_array[0] = &sdp_ctx; |
77 | if (av_sdp_create(ctx_array, 1, sdp, SDP_MAX_SIZE)) { |
78 | av_free(sdp); |
79 | return AVERROR_INVALIDDATA; |
80 | } |
81 | av_log(s, AV_LOG_VERBOSE, "SDP:\n%s\n", sdp); |
82 | ff_rtsp_send_cmd_with_content(s, "ANNOUNCE", rt->control_uri, |
83 | "Content-Type: application/sdp\r\n", |
84 | reply, NULL, sdp, strlen(sdp)); |
85 | av_free(sdp); |
86 | if (reply->status_code != RTSP_STATUS_OK) |
87 | return ff_rtsp_averror(reply->status_code, AVERROR_INVALIDDATA); |
88 | |
89 | /* Set up the RTSPStreams for each AVStream */ |
90 | for (i = 0; i < s->nb_streams; i++) { |
91 | RTSPStream *rtsp_st; |
92 | |
93 | rtsp_st = av_mallocz(sizeof(RTSPStream)); |
94 | if (!rtsp_st) |
95 | return AVERROR(ENOMEM); |
96 | dynarray_add(&rt->rtsp_streams, &rt->nb_rtsp_streams, rtsp_st); |
97 | |
98 | rtsp_st->stream_index = i; |
99 | |
100 | av_strlcpy(rtsp_st->control_url, rt->control_uri, sizeof(rtsp_st->control_url)); |
101 | /* Note, this must match the relative uri set in the sdp content */ |
102 | av_strlcatf(rtsp_st->control_url, sizeof(rtsp_st->control_url), |
103 | "/streamid=%d", i); |
104 | } |
105 | |
106 | return 0; |
107 | } |
108 | |
109 | static int rtsp_write_record(AVFormatContext *s) |
110 | { |
111 | RTSPState *rt = s->priv_data; |
112 | RTSPMessageHeader reply1, *reply = &reply1; |
113 | char cmd[1024]; |
114 | |
115 | snprintf(cmd, sizeof(cmd), |
116 | "Range: npt=0.000-\r\n"); |
117 | ff_rtsp_send_cmd(s, "RECORD", rt->control_uri, cmd, reply, NULL); |
118 | if (reply->status_code != RTSP_STATUS_OK) |
119 | return ff_rtsp_averror(reply->status_code, -1); |
120 | rt->state = RTSP_STATE_STREAMING; |
121 | return 0; |
122 | } |
123 | |
124 | static int rtsp_write_header(AVFormatContext *s) |
125 | { |
126 | int ret; |
127 | |
128 | ret = ff_rtsp_connect(s); |
129 | if (ret) |
130 | return ret; |
131 | |
132 | if (rtsp_write_record(s) < 0) { |
133 | ff_rtsp_close_streams(s); |
134 | ff_rtsp_close_connections(s); |
135 | return AVERROR_INVALIDDATA; |
136 | } |
137 | return 0; |
138 | } |
139 | |
140 | int ff_rtsp_tcp_write_packet(AVFormatContext *s, RTSPStream *rtsp_st) |
141 | { |
142 | RTSPState *rt = s->priv_data; |
143 | AVFormatContext *rtpctx = rtsp_st->transport_priv; |
144 | uint8_t *buf, *ptr; |
145 | int size; |
146 | uint8_t *interleave_header, *interleaved_packet; |
147 | |
148 | size = avio_close_dyn_buf(rtpctx->pb, &buf); |
149 | rtpctx->pb = NULL; |
150 | ptr = buf; |
151 | while (size > 4) { |
152 | uint32_t packet_len = AV_RB32(ptr); |
153 | int id; |
154 | /* The interleaving header is exactly 4 bytes, which happens to be |
155 | * the same size as the packet length header from |
156 | * ffio_open_dyn_packet_buf. So by writing the interleaving header |
157 | * over these bytes, we get a consecutive interleaved packet |
158 | * that can be written in one call. */ |
159 | interleaved_packet = interleave_header = ptr; |
160 | ptr += 4; |
161 | size -= 4; |
162 | if (packet_len > size || packet_len < 2) |
163 | break; |
164 | if (RTP_PT_IS_RTCP(ptr[1])) |
165 | id = rtsp_st->interleaved_max; /* RTCP */ |
166 | else |
167 | id = rtsp_st->interleaved_min; /* RTP */ |
168 | interleave_header[0] = '$'; |
169 | interleave_header[1] = id; |
170 | AV_WB16(interleave_header + 2, packet_len); |
171 | ffurl_write(rt->rtsp_hd_out, interleaved_packet, 4 + packet_len); |
172 | ptr += packet_len; |
173 | size -= packet_len; |
174 | } |
175 | av_free(buf); |
176 | return ffio_open_dyn_packet_buf(&rtpctx->pb, RTSP_TCP_MAX_PACKET_SIZE); |
177 | } |
178 | |
179 | static int rtsp_write_packet(AVFormatContext *s, AVPacket *pkt) |
180 | { |
181 | RTSPState *rt = s->priv_data; |
182 | RTSPStream *rtsp_st; |
183 | int n; |
184 | struct pollfd p = {ffurl_get_file_handle(rt->rtsp_hd), POLLIN, 0}; |
185 | AVFormatContext *rtpctx; |
186 | int ret; |
187 | |
188 | while (1) { |
189 | n = poll(&p, 1, 0); |
190 | if (n <= 0) |
191 | break; |
192 | if (p.revents & POLLIN) { |
193 | RTSPMessageHeader reply; |
194 | |
195 | /* Don't let ff_rtsp_read_reply handle interleaved packets, |
196 | * since it would block and wait for an RTSP reply on the socket |
197 | * (which may not be coming any time soon) if it handles |
198 | * interleaved packets internally. */ |
199 | ret = ff_rtsp_read_reply(s, &reply, NULL, 1, NULL); |
200 | if (ret < 0) |
201 | return AVERROR(EPIPE); |
202 | if (ret == 1) |
203 | ff_rtsp_skip_packet(s); |
204 | /* XXX: parse message */ |
205 | if (rt->state != RTSP_STATE_STREAMING) |
206 | return AVERROR(EPIPE); |
207 | } |
208 | } |
209 | |
210 | if (pkt->stream_index < 0 || pkt->stream_index >= rt->nb_rtsp_streams) |
211 | return AVERROR_INVALIDDATA; |
212 | rtsp_st = rt->rtsp_streams[pkt->stream_index]; |
213 | rtpctx = rtsp_st->transport_priv; |
214 | |
215 | ret = ff_write_chained(rtpctx, 0, pkt, s, 0); |
216 | /* ff_write_chained does all the RTP packetization. If using TCP as |
217 | * transport, rtpctx->pb is only a dyn_packet_buf that queues up the |
218 | * packets, so we need to send them out on the TCP connection separately. |
219 | */ |
220 | if (!ret && rt->lower_transport == RTSP_LOWER_TRANSPORT_TCP) |
221 | ret = ff_rtsp_tcp_write_packet(s, rtsp_st); |
222 | return ret; |
223 | } |
224 | |
225 | static int rtsp_write_close(AVFormatContext *s) |
226 | { |
227 | RTSPState *rt = s->priv_data; |
228 | |
229 | // If we want to send RTCP_BYE packets, these are sent by av_write_trailer. |
230 | // Thus call this on all streams before doing the teardown. This is |
231 | // done within ff_rtsp_undo_setup. |
232 | ff_rtsp_undo_setup(s, 1); |
233 | |
234 | ff_rtsp_send_cmd_async(s, "TEARDOWN", rt->control_uri, NULL); |
235 | |
236 | ff_rtsp_close_streams(s); |
237 | ff_rtsp_close_connections(s); |
238 | ff_network_close(); |
239 | return 0; |
240 | } |
241 | |
242 | AVOutputFormat ff_rtsp_muxer = { |
243 | .name = "rtsp", |
244 | .long_name = NULL_IF_CONFIG_SMALL("RTSP output"), |
245 | .priv_data_size = sizeof(RTSPState), |
246 | .audio_codec = AV_CODEC_ID_AAC, |
247 | .video_codec = AV_CODEC_ID_MPEG4, |
248 | .write_header = rtsp_write_header, |
249 | .write_packet = rtsp_write_packet, |
250 | .write_trailer = rtsp_write_close, |
251 | .flags = AVFMT_NOFILE | AVFMT_GLOBALHEADER, |
252 | .priv_class = &rtsp_muxer_class, |
253 | }; |
254 |