📄 ffmpeg_demux.c
字号:
return esd;}static void FFD_SetupObjects(FFDemux *ffd){ GF_ESD *esd; GF_ObjectDescriptor *od; u32 audio_esid = 0; if (ffd->audio_st>=0) { od = (GF_ObjectDescriptor *) gf_odf_desc_new(GF_ODF_OD_TAG); esd = FFD_GetESDescriptor(ffd, 1); od->objectDescriptorID = esd->ESID; audio_esid = esd->ESID; gf_list_add(od->ESDescriptors, esd); gf_term_add_media(ffd->service, (GF_Descriptor*)od, (ffd->video_st>=0) ? 1 : 0); } if (ffd->video_st>=0) { od = (GF_ObjectDescriptor *) gf_odf_desc_new(GF_ODF_OD_TAG); esd = FFD_GetESDescriptor(ffd, 0); od->objectDescriptorID = esd->ESID; esd->OCRESID = audio_esid; gf_list_add(od->ESDescriptors, esd); gf_term_add_media(ffd->service, (GF_Descriptor*)od, 0); }}static GF_Err FFD_ConnectService(GF_InputService *plug, GF_ClientService *serv, const char *url){ GF_Err e; s64 last_aud_pts; s32 i; const char *sOpt; char *ext, szName[1000]; FFDemux *ffd = plug->priv; AVInputFormat *av_in = NULL; if (ffd->ctx) return GF_SERVICE_ERROR; strcpy(szName, url); ext = strrchr(szName, '#'); ffd->service_type = 0; e = GF_NOT_SUPPORTED; ffd->service = serv; if (ext) { if (!stricmp(&ext[1], "video")) ffd->service_type = 1; else if (!stricmp(&ext[1], "audio")) ffd->service_type = 2; ext[0] = 0; } i = av_open_input_file(&ffd->ctx, szName, NULL, 0, NULL); if (i<0) { char szExt[20]; ext = strrchr(szName, '.'); strcpy(szExt, ext+1); strlwr(szExt); /*some extensions not supported by ffmpeg*/ if (ext && !strcmp(szExt, "cmp")) av_in = av_find_input_format("m4v"); i = av_open_input_file(&ffd->ctx, szName, av_in, 0, NULL); } switch (i) { case 0: e = GF_OK; break; case AVERROR_IO: e = GF_URL_ERROR; goto err_exit; case AVERROR_INVALIDDATA: e = GF_NON_COMPLIANT_BITSTREAM; goto err_exit; case AVERROR_NOMEM: e = GF_OUT_OF_MEM; goto err_exit; case AVERROR_NOFMT: e = GF_NOT_SUPPORTED; goto err_exit; default: e = GF_SERVICE_ERROR; goto err_exit; } if (av_find_stream_info(ffd->ctx) <0) goto err_exit; /*figure out if we can use codecs or not*/ ffd->audio_st = ffd->video_st = -1; for (i = 0; i < ffd->ctx->nb_streams; i++) { AVCodecContext *enc = ffd->ctx->streams[i]->codec; switch(enc->codec_type) { case CODEC_TYPE_AUDIO: if ((ffd->audio_st<0) && (ffd->service_type!=1)) { ffd->audio_st = i; ffd->audio_tscale = ffd->ctx->streams[i]->time_base; } break; case CODEC_TYPE_VIDEO: if ((ffd->video_st<0) && (ffd->service_type!=2)) { ffd->video_st = i; ffd->video_tscale = ffd->ctx->streams[i]->time_base; } break; default: break; } } if ((ffd->service_type==1) && (ffd->video_st<0)) goto err_exit; if ((ffd->service_type==2) && (ffd->audio_st<0)) goto err_exit; if ((ffd->video_st<0) && (ffd->audio_st<0)) goto err_exit; sOpt = gf_modules_get_option((GF_BaseInterface *)plug, "FFMPEG", "DataBufferMS"); ffd->data_buffer_ms = 0; if (sOpt) ffd->data_buffer_ms = atoi(sOpt); if (!ffd->data_buffer_ms) ffd->data_buffer_ms = FFD_DATA_BUFFER; /*check we do have increasing pts. If not we can't rely on pts, we must skip SL we assume video pts is always present*/ if (ffd->audio_st>=0) { last_aud_pts = 0; for (i=0; i<20; i++) { AVPacket pkt; pkt.stream_index = -1; if (av_read_frame(ffd->ctx, &pkt) <0) break; if (pkt.pts == AV_NOPTS_VALUE) pkt.pts = pkt.dts; if (pkt.stream_index==ffd->audio_st) last_aud_pts = pkt.pts; } if (last_aud_pts*ffd->audio_tscale.den<10*ffd->audio_tscale.num) ffd->unreliable_audio_timing = 1; } /*build seek*/ ffd->seekable = (av_seek_frame(ffd->ctx, -1, 0, AVSEEK_FLAG_BACKWARD)<0) ? 0 : 1; if (!ffd->seekable) { av_close_input_file(ffd->ctx); av_open_input_file(&ffd->ctx, szName, av_in, 0, NULL); av_find_stream_info(ffd->ctx); } /*let's go*/ gf_term_on_connect(serv, NULL, GF_OK); if (!ffd->service_type) FFD_SetupObjects(ffd); ffd->service_type = 0; return GF_OK;err_exit: if (ffd->ctx) av_close_input_file(ffd->ctx); ffd->ctx = NULL; gf_term_on_connect(serv, NULL, e); return GF_OK;}static GF_Descriptor *FFD_GetServiceDesc(GF_InputService *plug, u32 expect_type, const char *sub_url){ GF_ObjectDescriptor *od; GF_ESD *esd; FFDemux *ffd = plug->priv; if (!ffd->ctx) return NULL; if (expect_type==GF_MEDIA_OBJECT_UNDEF) { if (ffd->video_st>=0) expect_type=GF_MEDIA_OBJECT_VIDEO; else if (ffd->audio_st>=0) expect_type=GF_MEDIA_OBJECT_AUDIO; } /*since we don't handle multitrack in ffmpeg, we don't need to check sub_url, only use expected type*/ if (expect_type==GF_MEDIA_OBJECT_AUDIO) { od = (GF_ObjectDescriptor *) gf_odf_desc_new(GF_ODF_OD_TAG); od->objectDescriptorID = 1; esd = FFD_GetESDescriptor(ffd, 1); /*if session join, setup sync*/ if (ffd->video_ch) esd->OCRESID = ffd->video_st+1; gf_list_add(od->ESDescriptors, esd); ffd->service_type = 2; return (GF_Descriptor *) od; } if (expect_type==GF_MEDIA_OBJECT_VIDEO) { od = (GF_ObjectDescriptor *) gf_odf_desc_new(GF_ODF_OD_TAG); od->objectDescriptorID = 1; esd = FFD_GetESDescriptor(ffd, 0); /*if session join, setup sync*/ if (ffd->audio_ch) esd->OCRESID = ffd->audio_st+1; gf_list_add(od->ESDescriptors, esd); ffd->service_type = 1; return (GF_Descriptor *) od; } return NULL;}static GF_Err FFD_CloseService(GF_InputService *plug){ FFDemux *ffd = plug->priv; ffd->is_running = 0; if (ffd->ctx) av_close_input_file(ffd->ctx); ffd->ctx = NULL; ffd->audio_ch = ffd->video_ch = NULL; ffd->audio_run = ffd->video_run = 0; gf_term_on_disconnect(ffd->service, NULL, GF_OK); return GF_OK;}static GF_Err FFD_ConnectChannel(GF_InputService *plug, LPNETCHANNEL channel, const char *url, Bool upstream){ GF_Err e; u32 ESID; FFDemux *ffd = plug->priv; e = GF_STREAM_NOT_FOUND; if (upstream) { e = GF_ISOM_INVALID_FILE; goto exit; } if (!strstr(url, "ES_ID=")) { e = GF_NOT_SUPPORTED; goto exit; } sscanf(url, "ES_ID=%d", &ESID); if ((s32) ESID == 1 + ffd->audio_st) { if (ffd->audio_ch) { e = GF_SERVICE_ERROR; goto exit; } ffd->audio_ch = channel; e = GF_OK; } else if ((s32) ESID == 1 + ffd->video_st) { if (ffd->video_ch) { e = GF_SERVICE_ERROR; goto exit; } ffd->video_ch = channel; e = GF_OK; }exit: gf_term_on_connect(ffd->service, channel, e); return GF_OK;}static GF_Err FFD_DisconnectChannel(GF_InputService *plug, LPNETCHANNEL channel){ GF_Err e; FFDemux *ffd = plug->priv; e = GF_STREAM_NOT_FOUND; if (ffd->audio_ch == channel) { e = GF_OK; ffd->audio_ch = NULL; ffd->audio_run = 0; } else if (ffd->video_ch == channel) { e = GF_OK; ffd->video_ch = NULL; ffd->video_run = 0; } gf_term_on_disconnect(ffd->service, channel, e); return GF_OK;}static GF_Err FFD_ServiceCommand(GF_InputService *plug, GF_NetworkCommand *com){ FFDemux *ffd = plug->priv; if (!com->base.on_channel) return GF_NOT_SUPPORTED; switch (com->command_type) { /*only BIFS/OD work in pull mode (cf ffmpeg_in.h)*/ case GF_NET_CHAN_SET_PULL: return GF_NOT_SUPPORTED; case GF_NET_CHAN_INTERACTIVE: return ffd->seekable ? GF_OK : GF_NOT_SUPPORTED; case GF_NET_CHAN_BUFFER: com->buffer.max = com->buffer.min = 0; return GF_OK; case GF_NET_CHAN_DURATION: if (ffd->ctx->duration == AV_NOPTS_VALUE) com->duration.duration = -1; else com->duration.duration = (Double) ffd->ctx->duration / AV_TIME_BASE; return GF_OK; /*fetch start time*/ case GF_NET_CHAN_PLAY: if (com->play.speed<0) return GF_NOT_SUPPORTED; gf_mx_p(ffd->mx); ffd->seek_time = (com->play.start_range>=0) ? com->play.start_range : 0; if (ffd->audio_ch==com->base.on_channel) ffd->audio_run = 1; else if (ffd->video_ch==com->base.on_channel) ffd->video_run = 1; /*play on media stream, start thread*/ if ((ffd->audio_ch==com->base.on_channel) || (ffd->video_ch==com->base.on_channel)) { if (!ffd->is_running) { ffd->is_running = 1; gf_th_run(ffd->thread, FFDemux_Run, ffd); } } gf_mx_v(ffd->mx); return GF_OK; case GF_NET_CHAN_STOP: if (ffd->audio_ch==com->base.on_channel) ffd->audio_run = 0; else if (ffd->video_ch==com->base.on_channel) ffd->video_run = 0; return GF_OK; /*note we don't handle PAUSE/RESUME/SET_SPEED, this is automatically handled by the demuxing thread through buffer occupancy queries*/ default: return GF_OK; } return GF_OK;}static Bool FFD_CanHandleURLInService(GF_InputService *plug, const char *url){ char szURL[2048], *sep; FFDemux *ffd = (FFDemux *)plug->priv; const char *this_url = gf_term_get_service_url(ffd->service); if (!this_url || !url) return 0; strcpy(szURL, this_url); sep = strrchr(szURL, '#'); if (sep) sep[0] = 0; if ((url[0] != '#') && strnicmp(szURL, url, sizeof(char)*strlen(szURL))) return 0; sep = strrchr(url, '#'); if (!stricmp(sep, "#video") && (ffd->video_st>=0)) return 1; if (!stricmp(sep, "#audio") && (ffd->audio_st>=0)) return 1; return 0;}void *New_FFMPEG_Demux() { FFDemux *priv; GF_InputService *ffd = malloc(sizeof(GF_InputService)); memset(ffd, 0, sizeof(GF_InputService)); priv = malloc(sizeof(FFDemux)); memset(priv, 0, sizeof(FFDemux)); /* register all codecs, demux and protocols */ av_register_all(); ffd->CanHandleURL = FFD_CanHandleURL; ffd->CloseService = FFD_CloseService; ffd->ConnectChannel = FFD_ConnectChannel; ffd->ConnectService = FFD_ConnectService; ffd->DisconnectChannel = FFD_DisconnectChannel; ffd->GetServiceDescriptor = FFD_GetServiceDesc; ffd->ServiceCommand = FFD_ServiceCommand; ffd->CanHandleURLInService = FFD_CanHandleURLInService; priv->thread = gf_th_new(); priv->mx = gf_mx_new(); GF_REGISTER_MODULE_INTERFACE(ffd, GF_NET_CLIENT_INTERFACE, "FFMPEG Demuxer", "gpac distribution"); ffd->priv = priv; return ffd;}void Delete_FFMPEG_Demux(void *ifce){ FFDemux *ffd; GF_InputService *ptr = (GF_InputService *)ifce; ffd = ptr->priv; gf_th_del(ffd->thread); gf_mx_del(ffd->mx); free(ffd); free(ptr);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -