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; }
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; } } }
/// 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; }