/* * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __AAH_RX_PLAYER_H__ #define __AAH_RX_PLAYER_H__ #include #include #include #include #include #include #include #include #include #include #include #include "aah_decoder_pump.h" #include "pipe_event.h" namespace android { class AAH_RXPlayer : public MediaPlayerInterface { public: AAH_RXPlayer(); virtual status_t initCheck(); virtual status_t setDataSource(const char *url, const KeyedVector* headers); virtual status_t setDataSource(int fd, int64_t offset, int64_t length); virtual status_t setVideoSurface(const sp& surface); virtual status_t setVideoSurfaceTexture(const sp& surfaceTexture); virtual status_t prepare(); virtual status_t prepareAsync(); virtual status_t start(); virtual status_t stop(); virtual status_t pause(); virtual bool isPlaying(); virtual status_t seekTo(int msec); virtual status_t getCurrentPosition(int *msec); virtual status_t getDuration(int *msec); virtual status_t reset(); virtual status_t setLooping(int loop); virtual player_type playerType(); virtual status_t setParameter(int key, const Parcel &request); virtual status_t getParameter(int key, Parcel *reply); virtual status_t invoke(const Parcel& request, Parcel *reply); protected: virtual ~AAH_RXPlayer(); private: class ThreadWrapper : public Thread { public: friend class AAH_RXPlayer; explicit ThreadWrapper(AAH_RXPlayer& player) : Thread(false /* canCallJava */ ) , player_(player) { } virtual bool threadLoop() { return player_.threadLoop(); } private: AAH_RXPlayer& player_; DISALLOW_EVIL_CONSTRUCTORS(ThreadWrapper); }; #pragma pack(push, 1) // PacketBuffers are structures used by the RX ring buffer. The ring buffer // is a ring of pointers to PacketBuffer structures which act as variable // length byte arrays and hold the contents of received UDP packets. Rather // than make this a structure which hold a length and a pointer to another // allocated structure (which would require two allocations), this struct // uses a structure overlay pattern where allocation for the byte array // consists of allocating (arrayLen + sizeof(ssize_t)) bytes of data from // whatever pool/heap the packet buffer pulls from, and then overlaying the // packed PacketBuffer structure on top of the allocation. The one-byte // array at the end of the structure serves as an offset to the the data // portion of the allocation; packet buffers are never allocated on the // stack or using the new operator. Instead, the static allocate-byte-array // and destroy methods handle the allocate and overlay pattern. They also // allow for a potential future optimization where instead of just // allocating blocks from the process global heap and overlaying, the // allocator is replaced with a different implementation (private heap, // free-list, circular buffer, etc) which reduces potential heap // fragmentation issues which might arise from the frequent allocation and // destruction of the received UDP traffic. struct PacketBuffer { ssize_t length_; uint8_t data_[1]; // TODO : consider changing this to be some form of ring buffer or free // pool system instead of just using the heap in order to avoid heap // fragmentation. static PacketBuffer* allocate(ssize_t length); static void destroy(PacketBuffer* pb); private: // Force people to use allocate/destroy instead of new/delete. PacketBuffer() { } ~PacketBuffer() { } }; struct RetransRequest { uint32_t magic_; uint32_t mcast_ip_; uint16_t mcast_port_; uint16_t start_seq_; uint16_t end_seq_; }; #pragma pack(pop) enum GapStatus { kGS_NoGap = 0, kGS_NormalGap, kGS_FastStartGap, }; struct SeqNoGap { uint16_t start_seq_; uint16_t end_seq_; }; class RXRingBuffer { public: explicit RXRingBuffer(uint32_t capacity); ~RXRingBuffer(); bool initCheck() const { return (ring_ != NULL); } void reset(); // Push a packet buffer with a given sequence number into the ring // buffer. pushBuffer will always consume the buffer pushed to it, // either destroying it because it was a duplicate or overflow, or // holding on to it in the ring. Callers should not hold any references // to PacketBuffers after they have been pushed to the ring. Returns // false in the case of a serious error (such as ring overflow). // Callers should consider resetting the pipeline entirely in the event // of a serious error. bool pushBuffer(PacketBuffer* buf, uint16_t seq); // Fetch the next buffer in the RTP sequence. Returns NULL if there is // no buffer to fetch. If a non-NULL PacketBuffer is returned, // is_discon will be set to indicate whether or not this PacketBuffer is // discontiuous with any previously returned packet buffers. Packet // buffers returned by fetchBuffer are the caller's responsibility; they // must be certain to destroy the buffers when they are done. PacketBuffer* fetchBuffer(bool* is_discon); // Returns true and fills out the gap structure if the read pointer of // the ring buffer is currently pointing to a gap which would stall a // fetchBuffer operation. Returns false if the read pointer is not // pointing to a gap in the sequence currently. GapStatus fetchCurrentGap(SeqNoGap* gap); // Causes the read pointer to skip over any portion of a gap indicated // by nak. If nak is NULL, any gap currently blocking the read pointer // will be completely skipped. If any portion of a gap is skipped, the // next successful read from fetch buffer will indicate a discontinuity. void processNAK(const SeqNoGap* nak = NULL); // Compute the number of milliseconds until the inactivity timer for // this RTP stream. Returns -1 if there is no active timeout, or 0 if // the system has already timed out. int computeInactivityTimeout(); private: Mutex lock_; PacketBuffer** ring_; uint32_t capacity_; uint32_t rd_; uint32_t wr_; uint16_t rd_seq_; bool rd_seq_known_; bool waiting_for_fast_start_; bool fetched_first_packet_; uint64_t rtp_activity_timeout_; bool rtp_activity_timeout_valid_; DISALLOW_EVIL_CONSTRUCTORS(RXRingBuffer); }; class Substream : public virtual RefBase { public: Substream(uint32_t ssrc, OMXClient& omx); void cleanupBufferInProgress(); void shutdown(); void processPayloadStart(uint8_t* buf, uint32_t amt, int32_t ts_lower); void processPayloadCont (uint8_t* buf, uint32_t amt); void processTSTransform(const LinearTransform& trans); bool isAboutToUnderflow(); uint32_t getSSRC() const { return ssrc_; } uint16_t getProgramID() const { return (ssrc_ >> 5) & 0x1F; } status_t getStatus() const { return status_; } protected: virtual ~Substream(); private: void cleanupDecoder(); bool shouldAbort(const char* log_tag); void processCompletedBuffer(); bool setupSubstreamMeta(); bool setupMP3SubstreamMeta(); bool setupAACSubstreamMeta(); bool setupSubstreamType(uint8_t substream_type, uint8_t codec_type); uint32_t ssrc_; bool waiting_for_rap_; status_t status_; bool substream_details_known_; uint8_t substream_type_; uint8_t codec_type_; const char* codec_mime_type_; sp substream_meta_; MediaBuffer* buffer_in_progress_; uint32_t expected_buffer_size_; uint32_t buffer_filled_; Vector aux_data_in_progress_; uint32_t aux_data_expected_size_; sp decoder_; static int64_t kAboutToUnderflowThreshold; DISALLOW_EVIL_CONSTRUCTORS(Substream); }; typedef DefaultKeyedVector< uint32_t, sp > SubstreamVec; status_t startWorkThread(); void stopWorkThread(); virtual bool threadLoop(); bool setupSocket(); void cleanupSocket(); void resetPipeline(); void reset_l(); bool processRX(PacketBuffer* pb); void processRingBuffer(); void processCommandPacket(PacketBuffer* pb); bool processGaps(); int computeNextGapRetransmitTimeout(); void fetchAudioFlinger(); PipeEvent wakeup_work_thread_evt_; sp thread_wrapper_; Mutex api_lock_; bool is_playing_; bool data_source_set_; struct sockaddr_in listen_addr_; int sock_fd_; bool multicast_joined_; struct sockaddr_in transmitter_addr_; bool transmitter_known_; uint32_t current_epoch_; bool current_epoch_known_; SeqNoGap current_gap_; GapStatus current_gap_status_; uint64_t next_retrans_req_time_; RXRingBuffer ring_buffer_; SubstreamVec substreams_; OMXClient omx_; CCHelper cc_helper_; // Connection to audio flinger used to hack a path to setMasterVolume. sp audio_flinger_; static const uint32_t kRTPRingBufferSize; static const uint32_t kRetransRequestMagic; static const uint32_t kFastStartRequestMagic; static const uint32_t kRetransNAKMagic; static const uint32_t kGapRerequestTimeoutUSec; static const uint32_t kFastStartTimeoutUSec; static const uint32_t kRTPActivityTimeoutUSec; static const uint32_t INVOKE_GET_MASTER_VOLUME = 3; static const uint32_t INVOKE_SET_MASTER_VOLUME = 4; static uint64_t monotonicUSecNow(); DISALLOW_EVIL_CONSTRUCTORS(AAH_RXPlayer); }; } // namespace android #endif // __AAH_RX_PLAYER_H__