void syncPts() throw(EndOfStreamException)
 {
     if (!hasPacket())
         throw EndOfStreamException();
     sampleBufferSize = 0;
     if (packet.pts != AV_NOPTS_VALUE)
         sampleBufferStart = uint64_t(floor(ptsToTime(packet.pts) * pCodecCtx->sample_rate));
     
     if (sampleBufferStart > streamSize)
         streamSize = sampleBufferStart;
 }
            void decodeFrame() throw(PacketDecodeException)
            {
                if (!hasPacket())
                    throw PacketDecodeException();
                
                int dataSize = sampleBufferAlloc;
                int used = 
                #if (LIBAVCODEC_VERSION_MAJOR >= 53)
                    avcodec_decode_audio3(
                        pCodecCtx,
                        (int16_t*)sampleBufferAligned, &dataSize,
                        &packet);
                #else
                    avcodec_decode_audio2(
                        pCodecCtx,
                        (int16_t*)sampleBufferAligned, &dataSize,
                        packetBuffer, packetBufferSize);
                #endif
                
                if (used < 0)
                    throw PacketDecodeException();
                
                #if (LIBAVCODEC_VERSION_MAJOR >= 53)
                
                if ((size_t)used > packet.size)
                    used = packet.size;

                (char*&)(packet.data) += used;
                packet.size -= used;
                
                #else
                
                if ((size_t)used > packetBufferSize)
                    used = packetBufferSize;

                (char*&)packetBuffer += used;
                packetBufferSize -= used;
                
                #endif
                
                if (dataSize < 0)
                    dataSize = 0;

                sampleBuffer = sampleBufferAligned;
                sampleBufferStart += sampleBufferSize;
                sampleBufferSize = dataSize / sampleSize;
                
                if (sampleBufferStart + sampleBufferSize > streamSize)
                    streamSize = sampleBufferStart + sampleBufferSize;
            }
