| /* | |
| * GPAC - Multimedia Framework C SDK | |
| * | |
| * Authors: Jean Le Feuvre | |
| * Copyright (c) Telecom ParisTech 2000-2012 | |
| * All rights reserved | |
| * | |
| * This file is part of GPAC / FFMPEG module | |
| * | |
| * GPAC is free software; you can redistribute it and/or modify | |
| * it under the terms of the GNU Lesser General Public License as published by | |
| * the Free Software Foundation; either version 2, or (at your option) | |
| * any later version. | |
| * | |
| * GPAC is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| * GNU Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; see the file COPYING. If not, write to | |
| * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | |
| * | |
| */ | |
| #include "ffmpeg_in.h" | |
| #include <gpac/avparse.h> | |
| #include <libavcodec/avcodec.h> | |
| #if ((LIBAVCODEC_VERSION_MAJOR == 55) && (LIBAVCODEC_VERSION_MINOR >= 38)) || (LIBAVCODEC_VERSION_MAJOR > 55) | |
| #define HAS_HEVC | |
| #include <libavutil/opt.h> | |
| #endif | |
| #if defined(GPAC_CONFIG_ANDROID) || defined(GPAC_CONFIG_IOS) | |
| #define NO_10bit | |
| #endif | |
| //#define FFMPEG_DIRECT_DISPATCH | |
| #ifndef AV_INPUT_BUFFER_PADDING_SIZE | |
| #define AV_INPUT_BUFFER_PADDING_SIZE 0 | |
| #endif | |
| /** | |
| * Allocates data for FFMPEG decoding | |
| * \param oldBuffer The oldBuffer (freed if not NULL) | |
| * \param size Size to allocate (will use extra padding for real size) | |
| * \return The newly allocated buffer | |
| */ | |
| static uint8_t * ffmpeg_realloc_buffer(uint8_t * oldBuffer, u32 size) { | |
| uint8_t * buffer; | |
| /* Size of buffer must be larger, see avcodec_decode_video2 documentation */ | |
| u32 allocatedSz = sizeof( char ) * (AV_INPUT_BUFFER_PADDING_SIZE + size); | |
| if (oldBuffer) | |
| gf_free(oldBuffer); | |
| buffer = (uint8_t*)gf_malloc( allocatedSz ); | |
| if (buffer) | |
| memset( buffer, 0, allocatedSz); | |
| return buffer; | |
| } | |
| static AVCodec *ffmpeg_get_codec(u32 codec_4cc) | |
| { | |
| char name[5]; | |
| AVCodec *codec; | |
| strcpy(name, gf_4cc_to_str(codec_4cc)); | |
| codec = avcodec_find_decoder(codec_4cc); | |
| if (codec) return codec; | |
| codec = avcodec_find_decoder_by_name(name); | |
| if (codec) return codec; | |
| strupr(name); | |
| codec = avcodec_find_decoder_by_name(name); | |
| if (codec) return codec; | |
| strlwr(name); | |
| codec = avcodec_find_decoder_by_name(name); | |
| if (codec) return codec; | |
| /*custom mapings*/ | |
| if (!stricmp(name, "s263")) codec = avcodec_find_decoder(CODEC_ID_H263); | |
| else if (!stricmp(name, "mjp2")) { | |
| codec = avcodec_find_decoder_by_name("jpeg2000"); | |
| if (!codec) codec = avcodec_find_decoder_by_name("libopenjpeg"); | |
| } | |
| else if (!stricmp(name, "samr") || !stricmp(name, "amr ")) codec = avcodec_find_decoder(CODEC_ID_AMR_NB); | |
| else if (!stricmp(name, "sawb")) codec = avcodec_find_decoder(CODEC_ID_AMR_WB); | |
| return codec; | |
| } | |
| static void FFDEC_LoadDSI(FFDec *ffd, GF_BitStream *bs, AVCodec *codec, AVCodecContext *ctx, Bool from_ff_demux) | |
| { | |
| u32 dsi_size; | |
| if (!codec) return; | |
| dsi_size = (u32) gf_bs_available(bs); | |
| if (!dsi_size) return; | |
| /*demuxer is ffmpeg, extra data can be copied directly*/ | |
| if (from_ff_demux) { | |
| if (ctx->extradata) | |
| gf_free(ctx->extradata); | |
| ctx->extradata_size = dsi_size; | |
| ctx->extradata = ffmpeg_realloc_buffer(ctx->extradata, ctx->extradata_size); | |
| gf_bs_read_data(bs, (char *) ctx->extradata, ctx->extradata_size); | |
| return; | |
| } | |
| switch (codec->id) { | |
| case CODEC_ID_SVQ3: | |
| { | |
| u32 at_type, size; | |
| size = gf_bs_read_u32(bs); | |
| /*there should be an 'SMI' entry*/ | |
| at_type = gf_bs_read_u32(bs); | |
| if (at_type == GF_4CC('S', 'M', 'I', ' ')) { | |
| if (ctx->extradata) | |
| gf_free(ctx->extradata); | |
| ctx->extradata_size = 0x5a + size; | |
| ctx->extradata = ffmpeg_realloc_buffer(ctx->extradata, ctx->extradata_size); | |
| strcpy((char *) ctx->extradata, "SVQ3"); | |
| gf_bs_read_data(bs, (char *)ctx->extradata + 0x5a, size); | |
| } | |
| } | |
| break; | |
| default: | |
| if (ctx->extradata) | |
| gf_free(ctx->extradata); | |
| ctx->extradata_size = dsi_size; | |
| ctx->extradata = ffmpeg_realloc_buffer(ctx->extradata, ctx->extradata_size); | |
| gf_bs_read_data(bs, (char *)ctx->extradata, ctx->extradata_size); | |
| break; | |
| } | |
| } | |
| static GF_Err FFDEC_AttachStream(GF_BaseDecoder *plug, GF_ESD *esd) | |
| { | |
| u32 codec_id = 0; | |
| int gotpic; | |
| GF_BitStream *bs; | |
| AVCodecContext **ctx; | |
| AVCodec **codec; | |
| AVFrame **frame; | |
| #ifndef GPAC_DISABLE_AV_PARSERS | |
| GF_M4VDecSpecInfo dsi; | |
| GF_Err e; | |
| #endif | |
| FFDec *ffd = (FFDec *)plug->privateStack; | |
| if (esd->decoderConfig->upstream) return GF_NOT_SUPPORTED; | |
| if (!ffd->oti) return GF_NOT_SUPPORTED; | |
| /*locate any auxiliary video data descriptor on this stream*/ | |
| if (esd->dependsOnESID) { | |
| u32 i = 0; | |
| GF_Descriptor *d = NULL; | |
| if (esd->dependsOnESID != ffd->base_ES_ID) return GF_NOT_SUPPORTED; | |
| while ((d = (GF_Descriptor*)gf_list_enum(esd->extensionDescriptors, &i))) { | |
| if (d->tag == GF_ODF_AUX_VIDEO_DATA) break; | |
| } | |
| if (!d) return GF_NOT_SUPPORTED; | |
| ffd->depth_ES_ID = esd->ESID; | |
| ctx = &ffd->depth_ctx; | |
| codec = &ffd->depth_codec; | |
| frame = &ffd->depth_frame; | |
| } else { | |
| if (ffd->base_ES_ID) return GF_NOT_SUPPORTED; | |
| ffd->base_ES_ID = esd->ESID; | |
| ctx = &ffd->base_ctx; | |
| codec = &ffd->base_codec; | |
| frame = &ffd->base_frame; | |
| } | |
| if (!(*ctx)) { | |
| #ifdef USE_AVCTX3 | |
| *ctx = avcodec_alloc_context3(NULL); | |
| #else | |
| *ctx = avcodec_alloc_context(); | |
| #endif | |
| } | |
| /*private FFMPEG DSI*/ | |
| if (ffd->oti == GPAC_OTI_MEDIA_FFMPEG) { | |
| bs = gf_bs_new(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, GF_BITSTREAM_READ); | |
| codec_id = gf_bs_read_u32(bs); | |
| if (ffd->st==GF_STREAM_AUDIO) { | |
| (*ctx)->codec_type = AVMEDIA_TYPE_AUDIO; | |
| (*ctx)->sample_rate = gf_bs_read_u32(bs); | |
| (*ctx)->channels = gf_bs_read_u16(bs); | |
| (*ctx)->frame_size = gf_bs_read_u16(bs); | |
| /*bits_per_sample */gf_bs_read_u8(bs); | |
| /*num_frames_per_au*/ gf_bs_read_u8(bs); | |
| /*ffmpeg specific*/ | |
| (*ctx)->block_align = gf_bs_read_u16(bs); | |
| (*ctx)->bit_rate = gf_bs_read_u32(bs); | |
| (*ctx)->codec_tag = gf_bs_read_u32(bs); | |
| } else if (ffd->st==GF_STREAM_VISUAL) { | |
| (*ctx)->codec_type = AVMEDIA_TYPE_VIDEO; | |
| (*ctx)->width = gf_bs_read_u16(bs); | |
| (*ctx)->height = gf_bs_read_u16(bs); | |
| (*ctx)->bit_rate = gf_bs_read_u32(bs); | |
| (*ctx)->codec_tag = gf_bs_read_u32(bs); | |
| ffd->raw_pix_fmt = gf_bs_read_u32(bs); | |
| } | |
| *codec = avcodec_find_decoder(codec_id); | |
| FFDEC_LoadDSI(ffd, bs, *codec, *ctx, GF_TRUE); | |
| gf_bs_del(bs); | |
| } | |
| /*private QT DSI*/ | |
| else if (ffd->oti == GPAC_OTI_MEDIA_GENERIC) { | |
| bs = gf_bs_new(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, GF_BITSTREAM_READ); | |
| codec_id = gf_bs_read_u32(bs); | |
| if (ffd->st==GF_STREAM_AUDIO) { | |
| (*ctx)->codec_type = AVMEDIA_TYPE_AUDIO; | |
| (*ctx)->sample_rate = gf_bs_read_u32(bs); | |
| (*ctx)->channels = gf_bs_read_u16(bs); | |
| (*ctx)->frame_size = gf_bs_read_u16(bs); | |
| /*bits_per_sample */ gf_bs_read_u8(bs); | |
| /*num_frames_per_au*/ gf_bs_read_u8(bs); | |
| /*just in case...*/ | |
| if (codec_id == GF_4CC('a', 'm', 'r', ' ')) { | |
| (*ctx)->sample_rate = 8000; | |
| (*ctx)->channels = 1; | |
| (*ctx)->frame_size = 160; | |
| } | |
| } else if (ffd->st==GF_STREAM_VISUAL) { | |
| (*ctx)->codec_type = AVMEDIA_TYPE_VIDEO; | |
| (*ctx)->width = gf_bs_read_u16(bs); | |
| (*ctx)->height = gf_bs_read_u16(bs); | |
| } | |
| (*codec) = ffmpeg_get_codec(codec_id); | |
| codec_id = (*codec)->id; | |
| FFDEC_LoadDSI(ffd, bs, *codec, *ctx, GF_FALSE); | |
| gf_bs_del(bs); | |
| } | |
| /*use std MPEG-4 st/oti*/ | |
| else { | |
| u32 codec_id = 0; | |
| if (ffd->st==GF_STREAM_VISUAL) { | |
| (*ctx)->codec_type = AVMEDIA_TYPE_VIDEO; | |
| switch (ffd->oti) { | |
| case GPAC_OTI_VIDEO_MPEG4_PART2: | |
| codec_id = CODEC_ID_MPEG4; | |
| break; | |
| case GPAC_OTI_VIDEO_AVC: | |
| codec_id = CODEC_ID_H264; | |
| break; | |
| #ifdef HAS_HEVC | |
| case GPAC_OTI_VIDEO_HEVC: | |
| codec_id = AV_CODEC_ID_HEVC; | |
| break; | |
| #endif | |
| case GPAC_OTI_VIDEO_MPEG1: | |
| case GPAC_OTI_VIDEO_MPEG2_SIMPLE: | |
| case GPAC_OTI_VIDEO_MPEG2_MAIN: | |
| case GPAC_OTI_VIDEO_MPEG2_SNR: | |
| case GPAC_OTI_VIDEO_MPEG2_SPATIAL: | |
| case GPAC_OTI_VIDEO_MPEG2_HIGH: | |
| case GPAC_OTI_VIDEO_MPEG2_422: | |
| codec_id = CODEC_ID_MPEG2VIDEO; | |
| break; | |
| case GPAC_OTI_IMAGE_JPEG: | |
| codec_id = CODEC_ID_MJPEG; | |
| ffd->is_image = GF_TRUE; | |
| break; | |
| case GPAC_OTI_IMAGE_PNG: | |
| codec_id = CODEC_ID_PNG; | |
| ffd->is_image = GF_TRUE; | |
| break; | |
| #ifdef CODEC_ID_VP9 | |
| case GPAC_OTI_VIDEO_VP9: | |
| codec_id = CODEC_ID_VP9; | |
| break; | |
| #endif | |
| #ifdef CODEC_ID_AV1 | |
| case GPAC_OTI_VIDEO_AV1: | |
| codec_id = CODEC_ID_AV1; | |
| break; | |
| #endif | |
| case 0xFF: | |
| codec_id = CODEC_ID_SVQ3; | |
| break; | |
| } | |
| } else if (ffd->st==GF_STREAM_AUDIO) { | |
| (*ctx)->codec_type = AVMEDIA_TYPE_AUDIO; | |
| switch (ffd->oti) { | |
| case GPAC_OTI_AUDIO_MPEG2_PART3: | |
| case GPAC_OTI_AUDIO_MPEG1: | |
| (*ctx)->frame_size = 1152; | |
| codec_id = CODEC_ID_MP2; | |
| break; | |
| case GPAC_OTI_AUDIO_AC3: | |
| codec_id = CODEC_ID_AC3; | |
| break; | |
| case GPAC_OTI_AUDIO_EAC3: | |
| codec_id = CODEC_ID_EAC3; | |
| break; | |
| case GPAC_OTI_AUDIO_AAC_MPEG2_LCP: | |
| case GPAC_OTI_AUDIO_AAC_MPEG2_MP: | |
| case GPAC_OTI_AUDIO_AAC_MPEG2_SSRP: | |
| case GPAC_OTI_AUDIO_AAC_MPEG4: | |
| codec_id = CODEC_ID_AAC; | |
| break; | |
| #ifdef CODEC_ID_OPUS | |
| case GPAC_OTI_MEDIA_OPUS: | |
| codec_id = CODEC_ID_OPUS; | |
| (*ctx)->frame_size = 2880;//use max frame size | |
| break; | |
| #endif | |
| } | |
| } | |
| else if ((ffd->st==GF_STREAM_ND_SUBPIC) && (ffd->oti==0xe0)) { | |
| codec_id = CODEC_ID_DVD_SUBTITLE; | |
| } | |
| *codec = avcodec_find_decoder(codec_id); | |
| if (*codec) { | |
| switch (codec_id) { | |
| case CODEC_ID_AAC: | |
| #ifdef CODEC_ID_OPUS | |
| case CODEC_ID_OPUS: | |
| bs = gf_bs_new(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, GF_BITSTREAM_READ); | |
| FFDEC_LoadDSI(ffd, bs, *codec, *ctx, GF_FALSE); | |
| gf_bs_del(bs); | |
| #endif | |
| break; | |
| } | |
| } | |
| } | |
| /*should never happen*/ | |
| if (! (*codec)) return GF_OUT_OF_MEM; | |
| /*not sure this is the right way to do so, no doc in ffmpeg on this topic */ | |
| #if 0 | |
| /*check HW accel*/ | |
| avcodec_get_context_defaults3(*ctx, *codec); | |
| if (*codec) { | |
| AVHWAccel *hwaccel=NULL; | |
| while ((hwaccel = av_hwaccel_next(hwaccel))){ | |
| if (hwaccel->id == (*codec)->id) { | |
| (*ctx)->hwaccel_context = hwaccel; | |
| (*ctx)->pix_fmt = hwaccel->pix_fmt; | |
| break; | |
| } | |
| } | |
| } | |
| #endif | |
| /*setup MPEG-4 video streams*/ | |
| if (ffd->st==GF_STREAM_VISUAL) { | |
| /*for all MPEG-4 variants get size*/ | |
| if ((ffd->oti==GPAC_OTI_VIDEO_MPEG4_PART2) | |
| || (ffd->oti == GPAC_OTI_VIDEO_AVC) | |
| || (ffd->oti == GPAC_OTI_VIDEO_HEVC) | |
| || (ffd->oti == GPAC_OTI_VIDEO_AV1) | |
| ) { | |
| /*if not set this may be a remap of non-mpeg4 transport (eg, transport on MPEG-TS) where | |
| the DSI is carried in-band*/ | |
| if (esd->decoderConfig->decoderSpecificInfo && esd->decoderConfig->decoderSpecificInfo->data) { | |
| /*for regular MPEG-4, try to decode and if this fails try H263 decoder at first frame*/ | |
| if (ffd->oti==GPAC_OTI_VIDEO_MPEG4_PART2) { | |
| #ifndef GPAC_DISABLE_AV_PARSERS | |
| e = gf_m4v_get_config(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, &dsi); | |
| if (e) return e; | |
| if (dsi.width%2) dsi.width++; | |
| if (dsi.height%2) dsi.height++; | |
| (*ctx)->width = dsi.width; | |
| (*ctx)->height = dsi.height; | |
| if (!dsi.width && !dsi.height) ffd->check_short_header = GF_TRUE; | |
| ffd->previous_par = (dsi.par_num<<16) | dsi.par_den; | |
| ffd->no_par_update = GF_TRUE; | |
| #endif | |
| } else if (ffd->oti==GPAC_OTI_VIDEO_AVC) { | |
| ffd->check_h264_isma = GF_TRUE; | |
| } | |
| /*setup dsi for FFMPEG context BEFORE attaching decoder (otherwise not proper init)*/ | |
| (*ctx)->extradata = ffmpeg_realloc_buffer((*ctx)->extradata, esd->decoderConfig->decoderSpecificInfo->dataLength + 8); | |
| if ((*ctx)->extradata) { | |
| memcpy((*ctx)->extradata, esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength); | |
| (*ctx)->extradata_size = esd->decoderConfig->decoderSpecificInfo->dataLength; | |
| } else { | |
| /* out of mem ? */ | |
| (*ctx)->extradata_size = 0; | |
| } | |
| } | |
| } | |
| #if !defined(FF_API_AVFRAME_LAVC) | |
| *frame = avcodec_alloc_frame(); | |
| #else | |
| *frame = av_frame_alloc(); | |
| #endif | |
| } | |
| #ifdef HAS_HEVC | |
| if (ffd->oti == GPAC_OTI_VIDEO_HEVC) { | |
| GF_SystemRTInfo rti; | |
| u32 nb_threads, detected_nb_threads = 1; | |
| const char *sOpt; | |
| sOpt = gf_opts_get_key("OpenHEVC", "ThreadingType"); | |
| if (sOpt && !strcmp(sOpt, "wpp")) av_opt_set(*ctx, "thread_type", "slice", 0); | |
| else if (sOpt && !strcmp(sOpt, "frame+wpp")) av_opt_set(*ctx, "thread_type", "frameslice", 0); | |
| else { | |
| av_opt_set(*ctx, "thread_type", "frame", 0); | |
| if (!sOpt) gf_opts_set_key("OpenHEVC", "ThreadingType", "frame"); | |
| } | |
| if (gf_sys_get_rti(0, &rti, 0) ) { | |
| detected_nb_threads = (rti.nb_cores>1) ? rti.nb_cores-1 : 1; | |
| } | |
| sOpt = gf_opts_get_key("OpenHEVC", "NumThreads"); | |
| if (!sOpt) { | |
| char szO[100]; | |
| sprintf(szO, "%d", detected_nb_threads); | |
| gf_opts_set_key("OpenHEVC", "NumThreads", szO); | |
| nb_threads = detected_nb_threads; | |
| } else { | |
| nb_threads = atoi(sOpt); | |
| } | |
| if (nb_threads > detected_nb_threads) { | |
| GF_LOG(GF_LOG_WARNING, GF_LOG_CODEC, ("[HEVC@ffmpeg] Initializing with %d threads but only %d available cores detected on the system\n", nb_threads, rti.nb_cores)); | |
| } else { | |
| GF_LOG(GF_LOG_INFO, GF_LOG_CODEC, ("[HEVC@ffmpeg] Initializing with %d threads\n", nb_threads)); | |
| } | |
| fprintf(stderr, "[HEVC@ffmpeg] Initializing with %d threads\n", nb_threads); | |
| av_opt_set_int(*ctx, "threads", nb_threads, 0); | |
| /* Set the decoder id */ | |
| //av_opt_set_int(openHevcContext->c->priv_data, "decoder-id", i, 0); | |
| sOpt = gf_opts_get_key("OpenHEVC", "CBUnits"); | |
| if (!sOpt) gf_opts_set_key("OpenHEVC", "CBUnits", "4"); | |
| if (sOpt) ffd->output_cb_size = atoi(sOpt); | |
| } | |
| #endif //HAS_HEVC | |
| if (!ffd->output_cb_size) ffd->output_cb_size = 4; | |
| if (codec_id == CODEC_ID_RAWVIDEO) { | |
| (*ctx)->codec_id = CODEC_ID_RAWVIDEO; | |
| (*ctx)->pix_fmt = ffd->raw_pix_fmt; | |
| if ((*ctx)->extradata && strstr((char *) (*ctx)->extradata, "BottomUp")) ffd->flipped = GF_TRUE; | |
| } else { | |
| #ifdef USE_AVCTX3 | |
| if (avcodec_open2((*ctx), (*codec), NULL )<0) return GF_NON_COMPLIANT_BITSTREAM; | |
| #else | |
| if (avcodec_open((*ctx), (*codec) )<0) return GF_NON_COMPLIANT_BITSTREAM; | |
| #endif | |
| } | |
| /*setup audio streams*/ | |
| if (ffd->st==GF_STREAM_AUDIO) { | |
| if ((*codec)->id == CODEC_ID_MP2) { | |
| (*ctx)->frame_size = ((*ctx)->sample_rate > 24000) ? 1152 : 576; | |
| } | |
| /*may be 0 (cfg not known yet)*/ | |
| ffd->out_size = (*ctx)->channels * (*ctx)->frame_size * 2 /*16 / 8*/; | |
| if (!(*ctx)->sample_rate) (*ctx)->sample_rate = 44100; | |
| if (!(*ctx)->channels) (*ctx)->channels = 2; | |
| #if defined(USE_AVCTX3) | |
| #if !defined(FF_API_AVFRAME_LAVC) | |
| ffd->audio_frame = avcodec_alloc_frame(); | |
| #else | |
| ffd->audio_frame = av_frame_alloc(); | |
| #endif | |
| #endif | |
| } else { | |
| switch ((*codec)->id) { | |
| case CODEC_ID_MJPEG: | |
| case CODEC_ID_MJPEGB: | |
| case CODEC_ID_LJPEG: | |
| #if (LIBAVCODEC_VERSION_INT > AV_VERSION_INT(51, 20, 0)) | |
| case CODEC_ID_GIF: | |
| #endif | |
| case CODEC_ID_PNG: | |
| case CODEC_ID_RAWVIDEO: | |
| if ((*ctx)->pix_fmt==PIX_FMT_YUV420P) { | |
| ffd->pix_fmt = GF_PIXEL_YV12; | |
| } else { | |
| ffd->pix_fmt = GF_PIXEL_RGB_24; | |
| } | |
| break; | |
| case CODEC_ID_DVD_SUBTITLE: | |
| #if !defined(FF_API_AVFRAME_LAVC) | |
| *frame = avcodec_alloc_frame(); | |
| #else | |
| *frame = av_frame_alloc(); | |
| #endif | |
| #ifdef USE_AVCODEC2 | |
| { | |
| AVPacket pkt; | |
| av_init_packet(&pkt); | |
| pkt.data = (uint8_t *) esd->decoderConfig->decoderSpecificInfo->data; | |
| pkt.size = esd->decoderConfig->decoderSpecificInfo->dataLength; | |
| avcodec_decode_video2((*ctx), *frame, &gotpic, &pkt); | |
| } | |
| #else | |
| avcodec_decode_video((*ctx), *frame, &gotpic, esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength); | |
| #endif | |
| ffd->pix_fmt = GF_PIXEL_YV12; | |
| break; | |
| default: | |
| ffd->pix_fmt = GF_PIXEL_YV12; | |
| break; | |
| } | |
| ffd->out_pix_fmt = ffd->pix_fmt; | |
| if (ffd->out_pix_fmt == GF_PIXEL_YV12) { | |
| ffd->stride = (*ctx)->width; | |
| if (ffd->depth_codec) { | |
| ffd->out_size = (*ctx)->width * (*ctx)->height * 5 / 2; | |
| ffd->out_pix_fmt = GF_PIXEL_YUVD; | |
| ffd->yuv_size = (*ctx)->width * (*ctx)->height * 3 / 2; | |
| } else { | |
| ffd->out_size = (*ctx)->width * (*ctx)->height * 3 / 2; | |
| } | |
| } else { | |
| ffd->out_size = (*ctx)->width * (*ctx)->height * 3; | |
| } | |
| } | |
| return GF_OK; | |
| } | |
| static GF_Err FFDEC_DetachStream(GF_BaseDecoder *plug, u16 ES_ID) | |
| { | |
| AVCodecContext **ctx; | |
| AVCodec **codec; | |
| #ifdef FFMPEG_SWSCALE | |
| struct SwsContext **sws; | |
| #endif | |
| FFDec *ffd = (FFDec *)plug->privateStack; | |
| if (ffd->base_ES_ID==ES_ID) { | |
| ffd->base_ES_ID = 0; | |
| codec = &ffd->base_codec; | |
| ctx = &ffd->base_ctx; | |
| #ifdef FFMPEG_SWSCALE | |
| sws = &ffd->base_sws; | |
| #endif | |
| } else if (ffd->depth_ES_ID==ES_ID) { | |
| ffd->depth_ES_ID = 0; | |
| codec = &ffd->depth_codec; | |
| ctx = &ffd->depth_ctx; | |
| #ifdef FFMPEG_SWSCALE | |
| sws = &ffd->depth_sws; | |
| #endif | |
| } else { | |
| return GF_OK; | |
| } | |
| if (*ctx) { | |
| if ((*ctx)->extradata) gf_free((*ctx)->extradata); | |
| (*ctx)->extradata = NULL; | |
| if ((*ctx)->codec) avcodec_close((*ctx)); | |
| *ctx = NULL; | |
| } | |
| *codec = NULL; | |
| #if defined(USE_AVCTX3) | |
| if (ffd->audio_frame) { | |
| av_free(ffd->audio_frame); | |
| } | |
| if (ffd->base_frame) { | |
| av_free(ffd->base_frame); | |
| } | |
| if (ffd->depth_frame) { | |
| av_free(ffd->depth_frame); | |
| } | |
| #endif | |
| #ifdef FFMPEG_SWSCALE | |
| if (*sws) { | |
| sws_freeContext(*sws); | |
| *sws = NULL; | |
| } | |
| #endif | |
| return GF_OK; | |
| } | |
| static GF_Err FFDEC_GetCapabilities(GF_BaseDecoder *plug, GF_CodecCapability *capability) | |
| { | |
| FFDec *ffd = (FFDec *)plug->privateStack; | |
| /*base caps*/ | |
| switch (capability->CapCode) { | |
| /*ffmpeg seems quite reliable*/ | |
| case GF_CODEC_RESILIENT: | |
| capability->cap.valueInt = 1; | |
| return GF_OK; | |
| case GF_CODEC_PADDING_BYTES: | |
| capability->cap.valueInt = AV_INPUT_BUFFER_PADDING_SIZE; | |
| return GF_OK; | |
| case GF_CODEC_REORDER: | |
| capability->cap.valueInt = 1; | |
| return GF_OK; | |
| case GF_CODEC_RAW_MEMORY: | |
| capability->cap.valueBool = GF_TRUE; | |
| return GF_OK; | |
| case GF_CODEC_FRAME_OUTPUT: | |
| #if defined(USE_AVCTX3) && defined(FFMPEG_DIRECT_DISPATCH) | |
| //deactivated by default until we have more tests (stride, color formats) | |
| capability->cap.valueBool = GF_FALSE; | |
| #endif | |
| return GF_OK; | |
| case GF_CODEC_WANTS_THREAD: | |
| capability->cap.valueBool= GF_TRUE; | |
| break; | |
| } | |
| if (!ffd->base_ctx) { | |
| capability->cap.valueInt = 0; | |
| return GF_OK; | |
| } | |
| /*caps valid only if stream attached*/ | |
| switch (capability->CapCode) { | |
| case GF_CODEC_OUTPUT_SIZE: | |
| capability->cap.valueInt = ffd->out_size; | |
| break; | |
| case GF_CODEC_SAMPLERATE: | |
| capability->cap.valueInt = ffd->base_ctx->sample_rate; | |
| break; | |
| case GF_CODEC_NB_CHAN: | |
| capability->cap.valueInt = ffd->base_ctx->channels; | |
| break; | |
| case GF_CODEC_BITS_PER_SAMPLE: | |
| capability->cap.valueInt = 16; | |
| break; | |
| case GF_CODEC_BUFFER_MIN: | |
| capability->cap.valueInt = (ffd->st==GF_STREAM_AUDIO) ? 4 : 1; | |
| break; | |
| case GF_CODEC_BUFFER_MAX: | |
| /*for audio let the systems engine decide since we may have very large block size (1 sec with some QT movies)*/ | |
| capability->cap.valueInt = (ffd->st==GF_STREAM_AUDIO) ? 0 : (ffd->is_image ? 1 : ffd->output_cb_size); | |
| break; | |
| /*by default AAC access unit lasts num_samples (timescale being sampleRate)*/ | |
| case GF_CODEC_CU_DURATION: | |
| capability->cap.valueInt = (ffd->st==GF_STREAM_AUDIO) ? ffd->base_ctx->frame_size : 0; | |
| break; | |
| case GF_CODEC_WIDTH: | |
| capability->cap.valueInt = ffd->base_ctx->width; | |
| break; | |
| case GF_CODEC_HEIGHT: | |
| capability->cap.valueInt = ffd->base_ctx->height; | |
| break; | |
| case GF_CODEC_STRIDE: | |
| if (ffd->out_pix_fmt==GF_PIXEL_RGB_24) | |
| capability->cap.valueInt = ffd->stride*3; | |
| else if (ffd->out_pix_fmt==GF_PIXEL_RGBA) | |
| capability->cap.valueInt = ffd->stride*4; | |
| else | |
| capability->cap.valueInt = ffd->stride; | |
| break; | |
| case GF_CODEC_FPS: | |
| capability->cap.valueFloat = 30.0f; | |
| break; | |
| case GF_CODEC_PAR: | |
| capability->cap.valueInt = ffd->previous_par; | |
| break; | |
| case GF_CODEC_PIXEL_FORMAT: | |
| if (ffd->base_ctx->width) capability->cap.valueInt = ffd->out_pix_fmt; | |
| break; | |
| /*ffmpeg performs frame reordering internally*/ | |
| case GF_CODEC_REORDER: | |
| capability->cap.valueInt = 1; | |
| break; | |
| case GF_CODEC_TRUSTED_CTS: | |
| capability->cap.valueInt = 1; | |
| break; | |
| case GF_CODEC_WAIT_RAP: | |
| //ffd->ctx->hurry_up = 5; | |
| break; | |
| case GF_CODEC_CHANNEL_CONFIG: | |
| /*currently unused in ffmpeg*/ | |
| if (ffd->base_ctx->channels==1) { | |
| capability->cap.valueInt = GF_AUDIO_CH_FRONT_CENTER; | |
| } else { | |
| capability->cap.valueInt = GF_AUDIO_CH_FRONT_LEFT | GF_AUDIO_CH_FRONT_RIGHT; | |
| } | |
| break; | |
| case GF_CODEC_PADDING_BYTES: | |
| capability->cap.valueInt = AV_INPUT_BUFFER_PADDING_SIZE; | |
| break; | |
| default: | |
| capability->cap.valueInt = 0; | |
| break; | |
| } | |
| return GF_OK; | |
| } | |
| static GF_Err FFDEC_SetCapabilities(GF_BaseDecoder *plug, GF_CodecCapability capability) | |
| { | |
| FFDec *ffd = (FFDec *)plug->privateStack; | |
| assert(plug); | |
| assert( ffd ); | |
| switch (capability.CapCode) { | |
| case GF_CODEC_WAIT_RAP: | |
| ffd->frame_start = 0; | |
| if (ffd->st==GF_STREAM_VISUAL) { | |
| if (ffd->base_ctx && ffd->base_ctx->codec) avcodec_flush_buffers(ffd->base_ctx); | |
| if (ffd->depth_ctx && ffd->depth_ctx->codec) avcodec_flush_buffers(ffd->depth_ctx); | |
| } | |
| return GF_OK; | |
| case GF_CODEC_RAW_MEMORY: | |
| ffd->direct_output_mode = capability.cap.valueBool ? 1 : 0; | |
| return GF_OK; | |
| case GF_CODEC_FRAME_OUTPUT: | |
| ffd->direct_output_mode = capability.cap.valueInt ? 2 : 0; | |
| return GF_OK; | |
| default: | |
| /*return unsupported to avoid confusion by the player (like color space changing ...) */ | |
| return GF_NOT_SUPPORTED; | |
| } | |
| } | |
| static GF_Err FFDEC_GetOutputPixelFromat (u32 pix_fmt, u32 * out_pix_fmt, FFDec *ffd) | |
| { | |
| if (!out_pix_fmt || !ffd) return GF_BAD_PARAM; | |
| switch (pix_fmt) { | |
| case PIX_FMT_YUV420P: | |
| *out_pix_fmt = GF_PIXEL_YV12; | |
| break; | |
| case PIX_FMT_YUV420P10LE: | |
| *out_pix_fmt = GF_PIXEL_YV12_10; | |
| break; | |
| case PIX_FMT_YUV422P: | |
| *out_pix_fmt = GF_PIXEL_YUV422; | |
| break; | |
| case PIX_FMT_YUV422P10LE: | |
| *out_pix_fmt = GF_PIXEL_YUV422_10; | |
| break; | |
| case PIX_FMT_YUV444P: | |
| *out_pix_fmt = GF_PIXEL_YUV444; | |
| break; | |
| case PIX_FMT_YUV444P10LE: | |
| *out_pix_fmt = GF_PIXEL_YUV444_10; | |
| break; | |
| case PIX_FMT_RGBA: | |
| *out_pix_fmt = GF_PIXEL_RGBA; | |
| break; | |
| case PIX_FMT_RGB24: | |
| *out_pix_fmt = GF_PIXEL_RGB_24; | |
| break; | |
| case PIX_FMT_BGR24: | |
| *out_pix_fmt = GF_PIXEL_BGR_24; | |
| break; | |
| default: | |
| return GF_NOT_SUPPORTED; | |
| } | |
| return GF_OK; | |
| } | |
| static GF_Err FFDEC_ProcessAudio(FFDec *ffd, | |
| char *inBuffer, u32 inBufferLength, | |
| u16 ES_ID, u32 *CTS, | |
| char *outBuffer, u32 *outBufferLength, | |
| u8 PaddingBits, u32 mmlevel) | |
| { | |
| #ifdef USE_AVCODEC2 | |
| AVPacket pkt; | |
| #endif | |
| s32 gotpic; | |
| AVCodecContext *ctx = ffd->base_ctx; | |
| s32 len; | |
| u32 buf_size = (*outBufferLength); | |
| #ifdef USE_AVCODEC2 | |
| av_init_packet(&pkt); | |
| pkt.data = (uint8_t *)inBuffer; | |
| pkt.size = inBufferLength; | |
| #endif | |
| (*outBufferLength) = 0; | |
| /*seeking don't decode*/ | |
| if (!inBuffer || (mmlevel == GF_CODEC_LEVEL_SEEK)) { | |
| *outBufferLength = 0; | |
| ffd->frame_start = 0; | |
| return GF_OK; | |
| } | |
| if (ffd->frame_start>inBufferLength) ffd->frame_start = 0; | |
| //seek to last byte consumed by the previous decode4() | |
| else if (ffd->frame_start) { | |
| pkt.data += ffd->frame_start; | |
| pkt.size -= ffd->frame_start; | |
| } | |
| redecode: | |
| #if defined(USE_AVCTX3) | |
| len = avcodec_decode_audio4(ctx, ffd->audio_frame, &gotpic, &pkt); | |
| if (gotpic) { | |
| //int inputDataSize = av_samples_get_buffer_size(NULL, ctx->channels, ffd->audio_frame->nb_samples, ctx->sample_fmt, 1); | |
| gotpic = ffd->audio_frame->nb_samples * 2 * ctx->channels; | |
| } | |
| #elif defined(USE_AVCODEC2) | |
| gotpic = 192000; | |
| len = avcodec_decode_audio3(ctx, (short *)ffd->audio_buf, &gotpic, &pkt); | |
| #else | |
| gotpic = AVCODEC_MAX_AUDIO_FRAME_SIZE; | |
| len = avcodec_decode_audio2(ctx, (short *)ffd->audio_buf, &gotpic, inBuffer + ffd->frame_start, inBufferLength - ffd->frame_start); | |
| #endif | |
| if (len<0) { | |
| ffd->frame_start = 0; | |
| return GF_NON_COMPLIANT_BITSTREAM; | |
| } | |
| if (gotpic<0) { | |
| ffd->frame_start = 0; | |
| return GF_OK; | |
| } | |
| /*first config*/ | |
| if (!ffd->out_size) { | |
| u32 bpp = 2; | |
| if (ctx->channels * ctx->frame_size * bpp < gotpic) ctx->frame_size = gotpic / (bpp * ctx->channels); | |
| ffd->out_size = ctx->channels * ctx->frame_size * bpp; | |
| } | |
| if (ffd->out_size < (u32) gotpic) { | |
| /*looks like relying on frame_size is not a good idea for all codecs, so we use gotpic*/ | |
| (*outBufferLength) = ffd->out_size = gotpic; | |
| return GF_BUFFER_TOO_SMALL; | |
| } | |
| if (ffd->out_size > buf_size) { | |
| /*don't use too small output chunks otherwise we'll never have enough when mixing - we could | |
| also request more slots in the composition memory but let's not waste mem*/ | |
| if (ffd->out_size < (u32) 576*ctx->channels) ffd->out_size=ctx->channels*576; | |
| (*outBufferLength) = ffd->out_size; | |
| return GF_BUFFER_TOO_SMALL; | |
| } | |
| #if defined(USE_AVCTX3) | |
| if (ffd->audio_frame->format==AV_SAMPLE_FMT_FLTP) { | |
| s32 i, j; | |
| s16 *output = (s16 *) outBuffer; | |
| for (j=0; j<ctx->channels; j++) { | |
| Float* inputChannel = (Float*)ffd->audio_frame->extended_data[j]; | |
| for (i=0 ; i<ffd->audio_frame->nb_samples ; i++) { | |
| Float sample = inputChannel[i]; | |
| if (sample<-1.0f) sample=-1.0f; | |
| else if (sample>1.0f) sample=1.0f; | |
| output[i*ctx->channels + j] = (int16_t) (sample * GF_SHORT_MAX ); | |
| } | |
| } | |
| } else if (ffd->audio_frame->format==AV_SAMPLE_FMT_S16P) { | |
| s32 i, j; | |
| s16 *output = (s16 *) outBuffer; | |
| for (j=0; j<ctx->channels; j++) { | |
| s16* inputChannel = (s16*)ffd->audio_frame->extended_data[j]; | |
| for (i=0 ; i<ffd->audio_frame->nb_samples ; i++) { | |
| Float sample = inputChannel[i]; | |
| output[i*ctx->channels + j] = (int16_t) (sample ); | |
| } | |
| } | |
| } else if (ffd->audio_frame->format==AV_SAMPLE_FMT_U8) { | |
| u32 i, size = ffd->audio_frame->nb_samples * ctx->channels; | |
| s16 *output = (s16 *) outBuffer; | |
| s8 *input = (s8 *) ffd->audio_frame->data[0]; | |
| for (i=0; i<size; i++) { | |
| output [i] = input[i] * 128; | |
| } | |
| } else if (ffd->audio_frame->format==AV_SAMPLE_FMT_S32) { | |
| u32 i, shift, size = ffd->audio_frame->nb_samples * ctx->channels; | |
| s16 *output = (s16 *) outBuffer; | |
| s32 *input = (s32*) ffd->audio_frame->data[0]; | |
| shift = 1<<31; | |
| for (i=0; i<size; i++) { | |
| output [i] = input[i] * shift; | |
| } | |
| } else if (ffd->audio_frame->format==AV_SAMPLE_FMT_FLT) { | |
| u32 i, size = ffd->audio_frame->nb_samples * ctx->channels; | |
| s16 *output = (s16 *) outBuffer; | |
| Float *input = (Float *) ffd->audio_frame->data[0]; | |
| for (i=0; i<size; i++) { | |
| Float sample = input[i]; | |
| if (sample<-1.0f) sample=-1.0f; | |
| else if (sample>1.0f) sample=1.0f; | |
| output [i] = (int16_t) (sample * GF_SHORT_MAX); | |
| } | |
| } else if (ffd->audio_frame->format==AV_SAMPLE_FMT_S16) { | |
| memcpy(outBuffer, ffd->audio_frame->data[0], sizeof(char) * ffd->audio_frame->nb_samples * ctx->channels*2); | |
| } else if (ffd->audio_frame->nb_samples) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CODEC, ("[FFMPEG Decoder] Raw Audio format %d not supported\n", ffd->audio_frame->format )); | |
| } | |
| #else | |
| /*we're sure to have at least gotpic bytes available in output*/ | |
| memcpy(outBuffer, ffd->audio_buf, sizeof(char) * gotpic); | |
| #endif | |
| (*outBufferLength) += gotpic; | |
| outBuffer += gotpic; | |
| #if defined(USE_AVCTX3) | |
| ffd->audio_frame->nb_samples = 0; | |
| #endif | |
| ffd->frame_start += len; | |
| if (inBufferLength <= ffd->frame_start) { | |
| ffd->frame_start = 0; | |
| return GF_OK; | |
| } | |
| /*still space go on*/ | |
| if ((*outBufferLength)+ctx->frame_size<ffd->out_size) goto redecode; | |
| /*more frames in the current sample*/ | |
| return GF_PACKED_FRAMES; | |
| } | |
| //TODO check why we need this one ... | |
| static GF_Err FFDEC_ProcessRawVideo(FFDec *ffd, | |
| char *inBuffer, u32 inBufferLength, | |
| u16 ES_ID, u32 *CTS, | |
| char *outBuffer, u32 *outBufferLength, | |
| u8 PaddingBits, u32 mmlevel) | |
| { | |
| AVCodecContext *ctx = ffd->base_ctx; | |
| if (*outBufferLength != ffd->out_size) { | |
| *outBufferLength = ffd->out_size; | |
| return GF_BUFFER_TOO_SMALL; | |
| } | |
| if (inBufferLength) { | |
| *outBufferLength = ffd->out_size; | |
| // assert(inBufferLength==ffd->out_size); | |
| if (ffd->raw_pix_fmt==PIX_FMT_BGR24) { | |
| s32 i, j; | |
| for (j=0; j<ctx->height; j++) { | |
| u8 *src = (u8 *) inBuffer + j*3*ctx->width; | |
| u8 *dst = (u8 *)outBuffer + j*3*ctx->width; | |
| if (ffd->flipped) { | |
| dst = (u8 *)outBuffer + (ctx->height-j-1) * 3*ctx->width; | |
| } | |
| for (i=0; i<ctx->width; i++) { | |
| dst[0] = src[2]; | |
| dst[1] = src[1]; | |
| dst[2] = src[2]; | |
| src += 3; | |
| dst += 3; | |
| } | |
| } | |
| } else { | |
| memcpy(outBuffer, inBuffer, ffd->out_size); | |
| } | |
| } else { | |
| *outBufferLength = 0; | |
| } | |
| return GF_OK; | |
| } | |
| static GF_Err FFDEC_ProcessVideo(FFDec *ffd, | |
| char *inBuffer, u32 inBufferLength, | |
| u16 ES_ID, u32 *CTS, | |
| char *outBuffer, u32 *outBufferLength, | |
| u8 PaddingBits, u32 mmlevel) | |
| { | |
| #ifdef USE_AVCODEC2 | |
| AVPacket pkt; | |
| #endif | |
| AVPicture pict; | |
| u32 pix_out=0; | |
| s32 w, h, gotpic, stride, ret; | |
| u32 outsize=0, out_pix_fmt; | |
| AVCodecContext *ctx; | |
| AVCodec **codec; | |
| AVFrame *frame; | |
| #ifdef FFMPEG_SWSCALE | |
| struct SwsContext **cached_sws = &(ffd->base_sws); | |
| #endif | |
| if (!ES_ID || (ffd->base_ES_ID==ES_ID)) { | |
| ctx = ffd->base_ctx; | |
| codec = &ffd->base_codec; | |
| frame = ffd->base_frame; | |
| } else if (ffd->depth_ES_ID==ES_ID) { | |
| ctx = ffd->depth_ctx; | |
| codec = &ffd->depth_codec; | |
| frame = ffd->depth_frame; | |
| } else { | |
| return GF_BAD_PARAM; | |
| } | |
| /*WARNING: this breaks H264 (and maybe others) decoding, disabled for now*/ | |
| #if 0 | |
| if (!ctx->hurry_up) { | |
| switch (mmlevel) { | |
| case GF_CODEC_LEVEL_SEEK: | |
| case GF_CODEC_LEVEL_DROP: | |
| /*skip as much as possible*/ | |
| ctx->hurry_up = 5; | |
| break; | |
| case GF_CODEC_LEVEL_VERY_LATE: | |
| case GF_CODEC_LEVEL_LATE: | |
| /*skip B-frames*/ | |
| ctx->hurry_up = 1; | |
| break; | |
| default: | |
| ctx->hurry_up = 0; | |
| break; | |
| } | |
| } | |
| #endif | |
| #ifdef USE_AVCODEC2 | |
| av_init_packet(&pkt); | |
| pkt.data = (uint8_t *)inBuffer; | |
| pkt.size = inBufferLength; | |
| pkt.dts=0; | |
| pkt.pts = *CTS; | |
| #endif | |
| *outBufferLength = 0; | |
| /*visual stream*/ | |
| w = ctx->width; | |
| h = ctx->height; | |
| /*we have a valid frame not yet dispatched*/ | |
| if (ffd->had_pic) { | |
| ffd->had_pic = GF_FALSE; | |
| gotpic = ffd->needs_output_resize ? 0 : 1; | |
| ffd->needs_output_resize = 0; | |
| } else { | |
| if (ffd->check_h264_isma) { | |
| /*for AVC bitstreams after ISMA decryption, in case (as we do) the decryption DRM tool | |
| doesn't put back nalu size, do it ourselves...*/ | |
| if (inBuffer && !inBuffer[0] && !inBuffer[1] && !inBuffer[2] && (inBuffer[3]==0x01)) { | |
| u32 nalu_size; | |
| char *start, *end, *bufferEnd; | |
| start = inBuffer; | |
| end = inBuffer + 4; | |
| bufferEnd = inBuffer + inBufferLength; | |
| /* FIXME : SOUCHAY : not sure of exact behaviour, but old one was reading non-allocated memory */ | |
| while ((end+3) < bufferEnd) { | |
| if (!end[0] && !end[1] && !end[2] && (end[3]==0x01)) { | |
| nalu_size = (u32) (end - start - 4); | |
| start[0] = (nalu_size>>24)&0xFF; | |
| start[1] = (nalu_size>>16)&0xFF; | |
| start[2] = (nalu_size>>8)&0xFF; | |
| start[3] = (nalu_size)&0xFF; | |
| start = end; | |
| end = start+4; | |
| continue; | |
| } | |
| end++; | |
| } | |
| nalu_size = (u32) ((inBuffer+inBufferLength) - start - 4); | |
| start[0] = (nalu_size>>24)&0xFF; | |
| start[1] = (nalu_size>>16)&0xFF; | |
| start[2] = (nalu_size>>8)&0xFF; | |
| start[3] = (nalu_size)&0xFF; | |
| ffd->check_h264_isma = 2; | |
| } | |
| /*if we had ISMA E&A and lost it this is likely due to a pck loss - do NOT switch back to regular*/ | |
| else if (ffd->check_h264_isma == 1) { | |
| ffd->check_h264_isma = GF_FALSE; | |
| } | |
| } | |
| #ifdef USE_AVCODEC2 | |
| if (avcodec_decode_video2(ctx, frame, &gotpic, &pkt) < 0) { | |
| #else | |
| if (avcodec_decode_video(ctx, frame, &gotpic, inBuffer, inBufferLength) < 0) { | |
| #endif | |
| if (!ffd->check_short_header) { | |
| return GF_NON_COMPLIANT_BITSTREAM; | |
| } | |
| /*switch to H263 (ffmpeg MPEG-4 codec doesn't understand short headers)*/ | |
| { | |
| u32 old_codec = (*codec)->id; | |
| ffd->check_short_header = GF_FALSE; | |
| /*OK we loose the DSI stored in the codec context, but H263 doesn't need any, and if we're | |
| here this means the DSI was broken, so no big deal*/ | |
| avcodec_close(ctx); | |
| *codec = avcodec_find_decoder(CODEC_ID_H263); | |
| #ifdef USE_AVCTX3 | |
| if (! (*codec) || (avcodec_open2(ctx, *codec, NULL)<0)) return GF_NON_COMPLIANT_BITSTREAM; | |
| #else | |
| if (! (*codec) || (avcodec_open(ctx, *codec)<0)) return GF_NON_COMPLIANT_BITSTREAM; | |
| #endif | |
| #if USE_AVCODEC2 | |
| if (avcodec_decode_video2(ctx, frame, &gotpic, &pkt) < 0) { | |
| #else | |
| if (avcodec_decode_video(ctx, frame, &gotpic, inBuffer, inBufferLength) < 0) { | |
| #endif | |
| /*nope, stay in MPEG-4*/ | |
| avcodec_close(ctx); | |
| *codec = avcodec_find_decoder(old_codec); | |
| assert(*codec); | |
| #ifdef USE_AVCTX3 | |
| avcodec_open2(ctx, *codec, NULL); | |
| #else | |
| avcodec_open(ctx, *codec); | |
| #endif | |
| return GF_NON_COMPLIANT_BITSTREAM; | |
| } | |
| } | |
| } | |
| /* | |
| if (!gotpic && (!ctx->width || !ctx->height) ) { | |
| ctx->width = w; | |
| ctx->height = h; | |
| return GF_OK; | |
| } | |
| */ | |
| /*some streams use odd width/height frame values*/ | |
| if (ffd->out_pix_fmt == GF_PIXEL_YV12) { | |
| if (ctx->width%2) ctx->width++; | |
| if (ctx->height%2) ctx->height++; | |
| } | |
| } | |
| /*we have a picture and need resize, do it*/ | |
| if (gotpic && ffd->needs_output_resize) { | |
| ffd->needs_output_resize = GF_FALSE; | |
| ffd->had_pic = GF_TRUE; | |
| *outBufferLength = ffd->out_size; | |
| if (ffd->direct_output_mode) { | |
| ffd->frame_size_changed = GF_TRUE; | |
| } else { | |
| return GF_BUFFER_TOO_SMALL; | |
| } | |
| } | |
| stride = frame->linesize[0]; | |
| ret = FFDEC_GetOutputPixelFromat(ctx->pix_fmt, &out_pix_fmt, ffd); | |
| if ( ret < 0) | |
| { | |
| return ret; | |
| } | |
| /*recompute outsize in case on-the-fly change*/ | |
| if ((w != ctx->width) || (h != ctx->height) | |
| || (ffd->direct_output_mode && (stride != ffd->stride)) | |
| || (ffd->out_pix_fmt != out_pix_fmt) | |
| //need to realloc the conversion buffer | |
| ) { | |
| ffd->stride = (ffd->direct_output_mode) ? frame->linesize[0] : ctx->width; | |
| if (ctx->pix_fmt == PIX_FMT_RGBA) { | |
| ffd->out_pix_fmt = ffd->pix_fmt = GF_PIXEL_RGBA; | |
| outsize = ctx->width * ctx->height * 4; | |
| } | |
| else if (ffd->out_pix_fmt == GF_PIXEL_RGB_24) { | |
| outsize = ctx->width * ctx->height * 3; | |
| } | |
| else if (ctx->pix_fmt == PIX_FMT_YUV420P) | |
| { | |
| outsize = ffd->stride * ctx->height * 3 /2 ; | |
| ffd->out_pix_fmt = GF_PIXEL_YV12; | |
| } | |
| else if (ctx->pix_fmt == PIX_FMT_YUV422P) | |
| { | |
| outsize = ffd->stride * ctx->height * 2 ; | |
| ffd->out_pix_fmt = GF_PIXEL_YUV422; | |
| } | |
| else if (ctx->pix_fmt == PIX_FMT_YUV444P) | |
| { | |
| outsize = ffd->stride * ctx->height * 3 ; | |
| ffd->out_pix_fmt = GF_PIXEL_YUV444; | |
| } | |
| #ifndef NO_10bit | |
| //this YUV format is handled natively in GPAC | |
| else if (ctx->pix_fmt == PIX_FMT_YUV420P10LE) { | |
| ffd->stride = ffd->direct_output_mode ? frame->linesize[0] : ctx->width * 2; | |
| ffd->out_pix_fmt = GF_PIXEL_YV12_10; | |
| outsize = 3*ffd->stride * ctx->height / 2; | |
| } | |
| else if (ctx->pix_fmt == PIX_FMT_YUV422P10LE) { | |
| ffd->stride = ffd->direct_output_mode ? frame->linesize[0] : ctx->width * 2; | |
| ffd->out_pix_fmt = GF_PIXEL_YUV422_10; | |
| outsize = ffd->stride * ctx->height * 2; | |
| } | |
| else if (ctx->pix_fmt == PIX_FMT_YUV444P10LE) { | |
| ffd->stride = ffd->direct_output_mode ? frame->linesize[0] : ctx->width * 2; | |
| ffd->out_pix_fmt = GF_PIXEL_YUV444_10; | |
| outsize = ffd->stride * ctx->height * 3; | |
| } | |
| #endif | |
| if (ffd->depth_codec) { | |
| outsize = 5 * ctx->width * ctx->height / 2; | |
| ffd->yuv_size = 3 * ctx->width * ctx->height / 2; | |
| } | |
| ffd->out_size = outsize; | |
| if (!ffd->no_par_update && ctx->sample_aspect_ratio.num && ctx->sample_aspect_ratio.den) { | |
| ffd->previous_par = (ctx->sample_aspect_ratio.num<<16) | ctx->sample_aspect_ratio.den; | |
| } | |
| /*we didn't get any picture: wait for a picture before resizing output buffer, otherwise we will have no | |
| video in the output buffer*/ | |
| if (!gotpic) { | |
| ffd->needs_output_resize = GF_TRUE; | |
| // return GF_OK; | |
| } | |
| *outBufferLength = ffd->out_size; | |
| if (ffd->check_h264_isma) { | |
| inBuffer[0] = inBuffer[1] = inBuffer[2] = 0; | |
| inBuffer[3] = 1; | |
| } | |
| #ifdef FFMPEG_SWSCALE | |
| if (*cached_sws) { | |
| sws_freeContext(*cached_sws); | |
| *cached_sws = NULL; | |
| } | |
| #endif | |
| ffd->had_pic = GF_TRUE; | |
| if (ffd->direct_output_mode) { | |
| ffd->frame_size_changed = GF_TRUE; | |
| } else { | |
| return GF_BUFFER_TOO_SMALL; | |
| } | |
| } | |
| /*check PAR in case on-the-fly change*/ | |
| if (!ffd->no_par_update && ctx->sample_aspect_ratio.num && ctx->sample_aspect_ratio.den) { | |
| u32 new_par = (ctx->sample_aspect_ratio.num<<16) | ctx->sample_aspect_ratio.den; | |
| if (ffd->previous_par && (new_par != ffd->previous_par)) { | |
| ffd->previous_par = new_par; | |
| if (!gotpic) { | |
| ffd->needs_output_resize = GF_TRUE; | |
| return GF_OK; | |
| } | |
| *outBufferLength = ffd->out_size; | |
| ffd->had_pic = GF_TRUE; | |
| if (ffd->direct_output_mode) { | |
| ffd->frame_size_changed = GF_TRUE; | |
| } else { | |
| return GF_BUFFER_TOO_SMALL; | |
| } | |
| } | |
| } | |
| // if (mmlevel == GF_CODEC_LEVEL_SEEK) return GF_OK; | |
| if (!gotpic) return GF_OK; | |
| #if (LIBAVCODEC_VERSION_MAJOR>52) | |
| //fixme - investigate this, happens in some dash cases | |
| if ((frame->width!=ctx->width) || (frame->height!=ctx->height)) { | |
| *outBufferLength = 0; | |
| return GF_OK; | |
| } | |
| #endif | |
| //copy over CTS of packet passed in decode() | |
| *CTS = (u32) frame->pkt_pts; | |
| if (ffd->direct_output_mode) { | |
| *outBufferLength = ffd->out_size; | |
| return GF_OK; | |
| } | |
| if (ES_ID && (ES_ID == ffd->depth_ES_ID)) { | |
| s32 i; | |
| u8 *pYO, *pYD; | |
| pYO = frame->data[0]; | |
| pYD = (u8 *) outBuffer+ffd->yuv_size; | |
| for (i=0; i<ctx->height; i++) { | |
| memcpy(pYD, pYO, sizeof(char) * ctx->width); | |
| pYD += ctx->width; | |
| pYO += frame->linesize[0]; | |
| } | |
| *outBufferLength = ffd->out_size; | |
| return GF_OK; | |
| } | |
| #if defined(_WIN32_WCE) || defined(__SYMBIAN32__) | |
| if (ffd->pix_fmt==GF_PIXEL_RGB_24) { | |
| memcpy(outBuffer, frame->data[0], sizeof(char)*3*ctx->width); | |
| } else if (ffd->pix_fmt==GF_PIXEL_RGBA) { | |
| memcpy(outBuffer, frame->data[0], sizeof(char)*4*ctx->width); | |
| } else { | |
| s32 i; | |
| char *pYO, *pUO, *pVO; | |
| unsigned char *pYD, *pUD, *pVD; | |
| pYO = frame->data[0]; | |
| pUO = frame->data[1]; | |
| pVO = frame->data[2]; | |
| pYD = outBuffer; | |
| pUD = outBuffer + ctx->width * ctx->height; | |
| pVD = outBuffer + 5 * ctx->width * ctx->height / 4; | |
| for (i=0; i<ctx->height; i++) { | |
| memcpy(pYD, pYO, sizeof(char) * ctx->width); | |
| pYD += ctx->width; | |
| pYO += frame->linesize[0]; | |
| if (i%2) continue; | |
| memcpy(pUD, pUO, sizeof(char) * ctx->width/2); | |
| memcpy(pVD, pVO, sizeof(char) * ctx->width/2); | |
| pUD += ctx->width/2; | |
| pVD += ctx->width/2; | |
| pUO += frame->linesize[1]; | |
| pVO += frame->linesize[2]; | |
| } | |
| *outBufferLength = ffd->out_size; | |
| } | |
| #else | |
| memset(&pict, 0, sizeof(pict)); | |
| if (ffd->out_pix_fmt==GF_PIXEL_RGB_24) { | |
| pict.data[0] = (uint8_t *)outBuffer; | |
| pict.linesize[0] = 3*ctx->width; | |
| pix_out = PIX_FMT_RGB24; | |
| } else if (ffd->out_pix_fmt==GF_PIXEL_RGBA) { | |
| pict.data[0] = (uint8_t *)outBuffer; | |
| pict.linesize[0] = 4*ctx->width; | |
| pix_out = PIX_FMT_RGBA; | |
| } else { | |
| if (ffd->out_pix_fmt == GF_PIXEL_YV12) { | |
| pict.data[0] = (uint8_t *)outBuffer; | |
| pict.data[1] = (uint8_t *)outBuffer + ffd->stride * ctx->height; | |
| pict.data[2] = (uint8_t *)outBuffer + 5 * ffd->stride * ctx->height / 4; | |
| pict.linesize[0] = ffd->stride; | |
| pict.linesize[1] = pict.linesize[2] = ffd->stride/2; | |
| pix_out = PIX_FMT_YUV420P; | |
| } else if (ffd->out_pix_fmt == GF_PIXEL_YUV422) { | |
| pict.data[0] = (uint8_t *)outBuffer; | |
| pict.data[1] = (uint8_t *)outBuffer + ffd->stride * ctx->height; | |
| pict.data[2] = (uint8_t *)outBuffer + 3*ffd->stride * ctx->height/2; | |
| pict.linesize[0] = ffd->stride; | |
| pict.linesize[1] = pict.linesize[2] = ffd->stride/2; | |
| pix_out = PIX_FMT_YUV422P; | |
| } else if (ffd->out_pix_fmt == GF_PIXEL_YUV444) { | |
| pict.data[0] = (uint8_t *)outBuffer; | |
| pict.data[1] = (uint8_t *)outBuffer + ffd->stride * ctx->height; | |
| pict.data[2] = (uint8_t *)outBuffer + 2*ffd->stride * ctx->height; | |
| pict.linesize[0] = pict.linesize[1] = pict.linesize[2] = ffd->stride; | |
| pix_out = PIX_FMT_YUV444P; | |
| } | |
| #ifndef NO_10bit | |
| //this YUV format is handled natively in GPAC | |
| if (ctx->pix_fmt==PIX_FMT_YUV420P10LE) { | |
| pict.data[0] = (uint8_t *)outBuffer; | |
| pict.data[1] = (uint8_t *)outBuffer + ffd->stride * ctx->height; | |
| pict.data[2] = (uint8_t *)outBuffer + 5 * ffd->stride * ctx->height / 4; | |
| pict.linesize[0] = ffd->stride; | |
| pict.linesize[1] = pict.linesize[2] = ffd->stride/2; | |
| pix_out = PIX_FMT_YUV420P10LE; | |
| } else if (ctx->pix_fmt==PIX_FMT_YUV422P10LE) { | |
| pict.data[0] = (uint8_t *)outBuffer; | |
| pict.data[1] = (uint8_t *)outBuffer + ffd->stride * ctx->height; | |
| pict.data[2] = (uint8_t *)outBuffer + 3*ffd->stride * ctx->height/2; | |
| pict.linesize[0] = ffd->stride; | |
| pict.linesize[1] = pict.linesize[2] = ffd->stride/2; | |
| pix_out = PIX_FMT_YUV422P10LE; | |
| } else if (ctx->pix_fmt==PIX_FMT_YUV444P10LE) { | |
| pict.data[0] = (uint8_t *)outBuffer; | |
| pict.data[1] = (uint8_t *)outBuffer + ffd->stride * ctx->height; | |
| pict.data[2] = (uint8_t *)outBuffer + 2*ffd->stride * ctx->height; | |
| pict.linesize[0] = pict.linesize[1] = pict.linesize[2] = ffd->stride; | |
| pix_out = PIX_FMT_YUV444P10LE; | |
| } | |
| #endif | |
| #if (LIBAVCODEC_VERSION_MAJOR<56) | |
| if (!mmlevel && frame->interlaced_frame) { | |
| avpicture_deinterlace((AVPicture *) frame, (AVPicture *) frame, ctx->pix_fmt, ctx->width, ctx->height); | |
| } | |
| #endif | |
| } | |
| pict.data[3] = 0; | |
| pict.linesize[3] = 0; | |
| *CTS = (u32) frame->pkt_pts; | |
| #ifndef FFMPEG_SWSCALE | |
| img_convert(&pict, pix_out, (AVPicture *) frame, ctx->pix_fmt, ctx->width, ctx->height); | |
| #else | |
| *cached_sws = sws_getCachedContext(*cached_sws, | |
| ctx->width, ctx->height, ctx->pix_fmt, | |
| ctx->width, ctx->height, pix_out, | |
| SWS_BICUBIC, NULL, NULL, NULL); | |
| if ((*cached_sws)) { | |
| #if LIBSWSCALE_VERSION_INT < AV_VERSION_INT(0, 9, 0) | |
| sws_scale((*cached_sws), frame->data, frame->linesize, 0, ctx->height, pict.data, pict.linesize); | |
| #else | |
| sws_scale((*cached_sws), (const uint8_t * const*)frame->data, frame->linesize, 0, ctx->height, pict.data, pict.linesize); | |
| #endif | |
| } | |
| #endif | |
| *outBufferLength = ffd->out_size; | |
| #endif | |
| return GF_OK; | |
| } | |
| static GF_Err FFDEC_ProcessData(GF_MediaDecoder *plug, | |
| char *inBuffer, u32 inBufferLength, | |
| u16 ES_ID, u32 *CTS, | |
| char *outBuffer, u32 *outBufferLength, | |
| u8 PaddingBits, u32 mmlevel) | |
| { | |
| FFDec *ffd = (FFDec*)plug->privateStack; | |
| if (ffd->st==GF_STREAM_AUDIO) { | |
| return FFDEC_ProcessAudio(ffd, inBuffer, inBufferLength, ES_ID, CTS, outBuffer, outBufferLength, PaddingBits, mmlevel); | |
| } else if ( ffd->base_ctx->codec_id == CODEC_ID_RAWVIDEO) { | |
| return FFDEC_ProcessRawVideo(ffd, inBuffer, inBufferLength, ES_ID, CTS, outBuffer, outBufferLength, PaddingBits, mmlevel); | |
| } else { | |
| return FFDEC_ProcessVideo(ffd, inBuffer, inBufferLength, ES_ID, CTS, outBuffer, outBufferLength, PaddingBits, mmlevel); | |
| } | |
| } | |
| static GF_Err FFDEC_GetOutputBuffer(GF_MediaDecoder *ifcg, u16 ES_ID, u8 **pY_or_RGB, u8 **pU, u8 **pV) | |
| { | |
| FFDec *ffd = (FFDec*)ifcg->privateStack; | |
| AVFrame *frame; | |
| if (ffd->direct_output_mode != 1) return GF_BAD_PARAM; | |
| if (ES_ID && (ffd->depth_ES_ID==ES_ID)) { | |
| frame = ffd->depth_frame; | |
| *pY_or_RGB = frame->data[0]; | |
| } else { | |
| frame = ffd->base_frame; | |
| *pY_or_RGB = frame->data[0]; | |
| *pU = frame->data[1]; | |
| *pV = frame->data[2]; | |
| } | |
| return GF_OK; | |
| } | |
| #if defined(USE_AVCTX3) && defined(FFMPEG_DIRECT_DISPATCH) | |
| typedef struct | |
| { | |
| FFDec *ctx; | |
| AVFrame *frame; | |
| } FF_Frame; | |
| void FFFrame_Release(GF_MediaDecoderFrame *frame) | |
| { | |
| FF_Frame *ff_frame = (FF_Frame *)frame->user_data; | |
| av_frame_free(&ff_frame->frame); | |
| } | |
| GF_Err FFFrame_GetPlane(GF_MediaDecoderFrame *frame, u32 plane_idx, const char **outPlane, u32 *outStride) | |
| { | |
| FF_Frame *ff_frame = (FF_Frame *)frame->user_data; | |
| switch (ff_frame->ctx->out_pix_fmt) { | |
| case GF_PIXEL_YV12: | |
| case GF_PIXEL_YV12_10: | |
| case GF_PIXEL_YUV422: | |
| case GF_PIXEL_YUV422_10: | |
| case GF_PIXEL_YUV444: | |
| case GF_PIXEL_YUV444_10: | |
| if (plane_idx>2) return GF_BAD_PARAM; | |
| *outPlane = (const char *) ff_frame->frame->data[plane_idx]; | |
| *outStride = ff_frame->frame->linesize[plane_idx]; | |
| break; | |
| case GF_PIXEL_RGBA: | |
| case GF_PIXEL_RGB_24: | |
| case GF_PIXEL_BGR_24: | |
| if (plane_idx>0) return GF_BAD_PARAM; | |
| *outPlane = (const char *) ff_frame->frame->data[plane_idx]; | |
| *outStride = ff_frame->frame->linesize[plane_idx]; | |
| break; | |
| default: | |
| return GF_NOT_SUPPORTED; | |
| } | |
| return GF_OK; | |
| } | |
| static GF_Err FFDEC_GetOutputFrame(GF_MediaDecoder *ifcg, u16 ES_ID, GF_MediaDecoderFrame **frame, Bool *needs_resize) | |
| { | |
| GF_MediaDecoderFrame *a_frame; | |
| FF_Frame *ff_frame; | |
| FFDec *ffd = (FFDec*)ifcg->privateStack; | |
| *needs_resize = GF_FALSE; | |
| if (!ffd->base_frame) return GF_BAD_PARAM; | |
| GF_SAFEALLOC(a_frame, GF_MediaDecoderFrame); | |
| if (!a_frame) return GF_OUT_OF_MEM; | |
| GF_SAFEALLOC(ff_frame, FF_Frame); | |
| if (!ff_frame) { | |
| gf_free(a_frame); | |
| return GF_OUT_OF_MEM; | |
| } | |
| a_frame->user_data = ff_frame; | |
| ff_frame->ctx = ffd; | |
| ff_frame->frame = av_frame_clone(ffd->base_frame); | |
| a_frame->Release = FFFrame_Release; | |
| a_frame->GetPlane = FFFrame_GetPlane; | |
| *frame = a_frame; | |
| if (ffd->frame_size_changed) { | |
| ffd->frame_size_changed = GF_FALSE; | |
| *needs_resize = GF_TRUE; | |
| } | |
| return GF_OK; | |
| } | |
| #endif //USE_AVCTX3 | |
| static u32 FFDEC_CanHandleStream(GF_BaseDecoder *plug, u32 StreamType, GF_ESD *esd, u8 PL) | |
| { | |
| GF_BitStream *bs; | |
| u32 codec_id; | |
| Bool check_4cc; | |
| FFDec *ffd = (FFDec*)plug->privateStack; | |
| /*media type query*/ | |
| if (!esd) { | |
| if ((StreamType==GF_STREAM_VISUAL) || (StreamType==GF_STREAM_AUDIO)) return GF_CODEC_STREAM_TYPE_SUPPORTED; | |
| return GF_CODEC_NOT_SUPPORTED; | |
| } | |
| /*store types*/ | |
| ffd->oti = esd->decoderConfig->objectTypeIndication; | |
| ffd->st = StreamType; | |
| codec_id = 0; | |
| check_4cc = GF_FALSE; | |
| /*private from FFMPEG input*/ | |
| if (ffd->oti == GPAC_OTI_MEDIA_FFMPEG) { | |
| bs = gf_bs_new(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, GF_BITSTREAM_READ); | |
| codec_id = gf_bs_read_u32(bs); | |
| gf_bs_del(bs); | |
| } | |
| /*private from IsoMedia input*/ | |
| else if (ffd->oti == GPAC_OTI_MEDIA_GENERIC) { | |
| bs = gf_bs_new(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, GF_BITSTREAM_READ); | |
| codec_id = gf_bs_read_u32(bs); | |
| check_4cc = GF_TRUE; | |
| gf_bs_del(bs); | |
| if (codec_id == GF_4CC('s','a','m','r')) { | |
| codec_id = CODEC_ID_AMR_NB; | |
| } | |
| else if (codec_id == GF_4CC('s','a','w','b')) { | |
| codec_id = CODEC_ID_AMR_WB; | |
| } | |
| } | |
| else if (StreamType==GF_STREAM_AUDIO) { | |
| /*std MPEG-2 audio*/ | |
| switch (ffd->oti) { | |
| case GPAC_OTI_AUDIO_MPEG2_PART3: | |
| case GPAC_OTI_AUDIO_MPEG1: | |
| codec_id = CODEC_ID_MP2; | |
| break; | |
| case GPAC_OTI_AUDIO_AC3: | |
| codec_id = CODEC_ID_AC3; | |
| break; | |
| case GPAC_OTI_AUDIO_EAC3: | |
| codec_id = CODEC_ID_EAC3; | |
| break; | |
| case GPAC_OTI_AUDIO_AAC_MPEG2_LCP: | |
| case GPAC_OTI_AUDIO_AAC_MPEG2_MP: | |
| case GPAC_OTI_AUDIO_AAC_MPEG2_SSRP: | |
| case GPAC_OTI_AUDIO_AAC_MPEG4: | |
| codec_id = CODEC_ID_AAC; | |
| if (avcodec_find_decoder(codec_id) != NULL) | |
| return GF_CODEC_MAYBE_SUPPORTED; | |
| break; | |
| #ifdef CODEC_ID_OPUS | |
| case GPAC_OTI_MEDIA_OPUS: | |
| if (avcodec_find_decoder(CODEC_ID_OPUS) != NULL) | |
| return GF_CODEC_MAYBE_SUPPORTED; | |
| #endif | |
| return GF_CODEC_NOT_SUPPORTED; | |
| } | |
| } | |
| /*std MPEG-4 visual*/ | |
| else if (StreamType==GF_STREAM_VISUAL) { | |
| /*fixme - we should use some priority rather than declare ffmpeg can't handle svc*/ | |
| if (esd->decoderConfig->objectTypeIndication == GPAC_OTI_VIDEO_AVC) { | |
| if (esd->decoderConfig->decoderSpecificInfo && esd->decoderConfig->decoderSpecificInfo->data) { | |
| Bool is_svc = GF_FALSE; | |
| u32 i, count; | |
| GF_AVCConfig *cfg = gf_odf_avc_cfg_read(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength); | |
| if (!cfg) return GF_CODEC_SUPPORTED; | |
| if (esd->has_scalable_layers) | |
| is_svc = GF_TRUE; | |
| /*decode all NALUs*/ | |
| count = gf_list_count(cfg->sequenceParameterSets); | |
| for (i=0; i<count; i++) { | |
| GF_AVCConfigSlot *slc = (GF_AVCConfigSlot*)gf_list_get(cfg->sequenceParameterSets, i); | |
| u8 nal_type = slc->data[0] & 0x1F; | |
| if (nal_type==GF_AVC_NALU_SVC_SUBSEQ_PARAM) { | |
| is_svc = GF_TRUE; | |
| break; | |
| } | |
| } | |
| gf_odf_avc_cfg_del(cfg); | |
| return (is_svc || esd->decoderConfig->rvc_config || esd->decoderConfig->predefined_rvc_config) ? GF_CODEC_MAYBE_SUPPORTED : GF_CODEC_SUPPORTED; | |
| } | |
| if (esd->decoderConfig->rvc_config || esd->decoderConfig->predefined_rvc_config || esd->has_scalable_layers) return GF_CODEC_MAYBE_SUPPORTED; | |
| return GF_CODEC_SUPPORTED; | |
| } | |
| switch (ffd->oti) { | |
| /*MPEG-4 v1 simple profile*/ | |
| case GPAC_OTI_VIDEO_MPEG4_PART2: | |
| codec_id = CODEC_ID_MPEG4; | |
| break; | |
| /*H264 (not std OTI, just the way we use it internally)*/ | |
| case GPAC_OTI_VIDEO_AVC: | |
| codec_id = CODEC_ID_H264; | |
| break; | |
| #ifdef HAS_HEVC | |
| case GPAC_OTI_VIDEO_HEVC: | |
| codec_id = AV_CODEC_ID_HEVC; | |
| break; | |
| #endif | |
| /*MPEG1 video*/ | |
| case GPAC_OTI_VIDEO_MPEG1: | |
| /*MPEG2 video*/ | |
| case GPAC_OTI_VIDEO_MPEG2_SIMPLE: | |
| case GPAC_OTI_VIDEO_MPEG2_MAIN: | |
| case GPAC_OTI_VIDEO_MPEG2_SNR: | |
| case GPAC_OTI_VIDEO_MPEG2_SPATIAL: | |
| case GPAC_OTI_VIDEO_MPEG2_HIGH: | |
| case GPAC_OTI_VIDEO_MPEG2_422: | |
| codec_id = CODEC_ID_MPEG2VIDEO; | |
| break; | |
| /*JPEG*/ | |
| case GPAC_OTI_IMAGE_JPEG: | |
| codec_id = CODEC_ID_MJPEG; | |
| /*return maybe supported as FFMPEG JPEG decoder has some issues with many files, so let's use it only if no | |
| other dec is available*/ | |
| if (avcodec_find_decoder(codec_id) != NULL) | |
| return GF_CODEC_MAYBE_SUPPORTED; | |
| return GF_CODEC_NOT_SUPPORTED; | |
| case GPAC_OTI_IMAGE_PNG: | |
| if (avcodec_find_decoder(CODEC_ID_PNG) != NULL) | |
| return GF_CODEC_MAYBE_SUPPORTED; | |
| return GF_CODEC_NOT_SUPPORTED; | |
| #ifdef CODEC_ID_VP9 | |
| case GPAC_OTI_VIDEO_VP9: | |
| if (avcodec_find_decoder(AV_CODEC_ID_VP9) != NULL) | |
| return GF_CODEC_MAYBE_SUPPORTED; | |
| return GF_CODEC_NOT_SUPPORTED; | |
| #endif | |
| #ifdef CODEC_ID_AV1 | |
| case GPAC_OTI_VIDEO_AV1: | |
| if (avcodec_find_decoder(CODEC_ID_AV1) != NULL) | |
| return GF_CODEC_MAYBE_SUPPORTED; | |
| #endif | |
| return GF_CODEC_NOT_SUPPORTED; | |
| default: | |
| return GF_CODEC_NOT_SUPPORTED; | |
| } | |
| } | |
| /*NeroDigital DVD subtitles*/ | |
| else if ((StreamType==GF_STREAM_ND_SUBPIC) && (ffd->oti==0xe0)) | |
| return GF_CODEC_SUPPORTED; | |
| if (!codec_id) return GF_CODEC_NOT_SUPPORTED; | |
| if (check_4cc && (ffmpeg_get_codec(codec_id) != NULL)) { | |
| if (esd->decoderConfig->rvc_config || esd->decoderConfig->predefined_rvc_config) return GF_CODEC_MAYBE_SUPPORTED; | |
| return GF_CODEC_SUPPORTED; | |
| } | |
| if (avcodec_find_decoder(codec_id) != NULL) { | |
| if (esd->decoderConfig->rvc_config || esd->decoderConfig->predefined_rvc_config) return GF_CODEC_MAYBE_SUPPORTED; | |
| //for HEVC return MAYBE supported to fallback to openHEVC if present (more optimized for now) | |
| #ifdef HAS_HEVC | |
| if (codec_id == AV_CODEC_ID_HEVC) | |
| return GF_CODEC_MAYBE_SUPPORTED; | |
| #endif | |
| return GF_CODEC_SUPPORTED; | |
| } | |
| return GF_CODEC_NOT_SUPPORTED; | |
| } | |
| static const char *FFDEC_GetCodecName(GF_BaseDecoder *dec) | |
| { | |
| FFDec *ffd; | |
| if (!dec) | |
| return NULL; | |
| ffd = (FFDec*)dec->privateStack; | |
| if (ffd && ffd->base_codec) { | |
| sprintf(ffd->szCodec, "FFMPEG %s - version %s", ffd->base_codec->name ? ffd->base_codec->name : "unknown", LIBAVCODEC_IDENT); | |
| return ffd->szCodec; | |
| } | |
| return NULL; | |
| } | |
| void *FFDEC_Load() | |
| { | |
| GF_MediaDecoder *ptr; | |
| FFDec *priv; | |
| GF_SAFEALLOC(ptr , GF_MediaDecoder); | |
| if (!ptr) return NULL; | |
| GF_SAFEALLOC(priv , FFDec); | |
| if (!priv) { | |
| gf_free(ptr); | |
| return NULL; | |
| } | |
| ptr->privateStack = priv; | |
| /* Note for valgrind : those two functions cause a leak in valgrind */ | |
| GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[FFMPEG Decoder] Registering all ffmpeg codecs...\n") ); | |
| #ifdef FF_API_AVCODE_INIT /*commit ffmpeg 3211932c513338566b31d990d06957e15a644d13*/ | |
| avcodec_init(); | |
| #endif | |
| avcodec_register_all(); | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[FFMPEG Decoder] Done registering all ffmpeg codecs.\n") ); | |
| ptr->AttachStream = FFDEC_AttachStream; | |
| ptr->DetachStream = FFDEC_DetachStream; | |
| ptr->GetCapabilities = FFDEC_GetCapabilities; | |
| ptr->SetCapabilities = FFDEC_SetCapabilities; | |
| ptr->CanHandleStream = FFDEC_CanHandleStream; | |
| ptr->GetName = FFDEC_GetCodecName; | |
| ptr->ProcessData = FFDEC_ProcessData; | |
| ptr->GetOutputBuffer = FFDEC_GetOutputBuffer; | |
| #if defined(USE_AVCTX3) && defined(FFMPEG_DIRECT_DISPATCH) | |
| ptr->GetOutputFrame = FFDEC_GetOutputFrame; | |
| #endif | |
| GF_REGISTER_MODULE_INTERFACE(ptr, GF_MEDIA_DECODER_INTERFACE, "FFMPEG decoder", "gpac distribution"); | |
| return (GF_BaseInterface *) ptr; | |
| } | |
| void FFDEC_Delete(void *ifce) | |
| { | |
| GF_BaseDecoder *dec = (GF_BaseDecoder*)ifce; | |
| FFDec *ffd; | |
| if (!ifce) | |
| return; | |
| ffd = (FFDec*)dec->privateStack; | |
| dec->privateStack = NULL; | |
| if (ffd) { | |
| if (ffd->base_ctx && ffd->base_ctx->codec) avcodec_close(ffd->base_ctx); | |
| ffd->base_ctx = NULL; | |
| if (ffd->depth_ctx && ffd->depth_ctx->codec) avcodec_close(ffd->depth_ctx); | |
| ffd->depth_ctx = NULL; | |
| #ifdef FFMPEG_SWSCALE | |
| if (ffd->base_sws) sws_freeContext(ffd->base_sws); | |
| ffd->base_sws = NULL; | |
| if (ffd->depth_sws) sws_freeContext(ffd->base_sws); | |
| ffd->depth_sws = NULL; | |
| #endif | |
| gf_free(ffd); | |
| } | |
| gf_free(dec); | |
| } |