Example #1
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;
}
Example #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;
}
Example #3
0
static event_t *
rtmp_loop(rtmp_t *r, media_pipe_t *mp, char *url, char *errbuf, size_t errlen)
{
  RTMPPacket p = {0};
  int pos = -1, ret;
  uint32_t dts;
  event_t *e = NULL;

  while(1) {


    if(pos == -1) {

      mp->mp_eof = 0;
      ret = RTMP_GetNextMediaPacket(r->r, &p);

      if(ret == 2) {
	/* Wait for queues to drain */
	mp->mp_eof = 1;
      again:
	e = mp_wait_for_empty_queues(mp);

	if(e != NULL) {
	  e = rtmp_process_event(r, e, NULL);
	  if(e == NULL)
	    goto again;
	}

	if(e == NULL)
	  e = event_create_type(EVENT_EOF);
	break;
      }

      if(ret == 0) {
	int64_t restartpos = r->seekpos_video;

        if(cancellable_is_cancelled(mp->mp_cancellable)) {
          snprintf(errbuf, errlen, "Cancelled");
          return NULL;
        }

	TRACE(TRACE_ERROR, "RTMP", "Disconnected");
	sleep(1);

	if(restartpos == AV_NOPTS_VALUE) {
	  snprintf(errbuf, errlen,
		   "Giving up restart since nothing was decoded");
	  return NULL;
	}


	RTMP_Close(r->r);

	RTMP_Init(r->r, mp->mp_cancellable);

	memset(&p, 0, sizeof(p));

	TRACE(TRACE_DEBUG, "RTMP", "Reconnecting stream at pos %ld", 
	      restartpos);

	if(!RTMP_SetupURL(r->r, url)) {
	  snprintf(errbuf, errlen, "Unable to setup RTMP session");
	  return NULL;
	}

	if(!RTMP_Connect(r->r, NULL, errbuf, errlen, 5000)) {
	  return NULL;
	}

	if(!RTMP_ConnectStream(r->r, 0)) {
	  snprintf(errbuf, errlen, "Unable to stream RTMP session");
	  return NULL;
	}

	if(mp->mp_flags & MP_CAN_SEEK)
	  RTMP_SendSeek(r->r, restartpos / 1000);
	continue;
      }

      dts = p.m_nTimeStamp;

      switch(p.m_packetType) {
      case RTMP_PACKET_TYPE_INFO:
	if(handle_metadata(r, p.m_body, p.m_nBodySize, mp, errbuf, errlen)) {
	  RTMPPacket_Free(&p);
	  return NULL;
	}
	break;

      case RTMP_PACKET_TYPE_VIDEO:
	e = get_packet_v(r, (void *)p.m_body, p.m_nBodySize, dts, mp);
	break;

      case RTMP_PACKET_TYPE_AUDIO:
	e = get_packet_a(r, (void *)p.m_body, p.m_nBodySize, dts, mp);
	break;
	
      case 0x16:
	pos = 0;
	break;
      default:
	TRACE(TRACE_DEBUG, "RTMP", 
	      "Got unknown packet type %d\n", p.m_packetType);
	break;
      }
      if(pos == -1)
	RTMPPacket_Free(&p);
    }

    if(pos != -1) {
      if(pos + 11 < p.m_nBodySize) {
	uint32_t ds = AMF_DecodeInt24(p.m_body + pos + 1);
	  
	if(pos + 11 + ds + 4 > p.m_nBodySize) {
	  snprintf(errbuf, errlen, "Corrupt stream");
	  RTMPPacket_Free(&p);
	  return NULL;
	}

	dts = AMF_DecodeInt24(p.m_body + pos + 4);
	dts |= (p.m_body[pos + 7] << 24);

	if(p.m_body[pos] == RTMP_PACKET_TYPE_INFO) {
	  if(handle_metadata(r, p.m_body, p.m_nBodySize, mp, errbuf, errlen)) {
	    RTMPPacket_Free(&p);
	    return NULL;
	  }
	} else if(p.m_body[pos] == RTMP_PACKET_TYPE_VIDEO) {
	  e = get_packet_v(r, (void *)p.m_body + pos + 11, ds, dts, mp);
	} else if(p.m_body[pos] == RTMP_PACKET_TYPE_AUDIO) {
	  e = get_packet_a(r, (void *)p.m_body + pos + 11, ds, dts, mp);
	} else {
	  TRACE(TRACE_DEBUG, "RTMP", 
		"Got unknown packet type %d\n", p.m_body[pos]);
	}
	pos += 11 + ds + 4;
      } else {
	pos = -1;
	RTMPPacket_Free(&p);
      }
    }
    if(e != NULL)
      break;
  }
  return e;
}
Example #4
0
static event_t *
get_packet_v(rtmp_t *r, uint8_t *data, int size, int64_t dts,
	     media_pipe_t *mp)
{
  uint8_t flags;
  uint8_t type = 0;
  enum AVCodecID id;
  int d = 0;
  event_t *e;

  if(r->r->m_read.flags & RTMP_READ_SEEKING)
    return NULL; 

  if(size < 2)
    return NULL;

  flags = *data++;
  size--;


  switch(flags & 0xf) {
  case 7:
    type = *data++;
    size--;
    id = AV_CODEC_ID_H264;

    if(size < 3)
      return NULL;
    
    d = (AMF_DecodeInt24((char *)data) + 0xff800000) ^ 0xff800000;
    data += 3;
    size -= 3;
    break;

  case 4:
    type = *data++;
    size--;
    id = AV_CODEC_ID_VP6F;
    break;
  default:
    return NULL;
  }

  if(r->vcodec == NULL) {
    media_codec_params_t mcp = {0};

    switch(id) {
    case AV_CODEC_ID_H264:
      if(type != 0 || size < 0)
	return NULL;

      mcp.extradata      = data;
      mcp.extradata_size = size;
      break;

    case AV_CODEC_ID_VP6F:
      if(size < 1)
	return NULL;
      mcp.extradata      = data;
      mcp.extradata_size = size;
      break;

    default:
      abort();
    }
    mcp.width = r->width;
    mcp.height = r->height;
    r->vcodec = media_codec_create(id, 0, NULL, NULL, &mcp, mp);
    return NULL;
  }


  int skip = 0;

  //  r->last_video_dts = dts;

  int64_t pts = 1000LL * (dts + d);
  dts = 1000LL * dts;

  if(d < 0 || dts <= r->seekpos_video) {
    skip = 1;
    r->in_seek_skip = 1;
  } else if(r->in_seek_skip) {
    skip = 2;
    r->in_seek_skip = 0;
  } else {
    r->seekpos_video = dts;
  }

  e = sendpkt(r, &r->mp->mp_video, r->vcodec, dts, pts,
	      data, size, skip, MB_VIDEO, r->vframeduration, 1);
  return e;
}
Example #5
0
static event_t *
rtmp_loop(rtmp_t *r, media_pipe_t *mp, char *url, char *errbuf, size_t errlen)
{
  RTMPPacket p = {0};
  int pos = -1, ret;
  uint32_t dts;
  event_t *e = NULL;

  mp_set_playstatus_by_hold(mp, 0, NULL);

  while(1) {


    if(pos == -1) {

      mp->mp_eof = 0;
      ret = RTMP_GetNextMediaPacket(r->r, &p);

      if(ret == 2) {
	/* Wait for queues to drain */
	mp->mp_eof = 1;
      again:
	e = mp_wait_for_empty_queues(mp);

	if(e != NULL) {
	  e = rtmp_process_event(r, e, NULL);
	  if(e == NULL)
	    goto again;
	}
	mp_set_playstatus_stop(mp);

	if(e == NULL)
	  e = event_create_type(EVENT_EOF);
	break;
      }

      if(ret == 0) {
	RTMP_Close(r->r);
	  
	RTMP_Init(r->r);

	memset(&p, 0, sizeof(p));

	TRACE(TRACE_DEBUG, "RTMP", "Reconnecting stream at pos %d", 
	      r->seekbase);

	if(!RTMP_SetupURL(r->r, url)) {
	  snprintf(errbuf, errlen, "Unable to setup RTMP session");
	  e = NULL;
	  break;
	}

	if(!RTMP_Connect(r->r, NULL)) {
	  snprintf(errbuf, errlen, "Unable to connect RTMP session");
	  e = NULL;
	  break;
	}

	if(!RTMP_ConnectStream(r->r, r->can_seek ? r->seekbase / 1000 : 0)) {
	  snprintf(errbuf, errlen, "Unable to stream RTMP session");
	  return NULL;
	}
	r->epoch++;


	r->lastdts = 0;
	r->seekbase = AV_NOPTS_VALUE;
	mp_flush(mp, 0);
	continue;
      }

      dts = p.m_nTimeStamp;

      switch(p.m_packetType) {
      case RTMP_PACKET_TYPE_INFO:
	if(handle_metadata(r, p.m_body, p.m_nBodySize, mp, errbuf, errlen)) {
	  RTMPPacket_Free(&p);
	  return NULL;
	}
	break;

      case RTMP_PACKET_TYPE_VIDEO:
	e = get_packet_v(r, (void *)p.m_body, p.m_nBodySize, dts, mp);
	break;

      case RTMP_PACKET_TYPE_AUDIO:
	e = get_packet_a(r, (void *)p.m_body, p.m_nBodySize, dts, mp);
	break;
	
      case 0x16:
	pos = 0;
	break;
      default:
	TRACE(TRACE_DEBUG, "RTMP", 
	      "Got unknown packet type %d\n", p.m_packetType);
	break;
      }
      if(pos == -1)
	RTMPPacket_Free(&p);
    }

    if(pos != -1) {
      if(pos + 11 < p.m_nBodySize) {
	uint32_t ds = AMF_DecodeInt24(p.m_body + pos + 1);
	  
	if(pos + 11 + ds + 4 > p.m_nBodySize) {
	  snprintf(errbuf, errlen, "Corrupt stream");
	  RTMPPacket_Free(&p);
	  return NULL;
	}

	dts = AMF_DecodeInt24(p.m_body + pos + 4);
	dts |= (p.m_body[pos + 7] << 24);

	if(p.m_body[pos] == RTMP_PACKET_TYPE_INFO) {
	  if(handle_metadata(r, p.m_body, p.m_nBodySize, mp, errbuf, errlen)) {
	    RTMPPacket_Free(&p);
	    return NULL;
	  }
	} else if(p.m_body[pos] == RTMP_PACKET_TYPE_VIDEO) {
	  e = get_packet_v(r, (void *)p.m_body + pos + 11, ds, dts, mp);
	} else if(p.m_body[pos] == RTMP_PACKET_TYPE_AUDIO) {
	  e = get_packet_a(r, (void *)p.m_body + pos + 11, ds, dts, mp);
	} else {
	  TRACE(TRACE_DEBUG, "RTMP", 
		"Got unknown packet type %d\n", p.m_body[pos]);
	}
	pos += 11 + ds + 4;
      } else {
	pos = -1;
	RTMPPacket_Free(&p);
      }
    }
    if(e != NULL)
      break;
  }
  return e;
}
Example #6
0
static event_t *
get_packet_v(rtmp_t *r, uint8_t *data, size_t size, int64_t dts,
	     media_pipe_t *mp)
{
  uint8_t flags;
  uint8_t type = 0;
  enum CodecID id;
  int d = 0;
  event_t *e;

  if(r->r->m_read.flags & RTMP_READ_SEEKING)
    return NULL; 

  if(size < 2)
    return NULL;

  flags = *data++;
  size--;


  switch(flags & 0xf) {
  case 7:
    type = *data++;
    size--;
    id = CODEC_ID_H264;

    if(size < 3)
      return NULL;
    
    d = (AMF_DecodeInt24((char *)data) + 0xff800000) ^ 0xff800000;
    data += 3;
    size -= 3;
    break;

  case 4:
    type = *data++;
    size--;
    id = CODEC_ID_VP6F;
    break;
  default:
    return NULL;
  }

  if(r->vcodec == NULL) {
    AVCodecContext *ctx;
    media_codec_params_t mcp = {0};

    switch(id) {
    case CODEC_ID_H264:
      if(type != 0 || size < 0)
	return NULL;
	
      ctx = avcodec_alloc_context3(NULL);
      ctx->extradata = av_mallocz(size + FF_INPUT_BUFFER_PADDING_SIZE);
      memcpy(ctx->extradata, data, size);
      ctx->extradata_size =  size;
      break;

    case CODEC_ID_VP6F:
      if(size < 1)
	return NULL;

      ctx = avcodec_alloc_context3(NULL);
      ctx->extradata = av_mallocz(1 + FF_INPUT_BUFFER_PADDING_SIZE);
      memcpy(ctx->extradata, &type, 1);
      ctx->extradata_size =  1;
      break;

    default:
      abort();
    }
    mcp.width = r->width;
    mcp.height = r->height;
    r->vcodec = media_codec_create(id, 0, NULL, ctx, &mcp, mp);
    return NULL;
  }


  int skip = 0;

  int64_t pts = 1000LL * (dts + d);
  dts = 1000LL * dts;

  if(d < 0 || dts < r->seekpos) {
    skip = 1;
    r->in_seek_skip = 1;
  } else if(r->in_seek_skip) {
    skip = 2;
    r->in_seek_skip = 0;
  }

  r->lastdts = dts;

  e = sendpkt(r, &r->mp->mp_video, r->vcodec, dts, pts, AV_NOPTS_VALUE,
	      data, size, skip, MB_VIDEO, r->vframeduration);
  return e;
}
Example #7
0
uint32_t inline amf_read_i24(const uint8_t *b)
{
    return (uint32_t)AMF_DecodeInt24((const char *)b);
}