Commit | Line | Data |
---|---|---|
2ba45a60 DM |
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) | |
f6fa7814 | 87 | return ff_rtsp_averror(reply->status_code, AVERROR_INVALIDDATA); |
2ba45a60 DM |
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) | |
f6fa7814 | 119 | return ff_rtsp_averror(reply->status_code, -1); |
2ba45a60 DM |
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 | }; |