Ejemplo n.º 1
0
void
AMF_DecodeLongString(const char *data, AVal *bv)
{
  bv->av_len = AMF_DecodeInt32(data);
  bv->av_val = (bv->av_len > 0) ? (char *)data + 4 : NULL;
}
Ejemplo n.º 2
0
int
GetLastKeyframe(FILE * file,	// output file [in]
				int nSkipKeyFrames,	// max number of frames to skip when searching for key frame [in]
				uint32_t * dSeek,	// offset of the last key frame [out]
				char **initialFrame,	// content of the last keyframe [out]
				int *initialFrameType,	// initial frame type (audio/video) [out]
				uint32_t * nInitialFrameSize)	// length of initialFrame [out]
{
	const size_t bufferSize = 16;
	char buffer[bufferSize];
	uint8_t dataType;
	int bAudioOnly;
	off_t size;

	fseek(file, 0, SEEK_END);
	size = ftello(file);

	fseek(file, 4, SEEK_SET);
	if (fread(&dataType, sizeof(uint8_t), 1, file) != 1)
		return RD_FAILED;

	bAudioOnly = (dataType & 0x4) && !(dataType & 0x1);

	RTMP_Log(RTMP_LOGDEBUG, "bAudioOnly: %d, size: %llu", bAudioOnly,
			 (unsigned long long) size);

	// ok, we have to get the timestamp of the last keyframe (only keyframes are seekable) / last audio frame (audio only streams)

	//if(!bAudioOnly) // we have to handle video/video+audio different since we have non-seekable frames
	//{
	// find the last seekable frame
	off_t tsize = 0;
	uint32_t prevTagSize = 0;

	// go through the file and find the last video keyframe
	do
	{
		int xread;
		skipkeyframe:
		if (size - tsize < 13)
		{
			RTMP_Log(RTMP_LOGERROR,
					 "Unexpected start of file, error in tag sizes, couldn't arrive at prevTagSize=0");
			return RD_FAILED;
		}
		fseeko(file, size - tsize - 4, SEEK_SET);
		xread = fread(buffer, 1, 4, file);
		if (xread != 4)
		{
			RTMP_Log(RTMP_LOGERROR, "Couldn't read prevTagSize from file!");
			return RD_FAILED;
		}

		prevTagSize = AMF_DecodeInt32(buffer);
		//RTMP_Log(RTMP_LOGDEBUG, "Last packet: prevTagSize: %d", prevTagSize);

		if (prevTagSize == 0)
		{
			RTMP_Log(RTMP_LOGERROR, "Couldn't find keyframe to resume from!");
			return RD_FAILED;
		}

		if (prevTagSize < 0 || prevTagSize > size - 4 - 13)
		{
			RTMP_Log(RTMP_LOGERROR,
					 "Last tag size must be greater/equal zero (prevTagSize=%d) and smaller then filesize, corrupt file!",
					 prevTagSize);
			return RD_FAILED;
		}
		tsize += prevTagSize + 4;

		// read header
		fseeko(file, size - tsize, SEEK_SET);
		if (fread(buffer, 1, 12, file) != 12)
		{
			RTMP_Log(RTMP_LOGERROR, "Couldn't read header!");
			return RD_FAILED;
		}
		//*
#ifdef _DEBUG
		uint32_t ts = AMF_DecodeInt24(buffer + 4);
		ts |= (buffer[7] << 24);
		RTMP_Log(RTMP_LOGDEBUG, "%02X: TS: %d ms", buffer[0], ts);
#endif //*/

		// this just continues the loop whenever the number of skipped frames is > 0,
		// so we look for the next keyframe to continue with
		//
		// this helps if resuming from the last keyframe fails and one doesn't want to start
		// the download from the beginning
		//
		if (nSkipKeyFrames > 0
			&& !(!bAudioOnly
				 && (buffer[0] != 0x09 || (buffer[11] & 0xf0) != 0x10)))
		{
#ifdef _DEBUG
			RTMP_Log(RTMP_LOGDEBUG,
					 "xxxxxxxxxxxxxxxxxxxxxxxx Well, lets go one more back!");
#endif
			nSkipKeyFrames--;
			goto skipkeyframe;
		}

	}
	while ((bAudioOnly && buffer[0] != 0x08) || (!bAudioOnly && (buffer[0] != 0x09 || (buffer[11] & 0xf0) != 0x10))); // as long as we don't have a keyframe / last audio frame

	// save keyframe to compare/find position in stream
	*initialFrameType = buffer[0];
	*nInitialFrameSize = prevTagSize - 11;
	*initialFrame = (char *) malloc(*nInitialFrameSize);

	fseeko(file, size - tsize + 11, SEEK_SET);
	if (fread(*initialFrame, 1, *nInitialFrameSize, file) != *nInitialFrameSize)
	{
		RTMP_Log(RTMP_LOGERROR, "Couldn't read last keyframe, aborting!");
		return RD_FAILED;
	}

	*dSeek = AMF_DecodeInt24(buffer + 4); // set seek position to keyframe tmestamp
	*dSeek |= (buffer[7] << 24);
	//}
	//else // handle audio only, we can seek anywhere we'd like
	//{
	//}

	if (*dSeek < 0)
	{
		RTMP_Log(RTMP_LOGERROR,
				 "Last keyframe timestamp is negative, aborting, your file is corrupt!");
		return RD_FAILED;
	}
	RTMP_Log(RTMP_LOGDEBUG, "Last keyframe found at: %d ms, size: %d, type: %02X", *dSeek,
			 *nInitialFrameSize, *initialFrameType);

	/*
	   // now read the timestamp of the frame before the seekable keyframe:
	   fseeko(file, size-tsize-4, SEEK_SET);
	   if(fread(buffer, 1, 4, file) != 4) {
	   RTMP_Log(RTMP_LOGERROR, "Couldn't read prevTagSize from file!");
	   goto start;
	   }
	   uint32_t prevTagSize = RTMP_LIB::AMF_DecodeInt32(buffer);
	   fseeko(file, size-tsize-4-prevTagSize+4, SEEK_SET);
	   if(fread(buffer, 1, 4, file) != 4) {
	   RTMP_Log(RTMP_LOGERROR, "Couldn't read previous timestamp!");
	   goto start;
	   }
	   uint32_t timestamp = RTMP_LIB::AMF_DecodeInt24(buffer);
	   timestamp |= (buffer[3]<<24);
  
	   RTMP_Log(RTMP_LOGDEBUG, "Previous timestamp: %d ms", timestamp);
	 */

	if (*dSeek != 0)
	{
		// seek to position after keyframe in our file (we will ignore the keyframes resent by the server
		// since they are sent a couple of times and handling this would be a mess)
		fseeko(file, size - tsize + prevTagSize + 4, SEEK_SET);

		// make sure the WriteStream doesn't write headers and ignores all the 0ms TS packets
		// (including several meta data headers and the keyframe we seeked to)
		//bNoHeader = TRUE; if bResume==true this is true anyway
	}

	//}

	return RD_SUCCESS;
}
Ejemplo n.º 3
0
int main(int argc, char* argv[]) {
    char c, *inFile;
    inFile = NULL;
    sc_frame_rect rect;
    
	#ifndef _WIN32
    signal(SIGPIPE, sig_pipe_handler);
	#endif

    while ((c = getopt (argc, argv, "w:h:u:r:f:")) != -1) {
        switch (c) {
            case 'w':
                rect.width = (uint16_t) atoi(optarg);
                break;
            case 'h':
                rect.height = (uint16_t) atoi(optarg);
                break;
            case 'u':
                streamUri = optarg;
                break;
            case 'r':
                roomName = optarg;
                break;
            case 'f':
                inFile = optarg;
                break;
        }
    }
    
    int fd;
    if(inFile) {
        printf("Started streamer with width: %i, height: %i, URI: %s, roomName: %s, File: %s\n", rect.width, rect.height, streamUri, roomName, inFile);
        fd = open(inFile, O_RDONLY);
    } else {
        printf("Started streamer with width: %i, height: %i, URI: %s, roomName: %s \n", rect.width, rect.height, streamUri, roomName);
        fd = fileno(stdin);
    }
    
    // main processing loop
    while(TRUE) {
        sc_bytestream_packet packet = sc_bytestream_get_event(fd);
        sc_mouse_coords coords;
        sc_frame frame;
        
        if(streamer.rtmp_setup == 1 && (!RTMP_IsConnected(streamer.rtmp) || RTMP_IsTimedout(streamer.rtmp))) {
            #ifdef _WIN32
				sc_streamer_die();
			#else
				sc_streamer_reconnect(&streamer);
			#endif
        }
        
        RTMPPacket rp = { 0 };
        if(streamer.rtmp_setup == 1  && streamer.so_name != NULL && streamer.have_inital_SO != 1 && RTMP_ReadPacket(streamer.rtmp, &rp)) {
            if (RTMPPacket_IsReady(&rp) && rp.m_packetType == RTMP_PACKET_TYPE_SHARED_OBJECT)
            {
                AVal SO_name;
                AMF_DecodeString(rp.m_body, &SO_name);
                
                streamer.so_version = AMF_DecodeInt32(rp.m_body+2+SO_name.av_len);
                streamer.have_inital_SO = 1;
            }
        }
        
        switch(packet.header.type) {
            case STARTVIDEO:
                capture_rect = rect;
                start_time_stamp = packet.header.timestamp;
                streamer = sc_streamer_init_video(streamUri, roomName, capture_rect, start_time_stamp);
                break;
            case STOPVIDEO:
				sc_streamer_stop_video(&streamer);
				sc_streamer_teardown_windows();
                exit(1);
                break;
            case STARTCURSOR:
                start_time_stamp = packet.header.timestamp;
				streamer = sc_streamer_init_cursor(streamUri, roomName, start_time_stamp);
                break;
            case STOPCURSOR:
				sc_streamer_stop_cursor(&streamer);
				sc_streamer_teardown_windows();
                exit(1);
                break;
            case MOUSE:
				coords = parse_mouse_coords(packet);
                streamer.so_version++;
                sc_streamer_send_mouse_data(&streamer, &coords, packet.header.timestamp);
                break;
            case VIDEO:
				frame = parse_frame(packet);

				if((RTMP_IsConnected(streamer.rtmp) &&  !RTMP_IsTimedout(streamer.rtmp)))
					sc_streamer_send_frame(&streamer, &frame, packet.header.timestamp);
				
				free(frame.framePtr);
				break;
            case NO_DATA:
            default:
                exit(0);
                break;    // maybe a wait is in order
        }
    }
    
    return 0;
}
Ejemplo n.º 4
0
int
OpenResumeFile(const char *flvFile,	// file name [in]
			   FILE ** file,	// opened file [out]
			   off_t * size,	// size of the file [out]
			   char **metaHeader,	// meta data read from the file [out]
			   uint32_t * nMetaHeaderSize,	// length of metaHeader [out]
			   double *duration)	// duration of the stream in ms [out]
{
	size_t bufferSize = 0;
	char hbuf[16], *buffer = NULL;

	*nMetaHeaderSize = 0;
	*size = 0;

	*file = fopen(flvFile, "r+b");
	if (!*file)
		return RD_SUCCESS;		// RD_SUCCESS, because we go to fresh file mode instead of quiting

	fseek(*file, 0, SEEK_END);
	*size = ftello(*file);
	fseek(*file, 0, SEEK_SET);

	if (*size > 0)
	{
		// verify FLV format and read header
		uint32_t prevTagSize = 0;

		// check we've got a valid FLV file to continue!
		if (fread(hbuf, 1, 13, *file) != 13)
		{
			RTMP_Log(RTMP_LOGERROR, "Couldn't read FLV file header!");
			return RD_FAILED;
		}
		if (hbuf[0] != 'F' || hbuf[1] != 'L' || hbuf[2] != 'V'
			|| hbuf[3] != 0x01)
		{
			RTMP_Log(RTMP_LOGERROR, "Invalid FLV file!");
			return RD_FAILED;
		}

		if ((hbuf[4] & 0x05) == 0)
		{
			RTMP_Log(RTMP_LOGERROR,
					 "FLV file contains neither video nor audio, aborting!");
			return RD_FAILED;
		}

		uint32_t dataOffset = AMF_DecodeInt32(hbuf + 5);
		fseek(*file, dataOffset, SEEK_SET);

		if (fread(hbuf, 1, 4, *file) != 4)
		{
			RTMP_Log(RTMP_LOGERROR, "Invalid FLV file: missing first prevTagSize!");
			return RD_FAILED;
		}
		prevTagSize = AMF_DecodeInt32(hbuf);
		if (prevTagSize != 0)
		{
			RTMP_Log(RTMP_LOGWARNING,
					 "First prevTagSize is not zero: prevTagSize = 0x%08X",
					 prevTagSize);
		}

		// go through the file to find the meta data!
		off_t pos = dataOffset + 4;
		int bFoundMetaHeader = FALSE;

		while (pos < *size - 4 && !bFoundMetaHeader)
		{
			fseeko(*file, pos, SEEK_SET);
			if (fread(hbuf, 1, 4, *file) != 4)
				break;

			uint32_t dataSize = AMF_DecodeInt24(hbuf + 1);

			if (hbuf[0] == 0x12)
			{
				if (dataSize > bufferSize)
				{
					/* round up to next page boundary */
					bufferSize = dataSize + 4095;
					bufferSize ^= (bufferSize & 4095);
					free(buffer);
					buffer = (char *)malloc(bufferSize);
					if (!buffer)
						return RD_FAILED;
				}

				fseeko(*file, pos + 11, SEEK_SET);
				if (fread(buffer, 1, dataSize, *file) != dataSize)
					break;

				AMFObject metaObj;
				int nRes = AMF_Decode(&metaObj, buffer, dataSize, FALSE);
				if (nRes < 0)
				{
					RTMP_Log(RTMP_LOGERROR, "%s, error decoding meta data packet",
							 __FUNCTION__);
					break;
				}

				AVal metastring;
				AMFProp_GetString(AMF_GetProp(&metaObj, NULL, 0), &metastring);

				if (AVMATCH(&metastring, &av_onMetaData))
				{
					AMF_Dump(&metaObj);

					*nMetaHeaderSize = dataSize;
					if (*metaHeader)
						free(*metaHeader);
					*metaHeader = (char *) malloc(*nMetaHeaderSize);
					memcpy(*metaHeader, buffer, *nMetaHeaderSize);

					// get duration
					AMFObjectProperty prop;
					if (RTMP_FindFirstMatchingProperty
						(&metaObj, &av_duration, &prop))
					{
						*duration = AMFProp_GetNumber(&prop);
						RTMP_Log(RTMP_LOGDEBUG, "File has duration: %f", *duration);
					}

					bFoundMetaHeader = TRUE;
					break;
				}
				//metaObj.Reset();
				//delete obj;
			}
			pos += (dataSize + 11 + 4);
		}

		free(buffer);
		if (!bFoundMetaHeader)
			RTMP_Log(RTMP_LOGWARNING, "Couldn't locate meta data!");
	}

	return RD_SUCCESS;
}
Ejemplo n.º 5
0
// Decoding functions
uint32_t inline amf_read_i32(const uint8_t *b)
{
    return (uint32_t)AMF_DecodeInt32((const char*)b);
}