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