| 2743 | } |
| 2744 | |
| 2745 | bool RtmpChunkStream::OnPlay(const RtmpMessageHeader& mh, |
| 2746 | AMFInputStream* istream, |
| 2747 | Socket* socket) { |
| 2748 | if (!connection_context()->is_server_side()) { |
| 2749 | RTMP_ERROR(socket, mh) << "Client should not receive `play'"; |
| 2750 | return false; |
| 2751 | } |
| 2752 | uint32_t transaction_id = 0; |
| 2753 | if (!ReadAMFUint32(&transaction_id, istream)) { |
| 2754 | RTMP_ERROR(socket, mh) << "Fail to read play.TransactionId"; |
| 2755 | return false; |
| 2756 | } |
| 2757 | // ffmpeg send non-zero transaction_id for play. |
| 2758 | |
| 2759 | if (!ReadAMFNull(istream)) { |
| 2760 | RTMP_ERROR(socket, mh) << "Fail to read play.CommandObject"; |
| 2761 | return false; |
| 2762 | } |
| 2763 | RtmpPlayOptions play_opt; // inner fields are initialized with defaults. |
| 2764 | if (!ReadAMFString(&play_opt.stream_name, istream)) { |
| 2765 | RTMP_ERROR(socket, mh) << "Fail to read play.StreamName"; |
| 2766 | return false; |
| 2767 | } |
| 2768 | // start/duration/reset are optional, check emptiness of the stream before |
| 2769 | // calling ReadAMFXXX which prints log for failed branches. |
| 2770 | if (!istream->check_emptiness()) { |
| 2771 | if (!ReadAMFNumber(&play_opt.start, istream)) { |
| 2772 | RTMP_ERROR(socket, mh) << "Fail to read play.Start"; |
| 2773 | return false; |
| 2774 | } |
| 2775 | } |
| 2776 | if (!istream->check_emptiness()) { |
| 2777 | if (!ReadAMFNumber(&play_opt.duration, istream)) { |
| 2778 | RTMP_ERROR(socket, mh) << "Fail to read play.Duration"; |
| 2779 | return false; |
| 2780 | } |
| 2781 | } |
| 2782 | if (!istream->check_emptiness()) { |
| 2783 | if (!ReadAMFBool(&play_opt.reset, istream)) { |
| 2784 | RTMP_ERROR(socket, mh) << "Fail to read play.Reset"; |
| 2785 | return false; |
| 2786 | } |
| 2787 | } |
| 2788 | RPC_VLOG << socket->remote_side() << "[" << mh.stream_id |
| 2789 | << "] play{transaction_id=" << transaction_id |
| 2790 | << " stream_name=" << play_opt.stream_name |
| 2791 | << " start=" << play_opt.start |
| 2792 | << " duration=" << play_opt.duration |
| 2793 | << " reset=" << play_opt.reset << '}'; |
| 2794 | |
| 2795 | butil::IOBuf req_buf; |
| 2796 | TemporaryArrayBuilder<SocketMessagePtr<RtmpUnsentMessage>, 5> msgs; |
| 2797 | |
| 2798 | // TODO(gejun): RTMP spec sends StreamIsRecorded before StreamBegin |
| 2799 | // however SRS does not. |
| 2800 | // StreamBegin |
| 2801 | { |
| 2802 | char cntl_buf[6]; |
nothing calls this directly
no test coverage detected