Пример #3
0
bool
RTMP::sendPacket(RTMPPacket& packet)
{
    // Set the data size of the packet to send.
    RTMPHeader& hr = packet.header;

    hr.dataSize = payloadSize(packet);

    // This is the timestamp for our message.
    const std::uint32_t uptime = getUptime();
    
    // Look at the previous packet on the channel.
    bool prev = hasPacket(CHANNELS_OUT, hr.channel);

    // The packet shall be large if it contains an absolute timestamp.
    //      * This is necessary if there is no previous packet, or if the
    //        timestamp is smaller than the last packet.
    // Else it shall be medium if data size and packet type are the same
    // It shall be small if ...
    // It shall be minimal if it is exactly the same as its predecessor.

    // All packets should start off as large. They will stay large if there
    // is no previous packet.
    assert(hr.headerType == RTMP_PACKET_SIZE_LARGE);

    if (!prev) {
        hr._timestamp = uptime;
    }
    else {

        const RTMPPacket& prevPacket = getPacket(CHANNELS_OUT, hr.channel);
        const RTMPHeader& oldh = prevPacket.header;
        const std::uint32_t prevTimestamp = oldh._timestamp;

        // If this timestamp is later than the other and the difference fits
        // in 3 bytes, encode a relative one.
        if (uptime >= oldh._timestamp && uptime - prevTimestamp < 0xffffff) {
            //log_debug("Shrinking to medium");
            hr.headerType = RTMP_PACKET_SIZE_MEDIUM;
            hr._timestamp = uptime - prevTimestamp;

            // It can be still smaller if the data size is the same.
            if (oldh.dataSize == hr.dataSize &&
                    oldh.packetType == hr.packetType) {
                //log_debug("Shrinking to small");
                hr.headerType = RTMP_PACKET_SIZE_SMALL;
                // If there is no timestamp difference, the minimum size
                // is possible.
                if (hr._timestamp == 0) {
                    //log_debug("Shrinking to minimum");
                    hr.headerType = RTMP_PACKET_SIZE_MINIMUM;
                }
            }
        }
        else {
            // Otherwise we need an absolute one, so a large header.
            hr.headerType = RTMP_PACKET_SIZE_LARGE;
            hr._timestamp = uptime;
        }
    }

    assert (hr.headerType < 4);
  
    int nSize = packetSize[hr.headerType];
  
    int hSize = nSize;
    std::uint8_t* header;
    std::uint8_t* hptr;
    std::uint8_t* hend;
    std::uint8_t c;

    // If there is a payload, the same buffer is used to write the header.
    // Otherwise a separate buffer is used. But as we write them separately
    // anyway, why do we do that?

    // Work out where the beginning of the header is.
    header = payloadData(packet) - nSize;
    hend = payloadData(packet);
  
    // The header size includes only a single channel/type. If we need more,
    // they have to be added on.
    const int channelSize = hr.channel > 319 ? 3 : hr.channel > 63 ? 1 : 0;
    header -= channelSize;
    hSize += channelSize;

    /// Add space for absolute timestamp if necessary.
    if (hr.headerType == RTMP_PACKET_SIZE_LARGE && hr._timestamp >= 0xffffff) {
        header -= 4;
        hSize += 4;
    }

    hptr = header;
    c = hr.headerType << 6;
    switch (channelSize) {
        case 0:
            c |= hr.channel;
            break;
        case 1:
            break;
        case 2:
            c |= 1;
            break;
    }
    *hptr++ = c;

    if (channelSize) {
        const int tmp = hr.channel - 64;
        *hptr++ = tmp & 0xff;
        if (channelSize == 2) *hptr++ = tmp >> 8;
    }

    if (hr.headerType == RTMP_PACKET_SIZE_LARGE && hr._timestamp >= 0xffffff) {
        // Signify that the extended timestamp field is present.
        const std::uint32_t t = 0xffffff;
        hptr = encodeInt24(hptr, hend, t);
    }
    else if (hr.headerType != RTMP_PACKET_SIZE_MINIMUM) { 
        // Write absolute or relative timestamp. Only minimal packets have
        // no timestamp.
        hptr = encodeInt24(hptr, hend, hr._timestamp);
    }

    /// Encode dataSize and packet type for medium packets.
    if (nSize > 4) {
        hptr = encodeInt24(hptr, hend, hr.dataSize);
        *hptr++ = hr.packetType;
    }

    /// Encode streamID for large packets.
    if (hr.headerType == RTMP_PACKET_SIZE_LARGE) {
        hptr += encodeInt32LE(hptr, hr._streamID);
    }

    // Encode extended absolute timestamp if needed.
    if (hr.headerType == RTMP_PACKET_SIZE_LARGE && hr._timestamp >= 0xffffff) {
        hptr += encodeInt32LE(hptr, hr._timestamp);
    }

    nSize = hr.dataSize;
    std::uint8_t *buffer = payloadData(packet);
    int nChunkSize = _outChunkSize;

    std::string hx = hexify(header, payloadEnd(packet) - header, false);

    while (nSize + hSize) {

        if (nSize < nChunkSize) nChunkSize = nSize;

        // First write header.
        if (header) {
            const int chunk = nChunkSize + hSize;
            if (_socket.write(header, chunk) != chunk) {
                return false;
            }
            header = nullptr;
            hSize = 0;
        }
      
        else {
            // Then write data.
            if (_socket.write(buffer, nChunkSize) != nChunkSize) {
                return false;
          }
        
        }
  
        nSize -= nChunkSize;
        buffer += nChunkSize;
 
        if (nSize > 0) {
            header = buffer - 1;
            hSize = 1;
            if (channelSize) {
                header -= channelSize;
                hSize += channelSize;
            }

            *header = (0xc0 | c);
            if (channelSize) {
                int tmp = hr.channel - 64;
                header[1] = tmp & 0xff;
                if (channelSize == 2) header[2] = tmp >> 8;
            }
        }
    }
Пример #4
0
/// Fills a pre-existent RTMPPacket with information.
//
/// This is either read entirely from incoming data, or copied from a
/// previous packet in the same channel. This happens when the header type
/// is less than RTMP_PACKET_SIZE_LARGE.
//
/// It seems as if new packets can add to the data of old ones if they have
/// a minimal, small header.
bool
RTMP::readPacketHeader(RTMPPacket& packet)
{
      
    RTMPHeader& hr = packet.header;

    std::uint8_t hbuf[RTMPHeader::headerSize] = { 0 };
    std::uint8_t* header = hbuf;
  
    // The first read may fail, but otherwise we expect a complete header.
    if (readSocket(hbuf, 1) == 0) {
        return false;
    }

    //log_debug("Packet is %s", boost::io::group(std::hex, (unsigned)hbuf[0]));

    const int htype = ((hbuf[0] & 0xc0) >> 6);
    //log_debug("Thingy whatsit (packet size type): %s", htype);

    const int channel = (hbuf[0] & 0x3f);
    //log_debug("Channel: %s", channel);

    hr.headerType = static_cast<PacketSize>(htype);
    hr.channel = channel;
    ++header;

    if (hr.channel == 0) {
        if (readSocket(&hbuf[1], 1) != 1) {
          log_error(_("failed to read RTMP packet header 2nd byte"));
          return false;
        }
        hr.channel = hbuf[1] + 64;
        ++header;
    }
    else if (hr.channel == 1) {
        if (readSocket(&hbuf[1], 2) != 2) {
            log_error(_("Failed to read RTMP packet header 3nd byte"));
             return false;
        }
      
        const std::uint32_t tmp = (hbuf[2] << 8) + hbuf[1];
        hr.channel = tmp + 64;
        log_debug("%s, channel: %0x", __FUNCTION__, hr.channel);
        header += 2;
    }
  
    // This is the size in bytes of the packet header according to the
    // type.
    int nSize = packetSize[htype];

    /// If we didn't receive a large header, the timestamp is relative
    if (htype != RTMP_PACKET_SIZE_LARGE) {

        if (!hasPacket(CHANNELS_IN, hr.channel)) {
            log_error(_("Incomplete packet received on channel %s"), channel);
            return false;
        }

        // For all other header types, copy values from the last message of
        // this channel. This includes any payload data from incomplete
        // messages. 
        packet = getPacket(CHANNELS_IN, hr.channel);
    }
  
    --nSize;
  
    if (nSize > 0 && readSocket(header, nSize) != nSize) {
        log_error(_("Failed to read RTMP packet header. type: %s"),
                static_cast<unsigned>(hbuf[0]));
        return false;
    }

    if (nSize >= 3) {

        const std::uint32_t timestamp = decodeInt24(header);

        // Make our packet timestamp absolute. If the value is 0xffffff,
        // the absolute value comes later.
        if (timestamp != 0xffffff) {
            if (htype != RTMP_PACKET_SIZE_LARGE) {
                packet.header._timestamp += timestamp;
            }
            else {
                packet.header._timestamp = timestamp;
            }
        }

        // Have at least a different size payload from the last packet.
        if (nSize >= 6) {

            // We do this in case there was an incomplete packet in the
            // channel already.
            clearPayload(packet);
            hr.dataSize = decodeInt24(header + 3);

            // More than six: read packet type
            if (nSize > 6) {
                hr.packetType = static_cast<PacketType>(header[6]);
     
                // Large packets have a streamID.
                if (nSize == 11) {
                    hr._streamID = decodeInt32LE(header + 7);
                }
            }
        }
    }

    if (hr._timestamp == 0xffffff) {
      if (readSocket(header+nSize, 4) != 4) {
          log_error(_("%s, failed to read extended timestamp"),
              __FUNCTION__);
              return false;
            }
          hr._timestamp = amf::readNetworkLong(header+nSize);
    }
        
    const size_t bufSize = hr.dataSize + RTMPHeader::headerSize;

    // If the packet does not have a payload, it was a complete packet stored in
    // the channel for reference. This is the only case when a packet should
    // exist but have no payload. We re-allocate in this case.
    if (!hasPayload(packet)) {
        packet.buffer.reset(new SimpleBuffer(bufSize));

        // Why do this again? In case it was copied from the old packet?
        hr.headerType = static_cast<PacketSize>(htype);
    }
    
    // Resize anyway. If it's different from what it was before, we should
    // already have cleared it.
    packet.buffer->resize(bufSize);
    return true;
}