static int handle_metadata0(rtmp_t *r, AMFObject *obj, media_pipe_t *mp, char *errstr, size_t errlen) { AVal metastring; AMFObjectProperty prop; prop_t *m = mp->mp_prop_metadata; AMFProp_GetString(AMF_GetProp(obj, NULL, 0), &metastring); if(!AVMATCH(&metastring, &av_onMetaData)) { snprintf(errstr, errlen, "No metadata in metadata packet"); return -1; } if(RTMP_FindFirstMatchingProperty(obj, &av_duration, &prop) && prop.p_type == AMF_NUMBER && prop.p_vu.p_number > 0) { prop_set_float(prop_create(m, "duration"), prop.p_vu.p_number); r->total_duration = prop.p_vu.p_number * 1000; mp_set_duration(mp, r->total_duration * 1000LL); mp_set_clr_flags(mp, MP_CAN_SEEK, 0); if(r->ss == NULL && !(r->va->flags & BACKEND_VIDEO_NO_SUBTITLE_SCAN)) r->ss = sub_scanner_create(r->url, mp->mp_prop_subtitle_tracks, r->va, 0); } else { r->total_duration = 0; mp_set_duration(mp, AV_NOPTS_VALUE); mp_set_clr_flags(mp, 0, MP_CAN_SEEK); } if((RTMP_FindFirstMatchingProperty(obj, &av_videoframerate, &prop) && RTMP_FindFirstMatchingProperty(obj, &av_framerate, &prop)) && prop.p_type == AMF_NUMBER) { r->vframeduration = 1000000.0 / prop.p_vu.p_number; mp->mp_framerate.num = 1000000; mp->mp_framerate.den = prop.p_vu.p_number; } r->width = r->height = 0; if(RTMP_FindFirstMatchingProperty(obj, &av_width, &prop) && prop.p_type == AMF_NUMBER) r->width = prop.p_vu.p_number; if(RTMP_FindFirstMatchingProperty(obj, &av_height, &prop) && prop.p_type == AMF_NUMBER) r->height = prop.p_vu.p_number; if(r->width && r->height) TRACE(TRACE_DEBUG, "RTMP", "Video size %d x %d", r->width, r->height); return 0; }
static int handle_metadata0(rtmp_t *r, AMFObject *obj, media_pipe_t *mp, char *errstr, size_t errlen) { AVal metastring; AMFObjectProperty prop; prop_t *m = mp->mp_prop_metadata; AMFProp_GetString(AMF_GetProp(obj, NULL, 0), &metastring); if(!AVMATCH(&metastring, &av_onMetaData)) { snprintf(errstr, errlen, "No metadata in metadata packet"); return -1; } if(!RTMP_FindFirstMatchingProperty(obj, &av_duration, &prop) || prop.p_type != AMF_NUMBER) { snprintf(errstr, errlen, "Unable to parse total duration"); return -1; } prop_set_float(prop_create(m, "duration"), prop.p_vu.p_number); r->duration = prop.p_vu.p_number; if(!RTMP_FindFirstMatchingProperty(obj, &av_videoframerate, &prop) || prop.p_type != AMF_NUMBER) { if(!RTMP_FindFirstMatchingProperty(obj, &av_framerate, &prop) || prop.p_type != AMF_NUMBER) { snprintf(errstr, errlen, "Unable to parse video framerate"); return -1; } } r->vframeduration = 1000000.0 / prop.p_vu.p_number; r->width = r->height = 0; if(RTMP_FindFirstMatchingProperty(obj, &av_width, &prop) && prop.p_type == AMF_NUMBER) r->width = prop.p_vu.p_number; if(RTMP_FindFirstMatchingProperty(obj, &av_height, &prop) && prop.p_type == AMF_NUMBER) r->height = prop.p_vu.p_number; if(r->width && r->height) TRACE(TRACE_DEBUG, "RTMP", "Video size %d x %d", r->width, r->height); return 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; }