* commit '0d6fa3977b016f1b72b0b24b8834ff9222498548':
rtmp: Add seek support
Conflicts:
Changelog
libavformat/version.h
Merged-by: Michael Niedermayer <michaelni@gmx.at>
... | ... |
@@ -60,6 +60,7 @@ typedef enum { |
60 | 60 |
STATE_HANDSHAKED, ///< client has performed handshake |
61 | 61 |
STATE_FCPUBLISH, ///< client FCPublishing stream (for output) |
62 | 62 |
STATE_PLAYING, ///< client has started receiving multimedia data from server |
63 |
+ STATE_SEEKING, ///< client has started the seek operation. Back on STATE_PLAYING when the time comes |
|
63 | 64 |
STATE_PUBLISHING, ///< client has started sending multimedia data to server (for output) |
64 | 65 |
STATE_RECEIVING, ///< received a publish command (for input) |
65 | 66 |
STATE_STOPPED, ///< the broadcast has been stopped |
... | ... |
@@ -704,6 +705,28 @@ static int gen_play(URLContext *s, RTMPContext *rt) |
704 | 704 |
return rtmp_send_packet(rt, &pkt, 1); |
705 | 705 |
} |
706 | 706 |
|
707 |
+static int gen_seek(URLContext *s, RTMPContext *rt, int64_t timestamp) |
|
708 |
+{ |
|
709 |
+ RTMPPacket pkt; |
|
710 |
+ uint8_t *p; |
|
711 |
+ int ret; |
|
712 |
+ |
|
713 |
+ av_log(s, AV_LOG_DEBUG, "Sending seek command for timestamp %lld\n", timestamp); |
|
714 |
+ |
|
715 |
+ if ((ret = ff_rtmp_packet_create(&pkt, 3, RTMP_PT_INVOKE, 0, 26)) < 0) |
|
716 |
+ return ret; |
|
717 |
+ |
|
718 |
+ pkt.extra = rt->main_channel_id; |
|
719 |
+ |
|
720 |
+ p = pkt.data; |
|
721 |
+ ff_amf_write_string(&p, "seek"); |
|
722 |
+ ff_amf_write_number(&p, 0); //no tracking back responses |
|
723 |
+ ff_amf_write_null(&p); //as usual, the first null param |
|
724 |
+ ff_amf_write_number(&p, timestamp); //where we want to jump |
|
725 |
+ |
|
726 |
+ return rtmp_send_packet(rt, &pkt, 1); |
|
727 |
+} |
|
728 |
+ |
|
707 | 729 |
/** |
708 | 730 |
* Generate 'publish' call and send it to the server. |
709 | 731 |
*/ |
... | ... |
@@ -1960,6 +1983,7 @@ static int handle_invoke_status(URLContext *s, RTMPPacket *pkt) |
1960 | 1960 |
if (!t && !strcmp(tmpstr, "NetStream.Play.Stop")) rt->state = STATE_STOPPED; |
1961 | 1961 |
if (!t && !strcmp(tmpstr, "NetStream.Play.UnpublishNotify")) rt->state = STATE_STOPPED; |
1962 | 1962 |
if (!t && !strcmp(tmpstr, "NetStream.Publish.Start")) rt->state = STATE_PUBLISHING; |
1963 |
+ if (!t && !strcmp(tmpstr, "NetStream.Seek.Notify")) rt->state = STATE_PLAYING; |
|
1963 | 1964 |
|
1964 | 1965 |
return 0; |
1965 | 1966 |
} |
... | ... |
@@ -2148,6 +2172,17 @@ static int get_packet(URLContext *s, int for_header) |
2148 | 2148 |
} |
2149 | 2149 |
|
2150 | 2150 |
ret = rtmp_parse_result(s, rt, &rpkt); |
2151 |
+ |
|
2152 |
+ // At this point we must check if we are in the seek state and continue |
|
2153 |
+ // with the next packet. handle_invoke will get us out of this state |
|
2154 |
+ // when the right message is encountered |
|
2155 |
+ if (rt->state == STATE_SEEKING) { |
|
2156 |
+ ff_rtmp_packet_destroy(&rpkt); |
|
2157 |
+ // We continue, let the natural flow of things happen: |
|
2158 |
+ // AVERROR(EAGAIN) or handle_invoke gets us out of here |
|
2159 |
+ continue; |
|
2160 |
+ } |
|
2161 |
+ |
|
2151 | 2162 |
if (ret < 0) {//serious error in current packet |
2152 | 2163 |
ff_rtmp_packet_destroy(&rpkt); |
2153 | 2164 |
return ret; |
... | ... |
@@ -2512,6 +2547,25 @@ static int rtmp_read(URLContext *s, uint8_t *buf, int size) |
2512 | 2512 |
return orig_size; |
2513 | 2513 |
} |
2514 | 2514 |
|
2515 |
+static int64_t rtmp_seek(URLContext *s, int stream_index, int64_t timestamp, |
|
2516 |
+ int flags) |
|
2517 |
+{ |
|
2518 |
+ RTMPContext *rt = s->priv_data; |
|
2519 |
+ int ret; |
|
2520 |
+ av_log(s, AV_LOG_DEBUG, |
|
2521 |
+ "Seek on stream index %d at timestamp %lld with flags %08x\n", |
|
2522 |
+ stream_index, timestamp, flags); |
|
2523 |
+ if ((ret = gen_seek(s, rt, timestamp)) < 0) { |
|
2524 |
+ av_log(s, AV_LOG_ERROR, |
|
2525 |
+ "Unable to send seek command on stream index %d at timestamp %lld with flags %08x\n", |
|
2526 |
+ stream_index, timestamp, flags); |
|
2527 |
+ return ret; |
|
2528 |
+ } |
|
2529 |
+ rt->flv_off = rt->flv_size; |
|
2530 |
+ rt->state = STATE_SEEKING; |
|
2531 |
+ return timestamp; |
|
2532 |
+} |
|
2533 |
+ |
|
2515 | 2534 |
static int rtmp_write(URLContext *s, const uint8_t *buf, int size) |
2516 | 2535 |
{ |
2517 | 2536 |
RTMPContext *rt = s->priv_data; |
... | ... |
@@ -2663,6 +2717,7 @@ URLProtocol ff_##flavor##_protocol = { \ |
2663 | 2663 |
.name = #flavor, \ |
2664 | 2664 |
.url_open = rtmp_open, \ |
2665 | 2665 |
.url_read = rtmp_read, \ |
2666 |
+ .url_read_seek = rtmp_seek, \ |
|
2666 | 2667 |
.url_write = rtmp_write, \ |
2667 | 2668 |
.url_close = rtmp_close, \ |
2668 | 2669 |
.priv_data_size = sizeof(RTMPContext), \ |
... | ... |
@@ -30,8 +30,8 @@ |
30 | 30 |
#include "libavutil/avutil.h" |
31 | 31 |
|
32 | 32 |
#define LIBAVFORMAT_VERSION_MAJOR 55 |
33 |
-#define LIBAVFORMAT_VERSION_MINOR 12 |
|
34 |
-#define LIBAVFORMAT_VERSION_MICRO 102 |
|
33 |
+#define LIBAVFORMAT_VERSION_MINOR 13 |
|
34 |
+#define LIBAVFORMAT_VERSION_MICRO 100 |
|
35 | 35 |
|
36 | 36 |
#define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \ |
37 | 37 |
LIBAVFORMAT_VERSION_MINOR, \ |