目录
RTMP协议相关:
【流媒体】RTMP协议概述
【流媒体】RTMP协议的数据格式
【流媒体】RTMP协议的消息类型
【流媒体】RTMPDump---主流程简单分析
【流媒体】RTMPDump---RTMP_Connect函数(握手、网络连接)
【流媒体】RTMPDump---RTMP_ConnectStream(创建流连接)
参考雷博的系列文章(可以从一篇链接到其他文章):
RTMPdump 源代码分析 1: main()函数
在进行流连接之后,还可以进行传输过来数据的下载,执行这一功能的函数是Download(),其中使用RTMP_Read()读取数据,随后使用fwrite写入文件。写入文件通常是FLV格式,如果没有指定这个file,则会默认写到stdout
c
int
Download(RTMP * rtmp, // connected RTMP object
FILE * file, uint32_t dSeek, uint32_t dStopOffset, double duration, int bResume, char* metaHeader, uint32_t nMetaHeaderSize, char* initialFrame, int initialFrameType, uint32_t nInitialFrameSize, int nSkipKeyFrames, int bStdoutMode, int bLiveStream, int bRealtimeStream, int bHashes, int bOverrideBufferTime, uint32_t bufferTime, double* percent) // percentage downloaded [out]
{
int32_t now, lastUpdate;
int bufferSize = 64 * 1024;
char* buffer;
int nRead = 0;
off_t size = ftello(file);
unsigned long lastPercent = 0;
rtmp->m_read.timestamp = dSeek;
*percent = 0.0;
if (rtmp->m_read.timestamp)
{
RTMP_Log(RTMP_LOGDEBUG, "Continuing at TS: %d ms\n", rtmp->m_read.timestamp);
}
if (bLiveStream)
{
RTMP_LogPrintf("Starting Live Stream\n");
}
else
{
// print initial status
// Workaround to exit with 0 if the file is fully (> 99.9%) downloaded
if (duration > 0)
{
if ((double)rtmp->m_read.timestamp >= (double)duration * 999.0)
{
RTMP_LogPrintf("Already Completed at: %.3f sec Duration=%.3f sec\n",
(double)rtmp->m_read.timestamp / 1000.0,
(double)duration / 1000.0);
return RD_SUCCESS;
}
else
{
*percent = ((double)rtmp->m_read.timestamp) / (duration * 1000.0) * 100.0;
*percent = ((double)(int)(*percent * 10.0)) / 10.0;
RTMP_LogPrintf("%s download at: %.3f kB / %.3f sec (%.1f%%)\n",
bResume ? "Resuming" : "Starting",
(double)size / 1024.0, (double)rtmp->m_read.timestamp / 1000.0,
*percent);
}
}
else
{
RTMP_LogPrintf("%s download at: %.3f kB\n",
bResume ? "Resuming" : "Starting",
(double)size / 1024.0);
}
if (bRealtimeStream)
RTMP_LogPrintf(" in approximately realtime (disabled BUFX speedup hack)\n");
}
if (dStopOffset > 0)
RTMP_LogPrintf("For duration: %.3f sec\n", (double)(dStopOffset - dSeek) / 1000.0);
if (bResume && nInitialFrameSize > 0)
rtmp->m_read.flags |= RTMP_READ_RESUME;
rtmp->m_read.initialFrameType = initialFrameType;
rtmp->m_read.nResumeTS = dSeek;
rtmp->m_read.metaHeader = metaHeader;
rtmp->m_read.initialFrame = initialFrame;
rtmp->m_read.nMetaHeaderSize = nMetaHeaderSize;
rtmp->m_read.nInitialFrameSize = nInitialFrameSize;
buffer = (char*)malloc(bufferSize);
now = RTMP_GetTime();
lastUpdate = now - 1000;
do
{
// 读取信息
nRead = RTMP_Read(rtmp, buffer, bufferSize);
//RTMP_LogPrintf("nRead: %d\n", nRead);
if (nRead > 0)
{
// 将数据写入到file当中,FLV格式
// 如果这个file没有指定,默认是stdout
if (fwrite(buffer, sizeof(unsigned char), nRead, file) !=
(size_t)nRead)
{
RTMP_Log(RTMP_LOGERROR, "%s: Failed writing, exiting!", __FUNCTION__);
free(buffer);
return RD_FAILED;
}
size += nRead;
//RTMP_LogPrintf("write %dbytes (%.1f kB)\n", nRead, nRead/1024.0);
if (duration <= 0) // if duration unknown try to get it from the stream (onMetaData)
duration = RTMP_GetDuration(rtmp);
if (duration > 0)
{
// make sure we claim to have enough buffer time!
if (!bOverrideBufferTime && bufferTime < (duration * 1000.0))
{
bufferTime = (uint32_t)(duration * 1000.0) + 5000; // extra 5sec to make sure we've got enough
RTMP_Log(RTMP_LOGDEBUG,
"Detected that buffer time is less than duration, resetting to: %dms",
bufferTime);
RTMP_SetBufferMS(rtmp, bufferTime);
RTMP_UpdateBufferMS(rtmp);
}
*percent = ((double)rtmp->m_read.timestamp) / (duration * 1000.0) * 100.0;
*percent = ((double)(int)(*percent * 10.0)) / 10.0;
if (bHashes)
{
if (lastPercent + 1 <= *percent)
{
RTMP_LogStatus("#");
lastPercent = (unsigned long)* percent;
}
}
else
{
now = RTMP_GetTime();
if (abs(now - lastUpdate) > 200)
{
RTMP_LogStatus("\r%.3f kB / %.2f sec (%.1f%%)",
(double)size / 1024.0,
(double)(rtmp->m_read.timestamp) / 1000.0, *percent);
lastUpdate = now;
}
}
}
else
{
now = RTMP_GetTime();
if (abs(now - lastUpdate) > 200)
{
if (bHashes)
RTMP_LogStatus("#");
else
RTMP_LogStatus("\r%.3f kB / %.2f sec", (double)size / 1024.0,
(double)(rtmp->m_read.timestamp) / 1000.0);
lastUpdate = now;
}
}
}
else
{
#ifdef _DEBUG
RTMP_Log(RTMP_LOGDEBUG, "zero read!");
#endif
if (rtmp->m_read.status == RTMP_READ_EOF)
break;
}
} while (!RTMP_ctrlC && nRead > -1 && RTMP_IsConnected(rtmp) && !RTMP_IsTimedout(rtmp));
free(buffer);
if (nRead < 0)
nRead = rtmp->m_read.status;
/* Final status update */
if (!bHashes)
{
if (duration > 0)
{
*percent = ((double)rtmp->m_read.timestamp) / (duration * 1000.0) * 100.0;
*percent = ((double)(int)(*percent * 10.0)) / 10.0;
RTMP_LogStatus("\r%.3f kB / %.2f sec (%.1f%%)",
(double)size / 1024.0,
(double)(rtmp->m_read.timestamp) / 1000.0, *percent);
}
else
{
RTMP_LogStatus("\r%.3f kB / %.2f sec", (double)size / 1024.0,
(double)(rtmp->m_read.timestamp) / 1000.0);
}
}
RTMP_Log(RTMP_LOGDEBUG, "RTMP_Read returned: %d", nRead);
if (bResume && nRead == -2)
{
RTMP_LogPrintf("Couldn't resume FLV file, try --skip %d\n\n",
nSkipKeyFrames + 1);
return RD_FAILED;
}
if (nRead == -3)
return RD_SUCCESS;
if ((duration > 0 && *percent < 99.9) || RTMP_ctrlC || nRead < 0
|| RTMP_IsTimedout(rtmp))
{
return RD_INCOMPLETE;
}
return RD_SUCCESS;
}