modules/ffmpeg_in/ffmpeg_decode.c (1,180 lines of code) (raw):

/* * 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_ANDROID) || defined(GPAC_IPHONE) #define NO_10bit #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 ) * (FF_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_by_name(name); if (!codec) { strupr(name); codec = avcodec_find_decoder_by_name(name); if (!codec) { strlwr(name); codec = avcodec_find_decoder_by_name(name); } } /*custom mapings*/ if (!codec) { if (!stricmp(name, "s263")) codec = avcodec_find_decoder_by_name("h263"); else if (!stricmp(name, "samr") || !stricmp(name, "amr ")) codec = avcodec_find_decoder_by_name("amr_nb"); else if (!stricmp(name, "sawb")) codec = avcodec_find_decoder_by_name("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; const char *sOpt; #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); 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 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; } } else if ((ffd->st==GF_STREAM_ND_SUBPIC) && (ffd->oti==0xe0)) { codec_id = CODEC_ID_DVD_SUBTITLE; } *codec = avcodec_find_decoder(codec_id); } /*should never happen*/ if (! (*codec)) return GF_OUT_OF_MEM; /*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)) { /*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; sOpt = gf_modules_get_option((GF_BaseInterface *)plug, "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_modules_set_option((GF_BaseInterface *)plug, "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_modules_get_option((GF_BaseInterface *)plug, "OpenHEVC", "NumThreads"); if (!sOpt) { char szO[100]; sprintf(szO, "%d", detected_nb_threads); gf_modules_set_option((GF_BaseInterface *)plug, "OpenHEVC", "NumThreads", szO); nb_threads = detected_nb_threads; } else { nb_threads = atoi(sOpt); } if (nb_threads > detected_nb_threads) { GF_LOG(GF_LOG_CODEC, GF_LOG_WARNING, ("[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_CODEC, GF_LOG_INFO, ("[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_modules_get_option((GF_BaseInterface *)plug, "OpenHEVC", "CBUnits"); if (!sOpt) gf_modules_set_option((GF_BaseInterface *)plug, "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_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; } } sOpt = gf_modules_get_option((GF_BaseInterface *)plug, "Systems", "Output8bit"); if (!sOpt) gf_modules_set_option((GF_BaseInterface *)plug, "Systems", "Output8bit", (ffd->display_bpp>8) ? "no" : "yes"); if (sOpt && !strcmp(sOpt, "yes")) ffd->output_as_8bit = GF_TRUE; if (ffd->output_as_8bit && (ffd->stride > (u32) (*ctx)->width)) { ffd->stride /=2; ffd->out_size /= 2; ffd->conv_to_8bit = GF_TRUE; } 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); } #endif #ifdef FFMPEG_SWSCALE if (*sws) { sws_freeContext(*sws); *sws = NULL; } #endif if (ffd->conv_buffer) { gf_free(ffd->conv_buffer); ffd->conv_buffer = NULL; } 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 = FF_INPUT_BUFFER_PADDING_SIZE; return GF_OK; case GF_CODEC_REORDER: capability->cap.valueInt = 1; return GF_OK; case GF_CODEC_DIRECT_OUTPUT: capability->cap.valueBool = GF_TRUE; 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->conv_buffer) capability->cap.valueInt = ffd->base_ctx->width; 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; if (ffd->conv_buffer) capability->cap.valueInt = GF_PIXEL_YV12; break; /*ffmpeg performs frame reordering internally*/ case GF_CODEC_REORDER: 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 = FF_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_DISPLAY_BPP: ffd->display_bpp = capability.cap.valueInt; return GF_OK; 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_DIRECT_OUTPUT: ffd->direct_output = GF_TRUE; return GF_OK; default: /*return unsupported to avoid confusion by the player (like color space changing ...) */ return GF_NOT_SUPPORTED; } } 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) { #ifdef USE_AVCODEC2 AVPacket pkt; #endif AVPicture pict; u32 pix_out; s32 w, h, gotpic, stride; u32 outsize; AVCodecContext *ctx; AVCodec **codec; AVFrame *frame; #ifdef FFMPEG_SWSCALE struct SwsContext **cached_sws; #endif FFDec *ffd = (FFDec*)plug->privateStack; #ifdef FFMPEG_SWSCALE 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; #endif /*audio stream*/ if (ffd->st==GF_STREAM_AUDIO) { s32 len; u32 buf_size = (*outBufferLength); (*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_U8) { u32 i, size = ffd->audio_frame->nb_samples * ctx->channels; s16 *output = (s16 *) outBuffer; s8 *input = (s8 *) ffd->audio_frame->data; 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; shift = 1<<31; for (i=0; i<size; i++) { output [i] = input[i] * shift; } } else if (ffd->audio_frame->format==AV_SAMPLE_FMT_S16) { memcpy(outBuffer, ffd->audio_frame->data, sizeof(char) * ffd->audio_frame->nb_samples * ctx->channels*2); } else { GF_LOG(GF_LOG_WARNING, GF_LOG_CODEC, ("[FFMPEG Decoder] Raw Audio format %d not supoorted\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; } if ( ctx->codec_id == CODEC_ID_RAWVIDEO) { 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; } *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 = 1; } 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; return GF_BUFFER_TOO_SMALL; } stride = frame->linesize[0]; #ifndef NO_10bit if ((ctx->pix_fmt == PIX_FMT_YUV420P10LE) && ffd->output_as_8bit && (frame->linesize[0] >= 2*w) ) { ffd->conv_to_8bit = GF_TRUE; stride=w; } #endif /*recompute outsize in case on-the-fly change*/ if ((w != ctx->width) || (h != ctx->height) || (ffd->direct_output && (stride != ffd->stride)) || ((ffd->out_pix_fmt==GF_PIXEL_YV12) && (ctx->pix_fmt != PIX_FMT_YUV420P) && !ffd->output_as_8bit ) //need to realloc the conversion buffer || (ffd->conv_to_8bit && !ffd->conv_buffer && ffd->direct_output) ) { ffd->stride = (!ffd->conv_to_8bit && ffd->direct_output) ? frame->linesize[0] : ctx->width; if (ffd->out_pix_fmt == GF_PIXEL_RGB_24) { outsize = ctx->width * ctx->height * 3; } #ifndef NO_10bit //this YUV format is handled natively in GPAC else if ((ctx->pix_fmt == PIX_FMT_YUV420P10LE) && !ffd->output_as_8bit) { ffd->stride = ffd->direct_output ? frame->linesize[0] : ctx->width*2; outsize = ffd->stride * ctx->height * 3 / 2; ffd->out_pix_fmt = GF_PIXEL_YV12_10; } #endif //the rest will be YUV else { outsize = ffd->stride * ctx->height * 3 / 2; ffd->out_pix_fmt = GF_PIXEL_YV12; } 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->conv_to_8bit && ffd->direct_output) { ffd->conv_buffer = (char*)gf_realloc(ffd->conv_buffer, sizeof(char)*ffd->out_size); } 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; 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 if (ffd->direct_output && !ffd->conv_to_8bit) { *outBufferLength = ffd->out_size; return GF_OK; } if (ffd->conv_to_8bit) { GF_VideoSurface dst; memset(&dst, 0, sizeof(GF_VideoSurface)); dst.width = ctx->width; dst.height = ctx->height; dst.pitch_y = ctx->width; dst.video_buffer = ffd->direct_output ? ffd->conv_buffer : outBuffer; dst.pixel_format = GF_PIXEL_YV12; gf_color_write_yv12_10_to_yuv(&dst, (u8 *) frame->data[0], frame->data[1], frame->data[2], frame->linesize[0], ctx->width, ctx->height, NULL, GF_FALSE); *outBufferLength = ffd->out_size; return GF_OK; } if (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 { 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 { 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; #ifndef NO_10bit //this YUV format is handled natively in GPAC if (ctx->pix_fmt==PIX_FMT_YUV420P10LE) { pix_out = PIX_FMT_YUV420P10LE; } #endif if (!mmlevel && frame->interlaced_frame) { avpicture_deinterlace((AVPicture *) frame, (AVPicture *) frame, ctx->pix_fmt, ctx->width, ctx->height); } } pict.data[3] = 0; pict.linesize[3] = 0; #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) int sz = sws_scale((*cached_sws), frame->data, frame->linesize, 0, ctx->height, pict.data, pict.linesize); #else int sz = sws_scale((*cached_sws), (const uint8_t * const*)frame->data, frame->linesize, 0, ctx->height, pict.data, pict.linesize); #endif assert( sz > 0 ); } #endif *outBufferLength = ffd->out_size; #endif return GF_OK; } 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->conv_buffer) { *pY_or_RGB = (u8 *) ffd->conv_buffer; *pU = (u8 *) ffd->conv_buffer + ffd->stride * ffd->base_ctx->height; *pV = (u8 *) ffd->conv_buffer + 5*ffd->stride * ffd->base_ctx->height/4; return GF_OK; } 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; } 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); } 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; } } /*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_ref_base) 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_ref_base) 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; 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; /* 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") ); GF_SAFEALLOC(ptr , GF_MediaDecoder); GF_SAFEALLOC(priv , FFDec); ptr->privateStack = priv; 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; 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); }