AsfRecorder.c 研究
v1.1
开源项目, 代码来自:
http://sourceforge.net/projects/asfrecorder/
主要实现下载
http stream file
/* The core routine for ASF download/extraction. */
int
collectdata(int headeronly,
int dumpheaders,
FILE *rawfile,
FILE *outfile,
int bytesread,
unsigned char *Buffer,
int bufsize,
int sendlen,
struct hostent *hp,
char *server_name,
unsigned int addr,
unsigned short port,
int socket_type,
char *file,
struct HeaderInfo *hi,
int maxtime)
{
SOCKET conn_socket;
int retval;
SOCKADDR_IN server;
int eol;
int hdrpos;
int linepos;
int linenum;
char HTTPHeader[1024];
int resume = 0;
char HTTPLine[512];
char HTTPMessage[128];
char *hdrptr;
int errorcode;
...
conn_socket
= my_socket(AF_INET,socket_type,0); /* Open a socket */
...
retval = my_send(conn_socket,Buffer,sendlen,0);
for (;;)
{
char c;
if (readfromstream(conn_socket, &c, 1, rawfile) == 1)
{
if ((c != '/r') && (c != '/n'))
{
eol = 0;
HTTPLine[linepos++] = c;
}
else
HTTPLine[linepos++] = 0;
if (c == '/n')
{
if (eol == 0)
{
linepos = 0;
eol = 1;
linenum++;
hdrptr = HTTPLine;
/* Parse first line of HTTP reply */
if (linenum == 1)
{
if ((!strnicmp(hdrptr, "HTTP/1.0 ", 9)) ||
(!strnicmp(hdrptr, "HTTP/1.1 ", 9)) )
{
hdrptr+=9;
sscanf(hdrptr, "%d", &errorcode);
hdrptr+=4;
strcpy(HTTPMessage, hdrptr);
}
else
{
gui_seterror("Illegal server reply! Expected HTTP/1.0 or HTTP/1.1/n");
hi->contenttype = unknown_content;
}
}
else
{
/* Parse all other lines of HTTP reply */
if (!strnicmp(hdrptr, "Content-Type: ", 14))
{
hdrptr+=14;
strncpy(ContentType, hdrptr, sizeof(ContentType));
}
/* Parse all other lines of HTTP reply */
if (!strnicmp(hdrptr, "Pragma: ", 8))
{
hdrptr+=8;
if (!strnicmp(hdrptr, "features=", 9))
{
hdrptr+=9;
strncpy(Features, hdrptr, sizeof(Features));
}
}
}
}
else
{
HTTPHeader[hdrpos++] = 0;
break;
}
}
HTTPHeader[hdrpos++] = c;
}
else
{
gui_seterror("readfromstream() returned other than 1!/n");
if (eos()) break;
}
}
hi->contenttype = unknown_content;
/* Determine whether this is live content or not */
if (!stricmp(ContentType, "application/octet-stream"))
{
if (strstr(Features, "broadcast"))
{
hi->contenttype = live_content;
}
else
{
hi->contenttype = prerecorded_content;
}
}
else
{
...
}
...
/* handle live or prerecorded content */
if ((!eos()) && ((hi->contenttype == live_content) || (hi->contenttype == prerecorded_content)) )
{
unsigned int starttime_hi = 0xffffffff;
unsigned int starttime = 0xffffffff;
unsigned int startseqno = 0xffffffff;
unsigned int maxtimecode = 0;
int endofheaderposition = 0;
int numdatachunks = 0;
int sizeofdatachunks = 0;
int header_length = 0;
int header_offset = 0;
/* The main loop for chunk extraction/ASF generation */
for (;;)
{
...
/* Check for EOF and extract chunk header */
/* bytes are read one by one so this code */
/* remains portable to non-INTEL platforms */
if (eos()) { gui_setstatus("Connection reset/n"); break; }
/* read basic chunk type */
readfromstream(conn_socket, &c1, 1, rawfile);
readfromstream(conn_socket, &c2, 1, rawfile);
type = (c2<<8) + c1;
/* These header types correspond to "H$", "D$" and "E$" */
/* (Header, Data and End) */
if ((type != HEADER_CHUNK) && (type != DATA_CHUNK) && (type != END_CHUNK))
{
gui_logtext("Unknown header type: $x/n", type);
}
if (type == END_CHUNK)
{
gui_setstatus("Transfer complete./n");
break;
}
if (eos()) { gui_setstatus("Connection reset/n"); break; }
/* read chunk length (max 64k) */
readfromstream(conn_socket, &l1, 1, rawfile);
readfromstream(conn_socket, &l2, 1, rawfile);
length = (l2<<8) + l1;
if (eos()) { gui_setstatus("Connection reset/n"); break; }
/* read chunk sequence number */
readfromstream(conn_socket, &s1, 1, rawfile);
readfromstream(conn_socket, &s2, 1, rawfile);
readfromstream(conn_socket, &s3, 1, rawfile);
readfromstream(conn_socket, &s4, 1, rawfile);
seqno = (s4<<24) + (s3<<16) + (s2<<8) + s1;
if (eos()) { gui_setstatus("Connection reset/n"); break; }
/* read two unknown bytes */
readfromstream(conn_socket, &u1, 1, rawfile);
readfromstream(conn_socket, &u2, 1, rawfile);
partflag = (u2<<8) + u1;
if (eos()) { gui_setstatus("Connection reset/n"); break; }
/* read second length entry (length confirmation) */
readfromstream(conn_socket, &l1, 1, rawfile);
readfromstream(conn_socket, &l2, 1, rawfile);
length2 = (l2<<8) + l1;
if (eos()) { gui_setstatus("Connection reset/n"); break; }
/* Sanity check on chunk header. Second length entry must match the first. */
if (length2 != length)
{
gui_logtext("Length confirmation doesn't match!/n");
break;
}
/* calculate length of chunk body. */
bodylength = length-8;
/* check if the body length exceeds our buffer size */
if (bodylength > bufsize)
{
gui_logtext("Buffer too small. Chunk is %d bytes!/n", length);
break;
}
/* check length of chunk body */
if (bodylength <= 0)
{
gui_logtext("Chunk has no body!/n");
break;
}
/* Read chunk's body data */
if (type != HEADER_CHUNK) header_offset = 0;
got = readfromstream(conn_socket, Buffer + header_offset, bodylength, rawfile);
bodylength = header_offset + bodylength;
got = header_offset + got;
/* Try to extract a timecode from all known chunk/content types */
strcpy(timecodestring, "???");
timecode = 0;
/* this only applies to data chunks */
if (type == DATA_CHUNK)
{
int tc_start;
if (headeronly) break;
/* save the first seqno available as a reference */
if (startseqno == 0xffffffff)
{
startseqno = seqno;
}
/* fix the seqno for live recordings only */
if (hi->time == 0)
{
/* refer seqno to the point we "zapped in" (for live streams) */
if (startseqno != 0xffffffff)
seqno -= startseqno;
}
/* find the location of the time code */
if ((tc_start = whereis_timecode(Buffer)) > 0)
{
/* The timecode is an integer value defining milliseconds */
/* enough range for about 50 days! */
get_long(&Buffer[tc_start], &timecode);
/* save the first timecode available as a reference */
if (starttime == 0xffffffff)
starttime = timecode;
/* fix timecode for live recordings only */
if (hi->time == 0)
{
/* refer timecode to the point we "zapped in" (live streams) */
timecode -= starttime;
/* this fixes the timecodes in the memory buffer */
fix_timecodes(Buffer, bodylength, starttime, seqno, hi);
}
/* save max. timecode value */
if (timecode > maxtimecode)
maxtimecode = timecode;
/* create a string with a human-readable form of the timecode */
strcpy(timecodestring, createtimestring(timecode));
}
}
/* calculate progress indicator (scale: 0....10000) */
if (hi->time == 0)
/* live streams */
if (maxtime == 0) /* unlimited recording */
progress = 0;
else /* limited time recording */
progress = (int)((double)timecode*10000/(maxtime*60*1000));
else
/* prerecorded content */
progress = (int)((double)timecode*10000/hi->time);
/* Print current position in stream download */
gui_logtext("] kB (-%%), HDR: $x, part: $x, M bytes, seq $x, tc: %s/n", bytesread/1024, progress/100, (int)type, (int)partflag, length, seqno, timecodestring);
/* Extract the block size from the ASF header. */
/* This block size is essential in the ASF file */
/* format. All data chunks in the ASF file must */
/* have this length, even if network transmission */
/* sent us smaller chunks! */
if (type == HEADER_CHUNK)
{
int offs;
/* Headers may be split into several parts with */
/* a rising sequence number. This case occurs in */
/* audio books on broadcast.com, for example. */
/* This indicates the first header part */
if (partflag & 0x0400)
{
header_offset = 0;
get_long(&Buffer[16], &header_length);
header_length += 50;
}
/* header progress indicator */
if (bodylength < header_length)
gui_setstatus("receiving ASF header (%d/%d)!/n", bodylength, header_length);
/* Skip parsing the header if it hasn't been received completely */
if (!(partflag & 0x0800))
{
/* next header partition will be appended */
header_offset = bodylength;
/* this prevents saving a partial header to the output file */
bodylength = header_length;
}
else
{
/* finally got the header */
gui_setstatus("received ASF header!/n");
/* find a specific object in this header */
offs = find_id(HDR_ID,Buffer, got);
if (offs == -1)
{
gui_criticalerror("Unable to parse this ASF header!/n");
break;
}
else
{
/* extract required information */
hi->header_offs = offs;
get_quad(&Buffer[offs+HDR_TOTAL_SIZE_8], &hi->totalsize_hi, &hi->totalsize_lo);
get_quad(&Buffer[offs+HDR_FINE_TOTALTIME_8], &hi->totaltime_hi, &hi->totaltime_lo);
get_long(&Buffer[offs+HDR_PLAYTIME_OFFSET_4], &hi->offset);
get_long(&Buffer[offs+HDR_FLAGS_4], &hi->flags);
get_long(&Buffer[offs+HDR_ASF_CHUNKLENGTH_4], &hi->chunklength);
get_long(&Buffer[offs+HDR_ASF_CHUNKLENGTH_CONFIRM_4], &hi->chunklength2);
/* check if the extracted chunk length looks good */
if (!(hi->chunklength < MAX_CHUNK_SIZE && (hi->chunklength == hi->chunklength2)))
{
gui_criticalerror(
"Unable to capture this stream!/n"
"This one uses variable chunk sizes,/n"
"which is not supported. Sorry! ;-)/n");
hi->contenttype = unknown_content;
break;
}
/* calculate playtime in milliseconds (0 for live streams) */
if (hi->totaltime_hi == 0 && hi->totaltime_lo == 0)
{
hi->time = 0; /* live streams */
}
else
hi->time = (int)((double)429496.7296 * hi->totaltime_hi) + (hi->totaltime_lo / 10000) - hi->offset;
/* store position where the ASF header segment ends */
/* and the chunk data segment starts */
hi->endofheader_offs = bodylength - DATSEG_HDR_SIZE;
if (!headeronly)
gui_setstatus("receiving stream.../n");
else
break;
}
}
}
/* check chunk body for completeness */
if (got < bodylength)
{
if (outfile != NULL && type == DATA_CHUNK)
gui_logtext("Received incomplete chunk... (Chunk is NOT saved)/n");
}
else
{
/* Ignore media type header if the download */
/* resumes a previous transmission */
if (type == HEADER_CHUNK && resume)
{
gui_logtext("Not saving header since this is a RESUME./n");
}
else
{
/* count how many bytes we accepted from server */
bytesread += got;
/* some statistics for data chunks only */
if (type == DATA_CHUNK)
{
/* count number of chunks */
numdatachunks++;
/* count total size of chunks */
sizeofdatachunks += hi->chunklength;
}
if (outfile != NULL)
{
if (type == DATA_CHUNK)
{
/* When saving data chunks, seek to appropriate position */
/* in file. This should prevent problems when resuming to */
/* download files containing a truncated last chunk */
fseek(outfile, hi->endofheader_offs + DATSEG_HDR_SIZE + seqno * hi->chunklength, SEEK_SET);
}
/* Save chunk body to ASF file */
fwrite(Buffer, 1, got, outfile);
/* Fill up unused bytes in this chunk. */
/* ASF requires equally sized chunks for */
/* the main stream content */
if (type == DATA_CHUNK && got < (int)hi->chunklength)
{
int i;
for (i=0; i < (int)hi->chunklength-got; i++) fputc(0, outfile);
}
}
/* send progress information to main GUI */
gui_progressinfo(bytesread, timecodestring, progress, seqno, timecode);
/* set a new total time for the stream */
/* (important for preview and slider functionality) */
if (hi->time == 0)
{
if (maxtime == 0)
gui_modify_duration(timecode);
else
gui_modify_duration(maxtime*60*1000);
}
}
}
/* use recording time limit, if specified */
if (maxtime != 0)
{
if ((int)timecode >= (maxtime*60*1000))
{
gui_setstatus("maxtime reached./n");
break;
}
}
/* check for end request */
if (abortflag) break;
} /* for (;;) */
/* fix total file size and for live streams the header information as well */
if (outfile != NULL)
{
unsigned int filesize_lo;
unsigned int filesize_hi;
/* Determine file size of .ASF file */
fseek(outfile, 0, SEEK_END);
filesize_hi = 0;
filesize_lo = ftell(outfile);
/* write this file size to the ASF header */
fseek(outfile, hi->header_offs + HDR_TOTAL_SIZE_8, SEEK_SET);
write_quad(outfile, filesize_hi, filesize_lo);
/* Correct some details for live streams */
if (hi->time == 0)
{
double totaltime;
unsigned int totaltime_hi;
unsigned int totaltime_lo;
double playtime;
unsigned int playtime_hi;
unsigned int playtime_lo;
unsigned int num_chunks_hi;
unsigned int num_chunks_lo;
unsigned int segmentsize_hi;
unsigned int segmentsize_lo;
/* bugfixed calculations, these were broken in V 1.0 */
totaltime = (double)(maxtimecode) * 10000;
totaltime_hi = (int)(totaltime / 4294967296.0);
totaltime_lo = (int)(totaltime - ((double)totaltime_hi * 4294967296.0));
playtime = (double)(maxtimecode - hi->offset) * 10000;
playtime_hi = (int)(playtime / 4294967296.0);
playtime_lo = (int)(playtime - ((double)playtime_hi * 4294967296.0));
num_chunks_hi = 0;
num_chunks_lo = numdatachunks;
/* write correct number of packets for stream */
fseek(outfile, hi->header_offs + HDR_NUM_PACKETS_8, SEEK_SET);
write_quad(outfile, num_chunks_hi, num_chunks_lo);
/* write correct totaltime for stream */
fseek(outfile, hi->header_offs + HDR_FINE_TOTALTIME_8, SEEK_SET);
write_quad(outfile, totaltime_hi, totaltime_lo);
/* write correct playtime for stream */
fseek(outfile, hi->header_offs + HDR_FINE_PLAYTIME_8, SEEK_SET);
write_quad(outfile, playtime_hi, playtime_lo);
/* enable seeking in file */
fseek(outfile, hi->header_offs + HDR_FLAGS_4, SEEK_SET);
write_long(outfile, 0x00000002);
/* write total size of data chunk segment */
segmentsize_hi = 0;
segmentsize_lo = DATSEG_HDR_SIZE + sizeofdatachunks;
fseek(outfile, hi->endofheader_offs + 0x10, SEEK_SET);
write_quad(outfile, segmentsize_hi, segmentsize_lo);
/* write total number of chunks */
fseek(outfile, hi->endofheader_offs + DATSEG_NUMCHUNKS_4, SEEK_SET);
write_long(outfile, numdatachunks);
}
}
if (abortflag)
{
gui_seterror("Transfer aborted!/n");
}
}
}
}
my_closesocket(conn_socket);
}
return abortflag;
}
转载请注明原文地址: https://ibbs.8miu.com/read-29860.html