diff options
Diffstat (limited to 'libavformat')
450 files changed, 32517 insertions, 7438 deletions
diff --git a/libavformat/4xm.c b/libavformat/4xm.c index 7a87c3695a..6e63c67399 100644 --- a/libavformat/4xm.c +++ b/libavformat/4xm.c @@ -2,20 +2,20 @@ * 4X Technologies .4xm File Demuxer (no muxer) * Copyright (c) 2003 The ffmpeg Project * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -131,8 +131,11 @@ static int parse_strk(AVFormatContext *s, return AVERROR_INVALIDDATA; track = AV_RL32(buf + 8); - if (track < 0) + if ((unsigned)track >= UINT_MAX / sizeof(AudioTrack) - 1) { + av_log(s, AV_LOG_ERROR, "current_track too large\n"); return AVERROR_INVALIDDATA; + } + if (track + 1 > fourxm->track_count) { if (av_reallocp_array(&fourxm->tracks, track + 1, sizeof(AudioTrack))) return AVERROR(ENOMEM); @@ -152,6 +155,11 @@ static int parse_strk(AVFormatContext *s, av_log(s, AV_LOG_ERROR, "audio header invalid\n"); return AVERROR_INVALIDDATA; } + if (!fourxm->tracks[track].adpcm && fourxm->tracks[track].bits<8) { + av_log(s, AV_LOG_ERROR, "bits unspecified for non ADPCM\n"); + return AVERROR_INVALIDDATA; + } + /* allocate a new AVStream */ st = avformat_new_stream(s, NULL); if (!st) @@ -219,9 +227,14 @@ static int fourxm_read_header(AVFormatContext *s) for (i = 0; i < header_size - 8; i++) { fourcc_tag = AV_RL32(&header[i]); size = AV_RL32(&header[i + 4]); + if (size > header_size - i - 8 && (fourcc_tag == vtrk_TAG || fourcc_tag == strk_TAG)) { + av_log(s, AV_LOG_ERROR, "chunk larger than array %d>%d\n", size, header_size - i - 8); + return AVERROR_INVALIDDATA; + } if (fourcc_tag == std__TAG) { if (header_size - i < 16) { + av_log(s, AV_LOG_ERROR, "std TAG truncated\n"); ret = AVERROR_INVALIDDATA; goto fail; } @@ -277,7 +290,7 @@ static int fourxm_read_packet(AVFormatContext *s, return ret; fourcc_tag = AV_RL32(&header[0]); size = AV_RL32(&header[4]); - if (pb->eof_reached) + if (url_feof(pb)) return AVERROR(EIO); switch (fourcc_tag) { case LIST_TAG: diff --git a/libavformat/Makefile b/libavformat/Makefile index a83c60e87e..69544aa41f 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -1,3 +1,5 @@ +include $(SUBDIR)../config.mak + NAME = avformat FFLIBS = avcodec avutil @@ -12,7 +14,6 @@ OBJS = allformats.o \ format.o \ id3v1.o \ id3v2.o \ - log2_tab.o \ metadata.o \ mux.o \ options.o \ @@ -51,47 +52,66 @@ OBJS-$(CONFIG_RTPDEC) += rdt.o \ rtpdec_xiph.o \ srtp.o OBJS-$(CONFIG_RTPENC_CHAIN) += rtpenc_chain.o rtp.o +OBJS-$(CONFIG_SHARED) += log2_tab.o # muxers/demuxers OBJS-$(CONFIG_A64_MUXER) += a64.o rawenc.o -OBJS-$(CONFIG_AAC_DEMUXER) += aacdec.o rawdec.o +OBJS-$(CONFIG_AAC_DEMUXER) += aacdec.o apetag.o img2.o rawdec.o OBJS-$(CONFIG_AC3_DEMUXER) += ac3dec.o rawdec.o OBJS-$(CONFIG_AC3_MUXER) += rawenc.o +OBJS-$(CONFIG_ACT_DEMUXER) += act.o +OBJS-$(CONFIG_ADF_DEMUXER) += bintext.o sauce.o +OBJS-$(CONFIG_ADP_DEMUXER) += adp.o OBJS-$(CONFIG_ADX_DEMUXER) += adxdec.o OBJS-$(CONFIG_ADX_MUXER) += rawenc.o -OBJS-$(CONFIG_ADTS_MUXER) += adtsenc.o +OBJS-$(CONFIG_ADTS_MUXER) += adtsenc.o apetag.o OBJS-$(CONFIG_AEA_DEMUXER) += aea.o pcm.o -OBJS-$(CONFIG_AIFF_DEMUXER) += aiffdec.o pcm.o -OBJS-$(CONFIG_AIFF_MUXER) += aiffenc.o +OBJS-$(CONFIG_AFC_DEMUXER) += afc.o +OBJS-$(CONFIG_AIFF_DEMUXER) += aiffdec.o pcm.o isom.o \ + mov_chan.o +OBJS-$(CONFIG_AIFF_MUXER) += aiffenc.o isom.o id3v2enc.o OBJS-$(CONFIG_AMR_DEMUXER) += amr.o OBJS-$(CONFIG_AMR_MUXER) += amr.o OBJS-$(CONFIG_ANM_DEMUXER) += anm.o OBJS-$(CONFIG_APC_DEMUXER) += apc.o OBJS-$(CONFIG_APE_DEMUXER) += ape.o apetag.o img2.o +OBJS-$(CONFIG_AQTITLE_DEMUXER) += aqtitledec.o subtitles.o OBJS-$(CONFIG_ASF_DEMUXER) += asfdec.o asf.o asfcrypt.o \ avlanguage.o OBJS-$(CONFIG_ASF_MUXER) += asfenc.o asf.o -OBJS-$(CONFIG_ASS_DEMUXER) += assdec.o +OBJS-$(CONFIG_ASS_DEMUXER) += assdec.o subtitles.o OBJS-$(CONFIG_ASS_MUXER) += assenc.o +OBJS-$(CONFIG_AST_DEMUXER) += ast.o astdec.o +OBJS-$(CONFIG_AST_MUXER) += ast.o astenc.o OBJS-$(CONFIG_AU_DEMUXER) += au.o pcm.o OBJS-$(CONFIG_AU_MUXER) += au.o rawenc.o OBJS-$(CONFIG_AVI_DEMUXER) += avidec.o OBJS-$(CONFIG_AVI_MUXER) += avienc.o OBJS-$(CONFIG_AVISYNTH) += avisynth.o OBJS-$(CONFIG_AVM2_MUXER) += swfenc.o swf.o +OBJS-$(CONFIG_AVR_DEMUXER) += avr.o pcm.o OBJS-$(CONFIG_AVS_DEMUXER) += avs.o vocdec.o voc.o OBJS-$(CONFIG_BETHSOFTVID_DEMUXER) += bethsoftvid.o OBJS-$(CONFIG_BFI_DEMUXER) += bfi.o OBJS-$(CONFIG_BINK_DEMUXER) += bink.o +OBJS-$(CONFIG_BINTEXT_DEMUXER) += bintext.o sauce.o +OBJS-$(CONFIG_BIT_DEMUXER) += bit.o +OBJS-$(CONFIG_BIT_MUXER) += bit.o OBJS-$(CONFIG_BMV_DEMUXER) += bmv.o +OBJS-$(CONFIG_BOA_DEMUXER) += boadec.o +OBJS-$(CONFIG_BRSTM_DEMUXER) += brstm.o OBJS-$(CONFIG_C93_DEMUXER) += c93.o vocdec.o voc.o OBJS-$(CONFIG_CAF_DEMUXER) += cafdec.o caf.o mov.o mov_chan.o \ isom.o +OBJS-$(CONFIG_CAF_MUXER) += cafenc.o caf.o riff.o isom.o OBJS-$(CONFIG_CAVSVIDEO_DEMUXER) += cavsvideodec.o rawdec.o OBJS-$(CONFIG_CAVSVIDEO_MUXER) += rawenc.o OBJS-$(CONFIG_CDG_DEMUXER) += cdg.o OBJS-$(CONFIG_CDXL_DEMUXER) += cdxl.o +OBJS-$(CONFIG_CONCAT_DEMUXER) += concatdec.o OBJS-$(CONFIG_CRC_MUXER) += crcenc.o +OBJS-$(CONFIG_DATA_DEMUXER) += rawdec.o +OBJS-$(CONFIG_DATA_MUXER) += rawdec.o OBJS-$(CONFIG_DAUD_DEMUXER) += daud.o OBJS-$(CONFIG_DAUD_MUXER) += daud.o OBJS-$(CONFIG_DFA_DEMUXER) += dfa.o @@ -100,6 +120,7 @@ OBJS-$(CONFIG_DIRAC_MUXER) += rawenc.o OBJS-$(CONFIG_DNXHD_DEMUXER) += dnxhddec.o rawdec.o OBJS-$(CONFIG_DNXHD_MUXER) += rawenc.o OBJS-$(CONFIG_DSICIN_DEMUXER) += dsicin.o +OBJS-$(CONFIG_DTSHD_DEMUXER) += dtshddec.o OBJS-$(CONFIG_DTS_DEMUXER) += dtsdec.o rawdec.o OBJS-$(CONFIG_DTS_MUXER) += rawenc.o OBJS-$(CONFIG_DV_DEMUXER) += dv.o @@ -109,6 +130,7 @@ OBJS-$(CONFIG_EA_CDATA_DEMUXER) += eacdata.o OBJS-$(CONFIG_EA_DEMUXER) += electronicarts.o OBJS-$(CONFIG_EAC3_DEMUXER) += ac3dec.o rawdec.o OBJS-$(CONFIG_EAC3_MUXER) += rawenc.o +OBJS-$(CONFIG_EPAF_DEMUXER) += epafdec.o pcm.o OBJS-$(CONFIG_FFM_DEMUXER) += ffmdec.o OBJS-$(CONFIG_FFM_MUXER) += ffmenc.o OBJS-$(CONFIG_FFMETADATA_DEMUXER) += ffmetadec.o @@ -127,13 +149,17 @@ OBJS-$(CONFIG_FLV_MUXER) += flvenc.o avc.o OBJS-$(CONFIG_FOURXM_DEMUXER) += 4xm.o OBJS-$(CONFIG_FRAMECRC_MUXER) += framecrcenc.o framehash.o OBJS-$(CONFIG_FRAMEMD5_MUXER) += md5enc.o framehash.o +OBJS-$(CONFIG_FRM_DEMUXER) += frmdec.o OBJS-$(CONFIG_GIF_MUXER) += gif.o +OBJS-$(CONFIG_GIF_DEMUXER) += gifdec.o OBJS-$(CONFIG_GSM_DEMUXER) += gsmdec.o OBJS-$(CONFIG_GXF_DEMUXER) += gxf.o OBJS-$(CONFIG_GXF_MUXER) += gxfenc.o audiointerleave.o OBJS-$(CONFIG_G722_DEMUXER) += g722.o rawdec.o OBJS-$(CONFIG_G722_MUXER) += rawenc.o OBJS-$(CONFIG_G723_1_DEMUXER) += g723_1.o +OBJS-$(CONFIG_G723_1_MUXER) += rawenc.o +OBJS-$(CONFIG_G729_DEMUXER) += g729dec.o OBJS-$(CONFIG_H261_DEMUXER) += h261dec.o rawdec.o OBJS-$(CONFIG_H261_MUXER) += rawenc.o OBJS-$(CONFIG_H263_DEMUXER) += h263dec.o rawdec.o @@ -144,7 +170,10 @@ OBJS-$(CONFIG_HEVC_DEMUXER) += hevcdec.o rawdec.o OBJS-$(CONFIG_HLS_DEMUXER) += hls.o OBJS-$(CONFIG_HLS_MUXER) += hlsenc.o OBJS-$(CONFIG_HNM_DEMUXER) += hnm.o +OBJS-$(CONFIG_ICO_DEMUXER) += icodec.o +OBJS-$(CONFIG_ICO_MUXER) += icoenc.o OBJS-$(CONFIG_IDCIN_DEMUXER) += idcin.o +OBJS-$(CONFIG_IDF_DEMUXER) += bintext.o sauce.o OBJS-$(CONFIG_IFF_DEMUXER) += iff.o OBJS-$(CONFIG_ILBC_DEMUXER) += ilbc.o OBJS-$(CONFIG_ILBC_MUXER) += ilbc.o @@ -154,14 +183,20 @@ OBJS-$(CONFIG_IMAGE2PIPE_DEMUXER) += img2dec.o img2.o OBJS-$(CONFIG_IMAGE2PIPE_MUXER) += img2enc.o img2.o OBJS-$(CONFIG_INGENIENT_DEMUXER) += ingenientdec.o rawdec.o OBJS-$(CONFIG_IPMOVIE_DEMUXER) += ipmovie.o +OBJS-$(CONFIG_IRCAM_DEMUXER) += ircamdec.o ircam.o pcm.o +OBJS-$(CONFIG_IRCAM_MUXER) += ircamenc.o ircam.o rawenc.o OBJS-$(CONFIG_ISS_DEMUXER) += iss.o OBJS-$(CONFIG_IV8_DEMUXER) += iv8.o OBJS-$(CONFIG_IVF_DEMUXER) += ivfdec.o OBJS-$(CONFIG_IVF_MUXER) += ivfenc.o +OBJS-$(CONFIG_JACOSUB_DEMUXER) += jacosubdec.o subtitles.o +OBJS-$(CONFIG_JACOSUB_MUXER) += jacosubenc.o rawenc.o OBJS-$(CONFIG_JV_DEMUXER) += jvdec.o OBJS-$(CONFIG_LATM_DEMUXER) += rawdec.o -OBJS-$(CONFIG_LATM_MUXER) += latmenc.o +OBJS-$(CONFIG_LATM_MUXER) += latmenc.o rawenc.o OBJS-$(CONFIG_LMLM4_DEMUXER) += lmlm4.o +OBJS-$(CONFIG_LOAS_DEMUXER) += loasdec.o rawdec.o +OBJS-$(CONFIG_LVF_DEMUXER) += lvfdec.o OBJS-$(CONFIG_LXF_DEMUXER) += lxfdec.o OBJS-$(CONFIG_M4V_DEMUXER) += m4vdec.o rawdec.o OBJS-$(CONFIG_M4V_MUXER) += rawenc.o @@ -171,16 +206,19 @@ OBJS-$(CONFIG_MATROSKA_MUXER) += matroskaenc.o matroska.o \ isom.o avc.o \ flacenc_header.o avlanguage.o wv.o OBJS-$(CONFIG_MD5_MUXER) += md5enc.o +OBJS-$(CONFIG_MGSTS_DEMUXER) += mgsts.o +OBJS-$(CONFIG_MICRODVD_DEMUXER) += microdvddec.o subtitles.o +OBJS-$(CONFIG_MICRODVD_MUXER) += microdvdenc.o OBJS-$(CONFIG_MJPEG_DEMUXER) += rawdec.o OBJS-$(CONFIG_MJPEG_MUXER) += rawenc.o OBJS-$(CONFIG_MLP_DEMUXER) += rawdec.o OBJS-$(CONFIG_MLP_MUXER) += rawenc.o OBJS-$(CONFIG_MM_DEMUXER) += mm.o -OBJS-$(CONFIG_MMF_DEMUXER) += mmf.o pcm.o -OBJS-$(CONFIG_MMF_MUXER) += mmf.o +OBJS-$(CONFIG_MMF_DEMUXER) += mmf.o +OBJS-$(CONFIG_MMF_MUXER) += mmf.o rawenc.o OBJS-$(CONFIG_MOV_DEMUXER) += mov.o isom.o mov_chan.o OBJS-$(CONFIG_MOV_MUXER) += movenc.o isom.o avc.o \ - movenchint.o mov_chan.o + movenchint.o mov_chan.o rtp.o OBJS-$(CONFIG_MP2_MUXER) += mp3enc.o rawenc.o id3v2enc.o OBJS-$(CONFIG_MP3_DEMUXER) += mp3dec.o OBJS-$(CONFIG_MP3_MUXER) += mp3enc.o rawenc.o id3v2enc.o @@ -198,13 +236,17 @@ OBJS-$(CONFIG_MPEGTS_DEMUXER) += mpegts.o isom.o OBJS-$(CONFIG_MPEGTS_MUXER) += mpegtsenc.o OBJS-$(CONFIG_MPEGVIDEO_DEMUXER) += mpegvideodec.o rawdec.o OBJS-$(CONFIG_MPJPEG_MUXER) += mpjpeg.o +OBJS-$(CONFIG_MPL2_DEMUXER) += mpl2dec.o subtitles.o +OBJS-$(CONFIG_MPSUB_DEMUXER) += mpsubdec.o subtitles.o OBJS-$(CONFIG_MSNWC_TCP_DEMUXER) += msnwc_tcp.o OBJS-$(CONFIG_MTV_DEMUXER) += mtv.o OBJS-$(CONFIG_MVI_DEMUXER) += mvi.o +OBJS-$(CONFIG_MV_DEMUXER) += mvdec.o OBJS-$(CONFIG_MXF_DEMUXER) += mxfdec.o mxf.o OBJS-$(CONFIG_MXF_MUXER) += mxfenc.o mxf.o audiointerleave.o OBJS-$(CONFIG_MXG_DEMUXER) += mxg.o OBJS-$(CONFIG_NC_DEMUXER) += ncdec.o +OBJS-$(CONFIG_NISTSPHERE_DEMUXER) += nistspheredec.o pcm.o OBJS-$(CONFIG_NSV_DEMUXER) += nsvdec.o OBJS-$(CONFIG_NULL_MUXER) += nullenc.o OBJS-$(CONFIG_NUT_DEMUXER) += nutdec.o nut.o @@ -226,6 +268,7 @@ OBJS-$(CONFIG_OGG_MUXER) += oggenc.o \ vorbiscomment.o OBJS-$(CONFIG_OMA_DEMUXER) += omadec.o pcm.o oma.o OBJS-$(CONFIG_OMA_MUXER) += omaenc.o rawenc.o oma.o id3v2enc.o +OBJS-$(CONFIG_PAF_DEMUXER) += paf.o OBJS-$(CONFIG_PCM_ALAW_DEMUXER) += pcmdec.o pcm.o OBJS-$(CONFIG_PCM_ALAW_MUXER) += pcmenc.o rawenc.o OBJS-$(CONFIG_PCM_F32BE_DEMUXER) += pcmdec.o pcm.o @@ -266,17 +309,22 @@ OBJS-$(CONFIG_PCM_U32LE_DEMUXER) += pcmdec.o pcm.o OBJS-$(CONFIG_PCM_U32LE_MUXER) += pcmenc.o rawenc.o OBJS-$(CONFIG_PCM_U8_DEMUXER) += pcmdec.o pcm.o OBJS-$(CONFIG_PCM_U8_MUXER) += pcmenc.o rawenc.o +OBJS-$(CONFIG_PJS_DEMUXER) += pjsdec.o subtitles.o OBJS-$(CONFIG_PMP_DEMUXER) += pmpdec.o OBJS-$(CONFIG_PVA_DEMUXER) += pva.o +OBJS-$(CONFIG_PVF_DEMUXER) += pvfdec.o pcm.o OBJS-$(CONFIG_QCP_DEMUXER) += qcp.o OBJS-$(CONFIG_R3D_DEMUXER) += r3d.o OBJS-$(CONFIG_RAWVIDEO_DEMUXER) += rawvideodec.o OBJS-$(CONFIG_RAWVIDEO_MUXER) += rawenc.o +OBJS-$(CONFIG_REALTEXT_DEMUXER) += realtextdec.o subtitles.o +OBJS-$(CONFIG_REDSPARK_DEMUXER) += redspark.o OBJS-$(CONFIG_RL2_DEMUXER) += rl2.o OBJS-$(CONFIG_RM_DEMUXER) += rmdec.o rm.o rmsipr.o OBJS-$(CONFIG_RM_MUXER) += rmenc.o rm.o OBJS-$(CONFIG_ROQ_DEMUXER) += idroqdec.o OBJS-$(CONFIG_ROQ_MUXER) += idroqenc.o rawenc.o +OBJS-$(CONFIG_RSD_DEMUXER) += rsd.o OBJS-$(CONFIG_RSO_DEMUXER) += rsodec.o rso.o pcm.o OBJS-$(CONFIG_RSO_MUXER) += rsoenc.o rso.o OBJS-$(CONFIG_RPL_DEMUXER) += rpl.o @@ -297,8 +345,10 @@ OBJS-$(CONFIG_RTSP_DEMUXER) += rtsp.o rtspdec.o httpauth.o \ urldecode.o OBJS-$(CONFIG_RTSP_MUXER) += rtsp.o rtspenc.o httpauth.o \ urldecode.o +OBJS-$(CONFIG_SAMI_DEMUXER) += samidec.o subtitles.o OBJS-$(CONFIG_SAP_DEMUXER) += sapdec.o OBJS-$(CONFIG_SAP_MUXER) += sapenc.o +OBJS-$(CONFIG_SBG_DEMUXER) += sbgdec.o OBJS-$(CONFIG_SDP_DEMUXER) += rtsp.o OBJS-$(CONFIG_SEGAFILM_DEMUXER) += segafilm.o OBJS-$(CONFIG_SEGMENT_MUXER) += segment.o @@ -308,46 +358,61 @@ OBJS-$(CONFIG_SMACKER_DEMUXER) += smacker.o OBJS-$(CONFIG_SMJPEG_DEMUXER) += smjpegdec.o smjpeg.o OBJS-$(CONFIG_SMJPEG_MUXER) += smjpegenc.o smjpeg.o OBJS-$(CONFIG_SMOOTHSTREAMING_MUXER) += smoothstreamingenc.o isom.o +OBJS-$(CONFIG_SMUSH_DEMUXER) += smush.o OBJS-$(CONFIG_SOL_DEMUXER) += sol.o pcm.o OBJS-$(CONFIG_SOX_DEMUXER) += soxdec.o pcm.o -OBJS-$(CONFIG_SOX_MUXER) += soxenc.o +OBJS-$(CONFIG_SOX_MUXER) += soxenc.o rawenc.o OBJS-$(CONFIG_SPDIF_DEMUXER) += spdif.o spdifdec.o OBJS-$(CONFIG_SPDIF_MUXER) += spdif.o spdifenc.o -OBJS-$(CONFIG_SRT_DEMUXER) += srtdec.o -OBJS-$(CONFIG_SRT_MUXER) += rawenc.o +OBJS-$(CONFIG_SRT_DEMUXER) += srtdec.o subtitles.o +OBJS-$(CONFIG_SRT_MUXER) += srtenc.o OBJS-$(CONFIG_STR_DEMUXER) += psxstr.o +OBJS-$(CONFIG_SUBVIEWER1_DEMUXER) += subviewer1dec.o subtitles.o +OBJS-$(CONFIG_SUBVIEWER_DEMUXER) += subviewerdec.o subtitles.o OBJS-$(CONFIG_SWF_DEMUXER) += swfdec.o swf.o OBJS-$(CONFIG_SWF_MUXER) += swfenc.o swf.o OBJS-$(CONFIG_TAK_DEMUXER) += takdec.o apetag.o img2.o rawdec.o +OBJS-$(CONFIG_TEDCAPTIONS_DEMUXER) += tedcaptionsdec.o subtitles.o +OBJS-$(CONFIG_TEE_MUXER) += tee.o OBJS-$(CONFIG_THP_DEMUXER) += thp.o OBJS-$(CONFIG_TIERTEXSEQ_DEMUXER) += tiertexseq.o +OBJS-$(CONFIG_MKVTIMESTAMP_V2_MUXER) += mkvtimestamp_v2.o OBJS-$(CONFIG_TMV_DEMUXER) += tmv.o OBJS-$(CONFIG_TRUEHD_DEMUXER) += rawdec.o OBJS-$(CONFIG_TRUEHD_MUXER) += rawenc.o -OBJS-$(CONFIG_TTA_DEMUXER) += tta.o +OBJS-$(CONFIG_TTA_DEMUXER) += tta.o apetag.o img2.o OBJS-$(CONFIG_TTY_DEMUXER) += tty.o sauce.o OBJS-$(CONFIG_TXD_DEMUXER) += txd.o OBJS-$(CONFIG_VC1_DEMUXER) += rawdec.o +OBJS-$(CONFIG_VC1_MUXER) += rawenc.o OBJS-$(CONFIG_VC1T_DEMUXER) += vc1test.o OBJS-$(CONFIG_VC1T_MUXER) += vc1testenc.o +OBJS-$(CONFIG_VIVO_DEMUXER) += vivo.o OBJS-$(CONFIG_VMD_DEMUXER) += sierravmd.o +OBJS-$(CONFIG_VOBSUB_DEMUXER) += subtitles.o # mpeg demuxer is in the dependencies OBJS-$(CONFIG_VOC_DEMUXER) += vocdec.o voc.o OBJS-$(CONFIG_VOC_MUXER) += vocenc.o voc.o +OBJS-$(CONFIG_VPLAYER_DEMUXER) += vplayerdec.o subtitles.o OBJS-$(CONFIG_VQF_DEMUXER) += vqf.o -OBJS-$(CONFIG_W64_DEMUXER) += wavdec.o pcm.o +OBJS-$(CONFIG_W64_DEMUXER) += wavdec.o w64.o pcm.o +OBJS-$(CONFIG_W64_MUXER) += wavenc.o w64.o OBJS-$(CONFIG_WAV_DEMUXER) += wavdec.o pcm.o OBJS-$(CONFIG_WAV_MUXER) += wavenc.o OBJS-$(CONFIG_WC3_DEMUXER) += wc3movie.o OBJS-$(CONFIG_WEBM_MUXER) += matroskaenc.o matroska.o \ isom.o avc.o \ flacenc_header.o avlanguage.o wv.o +OBJS-$(CONFIG_WEBVTT_DEMUXER) += webvttdec.o subtitles.o +OBJS-$(CONFIG_WEBVTT_MUXER) += webvttenc.o OBJS-$(CONFIG_WSAUD_DEMUXER) += westwood_aud.o OBJS-$(CONFIG_WSVQA_DEMUXER) += westwood_vqa.o -OBJS-$(CONFIG_WTV_DEMUXER) += wtv.o asfdec.o asf.o asfcrypt.o \ +OBJS-$(CONFIG_WTV_DEMUXER) += wtvdec.o wtv_common.o asfdec.o asf.o asfcrypt.o \ avlanguage.o mpegts.o isom.o +OBJS-$(CONFIG_WTV_MUXER) += wtvenc.o wtv_common.o asf.o asfenc.o OBJS-$(CONFIG_WV_DEMUXER) += wvdec.o wv.o apetag.o img2.o OBJS-$(CONFIG_WV_MUXER) += wvenc.o wv.o apetag.o img2.o OBJS-$(CONFIG_XA_DEMUXER) += xa.o +OBJS-$(CONFIG_XBIN_DEMUXER) += bintext.o sauce.o OBJS-$(CONFIG_XMV_DEMUXER) += xmv.o OBJS-$(CONFIG_XWMA_DEMUXER) += xwma.o OBJS-$(CONFIG_YOP_DEMUXER) += yop.o @@ -355,15 +420,25 @@ OBJS-$(CONFIG_YUV4MPEGPIPE_MUXER) += yuv4mpeg.o OBJS-$(CONFIG_YUV4MPEGPIPE_DEMUXER) += yuv4mpeg.o # external libraries +OBJS-$(CONFIG_LIBGME_DEMUXER) += libgme.o +OBJS-$(CONFIG_LIBMODPLUG_DEMUXER) += libmodplug.o +OBJS-$(CONFIG_LIBNUT_DEMUXER) += libnut.o +OBJS-$(CONFIG_LIBNUT_MUXER) += libnut.o +OBJS-$(CONFIG_LIBQUVI_DEMUXER) += libquvi.o OBJS-$(CONFIG_LIBRTMP) += librtmp.o +OBJS-$(CONFIG_LIBSSH_PROTOCOL) += libssh.o # protocols I/O OBJS-$(CONFIG_APPLEHTTP_PROTOCOL) += hlsproto.o +OBJS-$(CONFIG_BLURAY_PROTOCOL) += bluray.o +OBJS-$(CONFIG_CACHE_PROTOCOL) += cache.o OBJS-$(CONFIG_CONCAT_PROTOCOL) += concat.o OBJS-$(CONFIG_CRYPTO_PROTOCOL) += crypto.o +OBJS-$(CONFIG_DATA_PROTOCOL) += data_uri.o OBJS-$(CONFIG_FFRTMPCRYPT_PROTOCOL) += rtmpcrypt.o rtmpdh.o OBJS-$(CONFIG_FFRTMPHTTP_PROTOCOL) += rtmphttp.o OBJS-$(CONFIG_FILE_PROTOCOL) += file.o +OBJS-$(CONFIG_FTP_PROTOCOL) += ftp.o OBJS-$(CONFIG_GOPHER_PROTOCOL) += gopher.o OBJS-$(CONFIG_HLS_PROTOCOL) += hlsproto.o OBJS-$(CONFIG_HTTP_PROTOCOL) += http.o httpauth.o urldecode.o @@ -389,10 +464,6 @@ OBJS-$(CONFIG_UNIX_PROTOCOL) += unix.o SKIPHEADERS-$(CONFIG_FFRTMPCRYPT_PROTOCOL) += rtmpdh.h SKIPHEADERS-$(CONFIG_NETWORK) += network.h rtsp.h - -EXAMPLES = metadata \ - output \ - TESTPROGS = seek \ srtp \ url \ @@ -403,6 +474,4 @@ TOOLS = aviocat \ ismindex \ pktdumper \ probetest \ - -$(SUBDIR)output-example$(EXESUF): ELIBS = $(patsubst %,$(LD_LIB),swscale avutil) -$(SUBDIR)output-example$(EXESUF): $(call PATH_LIBNAME,swscale) + seek_print \ diff --git a/libavformat/a64.c b/libavformat/a64.c index 15a0475703..786c75cc90 100644 --- a/libavformat/a64.c +++ b/libavformat/a64.c @@ -2,25 +2,24 @@ * a64 muxer * Copyright (c) 2009 Tobias Bindhammer * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "libavcodec/avcodec.h" -#include "libavcodec/a64enc.h" #include "libavcodec/bytestream.h" #include "avformat.h" #include "rawenc.h" diff --git a/libavformat/aacdec.c b/libavformat/aacdec.c index 8d87ce63ff..d93e75ec52 100644 --- a/libavformat/aacdec.c +++ b/libavformat/aacdec.c @@ -3,20 +3,20 @@ * Copyright (c) 2008 Michael Niedermayer <michaelni@gmx.at> * Copyright (c) 2009 Robert Swain ( rob opendot cl ) * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -25,16 +25,16 @@ #include "internal.h" #include "rawdec.h" #include "id3v1.h" - +#include "apetag.h" static int adts_aac_probe(AVProbeData *p) { int max_frames = 0, first_frames = 0; int fsize, frames; - uint8_t *buf0 = p->buf; - uint8_t *buf2; - uint8_t *buf; - uint8_t *end = buf0 + p->buf_size - 7; + const uint8_t *buf0 = p->buf; + const uint8_t *buf2; + const uint8_t *buf; + const uint8_t *end = buf0 + p->buf_size - 7; buf = buf0; @@ -48,6 +48,7 @@ static int adts_aac_probe(AVProbeData *p) fsize = (AV_RB32(buf2 + 3) >> 13) & 0x1FFF; if(fsize < 7) break; + fsize = FFMIN(fsize, end - buf2); buf2 += fsize; } max_frames = FFMAX(max_frames, frames); @@ -71,9 +72,15 @@ static int adts_aac_read_header(AVFormatContext *s) st->codec->codec_type = AVMEDIA_TYPE_AUDIO; st->codec->codec_id = s->iformat->raw_codec_id; - st->need_parsing = AVSTREAM_PARSE_FULL; + st->need_parsing = AVSTREAM_PARSE_FULL_RAW; ff_id3v1_read(s); + if (s->pb->seekable && + !av_dict_get(s->metadata, "", NULL, AV_DICT_IGNORE_SUFFIX)) { + int64_t cur = avio_tell(s->pb); + ff_ape_parse_tag(s); + avio_seek(s->pb, cur, SEEK_SET); + } //LCM of all possible ADTS sample rates avpriv_set_pts_info(st, 64, 1, 28224000); diff --git a/libavformat/ac3dec.c b/libavformat/ac3dec.c index 4ceffa54dd..3db23397d0 100644 --- a/libavformat/ac3dec.c +++ b/libavformat/ac3dec.c @@ -2,20 +2,20 @@ * RAW AC-3 and E-AC-3 demuxer * Copyright (c) 2007 Justin Ruggles <justin.ruggles@gmail.com> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -27,7 +27,7 @@ static int ac3_eac3_probe(AVProbeData *p, enum AVCodecID expected_codec_id) { int max_frames, first_frames = 0, frames; - uint8_t *buf, *buf2, *end; + const uint8_t *buf, *buf2, *end; AC3HeaderInfo hdr; GetBitContext gbc; enum AVCodecID codec_id = AV_CODEC_ID_AC3; @@ -37,14 +37,36 @@ static int ac3_eac3_probe(AVProbeData *p, enum AVCodecID expected_codec_id) end = buf + p->buf_size; for(; buf < end; buf++) { + if(buf > p->buf && !(buf[0] == 0x0B && buf[1] == 0x77) + && !(buf[0] == 0x77 && buf[1] == 0x0B) ) + continue; buf2 = buf; for(frames = 0; buf2 < end; frames++) { - init_get_bits(&gbc, buf2, 54); + uint8_t buf3[4096]; + int i; + if(!memcmp(buf2, "\x1\x10\0\0\0\0\0\0", 8)) + buf2+=16; + if (buf[0] == 0x77 && buf[1] == 0x0B) { + for(i=0; i<8; i+=2) { + buf3[i ] = buf[i+1]; + buf3[i+1] = buf[i ]; + } + init_get_bits(&gbc, buf3, 54); + }else + init_get_bits(&gbc, buf2, 54); if(avpriv_ac3_parse_header(&gbc, &hdr) < 0) break; - if(buf2 + hdr.frame_size > end || - av_crc(av_crc_get_table(AV_CRC_16_ANSI), 0, buf2 + 2, hdr.frame_size - 2)) + if(buf2 + hdr.frame_size > end) + break; + if (buf[0] == 0x77 && buf[1] == 0x0B) { + av_assert0(hdr.frame_size <= sizeof(buf3)); + for(i=8; i<hdr.frame_size; i+=2) { + buf3[i ] = buf[i+1]; + buf3[i+1] = buf[i ]; + } + } + if(av_crc(av_crc_get_table(AV_CRC_16_ANSI), 0, gbc.buffer + 2, hdr.frame_size - 2)) break; if (hdr.bitstream_id > 10) codec_id = AV_CODEC_ID_EAC3; @@ -57,31 +79,11 @@ static int ac3_eac3_probe(AVProbeData *p, enum AVCodecID expected_codec_id) if(codec_id != expected_codec_id) return 0; // keep this in sync with mp3 probe, both need to avoid // issues with MPEG-files! - if (first_frames >= 4) return AVPROBE_SCORE_EXTENSION + 1; - - if (max_frames) { - int pes = 0, i; - unsigned int code = -1; - -#define VIDEO_ID 0x000001e0 -#define AUDIO_ID 0x000001c0 - /* do a search for mpegps headers to be able to properly bias - * towards mpegps if we detect this stream as both. */ - for (i = 0; i<p->buf_size; i++) { - code = (code << 8) + p->buf[i]; - if ((code & 0xffffff00) == 0x100) { - if ((code & 0x1f0) == VIDEO_ID) pes++; - else if((code & 0x1e0) == AUDIO_ID) pes++; - } - } - - if (pes) - max_frames = (max_frames + pes - 1) / pes; - } - if (max_frames > 500) return AVPROBE_SCORE_EXTENSION; - else if (max_frames >= 4) return AVPROBE_SCORE_EXTENSION / 2; - else if (max_frames >= 1) return 1; - else return 0; + if (first_frames>=4) return AVPROBE_SCORE_EXTENSION + 1; + else if(max_frames>200)return AVPROBE_SCORE_EXTENSION; + else if(max_frames>=4) return AVPROBE_SCORE_EXTENSION/2; + else if(max_frames>=1) return 1; + else return 0; } #if CONFIG_AC3_DEMUXER diff --git a/libavformat/act.c b/libavformat/act.c new file mode 100644 index 0000000000..3f223d57b6 --- /dev/null +++ b/libavformat/act.c @@ -0,0 +1,207 @@ +/* + * ACT file format demuxer + * Copyright (c) 2007-2008 Vladimir Voroshilov + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include "avformat.h" +#include "riff.h" +#include "internal.h" +#include "libavcodec/get_bits.h" + +#define CHUNK_SIZE 512 +#define RIFF_TAG MKTAG('R','I','F','F') +#define WAVE_TAG MKTAG('W','A','V','E') + +typedef struct{ + int bytes_left_in_chunk; + uint8_t audio_buffer[22];///< temporary buffer for ACT frame + char second_packet; ///< 1 - if temporary buffer contains valid (second) G.729 packet +} ACTContext; + +static int probe(AVProbeData *p) +{ + int i; + + if ((AV_RL32(&p->buf[0]) != RIFF_TAG) || + (AV_RL32(&p->buf[8]) != WAVE_TAG) || + (AV_RL32(&p->buf[16]) != 16)) + return 0; + + //We can't be sure that this is ACT and not regular WAV + if (p->buf_size<512) + return 0; + + for(i=44; i<256; i++) + if(p->buf[i]) + return 0; + + if(p->buf[256]!=0x84) + return 0; + + for(i=264; i<512; i++) + if(p->buf[i]) + return 0; + + return AVPROBE_SCORE_MAX; +} + +static int read_header(AVFormatContext *s) +{ + ACTContext* ctx = s->priv_data; + AVIOContext *pb = s->pb; + int size; + AVStream* st; + + int min,sec,msec; + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + avio_skip(pb, 16); + size=avio_rl32(pb); + ff_get_wav_header(pb, st->codec, size); + + /* + 8000Hz (Fine-rec) file format has 10 bytes long + packets with 10ms of sound data in them + */ + if (st->codec->sample_rate != 8000) { + av_log(s, AV_LOG_ERROR, "Sample rate %d is not supported.\n", st->codec->sample_rate); + return AVERROR_INVALIDDATA; + } + + st->codec->frame_size=80; + st->codec->channels=1; + avpriv_set_pts_info(st, 64, 1, 100); + + st->codec->codec_id=AV_CODEC_ID_G729; + + avio_seek(pb, 257, SEEK_SET); + msec=avio_rl16(pb); + sec=avio_r8(pb); + min=avio_rl32(pb); + + st->duration = av_rescale(1000*(min*60+sec)+msec, st->codec->sample_rate, 1000 * st->codec->frame_size); + + ctx->bytes_left_in_chunk=CHUNK_SIZE; + + avio_seek(pb, 512, SEEK_SET); + + return 0; +} + + +static int read_packet(AVFormatContext *s, + AVPacket *pkt) +{ + ACTContext *ctx = s->priv_data; + AVIOContext *pb = s->pb; + int ret; + int frame_size=s->streams[0]->codec->sample_rate==8000?10:22; + + + if(s->streams[0]->codec->sample_rate==8000) + ret=av_new_packet(pkt, 10); + else + ret=av_new_packet(pkt, 11); + + if(ret) + return ret; + + if(s->streams[0]->codec->sample_rate==4400 && !ctx->second_packet) + { + ret = avio_read(pb, ctx->audio_buffer, frame_size); + + if(ret<0) + return ret; + if(ret!=frame_size) + return AVERROR(EIO); + + pkt->data[0]=ctx->audio_buffer[11]; + pkt->data[1]=ctx->audio_buffer[0]; + pkt->data[2]=ctx->audio_buffer[12]; + pkt->data[3]=ctx->audio_buffer[1]; + pkt->data[4]=ctx->audio_buffer[13]; + pkt->data[5]=ctx->audio_buffer[2]; + pkt->data[6]=ctx->audio_buffer[14]; + pkt->data[7]=ctx->audio_buffer[3]; + pkt->data[8]=ctx->audio_buffer[15]; + pkt->data[9]=ctx->audio_buffer[4]; + pkt->data[10]=ctx->audio_buffer[16]; + + ctx->second_packet=1; + } + else if(s->streams[0]->codec->sample_rate==4400 && ctx->second_packet) + { + pkt->data[0]=ctx->audio_buffer[5]; + pkt->data[1]=ctx->audio_buffer[17]; + pkt->data[2]=ctx->audio_buffer[6]; + pkt->data[3]=ctx->audio_buffer[18]; + pkt->data[4]=ctx->audio_buffer[7]; + pkt->data[5]=ctx->audio_buffer[19]; + pkt->data[6]=ctx->audio_buffer[8]; + pkt->data[7]=ctx->audio_buffer[20]; + pkt->data[8]=ctx->audio_buffer[9]; + pkt->data[9]=ctx->audio_buffer[21]; + pkt->data[10]=ctx->audio_buffer[10]; + + ctx->second_packet=0; + } + else // 8000 Hz + { + ret = avio_read(pb, ctx->audio_buffer, frame_size); + + if(ret<0) + return ret; + if(ret!=frame_size) + return AVERROR(EIO); + + pkt->data[0]=ctx->audio_buffer[5]; + pkt->data[1]=ctx->audio_buffer[0]; + pkt->data[2]=ctx->audio_buffer[6]; + pkt->data[3]=ctx->audio_buffer[1]; + pkt->data[4]=ctx->audio_buffer[7]; + pkt->data[5]=ctx->audio_buffer[2]; + pkt->data[6]=ctx->audio_buffer[8]; + pkt->data[7]=ctx->audio_buffer[3]; + pkt->data[8]=ctx->audio_buffer[9]; + pkt->data[9]=ctx->audio_buffer[4]; + } + + ctx->bytes_left_in_chunk -= frame_size; + + if(ctx->bytes_left_in_chunk < frame_size) + { + avio_skip(pb, ctx->bytes_left_in_chunk); + ctx->bytes_left_in_chunk=CHUNK_SIZE; + } + + pkt->duration=1; + + return ret; +} + +AVInputFormat ff_act_demuxer = { + .name = "act", + .long_name = "ACT Voice file format", + .priv_data_size = sizeof(ACTContext), + .read_probe = probe, + .read_header = read_header, + .read_packet = read_packet, +}; diff --git a/libavformat/adp.c b/libavformat/adp.c new file mode 100644 index 0000000000..c5feac4fa5 --- /dev/null +++ b/libavformat/adp.c @@ -0,0 +1,91 @@ +/* + * ADP demuxer + * Copyright (c) 2013 James Almer + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/channel_layout.h" +#include "libavutil/intreadwrite.h" +#include "avformat.h" +#include "internal.h" + +static int adp_probe(AVProbeData *p) +{ + int i; + + if (p->buf_size < 32) + return 0; + + for (i = 0; i < p->buf_size - 3; i+=32) + if (p->buf[i] != p->buf[i+2] || p->buf[i+1] != p->buf[i+3]) + return 0; + + return p->buf_size < 260 ? 1 : AVPROBE_SCORE_MAX / 4; +} + +static int adp_read_header(AVFormatContext *s) +{ + AVStream *st; + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_id = AV_CODEC_ID_ADPCM_DTK; + st->codec->channel_layout = AV_CH_LAYOUT_STEREO; + st->codec->channels = 2; + st->codec->sample_rate = 48000; + st->start_time = 0; + if (s->pb->seekable) + st->duration = av_get_audio_frame_duration(st->codec, avio_size(s->pb)); + + avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate); + + return 0; +} + +static int adp_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + int ret, size = 1024; + + if (url_feof(s->pb)) + return AVERROR_EOF; + + ret = av_get_packet(s->pb, pkt, size); + + if (ret != size) { + if (ret < 0) { + av_free_packet(pkt); + return ret; + } + av_shrink_packet(pkt, ret); + } + pkt->stream_index = 0; + + return ret; +} + +AVInputFormat ff_adp_demuxer = { + .name = "adp", + .long_name = NULL_IF_CONFIG_SMALL("ADP"), + .read_probe = adp_probe, + .read_header = adp_read_header, + .read_packet = adp_read_packet, + .extensions = "adp,dtk", +}; diff --git a/libavformat/adtsenc.c b/libavformat/adtsenc.c index e7c9ca6ce1..f25f966129 100644 --- a/libavformat/adtsenc.c +++ b/libavformat/adtsenc.c @@ -3,20 +3,20 @@ * Copyright (c) 2006 Baptiste Coudurier <baptiste.coudurier@smartjog.com> * Mans Rullgard <mans@mansr.com> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -24,16 +24,20 @@ #include "libavcodec/put_bits.h" #include "libavcodec/avcodec.h" #include "libavcodec/mpeg4audio.h" +#include "libavutil/opt.h" #include "avformat.h" +#include "apetag.h" #define ADTS_HEADER_SIZE 7 typedef struct { + AVClass *class; int write_adts; int objecttype; int sample_rate_index; int channel_conf; int pce_size; + int apetag; uint8_t pce_data[MAX_PCE_SIZE]; } ADTSContext; @@ -162,6 +166,30 @@ static int adts_write_packet(AVFormatContext *s, AVPacket *pkt) return 0; } +static int adts_write_trailer(AVFormatContext *s) +{ + ADTSContext *adts = s->priv_data; + + if (adts->apetag) + ff_ape_write_tag(s); + + return 0; +} + +#define ENC AV_OPT_FLAG_ENCODING_PARAM +#define OFFSET(obj) offsetof(ADTSContext, obj) +static const AVOption options[] = { + { "write_apetag", "Enable APE tag writing", OFFSET(apetag), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, ENC}, + { NULL }, +}; + +static const AVClass adts_muxer_class = { + .class_name = "ADTS muxer", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + AVOutputFormat ff_adts_muxer = { .name = "adts", .long_name = NULL_IF_CONFIG_SMALL("ADTS AAC (Advanced Audio Coding)"), @@ -172,4 +200,6 @@ AVOutputFormat ff_adts_muxer = { .video_codec = AV_CODEC_ID_NONE, .write_header = adts_write_header, .write_packet = adts_write_packet, + .write_trailer = adts_write_trailer, + .priv_class = &adts_muxer_class, }; diff --git a/libavformat/adxdec.c b/libavformat/adxdec.c index 49e19307d5..0a0436f184 100644 --- a/libavformat/adxdec.c +++ b/libavformat/adxdec.c @@ -1,20 +1,20 @@ /* * Copyright (c) 2011 Justin Ruggles * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -78,8 +78,7 @@ static int adx_read_header(AVFormatContext *s) c->header_size = avio_rb16(s->pb) + 4; avio_seek(s->pb, -4, SEEK_CUR); - avctx->extradata = av_mallocz(c->header_size + FF_INPUT_BUFFER_PADDING_SIZE); - if (!avctx->extradata) + if (ff_alloc_extradata(avctx, c->header_size)) return AVERROR(ENOMEM); if (avio_read(s->pb, avctx->extradata, c->header_size) < c->header_size) { av_freep(&avctx->extradata); diff --git a/libavformat/aea.c b/libavformat/aea.c index 7675009bbc..15b9b1cd32 100644 --- a/libavformat/aea.c +++ b/libavformat/aea.c @@ -3,20 +3,20 @@ * * Copyright (c) 2009 Benjamin Larsson * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/afc.c b/libavformat/afc.c new file mode 100644 index 0000000000..8dbd232fc1 --- /dev/null +++ b/libavformat/afc.c @@ -0,0 +1,79 @@ +/* + * AFC demuxer + * Copyright (c) 2012 Paul B Mahol + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/channel_layout.h" +#include "avformat.h" +#include "internal.h" + +typedef struct AFCDemuxContext { + int64_t data_end; +} AFCDemuxContext; + +static int afc_read_header(AVFormatContext *s) +{ + AFCDemuxContext *c = s->priv_data; + AVStream *st; + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_id = AV_CODEC_ID_ADPCM_AFC; + st->codec->channels = 2; + st->codec->channel_layout = AV_CH_LAYOUT_STEREO; + + if (ff_alloc_extradata(st->codec, 1)) + return AVERROR(ENOMEM); + st->codec->extradata[0] = 8 * st->codec->channels; + + c->data_end = avio_rb32(s->pb) + 32LL; + st->duration = avio_rb32(s->pb); + st->codec->sample_rate = avio_rb16(s->pb); + avio_skip(s->pb, 22); + avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate); + + return 0; +} + +static int afc_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + AFCDemuxContext *c = s->priv_data; + int64_t size; + int ret; + + size = FFMIN(c->data_end - avio_tell(s->pb), 18 * 128); + if (size <= 0) + return AVERROR_EOF; + + ret = av_get_packet(s->pb, pkt, size); + pkt->stream_index = 0; + return ret; +} + +AVInputFormat ff_afc_demuxer = { + .name = "afc", + .long_name = NULL_IF_CONFIG_SMALL("AFC"), + .priv_data_size = sizeof(AFCDemuxContext), + .read_header = afc_read_header, + .read_packet = afc_read_packet, + .extensions = "afc", + .flags = AVFMT_NOBINSEARCH | AVFMT_NOGENSEARCH | AVFMT_NO_BYTE_SEEK, +}; diff --git a/libavformat/aiff.h b/libavformat/aiff.h index f88f957e71..4470254022 100644 --- a/libavformat/aiff.h +++ b/libavformat/aiff.h @@ -2,20 +2,20 @@ * AIFF/AIFF-C muxer/demuxer common header * Copyright (c) 2006 Patrick Guimond * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -33,16 +33,20 @@ static const AVCodecTag ff_codec_aiff_tags[] = { { AV_CODEC_ID_PCM_S16BE, MKTAG('N','O','N','E') }, { AV_CODEC_ID_PCM_S8, MKTAG('N','O','N','E') }, + { AV_CODEC_ID_PCM_U8, MKTAG('r','a','w',' ') }, { AV_CODEC_ID_PCM_S24BE, MKTAG('N','O','N','E') }, { AV_CODEC_ID_PCM_S32BE, MKTAG('N','O','N','E') }, { AV_CODEC_ID_PCM_F32BE, MKTAG('f','l','3','2') }, { AV_CODEC_ID_PCM_F64BE, MKTAG('f','l','6','4') }, { AV_CODEC_ID_PCM_ALAW, MKTAG('a','l','a','w') }, { AV_CODEC_ID_PCM_MULAW, MKTAG('u','l','a','w') }, + { AV_CODEC_ID_PCM_S24BE, MKTAG('i','n','2','4') }, + { AV_CODEC_ID_PCM_S32BE, MKTAG('i','n','3','2') }, { AV_CODEC_ID_MACE3, MKTAG('M','A','C','3') }, { AV_CODEC_ID_MACE6, MKTAG('M','A','C','6') }, { AV_CODEC_ID_GSM, MKTAG('G','S','M',' ') }, - { AV_CODEC_ID_ADPCM_G726, MKTAG('G','7','2','6') }, + { AV_CODEC_ID_ADPCM_G722, MKTAG('G','7','2','2') }, + { AV_CODEC_ID_ADPCM_G726LE, MKTAG('G','7','2','6') }, { AV_CODEC_ID_PCM_S16BE, MKTAG('t','w','o','s') }, { AV_CODEC_ID_PCM_S16LE, MKTAG('s','o','w','t') }, { AV_CODEC_ID_ADPCM_IMA_QT, MKTAG('i','m','a','4') }, diff --git a/libavformat/aiffdec.c b/libavformat/aiffdec.c index 6243bcb30f..6f82d9339a 100644 --- a/libavformat/aiffdec.c +++ b/libavformat/aiffdec.c @@ -2,29 +2,33 @@ * AIFF/AIFF-C demuxer * Copyright (c) 2006 Patrick Guimond * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "libavutil/intreadwrite.h" #include "libavutil/mathematics.h" #include "libavutil/dict.h" #include "avformat.h" #include "internal.h" #include "pcm.h" #include "aiff.h" +#include "isom.h" +#include "id3v2.h" +#include "mov_chan.h" #define AIFF 0 #define AIFF_C_VERSION1 0xA2805140 @@ -54,7 +58,7 @@ static int get_tag(AVIOContext *pb, uint32_t * tag) { int size; - if (pb->eof_reached) + if (url_feof(pb)) return AVERROR(EIO); *tag = avio_rl32(pb); @@ -70,19 +74,20 @@ static int get_tag(AVIOContext *pb, uint32_t * tag) static void get_meta(AVFormatContext *s, const char *key, int size) { uint8_t *str = av_malloc(size+1); - int res; - if (!str) { - avio_skip(s->pb, size); - return; - } - - res = avio_read(s->pb, str, size); - if (res < 0) - return; + if (str) { + int res = avio_read(s->pb, str, size); + if (res < 0){ + av_free(str); + return; + } + size += (size&1)-res; + str[res] = 0; + av_dict_set(&s->metadata, key, str, AV_DICT_DONT_STRDUP_VAL); + }else + size+= size&1; - str[res] = 0; - av_dict_set(&s->metadata, key, str, AV_DICT_DONT_STRDUP_VAL); + avio_skip(s->pb, size); } /* Returns the number of sound data frames or negative on error */ @@ -136,16 +141,17 @@ static unsigned int get_aiff_header(AVFormatContext *s, int size, case AV_CODEC_ID_MACE3: codec->block_align = 2*codec->channels; break; + case AV_CODEC_ID_ADPCM_G726LE: + codec->bits_per_coded_sample = 5; + case AV_CODEC_ID_ADPCM_G722: case AV_CODEC_ID_MACE6: codec->block_align = 1*codec->channels; break; case AV_CODEC_ID_GSM: codec->block_align = 33; break; - case AV_CODEC_ID_QCELP: - codec->block_align = 35; - break; default: + aiff->block_duration = 1; break; } if (codec->block_align > 0) @@ -156,7 +162,7 @@ static unsigned int get_aiff_header(AVFormatContext *s, int size, /* Block align needs to be computed in all cases, as the definition * is specific to applications -> here we use the WAVE format definition */ if (!codec->block_align) - codec->block_align = (codec->bits_per_coded_sample * codec->channels) >> 3; + codec->block_align = (av_get_bits_per_sample(codec->codec_id) * codec->channels) >> 3; if (aiff->block_duration) { codec->bit_rate = codec->sample_rate * (codec->block_align << 3) / @@ -185,13 +191,14 @@ static int aiff_probe(AVProbeData *p) /* aiff input */ static int aiff_read_header(AVFormatContext *s) { - int size, filesize; - int64_t offset = 0; + int ret, size, filesize; + int64_t offset = 0, position; uint32_t tag; unsigned version = AIFF_C_VERSION1; AVIOContext *pb = s->pb; AVStream * st; AIFFInputContext *aiff = s->priv_data; + ID3v2ExtraMeta *id3v2_extra_meta = NULL; /* check FORM header */ filesize = get_tag(pb, &tag); @@ -228,6 +235,18 @@ static int aiff_read_header(AVFormatContext *s) if (offset > 0) // COMM is after SSND goto got_sound; break; + case MKTAG('I', 'D', '3', ' '): + position = avio_tell(pb); + ff_id3v2_read(s, ID3v2_DEFAULT_MAGIC, &id3v2_extra_meta); + if (id3v2_extra_meta) + if ((ret = ff_id3v2_parse_apic(s, &id3v2_extra_meta)) < 0) { + ff_id3v2_free_extra_meta(&id3v2_extra_meta); + return ret; + } + ff_id3v2_free_extra_meta(&id3v2_extra_meta); + if (position + size > avio_tell(pb)) + avio_skip(pb, position + size - avio_tell(pb)); + break; case MKTAG('F', 'V', 'E', 'R'): /* Version chunk */ version = avio_rb32(pb); break; @@ -248,7 +267,7 @@ static int aiff_read_header(AVFormatContext *s) offset = avio_rb32(pb); /* Offset of sound data */ avio_rb32(pb); /* BlockSize... don't care */ offset += avio_tell(pb); /* Compute absolute data offset */ - if (st->codec->block_align) /* Assume COMM already parsed */ + if (st->codec->block_align && !pb->seekable) /* Assume COMM already parsed */ goto got_sound; if (!pb->seekable) { av_log(s, AV_LOG_ERROR, "file is not seekable\n"); @@ -259,11 +278,32 @@ static int aiff_read_header(AVFormatContext *s) case MKTAG('w', 'a', 'v', 'e'): if ((uint64_t)size > (1<<30)) return -1; - st->codec->extradata = av_mallocz(size + FF_INPUT_BUFFER_PADDING_SIZE); - if (!st->codec->extradata) + if (ff_alloc_extradata(st->codec, size)) return AVERROR(ENOMEM); - st->codec->extradata_size = size; avio_read(pb, st->codec->extradata, size); + if (st->codec->codec_id == AV_CODEC_ID_QDM2 && size>=12*4 && !st->codec->block_align) { + st->codec->block_align = AV_RB32(st->codec->extradata+11*4); + aiff->block_duration = AV_RB32(st->codec->extradata+9*4); + } else if (st->codec->codec_id == AV_CODEC_ID_QCELP) { + char rate = 0; + if (size >= 25) + rate = st->codec->extradata[24]; + switch (rate) { + case 'H': // RATE_HALF + st->codec->block_align = 17; + break; + case 'F': // RATE_FULL + default: + st->codec->block_align = 35; + } + aiff->block_duration = 160; + st->codec->bit_rate = st->codec->sample_rate * (st->codec->block_align << 3) / + aiff->block_duration; + } + break; + case MKTAG('C','H','A','N'): + if(ff_mov_read_chan(s, pb, st, size) < 0) + return AVERROR_INVALIDDATA; break; default: /* Jump */ if (size & 1) /* Always even aligned */ @@ -305,7 +345,7 @@ static int aiff_read_packet(AVFormatContext *s, return AVERROR_EOF; /* Now for that packet */ - if (st->codec->block_align >= 33) // GSM, QCLP, IMA4 + if (st->codec->block_align >= 17) // GSM, QCLP, IMA4 size = st->codec->block_align; else size = (MAX_SIZE / st->codec->block_align) * st->codec->block_align; @@ -314,6 +354,8 @@ static int aiff_read_packet(AVFormatContext *s, if (res < 0) return res; + if (size >= st->codec->block_align) + pkt->flags &= ~AV_PKT_FLAG_CORRUPT; /* Only one stream in an AIFF file */ pkt->stream_index = 0; pkt->duration = (res / st->codec->block_align) * aiff->block_duration; diff --git a/libavformat/aiffenc.c b/libavformat/aiffenc.c index abcc424596..7c0b4fb803 100644 --- a/libavformat/aiffenc.c +++ b/libavformat/aiffenc.c @@ -2,42 +2,124 @@ * AIFF/AIFF-C muxer * Copyright (c) 2006 Patrick Guimond * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "libavutil/intfloat.h" +#include "libavutil/opt.h" #include "avformat.h" #include "internal.h" #include "aiff.h" #include "avio_internal.h" +#include "isom.h" +#include "id3v2.h" typedef struct { + const AVClass *class; int64_t form; int64_t frames; int64_t ssnd; + int audio_stream_idx; + AVPacketList *pict_list; + int write_id3v2; + int id3v2_version; } AIFFOutputContext; +static int put_id3v2_tags(AVFormatContext *s, AIFFOutputContext *aiff) +{ + int ret; + uint64_t pos, end, size; + ID3v2EncContext id3v2 = { 0 }; + AVIOContext *pb = s->pb; + AVPacketList *pict_list = aiff->pict_list; + + if (!pb->seekable) + return 0; + + if (!s->metadata && !aiff->pict_list) + return 0; + + avio_wl32(pb, MKTAG('I', 'D', '3', ' ')); + avio_wb32(pb, 0); + pos = avio_tell(pb); + + ff_id3v2_start(&id3v2, pb, aiff->id3v2_version, ID3v2_DEFAULT_MAGIC); + ff_id3v2_write_metadata(s, &id3v2); + while (pict_list) { + if ((ret = ff_id3v2_write_apic(s, &id3v2, &pict_list->pkt)) < 0) + return ret; + pict_list = pict_list->next; + } + ff_id3v2_finish(&id3v2, pb); + + end = avio_tell(pb); + size = end - pos; + + /* Update chunk size */ + avio_seek(pb, pos - 4, SEEK_SET); + avio_wb32(pb, size); + avio_seek(pb, end, SEEK_SET); + + if (size & 1) + avio_w8(pb, 0); + + return 0; +} + +static void put_meta(AVFormatContext *s, const char *key, uint32_t id) +{ + AVDictionaryEntry *tag; + AVIOContext *pb = s->pb; + + if (tag = av_dict_get(s->metadata, key, NULL, 0)) { + int size = strlen(tag->value); + + avio_wl32(pb, id); + avio_wb32(pb, FFALIGN(size, 2)); + avio_write(pb, tag->value, size); + if (size & 1) + avio_w8(pb, 0); + } +} + static int aiff_write_header(AVFormatContext *s) { AIFFOutputContext *aiff = s->priv_data; AVIOContext *pb = s->pb; - AVCodecContext *enc = s->streams[0]->codec; + AVCodecContext *enc; uint64_t sample_rate; - int aifc = 0; + int i, aifc = 0; + + aiff->audio_stream_idx = -1; + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + if (aiff->audio_stream_idx < 0 && st->codec->codec_type == AVMEDIA_TYPE_AUDIO) { + aiff->audio_stream_idx = i; + } else if (st->codec->codec_type != AVMEDIA_TYPE_VIDEO) { + av_log(s, AV_LOG_ERROR, "Only audio streams and pictures are allowed in AIFF.\n"); + return AVERROR(EINVAL); + } + } + if (aiff->audio_stream_idx < 0) { + av_log(s, AV_LOG_ERROR, "No audio stream present.\n"); + return AVERROR(EINVAL); + } + + enc = s->streams[aiff->audio_stream_idx]->codec; /* First verify if format is ok */ if (!enc->codec_tag) @@ -52,7 +134,6 @@ static int aiff_write_header(AVFormatContext *s) ffio_wfourcc(pb, aifc ? "AIFC" : "AIFF"); if (aifc) { // compressed audio - enc->bits_per_coded_sample = 16; if (!enc->block_align) { av_log(s, AV_LOG_ERROR, "block align not set\n"); return -1; @@ -63,6 +144,17 @@ static int aiff_write_header(AVFormatContext *s) avio_wb32(pb, 0xA2805140); } + if (enc->channels > 2 && enc->channel_layout) { + ffio_wfourcc(pb, "CHAN"); + avio_wb32(pb, 12); + ff_mov_write_chan(pb, enc->channel_layout); + } + + put_meta(s, "title", MKTAG('N', 'A', 'M', 'E')); + put_meta(s, "author", MKTAG('A', 'U', 'T', 'H')); + put_meta(s, "copyright", MKTAG('(', 'c', ')', ' ')); + put_meta(s, "comment", MKTAG('A', 'N', 'N', 'O')); + /* Common chunk */ ffio_wfourcc(pb, "COMM"); avio_wb32(pb, aifc ? 24 : 18); /* size */ @@ -91,6 +183,12 @@ static int aiff_write_header(AVFormatContext *s) avio_wb16(pb, 0); } + if (enc->codec_tag == MKTAG('Q','D','M','2') && enc->extradata_size) { + ffio_wfourcc(pb, "wave"); + avio_wb32(pb, enc->extradata_size); + avio_write(pb, enc->extradata, enc->extradata_size); + } + /* Sound data chunk */ ffio_wfourcc(pb, "SSND"); aiff->ssnd = avio_tell(pb); /* Sound chunk size */ @@ -98,7 +196,8 @@ static int aiff_write_header(AVFormatContext *s) avio_wb32(pb, 0); /* Data offset */ avio_wb32(pb, 0); /* Block-size (block align) */ - avpriv_set_pts_info(s->streams[0], 64, 1, s->streams[0]->codec->sample_rate); + avpriv_set_pts_info(s->streams[aiff->audio_stream_idx], 64, 1, + s->streams[aiff->audio_stream_idx]->codec->sample_rate); /* Data is starting here */ avio_flush(pb); @@ -108,16 +207,54 @@ static int aiff_write_header(AVFormatContext *s) static int aiff_write_packet(AVFormatContext *s, AVPacket *pkt) { + AIFFOutputContext *aiff = s->priv_data; AVIOContext *pb = s->pb; - avio_write(pb, pkt->data, pkt->size); + if (pkt->stream_index == aiff->audio_stream_idx) + avio_write(pb, pkt->data, pkt->size); + else { + int ret; + AVPacketList *pict_list, *last; + + if (s->streams[pkt->stream_index]->codec->codec_type != AVMEDIA_TYPE_VIDEO) + return 0; + + /* warn only once for each stream */ + if (s->streams[pkt->stream_index]->nb_frames == 1) { + av_log(s, AV_LOG_WARNING, "Got more than one picture in stream %d," + " ignoring.\n", pkt->stream_index); + } + if (s->streams[pkt->stream_index]->nb_frames >= 1) + return 0; + + pict_list = av_mallocz(sizeof(AVPacketList)); + if (!pict_list) + return AVERROR(ENOMEM); + + if ((ret = av_copy_packet(&pict_list->pkt, pkt)) < 0) { + av_freep(&pict_list); + return ret; + } + + if (!aiff->pict_list) + aiff->pict_list = pict_list; + else { + last = aiff->pict_list; + while (last->next) + last = last->next; + last->next = pict_list; + } + } + return 0; } static int aiff_write_trailer(AVFormatContext *s) { + int ret; AVIOContext *pb = s->pb; AIFFOutputContext *aiff = s->priv_data; - AVCodecContext *enc = s->streams[0]->codec; + AVPacketList *pict_list = aiff->pict_list; + AVCodecContext *enc = s->streams[aiff->audio_stream_idx]->codec; /* Chunks sizes must be even */ int64_t file_size, end_size; @@ -128,10 +265,6 @@ static int aiff_write_trailer(AVFormatContext *s) } if (s->pb->seekable) { - /* File length */ - avio_seek(pb, aiff->form, SEEK_SET); - avio_wb32(pb, file_size - aiff->form - 4); - /* Number of sample frames */ avio_seek(pb, aiff->frames, SEEK_SET); avio_wb32(pb, (file_size-aiff->ssnd-12)/enc->block_align); @@ -143,12 +276,46 @@ static int aiff_write_trailer(AVFormatContext *s) /* return to the end */ avio_seek(pb, end_size, SEEK_SET); + /* Write ID3 tags */ + if (aiff->write_id3v2) + if ((ret = put_id3v2_tags(s, aiff)) < 0) + return ret; + + /* File length */ + file_size = avio_tell(pb); + avio_seek(pb, aiff->form, SEEK_SET); + avio_wb32(pb, file_size - aiff->form - 4); + avio_flush(pb); } + while (pict_list) { + AVPacketList *next = pict_list->next; + av_free_packet(&pict_list->pkt); + av_freep(&pict_list); + pict_list = next; + } + return 0; } +#define OFFSET(x) offsetof(AIFFOutputContext, x) +#define ENC AV_OPT_FLAG_ENCODING_PARAM +static const AVOption options[] = { + { "write_id3v2", "Enable ID3 tags writing.", + OFFSET(write_id3v2), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, ENC }, + { "id3v2_version", "Select ID3v2 version to write. Currently 3 and 4 are supported.", + OFFSET(id3v2_version), AV_OPT_TYPE_INT, {.i64 = 4}, 3, 4, ENC }, + { NULL }, +}; + +static const AVClass aiff_muxer_class = { + .class_name = "AIFF muxer", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + AVOutputFormat ff_aiff_muxer = { .name = "aiff", .long_name = NULL_IF_CONFIG_SMALL("Audio IFF"), @@ -156,9 +323,10 @@ AVOutputFormat ff_aiff_muxer = { .extensions = "aif,aiff,afc,aifc", .priv_data_size = sizeof(AIFFOutputContext), .audio_codec = AV_CODEC_ID_PCM_S16BE, - .video_codec = AV_CODEC_ID_NONE, + .video_codec = AV_CODEC_ID_PNG, .write_header = aiff_write_header, .write_packet = aiff_write_packet, .write_trailer = aiff_write_trailer, .codec_tag = (const AVCodecTag* const []){ ff_codec_aiff_tags, 0 }, + .priv_class = &aiff_muxer_class, }; diff --git a/libavformat/allformats.c b/libavformat/allformats.c index 363462e684..bba9573587 100644 --- a/libavformat/allformats.c +++ b/libavformat/allformats.c @@ -2,20 +2,20 @@ * Register all the formats and protocols * Copyright (c) 2000, 2001, 2002 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -62,43 +62,58 @@ void av_register_all(void) REGISTER_MUXER (A64, a64); REGISTER_DEMUXER (AAC, aac); REGISTER_MUXDEMUX(AC3, ac3); + REGISTER_DEMUXER (ACT, act); + REGISTER_DEMUXER (ADF, adf); + REGISTER_DEMUXER (ADP, adp); REGISTER_MUXER (ADTS, adts); REGISTER_MUXDEMUX(ADX, adx); REGISTER_DEMUXER (AEA, aea); + REGISTER_DEMUXER (AFC, afc); REGISTER_MUXDEMUX(AIFF, aiff); REGISTER_MUXDEMUX(AMR, amr); REGISTER_DEMUXER (ANM, anm); REGISTER_DEMUXER (APC, apc); REGISTER_DEMUXER (APE, ape); + REGISTER_DEMUXER (AQTITLE, aqtitle); REGISTER_MUXDEMUX(ASF, asf); REGISTER_MUXDEMUX(ASS, ass); + REGISTER_MUXDEMUX(AST, ast); REGISTER_MUXER (ASF_STREAM, asf_stream); REGISTER_MUXDEMUX(AU, au); REGISTER_MUXDEMUX(AVI, avi); REGISTER_DEMUXER (AVISYNTH, avisynth); REGISTER_MUXER (AVM2, avm2); + REGISTER_DEMUXER (AVR, avr); REGISTER_DEMUXER (AVS, avs); REGISTER_DEMUXER (BETHSOFTVID, bethsoftvid); REGISTER_DEMUXER (BFI, bfi); + REGISTER_DEMUXER (BINTEXT, bintext); REGISTER_DEMUXER (BINK, bink); + REGISTER_MUXDEMUX(BIT, bit); REGISTER_DEMUXER (BMV, bmv); + REGISTER_DEMUXER (BRSTM, brstm); + REGISTER_DEMUXER (BOA, boa); REGISTER_DEMUXER (C93, c93); - REGISTER_DEMUXER (CAF, caf); + REGISTER_MUXDEMUX(CAF, caf); REGISTER_MUXDEMUX(CAVSVIDEO, cavsvideo); REGISTER_DEMUXER (CDG, cdg); REGISTER_DEMUXER (CDXL, cdxl); + REGISTER_DEMUXER (CONCAT, concat); REGISTER_MUXER (CRC, crc); + REGISTER_MUXDEMUX(DATA, data); REGISTER_MUXDEMUX(DAUD, daud); REGISTER_DEMUXER (DFA, dfa); REGISTER_MUXDEMUX(DIRAC, dirac); REGISTER_MUXDEMUX(DNXHD, dnxhd); REGISTER_DEMUXER (DSICIN, dsicin); REGISTER_MUXDEMUX(DTS, dts); + REGISTER_DEMUXER (DTSHD, dtshd); REGISTER_MUXDEMUX(DV, dv); REGISTER_DEMUXER (DXA, dxa); REGISTER_DEMUXER (EA, ea); REGISTER_DEMUXER (EA_CDATA, ea_cdata); REGISTER_MUXDEMUX(EAC3, eac3); + REGISTER_DEMUXER (EPAF, epaf); REGISTER_MUXER (F4V, f4v); REGISTER_MUXDEMUX(FFM, ffm); REGISTER_MUXDEMUX(FFMETADATA, ffmetadata); @@ -109,9 +124,11 @@ void av_register_all(void) REGISTER_DEMUXER (FOURXM, fourxm); REGISTER_MUXER (FRAMECRC, framecrc); REGISTER_MUXER (FRAMEMD5, framemd5); + REGISTER_DEMUXER (FRM, frm); REGISTER_MUXDEMUX(G722, g722); - REGISTER_DEMUXER (G723_1, g723_1); - REGISTER_MUXER (GIF, gif); + REGISTER_MUXDEMUX(G723_1, g723_1); + REGISTER_DEMUXER (G729, g729); + REGISTER_MUXDEMUX(GIF, gif); REGISTER_DEMUXER (GSM, gsm); REGISTER_MUXDEMUX(GXF, gxf); REGISTER_MUXDEMUX(H261, h261); @@ -120,7 +137,9 @@ void av_register_all(void) REGISTER_DEMUXER (HEVC, hevc); REGISTER_MUXDEMUX(HLS, hls); REGISTER_DEMUXER (HNM, hnm); + REGISTER_MUXDEMUX(ICO, ico); REGISTER_DEMUXER (IDCIN, idcin); + REGISTER_DEMUXER (IDF, idf); REGISTER_DEMUXER (IFF, iff); REGISTER_MUXDEMUX(ILBC, ilbc); REGISTER_MUXDEMUX(IMAGE2, image2); @@ -128,18 +147,24 @@ void av_register_all(void) REGISTER_DEMUXER (INGENIENT, ingenient); REGISTER_DEMUXER (IPMOVIE, ipmovie); REGISTER_MUXER (IPOD, ipod); + REGISTER_MUXDEMUX(IRCAM, ircam); REGISTER_MUXER (ISMV, ismv); REGISTER_DEMUXER (ISS, iss); REGISTER_DEMUXER (IV8, iv8); REGISTER_MUXDEMUX(IVF, ivf); + REGISTER_MUXDEMUX(JACOSUB, jacosub); REGISTER_DEMUXER (JV, jv); REGISTER_MUXDEMUX(LATM, latm); REGISTER_DEMUXER (LMLM4, lmlm4); + REGISTER_DEMUXER (LOAS, loas); + REGISTER_DEMUXER (LVF, lvf); REGISTER_DEMUXER (LXF, lxf); REGISTER_MUXDEMUX(M4V, m4v); REGISTER_MUXER (MD5, md5); REGISTER_MUXDEMUX(MATROSKA, matroska); REGISTER_MUXER (MATROSKA_AUDIO, matroska_audio); + REGISTER_DEMUXER (MGSTS, mgsts); + REGISTER_MUXDEMUX(MICRODVD, microdvd); REGISTER_MUXDEMUX(MJPEG, mjpeg); REGISTER_MUXDEMUX(MLP, mlp); REGISTER_DEMUXER (MM, mm); @@ -162,19 +187,24 @@ void av_register_all(void) REGISTER_DEMUXER (MPEGTSRAW, mpegtsraw); REGISTER_DEMUXER (MPEGVIDEO, mpegvideo); REGISTER_MUXER (MPJPEG, mpjpeg); + REGISTER_DEMUXER (MPL2, mpl2); + REGISTER_DEMUXER (MPSUB, mpsub); REGISTER_DEMUXER (MSNWC_TCP, msnwc_tcp); REGISTER_DEMUXER (MTV, mtv); + REGISTER_DEMUXER (MV, mv); REGISTER_DEMUXER (MVI, mvi); REGISTER_MUXDEMUX(MXF, mxf); REGISTER_MUXER (MXF_D10, mxf_d10); REGISTER_DEMUXER (MXG, mxg); REGISTER_DEMUXER (NC, nc); + REGISTER_DEMUXER (NISTSPHERE, nistsphere); REGISTER_DEMUXER (NSV, nsv); REGISTER_MUXER (NULL, null); REGISTER_MUXDEMUX(NUT, nut); REGISTER_DEMUXER (NUV, nuv); REGISTER_MUXDEMUX(OGG, ogg); REGISTER_MUXDEMUX(OMA, oma); + REGISTER_DEMUXER (PAF, paf); REGISTER_MUXDEMUX(PCM_ALAW, pcm_alaw); REGISTER_MUXDEMUX(PCM_MULAW, pcm_mulaw); REGISTER_MUXDEMUX(PCM_F64BE, pcm_f64be); @@ -195,20 +225,27 @@ void av_register_all(void) REGISTER_MUXDEMUX(PCM_U16BE, pcm_u16be); REGISTER_MUXDEMUX(PCM_U16LE, pcm_u16le); REGISTER_MUXDEMUX(PCM_U8, pcm_u8); + REGISTER_DEMUXER (PJS, pjs); REGISTER_DEMUXER (PMP, pmp); REGISTER_MUXER (PSP, psp); REGISTER_DEMUXER (PVA, pva); + REGISTER_DEMUXER (PVF, pvf); REGISTER_DEMUXER (QCP, qcp); REGISTER_DEMUXER (R3D, r3d); REGISTER_MUXDEMUX(RAWVIDEO, rawvideo); + REGISTER_DEMUXER (REALTEXT, realtext); + REGISTER_DEMUXER (REDSPARK, redspark); REGISTER_DEMUXER (RL2, rl2); REGISTER_MUXDEMUX(RM, rm); REGISTER_MUXDEMUX(ROQ, roq); REGISTER_DEMUXER (RPL, rpl); + REGISTER_DEMUXER (RSD, rsd); REGISTER_MUXDEMUX(RSO, rso); REGISTER_MUXDEMUX(RTP, rtp); REGISTER_MUXDEMUX(RTSP, rtsp); + REGISTER_DEMUXER (SAMI, sami); REGISTER_MUXDEMUX(SAP, sap); + REGISTER_DEMUXER (SBG, sbg); REGISTER_DEMUXER (SDP, sdp); #if CONFIG_RTPDEC ff_register_rtp_dynamic_payload_handlers(); @@ -216,52 +253,68 @@ void av_register_all(void) #endif REGISTER_DEMUXER (SEGAFILM, segafilm); REGISTER_MUXER (SEGMENT, segment); + REGISTER_MUXER (SEGMENT, stream_segment); REGISTER_DEMUXER (SHORTEN, shorten); REGISTER_DEMUXER (SIFF, siff); REGISTER_DEMUXER (SMACKER, smacker); REGISTER_MUXDEMUX(SMJPEG, smjpeg); REGISTER_MUXER (SMOOTHSTREAMING, smoothstreaming); + REGISTER_DEMUXER (SMUSH, smush); REGISTER_DEMUXER (SOL, sol); REGISTER_MUXDEMUX(SOX, sox); REGISTER_MUXDEMUX(SPDIF, spdif); REGISTER_MUXDEMUX(SRT, srt); REGISTER_DEMUXER (STR, str); + REGISTER_DEMUXER (SUBVIEWER1, subviewer1); + REGISTER_DEMUXER (SUBVIEWER, subviewer); REGISTER_MUXDEMUX(SWF, swf); REGISTER_DEMUXER (TAK, tak); + REGISTER_MUXER (TEE, tee); + REGISTER_DEMUXER (TEDCAPTIONS, tedcaptions); REGISTER_MUXER (TG2, tg2); REGISTER_MUXER (TGP, tgp); REGISTER_DEMUXER (THP, thp); REGISTER_DEMUXER (TIERTEXSEQ, tiertexseq); + REGISTER_MUXER (MKVTIMESTAMP_V2, mkvtimestamp_v2); REGISTER_DEMUXER (TMV, tmv); REGISTER_MUXDEMUX(TRUEHD, truehd); REGISTER_DEMUXER (TTA, tta); REGISTER_DEMUXER (TXD, txd); REGISTER_DEMUXER (TTY, tty); - REGISTER_DEMUXER (VC1, vc1); + REGISTER_MUXDEMUX(VC1, vc1); REGISTER_MUXDEMUX(VC1T, vc1t); + REGISTER_DEMUXER (VIVO, vivo); REGISTER_DEMUXER (VMD, vmd); + REGISTER_DEMUXER (VOBSUB, vobsub); REGISTER_MUXDEMUX(VOC, voc); + REGISTER_DEMUXER (VPLAYER, vplayer); REGISTER_DEMUXER (VQF, vqf); - REGISTER_DEMUXER (W64, w64); + REGISTER_MUXDEMUX(W64, w64); REGISTER_MUXDEMUX(WAV, wav); REGISTER_DEMUXER (WC3, wc3); REGISTER_MUXER (WEBM, webm); + REGISTER_MUXDEMUX(WEBVTT, webvtt); REGISTER_DEMUXER (WSAUD, wsaud); REGISTER_DEMUXER (WSVQA, wsvqa); - REGISTER_DEMUXER (WTV, wtv); + REGISTER_MUXDEMUX(WTV, wtv); REGISTER_MUXDEMUX(WV, wv); REGISTER_DEMUXER (XA, xa); + REGISTER_DEMUXER (XBIN, xbin); REGISTER_DEMUXER (XMV, xmv); REGISTER_DEMUXER (XWMA, xwma); REGISTER_DEMUXER (YOP, yop); REGISTER_MUXDEMUX(YUV4MPEGPIPE, yuv4mpegpipe); /* protocols */ + REGISTER_PROTOCOL(BLURAY, bluray); + REGISTER_PROTOCOL(CACHE, cache); REGISTER_PROTOCOL(CONCAT, concat); REGISTER_PROTOCOL(CRYPTO, crypto); + REGISTER_PROTOCOL(DATA, data); REGISTER_PROTOCOL(FFRTMPCRYPT, ffrtmpcrypt); REGISTER_PROTOCOL(FFRTMPHTTP, ffrtmphttp); REGISTER_PROTOCOL(FILE, file); + REGISTER_PROTOCOL(FTP, ftp); REGISTER_PROTOCOL(GOPHER, gopher); REGISTER_PROTOCOL(HLS, hls); REGISTER_PROTOCOL(HTTP, http); @@ -286,9 +339,14 @@ void av_register_all(void) REGISTER_PROTOCOL(UNIX, unix); /* external libraries */ + REGISTER_DEMUXER (LIBGME, libgme); + REGISTER_DEMUXER (LIBMODPLUG, libmodplug); + REGISTER_MUXDEMUX(LIBNUT, libnut); + REGISTER_DEMUXER (LIBQUVI, libquvi); REGISTER_PROTOCOL(LIBRTMP, librtmp); REGISTER_PROTOCOL(LIBRTMPE, librtmpe); REGISTER_PROTOCOL(LIBRTMPS, librtmps); REGISTER_PROTOCOL(LIBRTMPT, librtmpt); REGISTER_PROTOCOL(LIBRTMPTE, librtmpte); + REGISTER_PROTOCOL(LIBSSH, libssh); } diff --git a/libavformat/amr.c b/libavformat/amr.c index 3b1a468e34..db9bb4e59d 100644 --- a/libavformat/amr.c +++ b/libavformat/amr.c @@ -2,20 +2,20 @@ * amr file format * Copyright (c) 2001 ffmpeg project * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -26,6 +26,7 @@ Only mono files are supported. */ +#include "libavutil/avassert.h" #include "libavutil/channel_layout.h" #include "avformat.h" #include "internal.h" @@ -111,7 +112,7 @@ static int amr_read_packet(AVFormatContext *s, AVPacket *pkt) int read, size = 0, toc, mode; int64_t pos = avio_tell(s->pb); - if (s->pb->eof_reached) { + if (url_feof(s->pb)) { return AVERROR(EIO); } @@ -131,8 +132,6 @@ static int amr_read_packet(AVFormatContext *s, AVPacket *pkt) }; size = packed_size[mode]; - } else { - assert(0); } if (!size || av_new_packet(pkt, size)) diff --git a/libavformat/anm.c b/libavformat/anm.c index f78149285b..1c585750e6 100644 --- a/libavformat/anm.c +++ b/libavformat/anm.c @@ -2,20 +2,20 @@ * Deluxe Paint Animation demuxer * Copyright (c) 2009 Peter Ross * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -175,7 +175,7 @@ static int read_packet(AVFormatContext *s, Page *p; int tmp, record_size; - if (s->pb->eof_reached) + if (url_feof(s->pb)) return AVERROR(EIO); if (anm->page < 0) diff --git a/libavformat/apc.c b/libavformat/apc.c index 0b6c5836c1..08ae9351dc 100644 --- a/libavformat/apc.c +++ b/libavformat/apc.c @@ -2,20 +2,20 @@ * CRYO APC audio format demuxer * Copyright (c) 2007 Anssi Hannula <anssi.hannula@gmail.com> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -23,6 +23,7 @@ #include "libavutil/channel_layout.h" #include "avformat.h" +#include "internal.h" static int apc_probe(AVProbeData *p) { @@ -51,10 +52,7 @@ static int apc_read_header(AVFormatContext *s) avio_rl32(pb); /* number of samples */ st->codec->sample_rate = avio_rl32(pb); - st->codec->extradata_size = 2 * 4; - st->codec->extradata = av_malloc(st->codec->extradata_size + - FF_INPUT_BUFFER_PADDING_SIZE); - if (!st->codec->extradata) + if (ff_alloc_extradata(st->codec, 2 * 4)) return AVERROR(ENOMEM); /* initial predictor values for adpcm decoder */ @@ -82,6 +80,7 @@ static int apc_read_packet(AVFormatContext *s, AVPacket *pkt) { if (av_get_packet(s->pb, pkt, MAX_READ_SIZE) <= 0) return AVERROR(EIO); + pkt->flags &= ~AV_PKT_FLAG_CORRUPT; pkt->stream_index = 0; return 0; } diff --git a/libavformat/ape.c b/libavformat/ape.c index d967a5d660..613a59d5f8 100644 --- a/libavformat/ape.c +++ b/libavformat/ape.c @@ -3,20 +3,20 @@ * Copyright (c) 2007 Benjamin Zores <ben@geexbox.org> * based upon libdemac from Dave Chapman. * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -38,8 +38,6 @@ #define MAC_FORMAT_FLAG_HAS_SEEK_ELEMENTS 16 // has the number of seek elements after the peak level #define MAC_FORMAT_FLAG_CREATE_WAV_HEADER 32 // create the wave header on decompression (not stored) -#define MAC_SUBFRAME_SIZE 4608 - #define APE_EXTRADATA_SIZE 6 typedef struct { @@ -172,14 +170,14 @@ static int ape_read_header(AVFormatContext * s) tag = avio_rl32(pb); if (tag != MKTAG('M', 'A', 'C', ' ')) - return -1; + return AVERROR_INVALIDDATA; ape->fileversion = avio_rl16(pb); if (ape->fileversion < APE_MIN_VERSION || ape->fileversion > APE_MAX_VERSION) { av_log(s, AV_LOG_ERROR, "Unsupported file version - %d.%02d\n", ape->fileversion / 1000, (ape->fileversion % 1000) / 10); - return -1; + return AVERROR_PATCHWELCOME; } if (ape->fileversion >= 3980) { @@ -258,7 +256,7 @@ static int ape_read_header(AVFormatContext * s) if(ape->totalframes > UINT_MAX / sizeof(APEFrame)){ av_log(s, AV_LOG_ERROR, "Too many frames: %"PRIu32"\n", ape->totalframes); - return -1; + return AVERROR_INVALIDDATA; } if (ape->seektablelength / sizeof(*ape->seektable) < ape->totalframes) { av_log(s, AV_LOG_ERROR, @@ -340,7 +338,7 @@ static int ape_read_header(AVFormatContext * s) /* now we are ready: build format streams */ st = avformat_new_stream(s, NULL); if (!st) - return -1; + return AVERROR(ENOMEM); total_blocks = (ape->totalframes == 0) ? 0 : ((ape->totalframes - 1) * ape->blocksperframe) + ape->finalframeblocks; @@ -353,11 +351,11 @@ static int ape_read_header(AVFormatContext * s) st->nb_frames = ape->totalframes; st->start_time = 0; - st->duration = total_blocks / MAC_SUBFRAME_SIZE; - avpriv_set_pts_info(st, 64, MAC_SUBFRAME_SIZE, ape->samplerate); + st->duration = total_blocks; + avpriv_set_pts_info(st, 64, 1, ape->samplerate); - st->codec->extradata = av_malloc(APE_EXTRADATA_SIZE); - st->codec->extradata_size = APE_EXTRADATA_SIZE; + if (ff_alloc_extradata(st->codec, APE_EXTRADATA_SIZE)) + return AVERROR(ENOMEM); AV_WL16(st->codec->extradata + 0, ape->fileversion); AV_WL16(st->codec->extradata + 2, ape->compressiontype); AV_WL16(st->codec->extradata + 4, ape->formatflags); @@ -366,7 +364,7 @@ static int ape_read_header(AVFormatContext * s) for (i = 0; i < ape->totalframes; i++) { ape->frames[i].pts = pts; av_add_index_entry(st, ape->frames[i].pos, ape->frames[i].pts, 0, 0, AVINDEX_KEYFRAME); - pts += ape->blocksperframe / MAC_SUBFRAME_SIZE; + pts += ape->blocksperframe; } /* try to read APE tags */ @@ -385,7 +383,7 @@ static int ape_read_packet(AVFormatContext * s, AVPacket * pkt) APEContext *ape = s->priv_data; uint32_t extra_size = 8; - if (s->pb->eof_reached) + if (url_feof(s->pb)) return AVERROR_EOF; if (ape->currentframe >= ape->totalframes) return AVERROR_EOF; @@ -413,6 +411,8 @@ static int ape_read_packet(AVFormatContext * s, AVPacket * pkt) AV_WL32(pkt->data , nblocks); AV_WL32(pkt->data + 4, ape->frames[ape->currentframe].skip); ret = avio_read(s->pb, pkt->data + extra_size, ape->frames[ape->currentframe].size); + if (ret < 0) + return ret; pkt->pts = ape->frames[ape->currentframe].pts; pkt->stream_index = 0; diff --git a/libavformat/apetag.c b/libavformat/apetag.c index d4be91c837..a376a0bc4a 100644 --- a/libavformat/apetag.c +++ b/libavformat/apetag.c @@ -3,20 +3,20 @@ * Copyright (c) 2007 Benjamin Zores <ben@geexbox.org> * based upon libdemac from Dave Chapman. * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -27,8 +27,6 @@ #include "apetag.h" #include "internal.h" -#define APE_TAG_VERSION 2000 -#define APE_TAG_FOOTER_BYTES 32 #define APE_TAG_FLAG_CONTAINS_HEADER (1 << 31) #define APE_TAG_FLAG_CONTAINS_FOOTER (1 << 30) #define APE_TAG_FLAG_IS_HEADER (1 << 29) @@ -90,14 +88,13 @@ static int ape_tag_read_field(AVFormatContext *s) st->attached_pic.stream_index = st->index; st->attached_pic.flags |= AV_PKT_FLAG_KEY; } else { - st->codec->extradata = av_malloc(size + FF_INPUT_BUFFER_PADDING_SIZE); - if (!st->codec->extradata) + if (ff_alloc_extradata(st->codec, size)) return AVERROR(ENOMEM); if (avio_read(pb, st->codec->extradata, size) != size) { av_freep(&st->codec->extradata); + st->codec->extradata_size = 0; return AVERROR(EIO); } - st->codec->extradata_size = size; st->codec->codec_type = AVMEDIA_TYPE_ATTACHMENT; } } else { @@ -130,7 +127,7 @@ int64_t ff_ape_parse_tag(AVFormatContext *s) avio_seek(pb, file_size - APE_TAG_FOOTER_BYTES, SEEK_SET); avio_read(pb, buf, 8); /* APETAGEX */ - if (strncmp(buf, "APETAGEX", 8)) { + if (strncmp(buf, APE_TAG_PREAMBLE, 8)) { return 0; } @@ -172,43 +169,61 @@ int64_t ff_ape_parse_tag(AVFormatContext *s) return tag_start; } +static int string_is_ascii(const uint8_t *str) +{ + while (*str && *str >= 0x20 && *str <= 0x7e ) str++; + return !*str; +} + int ff_ape_write_tag(AVFormatContext *s) { AVDictionaryEntry *e = NULL; - int64_t start, end; - int size, count = 0; - - if (!s->pb->seekable) - return 0; + int size, ret, count = 0; + AVIOContext *dyn_bc = NULL; + uint8_t *dyn_buf = NULL; - start = avio_tell(s->pb); - - // header - avio_write(s->pb, "APETAGEX", 8); // id - avio_wl32 (s->pb, APE_TAG_VERSION); // version - avio_wl32(s->pb, 0); // reserve space for size - avio_wl32(s->pb, 0); // reserve space for tag count + if ((ret = avio_open_dyn_buf(&dyn_bc)) < 0) + goto end; // flags - avio_wl32(s->pb, APE_TAG_FLAG_CONTAINS_HEADER | APE_TAG_FLAG_CONTAINS_FOOTER | + avio_wl32(dyn_bc, APE_TAG_FLAG_CONTAINS_HEADER | APE_TAG_FLAG_CONTAINS_FOOTER | APE_TAG_FLAG_IS_HEADER); - ffio_fill(s->pb, 0, 8); // reserved + ffio_fill(dyn_bc, 0, 8); // reserved while ((e = av_dict_get(s->metadata, "", e, AV_DICT_IGNORE_SUFFIX))) { - int val_len = strlen(e->value); + int val_len; + + if (!string_is_ascii(e->key)) { + av_log(s, AV_LOG_WARNING, "Non ASCII keys are not allowed\n"); + continue; + } - avio_wl32(s->pb, val_len); // value length - avio_wl32(s->pb, 0); // item flags - avio_put_str(s->pb, e->key); // key - avio_write(s->pb, e->value, val_len); // value + val_len = strlen(e->value); + avio_wl32(dyn_bc, val_len); // value length + avio_wl32(dyn_bc, 0); // item flags + avio_put_str(dyn_bc, e->key); // key + avio_write(dyn_bc, e->value, val_len); // value count++; } + if (!count) + goto end; + + size = avio_close_dyn_buf(dyn_bc, &dyn_buf); + if (size <= 0) + goto end; + size += 20; + + // header + avio_write(s->pb, "APETAGEX", 8); // id + avio_wl32(s->pb, APE_TAG_VERSION); // version + avio_wl32(s->pb, size); + avio_wl32(s->pb, count); - size = avio_tell(s->pb) - start; + avio_write(s->pb, dyn_buf, size - 20); // footer avio_write(s->pb, "APETAGEX", 8); // id - avio_wl32 (s->pb, APE_TAG_VERSION); // version + avio_wl32(s->pb, APE_TAG_VERSION); // version avio_wl32(s->pb, size); // size avio_wl32(s->pb, count); // tag count @@ -216,12 +231,10 @@ int ff_ape_write_tag(AVFormatContext *s) avio_wl32(s->pb, APE_TAG_FLAG_CONTAINS_HEADER | APE_TAG_FLAG_CONTAINS_FOOTER); ffio_fill(s->pb, 0, 8); // reserved - // update values in the header - end = avio_tell(s->pb); - avio_seek(s->pb, start + 12, SEEK_SET); - avio_wl32(s->pb, size); - avio_wl32(s->pb, count); - avio_seek(s->pb, end, SEEK_SET); +end: + if (dyn_bc && !dyn_buf) + avio_close_dyn_buf(dyn_bc, &dyn_buf); + av_freep(&dyn_buf); - return 0; + return ret; } diff --git a/libavformat/apetag.h b/libavformat/apetag.h index 36e3211fc8..cf2a5f8ab4 100644 --- a/libavformat/apetag.h +++ b/libavformat/apetag.h @@ -3,20 +3,20 @@ * Copyright (c) 2007 Benjamin Zores <ben@geexbox.org> * based upon libdemac from Dave Chapman. * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -25,6 +25,10 @@ #include "avformat.h" +#define APE_TAG_PREAMBLE "APETAGEX" +#define APE_TAG_VERSION 2000 +#define APE_TAG_FOOTER_BYTES 32 + /** * Read and parse an APE tag * diff --git a/libavformat/aqtitledec.c b/libavformat/aqtitledec.c new file mode 100644 index 0000000000..810f95b493 --- /dev/null +++ b/libavformat/aqtitledec.c @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2012 Clément BÅ“sch + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * AQTitle subtitles format demuxer + * + * @see http://web.archive.org/web/20070210095721/http://www.volny.cz/aberka/czech/aqt.html + * @see https://trac.annodex.net/wiki/AQTitle + */ + +#include "avformat.h" +#include "internal.h" +#include "subtitles.h" +#include "libavutil/opt.h" + +typedef struct { + const AVClass *class; + FFDemuxSubtitlesQueue q; + AVRational frame_rate; +} AQTitleContext; + +static int aqt_probe(AVProbeData *p) +{ + int frame; + const char *ptr = p->buf; + + if (sscanf(ptr, "-->> %d", &frame) == 1) + return AVPROBE_SCORE_EXTENSION; + return 0; +} + +static int aqt_read_header(AVFormatContext *s) +{ + AQTitleContext *aqt = s->priv_data; + AVStream *st = avformat_new_stream(s, NULL); + int new_event = 1; + int64_t pos = 0, frame = AV_NOPTS_VALUE; + AVPacket *sub = NULL; + + if (!st) + return AVERROR(ENOMEM); + avpriv_set_pts_info(st, 64, aqt->frame_rate.den, aqt->frame_rate.num); + st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE; + st->codec->codec_id = AV_CODEC_ID_TEXT; + + while (!url_feof(s->pb)) { + char line[4096]; + int len = ff_get_line(s->pb, line, sizeof(line)); + + if (!len) + break; + + line[strcspn(line, "\r\n")] = 0; + + if (sscanf(line, "-->> %"SCNd64, &frame) == 1) { + new_event = 1; + pos = avio_tell(s->pb); + if (sub) { + sub->duration = frame - sub->pts; + sub = NULL; + } + } else if (*line) { + if (!new_event) { + sub = ff_subtitles_queue_insert(&aqt->q, "\n", 1, 1); + if (!sub) + return AVERROR(ENOMEM); + } + sub = ff_subtitles_queue_insert(&aqt->q, line, strlen(line), !new_event); + if (!sub) + return AVERROR(ENOMEM); + if (new_event) { + sub->pts = frame; + sub->duration = -1; + sub->pos = pos; + } + new_event = 0; + } + } + + ff_subtitles_queue_finalize(&aqt->q); + return 0; +} + +static int aqt_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + AQTitleContext *aqt = s->priv_data; + return ff_subtitles_queue_read_packet(&aqt->q, pkt); +} + +static int aqt_read_seek(AVFormatContext *s, int stream_index, + int64_t min_ts, int64_t ts, int64_t max_ts, int flags) +{ + AQTitleContext *aqt = s->priv_data; + return ff_subtitles_queue_seek(&aqt->q, s, stream_index, + min_ts, ts, max_ts, flags); +} + +static int aqt_read_close(AVFormatContext *s) +{ + AQTitleContext *aqt = s->priv_data; + ff_subtitles_queue_clean(&aqt->q); + return 0; +} + +#define OFFSET(x) offsetof(AQTitleContext, x) +#define SD AV_OPT_FLAG_SUBTITLE_PARAM|AV_OPT_FLAG_DECODING_PARAM +static const AVOption aqt_options[] = { + { "subfps", "set the movie frame rate", OFFSET(frame_rate), AV_OPT_TYPE_RATIONAL, {.dbl=25}, 0, INT_MAX, SD }, + { NULL } +}; + +static const AVClass aqt_class = { + .class_name = "aqtdec", + .item_name = av_default_item_name, + .option = aqt_options, + .version = LIBAVUTIL_VERSION_INT, +}; + +AVInputFormat ff_aqtitle_demuxer = { + .name = "aqtitle", + .long_name = NULL_IF_CONFIG_SMALL("AQTitle subtitles"), + .priv_data_size = sizeof(AQTitleContext), + .read_probe = aqt_probe, + .read_header = aqt_read_header, + .read_packet = aqt_read_packet, + .read_seek2 = aqt_read_seek, + .read_close = aqt_read_close, + .extensions = "aqt", + .priv_class = &aqt_class, +}; diff --git a/libavformat/asf.c b/libavformat/asf.c index ec34b5010d..80d24dbf43 100644 --- a/libavformat/asf.c +++ b/libavformat/asf.c @@ -1,20 +1,20 @@ /* * Copyright (c) 2000, 2001 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -159,7 +159,6 @@ const AVMetadataConv ff_asf_metadata_conv[] = { { "WM/Publisher", "publisher" }, { "WM/Tool", "encoder" }, { "WM/TrackNumber", "track" }, - { "WM/Track", "track" }, { "WM/MediaStationCallSign", "service_provider" }, { "WM/MediaStationName", "service_name" }, // { "Year" , "date" }, TODO: conversion year<->date diff --git a/libavformat/asf.h b/libavformat/asf.h index 2f6722adca..904d3486e8 100644 --- a/libavformat/asf.h +++ b/libavformat/asf.h @@ -1,20 +1,20 @@ /* * Copyright (c) 2000, 2001 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -28,14 +28,21 @@ #define PACKET_SIZE 3200 +typedef struct ASFPayload { + uint8_t type; + uint16_t size; +} ASFPayload; + typedef struct ASFStream { int num; unsigned char seq; /* use for reading */ AVPacket pkt; int frag_offset; + int packet_obj_size; int timestamp; int64_t duration; + int skip_to_key; int ds_span; /* descrambling */ int ds_packet_size; @@ -47,6 +54,9 @@ typedef struct ASFStream { int palette_changed; uint32_t palette[256]; + + int payload_ext_ct; + ASFPayload payload[8]; } ASFStream; typedef struct ASFMainHeader { @@ -178,4 +188,6 @@ extern const AVMetadataConv ff_asf_metadata_conv[]; extern AVInputFormat ff_asf_demuxer; +void ff_put_guid(AVIOContext *s, const ff_asf_guid *g); + #endif /* AVFORMAT_ASF_H */ diff --git a/libavformat/asfcrypt.c b/libavformat/asfcrypt.c index c261475848..a402758d0a 100644 --- a/libavformat/asfcrypt.c +++ b/libavformat/asfcrypt.c @@ -3,20 +3,20 @@ * Copyright (c) 2007 Reimar Doeffinger * This is a rewrite of code contained in freeme/freeme2 * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/asfcrypt.h b/libavformat/asfcrypt.h index 53388b426e..8b80d63c5d 100644 --- a/libavformat/asfcrypt.h +++ b/libavformat/asfcrypt.h @@ -2,20 +2,20 @@ * ASF decryption * Copyright (c) 2007 Reimar Doeffinger * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/asfdec.c b/libavformat/asfdec.c index 5b4366e7e9..528bcbd5b1 100644 --- a/libavformat/asfdec.c +++ b/libavformat/asfdec.c @@ -2,20 +2,20 @@ * ASF compatible demuxer * Copyright (c) 2000, 2001 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -68,7 +68,6 @@ typedef struct { unsigned int packet_frag_size; int64_t packet_frag_timestamp; int packet_multi_size; - int packet_obj_size; int packet_time_delta; int packet_time_start; int64_t packet_pos; @@ -96,12 +95,8 @@ static const AVClass asf_class = { #include <assert.h> #define ASF_MAX_STREAMS 127 -#define FRAME_HEADER_SIZE 17 -// Fix Me! FRAME_HEADER_SIZE may be different. - -static const ff_asf_guid index_guid = { - 0x90, 0x08, 0x00, 0x33, 0xb1, 0xe5, 0xcf, 0x11, 0x89, 0xf4, 0x00, 0xa0, 0xc9, 0x03, 0x49, 0xcb -}; +#define FRAME_HEADER_SIZE 16 +// Fix Me! FRAME_HEADER_SIZE may be different. (17 is known to be too large) #ifdef DEBUG static const ff_asf_guid stream_bitrate_guid = { /* (http://get.to/sdp) */ @@ -127,7 +122,7 @@ static void print_guid(ff_asf_guid *g) else PRINT_IF_GUID(g, ff_asf_codec_comment_header); else PRINT_IF_GUID(g, ff_asf_codec_comment1_header); else PRINT_IF_GUID(g, ff_asf_data_header); - else PRINT_IF_GUID(g, index_guid); + else PRINT_IF_GUID(g, ff_asf_simple_index_header); else PRINT_IF_GUID(g, ff_asf_head1_guid); else PRINT_IF_GUID(g, ff_asf_head2_guid); else PRINT_IF_GUID(g, ff_asf_my_guid); @@ -281,16 +276,20 @@ static void get_tag(AVFormatContext *s, const char *key, int type, int len, int { char *value; int64_t off = avio_tell(s->pb); +#define LEN 22 - if ((unsigned)len >= (UINT_MAX - 1) / 2) + if ((unsigned)len >= (UINT_MAX - LEN) / 2) return; - value = av_malloc(2 * len + 1); + value = av_malloc(2 * len + LEN); if (!value) goto finish; if (type == 0) { // UTF16-LE avio_get_str16le(s->pb, len, value, 2 * len + 1); + } else if (type == -1) { // ASCII + avio_read(s->pb, value, len); + value[len]=0; } else if (type == 1) { // byte array if (!strcmp(key, "WM/Picture")) { // handle cover art asf_read_picture(s, len); @@ -302,7 +301,7 @@ static void get_tag(AVFormatContext *s, const char *key, int type, int len, int goto finish; } else if (type > 1 && type <= 5) { // boolean or DWORD or QWORD or WORD uint64_t num = get_value(s->pb, type, type2_size); - snprintf(value, len, "%"PRIu64, num); + snprintf(value, LEN, "%"PRIu64, num); } else if (type == 6) { // (don't) handle GUID av_log(s, AV_LOG_DEBUG, "Unsupported GUID value in tag %s.\n", key); goto finish; @@ -367,17 +366,12 @@ static int asf_read_stream_properties(AVFormatContext *s, int64_t size) if (!st) return AVERROR(ENOMEM); avpriv_set_pts_info(st, 32, 1, 1000); /* 32 bit pts in ms */ - asf_st = av_mallocz(sizeof(ASFStream)); - if (!asf_st) - return AVERROR(ENOMEM); - st->priv_data = asf_st; - st->start_time = 0; start_time = asf->hdr.preroll; - asf_st->stream_language_index = 128; // invalid stream index means no language info - if (!(asf->hdr.flags & 0x01)) { // if we aren't streaming... - st->duration = asf->hdr.play_time / + int64_t fsize = avio_size(pb); + if (fsize <= 0 || (int64_t)asf->hdr.file_size <= 0 || FFABS(fsize - (int64_t)asf->hdr.file_size) < 10000) + st->duration = asf->hdr.play_time / (10000000 / 1000) - start_time; } ff_get_guid(pb, &g); @@ -405,6 +399,7 @@ static int asf_read_stream_properties(AVFormatContext *s, int64_t size) st->id = avio_rl16(pb) & 0x7f; /* stream id */ // mapping of asf ID to AV stream ID; asf->asfid2avid[st->id] = s->nb_streams - 1; + asf_st = &asf->streams[st->id]; avio_rl32(pb); @@ -430,7 +425,7 @@ static int asf_read_stream_properties(AVFormatContext *s, int64_t size) if (is_dvr_ms_audio) { // codec_id and codec_tag are unreliable in dvr_ms // files. Set them later by probing stream. - st->codec->codec_id = AV_CODEC_ID_PROBE; + st->request_probe = 1; st->codec->codec_tag = 0; } if (st->codec->codec_id == AV_CODEC_ID_AAC) @@ -467,9 +462,11 @@ static int asf_read_stream_properties(AVFormatContext *s, int64_t size) tag1 = avio_rl32(pb); avio_skip(pb, 20); if (sizeX > 40) { - st->codec->extradata_size = sizeX - 40; + st->codec->extradata_size = ffio_limit(pb, sizeX - 40); st->codec->extradata = av_mallocz(st->codec->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE); + if (!st->codec->extradata) + return AVERROR(ENOMEM); avio_read(pb, st->codec->extradata, st->codec->extradata_size); } @@ -538,8 +535,10 @@ static int asf_read_ext_stream_properties(AVFormatContext *s, int64_t size) stream_ct = avio_rl16(pb); // stream-name-count payload_ext_ct = avio_rl16(pb); // payload-extension-system-count - if (stream_num < 128) + if (stream_num < 128) { asf->stream_bitrates[stream_num] = leak_rate; + asf->streams[stream_num].payload_ext_ct = 0; + } for (i = 0; i < stream_ct; i++) { avio_rl16(pb); @@ -548,10 +547,18 @@ static int asf_read_ext_stream_properties(AVFormatContext *s, int64_t size) } for (i = 0; i < payload_ext_ct; i++) { + int size; ff_get_guid(pb, &g); - avio_skip(pb, 2); + size = avio_rl16(pb); ext_len = avio_rl32(pb); avio_skip(pb, ext_len); + if (stream_num < 128 && i < FF_ARRAY_ELEMS(asf->streams[stream_num].payload)) { + ASFPayload *p = &asf->streams[stream_num].payload[i]; + p->type = g[0]; + p->size = size; + av_log(s, AV_LOG_DEBUG, "Payload extension %x %d\n", g[0], p->size ); + asf->streams[stream_num].payload_ext_ct ++; + } } return 0; @@ -715,12 +722,16 @@ static int asf_read_header(AVFormatContext *s) ff_get_guid(pb, &g); if (ff_guidcmp(&g, &ff_asf_header)) - return -1; + return AVERROR_INVALIDDATA; avio_rl64(pb); avio_rl32(pb); avio_r8(pb); avio_r8(pb); memset(&asf->asfid2avid, -1, sizeof(asf->asfid2avid)); + + for (i = 0; i<128; i++) + asf->streams[i].stream_language_index = 128; // invalid stream index means no language info + for (;;) { uint64_t gpos = avio_tell(pb); ff_get_guid(pb, &g); @@ -737,7 +748,7 @@ static int asf_read_header(AVFormatContext *s) break; } if (gsize < 24) - return -1; + return AVERROR_INVALIDDATA; if (!ff_guidcmp(&g, &ff_asf_file_header)) { int ret = asf_read_file_properties(s, gsize); if (ret < 0) @@ -768,19 +779,30 @@ static int asf_read_header(AVFormatContext *s) continue; } else if (!ff_guidcmp(&g, &ff_asf_marker_header)) { asf_read_marker(s, gsize); - } else if (pb->eof_reached) { - return -1; + } else if (url_feof(pb)) { + return AVERROR_EOF; } else { if (!s->keylen) { if (!ff_guidcmp(&g, &ff_asf_content_encryption)) { + unsigned int len; + AVPacket pkt; av_log(s, AV_LOG_WARNING, "DRM protected stream detected, decoding will likely fail!\n"); + len= avio_rl32(pb); + av_log(s, AV_LOG_DEBUG, "Secret data:\n"); + av_get_packet(pb, &pkt, len); av_hex_dump_log(s, AV_LOG_DEBUG, pkt.data, pkt.size); av_free_packet(&pkt); + len= avio_rl32(pb); + get_tag(s, "ASF_Protection_Type", -1, len, 32); + len= avio_rl32(pb); + get_tag(s, "ASF_Key_ID", -1, len, 32); + len= avio_rl32(pb); + get_tag(s, "ASF_License_URL", -1, len, 32); } else if (!ff_guidcmp(&g, &ff_asf_ext_content_encryption)) { av_log(s, AV_LOG_WARNING, "Ext DRM protected stream detected, decoding will likely fail!\n"); + av_dict_set(&s->metadata, "encryption", "ASF Extended Content Encryption", 0); } else if (!ff_guidcmp(&g, &ff_asf_digital_signature)) { - av_log(s, AV_LOG_WARNING, - "Digital signature detected, decoding will likely fail!\n"); + av_log(s, AV_LOG_INFO, "Digital signature detected!\n"); } } } @@ -794,8 +816,8 @@ static int asf_read_header(AVFormatContext *s) avio_rl64(pb); avio_r8(pb); avio_r8(pb); - if (pb->eof_reached) - return -1; + if (url_feof(pb)) + return AVERROR_EOF; asf->data_offset = avio_tell(pb); asf->packet_size_left = 0; @@ -894,20 +916,20 @@ static int asf_get_packet(AVFormatContext *s, AVIOContext *pb) * the stream. */ if (pb->error == AVERROR(EAGAIN)) return AVERROR(EAGAIN); - if (!pb->eof_reached) + if (!url_feof(pb)) av_log(s, AV_LOG_ERROR, "ff asf bad header %x at:%"PRId64"\n", c, avio_tell(pb)); } if ((c & 0x8f) == 0x82) { if (d || e) { - if (!pb->eof_reached) + if (!url_feof(pb)) av_log(s, AV_LOG_ERROR, "ff asf bad non zero\n"); - return -1; + return AVERROR_INVALIDDATA; } c = avio_r8(pb); d = avio_r8(pb); rsize += 3; - } else if (!pb->eof_reached) { + } else if(!url_feof(pb)) { avio_seek(pb, -1, SEEK_CUR); // FIXME } @@ -923,12 +945,12 @@ static int asf_get_packet(AVFormatContext *s, AVIOContext *pb) av_log(s, AV_LOG_ERROR, "invalid packet_length %d at:%"PRId64"\n", packet_length, avio_tell(pb)); - return -1; + return AVERROR_INVALIDDATA; } if (padsize >= packet_length) { av_log(s, AV_LOG_ERROR, "invalid padsize %d at:%"PRId64"\n", padsize, avio_tell(pb)); - return -1; + return AVERROR_INVALIDDATA; } asf->packet_timestamp = avio_rl32(pb); @@ -948,7 +970,7 @@ static int asf_get_packet(AVFormatContext *s, AVIOContext *pb) av_log(s, AV_LOG_ERROR, "invalid packet header length %d for pktlen %d-%d at %"PRId64"\n", rsize, packet_length, padsize, avio_tell(pb)); - return -1; + return AVERROR_INVALIDDATA; } asf->packet_size_left = packet_length - padsize - rsize; if (packet_length < asf->hdr.min_pktsize) @@ -966,13 +988,16 @@ static int asf_get_packet(AVFormatContext *s, AVIOContext *pb) static int asf_read_frame_header(AVFormatContext *s, AVIOContext *pb) { ASFContext *asf = s->priv_data; + ASFStream *asfst; int rsize = 1; int num = avio_r8(pb); - int64_t ts0; + int i; + int64_t ts0, ts1 av_unused; asf->packet_segments--; asf->packet_key_frame = num >> 7; asf->stream_index = asf->asfid2avid[num & 0x7f]; + asfst = &asf->streams[num & 0x7f]; // sequence should be ignored! DO_2BITS(asf->packet_property >> 4, asf->packet_seq, 0); DO_2BITS(asf->packet_property >> 2, asf->packet_frag_offset, 0); @@ -980,26 +1005,62 @@ static int asf_read_frame_header(AVFormatContext *s, AVIOContext *pb) av_dlog(asf, "key:%d stream:%d seq:%d offset:%d replic_size:%d\n", asf->packet_key_frame, asf->stream_index, asf->packet_seq, asf->packet_frag_offset, asf->packet_replic_size); + if (rsize+(int64_t)asf->packet_replic_size > asf->packet_size_left) { + av_log(s, AV_LOG_ERROR, "packet_replic_size %d is invalid\n", asf->packet_replic_size); + return AVERROR_INVALIDDATA; + } if (asf->packet_replic_size >= 8) { - asf->packet_obj_size = avio_rl32(pb); - if (asf->packet_obj_size >= (1 << 24) || asf->packet_obj_size <= 0) { + int64_t end = avio_tell(pb) + asf->packet_replic_size; + AVRational aspect; + asfst->packet_obj_size = avio_rl32(pb); + if (asfst->packet_obj_size >= (1 << 24) || asfst->packet_obj_size <= 0) { av_log(s, AV_LOG_ERROR, "packet_obj_size invalid\n"); - return -1; + asfst->packet_obj_size = 0; + return AVERROR_INVALIDDATA; } asf->packet_frag_timestamp = avio_rl32(pb); // timestamp - if (asf->packet_replic_size >= 8 + 38 + 4) { - avio_skip(pb, 10); - ts0 = avio_rl64(pb); - avio_skip(pb, 8); - avio_skip(pb, 12); - avio_rl32(pb); - avio_skip(pb, asf->packet_replic_size - 8 - 38 - 4); - if (ts0 != -1) - asf->packet_frag_timestamp = ts0 / 10000; - else - asf->packet_frag_timestamp = AV_NOPTS_VALUE; - } else - avio_skip(pb, asf->packet_replic_size - 8); + + for (i = 0; i < asfst->payload_ext_ct; i++) { + ASFPayload *p = &asfst->payload[i]; + int size = p->size; + int64_t payend; + if (size == 0xFFFF) + size = avio_rl16(pb); + payend = avio_tell(pb) + size; + if (payend > end) { + av_log(s, AV_LOG_ERROR, "too long payload\n"); + break; + } + switch (p->type) { + case 0x50: +// duration = avio_rl16(pb); + break; + case 0x54: + aspect.num = avio_r8(pb); + aspect.den = avio_r8(pb); + if (aspect.num > 0 && aspect.den > 0 && asf->stream_index >= 0) { + s->streams[asf->stream_index]->sample_aspect_ratio = aspect; + } + break; + case 0x2A: + avio_skip(pb, 8); + ts0 = avio_rl64(pb); + ts1 = avio_rl64(pb); + if (ts0!= -1) asf->packet_frag_timestamp = ts0/10000; + else asf->packet_frag_timestamp = AV_NOPTS_VALUE; + break; + case 0x5B: + case 0xB7: + case 0xCC: + case 0xC0: + case 0xA0: + //unknown + break; + } + avio_seek(pb, payend, SEEK_SET); + } + + avio_seek(pb, end, SEEK_SET); rsize += asf->packet_replic_size; // FIXME - check validity } else if (asf->packet_replic_size == 1) { // multipacket - frag_offset is beginning timestamp @@ -1012,18 +1073,18 @@ static int asf_read_frame_header(AVFormatContext *s, AVIOContext *pb) } else if (asf->packet_replic_size != 0) { av_log(s, AV_LOG_ERROR, "unexpected packet_replic_size of %d\n", asf->packet_replic_size); - return -1; + return AVERROR_INVALIDDATA; } if (asf->packet_flags & 0x01) { DO_2BITS(asf->packet_segsizetype >> 6, asf->packet_frag_size, 0); // 0 is illegal if (rsize > asf->packet_size_left) { av_log(s, AV_LOG_ERROR, "packet_replic_size is invalid\n"); - return -1; + return AVERROR_INVALIDDATA; } else if (asf->packet_frag_size > asf->packet_size_left - rsize) { if (asf->packet_frag_size > asf->packet_size_left - rsize + asf->packet_padsize) { av_log(s, AV_LOG_ERROR, "packet_frag_size is invalid (%d-%d)\n", asf->packet_size_left, rsize); - return -1; + return AVERROR_INVALIDDATA; } else { int diff = asf->packet_frag_size - (asf->packet_size_left - rsize); asf->packet_size_left += diff; @@ -1031,16 +1092,12 @@ static int asf_read_frame_header(AVFormatContext *s, AVIOContext *pb) } } } else { - if (rsize > asf->packet_size_left) { - av_log(s, AV_LOG_ERROR, "packet_replic_size is invalid\n"); - return -1; - } asf->packet_frag_size = asf->packet_size_left - rsize; } if (asf->packet_replic_size == 1) { asf->packet_multi_size = asf->packet_frag_size; if (asf->packet_multi_size > asf->packet_size_left) - return -1; + return AVERROR_INVALIDDATA; } asf->packet_size_left -= rsize; @@ -1062,8 +1119,7 @@ static int asf_parse_packet(AVFormatContext *s, AVIOContext *pb, AVPacket *pkt) ASFStream *asf_st = 0; for (;;) { int ret; - - if (pb->eof_reached) + if (url_feof(pb)) return AVERROR_EOF; if (asf->packet_size_left < FRAME_HEADER_SIZE || @@ -1088,7 +1144,7 @@ static int asf_parse_packet(AVFormatContext *s, AVIOContext *pb, AVPacket *pkt) if (asf->stream_index < 0 || s->streams[asf->stream_index]->discard >= AVDISCARD_ALL || (!asf->packet_key_frame && - s->streams[asf->stream_index]->discard >= AVDISCARD_NONKEY)) { + (s->streams[asf->stream_index]->discard >= AVDISCARD_NONKEY || asf->streams[s->streams[asf->stream_index]->id].skip_to_key))) { asf->packet_time_start = 0; /* unhandled packet (should not happen) */ avio_skip(pb, asf->packet_frag_size); @@ -1098,7 +1154,8 @@ static int asf_parse_packet(AVFormatContext *s, AVIOContext *pb, AVPacket *pkt) asf->packet_frag_size); continue; } - asf->asf_st = s->streams[asf->stream_index]->priv_data; + asf->asf_st = &asf->streams[s->streams[asf->stream_index]->id]; + asf->asf_st->skip_to_key = 0; } asf_st = asf->asf_st; av_assert0(asf_st); @@ -1107,37 +1164,30 @@ static int asf_parse_packet(AVFormatContext *s, AVIOContext *pb, AVPacket *pkt) // frag_offset is here used as the beginning timestamp asf->packet_frag_timestamp = asf->packet_time_start; asf->packet_time_start += asf->packet_time_delta; - asf->packet_obj_size = asf->packet_frag_size = avio_r8(pb); + asf_st->packet_obj_size = asf->packet_frag_size = avio_r8(pb); asf->packet_size_left--; asf->packet_multi_size--; - if (asf->packet_multi_size < asf->packet_obj_size) { + if (asf->packet_multi_size < asf_st->packet_obj_size) { asf->packet_time_start = 0; avio_skip(pb, asf->packet_multi_size); asf->packet_size_left -= asf->packet_multi_size; continue; } - asf->packet_multi_size -= asf->packet_obj_size; - } - if (asf_st->frag_offset + asf->packet_frag_size <= asf_st->pkt.size && - asf_st->frag_offset + asf->packet_frag_size > asf->packet_obj_size) { - av_log(s, AV_LOG_INFO, "ignoring invalid packet_obj_size (%d %d %d %d)\n", - asf_st->frag_offset, asf->packet_frag_size, - asf->packet_obj_size, asf_st->pkt.size); - asf->packet_obj_size = asf_st->pkt.size; + asf->packet_multi_size -= asf_st->packet_obj_size; } - if (asf_st->pkt.size != asf->packet_obj_size || + if (asf_st->pkt.size != asf_st->packet_obj_size || // FIXME is this condition sufficient? asf_st->frag_offset + asf->packet_frag_size > asf_st->pkt.size) { if (asf_st->pkt.data) { av_log(s, AV_LOG_INFO, "freeing incomplete packet size %d, new %d\n", - asf_st->pkt.size, asf->packet_obj_size); + asf_st->pkt.size, asf_st->packet_obj_size); asf_st->frag_offset = 0; av_free_packet(&asf_st->pkt); } /* new packet */ - av_new_packet(&asf_st->pkt, asf->packet_obj_size); + av_new_packet(&asf_st->pkt, asf_st->packet_obj_size); asf_st->seq = asf->packet_seq; asf_st->pkt.dts = asf->packet_frag_timestamp - asf->hdr.preroll; asf_st->pkt.stream_index = asf->stream_index; @@ -1158,7 +1208,7 @@ static int asf_parse_packet(AVFormatContext *s, AVIOContext *pb, AVPacket *pkt) asf->stream_index, asf->packet_key_frame, asf_st->pkt.flags & AV_PKT_FLAG_KEY, s->streams[asf->stream_index]->codec->codec_type == AVMEDIA_TYPE_AUDIO, - asf->packet_obj_size); + asf_st->packet_obj_size); if (s->streams[asf->stream_index]->codec->codec_type == AVMEDIA_TYPE_AUDIO) asf->packet_key_frame = 1; if (asf->packet_key_frame) @@ -1313,21 +1363,34 @@ static void asf_reset_header(AVFormatContext *s) asf->packet_frag_size = 0; asf->packet_frag_timestamp = 0; asf->packet_multi_size = 0; - asf->packet_obj_size = 0; asf->packet_time_delta = 0; asf->packet_time_start = 0; - for (i = 0; i < s->nb_streams; i++) { - asf_st = s->streams[i]->priv_data; - if (!asf_st) - continue; + for (i = 0; i < 128; i++) { + asf_st = &asf->streams[i]; av_free_packet(&asf_st->pkt); + asf_st->packet_obj_size = 0; asf_st->frag_offset = 0; asf_st->seq = 0; } asf->asf_st = NULL; } +static void skip_to_key(AVFormatContext *s) +{ + ASFContext *asf = s->priv_data; + int i; + + for (i = 0; i < 128; i++) { + int j = asf->asfid2avid[i]; + ASFStream *asf_st = &asf->streams[i]; + if (j < 0 || s->streams[j]->codec->codec_type != AVMEDIA_TYPE_VIDEO) + continue; + + asf_st->skip_to_key = 1; + } +} + static int asf_read_close(AVFormatContext *s) { asf_reset_header(s); @@ -1338,6 +1401,7 @@ static int asf_read_close(AVFormatContext *s) static int64_t asf_read_pts(AVFormatContext *s, int stream_index, int64_t *ppos, int64_t pos_limit) { + ASFContext *asf = s->priv_data; AVPacket pkt1, *pkt = &pkt1; ASFStream *asf_st; int64_t pts; @@ -1353,11 +1417,13 @@ static int64_t asf_read_pts(AVFormatContext *s, int stream_index, s->packet_size * s->packet_size + s->data_offset; *ppos = pos; - avio_seek(s->pb, pos, SEEK_SET); + if (avio_seek(s->pb, pos, SEEK_SET) < 0) + return AV_NOPTS_VALUE; + ff_read_frame_flush(s); asf_reset_header(s); for (;;) { - if (asf_read_packet(s, pkt) < 0) { + if (av_read_frame(s, pkt) < 0) { av_log(s, AV_LOG_INFO, "asf_read_pts failed\n"); return AV_NOPTS_VALUE; } @@ -1368,8 +1434,7 @@ static int64_t asf_read_pts(AVFormatContext *s, int stream_index, if (pkt->flags & AV_PKT_FLAG_KEY) { i = pkt->stream_index; - asf_st = s->streams[i]->priv_data; - av_assert0(asf_st); + asf_st = &asf->streams[s->streams[i]->id]; // assert((asf_st->packet_pos - s->data_offset) % s->packet_size == 0); pos = asf_st->packet_pos; @@ -1392,17 +1457,21 @@ static void asf_build_simple_index(AVFormatContext *s, int stream_index) ff_asf_guid g; ASFContext *asf = s->priv_data; int64_t current_pos = avio_tell(s->pb); - int i; - avio_seek(s->pb, asf->data_object_offset + asf->data_object_size, SEEK_SET); + if(avio_seek(s->pb, asf->data_object_offset + asf->data_object_size, SEEK_SET) < 0) { + asf->index_read= -1; + return; + } + ff_get_guid(s->pb, &g); /* the data object can be followed by other top-level objects, * skip them until the simple index object is reached */ - while (ff_guidcmp(&g, &index_guid)) { + while (ff_guidcmp(&g, &ff_asf_simple_index_header)) { int64_t gsize = avio_rl64(s->pb); - if (gsize < 24 || s->pb->eof_reached) { + if (gsize < 24 || url_feof(s->pb)) { avio_seek(s->pb, current_pos, SEEK_SET); + asf->index_read= -1; return; } avio_skip(s->pb, gsize - 24); @@ -1412,6 +1481,7 @@ static void asf_build_simple_index(AVFormatContext *s, int stream_index) { int64_t itime, last_pos = -1; int pct, ict; + int i; int64_t av_unused gsize = avio_rl64(s->pb); ff_get_guid(s->pb, &g); itime = avio_rl64(s->pb); @@ -1434,7 +1504,7 @@ static void asf_build_simple_index(AVFormatContext *s, int stream_index) last_pos = pos; } } - asf->index_read = ict > 0; + asf->index_read = ict > 1; } avio_seek(s->pb, current_pos, SEEK_SET); } @@ -1444,8 +1514,6 @@ static int asf_read_seek(AVFormatContext *s, int stream_index, { ASFContext *asf = s->priv_data; AVStream *st = s->streams[stream_index]; - int64_t pos; - int index; if (s->packet_size <= 0) return -1; @@ -1462,16 +1530,18 @@ static int asf_read_seek(AVFormatContext *s, int stream_index, if (!asf->index_read) asf_build_simple_index(s, stream_index); - if ((asf->index_read && st->index_entries)) { - index = av_index_search_timestamp(st, pts, flags); + if ((asf->index_read > 0 && st->index_entries)) { + int index = av_index_search_timestamp(st, pts, flags); if (index >= 0) { /* find the position */ - pos = st->index_entries[index].pos; + uint64_t pos = st->index_entries[index].pos; /* do the seek */ av_log(s, AV_LOG_DEBUG, "SEEKTO: %"PRId64"\n", pos); - avio_seek(s->pb, pos, SEEK_SET); + if(avio_seek(s->pb, pos, SEEK_SET) < 0) + return -1; asf_reset_header(s); + skip_to_key(s); return 0; } } @@ -1479,6 +1549,7 @@ static int asf_read_seek(AVFormatContext *s, int stream_index, if (ff_seek_frame_binary(s, stream_index, pts, flags) < 0) return -1; asf_reset_header(s); + skip_to_key(s); return 0; } diff --git a/libavformat/asfenc.c b/libavformat/asfenc.c index e1a718966d..b4a3ffb7ef 100644 --- a/libavformat/asfenc.c +++ b/libavformat/asfenc.c @@ -2,23 +2,24 @@ * ASF muxer * Copyright (c) 2000, 2001 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "libavutil/avassert.h" #include "libavutil/dict.h" #include "libavutil/mathematics.h" #include "avformat.h" @@ -32,7 +33,7 @@ #define ASF_INDEXED_INTERVAL 10000000 -#define ASF_INDEX_BLOCK 600 +#define ASF_INDEX_BLOCK (1<<9) #define ASF_PACKET_ERROR_CORRECTION_DATA_SIZE 0x2 #define ASF_PACKET_ERROR_CORRECTION_FLAGS \ @@ -195,33 +196,36 @@ typedef struct { /* packet filling */ unsigned char multi_payloads_present; int packet_size_left; - int packet_timestamp_start; - int packet_timestamp_end; + int64_t packet_timestamp_start; + int64_t packet_timestamp_end; unsigned int packet_nb_payloads; uint8_t packet_buf[PACKET_SIZE]; AVIOContext pb; /* only for reading */ uint64_t data_offset; ///< beginning of the first data packet - int64_t last_indexed_pts; ASFIndex *index_ptr; - uint32_t nb_index_count; uint32_t nb_index_memory_alloc; uint16_t maximum_packet; + uint32_t next_packet_number; + uint16_t next_packet_count; + uint64_t next_packet_offset; + int next_start_sec; + int end_sec; } ASFContext; static const AVCodecTag codec_asf_bmp_tags[] = { - { AV_CODEC_ID_MPEG4, MKTAG('M', 'P', '4', 'S') }, { AV_CODEC_ID_MPEG4, MKTAG('M', '4', 'S', '2') }, + { AV_CODEC_ID_MPEG4, MKTAG('M', 'P', '4', 'S') }, { AV_CODEC_ID_MSMPEG4V3, MKTAG('M', 'P', '4', '3') }, { AV_CODEC_ID_NONE, 0 }, }; #define PREROLL_TIME 3100 -static void put_guid(AVIOContext *s, const ff_asf_guid *g) +void ff_put_guid(AVIOContext *s, const ff_asf_guid *g) { - assert(sizeof(*g) == 16); + av_assert0(sizeof(*g) == 16); avio_write(s, *g, sizeof(*g)); } @@ -245,7 +249,7 @@ static int64_t put_header(AVIOContext *pb, const ff_asf_guid *g) int64_t pos; pos = avio_tell(pb); - put_guid(pb, g); + ff_put_guid(pb, g); avio_wl64(pb, 24); return pos; } @@ -293,7 +297,7 @@ static int32_t get_send_time(ASFContext *asf, int64_t pres_time, uint64_t *offse int i; int32_t send_time = 0; *offset = asf->data_offset + DATA_HEADER_SIZE; - for (i = 0; i < asf->nb_index_count; i++) { + for (i = 0; i < asf->next_start_sec; i++) { if (pres_time <= asf->index_ptr[i].send_time) break; send_time = asf->index_ptr[i].send_time; @@ -311,7 +315,7 @@ static int asf_write_markers(AVFormatContext *s) AVRational scale = {1, 10000000}; int64_t hpos = put_header(pb, &ff_asf_marker_header); - put_guid(pb, &ff_asf_reserved_4); // ASF spec mandates this reserved value + ff_put_guid(pb, &ff_asf_reserved_4);// ASF spec mandates this reserved value avio_wl32(pb, s->nb_chapters); // markers count avio_wl16(pb, 0); // ASF spec mandates 0 for this avio_wl16(pb, 0); // name length 0, no name given @@ -386,7 +390,7 @@ static int asf_write_header1(AVFormatContext *s, int64_t file_size, put_chunk(s, 0x4824, 0, 0xc00); /* start of stream (length will be patched later) */ } - put_guid(pb, &ff_asf_header); + ff_put_guid(pb, &ff_asf_header); avio_wl64(pb, -1); /* header length, will be patched after */ avio_wl32(pb, 3 + has_title + !!metadata_count + s->nb_streams); /* number of chunks in header */ avio_w8(pb, 1); /* ??? */ @@ -395,7 +399,7 @@ static int asf_write_header1(AVFormatContext *s, int64_t file_size, /* file header */ header_offset = avio_tell(pb); hpos = put_header(pb, &ff_asf_file_header); - put_guid(pb, &ff_asf_my_guid); + ff_put_guid(pb, &ff_asf_my_guid); avio_wl64(pb, file_size); file_time = 0; avio_wl64(pb, unix_to_file_time(file_time)); @@ -411,7 +415,7 @@ static int asf_write_header1(AVFormatContext *s, int64_t file_size, /* unknown headers */ hpos = put_header(pb, &ff_asf_head1_guid); - put_guid(pb, &ff_asf_head2_guid); + ff_put_guid(pb, &ff_asf_head2_guid); avio_wl32(pb, 6); avio_wl16(pb, 0); end_header(pb, hpos); @@ -460,7 +464,7 @@ static int asf_write_header1(AVFormatContext *s, int64_t file_size, enc = s->streams[n]->codec; asf->streams[n].num = n + 1; - asf->streams[n].seq = 0; + asf->streams[n].seq = 1; switch (enc->codec_type) { case AVMEDIA_TYPE_AUDIO: @@ -478,11 +482,11 @@ static int asf_write_header1(AVFormatContext *s, int64_t file_size, hpos = put_header(pb, &ff_asf_stream_header); if (enc->codec_type == AVMEDIA_TYPE_AUDIO) { - put_guid(pb, &ff_asf_audio_stream); - put_guid(pb, &ff_asf_audio_conceal_spread); + ff_put_guid(pb, &ff_asf_audio_stream); + ff_put_guid(pb, &ff_asf_audio_conceal_spread); } else { - put_guid(pb, &ff_asf_video_stream); - put_guid(pb, &ff_asf_video_conceal_none); + ff_put_guid(pb, &ff_asf_video_stream); + ff_put_guid(pb, &ff_asf_video_conceal_none); } avio_wl64(pb, 0); /* ??? */ es_pos = avio_tell(pb); @@ -529,7 +533,7 @@ static int asf_write_header1(AVFormatContext *s, int64_t file_size, /* media comments */ hpos = put_header(pb, &ff_asf_codec_comment_header); - put_guid(pb, &ff_asf_codec_comment1_header); + ff_put_guid(pb, &ff_asf_codec_comment1_header); avio_wl32(pb, s->nb_streams); for (n = 0; n < s->nb_streams; n++) { AVCodec *p; @@ -599,9 +603,9 @@ static int asf_write_header1(AVFormatContext *s, int64_t file_size, /* movie chunk, followed by packets of packet_size */ asf->data_offset = cur_pos; - put_guid(pb, &ff_asf_data_header); + ff_put_guid(pb, &ff_asf_data_header); avio_wl64(pb, data_chunk_size); - put_guid(pb, &ff_asf_my_guid); + ff_put_guid(pb, &ff_asf_my_guid); avio_wl64(pb, asf->nb_packets); /* nb packets */ avio_w8(pb, 1); /* ??? */ avio_w8(pb, 1); /* ??? */ @@ -615,10 +619,8 @@ static int asf_write_header(AVFormatContext *s) s->packet_size = PACKET_SIZE; asf->nb_packets = 0; - asf->last_indexed_pts = 0; asf->index_ptr = av_malloc(sizeof(ASFIndex) * ASF_INDEX_BLOCK); asf->nb_index_memory_alloc = ASF_INDEX_BLOCK; - asf->nb_index_count = 0; asf->maximum_packet = 0; /* the data-chunk-size has to be 50 (DATA_HEADER_SIZE), which is @@ -637,6 +639,9 @@ static int asf_write_header(AVFormatContext *s) ffio_init_context(&asf->pb, asf->packet_buf, s->packet_size, 1, NULL, NULL, NULL, NULL); + if (s->avoid_negative_ts < 0) + s->avoid_negative_ts = 1; + return 0; } @@ -663,7 +668,7 @@ static int put_payload_parsing_info(AVFormatContext *s, padsize -= PACKET_HEADER_MIN_SIZE; if (asf->multi_payloads_present) padsize--; - assert(padsize >= 0); + av_assert0(padsize >= 0); avio_w8(pb, ASF_PACKET_ERROR_CORRECTION_FLAGS); for (i = 0; i < ASF_PACKET_ERROR_CORRECTION_DATA_SIZE; i++) @@ -702,20 +707,19 @@ static void flush_packet(AVFormatContext *s) ASFContext *asf = s->priv_data; int packet_hdr_size, packet_filled_size; - assert(asf->packet_timestamp_end >= asf->packet_timestamp_start); + av_assert0(asf->packet_timestamp_end >= asf->packet_timestamp_start); if (asf->is_streamed) put_chunk(s, 0x4424, s->packet_size, 0); packet_hdr_size = put_payload_parsing_info(s, asf->packet_timestamp_start, - asf->packet_timestamp_end - - asf->packet_timestamp_start, + asf->packet_timestamp_end - asf->packet_timestamp_start, asf->packet_nb_payloads, asf->packet_size_left); packet_filled_size = PACKET_SIZE - asf->packet_size_left; - assert(packet_hdr_size <= asf->packet_size_left); + av_assert0(packet_hdr_size <= asf->packet_size_left); memset(asf->packet_buf + packet_filled_size, 0, asf->packet_size_left); avio_write(s->pb, asf->packet_buf, s->packet_size - packet_hdr_size); @@ -730,7 +734,7 @@ static void flush_packet(AVFormatContext *s) } static void put_payload_header(AVFormatContext *s, ASFStream *stream, - int presentation_time, int m_obj_size, + int64_t presentation_time, int m_obj_size, int m_obj_offset, int payload_len, int flags) { ASFContext *asf = s->priv_data; @@ -753,7 +757,7 @@ static void put_payload_header(AVFormatContext *s, ASFStream *stream, avio_w8(pb, ASF_PAYLOAD_REPLICATED_DATA_LENGTH); avio_wl32(pb, m_obj_size); // Replicated Data - Media Object Size - avio_wl32(pb, presentation_time); // Replicated Data - Presentation Time + avio_wl32(pb, (uint32_t) presentation_time); // Replicated Data - Presentation Time if (asf->multi_payloads_present) { avio_wl16(pb, payload_len); // payload length @@ -761,7 +765,7 @@ static void put_payload_header(AVFormatContext *s, ASFStream *stream, } static void put_frame(AVFormatContext *s, ASFStream *stream, AVStream *avst, - int timestamp, const uint8_t *buf, + int64_t timestamp, const uint8_t *buf, int m_obj_size, int flags) { ASFContext *asf = s->priv_data; @@ -823,16 +827,59 @@ static void put_frame(AVFormatContext *s, ASFStream *stream, AVStream *avst, stream->seq++; } +static int update_index(AVFormatContext *s, int start_sec, + uint32_t packet_number, uint16_t packet_count, + uint64_t packet_offset) +{ + ASFContext *asf = s->priv_data; + + if (start_sec > asf->next_start_sec) { + int i; + + if (!asf->next_start_sec) { + asf->next_packet_number = packet_number; + asf->next_packet_count = packet_count; + asf->next_packet_offset = packet_offset; + } + + if (start_sec > asf->nb_index_memory_alloc) { + int err; + asf->nb_index_memory_alloc = (start_sec + ASF_INDEX_BLOCK) & ~(ASF_INDEX_BLOCK - 1); + if ((err = av_reallocp_array(&asf->index_ptr, + asf->nb_index_memory_alloc, + sizeof(*asf->index_ptr))) < 0) { + asf->nb_index_memory_alloc = 0; + return err; + } + } + for (i = asf->next_start_sec; i < start_sec; i++) { + asf->index_ptr[i].packet_number = asf->next_packet_number; + asf->index_ptr[i].packet_count = asf->next_packet_count; + asf->index_ptr[i].send_time = asf->next_start_sec * INT64_C(10000000); + asf->index_ptr[i].offset = asf->next_packet_offset; + + } + } + asf->maximum_packet = FFMAX(asf->maximum_packet, packet_count); + asf->next_packet_number = packet_number; + asf->next_packet_count = packet_count; + asf->next_packet_offset = packet_offset; + asf->next_start_sec = start_sec; + + return 0; +} + static int asf_write_packet(AVFormatContext *s, AVPacket *pkt) { ASFContext *asf = s->priv_data; AVIOContext *pb = s->pb; ASFStream *stream; - int64_t duration; AVCodecContext *codec; - int64_t packet_st, pts; - int start_sec, i; + uint32_t packet_number; + int64_t pts; + int start_sec; int flags = pkt->flags; + int ret; uint64_t offset = avio_tell(pb); codec = s->streams[pkt->stream_index]->codec; @@ -842,41 +889,26 @@ static int asf_write_packet(AVFormatContext *s, AVPacket *pkt) flags &= ~AV_PKT_FLAG_KEY; pts = (pkt->pts != AV_NOPTS_VALUE) ? pkt->pts : pkt->dts; - assert(pts != AV_NOPTS_VALUE); - duration = pts * 10000; - asf->duration = FFMAX(asf->duration, duration + pkt->duration * 10000); + av_assert0(pts != AV_NOPTS_VALUE); + pts *= 10000; + asf->duration = FFMAX(asf->duration, pts + pkt->duration * 10000); - packet_st = asf->nb_packets; + packet_number = asf->nb_packets; put_frame(s, stream, s->streams[pkt->stream_index], pkt->dts, pkt->data, pkt->size, flags); + start_sec = (int)((PREROLL_TIME * 10000 + pts + ASF_INDEXED_INTERVAL - 1) + / ASF_INDEXED_INTERVAL); + /* check index */ if ((!asf->is_streamed) && (flags & AV_PKT_FLAG_KEY)) { - start_sec = (int)(duration / INT64_C(10000000)); - if (start_sec != (int)(asf->last_indexed_pts / INT64_C(10000000))) { - for (i = asf->nb_index_count; i < start_sec; i++) { - if (i >= asf->nb_index_memory_alloc) { - int err; - asf->nb_index_memory_alloc += ASF_INDEX_BLOCK; - if ((err = av_reallocp_array(&asf->index_ptr, - asf->nb_index_memory_alloc, - sizeof(*asf->index_ptr))) < 0) { - asf->nb_index_memory_alloc = 0; - return err; - } - } - // store - asf->index_ptr[i].packet_number = (uint32_t)packet_st; - asf->index_ptr[i].packet_count = (uint16_t)(asf->nb_packets - packet_st); - asf->index_ptr[i].send_time = start_sec * INT64_C(10000000); - asf->index_ptr[i].offset = offset; - asf->maximum_packet = FFMAX(asf->maximum_packet, - (uint16_t)(asf->nb_packets - packet_st)); - } - asf->nb_index_count = start_sec; - asf->last_indexed_pts = duration; - } + uint16_t packet_count = asf->nb_packets - packet_number; + ret = update_index(s, start_sec, packet_number, packet_count, offset); + if (ret < 0) + return ret; } + asf->end_sec = start_sec; + return 0; } @@ -886,9 +918,9 @@ static int asf_write_index(AVFormatContext *s, ASFIndex *index, AVIOContext *pb = s->pb; int i; - put_guid(pb, &ff_asf_simple_index_header); + ff_put_guid(pb, &ff_asf_simple_index_header); avio_wl64(pb, 24 + 16 + 8 + 4 + 4 + (4 + 2) * count); - put_guid(pb, &ff_asf_my_guid); + ff_put_guid(pb, &ff_asf_my_guid); avio_wl64(pb, ASF_INDEXED_INTERVAL); avio_wl32(pb, max); avio_wl32(pb, count); @@ -904,6 +936,7 @@ static int asf_write_trailer(AVFormatContext *s) { ASFContext *asf = s->priv_data; int64_t file_size, data_size; + int ret; /* flush the current packet */ if (asf->pb.buf_ptr > asf->pb.buffer) @@ -911,8 +944,11 @@ static int asf_write_trailer(AVFormatContext *s) /* write index */ data_size = avio_tell(s->pb); - if ((!asf->is_streamed) && (asf->nb_index_count != 0)) - asf_write_index(s, asf->index_ptr, asf->maximum_packet, asf->nb_index_count); + if (!asf->is_streamed && asf->next_start_sec) { + if ((ret = update_index(s, asf->end_sec + 1, 0, 0, 0)) < 0) + return ret; + asf_write_index(s, asf->index_ptr, asf->maximum_packet, asf->next_start_sec); + } avio_flush(s->pb); if (asf->is_streamed || !s->pb->seekable) { @@ -924,7 +960,7 @@ static int asf_write_trailer(AVFormatContext *s) asf_write_header1(s, file_size, data_size - asf->data_offset); } - av_free(asf->index_ptr); + av_freep(&asf->index_ptr); return 0; } @@ -935,7 +971,7 @@ AVOutputFormat ff_asf_muxer = { .mime_type = "video/x-ms-asf", .extensions = "asf,wmv,wma", .priv_data_size = sizeof(ASFContext), - .audio_codec = CONFIG_LIBMP3LAME ? AV_CODEC_ID_MP3 : AV_CODEC_ID_MP2, + .audio_codec = AV_CODEC_ID_WMAV2, .video_codec = AV_CODEC_ID_MSMPEG4V3, .write_header = asf_write_header, .write_packet = asf_write_packet, @@ -954,7 +990,7 @@ AVOutputFormat ff_asf_stream_muxer = { .mime_type = "video/x-ms-asf", .extensions = "asf,wmv,wma", .priv_data_size = sizeof(ASFContext), - .audio_codec = CONFIG_LIBMP3LAME ? AV_CODEC_ID_MP3 : AV_CODEC_ID_MP2, + .audio_codec = AV_CODEC_ID_WMAV2, .video_codec = AV_CODEC_ID_MSMPEG4V3, .write_header = asf_write_stream_header, .write_packet = asf_write_packet, diff --git a/libavformat/assdec.c b/libavformat/assdec.c index 0041ca4cc3..35fcb519c1 100644 --- a/libavformat/assdec.c +++ b/libavformat/assdec.c @@ -2,37 +2,34 @@ * SSA/ASS demuxer * Copyright (c) 2008 Michael Niedermayer * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "libavutil/mathematics.h" #include "avformat.h" #include "internal.h" - -#define MAX_LINESIZE 2000 +#include "subtitles.h" +#include "libavcodec/internal.h" +#include "libavutil/bprint.h" typedef struct ASSContext{ - uint8_t *event_buffer; - uint8_t **event; - unsigned int event_count; - unsigned int event_index; + FFDemuxSubtitlesQueue q; }ASSContext; -static int probe(AVProbeData *p) +static int ass_probe(AVProbeData *p) { const char *header= "[Script Info]"; @@ -43,174 +40,130 @@ static int probe(AVProbeData *p) return 0; } -static int read_close(AVFormatContext *s) +static int ass_read_close(AVFormatContext *s) { ASSContext *ass = s->priv_data; - - av_freep(&ass->event_buffer); - av_freep(&ass->event); - + ff_subtitles_queue_clean(&ass->q); return 0; } -static int64_t get_pts(const uint8_t *p) +static int read_ts(const uint8_t *p, int64_t *start, int *duration) { - int hour, min, sec, hsec; - - if(sscanf(p, "%*[^,],%d:%d:%d%*c%d", &hour, &min, &sec, &hsec) != 4) - return AV_NOPTS_VALUE; - - av_dlog(NULL, "%d %d %d %d [%s]\n", hour, min, sec, hsec, p); - - min+= 60*hour; - sec+= 60*min; - - return sec*100+hsec; + int64_t end; + int hh1, mm1, ss1, ms1; + int hh2, mm2, ss2, ms2; + + if (sscanf(p, "%*[^,],%d:%d:%d%*c%d,%d:%d:%d%*c%d", + &hh1, &mm1, &ss1, &ms1, + &hh2, &mm2, &ss2, &ms2) == 8) { + end = (hh2*3600LL + mm2*60LL + ss2) * 100LL + ms2; + *start = (hh1*3600LL + mm1*60LL + ss1) * 100LL + ms1; + *duration = end - *start; + return 0; + } + return -1; } -static int event_cmp(const void *_a, const void *_b) +static int64_t get_line(AVBPrint *buf, AVIOContext *pb) { - const uint8_t *const *a = _a, *const *b = _b; - return get_pts(*a) - get_pts(*b); + int64_t pos = avio_tell(pb); + + av_bprint_clear(buf); + for (;;) { + char c = avio_r8(pb); + if (!c) + break; + av_bprint_chars(buf, c, 1); + if (c == '\n') + break; + } + return pos; } -static int read_header(AVFormatContext *s) +static int ass_read_header(AVFormatContext *s) { - int i, len, header_remaining; ASSContext *ass = s->priv_data; - AVIOContext *pb = s->pb; + AVBPrint header, line; + int header_remaining, res = 0; AVStream *st; - int allocated[2]={0}; - uint8_t *p, **dst[2]={0}; - int pos[2]={0}; st = avformat_new_stream(s, NULL); if (!st) - return -1; + return AVERROR(ENOMEM); avpriv_set_pts_info(st, 64, 1, 100); st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE; st->codec->codec_id= AV_CODEC_ID_SSA; header_remaining= INT_MAX; - dst[0] = &st->codec->extradata; - dst[1] = &ass->event_buffer; - while(!pb->eof_reached){ - uint8_t line[MAX_LINESIZE]; - len = ff_get_line(pb, line, sizeof(line)); + av_bprint_init(&header, 0, AV_BPRINT_SIZE_UNLIMITED); + av_bprint_init(&line, 0, AV_BPRINT_SIZE_UNLIMITED); - if(!memcmp(line, "[Events]", 8)) - header_remaining= 2; - else if(line[0]=='[') - header_remaining= INT_MAX; + for (;;) { + int64_t pos = get_line(&line, s->pb); - i= header_remaining==0; + if (!line.str[0]) // EOF + break; - if(i && get_pts(line) == AV_NOPTS_VALUE) - continue; + if (!memcmp(line.str, "[Events]", 8)) + header_remaining= 2; + else if (line.str[0]=='[') + header_remaining= INT_MAX; - p = av_fast_realloc(*(dst[i]), &allocated[i], pos[i]+MAX_LINESIZE); - if(!p) - goto fail; - *(dst[i])= p; - memcpy(p + pos[i], line, len+1); - pos[i] += len; - if(i) ass->event_count++; - else header_remaining--; - } - st->codec->extradata_size= pos[0]; - - if(ass->event_count >= UINT_MAX / sizeof(*ass->event)) - goto fail; - - ass->event= av_malloc(ass->event_count * sizeof(*ass->event)); - p= ass->event_buffer; - for(i=0; i<ass->event_count; i++){ - ass->event[i]= p; - while(*p && *p != '\n') - p++; - p++; + if (header_remaining) { + av_bprintf(&header, "%s", line.str); + header_remaining--; + } else { + int64_t ts_start = AV_NOPTS_VALUE; + int duration = -1; + AVPacket *sub; + + if (read_ts(line.str, &ts_start, &duration) < 0) + continue; + sub = ff_subtitles_queue_insert(&ass->q, line.str, line.len, 0); + if (!sub) { + res = AVERROR(ENOMEM); + goto end; + } + sub->pos = pos; + sub->pts = ts_start; + sub->duration = duration; + } } - qsort(ass->event, ass->event_count, sizeof(*ass->event), event_cmp); + av_bprint_finalize(&line, NULL); - return 0; + res = avpriv_bprint_to_extradata(st->codec, &header); + if (res < 0) + goto end; -fail: - read_close(s); + ff_subtitles_queue_finalize(&ass->q); - return -1; +end: + return res; } -static int read_packet(AVFormatContext *s, AVPacket *pkt) +static int ass_read_packet(AVFormatContext *s, AVPacket *pkt) { ASSContext *ass = s->priv_data; - uint8_t *p, *end; - - if(ass->event_index >= ass->event_count) - return AVERROR(EIO); - - p= ass->event[ ass->event_index ]; - - end= strchr(p, '\n'); - av_new_packet(pkt, end ? end-p+1 : strlen(p)); - pkt->flags |= AV_PKT_FLAG_KEY; - pkt->pos= p - ass->event_buffer + s->streams[0]->codec->extradata_size; - pkt->pts= pkt->dts= get_pts(p); - memcpy(pkt->data, p, pkt->size); - - ass->event_index++; - - return 0; + return ff_subtitles_queue_read_packet(&ass->q, pkt); } -static int read_seek2(AVFormatContext *s, int stream_index, - int64_t min_ts, int64_t ts, int64_t max_ts, int flags) +static int ass_read_seek(AVFormatContext *s, int stream_index, + int64_t min_ts, int64_t ts, int64_t max_ts, int flags) { ASSContext *ass = s->priv_data; - - if (flags & AVSEEK_FLAG_BYTE) { - return AVERROR(ENOSYS); - } else if (flags & AVSEEK_FLAG_FRAME) { - if (ts < 0 || ts >= ass->event_count) - return AVERROR(ERANGE); - ass->event_index = ts; - } else { - int i, idx = -1; - int64_t min_ts_diff = INT64_MAX; - if (stream_index == -1) { - AVRational time_base = s->streams[0]->time_base; - ts = av_rescale_q(ts, AV_TIME_BASE_Q, time_base); - min_ts = av_rescale_rnd(min_ts, time_base.den, - time_base.num * (int64_t)AV_TIME_BASE, - AV_ROUND_UP); - max_ts = av_rescale_rnd(max_ts, time_base.den, - time_base.num * (int64_t)AV_TIME_BASE, - AV_ROUND_DOWN); - } - /* TODO: ass->event[] is sorted by pts so we could do a binary search */ - for (i=0; i<ass->event_count; i++) { - int64_t pts = get_pts(ass->event[i]); - int64_t ts_diff = FFABS(pts - ts); - if (pts >= min_ts && pts <= max_ts && ts_diff < min_ts_diff) { - min_ts_diff = ts_diff; - idx = i; - } - } - if (idx < 0) - return AVERROR(ERANGE); - ass->event_index = idx; - } - return 0; + return ff_subtitles_queue_seek(&ass->q, s, stream_index, + min_ts, ts, max_ts, flags); } AVInputFormat ff_ass_demuxer = { .name = "ass", .long_name = NULL_IF_CONFIG_SMALL("SSA (SubStation Alpha) subtitle"), .priv_data_size = sizeof(ASSContext), - .read_probe = probe, - .read_header = read_header, - .read_packet = read_packet, - .read_close = read_close, - .read_seek2 = read_seek2, + .read_probe = ass_probe, + .read_header = ass_read_header, + .read_packet = ass_read_packet, + .read_close = ass_read_close, + .read_seek2 = ass_read_seek, }; diff --git a/libavformat/assenc.c b/libavformat/assenc.c index 751485dfb5..9fb9f232d9 100644 --- a/libavformat/assenc.c +++ b/libavformat/assenc.c @@ -2,27 +2,29 @@ * SSA/ASS muxer * Copyright (c) 2008 Michael Niedermayer * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "avformat.h" +#include "internal.h" typedef struct ASSContext{ unsigned int extra_index; + int write_ts; // 0: ssa (timing in payload), 1: ass (matroska like) }ASSContext; static int write_header(AVFormatContext *s) @@ -31,10 +33,13 @@ static int write_header(AVFormatContext *s) AVCodecContext *avctx= s->streams[0]->codec; uint8_t *last= NULL; - if(s->nb_streams != 1 || avctx->codec_id != AV_CODEC_ID_SSA){ + if (s->nb_streams != 1 || (avctx->codec_id != AV_CODEC_ID_SSA && + avctx->codec_id != AV_CODEC_ID_ASS)) { av_log(s, AV_LOG_ERROR, "Exactly one ASS/SSA stream is needed.\n"); return -1; } + ass->write_ts = avctx->codec_id == AV_CODEC_ID_ASS; + avpriv_set_pts_info(s->streams[0], 64, 1, 100); while(ass->extra_index < avctx->extradata_size){ uint8_t *p = avctx->extradata + ass->extra_index; @@ -57,7 +62,32 @@ static int write_header(AVFormatContext *s) static int write_packet(AVFormatContext *s, AVPacket *pkt) { - avio_write(s->pb, pkt->data, pkt->size); + ASSContext *ass = s->priv_data; + + if (ass->write_ts) { + long int layer; + char *p; + int64_t start = pkt->pts; + int64_t end = start + pkt->duration; + int hh1, mm1, ss1, ms1; + int hh2, mm2, ss2, ms2; + + p = pkt->data + strcspn(pkt->data, ",") + 1; // skip ReadOrder + layer = strtol(p, &p, 10); + if (*p == ',') + p++; + hh1 = (int)(start / 360000); mm1 = (int)(start / 6000) % 60; + hh2 = (int)(end / 360000); mm2 = (int)(end / 6000) % 60; + ss1 = (int)(start / 100) % 60; ms1 = (int)(start % 100); + ss2 = (int)(end / 100) % 60; ms2 = (int)(end % 100); + if (hh1 > 9) hh1 = 9, mm1 = 59, ss1 = 59, ms1 = 99; + if (hh2 > 9) hh2 = 9, mm2 = 59, ss2 = 59, ms2 = 99; + avio_printf(s->pb, "Dialogue: %ld,%d:%02d:%02d.%02d,%d:%02d:%02d.%02d,%s\r\n", + layer, hh1, mm1, ss1, ms1, hh2, mm2, ss2, ms2, p); + } else { + avio_write(s->pb, pkt->data, pkt->size); + } + return 0; } @@ -82,5 +112,5 @@ AVOutputFormat ff_ass_muxer = { .write_header = write_header, .write_packet = write_packet, .write_trailer = write_trailer, - .flags = AVFMT_GLOBALHEADER | AVFMT_NOTIMESTAMPS, + .flags = AVFMT_GLOBALHEADER | AVFMT_NOTIMESTAMPS | AVFMT_TS_NONSTRICT, }; diff --git a/libavformat/ast.c b/libavformat/ast.c new file mode 100644 index 0000000000..9de74aae69 --- /dev/null +++ b/libavformat/ast.c @@ -0,0 +1,29 @@ +/* + * AST common code + * Copyright (c) 2012 James Almer + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "avformat.h" +#include "internal.h" + +const AVCodecTag ff_codec_ast_tags[] = { + { AV_CODEC_ID_ADPCM_AFC, 0 }, + { AV_CODEC_ID_PCM_S16BE_PLANAR, 1 }, + { AV_CODEC_ID_NONE, 0 }, +}; diff --git a/libavformat/ast.h b/libavformat/ast.h new file mode 100644 index 0000000000..4a399ea410 --- /dev/null +++ b/libavformat/ast.h @@ -0,0 +1,30 @@ +/* + * AST common code + * Copyright (c) 2012 James Almer + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVFORMAT_AST_H +#define AVFORMAT_AST_H + +#include "avformat.h" +#include "internal.h" + +extern const AVCodecTag ff_codec_ast_tags[]; + +#endif /* AVFORMAT_AST_H */ diff --git a/libavformat/astdec.c b/libavformat/astdec.c new file mode 100644 index 0000000000..886274410d --- /dev/null +++ b/libavformat/astdec.c @@ -0,0 +1,119 @@ +/* + * AST demuxer + * Copyright (c) 2012 Paul B Mahol + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/channel_layout.h" +#include "libavutil/intreadwrite.h" +#include "avformat.h" +#include "internal.h" +#include "ast.h" + +static int ast_probe(AVProbeData *p) +{ + if (AV_RL32(p->buf) == MKTAG('S','T','R','M') && + AV_RB16(p->buf + 10) && + AV_RB16(p->buf + 12) && + AV_RB32(p->buf + 16)) + return AVPROBE_SCORE_MAX / 3 * 2; + return 0; +} + +static int ast_read_header(AVFormatContext *s) +{ + int depth; + AVStream *st; + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + avio_skip(s->pb, 8); + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_id = ff_codec_get_id(ff_codec_ast_tags, avio_rb16(s->pb)); + + depth = avio_rb16(s->pb); + if (depth != 16) { + avpriv_request_sample(s, "depth %d", depth); + return AVERROR_INVALIDDATA; + } + + st->codec->channels = avio_rb16(s->pb); + if (!st->codec->channels) + return AVERROR_INVALIDDATA; + + if (st->codec->channels == 2) + st->codec->channel_layout = AV_CH_LAYOUT_STEREO; + else if (st->codec->channels == 4) + st->codec->channel_layout = AV_CH_LAYOUT_4POINT0; + + avio_skip(s->pb, 2); + st->codec->sample_rate = avio_rb32(s->pb); + if (st->codec->sample_rate <= 0) + return AVERROR_INVALIDDATA; + st->start_time = 0; + st->duration = avio_rb32(s->pb); + avio_skip(s->pb, 40); + avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate); + + return 0; +} + +static int ast_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + uint32_t type, size; + int64_t pos; + int ret; + + if (url_feof(s->pb)) + return AVERROR_EOF; + + pos = avio_tell(s->pb); + type = avio_rl32(s->pb); + size = avio_rb32(s->pb); + if (size > INT_MAX / s->streams[0]->codec->channels) + return AVERROR_INVALIDDATA; + + size *= s->streams[0]->codec->channels; + if ((ret = avio_skip(s->pb, 24)) < 0) // padding + return ret; + + if (type == MKTAG('B','L','C','K')) { + ret = av_get_packet(s->pb, pkt, size); + pkt->stream_index = 0; + pkt->pos = pos; + } else { + av_log(s, AV_LOG_ERROR, "unknown chunk %x\n", type); + avio_skip(s->pb, size); + ret = AVERROR_INVALIDDATA; + } + + return ret; +} + +AVInputFormat ff_ast_demuxer = { + .name = "ast", + .long_name = NULL_IF_CONFIG_SMALL("AST (Audio Stream)"), + .read_probe = ast_probe, + .read_header = ast_read_header, + .read_packet = ast_read_packet, + .extensions = "ast", + .flags = AVFMT_GENERIC_INDEX, + .codec_tag = (const AVCodecTag* const []){ff_codec_ast_tags, 0}, +}; diff --git a/libavformat/astenc.c b/libavformat/astenc.c new file mode 100644 index 0000000000..edd802cbf3 --- /dev/null +++ b/libavformat/astenc.c @@ -0,0 +1,214 @@ +/* + * AST muxer + * Copyright (c) 2012 James Almer + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "avformat.h" +#include "avio_internal.h" +#include "internal.h" +#include "ast.h" +#include "libavutil/mathematics.h" +#include "libavutil/opt.h" + +typedef struct ASTMuxContext { + AVClass *class; + int64_t size; + int64_t samples; + int64_t loopstart; + int64_t loopend; + int fbs; +} ASTMuxContext; + +#define CHECK_LOOP(type) \ + if (ast->loop ## type > 0) { \ + ast->loop ## type = av_rescale_rnd(ast->loop ## type, enc->sample_rate, 1000, AV_ROUND_DOWN); \ + if (ast->loop ## type < 0 || ast->loop ## type > UINT_MAX) { \ + av_log(s, AV_LOG_ERROR, "Invalid loop" #type " value\n"); \ + return AVERROR(EINVAL); \ + } \ + } + +static int ast_write_header(AVFormatContext *s) +{ + ASTMuxContext *ast = s->priv_data; + AVIOContext *pb = s->pb; + AVCodecContext *enc; + unsigned int codec_tag; + + if (s->nb_streams == 1) { + enc = s->streams[0]->codec; + } else { + av_log(s, AV_LOG_ERROR, "only one stream is supported\n"); + return AVERROR(EINVAL); + } + + if (enc->codec_id == AV_CODEC_ID_ADPCM_AFC) { + av_log(s, AV_LOG_ERROR, "muxing ADPCM AFC is not implemented\n"); + return AVERROR_PATCHWELCOME; + } + + codec_tag = ff_codec_get_tag(ff_codec_ast_tags, enc->codec_id); + if (!codec_tag) { + av_log(s, AV_LOG_ERROR, "unsupported codec\n"); + return AVERROR(EINVAL); + } + + if (ast->loopend > 0 && ast->loopstart >= ast->loopend) { + av_log(s, AV_LOG_ERROR, "loopend can't be less or equal to loopstart\n"); + return AVERROR(EINVAL); + } + + /* Convert milliseconds to samples */ + CHECK_LOOP(start) + CHECK_LOOP(end) + + ffio_wfourcc(pb, "STRM"); + + ast->size = avio_tell(pb); + avio_wb32(pb, 0); /* File size minus header */ + avio_wb16(pb, codec_tag); + avio_wb16(pb, 16); /* Bit depth */ + avio_wb16(pb, enc->channels); + avio_wb16(pb, 0); /* Loop flag */ + avio_wb32(pb, enc->sample_rate); + + ast->samples = avio_tell(pb); + avio_wb32(pb, 0); /* Number of samples */ + avio_wb32(pb, 0); /* Loopstart */ + avio_wb32(pb, 0); /* Loopend */ + avio_wb32(pb, 0); /* Size of first block */ + + /* Unknown */ + avio_wb32(pb, 0); + avio_wl32(pb, 0x7F); + avio_wb64(pb, 0); + avio_wb64(pb, 0); + avio_wb32(pb, 0); + + avio_flush(pb); + + return 0; +} + +static int ast_write_packet(AVFormatContext *s, AVPacket *pkt) +{ + AVIOContext *pb = s->pb; + ASTMuxContext *ast = s->priv_data; + AVCodecContext *enc = s->streams[0]->codec; + int size = pkt->size / enc->channels; + + if (enc->frame_number == 1) + ast->fbs = size; + + ffio_wfourcc(pb, "BLCK"); + avio_wb32(pb, size); /* Block size */ + + /* padding */ + avio_wb64(pb, 0); + avio_wb64(pb, 0); + avio_wb64(pb, 0); + + avio_write(pb, pkt->data, pkt->size); + + return 0; +} + +static int ast_write_trailer(AVFormatContext *s) +{ + AVIOContext *pb = s->pb; + ASTMuxContext *ast = s->priv_data; + AVCodecContext *enc = s->streams[0]->codec; + int64_t file_size = avio_tell(pb); + int64_t samples = (file_size - 64 - (32 * enc->frame_number)) / enc->block_align; /* PCM_S16BE_PLANAR */ + + av_log(s, AV_LOG_DEBUG, "total samples: %"PRId64"\n", samples); + + if (s->pb->seekable) { + /* Number of samples */ + avio_seek(pb, ast->samples, SEEK_SET); + avio_wb32(pb, samples); + + /* Loopstart if provided */ + if (ast->loopstart > 0) { + if (ast->loopstart >= samples) { + av_log(s, AV_LOG_WARNING, "Loopstart value is out of range and will be ignored\n"); + ast->loopstart = -1; + avio_skip(pb, 4); + } else + avio_wb32(pb, ast->loopstart); + } else + avio_skip(pb, 4); + + /* Loopend if provided. Otherwise number of samples again */ + if (ast->loopend && ast->loopstart >= 0) { + if (ast->loopend > samples) { + av_log(s, AV_LOG_WARNING, "Loopend value is out of range and will be ignored\n"); + ast->loopend = samples; + } + avio_wb32(pb, ast->loopend); + } else { + avio_wb32(pb, samples); + } + + /* Size of first block */ + avio_wb32(pb, ast->fbs); + + /* File size minus header */ + avio_seek(pb, ast->size, SEEK_SET); + avio_wb32(pb, file_size - 64); + + /* Loop flag */ + if (ast->loopstart >= 0) { + avio_skip(pb, 6); + avio_wb16(pb, 0xFFFF); + } + + avio_seek(pb, file_size, SEEK_SET); + avio_flush(pb); + } + return 0; +} + +#define OFFSET(obj) offsetof(ASTMuxContext, obj) +static const AVOption options[] = { + { "loopstart", "Loopstart position in milliseconds.", OFFSET(loopstart), AV_OPT_TYPE_INT64, { .i64 = -1 }, -1, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM }, + { "loopend", "Loopend position in milliseconds.", OFFSET(loopend), AV_OPT_TYPE_INT64, { .i64 = 0 }, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM }, + { NULL }, +}; + +static const AVClass ast_muxer_class = { + .class_name = "AST muxer", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + +AVOutputFormat ff_ast_muxer = { + .name = "ast", + .long_name = NULL_IF_CONFIG_SMALL("AST (Audio Stream)"), + .extensions = "ast", + .priv_data_size = sizeof(ASTMuxContext), + .audio_codec = AV_CODEC_ID_PCM_S16BE_PLANAR, + .video_codec = AV_CODEC_ID_NONE, + .write_header = ast_write_header, + .write_packet = ast_write_packet, + .write_trailer = ast_write_trailer, + .priv_class = &ast_muxer_class, + .codec_tag = (const AVCodecTag* const []){ff_codec_ast_tags, 0}, +}; diff --git a/libavformat/au.c b/libavformat/au.c index 6b252b21ce..edf0f32f00 100644 --- a/libavformat/au.c +++ b/libavformat/au.c @@ -4,20 +4,20 @@ * * first version by Francois Revol <revol@free.fr> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -31,6 +31,12 @@ #include "internal.h" #include "avio_internal.h" #include "pcm.h" +#include "libavutil/avassert.h" + +/* if we don't know the size in advance */ +#define AU_UNKNOWN_SIZE ((uint32_t)(~0)) +/* the specification requires an annotation field of at least eight bytes */ +#define AU_HEADER_SIZE (24+8) static const AVCodecTag codec_au_tags[] = { { AV_CODEC_ID_PCM_MULAW, 1 }, @@ -40,7 +46,12 @@ static const AVCodecTag codec_au_tags[] = { { AV_CODEC_ID_PCM_S32BE, 5 }, { AV_CODEC_ID_PCM_F32BE, 6 }, { AV_CODEC_ID_PCM_F64BE, 7 }, + { AV_CODEC_ID_ADPCM_G726LE, 23 }, + { AV_CODEC_ID_ADPCM_G722,24 }, + { AV_CODEC_ID_ADPCM_G726LE, 25 }, + { AV_CODEC_ID_ADPCM_G726LE, 26 }, { AV_CODEC_ID_PCM_ALAW, 27 }, + { AV_CODEC_ID_ADPCM_G726LE, MKBETAG('7','2','6','2') }, { AV_CODEC_ID_NONE, 0 }, }; @@ -59,7 +70,7 @@ static int au_probe(AVProbeData *p) static int au_read_header(AVFormatContext *s) { - int size; + int size, data_size = 0; unsigned int tag; AVIOContext *pb = s->pb; unsigned int id, channels, rate; @@ -71,7 +82,12 @@ static int au_read_header(AVFormatContext *s) if (tag != MKTAG('.', 's', 'n', 'd')) return AVERROR_INVALIDDATA; size = avio_rb32(pb); /* header size */ - avio_rb32(pb); /* data size */ + data_size = avio_rb32(pb); /* data size in bytes */ + + if (data_size < 0 && data_size != AU_UNKNOWN_SIZE) { + av_log(s, AV_LOG_ERROR, "Invalid negative data size '%d' found\n", data_size); + return AVERROR_INVALIDDATA; + } id = avio_rb32(pb); rate = avio_rb32(pb); @@ -90,7 +106,15 @@ static int au_read_header(AVFormatContext *s) } bps = av_get_bits_per_sample(codec); - if (!bps) { + if (codec == AV_CODEC_ID_ADPCM_G726LE) { + if (id == MKBETAG('7','2','6','2')) { + bps = 2; + } else { + const uint8_t bpcss[] = {4, 0, 3, 5}; + av_assert0(id >= 23 && id < 23 + 4); + bps = bpcss[id - 23]; + } + } else if (!bps) { avpriv_request_sample(s, "Unknown bits per sample"); return AVERROR_PATCHWELCOME; } @@ -113,8 +137,11 @@ static int au_read_header(AVFormatContext *s) st->codec->codec_id = codec; st->codec->channels = channels; st->codec->sample_rate = rate; + st->codec->bits_per_coded_sample = bps; st->codec->bit_rate = channels * rate * bps; - st->codec->block_align = channels * bps >> 3; + st->codec->block_align = FFMAX(bps * st->codec->channels / 8, 1); + if (data_size != AU_UNKNOWN_SIZE) + st->duration = (((int64_t)data_size)<<3) / (st->codec->channels * (int64_t)bps); st->start_time = 0; avpriv_set_pts_info(st, 64, 1, rate); @@ -122,27 +149,12 @@ static int au_read_header(AVFormatContext *s) return 0; } -static int au_read_packet(AVFormatContext *s, AVPacket *pkt) -{ - int ret; - - ret = av_get_packet(s->pb, pkt, BLOCK_SIZE * - s->streams[0]->codec->block_align); - if (ret < 0) - return ret; - - pkt->stream_index = 0; - pkt->duration = ret / s->streams[0]->codec->block_align; - - return 0; -} - AVInputFormat ff_au_demuxer = { .name = "au", .long_name = NULL_IF_CONFIG_SMALL("Sun AU"), .read_probe = au_probe, .read_header = au_read_header, - .read_packet = au_read_packet, + .read_packet = ff_pcm_read_packet, .read_seek = ff_pcm_read_seek, .codec_tag = (const AVCodecTag* const []) { codec_au_tags, 0 }, }; @@ -153,35 +165,29 @@ AVInputFormat ff_au_demuxer = { #include "rawenc.h" -/* if we don't know the size in advance */ -#define AU_UNKNOWN_SIZE ((uint32_t)(~0)) - -/* AUDIO_FILE header */ -static int put_au_header(AVIOContext *pb, AVCodecContext *enc) +static int au_write_header(AVFormatContext *s) { - if (!enc->codec_tag) + AVIOContext *pb = s->pb; + AVCodecContext *enc = s->streams[0]->codec; + + if (s->nb_streams != 1) { + av_log(s, AV_LOG_ERROR, "only one stream is supported\n"); + return AVERROR(EINVAL); + } + + enc->codec_tag = ff_codec_get_tag(codec_au_tags, enc->codec_id); + if (!enc->codec_tag) { + av_log(s, AV_LOG_ERROR, "unsupported codec\n"); return AVERROR(EINVAL); + } ffio_wfourcc(pb, ".snd"); /* magic number */ - avio_wb32(pb, 24); /* header size */ + avio_wb32(pb, AU_HEADER_SIZE); /* header size */ avio_wb32(pb, AU_UNKNOWN_SIZE); /* data size */ avio_wb32(pb, enc->codec_tag); /* codec ID */ avio_wb32(pb, enc->sample_rate); avio_wb32(pb, enc->channels); - - return 0; -} - -static int au_write_header(AVFormatContext *s) -{ - AVIOContext *pb = s->pb; - int ret; - - s->priv_data = NULL; - - if ((ret = put_au_header(pb, s->streams[0]->codec)) < 0) - return ret; - + avio_wb64(pb, 0); /* annotation field */ avio_flush(pb); return 0; @@ -190,13 +196,12 @@ static int au_write_header(AVFormatContext *s) static int au_write_trailer(AVFormatContext *s) { AVIOContext *pb = s->pb; - int64_t file_size; + int64_t file_size = avio_tell(pb); - if (s->pb->seekable) { + if (s->pb->seekable && file_size < INT32_MAX) { /* update file size */ - file_size = avio_tell(pb); avio_seek(pb, 8, SEEK_SET); - avio_wb32(pb, (uint32_t)(file_size - 24)); + avio_wb32(pb, (uint32_t)(file_size - AU_HEADER_SIZE)); avio_seek(pb, file_size, SEEK_SET); avio_flush(pb); } diff --git a/libavformat/audiointerleave.c b/libavformat/audiointerleave.c index e4cde9d0aa..2aa95f3dc6 100644 --- a/libavformat/audiointerleave.c +++ b/libavformat/audiointerleave.c @@ -3,20 +3,20 @@ * * Copyright (c) 2009 Baptiste Coudurier <baptiste dot coudurier at gmail dot com> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -47,6 +47,10 @@ int ff_audio_interleave_init(AVFormatContext *s, if (!samples_per_frame) return -1; + if (!time_base.num) { + av_log(s, AV_LOG_ERROR, "timebase not set for audio interleave\n"); + return -1; + } for (i = 0; i < s->nb_streams; i++) { AVStream *st = s->streams[i]; AudioInterleaveContext *aic = st->priv_data; @@ -80,7 +84,8 @@ static int interleave_new_audio_packet(AVFormatContext *s, AVPacket *pkt, if (!size || (!flush && size == av_fifo_size(aic->fifo))) return 0; - av_new_packet(pkt, size); + if (av_new_packet(pkt, size) < 0) + return AVERROR(ENOMEM); av_fifo_generic_read(aic->fifo, pkt->data, size, NULL); pkt->dts = pkt->pts = aic->dts; @@ -113,10 +118,13 @@ int ff_audio_rechunk_interleave(AVFormatContext *s, AVPacket *out, AVPacket *pkt } av_fifo_generic_write(aic->fifo, pkt->data, pkt->size, NULL); } else { + int ret; // rewrite pts and dts to be decoded time line position pkt->pts = pkt->dts = aic->dts; aic->dts += pkt->duration; - ff_interleave_add_packet(s, pkt, compare_ts); + ret = ff_interleave_add_packet(s, pkt, compare_ts); + if (ret < 0) + return ret; } pkt = NULL; } @@ -125,8 +133,14 @@ int ff_audio_rechunk_interleave(AVFormatContext *s, AVPacket *out, AVPacket *pkt AVStream *st = s->streams[i]; if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) { AVPacket new_pkt; - while (interleave_new_audio_packet(s, &new_pkt, i, flush)) - ff_interleave_add_packet(s, &new_pkt, compare_ts); + int ret; + while ((ret = interleave_new_audio_packet(s, &new_pkt, i, flush)) > 0) { + ret = ff_interleave_add_packet(s, &new_pkt, compare_ts); + if (ret < 0) + return ret; + } + if (ret < 0) + return ret; } } diff --git a/libavformat/audiointerleave.h b/libavformat/audiointerleave.h index 9c7b548c1d..4d77832fa1 100644 --- a/libavformat/audiointerleave.h +++ b/libavformat/audiointerleave.h @@ -3,20 +3,20 @@ * * Copyright (c) 2009 Baptiste Coudurier <baptiste dot coudurier at gmail dot com> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/avc.c b/libavformat/avc.c index 2fd5ac807b..f5c513bb31 100644 --- a/libavformat/avc.c +++ b/libavformat/avc.c @@ -2,20 +2,20 @@ * AVC helper functions for muxers * Copyright (c) 2006 Baptiste Coudurier <baptiste.coudurier@smartjog.com> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/avc.h b/libavformat/avc.h index 579756ec0f..972e19b94a 100644 --- a/libavformat/avc.h +++ b/libavformat/avc.h @@ -2,20 +2,20 @@ * AVC helper functions for muxers * Copyright (c) 2008 Aurelien Jacobs <aurel@gnuage.org> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/avformat.h b/libavformat/avformat.h index 50e4f5c69d..6bd54cec64 100644 --- a/libavformat/avformat.h +++ b/libavformat/avformat.h @@ -1,20 +1,20 @@ /* * copyright (c) 2001 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -50,7 +50,7 @@ * * Main lavf structure used for both muxing and demuxing is AVFormatContext, * which exports all information about the file being read or written. As with - * most Libav structures, its size is not part of public ABI, so it cannot be + * most Libavformat structures, its size is not part of public ABI, so it cannot be * allocated on stack or directly with av_malloc(). To create an * AVFormatContext, use avformat_alloc_context() (some functions, like * avformat_open_input() might do that for you). @@ -220,7 +220,7 @@ struct AVFormatContext; * * Metadata is exported or set as pairs of key/value strings in the 'metadata' * fields of the AVFormatContext, AVStream, AVChapter and AVProgram structs - * using the @ref lavu_dict "AVDictionary" API. Like all strings in Libav, + * using the @ref lavu_dict "AVDictionary" API. Like all strings in FFmpeg, * metadata is assumed to be UTF-8 encoded Unicode. Note that metadata * exported by demuxers isn't checked to be valid UTF-8 in most cases. * @@ -337,6 +337,7 @@ typedef struct AVProbeData { int buf_size; /**< Size of buf except extra allocated bytes */ } AVProbeData; +#define AVPROBE_SCORE_RETRY (AVPROBE_SCORE_MAX/4) #define AVPROBE_SCORE_EXTENSION 50 ///< score for file extension #define AVPROBE_SCORE_MAX 100 ///< maximum score @@ -359,14 +360,24 @@ typedef struct AVProbeData { #define AVFMT_NOGENSEARCH 0x4000 /**< Format does not allow to fall back on generic search */ #define AVFMT_NO_BYTE_SEEK 0x8000 /**< Format does not allow seeking by bytes */ #define AVFMT_ALLOW_FLUSH 0x10000 /**< Format allows flushing. If not set, the muxer will not receive a NULL packet in the write_packet function. */ -#define AVFMT_TS_NONSTRICT 0x20000 /**< Format does not require strictly +#if LIBAVFORMAT_VERSION_MAJOR <= 54 +#define AVFMT_TS_NONSTRICT 0x8020000 //we try to be compatible to the ABIs of ffmpeg and major forks +#else +#define AVFMT_TS_NONSTRICT 0x20000 +#endif + /**< Format does not require strictly increasing timestamps, but they must still be monotonic */ #define AVFMT_TS_NEGATIVE 0x40000 /**< Format allows muxing negative timestamps. If not set the timestamp will be shifted in av_write_frame and av_interleaved_write_frame so they - start from 0. */ + start from 0. + The user or muxer can override this through + AVFormatContext.avoid_negative_ts + */ + +#define AVFMT_SEEK_TO_PTS 0x4000000 /**< Seeking is based on PTS */ /** * @addtogroup lavf_encoding @@ -436,8 +447,12 @@ typedef struct AVOutputFormat { * * @return 1 if the codec is supported, 0 if it is not. * A negative number if unknown. + * MKTAG('A', 'P', 'I', 'C') if the codec is only supported as AV_DISPOSITION_ATTACHED_PIC */ int (*query_codec)(enum AVCodecID id, int std_compliance); + + void (*get_output_timestamp)(struct AVFormatContext *s, int stream, + int64_t *dts, int64_t *wall); } AVOutputFormat; /** * @} @@ -464,7 +479,7 @@ typedef struct AVInputFormat { /** * Can use flags: AVFMT_NOFILE, AVFMT_NEEDNUMBER, AVFMT_SHOW_IDS, * AVFMT_GENERIC_INDEX, AVFMT_TS_DISCONT, AVFMT_NOBINSEARCH, - * AVFMT_NOGENSEARCH, AVFMT_NO_BYTE_SEEK. + * AVFMT_NOGENSEARCH, AVFMT_NO_BYTE_SEEK, AVFMT_SEEK_TO_PTS. */ int flags; @@ -577,11 +592,19 @@ enum AVStreamParseType { AVSTREAM_PARSE_HEADERS, /**< Only parse headers, do not repack. */ AVSTREAM_PARSE_TIMESTAMPS, /**< full parsing and interpolation of timestamps for frames not starting on a packet boundary */ AVSTREAM_PARSE_FULL_ONCE, /**< full parsing and repack of the first frame only, only implemented for H.264 currently */ + AVSTREAM_PARSE_FULL_RAW=MKTAG(0,'R','A','W'), /**< full parsing and repack with timestamp and position generation by parser for raw + this assumes that each packet in the file contains no demuxer level headers and + just codec level data, otherwise position generation would fail */ }; typedef struct AVIndexEntry { int64_t pos; - int64_t timestamp; + int64_t timestamp; /**< + * Timestamp in AVStream.time_base units, preferably the time from which on correctly decoded frames are available + * when seeking to this entry. That means preferable PTS on keyframe based formats. + * But demuxers can choose to store a different timestamp, if it is more convenient for the implementation or nothing better + * is known + */ #define AVINDEX_KEYFRAME 0x0001 int flags:2; int size:30; //Yeah, trying to keep the size of this small to reduce memory requirements (it is 24 vs. 32 bytes due to possible 8-byte alignment). @@ -613,6 +636,20 @@ typedef struct AVIndexEntry { #define AV_DISPOSITION_ATTACHED_PIC 0x0400 /** + * To specify text track kind (different from subtitles default). + */ +#define AV_DISPOSITION_CAPTIONS 0x10000 +#define AV_DISPOSITION_DESCRIPTIONS 0x20000 +#define AV_DISPOSITION_METADATA 0x40000 + +/** + * Options for behavior on timestamp wrap detection. + */ +#define AV_PTS_WRAP_IGNORE 0 ///< ignore the wrap +#define AV_PTS_WRAP_ADD_OFFSET 1 ///< add the format specific offset on wrap detection +#define AV_PTS_WRAP_SUB_OFFSET -1 ///< subtract the format specific offset on wrap detection + +/** * Stream structure. * New fields can be added to the end with minor version bumps. * Removal, reordering and changes to existing fields require a major @@ -658,10 +695,12 @@ typedef struct AVStream { AVRational time_base; /** - * Decoding: pts of the first frame of the stream, in stream time base. + * Decoding: pts of the first frame of the stream in presentation order, in stream time base. * Only set this if you are absolutely 100% sure that the value you set * it to really is the pts of the first frame. * This may be undefined (AV_NOPTS_VALUE). + * @note The ASF header does NOT contain a correct start_time the ASF + * demuxer must NOT set this. */ int64_t start_time; @@ -712,11 +751,18 @@ typedef struct AVStream { /** * Stream information used internally by av_find_stream_info() */ -#define MAX_STD_TIMEBASES (60*12+5) +#define MAX_STD_TIMEBASES (60*12+6) struct { - int nb_decoded_frames; + int64_t last_dts; + int64_t duration_gcd; + int duration_count; + double (*duration_error)[2][MAX_STD_TIMEBASES]; + int64_t codec_info_duration; + int64_t codec_info_duration_fields; int found_decoder; + int64_t last_duration; + /** * Those are used for average framerate estimation. */ @@ -730,11 +776,18 @@ typedef struct AVStream { int pts_wrap_bits; /**< number of bits in pts (used for wrapping control) */ #if FF_API_REFERENCE_DTS - /* a hack to keep ABI compatibility for avconv, which accesses parser even + /* a hack to keep ABI compatibility for ffmpeg and other applications, which accesses parser even * though it should not */ int64_t do_not_use; #endif // Timestamp generation support: + /** + * Timestamp corresponding to the last dts sync point. + * + * Initialized when AVCodecParserContext.dts_sync_point >= 0 and + * a DTS is received from the underlying container. Otherwise set to + * AV_NOPTS_VALUE by default. + */ int64_t first_dts; int64_t cur_dts; int64_t last_IP_pts; @@ -767,8 +820,83 @@ typedef struct AVStream { support seeking natively. */ int nb_index_entries; unsigned int index_entries_allocated_size; + + /** + * Real base framerate of the stream. + * This is the lowest framerate with which all timestamps can be + * represented accurately (it is the least common multiple of all + * framerates in the stream). Note, this value is just a guess! + * For example, if the time base is 1/90000 and all frames have either + * approximately 3600 or 1800 timer ticks, then r_frame_rate will be 50/1. + * + * Code outside avformat should access this field using: + * av_stream_get/set_r_frame_rate(stream) + */ + AVRational r_frame_rate; + + /** + * Stream Identifier + * This is the MPEG-TS stream identifier +1 + * 0 means unknown + */ + int stream_identifier; + + int64_t interleaver_chunk_size; + int64_t interleaver_chunk_duration; + + /** + * stream probing state + * -1 -> probing finished + * 0 -> no probing requested + * rest -> perform probing with request_probe being the minimum score to accept. + * NOT PART OF PUBLIC API + */ + int request_probe; + /** + * Indicates that everything up to the next keyframe + * should be discarded. + */ + int skip_to_keyframe; + + /** + * Number of samples to skip at the start of the frame decoded from the next packet. + */ + int skip_samples; + + /** + * Number of internally decoded frames, used internally in libavformat, do not access + * its lifetime differs from info which is why it is not in that structure. + */ + int nb_decoded_frames; + + /** + * Timestamp offset added to timestamps before muxing + * NOT PART OF PUBLIC API + */ + int64_t mux_ts_offset; + + /** + * Internal data to check for wrapping of the time stamp + */ + int64_t pts_wrap_reference; + + /** + * Options for behavior, when a wrap is detected. + * + * Defined by AV_PTS_WRAP_ values. + * + * If correction is enabled, there are two possibilities: + * If the first time stamp is near the wrap point, the wrap offset + * will be subtracted, which will create negative time stamps. + * Otherwise the offset will be added. + */ + int pts_wrap_behavior; + } AVStream; +AVRational av_stream_get_r_frame_rate(const AVStream *s); +void av_stream_set_r_frame_rate(AVStream *s, AVRational r); + #define AV_PROGRAM_RUNNING 1 /** @@ -784,6 +912,23 @@ typedef struct AVProgram { unsigned int *stream_index; unsigned int nb_stream_indexes; AVDictionary *metadata; + + int program_num; + int pmt_pid; + int pcr_pid; + + /***************************************************************** + * All fields below this line are not part of the public API. They + * may not be used outside of libavformat and can be changed and + * removed at will. + * New public fields should be added right above. + ***************************************************************** + */ + int64_t start_time; + int64_t end_time; + + int64_t pts_wrap_reference; ///< reference dts for wrap detection + int pts_wrap_behavior; ///< behavior on wrap detection } AVProgram; #define AVFMTCTX_NOHEADER 0x0001 /**< signal that no header is present @@ -796,6 +941,17 @@ typedef struct AVChapter { AVDictionary *metadata; } AVChapter; + +/** + * The duration of a video can be estimated through various ways, and this enum can be used + * to know how the duration was estimated. + */ +enum AVDurationEstimationMethod { + AVFMT_DURATION_FROM_PTS, ///< Duration accurately estimated from PTSes + AVFMT_DURATION_FROM_STREAM, ///< Duration estimated from a stream with a known duration + AVFMT_DURATION_FROM_BITRATE ///< Duration estimated from bitrate (less accurate) +}; + /** * Format I/O context. * New fields can be added to the end with minor version bumps. @@ -874,7 +1030,7 @@ typedef struct AVFormatContext { /** * Decoding: total stream bitrate in bit/s, 0 if not * available. Never set it directly if the file_size and the - * duration are known as Libav can compute it automatically. + * duration are known as FFmpeg can compute it automatically. */ int bit_rate; @@ -892,6 +1048,10 @@ typedef struct AVFormatContext { #define AVFMT_FLAG_CUSTOM_IO 0x0080 ///< The caller has supplied a custom AVIOContext, don't avio_close() it. #define AVFMT_FLAG_DISCARD_CORRUPT 0x0100 ///< Discard frames marked corrupted #define AVFMT_FLAG_FLUSH_PACKETS 0x0200 ///< Flush the AVIOContext every packet. +#define AVFMT_FLAG_MP4A_LATM 0x8000 ///< Enable RTP MP4A-LATM payload +#define AVFMT_FLAG_SORT_DTS 0x10000 ///< try to interleave outputted packets by dts (using this flag can slow demuxing down) +#define AVFMT_FLAG_PRIV_OPT 0x20000 ///< Enable use of private options by delaying codec open (this could be made default once all code is converted) +#define AVFMT_FLAG_KEEP_SIDE_DATA 0x40000 ///< Don't merge side data but keep it separate. /** * decoding: size of data to probe; encoding: unused. @@ -1000,6 +1160,108 @@ typedef struct AVFormatContext { */ int debug; #define FF_FDEBUG_TS 0x0001 + + /** + * Transport stream id. + * This will be moved into demuxer private options. Thus no API/ABI compatibility + */ + int ts_id; + + /** + * Audio preload in microseconds. + * Note, not all formats support this and unpredictable things may happen if it is used when not supported. + * - encoding: Set by user via AVOptions (NO direct access) + * - decoding: unused + */ + int audio_preload; + + /** + * Max chunk time in microseconds. + * Note, not all formats support this and unpredictable things may happen if it is used when not supported. + * - encoding: Set by user via AVOptions (NO direct access) + * - decoding: unused + */ + int max_chunk_duration; + + /** + * Max chunk size in bytes + * Note, not all formats support this and unpredictable things may happen if it is used when not supported. + * - encoding: Set by user via AVOptions (NO direct access) + * - decoding: unused + */ + int max_chunk_size; + + /** + * forces the use of wallclock timestamps as pts/dts of packets + * This has undefined results in the presence of B frames. + * - encoding: unused + * - decoding: Set by user via AVOptions (NO direct access) + */ + int use_wallclock_as_timestamps; + + /** + * Avoid negative timestamps during muxing. + * 0 -> allow negative timestamps + * 1 -> avoid negative timestamps + * -1 -> choose automatically (default) + * Note, this only works when interleave_packet_per_dts is in use. + * - encoding: Set by user via AVOptions (NO direct access) + * - decoding: unused + */ + int avoid_negative_ts; + + /** + * avio flags, used to force AVIO_FLAG_DIRECT. + * - encoding: unused + * - decoding: Set by user via AVOptions (NO direct access) + */ + int avio_flags; + + /** + * The duration field can be estimated through various ways, and this field can be used + * to know how the duration was estimated. + * - encoding: unused + * - decoding: Read by user via AVOptions (NO direct access) + */ + enum AVDurationEstimationMethod duration_estimation_method; + + /** + * Skip initial bytes when opening stream + * - encoding: unused + * - decoding: Set by user via AVOptions (NO direct access) + */ + unsigned int skip_initial_bytes; + + /** + * Correct single timestamp overflows + * - encoding: unused + * - decoding: Set by user via AVOPtions (NO direct access) + */ + unsigned int correct_ts_overflow; + + /** + * Force seeking to any (also non key) frames. + * - encoding: unused + * - decoding: Set by user via AVOPtions (NO direct access) + */ + int seek2any; + + /** + * Flush the I/O context after each packet. + * - encoding: Set by user via AVOptions (NO direct access) + * - decoding: unused + */ + int flush_packets; + + /** + * format probing score. + * The maximal score is AVPROBE_SCORE_MAX, its set when the demuxer probes + * the format. + * - encoding: unused + * - decoding: set by avformat, read by user via av_format_get_probe_score() (NO direct access) + */ + int probe_score; + /***************************************************************** * All fields below this line are not part of the public API. They * may not be used outside of libavformat and can be changed and @@ -1041,6 +1303,7 @@ typedef struct AVFormatContext { /** * Offset to remap timestamps to be non-negative. * Expressed in timebase units. + * @see AVStream.mux_ts_offset */ int64_t offset; @@ -1049,8 +1312,54 @@ typedef struct AVFormatContext { */ AVRational offset_timebase; + /** + * IO repositioned flag. + * This is set by avformat when the underlaying IO context read pointer + * is repositioned, for example when doing byte based seeking. + * Demuxers can use the flag to detect such changes. + */ + int io_repositioned; + + /** + * Forced video codec. + * This allows forcing a specific decoder, even when there are multiple with + * the same codec_id. + * Demuxing: Set by user via av_format_set_video_codec (NO direct access). + */ + AVCodec *video_codec; + + /** + * Forced audio codec. + * This allows forcing a specific decoder, even when there are multiple with + * the same codec_id. + * Demuxing: Set by user via av_format_set_audio_codec (NO direct access). + */ + AVCodec *audio_codec; + + /** + * Forced subtitle codec. + * This allows forcing a specific decoder, even when there are multiple with + * the same codec_id. + * Demuxing: Set by user via av_format_set_subtitle_codec (NO direct access). + */ + AVCodec *subtitle_codec; } AVFormatContext; +int av_format_get_probe_score(const AVFormatContext *s); +AVCodec * av_format_get_video_codec(const AVFormatContext *s); +void av_format_set_video_codec(AVFormatContext *s, AVCodec *c); +AVCodec * av_format_get_audio_codec(const AVFormatContext *s); +void av_format_set_audio_codec(AVFormatContext *s, AVCodec *c); +AVCodec * av_format_get_subtitle_codec(const AVFormatContext *s); +void av_format_set_subtitle_codec(AVFormatContext *s, AVCodec *c); + +/** + * Returns the method used to set ctx->duration. + * + * @return AVFMT_DURATION_FROM_PTS, AVFMT_DURATION_FROM_STREAM, or AVFMT_DURATION_FROM_BITRATE. + */ +enum AVDurationEstimationMethod av_fmt_ctx_get_duration_estimation_method(const AVFormatContext* ctx); + typedef struct AVPacketList { AVPacket pkt; struct AVPacketList *next; @@ -1088,7 +1397,6 @@ const char *avformat_license(void); * * @see av_register_input_format() * @see av_register_output_format() - * @see av_register_protocol() */ void av_register_all(void); @@ -1154,13 +1462,16 @@ const AVClass *avformat_get_class(void); * * When muxing, should be called by the user before avformat_write_header(). * + * User is required to call avcodec_close() and avformat_free_context() to + * clean up the allocation by avformat_new_stream(). + * * @param c If non-NULL, the AVCodecContext corresponding to the new stream * will be initialized to use this codec. This is needed for e.g. codec-specific * defaults to be set, so codec should be provided if it is known. * * @return newly created stream or NULL on error. */ -AVStream *avformat_new_stream(AVFormatContext *s, AVCodec *c); +AVStream *avformat_new_stream(AVFormatContext *s, const AVCodec *c); AVProgram *av_new_program(AVFormatContext *s, int id); @@ -1169,6 +1480,35 @@ AVProgram *av_new_program(AVFormatContext *s, int id); */ +#if FF_API_ALLOC_OUTPUT_CONTEXT +/** + * @deprecated deprecated in favor of avformat_alloc_output_context2() + */ +attribute_deprecated +AVFormatContext *avformat_alloc_output_context(const char *format, + AVOutputFormat *oformat, + const char *filename); +#endif + +/** + * Allocate an AVFormatContext for an output format. + * avformat_free_context() can be used to free the context and + * everything allocated by the framework within it. + * + * @param *ctx is set to the created format context, or to NULL in + * case of failure + * @param oformat format to use for allocating the context, if NULL + * format_name and filename are used instead + * @param format_name the name of output format to use for allocating the + * context, if NULL filename is used instead + * @param filename the name of the filename to use for allocating the + * context, may be NULL + * @return >= 0 in case of success, a negative AVERROR code in case of + * failure + */ +int avformat_alloc_output_context2(AVFormatContext **ctx, AVOutputFormat *oformat, + const char *format_name, const char *filename); + /** * @addtogroup lavf_decoding * @{ @@ -1201,6 +1541,15 @@ AVInputFormat *av_probe_input_format(AVProbeData *pd, int is_opened); AVInputFormat *av_probe_input_format2(AVProbeData *pd, int is_opened, int *score_max); /** + * Guess the file format. + * + * @param is_opened Whether the file is already opened; determines whether + * demuxers with or without AVFMT_NOFILE are probed. + * @param score_ret The score of the best detection. + */ +AVInputFormat *av_probe_input_format3(AVProbeData *pd, int is_opened, int *score_ret); + +/** * Probe a bytestream to determine the input format. Each time a probe returns * with a score that is too low, the probe buffer size is increased and another * attempt is made. When the maximum probe size is reached, the input format @@ -1212,9 +1561,17 @@ AVInputFormat *av_probe_input_format2(AVProbeData *pd, int is_opened, int *score * @param logctx the log context * @param offset the offset within the bytestream to probe from * @param max_probe_size the maximum probe buffer size (zero for default) - * @return 0 in case of success, a negative value corresponding to an + * @return the score in case of success, a negative value corresponding to an + * the maximal score is AVPROBE_SCORE_MAX * AVERROR code otherwise */ +int av_probe_input_buffer2(AVIOContext *pb, AVInputFormat **fmt, + const char *filename, void *logctx, + unsigned int offset, unsigned int max_probe_size); + +/** + * Like av_probe_input_buffer2() but returns 0 on success + */ int av_probe_input_buffer(AVIOContext *pb, AVInputFormat **fmt, const char *filename, void *logctx, unsigned int offset, unsigned int max_probe_size); @@ -1240,6 +1597,29 @@ int av_probe_input_buffer(AVIOContext *pb, AVInputFormat **fmt, */ int avformat_open_input(AVFormatContext **ps, const char *filename, AVInputFormat *fmt, AVDictionary **options); +attribute_deprecated +int av_demuxer_open(AVFormatContext *ic); + +#if FF_API_FORMAT_PARAMETERS +/** + * Read packets of a media file to get stream information. This + * is useful for file formats with no headers such as MPEG. This + * function also computes the real framerate in case of MPEG-2 repeat + * frame mode. + * The logical file position is not changed by this function; + * examined packets may be buffered for later processing. + * + * @param ic media file handle + * @return >=0 if OK, AVERROR_xxx on error + * @todo Let the user decide somehow what information is needed so that + * we do not waste time getting stuff the user does not need. + * + * @deprecated use avformat_find_stream_info. + */ +attribute_deprecated +int av_find_stream_info(AVFormatContext *ic); +#endif + /** * Read packets of a media file to get stream information. This * is useful for file formats with no headers such as MPEG. This @@ -1264,6 +1644,18 @@ int avformat_open_input(AVFormatContext **ps, const char *filename, AVInputForma int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options); /** + * Find the programs which belong to a given stream. + * + * @param ic media file handle + * @param last the last found program, the search will start after this + * program, or from the beginning if it is NULL + * @param s stream index + * @return the next program which belongs to s, NULL if no program is found or + * the last program is not among the programs of ic. + */ +AVProgram *av_find_program_from_stream(AVFormatContext *ic, AVProgram *last, int s); + +/** * Find the "best" stream in the file. * The best stream is determined according to various heuristics as the most * likely to be what the user expects. @@ -1294,6 +1686,24 @@ int av_find_best_stream(AVFormatContext *ic, AVCodec **decoder_ret, int flags); +#if FF_API_READ_PACKET +/** + * @deprecated use AVFMT_FLAG_NOFILLIN | AVFMT_FLAG_NOPARSE to read raw + * unprocessed packets + * + * Read a transport packet from a media file. + * + * This function is obsolete and should never be used. + * Use av_read_frame() instead. + * + * @param s media file handle + * @param pkt is filled + * @return 0 if OK, AVERROR_xxx on error + */ +attribute_deprecated +int av_read_packet(AVFormatContext *s, AVPacket *pkt); +#endif + /** * Return the next frame of a stream. * This function returns what is stored in the file, and does not validate @@ -1348,6 +1758,7 @@ int av_seek_frame(AVFormatContext *s, int stream_index, int64_t timestamp, * or if stream_index is -1, in AV_TIME_BASE units. * If flags contain AVSEEK_FLAG_ANY, then non-keyframes are treated as * keyframes (this may not be supported by all demuxers). + * If flags contain AVSEEK_FLAG_BACKWARD, it is ignored. * * @param stream_index index of the stream which is used as time base reference * @param min_ts smallest acceptable timestamp @@ -1375,6 +1786,17 @@ int av_read_play(AVFormatContext *s); */ int av_read_pause(AVFormatContext *s); +#if FF_API_CLOSE_INPUT_FILE +/** + * @deprecated use avformat_close_input() + * Close a media file (but not its codecs). + * + * @param s media file handle + */ +attribute_deprecated +void av_close_input_file(AVFormatContext *s); +#endif + /** * Close an opened input AVFormatContext. Free it and all its contents * and set *s to NULL. @@ -1384,6 +1806,30 @@ void avformat_close_input(AVFormatContext **s); * @} */ +#if FF_API_NEW_STREAM +/** + * Add a new stream to a media file. + * + * Can only be called in the read_header() function. If the flag + * AVFMTCTX_NOHEADER is in the format context, then new streams + * can be added in read_packet too. + * + * @param s media file handle + * @param id file-format-dependent stream ID + */ +attribute_deprecated +AVStream *av_new_stream(AVFormatContext *s, int id); +#endif + +#if FF_API_SET_PTS_INFO +/** + * @deprecated this function is not supposed to be called outside of lavf + */ +attribute_deprecated +void av_set_pts_info(AVStream *s, int pts_wrap_bits, + unsigned int pts_num, unsigned int pts_den); +#endif + #define AVSEEK_FLAG_BACKWARD 1 ///< seek backward #define AVSEEK_FLAG_BYTE 2 ///< seeking based on position in bytes #define AVSEEK_FLAG_ANY 4 ///< seek to any frame, even non-keyframes @@ -1491,6 +1937,25 @@ enum AVCodecID av_guess_codec(AVOutputFormat *fmt, const char *short_name, enum AVMediaType type); /** + * Get timing information for the data currently output. + * The exact meaning of "currently output" depends on the format. + * It is mostly relevant for devices that have an internal buffer and/or + * work in real time. + * @param s media file handle + * @param stream stream in the media file + * @param[out] dts DTS of the last packet output for the stream, in stream + * time_base units + * @param[out] wall absolute time when that packet whas output, + * in microsecond + * @return 0 if OK, AVERROR(ENOSYS) if the format does not support it + * Note: some formats or devices may not allow to measure dts and wall + * atomically. + */ +int av_get_output_timestamp(struct AVFormatContext *s, int stream, + int64_t *dts, int64_t *wall); + + +/** * @} */ @@ -1572,6 +2037,18 @@ enum AVCodecID av_codec_get_id(const struct AVCodecTag * const *tags, unsigned i */ unsigned int av_codec_get_tag(const struct AVCodecTag * const *tags, enum AVCodecID id); +/** + * Get the codec tag for the given codec id. + * + * @param tags list of supported codec_id - codec_tag pairs, as stored + * in AVInputFormat.codec_tag and AVOutputFormat.codec_tag + * @param id codec id that should be searched for in the list + * @param tag A pointer to the found tag + * @return 0 if id was not found in tags, > 0 if it was found + */ +int av_codec_get_tag2(const struct AVCodecTag * const *tags, enum AVCodecID id, + unsigned int *tag); + int av_find_default_stream_index(AVFormatContext *s); /** @@ -1706,11 +2183,60 @@ const struct AVCodecTag *avformat_get_riff_video_tags(void); * @return the table mapping RIFF FourCCs for audio to AVCodecID. */ const struct AVCodecTag *avformat_get_riff_audio_tags(void); + /** * @} */ /** + * Guess the sample aspect ratio of a frame, based on both the stream and the + * frame aspect ratio. + * + * Since the frame aspect ratio is set by the codec but the stream aspect ratio + * is set by the demuxer, these two may not be equal. This function tries to + * return the value that you should use if you would like to display the frame. + * + * Basic logic is to use the stream aspect ratio if it is set to something sane + * otherwise use the frame aspect ratio. This way a container setting, which is + * usually easy to modify can override the coded value in the frames. + * + * @param format the format context which the stream is part of + * @param stream the stream which the frame is part of + * @param frame the frame with the aspect ratio to be determined + * @return the guessed (valid) sample_aspect_ratio, 0/1 if no idea + */ +AVRational av_guess_sample_aspect_ratio(AVFormatContext *format, AVStream *stream, AVFrame *frame); + +/** + * Guess the frame rate, based on both the container and codec information. + * + * @param ctx the format context which the stream is part of + * @param stream the stream which the frame is part of + * @param frame the frame for which the frame rate should be determined, may be NULL + * @return the guessed (valid) frame rate, 0/1 if no idea + */ +AVRational av_guess_frame_rate(AVFormatContext *ctx, AVStream *stream, AVFrame *frame); + +/** + * Check if the stream st contained in s is matched by the stream specifier + * spec. + * + * See the "stream specifiers" chapter in the documentation for the syntax + * of spec. + * + * @return >0 if st is matched by spec; + * 0 if st is not matched by spec; + * AVERROR code if spec is invalid + * + * @note A stream specifier can match several streams in the format. + */ +int avformat_match_stream_specifier(AVFormatContext *s, AVStream *st, + const char *spec); + +int avformat_queue_attached_pictures(AVFormatContext *s); + + +/** * @} */ diff --git a/libavformat/avi.h b/libavformat/avi.h index e05db9c63f..34da76f715 100644 --- a/libavformat/avi.h +++ b/libavformat/avi.h @@ -1,20 +1,20 @@ /* * copyright (c) 2001 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/avidec.c b/libavformat/avidec.c index edc9c93c81..1a40e94c82 100644 --- a/libavformat/avidec.c +++ b/libavformat/avidec.c @@ -2,25 +2,27 @@ * AVI demuxer * Copyright (c) 2001 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "libavutil/avassert.h" #include "libavutil/avstring.h" #include "libavutil/bswap.h" +#include "libavutil/opt.h" #include "libavutil/dict.h" #include "libavutil/internal.h" #include "libavutil/intreadwrite.h" @@ -31,9 +33,6 @@ #include "internal.h" #include "riff.h" -#undef NDEBUG -#include <assert.h> - typedef struct AVIStream { int64_t frame_offset; /* current frame (video) or byte (audio) counter * (used to compute the pts) */ @@ -56,12 +55,16 @@ typedef struct AVIStream { AVFormatContext *sub_ctx; AVPacket sub_pkt; uint8_t *sub_buffer; + + int64_t seek_pos; } AVIStream; typedef struct { + const AVClass *class; int64_t riff_end; int64_t movi_end; int64_t fsize; + int64_t io_fsize; int64_t movi_list; int64_t last_pkt_pos; int index_loaded; @@ -70,9 +73,26 @@ typedef struct { int stream_index; DVDemuxContext *dv_demux; int odml_depth; + int use_odml; #define MAX_ODML_DEPTH 1000 + int64_t dts_max; } AVIContext; + +static const AVOption options[] = { + { "use_odml", "use odml index", offsetof(AVIContext, use_odml), AV_OPT_TYPE_INT, {.i64 = 1}, -1, 1, AV_OPT_FLAG_DECODING_PARAM}, + { NULL }, +}; + +static const AVClass demuxer_class = { + .class_name = "avi", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, + .category = AV_CLASS_CATEGORY_DEMUXER, +}; + + static const char avi_headers[][8] = { { 'R', 'I', 'F', 'F', 'A', 'V', 'I', ' ' }, { 'R', 'I', 'F', 'F', 'A', 'V', 'I', 'X' }, @@ -149,7 +169,7 @@ static int read_braindead_odml_indx(AVFormatContext *s, int frame_num) AVIStream *ast; int i; int64_t last_pos = -1; - int64_t filesize = avio_size(s->pb); + int64_t filesize = avi->fsize; av_dlog(s, "longs_pre_entry:%d index_type:%d entries_in_use:%d " @@ -192,9 +212,10 @@ static int read_braindead_odml_indx(AVFormatContext *s, int frame_num) int key = len >= 0; len &= 0x7FFFFFFF; - av_dlog(s, "pos:%"PRId64", len:%X\n", pos, len); - - if (pb->eof_reached) +#ifdef DEBUG_SEEK + av_log(s, AV_LOG_ERROR, "pos:%"PRId64", len:%X\n", pos, len); +#endif + if (url_feof(pb)) return AVERROR_INVALIDDATA; if (last_pos == pos || pos == base - 8) @@ -212,7 +233,7 @@ static int read_braindead_odml_indx(AVFormatContext *s, int frame_num) avio_rl32(pb); /* size */ duration = avio_rl32(pb); - if (pb->eof_reached) + if (url_feof(pb)) return AVERROR_INVALIDDATA; pos = avio_tell(pb); @@ -222,16 +243,21 @@ static int read_braindead_odml_indx(AVFormatContext *s, int frame_num) return AVERROR_INVALIDDATA; } - avio_seek(pb, offset + 8, SEEK_SET); + if (avio_seek(pb, offset + 8, SEEK_SET) < 0) + return -1; avi->odml_depth++; read_braindead_odml_indx(s, frame_num); avi->odml_depth--; frame_num += duration; - avio_seek(pb, pos, SEEK_SET); + if (avio_seek(pb, pos, SEEK_SET) < 0) { + av_log(s, AV_LOG_ERROR, "Failed to restore position after reading index\n"); + return -1; + } + } } - avi->index_loaded = 1; + avi->index_loaded = 2; return 0; } @@ -364,6 +390,7 @@ static int avi_read_header(AVFormatContext *s) int amv_file_format = 0; uint64_t list_end = 0; int ret; + AVDictionaryEntry *dict_entry; avi->stream_index = -1; @@ -371,8 +398,10 @@ static int avi_read_header(AVFormatContext *s) if (ret < 0) return ret; - avi->fsize = avio_size(pb); - if (avi->fsize <= 0) + av_log(avi, AV_LOG_DEBUG, "use odml:%d\n", avi->use_odml); + + avi->io_fsize = avi->fsize = avio_size(pb); + if (avi->fsize <= 0 || avi->fsize < avi->riff_end) avi->fsize = avi->riff_end == 8 ? INT64_MAX : avi->riff_end; /* first list tag */ @@ -380,7 +409,7 @@ static int avi_read_header(AVFormatContext *s) codec_type = -1; frame_period = 0; for (;;) { - if (pb->eof_reached) + if (url_feof(pb)) goto fail; tag = avio_rl32(pb); size = avio_rl32(pb); @@ -400,7 +429,7 @@ static int avi_read_header(AVFormatContext *s) if (size) avi->movi_end = avi->movi_list + size + (size & 1); else - avi->movi_end = avio_size(pb); + avi->movi_end = avi->fsize; av_dlog(NULL, "movi end=%"PRIx64"\n", avi->movi_end); goto end_of_header; } else if (tag1 == MKTAG('I', 'N', 'F', 'O')) @@ -428,7 +457,7 @@ static int avi_read_header(AVFormatContext *s) /* AVI header */ /* using frame_period is bad idea */ frame_period = avio_rl32(pb); - avio_skip(pb, 4); + avio_rl32(pb); /* max. bytes per second */ avio_rl32(pb); avi->non_interleaved |= avio_rl32(pb) & AVIF_MUSTUSEINDEX; @@ -484,6 +513,9 @@ static int avi_read_header(AVFormatContext *s) ast = s->streams[0]->priv_data; av_freep(&s->streams[0]->codec->extradata); av_freep(&s->streams[0]->codec); + if (s->streams[0]->info) + av_freep(&s->streams[0]->info->duration_error); + av_freep(&s->streams[0]->info); av_freep(&s->streams[0]); s->nb_streams = 0; if (CONFIG_DV_DEMUXER) { @@ -511,7 +543,7 @@ static int avi_read_header(AVFormatContext *s) break; } - assert(stream_index < s->nb_streams); + av_assert0(stream_index < s->nb_streams); st->codec->stream_codec_tag = handler; avio_rl32(pb); /* flags */ @@ -542,6 +574,10 @@ static int avi_read_header(AVFormatContext *s) st->start_time = 0; avio_rl32(pb); /* buffer size */ avio_rl32(pb); /* quality */ + if (ast->cum_len*ast->scale/ast->rate > 3600) { + av_log(s, AV_LOG_ERROR, "crazy start time, iam scared, giving up\n"); + return AVERROR_INVALIDDATA; + } ast->sample_size = avio_rl32(pb); /* sample ssize */ ast->cum_len *= FFMAX(1, ast->sample_size); av_dlog(s, "%"PRIu32" %"PRIu32" %d\n", @@ -563,20 +599,27 @@ static int avi_read_header(AVFormatContext *s) codec_type = AVMEDIA_TYPE_DATA; break; default: - av_log(s, AV_LOG_ERROR, "unknown stream type %X\n", tag1); - goto fail; + av_log(s, AV_LOG_INFO, "unknown stream type %X\n", tag1); } - if (ast->sample_size == 0) + if (ast->sample_size == 0) { st->duration = st->nb_frames; + if (st->duration > 0 && avi->io_fsize > 0 && avi->riff_end > avi->io_fsize) { + av_log(s, AV_LOG_DEBUG, "File is truncated adjusting duration\n"); + st->duration = av_rescale(st->duration, avi->io_fsize, avi->riff_end); + } + } ast->frame_offset = ast->cum_len; avio_skip(pb, size - 12 * 4); break; case MKTAG('s', 't', 'r', 'f'): /* stream header */ + if (!size) + break; if (stream_index >= (unsigned)s->nb_streams || avi->dv_demux) { avio_skip(pb, size); } else { uint64_t cur_pos = avio_tell(pb); + unsigned esize; if (cur_pos < list_end) size = FFMIN(size, list_end - cur_pos); st = s->streams[stream_index]; @@ -590,7 +633,7 @@ static int avi_read_header(AVFormatContext *s) avio_skip(pb, size); break; } - tag1 = ff_get_bmp_header(pb, st); + tag1 = ff_get_bmp_header(pb, st, &esize); if (tag1 == MKTAG('D', 'X', 'S', 'B') || tag1 == MKTAG('D', 'X', 'S', 'A')) { @@ -600,14 +643,13 @@ static int avi_read_header(AVFormatContext *s) break; } - if (size > 10 * 4 && size < (1 << 30)) { - st->codec->extradata_size = size - 10 * 4; - st->codec->extradata = av_malloc(st->codec->extradata_size + - FF_INPUT_BUFFER_PADDING_SIZE); - if (!st->codec->extradata) { - st->codec->extradata_size = 0; + if (size > 10 * 4 && size < (1 << 30) && size < avi->fsize) { + if (esize == size-1 && (esize&1)) { + st->codec->extradata_size = esize - 10 * 4; + } else + st->codec->extradata_size = size - 10 * 4; + if (ff_alloc_extradata(st->codec, st->codec->extradata_size)) return AVERROR(ENOMEM); - } avio_read(pb, st->codec->extradata, st->codec->extradata_size); @@ -620,7 +662,7 @@ static int avi_read_header(AVFormatContext *s) /* Extract palette from extradata if bpp <= 8. * This code assumes that extradata contains only palette. * This is true for all paletted codecs implemented in - * Libav. */ + * FFmpeg. */ if (st->codec->extradata_size && (st->codec->bits_per_coded_sample <= 8)) { int pal_size = (1 << st->codec->bits_per_coded_sample) << 2; @@ -629,12 +671,8 @@ static int avi_read_header(AVFormatContext *s) pal_size = FFMIN(pal_size, st->codec->extradata_size); pal_src = st->codec->extradata + st->codec->extradata_size - pal_size; -#if HAVE_BIGENDIAN for (i = 0; i < pal_size / 4; i++) - ast->pal[i] = av_bswap32(((uint32_t *)pal_src)[i]); -#else - memcpy(ast->pal, pal_src, pal_size); -#endif + ast->pal[i] = 0xFFU<<24 | AV_RL32(pal_src+4*i); ast->has_pal = 1; } @@ -647,11 +685,6 @@ static int avi_read_header(AVFormatContext *s) /* This is needed to get the pict type which is necessary * for generating correct pts. */ st->need_parsing = AVSTREAM_PARSE_HEADERS; - // Support "Resolution 1:1" for Avid AVI Codec - if (tag1 == MKTAG('A', 'V', 'R', 'n') && - st->codec->extradata_size >= 31 && - !memcmp(&st->codec->extradata[28], "1:1", 3)) - st->codec->codec_id = AV_CODEC_ID_RAWVIDEO; if (st->codec->codec_tag == 0 && st->codec->height > 0 && st->codec->extradata_size < 1U << 30) { @@ -701,15 +734,27 @@ static int avi_read_header(AVFormatContext *s) if (st->codec->stream_codec_tag == AV_RL32("Axan")) { st->codec->codec_id = AV_CODEC_ID_XAN_DPCM; st->codec->codec_tag = 0; + ast->dshow_block_align = 0; } if (amv_file_format) { st->codec->codec_id = AV_CODEC_ID_ADPCM_IMA_AMV; ast->dshow_block_align = 0; } + if (st->codec->codec_id == AV_CODEC_ID_AAC && ast->dshow_block_align <= 4 && ast->dshow_block_align) { + av_log(s, AV_LOG_DEBUG, "overriding invalid dshow_block_align of %d\n", ast->dshow_block_align); + ast->dshow_block_align = 0; + } + if (st->codec->codec_id == AV_CODEC_ID_AAC && ast->dshow_block_align == 1024 && ast->sample_size == 1024 || + st->codec->codec_id == AV_CODEC_ID_AAC && ast->dshow_block_align == 4096 && ast->sample_size == 4096 || + st->codec->codec_id == AV_CODEC_ID_MP3 && ast->dshow_block_align == 1152 && ast->sample_size == 1152) { + av_log(s, AV_LOG_DEBUG, "overriding sample_size\n"); + ast->sample_size = 0; + } break; case AVMEDIA_TYPE_SUBTITLE: st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE; - st->codec->codec_id = AV_CODEC_ID_PROBE; + st->request_probe= 1; + avio_skip(pb, size); break; default: st->codec->codec_type = AVMEDIA_TYPE_DATA; @@ -720,9 +765,31 @@ static int avi_read_header(AVFormatContext *s) } } break; + case MKTAG('s', 't', 'r', 'd'): + if (stream_index >= (unsigned)s->nb_streams + || s->streams[stream_index]->codec->extradata_size + || s->streams[stream_index]->codec->codec_tag == MKTAG('H','2','6','4')) { + avio_skip(pb, size); + } else { + uint64_t cur_pos = avio_tell(pb); + if (cur_pos < list_end) + size = FFMIN(size, list_end - cur_pos); + st = s->streams[stream_index]; + + if (size<(1<<30)) { + if (ff_alloc_extradata(st->codec, size)) + return AVERROR(ENOMEM); + avio_read(pb, st->codec->extradata, st->codec->extradata_size); + } + + if (st->codec->extradata_size & 1) //FIXME check if the encoder really did this correctly + avio_r8(pb); + } + break; case MKTAG('i', 'n', 'd', 'x'): i = avio_tell(pb); if (pb->seekable && !(s->flags & AVFMT_FLAG_IGNIDX) && + avi->use_odml && read_braindead_odml_indx(s, 0) < 0 && (s->error_recognition & AV_EF_EXPLODE)) goto fail; @@ -771,7 +838,7 @@ static int avi_read_header(AVFormatContext *s) if (s->error_recognition & AV_EF_EXPLODE) goto fail; avi->movi_list = avio_tell(pb) - 4; - avi->movi_end = avio_size(pb); + avi->movi_end = avi->fsize; goto end_of_header; } /* skip tag */ @@ -791,13 +858,27 @@ fail: if (!avi->index_loaded && pb->seekable) avi_load_index(s); - avi->index_loaded = 1; - avi->non_interleaved |= guess_ni_flag(s); + avi->index_loaded |= 1; + avi->non_interleaved |= guess_ni_flag(s) | (s->flags & AVFMT_FLAG_SORT_DTS); + + dict_entry = av_dict_get(s->metadata, "ISFT", NULL, 0); + if (dict_entry && !strcmp(dict_entry->value, "PotEncoder")) + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + if ( st->codec->codec_id == AV_CODEC_ID_MPEG1VIDEO + || st->codec->codec_id == AV_CODEC_ID_MPEG2VIDEO) + st->need_parsing = AVSTREAM_PARSE_FULL; + } + for (i = 0; i < s->nb_streams; i++) { AVStream *st = s->streams[i]; if (st->nb_index_entries) break; } + // DV-in-AVI cannot be non-interleaved, if set this must be + // a mis-detection. + if (avi->dv_demux) + avi->non_interleaved = 0; if (i == s->nb_streams && avi->non_interleaved) { av_log(s, AV_LOG_WARNING, "Non-interleaved AVI without index, switching to interleaved\n"); @@ -901,7 +982,7 @@ static AVStream *get_subtitle_pkt(AVFormatContext *s, AVStream *next_st, return sub_st; } -static int get_stream_idx(int *d) +static int get_stream_idx(unsigned *d) { if (d[0] >= '0' && d[0] <= '9' && d[1] >= '0' && d[1] <= '9') { @@ -911,6 +992,10 @@ static int get_stream_idx(int *d) } } +/** + * + * @param exit_early set to 1 to just gather packet position without making the changes needed to actually read & return the packet + */ static int avi_sync(AVFormatContext *s, int exit_early) { AVIContext *avi = s->priv_data; @@ -922,7 +1007,7 @@ static int avi_sync(AVFormatContext *s, int exit_early) start_sync: memset(d, -1, sizeof(d)); - for (i = sync = avio_tell(pb); !pb->eof_reached; i++) { + for (i = sync = avio_tell(pb); !url_feof(pb); i++) { int j; for (j = 0; j < 7; j++) @@ -934,7 +1019,7 @@ start_sync: n = get_stream_idx(d + 2); av_dlog(s, "%X %X %X %X %X %X %X %X %"PRId64" %u %d\n", d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], i, size, n); - if (i + (uint64_t)size > avi->fsize || d[0] > 127) + if (i*(avi->io_fsize>0) + (uint64_t)size > avi->fsize || d[0] > 127) continue; // parse ix## @@ -971,16 +1056,22 @@ start_sync: st = s->streams[n]; ast = st->priv_data; + if (!ast) { + av_log(s, AV_LOG_WARNING, "Skiping foreign stream %d packet\n", n); + continue; + } + if (s->nb_streams >= 2) { AVStream *st1 = s->streams[1]; AVIStream *ast1 = st1->priv_data; // workaround for broken small-file-bug402.avi - if (d[2] == 'w' && d[3] == 'b' && n == 0 && - st->codec->codec_type == AVMEDIA_TYPE_VIDEO && - st1->codec->codec_type == AVMEDIA_TYPE_AUDIO && - ast->prefix == 'd' * 256 + 'c' && - (d[2] * 256 + d[3] == ast1->prefix || - !ast1->prefix_count)) { + if ( d[2] == 'w' && d[3] == 'b' + && n == 0 + && st ->codec->codec_type == AVMEDIA_TYPE_VIDEO + && st1->codec->codec_type == AVMEDIA_TYPE_AUDIO + && ast->prefix == 'd'*256+'c' + && (d[2]*256+d[3] == ast1->prefix || !ast1->prefix_count) + ) { n = 1; st = st1; ast = ast1; @@ -997,9 +1088,9 @@ start_sync: || st->discard >= AVDISCARD_ALL)) { if (!exit_early) { ast->frame_offset += get_duration(ast, size); + avio_skip(pb, size); + goto start_sync; } - avio_skip(pb, size); - goto start_sync; } if (d[2] == 'p' && d[3] == 'c' && size <= 4 * 256 + 4) { @@ -1010,7 +1101,7 @@ start_sync: // b + (g << 8) + (r << 16); for (; k <= last; k++) - ast->pal[k] = avio_rb32(pb) >> 8; + ast->pal[k] = 0xFFU<<24 | avio_rb32(pb)>>8; ast->has_pal = 1; goto start_sync; @@ -1045,6 +1136,8 @@ start_sync: } } + if (pb->error) + return pb->error; return AVERROR_EOF; } @@ -1101,10 +1194,7 @@ static int avi_read_packet(AVFormatContext *s, AVPacket *pkt) return AVERROR_EOF; best_ast = best_st->priv_data; - best_ts = av_rescale_q(best_ts, - (AVRational) { FFMAX(1, best_ast->sample_size), - AV_TIME_BASE }, - best_st->time_base); + best_ts = best_ast->frame_offset; if (best_ast->remaining) { i = av_index_search_timestamp(best_st, best_ts, @@ -1119,15 +1209,18 @@ static int avi_read_packet(AVFormatContext *s, AVPacket *pkt) if (i >= 0) { int64_t pos = best_st->index_entries[i].pos; pos += best_ast->packet_size - best_ast->remaining; - avio_seek(s->pb, pos + 8, SEEK_SET); + if (avio_seek(s->pb, pos + 8, SEEK_SET) < 0) + return AVERROR_EOF; - assert(best_ast->remaining <= best_ast->packet_size); + av_assert0(best_ast->remaining <= best_ast->packet_size); avi->stream_index = best_stream_index; if (!best_ast->remaining) best_ast->packet_size = best_ast->remaining = best_st->index_entries[i].size; } + else + return AVERROR_EOF; } resync: @@ -1154,8 +1247,9 @@ resync: err = av_get_packet(pb, pkt, size); if (err < 0) return err; + size = err; - if (ast->has_pal && pkt->data && pkt->size < (unsigned)INT_MAX / 2) { + if (ast->has_pal && pkt->size < (unsigned)INT_MAX / 2) { uint8_t *pal; pal = av_packet_new_side_data(pkt, AV_PKT_DATA_PALETTE, @@ -1177,7 +1271,7 @@ FF_DISABLE_DEPRECATION_WARNINGS FF_ENABLE_DEPRECATION_WARNINGS #endif size = avpriv_dv_produce_packet(avi->dv_demux, pkt, - pkt->data, pkt->size); + pkt->data, pkt->size, pkt->pos); #if FF_API_DESTRUCT_PACKET FF_DISABLE_DEPRECATION_WARNINGS pkt->destruct = dstr; @@ -1215,14 +1309,32 @@ FF_ENABLE_DEPRECATION_WARNINGS if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) { AVIndexEntry *e; int index; - assert(st->index_entries); + av_assert0(st->index_entries); index = av_index_search_timestamp(st, ast->frame_offset, 0); e = &st->index_entries[index]; - if (index >= 0 && e->timestamp == ast->frame_offset) + if (index >= 0 && e->timestamp == ast->frame_offset) { + if (index == st->nb_index_entries-1) { + int key=1; + int i; + uint32_t state=-1; + for (i=0; i<FFMIN(size,256); i++) { + if (st->codec->codec_id == AV_CODEC_ID_MPEG4) { + if (state == 0x1B6) { + key= !(pkt->data[i]&0xC0); + break; + } + }else + break; + state= (state<<8) + pkt->data[i]; + } + if (!key) + e->flags &= ~AVINDEX_KEYFRAME; + } if (e->flags & AVINDEX_KEYFRAME) pkt->flags |= AV_PKT_FLAG_KEY; + } } else { pkt->flags |= AV_PKT_FLAG_KEY; } @@ -1234,6 +1346,22 @@ FF_ENABLE_DEPRECATION_WARNINGS ast->packet_size = 0; } + if (!avi->non_interleaved && pkt->pos >= 0 && ast->seek_pos > pkt->pos) { + av_free_packet(pkt); + goto resync; + } + ast->seek_pos= 0; + + if (!avi->non_interleaved && st->nb_index_entries>1 && avi->index_loaded>1) { + int64_t dts= av_rescale_q(pkt->dts, st->time_base, AV_TIME_BASE_Q); + + if (avi->dts_max - dts > 2*AV_TIME_BASE) { + avi->non_interleaved= 1; + av_log(s, AV_LOG_INFO, "Switching to NI mode, due to poor interleaving\n"); + }else if (avi->dts_max < dts) + avi->dts_max = dts; + } + return 0; } @@ -1253,7 +1381,9 @@ static int avi_read_idx1(AVFormatContext *s, int size) AVIStream *ast; unsigned int index, tag, flags, pos, len, first_packet = 1; unsigned last_pos = -1; + unsigned last_idx = -1; int64_t idx1_pos, first_packet_pos = 0, data_offset = 0; + int anykey = 0; nb_index_entries = size / 16; if (nb_index_entries <= 0) @@ -1266,8 +1396,16 @@ static int avi_read_idx1(AVFormatContext *s, int size) avi->stream_index = -1; avio_seek(pb, idx1_pos, SEEK_SET); + if (s->nb_streams == 1 && s->streams[0]->codec->codec_tag == AV_RL32("MMES")) { + first_packet_pos = 0; + data_offset = avi->movi_list; + } + /* Read the entries and sort them in each stream component. */ for (i = 0; i < nb_index_entries; i++) { + if (url_feof(pb)) + return -1; + tag = avio_rl32(pb); flags = avio_rl32(pb); pos = avio_rl32(pb); @@ -1282,7 +1420,7 @@ static int avi_read_idx1(AVFormatContext *s, int size) st = s->streams[index]; ast = st->priv_data; - if (first_packet && first_packet_pos && len) { + if (first_packet && first_packet_pos) { data_offset = first_packet_pos - pos; first_packet = 0; } @@ -1290,16 +1428,25 @@ static int avi_read_idx1(AVFormatContext *s, int size) av_dlog(s, "%d cum_len=%"PRId64"\n", len, ast->cum_len); - if (pb->eof_reached) - return AVERROR_INVALIDDATA; - + // even if we have only a single stream, we should + // switch to non-interleaved to get correct timestamps if (last_pos == pos) avi->non_interleaved = 1; - else if (len || !ast->sample_size) + if (last_idx != pos && len) { av_add_index_entry(st, pos, ast->cum_len, len, 0, (flags & AVIIF_INDEX) ? AVINDEX_KEYFRAME : 0); + last_idx= pos; + } ast->cum_len += get_duration(ast, len); last_pos = pos; + anykey |= flags&AVIIF_INDEX; + } + if (!anykey) { + for (index = 0; index < s->nb_streams; index++) { + st = s->streams[index]; + if (st->nb_index_entries) + st->index_entries[0].flags |= AVINDEX_KEYFRAME; + } } return 0; } @@ -1310,6 +1457,8 @@ static int guess_ni_flag(AVFormatContext *s) int64_t last_start = 0; int64_t first_end = INT64_MAX; int64_t oldpos = avio_tell(s->pb); + int *idx; + int64_t min_pos, pos; for (i = 0; i < s->nb_streams; i++) { AVStream *st = s->streams[i]; @@ -1333,7 +1482,35 @@ static int guess_ni_flag(AVFormatContext *s) first_end = st->index_entries[n - 1].pos; } avio_seek(s->pb, oldpos, SEEK_SET); - return last_start > first_end; + if (last_start > first_end) + return 1; + idx= av_calloc(s->nb_streams, sizeof(*idx)); + if (!idx) + return 0; + for (min_pos=pos=0; min_pos!=INT64_MAX; pos= min_pos+1LU) { + int64_t max_dts = INT64_MIN/2, min_dts= INT64_MAX/2; + min_pos = INT64_MAX; + + for (i=0; i<s->nb_streams; i++) { + AVStream *st = s->streams[i]; + AVIStream *ast = st->priv_data; + int n= st->nb_index_entries; + while (idx[i]<n && st->index_entries[idx[i]].pos < pos) + idx[i]++; + if (idx[i] < n) { + min_dts = FFMIN(min_dts, av_rescale_q(st->index_entries[idx[i]].timestamp/FFMAX(ast->sample_size, 1), st->time_base, AV_TIME_BASE_Q)); + min_pos = FFMIN(min_pos, st->index_entries[idx[i]].pos); + } + if (idx[i]) + max_dts = FFMAX(max_dts, av_rescale_q(st->index_entries[idx[i]-1].timestamp/FFMAX(ast->sample_size, 1), st->time_base, AV_TIME_BASE_Q)); + } + if (max_dts - min_dts > 2*AV_TIME_BASE) { + av_free(idx); + return 1; + } + } + av_free(idx); + return 0; } static int avi_load_index(AVFormatContext *s) @@ -1342,16 +1519,19 @@ static int avi_load_index(AVFormatContext *s) AVIOContext *pb = s->pb; uint32_t tag, size; int64_t pos = avio_tell(pb); + int64_t next; int ret = -1; if (avio_seek(pb, avi->movi_end, SEEK_SET) < 0) goto the_end; // maybe truncated file av_dlog(s, "movi_end=0x%"PRIx64"\n", avi->movi_end); for (;;) { - if (pb->eof_reached) - break; tag = avio_rl32(pb); size = avio_rl32(pb); + if (url_feof(pb)) + break; + next = avio_tell(pb) + size + (size & 1); + av_dlog(s, "tag=%c%c%c%c size=0x%x\n", tag & 0xff, (tag >> 8) & 0xff, @@ -1361,12 +1541,17 @@ static int avi_load_index(AVFormatContext *s) if (tag == MKTAG('i', 'd', 'x', '1') && avi_read_idx1(s, size) >= 0) { + avi->index_loaded=2; ret = 0; + }else if (tag == MKTAG('L', 'I', 'S', 'T')) { + uint32_t tag1 = avio_rl32(pb); + + if (tag1 == MKTAG('I', 'N', 'F', 'O')) + ff_read_riff_info(s, size - 4); + }else if (!ret) break; - } - size += (size & 1); - if (avio_skip(pb, size) < 0) + if (avio_seek(pb, next, SEEK_SET) < 0) break; // something is wrong here } @@ -1391,7 +1576,7 @@ static int avi_read_seek(AVFormatContext *s, int stream_index, AVIContext *avi = s->priv_data; AVStream *st; int i, index; - int64_t pos; + int64_t pos, pos_min; AVIStream *ast; /* Does not matter which stream is requested dv in avi has the @@ -1403,16 +1588,23 @@ static int avi_read_seek(AVFormatContext *s, int stream_index, if (!avi->index_loaded) { /* we only load the index on demand */ avi_load_index(s); - avi->index_loaded = 1; + avi->index_loaded |= 1; } + av_assert0(stream_index >= 0); st = s->streams[stream_index]; ast = st->priv_data; index = av_index_search_timestamp(st, timestamp * FFMAX(ast->sample_size, 1), flags); - if (index < 0) + if (index < 0) { + if (st->nb_index_entries > 0) + av_log(s, AV_LOG_DEBUG, "Failed to find timestamp %"PRId64 " in index %"PRId64 " .. %"PRId64 "\n", + timestamp * FFMAX(ast->sample_size, 1), + st->index_entries[0].timestamp, + st->index_entries[st->nb_index_entries - 1].timestamp); return AVERROR_INVALIDDATA; + } /* find the position */ pos = st->index_entries[index].pos; @@ -1426,15 +1618,18 @@ static int avi_read_seek(AVFormatContext *s, int stream_index, /* offsets. Calling with other stream indexes should have failed */ /* the av_index_search_timestamp call above. */ + if (avio_seek(s->pb, pos, SEEK_SET) < 0) + return -1; + /* Feed the DV video stream version of the timestamp to the */ /* DV demux so it can synthesize correct timestamps. */ ff_dv_offset_reset(avi->dv_demux, timestamp); - avio_seek(s->pb, pos, SEEK_SET); avi->stream_index = -1; return 0; } + pos_min = pos; for (i = 0; i < s->nb_streams; i++) { AVStream *st2 = s->streams[i]; AVIStream *ast2 = st2->priv_data; @@ -1450,35 +1645,47 @@ static int avi_read_seek(AVFormatContext *s, int stream_index, if (st2->nb_index_entries <= 0) continue; -// assert(st2->codec->block_align); - assert((int64_t)st2->time_base.num * ast2->rate == - (int64_t)st2->time_base.den * ast2->scale); +// av_assert1(st2->codec->block_align); + av_assert0((int64_t)st2->time_base.num * ast2->rate == + (int64_t)st2->time_base.den * ast2->scale); index = av_index_search_timestamp(st2, av_rescale_q(timestamp, st->time_base, st2->time_base) * FFMAX(ast2->sample_size, 1), - flags | AVSEEK_FLAG_BACKWARD); + flags | + AVSEEK_FLAG_BACKWARD | + (st2->codec->codec_type != AVMEDIA_TYPE_VIDEO ? AVSEEK_FLAG_ANY : 0)); if (index < 0) index = 0; + ast2->seek_pos = st2->index_entries[index].pos; + pos_min = FFMIN(pos_min,ast2->seek_pos); + } + for (i = 0; i < s->nb_streams; i++) { + AVStream *st2 = s->streams[i]; + AVIStream *ast2 = st2->priv_data; - if (!avi->non_interleaved) { - while (index > 0 && st2->index_entries[index].pos > pos) - index--; - while (index + 1 < st2->nb_index_entries && - st2->index_entries[index].pos < pos) - index++; - } + if (ast2->sub_ctx || st2->nb_index_entries <= 0) + continue; - av_dlog(s, "%"PRId64" %d %"PRId64"\n", - timestamp, index, st2->index_entries[index].timestamp); - /* extract the current frame number */ + index = av_index_search_timestamp( + st2, + av_rescale_q(timestamp, st->time_base, st2->time_base) * FFMAX(ast2->sample_size, 1), + flags | AVSEEK_FLAG_BACKWARD | (st2->codec->codec_type != AVMEDIA_TYPE_VIDEO ? AVSEEK_FLAG_ANY : 0)); + if (index < 0) + index = 0; + while (!avi->non_interleaved && index>0 && st2->index_entries[index-1].pos >= pos_min) + index--; ast2->frame_offset = st2->index_entries[index].timestamp; } /* do the seek */ - avio_seek(s->pb, pos, SEEK_SET); + if (avio_seek(s->pb, pos_min, SEEK_SET) < 0) { + av_log(s, AV_LOG_ERROR, "Seek failed\n"); + return -1; + } avi->stream_index = -1; + avi->dts_max = INT_MIN; return 0; } @@ -1527,4 +1734,5 @@ AVInputFormat ff_avi_demuxer = { .read_packet = avi_read_packet, .read_close = avi_read_close, .read_seek = avi_read_seek, + .priv_class = &demuxer_class, }; diff --git a/libavformat/avienc.c b/libavformat/avienc.c index 66339af2b0..3401cbc237 100644 --- a/libavformat/avienc.c +++ b/libavformat/avienc.c @@ -2,22 +2,25 @@ * AVI muxer * Copyright (c) 2000 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +//#define DEBUG + #include "avformat.h" #include "internal.h" #include "avi.h" @@ -25,6 +28,8 @@ #include "riff.h" #include "libavutil/intreadwrite.h" #include "libavutil/dict.h" +#include "libavutil/avassert.h" +#include "libavutil/timestamp.h" /* * TODO: @@ -52,7 +57,7 @@ typedef struct { typedef struct { int64_t frames_hdr_strm; - int audio_strm_length; + int64_t audio_strm_length; int packet_count; int entry; @@ -117,7 +122,7 @@ static int avi_write_counters(AVFormatContext* s, int riff_id) for(n = 0; n < s->nb_streams; n++) { AVIStream *avist= s->streams[n]->priv_data; - assert(avist->frames_hdr_strm); + av_assert0(avist->frames_hdr_strm); stream = s->streams[n]->codec; avio_seek(pb, avist->frames_hdr_strm, SEEK_SET); ff_parse_specific_params(stream, &au_byterate, &au_ssize, &au_scale); @@ -130,7 +135,7 @@ static int avi_write_counters(AVFormatContext* s, int riff_id) nb_frames = FFMAX(nb_frames, avist->packet_count); } if(riff_id == 1) { - assert(avi->frames_hdr_all); + av_assert0(avi->frames_hdr_all); avio_seek(pb, avi->frames_hdr_all, SEEK_SET); avio_wl32(pb, nb_frames); } @@ -151,7 +156,7 @@ static int avi_write_header(AVFormatContext *s) if (s->nb_streams > AVI_MAX_STREAM_COUNT) { av_log(s, AV_LOG_ERROR, "AVI does not support >%d streams\n", AVI_MAX_STREAM_COUNT); - return -1; + return AVERROR(EINVAL); } for(n=0;n<s->nb_streams;n++) { @@ -242,9 +247,18 @@ static int avi_write_header(AVFormatContext *s) ff_parse_specific_params(stream, &au_byterate, &au_ssize, &au_scale); + if ( stream->codec_type == AVMEDIA_TYPE_VIDEO + && stream->codec_id != AV_CODEC_ID_XSUB + && au_byterate > 1000LL*au_scale) { + au_byterate = 600; + au_scale = 1; + } + avpriv_set_pts_info(s->streams[i], 64, au_scale, au_byterate); + if(stream->codec_id == AV_CODEC_ID_XSUB) + au_scale = au_byterate = 0; + avio_wl32(pb, au_scale); /* scale */ avio_wl32(pb, au_byterate); /* rate */ - avpriv_set_pts_info(s->streams[i], 64, au_scale, au_byterate); avio_wl32(pb, 0); /* start */ avist->frames_hdr_strm = avio_tell(pb); /* remember this offset to fill later */ @@ -268,6 +282,8 @@ static int avi_write_header(AVFormatContext *s) ff_end_tag(pb, strh); if(stream->codec_type != AVMEDIA_TYPE_DATA){ + int ret; + strf = ff_start_tag(pb, "strf"); switch(stream->codec_type) { case AVMEDIA_TYPE_SUBTITLE: @@ -278,12 +294,15 @@ static int avi_write_header(AVFormatContext *s) ff_put_bmp_header(pb, stream, ff_codec_bmp_tags, 0); break; case AVMEDIA_TYPE_AUDIO: - if (ff_put_wav_header(pb, stream) < 0) { - return -1; + if ((ret = ff_put_wav_header(pb, stream)) < 0) { + return ret; } break; default: - return -1; + av_log(s, AV_LOG_ERROR, + "Invalid or not supported codec type '%s' found in the input\n", + (char *)av_x_if_null(av_get_media_type_string(stream->codec_type), "?")); + return AVERROR(EINVAL); } ff_end_tag(pb, strf); if ((t = av_dict_get(s->streams[i]->metadata, "title", NULL, 0))) { @@ -388,10 +407,13 @@ static int avi_write_ix(AVFormatContext *s) char ix_tag[] = "ix00"; int i, j; - assert(pb->seekable); + av_assert0(pb->seekable); - if (avi->riff_id > AVI_MASTER_INDEX_SIZE) - return -1; + if (avi->riff_id > AVI_MASTER_INDEX_SIZE) { + av_log(s, AV_LOG_ERROR, "Invalid riff index %d > %d\n", + avi->riff_id, AVI_MASTER_INDEX_SIZE); + return AVERROR(EINVAL); + } for (i=0;i<s->nb_streams;i++) { AVIStream *avist= s->streams[i]->priv_data; @@ -500,14 +522,21 @@ static int avi_write_packet(AVFormatContext *s, AVPacket *pkt) AVCodecContext *enc= s->streams[stream_index]->codec; int size= pkt->size; - while(enc->block_align==0 && pkt->dts != AV_NOPTS_VALUE && pkt->dts > avist->packet_count){ + av_dlog(s, "dts:%s packet_count:%d stream_index:%d\n", av_ts2str(pkt->dts), avist->packet_count, stream_index); + while(enc->block_align==0 && pkt->dts != AV_NOPTS_VALUE && pkt->dts > avist->packet_count && enc->codec_id != AV_CODEC_ID_XSUB && avist->packet_count){ AVPacket empty_packet; + if(pkt->dts - avist->packet_count > 60000){ + av_log(s, AV_LOG_ERROR, "Too large number of skipped frames %"PRId64" > 60000\n", pkt->dts - avist->packet_count); + return AVERROR(EINVAL); + } + av_init_packet(&empty_packet); empty_packet.size= 0; empty_packet.data= NULL; empty_packet.stream_index= stream_index; avi_write_packet(s, &empty_packet); + av_dlog(s, "dup dts:%s packet_count:%d\n", av_ts2str(pkt->dts), avist->packet_count); } avist->packet_count++; @@ -533,19 +562,19 @@ static int avi_write_packet(AVFormatContext *s, AVPacket *pkt) } if (s->pb->seekable) { - int err; AVIIndex* idx = &avist->indexes; int cl = idx->entry / AVI_INDEX_CLUSTER_SIZE; int id = idx->entry % AVI_INDEX_CLUSTER_SIZE; if (idx->ents_allocated <= idx->entry) { - if ((err = av_reallocp(&idx->cluster, (cl + 1) * sizeof(*idx->cluster))) < 0) { + idx->cluster = av_realloc_f(idx->cluster, sizeof(void*), cl+1); + if (!idx->cluster) { idx->ents_allocated = 0; idx->entry = 0; - return err; + return AVERROR(ENOMEM); } idx->cluster[cl] = av_malloc(AVI_INDEX_CLUSTER_SIZE*sizeof(AVIIentry)); if (!idx->cluster[cl]) - return -1; + return AVERROR(ENOMEM); idx->ents_allocated += AVI_INDEX_CLUSTER_SIZE; } @@ -610,7 +639,7 @@ static int avi_write_trailer(AVFormatContext *s) for (i=0; i<s->nb_streams; i++) { AVIStream *avist= s->streams[i]->priv_data; for (j=0; j<avist->indexes.ents_allocated/AVI_INDEX_CLUSTER_SIZE; j++) - av_free(avist->indexes.cluster[j]); + av_freep(&avist->indexes.cluster[j]); av_freep(&avist->indexes.cluster); avist->indexes.ents_allocated = avist->indexes.entry = 0; } diff --git a/libavformat/avio.c b/libavformat/avio.c index fe42974d3a..225d9821a0 100644 --- a/libavformat/avio.c +++ b/libavformat/avio.c @@ -2,20 +2,20 @@ * unbuffered I/O * Copyright (c) 2001 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -116,6 +116,16 @@ static int url_alloc_for_protocol(URLContext **puc, struct URLProtocol *up, if (up->flags & URL_PROTOCOL_FLAG_NETWORK && !ff_network_init()) return AVERROR(EIO); #endif + if ((flags & AVIO_FLAG_READ) && !up->url_read) { + av_log(NULL, AV_LOG_ERROR, + "Impossible to open the '%s' protocol for reading\n", up->name); + return AVERROR(EIO); + } + if ((flags & AVIO_FLAG_WRITE) && !up->url_write) { + av_log(NULL, AV_LOG_ERROR, + "Impossible to open the '%s' protocol for writing\n", up->name); + return AVERROR(EIO); + } uc = av_mallocz(sizeof(URLContext) + strlen(filename) + 1); if (!uc) { err = AVERROR(ENOMEM); @@ -135,8 +145,33 @@ static int url_alloc_for_protocol(URLContext **puc, struct URLProtocol *up, goto fail; } if (up->priv_data_class) { + int proto_len= strlen(up->name); + char *start = strchr(uc->filename, ','); *(const AVClass **)uc->priv_data = up->priv_data_class; av_opt_set_defaults(uc->priv_data); + if(!strncmp(up->name, uc->filename, proto_len) && uc->filename + proto_len == start){ + int ret= 0; + char *p= start; + char sep= *++p; + char *key, *val; + p++; + while(ret >= 0 && (key= strchr(p, sep)) && p<key && (val = strchr(key+1, sep))){ + *val= *key= 0; + ret= av_opt_set(uc->priv_data, p, key+1, 0); + if (ret == AVERROR_OPTION_NOT_FOUND) + av_log(uc, AV_LOG_ERROR, "Key '%s' not found.\n", p); + *val= *key= sep; + p= val+1; + } + if(ret<0 || p!=key){ + av_log(uc, AV_LOG_ERROR, "Error parsing options string %s\n", start); + av_freep(&uc->priv_data); + av_freep(&uc); + err = AVERROR(EINVAL); + goto fail; + } + memmove(start, key+1, strlen(key)); + } } } if (int_cb) @@ -187,12 +222,21 @@ int ffurl_alloc(URLContext **puc, const char *filename, int flags, char proto_str[128], proto_nested[128], *ptr; size_t proto_len = strspn(filename, URL_SCHEME_CHARS); - if (filename[proto_len] != ':' || is_dos_path(filename)) + if (!first_protocol) { + av_log(NULL, AV_LOG_WARNING, "No URL Protocols are registered. " + "Missing call to av_register_all()?\n"); + } + + if (filename[proto_len] != ':' && + (filename[proto_len] != ',' || !strchr(filename + proto_len + 1, ':')) || + is_dos_path(filename)) strcpy(proto_str, "file"); else av_strlcpy(proto_str, filename, FFMIN(proto_len + 1, sizeof(proto_str))); + if ((ptr = strchr(proto_str, ','))) + *ptr = '\0'; av_strlcpy(proto_nested, proto_str, sizeof(proto_nested)); if ((ptr = strchr(proto_nested, '+'))) *ptr = '\0'; @@ -205,6 +249,8 @@ int ffurl_alloc(URLContext **puc, const char *filename, int flags, return url_alloc_for_protocol(puc, up, filename, flags, int_cb); } *puc = NULL; + if (!strcmp("https", proto_str)) + av_log(NULL, AV_LOG_WARNING, "https protocol not found, recompile with openssl or gnutls enabled.\n"); return AVERROR_PROTOCOL_NOT_FOUND; } @@ -234,9 +280,12 @@ static inline int retry_transfer_wrapper(URLContext *h, uint8_t *buf, { int ret, len; int fast_retries = 5; + int64_t wait_since = 0; len = 0; while (len < size_min) { + if (ff_check_interrupt(&h->interrupt_callback)) + return AVERROR_EXIT; ret = transfer_func(h, buf + len, size - len); if (ret == AVERROR(EINTR)) continue; @@ -244,17 +293,22 @@ static inline int retry_transfer_wrapper(URLContext *h, uint8_t *buf, return ret; if (ret == AVERROR(EAGAIN)) { ret = 0; - if (fast_retries) + if (fast_retries) { fast_retries--; - else + } else { + if (h->rw_timeout) { + if (!wait_since) + wait_since = av_gettime(); + else if (av_gettime() > wait_since + h->rw_timeout) + return AVERROR(EIO); + } av_usleep(1000); + } } else if (ret < 1) return (ret < 0 && ret != AVERROR_EOF) ? ret : len; if (ret) fast_retries = FFMAX(fast_retries, 2); len += ret; - if (ff_check_interrupt(&h->interrupt_callback)) - return AVERROR_EXIT; } return len; } @@ -281,7 +335,7 @@ int ffurl_write(URLContext *h, const unsigned char *buf, int size) if (h->max_packet_size && size > h->max_packet_size) return AVERROR(EIO); - return retry_transfer_wrapper(h, buf, size, size, h->prot->url_write); + return retry_transfer_wrapper(h, (unsigned char *)buf, size, size, (void*)h->prot->url_write); } int64_t ffurl_seek(URLContext *h, int64_t pos, int whence) @@ -294,8 +348,9 @@ int64_t ffurl_seek(URLContext *h, int64_t pos, int whence) return ret; } -int ffurl_close(URLContext *h) +int ffurl_closep(URLContext **hh) { + URLContext *h= *hh; int ret = 0; if (!h) return 0; /* can happen when ffurl_open fails */ @@ -309,12 +364,18 @@ int ffurl_close(URLContext *h) if (h->prot->priv_data_size) { if (h->prot->priv_data_class) av_opt_free(h->priv_data); - av_free(h->priv_data); + av_freep(&h->priv_data); } - av_free(h); + av_freep(hh); return ret; } +int ffurl_close(URLContext *h) +{ + return ffurl_closep(&h); +} + + int avio_check(const char *url, int flags) { URLContext *h; @@ -361,7 +422,7 @@ int ffurl_get_multi_file_handle(URLContext *h, int **handles, int *numhandles) if (!h->prot->url_get_multi_file_handle) { if (!h->prot->url_get_file_handle) return AVERROR(ENOSYS); - *handles = av_malloc(sizeof(*handles)); + *handles = av_malloc(sizeof(**handles)); if (!*handles) return AVERROR(ENOMEM); *numhandles = 1; diff --git a/libavformat/avio.h b/libavformat/avio.h index b6d3cb33b2..4f4ac3cbaf 100644 --- a/libavformat/avio.h +++ b/libavformat/avio.h @@ -1,20 +1,20 @@ /* * copyright (c) 2001 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef AVFORMAT_AVIO_H @@ -115,6 +115,37 @@ typedef struct AVIOContext { * A combination of AVIO_SEEKABLE_ flags or 0 when the stream is not seekable. */ int seekable; + + /** + * max filesize, used to limit allocations + * This field is internal to libavformat and access from outside is not allowed. + */ + int64_t maxsize; + + /** + * avio_read and avio_write should if possible be satisfied directly + * instead of going through a buffer, and avio_seek will always + * call the underlying seek function directly. + */ + int direct; + + /** + * Bytes read statistic + * This field is internal to libavformat and access from outside is not allowed. + */ + int64_t bytes_read; + + /** + * seek statistic + * This field is internal to libavformat and access from outside is not allowed. + */ + int seek_count; + + /** + * writeout statistic + * This field is internal to libavformat and access from outside is not allowed. + */ + int writeout_count; } AVIOContext; /* unbuffered I/O */ @@ -146,6 +177,7 @@ int avio_check(const char *url, int flags); * @param opaque An opaque pointer to user-specific data. * @param read_packet A function for refilling the buffer, may be NULL. * @param write_packet A function for writing the buffer contents, may be NULL. + * The function may not change the input buffers content. * @param seek A function for seeking to specified byte position, may be NULL. * * @return Allocated AVIOContext or NULL on failure. @@ -191,8 +223,8 @@ int avio_put_str16le(AVIOContext *s, const char *str); /** * Oring this flag as into the "whence" parameter to a seek function causes it to - * seek by any means (like reopening and linear reading) or other normally unreasonble - * means that can be extreemly slow. + * seek by any means (like reopening and linear reading) or other normally unreasonable + * means that can be extremely slow. * This may be ignored by the seek code. */ #define AVSEEK_FORCE 0x20000 @@ -207,10 +239,7 @@ int64_t avio_seek(AVIOContext *s, int64_t offset, int whence); * Skip given number of bytes forward * @return new position or AVERROR. */ -static av_always_inline int64_t avio_skip(AVIOContext *s, int64_t offset) -{ - return avio_seek(s, offset, SEEK_CUR); -} +int64_t avio_skip(AVIOContext *s, int64_t offset); /** * ftell() equivalent for AVIOContext. @@ -227,12 +256,23 @@ static av_always_inline int64_t avio_tell(AVIOContext *s) */ int64_t avio_size(AVIOContext *s); +/** + * feof() equivalent for AVIOContext. + * @return non zero if and only if end of file + */ +int url_feof(AVIOContext *s); + /** @warning currently size is limited */ int avio_printf(AVIOContext *s, const char *fmt, ...) av_printf_format(2, 3); +/** + * Force flushing of buffered data to the output s. + * + * Force the buffered data to be immediately written to the output, + * without to wait to fill the internal buffer. + */ void avio_flush(AVIOContext *s); - /** * Read size bytes from AVIOContext into buf. * @return number of bytes read or AVERROR @@ -311,6 +351,14 @@ int avio_get_str16be(AVIOContext *pb, int maxlen, char *buf, int buflen); #define AVIO_FLAG_NONBLOCK 8 /** + * Use direct mode. + * avio_read and avio_write should if possible be satisfied directly + * instead of going through a buffer, and avio_seek will always + * call the underlying seek function directly. + */ +#define AVIO_FLAG_DIRECT 0x8000 + +/** * Create and initialize a AVIOContext for accessing the * resource indicated by url. * @note When the resource indicated by url has been opened in @@ -320,7 +368,7 @@ int avio_get_str16be(AVIOContext *pb, int maxlen, char *buf, int buflen); * In case of failure the pointed to value is set to NULL. * @param flags flags which control how the resource indicated by url * is to be opened - * @return 0 in case of success, a negative value corresponding to an + * @return >= 0 in case of success, a negative value corresponding to an * AVERROR code in case of failure */ int avio_open(AVIOContext **s, const char *url, int flags); @@ -339,7 +387,7 @@ int avio_open(AVIOContext **s, const char *url, int flags); * @param options A dictionary filled with protocol-private options. On return * this parameter will be destroyed and replaced with a dict containing options * that were not found. May be NULL. - * @return 0 in case of success, a negative value corresponding to an + * @return >= 0 in case of success, a negative value corresponding to an * AVERROR code in case of failure */ int avio_open2(AVIOContext **s, const char *url, int flags, @@ -417,13 +465,13 @@ int avio_pause(AVIOContext *h, int pause); * If stream_index is (-1) the timestamp should be in AV_TIME_BASE * units from the beginning of the presentation. * If a stream_index >= 0 is used and the protocol does not support - * seeking based on component streams, the call will fail with ENOTSUP. + * seeking based on component streams, the call will fail. * @param timestamp timestamp in AVStream.time_base units * or if there is no stream specified then in AV_TIME_BASE units. * @param flags Optional combination of AVSEEK_FLAG_BACKWARD, AVSEEK_FLAG_BYTE * and AVSEEK_FLAG_ANY. The protocol may silently ignore * AVSEEK_FLAG_BACKWARD and AVSEEK_FLAG_ANY, but AVSEEK_FLAG_BYTE will - * fail with ENOTSUP if used and not supported. + * fail if used and not supported. * @return >= 0 on success * @see AVInputFormat::read_seek */ diff --git a/libavformat/avio_internal.h b/libavformat/avio_internal.h index fdc98ec197..8441d49971 100644 --- a/libavformat/avio_internal.h +++ b/libavformat/avio_internal.h @@ -1,19 +1,19 @@ /* * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -78,16 +78,27 @@ static av_always_inline void ffio_wfourcc(AVIOContext *pb, const uint8_t *s) * @param s The read-only AVIOContext to rewind * @param buf The probe buffer containing the first buf_size bytes of the file * @param buf_size The size of buf - * @return 0 in case of success, a negative value corresponding to an + * @return >= 0 in case of success, a negative value corresponding to an * AVERROR code in case of failure */ -int ffio_rewind_with_probe_data(AVIOContext *s, unsigned char *buf, int buf_size); +int ffio_rewind_with_probe_data(AVIOContext *s, unsigned char **buf, int buf_size); uint64_t ffio_read_varlen(AVIOContext *bc); /** @warning must be called before any I/O */ int ffio_set_buf_size(AVIOContext *s, int buf_size); +/** + * Ensures that the requested seekback buffer size will be available + * + * Will ensure that when reading sequentially up to buf_size, seeking + * within the current pos and pos+buf_size is possible. + * Once the stream position moves outside this window this gurantee is lost. + */ +int ffio_ensure_seekback(AVIOContext *s, int buf_size); + +int ffio_limit(AVIOContext *s, int size); + void ffio_init_checksum(AVIOContext *s, unsigned long (*update_checksum)(unsigned long c, const uint8_t *p, unsigned int len), unsigned long checksum); @@ -114,7 +125,7 @@ int ffio_open_dyn_packet_buf(AVIOContext **s, int max_packet_size); * * @param s Used to return the pointer to the created AVIOContext. * In case of failure the pointed to value is set to NULL. - * @return 0 in case of success, a negative value corresponding to an + * @return >= 0 in case of success, a negative value corresponding to an * AVERROR code in case of failure */ int ffio_fdopen(AVIOContext **s, URLContext *h); diff --git a/libavformat/aviobuf.c b/libavformat/aviobuf.c index 0024f9e8b2..dc0ed717fd 100644 --- a/libavformat/aviobuf.c +++ b/libavformat/aviobuf.c @@ -2,20 +2,20 @@ * buffered I/O * Copyright (c) 2000,2001 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -24,6 +24,7 @@ #include "libavutil/intreadwrite.h" #include "libavutil/log.h" #include "libavutil/opt.h" +#include "libavutil/avassert.h" #include "avformat.h" #include "avio.h" #include "avio_internal.h" @@ -80,6 +81,7 @@ int ffio_init_context(AVIOContext *s, s->buffer_size = buffer_size; s->buf_ptr = buffer; s->opaque = opaque; + s->direct = 0; url_resetbuf(s, write_flag ? AVIO_FLAG_WRITE : AVIO_FLAG_READ); @@ -121,28 +123,34 @@ AVIOContext *avio_alloc_context( return s; } +static void writeout(AVIOContext *s, const uint8_t *data, int len) +{ + if (s->write_packet && !s->error) { + int ret = s->write_packet(s->opaque, (uint8_t *)data, len); + if (ret < 0) { + s->error = ret; + } + } + s->writeout_count ++; + s->pos += len; +} + static void flush_buffer(AVIOContext *s) { if (s->buf_ptr > s->buffer) { - if (s->write_packet && !s->error) { - int ret = s->write_packet(s->opaque, s->buffer, - s->buf_ptr - s->buffer); - if (ret < 0) { - s->error = ret; - } - } + writeout(s, s->buffer, s->buf_ptr - s->buffer); if (s->update_checksum) { s->checksum = s->update_checksum(s->checksum, s->checksum_ptr, s->buf_ptr - s->checksum_ptr); s->checksum_ptr = s->buffer; } - s->pos += s->buf_ptr - s->buffer; } s->buf_ptr = s->buffer; } void avio_w8(AVIOContext *s, int b) { + av_assert2(b>=-128 && b<=255); *s->buf_ptr++ = b; if (s->buf_ptr >= s->buf_end) flush_buffer(s); @@ -164,6 +172,11 @@ void ffio_fill(AVIOContext *s, int b, int count) void avio_write(AVIOContext *s, const unsigned char *buf, int size) { + if (s->direct && !s->update_checksum) { + avio_flush(s); + writeout(s, buf, size); + return; + } while (size > 0) { int len = FFMIN(s->buf_end - s->buf_ptr, size); memcpy(s->buf_ptr, buf, len); @@ -205,13 +218,14 @@ int64_t avio_seek(AVIOContext *s, int64_t offset, int whence) offset += offset1; } offset1 = offset - pos; - if (!s->must_flush && + if (!s->must_flush && (!s->direct || !s->seek) && offset1 >= 0 && offset1 <= (s->buf_end - s->buffer)) { /* can do the seek inside the buffer */ s->buf_ptr = s->buffer + offset1; } else if ((!s->seekable || offset1 <= s->buf_end + SHORT_SEEK_THRESHOLD - s->buffer) && !s->write_flag && offset1 >= 0 && + (!s->direct || !s->seek) && (whence != SEEK_END || force)) { while(s->pos < offset && !s->eof_reached) fill_buffer(s); @@ -229,6 +243,7 @@ int64_t avio_seek(AVIOContext *s, int64_t offset, int whence) return AVERROR(EPIPE); if ((res = s->seek(s->opaque, offset, SEEK_SET)) < 0) return res; + s->seek_count ++; if (!s->write_flag) s->buf_end = s->buffer; s->buf_ptr = s->buffer; @@ -238,6 +253,11 @@ int64_t avio_seek(AVIOContext *s, int64_t offset, int whence) return offset; } +int64_t avio_skip(AVIOContext *s, int64_t offset) +{ + return avio_seek(s, offset, SEEK_CUR); +} + int64_t avio_size(AVIOContext *s) { int64_t size; @@ -257,20 +277,31 @@ int64_t avio_size(AVIOContext *s) return size; } +int url_feof(AVIOContext *s) +{ + if(!s) + return 0; + if(s->eof_reached){ + s->eof_reached=0; + fill_buffer(s); + } + return s->eof_reached; +} + void avio_wl32(AVIOContext *s, unsigned int val) { - avio_w8(s, val); - avio_w8(s, val >> 8); - avio_w8(s, val >> 16); - avio_w8(s, val >> 24); + avio_w8(s, (uint8_t) val ); + avio_w8(s, (uint8_t)(val >> 8 )); + avio_w8(s, (uint8_t)(val >> 16)); + avio_w8(s, val >> 24 ); } void avio_wb32(AVIOContext *s, unsigned int val) { - avio_w8(s, val >> 24); - avio_w8(s, val >> 16); - avio_w8(s, val >> 8); - avio_w8(s, val); + avio_w8(s, val >> 24 ); + avio_w8(s, (uint8_t)(val >> 16)); + avio_w8(s, (uint8_t)(val >> 8 )); + avio_w8(s, (uint8_t) val ); } int avio_put_str(AVIOContext *s, const char *str) @@ -316,7 +347,7 @@ void ff_put_v(AVIOContext *bc, uint64_t val) int i = ff_get_v_length(val); while (--i > 0) - avio_w8(bc, 128 | (val >> (7 * i))); + avio_w8(bc, 128 | (uint8_t)(val >> (7*i))); avio_w8(bc, val & 127); } @@ -335,38 +366,37 @@ void avio_wb64(AVIOContext *s, uint64_t val) void avio_wl16(AVIOContext *s, unsigned int val) { - avio_w8(s, val); - avio_w8(s, val >> 8); + avio_w8(s, (uint8_t)val); + avio_w8(s, (int)val >> 8); } void avio_wb16(AVIOContext *s, unsigned int val) { - avio_w8(s, val >> 8); - avio_w8(s, val); + avio_w8(s, (int)val >> 8); + avio_w8(s, (uint8_t)val); } void avio_wl24(AVIOContext *s, unsigned int val) { avio_wl16(s, val & 0xffff); - avio_w8(s, val >> 16); + avio_w8(s, (int)val >> 16); } void avio_wb24(AVIOContext *s, unsigned int val) { - avio_wb16(s, val >> 8); - avio_w8(s, val); + avio_wb16(s, (int)val >> 8); + avio_w8(s, (uint8_t)val); } /* Input stream */ static void fill_buffer(AVIOContext *s) { - uint8_t *dst = !s->max_packet_size && - s->buf_end - s->buffer < s->buffer_size ? - s->buf_end : s->buffer; - int len = s->buffer_size - (dst - s->buffer); int max_buffer_size = s->max_packet_size ? s->max_packet_size : IO_BUFFER_SIZE; + uint8_t *dst = s->buf_end - s->buffer + max_buffer_size < s->buffer_size ? + s->buf_end : s->buffer; + int len = s->buffer_size - (dst - s->buffer); /* can't fill the buffer without read_packet, just set EOF if appropriate */ if (!s->read_packet && s->buf_ptr >= s->buf_end) @@ -384,11 +414,14 @@ static void fill_buffer(AVIOContext *s) } /* make buffer smaller in case it ended up large after probing */ - if (s->buffer_size > max_buffer_size) { - ffio_set_buf_size(s, max_buffer_size); + if (s->read_packet && s->buffer_size > max_buffer_size) { + if (dst == s->buffer) { + ffio_set_buf_size(s, max_buffer_size); - s->checksum_ptr = dst = s->buffer; - len = s->buffer_size; + s->checksum_ptr = dst = s->buffer; + } + av_assert0(len >= max_buffer_size); + len = max_buffer_size; } if (s->read_packet) @@ -405,6 +438,7 @@ static void fill_buffer(AVIOContext *s) s->pos += len; s->buf_ptr = dst; s->buf_end = dst + len; + s->bytes_read += len; } } @@ -453,7 +487,7 @@ int avio_read(AVIOContext *s, unsigned char *buf, int size) if (len > size) len = size; if (len == 0 || s->write_flag) { - if(size > s->buffer_size && !s->update_checksum){ + if((s->direct || size > s->buffer_size) && !s->update_checksum){ if(s->read_packet) len = s->read_packet(s->opaque, buf, size); if (len <= 0) { @@ -465,6 +499,7 @@ int avio_read(AVIOContext *s, unsigned char *buf, int size) break; } else { s->pos += len; + s->bytes_read += len; size -= len; buf += len; s->buf_ptr = s->buffer; @@ -484,8 +519,8 @@ int avio_read(AVIOContext *s, unsigned char *buf, int size) } } if (size1 == size) { - if (s->error) return s->error; - if (s->eof_reached) return AVERROR_EOF; + if (s->error) return s->error; + if (url_feof(s)) return AVERROR_EOF; } return size1 - size; } @@ -533,8 +568,8 @@ int ffio_read_partial(AVIOContext *s, unsigned char *buf, int size) memcpy(buf, s->buf_ptr, len); s->buf_ptr += len; if (!len) { - if (s->error) return s->error; - if (s->eof_reached) return AVERROR_EOF; + if (s->error) return s->error; + if (url_feof(s)) return AVERROR_EOF; } return len; } @@ -686,11 +721,12 @@ int ffio_fdopen(AVIOContext **s, URLContext *h) return AVERROR(ENOMEM); *s = avio_alloc_context(buffer, buffer_size, h->flags & AVIO_FLAG_WRITE, h, - ffurl_read, ffurl_write, ffurl_seek); + (void*)ffurl_read, (void*)ffurl_write, (void*)ffurl_seek); if (!*s) { av_free(buffer); return AVERROR(ENOMEM); } + (*s)->direct = h->flags & AVIO_FLAG_DIRECT; (*s)->seekable = h->is_streamed ? 0 : AVIO_SEEKABLE_NORMAL; (*s)->max_packet_size = max_packet_size; if(h->prot) { @@ -701,6 +737,31 @@ int ffio_fdopen(AVIOContext **s, URLContext *h) return 0; } +int ffio_ensure_seekback(AVIOContext *s, int buf_size) +{ + uint8_t *buffer; + int max_buffer_size = s->max_packet_size ? + s->max_packet_size : IO_BUFFER_SIZE; + + buf_size += s->buf_ptr - s->buffer + max_buffer_size; + + if (buf_size < s->buffer_size || s->seekable) + return 0; + av_assert0(!s->write_flag); + + buffer = av_malloc(buf_size); + if (!buffer) + return AVERROR(ENOMEM); + + memcpy(buffer, s->buffer, s->buffer_size); + av_free(s->buffer); + s->buf_ptr = buffer + (s->buf_ptr - s->buffer); + s->buf_end = buffer + (s->buf_end - s->buffer); + s->buffer = buffer; + s->buffer_size = buf_size; + return 0; +} + int ffio_set_buf_size(AVIOContext *s, int buf_size) { uint8_t *buffer; @@ -718,7 +779,7 @@ int ffio_set_buf_size(AVIOContext *s, int buf_size) static int url_resetbuf(AVIOContext *s, int flags) { - assert(flags == AVIO_FLAG_WRITE || flags == AVIO_FLAG_READ); + av_assert1(flags == AVIO_FLAG_WRITE || flags == AVIO_FLAG_READ); if (flags & AVIO_FLAG_WRITE) { s->buf_end = s->buffer + s->buffer_size; @@ -730,27 +791,32 @@ static int url_resetbuf(AVIOContext *s, int flags) return 0; } -int ffio_rewind_with_probe_data(AVIOContext *s, unsigned char *buf, int buf_size) +int ffio_rewind_with_probe_data(AVIOContext *s, unsigned char **bufp, int buf_size) { int64_t buffer_start; int buffer_size; int overlap, new_size, alloc_size; + uint8_t *buf = *bufp; - if (s->write_flag) + if (s->write_flag) { + av_freep(bufp); return AVERROR(EINVAL); + } buffer_size = s->buf_end - s->buffer; /* the buffers must touch or overlap */ - if ((buffer_start = s->pos - buffer_size) > buf_size) + if ((buffer_start = s->pos - buffer_size) > buf_size) { + av_freep(bufp); return AVERROR(EINVAL); + } overlap = buf_size - buffer_start; new_size = buf_size + buffer_size - overlap; alloc_size = FFMAX(s->buffer_size, new_size); if (alloc_size > buf_size) - if (!(buf = av_realloc(buf, alloc_size))) + if (!(buf = (*bufp) = av_realloc_f(buf, 1, alloc_size))) return AVERROR(ENOMEM); if (new_size > buf_size) { @@ -801,6 +867,10 @@ int avio_close(AVIOContext *s) avio_flush(s); h = s->opaque; av_freep(&s->buffer); + if (s->write_flag) + av_log(s, AV_LOG_DEBUG, "Statistics: %d seeks, %d writeouts\n", s->seek_count, s->writeout_count); + else + av_log(s, AV_LOG_DEBUG, "Statistics: %"PRId64" bytes read, %d seeks\n", s->bytes_read, s->seek_count); av_free(s); return ffurl_close(h); } diff --git a/libavformat/avisynth.c b/libavformat/avisynth.c index e411d359b7..e35306b6a5 100644 --- a/libavformat/avisynth.c +++ b/libavformat/avisynth.c @@ -1,229 +1,685 @@ /* - * AviSynth support - * Copyright (c) 2006 DivX, Inc. + * Avi/AvxSynth support + * Copyright (c) 2012 AvxSynth Team. * - * This file is part of Libav. - * - * Libav is free software; you can redistribute it and/or + * This file is part of FFmpeg + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <windows.h> -#include <vfw.h> - #include "libavutil/internal.h" +#include "libavcodec/internal.h" #include "avformat.h" #include "internal.h" -#include "riff.h" - -typedef struct { - PAVISTREAM handle; - AVISTREAMINFO info; - DWORD read; - LONG chunck_size; - LONG chunck_samples; -} AviSynthStream; - -typedef struct { - PAVIFILE file; - AviSynthStream *streams; - int nb_streams; - int next_stream; + +/* Enable function pointer definitions for runtime loading. */ +#define AVSC_NO_DECLSPEC + +/* Platform-specific directives for AviSynth vs AvxSynth. */ +#ifdef _WIN32 + #include <windows.h> + #undef EXTERN_C + #include "compat/avisynth/avisynth_c.h" + #include "compat/avisynth/avisynth_c_25.h" + #define AVISYNTH_LIB "avisynth" + #define USING_AVISYNTH +#else + #include <dlfcn.h> + #include "compat/avisynth/avxsynth_c.h" + #if defined (__APPLE__) + #define AVISYNTH_LIB "libavxsynth.dylib" + #else + #define AVISYNTH_LIB "libavxsynth.so" + #endif + + #define LoadLibrary(x) dlopen(x, RTLD_NOW | RTLD_GLOBAL) + #define GetProcAddress dlsym + #define FreeLibrary dlclose +#endif + +typedef struct AviSynthLibrary { + void *library; +#define AVSC_DECLARE_FUNC(name) name ## _func name + AVSC_DECLARE_FUNC(avs_bit_blt); + AVSC_DECLARE_FUNC(avs_clip_get_error); + AVSC_DECLARE_FUNC(avs_create_script_environment); + AVSC_DECLARE_FUNC(avs_delete_script_environment); + AVSC_DECLARE_FUNC(avs_get_audio); + AVSC_DECLARE_FUNC(avs_get_error); + AVSC_DECLARE_FUNC(avs_get_frame); + AVSC_DECLARE_FUNC(avs_get_version); + AVSC_DECLARE_FUNC(avs_get_video_info); + AVSC_DECLARE_FUNC(avs_invoke); + AVSC_DECLARE_FUNC(avs_release_clip); + AVSC_DECLARE_FUNC(avs_release_value); + AVSC_DECLARE_FUNC(avs_release_video_frame); + AVSC_DECLARE_FUNC(avs_take_clip); +#undef AVSC_DECLARE_FUNC +} AviSynthLibrary; + +typedef struct AviSynthContext { + AVS_ScriptEnvironment *env; + AVS_Clip *clip; + const AVS_VideoInfo *vi; + + /* avisynth_read_packet_video() iterates over this. */ + int n_planes; + const int *planes; + + int curr_stream; + int curr_frame; + int64_t curr_sample; + + int error; + + /* Linked list pointers. */ + struct AviSynthContext *next; } AviSynthContext; -static int avisynth_read_header(AVFormatContext *s) +static const int avs_planes_packed[1] = { 0 }; +static const int avs_planes_grey[1] = { AVS_PLANAR_Y }; +static const int avs_planes_yuv[3] = { AVS_PLANAR_Y, AVS_PLANAR_U, + AVS_PLANAR_V }; + +/* A conflict between C++ global objects, atexit, and dynamic loading requires + * us to register our own atexit handler to prevent double freeing. */ +static AviSynthLibrary *avs_library = NULL; +static int avs_atexit_called = 0; + +/* Linked list of AviSynthContexts. An atexit handler destroys this list. */ +static AviSynthContext *avs_ctx_list = NULL; + +static av_cold void avisynth_atexit_handler(void); + +static av_cold int avisynth_load_library(void) +{ + avs_library = av_mallocz(sizeof(AviSynthLibrary)); + if (!avs_library) + return AVERROR_UNKNOWN; + + avs_library->library = LoadLibrary(AVISYNTH_LIB); + if (!avs_library->library) + goto init_fail; + +#define LOAD_AVS_FUNC(name, continue_on_fail) \ + avs_library->name = \ + (void *)GetProcAddress(avs_library->library, #name); \ + if (!continue_on_fail && !avs_library->name) \ + goto fail; + + LOAD_AVS_FUNC(avs_bit_blt, 0); + LOAD_AVS_FUNC(avs_clip_get_error, 0); + LOAD_AVS_FUNC(avs_create_script_environment, 0); + LOAD_AVS_FUNC(avs_delete_script_environment, 0); + LOAD_AVS_FUNC(avs_get_audio, 0); + LOAD_AVS_FUNC(avs_get_error, 1); // New to AviSynth 2.6 + LOAD_AVS_FUNC(avs_get_frame, 0); + LOAD_AVS_FUNC(avs_get_version, 0); + LOAD_AVS_FUNC(avs_get_video_info, 0); + LOAD_AVS_FUNC(avs_invoke, 0); + LOAD_AVS_FUNC(avs_release_clip, 0); + LOAD_AVS_FUNC(avs_release_value, 0); + LOAD_AVS_FUNC(avs_release_video_frame, 0); + LOAD_AVS_FUNC(avs_take_clip, 0); +#undef LOAD_AVS_FUNC + + atexit(avisynth_atexit_handler); + return 0; + +fail: + FreeLibrary(avs_library->library); +init_fail: + av_freep(&avs_library); + return AVERROR_UNKNOWN; +} + +/* Note that avisynth_context_create and avisynth_context_destroy + * do not allocate or free the actual context! That is taken care of + * by libavformat. */ +static av_cold int avisynth_context_create(AVFormatContext *s) { AviSynthContext *avs = s->priv_data; - HRESULT res; - AVIFILEINFO info; - DWORD id; - AVStream *st; - AviSynthStream *stream; - wchar_t filename_wchar[1024] = { 0 }; - char filename_char[1024] = { 0 }; - - AVIFileInit(); - - /* AviSynth cannot accept UTF-8 file names. */ - MultiByteToWideChar(CP_UTF8, 0, s->filename, -1, filename_wchar, 1024); - WideCharToMultiByte(CP_THREAD_ACP, 0, filename_wchar, -1, filename_char, - 1024, NULL, NULL); - res = AVIFileOpen(&avs->file, filename_char, - OF_READ | OF_SHARE_DENY_WRITE, NULL); - if (res != S_OK) { - av_log(s, AV_LOG_ERROR, "AVIFileOpen failed with error %ld", res); - AVIFileExit(); - return -1; - } - - res = AVIFileInfo(avs->file, &info, sizeof(info)); - if (res != S_OK) { - av_log(s, AV_LOG_ERROR, "AVIFileInfo failed with error %ld", res); - AVIFileExit(); - return -1; - } - - avs->streams = av_mallocz(info.dwStreams * sizeof(AviSynthStream)); - - for (id = 0; id < info.dwStreams; id++) { - stream = &avs->streams[id]; - stream->read = 0; - if (AVIFileGetStream(avs->file, &stream->handle, 0, id) == S_OK && - AVIStreamInfo(stream->handle, &stream->info, - sizeof(stream->info)) == S_OK) { - if (stream->info.fccType == streamtypeAUDIO) { - WAVEFORMATEX wvfmt; - LONG struct_size = sizeof(WAVEFORMATEX); - if (AVIStreamReadFormat(stream->handle, 0, - &wvfmt, &struct_size) != S_OK) - continue; - - st = avformat_new_stream(s, NULL); - st->id = id; - st->codec->codec_type = AVMEDIA_TYPE_AUDIO; - - st->codec->block_align = wvfmt.nBlockAlign; - st->codec->channels = wvfmt.nChannels; - st->codec->sample_rate = wvfmt.nSamplesPerSec; - st->codec->bit_rate = wvfmt.nAvgBytesPerSec * 8; - st->codec->bits_per_coded_sample = wvfmt.wBitsPerSample; - - stream->chunck_samples = wvfmt.nSamplesPerSec * - (uint64_t)info.dwScale / - (uint64_t)info.dwRate; - stream->chunck_size = stream->chunck_samples * - wvfmt.nChannels * - wvfmt.wBitsPerSample / 8; - - st->codec->codec_tag = wvfmt.wFormatTag; - st->codec->codec_id = - ff_wav_codec_get_id(wvfmt.wFormatTag, - st->codec->bits_per_coded_sample); - } else if (stream->info.fccType == streamtypeVIDEO) { - BITMAPINFO imgfmt; - LONG struct_size = sizeof(BITMAPINFO); - - stream->chunck_size = stream->info.dwSampleSize; - stream->chunck_samples = 1; - - if (AVIStreamReadFormat(stream->handle, 0, &imgfmt, - &struct_size) != S_OK) - continue; - - st = avformat_new_stream(s, NULL); - st->id = id; - st->codec->codec_type = AVMEDIA_TYPE_VIDEO; - st->avg_frame_rate.num = stream->info.dwRate; - st->avg_frame_rate.den = stream->info.dwScale; - - st->codec->width = imgfmt.bmiHeader.biWidth; - st->codec->height = imgfmt.bmiHeader.biHeight; - - st->codec->bits_per_coded_sample = imgfmt.bmiHeader.biBitCount; - st->codec->bit_rate = (uint64_t)stream->info.dwSampleSize * - (uint64_t)stream->info.dwRate * 8 / - (uint64_t)stream->info.dwScale; - st->codec->codec_tag = imgfmt.bmiHeader.biCompression; - st->codec->codec_id = - ff_codec_get_id(ff_codec_bmp_tags, - imgfmt.bmiHeader.biCompression); - - st->duration = stream->info.dwLength; - } else { - AVIStreamRelease(stream->handle); - continue; - } - - avs->nb_streams++; - - st->codec->stream_codec_tag = stream->info.fccHandler; - - avpriv_set_pts_info(st, 64, info.dwScale, info.dwRate); - st->start_time = stream->info.dwStart; + int ret; + + if (!avs_library) + if (ret = avisynth_load_library()) + return ret; + + avs->env = avs_library->avs_create_script_environment(3); + if (avs_library->avs_get_error) { + const char *error = avs_library->avs_get_error(avs->env); + if (error) { + av_log(s, AV_LOG_ERROR, "%s\n", error); + return AVERROR_UNKNOWN; } } + if (!avs_ctx_list) { + avs_ctx_list = avs; + } else { + avs->next = avs_ctx_list; + avs_ctx_list = avs; + } + return 0; } -static int avisynth_read_packet(AVFormatContext *s, AVPacket *pkt) +static av_cold void avisynth_context_destroy(AviSynthContext *avs) +{ + if (avs_atexit_called) + return; + + if (avs == avs_ctx_list) { + avs_ctx_list = avs->next; + } else { + AviSynthContext *prev = avs_ctx_list; + while (prev->next != avs) + prev = prev->next; + prev->next = avs->next; + } + + if (avs->clip) { + avs_library->avs_release_clip(avs->clip); + avs->clip = NULL; + } + if (avs->env) { + avs_library->avs_delete_script_environment(avs->env); + avs->env = NULL; + } +} + +static av_cold void avisynth_atexit_handler(void) +{ + AviSynthContext *avs = avs_ctx_list; + + while (avs) { + AviSynthContext *next = avs->next; + avisynth_context_destroy(avs); + avs = next; + } + FreeLibrary(avs_library->library); + av_freep(&avs_library); + + avs_atexit_called = 1; +} + +/* Create AVStream from audio and video data. */ +static int avisynth_create_stream_video(AVFormatContext *s, AVStream *st) { AviSynthContext *avs = s->priv_data; - HRESULT res; - AviSynthStream *stream; - int stream_id = avs->next_stream; - LONG read_size; + int planar = 0; // 0: packed, 1: YUV, 2: Y8 + + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->codec->codec_id = AV_CODEC_ID_RAWVIDEO; + st->codec->width = avs->vi->width; + st->codec->height = avs->vi->height; + + st->time_base = (AVRational) { avs->vi->fps_denominator, + avs->vi->fps_numerator }; + st->avg_frame_rate = (AVRational) { avs->vi->fps_numerator, + avs->vi->fps_denominator }; + st->start_time = 0; + st->duration = avs->vi->num_frames; + st->nb_frames = avs->vi->num_frames; + + switch (avs->vi->pixel_type) { +#ifdef USING_AVISYNTH + case AVS_CS_YV24: + st->codec->pix_fmt = AV_PIX_FMT_YUV444P; + planar = 1; + break; + case AVS_CS_YV16: + st->codec->pix_fmt = AV_PIX_FMT_YUV422P; + planar = 1; + break; + case AVS_CS_YV411: + st->codec->pix_fmt = AV_PIX_FMT_YUV411P; + planar = 1; + break; + case AVS_CS_Y8: + st->codec->pix_fmt = AV_PIX_FMT_GRAY8; + planar = 2; + break; +#endif + case AVS_CS_BGR24: + st->codec->pix_fmt = AV_PIX_FMT_BGR24; + break; + case AVS_CS_BGR32: + st->codec->pix_fmt = AV_PIX_FMT_RGB32; + break; + case AVS_CS_YUY2: + st->codec->pix_fmt = AV_PIX_FMT_YUYV422; + break; + case AVS_CS_YV12: + st->codec->pix_fmt = AV_PIX_FMT_YUV420P; + planar = 1; + break; + case AVS_CS_I420: // Is this even used anywhere? + st->codec->pix_fmt = AV_PIX_FMT_YUV420P; + planar = 1; + break; + default: + av_log(s, AV_LOG_ERROR, + "unknown AviSynth colorspace %d\n", avs->vi->pixel_type); + avs->error = 1; + return AVERROR_UNKNOWN; + } - // handle interleaving manually... - stream = &avs->streams[stream_id]; + switch (planar) { + case 2: // Y8 + avs->n_planes = 1; + avs->planes = avs_planes_grey; + break; + case 1: // YUV + avs->n_planes = 3; + avs->planes = avs_planes_yuv; + break; + default: + avs->n_planes = 1; + avs->planes = avs_planes_packed; + } + return 0; +} - if (stream->read >= stream->info.dwLength) - return AVERROR(EIO); +static int avisynth_create_stream_audio(AVFormatContext *s, AVStream *st) +{ + AviSynthContext *avs = s->priv_data; - if (av_new_packet(pkt, stream->chunck_size)) - return AVERROR(EIO); - pkt->stream_index = stream_id; - pkt->pts = avs->streams[stream_id].read / - avs->streams[stream_id].chunck_samples; + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->sample_rate = avs->vi->audio_samples_per_second; + st->codec->channels = avs->vi->nchannels; + st->time_base = (AVRational) { 1, + avs->vi->audio_samples_per_second }; + + switch (avs->vi->sample_type) { + case AVS_SAMPLE_INT8: + st->codec->codec_id = AV_CODEC_ID_PCM_U8; + break; + case AVS_SAMPLE_INT16: + st->codec->codec_id = AV_CODEC_ID_PCM_S16LE; + break; + case AVS_SAMPLE_INT24: + st->codec->codec_id = AV_CODEC_ID_PCM_S24LE; + break; + case AVS_SAMPLE_INT32: + st->codec->codec_id = AV_CODEC_ID_PCM_S32LE; + break; + case AVS_SAMPLE_FLOAT: + st->codec->codec_id = AV_CODEC_ID_PCM_F32LE; + break; + default: + av_log(s, AV_LOG_ERROR, + "unknown AviSynth sample type %d\n", avs->vi->sample_type); + avs->error = 1; + return AVERROR_UNKNOWN; + } + return 0; +} - res = AVIStreamRead(stream->handle, stream->read, stream->chunck_samples, - pkt->data, stream->chunck_size, &read_size, NULL); +static int avisynth_create_stream(AVFormatContext *s) +{ + AviSynthContext *avs = s->priv_data; + AVStream *st; + int ret; + int id = 0; + + if (avs_has_video(avs->vi)) { + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR_UNKNOWN; + st->id = id++; + if (ret = avisynth_create_stream_video(s, st)) + return ret; + } + if (avs_has_audio(avs->vi)) { + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR_UNKNOWN; + st->id = id++; + if (ret = avisynth_create_stream_audio(s, st)) + return ret; + } + return 0; +} - pkt->pts = stream->read; - pkt->size = read_size; +static int avisynth_open_file(AVFormatContext *s) +{ + AviSynthContext *avs = s->priv_data; + AVS_Value arg, val; + int ret; +#ifdef USING_AVISYNTH + char filename_ansi[MAX_PATH * 4]; + wchar_t filename_wc[MAX_PATH * 4]; +#endif + + if (ret = avisynth_context_create(s)) + return ret; + +#ifdef USING_AVISYNTH + /* Convert UTF-8 to ANSI code page */ + MultiByteToWideChar(CP_UTF8, 0, s->filename, -1, filename_wc, MAX_PATH * 4); + WideCharToMultiByte(CP_THREAD_ACP, 0, filename_wc, -1, filename_ansi, + MAX_PATH * 4, NULL, NULL); + arg = avs_new_value_string(filename_ansi); +#else + arg = avs_new_value_string(s->filename); +#endif + val = avs_library->avs_invoke(avs->env, "Import", arg, 0); + if (avs_is_error(val)) { + av_log(s, AV_LOG_ERROR, "%s\n", avs_as_error(val)); + ret = AVERROR_UNKNOWN; + goto fail; + } + if (!avs_is_clip(val)) { + av_log(s, AV_LOG_ERROR, "AviSynth script did not return a clip\n"); + ret = AVERROR_UNKNOWN; + goto fail; + } + + avs->clip = avs_library->avs_take_clip(val, avs->env); + avs->vi = avs_library->avs_get_video_info(avs->clip); - stream->read += stream->chunck_samples; + /* Release the AVS_Value as it will go out of scope. */ + avs_library->avs_release_value(val); - // prepare for the next stream to read - do - avs->next_stream = (avs->next_stream + 1) % avs->nb_streams; - while (avs->next_stream != stream_id && - s->streams[avs->next_stream]->discard >= AVDISCARD_ALL); + if (ret = avisynth_create_stream(s)) + goto fail; - return (res == S_OK) ? pkt->size : -1; + return 0; + +fail: + avisynth_context_destroy(avs); + return ret; } -static int avisynth_read_close(AVFormatContext *s) +static void avisynth_next_stream(AVFormatContext *s, AVStream **st, + AVPacket *pkt, int *discard) { AviSynthContext *avs = s->priv_data; - int i; - for (i = 0; i < avs->nb_streams; i++) - AVIStreamRelease(avs->streams[i].handle); + pkt->stream_index = avs->curr_stream++; + avs->curr_stream %= s->nb_streams; + + *st = s->streams[pkt->stream_index]; + if ((*st)->discard == AVDISCARD_ALL) + *discard = 1; + else + *discard = 0; + + return; +} + +/* Copy AviSynth clip data into an AVPacket. */ +static int avisynth_read_packet_video(AVFormatContext *s, AVPacket *pkt, + int discard) +{ + AviSynthContext *avs = s->priv_data; + AVS_VideoFrame *frame; + unsigned char *dst_p; + const unsigned char *src_p; + int n, i, plane, rowsize, planeheight, pitch, bits; + const char *error; + + if (avs->curr_frame >= avs->vi->num_frames) + return AVERROR_EOF; + + /* This must happen even if the stream is discarded to prevent desync. */ + n = avs->curr_frame++; + if (discard) + return 0; + + pkt->pts = n; + pkt->dts = n; + pkt->duration = 1; + +#ifdef USING_AVISYNTH + /* Define the bpp values for the new AviSynth 2.6 colorspaces. + * Since AvxSynth doesn't have these functions, special-case + * it in order to avoid implicit declaration errors. */ + + if (avs_is_yv24(avs->vi)) + bits = 24; + else if (avs_is_yv16(avs->vi)) + bits = 16; + else if (avs_is_yv411(avs->vi)) + bits = 12; + else if (avs_is_y8(avs->vi)) + bits = 8; + else +#endif + bits = avs_bits_per_pixel(avs->vi); + + /* Without the cast to int64_t, calculation overflows at about 9k x 9k + * resolution. */ + pkt->size = (((int64_t)avs->vi->width * + (int64_t)avs->vi->height) * bits) / 8; + if (!pkt->size) + return AVERROR_UNKNOWN; + pkt->data = av_malloc(pkt->size); + if (!pkt->data) + return AVERROR(ENOMEM); + + frame = avs_library->avs_get_frame(avs->clip, n); + error = avs_library->avs_clip_get_error(avs->clip); + if (error) { + av_log(s, AV_LOG_ERROR, "%s\n", error); + avs->error = 1; + av_freep(&pkt->data); + return AVERROR_UNKNOWN; + } + + dst_p = pkt->data; + for (i = 0; i < avs->n_planes; i++) { + plane = avs->planes[i]; + src_p = avs_get_read_ptr_p(frame, plane); + pitch = avs_get_pitch_p(frame, plane); + +#ifdef USING_AVISYNTH + if (avs_library->avs_get_version(avs->clip) == 3) { + rowsize = avs_get_row_size_p_25(frame, plane); + planeheight = avs_get_height_p_25(frame, plane); + } else { + rowsize = avs_get_row_size_p(frame, plane); + planeheight = avs_get_height_p(frame, plane); + } +#else + rowsize = avs_get_row_size_p(frame, plane); + planeheight = avs_get_height_p(frame, plane); +#endif + + /* Flip RGB video. */ + if (avs_is_rgb24(avs->vi) || avs_is_rgb(avs->vi)) { + src_p = src_p + (planeheight - 1) * pitch; + pitch = -pitch; + } + + avs_library->avs_bit_blt(avs->env, dst_p, rowsize, src_p, pitch, + rowsize, planeheight); + dst_p += rowsize * planeheight; + } - av_free(avs->streams); - AVIFileRelease(avs->file); - AVIFileExit(); + avs_library->avs_release_video_frame(frame); return 0; } -static int avisynth_read_seek(AVFormatContext *s, int stream_index, - int64_t pts, int flags) +static int avisynth_read_packet_audio(AVFormatContext *s, AVPacket *pkt, + int discard) +{ + AviSynthContext *avs = s->priv_data; + AVRational fps, samplerate; + int samples; + int64_t n; + const char *error; + + if (avs->curr_sample >= avs->vi->num_audio_samples) + return AVERROR_EOF; + + fps.num = avs->vi->fps_numerator; + fps.den = avs->vi->fps_denominator; + samplerate.num = avs->vi->audio_samples_per_second; + samplerate.den = 1; + + if (avs_has_video(avs->vi)) { + if (avs->curr_frame < avs->vi->num_frames) + samples = av_rescale_q(avs->curr_frame, samplerate, fps) - + avs->curr_sample; + else + samples = av_rescale_q(1, samplerate, fps); + } else { + samples = 1000; + } + + /* After seeking, audio may catch up with video. */ + if (samples <= 0) { + pkt->size = 0; + pkt->data = NULL; + return 0; + } + + if (avs->curr_sample + samples > avs->vi->num_audio_samples) + samples = avs->vi->num_audio_samples - avs->curr_sample; + + /* This must happen even if the stream is discarded to prevent desync. */ + n = avs->curr_sample; + avs->curr_sample += samples; + if (discard) + return 0; + + pkt->pts = n; + pkt->dts = n; + pkt->duration = samples; + + pkt->size = avs_bytes_per_channel_sample(avs->vi) * + samples * avs->vi->nchannels; + if (!pkt->size) + return AVERROR_UNKNOWN; + pkt->data = av_malloc(pkt->size); + if (!pkt->data) + return AVERROR(ENOMEM); + + avs_library->avs_get_audio(avs->clip, pkt->data, n, samples); + error = avs_library->avs_clip_get_error(avs->clip); + if (error) { + av_log(s, AV_LOG_ERROR, "%s\n", error); + avs->error = 1; + av_freep(&pkt->data); + return AVERROR_UNKNOWN; + } + return 0; +} + +static av_cold int avisynth_read_header(AVFormatContext *s) +{ + int ret; + + // Calling library must implement a lock for thread-safe opens. + if (ret = avpriv_lock_avformat()) + return ret; + + if (ret = avisynth_open_file(s)) { + avpriv_unlock_avformat(); + return ret; + } + + avpriv_unlock_avformat(); + return 0; +} + +static int avisynth_read_packet(AVFormatContext *s, AVPacket *pkt) { AviSynthContext *avs = s->priv_data; - int stream_id; + AVStream *st; + int discard = 0; + int ret; + + if (avs->error) + return AVERROR_UNKNOWN; + + pkt->destruct = av_destruct_packet; + + /* If either stream reaches EOF, try to read the other one before + * giving up. */ + avisynth_next_stream(s, &st, pkt, &discard); + if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) { + ret = avisynth_read_packet_video(s, pkt, discard); + if (ret == AVERROR_EOF && avs_has_audio(avs->vi)) { + avisynth_next_stream(s, &st, pkt, &discard); + return avisynth_read_packet_audio(s, pkt, discard); + } + } else { + ret = avisynth_read_packet_audio(s, pkt, discard); + if (ret == AVERROR_EOF && avs_has_video(avs->vi)) { + avisynth_next_stream(s, &st, pkt, &discard); + return avisynth_read_packet_video(s, pkt, discard); + } + } - for (stream_id = 0; stream_id < avs->nb_streams; stream_id++) - avs->streams[stream_id].read = - pts * avs->streams[stream_id].chunck_samples; + return ret; +} + +static av_cold int avisynth_read_close(AVFormatContext *s) +{ + if (avpriv_lock_avformat()) + return AVERROR_UNKNOWN; + + avisynth_context_destroy(s->priv_data); + avpriv_unlock_avformat(); + return 0; +} + +static int avisynth_read_seek(AVFormatContext *s, int stream_index, + int64_t timestamp, int flags) +{ + AviSynthContext *avs = s->priv_data; + AVStream *st; + AVRational fps, samplerate; + + if (avs->error) + return AVERROR_UNKNOWN; + + fps = (AVRational) { avs->vi->fps_numerator, + avs->vi->fps_denominator }; + samplerate = (AVRational) { avs->vi->audio_samples_per_second, 1 }; + + st = s->streams[stream_index]; + if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) { + /* AviSynth frame counts are signed int. */ + if ((timestamp >= avs->vi->num_frames) || + (timestamp > INT_MAX) || + (timestamp < 0)) + return AVERROR_EOF; + avs->curr_frame = timestamp; + if (avs_has_audio(avs->vi)) + avs->curr_sample = av_rescale_q(timestamp, samplerate, fps); + } else { + if ((timestamp >= avs->vi->num_audio_samples) || (timestamp < 0)) + return AVERROR_EOF; + /* Force frame granularity for seeking. */ + if (avs_has_video(avs->vi)) { + avs->curr_frame = av_rescale_q(timestamp, fps, samplerate); + avs->curr_sample = av_rescale_q(avs->curr_frame, samplerate, fps); + } else { + avs->curr_sample = timestamp; + } + } return 0; } AVInputFormat ff_avisynth_demuxer = { .name = "avisynth", - .long_name = NULL_IF_CONFIG_SMALL("AviSynth"), + .long_name = NULL_IF_CONFIG_SMALL("AviSynth script"), .priv_data_size = sizeof(AviSynthContext), .read_header = avisynth_read_header, .read_packet = avisynth_read_packet, diff --git a/libavformat/avlanguage.c b/libavformat/avlanguage.c index e606ef22f9..39f2560d94 100644 --- a/libavformat/avlanguage.c +++ b/libavformat/avlanguage.c @@ -1,20 +1,20 @@ /* * Cyril Comparon, Larbi Joubala, Resonate-MP4 2009 * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/avlanguage.h b/libavformat/avlanguage.h index 2ec3e2d372..7fb8968810 100644 --- a/libavformat/avlanguage.h +++ b/libavformat/avlanguage.h @@ -1,20 +1,20 @@ /* * Cyril Comparon, Larbi Joubala, Resonate-MP4 2009 * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/avr.c b/libavformat/avr.c new file mode 100644 index 0000000000..02edf451a8 --- /dev/null +++ b/libavformat/avr.c @@ -0,0 +1,93 @@ +/* + * AVR demuxer + * Copyright (c) 2012 Paul B Mahol + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/intreadwrite.h" +#include "avformat.h" +#include "internal.h" +#include "pcm.h" + +static int avr_probe(AVProbeData *p) +{ + if (AV_RL32(p->buf) == MKTAG('2', 'B', 'I', 'T')) + return AVPROBE_SCORE_EXTENSION; + return 0; +} + +static int avr_read_header(AVFormatContext *s) +{ + uint16_t chan, sign, bps; + AVStream *st; + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + + avio_skip(s->pb, 4); // magic + avio_skip(s->pb, 8); // sample_name + + chan = avio_rb16(s->pb); + if (!chan) { + st->codec->channels = 1; + } else if (chan == 0xFFFFu) { + st->codec->channels = 2; + } else { + avpriv_request_sample(s, "chan %d", chan); + return AVERROR_PATCHWELCOME; + } + + st->codec->bits_per_coded_sample = bps = avio_rb16(s->pb); + + sign = avio_rb16(s->pb); + + avio_skip(s->pb, 2); // loop + avio_skip(s->pb, 2); // midi + avio_skip(s->pb, 1); // replay speed + + st->codec->sample_rate = avio_rb24(s->pb); + avio_skip(s->pb, 4 * 3); + avio_skip(s->pb, 2 * 3); + avio_skip(s->pb, 20); + avio_skip(s->pb, 64); + + st->codec->codec_id = ff_get_pcm_codec_id(bps, 0, 1, sign); + if (st->codec->codec_id == AV_CODEC_ID_NONE) { + avpriv_request_sample(s, "Bps %d and sign %d", bps, sign); + return AVERROR_PATCHWELCOME; + } + + st->codec->block_align = bps * st->codec->channels / 8; + + avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate); + return 0; +} + +AVInputFormat ff_avr_demuxer = { + .name = "avr", + .long_name = NULL_IF_CONFIG_SMALL("AVR (Audio Visual Research)"), + .read_probe = avr_probe, + .read_header = avr_read_header, + .read_packet = ff_pcm_read_packet, + .read_seek = ff_pcm_read_seek, + .extensions = "avr", + .flags = AVFMT_GENERIC_INDEX, +}; diff --git a/libavformat/avs.c b/libavformat/avs.c index 3e95a3656a..7814301244 100644 --- a/libavformat/avs.c +++ b/libavformat/avs.c @@ -2,20 +2,20 @@ * AVS demuxer. * Copyright (c) 2006 Aurelien Jacobs <aurel@gnuage.org> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -52,7 +52,7 @@ static int avs_probe(AVProbeData * p) if (d[0] == 'w' && d[1] == 'W' && d[2] == 0x10 && d[3] == 0) /* Ensure the buffer probe scores higher than the extension probe. * This avoids problems with misdetection as AviSynth scripts. */ - return AVPROBE_SCORE_EXTENSION + 1; + return AVPROBE_SCORE_EXTENSION + 5; return 0; } @@ -190,6 +190,9 @@ static int avs_read_packet(AVFormatContext * s, AVPacket * pkt) avs->st_video->codec->height = avs->height; avs->st_video->codec->bits_per_coded_sample=avs->bits_per_sample; avs->st_video->nb_frames = avs->nb_frames; +#if FF_API_R_FRAME_RATE + avs->st_video->r_frame_rate = +#endif avs->st_video->avg_frame_rate = (AVRational){avs->fps, 1}; } return avs_read_video_packet(s, pkt, type, sub_type, size, diff --git a/libavformat/bethsoftvid.c b/libavformat/bethsoftvid.c index 120e145a76..b8f08c1f87 100644 --- a/libavformat/bethsoftvid.c +++ b/libavformat/bethsoftvid.c @@ -2,20 +2,20 @@ * Bethsoft VID format Demuxer * Copyright (c) 2007 Nicholas Tung * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -189,7 +189,8 @@ static int read_frame(BVID_DemuxContext *vid, AVIOContext *pb, AVPacket *pkt, if (vid->palette) { uint8_t *pdata = av_packet_new_side_data(pkt, AV_PKT_DATA_PALETTE, BVID_PALETTE_SIZE); - memcpy(pdata, vid->palette, BVID_PALETTE_SIZE); + if (pdata) + memcpy(pdata, vid->palette, BVID_PALETTE_SIZE); av_freep(&vid->palette); } @@ -209,8 +210,8 @@ static int vid_read_packet(AVFormatContext *s, int audio_length; int ret_value; - if(vid->is_finished || pb->eof_reached) - return AVERROR(EIO); + if(vid->is_finished || url_feof(pb)) + return AVERROR_EOF; block_type = avio_r8(pb); switch(block_type){ diff --git a/libavformat/bfi.c b/libavformat/bfi.c index 19060e760f..b65a582e65 100644 --- a/libavformat/bfi.c +++ b/libavformat/bfi.c @@ -2,20 +2,20 @@ * Brute Force & Ignorance (BFI) demuxer * Copyright (c) 2008 Sisir Koppaka * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -81,6 +81,8 @@ static int bfi_read_header(AVFormatContext * s) /*Load the palette to extradata */ avio_skip(pb, 8); vstream->codec->extradata = av_malloc(768); + if (!vstream->codec->extradata) + return AVERROR(ENOMEM); vstream->codec->extradata_size = 768; avio_read(pb, vstream->codec->extradata, vstream->codec->extradata_size); @@ -92,6 +94,8 @@ static int bfi_read_header(AVFormatContext * s) vstream->codec->codec_type = AVMEDIA_TYPE_VIDEO; vstream->codec->codec_id = AV_CODEC_ID_BFI; vstream->codec->pix_fmt = AV_PIX_FMT_PAL8; + vstream->nb_frames = + vstream->duration = bfi->nframes; /* Set up the audio codec now... */ astream->codec->codec_type = AVMEDIA_TYPE_AUDIO; @@ -112,15 +116,15 @@ static int bfi_read_packet(AVFormatContext * s, AVPacket * pkt) BFIContext *bfi = s->priv_data; AVIOContext *pb = s->pb; int ret, audio_offset, video_offset, chunk_size, audio_size = 0; - if (bfi->nframes == 0 || pb->eof_reached) { - return AVERROR(EIO); + if (bfi->nframes == 0 || url_feof(pb)) { + return AVERROR_EOF; } /* If all previous chunks were completely read, then find a new one... */ if (!bfi->avflag) { uint32_t state = 0; while(state != MKTAG('S','A','V','I')){ - if (pb->eof_reached) + if (url_feof(pb)) return AVERROR(EIO); state = 256*state + avio_r8(pb); } diff --git a/libavformat/bink.c b/libavformat/bink.c index f093e7c3e2..6db73cce3a 100644 --- a/libavformat/bink.c +++ b/libavformat/bink.c @@ -3,20 +3,20 @@ * Copyright (c) 2008-2010 Peter Ross (pross@xvid.org) * Copyright (c) 2009 Daniel Verkamp (daniel@drv.nu) * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -116,10 +116,8 @@ static int read_header(AVFormatContext *s) vst->codec->codec_type = AVMEDIA_TYPE_VIDEO; vst->codec->codec_id = AV_CODEC_ID_BINKVIDEO; - vst->codec->extradata = av_mallocz(4 + FF_INPUT_BUFFER_PADDING_SIZE); - if (!vst->codec->extradata) + if (ff_alloc_extradata(vst->codec, 4)) return AVERROR(ENOMEM); - vst->codec->extradata_size = 4; avio_read(pb, vst->codec->extradata, 4); bink->num_audio_tracks = avio_rl32(pb); @@ -152,10 +150,8 @@ static int read_header(AVFormatContext *s) ast->codec->channels = 1; ast->codec->channel_layout = AV_CH_LAYOUT_MONO; } - ast->codec->extradata = av_mallocz(4 + FF_INPUT_BUFFER_PADDING_SIZE); - if (!ast->codec->extradata) + if (ff_alloc_extradata(ast->codec, 4)) return AVERROR(ENOMEM); - ast->codec->extradata_size = 4; AV_WL32(ast->codec->extradata, vst->codec->codec_tag); } @@ -202,7 +198,7 @@ static int read_packet(AVFormatContext *s, AVPacket *pkt) AVStream *st = s->streams[0]; // stream 0 is video stream with index if (bink->video_pts >= st->duration) - return AVERROR(EIO); + return AVERROR_EOF; index_entry = av_index_search_timestamp(st, bink->video_pts, AVSEEK_FLAG_ANY); diff --git a/libavformat/bintext.c b/libavformat/bintext.c new file mode 100644 index 0000000000..d50a8d9f34 --- /dev/null +++ b/libavformat/bintext.c @@ -0,0 +1,388 @@ +/* + * Binary text demuxer + * eXtended BINary text (XBIN) demuxer + * Artworx Data Format demuxer + * iCEDraw File demuxer + * Copyright (c) 2010 Peter Ross <pross@xvid.org> + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * Binary text demuxer + * eXtended BINary text (XBIN) demuxer + * Artworx Data Format demuxer + * iCEDraw File demuxer + */ + +#include "libavutil/intreadwrite.h" +#include "libavutil/opt.h" +#include "libavutil/parseutils.h" +#include "avformat.h" +#include "internal.h" +#include "sauce.h" +#include "libavcodec/bintext.h" + +typedef struct { + const AVClass *class; + int chars_per_frame; /**< characters to send decoder per frame; + set by private options as characters per second, and then + converted to characters per frame at runtime */ + int width, height; /**< video size (WxH pixels) (private option) */ + AVRational framerate; /**< frames per second (private option) */ + uint64_t fsize; /**< file size less metadata buffer */ +} BinDemuxContext; + +static AVStream * init_stream(AVFormatContext *s) +{ + BinDemuxContext *bin = s->priv_data; + AVStream *st = avformat_new_stream(s, NULL); + if (!st) + return NULL; + st->codec->codec_tag = 0; + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + + if (!bin->width) { + st->codec->width = (80<<3); + st->codec->height = (25<<4); + } + + avpriv_set_pts_info(st, 60, bin->framerate.den, bin->framerate.num); + + /* simulate tty display speed */ + bin->chars_per_frame = av_clip(av_q2d(st->time_base) * bin->chars_per_frame, 1, INT_MAX); + + return st; +} + +#if CONFIG_BINTEXT_DEMUXER | CONFIG_ADF_DEMUXER | CONFIG_IDF_DEMUXER +/** + * Given filesize and width, calculate height (assume font_height of 16) + */ +static void calculate_height(AVCodecContext *avctx, uint64_t fsize) +{ + avctx->height = (fsize / ((avctx->width>>3)*2)) << 4; +} +#endif + +#if CONFIG_BINTEXT_DEMUXER +static const uint8_t next_magic[]={ + 0x1A, 0x1B, '[', '0', ';', '3', '0', ';', '4', '0', 'm', 'N', 'E', 'X', 'T', 0x00 +}; + +static int next_tag_read(AVFormatContext *avctx, uint64_t *fsize) +{ + AVIOContext *pb = avctx->pb; + char buf[36]; + int len; + uint64_t start_pos = avio_size(pb) - 256; + + avio_seek(pb, start_pos, SEEK_SET); + if (avio_read(pb, buf, sizeof(next_magic)) != sizeof(next_magic)) + return -1; + if (memcmp(buf, next_magic, sizeof(next_magic))) + return -1; + if (avio_r8(pb) != 0x01) + return -1; + + *fsize -= 256; + +#define GET_EFI2_META(name,size) \ + len = avio_r8(pb); \ + if (len < 1 || len > size) \ + return -1; \ + if (avio_read(pb, buf, size) == size && *buf) { \ + buf[len] = 0; \ + av_dict_set(&avctx->metadata, name, buf, 0); \ + } + + GET_EFI2_META("filename", 12) + GET_EFI2_META("author", 20) + GET_EFI2_META("publisher", 20) + GET_EFI2_META("title", 35) + + return 0; +} + +static void predict_width(AVCodecContext *avctx, uint64_t fsize, int got_width) +{ + /** attempt to guess width */ + if (!got_width) + avctx->width = fsize > 4000 ? (160<<3) : (80<<3); +} + +static int bintext_read_header(AVFormatContext *s) +{ + BinDemuxContext *bin = s->priv_data; + AVIOContext *pb = s->pb; + + AVStream *st = init_stream(s); + if (!st) + return AVERROR(ENOMEM); + st->codec->codec_id = AV_CODEC_ID_BINTEXT; + + if (ff_alloc_extradata(st->codec, 2)) + return AVERROR(ENOMEM); + st->codec->extradata[0] = 16; + st->codec->extradata[1] = 0; + + if (pb->seekable) { + int got_width = 0; + bin->fsize = avio_size(pb); + if (ff_sauce_read(s, &bin->fsize, &got_width, 0) < 0) + next_tag_read(s, &bin->fsize); + if (!bin->width) { + predict_width(st->codec, bin->fsize, got_width); + calculate_height(st->codec, bin->fsize); + } + avio_seek(pb, 0, SEEK_SET); + } + return 0; +} +#endif /* CONFIG_BINTEXT_DEMUXER */ + +#if CONFIG_XBIN_DEMUXER +static int xbin_probe(AVProbeData *p) +{ + const uint8_t *d = p->buf; + + if (AV_RL32(d) == MKTAG('X','B','I','N') && d[4] == 0x1A && + AV_RL16(d+5) > 0 && AV_RL16(d+5) <= 160 && + d[9] > 0 && d[9] <= 32) + return AVPROBE_SCORE_MAX; + return 0; +} + +static int xbin_read_header(AVFormatContext *s) +{ + BinDemuxContext *bin = s->priv_data; + AVIOContext *pb = s->pb; + char fontheight, flags; + + AVStream *st = init_stream(s); + if (!st) + return AVERROR(ENOMEM); + + avio_skip(pb, 5); + st->codec->width = avio_rl16(pb)<<3; + st->codec->height = avio_rl16(pb); + fontheight = avio_r8(pb); + st->codec->height *= fontheight; + flags = avio_r8(pb); + + st->codec->extradata_size = 2; + if ((flags & BINTEXT_PALETTE)) + st->codec->extradata_size += 48; + if ((flags & BINTEXT_FONT)) + st->codec->extradata_size += fontheight * (flags & 0x10 ? 512 : 256); + st->codec->codec_id = flags & 4 ? AV_CODEC_ID_XBIN : AV_CODEC_ID_BINTEXT; + + if (ff_alloc_extradata(st->codec, st->codec->extradata_size)) + return AVERROR(ENOMEM); + st->codec->extradata[0] = fontheight; + st->codec->extradata[1] = flags; + if (avio_read(pb, st->codec->extradata + 2, st->codec->extradata_size - 2) < 0) + return AVERROR(EIO); + + if (pb->seekable) { + bin->fsize = avio_size(pb) - 9 - st->codec->extradata_size; + ff_sauce_read(s, &bin->fsize, NULL, 0); + avio_seek(pb, 9 + st->codec->extradata_size, SEEK_SET); + } + + return 0; +} +#endif /* CONFIG_XBIN_DEMUXER */ + +#if CONFIG_ADF_DEMUXER +static int adf_read_header(AVFormatContext *s) +{ + BinDemuxContext *bin = s->priv_data; + AVIOContext *pb = s->pb; + AVStream *st; + + if (avio_r8(pb) != 1) + return AVERROR_INVALIDDATA; + + st = init_stream(s); + if (!st) + return AVERROR(ENOMEM); + st->codec->codec_id = AV_CODEC_ID_BINTEXT; + + if (ff_alloc_extradata(st->codec, 2 + 48 + 4096)) + return AVERROR(ENOMEM); + st->codec->extradata[0] = 16; + st->codec->extradata[1] = BINTEXT_PALETTE|BINTEXT_FONT; + + if (avio_read(pb, st->codec->extradata + 2, 24) < 0) + return AVERROR(EIO); + avio_skip(pb, 144); + if (avio_read(pb, st->codec->extradata + 2 + 24, 24) < 0) + return AVERROR(EIO); + if (avio_read(pb, st->codec->extradata + 2 + 48, 4096) < 0) + return AVERROR(EIO); + + if (pb->seekable) { + int got_width = 0; + bin->fsize = avio_size(pb) - 1 - 192 - 4096; + st->codec->width = 80<<3; + ff_sauce_read(s, &bin->fsize, &got_width, 0); + if (!bin->width) + calculate_height(st->codec, bin->fsize); + avio_seek(pb, 1 + 192 + 4096, SEEK_SET); + } + return 0; +} +#endif /* CONFIG_ADF_DEMUXER */ + +#if CONFIG_IDF_DEMUXER +static const uint8_t idf_magic[] = { + 0x04, 0x31, 0x2e, 0x34, 0x00, 0x00, 0x00, 0x00, 0x4f, 0x00, 0x15, 0x00 +}; + +static int idf_probe(AVProbeData *p) +{ + if (p->buf_size < sizeof(idf_magic)) + return 0; + if (!memcmp(p->buf, idf_magic, sizeof(idf_magic))) + return AVPROBE_SCORE_MAX; + return 0; +} + +static int idf_read_header(AVFormatContext *s) +{ + BinDemuxContext *bin = s->priv_data; + AVIOContext *pb = s->pb; + AVStream *st; + int got_width = 0; + + if (!pb->seekable) + return AVERROR(EIO); + + st = init_stream(s); + if (!st) + return AVERROR(ENOMEM); + st->codec->codec_id = AV_CODEC_ID_IDF; + + if (ff_alloc_extradata(st->codec, 2 + 48 + 4096)) + return AVERROR(ENOMEM); + st->codec->extradata[0] = 16; + st->codec->extradata[1] = BINTEXT_PALETTE|BINTEXT_FONT; + + avio_seek(pb, avio_size(pb) - 4096 - 48, SEEK_SET); + + if (avio_read(pb, st->codec->extradata + 2 + 48, 4096) < 0) + return AVERROR(EIO); + if (avio_read(pb, st->codec->extradata + 2, 48) < 0) + return AVERROR(EIO); + + bin->fsize = avio_size(pb) - 12 - 4096 - 48; + ff_sauce_read(s, &bin->fsize, &got_width, 0); + if (!bin->width) + calculate_height(st->codec, bin->fsize); + avio_seek(pb, 12, SEEK_SET); + return 0; +} +#endif /* CONFIG_IDF_DEMUXER */ + +static int read_packet(AVFormatContext *s, + AVPacket *pkt) +{ + BinDemuxContext *bin = s->priv_data; + + if (bin->fsize > 0) { + if (av_get_packet(s->pb, pkt, bin->fsize) < 0) + return AVERROR(EIO); + bin->fsize = -1; /* done */ + } else if (!bin->fsize) { + if (url_feof(s->pb)) + return AVERROR(EIO); + if (av_get_packet(s->pb, pkt, bin->chars_per_frame) < 0) + return AVERROR(EIO); + } else { + return AVERROR(EIO); + } + + pkt->flags |= AV_PKT_FLAG_KEY; + return 0; +} + +#define OFFSET(x) offsetof(BinDemuxContext, x) +static const AVOption options[] = { + { "linespeed", "set simulated line speed (bytes per second)", OFFSET(chars_per_frame), AV_OPT_TYPE_INT, {.i64 = 6000}, 1, INT_MAX, AV_OPT_FLAG_DECODING_PARAM}, + { "video_size", "set video size, such as 640x480 or hd720.", OFFSET(width), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, AV_OPT_FLAG_DECODING_PARAM }, + { "framerate", "set framerate (frames per second)", OFFSET(framerate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, 0, AV_OPT_FLAG_DECODING_PARAM }, + { NULL }, +}; + +#define CLASS(name) \ +(const AVClass[1]){{ \ + .class_name = name, \ + .item_name = av_default_item_name, \ + .option = options, \ + .version = LIBAVUTIL_VERSION_INT, \ +}} + +#if CONFIG_BINTEXT_DEMUXER +AVInputFormat ff_bintext_demuxer = { + .name = "bin", + .long_name = NULL_IF_CONFIG_SMALL("Binary text"), + .priv_data_size = sizeof(BinDemuxContext), + .read_header = bintext_read_header, + .read_packet = read_packet, + .extensions = "bin", + .priv_class = CLASS("Binary text demuxer"), +}; +#endif + +#if CONFIG_XBIN_DEMUXER +AVInputFormat ff_xbin_demuxer = { + .name = "xbin", + .long_name = NULL_IF_CONFIG_SMALL("eXtended BINary text (XBIN)"), + .priv_data_size = sizeof(BinDemuxContext), + .read_probe = xbin_probe, + .read_header = xbin_read_header, + .read_packet = read_packet, + .priv_class = CLASS("eXtended BINary text (XBIN) demuxer"), +}; +#endif + +#if CONFIG_ADF_DEMUXER +AVInputFormat ff_adf_demuxer = { + .name = "adf", + .long_name = NULL_IF_CONFIG_SMALL("Artworx Data Format"), + .priv_data_size = sizeof(BinDemuxContext), + .read_header = adf_read_header, + .read_packet = read_packet, + .extensions = "adf", + .priv_class = CLASS("Artworx Data Format demuxer"), +}; +#endif + +#if CONFIG_IDF_DEMUXER +AVInputFormat ff_idf_demuxer = { + .name = "idf", + .long_name = NULL_IF_CONFIG_SMALL("iCE Draw File"), + .priv_data_size = sizeof(BinDemuxContext), + .read_probe = idf_probe, + .read_header = idf_read_header, + .read_packet = read_packet, + .extensions = "idf", + .priv_class = CLASS("iCE Draw File demuxer"), +}; +#endif diff --git a/libavformat/bit.c b/libavformat/bit.c new file mode 100644 index 0000000000..0be471ac4f --- /dev/null +++ b/libavformat/bit.c @@ -0,0 +1,156 @@ +/* + * G.729 bit format muxer and demuxer + * Copyright (c) 2007-2008 Vladimir Voroshilov + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include "avformat.h" +#include "internal.h" +#include "libavcodec/get_bits.h" +#include "libavcodec/put_bits.h" + +#define MAX_FRAME_SIZE 10 + +#define SYNC_WORD 0x6b21 +#define BIT_0 0x7f +#define BIT_1 0x81 + +static int probe(AVProbeData *p) +{ + int i, j; + + if(p->buf_size < 0x40) + return 0; + + for(i=0; i+3<p->buf_size && i< 10*0x50; ){ + if(AV_RL16(&p->buf[0]) != SYNC_WORD) + return 0; + j=AV_RL16(&p->buf[2]); + if(j!=0x40 && j!=0x50) + return 0; + i+=j; + } + return AVPROBE_SCORE_EXTENSION; +} + +static int read_header(AVFormatContext *s) +{ + AVStream* st; + + st=avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_id=AV_CODEC_ID_G729; + st->codec->sample_rate=8000; + st->codec->block_align = 16; + st->codec->channels=1; + + avpriv_set_pts_info(st, 64, 1, 100); + return 0; +} + +static int read_packet(AVFormatContext *s, + AVPacket *pkt) +{ + AVIOContext *pb = s->pb; + PutBitContext pbo; + uint16_t buf[8 * MAX_FRAME_SIZE + 2]; + int packet_size; + uint16_t* src=buf; + int i, j, ret; + int64_t pos= avio_tell(pb); + + if(url_feof(pb)) + return AVERROR_EOF; + + avio_rl16(pb); // sync word + packet_size = avio_rl16(pb) / 8; + if(packet_size > MAX_FRAME_SIZE) + return AVERROR_INVALIDDATA; + + ret = avio_read(pb, (uint8_t*)buf, (8 * packet_size) * sizeof(uint16_t)); + if(ret<0) + return ret; + if(ret != 8 * packet_size * sizeof(uint16_t)) + return AVERROR(EIO); + + if (av_new_packet(pkt, packet_size) < 0) + return AVERROR(ENOMEM); + + init_put_bits(&pbo, pkt->data, packet_size); + for(j=0; j < packet_size; j++) + for(i=0; i<8;i++) + put_bits(&pbo,1, AV_RL16(src++) == BIT_1 ? 1 : 0); + + flush_put_bits(&pbo); + + pkt->duration=1; + pkt->pos = pos; + return 0; +} + +AVInputFormat ff_bit_demuxer = { + .name = "bit", + .long_name = NULL_IF_CONFIG_SMALL("G.729 BIT file format"), + .read_probe = probe, + .read_header = read_header, + .read_packet = read_packet, + .extensions = "bit", +}; + +#if CONFIG_MUXERS +static int write_header(AVFormatContext *s) +{ + AVCodecContext *enc = s->streams[0]->codec; + + enc->codec_id = AV_CODEC_ID_G729; + enc->channels = 1; + enc->bits_per_coded_sample = 16; + enc->block_align = (enc->bits_per_coded_sample * enc->channels) >> 3; + + return 0; +} + +static int write_packet(AVFormatContext *s, AVPacket *pkt) +{ + AVIOContext *pb = s->pb; + GetBitContext gb; + int i; + + avio_wl16(pb, SYNC_WORD); + avio_wl16(pb, 8 * 10); + + init_get_bits(&gb, pkt->data, 8*10); + for(i=0; i< 8 * 10; i++) + avio_wl16(pb, get_bits1(&gb) ? BIT_1 : BIT_0); + + return 0; +} + +AVOutputFormat ff_bit_muxer = { + .name = "bit", + .long_name = NULL_IF_CONFIG_SMALL("G.729 BIT file format"), + .mime_type = "audio/bit", + .extensions = "bit", + .audio_codec = AV_CODEC_ID_G729, + .video_codec = AV_CODEC_ID_NONE, + .write_header = write_header, + .write_packet = write_packet, +}; +#endif diff --git a/libavformat/bluray.c b/libavformat/bluray.c new file mode 100644 index 0000000000..d2c57aab9c --- /dev/null +++ b/libavformat/bluray.c @@ -0,0 +1,235 @@ +/* + * BluRay (libbluray) protocol + * + * Copyright (c) 2012 Petri Hintukainen <phintuka <at> users.sourceforge.net> + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <libbluray/bluray.h> + +#include "libavutil/avstring.h" +#include "libavformat/avformat.h" +#include "libavformat/url.h" +#include "libavutil/opt.h" + +#define BLURAY_PROTO_PREFIX "bluray:" +#define MIN_PLAYLIST_LENGTH 180 /* 3 min */ + +typedef struct { + const AVClass *class; + + BLURAY *bd; + + int playlist; + int angle; + int chapter; + /*int region;*/ +} BlurayContext; + +#define OFFSET(x) offsetof(BlurayContext, x) +static const AVOption options[] = { +{"playlist", "", OFFSET(playlist), AV_OPT_TYPE_INT, { .i64=-1 }, -1, 99999, AV_OPT_FLAG_DECODING_PARAM }, +{"angle", "", OFFSET(angle), AV_OPT_TYPE_INT, { .i64=0 }, 0, 0xfe, AV_OPT_FLAG_DECODING_PARAM }, +{"chapter", "", OFFSET(chapter), AV_OPT_TYPE_INT, { .i64=1 }, 1, 0xfffe, AV_OPT_FLAG_DECODING_PARAM }, +/*{"region", "bluray player region code (1 = region A, 2 = region B, 4 = region C)", OFFSET(region), AV_OPT_TYPE_INT, { .i64=0 }, 0, 3, AV_OPT_FLAG_DECODING_PARAM },*/ +{NULL} +}; + +static const AVClass bluray_context_class = { + .class_name = "bluray", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + + +static int check_disc_info(URLContext *h) +{ + BlurayContext *bd = h->priv_data; + const BLURAY_DISC_INFO *disc_info; + + disc_info = bd_get_disc_info(bd->bd); + if (!disc_info) { + av_log(h, AV_LOG_ERROR, "bd_get_disc_info() failed\n"); + return -1; + } + + if (!disc_info->bluray_detected) { + av_log(h, AV_LOG_ERROR, "BluRay disc not detected\n"); + return -1; + } + + /* AACS */ + if (disc_info->aacs_detected && !disc_info->aacs_handled) { + if (!disc_info->libaacs_detected) { + av_log(h, AV_LOG_ERROR, + "Media stream encrypted with AACS, install and configure libaacs\n"); + } else { + av_log(h, AV_LOG_ERROR, "Your libaacs can't decrypt this media\n"); + } + return -1; + } + + /* BD+ */ + if (disc_info->bdplus_detected && !disc_info->bdplus_handled) { + /* + if (!disc_info->libbdplus_detected) { + av_log(h, AV_LOG_ERROR, + "Media stream encrypted with BD+, install and configure libbdplus"); + } else { + */ + av_log(h, AV_LOG_ERROR, "Unable to decrypt BD+ encrypted media\n"); + /*}*/ + return -1; + } + + return 0; +} + +static int bluray_close(URLContext *h) +{ + BlurayContext *bd = h->priv_data; + if (bd->bd) { + bd_close(bd->bd); + } + + return 0; +} + +static int bluray_open(URLContext *h, const char *path, int flags) +{ + BlurayContext *bd = h->priv_data; + int num_title_idx; + const char *diskname = path; + + av_strstart(path, BLURAY_PROTO_PREFIX, &diskname); + + bd->bd = bd_open(diskname, NULL); + if (!bd->bd) { + av_log(h, AV_LOG_ERROR, "bd_open() failed\n"); + return AVERROR(EIO); + } + + /* check if disc can be played */ + if (check_disc_info(h) < 0) { + return AVERROR(EIO); + } + + /* setup player registers */ + /* region code has no effect without menus + if (bd->region > 0 && bd->region < 5) { + av_log(h, AV_LOG_INFO, "setting region code to %d (%c)\n", bd->region, 'A' + (bd->region - 1)); + bd_set_player_setting(bd->bd, BLURAY_PLAYER_SETTING_REGION_CODE, bd->region); + } + */ + + /* load title list */ + num_title_idx = bd_get_titles(bd->bd, TITLES_RELEVANT, MIN_PLAYLIST_LENGTH); + av_log(h, AV_LOG_INFO, "%d usable playlists:\n", num_title_idx); + if (num_title_idx < 1) { + return AVERROR(EIO); + } + + /* if playlist was not given, select longest playlist */ + if (bd->playlist < 0) { + uint64_t duration = 0; + int i; + for (i = 0; i < num_title_idx; i++) { + BLURAY_TITLE_INFO *info = bd_get_title_info(bd->bd, i, 0); + + av_log(h, AV_LOG_INFO, "playlist %05d.mpls (%d:%02d:%02d)\n", + info->playlist, + ((int)(info->duration / 90000) / 3600), + ((int)(info->duration / 90000) % 3600) / 60, + ((int)(info->duration / 90000) % 60)); + + if (info->duration > duration) { + bd->playlist = info->playlist; + duration = info->duration; + } + + bd_free_title_info(info); + } + av_log(h, AV_LOG_INFO, "selected %05d.mpls\n", bd->playlist); + } + + /* select playlist */ + if (bd_select_playlist(bd->bd, bd->playlist) <= 0) { + av_log(h, AV_LOG_ERROR, "bd_select_playlist(%05d.mpls) failed\n", bd->playlist); + return AVERROR(EIO); + } + + /* select angle */ + if (bd->angle >= 0) { + bd_select_angle(bd->bd, bd->angle); + } + + /* select chapter */ + if (bd->chapter > 1) { + bd_seek_chapter(bd->bd, bd->chapter - 1); + } + + return 0; +} + +static int bluray_read(URLContext *h, unsigned char *buf, int size) +{ + BlurayContext *bd = h->priv_data; + int len; + + if (!bd || !bd->bd) { + return AVERROR(EFAULT); + } + + len = bd_read(bd->bd, buf, size); + + return len; +} + +static int64_t bluray_seek(URLContext *h, int64_t pos, int whence) +{ + BlurayContext *bd = h->priv_data; + + if (!bd || !bd->bd) { + return AVERROR(EFAULT); + } + + switch (whence) { + case SEEK_SET: + case SEEK_CUR: + case SEEK_END: + return bd_seek(bd->bd, pos); + + case AVSEEK_SIZE: + return bd_get_title_size(bd->bd); + } + + av_log(h, AV_LOG_ERROR, "Unsupported whence operation %d\n", whence); + return AVERROR(EINVAL); +} + + +URLProtocol ff_bluray_protocol = { + .name = "bluray", + .url_close = bluray_close, + .url_open = bluray_open, + .url_read = bluray_read, + .url_seek = bluray_seek, + .priv_data_size = sizeof(BlurayContext), + .priv_data_class = &bluray_context_class, +}; diff --git a/libavformat/bmv.c b/libavformat/bmv.c index b5572aff37..f7a6068af3 100644 --- a/libavformat/bmv.c +++ b/libavformat/bmv.c @@ -2,20 +2,20 @@ * Discworld II BMV demuxer * Copyright (c) 2011 Konstantin Shishkov. * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/boadec.c b/libavformat/boadec.c new file mode 100644 index 0000000000..45f6b3976b --- /dev/null +++ b/libavformat/boadec.c @@ -0,0 +1,78 @@ +/* + * Black ops audio demuxer + * Copyright (c) 2013 Michael Niedermayer + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/intreadwrite.h" +#include "avformat.h" + +static int probe(AVProbeData *p) +{ + if (p->buf_size < 2096) + return 0; + if ( AV_RL32(p->buf ) != 1 + || AV_RL32(p->buf + 8) > 100000 + || AV_RL32(p->buf + 12) > 8 + || AV_RL32(p->buf + 16) != 2096 + ||!AV_RL32(p->buf + 21) + || AV_RL16(p->buf + 25) != 2096 + || AV_RL32(p->buf + 48) % AV_RL32(p->buf + 21) + ) + return 0; + return AVPROBE_SCORE_EXTENSION; +} + + +static int read_header(AVFormatContext *s) +{ + AVStream *st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_id = AV_CODEC_ID_ADPCM_MS; + + avio_rl32(s->pb); + avio_rl32(s->pb); + st->codec->sample_rate = avio_rl32(s->pb); + st->codec->channels = avio_rl32(s->pb); + s->data_offset = avio_rl32(s->pb); + avio_r8(s->pb); + st->codec->block_align = st->codec->channels * avio_rl32(s->pb); + + avio_seek(s->pb, s->data_offset, SEEK_SET); + + return 0; +} + +static int read_packet(AVFormatContext *s, AVPacket *pkt) +{ + AVStream *st = s->streams[0]; + + return av_get_packet(s->pb, pkt, st->codec->block_align); +} + +AVInputFormat ff_boa_demuxer = { + .name = "boa", + .long_name = NULL_IF_CONFIG_SMALL("Black Ops Audio"), + .read_probe = probe, + .read_header = read_header, + .read_packet = read_packet, + .flags = AVFMT_GENERIC_INDEX, +}; diff --git a/libavformat/brstm.c b/libavformat/brstm.c new file mode 100644 index 0000000000..7781b3c559 --- /dev/null +++ b/libavformat/brstm.c @@ -0,0 +1,297 @@ +/* + * BRSTM demuxer + * Copyright (c) 2012 Paul B Mahol + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/intreadwrite.h" +#include "libavcodec/bytestream.h" +#include "avformat.h" +#include "internal.h" + +typedef struct BRSTMDemuxContext { + uint32_t block_size; + uint32_t block_count; + uint32_t current_block; + uint32_t samples_per_block; + uint32_t last_block_used_bytes; + uint8_t *table; + uint8_t *adpc; +} BRSTMDemuxContext; + +static int probe(AVProbeData *p) +{ + if (AV_RL32(p->buf) == MKTAG('R','S','T','M') && + (AV_RL16(p->buf + 4) == 0xFFFE || + AV_RL16(p->buf + 4) == 0xFEFF)) + return AVPROBE_SCORE_MAX / 3 * 2; + return 0; +} + +static int read_close(AVFormatContext *s) +{ + BRSTMDemuxContext *b = s->priv_data; + + av_freep(&b->table); + av_freep(&b->adpc); + + return 0; +} + +static int read_header(AVFormatContext *s) +{ + BRSTMDemuxContext *b = s->priv_data; + int bom, major, minor, codec, chunk; + int64_t pos, h1offset, toffset; + uint32_t size, start, asize; + AVStream *st; + int ret = AVERROR_EOF; + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + + avio_skip(s->pb, 4); + + bom = avio_rb16(s->pb); + if (bom != 0xFEFF && bom != 0xFFFE) { + av_log(s, AV_LOG_ERROR, "invalid byte order: %X\n", bom); + return AVERROR_INVALIDDATA; + } + if (bom == 0xFFFE) { + avpriv_request_sample(s, "little endian byte order"); + return AVERROR_PATCHWELCOME; + } + + major = avio_r8(s->pb); + minor = avio_r8(s->pb); + avio_skip(s->pb, 4); // size of file + size = avio_rb16(s->pb); + if (size < 14) + return AVERROR_INVALIDDATA; + + avio_skip(s->pb, size - 14); + pos = avio_tell(s->pb); + if (avio_rl32(s->pb) != MKTAG('H','E','A','D')) + return AVERROR_INVALIDDATA; + size = avio_rb32(s->pb); + if (size < 256) + return AVERROR_INVALIDDATA; + avio_skip(s->pb, 4); // unknown + h1offset = avio_rb32(s->pb); + if (h1offset > size) + return AVERROR_INVALIDDATA; + avio_skip(s->pb, 12); + toffset = avio_rb32(s->pb) + 16LL; + if (toffset > size) + return AVERROR_INVALIDDATA; + + avio_skip(s->pb, pos + h1offset + 8 - avio_tell(s->pb)); + codec = avio_r8(s->pb); + + switch (codec) { + case 0: codec = AV_CODEC_ID_PCM_S8_PLANAR; break; + case 1: codec = AV_CODEC_ID_PCM_S16BE_PLANAR; break; + case 2: codec = AV_CODEC_ID_ADPCM_THP; break; + default: + avpriv_request_sample(s, "codec %d", codec); + return AVERROR_PATCHWELCOME; + } + + avio_skip(s->pb, 1); // loop flag + st->codec->codec_id = codec; + st->codec->channels = avio_r8(s->pb); + if (!st->codec->channels) + return AVERROR_INVALIDDATA; + + avio_skip(s->pb, 1); // padding + st->codec->sample_rate = avio_rb16(s->pb); + if (!st->codec->sample_rate) + return AVERROR_INVALIDDATA; + + avio_skip(s->pb, 2); // padding + avio_skip(s->pb, 4); // loop start sample + st->start_time = 0; + st->duration = avio_rb32(s->pb); + avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate); + + start = avio_rb32(s->pb); + b->current_block = 0; + b->block_count = avio_rb32(s->pb); + if (b->block_count > UINT16_MAX) { + av_log(s, AV_LOG_WARNING, "too many blocks: %u\n", b->block_count); + return AVERROR_INVALIDDATA; + } + + b->block_size = avio_rb32(s->pb); + if (b->block_size > UINT16_MAX / st->codec->channels) + return AVERROR_INVALIDDATA; + b->block_size *= st->codec->channels; + + b->samples_per_block = avio_rb32(s->pb); + b->last_block_used_bytes = avio_rb32(s->pb); + if (b->last_block_used_bytes > UINT16_MAX / st->codec->channels) + return AVERROR_INVALIDDATA; + b->last_block_used_bytes *= st->codec->channels; + + avio_skip(s->pb, 4); // last block samples + avio_skip(s->pb, 4); // last block size + + if (codec == AV_CODEC_ID_ADPCM_THP) { + int ch; + + avio_skip(s->pb, pos + toffset - avio_tell(s->pb)); + toffset = avio_rb32(s->pb) + 16LL; + if (toffset > size) + return AVERROR_INVALIDDATA; + + avio_skip(s->pb, pos + toffset - avio_tell(s->pb)); + b->table = av_mallocz(32 * st->codec->channels); + if (!b->table) + return AVERROR(ENOMEM); + + for (ch = 0; ch < st->codec->channels; ch++) { + if (avio_read(s->pb, b->table + ch * 32, 32) != 32) { + ret = AVERROR_INVALIDDATA; + goto fail; + } + avio_skip(s->pb, 24); + } + } + + if (size < (avio_tell(s->pb) - pos)) { + ret = AVERROR_INVALIDDATA; + goto fail; + } + avio_skip(s->pb, size - (avio_tell(s->pb) - pos)); + + while (!url_feof(s->pb)) { + chunk = avio_rl32(s->pb); + size = avio_rb32(s->pb); + if (size < 8) { + ret = AVERROR_INVALIDDATA; + goto fail; + } + size -= 8; + switch (chunk) { + case MKTAG('A','D','P','C'): + if (codec != AV_CODEC_ID_ADPCM_THP) + goto skip; + + asize = b->block_count * st->codec->channels * 4; + if (size < asize) { + ret = AVERROR_INVALIDDATA; + goto fail; + } + if (b->adpc) { + av_log(s, AV_LOG_WARNING, "skipping additonal ADPC chunk\n"); + goto skip; + } else { + b->adpc = av_mallocz(asize); + if (!b->adpc) { + ret = AVERROR(ENOMEM); + goto fail; + } + avio_read(s->pb, b->adpc, asize); + avio_skip(s->pb, size - asize); + } + break; + case MKTAG('D','A','T','A'): + if ((start < avio_tell(s->pb)) || + (!b->adpc && codec == AV_CODEC_ID_ADPCM_THP)) { + ret = AVERROR_INVALIDDATA; + goto fail; + } + avio_skip(s->pb, start - avio_tell(s->pb)); + + if (major != 1 || minor) + avpriv_request_sample(s, "Version %d.%d", major, minor); + + return 0; + default: + av_log(s, AV_LOG_WARNING, "skipping unknown chunk: %X\n", chunk); +skip: + avio_skip(s->pb, size); + } + } + +fail: + read_close(s); + + return ret; +} + +static int read_packet(AVFormatContext *s, AVPacket *pkt) +{ + AVCodecContext *codec = s->streams[0]->codec; + BRSTMDemuxContext *b = s->priv_data; + uint32_t samples, size; + int ret; + + if (url_feof(s->pb)) + return AVERROR_EOF; + b->current_block++; + if (b->current_block == b->block_count) { + size = b->last_block_used_bytes; + samples = size / (8 * codec->channels) * 14; + } else if (b->current_block < b->block_count) { + size = b->block_size; + samples = b->samples_per_block; + } else { + return AVERROR_EOF; + } + + if (codec->codec_id == AV_CODEC_ID_ADPCM_THP) { + uint8_t *dst; + + if (av_new_packet(pkt, 8 + (32 + 4) * codec->channels + size) < 0) + return AVERROR(ENOMEM); + dst = pkt->data; + bytestream_put_be32(&dst, size); + bytestream_put_be32(&dst, samples); + bytestream_put_buffer(&dst, b->table, 32 * codec->channels); + bytestream_put_buffer(&dst, b->adpc + 4 * codec->channels * + (b->current_block - 1), 4 * codec->channels); + + ret = avio_read(s->pb, dst, size); + if (ret != size) + av_free_packet(pkt); + pkt->duration = samples; + } else { + ret = av_get_packet(s->pb, pkt, size); + } + + pkt->stream_index = 0; + + if (ret != size) + ret = AVERROR(EIO); + + return ret; +} + +AVInputFormat ff_brstm_demuxer = { + .name = "brstm", + .long_name = NULL_IF_CONFIG_SMALL("BRSTM (Binary Revolution Stream)"), + .priv_data_size = sizeof(BRSTMDemuxContext), + .read_probe = probe, + .read_header = read_header, + .read_packet = read_packet, + .read_close = read_close, + .extensions = "brstm", +}; diff --git a/libavformat/c93.c b/libavformat/c93.c index 3f2a98fcc4..93a61be838 100644 --- a/libavformat/c93.c +++ b/libavformat/c93.c @@ -2,20 +2,20 @@ * Interplay C93 demuxer * Copyright (c) 2007 Anssi Hannula <anssi.hannula@gmail.com> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -133,7 +133,7 @@ static int read_packet(AVFormatContext *s, AVPacket *pkt) } if (c93->current_frame >= br->frames) { if (c93->current_block >= 511 || !br[1].length) - return AVERROR(EIO); + return AVERROR_EOF; br++; c93->current_block++; c93->current_frame = 0; diff --git a/libavformat/cache.c b/libavformat/cache.c new file mode 100644 index 0000000000..35f81e83c1 --- /dev/null +++ b/libavformat/cache.c @@ -0,0 +1,140 @@ +/* + * Input cache protocol. + * Copyright (c) 2011 Michael Niedermayer + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Based on file.c by Fabrice Bellard + */ + +/** + * @TODO + * support non continuous caching + * support keeping files + * support filling with a background thread + */ + +#include "libavutil/avassert.h" +#include "libavutil/avstring.h" +#include "libavutil/file.h" +#include "avformat.h" +#include <fcntl.h> +#if HAVE_IO_H +#include <io.h> +#endif +#if HAVE_UNISTD_H +#include <unistd.h> +#endif +#include <sys/stat.h> +#include <stdlib.h> +#include "os_support.h" +#include "url.h" + +typedef struct Context { + int fd; + int64_t end; + int64_t pos; + URLContext *inner; +} Context; + +static int cache_open(URLContext *h, const char *arg, int flags) +{ + char *buffername; + Context *c= h->priv_data; + + av_strstart(arg, "cache:", &arg); + + c->fd = av_tempfile("ffcache", &buffername, 0, h); + if (c->fd < 0){ + av_log(h, AV_LOG_ERROR, "Failed to create tempfile\n"); + return c->fd; + } + + unlink(buffername); + av_freep(&buffername); + + return ffurl_open(&c->inner, arg, flags, &h->interrupt_callback, NULL); +} + +static int cache_read(URLContext *h, unsigned char *buf, int size) +{ + Context *c= h->priv_data; + int r; + + if(c->pos<c->end){ + r = read(c->fd, buf, FFMIN(size, c->end - c->pos)); + if(r>0) + c->pos += r; + return (-1 == r)?AVERROR(errno):r; + }else{ + r = ffurl_read(c->inner, buf, size); + if(r > 0){ + int r2= write(c->fd, buf, r); + av_assert0(r2==r); // FIXME handle cache failure + c->pos += r; + c->end += r; + } + return r; + } +} + +static int64_t cache_seek(URLContext *h, int64_t pos, int whence) +{ + Context *c= h->priv_data; + + if (whence == AVSEEK_SIZE) { + pos= ffurl_seek(c->inner, pos, whence); + if(pos <= 0){ + pos= ffurl_seek(c->inner, -1, SEEK_END); + ffurl_seek(c->inner, c->end, SEEK_SET); + if(pos <= 0) + return c->end; + } + return pos; + } + + pos= lseek(c->fd, pos, whence); + if(pos<0){ + return pos; + }else if(pos <= c->end){ + c->pos= pos; + return pos; + }else{ + if(lseek(c->fd, c->pos, SEEK_SET) < 0) { + av_log(h, AV_LOG_ERROR, "Failure to seek in cache\n"); + } + return AVERROR(EPIPE); + } +} + +static int cache_close(URLContext *h) +{ + Context *c= h->priv_data; + close(c->fd); + ffurl_close(c->inner); + + return 0; +} + +URLProtocol ff_cache_protocol = { + .name = "cache", + .url_open = cache_open, + .url_read = cache_read, + .url_seek = cache_seek, + .url_close = cache_close, + .priv_data_size = sizeof(Context), +}; diff --git a/libavformat/caf.c b/libavformat/caf.c index cf128d5624..c1ecc94425 100644 --- a/libavformat/caf.c +++ b/libavformat/caf.c @@ -2,20 +2,20 @@ * CAF common code * Copyright (c) 2007 Justin Ruggles * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -32,27 +32,35 @@ * Known codec tags for CAF */ const AVCodecTag ff_codec_caf_tags[] = { - { AV_CODEC_ID_AAC, MKBETAG('a','a','c',' ') }, - { AV_CODEC_ID_AC3, MKBETAG('a','c','-','3') }, - { AV_CODEC_ID_ALAC, MKBETAG('a','l','a','c') }, + { AV_CODEC_ID_AAC, MKTAG('a','a','c',' ') }, + { AV_CODEC_ID_AC3, MKTAG('a','c','-','3') }, + { AV_CODEC_ID_ADPCM_IMA_QT, MKTAG('i','m','a','4') }, + { AV_CODEC_ID_ADPCM_IMA_WAV, MKTAG('m','s', 0, 17 ) }, + { AV_CODEC_ID_ADPCM_MS, MKTAG('m','s', 0, 2 ) }, + { AV_CODEC_ID_ALAC, MKTAG('a','l','a','c') }, + { AV_CODEC_ID_AMR_NB, MKTAG('s','a','m','r') }, /* FIXME: use DV demuxer, as done in MOV */ - /*{ AV_CODEC_ID_DVAUDIO, MKBETAG('v','d','v','a') },*/ - /*{ AV_CODEC_ID_DVAUDIO, MKBETAG('d','v','c','a') },*/ - { AV_CODEC_ID_ADPCM_IMA_QT, MKBETAG('i','m','a','4') }, - { AV_CODEC_ID_MACE3, MKBETAG('M','A','C','3') }, - { AV_CODEC_ID_MACE6, MKBETAG('M','A','C','6') }, - { AV_CODEC_ID_MP3, MKBETAG('.','m','p','3') }, - { AV_CODEC_ID_MP2, MKBETAG('.','m','p','2') }, - { AV_CODEC_ID_MP1, MKBETAG('.','m','p','1') }, - { AV_CODEC_ID_PCM_ALAW, MKBETAG('a','l','a','w') }, - { AV_CODEC_ID_PCM_MULAW, MKBETAG('u','l','a','w') }, - { AV_CODEC_ID_QCELP, MKBETAG('Q','c','l','p') }, - { AV_CODEC_ID_QDM2, MKBETAG('Q','D','M','2') }, - { AV_CODEC_ID_QDM2, MKBETAG('Q','D','M','C') }, + /*{ AV_CODEC_ID_DVAUDIO, MKTAG('v','d','v','a') },*/ + /*{ AV_CODEC_ID_DVAUDIO, MKTAG('d','v','c','a') },*/ + { AV_CODEC_ID_GSM, MKTAG('a','g','s','m') }, + { AV_CODEC_ID_GSM_MS, MKTAG('m','s', 0, '1') }, + { AV_CODEC_ID_ILBC, MKTAG('i','l','b','c') }, + { AV_CODEC_ID_MACE3, MKTAG('M','A','C','3') }, + { AV_CODEC_ID_MACE6, MKTAG('M','A','C','6') }, + { AV_CODEC_ID_MP1, MKTAG('.','m','p','1') }, + { AV_CODEC_ID_MP2, MKTAG('.','m','p','2') }, + { AV_CODEC_ID_MP3, MKTAG('.','m','p','3') }, + { AV_CODEC_ID_MP3, MKTAG('m','s', 0 ,'U') }, + { AV_CODEC_ID_PCM_ALAW, MKTAG('a','l','a','w') }, + { AV_CODEC_ID_PCM_MULAW, MKTAG('u','l','a','w') }, + { AV_CODEC_ID_QCELP, MKTAG('Q','c','l','p') }, + { AV_CODEC_ID_QDM2, MKTAG('Q','D','M','2') }, + { AV_CODEC_ID_QDM2, MKTAG('Q','D','M','C') }, /* currently unsupported codecs */ - /*{ AC-3 over S/PDIF MKBETAG('c','a','c','3') },*/ - /*{ MPEG4CELP MKBETAG('c','e','l','p') },*/ - /*{ MPEG4HVXC MKBETAG('h','v','x','c') },*/ - /*{ MPEG4TwinVQ MKBETAG('t','w','v','q') },*/ + /*{ AC-3 over S/PDIF MKTAG('c','a','c','3') },*/ + /*{ MPEG4CELP MKTAG('c','e','l','p') },*/ + /*{ MPEG4HVXC MKTAG('h','v','x','c') },*/ + /*{ MPEG4TwinVQ MKTAG('t','w','v','q') },*/ { AV_CODEC_ID_NONE, 0 }, }; + diff --git a/libavformat/caf.h b/libavformat/caf.h index 7ca4dc5c66..9c25f2c683 100644 --- a/libavformat/caf.h +++ b/libavformat/caf.h @@ -2,20 +2,20 @@ * CAF common code * Copyright (c) 2007 Justin Ruggles * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/cafdec.c b/libavformat/cafdec.c index 6950eb2288..e954c65683 100644 --- a/libavformat/cafdec.c +++ b/libavformat/cafdec.c @@ -3,20 +3,20 @@ * Copyright (c) 2007 Justin Ruggles * Copyright (c) 2009 Peter Ross * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -69,7 +69,7 @@ static int read_desc_chunk(AVFormatContext *s) /* parse format description */ st->codec->codec_type = AVMEDIA_TYPE_AUDIO; st->codec->sample_rate = av_int2double(avio_rb64(pb)); - st->codec->codec_tag = avio_rb32(pb); + st->codec->codec_tag = avio_rl32(pb); flags = avio_rb32(pb); caf->bytes_per_packet = avio_rb32(pb); st->codec->block_align = caf->bytes_per_packet; @@ -86,7 +86,7 @@ static int read_desc_chunk(AVFormatContext *s) } /* determine codec */ - if (st->codec->codec_tag == MKBETAG('l','p','c','m')) + if (st->codec->codec_tag == MKTAG('l','p','c','m')) st->codec->codec_id = ff_mov_get_lpcm_codec_id(st->codec->bits_per_coded_sample, (flags ^ 0x2) | 0x4); else st->codec->codec_id = ff_codec_get_id(ff_codec_caf_tags, st->codec->codec_tag); @@ -130,8 +130,7 @@ static int read_kuki_chunk(AVFormatContext *s, int64_t size) } avio_read(pb, preamble, ALAC_PREAMBLE); - st->codec->extradata = av_mallocz(ALAC_HEADER + FF_INPUT_BUFFER_PADDING_SIZE); - if (!st->codec->extradata) + if (ff_alloc_extradata(st->codec, ALAC_HEADER)) return AVERROR(ENOMEM); /* For the old style cookie, we skip 12 bytes, then read 36 bytes. @@ -154,13 +153,10 @@ static int read_kuki_chunk(AVFormatContext *s, int64_t size) avio_read(pb, &st->codec->extradata[24], ALAC_NEW_KUKI - 12); avio_skip(pb, size - ALAC_NEW_KUKI); } - st->codec->extradata_size = ALAC_HEADER; } else { - st->codec->extradata = av_mallocz(size + FF_INPUT_BUFFER_PADDING_SIZE); - if (!st->codec->extradata) + if (ff_alloc_extradata(st->codec, size)) return AVERROR(ENOMEM); avio_read(pb, st->codec->extradata, size); - st->codec->extradata_size = size; } return 0; @@ -211,7 +207,7 @@ static void read_info_chunk(AVFormatContext *s, int64_t size) for (i = 0; i < nb_entries; i++) { char key[32]; char value[1024]; - avio_get_str(pb, INT_MAX, key, sizeof(key)); + avio_get_str(pb, INT_MAX, key, sizeof(key)); avio_get_str(pb, INT_MAX, value, sizeof(value)); av_dict_set(&s->metadata, key, value, 0); } @@ -224,7 +220,7 @@ static int read_header(AVFormatContext *s) AVStream *st; uint32_t tag = 0; int found_data, ret; - int64_t size; + int64_t size, pos; avio_skip(pb, 8); /* magic, version, file flags */ @@ -244,7 +240,7 @@ static int read_header(AVFormatContext *s) /* parse each chunk */ found_data = 0; - while (!pb->eof_reached) { + while (!url_feof(pb)) { /* stop at data chunk if seeking is not supported or data chunk size is unknown */ @@ -253,7 +249,8 @@ static int read_header(AVFormatContext *s) tag = avio_rb32(pb); size = avio_rb64(pb); - if (pb->eof_reached) + pos = avio_tell(pb); + if (url_feof(pb)) break; switch (tag) { @@ -289,15 +286,20 @@ static int read_header(AVFormatContext *s) default: #define _(x) ((x) >= ' ' ? (x) : ' ') - av_log(s, AV_LOG_WARNING, "skipping CAF chunk: %08X (%c%c%c%c)\n", - tag, _(tag>>24), _((tag>>16)&0xFF), _((tag>>8)&0xFF), _(tag&0xFF)); + av_log(s, AV_LOG_WARNING, "skipping CAF chunk: %08X (%c%c%c%c), size %"PRId64"\n", + tag, _(tag>>24), _((tag>>16)&0xFF), _((tag>>8)&0xFF), _(tag&0xFF), size); #undef _ case MKBETAG('f','r','e','e'): if (size < 0) return AVERROR_INVALIDDATA; - avio_skip(pb, size); break; } + + if (size > 0) { + if (pos > INT64_MAX - size) + return AVERROR_INVALIDDATA; + avio_skip(pb, FFMAX(0, pos + size - avio_tell(pb))); + } } if (!found_data) @@ -306,7 +308,7 @@ static int read_header(AVFormatContext *s) if (caf->bytes_per_packet > 0 && caf->frames_per_packet > 0) { if (caf->data_size > 0) st->nb_frames = (caf->data_size / caf->bytes_per_packet) * caf->frames_per_packet; - } else if (st->nb_index_entries) { + } else if (st->nb_index_entries && st->duration > 0) { st->codec->bit_rate = st->codec->sample_rate * caf->data_size * 8 / st->duration; } else { @@ -335,13 +337,15 @@ static int read_packet(AVFormatContext *s, AVPacket *pkt) int res, pkt_size = 0, pkt_frames = 0; int64_t left = CAF_MAX_PKT_SIZE; - if (pb->eof_reached) - return AVERROR(EIO); + if (url_feof(pb)) + return AVERROR_EOF; /* don't read past end of data chunk */ if (caf->data_size > 0) { left = (caf->data_start + caf->data_size) - avio_tell(pb); - if (left <= 0) + if (!left) + return AVERROR_EOF; + if (left < 0) return AVERROR(EIO); } @@ -392,7 +396,7 @@ static int read_seek(AVFormatContext *s, int stream_index, if (caf->frames_per_packet > 0 && caf->bytes_per_packet > 0) { /* calculate new byte position based on target frame position */ - pos = caf->bytes_per_packet * timestamp / caf->frames_per_packet; + pos = caf->bytes_per_packet * (timestamp / caf->frames_per_packet); if (caf->data_size > 0) pos = FFMIN(pos, caf->data_size); packet_cnt = pos / caf->bytes_per_packet; diff --git a/libavformat/cafenc.c b/libavformat/cafenc.c new file mode 100644 index 0000000000..cd3a0be907 --- /dev/null +++ b/libavformat/cafenc.c @@ -0,0 +1,280 @@ +/* + * Core Audio Format muxer + * Copyright (c) 2011 Carl Eugen Hoyos + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "avformat.h" +#include "caf.h" +#include "isom.h" +#include "avio_internal.h" +#include "libavutil/intfloat.h" +#include "libavutil/dict.h" + +typedef struct { + int64_t data; + uint8_t *pkt_sizes; + int size_buffer_size; + int size_entries_used; + int packets; +} CAFContext; + +static uint32_t codec_flags(enum AVCodecID codec_id) { + switch (codec_id) { + case AV_CODEC_ID_PCM_F32BE: + case AV_CODEC_ID_PCM_F64BE: + return 1; //< kCAFLinearPCMFormatFlagIsFloat + case AV_CODEC_ID_PCM_S16LE: + case AV_CODEC_ID_PCM_S24LE: + case AV_CODEC_ID_PCM_S32LE: + return 2; //< kCAFLinearPCMFormatFlagIsLittleEndian + case AV_CODEC_ID_PCM_F32LE: + case AV_CODEC_ID_PCM_F64LE: + return 3; //< kCAFLinearPCMFormatFlagIsFloat | kCAFLinearPCMFormatFlagIsLittleEndian + default: + return 0; + } +} + +static uint32_t samples_per_packet(enum AVCodecID codec_id, int channels) { + switch (codec_id) { + case AV_CODEC_ID_PCM_S8: + case AV_CODEC_ID_PCM_S16LE: + case AV_CODEC_ID_PCM_S16BE: + case AV_CODEC_ID_PCM_S24LE: + case AV_CODEC_ID_PCM_S24BE: + case AV_CODEC_ID_PCM_S32LE: + case AV_CODEC_ID_PCM_S32BE: + case AV_CODEC_ID_PCM_F32LE: + case AV_CODEC_ID_PCM_F32BE: + case AV_CODEC_ID_PCM_F64LE: + case AV_CODEC_ID_PCM_F64BE: + case AV_CODEC_ID_PCM_ALAW: + case AV_CODEC_ID_PCM_MULAW: + return 1; + case AV_CODEC_ID_MACE3: + case AV_CODEC_ID_MACE6: + return 6; + case AV_CODEC_ID_ADPCM_IMA_QT: + return 64; + case AV_CODEC_ID_AMR_NB: + case AV_CODEC_ID_GSM: + case AV_CODEC_ID_ILBC: + case AV_CODEC_ID_QCELP: + return 160; + case AV_CODEC_ID_GSM_MS: + return 320; + case AV_CODEC_ID_MP1: + return 384; + case AV_CODEC_ID_MP2: + case AV_CODEC_ID_MP3: + return 1152; + case AV_CODEC_ID_AC3: + return 1536; + case AV_CODEC_ID_QDM2: + return 2048 * channels; + case AV_CODEC_ID_ALAC: + return 4096; + case AV_CODEC_ID_ADPCM_IMA_WAV: + return (1024 - 4 * channels) * 8 / (4 * channels) + 1; + case AV_CODEC_ID_ADPCM_MS: + return (1024 - 7 * channels) * 2 / channels + 2; + default: + return 0; + } +} + +static int caf_write_header(AVFormatContext *s) +{ + AVIOContext *pb = s->pb; + AVCodecContext *enc = s->streams[0]->codec; + CAFContext *caf = s->priv_data; + AVDictionaryEntry *t = NULL; + unsigned int codec_tag = ff_codec_get_tag(ff_codec_caf_tags, enc->codec_id); + int64_t chunk_size = 0; + + switch (enc->codec_id) { + case AV_CODEC_ID_AAC: + case AV_CODEC_ID_AC3: + av_log(s, AV_LOG_ERROR, "muxing codec currently unsupported\n"); + return AVERROR_PATCHWELCOME; + } + + switch (enc->codec_id) { + case AV_CODEC_ID_PCM_S8: + case AV_CODEC_ID_PCM_S16LE: + case AV_CODEC_ID_PCM_S16BE: + case AV_CODEC_ID_PCM_S24LE: + case AV_CODEC_ID_PCM_S24BE: + case AV_CODEC_ID_PCM_S32LE: + case AV_CODEC_ID_PCM_S32BE: + case AV_CODEC_ID_PCM_F32LE: + case AV_CODEC_ID_PCM_F32BE: + case AV_CODEC_ID_PCM_F64LE: + case AV_CODEC_ID_PCM_F64BE: + case AV_CODEC_ID_PCM_ALAW: + case AV_CODEC_ID_PCM_MULAW: + codec_tag = MKTAG('l','p','c','m'); + } + + if (!codec_tag) { + av_log(s, AV_LOG_ERROR, "unsupported codec\n"); + return AVERROR_INVALIDDATA; + } + + if (!enc->block_align && !pb->seekable) { + av_log(s, AV_LOG_ERROR, "Muxing variable packet size not supported on non seekable output\n"); + return AVERROR_INVALIDDATA; + } + + ffio_wfourcc(pb, "caff"); //< mFileType + avio_wb16(pb, 1); //< mFileVersion + avio_wb16(pb, 0); //< mFileFlags + + ffio_wfourcc(pb, "desc"); //< Audio Description chunk + avio_wb64(pb, 32); //< mChunkSize + avio_wb64(pb, av_double2int(enc->sample_rate)); //< mSampleRate + avio_wl32(pb, codec_tag); //< mFormatID + avio_wb32(pb, codec_flags(enc->codec_id)); //< mFormatFlags + avio_wb32(pb, enc->block_align); //< mBytesPerPacket + avio_wb32(pb, samples_per_packet(enc->codec_id, enc->channels)); //< mFramesPerPacket + avio_wb32(pb, enc->channels); //< mChannelsPerFrame + avio_wb32(pb, av_get_bits_per_sample(enc->codec_id)); //< mBitsPerChannel + + if (enc->channel_layout) { + ffio_wfourcc(pb, "chan"); + avio_wb64(pb, 12); + ff_mov_write_chan(pb, enc->channel_layout); + } + + if (enc->codec_id == AV_CODEC_ID_ALAC) { + ffio_wfourcc(pb, "kuki"); + avio_wb64(pb, 12 + enc->extradata_size); + avio_write(pb, "\0\0\0\14frmaalac", 12); + avio_write(pb, enc->extradata, enc->extradata_size); + } else if (enc->codec_id == AV_CODEC_ID_AMR_NB) { + ffio_wfourcc(pb, "kuki"); + avio_wb64(pb, 29); + avio_write(pb, "\0\0\0\14frmasamr", 12); + avio_wb32(pb, 0x11); /* size */ + avio_write(pb, "samrFFMP", 8); + avio_w8(pb, 0); /* decoder version */ + + avio_wb16(pb, 0x81FF); /* Mode set (all modes for AMR_NB) */ + avio_w8(pb, 0x00); /* Mode change period (no restriction) */ + avio_w8(pb, 0x01); /* Frames per sample */ + } else if (enc->codec_id == AV_CODEC_ID_QDM2) { + ffio_wfourcc(pb, "kuki"); + avio_wb64(pb, enc->extradata_size); + avio_write(pb, enc->extradata, enc->extradata_size); + } + + if (av_dict_count(s->metadata)) { + ffio_wfourcc(pb, "info"); //< Information chunk + while ((t = av_dict_get(s->metadata, "", t, AV_DICT_IGNORE_SUFFIX))) { + chunk_size += strlen(t->key) + strlen(t->value) + 2; + } + avio_wb64(pb, chunk_size + 4); + avio_wb32(pb, av_dict_count(s->metadata)); + t = NULL; + while ((t = av_dict_get(s->metadata, "", t, AV_DICT_IGNORE_SUFFIX))) { + avio_put_str(pb, t->key); + avio_put_str(pb, t->value); + } + } + + ffio_wfourcc(pb, "data"); //< Audio Data chunk + caf->data = avio_tell(pb); + avio_wb64(pb, -1); //< mChunkSize + avio_wb32(pb, 0); //< mEditCount + + avio_flush(pb); + return 0; +} + +static int caf_write_packet(AVFormatContext *s, AVPacket *pkt) +{ + CAFContext *caf = s->priv_data; + + avio_write(s->pb, pkt->data, pkt->size); + if (!s->streams[0]->codec->block_align) { + void *pkt_sizes = caf->pkt_sizes; + int i, alloc_size = caf->size_entries_used + 5; + if (alloc_size < 0) { + caf->pkt_sizes = NULL; + } else { + caf->pkt_sizes = av_fast_realloc(caf->pkt_sizes, + &caf->size_buffer_size, + alloc_size); + } + if (!caf->pkt_sizes) { + av_free(pkt_sizes); + return AVERROR(ENOMEM); + } + for (i = 4; i > 0; i--) { + unsigned top = pkt->size >> i * 7; + if (top) + caf->pkt_sizes[caf->size_entries_used++] = 128 | top; + } + caf->pkt_sizes[caf->size_entries_used++] = pkt->size & 127; + caf->packets++; + } + return 0; +} + +static int caf_write_trailer(AVFormatContext *s) +{ + CAFContext *caf = s->priv_data; + AVIOContext *pb = s->pb; + AVCodecContext *enc = s->streams[0]->codec; + + if (pb->seekable) { + int64_t file_size = avio_tell(pb); + + avio_seek(pb, caf->data, SEEK_SET); + avio_wb64(pb, file_size - caf->data - 8); + avio_seek(pb, file_size, SEEK_SET); + if (!enc->block_align) { + ffio_wfourcc(pb, "pakt"); + avio_wb64(pb, caf->size_entries_used + 24); + avio_wb64(pb, caf->packets); ///< mNumberPackets + avio_wb64(pb, caf->packets * samples_per_packet(enc->codec_id, enc->channels)); ///< mNumberValidFrames + avio_wb32(pb, 0); ///< mPrimingFrames + avio_wb32(pb, 0); ///< mRemainderFrames + avio_write(pb, caf->pkt_sizes, caf->size_entries_used); + caf->size_buffer_size = 0; + } + avio_flush(pb); + } + av_freep(&caf->pkt_sizes); + return 0; +} + +AVOutputFormat ff_caf_muxer = { + .name = "caf", + .long_name = NULL_IF_CONFIG_SMALL("Apple CAF (Core Audio Format)"), + .mime_type = "audio/x-caf", + .extensions = "caf", + .priv_data_size = sizeof(CAFContext), + .audio_codec = AV_CODEC_ID_PCM_S16BE, + .video_codec = AV_CODEC_ID_NONE, + .write_header = caf_write_header, + .write_packet = caf_write_packet, + .write_trailer = caf_write_trailer, + .codec_tag = (const AVCodecTag* const []){ff_codec_caf_tags, 0}, +}; diff --git a/libavformat/cavsvideodec.c b/libavformat/cavsvideodec.c index fcca9e18b3..5ca3c80b32 100644 --- a/libavformat/cavsvideodec.c +++ b/libavformat/cavsvideodec.c @@ -2,20 +2,20 @@ * RAW Chinese AVS video demuxer * Copyright (c) 2009 Stefan Gehrer <stefan.gehrer@gmx.de> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/cdg.c b/libavformat/cdg.c index 974880a630..2d40d3dc55 100644 --- a/libavformat/cdg.c +++ b/libavformat/cdg.c @@ -2,20 +2,20 @@ * CD Graphics Demuxer * Copyright (c) 2009 Michael Tison * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -60,6 +60,12 @@ static int read_packet(AVFormatContext *s, AVPacket *pkt) } pkt->stream_index = 0; + pkt->dts= + pkt->pts= pkt->pos / CDG_PACKET_SIZE; + + if(ret>5 && (pkt->data[0]&0x3F) == 9 && (pkt->data[1]&0x3F)==1 && !(pkt->data[2+2+1] & 0x0F)){ + pkt->flags = AV_PKT_FLAG_KEY; + } return ret; } @@ -68,5 +74,6 @@ AVInputFormat ff_cdg_demuxer = { .long_name = NULL_IF_CONFIG_SMALL("CD Graphics"), .read_header = read_header, .read_packet = read_packet, + .flags = AVFMT_GENERIC_INDEX, .extensions = "cdg", }; diff --git a/libavformat/cdxl.c b/libavformat/cdxl.c index a2cba526ca..ab8a846cc5 100644 --- a/libavformat/cdxl.c +++ b/libavformat/cdxl.c @@ -2,20 +2,20 @@ * CDXL demuxer * Copyright (c) 2011-2012 Paul B Mahol * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -39,6 +39,48 @@ typedef struct CDXLDemuxContext { int audio_stream_index; } CDXLDemuxContext; +static int cdxl_read_probe(AVProbeData *p) +{ + int score = AVPROBE_SCORE_EXTENSION + 10; + + if (p->buf_size < CDXL_HEADER_SIZE) + return 0; + + /* reserved bytes should always be set to 0 */ + if (AV_RN64(&p->buf[24]) || AV_RN16(&p->buf[10])) + return 0; + + /* check type */ + if (p->buf[0] != 1) + return 0; + + /* check palette size */ + if (AV_RB16(&p->buf[20]) > 512) + return 0; + + /* check number of planes */ + if (p->buf[18] || !p->buf[19]) + return 0; + + /* check widh and height */ + if (!AV_RN16(&p->buf[14]) || !AV_RN16(&p->buf[16])) + return 0; + + /* chunk size */ + if (AV_RB32(&p->buf[2]) < AV_RB16(&p->buf[22]) + AV_RB16(&p->buf[20]) + CDXL_HEADER_SIZE) + return 0; + + /* previous chunk size */ + if (AV_RN32(&p->buf[6])) + score /= 2; + + /* current frame number, usually starts from 1 */ + if (AV_RB16(&p->buf[12]) != 1) + score /= 2; + + return score; +} + static int cdxl_read_header(AVFormatContext *s) { CDXLDemuxContext *cdxl = s->priv_data; @@ -68,7 +110,7 @@ static int cdxl_read_packet(AVFormatContext *s, AVPacket *pkt) int64_t pos; int ret; - if (pb->eof_reached) + if (url_feof(pb)) return AVERROR_EOF; pos = avio_tell(pb); @@ -180,6 +222,7 @@ AVInputFormat ff_cdxl_demuxer = { .name = "cdxl", .long_name = NULL_IF_CONFIG_SMALL("Commodore CDXL video"), .priv_data_size = sizeof(CDXLDemuxContext), + .read_probe = cdxl_read_probe, .read_header = cdxl_read_header, .read_packet = cdxl_read_packet, .extensions = "cdxl,xl", diff --git a/libavformat/concat.c b/libavformat/concat.c index 416bbf5fc3..3bbc83dfef 100644 --- a/libavformat/concat.c +++ b/libavformat/concat.c @@ -4,20 +4,20 @@ * Copyright (c) 2007 Wolfram Gloger * Copyright (c) 2010 Michele Orrù * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -111,9 +111,10 @@ static av_cold int concat_open(URLContext *h, const char *uri, int flags) if (err < 0) concat_close(h); - else if ((err = av_reallocp(&nodes, data->length * sizeof(*nodes))) < 0) + else if (!(nodes = av_realloc(nodes, data->length * sizeof(*nodes)))) { concat_close(h); - else + err = AVERROR(ENOMEM); + } else data->nodes = nodes; return err; } diff --git a/libavformat/concatdec.c b/libavformat/concatdec.c new file mode 100644 index 0000000000..428c749ab2 --- /dev/null +++ b/libavformat/concatdec.c @@ -0,0 +1,408 @@ +/* + * Copyright (c) 2012 Nicolas George + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/avstring.h" +#include "libavutil/opt.h" +#include "libavutil/parseutils.h" +#include "avformat.h" +#include "internal.h" +#include "url.h" + +typedef struct { + char *url; + int64_t start_time; + int64_t duration; +} ConcatFile; + +typedef struct { + AVClass *class; + ConcatFile *files; + ConcatFile *cur_file; + unsigned nb_files; + AVFormatContext *avf; + int safe; + int seekable; +} ConcatContext; + +static int concat_probe(AVProbeData *probe) +{ + return memcmp(probe->buf, "ffconcat version 1.0", 20) ? + 0 : AVPROBE_SCORE_MAX; +} + +static char *get_keyword(uint8_t **cursor) +{ + char *ret = *cursor += strspn(*cursor, SPACE_CHARS); + *cursor += strcspn(*cursor, SPACE_CHARS); + if (**cursor) { + *((*cursor)++) = 0; + *cursor += strspn(*cursor, SPACE_CHARS); + } + return ret; +} + +static int safe_filename(const char *f) +{ + const char *start = f; + + for (; *f; f++) { + /* A-Za-z0-9_- */ + if (!((unsigned)((*f | 32) - 'a') < 26 || + (unsigned)(*f - '0') < 10 || *f == '_' || *f == '-')) { + if (f == start) + return 0; + else if (*f == '/') + start = f + 1; + else if (*f != '.') + return 0; + } + } + return 1; +} + +#define FAIL(retcode) do { ret = (retcode); goto fail; } while(0) + +static int add_file(AVFormatContext *avf, char *filename, ConcatFile **rfile, + unsigned *nb_files_alloc) +{ + ConcatContext *cat = avf->priv_data; + ConcatFile *file; + char *url = NULL; + size_t url_len; + int ret; + + if (cat->safe > 0 && !safe_filename(filename)) { + av_log(avf, AV_LOG_ERROR, "Unsafe file name '%s'\n", filename); + FAIL(AVERROR(EPERM)); + } + url_len = strlen(avf->filename) + strlen(filename) + 16; + if (!(url = av_malloc(url_len))) + FAIL(AVERROR(ENOMEM)); + ff_make_absolute_url(url, url_len, avf->filename, filename); + av_freep(&filename); + + if (cat->nb_files >= *nb_files_alloc) { + size_t n = FFMAX(*nb_files_alloc * 2, 16); + ConcatFile *new_files; + if (n <= cat->nb_files || n > SIZE_MAX / sizeof(*cat->files) || + !(new_files = av_realloc(cat->files, n * sizeof(*cat->files)))) + FAIL(AVERROR(ENOMEM)); + cat->files = new_files; + *nb_files_alloc = n; + } + + file = &cat->files[cat->nb_files++]; + memset(file, 0, sizeof(*file)); + *rfile = file; + + file->url = url; + file->start_time = AV_NOPTS_VALUE; + file->duration = AV_NOPTS_VALUE; + + return 0; + +fail: + av_free(url); + av_free(filename); + return ret; +} + +static int open_file(AVFormatContext *avf, unsigned fileno) +{ + ConcatContext *cat = avf->priv_data; + ConcatFile *file = &cat->files[fileno]; + int ret; + + if (cat->avf) + avformat_close_input(&cat->avf); + if ((ret = avformat_open_input(&cat->avf, file->url, NULL, NULL)) < 0 || + (ret = avformat_find_stream_info(cat->avf, NULL)) < 0) { + av_log(avf, AV_LOG_ERROR, "Impossible to open '%s'\n", file->url); + return ret; + } + cat->cur_file = file; + if (file->start_time == AV_NOPTS_VALUE) + file->start_time = !fileno ? 0 : + cat->files[fileno - 1].start_time + + cat->files[fileno - 1].duration; + return 0; +} + +static int concat_read_close(AVFormatContext *avf) +{ + ConcatContext *cat = avf->priv_data; + unsigned i; + + if (cat->avf) + avformat_close_input(&cat->avf); + for (i = 0; i < cat->nb_files; i++) + av_freep(&cat->files[i].url); + av_freep(&cat->files); + return 0; +} + +static int concat_read_header(AVFormatContext *avf) +{ + ConcatContext *cat = avf->priv_data; + uint8_t buf[4096]; + uint8_t *cursor, *keyword; + int ret, line = 0, i; + unsigned nb_files_alloc = 0; + ConcatFile *file = NULL; + AVStream *st, *source_st; + int64_t time = 0; + + while (1) { + if ((ret = ff_get_line(avf->pb, buf, sizeof(buf))) <= 0) + break; + line++; + cursor = buf; + keyword = get_keyword(&cursor); + if (!*keyword || *keyword == '#') + continue; + + if (!strcmp(keyword, "file")) { + char *filename = av_get_token((const char **)&cursor, SPACE_CHARS); + if (!filename) { + av_log(avf, AV_LOG_ERROR, "Line %d: filename required\n", line); + FAIL(AVERROR_INVALIDDATA); + } + if ((ret = add_file(avf, filename, &file, &nb_files_alloc)) < 0) + FAIL(ret); + } else if (!strcmp(keyword, "duration")) { + char *dur_str = get_keyword(&cursor); + int64_t dur; + if (!file) { + av_log(avf, AV_LOG_ERROR, "Line %d: duration without file\n", + line); + FAIL(AVERROR_INVALIDDATA); + } + if ((ret = av_parse_time(&dur, dur_str, 1)) < 0) { + av_log(avf, AV_LOG_ERROR, "Line %d: invalid duration '%s'\n", + line, dur_str); + FAIL(ret); + } + file->duration = dur; + } else if (!strcmp(keyword, "ffconcat")) { + char *ver_kw = get_keyword(&cursor); + char *ver_val = get_keyword(&cursor); + if (strcmp(ver_kw, "version") || strcmp(ver_val, "1.0")) { + av_log(avf, AV_LOG_ERROR, "Line %d: invalid version\n", line); + FAIL(AVERROR_INVALIDDATA); + } + if (cat->safe < 0) + cat->safe = 1; + } else { + av_log(avf, AV_LOG_ERROR, "Line %d: unknown keyword '%s'\n", + line, keyword); + FAIL(AVERROR_INVALIDDATA); + } + } + if (ret < 0) + FAIL(ret); + if (!cat->nb_files) + FAIL(AVERROR_INVALIDDATA); + + for (i = 0; i < cat->nb_files; i++) { + if (cat->files[i].start_time == AV_NOPTS_VALUE) + cat->files[i].start_time = time; + else + time = cat->files[i].start_time; + if (cat->files[i].duration == AV_NOPTS_VALUE) + break; + time += cat->files[i].duration; + } + if (i == cat->nb_files) { + avf->duration = time; + cat->seekable = 1; + } + + if ((ret = open_file(avf, 0)) < 0) + FAIL(ret); + for (i = 0; i < cat->avf->nb_streams; i++) { + if (!(st = avformat_new_stream(avf, NULL))) + FAIL(AVERROR(ENOMEM)); + source_st = cat->avf->streams[i]; + if ((ret = avcodec_copy_context(st->codec, source_st->codec)) < 0) + FAIL(ret); + st->r_frame_rate = source_st->r_frame_rate; + st->avg_frame_rate = source_st->avg_frame_rate; + st->time_base = source_st->time_base; + st->sample_aspect_ratio = source_st->sample_aspect_ratio; + } + + return 0; + +fail: + concat_read_close(avf); + return ret; +} + +static int open_next_file(AVFormatContext *avf) +{ + ConcatContext *cat = avf->priv_data; + unsigned fileno = cat->cur_file - cat->files; + + if (cat->cur_file->duration == AV_NOPTS_VALUE) + cat->cur_file->duration = cat->avf->duration; + + if (++fileno >= cat->nb_files) + return AVERROR_EOF; + return open_file(avf, fileno); +} + +static int concat_read_packet(AVFormatContext *avf, AVPacket *pkt) +{ + ConcatContext *cat = avf->priv_data; + int ret; + int64_t delta; + + while (1) { + if ((ret = av_read_frame(cat->avf, pkt)) != AVERROR_EOF || + (ret = open_next_file(avf)) < 0) + break; + } + delta = av_rescale_q(cat->cur_file->start_time - cat->avf->start_time, + AV_TIME_BASE_Q, + cat->avf->streams[pkt->stream_index]->time_base); + if (pkt->pts != AV_NOPTS_VALUE) + pkt->pts += delta; + if (pkt->dts != AV_NOPTS_VALUE) + pkt->dts += delta; + return ret; +} + +static void rescale_interval(AVRational tb_in, AVRational tb_out, + int64_t *min_ts, int64_t *ts, int64_t *max_ts) +{ + *ts = av_rescale_q (* ts, tb_in, tb_out); + *min_ts = av_rescale_q_rnd(*min_ts, tb_in, tb_out, + AV_ROUND_UP | AV_ROUND_PASS_MINMAX); + *max_ts = av_rescale_q_rnd(*max_ts, tb_in, tb_out, + AV_ROUND_DOWN | AV_ROUND_PASS_MINMAX); +} + +static int try_seek(AVFormatContext *avf, int stream, + int64_t min_ts, int64_t ts, int64_t max_ts, int flags) +{ + ConcatContext *cat = avf->priv_data; + int64_t t0 = cat->cur_file->start_time - cat->avf->start_time; + + ts -= t0; + min_ts = min_ts == INT64_MIN ? INT64_MIN : min_ts - t0; + max_ts = max_ts == INT64_MAX ? INT64_MAX : max_ts - t0; + if (stream >= 0) { + if (stream >= cat->avf->nb_streams) + return AVERROR(EIO); + rescale_interval(AV_TIME_BASE_Q, cat->avf->streams[stream]->time_base, + &min_ts, &ts, &max_ts); + } + return avformat_seek_file(cat->avf, stream, min_ts, ts, max_ts, flags); +} + +static int real_seek(AVFormatContext *avf, int stream, + int64_t min_ts, int64_t ts, int64_t max_ts, int flags) +{ + ConcatContext *cat = avf->priv_data; + int ret, left, right; + + if (stream >= 0) { + if (stream >= avf->nb_streams) + return AVERROR(EINVAL); + rescale_interval(avf->streams[stream]->time_base, AV_TIME_BASE_Q, + &min_ts, &ts, &max_ts); + } + + left = 0; + right = cat->nb_files; + while (right - left > 1) { + int mid = (left + right) / 2; + if (ts < cat->files[mid].start_time) + right = mid; + else + left = mid; + } + + if ((ret = open_file(avf, left)) < 0) + return ret; + + ret = try_seek(avf, stream, min_ts, ts, max_ts, flags); + if (ret < 0 && + left < cat->nb_files - 1 && + cat->files[left + 1].start_time < max_ts) { + if ((ret = open_file(avf, left + 1)) < 0) + return ret; + ret = try_seek(avf, stream, min_ts, ts, max_ts, flags); + } + return ret; +} + +static int concat_seek(AVFormatContext *avf, int stream, + int64_t min_ts, int64_t ts, int64_t max_ts, int flags) +{ + ConcatContext *cat = avf->priv_data; + ConcatFile *cur_file_saved = cat->cur_file; + AVFormatContext *cur_avf_saved = cat->avf; + int ret; + + if (!cat->seekable) + return AVERROR(ESPIPE); /* XXX: can we use it? */ + if (flags & (AVSEEK_FLAG_BYTE | AVSEEK_FLAG_FRAME)) + return AVERROR(ENOSYS); + cat->avf = NULL; + if ((ret = real_seek(avf, stream, min_ts, ts, max_ts, flags)) < 0) { + if (cat->avf) + avformat_close_input(&cat->avf); + cat->avf = cur_avf_saved; + cat->cur_file = cur_file_saved; + } else { + avformat_close_input(&cur_avf_saved); + } + return ret; +} + +#define OFFSET(x) offsetof(ConcatContext, x) +#define DEC AV_OPT_FLAG_DECODING_PARAM + +static const AVOption options[] = { + { "safe", "enable safe mode", + OFFSET(safe), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 1, DEC }, + { NULL } +}; + +static const AVClass concat_class = { + .class_name = "concat demuxer", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + + +AVInputFormat ff_concat_demuxer = { + .name = "concat", + .long_name = NULL_IF_CONFIG_SMALL("Virtual concatenation script"), + .priv_data_size = sizeof(ConcatContext), + .read_probe = concat_probe, + .read_header = concat_read_header, + .read_packet = concat_read_packet, + .read_close = concat_read_close, + .read_seek2 = concat_seek, + .priv_class = &concat_class, +}; diff --git a/libavformat/crcenc.c b/libavformat/crcenc.c index 3b30cc9199..ac0638037e 100644 --- a/libavformat/crcenc.c +++ b/libavformat/crcenc.c @@ -2,20 +2,20 @@ * CRC encoder (for codec/format testing) * Copyright (c) 2002 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -57,7 +57,6 @@ static int crc_write_trailer(struct AVFormatContext *s) AVOutputFormat ff_crc_muxer = { .name = "crc", .long_name = NULL_IF_CONFIG_SMALL("CRC testing"), - .extensions = "", .priv_data_size = sizeof(CRCState), .audio_codec = AV_CODEC_ID_PCM_S16LE, .video_codec = AV_CODEC_ID_RAWVIDEO, diff --git a/libavformat/crypto.c b/libavformat/crypto.c index 3bc33f2bb9..a9b6e47682 100644 --- a/libavformat/crypto.c +++ b/libavformat/crypto.c @@ -2,20 +2,20 @@ * Decryption protocol handler * Copyright (c) 2011 Martin Storsjo * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -59,7 +59,7 @@ static const AVClass crypto_class = { .version = LIBAVUTIL_VERSION_INT, }; -static int crypto_open(URLContext *h, const char *uri, int flags) +static int crypto_open2(URLContext *h, const char *uri, int flags, AVDictionary **options) { const char *nested_url; int ret = 0; @@ -83,7 +83,7 @@ static int crypto_open(URLContext *h, const char *uri, int flags) goto err; } if ((ret = ffurl_open(&c->hd, nested_url, AVIO_FLAG_READ, - &h->interrupt_callback, NULL)) < 0) { + &h->interrupt_callback, options)) < 0) { av_log(h, AV_LOG_ERROR, "Unable to open input\n"); goto err; } @@ -161,7 +161,7 @@ static int crypto_close(URLContext *h) URLProtocol ff_crypto_protocol = { .name = "crypto", - .url_open = crypto_open, + .url_open2 = crypto_open2, .url_read = crypto_read, .url_close = crypto_close, .priv_data_size = sizeof(CryptoContext), diff --git a/libavformat/cutils.c b/libavformat/cutils.c index f58e152cac..0458a2dc7c 100644 --- a/libavformat/cutils.c +++ b/libavformat/cutils.c @@ -2,46 +2,25 @@ * various simple utilities for libavformat * Copyright (c) 2000, 2001, 2002 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "avformat.h" #include "internal.h" -/* add one element to a dynamic array */ -void ff_dynarray_add(intptr_t **tab_ptr, int *nb_ptr, intptr_t elem) -{ - /* see similar avconv.c:grow_array() */ - int nb, nb_alloc; - intptr_t *tab; - - nb = *nb_ptr; - tab = *tab_ptr; - if ((nb & (nb - 1)) == 0) { - if (nb == 0) - nb_alloc = 1; - else - nb_alloc = nb * 2; - tab = av_realloc(tab, nb_alloc * sizeof(intptr_t)); - *tab_ptr = tab; - } - tab[nb++] = elem; - *nb_ptr = nb; -} - #define ISLEAP(y) (((y) % 4 == 0) && (((y) % 100) != 0 || ((y) % 400) == 0)) #define LEAPS_COUNT(y) ((y)/4 - (y)/100 + (y)/400) diff --git a/libavformat/data_uri.c b/libavformat/data_uri.c new file mode 100644 index 0000000000..1598b999ae --- /dev/null +++ b/libavformat/data_uri.c @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2012 Nicolas George + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <string.h> +#include "libavutil/avstring.h" +#include "libavutil/base64.h" +#include "url.h" + +typedef struct { + const uint8_t *data; + void *tofree; + size_t size; + size_t pos; +} DataContext; + +static av_cold int data_open(URLContext *h, const char *uri, int flags) +{ + DataContext *dc = h->priv_data; + const char *data, *opt, *next; + char *ddata; + int ret, base64 = 0; + size_t in_size; + + /* data:content/type[;base64],payload */ + + av_strstart(uri, "data:", &uri); + data = strchr(uri, ','); + if (!data) { + av_log(h, AV_LOG_ERROR, "No ',' delimiter in URI\n"); + return AVERROR(EINVAL); + } + opt = uri; + while (opt < data) { + next = av_x_if_null(memchr(opt, ';', data - opt), data); + if (opt == uri) { + if (!memchr(opt, '/', next - opt)) { /* basic validity check */ + av_log(h, AV_LOG_ERROR, "Invalid content-type '%.*s'\n", + (int)(next - opt), opt); + return AVERROR(EINVAL); + } + av_log(h, AV_LOG_VERBOSE, "Content-type: %.*s\n", + (int)(next - opt), opt); + } else { + if (!av_strncasecmp(opt, "base64", next - opt)) { + base64 = 1; + } else { + av_log(h, AV_LOG_VERBOSE, "Ignoring option '%.*s'\n", + (int)(next - opt), opt); + } + } + opt = next + 1; + } + + data++; + in_size = strlen(data); + if (base64) { + size_t out_size = 3 * (in_size / 4) + 1; + + if (out_size > INT_MAX || !(ddata = av_malloc(out_size))) + return AVERROR(ENOMEM); + if ((ret = av_base64_decode(ddata, data, out_size)) < 0) { + av_free(ddata); + av_log(h, AV_LOG_ERROR, "Invalid base64 in URI\n"); + return ret; + } + dc->data = dc->tofree = ddata; + dc->size = ret; + } else { + dc->data = data; + dc->size = in_size; + } + return 0; +} + +static av_cold int data_close(URLContext *h) +{ + DataContext *dc = h->priv_data; + + av_freep(&dc->tofree); + return 0; +} + +static int data_read(URLContext *h, unsigned char *buf, int size) +{ + DataContext *dc = h->priv_data; + + if (dc->pos >= dc->size) + return AVERROR_EOF; + size = FFMIN(size, dc->size - dc->pos); + memcpy(buf, dc->data + dc->pos, size); + dc->pos += size; + return size; +} + +URLProtocol ff_data_protocol = { + .name = "data", + .url_open = data_open, + .url_close = data_close, + .url_read = data_read, + .priv_data_size = sizeof(DataContext), +}; diff --git a/libavformat/daud.c b/libavformat/daud.c index bb7ab7fbcd..6983785f3c 100644 --- a/libavformat/daud.c +++ b/libavformat/daud.c @@ -2,20 +2,20 @@ * D-Cinema audio demuxer * Copyright (c) 2005 Reimar Döffinger * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -41,7 +41,7 @@ static int daud_header(AVFormatContext *s) { static int daud_packet(AVFormatContext *s, AVPacket *pkt) { AVIOContext *pb = s->pb; int ret, size; - if (pb->eof_reached) + if (url_feof(pb)) return AVERROR(EIO); size = avio_rb16(pb); avio_rb16(pb); // unknown @@ -77,7 +77,7 @@ AVInputFormat ff_daud_demuxer = { .long_name = NULL_IF_CONFIG_SMALL("D-Cinema audio"), .read_header = daud_header, .read_packet = daud_packet, - .extensions = "302", + .extensions = "302,daud", }; #endif diff --git a/libavformat/dfa.c b/libavformat/dfa.c index fa32d1f2cf..12516afdc5 100644 --- a/libavformat/dfa.c +++ b/libavformat/dfa.c @@ -2,20 +2,20 @@ * Chronomaster DFA Format Demuxer * Copyright (c) 2011 Konstantin Shishkov * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -36,13 +36,15 @@ static int dfa_read_header(AVFormatContext *s) AVIOContext *pb = s->pb; AVStream *st; int frames; + int version; uint32_t mspf; if (avio_rl32(pb) != MKTAG('D', 'F', 'I', 'A')) { av_log(s, AV_LOG_ERROR, "Invalid magic for DFA\n"); return AVERROR_INVALIDDATA; } - avio_skip(pb, 2); // unused + + version = avio_rl16(pb); frames = avio_rl16(pb); st = avformat_new_stream(s, NULL); @@ -62,6 +64,12 @@ static int dfa_read_header(AVFormatContext *s) avio_skip(pb, 128 - 16); // padding st->duration = frames; + if (ff_alloc_extradata(st->codec, 2)) + return AVERROR(ENOMEM); + AV_WL16(st->codec->extradata, version); + if (version == 0x100) + st->sample_aspect_ratio = (AVRational){2, 1}; + return 0; } diff --git a/libavformat/diracdec.c b/libavformat/diracdec.c index f275212d4a..6636ead30d 100644 --- a/libavformat/diracdec.c +++ b/libavformat/diracdec.c @@ -2,20 +2,20 @@ * RAW Dirac demuxer * Copyright (c) 2007 Marco Gerards <marco@gnu.org> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/dnxhddec.c b/libavformat/dnxhddec.c index 8bb68145fc..7b554003f1 100644 --- a/libavformat/dnxhddec.c +++ b/libavformat/dnxhddec.c @@ -3,20 +3,20 @@ * Copyright (c) 2008 Baptiste Coudurier <baptiste.coudurier@gmail.com> * Copyright (c) 2009 Reimar Döffinger <Reimar.Doeffinger@gmx.de> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -37,7 +37,7 @@ static int dnxhd_probe(AVProbeData *p) if (!w || !h) return 0; compression_id = AV_RB32(p->buf + 0x28); - if (compression_id < 1237 || compression_id > 1253) + if (compression_id < 1235 || compression_id > 1253) return 0; return AVPROBE_SCORE_MAX; } diff --git a/libavformat/dsicin.c b/libavformat/dsicin.c index 6a7c8b948a..4a54680056 100644 --- a/libavformat/dsicin.c +++ b/libavformat/dsicin.c @@ -2,20 +2,20 @@ * Delphine Software International CIN File Demuxer * Copyright (c) 2006 Gregory Montoir (cyx@users.sourceforge.net) * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -28,6 +28,7 @@ #include "libavutil/intreadwrite.h" #include "avformat.h" #include "internal.h" +#include "avio_internal.h" typedef struct CinFileHeader { @@ -149,7 +150,7 @@ static int cin_read_frame_header(CinDemuxContext *cin, AVIOContext *pb) { hdr->video_frame_size = avio_rl32(pb); hdr->audio_frame_size = avio_rl32(pb); - if (pb->eof_reached || pb->error) + if (url_feof(pb) || pb->error) return AVERROR(EIO); if (avio_rl32(pb) != 0xAA55AA55) @@ -183,6 +184,8 @@ static int cin_read_packet(AVFormatContext *s, AVPacket *pkt) /* palette and video packet */ pkt_size = (palette_type + 3) * hdr->pal_colors_count + hdr->video_frame_size; + pkt_size = ffio_limit(pb, pkt_size); + ret = av_new_packet(pkt, 4 + pkt_size); if (ret < 0) return ret; diff --git a/libavformat/dtsdec.c b/libavformat/dtsdec.c index 9010711599..23cbe93516 100644 --- a/libavformat/dtsdec.c +++ b/libavformat/dtsdec.c @@ -2,20 +2,20 @@ * RAW DTS demuxer * Copyright (c) 2008 Benjamin Larsson * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -34,6 +34,7 @@ static int dts_probe(AVProbeData *p) uint32_t state = -1; int markers[3] = {0}; int sum, max; + int64_t diff = 0; buf = p->buf; @@ -54,12 +55,16 @@ static int dts_probe(AVProbeData *p) if (state == DCA_MARKER_14B_LE) if ((bytestream_get_be16(&bufp) & 0xF0FF) == 0xF007) markers[2]++; + + if (buf - p->buf >= 4) + diff += FFABS(AV_RL16(buf) - AV_RL16(buf-4)); } sum = markers[0] + markers[1] + markers[2]; max = markers[1] > markers[0]; max = markers[2] > markers[max] ? 2 : max; if (markers[max] > 3 && p->buf_size / markers[max] < 32*1024 && - markers[max] * 4 > sum * 3) + markers[max] * 4 > sum * 3 && + diff / p->buf_size > 200) return AVPROBE_SCORE_EXTENSION + 1; return 0; diff --git a/libavformat/dtshddec.c b/libavformat/dtshddec.c new file mode 100644 index 0000000000..55c4ca607e --- /dev/null +++ b/libavformat/dtshddec.c @@ -0,0 +1,139 @@ +/* + * Raw DTS-HD demuxer + * Copyright (c) 2012 Paul B Mahol + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/intreadwrite.h" +#include "libavutil/dict.h" +#include "avformat.h" + +#define AUPR_HDR 0x415550522D484452 +#define AUPRINFO 0x41555052494E464F +#define BITSHVTB 0x4249545348565442 +#define BLACKOUT 0x424C41434B4F5554 +#define BRANCHPT 0x4252414E43485054 +#define BUILDVER 0x4255494C44564552 +#define CORESSMD 0x434F524553534D44 +#define DTSHDHDR 0x4454534844484452 +#define EXTSS_MD 0x45585453535f4d44 +#define FILEINFO 0x46494C45494E464F +#define NAVI_TBL 0x4E4156492D54424C +#define STRMDATA 0x5354524D44415441 +#define TIMECODE 0x54494D45434F4445 + +typedef struct DTSHDDemuxContext { + uint64_t data_end; +} DTSHDDemuxContext; + +static int dtshd_probe(AVProbeData *p) +{ + if (AV_RB64(p->buf) == DTSHDHDR) + return AVPROBE_SCORE_MAX; + return 0; +} + +static int dtshd_read_header(AVFormatContext *s) +{ + DTSHDDemuxContext *dtshd = s->priv_data; + AVIOContext *pb = s->pb; + uint64_t chunk_type, chunk_size; + AVStream *st; + int ret; + char *value; + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_id = AV_CODEC_ID_DTS; + st->need_parsing = AVSTREAM_PARSE_FULL_RAW; + + while (!url_feof(pb)) { + chunk_type = avio_rb64(pb); + chunk_size = avio_rb64(pb); + + if (chunk_size < 4) { + av_log(s, AV_LOG_ERROR, "chunk size too small\n"); + return AVERROR_INVALIDDATA; + } + if (chunk_size > ((uint64_t)1 << 61)) { + av_log(s, AV_LOG_ERROR, "chunk size too big\n"); + return AVERROR_INVALIDDATA; + } + + switch (chunk_type) { + case STRMDATA: + dtshd->data_end = chunk_size + avio_tell(pb); + if (dtshd->data_end <= chunk_size) + return AVERROR_INVALIDDATA; + return 0; + break; + case FILEINFO: + if (chunk_size > INT_MAX) + goto skip; + value = av_malloc(chunk_size); + if (!value) + goto skip; + avio_read(pb, value, chunk_size); + value[chunk_size - 1] = 0; + av_dict_set(&s->metadata, "fileinfo", value, + AV_DICT_DONT_STRDUP_VAL); + break; + default: +skip: + ret = avio_skip(pb, chunk_size); + if (ret < 0) + return ret; + }; + } + + return AVERROR_EOF; +} + +static int raw_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + DTSHDDemuxContext *dtshd = s->priv_data; + int64_t size, left; + int ret; + + left = dtshd->data_end - avio_tell(s->pb); + size = FFMIN(left, 1024); + if (size <= 0) + return AVERROR_EOF; + + ret = av_get_packet(s->pb, pkt, size); + if (ret < 0) + return ret; + + pkt->stream_index = 0; + + return ret; +} + +AVInputFormat ff_dtshd_demuxer = { + .name = "dtshd", + .long_name = NULL_IF_CONFIG_SMALL("raw DTS-HD"), + .priv_data_size = sizeof(DTSHDDemuxContext), + .read_probe = dtshd_probe, + .read_header = dtshd_read_header, + .read_packet = raw_read_packet, + .flags = AVFMT_GENERIC_INDEX, + .extensions = "dtshd", + .raw_codec_id = AV_CODEC_ID_DTS, +}; diff --git a/libavformat/dv.c b/libavformat/dv.c index b0dad633b1..e3b0d0a31e 100644 --- a/libavformat/dv.c +++ b/libavformat/dv.c @@ -12,20 +12,20 @@ * Copyright (c) 2006 Daniel Maas <dmaas@maasdigital.com> * Funded by BBC Research & Development * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include <time.h> @@ -36,7 +36,9 @@ #include "libavutil/channel_layout.h" #include "libavutil/intreadwrite.h" #include "libavutil/mathematics.h" +#include "libavutil/timecode.h" #include "dv.h" +#include "libavutil/avassert.h" struct DVDemuxContext { const DVprofile* sys; /* Current DV profile. E.g.: 525/60, 625/50 */ @@ -89,6 +91,9 @@ static const uint8_t *dv_extract_pack(uint8_t *frame, enum dv_pack_type t) case dv_video_control: offs = (80 * 5 + 48 + 5); break; + case dv_timecode: + offs = (80*1 + 3 + 3); + break; default: return NULL; } @@ -137,9 +142,14 @@ static int dv_extract_audio(uint8_t *frame, uint8_t **ppcm, * channels 0,1 and odd 2,3. */ ipcm = (sys->height == 720 && !(frame[1] & 0x0C)) ? 2 : 0; + if (ipcm + sys->n_difchan > (quant == 1 ? 2 : 4)) { + av_log(NULL, AV_LOG_ERROR, "too many dv pcm frames\n"); + return AVERROR_INVALIDDATA; + } + /* for each DIF channel */ for (chan = 0; chan < sys->n_difchan; chan++) { - /* next stereo channel (50Mbps and 100Mbps only) */ + av_assert0(ipcm<4); pcm = ppcm[ipcm++]; if (!pcm) break; @@ -149,6 +159,7 @@ static int dv_extract_audio(uint8_t *frame, uint8_t **ppcm, frame += 6 * 80; /* skip DIF segment header */ if (quant == 1 && i == half_ch) { /* next stereo channel (12bit mode only) */ + av_assert0(ipcm<4); pcm = ppcm[ipcm++]; if (!pcm) break; @@ -278,11 +289,6 @@ static int dv_extract_video_info(DVDemuxContext *c, uint8_t *frame) avpriv_set_pts_info(c->vst, 64, c->sys->time_base.num, c->sys->time_base.den); avctx->time_base = c->sys->time_base; - if (!avctx->width) { - avctx->width = c->sys->width; - avctx->height = c->sys->height; - } - avctx->pix_fmt = c->sys->pix_fmt; /* finding out SAR is a little bit messy */ vsc_pack = dv_extract_pack(frame, dv_video_control); @@ -298,6 +304,22 @@ static int dv_extract_video_info(DVDemuxContext *c, uint8_t *frame) return size; } +static int dv_extract_timecode(DVDemuxContext* c, uint8_t* frame, char *tc) +{ + const uint8_t *tc_pack; + + // For PAL systems, drop frame bit is replaced by an arbitrary + // bit so its value should not be considered. Drop frame timecode + // is only relevant for NTSC systems. + int prevent_df = c->sys->ltc_divisor == 25 || c->sys->ltc_divisor == 50; + + tc_pack = dv_extract_pack(frame, dv_timecode); + if (!tc_pack) + return 0; + av_timecode_make_smpte_tc_string(tc, AV_RB32(tc_pack + 1), prevent_df); + return 1; +} + /* The following 3 functions constitute our interface to the world */ DVDemuxContext *avpriv_dv_init_demux(AVFormatContext *s) @@ -341,7 +363,7 @@ int avpriv_dv_get_packet(DVDemuxContext *c, AVPacket *pkt) } int avpriv_dv_produce_packet(DVDemuxContext *c, AVPacket *pkt, - uint8_t *buf, int buf_size) + uint8_t *buf, int buf_size, int64_t pos) { int size, i; uint8_t *ppcm[5] = { 0 }; @@ -356,6 +378,7 @@ int avpriv_dv_produce_packet(DVDemuxContext *c, AVPacket *pkt, /* FIXME: in case of no audio/bad audio we have to do something */ size = dv_extract_audio_info(c, buf); for (i = 0; i < c->ach; i++) { + c->audio_pkt[i].pos = pos; c->audio_pkt[i].size = size; c->audio_pkt[i].pts = c->abytes * 30000 * 8 / c->ast[i]->codec->bit_rate; @@ -381,6 +404,7 @@ int avpriv_dv_produce_packet(DVDemuxContext *c, AVPacket *pkt, size = dv_extract_video_info(c, buf); av_init_packet(pkt); pkt->data = buf; + pkt->pos = pos; pkt->size = size; pkt->flags |= AV_PKT_FLAG_KEY; pkt->stream_index = c->vst->index; @@ -413,10 +437,13 @@ static int64_t dv_frame_offset(AVFormatContext *s, DVDemuxContext *c, void ff_dv_offset_reset(DVDemuxContext *c, int64_t frame_offset) { c->frames = frame_offset; - if (c->ach) + if (c->ach) { + if (c->sys) { c->abytes = av_rescale_q(c->frames, c->sys->time_base, (AVRational) { 8, c->ast[0]->codec->bit_rate }); - + } else + av_log(c->fctx, AV_LOG_ERROR, "cannot adjust audio bytes\n"); + } c->audio_pkt[0].size = c->audio_pkt[1].size = 0; c->audio_pkt[2].size = c->audio_pkt[3].size = 0; } @@ -430,6 +457,38 @@ typedef struct RawDVContext { uint8_t buf[DV_MAX_FRAME_SIZE]; } RawDVContext; +static int dv_read_timecode(AVFormatContext *s) { + int ret; + char timecode[AV_TIMECODE_STR_SIZE]; + int64_t pos = avio_tell(s->pb); + + // Read 3 DIF blocks: Header block and 2 Subcode blocks. + int partial_frame_size = 3 * 80; + uint8_t *partial_frame = av_mallocz(sizeof(*partial_frame) * + partial_frame_size); + + RawDVContext *c = s->priv_data; + ret = avio_read(s->pb, partial_frame, partial_frame_size); + if (ret < 0) + goto finish; + + if (ret < partial_frame_size) { + ret = -1; + goto finish; + } + + ret = dv_extract_timecode(c->dv_demux, partial_frame, timecode); + if (ret) + av_dict_set(&s->metadata, "timecode", timecode, 0); + else + av_log(s, AV_LOG_ERROR, "Detected timecode is invalid\n"); + +finish: + av_free(partial_frame); + avio_seek(s->pb, pos, SEEK_SET); + return ret; +} + static int dv_read_header(AVFormatContext *s) { unsigned state, marker_pos = 0; @@ -441,7 +500,7 @@ static int dv_read_header(AVFormatContext *s) state = avio_rb32(s->pb); while ((state & 0xffffff7f) != 0x1f07003f) { - if (s->pb->eof_reached) { + if (url_feof(s->pb)) { av_log(s, AV_LOG_ERROR, "Cannot find DV header.\n"); return -1; } @@ -456,7 +515,7 @@ static int dv_read_header(AVFormatContext *s) } AV_WB32(c->buf, state); - if (avio_read(s->pb, c->buf + 4, DV_PROFILE_BYTES - 4) <= 0 || + if (avio_read(s->pb, c->buf + 4, DV_PROFILE_BYTES - 4) != DV_PROFILE_BYTES - 4 || avio_seek(s->pb, -DV_PROFILE_BYTES, SEEK_CUR) < 0) return AVERROR(EIO); @@ -473,6 +532,9 @@ static int dv_read_header(AVFormatContext *s) (AVRational) { 8, 1 }, c->dv_demux->sys->time_base); + if (s->pb->seekable) + dv_read_timecode(s); + return 0; } @@ -484,13 +546,14 @@ static int dv_read_packet(AVFormatContext *s, AVPacket *pkt) size = avpriv_dv_get_packet(c->dv_demux, pkt); if (size < 0) { + int64_t pos = avio_tell(s->pb); if (!c->dv_demux->sys) return AVERROR(EIO); size = c->dv_demux->sys->frame_size; if (avio_read(s->pb, c->buf, size) <= 0) return AVERROR(EIO); - size = avpriv_dv_produce_packet(c->dv_demux, pkt, c->buf, size); + size = avpriv_dv_produce_packet(c->dv_demux, pkt, c->buf, size, pos); } return size; diff --git a/libavformat/dv.h b/libavformat/dv.h index e8b2d37904..160c6ab873 100644 --- a/libavformat/dv.h +++ b/libavformat/dv.h @@ -8,20 +8,20 @@ * Raw DV format * Copyright (c) 2002 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -33,7 +33,7 @@ typedef struct DVDemuxContext DVDemuxContext; DVDemuxContext* avpriv_dv_init_demux(AVFormatContext* s); int avpriv_dv_get_packet(DVDemuxContext*, AVPacket *); -int avpriv_dv_produce_packet(DVDemuxContext*, AVPacket*, uint8_t*, int); +int avpriv_dv_produce_packet(DVDemuxContext*, AVPacket*, uint8_t*, int, int64_t); void ff_dv_offset_reset(DVDemuxContext *c, int64_t frame_offset); typedef struct DVMuxContext DVMuxContext; diff --git a/libavformat/dvenc.c b/libavformat/dvenc.c index 93ae0b56dc..e37bd23421 100644 --- a/libavformat/dvenc.c +++ b/libavformat/dvenc.c @@ -11,20 +11,20 @@ * 50 Mbps (DVCPRO50) support * Copyright (c) 2006 Daniel Maas <dmaas@maasdigital.com> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include <time.h> @@ -37,10 +37,14 @@ #include "dv.h" #include "libavutil/fifo.h" #include "libavutil/mathematics.h" +#include "libavutil/intreadwrite.h" +#include "libavutil/opt.h" +#include "libavutil/timecode.h" #define MAX_AUDIO_FRAME_SIZE 192000 // 1 second of 48khz 32bit audio struct DVMuxContext { + AVClass *av_class; const DVprofile* sys; /* current DV profile, e.g.: 525/60, 625/50 */ int n_ast; /* number of stereo audio streams (up to 2) */ AVStream *ast[2]; /* stereo audio streams */ @@ -50,6 +54,7 @@ struct DVMuxContext { int has_audio; /* frame under construction has audio */ int has_video; /* frame under construction has video */ uint8_t frame_buf[DV_MAX_FRAME_SIZE]; /* frame under construction */ + AVTimecode tc; /* timecode context */ }; static const int dv_aaux_packs_dist[12][9] = { @@ -77,34 +82,15 @@ static int dv_write_pack(enum dv_pack_type pack_id, DVMuxContext *c, uint8_t* bu { struct tm tc; time_t ct; - int ltc_frame; + uint32_t timecode; va_list ap; buf[0] = (uint8_t)pack_id; switch (pack_id) { case dv_timecode: - ct = (time_t)av_rescale_rnd(c->frames, c->sys->time_base.num, - c->sys->time_base.den, AV_ROUND_DOWN); - ff_brktimegm(ct, &tc); - /* - * LTC drop-frame frame counter drops two frames (0 and 1) every - * minute, unless it is exactly divisible by 10 - */ - ltc_frame = (c->frames + 2 * ct / 60 - 2 * ct / 600) % c->sys->ltc_divisor; - buf[1] = (0 << 7) | /* color frame: 0 - unsync; 1 - sync mode */ - (1 << 6) | /* drop frame timecode: 0 - nondrop; 1 - drop */ - ((ltc_frame / 10) << 4) | /* tens of frames */ - (ltc_frame % 10); /* units of frames */ - buf[2] = (1 << 7) | /* biphase mark polarity correction: 0 - even; 1 - odd */ - ((tc.tm_sec / 10) << 4) | /* tens of seconds */ - (tc.tm_sec % 10); /* units of seconds */ - buf[3] = (1 << 7) | /* binary group flag BGF0 */ - ((tc.tm_min / 10) << 4) | /* tens of minutes */ - (tc.tm_min % 10); /* units of minutes */ - buf[4] = (1 << 7) | /* binary group flag BGF2 */ - (1 << 6) | /* binary group flag BGF1 */ - ((tc.tm_hour / 10) << 4) | /* tens of hours */ - (tc.tm_hour % 10); /* units of hours */ + timecode = av_timecode_get_smpte_from_framenum(&c->tc, c->frames); + timecode |= 1<<23 | 1<<15 | 1<<7 | 1<<6; // biphase and binary group flags + AV_WB32(buf + 1, timecode); break; case dv_audio_source: /* AAUX source pack */ va_start(ap, buf); @@ -369,6 +355,10 @@ static void dv_delete_mux(DVMuxContext *c) static int dv_write_header(AVFormatContext *s) { + AVRational rate; + DVMuxContext *dvc = s->priv_data; + AVDictionaryEntry *tcr = av_dict_get(s->metadata, "timecode", NULL, 0); + if (!dv_init_mux(s)) { av_log(s, AV_LOG_ERROR, "Can't initialize DV format!\n" "Make sure that you supply exactly two streams:\n" @@ -376,7 +366,19 @@ static int dv_write_header(AVFormatContext *s) " (50Mbps allows an optional second audio stream)\n"); return -1; } - return 0; + rate.num = dvc->sys->ltc_divisor; + rate.den = 1; + if (!tcr) { // no global timecode, look into the streams + int i; + for (i = 0; i < s->nb_streams; i++) { + tcr = av_dict_get(s->streams[i]->metadata, "timecode", NULL, 0); + if (tcr) + break; + } + } + if (tcr && av_timecode_init_from_string(&dvc->tc, rate, tcr->value, s) >= 0) + return 0; + return av_timecode_init(&dvc->tc, rate, 0, 0, s); } static int dv_write_packet(struct AVFormatContext *s, AVPacket *pkt) diff --git a/libavformat/dxa.c b/libavformat/dxa.c index a1b85fe887..22ee2a97a5 100644 --- a/libavformat/dxa.c +++ b/libavformat/dxa.c @@ -2,20 +2,20 @@ * DXA demuxer * Copyright (c) 2007 Konstantin Shishkov * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -65,12 +65,12 @@ static int dxa_read_header(AVFormatContext *s) tag = avio_rl32(pb); if (tag != MKTAG('D', 'E', 'X', 'A')) - return -1; + return AVERROR_INVALIDDATA; flags = avio_r8(pb); c->frames = avio_rb16(pb); if(!c->frames){ av_log(s, AV_LOG_ERROR, "File contains no frames ???\n"); - return -1; + return AVERROR_INVALIDDATA; } fps = avio_rb32(pb); @@ -90,7 +90,7 @@ static int dxa_read_header(AVFormatContext *s) st = avformat_new_stream(s, NULL); if (!st) - return -1; + return AVERROR(ENOMEM); // Parse WAV data header if(avio_rl32(pb) == MKTAG('W', 'A', 'V', 'E')){ @@ -103,14 +103,14 @@ static int dxa_read_header(AVFormatContext *s) ast = avformat_new_stream(s, NULL); if (!ast) - return -1; + return AVERROR(ENOMEM); ret = ff_get_wav_header(pb, ast->codec, fsize); if (ret < 0) return ret; if (ast->codec->sample_rate > 0) avpriv_set_pts_info(ast, 64, 1, ast->codec->sample_rate); // find 'data' chunk - while(avio_tell(pb) < c->vidpos && !pb->eof_reached){ + while(avio_tell(pb) < c->vidpos && !url_feof(pb)){ tag = avio_rl32(pb); fsize = avio_rl32(pb); if(tag == MKTAG('d', 'a', 't', 'a')) break; @@ -168,7 +168,7 @@ static int dxa_read_packet(AVFormatContext *s, AVPacket *pkt) return 0; } avio_seek(s->pb, c->vidpos, SEEK_SET); - while(!s->pb->eof_reached && c->frames){ + while(!url_feof(s->pb) && c->frames){ avio_read(s->pb, buf, 4); switch(AV_RL32(buf)){ case MKTAG('N', 'U', 'L', 'L'): @@ -191,7 +191,7 @@ static int dxa_read_packet(AVFormatContext *s, AVPacket *pkt) size = AV_RB32(buf + 5); if(size > 0xFFFFFF){ av_log(s, AV_LOG_ERROR, "Frame size is too big: %d\n", size); - return -1; + return AVERROR_INVALIDDATA; } if(av_new_packet(pkt, size + DXA_EXTRA_SIZE + pal_size) < 0) return AVERROR(ENOMEM); @@ -209,10 +209,10 @@ static int dxa_read_packet(AVFormatContext *s, AVPacket *pkt) return 0; default: av_log(s, AV_LOG_ERROR, "Unknown tag %c%c%c%c\n", buf[0], buf[1], buf[2], buf[3]); - return -1; + return AVERROR_INVALIDDATA; } } - return AVERROR(EIO); + return AVERROR_EOF; } AVInputFormat ff_dxa_demuxer = { diff --git a/libavformat/eacdata.c b/libavformat/eacdata.c index b56ba1c90c..57e514566f 100644 --- a/libavformat/eacdata.c +++ b/libavformat/eacdata.c @@ -2,20 +2,20 @@ * Electronic Arts .cdata file Demuxer * Copyright (c) 2007 Peter Ross * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -40,7 +40,7 @@ static int cdata_probe(AVProbeData *p) { const uint8_t *b = p->buf; - if (b[0] == 0x04 && (b[1] == 0x00 || b[1] == 0x04 || b[1] == 0x0C)) + if (b[0] == 0x04 && (b[1] == 0x00 || b[1] == 0x04 || b[1] == 0x0C || b[1] == 0x14)) return AVPROBE_SCORE_MAX/8; return 0; } @@ -51,19 +51,21 @@ static int cdata_read_header(AVFormatContext *s) AVIOContext *pb = s->pb; unsigned int sample_rate, header; AVStream *st; + int64_t channel_layout = 0; header = avio_rb16(pb); switch (header) { case 0x0400: cdata->channels = 1; break; case 0x0404: cdata->channels = 2; break; - case 0x040C: cdata->channels = 4; break; + case 0x040C: cdata->channels = 4; channel_layout = AV_CH_LAYOUT_QUAD; break; + case 0x0414: cdata->channels = 6; channel_layout = AV_CH_LAYOUT_5POINT1_BACK; break; default: av_log(s, AV_LOG_INFO, "unknown header 0x%04x\n", header); return -1; }; sample_rate = avio_rb16(pb); - avio_skip(pb, 12); + avio_skip(pb, (avio_r8(pb) & 0x20) ? 15 : 11); st = avformat_new_stream(s, NULL); if (!st) @@ -72,6 +74,7 @@ static int cdata_read_header(AVFormatContext *s) st->codec->codec_tag = 0; /* no fourcc */ st->codec->codec_id = AV_CODEC_ID_ADPCM_EA_XAS; st->codec->channels = cdata->channels; + st->codec->channel_layout = channel_layout; st->codec->sample_rate = sample_rate; avpriv_set_pts_info(st, 64, 1, sample_rate); diff --git a/libavformat/electronicarts.c b/libavformat/electronicarts.c index 9d00c2d95e..97c19c932a 100644 --- a/libavformat/electronicarts.c +++ b/libavformat/electronicarts.c @@ -2,20 +2,20 @@ * Copyright (c) 2004 The ffmpeg Project * Copyright (c) 2006-2008 Peter Ross * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -66,6 +66,7 @@ typedef struct EaDemuxContext { enum AVCodecID video_codec; AVRational time_base; int width, height; + int nb_frames; int video_stream_index; enum AVCodecID audio_codec; @@ -106,7 +107,7 @@ static int process_audio_header_elements(AVFormatContext *s) ea->sample_rate = -1; ea->num_channels = 1; - while (!pb->eof_reached && in_header) { + while (!url_feof(pb) && in_header) { int in_subheader; uint8_t byte; byte = avio_r8(pb); @@ -115,7 +116,7 @@ static int process_audio_header_elements(AVFormatContext *s) case 0xFD: av_log(s, AV_LOG_DEBUG, "entered audio subheader\n"); in_subheader = 1; - while (!pb->eof_reached && in_subheader) { + while (!url_feof(pb) && in_subheader) { uint8_t subbyte; subbyte = avio_r8(pb); @@ -209,8 +210,7 @@ static int process_audio_header_elements(AVFormatContext *s) case -1: break; default: - av_log(s, AV_LOG_ERROR, - "unsupported stream type; revision=%i\n", revision); + avpriv_request_sample(s, "stream type; revision=%i", revision); return 0; } switch (revision2) { @@ -218,7 +218,14 @@ static int process_audio_header_elements(AVFormatContext *s) ea->audio_codec = AV_CODEC_ID_PCM_S16LE_PLANAR; break; case 10: - ea->audio_codec = AV_CODEC_ID_ADPCM_EA_R2; + switch (revision) { + case -1: + case 2: ea->audio_codec = AV_CODEC_ID_ADPCM_EA_R1; break; + case 3: ea->audio_codec = AV_CODEC_ID_ADPCM_EA_R2; break; + default: + avpriv_request_sample(s, "stream type; revision=%i, revision2=%i", revision, revision2); + return 0; + } break; case 16: ea->audio_codec = AV_CODEC_ID_MP3; @@ -227,15 +234,14 @@ static int process_audio_header_elements(AVFormatContext *s) break; default: ea->audio_codec = AV_CODEC_ID_NONE; - av_log(s, AV_LOG_ERROR, - "unsupported stream type; revision2=%i\n", revision2); + avpriv_request_sample(s, "stream type; revision2=%i", revision2); return 0; } break; default: - av_log(s, AV_LOG_ERROR, - "unsupported stream type; compression_type=%i\n", - compression_type); + avpriv_request_sample(s, + "stream type; compression_type=%i", + compression_type); return 0; } @@ -276,9 +282,9 @@ static void process_audio_header_eacs(AVFormatContext *s) ea->audio_codec = AV_CODEC_ID_ADPCM_IMA_EA_EACS; break; default: - av_log(s, AV_LOG_ERROR, - "unsupported stream type; audio compression_type=%i\n", - compression_type); + avpriv_request_sample(s, + "stream type; audio compression_type=%i", + compression_type); } } @@ -304,15 +310,23 @@ static void process_video_header_mdec(AVFormatContext *s) ea->video_codec = AV_CODEC_ID_MDEC; } -static void process_video_header_vp6(AVFormatContext *s) +static int process_video_header_vp6(AVFormatContext *s) { EaDemuxContext *ea = s->priv_data; AVIOContext *pb = s->pb; - avio_skip(pb, 16); + avio_skip(pb, 8); + ea->nb_frames = avio_rl32(pb); + avio_skip(pb, 4); ea->time_base.den = avio_rl32(pb); ea->time_base.num = avio_rl32(pb); + if (ea->time_base.den <= 0 || ea->time_base.num <= 0) { + av_log(s, AV_LOG_ERROR, "Timebase is invalid\n"); + return AVERROR_INVALIDDATA; + } ea->video_codec = AV_CODEC_ID_VP6; + + return 1; } static void process_video_header_cmv(AVFormatContext *s) @@ -350,7 +364,7 @@ static int process_ea_header(AVFormatContext *s) switch (blockid) { case ISNh_TAG: if (avio_rl32(pb) != EACS_TAG) { - av_log(s, AV_LOG_ERROR, "unknown 1SNh headerid\n"); + avpriv_request_sample(s, "unknown 1SNh headerid"); return 0; } process_audio_header_eacs(s); @@ -362,7 +376,7 @@ static int process_ea_header(AVFormatContext *s) if (blockid == GSTR_TAG) { avio_skip(pb, 4); } else if ((blockid & 0xFFFF) != PT00_TAG) { - av_log(s, AV_LOG_ERROR, "unknown SCHl headerid\n"); + avpriv_request_sample(s, "unknown SCHl headerid"); return 0; } err = process_audio_header_elements(s); @@ -378,7 +392,6 @@ static int process_ea_header(AVFormatContext *s) case kVGT_TAG: ea->video_codec = AV_CODEC_ID_TGV; - ea->time_base = (AVRational) { 1, 15 }; break; case mTCD_TAG: @@ -403,7 +416,7 @@ static int process_ea_header(AVFormatContext *s) break; case MVhd_TAG: - process_video_header_vp6(s); + err = process_video_header_vp6(s); break; } @@ -447,7 +460,7 @@ static int ea_read_header(AVFormatContext *s) EaDemuxContext *ea = s->priv_data; AVStream *st; - if (!process_ea_header(s)) + if (process_ea_header(s)<=0) return AVERROR(EIO); if (ea->video_codec) { @@ -458,12 +471,17 @@ static int ea_read_header(AVFormatContext *s) ea->video_stream_index = st->index; st->codec->codec_type = AVMEDIA_TYPE_VIDEO; st->codec->codec_id = ea->video_codec; + // parsing is necessary to make FFmpeg generate correct timestamps + if (st->codec->codec_id == AV_CODEC_ID_MPEG2VIDEO) + st->need_parsing = AVSTREAM_PARSE_HEADERS; st->codec->codec_tag = 0; /* no fourcc */ st->codec->width = ea->width; st->codec->height = ea->height; - avpriv_set_pts_info(st, 33, ea->time_base.num, ea->time_base.den); - st->avg_frame_rate = (AVRational) { ea->time_base.den, - ea->time_base.num }; + st->duration = st->nb_frames = ea->nb_frames; + if (ea->time_base.num) + avpriv_set_pts_info(st, 64, ea->time_base.num, ea->time_base.den); + st->r_frame_rate = + st->avg_frame_rate = av_inv_q(ea->time_base); } if (ea->audio_codec) { @@ -513,11 +531,12 @@ static int ea_read_packet(AVFormatContext *s, AVPacket *pkt) { EaDemuxContext *ea = s->priv_data; AVIOContext *pb = s->pb; + int partial_packet = 0; unsigned int chunk_type, chunk_size; int ret = 0, packet_read = 0, key = 0; int av_uninit(num_samples); - while (!packet_read) { + while (!packet_read || partial_packet) { chunk_type = avio_rl32(pb); chunk_size = ea->big_endian ? avio_rb32(pb) : avio_rl32(pb); if (chunk_size <= 8) @@ -545,6 +564,11 @@ static int ea_read_packet(AVFormatContext *s, AVPacket *pkt) avio_skip(pb, 8); chunk_size -= 12; } + if (partial_packet) { + avpriv_request_sample(s, "video header followed by audio packet"); + av_free_packet(pkt); + partial_packet = 0; + } ret = av_get_packet(pb, pkt, chunk_size); if (ret < 0) return ret; @@ -615,9 +639,15 @@ static int ea_read_packet(AVFormatContext *s, AVPacket *pkt) key = AV_PKT_FLAG_KEY; case MV0F_TAG: get_video_packet: - ret = av_get_packet(pb, pkt, chunk_size); - if (ret < 0) - return ret; + if (partial_packet) { + ret = av_append_packet(pb, pkt, chunk_size); + } else + ret = av_get_packet(pb, pkt, chunk_size); + if (ret < 0) { + packet_read = 1; + break; + } + partial_packet = chunk_type == MVIh_TAG; pkt->stream_index = ea->video_stream_index; pkt->flags |= key; packet_read = 1; @@ -629,6 +659,8 @@ get_video_packet: } } + if (ret < 0 && partial_packet) + av_free_packet(pkt); return ret; } diff --git a/libavformat/epafdec.c b/libavformat/epafdec.c new file mode 100644 index 0000000000..c737892325 --- /dev/null +++ b/libavformat/epafdec.c @@ -0,0 +1,104 @@ +/* + * Ensoniq Paris Audio File demuxer + * Copyright (c) 2012 Paul B Mahol + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/intreadwrite.h" +#include "avformat.h" +#include "internal.h" +#include "pcm.h" + +static int epaf_probe(AVProbeData *p) +{ + if (((AV_RL32(p->buf) == MKTAG('f','a','p',' ') && + AV_RL32(p->buf + 8) == 1) || + (AV_RL32(p->buf) == MKTAG(' ','p','a','f') && + AV_RN32(p->buf + 8) == 0)) && + !AV_RN32(p->buf + 4) && AV_RN32(p->buf + 12) && + AV_RN32(p->buf + 20)) + return AVPROBE_SCORE_MAX / 4 * 3; + return 0; +} + +static int epaf_read_header(AVFormatContext *s) +{ + int le, sample_rate, codec, channels; + AVStream *st; + + avio_skip(s->pb, 4); + if (avio_rl32(s->pb)) + return AVERROR_INVALIDDATA; + + le = avio_rl32(s->pb); + if (le && le != 1) + return AVERROR_INVALIDDATA; + + if (le) { + sample_rate = avio_rl32(s->pb); + codec = avio_rl32(s->pb); + channels = avio_rl32(s->pb); + } else { + sample_rate = avio_rb32(s->pb); + codec = avio_rb32(s->pb); + channels = avio_rb32(s->pb); + } + + if (!channels || !sample_rate) + return AVERROR_INVALIDDATA; + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->channels = channels; + st->codec->sample_rate = sample_rate; + switch (codec) { + case 0: + st->codec->codec_id = le ? AV_CODEC_ID_PCM_S16LE : AV_CODEC_ID_PCM_S16BE; + break; + case 2: + st->codec->codec_id = AV_CODEC_ID_PCM_S8; + break; + case 1: + avpriv_request_sample(s, "24-bit Paris PCM format"); + default: + return AVERROR_INVALIDDATA; + } + + st->codec->bits_per_coded_sample = av_get_bits_per_sample(st->codec->codec_id); + st->codec->block_align = st->codec->bits_per_coded_sample * st->codec->channels / 8; + + avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate); + + if (avio_skip(s->pb, 2024) < 0) + return AVERROR_INVALIDDATA; + return 0; +} + +AVInputFormat ff_epaf_demuxer = { + .name = "epaf", + .long_name = NULL_IF_CONFIG_SMALL("Ensoniq Paris Audio File"), + .read_probe = epaf_probe, + .read_header = epaf_read_header, + .read_packet = ff_pcm_read_packet, + .read_seek = ff_pcm_read_seek, + .extensions = "paf,fap", + .flags = AVFMT_GENERIC_INDEX, +}; diff --git a/libavformat/ffm.h b/libavformat/ffm.h index 70c3e887a0..b392b8d8d8 100644 --- a/libavformat/ffm.h +++ b/libavformat/ffm.h @@ -1,21 +1,21 @@ /* - * FFM (avserver live feed) common header + * FFM (ffserver live feed) common header * Copyright (c) 2001 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -54,6 +54,7 @@ typedef struct FFMContext { int64_t dts; uint8_t *packet_ptr, *packet_end; uint8_t packet[FFM_PACKET_SIZE]; + int64_t start_time; } FFMContext; #endif /* AVFORMAT_FFM_H */ diff --git a/libavformat/ffmdec.c b/libavformat/ffmdec.c index e6730eb2f5..b20ee131b6 100644 --- a/libavformat/ffmdec.c +++ b/libavformat/ffmdec.c @@ -1,21 +1,21 @@ /* - * FFM (avserver live feed) demuxer + * FFM (ffserver live feed) demuxer * Copyright (c) 2001 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -24,6 +24,7 @@ #include "avformat.h" #include "internal.h" #include "ffm.h" +#include "avio_internal.h" static int ffm_is_avail_data(AVFormatContext *s, int size) { @@ -60,7 +61,7 @@ static int ffm_resync(AVFormatContext *s, int state) { av_log(s, AV_LOG_ERROR, "resyncing\n"); while (state != PACKET_ID) { - if (s->pb->eof_reached) { + if (url_feof(s->pb)) { av_log(s, AV_LOG_ERROR, "cannot find FFM syncword\n"); return -1; } @@ -89,6 +90,11 @@ static int ffm_read_data(AVFormatContext *s, if (avio_tell(pb) == ffm->file_size) avio_seek(pb, ffm->packet_size, SEEK_SET); retry_read: + if (pb->buffer_size != ffm->packet_size) { + int64_t tell = avio_tell(pb); + ffio_set_buf_size(pb, ffm->packet_size); + avio_seek(pb, tell, SEEK_SET); + } id = avio_rb16(pb); /* PACKET_ID */ if (id != PACKET_ID) if (ffm_resync(s, id) < 0) @@ -105,8 +111,8 @@ static int ffm_read_data(AVFormatContext *s, if (ffm->first_packet || (frame_offset & 0x8000)) { if (!frame_offset) { /* This packet has no frame headers in it */ - if (avio_tell(pb) >= ffm->packet_size * 3) { - avio_seek(pb, -ffm->packet_size * 2, SEEK_CUR); + if (avio_tell(pb) >= ffm->packet_size * 3LL) { + avio_seek(pb, -ffm->packet_size * 2LL, SEEK_CUR); goto retry_read; } /* This is bad, we cannot find a valid frame header */ @@ -222,6 +228,138 @@ static int ffm_close(AVFormatContext *s) return 0; } +static int ffm2_read_header(AVFormatContext *s) +{ + FFMContext *ffm = s->priv_data; + AVStream *st; + AVIOContext *pb = s->pb; + AVCodecContext *codec; + + ffm->packet_size = avio_rb32(pb); + if (ffm->packet_size != FFM_PACKET_SIZE) + goto fail; + ffm->write_index = avio_rb64(pb); + /* get also filesize */ + if (pb->seekable) { + ffm->file_size = avio_size(pb); + if (ffm->write_index && 0) + adjust_write_index(s); + } else { + ffm->file_size = (UINT64_C(1) << 63) - 1; + } + + while(!url_feof(pb)) { + unsigned id = avio_rb32(pb); + unsigned size = avio_rb32(pb); + int64_t next = avio_tell(pb) + size; + char rc_eq_buf[128]; + + if(!id) + break; + + switch(id) { + case MKBETAG('M', 'A', 'I', 'N'): + avio_rb32(pb); /* nb_streams */ + avio_rb32(pb); /* total bitrate */ + break; + case MKBETAG('C', 'O', 'M', 'M'): + st = avformat_new_stream(s, NULL); + if (!st) + goto fail; + + avpriv_set_pts_info(st, 64, 1, 1000000); + + codec = st->codec; + /* generic info */ + codec->codec_id = avio_rb32(pb); + codec->codec_type = avio_r8(pb); + codec->bit_rate = avio_rb32(pb); + codec->flags = avio_rb32(pb); + codec->flags2 = avio_rb32(pb); + codec->debug = avio_rb32(pb); + if (codec->flags & CODEC_FLAG_GLOBAL_HEADER) { + if (ff_alloc_extradata(codec, avio_rb32(pb))) + return AVERROR(ENOMEM); + avio_read(pb, codec->extradata, codec->extradata_size); + } + avio_seek(pb, next, SEEK_SET); + id = avio_rb32(pb); + size = avio_rb32(pb); + next = avio_tell(pb) + size; + switch(id) { + case MKBETAG('S', 'T', 'V', 'I'): + codec->time_base.num = avio_rb32(pb); + codec->time_base.den = avio_rb32(pb); + codec->width = avio_rb16(pb); + codec->height = avio_rb16(pb); + codec->gop_size = avio_rb16(pb); + codec->pix_fmt = avio_rb32(pb); + codec->qmin = avio_r8(pb); + codec->qmax = avio_r8(pb); + codec->max_qdiff = avio_r8(pb); + codec->qcompress = avio_rb16(pb) / 10000.0; + codec->qblur = avio_rb16(pb) / 10000.0; + codec->bit_rate_tolerance = avio_rb32(pb); + avio_get_str(pb, INT_MAX, rc_eq_buf, sizeof(rc_eq_buf)); + codec->rc_eq = av_strdup(rc_eq_buf); + codec->rc_max_rate = avio_rb32(pb); + codec->rc_min_rate = avio_rb32(pb); + codec->rc_buffer_size = avio_rb32(pb); + codec->i_quant_factor = av_int2double(avio_rb64(pb)); + codec->b_quant_factor = av_int2double(avio_rb64(pb)); + codec->i_quant_offset = av_int2double(avio_rb64(pb)); + codec->b_quant_offset = av_int2double(avio_rb64(pb)); + codec->dct_algo = avio_rb32(pb); + codec->strict_std_compliance = avio_rb32(pb); + codec->max_b_frames = avio_rb32(pb); + codec->mpeg_quant = avio_rb32(pb); + codec->intra_dc_precision = avio_rb32(pb); + codec->me_method = avio_rb32(pb); + codec->mb_decision = avio_rb32(pb); + codec->nsse_weight = avio_rb32(pb); + codec->frame_skip_cmp = avio_rb32(pb); + codec->rc_buffer_aggressivity = av_int2double(avio_rb64(pb)); + codec->codec_tag = avio_rb32(pb); + codec->thread_count = avio_r8(pb); + codec->coder_type = avio_rb32(pb); + codec->me_cmp = avio_rb32(pb); + codec->me_subpel_quality = avio_rb32(pb); + codec->me_range = avio_rb32(pb); + codec->keyint_min = avio_rb32(pb); + codec->scenechange_threshold = avio_rb32(pb); + codec->b_frame_strategy = avio_rb32(pb); + codec->qcompress = av_int2double(avio_rb64(pb)); + codec->qblur = av_int2double(avio_rb64(pb)); + codec->max_qdiff = avio_rb32(pb); + codec->refs = avio_rb32(pb); + break; + case MKBETAG('S', 'T', 'A', 'U'): + codec->sample_rate = avio_rb32(pb); + codec->channels = avio_rl16(pb); + codec->frame_size = avio_rl16(pb); + break; + } + break; + } + avio_seek(pb, next, SEEK_SET); + } + + /* get until end of block reached */ + while ((avio_tell(pb) % ffm->packet_size) != 0) + avio_r8(pb); + + /* init packet demux */ + ffm->packet_ptr = ffm->packet; + ffm->packet_end = ffm->packet; + ffm->frame_offset = 0; + ffm->dts = 0; + ffm->read_state = READ_HEADER; + ffm->first_packet = 1; + return 0; + fail: + ffm_close(s); + return -1; +} static int ffm_read_header(AVFormatContext *s) { @@ -234,6 +372,8 @@ static int ffm_read_header(AVFormatContext *s) /* header */ tag = avio_rl32(pb); + if (tag == MKTAG('F', 'F', 'M', '2')) + return ffm2_read_header(s); if (tag != MKTAG('F', 'F', 'M', '1')) goto fail; ffm->packet_size = avio_rb32(pb); @@ -243,7 +383,7 @@ static int ffm_read_header(AVFormatContext *s) /* get also filesize */ if (pb->seekable) { ffm->file_size = avio_size(pb); - if (ffm->write_index) + if (ffm->write_index && 0) adjust_write_index(s); } else { ffm->file_size = (UINT64_C(1) << 63) - 1; @@ -326,9 +466,7 @@ static int ffm_read_header(AVFormatContext *s) goto fail; } if (codec->flags & CODEC_FLAG_GLOBAL_HEADER) { - codec->extradata_size = avio_rb32(pb); - codec->extradata = av_malloc(codec->extradata_size); - if (!codec->extradata) + if (ff_alloc_extradata(codec, avio_rb32(pb))) return AVERROR(ENOMEM); avio_read(pb, codec->extradata, codec->extradata_size); } @@ -380,7 +518,9 @@ static int ffm_read_packet(AVFormatContext *s, AVPacket *pkt) duration = AV_RB24(ffm->header + 5); - av_new_packet(pkt, size); + if (av_new_packet(pkt, size) < 0) { + return AVERROR(ENOMEM); + } pkt->stream_index = ffm->header[0]; if ((unsigned)pkt->stream_index >= s->nb_streams) { av_log(s, AV_LOG_ERROR, "invalid stream index %d\n", pkt->stream_index); @@ -422,11 +562,25 @@ static int ffm_seek(AVFormatContext *s, int stream_index, int64_t wanted_pts, in av_dlog(s, "wanted_pts=%0.6f\n", wanted_pts / 1000000.0); /* find the position using linear interpolation (better than dichotomy in typical cases) */ - pos_min = FFM_PACKET_SIZE; - pos_max = ffm->file_size - FFM_PACKET_SIZE; + if (ffm->write_index && ffm->write_index < ffm->file_size) { + if (get_dts(s, FFM_PACKET_SIZE) < wanted_pts) { + pos_min = FFM_PACKET_SIZE; + pos_max = ffm->write_index - FFM_PACKET_SIZE; + } else { + pos_min = ffm->write_index; + pos_max = ffm->file_size - FFM_PACKET_SIZE; + } + } else { + pos_min = FFM_PACKET_SIZE; + pos_max = ffm->file_size - FFM_PACKET_SIZE; + } while (pos_min <= pos_max) { pts_min = get_dts(s, pos_min); pts_max = get_dts(s, pos_max); + if (pts_min > wanted_pts || pts_max <= wanted_pts) { + pos = pts_min > wanted_pts ? pos_min : pos_max; + goto found; + } /* linear interpolation */ pos1 = (double)(pos_max - pos_min) * (double)(wanted_pts - pts_min) / (double)(pts_max - pts_min); @@ -464,14 +618,14 @@ static int ffm_probe(AVProbeData *p) { if ( p->buf[0] == 'F' && p->buf[1] == 'F' && p->buf[2] == 'M' && - p->buf[3] == '1') + (p->buf[3] == '1' || p->buf[3] == '2')) return AVPROBE_SCORE_MAX + 1; return 0; } AVInputFormat ff_ffm_demuxer = { .name = "ffm", - .long_name = NULL_IF_CONFIG_SMALL("FFM (AVserver live feed)"), + .long_name = NULL_IF_CONFIG_SMALL("FFM (FFserver live feed)"), .priv_data_size = sizeof(FFMContext), .read_probe = ffm_probe, .read_header = ffm_read_header, diff --git a/libavformat/ffmenc.c b/libavformat/ffmenc.c index 91658e12dd..eb809eb64c 100644 --- a/libavformat/ffmenc.c +++ b/libavformat/ffmenc.c @@ -1,28 +1,28 @@ /* - * FFM (avserver live feed) muxer + * FFM (ffserver live feed) muxer * Copyright (c) 2001 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <assert.h> - #include "libavutil/intreadwrite.h" #include "libavutil/intfloat.h" +#include "libavutil/avassert.h" +#include "libavutil/parseutils.h" #include "avformat.h" #include "internal.h" #include "ffm.h" @@ -36,7 +36,7 @@ static void flush_packet(AVFormatContext *s) fill_size = ffm->packet_end - ffm->packet_ptr; memset(ffm->packet_ptr, 0, fill_size); - assert(avio_tell(pb) % ffm->packet_size == 0); + av_assert1(avio_tell(pb) % ffm->packet_size == 0); /* put header */ avio_wb16(pb, PACKET_ID); @@ -83,21 +83,41 @@ static void ffm_write_data(AVFormatContext *s, } } +static void write_header_chunk(AVIOContext *pb, AVIOContext *dpb, unsigned id) +{ + uint8_t *dyn_buf; + int dyn_size= avio_close_dyn_buf(dpb, &dyn_buf); + avio_wb32(pb, id); + avio_wb32(pb, dyn_size); + avio_write(pb, dyn_buf, dyn_size); + av_free(dyn_buf); +} + static int ffm_write_header(AVFormatContext *s) { FFMContext *ffm = s->priv_data; + AVDictionaryEntry *t; AVStream *st; AVIOContext *pb = s->pb; AVCodecContext *codec; int bit_rate, i; + if (t = av_dict_get(s->metadata, "creation_time", NULL, 0)) { + int ret = av_parse_time(&ffm->start_time, t->value, 0); + if (ret < 0) + return ret; + } + ffm->packet_size = FFM_PACKET_SIZE; /* header */ - avio_wl32(pb, MKTAG('F', 'F', 'M', '1')); + avio_wl32(pb, MKTAG('F', 'F', 'M', '2')); avio_wb32(pb, ffm->packet_size); avio_wb64(pb, 0); /* current write position */ + if(avio_open_dyn_buf(&pb) < 0) + return AVERROR(ENOMEM); + avio_wb32(pb, s->nb_streams); bit_rate = 0; for(i=0;i<s->nb_streams;i++) { @@ -106,10 +126,14 @@ static int ffm_write_header(AVFormatContext *s) } avio_wb32(pb, bit_rate); + write_header_chunk(s->pb, pb, MKBETAG('M', 'A', 'I', 'N')); + /* list of streams */ for(i=0;i<s->nb_streams;i++) { st = s->streams[i]; avpriv_set_pts_info(st, 64, 1, 1000000); + if(avio_open_dyn_buf(&pb) < 0) + return AVERROR(ENOMEM); codec = st->codec; /* generic info */ @@ -119,6 +143,13 @@ static int ffm_write_header(AVFormatContext *s) avio_wb32(pb, codec->flags); avio_wb32(pb, codec->flags2); avio_wb32(pb, codec->debug); + if (codec->flags & CODEC_FLAG_GLOBAL_HEADER) { + avio_wb32(pb, codec->extradata_size); + avio_write(pb, codec->extradata, codec->extradata_size); + } + write_header_chunk(s->pb, pb, MKBETAG('C', 'O', 'M', 'M')); + if(avio_open_dyn_buf(&pb) < 0) + return AVERROR(ENOMEM); /* specific info */ switch(codec->codec_type) { case AVMEDIA_TYPE_VIDEO: @@ -165,20 +196,21 @@ static int ffm_write_header(AVFormatContext *s) avio_wb64(pb, av_double2int(codec->qblur)); avio_wb32(pb, codec->max_qdiff); avio_wb32(pb, codec->refs); + write_header_chunk(s->pb, pb, MKBETAG('S', 'T', 'V', 'I')); break; case AVMEDIA_TYPE_AUDIO: avio_wb32(pb, codec->sample_rate); avio_wl16(pb, codec->channels); avio_wl16(pb, codec->frame_size); + write_header_chunk(s->pb, pb, MKBETAG('S', 'T', 'A', 'U')); break; default: return -1; } - if (codec->flags & CODEC_FLAG_GLOBAL_HEADER) { - avio_wb32(pb, codec->extradata_size); - avio_write(pb, codec->extradata, codec->extradata_size); - } } + pb = s->pb; + + avio_wb64(pb, 0); // end of header /* flush until end of block reached */ while ((avio_tell(pb) % ffm->packet_size) != 0) @@ -189,7 +221,7 @@ static int ffm_write_header(AVFormatContext *s) /* init packet mux */ ffm->packet_ptr = ffm->packet; ffm->packet_end = ffm->packet + ffm->packet_size - FFM_HEADER_SIZE; - assert(ffm->packet_end >= ffm->packet); + av_assert0(ffm->packet_end >= ffm->packet); ffm->frame_offset = 0; ffm->dts = 0; ffm->first_packet = 1; @@ -199,11 +231,12 @@ static int ffm_write_header(AVFormatContext *s) static int ffm_write_packet(AVFormatContext *s, AVPacket *pkt) { + FFMContext *ffm = s->priv_data; int64_t dts; uint8_t header[FRAME_HEADER_SIZE+4]; int header_size = FRAME_HEADER_SIZE; - dts = pkt->dts; + dts = ffm->start_time + pkt->dts; /* packet size & key_frame */ header[0] = pkt->stream_index; header[1] = 0; @@ -211,7 +244,7 @@ static int ffm_write_packet(AVFormatContext *s, AVPacket *pkt) header[1] |= FLAG_KEY_FRAME; AV_WB24(header+2, pkt->size); AV_WB24(header+5, pkt->duration); - AV_WB64(header+8, pkt->pts); + AV_WB64(header+8, ffm->start_time + pkt->pts); if (pkt->pts != pkt->dts) { header[1] |= FLAG_DTS; AV_WB32(header+16, pkt->pts - pkt->dts); @@ -236,8 +269,7 @@ static int ffm_write_trailer(AVFormatContext *s) AVOutputFormat ff_ffm_muxer = { .name = "ffm", - .long_name = NULL_IF_CONFIG_SMALL("FFM (AVserver live feed)"), - .mime_type = "", + .long_name = NULL_IF_CONFIG_SMALL("FFM (FFserver live feed)"), .extensions = "ffm", .priv_data_size = sizeof(FFMContext), .audio_codec = AV_CODEC_ID_MP2, diff --git a/libavformat/ffmeta.h b/libavformat/ffmeta.h index a5380ca13d..ae8778d614 100644 --- a/libavformat/ffmeta.h +++ b/libavformat/ffmeta.h @@ -2,20 +2,20 @@ * Common data for metadata muxer/demuxer * Copyright (c) 2010 Anton Khirnov * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/ffmetadec.c b/libavformat/ffmetadec.c index 7dbf7ed35d..19c14e4ade 100644 --- a/libavformat/ffmetadec.c +++ b/libavformat/ffmetadec.c @@ -2,20 +2,20 @@ * Metadata demuxer * Copyright (c) 2010 Anton Khirnov * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -50,7 +50,7 @@ static void get_line(AVIOContext *s, uint8_t *buf, int size) buf[i++] = c; } buf[i] = 0; - } while (!s->eof_reached && (buf[0] == ';' || buf[0] == '#' || buf[0] == 0)); + } while (!url_feof(s) && (buf[0] == ';' || buf[0] == '#' || buf[0] == 0)); } static AVChapter *read_chapter(AVFormatContext *s) @@ -128,14 +128,14 @@ static int read_header(AVFormatContext *s) AVDictionary **m = &s->metadata; uint8_t line[1024]; - while(!s->pb->eof_reached) { + while(!url_feof(s->pb)) { get_line(s->pb, line, sizeof(line)); if (!memcmp(line, ID_STREAM, strlen(ID_STREAM))) { AVStream *st = avformat_new_stream(s, NULL); if (!st) - return -1; + return AVERROR(ENOMEM); st->codec->codec_type = AVMEDIA_TYPE_DATA; st->codec->codec_id = AV_CODEC_ID_FFMETADATA; @@ -145,7 +145,7 @@ static int read_header(AVFormatContext *s) AVChapter *ch = read_chapter(s); if (!ch) - return -1; + return AVERROR(ENOMEM); m = &ch->metadata; } else diff --git a/libavformat/ffmetaenc.c b/libavformat/ffmetaenc.c index 19fe6c960f..a9adbb1d19 100644 --- a/libavformat/ffmetaenc.c +++ b/libavformat/ffmetaenc.c @@ -2,20 +2,20 @@ * Metadata muxer * Copyright (c) 2010 Anton Khirnov * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/file.c b/libavformat/file.c index 2837e9f150..2defc75e5f 100644 --- a/libavformat/file.c +++ b/libavformat/file.c @@ -2,20 +2,20 @@ * buffered file I/O * Copyright (c) 2001 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -35,6 +35,14 @@ #include "os_support.h" #include "url.h" +/* Some systems may not have S_ISFIFO */ +#ifndef S_ISFIFO +# ifdef S_IFIFO +# define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO) +# else +# define S_ISFIFO(m) 0 +# endif +#endif /* standard file protocol */ @@ -42,10 +50,17 @@ typedef struct FileContext { const AVClass *class; int fd; int trunc; + int blocksize; } FileContext; static const AVOption file_options[] = { { "truncate", "Truncate existing files on write", offsetof(FileContext, trunc), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 1, AV_OPT_FLAG_ENCODING_PARAM }, + { "blocksize", "set I/O operation maximum block size", offsetof(FileContext, blocksize), AV_OPT_TYPE_INT, { .i64 = INT_MAX }, 1, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM }, + { NULL } +}; + +static const AVOption pipe_options[] = { + { "blocksize", "set I/O operation maximum block size", offsetof(FileContext, blocksize), AV_OPT_TYPE_INT, { .i64 = INT_MAX }, 1, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM }, { NULL } }; @@ -56,16 +71,29 @@ static const AVClass file_class = { .version = LIBAVUTIL_VERSION_INT, }; +static const AVClass pipe_class = { + .class_name = "pipe", + .item_name = av_default_item_name, + .option = pipe_options, + .version = LIBAVUTIL_VERSION_INT, +}; + static int file_read(URLContext *h, unsigned char *buf, int size) { FileContext *c = h->priv_data; - return read(c->fd, buf, size); + int r; + size = FFMIN(size, c->blocksize); + r = read(c->fd, buf, size); + return (-1 == r)?AVERROR(errno):r; } static int file_write(URLContext *h, const unsigned char *buf, int size) { FileContext *c = h->priv_data; - return write(c->fd, buf, size); + int r; + size = FFMIN(size, c->blocksize); + r = write(c->fd, buf, size); + return (-1 == r)?AVERROR(errno):r; } static int file_get_handle(URLContext *h) @@ -76,6 +104,17 @@ static int file_get_handle(URLContext *h) static int file_check(URLContext *h, int mask) { +#if HAVE_ACCESS && defined(R_OK) + int ret = 0; + if (access(h->filename, F_OK) < 0) + return AVERROR(errno); + if (mask&AVIO_FLAG_READ) + if (access(h->filename, R_OK) >= 0) + ret |= AVIO_FLAG_READ; + if (mask&AVIO_FLAG_WRITE) + if (access(h->filename, W_OK) >= 0) + ret |= AVIO_FLAG_WRITE; +#else struct stat st; int ret = stat(h->filename, &st); if (ret < 0) @@ -83,7 +122,7 @@ static int file_check(URLContext *h, int mask) ret |= st.st_mode&S_IRUSR ? mask&AVIO_FLAG_READ : 0; ret |= st.st_mode&S_IWUSR ? mask&AVIO_FLAG_WRITE : 0; - +#endif return ret; } @@ -94,6 +133,7 @@ static int file_open(URLContext *h, const char *filename, int flags) FileContext *c = h->priv_data; int access; int fd; + struct stat st; av_strstart(filename, "file:", &filename); @@ -115,6 +155,9 @@ static int file_open(URLContext *h, const char *filename, int flags) if (fd == -1) return AVERROR(errno); c->fd = fd; + + h->is_streamed = !fstat(fd, &st) && S_ISFIFO(st.st_mode); + return 0; } @@ -126,9 +169,8 @@ static int64_t file_seek(URLContext *h, int64_t pos, int whence) if (whence == AVSEEK_SIZE) { struct stat st; - ret = fstat(c->fd, &st); - return ret < 0 ? AVERROR(errno) : st.st_size; + return ret < 0 ? AVERROR(errno) : (S_ISFIFO(st.st_mode) ? 0 : st.st_size); } ret = lseek(c->fd, pos, whence); @@ -190,6 +232,7 @@ URLProtocol ff_pipe_protocol = { .url_get_file_handle = file_get_handle, .url_check = file_check, .priv_data_size = sizeof(FileContext), + .priv_data_class = &pipe_class, }; #endif /* CONFIG_PIPE_PROTOCOL */ diff --git a/libavformat/filmstripdec.c b/libavformat/filmstripdec.c index 3fa68424b7..1cd1a4837b 100644 --- a/libavformat/filmstripdec.c +++ b/libavformat/filmstripdec.c @@ -2,20 +2,20 @@ * Adobe Filmstrip demuxer * Copyright (c) 2010 Peter Ross * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -45,7 +45,7 @@ static int read_header(AVFormatContext *s) avio_seek(pb, avio_size(pb) - 36, SEEK_SET); if (avio_rb32(pb) != RAND_TAG) { - av_log(s, AV_LOG_ERROR, "magic number not found"); + av_log(s, AV_LOG_ERROR, "magic number not found\n"); return AVERROR_INVALIDDATA; } @@ -80,7 +80,7 @@ static int read_packet(AVFormatContext *s, FilmstripDemuxContext *film = s->priv_data; AVStream *st = s->streams[0]; - if (s->pb->eof_reached) + if (url_feof(s->pb)) return AVERROR(EIO); pkt->dts = avio_tell(s->pb) / (st->codec->width * (st->codec->height + film->leading) * 4); pkt->size = av_get_packet(s->pb, pkt, st->codec->width * st->codec->height * 4); diff --git a/libavformat/filmstripenc.c b/libavformat/filmstripenc.c index 90d9a7685c..85d49beec7 100644 --- a/libavformat/filmstripenc.c +++ b/libavformat/filmstripenc.c @@ -2,20 +2,20 @@ * Adobe Filmstrip muxer * Copyright (c) 2010 Peter Ross * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -64,7 +64,7 @@ static int write_trailer(AVFormatContext *s) avio_wb16(pb, st->codec->width); avio_wb16(pb, st->codec->height); avio_wb16(pb, 0); // leading - avio_wb16(pb, 1/av_q2d(st->codec->time_base)); + avio_wb16(pb, st->codec->time_base.den / st->codec->time_base.num); for (i = 0; i < 16; i++) avio_w8(pb, 0x00); // reserved diff --git a/libavformat/flac_picture.c b/libavformat/flac_picture.c index 69d27244ff..12d73e488f 100644 --- a/libavformat/flac_picture.c +++ b/libavformat/flac_picture.c @@ -1,23 +1,25 @@ /* * Raw FLAC picture parser + * Copyright (c) 2001 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "libavutil/avassert.h" #include "avformat.h" #include "flac_picture.h" #include "id3v2.h" @@ -43,8 +45,7 @@ int ff_flac_parse_picture(AVFormatContext *s, uint8_t *buf, int buf_size) if (type >= FF_ARRAY_ELEMS(ff_id3v2_picture_types) || type < 0) { av_log(s, AV_LOG_ERROR, "Invalid picture type: %d.\n", type); if (s->error_recognition & AV_EF_EXPLODE) { - ret = AVERROR_INVALIDDATA; - goto fail; + RETURN_ERROR(AVERROR_INVALIDDATA); } type = 0; } @@ -59,6 +60,7 @@ int ff_flac_parse_picture(AVFormatContext *s, uint8_t *buf, int buf_size) ret = AVERROR_INVALIDDATA; goto fail; } + av_assert0(len < sizeof(mimetype)); mimetype[len] = 0; while (mime->id != AV_CODEC_ID_NONE) { @@ -80,8 +82,7 @@ int ff_flac_parse_picture(AVFormatContext *s, uint8_t *buf, int buf_size) len = avio_rb32(pb); if (len > 0) { if (!(desc = av_malloc(len + 1))) { - ret = AVERROR(ENOMEM); - goto fail; + RETURN_ERROR(AVERROR(ENOMEM)); } if (avio_read(pb, desc, len) != len) { @@ -107,8 +108,7 @@ int ff_flac_parse_picture(AVFormatContext *s, uint8_t *buf, int buf_size) goto fail; } if (!(data = av_buffer_alloc(len))) { - ret = AVERROR(ENOMEM); - goto fail; + RETURN_ERROR(AVERROR(ENOMEM)); } if (avio_read(pb, data->data, len) != len) { av_log(s, AV_LOG_ERROR, "Error reading attached picture data.\n"); @@ -119,8 +119,7 @@ int ff_flac_parse_picture(AVFormatContext *s, uint8_t *buf, int buf_size) st = avformat_new_stream(s, NULL); if (!st) { - ret = AVERROR(ENOMEM); - goto fail; + RETURN_ERROR(AVERROR(ENOMEM)); } av_init_packet(&st->attached_pic); diff --git a/libavformat/flac_picture.h b/libavformat/flac_picture.h index c7005829bc..4374b6f4f6 100644 --- a/libavformat/flac_picture.h +++ b/libavformat/flac_picture.h @@ -1,20 +1,21 @@ /* * Raw FLAC picture parser + * Copyright (c) 2001 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -23,6 +24,8 @@ #include "avformat.h" +#define RETURN_ERROR(code) do { ret = (code); goto fail; } while (0) + int ff_flac_parse_picture(AVFormatContext *s, uint8_t *buf, int buf_size); #endif /* AVFORMAT_FLAC_PICTURE_H */ diff --git a/libavformat/flacdec.c b/libavformat/flacdec.c index ecbb7ec0ef..29310b831f 100644 --- a/libavformat/flacdec.c +++ b/libavformat/flacdec.c @@ -2,20 +2,20 @@ * Raw FLAC demuxer * Copyright (c) 2001 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -38,7 +38,7 @@ static int flac_read_header(AVFormatContext *s) return AVERROR(ENOMEM); st->codec->codec_type = AVMEDIA_TYPE_AUDIO; st->codec->codec_id = AV_CODEC_ID_FLAC; - st->need_parsing = AVSTREAM_PARSE_FULL; + st->need_parsing = AVSTREAM_PARSE_FULL_RAW; /* the parameters will be extracted from the compressed bitstream */ /* if fLaC marker is not found, assume there is no header */ @@ -48,7 +48,7 @@ static int flac_read_header(AVFormatContext *s) } /* process metadata blocks */ - while (!s->pb->eof_reached && !metadata_last) { + while (!url_feof(s->pb) && !metadata_last) { avio_read(s->pb, header, 4); avpriv_flac_parse_block_header(header, &metadata_last, &metadata_type, &metadata_size); @@ -63,8 +63,7 @@ static int flac_read_header(AVFormatContext *s) return AVERROR(ENOMEM); } if (avio_read(s->pb, buffer, metadata_size) != metadata_size) { - av_freep(&buffer); - return AVERROR(EIO); + RETURN_ERROR(AVERROR(EIO)); } break; /* skip metadata block for unsupported types */ @@ -78,12 +77,10 @@ static int flac_read_header(AVFormatContext *s) FLACStreaminfo si; /* STREAMINFO can only occur once */ if (found_streaminfo) { - av_freep(&buffer); - return AVERROR_INVALIDDATA; + RETURN_ERROR(AVERROR_INVALIDDATA); } if (metadata_size != FLAC_STREAMINFO_SIZE) { - av_freep(&buffer); - return AVERROR_INVALIDDATA; + RETURN_ERROR(AVERROR_INVALIDDATA); } found_streaminfo = 1; st->codec->extradata = buffer; @@ -105,24 +102,25 @@ static int flac_read_header(AVFormatContext *s) const uint8_t *offset; int i, chapters, track, ti; if (metadata_size < 431) - return AVERROR_INVALIDDATA; + RETURN_ERROR(AVERROR_INVALIDDATA); offset = buffer + 395; chapters = bytestream_get_byte(&offset) - 1; if (chapters <= 0) - return AVERROR_INVALIDDATA; + RETURN_ERROR(AVERROR_INVALIDDATA); for (i = 0; i < chapters; i++) { if (offset + 36 - buffer > metadata_size) - return AVERROR_INVALIDDATA; + RETURN_ERROR(AVERROR_INVALIDDATA); start = bytestream_get_be64(&offset); track = bytestream_get_byte(&offset); bytestream_get_buffer(&offset, isrc, 12); isrc[12] = 0; offset += 14; ti = bytestream_get_byte(&offset); - if (ti <= 0) return AVERROR_INVALIDDATA; + if (ti <= 0) RETURN_ERROR(AVERROR_INVALIDDATA); offset += ti * 12; avpriv_new_chapter(s, track, st->time_base, start, AV_NOPTS_VALUE, isrc); } + av_freep(&buffer); } else if (metadata_type == FLAC_METADATA_TYPE_PICTURE) { ret = ff_flac_parse_picture(s, buffer, metadata_size); av_freep(&buffer); @@ -133,8 +131,7 @@ static int flac_read_header(AVFormatContext *s) } else { /* STREAMINFO must be the first block */ if (!found_streaminfo) { - av_freep(&buffer); - return AVERROR_INVALIDDATA; + RETURN_ERROR(AVERROR_INVALIDDATA); } /* process supported blocks other than STREAMINFO */ if (metadata_type == FLAC_METADATA_TYPE_VORBIS_COMMENT) { @@ -147,6 +144,10 @@ static int flac_read_header(AVFormatContext *s) } return 0; + +fail: + av_free(buffer); + return ret; } static int flac_probe(AVProbeData *p) diff --git a/libavformat/flacenc.c b/libavformat/flacenc.c index 1e4042e070..87e93625f2 100644 --- a/libavformat/flacenc.c +++ b/libavformat/flacenc.c @@ -2,25 +2,26 @@ * raw FLAC muxer * Copyright (c) 2006-2009 Justin Ruggles * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "libavcodec/flac.h" #include "avformat.h" +#include "avio_internal.h" #include "flacenc.h" #include "vorbiscomment.h" #include "libavcodec/bytestream.h" @@ -31,17 +32,14 @@ static int flac_write_block_padding(AVIOContext *pb, unsigned int n_padding_byte { avio_w8(pb, last_block ? 0x81 : 0x01); avio_wb24(pb, n_padding_bytes); - while (n_padding_bytes > 0) { - avio_w8(pb, 0); - n_padding_bytes--; - } + ffio_fill(pb, 0, n_padding_bytes); return 0; } static int flac_write_block_comment(AVIOContext *pb, AVDictionary **m, int last_block, int bitexact) { - const char *vendor = bitexact ? "Libav" : LIBAVFORMAT_IDENT; + const char *vendor = bitexact ? "ffmpeg" : LIBAVFORMAT_IDENT; unsigned int len, count; uint8_t *p, *p0; @@ -69,6 +67,15 @@ static int flac_write_header(struct AVFormatContext *s) int ret; AVCodecContext *codec = s->streams[0]->codec; + if (s->nb_streams > 1) { + av_log(s, AV_LOG_ERROR, "only one stream is supported\n"); + return AVERROR(EINVAL); + } + if (codec->codec_id != AV_CODEC_ID_FLAC) { + av_log(s, AV_LOG_ERROR, "unsupported codec\n"); + return AVERROR(EINVAL); + } + ret = ff_flac_write_header(s->pb, codec, 0); if (ret) return ret; diff --git a/libavformat/flacenc.h b/libavformat/flacenc.h index 2edda67043..e83ee32aeb 100644 --- a/libavformat/flacenc.h +++ b/libavformat/flacenc.h @@ -2,20 +2,20 @@ * raw FLAC muxer * Copyright (C) 2009 Justin Ruggles * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/flacenc_header.c b/libavformat/flacenc_header.c index c1f7c86554..e16c14bf53 100644 --- a/libavformat/flacenc_header.c +++ b/libavformat/flacenc_header.c @@ -2,20 +2,20 @@ * raw FLAC muxer * Copyright (C) 2009 Justin Ruggles * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/flic.c b/libavformat/flic.c index 2eb6f057b4..7235f2e188 100644 --- a/libavformat/flic.c +++ b/libavformat/flic.c @@ -2,20 +2,20 @@ * FLI/FLC Animation File Demuxer * Copyright (c) 2003 The ffmpeg Project * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -80,7 +80,7 @@ static int flic_probe(AVProbeData *p) return 0; - return AVPROBE_SCORE_MAX; + return AVPROBE_SCORE_MAX - 1; } static int flic_read_header(AVFormatContext *s) @@ -117,7 +117,7 @@ static int flic_read_header(AVFormatContext *s) if (!st->codec->width || !st->codec->height) { /* Ugly hack needed for the following sample: */ - /* http://samples.libav.org/fli-flc/fli-bugs/specular.flc */ + /* http://samples.mplayerhq.hu/fli-flc/fli-bugs/specular.flc */ av_log(s, AV_LOG_WARNING, "File with no specified width/height. Trying 640x480.\n"); st->codec->width = 640; @@ -125,8 +125,8 @@ static int flic_read_header(AVFormatContext *s) } /* send over the whole 128-byte FLIC header */ - st->codec->extradata_size = FLIC_HEADER_SIZE; - st->codec->extradata = av_malloc(FLIC_HEADER_SIZE); + if (ff_alloc_extradata(st->codec, FLIC_HEADER_SIZE)) + return AVERROR(ENOMEM); memcpy(st->codec->extradata, header, FLIC_HEADER_SIZE); /* peek at the preamble to detect TFTD videos - they seem to always start with an audio chunk */ @@ -158,7 +158,6 @@ static int flic_read_header(AVFormatContext *s) ast->codec->codec_tag = 0; ast->codec->sample_rate = FLIC_TFTD_SAMPLE_RATE; ast->codec->channels = 1; - ast->codec->sample_fmt = AV_SAMPLE_FMT_U8; ast->codec->bit_rate = st->codec->sample_rate * 8; ast->codec->bits_per_coded_sample = 8; ast->codec->channel_layout = AV_CH_LAYOUT_MONO; @@ -177,8 +176,8 @@ static int flic_read_header(AVFormatContext *s) /* send over abbreviated FLIC header chunk */ av_free(st->codec->extradata); - st->codec->extradata_size = 12; - st->codec->extradata = av_malloc(12); + if (ff_alloc_extradata(st->codec, 12)) + return AVERROR(ENOMEM); memcpy(st->codec->extradata, header, 12); } else if (magic_number == FLIC_FILE_MAGIC_1) { @@ -187,7 +186,7 @@ static int flic_read_header(AVFormatContext *s) (magic_number == FLIC_FILE_MAGIC_3)) { avpriv_set_pts_info(st, 64, speed, 1000); } else { - av_log(s, AV_LOG_INFO, "Invalid or unsupported magic chunk in file\n"); + av_log(s, AV_LOG_ERROR, "Invalid or unsupported magic chunk in file\n"); return AVERROR_INVALIDDATA; } diff --git a/libavformat/flv.h b/libavformat/flv.h index fe0fc90d83..db9468f469 100644 --- a/libavformat/flv.h +++ b/libavformat/flv.h @@ -1,22 +1,22 @@ /* * FLV common header * - * Copyright (c) 2006 The Libav Project + * Copyright (c) 2006 The FFmpeg Project * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -46,6 +46,11 @@ #define AMF_END_OF_OBJECT 0x09 +#define KEYFRAMES_TAG "keyframes" +#define KEYFRAMES_TIMESTAMP_TAG "times" +#define KEYFRAMES_BYTEOFFSET_TAG "filepositions" + + enum { FLV_HEADER_FLAG_HASVIDEO = 1, FLV_HEADER_FLAG_HASAUDIO = 4, @@ -58,6 +63,13 @@ enum { }; enum { + FLV_STREAM_TYPE_VIDEO, + FLV_STREAM_TYPE_AUDIO, + FLV_STREAM_TYPE_DATA, + FLV_STREAM_TYPE_NB, +}; + +enum { FLV_MONO = 0, FLV_STEREO = 1, }; @@ -95,12 +107,16 @@ enum { FLV_CODECID_VP6A = 5, FLV_CODECID_SCREEN2 = 6, FLV_CODECID_H264 = 7, + FLV_CODECID_REALH263= 8, + FLV_CODECID_MPEG4 = 9, }; enum { - FLV_FRAME_KEY = 1 << FLV_VIDEO_FRAMETYPE_OFFSET, - FLV_FRAME_INTER = 2 << FLV_VIDEO_FRAMETYPE_OFFSET, - FLV_FRAME_DISP_INTER = 3 << FLV_VIDEO_FRAMETYPE_OFFSET, + FLV_FRAME_KEY = 1 << FLV_VIDEO_FRAMETYPE_OFFSET, ///< key frame (for AVC, a seekable frame) + FLV_FRAME_INTER = 2 << FLV_VIDEO_FRAMETYPE_OFFSET, ///< inter frame (for AVC, a non-seekable frame) + FLV_FRAME_DISP_INTER = 3 << FLV_VIDEO_FRAMETYPE_OFFSET, ///< disposable inter frame (H.263 only) + FLV_FRAME_GENERATED_KEY = 4 << FLV_VIDEO_FRAMETYPE_OFFSET, ///< generated key frame (reserved for server use only) + FLV_FRAME_VIDEO_INFO_CMD = 5 << FLV_VIDEO_FRAMETYPE_OFFSET, ///< video info/command frame }; typedef enum { diff --git a/libavformat/flvdec.c b/libavformat/flvdec.c index c65c90c1ae..1024001a47 100644 --- a/libavformat/flvdec.c +++ b/libavformat/flvdec.c @@ -1,26 +1,26 @@ /* * FLV demuxer - * Copyright (c) 2003 The Libav Project + * Copyright (c) 2003 The FFmpeg Project * * This demuxer will generate a 1 byte extradata for VP6F content. * It is composed of: * - upper 4bits: difference between encoded width and visible width * - lower 4bits: difference between encoded height and visible height * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -37,18 +37,14 @@ #include "avio_internal.h" #include "flv.h" -#define KEYFRAMES_TAG "keyframes" -#define KEYFRAMES_TIMESTAMP_TAG "times" -#define KEYFRAMES_BYTEOFFSET_TAG "filepositions" - #define VALIDATE_INDEX_TS_THRESH 2500 typedef struct { const AVClass *class; ///< Class for private options. int trust_metadata; ///< configure streams according onMetaData int wrong_dts; ///< wrong dts due to negative cts - uint8_t *new_extradata[2]; - int new_extradata_size[2]; + uint8_t *new_extradata[FLV_STREAM_TYPE_NB]; + int new_extradata_size[FLV_STREAM_TYPE_NB]; int last_sample_rate; int last_channels; struct { @@ -57,6 +53,7 @@ typedef struct { } validate_index[2]; int validate_next; int validate_count; + int searched_for_end; } FLVContext; static int flv_probe(AVProbeData *p) @@ -80,6 +77,11 @@ static AVStream *create_stream(AVFormatContext *s, int codec_type) if (!st) return NULL; st->codec->codec_type = codec_type; + if (s->nb_streams>=3 ||( s->nb_streams==2 + && s->streams[0]->codec->codec_type != AVMEDIA_TYPE_DATA + && s->streams[1]->codec->codec_type != AVMEDIA_TYPE_DATA)) + s->ctx_flags &= ~AVFMTCTX_NOHEADER; + avpriv_set_pts_info(st, 32, 1, 1000); /* 32 bit pts in ms */ return st; } @@ -189,7 +191,7 @@ static void flv_set_audio_codec(AVFormatContext *s, AVStream *astream, acodec->codec_id = AV_CODEC_ID_PCM_ALAW; break; default: - av_log(s, AV_LOG_INFO, "Unsupported audio codec (%x)\n", + avpriv_request_sample(s, "Audio codec (%x)", flv_codecid >> FLV_AUDIO_CODECID_OFFSET); acodec->codec_tag = flv_codecid >> FLV_AUDIO_CODECID_OFFSET; } @@ -228,6 +230,9 @@ static int flv_set_video_codec(AVFormatContext *s, AVStream *vstream, case FLV_CODECID_H263: vcodec->codec_id = AV_CODEC_ID_FLV1; break; + case FLV_CODECID_REALH263: + vcodec->codec_id = AV_CODEC_ID_H263; + break; // Really mean it this time case FLV_CODECID_SCREEN: vcodec->codec_id = AV_CODEC_ID_FLASHSV; break; @@ -241,9 +246,7 @@ static int flv_set_video_codec(AVFormatContext *s, AVStream *vstream, vcodec->codec_id = AV_CODEC_ID_VP6A; if (read) { if (vcodec->extradata_size != 1) { - vcodec->extradata = av_malloc(1); - if (vcodec->extradata) - vcodec->extradata_size = 1; + ff_alloc_extradata(vcodec, 1); } if (vcodec->extradata) vcodec->extradata[0] = avio_r8(s->pb); @@ -254,8 +257,11 @@ static int flv_set_video_codec(AVFormatContext *s, AVStream *vstream, case FLV_CODECID_H264: vcodec->codec_id = AV_CODEC_ID_H264; return 3; // not 4, reading packet type will consume one byte + case FLV_CODECID_MPEG4: + vcodec->codec_id = AV_CODEC_ID_MPEG4; + return 3; default: - av_log(s, AV_LOG_INFO, "Unsupported video codec (%x)\n", flv_codecid); + avpriv_request_sample(s, "Video codec (%x)", flv_codecid); vcodec->codec_tag = flv_codecid; } @@ -281,56 +287,55 @@ static int parse_keyframes_index(AVFormatContext *s, AVIOContext *ioc, AVStream *vstream, int64_t max_pos) { FLVContext *flv = s->priv_data; - unsigned int arraylen = 0, timeslen = 0, fileposlen = 0, i; - double num_val; + unsigned int timeslen = 0, fileposlen = 0, i; char str_val[256]; int64_t *times = NULL; int64_t *filepositions = NULL; int ret = AVERROR(ENOSYS); int64_t initial_pos = avio_tell(ioc); + if (vstream->nb_index_entries>0) { + av_log(s, AV_LOG_WARNING, "Skiping duplicate index\n"); + return 0; + } + if (s->flags & AVFMT_FLAG_IGNIDX) return 0; while (avio_tell(ioc) < max_pos - 2 && amf_get_string(ioc, str_val, sizeof(str_val)) > 0) { - int64_t *current_array; + int64_t **current_array; + unsigned int arraylen; // Expect array object in context if (avio_r8(ioc) != AMF_DATA_TYPE_ARRAY) break; arraylen = avio_rb32(ioc); - if (arraylen >> 28) + if (arraylen>>28) break; - /* Expect only 'times' or 'filepositions' sub-arrays in other - * case refuse to use such metadata for indexing. */ - if (!strcmp(KEYFRAMES_TIMESTAMP_TAG, str_val) && !times) { - if (!(times = av_mallocz(sizeof(*times) * arraylen))) { - ret = AVERROR(ENOMEM); - goto finish; - } + if (!strcmp(KEYFRAMES_TIMESTAMP_TAG , str_val) && !times) { + current_array = × timeslen = arraylen; - current_array = times; } else if (!strcmp(KEYFRAMES_BYTEOFFSET_TAG, str_val) && !filepositions) { - if (!(filepositions = av_mallocz(sizeof(*filepositions) * arraylen))) { - ret = AVERROR(ENOMEM); - goto finish; - } + current_array = &filepositions; fileposlen = arraylen; - current_array = filepositions; } else // unexpected metatag inside keyframes, will not use such // metadata for indexing break; + if (!(*current_array = av_mallocz(sizeof(**current_array) * arraylen))) { + ret = AVERROR(ENOMEM); + goto finish; + } + for (i = 0; i < arraylen && avio_tell(ioc) < max_pos - 1; i++) { if (avio_r8(ioc) != AMF_DATA_TYPE_NUMBER) - goto finish; - num_val = av_int2double(avio_rb64(ioc)); - current_array[i] = num_val; + goto invalid; + current_array[0][i] = av_int2double(avio_rb64(ioc)); } if (times && filepositions) { // All done, exiting at a position allowing amf_parse_object @@ -340,7 +345,7 @@ static int parse_keyframes_index(AVFormatContext *s, AVIOContext *ioc, } } - if (!ret && timeslen == fileposlen) { + if (timeslen == fileposlen && fileposlen>1 && max_pos <= filepositions[0]) { for (i = 0; i < fileposlen; i++) { av_add_index_entry(vstream, filepositions[i], times[i] * 1000, 0, 0, AVINDEX_KEYFRAME); @@ -350,16 +355,15 @@ static int parse_keyframes_index(AVFormatContext *s, AVIOContext *ioc, flv->validate_count = i + 1; } } - } else + } else { +invalid: av_log(s, AV_LOG_WARNING, "Invalid keyframes object, skipping.\n"); + } finish: av_freep(×); av_freep(&filepositions); - // If we got unexpected data, but successfully reset back to - // the start pos, the caller can continue parsing - if (ret < 0 && avio_seek(ioc, initial_pos, SEEK_SET) > 0) - return 0; + avio_seek(ioc, initial_pos, SEEK_SET); return ret; } @@ -391,10 +395,11 @@ static int amf_parse_object(AVFormatContext *s, AVStream *astream, break; case AMF_DATA_TYPE_OBJECT: if ((vstream || astream) && key && + ioc->seekable && !strcmp(KEYFRAMES_TAG, key) && depth == 1) if (parse_keyframes_index(s, ioc, vstream ? vstream : astream, max_pos) < 0) - return -1; + av_log(s, AV_LOG_ERROR, "Keyframe index parsing failed\n"); while (avio_tell(ioc) < max_pos - 2 && amf_get_string(ioc, str_val, sizeof(str_val)) > 0) @@ -481,6 +486,11 @@ static int amf_parse_object(AVFormatContext *s, AVStream *astream, } } + if (amf_type == AMF_DATA_TYPE_OBJECT && s->nb_streams == 1 && + ((!acodec && !strcmp(key, "audiocodecid")) || + (!vcodec && !strcmp(key, "videocodecid")))) + s->ctx_flags &= ~AVFMTCTX_NOHEADER; //If there is either audio/video missing, codecid will be an empty object + if (!strcmp(key, "duration") || !strcmp(key, "filesize") || !strcmp(key, "width") || @@ -514,6 +524,7 @@ static int flv_read_metabody(AVFormatContext *s, int64_t next_pos) { AMFDataType type; AVStream *stream, *astream, *vstream; + AVStream av_unused *dstream; AVIOContext *ioc; int i; // only needs to hold the string "onMetaData". @@ -522,6 +533,7 @@ static int flv_read_metabody(AVFormatContext *s, int64_t next_pos) astream = NULL; vstream = NULL; + dstream = NULL; ioc = s->pb; // first object needs to be "onMetaData" string @@ -540,10 +552,12 @@ static int flv_read_metabody(AVFormatContext *s, int64_t next_pos) // the lookup every time it is called. for (i = 0; i < s->nb_streams; i++) { stream = s->streams[i]; - if (stream->codec->codec_type == AVMEDIA_TYPE_AUDIO) - astream = stream; - else if (stream->codec->codec_type == AVMEDIA_TYPE_VIDEO) + if (stream->codec->codec_type == AVMEDIA_TYPE_VIDEO) vstream = stream; + else if (stream->codec->codec_type == AVMEDIA_TYPE_AUDIO) + astream = stream; + else if (stream->codec->codec_type == AVMEDIA_TYPE_DATA) + dstream = stream; } // parse the second object (we want a mixed array) @@ -576,6 +590,8 @@ static int flv_read_header(AVFormatContext *s) if (flags & FLV_HEADER_FLAG_HASAUDIO) if (!create_stream(s, AVMEDIA_TYPE_AUDIO)) return AVERROR(ENOMEM); + // Flag doesn't indicate whether or not there is script-data present. Must + // create that stream if it's encountered. offset = avio_rb32(s->pb); avio_seek(s->pb, offset, SEEK_SET); @@ -588,19 +604,18 @@ static int flv_read_header(AVFormatContext *s) static int flv_read_close(AVFormatContext *s) { + int i; FLVContext *flv = s->priv_data; - av_freep(&flv->new_extradata[0]); - av_freep(&flv->new_extradata[1]); + for (i=0; i<FLV_STREAM_TYPE_NB; i++) + av_freep(&flv->new_extradata[i]); return 0; } static int flv_get_extradata(AVFormatContext *s, AVStream *st, int size) { av_free(st->codec->extradata); - st->codec->extradata = av_mallocz(size + FF_INPUT_BUFFER_PADDING_SIZE); - if (!st->codec->extradata) + if (ff_alloc_extradata(st->codec, size)) return AVERROR(ENOMEM); - st->codec->extradata_size = size; avio_read(s->pb, st->codec->extradata, st->codec->extradata_size); return 0; } @@ -742,10 +757,12 @@ skip: static int flv_read_packet(AVFormatContext *s, AVPacket *pkt) { FLVContext *flv = s->priv_data; - int ret, i, type, size, flags, is_audio; - int64_t next, pos; + int ret, i, type, size, flags; + int stream_type=-1; + int64_t next, pos, meta_pos; int64_t dts, pts = AV_NOPTS_VALUE; - int sample_rate = 0, channels = 0; + int av_uninit(channels); + int av_uninit(sample_rate); AVStream *st = NULL; /* pkt size is repeated at end. skip it */ @@ -756,7 +773,7 @@ static int flv_read_packet(AVFormatContext *s, AVPacket *pkt) dts = avio_rb24(s->pb); dts |= avio_r8(s->pb) << 24; av_dlog(s, "type:%d, size:%d, dts:%"PRId64"\n", type, size, dts); - if (s->pb->eof_reached) + if (url_feof(s->pb)) return AVERROR_EOF; avio_skip(s->pb, 3); /* stream id, always 0 */ flags = 0; @@ -783,24 +800,28 @@ static int flv_read_packet(AVFormatContext *s, AVPacket *pkt) next = size + avio_tell(s->pb); if (type == FLV_TAG_TYPE_AUDIO) { - is_audio = 1; + stream_type = FLV_STREAM_TYPE_AUDIO; flags = avio_r8(s->pb); size--; } else if (type == FLV_TAG_TYPE_VIDEO) { - is_audio = 0; + stream_type = FLV_STREAM_TYPE_VIDEO; flags = avio_r8(s->pb); size--; - if ((flags & 0xf0) == 0x50) /* video info / command frame */ + if ((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_VIDEO_INFO_CMD) goto skip; + } else if (type == FLV_TAG_TYPE_META) { + stream_type=FLV_STREAM_TYPE_DATA; + if (size > 13 + 1 + 4 && dts == 0) { // Header-type metadata stuff + meta_pos = avio_tell(s->pb); + if (flv_read_metabody(s, next) == 0) { + goto skip; + } + avio_seek(s->pb, meta_pos, SEEK_SET); + } } else { - if (type == FLV_TAG_TYPE_META && size > 13 + 1 + 4) - if (flv_read_metabody(s, next) > 0) { - return flv_data_packet(s, pkt, dts, next); - } else /* skip packet */ - av_log(s, AV_LOG_DEBUG, - "skipping flv packet: type %d, size %d, flags %d\n", - type, size, flags); - + av_log(s, AV_LOG_DEBUG, + "skipping flv packet: type %d, size %d, flags %d\n", + type, size, flags); skip: avio_seek(s->pb, next, SEEK_SET); continue; @@ -813,50 +834,66 @@ skip: /* now find stream */ for (i = 0; i < s->nb_streams; i++) { st = s->streams[i]; - if (is_audio && st->codec->codec_type == AVMEDIA_TYPE_AUDIO) { - if (flv_same_audio_codec(st->codec, flags)) + if (stream_type == FLV_STREAM_TYPE_AUDIO) { + if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO && + (s->audio_codec_id || flv_same_audio_codec(st->codec, flags))) break; - } else if (!is_audio && - st->codec->codec_type == AVMEDIA_TYPE_VIDEO) { - if (flv_same_video_codec(st->codec, flags)) + } else if (stream_type == FLV_STREAM_TYPE_VIDEO) { + if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO && + (s->video_codec_id || flv_same_video_codec(st->codec, flags))) + break; + } else if (stream_type == FLV_STREAM_TYPE_DATA) { + if (st->codec->codec_type == AVMEDIA_TYPE_DATA) break; } } - if (i == s->nb_streams) - st = create_stream(s, is_audio ? AVMEDIA_TYPE_AUDIO - : AVMEDIA_TYPE_VIDEO); - av_dlog(s, "%d %X %d \n", is_audio, flags, st->discard); - if ((st->discard >= AVDISCARD_NONKEY && - !((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_KEY || is_audio)) || - (st->discard >= AVDISCARD_BIDIR && - ((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_DISP_INTER && !is_audio)) || - st->discard >= AVDISCARD_ALL) { + if (i == s->nb_streams) { + static const enum AVMediaType stream_types[] = {AVMEDIA_TYPE_VIDEO, AVMEDIA_TYPE_AUDIO, AVMEDIA_TYPE_DATA}; + av_log(s, AV_LOG_WARNING, "Stream discovered after head already parsed\n"); + st = create_stream(s, stream_types[stream_type]); + if (!st) + return AVERROR(ENOMEM); + + } + av_dlog(s, "%d %X %d \n", stream_type, flags, st->discard); + if ( (st->discard >= AVDISCARD_NONKEY && !((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_KEY || (stream_type == FLV_STREAM_TYPE_AUDIO))) + ||(st->discard >= AVDISCARD_BIDIR && ((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_DISP_INTER && (stream_type == FLV_STREAM_TYPE_VIDEO))) + || st->discard >= AVDISCARD_ALL + ) { avio_seek(s->pb, next, SEEK_SET); continue; } - if ((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_KEY) + if ((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_KEY || stream_type == FLV_STREAM_TYPE_AUDIO) av_add_index_entry(st, pos, dts, size, 0, AVINDEX_KEYFRAME); break; } // if not streamed and no duration from metadata then seek to end to find // the duration from the timestamps - if (s->pb->seekable && (!s->duration || s->duration == AV_NOPTS_VALUE)) { + if (s->pb->seekable && (!s->duration || s->duration == AV_NOPTS_VALUE) && !flv->searched_for_end) { int size; const int64_t pos = avio_tell(s->pb); - const int64_t fsize = avio_size(s->pb); + int64_t fsize = avio_size(s->pb); +retry_duration: avio_seek(s->pb, fsize - 4, SEEK_SET); size = avio_rb32(s->pb); avio_seek(s->pb, fsize - 3 - size, SEEK_SET); if (size == avio_rb24(s->pb) + 11) { uint32_t ts = avio_rb24(s->pb); ts |= avio_r8(s->pb) << 24; - s->duration = ts * (int64_t)AV_TIME_BASE / 1000; + if (ts) + s->duration = ts * (int64_t)AV_TIME_BASE / 1000; + else if (fsize >= 8 && fsize - 8 >= size) { + fsize -= size+4; + goto retry_duration; + } } + avio_seek(s->pb, pos, SEEK_SET); + flv->searched_for_end = 1; } - if (is_audio) { + if (stream_type == FLV_STREAM_TYPE_AUDIO) { int bits_per_coded_sample; channels = (flags & FLV_AUDIO_CHANNEL_MASK) == FLV_STEREO ? 2 : 1; sample_rate = 44100 << ((flags & FLV_AUDIO_SAMPLERATE_MASK) >> @@ -884,15 +921,16 @@ skip: flv_set_audio_codec(s, st, &ctx, flags & FLV_AUDIO_CODECID_MASK); sample_rate = ctx.sample_rate; } - } else { + } else if (stream_type == FLV_STREAM_TYPE_VIDEO) { size -= flv_set_video_codec(s, st, flags & FLV_VIDEO_CODECID_MASK, 1); } if (st->codec->codec_id == AV_CODEC_ID_AAC || - st->codec->codec_id == AV_CODEC_ID_H264) { + st->codec->codec_id == AV_CODEC_ID_H264 || + st->codec->codec_id == AV_CODEC_ID_MPEG4) { int type = avio_r8(s->pb); size--; - if (st->codec->codec_id == AV_CODEC_ID_H264) { + if (st->codec->codec_id == AV_CODEC_ID_H264 || st->codec->codec_id == AV_CODEC_ID_MPEG4) { // sign extension int32_t cts = (avio_rb24(s->pb) + 0xff800000) ^ 0xff800000; pts = dts + cts; @@ -904,19 +942,19 @@ skip: if (flv->wrong_dts) dts = AV_NOPTS_VALUE; } - if (type == 0) { + if (type == 0 && (!st->codec->extradata || st->codec->codec_id == AV_CODEC_ID_AAC)) { if (st->codec->extradata) { - if ((ret = flv_queue_extradata(flv, s->pb, is_audio, size)) < 0) + if ((ret = flv_queue_extradata(flv, s->pb, stream_type, size)) < 0) return ret; ret = AVERROR(EAGAIN); goto leave; } if ((ret = flv_get_extradata(s, st, size)) < 0) return ret; - if (st->codec->codec_id == AV_CODEC_ID_AAC) { + if (st->codec->codec_id == AV_CODEC_ID_AAC && 0) { MPEG4AudioConfig cfg; - avpriv_mpeg4audio_get_config(&cfg, st->codec->extradata, - st->codec->extradata_size * 8, 1); + if (avpriv_mpeg4audio_get_config(&cfg, st->codec->extradata, + st->codec->extradata_size * 8, 1) >= 0) { st->codec->channels = cfg.channels; st->codec->channel_layout = 0; if (cfg.ext_sample_rate) @@ -925,6 +963,7 @@ skip: st->codec->sample_rate = cfg.sample_rate; av_dlog(s, "mp4a config channels %d sample rate %d\n", st->codec->channels, st->codec->sample_rate); + } } ret = AVERROR(EAGAIN); @@ -940,31 +979,31 @@ skip: ret = av_get_packet(s->pb, pkt, size); if (ret < 0) - return AVERROR(EIO); - /* note: we need to modify the packet size here to handle the last - * packet */ - pkt->size = ret; + return ret; pkt->dts = dts; pkt->pts = pts == AV_NOPTS_VALUE ? dts : pts; pkt->stream_index = st->index; - if (flv->new_extradata[is_audio]) { + if (flv->new_extradata[stream_type]) { uint8_t *side = av_packet_new_side_data(pkt, AV_PKT_DATA_NEW_EXTRADATA, - flv->new_extradata_size[is_audio]); + flv->new_extradata_size[stream_type]); if (side) { - memcpy(side, flv->new_extradata[is_audio], - flv->new_extradata_size[is_audio]); - av_freep(&flv->new_extradata[is_audio]); - flv->new_extradata_size[is_audio] = 0; + memcpy(side, flv->new_extradata[stream_type], + flv->new_extradata_size[stream_type]); + av_freep(&flv->new_extradata[stream_type]); + flv->new_extradata_size[stream_type] = 0; } } - if (is_audio && (sample_rate != flv->last_sample_rate || + if (stream_type == FLV_STREAM_TYPE_AUDIO && + (sample_rate != flv->last_sample_rate || channels != flv->last_channels)) { flv->last_sample_rate = sample_rate; flv->last_channels = channels; ff_add_param_change(pkt, channels, 0, sample_rate, 0, 0); } - if (is_audio || ((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_KEY)) + if ( stream_type == FLV_STREAM_TYPE_AUDIO || + ((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_KEY) || + stream_type == FLV_STREAM_TYPE_DATA) pkt->flags |= AV_PKT_FLAG_KEY; leave: @@ -987,7 +1026,7 @@ static const AVOption options[] = { { NULL } }; -static const AVClass class = { +static const AVClass flv_class = { .class_name = "flvdec", .item_name = av_default_item_name, .option = options, @@ -1004,5 +1043,5 @@ AVInputFormat ff_flv_demuxer = { .read_seek = flv_read_seek, .read_close = flv_read_close, .extensions = "flv", - .priv_class = &class, + .priv_class = &flv_class, }; diff --git a/libavformat/flvenc.c b/libavformat/flvenc.c index ca453f6025..922410386e 100644 --- a/libavformat/flvenc.c +++ b/libavformat/flvenc.c @@ -1,37 +1,39 @@ /* * FLV muxer - * Copyright (c) 2003 The Libav Project + * Copyright (c) 2003 The FFmpeg Project * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "libavutil/intreadwrite.h" #include "libavutil/dict.h" #include "libavutil/intfloat.h" +#include "libavutil/avassert.h" #include "avc.h" #include "avformat.h" #include "flv.h" #include "internal.h" #include "metadata.h" -#undef NDEBUG -#include <assert.h> static const AVCodecTag flv_video_codec_ids[] = { { AV_CODEC_ID_FLV1, FLV_CODECID_H263 }, + { AV_CODEC_ID_H263, FLV_CODECID_REALH263 }, + { AV_CODEC_ID_MPEG4, FLV_CODECID_MPEG4 }, { AV_CODEC_ID_FLASHSV, FLV_CODECID_SCREEN }, { AV_CODEC_ID_FLASHSV2, FLV_CODECID_SCREEN2 }, { AV_CODEC_ID_VP6F, FLV_CODECID_VP6 }, @@ -77,12 +79,12 @@ static int get_audio_flags(AVFormatContext *s, AVCodecContext *enc) else if (enc->codec_id == AV_CODEC_ID_SPEEX) { if (enc->sample_rate != 16000) { av_log(s, AV_LOG_ERROR, - "flv only supports wideband (16kHz) Speex audio\n"); - return -1; + "FLV only supports wideband (16kHz) Speex audio\n"); + return AVERROR(EINVAL); } if (enc->channels != 1) { - av_log(s, AV_LOG_ERROR, "flv only supports mono Speex audio\n"); - return -1; + av_log(s, AV_LOG_ERROR, "FLV only supports mono Speex audio\n"); + return AVERROR(EINVAL); } return FLV_CODECID_SPEEX | FLV_SAMPLERATE_11025HZ | FLV_SAMPLESSIZE_16BIT; } else { @@ -105,9 +107,9 @@ static int get_audio_flags(AVFormatContext *s, AVCodecContext *enc) } default: av_log(s, AV_LOG_ERROR, - "flv does not support that sample rate, " - "choose from (44100, 22050, 11025).\n"); - return -1; + "FLV does not support sample rate %d, " + "choose from (44100, 22050, 11025)\n", enc->sample_rate); + return AVERROR(EINVAL); } } @@ -148,8 +150,9 @@ static int get_audio_flags(AVFormatContext *s, AVCodecContext *enc) flags |= enc->codec_tag << 4; break; default: - av_log(s, AV_LOG_ERROR, "codec not compatible with flv\n"); - return -1; + av_log(s, AV_LOG_ERROR, "Audio codec '%s' not compatible with FLV\n", + avcodec_get_name(enc->codec_id)); + return AVERROR(EINVAL); } return flags; @@ -215,8 +218,9 @@ static int flv_write_header(AVFormatContext *s) } video_enc = enc; if (enc->codec_tag == 0) { - av_log(s, AV_LOG_ERROR, "video codec not compatible with flv\n"); - return -1; + av_log(s, AV_LOG_ERROR, "Video codec '%s' for stream %d is not compatible with FLV\n", + avcodec_get_name(enc->codec_id), i); + return AVERROR(EINVAL); } break; case AVMEDIA_TYPE_AUDIO: @@ -231,14 +235,16 @@ static int flv_write_header(AVFormatContext *s) break; case AVMEDIA_TYPE_DATA: if (enc->codec_id != AV_CODEC_ID_TEXT) { - av_log(s, AV_LOG_ERROR, "codec not compatible with flv\n"); + av_log(s, AV_LOG_ERROR, "Data codec '%s' for stream %d is not compatible with FLV\n", + avcodec_get_name(enc->codec_id), i); return AVERROR_INVALIDDATA; } data_enc = enc; break; default: - av_log(s, AV_LOG_ERROR, "codec not compatible with flv\n"); - return -1; + av_log(s, AV_LOG_ERROR, "Codec type '%s' for stream %d is not compatible with FLV\n", + av_get_media_type_string(enc->codec_type), i); + return AVERROR(EINVAL); } avpriv_set_pts_info(s->streams[i], 32, 1, 1000); /* 32 bit pts in ms */ @@ -337,6 +343,22 @@ static int flv_write_header(AVFormatContext *s) } while ((tag = av_dict_get(s->metadata, "", tag, AV_DICT_IGNORE_SUFFIX))) { + if( !strcmp(tag->key, "width") + ||!strcmp(tag->key, "height") + ||!strcmp(tag->key, "videodatarate") + ||!strcmp(tag->key, "framerate") + ||!strcmp(tag->key, "videocodecid") + ||!strcmp(tag->key, "audiodatarate") + ||!strcmp(tag->key, "audiosamplerate") + ||!strcmp(tag->key, "audiosamplesize") + ||!strcmp(tag->key, "stereo") + ||!strcmp(tag->key, "audiocodecid") + ||!strcmp(tag->key, "duration") + ||!strcmp(tag->key, "onMetaData") + ){ + av_log(s, AV_LOG_DEBUG, "Ignoring metadata for %s\n", tag->key); + continue; + } put_amf_string(pb, tag->key); avio_w8(pb, AMF_DATA_TYPE_STRING); put_amf_string(pb, tag->value); @@ -363,7 +385,7 @@ static int flv_write_header(AVFormatContext *s) for (i = 0; i < s->nb_streams; i++) { AVCodecContext *enc = s->streams[i]->codec; - if (enc->codec_id == AV_CODEC_ID_AAC || enc->codec_id == AV_CODEC_ID_H264) { + if (enc->codec_id == AV_CODEC_ID_AAC || enc->codec_id == AV_CODEC_ID_H264 || enc->codec_id == AV_CODEC_ID_MPEG4) { int64_t pos; avio_w8(pb, enc->codec_type == AVMEDIA_TYPE_VIDEO ? FLV_TAG_TYPE_VIDEO : FLV_TAG_TYPE_AUDIO); @@ -406,7 +428,7 @@ static int flv_write_trailer(AVFormatContext *s) AVCodecContext *enc = s->streams[i]->codec; FLVStreamContext *sc = s->streams[i]->priv_data; if (enc->codec_type == AVMEDIA_TYPE_VIDEO && - enc->codec_id == AV_CODEC_ID_H264) + (enc->codec_id == AV_CODEC_ID_H264 || enc->codec_id == AV_CODEC_ID_MPEG4)) put_avc_eos_tag(pb, sc->last_ts); } @@ -435,12 +457,12 @@ static int flv_write_packet(AVFormatContext *s, AVPacket *pkt) unsigned ts; int size = pkt->size; uint8_t *data = NULL; - int flags = 0, flags_size; + int flags = -1, flags_size, ret; if (enc->codec_id == AV_CODEC_ID_VP6F || enc->codec_id == AV_CODEC_ID_VP6A || enc->codec_id == AV_CODEC_ID_AAC) flags_size = 2; - else if (enc->codec_id == AV_CODEC_ID_H264) + else if (enc->codec_id == AV_CODEC_ID_H264 || enc->codec_id == AV_CODEC_ID_MPEG4) flags_size = 5; else flags_size = 1; @@ -452,9 +474,9 @@ static int flv_write_packet(AVFormatContext *s, AVPacket *pkt) flags = enc->codec_tag; if (flags == 0) { av_log(s, AV_LOG_ERROR, - "video codec %X not compatible with flv\n", - enc->codec_id); - return -1; + "Video codec '%s' is not compatible with FLV\n", + avcodec_get_name(enc->codec_id)); + return AVERROR(EINVAL); } flags |= pkt->flags & AV_PKT_FLAG_KEY ? FLV_FRAME_KEY : FLV_FRAME_INTER; @@ -462,7 +484,7 @@ static int flv_write_packet(AVFormatContext *s, AVPacket *pkt) case AVMEDIA_TYPE_AUDIO: flags = get_audio_flags(s, enc); - assert(size); + av_assert0(size); avio_w8(pb, FLV_TAG_TYPE_AUDIO); break; @@ -473,11 +495,21 @@ static int flv_write_packet(AVFormatContext *s, AVPacket *pkt) return AVERROR(EINVAL); } - if (enc->codec_id == AV_CODEC_ID_H264) - /* check if extradata looks like MP4 */ + if (enc->codec_id == AV_CODEC_ID_H264 || enc->codec_id == AV_CODEC_ID_MPEG4) { + /* check if extradata looks like mp4 formated */ if (enc->extradata_size > 0 && *(uint8_t*)enc->extradata != 1) - if (ff_avc_parse_nal_units_buf(pkt->data, &data, &size) < 0) - return -1; + if ((ret = ff_avc_parse_nal_units_buf(pkt->data, &data, &size)) < 0) + return ret; + } else if (enc->codec_id == AV_CODEC_ID_AAC && pkt->size > 2 && + (AV_RB16(pkt->data) & 0xfff0) == 0xfff0) { + if (!s->streams[pkt->stream_index]->nb_frames) { + av_log(s, AV_LOG_ERROR, "Malformed AAC bitstream detected: " + "use audio bitstream filter 'aac_adtstoasc' to fix it " + "('-bsf:a aac_adtstoasc' option with ffmpeg)\n"); + return AVERROR_INVALIDDATA; + } + av_log(s, AV_LOG_WARNING, "aac bitstream error\n"); + } if (flv->delay == AV_NOPTS_VALUE) flv->delay = -pkt->dts; @@ -526,6 +558,7 @@ static int flv_write_packet(AVFormatContext *s, AVPacket *pkt) avio_seek(pb, data_size + 10 - 3, SEEK_CUR); avio_wb32(pb, data_size + 11); } else { + av_assert1(flags>=0); avio_w8(pb,flags); if (enc->codec_id == AV_CODEC_ID_VP6F || enc->codec_id == AV_CODEC_ID_VP6A) { if (enc->extradata_size) @@ -535,7 +568,7 @@ static int flv_write_packet(AVFormatContext *s, AVPacket *pkt) (FFALIGN(enc->height, 16) - enc->height)); } else if (enc->codec_id == AV_CODEC_ID_AAC) avio_w8(pb, 1); // AAC raw - else if (enc->codec_id == AV_CODEC_ID_H264) { + else if (enc->codec_id == AV_CODEC_ID_H264 || enc->codec_id == AV_CODEC_ID_MPEG4) { avio_w8(pb, 1); // AVC NALU avio_wb24(pb, pkt->pts - pkt->dts); } diff --git a/libavformat/format.c b/libavformat/format.c index 3a510cdac3..ac9100b604 100644 --- a/libavformat/format.c +++ b/libavformat/format.c @@ -2,25 +2,26 @@ * Format register and lookup * Copyright (c) 2000, 2001, 2002 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "avformat.h" #include "internal.h" +#include "libavutil/atomic.h" #include "libavutil/avstring.h" /** @@ -52,22 +53,18 @@ void av_register_input_format(AVInputFormat *format) { AVInputFormat **p = &first_iformat; - while (*p != NULL) - p = &(*p)->next; - - *p = format; format->next = NULL; + while(avpriv_atomic_ptr_cas((void * volatile *)p, NULL, format)) + p = &(*p)->next; } void av_register_output_format(AVOutputFormat *format) { AVOutputFormat **p = &first_oformat; - while (*p != NULL) - p = &(*p)->next; - - *p = format; format->next = NULL; + while(avpriv_atomic_ptr_cas((void * volatile *)p, NULL, format)) + p = &(*p)->next; } int av_match_ext(const char *filename, const char *extensions) @@ -134,7 +131,7 @@ AVOutputFormat *av_guess_format(const char *short_name, const char *filename, score_max = 0; while ((fmt = av_oformat_next(fmt))) { score = 0; - if (fmt->name && short_name && !av_strcasecmp(fmt->name, short_name)) + if (fmt->name && short_name && match_format(short_name, fmt->name)) score += 100; if (fmt->mime_type && mime_type && !strcmp(fmt->mime_type, mime_type)) score += 10; @@ -154,6 +151,10 @@ enum AVCodecID av_guess_codec(AVOutputFormat *fmt, const char *short_name, const char *filename, const char *mime_type, enum AVMediaType type) { + if (!strcmp(fmt->name, "segment") || !strcmp(fmt->name, "ssegment")) { + fmt = av_guess_format(NULL, filename, NULL); + } + if (type == AVMEDIA_TYPE_VIDEO) { enum AVCodecID codec_id = AV_CODEC_ID_NONE; diff --git a/libavformat/framecrcenc.c b/libavformat/framecrcenc.c index dd55c129f0..dbe49b5ba1 100644 --- a/libavformat/framecrcenc.c +++ b/libavformat/framecrcenc.c @@ -2,24 +2,25 @@ * frame CRC encoder (for codec/format testing) * Copyright (c) 2002 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "libavutil/adler32.h" +#include "libavutil/avstring.h" #include "avformat.h" #include "internal.h" @@ -28,8 +29,31 @@ static int framecrc_write_packet(struct AVFormatContext *s, AVPacket *pkt) uint32_t crc = av_adler32_update(0, pkt->data, pkt->size); char buf[256]; - snprintf(buf, sizeof(buf), "%d, %10"PRId64", %10"PRId64", %8d, %8d, 0x%08x\n", + snprintf(buf, sizeof(buf), "%d, %10"PRId64", %10"PRId64", %8d, %8d, 0x%08x", pkt->stream_index, pkt->dts, pkt->pts, pkt->duration, pkt->size, crc); + if (pkt->flags != AV_PKT_FLAG_KEY) + av_strlcatf(buf, sizeof(buf), ", F=0x%0X", pkt->flags); + if (pkt->side_data_elems) { + int i, j; + av_strlcatf(buf, sizeof(buf), ", S=%d", pkt->side_data_elems); + + for (i=0; i<pkt->side_data_elems; i++) { + uint32_t side_data_crc = 0; + if (HAVE_BIGENDIAN && AV_PKT_DATA_PALETTE == pkt->side_data[i].type) { + for (j=0; j<pkt->side_data[i].size; j++) { + side_data_crc = av_adler32_update(side_data_crc, + pkt->side_data[i].data + (j^3), + 1); + } + } else { + side_data_crc = av_adler32_update(0, + pkt->side_data[i].data, + pkt->side_data[i].size); + } + av_strlcatf(buf, sizeof(buf), ", %8d, 0x%08x", pkt->side_data[i].size, side_data_crc); + } + } + av_strlcatf(buf, sizeof(buf), "\n"); avio_write(s->pb, buf, strlen(buf)); return 0; } @@ -37,7 +61,6 @@ static int framecrc_write_packet(struct AVFormatContext *s, AVPacket *pkt) AVOutputFormat ff_framecrc_muxer = { .name = "framecrc", .long_name = NULL_IF_CONFIG_SMALL("framecrc testing"), - .extensions = "", .audio_codec = AV_CODEC_ID_PCM_S16LE, .video_codec = AV_CODEC_ID_RAWVIDEO, .write_header = ff_framehash_write_header, diff --git a/libavformat/framehash.c b/libavformat/framehash.c index 28e9e8407d..f97f59b9bb 100644 --- a/libavformat/framehash.c +++ b/libavformat/framehash.c @@ -1,20 +1,20 @@ /* * Common functions for the frame{crc,md5} muxers * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -23,6 +23,9 @@ int ff_framehash_write_header(AVFormatContext *s) { int i; + + if (s->nb_streams && !(s->streams[0]->codec->flags & CODEC_FLAG_BITEXACT)) + avio_printf(s->pb, "#software: %s\n", LIBAVFORMAT_IDENT); for (i = 0; i < s->nb_streams; i++) { AVStream *st = s->streams[i]; avpriv_set_pts_info(st, 64, st->codec->time_base.num, st->codec->time_base.den); diff --git a/libavformat/frmdec.c b/libavformat/frmdec.c new file mode 100644 index 0000000000..a6f19afcd4 --- /dev/null +++ b/libavformat/frmdec.c @@ -0,0 +1,110 @@ +/* + * Megalux Frame demuxer + * Copyright (c) 2010 Peter Ross <pross@xvid.org> + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * Megalux Frame demuxer + */ + +#include "libavcodec/raw.h" +#include "libavutil/intreadwrite.h" +#include "avformat.h" + +static const PixelFormatTag frm_pix_fmt_tags[] = { + { AV_PIX_FMT_RGB555, 1 }, + { AV_PIX_FMT_RGB0, 2 }, + { AV_PIX_FMT_RGB24, 3 }, + { AV_PIX_FMT_BGR0, 4 }, + { AV_PIX_FMT_BGRA, 5 }, + { AV_PIX_FMT_NONE, 0 }, +}; + +typedef struct { + int count; +} FrmContext; + +static int frm_read_probe(AVProbeData *p) +{ + if (p->buf_size > 8 && + p->buf[0] == 'F' && p->buf[1] == 'R' && p->buf[2] == 'M' && + AV_RL16(&p->buf[4]) && AV_RL16(&p->buf[6])) + return AVPROBE_SCORE_MAX / 4; + return 0; +} + +static int frm_read_header(AVFormatContext *avctx) +{ + AVIOContext *pb = avctx->pb; + AVStream *st = avformat_new_stream(avctx, 0); + if (!st) + return AVERROR(ENOMEM); + + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->codec->codec_id = AV_CODEC_ID_RAWVIDEO; + avio_skip(pb, 3); + + st->codec->pix_fmt = avpriv_find_pix_fmt(frm_pix_fmt_tags, avio_r8(pb)); + if (!st->codec->pix_fmt) + return AVERROR_INVALIDDATA; + + st->codec->codec_tag = 0; + st->codec->width = avio_rl16(pb); + st->codec->height = avio_rl16(pb); + return 0; +} + +static int frm_read_packet(AVFormatContext *avctx, AVPacket *pkt) +{ + FrmContext *s = avctx->priv_data; + AVCodecContext *stc = avctx->streams[0]->codec; + int packet_size, ret; + + if (s->count) + return AVERROR_EOF; + + packet_size = avpicture_get_size(stc->pix_fmt, stc->width, stc->height); + if (packet_size < 0) + return AVERROR_INVALIDDATA; + + ret = av_get_packet(avctx->pb, pkt, packet_size); + if (ret < 0) + return ret; + + if (stc->pix_fmt == AV_PIX_FMT_BGRA) { + int i; + for (i = 3; i + 1 <= pkt->size; i += 4) + pkt->data[i] = 0xFF - pkt->data[i]; + } + + pkt->stream_index = 0; + s->count++; + + return 0; +} + +AVInputFormat ff_frm_demuxer = { + .name = "frm", + .priv_data_size = sizeof(FrmContext), + .long_name = NULL_IF_CONFIG_SMALL("Megalux Frame"), + .read_probe = frm_read_probe, + .read_header = frm_read_header, + .read_packet = frm_read_packet, +}; diff --git a/libavformat/ftp.c b/libavformat/ftp.c new file mode 100644 index 0000000000..ed9a3db419 --- /dev/null +++ b/libavformat/ftp.c @@ -0,0 +1,721 @@ +/* + * Copyright (c) 2013 Lukasz Marek <lukasz.m.luki@gmail.com> + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/avstring.h" +#include "avformat.h" +#include "internal.h" +#include "url.h" +#include "libavutil/opt.h" +#include "libavutil/bprint.h" + +#define CONTROL_BUFFER_SIZE 1024 +#define CREDENTIALS_BUFFER_SIZE 128 + +typedef enum { + UNKNOWN, + READY, + DOWNLOADING, + UPLOADING, + DISCONNECTED +} FTPState; + +typedef struct { + const AVClass *class; + URLContext *conn_control; /**< Control connection */ + URLContext *conn_data; /**< Data connection, NULL when not connected */ + uint8_t control_buffer[CONTROL_BUFFER_SIZE]; /**< Control connection buffer */ + uint8_t *control_buf_ptr, *control_buf_end; + int server_data_port; /**< Data connection port opened by server, -1 on error. */ + int server_control_port; /**< Control connection port, default is 21 */ + char hostname[512]; /**< Server address. */ + char credencials[CREDENTIALS_BUFFER_SIZE]; /**< Authentication data */ + char path[MAX_URL_SIZE]; /**< Path to resource on server. */ + int64_t filesize; /**< Size of file on server, -1 on error. */ + int64_t position; /**< Current position, calculated. */ + int rw_timeout; /**< Network timeout. */ + const char *anonymous_password; /**< Password to be used for anonymous user. An email should be used. */ + int write_seekable; /**< Control seekability, 0 = disable, 1 = enable. */ + FTPState state; /**< State of data connection */ +} FTPContext; + +#define OFFSET(x) offsetof(FTPContext, x) +#define D AV_OPT_FLAG_DECODING_PARAM +#define E AV_OPT_FLAG_ENCODING_PARAM +static const AVOption options[] = { + {"timeout", "set timeout of socket I/O operations", OFFSET(rw_timeout), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, D|E }, + {"ftp-write-seekable", "control seekability of connection during encoding", OFFSET(write_seekable), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, E }, + {"ftp-anonymous-password", "password for anonymous login. E-mail address should be used.", OFFSET(anonymous_password), AV_OPT_TYPE_STRING, { 0 }, 0, 0, D|E }, + {NULL} +}; + +static const AVClass ftp_context_class = { + .class_name = "ftp", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + +static int ftp_getc(FTPContext *s) +{ + int len; + if (s->control_buf_ptr >= s->control_buf_end) { + len = ffurl_read(s->conn_control, s->control_buffer, CONTROL_BUFFER_SIZE); + if (len < 0) { + return len; + } else if (!len) { + return -1; + } else { + s->control_buf_ptr = s->control_buffer; + s->control_buf_end = s->control_buffer + len; + } + } + return *s->control_buf_ptr++; +} + +static int ftp_get_line(FTPContext *s, char *line, int line_size) +{ + int ch; + char *q = line; + + for (;;) { + ch = ftp_getc(s); + if (ch < 0) { + return ch; + } + if (ch == '\n') { + /* process line */ + if (q > line && q[-1] == '\r') + q--; + *q = '\0'; + return 0; + } else { + if ((q - line) < line_size - 1) + *q++ = ch; + } + } +} + +/* + * This routine returns ftp server response code. + * Server may send more than one response for a certain command. + * First expected code is returned. + */ +static int ftp_status(FTPContext *s, char **line, const int response_codes[]) +{ + int err, i, dash = 0, result = 0, code_found = 0; + char buf[CONTROL_BUFFER_SIZE]; + AVBPrint line_buffer; + + if (line) + av_bprint_init(&line_buffer, 0, AV_BPRINT_SIZE_AUTOMATIC); + + while (!code_found || dash) { + if ((err = ftp_get_line(s, buf, sizeof(buf))) < 0) { + if (line) + av_bprint_finalize(&line_buffer, NULL); + return err; + } + + av_log(s, AV_LOG_DEBUG, "%s\n", buf); + + if (strlen(buf) < 4) + continue; + + err = 0; + for (i = 0; i < 3; ++i) { + if (buf[i] < '0' || buf[i] > '9') + continue; + err *= 10; + err += buf[i] - '0'; + } + dash = !!(buf[3] == '-'); + + for (i = 0; response_codes[i]; ++i) { + if (err == response_codes[i]) { + if (line) + av_bprintf(&line_buffer, "%s", buf); + code_found = 1; + result = err; + break; + } + } + } + + if (line) + av_bprint_finalize(&line_buffer, line); + return result; +} + +static int ftp_send_command(FTPContext *s, const char *command, + const int response_codes[], char **response) +{ + int err; + + if ((err = ffurl_write(s->conn_control, command, strlen(command))) < 0) + return err; + if (!err) + return -1; + + /* return status */ + if (response_codes) { + return ftp_status(s, response, response_codes); + } + return 0; +} + +static void ftp_close_data_connection(FTPContext *s) +{ + ffurl_closep(&s->conn_data); + s->position = 0; + s->state = DISCONNECTED; +} + +static void ftp_close_both_connections(FTPContext *s) +{ + ffurl_closep(&s->conn_control); + ftp_close_data_connection(s); +} + +static int ftp_auth(FTPContext *s) +{ + const char *user = NULL, *pass = NULL; + char *end = NULL, buf[CONTROL_BUFFER_SIZE], credencials[CREDENTIALS_BUFFER_SIZE]; + int err; + static const int user_codes[] = {331, 230, 500, 530, 0}; /* 500, 530 are incorrect codes */ + static const int pass_codes[] = {230, 503, 530, 0}; /* 503, 530 are incorrect codes */ + + /* Authentication may be repeated, original string has to be saved */ + av_strlcpy(credencials, s->credencials, sizeof(credencials)); + + user = av_strtok(credencials, ":", &end); + pass = av_strtok(end, ":", &end); + + if (!user) { + user = "anonymous"; + pass = s->anonymous_password ? s->anonymous_password : "nopassword"; + } + + snprintf(buf, sizeof(buf), "USER %s\r\n", user); + err = ftp_send_command(s, buf, user_codes, NULL); + if (err == 331) { + if (pass) { + snprintf(buf, sizeof(buf), "PASS %s\r\n", pass); + err = ftp_send_command(s, buf, pass_codes, NULL); + } else + return AVERROR(EACCES); + } + if (err != 230) + return AVERROR(EACCES); + + return 0; +} + +static int ftp_passive_mode(FTPContext *s) +{ + char *res = NULL, *start = NULL, *end = NULL; + int i; + static const char *command = "PASV\r\n"; + static const int pasv_codes[] = {227, 501, 0}; /* 501 is incorrect code */ + + if (ftp_send_command(s, command, pasv_codes, &res) != 227 || !res) + goto fail; + + for (i = 0; res[i]; ++i) { + if (res[i] == '(') { + start = res + i + 1; + } else if (res[i] == ')') { + end = res + i; + break; + } + } + if (!start || !end) + goto fail; + + *end = '\0'; + /* skip ip */ + if (!av_strtok(start, ",", &end)) goto fail; + if (!av_strtok(end, ",", &end)) goto fail; + if (!av_strtok(end, ",", &end)) goto fail; + if (!av_strtok(end, ",", &end)) goto fail; + + /* parse port number */ + start = av_strtok(end, ",", &end); + if (!start) goto fail; + s->server_data_port = atoi(start) * 256; + start = av_strtok(end, ",", &end); + if (!start) goto fail; + s->server_data_port += atoi(start); + av_dlog(s, "Server data port: %d\n", s->server_data_port); + + av_free(res); + return 0; + + fail: + av_free(res); + s->server_data_port = -1; + av_log(s, AV_LOG_ERROR, "Set passive mode failed\n" + "Your FTP server may use IPv6 which is not supported yet.\n"); + return AVERROR(EIO); +} + +static int ftp_current_dir(FTPContext *s) +{ + char *res = NULL, *start = NULL, *end = NULL; + int i; + static const char *command = "PWD\r\n"; + static const int pwd_codes[] = {257, 0}; + + if (ftp_send_command(s, command, pwd_codes, &res) != 257 || !res) + goto fail; + + for (i = 0; res[i]; ++i) { + if (res[i] == '"') { + if (!start) { + start = res + i + 1; + continue; + } + end = res + i; + break; + } + } + + if (!end) + goto fail; + + if (end > res && end[-1] == '/') { + end[-1] = '\0'; + } else + *end = '\0'; + av_strlcpy(s->path, start, sizeof(s->path)); + + av_free(res); + return 0; + + fail: + av_free(res); + return AVERROR(EIO); +} + +static int ftp_file_size(FTPContext *s) +{ + char command[CONTROL_BUFFER_SIZE]; + char *res = NULL; + static const int size_codes[] = {213, 501, 550, 0}; /* 501, 550 are incorrect codes */ + + snprintf(command, sizeof(command), "SIZE %s\r\n", s->path); + if (ftp_send_command(s, command, size_codes, &res) == 213 && res) { + s->filesize = strtoll(&res[4], NULL, 10); + } else { + s->filesize = -1; + av_free(res); + return AVERROR(EIO); + } + + av_free(res); + return 0; +} + +static int ftp_retrieve(FTPContext *s) +{ + char command[CONTROL_BUFFER_SIZE]; + static const int retr_codes[] = {150, 550, 554, 0}; /* 550, 554 are incorrect codes */ + + snprintf(command, sizeof(command), "RETR %s\r\n", s->path); + if (ftp_send_command(s, command, retr_codes, NULL) != 150) + return AVERROR(EIO); + + s->state = DOWNLOADING; + + return 0; +} + +static int ftp_store(FTPContext *s) +{ + char command[CONTROL_BUFFER_SIZE]; + static const int stor_codes[] = {150, 0}; + + snprintf(command, sizeof(command), "STOR %s\r\n", s->path); + if (ftp_send_command(s, command, stor_codes, NULL) != 150) + return AVERROR(EIO); + + s->state = UPLOADING; + + return 0; +} + +static int ftp_type(FTPContext *s) +{ + static const char *command = "TYPE I\r\n"; + static const int type_codes[] = {200, 500, 504, 0}; /* 500, 504 are incorrect codes */ + + if (ftp_send_command(s, command, type_codes, NULL) != 200) + return AVERROR(EIO); + + return 0; +} + +static int ftp_restart(FTPContext *s, int64_t pos) +{ + char command[CONTROL_BUFFER_SIZE]; + static const int rest_codes[] = {350, 500, 501, 0}; /* 500, 501 are incorrect codes */ + + snprintf(command, sizeof(command), "REST %"PRId64"\r\n", pos); + if (ftp_send_command(s, command, rest_codes, NULL) != 350) + return AVERROR(EIO); + + return 0; +} + +static int ftp_connect_control_connection(URLContext *h) +{ + char buf[CONTROL_BUFFER_SIZE], opts_format[20], *response = NULL; + int err; + AVDictionary *opts = NULL; + FTPContext *s = h->priv_data; + static const int connect_codes[] = {220, 0}; + + if (!s->conn_control) { + ff_url_join(buf, sizeof(buf), "tcp", NULL, + s->hostname, s->server_control_port, NULL); + if (s->rw_timeout != -1) { + snprintf(opts_format, sizeof(opts_format), "%d", s->rw_timeout); + av_dict_set(&opts, "timeout", opts_format, 0); + } /* if option is not given, don't pass it and let tcp use its own default */ + err = ffurl_open(&s->conn_control, buf, AVIO_FLAG_READ_WRITE, + &h->interrupt_callback, &opts); + av_dict_free(&opts); + if (err < 0) { + av_log(h, AV_LOG_ERROR, "Cannot open control connection\n"); + return err; + } + + /* check if server is ready */ + if (ftp_status(s, ((h->flags & AVIO_FLAG_WRITE) ? &response : NULL), connect_codes) != 220) { + av_log(h, AV_LOG_ERROR, "FTP server not ready for new users\n"); + return AVERROR(EACCES); + } + + if ((h->flags & AVIO_FLAG_WRITE) && av_stristr(response, "pure-ftpd")) { + av_log(h, AV_LOG_WARNING, "Pure-FTPd server is used as an output protocol. It is known issue this implementation may produce incorrect content and it cannot be fixed at this moment."); + } + av_free(response); + + if ((err = ftp_auth(s)) < 0) { + av_log(h, AV_LOG_ERROR, "FTP authentication failed\n"); + return err; + } + + if ((err = ftp_type(s)) < 0) { + av_log(h, AV_LOG_ERROR, "Set content type failed\n"); + return err; + } + } + return 0; +} + +static int ftp_connect_data_connection(URLContext *h) +{ + int err; + char buf[CONTROL_BUFFER_SIZE], opts_format[20]; + AVDictionary *opts = NULL; + FTPContext *s = h->priv_data; + + if (!s->conn_data) { + /* Enter passive mode */ + if ((err = ftp_passive_mode(s)) < 0) + return err; + /* Open data connection */ + ff_url_join(buf, sizeof(buf), "tcp", NULL, s->hostname, s->server_data_port, NULL); + if (s->rw_timeout != -1) { + snprintf(opts_format, sizeof(opts_format), "%d", s->rw_timeout); + av_dict_set(&opts, "timeout", opts_format, 0); + } /* if option is not given, don't pass it and let tcp use its own default */ + err = ffurl_open(&s->conn_data, buf, h->flags, + &h->interrupt_callback, &opts); + av_dict_free(&opts); + if (err < 0) + return err; + + if (s->position) + if ((err = ftp_restart(s, s->position)) < 0) + return err; + } + s->state = READY; + return 0; +} + +static int ftp_abort(URLContext *h) +{ + static const char *command = "ABOR\r\n"; + int err; + static const int abor_codes[] = {225, 226, 0}; + FTPContext *s = h->priv_data; + + /* According to RCF 959: + "ABOR command tells the server to abort the previous FTP + service command and any associated transfer of data." + + There are FTP server implementations that don't response + to any commands during data transfer in passive mode (including ABOR). + + This implementation closes data connection by force. + */ + + if (ftp_send_command(s, command, NULL, NULL) < 0) { + ftp_close_both_connections(s); + if ((err = ftp_connect_control_connection(h)) < 0) { + av_log(h, AV_LOG_ERROR, "Reconnect failed.\n"); + return err; + } + } else { + ftp_close_data_connection(s); + if (ftp_status(s, NULL, abor_codes) < 225) { + /* wu-ftpd also closes control connection after data connection closing */ + ffurl_closep(&s->conn_control); + if ((err = ftp_connect_control_connection(h)) < 0) { + av_log(h, AV_LOG_ERROR, "Reconnect failed.\n"); + return err; + } + } + } + + return 0; +} + +static int ftp_open(URLContext *h, const char *url, int flags) +{ + char proto[10], path[MAX_URL_SIZE]; + int err; + FTPContext *s = h->priv_data; + + av_dlog(h, "ftp protocol open\n"); + + s->state = DISCONNECTED; + s->filesize = -1; + s->position = 0; + + av_url_split(proto, sizeof(proto), + s->credencials, sizeof(s->credencials), + s->hostname, sizeof(s->hostname), + &s->server_control_port, + path, sizeof(path), + url); + + if (s->server_control_port < 0 || s->server_control_port > 65535) + s->server_control_port = 21; + + if ((err = ftp_connect_control_connection(h)) < 0) + goto fail; + + if ((err = ftp_current_dir(s)) < 0) + goto fail; + av_strlcat(s->path, path, sizeof(s->path)); + + if (ftp_restart(s, 0) < 0) { + h->is_streamed = 1; + } else { + if (ftp_file_size(s) < 0 && flags & AVIO_FLAG_READ) + h->is_streamed = 1; + if (s->write_seekable != 1 && flags & AVIO_FLAG_WRITE) + h->is_streamed = 1; + } + + return 0; + + fail: + av_log(h, AV_LOG_ERROR, "FTP open failed\n"); + ffurl_closep(&s->conn_control); + ffurl_closep(&s->conn_data); + return err; +} + +static int64_t ftp_seek(URLContext *h, int64_t pos, int whence) +{ + FTPContext *s = h->priv_data; + int err; + int64_t new_pos, fake_pos; + + av_dlog(h, "ftp protocol seek %"PRId64" %d\n", pos, whence); + + switch(whence) { + case AVSEEK_SIZE: + return s->filesize; + case SEEK_SET: + new_pos = pos; + break; + case SEEK_CUR: + new_pos = s->position + pos; + break; + case SEEK_END: + if (s->filesize < 0) + return AVERROR(EIO); + new_pos = s->filesize + pos; + break; + default: + return AVERROR(EINVAL); + } + + if (h->is_streamed) + return AVERROR(EIO); + + /* XXX: Simulate behaviour of lseek in file protocol, which could be treated as a reference */ + new_pos = FFMAX(0, new_pos); + fake_pos = s->filesize != -1 ? FFMIN(new_pos, s->filesize) : new_pos; + + if (fake_pos != s->position) { + if ((err = ftp_abort(h)) < 0) + return err; + s->position = fake_pos; + } + return new_pos; +} + +static int ftp_read(URLContext *h, unsigned char *buf, int size) +{ + FTPContext *s = h->priv_data; + int read, err, retry_done = 0; + + av_dlog(h, "ftp protocol read %d bytes\n", size); + retry: + if (s->state == DISCONNECTED) { + /* optimization */ + if (s->position >= s->filesize) + return 0; + if ((err = ftp_connect_data_connection(h)) < 0) + return err; + } + if (s->state == READY) { + if (s->position >= s->filesize) + return 0; + if ((err = ftp_retrieve(s)) < 0) + return err; + } + if (s->conn_data && s->state == DOWNLOADING) { + read = ffurl_read(s->conn_data, buf, size); + if (read >= 0) { + s->position += read; + if (s->position >= s->filesize) { + /* server will terminate, but keep current position to avoid madness */ + /* save position to restart from it */ + int64_t pos = s->position; + if (ftp_abort(h) < 0) { + s->position = pos; + return AVERROR(EIO); + } + s->position = pos; + } + } + if (read <= 0 && s->position < s->filesize && !h->is_streamed) { + /* Server closed connection. Probably due to inactivity */ + int64_t pos = s->position; + av_log(h, AV_LOG_INFO, "Reconnect to FTP server.\n"); + if ((err = ftp_abort(h)) < 0) + return err; + if ((err = ftp_seek(h, pos, SEEK_SET)) < 0) { + av_log(h, AV_LOG_ERROR, "Position cannot be restored.\n"); + return err; + } + if (!retry_done) { + retry_done = 1; + goto retry; + } + } + return read; + } + + av_log(h, AV_LOG_DEBUG, "FTP read failed\n"); + return AVERROR(EIO); +} + +static int ftp_write(URLContext *h, const unsigned char *buf, int size) +{ + int err; + FTPContext *s = h->priv_data; + int written; + + av_dlog(h, "ftp protocol write %d bytes\n", size); + + if (s->state == DISCONNECTED) { + if ((err = ftp_connect_data_connection(h)) < 0) + return err; + } + if (s->state == READY) { + if ((err = ftp_store(s)) < 0) + return err; + } + if (s->conn_data && s->state == UPLOADING) { + written = ffurl_write(s->conn_data, buf, size); + if (written > 0) { + s->position += written; + s->filesize = FFMAX(s->filesize, s->position); + } + return written; + } + + av_log(h, AV_LOG_ERROR, "FTP write failed\n"); + return AVERROR(EIO); +} + +static int ftp_close(URLContext *h) +{ + av_dlog(h, "ftp protocol close\n"); + + ftp_close_both_connections(h->priv_data); + + return 0; +} + +static int ftp_get_file_handle(URLContext *h) +{ + FTPContext *s = h->priv_data; + + av_dlog(h, "ftp protocol get_file_handle\n"); + + if (s->conn_data) + return ffurl_get_file_handle(s->conn_data); + + return AVERROR(EIO); +} + +static int ftp_shutdown(URLContext *h, int flags) +{ + FTPContext *s = h->priv_data; + + av_dlog(h, "ftp protocol shutdown\n"); + + if (s->conn_data) + return ffurl_shutdown(s->conn_data, flags); + + return AVERROR(EIO); +} + +URLProtocol ff_ftp_protocol = { + .name = "ftp", + .url_open = ftp_open, + .url_read = ftp_read, + .url_write = ftp_write, + .url_seek = ftp_seek, + .url_close = ftp_close, + .url_get_file_handle = ftp_get_file_handle, + .url_shutdown = ftp_shutdown, + .priv_data_size = sizeof(FTPContext), + .priv_data_class = &ftp_context_class, + .flags = URL_PROTOCOL_FLAG_NETWORK, +}; diff --git a/libavformat/g722.c b/libavformat/g722.c index 8052939c79..1a87c7d28c 100644 --- a/libavformat/g722.c +++ b/libavformat/g722.c @@ -2,23 +2,24 @@ * g722 raw demuxer * Copyright (c) 2010 Martin Storsjo * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "libavutil/avassert.h" #include "avformat.h" #include "internal.h" #include "rawdec.h" @@ -39,7 +40,7 @@ static int g722_read_header(AVFormatContext *s) st->codec->bits_per_coded_sample = av_get_bits_per_sample(st->codec->codec_id); - assert(st->codec->bits_per_coded_sample > 0); + av_assert0(st->codec->bits_per_coded_sample > 0); avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate); return 0; diff --git a/libavformat/g723_1.c b/libavformat/g723_1.c index b67c07c404..4f3ce8f0ae 100644 --- a/libavformat/g723_1.c +++ b/libavformat/g723_1.c @@ -2,20 +2,20 @@ * G.723.1 demuxer * Copyright (c) 2010 Mohamed Naufal Basheer * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -81,6 +81,6 @@ AVInputFormat ff_g723_1_demuxer = { .long_name = NULL_IF_CONFIG_SMALL("G.723.1"), .read_header = g723_1_init, .read_packet = g723_1_read_packet, - .extensions = "tco", + .extensions = "tco,rco,g723_1", .flags = AVFMT_GENERIC_INDEX }; diff --git a/libavformat/g729dec.c b/libavformat/g729dec.c new file mode 100644 index 0000000000..794558ef10 --- /dev/null +++ b/libavformat/g729dec.c @@ -0,0 +1,103 @@ +/* + * G.729 raw format demuxer + * Copyright (c) 2011 Vladimir Voroshilov + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "avformat.h" +#include "internal.h" +#include "libavutil/log.h" +#include "libavutil/opt.h" + +typedef struct G729DemuxerContext { + AVClass *class; + int bit_rate; +} G729DemuxerContext; + +static int g729_read_header(AVFormatContext *s) +{ + AVStream* st; + G729DemuxerContext *s1 = s->priv_data; + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_id = AV_CODEC_ID_G729; + st->codec->sample_rate = 8000; + st->codec->channels = 1; + + if (s1 && s1->bit_rate) { + s->bit_rate = s1->bit_rate; + } + + if (s->bit_rate == 0) { + av_log(s, AV_LOG_DEBUG, "No bitrate specified. Assuming 8000 b/s\n"); + s->bit_rate = 8000; + } + + if (s->bit_rate == 6400) { + st->codec->block_align = 8; + } else if (s->bit_rate == 8000) { + st->codec->block_align = 10; + } else { + av_log(s, AV_LOG_ERROR, "Only 8000 b/s and 6400 b/s bitrates are supported. Provided: %d b/s\n", s->bit_rate); + return AVERROR_INVALIDDATA; + } + + avpriv_set_pts_info(st, st->codec->block_align << 3, 1, st->codec->sample_rate); + return 0; +} +static int g729_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + int ret; + + ret = av_get_packet(s->pb, pkt, s->streams[0]->codec->block_align); + + pkt->stream_index = 0; + if (ret < 0) + return ret; + + pkt->dts = pkt->pts = pkt->pos / s->streams[0]->codec->block_align; + + return ret; +} + +static const AVOption g729_options[] = { + { "bit_rate", "", offsetof(G729DemuxerContext, bit_rate), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, AV_OPT_FLAG_DECODING_PARAM }, + { NULL }, +}; + +static const AVClass g729_demuxer_class = { + .class_name = "g729 demuxer", + .item_name = av_default_item_name, + .option = g729_options, + .version = LIBAVUTIL_VERSION_INT, +}; + +AVInputFormat ff_g729_demuxer = { + .name = "g729", + .long_name = NULL_IF_CONFIG_SMALL("G.729 raw format demuxer"), + .priv_data_size = sizeof(G729DemuxerContext), + .read_header = g729_read_header, + .read_packet = g729_read_packet, + .flags = AVFMT_GENERIC_INDEX, + .extensions = "g729", + .priv_class = &g729_demuxer_class, +}; diff --git a/libavformat/gif.c b/libavformat/gif.c index 085c2e03fc..f6e76254da 100644 --- a/libavformat/gif.c +++ b/libavformat/gif.c @@ -4,347 +4,186 @@ * * first version by Francois Revol <revol@free.fr> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -/* - * Features and limitations: - * - currently no compression is performed, - * in fact the size of the data is 9/8 the size of the image in 8bpp - * - uses only a global standard palette - * - tested with IE 5.0, Opera for BeOS, NetPositive (BeOS), and Mozilla (BeOS). - * - * Reference documents: - * http://www.goice.co.jp/member/mo/formats/gif.html - * http://astronomy.swin.edu.au/pbourke/dataformats/gif/ - * http://www.dcs.ed.ac.uk/home/mxr/gfx/2d/GIF89a.txt - * - * this url claims to have an LZW algorithm not covered by Unisys patent: - * http://www.msg.net/utility/whirlgif/gifencod.html - * could help reduce the size of the files _a lot_... - * some sites mentions an RLE type compression also. - */ - #include "avformat.h" +#include "internal.h" +#include "libavutil/avassert.h" +#include "libavutil/imgutils.h" #include "libavutil/log.h" #include "libavutil/opt.h" -/* The GIF format uses reversed order for bitstreams... */ -/* at least they don't use PDP_ENDIAN :) */ -#define BITSTREAM_WRITER_LE - -#include "libavcodec/put_bits.h" - -/* bitstream minipacket size */ -#define GIF_CHUNKS 100 - -/* slows down the decoding (and some browsers don't like it) */ -/* update on the 'some browsers don't like it issue from above: - * this was probably due to missing 'Data Sub-block Terminator' - * (byte 19) in the app_header */ -#define GIF_ADD_APP_HEADER // required to enable looping of animated gif - -typedef struct { - unsigned char r; - unsigned char g; - unsigned char b; -} rgb_triplet; - -/* we use the standard 216 color palette */ - -/* this script was used to create the palette: - * for r in 00 33 66 99 cc ff; do - * for g in 00 33 66 99 cc ff; do - * echo -n " " - * for b in 00 33 66 99 cc ff; do - * echo -n "{ 0x$r, 0x$g, 0x$b }, " - * done - * echo "" - * done - * done - */ - -static const rgb_triplet gif_clut[216] = { - { 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x33 }, { 0x00, 0x00, 0x66 }, { 0x00, 0x00, 0x99 }, { 0x00, 0x00, 0xcc }, { 0x00, 0x00, 0xff }, - { 0x00, 0x33, 0x00 }, { 0x00, 0x33, 0x33 }, { 0x00, 0x33, 0x66 }, { 0x00, 0x33, 0x99 }, { 0x00, 0x33, 0xcc }, { 0x00, 0x33, 0xff }, - { 0x00, 0x66, 0x00 }, { 0x00, 0x66, 0x33 }, { 0x00, 0x66, 0x66 }, { 0x00, 0x66, 0x99 }, { 0x00, 0x66, 0xcc }, { 0x00, 0x66, 0xff }, - { 0x00, 0x99, 0x00 }, { 0x00, 0x99, 0x33 }, { 0x00, 0x99, 0x66 }, { 0x00, 0x99, 0x99 }, { 0x00, 0x99, 0xcc }, { 0x00, 0x99, 0xff }, - { 0x00, 0xcc, 0x00 }, { 0x00, 0xcc, 0x33 }, { 0x00, 0xcc, 0x66 }, { 0x00, 0xcc, 0x99 }, { 0x00, 0xcc, 0xcc }, { 0x00, 0xcc, 0xff }, - { 0x00, 0xff, 0x00 }, { 0x00, 0xff, 0x33 }, { 0x00, 0xff, 0x66 }, { 0x00, 0xff, 0x99 }, { 0x00, 0xff, 0xcc }, { 0x00, 0xff, 0xff }, - { 0x33, 0x00, 0x00 }, { 0x33, 0x00, 0x33 }, { 0x33, 0x00, 0x66 }, { 0x33, 0x00, 0x99 }, { 0x33, 0x00, 0xcc }, { 0x33, 0x00, 0xff }, - { 0x33, 0x33, 0x00 }, { 0x33, 0x33, 0x33 }, { 0x33, 0x33, 0x66 }, { 0x33, 0x33, 0x99 }, { 0x33, 0x33, 0xcc }, { 0x33, 0x33, 0xff }, - { 0x33, 0x66, 0x00 }, { 0x33, 0x66, 0x33 }, { 0x33, 0x66, 0x66 }, { 0x33, 0x66, 0x99 }, { 0x33, 0x66, 0xcc }, { 0x33, 0x66, 0xff }, - { 0x33, 0x99, 0x00 }, { 0x33, 0x99, 0x33 }, { 0x33, 0x99, 0x66 }, { 0x33, 0x99, 0x99 }, { 0x33, 0x99, 0xcc }, { 0x33, 0x99, 0xff }, - { 0x33, 0xcc, 0x00 }, { 0x33, 0xcc, 0x33 }, { 0x33, 0xcc, 0x66 }, { 0x33, 0xcc, 0x99 }, { 0x33, 0xcc, 0xcc }, { 0x33, 0xcc, 0xff }, - { 0x33, 0xff, 0x00 }, { 0x33, 0xff, 0x33 }, { 0x33, 0xff, 0x66 }, { 0x33, 0xff, 0x99 }, { 0x33, 0xff, 0xcc }, { 0x33, 0xff, 0xff }, - { 0x66, 0x00, 0x00 }, { 0x66, 0x00, 0x33 }, { 0x66, 0x00, 0x66 }, { 0x66, 0x00, 0x99 }, { 0x66, 0x00, 0xcc }, { 0x66, 0x00, 0xff }, - { 0x66, 0x33, 0x00 }, { 0x66, 0x33, 0x33 }, { 0x66, 0x33, 0x66 }, { 0x66, 0x33, 0x99 }, { 0x66, 0x33, 0xcc }, { 0x66, 0x33, 0xff }, - { 0x66, 0x66, 0x00 }, { 0x66, 0x66, 0x33 }, { 0x66, 0x66, 0x66 }, { 0x66, 0x66, 0x99 }, { 0x66, 0x66, 0xcc }, { 0x66, 0x66, 0xff }, - { 0x66, 0x99, 0x00 }, { 0x66, 0x99, 0x33 }, { 0x66, 0x99, 0x66 }, { 0x66, 0x99, 0x99 }, { 0x66, 0x99, 0xcc }, { 0x66, 0x99, 0xff }, - { 0x66, 0xcc, 0x00 }, { 0x66, 0xcc, 0x33 }, { 0x66, 0xcc, 0x66 }, { 0x66, 0xcc, 0x99 }, { 0x66, 0xcc, 0xcc }, { 0x66, 0xcc, 0xff }, - { 0x66, 0xff, 0x00 }, { 0x66, 0xff, 0x33 }, { 0x66, 0xff, 0x66 }, { 0x66, 0xff, 0x99 }, { 0x66, 0xff, 0xcc }, { 0x66, 0xff, 0xff }, - { 0x99, 0x00, 0x00 }, { 0x99, 0x00, 0x33 }, { 0x99, 0x00, 0x66 }, { 0x99, 0x00, 0x99 }, { 0x99, 0x00, 0xcc }, { 0x99, 0x00, 0xff }, - { 0x99, 0x33, 0x00 }, { 0x99, 0x33, 0x33 }, { 0x99, 0x33, 0x66 }, { 0x99, 0x33, 0x99 }, { 0x99, 0x33, 0xcc }, { 0x99, 0x33, 0xff }, - { 0x99, 0x66, 0x00 }, { 0x99, 0x66, 0x33 }, { 0x99, 0x66, 0x66 }, { 0x99, 0x66, 0x99 }, { 0x99, 0x66, 0xcc }, { 0x99, 0x66, 0xff }, - { 0x99, 0x99, 0x00 }, { 0x99, 0x99, 0x33 }, { 0x99, 0x99, 0x66 }, { 0x99, 0x99, 0x99 }, { 0x99, 0x99, 0xcc }, { 0x99, 0x99, 0xff }, - { 0x99, 0xcc, 0x00 }, { 0x99, 0xcc, 0x33 }, { 0x99, 0xcc, 0x66 }, { 0x99, 0xcc, 0x99 }, { 0x99, 0xcc, 0xcc }, { 0x99, 0xcc, 0xff }, - { 0x99, 0xff, 0x00 }, { 0x99, 0xff, 0x33 }, { 0x99, 0xff, 0x66 }, { 0x99, 0xff, 0x99 }, { 0x99, 0xff, 0xcc }, { 0x99, 0xff, 0xff }, - { 0xcc, 0x00, 0x00 }, { 0xcc, 0x00, 0x33 }, { 0xcc, 0x00, 0x66 }, { 0xcc, 0x00, 0x99 }, { 0xcc, 0x00, 0xcc }, { 0xcc, 0x00, 0xff }, - { 0xcc, 0x33, 0x00 }, { 0xcc, 0x33, 0x33 }, { 0xcc, 0x33, 0x66 }, { 0xcc, 0x33, 0x99 }, { 0xcc, 0x33, 0xcc }, { 0xcc, 0x33, 0xff }, - { 0xcc, 0x66, 0x00 }, { 0xcc, 0x66, 0x33 }, { 0xcc, 0x66, 0x66 }, { 0xcc, 0x66, 0x99 }, { 0xcc, 0x66, 0xcc }, { 0xcc, 0x66, 0xff }, - { 0xcc, 0x99, 0x00 }, { 0xcc, 0x99, 0x33 }, { 0xcc, 0x99, 0x66 }, { 0xcc, 0x99, 0x99 }, { 0xcc, 0x99, 0xcc }, { 0xcc, 0x99, 0xff }, - { 0xcc, 0xcc, 0x00 }, { 0xcc, 0xcc, 0x33 }, { 0xcc, 0xcc, 0x66 }, { 0xcc, 0xcc, 0x99 }, { 0xcc, 0xcc, 0xcc }, { 0xcc, 0xcc, 0xff }, - { 0xcc, 0xff, 0x00 }, { 0xcc, 0xff, 0x33 }, { 0xcc, 0xff, 0x66 }, { 0xcc, 0xff, 0x99 }, { 0xcc, 0xff, 0xcc }, { 0xcc, 0xff, 0xff }, - { 0xff, 0x00, 0x00 }, { 0xff, 0x00, 0x33 }, { 0xff, 0x00, 0x66 }, { 0xff, 0x00, 0x99 }, { 0xff, 0x00, 0xcc }, { 0xff, 0x00, 0xff }, - { 0xff, 0x33, 0x00 }, { 0xff, 0x33, 0x33 }, { 0xff, 0x33, 0x66 }, { 0xff, 0x33, 0x99 }, { 0xff, 0x33, 0xcc }, { 0xff, 0x33, 0xff }, - { 0xff, 0x66, 0x00 }, { 0xff, 0x66, 0x33 }, { 0xff, 0x66, 0x66 }, { 0xff, 0x66, 0x99 }, { 0xff, 0x66, 0xcc }, { 0xff, 0x66, 0xff }, - { 0xff, 0x99, 0x00 }, { 0xff, 0x99, 0x33 }, { 0xff, 0x99, 0x66 }, { 0xff, 0x99, 0x99 }, { 0xff, 0x99, 0xcc }, { 0xff, 0x99, 0xff }, - { 0xff, 0xcc, 0x00 }, { 0xff, 0xcc, 0x33 }, { 0xff, 0xcc, 0x66 }, { 0xff, 0xcc, 0x99 }, { 0xff, 0xcc, 0xcc }, { 0xff, 0xcc, 0xff }, - { 0xff, 0xff, 0x00 }, { 0xff, 0xff, 0x33 }, { 0xff, 0xff, 0x66 }, { 0xff, 0xff, 0x99 }, { 0xff, 0xff, 0xcc }, { 0xff, 0xff, 0xff }, -}; - -/* GIF header */ static int gif_image_write_header(AVIOContext *pb, int width, int height, int loop_count, uint32_t *palette) { int i; - unsigned int v; avio_write(pb, "GIF", 3); avio_write(pb, "89a", 3); avio_wl16(pb, width); avio_wl16(pb, height); - avio_w8(pb, 0xf7); /* flags: global clut, 256 entries */ - avio_w8(pb, 0x1f); /* background color index */ - avio_w8(pb, 0); /* aspect ratio */ - - /* the global palette */ - if (!palette) { - avio_write(pb, (const unsigned char *)gif_clut, 216 * 3); - for (i = 0; i < ((256 - 216) * 3); i++) - avio_w8(pb, 0); - } else { + if (palette) { + avio_w8(pb, 0xf7); /* flags: global clut, 256 entries */ + avio_w8(pb, 0x1f); /* background color index */ + avio_w8(pb, 0); /* aspect ratio */ for (i = 0; i < 256; i++) { - v = palette[i]; - avio_w8(pb, (v >> 16) & 0xff); - avio_w8(pb, (v >> 8) & 0xff); - avio_w8(pb, (v) & 0xff); + const uint32_t v = palette[i] & 0xffffff; + avio_wb24(pb, v); } + } else { + avio_w8(pb, 0); /* flags */ + avio_w8(pb, 0); /* background color index */ + avio_w8(pb, 0); /* aspect ratio */ } - /* update: this is the 'NETSCAPE EXTENSION' that allows for looped animated - * GIF, see http://members.aol.com/royalef/gifabout.htm#net-extension - * - * byte 1 : 33 (hex 0x21) GIF Extension code - * byte 2 : 255 (hex 0xFF) Application Extension Label - * byte 3 : 11 (hex (0x0B) Length of Application Block - * (eleven bytes of data to follow) - * bytes 4 to 11 : "NETSCAPE" - * bytes 12 to 14 : "2.0" - * byte 15 : 3 (hex 0x03) Length of Data Sub-Block - * (three bytes of data to follow) - * byte 16 : 1 (hex 0x01) - * bytes 17 to 18 : 0 to 65535, an unsigned integer in - * lo-hi byte format. This indicate the - * number of iterations the loop should - * be executed. - * bytes 19 : 0 (hex 0x00) a Data Sub-block Terminator - */ - /* application extension header */ -#ifdef GIF_ADD_APP_HEADER - if (loop_count >= 0 && loop_count <= 65535) { - avio_w8(pb, 0x21); - avio_w8(pb, 0xff); - avio_w8(pb, 0x0b); - // bytes 4 to 14 + if (loop_count >= 0 ) { + /* "NETSCAPE EXTENSION" for looped animation GIF */ + avio_w8(pb, 0x21); /* GIF Extension code */ + avio_w8(pb, 0xff); /* Application Extension Label */ + avio_w8(pb, 0x0b); /* Length of Application Block */ avio_write(pb, "NETSCAPE2.0", sizeof("NETSCAPE2.0") - 1); - avio_w8(pb, 0x03); // byte 15 - avio_w8(pb, 0x01); // byte 16 + avio_w8(pb, 0x03); /* Length of Data Sub-Block */ + avio_w8(pb, 0x01); avio_wl16(pb, (uint16_t)loop_count); - avio_w8(pb, 0x00); // byte 19 + avio_w8(pb, 0x00); /* Data Sub-block Terminator */ } -#endif - return 0; -} - -/* this is maybe slow, but allows for extensions */ -static inline unsigned char gif_clut_index(uint8_t r, uint8_t g, uint8_t b) -{ - return (((r) / 47) % 6) * 6 * 6 + (((g) / 47) % 6) * 6 + (((b) / 47) % 6); -} - -static int gif_image_write_image(AVIOContext *pb, - int x1, int y1, int width, int height, - const uint8_t *buf, int linesize, int pix_fmt) -{ - PutBitContext p; - uint8_t buffer[200]; /* 100 * 9 / 8 = 113 */ - int i, left, w, v; - const uint8_t *ptr; - /* image block */ - - avio_w8(pb, 0x2c); - avio_wl16(pb, x1); - avio_wl16(pb, y1); - avio_wl16(pb, width); - avio_wl16(pb, height); - avio_w8(pb, 0x00); /* flags */ - /* no local clut */ - - avio_w8(pb, 0x08); - - left = width * height; - - init_put_bits(&p, buffer, 130); - -/* - * the thing here is the bitstream is written as little packets, with a size - * byte before but it's still the same bitstream between packets (no flush !) - */ - ptr = buf; - w = width; - while (left > 0) { - put_bits(&p, 9, 0x0100); /* clear code */ - - for (i = (left < GIF_CHUNKS) ? left : GIF_CHUNKS; i; i--) { - if (pix_fmt == AV_PIX_FMT_RGB24) { - v = gif_clut_index(ptr[0], ptr[1], ptr[2]); - ptr += 3; - } else { - v = *ptr++; - } - put_bits(&p, 9, v); - if (--w == 0) { - w = width; - buf += linesize; - ptr = buf; - } - } - - if (left <= GIF_CHUNKS) { - put_bits(&p, 9, 0x101); /* end of stream */ - flush_put_bits(&p); - } - if (put_bits_ptr(&p) - p.buf > 0) { - avio_w8(pb, put_bits_ptr(&p) - p.buf); /* byte count of the packet */ - avio_write(pb, p.buf, put_bits_ptr(&p) - p.buf); /* the actual buffer */ - p.buf_ptr = p.buf; /* dequeue the bytes off the bitstream */ - } - left -= GIF_CHUNKS; - } - avio_w8(pb, 0x00); /* end of image block */ return 0; } typedef struct { - AVClass *class; /** Class for private options. */ - int64_t time, file_time; - uint8_t buffer[100]; /* data chunks */ + AVClass *class; int loop; + int last_delay; + AVPacket *prev_pkt; + int duration; } GIFContext; static int gif_write_header(AVFormatContext *s) { GIFContext *gif = s->priv_data; AVIOContext *pb = s->pb; - AVCodecContext *enc, *video_enc; - int i, width, height /*, rate*/; - -/* XXX: do we reject audio streams or just ignore them ? - * if (s->nb_streams > 1) - * return -1; - */ - gif->time = 0; - gif->file_time = 0; + AVCodecContext *video_enc; + int width, height; + uint32_t palette[AVPALETTE_COUNT]; - video_enc = NULL; - for (i = 0; i < s->nb_streams; i++) { - enc = s->streams[i]->codec; - if (enc->codec_type != AVMEDIA_TYPE_AUDIO) - video_enc = enc; + if (s->nb_streams != 1 || + s->streams[0]->codec->codec_type != AVMEDIA_TYPE_VIDEO || + s->streams[0]->codec->codec_id != AV_CODEC_ID_GIF) { + av_log(s, AV_LOG_ERROR, + "GIF muxer supports only a single video GIF stream.\n"); + return AVERROR(EINVAL); } - if (!video_enc) { - av_free(gif); - return -1; - } else { - width = video_enc->width; - height = video_enc->height; -// rate = video_enc->time_base.den; - } + video_enc = s->streams[0]->codec; + width = video_enc->width; + height = video_enc->height; - if (video_enc->pix_fmt != AV_PIX_FMT_RGB24) { - av_log(s, AV_LOG_ERROR, - "ERROR: gif only handles the rgb24 pixel format. Use -pix_fmt rgb24.\n"); - return AVERROR(EIO); + avpriv_set_pts_info(s->streams[0], 64, 1, 100); + if (avpriv_set_systematic_pal2(palette, video_enc->pix_fmt) < 0) { + av_assert0(video_enc->pix_fmt == AV_PIX_FMT_PAL8); + gif_image_write_header(pb, width, height, gif->loop, NULL); + } else { + gif_image_write_header(pb, width, height, gif->loop, palette); } - gif_image_write_header(pb, width, height, gif->loop, NULL); - avio_flush(s->pb); return 0; } -static int gif_write_video(AVFormatContext *s, AVCodecContext *enc, - const uint8_t *buf, int size) +static int flush_packet(AVFormatContext *s, AVPacket *new) { + GIFContext *gif = s->priv_data; + int size; AVIOContext *pb = s->pb; - int jiffies; + uint8_t flags = 0x4, transparent_color_index = 0x1f; + const uint32_t *palette; + AVPacket *pkt = gif->prev_pkt; + + if (!pkt) + return 0; + + /* Mark one colour as transparent if the input palette contains at least + * one colour that is more than 50% transparent. */ + palette = (uint32_t*)av_packet_get_side_data(pkt, AV_PKT_DATA_PALETTE, &size); + if (palette && size != AVPALETTE_SIZE) { + av_log(s, AV_LOG_ERROR, "Invalid palette extradata\n"); + return AVERROR_INVALIDDATA; + } + if (palette) { + unsigned i, smallest_alpha = 0xff; + + for (i = 0; i < AVPALETTE_COUNT; i++) { + const uint32_t v = palette[i]; + if (v >> 24 < smallest_alpha) { + smallest_alpha = v >> 24; + transparent_color_index = i; + } + } + if (smallest_alpha < 128) + flags |= 0x1; /* Transparent Color Flag */ + } + + if (new && new->pts != AV_NOPTS_VALUE) + gif->duration = av_clip_uint16(new->pts - gif->prev_pkt->pts); + else if (!new && gif->last_delay >= 0) + gif->duration = gif->last_delay; /* graphic control extension block */ avio_w8(pb, 0x21); avio_w8(pb, 0xf9); avio_w8(pb, 0x04); /* block size */ - avio_w8(pb, 0x04); /* flags */ - - /* 1 jiffy is 1/70 s */ - /* the delay_time field indicates the number of jiffies - 1 */ - /* XXX: should use delay, in order to be more accurate */ - /* instead of using the same rounded value each time */ - /* XXX: don't even remember if I really use it for now */ - jiffies = (70 * enc->time_base.num / enc->time_base.den) - 1; - - avio_wl16(pb, jiffies); - - avio_w8(pb, 0x1f); /* transparent color index */ + avio_w8(pb, flags); + avio_wl16(pb, gif->duration); + avio_w8(pb, transparent_color_index); avio_w8(pb, 0x00); - gif_image_write_image(pb, 0, 0, enc->width, enc->height, - buf, enc->width * 3, AV_PIX_FMT_RGB24); + avio_write(pb, pkt->data, pkt->size); + + av_free_packet(gif->prev_pkt); + if (new) + av_copy_packet(gif->prev_pkt, new); return 0; } static int gif_write_packet(AVFormatContext *s, AVPacket *pkt) { - AVCodecContext *codec = s->streams[pkt->stream_index]->codec; - if (codec->codec_type == AVMEDIA_TYPE_AUDIO) - return 0; /* just ignore audio */ - else - return gif_write_video(s, codec, pkt->data, pkt->size); + GIFContext *gif = s->priv_data; + + if (!gif->prev_pkt) { + gif->prev_pkt = av_malloc(sizeof(*gif->prev_pkt)); + if (!gif->prev_pkt) + return AVERROR(ENOMEM); + return av_copy_packet(gif->prev_pkt, pkt); + } + return flush_packet(s, pkt); } static int gif_write_trailer(AVFormatContext *s) { + GIFContext *gif = s->priv_data; AVIOContext *pb = s->pb; + flush_packet(s, NULL); + av_freep(&gif->prev_pkt); avio_w8(pb, 0x3b); return 0; @@ -353,8 +192,10 @@ static int gif_write_trailer(AVFormatContext *s) #define OFFSET(x) offsetof(GIFContext, x) #define ENC AV_OPT_FLAG_ENCODING_PARAM static const AVOption options[] = { - { "loop", "Number of times to loop the output.", OFFSET(loop), - AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 65535, ENC }, + { "loop", "Number of times to loop the output: -1 - no loop, 0 - infinite loop", OFFSET(loop), + AV_OPT_TYPE_INT, { .i64 = 0 }, -1, 65535, ENC }, + { "final_delay", "Force delay (in ms) after the last frame", OFFSET(last_delay), + AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 65535, ENC }, { NULL }, }; @@ -372,9 +213,10 @@ AVOutputFormat ff_gif_muxer = { .extensions = "gif", .priv_data_size = sizeof(GIFContext), .audio_codec = AV_CODEC_ID_NONE, - .video_codec = AV_CODEC_ID_RAWVIDEO, + .video_codec = AV_CODEC_ID_GIF, .write_header = gif_write_header, .write_packet = gif_write_packet, .write_trailer = gif_write_trailer, .priv_class = &gif_muxer_class, + .flags = AVFMT_VARIABLE_FPS, }; diff --git a/libavformat/gifdec.c b/libavformat/gifdec.c new file mode 100644 index 0000000000..2981bcabbe --- /dev/null +++ b/libavformat/gifdec.c @@ -0,0 +1,334 @@ +/* + * GIF demuxer + * Copyright (c) 2012 Vitaliy E Sugrobov + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * GIF demuxer. + */ + +#include "avformat.h" +#include "libavutil/intreadwrite.h" +#include "libavutil/opt.h" +#include "internal.h" +#include "libavcodec/gif.h" + +typedef struct GIFDemuxContext { + const AVClass *class; + /** + * Time span in hundredths of second before + * the next frame should be drawn on screen. + */ + int delay; + /** + * Minimum allowed delay between frames in hundredths of + * second. Values below this threshold considered to be + * invalid and set to value of default_delay. + */ + int min_delay; + int default_delay; + + /** + * loop options + */ + int total_iter; + int iter_count; + int ignore_loop; +} GIFDemuxContext; + +/** + * Major web browsers display gifs at ~10-15fps when rate + * is not explicitly set or have too low values. We assume default rate to be 10. + * Default delay = 100hundredths of second / 10fps = 10hos per frame. + */ +#define GIF_DEFAULT_DELAY 10 +/** + * By default delay values less than this threshold considered to be invalid. + */ +#define GIF_MIN_DELAY 2 + +static int gif_probe(AVProbeData *p) +{ + /* check magick */ + if (memcmp(p->buf, gif87a_sig, 6) && memcmp(p->buf, gif89a_sig, 6)) + return 0; + + /* width or height contains zero? */ + if (!AV_RL16(&p->buf[6]) || !AV_RL16(&p->buf[8])) + return 0; + + return AVPROBE_SCORE_MAX; +} + +static int resync(AVIOContext *pb) +{ + int i; + for (i = 0; i < 6; i++) { + int b = avio_r8(pb); + if (b != gif87a_sig[i] && b != gif89a_sig[i]) + i = -(b != 'G'); + if (url_feof(pb)) + return AVERROR_EOF; + } + return 0; +} + +static int gif_read_header(AVFormatContext *s) +{ + GIFDemuxContext *gdc = s->priv_data; + AVIOContext *pb = s->pb; + AVStream *st; + int width, height, ret; + + if ((ret = resync(pb)) < 0) + return ret; + + gdc->delay = gdc->default_delay; + width = avio_rl16(pb); + height = avio_rl16(pb); + + if (width == 0 || height == 0) + return AVERROR_INVALIDDATA; + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + /* GIF format operates with time in "hundredths of second", + * therefore timebase is 1/100 */ + avpriv_set_pts_info(st, 64, 1, 100); + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->codec->codec_id = AV_CODEC_ID_GIF; + st->codec->width = width; + st->codec->height = height; + + /* jump to start because gif decoder needs header data too */ + if (avio_seek(pb, 0, SEEK_SET) != 0) + return AVERROR(EIO); + + return 0; +} + +static int gif_skip_subblocks(AVIOContext *pb) +{ + int sb_size, ret = 0; + + while (0x00 != (sb_size = avio_r8(pb))) { + if ((ret = avio_skip(pb, sb_size)) < 0) + return ret; + } + + return ret; +} + +static int gif_read_ext(AVFormatContext *s) +{ + GIFDemuxContext *gdc = s->priv_data; + AVIOContext *pb = s->pb; + int sb_size, ext_label = avio_r8(pb); + int ret; + + if (ext_label == GIF_GCE_EXT_LABEL) { + if ((sb_size = avio_r8(pb)) < 4) { + av_log(s, AV_LOG_FATAL, "Graphic Control Extension block's size less than 4.\n"); + return AVERROR_INVALIDDATA; + } + + /* skip packed fields */ + if ((ret = avio_skip(pb, 1)) < 0) + return ret; + + gdc->delay = avio_rl16(pb); + + if (gdc->delay < gdc->min_delay) + gdc->delay = gdc->default_delay; + + /* skip the rest of the Graphic Control Extension block */ + if ((ret = avio_skip(pb, sb_size - 3)) < 0 ) + return ret; + } else if (ext_label == GIF_APP_EXT_LABEL) { + uint8_t data[256]; + + sb_size = avio_r8(pb); + ret = avio_read(pb, data, sb_size); + if (ret < 0 || !sb_size) + return ret; + + if (sb_size == strlen(NETSCAPE_EXT_STR)) { + sb_size = avio_r8(pb); + ret = avio_read(pb, data, sb_size); + if (ret < 0 || !sb_size) + return ret; + + if (sb_size == 3 && data[0] == 1) { + gdc->total_iter = AV_RL16(data+1); + + if (gdc->total_iter == 0) + gdc->total_iter = -1; + } + } + } + + if ((ret = gif_skip_subblocks(pb)) < 0) + return ret; + + return 0; +} + +static int gif_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + GIFDemuxContext *gdc = s->priv_data; + AVIOContext *pb = s->pb; + int packed_fields, block_label, ct_size, + keyframe, frame_parsed = 0, ret; + int64_t frame_start = avio_tell(pb), frame_end; + unsigned char buf[6]; + + if ((ret = avio_read(pb, buf, 6)) == 6) { + keyframe = memcmp(buf, gif87a_sig, 6) == 0 || + memcmp(buf, gif89a_sig, 6) == 0; + } else if (ret < 0) { + return ret; + } else { + keyframe = 0; + } + + if (keyframe) { +parse_keyframe: + /* skip 2 bytes of width and 2 of height */ + if ((ret = avio_skip(pb, 4)) < 0) + return ret; + + packed_fields = avio_r8(pb); + + /* skip 1 byte of Background Color Index and 1 byte of Pixel Aspect Ratio */ + if ((ret = avio_skip(pb, 2)) < 0) + return ret; + + /* global color table presence */ + if (packed_fields & 0x80) { + ct_size = 3 * (1 << ((packed_fields & 0x07) + 1)); + + if ((ret = avio_skip(pb, ct_size)) < 0) + return ret; + } + } else { + avio_seek(pb, -ret, SEEK_CUR); + ret = AVERROR_EOF; + } + + while (GIF_TRAILER != (block_label = avio_r8(pb)) && !url_feof(pb)) { + if (block_label == GIF_EXTENSION_INTRODUCER) { + if ((ret = gif_read_ext (s)) < 0 ) + goto resync; + } else if (block_label == GIF_IMAGE_SEPARATOR) { + /* skip to last byte of Image Descriptor header */ + if ((ret = avio_skip(pb, 8)) < 0) + return ret; + + packed_fields = avio_r8(pb); + + /* local color table presence */ + if (packed_fields & 0x80) { + ct_size = 3 * (1 << ((packed_fields & 0x07) + 1)); + + if ((ret = avio_skip(pb, ct_size)) < 0) + return ret; + } + + /* read LZW Minimum Code Size */ + if (avio_r8(pb) < 1) { + av_log(s, AV_LOG_ERROR, "lzw minimum code size must be >= 1\n"); + goto resync; + } + + if ((ret = gif_skip_subblocks(pb)) < 0) + goto resync; + + frame_end = avio_tell(pb); + + if (avio_seek(pb, frame_start, SEEK_SET) != frame_start) + return AVERROR(EIO); + + ret = av_get_packet(pb, pkt, frame_end - frame_start); + if (ret < 0) + return ret; + + if (keyframe) + pkt->flags |= AV_PKT_FLAG_KEY; + + pkt->stream_index = 0; + pkt->duration = gdc->delay; + + /* Graphic Control Extension's scope is single frame. + * Remove its influence. */ + gdc->delay = gdc->default_delay; + frame_parsed = 1; + + break; + } else { + av_log(s, AV_LOG_ERROR, "invalid block label\n"); +resync: + if (!keyframe) + avio_seek(pb, frame_start, SEEK_SET); + if ((ret = resync(pb)) < 0) + return ret; + frame_start = avio_tell(pb) - 6; + keyframe = 1; + goto parse_keyframe; + } + } + + if ((ret >= 0 && !frame_parsed) || ret == AVERROR_EOF) { + /* This might happen when there is no image block + * between extension blocks and GIF_TRAILER or EOF */ + if (!gdc->ignore_loop && (block_label == GIF_TRAILER || url_feof(pb)) + && (gdc->total_iter < 0 || ++gdc->iter_count < gdc->total_iter)) + return avio_seek(pb, 0, SEEK_SET); + return AVERROR_EOF; + } else + return ret; +} + +static const AVOption options[] = { + { "min_delay" , "minimum valid delay between frames (in hundredths of second)", offsetof(GIFDemuxContext, min_delay) , AV_OPT_TYPE_INT, {.i64 = GIF_MIN_DELAY} , 0, 100 * 60, AV_OPT_FLAG_DECODING_PARAM }, + { "default_delay", "default delay between frames (in hundredths of second)" , offsetof(GIFDemuxContext, default_delay), AV_OPT_TYPE_INT, {.i64 = GIF_DEFAULT_DELAY}, 0, 100 * 60, AV_OPT_FLAG_DECODING_PARAM }, + { "ignore_loop" , "ignore loop setting (netscape extension)" , offsetof(GIFDemuxContext, ignore_loop) , AV_OPT_TYPE_INT, {.i64 = 1} , 0, 1, AV_OPT_FLAG_DECODING_PARAM }, + { NULL }, +}; + +static const AVClass demuxer_class = { + .class_name = "GIF demuxer", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, + .category = AV_CLASS_CATEGORY_DEMUXER, +}; + +AVInputFormat ff_gif_demuxer = { + .name = "gif", + .long_name = NULL_IF_CONFIG_SMALL("CompuServe Graphics Interchange Format (GIF)"), + .priv_data_size = sizeof(GIFDemuxContext), + .read_probe = gif_probe, + .read_header = gif_read_header, + .read_packet = gif_read_packet, + .flags = AVFMT_GENERIC_INDEX, + .priv_class = &demuxer_class, +}; diff --git a/libavformat/gopher.c b/libavformat/gopher.c index a149f7fd6b..9dc155ad11 100644 --- a/libavformat/gopher.c +++ b/libavformat/gopher.c @@ -5,20 +5,20 @@ * * based on libavformat/http.c, Copyright (c) 2000, 2001 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/gsmdec.c b/libavformat/gsmdec.c index f9ecbafcda..dbe557bfdd 100644 --- a/libavformat/gsmdec.c +++ b/libavformat/gsmdec.c @@ -2,20 +2,20 @@ * RAW GSM demuxer * Copyright (c) 2011 Justin Ruggles * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -48,7 +48,6 @@ static int gsm_read_packet(AVFormatContext *s, AVPacket *pkt) av_free_packet(pkt); return ret < 0 ? ret : AVERROR(EIO); } - pkt->size = ret; pkt->duration = 1; pkt->pts = pkt->pos / GSM_BLOCK_SIZE; @@ -81,7 +80,7 @@ static const AVOption options[] = { { NULL }, }; -static const AVClass class = { +static const AVClass gsm_class = { .class_name = "gsm demuxer", .item_name = av_default_item_name, .option = options, @@ -97,5 +96,5 @@ AVInputFormat ff_gsm_demuxer = { .flags = AVFMT_GENERIC_INDEX, .extensions = "gsm", .raw_codec_id = AV_CODEC_ID_GSM, - .priv_class = &class, + .priv_class = &gsm_class, }; diff --git a/libavformat/gxf.c b/libavformat/gxf.c index 6feb3c30dc..c36479a821 100644 --- a/libavformat/gxf.c +++ b/libavformat/gxf.c @@ -2,20 +2,20 @@ * GXF demuxer. * Copyright (c) 2006 Reimar Doeffinger * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -31,9 +31,31 @@ struct gxf_stream_info { int64_t last_field; AVRational frames_per_second; int32_t fields_per_frame; + int64_t track_aux_data; }; /** + * @brief parse gxf timecode and add it to metadata + */ +static int add_timecode_metadata(AVDictionary **pm, const char *key, uint32_t timecode, int fields_per_frame) +{ + char tmp[128]; + int field = timecode & 0xff; + int frame = fields_per_frame ? field / fields_per_frame : field; + int second = (timecode >> 8) & 0xff; + int minute = (timecode >> 16) & 0xff; + int hour = (timecode >> 24) & 0x1f; + int drop = (timecode >> 29) & 1; + // bit 30: color_frame, unused + // ignore invalid time code + if (timecode >> 31) + return 0; + snprintf(tmp, sizeof(tmp), "%02d:%02d:%02d%c%02d", + hour, minute, second, drop ? ';' : ':', frame); + return av_dict_set(pm, key, tmp, 0); +} + +/** * @brief parses a packet header, extracting type and length * @param pb AVIOContext to read header from * @param type detected packet type is stored here @@ -94,12 +116,10 @@ static int get_sindex(AVFormatContext *s, int id, int format) { st->codec->codec_id = AV_CODEC_ID_MJPEG; break; case 13: - case 15: - st->codec->codec_type = AVMEDIA_TYPE_VIDEO; - st->codec->codec_id = AV_CODEC_ID_DVVIDEO; - break; case 14: + case 15: case 16: + case 25: st->codec->codec_type = AVMEDIA_TYPE_VIDEO; st->codec->codec_id = AV_CODEC_ID_DVVIDEO; break; @@ -143,6 +163,12 @@ static int get_sindex(AVFormatContext *s, int id, int format) { st->codec->channel_layout = AV_CH_LAYOUT_STEREO; st->codec->sample_rate = 48000; break; + case 26: /* AVCi50 / AVCi100 (AVC Intra) */ + case 29: /* AVCHD */ + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->codec->codec_id = AV_CODEC_ID_H264; + st->need_parsing = AVSTREAM_PARSE_HEADERS; + break; // timecode tracks: case 7: case 8: @@ -150,6 +176,10 @@ static int get_sindex(AVFormatContext *s, int id, int format) { st->codec->codec_type = AVMEDIA_TYPE_DATA; st->codec->codec_id = AV_CODEC_ID_NONE; break; + case 30: + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->codec->codec_id = AV_CODEC_ID_DNXHD; + break; default: st->codec->codec_type = AVMEDIA_TYPE_UNKNOWN; st->codec->codec_id = AV_CODEC_ID_NONE; @@ -226,6 +256,7 @@ static AVRational fps_umf2avr(uint32_t flags) { static void gxf_track_tags(AVIOContext *pb, int *len, struct gxf_stream_info *si) { si->frames_per_second = (AVRational){0, 0}; si->fields_per_frame = 0; + si->track_aux_data = 0x80000000; while (*len >= 2) { GXFTrackTag tag = avio_r8(pb); int tlen = avio_r8(pb); @@ -239,7 +270,9 @@ static void gxf_track_tags(AVIOContext *pb, int *len, struct gxf_stream_info *si si->frames_per_second = fps_tag2avr(value); else if (tag == TRACK_FPF && (value == 1 || value == 2)) si->fields_per_frame = value; - } else + } else if (tlen == 8 && tag == TRACK_AUX) + si->track_aux_data = avio_rl64(pb); + else avio_skip(pb, tlen); } } @@ -249,15 +282,16 @@ static void gxf_track_tags(AVIOContext *pb, int *len, struct gxf_stream_info *si */ static void gxf_read_index(AVFormatContext *s, int pkt_len) { AVIOContext *pb = s->pb; - AVStream *st = s->streams[0]; + AVStream *st; uint32_t fields_per_map = avio_rl32(pb); uint32_t map_cnt = avio_rl32(pb); int i; pkt_len -= 8; - if (s->flags & AVFMT_FLAG_IGNIDX) { + if ((s->flags & AVFMT_FLAG_IGNIDX) || !s->streams) { avio_skip(pb, pkt_len); return; } + st = s->streams[0]; if (map_cnt > 1000) { av_log(s, AV_LOG_ERROR, "too many index entries %u (%x)\n", map_cnt, map_cnt); map_cnt = 1000; @@ -317,8 +351,6 @@ static int gxf_header(AVFormatContext *s) { track_id = avio_r8(pb); track_len = avio_rb16(pb); len -= track_len; - gxf_track_tags(pb, &track_len, si); - avio_skip(pb, track_len); if (!(track_type & 0x80)) { av_log(s, AV_LOG_ERROR, "invalid track type %x\n", track_type); continue; @@ -329,6 +361,16 @@ static int gxf_header(AVFormatContext *s) { continue; } track_id &= 0x3f; + gxf_track_tags(pb, &track_len, si); + // check for timecode tracks + if (track_type == 7 || track_type == 8 || track_type == 24) { + add_timecode_metadata(&s->metadata, "timecode", + si->track_aux_data & 0xffffffff, + si->fields_per_frame); + + } + avio_skip(pb, track_len); + idx = get_sindex(s, track_id, track_type); if (idx < 0) continue; st = s->streams[idx]; @@ -363,10 +405,21 @@ static int gxf_header(AVFormatContext *s) { avio_skip(pb, 0x30); // payload description fps = fps_umf2avr(avio_rl32(pb)); if (!main_timebase.num || !main_timebase.den) { + av_log(s, AV_LOG_WARNING, "No FPS track tag, using UMF fps tag." + " This might give wrong results.\n"); // this may not always be correct, but simply the best we can get main_timebase.num = fps.den; main_timebase.den = fps.num * 2; } + + if (len >= 0x18) { + len -= 0x18; + avio_skip(pb, 0x10); + add_timecode_metadata(&s->metadata, "timecode_at_mark_in", + avio_rl32(pb), si->fields_per_frame); + add_timecode_metadata(&s->metadata, "timecode_at_mark_out", + avio_rl32(pb), si->fields_per_frame); + } } else av_log(s, AV_LOG_INFO, "UMF packet too short\n"); } else @@ -385,7 +438,7 @@ static int gxf_header(AVFormatContext *s) { #define READ_ONE() \ { \ - if (!max_interval-- || pb->eof_reached) \ + if (!max_interval-- || url_feof(pb)) \ goto out; \ tmp = tmp << 8 | avio_r8(pb); \ } @@ -447,7 +500,7 @@ static int gxf_packet(AVFormatContext *s, AVPacket *pkt) { int field_nr, field_info, skip = 0; int stream_index; if (!parse_packet_header(pb, &pkt_type, &pkt_len)) { - if (!pb->eof_reached) + if (!url_feof(pb)) av_log(s, AV_LOG_ERROR, "sync lost\n"); return -1; } @@ -499,7 +552,7 @@ static int gxf_packet(AVFormatContext *s, AVPacket *pkt) { return ret; } - return AVERROR(EIO); + return AVERROR_EOF; } static int gxf_seek(AVFormatContext *s, int stream_index, int64_t timestamp, int flags) { diff --git a/libavformat/gxf.h b/libavformat/gxf.h index c1ac399458..dcdcdefc2d 100644 --- a/libavformat/gxf.h +++ b/libavformat/gxf.h @@ -2,20 +2,20 @@ * GXF demuxer * copyright (c) 2006 Reimar Doeffinger * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/gxfenc.c b/libavformat/gxfenc.c index fea1d5d818..5fa510057d 100644 --- a/libavformat/gxfenc.c +++ b/libavformat/gxfenc.c @@ -2,25 +2,28 @@ * GXF muxer. * Copyright (c) 2006 SmartJog S.A., Baptiste Coudurier <baptiste dot coudurier at smartjog dot com> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "libavutil/avassert.h" #include "libavutil/intfloat.h" +#include "libavutil/opt.h" #include "libavutil/mathematics.h" +#include "libavutil/timecode.h" #include "avformat.h" #include "internal.h" #include "gxf.h" @@ -28,6 +31,18 @@ #define GXF_AUDIO_PACKET_SIZE 65536 +#define GXF_TIMECODE(c, d, h, m, s, f) \ + ((c) << 30 | (d) << 29 | (h) << 24 | (m) << 16 | (s) << 8 | (f)) + +typedef struct GXFTimecode{ + int hh; + int mm; + int ss; + int ff; + int color; + int drop; +} GXFTimecode; + typedef struct GXFStreamContext { AudioInterleaveContext aic; uint32_t track_type; @@ -48,6 +63,7 @@ typedef struct GXFStreamContext { } GXFStreamContext; typedef struct GXFContext { + AVClass *av_class; uint32_t nb_fields; uint16_t audio_tracks; uint16_t mpeg_tracks; @@ -66,6 +82,7 @@ typedef struct GXFContext { uint64_t *map_offsets; ///< offset of map packets unsigned map_offsets_nb; unsigned packet_count; + GXFTimecode tc; } GXFContext; static const struct { @@ -188,33 +205,50 @@ static int gxf_write_mpeg_auxiliary(AVIOContext *pb, AVStream *st) else starting_line = 23; // default PAL - size = snprintf(buffer, 1024, "Ver 1\nBr %.6f\nIpg 1\nPpi %d\nBpiop %d\n" + size = snprintf(buffer, sizeof(buffer), "Ver 1\nBr %.6f\nIpg 1\nPpi %d\nBpiop %d\n" "Pix 0\nCf %d\nCg %d\nSl %d\nnl16 %d\nVi 1\nf1 1\n", (float)st->codec->bit_rate, sc->p_per_gop, sc->b_per_i_or_p, st->codec->pix_fmt == AV_PIX_FMT_YUV422P ? 2 : 1, sc->first_gop_closed == 1, starting_line, (st->codec->height + 15) / 16); + av_assert0(size < sizeof(buffer)); avio_w8(pb, TRACK_MPG_AUX); avio_w8(pb, size + 1); avio_write(pb, (uint8_t *)buffer, size + 1); return size + 3; } -static int gxf_write_timecode_auxiliary(AVIOContext *pb, GXFStreamContext *sc) +static int gxf_write_dv_auxiliary(AVIOContext *pb, AVStream *st) { - avio_w8(pb, 0); /* fields */ - avio_w8(pb, 0); /* seconds */ - avio_w8(pb, 0); /* minutes */ - avio_w8(pb, 0); /* flags + hours */ + int64_t track_aux_data = 0; + + avio_w8(pb, TRACK_AUX); + avio_w8(pb, 8); + if (st->codec->pix_fmt == AV_PIX_FMT_YUV420P) + track_aux_data |= 0x01; /* marks stream as DVCAM instead of DVPRO */ + track_aux_data |= 0x40000000; /* aux data is valid */ + avio_wl64(pb, track_aux_data); + return 8; +} + +static int gxf_write_timecode_auxiliary(AVIOContext *pb, GXFContext *gxf) +{ + uint32_t timecode = GXF_TIMECODE(gxf->tc.color, gxf->tc.drop, + gxf->tc.hh, gxf->tc.mm, + gxf->tc.ss, gxf->tc.ff); + + avio_w8(pb, TRACK_AUX); + avio_w8(pb, 8); + avio_wl32(pb, timecode); /* reserved */ - avio_wb32(pb, 0); + avio_wl32(pb, 0); return 8; } static int gxf_write_track_description(AVFormatContext *s, GXFStreamContext *sc, int index) { + GXFContext *gxf = s->priv_data; AVIOContext *pb = s->pb; int64_t pos; - int mpeg = sc->track_type == 4 || sc->track_type == 9; /* track description section */ avio_w8(pb, sc->media_type + 0x80); @@ -230,13 +264,21 @@ static int gxf_write_track_description(AVFormatContext *s, GXFStreamContext *sc, avio_wb16(pb, sc->media_info); avio_w8(pb, 0); - if (!mpeg) { - /* auxiliary information */ - avio_w8(pb, TRACK_AUX); - avio_w8(pb, 8); - if (sc->track_type == 3) - gxf_write_timecode_auxiliary(pb, sc); - else + switch (sc->track_type) { + case 3: /* timecode */ + gxf_write_timecode_auxiliary(pb, gxf); + break; + case 4: /* MPEG2 */ + case 9: /* MPEG1 */ + gxf_write_mpeg_auxiliary(pb, s->streams[index]); + break; + case 5: /* DV25 */ + case 6: /* DV50 */ + gxf_write_dv_auxiliary(pb, s->streams[index]); + break; + default: + avio_w8(pb, TRACK_AUX); + avio_w8(pb, 8); avio_wl64(pb, 0); } @@ -245,9 +287,6 @@ static int gxf_write_track_description(AVFormatContext *s, GXFStreamContext *sc, avio_w8(pb, 4); avio_wb32(pb, 0); - if (mpeg) - gxf_write_mpeg_auxiliary(pb, s->streams[index]); - /* frame rate */ avio_w8(pb, TRACK_FPS); avio_w8(pb, 4); @@ -398,25 +437,36 @@ static int gxf_write_umf_material_description(AVFormatContext *s) int timecode_base = gxf->time_base.den == 60000 ? 60 : 50; int64_t timestamp = 0; AVDictionaryEntry *t; - uint32_t timecode; + uint64_t nb_fields; + uint32_t timecode_in; // timecode at mark in + uint32_t timecode_out; // timecode at mark out if (t = av_dict_get(s->metadata, "creation_time", NULL, 0)) timestamp = ff_iso8601_to_unix_time(t->value); - // XXX drop frame - timecode = - gxf->nb_fields / (timecode_base * 3600) % 24 << 24 | // hours - gxf->nb_fields / (timecode_base * 60) % 60 << 16 | // minutes - gxf->nb_fields / timecode_base % 60 << 8 | // seconds - gxf->nb_fields % timecode_base; // fields + timecode_in = GXF_TIMECODE(gxf->tc.color, gxf->tc.drop, + gxf->tc.hh, gxf->tc.mm, + gxf->tc.ss, gxf->tc.ff); + + nb_fields = gxf->nb_fields + + gxf->tc.hh * (timecode_base * 3600) + + gxf->tc.mm * (timecode_base * 60) + + gxf->tc.ss * timecode_base + + gxf->tc.ff; + + timecode_out = GXF_TIMECODE(gxf->tc.color, gxf->tc.drop, + nb_fields / (timecode_base * 3600) % 24, + nb_fields / (timecode_base * 60) % 60, + nb_fields / timecode_base % 60, + nb_fields % timecode_base); avio_wl32(pb, gxf->flags); avio_wl32(pb, gxf->nb_fields); /* length of the longest track */ avio_wl32(pb, gxf->nb_fields); /* length of the shortest track */ avio_wl32(pb, 0); /* mark in */ avio_wl32(pb, gxf->nb_fields); /* mark out */ - avio_wl32(pb, 0); /* timecode mark in */ - avio_wl32(pb, timecode); /* timecode mark out */ + avio_wl32(pb, timecode_in); /* timecode mark in */ + avio_wl32(pb, timecode_out); /* timecode mark out */ avio_wl64(pb, timestamp); /* modification time */ avio_wl64(pb, timestamp); /* creation time */ avio_wl16(pb, 0); /* reserved */ @@ -491,9 +541,9 @@ static int gxf_write_umf_media_mpeg(AVIOContext *pb, AVStream *st) return 32; } -static int gxf_write_umf_media_timecode(AVIOContext *pb, GXFStreamContext *sc) +static int gxf_write_umf_media_timecode(AVIOContext *pb, int drop) { - avio_wl32(pb, 1); /* non drop frame */ + avio_wl32(pb, drop); /* drop frame */ avio_wl32(pb, 0); /* reserved */ avio_wl32(pb, 0); /* reserved */ avio_wl32(pb, 0); /* reserved */ @@ -504,13 +554,20 @@ static int gxf_write_umf_media_timecode(AVIOContext *pb, GXFStreamContext *sc) return 32; } -static int gxf_write_umf_media_dv(AVIOContext *pb, GXFStreamContext *sc) +static int gxf_write_umf_media_dv(AVIOContext *pb, GXFStreamContext *sc, AVStream *st) { - int i; - - for (i = 0; i < 8; i++) { - avio_wb32(pb, 0); - } + int dv_umf_data = 0; + + if (st->codec->pix_fmt == AV_PIX_FMT_YUV420P) + dv_umf_data |= 0x20; /* marks as DVCAM instead of DVPRO */ + avio_wl32(pb, dv_umf_data); + avio_wl32(pb, 0); + avio_wl32(pb, 0); + avio_wl32(pb, 0); + avio_wl32(pb, 0); + avio_wl32(pb, 0); + avio_wl32(pb, 0); + avio_wl32(pb, 0); return 32; } @@ -562,7 +619,7 @@ static int gxf_write_umf_media_description(AVFormatContext *s) avio_wl32(pb, 0); /* reserved */ if (sc == &gxf->timecode_track) - gxf_write_umf_media_timecode(pb, sc); /* 8 0bytes */ + gxf_write_umf_media_timecode(pb, gxf->tc.drop); else { AVStream *st = s->streams[i]; switch (st->codec->codec_id) { @@ -574,7 +631,7 @@ static int gxf_write_umf_media_description(AVFormatContext *s) gxf_write_umf_media_audio(pb, sc); break; case AV_CODEC_ID_DVVIDEO: - gxf_write_umf_media_dv(pb, sc); + gxf_write_umf_media_dv(pb, sc, st); break; } } @@ -625,6 +682,25 @@ static void gxf_init_timecode_track(GXFStreamContext *sc, GXFStreamContext *vsc) sc->fields = vsc->fields; } +static int gxf_init_timecode(AVFormatContext *s, GXFTimecode *tc, const char *tcstr, int fields) +{ + char c; + + if (sscanf(tcstr, "%d:%d:%d%c%d", &tc->hh, &tc->mm, &tc->ss, &c, &tc->ff) != 5) { + av_log(s, AV_LOG_ERROR, "unable to parse timecode, " + "syntax: hh:mm:ss[:;.]ff\n"); + return -1; + } + + tc->color = 0; + tc->drop = c != ':'; + + if (fields == 2) + tc->ff = tc->ff * 2; + + return 0; +} + static int gxf_write_header(AVFormatContext *s) { AVIOContext *pb = s->pb; @@ -632,9 +708,11 @@ static int gxf_write_header(AVFormatContext *s) GXFStreamContext *vsc = NULL; uint8_t tracks[255] = {0}; int i, media_info = 0; + int ret; + AVDictionaryEntry *tcr = av_dict_get(s->metadata, "timecode", NULL, 0); if (!pb->seekable) { - av_log(s, AV_LOG_ERROR, "gxf muxer does not support streamed output, patch welcome"); + av_log(s, AV_LOG_ERROR, "gxf muxer does not support streamed output, patch welcome\n"); return -1; } @@ -692,6 +770,8 @@ static int gxf_write_header(AVFormatContext *s) "gxf muxer only accepts PAL or NTSC resolutions currently\n"); return -1; } + if (!tcr) + tcr = av_dict_get(st->metadata, "timecode", NULL, 0); avpriv_set_pts_info(st, 64, gxf->time_base.num, gxf->time_base.den); if (gxf_find_lines_index(st) < 0) sc->lines_index = -1; @@ -743,10 +823,14 @@ static int gxf_write_header(AVFormatContext *s) if (ff_audio_interleave_init(s, GXF_samples_per_frame, (AVRational){ 1, 48000 }) < 0) return -1; + if (tcr && vsc) + gxf_init_timecode(s, &gxf->tc, tcr->value, vsc->fields); + gxf_init_timecode_track(&gxf->timecode_track, vsc); gxf->flags |= 0x200000; // time code track is non-drop frame - gxf_write_map_packet(s, 0); + if ((ret = gxf_write_map_packet(s, 0)) < 0) + return ret; gxf_write_flt_packet(s); gxf_write_umf_packet(s); @@ -770,6 +854,7 @@ static int gxf_write_trailer(AVFormatContext *s) AVIOContext *pb = s->pb; int64_t end; int i; + int ret; ff_audio_interleave_close(s); @@ -777,14 +862,16 @@ static int gxf_write_trailer(AVFormatContext *s) end = avio_tell(pb); avio_seek(pb, 0, SEEK_SET); /* overwrite map, flt and umf packets with new values */ - gxf_write_map_packet(s, 1); + if ((ret = gxf_write_map_packet(s, 1)) < 0) + return ret; gxf_write_flt_packet(s); gxf_write_umf_packet(s); avio_flush(pb); /* update duration in all map packets */ for (i = 1; i < gxf->map_offsets_nb; i++) { avio_seek(pb, gxf->map_offsets[i], SEEK_SET); - gxf_write_map_packet(s, 1); + if ((ret = gxf_write_map_packet(s, 1)) < 0) + return ret; avio_flush(pb); } @@ -863,6 +950,7 @@ static int gxf_write_packet(AVFormatContext *s, AVPacket *pkt) int64_t pos = avio_tell(pb); int padding = 0; int packet_start_offset = avio_tell(pb) / 1024; + int ret; gxf_write_packet_header(pb, PKT_MEDIA); if (st->codec->codec_id == AV_CODEC_ID_MPEG2VIDEO && pkt->size % 4) /* MPEG-2 frames must be padded */ @@ -880,6 +968,7 @@ static int gxf_write_packet(AVFormatContext *s, AVPacket *pkt) gxf->flt_entries_nb + 500, sizeof(*gxf->flt_entries))) < 0) { gxf->flt_entries_nb = 0; + gxf->nb_fields = 0; av_log(s, AV_LOG_ERROR, "could not reallocate flt entries\n"); return err; } @@ -892,7 +981,8 @@ static int gxf_write_packet(AVFormatContext *s, AVPacket *pkt) gxf->packet_count++; if (gxf->packet_count == 100) { - gxf_write_map_packet(s, 0); + if ((ret = gxf_write_map_packet(s, 0)) < 0) + return ret; gxf->packet_count = 0; } diff --git a/libavformat/h261dec.c b/libavformat/h261dec.c index 4a5805020f..8d882aec61 100644 --- a/libavformat/h261dec.c +++ b/libavformat/h261dec.c @@ -2,20 +2,20 @@ * RAW H.261 video demuxer * Copyright (c) 2009 Michael Niedermayer <michaelni@gmx.at> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -33,7 +33,7 @@ static int h261_probe(AVProbeData *p) int src_fmt=0; GetBitContext gb; - init_get_bits(&gb, p->buf, p->buf_size*8); + init_get_bits8(&gb, p->buf, p->buf_size); for(i=0; i<p->buf_size*8; i++){ if ((code & 0x01ff0000) || !(code & 0xff00)) { diff --git a/libavformat/h263dec.c b/libavformat/h263dec.c index 4d826d8c2d..e6e0345b69 100644 --- a/libavformat/h263dec.c +++ b/libavformat/h263dec.c @@ -2,20 +2,20 @@ * RAW H.263 video demuxer * Copyright (c) 2009 Michael Niedermayer <michaelni@gmx.at> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/h264dec.c b/libavformat/h264dec.c index 6fd45c1336..76073dda78 100644 --- a/libavformat/h264dec.c +++ b/libavformat/h264dec.c @@ -2,20 +2,20 @@ * RAW H.264 video demuxer * Copyright (c) 2008 Michael Niedermayer <michaelni@gmx.at> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/hevcdec.c b/libavformat/hevcdec.c index 65a3cdfb3c..e36a0513e3 100644 --- a/libavformat/hevcdec.c +++ b/libavformat/hevcdec.c @@ -2,20 +2,20 @@ * RAW HEVC video demuxer * Copyright (c) 2013 Dirk Farin <dirk.farin@gmail.com> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/hls.c b/libavformat/hls.c index ea16f8abf3..231f63062e 100644 --- a/libavformat/hls.c +++ b/libavformat/hls.c @@ -2,20 +2,20 @@ * Apple HTTP Live Streaming demuxer * Copyright (c) 2010 Martin Storsjo * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -103,6 +103,8 @@ typedef struct HLSContext { int64_t seek_timestamp; int seek_flags; AVIOInterruptCB *interrupt_callback; + char *user_agent; ///< holds HTTP user agent set as an AVOption to the HTTP protocol context + char *cookies; ///< holds HTTP cookie values set in either the initial response or as an AVOption to the HTTP protocol context } HLSContext; static int read_chomp_line(AVIOContext *s, char *buf, int maxlen) @@ -139,6 +141,8 @@ static void free_variant_list(HLSContext *c) av_free(var); } av_freep(&c->variants); + av_freep(&c->cookies); + av_freep(&c->user_agent); c->n_variants = 0; } @@ -208,14 +212,24 @@ static int parse_playlist(HLSContext *c, const char *url, uint8_t iv[16] = ""; int has_iv = 0; char key[MAX_URL_SIZE] = ""; - char line[1024]; + char line[MAX_URL_SIZE]; const char *ptr; int close_in = 0; if (!in) { + AVDictionary *opts = NULL; close_in = 1; - if ((ret = avio_open2(&in, url, AVIO_FLAG_READ, - c->interrupt_callback, NULL)) < 0) + /* Some HLS servers don't like being sent the range header */ + av_dict_set(&opts, "seekable", "0", 0); + + // broker prior HTTP options that should be consistent across requests + av_dict_set(&opts, "user-agent", c->user_agent, 0); + av_dict_set(&opts, "cookies", c->cookies, 0); + + ret = avio_open2(&in, url, AVIO_FLAG_READ, + c->interrupt_callback, &opts); + av_dict_free(&opts); + if (ret < 0) return ret; } @@ -229,7 +243,7 @@ static int parse_playlist(HLSContext *c, const char *url, free_segment_list(var); var->finished = 0; } - while (!in->eof_reached) { + while (!url_feof(in)) { read_chomp_line(in, line, sizeof(line)); if (av_strstart(line, "#EXT-X-STREAM-INF:", &ptr)) { struct variant_info info = {{0}}; @@ -324,19 +338,27 @@ fail: return ret; } -static int open_input(struct variant *var) +static int open_input(HLSContext *c, struct variant *var) { + AVDictionary *opts = NULL; + int ret; struct segment *seg = var->segments[var->cur_seq_no - var->start_seq_no]; + + // broker prior HTTP options that should be consistent across requests + av_dict_set(&opts, "user-agent", c->user_agent, 0); + av_dict_set(&opts, "cookies", c->cookies, 0); + av_dict_set(&opts, "seekable", "0", 0); + if (seg->key_type == KEY_NONE) { - return ffurl_open(&var->input, seg->url, AVIO_FLAG_READ, - &var->parent->interrupt_callback, NULL); + ret = ffurl_open(&var->input, seg->url, AVIO_FLAG_READ, + &var->parent->interrupt_callback, &opts); + goto cleanup; } else if (seg->key_type == KEY_AES_128) { char iv[33], key[33], url[MAX_URL_SIZE]; - int ret; if (strcmp(seg->key, var->key_url)) { URLContext *uc; if (ffurl_open(&uc, seg->key, AVIO_FLAG_READ, - &var->parent->interrupt_callback, NULL) == 0) { + &var->parent->interrupt_callback, &opts) == 0) { if (ffurl_read_complete(uc, var->key, sizeof(var->key)) != sizeof(var->key)) { av_log(NULL, AV_LOG_ERROR, "Unable to read key file %s\n", @@ -358,17 +380,25 @@ static int open_input(struct variant *var) snprintf(url, sizeof(url), "crypto:%s", seg->url); if ((ret = ffurl_alloc(&var->input, url, AVIO_FLAG_READ, &var->parent->interrupt_callback)) < 0) - return ret; + goto cleanup; av_opt_set(var->input->priv_data, "key", key, 0); av_opt_set(var->input->priv_data, "iv", iv, 0); - if ((ret = ffurl_connect(var->input, NULL)) < 0) { + /* Need to repopulate options */ + av_dict_free(&opts); + av_dict_set(&opts, "seekable", "0", 0); + if ((ret = ffurl_connect(var->input, &opts)) < 0) { ffurl_close(var->input); var->input = NULL; - return ret; + goto cleanup; } - return 0; + ret = 0; } - return AVERROR(ENOSYS); + else + ret = AVERROR(ENOSYS); + +cleanup: + av_dict_free(&opts); + return ret; } static int read_data(void *opaque, uint8_t *buf, int buf_size) @@ -413,7 +443,7 @@ reload: goto reload; } - ret = open_input(v); + ret = open_input(c, v); if (ret < 0) return ret; } @@ -446,11 +476,27 @@ reload: static int hls_read_header(AVFormatContext *s) { + URLContext *u = (s->flags & AVFMT_FLAG_CUSTOM_IO) ? NULL : s->pb->opaque; HLSContext *c = s->priv_data; int ret = 0, i, j, stream_offset = 0; c->interrupt_callback = &s->interrupt_callback; + // if the URL context is good, read important options we must broker later + if (u && u->prot->priv_data_class) { + // get the previous user agent & set back to null if string size is zero + av_freep(&c->user_agent); + av_opt_get(u->priv_data, "user-agent", 0, (uint8_t**)&(c->user_agent)); + if (c->user_agent && !strlen(c->user_agent)) + av_freep(&c->user_agent); + + // get the previous cookies & set back to null if string size is zero + av_freep(&c->cookies); + av_opt_get(u->priv_data, "cookies", 0, (uint8_t**)&(c->cookies)); + if (c->cookies && !strlen(c->cookies)) + av_freep(&c->cookies); + } + if ((ret = parse_playlist(c, s->filename, NULL, s->pb)) < 0) goto fail; @@ -520,6 +566,7 @@ static int hls_read_header(AVFormatContext *s) * so avformat_close_input shouldn't be called. If * avformat_open_input fails below, it frees and zeros the * context, so it doesn't need any special treatment like this. */ + av_log(s, AV_LOG_ERROR, "Error when loading first segment '%s'\n", v->segments[0]->url); avformat_free_context(v->ctx); v->ctx = NULL; goto fail; @@ -577,7 +624,7 @@ static int recheck_discard_flags(AVFormatContext *s, int first) /* Check if any new streams are needed */ for (i = 0; i < c->n_variants; i++) - c->variants[i]->cur_needed = 0;; + c->variants[i]->cur_needed = 0; for (i = 0; i < s->nb_streams; i++) { AVStream *st = s->streams[i]; @@ -627,7 +674,7 @@ start: AVStream *st; ret = av_read_frame(var->ctx, &var->pkt); if (ret < 0) { - if (!var->pb.eof_reached) + if (!url_feof(&var->pb) && ret != AVERROR_EOF) return ret; reset_packet(&var->pkt); break; diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c index 3dd4273eda..dcd53e5f40 100644 --- a/libavformat/hlsenc.c +++ b/libavformat/hlsenc.c @@ -2,20 +2,20 @@ * Apple HTTP Live Streaming segmenter * Copyright (c) 2012, Luca Barbato * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -165,8 +165,10 @@ static int hls_start(AVFormatContext *s) int err = 0; if (av_get_frame_filename(oc->filename, sizeof(oc->filename), - c->basename, c->wrap ? c->number % c->wrap : c->number) < 0) + c->basename, c->wrap ? c->number % c->wrap : c->number) < 0) { + av_log(oc, AV_LOG_ERROR, "Invalid segment filename template '%s'\n", c->basename); return AVERROR(EINVAL); + } c->number++; if ((err = avio_open2(&oc->pb, oc->filename, AVIO_FLAG_WRITE, @@ -249,6 +251,7 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) AVFormatContext *oc = hls->avf; AVStream *st = s->streams[pkt->stream_index]; int64_t end_pts = hls->recording_time * hls->number; + int is_ref_pkt = 1; int ret, can_split = 1; if (hls->start_pts == AV_NOPTS_VALUE) { @@ -259,10 +262,12 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) if (hls->has_video) { can_split = st->codec->codec_type == AVMEDIA_TYPE_VIDEO && pkt->flags & AV_PKT_FLAG_KEY; + is_ref_pkt = st->codec->codec_type == AVMEDIA_TYPE_VIDEO; } if (pkt->pts == AV_NOPTS_VALUE) - can_split = 0; - else + is_ref_pkt = can_split = 0; + + if (is_ref_pkt) hls->duration = av_rescale(pkt->pts - hls->end_pts, st->time_base.num, st->time_base.den); @@ -314,10 +319,10 @@ static int hls_write_trailer(struct AVFormatContext *s) #define OFFSET(x) offsetof(HLSContext, x) #define E AV_OPT_FLAG_ENCODING_PARAM static const AVOption options[] = { - {"start_number", "first number in the sequence", OFFSET(sequence),AV_OPT_TYPE_INT64, {.i64 = 0}, 0, INT64_MAX, E}, - {"hls_time", "segment length in seconds", OFFSET(time), AV_OPT_TYPE_FLOAT, {.dbl = 2}, 0, FLT_MAX, E}, - {"hls_list_size", "maximum number of playlist entries", OFFSET(size), AV_OPT_TYPE_INT, {.i64 = 5}, 0, INT_MAX, E}, - {"hls_wrap", "number after which the index wraps", OFFSET(wrap), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E}, + {"start_number", "set first number in the sequence", OFFSET(sequence),AV_OPT_TYPE_INT64, {.i64 = 0}, 0, INT64_MAX, E}, + {"hls_time", "set segment length in seconds", OFFSET(time), AV_OPT_TYPE_FLOAT, {.dbl = 2}, 0, FLT_MAX, E}, + {"hls_list_size", "set maximum number of playlist entries", OFFSET(size), AV_OPT_TYPE_INT, {.i64 = 5}, 0, INT_MAX, E}, + {"hls_wrap", "set number after which the index wraps", OFFSET(wrap), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E}, { NULL }, }; diff --git a/libavformat/hlsproto.c b/libavformat/hlsproto.c index ec357de94f..f6fcbe5e0e 100644 --- a/libavformat/hlsproto.c +++ b/libavformat/hlsproto.c @@ -2,20 +2,20 @@ * Apple HTTP Live Streaming Protocol Handler * Copyright (c) 2010 Martin Storsjo * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -126,7 +126,7 @@ static int parse_playlist(URLContext *h, const char *url) free_segment_list(s); s->finished = 0; - while (!in->eof_reached) { + while (!url_feof(in)) { read_chomp_line(in, line, sizeof(line)); if (av_strstart(line, "#EXT-X-STREAM-INF:", &ptr)) { struct variant_info info = {{0}}; diff --git a/libavformat/hnm.c b/libavformat/hnm.c index ee34a14aa4..e3154141f5 100644 --- a/libavformat/hnm.c +++ b/libavformat/hnm.c @@ -3,20 +3,20 @@ * * Copyright (c) 2012 David Kment * - * This file is part of Libav. + * This file is part of FFmpeg . * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg ; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/http.c b/libavformat/http.c index 344507ef8d..9be1181f30 100644 --- a/libavformat/http.c +++ b/libavformat/http.c @@ -1,21 +1,21 @@ /* - * HTTP protocol for avconv client + * HTTP protocol for ffmpeg client * Copyright (c) 2000, 2001 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -33,7 +33,7 @@ #include <zlib.h> #endif -/* XXX: POST protocol is not completely implemented because avconv uses +/* XXX: POST protocol is not completely implemented because ffmpeg uses only a subset of it. */ /* The IO buffer size is unrelated to the max URL size in itself, but needs @@ -50,18 +50,29 @@ typedef struct { int line_count; int http_code; int64_t chunksize; /**< Used if "Transfer-Encoding: chunked" otherwise -1. */ + char *content_type; + char *user_agent; int64_t off, filesize; + int icy_data_read; ///< how much data was read since last ICY metadata packet + int icy_metaint; ///< after how many bytes of read data a new metadata packet will be found char location[MAX_URL_SIZE]; HTTPAuthState auth_state; HTTPAuthState proxy_auth_state; char *headers; int willclose; /**< Set if the server correctly handles Connection: close and will close the connection after feeding us the content. */ + int seekable; /**< Control seekability, 0 = disable, 1 = enable, -1 = probe. */ int chunked_post; int end_chunked_post; /**< A flag which indicates if the end of chunked encoding has been sent. */ int end_header; /**< A flag which indicates we have finished to read POST reply. */ int multiple_requests; /**< A flag which indicates if we use persistent connections. */ uint8_t *post_data; int post_datalen; + int is_akamai; + char *mime_type; + char *cookies; ///< holds newline (\n) delimited Set-Cookie header field values (without the "Set-Cookie: " field name) + int icy; + char *icy_metadata_headers; + char *icy_metadata_packet; #if CONFIG_ZLIB int compressed; z_stream inflate_stream; @@ -74,11 +85,20 @@ typedef struct { #define OFFSET(x) offsetof(HTTPContext, x) #define D AV_OPT_FLAG_DECODING_PARAM #define E AV_OPT_FLAG_ENCODING_PARAM +#define DEFAULT_USER_AGENT "Lavf/" AV_STRINGIFY(LIBAVFORMAT_VERSION) static const AVOption options[] = { +{"seekable", "control seekability of connection", OFFSET(seekable), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 1, D }, {"chunked_post", "use chunked transfer-encoding for posts", OFFSET(chunked_post), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1, E }, -{"headers", "custom HTTP headers, can override built in default headers", OFFSET(headers), AV_OPT_TYPE_STRING, { 0 }, 0, 0, D|E }, +{"headers", "set custom HTTP headers, can override built in default headers", OFFSET(headers), AV_OPT_TYPE_STRING, { 0 }, 0, 0, D|E }, +{"content_type", "force a content type", OFFSET(content_type), AV_OPT_TYPE_STRING, { 0 }, 0, 0, D|E }, +{"user-agent", "override User-Agent header", OFFSET(user_agent), AV_OPT_TYPE_STRING, {.str = DEFAULT_USER_AGENT}, 0, 0, D }, {"multiple_requests", "use persistent connections", OFFSET(multiple_requests), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, D|E }, -{"post_data", "custom HTTP post data", OFFSET(post_data), AV_OPT_TYPE_BINARY, .flags = D|E }, +{"post_data", "set custom HTTP post data", OFFSET(post_data), AV_OPT_TYPE_BINARY, .flags = D|E }, +{"mime_type", "set MIME type", OFFSET(mime_type), AV_OPT_TYPE_STRING, {0}, 0, 0, 0 }, +{"cookies", "set cookies to be sent in applicable future requests, use newline delimited Set-Cookie HTTP field value syntax", OFFSET(cookies), AV_OPT_TYPE_STRING, {0}, 0, 0, D }, +{"icy", "request ICY metadata", OFFSET(icy), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, D }, +{"icy_metadata_headers", "return ICY metadata headers", OFFSET(icy_metadata_headers), AV_OPT_TYPE_STRING, {0}, 0, 0, 0 }, +{"icy_metadata_packet", "return current ICY metadata packet", OFFSET(icy_metadata_packet), AV_OPT_TYPE_STRING, {0}, 0, 0, 0 }, {"auth_type", "HTTP authentication type", OFFSET(auth_state.auth_type), AV_OPT_TYPE_INT, {.i64 = HTTP_AUTH_NONE}, HTTP_AUTH_NONE, HTTP_AUTH_BASIC, D|E, "auth_type" }, {"none", "No auth method set, autodetect", 0, AV_OPT_TYPE_CONST, {.i64 = HTTP_AUTH_NONE}, 0, 0, D|E, "auth_type" }, {"basic", "HTTP basic authentication", 0, AV_OPT_TYPE_CONST, {.i64 = HTTP_AUTH_BASIC}, 0, 0, D|E, "auth_type" }, @@ -174,8 +194,7 @@ static int http_open_cnx(URLContext *h, AVDictionary **options) if (s->http_code == 401) { if ((cur_auth_type == HTTP_AUTH_NONE || s->auth_state.stale) && s->auth_state.auth_type != HTTP_AUTH_NONE && attempts < 4) { - ffurl_close(s->hd); - s->hd = NULL; + ffurl_closep(&s->hd); goto redo; } else goto fail; @@ -183,8 +202,7 @@ static int http_open_cnx(URLContext *h, AVDictionary **options) if (s->http_code == 407) { if ((cur_proxy_auth_type == HTTP_AUTH_NONE || s->proxy_auth_state.stale) && s->proxy_auth_state.auth_type != HTTP_AUTH_NONE && attempts < 4) { - ffurl_close(s->hd); - s->hd = NULL; + ffurl_closep(&s->hd); goto redo; } else goto fail; @@ -192,8 +210,7 @@ static int http_open_cnx(URLContext *h, AVDictionary **options) if ((s->http_code == 301 || s->http_code == 302 || s->http_code == 303 || s->http_code == 307) && location_changed == 1) { /* url moved, get next */ - ffurl_close(s->hd); - s->hd = NULL; + ffurl_closep(&s->hd); if (redirects++ >= MAX_REDIRECTS) return AVERROR(EIO); /* Restart the authentication process with the new target, which @@ -206,8 +223,7 @@ static int http_open_cnx(URLContext *h, AVDictionary **options) return 0; fail: if (s->hd) - ffurl_close(s->hd); - s->hd = NULL; + ffurl_closep(&s->hd); return AVERROR(EIO); } @@ -218,6 +234,7 @@ int ff_http_do_new_request(URLContext *h, const char *uri) int ret; s->off = 0; + s->icy_data_read = 0; av_strlcpy(s->location, uri, sizeof(s->location)); av_dict_copy(&options, s->chained_options, 0); @@ -232,7 +249,10 @@ static int http_open(URLContext *h, const char *uri, int flags, HTTPContext *s = h->priv_data; int ret; - h->is_streamed = 1; + if( s->seekable == 1 ) + h->is_streamed = 0; + else + h->is_streamed = 1; s->filesize = -1; av_strlcpy(s->location, uri, sizeof(s->location)); @@ -296,6 +316,7 @@ static int process_line(URLContext *h, char *line, int line_count, { HTTPContext *s = h->priv_data; char *tag, *p, *end; + char redirected_location[MAX_URL_SIZE]; /* end of header */ if (line[0] == '\0') { @@ -335,7 +356,8 @@ static int process_line(URLContext *h, char *line, int line_count, while (av_isspace(*p)) p++; if (!av_strcasecmp(tag, "Location")) { - av_strlcpy(s->location, p, sizeof(s->location)); + ff_make_absolute_url(redirected_location, sizeof(redirected_location), s->location, p); + av_strlcpy(s->location, redirected_location, sizeof(s->location)); *new_location = 1; } else if (!av_strcasecmp (tag, "Content-Length") && s->filesize == -1) { s->filesize = strtoll(p, NULL, 10); @@ -348,8 +370,9 @@ static int process_line(URLContext *h, char *line, int line_count, if ((slash = strchr(p, '/')) && strlen(slash) > 0) s->filesize = strtoll(slash+1, NULL, 10); } - h->is_streamed = 0; /* we _can_ in fact seek */ - } else if (!av_strcasecmp(tag, "Accept-Ranges") && !strncmp(p, "bytes", 5)) { + if (s->seekable == -1 && (!s->is_akamai || s->filesize != 2147483647)) + h->is_streamed = 0; /* we _can_ in fact seek */ + } else if (!av_strcasecmp(tag, "Accept-Ranges") && !strncmp(p, "bytes", 5) && s->seekable == -1) { h->is_streamed = 0; } else if (!av_strcasecmp (tag, "Transfer-Encoding") && !av_strncasecmp(p, "chunked", 7)) { s->filesize = -1; @@ -363,6 +386,34 @@ static int process_line(URLContext *h, char *line, int line_count, } else if (!av_strcasecmp (tag, "Connection")) { if (!strcmp(p, "close")) s->willclose = 1; + } else if (!av_strcasecmp (tag, "Server") && !av_strcasecmp (p, "AkamaiGHost")) { + s->is_akamai = 1; + } else if (!av_strcasecmp (tag, "Content-Type")) { + av_free(s->mime_type); s->mime_type = av_strdup(p); + } else if (!av_strcasecmp (tag, "Set-Cookie")) { + if (!s->cookies) { + if (!(s->cookies = av_strdup(p))) + return AVERROR(ENOMEM); + } else { + char *tmp = s->cookies; + size_t str_size = strlen(tmp) + strlen(p) + 2; + if (!(s->cookies = av_malloc(str_size))) { + s->cookies = tmp; + return AVERROR(ENOMEM); + } + snprintf(s->cookies, str_size, "%s\n%s", tmp, p); + av_free(tmp); + } + } else if (!av_strcasecmp (tag, "Icy-MetaInt")) { + s->icy_metaint = strtoll(p, NULL, 10); + } else if (!av_strncasecmp(tag, "Icy-", 4)) { + // Concat all Icy- header lines + char *buf = av_asprintf("%s%s: %s\n", + s->icy_metadata_headers ? s->icy_metadata_headers : "", tag, p); + if (!buf) + return AVERROR(ENOMEM); + av_freep(&s->icy_metadata_headers); + s->icy_metadata_headers = buf; } else if (!av_strcasecmp (tag, "Content-Encoding")) { if (!av_strncasecmp(p, "gzip", 4) || !av_strncasecmp(p, "deflate", 7)) { #if CONFIG_ZLIB @@ -386,13 +437,109 @@ static int process_line(URLContext *h, char *line, int line_count, // the header at all if this is the case). } else { av_log(h, AV_LOG_WARNING, "Unknown content coding: %s\n", p); - return AVERROR(ENOSYS); } } } return 1; } +/** + * Create a string containing cookie values for use as a HTTP cookie header + * field value for a particular path and domain from the cookie values stored in + * the HTTP protocol context. The cookie string is stored in *cookies. + * + * @return a negative value if an error condition occurred, 0 otherwise + */ +static int get_cookies(HTTPContext *s, char **cookies, const char *path, + const char *domain) +{ + // cookie strings will look like Set-Cookie header field values. Multiple + // Set-Cookie fields will result in multiple values delimited by a newline + int ret = 0; + char *next, *cookie, *set_cookies = av_strdup(s->cookies), *cset_cookies = set_cookies; + + if (!set_cookies) return AVERROR(EINVAL); + + *cookies = NULL; + while ((cookie = av_strtok(set_cookies, "\n", &next))) { + int domain_offset = 0; + char *param, *next_param, *cdomain = NULL, *cpath = NULL, *cvalue = NULL; + set_cookies = NULL; + + while ((param = av_strtok(cookie, "; ", &next_param))) { + cookie = NULL; + if (!av_strncasecmp("path=", param, 5)) { + av_free(cpath); + cpath = av_strdup(¶m[5]); + } else if (!av_strncasecmp("domain=", param, 7)) { + av_free(cdomain); + cdomain = av_strdup(¶m[7]); + } else if (!av_strncasecmp("secure", param, 6) || + !av_strncasecmp("comment", param, 7) || + !av_strncasecmp("max-age", param, 7) || + !av_strncasecmp("version", param, 7)) { + // ignore Comment, Max-Age, Secure and Version + } else { + av_free(cvalue); + cvalue = av_strdup(param); + } + } + if (!cdomain) + cdomain = av_strdup(domain); + + // ensure all of the necessary values are valid + if (!cdomain || !cpath || !cvalue) { + av_log(s, AV_LOG_WARNING, + "Invalid cookie found, no value, path or domain specified\n"); + goto done_cookie; + } + + // check if the request path matches the cookie path + if (av_strncasecmp(path, cpath, strlen(cpath))) + goto done_cookie; + + // the domain should be at least the size of our cookie domain + domain_offset = strlen(domain) - strlen(cdomain); + if (domain_offset < 0) + goto done_cookie; + + // match the cookie domain + if (av_strcasecmp(&domain[domain_offset], cdomain)) + goto done_cookie; + + // cookie parameters match, so copy the value + if (!*cookies) { + if (!(*cookies = av_strdup(cvalue))) { + ret = AVERROR(ENOMEM); + goto done_cookie; + } + } else { + char *tmp = *cookies; + size_t str_size = strlen(cvalue) + strlen(*cookies) + 3; + if (!(*cookies = av_malloc(str_size))) { + ret = AVERROR(ENOMEM); + goto done_cookie; + } + snprintf(*cookies, str_size, "%s; %s", tmp, cvalue); + av_free(tmp); + } + + done_cookie: + av_free(cdomain); + av_free(cpath); + av_free(cvalue); + if (ret < 0) { + if (*cookies) av_freep(cookies); + av_free(cset_cookies); + return ret; + } + } + + av_free(cset_cookies); + + return 0; +} + static inline int has_header(const char *str, const char *header) { /* header + 2 to skip over CRLF prefix. (make sure you have one!) */ @@ -432,7 +579,7 @@ static int http_connect(URLContext *h, const char *path, const char *local_path, { HTTPContext *s = h->priv_data; int post, err; - char headers[1024] = ""; + char headers[4096] = ""; char *authstr = NULL, *proxyauthstr = NULL; int64_t off = s->off; int len = 0; @@ -469,12 +616,15 @@ static int http_connect(URLContext *h, const char *path, const char *local_path, /* set default headers if needed */ if (!has_header(s->headers, "\r\nUser-Agent: ")) - len += av_strlcatf(headers + len, sizeof(headers) - len, - "User-Agent: %s\r\n", LIBAVFORMAT_IDENT); + len += av_strlcatf(headers + len, sizeof(headers) - len, + "User-Agent: %s\r\n", s->user_agent); if (!has_header(s->headers, "\r\nAccept: ")) len += av_strlcpy(headers + len, "Accept: */*\r\n", sizeof(headers) - len); - if (!has_header(s->headers, "\r\nRange: ") && !post) + // Note: we send this on purpose even when s->off is 0 when we're probing, + // since it allows us to detect more reliably if a (non-conforming) + // server supports seeking by analysing the reply headers. + if (!has_header(s->headers, "\r\nRange: ") && !post && (s->off > 0 || s->seekable == -1)) len += av_strlcatf(headers + len, sizeof(headers) - len, "Range: bytes=%"PRId64"-\r\n", s->off); if (send_expect_100 && !has_header(s->headers, "\r\nExpect: ")) @@ -497,6 +647,21 @@ static int http_connect(URLContext *h, const char *path, const char *local_path, if (!has_header(s->headers, "\r\nContent-Length: ") && s->post_data) len += av_strlcatf(headers + len, sizeof(headers) - len, "Content-Length: %d\r\n", s->post_datalen); + if (!has_header(s->headers, "\r\nContent-Type: ") && s->content_type) + len += av_strlcatf(headers + len, sizeof(headers) - len, + "Content-Type: %s\r\n", s->content_type); + if (!has_header(s->headers, "\r\nCookie: ") && s->cookies) { + char *cookies = NULL; + if (!get_cookies(s, &cookies, path, hoststr)) { + len += av_strlcatf(headers + len, sizeof(headers) - len, + "Cookie: %s\r\n", cookies); + av_free(cookies); + } + } + if (!has_header(s->headers, "\r\nIcy-MetaData: ") && s->icy) { + len += av_strlcatf(headers + len, sizeof(headers) - len, + "Icy-MetaData: %d\r\n", 1); + } /* now add in custom headers */ if (s->headers) @@ -530,6 +695,7 @@ static int http_connect(URLContext *h, const char *path, const char *local_path, s->buf_end = s->buffer; s->line_count = 0; s->off = 0; + s->icy_data_read = 0; s->filesize = -1; s->willclose = 0; s->end_chunked_post = 0; @@ -569,6 +735,7 @@ static int http_buf_read(URLContext *h, uint8_t *buf, int size) } if (len > 0) { s->off += len; + s->icy_data_read += len; if (s->chunksize > 0) s->chunksize -= len; } @@ -642,6 +809,32 @@ static int http_read(URLContext *h, uint8_t *buf, int size) } size = FFMIN(size, s->chunksize); } + if (s->icy_metaint > 0) { + int remaining = s->icy_metaint - s->icy_data_read; /* until next metadata packet */ + if (!remaining) { + // The metadata packet is variable sized. It has a 1 byte header + // which sets the length of the packet (divided by 16). If it's 0, + // the metadata doesn't change. After the packet, icy_metaint bytes + // of normal data follow. + int ch = http_getc(s); + if (ch < 0) + return ch; + if (ch > 0) { + char data[255 * 16 + 1]; + int n; + int ret; + ch *= 16; + for (n = 0; n < ch; n++) + data[n] = http_getc(s); + data[ch + 1] = 0; + if ((ret = av_opt_set(s, "icy_metadata_packet", data, 0)) < 0) + return ret; + } + s->icy_data_read = 0; + remaining = s->icy_metaint; + } + size = FFMIN(size, remaining); + } #if CONFIG_ZLIB if (s->compressed) return http_buf_read_compressed(h, buf, size); @@ -708,7 +901,7 @@ static int http_close(URLContext *h) } if (s->hd) - ffurl_close(s->hd); + ffurl_closep(&s->hd); av_dict_free(&s->chained_options); return ret; } @@ -796,7 +989,7 @@ static int http_proxy_close(URLContext *h) { HTTPContext *s = h->priv_data; if (s->hd) - ffurl_close(s->hd); + ffurl_closep(&s->hd); return 0; } @@ -811,7 +1004,10 @@ static int http_proxy_open(URLContext *h, const char *uri, int flags) char *authstr; int new_loc; - h->is_streamed = 1; + if( s->seekable == 1 ) + h->is_streamed = 0; + else + h->is_streamed = 1; av_url_split(NULL, 0, auth, sizeof(auth), hostname, sizeof(hostname), &port, pathbuf, sizeof(pathbuf), uri); @@ -867,8 +1063,7 @@ redo: if (s->http_code == 407 && (cur_auth_type == HTTP_AUTH_NONE || s->proxy_auth_state.stale) && s->proxy_auth_state.auth_type != HTTP_AUTH_NONE && attempts < 2) { - ffurl_close(s->hd); - s->hd = NULL; + ffurl_closep(&s->hd); goto redo; } diff --git a/libavformat/http.h b/libavformat/http.h index f0d9d4aea8..a19ad8ed3e 100644 --- a/libavformat/http.h +++ b/libavformat/http.h @@ -2,20 +2,20 @@ * HTTP definitions * Copyright (c) 2010 Josh Allmann * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -38,7 +38,7 @@ void ff_http_init_auth_state(URLContext *dest, const URLContext *src); /** * Send a new HTTP request, reusing the old connection. * - * @param h pointer to the ressource + * @param h pointer to the resource * @param uri uri used to perform the request * @return a negative value if an error condition occurred, 0 * otherwise diff --git a/libavformat/httpauth.c b/libavformat/httpauth.c index b96da3ef12..5ca48b9c1d 100644 --- a/libavformat/httpauth.c +++ b/libavformat/httpauth.c @@ -2,20 +2,20 @@ * HTTP authentication * Copyright (c) 2010 Martin Storsjo * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/httpauth.h b/libavformat/httpauth.h index 99bf43ffdc..fc17c94859 100644 --- a/libavformat/httpauth.h +++ b/libavformat/httpauth.h @@ -2,20 +2,20 @@ * HTTP authentication * Copyright (c) 2010 Martin Storsjo * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/icodec.c b/libavformat/icodec.c new file mode 100644 index 0000000000..fa308da722 --- /dev/null +++ b/libavformat/icodec.c @@ -0,0 +1,182 @@ +/* + * Microsoft Windows ICO demuxer + * Copyright (c) 2011 Peter Ross (pross@xvid.org) + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * Microsoft Windows ICO demuxer + */ + +#include "libavutil/intreadwrite.h" +#include "libavcodec/bytestream.h" +#include "libavcodec/bmp.h" +#include "avformat.h" +#include "internal.h" + +typedef struct { + int offset; + int size; + int nb_pal; +} IcoImage; + +typedef struct { + int current_image; + int nb_images; + IcoImage * images; +} IcoDemuxContext; + +static int probe(AVProbeData *p) +{ + if (AV_RL16(p->buf) == 0 && AV_RL16(p->buf + 2) == 1 && AV_RL16(p->buf + 4)) + return AVPROBE_SCORE_MAX / 3; + return 0; +} + +static int read_header(AVFormatContext *s) +{ + IcoDemuxContext *ico = s->priv_data; + AVIOContext *pb = s->pb; + int i, codec; + + avio_skip(pb, 4); + ico->nb_images = avio_rl16(pb); + + ico->images = av_malloc(ico->nb_images * sizeof(IcoImage)); + if (!ico->images) + return AVERROR(ENOMEM); + + for (i = 0; i < ico->nb_images; i++) { + AVStream *st; + int tmp; + + if (avio_seek(pb, 6 + i * 16, SEEK_SET) < 0) + break; + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->codec->width = avio_r8(pb); + st->codec->height = avio_r8(pb); + ico->images[i].nb_pal = avio_r8(pb); + if (ico->images[i].nb_pal == 255) + ico->images[i].nb_pal = 0; + + avio_skip(pb, 5); + + ico->images[i].size = avio_rl32(pb); + ico->images[i].offset = avio_rl32(pb); + + if (avio_seek(pb, ico->images[i].offset, SEEK_SET) < 0) + break; + + codec = avio_rl32(pb); + switch (codec) { + case MKTAG(0x89, 'P', 'N', 'G'): + st->codec->codec_id = AV_CODEC_ID_PNG; + st->codec->width = 0; + st->codec->height = 0; + break; + case 40: + if (ico->images[i].size < 40) + return AVERROR_INVALIDDATA; + st->codec->codec_id = AV_CODEC_ID_BMP; + tmp = avio_rl32(pb); + if (tmp) + st->codec->width = tmp; + tmp = avio_rl32(pb); + if (tmp) + st->codec->height = tmp / 2; + break; + default: + avpriv_request_sample(s, "codec %d", codec); + return AVERROR_INVALIDDATA; + } + } + + return 0; +} + +static int read_packet(AVFormatContext *s, AVPacket *pkt) +{ + IcoDemuxContext *ico = s->priv_data; + IcoImage *image; + AVIOContext *pb = s->pb; + AVStream *st = s->streams[0]; + int ret; + + if (ico->current_image >= ico->nb_images) + return AVERROR(EIO); + + image = &ico->images[ico->current_image]; + + if ((ret = avio_seek(pb, image->offset, SEEK_SET)) < 0) + return ret; + + if (s->streams[ico->current_image]->codec->codec_id == AV_CODEC_ID_PNG) { + if ((ret = av_get_packet(pb, pkt, image->size)) < 0) + return ret; + } else { + uint8_t *buf; + if ((ret = av_new_packet(pkt, 14 + image->size)) < 0) + return ret; + buf = pkt->data; + + /* add BMP header */ + bytestream_put_byte(&buf, 'B'); + bytestream_put_byte(&buf, 'M'); + bytestream_put_le32(&buf, pkt->size); + bytestream_put_le16(&buf, 0); + bytestream_put_le16(&buf, 0); + bytestream_put_le32(&buf, 0); + + if ((ret = avio_read(pb, buf, image->size)) < 0) + return ret; + + st->codec->bits_per_coded_sample = AV_RL16(buf + 14); + + if (AV_RL32(buf + 32)) + image->nb_pal = AV_RL32(buf + 32); + + if (st->codec->bits_per_coded_sample <= 8 && !image->nb_pal) { + image->nb_pal = 1 << st->codec->bits_per_coded_sample; + AV_WL32(buf + 32, image->nb_pal); + } + + AV_WL32(buf - 4, 14 + 40 + image->nb_pal * 4); + AV_WL32(buf + 8, AV_RL32(buf + 8) / 2); + } + + pkt->stream_index = ico->current_image++; + pkt->flags |= AV_PKT_FLAG_KEY; + + return 0; +} + +AVInputFormat ff_ico_demuxer = { + .name = "ico", + .long_name = NULL_IF_CONFIG_SMALL("Microsoft Windows ICO"), + .priv_data_size = sizeof(IcoDemuxContext), + .read_probe = probe, + .read_header = read_header, + .read_packet = read_packet, + .flags = AVFMT_NOTIMESTAMPS, +}; diff --git a/libavformat/icoenc.c b/libavformat/icoenc.c new file mode 100644 index 0000000000..561c6cadcc --- /dev/null +++ b/libavformat/icoenc.c @@ -0,0 +1,202 @@ +/* + * Microsoft Windows ICO muxer + * Copyright (c) 2012 Michael Bradshaw <mjbshaw gmail com> + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * Microsoft Windows ICO muxer + */ + +#include "libavutil/intreadwrite.h" +#include "libavutil/pixdesc.h" +#include "avformat.h" + +typedef struct { + int offset; + int size; + unsigned char width; + unsigned char height; + short bits; +} IcoImage; + +typedef struct { + int current_image; + int nb_images; + IcoImage *images; +} IcoMuxContext; + +static int ico_check_attributes(AVFormatContext *s, const AVCodecContext *c) +{ + if (c->codec_id == AV_CODEC_ID_BMP) { + if (c->pix_fmt == AV_PIX_FMT_PAL8 && AV_PIX_FMT_RGB32 != AV_PIX_FMT_BGRA) { + av_log(s, AV_LOG_ERROR, "Wrong endianness for bmp pixel format\n"); + return AVERROR(EINVAL); + } else if (c->pix_fmt != AV_PIX_FMT_PAL8 && + c->pix_fmt != AV_PIX_FMT_RGB555LE && + c->pix_fmt != AV_PIX_FMT_BGR24 && + c->pix_fmt != AV_PIX_FMT_BGRA) { + av_log(s, AV_LOG_ERROR, "BMP must be 1bit, 4bit, 8bit, 16bit, 24bit, or 32bit\n"); + return AVERROR(EINVAL); + } + } else if (c->codec_id == AV_CODEC_ID_PNG) { + if (c->pix_fmt != AV_PIX_FMT_RGBA) { + av_log(s, AV_LOG_ERROR, "PNG in ico requires pixel format to be rgba\n"); + return AVERROR(EINVAL); + } + } else { + av_log(s, AV_LOG_ERROR, "Unsupported codec %s\n", c->codec_name); + return AVERROR(EINVAL); + } + + if (c->width > 256 || + c->height > 256) { + av_log(s, AV_LOG_ERROR, "Unsupported dimensions %dx%d (dimensions cannot exceed 256x256)\n", c->width, c->height); + return AVERROR(EINVAL); + } + + return 0; +} + +static int ico_write_header(AVFormatContext *s) +{ + IcoMuxContext *ico = s->priv_data; + AVIOContext *pb = s->pb; + int ret; + int i; + + if (!pb->seekable) { + av_log(s, AV_LOG_ERROR, "Output is not seekable\n"); + return AVERROR(EINVAL); + } + + ico->current_image = 0; + ico->nb_images = s->nb_streams; + + avio_wl16(pb, 0); // reserved + avio_wl16(pb, 1); // 1 == icon + avio_skip(pb, 2); // skip the number of images + + for (i = 0; i < s->nb_streams; i++) { + if (ret = ico_check_attributes(s, s->streams[i]->codec)) + return ret; + + // Fill in later when writing trailer... + avio_skip(pb, 16); + } + + ico->images = av_mallocz(ico->nb_images * sizeof(IcoMuxContext)); + if (!ico->images) + return AVERROR(ENOMEM); + + avio_flush(pb); + + return 0; +} + +static int ico_write_packet(AVFormatContext *s, AVPacket *pkt) +{ + IcoMuxContext *ico = s->priv_data; + IcoImage *image; + AVIOContext *pb = s->pb; + AVCodecContext *c = s->streams[pkt->stream_index]->codec; + int i; + + if (ico->current_image >= ico->nb_images) { + av_log(s, AV_LOG_ERROR, "ICO already contains %d images\n", ico->current_image); + return AVERROR(EIO); + } + + image = &ico->images[ico->current_image++]; + + image->offset = avio_tell(pb); + image->width = (c->width == 256) ? 0 : c->width; + image->height = (c->height == 256) ? 0 : c->height; + + if (c->codec_id == AV_CODEC_ID_PNG) { + image->bits = c->bits_per_coded_sample; + image->size = pkt->size; + + avio_write(pb, pkt->data, pkt->size); + } else { // BMP + if (AV_RL32(pkt->data + 14) != 40) { // must be BITMAPINFOHEADER + av_log(s, AV_LOG_ERROR, "Invalid BMP\n"); + return AVERROR(EINVAL); + } + + image->bits = AV_RL16(pkt->data + 28); // allows things like 1bit and 4bit images to be preserved + image->size = pkt->size - 14 + c->height * (c->width + 7) / 8; + + avio_write(pb, pkt->data + 14, 8); // Skip the BITMAPFILEHEADER header + avio_wl32(pb, AV_RL32(pkt->data + 22) * 2); // rewrite height as 2 * height + avio_write(pb, pkt->data + 26, pkt->size - 26); + + for (i = 0; i < c->height * (c->width + 7) / 8; ++i) + avio_w8(pb, 0x00); // Write bitmask (opaque) + } + + return 0; +} + +static int ico_write_trailer(AVFormatContext *s) +{ + IcoMuxContext *ico = s->priv_data; + AVIOContext *pb = s->pb; + int i; + + avio_seek(pb, 4, SEEK_SET); + + avio_wl16(pb, ico->current_image); + + for (i = 0; i < ico->nb_images; i++) { + avio_w8(pb, ico->images[i].width); + avio_w8(pb, ico->images[i].height); + + if (s->streams[i]->codec->codec_id == AV_CODEC_ID_BMP && + s->streams[i]->codec->pix_fmt == AV_PIX_FMT_PAL8) { + avio_w8(pb, (ico->images[i].bits >= 8) ? 0 : 1 << ico->images[i].bits); + } else { + avio_w8(pb, 0); + } + + avio_w8(pb, 0); // reserved + avio_wl16(pb, 1); // color planes + avio_wl16(pb, ico->images[i].bits); + avio_wl32(pb, ico->images[i].size); + avio_wl32(pb, ico->images[i].offset); + } + + av_freep(&ico->images); + + return 0; +} + +AVOutputFormat ff_ico_muxer = { + .name = "ico", + .long_name = NULL_IF_CONFIG_SMALL("Microsoft Windows ICO"), + .priv_data_size = sizeof(IcoMuxContext), + .mime_type = "image/vnd.microsoft.icon", + .extensions = "ico", + .audio_codec = AV_CODEC_ID_NONE, + .video_codec = AV_CODEC_ID_BMP, + .write_header = ico_write_header, + .write_packet = ico_write_packet, + .write_trailer = ico_write_trailer, + .flags = AVFMT_NOTIMESTAMPS, +}; diff --git a/libavformat/id3v1.c b/libavformat/id3v1.c index 87930ff361..d73adc713b 100644 --- a/libavformat/id3v1.c +++ b/libavformat/id3v1.c @@ -2,20 +2,20 @@ * ID3v1 header parser * Copyright (c) 2003 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -23,6 +23,7 @@ #include "libavcodec/avcodec.h" #include "libavutil/dict.h" +/* See Genre List at http://id3.org/id3v2.3.0 */ const char * const ff_id3v1_genre_str[ID3v1_GENRE_MAX + 1] = { [0] = "Blues", [1] = "Classic Rock", @@ -91,7 +92,7 @@ const char * const ff_id3v1_genre_str[ID3v1_GENRE_MAX + 1] = { [64] = "Native American", [65] = "Cabaret", [66] = "New Wave", - [67] = "Psychadelic", + [67] = "Psychadelic", /* sic, the misspelling is used in the specification */ [68] = "Rave", [69] = "Showtunes", [70] = "Trailer", diff --git a/libavformat/id3v1.h b/libavformat/id3v1.h index 71070736f1..d5dca35873 100644 --- a/libavformat/id3v1.h +++ b/libavformat/id3v1.h @@ -2,20 +2,20 @@ * ID3v1 header parser * Copyright (c) 2003 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/id3v2.c b/libavformat/id3v2.c index e5f7486e1d..4bc76a321c 100644 --- a/libavformat/id3v2.c +++ b/libavformat/id3v2.c @@ -1,24 +1,37 @@ /* - * ID3v2 header parser * Copyright (c) 2003 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +/** + * @file + * ID3v2 header parser + * + * Specifications available at: + * http://id3.org/Developer_Information + */ + +#include "config.h" + +#if CONFIG_ZLIB +#include <zlib.h> +#endif + #include "libavutil/avstring.h" #include "libavutil/dict.h" #include "libavutil/intreadwrite.h" @@ -258,7 +271,7 @@ static int decode_str(AVFormatContext *s, AVIOContext *pb, int encoding, * Parse a text tag. */ static void read_ttag(AVFormatContext *s, AVIOContext *pb, int taglen, - const char *key) + AVDictionary **metadata, const char *key) { uint8_t *dst; int encoding, dict_flags = AV_DICT_DONT_OVERWRITE | AV_DICT_DONT_STRDUP_VAL; @@ -293,7 +306,7 @@ static void read_ttag(AVFormatContext *s, AVIOContext *pb, int taglen, av_freep(&dst); if (dst) - av_dict_set(&s->metadata, key, dst, dict_flags); + av_dict_set(metadata, key, dst, dict_flags); } /** @@ -487,7 +500,7 @@ static void read_apic(AVFormatContext *s, AVIOContext *pb, int taglen, } apic->buf = av_buffer_alloc(taglen + FF_INPUT_BUFFER_PADDING_SIZE); - if (!apic->buf || avio_read(pb, apic->buf->data, taglen) != taglen) + if (!apic->buf || !taglen || avio_read(pb, apic->buf->data, taglen) != taglen) goto fail; memset(apic->buf->data + taglen, 0, FF_INPUT_BUFFER_PADDING_SIZE); @@ -505,6 +518,53 @@ fail: avio_seek(pb, end, SEEK_SET); } +static void read_chapter(AVFormatContext *s, AVIOContext *pb, int len, char *ttag, ID3v2ExtraMeta **extra_meta) +{ + AVRational time_base = {1, 1000}; + uint32_t start, end; + AVChapter *chapter; + uint8_t *dst = NULL; + int taglen; + char tag[5]; + + if (decode_str(s, pb, 0, &dst, &len) < 0) + return; + if (len < 16) + return; + + start = avio_rb32(pb); + end = avio_rb32(pb); + avio_skip(pb, 8); + + chapter = avpriv_new_chapter(s, s->nb_chapters + 1, time_base, start, end, dst); + if (!chapter) { + av_free(dst); + return; + } + + len -= 16; + while (len > 10) { + avio_read(pb, tag, 4); + tag[4] = 0; + taglen = avio_rb32(pb); + avio_skip(pb, 2); + len -= 10; + if (taglen < 0 || taglen > len) { + av_free(dst); + return; + } + if (tag[0] == 'T') + read_ttag(s, pb, taglen, &chapter->metadata, tag); + else + avio_skip(pb, taglen); + len -= taglen; + } + + ff_metadata_conv(&chapter->metadata, NULL, ff_id3v2_34_metadata_conv); + ff_metadata_conv(&chapter->metadata, NULL, ff_id3v2_4_metadata_conv); + av_free(dst); +} + typedef struct ID3v2EMFunc { const char *tag3; const char *tag4; @@ -516,6 +576,7 @@ typedef struct ID3v2EMFunc { static const ID3v2EMFunc id3v2_extra_meta_funcs[] = { { "GEO", "GEOB", read_geobtag, free_geobtag }, { "PIC", "APIC", read_apic, free_apic }, + { "CHAP","CHAP", read_chapter, NULL }, { NULL } }; @@ -528,7 +589,7 @@ static const ID3v2EMFunc *get_extra_meta_func(const char *tag, int isv34) { int i = 0; while (id3v2_extra_meta_funcs[i].tag3) { - if (!memcmp(tag, + if (tag && !memcmp(tag, (isv34 ? id3v2_extra_meta_funcs[i].tag4 : id3v2_extra_meta_funcs[i].tag3), (isv34 ? 4 : 3))) @@ -541,7 +602,8 @@ static const ID3v2EMFunc *get_extra_meta_func(const char *tag, int isv34) static void id3v2_parse(AVFormatContext *s, int len, uint8_t version, uint8_t flags, ID3v2ExtraMeta **extra_meta) { - int isv34, tlen, unsync; + int isv34, unsync; + unsigned tlen; char tag[5]; int64_t next, end = avio_tell(s->pb) + len; int taghdrlen; @@ -550,7 +612,11 @@ static void id3v2_parse(AVFormatContext *s, int len, uint8_t version, AVIOContext *pbx; unsigned char *buffer = NULL; int buffer_size = 0; - const ID3v2EMFunc *extra_func; + const ID3v2EMFunc *extra_func = NULL; + unsigned char *uncompressed_buffer = NULL; + int uncompressed_buffer_size = 0; + + av_log(s, AV_LOG_DEBUG, "id3v2 ver:%d flags:%02X len:%d\n", version, flags, len); switch (version) { case 2: @@ -586,11 +652,19 @@ static void id3v2_parse(AVFormatContext *s, int len, uint8_t version, goto error; } avio_skip(s->pb, extlen); + len -= extlen + 4; + if (len < 0) { + reason = "extended header too long."; + goto error; + } } while (len >= taghdrlen) { unsigned int tflags = 0; int tunsync = 0; + int tcomp = 0; + int tencr = 0; + unsigned long dlen; if (isv34) { avio_read(s->pb, tag, 4); @@ -606,13 +680,13 @@ static void id3v2_parse(AVFormatContext *s, int len, uint8_t version, tag[3] = 0; tlen = avio_rb24(s->pb); } - if (tlen < 0 || tlen > len - taghdrlen) { - av_log(s, AV_LOG_WARNING, - "Invalid size in frame %s, skipping the rest of tag.\n", - tag); + if (tlen > (1<<28)) break; - } len -= taghdrlen + tlen; + + if (len < 0) + break; + next = avio_tell(s->pb) + tlen; if (!tlen) { @@ -623,30 +697,50 @@ static void id3v2_parse(AVFormatContext *s, int len, uint8_t version, } if (tflags & ID3v2_FLAG_DATALEN) { - avio_rb32(s->pb); + if (tlen < 4) + break; + dlen = avio_rb32(s->pb); tlen -= 4; - } + } else + dlen = tlen; + + tcomp = tflags & ID3v2_FLAG_COMPRESSION; + tencr = tflags & ID3v2_FLAG_ENCRYPTION; + + /* skip encrypted tags and, if no zlib, compressed tags */ + if (tencr || (!CONFIG_ZLIB && tcomp)) { + const char *type; + if (!tcomp) + type = "encrypted"; + else if (!tencr) + type = "compressed"; + else + type = "encrypted and compressed"; - if (tflags & (ID3v2_FLAG_ENCRYPTION | ID3v2_FLAG_COMPRESSION)) { - av_log(s, AV_LOG_WARNING, - "Skipping encrypted/compressed ID3v2 frame %s.\n", tag); + av_log(s, AV_LOG_WARNING, "Skipping %s ID3v2 frame %s.\n", type, tag); avio_skip(s->pb, tlen); /* check for text tag or supported special meta tag */ } else if (tag[0] == 'T' || (extra_meta && (extra_func = get_extra_meta_func(tag, isv34)))) { - if (unsync || tunsync) { - int64_t end = avio_tell(s->pb) + tlen; - uint8_t *b; + pbx = s->pb; + + if (unsync || tunsync || tcomp) { av_fast_malloc(&buffer, &buffer_size, tlen); if (!buffer) { av_log(s, AV_LOG_ERROR, "Failed to alloc %d bytes\n", tlen); goto seek; } + } + if (unsync || tunsync) { + int64_t end = avio_tell(s->pb) + tlen; + uint8_t *b; + b = buffer; - while (avio_tell(s->pb) < end && !s->pb->eof_reached) { + while (avio_tell(s->pb) < end && b - buffer < tlen && !s->pb->eof_reached) { *b++ = avio_r8(s->pb); if (*(b - 1) == 0xff && avio_tell(s->pb) < end - 1 && + b - buffer < tlen && !s->pb->eof_reached ) { uint8_t val = avio_r8(s->pb); *b++ = val ? val : avio_r8(s->pb); @@ -656,18 +750,48 @@ static void id3v2_parse(AVFormatContext *s, int len, uint8_t version, NULL); tlen = b - buffer; pbx = &pb; // read from sync buffer - } else { - pbx = s->pb; // read straight from input } + +#if CONFIG_ZLIB + if (tcomp) { + int err; + + av_log(s, AV_LOG_DEBUG, "Compresssed frame %s tlen=%d dlen=%ld\n", tag, tlen, dlen); + + av_fast_malloc(&uncompressed_buffer, &uncompressed_buffer_size, dlen); + if (!uncompressed_buffer) { + av_log(s, AV_LOG_ERROR, "Failed to alloc %ld bytes\n", dlen); + goto seek; + } + + if (!(unsync || tunsync)) { + err = avio_read(s->pb, buffer, tlen); + if (err < 0) { + av_log(s, AV_LOG_ERROR, "Failed to read compressed tag\n"); + goto seek; + } + tlen = err; + } + + err = uncompress(uncompressed_buffer, &dlen, buffer, tlen); + if (err != Z_OK) { + av_log(s, AV_LOG_ERROR, "Failed to uncompress tag: %d\n", err); + goto seek; + } + ffio_init_context(&pb, uncompressed_buffer, dlen, 0, NULL, NULL, NULL, NULL); + tlen = dlen; + pbx = &pb; // read from sync buffer + } +#endif if (tag[0] == 'T') /* parse text tag */ - read_ttag(s, pbx, tlen, tag); + read_ttag(s, pbx, tlen, &s->metadata, tag); else /* parse special meta tag */ extra_func->read(s, pbx, tlen, tag, extra_meta); } else if (!tag[0]) { if (tag[1]) - av_log(s, AV_LOG_WARNING, "invalid frame id, assuming padding"); + av_log(s, AV_LOG_WARNING, "invalid frame id, assuming padding\n"); avio_skip(s->pb, tlen); break; } @@ -686,6 +810,7 @@ error: version, reason); avio_seek(s->pb, end, SEEK_SET); av_free(buffer); + av_free(uncompressed_buffer); return; } @@ -701,8 +826,10 @@ void ff_id3v2_read(AVFormatContext *s, const char *magic, /* save the current offset in case there's nothing to read/skip */ off = avio_tell(s->pb); ret = avio_read(s->pb, buf, ID3v2_HEADER_SIZE); - if (ret != ID3v2_HEADER_SIZE) + if (ret != ID3v2_HEADER_SIZE) { + avio_seek(s->pb, off, SEEK_SET); break; + } found_header = ff_id3v2_match(buf, magic); if (found_header) { /* parse ID3v2 header */ diff --git a/libavformat/id3v2.h b/libavformat/id3v2.h index 7cb4296f11..e893922133 100644 --- a/libavformat/id3v2.h +++ b/libavformat/id3v2.h @@ -2,20 +2,20 @@ * ID3v2 header parser * Copyright (c) 2003 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/id3v2enc.c b/libavformat/id3v2enc.c index df00f05b24..60522444fc 100644 --- a/libavformat/id3v2enc.c +++ b/libavformat/id3v2enc.c @@ -1,32 +1,36 @@ /* * ID3v2 header writer * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include <stdint.h> #include <string.h> +#include "libavutil/avstring.h" #include "libavutil/dict.h" #include "libavutil/intreadwrite.h" #include "avformat.h" #include "avio.h" +#include "avio_internal.h" #include "id3v2.h" +#define PADDING_BYTES 10 + static void id3v2_put_size(AVIOContext *pb, int size) { avio_w8(pb, size >> 21 & 0x7f); @@ -109,6 +113,44 @@ static int id3v2_check_write_tag(ID3v2EncContext *id3, AVIOContext *pb, AVDictio return -1; } +static void id3v2_3_metadata_split_date(AVDictionary **pm) +{ + AVDictionaryEntry *mtag = NULL; + AVDictionary *dst = NULL; + const char *key, *value; + char year[5] = {0}, day_month[5] = {0}; + int i; + + while ((mtag = av_dict_get(*pm, "", mtag, AV_DICT_IGNORE_SUFFIX))) { + key = mtag->key; + if (!av_strcasecmp(key, "date")) { + /* split date tag using "YYYY-MM-DD" format into year and month/day segments */ + value = mtag->value; + i = 0; + while (value[i] >= '0' && value[i] <= '9') i++; + if (value[i] == '\0' || value[i] == '-') { + av_strlcpy(year, value, sizeof(year)); + av_dict_set(&dst, "TYER", year, 0); + + if (value[i] == '-' && + value[i+1] >= '0' && value[i+1] <= '1' && + value[i+2] >= '0' && value[i+2] <= '9' && + value[i+3] == '-' && + value[i+4] >= '0' && value[i+4] <= '3' && + value[i+5] >= '0' && value[i+5] <= '9' && + (value[i+6] == '\0' || value[i+6] == ' ')) { + snprintf(day_month, sizeof(day_month), "%.2s%.2s", value + i + 4, value + i + 1); + av_dict_set(&dst, "TDAT", day_month, 0); + } + } else + av_dict_set(&dst, key, value, 0); + } else + av_dict_set(&dst, key, mtag->value, 0); + } + av_dict_free(pm); + *pm = dst; +} + void ff_id3v2_start(ID3v2EncContext *id3, AVIOContext *pb, int id3v2_version, const char *magic) { @@ -123,31 +165,31 @@ void ff_id3v2_start(ID3v2EncContext *id3, AVIOContext *pb, int id3v2_version, avio_wb32(pb, 0); } -int ff_id3v2_write_metadata(AVFormatContext *s, ID3v2EncContext *id3) +static int write_metadata(AVIOContext *pb, AVDictionary **metadata, + ID3v2EncContext *id3, int enc) { AVDictionaryEntry *t = NULL; - int enc = id3->version == 3 ? ID3v2_ENCODING_UTF16BOM : - ID3v2_ENCODING_UTF8; - - ff_metadata_conv(&s->metadata, ff_id3v2_34_metadata_conv, NULL); - if (id3->version == 4) - ff_metadata_conv(&s->metadata, ff_id3v2_4_metadata_conv, NULL); + int ret; - while ((t = av_dict_get(s->metadata, "", t, AV_DICT_IGNORE_SUFFIX))) { - int ret; + ff_metadata_conv(metadata, ff_id3v2_34_metadata_conv, NULL); + if (id3->version == 3) + id3v2_3_metadata_split_date(metadata); + else if (id3->version == 4) + ff_metadata_conv(metadata, ff_id3v2_4_metadata_conv, NULL); - if ((ret = id3v2_check_write_tag(id3, s->pb, t, ff_id3v2_tags, enc)) > 0) { + while ((t = av_dict_get(*metadata, "", t, AV_DICT_IGNORE_SUFFIX))) { + if ((ret = id3v2_check_write_tag(id3, pb, t, ff_id3v2_tags, enc)) > 0) { id3->len += ret; continue; } - if ((ret = id3v2_check_write_tag(id3, s->pb, t, id3->version == 3 ? - ff_id3v2_3_tags : ff_id3v2_4_tags, enc)) > 0) { + if ((ret = id3v2_check_write_tag(id3, pb, t, id3->version == 3 ? + ff_id3v2_3_tags : ff_id3v2_4_tags, enc)) > 0) { id3->len += ret; continue; } /* unknown tag, write as TXXX frame */ - if ((ret = id3v2_put_ttag(id3, s->pb, t->key, t->value, MKBETAG('T', 'X', 'X', 'X'), enc)) < 0) + if ((ret = id3v2_put_ttag(id3, pb, t->key, t->value, MKBETAG('T', 'X', 'X', 'X'), enc)) < 0) return ret; id3->len += ret; } @@ -155,6 +197,64 @@ int ff_id3v2_write_metadata(AVFormatContext *s, ID3v2EncContext *id3) return 0; } +static int write_chapter(AVFormatContext *s, ID3v2EncContext *id3, int id, int enc) +{ + const AVRational time_base = {1, 1000}; + AVChapter *ch = s->chapters[id]; + uint8_t *dyn_buf = NULL; + AVIOContext *dyn_bc = NULL; + char name[123]; + int len, start, end, ret; + + if ((ret = avio_open_dyn_buf(&dyn_bc)) < 0) + goto fail; + + start = av_rescale_q(ch->start, ch->time_base, time_base); + end = av_rescale_q(ch->end, ch->time_base, time_base); + + snprintf(name, 122, "ch%d", id); + id3->len += avio_put_str(dyn_bc, name); + avio_wb32(dyn_bc, start); + avio_wb32(dyn_bc, end); + avio_wb32(dyn_bc, 0xFFFFFFFFu); + avio_wb32(dyn_bc, 0xFFFFFFFFu); + + if ((ret = write_metadata(dyn_bc, &ch->metadata, id3, enc)) < 0) + goto fail; + + len = avio_close_dyn_buf(dyn_bc, &dyn_buf); + id3->len += 16 + ID3v2_HEADER_SIZE; + + avio_wb32(s->pb, MKBETAG('C', 'H', 'A', 'P')); + avio_wb32(s->pb, len); + avio_wb16(s->pb, 0); + avio_write(s->pb, dyn_buf, len); + +fail: + if (dyn_bc && !dyn_buf) + avio_close_dyn_buf(dyn_bc, &dyn_buf); + av_freep(&dyn_buf); + + return ret; +} + +int ff_id3v2_write_metadata(AVFormatContext *s, ID3v2EncContext *id3) +{ + int enc = id3->version == 3 ? ID3v2_ENCODING_UTF16BOM : + ID3v2_ENCODING_UTF8; + int i, ret; + + if ((ret = write_metadata(s->pb, &s->metadata, id3, enc)) < 0) + return ret; + + for (i = 0; i < s->nb_chapters; i++) { + if ((ret = write_chapter(s, id3, i, enc)) < 0) + return ret; + } + + return 0; +} + int ff_id3v2_write_apic(AVFormatContext *s, ID3v2EncContext *id3, AVPacket *pkt) { AVStream *st = s->streams[pkt->stream_index]; @@ -195,6 +295,10 @@ int ff_id3v2_write_apic(AVFormatContext *s, ID3v2EncContext *id3, AVPacket *pkt) if ((e = av_dict_get(st->metadata, "title", NULL, 0))) desc = e->value; + /* use UTF16 only for non-ASCII strings */ + if (enc == ID3v2_ENCODING_UTF16BOM && string_is_ascii(desc)) + enc = ID3v2_ENCODING_ISO8859; + /* start writing */ if (avio_open_dyn_buf(&dyn_buf) < 0) return AVERROR(ENOMEM); @@ -222,7 +326,15 @@ int ff_id3v2_write_apic(AVFormatContext *s, ID3v2EncContext *id3, AVPacket *pkt) void ff_id3v2_finish(ID3v2EncContext *id3, AVIOContext *pb) { - int64_t cur_pos = avio_tell(pb); + int64_t cur_pos; + + /* adding an arbitrary amount of padding bytes at the end of the + * ID3 metadata fixes cover art display for some software (iTunes, + * Traktor, Serato, Torq) */ + ffio_fill(pb, 0, PADDING_BYTES); + id3->len += PADDING_BYTES; + + cur_pos = avio_tell(pb); avio_seek(pb, id3->size_pos, SEEK_SET); id3v2_put_size(pb, id3->len); avio_seek(pb, cur_pos, SEEK_SET); diff --git a/libavformat/idcin.c b/libavformat/idcin.c index 2536e8bd62..9671fca6f8 100644 --- a/libavformat/idcin.c +++ b/libavformat/idcin.c @@ -2,20 +2,20 @@ * id Quake II CIN File Demuxer * Copyright (c) 2003 The ffmpeg Project * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -93,7 +93,7 @@ typedef struct IdcinDemuxContext { static int idcin_probe(AVProbeData *p) { - unsigned int number; + unsigned int number, sample_rate; /* * This is what you could call a "probabilistic" file check: id CIN @@ -122,18 +122,18 @@ static int idcin_probe(AVProbeData *p) return 0; /* check the audio sample rate */ - number = AV_RL32(&p->buf[8]); - if ((number != 0) && ((number < 8000) | (number > 48000))) + sample_rate = AV_RL32(&p->buf[8]); + if (sample_rate && (sample_rate < 8000 || sample_rate > 48000)) return 0; /* check the audio bytes/sample */ number = AV_RL32(&p->buf[12]); - if (number > 2) + if (number > 2 || sample_rate && !number) return 0; /* check the audio channels */ number = AV_RL32(&p->buf[16]); - if (number > 2) + if (number > 2 || sample_rate && !number) return 0; /* return half certainty since this check is a bit sketchy */ @@ -196,8 +196,8 @@ static int idcin_read_header(AVFormatContext *s) st->codec->height = height; /* load up the Huffman tables into extradata */ - st->codec->extradata_size = HUFFMAN_TABLE_SIZE; - st->codec->extradata = av_malloc(HUFFMAN_TABLE_SIZE); + if (ff_alloc_extradata(st->codec, HUFFMAN_TABLE_SIZE)) + return AVERROR(ENOMEM); ret = avio_read(pb, st->codec->extradata, HUFFMAN_TABLE_SIZE); if (ret < 0) { return ret; @@ -260,7 +260,7 @@ static int idcin_read_packet(AVFormatContext *s, unsigned char palette_buffer[768]; uint32_t palette[256]; - if (s->pb->eof_reached) + if (url_feof(s->pb)) return s->pb->error ? s->pb->error : AVERROR_EOF; if (idcin->next_chunk_is_video) { @@ -288,7 +288,9 @@ static int idcin_read_packet(AVFormatContext *s, r = palette_buffer[i * 3 ] << palette_scale; g = palette_buffer[i * 3 + 1] << palette_scale; b = palette_buffer[i * 3 + 2] << palette_scale; - palette[i] = (r << 16) | (g << 8) | (b); + palette[i] = (0xFFU << 24) | (r << 16) | (g << 8) | (b); + if (palette_scale == 2) + palette[i] |= palette[i] >> 6 & 0x30303; } } @@ -303,6 +305,8 @@ static int idcin_read_packet(AVFormatContext *s, } /* skip the number of decoded bytes (always equal to width * height) */ avio_skip(pb, 4); + if (chunk_size < 4) + return AVERROR_INVALIDDATA; chunk_size -= 4; ret= av_get_packet(pb, pkt, chunk_size); if (ret < 0) @@ -317,9 +321,9 @@ static int idcin_read_packet(AVFormatContext *s, pal = av_packet_new_side_data(pkt, AV_PKT_DATA_PALETTE, AVPALETTE_SIZE); - if (ret < 0) { + if (!pal) { av_free_packet(pkt); - return ret; + return AVERROR(ENOMEM); } memcpy(pal, palette, AVPALETTE_SIZE); pkt->flags |= AV_PKT_FLAG_KEY; diff --git a/libavformat/idroqdec.c b/libavformat/idroqdec.c index 82eff24e94..8cdd72e33a 100644 --- a/libavformat/idroqdec.c +++ b/libavformat/idroqdec.c @@ -2,20 +2,20 @@ * id RoQ (.roq) File Demuxer * Copyright (c) 2003 The ffmpeg Project * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -31,6 +31,7 @@ #include "libavutil/intreadwrite.h" #include "avformat.h" #include "internal.h" +#include "avio_internal.h" #define RoQ_MAGIC_NUMBER 0x1084 #define RoQ_CHUNK_PREAMBLE_SIZE 8 @@ -105,7 +106,7 @@ static int roq_read_packet(AVFormatContext *s, while (!packet_read) { - if (s->pb->eof_reached) + if (url_feof(s->pb)) return AVERROR(EIO); /* get the next chunk preamble */ @@ -118,6 +119,8 @@ static int roq_read_packet(AVFormatContext *s, if(chunk_size > INT_MAX) return AVERROR_INVALIDDATA; + chunk_size = ffio_limit(pb, chunk_size); + switch (chunk_type) { case RoQ_INFO: diff --git a/libavformat/idroqenc.c b/libavformat/idroqenc.c index 2ce4d7d493..50c428046b 100644 --- a/libavformat/idroqenc.c +++ b/libavformat/idroqenc.c @@ -2,20 +2,20 @@ * id RoQ (.roq) File muxer * Copyright (c) 2007 Vitor Sessak * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/iff.c b/libavformat/iff.c index 79f5f16050..edf308b135 100644 --- a/libavformat/iff.c +++ b/libavformat/iff.c @@ -1,23 +1,22 @@ /* - * IFF (.iff) file demuxer * Copyright (c) 2008 Jaikrishnan Menon <realityman@gmx.net> * Copyright (c) 2010 Peter Ross <pross@xvid.org> * Copyright (c) 2010 Sebastian Vater <cdgs.basty@googlemail.com> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -29,13 +28,19 @@ * http://wiki.multimedia.cx/index.php?title=IFF */ +#include "libavutil/avassert.h" #include "libavutil/channel_layout.h" #include "libavutil/intreadwrite.h" #include "libavutil/dict.h" +#include "libavcodec/bytestream.h" #include "avformat.h" #include "internal.h" #define ID_8SVX MKTAG('8','S','V','X') +#define ID_16SV MKTAG('1','6','S','V') +#define ID_MAUD MKTAG('M','A','U','D') +#define ID_MHDR MKTAG('M','H','D','R') +#define ID_MDAT MKTAG('M','D','A','T') #define ID_VHDR MKTAG('V','H','D','R') #define ID_ATAK MKTAG('A','T','A','K') #define ID_RLSE MKTAG('R','L','S','E') @@ -43,7 +48,13 @@ #define ID_PBM MKTAG('P','B','M',' ') #define ID_ILBM MKTAG('I','L','B','M') #define ID_BMHD MKTAG('B','M','H','D') +#define ID_DGBL MKTAG('D','G','B','L') +#define ID_CAMG MKTAG('C','A','M','G') #define ID_CMAP MKTAG('C','M','A','P') +#define ID_ACBM MKTAG('A','C','B','M') +#define ID_DEEP MKTAG('D','E','E','P') +#define ID_RGB8 MKTAG('R','G','B','8') +#define ID_RGBN MKTAG('R','G','B','N') #define ID_FORM MKTAG('F','O','R','M') #define ID_ANNO MKTAG('A','N','N','O') @@ -54,31 +65,49 @@ #define ID_FVER MKTAG('F','V','E','R') #define ID_NAME MKTAG('N','A','M','E') #define ID_TEXT MKTAG('T','E','X','T') +#define ID_ABIT MKTAG('A','B','I','T') #define ID_BODY MKTAG('B','O','D','Y') -#define ID_ANNO MKTAG('A','N','N','O') +#define ID_DBOD MKTAG('D','B','O','D') +#define ID_DPEL MKTAG('D','P','E','L') +#define ID_DLOC MKTAG('D','L','O','C') +#define ID_TVDC MKTAG('T','V','D','C') #define LEFT 2 #define RIGHT 4 #define STEREO 6 +/** + * This number of bytes if added at the beginning of each AVPacket + * which contain additional information about video properties + * which has to be shared between demuxer and decoder. + * This number may change between frames, e.g. the demuxer might + * set it to smallest possible size of 2 to indicate that there's + * no extradata changing in this frame. + */ +#define IFF_EXTRA_VIDEO_SIZE 41 + typedef enum { COMP_NONE, COMP_FIB, COMP_EXP } svx8_compression_type; -typedef enum { - BITMAP_RAW, - BITMAP_BYTERUN1 -} bitmap_compression_type; - typedef struct { - uint64_t body_pos; + int64_t body_pos; + int64_t body_end; uint32_t body_size; - uint32_t sent_bytes; + svx8_compression_type svx8_compression; + unsigned maud_bits; + unsigned maud_compression; + unsigned bitmap_compression; ///< delta compression method used + unsigned bpp; ///< bits per plane to decode (differs from bits_per_coded_sample if HAM) + unsigned ham; ///< 0 if non-HAM or number of hold bits (6 for bpp > 6, 4 otherwise) + unsigned flags; ///< 1 for EHB, 0 is no extra half darkening + unsigned transparency; ///< transparency color index in palette + unsigned masking; ///< masking method used + uint8_t tvdc[32]; ///< TVDC lookup table } IffDemuxContext; - /* Metadata string read */ static int get_metadata(AVFormatContext *s, const char *const tag, @@ -102,19 +131,38 @@ static int iff_probe(AVProbeData *p) { const uint8_t *d = p->buf; - if ( AV_RL32(d) == ID_FORM && - (AV_RL32(d+8) == ID_8SVX || AV_RL32(d+8) == ID_PBM || AV_RL32(d+8) == ID_ILBM) ) + if ( AV_RL32(d) == ID_FORM && + (AV_RL32(d+8) == ID_8SVX || + AV_RL32(d+8) == ID_16SV || + AV_RL32(d+8) == ID_MAUD || + AV_RL32(d+8) == ID_PBM || + AV_RL32(d+8) == ID_ACBM || + AV_RL32(d+8) == ID_DEEP || + AV_RL32(d+8) == ID_ILBM || + AV_RL32(d+8) == ID_RGB8 || + AV_RL32(d+8) == ID_RGBN) ) return AVPROBE_SCORE_MAX; return 0; } +static const uint8_t deep_rgb24[] = {0, 0, 0, 3, 0, 1, 0, 8, 0, 2, 0, 8, 0, 3, 0, 8}; +static const uint8_t deep_rgba[] = {0, 0, 0, 4, 0, 1, 0, 8, 0, 2, 0, 8, 0, 3, 0, 8}; +static const uint8_t deep_bgra[] = {0, 0, 0, 4, 0, 3, 0, 8, 0, 2, 0, 8, 0, 1, 0, 8}; +static const uint8_t deep_argb[] = {0, 0, 0, 4, 0,17, 0, 8, 0, 1, 0, 8, 0, 2, 0, 8}; +static const uint8_t deep_abgr[] = {0, 0, 0, 4, 0,17, 0, 8, 0, 3, 0, 8, 0, 2, 0, 8}; + static int iff_read_header(AVFormatContext *s) { IffDemuxContext *iff = s->priv_data; AVIOContext *pb = s->pb; AVStream *st; + uint8_t *buf; uint32_t chunk_id, data_size; - int compression = -1; + uint32_t screenmode = 0, num, den; + unsigned transparency = 0; + unsigned masking = 0; // no mask + uint8_t fmt[16]; + int fmt_size; st = avformat_new_stream(s, NULL); if (!st) @@ -125,8 +173,12 @@ static int iff_read_header(AVFormatContext *s) avio_skip(pb, 8); // codec_tag used by ByteRun1 decoder to distinguish progressive (PBM) and interlaced (ILBM) content st->codec->codec_tag = avio_rl32(pb); + iff->bitmap_compression = -1; + iff->svx8_compression = -1; + iff->maud_bits = -1; + iff->maud_compression = -1; - while(!pb->eof_reached) { + while(!url_feof(pb)) { uint64_t orig_pos; int res; const char *metadata_tag = NULL; @@ -144,12 +196,38 @@ static int iff_read_header(AVFormatContext *s) st->codec->sample_rate = avio_rb16(pb); if (data_size >= 16) { avio_skip(pb, 1); - compression = avio_r8(pb); + iff->svx8_compression = avio_r8(pb); } break; + case ID_MHDR: + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + + if (data_size < 32) + return AVERROR_INVALIDDATA; + avio_skip(pb, 4); + iff->maud_bits = avio_rb16(pb); + avio_skip(pb, 2); + num = avio_rb32(pb); + den = avio_rb16(pb); + if (!den) + return AVERROR_INVALIDDATA; + avio_skip(pb, 2); + st->codec->sample_rate = num / den; + st->codec->channels = avio_rb16(pb); + iff->maud_compression = avio_rb16(pb); + if (st->codec->channels == 1) + st->codec->channel_layout = AV_CH_LAYOUT_MONO; + else if (st->codec->channels == 2) + st->codec->channel_layout = AV_CH_LAYOUT_STEREO; + break; + + case ID_ABIT: case ID_BODY: + case ID_DBOD: + case ID_MDAT: iff->body_pos = avio_tell(pb); + iff->body_end = iff->body_pos + data_size; iff->body_size = data_size; break; @@ -165,17 +243,23 @@ static int iff_read_header(AVFormatContext *s) } break; + case ID_CAMG: + if (data_size < 4) + return AVERROR_INVALIDDATA; + screenmode = avio_rb32(pb); + break; + case ID_CMAP: if (data_size < 3 || data_size > 768 || data_size % 3) { av_log(s, AV_LOG_ERROR, "Invalid CMAP chunk size %d\n", data_size); return AVERROR_INVALIDDATA; } - st->codec->extradata_size = data_size; - st->codec->extradata = av_malloc(data_size); + st->codec->extradata_size = data_size + IFF_EXTRA_VIDEO_SIZE; + st->codec->extradata = av_malloc(data_size + IFF_EXTRA_VIDEO_SIZE + FF_INPUT_BUFFER_PADDING_SIZE); if (!st->codec->extradata) return AVERROR(ENOMEM); - if (avio_read(pb, st->codec->extradata, data_size) < 0) + if (avio_read(pb, st->codec->extradata + IFF_EXTRA_VIDEO_SIZE, data_size) < 0) return AVERROR(EIO); break; @@ -187,38 +271,78 @@ static int iff_read_header(AVFormatContext *s) st->codec->height = avio_rb16(pb); avio_skip(pb, 4); // x, y offset st->codec->bits_per_coded_sample = avio_r8(pb); - if (data_size >= 11) { - avio_skip(pb, 1); // masking - compression = avio_r8(pb); + if (data_size >= 10) + masking = avio_r8(pb); + if (data_size >= 11) + iff->bitmap_compression = avio_r8(pb); + if (data_size >= 14) { + avio_skip(pb, 1); // padding + transparency = avio_rb16(pb); } if (data_size >= 16) { - avio_skip(pb, 3); // paddding, transparent st->sample_aspect_ratio.num = avio_r8(pb); st->sample_aspect_ratio.den = avio_r8(pb); } break; - case ID_ANNO: - case ID_TEXT: - metadata_tag = "comment"; + case ID_DPEL: + if (data_size < 4 || (data_size & 3)) + return AVERROR_INVALIDDATA; + if ((fmt_size = avio_read(pb, fmt, sizeof(fmt))) < 0) + return fmt_size; + if (fmt_size == sizeof(deep_rgb24) && !memcmp(fmt, deep_rgb24, sizeof(deep_rgb24))) + st->codec->pix_fmt = AV_PIX_FMT_RGB24; + else if (fmt_size == sizeof(deep_rgba) && !memcmp(fmt, deep_rgba, sizeof(deep_rgba))) + st->codec->pix_fmt = AV_PIX_FMT_RGBA; + else if (fmt_size == sizeof(deep_bgra) && !memcmp(fmt, deep_bgra, sizeof(deep_bgra))) + st->codec->pix_fmt = AV_PIX_FMT_BGRA; + else if (fmt_size == sizeof(deep_argb) && !memcmp(fmt, deep_argb, sizeof(deep_argb))) + st->codec->pix_fmt = AV_PIX_FMT_ARGB; + else if (fmt_size == sizeof(deep_abgr) && !memcmp(fmt, deep_abgr, sizeof(deep_abgr))) + st->codec->pix_fmt = AV_PIX_FMT_ABGR; + else { + avpriv_request_sample(s, "color format %.16s", fmt); + return AVERROR_PATCHWELCOME; + } break; - case ID_AUTH: - metadata_tag = "artist"; + case ID_DGBL: + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + if (data_size < 8) + return AVERROR_INVALIDDATA; + st->codec->width = avio_rb16(pb); + st->codec->height = avio_rb16(pb); + iff->bitmap_compression = avio_rb16(pb); + st->sample_aspect_ratio.num = avio_r8(pb); + st->sample_aspect_ratio.den = avio_r8(pb); + st->codec->bits_per_coded_sample = 24; break; - case ID_COPYRIGHT: - metadata_tag = "copyright"; + case ID_DLOC: + if (data_size < 4) + return AVERROR_INVALIDDATA; + st->codec->width = avio_rb16(pb); + st->codec->height = avio_rb16(pb); break; - case ID_NAME: - metadata_tag = "title"; + case ID_TVDC: + if (data_size < sizeof(iff->tvdc)) + return AVERROR_INVALIDDATA; + res = avio_read(pb, iff->tvdc, sizeof(iff->tvdc)); + if (res < 0) + return res; break; + + case ID_ANNO: + case ID_TEXT: metadata_tag = "comment"; break; + case ID_AUTH: metadata_tag = "artist"; break; + case ID_COPYRIGHT: metadata_tag = "copyright"; break; + case ID_NAME: metadata_tag = "title"; break; } if (metadata_tag) { if ((res = get_metadata(s, metadata_tag, data_size)) < 0) { - av_log(s, AV_LOG_ERROR, "cannot allocate metadata tag %s!", metadata_tag); + av_log(s, AV_LOG_ERROR, "cannot allocate metadata tag %s!\n", metadata_tag); return res; } } @@ -231,7 +355,29 @@ static int iff_read_header(AVFormatContext *s) case AVMEDIA_TYPE_AUDIO: avpriv_set_pts_info(st, 32, 1, st->codec->sample_rate); - switch(compression) { + if (st->codec->codec_tag == ID_16SV) + st->codec->codec_id = AV_CODEC_ID_PCM_S16BE_PLANAR; + else if (st->codec->codec_tag == ID_MAUD) { + if (iff->maud_bits == 8 && !iff->maud_compression) { + st->codec->codec_id = AV_CODEC_ID_PCM_U8; + } else if (iff->maud_bits == 16 && !iff->maud_compression) { + st->codec->codec_id = AV_CODEC_ID_PCM_S16BE; + } else if (iff->maud_bits == 8 && iff->maud_compression == 2) { + st->codec->codec_id = AV_CODEC_ID_PCM_ALAW; + } else if (iff->maud_bits == 8 && iff->maud_compression == 3) { + st->codec->codec_id = AV_CODEC_ID_PCM_MULAW; + } else { + avpriv_request_sample(s, "compression %d and bit depth %d", iff->maud_compression, iff->maud_bits); + return AVERROR_PATCHWELCOME; + } + + st->codec->bits_per_coded_sample = + av_get_bits_per_sample(st->codec->codec_id); + + st->codec->block_align = + st->codec->bits_per_coded_sample * st->codec->channels / 8; + } else { + switch (iff->svx8_compression) { case COMP_NONE: st->codec->codec_id = AV_CODEC_ID_PCM_S8_PLANAR; break; @@ -242,27 +388,44 @@ static int iff_read_header(AVFormatContext *s) st->codec->codec_id = AV_CODEC_ID_8SVX_EXP; break; default: - av_log(s, AV_LOG_ERROR, "unknown compression method\n"); + av_log(s, AV_LOG_ERROR, + "Unknown SVX8 compression method '%d'\n", iff->svx8_compression); return -1; } + } - st->codec->bits_per_coded_sample = 8; + st->codec->bits_per_coded_sample = av_get_bits_per_sample(st->codec->codec_id); st->codec->bit_rate = st->codec->channels * st->codec->sample_rate * st->codec->bits_per_coded_sample; st->codec->block_align = st->codec->channels * st->codec->bits_per_coded_sample; break; case AVMEDIA_TYPE_VIDEO: - switch (compression) { - case BITMAP_RAW: - st->codec->codec_id = AV_CODEC_ID_IFF_ILBM; - break; - case BITMAP_BYTERUN1: - st->codec->codec_id = AV_CODEC_ID_IFF_BYTERUN1; - break; - default: - av_log(s, AV_LOG_ERROR, "unknown compression method\n"); - return AVERROR_INVALIDDATA; + iff->bpp = st->codec->bits_per_coded_sample; + if ((screenmode & 0x800 /* Hold And Modify */) && iff->bpp <= 8) { + iff->ham = iff->bpp > 6 ? 6 : 4; + st->codec->bits_per_coded_sample = 24; } + iff->flags = (screenmode & 0x80 /* Extra HalfBrite */) && iff->bpp <= 8; + iff->masking = masking; + iff->transparency = transparency; + + if (!st->codec->extradata) { + st->codec->extradata_size = IFF_EXTRA_VIDEO_SIZE; + st->codec->extradata = av_malloc(IFF_EXTRA_VIDEO_SIZE + FF_INPUT_BUFFER_PADDING_SIZE); + if (!st->codec->extradata) + return AVERROR(ENOMEM); + } + av_assert0(st->codec->extradata_size >= IFF_EXTRA_VIDEO_SIZE); + buf = st->codec->extradata; + bytestream_put_be16(&buf, IFF_EXTRA_VIDEO_SIZE); + bytestream_put_byte(&buf, iff->bitmap_compression); + bytestream_put_byte(&buf, iff->bpp); + bytestream_put_byte(&buf, iff->ham); + bytestream_put_byte(&buf, iff->flags); + bytestream_put_be16(&buf, iff->transparency); + bytestream_put_byte(&buf, iff->masking); + bytestream_put_buffer(&buf, iff->tvdc, sizeof(iff->tvdc)); + st->codec->codec_id = AV_CODEC_ID_IFF_ILBM; break; default: return -1; @@ -276,19 +439,37 @@ static int iff_read_packet(AVFormatContext *s, { IffDemuxContext *iff = s->priv_data; AVIOContext *pb = s->pb; + AVStream *st = s->streams[0]; int ret; + int64_t pos = avio_tell(pb); - if(iff->sent_bytes >= iff->body_size) + if (pos >= iff->body_end) return AVERROR_EOF; - ret = av_get_packet(pb, pkt, iff->body_size); - if (ret < 0) - return ret; + if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) { + if (st->codec->codec_tag == ID_MAUD) { + ret = av_get_packet(pb, pkt, FFMIN(iff->body_end - pos, 1024 * st->codec->block_align)); + } else { + ret = av_get_packet(pb, pkt, iff->body_size); + } + } else if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) { + uint8_t *buf; - if(iff->sent_bytes == 0) - pkt->flags |= AV_PKT_FLAG_KEY; - iff->sent_bytes = iff->body_size; + if (av_new_packet(pkt, iff->body_size + 2) < 0) { + return AVERROR(ENOMEM); + } + + buf = pkt->data; + bytestream_put_be16(&buf, 2); + ret = avio_read(pb, buf, iff->body_size); + } else { + av_assert0(0); + } + if (pos == iff->body_pos) + pkt->flags |= AV_PKT_FLAG_KEY; + if (ret < 0) + return ret; pkt->stream_index = 0; return ret; } @@ -300,4 +481,5 @@ AVInputFormat ff_iff_demuxer = { .read_probe = iff_probe, .read_header = iff_read_header, .read_packet = iff_read_packet, + .flags = AVFMT_GENERIC_INDEX | AVFMT_NO_BYTE_SEEK, }; diff --git a/libavformat/ilbc.c b/libavformat/ilbc.c index e44af487b2..3f154ce29c 100644 --- a/libavformat/ilbc.c +++ b/libavformat/ilbc.c @@ -2,20 +2,20 @@ * iLBC storage file format * Copyright (c) 2012 Martin Storsjo * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/img2.c b/libavformat/img2.c index 3b49a32391..7539359540 100644 --- a/libavformat/img2.c +++ b/libavformat/img2.c @@ -3,20 +3,20 @@ * Copyright (c) 2000, 2001, 2002 Fabrice Bellard * Copyright (c) 2004 Michael Niedermayer * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -31,8 +31,11 @@ typedef struct { static const IdStrMap img_tags[] = { { AV_CODEC_ID_MJPEG, "jpeg" }, { AV_CODEC_ID_MJPEG, "jpg" }, + { AV_CODEC_ID_MJPEG, "jps" }, { AV_CODEC_ID_LJPEG, "ljpg" }, + { AV_CODEC_ID_JPEGLS, "jls" }, { AV_CODEC_ID_PNG, "png" }, + { AV_CODEC_ID_PNG, "pns" }, { AV_CODEC_ID_PNG, "mng" }, { AV_CODEC_ID_PPM, "ppm" }, { AV_CODEC_ID_PPM, "pnm" }, @@ -45,28 +48,34 @@ static const IdStrMap img_tags[] = { { AV_CODEC_ID_MPEG4, "mpg4-img" }, { AV_CODEC_ID_FFV1, "ffv1-img" }, { AV_CODEC_ID_RAWVIDEO, "y" }, + { AV_CODEC_ID_RAWVIDEO, "raw" }, { AV_CODEC_ID_BMP, "bmp" }, - { AV_CODEC_ID_GIF, "gif" }, { AV_CODEC_ID_TARGA, "tga" }, { AV_CODEC_ID_TIFF, "tiff" }, { AV_CODEC_ID_TIFF, "tif" }, { AV_CODEC_ID_SGI, "sgi" }, { AV_CODEC_ID_PTX, "ptx" }, { AV_CODEC_ID_PCX, "pcx" }, + { AV_CODEC_ID_BRENDER_PIX, "pix" }, { AV_CODEC_ID_SUNRAST, "sun" }, { AV_CODEC_ID_SUNRAST, "ras" }, { AV_CODEC_ID_SUNRAST, "rs" }, { AV_CODEC_ID_SUNRAST, "im1" }, { AV_CODEC_ID_SUNRAST, "im8" }, { AV_CODEC_ID_SUNRAST, "im24" }, + { AV_CODEC_ID_SUNRAST, "im32" }, { AV_CODEC_ID_SUNRAST, "sunras" }, + { AV_CODEC_ID_JPEG2000, "j2c" }, { AV_CODEC_ID_JPEG2000, "jp2" }, { AV_CODEC_ID_JPEG2000, "jpc" }, { AV_CODEC_ID_JPEG2000, "j2k" }, { AV_CODEC_ID_DPX, "dpx" }, + { AV_CODEC_ID_EXR, "exr" }, { AV_CODEC_ID_PICTOR, "pic" }, + { AV_CODEC_ID_V210X, "yuv10" }, { AV_CODEC_ID_WEBP, "webp" }, { AV_CODEC_ID_XBM, "xbm" }, + { AV_CODEC_ID_XFACE, "xface" }, { AV_CODEC_ID_XWD, "xwd" }, { AV_CODEC_ID_NONE, NULL } }; diff --git a/libavformat/img2dec.c b/libavformat/img2dec.c index 9acb6f6927..5163e69f65 100644 --- a/libavformat/img2dec.c +++ b/libavformat/img2dec.c @@ -3,23 +3,24 @@ * Copyright (c) 2000, 2001, 2002 Fabrice Bellard * Copyright (c) 2004 Michael Niedermayer * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include <sys/stat.h> #include "libavutil/avstring.h" #include "libavutil/log.h" #include "libavutil/opt.h" @@ -27,20 +28,43 @@ #include "libavutil/parseutils.h" #include "avformat.h" #include "internal.h" +#if HAVE_GLOB +#include <glob.h> + +/* Locally define as 0 (bitwise-OR no-op) any missing glob options that + are non-posix glibc/bsd extensions. */ +#ifndef GLOB_NOMAGIC +#define GLOB_NOMAGIC 0 +#endif +#ifndef GLOB_BRACE +#define GLOB_BRACE 0 +#endif + +#endif /* HAVE_GLOB */ typedef struct { const AVClass *class; /**< Class for private options. */ int img_first; int img_last; int img_number; + int64_t pts; int img_count; int is_pipe; + int split_planes; /**< use independent file for each Y, U, V plane */ char path[1024]; char *pixel_format; /**< Set by a private option. */ - char *video_size; /**< Set by a private option. */ - char *framerate; /**< Set by a private option. */ + int width, height; /**< Set by a private option. */ + AVRational framerate; /**< Set by a private option. */ int loop; + enum { PT_GLOB_SEQUENCE, PT_GLOB, PT_SEQUENCE } pattern_type; + int use_glob; +#if HAVE_GLOB + glob_t globstate; +#endif int start_number; + int start_number_range; + int frame_size; + int ts_from_file; } VideoDemuxData; static const int sizes[][2] = { @@ -70,15 +94,44 @@ static int infer_size(int *width_ptr, int *height_ptr, int size) return -1; } -/* return -1 if no image found */ +static int is_glob(const char *path) +{ +#if HAVE_GLOB + size_t span = 0; + const char *p = path; + + while (p = strchr(p, '%')) { + if (*(++p) == '%') { + ++p; + continue; + } + if (span = strspn(p, "*?[]{}")) + break; + } + /* Did we hit a glob char or get to the end? */ + return span != 0; +#else + return 0; +#endif +} + +/** + * Get index range of image files matched by path. + * + * @param pfirst_index pointer to index updated with the first number in the range + * @param plast_index pointer to index updated with the last number in the range + * @param path path which has to be matched by the image files in the range + * @param start_index minimum accepted value for the first index in the range + * @return -1 if no image file could be found + */ static int find_image_range(int *pfirst_index, int *plast_index, - const char *path, int max_start) + const char *path, int start_index, int start_index_range) { char buf[1024]; int range, last_index, range1, first_index; /* find the first image */ - for (first_index = 0; first_index < max_start; first_index++) { + for (first_index = start_index; first_index < start_index + start_index_range; first_index++) { if (av_get_frame_filename(buf, sizeof(buf), path, first_index) < 0) { *pfirst_index = *plast_index = 1; @@ -89,7 +142,7 @@ static int find_image_range(int *pfirst_index, int *plast_index, if (avio_check(buf, AVIO_FLAG_READ) > 0) break; } - if (first_index == 5) + if (first_index == start_index + start_index_range) goto fail; /* find the last image */ @@ -129,6 +182,10 @@ static int img_read_probe(AVProbeData *p) if (p->filename && ff_guess_image2_codec(p->filename)) { if (av_filename_number_test(p->filename)) return AVPROBE_SCORE_MAX; + else if (is_glob(p->filename)) + return AVPROBE_SCORE_MAX; + else if (av_match_ext(p->filename, "raw") || av_match_ext(p->filename, "gif")) + return 5; else return AVPROBE_SCORE_EXTENSION; } @@ -138,11 +195,9 @@ static int img_read_probe(AVProbeData *p) static int img_read_header(AVFormatContext *s1) { VideoDemuxData *s = s1->priv_data; - int first_index, last_index, ret = 0; - int width = 0, height = 0; + int first_index, last_index; AVStream *st; enum AVPixelFormat pix_fmt = AV_PIX_FMT_NONE; - AVRational framerate; s1->ctx_flags |= AVFMTCTX_NOHEADER; @@ -157,17 +212,6 @@ static int img_read_header(AVFormatContext *s1) s->pixel_format); return AVERROR(EINVAL); } - if (s->video_size && - (ret = av_parse_video_size(&width, &height, s->video_size)) < 0) { - av_log(s, AV_LOG_ERROR, - "Could not parse video size: %s.\n", s->video_size); - return ret; - } - if ((ret = av_parse_video_rate(&framerate, s->framerate)) < 0) { - av_log(s, AV_LOG_ERROR, - "Could not parse framerate: %s.\n", s->framerate); - return ret; - } av_strlcpy(s->path, s1->filename, sizeof(s->path)); s->img_number = 0; @@ -181,23 +225,86 @@ static int img_read_header(AVFormatContext *s1) st->need_parsing = AVSTREAM_PARSE_FULL; } - avpriv_set_pts_info(st, 60, framerate.den, framerate.num); + if (s->ts_from_file) + avpriv_set_pts_info(st, 64, 1, 1); + else + avpriv_set_pts_info(st, 64, s->framerate.den, s->framerate.num); - if (width && height) { - st->codec->width = width; - st->codec->height = height; + if (s->width && s->height) { + st->codec->width = s->width; + st->codec->height = s->height; } if (!s->is_pipe) { - if (find_image_range(&first_index, &last_index, s->path, - FFMAX(s->start_number, 5)) < 0) - return AVERROR(ENOENT); + if (s->pattern_type == PT_GLOB_SEQUENCE) { + s->use_glob = is_glob(s->path); + if (s->use_glob) { + char *p = s->path, *q, *dup; + int gerr; + + av_log(s1, AV_LOG_WARNING, "Pattern type 'glob_sequence' is deprecated: " + "use pattern_type 'glob' instead\n"); +#if HAVE_GLOB + dup = q = av_strdup(p); + while (*q) { + /* Do we have room for the next char and a \ insertion? */ + if ((p - s->path) >= (sizeof(s->path) - 2)) + break; + if (*q == '%' && strspn(q + 1, "%*?[]{}")) + ++q; + else if (strspn(q, "\\*?[]{}")) + *p++ = '\\'; + *p++ = *q++; + } + *p = 0; + av_free(dup); + + gerr = glob(s->path, GLOB_NOCHECK|GLOB_BRACE|GLOB_NOMAGIC, NULL, &s->globstate); + if (gerr != 0) { + return AVERROR(ENOENT); + } + first_index = 0; + last_index = s->globstate.gl_pathc - 1; +#endif + } + } + if ((s->pattern_type == PT_GLOB_SEQUENCE && !s->use_glob) || s->pattern_type == PT_SEQUENCE) { + if (find_image_range(&first_index, &last_index, s->path, + s->start_number, s->start_number_range) < 0) { + av_log(s1, AV_LOG_ERROR, + "Could find no file with path '%s' and index in the range %d-%d\n", + s->path, s->start_number, s->start_number + s->start_number_range - 1); + return AVERROR(ENOENT); + } + } else if (s->pattern_type == PT_GLOB) { +#if HAVE_GLOB + int gerr; + gerr = glob(s->path, GLOB_NOCHECK|GLOB_BRACE|GLOB_NOMAGIC, NULL, &s->globstate); + if (gerr != 0) { + return AVERROR(ENOENT); + } + first_index = 0; + last_index = s->globstate.gl_pathc - 1; + s->use_glob = 1; +#else + av_log(s1, AV_LOG_ERROR, + "Pattern type 'glob' was selected but globbing " + "is not supported by this libavformat build\n"); + return AVERROR(ENOSYS); +#endif + } else if (s->pattern_type != PT_GLOB_SEQUENCE) { + av_log(s1, AV_LOG_ERROR, + "Unknown value '%d' for pattern_type option\n", s->pattern_type); + return AVERROR(EINVAL); + } s->img_first = first_index; s->img_last = last_index; s->img_number = first_index; /* compute duration */ - st->start_time = 0; - st->duration = last_index - first_index + 1; + if (!s->ts_from_file) { + st->start_time = 0; + st->duration = last_index - first_index + 1; + } } if (s1->video_codec_id) { @@ -207,8 +314,12 @@ static int img_read_header(AVFormatContext *s1) st->codec->codec_type = AVMEDIA_TYPE_AUDIO; st->codec->codec_id = s1->audio_codec_id; } else { + const char *str = strrchr(s->path, '.'); + s->split_planes = str && !av_strcasecmp(str + 1, "y"); st->codec->codec_type = AVMEDIA_TYPE_VIDEO; st->codec->codec_id = ff_guess_image2_codec(s->path); + if (st->codec->codec_id == AV_CODEC_ID_LJPEG) + st->codec->codec_id = AV_CODEC_ID_MJPEG; } if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO && pix_fmt != AV_PIX_FMT_NONE) @@ -220,7 +331,8 @@ static int img_read_header(AVFormatContext *s1) static int img_read_packet(AVFormatContext *s1, AVPacket *pkt) { VideoDemuxData *s = s1->priv_data; - char filename[1024]; + char filename_bytes[1024]; + char *filename = filename_bytes; int i; int size[3] = { 0 }, ret[3] = { 0 }; AVIOContext *f[3] = { NULL }; @@ -233,10 +345,16 @@ static int img_read_packet(AVFormatContext *s1, AVPacket *pkt) } if (s->img_number > s->img_last) return AVERROR_EOF; - if (av_get_frame_filename(filename, sizeof(filename), + if (s->use_glob) { +#if HAVE_GLOB + filename = s->globstate.gl_pathv[s->img_number]; +#endif + } else { + if (av_get_frame_filename(filename_bytes, sizeof(filename_bytes), s->path, s->img_number) < 0 && s->img_number > 1) return AVERROR(EIO); + } for (i = 0; i < 3; i++) { if (avio_open2(&f[i], filename, AVIO_FLAG_READ, &s1->interrupt_callback, NULL) < 0) { @@ -248,7 +366,7 @@ static int img_read_packet(AVFormatContext *s1, AVPacket *pkt) } size[i] = avio_size(f[i]); - if (codec->codec_id != AV_CODEC_ID_RAWVIDEO) + if (!s->split_planes) break; filename[strlen(filename) - 1] = 'U' + i; } @@ -257,14 +375,28 @@ static int img_read_packet(AVFormatContext *s1, AVPacket *pkt) infer_size(&codec->width, &codec->height, size[0]); } else { f[0] = s1->pb; - if (f[0]->eof_reached) + if (url_feof(f[0])) return AVERROR(EIO); - size[0] = 4096; + if (s->frame_size > 0) { + size[0] = s->frame_size; + } else { + size[0] = 4096; + } } - av_new_packet(pkt, size[0] + size[1] + size[2]); + if (av_new_packet(pkt, size[0] + size[1] + size[2]) < 0) + return AVERROR(ENOMEM); pkt->stream_index = 0; pkt->flags |= AV_PKT_FLAG_KEY; + if (s->ts_from_file) { + struct stat img_stat; + if (stat(filename, &img_stat)) + return AVERROR(EIO); + pkt->pts = (int64_t)img_stat.st_mtime; + av_add_index_entry(s1->streams[0], s->img_number, pkt->pts, 0, 0, AVINDEX_KEYFRAME); + } else if (!s->is_pipe) { + pkt->pts = s->pts; + } pkt->size = 0; for (i = 0; i < 3; i++) { @@ -283,18 +415,59 @@ static int img_read_packet(AVFormatContext *s1, AVPacket *pkt) } else { s->img_count++; s->img_number++; + s->pts++; return 0; } } +static int img_read_close(struct AVFormatContext* s1) +{ + VideoDemuxData *s = s1->priv_data; +#if HAVE_GLOB + if (s->use_glob) { + globfree(&s->globstate); + } +#endif + return 0; +} + +static int img_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp, int flags) +{ + VideoDemuxData *s1 = s->priv_data; + AVStream *st = s->streams[0]; + + if (s1->ts_from_file) { + int index = av_index_search_timestamp(st, timestamp, flags); + if(index < 0) + return -1; + s1->img_number = st->index_entries[index].pos; + return 0; + } + + if (timestamp < 0 || !s1->loop && timestamp > s1->img_last - s1->img_first) + return -1; + s1->img_number = timestamp%(s1->img_last - s1->img_first + 1) + s1->img_first; + s1->pts = timestamp; + return 0; +} + #define OFFSET(x) offsetof(VideoDemuxData, x) #define DEC AV_OPT_FLAG_DECODING_PARAM static const AVOption options[] = { - { "pixel_format", "", OFFSET(pixel_format), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, DEC }, - { "video_size", "", OFFSET(video_size), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, DEC }, - { "framerate", "", OFFSET(framerate), AV_OPT_TYPE_STRING, { .str = "25" }, 0, 0, DEC }, - { "loop", "", OFFSET(loop), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, DEC }, - { "start_number", "first number in the sequence", OFFSET(start_number), AV_OPT_TYPE_INT, { .i64 = 1 }, 1, INT_MAX, DEC }, + { "framerate", "set the video framerate", OFFSET(framerate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, 0, DEC }, + { "loop", "force loop over input file sequence", OFFSET(loop), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, 1, DEC }, + + { "pattern_type", "set pattern type", OFFSET(pattern_type), AV_OPT_TYPE_INT, {.i64=PT_GLOB_SEQUENCE}, 0, INT_MAX, DEC, "pattern_type"}, + { "glob_sequence","select glob/sequence pattern type", 0, AV_OPT_TYPE_CONST, {.i64=PT_GLOB_SEQUENCE}, INT_MIN, INT_MAX, DEC, "pattern_type" }, + { "glob", "select glob pattern type", 0, AV_OPT_TYPE_CONST, {.i64=PT_GLOB }, INT_MIN, INT_MAX, DEC, "pattern_type" }, + { "sequence", "select sequence pattern type", 0, AV_OPT_TYPE_CONST, {.i64=PT_SEQUENCE }, INT_MIN, INT_MAX, DEC, "pattern_type" }, + + { "pixel_format", "set video pixel format", OFFSET(pixel_format), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, DEC }, + { "start_number", "set first number in the sequence", OFFSET(start_number), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, INT_MAX, DEC }, + { "start_number_range", "set range for looking at the first sequence number", OFFSET(start_number_range), AV_OPT_TYPE_INT, {.i64 = 5}, 1, INT_MAX, DEC }, + { "video_size", "set video size", OFFSET(width), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, DEC }, + { "frame_size", "force frame size in bytes", OFFSET(frame_size), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, INT_MAX, DEC }, + { "ts_from_file", "set frame timestamp from file's one", OFFSET(ts_from_file), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, 1, DEC }, { NULL }, }; @@ -312,6 +485,8 @@ AVInputFormat ff_image2_demuxer = { .read_probe = img_read_probe, .read_header = img_read_header, .read_packet = img_read_packet, + .read_close = img_read_close, + .read_seek = img_read_seek, .flags = AVFMT_NOFILE, .priv_class = &img2_class, }; diff --git a/libavformat/img2enc.c b/libavformat/img2enc.c index 7b94869c56..8adf3526a6 100644 --- a/libavformat/img2enc.c +++ b/libavformat/img2enc.c @@ -3,42 +3,48 @@ * Copyright (c) 2000, 2001, 2002 Fabrice Bellard * Copyright (c) 2004 Michael Niedermayer * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "libavutil/intreadwrite.h" #include "libavutil/avstring.h" #include "libavutil/log.h" +#include "libavutil/opt.h" +#include "libavutil/pixdesc.h" #include "avformat.h" #include "avio_internal.h" #include "internal.h" -#include "libavutil/opt.h" typedef struct { const AVClass *class; /**< Class for private options. */ int img_number; int is_pipe; + int split_planes; /**< use independent file for each Y, U, V plane */ char path[1024]; int update; + int use_strftime; } VideoMuxData; static int write_header(AVFormatContext *s) { VideoMuxData *img = s->priv_data; + AVStream *st = s->streams[0]; + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(st->codec->pix_fmt); + const char *str; av_strlcpy(img->path, s->filename, sizeof(img->path)); @@ -48,74 +54,77 @@ static int write_header(AVFormatContext *s) else img->is_pipe = 1; + str = strrchr(img->path, '.'); + img->split_planes = str + && !av_strcasecmp(str + 1, "y") + && s->nb_streams == 1 + && st->codec->codec_id == AV_CODEC_ID_RAWVIDEO + && desc + &&(desc->flags & AV_PIX_FMT_FLAG_PLANAR) + && desc->nb_components >= 3; return 0; } static int write_packet(AVFormatContext *s, AVPacket *pkt) { VideoMuxData *img = s->priv_data; - AVIOContext *pb[3]; + AVIOContext *pb[4]; char filename[1024]; AVCodecContext *codec = s->streams[pkt->stream_index]->codec; + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(codec->pix_fmt); int i; if (!img->is_pipe) { if (img->update) { av_strlcpy(filename, img->path, sizeof(filename)); + } else if (img->use_strftime) { + time_t now0; + struct tm *tm; + time(&now0); + tm = localtime(&now0); + if (!strftime(filename, sizeof(filename), img->path, tm)) { + av_log(s, AV_LOG_ERROR, "Could not get frame filename with strftime\n"); + return AVERROR(EINVAL); + } } else if (av_get_frame_filename(filename, sizeof(filename), img->path, img->img_number) < 0 && img->img_number > 1) { av_log(s, AV_LOG_ERROR, - "Could not get frame filename number %d from pattern '%s'\n", + "Could not get frame filename number %d from pattern '%s' (either set updatefirst or use a pattern like %%03d within the filename pattern)\n", img->img_number, img->path); - return AVERROR(EIO); + return AVERROR(EINVAL); } - for (i = 0; i < 3; i++) { + for (i = 0; i < 4; i++) { if (avio_open2(&pb[i], filename, AVIO_FLAG_WRITE, &s->interrupt_callback, NULL) < 0) { av_log(s, AV_LOG_ERROR, "Could not open file : %s\n", filename); return AVERROR(EIO); } - if (codec->codec_id != AV_CODEC_ID_RAWVIDEO) + if (!img->split_planes || i+1 >= desc->nb_components) break; - filename[strlen(filename) - 1] = 'U' + i; + filename[strlen(filename) - 1] = ((int[]){'U','V','A','x'})[i]; } } else { pb[0] = s->pb; } - if (codec->codec_id == AV_CODEC_ID_RAWVIDEO) { + if (img->split_planes) { int ysize = codec->width * codec->height; - avio_write(pb[0], pkt->data, ysize); - avio_write(pb[1], pkt->data + ysize, (pkt->size - ysize) / 2); - avio_write(pb[2], pkt->data + ysize + (pkt->size - ysize) / 2, (pkt->size - ysize) / 2); + int usize = FF_CEIL_RSHIFT(codec->width, desc->log2_chroma_w) * FF_CEIL_RSHIFT(codec->height, desc->log2_chroma_h); + if (desc->comp[0].depth_minus1 >= 8) { + ysize *= 2; + usize *= 2; + } + avio_write(pb[0], pkt->data , ysize); + avio_write(pb[1], pkt->data + ysize , usize); + avio_write(pb[2], pkt->data + ysize + usize, usize); avio_close(pb[1]); avio_close(pb[2]); - } else { - if (ff_guess_image2_codec(s->filename) == AV_CODEC_ID_JPEG2000) { - AVStream *st = s->streams[0]; - if (st->codec->extradata_size > 8 && - AV_RL32(st->codec->extradata + 4) == MKTAG('j', 'p', '2', 'h')) { - if (pkt->size < 8 || - AV_RL32(pkt->data + 4) != MKTAG('j', 'p', '2', 'c')) - goto error; - avio_wb32(pb[0], 12); - ffio_wfourcc(pb[0], "jP "); - avio_wb32(pb[0], 0x0D0A870A); // signature - avio_wb32(pb[0], 20); - ffio_wfourcc(pb[0], "ftyp"); - ffio_wfourcc(pb[0], "jp2 "); - avio_wb32(pb[0], 0); - ffio_wfourcc(pb[0], "jp2 "); - avio_write(pb[0], st->codec->extradata, st->codec->extradata_size); - } else if (pkt->size < 8 || - (!st->codec->extradata_size && - AV_RL32(pkt->data + 4) != MKTAG('j', 'P', ' ', ' '))) { // signature -error: - av_log(s, AV_LOG_ERROR, "malformed JPEG 2000 codestream\n"); - return -1; - } + if (desc->nb_components > 3) { + avio_write(pb[3], pkt->data + ysize + 2*usize, ysize); + avio_close(pb[3]); } + } else { avio_write(pb[0], pkt->data, pkt->size); } avio_flush(pb[0]); @@ -130,8 +139,10 @@ error: #define OFFSET(x) offsetof(VideoMuxData, x) #define ENC AV_OPT_FLAG_ENCODING_PARAM static const AVOption muxoptions[] = { - { "start_number", "first number in the sequence", OFFSET(img_number), AV_OPT_TYPE_INT, { .i64 = 1 }, 1, INT_MAX, ENC }, + { "updatefirst", "continuously overwrite one file", OFFSET(update), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, ENC }, { "update", "continuously overwrite one file", OFFSET(update), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, ENC }, + { "start_number", "set first number in the sequence", OFFSET(img_number), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, INT_MAX, ENC }, + { "strftime", "use strftime for filename", OFFSET(use_strftime), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, ENC }, { NULL }, }; @@ -146,9 +157,9 @@ static const AVClass img2mux_class = { AVOutputFormat ff_image2_muxer = { .name = "image2", .long_name = NULL_IF_CONFIG_SMALL("image2 sequence"), - .extensions = "bmp,dpx,jpeg,jpg,ljpg,pam,pbm,pcx,pgm,pgmyuv,png," - "ppm,sgi,tga,tif,tiff,jp2,xwd,sun,ras,rs,im1,im8,im24," - "sunras,xbm", + .extensions = "bmp,dpx,jls,jpeg,jpg,ljpg,pam,pbm,pcx,pgm,pgmyuv,png," + "ppm,sgi,tga,tif,tiff,jp2,j2c,j2k,xwd,sun,ras,rs,im1,im8,im24," + "sunras,xbm,xface", .priv_data_size = sizeof(VideoMuxData), .video_codec = AV_CODEC_ID_MJPEG, .write_header = write_header, diff --git a/libavformat/ingenientdec.c b/libavformat/ingenientdec.c index 42b29ef6e3..94c549c711 100644 --- a/libavformat/ingenientdec.c +++ b/libavformat/ingenientdec.c @@ -2,20 +2,20 @@ * RAW Ingenient MJPEG demuxer * Copyright (c) 2005 Alex Beregszaszi * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -44,17 +44,10 @@ static int ingenient_read_packet(AVFormatContext *s, AVPacket *pkt) av_log(s, AV_LOG_DEBUG, "Ingenient packet: size=%d, width=%d, height=%d, unk1=%d unk2=%d\n", size, w, h, unk1, unk2); - if (av_new_packet(pkt, size) < 0) - return AVERROR(ENOMEM); - - pkt->pos = avio_tell(s->pb); - pkt->stream_index = 0; - ret = avio_read(s->pb, pkt->data, size); - if (ret < 0) { - av_free_packet(pkt); + ret = av_get_packet(s->pb, pkt, size); + if (ret < 0) return ret; - } - pkt->size = ret; + pkt->stream_index = 0; return ret; } diff --git a/libavformat/internal.h b/libavformat/internal.h index 1bc3e51761..36ee4c0be9 100644 --- a/libavformat/internal.h +++ b/libavformat/internal.h @@ -1,20 +1,20 @@ /* * copyright (c) 2001 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -26,6 +26,10 @@ #define MAX_URL_SIZE 4096 +/** size of probe buffer, for guessing file type from file contents */ +#define PROBE_BUF_MIN 2048 +#define PROBE_BUF_MAX (1<<20) + #ifdef DEBUG # define hex_dump_debug(class, buf, size) av_hex_dump_log(class, AV_LOG_DEBUG, buf, size) #else @@ -42,20 +46,18 @@ typedef struct CodecMime{ enum AVCodecID id; } CodecMime; -void ff_dynarray_add(intptr_t **tab_ptr, int *nb_ptr, intptr_t elem); - #ifdef __GNUC__ #define dynarray_add(tab, nb_ptr, elem)\ do {\ __typeof__(tab) _tab = (tab);\ __typeof__(elem) _elem = (elem);\ (void)sizeof(**_tab == _elem); /* check that types are compatible */\ - ff_dynarray_add((intptr_t **)_tab, nb_ptr, (intptr_t)_elem);\ + av_dynarray_add(_tab, nb_ptr, _elem);\ } while(0) #else #define dynarray_add(tab, nb_ptr, elem)\ do {\ - ff_dynarray_add((intptr_t **)(tab), nb_ptr, (intptr_t)(elem));\ + av_dynarray_add((tab), nb_ptr, (elem));\ } while(0) #endif @@ -78,8 +80,9 @@ void ff_program_add_stream_index(AVFormatContext *ac, int progid, unsigned int i /** * Add packet to AVFormatContext->packet_buffer list, determining its * interleaved position using compare() function argument. + * @return 0, or < 0 on error */ -void ff_interleave_add_packet(AVFormatContext *s, AVPacket *pkt, +int ff_interleave_add_packet(AVFormatContext *s, AVPacket *pkt, int (*compare)(AVFormatContext *, AVPacket *, AVPacket *)); void ff_read_frame_flush(AVFormatContext *s); @@ -243,6 +246,9 @@ int ff_seek_frame_binary(AVFormatContext *s, int stream_index, */ void ff_update_cur_dts(AVFormatContext *s, AVStream *ref_st, int64_t timestamp); +int ff_find_last_ts(AVFormatContext *s, int stream_index, int64_t *ts, int64_t *pos, + int64_t (*read_timestamp)(struct AVFormatContext *, int , int64_t *, int64_t )); + /** * Perform a binary search using read_timestamp(). * @@ -312,6 +318,8 @@ int ff_read_packet(AVFormatContext *s, AVPacket *pkt); int ff_interleave_packet_per_dts(AVFormatContext *s, AVPacket *out, AVPacket *pkt, int flush); +void ff_free_stream(AVFormatContext *s, AVStream *st); + /** * Return the frame duration in seconds. Return 0 if not available. */ @@ -339,4 +347,27 @@ enum AVCodecID ff_codec_get_id(const AVCodecTag *tags, unsigned int tag); */ enum AVCodecID ff_get_pcm_codec_id(int bps, int flt, int be, int sflags); +/** + * Chooses a timebase for muxing the specified stream. + * + * The choosen timebase allows sample accurate timestamps based + * on the framerate or sample rate for audio streams. It also is + * at least as precisse as 1/min_precission would be. + */ +AVRational ff_choose_timebase(AVFormatContext *s, AVStream *st, int min_precission); + +/** + * Generate standard extradata for AVC-Intra based on width/height and field order. + */ +void ff_generate_avci_extradata(AVStream *st); + +/** + * Allocate extradata with additional FF_INPUT_BUFFER_PADDING_SIZE at end + * which is always set to 0. + * + * @param size size of extradata + * @return 0 if OK, AVERROR_xxx on error + */ +int ff_alloc_extradata(AVCodecContext *avctx, int size); + #endif /* AVFORMAT_INTERNAL_H */ diff --git a/libavformat/ipmovie.c b/libavformat/ipmovie.c index 60ae939003..676363b628 100644 --- a/libavformat/ipmovie.c +++ b/libavformat/ipmovie.c @@ -2,20 +2,20 @@ * Interplay MVE File Demuxer * Copyright (c) 2003 The ffmpeg Project * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -117,7 +117,7 @@ static int load_ipmovie_packet(IPMVEContext *s, AVIOContext *pb, int chunk_type; - if (s->audio_chunk_offset) { + if (s->audio_chunk_offset && s->audio_channels && s->audio_bits) { if (s->audio_type == AV_CODEC_ID_NONE) { av_log(NULL, AV_LOG_ERROR, "Can not read audio packet before" "audio codec is known\n"); @@ -237,7 +237,7 @@ static int process_ipmovie_chunk(IPMVEContext *s, AVIOContext *pb, return chunk_type; /* read the next chunk, wherever the file happens to be pointing */ - if (pb->eof_reached) + if (url_feof(pb)) return CHUNK_EOF; if (avio_read(pb, chunk_preamble, CHUNK_PREAMBLE_SIZE) != CHUNK_PREAMBLE_SIZE) @@ -283,7 +283,7 @@ static int process_ipmovie_chunk(IPMVEContext *s, AVIOContext *pb, while ((chunk_size > 0) && (chunk_type != CHUNK_BAD)) { /* read the next chunk, wherever the file happens to be pointing */ - if (pb->eof_reached) { + if (url_feof(pb)) { chunk_type = CHUNK_EOF; break; } @@ -476,7 +476,8 @@ static int process_ipmovie_chunk(IPMVEContext *s, AVIOContext *pb, r = scratch[j++] * 4; g = scratch[j++] * 4; b = scratch[j++] * 4; - s->palette[i] = (r << 16) | (g << 8) | (b); + s->palette[i] = (0xFFU << 24) | (r << 16) | (g << 8) | (b); + s->palette[i] |= s->palette[i] >> 6 & 0x30303; } s->has_palette = 1; break; @@ -526,11 +527,12 @@ static const char signature[] = "Interplay MVE File\x1A\0\x1A"; static int ipmovie_probe(AVProbeData *p) { - uint8_t *b = p->buf; - uint8_t *b_end = p->buf + p->buf_size - sizeof(signature); + const uint8_t *b = p->buf; + const uint8_t *b_end = p->buf + p->buf_size - sizeof(signature); do { - if (memcmp(b++, signature, sizeof(signature)) == 0) + if (b[0] == signature[0] && memcmp(b, signature, sizeof(signature)) == 0) return AVPROBE_SCORE_MAX; + b++; } while (b < b_end); return 0; @@ -543,14 +545,14 @@ static int ipmovie_read_header(AVFormatContext *s) AVPacket pkt; AVStream *st; unsigned char chunk_preamble[CHUNK_PREAMBLE_SIZE]; - int chunk_type; + int chunk_type, i; uint8_t signature_buffer[sizeof(signature)]; avio_read(pb, signature_buffer, sizeof(signature_buffer)); while (memcmp(signature_buffer, signature, sizeof(signature))) { memmove(signature_buffer, signature_buffer + 1, sizeof(signature_buffer) - 1); signature_buffer[sizeof(signature_buffer) - 1] = avio_r8(pb); - if (pb->eof_reached) + if (url_feof(pb)) return AVERROR_EOF; } /* initialize private context members */ @@ -561,6 +563,9 @@ static int ipmovie_read_header(AVFormatContext *s) /* on the first read, this will position the stream at the first chunk */ ipmovie->next_chunk_offset = avio_tell(pb) + 4; + for (i = 0; i < 256; i++) + ipmovie->palette[i] = 0xFFU << 24; + /* process the first chunk which should be CHUNK_INIT_VIDEO */ if (process_ipmovie_chunk(ipmovie, pb, &pkt) != CHUNK_INIT_VIDEO) return AVERROR_INVALIDDATA; @@ -622,6 +627,7 @@ static int ipmovie_read_packet(AVFormatContext *s, AVIOContext *pb = s->pb; int ret; + for (;;) { ret = process_ipmovie_chunk(ipmovie, pb, pkt); if (ret == CHUNK_BAD) ret = AVERROR_INVALIDDATA; @@ -631,10 +637,13 @@ static int ipmovie_read_packet(AVFormatContext *s, ret = AVERROR(ENOMEM); else if (ret == CHUNK_VIDEO) ret = 0; + else if (ret == CHUNK_INIT_VIDEO || ret == CHUNK_INIT_AUDIO) + continue; else ret = -1; return ret; + } } AVInputFormat ff_ipmovie_demuxer = { diff --git a/libavformat/ircam.c b/libavformat/ircam.c new file mode 100644 index 0000000000..a267c18a08 --- /dev/null +++ b/libavformat/ircam.c @@ -0,0 +1,47 @@ +/* + * IRCAM common code + * Copyright (c) 2012 Paul B Mahol + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "avformat.h" +#include "internal.h" + +const AVCodecTag ff_codec_ircam_le_tags[] = { + { AV_CODEC_ID_PCM_ALAW, 0x10001 }, + { AV_CODEC_ID_PCM_F32LE, 0x00004 }, + { AV_CODEC_ID_PCM_F64LE, 0x00008 }, + { AV_CODEC_ID_PCM_MULAW, 0x20001 }, + { AV_CODEC_ID_PCM_S16LE, 0x00002 }, + { AV_CODEC_ID_PCM_S24LE, 0x00003 }, + { AV_CODEC_ID_PCM_S32LE, 0x40004 }, + { AV_CODEC_ID_PCM_S8, 0x00001 }, + { AV_CODEC_ID_NONE, 0 }, +}; + +const AVCodecTag ff_codec_ircam_be_tags[] = { + { AV_CODEC_ID_PCM_ALAW, 0x10001 }, + { AV_CODEC_ID_PCM_F32BE, 0x00004 }, + { AV_CODEC_ID_PCM_F64BE, 0x00008 }, + { AV_CODEC_ID_PCM_MULAW, 0x20001 }, + { AV_CODEC_ID_PCM_S16BE, 0x00002 }, + { AV_CODEC_ID_PCM_S24BE, 0x00003 }, + { AV_CODEC_ID_PCM_S32BE, 0x40004 }, + { AV_CODEC_ID_PCM_S8, 0x00001 }, + { AV_CODEC_ID_NONE, 0 }, +}; diff --git a/libavformat/ircam.h b/libavformat/ircam.h new file mode 100644 index 0000000000..f7f9c84b91 --- /dev/null +++ b/libavformat/ircam.h @@ -0,0 +1,30 @@ +/* + * IRCAM common code + * Copyright (c) 2012 Paul B Mahol + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVFORMAT_IRCAM_H +#define AVFORMAT_IRCAM_H + +#include "internal.h" + +extern const AVCodecTag ff_codec_ircam_be_tags[]; +extern const AVCodecTag ff_codec_ircam_le_tags[]; + +#endif /* AVFORMAT_IRCAM_H */ diff --git a/libavformat/ircamdec.c b/libavformat/ircamdec.c new file mode 100644 index 0000000000..f9533ec4a5 --- /dev/null +++ b/libavformat/ircamdec.c @@ -0,0 +1,115 @@ +/* + * IRCAM demuxer + * Copyright (c) 2012 Paul B Mahol + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/intreadwrite.h" +#include "avformat.h" +#include "internal.h" +#include "pcm.h" +#include "ircam.h" + +static int ircam_probe(AVProbeData *p) +{ + if ((p->buf[0] == 0x64 && p->buf[1] == 0xA3 && p->buf[3] == 0x00 && + p->buf[2] >= 1 && p->buf[2] <= 4) || + (p->buf[3] == 0x64 && p->buf[2] == 0xA3 && p->buf[0] == 0x00 && + p->buf[1] >= 1 && p->buf[1] <= 3) && + AV_RN32(p->buf + 4) && AV_RN32(p->buf + 8)) + return AVPROBE_SCORE_MAX / 4 * 3; + return 0; +} + +static const struct endianess { + uint32_t magic; + int is_le; +} table[] = { + { 0x64A30100, 0 }, + { 0x64A30200, 1 }, + { 0x64A30300, 0 }, + { 0x64A30400, 1 }, + { 0x0001A364, 1 }, + { 0x0002A364, 0 }, + { 0x0003A364, 1 }, +}; + +static int ircam_read_header(AVFormatContext *s) +{ + uint32_t magic, sample_rate, channels, tag; + const AVCodecTag *tags; + int le = -1, i; + AVStream *st; + + magic = avio_rl32(s->pb); + for (i = 0; i < 7; i++) { + if (magic == table[i].magic) { + le = table[i].is_le; + break; + } + } + + if (le == 1) { + sample_rate = av_int2float(avio_rl32(s->pb)); + channels = avio_rl32(s->pb); + tag = avio_rl32(s->pb); + tags = ff_codec_ircam_le_tags; + } else if (le == 0) { + sample_rate = av_int2float(avio_rb32(s->pb)); + channels = avio_rb32(s->pb); + tag = avio_rb32(s->pb); + tags = ff_codec_ircam_be_tags; + } else { + return AVERROR_INVALIDDATA; + } + + if (!channels || !sample_rate) + return AVERROR_INVALIDDATA; + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->channels = channels; + st->codec->sample_rate = sample_rate; + + st->codec->codec_id = ff_codec_get_id(tags, tag); + if (st->codec->codec_id == AV_CODEC_ID_NONE) { + av_log(s, AV_LOG_ERROR, "unknown tag %X\n", tag); + return AVERROR_INVALIDDATA; + } + + st->codec->bits_per_coded_sample = av_get_bits_per_sample(st->codec->codec_id); + st->codec->block_align = st->codec->bits_per_coded_sample * st->codec->channels / 8; + avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate); + avio_skip(s->pb, 1008); + + return 0; +} + +AVInputFormat ff_ircam_demuxer = { + .name = "ircam", + .long_name = NULL_IF_CONFIG_SMALL("Berkeley/IRCAM/CARL Sound Format"), + .read_probe = ircam_probe, + .read_header = ircam_read_header, + .read_packet = ff_pcm_read_packet, + .read_seek = ff_pcm_read_seek, + .extensions = "sf,ircam", + .flags = AVFMT_GENERIC_INDEX, +}; diff --git a/libavformat/ircamenc.c b/libavformat/ircamenc.c new file mode 100644 index 0000000000..38f15fbceb --- /dev/null +++ b/libavformat/ircamenc.c @@ -0,0 +1,62 @@ +/* + * IRCAM muxer + * Copyright (c) 2012 Paul B Mahol + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/intreadwrite.h" +#include "avformat.h" +#include "avio_internal.h" +#include "internal.h" +#include "rawenc.h" +#include "ircam.h" + +static int ircam_write_header(AVFormatContext *s) +{ + AVCodecContext *codec = s->streams[0]->codec; + uint32_t tag; + + if (s->nb_streams != 1) { + av_log(s, AV_LOG_ERROR, "only one stream is supported\n"); + return AVERROR(EINVAL); + } + + tag = ff_codec_get_tag(ff_codec_ircam_le_tags, codec->codec_id); + if (!tag) { + av_log(s, AV_LOG_ERROR, "unsupported codec\n"); + return AVERROR(EINVAL); + } + + avio_wl32(s->pb, 0x0001A364); + avio_wl32(s->pb, av_float2int(codec->sample_rate)); + avio_wl32(s->pb, codec->channels); + avio_wl32(s->pb, tag); + ffio_fill(s->pb, 0, 1008); + return 0; +} + +AVOutputFormat ff_ircam_muxer = { + .name = "ircam", + .extensions = "sf,ircam", + .long_name = NULL_IF_CONFIG_SMALL("Berkeley/IRCAM/CARL Sound Format"), + .audio_codec = AV_CODEC_ID_PCM_S16LE, + .video_codec = AV_CODEC_ID_NONE, + .write_header = ircam_write_header, + .write_packet = ff_raw_write_packet, + .codec_tag = (const AVCodecTag *const []){ ff_codec_ircam_le_tags, 0 }, +}; diff --git a/libavformat/isom.c b/libavformat/isom.c index 372e877ff0..de9d20ccab 100644 --- a/libavformat/isom.c +++ b/libavformat/isom.c @@ -4,20 +4,20 @@ * Copyright (c) 2002 Francois Revol <revol@free.fr> * Copyright (c) 2006 Baptiste Coudurier <baptiste.coudurier@free.fr> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -68,7 +68,6 @@ const AVCodecTag ff_codec_movvideo_tags[] = { { AV_CODEC_ID_RAWVIDEO, MKTAG('r', 'a', 'w', ' ') }, /* Uncompressed RGB */ { AV_CODEC_ID_RAWVIDEO, MKTAG('y', 'u', 'v', '2') }, /* Uncompressed YUV422 */ - { AV_CODEC_ID_RAWVIDEO, MKTAG('A', 'V', 'U', 'I') }, /* YUV with alpha-channel (AVID Uncompressed) */ { AV_CODEC_ID_RAWVIDEO, MKTAG('2', 'v', 'u', 'y') }, /* UNCOMPRESSED 8BIT 4:2:2 */ { AV_CODEC_ID_RAWVIDEO, MKTAG('y', 'u', 'v', 's') }, /* same as 2vuy but byte swapped */ @@ -81,13 +80,28 @@ const AVCodecTag ff_codec_movvideo_tags[] = { { AV_CODEC_ID_RAWVIDEO, MKTAG('A', 'B', 'G', 'R') }, { AV_CODEC_ID_RAWVIDEO, MKTAG('b', '1', '6', 'g') }, { AV_CODEC_ID_RAWVIDEO, MKTAG('b', '4', '8', 'r') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('b', 'x', 'b', 'g') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('b', 'x', 'r', 'g') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('b', 'x', 'y', 'v') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('N', 'O', '1', '6') }, { AV_CODEC_ID_RAWVIDEO, MKTAG('D', 'V', 'O', 'O') }, /* Digital Voodoo SD 8 Bit */ + { AV_CODEC_ID_RAWVIDEO, MKTAG('R', '4', '2', '0') }, /* Radius DV YUV PAL */ + { AV_CODEC_ID_RAWVIDEO, MKTAG('R', '4', '1', '1') }, /* Radius DV YUV NTSC */ { AV_CODEC_ID_R10K, MKTAG('R', '1', '0', 'k') }, /* UNCOMPRESSED 10BIT RGB */ { AV_CODEC_ID_R10K, MKTAG('R', '1', '0', 'g') }, /* UNCOMPRESSED 10BIT RGB */ { AV_CODEC_ID_R210, MKTAG('r', '2', '1', '0') }, /* UNCOMPRESSED 10BIT RGB */ + { AV_CODEC_ID_AVUI, MKTAG('A', 'V', 'U', 'I') }, /* AVID Uncompressed deinterleaved UYVY422 */ + { AV_CODEC_ID_AVRP, MKTAG('A', 'V', 'r', 'p') }, /* Avid 1:1 10-bit RGB Packer */ + { AV_CODEC_ID_AVRP, MKTAG('S', 'U', 'D', 'S') }, /* Avid DS Uncompressed */ { AV_CODEC_ID_V210, MKTAG('v', '2', '1', '0') }, /* UNCOMPRESSED 10BIT 4:2:2 */ + { AV_CODEC_ID_V210, MKTAG('b', 'x', 'y', '2') }, /* BOXX 10BIT 4:2:2 */ + { AV_CODEC_ID_V308, MKTAG('v', '3', '0', '8') }, /* UNCOMPRESSED 8BIT 4:4:4 */ + { AV_CODEC_ID_V408, MKTAG('v', '4', '0', '8') }, /* UNCOMPRESSED 8BIT 4:4:4:4 */ { AV_CODEC_ID_V410, MKTAG('v', '4', '1', '0') }, /* UNCOMPRESSED 10BIT 4:4:4 */ + { AV_CODEC_ID_Y41P, MKTAG('Y', '4', '1', 'P') }, /* UNCOMPRESSED 12BIT 4:1:1 */ + { AV_CODEC_ID_YUV4, MKTAG('y', 'u', 'v', '4') }, /* libquicktime packed yuv420p */ + { AV_CODEC_ID_TARGA_Y216, MKTAG('Y', '2', '1', '6') }, { AV_CODEC_ID_MJPEG, MKTAG('j', 'p', 'e', 'g') }, /* PhotoJPEG */ { AV_CODEC_ID_MJPEG, MKTAG('m', 'j', 'p', 'a') }, /* Motion-JPEG (format A) */ @@ -131,6 +145,7 @@ const AVCodecTag ff_codec_movvideo_tags[] = { { AV_CODEC_ID_8BPS, MKTAG('8', 'B', 'P', 'S') }, /* Planar RGB (8BPS) */ { AV_CODEC_ID_SMC, MKTAG('s', 'm', 'c', ' ') }, /* Apple Graphics (SMC) */ { AV_CODEC_ID_QTRLE, MKTAG('r', 'l', 'e', ' ') }, /* Apple Animation (RLE) */ + { AV_CODEC_ID_SGIRLE, MKTAG('r', 'l', 'e', '1') }, /* SGI RLE 8-bit */ { AV_CODEC_ID_MSRLE, MKTAG('W', 'R', 'L', 'E') }, { AV_CODEC_ID_QDRAW, MKTAG('q', 'd', 'r', 'w') }, /* QuickDraw */ @@ -152,9 +167,12 @@ const AVCodecTag ff_codec_movvideo_tags[] = { { AV_CODEC_ID_H264, MKTAG('a', 'i', '1', '3') }, /* AVC-Intra 100M 1080p24/30/60 */ { AV_CODEC_ID_H264, MKTAG('a', 'i', '1', '5') }, /* AVC-Intra 100M 1080i50 */ { AV_CODEC_ID_H264, MKTAG('a', 'i', '1', '6') }, /* AVC-Intra 100M 1080i60 */ + { AV_CODEC_ID_H264, MKTAG('a', 'i', 'v', 'x') }, /* XAVC 4:2:2 10bit */ + { AV_CODEC_ID_H264, MKTAG('A', 'V', 'i', 'n') }, /* AVC-Intra with implicit SPS/PPS */ { AV_CODEC_ID_MPEG1VIDEO, MKTAG('m', '1', 'v', '1') }, /* Apple MPEG-1 Camcorder */ { AV_CODEC_ID_MPEG1VIDEO, MKTAG('m', 'p', 'e', 'g') }, /* MPEG */ + { AV_CODEC_ID_MPEG1VIDEO, MKTAG('m', '1', 'v', ' ') }, { AV_CODEC_ID_MPEG2VIDEO, MKTAG('m', '2', 'v', '1') }, /* Apple MPEG-2 Camcorder */ { AV_CODEC_ID_MPEG2VIDEO, MKTAG('h', 'd', 'v', '1') }, /* MPEG2 HDV 720p30 */ { AV_CODEC_ID_MPEG2VIDEO, MKTAG('h', 'd', 'v', '2') }, /* MPEG2 HDV 1080i60 */ @@ -172,6 +190,7 @@ const AVCodecTag ff_codec_movvideo_tags[] = { { AV_CODEC_ID_MPEG2VIDEO, MKTAG('m', 'x', '4', 'p') }, /* MPEG2 IMX PAL 625/50 40mb/s produced by FCP */ { AV_CODEC_ID_MPEG2VIDEO, MKTAG('m', 'x', '3', 'n') }, /* MPEG2 IMX NTSC 525/60 30mb/s produced by FCP */ { AV_CODEC_ID_MPEG2VIDEO, MKTAG('m', 'x', '3', 'p') }, /* MPEG2 IMX PAL 625/50 30mb/s produced by FCP */ + { AV_CODEC_ID_MPEG2VIDEO, MKTAG('x', 'd', '5', '1') }, /* XDCAM HD422 720p30 CBR */ { AV_CODEC_ID_MPEG2VIDEO, MKTAG('x', 'd', '5', '4') }, /* XDCAM HD422 720p24 CBR */ { AV_CODEC_ID_MPEG2VIDEO, MKTAG('x', 'd', '5', '5') }, /* XDCAM HD422 720p25 CBR */ { AV_CODEC_ID_MPEG2VIDEO, MKTAG('x', 'd', '5', '9') }, /* XDCAM HD422 720p60 CBR */ @@ -206,6 +225,7 @@ const AVCodecTag ff_codec_movvideo_tags[] = { { AV_CODEC_ID_TIFF, MKTAG('t', 'i', 'f', 'f') }, /* TIFF embedded in MOV */ { AV_CODEC_ID_GIF, MKTAG('g', 'i', 'f', ' ') }, /* embedded gif files as frames (usually one "click to play movie" frame) */ { AV_CODEC_ID_PNG, MKTAG('p', 'n', 'g', ' ') }, + { AV_CODEC_ID_PNG, MKTAG('M', 'N', 'G', ' ') }, { AV_CODEC_ID_VC1, MKTAG('v', 'c', '-', '1') }, /* SMPTE RP 2025 */ { AV_CODEC_ID_CAVS, MKTAG('a', 'v', 's', '2') }, @@ -218,12 +238,14 @@ const AVCodecTag ff_codec_movvideo_tags[] = { { AV_CODEC_ID_RAWVIDEO, MKTAG('A', 'V', 'u', 'p') }, { AV_CODEC_ID_SGI, MKTAG('s', 'g', 'i', ' ') }, /* SGI */ { AV_CODEC_ID_DPX, MKTAG('d', 'p', 'x', ' ') }, /* DPX */ + { AV_CODEC_ID_EXR, MKTAG('e', 'x', 'r', ' ') }, /* OpenEXR */ { AV_CODEC_ID_PRORES, MKTAG('a', 'p', 'c', 'h') }, /* Apple ProRes 422 High Quality */ { AV_CODEC_ID_PRORES, MKTAG('a', 'p', 'c', 'n') }, /* Apple ProRes 422 Standard Definition */ { AV_CODEC_ID_PRORES, MKTAG('a', 'p', 'c', 's') }, /* Apple ProRes 422 LT */ { AV_CODEC_ID_PRORES, MKTAG('a', 'p', 'c', 'o') }, /* Apple ProRes 422 Proxy */ { AV_CODEC_ID_PRORES, MKTAG('a', 'p', '4', 'h') }, /* Apple ProRes 4444 */ + { AV_CODEC_ID_FLIC, MKTAG('f', 'l', 'i', 'c') }, { AV_CODEC_ID_AIC, MKTAG('i', 'c', 'o', 'd') }, @@ -262,6 +284,7 @@ const AVCodecTag ff_codec_movaudio_tags[] = { { AV_CODEC_ID_PCM_MULAW, MKTAG('u', 'l', 'a', 'w') }, { AV_CODEC_ID_PCM_S16BE, MKTAG('t', 'w', 'o', 's') }, { AV_CODEC_ID_PCM_S16LE, MKTAG('s', 'o', 'w', 't') }, + { AV_CODEC_ID_PCM_S16BE, MKTAG('l', 'p', 'c', 'm') }, { AV_CODEC_ID_PCM_S16LE, MKTAG('l', 'p', 'c', 'm') }, { AV_CODEC_ID_PCM_S24BE, MKTAG('i', 'n', '2', '4') }, { AV_CODEC_ID_PCM_S24LE, MKTAG('i', 'n', '2', '4') }, @@ -276,13 +299,17 @@ const AVCodecTag ff_codec_movaudio_tags[] = { { AV_CODEC_ID_QDM2, MKTAG('Q', 'D', 'M', '2') }, { AV_CODEC_ID_QDMC, MKTAG('Q', 'D', 'M', 'C') }, { AV_CODEC_ID_SPEEX, MKTAG('s', 'p', 'e', 'x') }, /* Flash Media Server */ + { AV_CODEC_ID_SPEEX, MKTAG('S', 'P', 'X', 'N') }, { AV_CODEC_ID_WMAV2, MKTAG('W', 'M', 'A', '2') }, + { AV_CODEC_ID_EVRC, MKTAG('s', 'e', 'v', 'c') }, /* 3GPP2 */ + { AV_CODEC_ID_SMV, MKTAG('s', 's', 'm', 'v') }, /* 3GPP2 */ { AV_CODEC_ID_NONE, 0 }, }; const AVCodecTag ff_codec_movsubtitle_tags[] = { { AV_CODEC_ID_MOV_TEXT, MKTAG('t', 'e', 'x', 't') }, { AV_CODEC_ID_MOV_TEXT, MKTAG('t', 'x', '3', 'g') }, + { AV_CODEC_ID_EIA_608, MKTAG('c', '6', '0', '8') }, { AV_CODEC_ID_NONE, 0 }, }; @@ -345,7 +372,7 @@ int ff_mov_lang_to_iso639(unsigned code, char to[4]) memset(to, 0, 4); /* is it the mangled iso code? */ /* see http://www.geocities.com/xhelmboyx/quicktime/formats/mp4-layout.txt */ - if (code > 138) { + if (code >= 0x400 && code != 0x7fff) { for (i = 2; i >= 0; i--) { to[i] = 0x60 + (code & 0x1f); code >>= 5; @@ -417,6 +444,11 @@ int ff_mp4_read_dec_config_descr(AVFormatContext *fc, AVStream *st, AVIOContext avio_rb32(pb); /* max bitrate */ avio_rb32(pb); /* avg bitrate */ + if(avcodec_is_open(st->codec)) { + av_log(fc, AV_LOG_DEBUG, "codec open in read_dec_config_descr\n"); + return -1; + } + st->codec->codec_id= ff_codec_get_id(ff_mp4_obj_type, object_type_id); av_dlog(fc, "esds object type id 0x%02x\n", object_type_id); len = ff_mp4_read_descr(fc, pb, &tag); @@ -425,11 +457,9 @@ int ff_mp4_read_dec_config_descr(AVFormatContext *fc, AVStream *st, AVIOContext if (!len || (uint64_t)len > (1<<30)) return -1; av_free(st->codec->extradata); - st->codec->extradata = av_mallocz(len + FF_INPUT_BUFFER_PADDING_SIZE); - if (!st->codec->extradata) + if (ff_alloc_extradata(st->codec, len)) return AVERROR(ENOMEM); avio_read(pb, st->codec->extradata, len); - st->codec->extradata_size = len; if (st->codec->codec_id == AV_CODEC_ID_AAC) { MPEG4AudioConfig cfg; avpriv_mpeg4audio_get_config(&cfg, st->codec->extradata, @@ -452,3 +482,95 @@ int ff_mp4_read_dec_config_descr(AVFormatContext *fc, AVStream *st, AVIOContext } return 0; } + +typedef struct MovChannelLayout { + int64_t channel_layout; + uint32_t layout_tag; +} MovChannelLayout; + +static const MovChannelLayout mov_channel_layout[] = { + { AV_CH_LAYOUT_MONO, (100<<16) | 1}, // kCAFChannelLayoutTag_Mono + { AV_CH_LAYOUT_STEREO, (101<<16) | 2}, // kCAFChannelLayoutTag_Stereo + { AV_CH_LAYOUT_STEREO, (102<<16) | 2}, // kCAFChannelLayoutTag_StereoHeadphones + { AV_CH_LAYOUT_2_1, (131<<16) | 3}, // kCAFChannelLayoutTag_ITU_2_1 + { AV_CH_LAYOUT_QUAD, (132<<16) | 4}, // kCAFChannelLayoutTag_ITU_2_2 + { AV_CH_LAYOUT_2_2, (132<<16) | 4}, // kCAFChannelLayoutTag_ITU_2_2 + { AV_CH_LAYOUT_QUAD, (108<<16) | 4}, // kCAFChannelLayoutTag_Quadraphonic + { AV_CH_LAYOUT_SURROUND, (113<<16) | 3}, // kCAFChannelLayoutTag_MPEG_3_0_A + { AV_CH_LAYOUT_4POINT0, (115<<16) | 4}, // kCAFChannelLayoutTag_MPEG_4_0_A + { AV_CH_LAYOUT_5POINT0_BACK, (117<<16) | 5}, // kCAFChannelLayoutTag_MPEG_5_0_A + { AV_CH_LAYOUT_5POINT0, (117<<16) | 5}, // kCAFChannelLayoutTag_MPEG_5_0_A + { AV_CH_LAYOUT_5POINT1_BACK, (121<<16) | 6}, // kCAFChannelLayoutTag_MPEG_5_1_A + { AV_CH_LAYOUT_5POINT1, (121<<16) | 6}, // kCAFChannelLayoutTag_MPEG_5_1_A + { AV_CH_LAYOUT_7POINT1, (128<<16) | 8}, // kCAFChannelLayoutTag_MPEG_7_1_C + { AV_CH_LAYOUT_7POINT1_WIDE, (126<<16) | 8}, // kCAFChannelLayoutTag_MPEG_7_1_A + { AV_CH_LAYOUT_5POINT1_BACK|AV_CH_LAYOUT_STEREO_DOWNMIX, (130<<16) | 8}, // kCAFChannelLayoutTag_SMPTE_DTV + { AV_CH_LAYOUT_STEREO|AV_CH_LOW_FREQUENCY, (133<<16) | 3}, // kCAFChannelLayoutTag_DVD_4 + { AV_CH_LAYOUT_2_1|AV_CH_LOW_FREQUENCY, (134<<16) | 4}, // kCAFChannelLayoutTag_DVD_5 + { AV_CH_LAYOUT_QUAD|AV_CH_LOW_FREQUENCY, (135<<16) | 4}, // kCAFChannelLayoutTag_DVD_6 + { AV_CH_LAYOUT_2_2|AV_CH_LOW_FREQUENCY, (135<<16) | 4}, // kCAFChannelLayoutTag_DVD_6 + { AV_CH_LAYOUT_SURROUND|AV_CH_LOW_FREQUENCY, (136<<16) | 4}, // kCAFChannelLayoutTag_DVD_10 + { AV_CH_LAYOUT_4POINT0|AV_CH_LOW_FREQUENCY, (137<<16) | 5}, // kCAFChannelLayoutTag_DVD_11 + { 0, 0}, +}; +#if 0 +int ff_mov_read_chan(AVFormatContext *s, AVStream *st, int64_t size) +{ + AVCodecContext *codec= st->codec; + uint32_t layout_tag; + AVIOContext *pb = s->pb; + const MovChannelLayout *layouts = mov_channel_layout; + + if (size < 12) + return AVERROR_INVALIDDATA; + + layout_tag = avio_rb32(pb); + size -= 4; + if (layout_tag == 0) { // kCAFChannelLayoutTag_UseChannelDescriptions + // Channel descriptions not implemented + av_log_ask_for_sample(s, "Unimplemented container channel layout.\n"); + avio_skip(pb, size); + return 0; + } + if (layout_tag == 0x10000) { // kCAFChannelLayoutTag_UseChannelBitmap + codec->channel_layout = avio_rb32(pb); + size -= 4; + avio_skip(pb, size); + return 0; + } + while (layouts->channel_layout) { + if (layout_tag == layouts->layout_tag) { + codec->channel_layout = layouts->channel_layout; + break; + } + layouts++; + } + if (!codec->channel_layout) + av_log(s, AV_LOG_WARNING, "Unknown container channel layout.\n"); + avio_skip(pb, size); + + return 0; +} +#endif + +void ff_mov_write_chan(AVIOContext *pb, int64_t channel_layout) +{ + const MovChannelLayout *layouts; + uint32_t layout_tag = 0; + + for (layouts = mov_channel_layout; layouts->channel_layout; layouts++) + if (channel_layout == layouts->channel_layout) { + layout_tag = layouts->layout_tag; + break; + } + + if (layout_tag) { + avio_wb32(pb, layout_tag); // mChannelLayoutTag + avio_wb32(pb, 0); // mChannelBitmap + } else { + avio_wb32(pb, 0x10000); // kCAFChannelLayoutTag_UseChannelBitmap + avio_wb32(pb, channel_layout); + } + avio_wb32(pb, 0); // mNumberChannelDescriptions +} + diff --git a/libavformat/isom.h b/libavformat/isom.h index 94f1296a1d..828e5005ad 100644 --- a/libavformat/isom.h +++ b/libavformat/isom.h @@ -4,20 +4,20 @@ * copyright (c) 2002 Francois Revol <revol@free.fr> * copyright (c) 2006 Baptiste Coudurier <baptiste.coudurier@free.fr> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -94,6 +94,7 @@ typedef struct MOVSbgp { typedef struct MOVStreamContext { AVIOContext *pb; + int pb_is_copied; int ffindex; ///< AVStream index int next_chunk; unsigned int chunk_count; @@ -108,14 +109,17 @@ typedef struct MOVStreamContext { unsigned *stps_data; ///< partial sync sample for mpeg-2 open gop int ctts_index; int ctts_sample; - unsigned int sample_size; + unsigned int sample_size; ///< may contain value calculated from stsd or value from stsz atom + unsigned int stsz_sample_size; ///< always contains sample size from stsz atom unsigned int sample_count; int *sample_sizes; int keyframe_absent; unsigned int keyframe_count; int *keyframes; int time_scale; - int64_t time_offset; ///< time offset of the first edit list entry + int64_t empty_duration; ///< empty duration of the first edit list entry + int64_t start_time; ///< start time of the media + int64_t time_offset; ///< time offset of the edit list entries int current_sample; unsigned int bytes_per_frame; unsigned int samples_per_frame; @@ -125,6 +129,7 @@ typedef struct MOVStreamContext { unsigned drefs_count; MOVDref *drefs; int dref_id; + int timecode_track; int wrong_dts; ///< dts are wrong due to huge ctts offset (iMovie files) int width; ///< tkhd width int height; ///< tkhd height @@ -132,12 +137,15 @@ typedef struct MOVStreamContext { uint32_t palette[256]; int has_palette; int64_t data_size; + uint32_t tmcd_flags; ///< tmcd track flags int64_t track_end; ///< used for dts generation in fragmented movie files + int start_pad; ///< amount of samples to skip due to enc-dec delay unsigned int rap_group_count; MOVSbgp *rap_group; } MOVStreamContext; typedef struct MOVContext { + AVClass *avclass; AVFormatContext *fc; int time_scale; int64_t duration; ///< duration of the longest track @@ -151,7 +159,11 @@ typedef struct MOVContext { unsigned trex_count; int itunes_metadata; ///< metadata are itunes style int chapter_track; + int use_absolute_path; + int ignore_editlist; int64_t next_root_atom; ///< offset of the next root atom + int *bitrates; ///< bitrates read before streams creation + int bitrates_count; } MOVContext; int ff_mp4_read_descr_len(AVIOContext *pb); @@ -199,5 +211,6 @@ int ff_mov_read_esds(AVFormatContext *fc, AVIOContext *pb, MOVAtom atom); enum AVCodecID ff_mov_get_lpcm_codec_id(int bps, int flags); int ff_mov_read_stsd_entries(MOVContext *c, AVIOContext *pb, int entries); +void ff_mov_write_chan(AVIOContext *pb, int64_t channel_layout); #endif /* AVFORMAT_ISOM_H */ diff --git a/libavformat/iss.c b/libavformat/iss.c index 3f7f4feff2..e4335b4cbf 100644 --- a/libavformat/iss.c +++ b/libavformat/iss.c @@ -2,20 +2,20 @@ * ISS (.iss) file demuxer * Copyright (c) 2008 Jaikrishnan Menon <realityman@gmx.net> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -88,6 +88,11 @@ static av_cold int iss_read_header(AVFormatContext *s) get_token(pb, token, sizeof(token)); //Version ID get_token(pb, token, sizeof(token)); //Size + if (iss->packet_size <= 0) { + av_log(s, AV_LOG_ERROR, "packet_size %d is invalid\n", iss->packet_size); + return AVERROR_INVALIDDATA; + } + iss->sample_start_pos = avio_tell(pb); st = avformat_new_stream(s, NULL); diff --git a/libavformat/iv8.c b/libavformat/iv8.c index 56909e3a22..38b79609f6 100644 --- a/libavformat/iv8.c +++ b/libavformat/iv8.c @@ -1,20 +1,20 @@ /* * Copyright (c) 2009 Michael Niedermayer * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/ivfdec.c b/libavformat/ivfdec.c index b3555f42a4..c0fdafbf02 100644 --- a/libavformat/ivfdec.c +++ b/libavformat/ivfdec.c @@ -1,20 +1,20 @@ /* * Copyright (c) 2010 David Conrad * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/ivfenc.c b/libavformat/ivfenc.c index 3cd16166c7..45bae22097 100644 --- a/libavformat/ivfenc.c +++ b/libavformat/ivfenc.c @@ -1,20 +1,20 @@ /* * Copyright (c) 2010 Reimar Döffinger * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "avformat.h" diff --git a/libavformat/jacosubdec.c b/libavformat/jacosubdec.c new file mode 100644 index 0000000000..e2cbaad83e --- /dev/null +++ b/libavformat/jacosubdec.c @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2012 Clément BÅ“sch + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * JACOsub subtitle demuxer + * @see http://unicorn.us.com/jacosub/jscripts.html + * @todo Support P[ALETTE] directive. + */ + +#include "avformat.h" +#include "internal.h" +#include "subtitles.h" +#include "libavcodec/internal.h" +#include "libavcodec/jacosub.h" +#include "libavutil/avstring.h" +#include "libavutil/bprint.h" +#include "libavutil/intreadwrite.h" + +typedef struct { + int shift; + unsigned timeres; + FFDemuxSubtitlesQueue q; +} JACOsubContext; + +static int timed_line(const char *ptr) +{ + char c; + return (sscanf(ptr, "%*u:%*u:%*u.%*u %*u:%*u:%*u.%*u %c", &c) == 1 || + sscanf(ptr, "@%*u @%*u %c", &c) == 1); +} + +static int jacosub_probe(AVProbeData *p) +{ + const char *ptr = p->buf; + const char *ptr_end = p->buf + p->buf_size; + + if (AV_RB24(ptr) == 0xEFBBBF) + ptr += 3; /* skip UTF-8 BOM */ + + while (ptr < ptr_end) { + while (jss_whitespace(*ptr)) + ptr++; + if (*ptr != '#' && *ptr != '\n') { + if (timed_line(ptr)) + return AVPROBE_SCORE_EXTENSION + 1; + return 0; + } + ptr += ff_subtitles_next_line(ptr); + } + return 0; +} + +static const char * const cmds[] = { + "CLOCKPAUSE", + "DIRECTIVE", + "FONT", + "HRES", + "INCLUDE", + "PALETTE", + "QUANTIZE", + "RAMP", + "SHIFT", + "TIMERES", +}; + +static int get_jss_cmd(char k) +{ + int i; + + k = av_toupper(k); + for (i = 0; i < FF_ARRAY_ELEMS(cmds); i++) + if (k == cmds[i][0]) + return i; + return -1; +} + +static int jacosub_read_close(AVFormatContext *s) +{ + JACOsubContext *jacosub = s->priv_data; + ff_subtitles_queue_clean(&jacosub->q); + return 0; +} + +static const char *read_ts(JACOsubContext *jacosub, const char *buf, + int64_t *start, int *duration) +{ + int len; + unsigned hs, ms, ss, fs; // hours, minutes, seconds, frame start + unsigned he, me, se, fe; // hours, minutes, seconds, frame end + int ts_start, ts_end; + + /* timed format */ + if (sscanf(buf, "%u:%u:%u.%u %u:%u:%u.%u %n", + &hs, &ms, &ss, &fs, + &he, &me, &se, &fe, &len) == 8) { + ts_start = (hs*3600 + ms*60 + ss) * jacosub->timeres + fs; + ts_end = (he*3600 + me*60 + se) * jacosub->timeres + fe; + goto shift_and_ret; + } + + /* timestamps format */ + if (sscanf(buf, "@%u @%u %n", &ts_start, &ts_end, &len) == 2) + goto shift_and_ret; + + return NULL; + +shift_and_ret: + ts_start = (ts_start + jacosub->shift) * 100 / jacosub->timeres; + ts_end = (ts_end + jacosub->shift) * 100 / jacosub->timeres; + *start = ts_start; + *duration = ts_start + ts_end; + return buf + len; +} + +static int get_shift(int timeres, const char *buf) +{ + int sign = 1; + int a = 0, b = 0, c = 0, d = 0; +#define SSEP "%*1[.:]" + int n = sscanf(buf, "%d"SSEP"%d"SSEP"%d"SSEP"%d", &a, &b, &c, &d); +#undef SSEP + + if (*buf == '-' || a < 0) { + sign = -1; + a = FFABS(a); + } + + switch (n) { + case 4: return sign * ((a*3600 + b*60 + c) * timeres + d); + case 3: return sign * (( a*60 + b) * timeres + c); + case 2: return sign * (( a) * timeres + b); + } + + return 0; +} + +static int jacosub_read_header(AVFormatContext *s) +{ + AVBPrint header; + AVIOContext *pb = s->pb; + char line[JSS_MAX_LINESIZE]; + JACOsubContext *jacosub = s->priv_data; + int shift_set = 0; // only the first shift matters + int merge_line = 0; + int i, ret; + + AVStream *st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + avpriv_set_pts_info(st, 64, 1, 100); + st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE; + st->codec->codec_id = AV_CODEC_ID_JACOSUB; + + jacosub->timeres = 30; + + av_bprint_init(&header, 1024+FF_INPUT_BUFFER_PADDING_SIZE, 4096); + + while (!url_feof(pb)) { + int cmd_len; + const char *p = line; + int64_t pos = avio_tell(pb); + int len = ff_get_line(pb, line, sizeof(line)); + + p = jss_skip_whitespace(p); + + /* queue timed line */ + if (merge_line || timed_line(p)) { + AVPacket *sub; + + sub = ff_subtitles_queue_insert(&jacosub->q, line, len, merge_line); + if (!sub) + return AVERROR(ENOMEM); + sub->pos = pos; + merge_line = len > 1 && !strcmp(&line[len - 2], "\\\n"); + continue; + } + + /* skip all non-compiler commands and focus on the command */ + if (*p != '#') + continue; + p++; + i = get_jss_cmd(p[0]); + if (i == -1) + continue; + + /* trim command + spaces */ + cmd_len = strlen(cmds[i]); + if (av_strncasecmp(p, cmds[i], cmd_len) == 0) + p += cmd_len; + else + p++; + p = jss_skip_whitespace(p); + + /* handle commands which affect the whole script */ + switch (cmds[i][0]) { + case 'S': // SHIFT command affect the whole script... + if (!shift_set) { + jacosub->shift = get_shift(jacosub->timeres, p); + shift_set = 1; + } + av_bprintf(&header, "#S %s", p); + break; + case 'T': // ...but must be placed after TIMERES + jacosub->timeres = strtol(p, NULL, 10); + if (!jacosub->timeres) + jacosub->timeres = 30; + else + av_bprintf(&header, "#T %s", p); + break; + } + } + + /* general/essential directives in the extradata */ + ret = avpriv_bprint_to_extradata(st->codec, &header); + if (ret < 0) + return ret; + + /* SHIFT and TIMERES affect the whole script so packet timing can only be + * done in a second pass */ + for (i = 0; i < jacosub->q.nb_subs; i++) { + AVPacket *sub = &jacosub->q.subs[i]; + read_ts(jacosub, sub->data, &sub->pts, &sub->duration); + } + ff_subtitles_queue_finalize(&jacosub->q); + + return 0; +} + +static int jacosub_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + JACOsubContext *jacosub = s->priv_data; + return ff_subtitles_queue_read_packet(&jacosub->q, pkt); +} + +static int jacosub_read_seek(AVFormatContext *s, int stream_index, + int64_t min_ts, int64_t ts, int64_t max_ts, int flags) +{ + JACOsubContext *jacosub = s->priv_data; + return ff_subtitles_queue_seek(&jacosub->q, s, stream_index, + min_ts, ts, max_ts, flags); +} + +AVInputFormat ff_jacosub_demuxer = { + .name = "jacosub", + .long_name = NULL_IF_CONFIG_SMALL("JACOsub subtitle format"), + .priv_data_size = sizeof(JACOsubContext), + .read_probe = jacosub_probe, + .read_header = jacosub_read_header, + .read_packet = jacosub_read_packet, + .read_seek2 = jacosub_read_seek, + .read_close = jacosub_read_close, +}; diff --git a/libavformat/jacosubenc.c b/libavformat/jacosubenc.c new file mode 100644 index 0000000000..a11e45a4aa --- /dev/null +++ b/libavformat/jacosubenc.c @@ -0,0 +1,42 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "avformat.h" +#include "rawenc.h" + +static int jacosub_write_header(AVFormatContext *s) +{ + const AVCodecContext *avctx = s->streams[0]->codec; + + if (avctx->extradata_size) { + avio_write(s->pb, avctx->extradata, avctx->extradata_size - 1); + avio_flush(s->pb); + } + return 0; +} + +AVOutputFormat ff_jacosub_muxer = { + .name = "jacosub", + .long_name = NULL_IF_CONFIG_SMALL("JACOsub subtitle format"), + .mime_type = "text/x-jacosub", + .extensions = "jss,js", + .write_header = jacosub_write_header, + .write_packet = ff_raw_write_packet, + .flags = AVFMT_TS_NONSTRICT, + .subtitle_codec = AV_CODEC_ID_JACOSUB, +}; diff --git a/libavformat/jvdec.c b/libavformat/jvdec.c index cf9df8c977..69ac8f27fd 100644 --- a/libavformat/jvdec.c +++ b/libavformat/jvdec.c @@ -2,20 +2,20 @@ * Bitmap Brothers JV demuxer * Copyright (c) 2005, 2011 Peter Ross <pross@xvid.org> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -33,10 +33,10 @@ #define JV_PREAMBLE_SIZE 5 typedef struct { - int audio_size; /** audio packet size (bytes) */ - int video_size; /** video packet size (bytes) */ - int palette_size; /** palette size (bytes) */ - int video_type; /** per-frame video compression type */ + int audio_size; /**< audio packet size (bytes) */ + int video_size; /**< video packet size (bytes) */ + int palette_size; /**< palette size (bytes) */ + int video_type; /**< per-frame video compression type */ } JVFrame; typedef struct { @@ -53,8 +53,8 @@ typedef struct { static int read_probe(AVProbeData *pd) { - if (pd->buf[0] == 'J' && pd->buf[1] == 'V' && - !memcmp(pd->buf + 4, MAGIC, FFMIN(strlen(MAGIC), pd->buf_size - 4))) + if (pd->buf[0] == 'J' && pd->buf[1] == 'V' && strlen(MAGIC) <= pd->buf_size - 4 && + !memcmp(pd->buf + 4, MAGIC, strlen(MAGIC))) return AVPROBE_SCORE_MAX; return 0; } @@ -142,7 +142,7 @@ static int read_packet(AVFormatContext *s, AVPacket *pkt) AVIOContext *pb = s->pb; AVStream *ast = s->streams[0]; - while (!s->pb->eof_reached && jv->pts < ast->nb_index_entries) { + while (!url_feof(s->pb) && jv->pts < ast->nb_index_entries) { const AVIndexEntry *e = ast->index_entries + jv->pts; const JVFrame *jvf = jv->frames + jv->pts; @@ -166,7 +166,7 @@ static int read_packet(AVFormatContext *s, AVPacket *pkt) AV_WL32(pkt->data, jvf->video_size); pkt->data[4] = jvf->video_type; - if (avio_read(pb, pkt->data + JV_PREAMBLE_SIZE, size) < 0) + if ((size = avio_read(pb, pkt->data + JV_PREAMBLE_SIZE, size)) < 0) return AVERROR(EIO); pkt->size = size + JV_PREAMBLE_SIZE; @@ -218,6 +218,15 @@ static int read_seek(AVFormatContext *s, int stream_index, return 0; } +static int read_close(AVFormatContext *s) +{ + JVDemuxContext *jv = s->priv_data; + + av_freep(&jv->frames); + + return 0; +} + AVInputFormat ff_jv_demuxer = { .name = "jv", .long_name = NULL_IF_CONFIG_SMALL("Bitmap Brothers JV"), @@ -226,4 +235,5 @@ AVInputFormat ff_jv_demuxer = { .read_header = read_header, .read_packet = read_packet, .read_seek = read_seek, + .read_close = read_close, }; diff --git a/libavformat/latmenc.c b/libavformat/latmenc.c index 7270292f7a..e69de2b122 100644 --- a/libavformat/latmenc.c +++ b/libavformat/latmenc.c @@ -2,20 +2,20 @@ * LATM/LOAS muxer * Copyright (c) 2011 Kieran Kunhya <kieran@kunhya.com> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -25,6 +25,9 @@ #include "libavcodec/mpeg4audio.h" #include "libavutil/opt.h" #include "avformat.h" +#include "rawenc.h" + +#define MAX_EXTRADATA_SIZE 1024 typedef struct { AVClass *av_class; @@ -33,6 +36,7 @@ typedef struct { int object_type; int counter; int mod; + uint8_t buffer[0x1fff + MAX_EXTRADATA_SIZE + 1024]; } LATMContext; static const AVOption options[] = { @@ -50,15 +54,21 @@ static const AVClass latm_muxer_class = { static int latm_decode_extradata(LATMContext *ctx, uint8_t *buf, int size) { - GetBitContext gb; MPEG4AudioConfig m4ac; - init_get_bits(&gb, buf, size * 8); + if (size > MAX_EXTRADATA_SIZE) { + av_log(ctx, AV_LOG_ERROR, "Extradata is larger than currently supported.\n"); + return AVERROR_INVALIDDATA; + } ctx->off = avpriv_mpeg4audio_get_config(&m4ac, buf, size * 8, 1); if (ctx->off < 0) return ctx->off; - skip_bits_long(&gb, ctx->off); + if (ctx->object_type == AOT_ALS && (ctx->off & 7)) { + // as long as avpriv_mpeg4audio_get_config works correctly this is impossible + av_log(ctx, AV_LOG_ERROR, "BUG: ALS offset is not byte-aligned\n"); + return AVERROR_INVALIDDATA; + } /* FIXME: are any formats not allowed in LATM? */ if (m4ac.object_type > AOT_SBR && m4ac.object_type != AOT_ALS) { @@ -76,6 +86,9 @@ static int latm_write_header(AVFormatContext *s) LATMContext *ctx = s->priv_data; AVCodecContext *avctx = s->streams[0]->codec; + if (avctx->codec_id == AV_CODEC_ID_AAC_LATM) + return 0; + if (avctx->extradata_size > 0 && latm_decode_extradata(ctx, avctx->extradata, avctx->extradata_size) < 0) return AVERROR_INVALIDDATA; @@ -83,19 +96,16 @@ static int latm_write_header(AVFormatContext *s) return 0; } -static int latm_write_frame_header(AVFormatContext *s, PutBitContext *bs) +static void latm_write_frame_header(AVFormatContext *s, PutBitContext *bs) { LATMContext *ctx = s->priv_data; AVCodecContext *avctx = s->streams[0]->codec; - GetBitContext gb; int header_size; /* AudioMuxElement */ put_bits(bs, 1, !!ctx->counter); if (!ctx->counter) { - init_get_bits(&gb, avctx->extradata, avctx->extradata_size * 8); - /* StreamMuxConfig */ put_bits(bs, 1, 0); /* audioMuxVersion */ put_bits(bs, 1, 1); /* allStreamsSameTimeFraming */ @@ -105,12 +115,17 @@ static int latm_write_frame_header(AVFormatContext *s, PutBitContext *bs) /* AudioSpecificConfig */ if (ctx->object_type == AOT_ALS) { - header_size = avctx->extradata_size-(ctx->off + 7) >> 3; - avpriv_copy_bits(bs, &avctx->extradata[ctx->off], header_size); + header_size = avctx->extradata_size-(ctx->off >> 3); + avpriv_copy_bits(bs, &avctx->extradata[ctx->off >> 3], header_size); } else { + // + 3 assumes not scalable and dependsOnCoreCoder == 0, + // see decode_ga_specific_config in libavcodec/aacdec.c avpriv_copy_bits(bs, avctx->extradata, ctx->off + 3); if (!ctx->channel_conf) { + GetBitContext gb; + init_get_bits8(&gb, avctx->extradata, avctx->extradata_size); + skip_bits_long(&gb, ctx->off + 3); avpriv_copy_pce_data(bs, &gb); } } @@ -124,28 +139,36 @@ static int latm_write_frame_header(AVFormatContext *s, PutBitContext *bs) ctx->counter++; ctx->counter %= ctx->mod; - - return 0; } static int latm_write_packet(AVFormatContext *s, AVPacket *pkt) { + LATMContext *ctx = s->priv_data; AVIOContext *pb = s->pb; PutBitContext bs; int i, len; uint8_t loas_header[] = "\x56\xe0\x00"; - uint8_t *buf; + + if (s->streams[0]->codec->codec_id == AV_CODEC_ID_AAC_LATM) + return ff_raw_write_packet(s, pkt); if (pkt->size > 2 && pkt->data[0] == 0xff && (pkt->data[1] >> 4) == 0xf) { av_log(s, AV_LOG_ERROR, "ADTS header detected - ADTS will not be incorrectly muxed into LATM\n"); return AVERROR_INVALIDDATA; } - buf = av_malloc(pkt->size+1024); - if (!buf) - return AVERROR(ENOMEM); + if (!s->streams[0]->codec->extradata) { + if(pkt->size > 2 && pkt->data[0] == 0x56 && (pkt->data[1] >> 4) == 0xe && + (AV_RB16(pkt->data + 1) & 0x1FFF) + 3 == pkt->size) + return ff_raw_write_packet(s, pkt); + else + return AVERROR_INVALIDDATA; + } + + if (pkt->size > 0x1fff) + goto too_large; - init_put_bits(&bs, buf, pkt->size+1024); + init_put_bits(&bs, ctx->buffer, pkt->size+1024+MAX_EXTRADATA_SIZE); latm_write_frame_header(s, &bs); @@ -158,30 +181,46 @@ static int latm_write_packet(AVFormatContext *s, AVPacket *pkt) /* The LATM payload is written unaligned */ /* PayloadMux() */ - for (i = 0; i < pkt->size; i++) - put_bits(&bs, 8, pkt->data[i]); + if (pkt->size && (pkt->data[0] & 0xe1) == 0x81) { + // Convert byte-aligned DSE to non-aligned. + // Due to the input format encoding we know that + // it is naturally byte-aligned in the input stream, + // so there are no padding bits to account for. + // To avoid having to add padding bits and rearrange + // the whole stream we just remove the byte-align flag. + // This allows us to remux our FATE AAC samples into latm + // files that are still playable with minimal effort. + put_bits(&bs, 8, pkt->data[0] & 0xfe); + avpriv_copy_bits(&bs, pkt->data + 1, 8*pkt->size - 8); + } else + avpriv_copy_bits(&bs, pkt->data, 8*pkt->size); avpriv_align_put_bits(&bs); flush_put_bits(&bs); len = put_bits_count(&bs) >> 3; + if (len > 0x1fff) + goto too_large; + loas_header[1] |= (len >> 8) & 0x1f; loas_header[2] |= len & 0xff; avio_write(pb, loas_header, 3); - avio_write(pb, buf, len); - - av_free(buf); + avio_write(pb, ctx->buffer, len); return 0; + +too_large: + av_log(s, AV_LOG_ERROR, "LATM packet size larger than maximum size 0x1fff\n"); + return AVERROR_INVALIDDATA; } AVOutputFormat ff_latm_muxer = { .name = "latm", .long_name = NULL_IF_CONFIG_SMALL("LOAS/LATM"), .mime_type = "audio/MP4A-LATM", - .extensions = "latm", + .extensions = "latm,loas", .priv_data_size = sizeof(LATMContext), .audio_codec = AV_CODEC_ID_AAC, .video_codec = AV_CODEC_ID_NONE, diff --git a/libavformat/libavformat.v b/libavformat/libavformat.v index 6f11d600b9..0b476682ff 100644 --- a/libavformat/libavformat.v +++ b/libavformat/libavformat.v @@ -1,4 +1,37 @@ LIBAVFORMAT_$MAJOR { global: av*; + #FIXME those are for ffserver + ff_inet_aton; + ff_socket_nonblock; + ffm_set_write_index; + ffm_read_write_index; + ffm_write_write_index; + ff_mpegts_parse_close; + ff_mpegts_parse_open; + ff_mpegts_parse_packet; + ff_rtsp_parse_line; + ff_rtp_get_local_rtp_port; + ff_rtp_get_local_rtcp_port; + ffio_open_dyn_packet_buf; + ffio_set_buf_size; + ffurl_close; + ffurl_open; + ffurl_read_complete; + ffurl_seek; + ffurl_size; + ffurl_write; + ffurl_protocol_next; + url_open; + url_close; + url_write; + #those are deprecated, remove on next bump + url_*; + ff_timefilter_destroy; + ff_timefilter_new; + ff_timefilter_update; + ff_timefilter_reset; + get_*; + put_*; + ff_codec_get_id; local: *; }; diff --git a/libavformat/libgme.c b/libavformat/libgme.c new file mode 100644 index 0000000000..1ae63dc454 --- /dev/null +++ b/libavformat/libgme.c @@ -0,0 +1,201 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** +* @file +* libgme demuxer +*/ + +#include <gme/gme.h> +#include "libavutil/avstring.h" +#include "libavutil/eval.h" +#include "libavutil/opt.h" +#include "avformat.h" +#include "internal.h" + +typedef struct GMEContext { + const AVClass *class; + Music_Emu *music_emu; + gme_info_t *info; ///< selected track + + /* options */ + int track_index; + int sample_rate; + int64_t max_size; +} GMEContext; + +#define OFFSET(x) offsetof(GMEContext, x) +#define A AV_OPT_FLAG_AUDIO_PARAM +#define D AV_OPT_FLAG_DECODING_PARAM +static const AVOption options[] = { + {"track_index", "set track that should be played", OFFSET(track_index), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, A|D}, + {"sample_rate", "set sample rate", OFFSET(sample_rate), AV_OPT_TYPE_INT, {.i64 = 44100}, 1000, 999999, A|D}, + {"max_size", "set max file size supported (in bytes)", OFFSET(max_size), AV_OPT_TYPE_INT64, {.i64 = 50 * 1024 * 1024}, 0, SIZE_MAX, A|D}, + {NULL} +}; + +static void add_meta(AVFormatContext *s, const char *name, const char *value) +{ + if (value && value[0]) + av_dict_set(&s->metadata, name, value, 0); +} + +static int load_metadata(AVFormatContext *s) +{ + GMEContext *gme = s->priv_data; + gme_info_t *info = gme->info; + char buf[30]; + + add_meta(s, "system", info->system); + add_meta(s, "game", info->game); + add_meta(s, "song", info->song); + add_meta(s, "author", info->author); + add_meta(s, "copyright", info->copyright); + add_meta(s, "comment", info->comment); + add_meta(s, "dumper", info->dumper); + + snprintf(buf, sizeof(buf), "%d", (int)gme_track_count(gme->music_emu)); + add_meta(s, "tracks", buf); + + return 0; +} + +#define AUDIO_PKT_SIZE 512 + +static int read_header_gme(AVFormatContext *s) +{ + AVStream *st; + AVIOContext *pb = s->pb; + GMEContext *gme = s->priv_data; + int64_t sz = avio_size(pb); + char *buf; + char dummy; + + if (sz < 0) { + av_log(s, AV_LOG_WARNING, "Could not determine file size\n"); + sz = gme->max_size; + } else if (gme->max_size && sz > gme->max_size) { + sz = gme->max_size; + } + + buf = av_malloc(sz); + if (!buf) + return AVERROR(ENOMEM); + sz = avio_read(pb, buf, sz); + + // Data left means our buffer (the max_size option) is too small + if (avio_read(pb, &dummy, 1) == 1) { + av_log(s, AV_LOG_ERROR, "File size is larger than max_size option " + "value %"PRIi64", consider increasing the max_size option\n", + gme->max_size); + return AVERROR_BUFFER_TOO_SMALL; + } + + if (gme_open_data(buf, sz, &gme->music_emu, gme->sample_rate)) { + av_freep(&buf); + return AVERROR_INVALIDDATA; + } + av_freep(&buf); + + if (gme_track_info(gme->music_emu, &gme->info, gme->track_index)) + return AVERROR_STREAM_NOT_FOUND; + + if (gme_start_track(gme->music_emu, gme->track_index)) + return AVERROR_UNKNOWN; + + load_metadata(s); + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + avpriv_set_pts_info(st, 64, 1, 1000); + if (st->duration > 0) + st->duration = gme->info->length; + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_id = AV_NE(AV_CODEC_ID_PCM_S16BE, AV_CODEC_ID_PCM_S16LE); + st->codec->channels = 2; + st->codec->sample_rate = gme->sample_rate; + + return 0; +} + +static int read_packet_gme(AVFormatContext *s, AVPacket *pkt) +{ + GMEContext *gme = s->priv_data; + int n_samples = AUDIO_PKT_SIZE / 2; + int ret; + + if (gme_track_ended(gme->music_emu)) + return AVERROR_EOF; + + if ((ret = av_new_packet(pkt, AUDIO_PKT_SIZE)) < 0) + return ret; + + if (gme_play(gme->music_emu, n_samples, (short *)pkt->data)) + return AVERROR_EXTERNAL; + pkt->size = AUDIO_PKT_SIZE; + + return 0; +} + +static int read_close_gme(AVFormatContext *s) +{ + GMEContext *gme = s->priv_data; + gme_free_info(gme->info); + gme_delete(gme->music_emu); + return 0; +} + +static int read_seek_gme(AVFormatContext *s, int stream_idx, int64_t ts, int flags) +{ + GMEContext *gme = s->priv_data; + if (!gme_seek(gme->music_emu, (int)ts)) + return AVERROR_EXTERNAL; + return 0; +} + +static int probe_gme(AVProbeData *p) +{ + // Reads 4 bytes - returns "" if unknown format. + if (gme_identify_header(p->buf)[0]) { + if (p->buf_size < 16384) + return AVPROBE_SCORE_MAX / 4 + 1; + else + return AVPROBE_SCORE_MAX / 2; + } + return 0; +} + +static const AVClass class_gme = { + .class_name = "Game Music Emu demuxer", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + +AVInputFormat ff_libgme_demuxer = { + .name = "libgme", + .long_name = NULL_IF_CONFIG_SMALL("Game Music Emu demuxer"), + .priv_data_size = sizeof(GMEContext), + .read_probe = probe_gme, + .read_header = read_header_gme, + .read_packet = read_packet_gme, + .read_close = read_close_gme, + .read_seek = read_seek_gme, + .priv_class = &class_gme, +}; diff --git a/libavformat/libmodplug.c b/libavformat/libmodplug.c new file mode 100644 index 0000000000..836b7c2ec0 --- /dev/null +++ b/libavformat/libmodplug.c @@ -0,0 +1,381 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** +* @file +* ModPlug demuxer +* @todo better probing than extensions matching +*/ + +#include <libmodplug/modplug.h> +#include "libavutil/avstring.h" +#include "libavutil/eval.h" +#include "libavutil/opt.h" +#include "avformat.h" +#include "internal.h" + +typedef struct ModPlugContext { + const AVClass *class; + ModPlugFile *f; + uint8_t *buf; ///< input file content + + /* options */ + int noise_reduction; + int reverb_depth; + int reverb_delay; + int bass_amount; + int bass_range; + int surround_depth; + int surround_delay; + + int max_size; ///< max file size to allocate + + /* optional video stream */ + double ts_per_packet; ///< used to define the pts/dts using packet_count; + int packet_count; ///< total number of audio packets + int print_textinfo; ///< bool flag for printing speed, tempo, order, ... + int video_stream; ///< 1 if the user want a video stream, otherwise 0 + int w; ///< video stream width in char (one char = 8x8px) + int h; ///< video stream height in char (one char = 8x8px) + int video_switch; ///< 1 if current packet is video, otherwise 0 + int fsize; ///< constant frame size + int linesize; ///< line size in bytes + char *color_eval; ///< color eval user input expression + AVExpr *expr; ///< parsed color eval expression +} ModPlugContext; + +static const char *var_names[] = { + "x", "y", + "w", "h", + "t", + "speed", "tempo", "order", "pattern", "row", + NULL +}; + +enum var_name { + VAR_X, VAR_Y, + VAR_W, VAR_H, + VAR_TIME, + VAR_SPEED, VAR_TEMPO, VAR_ORDER, VAR_PATTERN, VAR_ROW, + VAR_VARS_NB +}; + +#define FF_MODPLUG_MAX_FILE_SIZE (100 * 1<<20) // 100M +#define FF_MODPLUG_DEF_FILE_SIZE ( 5 * 1<<20) // 5M + +#define OFFSET(x) offsetof(ModPlugContext, x) +#define D AV_OPT_FLAG_DECODING_PARAM +static const AVOption options[] = { + {"noise_reduction", "Enable noise reduction 0(off)-1(on)", OFFSET(noise_reduction), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, D}, + {"reverb_depth", "Reverb level 0(quiet)-100(loud)", OFFSET(reverb_depth), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 100, D}, + {"reverb_delay", "Reverb delay in ms, usually 40-200ms", OFFSET(reverb_delay), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, D}, + {"bass_amount", "XBass level 0(quiet)-100(loud)", OFFSET(bass_amount), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 100, D}, + {"bass_range", "XBass cutoff in Hz 10-100", OFFSET(bass_range), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 100, D}, + {"surround_depth", "Surround level 0(quiet)-100(heavy)", OFFSET(surround_depth), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 100, D}, + {"surround_delay", "Surround delay in ms, usually 5-40ms", OFFSET(surround_delay), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, D}, + {"max_size", "Max file size supported (in bytes). Default is 5MB. Set to 0 for no limit (not recommended)", + OFFSET(max_size), AV_OPT_TYPE_INT, {.i64 = FF_MODPLUG_DEF_FILE_SIZE}, 0, FF_MODPLUG_MAX_FILE_SIZE, D}, + {"video_stream_expr", "Color formula", OFFSET(color_eval), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, D}, + {"video_stream", "Make demuxer output a video stream", OFFSET(video_stream), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, D}, + {"video_stream_w", "Video stream width in char (one char = 8x8px)", OFFSET(w), AV_OPT_TYPE_INT, {.i64 = 30}, 20, 512, D}, + {"video_stream_h", "Video stream height in char (one char = 8x8px)", OFFSET(h), AV_OPT_TYPE_INT, {.i64 = 30}, 20, 512, D}, + {"video_stream_ptxt", "Print speed, tempo, order, ... in video stream", OFFSET(print_textinfo), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1, D}, + {NULL}, +}; + +#define SET_OPT_IF_REQUESTED(libopt, opt, flag) do { \ + if (modplug->opt) { \ + settings.libopt = modplug->opt; \ + settings.mFlags |= flag; \ + } \ +} while (0) + +#define ADD_META_MULTIPLE_ENTRIES(entry_name, fname) do { \ + if (n_## entry_name ##s) { \ + unsigned i, n = 0; \ + \ + for (i = 0; i < n_## entry_name ##s; i++) { \ + char item_name[64] = {0}; \ + fname(f, i, item_name); \ + if (!*item_name) \ + continue; \ + if (n) \ + av_dict_set(&s->metadata, #entry_name, "\n", AV_DICT_APPEND); \ + av_dict_set(&s->metadata, #entry_name, item_name, AV_DICT_APPEND); \ + n++; \ + } \ + \ + extra = av_asprintf(", %u/%u " #entry_name "%s", \ + n, n_## entry_name ##s, n > 1 ? "s" : ""); \ + if (!extra) \ + return AVERROR(ENOMEM); \ + av_dict_set(&s->metadata, "extra info", extra, AV_DICT_APPEND); \ + av_free(extra); \ + } \ +} while (0) + +static int modplug_load_metadata(AVFormatContext *s) +{ + ModPlugContext *modplug = s->priv_data; + ModPlugFile *f = modplug->f; + char *extra; + const char *name = ModPlug_GetName(f); + const char *msg = ModPlug_GetMessage(f); + + unsigned n_instruments = ModPlug_NumInstruments(f); + unsigned n_samples = ModPlug_NumSamples(f); + unsigned n_patterns = ModPlug_NumPatterns(f); + unsigned n_channels = ModPlug_NumChannels(f); + + if (name && *name) av_dict_set(&s->metadata, "name", name, 0); + if (msg && *msg) av_dict_set(&s->metadata, "message", msg, 0); + + extra = av_asprintf("%u pattern%s, %u channel%s", + n_patterns, n_patterns > 1 ? "s" : "", + n_channels, n_channels > 1 ? "s" : ""); + if (!extra) + return AVERROR(ENOMEM); + av_dict_set(&s->metadata, "extra info", extra, AV_DICT_DONT_STRDUP_VAL); + + ADD_META_MULTIPLE_ENTRIES(instrument, ModPlug_InstrumentName); + ADD_META_MULTIPLE_ENTRIES(sample, ModPlug_SampleName); + + return 0; +} + +#define AUDIO_PKT_SIZE 512 + +static int modplug_read_header(AVFormatContext *s) +{ + AVStream *st; + AVIOContext *pb = s->pb; + ModPlug_Settings settings; + ModPlugContext *modplug = s->priv_data; + int64_t sz = avio_size(pb); + + if (sz < 0) { + av_log(s, AV_LOG_WARNING, "Could not determine file size\n"); + sz = modplug->max_size; + } else if (modplug->max_size && sz > modplug->max_size) { + sz = modplug->max_size; + av_log(s, AV_LOG_WARNING, "Max file size reach%s, allocating %"PRIi64"B " + "but demuxing is likely to fail due to incomplete buffer\n", + sz == FF_MODPLUG_DEF_FILE_SIZE ? " (see -max_size)" : "", sz); + } + + if (modplug->color_eval) { + int r = av_expr_parse(&modplug->expr, modplug->color_eval, var_names, + NULL, NULL, NULL, NULL, 0, s); + if (r < 0) + return r; + } + + modplug->buf = av_malloc(modplug->max_size); + if (!modplug->buf) + return AVERROR(ENOMEM); + sz = avio_read(pb, modplug->buf, sz); + + ModPlug_GetSettings(&settings); + settings.mChannels = 2; + settings.mBits = 16; + settings.mFrequency = 44100; + settings.mResamplingMode = MODPLUG_RESAMPLE_FIR; // best quality + settings.mLoopCount = 0; // prevents looping forever + + if (modplug->noise_reduction) settings.mFlags |= MODPLUG_ENABLE_NOISE_REDUCTION; + SET_OPT_IF_REQUESTED(mReverbDepth, reverb_depth, MODPLUG_ENABLE_REVERB); + SET_OPT_IF_REQUESTED(mReverbDelay, reverb_delay, MODPLUG_ENABLE_REVERB); + SET_OPT_IF_REQUESTED(mBassAmount, bass_amount, MODPLUG_ENABLE_MEGABASS); + SET_OPT_IF_REQUESTED(mBassRange, bass_range, MODPLUG_ENABLE_MEGABASS); + SET_OPT_IF_REQUESTED(mSurroundDepth, surround_depth, MODPLUG_ENABLE_SURROUND); + SET_OPT_IF_REQUESTED(mSurroundDelay, surround_delay, MODPLUG_ENABLE_SURROUND); + + if (modplug->reverb_depth) settings.mReverbDepth = modplug->reverb_depth; + if (modplug->reverb_delay) settings.mReverbDelay = modplug->reverb_delay; + if (modplug->bass_amount) settings.mBassAmount = modplug->bass_amount; + if (modplug->bass_range) settings.mBassRange = modplug->bass_range; + if (modplug->surround_depth) settings.mSurroundDepth = modplug->surround_depth; + if (modplug->surround_delay) settings.mSurroundDelay = modplug->surround_delay; + + ModPlug_SetSettings(&settings); + + modplug->f = ModPlug_Load(modplug->buf, sz); + if (!modplug->f) + return AVERROR_INVALIDDATA; + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + avpriv_set_pts_info(st, 64, 1, 1000); + st->duration = ModPlug_GetLength(modplug->f); + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_id = AV_CODEC_ID_PCM_S16LE; + st->codec->channels = settings.mChannels; + st->codec->sample_rate = settings.mFrequency; + + // timebase = 1/1000, 2ch 16bits 44.1kHz-> 2*2*44100 + modplug->ts_per_packet = 1000*AUDIO_PKT_SIZE / (4*44100.); + + if (modplug->video_stream) { + AVStream *vst = avformat_new_stream(s, NULL); + if (!vst) + return AVERROR(ENOMEM); + avpriv_set_pts_info(vst, 64, 1, 1000); + vst->duration = st->duration; + vst->codec->codec_type = AVMEDIA_TYPE_VIDEO; + vst->codec->codec_id = AV_CODEC_ID_XBIN; + vst->codec->width = modplug->w << 3; + vst->codec->height = modplug->h << 3; + modplug->linesize = modplug->w * 3; + modplug->fsize = modplug->linesize * modplug->h; + } + + return modplug_load_metadata(s); +} + +static void write_text(uint8_t *dst, const char *s, int linesize, int x, int y) +{ + int i; + dst += y*linesize + x*3; + for (i = 0; s[i]; i++, dst += 3) { + dst[0] = 0x0; // count - 1 + dst[1] = s[i]; // char + dst[2] = 0x0f; // background / foreground + } +} + +#define PRINT_INFO(line, name, idvalue) do { \ + snprintf(intbuf, sizeof(intbuf), "%.0f", var_values[idvalue]); \ + write_text(pkt->data, name ":", modplug->linesize, 0+1, line+1); \ + write_text(pkt->data, intbuf, modplug->linesize, 10+1, line+1); \ +} while (0) + +static int modplug_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + ModPlugContext *modplug = s->priv_data; + + if (modplug->video_stream) { + modplug->video_switch ^= 1; // one video packet for one audio packet + if (modplug->video_switch) { + double var_values[VAR_VARS_NB]; + + var_values[VAR_W ] = modplug->w; + var_values[VAR_H ] = modplug->h; + var_values[VAR_TIME ] = modplug->packet_count * modplug->ts_per_packet; + var_values[VAR_SPEED ] = ModPlug_GetCurrentSpeed (modplug->f); + var_values[VAR_TEMPO ] = ModPlug_GetCurrentTempo (modplug->f); + var_values[VAR_ORDER ] = ModPlug_GetCurrentOrder (modplug->f); + var_values[VAR_PATTERN] = ModPlug_GetCurrentPattern(modplug->f); + var_values[VAR_ROW ] = ModPlug_GetCurrentRow (modplug->f); + + if (av_new_packet(pkt, modplug->fsize) < 0) + return AVERROR(ENOMEM); + pkt->stream_index = 1; + memset(pkt->data, 0, modplug->fsize); + + if (modplug->print_textinfo) { + char intbuf[32]; + PRINT_INFO(0, "speed", VAR_SPEED); + PRINT_INFO(1, "tempo", VAR_TEMPO); + PRINT_INFO(2, "order", VAR_ORDER); + PRINT_INFO(3, "pattern", VAR_PATTERN); + PRINT_INFO(4, "row", VAR_ROW); + PRINT_INFO(5, "ts", VAR_TIME); + } + + if (modplug->expr) { + int x, y; + for (y = 0; y < modplug->h; y++) { + for (x = 0; x < modplug->w; x++) { + double color; + var_values[VAR_X] = x; + var_values[VAR_Y] = y; + color = av_expr_eval(modplug->expr, var_values, NULL); + pkt->data[y*modplug->linesize + x*3 + 2] |= av_clip((int)color, 0, 0xf)<<4; + } + } + } + pkt->pts = pkt->dts = var_values[VAR_TIME]; + pkt->flags |= AV_PKT_FLAG_KEY; + return 0; + } + } + + if (av_new_packet(pkt, AUDIO_PKT_SIZE) < 0) + return AVERROR(ENOMEM); + + if (modplug->video_stream) + pkt->pts = pkt->dts = modplug->packet_count++ * modplug->ts_per_packet; + + pkt->size = ModPlug_Read(modplug->f, pkt->data, AUDIO_PKT_SIZE); + if (pkt->size <= 0) { + av_free_packet(pkt); + return pkt->size == 0 ? AVERROR_EOF : AVERROR(EIO); + } + return 0; +} + +static int modplug_read_close(AVFormatContext *s) +{ + ModPlugContext *modplug = s->priv_data; + ModPlug_Unload(modplug->f); + av_freep(&modplug->buf); + return 0; +} + +static int modplug_read_seek(AVFormatContext *s, int stream_idx, int64_t ts, int flags) +{ + ModPlugContext *modplug = s->priv_data; + ModPlug_Seek(modplug->f, (int)ts); + if (modplug->video_stream) + modplug->packet_count = ts / modplug->ts_per_packet; + return 0; +} + +static const char modplug_extensions[] = "669,abc,amf,ams,dbm,dmf,dsm,far,it,mdl,med,mid,mod,mt2,mtm,okt,psm,ptm,s3m,stm,ult,umx,xm,itgz,itr,itz,mdgz,mdr,mdz,s3gz,s3r,s3z,xmgz,xmr,xmz"; + +static int modplug_probe(AVProbeData *p) +{ + if (av_match_ext(p->filename, modplug_extensions)) { + if (p->buf_size < 16384) + return AVPROBE_SCORE_EXTENSION/2-1; + else + return AVPROBE_SCORE_EXTENSION; + } + return 0; +} + +static const AVClass modplug_class = { + .class_name = "ModPlug demuxer", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + +AVInputFormat ff_libmodplug_demuxer = { + .name = "libmodplug", + .long_name = NULL_IF_CONFIG_SMALL("ModPlug demuxer"), + .priv_data_size = sizeof(ModPlugContext), + .read_probe = modplug_probe, + .read_header = modplug_read_header, + .read_packet = modplug_read_packet, + .read_close = modplug_read_close, + .read_seek = modplug_read_seek, + .extensions = modplug_extensions, + .priv_class = &modplug_class, +}; diff --git a/libavformat/libnut.c b/libavformat/libnut.c new file mode 100644 index 0000000000..be8f3cb283 --- /dev/null +++ b/libavformat/libnut.c @@ -0,0 +1,324 @@ +/* + * NUT (de)muxing via libnut + * copyright (c) 2006 Oded Shimon <ods15@ods15.dyndns.org> + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * NUT demuxing and muxing via libnut. + * @author Oded Shimon <ods15@ods15.dyndns.org> + */ + +#include "avformat.h" +#include "internal.h" +#include "riff.h" +#include <libnut.h> + +#define ID_STRING "nut/multimedia container" +#define ID_LENGTH (strlen(ID_STRING) + 1) + +typedef struct { + nut_context_tt * nut; + nut_stream_header_tt * s; +} NUTContext; + +static const AVCodecTag nut_tags[] = { + { AV_CODEC_ID_MPEG4, MKTAG('m', 'p', '4', 'v') }, + { AV_CODEC_ID_MP3, MKTAG('m', 'p', '3', ' ') }, + { AV_CODEC_ID_VORBIS, MKTAG('v', 'r', 'b', 's') }, + { 0, 0 }, +}; + +#if CONFIG_LIBNUT_MUXER +static int av_write(void * h, size_t len, const uint8_t * buf) { + AVIOContext * bc = h; + avio_write(bc, buf, len); + //avio_flush(bc); + return len; +} + +static int nut_write_header(AVFormatContext * avf) { + NUTContext * priv = avf->priv_data; + AVIOContext * bc = avf->pb; + nut_muxer_opts_tt mopts = { + .output = { + .priv = bc, + .write = av_write, + }, + .alloc = { av_malloc, av_realloc, av_free }, + .write_index = 1, + .realtime_stream = 0, + .max_distance = 32768, + .fti = NULL, + }; + nut_stream_header_tt * s; + int i; + + priv->s = s = av_mallocz((avf->nb_streams + 1) * sizeof*s); + if(!s) + return AVERROR(ENOMEM); + + for (i = 0; i < avf->nb_streams; i++) { + AVCodecContext * codec = avf->streams[i]->codec; + int j; + int fourcc = 0; + int num, denom, ssize; + + s[i].type = codec->codec_type == AVMEDIA_TYPE_VIDEO ? NUT_VIDEO_CLASS : NUT_AUDIO_CLASS; + + if (codec->codec_tag) fourcc = codec->codec_tag; + else fourcc = ff_codec_get_tag(nut_tags, codec->codec_id); + + if (!fourcc) { + if (codec->codec_type == AVMEDIA_TYPE_VIDEO) fourcc = ff_codec_get_tag(ff_codec_bmp_tags, codec->codec_id); + if (codec->codec_type == AVMEDIA_TYPE_AUDIO) fourcc = ff_codec_get_tag(ff_codec_wav_tags, codec->codec_id); + } + + s[i].fourcc_len = 4; + s[i].fourcc = av_malloc(s[i].fourcc_len); + for (j = 0; j < s[i].fourcc_len; j++) s[i].fourcc[j] = (fourcc >> (j*8)) & 0xFF; + + ff_parse_specific_params(codec, &num, &ssize, &denom); + avpriv_set_pts_info(avf->streams[i], 60, denom, num); + + s[i].time_base.num = denom; + s[i].time_base.den = num; + + s[i].fixed_fps = 0; + s[i].decode_delay = codec->has_b_frames; + s[i].codec_specific_len = codec->extradata_size; + s[i].codec_specific = codec->extradata; + + if (codec->codec_type == AVMEDIA_TYPE_VIDEO) { + s[i].width = codec->width; + s[i].height = codec->height; + s[i].sample_width = 0; + s[i].sample_height = 0; + s[i].colorspace_type = 0; + } else { + s[i].samplerate_num = codec->sample_rate; + s[i].samplerate_denom = 1; + s[i].channel_count = codec->channels; + } + } + + s[avf->nb_streams].type = -1; + priv->nut = nut_muxer_init(&mopts, s, NULL); + + return 0; +} + +static int nut_write_packet(AVFormatContext * avf, AVPacket * pkt) { + NUTContext * priv = avf->priv_data; + nut_packet_tt p; + + p.len = pkt->size; + p.stream = pkt->stream_index; + p.pts = pkt->pts; + p.flags = pkt->flags & AV_PKT_FLAG_KEY ? NUT_FLAG_KEY : 0; + p.next_pts = 0; + + nut_write_frame_reorder(priv->nut, &p, pkt->data); + + return 0; +} + +static int nut_write_trailer(AVFormatContext * avf) { + AVIOContext * bc = avf->pb; + NUTContext * priv = avf->priv_data; + int i; + + nut_muxer_uninit_reorder(priv->nut); + avio_flush(bc); + + for(i = 0; priv->s[i].type != -1; i++ ) av_freep(&priv->s[i].fourcc); + av_freep(&priv->s); + + return 0; +} + +AVOutputFormat ff_libnut_muxer = { + .name = "libnut", + .long_name = "nut format", + .mime_type = "video/x-nut", + .extensions = "nut", + .priv_data_size = sizeof(NUTContext), + .audio_codec = AV_CODEC_ID_VORBIS, + .video_codec = AV_CODEC_ID_MPEG4, + .write_header = nut_write_header, + .write_packet = nut_write_packet, + .write_trailer = nut_write_trailer, + .flags = AVFMT_GLOBALHEADER, +}; +#endif /* CONFIG_LIBNUT_MUXER */ + +static int nut_probe(AVProbeData *p) { + if (!memcmp(p->buf, ID_STRING, ID_LENGTH)) return AVPROBE_SCORE_MAX; + + return 0; +} + +static size_t av_read(void * h, size_t len, uint8_t * buf) { + AVIOContext * bc = h; + return avio_read(bc, buf, len); +} + +static off_t av_seek(void * h, long long pos, int whence) { + AVIOContext * bc = h; + if (whence == SEEK_END) { + pos = avio_size(bc) + pos; + whence = SEEK_SET; + } + return avio_seek(bc, pos, whence); +} + +static int nut_read_header(AVFormatContext * avf) { + NUTContext * priv = avf->priv_data; + AVIOContext * bc = avf->pb; + nut_demuxer_opts_tt dopts = { + .input = { + .priv = bc, + .seek = av_seek, + .read = av_read, + .eof = NULL, + .file_pos = 0, + }, + .alloc = { av_malloc, av_realloc, av_free }, + .read_index = 1, + .cache_syncpoints = 1, + }; + nut_context_tt * nut = priv->nut = nut_demuxer_init(&dopts); + nut_stream_header_tt * s; + int ret, i; + + if(!nut) + return -1; + + if ((ret = nut_read_headers(nut, &s, NULL))) { + av_log(avf, AV_LOG_ERROR, " NUT error: %s\n", nut_error(ret)); + nut_demuxer_uninit(nut); + priv->nut = NULL; + return -1; + } + + priv->s = s; + + for (i = 0; s[i].type != -1 && i < 2; i++) { + AVStream * st = avformat_new_stream(avf, NULL); + int j; + + if (!st) + return AVERROR(ENOMEM); + + for (j = 0; j < s[i].fourcc_len && j < 8; j++) st->codec->codec_tag |= s[i].fourcc[j]<<(j*8); + + st->codec->has_b_frames = s[i].decode_delay; + + st->codec->extradata_size = s[i].codec_specific_len; + if (st->codec->extradata_size) { + if(ff_alloc_extradata(st->codec, st->codec->extradata_size)){ + nut_demuxer_uninit(nut); + priv->nut = NULL; + return AVERROR(ENOMEM); + } + memcpy(st->codec->extradata, s[i].codec_specific, st->codec->extradata_size); + } + + avpriv_set_pts_info(avf->streams[i], 60, s[i].time_base.num, s[i].time_base.den); + st->start_time = 0; + st->duration = s[i].max_pts; + + st->codec->codec_id = ff_codec_get_id(nut_tags, st->codec->codec_tag); + + switch(s[i].type) { + case NUT_AUDIO_CLASS: + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + if (st->codec->codec_id == AV_CODEC_ID_NONE) st->codec->codec_id = ff_codec_get_id(ff_codec_wav_tags, st->codec->codec_tag); + + st->codec->channels = s[i].channel_count; + st->codec->sample_rate = s[i].samplerate_num / s[i].samplerate_denom; + break; + case NUT_VIDEO_CLASS: + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + if (st->codec->codec_id == AV_CODEC_ID_NONE) st->codec->codec_id = ff_codec_get_id(ff_codec_bmp_tags, st->codec->codec_tag); + + st->codec->width = s[i].width; + st->codec->height = s[i].height; + st->sample_aspect_ratio.num = s[i].sample_width; + st->sample_aspect_ratio.den = s[i].sample_height; + break; + } + if (st->codec->codec_id == AV_CODEC_ID_NONE) av_log(avf, AV_LOG_ERROR, "Unknown codec?!\n"); + } + + return 0; +} + +static int nut_read_packet(AVFormatContext * avf, AVPacket * pkt) { + NUTContext * priv = avf->priv_data; + nut_packet_tt pd; + int ret; + + ret = nut_read_next_packet(priv->nut, &pd); + + if (ret || av_new_packet(pkt, pd.len) < 0) { + if (ret != NUT_ERR_EOF) + av_log(avf, AV_LOG_ERROR, " NUT error: %s\n", nut_error(ret)); + return -1; + } + + if (pd.flags & NUT_FLAG_KEY) pkt->flags |= AV_PKT_FLAG_KEY; + pkt->pts = pd.pts; + pkt->stream_index = pd.stream; + pkt->pos = avio_tell(avf->pb); + + ret = nut_read_frame(priv->nut, &pd.len, pkt->data); + + return ret; +} + +static int nut_read_seek(AVFormatContext * avf, int stream_index, int64_t target_ts, int flags) { + NUTContext * priv = avf->priv_data; + int active_streams[] = { stream_index, -1 }; + double time_pos = target_ts * priv->s[stream_index].time_base.num / (double)priv->s[stream_index].time_base.den; + + if (nut_seek(priv->nut, time_pos, 2*!(flags & AVSEEK_FLAG_BACKWARD), active_streams)) return -1; + + return 0; +} + +static int nut_read_close(AVFormatContext *s) { + NUTContext * priv = s->priv_data; + + nut_demuxer_uninit(priv->nut); + + return 0; +} + +AVInputFormat ff_libnut_demuxer = { + .name = "libnut", + .long_name = NULL_IF_CONFIG_SMALL("NUT format"), + .priv_data_size = sizeof(NUTContext), + .read_probe = nut_probe, + .read_header = nut_read_header, + .read_packet = nut_read_packet, + .read_close = nut_read_close, + .read_seek = nut_read_seek, + .extensions = "nut", +}; diff --git a/libavformat/libquvi.c b/libavformat/libquvi.c new file mode 100644 index 0000000000..ca71f9fd8c --- /dev/null +++ b/libavformat/libquvi.c @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2013 Clément BÅ“sch + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <quvi/quvi.h> + +#include "libavformat/avformat.h" +#include "libavformat/internal.h" +#include "libavutil/opt.h" + +typedef struct { + const AVClass *class; + char *format; + AVFormatContext *fmtctx; +} LibQuviContext; + +#define OFFSET(x) offsetof(LibQuviContext, x) +#define FLAGS AV_OPT_FLAG_DECODING_PARAM +static const AVOption libquvi_options[] = { + { "format", "request specific format", OFFSET(format), AV_OPT_TYPE_STRING, {.str="best"}, .flags = FLAGS }, + { NULL } +}; + +static const AVClass libquvi_context_class = { + .class_name = "libquvi", + .item_name = av_default_item_name, + .option = libquvi_options, + .version = LIBAVUTIL_VERSION_INT, +}; + +static int libquvi_close(AVFormatContext *s) +{ + LibQuviContext *qc = s->priv_data; + if (qc->fmtctx) + avformat_close_input(&qc->fmtctx); + return 0; +} + +static int libquvi_read_header(AVFormatContext *s) +{ + int i, ret; + quvi_t q; + quvi_media_t m; + QUVIcode rc; + LibQuviContext *qc = s->priv_data; + char *media_url, *pagetitle; + + rc = quvi_init(&q); + if (rc != QUVI_OK) + goto quvi_fail; + + quvi_setopt(q, QUVIOPT_FORMAT, qc->format); + + rc = quvi_parse(q, s->filename, &m); + if (rc != QUVI_OK) + goto quvi_fail; + + rc = quvi_getprop(m, QUVIPROP_MEDIAURL, &media_url); + if (rc != QUVI_OK) + goto quvi_fail; + + ret = avformat_open_input(&qc->fmtctx, media_url, NULL, NULL); + if (ret < 0) + goto end; + + rc = quvi_getprop(m, QUVIPROP_PAGETITLE, &pagetitle); + if (rc == QUVI_OK) + av_dict_set(&s->metadata, "title", pagetitle, 0); + + for (i = 0; i < qc->fmtctx->nb_streams; i++) { + AVStream *st = avformat_new_stream(s, NULL); + AVStream *ist = qc->fmtctx->streams[i]; + if (!st) { + ret = AVERROR(ENOMEM); + goto end; + } + avpriv_set_pts_info(st, ist->pts_wrap_bits, ist->time_base.num, ist->time_base.den); + avcodec_copy_context(st->codec, qc->fmtctx->streams[i]->codec); + } + + return 0; + +quvi_fail: + av_log(s, AV_LOG_ERROR, "%s\n", quvi_strerror(q, rc)); + ret = AVERROR_EXTERNAL; + +end: + quvi_parse_close(&m); + quvi_close(&q); + return ret; +} + +static int libquvi_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + LibQuviContext *qc = s->priv_data; + return av_read_frame(qc->fmtctx, pkt); +} + +static int libquvi_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp, int flags) +{ + LibQuviContext *qc = s->priv_data; + return av_seek_frame(qc->fmtctx, stream_index, timestamp, flags); +} + +static int libquvi_probe(AVProbeData *p) +{ + int score; + quvi_t q; + QUVIcode rc; + + rc = quvi_init(&q); + if (rc != QUVI_OK) + return AVERROR(ENOMEM); + score = quvi_supported(q, (char *)p->filename) == QUVI_OK ? AVPROBE_SCORE_EXTENSION : 0; + quvi_close(&q); + return score; +} + +AVInputFormat ff_libquvi_demuxer = { + .name = "libquvi", + .long_name = NULL_IF_CONFIG_SMALL("libquvi demuxer"), + .priv_data_size = sizeof(LibQuviContext), + .read_probe = libquvi_probe, + .read_header = libquvi_read_header, + .read_packet = libquvi_read_packet, + .read_close = libquvi_close, + .read_seek = libquvi_read_seek, + .priv_class = &libquvi_context_class, + .flags = AVFMT_NOFILE, +}; diff --git a/libavformat/librtmp.c b/libavformat/librtmp.c index 7133bd655b..5b4c39dde5 100644 --- a/libavformat/librtmp.c +++ b/libavformat/librtmp.c @@ -2,20 +2,20 @@ * RTMP network protocol * Copyright (c) 2010 Howard Chu * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/libssh.c b/libavformat/libssh.c new file mode 100644 index 0000000000..4a9b8674cf --- /dev/null +++ b/libavformat/libssh.c @@ -0,0 +1,229 @@ +/* + * Copyright (c) 2013 Lukasz Marek <lukasz.m.luki@gmail.com> + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <fcntl.h> +#include <libssh/sftp.h> +#include "libavutil/avstring.h" +#include "libavutil/opt.h" +#include "avformat.h" +#include "internal.h" +#include "url.h" + +typedef struct { + const AVClass *class; + ssh_session session; + sftp_session sftp; + sftp_file file; + int64_t filesize; + int rw_timeout; + int trunc; +} LIBSSHContext; + +static int libssh_close(URLContext *h) +{ + LIBSSHContext *s = h->priv_data; + if (s->file) + sftp_close(s->file); + if (s->sftp) + sftp_free(s->sftp); + if (s->session) { + ssh_disconnect(s->session); + ssh_free(s->session); + } + return 0; +} + +static int libssh_open(URLContext *h, const char *url, int flags) +{ + static const int verbosity = SSH_LOG_NOLOG; + LIBSSHContext *s = h->priv_data; + char proto[10], path[MAX_URL_SIZE], hostname[1024], credencials[1024]; + int port = 22, access, ret; + long timeout = s->rw_timeout * 1000; + const char *user = NULL, *pass = NULL; + char *end = NULL; + sftp_attributes stat; + + av_url_split(proto, sizeof(proto), + credencials, sizeof(credencials), + hostname, sizeof(hostname), + &port, + path, sizeof(path), + url); + + if (port <= 0 || port > 65535) + port = 22; + + if (!(s->session = ssh_new())) { + ret = AVERROR(ENOMEM); + goto fail; + } + user = av_strtok(credencials, ":", &end); + pass = av_strtok(end, ":", &end); + ssh_options_set(s->session, SSH_OPTIONS_HOST, hostname); + ssh_options_set(s->session, SSH_OPTIONS_PORT, &port); + ssh_options_set(s->session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity); + if (timeout > 0) + ssh_options_set(s->session, SSH_OPTIONS_TIMEOUT_USEC, &timeout); + if (user) + ssh_options_set(s->session, SSH_OPTIONS_USER, user); + + if (ssh_connect(s->session) != SSH_OK) { + av_log(h, AV_LOG_ERROR, "Connection failed. %s\n", ssh_get_error(s->session)); + ret = AVERROR(EIO); + goto fail; + } + + if (pass && ssh_userauth_password(s->session, NULL, pass) != SSH_AUTH_SUCCESS) { + av_log(h, AV_LOG_ERROR, "Error authenticating with password: %s\n", ssh_get_error(s->session)); + ret = AVERROR(EACCES); + goto fail; + } + + if (!(s->sftp = sftp_new(s->session))) { + av_log(h, AV_LOG_ERROR, "SFTP session creation failed: %s\n", ssh_get_error(s->session)); + ret = AVERROR(ENOMEM); + goto fail; + } + + if (sftp_init(s->sftp) != SSH_OK) { + av_log(h, AV_LOG_ERROR, "Error initializing sftp session: %s\n", ssh_get_error(s->session)); + ret = AVERROR(EIO); + goto fail; + } + + if ((flags & AVIO_FLAG_WRITE) && (flags & AVIO_FLAG_READ)) { + access = O_CREAT | O_RDWR; + if (s->trunc) + access |= O_TRUNC; + } else if (flags & AVIO_FLAG_WRITE) { + access = O_CREAT | O_WRONLY; + if (s->trunc) + access |= O_TRUNC; + } else { + access = O_RDONLY; + } + + if (!(s->file = sftp_open(s->sftp, path, access, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH))) { + av_log(h, AV_LOG_ERROR, "Error opening sftp file: %s\n", ssh_get_error(s->session)); + ret = AVERROR(EIO); + goto fail; + } + + if (!(stat = sftp_fstat(s->file))) { + av_log(h, AV_LOG_WARNING, "Cannot stat remote file %s.\n", path); + s->filesize = -1; + } else { + s->filesize = stat->size; + sftp_attributes_free(stat); + } + + return 0; + + fail: + libssh_close(h); + return ret; +} + +static int64_t libssh_seek(URLContext *h, int64_t pos, int whence) +{ + LIBSSHContext *s = h->priv_data; + int64_t newpos; + + if (s->filesize == -1 && (whence == AVSEEK_SIZE || whence == SEEK_END)) { + av_log(h, AV_LOG_ERROR, "Error during seeking.\n"); + return AVERROR(EIO); + } + + switch(whence) { + case AVSEEK_SIZE: + return s->filesize; + case SEEK_SET: + newpos = pos; + break; + case SEEK_CUR: + newpos = sftp_tell64(s->file); + break; + case SEEK_END: + newpos = s->filesize + pos; + break; + default: + return AVERROR(EINVAL); + } + + if (sftp_seek64(s->file, newpos)) { + av_log(h, AV_LOG_ERROR, "Error during seeking.\n"); + return AVERROR(EIO); + } + + return newpos; +} + +static int libssh_read(URLContext *h, unsigned char *buf, int size) +{ + LIBSSHContext *s = h->priv_data; + int bytes_read; + + if ((bytes_read = sftp_read(s->file, buf, size)) < 0) { + av_log(h, AV_LOG_ERROR, "Read error.\n"); + return AVERROR(EIO); + } + return bytes_read; +} + +static int libssh_write(URLContext *h, const unsigned char *buf, int size) +{ + LIBSSHContext *s = h->priv_data; + int bytes_written; + + if ((bytes_written = sftp_write(s->file, buf, size)) < 0) { + av_log(h, AV_LOG_ERROR, "Write error.\n"); + return AVERROR(EIO); + } + return bytes_written; +} + +#define OFFSET(x) offsetof(LIBSSHContext, x) +#define D AV_OPT_FLAG_DECODING_PARAM +#define E AV_OPT_FLAG_ENCODING_PARAM +static const AVOption options[] = { + {"timeout", "set timeout of socket I/O operations", OFFSET(rw_timeout), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, D|E }, + {"truncate", "Truncate existing files on write", OFFSET(trunc), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 1, E }, + {NULL} +}; + +static const AVClass libssh_context_class = { + .class_name = "libssh", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + +URLProtocol ff_libssh_protocol = { + .name = "sftp", + .url_open = libssh_open, + .url_read = libssh_read, + .url_write = libssh_write, + .url_seek = libssh_seek, + .url_close = libssh_close, + .priv_data_size = sizeof(LIBSSHContext), + .priv_data_class = &libssh_context_class, + .flags = URL_PROTOCOL_FLAG_NETWORK, +}; diff --git a/libavformat/lmlm4.c b/libavformat/lmlm4.c index b67b68e27b..c8ea42168b 100644 --- a/libavformat/lmlm4.c +++ b/libavformat/lmlm4.c @@ -5,20 +5,20 @@ * Due to a lack of sample files, only files with one channel are supported. * u-law and ADPCM audio are unsupported for the same reason. * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -35,7 +35,7 @@ #define LMLM4_MAX_PACKET_SIZE 1024 * 1024 static int lmlm4_probe(AVProbeData * pd) { - unsigned char *buf = pd->buf; + const unsigned char *buf = pd->buf; unsigned int frame_type, packet_size; frame_type = AV_RB16(buf+2); @@ -93,8 +93,8 @@ static int lmlm4_read_packet(AVFormatContext *s, AVPacket *pkt) { av_log(s, AV_LOG_ERROR, "invalid or unsupported frame_type\n"); return AVERROR(EIO); } - if (packet_size > LMLM4_MAX_PACKET_SIZE) { - av_log(s, AV_LOG_ERROR, "packet size exceeds maximum\n"); + if (packet_size > LMLM4_MAX_PACKET_SIZE || packet_size<=8) { + av_log(s, AV_LOG_ERROR, "packet size %d is invalid\n", packet_size); return AVERROR(EIO); } diff --git a/libavformat/loasdec.c b/libavformat/loasdec.c new file mode 100644 index 0000000000..05ef0fe3ad --- /dev/null +++ b/libavformat/loasdec.c @@ -0,0 +1,87 @@ +/* + * LOAS AudioSyncStream demuxer + * Copyright (c) 2008 Michael Niedermayer <michaelni@gmx.at> + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/intreadwrite.h" +#include "libavutil/internal.h" +#include "avformat.h" +#include "internal.h" +#include "rawdec.h" + +static int loas_probe(AVProbeData *p) +{ + int max_frames = 0, first_frames = 0; + int fsize, frames; + const uint8_t *buf0 = p->buf; + const uint8_t *buf2; + const uint8_t *buf; + const uint8_t *end = buf0 + p->buf_size - 3; + buf = buf0; + + for(; buf < end; buf= buf2+1) { + buf2 = buf; + + for(frames = 0; buf2 < end; frames++) { + uint32_t header = AV_RB24(buf2); + if((header >> 13) != 0x2B7) + break; + fsize = (header & 0x1FFF) + 3; + if(fsize < 7) + break; + fsize = FFMIN(fsize, end - buf2); + buf2 += fsize; + } + max_frames = FFMAX(max_frames, frames); + if(buf == buf0) + first_frames= frames; + } + if (first_frames>=3) return AVPROBE_SCORE_EXTENSION+1; + else if(max_frames>100)return AVPROBE_SCORE_EXTENSION; + else if(max_frames>=3) return AVPROBE_SCORE_EXTENSION / 2; + else return 0; +} + +static int loas_read_header(AVFormatContext *s) +{ + AVStream *st; + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_id = s->iformat->raw_codec_id; + st->need_parsing = AVSTREAM_PARSE_FULL_RAW; + + //LCM of all possible AAC sample rates + avpriv_set_pts_info(st, 64, 1, 28224000); + + return 0; +} + +AVInputFormat ff_loas_demuxer = { + .name = "loas", + .long_name = NULL_IF_CONFIG_SMALL("LOAS AudioSyncStream"), + .read_probe = loas_probe, + .read_header = loas_read_header, + .read_packet = ff_raw_read_partial_packet, + .flags= AVFMT_GENERIC_INDEX, + .raw_codec_id = AV_CODEC_ID_AAC_LATM, +}; diff --git a/libavformat/lvfdec.c b/libavformat/lvfdec.c new file mode 100644 index 0000000000..a809c6767b --- /dev/null +++ b/libavformat/lvfdec.c @@ -0,0 +1,148 @@ +/* + * LVF demuxer + * Copyright (c) 2012 Paul B Mahol + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/intreadwrite.h" +#include "avformat.h" +#include "riff.h" + +static int lvf_probe(AVProbeData *p) +{ + if (AV_RL32(p->buf) == MKTAG('L', 'V', 'F', 'F')) + return AVPROBE_SCORE_EXTENSION; + return 0; +} + +static int lvf_read_header(AVFormatContext *s) +{ + AVStream *st; + int64_t next_offset; + unsigned size, nb_streams, id; + + avio_skip(s->pb, 16); + nb_streams = avio_rl32(s->pb); + if (!nb_streams) + return AVERROR_INVALIDDATA; + if (nb_streams > 2) { + avpriv_request_sample(s, "%d streams", nb_streams); + return AVERROR_PATCHWELCOME; + } + + avio_skip(s->pb, 1012); + + while (!url_feof(s->pb)) { + id = avio_rl32(s->pb); + size = avio_rl32(s->pb); + next_offset = avio_tell(s->pb) + size; + + switch (id) { + case MKTAG('0', '0', 'f', 'm'): + st = avformat_new_stream(s, 0); + if (!st) + return AVERROR(ENOMEM); + + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + avio_skip(s->pb, 4); + st->codec->width = avio_rl32(s->pb); + st->codec->height = avio_rl32(s->pb); + avio_skip(s->pb, 4); + st->codec->codec_tag = avio_rl32(s->pb); + st->codec->codec_id = ff_codec_get_id(ff_codec_bmp_tags, + st->codec->codec_tag); + avpriv_set_pts_info(st, 32, 1, 1000); + break; + case MKTAG('0', '1', 'f', 'm'): + st = avformat_new_stream(s, 0); + if (!st) + return AVERROR(ENOMEM); + + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_tag = avio_rl16(s->pb); + st->codec->channels = avio_rl16(s->pb); + st->codec->sample_rate = avio_rl16(s->pb); + avio_skip(s->pb, 8); + st->codec->bits_per_coded_sample = avio_r8(s->pb); + st->codec->codec_id = ff_codec_get_id(ff_codec_wav_tags, + st->codec->codec_tag); + avpriv_set_pts_info(st, 32, 1, 1000); + break; + case 0: + avio_seek(s->pb, 2048 + 8, SEEK_SET); + return 0; + default: + avpriv_request_sample(s, "id %d", id); + return AVERROR_PATCHWELCOME; + } + + avio_seek(s->pb, next_offset, SEEK_SET); + } + + return AVERROR_EOF; +} + +static int lvf_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + unsigned size, flags, timestamp, id; + int64_t pos; + int ret, is_video = 0; + + pos = avio_tell(s->pb); + while (!url_feof(s->pb)) { + id = avio_rl32(s->pb); + size = avio_rl32(s->pb); + + if (size == 0xFFFFFFFFu) + return AVERROR_EOF; + + switch (id) { + case MKTAG('0', '0', 'd', 'c'): + is_video = 1; + case MKTAG('0', '1', 'w', 'b'): + if (size < 8) + return AVERROR_INVALIDDATA; + timestamp = avio_rl32(s->pb); + flags = avio_rl32(s->pb); + ret = av_get_packet(s->pb, pkt, size - 8); + if (flags & (1 << 12)) + pkt->flags |= AV_PKT_FLAG_KEY; + pkt->stream_index = is_video ? 0 : 1; + pkt->pts = timestamp; + pkt->pos = pos; + return ret; + default: + ret = avio_skip(s->pb, size); + } + + if (ret < 0) + return ret; + } + + return AVERROR_EOF; +} + +AVInputFormat ff_lvf_demuxer = { + .name = "lvf", + .long_name = NULL_IF_CONFIG_SMALL("LVF"), + .read_probe = lvf_probe, + .read_header = lvf_read_header, + .read_packet = lvf_read_packet, + .extensions = "lvf", + .flags = AVFMT_GENERIC_INDEX, +}; diff --git a/libavformat/lxfdec.c b/libavformat/lxfdec.c index 859fc71877..f0a96396b4 100644 --- a/libavformat/lxfdec.c +++ b/libavformat/lxfdec.c @@ -2,20 +2,20 @@ * LXF demuxer * Copyright (c) 2010 Tomas Härdin * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -90,7 +90,7 @@ static int sync(AVFormatContext *s, uint8_t *header) return ret < 0 ? ret : AVERROR_EOF; while (memcmp(buf, LXF_IDENT, LXF_IDENT_LENGTH)) { - if (s->pb->eof_reached) + if (url_feof(s->pb)) return AVERROR_EOF; memmove(buf, &buf[1], LXF_IDENT_LENGTH-1); @@ -138,8 +138,9 @@ static int get_packet_header(AVFormatContext *s) } //read the rest of the packet header - ret = avio_read(pb, header + (p - header), header_size - (p - header)); - if (ret != header_size - (p - header)) + if ((ret = avio_read(pb, header + (p - header), + header_size - (p - header))) != + header_size - (p - header)) return ret < 0 ? ret : AVERROR_EOF; if (check_checksum(header, header_size)) @@ -258,6 +259,7 @@ static int lxf_read_header(AVFormatContext *s) st->codec->bit_rate = 1000000 * ((video_params >> 14) & 0xFF); st->codec->codec_tag = video_params & 0xF; st->codec->codec_id = ff_codec_get_id(lxf_tags, st->codec->codec_tag); + st->need_parsing = AVSTREAM_PARSE_HEADERS; av_log(s, AV_LOG_DEBUG, "record: %x = %i-%02i-%02i\n", record_date, 1900 + (record_date & 0x7F), (record_date >> 7) & 0xF, diff --git a/libavformat/m4vdec.c b/libavformat/m4vdec.c index 04bd0621bc..c2fd4d7ab6 100644 --- a/libavformat/m4vdec.c +++ b/libavformat/m4vdec.c @@ -2,20 +2,20 @@ * RAW MPEG-4 video demuxer * Copyright (c) 2006 Thijs Vermeir <thijs.vermeir@barco.com> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -45,7 +45,7 @@ static int mpeg4video_probe(AVProbeData *probe_packet) } if (VOP >= VISO && VOP >= VOL && VO >= VOL && VOL > 0 && res==0) - return AVPROBE_SCORE_EXTENSION; + return VOP+VO > 3 ? AVPROBE_SCORE_EXTENSION : AVPROBE_SCORE_EXTENSION/2; return 0; } diff --git a/libavformat/matroska.c b/libavformat/matroska.c index 410e2f40c3..77a88a8756 100644 --- a/libavformat/matroska.c +++ b/libavformat/matroska.c @@ -2,25 +2,28 @@ * Matroska common data * Copyright (c) 2003-2004 The ffmpeg Project * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "matroska.h" +/* If you add a tag here that is not in ff_codec_bmp_tags[] + or ff_codec_wav_tags[], add it also to additional_audio_tags[] + or additional_video_tags[] in matroskaenc.c */ const CodecTags ff_mkv_codec_tags[]={ {"A_AAC" , AV_CODEC_ID_AAC}, {"A_AC3" , AV_CODEC_ID_AC3}, @@ -32,6 +35,8 @@ const CodecTags ff_mkv_codec_tags[]={ {"A_MPEG/L2" , AV_CODEC_ID_MP2}, {"A_MPEG/L1" , AV_CODEC_ID_MP2}, {"A_MPEG/L3" , AV_CODEC_ID_MP3}, + {"A_OPUS", AV_CODEC_ID_OPUS}, + {"A_OPUS/EXPERIMENTAL",AV_CODEC_ID_OPUS}, {"A_PCM/FLOAT/IEEE" , AV_CODEC_ID_PCM_F32LE}, {"A_PCM/FLOAT/IEEE" , AV_CODEC_ID_PCM_F64LE}, {"A_PCM/INT/BIG" , AV_CODEC_ID_PCM_S16BE}, @@ -52,14 +57,27 @@ const CodecTags ff_mkv_codec_tags[]={ {"A_VORBIS" , AV_CODEC_ID_VORBIS}, {"A_WAVPACK4" , AV_CODEC_ID_WAVPACK}, + {"D_WEBVTT/SUBTITLES" , AV_CODEC_ID_WEBVTT}, + {"D_WEBVTT/CAPTIONS" , AV_CODEC_ID_WEBVTT}, + {"D_WEBVTT/DESCRIPTIONS", AV_CODEC_ID_WEBVTT}, + {"D_WEBVTT/METADATA" , AV_CODEC_ID_WEBVTT}, + + {"S_TEXT/UTF8" , AV_CODEC_ID_SUBRIP}, {"S_TEXT/UTF8" , AV_CODEC_ID_TEXT}, {"S_TEXT/UTF8" , AV_CODEC_ID_SRT}, {"S_TEXT/ASCII" , AV_CODEC_ID_TEXT}, +#if FF_API_ASS_SSA {"S_TEXT/ASS" , AV_CODEC_ID_SSA}, {"S_TEXT/SSA" , AV_CODEC_ID_SSA}, {"S_ASS" , AV_CODEC_ID_SSA}, {"S_SSA" , AV_CODEC_ID_SSA}, +#endif + {"S_TEXT/ASS" , AV_CODEC_ID_ASS}, + {"S_TEXT/SSA" , AV_CODEC_ID_ASS}, + {"S_ASS" , AV_CODEC_ID_ASS}, + {"S_SSA" , AV_CODEC_ID_ASS}, {"S_VOBSUB" , AV_CODEC_ID_DVD_SUBTITLE}, + {"S_DVBSUB" , AV_CODEC_ID_DVB_SUBTITLE}, {"S_HDMV/PGS" , AV_CODEC_ID_HDMV_PGS_SUBTITLE}, {"V_DIRAC" , AV_CODEC_ID_DIRAC}, @@ -77,6 +95,7 @@ const CodecTags ff_mkv_codec_tags[]={ {"V_REAL/RV20" , AV_CODEC_ID_RV20}, {"V_REAL/RV30" , AV_CODEC_ID_RV30}, {"V_REAL/RV40" , AV_CODEC_ID_RV40}, + {"V_SNOW" , AV_CODEC_ID_SNOW}, {"V_THEORA" , AV_CODEC_ID_THEORA}, {"V_UNCOMPRESSED" , AV_CODEC_ID_RAWVIDEO}, {"V_VP8" , AV_CODEC_ID_VP8}, @@ -93,6 +112,7 @@ const CodecMime ff_mkv_mime_tags[] = { {"image/tiff" , AV_CODEC_ID_TIFF}, {"application/x-truetype-font", AV_CODEC_ID_TTF}, {"application/x-font" , AV_CODEC_ID_TTF}, + {"application/vnd.ms-opentype", AV_CODEC_ID_OTF}, {"" , AV_CODEC_ID_NONE} }; @@ -102,3 +122,27 @@ const AVMetadataConv ff_mkv_metadata_conv[] = { { "PART_NUMBER" , "track" }, { 0 } }; + +const char * const ff_matroska_video_stereo_mode[MATROSKA_VIDEO_STEREO_MODE_COUNT] = { + "mono", + "left_right", + "bottom_top", + "top_bottom", + "checkerboard_rl", + "checkerboard_lr", + "row_interleaved_rl", + "row_interleaved_lr", + "col_interleaved_rl", + "col_interleaved_lr", + "anaglyph_cyan_red", + "right_left", + "anaglyph_green_magenta", + "block_lr", + "block_rl", +}; + +const char * const ff_matroska_video_stereo_plane[MATROSKA_VIDEO_STEREO_PLANE_COUNT] = { + "left", + "right", + "background", +}; diff --git a/libavformat/matroska.h b/libavformat/matroska.h index 0dbc724d56..3bb5aee080 100644 --- a/libavformat/matroska.h +++ b/libavformat/matroska.h @@ -2,20 +2,20 @@ * Matroska constants * Copyright (c) 2003-2004 The ffmpeg Project * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -78,14 +78,21 @@ #define MATROSKA_ID_TRACKNUMBER 0xD7 #define MATROSKA_ID_TRACKUID 0x73C5 #define MATROSKA_ID_TRACKTYPE 0x83 -#define MATROSKA_ID_TRACKAUDIO 0xE1 -#define MATROSKA_ID_TRACKVIDEO 0xE0 +#define MATROSKA_ID_TRACKVIDEO 0xE0 +#define MATROSKA_ID_TRACKAUDIO 0xE1 +#define MATROSKA_ID_TRACKOPERATION 0xE2 +#define MATROSKA_ID_TRACKCOMBINEPLANES 0xE3 +#define MATROSKA_ID_TRACKPLANE 0xE4 +#define MATROSKA_ID_TRACKPLANEUID 0xE5 +#define MATROSKA_ID_TRACKPLANETYPE 0xE6 #define MATROSKA_ID_CODECID 0x86 #define MATROSKA_ID_CODECPRIVATE 0x63A2 #define MATROSKA_ID_CODECNAME 0x258688 #define MATROSKA_ID_CODECINFOURL 0x3B4040 #define MATROSKA_ID_CODECDOWNLOADURL 0x26B240 #define MATROSKA_ID_CODECDECODEALL 0xAA +#define MATROSKA_ID_CODECDELAY 0x56AA +#define MATROSKA_ID_SEEKPREROLL 0x56BB #define MATROSKA_ID_TRACKNAME 0x536E #define MATROSKA_ID_TRACKLANGUAGE 0x22B59C #define MATROSKA_ID_TRACKFLAGENABLED 0xB9 @@ -113,6 +120,7 @@ #define MATROSKA_ID_VIDEODISPLAYUNIT 0x54B2 #define MATROSKA_ID_VIDEOFLAGINTERLACED 0x9A #define MATROSKA_ID_VIDEOSTEREOMODE 0x53B8 +#define MATROSKA_ID_VIDEOALPHAMODE 0x53C0 #define MATROSKA_ID_VIDEOASPECTRATIO 0x54B3 #define MATROSKA_ID_VIDEOCOLORSPACE 0x2EB524 @@ -131,6 +139,15 @@ #define MATROSKA_ID_ENCODINGCOMPALGO 0x4254 #define MATROSKA_ID_ENCODINGCOMPSETTINGS 0x4255 +#define MATROSKA_ID_ENCODINGENCRYPTION 0x5035 +#define MATROSKA_ID_ENCODINGENCAESSETTINGS 0x47E7 +#define MATROSKA_ID_ENCODINGENCALGO 0x47E1 +#define MATROSKA_ID_ENCODINGENCKEYID 0x47E2 +#define MATROSKA_ID_ENCODINGSIGALGO 0x47E5 +#define MATROSKA_ID_ENCODINGSIGHASHALGO 0x47E6 +#define MATROSKA_ID_ENCODINGSIGKEYID 0x47E4 +#define MATROSKA_ID_ENCODINGSIGNATURE 0x47E3 + /* ID in the cues master */ #define MATROSKA_ID_POINTENTRY 0xBB @@ -141,6 +158,8 @@ /* IDs in the cuetrackposition master */ #define MATROSKA_ID_CUETRACK 0xF7 #define MATROSKA_ID_CUECLUSTERPOSITION 0xF1 +#define MATROSKA_ID_CUERELATIVEPOSITION 0xF0 +#define MATROSKA_ID_CUEDURATION 0xB2 #define MATROSKA_ID_CUEBLOCKNUMBER 0x5378 /* IDs in the tags master */ @@ -170,6 +189,10 @@ #define MATROSKA_ID_CLUSTERPOSITION 0xA7 #define MATROSKA_ID_CLUSTERPREVSIZE 0xAB #define MATROSKA_ID_BLOCKGROUP 0xA0 +#define MATROSKA_ID_BLOCKADDITIONS 0x75A1 +#define MATROSKA_ID_BLOCKMORE 0xA6 +#define MATROSKA_ID_BLOCKADDID 0xEE +#define MATROSKA_ID_BLOCKADDITIONAL 0xA5 #define MATROSKA_ID_SIMPLEBLOCK 0xA3 /* IDs in the blockgroup master */ @@ -177,6 +200,7 @@ #define MATROSKA_ID_BLOCKDURATION 0x9B #define MATROSKA_ID_BLOCKREFERENCE 0xFB #define MATROSKA_ID_CODECSTATE 0xA4 +#define MATROSKA_ID_DISCARDPADDING 0x75A2 /* IDs in the attachments master */ #define MATROSKA_ID_ATTACHEDFILE 0x61A7 @@ -211,6 +235,7 @@ typedef enum { MATROSKA_TRACK_TYPE_LOGO = 0x10, MATROSKA_TRACK_TYPE_SUBTITLE = 0x11, MATROSKA_TRACK_TYPE_CONTROL = 0x20, + MATROSKA_TRACK_TYPE_METADATA = 0x21, } MatroskaTrackType; typedef enum { @@ -243,15 +268,20 @@ typedef enum { */ typedef struct CodecTags{ - char str[20]; + char str[22]; enum AVCodecID id; }CodecTags; /* max. depth in the EBML tree structure */ #define EBML_MAX_DEPTH 16 +#define MATROSKA_VIDEO_STEREO_MODE_COUNT 15 +#define MATROSKA_VIDEO_STEREO_PLANE_COUNT 3 + extern const CodecTags ff_mkv_codec_tags[]; extern const CodecMime ff_mkv_mime_tags[]; extern const AVMetadataConv ff_mkv_metadata_conv[]; +extern const char * const ff_matroska_video_stereo_mode[MATROSKA_VIDEO_STEREO_MODE_COUNT]; +extern const char * const ff_matroska_video_stereo_plane[MATROSKA_VIDEO_STEREO_PLANE_COUNT]; #endif /* AVFORMAT_MATROSKA_H */ diff --git a/libavformat/matroskadec.c b/libavformat/matroskadec.c index f798342443..f770d67af7 100644 --- a/libavformat/matroskadec.c +++ b/libavformat/matroskadec.c @@ -1,21 +1,21 @@ /* * Matroska file demuxer - * Copyright (c) 2003-2008 The Libav Project + * Copyright (c) 2003-2008 The FFmpeg Project * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -39,6 +39,7 @@ #include "matroska.h" #include "libavcodec/bytestream.h" #include "libavcodec/mpeg4audio.h" +#include "libavutil/base64.h" #include "libavutil/intfloat.h" #include "libavutil/intreadwrite.h" #include "libavutil/avstring.h" @@ -102,9 +103,15 @@ typedef struct { } MatroskaTrackCompression; typedef struct { + uint64_t algo; + EbmlBin key_id; +} MatroskaTrackEncryption; + +typedef struct { uint64_t scope; uint64_t type; MatroskaTrackCompression compression; + MatroskaTrackEncryption encryption; } MatroskaTrackEncoding; typedef struct { @@ -113,7 +120,9 @@ typedef struct { uint64_t display_height; uint64_t pixel_width; uint64_t pixel_height; - uint64_t fourcc; + EbmlBin color_space; + uint64_t stereo_mode; + uint64_t alpha_mode; } MatroskaTrackVideo; typedef struct { @@ -134,6 +143,15 @@ typedef struct { } MatroskaTrackAudio; typedef struct { + uint64_t uid; + uint64_t type; +} MatroskaTrackPlane; + +typedef struct { + EbmlList combine_planes; +} MatroskaTrackOperation; + +typedef struct { uint64_t num; uint64_t uid; uint64_t type; @@ -145,13 +163,17 @@ typedef struct { uint64_t default_duration; uint64_t flag_default; uint64_t flag_forced; + uint64_t codec_delay; + uint64_t seek_preroll; MatroskaTrackVideo video; MatroskaTrackAudio audio; + MatroskaTrackOperation operation; EbmlList encodings; AVStream *stream; int64_t end_timecode; int ms_compat; + uint64_t max_block_additional_id; } MatroskaTrack; typedef struct { @@ -230,6 +252,7 @@ typedef struct { uint64_t time_scale; double duration; char *title; + EbmlBin date_utc; EbmlList tracks; EbmlList attachments; EbmlList chapters; @@ -267,6 +290,9 @@ typedef struct { int64_t reference; uint64_t non_simple; EbmlBin bin; + uint64_t additional_id; + EbmlBin additional; + uint64_t discard_padding; } MatroskaBlock; static EbmlSyntax ebml_header[] = { @@ -291,25 +317,26 @@ static EbmlSyntax matroska_info[] = { { MATROSKA_ID_TITLE, EBML_UTF8, 0, offsetof(MatroskaDemuxContext,title) }, { MATROSKA_ID_WRITINGAPP, EBML_NONE }, { MATROSKA_ID_MUXINGAPP, EBML_NONE }, - { MATROSKA_ID_DATEUTC, EBML_NONE }, + { MATROSKA_ID_DATEUTC, EBML_BIN, 0, offsetof(MatroskaDemuxContext,date_utc) }, { MATROSKA_ID_SEGMENTUID, EBML_NONE }, { 0 } }; static EbmlSyntax matroska_track_video[] = { { MATROSKA_ID_VIDEOFRAMERATE, EBML_FLOAT,0, offsetof(MatroskaTrackVideo,frame_rate) }, - { MATROSKA_ID_VIDEODISPLAYWIDTH, EBML_UINT, 0, offsetof(MatroskaTrackVideo,display_width) }, - { MATROSKA_ID_VIDEODISPLAYHEIGHT, EBML_UINT, 0, offsetof(MatroskaTrackVideo,display_height) }, + { MATROSKA_ID_VIDEODISPLAYWIDTH, EBML_UINT, 0, offsetof(MatroskaTrackVideo,display_width), {.u=-1} }, + { MATROSKA_ID_VIDEODISPLAYHEIGHT, EBML_UINT, 0, offsetof(MatroskaTrackVideo,display_height), {.u=-1} }, { MATROSKA_ID_VIDEOPIXELWIDTH, EBML_UINT, 0, offsetof(MatroskaTrackVideo,pixel_width) }, { MATROSKA_ID_VIDEOPIXELHEIGHT, EBML_UINT, 0, offsetof(MatroskaTrackVideo,pixel_height) }, - { MATROSKA_ID_VIDEOCOLORSPACE, EBML_UINT, 0, offsetof(MatroskaTrackVideo,fourcc) }, + { MATROSKA_ID_VIDEOCOLORSPACE, EBML_BIN, 0, offsetof(MatroskaTrackVideo,color_space) }, + { MATROSKA_ID_VIDEOSTEREOMODE, EBML_UINT, 0, offsetof(MatroskaTrackVideo,stereo_mode) }, + { MATROSKA_ID_VIDEOALPHAMODE, EBML_UINT, 0, offsetof(MatroskaTrackVideo,alpha_mode) }, { MATROSKA_ID_VIDEOPIXELCROPB, EBML_NONE }, { MATROSKA_ID_VIDEOPIXELCROPT, EBML_NONE }, { MATROSKA_ID_VIDEOPIXELCROPL, EBML_NONE }, { MATROSKA_ID_VIDEOPIXELCROPR, EBML_NONE }, { MATROSKA_ID_VIDEODISPLAYUNIT, EBML_NONE }, { MATROSKA_ID_VIDEOFLAGINTERLACED,EBML_NONE }, - { MATROSKA_ID_VIDEOSTEREOMODE, EBML_NONE }, { MATROSKA_ID_VIDEOASPECTRATIO, EBML_NONE }, { 0 } }; @@ -328,10 +355,21 @@ static EbmlSyntax matroska_track_encoding_compression[] = { { 0 } }; +static EbmlSyntax matroska_track_encoding_encryption[] = { + { MATROSKA_ID_ENCODINGENCALGO, EBML_UINT, 0, offsetof(MatroskaTrackEncryption,algo), {.u=0} }, + { MATROSKA_ID_ENCODINGENCKEYID, EBML_BIN, 0, offsetof(MatroskaTrackEncryption,key_id) }, + { MATROSKA_ID_ENCODINGENCAESSETTINGS, EBML_NONE }, + { MATROSKA_ID_ENCODINGSIGALGO, EBML_NONE }, + { MATROSKA_ID_ENCODINGSIGHASHALGO, EBML_NONE }, + { MATROSKA_ID_ENCODINGSIGKEYID, EBML_NONE }, + { MATROSKA_ID_ENCODINGSIGNATURE, EBML_NONE }, + { 0 } +}; static EbmlSyntax matroska_track_encoding[] = { { MATROSKA_ID_ENCODINGSCOPE, EBML_UINT, 0, offsetof(MatroskaTrackEncoding,scope), {.u=1} }, { MATROSKA_ID_ENCODINGTYPE, EBML_UINT, 0, offsetof(MatroskaTrackEncoding,type), {.u=0} }, { MATROSKA_ID_ENCODINGCOMPRESSION,EBML_NEST, 0, offsetof(MatroskaTrackEncoding,compression), {.n=matroska_track_encoding_compression} }, + { MATROSKA_ID_ENCODINGENCRYPTION, EBML_NEST, 0, offsetof(MatroskaTrackEncoding,encryption), {.n=matroska_track_encoding_encryption} }, { MATROSKA_ID_ENCODINGORDER, EBML_NONE }, { 0 } }; @@ -341,6 +379,22 @@ static EbmlSyntax matroska_track_encodings[] = { { 0 } }; +static EbmlSyntax matroska_track_plane[] = { + { MATROSKA_ID_TRACKPLANEUID, EBML_UINT, 0, offsetof(MatroskaTrackPlane,uid) }, + { MATROSKA_ID_TRACKPLANETYPE, EBML_UINT, 0, offsetof(MatroskaTrackPlane,type) }, + { 0 } +}; + +static EbmlSyntax matroska_track_combine_planes[] = { + { MATROSKA_ID_TRACKPLANE, EBML_NEST, sizeof(MatroskaTrackPlane), offsetof(MatroskaTrackOperation,combine_planes), {.n=matroska_track_plane} }, + { 0 } +}; + +static EbmlSyntax matroska_track_operation[] = { + { MATROSKA_ID_TRACKCOMBINEPLANES, EBML_NEST, 0, 0, {.n=matroska_track_combine_planes} }, + { 0 } +}; + static EbmlSyntax matroska_track[] = { { MATROSKA_ID_TRACKNUMBER, EBML_UINT, 0, offsetof(MatroskaTrack,num) }, { MATROSKA_ID_TRACKNAME, EBML_UTF8, 0, offsetof(MatroskaTrack,name) }, @@ -355,7 +409,11 @@ static EbmlSyntax matroska_track[] = { { MATROSKA_ID_TRACKFLAGFORCED, EBML_UINT, 0, offsetof(MatroskaTrack,flag_forced), {.u=0} }, { MATROSKA_ID_TRACKVIDEO, EBML_NEST, 0, offsetof(MatroskaTrack,video), {.n=matroska_track_video} }, { MATROSKA_ID_TRACKAUDIO, EBML_NEST, 0, offsetof(MatroskaTrack,audio), {.n=matroska_track_audio} }, + { MATROSKA_ID_TRACKOPERATION, EBML_NEST, 0, offsetof(MatroskaTrack,operation), {.n=matroska_track_operation} }, { MATROSKA_ID_TRACKCONTENTENCODINGS,EBML_NEST, 0, 0, {.n=matroska_track_encodings} }, + { MATROSKA_ID_TRACKMAXBLKADDID, EBML_UINT, 0, offsetof(MatroskaTrack,max_block_additional_id) }, + { MATROSKA_ID_CODECDELAY, EBML_UINT, 0, offsetof(MatroskaTrack,codec_delay) }, + { MATROSKA_ID_SEEKPREROLL, EBML_UINT, 0, offsetof(MatroskaTrack,seek_preroll) }, { MATROSKA_ID_TRACKFLAGENABLED, EBML_NONE }, { MATROSKA_ID_TRACKFLAGLACING, EBML_NONE }, { MATROSKA_ID_CODECNAME, EBML_NONE }, @@ -364,7 +422,6 @@ static EbmlSyntax matroska_track[] = { { MATROSKA_ID_CODECDOWNLOADURL, EBML_NONE }, { MATROSKA_ID_TRACKMINCACHE, EBML_NONE }, { MATROSKA_ID_TRACKMAXCACHE, EBML_NONE }, - { MATROSKA_ID_TRACKMAXBLKADDID, EBML_NONE }, { 0 } }; @@ -422,6 +479,8 @@ static EbmlSyntax matroska_chapters[] = { static EbmlSyntax matroska_index_pos[] = { { MATROSKA_ID_CUETRACK, EBML_UINT, 0, offsetof(MatroskaIndexPos,track) }, { MATROSKA_ID_CUECLUSTERPOSITION, EBML_UINT, 0, offsetof(MatroskaIndexPos,pos) }, + { MATROSKA_ID_CUERELATIVEPOSITION,EBML_NONE }, + { MATROSKA_ID_CUEDURATION, EBML_NONE }, { MATROSKA_ID_CUEBLOCKNUMBER, EBML_NONE }, { 0 } }; @@ -495,10 +554,23 @@ static EbmlSyntax matroska_segments[] = { { 0 } }; +static EbmlSyntax matroska_blockmore[] = { + { MATROSKA_ID_BLOCKADDID, EBML_UINT, 0, offsetof(MatroskaBlock,additional_id) }, + { MATROSKA_ID_BLOCKADDITIONAL, EBML_BIN, 0, offsetof(MatroskaBlock,additional) }, + { 0 } +}; + +static EbmlSyntax matroska_blockadditions[] = { + { MATROSKA_ID_BLOCKMORE, EBML_NEST, 0, 0, {.n=matroska_blockmore} }, + { 0 } +}; + static EbmlSyntax matroska_blockgroup[] = { { MATROSKA_ID_BLOCK, EBML_BIN, 0, offsetof(MatroskaBlock,bin) }, + { MATROSKA_ID_BLOCKADDITIONS, EBML_NEST, 0, 0, {.n=matroska_blockadditions} }, { MATROSKA_ID_SIMPLEBLOCK, EBML_BIN, 0, offsetof(MatroskaBlock,bin) }, - { MATROSKA_ID_BLOCKDURATION, EBML_UINT, 0, offsetof(MatroskaBlock,duration), {.u=AV_NOPTS_VALUE} }, + { MATROSKA_ID_BLOCKDURATION, EBML_UINT, 0, offsetof(MatroskaBlock,duration) }, + { MATROSKA_ID_DISCARDPADDING, EBML_UINT, 0, offsetof(MatroskaBlock,discard_padding) }, { MATROSKA_ID_BLOCKREFERENCE, EBML_UINT, 0, offsetof(MatroskaBlock,reference) }, { MATROSKA_ID_CODECSTATE, EBML_NONE }, { 1, EBML_UINT, 0, offsetof(MatroskaBlock,non_simple), {.u=1} }, @@ -571,7 +643,7 @@ static int matroska_resync(MatroskaDemuxContext *matroska, int64_t last_pos) id = avio_rb32(pb); // try to find a toplevel element - while (!pb->eof_reached) { + while (!url_feof(pb)) { if (id == MATROSKA_ID_INFO || id == MATROSKA_ID_TRACKS || id == MATROSKA_ID_CUES || id == MATROSKA_ID_TAGS || id == MATROSKA_ID_SEEKHEAD || id == MATROSKA_ID_ATTACHMENTS || @@ -623,7 +695,7 @@ static int ebml_read_num(MatroskaDemuxContext *matroska, AVIOContext *pb, * use it safely here to catch EOS. */ if (!(total = avio_r8(pb))) { /* we might encounter EOS here */ - if (!pb->eof_reached) { + if (!url_feof(pb)) { int64_t pos = avio_tell(pb); av_log(matroska->ctx, AV_LOG_ERROR, "Read error at pos. %"PRIu64" (0x%"PRIx64")\n", @@ -733,14 +805,15 @@ static int ebml_read_ascii(AVIOContext *pb, int size, char **str) */ static int ebml_read_binary(AVIOContext *pb, int length, EbmlBin *bin) { - av_free(bin->data); - if (!(bin->data = av_malloc(length))) + av_fast_padded_malloc(&bin->data, &bin->size, length); + if (!bin->data) return AVERROR(ENOMEM); bin->size = length; bin->pos = avio_tell(pb); if (avio_read(pb, bin->data, length) != length) { av_freep(&bin->data); + bin->size = 0; return AVERROR(EIO); } @@ -884,16 +957,15 @@ static int ebml_parse_elem(MatroskaDemuxContext *matroska, uint32_t id = syntax->id; uint64_t length; int res; + void *newelem; data = (char *)data + syntax->data_offset; if (syntax->list_elem_size) { EbmlList *list = data; - if ((res = av_reallocp_array(&list->elem, - list->nb_elem + 1, - syntax->list_elem_size)) < 0) { - list->nb_elem = 0; - return res; - } + newelem = av_realloc_array(list->elem, list->nb_elem+1, syntax->list_elem_size); + if (!newelem) + return AVERROR(ENOMEM); + list->elem = newelem; data = (char*)list->elem + list->nb_elem*syntax->list_elem_size; memset(data, 0, syntax->list_elem_size); list->nb_elem++; @@ -924,7 +996,10 @@ static int ebml_parse_elem(MatroskaDemuxContext *matroska, return ebml_parse_nest(matroska, syntax->def.n, data); case EBML_PASS: return ebml_parse_id(matroska, syntax->def.n, id, data); case EBML_STOP: return 1; - default: return avio_skip(pb,length)<0 ? AVERROR(EIO) : 0; + default: + if(ffio_limit(pb, length) != length) + return AVERROR(EIO); + return avio_skip(pb,length)<0 ? AVERROR(EIO) : 0; } if (res == AVERROR_INVALIDDATA) av_log(matroska->ctx, AV_LOG_ERROR, "Invalid element\n"); @@ -1028,7 +1103,7 @@ static int matroska_decode_buffer(uint8_t** buf, int* buf_size, int result = 0; int olen; - if (pkt_size >= 10000000) + if (pkt_size >= 10000000U) return AVERROR_INVALIDDATA; switch (encodings[0].compression.algo) { @@ -1036,6 +1111,11 @@ static int matroska_decode_buffer(uint8_t** buf, int* buf_size, int header_size = encodings[0].compression.settings.size; uint8_t *header = encodings[0].compression.settings.data; + if (header_size && !header) { + av_log(NULL, AV_LOG_ERROR, "Compression size but no data in headerstrip\n"); + return -1; + } + if (!header_size) return 0; @@ -1084,7 +1164,10 @@ static int matroska_decode_buffer(uint8_t** buf, int* buf_size, pkt_data = newpktdata; zstream.avail_out = pkt_size - zstream.total_out; zstream.next_out = pkt_data + zstream.total_out; - result = inflate(&zstream, Z_NO_FLUSH); + if (pkt_data) { + result = inflate(&zstream, Z_NO_FLUSH); + } else + result = Z_MEM_ERROR; } while (result==Z_OK && pkt_size<10000000); pkt_size = zstream.total_out; inflateEnd(&zstream); @@ -1115,7 +1198,10 @@ static int matroska_decode_buffer(uint8_t** buf, int* buf_size, pkt_data = newpktdata; bzstream.avail_out = pkt_size - bzstream.total_out_lo32; bzstream.next_out = pkt_data + bzstream.total_out_lo32; - result = BZ2_bzDecompress(&bzstream); + if (pkt_data) { + result = BZ2_bzDecompress(&bzstream); + } else + result = BZ_MEM_ERROR; } while (result==BZ_OK && pkt_size<10000000); pkt_size = bzstream.total_out_lo32; BZ2_bzDecompressEnd(&bzstream); @@ -1141,6 +1227,7 @@ static int matroska_decode_buffer(uint8_t** buf, int* buf_size, return result; } +#if FF_API_ASS_SSA static void matroska_fix_ass_packet(MatroskaDemuxContext *matroska, AVPacket *pkt, uint64_t display_duration) { @@ -1148,7 +1235,8 @@ static void matroska_fix_ass_packet(MatroskaDemuxContext *matroska, char *layer, *ptr = pkt->data, *end = ptr+pkt->size; for (; *ptr!=',' && ptr<end-1; ptr++); if (*ptr == ',') - layer = ++ptr; + ptr++; + layer = ptr; for (; *ptr!=',' && ptr<end-1; ptr++); if (*ptr == ',') { int64_t end_pts = pkt->pts + display_duration; @@ -1176,17 +1264,17 @@ static void matroska_fix_ass_packet(MatroskaDemuxContext *matroska, static int matroska_merge_packets(AVPacket *out, AVPacket *in) { - int old_size = out->size; int ret = av_grow_packet(out, in->size); if (ret < 0) return ret; - memcpy(out->data + old_size, in->data, in->size); + memcpy(out->data + out->size - in->size, in->data, in->size); av_free_packet(in); av_free(in); return 0; } +#endif static void matroska_convert_tag(AVFormatContext *s, EbmlList *list, AVDictionary **metadata, char *prefix) @@ -1328,26 +1416,20 @@ static void matroska_execute_seekhead(MatroskaDemuxContext *matroska) continue; } - if (matroska_parse_seekhead_entry(matroska, i) < 0) + if (matroska_parse_seekhead_entry(matroska, i) < 0) { + // mark index as broken + matroska->cues_parsing_deferred = -1; break; + } } } -static void matroska_parse_cues(MatroskaDemuxContext *matroska) { - EbmlList *seekhead_list = &matroska->seekhead; - MatroskaSeekhead *seekhead = seekhead_list->elem; +static void matroska_add_index_entries(MatroskaDemuxContext *matroska) { EbmlList *index_list; MatroskaIndex *index; int index_scale = 1; int i, j; - for (i = 0; i < seekhead_list->nb_elem; i++) - if (seekhead[i].id == MATROSKA_ID_CUES) - break; - assert(i <= seekhead_list->nb_elem); - - matroska_parse_seekhead_entry(matroska, i); - index_list = &matroska->index; index = index_list->elem; if (index_list->nb_elem @@ -1369,6 +1451,21 @@ static void matroska_parse_cues(MatroskaDemuxContext *matroska) { } } +static void matroska_parse_cues(MatroskaDemuxContext *matroska) { + EbmlList *seekhead_list = &matroska->seekhead; + MatroskaSeekhead *seekhead = seekhead_list->elem; + int i; + + for (i = 0; i < seekhead_list->nb_elem; i++) + if (seekhead[i].id == MATROSKA_ID_CUES) + break; + av_assert1(i <= seekhead_list->nb_elem); + + if (matroska_parse_seekhead_entry(matroska, i) < 0) + matroska->cues_parsing_deferred = -1; + matroska_add_index_entries(matroska); +} + static int matroska_aac_profile(char *codec_id) { static const char * const aac_profiles[] = { "MAIN", "LC", "SSR" }; @@ -1390,6 +1487,17 @@ static int matroska_aac_sri(int samplerate) return sri; } +static void matroska_metadata_creation_time(AVDictionary **metadata, int64_t date_utc) +{ + char buffer[32]; + /* Convert to seconds and adjust by number of seconds between 2001-01-01 and Epoch */ + time_t creation_time = date_utc / 1000000000 + 978307200; + struct tm *ptm = gmtime(&creation_time); + if (!ptm) return; + strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", ptm); + av_dict_set(metadata, "creation_time", buffer, 0); +} + static int matroska_read_header(AVFormatContext *s) { MatroskaDemuxContext *matroska = s->priv_data; @@ -1402,20 +1510,25 @@ static int matroska_read_header(AVFormatContext *s) int64_t pos; Ebml ebml = { 0 }; AVStream *st; - int i, j, res; + int i, j, k, res; matroska->ctx = s; /* First read the EBML header. */ if (ebml_parse(matroska, ebml_syntax, &ebml) || ebml.version > EBML_VERSION || ebml.max_size > sizeof(uint64_t) - || ebml.id_length > sizeof(uint32_t) || ebml.doctype_version > 2) { + || ebml.id_length > sizeof(uint32_t) || ebml.doctype_version > 3 || !ebml.doctype) { av_log(matroska->ctx, AV_LOG_ERROR, "EBML header using unsupported features\n" "(EBML version %"PRIu64", doctype %s, doc version %"PRIu64")\n", ebml.version, ebml.doctype, ebml.doctype_version); ebml_free(ebml_syntax, &ebml); return AVERROR_PATCHWELCOME; + } else if (ebml.doctype_version == 3) { + av_log(matroska->ctx, AV_LOG_WARNING, + "EBML header using unsupported features\n" + "(EBML version %"PRIu64", doctype %s, doc version %"PRIu64")\n", + ebml.version, ebml.doctype, ebml.doctype_version); } for (i = 0; i < FF_ARRAY_ELEMS(matroska_doctypes); i++) if (!strcmp(ebml.doctype, matroska_doctypes[i])) @@ -1449,21 +1562,27 @@ static int matroska_read_header(AVFormatContext *s) * 1000 / AV_TIME_BASE; av_dict_set(&s->metadata, "title", matroska->title, 0); + if (matroska->date_utc.size == 8) + matroska_metadata_creation_time(&s->metadata, AV_RB64(matroska->date_utc.data)); + tracks = matroska->tracks.elem; for (i=0; i < matroska->tracks.nb_elem; i++) { MatroskaTrack *track = &tracks[i]; enum AVCodecID codec_id = AV_CODEC_ID_NONE; - EbmlList *encodings_list = &tracks->encodings; + EbmlList *encodings_list = &track->encodings; MatroskaTrackEncoding *encodings = encodings_list->elem; uint8_t *extradata = NULL; int extradata_size = 0; int extradata_offset = 0; + uint32_t fourcc = 0; AVIOContext b; + char* key_id_base64 = NULL; /* Apply some sanity checks. */ if (track->type != MATROSKA_TRACK_TYPE_VIDEO && track->type != MATROSKA_TRACK_TYPE_AUDIO && - track->type != MATROSKA_TRACK_TYPE_SUBTITLE) { + track->type != MATROSKA_TRACK_TYPE_SUBTITLE && + track->type != MATROSKA_TRACK_TYPE_METADATA) { av_log(matroska->ctx, AV_LOG_INFO, "Unknown or unsupported track type %"PRIu64"\n", track->type); @@ -1475,10 +1594,12 @@ static int matroska_read_header(AVFormatContext *s) if (track->type == MATROSKA_TRACK_TYPE_VIDEO) { if (!track->default_duration && track->video.frame_rate > 0) track->default_duration = 1000000000/track->video.frame_rate; - if (!track->video.display_width) + if (track->video.display_width == -1) track->video.display_width = track->video.pixel_width; - if (!track->video.display_height) + if (track->video.display_height == -1) track->video.display_height = track->video.pixel_height; + if (track->video.color_space.size == 4) + fourcc = AV_RL32(track->video.color_space.data); } else if (track->type == MATROSKA_TRACK_TYPE_AUDIO) { if (!track->audio.out_samplerate) track->audio.out_samplerate = track->audio.samplerate; @@ -1487,8 +1608,24 @@ static int matroska_read_header(AVFormatContext *s) av_log(matroska->ctx, AV_LOG_ERROR, "Multiple combined encodings not supported"); } else if (encodings_list->nb_elem == 1) { - if (encodings[0].type || - ( + if (encodings[0].type) { + if (encodings[0].encryption.key_id.size > 0) { + /* Save the encryption key id to be stored later as a + metadata tag. */ + const int b64_size = AV_BASE64_SIZE(encodings[0].encryption.key_id.size); + key_id_base64 = av_malloc(b64_size); + if (key_id_base64 == NULL) + return AVERROR(ENOMEM); + + av_base64_encode(key_id_base64, b64_size, + encodings[0].encryption.key_id.data, + encodings[0].encryption.key_id.size); + } else { + encodings[0].scope = 0; + av_log(matroska->ctx, AV_LOG_ERROR, + "Unsupported encoding type"); + } + } else if ( #if CONFIG_ZLIB encodings[0].compression.algo != MATROSKA_TRACK_ENCODING_COMP_ZLIB && #endif @@ -1498,7 +1635,7 @@ static int matroska_read_header(AVFormatContext *s) #if CONFIG_LZO encodings[0].compression.algo != MATROSKA_TRACK_ENCODING_COMP_LZO && #endif - encodings[0].compression.algo != MATROSKA_TRACK_ENCODING_COMP_HEADERSTRIP)) { + encodings[0].compression.algo != MATROSKA_TRACK_ENCODING_COMP_HEADERSTRIP) { encodings[0].scope = 0; av_log(matroska->ctx, AV_LOG_ERROR, "Unsupported encoding type"); @@ -1528,15 +1665,23 @@ static int matroska_read_header(AVFormatContext *s) } st = track->stream = avformat_new_stream(s, NULL); - if (st == NULL) + if (st == NULL) { + av_free(key_id_base64); return AVERROR(ENOMEM); + } + + if (key_id_base64) { + /* export encryption key id as base64 metadata tag */ + av_dict_set(&st->metadata, "enc_key_id", key_id_base64, 0); + av_freep(&key_id_base64); + } if (!strcmp(track->codec_id, "V_MS/VFW/FOURCC") && track->codec_priv.size >= 40 && track->codec_priv.data != NULL) { track->ms_compat = 1; - track->video.fourcc = AV_RL32(track->codec_priv.data + 16); - codec_id = ff_codec_get_id(ff_codec_bmp_tags, track->video.fourcc); + fourcc = AV_RL32(track->codec_priv.data + 16); + codec_id = ff_codec_get_id(ff_codec_bmp_tags, fourcc); extradata_offset = 40; } else if (!strcmp(track->codec_id, "A_MS/ACM") && track->codec_priv.size >= 14 @@ -1552,8 +1697,8 @@ static int matroska_read_header(AVFormatContext *s) } else if (!strcmp(track->codec_id, "V_QUICKTIME") && (track->codec_priv.size >= 86) && (track->codec_priv.data != NULL)) { - track->video.fourcc = AV_RL32(track->codec_priv.data); - codec_id=ff_codec_get_id(ff_codec_movvideo_tags, track->video.fourcc); + fourcc = AV_RL32(track->codec_priv.data); + codec_id = ff_codec_get_id(ff_codec_movvideo_tags, fourcc); } else if (codec_id == AV_CODEC_ID_PCM_S16BE) { switch (track->audio.bitdepth) { case 8: codec_id = AV_CODEC_ID_PCM_U8; break; @@ -1584,7 +1729,7 @@ static int matroska_read_header(AVFormatContext *s) extradata_size = 5; } else extradata_size = 2; - } else if (codec_id == AV_CODEC_ID_ALAC && track->codec_priv.size) { + } else if (codec_id == AV_CODEC_ID_ALAC && track->codec_priv.size && track->codec_priv.size < INT_MAX - 12 - FF_INPUT_BUFFER_PADDING_SIZE) { /* Only ALAC's magic cookie is stored in Matroska's track headers. Create the "atom size", "tag", and "tag version" fields the decoder expects manually. */ @@ -1599,7 +1744,7 @@ static int matroska_read_header(AVFormatContext *s) track->codec_priv.size); } else if (codec_id == AV_CODEC_ID_TTA) { extradata_size = 30; - extradata = av_mallocz(extradata_size); + extradata = av_mallocz(extradata_size + FF_INPUT_BUFFER_PADDING_SIZE); if (extradata == NULL) return AVERROR(ENOMEM); ffio_init_context(&b, extradata, extradata_size, 1, @@ -1608,17 +1753,21 @@ static int matroska_read_header(AVFormatContext *s) avio_wl16(&b, 1); avio_wl16(&b, track->audio.channels); avio_wl16(&b, track->audio.bitdepth); + if (track->audio.out_samplerate < 0 || track->audio.out_samplerate > INT_MAX) + return AVERROR_INVALIDDATA; avio_wl32(&b, track->audio.out_samplerate); - avio_wl32(&b, matroska->ctx->duration * track->audio.out_samplerate); + avio_wl32(&b, av_rescale((matroska->duration * matroska->time_scale), track->audio.out_samplerate, AV_TIME_BASE * 1000)); } else if (codec_id == AV_CODEC_ID_RV10 || codec_id == AV_CODEC_ID_RV20 || codec_id == AV_CODEC_ID_RV30 || codec_id == AV_CODEC_ID_RV40) { extradata_offset = 26; } else if (codec_id == AV_CODEC_ID_RA_144) { track->audio.out_samplerate = 8000; track->audio.channels = 1; - } else if (codec_id == AV_CODEC_ID_RA_288 || codec_id == AV_CODEC_ID_COOK || - codec_id == AV_CODEC_ID_ATRAC3 || codec_id == AV_CODEC_ID_SIPR) { + } else if ((codec_id == AV_CODEC_ID_RA_288 || codec_id == AV_CODEC_ID_COOK || + codec_id == AV_CODEC_ID_ATRAC3 || codec_id == AV_CODEC_ID_SIPR) + && track->codec_priv.data) { int flavor; + ffio_init_context(&b, track->codec_priv.data,track->codec_priv.size, 0, NULL, NULL, NULL, NULL); avio_skip(&b, 22); @@ -1632,13 +1781,15 @@ static int matroska_read_header(AVFormatContext *s) track->audio.sub_packet_h <= 0 || track->audio.frame_size <= 0 || track->audio.sub_packet_size <= 0) return AVERROR_INVALIDDATA; - track->audio.buf = av_malloc(track->audio.frame_size * track->audio.sub_packet_h); + track->audio.buf = av_malloc_array(track->audio.sub_packet_h, track->audio.frame_size); + if (!track->audio.buf) + return AVERROR(ENOMEM); if (codec_id == AV_CODEC_ID_RA_288) { st->codec->block_align = track->audio.coded_framesize; track->codec_priv.size = 0; } else { if (codec_id == AV_CODEC_ID_SIPR && flavor < 4) { - const int sipr_bit_rate[4] = { 6504, 8496, 5000, 16000 }; + static const int sipr_bit_rate[4] = { 6504, 8496, 5000, 16000 }; track->audio.sub_packet_size = ff_sipr_subpk_size[flavor]; st->codec->bit_rate = sipr_bit_rate[flavor]; } @@ -1672,11 +1823,8 @@ static int matroska_read_header(AVFormatContext *s) st->codec->extradata = extradata; st->codec->extradata_size = extradata_size; } else if(track->codec_priv.data && track->codec_priv.size > 0){ - st->codec->extradata = av_mallocz(track->codec_priv.size + - FF_INPUT_BUFFER_PADDING_SIZE); - if(st->codec->extradata == NULL) + if (ff_alloc_extradata(st->codec, track->codec_priv.size)) return AVERROR(ENOMEM); - st->codec->extradata_size = track->codec_priv.size; memcpy(st->codec->extradata, track->codec_priv.data + extradata_offset, track->codec_priv.size); @@ -1684,8 +1832,10 @@ static int matroska_read_header(AVFormatContext *s) } if (track->type == MATROSKA_TRACK_TYPE_VIDEO) { + MatroskaTrackPlane *planes = track->operation.combine_planes.elem; + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; - st->codec->codec_tag = track->video.fourcc; + st->codec->codec_tag = fourcc; st->codec->width = track->video.pixel_width; st->codec->height = track->video.pixel_height; av_reduce(&st->sample_aspect_ratio.num, @@ -1693,22 +1843,76 @@ static int matroska_read_header(AVFormatContext *s) st->codec->height * track->video.display_width, st->codec-> width * track->video.display_height, 255); - if (st->codec->codec_id != AV_CODEC_ID_H264 && - st->codec->codec_id != AV_CODEC_ID_HEVC) + if (st->codec->codec_id != AV_CODEC_ID_HEVC) st->need_parsing = AVSTREAM_PARSE_HEADERS; + if (track->default_duration) { av_reduce(&st->avg_frame_rate.num, &st->avg_frame_rate.den, 1000000000, track->default_duration, 30000); +#if FF_API_R_FRAME_RATE + if (st->avg_frame_rate.num < st->avg_frame_rate.den * 1000L) + st->r_frame_rate = st->avg_frame_rate; +#endif + } + + /* export stereo mode flag as metadata tag */ + if (track->video.stereo_mode && track->video.stereo_mode < MATROSKA_VIDEO_STEREO_MODE_COUNT) + av_dict_set(&st->metadata, "stereo_mode", ff_matroska_video_stereo_mode[track->video.stereo_mode], 0); + + /* export alpha mode flag as metadata tag */ + if (track->video.alpha_mode) + av_dict_set(&st->metadata, "alpha_mode", "1", 0); + + /* if we have virtual track, mark the real tracks */ + for (j=0; j < track->operation.combine_planes.nb_elem; j++) { + char buf[32]; + if (planes[j].type >= MATROSKA_VIDEO_STEREO_PLANE_COUNT) + continue; + snprintf(buf, sizeof(buf), "%s_%d", + ff_matroska_video_stereo_plane[planes[j].type], i); + for (k=0; k < matroska->tracks.nb_elem; k++) + if (planes[j].uid == tracks[k].uid) { + av_dict_set(&s->streams[k]->metadata, + "stereo_mode", buf, 0); + break; + } } } else if (track->type == MATROSKA_TRACK_TYPE_AUDIO) { st->codec->codec_type = AVMEDIA_TYPE_AUDIO; st->codec->sample_rate = track->audio.out_samplerate; st->codec->channels = track->audio.channels; + st->codec->bits_per_coded_sample = track->audio.bitdepth; if (st->codec->codec_id != AV_CODEC_ID_AAC) st->need_parsing = AVSTREAM_PARSE_HEADERS; + if (track->codec_delay > 0) { + st->codec->delay = av_rescale_q(track->codec_delay, + (AVRational){1, 1000000000}, + (AVRational){1, st->codec->sample_rate}); + } + if (track->seek_preroll > 0) { + av_codec_set_seek_preroll(st->codec, + av_rescale_q(track->seek_preroll, + (AVRational){1, 1000000000}, + (AVRational){1, st->codec->sample_rate})); + } + } else if (codec_id == AV_CODEC_ID_WEBVTT) { + st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE; + + if (!strcmp(track->codec_id, "D_WEBVTT/CAPTIONS")) { + st->disposition |= AV_DISPOSITION_CAPTIONS; + } else if (!strcmp(track->codec_id, "D_WEBVTT/DESCRIPTIONS")) { + st->disposition |= AV_DISPOSITION_DESCRIPTIONS; + } else if (!strcmp(track->codec_id, "D_WEBVTT/METADATA")) { + st->disposition |= AV_DISPOSITION_METADATA; + } } else if (track->type == MATROSKA_TRACK_TYPE_SUBTITLE) { st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE; - if (st->codec->codec_id == AV_CODEC_ID_SSA) +#if FF_API_ASS_SSA + if (st->codec->codec_id == AV_CODEC_ID_SSA || + st->codec->codec_id == AV_CODEC_ID_ASS) +#else + if (st->codec->codec_id == AV_CODEC_ID_ASS) +#endif matroska->contains_ssa = 1; } } @@ -1726,10 +1930,8 @@ static int matroska_read_header(AVFormatContext *s) av_dict_set(&st->metadata, "mimetype", attachements[j].mime, 0); st->codec->codec_id = AV_CODEC_ID_NONE; st->codec->codec_type = AVMEDIA_TYPE_ATTACHMENT; - st->codec->extradata = av_malloc(attachements[j].bin.size); - if(st->codec->extradata == NULL) + if (ff_alloc_extradata(st->codec, attachements[j].bin.size)) break; - st->codec->extradata_size = attachements[j].bin.size; memcpy(st->codec->extradata, attachements[j].bin.data, attachements[j].bin.size); for (i=0; ff_mkv_mime_tags[i].id != AV_CODEC_ID_NONE; i++) { @@ -1756,6 +1958,8 @@ static int matroska_read_header(AVFormatContext *s) max_start = chapters[i].start; } + matroska_add_index_entries(matroska); + matroska_convert_tags(s); return 0; @@ -1825,7 +2029,7 @@ static int matroska_parse_laces(MatroskaDemuxContext *matroska, uint8_t **buf, return 0; } - assert(size > 0); + av_assert0(size > 0); *laces = *data + 1; data += 1; size -= 1; @@ -1839,18 +2043,18 @@ static int matroska_parse_laces(MatroskaDemuxContext *matroska, uint8_t **buf, uint32_t total = 0; for (n = 0; res == 0 && n < *laces - 1; n++) { while (1) { - if (size == 0) { - res = AVERROR_EOF; + if (size <= total) { + res = AVERROR_INVALIDDATA; break; } temp = *data; + total += temp; lace_size[n] += temp; data += 1; size -= 1; if (temp != 0xff) break; } - total += lace_size[n]; } if (size <= total) { res = AVERROR_INVALIDDATA; @@ -1874,10 +2078,10 @@ static int matroska_parse_laces(MatroskaDemuxContext *matroska, uint8_t **buf, uint64_t num; uint64_t total; n = matroska_ebmlnum_uint(matroska, data, size, &num); - if (n < 0) { + if (n < 0 || num > INT_MAX) { av_log(matroska->ctx, AV_LOG_INFO, "EBML block data error\n"); - res = n; + res = n<0 ? n : AVERROR_INVALIDDATA; break; } data += n; @@ -1887,10 +2091,10 @@ static int matroska_parse_laces(MatroskaDemuxContext *matroska, uint8_t **buf, int64_t snum; int r; r = matroska_ebmlnum_sint(matroska, data, size, &snum); - if (r < 0) { + if (r < 0 || lace_size[n - 1] + snum > (uint64_t)INT_MAX) { av_log(matroska->ctx, AV_LOG_INFO, "EBML block data error\n"); - res = r; + res = r<0 ? r : AVERROR_INVALIDDATA; break; } data += r; @@ -1918,7 +2122,7 @@ static int matroska_parse_rm_audio(MatroskaDemuxContext *matroska, MatroskaTrack *track, AVStream *st, uint8_t *data, int size, - uint64_t timecode, uint64_t duration, + uint64_t timecode, int64_t pos) { int a = st->codec->block_align; @@ -1949,7 +2153,7 @@ static int matroska_parse_rm_audio(MatroskaDemuxContext *matroska, } memcpy(track->audio.buf + y*w, data, w); } else { - if (size < sps * w / sps) { + if (size < sps * w / sps || h<=0) { av_log(matroska->ctx, AV_LOG_ERROR, "Corrupt generic RM-style audio packet size\n"); return AVERROR_INVALIDDATA; @@ -1967,8 +2171,11 @@ static int matroska_parse_rm_audio(MatroskaDemuxContext *matroska, } while (track->audio.pkt_cnt) { - AVPacket *pkt = av_mallocz(sizeof(AVPacket)); - av_new_packet(pkt, a); + AVPacket *pkt = NULL; + if (!(pkt = av_mallocz(sizeof(AVPacket))) || av_new_packet(pkt, a) < 0){ + av_free(pkt); + return AVERROR(ENOMEM); + } memcpy(pkt->data, track->audio.buf + a * (h*w / a - track->audio.pkt_cnt--), a); pkt->pts = track->audio.buf_timecode; @@ -2062,19 +2269,135 @@ fail: return ret; } +static int matroska_parse_webvtt(MatroskaDemuxContext *matroska, + MatroskaTrack *track, + AVStream *st, + uint8_t *data, int data_len, + uint64_t timecode, + uint64_t duration, + int64_t pos) +{ + AVPacket *pkt; + uint8_t *id, *settings, *text, *buf; + int id_len, settings_len, text_len; + uint8_t *p, *q; + int err; + + if (data_len <= 0) + return AVERROR_INVALIDDATA; + + p = data; + q = data + data_len; + + id = p; + id_len = -1; + while (p < q) { + if (*p == '\r' || *p == '\n') { + id_len = p - id; + if (*p == '\r') + p++; + break; + } + p++; + } + + if (p >= q || *p != '\n') + return AVERROR_INVALIDDATA; + p++; + + settings = p; + settings_len = -1; + while (p < q) { + if (*p == '\r' || *p == '\n') { + settings_len = p - settings; + if (*p == '\r') + p++; + break; + } + p++; + } + + if (p >= q || *p != '\n') + return AVERROR_INVALIDDATA; + p++; + + text = p; + text_len = q - p; + while (text_len > 0) { + const int len = text_len - 1; + const uint8_t c = p[len]; + if (c != '\r' && c != '\n') + break; + text_len = len; + } + + if (text_len <= 0) + return AVERROR_INVALIDDATA; + + pkt = av_mallocz(sizeof(*pkt)); + err = av_new_packet(pkt, text_len); + if (err < 0) { + av_free(pkt); + return AVERROR(err); + } + + memcpy(pkt->data, text, text_len); + + if (id_len > 0) { + buf = av_packet_new_side_data(pkt, + AV_PKT_DATA_WEBVTT_IDENTIFIER, + id_len); + if (buf == NULL) { + av_free(pkt); + return AVERROR(ENOMEM); + } + memcpy(buf, id, id_len); + } + + if (settings_len > 0) { + buf = av_packet_new_side_data(pkt, + AV_PKT_DATA_WEBVTT_SETTINGS, + settings_len); + if (buf == NULL) { + av_free(pkt); + return AVERROR(ENOMEM); + } + memcpy(buf, settings, settings_len); + } + + // Do we need this for subtitles? + // pkt->flags = AV_PKT_FLAG_KEY; + + pkt->stream_index = st->index; + pkt->pts = timecode; + + // Do we need this for subtitles? + // pkt->dts = timecode; + + pkt->duration = duration; + pkt->pos = pos; + + dynarray_add(&matroska->packets, &matroska->num_packets, pkt); + matroska->prev_pkt = pkt; + + return 0; +} + static int matroska_parse_frame(MatroskaDemuxContext *matroska, MatroskaTrack *track, AVStream *st, uint8_t *data, int pkt_size, - uint64_t timecode, uint64_t duration, - int64_t pos, int is_keyframe) + uint64_t timecode, uint64_t lace_duration, + int64_t pos, int is_keyframe, + uint8_t *additional, uint64_t additional_id, int additional_size, + uint64_t discard_padding) { MatroskaTrackEncoding *encodings = track->encodings.elem; uint8_t *pkt_data = data; int offset = 0, res; AVPacket *pkt; - if (encodings && encodings->scope & 1) { + if (encodings && !encodings->type && encodings->scope & 1) { res = matroska_decode_buffer(&pkt_data, &pkt_size, track); if (res < 0) return res; @@ -2099,7 +2422,8 @@ static int matroska_parse_frame(MatroskaDemuxContext *matroska, /* XXX: prevent data copy... */ if (av_new_packet(pkt, pkt_size + offset) < 0) { av_free(pkt); - return AVERROR(ENOMEM); + res = AVERROR(ENOMEM); + goto fail; } if (st->codec->codec_id == AV_CODEC_ID_PRORES) { @@ -2111,23 +2435,71 @@ static int matroska_parse_frame(MatroskaDemuxContext *matroska, memcpy(pkt->data + offset, pkt_data, pkt_size); if (pkt_data != data) - av_free(pkt_data); + av_freep(&pkt_data); pkt->flags = is_keyframe; pkt->stream_index = st->index; + if (additional_size > 0) { + uint8_t *side_data = av_packet_new_side_data(pkt, + AV_PKT_DATA_MATROSKA_BLOCKADDITIONAL, + additional_size + 8); + if(side_data == NULL) { + av_free_packet(pkt); + av_free(pkt); + return AVERROR(ENOMEM); + } + AV_WB64(side_data, additional_id); + memcpy(side_data + 8, additional, additional_size); + } + + if (discard_padding) { + uint8_t *side_data = av_packet_new_side_data(pkt, + AV_PKT_DATA_SKIP_SAMPLES, + 10); + if(side_data == NULL) { + av_free_packet(pkt); + av_free(pkt); + return AVERROR(ENOMEM); + } + AV_WL32(side_data, 0); + AV_WL32(side_data + 4, av_rescale_q(discard_padding, + (AVRational){1, 1000000000}, + (AVRational){1, st->codec->sample_rate})); + } + if (track->ms_compat) pkt->dts = timecode; else pkt->pts = timecode; pkt->pos = pos; - if (st->codec->codec_id == AV_CODEC_ID_TEXT) - pkt->convergence_duration = duration; - else if (track->type != MATROSKA_TRACK_TYPE_SUBTITLE) - pkt->duration = duration; + if (st->codec->codec_id == AV_CODEC_ID_SUBRIP) { + /* + * For backward compatibility. + * Historically, we have put subtitle duration + * in convergence_duration, on the off chance + * that the time_scale is less than 1us, which + * could result in a 32bit overflow on the + * normal duration field. + */ + pkt->convergence_duration = lace_duration; + } + + if (track->type != MATROSKA_TRACK_TYPE_SUBTITLE || + lace_duration <= INT_MAX) { + /* + * For non subtitle tracks, just store the duration + * as normal. + * + * If it's a subtitle track and duration value does + * not overflow a uint32, then also store it normally. + */ + pkt->duration = lace_duration; + } +#if FF_API_ASS_SSA if (st->codec->codec_id == AV_CODEC_ID_SSA) - matroska_fix_ass_packet(matroska, pkt, duration); + matroska_fix_ass_packet(matroska, pkt, lace_duration); if (matroska->prev_pkt && timecode != AV_NOPTS_VALUE && @@ -2139,6 +2511,10 @@ static int matroska_parse_frame(MatroskaDemuxContext *matroska, dynarray_add(&matroska->packets,&matroska->num_packets,pkt); matroska->prev_pkt = pkt; } +#else + dynarray_add(&matroska->packets, &matroska->num_packets, pkt); + matroska->prev_pkt = pkt; +#endif return 0; fail: @@ -2150,7 +2526,8 @@ fail: static int matroska_parse_block(MatroskaDemuxContext *matroska, uint8_t *data, int size, int64_t pos, uint64_t cluster_time, uint64_t block_duration, int is_keyframe, - int64_t cluster_pos) + uint8_t *additional, uint64_t additional_id, int additional_size, + int64_t cluster_pos, uint64_t discard_padding) { uint64_t timecode = AV_NOPTS_VALUE; MatroskaTrack *track; @@ -2159,7 +2536,8 @@ static int matroska_parse_block(MatroskaDemuxContext *matroska, uint8_t *data, int16_t block_time; uint32_t *lace_size = NULL; int n, flags, laces = 0; - uint64_t num, duration; + uint64_t num; + int trust_default_duration = 1; if ((n = matroska_ebmlnum_uint(matroska, data, size, &num)) < 0) { av_log(matroska->ctx, AV_LOG_ERROR, "EBML block data error\n"); @@ -2178,8 +2556,9 @@ static int matroska_parse_block(MatroskaDemuxContext *matroska, uint8_t *data, st = track->stream; if (st->discard >= AVDISCARD_ALL) return res; + av_assert1(block_duration != AV_NOPTS_VALUE); - block_time = AV_RB16(data); + block_time = sign_extend(AV_RB16(data), 16); data += 2; flags = *data++; size -= 3; @@ -2197,9 +2576,14 @@ static int matroska_parse_block(MatroskaDemuxContext *matroska, uint8_t *data, } if (matroska->skip_to_keyframe && track->type != MATROSKA_TRACK_TYPE_SUBTITLE) { - if (!is_keyframe || timecode < matroska->skip_to_timecode) + if (timecode < matroska->skip_to_timecode) return res; - matroska->skip_to_keyframe = 0; + if (is_keyframe) + matroska->skip_to_keyframe = 0; + else if (!st->skip_to_keyframe) { + av_log(matroska->ctx, AV_LOG_ERROR, "File is broken, keyframes not correctly marked!\n"); + matroska->skip_to_keyframe = 0; + } } res = matroska_parse_laces(matroska, &data, &size, (flags & 0x06) >> 1, @@ -2208,22 +2592,29 @@ static int matroska_parse_block(MatroskaDemuxContext *matroska, uint8_t *data, if (res) goto end; - if (block_duration != AV_NOPTS_VALUE) { - duration = block_duration / laces; - if (block_duration != duration * laces) { - av_log(matroska->ctx, AV_LOG_WARNING, - "Incorrect block_duration, possibly corrupted container"); + if (track->audio.samplerate == 8000) { + // If this is needed for more codecs, then add them here + if (st->codec->codec_id == AV_CODEC_ID_AC3) { + if(track->audio.samplerate != st->codec->sample_rate || !st->codec->frame_size) + trust_default_duration = 0; } - } else { - duration = track->default_duration / matroska->time_scale; - block_duration = duration * laces; } - if (timecode != AV_NOPTS_VALUE) + if (!block_duration && trust_default_duration) + block_duration = track->default_duration * laces / matroska->time_scale; + + if (cluster_time != (uint64_t)-1 && (block_time >= 0 || cluster_time >= -block_time)) track->end_timecode = FFMAX(track->end_timecode, timecode + block_duration); for (n = 0; n < laces; n++) { + int64_t lace_duration = block_duration*(n+1) / laces - block_duration*n / laces; + + if (lace_size[n] > size) { + av_log(matroska->ctx, AV_LOG_ERROR, "Invalid packet size\n"); + break; + } + if ((st->codec->codec_id == AV_CODEC_ID_RA_288 || st->codec->codec_id == AV_CODEC_ID_COOK || st->codec->codec_id == AV_CODEC_ID_SIPR || @@ -2232,21 +2623,32 @@ static int matroska_parse_block(MatroskaDemuxContext *matroska, uint8_t *data, res = matroska_parse_rm_audio(matroska, track, st, data, lace_size[n], - timecode, duration, pos); + timecode, pos); + if (res) + goto end; + + } else if (st->codec->codec_id == AV_CODEC_ID_WEBVTT) { + res = matroska_parse_webvtt(matroska, track, st, + data, lace_size[n], + timecode, lace_duration, + pos); if (res) goto end; } else { res = matroska_parse_frame(matroska, track, st, data, lace_size[n], - timecode, duration, - pos, !n? is_keyframe : 0); + timecode, lace_duration, + pos, !n? is_keyframe : 0, + additional, additional_id, additional_size, + discard_padding); if (res) goto end; } if (timecode != AV_NOPTS_VALUE) - timecode = duration ? timecode + duration : AV_NOPTS_VALUE; + timecode = lace_duration ? timecode + lace_duration : AV_NOPTS_VALUE; data += lace_size[n]; + size -= lace_size[n]; } end: @@ -2294,18 +2696,22 @@ static int matroska_parse_cluster_incremental(MatroskaDemuxContext *matroska) i = blocks_list->nb_elem - 1; if (blocks[i].bin.size > 0 && blocks[i].bin.data) { int is_keyframe = blocks[i].non_simple ? !blocks[i].reference : -1; + uint8_t* additional = blocks[i].additional.size > 0 ? + blocks[i].additional.data : NULL; if (!blocks[i].non_simple) - blocks[i].duration = AV_NOPTS_VALUE; + blocks[i].duration = 0; res = matroska_parse_block(matroska, blocks[i].bin.data, blocks[i].bin.size, blocks[i].bin.pos, matroska->current_cluster.timecode, blocks[i].duration, is_keyframe, - matroska->current_cluster_pos); + additional, blocks[i].additional_id, + blocks[i].additional.size, + matroska->current_cluster_pos, + blocks[i].discard_padding); } } - if (res < 0) matroska->done = 1; return res; } @@ -2325,16 +2731,14 @@ static int matroska_parse_cluster(MatroskaDemuxContext *matroska) res = ebml_parse(matroska, matroska_clusters, &cluster); blocks_list = &cluster.blocks; blocks = blocks_list->elem; - for (i=0; i<blocks_list->nb_elem && !res; i++) + for (i=0; i<blocks_list->nb_elem; i++) if (blocks[i].bin.size > 0 && blocks[i].bin.data) { int is_keyframe = blocks[i].non_simple ? !blocks[i].reference : -1; - if (!blocks[i].non_simple) - blocks[i].duration = AV_NOPTS_VALUE; res=matroska_parse_block(matroska, blocks[i].bin.data, blocks[i].bin.size, blocks[i].bin.pos, cluster.timecode, - blocks[i].duration, is_keyframe, - pos); + blocks[i].duration, is_keyframe, NULL, 0, 0, + pos, blocks[i].discard_padding); } ebml_free(matroska_cluster, &cluster); return res; @@ -2343,22 +2747,16 @@ static int matroska_parse_cluster(MatroskaDemuxContext *matroska) static int matroska_read_packet(AVFormatContext *s, AVPacket *pkt) { MatroskaDemuxContext *matroska = s->priv_data; - int ret = 0; - while (!ret && matroska_deliver_packet(matroska, pkt)) { + while (matroska_deliver_packet(matroska, pkt)) { int64_t pos = avio_tell(matroska->ctx->pb); if (matroska->done) return AVERROR_EOF; if (matroska_parse_cluster(matroska) < 0) - ret = matroska_resync(matroska, pos); + matroska_resync(matroska, pos); } - if (ret == AVERROR_INVALIDDATA && pkt->data) { - pkt->flags |= AV_PKT_FLAG_CORRUPT; - return 0; - } - - return ret; + return 0; } static int matroska_read_seek(AVFormatContext *s, int stream_index, @@ -2370,13 +2768,13 @@ static int matroska_read_seek(AVFormatContext *s, int stream_index, int i, index, index_sub, index_min; /* Parse the CUES now since we need the index data to seek. */ - if (matroska->cues_parsing_deferred) { - matroska_parse_cues(matroska); + if (matroska->cues_parsing_deferred > 0) { matroska->cues_parsing_deferred = 0; + matroska_parse_cues(matroska); } if (!st->nb_index_entries) - return 0; + goto err; timestamp = FFMAX(timestamp, st->index_entries[0].timestamp); if ((index = av_index_search_timestamp(st, timestamp, flags)) < 0) { @@ -2390,8 +2788,8 @@ static int matroska_read_seek(AVFormatContext *s, int stream_index, } matroska_clear_queue(matroska); - if (index < 0) - return 0; + if (index < 0 || (matroska->cues_parsing_deferred < 0 && index == st->nb_index_entries - 1)) + goto err; index_min = index; for (i=0; i < matroska->tracks.nb_elem; i++) { @@ -2400,22 +2798,40 @@ static int matroska_read_seek(AVFormatContext *s, int stream_index, tracks[i].audio.buf_timecode = AV_NOPTS_VALUE; tracks[i].end_timecode = 0; if (tracks[i].type == MATROSKA_TRACK_TYPE_SUBTITLE - && !tracks[i].stream->discard != AVDISCARD_ALL) { + && tracks[i].stream->discard != AVDISCARD_ALL) { index_sub = av_index_search_timestamp(tracks[i].stream, st->index_entries[index].timestamp, AVSEEK_FLAG_BACKWARD); - if (index_sub >= 0 - && st->index_entries[index_sub].pos < st->index_entries[index_min].pos - && st->index_entries[index].timestamp - st->index_entries[index_sub].timestamp < 30000000000/matroska->time_scale) - index_min = index_sub; + while(index_sub >= 0 + && index_min >= 0 + && tracks[i].stream->index_entries[index_sub].pos < st->index_entries[index_min].pos + && st->index_entries[index].timestamp - tracks[i].stream->index_entries[index_sub].timestamp < 30000000000/matroska->time_scale) + index_min--; } } avio_seek(s->pb, st->index_entries[index_min].pos, SEEK_SET); matroska->current_id = 0; - matroska->skip_to_keyframe = !(flags & AVSEEK_FLAG_ANY); - matroska->skip_to_timecode = st->index_entries[index].timestamp; + if (flags & AVSEEK_FLAG_ANY) { + st->skip_to_keyframe = 0; + matroska->skip_to_timecode = timestamp; + } else { + st->skip_to_keyframe = 1; + matroska->skip_to_timecode = st->index_entries[index].timestamp; + } + matroska->skip_to_keyframe = 1; matroska->done = 0; + matroska->num_levels = 0; ff_update_cur_dts(s, st, st->index_entries[index].timestamp); return 0; +err: + // slightly hackish but allows proper fallback to + // the generic seeking code. + matroska_clear_queue(matroska); + matroska->current_id = 0; + st->skip_to_keyframe = + matroska->skip_to_keyframe = 0; + matroska->done = 0; + matroska->num_levels = 0; + return -1; } static int matroska_read_close(AVFormatContext *s) diff --git a/libavformat/matroskaenc.c b/libavformat/matroskaenc.c index f3a35f8986..d8355b23cc 100644 --- a/libavformat/matroskaenc.c +++ b/libavformat/matroskaenc.c @@ -2,31 +2,33 @@ * Matroska muxer * Copyright (c) 2007 David Conrad * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "avc.h" #include "avformat.h" +#include "avio_internal.h" #include "avlanguage.h" #include "flacenc.h" #include "internal.h" #include "isom.h" #include "matroska.h" #include "riff.h" +#include "subtitles.h" #include "wv.h" #include "libavutil/avstring.h" @@ -38,9 +40,11 @@ #include "libavutil/opt.h" #include "libavutil/random_seed.h" #include "libavutil/samplefmt.h" +#include "libavutil/sha.h" #include "libavcodec/xiph.h" #include "libavcodec/mpeg4audio.h" +#include "libavcodec/internal.h" typedef struct ebml_master { int64_t pos; ///< absolute offset in the file where the master's elements start @@ -65,6 +69,8 @@ typedef struct { uint64_t pts; int tracknum; int64_t cluster_pos; ///< file offset of the cluster containing the block + int64_t relative_pos; ///< relative offset from the position of the cluster containing the block + int64_t duration; ///< duration of the block according to time base } mkv_cuepoint; typedef struct { @@ -75,6 +81,7 @@ typedef struct { typedef struct { int write_dts; + int has_cue; } mkv_track; #define MODE_MATROSKAv2 0x01 @@ -103,6 +110,8 @@ typedef struct MatroskaMuxContext { int cluster_size_limit; int64_t cues_pos; int64_t cluster_time_limit; + + uint32_t chapter_id_offset; int wrote_chapters; } MatroskaMuxContext; @@ -111,13 +120,16 @@ typedef struct MatroskaMuxContext { * offset, 4 bytes for target EBML ID */ #define MAX_SEEKENTRY_SIZE 21 -/** per-cuepoint-track - 3 1-byte EBML IDs, 3 1-byte EBML sizes, 2 +/** per-cuepoint-track - 5 1-byte EBML IDs, 5 1-byte EBML sizes, 4 * 8-byte uint max */ -#define MAX_CUETRACKPOS_SIZE 22 +#define MAX_CUETRACKPOS_SIZE 42 /** per-cuepoint - 2 1-byte EBML IDs, 2 1-byte EBML sizes, 8-byte uint max */ #define MAX_CUEPOINT_SIZE(num_tracks) 12 + MAX_CUETRACKPOS_SIZE*num_tracks +/** Seek preroll value for opus */ +#define OPUS_SEEK_PREROLL 80000000 + static int ebml_id_size(unsigned int id) { @@ -128,7 +140,7 @@ static void put_ebml_id(AVIOContext *pb, unsigned int id) { int i = ebml_id_size(id); while (i--) - avio_w8(pb, id >> (i*8)); + avio_w8(pb, (uint8_t)(id >> (i*8))); } /** @@ -138,10 +150,9 @@ static void put_ebml_id(AVIOContext *pb, unsigned int id) */ static void put_ebml_size_unknown(AVIOContext *pb, int bytes) { - assert(bytes <= 8); + av_assert0(bytes <= 8); avio_w8(pb, 0x1ff >> bytes); - while (--bytes) - avio_w8(pb, 0xff); + ffio_fill(pb, 0xff, bytes - 1); } /** @@ -165,18 +176,18 @@ static void put_ebml_num(AVIOContext *pb, uint64_t num, int bytes) int i, needed_bytes = ebml_num_size(num); // sizes larger than this are currently undefined in EBML - assert(num < (1ULL<<56)-1); + av_assert0(num < (1ULL<<56)-1); if (bytes == 0) // don't care how many bytes are used, so use the min bytes = needed_bytes; // the bytes needed to write the given size would exceed the bytes // that we need to use, so write unknown size. This shouldn't happen. - assert(bytes >= needed_bytes); + av_assert0(bytes >= needed_bytes); num |= 1ULL << bytes*7; for (i = bytes - 1; i >= 0; i--) - avio_w8(pb, num >> i*8); + avio_w8(pb, (uint8_t)(num >> i*8)); } static void put_ebml_uint(AVIOContext *pb, unsigned int elementid, uint64_t val) @@ -188,7 +199,7 @@ static void put_ebml_uint(AVIOContext *pb, unsigned int elementid, uint64_t val) put_ebml_id(pb, elementid); put_ebml_num(pb, bytes, 0); for (i = bytes - 1; i >= 0; i--) - avio_w8(pb, val >> i*8); + avio_w8(pb, (uint8_t)(val >> i*8)); } static void put_ebml_float(AVIOContext *pb, unsigned int elementid, double val) @@ -221,7 +232,7 @@ static void put_ebml_void(AVIOContext *pb, uint64_t size) { int64_t currentpos = avio_tell(pb); - assert(size >= 2); + av_assert0(size >= 2); put_ebml_id(pb, EBML_ID_VOID); // we need to subtract the length needed to store the size from the @@ -231,8 +242,7 @@ static void put_ebml_void(AVIOContext *pb, uint64_t size) put_ebml_num(pb, size-1, 0); else put_ebml_num(pb, size-9, 8); - while(avio_tell(pb) < currentpos + size) - avio_w8(pb, 0); + ffio_fill(pb, 0, currentpos + size - avio_tell(pb)); } static ebml_master start_ebml_master(AVIOContext *pb, unsigned int elementid, uint64_t expectedsize) @@ -255,9 +265,7 @@ static void end_ebml_master(AVIOContext *pb, ebml_master master) static void put_xiph_size(AVIOContext *pb, int size) { - int i; - for (i = 0; i < size / 255; i++) - avio_w8(pb, 255); + ffio_fill(pb, 255, size / 255); avio_w8(pb, size % 255); } @@ -294,17 +302,16 @@ static mkv_seekhead * mkv_start_seekhead(AVIOContext *pb, int64_t segment_offset static int mkv_add_seekhead_entry(mkv_seekhead *seekhead, unsigned int elementid, uint64_t filepos) { - int err; + mkv_seekhead_entry *entries = seekhead->entries; // don't store more elements than we reserved space for if (seekhead->max_entries > 0 && seekhead->max_entries <= seekhead->num_entries) return -1; - if ((err = av_reallocp_array(&seekhead->entries, seekhead->num_entries + 1, - sizeof(*seekhead->entries))) < 0) { - seekhead->num_entries = 0; - return err; - } + entries = av_realloc_array(entries, seekhead->num_entries + 1, sizeof(mkv_seekhead_entry)); + if (entries == NULL) + return AVERROR(ENOMEM); + seekhead->entries = entries; seekhead->entries[seekhead->num_entries].elementid = elementid; seekhead->entries[seekhead->num_entries++].segmentpos = filepos - seekhead->segment_offset; @@ -359,7 +366,7 @@ static int64_t mkv_write_seekhead(AVIOContext *pb, mkv_seekhead *seekhead) currentpos = seekhead->filepos; } fail: - av_free(seekhead->entries); + av_freep(&seekhead->entries); av_free(seekhead); return currentpos; @@ -375,27 +382,29 @@ static mkv_cues * mkv_start_cues(int64_t segment_offset) return cues; } -static int mkv_add_cuepoint(mkv_cues *cues, int stream, int64_t ts, int64_t cluster_pos) +static int mkv_add_cuepoint(mkv_cues *cues, int stream, int64_t ts, int64_t cluster_pos, int64_t relative_pos, + int64_t duration) { - int err; + mkv_cuepoint *entries = cues->entries; if (ts < 0) return 0; - if ((err = av_reallocp_array(&cues->entries, cues->num_entries + 1, - sizeof(*cues->entries))) < 0) { - cues->num_entries = 0; - return err; - } + entries = av_realloc_array(entries, cues->num_entries + 1, sizeof(mkv_cuepoint)); + if (entries == NULL) + return AVERROR(ENOMEM); + cues->entries = entries; cues->entries[cues->num_entries].pts = ts; cues->entries[cues->num_entries].tracknum = stream + 1; - cues->entries[cues->num_entries++].cluster_pos = cluster_pos - cues->segment_offset; + cues->entries[cues->num_entries].cluster_pos = cluster_pos - cues->segment_offset; + cues->entries[cues->num_entries].relative_pos = relative_pos; + cues->entries[cues->num_entries++].duration = duration; return 0; } -static int64_t mkv_write_cues(AVIOContext *pb, mkv_cues *cues, int num_tracks) +static int64_t mkv_write_cues(AVIOContext *pb, mkv_cues *cues, mkv_track *tracks, int num_tracks) { ebml_master cues_element; int64_t currentpos; @@ -414,10 +423,20 @@ static int64_t mkv_write_cues(AVIOContext *pb, mkv_cues *cues, int num_tracks) // put all the entries from different tracks that have the exact same // timestamp into the same CuePoint + for (j = 0; j < num_tracks; j++) + tracks[j].has_cue = 0; for (j = 0; j < cues->num_entries - i && entry[j].pts == pts; j++) { + int tracknum = entry[j].tracknum - 1; + av_assert0(tracknum>=0 && tracknum<num_tracks); + if (tracks[tracknum].has_cue) + continue; + tracks[tracknum].has_cue = 1; track_positions = start_ebml_master(pb, MATROSKA_ID_CUETRACKPOSITION, MAX_CUETRACKPOS_SIZE); - put_ebml_uint(pb, MATROSKA_ID_CUETRACK , entry[j].tracknum ); - put_ebml_uint(pb, MATROSKA_ID_CUECLUSTERPOSITION, entry[j].cluster_pos); + put_ebml_uint(pb, MATROSKA_ID_CUETRACK , entry[j].tracknum ); + put_ebml_uint(pb, MATROSKA_ID_CUECLUSTERPOSITION , entry[j].cluster_pos); + put_ebml_uint(pb, MATROSKA_ID_CUERELATIVEPOSITION, entry[j].relative_pos); + if (entry[j].duration != -1) + put_ebml_uint(pb, MATROSKA_ID_CUEDURATION , entry[j].duration); end_ebml_master(pb, track_positions); } i += j - 1; @@ -508,7 +527,7 @@ static int mkv_write_codecprivate(AVFormatContext *s, AVIOContext *pb, AVCodecCo avio_write(dyn_cp, codec->extradata + 12, codec->extradata_size - 12); } - else if (codec->extradata_size) + else if (codec->extradata_size && codec->codec_id != AV_CODEC_ID_TTA) avio_write(dyn_cp, codec->extradata, codec->extradata_size); } else if (codec->codec_type == AVMEDIA_TYPE_VIDEO) { if (qt_id) { @@ -520,8 +539,9 @@ static int mkv_write_codecprivate(AVFormatContext *s, AVIOContext *pb, AVCodecCo if (!codec->codec_tag) codec->codec_tag = ff_codec_get_tag(ff_codec_bmp_tags, codec->codec_id); if (!codec->codec_tag) { - av_log(s, AV_LOG_ERROR, "No bmp codec ID found.\n"); - ret = -1; + av_log(s, AV_LOG_ERROR, "No bmp codec tag found for codec %s\n", + avcodec_get_name(codec->codec_id)); + ret = AVERROR(EINVAL); } ff_put_bmp_header(dyn_cp, codec, ff_codec_bmp_tags, 0); @@ -531,8 +551,9 @@ static int mkv_write_codecprivate(AVFormatContext *s, AVIOContext *pb, AVCodecCo unsigned int tag; tag = ff_codec_get_tag(ff_codec_wav_tags, codec->codec_id); if (!tag) { - av_log(s, AV_LOG_ERROR, "No wav codec ID found.\n"); - ret = -1; + av_log(s, AV_LOG_ERROR, "No wav codec tag found for codec %s\n", + avcodec_get_name(codec->codec_id)); + ret = AVERROR(EINVAL); } if (!codec->codec_tag) codec->codec_tag = tag; @@ -552,7 +573,7 @@ static int mkv_write_tracks(AVFormatContext *s) MatroskaMuxContext *mkv = s->priv_data; AVIOContext *pb = s->pb; ebml_master tracks; - int i, j, ret; + int i, j, ret, default_stream_exists = 0; ret = mkv_add_seekhead_entry(mkv->main_seekhead, MATROSKA_ID_TRACKS, avio_tell(pb)); if (ret < 0) return ret; @@ -560,6 +581,10 @@ static int mkv_write_tracks(AVFormatContext *s) tracks = start_ebml_master(pb, MATROSKA_ID_TRACKS, 0); for (i = 0; i < s->nb_streams; i++) { AVStream *st = s->streams[i]; + default_stream_exists |= st->disposition & AV_DISPOSITION_DEFAULT; + } + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; AVCodecContext *codec = st->codec; ebml_master subinfo, track; int native_id = 0; @@ -567,6 +592,8 @@ static int mkv_write_tracks(AVFormatContext *s) int bit_depth = av_get_bits_per_sample(codec->codec_id); int sample_rate = codec->sample_rate; int output_sample_rate = 0; + int display_width_div = 1; + int display_height_div = 1; AVDictionaryEntry *tag; if (codec->codec_type == AVMEDIA_TYPE_ATTACHMENT) { @@ -576,6 +603,8 @@ static int mkv_write_tracks(AVFormatContext *s) if (!bit_depth) bit_depth = av_get_bytes_per_sample(codec->sample_fmt) << 3; + if (!bit_depth) + bit_depth = codec->bits_per_coded_sample; if (codec->codec_id == AV_CODEC_ID_AAC) get_aac_sample_rates(s, codec, &sample_rate, &output_sample_rate); @@ -588,34 +617,74 @@ static int mkv_write_tracks(AVFormatContext *s) if ((tag = av_dict_get(st->metadata, "title", NULL, 0))) put_ebml_string(pb, MATROSKA_ID_TRACKNAME, tag->value); tag = av_dict_get(st->metadata, "language", NULL, 0); - put_ebml_string(pb, MATROSKA_ID_TRACKLANGUAGE, tag ? tag->value:"und"); + if (mkv->mode != MODE_WEBM || codec->codec_id != AV_CODEC_ID_WEBVTT) { + put_ebml_string(pb, MATROSKA_ID_TRACKLANGUAGE, tag ? tag->value:"und"); + } else if (tag && tag->value) { + put_ebml_string(pb, MATROSKA_ID_TRACKLANGUAGE, tag->value); + } // The default value for TRACKFLAGDEFAULT is 1, so add element // if we need to clear it. - if (!(st->disposition & AV_DISPOSITION_DEFAULT)) + if (default_stream_exists && !(st->disposition & AV_DISPOSITION_DEFAULT)) put_ebml_uint(pb, MATROSKA_ID_TRACKFLAGDEFAULT, !!(st->disposition & AV_DISPOSITION_DEFAULT)); - // look for a codec ID string specific to mkv to use, - // if none are found, use AVI codes - for (j = 0; ff_mkv_codec_tags[j].id != AV_CODEC_ID_NONE; j++) { - if (ff_mkv_codec_tags[j].id == codec->codec_id) { - put_ebml_string(pb, MATROSKA_ID_CODECID, ff_mkv_codec_tags[j].str); - native_id = 1; - break; + if (st->disposition & AV_DISPOSITION_FORCED) + put_ebml_uint(pb, MATROSKA_ID_TRACKFLAGFORCED, 1); + + if (mkv->mode == MODE_WEBM && codec->codec_id == AV_CODEC_ID_WEBVTT) { + const char *codec_id; + if (st->disposition & AV_DISPOSITION_CAPTIONS) { + codec_id = "D_WEBVTT/CAPTIONS"; + native_id = MATROSKA_TRACK_TYPE_SUBTITLE; + } else if (st->disposition & AV_DISPOSITION_DESCRIPTIONS) { + codec_id = "D_WEBVTT/DESCRIPTIONS"; + native_id = MATROSKA_TRACK_TYPE_METADATA; + } else if (st->disposition & AV_DISPOSITION_METADATA) { + codec_id = "D_WEBVTT/METADATA"; + native_id = MATROSKA_TRACK_TYPE_METADATA; + } else { + codec_id = "D_WEBVTT/SUBTITLES"; + native_id = MATROSKA_TRACK_TYPE_SUBTITLE; + } + put_ebml_string(pb, MATROSKA_ID_CODECID, codec_id); + } else { + // look for a codec ID string specific to mkv to use, + // if none are found, use AVI codes + for (j = 0; ff_mkv_codec_tags[j].id != AV_CODEC_ID_NONE; j++) { + if (ff_mkv_codec_tags[j].id == codec->codec_id) { + put_ebml_string(pb, MATROSKA_ID_CODECID, ff_mkv_codec_tags[j].str); + native_id = 1; + break; + } } } + if (codec->codec_id == AV_CODEC_ID_OPUS) { + uint64_t codec_delay =av_rescale_q(codec->delay, + (AVRational){1, codec->sample_rate}, + (AVRational){1, 1000000000}); + put_ebml_uint(pb, MATROSKA_ID_CODECDELAY, codec_delay); + put_ebml_uint(pb, MATROSKA_ID_SEEKPREROLL, OPUS_SEEK_PREROLL); + + } + if (mkv->mode == MODE_WEBM && !(codec->codec_id == AV_CODEC_ID_VP8 || - codec->codec_id == AV_CODEC_ID_VORBIS)) { + codec->codec_id == AV_CODEC_ID_VP9 || + ((codec->codec_id == AV_CODEC_ID_OPUS)&&(codec->strict_std_compliance <= FF_COMPLIANCE_EXPERIMENTAL)) || + codec->codec_id == AV_CODEC_ID_VORBIS || + codec->codec_id == AV_CODEC_ID_WEBVTT)) { av_log(s, AV_LOG_ERROR, - "Only VP8 video and Vorbis audio are supported for WebM.\n"); + "Only VP8,VP9 video and Vorbis,Opus(experimental, use -strict -2) audio and WebVTT subtitles are supported for WebM.\n"); return AVERROR(EINVAL); } switch (codec->codec_type) { case AVMEDIA_TYPE_VIDEO: put_ebml_uint(pb, MATROSKA_ID_TRACKTYPE, MATROSKA_TRACK_TYPE_VIDEO); - put_ebml_uint(pb, MATROSKA_ID_TRACKDEFAULTDURATION, av_q2d(codec->time_base)*1E9); + if(st->avg_frame_rate.num && st->avg_frame_rate.den && 1.0/av_q2d(st->avg_frame_rate) > av_q2d(codec->time_base)) + put_ebml_uint(pb, MATROSKA_ID_TRACKDEFAULTDURATION, 1E9/av_q2d(st->avg_frame_rate)); + else + put_ebml_uint(pb, MATROSKA_ID_TRACKDEFAULTDURATION, av_q2d(codec->time_base)*1E9); if (!native_id && ff_codec_get_tag(ff_codec_movvideo_tags, codec->codec_id) && @@ -637,30 +706,64 @@ static int mkv_write_tracks(AVFormatContext *s) // XXX: interlace flag? put_ebml_uint (pb, MATROSKA_ID_VIDEOPIXELWIDTH , codec->width); put_ebml_uint (pb, MATROSKA_ID_VIDEOPIXELHEIGHT, codec->height); - if ((tag = av_dict_get(s->metadata, "stereo_mode", NULL, 0))) { - uint8_t stereo_fmt = atoi(tag->value); - int valid_fmt = 0; - - switch (mkv->mode) { - case MODE_WEBM: - if (stereo_fmt <= MATROSKA_VIDEO_STEREOMODE_TYPE_TOP_BOTTOM - || stereo_fmt == MATROSKA_VIDEO_STEREOMODE_TYPE_RIGHT_LEFT) - valid_fmt = 1; + + if ((tag = av_dict_get(st->metadata, "stereo_mode", NULL, 0)) || + (tag = av_dict_get( s->metadata, "stereo_mode", NULL, 0))) { + // save stereo mode flag + uint64_t st_mode = MATROSKA_VIDEO_STEREO_MODE_COUNT; + + for (j=0; j<MATROSKA_VIDEO_STEREO_MODE_COUNT; j++) + if (!strcmp(tag->value, ff_matroska_video_stereo_mode[j])){ + st_mode = j; + break; + } + + if ((mkv->mode == MODE_WEBM && st_mode > 3 && st_mode != 11) + || st_mode >= MATROSKA_VIDEO_STEREO_MODE_COUNT) { + av_log(s, AV_LOG_ERROR, + "The specified stereo mode is not valid.\n"); + return AVERROR(EINVAL); + } else + put_ebml_uint(pb, MATROSKA_ID_VIDEOSTEREOMODE, st_mode); + + switch (st_mode) { + case 1: + case 8: + case 9: + case 11: + display_width_div = 2; break; - case MODE_MATROSKAv2: - if (stereo_fmt <= MATROSKA_VIDEO_STEREOMODE_TYPE_BOTH_EYES_BLOCK_RL) - valid_fmt = 1; + case 2: + case 3: + case 6: + case 7: + display_height_div = 2; break; } + } - if (valid_fmt) - put_ebml_uint (pb, MATROSKA_ID_VIDEOSTEREOMODE, stereo_fmt); + if ((tag = av_dict_get(st->metadata, "alpha_mode", NULL, 0)) || + (tag = av_dict_get( s->metadata, "alpha_mode", NULL, 0)) || + (codec->pix_fmt == AV_PIX_FMT_YUVA420P)) { + put_ebml_uint(pb, MATROSKA_ID_VIDEOALPHAMODE, 1); } + if (st->sample_aspect_ratio.num) { - int d_width = codec->width*av_q2d(st->sample_aspect_ratio); - put_ebml_uint(pb, MATROSKA_ID_VIDEODISPLAYWIDTH , d_width); - put_ebml_uint(pb, MATROSKA_ID_VIDEODISPLAYHEIGHT, codec->height); - put_ebml_uint(pb, MATROSKA_ID_VIDEODISPLAYUNIT, 3); + int64_t d_width = av_rescale(codec->width, st->sample_aspect_ratio.num, st->sample_aspect_ratio.den); + if (d_width > INT_MAX) { + av_log(s, AV_LOG_ERROR, "Overflow in display width\n"); + return AVERROR(EINVAL); + } + put_ebml_uint(pb, MATROSKA_ID_VIDEODISPLAYWIDTH , d_width / display_width_div); + put_ebml_uint(pb, MATROSKA_ID_VIDEODISPLAYHEIGHT, codec->height / display_height_div); + } else if (display_width_div != 1 || display_height_div != 1) { + put_ebml_uint(pb, MATROSKA_ID_VIDEODISPLAYWIDTH , codec->width / display_width_div); + put_ebml_uint(pb, MATROSKA_ID_VIDEODISPLAYHEIGHT, codec->height / display_height_div); + } + + if (codec->codec_id == AV_CODEC_ID_RAWVIDEO) { + uint32_t color_space = av_le2ne32(codec->codec_tag); + put_ebml_binary(pb, MATROSKA_ID_VIDEOCOLORSPACE, &color_space, sizeof(color_space)); } end_ebml_master(pb, subinfo); break; @@ -683,18 +786,25 @@ static int mkv_write_tracks(AVFormatContext *s) break; case AVMEDIA_TYPE_SUBTITLE: - put_ebml_uint(pb, MATROSKA_ID_TRACKTYPE, MATROSKA_TRACK_TYPE_SUBTITLE); if (!native_id) { av_log(s, AV_LOG_ERROR, "Subtitle codec %d is not supported.\n", codec->codec_id); return AVERROR(ENOSYS); } + + if (mkv->mode != MODE_WEBM || codec->codec_id != AV_CODEC_ID_WEBVTT) + native_id = MATROSKA_TRACK_TYPE_SUBTITLE; + + put_ebml_uint(pb, MATROSKA_ID_TRACKTYPE, native_id); break; default: av_log(s, AV_LOG_ERROR, "Only audio, video, and subtitles are supported for Matroska.\n"); - break; + return AVERROR(EINVAL); + } + + if (mkv->mode != MODE_WEBM || codec->codec_id != AV_CODEC_ID_WEBVTT) { + ret = mkv_write_codecprivate(s, pb, codec, native_id, qt_id); + if (ret < 0) return ret; } - ret = mkv_write_codecprivate(s, pb, codec, native_id, qt_id); - if (ret < 0) return ret; end_ebml_master(pb, track); @@ -729,7 +839,7 @@ static int mkv_write_chapters(AVFormatContext *s) AVDictionaryEntry *t = NULL; chapteratom = start_ebml_master(pb, MATROSKA_ID_CHAPTERATOM, 0); - put_ebml_uint(pb, MATROSKA_ID_CHAPTERUID, c->id); + put_ebml_uint(pb, MATROSKA_ID_CHAPTERUID, c->id + mkv->chapter_id_offset); put_ebml_uint(pb, MATROSKA_ID_CHAPTERTIMESTART, av_rescale_q(c->start, c->time_base, scale)); put_ebml_uint(pb, MATROSKA_ID_CHAPTERTIMEEND, @@ -803,21 +913,33 @@ static int mkv_write_tag(AVFormatContext *s, AVDictionary *m, unsigned int eleme end_ebml_master(s->pb, targets); while ((t = av_dict_get(m, "", t, AV_DICT_IGNORE_SUFFIX))) - if (av_strcasecmp(t->key, "title")) + if (av_strcasecmp(t->key, "title") && av_strcasecmp(t->key, "stereo_mode")) mkv_write_simpletag(s->pb, t); end_ebml_master(s->pb, tag); return 0; } +static int mkv_check_tag(AVDictionary *m) +{ + AVDictionaryEntry *t = NULL; + + while ((t = av_dict_get(m, "", t, AV_DICT_IGNORE_SUFFIX))) + if (av_strcasecmp(t->key, "title") && av_strcasecmp(t->key, "stereo_mode")) + return 1; + + return 0; +} + static int mkv_write_tags(AVFormatContext *s) { + MatroskaMuxContext *mkv = s->priv_data; ebml_master tags = {0}; int i, ret; ff_metadata_conv_ctx(s, ff_mkv_metadata_conv, NULL); - if (av_dict_get(s->metadata, "", NULL, AV_DICT_IGNORE_SUFFIX)) { + if (mkv_check_tag(s->metadata)) { ret = mkv_write_tag(s, s->metadata, 0, 0, &tags); if (ret < 0) return ret; } @@ -825,7 +947,7 @@ static int mkv_write_tags(AVFormatContext *s) for (i = 0; i < s->nb_streams; i++) { AVStream *st = s->streams[i]; - if (!av_dict_get(st->metadata, "", 0, AV_DICT_IGNORE_SUFFIX)) + if (!mkv_check_tag(st->metadata)) continue; ret = mkv_write_tag(s, st->metadata, MATROSKA_ID_TAGTARGETS_TRACKUID, i + 1, &tags); @@ -835,10 +957,10 @@ static int mkv_write_tags(AVFormatContext *s) for (i = 0; i < s->nb_chapters; i++) { AVChapter *ch = s->chapters[i]; - if (!av_dict_get(ch->metadata, "", NULL, AV_DICT_IGNORE_SUFFIX)) + if (!mkv_check_tag(ch->metadata)) continue; - ret = mkv_write_tag(s, ch->metadata, MATROSKA_ID_TAGTARGETS_CHAPTERUID, ch->id, &tags); + ret = mkv_write_tag(s, ch->metadata, MATROSKA_ID_TAGTARGETS_CHAPTERUID, ch->id + mkv->chapter_id_offset, &tags); if (ret < 0) return ret; } @@ -870,6 +992,7 @@ static int mkv_write_attachments(AVFormatContext *s) ebml_master attached_file; AVDictionaryEntry *t; const char *mimetype = NULL; + uint64_t fileuid; if (st->codec->codec_type != AVMEDIA_TYPE_ATTACHMENT) continue; @@ -899,9 +1022,25 @@ static int mkv_write_attachments(AVFormatContext *s) return AVERROR(EINVAL); } + if (st->codec->flags & CODEC_FLAG_BITEXACT) { + struct AVSHA *sha = av_sha_alloc(); + uint8_t digest[20]; + if (!sha) + return AVERROR(ENOMEM); + av_sha_init(sha, 160); + av_sha_update(sha, st->codec->extradata, st->codec->extradata_size); + av_sha_final(sha, digest); + av_free(sha); + fileuid = AV_RL64(digest); + } else { + fileuid = av_lfg_get(&c); + } + av_log(s, AV_LOG_VERBOSE, "Using %.16"PRIx64" for attachment %d\n", + fileuid, i); + put_ebml_string(pb, MATROSKA_ID_FILEMIMETYPE, mimetype); put_ebml_binary(pb, MATROSKA_ID_FILEDATA, st->codec->extradata, st->codec->extradata_size); - put_ebml_uint(pb, MATROSKA_ID_FILEUID, av_lfg_get(&c)); + put_ebml_uint(pb, MATROSKA_ID_FILEUID, fileuid); end_ebml_master(pb, attached_file); } end_ebml_master(pb, attachments); @@ -920,6 +1059,22 @@ static int mkv_write_header(AVFormatContext *s) if (!strcmp(s->oformat->name, "webm")) mkv->mode = MODE_WEBM; else mkv->mode = MODE_MATROSKAv2; + if (s->avoid_negative_ts < 0) + s->avoid_negative_ts = 1; + + for (i = 0; i < s->nb_streams; i++) + if (s->streams[i]->codec->codec_id == AV_CODEC_ID_ATRAC3 || + s->streams[i]->codec->codec_id == AV_CODEC_ID_COOK || + s->streams[i]->codec->codec_id == AV_CODEC_ID_RA_288 || + s->streams[i]->codec->codec_id == AV_CODEC_ID_SIPR || + s->streams[i]->codec->codec_id == AV_CODEC_ID_RV10 || + s->streams[i]->codec->codec_id == AV_CODEC_ID_RV20) { + av_log(s, AV_LOG_ERROR, + "The Matroska muxer does not yet support muxing %s\n", + avcodec_get_name(s->streams[i]->codec->codec_id)); + return AVERROR_PATCHWELCOME; + } + mkv->tracks = av_mallocz(s->nb_streams * sizeof(*mkv->tracks)); if (!mkv->tracks) return AVERROR(ENOMEM); @@ -930,7 +1085,7 @@ static int mkv_write_header(AVFormatContext *s) put_ebml_uint (pb, EBML_ID_EBMLMAXIDLENGTH , 4); put_ebml_uint (pb, EBML_ID_EBMLMAXSIZELENGTH , 8); put_ebml_string (pb, EBML_ID_DOCTYPE , s->oformat->name); - put_ebml_uint (pb, EBML_ID_DOCTYPEVERSION , 2); + put_ebml_uint (pb, EBML_ID_DOCTYPEVERSION , 4); put_ebml_uint (pb, EBML_ID_DOCTYPEREADVERSION , 2); end_ebml_master(pb, ebml_header); @@ -965,6 +1120,18 @@ static int mkv_write_header(AVFormatContext *s) put_ebml_string(pb, MATROSKA_ID_MUXINGAPP , LIBAVFORMAT_IDENT); put_ebml_string(pb, MATROSKA_ID_WRITINGAPP, LIBAVFORMAT_IDENT); put_ebml_binary(pb, MATROSKA_ID_SEGMENTUID, segment_uid, 16); + } else { + const char *ident = "Lavf"; + put_ebml_string(pb, MATROSKA_ID_MUXINGAPP , ident); + put_ebml_string(pb, MATROSKA_ID_WRITINGAPP, ident); + } + + if (tag = av_dict_get(s->metadata, "creation_time", NULL, 0)) { + // Adjust time so it's relative to 2001-01-01 and convert to nanoseconds. + int64_t date_utc = (ff_iso8601_to_unix_time(tag->value) - 978307200) * 1000000000; + uint8_t date_utc_buf[8]; + AV_WB64(date_utc_buf, date_utc); + put_ebml_binary(pb, MATROSKA_ID_DATEUTC, date_utc_buf, 8); } // reserve space for the duration @@ -976,6 +1143,9 @@ static int mkv_write_header(AVFormatContext *s) ret = mkv_write_tracks(s); if (ret < 0) return ret; + for (i = 0; i < s->nb_chapters; i++) + mkv->chapter_id_offset = FFMAX(mkv->chapter_id_offset, 1LL - s->chapters[i]->id); + if (mkv->mode != MODE_WEBM) { ret = mkv_write_chapters(s); if (ret < 0) return ret; @@ -1001,6 +1171,7 @@ static int mkv_write_header(AVFormatContext *s) av_init_packet(&mkv->cur_audio_pkt); mkv->cur_audio_pkt.size = 0; + mkv->cluster_pos = -1; avio_flush(pb); @@ -1040,11 +1211,12 @@ static int ass_get_duration(const uint8_t *p) if (sscanf(p, "%*[^,],%d:%d:%d%*c%d,%d:%d:%d%*c%d", &sh, &sm, &ss, &sc, &eh, &em, &es, &ec) != 8) return 0; - start = 3600000*sh + 60000*sm + 1000*ss + 10*sc; - end = 3600000*eh + 60000*em + 1000*es + 10*ec; + start = 3600000LL*sh + 60000LL*sm + 1000LL*ss + 10LL*sc; + end = 3600000LL*eh + 60000LL*em + 1000LL*es + 10LL*ec; return end - start; } +#if FF_API_ASS_SSA static int mkv_write_ass_blocks(AVFormatContext *s, AVIOContext *pb, AVPacket *pkt) { MatroskaMuxContext *mkv = s->priv_data; @@ -1089,6 +1261,7 @@ static int mkv_write_ass_blocks(AVFormatContext *s, AVIOContext *pb, AVPacket *p return max_duration; } +#endif static int mkv_strip_wavpack(const uint8_t *src, uint8_t **pdst, int *size) { @@ -1148,9 +1321,11 @@ static void mkv_write_block(AVFormatContext *s, AVIOContext *pb, { MatroskaMuxContext *mkv = s->priv_data; AVCodecContext *codec = s->streams[pkt->stream_index]->codec; - uint8_t *data = NULL; - int offset = 0, size = pkt->size; + uint8_t *data = NULL, *side_data = NULL; + int offset = 0, size = pkt->size, side_data_size = 0; int64_t ts = mkv->tracks[pkt->stream_index].write_dts ? pkt->dts : pkt->pts; + uint64_t additional_id = 0, discard_padding = 0; + ebml_master block_group, block_additions, block_more; av_log(s, AV_LOG_DEBUG, "Writing block at offset %" PRIu64 ", size %d, " "pts %" PRId64 ", dts %" PRId64 ", duration %d, flags %d\n", @@ -1174,6 +1349,30 @@ static void mkv_write_block(AVFormatContext *s, AVIOContext *pb, offset = 8; } + side_data = av_packet_get_side_data(pkt, + AV_PKT_DATA_SKIP_SAMPLES, + &side_data_size); + + if (side_data && side_data_size >= 10) { + discard_padding = av_rescale_q(AV_RL32(side_data + 4), + (AVRational){1, codec->sample_rate}, + (AVRational){1, 1000000000}); + } + + side_data = av_packet_get_side_data(pkt, + AV_PKT_DATA_MATROSKA_BLOCKADDITIONAL, + &side_data_size); + if (side_data) { + additional_id = AV_RB64(side_data); + side_data += 8; + side_data_size -= 8; + } + + if ((side_data_size && additional_id == 1) || discard_padding) { + block_group = start_ebml_master(pb, MATROSKA_ID_BLOCKGROUP, 0); + blockid = MATROSKA_ID_BLOCK; + } + put_ebml_id(pb, blockid); put_ebml_num(pb, size+4, 0); avio_w8(pb, 0x80 | (pkt->stream_index + 1)); // this assumes stream_index is less than 126 @@ -1182,6 +1381,24 @@ static void mkv_write_block(AVFormatContext *s, AVIOContext *pb, avio_write(pb, data + offset, size); if (data != pkt->data) av_free(data); + + if (discard_padding) { + put_ebml_uint(pb, MATROSKA_ID_DISCARDPADDING, discard_padding); + } + + if (side_data_size && additional_id == 1) { + block_additions = start_ebml_master(pb, MATROSKA_ID_BLOCKADDITIONS, 0); + block_more = start_ebml_master(pb, MATROSKA_ID_BLOCKMORE, 0); + put_ebml_uint(pb, MATROSKA_ID_BLOCKADDID, 1); + put_ebml_id(pb, MATROSKA_ID_BLOCKADDITIONAL); + put_ebml_num(pb, side_data_size, 0); + avio_write(pb, side_data, side_data_size); + end_ebml_master(pb, block_more); + end_ebml_master(pb, block_additions); + } + if ((side_data_size && additional_id == 1) || discard_padding) { + end_ebml_master(pb, block_group); + } } static int srt_get_duration(uint8_t **buf) @@ -1198,7 +1415,7 @@ static int srt_get_duration(uint8_t **buf) s_hsec += 1000*s_sec; e_hsec += 1000*e_sec; duration = e_hsec - s_hsec; } - *buf += strcspn(*buf, "\n") + 1; + *buf += ff_subtitles_next_line(*buf); } return duration; } @@ -1219,6 +1436,44 @@ static int mkv_write_srt_blocks(AVFormatContext *s, AVIOContext *pb, AVPacket *p return duration; } +static int mkv_write_vtt_blocks(AVFormatContext *s, AVIOContext *pb, AVPacket *pkt) +{ + MatroskaMuxContext *mkv = s->priv_data; + ebml_master blockgroup; + int id_size, settings_size, size; + uint8_t *id, *settings; + int64_t ts = mkv->tracks[pkt->stream_index].write_dts ? pkt->dts : pkt->pts; + const int flags = 0; + + id_size = 0; + id = av_packet_get_side_data(pkt, AV_PKT_DATA_WEBVTT_IDENTIFIER, + &id_size); + + settings_size = 0; + settings = av_packet_get_side_data(pkt, AV_PKT_DATA_WEBVTT_SETTINGS, + &settings_size); + + size = id_size + 1 + settings_size + 1 + pkt->size; + + av_log(s, AV_LOG_DEBUG, "Writing block at offset %" PRIu64 ", size %d, " + "pts %" PRId64 ", dts %" PRId64 ", duration %d, flags %d\n", + avio_tell(pb), size, pkt->pts, pkt->dts, pkt->duration, flags); + + blockgroup = start_ebml_master(pb, MATROSKA_ID_BLOCKGROUP, mkv_blockgroup_size(size)); + + put_ebml_id(pb, MATROSKA_ID_BLOCK); + put_ebml_num(pb, size+4, 0); + avio_w8(pb, 0x80 | (pkt->stream_index + 1)); // this assumes stream_index is less than 126 + avio_wb16(pb, ts - mkv->cluster_pts); + avio_w8(pb, flags); + avio_printf(pb, "%.*s\n%.*s\n%.*s", id_size, id, settings_size, settings, pkt->size, pkt->data); + + put_ebml_uint(pb, MATROSKA_ID_BLOCKDURATION, pkt->duration); + end_ebml_master(pb, blockgroup); + + return pkt->duration; +} + static void mkv_flush_dynbuf(AVFormatContext *s) { MatroskaMuxContext *mkv = s->priv_data; @@ -1243,6 +1498,7 @@ static int mkv_write_packet_internal(AVFormatContext *s, AVPacket *pkt) int duration = pkt->duration; int ret; int64_t ts = mkv->tracks[pkt->stream_index].write_dts ? pkt->dts : pkt->pts; + int64_t relative_packet_pos; if (ts == AV_NOPTS_VALUE) { av_log(s, AV_LOG_ERROR, "Can't write packet with unknown timestamp\n"); @@ -1250,34 +1506,48 @@ static int mkv_write_packet_internal(AVFormatContext *s, AVPacket *pkt) } if (!s->pb->seekable) { - if (!mkv->dyn_bc) - avio_open_dyn_buf(&mkv->dyn_bc); + if (!mkv->dyn_bc) { + if ((ret = avio_open_dyn_buf(&mkv->dyn_bc)) < 0) { + av_log(s, AV_LOG_ERROR, "Failed to open dynamic buffer\n"); + return ret; + } + } pb = mkv->dyn_bc; } - if (!mkv->cluster_pos) { + if (mkv->cluster_pos == -1) { mkv->cluster_pos = avio_tell(s->pb); mkv->cluster = start_ebml_master(pb, MATROSKA_ID_CLUSTER, 0); put_ebml_uint(pb, MATROSKA_ID_CLUSTERTIMECODE, FFMAX(0, ts)); mkv->cluster_pts = FFMAX(0, ts); } + relative_packet_pos = avio_tell(s->pb) - mkv->cluster.pos; + if (codec->codec_type != AVMEDIA_TYPE_SUBTITLE) { mkv_write_block(s, pb, MATROSKA_ID_SIMPLEBLOCK, pkt, keyframe << 7); +#if FF_API_ASS_SSA } else if (codec->codec_id == AV_CODEC_ID_SSA) { duration = mkv_write_ass_blocks(s, pb, pkt); +#endif } else if (codec->codec_id == AV_CODEC_ID_SRT) { duration = mkv_write_srt_blocks(s, pb, pkt); + } else if (codec->codec_id == AV_CODEC_ID_WEBVTT) { + duration = mkv_write_vtt_blocks(s, pb, pkt); } else { ebml_master blockgroup = start_ebml_master(pb, MATROSKA_ID_BLOCKGROUP, mkv_blockgroup_size(pkt->size)); - duration = pkt->convergence_duration; + /* For backward compatibility, prefer convergence_duration. */ + if (pkt->convergence_duration > 0) { + duration = pkt->convergence_duration; + } mkv_write_block(s, pb, MATROSKA_ID_BLOCK, pkt, 0); put_ebml_uint(pb, MATROSKA_ID_BLOCKDURATION, duration); end_ebml_master(pb, blockgroup); } - if (codec->codec_type == AVMEDIA_TYPE_VIDEO && keyframe) { - ret = mkv_add_cuepoint(mkv->cues, pkt->stream_index, ts, mkv->cluster_pos); + if ((codec->codec_type == AVMEDIA_TYPE_VIDEO && keyframe) || codec->codec_type == AVMEDIA_TYPE_SUBTITLE) { + ret = mkv_add_cuepoint(mkv->cues, pkt->stream_index, ts, mkv->cluster_pos, relative_packet_pos, + codec->codec_type == AVMEDIA_TYPE_SUBTITLE ? duration : -1); if (ret < 0) return ret; } @@ -1310,7 +1580,7 @@ static int mkv_write_packet(AVFormatContext *s, AVPacket *pkt) cluster_size = avio_tell(pb); } - if (mkv->cluster_pos && + if (mkv->cluster_pos != -1 && (cluster_size > mkv->cluster_size_limit || cluster_time > mkv->cluster_time_limit || (codec_type == AVMEDIA_TYPE_VIDEO && keyframe && @@ -1319,7 +1589,7 @@ static int mkv_write_packet(AVFormatContext *s, AVPacket *pkt) " bytes, pts %" PRIu64 "dts %" PRIu64 "\n", avio_tell(pb), pkt->pts, pkt->dts); end_ebml_master(pb, mkv->cluster); - mkv->cluster_pos = 0; + mkv->cluster_pos = -1; if (mkv->dyn_bc) mkv_flush_dynbuf(s); avio_flush(s->pb); @@ -1344,6 +1614,9 @@ static int mkv_write_packet(AVFormatContext *s, AVPacket *pkt) ret = mkv->cur_audio_pkt.buf ? 0 : AVERROR(ENOMEM); } else ret = av_dup_packet(&mkv->cur_audio_pkt); + if (mkv->cur_audio_pkt.side_data_elems > 0) { + ret = av_copy_packet_side_data(&mkv->cur_audio_pkt, &mkv->cur_audio_pkt); + } } else ret = mkv_write_packet_internal(s, pkt); return ret; @@ -1358,11 +1631,11 @@ static int mkv_write_flush_packet(AVFormatContext *s, AVPacket *pkt) else pb = mkv->dyn_bc; if (!pkt) { - if (mkv->cluster_pos) { + if (mkv->cluster_pos != -1) { av_log(s, AV_LOG_DEBUG, "Flushing cluster at offset %" PRIu64 " bytes\n", avio_tell(pb)); end_ebml_master(pb, mkv->cluster); - mkv->cluster_pos = 0; + mkv->cluster_pos = -1; if (mkv->dyn_bc) mkv_flush_dynbuf(s); avio_flush(s->pb); @@ -1392,7 +1665,7 @@ static int mkv_write_trailer(AVFormatContext *s) if (mkv->dyn_bc) { end_ebml_master(mkv->dyn_bc, mkv->cluster); mkv_flush_dynbuf(s); - } else if (mkv->cluster_pos) { + } else if (mkv->cluster_pos != -1) { end_ebml_master(pb, mkv->cluster); } @@ -1409,7 +1682,7 @@ static int mkv_write_trailer(AVFormatContext *s) currentpos = avio_tell(pb); avio_seek(pb, mkv->cues_pos, SEEK_SET); - cuespos = mkv_write_cues(pb, mkv->cues, s->nb_streams); + cuespos = mkv_write_cues(pb, mkv->cues, mkv->tracks, s->nb_streams); cues_end = avio_tell(pb); if (cues_end > cuespos + mkv->reserve_cues_space) { av_log(s, AV_LOG_ERROR, "Insufficient space reserved for cues: %d " @@ -1423,7 +1696,7 @@ static int mkv_write_trailer(AVFormatContext *s) avio_seek(pb, currentpos, SEEK_SET); } else { - cuespos = mkv_write_cues(pb, mkv->cues, s->nb_streams); + cuespos = mkv_write_cues(pb, mkv->cues, mkv->tracks, s->nb_streams); } ret = mkv_add_seekhead_entry(mkv->main_seekhead, MATROSKA_ID_CUES, cuespos); @@ -1442,7 +1715,7 @@ static int mkv_write_trailer(AVFormatContext *s) } end_ebml_master(pb, mkv->segment); - av_free(mkv->tracks); + av_freep(&mkv->tracks); av_freep(&mkv->cues->entries); av_freep(&mkv->cues); @@ -1465,6 +1738,31 @@ static int mkv_query_codec(enum AVCodecID codec_id, int std_compliance) return 0; } +static const AVCodecTag additional_audio_tags[] = { + { AV_CODEC_ID_ALAC, 0XFFFFFFFF }, + { AV_CODEC_ID_EAC3, 0XFFFFFFFF }, + { AV_CODEC_ID_MLP, 0xFFFFFFFF }, + { AV_CODEC_ID_OPUS, 0xFFFFFFFF }, + { AV_CODEC_ID_PCM_S16BE, 0xFFFFFFFF }, + { AV_CODEC_ID_PCM_S24BE, 0xFFFFFFFF }, + { AV_CODEC_ID_PCM_S32BE, 0xFFFFFFFF }, + { AV_CODEC_ID_QDM2, 0xFFFFFFFF }, + { AV_CODEC_ID_RA_144, 0xFFFFFFFF }, + { AV_CODEC_ID_RA_288, 0xFFFFFFFF }, + { AV_CODEC_ID_COOK, 0xFFFFFFFF }, + { AV_CODEC_ID_TRUEHD, 0xFFFFFFFF }, + { AV_CODEC_ID_NONE, 0xFFFFFFFF } +}; + +static const AVCodecTag additional_video_tags[] = { + { AV_CODEC_ID_RV10, 0xFFFFFFFF }, + { AV_CODEC_ID_RV20, 0xFFFFFFFF }, + { AV_CODEC_ID_RV30, 0xFFFFFFFF }, + { AV_CODEC_ID_RV40, 0xFFFFFFFF }, + { AV_CODEC_ID_VP9, 0xFFFFFFFF }, + { AV_CODEC_ID_NONE, 0xFFFFFFFF } +}; + #define OFFSET(x) offsetof(MatroskaMuxContext, x) #define FLAGS AV_OPT_FLAG_ENCODING_PARAM static const AVOption options[] = { @@ -1498,9 +1796,14 @@ AVOutputFormat ff_matroska_muxer = { .flags = AVFMT_GLOBALHEADER | AVFMT_VARIABLE_FPS | AVFMT_TS_NONSTRICT | AVFMT_ALLOW_FLUSH, .codec_tag = (const AVCodecTag* const []){ - ff_codec_bmp_tags, ff_codec_wav_tags, 0 + ff_codec_bmp_tags, ff_codec_wav_tags, + additional_audio_tags, additional_video_tags, 0 }, +#if FF_API_ASS_SSA .subtitle_codec = AV_CODEC_ID_SSA, +#else + .subtitle_codec = AV_CODEC_ID_ASS, +#endif .query_codec = mkv_query_codec, .priv_class = &matroska_class, }; @@ -1522,6 +1825,7 @@ AVOutputFormat ff_webm_muxer = { .priv_data_size = sizeof(MatroskaMuxContext), .audio_codec = AV_CODEC_ID_VORBIS, .video_codec = AV_CODEC_ID_VP8, + .subtitle_codec = AV_CODEC_ID_WEBVTT, .write_header = mkv_write_header, .write_packet = mkv_write_flush_packet, .write_trailer = mkv_write_trailer, @@ -1540,7 +1844,7 @@ static const AVClass mka_class = { }; AVOutputFormat ff_matroska_audio_muxer = { .name = "matroska", - .long_name = NULL_IF_CONFIG_SMALL("Matroska"), + .long_name = NULL_IF_CONFIG_SMALL("Matroska Audio"), .mime_type = "audio/x-matroska", .extensions = "mka", .priv_data_size = sizeof(MatroskaMuxContext), @@ -1552,7 +1856,9 @@ AVOutputFormat ff_matroska_audio_muxer = { .write_trailer = mkv_write_trailer, .flags = AVFMT_GLOBALHEADER | AVFMT_TS_NONSTRICT | AVFMT_ALLOW_FLUSH, - .codec_tag = (const AVCodecTag* const []){ ff_codec_wav_tags, 0 }, + .codec_tag = (const AVCodecTag* const []){ + ff_codec_wav_tags, additional_audio_tags, 0 + }, .priv_class = &mka_class, }; #endif diff --git a/libavformat/md5enc.c b/libavformat/md5enc.c index 92497045eb..8b14fbaa0f 100644 --- a/libavformat/md5enc.c +++ b/libavformat/md5enc.c @@ -2,38 +2,45 @@ * MD5 encoder (for codec/format testing) * Copyright (c) 2009 Reimar Döffinger, based on crcenc (c) 2002 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "libavutil/md5.h" +#include "libavutil/avassert.h" +#include "libavutil/avstring.h" +#include "libavutil/hash.h" +#include "libavutil/opt.h" #include "avformat.h" #include "internal.h" struct MD5Context { - struct AVMD5 *md5; + const AVClass *avclass; + struct AVHashContext *hash; + char *hash_name; }; static void md5_finish(struct AVFormatContext *s, char *buf) { struct MD5Context *c = s->priv_data; - uint8_t md5[16]; + uint8_t md5[AV_HASH_MAX_SIZE]; int i, offset = strlen(buf); - av_md5_final(c->md5, md5); - for (i = 0; i < sizeof(md5); i++) { + int len = av_hash_get_size(c->hash); + av_assert0(len > 0 && len <= sizeof(md5)); + av_hash_final(c->hash, md5); + for (i = 0; i < len; i++) { snprintf(buf + offset, 3, "%02"PRIx8, md5[i]); offset += 2; } @@ -44,39 +51,54 @@ static void md5_finish(struct AVFormatContext *s, char *buf) avio_flush(s->pb); } +#define OFFSET(x) offsetof(struct MD5Context, x) +#define ENC AV_OPT_FLAG_ENCODING_PARAM +static const AVOption hash_options[] = { + { "hash", "set hash to use", OFFSET(hash_name), AV_OPT_TYPE_STRING, {.str = "md5"}, 0, 0, ENC }, + { NULL }, +}; + +static const AVClass md5enc_class = { + .class_name = "hash encoder class", + .item_name = av_default_item_name, + .option = hash_options, + .version = LIBAVUTIL_VERSION_INT, +}; + #if CONFIG_MD5_MUXER static int write_header(struct AVFormatContext *s) { struct MD5Context *c = s->priv_data; - c->md5 = av_md5_alloc(); - if (!c->md5) - return AVERROR(ENOMEM); - av_md5_init(c->md5); + int res = av_hash_alloc(&c->hash, c->hash_name); + if (res < 0) + return res; + av_hash_init(c->hash); return 0; } static int write_packet(struct AVFormatContext *s, AVPacket *pkt) { struct MD5Context *c = s->priv_data; - av_md5_update(c->md5, pkt->data, pkt->size); + av_hash_update(c->hash, pkt->data, pkt->size); return 0; } static int write_trailer(struct AVFormatContext *s) { struct MD5Context *c = s->priv_data; - char buf[64] = "MD5="; + char buf[256]; + av_strlcpy(buf, av_hash_get_name(c->hash), sizeof(buf) - 200); + av_strlcat(buf, "=", sizeof(buf) - 200); md5_finish(s, buf); - av_freep(&c->md5); + av_hash_freep(&c->hash); return 0; } AVOutputFormat ff_md5_muxer = { .name = "md5", .long_name = NULL_IF_CONFIG_SMALL("MD5 testing"), - .extensions = "", .priv_data_size = sizeof(struct MD5Context), .audio_codec = AV_CODEC_ID_PCM_S16LE, .video_codec = AV_CODEC_ID_RAWVIDEO, @@ -84,6 +106,7 @@ AVOutputFormat ff_md5_muxer = { .write_packet = write_packet, .write_trailer = write_trailer, .flags = AVFMT_NOTIMESTAMPS, + .priv_class = &md5enc_class, }; #endif @@ -91,18 +114,23 @@ AVOutputFormat ff_md5_muxer = { static int framemd5_write_header(struct AVFormatContext *s) { struct MD5Context *c = s->priv_data; - c->md5 = av_md5_alloc(); - if (!c->md5) - return AVERROR(ENOMEM); - return ff_framehash_write_header(s); + int res = av_hash_alloc(&c->hash, c->hash_name); + if (res < 0) + return res; + avio_printf(s->pb, "#format: frame checksums\n"); + avio_printf(s->pb, "#version: 1\n"); + avio_printf(s->pb, "#hash: %s\n", av_hash_get_name(c->hash)); + ff_framehash_write_header(s); + avio_printf(s->pb, "#stream#, dts, pts, duration, size, hash\n"); + return 0; } static int framemd5_write_packet(struct AVFormatContext *s, AVPacket *pkt) { struct MD5Context *c = s->priv_data; char buf[256]; - av_md5_init(c->md5); - av_md5_update(c->md5, pkt->data, pkt->size); + av_hash_init(c->hash); + av_hash_update(c->hash, pkt->data, pkt->size); snprintf(buf, sizeof(buf) - 64, "%d, %10"PRId64", %10"PRId64", %8d, %8d, ", pkt->stream_index, pkt->dts, pkt->pts, pkt->duration, pkt->size); @@ -113,14 +141,20 @@ static int framemd5_write_packet(struct AVFormatContext *s, AVPacket *pkt) static int framemd5_write_trailer(struct AVFormatContext *s) { struct MD5Context *c = s->priv_data; - av_freep(&c->md5); + av_hash_freep(&c->hash); return 0; } +static const AVClass framemd5_class = { + .class_name = "frame hash encoder class", + .item_name = av_default_item_name, + .option = hash_options, + .version = LIBAVUTIL_VERSION_INT, +}; + AVOutputFormat ff_framemd5_muxer = { .name = "framemd5", .long_name = NULL_IF_CONFIG_SMALL("Per-frame MD5 testing"), - .extensions = "", .priv_data_size = sizeof(struct MD5Context), .audio_codec = AV_CODEC_ID_PCM_S16LE, .video_codec = AV_CODEC_ID_RAWVIDEO, @@ -129,5 +163,6 @@ AVOutputFormat ff_framemd5_muxer = { .write_trailer = framemd5_write_trailer, .flags = AVFMT_VARIABLE_FPS | AVFMT_TS_NONSTRICT | AVFMT_TS_NEGATIVE, + .priv_class = &framemd5_class, }; #endif diff --git a/libavformat/md5proto.c b/libavformat/md5proto.c index 12ddde3d8c..6af0a6ed26 100644 --- a/libavformat/md5proto.c +++ b/libavformat/md5proto.c @@ -1,20 +1,20 @@ /* * Copyright (c) 2010 Mans Rullgard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/metadata-example.c b/libavformat/metadata-example.c deleted file mode 100644 index 7bf77e7378..0000000000 --- a/libavformat/metadata-example.c +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2011 Reinhard Tartler - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -/** - * @file - * @example libavformat/metadata-example.c - * Shows how the metadata API can be used in application programs. - */ - -#include <stdio.h> - -#include <libavformat/avformat.h> -#include <libavutil/dict.h> - -int main (int argc, char **argv) -{ - AVFormatContext *fmt_ctx = NULL; - AVDictionaryEntry *tag = NULL; - int ret; - - if (argc != 2) { - printf("usage: %s <input_file>\n" - "example program to demonstrate the use of the libavformat metadata API.\n" - "\n", argv[0]); - return 1; - } - - av_register_all(); - if ((ret = avformat_open_input(&fmt_ctx, argv[1], NULL, NULL))) - return ret; - - while ((tag = av_dict_get(fmt_ctx->metadata, "", tag, AV_DICT_IGNORE_SUFFIX))) - printf("%s=%s\n", tag->key, tag->value); - - avformat_free_context(fmt_ctx); - return 0; -} diff --git a/libavformat/metadata.c b/libavformat/metadata.c index 77fb298217..fc3a9d7d55 100644 --- a/libavformat/metadata.c +++ b/libavformat/metadata.c @@ -1,20 +1,20 @@ /* * copyright (c) 2009 Michael Niedermayer * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/metadata.h b/libavformat/metadata.h index eee3ee4391..6586094e82 100644 --- a/libavformat/metadata.h +++ b/libavformat/metadata.h @@ -1,20 +1,20 @@ /* * copyright (c) 2009 Michael Niedermayer * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/mgsts.c b/libavformat/mgsts.c new file mode 100644 index 0000000000..dec6bfc8f6 --- /dev/null +++ b/libavformat/mgsts.c @@ -0,0 +1,106 @@ +/* + * Metar Gear Solid: The Twin Snakes demuxer + * Copyright (c) 2012 Paul B Mahol + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/intreadwrite.h" +#include "libavutil/intfloat.h" +#include "avformat.h" +#include "riff.h" + +static int read_probe(AVProbeData *p) +{ + if (AV_RB32(p->buf ) != 0x000E || + AV_RB32(p->buf + 4) != 0x0050 || + AV_RB32(p->buf + 12) != 0x0034) + return 0; + return AVPROBE_SCORE_MAX; +} + +static int read_header(AVFormatContext *s) +{ + AVIOContext *pb = s->pb; + AVStream *st; + AVRational fps; + uint32_t chunk_size; + + avio_skip(pb, 4); + chunk_size = avio_rb32(pb); + if (chunk_size != 80) + return AVERROR(EIO); + avio_skip(pb, 20); + + st = avformat_new_stream(s, 0); + if (!st) + return AVERROR(ENOMEM); + + st->need_parsing = AVSTREAM_PARSE_HEADERS; + st->start_time = 0; + st->nb_frames = + st->duration = avio_rb32(pb); + fps = av_d2q(av_int2float(avio_rb32(pb)), INT_MAX); + st->codec->width = avio_rb32(pb); + st->codec->height = avio_rb32(pb); + avio_skip(pb, 12); + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->codec->codec_tag = avio_rb32(pb); + st->codec->codec_id = ff_codec_get_id(ff_codec_bmp_tags, + st->codec->codec_tag); + avpriv_set_pts_info(st, 64, fps.den, fps.num); + avio_skip(pb, 20); + + return 0; +} + +static int read_packet(AVFormatContext *s, AVPacket *pkt) +{ + AVIOContext *pb = s->pb; + uint32_t chunk_size, payload_size; + int ret; + + if (url_feof(pb)) + return AVERROR_EOF; + + avio_skip(pb, 4); + chunk_size = avio_rb32(pb); + avio_skip(pb, 4); + payload_size = avio_rb32(pb); + + if (chunk_size < payload_size + 16) + return AVERROR(EIO); + + ret = av_get_packet(pb, pkt, payload_size); + if (ret < 0) + return ret; + + pkt->pos -= 16; + pkt->duration = 1; + avio_skip(pb, chunk_size - (ret + 16)); + + return ret; +} + +AVInputFormat ff_mgsts_demuxer = { + .name = "mgsts", + .long_name = NULL_IF_CONFIG_SMALL("Metal Gear Solid: The Twin Snakes"), + .read_probe = read_probe, + .read_header = read_header, + .read_packet = read_packet, + .flags = AVFMT_GENERIC_INDEX, +}; diff --git a/libavformat/microdvddec.c b/libavformat/microdvddec.c new file mode 100644 index 0000000000..7b49e43976 --- /dev/null +++ b/libavformat/microdvddec.c @@ -0,0 +1,167 @@ +/* + * MicroDVD subtitle demuxer + * Copyright (c) 2010 Aurelien Jacobs <aurel@gnuage.org> + * Copyright (c) 2012 Clément BÅ“sch <u pkh me> + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "avformat.h" +#include "internal.h" +#include "subtitles.h" +#include "libavutil/intreadwrite.h" + +#define MAX_LINESIZE 2048 + + +typedef struct { + FFDemuxSubtitlesQueue q; +} MicroDVDContext; + + +static int microdvd_probe(AVProbeData *p) +{ + unsigned char c; + const uint8_t *ptr = p->buf; + int i; + + if (AV_RB24(ptr) == 0xEFBBBF) + ptr += 3; /* skip UTF-8 BOM */ + + for (i=0; i<3; i++) { + if (sscanf(ptr, "{%*d}{}%c", &c) != 1 && + sscanf(ptr, "{%*d}{%*d}%c", &c) != 1 && + sscanf(ptr, "{DEFAULT}{}%c", &c) != 1) + return 0; + ptr += ff_subtitles_next_line(ptr); + } + return AVPROBE_SCORE_MAX; +} + +static int64_t get_pts(const char *buf) +{ + int frame; + char c; + + if (sscanf(buf, "{%d}{%c", &frame, &c) == 2) + return frame; + return AV_NOPTS_VALUE; +} + +static int get_duration(const char *buf) +{ + int frame_start, frame_end; + + if (sscanf(buf, "{%d}{%d}", &frame_start, &frame_end) == 2) + return frame_end - frame_start; + return -1; +} + +static int microdvd_read_header(AVFormatContext *s) +{ + AVRational pts_info = (AVRational){ 2997, 125 }; /* default: 23.976 fps */ + MicroDVDContext *microdvd = s->priv_data; + AVStream *st = avformat_new_stream(s, NULL); + int i = 0; + char line[MAX_LINESIZE]; + + if (!st) + return AVERROR(ENOMEM); + + while (!url_feof(s->pb)) { + char *p = line; + AVPacket *sub; + int64_t pos = avio_tell(s->pb); + int len = ff_get_line(s->pb, line, sizeof(line)); + + if (!len) + break; + line[strcspn(line, "\r\n")] = 0; + if (i++ < 3) { + int frame; + double fps; + char c; + + if ((sscanf(line, "{%d}{}%6lf", &frame, &fps) == 2 || + sscanf(line, "{%d}{%*d}%6lf", &frame, &fps) == 2) + && frame <= 1 && fps > 3 && fps < 100) + pts_info = av_d2q(fps, 100000); + if (!st->codec->extradata && sscanf(line, "{DEFAULT}{}%c", &c) == 1) { + st->codec->extradata = av_strdup(line + 11); + if (!st->codec->extradata) + return AVERROR(ENOMEM); + st->codec->extradata_size = strlen(st->codec->extradata) + 1; + continue; + } + } +#define SKIP_FRAME_ID \ + p = strchr(p, '}'); \ + if (!p) { \ + av_log(s, AV_LOG_WARNING, "Invalid event \"%s\"" \ + " at line %d\n", line, i); \ + continue; \ + } \ + p++ + SKIP_FRAME_ID; + SKIP_FRAME_ID; + if (!*p) + continue; + sub = ff_subtitles_queue_insert(µdvd->q, p, strlen(p), 0); + if (!sub) + return AVERROR(ENOMEM); + sub->pos = pos; + sub->pts = get_pts(line); + sub->duration = get_duration(line); + } + ff_subtitles_queue_finalize(µdvd->q); + avpriv_set_pts_info(st, 64, pts_info.den, pts_info.num); + st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE; + st->codec->codec_id = AV_CODEC_ID_MICRODVD; + return 0; +} + +static int microdvd_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + MicroDVDContext *microdvd = s->priv_data; + return ff_subtitles_queue_read_packet(µdvd->q, pkt); +} + +static int microdvd_read_seek(AVFormatContext *s, int stream_index, + int64_t min_ts, int64_t ts, int64_t max_ts, int flags) +{ + MicroDVDContext *microdvd = s->priv_data; + return ff_subtitles_queue_seek(µdvd->q, s, stream_index, + min_ts, ts, max_ts, flags); +} + +static int microdvd_read_close(AVFormatContext *s) +{ + MicroDVDContext *microdvd = s->priv_data; + ff_subtitles_queue_clean(µdvd->q); + return 0; +} + +AVInputFormat ff_microdvd_demuxer = { + .name = "microdvd", + .long_name = NULL_IF_CONFIG_SMALL("MicroDVD subtitle format"), + .priv_data_size = sizeof(MicroDVDContext), + .read_probe = microdvd_probe, + .read_header = microdvd_read_header, + .read_packet = microdvd_read_packet, + .read_seek2 = microdvd_read_seek, + .read_close = microdvd_read_close, +}; diff --git a/libavformat/microdvdenc.c b/libavformat/microdvdenc.c new file mode 100644 index 0000000000..4d8438437b --- /dev/null +++ b/libavformat/microdvdenc.c @@ -0,0 +1,67 @@ +/* + * MicroDVD subtitle muxer + * Copyright (c) 2010 Aurelien Jacobs <aurel@gnuage.org> + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <inttypes.h> +#include "avformat.h" +#include "internal.h" + +static int microdvd_write_header(struct AVFormatContext *s) +{ + AVCodecContext *avctx = s->streams[0]->codec; + AVRational tb = avctx->time_base; + + if (s->nb_streams != 1 || avctx->codec_id != AV_CODEC_ID_MICRODVD) { + av_log(s, AV_LOG_ERROR, "Exactly one MicroDVD stream is needed.\n"); + return -1; + } + + if (avctx->extradata && avctx->extradata_size > 0) { + avio_write(s->pb, "{DEFAULT}{}", 11); + avio_write(s->pb, avctx->extradata, avctx->extradata_size); + avio_flush(s->pb); + } + + avpriv_set_pts_info(s->streams[0], 64, tb.num, tb.den); + return 0; +} + +static int microdvd_write_packet(AVFormatContext *avf, AVPacket *pkt) +{ + avio_printf(avf->pb, "{%"PRId64"}", pkt->pts); + if (pkt->duration < 0) + avio_write(avf->pb, "{}", 2); + else + avio_printf(avf->pb, "{%"PRId64"}", pkt->pts + pkt->duration); + avio_write(avf->pb, pkt->data, pkt->size); + avio_write(avf->pb, "\n", 1); + return 0; +} + +AVOutputFormat ff_microdvd_muxer = { + .name = "microdvd", + .long_name = NULL_IF_CONFIG_SMALL("MicroDVD subtitle format"), + .mime_type = "text/x-microdvd", + .extensions = "sub", + .write_header = microdvd_write_header, + .write_packet = microdvd_write_packet, + .flags = AVFMT_NOTIMESTAMPS, + .subtitle_codec = AV_CODEC_ID_MICRODVD, +}; diff --git a/libavformat/mkvtimestamp_v2.c b/libavformat/mkvtimestamp_v2.c new file mode 100644 index 0000000000..7ba6691952 --- /dev/null +++ b/libavformat/mkvtimestamp_v2.c @@ -0,0 +1,50 @@ +/* + * extract pts as timecode v2, as defined by mkvtoolnix + * Copyright (c) 2009 David Conrad + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "avformat.h" +#include "internal.h" + +static int write_header(AVFormatContext *s) +{ + static const char *header = "# timecode format v2\n"; + avio_write(s->pb, header, strlen(header)); + avpriv_set_pts_info(s->streams[0], 64, 1, 1000); + return 0; +} + +static int write_packet(AVFormatContext *s, AVPacket *pkt) +{ + char buf[256]; + if (pkt->stream_index) + av_log(s, AV_LOG_WARNING, "More than one stream unsupported\n"); + snprintf(buf, sizeof(buf), "%" PRId64 "\n", pkt->dts); + avio_write(s->pb, buf, strlen(buf)); + return 0; +} + +AVOutputFormat ff_mkvtimestamp_v2_muxer = { + .name = "mkvtimestamp_v2", + .long_name = NULL_IF_CONFIG_SMALL("extract pts as timecode v2 format, as defined by mkvtoolnix"), + .audio_codec = AV_CODEC_ID_NONE, + .video_codec = AV_CODEC_ID_RAWVIDEO, + .write_header = write_header, + .write_packet = write_packet, +}; diff --git a/libavformat/mm.c b/libavformat/mm.c index 8c9cbd79d2..4315802f73 100644 --- a/libavformat/mm.c +++ b/libavformat/mm.c @@ -2,20 +2,20 @@ * American Laser Games MM Format Demuxer * Copyright (c) 2006 Peter Ross * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -176,7 +176,6 @@ static int read_packet(AVFormatContext *s, case MM_TYPE_AUDIO : if (av_get_packet(s->pb, pkt, length)<0) return AVERROR(ENOMEM); - pkt->size = length; pkt->stream_index = 1; pkt->pts = mm->audio_pts++; return 0; diff --git a/libavformat/mmf.c b/libavformat/mmf.c index 61f1d7adf4..cb87a6dddb 100644 --- a/libavformat/mmf.c +++ b/libavformat/mmf.c @@ -2,20 +2,20 @@ * Yamaha SMAF format * Copyright (c) 2005 Vidar Madsen * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -24,11 +24,13 @@ #include "avio_internal.h" #include "internal.h" #include "pcm.h" +#include "rawenc.h" #include "riff.h" typedef struct { int64_t atrpos, atsqpos, awapos; - int64_t data_size; + int64_t data_end; + int stereo; } MMFContext; static const int mmf_rates[] = { 4000, 8000, 11025, 22050, 44100 }; @@ -67,26 +69,38 @@ static int mmf_write_header(AVFormatContext *s) AVIOContext *pb = s->pb; int64_t pos; int rate; + const char *version = s->streams[0]->codec->flags & CODEC_FLAG_BITEXACT ? + "VN:Lavf," : + "VN:"LIBAVFORMAT_IDENT","; rate = mmf_rate_code(s->streams[0]->codec->sample_rate); if (rate < 0) { - av_log(s, AV_LOG_ERROR, "Unsupported sample rate %d\n", + av_log(s, AV_LOG_ERROR, "Unsupported sample rate %d, supported are 4000, 8000, 11025, 22050 and 44100\n", s->streams[0]->codec->sample_rate); - return -1; + return AVERROR(EINVAL); + } + + mmf->stereo = s->streams[0]->codec->channels > 1; + if (mmf->stereo && + s->streams[0]->codec->strict_std_compliance > FF_COMPLIANCE_EXPERIMENTAL) { + av_log(s, AV_LOG_ERROR, "Yamaha SMAF stereo is experimental, " + "add '-strict %d' if you want to use it.\n", + FF_COMPLIANCE_EXPERIMENTAL); + return AVERROR(EINVAL); } ffio_wfourcc(pb, "MMMD"); avio_wb32(pb, 0); pos = ff_start_tag(pb, "CNTI"); avio_w8(pb, 0); /* class */ - avio_w8(pb, 0); /* type */ - avio_w8(pb, 0); /* code type */ + avio_w8(pb, 1); /* type */ + avio_w8(pb, 1); /* code type */ avio_w8(pb, 0); /* status */ avio_w8(pb, 0); /* counts */ end_tag_be(pb, pos); pos = ff_start_tag(pb, "OPDA"); - avio_write(pb, "VN:libavcodec,", sizeof("VN:libavcodec,") -1); /* metadata ("ST:songtitle,VN:version,...") */ + avio_write(pb, version, strlen(version)); /* metadata ("ST:songtitle,VN:version,...") */ end_tag_be(pb, pos); avio_write(pb, "ATR\x00", 4); @@ -94,7 +108,7 @@ static int mmf_write_header(AVFormatContext *s) mmf->atrpos = avio_tell(pb); avio_w8(pb, 0); /* format type */ avio_w8(pb, 0); /* sequence type */ - avio_w8(pb, (0 << 7) | (1 << 4) | rate); /* (channel << 7) | (format << 4) | rate */ + avio_w8(pb, (mmf->stereo << 7) | (1 << 4) | rate); /* (channel << 7) | (format << 4) | rate */ avio_w8(pb, 0); /* wave base bit */ avio_w8(pb, 2); /* time base d */ avio_w8(pb, 2); /* time base g */ @@ -114,13 +128,6 @@ static int mmf_write_header(AVFormatContext *s) return 0; } -static int mmf_write_packet(AVFormatContext *s, AVPacket *pkt) -{ - AVIOContext *pb = s->pb; - avio_write(pb, pkt->data, pkt->size); - return 0; -} - /* Write a variable-length symbol */ static void put_varlength(AVIOContext *pb, int val) { @@ -154,7 +161,7 @@ static int mmf_write_trailer(AVFormatContext *s) /* "play wav" */ avio_w8(pb, 0); /* start time */ - avio_w8(pb, 1); /* (channel << 6) | wavenum */ + avio_w8(pb, (mmf->stereo << 6) | 1); /* (channel << 6) | wavenum */ gatetime = size * 500 / s->streams[0]->codec->sample_rate; put_varlength(pb, gatetime); /* duration */ @@ -197,7 +204,7 @@ static int mmf_read_header(AVFormatContext *s) tag = avio_rl32(pb); if (tag != MKTAG('M', 'M', 'M', 'D')) - return -1; + return AVERROR_INVALIDDATA; avio_skip(pb, 4); /* file_size */ /* Skip some unused chunks that may or may not be present */ @@ -214,11 +221,11 @@ static int mmf_read_header(AVFormatContext *s) /* Tag = "ATRx", where "x" = track number */ if ((tag & 0xffffff) == MKTAG('M', 'T', 'R', 0)) { av_log(s, AV_LOG_ERROR, "MIDI like format found, unsupported\n"); - return -1; + return AVERROR_PATCHWELCOME; } if ((tag & 0xffffff) != MKTAG('A', 'T', 'R', 0)) { av_log(s, AV_LOG_ERROR, "Unsupported SMAF chunk %08x\n", tag); - return -1; + return AVERROR_PATCHWELCOME; } avio_r8(pb); /* format type */ @@ -227,7 +234,7 @@ static int mmf_read_header(AVFormatContext *s) rate = mmf_rate(params & 0x0f); if (rate < 0) { av_log(s, AV_LOG_ERROR, "Invalid sample rate\n"); - return -1; + return AVERROR_INVALIDDATA; } avio_r8(pb); /* wave base bit */ avio_r8(pb); /* time base d */ @@ -247,9 +254,9 @@ static int mmf_read_header(AVFormatContext *s) /* Make sure it's followed by an Awa chunk, aka wave data */ if ((tag & 0xffffff) != MKTAG('A', 'w', 'a', 0)) { av_log(s, AV_LOG_ERROR, "Unexpected SMAF chunk %08x\n", tag); - return -1; + return AVERROR_INVALIDDATA; } - mmf->data_size = size; + mmf->data_end = avio_tell(pb) + size; st = avformat_new_stream(s, NULL); if (!st) @@ -258,8 +265,8 @@ static int mmf_read_header(AVFormatContext *s) st->codec->codec_type = AVMEDIA_TYPE_AUDIO; st->codec->codec_id = AV_CODEC_ID_ADPCM_YAMAHA; st->codec->sample_rate = rate; - st->codec->channels = 1; - st->codec->channel_layout = AV_CH_LAYOUT_MONO; + st->codec->channels = (params >> 7) + 1; + st->codec->channel_layout = params >> 7 ? AV_CH_LAYOUT_STEREO : AV_CH_LAYOUT_MONO; st->codec->bits_per_coded_sample = 4; st->codec->bit_rate = st->codec->sample_rate * st->codec->bits_per_coded_sample; @@ -274,29 +281,20 @@ static int mmf_read_header(AVFormatContext *s) static int mmf_read_packet(AVFormatContext *s, AVPacket *pkt) { MMFContext *mmf = s->priv_data; - int ret, size; - - if (s->pb->eof_reached) - return AVERROR(EIO); + int64_t left, size; + int ret; - size = MAX_SIZE; - if (size > mmf->data_size) - size = mmf->data_size; + left = mmf->data_end - avio_tell(s->pb); + size = FFMIN(left, MAX_SIZE); + if (url_feof(s->pb) || size <= 0) + return AVERROR_EOF; - if (!size) - return AVERROR(EIO); - - if (av_new_packet(pkt, size)) - return AVERROR(EIO); - pkt->stream_index = 0; - - ret = avio_read(s->pb, pkt->data, pkt->size); + ret = av_get_packet(s->pb, pkt, size); if (ret < 0) - av_free_packet(pkt); + return ret; - mmf->data_size -= ret; + pkt->stream_index = 0; - pkt->size = ret; return ret; } @@ -308,7 +306,7 @@ AVInputFormat ff_mmf_demuxer = { .read_probe = mmf_probe, .read_header = mmf_read_header, .read_packet = mmf_read_packet, - .read_seek = ff_pcm_read_seek, + .flags = AVFMT_GENERIC_INDEX, }; #endif @@ -322,7 +320,7 @@ AVOutputFormat ff_mmf_muxer = { .audio_codec = AV_CODEC_ID_ADPCM_YAMAHA, .video_codec = AV_CODEC_ID_NONE, .write_header = mmf_write_header, - .write_packet = mmf_write_packet, + .write_packet = ff_raw_write_packet, .write_trailer = mmf_write_trailer, }; #endif diff --git a/libavformat/mms.c b/libavformat/mms.c index 192e7039af..46fbede90c 100644 --- a/libavformat/mms.c +++ b/libavformat/mms.c @@ -4,20 +4,20 @@ * Copyright (c) 2007 Björn Axelsson * Copyright (c) 2010 Zhentan Feng <spyfeng at gmail dot com> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mms.h" diff --git a/libavformat/mms.h b/libavformat/mms.h index e89da41bb6..57e3d7e18a 100644 --- a/libavformat/mms.h +++ b/libavformat/mms.h @@ -2,20 +2,20 @@ * MMS protocol common definitions. * Copyright (c) 2010 Zhentan Feng <spyfeng at gmail dot com> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef AVFORMAT_MMS_H diff --git a/libavformat/mmsh.c b/libavformat/mmsh.c index 5e9d0bc134..482ece4031 100644 --- a/libavformat/mmsh.c +++ b/libavformat/mmsh.c @@ -2,20 +2,20 @@ * MMS protocol over HTTP * Copyright (c) 2010 Zhentan Feng <spyfeng at gmail dot com> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -56,6 +56,7 @@ typedef enum { typedef struct { MMSContext mms; + uint8_t location[1024]; int request_seq; ///< request packet sequence int chunk_seq; ///< data packet sequence } MMSHContext; @@ -65,9 +66,9 @@ static int mmsh_close(URLContext *h) MMSHContext *mmsh = (MMSHContext *)h->priv_data; MMSContext *mms = &mmsh->mms; if (mms->mms_hd) - ffurl_close(mms->mms_hd); - av_free(mms->streams); - av_free(mms->asf_header); + ffurl_closep(&mms->mms_hd); + av_freep(&mms->streams); + av_freep(&mms->asf_header); return 0; } @@ -210,10 +211,10 @@ static int get_http_header_data(MMSHContext *mmsh) } } -static int mmsh_open(URLContext *h, const char *uri, int flags) +static int mmsh_open_internal(URLContext *h, const char *uri, int flags, int timestamp, int64_t pos) { int i, port, err; - char httpname[256], path[256], host[128], location[1024]; + char httpname[256], path[256], host[128]; char *stream_selection = NULL; char headers[1024]; MMSHContext *mmsh = h->priv_data; @@ -221,10 +222,10 @@ static int mmsh_open(URLContext *h, const char *uri, int flags) mmsh->request_seq = h->is_streamed = 1; mms = &mmsh->mms; - av_strlcpy(location, uri, sizeof(location)); + av_strlcpy(mmsh->location, uri, sizeof(mmsh->location)); av_url_split(NULL, 0, NULL, 0, - host, sizeof(host), &port, path, sizeof(path), location); + host, sizeof(host), &port, path, sizeof(path), mmsh->location); if (port<0) port = 80; // default mmsh protocol port ff_url_join(httpname, sizeof(httpname), "http", NULL, host, port, "%s", path); @@ -241,7 +242,7 @@ static int mmsh_open(URLContext *h, const char *uri, int flags) "Pragma: no-cache,rate=1.000000,stream-time=0," "stream-offset=0:0,request-context=%u,max-duration=0\r\n" CLIENTGUID - "Connection: Close\r\n\r\n", + "Connection: Close\r\n", host, port, mmsh->request_seq++); av_opt_set(mms->mms_hd->priv_data, "headers", headers, 0); @@ -282,8 +283,9 @@ static int mmsh_open(URLContext *h, const char *uri, int flags) CLIENTGUID "Pragma: stream-switch-count=%d\r\n" "Pragma: stream-switch-entry=%s\r\n" - "Connection: Close\r\n\r\n", - host, port, mmsh->request_seq++, mms->stream_num, stream_selection); + "Pragma: no-cache,rate=1.000000,stream-time=%u" + "Connection: Close\r\n", + host, port, mmsh->request_seq++, mms->stream_num, stream_selection, timestamp); av_freep(&stream_selection); if (err < 0) { av_log(NULL, AV_LOG_ERROR, "Build play request failed!\n"); @@ -312,6 +314,11 @@ fail: return err; } +static int mmsh_open(URLContext *h, const char *uri, int flags) +{ + return mmsh_open_internal(h, uri, flags, 0, 0); +} + static int handle_chunk_type(MMSHContext *mmsh) { MMSContext *mms = &mmsh->mms; @@ -358,11 +365,49 @@ static int mmsh_read(URLContext *h, uint8_t *buf, int size) return res; } +static int64_t mmsh_read_seek(URLContext *h, int stream_index, + int64_t timestamp, int flags) +{ + MMSHContext *mmsh_old = h->priv_data; + MMSHContext *mmsh = av_mallocz(sizeof(*mmsh)); + int ret; + + if (!mmsh) + return AVERROR(ENOMEM); + + h->priv_data = mmsh; + ret= mmsh_open_internal(h, mmsh_old->location, 0, FFMAX(timestamp, 0), 0); + if(ret>=0){ + h->priv_data = mmsh_old; + mmsh_close(h); + h->priv_data = mmsh; + av_free(mmsh_old); + mmsh->mms.asf_header_read_size = mmsh->mms.asf_header_size; + }else { + h->priv_data = mmsh_old; + av_free(mmsh); + } + + return ret; +} + +static int64_t mmsh_seek(URLContext *h, int64_t pos, int whence) +{ + MMSHContext *mmsh = h->priv_data; + MMSContext *mms = &mmsh->mms; + + if(pos == 0 && whence == SEEK_CUR) + return mms->asf_header_read_size + mms->remaining_in_len + mmsh->chunk_seq * (int64_t)mms->asf_packet_len; + return AVERROR(ENOSYS); +} + URLProtocol ff_mmsh_protocol = { .name = "mmsh", .url_open = mmsh_open, .url_read = mmsh_read, + .url_seek = mmsh_seek, .url_close = mmsh_close, + .url_read_seek = mmsh_read_seek, .priv_data_size = sizeof(MMSHContext), .flags = URL_PROTOCOL_FLAG_NETWORK, }; diff --git a/libavformat/mmst.c b/libavformat/mmst.c index a17b4c12a8..167303697b 100644 --- a/libavformat/mmst.c +++ b/libavformat/mmst.c @@ -4,20 +4,20 @@ * Copyright (c) 2007 Björn Axelsson * Copyright (c) 2010 Zhentan Feng <spyfeng at gmail dot com> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -152,7 +152,7 @@ static int send_command_packet(MMSTContext *mmst) return 0; } -static void mms_put_utf16(MMSContext *mms, uint8_t *src) +static void mms_put_utf16(MMSContext *mms, const uint8_t *src) { AVIOContext bic; int size = mms->write_out_ptr - mms->out_buffer; diff --git a/libavformat/mov.c b/libavformat/mov.c index d9cb36b67a..eec8485b65 100644 --- a/libavformat/mov.c +++ b/libavformat/mov.c @@ -6,20 +6,20 @@ * first version by Francois Revol <revol@free.fr> * seek function by Gael Chardon <gael.dev@4now.net> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -34,6 +34,8 @@ #include "libavutil/mathematics.h" #include "libavutil/avstring.h" #include "libavutil/dict.h" +#include "libavutil/opt.h" +#include "libavutil/timecode.h" #include "libavcodec/ac3tab.h" #include "avformat.h" #include "internal.h" @@ -126,6 +128,74 @@ static int mov_metadata_gnre(MOVContext *c, AVIOContext *pb, return 0; } +static int mov_read_custom_metadata(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + char key[1024]={0}, data[1024]={0}; + int i; + AVStream *st; + MOVStreamContext *sc; + + if (c->fc->nb_streams < 1) + return 0; + st = c->fc->streams[c->fc->nb_streams-1]; + sc = st->priv_data; + + if (atom.size <= 8) return 0; + + for (i = 0; i < 3; i++) { // Parse up to three sub-atoms looking for name and data. + int data_size = avio_rb32(pb); + int tag = avio_rl32(pb); + int str_size = 0, skip_size = 0; + char *target = NULL; + + switch (tag) { + case MKTAG('n','a','m','e'): + avio_rb32(pb); // version/flags + str_size = skip_size = data_size - 12; + atom.size -= 12; + target = key; + break; + case MKTAG('d','a','t','a'): + avio_rb32(pb); // version/flags + avio_rb32(pb); // reserved (zero) + str_size = skip_size = data_size - 16; + atom.size -= 16; + target = data; + break; + default: + skip_size = data_size - 8; + str_size = 0; + break; + } + + if (target) { + str_size = FFMIN3(sizeof(data)-1, str_size, atom.size); + avio_read(pb, target, str_size); + target[str_size] = 0; + } + atom.size -= skip_size; + + // If we didn't read the full data chunk for the sub-atom, skip to the end of it. + if (skip_size > str_size) avio_skip(pb, skip_size - str_size); + } + + if (*key && *data) { + if (strcmp(key, "iTunSMPB") == 0) { + int priming, remainder, samples; + if(sscanf(data, "%*X %X %X %X", &priming, &remainder, &samples) == 3){ + if(priming>0 && priming<16384) + sc->start_pad = priming; + return 1; + } + } + if (strcmp(key, "cdec") == 0) { +// av_dict_set(&st->metadata, key, data, 0); + return 1; + } + } + return 0; +} + static const uint32_t mac_to_unicode[128] = { 0x00C4,0x00C5,0x00C7,0x00C9,0x00D1,0x00D6,0x00DC,0x00E1, 0x00E0,0x00E2,0x00E4,0x00E3,0x00E5,0x00E7,0x00E9,0x00E8, @@ -156,7 +226,7 @@ static int mov_read_mac_string(MOVContext *c, AVIOContext *pb, int len, uint8_t t, c = avio_r8(pb); if (c < 0x80 && p < end) *p++ = c; - else + else if (p < end) PUT_UTF8(mac_to_unicode[c-0x80], t, if (p < end) *p++ = t;); } *p = 0; @@ -205,6 +275,17 @@ static int mov_read_covr(MOVContext *c, AVIOContext *pb, int type, int len) return 0; } +static int mov_metadata_raw(MOVContext *c, AVIOContext *pb, + unsigned len, const char *key) +{ + char *value = av_malloc(len + 1); + if (!value) + return AVERROR(ENOMEM); + avio_read(pb, value, len); + value[len] = 0; + return av_dict_set(&c->fc->metadata, key, value, AV_DICT_DONT_STRDUP_VAL); +} + static int mov_read_udta_string(MOVContext *c, AVIOContext *pb, MOVAtom atom) { #ifdef MOV_EXPORT_ALL_METADATA @@ -216,6 +297,9 @@ static int mov_read_udta_string(MOVContext *c, AVIOContext *pb, MOVAtom atom) uint32_t data_type = 0, str_size; int (*parse)(MOVContext*, AVIOContext*, unsigned, const char*) = NULL; + if (c->itunes_metadata && atom.type == MKTAG('-','-','-','-')) + return mov_read_custom_metadata(c, pb, atom); + switch (atom.type) { case MKTAG(0xa9,'n','a','m'): key = "title"; break; case MKTAG(0xa9,'a','u','t'): @@ -224,6 +308,8 @@ static int mov_read_udta_string(MOVContext *c, AVIOContext *pb, MOVAtom atom) case MKTAG(0xa9,'w','r','t'): key = "composer"; break; case MKTAG( 'c','p','r','t'): case MKTAG(0xa9,'c','p','y'): key = "copyright"; break; + case MKTAG(0xa9,'g','r','p'): key = "grouping"; break; + case MKTAG(0xa9,'l','y','r'): key = "lyrics"; break; case MKTAG(0xa9,'c','m','t'): case MKTAG(0xa9,'i','n','f'): key = "comment"; break; case MKTAG(0xa9,'a','l','b'): key = "album"; break; @@ -234,6 +320,9 @@ static int mov_read_udta_string(MOVContext *c, AVIOContext *pb, MOVAtom atom) case MKTAG(0xa9,'t','o','o'): case MKTAG(0xa9,'s','w','r'): key = "encoder"; break; case MKTAG(0xa9,'e','n','c'): key = "encoder"; break; + case MKTAG(0xa9,'m','a','k'): key = "make"; break; + case MKTAG(0xa9,'m','o','d'): key = "model"; break; + case MKTAG(0xa9,'x','y','z'): key = "location"; break; case MKTAG( 'd','e','s','c'): key = "description";break; case MKTAG( 'l','d','e','s'): key = "synopsis"; break; case MKTAG( 't','v','s','h'): key = "show"; break; @@ -253,6 +342,10 @@ static int mov_read_udta_string(MOVContext *c, AVIOContext *pb, MOVAtom atom) parse = mov_metadata_int8_no_padding; break; case MKTAG( 'p','g','a','p'): key = "gapless_playback"; parse = mov_metadata_int8_no_padding; break; + case MKTAG( '@','P','R','M'): + return mov_metadata_raw(c, pb, atom.size, "premiere_version"); + case MKTAG( '@','P','R','Q'): + return mov_metadata_raw(c, pb, atom.size, "quicktime_version"); } if (c->itunes_metadata && atom.size > 8) { @@ -297,7 +390,7 @@ static int mov_read_udta_string(MOVContext *c, AVIOContext *pb, MOVAtom atom) if (parse) parse(c, pb, str_size, key); else { - if (data_type == 3 || (data_type == 0 && langcode < 0x800)) { // MAC Encoded + if (data_type == 3 || (data_type == 0 && (langcode < 0x400 || langcode == 0x7fff))) { // MAC Encoded mov_read_mac_string(c, pb, str_size, str, sizeof(str)); } else { avio_read(pb, str, str_size); @@ -366,6 +459,7 @@ static int mov_read_dref(MOVContext *c, AVIOContext *pb, MOVAtom atom) entries >= UINT_MAX / sizeof(*sc->drefs)) return AVERROR_INVALIDDATA; av_free(sc->drefs); + sc->drefs_count = 0; sc->drefs = av_mallocz(entries * sizeof(*sc->drefs)); if (!sc->drefs) return AVERROR(ENOMEM); @@ -415,7 +509,7 @@ static int mov_read_dref(MOVContext *c, AVIOContext *pb, MOVAtom atom) avio_skip(pb, 16); for (type = 0; type != -1 && avio_tell(pb) < next; ) { - if (pb->eof_reached) + if(url_feof(pb)) return AVERROR_EOF; type = avio_rb16(pb); len = avio_rb16(pb); @@ -462,6 +556,8 @@ static int mov_read_hdlr(MOVContext *c, AVIOContext *pb, MOVAtom atom) AVStream *st; uint32_t type; uint32_t av_unused ctype; + int title_size; + char *title_str; if (c->fc->nb_streams < 1) // meta before first trak return 0; @@ -491,6 +587,19 @@ static int mov_read_hdlr(MOVContext *c, AVIOContext *pb, MOVAtom atom) avio_rb32(pb); /* component flags */ avio_rb32(pb); /* component flags mask */ + title_size = atom.size - 24; + if (title_size > 0) { + title_str = av_malloc(title_size + 1); /* Add null terminator */ + if (!title_str) + return AVERROR(ENOMEM); + avio_read(pb, title_str, title_size); + title_str[title_size] = 0; + if (title_str[0]) + av_dict_set(&st->metadata, "handler_name", title_str + + (!c->isom && title_str[0] == title_size - 1), 0); + av_freep(&title_str); + } + return 0; } @@ -584,6 +693,9 @@ static int mov_read_chan(MOVContext *c, AVIOContext *pb, MOVAtom atom) if (atom.size < 16) return 0; + /* skip version and flags */ + avio_skip(pb, 4); + ff_mov_read_chan(c->fc, pb, st, atom.size - 4); return 0; @@ -597,7 +709,9 @@ static int mov_read_wfex(MOVContext *c, AVIOContext *pb, MOVAtom atom) return 0; st = c->fc->streams[c->fc->nb_streams-1]; - ff_get_wav_header(pb, st->codec, atom.size); + if (ff_get_wav_header(pb, st->codec, atom.size) < 0) { + av_log(c->fc, AV_LOG_WARNING, "get_wav_header failed\n"); + } return 0; } @@ -671,6 +785,12 @@ static int mov_read_moov(MOVContext *c, AVIOContext *pb, MOVAtom atom) { int ret; + if (c->found_moov) { + av_log(c->fc, AV_LOG_WARNING, "Found duplicated MOOV Atom. Skipped it\n"); + avio_skip(pb, atom.size); + return 0; + } + if ((ret = mov_read_default(c, pb, atom)) < 0) return ret; /* we parsed the 'moov' atom, we can terminate the parsing as soon as we find the 'mdat' */ @@ -686,13 +806,16 @@ static int mov_read_moof(MOVContext *c, AVIOContext *pb, MOVAtom atom) return mov_read_default(c, pb, atom); } -static void mov_metadata_creation_time(AVDictionary **metadata, time_t time) +static void mov_metadata_creation_time(AVDictionary **metadata, int64_t time) { char buffer[32]; if (time) { struct tm *ptm; - time -= 2082844800; /* seconds between 1904-01-01 and Epoch */ - ptm = gmtime(&time); + time_t timet; + if(time >= 2082844800) + time -= 2082844800; /* seconds between 1904-01-01 and Epoch */ + timet = time; + ptm = gmtime(&timet); if (!ptm) return; strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", ptm); av_dict_set(metadata, "creation_time", buffer, 0); @@ -706,7 +829,7 @@ static int mov_read_mdhd(MOVContext *c, AVIOContext *pb, MOVAtom atom) int version; char language[4] = {0}; unsigned lang; - time_t creation_time; + int64_t creation_time; if (c->fc->nb_streams < 1) return 0; @@ -746,7 +869,7 @@ static int mov_read_mdhd(MOVContext *c, AVIOContext *pb, MOVAtom atom) static int mov_read_mvhd(MOVContext *c, AVIOContext *pb, MOVAtom atom) { - time_t creation_time; + int64_t creation_time; int version = avio_r8(pb); /* version */ avio_rb24(pb); /* flags */ @@ -763,6 +886,10 @@ static int mov_read_mvhd(MOVContext *c, AVIOContext *pb, MOVAtom atom) av_dlog(c->fc, "time scale = %i\n", c->time_scale); c->duration = (version == 1) ? avio_rb64(pb) : avio_rb32(pb); /* duration */ + // set the AVCodecContext duration because the duration of individual tracks + // may be inaccurate + if (c->time_scale > 0 && !c->trex_data) + c->fc->duration = av_rescale(c->duration, AV_TIME_BASE, c->time_scale); avio_rb32(pb); /* preferred scale */ avio_rb16(pb); /* preferred volume */ @@ -778,31 +905,6 @@ static int mov_read_mvhd(MOVContext *c, AVIOContext *pb, MOVAtom atom) avio_rb32(pb); /* selection duration */ avio_rb32(pb); /* current time */ avio_rb32(pb); /* next track ID */ - - return 0; -} - -static int mov_read_smi(MOVContext *c, AVIOContext *pb, MOVAtom atom) -{ - AVStream *st; - - if (c->fc->nb_streams < 1) - return 0; - st = c->fc->streams[c->fc->nb_streams-1]; - - if ((uint64_t)atom.size > (1<<30)) - return AVERROR_INVALIDDATA; - - // currently SVQ3 decoder expect full STSD header - so let's fake it - // this should be fixed and just SMI header should be passed - av_free(st->codec->extradata); - st->codec->extradata = av_mallocz(atom.size + 0x5a + FF_INPUT_BUFFER_PADDING_SIZE); - if (!st->codec->extradata) - return AVERROR(ENOMEM); - st->codec->extradata_size = 0x5a + atom.size; - memcpy(st->codec->extradata, "SVQ3", 4); // fake - avio_read(pb, st->codec->extradata + 0x5a, atom.size); - av_dlog(c->fc, "Reading SMI %"PRId64" %s\n", atom.size, st->codec->extradata + 0x5a); return 0; } @@ -815,7 +917,7 @@ static int mov_read_enda(MOVContext *c, AVIOContext *pb, MOVAtom atom) return 0; st = c->fc->streams[c->fc->nb_streams-1]; - little_endian = avio_rb16(pb); + little_endian = avio_rb16(pb) & 0xFF; av_dlog(c->fc, "enda %d\n", little_endian); if (little_endian == 1) { switch (st->codec->codec_id) { @@ -873,7 +975,8 @@ static int mov_read_fiel(MOVContext *c, AVIOContext *pb, MOVAtom atom) } /* FIXME modify qdm2/svq3/h264 decoders to take full atom as extradata */ -static int mov_read_extradata(MOVContext *c, AVIOContext *pb, MOVAtom atom) +static int mov_read_extradata(MOVContext *c, AVIOContext *pb, MOVAtom atom, + enum AVCodecID codec_id) { AVStream *st; uint64_t size; @@ -883,6 +986,10 @@ static int mov_read_extradata(MOVContext *c, AVIOContext *pb, MOVAtom atom) if (c->fc->nb_streams < 1) // will happen with jp2 files return 0; st= c->fc->streams[c->fc->nb_streams-1]; + + if (st->codec->codec_id != codec_id) + return 0; /* unexpected codec_id - don't mess with extradata */ + size= (uint64_t)st->codec->extradata_size + atom.size + 8 + FF_INPUT_BUFFER_PADDING_SIZE; if (size > INT_MAX || (uint64_t)atom.size > INT_MAX) return AVERROR_INVALIDDATA; @@ -898,6 +1005,62 @@ static int mov_read_extradata(MOVContext *c, AVIOContext *pb, MOVAtom atom) return 0; } +/* wrapper functions for reading ALAC/AVS/MJPEG/MJPEG2000 extradata atoms only for those codecs */ +static int mov_read_alac(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + return mov_read_extradata(c, pb, atom, AV_CODEC_ID_ALAC); +} + +static int mov_read_avss(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + return mov_read_extradata(c, pb, atom, AV_CODEC_ID_AVS); +} + +static int mov_read_jp2h(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + return mov_read_extradata(c, pb, atom, AV_CODEC_ID_JPEG2000); +} + +static int mov_read_avid(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + return mov_read_extradata(c, pb, atom, AV_CODEC_ID_AVUI); +} + +static int mov_read_targa_y216(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + int ret = mov_read_extradata(c, pb, atom, AV_CODEC_ID_TARGA_Y216); + + if (!ret && c->fc->nb_streams >= 1) { + AVCodecContext *avctx = c->fc->streams[c->fc->nb_streams-1]->codec; + if (avctx->extradata_size >= 40) { + avctx->height = AV_RB16(&avctx->extradata[36]); + avctx->width = AV_RB16(&avctx->extradata[38]); + } + } + return ret; +} + +static int mov_read_ares(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + AVCodecContext *codec = c->fc->streams[c->fc->nb_streams-1]->codec; + if (codec->codec_tag == MKTAG('A', 'V', 'i', 'n') && + codec->codec_id == AV_CODEC_ID_H264 && + atom.size > 11) { + avio_skip(pb, 10); + /* For AVID AVCI50, force width of 1440 to be able to select the correct SPS and PPS */ + if (avio_rb16(pb) == 0xd4d) + codec->width = 1440; + return 0; + } + + return mov_read_avid(c, pb, atom); +} + +static int mov_read_svq3(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + return mov_read_extradata(c, pb, atom, AV_CODEC_ID_SVQ3); +} + static int mov_read_wave(MOVContext *c, AVIOContext *pb, MOVAtom atom) { AVStream *st; @@ -909,13 +1072,13 @@ static int mov_read_wave(MOVContext *c, AVIOContext *pb, MOVAtom atom) if ((uint64_t)atom.size > (1<<30)) return AVERROR_INVALIDDATA; - if (st->codec->codec_id == AV_CODEC_ID_QDM2 || st->codec->codec_id == AV_CODEC_ID_QDMC) { + if (st->codec->codec_id == AV_CODEC_ID_QDM2 || + st->codec->codec_id == AV_CODEC_ID_QDMC || + st->codec->codec_id == AV_CODEC_ID_SPEEX) { // pass all frma atom to codec, needed at least for QDMC and QDM2 av_free(st->codec->extradata); - st->codec->extradata = av_mallocz(atom.size + FF_INPUT_BUFFER_PADDING_SIZE); - if (!st->codec->extradata) + if (ff_alloc_extradata(st->codec, atom.size)) return AVERROR(ENOMEM); - st->codec->extradata_size = atom.size; avio_read(pb, st->codec->extradata, atom.size); } else if (atom.size > 8) { /* to read frma, esds atoms */ int ret; @@ -951,10 +1114,8 @@ static int mov_read_glbl(MOVContext *c, AVIOContext *pb, MOVAtom atom) return mov_read_default(c, pb, atom); } av_free(st->codec->extradata); - st->codec->extradata = av_mallocz(atom.size + FF_INPUT_BUFFER_PADDING_SIZE); - if (!st->codec->extradata) + if (ff_alloc_extradata(st->codec, atom.size)) return AVERROR(ENOMEM); - st->codec->extradata_size = atom.size; avio_read(pb, st->codec->extradata, atom.size); return 0; } @@ -976,10 +1137,8 @@ static int mov_read_dvc1(MOVContext *c, AVIOContext *pb, MOVAtom atom) return 0; av_free(st->codec->extradata); - st->codec->extradata = av_mallocz(atom.size - 7 + FF_INPUT_BUFFER_PADDING_SIZE); - if (!st->codec->extradata) + if (ff_alloc_extradata(st->codec, atom.size - 7)) return AVERROR(ENOMEM); - st->codec->extradata_size = atom.size - 7; avio_seek(pb, 6, SEEK_CUR); avio_read(pb, st->codec->extradata, st->codec->extradata_size); return 0; @@ -1004,10 +1163,8 @@ static int mov_read_strf(MOVContext *c, AVIOContext *pb, MOVAtom atom) return AVERROR_INVALIDDATA; av_free(st->codec->extradata); - st->codec->extradata = av_mallocz(atom.size - 40 + FF_INPUT_BUFFER_PADDING_SIZE); - if (!st->codec->extradata) + if (ff_alloc_extradata(st->codec, atom.size - 40)) return AVERROR(ENOMEM); - st->codec->extradata_size = atom.size - 40; avio_skip(pb, 40); avio_read(pb, st->codec->extradata, atom.size - 40); return 0; @@ -1089,7 +1246,9 @@ static int mov_codec_id(AVStream *st, uint32_t format) id = ff_codec_get_id(ff_codec_bmp_tags, format); if (id > 0) st->codec->codec_type = AVMEDIA_TYPE_VIDEO; - else if (st->codec->codec_type == AVMEDIA_TYPE_DATA) { + else if (st->codec->codec_type == AVMEDIA_TYPE_DATA || + (st->codec->codec_type == AVMEDIA_TYPE_SUBTITLE && + st->codec->codec_id == AV_CODEC_ID_NONE)) { id = ff_codec_get_id(ff_codec_movsubtitle_tags, format); if (id > 0) st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE; @@ -1129,8 +1288,11 @@ static void mov_parse_stsd_video(MOVContext *c, AVIOContext *pb, if (len < 31) avio_skip(pb, 31 - len); /* codec_tag YV12 triggers an UV swap in rawdec.c */ - if (!memcmp(st->codec->codec_name, "Planar Y'CbCr 8-bit 4:2:0", 25)) + if (!memcmp(st->codec->codec_name, "Planar Y'CbCr 8-bit 4:2:0", 25)) { st->codec->codec_tag = MKTAG('I', '4', '2', '0'); + st->codec->width &= ~1; + st->codec->height &= ~1; + } /* Flash Media Server uses tag H263 with Sorenson Spark */ if (st->codec->codec_tag == MKTAG('H','2','6','3') && !memcmp(st->codec->codec_name, "Sorenson H263", 13)) @@ -1148,7 +1310,7 @@ static void mov_parse_stsd_video(MOVContext *c, AVIOContext *pb, if ((color_depth == 2) || (color_depth == 4) || (color_depth == 8)) { /* for palette traversal */ unsigned int color_start, color_count, color_end; - unsigned char r, g, b; + unsigned char a, r, g, b; if (color_greyscale) { int color_index, color_dec; @@ -1158,8 +1320,11 @@ static void mov_parse_stsd_video(MOVContext *c, AVIOContext *pb, color_index = 255; color_dec = 256 / (color_count - 1); for (j = 0; j < color_count; j++) { + if (st->codec->codec_id == AV_CODEC_ID_CINEPAK){ + r = g = b = color_count - 1 - color_index; + } else r = g = b = color_index; - sc->palette[j] = (r << 16) | (g << 8) | (b); + sc->palette[j] = (0xFFU << 24) | (r << 16) | (g << 8) | (b); color_index -= color_dec; if (color_index < 0) color_index = 0; @@ -1179,7 +1344,7 @@ static void mov_parse_stsd_video(MOVContext *c, AVIOContext *pb, r = color_table[j * 3 + 0]; g = color_table[j * 3 + 1]; b = color_table[j * 3 + 2]; - sc->palette[j] = (r << 16) | (g << 8) | (b); + sc->palette[j] = (0xFFU << 24) | (r << 16) | (g << 8) | (b); } } else { /* load the palette from the file */ @@ -1188,10 +1353,9 @@ static void mov_parse_stsd_video(MOVContext *c, AVIOContext *pb, color_end = avio_rb16(pb); if ((color_start <= 255) && (color_end <= 255)) { for (j = color_start; j <= color_end; j++) { - /* each R, G, or B component is 16 bits; - * only use the top 8 bits; skip alpha bytes - * up front */ - avio_r8(pb); + /* each A, R, G, or B component is 16 bits; + * only use the top 8 bits */ + a = avio_r8(pb); avio_r8(pb); r = avio_r8(pb); avio_r8(pb); @@ -1199,7 +1363,7 @@ static void mov_parse_stsd_video(MOVContext *c, AVIOContext *pb, avio_r8(pb); b = avio_r8(pb); avio_r8(pb); - sc->palette[j] = (r << 16) | (g << 8) | (b); + sc->palette[j] = (a << 24 ) | (r << 16) | (g << 8) | (b); } } } @@ -1212,6 +1376,7 @@ static void mov_parse_stsd_audio(MOVContext *c, AVIOContext *pb, { int bits_per_sample, flags; uint16_t version = avio_rb16(pb); + AVDictionaryEntry *compatible_brands = av_dict_get(c->fc->metadata, "compatible_brands", NULL, AV_DICT_MATCH_CASE); avio_rb16(pb); /* revision level */ avio_rb32(pb); /* vendor */ @@ -1227,7 +1392,9 @@ static void mov_parse_stsd_audio(MOVContext *c, AVIOContext *pb, // Read QT version 1 fields. In version 0 these do not exist. av_dlog(c->fc, "version =%d, isom =%d\n", version, c->isom); - if (!c->isom) { + if (!c->isom || + (compatible_brands && strstr(compatible_brands->value, "qt "))) { + if (version == 1) { sc->samples_per_frame = avio_rb32(pb); avio_rb32(pb); /* bytes per packet */ @@ -1312,11 +1479,19 @@ static int mov_parse_stsd_data(MOVContext *c, AVIOContext *pb, int size) { if (st->codec->codec_tag == MKTAG('t','m','c','d')) { - st->codec->extradata_size = size; - st->codec->extradata = av_malloc(size + FF_INPUT_BUFFER_PADDING_SIZE); - if (!st->codec->extradata) + if (ff_alloc_extradata(st->codec, size)) return AVERROR(ENOMEM); avio_read(pb, st->codec->extradata, size); + if (size > 16) { + MOVStreamContext *tmcd_ctx = st->priv_data; + int val; + val = AV_RB32(st->codec->extradata + 4); + tmcd_ctx->tmcd_flags = val; + if (val & 1) + st->codec->flags2 |= CODEC_FLAG2_DROP_FRAME_TIMECODE; + st->codec->time_base.den = st->codec->extradata[16]; /* number of frame */ + st->codec->time_base.num = 1; + } } else { /* other codec type, just skip (rtp, mp4s ...) */ avio_skip(pb, size); @@ -1371,6 +1546,9 @@ static int mov_finalize_stsd_codec(MOVContext *c, AVIOContext *pb, case AV_CODEC_ID_ADPCM_MS: case AV_CODEC_ID_ADPCM_IMA_WAV: case AV_CODEC_ID_ILBC: + case AV_CODEC_ID_MACE3: + case AV_CODEC_ID_MACE6: + case AV_CODEC_ID_QDM2: st->codec->block_align = sc->bytes_per_frame; break; case AV_CODEC_ID_ALAC: @@ -1379,6 +1557,12 @@ static int mov_finalize_stsd_codec(MOVContext *c, AVIOContext *pb, st->codec->sample_rate = AV_RB32(st->codec->extradata + 32); } break; + case AV_CODEC_ID_AC3: + st->need_parsing = AVSTREAM_PARSE_FULL; + break; + case AV_CODEC_ID_MPEG1VIDEO: + st->need_parsing = AVSTREAM_PARSE_FULL; + break; case AV_CODEC_ID_VC1: st->need_parsing = AVSTREAM_PARSE_FULL; break; @@ -1395,12 +1579,9 @@ static int mov_skip_multiple_stsd(MOVContext *c, AVIOContext *pb, int video_codec_id = ff_codec_get_id(ff_codec_movvideo_tags, format); if (codec_tag && - (codec_tag == AV_RL32("avc1") || - codec_tag == AV_RL32("hvc1") || - codec_tag == AV_RL32("hev1") || (codec_tag != format && (c->fc->video_codec_id ? video_codec_id != c->fc->video_codec_id - : codec_tag != MKTAG('j','p','e','g'))))) { + : codec_tag != MKTAG('j','p','e','g')))) { /* Multiple fourcc, we skip JPEG. This is not correct, we should * export it as a separate AVStream but this needs a few changes * in the MOV demuxer, patch welcome. */ @@ -1409,6 +1590,11 @@ static int mov_skip_multiple_stsd(MOVContext *c, AVIOContext *pb, avio_skip(pb, size); return 1; } + if ( codec_tag == AV_RL32("avc1") || + codec_tag == AV_RL32("hvc1") || + codec_tag == AV_RL32("hev1") + ) + av_log(c->fc, AV_LOG_WARNING, "Concatenated H.264 or H.265 might not play correctly.\n"); return 0; } @@ -1432,15 +1618,15 @@ int ff_mov_read_stsd_entries(MOVContext *c, AVIOContext *pb, int entries) int ret, dref_id = 1; MOVAtom a = { AV_RL32("stsd") }; int64_t start_pos = avio_tell(pb); - uint32_t size = avio_rb32(pb); /* size */ + int64_t size = avio_rb32(pb); /* size */ uint32_t format = avio_rl32(pb); /* data format */ if (size >= 16) { avio_rb32(pb); /* reserved */ avio_rb16(pb); /* reserved */ dref_id = avio_rb16(pb); - } else { - av_log(c->fc, AV_LOG_ERROR, "invalid size %d in stsd\n", size); + }else if (size <= 7){ + av_log(c->fc, AV_LOG_ERROR, "invalid size %"PRId64" in stsd\n", size); return AVERROR_INVALIDDATA; } @@ -1453,7 +1639,7 @@ int ff_mov_read_stsd_entries(MOVContext *c, AVIOContext *pb, int entries) id = mov_codec_id(st, format); - av_dlog(c->fc, "size=%d 4CC= %c%c%c%c codec_type=%d\n", size, + av_dlog(c->fc, "size=%"PRId64" 4CC= %c%c%c%c codec_type=%d\n", size, (format >> 0) & 0xff, (format >> 8) & 0xff, (format >> 16) & 0xff, (format >> 24) & 0xff, st->codec->codec_type); @@ -1593,6 +1779,8 @@ static int mov_read_stss(MOVContext *c, AVIOContext *pb, MOVAtom atom) if (!entries) { sc->keyframe_absent = 1; + if (!st->need_parsing) + st->need_parsing = AVSTREAM_PARSE_HEADERS; return 0; } if (entries >= UINT_MAX / sizeof(int)) @@ -1634,6 +1822,7 @@ static int mov_read_stsz(MOVContext *c, AVIOContext *pb, MOVAtom atom) sample_size = avio_rb32(pb); if (!sc->sample_size) /* do not overwrite value computed in stsd */ sc->sample_size = sample_size; + sc->stsz_sample_size = sample_size; field_size = 32; } else { sample_size = 0; @@ -1711,10 +1900,8 @@ static int mov_read_stts(MOVContext *c, AVIOContext *pb, MOVAtom atom) av_dlog(c->fc, "track[%i].stts.entries = %i\n", c->fc->nb_streams-1, entries); - if (!entries) - return 0; if (entries >= UINT_MAX / sizeof(*sc->stts_data)) - return AVERROR(EINVAL); + return -1; sc->stts_data = av_malloc(entries * sizeof(*sc->stts_data)); if (!sc->stts_data) @@ -1726,6 +1913,12 @@ static int mov_read_stts(MOVContext *c, AVIOContext *pb, MOVAtom atom) sample_count=avio_rb32(pb); sample_duration = avio_rb32(pb); + + /* sample_duration < 0 is invalid based on the spec */ + if (sample_duration < 0) { + av_log(c->fc, AV_LOG_ERROR, "Invalid SampleDelta in STTS %d\n", sample_duration); + sample_duration = 1; + } if (sample_count < 0) { av_log(c->fc, AV_LOG_ERROR, "Invalid sample_count=%d\n", sample_count); return AVERROR_INVALIDDATA; @@ -1752,6 +1945,13 @@ static int mov_read_stts(MOVContext *c, AVIOContext *pb, MOVAtom atom) return 0; } +static void mov_update_dts_shift(MOVStreamContext *sc, int duration) +{ + if (duration < 0) { + sc->dts_shift = FFMAX(sc->dts_shift, -duration); + } +} + static int mov_read_ctts(MOVContext *c, AVIOContext *pb, MOVAtom atom) { AVStream *st; @@ -1783,8 +1983,19 @@ static int mov_read_ctts(MOVContext *c, AVIOContext *pb, MOVAtom atom) sc->ctts_data[i].count = count; sc->ctts_data[i].duration= duration; - if (duration < 0) - sc->dts_shift = FFMAX(sc->dts_shift, -duration); + + av_dlog(c->fc, "count=%d, duration=%d\n", + count, duration); + + if (FFABS(duration) > (1<<28) && i+2<entries) { + av_log(c->fc, AV_LOG_WARNING, "CTTS invalid\n"); + av_freep(&sc->ctts_data); + sc->ctts_count = 0; + return 0; + } + + if (i+2<entries) + mov_update_dts_shift(sc, duration); } sc->ctts_count = i; @@ -1850,12 +2061,13 @@ static void mov_build_index(MOVContext *mov, AVStream *st) uint64_t stream_size = 0; /* adjust first dts according to edit list */ - if (sc->time_offset && mov->time_scale > 0) { - if (sc->time_offset < 0) - sc->time_offset = av_rescale(sc->time_offset, sc->time_scale, mov->time_scale); + if ((sc->empty_duration || sc->start_time) && mov->time_scale > 0) { + if (sc->empty_duration) + sc->empty_duration = av_rescale(sc->empty_duration, sc->time_scale, mov->time_scale); + sc->time_offset = sc->start_time - sc->empty_duration; current_dts = -sc->time_offset; - if (sc->ctts_data && sc->stts_data && sc->stts_data[0].duration && - sc->ctts_data[0].duration / sc->stts_data[0].duration > 16) { + if (sc->ctts_count>0 && sc->stts_count>0 && + sc->ctts_data[0].duration / FFMAX(sc->stts_data[0].duration, 1) > 16) { /* more than 16 frames delay, dts are likely wrong this happens with files created by iMovie */ sc->wrong_dts = 1; @@ -1873,11 +2085,11 @@ static void mov_build_index(MOVContext *mov, AVStream *st) unsigned int rap_group_index = 0; unsigned int rap_group_sample = 0; int rap_group_present = sc->rap_group_count && sc->rap_group; - int key_off = (sc->keyframes && sc->keyframes[0] > 0) || (sc->stps_data && sc->stps_data[0] > 0); + int key_off = (sc->keyframe_count && sc->keyframes[0] > 0) || (sc->stps_count && sc->stps_data[0] > 0); current_dts -= sc->dts_shift; - if (!sc->sample_count) + if (!sc->sample_count || st->nb_index_entries) return; if (sc->sample_count >= UINT_MAX / sizeof(*st->index_entries) - st->nb_index_entries) return; @@ -1890,10 +2102,22 @@ static void mov_build_index(MOVContext *mov, AVStream *st) st->index_entries_allocated_size = (st->nb_index_entries + sc->sample_count) * sizeof(*st->index_entries); for (i = 0; i < sc->chunk_count; i++) { + int64_t next_offset = i+1 < sc->chunk_count ? sc->chunk_offsets[i+1] : INT64_MAX; current_offset = sc->chunk_offsets[i]; while (stsc_index + 1 < sc->stsc_count && i + 1 == sc->stsc_data[stsc_index + 1].first) stsc_index++; + + if (next_offset > current_offset && sc->sample_size>0 && sc->sample_size < sc->stsz_sample_size && + sc->stsc_data[stsc_index].count * (int64_t)sc->stsz_sample_size > next_offset - current_offset) { + av_log(mov->fc, AV_LOG_WARNING, "STSZ sample size %d invalid (too large), ignoring\n", sc->stsz_sample_size); + sc->stsz_sample_size = sc->sample_size; + } + if (sc->stsz_sample_size>0 && sc->stsz_sample_size < sc->sample_size) { + av_log(mov->fc, AV_LOG_WARNING, "STSZ sample size %d invalid (too small), ignoring\n", sc->stsz_sample_size); + sc->stsz_sample_size = sc->sample_size; + } + for (j = 0; j < sc->stsc_data[stsc_index].count; j++) { int keyframe = 0; if (current_sample >= sc->sample_count) { @@ -1920,7 +2144,7 @@ static void mov_build_index(MOVContext *mov, AVStream *st) } if (keyframe) distance = 0; - sample_size = sc->sample_size > 0 ? sc->sample_size : sc->sample_sizes[current_sample]; + sample_size = sc->stsz_sample_size > 0 ? sc->stsz_sample_size : sc->sample_sizes[current_sample]; if (sc->pseudo_stream_id == -1 || sc->stsc_data[stsc_index].id - 1 == sc->pseudo_stream_id) { AVIndexEntry *e = &st->index_entries[st->nb_index_entries++]; @@ -2037,14 +2261,14 @@ static void mov_build_index(MOVContext *mov, AVStream *st) } } -static int mov_open_dref(AVIOContext **pb, char *src, MOVDref *ref, - AVIOInterruptCB *int_cb) +static int mov_open_dref(AVIOContext **pb, const char *src, MOVDref *ref, + AVIOInterruptCB *int_cb, int use_absolute_path, AVFormatContext *fc) { /* try relative path, we do not try the absolute because it can leak information about our system to an attacker */ if (ref->nlvl_to > 0 && ref->nlvl_from > 0) { char filename[1024]; - char *src_path; + const char *src_path; int i, l; /* find a source dir */ @@ -2076,11 +2300,26 @@ static int mov_open_dref(AVIOContext **pb, char *src, MOVDref *ref, if (!avio_open2(pb, filename, AVIO_FLAG_READ, int_cb, NULL)) return 0; } + } else if (use_absolute_path) { + av_log(fc, AV_LOG_WARNING, "Using absolute path on user request, " + "this is a possible security issue\n"); + if (!avio_open2(pb, ref->path, AVIO_FLAG_READ, int_cb, NULL)) + return 0; } return AVERROR(ENOENT); } +static void fix_timescale(MOVContext *c, MOVStreamContext *sc) +{ + if (sc->time_scale <= 0) { + av_log(c->fc, AV_LOG_WARNING, "stream %d, timescale not set\n", sc->ffindex); + sc->time_scale = c->time_scale; + if (sc->time_scale <= 0) + sc->time_scale = 1; + } +} + static int mov_read_trak(MOVContext *c, AVIOContext *pb, MOVAtom atom) { AVStream *st; @@ -2108,12 +2347,7 @@ static int mov_read_trak(MOVContext *c, AVIOContext *pb, MOVAtom atom) return 0; } - if (sc->time_scale <= 0) { - av_log(c->fc, AV_LOG_WARNING, "stream %d, timescale not set\n", st->index); - sc->time_scale = c->time_scale; - if (sc->time_scale <= 0) - sc->time_scale = 1; - } + fix_timescale(c, sc); avpriv_set_pts_info(st, 64, 1, sc->time_scale); @@ -2121,14 +2355,17 @@ static int mov_read_trak(MOVContext *c, AVIOContext *pb, MOVAtom atom) if (sc->dref_id-1 < sc->drefs_count && sc->drefs[sc->dref_id-1].path) { MOVDref *dref = &sc->drefs[sc->dref_id - 1]; - if (mov_open_dref(&sc->pb, c->fc->filename, dref, &c->fc->interrupt_callback) < 0) + if (mov_open_dref(&sc->pb, c->fc->filename, dref, &c->fc->interrupt_callback, + c->use_absolute_path, c->fc) < 0) av_log(c->fc, AV_LOG_ERROR, "stream %d, error opening alias: path='%s', dir='%s', " "filename='%s', volume='%s', nlvl_from=%d, nlvl_to=%d\n", st->index, dref->path, dref->dir, dref->filename, dref->volume, dref->nlvl_from, dref->nlvl_to); - } else + } else { sc->pb = c->fc->pb; + sc->pb_is_copied = 1; + } if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) { if (!st->sample_aspect_ratio.num && @@ -2137,9 +2374,21 @@ static int mov_read_trak(MOVContext *c, AVIOContext *pb, MOVAtom atom) ((double)st->codec->width * sc->height), INT_MAX); } - if (st->duration != AV_NOPTS_VALUE && st->duration > 0) + if (st->duration > 0) av_reduce(&st->avg_frame_rate.num, &st->avg_frame_rate.den, sc->time_scale*st->nb_frames, st->duration, INT_MAX); + +#if FF_API_R_FRAME_RATE + if (sc->stts_count == 1 || (sc->stts_count == 2 && sc->stts_data[1].count == 1)) + av_reduce(&st->r_frame_rate.num, &st->r_frame_rate.den, + sc->time_scale, sc->stts_data[0].duration, INT_MAX); +#endif + } + + // done for ai5q, ai52, ai55, ai1q, ai12 and ai15. + if (!st->codec->extradata_size && st->codec->codec_id == AV_CODEC_ID_H264 && + st->codec->codec_tag != MKTAG('a', 'v', 'c', '1')) { + ff_generate_avci_extradata(st); } switch (st->codec->codec_id) { @@ -2247,6 +2496,21 @@ static int mov_read_tkhd(MOVContext *c, AVIOContext *pb, MOVAtom atom) sc->width = width >> 16; sc->height = height >> 16; + //Assign clockwise rotate values based on transform matrix so that + //we can compensate for iPhone orientation during capture. + + if (display_matrix[1][0] == -65536 && display_matrix[0][1] == 65536) { + av_dict_set(&st->metadata, "rotate", "90", 0); + } + + if (display_matrix[0][0] == -65536 && display_matrix[1][1] == -65536) { + av_dict_set(&st->metadata, "rotate", "180", 0); + } + + if (display_matrix[1][0] == 65536 && display_matrix[0][1] == -65536) { + av_dict_set(&st->metadata, "rotate", "270", 0); + } + // transform the display width/height according to the matrix // skip this if the display matrix is the default identity matrix // or if it is rotating the picture, ex iPhone 3GS @@ -2326,6 +2590,9 @@ static int mov_read_trex(MOVContext *c, AVIOContext *pb, MOVAtom atom) c->trex_count = 0; return err; } + + c->fc->duration = AV_NOPTS_VALUE; // the duration from mvhd is not representing the whole file when fragments are used. + trex = &c->trex_data[c->trex_count++]; avio_r8(pb); /* version */ avio_rb24(pb); /* flags */ @@ -2360,7 +2627,7 @@ static int mov_read_trun(MOVContext *c, AVIOContext *pb, MOVAtom atom) return AVERROR_INVALIDDATA; } sc = st->priv_data; - if (sc->pseudo_stream_id+1 != frag->stsd_id) + if (sc->pseudo_stream_id+1 != frag->stsd_id && sc->pseudo_stream_id != -1) return 0; avio_r8(pb); /* version */ flags = avio_rb24(pb); @@ -2408,6 +2675,7 @@ static int mov_read_trun(MOVContext *c, AVIOContext *pb, MOVAtom atom) sc->ctts_data[sc->ctts_count].count = 1; sc->ctts_data[sc->ctts_count].duration = (flags & MOV_TRUN_SAMPLE_CTS) ? avio_rb32(pb) : 0; + mov_update_dts_shift(sc, sc->ctts_data[sc->ctts_count].duration); sc->ctts_count++; if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) keyframe = 1; @@ -2472,7 +2740,7 @@ static int mov_read_cmov(MOVContext *c, AVIOContext *pb, MOVAtom atom) if (avio_rl32(pb) != MKTAG('d','c','o','m')) return AVERROR_INVALIDDATA; if (avio_rl32(pb) != MKTAG('z','l','i','b')) { - av_log(c->fc, AV_LOG_ERROR, "unknown compression for cmov atom !"); + av_log(c->fc, AV_LOG_ERROR, "unknown compression for cmov atom !\n"); return AVERROR_INVALIDDATA; } avio_rb32(pb); /* cmvd atom */ @@ -2511,9 +2779,10 @@ free_and_return: static int mov_read_elst(MOVContext *c, AVIOContext *pb, MOVAtom atom) { MOVStreamContext *sc; - int i, edit_count, version; + int i, edit_count, version, edit_start_index = 0; + int unsupported = 0; - if (c->fc->nb_streams < 1) + if (c->fc->nb_streams < 1 || c->ignore_editlist) return 0; sc = c->fc->streams[c->fc->nb_streams-1]->priv_data; @@ -2524,9 +2793,11 @@ static int mov_read_elst(MOVContext *c, AVIOContext *pb, MOVAtom atom) if ((uint64_t)edit_count*12+8 > atom.size) return AVERROR_INVALIDDATA; + av_dlog(c->fc, "track[%i].edit_count = %i\n", c->fc->nb_streams-1, edit_count); for (i=0; i<edit_count; i++){ int64_t time; int64_t duration; + int rate; if (version == 1) { duration = avio_rb64(pb); time = avio_rb64(pb); @@ -2534,22 +2805,109 @@ static int mov_read_elst(MOVContext *c, AVIOContext *pb, MOVAtom atom) duration = avio_rb32(pb); /* segment duration */ time = (int32_t)avio_rb32(pb); /* media time */ } - avio_rb32(pb); /* Media rate */ - if (i == 0 && time >= -1) { - sc->time_offset = time != -1 ? time : -duration; - } + rate = avio_rb32(pb); + if (i == 0 && time == -1) { + sc->empty_duration = duration; + edit_start_index = 1; + } else if (i == edit_start_index && time >= 0) + sc->start_time = time; + else + unsupported = 1; + + av_dlog(c->fc, "duration=%"PRId64" time=%"PRId64" rate=%f\n", + duration, time, rate / 65536.0); } - if (edit_count > 1) + if (unsupported) av_log(c->fc, AV_LOG_WARNING, "multiple edit list entries, " "a/v desync might occur, patch welcome\n"); - av_dlog(c->fc, "track[%i].edit_count = %i\n", c->fc->nb_streams-1, edit_count); + return 0; +} + +static int mov_read_tmcd(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + MOVStreamContext *sc; + + if (c->fc->nb_streams < 1) + return AVERROR_INVALIDDATA; + sc = c->fc->streams[c->fc->nb_streams - 1]->priv_data; + sc->timecode_track = avio_rb32(pb); + return 0; +} + +static int mov_read_uuid(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + int ret; + uint8_t uuid[16]; + static const uint8_t uuid_isml_manifest[] = { + 0xa5, 0xd4, 0x0b, 0x30, 0xe8, 0x14, 0x11, 0xdd, + 0xba, 0x2f, 0x08, 0x00, 0x20, 0x0c, 0x9a, 0x66 + }; + + if (atom.size < sizeof(uuid) || atom.size == INT64_MAX) + return AVERROR_INVALIDDATA; + + ret = avio_read(pb, uuid, sizeof(uuid)); + if (ret < 0) { + return ret; + } else if (ret != sizeof(uuid)) { + return AVERROR_INVALIDDATA; + } + if (!memcmp(uuid, uuid_isml_manifest, sizeof(uuid))) { + uint8_t *buffer, *ptr; + char *endptr; + size_t len = atom.size - sizeof(uuid); + + if (len < 4) { + return AVERROR_INVALIDDATA; + } + ret = avio_skip(pb, 4); // zeroes + len -= 4; + + buffer = av_mallocz(len + 1); + if (!buffer) { + return AVERROR(ENOMEM); + } + ret = avio_read(pb, buffer, len); + if (ret < 0) { + av_free(buffer); + return ret; + } else if (ret != len) { + av_free(buffer); + return AVERROR_INVALIDDATA; + } + + ptr = buffer; + while ((ptr = av_stristr(ptr, "systemBitrate=\"")) != NULL) { + ptr += sizeof("systemBitrate=\"") - 1; + c->bitrates_count++; + c->bitrates = av_realloc_f(c->bitrates, c->bitrates_count, sizeof(*c->bitrates)); + if (!c->bitrates) { + c->bitrates_count = 0; + av_free(buffer); + return AVERROR(ENOMEM); + } + errno = 0; + ret = strtol(ptr, &endptr, 10); + if (ret < 0 || errno || *endptr != '"') { + c->bitrates[c->bitrates_count - 1] = 0; + } else { + c->bitrates[c->bitrates_count - 1] = ret; + } + } + + av_free(buffer); + } return 0; } static const MOVParseTableEntry mov_default_parse_table[] = { -{ MKTAG('a','v','s','s'), mov_read_extradata }, +{ MKTAG('A','C','L','R'), mov_read_avid }, +{ MKTAG('A','P','R','G'), mov_read_avid }, +{ MKTAG('A','A','L','P'), mov_read_avid }, +{ MKTAG('A','R','E','S'), mov_read_ares }, +{ MKTAG('a','v','s','s'), mov_read_avss }, { MKTAG('c','h','p','l'), mov_read_chpl }, { MKTAG('c','o','6','4'), mov_read_stco }, { MKTAG('c','t','t','s'), mov_read_ctts }, /* composition time to sample */ @@ -2563,7 +2921,7 @@ static const MOVParseTableEntry mov_default_parse_table[] = { { MKTAG('g','l','b','l'), mov_read_glbl }, { MKTAG('h','d','l','r'), mov_read_hdlr }, { MKTAG('i','l','s','t'), mov_read_ilst }, -{ MKTAG('j','p','2','h'), mov_read_extradata }, +{ MKTAG('j','p','2','h'), mov_read_jp2h }, { MKTAG('m','d','a','t'), mov_read_mdat }, { MKTAG('m','d','h','d'), mov_read_mdhd }, { MKTAG('m','d','i','a'), mov_read_default }, @@ -2573,8 +2931,8 @@ static const MOVParseTableEntry mov_default_parse_table[] = { { MKTAG('m','o','o','v'), mov_read_moov }, { MKTAG('m','v','e','x'), mov_read_default }, { MKTAG('m','v','h','d'), mov_read_mvhd }, -{ MKTAG('S','M','I',' '), mov_read_smi }, /* Sorenson extension ??? */ -{ MKTAG('a','l','a','c'), mov_read_extradata }, /* alac specific atom */ +{ MKTAG('S','M','I',' '), mov_read_svq3 }, +{ MKTAG('a','l','a','c'), mov_read_alac }, /* alac specific atom */ { MKTAG('a','v','c','C'), mov_read_glbl }, { MKTAG('p','a','s','p'), mov_read_pasp }, { MKTAG('s','t','b','l'), mov_read_default }, @@ -2592,6 +2950,7 @@ static const MOVParseTableEntry mov_default_parse_table[] = { { MKTAG('t','r','a','k'), mov_read_trak }, { MKTAG('t','r','a','f'), mov_read_default }, { MKTAG('t','r','e','f'), mov_read_default }, +{ MKTAG('t','m','c','d'), mov_read_tmcd }, { MKTAG('c','h','a','p'), mov_read_chap }, { MKTAG('t','r','e','x'), mov_read_trex }, { MKTAG('t','r','u','n'), mov_read_trun }, @@ -2607,6 +2966,8 @@ static const MOVParseTableEntry mov_default_parse_table[] = { { MKTAG('d','v','c','1'), mov_read_dvc1 }, { MKTAG('s','b','g','p'), mov_read_sbgp }, { MKTAG('h','v','c','C'), mov_read_glbl }, +{ MKTAG('u','u','i','d'), mov_read_uuid }, +{ MKTAG('C','i','n', 0x8e), mov_read_targa_y216 }, { 0, NULL } }; @@ -2618,25 +2979,33 @@ static int mov_read_default(MOVContext *c, AVIOContext *pb, MOVAtom atom) if (atom.size < 0) atom.size = INT64_MAX; - while (total_size + 8 < atom.size && !pb->eof_reached) { + while (total_size + 8 <= atom.size && !url_feof(pb)) { int (*parse)(MOVContext*, AVIOContext*, MOVAtom) = NULL; a.size = atom.size; a.type=0; if (atom.size >= 8) { a.size = avio_rb32(pb); a.type = avio_rl32(pb); + if (atom.type != MKTAG('r','o','o','t') && + atom.type != MKTAG('m','o','o','v')) + { + if (a.type == MKTAG('t','r','a','k') || a.type == MKTAG('m','d','a','t')) + { + av_log(c->fc, AV_LOG_ERROR, "Broken file, trak/mdat not at top-level\n"); + avio_skip(pb, -8); + return 0; + } + } + total_size += 8; + if (a.size == 1) { /* 64 bit extended size */ + a.size = avio_rb64(pb) - 8; + total_size += 8; + } } av_dlog(c->fc, "type: %08x '%.4s' parent:'%.4s' sz: %"PRId64" %"PRId64" %"PRId64"\n", a.type, (char*)&a.type, (char*)&atom.type, a.size, total_size, atom.size); - total_size += 8; - if (a.size == 1) { /* 64 bit extended size */ - a.size = avio_rb64(pb) - 8; - total_size += 8; - } if (a.size == 0) { - a.size = atom.size - total_size; - if (a.size <= 8) - break; + a.size = atom.size - total_size + 8; } a.size -= 8; if (a.size < 0) @@ -2691,46 +3060,81 @@ static int mov_read_default(MOVContext *c, AVIOContext *pb, MOVAtom atom) static int mov_probe(AVProbeData *p) { - unsigned int offset; + int64_t offset; uint32_t tag; int score = 0; + int moov_offset = -1; /* check file header */ offset = 0; for (;;) { /* ignore invalid offset */ if ((offset + 8) > (unsigned int)p->buf_size) - return score; + break; tag = AV_RL32(p->buf + offset + 4); switch(tag) { /* check for obvious tags */ - case MKTAG('j','P',' ',' '): /* jpeg 2000 signature */ case MKTAG('m','o','o','v'): + moov_offset = offset + 4; + case MKTAG('j','P',' ',' '): /* jpeg 2000 signature */ case MKTAG('m','d','a','t'): case MKTAG('p','n','o','t'): /* detect movs with preview pics like ew.mov and april.mov */ case MKTAG('u','d','t','a'): /* Packet Video PVAuthor adds this and a lot of more junk */ case MKTAG('f','t','y','p'): - return AVPROBE_SCORE_MAX; + if (AV_RB32(p->buf+offset) < 8 && + (AV_RB32(p->buf+offset) != 1 || + offset + 12 > (unsigned int)p->buf_size || + AV_RB64(p->buf+offset + 8) == 0)) { + score = FFMAX(score, AVPROBE_SCORE_EXTENSION); + } else { + score = AVPROBE_SCORE_MAX; + } + offset = FFMAX(4, AV_RB32(p->buf+offset)) + offset; + break; /* those are more common words, so rate then a bit less */ case MKTAG('e','d','i','w'): /* xdcam files have reverted first tags */ case MKTAG('w','i','d','e'): case MKTAG('f','r','e','e'): case MKTAG('j','u','n','k'): case MKTAG('p','i','c','t'): - return AVPROBE_SCORE_MAX - 5; + score = FFMAX(score, AVPROBE_SCORE_MAX - 5); + offset = FFMAX(4, AV_RB32(p->buf+offset)) + offset; + break; case MKTAG(0x82,0x82,0x7f,0x7d): case MKTAG('s','k','i','p'): case MKTAG('u','u','i','d'): case MKTAG('p','r','f','l'): - offset = AV_RB32(p->buf+offset) + offset; /* if we only find those cause probedata is too small at least rate them */ - score = AVPROBE_SCORE_EXTENSION; + score = FFMAX(score, AVPROBE_SCORE_EXTENSION); + offset = FFMAX(4, AV_RB32(p->buf+offset)) + offset; break; default: - /* unrecognized tag */ - return score; + offset = FFMAX(4, AV_RB32(p->buf+offset)) + offset; } } + if(score > AVPROBE_SCORE_MAX - 50 && moov_offset != -1) { + /* moov atom in the header - we should make sure that this is not a + * MOV-packed MPEG-PS */ + offset = moov_offset; + + while(offset < (p->buf_size - 16)){ /* Sufficient space */ + /* We found an actual hdlr atom */ + if(AV_RL32(p->buf + offset ) == MKTAG('h','d','l','r') && + AV_RL32(p->buf + offset + 8) == MKTAG('m','h','l','r') && + AV_RL32(p->buf + offset + 12) == MKTAG('M','P','E','G')){ + av_log(NULL, AV_LOG_WARNING, "Found media data tag MPEG indicating this is a MOV-packed MPEG-PS.\n"); + /* We found a media handler reference atom describing an + * MPEG-PS-in-MOV, return a + * low score to force expanding the probe window until + * mpegps_probe finds what it needs */ + return 5; + }else + /* Keep looking */ + offset+=2; + } + } + + return score; } // must be done after parsing all trak because there's no order requirement @@ -2792,7 +3196,7 @@ static void mov_read_chapters(AVFormatContext *s) if (len == 1 || len == 2) title[len] = 0; else - avio_get_str(sc->pb, len - 2, title + 2, title_len - 2); + avio_get_str(sc->pb, INT_MAX, title + 2, len - 1); } } @@ -2803,6 +3207,49 @@ finish: avio_seek(sc->pb, cur_pos, SEEK_SET); } +static int parse_timecode_in_framenum_format(AVFormatContext *s, AVStream *st, + uint32_t value, int flags) +{ + AVTimecode tc; + char buf[AV_TIMECODE_STR_SIZE]; + AVRational rate = {st->codec->time_base.den, + st->codec->time_base.num}; + int ret = av_timecode_init(&tc, rate, flags, 0, s); + if (ret < 0) + return ret; + av_dict_set(&st->metadata, "timecode", + av_timecode_make_string(&tc, buf, value), 0); + return 0; +} + +static int mov_read_timecode_track(AVFormatContext *s, AVStream *st) +{ + MOVStreamContext *sc = st->priv_data; + int flags = 0; + int64_t cur_pos = avio_tell(sc->pb); + uint32_t value; + + if (!st->nb_index_entries) + return -1; + + avio_seek(sc->pb, st->index_entries->pos, SEEK_SET); + value = avio_rb32(s->pb); + + if (sc->tmcd_flags & 0x0001) flags |= AV_TIMECODE_FLAG_DROPFRAME; + if (sc->tmcd_flags & 0x0002) flags |= AV_TIMECODE_FLAG_24HOURSMAX; + if (sc->tmcd_flags & 0x0004) flags |= AV_TIMECODE_FLAG_ALLOWNEGATIVE; + + /* Assume Counter flag is set to 1 in tmcd track (even though it is likely + * not the case) and thus assume "frame number format" instead of QT one. + * No sample with tmcd track can be found with a QT timecode at the moment, + * despite what the tmcd track "suggests" (Counter flag set to 0 means QT + * format). */ + parse_timecode_in_framenum_format(s, st, value, flags); + + avio_seek(sc->pb, cur_pos, SEEK_SET); + return 0; +} + static int mov_read_close(AVFormatContext *s) { MOVContext *mov = s->priv_data; @@ -2818,8 +3265,15 @@ static int mov_read_close(AVFormatContext *s) av_freep(&sc->drefs[j].dir); } av_freep(&sc->drefs); - if (sc->pb && sc->pb != s->pb) + if (!sc->pb_is_copied) avio_close(sc->pb); + sc->pb = NULL; + av_freep(&sc->chunk_offsets); + av_freep(&sc->keyframes); + av_freep(&sc->sample_sizes); + av_freep(&sc->stps_data); + av_freep(&sc->stsc_data); + av_freep(&sc->stts_data); } if (mov->dv_demux) { @@ -2832,15 +3286,50 @@ static int mov_read_close(AVFormatContext *s) } av_freep(&mov->trex_data); + av_freep(&mov->bitrates); return 0; } +static int tmcd_is_referenced(AVFormatContext *s, int tmcd_id) +{ + int i; + + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + MOVStreamContext *sc = st->priv_data; + + if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO && + sc->timecode_track == tmcd_id) + return 1; + } + return 0; +} + +/* look for a tmcd track not referenced by any video track, and export it globally */ +static void export_orphan_timecode(AVFormatContext *s) +{ + int i; + + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + + if (st->codec->codec_tag == MKTAG('t','m','c','d') && + !tmcd_is_referenced(s, i + 1)) { + AVDictionaryEntry *tcr = av_dict_get(st->metadata, "timecode", NULL, 0); + if (tcr) { + av_dict_set(&s->metadata, "timecode", tcr->value, 0); + break; + } + } + } +} + static int mov_read_header(AVFormatContext *s) { MOVContext *mov = s->priv_data; AVIOContext *pb = s->pb; - int err; + int i, j, err; MOVAtom atom = { AV_RL32("root") }; mov->fc = s; @@ -2863,11 +3352,45 @@ static int mov_read_header(AVFormatContext *s) } av_dlog(mov->fc, "on_parse_exit_offset=%"PRId64"\n", avio_tell(pb)); - if (pb->seekable && mov->chapter_track > 0) - mov_read_chapters(s); + if (pb->seekable) { + if (mov->chapter_track > 0) + mov_read_chapters(s); + for (i = 0; i < s->nb_streams; i++) + if (s->streams[i]->codec->codec_tag == AV_RL32("tmcd")) + mov_read_timecode_track(s, s->streams[i]); + } + + /* copy timecode metadata from tmcd tracks to the related video streams */ + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + MOVStreamContext *sc = st->priv_data; + if (sc->timecode_track > 0) { + AVDictionaryEntry *tcr; + int tmcd_st_id = -1; + + for (j = 0; j < s->nb_streams; j++) + if (s->streams[j]->id == sc->timecode_track) + tmcd_st_id = j; + + if (tmcd_st_id < 0 || tmcd_st_id == i) + continue; + tcr = av_dict_get(s->streams[tmcd_st_id]->metadata, "timecode", NULL, 0); + if (tcr) + av_dict_set(&st->metadata, "timecode", tcr->value, 0); + } + } + export_orphan_timecode(s); + + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + MOVStreamContext *sc = st->priv_data; + fix_timescale(mov, sc); + if(st->codec->codec_type == AVMEDIA_TYPE_AUDIO && st->codec->codec_id == AV_CODEC_ID_AAC) { + st->skip_samples = sc->start_pad; + } + } if (mov->trex_data) { - int i; for (i = 0; i < s->nb_streams; i++) { AVStream *st = s->streams[i]; MOVStreamContext *sc = st->priv_data; @@ -2876,6 +3399,12 @@ static int mov_read_header(AVFormatContext *s) } } + for (i = 0; i < mov->bitrates_count && i < s->nb_streams; i++) { + if (mov->bitrates[i]) { + s->streams[i]->codec->bit_rate = mov->bitrates[i]; + } + } + return 0; } @@ -2912,6 +3441,7 @@ static int mov_read_packet(AVFormatContext *s, AVPacket *pkt) AVIndexEntry *sample; AVStream *st = NULL; int ret; + mov->fc = s; retry: sample = mov_find_next_sample(s, &st); if (!sample) { @@ -2921,7 +3451,7 @@ static int mov_read_packet(AVFormatContext *s, AVPacket *pkt) avio_seek(s->pb, mov->next_root_atom, SEEK_SET); mov->next_root_atom = 0; if (mov_read_default(mov, s->pb, (MOVAtom){ AV_RL32("root"), INT64_MAX }) < 0 || - s->pb->eof_reached) + url_feof(s->pb)) return AVERROR_EOF; av_dlog(s, "read fragments, offset 0x%"PRIx64"\n", avio_tell(s->pb)); goto retry; @@ -2930,6 +3460,11 @@ static int mov_read_packet(AVFormatContext *s, AVPacket *pkt) /* must be done just before reading, to avoid infinite loop on sample */ sc->current_sample++; + if (mov->next_root_atom) { + sample->pos = FFMIN(sample->pos, mov->next_root_atom); + sample->size = FFMIN(sample->size, (mov->next_root_atom - sample->pos)); + } + if (st->discard != AVDISCARD_ALL) { if (avio_seek(sc->pb, sample->pos, SEEK_SET) != sample->pos) { av_log(mov->fc, AV_LOG_ERROR, "stream %d, offset 0x%"PRIx64": partial file\n", @@ -2952,7 +3487,7 @@ static int mov_read_packet(AVFormatContext *s, AVPacket *pkt) } #if CONFIG_DV_DEMUXER if (mov->dv_demux && sc->dv_audio_container) { - avpriv_dv_produce_packet(mov->dv_demux, pkt, pkt->data, pkt->size); + avpriv_dv_produce_packet(mov->dv_demux, pkt, pkt->data, pkt->size, pkt->pos); av_free(pkt->data); pkt->size = 0; ret = avpriv_dv_get_packet(mov->dv_demux, pkt); @@ -3029,8 +3564,6 @@ static int mov_read_seek(AVFormatContext *s, int stream_index, int64_t sample_ti if (stream_index >= s->nb_streams) return AVERROR_INVALIDDATA; - if (sample_time < 0) - sample_time = 0; st = s->streams[stream_index]; sample = mov_seek_stream(s, st, sample_time, flags); @@ -3041,7 +3574,10 @@ static int mov_read_seek(AVFormatContext *s, int stream_index, int64_t sample_ti seek_timestamp = st->index_entries[sample].timestamp; for (i = 0; i < s->nb_streams; i++) { + MOVStreamContext *sc = s->streams[i]->priv_data; st = s->streams[i]; + st->skip_samples = (sample_time <= 0) ? sc->start_pad : 0; + if (stream_index == i) continue; @@ -3051,6 +3587,23 @@ static int mov_read_seek(AVFormatContext *s, int stream_index, int64_t sample_ti return 0; } +static const AVOption options[] = { + {"use_absolute_path", + "allow using absolute path when opening alias, this is a possible security issue", + offsetof(MOVContext, use_absolute_path), FF_OPT_TYPE_INT, {.i64 = 0}, + 0, 1, AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_DECODING_PARAM}, + {"ignore_editlist", "", offsetof(MOVContext, ignore_editlist), FF_OPT_TYPE_INT, {.i64 = 0}, + 0, 1, AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_DECODING_PARAM}, + {NULL} +}; + +static const AVClass mov_class = { + .class_name = "mov,mp4,m4a,3gp,3g2,mj2", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + AVInputFormat ff_mov_demuxer = { .name = "mov,mp4,m4a,3gp,3g2,mj2", .long_name = NULL_IF_CONFIG_SMALL("QuickTime / MOV"), @@ -3060,4 +3613,6 @@ AVInputFormat ff_mov_demuxer = { .read_packet = mov_read_packet, .read_close = mov_read_close, .read_seek = mov_read_seek, + .priv_class = &mov_class, + .flags = AVFMT_NO_BYTE_SEEK, }; diff --git a/libavformat/mov_chan.c b/libavformat/mov_chan.c index aa7ba1079f..e7b5221f08 100644 --- a/libavformat/mov_chan.c +++ b/libavformat/mov_chan.c @@ -1,20 +1,20 @@ /* * Copyright (c) 2011 Justin Ruggles * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -426,6 +426,7 @@ static const enum MovChannelLayoutTag mov_ch_layouts_wav[] = { MOV_CH_LAYOUT_MPEG_7_1_A, MOV_CH_LAYOUT_MPEG_7_1_C, MOV_CH_LAYOUT_SMPTE_DTV, + 0, }; static const struct { @@ -570,6 +571,7 @@ int ff_mov_read_chan(AVFormatContext *s, AVIOContext *pb, AVStream *st, avio_rl32(pb); // mCoordinates[0] avio_rl32(pb); // mCoordinates[1] avio_rl32(pb); // mCoordinates[2] + size -= 20; if (layout_tag == 0) { uint32_t mask_incr = mov_get_channel_label(label); if (mask_incr == 0) { @@ -584,6 +586,7 @@ int ff_mov_read_chan(AVFormatContext *s, AVIOContext *pb, AVStream *st, st->codec->channel_layout = label_mask; } else st->codec->channel_layout = ff_mov_get_channel_layout(layout_tag, bitmap); + avio_skip(pb, size - 12); return 0; } diff --git a/libavformat/mov_chan.h b/libavformat/mov_chan.h index 3fae93961e..ca28345aef 100644 --- a/libavformat/mov_chan.h +++ b/libavformat/mov_chan.h @@ -1,20 +1,20 @@ /* * Copyright (c) 2011 Justin Ruggles * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/movenc.c b/libavformat/movenc.c index 8f7153ace7..cfb73d7b39 100644 --- a/libavformat/movenc.c +++ b/libavformat/movenc.c @@ -4,20 +4,20 @@ * Copyright (c) 2004 Gildas Bazin <gbazin at videolan dot org> * Copyright (c) 2009 Baptiste Coudurier <baptiste dot coudurier at gmail dot com> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -46,6 +46,7 @@ static const AVOption options[] = { { "movflags", "MOV muxer flags", offsetof(MOVMuxContext, flags), AV_OPT_TYPE_FLAGS, {.i64 = 0}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, { "rtphint", "Add RTP hint tracks", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_RTP_HINT}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, + { "moov_size", "maximum moov size so it can be placed at the begin", offsetof(MOVMuxContext, reserved_moov_size), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, 0 }, { "empty_moov", "Make the initial moov atom empty (not supported by QuickTime)", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_EMPTY_MOOV}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, { "frag_keyframe", "Fragment at video keyframes", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_FRAG_KEYFRAME}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, { "separate_moof", "Write separate moof/mdat atoms for each track", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_SEPARATE_MOOF}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, @@ -54,13 +55,15 @@ static const AVOption options[] = { { "faststart", "Run a second pass to put the index (moov atom) at the beginning of the file", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_FASTSTART}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, { "omit_tfhd_offset", "Omit the base data offset in tfhd atoms", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_OMIT_TFHD_OFFSET}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, FF_RTP_FLAG_OPTS(MOVMuxContext, rtp_flags), - { "skip_iods", "Skip writing iods atom.", offsetof(MOVMuxContext, iods_skip), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, AV_OPT_FLAG_ENCODING_PARAM}, + { "skip_iods", "Skip writing iods atom.", offsetof(MOVMuxContext, iods_skip), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1, AV_OPT_FLAG_ENCODING_PARAM}, { "iods_audio_profile", "iods audio profile atom.", offsetof(MOVMuxContext, iods_audio_profile), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 255, AV_OPT_FLAG_ENCODING_PARAM}, { "iods_video_profile", "iods video profile atom.", offsetof(MOVMuxContext, iods_video_profile), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 255, AV_OPT_FLAG_ENCODING_PARAM}, { "frag_duration", "Maximum fragment duration", offsetof(MOVMuxContext, max_fragment_duration), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM}, { "min_frag_duration", "Minimum fragment duration", offsetof(MOVMuxContext, min_fragment_duration), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM}, { "frag_size", "Maximum fragment size", offsetof(MOVMuxContext, max_fragment_size), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM}, { "ism_lookahead", "Number of lookahead entries for ISM files", offsetof(MOVMuxContext, ism_lookahead), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM}, + { "use_editlist", "use edit list", offsetof(MOVMuxContext, use_editlist), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 1, AV_OPT_FLAG_ENCODING_PARAM}, + { "video_track_timescale", "set timescale of all video tracks", offsetof(MOVMuxContext, video_track_timescale), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM}, { NULL }, }; @@ -72,6 +75,8 @@ static const AVClass flavor ## _muxer_class = {\ .version = LIBAVUTIL_VERSION_INT,\ }; +static int get_moov_size(AVFormatContext *s); + //FIXME support 64 bit variant with wide placeholders static int64_t update_size(AVIOContext *pb, int64_t pos) { @@ -83,6 +88,12 @@ static int64_t update_size(AVIOContext *pb, int64_t pos) return curpos - pos; } +static int supports_edts(MOVMuxContext *mov) +{ + // EDTS with fragments is tricky as we don't know the duration when its written + return (mov->use_editlist<0 && !(mov->flags & FF_MOV_FLAG_FRAGMENT)) || mov->use_editlist>0; +} + static int co64_required(const MOVTrack *track) { if (track->entry > 0 && track->cluster[track->entry - 1].pos + track->data_offset > UINT32_MAX) @@ -97,13 +108,15 @@ static int mov_write_stco_tag(AVIOContext *pb, MOVTrack *track) int mode64 = co64_required(track); // use 32 bit size variant if possible int64_t pos = avio_tell(pb); avio_wb32(pb, 0); /* size */ - if (mode64) { + if (mode64) ffio_wfourcc(pb, "co64"); - } else + else ffio_wfourcc(pb, "stco"); avio_wb32(pb, 0); /* version & flags */ - avio_wb32(pb, track->entry); /* entry count */ + avio_wb32(pb, track->chunkCount); /* entry count */ for (i = 0; i < track->entry; i++) { + if (!track->cluster[i].chunkNum) + continue; if (mode64 == 1) avio_wb64(pb, track->cluster[i].pos + track->data_offset); else @@ -159,10 +172,10 @@ static int mov_write_stsc_tag(AVIOContext *pb, MOVTrack *track) ffio_wfourcc(pb, "stsc"); avio_wb32(pb, 0); // version & flags entryPos = avio_tell(pb); - avio_wb32(pb, track->entry); // entry count + avio_wb32(pb, track->chunkCount); // entry count for (i = 0; i < track->entry; i++) { - if (oldval != track->cluster[i].samples_in_chunk) { - avio_wb32(pb, i + 1); // first chunk + if (oldval != track->cluster[i].samples_in_chunk && track->cluster[i].chunkNum) { + avio_wb32(pb, track->cluster[i].chunkNum); // first chunk avio_wb32(pb, track->cluster[i].samples_in_chunk); // samples per chunk avio_wb32(pb, 0x1); // sample description index oldval = track->cluster[i].samples_in_chunk; @@ -269,6 +282,22 @@ static int mov_write_extradata_tag(AVIOContext *pb, MOVTrack *track) return track->enc->extradata_size; } +static int mov_write_enda_tag(AVIOContext *pb) +{ + avio_wb32(pb, 10); + ffio_wfourcc(pb, "enda"); + avio_wb16(pb, 1); /* little endian */ + return 10; +} + +static int mov_write_enda_tag_be(AVIOContext *pb) +{ + avio_wb32(pb, 10); + ffio_wfourcc(pb, "enda"); + avio_wb16(pb, 0); /* big endian */ + return 10; +} + static void put_descr(AVIOContext *pb, int tag, unsigned int size) { int i = 3; @@ -278,10 +307,22 @@ static void put_descr(AVIOContext *pb, int tag, unsigned int size) avio_w8(pb, size & 0x7F); } +static unsigned compute_avg_bitrate(MOVTrack *track) +{ + uint64_t size = 0; + int i; + if (!track->track_duration) + return 0; + for (i = 0; i < track->entry; i++) + size += track->cluster[i].size; + return size * 8 * track->timescale / track->track_duration; +} + static int mov_write_esds_tag(AVIOContext *pb, MOVTrack *track) // Basic { int64_t pos = avio_tell(pb); int decoder_specific_info_len = track->vos_len ? 5 + track->vos_len : 0; + unsigned avg_bitrate; avio_wb32(pb, 0); // size ffio_wfourcc(pb, "esds"); @@ -312,12 +353,10 @@ static int mov_write_esds_tag(AVIOContext *pb, MOVTrack *track) // Basic avio_wb24(pb, track->enc->rc_buffer_size >> 3); // Buffersize DB - avio_wb32(pb, FFMAX(track->enc->bit_rate, track->enc->rc_max_rate)); // maxbitrate (FIXME should be max rate in any 1 sec window) - if (track->enc->rc_max_rate != track->enc->rc_min_rate || - track->enc->rc_min_rate == 0) - avio_wb32(pb, 0); // vbr - else - avio_wb32(pb, track->enc->rc_max_rate); // avg bitrate + avg_bitrate = compute_avg_bitrate(track); + // maxbitrate (FIXME should be max rate in any 1 sec window) + avio_wb32(pb, FFMAX3(track->enc->bit_rate, track->enc->rc_max_rate, avg_bitrate)); + avio_wb32(pb, avg_bitrate); if (track->vos_len) { // DecoderSpecific info descriptor @@ -331,6 +370,22 @@ static int mov_write_esds_tag(AVIOContext *pb, MOVTrack *track) // Basic return update_size(pb, pos); } +static int mov_pcm_le_gt16(enum AVCodecID codec_id) +{ + return codec_id == AV_CODEC_ID_PCM_S24LE || + codec_id == AV_CODEC_ID_PCM_S32LE || + codec_id == AV_CODEC_ID_PCM_F32LE || + codec_id == AV_CODEC_ID_PCM_F64LE; +} + +static int mov_pcm_be_gt16(enum AVCodecID codec_id) +{ + return codec_id == AV_CODEC_ID_PCM_S24BE || + codec_id == AV_CODEC_ID_PCM_S32BE || + codec_id == AV_CODEC_ID_PCM_F32BE || + codec_id == AV_CODEC_ID_PCM_F64BE; +} + static int mov_write_ms_tag(AVIOContext *pb, MOVTrack *track) { int64_t pos = avio_tell(pb); @@ -382,9 +437,11 @@ static int mov_write_wave_tag(AVIOContext *pb, MOVTrack *track) avio_wb32(pb, 0); /* size */ ffio_wfourcc(pb, "wave"); + if (track->enc->codec_id != AV_CODEC_ID_QDM2) { avio_wb32(pb, 12); /* size */ ffio_wfourcc(pb, "frma"); avio_wl32(pb, track->tag); + } if (track->enc->codec_id == AV_CODEC_ID_AAC) { /* useless atom needed by mplayer, ipod, not needed by quicktime */ @@ -392,11 +449,16 @@ static int mov_write_wave_tag(AVIOContext *pb, MOVTrack *track) ffio_wfourcc(pb, "mp4a"); avio_wb32(pb, 0); mov_write_esds_tag(pb, track); + } else if (mov_pcm_le_gt16(track->enc->codec_id)) { + mov_write_enda_tag(pb); + } else if (mov_pcm_be_gt16(track->enc->codec_id)) { + mov_write_enda_tag_be(pb); } else if (track->enc->codec_id == AV_CODEC_ID_AMR_NB) { mov_write_amr_tag(pb, track); } else if (track->enc->codec_id == AV_CODEC_ID_AC3) { mov_write_ac3_tag(pb, track); - } else if (track->enc->codec_id == AV_CODEC_ID_ALAC) { + } else if (track->enc->codec_id == AV_CODEC_ID_ALAC || + track->enc->codec_id == AV_CODEC_ID_QDM2) { mov_write_extradata_tag(pb, track); } else if (track->enc->codec_id == AV_CODEC_ID_ADPCM_MS || track->enc->codec_id == AV_CODEC_ID_ADPCM_IMA_WAV) { @@ -557,6 +619,8 @@ static int get_samples_per_packet(MOVTrack *track) { int i, first_duration; +// return track->enc->frame_size; + /* use 1 for raw PCM */ if (!track->audio_vbr) return 1; @@ -579,9 +643,17 @@ static int mov_write_audio_tag(AVIOContext *pb, MOVTrack *track) uint32_t tag = track->tag; if (track->mode == MODE_MOV) { - if (mov_get_lpcm_flags(track->enc->codec_id)) - tag = AV_RL32("lpcm"); - version = 2; + if (track->timescale > UINT16_MAX) { + if (mov_get_lpcm_flags(track->enc->codec_id)) + tag = AV_RL32("lpcm"); + version = 2; + } else if (track->audio_vbr || mov_pcm_le_gt16(track->enc->codec_id) || + mov_pcm_be_gt16(track->enc->codec_id) || + track->enc->codec_id == AV_CODEC_ID_ADPCM_MS || + track->enc->codec_id == AV_CODEC_ID_ADPCM_IMA_WAV || + track->enc->codec_id == AV_CODEC_ID_QDM2) { + version = 1; + } } avio_wb32(pb, 0); /* size */ @@ -610,10 +682,19 @@ static int mov_write_audio_tag(AVIOContext *pb, MOVTrack *track) avio_wb32(pb, track->sample_size); avio_wb32(pb, get_samples_per_packet(track)); } else { - /* reserved for mp4/3gp */ - avio_wb16(pb, 2); - avio_wb16(pb, 16); - avio_wb16(pb, 0); + if (track->mode == MODE_MOV) { + avio_wb16(pb, track->enc->channels); + if (track->enc->codec_id == AV_CODEC_ID_PCM_U8 || + track->enc->codec_id == AV_CODEC_ID_PCM_S8) + avio_wb16(pb, 8); /* bits per sample */ + else + avio_wb16(pb, 16); + avio_wb16(pb, track->audio_vbr ? -2 : 0); /* compression ID */ + } else { /* reserved for mp4/3gp */ + avio_wb16(pb, 2); + avio_wb16(pb, 16); + avio_wb16(pb, 0); + } avio_wb16(pb, 0); /* packet size (= 0) */ avio_wb16(pb, track->enc->sample_rate <= UINT16_MAX ? @@ -621,13 +702,27 @@ static int mov_write_audio_tag(AVIOContext *pb, MOVTrack *track) avio_wb16(pb, 0); /* Reserved */ } + if (version == 1) { /* SoundDescription V1 extended info */ + if (mov_pcm_le_gt16(track->enc->codec_id) || + mov_pcm_be_gt16(track->enc->codec_id)) + avio_wb32(pb, 1); /* must be 1 for uncompressed formats */ + else + avio_wb32(pb, track->enc->frame_size); /* Samples per packet */ + avio_wb32(pb, track->sample_size / track->enc->channels); /* Bytes per packet */ + avio_wb32(pb, track->sample_size); /* Bytes per frame */ + avio_wb32(pb, 2); /* Bytes per sample */ + } + if (track->mode == MODE_MOV && (track->enc->codec_id == AV_CODEC_ID_AAC || track->enc->codec_id == AV_CODEC_ID_AC3 || track->enc->codec_id == AV_CODEC_ID_AMR_NB || track->enc->codec_id == AV_CODEC_ID_ALAC || track->enc->codec_id == AV_CODEC_ID_ADPCM_MS || - track->enc->codec_id == AV_CODEC_ID_ADPCM_IMA_WAV)) + track->enc->codec_id == AV_CODEC_ID_ADPCM_IMA_WAV || + track->enc->codec_id == AV_CODEC_ID_QDM2 || + (mov_pcm_le_gt16(track->enc->codec_id) && version==1) || + (mov_pcm_be_gt16(track->enc->codec_id) && version==1))) mov_write_wave_tag(pb, track); else if (track->tag == MKTAG('m','p','4','a')) mov_write_esds_tag(pb, track); @@ -660,19 +755,6 @@ static int mov_write_d263_tag(AVIOContext *pb) return 0xf; } -/* TODO: No idea about these values */ -static int mov_write_svq3_tag(AVIOContext *pb) -{ - avio_wb32(pb, 0x15); - ffio_wfourcc(pb, "SMI "); - ffio_wfourcc(pb, "SEQH"); - avio_wb32(pb, 0x5); - avio_wb32(pb, 0xe2c0211d); - avio_wb32(pb, 0xc0000000); - avio_w8(pb, 0); - return 0x15; -} - static int mov_write_avcc_tag(AVIOContext *pb, MOVTrack *track) { int64_t pos = avio_tell(pb); @@ -781,20 +863,20 @@ static int mov_get_dv_codec_tag(AVFormatContext *s, MOVTrack *track) { int tag; - if (track->enc->width == 720) /* SD */ - if (track->enc->height == 480) /* NTSC */ + if (track->enc->width == 720) { /* SD */ + if (track->enc->height == 480) { /* NTSC */ if (track->enc->pix_fmt == AV_PIX_FMT_YUV422P) tag = MKTAG('d','v','5','n'); else tag = MKTAG('d','v','c',' '); - else if (track->enc->pix_fmt == AV_PIX_FMT_YUV422P) tag = MKTAG('d','v','5','p'); + }else if (track->enc->pix_fmt == AV_PIX_FMT_YUV422P) tag = MKTAG('d','v','5','p'); else if (track->enc->pix_fmt == AV_PIX_FMT_YUV420P) tag = MKTAG('d','v','c','p'); else tag = MKTAG('d','v','p','p'); - else if (track->enc->height == 720) /* HD 720 line */ + } else if (track->enc->height == 720) { /* HD 720 line */ if (track->enc->time_base.den == 50) tag = MKTAG('d','v','h','q'); else tag = MKTAG('d','v','h','p'); - else if (track->enc->height == 1080) /* HD 1080 line */ + } else if (track->enc->height == 1080) { /* HD 1080 line */ if (track->enc->time_base.den == 25) tag = MKTAG('d','v','h','5'); else tag = MKTAG('d','v','h','6'); - else { + } else { av_log(s, AV_LOG_ERROR, "unsupported height for dv codec\n"); return 0; } @@ -802,11 +884,84 @@ static int mov_get_dv_codec_tag(AVFormatContext *s, MOVTrack *track) return tag; } +static AVRational find_fps(AVFormatContext *s, AVStream *st) +{ + AVRational rate = {st->codec->time_base.den, st->codec->time_base.num}; + /* if the codec time base makes no sense, try to fallback on stream frame rate */ + if (av_timecode_check_frame_rate(rate) < 0) { + av_log(s, AV_LOG_DEBUG, "timecode: tbc=%d/%d invalid, fallback on %d/%d\n", + rate.num, rate.den, st->avg_frame_rate.num, st->avg_frame_rate.den); + rate = st->avg_frame_rate; + } + + return rate; +} + +static int mov_get_mpeg2_xdcam_codec_tag(AVFormatContext *s, MOVTrack *track) +{ + int tag = MKTAG('m', '2', 'v', '1'); //fallback tag + int interlaced = track->enc->field_order > AV_FIELD_PROGRESSIVE; + AVStream *st = track->st; + int rate = av_q2d(find_fps(s, st)); + + if (track->enc->pix_fmt == AV_PIX_FMT_YUV420P) { + if (track->enc->width == 1280 && track->enc->height == 720) { + if (!interlaced) { + if (rate == 24) tag = MKTAG('x','d','v','4'); + else if (rate == 25) tag = MKTAG('x','d','v','5'); + else if (rate == 30) tag = MKTAG('x','d','v','1'); + else if (rate == 50) tag = MKTAG('x','d','v','a'); + else if (rate == 60) tag = MKTAG('x','d','v','9'); + } + } else if (track->enc->width == 1440 && track->enc->height == 1080) { + if (!interlaced) { + if (rate == 24) tag = MKTAG('x','d','v','6'); + else if (rate == 25) tag = MKTAG('x','d','v','7'); + else if (rate == 30) tag = MKTAG('x','d','v','8'); + } else { + if (rate == 25) tag = MKTAG('x','d','v','3'); + else if (rate == 30) tag = MKTAG('x','d','v','2'); + } + } else if (track->enc->width == 1920 && track->enc->height == 1080) { + if (!interlaced) { + if (rate == 24) tag = MKTAG('x','d','v','d'); + else if (rate == 25) tag = MKTAG('x','d','v','e'); + else if (rate == 30) tag = MKTAG('x','d','v','f'); + } else { + if (rate == 25) tag = MKTAG('x','d','v','c'); + else if (rate == 30) tag = MKTAG('x','d','v','b'); + } + } + } else if (track->enc->pix_fmt == AV_PIX_FMT_YUV422P) { + if (track->enc->width == 1280 && track->enc->height == 720) { + if (!interlaced) { + if (rate == 24) tag = MKTAG('x','d','5','4'); + else if (rate == 25) tag = MKTAG('x','d','5','5'); + else if (rate == 30) tag = MKTAG('x','d','5','1'); + else if (rate == 50) tag = MKTAG('x','d','5','a'); + else if (rate == 60) tag = MKTAG('x','d','5','9'); + } + } else if (track->enc->width == 1920 && track->enc->height == 1080) { + if (!interlaced) { + if (rate == 24) tag = MKTAG('x','d','5','d'); + else if (rate == 25) tag = MKTAG('x','d','5','e'); + else if (rate == 30) tag = MKTAG('x','d','5','f'); + } else { + if (rate == 25) tag = MKTAG('x','d','5','c'); + else if (rate == 30) tag = MKTAG('x','d','5','b'); + } + } + } + + return tag; +} + static const struct { enum AVPixelFormat pix_fmt; uint32_t tag; unsigned bps; } mov_pix_fmt_tags[] = { + { AV_PIX_FMT_YUYV422, MKTAG('y','u','v','2'), 0 }, { AV_PIX_FMT_YUYV422, MKTAG('y','u','v','s'), 0 }, { AV_PIX_FMT_UYVY422, MKTAG('2','v','u','y'), 0 }, { AV_PIX_FMT_RGB555BE,MKTAG('r','a','w',' '), 16 }, @@ -832,7 +987,8 @@ static int mov_get_rawvideo_codec_tag(AVFormatContext *s, MOVTrack *track) if (track->enc->pix_fmt == mov_pix_fmt_tags[i].pix_fmt) { tag = mov_pix_fmt_tags[i].tag; track->enc->bits_per_coded_sample = mov_pix_fmt_tags[i].bps; - break; + if (track->enc->codec_tag == mov_pix_fmt_tags[i].tag) + break; } } @@ -847,11 +1003,14 @@ static int mov_get_codec_tag(AVFormatContext *s, MOVTrack *track) (track->enc->codec_id == AV_CODEC_ID_DVVIDEO || track->enc->codec_id == AV_CODEC_ID_RAWVIDEO || track->enc->codec_id == AV_CODEC_ID_H263 || + track->enc->codec_id == AV_CODEC_ID_MPEG2VIDEO || av_get_bits_per_sample(track->enc->codec_id)))) { // pcm audio if (track->enc->codec_id == AV_CODEC_ID_DVVIDEO) tag = mov_get_dv_codec_tag(s, track); else if (track->enc->codec_id == AV_CODEC_ID_RAWVIDEO) tag = mov_get_rawvideo_codec_tag(s, track); + else if (track->enc->codec_id == AV_CODEC_ID_MPEG2VIDEO) + tag = mov_get_mpeg2_xdcam_codec_tag(s, track); else if (track->enc->codec_type == AVMEDIA_TYPE_VIDEO) { tag = ff_codec_get_tag(ff_codec_movvideo_tags, track->enc->codec_id); if (!tag) { // if no mac fcc found, try with Microsoft tags @@ -888,7 +1047,7 @@ static const AVCodecTag codec_3gp_tags[] = { { AV_CODEC_ID_NONE, 0 }, }; -static const AVCodecTag codec_f4v_tags[] = { +static const AVCodecTag codec_f4v_tags[] = { // XXX: add GIF/PNG/JPEG? { AV_CODEC_ID_MP3, MKTAG('.','m','p','3') }, { AV_CODEC_ID_AAC, MKTAG('m','p','4','a') }, { AV_CODEC_ID_H264, MKTAG('a','v','c','1') }, @@ -980,6 +1139,32 @@ static int mov_write_pasp_tag(AVIOContext *pb, MOVTrack *track) return 16; } +static void find_compressor(char * compressor_name, int len, MOVTrack *track) +{ + int xdcam_res = (track->enc->width == 1280 && track->enc->height == 720) + || (track->enc->width == 1440 && track->enc->height == 1080) + || (track->enc->width == 1920 && track->enc->height == 1080); + + if (track->mode == MODE_MOV && track->enc->codec && track->enc->codec->name) { + av_strlcpy(compressor_name, track->enc->codec->name, 32); + } else if (track->enc->codec_id == AV_CODEC_ID_MPEG2VIDEO && xdcam_res) { + int interlaced = track->enc->field_order > AV_FIELD_PROGRESSIVE; + AVStream *st = track->st; + int rate = av_q2d(find_fps(NULL, st)); + av_strlcatf(compressor_name, len, "XDCAM"); + if (track->enc->pix_fmt == AV_PIX_FMT_YUV422P) { + av_strlcatf(compressor_name, len, " HD422"); + } else if(track->enc->width == 1440) { + av_strlcatf(compressor_name, len, " HD"); + } else + av_strlcatf(compressor_name, len, " EX"); + + av_strlcatf(compressor_name, len, " %d%c", track->enc->height, interlaced ? 'i' : 'p'); + + av_strlcatf(compressor_name, len, "%d", rate * (interlaced + 1)); + } +} + static int mov_write_video_tag(AVIOContext *pb, MOVTrack *track) { int64_t pos = avio_tell(pb); @@ -1015,8 +1200,7 @@ static int mov_write_video_tag(AVIOContext *pb, MOVTrack *track) avio_wb16(pb, 1); /* Frame count (= 1) */ /* FIXME not sure, ISO 14496-1 draft where it shall be set to 0 */ - if (track->mode == MODE_MOV && track->enc->codec && track->enc->codec->name) - av_strlcpy(compressor_name, track->enc->codec->name, 32); + find_compressor(compressor_name, 32, track); avio_w8(pb, strlen(compressor_name)); avio_write(pb, compressor_name, 31); @@ -1029,17 +1213,17 @@ static int mov_write_video_tag(AVIOContext *pb, MOVTrack *track) mov_write_esds_tag(pb, track); else if (track->enc->codec_id == AV_CODEC_ID_H263) mov_write_d263_tag(pb); - else if (track->enc->codec_id == AV_CODEC_ID_SVQ3) - mov_write_svq3_tag(pb); - else if (track->enc->codec_id == AV_CODEC_ID_DNXHD) + else if (track->enc->codec_id == AV_CODEC_ID_AVUI || + track->enc->codec_id == AV_CODEC_ID_SVQ3) { + mov_write_extradata_tag(pb, track); + avio_wb32(pb, 0); + } else if (track->enc->codec_id == AV_CODEC_ID_DNXHD) mov_write_avid_tag(pb, track); else if (track->enc->codec_id == AV_CODEC_ID_H264) { mov_write_avcc_tag(pb, track); if (track->mode == MODE_IPOD) mov_write_uuid_tag_ipod(pb); - } else if (track->enc->field_order != AV_FIELD_UNKNOWN) - mov_write_fiel_tag(pb, track); - else if (track->enc->codec_id == AV_CODEC_ID_VC1 && track->vos_len > 0) + } else if (track->enc->codec_id == AV_CODEC_ID_VC1 && track->vos_len > 0) mov_write_dvc1_tag(pb, track); else if (track->enc->codec_id == AV_CODEC_ID_VP6F || track->enc->codec_id == AV_CODEC_ID_VP6A) { @@ -1048,6 +1232,12 @@ static int mov_write_video_tag(AVIOContext *pb, MOVTrack *track) } else if (track->vos_len > 0) mov_write_glbl_tag(pb, track); + if (track->enc->codec_id != AV_CODEC_ID_H264 && + track->enc->codec_id != AV_CODEC_ID_MPEG4 && + track->enc->codec_id != AV_CODEC_ID_DNXHD) + if (track->enc->field_order != AV_FIELD_UNKNOWN) + mov_write_fiel_tag(pb, track); + if (track->enc->sample_aspect_ratio.den && track->enc->sample_aspect_ratio.num && track->enc->sample_aspect_ratio.den != track->enc->sample_aspect_ratio.num) { mov_write_pasp_tag(pb, track); @@ -1079,6 +1269,27 @@ static int mov_write_rtp_tag(AVIOContext *pb, MOVTrack *track) static int mov_write_tmcd_tag(AVIOContext *pb, MOVTrack *track) { int64_t pos = avio_tell(pb); +#if 1 + int frame_duration = av_rescale(track->timescale, track->enc->time_base.num, track->enc->time_base.den); + int nb_frames = 1.0/av_q2d(track->enc->time_base) + 0.5; + + if (nb_frames > 255) { + av_log(NULL, AV_LOG_ERROR, "fps %d is too large\n", nb_frames); + return AVERROR(EINVAL); + } + + avio_wb32(pb, 0); /* size */ + ffio_wfourcc(pb, "tmcd"); /* Data format */ + avio_wb32(pb, 0); /* Reserved */ + avio_wb32(pb, 1); /* Data reference index */ + avio_wb32(pb, 0); /* Flags */ + avio_wb32(pb, track->timecode_flags); /* Flags (timecode) */ + avio_wb32(pb, track->timescale); /* Timescale */ + avio_wb32(pb, frame_duration); /* Frame duration */ + avio_w8(pb, nb_frames); /* Number of frames */ + avio_wb24(pb, 0); /* Reserved */ + /* TODO: source reference string */ +#else avio_wb32(pb, 0); /* size */ ffio_wfourcc(pb, "tmcd"); /* Data format */ @@ -1086,6 +1297,7 @@ static int mov_write_tmcd_tag(AVIOContext *pb, MOVTrack *track) avio_wb32(pb, 1); /* Data reference index */ if (track->enc->extradata_size) avio_write(pb, track->enc->extradata, track->enc->extradata_size); +#endif return update_size(pb, pos); } @@ -1192,6 +1404,7 @@ static int mov_write_dref_tag(AVIOContext *pb) avio_wb32(pb, 1); /* entry count */ avio_wb32(pb, 0xc); /* size */ + //FIXME add the alis and rsrc atom ffio_wfourcc(pb, "url "); avio_wb32(pb, 1); /* version & flags */ @@ -1237,9 +1450,32 @@ static int mov_write_nmhd_tag(AVIOContext *pb) return 12; } -static int mov_write_gmhd_tag(AVIOContext *pb) +static int mov_write_tcmi_tag(AVIOContext *pb, MOVTrack *track) +{ + int64_t pos = avio_tell(pb); + const char *font = "Lucida Grande"; + avio_wb32(pb, 0); /* size */ + ffio_wfourcc(pb, "tcmi"); /* timecode media information atom */ + avio_wb32(pb, 0); /* version & flags */ + avio_wb16(pb, 0); /* text font */ + avio_wb16(pb, 0); /* text face */ + avio_wb16(pb, 12); /* text size */ + avio_wb16(pb, 0); /* (unknown, not in the QT specs...) */ + avio_wb16(pb, 0x0000); /* text color (red) */ + avio_wb16(pb, 0x0000); /* text color (green) */ + avio_wb16(pb, 0x0000); /* text color (blue) */ + avio_wb16(pb, 0xffff); /* background color (red) */ + avio_wb16(pb, 0xffff); /* background color (green) */ + avio_wb16(pb, 0xffff); /* background color (blue) */ + avio_w8(pb, strlen(font)); /* font len (part of the pascal string) */ + avio_write(pb, font, strlen(font)); /* font name */ + return update_size(pb, pos); +} + +static int mov_write_gmhd_tag(AVIOContext *pb, MOVTrack *track) { - avio_wb32(pb, 0x20); /* size */ + int64_t pos = avio_tell(pb); + avio_wb32(pb, 0); /* size */ ffio_wfourcc(pb, "gmhd"); avio_wb32(pb, 0x18); /* gmin size */ ffio_wfourcc(pb, "gmin");/* generic media info */ @@ -1250,7 +1486,36 @@ static int mov_write_gmhd_tag(AVIOContext *pb) avio_wb16(pb, 0x8000); /* opColor (b?) */ avio_wb16(pb, 0); /* balance */ avio_wb16(pb, 0); /* reserved */ - return 0x20; + + /* + * This special text atom is required for + * Apple Quicktime chapters. The contents + * don't appear to be documented, so the + * bytes are copied verbatim. + */ + if (track->tag != MKTAG('c','6','0','8')) { + avio_wb32(pb, 0x2C); /* size */ + ffio_wfourcc(pb, "text"); + avio_wb16(pb, 0x01); + avio_wb32(pb, 0x00); + avio_wb32(pb, 0x00); + avio_wb32(pb, 0x00); + avio_wb32(pb, 0x01); + avio_wb32(pb, 0x00); + avio_wb32(pb, 0x00); + avio_wb32(pb, 0x00); + avio_wb32(pb, 0x00004000); + avio_wb16(pb, 0x0000); + } + + if (track->enc->codec_tag == MKTAG('t','m','c','d')) { + int64_t tmcd_pos = avio_tell(pb); + avio_wb32(pb, 0); /* size */ + ffio_wfourcc(pb, "tmcd"); + mov_write_tcmi_tag(pb, track); + update_size(pb, tmcd_pos); + } + return update_size(pb, pos); } static int mov_write_smhd_tag(AVIOContext *pb) @@ -1290,9 +1555,14 @@ static int mov_write_hdlr_tag(AVIOContext *pb, MOVTrack *track) hdlr_type = "soun"; descr = "SoundHandler"; } else if (track->enc->codec_type == AVMEDIA_TYPE_SUBTITLE) { + if (track->tag == MKTAG('c','6','0','8')) { + hdlr_type = "clcp"; + descr = "ClosedCaptionHandler"; + } else { if (track->tag == MKTAG('t','x','3','g')) hdlr_type = "sbtl"; else hdlr_type = "text"; descr = "SubtitleHandler"; + } } else if (track->enc->codec_tag == MKTAG('r','t','p',' ')) { hdlr_type = "hint"; descr = "HintHandler"; @@ -1351,12 +1621,15 @@ static int mov_write_minf_tag(AVIOContext *pb, MOVTrack *track) else if (track->enc->codec_type == AVMEDIA_TYPE_AUDIO) mov_write_smhd_tag(pb); else if (track->enc->codec_type == AVMEDIA_TYPE_SUBTITLE) { - if (track->tag == MKTAG('t','e','x','t')) mov_write_gmhd_tag(pb); - else mov_write_nmhd_tag(pb); + if (track->tag == MKTAG('t','e','x','t') || track->tag == MKTAG('c','6','0','8')) { + mov_write_gmhd_tag(pb, track); + } else { + mov_write_nmhd_tag(pb); + } } else if (track->tag == MKTAG('r','t','p',' ')) { mov_write_hmhd_tag(pb); } else if (track->tag == MKTAG('t','m','c','d')) { - mov_write_gmhd_tag(pb); + mov_write_gmhd_tag(pb, track); } if (track->mode == MODE_MOV) /* FIXME: Why do it for MODE_MOV only ? */ mov_write_hdlr_tag(pb, NULL); @@ -1412,11 +1685,30 @@ static int mov_write_mdia_tag(AVIOContext *pb, MOVTrack *track) return update_size(pb, pos); } +/* transformation matrix + |a b u| + |c d v| + |tx ty w| */ +static void write_matrix(AVIOContext *pb, int16_t a, int16_t b, int16_t c, + int16_t d, int16_t tx, int16_t ty) +{ + avio_wb32(pb, a << 16); /* 16.16 format */ + avio_wb32(pb, b << 16); /* 16.16 format */ + avio_wb32(pb, 0); /* u in 2.30 format */ + avio_wb32(pb, c << 16); /* 16.16 format */ + avio_wb32(pb, d << 16); /* 16.16 format */ + avio_wb32(pb, 0); /* v in 2.30 format */ + avio_wb32(pb, tx << 16); /* 16.16 format */ + avio_wb32(pb, ty << 16); /* 16.16 format */ + avio_wb32(pb, 1 << 30); /* w in 2.30 format */ +} + static int mov_write_tkhd_tag(AVIOContext *pb, MOVTrack *track, AVStream *st) { int64_t duration = av_rescale_rnd(track->track_duration, MOV_TIMESCALE, track->timescale, AV_ROUND_UP); int version = duration < INT32_MAX ? 0 : 1; + int rotation = 0; if (track->mode == MODE_ISM) version = 1; @@ -1453,16 +1745,19 @@ static int mov_write_tkhd_tag(AVIOContext *pb, MOVTrack *track, AVStream *st) avio_wb16(pb, 0); /* reserved */ /* Matrix structure */ - avio_wb32(pb, 0x00010000); /* reserved */ - avio_wb32(pb, 0x0); /* reserved */ - avio_wb32(pb, 0x0); /* reserved */ - avio_wb32(pb, 0x0); /* reserved */ - avio_wb32(pb, 0x00010000); /* reserved */ - avio_wb32(pb, 0x0); /* reserved */ - avio_wb32(pb, 0x0); /* reserved */ - avio_wb32(pb, 0x0); /* reserved */ - avio_wb32(pb, 0x40000000); /* reserved */ - + if (st && st->metadata) { + AVDictionaryEntry *rot = av_dict_get(st->metadata, "rotate", NULL, 0); + rotation = (rot && rot->value) ? atoi(rot->value) : 0; + } + if (rotation == 90) { + write_matrix(pb, 0, 1, -1, 0, track->enc->height, 0); + } else if (rotation == 180) { + write_matrix(pb, -1, 0, 0, -1, track->enc->width, track->enc->height); + } else if (rotation == 270) { + write_matrix(pb, 0, -1, 1, 0, 0, track->enc->width); + } else { + write_matrix(pb, 1, 0, 0, 1, 0, 0); + } /* Track width and height, for visual only */ if (st && (track->enc->codec_type == AVMEDIA_TYPE_VIDEO || track->enc->codec_type == AVMEDIA_TYPE_SUBTITLE)) { @@ -1500,6 +1795,12 @@ static int mov_write_tapt_tag(AVIOContext *pb, MOVTrack *track) avio_wb32(pb, track->enc->height << 16); avio_wb32(pb, 20); + ffio_wfourcc(pb, "prof"); + avio_wb32(pb, 0); + avio_wb32(pb, width << 16); + avio_wb32(pb, track->enc->height << 16); + + avio_wb32(pb, 20); ffio_wfourcc(pb, "enof"); avio_wb32(pb, 0); avio_wb32(pb, track->enc->width << 16); @@ -1542,6 +1843,10 @@ static int mov_write_edts_tag(AVIOContext *pb, MOVTrack *track) avio_wb32(pb, -1); } avio_wb32(pb, 0x00010000); + } else { + av_assert0(av_rescale_rnd(track->cluster[0].dts, MOV_TIMESCALE, track->timescale, AV_ROUND_DOWN) <= 0); + start_ct = -FFMIN(track->cluster[0].dts, 0); //FFMIN needed due to rounding + duration += delay; } /* duration */ @@ -1613,11 +1918,8 @@ static int mov_write_trak_tag(AVIOContext *pb, MOVMuxContext *mov, avio_wb32(pb, 0); /* size */ ffio_wfourcc(pb, "trak"); mov_write_tkhd_tag(pb, track, st); - if (track->mode == MODE_PSP || track->flags & MOV_TRACK_CTTS || - (track->entry && track->cluster[0].dts)) { - if (!(mov->flags & FF_MOV_FLAG_FRAGMENT)) - mov_write_edts_tag(pb, track); // PSP Movies require edts box - } + if (supports_edts(mov)) + mov_write_edts_tag(pb, track); // PSP Movies and several other cases require edts box if (track->tref_tag) mov_write_tref_tag(pb, track); mov_write_mdia_tag(pb, track); @@ -1627,7 +1929,7 @@ static int mov_write_trak_tag(AVIOContext *pb, MOVMuxContext *mov, mov_write_udta_sdp(pb, track); if (track->enc->codec_type == AVMEDIA_TYPE_VIDEO && track->mode == MODE_MOV) { double sample_aspect_ratio = av_q2d(st->sample_aspect_ratio); - if (0.0 != sample_aspect_ratio && 1.0 != sample_aspect_ratio) + if (st->sample_aspect_ratio.num && 1.0 != sample_aspect_ratio) mov_write_tapt_tag(pb, track); } return update_size(pb, pos); @@ -1727,15 +2029,7 @@ static int mov_write_mvhd_tag(AVIOContext *pb, MOVMuxContext *mov) avio_wb32(pb, 0); /* reserved */ /* Matrix structure */ - avio_wb32(pb, 0x00010000); /* reserved */ - avio_wb32(pb, 0x0); /* reserved */ - avio_wb32(pb, 0x0); /* reserved */ - avio_wb32(pb, 0x0); /* reserved */ - avio_wb32(pb, 0x00010000); /* reserved */ - avio_wb32(pb, 0x0); /* reserved */ - avio_wb32(pb, 0x0); /* reserved */ - avio_wb32(pb, 0x0); /* reserved */ - avio_wb32(pb, 0x40000000); /* reserved */ + write_matrix(pb, 1, 0, 0, 1, 0, 0); avio_wb32(pb, 0); /* reserved (preview time) */ avio_wb32(pb, 0); /* reserved (preview duration) */ @@ -1821,28 +2115,79 @@ static int mov_write_string_metadata(AVFormatContext *s, AVIOContext *pb, return mov_write_string_tag(pb, name, t->value, lang, long_style); } -/* iTunes track number */ +/* iTunes bpm number */ +static int mov_write_tmpo_tag(AVIOContext *pb, AVFormatContext *s) +{ + AVDictionaryEntry *t = av_dict_get(s->metadata, "tmpo", NULL, 0); + int size = 0, tmpo = t ? atoi(t->value) : 0; + if (tmpo) { + size = 26; + avio_wb32(pb, size); + ffio_wfourcc(pb, "tmpo"); + avio_wb32(pb, size-8); /* size */ + ffio_wfourcc(pb, "data"); + avio_wb32(pb, 0x15); //type specifier + avio_wb32(pb, 0); + avio_wb16(pb, tmpo); // data + } + return size; +} + +/* iTunes track or disc number */ static int mov_write_trkn_tag(AVIOContext *pb, MOVMuxContext *mov, - AVFormatContext *s) + AVFormatContext *s, int disc) { - AVDictionaryEntry *t = av_dict_get(s->metadata, "track", NULL, 0); + AVDictionaryEntry *t = av_dict_get(s->metadata, + disc ? "disc" : "track", + NULL, 0); int size = 0, track = t ? atoi(t->value) : 0; if (track) { + int tracks = 0; + char *slash = strchr(t->value, '/'); + if (slash) + tracks = atoi(slash + 1); avio_wb32(pb, 32); /* size */ - ffio_wfourcc(pb, "trkn"); + ffio_wfourcc(pb, disc ? "disk" : "trkn"); avio_wb32(pb, 24); /* size */ ffio_wfourcc(pb, "data"); avio_wb32(pb, 0); // 8 bytes empty avio_wb32(pb, 0); avio_wb16(pb, 0); // empty - avio_wb16(pb, track); // track number - avio_wb16(pb, 0); // total track number + avio_wb16(pb, track); // track / disc number + avio_wb16(pb, tracks); // total track / disc number avio_wb16(pb, 0); // empty size = 32; } return size; } +static int mov_write_int8_metadata(AVFormatContext *s, AVIOContext *pb, + const char *name, const char *tag, + int len) +{ + AVDictionaryEntry *t = NULL; + uint8_t num; + int size = 24 + len; + + if (len != 1 && len != 4) + return -1; + + if (!(t = av_dict_get(s->metadata, tag, NULL, 0))) + return 0; + num = atoi(t->value); + + avio_wb32(pb, size); + ffio_wfourcc(pb, name); + avio_wb32(pb, size - 8); + ffio_wfourcc(pb, "data"); + avio_wb32(pb, 0x15); + avio_wb32(pb, 0); + if (len==4) avio_wb32(pb, num); + else avio_w8 (pb, num); + + return size; +} + /* iTunes meta data list */ static int mov_write_ilst_tag(AVIOContext *pb, MOVMuxContext *mov, AVFormatContext *s) @@ -1867,7 +2212,14 @@ static int mov_write_ilst_tag(AVIOContext *pb, MOVMuxContext *mov, mov_write_string_metadata(s, pb, "tvsh", "show" , 1); mov_write_string_metadata(s, pb, "tven", "episode_id",1); mov_write_string_metadata(s, pb, "tvnn", "network" , 1); - mov_write_trkn_tag(pb, mov, s); + mov_write_int8_metadata (s, pb, "tves", "episode_sort",4); + mov_write_int8_metadata (s, pb, "tvsn", "season_number",4); + mov_write_int8_metadata (s, pb, "stik", "media_type",1); + mov_write_int8_metadata (s, pb, "hdvd", "hd_video", 1); + mov_write_int8_metadata (s, pb, "pgap", "gapless_playback",1); + mov_write_trkn_tag(pb, mov, s, 0); // track number + mov_write_trkn_tag(pb, mov, s, 1); // disc number + mov_write_tmpo_tag(pb, s); return update_size(pb, pos); } @@ -1995,7 +2347,10 @@ static int mov_write_udta_tag(AVIOContext *pb, MOVMuxContext *mov, mov_write_string_metadata(s, pb_buf, "\251alb", "album", 0); mov_write_string_metadata(s, pb_buf, "\251day", "date", 0); mov_write_string_metadata(s, pb_buf, "\251swr", "encoder", 0); + // currently ignored by mov.c mov_write_string_metadata(s, pb_buf, "\251des", "comment", 0); + // add support for libquicktime, this atom is also actually read by mov.c + mov_write_string_metadata(s, pb_buf, "\251cmt", "comment", 0); mov_write_string_metadata(s, pb_buf, "\251gen", "genre", 0); mov_write_string_metadata(s, pb_buf, "\251cpy", "copyright", 0); } else { @@ -2066,6 +2421,29 @@ static int mov_write_uuidusmt_tag(AVIOContext *pb, AVFormatContext *s) return 0; } +static void build_chunks(MOVTrack *trk) +{ + int i; + MOVIentry *chunk = &trk->cluster[0]; + uint64_t chunkSize = chunk->size; + chunk->chunkNum = 1; + if (trk->chunkCount) + return; + trk->chunkCount = 1; + for (i = 1; i<trk->entry; i++){ + if (chunk->pos + chunkSize == trk->cluster[i].pos && + chunkSize + trk->cluster[i].size < (1<<20)){ + chunkSize += trk->cluster[i].size; + chunk->samples_in_chunk += trk->cluster[i].entries; + } else { + trk->cluster[i].chunkNum = chunk->chunkNum+1; + chunk=&trk->cluster[i]; + chunkSize = chunk->size; + trk->chunkCount++; + } + } +} + static int mov_write_moov_tag(AVIOContext *pb, MOVMuxContext *mov, AVFormatContext *s) { @@ -2080,6 +2458,9 @@ static int mov_write_moov_tag(AVIOContext *pb, MOVMuxContext *mov, mov->tracks[i].time = mov->time; mov->tracks[i].track_id = i + 1; + + if (mov->tracks[i].entry) + build_chunks(&mov->tracks[i]); } if (mov->chapter_track) @@ -2094,6 +2475,14 @@ static int mov_write_moov_tag(AVIOContext *pb, MOVMuxContext *mov, mov->tracks[mov->tracks[i].src_track].track_id; } } + for (i = 0; i < mov->nb_streams; i++) { + if (mov->tracks[i].tag == MKTAG('t','m','c','d')) { + int src_trk = mov->tracks[i].src_track; + mov->tracks[src_trk].tref_tag = mov->tracks[i].tag; + mov->tracks[src_trk].tref_id = mov->tracks[i].track_id; + mov->tracks[i].track_duration = mov->tracks[src_trk].track_duration; + } + } mov_write_mvhd_tag(pb, mov); if (mov->mode != MODE_MOV && !mov->iods_skip) @@ -2137,7 +2526,7 @@ static int mov_write_isml_manifest(AVIOContext *pb, MOVMuxContext *mov) { int64_t pos = avio_tell(pb); int i; - const uint8_t uuid[] = { + static const uint8_t uuid[] = { 0xa5, 0xd4, 0x0b, 0x30, 0xe8, 0x14, 0x11, 0xdd, 0xba, 0x2f, 0x08, 0x00, 0x20, 0x0c, 0x9a, 0x66 }; @@ -2338,7 +2727,7 @@ static int mov_write_trun_tag(AVIOContext *pb, MOVMuxContext *mov, static int mov_write_tfxd_tag(AVIOContext *pb, MOVTrack *track) { int64_t pos = avio_tell(pb); - const uint8_t uuid[] = { + static const uint8_t uuid[] = { 0x6d, 0x1d, 0x9b, 0x05, 0x42, 0xd5, 0x44, 0xe6, 0x80, 0xe2, 0x14, 0x1d, 0xaf, 0xf7, 0x57, 0xb2 }; @@ -2360,7 +2749,7 @@ static int mov_write_tfrf_tag(AVIOContext *pb, MOVMuxContext *mov, { int n = track->nb_frag_info - 1 - entry, i; int size = 8 + 16 + 4 + 1 + 16*n; - const uint8_t uuid[] = { + static const uint8_t uuid[] = { 0xd4, 0x80, 0x7e, 0xf2, 0xca, 0x39, 0x46, 0x95, 0x8e, 0x54, 0x26, 0xcb, 0x9e, 0x46, 0xa7, 0x9f }; @@ -2384,8 +2773,7 @@ static int mov_write_tfrf_tag(AVIOContext *pb, MOVMuxContext *mov, int free_size = 16 * (mov->ism_lookahead - n); avio_wb32(pb, free_size); ffio_wfourcc(pb, "free"); - for (i = 0; i < free_size - 8; i++) - avio_w8(pb, 0); + ffio_fill(pb, 0, free_size - 8); } return 0; @@ -2741,10 +3129,8 @@ static int mov_flush_fragment(AVFormatContext *s) if (!(mov->flags & FF_MOV_FLAG_EMPTY_MOOV) && mov->fragments == 0) { int64_t pos = avio_tell(s->pb); - int ret; - AVIOContext *moov_buf; uint8_t *buf; - int buf_size; + int buf_size, moov_size; for (i = 0; i < mov->nb_streams; i++) if (!mov->tracks[i].entry) @@ -2753,12 +3139,9 @@ static int mov_flush_fragment(AVFormatContext *s) if (i < mov->nb_streams) return 0; - if ((ret = ffio_open_null_buf(&moov_buf)) < 0) - return ret; - mov_write_moov_tag(moov_buf, mov, s); - buf_size = ffio_close_null_buf(moov_buf); + moov_size = get_moov_size(s); for (i = 0; i < mov->nb_streams; i++) - mov->tracks[i].data_offset = pos + buf_size + 8; + mov->tracks[i].data_offset = pos + moov_size + 8; mov_write_moov_tag(s->pb, mov, s); @@ -2901,6 +3284,9 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt) av_log(s, AV_LOG_ERROR, "fatal error, input is not a single packet, implement a AVParser for it\n"); return -1; } + } else if (enc->codec_id == AV_CODEC_ID_ADPCM_MS || + enc->codec_id == AV_CODEC_ID_ADPCM_IMA_WAV) { + samples_in_chunk = enc->frame_size; } else if (trk->sample_size) samples_in_chunk = size / trk->sample_size; else @@ -2913,6 +3299,16 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt) memcpy(trk->vos_data, enc->extradata, trk->vos_len); } + if (enc->codec_id == AV_CODEC_ID_AAC && pkt->size > 2 && + (AV_RB16(pkt->data) & 0xfff0) == 0xfff0) { + if (!s->streams[pkt->stream_index]->nb_frames) { + av_log(s, AV_LOG_ERROR, "Malformed AAC bitstream detected: " + "use audio bitstream filter 'aac_adtstoasc' to fix it " + "('-bsf:a aac_adtstoasc' option with ffmpeg)\n"); + return -1; + } + av_log(s, AV_LOG_WARNING, "aac bitstream error\n"); + } if (enc->codec_id == AV_CODEC_ID_H264 && trk->vos_len > 0 && *(uint8_t *)trk->vos_data != 1) { /* from x264 or from bytestream h264 */ /* nal reformating needed */ @@ -2947,6 +3343,7 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt) trk->cluster[trk->entry].pos = avio_tell(pb) - size; trk->cluster[trk->entry].samples_in_chunk = samples_in_chunk; + trk->cluster[trk->entry].chunkNum = 0; trk->cluster[trk->entry].size = size; trk->cluster[trk->entry].entries = samples_in_chunk; trk->cluster[trk->entry].dts = pkt->dts; @@ -2957,9 +3354,13 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt) * of this packet to be what the previous packets duration implies. */ trk->cluster[trk->entry].dts = trk->start_dts + trk->track_duration; } + if (!trk->entry && trk->start_dts == AV_NOPTS_VALUE && !supports_edts(mov)) { + trk->cluster[trk->entry].dts = trk->start_dts = 0; + } if (trk->start_dts == AV_NOPTS_VALUE) trk->start_dts = pkt->dts; trk->track_duration = pkt->dts - trk->start_dts + pkt->duration; + trk->last_sample_is_subtitle_end = 0; if (pkt->pts == AV_NOPTS_VALUE) { av_log(s, AV_LOG_WARNING, "pts has no value\n"); @@ -2994,12 +3395,8 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt) return 0; } -static int mov_write_packet(AVFormatContext *s, AVPacket *pkt) +static int mov_write_single_packet(AVFormatContext *s, AVPacket *pkt) { - if (!pkt) { - mov_flush_fragment(s); - return 1; - } else { MOVMuxContext *mov = s->priv_data; MOVTrack *trk = &mov->tracks[pkt->stream_index]; AVCodecContext *enc = trk->enc; @@ -3009,7 +3406,7 @@ static int mov_write_packet(AVFormatContext *s, AVPacket *pkt) if (!pkt->size) return 0; /* Discard 0 sized packets */ - if (trk->entry) + if (trk->entry && pkt->stream_index < s->nb_streams) frag_duration = av_rescale_q(pkt->dts - trk->cluster[0].dts, s->streams[pkt->stream_index]->time_base, AV_TIME_BASE_Q); @@ -3024,6 +3421,73 @@ static int mov_write_packet(AVFormatContext *s, AVPacket *pkt) } return ff_mov_write_packet(s, pkt); +} + +static int mov_write_subtitle_end_packet(AVFormatContext *s, + int stream_index, + int64_t dts) { + AVPacket end; + uint8_t data[2] = {0}; + int ret; + + av_init_packet(&end); + end.size = sizeof(data); + end.data = data; + end.pts = dts; + end.dts = dts; + end.duration = 0; + end.stream_index = stream_index; + + ret = mov_write_single_packet(s, &end); + av_free_packet(&end); + + return ret; +} + +static int mov_write_packet(AVFormatContext *s, AVPacket *pkt) +{ + if (!pkt) { + mov_flush_fragment(s); + return 1; + } else { + int i; + MOVMuxContext *mov = s->priv_data; + + if (!pkt->size) return 0; /* Discard 0 sized packets */ + + /* + * Subtitles require special handling. + * + * 1) For full complaince, every track must have a sample at + * dts == 0, which is rarely true for subtitles. So, as soon + * as we see any packet with dts > 0, write an empty subtitle + * at dts == 0 for any subtitle track with no samples in it. + * + * 2) For each subtitle track, check if the current packet's + * dts is past the duration of the last subtitle sample. If + * so, we now need to write an end sample for that subtitle. + * + * This must be done conditionally to allow for subtitles that + * immediately replace each other, in which case an end sample + * is not needed, and is, in fact, actively harmful. + * + * 3) See mov_write_trailer for how the final end sample is + * handled. + */ + for (i = 0; i < mov->nb_streams; i++) { + MOVTrack *trk = &mov->tracks[i]; + int ret; + + if (trk->enc->codec_id == AV_CODEC_ID_MOV_TEXT && + trk->track_duration < pkt->dts && + (trk->entry == 0 || !trk->last_sample_is_subtitle_end)) { + ret = mov_write_subtitle_end_packet(s, i, trk->track_duration); + if (ret < 0) return ret; + trk->last_sample_is_subtitle_end = 1; + } + } + + return mov_write_single_packet(s, pkt); } } @@ -3031,12 +3495,12 @@ static int mov_write_packet(AVFormatContext *s, AVPacket *pkt) // as samples, and a tref pointing from the other tracks to the chapter one. static int mov_create_chapter_track(AVFormatContext *s, int tracknum) { + AVIOContext *pb; + MOVMuxContext *mov = s->priv_data; MOVTrack *track = &mov->tracks[tracknum]; AVPacket pkt = { .stream_index = tracknum, .flags = AV_PKT_FLAG_KEY }; int i, len; - // These properties are required to make QT recognize the chapter track - uint8_t chapter_properties[43] = { 0, 0, 0, 0, 0, 0, 0, 1, }; track->mode = mov->mode; track->tag = MKTAG('t','e','x','t'); @@ -3045,11 +3509,57 @@ static int mov_create_chapter_track(AVFormatContext *s, int tracknum) if (!track->enc) return AVERROR(ENOMEM); track->enc->codec_type = AVMEDIA_TYPE_SUBTITLE; - track->enc->extradata = av_malloc(sizeof(chapter_properties)); - if (track->enc->extradata == NULL) +#if 0 + // These properties are required to make QT recognize the chapter track + uint8_t chapter_properties[43] = { 0, 0, 0, 0, 0, 0, 0, 1, }; + if (ff_alloc_extradata(track->enc, sizeof(chapter_properties))) return AVERROR(ENOMEM); - track->enc->extradata_size = sizeof(chapter_properties); memcpy(track->enc->extradata, chapter_properties, sizeof(chapter_properties)); +#else + if (avio_open_dyn_buf(&pb) >= 0) { + int size; + uint8_t *buf; + + /* Stub header (usually for Quicktime chapter track) */ + // TextSampleEntry + avio_wb32(pb, 0x01); // displayFlags + avio_w8(pb, 0x00); // horizontal justification + avio_w8(pb, 0x00); // vertical justification + avio_w8(pb, 0x00); // bgColourRed + avio_w8(pb, 0x00); // bgColourGreen + avio_w8(pb, 0x00); // bgColourBlue + avio_w8(pb, 0x00); // bgColourAlpha + // BoxRecord + avio_wb16(pb, 0x00); // defTextBoxTop + avio_wb16(pb, 0x00); // defTextBoxLeft + avio_wb16(pb, 0x00); // defTextBoxBottom + avio_wb16(pb, 0x00); // defTextBoxRight + // StyleRecord + avio_wb16(pb, 0x00); // startChar + avio_wb16(pb, 0x00); // endChar + avio_wb16(pb, 0x01); // fontID + avio_w8(pb, 0x00); // fontStyleFlags + avio_w8(pb, 0x00); // fontSize + avio_w8(pb, 0x00); // fgColourRed + avio_w8(pb, 0x00); // fgColourGreen + avio_w8(pb, 0x00); // fgColourBlue + avio_w8(pb, 0x00); // fgColourAlpha + // FontTableBox + avio_wb32(pb, 0x0D); // box size + ffio_wfourcc(pb, "ftab"); // box atom name + avio_wb16(pb, 0x01); // entry count + // FontRecord + avio_wb16(pb, 0x01); // font ID + avio_w8(pb, 0x00); // font name length + + if ((size = avio_close_dyn_buf(pb, &buf)) > 0) { + track->enc->extradata = buf; + track->enc->extradata_size = size; + } else { + av_free(&buf); + } + } +#endif for (i = 0; i < s->nb_chapters; i++) { AVChapter *c = s->chapters[i]; @@ -3075,6 +3585,43 @@ static int mov_create_chapter_track(AVFormatContext *s, int tracknum) return 0; } +static int mov_create_timecode_track(AVFormatContext *s, int index, int src_index, const char *tcstr) +{ + int ret; + MOVMuxContext *mov = s->priv_data; + MOVTrack *track = &mov->tracks[index]; + AVStream *src_st = s->streams[src_index]; + AVTimecode tc; + AVPacket pkt = {.stream_index = index, .flags = AV_PKT_FLAG_KEY, .size = 4}; + AVRational rate = find_fps(s, src_st); + + /* compute the frame number */ + ret = av_timecode_init_from_string(&tc, rate, tcstr, s); + if (ret < 0) + return ret; + + /* tmcd track based on video stream */ + track->mode = mov->mode; + track->tag = MKTAG('t','m','c','d'); + track->src_track = src_index; + track->timescale = mov->tracks[src_index].timescale; + if (tc.flags & AV_TIMECODE_FLAG_DROPFRAME) + track->timecode_flags |= MOV_TIMECODE_FLAG_DROPFRAME; + + /* encode context: tmcd data stream */ + track->enc = avcodec_alloc_context3(NULL); + track->enc->codec_type = AVMEDIA_TYPE_DATA; + track->enc->codec_tag = track->tag; + track->enc->time_base = av_inv_q(rate); + + /* the tmcd track just contains one packet with the frame number */ + pkt.data = av_malloc(pkt.size); + AV_WB32(pkt.data, tc.start); + ret = ff_mov_write_packet(s, &pkt); + av_free(pkt.data); + return ret; +} + /* * st->disposition controls the "enabled" flag in the tkhd tag. * QuickTime will not play a track if it is not enabled. So make sure @@ -3132,18 +3679,20 @@ static void mov_free(AVFormatContext *s) if (mov->chapter_track) { if (mov->tracks[mov->chapter_track].enc) - av_free(mov->tracks[mov->chapter_track].enc->extradata); + av_freep(&mov->tracks[mov->chapter_track].enc->extradata); av_freep(&mov->tracks[mov->chapter_track].enc); } for (i = 0; i < mov->nb_streams; i++) { if (mov->tracks[i].tag == MKTAG('r','t','p',' ')) ff_mov_close_hinting(&mov->tracks[i]); + else if (mov->tracks[i].tag == MKTAG('t','m','c','d') && mov->nb_meta_tmcd) + av_freep(&mov->tracks[i].enc); av_freep(&mov->tracks[i].cluster); av_freep(&mov->tracks[i].frag_info); if (mov->tracks[i].vos_len) - av_free(mov->tracks[i].vos_data); + av_freep(&mov->tracks[i].vos_data); } av_freep(&mov->tracks); @@ -3153,8 +3702,8 @@ static int mov_write_header(AVFormatContext *s) { AVIOContext *pb = s->pb; MOVMuxContext *mov = s->priv_data; - AVDictionaryEntry *t; - int i, hint_track = 0; + AVDictionaryEntry *t, *global_tcr = av_dict_get(s->metadata, "timecode", NULL, 0); + int i, hint_track = 0, tmcd_track = 0; /* Default mode == MP4 */ mov->mode = MODE_MP4; @@ -3189,7 +3738,12 @@ static int mov_write_header(AVFormatContext *s) av_log(s, AV_LOG_WARNING, "The faststart flag is incompatible " "with fragmentation and custom IO, disabling faststart\n"); mov->flags &= ~FF_MOV_FLAG_FASTSTART; - } + } else + mov->reserved_moov_size = -1; + } + + if (!supports_edts(mov) && s->avoid_negative_ts < 0) { + s->avoid_negative_ts = 1; } /* Non-seekable output is ok if using fragmentation. If ism_lookahead @@ -3200,10 +3754,20 @@ static int mov_write_header(AVFormatContext *s) return AVERROR(EINVAL); } - mov_write_ftyp_tag(pb,s); if (mov->mode == MODE_PSP) { - if (s->nb_streams != 2) { + int video_streams_nb = 0, audio_streams_nb = 0, other_streams_nb = 0; + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) + video_streams_nb++; + else if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) + audio_streams_nb++; + else + other_streams_nb++; + } + + if (video_streams_nb != 1 || audio_streams_nb != 1 || other_streams_nb) { av_log(s, AV_LOG_ERROR, "PSP mode need one video and one audio stream\n"); return AVERROR(EINVAL); } @@ -3226,6 +3790,32 @@ static int mov_write_header(AVFormatContext *s) } } + if (mov->mode == MODE_MOV) { + tmcd_track = mov->nb_streams; + + /* +1 tmcd track for each video stream with a timecode */ + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO && + (global_tcr || av_dict_get(st->metadata, "timecode", NULL, 0))) + mov->nb_meta_tmcd++; + } + + /* check if there is already a tmcd track to remux */ + if (mov->nb_meta_tmcd) { + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + if (st->codec->codec_tag == MKTAG('t','m','c','d')) { + av_log(s, AV_LOG_WARNING, "You requested a copy of the original timecode track " + "so timecode metadata are now ignored\n"); + mov->nb_meta_tmcd = 0; + } + } + } + + mov->nb_streams += mov->nb_meta_tmcd; + } + // Reserve an extra stream for chapters for the case where chapters // are written in the trailer mov->tracks = av_mallocz((mov->nb_streams + 1) * sizeof(*mov->tracks)); @@ -3238,6 +3828,7 @@ static int mov_write_header(AVFormatContext *s) AVDictionaryEntry *lang = av_dict_get(st->metadata, "language", NULL,0); track->enc = st->codec; + track->st = st; track->language = ff_mov_iso639_to_lang(lang?lang->value:"und", mov->mode!=MODE_MOV); if (track->language < 0) track->language = 0; @@ -3262,7 +3853,13 @@ static int mov_write_header(AVFormatContext *s) } track->height = track->tag >> 24 == 'n' ? 486 : 576; } - track->timescale = st->codec->time_base.den; + if (mov->video_track_timescale) { + track->timescale = mov->video_track_timescale; + } else { + track->timescale = st->codec->time_base.den; + while(track->timescale < 10000) + track->timescale *= 2; + } if (track->mode == MODE_MOV && track->timescale > 100000) av_log(s, AV_LOG_WARNING, "WARNING codec timebase is very high. If duration is too long,\n" @@ -3270,17 +3867,23 @@ static int mov_write_header(AVFormatContext *s) "or choose different container.\n"); } else if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) { track->timescale = st->codec->sample_rate; - /* set sample_size for PCM and ADPCM */ - if (av_get_bits_per_sample(st->codec->codec_id) || - st->codec->codec_id == AV_CODEC_ID_ILBC) { + if (!st->codec->frame_size && !av_get_bits_per_sample(st->codec->codec_id)) { + av_log(s, AV_LOG_WARNING, "track %d: codec frame size is not set\n", i); + track->audio_vbr = 1; + }else if (st->codec->codec_id == AV_CODEC_ID_ADPCM_MS || + st->codec->codec_id == AV_CODEC_ID_ADPCM_IMA_WAV || + st->codec->codec_id == AV_CODEC_ID_ILBC){ if (!st->codec->block_align) { - av_log(s, AV_LOG_ERROR, "track %d: codec block align is not set\n", i); + av_log(s, AV_LOG_ERROR, "track %d: codec block align is not set for adpcm\n", i); goto error; } track->sample_size = st->codec->block_align; + }else if (st->codec->frame_size > 1){ /* assume compressed audio */ + track->audio_vbr = 1; + }else{ + track->sample_size = (av_get_bits_per_sample(st->codec->codec_id) >> 3) * st->codec->channels; } - /* set audio_vbr for compressed audio */ - if (av_get_bits_per_sample(st->codec->codec_id) < 8) { + if (st->codec->codec_id == AV_CODEC_ID_ILBC) { track->audio_vbr = 1; } if (track->mode != MODE_MOV && @@ -3293,6 +3896,8 @@ static int mov_write_header(AVFormatContext *s) track->timescale = st->codec->time_base.den; } else if (st->codec->codec_type == AVMEDIA_TYPE_DATA) { track->timescale = st->codec->time_base.den; + } else { + track->timescale = MOV_TIMESCALE; } if (!track->height) track->height = st->codec->height; @@ -3321,6 +3926,12 @@ static int mov_write_header(AVFormatContext *s) mov->flags |= FF_MOV_FLAG_FRAG_KEYFRAME; } + if (mov->reserved_moov_size){ + mov->reserved_moov_pos= avio_tell(pb); + if (mov->reserved_moov_size > 0) + avio_skip(pb, mov->reserved_moov_size); + } + if (!(mov->flags & FF_MOV_FLAG_FRAGMENT)) { if (mov->flags & FF_MOV_FLAG_FASTSTART) mov->reserved_moov_pos = avio_tell(pb); @@ -3342,12 +3953,31 @@ static int mov_write_header(AVFormatContext *s) AVStream *st = s->streams[i]; if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO || st->codec->codec_type == AVMEDIA_TYPE_AUDIO) { - ff_mov_init_hinting(s, hint_track, i); + if (ff_mov_init_hinting(s, hint_track, i) < 0) + goto error; hint_track++; } } } + if (mov->nb_meta_tmcd) { + /* Initialize the tmcd tracks */ + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + t = global_tcr; + + if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) { + if (!t) + t = av_dict_get(st->metadata, "timecode", NULL, 0); + if (!t) + continue; + if (mov_create_timecode_track(s, tmcd_track, i, t->value) < 0) + goto error; + tmcd_track++; + } + } + } + avio_flush(pb); if (mov->flags & FF_MOV_FLAG_ISML) @@ -3478,6 +4108,19 @@ static int mov_write_trailer(AVFormatContext *s) int i; int64_t moov_pos; + /* + * Before actually writing the trailer, make sure that there are no + * dangling subtitles, that need a terminating sample. + */ + for (i = 0; i < mov->nb_streams; i++) { + MOVTrack *trk = &mov->tracks[i]; + if (trk->enc->codec_id == AV_CODEC_ID_MOV_TEXT && + !trk->last_sample_is_subtitle_end) { + mov_write_subtitle_end_packet(s, i, trk->track_duration); + trk->last_sample_is_subtitle_end = 1; + } + } + // If there were no chapters when the header was written, but there // are chapters now, write them in the trailer. This only works // when we are not doing fragments. @@ -3505,7 +4148,7 @@ static int mov_write_trailer(AVFormatContext *s) ffio_wfourcc(pb, "mdat"); avio_wb64(pb, mov->mdat_size + 16); } - avio_seek(pb, moov_pos, SEEK_SET); + avio_seek(pb, mov->reserved_moov_size > 0 ? mov->reserved_moov_pos : moov_pos, SEEK_SET); if (mov->flags & FF_MOV_FLAG_FASTSTART) { av_log(s, AV_LOG_INFO, "Starting second pass: moving the moov atom to the beginning of the file\n"); @@ -3514,6 +4157,19 @@ static int mov_write_trailer(AVFormatContext *s) avio_seek(s->pb, mov->reserved_moov_pos, SEEK_SET); mov_write_moov_tag(pb, mov, s); } + } else if (mov->reserved_moov_size > 0) { + int64_t size; + mov_write_moov_tag(pb, mov, s); + size = mov->reserved_moov_size - (avio_tell(pb) - mov->reserved_moov_pos); + if (size < 8){ + av_log(s, AV_LOG_ERROR, "reserved_moov_size is too small, needed %"PRId64" additional\n", 8-size); + return -1; + } + avio_wb32(pb, size); + ffio_wfourcc(pb, "free"); + for (i = 0; i < size; i++) + avio_w8(pb, 0); + avio_seek(pb, moov_pos, SEEK_SET); } else { mov_write_moov_tag(pb, mov, s); } @@ -3681,7 +4337,7 @@ AVOutputFormat ff_f4v_muxer = { .write_header = mov_write_header, .write_packet = mov_write_packet, .write_trailer = mov_write_trailer, - .flags = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH | AVFMT_TS_NEGATIVE, + .flags = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH, .codec_tag = (const AVCodecTag* const []){ codec_f4v_tags, 0 }, .priv_class = &f4v_muxer_class, }; diff --git a/libavformat/movenc.h b/libavformat/movenc.h index 1b669e8613..09f3ea772c 100644 --- a/libavformat/movenc.h +++ b/libavformat/movenc.h @@ -4,20 +4,20 @@ * Copyright (c) 2004 Gildas Bazin <gbazin at videolan dot org> * Copyright (c) 2009 Baptiste Coudurier <baptiste dot coudurier at gmail dot com> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -36,7 +36,7 @@ #define MODE_MOV 0x02 #define MODE_3GP 0x04 #define MODE_PSP 0x08 // example working PSP command line: -// avconv -i testinput.avi -f psp -r 14.985 -s 320x240 -b 768 -ar 24000 -ab 32 M4V00001.MP4 +// ffmpeg -i testinput.avi -f psp -r 14.985 -s 320x240 -b 768 -ar 24000 -ab 32 M4V00001.MP4 #define MODE_3G2 0x10 #define MODE_IPOD 0x20 #define MODE_ISM 0x40 @@ -47,6 +47,7 @@ typedef struct MOVIentry { int64_t dts; unsigned int size; unsigned int samples_in_chunk; + unsigned int chunkNum; ///< Chunk number if the current entry is a chunk start otherwise 0 unsigned int entries; int cts; #define MOV_SYNC_SAMPLE 0x0001 @@ -81,16 +82,23 @@ typedef struct MOVTrack { unsigned timescale; uint64_t time; int64_t track_duration; + int last_sample_is_subtitle_end; long sample_count; long sample_size; + long chunkCount; int has_keyframes; #define MOV_TRACK_CTTS 0x0001 #define MOV_TRACK_STPS 0x0002 #define MOV_TRACK_ENABLED 0x0004 uint32_t flags; +#define MOV_TIMECODE_FLAG_DROPFRAME 0x0001 +#define MOV_TIMECODE_FLAG_24HOURSMAX 0x0002 +#define MOV_TIMECODE_FLAG_ALLOWNEGATIVE 0x0004 + uint32_t timecode_flags; int language; int track_id; int tag; ///< stsd fourcc + AVStream *st; AVCodecContext *enc; int vos_len; @@ -104,7 +112,7 @@ typedef struct MOVTrack { int64_t start_dts; int hint_track; ///< the track that hints this track, -1 if no hint track is set - int src_track; ///< the track that this hint track describes + int src_track; ///< the track that this hint (or tmcd) track describes AVFormatContext *rtp_ctx; ///< the format context for the hinting rtp muxer uint32_t prev_rtp_ts; int64_t cur_rtp_ts_unwrapped; @@ -140,6 +148,7 @@ typedef struct MOVMuxContext { int mode; int64_t time; int nb_streams; + int nb_meta_tmcd; ///< number of new created tmcd track based on metadata (aka not data copy) int chapter_track; ///< qt chapter track number int64_t mdat_pos; uint64_t mdat_size; @@ -147,6 +156,7 @@ typedef struct MOVMuxContext { int flags; int rtp_flags; + int iods_skip; int iods_video_profile; int iods_audio_profile; @@ -158,6 +168,10 @@ typedef struct MOVMuxContext { int ism_lookahead; AVIOContext *mdat_buf; + int use_editlist; + int video_track_timescale; + + int reserved_moov_size; ///< 0 for disabled, -1 for automatic, size otherwise int64_t reserved_moov_pos; } MOVMuxContext; diff --git a/libavformat/movenchint.c b/libavformat/movenchint.c index a8f5f34f9c..9d6a66e8ce 100644 --- a/libavformat/movenchint.c +++ b/libavformat/movenchint.c @@ -2,20 +2,20 @@ * MOV, 3GP, MP4 muxer RTP hinting * Copyright (c) 2010 Martin Storsjo * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -104,11 +104,12 @@ static void sample_queue_push(HintSampleQueue *queue, uint8_t *data, int size, if (size <= 14) return; if (!queue->samples || queue->len >= queue->size) { - queue->size += 10; - if (av_reallocp(&queue->samples, sizeof(*queue->samples) * queue->size) < 0) { - queue->len = queue->size = 0; + HintSample *samples; + samples = av_realloc_array(queue->samples, queue->size + 10, sizeof(HintSample)); + if (!samples) return; - } + queue->size += 10; + queue->samples = samples; } queue->samples[queue->len].data = data; queue->samples[queue->len].size = size; diff --git a/libavformat/mp3dec.c b/libavformat/mp3dec.c index 5c263edca9..30ec7771fd 100644 --- a/libavformat/mp3dec.c +++ b/libavformat/mp3dec.c @@ -2,23 +2,24 @@ * MP3 demuxer * Copyright (c) 2003 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "libavutil/opt.h" #include "libavutil/avstring.h" #include "libavutil/intreadwrite.h" #include "libavutil/dict.h" @@ -35,8 +36,15 @@ #define XING_TOC_COUNT 100 -typedef struct MP3DecContext { +typedef struct { + AVClass *class; + int64_t filesize; + int64_t header_filesize; int xing_toc; + int start_pad; + int end_pad; + int usetoc; + int is_cbr; } MP3DecContext; /* mp3 read */ @@ -46,7 +54,7 @@ static int mp3_read_probe(AVProbeData *p) int max_frames, first_frames = 0; int fsize, frames, sample_rate; uint32_t header; - uint8_t *buf, *buf0, *buf2, *end; + const uint8_t *buf, *buf0, *buf2, *end; AVCodecContext avctx; buf0 = p->buf; @@ -73,31 +81,13 @@ static int mp3_read_probe(AVProbeData *p) } // keep this in sync with ac3 probe, both need to avoid // issues with MPEG-files! - if (first_frames >= 4) return AVPROBE_SCORE_EXTENSION + 1; - - if (max_frames) { - int pes = 0, i; - unsigned int code = -1; - -#define VIDEO_ID 0x000001e0 -#define AUDIO_ID 0x000001c0 - /* do a search for mpegps headers to be able to properly bias - * towards mpegps if we detect this stream as both. */ - for (i = 0; i<p->buf_size; i++) { - code = (code << 8) + p->buf[i]; - if ((code & 0xffffff00) == 0x100) { - if ((code & 0x1f0) == VIDEO_ID) pes++; - else if((code & 0x1e0) == AUDIO_ID) pes++; - } - } - - if (pes) - max_frames = (max_frames + pes - 1) / pes; - } - if (max_frames > 500) return AVPROBE_SCORE_EXTENSION; - else if (max_frames >= 4) return AVPROBE_SCORE_EXTENSION / 2; - else if (max_frames >= 1) return 1; - else return 0; + if (first_frames>=4) return AVPROBE_SCORE_EXTENSION + 1; + else if(max_frames>200)return AVPROBE_SCORE_EXTENSION; + else if(max_frames>=4) return AVPROBE_SCORE_EXTENSION / 2; + else if(ff_id3v2_match(buf0, ID3v2_DEFAULT_MAGIC) && 2*ff_id3v2_tag_len(buf0) >= p->buf_size) + return p->buf_size < PROBE_BUF_MAX ? AVPROBE_SCORE_EXTENSION / 4 : AVPROBE_SCORE_EXTENSION - 2; + else if(max_frames>=1) return 1; + else return 0; //mpegps_mp3_unrecognized_format.mpg has max_frames=3 } @@ -105,22 +95,24 @@ static void read_xing_toc(AVFormatContext *s, int64_t filesize, int64_t duration { int i; MP3DecContext *mp3 = s->priv_data; + int fill_index = mp3->usetoc && duration > 0; if (!filesize && !(filesize = avio_size(s->pb))) { av_log(s, AV_LOG_WARNING, "Cannot determine file size, skipping TOC table.\n"); - return; + fill_index = 0; } for (i = 0; i < XING_TOC_COUNT; i++) { uint8_t b = avio_r8(s->pb); - - av_add_index_entry(s->streams[0], + if (fill_index) + av_add_index_entry(s->streams[0], av_rescale(b, filesize, 256), av_rescale(i, duration, XING_TOC_COUNT), 0, 0, AVINDEX_KEYFRAME); } - mp3->xing_toc = 1; + if (fill_index) + mp3->xing_toc = 1; } /** @@ -128,10 +120,11 @@ static void read_xing_toc(AVFormatContext *s, int64_t filesize, int64_t duration */ static int mp3_parse_vbr_tags(AVFormatContext *s, AVStream *st, int64_t base) { + MP3DecContext *mp3 = s->priv_data; uint32_t v, spf; unsigned frames = 0; /* Total number of frames in file */ unsigned size = 0; /* Total number of bytes in the stream */ - const int64_t xing_offtbl[2][2] = {{32, 17}, {17,9}}; + static const int64_t xing_offtbl[2][2] = {{32, 17}, {17,9}}; MPADecodeHeader c; int vbrtag_size = 0; int is_cbr; @@ -157,9 +150,21 @@ static int mp3_parse_vbr_tags(AVFormatContext *s, AVStream *st, int64_t base) frames = avio_rb32(s->pb); if(v & XING_FLAG_SIZE) size = avio_rb32(s->pb); - if (v & XING_FLAG_TOC && frames) + if (v & XING_FLAG_TOC) read_xing_toc(s, size, av_rescale_q(frames, (AVRational){spf, c.sample_rate}, st->time_base)); + if(v & 8) + avio_skip(s->pb, 4); + + v = avio_rb32(s->pb); + if(v == MKBETAG('L', 'A', 'M', 'E') || v == MKBETAG('L', 'a', 'v', 'f')) { + avio_skip(s->pb, 21-4); + v= avio_rb24(s->pb); + mp3->start_pad = v>>12; + mp3-> end_pad = v&4095; + st->skip_samples = mp3->start_pad + 528 + 1; + av_log(s, AV_LOG_DEBUG, "pad %d %d\n", mp3->start_pad, mp3-> end_pad); + } } /* Check for VBRI tag (always 32 bytes after end of mpegaudio header) */ @@ -187,11 +192,15 @@ static int mp3_parse_vbr_tags(AVFormatContext *s, AVStream *st, int64_t base) if (size && frames && !is_cbr) st->codec->bit_rate = av_rescale(size, 8 * c.sample_rate, frames * (int64_t)spf); + mp3->is_cbr = is_cbr; + mp3->header_filesize = size; + return 0; } static int mp3_read_header(AVFormatContext *s) { + MP3DecContext *mp3 = s->priv_data; AVStream *st; int64_t off; @@ -201,17 +210,21 @@ static int mp3_read_header(AVFormatContext *s) st->codec->codec_type = AVMEDIA_TYPE_AUDIO; st->codec->codec_id = AV_CODEC_ID_MP3; - st->need_parsing = AVSTREAM_PARSE_FULL; + st->need_parsing = AVSTREAM_PARSE_FULL_RAW; st->start_time = 0; // lcm of all mp3 sample rates avpriv_set_pts_info(st, 64, 1, 14112000); + s->pb->maxsize = -1; off = avio_tell(s->pb); if (!av_dict_get(s->metadata, "", NULL, AV_DICT_IGNORE_SUFFIX)) ff_id3v1_read(s); + if(s->pb->seekable) + mp3->filesize = avio_size(s->pb); + if (mp3_parse_vbr_tags(s, st, off) < 0) avio_seek(s->pb, off, SEEK_SET); @@ -223,15 +236,26 @@ static int mp3_read_header(AVFormatContext *s) static int mp3_read_packet(AVFormatContext *s, AVPacket *pkt) { - int ret; - - ret = av_get_packet(s->pb, pkt, MP3_PACKET_SIZE); - if (ret < 0) - return ret; + MP3DecContext *mp3 = s->priv_data; + int ret, size; + int64_t pos; + + size= MP3_PACKET_SIZE; + pos = avio_tell(s->pb); + if(mp3->filesize > ID3v1_TAG_SIZE && pos < mp3->filesize) + size= FFMIN(size, mp3->filesize - pos); + + ret= av_get_packet(s->pb, pkt, size); + if (ret <= 0) { + if(ret<0) + return ret; + return AVERROR_EOF; + } + pkt->flags &= ~AV_PKT_FLAG_CORRUPT; pkt->stream_index = 0; - if (ret > ID3v1_TAG_SIZE && + if (ret >= ID3v1_TAG_SIZE && memcmp(&pkt->data[ret - ID3v1_TAG_SIZE], "TAG", 3) == 0) ret -= ID3v1_TAG_SIZE; @@ -241,38 +265,93 @@ static int mp3_read_packet(AVFormatContext *s, AVPacket *pkt) return ret; } +static int check(AVFormatContext *s, int64_t pos) +{ + int64_t ret = avio_seek(s->pb, pos, SEEK_SET); + unsigned header; + MPADecodeHeader sd; + if (ret < 0) + return ret; + header = avio_rb32(s->pb); + if (ff_mpa_check_header(header) < 0) + return -1; + if (avpriv_mpegaudio_decode_header(&sd, header) == 1) + return -1; + return sd.frame_size; +} + static int mp3_seek(AVFormatContext *s, int stream_index, int64_t timestamp, int flags) { MP3DecContext *mp3 = s->priv_data; - AVIndexEntry *ie; + AVIndexEntry *ie, ie1; AVStream *st = s->streams[0]; int64_t ret = av_index_search_timestamp(st, timestamp, flags); - uint32_t header = 0; - - if (!mp3->xing_toc) - return AVERROR(ENOSYS); + int i, j; + int dir = (flags&AVSEEK_FLAG_BACKWARD) ? -1 : 1; + + if (mp3->is_cbr && st->duration > 0 && mp3->header_filesize > s->data_offset) { + int64_t filesize = avio_size(s->pb); + int64_t duration; + if (filesize <= s->data_offset) + filesize = mp3->header_filesize; + filesize -= s->data_offset; + duration = av_rescale(st->duration, filesize, mp3->header_filesize - s->data_offset); + ie = &ie1; + timestamp = av_clip64(timestamp, 0, duration); + ie->timestamp = timestamp; + ie->pos = av_rescale(timestamp, filesize, duration) + s->data_offset; + } else if (mp3->xing_toc) { + if (ret < 0) + return ret; + + ie = &st->index_entries[ret]; + } else { + st->skip_samples = timestamp <= 0 ? mp3->start_pad + 528 + 1 : 0; - if (ret < 0) - return ret; + return -1; + } - ie = &st->index_entries[ret]; ret = avio_seek(s->pb, ie->pos, SEEK_SET); if (ret < 0) return ret; - while (!s->pb->eof_reached) { - header = (header << 8) + avio_r8(s->pb); - if (ff_mpa_check_header(header) >= 0) { - ff_update_cur_dts(s, st, ie->timestamp); - ret = avio_seek(s->pb, -4, SEEK_CUR); - return (ret >= 0) ? 0 : ret; +#define MIN_VALID 3 + for(i=0; i<4096; i++) { + int64_t pos = ie->pos + i*dir; + for(j=0; j<MIN_VALID; j++) { + ret = check(s, pos); + if(ret < 0) + break; + pos += ret; } + if(j==MIN_VALID) + break; } + if(j!=MIN_VALID) + i=0; - return AVERROR_EOF; + ret = avio_seek(s->pb, ie->pos + i*dir, SEEK_SET); + if (ret < 0) + return ret; + ff_update_cur_dts(s, st, ie->timestamp); + st->skip_samples = ie->timestamp <= 0 ? mp3->start_pad + 528 + 1 : 0; + return 0; } +static const AVOption options[] = { + { "usetoc", "use table of contents", offsetof(MP3DecContext, usetoc), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 1, AV_OPT_FLAG_DECODING_PARAM}, + { NULL }, +}; + +static const AVClass demuxer_class = { + .class_name = "mp3", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, + .category = AV_CLASS_CATEGORY_DEMUXER, +}; + AVInputFormat ff_mp3_demuxer = { .name = "mp3", .long_name = NULL_IF_CONFIG_SMALL("MP2/3 (MPEG audio layer 2/3)"), @@ -283,4 +362,5 @@ AVInputFormat ff_mp3_demuxer = { .priv_data_size = sizeof(MP3DecContext), .flags = AVFMT_GENERIC_INDEX, .extensions = "mp2,mp3,m2a,mpa", /* XXX: use probe */ + .priv_class = &demuxer_class, }; diff --git a/libavformat/mp3enc.c b/libavformat/mp3enc.c index 574a3b2e95..a5f672b257 100644 --- a/libavformat/mp3enc.c +++ b/libavformat/mp3enc.c @@ -2,20 +2,20 @@ * MP3 muxer * Copyright (c) 2003 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -51,11 +51,12 @@ static int id3v1_create_tag(AVFormatContext *s, uint8_t *buf) buf[0] = 'T'; buf[1] = 'A'; buf[2] = 'G'; - count += id3v1_set_string(s, "TIT2", buf + 3, 30); //title - count += id3v1_set_string(s, "TPE1", buf + 33, 30); //author|artist - count += id3v1_set_string(s, "TALB", buf + 63, 30); //album - count += id3v1_set_string(s, "TDRL", buf + 93, 4); //date - count += id3v1_set_string(s, "comment", buf + 97, 30); + /* we knowingly overspecify each tag length by one byte to compensate for the mandatory null byte added by av_strlcpy */ + count += id3v1_set_string(s, "TIT2", buf + 3, 30 + 1); //title + count += id3v1_set_string(s, "TPE1", buf + 33, 30 + 1); //author|artist + count += id3v1_set_string(s, "TALB", buf + 63, 30 + 1); //album + count += id3v1_set_string(s, "TDRL", buf + 93, 4 + 1); //date + count += id3v1_set_string(s, "comment", buf + 97, 30 + 1); if ((tag = av_dict_get(s->metadata, "TRCK", NULL, 0))) { //track buf[125] = 0; buf[126] = atoi(tag->value); @@ -110,19 +111,22 @@ static const uint8_t xing_offtbl[2][2] = {{32, 17}, {17, 9}}; /* * Write an empty XING header and initialize respective data. */ -static void mp3_write_xing(AVFormatContext *s) +static int mp3_write_xing(AVFormatContext *s) { MP3Context *mp3 = s->priv_data; AVCodecContext *codec = s->streams[mp3->audio_stream_idx]->codec; - int32_t header; - MPADecodeHeader mpah; - int srate_idx, i, channels; - int bitrate_idx; - int xing_offset; - int ver = 0; + int bitrate_idx; + int best_bitrate_idx = -1; + int best_bitrate_error= INT_MAX; + int xing_offset; + int32_t header, mask; + MPADecodeHeader c; + int srate_idx, ver = 0, i, channels; + int needed; + const char *vendor = (codec->flags & CODEC_FLAG_BITEXACT) ? "Lavf" : LIBAVFORMAT_IDENT; if (!s->pb->seekable) - return; + return 0; for (i = 0; i < FF_ARRAY_ELEMS(avpriv_mpa_freq_tab); i++) { const uint16_t base_freq = avpriv_mpa_freq_tab[i]; @@ -136,9 +140,8 @@ static void mp3_write_xing(AVFormatContext *s) break; } if (i == FF_ARRAY_ELEMS(avpriv_mpa_freq_tab)) { - av_log(s, AV_LOG_WARNING, "Unsupported sample rate, not writing Xing " - "header.\n"); - return; + av_log(s, AV_LOG_WARNING, "Unsupported sample rate, not writing Xing header.\n"); + return -1; } switch (codec->channels) { @@ -146,41 +149,76 @@ static void mp3_write_xing(AVFormatContext *s) case 2: channels = MPA_STEREO; break; default: av_log(s, AV_LOG_WARNING, "Unsupported number of channels, " "not writing Xing header.\n"); - return; + return -1; } - /* 64 kbps frame, should be large enough */ - bitrate_idx = (ver == 3) ? 5 : 8; - /* dummy MPEG audio header */ - header = 0xff << 24; // sync + header = 0xffU << 24; // sync header |= (0x7 << 5 | ver << 3 | 0x1 << 1 | 0x1) << 16; // sync/audio-version/layer 3/no crc*/ - header |= (bitrate_idx << 4 | srate_idx << 2) << 8; + header |= (srate_idx << 2) << 8; header |= channels << 6; - avio_wb32(s->pb, header); - avpriv_mpegaudio_decode_header(&mpah, header); + for (bitrate_idx=1; bitrate_idx<15; bitrate_idx++) { + int error; + avpriv_mpegaudio_decode_header(&c, header | (bitrate_idx << (4+8))); + error= FFABS(c.bit_rate - codec->bit_rate); + if(error < best_bitrate_error){ + best_bitrate_error= error; + best_bitrate_idx = bitrate_idx; + } + } + av_assert0(best_bitrate_idx >= 0); + + for (bitrate_idx= best_bitrate_idx;; bitrate_idx++) { + if (15 == bitrate_idx) + return -1; + mask = bitrate_idx << (4+8); + header |= mask; + avpriv_mpegaudio_decode_header(&c, header); + xing_offset=xing_offtbl[c.lsf == 1][c.nb_channels == 1]; + needed = 4 // header + + xing_offset + + 4 // xing tag + + 4 // frames/size/toc flags + + 4 // frames + + 4 // size + + XING_TOC_SIZE // toc + + 24 + ; + + if (needed <= c.frame_size) + break; + header &= ~mask; + } - av_assert0(mpah.frame_size >= XING_MAX_SIZE); + avio_wb32(s->pb, header); - xing_offset = xing_offtbl[ver != 3][codec->channels == 1]; ffio_fill(s->pb, 0, xing_offset); mp3->xing_offset = avio_tell(s->pb); ffio_wfourcc(s->pb, "Xing"); avio_wb32(s->pb, 0x01 | 0x02 | 0x04); // frames / size / TOC - mp3->size = mpah.frame_size; - mp3->want = 1; + mp3->size = c.frame_size; + mp3->want=1; + mp3->seen=0; + mp3->pos=0; avio_wb32(s->pb, 0); // frames avio_wb32(s->pb, 0); // size - // TOC - for (i = 0; i < XING_TOC_SIZE; i++) - avio_w8(s->pb, 255 * i / XING_TOC_SIZE); + // toc + for (i = 0; i < XING_TOC_SIZE; ++i) + avio_w8(s->pb, (uint8_t)(255 * i / XING_TOC_SIZE)); - mpah.frame_size -= 4 + xing_offset + 4 + 4 + 4 + 4 + XING_TOC_SIZE; - ffio_fill(s->pb, 0, mpah.frame_size); + for (i = 0; i < strlen(vendor); ++i) + avio_w8(s->pb, vendor[i]); + for (; i < 21; ++i) + avio_w8(s->pb, 0); + avio_wb24(s->pb, FFMAX(codec->delay - 528 - 1, 0)<<12); + + ffio_fill(s->pb, 0, c.frame_size - needed); + + return 0; } /* @@ -201,7 +239,7 @@ static void mp3_xing_add_frame(MP3Context *mp3, AVPacket *pkt) if (XING_NUM_BAGS == ++mp3->pos) { /* shrink table to half size by throwing away each second bag. */ for (i = 1; i < XING_NUM_BAGS; i += 2) - mp3->bag[i / 2] = mp3->bag[i]; + mp3->bag[i >> 1] = mp3->bag[i]; /* double wanted amount per bag. */ mp3->want *= 2; @@ -217,17 +255,43 @@ static int mp3_write_audio_packet(AVFormatContext *s, AVPacket *pkt) { MP3Context *mp3 = s->priv_data; - if (mp3->xing_offset && pkt->size >= 4) { + if (pkt->data && pkt->size >= 4) { MPADecodeHeader c; + int av_unused base; + uint32_t head = AV_RB32(pkt->data); - avpriv_mpegaudio_decode_header(&c, AV_RB32(pkt->data)); + if (ff_mpa_check_header(head) < 0) { + av_log(s, AV_LOG_WARNING, "Audio packet of size %d (starting with %08X...) " + "is invalid, writing it anyway.\n", pkt->size, head); + return ff_raw_write_packet(s, pkt); + } + avpriv_mpegaudio_decode_header(&c, head); if (!mp3->initial_bitrate) mp3->initial_bitrate = c.bit_rate; if ((c.bit_rate == 0) || (mp3->initial_bitrate != c.bit_rate)) mp3->has_variable_bitrate = 1; - mp3_xing_add_frame(mp3, pkt); +#ifdef FILTER_VBR_HEADERS + /* filter out XING and INFO headers. */ + base = 4 + xing_offtbl[c.lsf == 1][c.nb_channels == 1]; + + if (base + 4 <= pkt->size) { + uint32_t v = AV_RB32(pkt->data + base); + + if (MKBETAG('X','i','n','g') == v || MKBETAG('I','n','f','o') == v) + return 0; + } + + /* filter out VBRI headers. */ + base = 4 + 32; + + if (base + 4 <= pkt->size && MKBETAG('V','B','R','I') == AV_RB32(pkt->data + base)) + return 0; +#endif + + if (mp3->xing_offset) + mp3_xing_add_frame(mp3, pkt); } return ff_raw_write_packet(s, pkt); @@ -301,6 +365,17 @@ static int mp3_write_trailer(struct AVFormatContext *s) return 0; } +static int query_codec(enum AVCodecID id, int std_compliance) +{ + const CodecMime *cm= ff_id3v2_mime_tags; + while(cm->id != AV_CODEC_ID_NONE) { + if(id == cm->id) + return MKTAG('A', 'P', 'I', 'C'); + cm++; + } + return -1; +} + #if CONFIG_MP2_MUXER AVOutputFormat ff_mp2_muxer = { .name = "mp2", @@ -436,6 +511,7 @@ AVOutputFormat ff_mp3_muxer = { .write_header = mp3_write_header, .write_packet = mp3_write_packet, .write_trailer = mp3_write_trailer, + .query_codec = query_codec, .flags = AVFMT_NOTIMESTAMPS, .priv_class = &mp3_muxer_class, }; diff --git a/libavformat/mpc.c b/libavformat/mpc.c index 5e771ae4aa..8abc488553 100644 --- a/libavformat/mpc.c +++ b/libavformat/mpc.c @@ -2,20 +2,20 @@ * Musepack demuxer * Copyright (c) 2006 Konstantin Shishkov * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -95,8 +95,8 @@ static int mpc_read_header(AVFormatContext *s) st->codec->channel_layout = AV_CH_LAYOUT_STEREO; st->codec->bits_per_coded_sample = 16; - st->codec->extradata_size = 16; - st->codec->extradata = av_mallocz(st->codec->extradata_size+FF_INPUT_BUFFER_PADDING_SIZE); + if (ff_alloc_extradata(st->codec, 16)) + return AVERROR(ENOMEM); avio_read(s->pb, st->codec->extradata, 16); st->codec->sample_rate = mpc_rate[st->codec->extradata[2] & 3]; avpriv_set_pts_info(st, 32, MPC_FRAMESIZE, st->codec->sample_rate); @@ -196,11 +196,11 @@ static int mpc_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp MPCContext *c = s->priv_data; AVPacket pkt1, *pkt = &pkt1; int ret; - int index = av_index_search_timestamp(st, timestamp - DELAY_FRAMES, flags); + int index = av_index_search_timestamp(st, FFMAX(timestamp - DELAY_FRAMES, 0), flags); uint32_t lastframe; /* if found, seek there */ - if (index >= 0){ + if (index >= 0 && st->index_entries[st->nb_index_entries-1].timestamp >= timestamp - DELAY_FRAMES){ c->curframe = st->index_entries[index].pos; return 0; } diff --git a/libavformat/mpc8.c b/libavformat/mpc8.c index 3564e81c32..7017c187f7 100644 --- a/libavformat/mpc8.c +++ b/libavformat/mpc8.c @@ -2,20 +2,20 @@ * Musepack SV8 demuxer * Copyright (c) 2007 Konstantin Shishkov * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -55,7 +55,7 @@ typedef struct { int64_t apetag_start; } MPCContext; -static inline int64_t bs_get_v(uint8_t **bs) +static inline int64_t bs_get_v(const uint8_t **bs) { int64_t v = 0; int br = 0; @@ -75,8 +75,8 @@ static inline int64_t bs_get_v(uint8_t **bs) static int mpc8_probe(AVProbeData *p) { - uint8_t *bs = p->buf + 4; - uint8_t *bs_end = bs + p->buf_size; + const uint8_t *bs = p->buf + 4; + const uint8_t *bs_end = bs + p->buf_size; int64_t size; if (p->buf_size < 16) @@ -150,7 +150,7 @@ static void mpc8_parse_seektable(AVFormatContext *s, int64_t off) av_log(s, AV_LOG_ERROR, "No seek table at given position\n"); return; } - if (size < 0 || size >= INT_MAX / 2) { + if (size > INT_MAX/10 || size<=0) { av_log(s, AV_LOG_ERROR, "Bad seek table size\n"); return; } @@ -213,7 +213,7 @@ static int mpc8_read_header(AVFormatContext *s) return AVERROR_INVALIDDATA; } - while(!pb->eof_reached){ + while(!url_feof(pb)){ pos = avio_tell(pb); mpc8_get_chunk_header(pb, &tag, &size); if(tag == TAG_STREAMHDR) @@ -241,8 +241,8 @@ static int mpc8_read_header(AVFormatContext *s) st->codec->codec_id = AV_CODEC_ID_MUSEPACK8; st->codec->bits_per_coded_sample = 16; - st->codec->extradata_size = 2; - st->codec->extradata = av_mallocz(st->codec->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE); + if (ff_alloc_extradata(st->codec, 2)) + return AVERROR(ENOMEM); avio_read(pb, st->codec->extradata, st->codec->extradata_size); st->codec->channels = (st->codec->extradata[1] >> 4) + 1; @@ -251,6 +251,8 @@ static int mpc8_read_header(AVFormatContext *s) st->start_time = 0; st->duration = c->samples / (1152 << (st->codec->extradata[1]&3)*2); size -= avio_tell(pb) - pos; + if (size > 0) + avio_skip(pb, size); if (pb->seekable) { int64_t pos = avio_tell(s->pb); @@ -267,7 +269,7 @@ static int mpc8_read_packet(AVFormatContext *s, AVPacket *pkt) int tag; int64_t pos, size; - while(!s->pb->eof_reached){ + while(!url_feof(s->pb)){ pos = avio_tell(s->pb); /* don't return bogus packets with the ape tag data */ diff --git a/libavformat/mpeg.c b/libavformat/mpeg.c index 7430bb0113..1ea58d5ed5 100644 --- a/libavformat/mpeg.c +++ b/libavformat/mpeg.c @@ -2,20 +2,20 @@ * MPEG1/2 demuxer * Copyright (c) 2000, 2001, 2002 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -23,15 +23,21 @@ #include "internal.h" #include "mpeg.h" +#if CONFIG_VOBSUB_DEMUXER +# include "subtitles.h" +# include "libavutil/bprint.h" +#endif + #undef NDEBUG #include <assert.h> +#include "libavutil/avassert.h" /*********************************************/ /* demux code */ #define MAX_SYNC_SIZE 100000 -static int check_pes(uint8_t *p, uint8_t *end){ +static int check_pes(const uint8_t *p, const uint8_t *end){ int pes1; int pes2= (p[3] & 0xC0) == 0x80 && (p[4] & 0xC0) != 0x40 @@ -74,6 +80,7 @@ static int mpegps_probe(AVProbeData *p) // and audio streams else if((code & 0xe0) == AUDIO_ID && pes) {audio++; i+=len;} else if(code == PRIVATE_STREAM_1 && pes) {priv1++; i+=len;} + else if(code == 0x1fd && pes) vid++; //VC1 else if((code & 0xf0) == VIDEO_ID && !pes) invalid++; else if((code & 0xe0) == AUDIO_ID && !pes) invalid++; @@ -81,18 +88,19 @@ static int mpegps_probe(AVProbeData *p) } } - if(vid+audio > invalid) /* invalid VDR files nd short PES streams */ + if(vid+audio > invalid+1) /* invalid VDR files nd short PES streams */ score = AVPROBE_SCORE_EXTENSION / 2; if(sys>invalid && sys*9 <= pspack*10) - return pspack > 2 ? AVPROBE_SCORE_EXTENSION + 2 : AVPROBE_SCORE_EXTENSION / 2; // 1 more than .mpg + return (audio > 12 || vid > 3 || pspack > 2) ? AVPROBE_SCORE_EXTENSION + 2 : AVPROBE_SCORE_EXTENSION / 2; // 1 more than .mpg if(pspack > invalid && (priv1+vid+audio)*10 >= pspack*9) return pspack > 2 ? AVPROBE_SCORE_EXTENSION + 2 : AVPROBE_SCORE_EXTENSION / 2; // 1 more than .mpg if((!!vid ^ !!audio) && (audio > 4 || vid > 1) && !sys && !pspack && p->buf_size>2048 && vid + audio > invalid) /* PES stream */ - return (audio > 12 || vid > 3) ? AVPROBE_SCORE_EXTENSION + 2 : AVPROBE_SCORE_EXTENSION / 2; + return (audio > 12 || vid > 3 + 2*invalid) ? AVPROBE_SCORE_EXTENSION + 2 : AVPROBE_SCORE_EXTENSION / 2; //02-Penguin.flac has sys:0 priv1:0 pspack:0 vid:0 audio:1 //mp3_misidentified_2.mp3 has sys:0 priv1:0 pspack:0 vid:0 audio:6 + //Have\ Yourself\ a\ Merry\ Little\ Christmas.mp3 0 0 0 5 0 1 len:21618 return score; } @@ -101,25 +109,30 @@ typedef struct MpegDemuxContext { int32_t header_state; unsigned char psm_es_type[256]; int sofdec; + int dvd; + int imkh_cctv; +#if CONFIG_VOBSUB_DEMUXER + AVFormatContext *sub_ctx; + FFDemuxSubtitlesQueue q[32]; +#endif } MpegDemuxContext; static int mpegps_read_header(AVFormatContext *s) { MpegDemuxContext *m = s->priv_data; - const char *sofdec = "Sofdec"; - int v, i = 0; + char buffer[7]; + int64_t last_pos = avio_tell(s->pb); m->header_state = 0xff; s->ctx_flags |= AVFMTCTX_NOHEADER; - m->sofdec = -1; - do { - v = avio_r8(s->pb); - m->header_state = m->header_state << 8 | v; - m->sofdec++; - } while (v == sofdec[i] && i++ < 6); - - m->sofdec = (m->sofdec == 6) ? 1 : 0; + avio_get_str(s->pb, 6, buffer, sizeof(buffer)); + if (!memcmp("IMKH", buffer, 4)) { + m->imkh_cctv = 1; + } else if (!memcmp("Sofdec", buffer, 6)) { + m->sofdec = 1; + } else + avio_seek(s->pb, last_pos, SEEK_SET); /* no need to do more */ return 0; @@ -144,7 +157,7 @@ static int find_next_start_code(AVIOContext *pb, int *size_ptr, state = *header_state; n = *size_ptr; while (n > 0) { - if (pb->eof_reached) + if (url_feof(pb)) break; v = avio_r8(pb); n--; @@ -218,7 +231,7 @@ static int mpegps_read_pes_header(AVFormatContext *s, startcode = find_next_start_code(s->pb, &size, &m->header_state); last_sync = avio_tell(s->pb); if (startcode < 0){ - if(s->pb->eof_reached) + if(url_feof(s->pb)) return AVERROR_EOF; //FIXME we should remember header_state return AVERROR(EAGAIN); @@ -233,21 +246,82 @@ static int mpegps_read_pes_header(AVFormatContext *s, goto redo; } if (startcode == PRIVATE_STREAM_2) { - len = avio_rb16(s->pb); if (!m->sofdec) { - while (len-- >= 6) { - if (avio_r8(s->pb) == 'S') { - uint8_t buf[5]; - avio_read(s->pb, buf, sizeof(buf)); - m->sofdec = !memcmp(buf, "ofdec", 5); - len -= sizeof(buf); - break; + /* Need to detect whether this from a DVD or a 'Sofdec' stream */ + int len = avio_rb16(s->pb); + int bytesread = 0; + uint8_t *ps2buf = av_malloc(len); + + if (ps2buf) { + bytesread = avio_read(s->pb, ps2buf, len); + + if (bytesread != len) { + avio_skip(s->pb, len - bytesread); + } else { + uint8_t *p = 0; + if (len >= 6) + p = memchr(ps2buf, 'S', len - 5); + + if (p) + m->sofdec = !memcmp(p+1, "ofdec", 5); + + m->sofdec -= !m->sofdec; + + if (m->sofdec < 0) { + if (len == 980 && ps2buf[0] == 0) { + /* PCI structure? */ + uint32_t startpts = AV_RB32(ps2buf + 0x0d); + uint32_t endpts = AV_RB32(ps2buf + 0x11); + uint8_t hours = ((ps2buf[0x19] >> 4) * 10) + (ps2buf[0x19] & 0x0f); + uint8_t mins = ((ps2buf[0x1a] >> 4) * 10) + (ps2buf[0x1a] & 0x0f); + uint8_t secs = ((ps2buf[0x1b] >> 4) * 10) + (ps2buf[0x1b] & 0x0f); + + m->dvd = (hours <= 23 && + mins <= 59 && + secs <= 59 && + (ps2buf[0x19] & 0x0f) < 10 && + (ps2buf[0x1a] & 0x0f) < 10 && + (ps2buf[0x1b] & 0x0f) < 10 && + endpts >= startpts); + } else if (len == 1018 && ps2buf[0] == 1) { + /* DSI structure? */ + uint8_t hours = ((ps2buf[0x1d] >> 4) * 10) + (ps2buf[0x1d] & 0x0f); + uint8_t mins = ((ps2buf[0x1e] >> 4) * 10) + (ps2buf[0x1e] & 0x0f); + uint8_t secs = ((ps2buf[0x1f] >> 4) * 10) + (ps2buf[0x1f] & 0x0f); + + m->dvd = (hours <= 23 && + mins <= 59 && + secs <= 59 && + (ps2buf[0x1d] & 0x0f) < 10 && + (ps2buf[0x1e] & 0x0f) < 10 && + (ps2buf[0x1f] & 0x0f) < 10); + } + } } + + av_free(ps2buf); + + /* If this isn't a DVD packet or no memory + * could be allocated, just ignore it. + * If we did, move back to the start of the + * packet (plus 'length' field) */ + if (!m->dvd || avio_skip(s->pb, -(len + 2)) < 0) { + /* Skip back failed. + * This packet will be lost but that can't be helped + * if we can't skip back + */ + goto redo; + } + } else { + /* No memory */ + avio_skip(s->pb, len); + goto redo; } - m->sofdec -= !m->sofdec; + } else if (!m->dvd) { + int len = avio_rb16(s->pb); + avio_skip(s->pb, len); + goto redo; } - avio_skip(s->pb, len); - goto redo; } if (startcode == PROGRAM_STREAM_MAP) { mpegps_psm_parse(m, s->pb); @@ -257,7 +331,9 @@ static int mpegps_read_pes_header(AVFormatContext *s, /* find matching stream */ if (!((startcode >= 0x1c0 && startcode <= 0x1df) || (startcode >= 0x1e0 && startcode <= 0x1ef) || - (startcode == 0x1bd) || (startcode == 0x1fd))) + (startcode == 0x1bd) || + (startcode == PRIVATE_STREAM_2) || + (startcode == 0x1fd))) goto redo; if (ppos) { *ppos = avio_tell(s->pb) - 4; @@ -265,6 +341,8 @@ static int mpegps_read_pes_header(AVFormatContext *s, len = avio_rb16(s->pb); pts = dts = AV_NOPTS_VALUE; + if (startcode != PRIVATE_STREAM_2) + { /* stuffing */ for(;;) { if (len < 1) @@ -338,22 +416,11 @@ static int mpegps_read_pes_header(AVFormatContext *s, } else if( c!= 0xf ) goto redo; + } - if (startcode == PRIVATE_STREAM_1 && !m->psm_es_type[startcode & 0xff]) { + if (startcode == PRIVATE_STREAM_1) { startcode = avio_r8(s->pb); len--; - if (startcode >= 0x80 && startcode <= 0xcf) { - /* audio: skip header */ - avio_r8(s->pb); - avio_r8(s->pb); - avio_r8(s->pb); - len -= 3; - if (startcode >= 0xb0 && startcode <= 0xbf) { - /* MLP/TrueHD audio has a 4-byte header */ - avio_r8(s->pb); - len--; - } - } } if(len<0) goto error_redo; @@ -380,20 +447,30 @@ static int mpegps_read_packet(AVFormatContext *s, MpegDemuxContext *m = s->priv_data; AVStream *st; int len, startcode, i, es_type, ret; + int lpcm_header_len = -1; //Init to supress warning + int request_probe= 0; enum AVCodecID codec_id = AV_CODEC_ID_NONE; enum AVMediaType type; int64_t pts, dts, dummy_pos; //dummy_pos is needed for the index building to work - uint8_t av_uninit(dvdaudio_substream_type); redo: len = mpegps_read_pes_header(s, &dummy_pos, &startcode, &pts, &dts); if (len < 0) return len; - if(startcode == 0x1bd) { - dvdaudio_substream_type = avio_r8(s->pb); - avio_skip(s->pb, 3); - len -= 4; + if (startcode >= 0x80 && startcode <= 0xcf) { + if(len < 4) + goto skip; + + /* audio: skip header */ + avio_r8(s->pb); + lpcm_header_len = avio_rb16(s->pb); + len -= 3; + if (startcode >= 0xb0 && startcode <= 0xbf) { + /* MLP/TrueHD audio has a 4-byte header */ + avio_r8(s->pb); + len--; + } } /* now find stream */ @@ -404,7 +481,6 @@ static int mpegps_read_packet(AVFormatContext *s, } es_type = m->psm_es_type[startcode & 0xff]; - if(es_type > 0 && es_type != STREAM_TYPE_PRIVATE_DATA){ if(es_type == STREAM_TYPE_VIDEO_MPEG1){ codec_id = AV_CODEC_ID_MPEG2VIDEO; type = AVMEDIA_TYPE_VIDEO; @@ -427,9 +503,9 @@ static int mpegps_read_packet(AVFormatContext *s, } else if(es_type == STREAM_TYPE_AUDIO_AC3){ codec_id = AV_CODEC_ID_AC3; type = AVMEDIA_TYPE_AUDIO; - } else { - goto skip; - } + } else if(m->imkh_cctv && es_type == 0x91){ + codec_id = AV_CODEC_ID_PCM_MULAW; + type = AVMEDIA_TYPE_AUDIO; } else if (startcode >= 0x1e0 && startcode <= 0x1ef) { static const unsigned char avs_seqh[4] = { 0, 0, 1, 0xb0 }; unsigned char buf[8]; @@ -438,8 +514,11 @@ static int mpegps_read_packet(AVFormatContext *s, if(!memcmp(buf, avs_seqh, 4) && (buf[6] != 0 || buf[7] != 1)) codec_id = AV_CODEC_ID_CAVS; else - codec_id = AV_CODEC_ID_PROBE; + request_probe= 1; type = AVMEDIA_TYPE_VIDEO; + } else if (startcode == PRIVATE_STREAM_2) { + type = AVMEDIA_TYPE_DATA; + codec_id = AV_CODEC_ID_DVD_NAV; } else if (startcode >= 0x1c0 && startcode <= 0x1df) { type = AVMEDIA_TYPE_AUDIO; codec_id = m->sofdec > 0 ? AV_CODEC_ID_ADPCM_ADX : AV_CODEC_ID_MP2; @@ -453,7 +532,11 @@ static int mpegps_read_packet(AVFormatContext *s, codec_id = AV_CODEC_ID_DTS; } else if (startcode >= 0xa0 && startcode <= 0xaf) { type = AVMEDIA_TYPE_AUDIO; - codec_id = AV_CODEC_ID_PCM_DVD; + if(lpcm_header_len == 6) { + codec_id = AV_CODEC_ID_MLP; + } else { + codec_id = AV_CODEC_ID_PCM_DVD; + } } else if (startcode >= 0xb0 && startcode <= 0xbf) { type = AVMEDIA_TYPE_AUDIO; codec_id = AV_CODEC_ID_TRUEHD; @@ -467,19 +550,6 @@ static int mpegps_read_packet(AVFormatContext *s, } else if (startcode >= 0xfd55 && startcode <= 0xfd5f) { type = AVMEDIA_TYPE_VIDEO; codec_id = AV_CODEC_ID_VC1; - } else if (startcode == 0x1bd) { - // check dvd audio substream type - type = AVMEDIA_TYPE_AUDIO; - switch(dvdaudio_substream_type & 0xe0) { - case 0xa0: codec_id = AV_CODEC_ID_PCM_DVD; - break; - case 0x80: if((dvdaudio_substream_type & 0xf8) == 0x88) - codec_id = AV_CODEC_ID_DTS; - else codec_id = AV_CODEC_ID_AC3; - break; - default: av_log(s, AV_LOG_ERROR, "Unknown 0x1bd sub-stream\n"); - goto skip; - } } else { skip: /* skip packet */ @@ -493,10 +563,24 @@ static int mpegps_read_packet(AVFormatContext *s, st->id = startcode; st->codec->codec_type = type; st->codec->codec_id = codec_id; + if (st->codec->codec_id == AV_CODEC_ID_PCM_MULAW) { + st->codec->channels = 1; + st->codec->channel_layout = AV_CH_LAYOUT_MONO; + st->codec->sample_rate = 8000; + } + st->request_probe = request_probe; st->need_parsing = AVSTREAM_PARSE_FULL; found: if(st->discard >= AVDISCARD_ALL) goto skip; + if (startcode >= 0xa0 && startcode <= 0xaf) { + if (lpcm_header_len == 6 && st->codec->codec_id == AV_CODEC_ID_MLP) { + if (len < 6) + goto skip; + avio_skip(s->pb, 6); + len -=6; + } + } ret = av_get_packet(s->pb, pkt, len); pkt->pts = pts; pkt->dts = dts; @@ -547,3 +631,319 @@ AVInputFormat ff_mpegps_demuxer = { .read_timestamp = mpegps_read_dts, .flags = AVFMT_SHOW_IDS | AVFMT_TS_DISCONT, }; + +#if CONFIG_VOBSUB_DEMUXER + +#define REF_STRING "# VobSub index file," + +static int vobsub_probe(AVProbeData *p) +{ + if (!strncmp(p->buf, REF_STRING, sizeof(REF_STRING) - 1)) + return AVPROBE_SCORE_MAX; + return 0; +} + +static int vobsub_read_header(AVFormatContext *s) +{ + int i, ret = 0, header_parsed = 0, langidx = 0; + MpegDemuxContext *vobsub = s->priv_data; + char *sub_name = NULL; + size_t fname_len; + char *ext, *header_str; + AVBPrint header; + int64_t delay = 0; + AVStream *st = NULL; + + sub_name = av_strdup(s->filename); + fname_len = strlen(sub_name); + ext = sub_name - 3 + fname_len; + if (fname_len < 4 || *(ext - 1) != '.') { + av_log(s, AV_LOG_ERROR, "The input index filename is too short " + "to guess the associated .SUB file\n"); + ret = AVERROR_INVALIDDATA; + goto end; + } + memcpy(ext, !strncmp(ext, "IDX", 3) ? "SUB" : "sub", 3); + av_log(s, AV_LOG_VERBOSE, "IDX/SUB: %s -> %s\n", s->filename, sub_name); + ret = avformat_open_input(&vobsub->sub_ctx, sub_name, &ff_mpegps_demuxer, NULL); + if (ret < 0) { + av_log(s, AV_LOG_ERROR, "Unable to open %s as MPEG subtitles\n", sub_name); + goto end; + } + + av_bprint_init(&header, 0, AV_BPRINT_SIZE_UNLIMITED); + while (!url_feof(s->pb)) { + char line[2048]; + int len = ff_get_line(s->pb, line, sizeof(line)); + + if (!len) + break; + + line[strcspn(line, "\r\n")] = 0; + + if (!strncmp(line, "id:", 3)) { + int n, stream_id = 0; + char id[64] = {0}; + + n = sscanf(line, "id: %63[^,], index: %u", id, &stream_id); + if (n != 2) { + av_log(s, AV_LOG_WARNING, "Unable to parse index line '%s', " + "assuming 'id: und, index: 0'\n", line); + strcpy(id, "und"); + stream_id = 0; + } + + if (stream_id >= FF_ARRAY_ELEMS(vobsub->q)) { + av_log(s, AV_LOG_ERROR, "Maximum number of subtitles streams reached\n"); + ret = AVERROR(EINVAL); + goto end; + } + + st = avformat_new_stream(s, NULL); + if (!st) { + ret = AVERROR(ENOMEM); + goto end; + } + st->id = stream_id; + st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE; + st->codec->codec_id = AV_CODEC_ID_DVD_SUBTITLE; + avpriv_set_pts_info(st, 64, 1, 1000); + av_dict_set(&st->metadata, "language", id, 0); + av_log(s, AV_LOG_DEBUG, "IDX stream[%d] id=%s\n", stream_id, id); + header_parsed = 1; + + } else if (st && !strncmp(line, "timestamp:", 10)) { + AVPacket *sub; + int hh, mm, ss, ms; + int64_t pos, timestamp; + const char *p = line + 10; + + if (!s->nb_streams) { + av_log(s, AV_LOG_ERROR, "Timestamp declared before any stream\n"); + ret = AVERROR_INVALIDDATA; + goto end; + } + + if (sscanf(p, "%02d:%02d:%02d:%03d, filepos: %"SCNx64, + &hh, &mm, &ss, &ms, &pos) != 5) { + av_log(s, AV_LOG_ERROR, "Unable to parse timestamp line '%s', " + "abort parsing\n", line); + break; + } + timestamp = (hh*3600LL + mm*60LL + ss) * 1000LL + ms + delay; + timestamp = av_rescale_q(timestamp, (AVRational){1,1000}, st->time_base); + + sub = ff_subtitles_queue_insert(&vobsub->q[s->nb_streams - 1], "", 0, 0); + if (!sub) { + ret = AVERROR(ENOMEM); + goto end; + } + sub->pos = pos; + sub->pts = timestamp; + sub->stream_index = s->nb_streams - 1; + + } else if (st && !strncmp(line, "alt:", 4)) { + const char *p = line + 4; + + while (*p == ' ') + p++; + av_dict_set(&st->metadata, "title", p, 0); + av_log(s, AV_LOG_DEBUG, "IDX stream[%d] name=%s\n", st->id, p); + header_parsed = 1; + + } else if (!strncmp(line, "delay:", 6)) { + int sign = 1, hh = 0, mm = 0, ss = 0, ms = 0; + const char *p = line + 6; + + while (*p == ' ') + p++; + if (*p == '-' || *p == '+') { + sign = *p == '-' ? -1 : 1; + p++; + } + sscanf(p, "%d:%d:%d:%d", &hh, &mm, &ss, &ms); + delay = ((hh*3600LL + mm*60LL + ss) * 1000LL + ms) * sign; + + } else if (!strncmp(line, "langidx:", 8)) { + const char *p = line + 8; + + if (sscanf(p, "%d", &langidx) != 1) + av_log(s, AV_LOG_ERROR, "Invalid langidx specified\n"); + + } else if (!header_parsed) { + if (line[0] && line[0] != '#') + av_bprintf(&header, "%s\n", line); + } + } + + if (langidx < s->nb_streams) + s->streams[langidx]->disposition |= AV_DISPOSITION_DEFAULT; + + for (i = 0; i < s->nb_streams; i++) { + vobsub->q[i].sort = SUB_SORT_POS_TS; + ff_subtitles_queue_finalize(&vobsub->q[i]); + } + + if (!av_bprint_is_complete(&header)) { + av_bprint_finalize(&header, NULL); + ret = AVERROR(ENOMEM); + goto end; + } + av_bprint_finalize(&header, &header_str); + for (i = 0; i < s->nb_streams; i++) { + AVStream *sub_st = s->streams[i]; + sub_st->codec->extradata = av_strdup(header_str); + sub_st->codec->extradata_size = header.len; + } + av_free(header_str); + +end: + av_free(sub_name); + return ret; +} + +#define FAIL(r) do { ret = r; goto fail; } while (0) + +static int vobsub_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + MpegDemuxContext *vobsub = s->priv_data; + FFDemuxSubtitlesQueue *q; + AVIOContext *pb = vobsub->sub_ctx->pb; + int ret, psize, total_read = 0, i; + AVPacket idx_pkt; + + int64_t min_ts = INT64_MAX; + int sid = 0; + for (i = 0; i < s->nb_streams; i++) { + FFDemuxSubtitlesQueue *tmpq = &vobsub->q[i]; + int64_t ts = tmpq->subs[tmpq->current_sub_idx].pts; + if (ts < min_ts) { + min_ts = ts; + sid = i; + } + } + q = &vobsub->q[sid]; + ret = ff_subtitles_queue_read_packet(q, &idx_pkt); + if (ret < 0) + return ret; + + /* compute maximum packet size using the next packet position. This is + * useful when the len in the header is non-sense */ + if (q->current_sub_idx < q->nb_subs) { + psize = q->subs[q->current_sub_idx].pos - idx_pkt.pos; + } else { + int64_t fsize = avio_size(pb); + psize = fsize < 0 ? 0xffff : fsize - idx_pkt.pos; + } + + avio_seek(pb, idx_pkt.pos, SEEK_SET); + + av_init_packet(pkt); + pkt->size = 0; + pkt->data = NULL; + + do { + int n, to_read, startcode; + int64_t pts, dts; + int64_t old_pos = avio_tell(pb), new_pos; + int pkt_size; + + ret = mpegps_read_pes_header(vobsub->sub_ctx, NULL, &startcode, &pts, &dts); + if (ret < 0) { + if (pkt->size) // raise packet even if incomplete + break; + FAIL(ret); + } + to_read = ret & 0xffff; + new_pos = avio_tell(pb); + pkt_size = ret + (new_pos - old_pos); + + /* this prevents reads above the current packet */ + if (total_read + pkt_size > psize) + break; + total_read += pkt_size; + + /* the current chunk doesn't match the stream index (unlikely) */ + if ((startcode & 0x1f) != idx_pkt.stream_index) + break; + + ret = av_grow_packet(pkt, to_read); + if (ret < 0) + FAIL(ret); + + n = avio_read(pb, pkt->data + (pkt->size - to_read), to_read); + if (n < to_read) + pkt->size -= to_read - n; + } while (total_read < psize); + + pkt->pts = pkt->dts = idx_pkt.pts; + pkt->pos = idx_pkt.pos; + pkt->stream_index = idx_pkt.stream_index; + + av_free_packet(&idx_pkt); + return 0; + +fail: + av_free_packet(pkt); + av_free_packet(&idx_pkt); + return ret; +} + +static int vobsub_read_seek(AVFormatContext *s, int stream_index, + int64_t min_ts, int64_t ts, int64_t max_ts, int flags) +{ + MpegDemuxContext *vobsub = s->priv_data; + + /* Rescale requested timestamps based on the first stream (timebase is the + * same for all subtitles stream within a .idx/.sub). Rescaling is done just + * like in avformat_seek_file(). */ + if (stream_index == -1 && s->nb_streams != 1) { + int i, ret = 0; + AVRational time_base = s->streams[0]->time_base; + ts = av_rescale_q(ts, AV_TIME_BASE_Q, time_base); + min_ts = av_rescale_rnd(min_ts, time_base.den, + time_base.num * (int64_t)AV_TIME_BASE, + AV_ROUND_UP | AV_ROUND_PASS_MINMAX); + max_ts = av_rescale_rnd(max_ts, time_base.den, + time_base.num * (int64_t)AV_TIME_BASE, + AV_ROUND_DOWN | AV_ROUND_PASS_MINMAX); + for (i = 0; i < s->nb_streams; i++) { + int r = ff_subtitles_queue_seek(&vobsub->q[i], s, stream_index, + min_ts, ts, max_ts, flags); + if (r < 0) + ret = r; + } + return ret; + } + + if (stream_index == -1) // only 1 stream + stream_index = 0; + return ff_subtitles_queue_seek(&vobsub->q[stream_index], s, stream_index, + min_ts, ts, max_ts, flags); +} + +static int vobsub_read_close(AVFormatContext *s) +{ + int i; + MpegDemuxContext *vobsub = s->priv_data; + + for (i = 0; i < s->nb_streams; i++) + ff_subtitles_queue_clean(&vobsub->q[i]); + if (vobsub->sub_ctx) + avformat_close_input(&vobsub->sub_ctx); + return 0; +} + +AVInputFormat ff_vobsub_demuxer = { + .name = "vobsub", + .long_name = NULL_IF_CONFIG_SMALL("VobSub subtitle format"), + .priv_data_size = sizeof(MpegDemuxContext), + .read_probe = vobsub_probe, + .read_header = vobsub_read_header, + .read_packet = vobsub_read_packet, + .read_seek2 = vobsub_read_seek, + .read_close = vobsub_read_close, + .flags = AVFMT_SHOW_IDS, + .extensions = "idx", +}; +#endif diff --git a/libavformat/mpeg.h b/libavformat/mpeg.h index 629f2f061d..cf10d6a4c4 100644 --- a/libavformat/mpeg.h +++ b/libavformat/mpeg.h @@ -2,20 +2,20 @@ * MPEG1/2 muxer and demuxer common defines * Copyright (c) 2000, 2001, 2002 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -41,7 +41,7 @@ #define AUDIO_ID 0xc0 #define VIDEO_ID 0xe0 #define AC3_ID 0x80 -#define DTS_ID 0x8a +#define DTS_ID 0x88 #define LPCM_ID 0xa0 #define SUB_ID 0x20 diff --git a/libavformat/mpegenc.c b/libavformat/mpegenc.c index 85a12d69d8..0a9d69b344 100644 --- a/libavformat/mpegenc.c +++ b/libavformat/mpegenc.c @@ -2,20 +2,20 @@ * MPEG1/2 muxer * Copyright (c) 2000, 2001, 2002 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -66,6 +66,7 @@ typedef struct { int pack_header_freq; /* frequency (in packets^-1) at which we send pack headers */ int system_header_freq; int system_header_size; + int user_mux_rate; /* bitrate in units of bits/s */ int mux_rate; /* bitrate in units of 50 bytes/s */ /* stream info */ int audio_bound; @@ -264,8 +265,7 @@ static int put_system_header(AVFormatContext *ctx, uint8_t *buf,int only_for_str flush_put_bits(&pb); size = put_bits_ptr(&pb) - pb.buf; /* patch packet size */ - buf[4] = (size - 6) >> 8; - buf[5] = (size - 6) & 0xff; + AV_WB16(buf + 4, size - 6); return size; } @@ -320,7 +320,7 @@ static av_cold int mpeg_mux_init(AVFormatContext *ctx) } else s->packet_size = 2048; if (ctx->max_delay < 0) /* Not set by the caller */ - ctx->max_delay = 0; + ctx->max_delay = 0.7*AV_TIME_BASE; s->vcd_padding_bytes_written = 0; s->vcd_padding_bitrate=0; @@ -379,6 +379,10 @@ static av_cold int mpeg_mux_init(AVFormatContext *ctx) av_log(ctx, AV_LOG_WARNING, "VBV buffer size not set, muxing may fail\n"); stream->max_buffer_size = 230*1024; //FIXME this is probably too small as default } + if (stream->max_buffer_size > 1024 * 8191) { + av_log(ctx, AV_LOG_WARNING, "buffer size %d, too large\n", stream->max_buffer_size); + stream->max_buffer_size = 1024 * 8191; + } s->video_bound++; break; case AVMEDIA_TYPE_SUBTITLE: @@ -416,12 +420,18 @@ static av_cold int mpeg_mux_init(AVFormatContext *ctx) video_bitrate += codec_rate; } - if (!s->mux_rate) { + if (s->user_mux_rate) { + s->mux_rate = (s->user_mux_rate + (8 * 50) - 1) / (8 * 50); + } else { /* we increase slightly the bitrate to take into account the headers. XXX: compute it exactly */ bitrate += bitrate / 20; bitrate += 10000; s->mux_rate = (bitrate + (8 * 50) - 1) / (8 * 50); + if (s->mux_rate >= (1<<22)) { + av_log(ctx, AV_LOG_WARNING, "mux rate %d is too large\n", s->mux_rate); + s->mux_rate = (1<<22) - 1; + } } if (s->is_vcd) { @@ -480,7 +490,7 @@ static av_cold int mpeg_mux_init(AVFormatContext *ctx) stream->packet_number = 0; } s->system_header_size = get_system_header_size(ctx); - s->last_scr = 0; + s->last_scr = AV_NOPTS_VALUE; return 0; fail: for(i=0;i<ctx->nb_streams;i++) { @@ -838,7 +848,7 @@ static int flush_packet(AVFormatContext *ctx, int stream_index, /* output data */ assert(payload_size - stuffing_size <= av_fifo_size(stream->fifo)); - av_fifo_generic_read(stream->fifo, ctx->pb, payload_size - stuffing_size, &avio_write); + av_fifo_generic_read(stream->fifo, ctx->pb, payload_size - stuffing_size, (void*)avio_write); stream->bytes_to_iframe -= payload_size - stuffing_size; }else{ payload_size= @@ -903,7 +913,7 @@ static int remove_decoded_packets(AVFormatContext *ctx, int64_t scr){ if(stream->buffer_index < pkt_desc->size || stream->predecode_packet == stream->premux_packet){ av_log(ctx, AV_LOG_ERROR, - "buffer underflow i=%d bufi=%d size=%d\n", + "buffer underflow st=%d bufi=%d size=%d\n", i, stream->buffer_index, pkt_desc->size); break; } @@ -935,7 +945,7 @@ retry: StreamInfo *stream = st->priv_data; const int avail_data= av_fifo_size(stream->fifo); const int space= stream->max_buffer_size - stream->buffer_index; - int rel_space= 1024*space / stream->max_buffer_size; + int rel_space= 1024LL*space / stream->max_buffer_size; PacketDesc *next_pkt= stream->premux_packet; /* for subtitle, a single PES packet must be generated, @@ -945,14 +955,16 @@ retry: return 0; if(avail_data==0) continue; - assert(avail_data>0); + av_assert0(avail_data>0); if(space < s->packet_size && !ignore_constraints) continue; if(next_pkt && next_pkt->dts - scr > max_delay) continue; - + if ( stream->predecode_packet + && stream->predecode_packet->size > stream->buffer_index) + rel_space += 1<<28; if(rel_space > best_score){ best_score= rel_space; best_i = i; @@ -1059,13 +1071,22 @@ static int mpeg_mux_write_packet(AVFormatContext *ctx, AVPacket *pkt) pts= pkt->pts; dts= pkt->dts; - if(pts != AV_NOPTS_VALUE) pts += 2*preload; - if(dts != AV_NOPTS_VALUE){ - if(!s->last_scr) - s->last_scr= dts + preload; - dts += 2*preload; + if (s->last_scr == AV_NOPTS_VALUE) { + if (dts == AV_NOPTS_VALUE || (dts < preload && ctx->avoid_negative_ts) || s->is_dvd) { + if (dts != AV_NOPTS_VALUE) + s->preload += av_rescale(-dts, AV_TIME_BASE, 90000); + s->last_scr = 0; + } else { + s->last_scr = dts - preload; + s->preload = 0; + } + preload = av_rescale(s->preload, 90000, AV_TIME_BASE); + av_log(ctx, AV_LOG_DEBUG, "First SCR: %"PRId64" First DTS: %"PRId64"\n", s->last_scr, dts + preload); } + if (dts != AV_NOPTS_VALUE) dts += preload; + if (pts != AV_NOPTS_VALUE) pts += preload; + av_dlog(ctx, "dts:%f pts:%f flags:%d stream:%d nopts:%d\n", dts / 90000.0, pts / 90000.0, pkt->flags, pkt->stream_index, pts != AV_NOPTS_VALUE); @@ -1133,7 +1154,7 @@ static int mpeg_mux_end(AVFormatContext *ctx) #define OFFSET(x) offsetof(MpegMuxContext, x) #define E AV_OPT_FLAG_ENCODING_PARAM static const AVOption options[] = { - { "muxrate", NULL, OFFSET(mux_rate), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E }, + { "muxrate", NULL, OFFSET(user_mux_rate), AV_OPT_TYPE_INT, {.i64 = 0}, 0, ((1<<22) - 1) * (8 * 50), E }, { "preload", "Initial demux-decode delay in microseconds.", OFFSET(preload), AV_OPT_TYPE_INT, {.i64 = 500000}, 0, INT_MAX, E}, { NULL }, }; diff --git a/libavformat/mpegts.c b/libavformat/mpegts.c index a6a4a0a6fe..c91018ec0f 100644 --- a/libavformat/mpegts.c +++ b/libavformat/mpegts.c @@ -2,20 +2,20 @@ * MPEG2 transport stream (aka DVB) demuxer * Copyright (c) 2002-2003 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -26,6 +26,7 @@ #include "libavutil/dict.h" #include "libavutil/mathematics.h" #include "libavutil/opt.h" +#include "libavutil/avassert.h" #include "libavcodec/bytestream.h" #include "libavcodec/get_bits.h" #include "libavcodec/mathops.h" @@ -52,7 +53,7 @@ enum MpegTSFilterType { typedef struct MpegTSFilter MpegTSFilter; -typedef int PESCallback(MpegTSFilter *f, const uint8_t *buf, int len, int is_start, int64_t pos); +typedef int PESCallback(MpegTSFilter *f, const uint8_t *buf, int len, int is_start, int64_t pos, int64_t cur_pcr); typedef struct MpegTSPESFilter { PESCallback *pes_cb; @@ -98,9 +99,11 @@ struct MpegTSContext { /** raw packet size, including FEC if present */ int raw_packet_size; - int pos47; - /** position corresponding to pos47, or 0 if pos47 invalid */ - int64_t pos; + int size_stat[3]; + int size_stat_count; +#define SIZE_STAT_THRESHOLD 10 + + int64_t pos47_full; /** if true, all pids are analyzed to find streams */ int auto_guess; @@ -108,6 +111,9 @@ struct MpegTSContext { /** compute exact PCR for each transport stream packet */ int mpeg2ts_compute_pcr; + /** fix dvb teletext pts */ + int fix_teletext_pts; + int64_t cur_pcr; /**< used to estimate the exact PCR */ int pcr_incr; /**< used to estimate the exact PCR */ @@ -126,12 +132,14 @@ struct MpegTSContext { unsigned int nb_prg; struct Program *prg; + int8_t crc_validity[NB_PID_MAX]; /** filters for various streams specified by PMT + for the PAT and PMT */ MpegTSFilter *pids[NB_PID_MAX]; + int current_pid; }; -static const AVOption options[] = { +static const AVOption mpegtsraw_options[] = { {"compute_pcr", "Compute exact PCR for each transport stream packet.", offsetof(MpegTSContext, mpeg2ts_compute_pcr), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, AV_OPT_FLAG_DECODING_PARAM }, { NULL }, @@ -140,7 +148,20 @@ static const AVOption options[] = { static const AVClass mpegtsraw_class = { .class_name = "mpegtsraw demuxer", .item_name = av_default_item_name, - .option = options, + .option = mpegtsraw_options, + .version = LIBAVUTIL_VERSION_INT, +}; + +static const AVOption mpegts_options[] = { + {"fix_teletext_pts", "Try to fix pts values of dvb teletext streams.", offsetof(MpegTSContext, fix_teletext_pts), AV_OPT_TYPE_INT, + {.i64 = 1}, 0, 1, AV_OPT_FLAG_DECODING_PARAM }, + { NULL }, +}; + +static const AVClass mpegts_class = { + .class_name = "mpegts demuxer", + .item_name = av_default_item_name, + .option = mpegts_options, .version = LIBAVUTIL_VERSION_INT, }; @@ -179,14 +200,30 @@ typedef struct PESContext { uint8_t header[MAX_PES_HEADER_SIZE]; AVBufferRef *buffer; SLConfigDescr sl; + int64_t last_pcr; } PESContext; extern AVInputFormat ff_mpegts_demuxer; +static void clear_avprogram(MpegTSContext *ts, unsigned int programid) +{ + AVProgram *prg = NULL; + int i; + for(i=0; i<ts->stream->nb_programs; i++) + if(ts->stream->programs[i]->id == programid){ + prg = ts->stream->programs[i]; + break; + } + if (!prg) + return; + prg->nb_stream_indexes = 0; +} + static void clear_program(MpegTSContext *ts, unsigned int programid) { int i; + clear_avprogram(ts, programid); for(i=0; i<ts->nb_prg; i++) if(ts->prg[i].id == programid) ts->prg[i].nb_pids = 0; @@ -229,6 +266,17 @@ static void add_pid_to_pmt(MpegTSContext *ts, unsigned int programid, unsigned i p->pids[p->nb_pids++] = pid; } +static void set_pcr_pid(AVFormatContext *s, unsigned int programid, unsigned int pid) +{ + int i; + for(i=0; i<s->nb_programs; i++) { + if(s->programs[i]->id == programid) { + s->programs[i]->pcr_pid = pid; + break; + } + } +} + /** * @brief discard_pid() decides if the pid is to be discarded according * to caller's programs selection @@ -280,6 +328,7 @@ static int discard_pid(MpegTSContext *ts, unsigned int pid) static void write_section_data(AVFormatContext *s, MpegTSFilter *tss1, const uint8_t *buf, int buf_size, int is_start) { + MpegTSContext *ts = s->priv_data; MpegTSSectionFilter *tss = &tss1->u.section_filter; int len; @@ -307,10 +356,19 @@ static void write_section_data(AVFormatContext *s, MpegTSFilter *tss1, } if (tss->section_h_size != -1 && tss->section_index >= tss->section_h_size) { + int crc_valid = 1; tss->end_of_section_reached = 1; - if (!tss->check_crc || - av_crc(av_crc_get_table(AV_CRC_32_IEEE), -1, - tss->section_buf, tss->section_h_size) == 0) + + if (tss->check_crc){ + crc_valid = !av_crc(av_crc_get_table(AV_CRC_32_IEEE), -1, tss->section_buf, tss->section_h_size); + if (crc_valid){ + ts->crc_validity[ tss1->pid ] = 100; + }else if(ts->crc_validity[ tss1->pid ] > -10){ + ts->crc_validity[ tss1->pid ]--; + }else + crc_valid = 2; + } + if (crc_valid) tss->section_cb(tss1, tss->section_buf, tss->section_h_size); } } @@ -400,7 +458,7 @@ static int analyze(const uint8_t *buf, int size, int packet_size, int *index){ memset(stat, 0, packet_size*sizeof(int)); for(x=i=0; i<size-3; i++){ - if(buf[i] == 0x47 && !(buf[i+1] & 0x80) && (buf[i+3] & 0x30)){ + if(buf[i] == 0x47 && !(buf[i+1] & 0x80) && buf[i+3] != 0x47){ stat[x]++; if(stat[x] > best_score){ best_score= stat[x]; @@ -535,7 +593,11 @@ static const StreamType ISO_types[] = { { 0x04, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_MP3 }, { 0x0f, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AAC }, { 0x10, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG4 }, + /* Makito encoder sets stream type 0x11 for AAC, + * so auto-detect LOAS/LATM instead of hardcoding it. */ +#if !CONFIG_LOAS_DEMUXER { 0x11, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AAC_LATM }, /* LATM syntax */ +#endif { 0x1b, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H264 }, { 0x24, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_HEVC }, { 0x42, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_CAVS }, @@ -552,6 +614,8 @@ static const StreamType HDMV_types[] = { { 0x84, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_EAC3 }, { 0x85, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_DTS }, /* DTS HD */ { 0x86, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_DTS }, /* DTS HD MASTER*/ + { 0xa1, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_EAC3 }, /* E-AC3 Secondary Audio */ + { 0xa2, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_DTS }, /* DTS Express Secondary Audio */ { 0x90, AVMEDIA_TYPE_SUBTITLE, AV_CODEC_ID_HDMV_PGS_SUBTITLE }, { 0 }, }; @@ -570,10 +634,17 @@ static const StreamType REGD_types[] = { { MKTAG('D','T','S','1'), AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_DTS }, { MKTAG('D','T','S','2'), AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_DTS }, { MKTAG('D','T','S','3'), AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_DTS }, + { MKTAG('H','E','V','C'), AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_HEVC }, + { MKTAG('K','L','V','A'), AVMEDIA_TYPE_DATA, AV_CODEC_ID_SMPTE_KLV }, { MKTAG('V','C','-','1'), AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_VC1 }, { 0 }, }; +static const StreamType METADATA_types[] = { + { MKTAG('K','L','V','A'), AVMEDIA_TYPE_DATA, AV_CODEC_ID_SMPTE_KLV }, + { 0 }, +}; + /* descriptor present */ static const StreamType DESC_types[] = { { 0x6a, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AC3 }, /* AC-3 descriptor */ @@ -587,10 +658,16 @@ static const StreamType DESC_types[] = { static void mpegts_find_stream_type(AVStream *st, uint32_t stream_type, const StreamType *types) { + if (avcodec_is_open(st->codec)) { + av_log(NULL, AV_LOG_DEBUG, "cannot set stream info, codec is open\n"); + return; + } + for (; types->stream_type; types++) { if (stream_type == types->stream_type) { st->codec->codec_type = types->codec_type; st->codec->codec_id = types->codec_id; + st->request_probe = 0; return; } } @@ -599,6 +676,14 @@ static void mpegts_find_stream_type(AVStream *st, static int mpegts_set_stream_info(AVStream *st, PESContext *pes, uint32_t stream_type, uint32_t prog_reg_desc) { + int old_codec_type= st->codec->codec_type; + int old_codec_id = st->codec->codec_id; + + if (avcodec_is_open(st->codec)) { + av_log(pes->stream, AV_LOG_DEBUG, "cannot set stream info, codec is open\n"); + return 0; + } + avpriv_set_pts_info(st, 33, 1, 90000); st->priv_data = pes; st->codec->codec_type = AVMEDIA_TYPE_DATA; @@ -614,7 +699,8 @@ static int mpegts_set_stream_info(AVStream *st, PESContext *pes, st->codec->codec_tag = pes->stream_type; mpegts_find_stream_type(st, pes->stream_type, ISO_types); - if (prog_reg_desc == AV_RL32("HDMV") && + if ((prog_reg_desc == AV_RL32("HDMV") || + prog_reg_desc == AV_RL32("HDPR")) && st->codec->codec_id == AV_CODEC_ID_NONE) { mpegts_find_stream_type(st, pes->stream_type, HDMV_types); if (pes->stream_type == 0x83) { @@ -644,6 +730,10 @@ static int mpegts_set_stream_info(AVStream *st, PESContext *pes, } if (st->codec->codec_id == AV_CODEC_ID_NONE) mpegts_find_stream_type(st, pes->stream_type, MISC_types); + if (st->codec->codec_id == AV_CODEC_ID_NONE){ + st->codec->codec_id = old_codec_id; + st->codec->codec_type= old_codec_type; + } return 0; } @@ -682,6 +772,13 @@ static void new_pes_packet(PESContext *pes, AVPacket *pkt) pes->flags = 0; } +static uint64_t get_ts64(GetBitContext *gb, int bits) +{ + if (get_bits_left(gb) < bits) + return AV_NOPTS_VALUE; + return get_bits64(gb, bits); +} + static int read_sl_header(PESContext *pes, SLConfigDescr *sl, const uint8_t *buf, int buf_size) { GetBitContext gb; @@ -689,6 +786,7 @@ static int read_sl_header(PESContext *pes, SLConfigDescr *sl, const uint8_t *buf int padding_flag = 0, padding_bits = 0, inst_bitrate_flag = 0; int dts_flag = -1, cts_flag = -1; int64_t dts = AV_NOPTS_VALUE, cts = AV_NOPTS_VALUE; + init_get_bits(&gb, buf, buf_size*8); if (sl->use_au_start) @@ -727,9 +825,9 @@ static int read_sl_header(PESContext *pes, SLConfigDescr *sl, const uint8_t *buf if (sl->inst_bitrate_len) inst_bitrate_flag = get_bits1(&gb); if (dts_flag == 1) - dts = get_bits64(&gb, sl->timestamp_len); + dts = get_ts64(&gb, sl->timestamp_len); if (cts_flag == 1) - cts = get_bits64(&gb, sl->timestamp_len); + cts = get_ts64(&gb, sl->timestamp_len); if (sl->au_len > 0) skip_bits_long(&gb, sl->au_len); if (inst_bitrate_flag) @@ -750,7 +848,7 @@ static int read_sl_header(PESContext *pes, SLConfigDescr *sl, const uint8_t *buf /* return non zero if a packet could be constructed */ static int mpegts_push_data(MpegTSFilter *filter, const uint8_t *buf, int buf_size, int is_start, - int64_t pos) + int64_t pos, int64_t pcr) { PESContext *pes = filter->u.pes_filter.opaque; MpegTSContext *ts = pes->ts; @@ -760,6 +858,9 @@ static int mpegts_push_data(MpegTSFilter *filter, if(!ts->pkt) return 0; + if (pcr != -1) + pes->last_pcr = pcr; + if (is_start) { if (pes->state == MPEGTS_PAYLOAD && pes->data_index > 0) { new_pes_packet(pes, ts->pkt); @@ -820,10 +921,10 @@ static int mpegts_push_data(MpegTSFilter *filter, code != 0x1ff && code != 0x1f2 && /* program_stream_directory, DSMCC_stream */ code != 0x1f8) { /* ITU-T Rec. H.222.1 type E stream */ pes->state = MPEGTS_PESHEADER; - if (pes->st->codec->codec_id == AV_CODEC_ID_NONE) { + if (pes->st->codec->codec_id == AV_CODEC_ID_NONE && !pes->st->request_probe) { av_dlog(pes->stream, "pid=%x stream_type=%x probing\n", pes->pid, pes->stream_type); - pes->st->codec->codec_id = AV_CODEC_ID_PROBE; + pes->st->request_probe= 1; } } else { pes->state = MPEGTS_PAYLOAD; @@ -906,6 +1007,38 @@ static int mpegts_push_data(MpegTSFilter *filter, p += sl_header_bytes; buf_size -= sl_header_bytes; } + if (pes->stream_type == 0x15 && buf_size >= 5) { + /* skip metadata access unit header */ + pes->pes_header_size += 5; + p += 5; + buf_size -= 5; + } + if (pes->ts->fix_teletext_pts && pes->st->codec->codec_id == AV_CODEC_ID_DVB_TELETEXT) { + AVProgram *p = NULL; + while ((p = av_find_program_from_stream(pes->stream, p, pes->st->index))) { + if (p->pcr_pid != -1 && p->discard != AVDISCARD_ALL) { + MpegTSFilter *f = pes->ts->pids[p->pcr_pid]; + if (f && f->type == MPEGTS_PES) { + PESContext *pcrpes = f->u.pes_filter.opaque; + if (pcrpes && pcrpes->last_pcr != -1 && pcrpes->st && pcrpes->st->discard != AVDISCARD_ALL) { + // teletext packets do not always have correct timestamps, + // the standard says they should be handled after 40.6 ms at most, + // and the pcr error to this packet should be no more than 100 ms. + // TODO: we should interpolate the PCR, not just use the last one + int64_t pcr = pcrpes->last_pcr / 300; + pes->st->pts_wrap_reference = pcrpes->st->pts_wrap_reference; + pes->st->pts_wrap_behavior = pcrpes->st->pts_wrap_behavior; + if (pes->dts == AV_NOPTS_VALUE || pes->dts < pcr) { + pes->pts = pes->dts = pcr; + } else if (pes->dts > pcr + 3654 + 9000) { + pes->pts = pes->dts = pcr + 3654 + 9000; + } + break; + } + } + } + } + } } break; case MPEGTS_PAYLOAD: @@ -962,6 +1095,7 @@ static PESContext *add_pes_stream(MpegTSContext *ts, int pid, int pcr_pid) pes->state = MPEGTS_SKIP; pes->pts = AV_NOPTS_VALUE; pes->dts = AV_NOPTS_VALUE; + pes->last_pcr = -1; tss = mpegts_open_pes_filter(ts, pid, mpegts_push_data, pes); if (!tss) { av_free(pes); @@ -1293,15 +1427,17 @@ int ff_parse_mpeg2_descriptor(AVFormatContext *fc, AVStream *st, int stream_type break; case 0x1F: /* FMC descriptor */ get16(pp, desc_end); - if (mp4_descr_count > 0 && st->codec->codec_id == AV_CODEC_ID_AAC_LATM && + if (mp4_descr_count > 0 && (st->codec->codec_id == AV_CODEC_ID_AAC_LATM || st->request_probe>0) && mp4_descr->dec_config_descr_len && mp4_descr->es_id == pid) { AVIOContext pb; ffio_init_context(&pb, mp4_descr->dec_config_descr, mp4_descr->dec_config_descr_len, 0, NULL, NULL, NULL, NULL); ff_mp4_read_dec_config_descr(fc, st, &pb); if (st->codec->codec_id == AV_CODEC_ID_AAC && - st->codec->extradata_size > 0) - st->need_parsing = 0; + st->codec->extradata_size > 0){ + st->request_probe= st->need_parsing = 0; + st->codec->codec_type= AVMEDIA_TYPE_AUDIO; + } } break; case 0x56: /* DVB teletext descriptor */ @@ -1331,9 +1467,7 @@ int ff_parse_mpeg2_descriptor(AVFormatContext *fc, AVStream *st, int stream_type if (st->codec->extradata_size == 4 && memcmp(st->codec->extradata, *pp, 4)) avpriv_request_sample(fc, "DVB sub with multiple IDs"); } else { - st->codec->extradata = av_malloc(4 + FF_INPUT_BUFFER_PADDING_SIZE); - if (st->codec->extradata) { - st->codec->extradata_size = 4; + if (!ff_alloc_extradata(st->codec, 4)) { memcpy(st->codec->extradata, *pp, 4); } } @@ -1363,6 +1497,18 @@ int ff_parse_mpeg2_descriptor(AVFormatContext *fc, AVStream *st, int stream_type if (st->codec->codec_id == AV_CODEC_ID_NONE) mpegts_find_stream_type(st, st->codec->codec_tag, REGD_types); break; + case 0x52: /* stream identifier descriptor */ + st->stream_identifier = 1 + get8(pp, desc_end); + break; + case 0x26: /* metadata descriptor */ + if (get16(pp, desc_end) == 0xFFFF) + *pp += 4; + if (get8(pp, desc_end) == 0xFF) { + st->codec->codec_tag = bytestream_get_le32(pp); + if (st->codec->codec_id == AV_CODEC_ID_NONE) + mpegts_find_stream_type(st, st->codec->codec_tag, METADATA_types); + } + break; default: break; } @@ -1405,6 +1551,7 @@ static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len return; pcr_pid &= 0x1fff; add_pid_to_pmt(ts, h->id, pcr_pid); + set_pcr_pid(ts->stream, h->id, pcr_pid); av_dlog(ts->stream, "pcr_pid=0x%x\n", pcr_pid); @@ -1441,7 +1588,7 @@ static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len // stop parsing after pmt, we found header if (!ts->stream->nb_streams) - ts->stop_parse = 1; + ts->stop_parse = 2; for(;;) { st = 0; @@ -1453,12 +1600,16 @@ static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len if (pid < 0) break; pid &= 0x1fff; + if (pid == ts->current_pid) + break; /* now create stream */ if (ts->pids[pid] && ts->pids[pid]->type == MPEGTS_PES) { pes = ts->pids[pid]->u.pes_filter.opaque; if (!pes->st) { pes->st = avformat_new_stream(pes->stream, NULL); + if (!pes->st) + goto out; pes->st->id = pes->pid; } st = pes->st; @@ -1467,6 +1618,8 @@ static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len pes = add_pes_stream(ts, pid, pcr_pid); if (pes) { st = avformat_new_stream(pes->stream, NULL); + if (!st) + goto out; st->id = pes->pid; } } else { @@ -1475,6 +1628,8 @@ static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len st = ts->stream->streams[idx]; } else { st = avformat_new_stream(ts->stream, NULL); + if (!st) + goto out; st->id = pid; st->codec->codec_type = AVMEDIA_TYPE_DATA; } @@ -1521,6 +1676,7 @@ static void pat_cb(MpegTSFilter *filter, const uint8_t *section, int section_len SectionHeader h1, *h = &h1; const uint8_t *p, *p_end; int sid, pmt_pid; + AVProgram *program; av_dlog(ts->stream, "PAT:\n"); hex_dump_debug(ts->stream, section, section_len); @@ -1532,6 +1688,8 @@ static void pat_cb(MpegTSFilter *filter, const uint8_t *section, int section_len if (h->tid != PAT_TID) return; + ts->stream->ts_id = h->id; + clear_programs(ts); for(;;) { sid = get16(&p, p_end); @@ -1542,20 +1700,42 @@ static void pat_cb(MpegTSFilter *filter, const uint8_t *section, int section_len break; pmt_pid &= 0x1fff; + if (pmt_pid == ts->current_pid) + break; + av_dlog(ts->stream, "sid=0x%x pid=0x%x\n", sid, pmt_pid); if (sid == 0x0000) { /* NIT info */ } else { - av_new_program(ts->stream, sid); - if (ts->pids[pmt_pid]) - mpegts_close_filter(ts, ts->pids[pmt_pid]); - mpegts_open_section_filter(ts, pmt_pid, pmt_cb, ts, 1); + MpegTSFilter *fil = ts->pids[pmt_pid]; + program = av_new_program(ts->stream, sid); + program->program_num = sid; + program->pmt_pid = pmt_pid; + if (fil) + if ( fil->type != MPEGTS_SECTION + || fil->pid != pmt_pid + || fil->u.section_filter.section_cb != pmt_cb) + mpegts_close_filter(ts, ts->pids[pmt_pid]); + + if (!ts->pids[pmt_pid]) + mpegts_open_section_filter(ts, pmt_pid, pmt_cb, ts, 1); add_pat_entry(ts, sid); add_pid_to_pmt(ts, sid, 0); //add pat pid to program add_pid_to_pmt(ts, sid, pmt_pid); } } + + if (sid < 0) { + int i,j; + for (j=0; j<ts->stream->nb_programs; j++) { + for (i=0; i<ts->nb_prg; i++) + if (ts->prg[i].id == ts->stream->programs[j]->id) + break; + if (i==ts->nb_prg) + clear_avprogram(ts, ts->stream->programs[j]->id); + } + } } static void sdt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len) @@ -1635,6 +1815,9 @@ static void sdt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len } } +static int parse_pcr(int64_t *ppcr_high, int *ppcr_low, + const uint8_t *packet); + /* handle one TS packet */ static int handle_packet(MpegTSContext *ts, const uint8_t *packet) { @@ -1656,6 +1839,7 @@ static int handle_packet(MpegTSContext *ts, const uint8_t *packet) } if (!tss) return 0; + ts->current_pid = pid; afc = (packet[3] >> 4) & 3; if (afc == 0) /* reserved value */ @@ -1676,7 +1860,7 @@ static int handle_packet(MpegTSContext *ts, const uint8_t *packet) tss->last_cc = cc; if (!cc_ok) { - av_log(ts->stream, AV_LOG_WARNING, + av_log(ts->stream, AV_LOG_DEBUG, "Continuity check failed for pid %d expected %d got %d\n", pid, expected_cc, cc); if(tss->type == MPEGTS_PES) { @@ -1698,7 +1882,10 @@ static int handle_packet(MpegTSContext *ts, const uint8_t *packet) return 0; pos = avio_tell(ts->stream->pb); - MOD_UNLIKELY(ts->pos47, pos, ts->raw_packet_size, ts->pos); + if (pos >= 0) { + av_assert0(pos >= TS_PACKET_SIZE); + ts->pos47_full = pos - TS_PACKET_SIZE; + } if (tss->type == MPEGTS_SECTION) { if (is_start) { @@ -1727,15 +1914,53 @@ static int handle_packet(MpegTSContext *ts, const uint8_t *packet) } } else { int ret; + int64_t pcr = -1; + int64_t pcr_h; + int pcr_l; + if (parse_pcr(&pcr_h, &pcr_l, packet) == 0) + pcr = pcr_h * 300 + pcr_l; // Note: The position here points actually behind the current packet. if ((ret = tss->u.pes_filter.pes_cb(tss, p, p_end - p, is_start, - pos - ts->raw_packet_size)) < 0) + pos - ts->raw_packet_size, pcr)) < 0) return ret; } return 0; } +static void reanalyze(MpegTSContext *ts) { + AVIOContext *pb = ts->stream->pb; + int64_t pos = avio_tell(pb); + if(pos < 0) + return; + pos -= ts->pos47_full; + if (pos == TS_PACKET_SIZE) { + ts->size_stat[0] ++; + } else if (pos == TS_DVHS_PACKET_SIZE) { + ts->size_stat[1] ++; + } else if (pos == TS_FEC_PACKET_SIZE) { + ts->size_stat[2] ++; + } + + ts->size_stat_count ++; + if(ts->size_stat_count > SIZE_STAT_THRESHOLD) { + int newsize = 0; + if (ts->size_stat[0] > SIZE_STAT_THRESHOLD) { + newsize = TS_PACKET_SIZE; + } else if (ts->size_stat[1] > SIZE_STAT_THRESHOLD) { + newsize = TS_DVHS_PACKET_SIZE; + } else if (ts->size_stat[2] > SIZE_STAT_THRESHOLD) { + newsize = TS_FEC_PACKET_SIZE; + } + if (newsize && newsize != ts->raw_packet_size) { + av_log(ts->stream, AV_LOG_WARNING, "changing packet size to %d\n", newsize); + ts->raw_packet_size = newsize; + } + ts->size_stat_count = 0; + memset(ts->size_stat, 0, sizeof(ts->size_stat)); + } +} + /* XXX: try to find a better synchro over several packets (use get_packet_size() ?) */ static int mpegts_resync(AVFormatContext *s) @@ -1745,10 +1970,11 @@ static int mpegts_resync(AVFormatContext *s) for(i = 0;i < MAX_RESYNC_SIZE; i++) { c = avio_r8(pb); - if (pb->eof_reached) + if (url_feof(pb)) return -1; if (c == 0x47) { avio_seek(pb, -1, SEEK_CUR); + reanalyze(s->priv_data); return 0; } } @@ -1770,7 +1996,7 @@ static int read_packet(AVFormatContext *s, uint8_t *buf, int raw_packet_size, co /* check packet sync byte */ if ((*data)[0] != 0x47) { /* find a new packet start */ - avio_seek(pb, -TS_PACKET_SIZE, SEEK_CUR); + avio_seek(pb, -raw_packet_size, SEEK_CUR); if (mpegts_resync(s) < 0) return AVERROR(EAGAIN); else @@ -1793,7 +2019,7 @@ static void finished_reading_packet(AVFormatContext *s, int raw_packet_size) static int handle_packets(MpegTSContext *ts, int nb_packets) { AVFormatContext *s = ts->stream; - uint8_t packet[TS_PACKET_SIZE+FF_INPUT_BUFFER_PADDING_SIZE]; + uint8_t packet[TS_PACKET_SIZE + FF_INPUT_BUFFER_PADDING_SIZE]; const uint8_t *data; int packet_num, ret = 0; @@ -1808,6 +2034,7 @@ static int handle_packets(MpegTSContext *ts, int nb_packets) av_buffer_unref(&pes->buffer); pes->data_index = 0; pes->state = MPEGTS_SKIP; /* skip until pes header */ + pes->last_pcr = -1; } ts->pids[i]->last_cc = -1; } @@ -1818,11 +2045,15 @@ static int handle_packets(MpegTSContext *ts, int nb_packets) packet_num = 0; memset(packet + TS_PACKET_SIZE, 0, FF_INPUT_BUFFER_PADDING_SIZE); for(;;) { - if (ts->stop_parse>0) - break; packet_num++; - if (nb_packets != 0 && packet_num >= nb_packets) + if (nb_packets != 0 && packet_num >= nb_packets || + ts->stop_parse > 1) { + ret = AVERROR(EAGAIN); + break; + } + if (ts->stop_parse > 0) break; + ret = read_packet(s, packet, ts->raw_packet_size, &data); if (ret != 0) break; @@ -1838,24 +2069,34 @@ static int handle_packets(MpegTSContext *ts, int nb_packets) static int mpegts_probe(AVProbeData *p) { const int size= p->buf_size; - int score, fec_score, dvhs_score; + int maxscore=0; + int sumscore=0; + int i; int check_count= size / TS_FEC_PACKET_SIZE; #define CHECK_COUNT 10 +#define CHECK_BLOCK 100 if (check_count < CHECK_COUNT) return -1; - score = analyze(p->buf, TS_PACKET_SIZE *check_count, TS_PACKET_SIZE , NULL)*CHECK_COUNT/check_count; - dvhs_score= analyze(p->buf, TS_DVHS_PACKET_SIZE*check_count, TS_DVHS_PACKET_SIZE, NULL)*CHECK_COUNT/check_count; - fec_score = analyze(p->buf, TS_FEC_PACKET_SIZE *check_count, TS_FEC_PACKET_SIZE , NULL)*CHECK_COUNT/check_count; - av_dlog(NULL, "score: %d, dvhs_score: %d, fec_score: %d \n", - score, dvhs_score, fec_score); + for (i=0; i<check_count; i+=CHECK_BLOCK){ + int left = FFMIN(check_count - i, CHECK_BLOCK); + int score = analyze(p->buf + TS_PACKET_SIZE *i, TS_PACKET_SIZE *left, TS_PACKET_SIZE , NULL); + int dvhs_score= analyze(p->buf + TS_DVHS_PACKET_SIZE*i, TS_DVHS_PACKET_SIZE*left, TS_DVHS_PACKET_SIZE, NULL); + int fec_score = analyze(p->buf + TS_FEC_PACKET_SIZE *i, TS_FEC_PACKET_SIZE *left, TS_FEC_PACKET_SIZE , NULL); + score = FFMAX3(score, dvhs_score, fec_score); + sumscore += score; + maxscore = FFMAX(maxscore, score); + } + + sumscore = sumscore*CHECK_COUNT/check_count; + maxscore = maxscore*CHECK_COUNT/CHECK_BLOCK; -// we need a clear definition for the returned score otherwise things will become messy sooner or later - if (score > fec_score && score > dvhs_score && score > 6) return AVPROBE_SCORE_MAX + score - CHECK_COUNT; - else if(dvhs_score > score && dvhs_score > fec_score && dvhs_score > 6) return AVPROBE_SCORE_MAX + dvhs_score - CHECK_COUNT; - else if( fec_score > 6) return AVPROBE_SCORE_MAX + fec_score - CHECK_COUNT; - else return -1; + av_dlog(0, "TS score: %d %d\n", sumscore, maxscore); + + if (sumscore > 6) return AVPROBE_SCORE_MAX + sumscore - CHECK_COUNT; + else if (maxscore > 6) return AVPROBE_SCORE_MAX/2 + sumscore - CHECK_COUNT; + else return -1; } /* return the 90kHz PCR and the extension for the 27MHz PCR. return @@ -1887,22 +2128,33 @@ static int parse_pcr(int64_t *ppcr_high, int *ppcr_low, return 0; } +static void seek_back(AVFormatContext *s, AVIOContext *pb, int64_t pos) { + + /* NOTE: We attempt to seek on non-seekable files as well, as the + * probe buffer usually is big enough. Only warn if the seek failed + * on files where the seek should work. */ + if (avio_seek(pb, pos, SEEK_SET) < 0) + av_log(s, pb->seekable ? AV_LOG_ERROR : AV_LOG_INFO, "Unable to seek back to the start\n"); +} + static int mpegts_read_header(AVFormatContext *s) { MpegTSContext *ts = s->priv_data; AVIOContext *pb = s->pb; - uint8_t buf[5*1024]; + uint8_t buf[8*1024]={0}; int len; int64_t pos; - /* read the first 1024 bytes to get packet size */ + ffio_ensure_seekback(pb, s->probesize); + + /* read the first 8192 bytes to get packet size */ pos = avio_tell(pb); len = avio_read(pb, buf, sizeof(buf)); - if (len != sizeof(buf)) - goto fail; - ts->raw_packet_size = get_packet_size(buf, sizeof(buf)); - if (ts->raw_packet_size <= 0) - goto fail; + ts->raw_packet_size = get_packet_size(buf, len); + if (ts->raw_packet_size <= 0) { + av_log(s, AV_LOG_WARNING, "Could not detect TS packet size, defaulting to non-FEC/DVHS\n"); + ts->raw_packet_size = TS_PACKET_SIZE; + } ts->stream = s; ts->auto_guess = 0; @@ -1910,8 +2162,7 @@ static int mpegts_read_header(AVFormatContext *s) /* normal demux */ /* first do a scan to get all the services */ - if (avio_seek(pb, pos, SEEK_SET) < 0 && pb->seekable) - av_log(s, AV_LOG_ERROR, "Unable to seek back to the start\n"); + seek_back(s, pb, pos); mpegts_open_section_filter(ts, SDT_PID, sdt_cb, ts, 1); @@ -1949,7 +2200,7 @@ static int mpegts_read_header(AVFormatContext *s) for(;;) { ret = read_packet(s, packet, ts->raw_packet_size, &data); if (ret < 0) - return -1; + goto fail; pid = AV_RB16(data + 1) & 0x1fff; if ((pcr_pid == -1 || pcr_pid == pid) && parse_pcr(&pcr_h, &pcr_l, data) == 0) { @@ -1977,7 +2228,7 @@ static int mpegts_read_header(AVFormatContext *s) st->start_time / 1000000.0, pcrs[0] / 27e6, ts->pcr_incr); } - avio_seek(pb, pos, SEEK_SET); + seek_back(s, pb, pos); return 0; fail: return -1; @@ -2043,6 +2294,7 @@ static int mpegts_read_packet(AVFormatContext *s, ts->pkt = pkt; ret = handle_packets(ts, 0); if (ret < 0) { + av_free_packet(ts->pkt); /* flush pes data left */ for (i = 0; i < NB_PID_MAX; i++) { if (ts->pids[i] && ts->pids[i]->type == MPEGTS_PES) { @@ -2079,66 +2331,68 @@ static int mpegts_read_close(AVFormatContext *s) return 0; } -static int64_t mpegts_get_pcr(AVFormatContext *s, int stream_index, +static av_unused int64_t mpegts_get_pcr(AVFormatContext *s, int stream_index, int64_t *ppos, int64_t pos_limit) { MpegTSContext *ts = s->priv_data; int64_t pos, timestamp; uint8_t buf[TS_PACKET_SIZE]; int pcr_l, pcr_pid = ((PESContext*)s->streams[stream_index]->priv_data)->pcr_pid; - const int find_next= 1; - pos = ((*ppos + ts->raw_packet_size - 1 - ts->pos47) / ts->raw_packet_size) * ts->raw_packet_size + ts->pos47; - if (find_next) { - for(;;) { - avio_seek(s->pb, pos, SEEK_SET); - if (avio_read(s->pb, buf, TS_PACKET_SIZE) != TS_PACKET_SIZE) + int pos47 = ts->pos47_full % ts->raw_packet_size; + pos = ((*ppos + ts->raw_packet_size - 1 - pos47) / ts->raw_packet_size) * ts->raw_packet_size + pos47; + while(pos < pos_limit) { + if (avio_seek(s->pb, pos, SEEK_SET) < 0) + return AV_NOPTS_VALUE; + if (avio_read(s->pb, buf, TS_PACKET_SIZE) != TS_PACKET_SIZE) + return AV_NOPTS_VALUE; + if (buf[0] != 0x47) { + avio_seek(s->pb, -TS_PACKET_SIZE, SEEK_CUR); + if (mpegts_resync(s) < 0) return AV_NOPTS_VALUE; - if ((pcr_pid < 0 || (AV_RB16(buf + 1) & 0x1fff) == pcr_pid) && - parse_pcr(×tamp, &pcr_l, buf) == 0) { - break; - } - pos += ts->raw_packet_size; + pos = avio_tell(s->pb); + continue; } - } else { - for(;;) { - pos -= ts->raw_packet_size; - if (pos < 0) - return AV_NOPTS_VALUE; - avio_seek(s->pb, pos, SEEK_SET); - if (avio_read(s->pb, buf, TS_PACKET_SIZE) != TS_PACKET_SIZE) - return AV_NOPTS_VALUE; - if ((pcr_pid < 0 || (AV_RB16(buf + 1) & 0x1fff) == pcr_pid) && - parse_pcr(×tamp, &pcr_l, buf) == 0) { - break; - } + if ((pcr_pid < 0 || (AV_RB16(buf + 1) & 0x1fff) == pcr_pid) && + parse_pcr(×tamp, &pcr_l, buf) == 0) { + *ppos = pos; + return timestamp; } + pos += ts->raw_packet_size; } - *ppos = pos; - return timestamp; + return AV_NOPTS_VALUE; } -static int read_seek(AVFormatContext *s, int stream_index, int64_t target_ts, int flags){ +static int64_t mpegts_get_dts(AVFormatContext *s, int stream_index, + int64_t *ppos, int64_t pos_limit) +{ MpegTSContext *ts = s->priv_data; - uint8_t buf[TS_PACKET_SIZE]; int64_t pos; - - if (ff_seek_frame_binary(s, stream_index, target_ts, flags) < 0) - return -1; - - pos= avio_tell(s->pb); - - for(;;) { - avio_seek(s->pb, pos, SEEK_SET); - if (avio_read(s->pb, buf, TS_PACKET_SIZE) != TS_PACKET_SIZE) - return -1; -// pid = AV_RB16(buf + 1) & 0x1fff; - if(buf[1] & 0x40) break; - pos += ts->raw_packet_size; + int pos47 = ts->pos47_full % ts->raw_packet_size; + pos = ((*ppos + ts->raw_packet_size - 1 - pos47) / ts->raw_packet_size) * ts->raw_packet_size + pos47; + ff_read_frame_flush(s); + if (avio_seek(s->pb, pos, SEEK_SET) < 0) + return AV_NOPTS_VALUE; + while(pos < pos_limit) { + int ret; + AVPacket pkt; + av_init_packet(&pkt); + ret= av_read_frame(s, &pkt); + if(ret < 0) + return AV_NOPTS_VALUE; + av_free_packet(&pkt); + if(pkt.dts != AV_NOPTS_VALUE && pkt.pos >= 0){ + ff_reduce_index(s, pkt.stream_index); + av_add_index_entry(s->streams[pkt.stream_index], pkt.pos, pkt.dts, 0, 0, AVINDEX_KEYFRAME /* FIXME keyframe? */); + if(pkt.stream_index == stream_index && pkt.pos >= *ppos){ + *ppos= pkt.pos; + return pkt.dts; + } + } + pos = pkt.pos; } - avio_seek(s->pb, pos, SEEK_SET); - return 0; + return AV_NOPTS_VALUE; } /**************************************************************/ @@ -2155,6 +2409,9 @@ MpegTSContext *ff_mpegts_parse_open(AVFormatContext *s) ts->raw_packet_size = TS_PACKET_SIZE; ts->stream = s; ts->auto_guess = 1; + mpegts_open_section_filter(ts, SDT_PID, sdt_cb, ts, 1); + mpegts_open_section_filter(ts, PAT_PID, pat_cb, ts, 1); + return ts; } @@ -2167,10 +2424,8 @@ int ff_mpegts_parse_packet(MpegTSContext *ts, AVPacket *pkt, len1 = len; ts->pkt = pkt; - ts->stop_parse = 0; for(;;) { - if (ts->stop_parse>0) - break; + ts->stop_parse = 0; if (len < TS_PACKET_SIZE) return -1; if (buf[0] != 0x47) { @@ -2180,6 +2435,8 @@ int ff_mpegts_parse_packet(MpegTSContext *ts, AVPacket *pkt, handle_packet(ts, buf); buf += TS_PACKET_SIZE; len -= TS_PACKET_SIZE; + if (ts->stop_parse == 1) + break; } } return len1 - len; @@ -2199,9 +2456,9 @@ AVInputFormat ff_mpegts_demuxer = { .read_header = mpegts_read_header, .read_packet = mpegts_read_packet, .read_close = mpegts_read_close, - .read_seek = read_seek, - .read_timestamp = mpegts_get_pcr, + .read_timestamp = mpegts_get_dts, .flags = AVFMT_SHOW_IDS | AVFMT_TS_DISCONT, + .priv_class = &mpegts_class, }; AVInputFormat ff_mpegtsraw_demuxer = { @@ -2211,8 +2468,7 @@ AVInputFormat ff_mpegtsraw_demuxer = { .read_header = mpegts_read_header, .read_packet = mpegts_raw_read_packet, .read_close = mpegts_read_close, - .read_seek = read_seek, - .read_timestamp = mpegts_get_pcr, + .read_timestamp = mpegts_get_dts, .flags = AVFMT_SHOW_IDS | AVFMT_TS_DISCONT, .priv_class = &mpegtsraw_class, }; diff --git a/libavformat/mpegts.h b/libavformat/mpegts.h index fafe98fcf8..269c23b925 100644 --- a/libavformat/mpegts.h +++ b/libavformat/mpegts.h @@ -2,20 +2,20 @@ * MPEG2 transport stream defines * Copyright (c) 2003 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/mpegtsenc.c b/libavformat/mpegtsenc.c index 8efd93ef31..1d51b97bdf 100644 --- a/libavformat/mpegtsenc.c +++ b/libavformat/mpegtsenc.c @@ -2,20 +2,20 @@ * MPEG2 transport stream (aka DVB) muxer * Copyright (c) 2003 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -25,6 +25,7 @@ #include "libavutil/intreadwrite.h" #include "libavutil/mathematics.h" #include "libavutil/opt.h" +#include "libavutil/avassert.h" #include "libavcodec/internal.h" #include "avformat.h" #include "internal.h" @@ -76,12 +77,15 @@ typedef struct MpegTSWrite { int pmt_start_pid; int start_pid; + int m2ts_mode; int reemit_pat_pmt; // backward compatibility #define MPEGTS_FLAG_REEMIT_PAT_PMT 0x01 #define MPEGTS_FLAG_AAC_LATM 0x02 int flags; + int copyts; + int tables_version; } MpegTSWrite; /* a PES packet header is generated every DEFAULT_PES_HEADER_FREQ packets */ @@ -96,9 +100,12 @@ static const AVOption options[] = { { "mpegts_service_id", "Set service_id field.", offsetof(MpegTSWrite, service_id), AV_OPT_TYPE_INT, {.i64 = 0x0001 }, 0x0001, 0xffff, AV_OPT_FLAG_ENCODING_PARAM}, { "mpegts_pmt_start_pid", "Set the first pid of the PMT.", - offsetof(MpegTSWrite, pmt_start_pid), AV_OPT_TYPE_INT, {.i64 = 0x1000 }, 0x1000, 0x1f00, AV_OPT_FLAG_ENCODING_PARAM}, + offsetof(MpegTSWrite, pmt_start_pid), AV_OPT_TYPE_INT, {.i64 = 0x1000 }, 0x0010, 0x1f00, AV_OPT_FLAG_ENCODING_PARAM}, { "mpegts_start_pid", "Set the first pid.", offsetof(MpegTSWrite, start_pid), AV_OPT_TYPE_INT, {.i64 = 0x0100 }, 0x0100, 0x0f00, AV_OPT_FLAG_ENCODING_PARAM}, + {"mpegts_m2ts_mode", "Enable m2ts mode.", + offsetof(MpegTSWrite, m2ts_mode), AV_OPT_TYPE_INT, {.i64 = -1 }, + -1,1, AV_OPT_FLAG_ENCODING_PARAM}, { "muxrate", NULL, offsetof(MpegTSWrite, mux_rate), AV_OPT_TYPE_INT, {.i64 = 1}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM}, { "pes_payload_size", "Minimum PES packet payload in bytes", offsetof(MpegTSWrite, pes_payload_size), AV_OPT_TYPE_INT, {.i64 = DEFAULT_PES_PAYLOAD_SIZE}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM}, @@ -113,6 +120,10 @@ static const AVOption options[] = { // backward compatibility { "resend_headers", "Reemit PAT/PMT before writing the next packet", offsetof(MpegTSWrite, reemit_pat_pmt), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM}, + { "mpegts_copyts", "don't offset dts/pts", + offsetof(MpegTSWrite, copyts), AV_OPT_TYPE_INT, {.i64=-1}, -1, 1, AV_OPT_FLAG_ENCODING_PARAM}, + { "tables_version", "set PAT, PMT and SDT version", + offsetof(MpegTSWrite, tables_version), AV_OPT_TYPE_INT, {.i64=0}, 0, 31, AV_OPT_FLAG_ENCODING_PARAM}, { NULL }, }; @@ -191,7 +202,7 @@ static int mpegts_write_section1(MpegTSSection *s, int tid, int id, tot_len = 3 + 5 + len + 4; /* check if not too big */ if (tot_len > 1024) - return -1; + return AVERROR_INVALIDDATA; q = section; *q++ = tid; @@ -209,7 +220,7 @@ static int mpegts_write_section1(MpegTSSection *s, int tid, int id, /*********************************************/ /* mpegts writer */ -#define DEFAULT_PROVIDER_NAME "Libav" +#define DEFAULT_PROVIDER_NAME "FFmpeg" #define DEFAULT_SERVICE_NAME "Service01" /* we retransmit the SI info at this rate */ @@ -223,6 +234,7 @@ typedef struct MpegTSWriteStream { int cc; int payload_size; int first_pts_check; ///< first pts check needed + int prev_payload_key; int64_t payload_pts; int64_t payload_dts; int payload_flags; @@ -243,7 +255,7 @@ static void mpegts_write_pat(AVFormatContext *s) put16(&q, service->sid); put16(&q, 0xe000 | service->pmt.pid); } - mpegts_write_section1(&ts->pat, PAT_TID, ts->tsid, 0, 0, 0, + mpegts_write_section1(&ts->pat, PAT_TID, ts->tsid, ts->tables_version, 0, 0, data, q - data); } @@ -311,6 +323,20 @@ static void mpegts_write_pmt(AVFormatContext *s, MpegTSService *service) /* write optional descriptors here */ switch(st->codec->codec_type) { case AVMEDIA_TYPE_AUDIO: + if(st->codec->codec_id==AV_CODEC_ID_EAC3){ + *q++=0x7a; // EAC3 descriptor see A038 DVB SI + *q++=1; // 1 byte, all flags sets to 0 + *q++=0; // omit all fields... + } + if(st->codec->codec_id==AV_CODEC_ID_S302M){ + *q++ = 0x05; /* MPEG-2 registration descriptor*/ + *q++ = 4; + *q++ = 'B'; + *q++ = 'S'; + *q++ = 'S'; + *q++ = 'D'; + } + if (lang) { char *p; char *next = lang->value; @@ -374,13 +400,23 @@ static void mpegts_write_pmt(AVFormatContext *s, MpegTSService *service) *q++ = 'c'; } break; + case AVMEDIA_TYPE_DATA: + if (st->codec->codec_id == AV_CODEC_ID_SMPTE_KLV) { + *q++ = 0x05; /* MPEG-2 registration descriptor */ + *q++ = 4; + *q++ = 'K'; + *q++ = 'L'; + *q++ = 'V'; + *q++ = 'A'; + } + break; } val = 0xf000 | (q - desc_length_ptr - 2); desc_length_ptr[0] = val >> 8; desc_length_ptr[1] = val; } - mpegts_write_section1(&service->pmt, PMT_TID, service->sid, 0, 0, 0, + mpegts_write_section1(&service->pmt, PMT_TID, service->sid, ts->tables_version, 0, 0, data, q - data); } @@ -435,7 +471,7 @@ static void mpegts_write_sdt(AVFormatContext *s) desc_list_len_ptr[0] = val >> 8; desc_list_len_ptr[1] = val; } - mpegts_write_section1(&ts->sdt, SDT_TID, ts->tsid, 0, 0, 0, + mpegts_write_section1(&ts->sdt, SDT_TID, ts->tsid, ts->tables_version, 0, 0, data, q - data); } @@ -458,9 +494,28 @@ static MpegTSService *mpegts_add_service(MpegTSWrite *ts, return service; } +static int64_t get_pcr(const MpegTSWrite *ts, AVIOContext *pb) +{ + return av_rescale(avio_tell(pb) + 11, 8 * PCR_TIME_BASE, ts->mux_rate) + + ts->first_pcr; +} + +static void mpegts_prefix_m2ts_header(AVFormatContext *s) +{ + MpegTSWrite *ts = s->priv_data; + if (ts->m2ts_mode) { + int64_t pcr = get_pcr(s->priv_data, s->pb); + uint32_t tp_extra_header = pcr % 0x3fffffff; + tp_extra_header = AV_RB32(&tp_extra_header); + avio_write(s->pb, (unsigned char *) &tp_extra_header, + sizeof(tp_extra_header)); + } +} + static void section_write_packet(MpegTSSection *s, const uint8_t *packet) { AVFormatContext *ctx = s->opaque; + mpegts_prefix_m2ts_header(ctx); avio_write(ctx->pb, packet, TS_PACKET_SIZE); } @@ -601,9 +656,10 @@ static int mpegts_write_header(AVFormatContext *s) ts->pat_packet_period = (ts->mux_rate * PAT_RETRANS_TIME) / (TS_PACKET_SIZE * 8 * 1000); - ts->first_pcr = av_rescale(s->max_delay, PCR_TIME_BASE, AV_TIME_BASE); + if(ts->copyts < 1) + ts->first_pcr = av_rescale(s->max_delay, PCR_TIME_BASE, AV_TIME_BASE); } else { - /* Arbitrary values, PAT/PMT could be written on key frames */ + /* Arbitrary values, PAT/PMT will also be written on video key frames */ ts->sdt_packet_period = 200; ts->pat_packet_period = 40; if (pcr_st->codec->codec_type == AVMEDIA_TYPE_AUDIO) { @@ -620,6 +676,8 @@ static int mpegts_write_header(AVFormatContext *s) service->pcr_packet_period = pcr_st->codec->time_base.den/(10*pcr_st->codec->time_base.num); } + if(!service->pcr_packet_period) + service->pcr_packet_period = 1; } // output a PCR as soon as possible @@ -636,6 +694,14 @@ static int mpegts_write_header(AVFormatContext *s) service->pcr_packet_period, ts->sdt_packet_period, ts->pat_packet_period); + if (ts->m2ts_mode == -1) { + if (av_match_ext(s->filename, "m2ts")) { + ts->m2ts_mode = 1; + } else { + ts->m2ts_mode = 0; + } + } + avio_flush(s->pb); return 0; @@ -659,7 +725,7 @@ static int mpegts_write_header(AVFormatContext *s) } /* send SDT, PAT and PMT tables regulary */ -static void retransmit_si_info(AVFormatContext *s) +static void retransmit_si_info(AVFormatContext *s, int force_pat) { MpegTSWrite *ts = s->priv_data; int i; @@ -668,7 +734,7 @@ static void retransmit_si_info(AVFormatContext *s) ts->sdt_packet_count = 0; mpegts_write_sdt(s); } - if (++ts->pat_packet_count == ts->pat_packet_period) { + if (++ts->pat_packet_count == ts->pat_packet_period || force_pat) { ts->pat_packet_count = 0; mpegts_write_pat(s); for(i = 0; i < ts->nb_services; i++) { @@ -677,12 +743,6 @@ static void retransmit_si_info(AVFormatContext *s) } } -static int64_t get_pcr(const MpegTSWrite *ts, AVIOContext *pb) -{ - return av_rescale(avio_tell(pb) + 11, 8 * PCR_TIME_BASE, ts->mux_rate) + - ts->first_pcr; -} - static int write_pcr_bits(uint8_t *buf, int64_t pcr) { int64_t pcr_low = pcr % 300, pcr_high = pcr / 300; @@ -709,6 +769,7 @@ static void mpegts_insert_null_packet(AVFormatContext *s) *q++ = 0xff; *q++ = 0x10; memset(q, 0x0FF, TS_PACKET_SIZE - (q - buf)); + mpegts_prefix_m2ts_header(s); avio_write(s->pb, buf, TS_PACKET_SIZE); } @@ -734,6 +795,7 @@ static void mpegts_insert_pcr_only(AVFormatContext *s, AVStream *st) /* stuffing bytes */ memset(q, 0xFF, TS_PACKET_SIZE - (q - buf)); + mpegts_prefix_m2ts_header(s); avio_write(s->pb, buf, TS_PACKET_SIZE); } @@ -755,7 +817,7 @@ static void write_pts(uint8_t *q, int fourbits, int64_t pts) static void set_af_flag(uint8_t *pkt, int flag) { // expect at least one flag to set - assert(flag); + av_assert0(flag); if ((pkt[3] & 0x20) == 0) { // no AF yet, set adaptation field flag @@ -771,7 +833,7 @@ static void set_af_flag(uint8_t *pkt, int flag) static void extend_af(uint8_t *pkt, int size) { // expect already existing adaptation field - assert(pkt[3] & 0x20); + av_assert0(pkt[3] & 0x20); pkt[4] += size; } @@ -801,10 +863,12 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st, int afc_len, stuffing_len; int64_t pcr = -1; /* avoid warning */ int64_t delay = av_rescale(s->max_delay, 90000, AV_TIME_BASE); + int force_pat = st->codec->codec_type == AVMEDIA_TYPE_VIDEO && key && !ts_st->prev_payload_key; is_start = 1; while (payload_size > 0) { - retransmit_si_info(s); + retransmit_si_info(s, force_pat); + force_pat = 0; write_pcr = 0; if (ts_st->pid == ts_st->service->pcr_pid) { @@ -874,6 +938,10 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st, st->codec->codec_id == AV_CODEC_ID_MP3 || st->codec->codec_id == AV_CODEC_ID_AAC)) { *q++ = 0xc0; + } else if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO && + st->codec->codec_id == AV_CODEC_ID_AC3 && + ts->m2ts_mode) { + *q++ = 0xfd; } else { *q++ = 0xbd; if (st->codec->codec_type == AVMEDIA_TYPE_SUBTITLE) { @@ -903,6 +971,17 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st, */ header_len += 3; } + /* for Blu-ray AC3 Audio the PES Extension flag should be as follow + * otherwise it will not play sound on blu-ray + */ + if (ts->m2ts_mode && + st->codec->codec_type == AVMEDIA_TYPE_AUDIO && + st->codec->codec_id == AV_CODEC_ID_AC3) { + /* set PES_extension_flag */ + pes_extension = 1; + flags |= 0x01; + header_len += 3; + } len = payload_size + header_len + 3; if (private_code != 0) len++; @@ -911,8 +990,8 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st, *q++ = len >> 8; *q++ = len; val = 0x80; - /* data alignment indicator is required for subtitle data */ - if (st->codec->codec_type == AVMEDIA_TYPE_SUBTITLE) + /* data alignment indicator is required for subtitle and data streams */ + if (st->codec->codec_type == AVMEDIA_TYPE_SUBTITLE || st->codec->codec_type == AVMEDIA_TYPE_DATA) val |= 0x04; *q++ = val; *q++ = flags; @@ -935,6 +1014,17 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st, */ *q++ = 0x00 | 0x60; } + /* For Blu-ray AC3 Audio Setting extended flags */ + if (ts->m2ts_mode && + pes_extension && + st->codec->codec_id == AV_CODEC_ID_AC3) { + flags = 0x01; /* set PES_extension_flag_2 */ + *q++ = flags; + *q++ = 0x80 | 0x01; /* marker bit + extension length */ + *q++ = 0x00 | 0x71; /* for AC3 Audio (specifically on blue-rays) */ + } + + if (private_code != 0) *q++ = private_code; is_start = 0; @@ -970,9 +1060,11 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st, memcpy(buf + TS_PACKET_SIZE - len, payload, len); payload += len; payload_size -= len; + mpegts_prefix_m2ts_header(s); avio_write(s->pb, buf, TS_PACKET_SIZE); } avio_flush(s->pb); + ts_st->prev_payload_key = key; } static int mpegts_write_packet_internal(AVFormatContext *s, AVPacket *pkt) @@ -983,8 +1075,8 @@ static int mpegts_write_packet_internal(AVFormatContext *s, AVPacket *pkt) uint8_t *data= NULL; MpegTSWrite *ts = s->priv_data; MpegTSWriteStream *ts_st = st->priv_data; - const uint64_t delay = av_rescale(s->max_delay, 90000, AV_TIME_BASE)*2; - int64_t dts = AV_NOPTS_VALUE, pts = AV_NOPTS_VALUE; + const int64_t delay = av_rescale(s->max_delay, 90000, AV_TIME_BASE)*2; + int64_t dts = pkt->dts, pts = pkt->pts; if (ts->reemit_pat_pmt) { av_log(s, AV_LOG_WARNING, "resend_headers option is deprecated, use -mpegts_flags resend_headers\n"); @@ -998,14 +1090,16 @@ static int mpegts_write_packet_internal(AVFormatContext *s, AVPacket *pkt) ts->flags &= ~MPEGTS_FLAG_REEMIT_PAT_PMT; } - if (pkt->pts != AV_NOPTS_VALUE) - pts = pkt->pts + delay; - if (pkt->dts != AV_NOPTS_VALUE) - dts = pkt->dts + delay; + if(ts->copyts < 1){ + if (pts != AV_NOPTS_VALUE) + pts += delay; + if (dts != AV_NOPTS_VALUE) + dts += delay; + } if (ts_st->first_pts_check && pts == AV_NOPTS_VALUE) { - av_log(s, AV_LOG_ERROR, "first pts value must set\n"); - return AVERROR(EINVAL); + av_log(s, AV_LOG_ERROR, "first pts value must be set\n"); + return AVERROR_INVALIDDATA; } ts_st->first_pts_check = 0; @@ -1014,9 +1108,12 @@ static int mpegts_write_packet_internal(AVFormatContext *s, AVPacket *pkt) uint32_t state = -1; if (pkt->size < 5 || AV_RB32(pkt->data) != 0x0000001) { + if (!st->nb_frames) { av_log(s, AV_LOG_ERROR, "H.264 bitstream malformed, " - "no startcode found, use -bsf h264_mp4toannexb\n"); + "no startcode found, use the h264_mp4toannexb bitstream filter (-bsf h264_mp4toannexb)\n"); return AVERROR(EINVAL); + } + av_log(s, AV_LOG_WARNING, "H.264 bitstream error, startcode missing\n"); } do { @@ -1039,7 +1136,7 @@ static int mpegts_write_packet_internal(AVFormatContext *s, AVPacket *pkt) } else if (st->codec->codec_id == AV_CODEC_ID_AAC) { if (pkt->size < 2) { av_log(s, AV_LOG_ERROR, "AAC packet too short\n"); - return AVERROR(EINVAL); + return AVERROR_INVALIDDATA; } if ((AV_RB16(pkt->data) & 0xfff0) != 0xfff0) { int ret; @@ -1048,7 +1145,7 @@ static int mpegts_write_packet_internal(AVFormatContext *s, AVPacket *pkt) if (!ts_st->amux) { av_log(s, AV_LOG_ERROR, "AAC bitstream not in ADTS format " "and extradata missing\n"); - return AVERROR(EINVAL); + return AVERROR_INVALIDDATA; } av_init_packet(&pkt2); @@ -1071,28 +1168,36 @@ static int mpegts_write_packet_internal(AVFormatContext *s, AVPacket *pkt) } } - if (st->codec->codec_type != AVMEDIA_TYPE_AUDIO) { + if (pkt->dts != AV_NOPTS_VALUE) { + int i; + for(i=0; i<s->nb_streams; i++){ + AVStream *st2 = s->streams[i]; + MpegTSWriteStream *ts_st2 = st2->priv_data; + if( ts_st2->payload_size + && (ts_st2->payload_dts == AV_NOPTS_VALUE || dts - ts_st2->payload_dts > delay/2)){ + mpegts_write_pes(s, st2, ts_st2->payload, ts_st2->payload_size, + ts_st2->payload_pts, ts_st2->payload_dts, + ts_st2->payload_flags & AV_PKT_FLAG_KEY); + ts_st2->payload_size = 0; + } + } + } + + if (ts_st->payload_size && ts_st->payload_size + size > ts->pes_payload_size) { + mpegts_write_pes(s, st, ts_st->payload, ts_st->payload_size, + ts_st->payload_pts, ts_st->payload_dts, + ts_st->payload_flags & AV_PKT_FLAG_KEY); + ts_st->payload_size = 0; + } + + if (st->codec->codec_type != AVMEDIA_TYPE_AUDIO || size > ts->pes_payload_size) { + av_assert0(!ts_st->payload_size); // for video and subtitle, write a single pes packet mpegts_write_pes(s, st, buf, size, pts, dts, pkt->flags & AV_PKT_FLAG_KEY); av_free(data); return 0; } - if (ts_st->payload_size + size > ts->pes_payload_size) { - if (ts_st->payload_size) { - mpegts_write_pes(s, st, ts_st->payload, ts_st->payload_size, - ts_st->payload_pts, ts_st->payload_dts, - ts_st->payload_flags & AV_PKT_FLAG_KEY); - ts_st->payload_size = 0; - } - if (size > ts->pes_payload_size) { - mpegts_write_pes(s, st, buf, size, pts, dts, - pkt->flags & AV_PKT_FLAG_KEY); - av_free(data); - return 0; - } - } - if (!ts_st->payload_size) { ts_st->payload_pts = pts; ts_st->payload_dts = dts; @@ -1168,7 +1273,7 @@ AVOutputFormat ff_mpegts_muxer = { .name = "mpegts", .long_name = NULL_IF_CONFIG_SMALL("MPEG-TS (MPEG-2 Transport Stream)"), .mime_type = "video/x-mpegts", - .extensions = "ts,m2t", + .extensions = "ts,m2t,m2ts,mts", .priv_data_size = sizeof(MpegTSWrite), .audio_codec = AV_CODEC_ID_MP2, .video_codec = AV_CODEC_ID_MPEG2VIDEO, diff --git a/libavformat/mpegvideodec.c b/libavformat/mpegvideodec.c index 96b95a58f1..a400978d02 100644 --- a/libavformat/mpegvideodec.c +++ b/libavformat/mpegvideodec.c @@ -3,20 +3,20 @@ * Copyright (c) 2002-2003 Fabrice Bellard * Copyright (c) 2006 Michael Niedermayer <michaelni@gmx.at> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -34,8 +34,9 @@ static int mpegvideo_probe(AVProbeData *p) { uint32_t code= -1; - int pic=0, seq=0, slice=0, pspack=0, pes=0; + int pic=0, seq=0, slice=0, pspack=0, vpes=0, apes=0, res=0, sicle=0; int i; + uint32_t last = 0; for(i=0; i<p->buf_size; i++){ code = (code<<8) + p->buf[i]; @@ -43,15 +44,28 @@ static int mpegvideo_probe(AVProbeData *p) switch(code){ case SEQ_START_CODE: seq++; break; case PICTURE_START_CODE: pic++; break; - case SLICE_START_CODE: slice++; break; case PACK_START_CODE: pspack++; break; + case 0x1b6: + res++; break; } - if ((code & 0x1f0) == VIDEO_ID) pes++; - else if((code & 0x1e0) == AUDIO_ID) pes++; + if (code >= SLICE_START_CODE && code <= 0x1af) { + if (last >= SLICE_START_CODE && last <= 0x1af) { + if (code >= last) slice++; + else sicle++; + }else{ + if (code == SLICE_START_CODE) slice++; + else sicle++; + } + } + if ((code & 0x1f0) == VIDEO_ID) vpes++; + else if((code & 0x1e0) == AUDIO_ID) apes++; + last = code; } } - if(seq && seq*9<=pic*10 && pic*9<=slice*10 && !pspack && !pes) - return pic>1 ? AVPROBE_SCORE_EXTENSION + 1 : AVPROBE_SCORE_EXTENSION / 2; // 1 more than .mpg + if(seq && seq*9<=pic*10 && pic*9<=slice*10 && !pspack && !apes && !res && slice > sicle) { + if(vpes) return AVPROBE_SCORE_EXTENSION / 4; + else return pic>1 ? AVPROBE_SCORE_EXTENSION + 1 : AVPROBE_SCORE_EXTENSION / 2; // +1 for .mpg + } return 0; } diff --git a/libavformat/mpjpeg.c b/libavformat/mpjpeg.c index 2114189244..2586ea8eb4 100644 --- a/libavformat/mpjpeg.c +++ b/libavformat/mpjpeg.c @@ -2,33 +2,33 @@ * Multipart JPEG format * Copyright (c) 2000, 2001, 2002, 2003 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "avformat.h" /* Multipart JPEG */ -#define BOUNDARY_TAG "avserver" +#define BOUNDARY_TAG "ffserver" static int mpjpeg_write_header(AVFormatContext *s) { uint8_t buf1[256]; - snprintf(buf1, sizeof(buf1), "--%s\n", BOUNDARY_TAG); + snprintf(buf1, sizeof(buf1), "--%s\r\n", BOUNDARY_TAG); avio_write(s->pb, buf1, strlen(buf1)); avio_flush(s->pb); return 0; @@ -38,11 +38,14 @@ static int mpjpeg_write_packet(AVFormatContext *s, AVPacket *pkt) { uint8_t buf1[256]; - snprintf(buf1, sizeof(buf1), "Content-type: image/jpeg\n\n"); + snprintf(buf1, sizeof(buf1), "Content-type: image/jpeg\r\n"); + avio_write(s->pb, buf1, strlen(buf1)); + + snprintf(buf1, sizeof(buf1), "Content-length: %d\r\n\r\n", pkt->size); avio_write(s->pb, buf1, strlen(buf1)); avio_write(s->pb, pkt->data, pkt->size); - snprintf(buf1, sizeof(buf1), "\n--%s\n", BOUNDARY_TAG); + snprintf(buf1, sizeof(buf1), "\r\n--%s\r\n", BOUNDARY_TAG); avio_write(s->pb, buf1, strlen(buf1)); return 0; } diff --git a/libavformat/mpl2dec.c b/libavformat/mpl2dec.c new file mode 100644 index 0000000000..17b302ddfa --- /dev/null +++ b/libavformat/mpl2dec.c @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2012 Clément BÅ“sch + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * MPL2 subtitles format demuxer + */ + +#include "avformat.h" +#include "internal.h" +#include "subtitles.h" + +typedef struct { + FFDemuxSubtitlesQueue q; +} MPL2Context; + +static int mpl2_probe(AVProbeData *p) +{ + int i; + char c; + int64_t start, end; + const unsigned char *ptr = p->buf; + const unsigned char *ptr_end = ptr + p->buf_size; + + for (i = 0; i < 2; i++) { + if (sscanf(ptr, "[%"SCNd64"][%"SCNd64"]%c", &start, &end, &c) != 3 && + sscanf(ptr, "[%"SCNd64"][]%c", &start, &c) != 2) + return 0; + ptr += ff_subtitles_next_line(ptr); + if (ptr >= ptr_end) + return 0; + } + return AVPROBE_SCORE_MAX; +} + +static int read_ts(char **line, int64_t *pts_start, int *duration) +{ + char c; + int len; + int64_t end; + + if (sscanf(*line, "[%"SCNd64"][]%c%n", + pts_start, &c, &len) >= 2) { + *duration = -1; + *line += len - 1; + return 0; + } + if (sscanf(*line, "[%"SCNd64"][%"SCNd64"]%c%n", + pts_start, &end, &c, &len) >= 3) { + *duration = end - *pts_start; + *line += len - 1; + return 0; + } + return -1; +} + +static int mpl2_read_header(AVFormatContext *s) +{ + MPL2Context *mpl2 = s->priv_data; + AVStream *st = avformat_new_stream(s, NULL); + int res = 0; + + if (!st) + return AVERROR(ENOMEM); + avpriv_set_pts_info(st, 64, 1, 10); + st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE; + st->codec->codec_id = AV_CODEC_ID_MPL2; + + while (!url_feof(s->pb)) { + char line[4096]; + char *p = line; + const int64_t pos = avio_tell(s->pb); + int len = ff_get_line(s->pb, line, sizeof(line)); + int64_t pts_start; + int duration; + + if (!len) + break; + + line[strcspn(line, "\r\n")] = 0; + + if (!read_ts(&p, &pts_start, &duration)) { + AVPacket *sub; + + sub = ff_subtitles_queue_insert(&mpl2->q, p, strlen(p), 0); + if (!sub) + return AVERROR(ENOMEM); + sub->pos = pos; + sub->pts = pts_start; + sub->duration = duration; + } + } + + ff_subtitles_queue_finalize(&mpl2->q); + return res; +} + +static int mpl2_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + MPL2Context *mpl2 = s->priv_data; + return ff_subtitles_queue_read_packet(&mpl2->q, pkt); +} + +static int mpl2_read_seek(AVFormatContext *s, int stream_index, + int64_t min_ts, int64_t ts, int64_t max_ts, int flags) +{ + MPL2Context *mpl2 = s->priv_data; + return ff_subtitles_queue_seek(&mpl2->q, s, stream_index, + min_ts, ts, max_ts, flags); +} + +static int mpl2_read_close(AVFormatContext *s) +{ + MPL2Context *mpl2 = s->priv_data; + ff_subtitles_queue_clean(&mpl2->q); + return 0; +} + +AVInputFormat ff_mpl2_demuxer = { + .name = "mpl2", + .long_name = NULL_IF_CONFIG_SMALL("MPL2 subtitles"), + .priv_data_size = sizeof(MPL2Context), + .read_probe = mpl2_probe, + .read_header = mpl2_read_header, + .read_packet = mpl2_read_packet, + .read_seek2 = mpl2_read_seek, + .read_close = mpl2_read_close, + .extensions = "txt,mpl2", +}; diff --git a/libavformat/mpsubdec.c b/libavformat/mpsubdec.c new file mode 100644 index 0000000000..c5bdcdb626 --- /dev/null +++ b/libavformat/mpsubdec.c @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2012 Clément BÅ“sch + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * MPlayer subtitles format demuxer + */ + +#include "avformat.h" +#include "internal.h" +#include "subtitles.h" + +typedef struct { + FFDemuxSubtitlesQueue q; +} MPSubContext; + +static int mpsub_probe(AVProbeData *p) +{ + const char *ptr = p->buf; + const char *ptr_end = p->buf + p->buf_size; + + while (ptr < ptr_end) { + int inc; + + if (!memcmp(ptr, "FORMAT=TIME", 11)) + return AVPROBE_SCORE_EXTENSION; + if (!memcmp(ptr, "FORMAT=", 7)) + return AVPROBE_SCORE_EXTENSION / 3; + inc = ff_subtitles_next_line(ptr); + if (!inc) + break; + ptr += inc; + } + return 0; +} + +static int mpsub_read_header(AVFormatContext *s) +{ + MPSubContext *mpsub = s->priv_data; + AVStream *st; + AVBPrint buf; + AVRational pts_info = (AVRational){ 100, 1 }; // ts based by default + int res = 0; + float multiplier = 100.0; + float current_pts = 0; + + av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED); + + while (!url_feof(s->pb)) { + char line[1024]; + float start, duration; + int fps, len = ff_get_line(s->pb, line, sizeof(line)); + + if (!len) + break; + + line[strcspn(line, "\r\n")] = 0; + + if (sscanf(line, "FORMAT=%d", &fps) == 1 && fps > 3 && fps < 100) { + /* frame based timing */ + pts_info = (AVRational){ fps, 1 }; + multiplier = 1.0; + } else if (sscanf(line, "%f %f", &start, &duration) == 2) { + AVPacket *sub; + const int64_t pos = avio_tell(s->pb); + + ff_subtitles_read_chunk(s->pb, &buf); + if (buf.len) { + sub = ff_subtitles_queue_insert(&mpsub->q, buf.str, buf.len, 0); + if (!sub) { + res = AVERROR(ENOMEM); + goto end; + } + sub->pts = (int64_t)(current_pts + start*multiplier); + sub->duration = (int)(duration * multiplier); + current_pts += (start + duration) * multiplier; + sub->pos = pos; + } + } + } + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + avpriv_set_pts_info(st, 64, pts_info.den, pts_info.num); + st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE; + st->codec->codec_id = AV_CODEC_ID_TEXT; + + ff_subtitles_queue_finalize(&mpsub->q); + +end: + av_bprint_finalize(&buf, NULL); + return res; +} + +static int mpsub_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + MPSubContext *mpsub = s->priv_data; + return ff_subtitles_queue_read_packet(&mpsub->q, pkt); +} + +static int mpsub_read_seek(AVFormatContext *s, int stream_index, + int64_t min_ts, int64_t ts, int64_t max_ts, int flags) +{ + MPSubContext *mpsub = s->priv_data; + return ff_subtitles_queue_seek(&mpsub->q, s, stream_index, + min_ts, ts, max_ts, flags); +} + +static int mpsub_read_close(AVFormatContext *s) +{ + MPSubContext *mpsub = s->priv_data; + ff_subtitles_queue_clean(&mpsub->q); + return 0; +} + +AVInputFormat ff_mpsub_demuxer = { + .name = "mpsub", + .long_name = NULL_IF_CONFIG_SMALL("MPlayer subtitles"), + .priv_data_size = sizeof(MPSubContext), + .read_probe = mpsub_probe, + .read_header = mpsub_read_header, + .read_packet = mpsub_read_packet, + .read_seek2 = mpsub_read_seek, + .read_close = mpsub_read_close, + .extensions = "sub", +}; diff --git a/libavformat/msnwc_tcp.c b/libavformat/msnwc_tcp.c index b6d30feb9d..0ac1c30747 100644 --- a/libavformat/msnwc_tcp.c +++ b/libavformat/msnwc_tcp.c @@ -1,20 +1,20 @@ /* * Copyright (C) 2008 Ramiro Polla * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -89,10 +89,10 @@ static int msnwc_tcp_read_header(AVFormatContext *ctx) /* Some files start with "connected\r\n\r\n". * So skip until we find the first byte of struct size */ - while(avio_r8(pb) != HEADER_SIZE && !pb->eof_reached); + while(avio_r8(pb) != HEADER_SIZE && !url_feof(pb)); - if(pb->eof_reached) { - av_log(ctx, AV_LOG_ERROR, "Could not find valid start."); + if(url_feof(pb)) { + av_log(ctx, AV_LOG_ERROR, "Could not find valid start.\n"); return -1; } diff --git a/libavformat/mtv.c b/libavformat/mtv.c index ed250bccc3..0517dd2d83 100644 --- a/libavformat/mtv.c +++ b/libavformat/mtv.c @@ -2,20 +2,20 @@ * mtv demuxer * Copyright (c) 2006 Reynaldo H. Verdejo Pinochet * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -42,10 +42,10 @@ typedef struct MTVDemuxContext { unsigned int audio_br; ///< bitrate of audio channel (mp3) unsigned int img_colorfmt; ///< frame colorfmt rgb 565/555 unsigned int img_bpp; ///< frame bits per pixel - unsigned int img_width; // - unsigned int img_height; // + unsigned int img_width; + unsigned int img_height; unsigned int img_segment_size; ///< size of image segment - unsigned int video_fps; // + unsigned int video_fps; unsigned int full_segment_size; } MTVDemuxContext; @@ -57,7 +57,7 @@ static int mtv_probe(AVProbeData *p) return 0; /* Check for nonzero in bpp and (width|height) header fields */ - if(!(p->buf[51] && AV_RL16(&p->buf[52]) | AV_RL16(&p->buf[54]))) + if(p->buf_size < 57 || !(p->buf[51] && AV_RL16(&p->buf[52]) | AV_RL16(&p->buf[54]))) return 0; /* If width or height are 0 then imagesize header field should not */ @@ -96,13 +96,19 @@ static int mtv_read_header(AVFormatContext *s) /* Calculate width and height if missing from header */ - if(!mtv->img_width) + if(mtv->img_bpp>>3){ + if(!mtv->img_width && mtv->img_height) mtv->img_width=mtv->img_segment_size / (mtv->img_bpp>>3) / mtv->img_height; - if(!mtv->img_height) + if(!mtv->img_height && mtv->img_width) mtv->img_height=mtv->img_segment_size / (mtv->img_bpp>>3) / mtv->img_width; + } + if(!mtv->img_height || !mtv->img_width || !mtv->img_segment_size){ + av_log(s, AV_LOG_ERROR, "width or height or segment_size is invalid and I cannot calculate them from other information\n"); + return AVERROR(EINVAL); + } avio_skip(pb, 4); audio_subsegments = avio_rl16(pb); diff --git a/libavformat/mux.c b/libavformat/mux.c index 505ed2e6f8..eff7caab25 100644 --- a/libavformat/mux.c +++ b/libavformat/mux.c @@ -1,21 +1,21 @@ /* - * muxing functions for use within Libav + * muxing functions for use within FFmpeg * Copyright (c) 2000, 2001, 2002 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -27,6 +27,7 @@ #include "libavutil/opt.h" #include "libavutil/dict.h" #include "libavutil/pixdesc.h" +#include "libavutil/timestamp.h" #include "metadata.h" #include "id3v2.h" #include "libavutil/avassert.h" @@ -48,7 +49,7 @@ /** * @file - * muxing functions for use within Libav + * muxing functions for use within libavformat */ /* fraction handling */ @@ -101,6 +102,88 @@ static void frac_add(AVFrac *f, int64_t incr) f->num = num; } +AVRational ff_choose_timebase(AVFormatContext *s, AVStream *st, int min_precission) +{ + AVRational q; + int j; + + if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) { + q = (AVRational){1, st->codec->sample_rate}; + } else { + q = st->codec->time_base; + } + for (j=2; j<14; j+= 1+(j>2)) + while (q.den / q.num < min_precission && q.num % j == 0) + q.num /= j; + while (q.den / q.num < min_precission && q.den < (1<<24)) + q.den <<= 1; + + return q; +} + +int avformat_alloc_output_context2(AVFormatContext **avctx, AVOutputFormat *oformat, + const char *format, const char *filename) +{ + AVFormatContext *s = avformat_alloc_context(); + int ret = 0; + + *avctx = NULL; + if (!s) + goto nomem; + + if (!oformat) { + if (format) { + oformat = av_guess_format(format, NULL, NULL); + if (!oformat) { + av_log(s, AV_LOG_ERROR, "Requested output format '%s' is not a suitable output format\n", format); + ret = AVERROR(EINVAL); + goto error; + } + } else { + oformat = av_guess_format(NULL, filename, NULL); + if (!oformat) { + ret = AVERROR(EINVAL); + av_log(s, AV_LOG_ERROR, "Unable to find a suitable output format for '%s'\n", + filename); + goto error; + } + } + } + + s->oformat = oformat; + if (s->oformat->priv_data_size > 0) { + s->priv_data = av_mallocz(s->oformat->priv_data_size); + if (!s->priv_data) + goto nomem; + if (s->oformat->priv_class) { + *(const AVClass**)s->priv_data= s->oformat->priv_class; + av_opt_set_defaults(s->priv_data); + } + } else + s->priv_data = NULL; + + if (filename) + av_strlcpy(s->filename, filename, sizeof(s->filename)); + *avctx = s; + return 0; +nomem: + av_log(s, AV_LOG_ERROR, "Out of memory\n"); + ret = AVERROR(ENOMEM); +error: + avformat_free_context(s); + return ret; +} + +#if FF_API_ALLOC_OUTPUT_CONTEXT +AVFormatContext *avformat_alloc_output_context(const char *format, + AVOutputFormat *oformat, const char *filename) +{ + AVFormatContext *avctx; + int ret = avformat_alloc_output_context2(&avctx, oformat, format, filename); + return ret < 0 ? NULL : avctx; +} +#endif + static int validate_codec_tag(AVFormatContext *s, AVStream *st) { const AVCodecTag *avctag; @@ -148,6 +231,9 @@ static int init_muxer(AVFormatContext *s, AVDictionary **options) if ((ret = av_opt_set_dict(s, &tmp)) < 0) goto fail; + if (s->priv_data && s->oformat->priv_class && *(const AVClass**)s->priv_data==s->oformat->priv_class && + (ret = av_opt_set_dict(s->priv_data, &tmp)) < 0) + goto fail; // some sanity checks if (s->nb_streams == 0 && !(of->flags & AVFMT_NOSTREAMS)) { @@ -185,18 +271,18 @@ static int init_muxer(AVFormatContext *s, AVDictionary **options) ret = AVERROR(EINVAL); goto fail; } - - if (av_cmp_q(st->sample_aspect_ratio, - codec->sample_aspect_ratio)) { + if (av_cmp_q(st->sample_aspect_ratio, codec->sample_aspect_ratio) + && FFABS(av_q2d(st->sample_aspect_ratio) - av_q2d(codec->sample_aspect_ratio)) > 0.004*av_q2d(st->sample_aspect_ratio) + ) { if (st->sample_aspect_ratio.num != 0 && st->sample_aspect_ratio.den != 0 && codec->sample_aspect_ratio.den != 0 && codec->sample_aspect_ratio.den != 0) { av_log(s, AV_LOG_ERROR, "Aspect ratio mismatch between muxer " - "(%d/%d) and encoder layer (%d/%d)\n", - st->sample_aspect_ratio.num, st->sample_aspect_ratio.den, - codec->sample_aspect_ratio.num, - codec->sample_aspect_ratio.den); + "(%d/%d) and encoder layer (%d/%d)\n", + st->sample_aspect_ratio.num, st->sample_aspect_ratio.den, + codec->sample_aspect_ratio.num, + codec->sample_aspect_ratio.den); ret = AVERROR(EINVAL); goto fail; } @@ -205,21 +291,23 @@ static int init_muxer(AVFormatContext *s, AVDictionary **options) } if (of->codec_tag) { - if (codec->codec_tag && - codec->codec_id == AV_CODEC_ID_RAWVIDEO && - !av_codec_get_tag(of->codec_tag, codec->codec_id) && - !validate_codec_tag(s, st)) { + if ( codec->codec_tag + && codec->codec_id == AV_CODEC_ID_RAWVIDEO + && ( av_codec_get_tag(of->codec_tag, codec->codec_id) == 0 + || av_codec_get_tag(of->codec_tag, codec->codec_id) == MKTAG('r', 'a', 'w', ' ')) + && !validate_codec_tag(s, st)) { // the current rawvideo encoding system ends up setting - // the wrong codec_tag for avi, we override it here + // the wrong codec_tag for avi/mov, we override it here codec->codec_tag = 0; } if (codec->codec_tag) { if (!validate_codec_tag(s, st)) { - char tagbuf[32]; + char tagbuf[32], tagbuf2[32]; av_get_codec_tag_string(tagbuf, sizeof(tagbuf), codec->codec_tag); + av_get_codec_tag_string(tagbuf2, sizeof(tagbuf2), av_codec_get_tag(s->oformat->codec_tag, codec->codec_id)); av_log(s, AV_LOG_ERROR, - "Tag %s/0x%08x incompatible with output codec id '%d'\n", - tagbuf, codec->codec_tag, codec->codec_id); + "Tag %s/0x%08x incompatible with output codec id '%d' (%s)\n", + tagbuf, codec->codec_tag, codec->codec_id, tagbuf2); ret = AVERROR_INVALIDDATA; goto fail; } @@ -305,6 +393,8 @@ int avformat_write_header(AVFormatContext *s, AVDictionary **options) if (s->oformat->write_header) { ret = s->oformat->write_header(s); + if (ret >= 0 && s->pb && s->pb->error < 0) + ret = s->pb->error; if (ret < 0) return ret; } @@ -312,20 +402,24 @@ int avformat_write_header(AVFormatContext *s, AVDictionary **options) if ((ret = init_pts(s)) < 0) return ret; + if (s->avoid_negative_ts < 0) { + if (s->oformat->flags & (AVFMT_TS_NEGATIVE | AVFMT_NOTIMESTAMPS)) { + s->avoid_negative_ts = 0; + } else + s->avoid_negative_ts = 1; + } + return 0; } //FIXME merge with compute_pkt_fields static int compute_pkt_fields2(AVFormatContext *s, AVStream *st, AVPacket *pkt) { - int delay = FFMAX(st->codec->has_b_frames, !!st->codec->max_b_frames); + int delay = FFMAX(st->codec->has_b_frames, st->codec->max_b_frames > 0); int num, den, frame_size, i; - av_dlog(s, "compute_pkt_fields2: pts:%" PRId64 " dts:%" PRId64 " cur_dts:%" PRId64 " b:%d size:%d st:%d\n", - pkt->pts, pkt->dts, st->cur_dts, delay, pkt->size, pkt->stream_index); - -/* if(pkt->pts == AV_NOPTS_VALUE && pkt->dts == AV_NOPTS_VALUE) - * return AVERROR(EINVAL);*/ + av_dlog(s, "compute_pkt_fields2: pts:%s dts:%s cur_dts:%s b:%d size:%d st:%d\n", + av_ts2str(pkt->pts), av_ts2str(pkt->dts), av_ts2str(st->cur_dts), delay, pkt->size, pkt->stream_index); /* duration field */ if (pkt->duration == 0) { @@ -340,6 +434,11 @@ static int compute_pkt_fields2(AVFormatContext *s, AVStream *st, AVPacket *pkt) //XXX/FIXME this is a temporary hack until all encoders output pts if ((pkt->pts == 0 || pkt->pts == AV_NOPTS_VALUE) && pkt->dts == AV_NOPTS_VALUE && !delay) { + static int warned; + if (!warned) { + av_log(s, AV_LOG_WARNING, "Encoder did not produce proper pts, making some up.\n"); + warned = 1; + } pkt->dts = // pkt->pts= st->cur_dts; pkt->pts = st->pts.val; @@ -360,17 +459,18 @@ static int compute_pkt_fields2(AVFormatContext *s, AVStream *st, AVPacket *pkt) ((!(s->oformat->flags & AVFMT_TS_NONSTRICT) && st->cur_dts >= pkt->dts) || st->cur_dts > pkt->dts)) { av_log(s, AV_LOG_ERROR, - "Application provided invalid, non monotonically increasing dts to muxer in stream %d: %" PRId64 " >= %" PRId64 "\n", - st->index, st->cur_dts, pkt->dts); + "Application provided invalid, non monotonically increasing dts to muxer in stream %d: %s >= %s\n", + st->index, av_ts2str(st->cur_dts), av_ts2str(pkt->dts)); return AVERROR(EINVAL); } if (pkt->dts != AV_NOPTS_VALUE && pkt->pts != AV_NOPTS_VALUE && pkt->pts < pkt->dts) { - av_log(s, AV_LOG_ERROR, "pts < dts in stream %d\n", st->index); + av_log(s, AV_LOG_ERROR, "pts (%s) < dts (%s) in stream %d\n", + av_ts2str(pkt->pts), av_ts2str(pkt->dts), st->index); return AVERROR(EINVAL); } - av_dlog(s, "av_write_frame: pts2:%"PRId64" dts2:%"PRId64"\n", - pkt->pts, pkt->dts); + av_dlog(s, "av_write_frame: pts2:%s dts2:%s\n", + av_ts2str(pkt->pts), av_ts2str(pkt->dts)); st->cur_dts = pkt->dts; st->pts.val = pkt->dts; @@ -395,37 +495,53 @@ static int compute_pkt_fields2(AVFormatContext *s, AVStream *st, AVPacket *pkt) return 0; } -/* +/** + * Make timestamps non negative, move side data from payload to internal struct, call muxer, and restore + * sidedata. + * * FIXME: this function should NEVER get undefined pts/dts beside when the * AVFMT_NOTIMESTAMPS is set. * Those additional safety checks should be dropped once the correct checks * are set in the callers. */ - static int write_packet(AVFormatContext *s, AVPacket *pkt) { - int ret; - if (!(s->oformat->flags & (AVFMT_TS_NEGATIVE | AVFMT_NOTIMESTAMPS))) { - AVRational time_base = s->streams[pkt->stream_index]->time_base; - int64_t offset = 0; + int ret, did_split; - if (!s->offset && pkt->dts != AV_NOPTS_VALUE && pkt->dts < 0) { + if (s->avoid_negative_ts > 0) { + AVStream *st = s->streams[pkt->stream_index]; + int64_t offset = st->mux_ts_offset; + + if (pkt->dts < 0 && pkt->dts != AV_NOPTS_VALUE && !s->offset) { s->offset = -pkt->dts; - s->offset_timebase = time_base; + s->offset_timebase = st->time_base; + } + + if (s->offset && !offset) { + offset = st->mux_ts_offset = + av_rescale_q_rnd(s->offset, + s->offset_timebase, + st->time_base, + AV_ROUND_UP); } - if (s->offset) - offset = av_rescale_q(s->offset, s->offset_timebase, time_base); if (pkt->dts != AV_NOPTS_VALUE) pkt->dts += offset; if (pkt->pts != AV_NOPTS_VALUE) pkt->pts += offset; + + av_assert2(pkt->dts == AV_NOPTS_VALUE || pkt->dts >= 0); } + + did_split = av_packet_split_side_data(pkt); ret = s->oformat->write_packet(s, pkt); - if (s->pb && ret >= 0 && s->flags & AVFMT_FLAG_FLUSH_PACKETS) + if (s->flush_packets && s->pb && ret >= 0 && s->flags & AVFMT_FLAG_FLUSH_PACKETS) avio_flush(s->pb); + if (did_split) + av_packet_merge_side_data(pkt); + return ret; } @@ -434,8 +550,14 @@ int av_write_frame(AVFormatContext *s, AVPacket *pkt) int ret; if (!pkt) { - if (s->oformat->flags & AVFMT_ALLOW_FLUSH) - return s->oformat->write_packet(s, pkt); + if (s->oformat->flags & AVFMT_ALLOW_FLUSH) { + ret = s->oformat->write_packet(s, NULL); + if (s->flush_packets && s->pb && s->pb->error >= 0) + avio_flush(s->pb); + if (ret >= 0 && s->pb && s->pb->error < 0) + ret = s->pb->error; + return ret; + } return 1; } @@ -445,18 +567,26 @@ int av_write_frame(AVFormatContext *s, AVPacket *pkt) return ret; ret = write_packet(s, pkt); + if (ret >= 0 && s->pb && s->pb->error < 0) + ret = s->pb->error; if (ret >= 0) s->streams[pkt->stream_index]->nb_frames++; return ret; } -void ff_interleave_add_packet(AVFormatContext *s, AVPacket *pkt, +#define CHUNK_START 0x1000 + +int ff_interleave_add_packet(AVFormatContext *s, AVPacket *pkt, int (*compare)(AVFormatContext *, AVPacket *, AVPacket *)) { AVPacketList **next_point, *this_pktl; + AVStream *st = s->streams[pkt->stream_index]; + int chunked = s->max_chunk_size || s->max_chunk_duration; this_pktl = av_mallocz(sizeof(AVPacketList)); + if (!this_pktl) + return AVERROR(ENOMEM); this_pktl->pkt = *pkt; #if FF_API_DESTRUCT_PACKET FF_DISABLE_DEPRECATION_WARNINGS @@ -464,23 +594,48 @@ FF_DISABLE_DEPRECATION_WARNINGS FF_ENABLE_DEPRECATION_WARNINGS #endif pkt->buf = NULL; - av_dup_packet(&this_pktl->pkt); // duplicate the packet if it uses non-alloced memory + av_dup_packet(&this_pktl->pkt); // duplicate the packet if it uses non-allocated memory + av_copy_packet_side_data(&this_pktl->pkt, &this_pktl->pkt); // copy side data if (s->streams[pkt->stream_index]->last_in_packet_buffer) { - next_point = &(s->streams[pkt->stream_index]->last_in_packet_buffer->next); - } else + next_point = &(st->last_in_packet_buffer->next); + } else { next_point = &s->packet_buffer; + } + if (chunked) { + uint64_t max= av_rescale_q_rnd(s->max_chunk_duration, AV_TIME_BASE_Q, st->time_base, AV_ROUND_UP); + st->interleaver_chunk_size += pkt->size; + st->interleaver_chunk_duration += pkt->duration; + if ( (s->max_chunk_size && st->interleaver_chunk_size > s->max_chunk_size) + || (max && st->interleaver_chunk_duration > max)) { + st->interleaver_chunk_size = 0; + this_pktl->pkt.flags |= CHUNK_START; + if (max && st->interleaver_chunk_duration > max) { + int64_t syncoffset = (st->codec->codec_type == AVMEDIA_TYPE_VIDEO)*max/2; + int64_t syncto = av_rescale(pkt->dts + syncoffset, 1, max)*max - syncoffset; + + st->interleaver_chunk_duration += (pkt->dts - syncto)/8 - max; + } else + st->interleaver_chunk_duration = 0; + } + } if (*next_point) { + if (chunked && !(this_pktl->pkt.flags & CHUNK_START)) + goto next_non_null; + if (compare(s, &s->packet_buffer_end->pkt, pkt)) { - while (!compare(s, &(*next_point)->pkt, pkt)) + while ( *next_point + && ((chunked && !((*next_point)->pkt.flags&CHUNK_START)) + || !compare(s, &(*next_point)->pkt, pkt))) next_point = &(*next_point)->next; - goto next_non_null; + if (*next_point) + goto next_non_null; } else { next_point = &(s->packet_buffer_end->next); } } - assert(!*next_point); + av_assert1(!*next_point); s->packet_buffer_end = this_pktl; next_non_null: @@ -489,6 +644,7 @@ next_non_null: s->streams[pkt->stream_index]->last_in_packet_buffer = *next_point = this_pktl; + return 0; } static int interleave_compare_dts(AVFormatContext *s, AVPacket *next, @@ -498,6 +654,16 @@ static int interleave_compare_dts(AVFormatContext *s, AVPacket *next, AVStream *st2 = s->streams[next->stream_index]; int comp = av_compare_ts(next->dts, st2->time_base, pkt->dts, st->time_base); + if (s->audio_preload && ((st->codec->codec_type == AVMEDIA_TYPE_AUDIO) != (st2->codec->codec_type == AVMEDIA_TYPE_AUDIO))) { + int64_t ts = av_rescale_q(pkt ->dts, st ->time_base, AV_TIME_BASE_Q) - s->audio_preload*(st ->codec->codec_type == AVMEDIA_TYPE_AUDIO); + int64_t ts2= av_rescale_q(next->dts, st2->time_base, AV_TIME_BASE_Q) - s->audio_preload*(st2->codec->codec_type == AVMEDIA_TYPE_AUDIO); + if (ts == ts2) { + ts= ( pkt ->dts* st->time_base.num*AV_TIME_BASE - s->audio_preload*(int64_t)(st ->codec->codec_type == AVMEDIA_TYPE_AUDIO)* st->time_base.den)*st2->time_base.den + -( next->dts*st2->time_base.num*AV_TIME_BASE - s->audio_preload*(int64_t)(st2->codec->codec_type == AVMEDIA_TYPE_AUDIO)*st2->time_base.den)* st->time_base.den; + ts2=0; + } + comp= (ts>ts2) - (ts<ts2); + } if (comp == 0) return pkt->stream_index < next->stream_index; @@ -508,27 +674,59 @@ int ff_interleave_packet_per_dts(AVFormatContext *s, AVPacket *out, AVPacket *pkt, int flush) { AVPacketList *pktl; - int stream_count = 0; - int i; + int stream_count = 0, noninterleaved_count = 0; + int64_t delta_dts_max = 0; + int i, ret; if (pkt) { - ff_interleave_add_packet(s, pkt, interleave_compare_dts); + ret = ff_interleave_add_packet(s, pkt, interleave_compare_dts); + if (ret < 0) + return ret; } - for (i = 0; i < s->nb_streams; i++) - stream_count += !!s->streams[i]->last_in_packet_buffer; + for (i = 0; i < s->nb_streams; i++) { + if (s->streams[i]->last_in_packet_buffer) { + ++stream_count; + } else if (s->streams[i]->codec->codec_type == AVMEDIA_TYPE_SUBTITLE) { + ++noninterleaved_count; + } + } - if (stream_count && (s->nb_streams == stream_count || flush)) { + if (s->nb_streams == stream_count) { + flush = 1; + } else if (!flush) { + for (i=0; i < s->nb_streams; i++) { + if (s->streams[i]->last_in_packet_buffer) { + int64_t delta_dts = + av_rescale_q(s->streams[i]->last_in_packet_buffer->pkt.dts, + s->streams[i]->time_base, + AV_TIME_BASE_Q) - + av_rescale_q(s->packet_buffer->pkt.dts, + s->streams[s->packet_buffer->pkt.stream_index]->time_base, + AV_TIME_BASE_Q); + delta_dts_max= FFMAX(delta_dts_max, delta_dts); + } + } + if (s->nb_streams == stream_count+noninterleaved_count && + delta_dts_max > 20*AV_TIME_BASE) { + av_log(s, AV_LOG_DEBUG, "flushing with %d noninterleaved\n", noninterleaved_count); + flush = 1; + } + } + if (stream_count && flush) { + AVStream *st; pktl = s->packet_buffer; *out = pktl->pkt; + st = s->streams[out->stream_index]; s->packet_buffer = pktl->next; if (!s->packet_buffer) s->packet_buffer_end = NULL; - if (s->streams[out->stream_index]->last_in_packet_buffer == pktl) - s->streams[out->stream_index]->last_in_packet_buffer = NULL; + if (st->last_in_packet_buffer == pktl) + st->last_in_packet_buffer = NULL; av_freep(&pktl); + return 1; } else { av_init_packet(out); @@ -567,8 +765,8 @@ int av_interleaved_write_frame(AVFormatContext *s, AVPacket *pkt) if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO && pkt->size == 0) return 0; - av_dlog(s, "av_interleaved_write_frame size:%d dts:%" PRId64 " pts:%" PRId64 "\n", - pkt->size, pkt->dts, pkt->pts); + av_dlog(s, "av_interleaved_write_frame size:%d dts:%s pts:%s\n", + pkt->size, av_ts2str(pkt->dts), av_ts2str(pkt->pts)); if ((ret = compute_pkt_fields2(s, st, pkt)) < 0 && !(s->oformat->flags & AVFMT_NOTIMESTAMPS)) return ret; @@ -594,6 +792,8 @@ int av_interleaved_write_frame(AVFormatContext *s, AVPacket *pkt) if (ret < 0) return ret; + if(s->pb && s->pb->error) + return s->pb->error; } } @@ -617,15 +817,18 @@ int av_write_trailer(AVFormatContext *s) if (ret < 0) goto fail; + if(s->pb && s->pb->error) + goto fail; } if (s->oformat->write_trailer) ret = s->oformat->write_trailer(s); - if (!(s->oformat->flags & AVFMT_NOFILE)) - avio_flush(s->pb); - fail: + if (s->pb) + avio_flush(s->pb); + if (ret == 0) + ret = s->pb ? s->pb->error : 0; for (i = 0; i < s->nb_streams; i++) { av_freep(&s->streams[i]->priv_data); av_freep(&s->streams[i]->index_entries); @@ -636,6 +839,15 @@ fail: return ret; } +int av_get_output_timestamp(struct AVFormatContext *s, int stream, + int64_t *dts, int64_t *wall) +{ + if (!s->oformat || !s->oformat->get_output_timestamp) + return AVERROR(ENOSYS); + s->oformat->get_output_timestamp(s, stream, dts, wall); + return 0; +} + int ff_write_chained(AVFormatContext *dst, int dst_stream, AVPacket *pkt, AVFormatContext *src) { @@ -651,5 +863,9 @@ int ff_write_chained(AVFormatContext *dst, int dst_stream, AVPacket *pkt, local_pkt.dts = av_rescale_q(pkt->dts, src->streams[pkt->stream_index]->time_base, dst->streams[dst_stream]->time_base); + if (pkt->duration) + local_pkt.duration = av_rescale_q(pkt->duration, + src->streams[pkt->stream_index]->time_base, + dst->streams[dst_stream]->time_base); return av_write_frame(dst, &local_pkt); } diff --git a/libavformat/mvdec.c b/libavformat/mvdec.c new file mode 100644 index 0000000000..5525233db5 --- /dev/null +++ b/libavformat/mvdec.c @@ -0,0 +1,441 @@ +/* + * Silicon Graphics Movie demuxer + * Copyright (c) 2012 Peter Ross + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * Silicon Graphics Movie demuxer + */ + +#include "libavutil/eval.h" +#include "libavutil/intreadwrite.h" +#include "libavutil/rational.h" +#include "avformat.h" +#include "internal.h" + +typedef struct { + int nb_video_tracks; + int nb_audio_tracks; + + int eof_count; /**< number of streams that have finished */ + int stream_index; /**< current stream index */ + int frame[2]; /**< frame nb for current stream */ +} MvContext; + +#define AUDIO_FORMAT_SIGNED 401 + +static int mv_probe(AVProbeData *p) +{ + if (AV_RB32(p->buf) == MKBETAG('M','O','V','I') && AV_RB16(p->buf + 4) < 3) + return AVPROBE_SCORE_MAX; + return 0; +} + +static char * var_read_string(AVIOContext *pb, int size) +{ + char *str = av_malloc(size + 1); + int n; + if (!str) + return NULL; + n = avio_get_str(pb, size, str, size + 1); + if (n < size) + avio_skip(pb, size - n); + return str; +} + +static int var_read_int(AVIOContext *pb, int size) +{ + int v; + char * s = var_read_string(pb, size); + if (!s || sscanf(s, "%d", &v) != 1) + v = 0; + av_free(s); + return v; +} + +static AVRational var_read_float(AVIOContext *pb, int size) +{ + AVRational v; + char * s = var_read_string(pb, size); + if (!s) + return (AVRational){0, 0}; + v = av_d2q(av_strtod(s, NULL), INT_MAX); + av_free(s); + return v; +} + +static void var_read_metadata(AVFormatContext *avctx, const char *tag, int size) +{ + char *value = var_read_string(avctx->pb, size); + if (value) + av_dict_set(&avctx->metadata, tag, value, AV_DICT_DONT_STRDUP_VAL); +} + +static int set_channels(AVFormatContext *avctx, AVStream *st, int channels) { + if (channels <= 0) { + av_log(avctx, AV_LOG_ERROR, "Channel count %d invalid\n", channels); + return AVERROR_INVALIDDATA; + } + st->codec->channels = channels; + st->codec->channel_layout = (st->codec->channels == 1) ? AV_CH_LAYOUT_MONO : AV_CH_LAYOUT_STEREO; + return 0; +} + +/** + * Parse global variable + * @return < 0 if unknown + */ +static int parse_global_var(AVFormatContext *avctx, AVStream *st, const char *name, int size) +{ + MvContext *mv = avctx->priv_data; + AVIOContext *pb = avctx->pb; + if (!strcmp(name, "__NUM_I_TRACKS")) { + mv->nb_video_tracks = var_read_int(pb, size); + } else if (!strcmp(name, "__NUM_A_TRACKS")) { + mv->nb_audio_tracks = var_read_int(pb, size); + } else if (!strcmp(name, "COMMENT") || !strcmp(name, "TITLE")) { + var_read_metadata(avctx, name, size); + } else if (!strcmp(name, "LOOP_MODE") || !strcmp(name, "NUM_LOOPS") || !strcmp(name, "OPTIMIZED")) { + avio_skip(pb, size); // ignore + } else + return -1; + + return 0; +} + +/** + * Parse audio variable + * @return < 0 if unknown + */ +static int parse_audio_var(AVFormatContext *avctx, AVStream *st, const char *name, int size) +{ + AVIOContext *pb = avctx->pb; + if (!strcmp(name, "__DIR_COUNT")) { + st->nb_frames = var_read_int(pb, size); + } else if (!strcmp(name, "AUDIO_FORMAT")) { + st->codec->codec_id = var_read_int(pb, size); + } else if (!strcmp(name, "COMPRESSION")) { + st->codec->codec_tag = var_read_int(pb, size); + } else if (!strcmp(name, "DEFAULT_VOL")) { + var_read_metadata(avctx, name, size); + } else if (!strcmp(name, "NUM_CHANNELS")) { + return set_channels(avctx, st, var_read_int(pb, size)); + } else if (!strcmp(name, "SAMPLE_RATE")) { + st->codec->sample_rate = var_read_int(pb, size); + avpriv_set_pts_info(st, 33, 1, st->codec->sample_rate); + } else if (!strcmp(name, "SAMPLE_WIDTH")) { + st->codec->bits_per_coded_sample = var_read_int(pb, size) * 8; + } else + return -1; + return 0; +} + +/** + * Parse video variable + * @return < 0 if unknown + */ +static int parse_video_var(AVFormatContext *avctx, AVStream *st, const char *name, int size) +{ + AVIOContext *pb = avctx->pb; + if (!strcmp(name, "__DIR_COUNT")) { + st->nb_frames = st->duration = var_read_int(pb, size); + } else if (!strcmp(name, "COMPRESSION")) { + char * str = var_read_string(pb, size); + if (!str) + return AVERROR_INVALIDDATA; + if (!strcmp(str, "1")) { + st->codec->codec_id = AV_CODEC_ID_MVC1; + } else if (!strcmp(str, "2")) { + st->codec->pix_fmt = AV_PIX_FMT_ABGR; + st->codec->codec_id = AV_CODEC_ID_RAWVIDEO; + } else if (!strcmp(str, "3")) { + st->codec->codec_id = AV_CODEC_ID_SGIRLE; + } else if (!strcmp(str, "10")) { + st->codec->codec_id = AV_CODEC_ID_MJPEG; + } else if (!strcmp(str, "MVC2")) { + st->codec->codec_id = AV_CODEC_ID_MVC2; + } else { + avpriv_request_sample(avctx, "video compression %s", str); + } + av_free(str); + } else if (!strcmp(name, "FPS")) { + AVRational fps = var_read_float(pb, size); + avpriv_set_pts_info(st, 64, fps.den, fps.num); + } else if (!strcmp(name, "HEIGHT")) { + st->codec->height = var_read_int(pb, size); + } else if (!strcmp(name, "PIXEL_ASPECT")) { + st->sample_aspect_ratio = var_read_float(pb, size); + av_reduce(&st->sample_aspect_ratio.num, &st->sample_aspect_ratio.den, + st->sample_aspect_ratio.num, st->sample_aspect_ratio.den, INT_MAX); + } else if (!strcmp(name, "WIDTH")) { + st->codec->width = var_read_int(pb, size); + } else if (!strcmp(name, "ORIENTATION")) { + if (var_read_int(pb, size) == 1101) { + st->codec->extradata = av_strdup("BottomUp"); + st->codec->extradata_size = 9; + } + } else if (!strcmp(name, "Q_SPATIAL") || !strcmp(name, "Q_TEMPORAL")) { + var_read_metadata(avctx, name, size); + } else if (!strcmp(name, "INTERLACING") || !strcmp(name, "PACKING")) { + avio_skip(pb, size); // ignore + } else + return -1; + return 0; +} + +static void read_table(AVFormatContext *avctx, AVStream *st, int (*parse)(AVFormatContext *avctx, AVStream *st, const char *name, int size)) +{ + int count, i; + AVIOContext *pb = avctx->pb; + avio_skip(pb, 4); + count = avio_rb32(pb); + avio_skip(pb, 4); + for (i = 0; i < count; i++) { + char name[17]; + int size; + avio_read(pb, name, 16); + name[sizeof(name) - 1] = 0; + size = avio_rb32(pb); + if (parse(avctx, st, name, size) < 0) { + avpriv_request_sample(avctx, "variable %s", name); + avio_skip(pb, size); + } + } +} + +static void read_index(AVIOContext *pb, AVStream *st) +{ + uint64_t timestamp = 0; + int i; + for (i = 0; i < st->nb_frames; i++) { + uint32_t pos = avio_rb32(pb); + uint32_t size = avio_rb32(pb); + avio_skip(pb, 8); + av_add_index_entry(st, pos, timestamp, size, 0, AVINDEX_KEYFRAME); + if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) { + timestamp += size / (st->codec->channels * 2); + } else { + timestamp++; + } + } +} + +static int mv_read_header(AVFormatContext *avctx) +{ + MvContext *mv = avctx->priv_data; + AVIOContext *pb = avctx->pb; + AVStream *ast = NULL, *vst = NULL; //initialization to suppress warning + int version, i; + + avio_skip(pb, 4); + + version = avio_rb16(pb); + if (version == 2) { + uint64_t timestamp; + int v; + avio_skip(pb, 22); + + /* allocate audio track first to prevent unnecessary seeking + (audio packet always precede video packet for a given frame) */ + ast = avformat_new_stream(avctx, NULL); + if (!ast) + return AVERROR(ENOMEM); + + vst = avformat_new_stream(avctx, NULL); + if (!vst) + return AVERROR(ENOMEM); + vst->codec->codec_type = AVMEDIA_TYPE_VIDEO; + avpriv_set_pts_info(vst, 64, 1, 15); + vst->nb_frames = avio_rb32(pb); + v = avio_rb32(pb); + switch (v) { + case 1: + vst->codec->codec_id = AV_CODEC_ID_MVC1; + break; + case 2: + vst->codec->pix_fmt = AV_PIX_FMT_ARGB; + vst->codec->codec_id = AV_CODEC_ID_RAWVIDEO; + break; + default: + avpriv_request_sample(avctx, "video compression %i", v); + break; + } + vst->codec->codec_tag = 0; + vst->codec->width = avio_rb32(pb); + vst->codec->height = avio_rb32(pb); + avio_skip(pb, 12); + + ast->codec->codec_type = AVMEDIA_TYPE_AUDIO; + ast->nb_frames = vst->nb_frames; + ast->codec->sample_rate = avio_rb32(pb); + avpriv_set_pts_info(ast, 33, 1, ast->codec->sample_rate); + if (set_channels(avctx, ast, avio_rb32(pb)) < 0) + return AVERROR_INVALIDDATA; + + v = avio_rb32(pb); + if (v == AUDIO_FORMAT_SIGNED) { + ast->codec->codec_id = AV_CODEC_ID_PCM_S16BE; + } else { + avpriv_request_sample(avctx, "audio compression (format %i)", v); + } + + avio_skip(pb, 12); + var_read_metadata(avctx, "title", 0x80); + var_read_metadata(avctx, "comment", 0x100); + avio_skip(pb, 0x80); + + timestamp = 0; + for (i = 0; i < vst->nb_frames; i++) { + uint32_t pos = avio_rb32(pb); + uint32_t asize = avio_rb32(pb); + uint32_t vsize = avio_rb32(pb); + avio_skip(pb, 8); + av_add_index_entry(ast, pos, timestamp, asize, 0, AVINDEX_KEYFRAME); + av_add_index_entry(vst, pos + asize, i, vsize, 0, AVINDEX_KEYFRAME); + timestamp += asize / (ast->codec->channels * 2); + } + } else if (!version && avio_rb16(pb) == 3) { + avio_skip(pb, 4); + + read_table(avctx, NULL, parse_global_var); + + if (mv->nb_audio_tracks > 1) { + avpriv_request_sample(avctx, "multiple audio streams support"); + return AVERROR_PATCHWELCOME; + } else if (mv->nb_audio_tracks) { + ast = avformat_new_stream(avctx, NULL); + if (!ast) + return AVERROR(ENOMEM); + ast->codec->codec_type = AVMEDIA_TYPE_AUDIO; + /* temporarily store compression value in codec_tag; format value in codec_id */ + read_table(avctx, ast, parse_audio_var); + if (ast->codec->codec_tag == 100 && ast->codec->codec_id == AUDIO_FORMAT_SIGNED && ast->codec->bits_per_coded_sample == 16) { + ast->codec->codec_id = AV_CODEC_ID_PCM_S16BE; + } else { + avpriv_request_sample(avctx, "audio compression %i (format %i, width %i)", + ast->codec->codec_tag, ast->codec->codec_id, ast->codec->bits_per_coded_sample); + ast->codec->codec_id = AV_CODEC_ID_NONE; + } + ast->codec->codec_tag = 0; + if (ast->codec->channels <= 0) { + av_log(avctx, AV_LOG_ERROR, "No valid channel count found\n"); + return AVERROR_INVALIDDATA; + } + } + + if (mv->nb_video_tracks > 1) { + avpriv_request_sample(avctx, "multiple video streams support"); + return AVERROR_PATCHWELCOME; + } else if (mv->nb_video_tracks) { + vst = avformat_new_stream(avctx, NULL); + if (!vst) + return AVERROR(ENOMEM); + vst->codec->codec_type = AVMEDIA_TYPE_VIDEO; + read_table(avctx, vst, parse_video_var); + } + + if (mv->nb_audio_tracks) + read_index(pb, ast); + + if (mv->nb_video_tracks) + read_index(pb, vst); + } else { + avpriv_request_sample(avctx, "version %i", version); + return AVERROR_PATCHWELCOME; + } + + return 0; +} + +static int mv_read_packet(AVFormatContext *avctx, AVPacket *pkt) +{ + MvContext *mv = avctx->priv_data; + AVIOContext *pb = avctx->pb; + AVStream *st = avctx->streams[mv->stream_index]; + const AVIndexEntry *index; + int frame = mv->frame[mv->stream_index]; + int ret; + uint64_t pos; + + if (frame < st->nb_index_entries) { + index = &st->index_entries[frame]; + pos = avio_tell(pb); + if (index->pos > pos) + avio_skip(pb, index->pos - pos); + else if (index->pos < pos) { + if (!pb->seekable) + return AVERROR(EIO); + ret = avio_seek(pb, index->pos, SEEK_SET); + if (ret < 0) + return ret; + } + ret = av_get_packet(pb, pkt, index->size); + if (ret < 0) + return ret; + + pkt->stream_index = mv->stream_index; + pkt->pts = index->timestamp; + pkt->flags |= AV_PKT_FLAG_KEY; + + mv->frame[mv->stream_index]++; + mv->eof_count = 0; + } else { + mv->eof_count++; + if (mv->eof_count >= avctx->nb_streams) + return AVERROR_EOF; + } + + mv->stream_index++; + if (mv->stream_index >= avctx->nb_streams) + mv->stream_index = 0; + + return 0; +} + +static int mv_read_seek(AVFormatContext *avctx, int stream_index, int64_t timestamp, int flags) +{ + MvContext *mv = avctx->priv_data; + AVStream *st = avctx->streams[stream_index]; + int frame, i; + + if ((flags & AVSEEK_FLAG_FRAME) || (flags & AVSEEK_FLAG_BYTE)) + return AVERROR(ENOSYS); + + if (!avctx->pb->seekable) + return AVERROR(EIO); + + frame = av_index_search_timestamp(st, timestamp, flags); + if (frame < 0) + return -1; + + for (i = 0; i < avctx->nb_streams; i++) + mv->frame[i] = frame; + return 0; +} + +AVInputFormat ff_mv_demuxer = { + .name = "mv", + .long_name = NULL_IF_CONFIG_SMALL("Silicon Graphics Movie"), + .priv_data_size = sizeof(MvContext), + .read_probe = mv_probe, + .read_header = mv_read_header, + .read_packet = mv_read_packet, + .read_seek = mv_read_seek, +}; diff --git a/libavformat/mvi.c b/libavformat/mvi.c index 69b0aed571..5efc443152 100644 --- a/libavformat/mvi.c +++ b/libavformat/mvi.c @@ -2,20 +2,20 @@ * Motion Pixels MVI Demuxer * Copyright (c) 2008 Gregory Montoir (cyx@users.sourceforge.net) * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -52,8 +52,8 @@ static int read_header(AVFormatContext *s) if (!vst) return AVERROR(ENOMEM); - vst->codec->extradata_size = 2; - vst->codec->extradata = av_mallocz(2 + FF_INPUT_BUFFER_PADDING_SIZE); + if (ff_alloc_extradata(vst->codec, 2)) + return AVERROR(ENOMEM); version = avio_r8(pb); vst->codec->extradata[0] = avio_r8(pb); diff --git a/libavformat/mxf.c b/libavformat/mxf.c index 040d8a26c1..4a4158a1d3 100644 --- a/libavformat/mxf.c +++ b/libavformat/mxf.c @@ -2,20 +2,20 @@ * MXF * Copyright (c) 2006 SmartJog S.A., Baptiste Coudurier <baptiste dot coudurier at smartjog dot com> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -41,9 +41,11 @@ const MXFCodecUL ff_mxf_codec_uls[] = { { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x02,0x01,0x02,0x00 }, 13, AV_CODEC_ID_DVVIDEO }, /* DV25 IEC PAL */ { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x07,0x04,0x01,0x02,0x02,0x03,0x01,0x01,0x00 }, 14, AV_CODEC_ID_JPEG2000 }, /* JPEG2000 Codestream */ { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x01,0x7F,0x00,0x00,0x00 }, 13, AV_CODEC_ID_RAWVIDEO }, /* Uncompressed */ + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0A,0x04,0x01,0x02,0x01,0x01,0x02,0x01,0x00 }, 15, AV_CODEC_ID_RAWVIDEO }, /* Uncompressed 422 8-bit */ + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x71,0x00,0x00,0x00 }, 13, AV_CODEC_ID_DNXHD }, /* SMPTE VC-3/DNxHD */ { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x03,0x02,0x00,0x00 }, 14, AV_CODEC_ID_DNXHD }, /* SMPTE VC-3/DNxHD */ { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0A,0x04,0x01,0x02,0x02,0x01,0x32,0x00,0x00 }, 14, AV_CODEC_ID_H264 }, /* H.264/MPEG-4 AVC Intra */ - { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0A,0x04,0x01,0x02,0x01,0x01,0x02,0x02,0x00 }, 15, AV_CODEC_ID_V210 }, /* V210 */ + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0A,0x04,0x01,0x02,0x01,0x01,0x02,0x02,0x01 }, 16, AV_CODEC_ID_V210 }, /* V210 */ /* SoundEssenceCompression */ { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x02,0x02,0x01,0x00,0x00,0x00,0x00 }, 13, AV_CODEC_ID_PCM_S16LE }, /* Uncompressed */ { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x02,0x02,0x01,0x7F,0x00,0x00,0x00 }, 13, AV_CODEC_ID_PCM_S16LE }, @@ -104,3 +106,32 @@ int ff_mxf_decode_pixel_layout(const char pixel_layout[16], enum AVPixelFormat * return -1; } + +static const MXFSamplesPerFrame mxf_samples_per_frames[] = { + { { 1001, 24000 }, { 2002, 0, 0, 0, 0, 0 } }, // FILM 23.976 + { { 1, 24}, { 2000, 0, 0, 0, 0, 0 } }, // FILM 24 + { { 1001, 30000 }, { 1602, 1601, 1602, 1601, 1602, 0 } }, // NTSC 29.97 + { { 1001, 60000 }, { 801, 801, 801, 801, 800, 0 } }, // NTSC 59.94 + { { 1, 25 }, { 1920, 0, 0, 0, 0, 0 } }, // PAL 25 + { { 1, 50 }, { 960, 0, 0, 0, 0, 0 } }, // PAL 50 +}; + +const MXFSamplesPerFrame *ff_mxf_get_samples_per_frame(AVFormatContext *s, AVRational time_base) +{ + int i; + for (i = 0; i < FF_ARRAY_ELEMS(mxf_samples_per_frames); i++) { + if (!av_cmp_q(mxf_samples_per_frames[i].time_base, time_base)) + return &mxf_samples_per_frames[i]; + } + + // Find closest container time base for approximative codec time base like 1/29.97, 1/30, ... + for (i = 0; i < FF_ARRAY_ELEMS(mxf_samples_per_frames); i++) { + if (fabs(av_q2d(mxf_samples_per_frames[i].time_base) - av_q2d(time_base)) < 0.0001) { + av_log(s, AV_LOG_WARNING, "%d/%d input time base matched %d/%d container time base\n", + time_base.num, time_base.den, + mxf_samples_per_frames[i].time_base.num, mxf_samples_per_frames[i].time_base.den); + return &mxf_samples_per_frames[i]; + } + } + return NULL; +} diff --git a/libavformat/mxf.h b/libavformat/mxf.h index 773f30f3a2..4c751e8eef 100644 --- a/libavformat/mxf.h +++ b/libavformat/mxf.h @@ -2,25 +2,26 @@ * MXF * Copyright (c) 2006 SmartJog S.A., Baptiste Coudurier <baptiste dot coudurier at smartjog dot com> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef AVFORMAT_MXF_H #define AVFORMAT_MXF_H +#include "avformat.h" #include "libavcodec/avcodec.h" #include <stdint.h> @@ -47,11 +48,11 @@ enum MXFMetadataSetType { }; enum MXFFrameLayout { - FullFrame = 0, - MixedFields, - OneField, - SegmentedFrame, - SeparateFields + FullFrame = 0, + SeparateFields, + OneField, + MixedFields, + SegmentedFrame, }; typedef struct KLVPacket { @@ -66,11 +67,17 @@ typedef struct MXFCodecUL { int id; } MXFCodecUL; +typedef struct { + struct AVRational time_base; + int samples_per_frame[6]; +} MXFSamplesPerFrame; + extern const MXFCodecUL ff_mxf_data_definition_uls[]; extern const MXFCodecUL ff_mxf_codec_uls[]; extern const MXFCodecUL ff_mxf_pixel_format_uls[]; int ff_mxf_decode_pixel_layout(const char pixel_layout[16], enum AVPixelFormat *pix_fmt); +const MXFSamplesPerFrame *ff_mxf_get_samples_per_frame(AVFormatContext *s, AVRational time_base); #define PRINT_KEY(pc, s, x) av_dlog(pc, "%s %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n", s, \ (x)[0], (x)[1], (x)[2], (x)[3], (x)[4], (x)[5], (x)[6], (x)[7], (x)[8], (x)[9], (x)[10], (x)[11], (x)[12], (x)[13], (x)[14], (x)[15]) diff --git a/libavformat/mxfdec.c b/libavformat/mxfdec.c index dedf636e17..d0cbeead96 100644 --- a/libavformat/mxfdec.c +++ b/libavformat/mxfdec.c @@ -2,20 +2,20 @@ * MXF demuxer. * Copyright (c) 2006 SmartJog S.A., Baptiste Coudurier <baptiste dot coudurier at smartjog dot com> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -44,8 +44,10 @@ */ #include "libavutil/aes.h" +#include "libavutil/avassert.h" #include "libavutil/mathematics.h" #include "libavcodec/bytestream.h" +#include "libavutil/timecode.h" #include "avformat.h" #include "internal.h" #include "mxf.h" @@ -67,7 +69,7 @@ typedef enum { OP3b, OP3c, OPAtom, - OPSonyOpt, /* FATE sample, violates the spec in places */ + OPSONYOpt, /* FATE sample, violates the spec in places */ } MXFOP; typedef struct { @@ -114,12 +116,23 @@ typedef struct { typedef struct { UID uid; enum MXFMetadataSetType type; + int drop_frame; + int start_frame; + struct AVRational rate; + AVTimecode tc; +} MXFTimecodeComponent; + +typedef struct { + UID uid; + enum MXFMetadataSetType type; MXFSequence *sequence; /* mandatory, and only one */ UID sequence_ref; int track_id; uint8_t track_number[4]; AVRational edit_rate; int intra_only; + uint64_t sample_count; + int64_t original_duration; ///< duration before multiplying st->duration by SampleRate/EditRate } MXFTrack; typedef struct { @@ -134,6 +147,7 @@ typedef struct { int frame_layout; /* See MXFFrameLayout enum */ int channels; int bits_per_sample; + int field_dominance; unsigned int component_depth; unsigned int horiz_subsampling; unsigned int vert_subsampling; @@ -199,6 +213,7 @@ typedef struct { struct AVAES *aesc; uint8_t *local_tags; int local_tags_count; + uint64_t last_partition; uint64_t footer_partition; KLVPacket current_klv_data; int current_klv_index; @@ -228,6 +243,8 @@ typedef struct { enum MXFMetadataSetType type; } MXFMetadataReadTableEntry; +static int mxf_read_close(AVFormatContext *s); + /* partial keys to match */ static const uint8_t mxf_header_partition_pack_key[] = { 0x06,0x0e,0x2b,0x34,0x02,0x05,0x01,0x01,0x0d,0x01,0x02,0x01,0x01,0x02 }; static const uint8_t mxf_essence_element_key[] = { 0x06,0x0e,0x2b,0x34,0x01,0x02,0x01,0x01,0x0d,0x01,0x03,0x01 }; @@ -238,6 +255,7 @@ static const uint8_t mxf_klv_key[] = { 0x06,0x0e,0x2b,0x static const uint8_t mxf_crypto_source_container_ul[] = { 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x09,0x06,0x01,0x01,0x02,0x02,0x00,0x00,0x00 }; static const uint8_t mxf_encrypted_triplet_key[] = { 0x06,0x0e,0x2b,0x34,0x02,0x04,0x01,0x07,0x0d,0x01,0x03,0x01,0x02,0x7e,0x01,0x00 }; static const uint8_t mxf_encrypted_essence_container[] = { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x07,0x0d,0x01,0x03,0x01,0x02,0x0b,0x01,0x00 }; +static const uint8_t mxf_random_index_pack_key[] = { 0x06,0x0E,0x2B,0x34,0x02,0x05,0x01,0x01,0x0D,0x01,0x02,0x01,0x01,0x11,0x01,0x00 }; static const uint8_t mxf_sony_mpeg4_extradata[] = { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x01,0x0e,0x06,0x06,0x02,0x02,0x01,0x00,0x00 }; #define IS_KLV_KEY(x, y) (!memcmp(x, y, sizeof(y))) @@ -260,7 +278,7 @@ static int64_t klv_decode_ber_length(AVIOContext *pb) static int mxf_read_sync(AVIOContext *pb, const uint8_t *key, unsigned size) { int i, b; - for (i = 0; i < size && !pb->eof_reached; i++) { + for (i = 0; i < size && !url_feof(pb); i++) { b = avio_r8(pb); if (b == key[0]) i = 0; @@ -310,7 +328,7 @@ static int mxf_get_d10_aes3_packet(AVIOContext *pb, AVStream *st, AVPacket *pkt, data_ptr = pkt->data; end_ptr = pkt->data + length; buf_ptr = pkt->data + 4; /* skip SMPTE 331M header */ - for (; buf_ptr + st->codec->channels*4 < end_ptr; ) { + for (; buf_ptr + st->codec->channels*4 <= end_ptr; ) { for (i = 0; i < st->codec->channels; i++) { uint32_t sample = bytestream_get_le32(&buf_ptr); if (st->codec->bits_per_coded_sample == 24) @@ -397,12 +415,14 @@ static int mxf_read_primer_pack(void *arg, AVIOContext *pb, int tag, int size, U avpriv_request_sample(pb, "Primer pack item length %d", item_len); return AVERROR_PATCHWELCOME; } - if (item_num > UINT_MAX / item_len) + if (item_num > 65536) { + av_log(mxf->fc, AV_LOG_ERROR, "item_num %d is too large\n", item_num); return AVERROR_INVALIDDATA; - mxf->local_tags_count = item_num; - mxf->local_tags = av_malloc(item_num*item_len); + } + mxf->local_tags = av_calloc(item_num, item_len); if (!mxf->local_tags) return AVERROR(ENOMEM); + mxf->local_tags_count = item_num; avio_read(pb, mxf->local_tags, item_num*item_len); return 0; } @@ -410,20 +430,15 @@ static int mxf_read_primer_pack(void *arg, AVIOContext *pb, int tag, int size, U static int mxf_read_partition_pack(void *arg, AVIOContext *pb, int tag, int size, UID uid, int64_t klv_offset) { MXFContext *mxf = arg; - MXFPartition *partition; + MXFPartition *partition, *tmp_part; UID op; uint64_t footer_partition; uint32_t nb_essence_containers; - int err; - if (mxf->partitions_count+1 >= UINT_MAX / sizeof(*mxf->partitions)) + tmp_part = av_realloc_array(mxf->partitions, mxf->partitions_count + 1, sizeof(*mxf->partitions)); + if (!tmp_part) return AVERROR(ENOMEM); - - if ((err = av_reallocp_array(&mxf->partitions, mxf->partitions_count + 1, - sizeof(*mxf->partitions))) < 0) { - mxf->partitions_count = 0; - return err; - } + mxf->partitions = tmp_part; if (mxf->parsing_backward) { /* insert the new partition pack in the middle @@ -508,7 +523,7 @@ static int mxf_read_partition_pack(void *arg, AVIOContext *pb, int tag, int size else if (op[12] == 3 && op[13] == 1) mxf->op = OP3a; else if (op[12] == 3 && op[13] == 2) mxf->op = OP3b; else if (op[12] == 3 && op[13] == 3) mxf->op = OP3c; - else if (op[12] == 64&& op[13] == 1) mxf->op = OPSonyOpt; + else if (op[12] == 64&& op[13] == 1) mxf->op = OPSONYOpt; else if (op[12] == 0x10) { /* SMPTE 390m: "There shall be exactly one essence container" * The following block deals with files that violate this, namely: @@ -519,10 +534,8 @@ static int mxf_read_partition_pack(void *arg, AVIOContext *pb, int tag, int size /* only nag once */ if (!mxf->op) - av_log(mxf->fc, AV_LOG_WARNING, - "\"OPAtom\" with %u ECs - assuming %s\n", - nb_essence_containers, - op == OP1a ? "OP1a" : "OPAtom"); + av_log(mxf->fc, AV_LOG_WARNING, "\"OPAtom\" with %u ECs - assuming %s\n", + nb_essence_containers, op == OP1a ? "OP1a" : "OPAtom"); mxf->op = op; } else @@ -535,7 +548,7 @@ static int mxf_read_partition_pack(void *arg, AVIOContext *pb, int tag, int size if (partition->kag_size <= 0 || partition->kag_size > (1 << 20)) { av_log(mxf->fc, AV_LOG_WARNING, "invalid KAGSize %i - guessing ", partition->kag_size); - if (mxf->op == OPSonyOpt) + if (mxf->op == OPSONYOpt) partition->kag_size = 512; else partition->kag_size = 1; @@ -548,15 +561,12 @@ static int mxf_read_partition_pack(void *arg, AVIOContext *pb, int tag, int size static int mxf_add_metadata_set(MXFContext *mxf, void *metadata_set) { - int err; + MXFMetadataSet **tmp; - if (mxf->metadata_sets_count+1 >= UINT_MAX / sizeof(*mxf->metadata_sets)) + tmp = av_realloc_array(mxf->metadata_sets, mxf->metadata_sets_count + 1, sizeof(*mxf->metadata_sets)); + if (!tmp) return AVERROR(ENOMEM); - if ((err = av_reallocp_array(&mxf->metadata_sets, mxf->metadata_sets_count + 1, - sizeof(*mxf->metadata_sets))) < 0) { - mxf->metadata_sets_count = 0; - return err; - } + mxf->metadata_sets = tmp; mxf->metadata_sets[mxf->metadata_sets_count] = metadata_set; mxf->metadata_sets_count++; return 0; @@ -578,9 +588,7 @@ static int mxf_read_content_storage(void *arg, AVIOContext *pb, int tag, int siz switch (tag) { case 0x1901: mxf->packages_count = avio_rb32(pb); - if (mxf->packages_count >= UINT_MAX / sizeof(UID)) - return AVERROR_INVALIDDATA; - mxf->packages_refs = av_malloc(mxf->packages_count * sizeof(UID)); + mxf->packages_refs = av_calloc(mxf->packages_count, sizeof(UID)); if (!mxf->packages_refs) return AVERROR(ENOMEM); avio_skip(pb, 4); /* useless size of objects, always 16 according to specs */ @@ -618,9 +626,7 @@ static int mxf_read_material_package(void *arg, AVIOContext *pb, int tag, int si switch(tag) { case 0x4403: package->tracks_count = avio_rb32(pb); - if (package->tracks_count >= UINT_MAX / sizeof(UID)) - return AVERROR_INVALIDDATA; - package->tracks_refs = av_malloc(package->tracks_count * sizeof(UID)); + package->tracks_refs = av_calloc(package->tracks_count, sizeof(UID)); if (!package->tracks_refs) return AVERROR(ENOMEM); avio_skip(pb, 4); /* useless size of objects, always 16 according to specs */ @@ -630,6 +636,23 @@ static int mxf_read_material_package(void *arg, AVIOContext *pb, int tag, int si return 0; } +static int mxf_read_timecode_component(void *arg, AVIOContext *pb, int tag, int size, UID uid, int64_t klv_offset) +{ + MXFTimecodeComponent *mxf_timecode = arg; + switch(tag) { + case 0x1501: + mxf_timecode->start_frame = avio_rb64(pb); + break; + case 0x1502: + mxf_timecode->rate = (AVRational){avio_rb16(pb), 1}; + break; + case 0x1503: + mxf_timecode->drop_frame = avio_r8(pb); + break; + } + return 0; +} + static int mxf_read_track(void *arg, AVIOContext *pb, int tag, int size, UID uid, int64_t klv_offset) { MXFTrack *track = arg; @@ -663,9 +686,7 @@ static int mxf_read_sequence(void *arg, AVIOContext *pb, int tag, int size, UID break; case 0x1001: sequence->structural_components_count = avio_rb32(pb); - if (sequence->structural_components_count >= UINT_MAX / sizeof(UID)) - return AVERROR_INVALIDDATA; - sequence->structural_components_refs = av_malloc(sequence->structural_components_count * sizeof(UID)); + sequence->structural_components_refs = av_calloc(sequence->structural_components_count, sizeof(UID)); if (!sequence->structural_components_refs) return AVERROR(ENOMEM); avio_skip(pb, 4); /* useless size of objects, always 16 according to specs */ @@ -681,9 +702,7 @@ static int mxf_read_source_package(void *arg, AVIOContext *pb, int tag, int size switch(tag) { case 0x4403: package->tracks_count = avio_rb32(pb); - if (package->tracks_count >= UINT_MAX / sizeof(UID)) - return AVERROR_INVALIDDATA; - package->tracks_refs = av_malloc(package->tracks_count * sizeof(UID)); + package->tracks_refs = av_calloc(package->tracks_count, sizeof(UID)); if (!package->tracks_refs) return AVERROR(ENOMEM); avio_skip(pb, 4); /* useless size of objects, always 16 according to specs */ @@ -706,29 +725,13 @@ static int mxf_read_index_entry_array(AVIOContext *pb, MXFIndexTableSegment *seg int i, length; segment->nb_index_entries = avio_rb32(pb); - if (!segment->nb_index_entries) - return 0; - else if (segment->nb_index_entries < 0 || - segment->nb_index_entries > - (INT_MAX / sizeof(*segment->stream_offset_entries))) - return AVERROR(ENOMEM); length = avio_rb32(pb); - segment->temporal_offset_entries = av_mallocz(segment->nb_index_entries * - sizeof(*segment->temporal_offset_entries)); - segment->flag_entries = av_mallocz(segment->nb_index_entries * - sizeof(*segment->flag_entries)); - segment->stream_offset_entries = av_mallocz(segment->nb_index_entries * - sizeof(*segment->stream_offset_entries)); - - if (!segment->flag_entries || !segment->stream_offset_entries || - !segment->temporal_offset_entries) { - av_freep(&segment->flag_entries); - av_freep(&segment->stream_offset_entries); - av_freep(&segment->temporal_offset_entries); + if (!(segment->temporal_offset_entries=av_calloc(segment->nb_index_entries, sizeof(*segment->temporal_offset_entries))) || + !(segment->flag_entries = av_calloc(segment->nb_index_entries, sizeof(*segment->flag_entries))) || + !(segment->stream_offset_entries = av_calloc(segment->nb_index_entries, sizeof(*segment->stream_offset_entries)))) return AVERROR(ENOMEM); - } for (i = 0; i < segment->nb_index_entries; i++) { segment->temporal_offset_entries[i] = avio_r8(pb); @@ -780,17 +783,18 @@ static int mxf_read_index_table_segment(void *arg, AVIOContext *pb, int tag, int static void mxf_read_pixel_layout(AVIOContext *pb, MXFDescriptor *descriptor) { int code, value, ofs = 0; - char layout[16] = {0}; + char layout[16] = {0}; /* not for printing, may end up not terminated on purpose */ do { code = avio_r8(pb); value = avio_r8(pb); av_dlog(NULL, "pixel layout: code %#x\n", code); - if (ofs < 16) { + if (ofs <= 14) { layout[ofs++] = code; layout[ofs++] = value; - } + } else + break; /* don't read byte by byte on sneaky files filled with lots of non-zeroes */ } while (code != 0); /* SMPTE 377M E.2.46 */ ff_mxf_decode_pixel_layout(layout, &descriptor->pix_fmt); @@ -803,9 +807,7 @@ static int mxf_read_generic_descriptor(void *arg, AVIOContext *pb, int tag, int switch(tag) { case 0x3F01: descriptor->sub_descriptors_count = avio_rb32(pb); - if (descriptor->sub_descriptors_count >= UINT_MAX / sizeof(UID)) - return AVERROR_INVALIDDATA; - descriptor->sub_descriptors_refs = av_malloc(descriptor->sub_descriptors_count * sizeof(UID)); + descriptor->sub_descriptors_refs = av_calloc(descriptor->sub_descriptors_count, sizeof(UID)); if (!descriptor->sub_descriptors_refs) return AVERROR(ENOMEM); avio_skip(pb, 4); /* useless size of objects, always 16 according to specs */ @@ -833,6 +835,9 @@ static int mxf_read_generic_descriptor(void *arg, AVIOContext *pb, int tag, int descriptor->aspect_ratio.num = avio_rb32(pb); descriptor->aspect_ratio.den = avio_rb32(pb); break; + case 0x3212: + descriptor->field_dominance = avio_r8(pb); + break; case 0x3301: descriptor->component_depth = avio_rb32(pb); break; @@ -861,7 +866,11 @@ static int mxf_read_generic_descriptor(void *arg, AVIOContext *pb, int tag, int default: /* Private uid used by SONY C0023S01.mxf */ if (IS_KLV_KEY(uid, mxf_sony_mpeg4_extradata)) { - descriptor->extradata = av_malloc(size + FF_INPUT_BUFFER_PADDING_SIZE); + if (descriptor->extradata) + av_log(NULL, AV_LOG_WARNING, "Duplicate sony_mpeg4_extradata\n"); + av_free(descriptor->extradata); + descriptor->extradata_size = 0; + descriptor->extradata = av_malloc(size); if (!descriptor->extradata) return AVERROR(ENOMEM); descriptor->extradata_size = size; @@ -955,9 +964,8 @@ static int mxf_get_sorted_table_segments(MXFContext *mxf, int *nb_sorted_segment if (!nb_segments) return AVERROR_INVALIDDATA; - *sorted_segments = av_mallocz(nb_segments * sizeof(**sorted_segments)); - unsorted_segments = av_mallocz(nb_segments * sizeof(*unsorted_segments)); - if (!*sorted_segments || !unsorted_segments) { + if (!(unsorted_segments = av_calloc(nb_segments, sizeof(*unsorted_segments))) || + !(*sorted_segments = av_calloc(nb_segments, sizeof(**sorted_segments)))) { av_freep(sorted_segments); av_free(unsorted_segments); return AVERROR(ENOMEM); @@ -972,19 +980,23 @@ static int mxf_get_sorted_table_segments(MXFContext *mxf, int *nb_sorted_segment /* sort segments by {BodySID, IndexSID, IndexStartPosition}, remove duplicates while we're at it */ for (i = 0; i < nb_segments; i++) { int best = -1, best_body_sid = -1, best_index_sid = -1, best_index_start = -1; + uint64_t best_index_duration = 0; for (j = 0; j < nb_segments; j++) { MXFIndexTableSegment *s = unsorted_segments[j]; /* Require larger BosySID, IndexSID or IndexStartPosition then the previous entry. This removes duplicates. * We want the smallest values for the keys than what we currently have, unless this is the first such entry this time around. + * If we come across an entry with the same IndexStartPosition but larger IndexDuration, then we'll prefer it over the one we currently have. */ if ((i == 0 || s->body_sid > last_body_sid || s->index_sid > last_index_sid || s->index_start_position > last_index_start) && - (best == -1 || s->body_sid < best_body_sid || s->index_sid < best_index_sid || s->index_start_position < best_index_start)) { + (best == -1 || s->body_sid < best_body_sid || s->index_sid < best_index_sid || s->index_start_position < best_index_start || + (s->index_start_position == best_index_start && s->index_duration > best_index_duration))) { best = j; best_body_sid = s->body_sid; best_index_sid = s->index_sid; best_index_start = s->index_start_position; + best_index_duration = s->index_duration; } } @@ -1125,14 +1137,8 @@ static int mxf_compute_ptses_fake_index(MXFContext *mxf, MXFIndexTable *index_ta if (index_table->nb_ptses <= 0) return 0; - if (index_table->nb_ptses > INT_MAX / sizeof(AVIndexEntry)) - return AVERROR(ENOMEM); - - index_table->ptses = av_mallocz(index_table->nb_ptses * - sizeof(int64_t)); - index_table->fake_index = av_mallocz(index_table->nb_ptses * - sizeof(AVIndexEntry)); - if (!index_table->ptses || !index_table->fake_index) { + if (!(index_table->ptses = av_calloc(index_table->nb_ptses, sizeof(int64_t))) || + !(index_table->fake_index = av_calloc(index_table->nb_ptses, sizeof(AVIndexEntry)))) { av_freep(&index_table->ptses); return AVERROR(ENOMEM); } @@ -1237,9 +1243,7 @@ static int mxf_compute_index_tables(MXFContext *mxf) } } - if (mxf->nb_index_tables > INT_MAX / sizeof(MXFIndexTable) || - !(mxf->index_tables = av_mallocz(mxf->nb_index_tables * - sizeof(MXFIndexTable)))) { + if (!(mxf->index_tables = av_calloc(mxf->nb_index_tables, sizeof(MXFIndexTable)))) { av_log(mxf->fc, AV_LOG_ERROR, "failed to allocate index tables\n"); ret = AVERROR(ENOMEM); goto finish_decoding_index; @@ -1258,12 +1262,8 @@ static int mxf_compute_index_tables(MXFContext *mxf) for (i = j = 0; j < mxf->nb_index_tables; i += mxf->index_tables[j++].nb_segments) { MXFIndexTable *t = &mxf->index_tables[j]; - if (t->nb_segments > - (INT_MAX / sizeof(MXFIndexTableSegment *)) || - !(t->segments = av_mallocz(t->nb_segments * - sizeof(MXFIndexTableSegment*)))) { - av_log(mxf->fc, AV_LOG_ERROR, "failed to allocate IndexTableSegment" - " pointer array\n"); + if (!(t->segments = av_calloc(t->nb_segments, sizeof(MXFIndexTableSegment*)))) { + av_log(mxf->fc, AV_LOG_ERROR, "failed to allocate IndexTableSegment pointer array\n"); ret = AVERROR(ENOMEM); goto finish_decoding_index; } @@ -1307,12 +1307,20 @@ finish_decoding_index: return ret; } -static int mxf_is_intra_only(MXFDescriptor *d) +static int mxf_is_intra_only(MXFDescriptor *descriptor) { return mxf_get_codec_ul(mxf_intra_only_essence_container_uls, - &d->essence_container_ul)->id != AV_CODEC_ID_NONE || + &descriptor->essence_container_ul)->id != AV_CODEC_ID_NONE || mxf_get_codec_ul(mxf_intra_only_picture_essence_coding_uls, - &d->essence_codec_ul)->id != AV_CODEC_ID_NONE; + &descriptor->essence_codec_ul)->id != AV_CODEC_ID_NONE; +} + +static int mxf_add_timecode_metadata(AVDictionary **pm, const char *key, AVTimecode *tc) +{ + char buf[AV_TIMECODE_STR_SIZE]; + av_dict_set(pm, key, av_timecode_make_string(tc, buf, 0), 0); + + return 0; } static int mxf_parse_structural_metadata(MXFContext *mxf) @@ -1339,25 +1347,48 @@ static int mxf_parse_structural_metadata(MXFContext *mxf) MXFTrack *temp_track = NULL; MXFDescriptor *descriptor = NULL; MXFStructuralComponent *component = NULL; + MXFTimecodeComponent *mxf_tc = NULL; UID *essence_container_ul = NULL; const MXFCodecUL *codec_ul = NULL; const MXFCodecUL *container_ul = NULL; const MXFCodecUL *pix_fmt_ul = NULL; AVStream *st; + AVTimecode tc; + int flags; if (!(material_track = mxf_resolve_strong_ref(mxf, &material_package->tracks_refs[i], Track))) { av_log(mxf->fc, AV_LOG_ERROR, "could not resolve material track strong ref\n"); continue; } + if ((component = mxf_resolve_strong_ref(mxf, &material_track->sequence_ref, TimecodeComponent))) { + mxf_tc = (MXFTimecodeComponent*)component; + flags = mxf_tc->drop_frame == 1 ? AV_TIMECODE_FLAG_DROPFRAME : 0; + if (av_timecode_init(&tc, mxf_tc->rate, flags, mxf_tc->start_frame, mxf->fc) == 0) { + mxf_add_timecode_metadata(&mxf->fc->metadata, "timecode", &tc); + } + } + if (!(material_track->sequence = mxf_resolve_strong_ref(mxf, &material_track->sequence_ref, Sequence))) { av_log(mxf->fc, AV_LOG_ERROR, "could not resolve material track sequence strong ref\n"); continue; } + for (j = 0; j < material_track->sequence->structural_components_count; j++) { + component = mxf_resolve_strong_ref(mxf, &material_track->sequence->structural_components_refs[j], TimecodeComponent); + if (!component) + continue; + + mxf_tc = (MXFTimecodeComponent*)component; + flags = mxf_tc->drop_frame == 1 ? AV_TIMECODE_FLAG_DROPFRAME : 0; + if (av_timecode_init(&tc, mxf_tc->rate, flags, mxf_tc->start_frame, mxf->fc) == 0) { + mxf_add_timecode_metadata(&mxf->fc->metadata, "timecode", &tc); + break; + } + } + /* TODO: handle multiple source clips */ for (j = 0; j < material_track->sequence->structural_components_count; j++) { - /* TODO: handle timecode component */ component = mxf_resolve_strong_ref(mxf, &material_track->sequence->structural_components_refs[j], SourceClip); if (!component) continue; @@ -1415,12 +1446,21 @@ static int mxf_parse_structural_metadata(MXFContext *mxf) } st->id = source_track->track_id; st->priv_data = source_track; - st->duration = component->duration; + source_track->original_duration = st->duration = component->duration; if (st->duration == -1) st->duration = AV_NOPTS_VALUE; st->start_time = component->start_position; + if (material_track->edit_rate.num <= 0 || material_track->edit_rate.den <= 0) { + av_log(mxf->fc, AV_LOG_WARNING, + "invalid edit rate (%d/%d) found on stream #%d, defaulting to 25/1\n", + material_track->edit_rate.num, material_track->edit_rate.den, st->index); + material_track->edit_rate = (AVRational){25, 1}; + } avpriv_set_pts_info(st, 64, material_track->edit_rate.den, material_track->edit_rate.num); + /* ensure SourceTrack EditRate == MaterialTrack EditRate since only the former is accessible via st->priv_data */ + source_track->edit_rate = material_track->edit_rate; + PRINT_KEY(mxf->fc, "data definition ul", source_track->sequence->data_definition_ul); codec_ul = mxf_get_codec_ul(ff_mxf_data_definition_uls, &source_track->sequence->data_definition_ul); st->codec->codec_type = codec_ul->id; @@ -1465,51 +1505,51 @@ static int mxf_parse_structural_metadata(MXFContext *mxf) /* TODO: drop PictureEssenceCoding and SoundEssenceCompression, only check EssenceContainer */ codec_ul = mxf_get_codec_ul(ff_mxf_codec_uls, &descriptor->essence_codec_ul); - st->codec->codec_id = codec_ul->id; - if (descriptor->extradata) { - st->codec->extradata = descriptor->extradata; - st->codec->extradata_size = descriptor->extradata_size; + st->codec->codec_id = (enum AVCodecID)codec_ul->id; + av_log(mxf->fc, AV_LOG_VERBOSE, "%s: Universal Label: ", + avcodec_get_name(st->codec->codec_id)); + for (k = 0; k < 16; k++) { + av_log(mxf->fc, AV_LOG_VERBOSE, "%.2x", + descriptor->essence_codec_ul[k]); + if (!(k+1 & 19) || k == 5) + av_log(mxf->fc, AV_LOG_VERBOSE, "."); } + av_log(mxf->fc, AV_LOG_VERBOSE, "\n"); if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) { source_track->intra_only = mxf_is_intra_only(descriptor); container_ul = mxf_get_codec_ul(mxf_picture_essence_container_uls, essence_container_ul); if (st->codec->codec_id == AV_CODEC_ID_NONE) st->codec->codec_id = container_ul->id; st->codec->width = descriptor->width; - /* Field height, not frame height */ - st->codec->height = descriptor->height; + st->codec->height = descriptor->height; /* Field height, not frame height */ switch (descriptor->frame_layout) { case SegmentedFrame: /* This one is a weird layout I don't fully understand. */ - av_log(mxf->fc, AV_LOG_INFO, - "SegmentedFrame layout isn't currently supported\n"); + av_log(mxf->fc, AV_LOG_INFO, "SegmentedFrame layout isn't currently supported\n"); break; case FullFrame: + st->codec->field_order = AV_FIELD_PROGRESSIVE; break; case OneField: /* Every other line is stored and needs to be duplicated. */ - av_log(mxf->fc, AV_LOG_INFO, - "OneField frame layout isn't currently supported\n"); + av_log(mxf->fc, AV_LOG_INFO, "OneField frame layout isn't currently supported\n"); + break; /* The correct thing to do here is fall through, but by breaking we might be + able to decode some streams at half the vertical resolution, rather than not al all. + It's also for compatibility with the old behavior. */ + case MixedFields: break; - /* The correct thing to do here is fall through, but by - * breaking we might be able to decode some streams at half - * the vertical resolution, rather than not al all. - * It's also for compatibility with the old behavior. */ case SeparateFields: - case MixedFields: - /* Turn field height into frame height. */ - st->codec->height *= 2; + st->codec->height *= 2; /* Turn field height into frame height. */ + break; default: - av_log(mxf->fc, AV_LOG_INFO, - "Unknown frame layout type: %d\n", - descriptor->frame_layout); + av_log(mxf->fc, AV_LOG_INFO, "Unknown frame layout type: %d\n", descriptor->frame_layout); } if (st->codec->codec_id == AV_CODEC_ID_RAWVIDEO) { st->codec->pix_fmt = descriptor->pix_fmt; if (st->codec->pix_fmt == AV_PIX_FMT_NONE) { pix_fmt_ul = mxf_get_codec_ul(ff_mxf_pixel_format_uls, &descriptor->essence_codec_ul); - st->codec->pix_fmt = pix_fmt_ul->id; + st->codec->pix_fmt = (enum AVPixelFormat)pix_fmt_ul->id; if (st->codec->pix_fmt == AV_PIX_FMT_NONE) { /* support files created before RP224v10 by defaulting to UYVY422 if subsampling is 4:2:2 and component depth is 8-bit */ @@ -1524,8 +1564,9 @@ static int mxf_parse_structural_metadata(MXFContext *mxf) st->need_parsing = AVSTREAM_PARSE_HEADERS; } else if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) { container_ul = mxf_get_codec_ul(mxf_sound_essence_container_uls, essence_container_ul); - if (st->codec->codec_id == AV_CODEC_ID_NONE) - st->codec->codec_id = container_ul->id; + /* Only overwrite existing codec ID if it is unset or A-law, which is the default according to SMPTE RP 224. */ + if (st->codec->codec_id == AV_CODEC_ID_NONE || (st->codec->codec_id == AV_CODEC_ID_PCM_ALAW && (enum AVCodecID)container_ul->id != AV_CODEC_ID_NONE)) + st->codec->codec_id = (enum AVCodecID)container_ul->id; st->codec->channels = descriptor->channels; st->codec->bits_per_coded_sample = descriptor->bits_per_sample; @@ -1540,6 +1581,10 @@ static int mxf_parse_structural_metadata(MXFContext *mxf) avpriv_set_pts_info(st, 64, 1, 48000); } + /* if duration is set, rescale it from EditRate to SampleRate */ + if (st->duration != AV_NOPTS_VALUE) + st->duration = av_rescale_q(st->duration, av_inv_q(material_track->edit_rate), st->time_base); + /* TODO: implement AV_CODEC_ID_RAWAUDIO */ if (st->codec->codec_id == AV_CODEC_ID_PCM_S16LE) { if (descriptor->bits_per_sample > 16 && descriptor->bits_per_sample <= 24) @@ -1555,6 +1600,13 @@ static int mxf_parse_structural_metadata(MXFContext *mxf) st->need_parsing = AVSTREAM_PARSE_FULL; } } + if (descriptor->extradata) { + if (!ff_alloc_extradata(st->codec, descriptor->extradata_size)) { + memcpy(st->codec->extradata, descriptor->extradata, descriptor->extradata_size); + } + } else if(st->codec->codec_id == AV_CODEC_ID_H264) { + ff_generate_avci_extradata(st); + } if (st->codec->codec_type != AVMEDIA_TYPE_DATA && (*essence_container_ul)[15] > 0x01) { /* TODO: decode timestamps */ st->need_parsing = AVSTREAM_PARSE_TIMESTAMPS; @@ -1566,6 +1618,124 @@ fail_and_free: return ret; } +static int mxf_read_utf16_string(AVIOContext *pb, int size, char** str) +{ + int ret; + size_t buf_size; + + if (size < 0) + return AVERROR(EINVAL); + + buf_size = size + size/2 + 1; + *str = av_malloc(buf_size); + if (!*str) + return AVERROR(ENOMEM); + + if ((ret = avio_get_str16be(pb, size, *str, buf_size)) < 0) { + av_freep(str); + return ret; + } + + return ret; +} + +static int mxf_uid_to_str(UID uid, char **str) +{ + int i; + char *p; + p = *str = av_mallocz(sizeof(UID) * 2 + 4 + 1); + if (!p) + return AVERROR(ENOMEM); + for (i = 0; i < sizeof(UID); i++) { + snprintf(p, 2 + 1, "%.2x", uid[i]); + p += 2; + if (i == 3 || i == 5 || i == 7 || i == 9) { + snprintf(p, 1 + 1, "-"); + p++; + } + } + return 0; +} + +static int mxf_timestamp_to_str(uint64_t timestamp, char **str) +{ + struct tm time = {0}; + time.tm_year = (timestamp >> 48) - 1900; + time.tm_mon = (timestamp >> 40 & 0xFF) - 1; + time.tm_mday = (timestamp >> 32 & 0xFF); + time.tm_hour = (timestamp >> 24 & 0xFF); + time.tm_min = (timestamp >> 16 & 0xFF); + time.tm_sec = (timestamp >> 8 & 0xFF); + + /* ensure month/day are valid */ + time.tm_mon = FFMAX(time.tm_mon, 0); + time.tm_mday = FFMAX(time.tm_mday, 1); + + *str = av_mallocz(32); + if (!*str) + return AVERROR(ENOMEM); + strftime(*str, 32, "%Y-%m-%d %H:%M:%S", &time); + + return 0; +} + +#define SET_STR_METADATA(pb, name, str) do { \ + if ((ret = mxf_read_utf16_string(pb, size, &str)) < 0) \ + return ret; \ + av_dict_set(&s->metadata, name, str, AV_DICT_DONT_STRDUP_VAL); \ +} while (0) + +#define SET_UID_METADATA(pb, name, var, str) do { \ + avio_read(pb, var, 16); \ + if ((ret = mxf_uid_to_str(var, &str)) < 0) \ + return ret; \ + av_dict_set(&s->metadata, name, str, AV_DICT_DONT_STRDUP_VAL); \ +} while (0) + +#define SET_TS_METADATA(pb, name, var, str) do { \ + var = avio_rb64(pb); \ + if ((ret = mxf_timestamp_to_str(var, &str)) < 0) \ + return ret; \ + av_dict_set(&s->metadata, name, str, AV_DICT_DONT_STRDUP_VAL); \ +} while (0) + +static int mxf_read_identification_metadata(void *arg, AVIOContext *pb, int tag, int size, UID _uid, int64_t klv_offset) +{ + MXFContext *mxf = arg; + AVFormatContext *s = mxf->fc; + int ret; + UID uid = { 0 }; + char *str = NULL; + uint64_t ts; + switch (tag) { + case 0x3C01: + SET_STR_METADATA(pb, "company_name", str); + break; + case 0x3C02: + SET_STR_METADATA(pb, "product_name", str); + break; + case 0x3C04: + SET_STR_METADATA(pb, "product_version", str); + break; + case 0x3C05: + SET_UID_METADATA(pb, "product_uid", uid, str); + break; + case 0x3C06: + SET_TS_METADATA(pb, "modification_date", ts, str); + break; + case 0x3C08: + SET_STR_METADATA(pb, "application_platform", str); + break; + case 0x3C09: + SET_UID_METADATA(pb, "generation_uid", uid, str); + break; + case 0x3C0A: + SET_UID_METADATA(pb, "uid", uid, str); + break; + } + return 0; +} + static const MXFMetadataReadTableEntry mxf_metadata_read_table[] = { { { 0x06,0x0E,0x2B,0x34,0x02,0x05,0x01,0x01,0x0d,0x01,0x02,0x01,0x01,0x05,0x01,0x00 }, mxf_read_primer_pack }, { { 0x06,0x0E,0x2B,0x34,0x02,0x05,0x01,0x01,0x0d,0x01,0x02,0x01,0x01,0x02,0x01,0x00 }, mxf_read_partition_pack }, @@ -1578,6 +1748,7 @@ static const MXFMetadataReadTableEntry mxf_metadata_read_table[] = { { { 0x06,0x0E,0x2B,0x34,0x02,0x05,0x01,0x01,0x0d,0x01,0x02,0x01,0x01,0x03,0x04,0x00 }, mxf_read_partition_pack }, { { 0x06,0x0E,0x2B,0x34,0x02,0x05,0x01,0x01,0x0d,0x01,0x02,0x01,0x01,0x04,0x02,0x00 }, mxf_read_partition_pack }, { { 0x06,0x0E,0x2B,0x34,0x02,0x05,0x01,0x01,0x0d,0x01,0x02,0x01,0x01,0x04,0x04,0x00 }, mxf_read_partition_pack }, + { { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0D,0x01,0x01,0x01,0x01,0x01,0x30,0x00 }, mxf_read_identification_metadata }, { { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x18,0x00 }, mxf_read_content_storage, 0, AnyType }, { { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x37,0x00 }, mxf_read_source_package, sizeof(MXFPackage), SourcePackage }, { { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x36,0x00 }, mxf_read_material_package, sizeof(MXFPackage), MaterialPackage }, @@ -1592,6 +1763,7 @@ static const MXFMetadataReadTableEntry mxf_metadata_read_table[] = { { { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x47,0x00 }, mxf_read_generic_descriptor, sizeof(MXFDescriptor), Descriptor }, /* AES3 */ { { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x3A,0x00 }, mxf_read_track, sizeof(MXFTrack), Track }, /* Static Track */ { { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x3B,0x00 }, mxf_read_track, sizeof(MXFTrack), Track }, /* Generic Track */ + { { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x14,0x00 }, mxf_read_timecode_component, sizeof(MXFTimecodeComponent), TimecodeComponent }, { { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x04,0x01,0x02,0x02,0x00,0x00 }, mxf_read_cryptographic_context, sizeof(MXFCryptoContext), CryptoContext }, { { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x02,0x01,0x01,0x10,0x01,0x00 }, mxf_read_index_table_segment, sizeof(MXFIndexTableSegment), IndexTableSegment }, { { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, NULL, 0, AnyType }, @@ -1605,7 +1777,7 @@ static int mxf_read_local_tags(MXFContext *mxf, KLVPacket *klv, MXFMetadataReadF if (!ctx) return AVERROR(ENOMEM); - while (avio_tell(pb) + 4 < klv_end && !pb->eof_reached) { + while (avio_tell(pb) + 4 < klv_end && !url_feof(pb)) { int ret; int tag = avio_rb16(pb); int size = avio_rb16(pb); /* KLV specified by 0x53 */ @@ -1636,6 +1808,9 @@ static int mxf_read_local_tags(MXFContext *mxf, KLVPacket *klv, MXFMetadataReadF /* Accept the 64k local set limit being exceeded (Avid). Don't accept * it extending past the end of the KLV though (zzuf5.mxf). */ if (avio_tell(pb) > klv_end) { + if (ctx_size) + av_free(ctx); + av_log(mxf->fc, AV_LOG_ERROR, "local tag %#04x extends past end of local set @ %#"PRIx64"\n", tag, klv->offset); @@ -1679,31 +1854,33 @@ static int mxf_parse_handle_essence(MXFContext *mxf) if (mxf->parsing_backward) { return mxf_seek_to_previous_partition(mxf); - } else { - if (!mxf->footer_partition) { - av_dlog(mxf->fc, "no footer\n"); - return 0; - } + } else if (mxf->footer_partition || mxf->last_partition){ + uint64_t offset; - av_dlog(mxf->fc, "seeking to footer\n"); + offset = mxf->footer_partition ? mxf->footer_partition : mxf->last_partition; + + av_dlog(mxf->fc, "seeking to last partition\n"); /* remember where we were so we don't end up seeking further back than this */ mxf->last_forward_tell = avio_tell(pb); if (!pb->seekable) { - av_log(mxf->fc, AV_LOG_INFO, "file is not seekable - not parsing footer\n"); + av_log(mxf->fc, AV_LOG_INFO, "file is not seekable - not parsing last partition\n"); return -1; } - /* seek to footer partition and parse backward */ - if ((ret = avio_seek(pb, mxf->run_in + mxf->footer_partition, SEEK_SET)) < 0) { - av_log(mxf->fc, AV_LOG_ERROR, "failed to seek to footer @ 0x%"PRIx64" (%"PRId64") - partial file?\n", - mxf->run_in + mxf->footer_partition, ret); + /* seek to last partition and parse backward */ + if ((ret = avio_seek(pb, mxf->run_in + offset, SEEK_SET)) < 0) { + av_log(mxf->fc, AV_LOG_ERROR, "failed to seek to last partition @ 0x%"PRIx64" (%"PRId64") - partial file?\n", + mxf->run_in + offset, ret); return ret; } mxf->current_partition = NULL; mxf->parsing_backward = 1; + } else { + av_dlog(mxf->fc, "can't find last partition\n"); + return 0; } return 1; @@ -1719,8 +1896,7 @@ static int mxf_parse_handle_partition_or_eof(MXFContext *mxf) } /** - * Figure out the proper offset and length of the essence container - * in each partition + * Figures out the proper offset and length of the essence container in each partition */ static void mxf_compute_essence_containers(MXFContext *mxf) { @@ -1760,38 +1936,6 @@ static int64_t round_to_kag(int64_t position, int kag_size) return ret == position ? ret : ret + kag_size; } -static inline void compute_partition_essence_offset(AVFormatContext *s, - MXFContext *mxf, - KLVPacket *klv) -{ - MXFPartition *cur_part = mxf->current_partition; - /* for OP1a we compute essence_offset - * for OPAtom we point essence_offset after the KL - * (usually op1a_essence_offset + 20 or 25) - * TODO: for OP1a we could eliminate this entire if statement, always - * stopping parsing at op1a_essence_offset - * for OPAtom we still need the actual essence_offset though - * (the KL's length can vary) - */ - int64_t op1a_essence_offset = - round_to_kag(cur_part->this_partition + cur_part->pack_length, - cur_part->kag_size) + - round_to_kag(cur_part->header_byte_count, cur_part->kag_size) + - round_to_kag(cur_part->index_byte_count, cur_part->kag_size); - - if (mxf->op == OPAtom) { - /* point essence_offset to the actual data - * OPAtom has all the essence in one big KLV - */ - cur_part->essence_offset = avio_tell(s->pb); - cur_part->essence_length = klv->length; - } else { - /* NOTE: op1a_essence_offset may be less than to klv.offset - * (C0023S01.mxf) */ - cur_part->essence_offset = op1a_essence_offset; - } -} - static int is_pcm(enum AVCodecID codec_id) { /* we only care about "normal" PCM codecs until we get samples */ @@ -1828,6 +1972,34 @@ static void mxf_handle_small_eubc(AVFormatContext *s) mxf->edit_units_per_packet = 1920; } +static void mxf_read_random_index_pack(AVFormatContext *s) +{ + MXFContext *mxf = s->priv_data; + uint32_t length; + int64_t file_size; + KLVPacket klv; + + if (!s->pb->seekable) + return; + + file_size = avio_size(s->pb); + avio_seek(s->pb, file_size - 4, SEEK_SET); + length = avio_rb32(s->pb); + if (length <= 32 || length >= FFMIN(file_size, INT_MAX)) + goto end; + avio_seek(s->pb, file_size - length, SEEK_SET); + if (klv_read_packet(&klv, s->pb) < 0 || + !IS_KLV_KEY(klv.key, mxf_random_index_pack_key) || + klv.length != length - 20) + goto end; + + avio_skip(s->pb, klv.length - 12); + mxf->last_partition = avio_rb64(s->pb); + +end: + avio_seek(s->pb, mxf->run_in, SEEK_SET); +} + static int mxf_read_header(AVFormatContext *s) { MXFContext *mxf = s->priv_data; @@ -1846,7 +2018,9 @@ static int mxf_read_header(AVFormatContext *s) mxf->fc = s; mxf->run_in = avio_tell(s->pb); - while (!s->pb->eof_reached) { + mxf_read_random_index_pack(s); + + while (!url_feof(s->pb)) { const MXFMetadataReadTableEntry *metadata; if (klv_read_packet(&klv, s->pb) < 0) { @@ -1865,13 +2039,32 @@ static int mxf_read_header(AVFormatContext *s) IS_KLV_KEY(klv.key, mxf_system_item_key)) { if (!mxf->current_partition) { - av_log(mxf->fc, AV_LOG_ERROR, - "found essence prior to first PartitionPack\n"); + av_log(mxf->fc, AV_LOG_ERROR, "found essence prior to first PartitionPack\n"); return AVERROR_INVALIDDATA; } if (!mxf->current_partition->essence_offset) { - compute_partition_essence_offset(s, mxf, &klv); + /* for OP1a we compute essence_offset + * for OPAtom we point essence_offset after the KL (usually op1a_essence_offset + 20 or 25) + * TODO: for OP1a we could eliminate this entire if statement, always stopping parsing at op1a_essence_offset + * for OPAtom we still need the actual essence_offset though (the KL's length can vary) + */ + int64_t op1a_essence_offset = + round_to_kag(mxf->current_partition->this_partition + + mxf->current_partition->pack_length, mxf->current_partition->kag_size) + + round_to_kag(mxf->current_partition->header_byte_count, mxf->current_partition->kag_size) + + round_to_kag(mxf->current_partition->index_byte_count, mxf->current_partition->kag_size); + + if (mxf->op == OPAtom) { + /* point essence_offset to the actual data + * OPAtom has all the essence in one big KLV + */ + mxf->current_partition->essence_offset = avio_tell(s->pb); + mxf->current_partition->essence_length = klv.length; + } else { + /* NOTE: op1a_essence_offset may be less than to klv.offset (C0023S01.mxf) */ + mxf->current_partition->essence_offset = op1a_essence_offset; + } } if (!essence_offset) @@ -1931,10 +2124,10 @@ static int mxf_read_header(AVFormatContext *s) /* we need to do this before computing the index tables * to be able to fill in zero IndexDurations with st->duration */ if ((ret = mxf_parse_structural_metadata(mxf)) < 0) - return ret; + goto fail; if ((ret = mxf_compute_index_tables(mxf)) < 0) - return ret; + goto fail; if (mxf->nb_index_tables > 1) { /* TODO: look up which IndexSID to use via EssenceContainerData */ @@ -1942,12 +2135,17 @@ static int mxf_read_header(AVFormatContext *s) mxf->nb_index_tables, mxf->index_tables[0].index_sid); } else if (mxf->nb_index_tables == 0 && mxf->op == OPAtom) { av_log(mxf->fc, AV_LOG_ERROR, "cannot demux OPAtom without an index\n"); - return AVERROR_INVALIDDATA; + ret = AVERROR_INVALIDDATA; + goto fail; } mxf_handle_small_eubc(s); return 0; +fail: + mxf_read_close(s); + + return ret; } /** @@ -1964,11 +2162,9 @@ static int64_t mxf_set_current_edit_unit(MXFContext *mxf, int64_t current_offset if (mxf->nb_index_tables <= 0) return -1; - /* find mxf->current_edit_unit so that the next edit unit starts ahead - * of current_offset */ + /* find mxf->current_edit_unit so that the next edit unit starts ahead of current_offset */ while (mxf->current_edit_unit >= 0) { - if (mxf_edit_unit_absolute_offset(mxf, t, mxf->current_edit_unit + 1, - NULL, &next_ofs, 0) < 0) + if (mxf_edit_unit_absolute_offset(mxf, t, mxf->current_edit_unit + 1, NULL, &next_ofs, 0) < 0) return -1; if (next_ofs <= last_ofs) { @@ -1986,29 +2182,76 @@ static int64_t mxf_set_current_edit_unit(MXFContext *mxf, int64_t current_offset mxf->current_edit_unit++; } - /* not checking mxf->current_edit_unit >= t->nb_ptses here since CBR files - * may lack IndexEntryArrays */ + /* not checking mxf->current_edit_unit >= t->nb_ptses here since CBR files may lack IndexEntryArrays */ if (mxf->current_edit_unit < 0) return -1; return next_ofs; } +static int mxf_compute_sample_count(MXFContext *mxf, int stream_index, uint64_t *sample_count) +{ + int i, total = 0, size = 0; + AVStream *st = mxf->fc->streams[stream_index]; + MXFTrack *track = st->priv_data; + AVRational time_base = av_inv_q(track->edit_rate); + AVRational sample_rate = av_inv_q(st->time_base); + const MXFSamplesPerFrame *spf = NULL; + + if ((sample_rate.num / sample_rate.den) == 48000) + spf = ff_mxf_get_samples_per_frame(mxf->fc, time_base); + if (!spf) { + int remainder = (sample_rate.num * time_base.num) % (time_base.den * sample_rate.den); + *sample_count = av_q2d(av_mul_q((AVRational){mxf->current_edit_unit, 1}, + av_mul_q(sample_rate, time_base))); + if (remainder) + av_log(mxf->fc, AV_LOG_WARNING, + "seeking detected on stream #%d with time base (%d/%d) and sample rate (%d/%d), audio pts won't be accurate.\n", + stream_index, time_base.num, time_base.den, sample_rate.num, sample_rate.den); + return 0; + } + + while (spf->samples_per_frame[size]) { + total += spf->samples_per_frame[size]; + size++; + } + + av_assert2(size); + + *sample_count = (mxf->current_edit_unit / size) * (uint64_t)total; + for (i = 0; i < mxf->current_edit_unit % size; i++) { + *sample_count += spf->samples_per_frame[i]; + } + + return 0; +} + +static int mxf_set_audio_pts(MXFContext *mxf, AVCodecContext *codec, AVPacket *pkt) +{ + MXFTrack *track = mxf->fc->streams[pkt->stream_index]->priv_data; + pkt->pts = track->sample_count; + if ( codec->channels <= 0 + || av_get_bits_per_sample(codec->codec_id) <= 0 + || codec->channels * (int64_t)av_get_bits_per_sample(codec->codec_id) < 8) + return AVERROR(EINVAL); + track->sample_count += pkt->size / (codec->channels * (int64_t)av_get_bits_per_sample(codec->codec_id) / 8); + return 0; +} + static int mxf_read_packet_old(AVFormatContext *s, AVPacket *pkt) { KLVPacket klv; MXFContext *mxf = s->priv_data; - while (!s->pb->eof_reached) { - if (klv_read_packet(&klv, s->pb) < 0) - return -1; + while (klv_read_packet(&klv, s->pb) == 0) { + int ret; PRINT_KEY(s, "read packet", klv.key); av_dlog(s, "size %"PRIu64" offset %#"PRIx64"\n", klv.length, klv.offset); if (IS_KLV_KEY(klv.key, mxf_encrypted_triplet_key)) { - int res = mxf_decrypt_triplet(s, pkt, &klv); - if (res < 0) { + ret = mxf_decrypt_triplet(s, pkt, &klv); + if (ret < 0) { av_log(s, AV_LOG_ERROR, "invalid encoded triplet\n"); - return -1; + return AVERROR_INVALIDDATA; } return 0; } @@ -2018,6 +2261,7 @@ static int mxf_read_packet_old(AVFormatContext *s, AVPacket *pkt) int64_t next_ofs, next_klv; AVStream *st; MXFTrack *track; + AVCodecContext *codec; if (index < 0) { av_log(s, AV_LOG_ERROR, "error getting stream index %d\n", AV_RB32(klv.key+12)); @@ -2034,9 +2278,8 @@ static int mxf_read_packet_old(AVFormatContext *s, AVPacket *pkt) next_ofs = mxf_set_current_edit_unit(mxf, klv.offset); if (next_ofs >= 0 && next_klv > next_ofs) { - /* if this check is hit then it's possible OPAtom was treated - * as OP1a truncate the packet since it's probably very large - * (>2 GiB is common) */ + /* if this check is hit then it's possible OPAtom was treated as OP1a + * truncate the packet since it's probably very large (>2 GiB is common) */ avpriv_request_sample(s, "OPAtom misinterpreted as OP1a?" "KLV for edit unit %i extending into " @@ -2049,31 +2292,33 @@ static int mxf_read_packet_old(AVFormatContext *s, AVPacket *pkt) if (klv.key[12] == 0x06 && klv.key[13] == 0x01 && klv.key[14] == 0x10) { if (mxf_get_d10_aes3_packet(s->pb, s->streams[index], pkt, klv.length) < 0) { av_log(s, AV_LOG_ERROR, "error reading D-10 aes3 frame\n"); - return -1; + return AVERROR_INVALIDDATA; } } else { - int ret = av_get_packet(s->pb, pkt, klv.length); + ret = av_get_packet(s->pb, pkt, klv.length); if (ret < 0) return ret; } pkt->stream_index = index; pkt->pos = klv.offset; - if (s->streams[index]->codec->codec_type == AVMEDIA_TYPE_VIDEO && next_ofs >= 0) { - /* mxf->current_edit_unit good - see if we have an - * index table to derive timestamps from */ + codec = s->streams[index]->codec; + if (codec->codec_type == AVMEDIA_TYPE_VIDEO && next_ofs >= 0) { + /* mxf->current_edit_unit good - see if we have an index table to derive timestamps from */ MXFIndexTable *t = &mxf->index_tables[0]; - if (mxf->nb_index_tables >= 1 && - mxf->current_edit_unit < t->nb_ptses) { + if (mxf->nb_index_tables >= 1 && mxf->current_edit_unit < t->nb_ptses) { pkt->dts = mxf->current_edit_unit + t->first_dts; pkt->pts = t->ptses[mxf->current_edit_unit]; } else if (track->intra_only) { /* intra-only -> PTS = EditUnit. - * let utils.c figure out DTS since it can be - * < PTS if low_delay = 0 (Sony IMX30) */ + * let utils.c figure out DTS since it can be < PTS if low_delay = 0 (Sony IMX30) */ pkt->pts = mxf->current_edit_unit; } + } else if (codec->codec_type == AVMEDIA_TYPE_AUDIO) { + int ret = mxf_set_audio_pts(mxf, codec, pkt); + if (ret < 0) + return ret; } /* seek for truncated packets */ @@ -2084,7 +2329,7 @@ static int mxf_read_packet_old(AVFormatContext *s, AVPacket *pkt) skip: avio_skip(s->pb, klv.length); } - return AVERROR_EOF; + return url_feof(s->pb) ? AVERROR_EOF : -1; } static int mxf_read_packet(AVFormatContext *s, AVPacket *pkt) @@ -2128,22 +2373,26 @@ static int mxf_read_packet(AVFormatContext *s, AVPacket *pkt) if ((ret64 = avio_seek(s->pb, pos, SEEK_SET)) < 0) return ret64; - if ((ret = av_get_packet(s->pb, pkt, size)) != size) - return ret < 0 ? ret : AVERROR_EOF; + if ((size = av_get_packet(s->pb, pkt, size)) < 0) + return size; + + pkt->stream_index = 0; if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO && t->ptses && mxf->current_edit_unit >= 0 && mxf->current_edit_unit < t->nb_ptses) { pkt->dts = mxf->current_edit_unit + t->first_dts; pkt->pts = t->ptses[mxf->current_edit_unit]; + } else if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) { + int ret = mxf_set_audio_pts(mxf, st->codec, pkt); + if (ret < 0) + return ret; } - pkt->stream_index = 0; mxf->current_edit_unit += edit_units; return 0; } - static int mxf_read_close(AVFormatContext *s) { MXFContext *mxf = s->priv_data; @@ -2157,6 +2406,9 @@ static int mxf_read_close(AVFormatContext *s) for (i = 0; i < mxf->metadata_sets_count; i++) { switch (mxf->metadata_sets[i]->type) { + case Descriptor: + av_freep(&((MXFDescriptor *)mxf->metadata_sets[i])->extradata); + break; case MultipleDescriptor: av_freep(&((MXFDescriptor *)mxf->metadata_sets[i])->sub_descriptors_refs); break; @@ -2183,10 +2435,12 @@ static int mxf_read_close(AVFormatContext *s) av_freep(&mxf->aesc); av_freep(&mxf->local_tags); - for (i = 0; i < mxf->nb_index_tables; i++) { - av_freep(&mxf->index_tables[i].segments); - av_freep(&mxf->index_tables[i].ptses); - av_freep(&mxf->index_tables[i].fake_index); + if (mxf->index_tables) { + for (i = 0; i < mxf->nb_index_tables; i++) { + av_freep(&mxf->index_tables[i].segments); + av_freep(&mxf->index_tables[i].ptses); + av_freep(&mxf->index_tables[i].fake_index); + } } av_freep(&mxf->index_tables); @@ -2194,8 +2448,8 @@ static int mxf_read_close(AVFormatContext *s) } static int mxf_probe(AVProbeData *p) { - uint8_t *bufp = p->buf; - uint8_t *end = p->buf + p->buf_size; + const uint8_t *bufp = p->buf; + const uint8_t *end = p->buf + p->buf_size; if (p->buf_size < sizeof(mxf_header_partition_pack_key)) return 0; @@ -2217,8 +2471,14 @@ static int mxf_read_seek(AVFormatContext *s, int stream_index, int64_t sample_ti int64_t seconds; MXFContext* mxf = s->priv_data; int64_t seekpos; - int ret; + int i, ret; + int64_t ret64; MXFIndexTable *t; + MXFTrack *source_track = st->priv_data; + + /* if audio then truncate sample_time to EditRate */ + if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) + sample_time = av_rescale_q(sample_time, st->time_base, av_inv_q(source_track->edit_rate)); if (mxf->nb_index_tables <= 0) { if (!s->bit_rate) @@ -2227,9 +2487,10 @@ static int mxf_read_seek(AVFormatContext *s, int stream_index, int64_t sample_ti sample_time = 0; seconds = av_rescale(sample_time, st->time_base.num, st->time_base.den); - if ((ret = avio_seek(s->pb, (s->bit_rate * seconds) >> 3, SEEK_SET)) < 0) - return ret; + if ((ret64 = avio_seek(s->pb, (s->bit_rate * seconds) >> 3, SEEK_SET)) < 0) + return ret64; ff_update_cur_dts(s, st, sample_time); + mxf->current_edit_unit = sample_time; } else { t = &mxf->index_tables[0]; @@ -2244,7 +2505,7 @@ static int mxf_read_seek(AVFormatContext *s, int stream_index, int64_t sample_ti } else { /* no IndexEntryArray (one or more CBR segments) * make sure we don't seek past the end */ - sample_time = FFMIN(sample_time, st->duration - 1); + sample_time = FFMIN(sample_time, source_track->original_duration - 1); } if ((ret = mxf_edit_unit_absolute_offset(mxf, t, sample_time, &sample_time, &seekpos, 1)) << 0) @@ -2254,6 +2515,20 @@ static int mxf_read_seek(AVFormatContext *s, int stream_index, int64_t sample_ti mxf->current_edit_unit = sample_time; avio_seek(s->pb, seekpos, SEEK_SET); } + + // Update all tracks sample count + for (i = 0; i < s->nb_streams; i++) { + AVStream *cur_st = s->streams[i]; + MXFTrack *cur_track = cur_st->priv_data; + uint64_t current_sample_count = 0; + if (cur_st->codec->codec_type == AVMEDIA_TYPE_AUDIO) { + ret = mxf_compute_sample_count(mxf, i, ¤t_sample_count); + if (ret < 0) + return ret; + + cur_track->sample_count = current_sample_count; + } + } return 0; } diff --git a/libavformat/mxfenc.c b/libavformat/mxfenc.c index 4fd5687beb..5e77a3f068 100644 --- a/libavformat/mxfenc.c +++ b/libavformat/mxfenc.c @@ -3,20 +3,20 @@ * Copyright (c) 2008 GUCAS, Zhentan Feng <spyfeng at gmail dot com> * Copyright (c) 2008 Baptiste Coudurier <baptiste dot coudurier at gmail dot com> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -34,15 +34,18 @@ #include <math.h> #include <time.h> +#include "libavutil/opt.h" #include "libavutil/random_seed.h" +#include "libavutil/timecode.h" +#include "libavutil/avassert.h" #include "libavcodec/bytestream.h" +#include "libavcodec/dnxhddata.h" #include "audiointerleave.h" #include "avformat.h" +#include "avio_internal.h" #include "internal.h" #include "mxf.h" - -static const int NTSC_samples_per_frame[] = { 1602, 1601, 1602, 1601, 1602, 0 }; -static const int PAL_samples_per_frame[] = { 1920, 0 }; +#include "config.h" extern AVOutputFormat ff_mxf_d10_muxer; @@ -68,9 +71,12 @@ typedef struct { const UID *codec_ul; int order; ///< interleaving order if dts are equal int interlaced; ///< whether picture is interlaced + int field_dominance; ///< tff=1, bff=2 + int component_depth; int temporal_reordering; AVRational aspect_ratio; ///< display aspect ratio int closed_gop; ///< gop is closed, used in mpeg-2 frame parsing + int video_bit_rate; } MXFStreamContext; typedef struct { @@ -87,6 +93,8 @@ static const struct { { AV_CODEC_ID_MPEG2VIDEO, 0 }, { AV_CODEC_ID_PCM_S24LE, 1 }, { AV_CODEC_ID_PCM_S16LE, 1 }, + { AV_CODEC_ID_DVVIDEO, 15 }, + { AV_CODEC_ID_DNXHD, 24 }, { AV_CODEC_ID_NONE } }; @@ -163,6 +171,101 @@ static const MXFContainerEssenceEntry mxf_essence_container_uls[] = { { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x06,0x01,0x10,0x00 }, { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x02,0x02,0x01,0x00,0x00,0x00,0x00 }, mxf_write_generic_sound_desc }, + // DV Unknown + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x02,0x7F,0x01 }, + { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x18,0x01,0x01,0x00 }, + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x02,0x00,0x00,0x00 }, + mxf_write_cdci_desc }, + // DV25 525/60 + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x02,0x40,0x01 }, + { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x18,0x01,0x01,0x00 }, + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x02,0x02,0x01,0x00 }, + mxf_write_cdci_desc }, + // DV25 625/50 + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x02,0x41,0x01 }, + { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x18,0x01,0x01,0x00 }, + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x02,0x02,0x02,0x00 }, + mxf_write_cdci_desc }, + // DV50 525/60 + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x02,0x50,0x01 }, + { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x18,0x01,0x01,0x00 }, + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x02,0x02,0x03,0x00 }, + mxf_write_cdci_desc }, + // DV50 625/50 + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x02,0x51,0x01 }, + { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x18,0x01,0x01,0x00 }, + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x02,0x02,0x04,0x00 }, + mxf_write_cdci_desc }, + // DV100 1080/60 + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x02,0x60,0x01 }, + { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x18,0x01,0x01,0x00 }, + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x02,0x02,0x05,0x00 }, + mxf_write_cdci_desc }, + // DV100 1080/50 + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x02,0x61,0x01 }, + { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x18,0x01,0x01,0x00 }, + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x02,0x02,0x06,0x00 }, + mxf_write_cdci_desc }, + // DV100 720/60 + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x02,0x62,0x01 }, + { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x18,0x01,0x01,0x00 }, + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x02,0x02,0x07,0x00 }, + mxf_write_cdci_desc }, + // DV100 720/50 + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x02,0x63,0x01 }, + { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x18,0x01,0x01,0x00 }, + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x02,0x02,0x08,0x00 }, + mxf_write_cdci_desc }, + // DNxHD 1080p 10bit high + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x11,0x01,0x00 }, + { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x15,0x01,0x05,0x00 }, + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0A,0x04,0x01,0x02,0x02,0x71,0x01,0x00,0x00 }, + mxf_write_cdci_desc }, + // DNxHD 1080p 8bit medium + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x11,0x01,0x00 }, + { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x15,0x01,0x05,0x00 }, + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0A,0x04,0x01,0x02,0x02,0x71,0x03,0x00,0x00 }, + mxf_write_cdci_desc }, + // DNxHD 1080p 8bit high + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x11,0x01,0x00 }, + { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x15,0x01,0x05,0x00 }, + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0A,0x04,0x01,0x02,0x02,0x71,0x04,0x00,0x00 }, + mxf_write_cdci_desc }, + // DNxHD 1080i 10bit high + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x11,0x01,0x00 }, + { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x15,0x01,0x05,0x00 }, + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0A,0x04,0x01,0x02,0x02,0x71,0x07,0x00,0x00 }, + mxf_write_cdci_desc }, + // DNxHD 1080i 8bit medium + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x11,0x01,0x00 }, + { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x15,0x01,0x05,0x00 }, + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0A,0x04,0x01,0x02,0x02,0x71,0x08,0x00,0x00 }, + mxf_write_cdci_desc }, + // DNxHD 1080i 8bit high + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x11,0x01,0x00 }, + { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x15,0x01,0x05,0x00 }, + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0A,0x04,0x01,0x02,0x02,0x71,0x09,0x00,0x00 }, + mxf_write_cdci_desc }, + // DNxHD 720p 10bit + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x01,0x0d,0x01,0x03,0x01,0x02,0x11,0x01,0x00 }, + { 0x06,0x0e,0x2b,0x34,0x01,0x02,0x01,0x01,0x0d,0x01,0x03,0x01,0x15,0x01,0x05,0x00 }, + { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0A,0x04,0x01,0x02,0x02,0x71,0x10,0x00,0x00 }, + mxf_write_cdci_desc }, + // DNxHD 720p 8bit high + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x01,0x0d,0x01,0x03,0x01,0x02,0x11,0x01,0x00 }, + { 0x06,0x0e,0x2b,0x34,0x01,0x02,0x01,0x01,0x0d,0x01,0x03,0x01,0x15,0x01,0x05,0x00 }, + { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0A,0x04,0x01,0x02,0x02,0x71,0x11,0x00,0x00 }, + mxf_write_cdci_desc }, + // DNxHD 720p 8bit medium + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x01,0x0d,0x01,0x03,0x01,0x02,0x11,0x01,0x00 }, + { 0x06,0x0e,0x2b,0x34,0x01,0x02,0x01,0x01,0x0d,0x01,0x03,0x01,0x15,0x01,0x05,0x00 }, + { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0A,0x04,0x01,0x02,0x02,0x71,0x12,0x00,0x00 }, + mxf_write_cdci_desc }, + // DNxHD 720p 8bit low + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x01,0x0d,0x01,0x03,0x01,0x02,0x11,0x01,0x00 }, + { 0x06,0x0e,0x2b,0x34,0x01,0x02,0x01,0x01,0x0d,0x01,0x03,0x01,0x15,0x01,0x05,0x00 }, + { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0A,0x04,0x01,0x02,0x02,0x71,0x13,0x00,0x00 }, + mxf_write_cdci_desc }, { { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, @@ -170,6 +273,7 @@ static const MXFContainerEssenceEntry mxf_essence_container_uls[] = { }; typedef struct MXFContext { + AVClass *av_class; int64_t footer_partition_offset; int essence_container_count; AVRational time_base; @@ -183,10 +287,9 @@ typedef struct MXFContext { unsigned body_partitions_count; int last_key_index; ///< index of last key frame uint64_t duration; + AVTimecode tc; ///< timecode context AVStream *timecode_track; int timecode_base; ///< rounded time code base (25 or 30) - int timecode_start; ///< frame number computed from mpeg-2 gop header timecode - int timecode_drop_frame; ///< time code use drop frame method frop mpeg-2 essence gop header int edit_unit_byte_count; ///< fixed edit unit byte count uint64_t body_offset; uint32_t instance_number; @@ -279,6 +382,7 @@ static const MXFLocalTagPair mxf_local_tag_batch[] = { { 0x3208, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x01,0x04,0x01,0x05,0x01,0x0B,0x00,0x00,0x00}}, /* Display Height */ { 0x320E, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x01,0x04,0x01,0x01,0x01,0x01,0x00,0x00,0x00}}, /* Aspect Ratio */ { 0x3201, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x04,0x01,0x06,0x01,0x00,0x00,0x00,0x00}}, /* Picture Essence Coding */ + { 0x3212, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x04,0x01,0x03,0x01,0x06,0x00,0x00,0x00}}, /* Field Dominance (Opt) */ // CDCI Picture Essence Descriptor { 0x3301, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x04,0x01,0x05,0x03,0x0A,0x00,0x00,0x00}}, /* Component Depth */ { 0x3302, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x01,0x04,0x01,0x05,0x01,0x05,0x00,0x00,0x00}}, /* Horizontal Subsampling */ @@ -426,18 +530,25 @@ static const MXFCodecUL *mxf_get_data_definition_ul(int type) return uls; } +//one EC -> one descriptor. N ECs -> MultipleDescriptor + N descriptors +#define DESCRIPTOR_COUNT(essence_container_count) \ + (essence_container_count > 1 ? essence_container_count + 1 : essence_container_count) + static void mxf_write_essence_container_refs(AVFormatContext *s) { MXFContext *c = s->priv_data; AVIOContext *pb = s->pb; int i; - mxf_write_refs_count(pb, c->essence_container_count); + mxf_write_refs_count(pb, DESCRIPTOR_COUNT(c->essence_container_count)); av_log(s,AV_LOG_DEBUG, "essence container count:%d\n", c->essence_container_count); for (i = 0; i < c->essence_container_count; i++) { MXFStreamContext *sc = s->streams[i]->priv_data; avio_write(pb, mxf_essence_container_uls[sc->index].container_ul, 16); } + + if (c->essence_container_count > 1) + avio_write(pb, multiple_desc_ul, 16); } static void mxf_write_preface(AVFormatContext *s) @@ -447,7 +558,7 @@ static void mxf_write_preface(AVFormatContext *s) mxf_write_metadata_key(pb, 0x012f00); PRINT_KEY(s, "preface key", pb->buf_ptr - 16); - klv_encode_ber_length(pb, 130 + 16 * mxf->essence_container_count); + klv_encode_ber_length(pb, 130 + 16LL * DESCRIPTOR_COUNT(mxf->essence_container_count)); // write preface set uid mxf_write_local_tag(pb, 16, 0x3C0A); @@ -476,7 +587,7 @@ static void mxf_write_preface(AVFormatContext *s) avio_write(pb, op1a_ul, 16); // write essence_container_refs - mxf_write_local_tag(pb, 8 + 16 * mxf->essence_container_count, 0x3B0A); + mxf_write_local_tag(pb, 8 + 16LL * DESCRIPTOR_COUNT(mxf->essence_container_count), 0x3B0A); mxf_write_essence_container_refs(s); // write dm_scheme_refs @@ -499,7 +610,7 @@ static void mxf_write_identification(AVFormatContext *s) { MXFContext *mxf = s->priv_data; AVIOContext *pb = s->pb; - const char *company = "Libav"; + const char *company = "FFmpeg"; const char *product = "OP1a Muxer"; const char *version; int length; @@ -664,7 +775,7 @@ static void mxf_write_timecode_component(AVFormatContext *s, AVStream *st, enum // Start Time Code mxf_write_local_tag(pb, 8, 0x1501); - avio_wb64(pb, mxf->timecode_start); + avio_wb64(pb, mxf->tc.start); // Rounded Time Code Base mxf_write_local_tag(pb, 2, 0x1502); @@ -672,7 +783,7 @@ static void mxf_write_timecode_component(AVFormatContext *s, AVStream *st, enum // Drop Frame mxf_write_local_tag(pb, 1, 0x1503); - avio_w8(pb, mxf->timecode_drop_frame); + avio_w8(pb, !!(mxf->tc.flags & AV_TIMECODE_FLAG_DROPFRAME)); } static void mxf_write_structural_component(AVFormatContext *s, AVStream *st, enum MXFMetadataSetType type) @@ -720,7 +831,7 @@ static void mxf_write_multi_descriptor(AVFormatContext *s) mxf_write_metadata_key(pb, 0x014400); PRINT_KEY(s, "multiple descriptor key", pb->buf_ptr - 16); - klv_encode_ber_length(pb, 64 + 16 * s->nb_streams); + klv_encode_ber_length(pb, 64 + 16LL * s->nb_streams); mxf_write_local_tag(pb, 16, 0x3C0A); mxf_write_uuid(pb, MultipleDescriptor, 0); @@ -784,8 +895,11 @@ static void mxf_write_cdci_common(AVFormatContext *s, AVStream *st, const UID ke int stored_height = (st->codec->height+15)/16*16; int display_height; int f1, f2; + unsigned desc_size = size+8+8+8+8+8+8+5+16+sc->interlaced*4+12+20; + if (sc->interlaced && sc->field_dominance) + desc_size += 5; - mxf_write_generic_desc(s, st, key, size+8+8+8+8+8+8+5+16+sc->interlaced*4+12+20); + mxf_write_generic_desc(s, st, key, desc_size); mxf_write_local_tag(pb, 4, 0x3203); avio_wb32(pb, st->codec->width); @@ -808,7 +922,7 @@ static void mxf_write_cdci_common(AVFormatContext *s, AVStream *st, const UID ke // component depth mxf_write_local_tag(pb, 4, 0x3301); - avio_wb32(pb, 8); + avio_wb32(pb, sc->component_depth); // horizontal subsampling mxf_write_local_tag(pb, 4, 0x3302); @@ -820,9 +934,9 @@ static void mxf_write_cdci_common(AVFormatContext *s, AVStream *st, const UID ke // video line map switch (st->codec->height) { - case 576: f1 = 23; f2 = 336; break; + case 576: f1 = 23; f2 = st->codec->codec_id == AV_CODEC_ID_DVVIDEO ? 335 : 336; break; case 608: f1 = 7; f2 = 320; break; - case 480: f1 = 20; f2 = 283; break; + case 480: f1 = 20; f2 = st->codec->codec_id == AV_CODEC_ID_DVVIDEO ? 285 : 283; break; case 512: f1 = 7; f2 = 270; break; case 720: f1 = 26; f2 = 0; break; // progressive case 1080: f1 = 21; f2 = 584; break; @@ -847,6 +961,12 @@ static void mxf_write_cdci_common(AVFormatContext *s, AVStream *st, const UID ke mxf_write_local_tag(pb, 16, 0x3201); avio_write(pb, *sc->codec_ul, 16); + + if (sc->interlaced && sc->field_dominance) { + mxf_write_local_tag(pb, 1, 0x3212); + avio_w8(pb, sc->field_dominance); + } + } static void mxf_write_cdci_desc(AVFormatContext *s, AVStream *st) @@ -857,13 +977,14 @@ static void mxf_write_cdci_desc(AVFormatContext *s, AVStream *st) static void mxf_write_mpegvideo_desc(AVFormatContext *s, AVStream *st) { AVIOContext *pb = s->pb; + MXFStreamContext *sc = st->priv_data; int profile_and_level = (st->codec->profile<<4) | st->codec->level; mxf_write_cdci_common(s, st, mxf_mpegvideo_descriptor_key, 8+5); // bit rate mxf_write_local_tag(pb, 4, 0x8000); - avio_wb32(pb, st->codec->bit_rate); + avio_wb32(pb, sc->video_bit_rate); // profile and level mxf_write_local_tag(pb, 1, 0x8007); @@ -1053,8 +1174,8 @@ static void mxf_write_index_table_segment(AVFormatContext *s) if (mxf->edit_unit_byte_count) { klv_encode_ber_length(pb, 80); } else { - klv_encode_ber_length(pb, 85 + 12+(s->nb_streams+1)*6 + - 12+mxf->edit_units_count*(11+mxf->slice_count*4)); + klv_encode_ber_length(pb, 85 + 12+(s->nb_streams+1LL)*6 + + 12+mxf->edit_units_count*(11+mxf->slice_count*4LL)); } // instance id @@ -1176,9 +1297,8 @@ static void mxf_write_klv_fill(AVFormatContext *s) avio_write(s->pb, klv_fill_key, 16); pad -= 16 + 4; klv_encode_ber4_length(s->pb, pad); - for (; pad; pad--) - avio_w8(s->pb, 0); - assert(!(avio_tell(s->pb) & (KAG_SIZE-1))); + ffio_fill(s->pb, 0, pad); + av_assert1(!(avio_tell(s->pb) & (KAG_SIZE-1))); } } @@ -1216,7 +1336,7 @@ static int mxf_write_partition(AVFormatContext *s, int bodysid, // write klv avio_write(pb, key, 16); - klv_encode_ber_length(pb, 88 + 16 * mxf->essence_container_count); + klv_encode_ber_length(pb, 88 + 16LL * DESCRIPTOR_COUNT(mxf->essence_container_count)); // write partition value avio_wb16(pb, 1); // majorVersion @@ -1279,6 +1399,154 @@ static int mxf_write_partition(AVFormatContext *s, int bodysid, return 0; } +static int mxf_parse_dnxhd_frame(AVFormatContext *s, AVStream *st, +AVPacket *pkt) +{ + MXFContext *mxf = s->priv_data; + MXFStreamContext *sc = st->priv_data; + int i, cid; + uint8_t* header_cid; + int frame_size = 0; + + if (mxf->header_written) + return 1; + + if (pkt->size < 43) + return -1; + + header_cid = pkt->data + 0x28; + cid = header_cid[0] << 24 | header_cid[1] << 16 | header_cid[2] << 8 | header_cid[3]; + + if ((frame_size = avpriv_dnxhd_get_frame_size(cid)) < 0) + return -1; + + switch (cid) { + case 1235: + sc->index = 24; + sc->component_depth = 10; + break; + case 1237: + sc->index = 25; + break; + case 1238: + sc->index = 26; + break; + case 1241: + sc->index = 27; + sc->component_depth = 10; + break; + case 1242: + sc->index = 28; + break; + case 1243: + sc->index = 29; + break; + case 1250: + sc->index = 30; + sc->component_depth = 10; + break; + case 1251: + sc->index = 31; + break; + case 1252: + sc->index = 32; + break; + case 1253: + sc->index = 33; + break; + default: + return -1; + } + + sc->codec_ul = &mxf_essence_container_uls[sc->index].codec_ul; + sc->aspect_ratio = (AVRational){ 16, 9 }; + + mxf->edit_unit_byte_count = KAG_SIZE; + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + MXFStreamContext *sc = st->priv_data; + if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) { + mxf->edit_unit_byte_count += 16 + 4 + sc->aic.samples[0]*sc->aic.sample_size; + mxf->edit_unit_byte_count += klv_fill_size(mxf->edit_unit_byte_count); + } else if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) { + mxf->edit_unit_byte_count += 16 + 4 + frame_size; + mxf->edit_unit_byte_count += klv_fill_size(mxf->edit_unit_byte_count); + } + } + + return 1; +} + +static int mxf_parse_dv_frame(AVFormatContext *s, AVStream *st, AVPacket *pkt) +{ + MXFContext *mxf = s->priv_data; + MXFStreamContext *sc = st->priv_data; + uint8_t *vs_pack, *vsc_pack; + int i, ul_index, frame_size, stype, pal; + + if (mxf->header_written) + return 1; + + // Check for minimal frame size + if (pkt->size < 120000) + return -1; + + vs_pack = pkt->data + 80*5 + 48; + vsc_pack = pkt->data + 80*5 + 53; + stype = vs_pack[3] & 0x1f; + pal = (vs_pack[3] >> 5) & 0x1; + + if ((vs_pack[2] & 0x07) == 0x02) + sc->aspect_ratio = (AVRational){ 16, 9 }; + else + sc->aspect_ratio = (AVRational){ 4, 3 }; + + sc->interlaced = (vsc_pack[3] >> 4) & 0x01; + // TODO: fix dv encoder to set proper FF/FS value in VSC pack + // and set field dominance accordingly + // av_log(s, AV_LOG_DEBUG, "DV vsc pack ff/ss = %x\n", vsc_pack[2] >> 6); + + switch (stype) { + case 0x18: // DV100 720p + ul_index = 6 + pal; + frame_size = pal ? 288000 : 240000; + if (sc->interlaced) { + av_log(s, AV_LOG_ERROR, "source marked as interlaced but codec profile is progressive\n"); + sc->interlaced = 0; + } + break; + case 0x14: // DV100 1080i + ul_index = 4 + pal; + frame_size = pal ? 576000 : 480000; + break; + case 0x04: // DV50 + ul_index = 2 + pal; + frame_size = pal ? 288000 : 240000; + break; + default: // DV25 + ul_index = 0 + pal; + frame_size = pal ? 144000 : 120000; + } + + sc->index = ul_index + 16; + sc->codec_ul = &mxf_essence_container_uls[sc->index].codec_ul; + + mxf->edit_unit_byte_count = KAG_SIZE; + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + MXFStreamContext *sc = st->priv_data; + if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) { + mxf->edit_unit_byte_count += 16 + 4 + sc->aic.samples[0]*sc->aic.sample_size; + mxf->edit_unit_byte_count += klv_fill_size(mxf->edit_unit_byte_count); + } else if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) { + mxf->edit_unit_byte_count += 16 + 4 + frame_size; + mxf->edit_unit_byte_count += klv_fill_size(mxf->edit_unit_byte_count); + } + } + + return 1; +} + static const UID mxf_mpeg2_codec_uls[] = { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x03,0x04,0x01,0x02,0x02,0x01,0x01,0x10,0x00 }, // MP-ML I-Frame { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x03,0x04,0x01,0x02,0x02,0x01,0x01,0x11,0x00 }, // MP-ML Long GOP @@ -1316,7 +1584,6 @@ static int mxf_parse_mpeg2_frame(AVFormatContext *s, AVStream *st, AVPacket *pkt, MXFIndexEntry *e) { MXFStreamContext *sc = st->priv_data; - MXFContext *mxf = s->priv_data; uint32_t c = -1; int i; @@ -1328,6 +1595,8 @@ static int mxf_parse_mpeg2_frame(AVFormatContext *s, AVStream *st, st->codec->level = pkt->data[i+2] >> 4; } else if (i + 5 < pkt->size && (pkt->data[i+1] & 0xf0) == 0x80) { // pict coding ext sc->interlaced = !(pkt->data[i+5] & 0x80); // progressive frame + if (sc->interlaced) + sc->field_dominance = 1 + !(pkt->data[i+4] & 0x80); // top field first break; } } else if (c == 0x1b8) { // gop @@ -1336,21 +1605,6 @@ static int mxf_parse_mpeg2_frame(AVFormatContext *s, AVStream *st, if (e->flags & 0x40) // sequence header present e->flags |= 0x80; // random access } - if (!mxf->header_written) { - unsigned hours = (pkt->data[i+1]>>2) & 0x1f; - unsigned minutes = ((pkt->data[i+1] & 0x03) << 4) | (pkt->data[i+2]>>4); - unsigned seconds = ((pkt->data[i+2] & 0x07) << 3) | (pkt->data[i+3]>>5); - unsigned frames = ((pkt->data[i+3] & 0x1f) << 1) | (pkt->data[i+4]>>7); - mxf->timecode_drop_frame = !!(pkt->data[i+1] & 0x80); - mxf->timecode_start = (hours*3600 + minutes*60 + seconds) * - mxf->timecode_base + frames; - if (mxf->timecode_drop_frame) { - unsigned tminutes = 60 * hours + minutes; - mxf->timecode_start -= 2 * (tminutes - tminutes / 10); - } - av_log(s, AV_LOG_DEBUG, "frame %d %d:%d:%d%c%d\n", mxf->timecode_start, - hours, minutes, seconds, mxf->timecode_drop_frame ? ';':':', frames); - } } else if (c == 0x1b3) { // seq e->flags |= 0x40; switch ((pkt->data[i+4]>>4) & 0xf) { @@ -1406,17 +1660,18 @@ static void mxf_gen_umid(AVFormatContext *s) AV_WB64(mxf->umid , umid); AV_WB64(mxf->umid+8, umid>>8); - mxf->instance_number = seed; + mxf->instance_number = seed & 0xFFFFFF; } static int mxf_write_header(AVFormatContext *s) { MXFContext *mxf = s->priv_data; - int i; + int i, ret; uint8_t present[FF_ARRAY_ELEMS(mxf_essence_container_uls)] = {0}; - const int *samples_per_frame = NULL; + const MXFSamplesPerFrame *spf = NULL; AVDictionaryEntry *t; int64_t timestamp = 0; + AVDictionaryEntry *tcr = av_dict_get(s->metadata, "timecode", NULL, 0); if (!s->nb_streams) return -1; @@ -1428,44 +1683,54 @@ static int mxf_write_header(AVFormatContext *s) return AVERROR(ENOMEM); st->priv_data = sc; + if ((i == 0) ^ (st->codec->codec_type == AVMEDIA_TYPE_VIDEO)) { + av_log(s, AV_LOG_ERROR, "there must be exactly one video stream and it must be the first one\n"); + return -1; + } + if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) { - if (i != 0) { - av_log(s, AV_LOG_ERROR, "video stream must be first track\n"); - return -1; - } - if (fabs(av_q2d(st->codec->time_base) - 1/25.0) < 0.0001) { - samples_per_frame = PAL_samples_per_frame; - mxf->time_base = (AVRational){ 1, 25 }; - mxf->timecode_base = 25; - } else if (fabs(av_q2d(st->codec->time_base) - 1001/30000.0) < 0.0001) { - samples_per_frame = NTSC_samples_per_frame; - mxf->time_base = (AVRational){ 1001, 30000 }; - mxf->timecode_base = 30; - } else { - av_log(s, AV_LOG_ERROR, "unsupported video frame rate\n"); - return -1; + AVRational rate, tbc = st->codec->time_base; + // Default component depth to 8 + sc->component_depth = 8; + mxf->timecode_base = (tbc.den + tbc.num/2) / tbc.num; + spf = ff_mxf_get_samples_per_frame(s, tbc); + if (!spf) { + av_log(s, AV_LOG_ERROR, "Unsupported video frame rate %d/%d\n", + tbc.den, tbc.num); + return AVERROR(EINVAL); } + mxf->time_base = spf->time_base; + rate = av_inv_q(mxf->time_base); avpriv_set_pts_info(st, 64, mxf->time_base.num, mxf->time_base.den); + if (!tcr) + tcr = av_dict_get(st->metadata, "timecode", NULL, 0); + if (tcr) + ret = av_timecode_init_from_string(&mxf->tc, rate, tcr->value, s); + else + ret = av_timecode_init(&mxf->tc, rate, 0, 0, s); + if (ret < 0) + return ret; + sc->video_bit_rate = st->codec->bit_rate ? st->codec->bit_rate : st->codec->rc_max_rate; if (s->oformat == &ff_mxf_d10_muxer) { - if (st->codec->bit_rate == 50000000) + if (sc->video_bit_rate == 50000000) { if (mxf->time_base.den == 25) sc->index = 3; else sc->index = 5; - else if (st->codec->bit_rate == 40000000) + } else if (sc->video_bit_rate == 40000000) { if (mxf->time_base.den == 25) sc->index = 7; else sc->index = 9; - else if (st->codec->bit_rate == 30000000) + } else if (sc->video_bit_rate == 30000000) { if (mxf->time_base.den == 25) sc->index = 11; else sc->index = 13; - else { + } else { av_log(s, AV_LOG_ERROR, "error MXF D-10 only support 30/40/50 mbit/s\n"); return -1; } mxf->edit_unit_byte_count = KAG_SIZE; // system element - mxf->edit_unit_byte_count += 16 + 4 + (uint64_t)st->codec->bit_rate * + mxf->edit_unit_byte_count += 16 + 4 + (uint64_t)sc->video_bit_rate * mxf->time_base.num / (8*mxf->time_base.den); mxf->edit_unit_byte_count += klv_fill_size(mxf->edit_unit_byte_count); - mxf->edit_unit_byte_count += 16 + 4 + 4 + samples_per_frame[0]*8*4; + mxf->edit_unit_byte_count += 16 + 4 + 4 + spf->samples_per_frame[0]*8*4; mxf->edit_unit_byte_count += klv_fill_size(mxf->edit_unit_byte_count); } } else if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) { @@ -1519,7 +1784,10 @@ static int mxf_write_header(AVFormatContext *s) MXFStreamContext *sc = s->streams[i]->priv_data; // update element count sc->track_essence_element_key[13] = present[sc->index]; - sc->order = AV_RB32(sc->track_essence_element_key+12); + if (!memcmp(sc->track_essence_element_key, mxf_essence_container_uls[15].element_ul, 13)) // DV + sc->order = (0x15 << 24) | AV_RB32(sc->track_essence_element_key+13); + else + sc->order = AV_RB32(sc->track_essence_element_key+12); } if (t = av_dict_get(s->metadata, "creation_time", NULL, 0)) @@ -1536,10 +1804,10 @@ static int mxf_write_header(AVFormatContext *s) return AVERROR(ENOMEM); mxf->timecode_track->index = -1; - if (!samples_per_frame) - samples_per_frame = PAL_samples_per_frame; + if (!spf) + spf = ff_mxf_get_samples_per_frame(s, (AVRational){ 1, 25 }); - if (ff_audio_interleave_init(s, samples_per_frame, mxf->time_base) < 0) + if (ff_audio_interleave_init(s, spf->samples_per_frame, mxf->time_base) < 0) return -1; return 0; @@ -1548,24 +1816,6 @@ static int mxf_write_header(AVFormatContext *s) static const uint8_t system_metadata_pack_key[] = { 0x06,0x0E,0x2B,0x34,0x02,0x05,0x01,0x01,0x0D,0x01,0x03,0x01,0x04,0x01,0x01,0x00 }; static const uint8_t system_metadata_package_set_key[] = { 0x06,0x0E,0x2B,0x34,0x02,0x43,0x01,0x01,0x0D,0x01,0x03,0x01,0x04,0x01,0x02,0x01 }; -static uint32_t framenum_to_12m_time_code(unsigned frame, int drop, int fps) -{ - return (0 << 31) | // color frame flag - (drop << 30) | // drop frame flag - ( ((frame % fps) / 10) << 28) | // tens of frames - ( ((frame % fps) % 10) << 24) | // units of frames - (0 << 23) | // field phase (NTSC), b0 (PAL) - ((((frame / fps) % 60) / 10) << 20) | // tens of seconds - ((((frame / fps) % 60) % 10) << 16) | // units of seconds - (0 << 15) | // b0 (NTSC), b2 (PAL) - ((((frame / (fps * 60)) % 60) / 10) << 12) | // tens of minutes - ((((frame / (fps * 60)) % 60) % 10) << 8) | // units of minutes - (0 << 7) | // b1 - (0 << 6) | // b2 (NTSC), field phase (PAL) - ((((frame / (fps * 3600) % 24)) / 10) << 4) | // tens of hours - ( (frame / (fps * 3600) % 24)) % 10; // units of hours -} - static void mxf_write_system_item(AVFormatContext *s) { MXFContext *mxf = s->priv_data; @@ -1573,7 +1823,7 @@ static void mxf_write_system_item(AVFormatContext *s) unsigned frame; uint32_t time_code; - frame = mxf->timecode_start + mxf->last_indexed_edit_unit + mxf->edit_units_count; + frame = mxf->last_indexed_edit_unit + mxf->edit_units_count; // write system metadata pack avio_write(pb, system_metadata_pack_key, 16); @@ -1582,7 +1832,7 @@ static void mxf_write_system_item(AVFormatContext *s) avio_w8(pb, 0x04); // content package rate avio_w8(pb, 0x00); // content package type avio_wb16(pb, 0x00); // channel handle - avio_wb16(pb, frame); // continuity count + avio_wb16(pb, (mxf->tc.start + frame) & 0xFFFF); // continuity count, supposed to overflow if (mxf->essence_container_count > 1) avio_write(pb, multiple_desc_ul, 16); else { @@ -1594,8 +1844,7 @@ static void mxf_write_system_item(AVFormatContext *s) avio_wb64(pb, 0); // creation date/time stamp avio_w8(pb, 0x81); // SMPTE 12M time code - time_code = framenum_to_12m_time_code(frame, mxf->timecode_drop_frame, - mxf->timecode_base); + time_code = av_timecode_get_smpte_from_framenum(&mxf->tc, frame); avio_wb32(pb, time_code); avio_wb32(pb, 0); // binary group data avio_wb64(pb, 0); @@ -1612,7 +1861,8 @@ static void mxf_write_d10_video_packet(AVFormatContext *s, AVStream *st, AVPacke { MXFContext *mxf = s->priv_data; AVIOContext *pb = s->pb; - int packet_size = (uint64_t)st->codec->bit_rate*mxf->time_base.num / + MXFStreamContext *sc = st->priv_data; + int packet_size = (uint64_t)sc->video_bit_rate*mxf->time_base.num / (8*mxf->time_base.den); // frame size int pad; @@ -1628,13 +1878,11 @@ static void mxf_write_d10_video_packet(AVFormatContext *s, AVStream *st, AVPacke avio_write(s->pb, klv_fill_key, 16); pad -= 16 + 4; klv_encode_ber4_length(s->pb, pad); - for (; pad; pad--) - avio_w8(s->pb, 0); - assert(!(avio_tell(s->pb) & (KAG_SIZE-1))); + ffio_fill(s->pb, 0, pad); + av_assert1(!(avio_tell(s->pb) & (KAG_SIZE-1))); } else { av_log(s, AV_LOG_WARNING, "cannot fill d-10 video packet\n"); - for (; pad > 0; pad--) - avio_w8(s->pb, 0); + ffio_fill(s->pb, 0, pad); } } @@ -1693,6 +1941,16 @@ static int mxf_write_packet(AVFormatContext *s, AVPacket *pkt) av_log(s, AV_LOG_ERROR, "could not get mpeg2 profile and level\n"); return -1; } + } else if (st->codec->codec_id == AV_CODEC_ID_DNXHD) { + if (!mxf_parse_dnxhd_frame(s, st, pkt)) { + av_log(s, AV_LOG_ERROR, "could not get dnxhd profile\n"); + return -1; + } + } else if (st->codec->codec_id == AV_CODEC_ID_DVVIDEO) { + if (!mxf_parse_dv_frame(s, st, pkt)) { + av_log(s, AV_LOG_ERROR, "could not get dv profile\n"); + return -1; + } } if (!mxf->header_written) { @@ -1760,7 +2018,7 @@ static void mxf_write_random_index_pack(AVFormatContext *s) int i; avio_write(pb, random_index_pack_key, 16); - klv_encode_ber_length(pb, 28 + 12*mxf->body_partitions_count); + klv_encode_ber_length(pb, 28 + 12LL*mxf->body_partitions_count); if (mxf->edit_unit_byte_count) avio_wb32(pb, 1); // BodySID of header partition diff --git a/libavformat/mxg.c b/libavformat/mxg.c index 1d1488c059..b2b5b86c6f 100644 --- a/libavformat/mxg.c +++ b/libavformat/mxg.c @@ -2,20 +2,20 @@ * MxPEG clip file demuxer * Copyright (c) 2010 Anatoly Nenashev * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -75,7 +75,7 @@ static int mxg_read_header(AVFormatContext *s) static uint8_t* mxg_find_startmarker(uint8_t *p, uint8_t *end) { for (; p < end - 3; p += 4) { - uint32_t x = *(uint32_t*)p; + uint32_t x = AV_RN32(p); if (x & (~(x+0x01010101)) & 0x80808080) { if (p[0] == 0xff) { @@ -102,17 +102,19 @@ static int mxg_update_cache(AVFormatContext *s, unsigned int cache_size) MXGContext *mxg = s->priv_data; unsigned int current_pos = mxg->buffer_ptr - mxg->buffer; unsigned int soi_pos; + uint8_t *buffer; int ret; /* reallocate internal buffer */ if (current_pos > current_pos + cache_size) return AVERROR(ENOMEM); - if (mxg->soi_ptr) soi_pos = mxg->soi_ptr - mxg->buffer; - mxg->buffer = av_fast_realloc(mxg->buffer, &mxg->buffer_size, - current_pos + cache_size + - FF_INPUT_BUFFER_PADDING_SIZE); - if (!mxg->buffer) + soi_pos = mxg->soi_ptr - mxg->buffer; + buffer = av_fast_realloc(mxg->buffer, &mxg->buffer_size, + current_pos + cache_size + + FF_INPUT_BUFFER_PADDING_SIZE); + if (!buffer) return AVERROR(ENOMEM); + mxg->buffer = buffer; mxg->buffer_ptr = mxg->buffer + current_pos; if (mxg->soi_ptr) mxg->soi_ptr = mxg->buffer + soi_pos; @@ -134,7 +136,7 @@ static int mxg_read_packet(AVFormatContext *s, AVPacket *pkt) uint8_t *startmarker_ptr, *end, *search_end, marker; MXGContext *mxg = s->priv_data; - while (!s->pb->eof_reached && !s->pb->error){ + while (!url_feof(s->pb) && !s->pb->error){ if (mxg->cache_size <= OVERREAD_SIZE) { /* update internal buffer */ ret = mxg_update_cache(s, DEFAULT_PACKET_SIZE + OVERREAD_SIZE); diff --git a/libavformat/ncdec.c b/libavformat/ncdec.c index 40d8dac3a7..8cb7ed4bd4 100644 --- a/libavformat/ncdec.c +++ b/libavformat/ncdec.c @@ -3,20 +3,20 @@ * Copyright (c) 2009 Nicolas Martin (martinic at iro dot umontreal dot ca) * Edouard Auvinet * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -67,7 +67,7 @@ static int nc_read_packet(AVFormatContext *s, AVPacket *pkt) uint32_t state=-1; while (state != NC_VIDEO_FLAG) { - if (s->pb->eof_reached) + if (url_feof(s->pb)) return AVERROR(EIO); state = (state<<8) + avio_r8(s->pb); } diff --git a/libavformat/network.c b/libavformat/network.c index 6d308ebd01..2ba435d472 100644 --- a/libavformat/network.c +++ b/libavformat/network.c @@ -1,20 +1,20 @@ /* - * Copyright (c) 2007 The Libav Project + * Copyright (c) 2007 The FFmpeg Project * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -22,11 +22,15 @@ #include "network.h" #include "url.h" #include "libavcodec/internal.h" +#include "libavutil/avutil.h" #include "libavutil/mem.h" +#include "libavutil/time.h" #if HAVE_THREADS #if HAVE_PTHREADS #include <pthread.h> +#elif HAVE_OS2THREADS +#include "compat/os2threads.h" #else #include "compat/w32pthreads.h" #endif @@ -37,7 +41,6 @@ static int openssl_init; #if HAVE_THREADS #include <openssl/crypto.h> -#include "libavutil/avutil.h" pthread_mutex_t *openssl_mutexes; static void openssl_lock(int mode, int type, const char *file, int line) { @@ -147,6 +150,26 @@ int ff_network_wait_fd(int fd, int write) return ret < 0 ? ff_neterrno() : p.revents & (ev | POLLERR | POLLHUP) ? 0 : AVERROR(EAGAIN); } +int ff_network_wait_fd_timeout(int fd, int write, int64_t timeout, AVIOInterruptCB *int_cb) +{ + int ret; + int64_t wait_start = 0; + + while (1) { + if (ff_check_interrupt(int_cb)) + return AVERROR_EXIT; + ret = ff_network_wait_fd(fd, write); + if (ret != AVERROR(EAGAIN)) + return ret; + if (timeout > 0) { + if (!wait_start) + wait_start = av_gettime(); + else if (av_gettime() - wait_start > timeout) + return AVERROR(ETIMEDOUT); + } + } +} + void ff_network_close(void) { #if HAVE_WINSOCK2_H @@ -202,7 +225,7 @@ static int ff_poll_interrupt(struct pollfd *p, nfds_t nfds, int timeout, ret = poll(p, nfds, POLLING_TIME); if (ret != 0) break; - } while (timeout < 0 || runs-- > 0); + } while (timeout <= 0 || runs-- > 0); if (!ret) return AVERROR(ETIMEDOUT); @@ -222,8 +245,10 @@ int ff_socket(int af, int type, int proto) { fd = socket(af, type, proto); #if HAVE_FCNTL - if (fd != -1) - fcntl(fd, F_SETFD, FD_CLOEXEC); + if (fd != -1) { + if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) + av_log(NULL, AV_LOG_DEBUG, "Failed to set close on exec\n"); + } #endif } return fd; @@ -235,7 +260,9 @@ int ff_listen_bind(int fd, const struct sockaddr *addr, int ret; int reuse = 1; struct pollfd lp = { fd, POLLIN, 0 }; - setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)); + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse))) { + av_log(NULL, AV_LOG_WARNING, "setsockopt(SO_REUSEADDR) failed\n"); + } ret = bind(fd, addr, addrlen); if (ret) return ff_neterrno(); diff --git a/libavformat/network.h b/libavformat/network.h index 1d3fb7b7e0..c60e1424a1 100644 --- a/libavformat/network.h +++ b/libavformat/network.h @@ -1,20 +1,20 @@ /* - * Copyright (c) 2007 The Libav Project + * Copyright (c) 2007 The FFmpeg Project * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -27,6 +27,7 @@ #include "config.h" #include "libavutil/error.h" #include "os_support.h" +#include "avio.h" #include "url.h" #if HAVE_UNISTD_H @@ -82,6 +83,18 @@ void ff_tls_deinit(void); int ff_network_wait_fd(int fd, int write); +/** + * This works similarly to ff_network_wait_fd, but waits up to 'timeout' microseconds + * Uses ff_network_wait_fd in a loop + * + * @fd Socket descriptor + * @write Set 1 to wait for socket able to be read, 0 to be written + * @timeout Timeout interval, in microseconds. Actual precision is 100000 mcs, due to ff_network_wait_fd usage + * @param int_cb Interrupt callback, is checked before each ff_network_wait_fd call + * @return 0 if data can be read/written, AVERROR(ETIMEDOUT) if timeout expired, or negative error code + */ +int ff_network_wait_fd_timeout(int fd, int write, int64_t timeout, AVIOInterruptCB *int_cb); + int ff_inet_aton (const char * str, struct in_addr * add); #if !HAVE_STRUCT_SOCKADDR_STORAGE diff --git a/libavformat/nistspheredec.c b/libavformat/nistspheredec.c new file mode 100644 index 0000000000..c09df9ccc1 --- /dev/null +++ b/libavformat/nistspheredec.c @@ -0,0 +1,128 @@ +/* + * NIST Sphere demuxer + * Copyright (c) 2012 Paul B Mahol + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/avstring.h" +#include "libavutil/intreadwrite.h" +#include "avformat.h" +#include "internal.h" +#include "pcm.h" + +static int nist_probe(AVProbeData *p) +{ + if (AV_RL64(p->buf) == AV_RL64("NIST_1A\x0a")) + return AVPROBE_SCORE_MAX; + return 0; +} + +static int nist_read_header(AVFormatContext *s) +{ + char buffer[32], coding[32] = "pcm", format[32] = "01"; + int bps = 0, be = 0; + int32_t header_size; + AVStream *st; + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + + ff_get_line(s->pb, buffer, sizeof(buffer)); + ff_get_line(s->pb, buffer, sizeof(buffer)); + sscanf(buffer, "%"SCNd32, &header_size); + if (header_size <= 0) + return AVERROR_INVALIDDATA; + + while (!url_feof(s->pb)) { + ff_get_line(s->pb, buffer, sizeof(buffer)); + + if (avio_tell(s->pb) >= header_size) + return AVERROR_INVALIDDATA; + + if (!memcmp(buffer, "end_head", 8)) { + if (!st->codec->bits_per_coded_sample) + st->codec->bits_per_coded_sample = bps << 3; + + if (!av_strcasecmp(coding, "pcm")) { + st->codec->codec_id = ff_get_pcm_codec_id(st->codec->bits_per_coded_sample, + 0, be, 0xFFFF); + } else if (!av_strcasecmp(coding, "alaw")) { + st->codec->codec_id = AV_CODEC_ID_PCM_ALAW; + } else if (!av_strcasecmp(coding, "ulaw") || + !av_strcasecmp(coding, "mu-law")) { + st->codec->codec_id = AV_CODEC_ID_PCM_MULAW; + } else { + avpriv_request_sample(s, "coding %s", coding); + } + + avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate); + + st->codec->block_align = st->codec->bits_per_coded_sample * st->codec->channels / 8; + + if (avio_tell(s->pb) > header_size) + return AVERROR_INVALIDDATA; + + avio_skip(s->pb, header_size - avio_tell(s->pb)); + + return 0; + } else if (!memcmp(buffer, "channel_count", 13)) { + sscanf(buffer, "%*s %*s %"SCNd32, &st->codec->channels); + } else if (!memcmp(buffer, "sample_byte_format", 18)) { + sscanf(buffer, "%*s %*s %31s", format); + + if (!av_strcasecmp(format, "01")) { + be = 0; + } else if (!av_strcasecmp(format, "10")) { + be = 1; + } else if (av_strcasecmp(format, "1")) { + avpriv_request_sample(s, "sample byte format %s", format); + return AVERROR_PATCHWELCOME; + } + } else if (!memcmp(buffer, "sample_coding", 13)) { + sscanf(buffer, "%*s %*s %31s", coding); + } else if (!memcmp(buffer, "sample_count", 12)) { + sscanf(buffer, "%*s %*s %"SCNd64, &st->duration); + } else if (!memcmp(buffer, "sample_n_bytes", 14)) { + sscanf(buffer, "%*s %*s %"SCNd32, &bps); + } else if (!memcmp(buffer, "sample_rate", 11)) { + sscanf(buffer, "%*s %*s %"SCNd32, &st->codec->sample_rate); + } else if (!memcmp(buffer, "sample_sig_bits", 15)) { + sscanf(buffer, "%*s %*s %"SCNd32, &st->codec->bits_per_coded_sample); + } else { + char key[32], value[32]; + sscanf(buffer, "%31s %*s %31s", key, value); + av_dict_set(&s->metadata, key, value, AV_DICT_APPEND); + } + } + + return AVERROR_EOF; +} + +AVInputFormat ff_nistsphere_demuxer = { + .name = "nistsphere", + .long_name = NULL_IF_CONFIG_SMALL("NIST SPeech HEader REsources"), + .read_probe = nist_probe, + .read_header = nist_read_header, + .read_packet = ff_pcm_read_packet, + .read_seek = ff_pcm_read_seek, + .extensions = "nist,sph", + .flags = AVFMT_GENERIC_INDEX, +}; diff --git a/libavformat/noproxy-test.c b/libavformat/noproxy-test.c index e6cc421413..4524764cbf 100644 --- a/libavformat/noproxy-test.c +++ b/libavformat/noproxy-test.c @@ -1,20 +1,20 @@ /* * Copyright (c) 2013 Martin Storsjo * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/nsvdec.c b/libavformat/nsvdec.c index 18ebbe35f4..4a749a2feb 100644 --- a/libavformat/nsvdec.c +++ b/libavformat/nsvdec.c @@ -1,23 +1,23 @@ /* * NSV demuxer - * Copyright (c) 2004 The Libav Project + * Copyright (c) 2004 The FFmpeg Project * * first version by Francois Revol <revol@free.fr> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -26,6 +26,7 @@ #include "avformat.h" #include "internal.h" #include "libavutil/dict.h" +#include "libavutil/intreadwrite.h" /* max bytes to crawl for trying to resync * stupid streaming servers don't start at chunk boundaries... @@ -69,11 +70,7 @@ * so the header seems to not be mandatory. (for streaming). * * index slice duration check (excepts nsvtrailer.nsv): - * for f in [^n]*.nsv; do - * DUR="$(avconv -i "$f" 2> /dev/null | grep 'NSVf duration' | cut -d ' ' -f 4)" - * IC="$(avconv -i "$f" 2> /dev/null | grep 'INDEX ENTRIES' | cut -d ' ' -f 2)" - * echo "duration $DUR, slite time $(($DUR/$IC))" - * done + * for f in [^n]*.nsv; do DUR="$(ffmpeg -i "$f" 2>/dev/null | grep 'NSVf duration' | cut -d ' ' -f 4)"; IC="$(ffmpeg -i "$f" 2>/dev/null | grep 'INDEX ENTRIES' | cut -d ' ' -f 2)"; echo "duration $DUR, slite time $(($DUR/$IC))"; done */ /* @@ -99,8 +96,8 @@ struct NSVs_header { uint32_t chunk_tag; /* 'NSVs' */ uint32_t v4cc; /* or 'NONE' */ uint32_t a4cc; /* or 'NONE' */ - uint16_t vwidth; /* assert(vwidth%16==0) */ - uint16_t vheight; /* assert(vheight%16==0) */ + uint16_t vwidth; /* av_assert(vwidth%16==0) */ + uint16_t vheight; /* av_assert(vheight%16==0) */ uint8_t framerate; /* value = (framerate&0x80)?frtable[frameratex0x7f]:framerate */ uint16_t unknown; }; @@ -192,6 +189,7 @@ static const AVCodecTag nsv_codec_video_tags[] = { { AV_CODEC_ID_VP6, MKTAG('V', 'P', '6', '0') }, { AV_CODEC_ID_VP6, MKTAG('V', 'P', '6', '1') }, { AV_CODEC_ID_VP6, MKTAG('V', 'P', '6', '2') }, + { AV_CODEC_ID_VP8, MKTAG('V', 'P', '8', '0') }, /* { AV_CODEC_ID_VP4, MKTAG('V', 'P', '4', ' ') }, { AV_CODEC_ID_VP4, MKTAG('V', 'P', '4', '0') }, @@ -205,6 +203,7 @@ static const AVCodecTag nsv_codec_audio_tags[] = { { AV_CODEC_ID_MP3, MKTAG('M', 'P', '3', ' ') }, { AV_CODEC_ID_AAC, MKTAG('A', 'A', 'C', ' ') }, { AV_CODEC_ID_AAC, MKTAG('A', 'A', 'C', 'P') }, + { AV_CODEC_ID_AAC, MKTAG('V', 'L', 'B', ' ') }, { AV_CODEC_ID_SPEEX, MKTAG('S', 'P', 'X', ' ') }, { AV_CODEC_ID_PCM_U16LE, MKTAG('P', 'C', 'M', ' ') }, { AV_CODEC_ID_NONE, 0 }, @@ -233,7 +232,7 @@ static int nsv_resync(AVFormatContext *s) //nsv->state = NSV_UNSYNC; for (i = 0; i < NSV_MAX_RESYNC; i++) { - if (pb->eof_reached) { + if (url_feof(pb)) { av_dlog(s, "NSV EOF\n"); nsv->state = NSV_UNSYNC; return -1; @@ -300,7 +299,7 @@ static int nsv_parse_NSVf_header(AVFormatContext *s) table_entries_used = avio_rl32(pb); av_dlog(s, "NSV NSVf info-strings size: %d, table entries: %d, bis %d\n", strings_size, table_entries, table_entries_used); - if (pb->eof_reached) + if (url_feof(pb)) return -1; av_dlog(s, "NSV got header; filepos %"PRId64"\n", avio_tell(pb)); @@ -337,7 +336,7 @@ static int nsv_parse_NSVf_header(AVFormatContext *s) } av_free(strings); } - if (pb->eof_reached) + if (url_feof(pb)) return -1; av_dlog(s, "NSV got infos; filepos %"PRId64"\n", avio_tell(pb)); @@ -369,7 +368,7 @@ static int nsv_parse_NSVf_header(AVFormatContext *s) avio_seek(pb, nsv->base_offset + size, SEEK_SET); /* required for dumbdriving-271.nsv (2 extra bytes) */ - if (pb->eof_reached) + if (url_feof(pb)) return -1; nsv->state = NSV_HAS_READ_NSVF; return 0; @@ -547,7 +546,7 @@ static int nsv_read_chunk(AVFormatContext *s, int fill_header) return 0; //-1; /* hey! eat what you've in your plate first! */ null_chunk_retry: - if (pb->eof_reached) + if (url_feof(pb)) return -1; for (i = 0; i < NSV_MAX_RESYNC_TRIES && nsv->state < NSV_FOUND_NSVS && !err; i++) @@ -582,7 +581,7 @@ null_chunk_retry: vsize -= auxsize + sizeof(uint16_t) + sizeof(uint32_t); /* that's becoming braindead */ } - if (pb->eof_reached) + if (url_feof(pb)) return -1; if (!vsize && !asize) { nsv->state = NSV_UNSYNC; @@ -627,7 +626,10 @@ null_chunk_retry: if (bps != 16) { av_dlog(s, "NSV AUDIO bit/sample != 16 (%d)!!!\n", bps); } - bps /= channels; // ??? + if(channels) + bps /= channels; // ??? + else + av_log(s, AV_LOG_WARNING, "Channels is 0\n"); if (bps == 8) st[NSV_ST_AUDIO]->codec->codec_id = AV_CODEC_ID_PCM_U8; samplerate /= 4;/* UGH ??? XXX */ @@ -732,10 +734,8 @@ static int nsv_read_close(AVFormatContext *s) static int nsv_probe(AVProbeData *p) { - int i; - int score; - int vsize, asize, auxcount; - score = 0; + int i, score = 0; + av_dlog(NULL, "nsv_probe(), buf_size %d\n", p->buf_size); /* check file header */ /* streamed files might not have any header */ @@ -747,19 +747,14 @@ static int nsv_probe(AVProbeData *p) /* seems the servers don't bother starting clean chunks... */ /* sometimes even the first header is at 9KB or something :^) */ for (i = 1; i < p->buf_size - 3; i++) { - if (p->buf[i+0] == 'N' && p->buf[i+1] == 'S' && - p->buf[i+2] == 'V' && p->buf[i+3] == 's') { - score = AVPROBE_SCORE_MAX/5; + if (AV_RL32(p->buf + i) == AV_RL32("NSVs")) { /* Get the chunk size and check if at the end we are getting 0xBEEF */ - auxcount = p->buf[i+19]; - vsize = p->buf[i+20] | p->buf[i+21] << 8; - asize = p->buf[i+22] | p->buf[i+23] << 8; - vsize = (vsize << 4) | (auxcount >> 4); - if ((asize + vsize + i + 23) < p->buf_size - 2) { - if (p->buf[i+23+asize+vsize+1] == 0xEF && - p->buf[i+23+asize+vsize+2] == 0xBE) - return AVPROBE_SCORE_MAX-20; - } + int vsize = AV_RL24(p->buf+i+19) >> 4; + int asize = AV_RL16(p->buf+i+22); + int offset = i + 23 + asize + vsize + 1; + if (offset <= p->buf_size - 2 && AV_RL16(p->buf + offset) == 0xBEEF) + return 4*AVPROBE_SCORE_MAX/5; + score = AVPROBE_SCORE_MAX/5; } } /* so we'll have more luck on extension... */ diff --git a/libavformat/nullenc.c b/libavformat/nullenc.c index 829f2a841e..7da297b63e 100644 --- a/libavformat/nullenc.c +++ b/libavformat/nullenc.c @@ -2,20 +2,20 @@ * RAW null muxer * Copyright (c) 2002 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/nut.c b/libavformat/nut.c index d9a042bca0..8b8a4cb8ac 100644 --- a/libavformat/nut.c +++ b/libavformat/nut.c @@ -2,20 +2,20 @@ * nut * Copyright (c) 2004-2007 Michael Niedermayer * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -27,9 +27,10 @@ const AVCodecTag ff_nut_subtitle_tags[] = { { AV_CODEC_ID_TEXT, MKTAG('U', 'T', 'F', '8') }, - { AV_CODEC_ID_SSA, MKTAG('S', 'S', 'A', 0) }, + { AV_CODEC_ID_SSA, MKTAG('S', 'S', 'A', 0 ) }, { AV_CODEC_ID_DVD_SUBTITLE, MKTAG('D', 'V', 'D', 'S') }, { AV_CODEC_ID_DVB_SUBTITLE, MKTAG('D', 'V', 'B', 'S') }, + { AV_CODEC_ID_DVB_TELETEXT, MKTAG('D', 'V', 'B', 'T') }, { AV_CODEC_ID_NONE, 0 } }; @@ -40,24 +41,28 @@ const AVCodecTag ff_nut_data_tags[] = { const AVCodecTag ff_nut_video_tags[] = { { AV_CODEC_ID_VP9, MKTAG('V', 'P', '9', '0') }, - { AV_CODEC_ID_RAWVIDEO, MKTAG('R', 'G', 'B', 15) }, - { AV_CODEC_ID_RAWVIDEO, MKTAG('B', 'G', 'R', 15) }, - { AV_CODEC_ID_RAWVIDEO, MKTAG('R', 'G', 'B', 16) }, - { AV_CODEC_ID_RAWVIDEO, MKTAG('B', 'G', 'R', 16) }, - { AV_CODEC_ID_RAWVIDEO, MKTAG(15, 'B', 'G', 'R') }, - { AV_CODEC_ID_RAWVIDEO, MKTAG(15, 'R', 'G', 'B') }, - { AV_CODEC_ID_RAWVIDEO, MKTAG(16, 'B', 'G', 'R') }, - { AV_CODEC_ID_RAWVIDEO, MKTAG(16, 'R', 'G', 'B') }, - { AV_CODEC_ID_RAWVIDEO, MKTAG('R', 'G', 'B', 12) }, - { AV_CODEC_ID_RAWVIDEO, MKTAG('B', 'G', 'R', 12) }, - { AV_CODEC_ID_RAWVIDEO, MKTAG(12, 'B', 'G', 'R') }, - { AV_CODEC_ID_RAWVIDEO, MKTAG(12, 'R', 'G', 'B') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('R', 'G', 'B', 15 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('B', 'G', 'R', 15 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('R', 'G', 'B', 16 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('B', 'G', 'R', 16 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(15 , 'B', 'G', 'R') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(15 , 'R', 'G', 'B') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(16 , 'B', 'G', 'R') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(16 , 'R', 'G', 'B') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('R', 'G', 'B', 12 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('B', 'G', 'R', 12 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(12 , 'B', 'G', 'R') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(12 , 'R', 'G', 'B') }, { AV_CODEC_ID_RAWVIDEO, MKTAG('R', 'G', 'B', 'A') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('R', 'G', 'B', 0 ) }, { AV_CODEC_ID_RAWVIDEO, MKTAG('B', 'G', 'R', 'A') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('B', 'G', 'R', 0 ) }, { AV_CODEC_ID_RAWVIDEO, MKTAG('A', 'B', 'G', 'R') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG( 0 , 'B', 'G', 'R') }, { AV_CODEC_ID_RAWVIDEO, MKTAG('A', 'R', 'G', 'B') }, - { AV_CODEC_ID_RAWVIDEO, MKTAG('R', 'G', 'B', 24) }, - { AV_CODEC_ID_RAWVIDEO, MKTAG('B', 'G', 'R', 24) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG( 0 , 'R', 'G', 'B') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('R', 'G', 'B', 24 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('B', 'G', 'R', 24 ) }, { AV_CODEC_ID_RAWVIDEO, MKTAG('4', '1', '1', 'P') }, { AV_CODEC_ID_RAWVIDEO, MKTAG('4', '2', '2', 'P') }, { AV_CODEC_ID_RAWVIDEO, MKTAG('4', '2', '2', 'P') }, @@ -67,32 +72,50 @@ const AVCodecTag ff_nut_video_tags[] = { { AV_CODEC_ID_RAWVIDEO, MKTAG('4', '4', '4', 'P') }, { AV_CODEC_ID_RAWVIDEO, MKTAG('B', '1', 'W', '0') }, { AV_CODEC_ID_RAWVIDEO, MKTAG('B', '0', 'W', '1') }, - { AV_CODEC_ID_RAWVIDEO, MKTAG('B', 'G', 'R', 8) }, - { AV_CODEC_ID_RAWVIDEO, MKTAG('R', 'G', 'B', 8) }, - { AV_CODEC_ID_RAWVIDEO, MKTAG('B', 'G', 'R', 4) }, - { AV_CODEC_ID_RAWVIDEO, MKTAG('R', 'G', 'B', 4) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('B', 'G', 'R', 8 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('R', 'G', 'B', 8 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('B', 'G', 'R', 4 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('R', 'G', 'B', 4 ) }, { AV_CODEC_ID_RAWVIDEO, MKTAG('B', '4', 'B', 'Y') }, { AV_CODEC_ID_RAWVIDEO, MKTAG('R', '4', 'B', 'Y') }, - { AV_CODEC_ID_RAWVIDEO, MKTAG('B', 'G', 'R', 48) }, - { AV_CODEC_ID_RAWVIDEO, MKTAG('R', 'G', 'B', 48) }, - { AV_CODEC_ID_RAWVIDEO, MKTAG(48, 'B', 'G', 'R') }, - { AV_CODEC_ID_RAWVIDEO, MKTAG(48, 'R', 'G', 'B') }, - { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '3', 11, 10) }, - { AV_CODEC_ID_RAWVIDEO, MKTAG(10, 11, '3', 'Y') }, - { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '3', 10, 10) }, - { AV_CODEC_ID_RAWVIDEO, MKTAG(10, 10, '3', 'Y') }, - { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '3', 0, 10) }, - { AV_CODEC_ID_RAWVIDEO, MKTAG(10, 0, '3', 'Y') }, - { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '1', 0, 16) }, - { AV_CODEC_ID_RAWVIDEO, MKTAG(16, 0, '1', 'Y') }, - { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '3', 11, 16) }, - { AV_CODEC_ID_RAWVIDEO, MKTAG(16, 11, '3', 'Y') }, - { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '3', 10, 16) }, - { AV_CODEC_ID_RAWVIDEO, MKTAG(16, 10, '3', 'Y') }, - { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '3', 0, 16) }, - { AV_CODEC_ID_RAWVIDEO, MKTAG(16, 0, '3', 'Y') }, - { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '4', 11, 8) }, - { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '2', 0, 8) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('B', 'G', 'R', 48 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('R', 'G', 'B', 48 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(48 , 'B', 'G', 'R') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(48 , 'R', 'G', 'B') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('B', 'R', 'A', 64 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('R', 'B', 'A', 64 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(64 , 'B', 'R', 'A') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(64 , 'R', 'B', 'A') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '3', 11 , 10 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(10 , 11 , '3', 'Y') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '3', 10 , 10 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(10 , 10 , '3', 'Y') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '3', 0 , 10 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(10 , 0 , '3', 'Y') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '3', 11 , 12 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(12 , 11 , '3', 'Y') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '3', 10 , 12 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(12 , 10 , '3', 'Y') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '3', 0 , 12 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(12 , 0 , '3', 'Y') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '3', 11 , 14 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(14 , 11 , '3', 'Y') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '3', 10 , 14 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(14 , 10 , '3', 'Y') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '3', 0 , 14 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(14 , 0 , '3', 'Y') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '1', 0 , 16 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(16 , 0 , '1', 'Y') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '3', 11 , 16 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(16 , 11 , '3', 'Y') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '3', 10 , 16 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(16 , 10 , '3', 'Y') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '3', 0 , 16 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(16 , 0 , '3', 'Y') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '4', 11 , 8 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '4', 10 , 8 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '4', 0 , 8 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '2', 0 , 8 ) }, { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '1', 0, 9) }, { AV_CODEC_ID_RAWVIDEO, MKTAG(9, 0, '1', 'Y') }, @@ -121,40 +144,59 @@ const AVCodecTag ff_nut_video_tags[] = { { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '4', 0, 16) }, { AV_CODEC_ID_RAWVIDEO, MKTAG(16, 0, '4', 'Y') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('G', '3', 0, 8) }, + + { AV_CODEC_ID_RAWVIDEO, MKTAG('G', '3', 0, 9) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG( 9, 0, '3', 'G') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('G', '3', 0, 10) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(10, 0, '3', 'G') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('G', '3', 0, 12) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(12, 0, '3', 'G') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('G', '3', 0, 14) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(14, 0, '3', 'G') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('G', '3', 0, 16) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(16, 0, '3', 'G') }, + { AV_CODEC_ID_NONE, 0 } }; -const AVCodecTag ff_nut_audio_tags[] = { +static const AVCodecTag nut_audio_extra_tags[] = { { AV_CODEC_ID_PCM_ALAW, MKTAG('A', 'L', 'A', 'W') }, { AV_CODEC_ID_PCM_MULAW, MKTAG('U', 'L', 'A', 'W') }, - { AV_CODEC_ID_PCM_F32BE, MKTAG(32, 'D', 'F', 'P') }, - { AV_CODEC_ID_PCM_F32LE, MKTAG('P', 'F', 'D', 32) }, - { AV_CODEC_ID_PCM_F64BE, MKTAG(64, 'D', 'F', 'P') }, - { AV_CODEC_ID_PCM_F64LE, MKTAG('P', 'F', 'D', 64) }, - { AV_CODEC_ID_PCM_S16BE, MKTAG(16, 'D', 'S', 'P') }, - { AV_CODEC_ID_PCM_S16LE, MKTAG('P', 'S', 'D', 16) }, - { AV_CODEC_ID_PCM_S24BE, MKTAG(24, 'D', 'S', 'P') }, - { AV_CODEC_ID_PCM_S24LE, MKTAG('P', 'S', 'D', 24) }, - { AV_CODEC_ID_PCM_S32BE, MKTAG(32, 'D', 'S', 'P') }, - { AV_CODEC_ID_PCM_S32LE, MKTAG('P', 'S', 'D', 32) }, - { AV_CODEC_ID_PCM_S8, MKTAG('P', 'S', 'D', 8) }, - { AV_CODEC_ID_PCM_U16BE, MKTAG(16, 'D', 'U', 'P') }, - { AV_CODEC_ID_PCM_U16LE, MKTAG('P', 'U', 'D', 16) }, - { AV_CODEC_ID_PCM_U24BE, MKTAG(24, 'D', 'U', 'P') }, - { AV_CODEC_ID_PCM_U24LE, MKTAG('P', 'U', 'D', 24) }, - { AV_CODEC_ID_PCM_U32BE, MKTAG(32, 'D', 'U', 'P') }, - { AV_CODEC_ID_PCM_U32LE, MKTAG('P', 'U', 'D', 32) }, - { AV_CODEC_ID_PCM_U8, MKTAG('P', 'U', 'D', 8) }, - { AV_CODEC_ID_PCM_S16LE_PLANAR, MKTAG('P', 'S', 'P', 16) }, - { AV_CODEC_ID_PCM_S24LE_PLANAR, MKTAG('P', 'S', 'P', 24) }, - { AV_CODEC_ID_PCM_S32LE_PLANAR, MKTAG('P', 'S', 'P', 32) }, { AV_CODEC_ID_MP3, MKTAG('M', 'P', '3', ' ') }, - { AV_CODEC_ID_NONE, 0 } + { AV_CODEC_ID_NONE, 0 } +}; + +const AVCodecTag ff_nut_audio_tags[] = { + { AV_CODEC_ID_PCM_F32BE, MKTAG(32 , 'D', 'F', 'P') }, + { AV_CODEC_ID_PCM_F32LE, MKTAG('P', 'F', 'D', 32 ) }, + { AV_CODEC_ID_PCM_F64BE, MKTAG(64 , 'D', 'F', 'P') }, + { AV_CODEC_ID_PCM_F64LE, MKTAG('P', 'F', 'D', 64 ) }, + { AV_CODEC_ID_PCM_S16BE, MKTAG(16 , 'D', 'S', 'P') }, + { AV_CODEC_ID_PCM_S16LE, MKTAG('P', 'S', 'D', 16 ) }, + { AV_CODEC_ID_PCM_S24BE, MKTAG(24 , 'D', 'S', 'P') }, + { AV_CODEC_ID_PCM_S24LE, MKTAG('P', 'S', 'D', 24 ) }, + { AV_CODEC_ID_PCM_S32BE, MKTAG(32 , 'D', 'S', 'P') }, + { AV_CODEC_ID_PCM_S32LE, MKTAG('P', 'S', 'D', 32 ) }, + { AV_CODEC_ID_PCM_S8, MKTAG('P', 'S', 'D', 8 ) }, + { AV_CODEC_ID_PCM_U16BE, MKTAG(16 , 'D', 'U', 'P') }, + { AV_CODEC_ID_PCM_U16LE, MKTAG('P', 'U', 'D', 16 ) }, + { AV_CODEC_ID_PCM_U24BE, MKTAG(24 , 'D', 'U', 'P') }, + { AV_CODEC_ID_PCM_U24LE, MKTAG('P', 'U', 'D', 24 ) }, + { AV_CODEC_ID_PCM_U32BE, MKTAG(32 , 'D', 'U', 'P') }, + { AV_CODEC_ID_PCM_U32LE, MKTAG('P', 'U', 'D', 32 ) }, + { AV_CODEC_ID_PCM_U8, MKTAG('P', 'U', 'D', 8 ) }, + { AV_CODEC_ID_PCM_S8_PLANAR, MKTAG('P', 'S', 'P', 8 ) }, + { AV_CODEC_ID_PCM_S16BE_PLANAR, MKTAG(16 , 'P', 'S', 'P') }, + { AV_CODEC_ID_PCM_S16LE_PLANAR, MKTAG('P', 'S', 'P', 16 ) }, + { AV_CODEC_ID_PCM_S24LE_PLANAR, MKTAG('P', 'S', 'P', 24 ) }, + { AV_CODEC_ID_PCM_S32LE_PLANAR, MKTAG('P', 'S', 'P', 32 ) }, + { AV_CODEC_ID_NONE, 0 } }; const AVCodecTag * const ff_nut_codec_tags[] = { ff_nut_video_tags, ff_nut_audio_tags, ff_nut_subtitle_tags, - ff_codec_bmp_tags, ff_codec_wav_tags, ff_nut_data_tags, 0 + ff_codec_bmp_tags, ff_codec_wav_tags, nut_audio_extra_tags, ff_nut_data_tags, 0 }; void ff_nut_reset_ts(NUTContext *nut, AVRational time_base, int64_t val) @@ -170,7 +212,7 @@ void ff_nut_reset_ts(NUTContext *nut, AVRational time_base, int64_t val) int64_t ff_lsb2full(StreamContext *stream, int64_t lsb) { - int64_t mask = (1 << stream->msb_pts_shift) - 1; + int64_t mask = (1ULL << stream->msb_pts_shift) - 1; int64_t delta = stream->last_pts - mask / 2; return ((lsb - delta) & mask) + delta; } @@ -196,6 +238,8 @@ int ff_nut_add_sp(NUTContext *nut, int64_t pos, int64_t back_ptr, int64_t ts) return AVERROR(ENOMEM); } + nut->sp_count++; + sp->pos = pos; sp->back_ptr = back_ptr; sp->ts = ts; diff --git a/libavformat/nut.h b/libavformat/nut.h index 6357b3d2b1..da456ac5b0 100644 --- a/libavformat/nut.h +++ b/libavformat/nut.h @@ -2,20 +2,20 @@ * "NUT" Container Format (de)muxer * Copyright (c) 2006 Michael Niedermayer * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -78,6 +78,7 @@ typedef struct StreamContext { int msb_pts_shift; int max_pts_distance; int decode_delay; //FIXME duplicate of has_b_frames + int64_t *keyframe_pts; } StreamContext; typedef struct ChapterContext { @@ -100,6 +101,9 @@ typedef struct NUTContext { int header_count; AVRational *time_base; struct AVTreeNode *syncpoints; + int sp_count; + int64_t max_pts; + AVRational *max_pts_tb; } NUTContext; extern const AVCodecTag ff_nut_subtitle_tags[]; diff --git a/libavformat/nutdec.c b/libavformat/nutdec.c index 6328549d7a..aa7ca676fb 100644 --- a/libavformat/nutdec.c +++ b/libavformat/nutdec.c @@ -3,24 +3,25 @@ * Copyright (c) 2004-2006 Michael Niedermayer * Copyright (c) 2003 Alex Beregszaszi * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "libavutil/avstring.h" +#include "libavutil/avassert.h" #include "libavutil/bswap.h" #include "libavutil/dict.h" #include "libavutil/mathematics.h" @@ -29,11 +30,11 @@ #include "nut.h" #include "riff.h" -#undef NDEBUG -#include <assert.h> - #define NUT_MAX_STREAMS 256 /* arbitrary sanity check value */ +static int64_t nut_read_timestamp(AVFormatContext *s, int stream_index, + int64_t *pos_arg, int64_t pos_limit); + static int get_str(AVIOContext *bc, char *string, unsigned int maxlen) { unsigned int len = ffio_read_varlen(bc); @@ -72,8 +73,10 @@ static uint64_t get_fourcc(AVIOContext *bc) return avio_rl16(bc); else if (len == 4) return avio_rl32(bc); - else + else { + av_log(NULL, AV_LOG_ERROR, "Unsupported fourcc length %d\n", len); return -1; + } } #ifdef TRACE @@ -97,8 +100,18 @@ static inline int64_t get_s_trace(AVIOContext *bc, const char *file, return v; } +static inline uint64_t get_4cc_trace(AVIOContext *bc, char *file, + char *func, int line) +{ + uint64_t v = get_fourcc(bc); + + av_log(NULL, AV_LOG_DEBUG, "get_fourcc %5"PRId64" / %"PRIX64" in %s %s:%d\n", + v, v, file, func, line); + return v; +} #define ffio_read_varlen(bc) get_v_trace(bc, __FILE__, __PRETTY_FUNCTION__, __LINE__) #define get_s(bc) get_s_trace(bc, __FILE__, __PRETTY_FUNCTION__, __LINE__) +#define get_fourcc(bc) get_4cc_trace(bc, __FILE__, __PRETTY_FUNCTION__, __LINE__) #endif static int get_packetheader(NUTContext *nut, AVIOContext *bc, @@ -130,7 +143,7 @@ static uint64_t find_any_startcode(AVIOContext *bc, int64_t pos) /* Note, this may fail if the stream is not seekable, but that should * not matter, as in this case we simply start where we currently are */ avio_seek(bc, pos, SEEK_SET); - while (!bc->eof_reached) { + while (!url_feof(bc)) { state = (state << 8) | avio_r8(bc); if ((state >> 56) != 'N') continue; @@ -230,6 +243,8 @@ static int decode_main_header(NUTContext *nut) GET_V(nut->time_base_count, tmp > 0 && tmp < INT_MAX / sizeof(AVRational)); nut->time_base = av_malloc(nut->time_base_count * sizeof(AVRational)); + if (!nut->time_base) + return AVERROR(ENOMEM); for (i = 0; i < nut->time_base_count; i++) { GET_V(nut->time_base[i].num, tmp > 0 && tmp < (1ULL << 31)); @@ -297,7 +312,7 @@ static int decode_main_header(NUTContext *nut) nut->frame_code[i].header_idx = tmp_head_idx; } } - assert(nut->frame_code['N'].flags == FLAG_INVALID); + av_assert0(nut->frame_code['N'].flags == FLAG_INVALID); if (end > avio_tell(bc) + 4) { int rem = 1024; @@ -317,7 +332,7 @@ static int decode_main_header(NUTContext *nut) avio_read(bc, hdr, nut->header_len[i]); nut->header[i] = hdr; } - assert(nut->header_len[0] == 0); + av_assert0(nut->header_len[0] == 0); } if (skip_reserved(bc, end) || ffio_get_checksum(bc)) { @@ -325,7 +340,9 @@ static int decode_main_header(NUTContext *nut) return AVERROR_INVALIDDATA; } - nut->stream = av_mallocz(sizeof(StreamContext) * stream_count); + nut->stream = av_calloc(stream_count, sizeof(StreamContext)); + if (!nut->stream) + return AVERROR(ENOMEM); for (i = 0; i < stream_count; i++) avformat_new_stream(s, NULL); @@ -398,8 +415,8 @@ static int decode_stream_header(NUTContext *nut) GET_V(st->codec->extradata_size, tmp < (1 << 30)); if (st->codec->extradata_size) { - st->codec->extradata = av_mallocz(st->codec->extradata_size + - FF_INPUT_BUFFER_PADDING_SIZE); + if (ff_alloc_extradata(st->codec, st->codec->extradata_size)) + return AVERROR(ENOMEM); avio_read(bc, st->codec->extradata, st->codec->extradata_size); } @@ -514,6 +531,14 @@ static int decode_info_header(NUTContext *nut) set_disposition_bits(s, str_value, stream_id_plus1 - 1); continue; } + + if (stream_id_plus1 && !strcmp(name, "r_frame_rate")) { + sscanf(str_value, "%d/%d", &st->r_frame_rate.num, &st->r_frame_rate.den); + if (st->r_frame_rate.num >= 1000LL*st->r_frame_rate.den) + st->r_frame_rate.num = st->r_frame_rate.den = 0; + continue; + } + if (metadata && av_strcasecmp(name, "Uses") && av_strcasecmp(name, "Depends") && av_strcasecmp(name, "Replaces")) av_dict_set(metadata, name, str_value, 0); @@ -531,7 +556,8 @@ static int decode_syncpoint(NUTContext *nut, int64_t *ts, int64_t *back_ptr) { AVFormatContext *s = nut->avf; AVIOContext *bc = s->pb; - int64_t end, tmp; + int64_t end; + uint64_t tmp; int ret; nut->last_syncpoint_pos = avio_tell(bc) - 8; @@ -542,7 +568,7 @@ static int decode_syncpoint(NUTContext *nut, int64_t *ts, int64_t *back_ptr) tmp = ffio_read_varlen(bc); *back_ptr = nut->last_syncpoint_pos - 16 * ffio_read_varlen(bc); if (*back_ptr < 0) - return -1; + return AVERROR_INVALIDDATA; ff_nut_reset_ts(nut, nut->time_base[tmp % nut->time_base_count], tmp / nut->time_base_count); @@ -552,8 +578,8 @@ static int decode_syncpoint(NUTContext *nut, int64_t *ts, int64_t *back_ptr) return AVERROR_INVALIDDATA; } - *ts = tmp / s->nb_streams * - av_q2d(nut->time_base[tmp % s->nb_streams]) * AV_TIME_BASE; + *ts = tmp / nut->time_base_count * + av_q2d(nut->time_base[tmp % nut->time_base_count]) * AV_TIME_BASE; if ((ret = ff_nut_add_sp(nut, nut->last_syncpoint_pos, *back_ptr, *ts)) < 0) return ret; @@ -561,6 +587,19 @@ static int decode_syncpoint(NUTContext *nut, int64_t *ts, int64_t *back_ptr) return 0; } +//FIXME calculate exactly, this is just a good approximation. +static int64_t find_duration(NUTContext *nut, int64_t filesize) +{ + AVFormatContext *s = nut->avf; + int64_t duration = 0; + + ff_find_last_ts(s, -1, &duration, NULL, nut_read_timestamp); + + if(duration > 0) + s->duration_estimation_method = AVFMT_DURATION_FROM_PTS; + return duration; +} + static int find_and_decode_index(NUTContext *nut) { AVFormatContext *s = nut->avf; @@ -569,23 +608,39 @@ static int find_and_decode_index(NUTContext *nut) int i, j, syncpoint_count; int64_t filesize = avio_size(bc); int64_t *syncpoints; + uint64_t max_pts; int8_t *has_keyframe; int ret = AVERROR_INVALIDDATA; + if(filesize <= 0) + return -1; + avio_seek(bc, filesize - 12, SEEK_SET); avio_seek(bc, filesize - avio_rb64(bc), SEEK_SET); if (avio_rb64(bc) != INDEX_STARTCODE) { av_log(s, AV_LOG_ERROR, "no index at the end\n"); + + if(s->duration<=0) + s->duration = find_duration(nut, filesize); return ret; } end = get_packetheader(nut, bc, 1, INDEX_STARTCODE); end += avio_tell(bc); - ffio_read_varlen(bc); // max_pts + max_pts = ffio_read_varlen(bc); + s->duration = av_rescale_q(max_pts / nut->time_base_count, + nut->time_base[max_pts % nut->time_base_count], + AV_TIME_BASE_Q); + s->duration_estimation_method = AVFMT_DURATION_FROM_PTS; + GET_V(syncpoint_count, tmp < INT_MAX / 8 && tmp > 0); - syncpoints = av_malloc(sizeof(int64_t) * syncpoint_count); - has_keyframe = av_malloc(sizeof(int8_t) * (syncpoint_count + 1)); + syncpoints = av_malloc_array(syncpoint_count, sizeof(int64_t)); + has_keyframe = av_malloc_array(syncpoint_count + 1, sizeof(int8_t)); + if (!syncpoints || !has_keyframe) { + ret = AVERROR(ENOMEM); + goto fail; + } for (i = 0; i < syncpoint_count; i++) { syncpoints[i] = ffio_read_varlen(bc); if (syncpoints[i] <= 0) @@ -605,7 +660,7 @@ static int find_and_decode_index(NUTContext *nut) int flag = x & 1; x >>= 1; if (n + x >= syncpoint_count + 1) { - av_log(s, AV_LOG_ERROR, "index overflow A\n"); + av_log(s, AV_LOG_ERROR, "index overflow A %d + %"PRIu64" >= %d\n", n, x, syncpoint_count + 1); goto fail; } while (x--) @@ -625,7 +680,7 @@ static int find_and_decode_index(NUTContext *nut) av_log(s, AV_LOG_ERROR, "keyframe before first syncpoint in index\n"); goto fail; } - assert(n <= syncpoint_count + 1); + av_assert0(n <= syncpoint_count + 1); for (; j < n && j < syncpoint_count; j++) { if (has_keyframe[j]) { uint64_t B, A = ffio_read_varlen(bc); @@ -712,7 +767,7 @@ static int nut_read_header(AVFormatContext *s) find_and_decode_index(nut); avio_seek(bc, orig_pos, SEEK_SET); } - assert(nut->next_startcode == SYNCPOINT_STARTCODE); + av_assert0(nut->next_startcode == SYNCPOINT_STARTCODE); ff_metadata_conv_ctx(s, NULL, ff_nut_metadata_conv); @@ -757,7 +812,7 @@ static int decode_frame_header(NUTContext *nut, int64_t *pts, int *stream_id, if (coded_pts < (1 << stc->msb_pts_shift)) { *pts = ff_lsb2full(stc, coded_pts); } else - *pts = coded_pts - (1 << stc->msb_pts_shift); + *pts = coded_pts - (1LL << stc->msb_pts_shift); } else *pts = stc->last_pts + pts_delta; if (flags & FLAG_SIZE_MSB) @@ -822,7 +877,8 @@ static int decode_frame(NUTContext *nut, AVPacket *pkt, int frame_code) return 1; } - av_new_packet(pkt, size + nut->header_len[header_idx]); + if (av_new_packet(pkt, size + nut->header_len[header_idx]) < 0) + return AVERROR(ENOMEM); memcpy(pkt->data, nut->header[header_idx], nut->header_len[header_idx]); pkt->pos = avio_tell(bc); // FIXME avio_read(bc, pkt->data + nut->header_len[header_idx], size); @@ -851,7 +907,7 @@ static int nut_read_packet(AVFormatContext *s, AVPacket *pkt) pos -= 8; } else { frame_code = avio_r8(bc); - if (bc->eof_reached) + if (url_feof(bc)) return AVERROR_EOF; if (frame_code == 'N') { tmp = frame_code; @@ -905,21 +961,18 @@ static int64_t nut_read_timestamp(AVFormatContext *s, int stream_index, do { pos = find_startcode(bc, SYNCPOINT_STARTCODE, pos) + 1; if (pos < 1) { - assert(nut->next_startcode == 0); av_log(s, AV_LOG_ERROR, "read_timestamp failed.\n"); return AV_NOPTS_VALUE; } } while (decode_syncpoint(nut, &pts, &back_ptr) < 0); *pos_arg = pos - 1; - assert(nut->last_syncpoint_pos == *pos_arg); + av_assert0(nut->last_syncpoint_pos == *pos_arg); av_log(s, AV_LOG_DEBUG, "return %"PRId64" %"PRId64"\n", pts, back_ptr); - if (stream_index == -1) - return pts; - else if (stream_index == -2) + if (stream_index == -2) return back_ptr; - - return AV_NOPTS_VALUE; + av_assert0(stream_index == -1); + return pts; } static int read_seek(AVFormatContext *s, int stream_index, @@ -936,6 +989,8 @@ static int read_seek(AVFormatContext *s, int stream_index, if (st->index_entries) { int index = av_index_search_timestamp(st, pts, flags); if (index < 0) + index = av_index_search_timestamp(st, pts, flags ^ AVSEEK_FLAG_BACKWARD); + if (index < 0) return -1; pos2 = st->index_entries[index].pos; @@ -968,7 +1023,7 @@ static int read_seek(AVFormatContext *s, int stream_index, sp = av_tree_find(nut->syncpoints, &dummy, (void *) ff_nut_sp_pos_cmp, NULL); - assert(sp); + av_assert0(sp); pos2 = sp->back_ptr - 15; } av_log(NULL, AV_LOG_DEBUG, "SEEKTO: %"PRId64"\n", pos2); @@ -1000,6 +1055,7 @@ static int nut_read_close(AVFormatContext *s) AVInputFormat ff_nut_demuxer = { .name = "nut", .long_name = NULL_IF_CONFIG_SMALL("NUT"), + .flags = AVFMT_SEEK_TO_PTS, .priv_data_size = sizeof(NUTContext), .read_probe = nut_probe, .read_header = nut_read_header, diff --git a/libavformat/nutenc.c b/libavformat/nutenc.c index acce86c584..b6d754d595 100644 --- a/libavformat/nutenc.c +++ b/libavformat/nutenc.c @@ -2,20 +2,20 @@ * nut muxer * Copyright (c) 2004-2007 Michael Niedermayer * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -23,6 +23,7 @@ #include "libavutil/mathematics.h" #include "libavutil/tree.h" #include "libavutil/dict.h" +#include "libavutil/avassert.h" #include "libavcodec/mpegaudiodata.h" #include "nut.h" #include "internal.h" @@ -60,12 +61,9 @@ static int find_expected_header(AVCodecContext *c, int size, int key_frame, lsf = sample_rate < (24000 + 32000) / 2; mpeg25 = sample_rate < (12000 + 16000) / 2; sample_rate <<= lsf + mpeg25; - if (sample_rate < (32000 + 44100) / 2) - sample_rate_index = 2; - else if (sample_rate < (44100 + 48000) / 2) - sample_rate_index = 0; - else - sample_rate_index = 1; + if (sample_rate < (32000 + 44100) / 2) sample_rate_index = 2; + else if (sample_rate < (44100 + 48000) / 2) sample_rate_index = 0; + else sample_rate_index = 1; sample_rate = avpriv_mpa_freq_tab[sample_rate_index] >> (lsf + mpeg25); @@ -99,8 +97,7 @@ static int find_expected_header(AVCodecContext *c, int size, int key_frame, return 0; } -static int find_header_idx(AVFormatContext *s, AVCodecContext *c, int size, - int frame_type) +static int find_header_idx(AVFormatContext *s, AVCodecContext *c, int size, int frame_type) { NUTContext *nut = s->priv_data; uint8_t out[64]; @@ -169,6 +166,19 @@ static void build_frame_code(AVFormatContext *s) int is_audio = codec->codec_type == AVMEDIA_TYPE_AUDIO; int intra_only = /*codec->intra_only || */ is_audio; int pred_count; + int frame_size = 0; + + if (codec->codec_type == AVMEDIA_TYPE_AUDIO) { + frame_size = av_get_audio_frame_duration(codec, 0); + if (codec->codec_id == AV_CODEC_ID_VORBIS && !frame_size) + frame_size = 64; + } else { + AVRational f = av_div_q(codec->time_base, *nut->stream[stream_id].time_base); + if (f.den == 1 && f.num>0) + frame_size = f.num; + } + if (!frame_size) + frame_size = 1; for (key_frame = 0; key_frame < 2; key_frame++) { if (!intra_only || !keyframe_0_esc || key_frame != 0) { @@ -184,29 +194,32 @@ static void build_frame_code(AVFormatContext *s) } key_frame = intra_only; +#if 1 if (is_audio) { int frame_bytes = codec->frame_size * (int64_t)codec->bit_rate / (8 * codec->sample_rate); int pts; - for (pts = 0; pts < 2; pts++) + for (pts = 0; pts < 2; pts++) { for (pred = 0; pred < 2; pred++) { - FrameCode *ft = &nut->frame_code[start2]; + FrameCode *ft = &nut->frame_code[start2]; ft->flags = FLAG_KEY * key_frame; ft->stream_id = stream_id; ft->size_mul = frame_bytes + 2; ft->size_lsb = frame_bytes + pred; - ft->pts_delta = pts; + ft->pts_delta = pts * frame_size; ft->header_idx = find_header_idx(s, codec, frame_bytes + pred, key_frame); start2++; } + } } else { FrameCode *ft = &nut->frame_code[start2]; ft->flags = FLAG_KEY | FLAG_SIZE_MSB; ft->stream_id = stream_id; ft->size_mul = 1; - ft->pts_delta = 1; + ft->pts_delta = frame_size; start2++; } +#endif if (codec->has_b_frames) { pred_count = 5; @@ -229,6 +242,8 @@ static void build_frame_code(AVFormatContext *s) int start3 = start2 + (end2 - start2) * pred / pred_count; int end3 = start2 + (end2 - start2) * (pred + 1) / pred_count; + pred_table[pred] *= frame_size; + for (index = start3; index < end3; index++) { FrameCode *ft = &nut->frame_code[index]; ft->flags = FLAG_KEY * key_frame; @@ -243,15 +258,13 @@ static void build_frame_code(AVFormatContext *s) } } } - memmove(&nut->frame_code['N' + 1], &nut->frame_code['N'], - sizeof(FrameCode) * (255 - 'N')); + memmove(&nut->frame_code['N' + 1], &nut->frame_code['N'], sizeof(FrameCode) * (255 - 'N')); nut->frame_code[0].flags = nut->frame_code[255].flags = nut->frame_code['N'].flags = FLAG_INVALID; } -static void put_tt(NUTContext *nut, AVRational *time_base, AVIOContext *bc, - uint64_t val) +static void put_tt(NUTContext *nut, AVRational *time_base, AVIOContext *bc, uint64_t val) { val *= nut->time_base_count; val += time_base - nut->time_base; @@ -282,8 +295,7 @@ static inline void ff_put_v_trace(AVIOContext *bc, uint64_t v, const char *file, ff_put_v(bc, v); } -static inline void put_s_trace(AVIOContext *bc, int64_t v, const char *file, - const char *func, int line) +static inline void put_s_trace(AVIOContext *bc, int64_t v, const char *file, const char *func, int line) { av_log(NULL, AV_LOG_DEBUG, "put_s %5"PRId64" / %"PRIX64" in %s %s:%d\n", v, v, file, func, line); @@ -342,17 +354,12 @@ static void write_mainheader(NUTContext *nut, AVIOContext *bc) tmp_fields = 0; tmp_size = 0; // tmp_res=0; - if (tmp_pts != nut->frame_code[i].pts_delta) - tmp_fields = 1; - if (tmp_mul != nut->frame_code[i].size_mul) - tmp_fields = 2; - if (tmp_stream != nut->frame_code[i].stream_id) - tmp_fields = 3; - if (tmp_size != nut->frame_code[i].size_lsb) - tmp_fields = 4; -// if(tmp_res != nut->frame_code[i].res ) tmp_fields=5; - if (tmp_head_idx != nut->frame_code[i].header_idx) - tmp_fields = 8; + if (tmp_pts != nut->frame_code[i].pts_delta ) tmp_fields = 1; + if (tmp_mul != nut->frame_code[i].size_mul ) tmp_fields = 2; + if (tmp_stream != nut->frame_code[i].stream_id ) tmp_fields = 3; + if (tmp_size != nut->frame_code[i].size_lsb ) tmp_fields = 4; +// if (tmp_res != nut->frame_code[i].res ) tmp_fields=5; + if (tmp_head_idx != nut->frame_code[i].header_idx) tmp_fields = 8; tmp_pts = nut->frame_code[i].pts_delta; tmp_flags = nut->frame_code[i].flags; @@ -381,22 +388,14 @@ static void write_mainheader(NUTContext *nut, AVIOContext *bc) ff_put_v(bc, tmp_flags); ff_put_v(bc, tmp_fields); - if (tmp_fields > 0) - put_s(bc, tmp_pts); - if (tmp_fields > 1) - ff_put_v(bc, tmp_mul); - if (tmp_fields > 2) - ff_put_v(bc, tmp_stream); - if (tmp_fields > 3) - ff_put_v(bc, tmp_size); - if (tmp_fields > 4) - ff_put_v(bc, 0 /*tmp_res*/); - if (tmp_fields > 5) - ff_put_v(bc, j); - if (tmp_fields > 6) - ff_put_v(bc, tmp_match); - if (tmp_fields > 7) - ff_put_v(bc, tmp_head_idx); + if (tmp_fields > 0) put_s(bc, tmp_pts); + if (tmp_fields > 1) ff_put_v(bc, tmp_mul); + if (tmp_fields > 2) ff_put_v(bc, tmp_stream); + if (tmp_fields > 3) ff_put_v(bc, tmp_size); + if (tmp_fields > 4) ff_put_v(bc, 0 /*tmp_res*/); + if (tmp_fields > 5) ff_put_v(bc, j); + if (tmp_fields > 6) ff_put_v(bc, tmp_match); + if (tmp_fields > 7) ff_put_v(bc, tmp_head_idx); } ff_put_v(bc, nut->header_count - 1); for (i = 1; i < nut->header_count; i++) { @@ -410,30 +409,17 @@ static int write_streamheader(AVFormatContext *avctx, AVIOContext *bc, { NUTContext *nut = avctx->priv_data; AVCodecContext *codec = st->codec; - unsigned codec_tag = av_codec_get_tag(ff_nut_codec_tags, codec->codec_id); ff_put_v(bc, i); switch (codec->codec_type) { - case AVMEDIA_TYPE_VIDEO: - ff_put_v(bc, 0); - break; - case AVMEDIA_TYPE_AUDIO: - ff_put_v(bc, 1); - break; - case AVMEDIA_TYPE_SUBTITLE: - ff_put_v(bc, 2); - break; - default: - ff_put_v(bc, 3); - break; + case AVMEDIA_TYPE_VIDEO: ff_put_v(bc, 0); break; + case AVMEDIA_TYPE_AUDIO: ff_put_v(bc, 1); break; + case AVMEDIA_TYPE_SUBTITLE: ff_put_v(bc, 2); break; + default: ff_put_v(bc, 3); break; } ff_put_v(bc, 4); - - if (!codec_tag || codec->codec_id == AV_CODEC_ID_RAWVIDEO) - codec_tag = codec->codec_tag; - - if (codec_tag) { - avio_wl32(bc, codec_tag); + if (codec->codec_tag) { + avio_wl32(bc, codec->codec_tag); } else { av_log(avctx, AV_LOG_ERROR, "No codec tag defined for stream %d\n", i); return AVERROR(EINVAL); @@ -509,20 +495,28 @@ static int write_globalinfo(NUTContext *nut, AVIOContext *bc) return 0; } -static int write_streaminfo(NUTContext *nut, AVIOContext *bc, int stream_id){ +static int write_streaminfo(NUTContext *nut, AVIOContext *bc, int stream_id) { AVFormatContext *s= nut->avf; AVStream* st = s->streams[stream_id]; + AVDictionaryEntry *t = NULL; AVIOContext *dyn_bc; uint8_t *dyn_buf=NULL; int count=0, dyn_size, i; int ret = avio_open_dyn_buf(&dyn_bc); - if(ret < 0) + if (ret < 0) return ret; + while ((t = av_dict_get(st->metadata, "", t, AV_DICT_IGNORE_SUFFIX))) + count += add_info(dyn_bc, t->key, t->value); for (i=0; ff_nut_dispositions[i].flag; ++i) { if (st->disposition & ff_nut_dispositions[i].flag) count += add_info(dyn_bc, "Disposition", ff_nut_dispositions[i].str); } + if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) { + uint8_t buf[256]; + snprintf(buf, sizeof(buf), "%d/%d", st->codec->time_base.den, st->codec->time_base.num); + count += add_info(dyn_bc, "r_frame_rate", buf); + } dyn_size = avio_close_dyn_buf(dyn_bc, &dyn_buf); if (count) { @@ -568,6 +562,51 @@ static int write_chapter(NUTContext *nut, AVIOContext *bc, int id) return 0; } +static int write_index(NUTContext *nut, AVIOContext *bc) { + int i; + Syncpoint dummy= { .pos= 0 }; + Syncpoint *next_node[2] = { NULL }; + int64_t startpos = avio_tell(bc); + int64_t payload_size; + + put_tt(nut, nut->max_pts_tb, bc, nut->max_pts); + + ff_put_v(bc, nut->sp_count); + + for (i=0; i<nut->sp_count; i++) { + av_tree_find(nut->syncpoints, &dummy, (void *) ff_nut_sp_pos_cmp, (void**)next_node); + ff_put_v(bc, (next_node[1]->pos >> 4) - (dummy.pos>>4)); + dummy.pos = next_node[1]->pos; + } + + for (i=0; i<nut->avf->nb_streams; i++) { + StreamContext *nus= &nut->stream[i]; + int64_t last_pts= -1; + int j, k; + for (j=0; j<nut->sp_count; j++) { + int flag = (nus->keyframe_pts[j] != AV_NOPTS_VALUE) ^ (j+1 == nut->sp_count); + int n = 0; + for (; j<nut->sp_count && (nus->keyframe_pts[j] != AV_NOPTS_VALUE) == flag; j++) + n++; + + ff_put_v(bc, 1 + 2*flag + 4*n); + for (k= j - n; k<=j && k<nut->sp_count; k++) { + if (nus->keyframe_pts[k] == AV_NOPTS_VALUE) + continue; + av_assert0(nus->keyframe_pts[k] > last_pts); + ff_put_v(bc, nus->keyframe_pts[k] - last_pts); + last_pts = nus->keyframe_pts[k]; + } + } + } + + payload_size = avio_tell(bc) - startpos + 8 + 4; + + avio_wb64(bc, 8 + payload_size + av_log2(payload_size) / 7 + 1 + 4*(payload_size > 4096)); + + return 0; +} + static int write_headers(AVFormatContext *avctx, AVIOContext *bc) { NUTContext *nut = avctx->priv_data; @@ -641,12 +680,11 @@ static int nut_write_header(AVFormatContext *s) nut->avf = s; - nut->stream = av_mallocz(sizeof(StreamContext) * s->nb_streams); - if (s->nb_chapters) - nut->chapter = av_mallocz(sizeof(ChapterContext) * s->nb_chapters); - nut->time_base = av_mallocz(sizeof(AVRational) * (s->nb_streams + - s->nb_chapters)); - if (!nut->stream || (s->nb_chapters && !nut->chapter) || !nut->time_base) { + nut->stream = av_calloc(s->nb_streams, sizeof(*nut->stream )); + nut->chapter = av_calloc(s->nb_chapters, sizeof(*nut->chapter)); + nut->time_base= av_calloc(s->nb_streams + + s->nb_chapters, sizeof(*nut->time_base)); + if (!nut->stream || !nut->chapter || !nut->time_base) { av_freep(&nut->stream); av_freep(&nut->chapter); av_freep(&nut->time_base); @@ -657,8 +695,13 @@ static int nut_write_header(AVFormatContext *s) AVStream *st = s->streams[i]; int ssize; AVRational time_base; - ff_parse_specific_params(st->codec, &time_base.den, &ssize, - &time_base.num); + ff_parse_specific_params(st->codec, &time_base.den, &ssize, &time_base.num); + + if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO && st->codec->sample_rate) { + time_base = (AVRational) {1, st->codec->sample_rate}; + } else { + time_base = ff_choose_timebase(s, st, 48000); + } avpriv_set_pts_info(st, 64, time_base.num, time_base.den); @@ -695,7 +738,7 @@ static int nut_write_header(AVFormatContext *s) nut->max_distance = MAX_DISTANCE; build_elision_headers(s); build_frame_code(s); - assert(nut->frame_code['N'].flags == FLAG_INVALID); + av_assert0(nut->frame_code['N'].flags == FLAG_INVALID); avio_write(bc, ID_STRING, strlen(ID_STRING)); avio_w8(bc, 0); @@ -703,9 +746,10 @@ static int nut_write_header(AVFormatContext *s) if ((ret = write_headers(s, bc)) < 0) return ret; - avio_flush(bc); + if (s->avoid_negative_ts < 0) + s->avoid_negative_ts = 1; - //FIXME index + avio_flush(bc); return 0; } @@ -762,8 +806,8 @@ static int nut_write_packet(AVFormatContext *s, AVPacket *pkt) AVIOContext *bc = s->pb, *dyn_bc; FrameCode *fc; int64_t coded_pts; - int best_length, frame_code, flags, needed_flags, i, header_idx, - best_header_idx; + int best_length, frame_code, flags, needed_flags, i, header_idx; + int best_header_idx; int key_frame = !!(pkt->flags & AV_PKT_FLAG_KEY); int store_sp = 0; int ret; @@ -772,7 +816,7 @@ static int nut_write_packet(AVFormatContext *s, AVPacket *pkt) av_log(s, AV_LOG_ERROR, "Negative pts not supported stream %d, pts %"PRId64"\n", pkt->stream_index, pkt->pts); - return AVERROR_INVALIDDATA; + return AVERROR(EINVAL); } if (1LL << (20 + 3 * nut->header_count) <= avio_tell(bc)) @@ -781,8 +825,7 @@ static int nut_write_packet(AVFormatContext *s, AVPacket *pkt) if (key_frame && !(nus->last_flags & FLAG_KEY)) store_sp = 1; - if (pkt->size + 30 /*FIXME check*/ + avio_tell(bc) >= - nut->last_syncpoint_pos + nut->max_distance) + if (pkt->size + 30 /*FIXME check*/ + avio_tell(bc) >= nut->last_syncpoint_pos + nut->max_distance) store_sp = 1; //FIXME: Ensure store_sp is 1 in the first place. @@ -817,8 +860,19 @@ static int nut_write_packet(AVFormatContext *s, AVPacket *pkt) if ((ret = ff_nut_add_sp(nut, nut->last_syncpoint_pos, 0 /*unused*/, pkt->dts)) < 0) return ret; + + if ((1ll<<60) % nut->sp_count == 0) + for (i=0; i<s->nb_streams; i++) { + int j; + StreamContext *nus = &nut->stream[i]; + av_reallocp_array(&nus->keyframe_pts, 2*nut->sp_count, sizeof(*nus->keyframe_pts)); + if (!nus->keyframe_pts) + return AVERROR(ENOMEM); + for (j=nut->sp_count == 1 ? 0 : nut->sp_count; j<2*nut->sp_count; j++) + nus->keyframe_pts[j] = AV_NOPTS_VALUE; + } } - assert(nus->last_pts != AV_NOPTS_VALUE); + av_assert0(nus->last_pts != AV_NOPTS_VALUE); coded_pts = pkt->pts & ((1 << nus->msb_pts_shift) - 1); if (ff_lsb2full(nus, coded_pts) != pkt->pts) @@ -862,9 +916,8 @@ static int nut_write_packet(AVFormatContext *s, AVPacket *pkt) if (flags & FLAG_CODED_PTS) length += ff_get_v_length(coded_pts); - if ((flags & FLAG_CODED) - && nut->header_len[best_header_idx] > - nut->header_len[fc->header_idx] + 1) { + if ( (flags & FLAG_CODED) + && nut->header_len[best_header_idx] > nut->header_len[fc->header_idx] + 1) { flags |= FLAG_HEADER_IDX; } @@ -883,7 +936,7 @@ static int nut_write_packet(AVFormatContext *s, AVPacket *pkt) frame_code = i; } } - assert(frame_code != -1); + av_assert0(frame_code != -1); fc = &nut->frame_code[frame_code]; flags = fc->flags; needed_flags = get_needed_flags(nut, nus, fc, pkt); @@ -895,27 +948,20 @@ static int nut_write_packet(AVFormatContext *s, AVPacket *pkt) ff_put_v(bc, (flags ^ needed_flags) & ~(FLAG_CODED)); flags = needed_flags; } - if (flags & FLAG_STREAM_ID) - ff_put_v(bc, pkt->stream_index); - if (flags & FLAG_CODED_PTS) - ff_put_v(bc, coded_pts); - if (flags & FLAG_SIZE_MSB) - ff_put_v(bc, pkt->size / fc->size_mul); - if (flags & FLAG_HEADER_IDX) - ff_put_v(bc, header_idx = best_header_idx); - - if (flags & FLAG_CHECKSUM) - avio_wl32(bc, ffio_get_checksum(bc)); - else - ffio_get_checksum(bc); + if (flags & FLAG_STREAM_ID) ff_put_v(bc, pkt->stream_index); + if (flags & FLAG_CODED_PTS) ff_put_v(bc, coded_pts); + if (flags & FLAG_SIZE_MSB ) ff_put_v(bc, pkt->size / fc->size_mul); + if (flags & FLAG_HEADER_IDX) ff_put_v(bc, header_idx = best_header_idx); + + if (flags & FLAG_CHECKSUM) avio_wl32(bc, ffio_get_checksum(bc)); + else ffio_get_checksum(bc); - avio_write(bc, pkt->data + nut->header_len[header_idx], - pkt->size - nut->header_len[header_idx]); + avio_write(bc, pkt->data + nut->header_len[header_idx], pkt->size - nut->header_len[header_idx]); nus->last_flags = flags; nus->last_pts = pkt->pts; //FIXME just store one per syncpoint - if (flags & FLAG_KEY) + if (flags & FLAG_KEY) { av_add_index_entry( s->streams[pkt->stream_index], nut->last_syncpoint_pos, @@ -923,6 +969,14 @@ static int nut_write_packet(AVFormatContext *s, AVPacket *pkt) 0, 0, AVINDEX_KEYFRAME); + if (nus->keyframe_pts && nus->keyframe_pts[nut->sp_count] == AV_NOPTS_VALUE) + nus->keyframe_pts[nut->sp_count] = pkt->pts; + } + + if (!nut->max_pts_tb || av_compare_ts(nut->max_pts, *nut->max_pts_tb, pkt->pts, *nus->time_base) < 0) { + nut->max_pts = pkt->pts; + nut->max_pts_tb = nus->time_base; + } return 0; } @@ -930,12 +984,22 @@ static int nut_write_packet(AVFormatContext *s, AVPacket *pkt) static int nut_write_trailer(AVFormatContext *s) { NUTContext *nut = s->priv_data; - AVIOContext *bc = s->pb; + AVIOContext *bc = s->pb, *dyn_bc; + int i, ret; while (nut->header_count < 3) write_headers(s, bc); + ret = avio_open_dyn_buf(&dyn_bc); + if (ret >= 0 && nut->sp_count) { + write_index(nut, dyn_bc); + put_packet(nut, bc, dyn_bc, 1, INDEX_STARTCODE); + } + ff_nut_free_sp(nut); + for (i=0; i<s->nb_streams; i++) + av_freep(&nut->stream[i].keyframe_pts); + av_freep(&nut->stream); av_freep(&nut->chapter); av_freep(&nut->time_base); diff --git a/libavformat/nuv.c b/libavformat/nuv.c index 9336912fe0..32d0e02f68 100644 --- a/libavformat/nuv.c +++ b/libavformat/nuv.c @@ -2,20 +2,20 @@ * NuppelVideo demuxer. * Copyright (c) 2006 Reimar Doeffinger * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -72,7 +72,7 @@ static int get_codec_data(AVIOContext *pb, AVStream *vst, if (!vst && !myth) return 1; // no codec data needed - while (!pb->eof_reached) { + while (!url_feof(pb)) { int size, subtype; frametype = avio_r8(pb); @@ -86,10 +86,8 @@ static int get_codec_data(AVIOContext *pb, AVStream *vst, av_freep(&vst->codec->extradata); vst->codec->extradata_size = 0; } - vst->codec->extradata = av_malloc(size); - if (!vst->codec->extradata) + if (ff_alloc_extradata(vst->codec, size)) return AVERROR(ENOMEM); - vst->codec->extradata_size = size; avio_read(pb, vst->codec->extradata, size); size = 0; if (!myth) @@ -195,6 +193,9 @@ static int nuv_header(AVFormatContext *s) vst->codec->bits_per_coded_sample = 10; vst->sample_aspect_ratio = av_d2q(aspect * height / width, 10000); +#if FF_API_R_FRAME_RATE + vst->r_frame_rate = +#endif vst->avg_frame_rate = av_d2q(fps, 60000); avpriv_set_pts_info(vst, 32, 1, 1000); } else @@ -236,7 +237,7 @@ static int nuv_packet(AVFormatContext *s, AVPacket *pkt) nuv_frametype frametype; int ret, size; - while (!pb->eof_reached) { + while (!url_feof(pb)) { int copyhdrsize = ctx->rtjpg_video ? HDRSIZE : 0; uint64_t pos = avio_tell(pb); @@ -262,10 +263,9 @@ static int nuv_packet(AVFormatContext *s, AVPacket *pkt) ret = av_new_packet(pkt, copyhdrsize + size); if (ret < 0) return ret; - // HACK: we have no idea if it is a keyframe, - // but if we mark none seeking will not work at all. - pkt->flags |= AV_PKT_FLAG_KEY; + pkt->pos = pos; + pkt->flags |= hdr[2] == 0 ? AV_PKT_FLAG_KEY : 0; pkt->pts = AV_RL32(&hdr[4]); pkt->stream_index = ctx->v_id; memcpy(pkt->data, hdr, copyhdrsize); @@ -303,6 +303,81 @@ static int nuv_packet(AVFormatContext *s, AVPacket *pkt) return AVERROR(EIO); } +/** + * \brief looks for the string RTjjjjjjjjjj in the stream too resync reading + * \return 1 if the syncword is found 0 otherwise. + */ +static int nuv_resync(AVFormatContext *s, int64_t pos_limit) { + AVIOContext *pb = s->pb; + uint32_t tag = 0; + while(!url_feof(pb) && avio_tell(pb) < pos_limit) { + tag = (tag << 8) | avio_r8(pb); + if (tag == MKBETAG('R','T','j','j') && + (tag = avio_rb32(pb)) == MKBETAG('j','j','j','j') && + (tag = avio_rb32(pb)) == MKBETAG('j','j','j','j')) + return 1; + } + return 0; +} + +/** + * \brief attempts to read a timestamp from stream at the given stream position + * \return timestamp if successful and AV_NOPTS_VALUE if failure + */ +static int64_t nuv_read_dts(AVFormatContext *s, int stream_index, + int64_t *ppos, int64_t pos_limit) +{ + NUVContext *ctx = s->priv_data; + AVIOContext *pb = s->pb; + uint8_t hdr[HDRSIZE]; + nuv_frametype frametype; + int size, key, idx; + int64_t pos, dts; + + if (avio_seek(pb, *ppos, SEEK_SET) < 0) + return AV_NOPTS_VALUE; + + if (!nuv_resync(s, pos_limit)) + return AV_NOPTS_VALUE; + + while (!url_feof(pb) && avio_tell(pb) < pos_limit) { + if (avio_read(pb, hdr, HDRSIZE) < HDRSIZE) + return AV_NOPTS_VALUE; + frametype = hdr[0]; + size = PKTSIZE(AV_RL32(&hdr[8])); + switch (frametype) { + case NUV_SEEKP: + break; + case NUV_AUDIO: + case NUV_VIDEO: + if (frametype == NUV_VIDEO) { + idx = ctx->v_id; + key = hdr[2] == 0; + } else { + idx = ctx->a_id; + key = 1; + } + if (stream_index == idx) { + + pos = avio_tell(s->pb) - HDRSIZE; + dts = AV_RL32(&hdr[4]); + + // TODO - add general support in av_gen_search, so it adds positions after reading timestamps + av_add_index_entry(s->streams[stream_index], pos, dts, size + HDRSIZE, 0, + key ? AVINDEX_KEYFRAME : 0); + + *ppos = pos; + return dts; + } + default: + avio_skip(pb, size); + break; + } + } + return AV_NOPTS_VALUE; +} + + AVInputFormat ff_nuv_demuxer = { .name = "nuv", .long_name = NULL_IF_CONFIG_SMALL("NuppelVideo"), @@ -310,5 +385,6 @@ AVInputFormat ff_nuv_demuxer = { .read_probe = nuv_probe, .read_header = nuv_header, .read_packet = nuv_packet, + .read_timestamp = nuv_read_dts, .flags = AVFMT_GENERIC_INDEX, }; diff --git a/libavformat/oggdec.c b/libavformat/oggdec.c index ae9da3aba1..a099eb382e 100644 --- a/libavformat/oggdec.c +++ b/libavformat/oggdec.c @@ -28,8 +28,9 @@ DEALINGS IN THE SOFTWARE. */ - #include <stdio.h> +#include "libavutil/avassert.h" +#include "libavutil/intreadwrite.h" #include "oggdec.h" #include "avformat.h" #include "internal.h" @@ -56,6 +57,9 @@ static const struct ogg_codec * const ogg_codecs[] = { NULL }; +static int64_t ogg_calc_pts(AVFormatContext *s, int idx, int64_t *dts); +static int ogg_new_stream(AVFormatContext *s, uint32_t serial); + //FIXME We could avoid some structure duplication static int ogg_save(AVFormatContext *s) { @@ -95,9 +99,10 @@ static int ogg_restore(AVFormatContext *s, int discard) if (!discard) { for (i = 0; i < ogg->nstreams; i++) - av_free(ogg->streams[i].buf); + av_freep(&ogg->streams[i].buf); avio_seek(bc, ost->pos, SEEK_SET); + ogg->page_pos = -1; ogg->curidx = ost->curidx; ogg->nstreams = ost->nstreams; if ((err = av_reallocp_array(&ogg->streams, ogg->nstreams, @@ -114,9 +119,11 @@ static int ogg_restore(AVFormatContext *s, int discard) return 0; } -static int ogg_reset(struct ogg *ogg) +static int ogg_reset(AVFormatContext *s) { + struct ogg *ogg = s->priv_data; int i; + int64_t start_pos = avio_tell(s->pb); for (i = 0; i < ogg->nstreams; i++) { struct ogg_stream *os = ogg->streams + i; @@ -131,8 +138,13 @@ static int ogg_reset(struct ogg *ogg) os->nsegs = 0; os->segp = 0; os->incomplete = 0; + os->got_data = 0; + if (start_pos <= s->data_offset) { + os->lastpts = 0; + } } + ogg->page_pos = -1; ogg->curidx = -1; return 0; @@ -150,38 +162,105 @@ static const struct ogg_codec *ogg_find_codec(uint8_t *buf, int size) return NULL; } -static int ogg_new_stream(AVFormatContext *s, uint32_t serial, int new_avstream) +/** + * Replace the current stream with a new one. This is a typical webradio + * situation where a new audio stream spawn (identified with a new serial) and + * must replace the previous one (track switch). + */ +static int ogg_replace_stream(AVFormatContext *s, uint32_t serial, int nsegs) { struct ogg *ogg = s->priv_data; - int idx = ogg->nstreams++; - AVStream *st; struct ogg_stream *os; + const struct ogg_codec *codec; + int i = 0; + + if (s->pb->seekable) { + uint8_t magic[8]; + int64_t pos = avio_tell(s->pb); + avio_skip(s->pb, nsegs); + avio_read(s->pb, magic, sizeof(magic)); + avio_seek(s->pb, pos, SEEK_SET); + codec = ogg_find_codec(magic, sizeof(magic)); + if (!codec) { + av_log(s, AV_LOG_ERROR, "Cannot identify new stream\n"); + return AVERROR_INVALIDDATA; + } + for (i = 0; i < ogg->nstreams; i++) { + if (ogg->streams[i].codec == codec) + break; + } + if (i >= ogg->nstreams) + return ogg_new_stream(s, serial); + } else if (ogg->nstreams != 1) { + avpriv_report_missing_feature(s, "Changing stream parameters in multistream ogg"); + return AVERROR_PATCHWELCOME; + } - os = av_realloc(ogg->streams, ogg->nstreams * sizeof(*ogg->streams)); + os = &ogg->streams[i]; - if (!os) - return AVERROR(ENOMEM); + os->serial = serial; + return i; - ogg->streams = os; +#if 0 + buf = os->buf; + bufsize = os->bufsize; + codec = os->codec; + + if (!ogg->state || ogg->state->streams[i].private != os->private) + av_freep(&ogg->streams[i].private); + + /* Set Ogg stream settings similar to what is done in ogg_new_stream(). We + * also re-use the ogg_stream allocated buffer */ + memset(os, 0, sizeof(*os)); + os->serial = serial; + os->bufsize = bufsize; + os->buf = buf; + os->header = -1; + os->codec = codec; + + return i; +#endif +} + +static int ogg_new_stream(AVFormatContext *s, uint32_t serial) +{ + struct ogg *ogg = s->priv_data; + int idx = ogg->nstreams; + AVStream *st; + struct ogg_stream *os; + size_t size; - memset(ogg->streams + idx, 0, sizeof(*ogg->streams)); + if (ogg->state) { + av_log(s, AV_LOG_ERROR, "New streams are not supposed to be added " + "in between Ogg context save/restore operations.\n"); + return AVERROR_BUG; + } - os = ogg->streams + idx; + /* Allocate and init a new Ogg Stream */ + if (av_size_mult(ogg->nstreams + 1, sizeof(*ogg->streams), &size) < 0 || + !(os = av_realloc(ogg->streams, size))) + return AVERROR(ENOMEM); + ogg->streams = os; + os = ogg->streams + idx; + memset(os, 0, sizeof(*os)); os->serial = serial; os->bufsize = DECODER_BUFFER_SIZE; os->buf = av_malloc(os->bufsize + FF_INPUT_BUFFER_PADDING_SIZE); os->header = -1; os->start_granule = OGG_NOGRANULE_VALUE; + if (!os->buf) + return AVERROR(ENOMEM); - if (new_avstream) { - st = avformat_new_stream(s, NULL); - if (!st) - return AVERROR(ENOMEM); - - st->id = idx; - avpriv_set_pts_info(st, 64, 1, 1000000); + /* Create the associated AVStream */ + st = avformat_new_stream(s, NULL); + if (!st) { + av_freep(&os->buf); + return AVERROR(ENOMEM); } + st->id = idx; + avpriv_set_pts_info(st, 64, 1, 1000000); + ogg->nstreams++; return idx; } @@ -203,7 +282,17 @@ static int ogg_new_buf(struct ogg *ogg, int idx) return 0; } -static int ogg_read_page(AVFormatContext *s, int *str) +static int data_packets_seen(const struct ogg *ogg) +{ + int i; + + for (i = 0; i < ogg->nstreams; i++) + if (ogg->streams[i].got_data) + return 1; + return 0; +} + +static int ogg_read_page(AVFormatContext *s, int *sid) { AVIOContext *bc = s->pb; struct ogg *ogg = s->priv_data; @@ -228,9 +317,15 @@ static int ogg_read_page(AVFormatContext *s, int *str) sync[(sp + 2) & 3] == 'g' && sync[(sp + 3) & 3] == 'S') break; + if(!i && bc->seekable && ogg->page_pos > 0) { + memset(sync, 0, 4); + avio_seek(bc, ogg->page_pos+4, SEEK_SET); + ogg->page_pos = -1; + } + c = avio_r8(bc); - if (bc->eof_reached) + if (url_feof(bc)) return AVERROR_EOF; sync[sp++ & 3] = c; @@ -241,8 +336,10 @@ static int ogg_read_page(AVFormatContext *s, int *str) return AVERROR_INVALIDDATA; } - if (avio_r8(bc) != 0) /* version */ + if (avio_r8(bc) != 0) { /* version */ + av_log (s, AV_LOG_ERROR, "ogg page, unsupported version\n"); return AVERROR_INVALIDDATA; + } flags = avio_r8(bc); gp = avio_rl64(bc); @@ -252,28 +349,19 @@ static int ogg_read_page(AVFormatContext *s, int *str) idx = ogg_find_stream(ogg, serial); if (idx < 0) { - if (ogg->headers) { - int n; - - for (n = 0; n < ogg->nstreams; n++) { - av_freep(&ogg->streams[n].buf); - if (!ogg->state || - ogg->state->streams[n].private != ogg->streams[n].private) - av_freep(&ogg->streams[n].private); - } - - ogg->curidx = -1; - ogg->nstreams = 0; + if (data_packets_seen(ogg)) + idx = ogg_replace_stream(s, serial, nsegs); + else + idx = ogg_new_stream(s, serial); - idx = ogg_new_stream(s, serial, 0); - } else { - idx = ogg_new_stream(s, serial, 1); - } - if (idx < 0) + if (idx < 0) { + av_log(s, AV_LOG_ERROR, "failed to create or replace stream\n"); return idx; + } } os = ogg->streams + idx; + ogg->page_pos = os->page_pos = avio_tell(bc) - 27; if (os->psize > 0) @@ -290,8 +378,14 @@ static int ogg_read_page(AVFormatContext *s, int *str) for (i = 0; i < nsegs; i++) size += os->segments[i]; + if (!(flags & OGG_FLAG_BOS)) + os->got_data = 1; + if (flags & OGG_FLAG_CONT || os->incomplete) { if (!os->psize) { + // If this is the very first segment we started + // playback in the middle of a continuation packet. + // Discard it since we missed the start of it. while (os->segp < os->nsegs) { int seg = os->segments[os->segp++]; os->pstart += seg; @@ -323,13 +417,20 @@ static int ogg_read_page(AVFormatContext *s, int *str) os->flags = flags; memset(os->buf + os->bufpos, 0, FF_INPUT_BUFFER_PADDING_SIZE); - if (str) - *str = idx; + if (sid) + *sid = idx; return 0; } -static int ogg_packet(AVFormatContext *s, int *str, int *dstart, int *dsize, +/** + * @brief find the next Ogg packet + * @param *sid is set to the stream for the packet or -1 if there is + * no matching stream, in that case assume all other return + * values to be uninitialized. + * @return negative value on error or EOF. + */ +static int ogg_packet(AVFormatContext *s, int *sid, int *dstart, int *dsize, int64_t *fpos) { struct ogg *ogg = s->priv_data; @@ -339,6 +440,8 @@ static int ogg_packet(AVFormatContext *s, int *str, int *dstart, int *dsize, int segp = 0, psize = 0; av_dlog(s, "ogg_packet: curidx=%i\n", ogg->curidx); + if (sid) + *sid = -1; do { idx = ogg->curidx; @@ -389,8 +492,6 @@ static int ogg_packet(AVFormatContext *s, int *str, int *dstart, int *dsize, } } while (!complete); - av_dlog(s, "ogg_packet: idx %i, frame size %i, start %i\n", - idx, os->psize, os->pstart); if (os->granule == -1) av_log(s, AV_LOG_WARNING, @@ -434,8 +535,8 @@ static int ogg_packet(AVFormatContext *s, int *str, int *dstart, int *dsize, os->pduration = 0; if (os->codec && os->codec->packet) os->codec->packet(s, idx); - if (str) - *str = idx; + if (sid) + *sid = idx; if (dstart) *dstart = os->pstart; if (dsize) @@ -444,6 +545,8 @@ static int ogg_packet(AVFormatContext *s, int *str, int *dstart, int *dsize, *fpos = os->sync_pos; os->pstart += os->psize; os->psize = 0; + if(os->pstart == os->bufpos) + os->bufpos = os->pstart = 0; os->sync_pos = os->page_pos; } @@ -462,43 +565,12 @@ static int ogg_packet(AVFormatContext *s, int *str, int *dstart, int *dsize, return 0; } -static int ogg_get_headers(AVFormatContext *s) -{ - struct ogg *ogg = s->priv_data; - int ret, i; - - do { - ret = ogg_packet(s, NULL, NULL, NULL, NULL); - if (ret < 0) - return ret; - } while (!ogg->headers); - - for (i = 0; i < ogg->nstreams; i++) { - struct ogg_stream *os = ogg->streams + i; - - if (os->codec && os->codec->nb_header && - os->nb_header < os->codec->nb_header) { - av_log(s, AV_LOG_ERROR, - "Headers mismatch for stream %d: " - "expected %d received %d.\n", - i, os->codec->nb_header, os->nb_header); - if (s->error_recognition & AV_EF_EXPLODE) - return AVERROR_INVALIDDATA; - } - if (os->start_granule != OGG_NOGRANULE_VALUE) - os->lastpts = s->streams[i]->start_time = - ogg_gptopts(s, i, os->start_granule, NULL); - } - av_dlog(s, "found headers\n"); - - return 0; -} - static int ogg_get_length(AVFormatContext *s) { struct ogg *ogg = s->priv_data; int i; int64_t size, end; + int streams_left=0; if (!s->pb->seekable) return 0; @@ -514,19 +586,44 @@ static int ogg_get_length(AVFormatContext *s) ogg_save(s); avio_seek(s->pb, end, SEEK_SET); + ogg->page_pos = -1; while (!ogg_read_page(s, &i)) { if (ogg->streams[i].granule != -1 && ogg->streams[i].granule != 0 && ogg->streams[i].codec) { s->streams[i]->duration = ogg_gptopts(s, i, ogg->streams[i].granule, NULL); - if (s->streams[i]->start_time != AV_NOPTS_VALUE) + if (s->streams[i]->start_time != AV_NOPTS_VALUE) { s->streams[i]->duration -= s->streams[i]->start_time; + streams_left-= (ogg->streams[i].got_start==-1); + ogg->streams[i].got_start= 1; + } else if(!ogg->streams[i].got_start) { + ogg->streams[i].got_start= -1; + streams_left++; + } } } ogg_restore(s, 0); + ogg_save (s); + avio_seek (s->pb, s->data_offset, SEEK_SET); + ogg_reset(s); + while (streams_left > 0 && !ogg_packet(s, &i, NULL, NULL, NULL)) { + int64_t pts; + if (i < 0) continue; + pts = ogg_calc_pts(s, i, NULL); + if (pts != AV_NOPTS_VALUE && s->streams[i]->start_time == AV_NOPTS_VALUE && !ogg->streams[i].got_start) { + s->streams[i]->duration -= pts; + ogg->streams[i].got_start= 1; + streams_left--; + }else if(s->streams[i]->start_time != AV_NOPTS_VALUE && !ogg->streams[i].got_start) { + ogg->streams[i].got_start= 1; + streams_left--; + } + } + ogg_restore (s, 0); + return 0; } @@ -536,14 +633,14 @@ static int ogg_read_close(AVFormatContext *s) int i; for (i = 0; i < ogg->nstreams; i++) { - av_free(ogg->streams[i].buf); + av_freep(&ogg->streams[i].buf); if (ogg->streams[i].codec && ogg->streams[i].codec->cleanup) { ogg->streams[i].codec->cleanup(s, i); } - av_free(ogg->streams[i].private); + av_freep(&ogg->streams[i].private); } - av_free(ogg->streams); + av_freep(&ogg->streams); return 0; } @@ -551,22 +648,41 @@ static int ogg_read_header(AVFormatContext *s) { struct ogg *ogg = s->priv_data; int ret, i; + ogg->curidx = -1; + //linear headers seek from start - ret = ogg_get_headers(s); - if (ret < 0) { - ogg_read_close(s); - return ret; - } + do { + ret = ogg_packet(s, NULL, NULL, NULL, NULL); + if (ret < 0) { + ogg_read_close(s); + return ret; + } + } while (!ogg->headers); + av_dlog(s, "found headers\n"); - for (i = 0; i < ogg->nstreams; i++) - if (ogg->streams[i].header < 0) + for (i = 0; i < ogg->nstreams; i++) { + struct ogg_stream *os = ogg->streams + i; + + if (ogg->streams[i].header < 0) { + av_log(s, AV_LOG_ERROR, "Header parsing failed for stream %d\n", i); ogg->streams[i].codec = NULL; + } else if (os->codec && os->nb_header < os->codec->nb_header) { + av_log(s, AV_LOG_WARNING, + "Headers mismatch for stream %d: " + "expected %d received %d.\n", + i, os->codec->nb_header, os->nb_header); + if (s->error_recognition & AV_EF_EXPLODE) + return AVERROR_INVALIDDATA; + } + if (os->start_granule != OGG_NOGRANULE_VALUE) + os->lastpts = s->streams[i]->start_time = + ogg_gptopts(s, i, os->start_granule, NULL); + } //linear granulepos seek from end ogg_get_length(s); - //fill the extradata in the per codec callbacks return 0; } @@ -600,14 +716,32 @@ static int64_t ogg_calc_pts(AVFormatContext *s, int idx, int64_t *dts) return pts; } +static void ogg_validate_keyframe(AVFormatContext *s, int idx, int pstart, int psize) +{ + struct ogg *ogg = s->priv_data; + struct ogg_stream *os = ogg->streams + idx; + if (psize && s->streams[idx]->codec->codec_id == AV_CODEC_ID_THEORA) { + if (!!(os->pflags & AV_PKT_FLAG_KEY) != !(os->buf[pstart] & 0x40)) { + os->pflags ^= AV_PKT_FLAG_KEY; + av_log(s, AV_LOG_WARNING, "Broken file, %skeyframe not correctly marked.\n", + (os->pflags & AV_PKT_FLAG_KEY) ? "" : "non-"); + } + } +} + static int ogg_read_packet(AVFormatContext *s, AVPacket *pkt) { struct ogg *ogg; struct ogg_stream *os; - int idx = -1, ret; + int idx, ret; int pstart, psize; int64_t fpos, pts, dts; + if (s->io_repositioned) { + ogg_reset(s); + s->io_repositioned = 0; + } + //Get an ogg packet retry: do { @@ -621,6 +755,7 @@ retry: // pflags might not be set until after this pts = ogg_calc_pts(s, idx, &dts); + ogg_validate_keyframe(s, idx, pstart, psize); if (os->keyframe_seek && !(os->pflags & AV_PKT_FLAG_KEY)) goto retry; @@ -639,6 +774,18 @@ retry: pkt->duration = os->pduration; pkt->pos = fpos; + if (os->end_trimming) { + uint8_t *side_data = av_packet_new_side_data(pkt, + AV_PKT_DATA_SKIP_SAMPLES, + 10); + if(side_data == NULL) { + av_free_packet(pkt); + av_free(pkt); + return AVERROR(ENOMEM); + } + AV_WL32(side_data + 4, os->end_trimming); + } + return psize; } @@ -648,22 +795,33 @@ static int64_t ogg_read_timestamp(AVFormatContext *s, int stream_index, struct ogg *ogg = s->priv_data; AVIOContext *bc = s->pb; int64_t pts = AV_NOPTS_VALUE; - int i = -1; + int64_t keypos = -1; + int i; + int pstart, psize; avio_seek(bc, *pos_arg, SEEK_SET); - ogg_reset(ogg); + ogg_reset(s); - while (avio_tell(bc) < pos_limit && - !ogg_packet(s, &i, NULL, NULL, pos_arg)) { + while ( avio_tell(bc) <= pos_limit + && !ogg_packet(s, &i, &pstart, &psize, pos_arg)) { if (i == stream_index) { struct ogg_stream *os = ogg->streams + stream_index; pts = ogg_calc_pts(s, i, NULL); - if (os->keyframe_seek && !(os->pflags & AV_PKT_FLAG_KEY)) - pts = AV_NOPTS_VALUE; + ogg_validate_keyframe(s, i, pstart, psize); + if (os->pflags & AV_PKT_FLAG_KEY) { + keypos = *pos_arg; + } else if (os->keyframe_seek) { + // if we had a previous keyframe but no pts for it, + // return that keyframe with this pts value. + if (keypos >= 0) + *pos_arg = keypos; + else + pts = AV_NOPTS_VALUE; + } } if (pts != AV_NOPTS_VALUE) break; } - ogg_reset(ogg); + ogg_reset(s); return pts; } @@ -674,6 +832,11 @@ static int ogg_read_seek(AVFormatContext *s, int stream_index, struct ogg_stream *os = ogg->streams + stream_index; int ret; + av_assert0(stream_index < ogg->nstreams); + // Ensure everything is reset even when seeking via + // the generated index. + ogg_reset(s); + // Try seeking to a keyframe first. If this fails (very possible), // av_seek_frame will fall back to ignoring keyframes if (s->streams[stream_index]->codec->codec_type == AVMEDIA_TYPE_VIDEO @@ -705,5 +868,5 @@ AVInputFormat ff_ogg_demuxer = { .read_seek = ogg_read_seek, .read_timestamp = ogg_read_timestamp, .extensions = "ogg", - .flags = AVFMT_GENERIC_INDEX | AVFMT_NOBINSEARCH, + .flags = AVFMT_GENERIC_INDEX | AVFMT_TS_DISCONT | AVFMT_NOBINSEARCH, }; diff --git a/libavformat/oggdec.h b/libavformat/oggdec.h index 918378d2ea..c31859fecd 100644 --- a/libavformat/oggdec.h +++ b/libavformat/oggdec.h @@ -81,7 +81,10 @@ struct ogg_stream { int incomplete; ///< whether we're expecting a continuation in the next page int page_end; ///< current packet is the last one completed in the page int keyframe_seek; + int got_start; + int got_data; ///< 1 if the stream got some data (non-initial packets), 0 otherwise int nb_header; ///< set to the number of parsed headers + int end_trimming; ///< set the number of packets to drop from the end void *private; }; @@ -98,6 +101,7 @@ struct ogg { int nstreams; int headers; int curidx; + int64_t page_pos; ///< file offset of the current page struct ogg_state *state; }; @@ -105,7 +109,7 @@ struct ogg { #define OGG_FLAG_BOS 2 #define OGG_FLAG_EOS 4 -#define OGG_NOGRANULE_VALUE -1ull +#define OGG_NOGRANULE_VALUE (-1ull) extern const struct ogg_codec ff_celt_codec; extern const struct ogg_codec ff_dirac_codec; diff --git a/libavformat/oggenc.c b/libavformat/oggenc.c index e01c365125..1883501745 100644 --- a/libavformat/oggenc.c +++ b/libavformat/oggenc.c @@ -2,20 +2,20 @@ * Ogg muxer * Copyright (c) 2007 Baptiste Coudurier <baptiste dot coudurier at free dot fr> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -75,10 +75,12 @@ typedef struct { #define PARAM AV_OPT_FLAG_ENCODING_PARAM static const AVOption options[] = { + { "oggpagesize", "Set preferred Ogg page size.", + offsetof(OGGContext, pref_size), AV_OPT_TYPE_INT, {.i64 = 0}, 0, MAX_PAGE_SIZE, AV_OPT_FLAG_ENCODING_PARAM}, { "pagesize", "preferred page size in bytes (deprecated)", OFFSET(pref_size), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, MAX_PAGE_SIZE, PARAM }, { "page_duration", "preferred page duration, in microseconds", - OFFSET(pref_duration), AV_OPT_TYPE_INT, { .i64 = 1000000 }, 0, INT64_MAX, PARAM }, + OFFSET(pref_duration), AV_OPT_TYPE_INT64, { .i64 = 1000000 }, 0, INT64_MAX, PARAM }, { NULL }, }; @@ -137,6 +139,11 @@ static int ogg_write_page(AVFormatContext *s, OGGPage *page, int extra_flags) return 0; } +static int ogg_key_granule(OGGStreamContext *oggstream, int64_t granule) +{ + return oggstream->kfgshift && !(granule & ((1<<oggstream->kfgshift)-1)); +} + static int64_t ogg_granule_to_timestamp(OGGStreamContext *oggstream, int64_t granule) { if (oggstream->kfgshift) @@ -207,9 +214,14 @@ static int ogg_buffer_data(AVFormatContext *s, AVStream *st, int i, segments, len, flush = 0; // Handles VFR by flushing page because this frame needs to have a timestamp + // For theora, keyframes also need to have a timestamp to correctly mark + // them as such, otherwise seeking will not work correctly at the very + // least with old libogg versions. + // Do not try to flush header packets though, that will create broken files. if (st->codec->codec_id == AV_CODEC_ID_THEORA && !header && - ogg_granule_to_timestamp(oggstream, granule) > - ogg_granule_to_timestamp(oggstream, oggstream->last_granule) + 1) { + (ogg_granule_to_timestamp(oggstream, granule) > + ogg_granule_to_timestamp(oggstream, oggstream->last_granule) + 1 || + ogg_key_granule(oggstream, granule))) { if (oggstream->page.granule != -1) ogg_buffer_page(s, oggstream); flush = 1; @@ -268,7 +280,7 @@ static int ogg_buffer_data(AVFormatContext *s, AVStream *st, static uint8_t *ogg_write_vorbiscomment(int offset, int bitexact, int *header_len, AVDictionary **m, int framing_bit) { - const char *vendor = bitexact ? "Libav" : LIBAVFORMAT_IDENT; + const char *vendor = bitexact ? "ffmpeg" : LIBAVFORMAT_IDENT; int size; uint8_t *p, *p0; unsigned int count; @@ -389,7 +401,7 @@ static int ogg_build_opus_headers(AVCodecContext *avctx, static int ogg_write_header(AVFormatContext *s) { OGGContext *ogg = s->priv_data; - OGGStreamContext *oggstream; + OGGStreamContext *oggstream = NULL; int i, j; if (ogg->pref_size) @@ -399,13 +411,13 @@ static int ogg_write_header(AVFormatContext *s) AVStream *st = s->streams[i]; unsigned serial_num = i; - if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) + if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) { if (st->codec->codec_id == AV_CODEC_ID_OPUS) /* Opus requires a fixed 48kHz clock */ avpriv_set_pts_info(st, 64, 1, 48000); else avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate); - else if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) + } else if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) avpriv_set_pts_info(st, 64, st->codec->time_base.num, st->codec->time_base.den); if (st->codec->codec_id != AV_CODEC_ID_VORBIS && st->codec->codec_id != AV_CODEC_ID_THEORA && @@ -434,11 +446,13 @@ static int ogg_write_header(AVFormatContext *s) } while (j < i); oggstream->serial_num = serial_num; + av_dict_copy(&st->metadata, s->metadata, AV_DICT_DONT_OVERWRITE); + st->priv_data = oggstream; if (st->codec->codec_id == AV_CODEC_ID_FLAC) { int err = ogg_build_flac_headers(st->codec, oggstream, st->codec->flags & CODEC_FLAG_BITEXACT, - &s->metadata); + &st->metadata); if (err) { av_log(s, AV_LOG_ERROR, "Error writing FLAC headers\n"); av_freep(&st->priv_data); @@ -447,7 +461,7 @@ static int ogg_write_header(AVFormatContext *s) } else if (st->codec->codec_id == AV_CODEC_ID_SPEEX) { int err = ogg_build_speex_headers(st->codec, oggstream, st->codec->flags & CODEC_FLAG_BITEXACT, - &s->metadata); + &st->metadata); if (err) { av_log(s, AV_LOG_ERROR, "Error writing Speex headers\n"); av_freep(&st->priv_data); @@ -456,7 +470,7 @@ static int ogg_write_header(AVFormatContext *s) } else if (st->codec->codec_id == AV_CODEC_ID_OPUS) { int err = ogg_build_opus_headers(st->codec, oggstream, st->codec->flags & CODEC_FLAG_BITEXACT, - &s->metadata); + &st->metadata); if (err) { av_log(s, AV_LOG_ERROR, "Error writing Opus headers\n"); av_freep(&st->priv_data); @@ -477,7 +491,7 @@ static int ogg_write_header(AVFormatContext *s) } p = ogg_write_vorbiscomment(7, st->codec->flags & CODEC_FLAG_BITEXACT, - &oggstream->header_len[1], &s->metadata, + &oggstream->header_len[1], &st->metadata, framing_bit); oggstream->header[1] = p; if (!p) @@ -508,7 +522,7 @@ static int ogg_write_header(AVFormatContext *s) AVStream *st = s->streams[j]; OGGStreamContext *oggstream = st->priv_data; for (i = 1; i < 3; i++) { - if (oggstream && oggstream->header_len[i]) + if (oggstream->header_len[i]) ogg_buffer_data(s, st, oggstream->header[i], oggstream->header_len[i], 0, 1); } @@ -600,7 +614,7 @@ static int ogg_write_trailer(AVFormatContext *s) if (st->codec->codec_id == AV_CODEC_ID_FLAC || st->codec->codec_id == AV_CODEC_ID_SPEEX || st->codec->codec_id == AV_CODEC_ID_OPUS) { - av_free(oggstream->header[0]); + av_freep(&oggstream->header[0]); } av_freep(&oggstream->header[1]); av_freep(&st->priv_data); diff --git a/libavformat/oggparsecelt.c b/libavformat/oggparsecelt.c index 0deccc2d08..f3c2c1a632 100644 --- a/libavformat/oggparsecelt.c +++ b/libavformat/oggparsecelt.c @@ -1,21 +1,21 @@ /* - * Xiph CELT / Opus parser for Ogg + * Xiph CELT parser for Ogg * Copyright (c) 2011 Nicolas George * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -44,16 +44,12 @@ static int celt_header(AVFormatContext *s, int idx) uint32_t version, sample_rate, nb_channels, frame_size; uint32_t overlap, extra_headers; - uint8_t *extradata; - extradata = av_malloc(2 * sizeof(uint32_t) + - FF_INPUT_BUFFER_PADDING_SIZE); priv = av_malloc(sizeof(struct oggcelt_private)); - if (!extradata || !priv) { - av_free(extradata); - av_free(priv); + if (!priv) + return AVERROR(ENOMEM); + if (ff_alloc_extradata(st->codec, 2 * sizeof(uint32_t)) < 0) return AVERROR(ENOMEM); - } version = AV_RL32(p + 28); /* unused header size field skipped */ sample_rate = AV_RL32(p + 36); @@ -62,21 +58,18 @@ static int celt_header(AVFormatContext *s, int idx) overlap = AV_RL32(p + 48); /* unused bytes per packet field skipped */ extra_headers = AV_RL32(p + 56); - av_free(os->private); - av_free(st->codec->extradata); st->codec->codec_type = AVMEDIA_TYPE_AUDIO; st->codec->codec_id = AV_CODEC_ID_CELT; st->codec->sample_rate = sample_rate; st->codec->channels = nb_channels; st->codec->frame_size = frame_size; - st->codec->extradata = extradata; - st->codec->extradata_size = 2 * sizeof(uint32_t); if (sample_rate) avpriv_set_pts_info(st, 64, 1, sample_rate); priv->extra_headers_left = 1 + extra_headers; + av_free(os->private); os->private = priv; - AV_WL32(extradata + 0, overlap); - AV_WL32(extradata + 4, version); + AV_WL32(st->codec->extradata + 0, overlap); + AV_WL32(st->codec->extradata + 4, version); return 1; } else if (priv && priv->extra_headers_left) { /* Extra headers (vorbiscomment) */ diff --git a/libavformat/oggparsedirac.c b/libavformat/oggparsedirac.c index 55a0b59127..73bc495aa8 100644 --- a/libavformat/oggparsedirac.c +++ b/libavformat/oggparsedirac.c @@ -1,20 +1,20 @@ /* * Copyright (C) 2008 David Conrad * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/oggparseflac.c b/libavformat/oggparseflac.c index f59b4008dc..f69533f6c1 100644 --- a/libavformat/oggparseflac.c +++ b/libavformat/oggparseflac.c @@ -1,20 +1,20 @@ /* * Copyright (C) 2005 Matthieu CASTET * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -62,10 +62,9 @@ flac_header (AVFormatContext * s, int idx) st->codec->codec_id = AV_CODEC_ID_FLAC; st->need_parsing = AVSTREAM_PARSE_HEADERS; - st->codec->extradata = - av_malloc(FLAC_STREAMINFO_SIZE + FF_INPUT_BUFFER_PADDING_SIZE); - memcpy(st->codec->extradata, streaminfo_start, FLAC_STREAMINFO_SIZE); - st->codec->extradata_size = FLAC_STREAMINFO_SIZE; + if (ff_alloc_extradata(st->codec, FLAC_STREAMINFO_SIZE) < 0) + return AVERROR(ENOMEM); + memcpy(st->codec->extradata, streaminfo_start, st->codec->extradata_size); avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate); } else if (mdt == FLAC_METADATA_TYPE_VORBIS_COMMENT) { @@ -78,11 +77,32 @@ flac_header (AVFormatContext * s, int idx) static int old_flac_header (AVFormatContext * s, int idx) { + struct ogg *ogg = s->priv_data; AVStream *st = s->streams[idx]; + struct ogg_stream *os = ogg->streams + idx; + AVCodecParserContext *parser = av_parser_init(AV_CODEC_ID_FLAC); + int size; + uint8_t *data; + + if (!parser) + return -1; + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; st->codec->codec_id = AV_CODEC_ID_FLAC; - return 0; + parser->flags = PARSER_FLAG_COMPLETE_FRAMES; + av_parser_parse2(parser, st->codec, + &data, &size, os->buf + os->pstart, os->psize, + AV_NOPTS_VALUE, AV_NOPTS_VALUE, -1); + + av_parser_close(parser); + + if (st->codec->sample_rate) { + avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate); + return 0; + } + + return 1; } const struct ogg_codec ff_flac_codec = { diff --git a/libavformat/oggparseogm.c b/libavformat/oggparseogm.c index 56ea5575f2..b8c502a5ff 100644 --- a/libavformat/oggparseogm.c +++ b/libavformat/oggparseogm.c @@ -23,6 +23,7 @@ **/ #include <stdlib.h> +#include "libavutil/avassert.h" #include "libavutil/intreadwrite.h" #include "libavcodec/get_bits.h" #include "libavcodec/bytestream.h" @@ -40,6 +41,7 @@ ogm_header(AVFormatContext *s, int idx) GetByteContext p; uint64_t time_unit; uint64_t spu; + uint32_t size; bytestream2_init(&p, os->buf + os->pstart, os->psize); if (!(bytestream2_peek_byte(&p) & 1)) @@ -68,11 +70,13 @@ ogm_header(AVFormatContext *s, int idx) acid[4] = 0; cid = strtol(acid, NULL, 16); st->codec->codec_id = ff_codec_get_id(ff_codec_wav_tags, cid); - st->need_parsing = AVSTREAM_PARSE_FULL; + // our parser completely breaks AAC in Ogg + if (st->codec->codec_id != AV_CODEC_ID_AAC) + st->need_parsing = AVSTREAM_PARSE_FULL; } - bytestream2_skip(&p, 4); /* useless size field */ - + size = bytestream2_get_le32(&p); + size = FFMIN(size, os->psize); time_unit = bytestream2_get_le64(&p); spu = bytestream2_get_le64(&p); bytestream2_skip(&p, 4); /* default_len */ @@ -86,8 +90,18 @@ ogm_header(AVFormatContext *s, int idx) st->codec->channels = bytestream2_get_le16(&p); bytestream2_skip(&p, 2); /* block_align */ st->codec->bit_rate = bytestream2_get_le32(&p) * 8; - st->codec->sample_rate = spu * 10000000 / time_unit; + st->codec->sample_rate = time_unit ? spu * 10000000 / time_unit : 0; avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate); + if (size >= 56 && st->codec->codec_id == AV_CODEC_ID_AAC) { + bytestream2_skip(&p, 4); + size -= 4; + } + if (size > 52) { + av_assert0(FF_INPUT_BUFFER_PADDING_SIZE <= 52); + size -= 52; + ff_alloc_extradata(st->codec, size); + bytestream2_get_buffer(&p, st->codec->extradata, st->codec->extradata_size); + } } } else if (bytestream2_peek_byte(&p) == 3) { bytestream2_skip(&p, 7); diff --git a/libavformat/oggparseopus.c b/libavformat/oggparseopus.c index babd0f0fa6..78aa333187 100644 --- a/libavformat/oggparseopus.c +++ b/libavformat/oggparseopus.c @@ -2,20 +2,20 @@ * Opus parser for Ogg * Copyright (c) 2012 Nicolas George * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -41,29 +41,28 @@ static int opus_header(AVFormatContext *avf, int idx) AVStream *st = avf->streams[idx]; struct oggopus_private *priv = os->private; uint8_t *packet = os->buf + os->pstart; - uint8_t *extradata; if (!priv) { priv = os->private = av_mallocz(sizeof(*priv)); if (!priv) return AVERROR(ENOMEM); } + if (os->flags & OGG_FLAG_BOS) { if (os->psize < OPUS_HEAD_SIZE || (AV_RL8(packet + 8) & 0xF0) != 0) return AVERROR_INVALIDDATA; - st->codec->codec_type = AVMEDIA_TYPE_AUDIO; st->codec->codec_id = AV_CODEC_ID_OPUS; - st->codec->channels = AV_RL8(packet + 9); + st->codec->channels = AV_RL8 (packet + 9); priv->pre_skip = AV_RL16(packet + 10); + /*orig_sample_rate = AV_RL32(packet + 12);*/ + /*gain = AV_RL16(packet + 16);*/ + /*channel_map = AV_RL8 (packet + 18);*/ - extradata = av_malloc(os->psize + FF_INPUT_BUFFER_PADDING_SIZE); - if (!extradata) + if (ff_alloc_extradata(st->codec, os->psize)) return AVERROR(ENOMEM); - memcpy(extradata, packet, os->psize); - st->codec->extradata = extradata; - st->codec->extradata_size = os->psize; + memcpy(st->codec->extradata, packet, os->psize); st->codec->sample_rate = 48000; avpriv_set_pts_info(st, 64, 1, 48000); @@ -121,10 +120,10 @@ static int opus_packet(AVFormatContext *avf, int idx) skip = FFMIN(skip, os->pduration); if (skip > 0) { os->pduration = skip < os->pduration ? os->pduration - skip : 1; - av_log(avf, AV_LOG_WARNING, - "Last packet is truncated to %d (because of unimplemented end trim support).\n", + os->end_trimming = skip; + av_log(avf, AV_LOG_DEBUG, + "Last packet was truncated to %d due to end trimming.\n", os->pduration); - return AVERROR_PATCHWELCOME; } } @@ -137,6 +136,5 @@ const struct ogg_codec ff_opus_codec = { .magicsize = 8, .header = opus_header, .packet = opus_packet, - .granule_is_start = 1, .nb_header = 1, }; diff --git a/libavformat/oggparseskeleton.c b/libavformat/oggparseskeleton.c index f437c69444..d94b0c2e07 100644 --- a/libavformat/oggparseskeleton.c +++ b/libavformat/oggparseskeleton.c @@ -1,20 +1,20 @@ /* * Copyright (C) 2010 David Conrad * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -37,6 +37,9 @@ static int skeleton_header(AVFormatContext *s, int idx) strcpy(st->codec->codec_name, "skeleton"); st->codec->codec_type = AVMEDIA_TYPE_DATA; + if ((os->flags & OGG_FLAG_EOS) && os->psize == 0) + return 1; + if (os->psize < 8) return -1; @@ -47,7 +50,7 @@ static int skeleton_header(AVFormatContext *s, int idx) version_major = AV_RL16(buf+8); version_minor = AV_RL16(buf+10); - if (version_major != 3) { + if (version_major != 3 && version_major != 4) { av_log(s, AV_LOG_WARNING, "Unknown skeleton version %d.%d\n", version_major, version_minor); return -1; @@ -61,7 +64,7 @@ static int skeleton_header(AVFormatContext *s, int idx) start_num = AV_RL64(buf+12); start_den = AV_RL64(buf+20); - if (start_den) { + if (start_den > 0 && start_num > 0) { int base_den; av_reduce(&start_time, &base_den, start_num, start_den, INT_MAX); avpriv_set_pts_info(st, 64, 1, base_den); @@ -74,12 +77,16 @@ static int skeleton_header(AVFormatContext *s, int idx) target_idx = ogg_find_stream(ogg, AV_RL32(buf+12)); start_granule = AV_RL64(buf+36); + if (target_idx < 0) { + av_log(s, AV_LOG_WARNING, "Serial number in fisbone doesn't match any stream\n"); + return 1; + } + os = ogg->streams + target_idx; if (os->start_granule != OGG_NOGRANULE_VALUE) { - avpriv_report_missing_feature(s, - "Multiple fisbone for the same stream"); + av_log(s, AV_LOG_WARNING, "Multiple fisbone for the same stream\n"); return 1; } - if (target_idx >= 0 && start_granule != OGG_NOGRANULE_VALUE) { + if (start_granule != OGG_NOGRANULE_VALUE) { os->start_granule = start_granule; } } diff --git a/libavformat/oggparsespeex.c b/libavformat/oggparsespeex.c index 42480a3a27..1b9de9c95b 100644 --- a/libavformat/oggparsespeex.c +++ b/libavformat/oggparsespeex.c @@ -58,6 +58,11 @@ static int speex_header(AVFormatContext *s, int idx) { st->codec->codec_type = AVMEDIA_TYPE_AUDIO; st->codec->codec_id = AV_CODEC_ID_SPEEX; + if (os->psize < 68) { + av_log(s, AV_LOG_ERROR, "speex packet too small\n"); + return AVERROR_INVALIDDATA; + } + st->codec->sample_rate = AV_RL32(p + 36); st->codec->channels = AV_RL32(p + 48); if (st->codec->channels < 1 || st->codec->channels > 2) { @@ -72,9 +77,8 @@ static int speex_header(AVFormatContext *s, int idx) { if (frames_per_packet) spxp->packet_size *= frames_per_packet; - st->codec->extradata_size = os->psize; - st->codec->extradata = av_malloc(st->codec->extradata_size - + FF_INPUT_BUFFER_PADDING_SIZE); + if (ff_alloc_extradata(st->codec, os->psize) < 0) + return AVERROR(ENOMEM); memcpy(st->codec->extradata, p, st->codec->extradata_size); avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate); diff --git a/libavformat/oggparsetheora.c b/libavformat/oggparsetheora.c index 035bdc24e2..6458b97bc0 100644 --- a/libavformat/oggparsetheora.c +++ b/libavformat/oggparsetheora.c @@ -122,6 +122,7 @@ static int theora_header(AVFormatContext *s, int idx) return AVERROR_INVALIDDATA; break; default: + av_log(s, AV_LOG_ERROR, "Unknown header type %X\n", os->buf[os->pstart]); return AVERROR_INVALIDDATA; } @@ -165,10 +166,47 @@ static uint64_t theora_gptopts(AVFormatContext *ctx, int idx, uint64_t gp, return iframe + pframe; } +static int theora_packet(AVFormatContext *s, int idx) +{ + struct ogg *ogg = s->priv_data; + struct ogg_stream *os = ogg->streams + idx; + int duration; + + /* first packet handling + here we parse the duration of each packet in the first page and compare + the total duration to the page granule to find the encoder delay and + set the first timestamp */ + + if ((!os->lastpts || os->lastpts == AV_NOPTS_VALUE) && !(os->flags & OGG_FLAG_EOS)) { + int seg; + + duration = 1; + for (seg = os->segp; seg < os->nsegs; seg++) { + if (os->segments[seg] < 255) + duration ++; + } + + os->lastpts = os->lastdts = theora_gptopts(s, idx, os->granule, NULL) - duration; + if(s->streams[idx]->start_time == AV_NOPTS_VALUE) { + s->streams[idx]->start_time = os->lastpts; + if (s->streams[idx]->duration) + s->streams[idx]->duration -= s->streams[idx]->start_time; + } + } + + /* parse packet duration */ + if (os->psize > 0) { + os->pduration = 1; + } + + return 0; +} + const struct ogg_codec ff_theora_codec = { .magic = "\200theora", .magicsize = 7, .header = theora_header, + .packet = theora_packet, .gptopts = theora_gptopts, .nb_header = 3, }; diff --git a/libavformat/oggparsevorbis.c b/libavformat/oggparsevorbis.c index c19b54552c..36ad7384ea 100644 --- a/libavformat/oggparsevorbis.c +++ b/libavformat/oggparsevorbis.c @@ -141,9 +141,10 @@ int ff_vorbis_comment(AVFormatContext *as, AVDictionary **m, char *pict = av_malloc(vl); if (!pict) { + av_log(as, AV_LOG_WARNING, "out-of-memory error. Skipping cover art block.\n"); av_freep(&tt); av_freep(&ct); - return AVERROR(ENOMEM); + continue; } if ((ret = av_base64_decode(pict, ct, vl)) > 0) ret = ff_flac_parse_picture(as, pict, ret); @@ -201,12 +202,15 @@ static int fixup_vorbis_headers(AVFormatContext *as, uint8_t **buf) { int i, offset, len, err; + int buf_len; unsigned char *ptr; len = priv->len[0] + priv->len[1] + priv->len[2]; - ptr = *buf = av_mallocz(len + len / 255 + 64); + buf_len = len + len / 255 + 64; + ptr = *buf = av_realloc(NULL, buf_len); if (!ptr) return AVERROR(ENOMEM); + memset(*buf, '\0', buf_len); ptr[0] = 2; offset = 1; @@ -269,6 +273,7 @@ static int vorbis_header(AVFormatContext *s, int idx) const uint8_t *p = os->buf + os->pstart + 7; /* skip "\001vorbis" tag */ unsigned blocksize, bs0, bs1; int srate; + int channels; if (os->psize != 30) return AVERROR_INVALIDDATA; @@ -276,7 +281,12 @@ static int vorbis_header(AVFormatContext *s, int idx) if (bytestream_get_le32(&p) != 0) /* vorbis_version */ return AVERROR_INVALIDDATA; - st->codec->channels = bytestream_get_byte(&p); + channels = bytestream_get_byte(&p); + if (st->codec->channels && channels != st->codec->channels) { + av_log(s, AV_LOG_ERROR, "Channel change is not supported\n"); + return AVERROR_PATCHWELCOME; + } + st->codec->channels = channels; srate = bytestream_get_le32(&p); p += 4; // skip maximum bitrate st->codec->bit_rate = bytestream_get_le32(&p); // nominal bitrate @@ -341,23 +351,28 @@ static int vorbis_packet(AVFormatContext *s, int idx) * here we parse the duration of each packet in the first page and compare * the total duration to the page granule to find the encoder delay and * set the first timestamp */ - if (!os->lastpts) { - int seg; + if ((!os->lastpts || os->lastpts == AV_NOPTS_VALUE) && !(os->flags & OGG_FLAG_EOS)) { + int seg, d; uint8_t *last_pkt = os->buf + os->pstart; uint8_t *next_pkt = last_pkt; - int first_duration = 0; avpriv_vorbis_parse_reset(&priv->vp); duration = 0; - for (seg = 0; seg < os->nsegs; seg++) { + seg = os->segp; + d = avpriv_vorbis_parse_frame(&priv->vp, last_pkt, 1); + if (d < 0) { + os->pflags |= AV_PKT_FLAG_CORRUPT; + return 0; + } + duration += d; + last_pkt = next_pkt = next_pkt + os->psize; + for (; seg < os->nsegs; seg++) { if (os->segments[seg] < 255) { int d = avpriv_vorbis_parse_frame(&priv->vp, last_pkt, 1); if (d < 0) { duration = os->granule; break; } - if (!duration) - first_duration = d; duration += d; last_pkt = next_pkt + os->segments[seg]; } @@ -365,10 +380,11 @@ static int vorbis_packet(AVFormatContext *s, int idx) } os->lastpts = os->lastdts = os->granule - duration; - s->streams[idx]->start_time = os->lastpts + first_duration; - if (s->streams[idx]->duration) - s->streams[idx]->duration -= s->streams[idx]->start_time; - s->streams[idx]->cur_dts = AV_NOPTS_VALUE; + if (s->streams[idx]->start_time == AV_NOPTS_VALUE) { + s->streams[idx]->start_time = FFMAX(os->lastpts, 0); + if (s->streams[idx]->duration) + s->streams[idx]->duration -= s->streams[idx]->start_time; + } priv->final_pts = AV_NOPTS_VALUE; avpriv_vorbis_parse_reset(&priv->vp); } @@ -376,7 +392,7 @@ static int vorbis_packet(AVFormatContext *s, int idx) /* parse packet duration */ if (os->psize > 0) { duration = avpriv_vorbis_parse_frame(&priv->vp, os->buf + os->pstart, 1); - if (duration <= 0) { + if (duration < 0) { os->pflags |= AV_PKT_FLAG_CORRUPT; return 0; } diff --git a/libavformat/oma.c b/libavformat/oma.c index 27b598857c..27028674db 100644 --- a/libavformat/oma.c +++ b/libavformat/oma.c @@ -1,20 +1,20 @@ /* * Sony OpenMG (OMA) common data * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/oma.h b/libavformat/oma.h index 9a35da2623..e2a187bacc 100644 --- a/libavformat/oma.h +++ b/libavformat/oma.h @@ -1,20 +1,20 @@ /* * Sony OpenMG (OMA) common data * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/omadec.c b/libavformat/omadec.c index e5a20903b0..4017db3f2b 100644 --- a/libavformat/omadec.c +++ b/libavformat/omadec.c @@ -5,20 +5,20 @@ * 2008 Benjamin Larsson * 2011 David Goldwich * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -37,7 +37,6 @@ * - Sound data organized in packets follow the EA3 header * (can be encrypted using the Sony DRM!). * - * CODEC SUPPORT: Only ATRAC3 codec is currently supported! */ #include "libavutil/channel_layout.h" @@ -172,13 +171,9 @@ static int nprobe(AVFormatContext *s, uint8_t *enc_header, unsigned size, taglen = AV_RB32(&enc_header[pos + 32]); datalen = AV_RB32(&enc_header[pos + 36]) >> 4; - pos += 44; - if (size - pos < taglen) - return -1; - - pos += taglen; + pos += 44L + taglen; - if (datalen << 4 > size - pos) + if (pos + (((uint64_t)datalen) << 4) > size) return -1; av_des_init(&av_des, n_val, 192, 1); @@ -237,6 +232,12 @@ static int decrypt_init(AVFormatContext *s, ID3v2ExtraMeta *em, uint8_t *header) av_log(s, AV_LOG_ERROR, "Invalid encryption header\n"); return AVERROR_INVALIDDATA; } + if ( OMA_ENC_HEADER_SIZE + oc->k_size + oc->e_size + oc->i_size + 8 > geob->datasize + || OMA_ENC_HEADER_SIZE + 48 > geob->datasize + ) { + av_log(s, AV_LOG_ERROR, "Too little GEOB data\n"); + return AVERROR_INVALIDDATA; + } oc->rid = AV_RB32(&gdata[OMA_ENC_HEADER_SIZE + 28]); av_log(s, AV_LOG_DEBUG, "RID: %.8x\n", oc->rid); @@ -263,7 +264,7 @@ static int decrypt_init(AVFormatContext *s, ID3v2ExtraMeta *em, uint8_t *header) !nprobe(s, gdata, geob->datasize, oc->n_val)) break; } - if (i >= sizeof(leaf_table)) { + if (i >= FF_ARRAY_ELEMS(leaf_table)) { av_log(s, AV_LOG_ERROR, "Invalid key\n"); return AVERROR_INVALIDDATA; } @@ -348,12 +349,10 @@ static int oma_read_header(AVFormatContext *s) /* fake the ATRAC3 extradata * (wav format, makes stream copy to wav work) */ - st->codec->extradata_size = 14; - edata = av_mallocz(14 + FF_INPUT_BUFFER_PADDING_SIZE); - if (!edata) + if (ff_alloc_extradata(st->codec, 14)) return AVERROR(ENOMEM); - st->codec->extradata = edata; + edata = st->codec->extradata; AV_WL16(&edata[0], 1); // always 1 AV_WL32(&edata[2], samplerate); // samples rate AV_WL16(&edata[6], jsflag); // coding mode @@ -384,7 +383,7 @@ static int oma_read_header(AVFormatContext *s) av_log(s, AV_LOG_ERROR, "Unsupported codec ATRAC3+!\n"); break; case OMA_CODECID_MP3: - st->need_parsing = AVSTREAM_PARSE_FULL; + st->need_parsing = AVSTREAM_PARSE_FULL_RAW; framesize = 1024; break; case OMA_CODECID_LPCM: diff --git a/libavformat/omaenc.c b/libavformat/omaenc.c index ea28e109c4..b947987234 100644 --- a/libavformat/omaenc.c +++ b/libavformat/omaenc.c @@ -3,20 +3,20 @@ * * Copyright (c) 2011 Michael Karcher * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -61,7 +61,7 @@ static av_cold int oma_write_header(AVFormatContext *s) switch(format->codec_tag) { case OMA_CODECID_ATRAC3: if (format->channels != 2) { - av_log(s, AV_LOG_ERROR, "ATRAC3 in OMA is only supported with 2 channels"); + av_log(s, AV_LOG_ERROR, "ATRAC3 in OMA is only supported with 2 channels\n"); return AVERROR(EINVAL); } if (format->extradata_size == 14) /* WAV format extradata */ @@ -84,8 +84,9 @@ static av_cold int oma_write_header(AVFormatContext *s) (format->block_align/8 - 1)); break; default: - av_log(s, AV_LOG_ERROR, "OMA: unsupported codec tag %d for write\n", + av_log(s, AV_LOG_ERROR, "unsupported codec tag %d for write\n", format->codec_tag); + return AVERROR(EINVAL); } for (i = 0; i < (EA3_HEADER_SIZE - 36)/4; i++) avio_wl32(s->pb, 0); /* Padding */ diff --git a/libavformat/options.c b/libavformat/options.c index d99f04aab5..5218e5b92a 100644 --- a/libavformat/options.c +++ b/libavformat/options.c @@ -1,20 +1,20 @@ /* * Copyright (c) 2000, 2001, 2002 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "avformat.h" @@ -76,6 +76,13 @@ static const AVClass *format_child_class_next(const AVClass *prev) return NULL; } +static AVClassCategory get_category(void *ptr) +{ + AVFormatContext* s = ptr; + if(s->iformat) return AV_CLASS_CATEGORY_DEMUXER; + else return AV_CLASS_CATEGORY_MUXER; +} + static const AVClass av_format_context_class = { .class_name = "AVFormatContext", .item_name = format_to_name, @@ -83,6 +90,8 @@ static const AVClass av_format_context_class = { .version = LIBAVUTIL_VERSION_INT, .child_next = format_child_next, .child_class_next = format_child_class_next, + .category = AV_CLASS_CATEGORY_MUXER, + .get_category = get_category, }; static void avformat_get_context_defaults(AVFormatContext *s) @@ -103,6 +112,11 @@ AVFormatContext *avformat_alloc_context(void) return ic; } +enum AVDurationEstimationMethod av_fmt_ctx_get_duration_estimation_method(const AVFormatContext* ctx) +{ + return ctx->duration_estimation_method; +} + const AVClass *avformat_get_class(void) { return &av_format_context_class; diff --git a/libavformat/options_table.h b/libavformat/options_table.h index 54bf25fc27..8145325c1c 100644 --- a/libavformat/options_table.h +++ b/libavformat/options_table.h @@ -1,18 +1,20 @@ /* - * This file is part of Libav. + * Copyright (c) 2000, 2001, 2002 Fabrice Bellard * - * Libav is free software; you can redistribute it and/or + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -31,6 +33,8 @@ #define D AV_OPT_FLAG_DECODING_PARAM static const AVOption avformat_options[] = { +{"avioflags", NULL, OFFSET(avio_flags), AV_OPT_TYPE_FLAGS, {.i64 = DEFAULT }, INT_MIN, INT_MAX, D|E, "avioflags"}, +{"direct", "reduce buffering", 0, AV_OPT_TYPE_CONST, {.i64 = AVIO_FLAG_DIRECT }, INT_MIN, INT_MAX, D|E, "avioflags"}, {"probesize", "set probing size", OFFSET(probesize), AV_OPT_TYPE_INT, {.i64 = 5000000 }, 32, INT_MAX, D}, {"packetsize", "set packet size", OFFSET(packet_size), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, 0, INT_MAX, E}, {"fflags", NULL, OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64 = AVFMT_FLAG_FLUSH_PACKETS }, INT_MIN, INT_MAX, D|E, "fflags"}, @@ -41,8 +45,12 @@ static const AVOption avformat_options[] = { {"noparse", "disable AVParsers, this needs nofillin too", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_NOPARSE }, INT_MIN, INT_MAX, D, "fflags"}, {"igndts", "ignore dts", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_IGNDTS }, INT_MIN, INT_MAX, D, "fflags"}, {"discardcorrupt", "discard corrupted frames", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_DISCARD_CORRUPT }, INT_MIN, INT_MAX, D, "fflags"}, +{"sortdts", "try to interleave outputted packets by dts", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_SORT_DTS }, INT_MIN, INT_MAX, D, "fflags"}, +{"keepside", "don't merge side data", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_KEEP_SIDE_DATA }, INT_MIN, INT_MAX, D, "fflags"}, +{"latm", "enable RTP MP4A-LATM payload", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_MP4A_LATM }, INT_MIN, INT_MAX, E, "fflags"}, {"nobuffer", "reduce the latency introduced by optional buffering", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_NOBUFFER }, 0, INT_MAX, D, "fflags"}, -{"analyzeduration", "how many microseconds are analyzed to estimate duration", OFFSET(max_analyze_duration), AV_OPT_TYPE_INT, {.i64 = 5*AV_TIME_BASE }, 0, INT_MAX, D}, +{"seek2any", "allow seeking to non-keyframes on demuxer level when supported", OFFSET(seek2any), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, 1, D}, +{"analyzeduration", "specify how many microseconds are analyzed to probe the input", OFFSET(max_analyze_duration), AV_OPT_TYPE_INT, {.i64 = 5*AV_TIME_BASE }, 0, INT_MAX, D}, {"cryptokey", "decryption key", OFFSET(key), AV_OPT_TYPE_BINARY, {.dbl = 0}, 0, 0, D}, {"indexmem", "max memory used for timestamp index (per stream)", OFFSET(max_index_size), AV_OPT_TYPE_INT, {.i64 = 1<<20 }, 0, INT_MAX, D}, {"rtbufsize", "max memory used for buffering real-time frames", OFFSET(max_picture_buffer), AV_OPT_TYPE_INT, {.i64 = 3041280 }, 0, INT_MAX, D}, /* defaults to 1s of 15fps 352x288 YUYV422 video */ @@ -50,6 +58,9 @@ static const AVOption avformat_options[] = { {"ts", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_FDEBUG_TS }, INT_MIN, INT_MAX, E|D, "fdebug"}, {"max_delay", "maximum muxing or demuxing delay in microseconds", OFFSET(max_delay), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, INT_MAX, E|D}, {"fpsprobesize", "number of frames used to probe fps", OFFSET(fps_probe_size), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX-1, D}, +{"audio_preload", "microseconds by which audio packets should be interleaved earlier", OFFSET(audio_preload), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX-1, E}, +{"chunk_duration", "microseconds for each chunk", OFFSET(max_chunk_duration), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX-1, E}, +{"chunk_size", "size in bytes for each chunk", OFFSET(max_chunk_size), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX-1, E}, /* this is a crutch for avconv, since it cannot deal with identically named options in different contexts. * to be removed when avconv is fixed */ {"f_err_detect", "set error detection flags (deprecated; use err_detect, save via avconv)", OFFSET(error_recognition), AV_OPT_TYPE_FLAGS, {.i64 = AV_EF_CRCCHECK }, INT_MIN, INT_MAX, D, "err_detect"}, @@ -58,6 +69,14 @@ static const AVOption avformat_options[] = { {"bitstream", "detect bitstream specification deviations", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_BITSTREAM }, INT_MIN, INT_MAX, D, "err_detect"}, {"buffer", "detect improper bitstream length", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_BUFFER }, INT_MIN, INT_MAX, D, "err_detect"}, {"explode", "abort decoding on minor error detection", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_EXPLODE }, INT_MIN, INT_MAX, D, "err_detect"}, +{"careful", "consider things that violate the spec, are fast to check and have not been seen in the wild as errors", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_CAREFUL }, INT_MIN, INT_MAX, D, "err_detect"}, +{"compliant", "consider all spec non compliancies as errors", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_COMPLIANT }, INT_MIN, INT_MAX, D, "err_detect"}, +{"aggressive", "consider things that a sane encoder shouldn't do as an error", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_AGGRESSIVE }, INT_MIN, INT_MAX, D, "err_detect"}, +{"use_wallclock_as_timestamps", "use wallclock as timestamps", OFFSET(use_wallclock_as_timestamps), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX-1, D}, +{"avoid_negative_ts", "shift timestamps to make them non-negative. 1 enables, 0 disables, default of -1 enables when required by target format.", OFFSET(avoid_negative_ts), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 1, E}, +{"skip_initial_bytes", "set number of bytes to skip before reading header and frames", OFFSET(skip_initial_bytes), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX-1, D}, +{"correct_ts_overflow", "correct single timestamp overflows", OFFSET(correct_ts_overflow), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1, D}, +{"flush_packets", "enable flushing of the I/O context after each packet", OFFSET(flush_packets), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1, E}, {NULL}, }; diff --git a/libavformat/os_support.c b/libavformat/os_support.c index 4ae5ac1bf0..3218956df5 100644 --- a/libavformat/os_support.c +++ b/libavformat/os_support.c @@ -3,20 +3,20 @@ * Copyright (c) 2000, 2001, 2002 Fabrice Bellard * copyright (c) 2002 Francois Revol * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/os_support.h b/libavformat/os_support.h index ae8cef7efa..7c7cd1f1a4 100644 --- a/libavformat/os_support.h +++ b/libavformat/os_support.h @@ -2,20 +2,20 @@ * various OS-feature replacement utilities * copyright (c) 2000, 2001, 2002 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -33,8 +33,17 @@ #if defined(_WIN32) && !defined(__MINGW32CE__) # include <fcntl.h> +# ifdef lseek +# undef lseek +# endif # define lseek(f,p,w) _lseeki64((f), (p), (w)) +# ifdef stat +# undef stat +# endif # define stat _stati64 +# ifdef fstat +# undef fstat +# endif # define fstat(f,s) _fstati64((f), (s)) #endif /* defined(__MINGW32__) && !defined(__MINGW32CE__) */ @@ -45,8 +54,6 @@ #include <io.h> #endif #define mkdir(a, b) _mkdir(a) -#else -#include <sys/stat.h> #endif static inline int is_dos_path(const char *path) diff --git a/libavformat/output-example.c b/libavformat/output-example.c deleted file mode 100644 index 496b7f8212..0000000000 --- a/libavformat/output-example.c +++ /dev/null @@ -1,527 +0,0 @@ -/* - * Copyright (c) 2003 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -/** - * @file - * libavformat API example. - * - * @example libavformat/output-example.c - * Output a media file in any supported libavformat format. - * The default codecs are used. - */ - -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <math.h> - -#include "libavutil/mathematics.h" -#include "libavformat/avformat.h" -#include "libswscale/swscale.h" - -/* 5 seconds stream duration */ -#define STREAM_DURATION 5.0 -#define STREAM_FRAME_RATE 25 /* 25 images/s */ -#define STREAM_NB_FRAMES ((int)(STREAM_DURATION * STREAM_FRAME_RATE)) -#define STREAM_PIX_FMT AV_PIX_FMT_YUV420P /* default pix_fmt */ - -static int sws_flags = SWS_BICUBIC; - -/**************************************************************/ -/* audio output */ - -static float t, tincr, tincr2; -static int16_t *samples; -static int audio_input_frame_size; - -/* - * add an audio output stream - */ -static AVStream *add_audio_stream(AVFormatContext *oc, enum AVCodecID codec_id) -{ - AVCodecContext *c; - AVStream *st; - AVCodec *codec; - - /* find the audio encoder */ - codec = avcodec_find_encoder(codec_id); - if (!codec) { - fprintf(stderr, "codec not found\n"); - exit(1); - } - - st = avformat_new_stream(oc, codec); - if (!st) { - fprintf(stderr, "Could not alloc stream\n"); - exit(1); - } - - c = st->codec; - - /* put sample parameters */ - c->sample_fmt = AV_SAMPLE_FMT_S16; - c->bit_rate = 64000; - c->sample_rate = 44100; - c->channels = 2; - - // some formats want stream headers to be separate - if (oc->oformat->flags & AVFMT_GLOBALHEADER) - c->flags |= CODEC_FLAG_GLOBAL_HEADER; - - return st; -} - -static void open_audio(AVFormatContext *oc, AVStream *st) -{ - AVCodecContext *c; - - c = st->codec; - - /* open it */ - if (avcodec_open2(c, NULL, NULL) < 0) { - fprintf(stderr, "could not open codec\n"); - exit(1); - } - - /* init signal generator */ - t = 0; - tincr = 2 * M_PI * 110.0 / c->sample_rate; - /* increment frequency by 110 Hz per second */ - tincr2 = 2 * M_PI * 110.0 / c->sample_rate / c->sample_rate; - - if (c->codec->capabilities & CODEC_CAP_VARIABLE_FRAME_SIZE) - audio_input_frame_size = 10000; - else - audio_input_frame_size = c->frame_size; - samples = av_malloc(audio_input_frame_size * - av_get_bytes_per_sample(c->sample_fmt) * - c->channels); -} - -/* Prepare a 16 bit dummy audio frame of 'frame_size' samples and - * 'nb_channels' channels. */ -static void get_audio_frame(int16_t *samples, int frame_size, int nb_channels) -{ - int j, i, v; - int16_t *q; - - q = samples; - for (j = 0; j < frame_size; j++) { - v = (int)(sin(t) * 10000); - for (i = 0; i < nb_channels; i++) - *q++ = v; - t += tincr; - tincr += tincr2; - } -} - -static void write_audio_frame(AVFormatContext *oc, AVStream *st) -{ - AVCodecContext *c; - AVPacket pkt = { 0 }; // data and size must be 0; - AVFrame *frame = avcodec_alloc_frame(); - int got_packet; - - av_init_packet(&pkt); - c = st->codec; - - get_audio_frame(samples, audio_input_frame_size, c->channels); - frame->nb_samples = audio_input_frame_size; - avcodec_fill_audio_frame(frame, c->channels, c->sample_fmt, - (uint8_t *)samples, - audio_input_frame_size * - av_get_bytes_per_sample(c->sample_fmt) * - c->channels, 1); - - avcodec_encode_audio2(c, &pkt, frame, &got_packet); - if (!got_packet) - return; - - pkt.stream_index = st->index; - - /* Write the compressed frame to the media file. */ - if (av_interleaved_write_frame(oc, &pkt) != 0) { - fprintf(stderr, "Error while writing audio frame\n"); - exit(1); - } - avcodec_free_frame(&frame); -} - -static void close_audio(AVFormatContext *oc, AVStream *st) -{ - avcodec_close(st->codec); - - av_free(samples); -} - -/**************************************************************/ -/* video output */ - -static AVFrame *picture, *tmp_picture; -static int frame_count; - -/* Add a video output stream. */ -static AVStream *add_video_stream(AVFormatContext *oc, enum AVCodecID codec_id) -{ - AVCodecContext *c; - AVStream *st; - AVCodec *codec; - - /* find the video encoder */ - codec = avcodec_find_encoder(codec_id); - if (!codec) { - fprintf(stderr, "codec not found\n"); - exit(1); - } - - st = avformat_new_stream(oc, codec); - if (!st) { - fprintf(stderr, "Could not alloc stream\n"); - exit(1); - } - - c = st->codec; - - /* Put sample parameters. */ - c->bit_rate = 400000; - /* Resolution must be a multiple of two. */ - c->width = 352; - c->height = 288; - /* timebase: This is the fundamental unit of time (in seconds) in terms - * of which frame timestamps are represented. For fixed-fps content, - * timebase should be 1/framerate and timestamp increments should be - * identical to 1. */ - c->time_base.den = STREAM_FRAME_RATE; - c->time_base.num = 1; - c->gop_size = 12; /* emit one intra frame every twelve frames at most */ - c->pix_fmt = STREAM_PIX_FMT; - if (c->codec_id == AV_CODEC_ID_MPEG2VIDEO) { - /* just for testing, we also add B frames */ - c->max_b_frames = 2; - } - if (c->codec_id == AV_CODEC_ID_MPEG1VIDEO) { - /* Needed to avoid using macroblocks in which some coeffs overflow. - * This does not happen with normal video, it just happens here as - * the motion of the chroma plane does not match the luma plane. */ - c->mb_decision = 2; - } - /* Some formats want stream headers to be separate. */ - if (oc->oformat->flags & AVFMT_GLOBALHEADER) - c->flags |= CODEC_FLAG_GLOBAL_HEADER; - - return st; -} - -static AVFrame *alloc_picture(enum AVPixelFormat pix_fmt, int width, int height) -{ - AVFrame *picture; - uint8_t *picture_buf; - int size; - - picture = avcodec_alloc_frame(); - if (!picture) - return NULL; - size = avpicture_get_size(pix_fmt, width, height); - picture_buf = av_malloc(size); - if (!picture_buf) { - av_free(picture); - return NULL; - } - avpicture_fill((AVPicture *)picture, picture_buf, - pix_fmt, width, height); - return picture; -} - -static void open_video(AVFormatContext *oc, AVStream *st) -{ - AVCodecContext *c; - - c = st->codec; - - /* open the codec */ - if (avcodec_open2(c, NULL, NULL) < 0) { - fprintf(stderr, "could not open codec\n"); - exit(1); - } - - /* Allocate the encoded raw picture. */ - picture = alloc_picture(c->pix_fmt, c->width, c->height); - if (!picture) { - fprintf(stderr, "Could not allocate picture\n"); - exit(1); - } - - /* If the output format is not YUV420P, then a temporary YUV420P - * picture is needed too. It is then converted to the required - * output format. */ - tmp_picture = NULL; - if (c->pix_fmt != AV_PIX_FMT_YUV420P) { - tmp_picture = alloc_picture(AV_PIX_FMT_YUV420P, c->width, c->height); - if (!tmp_picture) { - fprintf(stderr, "Could not allocate temporary picture\n"); - exit(1); - } - } -} - -/* Prepare a dummy image. */ -static void fill_yuv_image(AVFrame *pict, int frame_index, - int width, int height) -{ - int x, y, i; - - i = frame_index; - - /* Y */ - for (y = 0; y < height; y++) - for (x = 0; x < width; x++) - pict->data[0][y * pict->linesize[0] + x] = x + y + i * 3; - - /* Cb and Cr */ - for (y = 0; y < height / 2; y++) { - for (x = 0; x < width / 2; x++) { - pict->data[1][y * pict->linesize[1] + x] = 128 + y + i * 2; - pict->data[2][y * pict->linesize[2] + x] = 64 + x + i * 5; - } - } -} - -static void write_video_frame(AVFormatContext *oc, AVStream *st) -{ - int ret; - AVCodecContext *c; - static struct SwsContext *img_convert_ctx; - - c = st->codec; - - if (frame_count >= STREAM_NB_FRAMES) { - /* No more frames to compress. The codec has a latency of a few - * frames if using B-frames, so we get the last frames by - * passing the same picture again. */ - } else { - if (c->pix_fmt != AV_PIX_FMT_YUV420P) { - /* as we only generate a YUV420P picture, we must convert it - * to the codec pixel format if needed */ - if (img_convert_ctx == NULL) { - img_convert_ctx = sws_getContext(c->width, c->height, - AV_PIX_FMT_YUV420P, - c->width, c->height, - c->pix_fmt, - sws_flags, NULL, NULL, NULL); - if (img_convert_ctx == NULL) { - fprintf(stderr, - "Cannot initialize the conversion context\n"); - exit(1); - } - } - fill_yuv_image(tmp_picture, frame_count, c->width, c->height); - sws_scale(img_convert_ctx, tmp_picture->data, tmp_picture->linesize, - 0, c->height, picture->data, picture->linesize); - } else { - fill_yuv_image(picture, frame_count, c->width, c->height); - } - } - - if (oc->oformat->flags & AVFMT_RAWPICTURE) { - /* Raw video case - the API will change slightly in the near - * future for that. */ - AVPacket pkt; - av_init_packet(&pkt); - - pkt.flags |= AV_PKT_FLAG_KEY; - pkt.stream_index = st->index; - pkt.data = (uint8_t *)picture; - pkt.size = sizeof(AVPicture); - - ret = av_interleaved_write_frame(oc, &pkt); - } else { - AVPacket pkt = { 0 }; - int got_packet; - av_init_packet(&pkt); - - /* encode the image */ - ret = avcodec_encode_video2(c, &pkt, picture, &got_packet); - /* If size is zero, it means the image was buffered. */ - if (!ret && got_packet && pkt.size) { - if (pkt.pts != AV_NOPTS_VALUE) { - pkt.pts = av_rescale_q(pkt.pts, - c->time_base, st->time_base); - } - if (pkt.dts != AV_NOPTS_VALUE) { - pkt.dts = av_rescale_q(pkt.dts, - c->time_base, st->time_base); - } - pkt.stream_index = st->index; - - /* Write the compressed frame to the media file. */ - ret = av_interleaved_write_frame(oc, &pkt); - } else { - ret = 0; - } - } - if (ret != 0) { - fprintf(stderr, "Error while writing video frame\n"); - exit(1); - } - frame_count++; -} - -static void close_video(AVFormatContext *oc, AVStream *st) -{ - avcodec_close(st->codec); - av_free(picture->data[0]); - av_free(picture); - if (tmp_picture) { - av_free(tmp_picture->data[0]); - av_free(tmp_picture); - } -} - -/**************************************************************/ -/* media file output */ - -int main(int argc, char **argv) -{ - const char *filename; - AVOutputFormat *fmt; - AVFormatContext *oc; - AVStream *audio_st, *video_st; - double audio_pts, video_pts; - int i; - - /* Initialize libavcodec, and register all codecs and formats. */ - av_register_all(); - - if (argc != 2) { - printf("usage: %s output_file\n" - "API example program to output a media file with libavformat.\n" - "The output format is automatically guessed according to the file extension.\n" - "Raw images can also be output by using '%%d' in the filename\n" - "\n", argv[0]); - return 1; - } - - filename = argv[1]; - - /* Autodetect the output format from the name. default is MPEG. */ - fmt = av_guess_format(NULL, filename, NULL); - if (!fmt) { - printf("Could not deduce output format from file extension: using MPEG.\n"); - fmt = av_guess_format("mpeg", NULL, NULL); - } - if (!fmt) { - fprintf(stderr, "Could not find suitable output format\n"); - return 1; - } - - /* Allocate the output media context. */ - oc = avformat_alloc_context(); - if (!oc) { - fprintf(stderr, "Memory error\n"); - return 1; - } - oc->oformat = fmt; - snprintf(oc->filename, sizeof(oc->filename), "%s", filename); - - /* Add the audio and video streams using the default format codecs - * and initialize the codecs. */ - video_st = NULL; - audio_st = NULL; - if (fmt->video_codec != AV_CODEC_ID_NONE) { - video_st = add_video_stream(oc, fmt->video_codec); - } - if (fmt->audio_codec != AV_CODEC_ID_NONE) { - audio_st = add_audio_stream(oc, fmt->audio_codec); - } - - /* Now that all the parameters are set, we can open the audio and - * video codecs and allocate the necessary encode buffers. */ - if (video_st) - open_video(oc, video_st); - if (audio_st) - open_audio(oc, audio_st); - - av_dump_format(oc, 0, filename, 1); - - /* open the output file, if needed */ - if (!(fmt->flags & AVFMT_NOFILE)) { - if (avio_open(&oc->pb, filename, AVIO_FLAG_WRITE) < 0) { - fprintf(stderr, "Could not open '%s'\n", filename); - return 1; - } - } - - /* Write the stream header, if any. */ - avformat_write_header(oc, NULL); - - for (;;) { - /* Compute current audio and video time. */ - if (audio_st) - audio_pts = (double)audio_st->pts.val * audio_st->time_base.num / audio_st->time_base.den; - else - audio_pts = 0.0; - - if (video_st) - video_pts = (double)video_st->pts.val * video_st->time_base.num / - video_st->time_base.den; - else - video_pts = 0.0; - - if ((!audio_st || audio_pts >= STREAM_DURATION) && - (!video_st || video_pts >= STREAM_DURATION)) - break; - - /* write interleaved audio and video frames */ - if (!video_st || (video_st && audio_st && audio_pts < video_pts)) { - write_audio_frame(oc, audio_st); - } else { - write_video_frame(oc, video_st); - } - } - - /* Write the trailer, if any. The trailer must be written before you - * close the CodecContexts open when you wrote the header; otherwise - * av_write_trailer() may try to use memory that was freed on - * av_codec_close(). */ - av_write_trailer(oc); - - /* Close each codec. */ - if (video_st) - close_video(oc, video_st); - if (audio_st) - close_audio(oc, audio_st); - - /* Free the streams. */ - for (i = 0; i < oc->nb_streams; i++) { - av_freep(&oc->streams[i]->codec); - av_freep(&oc->streams[i]); - } - - if (!(fmt->flags & AVFMT_NOFILE)) - /* Close the output file. */ - avio_close(oc->pb); - - /* free the stream */ - av_free(oc); - - return 0; -} diff --git a/libavformat/paf.c b/libavformat/paf.c new file mode 100644 index 0000000000..09aefe6770 --- /dev/null +++ b/libavformat/paf.c @@ -0,0 +1,262 @@ +/* + * Packed Animation File demuxer + * Copyright (c) 2012 Paul B Mahol + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/channel_layout.h" +#include "libavcodec/paf.h" +#include "avformat.h" +#include "internal.h" + +#define MAGIC "Packed Animation File V1.0\n(c) 1992-96 Amazing Studio\x0a\x1a" + +typedef struct { + uint32_t buffer_size; + uint32_t frame_blks; + uint32_t nb_frames; + uint32_t start_offset; + uint32_t preload_count; + uint32_t max_video_blks; + uint32_t max_audio_blks; + + uint32_t current_frame; + uint32_t current_frame_count; + uint32_t current_frame_block; + + uint32_t *blocks_count_table; + uint32_t *frames_offset_table; + uint32_t *blocks_offset_table; + + uint8_t *video_frame; + int video_size; + + uint8_t *audio_frame; + uint8_t *temp_audio_frame; + int audio_size; + + int got_audio; +} PAFDemuxContext; + +static int read_probe(AVProbeData *p) +{ + if ((p->buf_size >= strlen(MAGIC)) && + !memcmp(p->buf, MAGIC, strlen(MAGIC))) + return AVPROBE_SCORE_MAX; + return 0; +} + +static int read_close(AVFormatContext *s) +{ + PAFDemuxContext *p = s->priv_data; + + av_freep(&p->blocks_count_table); + av_freep(&p->frames_offset_table); + av_freep(&p->blocks_offset_table); + av_freep(&p->video_frame); + av_freep(&p->audio_frame); + av_freep(&p->temp_audio_frame); + + return 0; +} + +static void read_table(AVFormatContext *s, uint32_t *table, uint32_t count) +{ + int i; + + for (i = 0; i < count; i++) + table[i] = avio_rl32(s->pb); + + avio_skip(s->pb, 4 * (FFALIGN(count, 512) - count)); +} + +static int read_header(AVFormatContext *s) +{ + PAFDemuxContext *p = s->priv_data; + AVIOContext *pb = s->pb; + AVStream *ast, *vst; + int ret = 0; + + avio_skip(pb, 132); + + vst = avformat_new_stream(s, 0); + if (!vst) + return AVERROR(ENOMEM); + + vst->start_time = 0; + vst->nb_frames = + vst->duration = + p->nb_frames = avio_rl32(pb); + avio_skip(pb, 4); + vst->codec->width = avio_rl32(pb); + vst->codec->height = avio_rl32(pb); + avio_skip(pb, 4); + vst->codec->codec_type = AVMEDIA_TYPE_VIDEO; + vst->codec->codec_tag = 0; + vst->codec->codec_id = AV_CODEC_ID_PAF_VIDEO; + avpriv_set_pts_info(vst, 64, 1, 10); + + ast = avformat_new_stream(s, 0); + if (!ast) + return AVERROR(ENOMEM); + + ast->start_time = 0; + ast->codec->codec_type = AVMEDIA_TYPE_AUDIO; + ast->codec->codec_tag = 0; + ast->codec->codec_id = AV_CODEC_ID_PAF_AUDIO; + ast->codec->channels = 2; + ast->codec->channel_layout = AV_CH_LAYOUT_STEREO; + ast->codec->sample_rate = 22050; + avpriv_set_pts_info(ast, 64, 1, 22050); + + p->buffer_size = avio_rl32(pb); + p->preload_count = avio_rl32(pb); + p->frame_blks = avio_rl32(pb); + p->start_offset = avio_rl32(pb); + p->max_video_blks = avio_rl32(pb); + p->max_audio_blks = avio_rl32(pb); + if (p->buffer_size < 175 || + p->max_audio_blks < 2 || + p->max_video_blks < 1 || + p->frame_blks < 1 || + p->nb_frames < 1 || + p->preload_count < 1 || + p->buffer_size > 2048 || + p->max_video_blks > 2048 || + p->max_audio_blks > 2048 || + p->nb_frames > INT_MAX / sizeof(uint32_t) || + p->frame_blks > INT_MAX / sizeof(uint32_t)) + return AVERROR_INVALIDDATA; + + p->blocks_count_table = av_mallocz(p->nb_frames * sizeof(uint32_t)); + p->frames_offset_table = av_mallocz(p->nb_frames * sizeof(uint32_t)); + p->blocks_offset_table = av_mallocz(p->frame_blks * sizeof(uint32_t)); + + p->video_size = p->max_video_blks * p->buffer_size; + p->video_frame = av_mallocz(p->video_size); + + p->audio_size = p->max_audio_blks * p->buffer_size; + p->audio_frame = av_mallocz(p->audio_size); + p->temp_audio_frame = av_mallocz(p->audio_size); + + if (!p->blocks_count_table || + !p->frames_offset_table || + !p->blocks_offset_table || + !p->video_frame || + !p->audio_frame || + !p->temp_audio_frame) { + ret = AVERROR(ENOMEM); + goto fail; + } + + avio_seek(pb, p->buffer_size, SEEK_SET); + + read_table(s, p->blocks_count_table, p->nb_frames); + read_table(s, p->frames_offset_table, p->nb_frames); + read_table(s, p->blocks_offset_table, p->frame_blks); + + p->got_audio = 0; + p->current_frame = 0; + p->current_frame_block = 0; + + avio_seek(pb, p->start_offset, SEEK_SET); + + return 0; + +fail: + read_close(s); + + return ret; +} + +static int read_packet(AVFormatContext *s, AVPacket *pkt) +{ + PAFDemuxContext *p = s->priv_data; + AVIOContext *pb = s->pb; + uint32_t count, offset; + int size, i; + + if (p->current_frame >= p->nb_frames) + return AVERROR_EOF; + + if (url_feof(pb)) + return AVERROR_EOF; + + if (p->got_audio) { + if (av_new_packet(pkt, p->audio_size) < 0) + return AVERROR(ENOMEM); + + memcpy(pkt->data, p->temp_audio_frame, p->audio_size); + pkt->duration = PAF_SOUND_SAMPLES * (p->audio_size / PAF_SOUND_FRAME_SIZE); + pkt->flags |= AV_PKT_FLAG_KEY; + pkt->stream_index = 1; + p->got_audio = 0; + return pkt->size; + } + + count = (p->current_frame == 0) ? p->preload_count : p->blocks_count_table[p->current_frame - 1]; + for (i = 0; i < count; i++) { + if (p->current_frame_block >= p->frame_blks) + return AVERROR_INVALIDDATA; + + offset = p->blocks_offset_table[p->current_frame_block] & ~(1U << 31); + if (p->blocks_offset_table[p->current_frame_block] & (1U << 31)) { + if (offset > p->audio_size - p->buffer_size) + return AVERROR_INVALIDDATA; + + avio_read(pb, p->audio_frame + offset, p->buffer_size); + if (offset == (p->max_audio_blks - 2) * p->buffer_size) { + memcpy(p->temp_audio_frame, p->audio_frame, p->audio_size); + p->got_audio = 1; + } + } else { + if (offset > p->video_size - p->buffer_size) + return AVERROR_INVALIDDATA; + + avio_read(pb, p->video_frame + offset, p->buffer_size); + } + p->current_frame_block++; + } + + if (p->frames_offset_table[p->current_frame] >= p->video_size) + return AVERROR_INVALIDDATA; + + size = p->video_size - p->frames_offset_table[p->current_frame]; + + if (av_new_packet(pkt, size) < 0) + return AVERROR(ENOMEM); + + pkt->stream_index = 0; + pkt->duration = 1; + memcpy(pkt->data, p->video_frame + p->frames_offset_table[p->current_frame], size); + if (pkt->data[0] & 0x20) + pkt->flags |= AV_PKT_FLAG_KEY; + p->current_frame++; + + return pkt->size; +} + +AVInputFormat ff_paf_demuxer = { + .name = "paf", + .long_name = NULL_IF_CONFIG_SMALL("Amazing Studio Packed Animation File"), + .priv_data_size = sizeof(PAFDemuxContext), + .read_probe = read_probe, + .read_header = read_header, + .read_packet = read_packet, + .read_close = read_close, +}; diff --git a/libavformat/pcm.c b/libavformat/pcm.c index 892e8ca24e..2fe44dcb40 100644 --- a/libavformat/pcm.c +++ b/libavformat/pcm.c @@ -2,20 +2,20 @@ * PCM common functions * Copyright (c) 2003 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -23,6 +23,26 @@ #include "avformat.h" #include "pcm.h" +#define RAW_SAMPLES 1024 + +int ff_pcm_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + int ret, size; + + size= RAW_SAMPLES*s->streams[0]->codec->block_align; + if (size <= 0) + return AVERROR(EINVAL); + + ret= av_get_packet(s->pb, pkt, size); + + pkt->flags &= ~AV_PKT_FLAG_CORRUPT; + pkt->stream_index = 0; + if (ret < 0) + return ret; + + return ret; +} + int ff_pcm_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp, int flags) { diff --git a/libavformat/pcm.h b/libavformat/pcm.h index 30cbc86ee3..9af36d5a2e 100644 --- a/libavformat/pcm.h +++ b/libavformat/pcm.h @@ -2,20 +2,20 @@ * PCM common functions * Copyright (C) 2007 Aurelien Jacobs <aurel@gnuage.org> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -24,6 +24,7 @@ #include "avformat.h" +int ff_pcm_read_packet(AVFormatContext *s, AVPacket *pkt); int ff_pcm_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp, int flags); diff --git a/libavformat/pcmdec.c b/libavformat/pcmdec.c index 716d8b7977..f4264d91c3 100644 --- a/libavformat/pcmdec.c +++ b/libavformat/pcmdec.c @@ -2,20 +2,20 @@ * RAW PCM demuxers * Copyright (c) 2002 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -24,8 +24,7 @@ #include "pcm.h" #include "libavutil/log.h" #include "libavutil/opt.h" - -#define RAW_SAMPLES 1024 +#include "libavutil/avassert.h" typedef struct PCMAudioDemuxerContext { AVClass *class; @@ -51,7 +50,7 @@ static int pcm_read_header(AVFormatContext *s) st->codec->bits_per_coded_sample = av_get_bits_per_sample(st->codec->codec_id); - assert(st->codec->bits_per_coded_sample > 0); + av_assert0(st->codec->bits_per_coded_sample > 0); st->codec->block_align = st->codec->bits_per_coded_sample * st->codec->channels / 8; @@ -60,29 +59,8 @@ static int pcm_read_header(AVFormatContext *s) return 0; } -static int pcm_read_packet(AVFormatContext *s, AVPacket *pkt) -{ - int ret, size, bps; - // AVStream *st = s->streams[0]; - - size= RAW_SAMPLES*s->streams[0]->codec->block_align; - - ret= av_get_packet(s->pb, pkt, size); - - pkt->stream_index = 0; - if (ret < 0) - return ret; - - bps= av_get_bits_per_sample(s->streams[0]->codec->codec_id); - assert(bps); // if false there IS a bug elsewhere (NOT in this function) - pkt->dts= - pkt->pts= pkt->pos*8 / (bps * s->streams[0]->codec->channels); - - return ret; -} - static const AVOption pcm_options[] = { - { "sample_rate", "", offsetof(PCMAudioDemuxerContext, sample_rate), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, AV_OPT_FLAG_DECODING_PARAM }, + { "sample_rate", "", offsetof(PCMAudioDemuxerContext, sample_rate), AV_OPT_TYPE_INT, {.i64 = 44100}, 0, INT_MAX, AV_OPT_FLAG_DECODING_PARAM }, { "channels", "", offsetof(PCMAudioDemuxerContext, channels), AV_OPT_TYPE_INT, {.i64 = 1}, 0, INT_MAX, AV_OPT_FLAG_DECODING_PARAM }, { NULL }, }; @@ -99,7 +77,7 @@ AVInputFormat ff_pcm_ ## name_ ## _demuxer = { \ .long_name = NULL_IF_CONFIG_SMALL(long_name_), \ .priv_data_size = sizeof(PCMAudioDemuxerContext), \ .read_header = pcm_read_header, \ - .read_packet = pcm_read_packet, \ + .read_packet = ff_pcm_read_packet, \ .read_seek = ff_pcm_read_seek, \ .flags = AVFMT_GENERIC_INDEX, \ .extensions = ext, \ diff --git a/libavformat/pcmenc.c b/libavformat/pcmenc.c index e45ab8412a..3e4f308057 100644 --- a/libavformat/pcmenc.c +++ b/libavformat/pcmenc.c @@ -2,20 +2,20 @@ * RAW PCM muxers * Copyright (c) 2002 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/pjsdec.c b/libavformat/pjsdec.c new file mode 100644 index 0000000000..a69a31660d --- /dev/null +++ b/libavformat/pjsdec.c @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2012 Clément BÅ“sch + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * PJS (Phoenix Japanimation Society) subtitles format demuxer + * + * @see http://subs.com.ru/page.php?al=pjs + */ + +#include "avformat.h" +#include "internal.h" +#include "subtitles.h" + +typedef struct { + FFDemuxSubtitlesQueue q; +} PJSContext; + +static int pjs_probe(AVProbeData *p) +{ + char c; + int64_t start, end; + const unsigned char *ptr = p->buf; + + if (sscanf(ptr, "%"SCNd64",%"SCNd64",%c", &start, &end, &c) == 3) { + size_t q1pos = strcspn(ptr, "\""); + size_t q2pos = q1pos + strcspn(ptr + q1pos + 1, "\"") + 1; + if (strcspn(ptr, "\r\n") > q2pos) + return AVPROBE_SCORE_MAX; + } + return 0; +} + +static int64_t read_ts(char **line, int *duration) +{ + int64_t start, end; + + if (sscanf(*line, "%"SCNd64",%"SCNd64, &start, &end) == 2) { + *line += strcspn(*line, "\"") + 1; + *duration = end - start; + return start; + } + return AV_NOPTS_VALUE; +} + +static int pjs_read_header(AVFormatContext *s) +{ + PJSContext *pjs = s->priv_data; + AVStream *st = avformat_new_stream(s, NULL); + int res = 0; + + if (!st) + return AVERROR(ENOMEM); + avpriv_set_pts_info(st, 64, 1, 10); + st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE; + st->codec->codec_id = AV_CODEC_ID_PJS; + + while (!url_feof(s->pb)) { + char line[4096]; + char *p = line; + const int64_t pos = avio_tell(s->pb); + int len = ff_get_line(s->pb, line, sizeof(line)); + int64_t pts_start; + int duration; + + if (!len) + break; + + line[strcspn(line, "\r\n")] = 0; + + pts_start = read_ts(&p, &duration); + if (pts_start != AV_NOPTS_VALUE) { + AVPacket *sub; + + p[strcspn(p, "\"")] = 0; + sub = ff_subtitles_queue_insert(&pjs->q, p, strlen(p), 0); + if (!sub) + return AVERROR(ENOMEM); + sub->pos = pos; + sub->pts = pts_start; + sub->duration = duration; + } + } + + ff_subtitles_queue_finalize(&pjs->q); + return res; +} + +static int pjs_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + PJSContext *pjs = s->priv_data; + return ff_subtitles_queue_read_packet(&pjs->q, pkt); +} + +static int pjs_read_seek(AVFormatContext *s, int stream_index, + int64_t min_ts, int64_t ts, int64_t max_ts, int flags) +{ + PJSContext *pjs = s->priv_data; + return ff_subtitles_queue_seek(&pjs->q, s, stream_index, + min_ts, ts, max_ts, flags); +} + +static int pjs_read_close(AVFormatContext *s) +{ + PJSContext *pjs = s->priv_data; + ff_subtitles_queue_clean(&pjs->q); + return 0; +} + +AVInputFormat ff_pjs_demuxer = { + .name = "pjs", + .long_name = NULL_IF_CONFIG_SMALL("PJS (Phoenix Japanimation Society) subtitles"), + .priv_data_size = sizeof(PJSContext), + .read_probe = pjs_probe, + .read_header = pjs_read_header, + .read_packet = pjs_read_packet, + .read_seek2 = pjs_read_seek, + .read_close = pjs_read_close, + .extensions = "pjs", +}; diff --git a/libavformat/pmpdec.c b/libavformat/pmpdec.c index 484be43ce9..71f450e9d3 100644 --- a/libavformat/pmpdec.c +++ b/libavformat/pmpdec.c @@ -1,21 +1,21 @@ /* - * PMP demuxer + * PMP demuxer. * Copyright (c) 2011 Reimar Döffinger * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -23,18 +23,18 @@ #include "avformat.h" #include "internal.h" -typedef struct PMPContext { - int cur_stream; - int num_streams; - int audio_packets; - int current_packet; +typedef struct { + int cur_stream; + int num_streams; + int audio_packets; + int current_packet; uint32_t *packet_sizes; - int packet_sizes_alloc; + int packet_sizes_alloc; } PMPContext; -static int pmp_probe(AVProbeData *p) -{ - if (!memcmp(p->buf, "pmpm\1\0\0\0", 8)) +static int pmp_probe(AVProbeData *p) { + if (AV_RN32(p->buf) == AV_RN32("pmpm") && + AV_RL32(p->buf + 4) == 1) return AVPROBE_SCORE_MAX; return 0; } @@ -44,11 +44,13 @@ static int pmp_header(AVFormatContext *s) PMPContext *pmp = s->priv_data; AVIOContext *pb = s->pb; int tb_num, tb_den; - int index_cnt; + uint32_t index_cnt; int audio_codec_id = AV_CODEC_ID_NONE; int srate, channels; - int i; + unsigned i; uint64_t pos; + int64_t fsize = avio_size(pb); + AVStream *vst = avformat_new_stream(s, NULL); if (!vst) return AVERROR(ENOMEM); @@ -65,7 +67,7 @@ static int pmp_header(AVFormatContext *s) av_log(s, AV_LOG_ERROR, "Unsupported video format\n"); break; } - index_cnt = avio_rl32(pb); + index_cnt = avio_rl32(pb); vst->codec->width = avio_rl32(pb); vst->codec->height = avio_rl32(pb); @@ -73,14 +75,14 @@ static int pmp_header(AVFormatContext *s) tb_den = avio_rl32(pb); avpriv_set_pts_info(vst, 32, tb_num, tb_den); vst->nb_frames = index_cnt; - vst->duration = index_cnt; + vst->duration = index_cnt; switch (avio_rl32(pb)) { case 0: audio_codec_id = AV_CODEC_ID_MP3; break; case 1: - av_log(s, AV_LOG_WARNING, "AAC is not yet correctly supported\n"); + av_log(s, AV_LOG_ERROR, "AAC not yet correctly supported\n"); audio_codec_id = AV_CODEC_ID_AAC; break; default: @@ -89,26 +91,38 @@ static int pmp_header(AVFormatContext *s) } pmp->num_streams = avio_rl16(pb) + 1; avio_skip(pb, 10); - srate = avio_rl32(pb); + srate = avio_rl32(pb); channels = avio_rl32(pb) + 1; + pos = avio_tell(pb) + 4LL*index_cnt; + for (i = 0; i < index_cnt; i++) { + uint32_t size = avio_rl32(pb); + int flags = size & 1 ? AVINDEX_KEYFRAME : 0; + if (url_feof(pb)) { + av_log(s, AV_LOG_FATAL, "Encountered EOF while reading index.\n"); + return AVERROR_INVALIDDATA; + } + size >>= 1; + if (size < 9 + 4*pmp->num_streams) { + av_log(s, AV_LOG_ERROR, "Packet too small\n"); + return AVERROR_INVALIDDATA; + } + av_add_index_entry(vst, pos, i, size, 0, flags); + pos += size; + if (fsize > 0 && i == 0 && pos > fsize) { + av_log(s, AV_LOG_ERROR, "File ends before first packet\n"); + return AVERROR_INVALIDDATA; + } + } for (i = 1; i < pmp->num_streams; i++) { AVStream *ast = avformat_new_stream(s, NULL); if (!ast) return AVERROR(ENOMEM); - ast->codec->codec_type = AVMEDIA_TYPE_AUDIO; - ast->codec->codec_id = audio_codec_id; - ast->codec->channels = channels; + ast->codec->codec_type = AVMEDIA_TYPE_AUDIO; + ast->codec->codec_id = audio_codec_id; + ast->codec->channels = channels; ast->codec->sample_rate = srate; avpriv_set_pts_info(ast, 32, 1, srate); } - pos = avio_tell(pb) + 4 * index_cnt; - for (i = 0; i < index_cnt; i++) { - int size = avio_rl32(pb); - int flags = size & 1 ? AVINDEX_KEYFRAME : 0; - size >>= 1; - av_add_index_entry(vst, pos, i, size, 0, flags); - pos += size; - } return 0; } @@ -119,11 +133,15 @@ static int pmp_packet(AVFormatContext *s, AVPacket *pkt) int ret = 0; int i; - if (pb->eof_reached) + if (url_feof(pb)) return AVERROR_EOF; if (pmp->cur_stream == 0) { int num_packets; pmp->audio_packets = avio_r8(pb); + if (!pmp->audio_packets) { + avpriv_request_sample(s, "0 audio packets"); + return AVERROR_PATCHWELCOME; + } num_packets = (pmp->num_streams - 1) * pmp->audio_packets + 1; avio_skip(pb, 8); pmp->current_packet = 0; @@ -138,7 +156,7 @@ static int pmp_packet(AVFormatContext *s, AVPacket *pkt) pmp->packet_sizes[i] = avio_rl32(pb); } ret = av_get_packet(pb, pkt, pmp->packet_sizes[pmp->current_packet]); - if (ret > 0) { + if (ret >= 0) { ret = 0; // FIXME: this is a hack that should be removed once // compute_pkt_fields() can handle timestamps properly @@ -146,14 +164,13 @@ static int pmp_packet(AVFormatContext *s, AVPacket *pkt) pkt->dts = s->streams[0]->cur_dts++; pkt->stream_index = pmp->cur_stream; } - pmp->current_packet++; - if (pmp->current_packet == 1 || pmp->current_packet > pmp->audio_packets) + if (pmp->current_packet % pmp->audio_packets == 0) pmp->cur_stream = (pmp->cur_stream + 1) % pmp->num_streams; - + pmp->current_packet++; return ret; } -static int pmp_seek(AVFormatContext *s, int stream_idx, int64_t ts, int flags) +static int pmp_seek(AVFormatContext *s, int stream_index, int64_t ts, int flags) { PMPContext *pmp = s->priv_data; pmp->cur_stream = 0; diff --git a/libavformat/psxstr.c b/libavformat/psxstr.c index e662ed788d..3409d6a5af 100644 --- a/libavformat/psxstr.c +++ b/libavformat/psxstr.c @@ -2,20 +2,20 @@ * Sony Playstation (PSX) STR File Demuxer * Copyright (c) 2003 The ffmpeg Project * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -66,11 +66,13 @@ typedef struct StrDemuxContext { StrChannel channels[32]; } StrDemuxContext; -static const char sync_header[12] = {0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00}; +static const uint8_t sync_header[12] = {0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00}; static int str_probe(AVProbeData *p) { - uint8_t *sector= p->buf; + const uint8_t *sector= p->buf; + const uint8_t *end= sector + p->buf_size; + int aud=0, vid=0; if (p->buf_size < RAW_CD_SECTOR_SIZE) return 0; @@ -82,20 +84,52 @@ static int str_probe(AVProbeData *p) sector += RIFF_HEADER_SIZE; } - /* look for CD sync header (00, 0xFF x 10, 00) */ - if (memcmp(sector,sync_header,sizeof(sync_header))) - return 0; + while (end - sector >= RAW_CD_SECTOR_SIZE) { + /* look for CD sync header (00, 0xFF x 10, 00) */ + if (memcmp(sector,sync_header,sizeof(sync_header))) + return 0; - if(sector[0x11] >= 32) - return 0; - if( (sector[0x12] & CDXA_TYPE_MASK) != CDXA_TYPE_VIDEO - && (sector[0x12] & CDXA_TYPE_MASK) != CDXA_TYPE_AUDIO - && (sector[0x12] & CDXA_TYPE_MASK) != CDXA_TYPE_DATA) - return 0; + if (sector[0x11] >= 32) + return 0; + + switch (sector[0x12] & CDXA_TYPE_MASK) { + case CDXA_TYPE_DATA: + case CDXA_TYPE_VIDEO: { + int current_sector = AV_RL16(§or[0x1C]); + int sector_count = AV_RL16(§or[0x1E]); + int frame_size = AV_RL32(§or[0x24]); + + if(!( frame_size>=0 + && current_sector < sector_count + && sector_count*VIDEO_DATA_CHUNK_SIZE >=frame_size)){ + return 0; + } + /*st->codec->width = AV_RL16(§or[0x28]); + st->codec->height = AV_RL16(§or[0x2A]);*/ + +// if (current_sector == sector_count-1) { + vid++; +// } + + } + break; + case CDXA_TYPE_AUDIO: + if(sector[0x13]&0x2A) + return 0; + aud++; + break; + default: + if(sector[0x12] & CDXA_TYPE_MASK) + return 0; + } + sector += RAW_CD_SECTOR_SIZE; + } /* MPEG files (like those ripped from VCDs) can also look like this; * only return half certainty */ - return AVPROBE_SCORE_EXTENSION; + if(vid+aud > 3) return AVPROBE_SCORE_EXTENSION; + else if(vid+aud) return 1; + else return 0; } static int str_read_header(AVFormatContext *s) @@ -257,7 +291,7 @@ FF_ENABLE_DEPRECATION_WARNINGS break; } - if (pb->eof_reached) + if (url_feof(pb)) return AVERROR(EIO); } } diff --git a/libavformat/pva.c b/libavformat/pva.c index 3abfc18508..9b7a40a068 100644 --- a/libavformat/pva.c +++ b/libavformat/pva.c @@ -2,20 +2,20 @@ * TechnoTrend PVA (.pva) demuxer * Copyright (c) 2007, 2008 Ivo van Poorten * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -32,13 +32,26 @@ typedef struct { int continue_pes; } PVAContext; +static int pva_check(const uint8_t *p) { + int length = AV_RB16(p + 6); + if (AV_RB16(p) != PVA_MAGIC || !p[2] || p[2] > 2 || p[4] != 0x55 || + (p[5] & 0xe0) || length > PVA_MAX_PAYLOAD_LENGTH) + return -1; + return length + 8; +} + static int pva_probe(AVProbeData * pd) { - unsigned char *buf = pd->buf; + const unsigned char *buf = pd->buf; + int len = pva_check(buf); - if (AV_RB16(buf) == PVA_MAGIC && buf[2] && buf[2] < 3 && buf[4] == 0x55) + if (len < 0) + return 0; + + if (pd->buf_size >= len + 8 && + pva_check(buf + len) >= 0) return AVPROBE_SCORE_EXTENSION; - return 0; + return AVPROBE_SCORE_MAX / 4; } static int pva_read_header(AVFormatContext *s) { diff --git a/libavformat/pvfdec.c b/libavformat/pvfdec.c new file mode 100644 index 0000000000..c678046156 --- /dev/null +++ b/libavformat/pvfdec.c @@ -0,0 +1,75 @@ +/* + * PVF demuxer + * Copyright (c) 2012 Paul B Mahol + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "avformat.h" +#include "internal.h" +#include "pcm.h" + +static int pvf_probe(AVProbeData *p) +{ + if (!memcmp(p->buf, "PVF1\n", 5)) + return AVPROBE_SCORE_MAX; + return 0; +} + +static int pvf_read_header(AVFormatContext *s) +{ + char buffer[32]; + AVStream *st; + int bps, channels, sample_rate; + + avio_skip(s->pb, 5); + ff_get_line(s->pb, buffer, sizeof(buffer)); + if (sscanf(buffer, "%d %d %d", + &channels, + &sample_rate, + &bps) != 3) + return AVERROR_INVALIDDATA; + + if (channels <= 0 || bps <= 0 || sample_rate <= 0) + return AVERROR_INVALIDDATA; + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->channels = channels; + st->codec->sample_rate = sample_rate; + st->codec->codec_id = ff_get_pcm_codec_id(bps, 0, 1, 0xFFFF); + st->codec->bits_per_coded_sample = bps; + st->codec->block_align = bps * st->codec->channels / 8; + + avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate); + + return 0; +} + +AVInputFormat ff_pvf_demuxer = { + .name = "pvf", + .long_name = NULL_IF_CONFIG_SMALL("PVF (Portable Voice Format)"), + .read_probe = pvf_probe, + .read_header = pvf_read_header, + .read_packet = ff_pcm_read_packet, + .read_seek = ff_pcm_read_seek, + .extensions = "pvf", + .flags = AVFMT_GENERIC_INDEX, +}; diff --git a/libavformat/qcp.c b/libavformat/qcp.c index 30d362f938..1ef86fe34d 100644 --- a/libavformat/qcp.c +++ b/libavformat/qcp.c @@ -2,20 +2,20 @@ * QCP format (.qcp) demuxer * Copyright (c) 2009 Kenan Gillet * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -102,11 +102,9 @@ static int qcp_read_header(AVFormatContext *s) if (is_qcelp_13k_guid(buf)) { st->codec->codec_id = AV_CODEC_ID_QCELP; } else if (!memcmp(buf, guid_evrc, 16)) { - av_log(s, AV_LOG_ERROR, "EVRC codec is not supported.\n"); - return AVERROR_PATCHWELCOME; + st->codec->codec_id = AV_CODEC_ID_EVRC; } else if (!memcmp(buf, guid_smv, 16)) { - av_log(s, AV_LOG_ERROR, "SMV codec is not supported.\n"); - return AVERROR_PATCHWELCOME; + st->codec->codec_id = AV_CODEC_ID_SMV; } else { av_log(s, AV_LOG_ERROR, "Unknown codec GUID.\n"); return AVERROR_INVALIDDATA; @@ -141,7 +139,7 @@ static int qcp_read_packet(AVFormatContext *s, AVPacket *pkt) QCPContext *c = s->priv_data; unsigned int chunk_size, tag; - while(!pb->eof_reached) { + while(!url_feof(pb)) { if (c->data_size) { int pkt_size, ret, mode = avio_r8(pb); diff --git a/libavformat/qtpalette.h b/libavformat/qtpalette.h index ecc85d3408..7d6802f73c 100644 --- a/libavformat/qtpalette.h +++ b/libavformat/qtpalette.h @@ -3,20 +3,20 @@ * Automatically generated from a utility derived from XAnim: * http://xanim.va.pubnix.com/home.html * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/r3d.c b/libavformat/r3d.c index 74a1f2baa2..0719fb601a 100644 --- a/libavformat/r3d.c +++ b/libavformat/r3d.c @@ -2,20 +2,20 @@ * R3D REDCODE demuxer * Copyright (c) 2008 Baptiste Coudurier <baptiste dot coudurier at gmail dot com> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -86,6 +86,9 @@ static int r3d_read_red1(AVFormatContext *s) framerate.num = avio_rb16(s->pb); framerate.den = avio_rb16(s->pb); if (framerate.num > 0 && framerate.den > 0) { +#if FF_API_R_FRAME_RATE + st->r_frame_rate = +#endif st->avg_frame_rate = framerate; } @@ -136,8 +139,7 @@ static int r3d_read_rdvo(AVFormatContext *s, Atom *atom) if (st->avg_frame_rate.num) st->duration = av_rescale_q(r3d->video_offsets_count, - (AVRational){st->avg_frame_rate.den, - st->avg_frame_rate.num}, + av_inv_q(st->avg_frame_rate), st->time_base); av_dlog(s, "duration %"PRId64"\n", st->duration); @@ -312,7 +314,8 @@ static int r3d_read_reda(AVFormatContext *s, AVPacket *pkt, Atom *atom) pkt->stream_index = 1; pkt->dts = dts; - pkt->duration = av_rescale(samples, st->time_base.den, st->codec->sample_rate); + if (st->codec->sample_rate) + pkt->duration = av_rescale(samples, st->time_base.den, st->codec->sample_rate); av_dlog(s, "pkt dts %"PRId64" duration %d samples %d sample rate %d\n", pkt->dts, pkt->duration, samples, st->codec->sample_rate); @@ -369,7 +372,7 @@ static int r3d_seek(AVFormatContext *s, int stream_index, int64_t sample_time, i return -1; frame_num = av_rescale_q(sample_time, st->time_base, - (AVRational){st->avg_frame_rate.den, st->avg_frame_rate.num}); + av_inv_q(st->avg_frame_rate)); av_dlog(s, "seek frame num %d timestamp %"PRId64"\n", frame_num, sample_time); diff --git a/libavformat/rawdec.c b/libavformat/rawdec.c index 41e1700b8b..a9ff22ae18 100644 --- a/libavformat/rawdec.c +++ b/libavformat/rawdec.c @@ -3,20 +3,20 @@ * Copyright (c) 2001 Fabrice Bellard * Copyright (c) 2005 Alex Beregszaszi * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -27,6 +27,7 @@ #include "libavutil/opt.h" #include "libavutil/parseutils.h" #include "libavutil/pixdesc.h" +#include "libavutil/avassert.h" #define RAW_PACKET_SIZE 1024 @@ -45,12 +46,8 @@ int ff_raw_read_partial_packet(AVFormatContext *s, AVPacket *pkt) if (ret < 0) { av_free_packet(pkt); return ret; - } else if (ret < size) { - /* initialize end of packet for partial reads to avoid reading - * uninitialized data on allowed overreads */ - memset(pkt->data + ret, 0, FF_INPUT_BUFFER_PADDING_SIZE); } - pkt->size = ret; + av_shrink_packet(pkt, ret); return ret; } @@ -61,7 +58,7 @@ int ff_raw_audio_read_header(AVFormatContext *s) return AVERROR(ENOMEM); st->codec->codec_type = AVMEDIA_TYPE_AUDIO; st->codec->codec_id = s->iformat->raw_codec_id; - st->need_parsing = AVSTREAM_PARSE_FULL; + st->need_parsing = AVSTREAM_PARSE_FULL_RAW; st->start_time = 0; /* the parameters will be extracted from the compressed bitstream */ @@ -73,7 +70,6 @@ int ff_raw_video_read_header(AVFormatContext *s) { AVStream *st; FFRawVideoDemuxerContext *s1 = s->priv_data; - AVRational framerate; int ret = 0; @@ -85,29 +81,45 @@ int ff_raw_video_read_header(AVFormatContext *s) st->codec->codec_type = AVMEDIA_TYPE_VIDEO; st->codec->codec_id = s->iformat->raw_codec_id; - st->need_parsing = AVSTREAM_PARSE_FULL; + st->need_parsing = AVSTREAM_PARSE_FULL_RAW; - if ((ret = av_parse_video_rate(&framerate, s1->framerate)) < 0) { - av_log(s, AV_LOG_ERROR, "Could not parse framerate: %s.\n", s1->framerate); - goto fail; - } - - st->avg_frame_rate = framerate; - avpriv_set_pts_info(st, 64, framerate.den, framerate.num); + st->codec->time_base = av_inv_q(s1->framerate); + avpriv_set_pts_info(st, 64, 1, 1200000); fail: return ret; } +static int ff_raw_data_read_header(AVFormatContext *s) +{ + AVStream *st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + st->codec->codec_type = AVMEDIA_TYPE_DATA; + st->codec->codec_id = s->iformat->raw_codec_id; + st->start_time = 0; + return 0; +} + /* Note: Do not forget to add new entries to the Makefile as well. */ #define OFFSET(x) offsetof(FFRawVideoDemuxerContext, x) #define DEC AV_OPT_FLAG_DECODING_PARAM const AVOption ff_rawvideo_options[] = { - { "framerate", "", OFFSET(framerate), AV_OPT_TYPE_STRING, {.str = "25"}, 0, 0, DEC}, + { "framerate", "", OFFSET(framerate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, 0, DEC}, { NULL }, }; +#if CONFIG_DATA_DEMUXER +AVInputFormat ff_data_demuxer = { + .name = "data", + .long_name = NULL_IF_CONFIG_SMALL("raw data"), + .read_header = ff_raw_data_read_header, + .read_packet = ff_raw_read_partial_packet, + .raw_codec_id = AV_CODEC_ID_NONE, +}; +#endif + #if CONFIG_LATM_DEMUXER AVInputFormat ff_latm_demuxer = { .name = "latm", @@ -121,7 +133,7 @@ AVInputFormat ff_latm_demuxer = { #endif #if CONFIG_MJPEG_DEMUXER -FF_DEF_RAWVIDEO_DEMUXER(mjpeg, "raw MJPEG video", NULL, "mjpg,mjpeg", AV_CODEC_ID_MJPEG) +FF_DEF_RAWVIDEO_DEMUXER(mjpeg, "raw MJPEG video", NULL, "mjpg,mjpeg,mpo", AV_CODEC_ID_MJPEG) #endif #if CONFIG_MLP_DEMUXER diff --git a/libavformat/rawdec.h b/libavformat/rawdec.h index a5487784c9..59108559d3 100644 --- a/libavformat/rawdec.h +++ b/libavformat/rawdec.h @@ -2,20 +2,20 @@ * RAW demuxers * Copyright (C) 2007 Aurelien Jacobs <aurel@gnuage.org> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -30,7 +30,7 @@ typedef struct FFRawVideoDemuxerContext { const AVClass *class; /**< Class for private options. */ char *video_size; /**< String describing video size, set by a private option. */ char *pixel_format; /**< Set by a private option. */ - char *framerate; /**< String describing framerate, set by a private option. */ + AVRational framerate; /**< AVRational describing framerate, set by a private option. */ } FFRawVideoDemuxerContext; extern const AVOption ff_rawvideo_options[]; diff --git a/libavformat/rawenc.c b/libavformat/rawenc.c index 852a27d6e4..7a1fa93e8b 100644 --- a/libavformat/rawenc.c +++ b/libavformat/rawenc.c @@ -3,20 +3,20 @@ * Copyright (c) 2001 Fabrice Bellard * Copyright (c) 2005 Alex Beregszaszi * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -68,6 +68,15 @@ AVOutputFormat ff_cavsvideo_muxer = { }; #endif +#if CONFIG_DATA_MUXER +AVOutputFormat ff_data_muxer = { + .name = "data", + .long_name = NULL_IF_CONFIG_SMALL("raw data"), + .write_packet = ff_raw_write_packet, + .flags = AVFMT_NOTIMESTAMPS, +}; +#endif + #if CONFIG_DIRAC_MUXER AVOutputFormat ff_dirac_muxer = { .name = "dirac", @@ -131,6 +140,19 @@ AVOutputFormat ff_g722_muxer = { }; #endif +#if CONFIG_G723_1_MUXER +AVOutputFormat ff_g723_1_muxer = { + .name = "g723_1", + .long_name = NULL_IF_CONFIG_SMALL("raw G.723.1"), + .mime_type = "audio/g723", + .extensions = "tco,rco", + .audio_codec = AV_CODEC_ID_G723_1, + .video_codec = AV_CODEC_ID_NONE, + .write_packet = ff_raw_write_packet, + .flags = AVFMT_NOTIMESTAMPS, +}; +#endif + #if CONFIG_H261_MUXER AVOutputFormat ff_h261_muxer = { .name = "h261", @@ -243,18 +265,6 @@ AVOutputFormat ff_rawvideo_muxer = { }; #endif -#if CONFIG_SRT_MUXER -AVOutputFormat ff_srt_muxer = { - .name = "srt", - .long_name = NULL_IF_CONFIG_SMALL("SubRip subtitle"), - .mime_type = "application/x-subrip", - .extensions = "srt", - .write_packet = ff_raw_write_packet, - .flags = AVFMT_NOTIMESTAMPS, - .subtitle_codec = AV_CODEC_ID_SRT, -}; -#endif - #if CONFIG_TRUEHD_MUXER AVOutputFormat ff_truehd_muxer = { .name = "truehd", @@ -266,3 +276,15 @@ AVOutputFormat ff_truehd_muxer = { .flags = AVFMT_NOTIMESTAMPS, }; #endif + +#if CONFIG_VC1_MUXER +AVOutputFormat ff_vc1_muxer = { + .name = "vc1", + .long_name = NULL_IF_CONFIG_SMALL("raw VC-1 video"), + .extensions = "vc1", + .audio_codec = AV_CODEC_ID_NONE, + .video_codec = AV_CODEC_ID_VC1, + .write_packet = ff_raw_write_packet, + .flags = AVFMT_NOTIMESTAMPS, +}; +#endif diff --git a/libavformat/rawenc.h b/libavformat/rawenc.h index daa5489da8..b5523090b8 100644 --- a/libavformat/rawenc.h +++ b/libavformat/rawenc.h @@ -2,20 +2,20 @@ * RAW muxers * Copyright (C) 2007 Aurelien Jacobs <aurel@gnuage.org> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/rawvideodec.c b/libavformat/rawvideodec.c index 5f372c9a2c..cbcae43c72 100644 --- a/libavformat/rawvideodec.c +++ b/libavformat/rawvideodec.c @@ -2,20 +2,20 @@ * RAW video demuxer * Copyright (c) 2001 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -27,18 +27,16 @@ typedef struct RawVideoDemuxerContext { const AVClass *class; /**< Class for private options. */ - char *video_size; /**< String describing video size, set by a private option. */ + int width, height; /**< Integers describing video size, set by a private option. */ char *pixel_format; /**< Set by a private option. */ - char *framerate; /**< String describing framerate, set by a private option. */ + AVRational framerate; /**< AVRational describing framerate, set by a private option. */ } RawVideoDemuxerContext; static int rawvideo_read_header(AVFormatContext *ctx) { RawVideoDemuxerContext *s = ctx->priv_data; - int width = 0, height = 0, ret = 0; enum AVPixelFormat pix_fmt; - AVRational framerate; AVStream *st; st = avformat_new_stream(ctx, NULL); @@ -49,29 +47,19 @@ static int rawvideo_read_header(AVFormatContext *ctx) st->codec->codec_id = ctx->iformat->raw_codec_id; - if (s->video_size && - (ret = av_parse_video_size(&width, &height, s->video_size)) < 0) { - av_log(ctx, AV_LOG_ERROR, "Couldn't parse video size.\n"); - return ret; - } - if ((pix_fmt = av_get_pix_fmt(s->pixel_format)) == AV_PIX_FMT_NONE) { av_log(ctx, AV_LOG_ERROR, "No such pixel format: %s.\n", s->pixel_format); return AVERROR(EINVAL); } - if ((ret = av_parse_video_rate(&framerate, s->framerate)) < 0) { - av_log(ctx, AV_LOG_ERROR, "Could not parse framerate: %s.\n", - s->framerate); - return ret; - } - - avpriv_set_pts_info(st, 64, framerate.den, framerate.num); + avpriv_set_pts_info(st, 64, s->framerate.den, s->framerate.num); - st->codec->width = width; - st->codec->height = height; + st->codec->width = s->width; + st->codec->height = s->height; st->codec->pix_fmt = pix_fmt; + st->codec->bit_rate = av_rescale_q(avpicture_get_size(st->codec->pix_fmt, s->width, s->height), + (AVRational){8,1}, st->time_base); return 0; } @@ -101,9 +89,9 @@ static int rawvideo_read_packet(AVFormatContext *s, AVPacket *pkt) #define OFFSET(x) offsetof(RawVideoDemuxerContext, x) #define DEC AV_OPT_FLAG_DECODING_PARAM static const AVOption rawvideo_options[] = { - { "video_size", "A string describing frame size, such as 640x480 or hd720.", OFFSET(video_size), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, DEC }, - { "pixel_format", "", OFFSET(pixel_format), AV_OPT_TYPE_STRING, {.str = "yuv420p"}, 0, 0, DEC }, - { "framerate", "", OFFSET(framerate), AV_OPT_TYPE_STRING, {.str = "25"}, 0, 0, DEC }, + { "video_size", "set frame size", OFFSET(width), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, DEC }, + { "pixel_format", "set pixel format", OFFSET(pixel_format), AV_OPT_TYPE_STRING, {.str = "yuv420p"}, 0, 0, DEC }, + { "framerate", "set frame rate", OFFSET(framerate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, 0, DEC }, { NULL }, }; diff --git a/libavformat/rdt.c b/libavformat/rdt.c index a90c16849c..201a3e0464 100644 --- a/libavformat/rdt.c +++ b/libavformat/rdt.c @@ -2,20 +2,20 @@ * Realmedia RTSP protocol (RDT) support. * Copyright (c) 2007 Ronald S. Bultje * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -98,7 +98,7 @@ ff_rdt_calc_response_and_checksum(char response[41], char chksum[9], unsigned char zres[16], buf[64] = { 0xa1, 0xe9, 0x14, 0x9d, 0x0e, 0x6b, 0x3b, 0x59 }; #define XOR_TABLE_SIZE 37 - const unsigned char xor_table[XOR_TABLE_SIZE] = { + static const unsigned char xor_table[XOR_TABLE_SIZE] = { 0x05, 0x18, 0x74, 0xd0, 0x0d, 0x09, 0x02, 0x53, 0xc0, 0x01, 0x05, 0x05, 0x67, 0x03, 0x19, 0x70, 0x08, 0x27, 0x66, 0x10, 0x10, 0x72, 0x08, 0x09, @@ -176,7 +176,7 @@ rdt_load_mdpr (PayloadContext *rdt, AVStream *st, int rule_nr) size = rdt->mlti_data_size; avio_seek(&pb, 0, SEEK_SET); } - if (ff_rm_read_mdpr_codecdata(rdt->rmctx, &pb, st, rdt->rmst[st->index], size) < 0) + if (ff_rm_read_mdpr_codecdata(rdt->rmctx, &pb, st, rdt->rmst[st->index], size, NULL) < 0) return -1; return 0; diff --git a/libavformat/rdt.h b/libavformat/rdt.h index bd168903f5..ce6026f49c 100644 --- a/libavformat/rdt.h +++ b/libavformat/rdt.h @@ -2,20 +2,20 @@ * Realmedia RTSP (RDT) definitions * Copyright (c) 2007 Ronald S. Bultje <rbultje@ronald.bitfreak.net> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/realtextdec.c b/libavformat/realtextdec.c new file mode 100644 index 0000000000..5e4981a40b --- /dev/null +++ b/libavformat/realtextdec.c @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2012 Clément BÅ“sch + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * RealText subtitle demuxer + * @see http://service.real.com/help/library/guides/ProductionGuide/prodguide/htmfiles/realtext.htm + */ + +#include "avformat.h" +#include "internal.h" +#include "subtitles.h" +#include "libavutil/avstring.h" +#include "libavutil/bprint.h" +#include "libavutil/intreadwrite.h" + +typedef struct { + FFDemuxSubtitlesQueue q; +} RealTextContext; + +static int realtext_probe(AVProbeData *p) +{ + const unsigned char *ptr = p->buf; + + if (AV_RB24(ptr) == 0xEFBBBF) + ptr += 3; /* skip UTF-8 BOM */ + return !av_strncasecmp(ptr, "<window", 7) ? AVPROBE_SCORE_EXTENSION : 0; +} + +static int read_ts(const char *s) +{ + int hh, mm, ss, ms; + + if (sscanf(s, "%u:%u:%u.%u", &hh, &mm, &ss, &ms) == 4) return (hh*3600 + mm*60 + ss) * 100 + ms; + if (sscanf(s, "%u:%u:%u" , &hh, &mm, &ss ) == 3) return (hh*3600 + mm*60 + ss) * 100; + if (sscanf(s, "%u:%u.%u", &mm, &ss, &ms) == 3) return ( mm*60 + ss) * 100 + ms; + if (sscanf(s, "%u:%u" , &mm, &ss ) == 2) return ( mm*60 + ss) * 100; + if (sscanf(s, "%u.%u", &ss, &ms) == 2) return ( ss) * 100 + ms; + return strtol(s, NULL, 10) * 100; +} + +static int realtext_read_header(AVFormatContext *s) +{ + RealTextContext *rt = s->priv_data; + AVStream *st = avformat_new_stream(s, NULL); + AVBPrint buf; + char c = 0; + int res = 0, duration = read_ts("60"); // default duration is 60 seconds + + if (!st) + return AVERROR(ENOMEM); + avpriv_set_pts_info(st, 64, 1, 100); + st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE; + st->codec->codec_id = AV_CODEC_ID_REALTEXT; + + av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED); + + while (!url_feof(s->pb)) { + AVPacket *sub; + const int64_t pos = avio_tell(s->pb) - (c != 0); + int n = ff_smil_extract_next_chunk(s->pb, &buf, &c); + + if (n == 0) + break; + + if (!av_strncasecmp(buf.str, "<window", 7)) { + /* save header to extradata */ + const char *p = ff_smil_get_attr_ptr(buf.str, "duration"); + + if (p) + duration = read_ts(p); + st->codec->extradata = av_strdup(buf.str); + if (!st->codec->extradata) { + res = AVERROR(ENOMEM); + goto end; + } + st->codec->extradata_size = buf.len + 1; + } else { + /* if we just read a <time> tag, introduce a new event, otherwise merge + * with the previous one */ + int merge = !av_strncasecmp(buf.str, "<time", 5) ? 0 : 1; + sub = ff_subtitles_queue_insert(&rt->q, buf.str, buf.len, merge); + if (!sub) { + res = AVERROR(ENOMEM); + goto end; + } + if (!merge) { + const char *begin = ff_smil_get_attr_ptr(buf.str, "begin"); + const char *end = ff_smil_get_attr_ptr(buf.str, "end"); + + sub->pos = pos; + sub->pts = begin ? read_ts(begin) : 0; + sub->duration = end ? (read_ts(end) - sub->pts) : duration; + } + } + av_bprint_clear(&buf); + } + ff_subtitles_queue_finalize(&rt->q); + +end: + av_bprint_finalize(&buf, NULL); + return res; +} + +static int realtext_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + RealTextContext *rt = s->priv_data; + return ff_subtitles_queue_read_packet(&rt->q, pkt); +} + +static int realtext_read_seek(AVFormatContext *s, int stream_index, + int64_t min_ts, int64_t ts, int64_t max_ts, int flags) +{ + RealTextContext *rt = s->priv_data; + return ff_subtitles_queue_seek(&rt->q, s, stream_index, + min_ts, ts, max_ts, flags); +} + +static int realtext_read_close(AVFormatContext *s) +{ + RealTextContext *rt = s->priv_data; + ff_subtitles_queue_clean(&rt->q); + return 0; +} + +AVInputFormat ff_realtext_demuxer = { + .name = "realtext", + .long_name = NULL_IF_CONFIG_SMALL("RealText subtitle format"), + .priv_data_size = sizeof(RealTextContext), + .read_probe = realtext_probe, + .read_header = realtext_read_header, + .read_packet = realtext_read_packet, + .read_seek2 = realtext_read_seek, + .read_close = realtext_read_close, + .extensions = "rt", +}; diff --git a/libavformat/redspark.c b/libavformat/redspark.c new file mode 100644 index 0000000000..6d4c8c5c1a --- /dev/null +++ b/libavformat/redspark.c @@ -0,0 +1,170 @@ +/* + * RedSpark demuxer + * Copyright (c) 2013 James Almer + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavcodec/bytestream.h" +#include "libavutil/intreadwrite.h" +#include "avformat.h" +#include "avio.h" +#include "internal.h" + +#define HEADER_SIZE 4096 + +typedef struct RedSparkContext { + int samples_count; +} RedSparkContext; + +static int redspark_probe(AVProbeData *p) +{ + uint32_t key, data; + uint8_t header[8]; + + /* Decrypt first 8 bytes of the header */ + data = AV_RB32(p->buf); + data = data ^ (key = data ^ 0x52656453); + AV_WB32(header, data); + key = (key << 11) | (key >> 21); + + data = AV_RB32(p->buf + 4) ^ (((key << 3) | (key >> 29)) + key); + AV_WB32(header + 4, data); + + if (AV_RB64(header) == AV_RB64("RedSpark")) + return AVPROBE_SCORE_MAX; + + return 0; +} + +static int redspark_read_header(AVFormatContext *s) +{ + AVIOContext *pb = s->pb; + RedSparkContext *redspark = s->priv_data; + AVCodecContext *codec; + GetByteContext gbc; + int i, coef_off, ret = 0; + uint32_t key, data; + uint8_t *header, *pbc; + AVStream *st; + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + codec = st->codec; + + header = av_malloc(HEADER_SIZE + FF_INPUT_BUFFER_PADDING_SIZE); + if (!header) + return AVERROR(ENOMEM); + pbc = header; + + /* Decrypt header */ + data = avio_rb32(pb); + data = data ^ (key = data ^ 0x52656453); + bytestream_put_be32(&pbc, data); + key = (key << 11) | (key >> 21); + + for (i = 4; i < HEADER_SIZE; i += 4) { + data = avio_rb32(pb) ^ (key = ((key << 3) | (key >> 29)) + key); + bytestream_put_be32(&pbc, data); + } + + codec->codec_id = AV_CODEC_ID_ADPCM_THP; + codec->codec_type = AVMEDIA_TYPE_AUDIO; + + bytestream2_init(&gbc, header, HEADER_SIZE); + bytestream2_seek(&gbc, 0x3c, SEEK_SET); + codec->sample_rate = bytestream2_get_be32u(&gbc); + if (codec->sample_rate <= 0 || codec->sample_rate > 96000) { + av_log(s, AV_LOG_ERROR, "Invalid sample rate: %d\n", codec->sample_rate); + ret = AVERROR_INVALIDDATA; + goto fail; + } + + st->duration = bytestream2_get_be32u(&gbc) * 14; + redspark->samples_count = 0; + bytestream2_skipu(&gbc, 10); + codec->channels = bytestream2_get_byteu(&gbc); + if (!codec->channels) { + ret = AVERROR_INVALIDDATA; + goto fail; + } + + coef_off = 0x54 + codec->channels * 8; + if (bytestream2_get_byteu(&gbc)) // Loop flag + coef_off += 16; + + if (coef_off + codec->channels * (32 + 14) > HEADER_SIZE) { + ret = AVERROR_INVALIDDATA; + goto fail; + } + + if (ff_alloc_extradata(codec, 32 * codec->channels)) { + ret = AVERROR(ENOMEM); + goto fail; + } + + /* Get the ADPCM table */ + bytestream2_seek(&gbc, coef_off, SEEK_SET); + for (i = 0; i < codec->channels; i++) { + if (bytestream2_get_bufferu(&gbc, codec->extradata + i * 32, 32) != 32) { + ret = AVERROR_INVALIDDATA; + goto fail; + } + bytestream2_skipu(&gbc, 14); + } + + avpriv_set_pts_info(st, 64, 1, codec->sample_rate); + +fail: + av_free(header); + + return ret; +} + +static int redspark_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + AVCodecContext *codec = s->streams[0]->codec; + RedSparkContext *redspark = s->priv_data; + uint32_t size = 8 * codec->channels; + int ret; + + if (url_feof(s->pb) || redspark->samples_count == s->streams[0]->duration) + return AVERROR_EOF; + + ret = av_get_packet(s->pb, pkt, size); + if (ret != size) { + av_free_packet(pkt); + return AVERROR(EIO); + } + + pkt->duration = 14; + redspark->samples_count += pkt->duration; + pkt->stream_index = 0; + + return ret; +} + +AVInputFormat ff_redspark_demuxer = { + .name = "redspark", + .long_name = NULL_IF_CONFIG_SMALL("RedSpark"), + .priv_data_size = sizeof(RedSparkContext), + .read_probe = redspark_probe, + .read_header = redspark_read_header, + .read_packet = redspark_read_packet, + .extensions = "rsd", +}; diff --git a/libavformat/riff.c b/libavformat/riff.c index 2668d19840..849c1bf9f1 100644 --- a/libavformat/riff.c +++ b/libavformat/riff.c @@ -2,20 +2,20 @@ * RIFF common functions and data * Copyright (c) 2000 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -25,7 +25,10 @@ #include "riff.h" /* Note: When encoding, the first matching tag is used, so order is - * important if multiple tags are possible for a given codec. */ + * important if multiple tags are possible for a given codec. + * Note also that this list is used for more than just riff, other + * files use it as well. + */ const AVCodecTag ff_codec_bmp_tags[] = { { AV_CODEC_ID_H264, MKTAG('H', '2', '6', '4') }, { AV_CODEC_ID_H264, MKTAG('h', '2', '6', '4') }, @@ -33,7 +36,12 @@ const AVCodecTag ff_codec_bmp_tags[] = { { AV_CODEC_ID_H264, MKTAG('x', '2', '6', '4') }, { AV_CODEC_ID_H264, MKTAG('a', 'v', 'c', '1') }, { AV_CODEC_ID_H264, MKTAG('D', 'A', 'V', 'C') }, + { AV_CODEC_ID_H264, MKTAG('S', 'M', 'V', '2') }, { AV_CODEC_ID_H264, MKTAG('V', 'S', 'S', 'H') }, + { AV_CODEC_ID_H264, MKTAG('Q', '2', '6', '4') }, /* QNAP surveillance system */ + { AV_CODEC_ID_H264, MKTAG('V', '2', '6', '4') }, + { AV_CODEC_ID_H264, MKTAG('G', 'A', 'V', 'C') }, /* GeoVision camera */ + { AV_CODEC_ID_H264, MKTAG('U', 'M', 'S', 'V') }, { AV_CODEC_ID_H263, MKTAG('H', '2', '6', '3') }, { AV_CODEC_ID_H263, MKTAG('X', '2', '6', '3') }, { AV_CODEC_ID_H263, MKTAG('T', '2', '6', '3') }, @@ -41,11 +49,11 @@ const AVCodecTag ff_codec_bmp_tags[] = { { AV_CODEC_ID_H263, MKTAG('V', 'X', '1', 'K') }, { AV_CODEC_ID_H263, MKTAG('Z', 'y', 'G', 'o') }, { AV_CODEC_ID_H263, MKTAG('M', '2', '6', '3') }, + { AV_CODEC_ID_H263, MKTAG('l', 's', 'v', 'm') }, { AV_CODEC_ID_H263P, MKTAG('H', '2', '6', '3') }, { AV_CODEC_ID_H263I, MKTAG('I', '2', '6', '3') }, /* Intel H.263 */ { AV_CODEC_ID_H261, MKTAG('H', '2', '6', '1') }, - { AV_CODEC_ID_H263P, MKTAG('U', '2', '6', '3') }, - { AV_CODEC_ID_H263P, MKTAG('v', 'i', 'v', '1') }, + { AV_CODEC_ID_H263, MKTAG('U', '2', '6', '3') }, { AV_CODEC_ID_MPEG4, MKTAG('F', 'M', 'P', '4') }, { AV_CODEC_ID_MPEG4, MKTAG('D', 'I', 'V', 'X') }, { AV_CODEC_ID_MPEG4, MKTAG('D', 'X', '5', '0') }, @@ -78,6 +86,7 @@ const AVCodecTag ff_codec_bmp_tags[] = { { AV_CODEC_ID_MPEG4, MKTAG('G', 'E', 'O', 'X') }, /* flipped video */ { AV_CODEC_ID_MPEG4, MKTAG('H', 'D', 'X', '4') }, + { AV_CODEC_ID_MPEG4, MKTAG('D', 'M', '4', 'V') }, { AV_CODEC_ID_MPEG4, MKTAG('D', 'M', 'K', '2') }, { AV_CODEC_ID_MPEG4, MKTAG('D', 'I', 'G', 'I') }, { AV_CODEC_ID_MPEG4, MKTAG('I', 'N', 'M', 'C') }, @@ -92,8 +101,11 @@ const AVCodecTag ff_codec_bmp_tags[] = { { AV_CODEC_ID_MPEG4, MKTAG('G', 'E', 'O', 'V') }, /* Samsung SHR-6040 */ { AV_CODEC_ID_MPEG4, MKTAG('S', 'I', 'P', 'P') }, + { AV_CODEC_ID_MPEG4, MKTAG('S', 'M', '4', 'V') }, { AV_CODEC_ID_MPEG4, MKTAG('X', 'V', 'I', 'X') }, { AV_CODEC_ID_MPEG4, MKTAG('D', 'r', 'e', 'X') }, + { AV_CODEC_ID_MPEG4, MKTAG('Q', 'M', 'P', '4') }, /* QNAP Systems */ + { AV_CODEC_ID_MPEG4, MKTAG('P', 'L', 'V', '1') }, /* Pelco DVR MPEG-4 */ { AV_CODEC_ID_MSMPEG4V3, MKTAG('M', 'P', '4', '3') }, { AV_CODEC_ID_MSMPEG4V3, MKTAG('D', 'I', 'V', '3') }, { AV_CODEC_ID_MSMPEG4V3, MKTAG('M', 'P', 'G', '3') }, @@ -110,6 +122,7 @@ const AVCodecTag ff_codec_bmp_tags[] = { { AV_CODEC_ID_MSMPEG4V1, MKTAG('M', 'P', '4', '1') }, { AV_CODEC_ID_WMV1, MKTAG('W', 'M', 'V', '1') }, { AV_CODEC_ID_WMV2, MKTAG('W', 'M', 'V', '2') }, + { AV_CODEC_ID_WMV2, MKTAG('G', 'X', 'V', 'E') }, { AV_CODEC_ID_DVVIDEO, MKTAG('d', 'v', 's', 'd') }, { AV_CODEC_ID_DVVIDEO, MKTAG('d', 'v', 'h', 'd') }, { AV_CODEC_ID_DVVIDEO, MKTAG('d', 'v', 'h', '1') }, @@ -125,6 +138,10 @@ const AVCodecTag ff_codec_bmp_tags[] = { { AV_CODEC_ID_DVVIDEO, MKTAG('d', 'v', 'c', ' ') }, { AV_CODEC_ID_DVVIDEO, MKTAG('d', 'v', 'c', 's') }, { AV_CODEC_ID_DVVIDEO, MKTAG('d', 'v', 'h', '1') }, + { AV_CODEC_ID_DVVIDEO, MKTAG('d', 'v', 'i', 's') }, + { AV_CODEC_ID_DVVIDEO, MKTAG('p', 'd', 'v', 'c') }, + { AV_CODEC_ID_DVVIDEO, MKTAG('S', 'L', '2', '5') }, + { AV_CODEC_ID_DVVIDEO, MKTAG('S', 'L', 'D', 'V') }, { AV_CODEC_ID_MPEG1VIDEO, MKTAG('m', 'p', 'g', '1') }, { AV_CODEC_ID_MPEG1VIDEO, MKTAG('m', 'p', 'g', '2') }, { AV_CODEC_ID_MPEG2VIDEO, MKTAG('m', 'p', 'g', '2') }, @@ -144,6 +161,8 @@ const AVCodecTag ff_codec_bmp_tags[] = { /* Matrox MPEG-2 intra-only */ { AV_CODEC_ID_MPEG2VIDEO, MKTAG('M', '7', '0', '1') }, { AV_CODEC_ID_MPEG2VIDEO, MKTAG('m', 'p', 'g', 'v') }, + { AV_CODEC_ID_MPEG1VIDEO, MKTAG('B', 'W', '1', '0') }, + { AV_CODEC_ID_MPEG1VIDEO, MKTAG('X', 'M', 'P', 'G') }, /* Xing MPEG intra only */ { AV_CODEC_ID_MJPEG, MKTAG('M', 'J', 'P', 'G') }, { AV_CODEC_ID_MJPEG, MKTAG('L', 'J', 'P', 'G') }, { AV_CODEC_ID_MJPEG, MKTAG('d', 'm', 'b', '1') }, @@ -158,7 +177,7 @@ const AVCodecTag ff_codec_bmp_tags[] = { { AV_CODEC_ID_MJPEG, MKTAG('M', 'J', 'L', 'S') }, { AV_CODEC_ID_MJPEG, MKTAG('j', 'p', 'e', 'g') }, { AV_CODEC_ID_MJPEG, MKTAG('I', 'J', 'P', 'G') }, - { AV_CODEC_ID_MJPEG, MKTAG('A', 'V', 'R', 'n') }, + { AV_CODEC_ID_AVRN, MKTAG('A', 'V', 'R', 'n') }, { AV_CODEC_ID_MJPEG, MKTAG('A', 'C', 'D', 'V') }, { AV_CODEC_ID_MJPEG, MKTAG('Q', 'I', 'V', 'G') }, /* SL M-JPEG */ @@ -213,11 +232,16 @@ const AVCodecTag ff_codec_bmp_tags[] = { { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', 'U', 'V', '9') }, { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', 'V', 'U', '9') }, { AV_CODEC_ID_RAWVIDEO, MKTAG('a', 'u', 'v', '2') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', 'V', 'Y', 'U') }, { AV_CODEC_ID_FRWU, MKTAG('F', 'R', 'W', 'U') }, { AV_CODEC_ID_R10K, MKTAG('R', '1', '0', 'k') }, { AV_CODEC_ID_R210, MKTAG('r', '2', '1', '0') }, { AV_CODEC_ID_V210, MKTAG('v', '2', '1', '0') }, + { AV_CODEC_ID_V308, MKTAG('v', '3', '0', '8') }, + { AV_CODEC_ID_V408, MKTAG('v', '4', '0', '8') }, + { AV_CODEC_ID_AYUV, MKTAG('A', 'Y', 'U', 'V') }, { AV_CODEC_ID_V410, MKTAG('v', '4', '1', '0') }, + { AV_CODEC_ID_YUV4, MKTAG('y', 'u', 'v', '4') }, { AV_CODEC_ID_INDEO3, MKTAG('I', 'V', '3', '1') }, { AV_CODEC_ID_INDEO3, MKTAG('I', 'V', '3', '2') }, { AV_CODEC_ID_INDEO4, MKTAG('I', 'V', '4', '1') }, @@ -252,8 +276,10 @@ const AVCodecTag ff_codec_bmp_tags[] = { { AV_CODEC_ID_TRUEMOTION1, MKTAG('P', 'V', 'E', 'Z') }, { AV_CODEC_ID_MSZH, MKTAG('M', 'S', 'Z', 'H') }, { AV_CODEC_ID_ZLIB, MKTAG('Z', 'L', 'I', 'B') }, + { AV_CODEC_ID_SNOW, MKTAG('S', 'N', 'O', 'W') }, { AV_CODEC_ID_4XM, MKTAG('4', 'X', 'M', 'V') }, { AV_CODEC_ID_FLV1, MKTAG('F', 'L', 'V', '1') }, + { AV_CODEC_ID_FLV1, MKTAG('S', '2', '6', '3') }, { AV_CODEC_ID_FLASHSV, MKTAG('F', 'S', 'V', '1') }, { AV_CODEC_ID_SVQ1, MKTAG('s', 'v', 'q', '1') }, { AV_CODEC_ID_TSCC, MKTAG('t', 's', 'c', 'c') }, @@ -269,6 +295,8 @@ const AVCodecTag ff_codec_bmp_tags[] = { { AV_CODEC_ID_VC1IMAGE, MKTAG('W', 'V', 'P', '2') }, { AV_CODEC_ID_LOCO, MKTAG('L', 'O', 'C', 'O') }, { AV_CODEC_ID_WNV1, MKTAG('W', 'N', 'V', '1') }, + { AV_CODEC_ID_WNV1, MKTAG('Y', 'U', 'V', '8') }, + { AV_CODEC_ID_AASC, MKTAG('A', 'A', 'S', '4') }, { AV_CODEC_ID_AASC, MKTAG('A', 'A', 'S', 'C') }, { AV_CODEC_ID_INDEO2, MKTAG('R', 'T', '2', '1') }, { AV_CODEC_ID_FRAPS, MKTAG('F', 'P', 'S', '1') }, @@ -282,6 +310,7 @@ const AVCodecTag ff_codec_bmp_tags[] = { { AV_CODEC_ID_JPEG2000, MKTAG('M', 'J', '2', 'C') }, { AV_CODEC_ID_JPEG2000, MKTAG('L', 'J', '2', 'C') }, { AV_CODEC_ID_JPEG2000, MKTAG('L', 'J', '2', 'K') }, + { AV_CODEC_ID_JPEG2000, MKTAG('I', 'P', 'J', '2') }, { AV_CODEC_ID_VMNC, MKTAG('V', 'M', 'n', 'c') }, { AV_CODEC_ID_TARGA, MKTAG('t', 'g', 'a', ' ') }, { AV_CODEC_ID_PNG, MKTAG('M', 'P', 'N', 'G') }, @@ -297,15 +326,20 @@ const AVCodecTag ff_codec_bmp_tags[] = { { AV_CODEC_ID_DPX, MKTAG('d', 'p', 'x', ' ') }, { AV_CODEC_ID_KGV1, MKTAG('K', 'G', 'V', '1') }, { AV_CODEC_ID_LAGARITH, MKTAG('L', 'A', 'G', 'S') }, + { AV_CODEC_ID_AMV, MKTAG('A', 'M', 'V', 'F') }, { AV_CODEC_ID_UTVIDEO, MKTAG('U', 'L', 'R', 'A') }, { AV_CODEC_ID_UTVIDEO, MKTAG('U', 'L', 'R', 'G') }, { AV_CODEC_ID_UTVIDEO, MKTAG('U', 'L', 'Y', '0') }, { AV_CODEC_ID_UTVIDEO, MKTAG('U', 'L', 'Y', '2') }, + /* Ut Video version 13.0.1 BT.709 codecs */ { AV_CODEC_ID_UTVIDEO, MKTAG('U', 'L', 'H', '0') }, { AV_CODEC_ID_UTVIDEO, MKTAG('U', 'L', 'H', '2') }, { AV_CODEC_ID_VBLE, MKTAG('V', 'B', 'L', 'E') }, + { AV_CODEC_ID_ESCAPE130, MKTAG('E', '1', '3', '0') }, { AV_CODEC_ID_DXTORY, MKTAG('x', 't', 'o', 'r') }, { AV_CODEC_ID_ZEROCODEC, MKTAG('Z', 'E', 'C', 'O') }, + { AV_CODEC_ID_Y41P, MKTAG('Y', '4', '1', 'P') }, + { AV_CODEC_ID_FLIC, MKTAG('A', 'F', 'L', 'C') }, { AV_CODEC_ID_MSS1, MKTAG('M', 'S', 'S', '1') }, { AV_CODEC_ID_MSA1, MKTAG('M', 'S', 'A', '1') }, { AV_CODEC_ID_TSCC2, MKTAG('T', 'S', 'C', '2') }, @@ -313,6 +347,8 @@ const AVCodecTag ff_codec_bmp_tags[] = { { AV_CODEC_ID_CLLC, MKTAG('C', 'L', 'L', 'C') }, { AV_CODEC_ID_MSS2, MKTAG('M', 'S', 'S', '2') }, { AV_CODEC_ID_SVQ3, MKTAG('S', 'V', 'Q', '3') }, + { AV_CODEC_ID_012V, MKTAG('0', '1', '2', 'v') }, + { AV_CODEC_ID_012V, MKTAG('a', '1', '2', 'v') }, { AV_CODEC_ID_G2M, MKTAG('G', '2', 'M', '2') }, { AV_CODEC_ID_G2M, MKTAG('G', '2', 'M', '3') }, { AV_CODEC_ID_G2M, MKTAG('G', '2', 'M', '4') }, @@ -332,12 +368,17 @@ const AVCodecTag ff_codec_wav_tags[] = { { AV_CODEC_ID_PCM_ALAW, 0x0006 }, { AV_CODEC_ID_PCM_MULAW, 0x0007 }, { AV_CODEC_ID_WMAVOICE, 0x000A }, + { AV_CODEC_ID_ADPCM_IMA_OKI, 0x0010 }, { AV_CODEC_ID_ADPCM_IMA_WAV, 0x0011 }, /* must come after adpcm_ima_wav in this list */ { AV_CODEC_ID_PCM_ZORK, 0x0011 }, + { AV_CODEC_ID_ADPCM_IMA_OKI, 0x0017 }, { AV_CODEC_ID_ADPCM_YAMAHA, 0x0020 }, { AV_CODEC_ID_TRUESPEECH, 0x0022 }, { AV_CODEC_ID_GSM_MS, 0x0031 }, + { AV_CODEC_ID_GSM_MS, 0x0032 }, /* msn audio */ + { AV_CODEC_ID_AMR_NB, 0x0038 }, /* rogue format number */ + { AV_CODEC_ID_G723_1, 0x0042 }, { AV_CODEC_ID_ADPCM_G726, 0x0045 }, { AV_CODEC_ID_MP2, 0x0050 }, { AV_CODEC_ID_MP3, 0x0055 }, @@ -347,9 +388,11 @@ const AVCodecTag ff_codec_wav_tags[] = { { AV_CODEC_ID_ADPCM_IMA_DK4, 0x0061 }, /* rogue format number */ { AV_CODEC_ID_ADPCM_IMA_DK3, 0x0062 }, + { AV_CODEC_ID_ADPCM_G726, 0x0064 }, { AV_CODEC_ID_ADPCM_IMA_WAV, 0x0069 }, { AV_CODEC_ID_METASOUND, 0x0075 }, { AV_CODEC_ID_AAC, 0x00ff }, + { AV_CODEC_ID_G723_1, 0x0111 }, { AV_CODEC_ID_SIPR, 0x0130 }, { AV_CODEC_ID_WMAV1, 0x0160 }, { AV_CODEC_ID_WMAV2, 0x0161 }, @@ -367,9 +410,14 @@ const AVCodecTag ff_codec_wav_tags[] = { { AV_CODEC_ID_AAC_LATM, 0x1602 }, { AV_CODEC_ID_AC3, 0x2000 }, { AV_CODEC_ID_DTS, 0x2001 }, + { AV_CODEC_ID_SONIC, 0x2048 }, + { AV_CODEC_ID_SONIC_LS, 0x2048 }, { AV_CODEC_ID_PCM_MULAW, 0x6c75 }, { AV_CODEC_ID_AAC, 0x706d }, { AV_CODEC_ID_AAC, 0x4143 }, + { AV_CODEC_ID_XAN_DPCM, 0x594a }, + { AV_CODEC_ID_G723_1, 0xA100 }, + { AV_CODEC_ID_AAC, 0xA106 }, { AV_CODEC_ID_SPEEX, 0xA109 }, { AV_CODEC_ID_FLAC, 0xF1AC }, { AV_CODEC_ID_ADPCM_SWF, ('S' << 8) + 'F' }, @@ -388,7 +436,9 @@ const AVMetadataConv ff_riff_info_conv[] = { { "INAM", "title" }, { "IPRD", "album" }, { "IPRT", "track" }, + { "ITRK", "track" }, { "ISFT", "encoder" }, + { "ISMP", "timecode" }, { "ITCH", "encoded_by" }, { 0 }, }; diff --git a/libavformat/riff.h b/libavformat/riff.h index f458f266e6..1bf437e06c 100644 --- a/libavformat/riff.h +++ b/libavformat/riff.h @@ -2,20 +2,20 @@ * RIFF common functions and data * copyright (c) 2000 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -43,14 +43,14 @@ void ff_end_tag(AVIOContext *pb, int64_t start); * bits_per_encoded_sample fields. Does not read extradata. * @return codec tag */ -int ff_get_bmp_header(AVIOContext *pb, AVStream *st); +int ff_get_bmp_header(AVIOContext *pb, AVStream *st, unsigned *esize); void ff_put_bmp_header(AVIOContext *pb, AVCodecContext *enc, const AVCodecTag *tags, int for_asf); int ff_put_wav_header(AVIOContext *pb, AVCodecContext *enc); enum AVCodecID ff_wav_codec_get_id(unsigned int tag, int bps); int ff_get_wav_header(AVIOContext *pb, AVCodecContext *codec, int size); -extern const AVCodecTag ff_codec_bmp_tags[]; +extern const AVCodecTag ff_codec_bmp_tags[]; // exposed through avformat_get_riff_video_tags() extern const AVCodecTag ff_codec_wav_tags[]; void ff_parse_specific_params(AVCodecContext *stream, int *au_rate, int *au_ssize, int *au_scale); @@ -91,10 +91,7 @@ static av_always_inline int ff_guidcmp(const void *g1, const void *g2) return memcmp(g1, g2, sizeof(ff_asf_guid)); } -static av_always_inline int ff_get_guid(AVIOContext *s, ff_asf_guid *g) -{ - return avio_read(s, *g, sizeof(*g)); -} +void ff_get_guid(AVIOContext *s, ff_asf_guid *g); enum AVCodecID ff_codec_guid_get_id(const AVCodecGuid *guids, ff_asf_guid guid); diff --git a/libavformat/riffdec.c b/libavformat/riffdec.c index 74f93acedf..de8eb5f588 100644 --- a/libavformat/riffdec.c +++ b/libavformat/riffdec.c @@ -2,20 +2,20 @@ * RIFF demuxing functions and data * Copyright (c) 2000 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -37,6 +37,13 @@ const AVCodecGuid ff_codec_wav_guids[] = { { AV_CODEC_ID_NONE } }; +void ff_get_guid(AVIOContext *s, ff_asf_guid *g) +{ + av_assert0(sizeof(*g) == 16); //compiler will optimize this out + if (avio_read(s, *g, sizeof(*g)) < (int)sizeof(*g)) + memset(*g, 0, sizeof(*g)); +} + enum AVCodecID ff_codec_guid_get_id(const AVCodecGuid *guids, ff_asf_guid guid) { int i; @@ -57,7 +64,10 @@ enum AVCodecID ff_codec_guid_get_id(const AVCodecGuid *guids, ff_asf_guid guid) static void parse_waveformatex(AVIOContext *pb, AVCodecContext *c) { ff_asf_guid subformat; - c->bits_per_coded_sample = avio_rl16(pb); + int bps = avio_rl16(pb); + if (bps) + c->bits_per_coded_sample = bps; + c->channel_layout = avio_rl32(pb); /* dwChannelMask */ ff_get_guid(pb, &subformat); @@ -105,12 +115,9 @@ int ff_get_wav_header(AVIOContext *pb, AVCodecContext *codec, int size) cbSize -= 22; size -= 22; } - codec->extradata_size = cbSize; if (cbSize > 0) { av_free(codec->extradata); - codec->extradata = av_mallocz(codec->extradata_size + - FF_INPUT_BUFFER_PADDING_SIZE); - if (!codec->extradata) + if (ff_alloc_extradata(codec, cbSize)) return AVERROR(ENOMEM); avio_read(pb, codec->extradata, codec->extradata_size); size -= cbSize; @@ -132,7 +139,7 @@ int ff_get_wav_header(AVIOContext *pb, AVCodecContext *codec, int size) codec->sample_rate = 0; } /* override bits_per_coded_sample for G.726 */ - if (codec->codec_id == AV_CODEC_ID_ADPCM_G726) + if (codec->codec_id == AV_CODEC_ID_ADPCM_G726 && codec->sample_rate) codec->bits_per_coded_sample = codec->bit_rate / codec->sample_rate; return 0; @@ -155,10 +162,11 @@ enum AVCodecID ff_wav_codec_get_id(unsigned int tag, int bps) return id; } -int ff_get_bmp_header(AVIOContext *pb, AVStream *st) +int ff_get_bmp_header(AVIOContext *pb, AVStream *st, unsigned *esize) { int tag1; - avio_rl32(pb); /* size */ + if(esize) *esize = avio_rl32(pb); + else avio_rl32(pb); st->codec->width = avio_rl32(pb); st->codec->height = (int32_t)avio_rl32(pb); avio_rl16(pb); /* planes */ @@ -189,12 +197,23 @@ int ff_read_riff_info(AVFormatContext *s, int64_t size) chunk_code = avio_rl32(pb); chunk_size = avio_rl32(pb); - + if (url_feof(pb)) { + if (chunk_code || chunk_size) { + av_log(s, AV_LOG_WARNING, "INFO subchunk truncated\n"); + return AVERROR_INVALIDDATA; + } + return AVERROR_EOF; + } if (chunk_size > end || end - chunk_size < cur || chunk_size == UINT_MAX) { - av_log(s, AV_LOG_WARNING, "too big INFO subchunk\n"); - break; + avio_seek(pb, -9, SEEK_CUR); + chunk_code = avio_rl32(pb); + chunk_size = avio_rl32(pb); + if (chunk_size > end || end - chunk_size < cur || chunk_size == UINT_MAX) { + av_log(s, AV_LOG_WARNING, "too big INFO subchunk\n"); + return AVERROR_INVALIDDATA; + } } chunk_size += (chunk_size & 1); @@ -209,7 +228,7 @@ int ff_read_riff_info(AVFormatContext *s, int64_t size) continue; } - value = av_malloc(chunk_size + 1); + value = av_mallocz(chunk_size + 1); if (!value) { av_log(s, AV_LOG_ERROR, "out of memory, unable to read INFO tag\n"); @@ -219,14 +238,10 @@ int ff_read_riff_info(AVFormatContext *s, int64_t size) AV_WL32(key, chunk_code); if (avio_read(pb, value, chunk_size) != chunk_size) { - av_free(value); av_log(s, AV_LOG_WARNING, "premature end of file while reading INFO tag\n"); - break; } - value[chunk_size] = 0; - av_dict_set(&s->metadata, key, value, AV_DICT_DONT_STRDUP_VAL); } diff --git a/libavformat/riffenc.c b/libavformat/riffenc.c index 98e97c05ed..bcfe018f74 100644 --- a/libavformat/riffenc.c +++ b/libavformat/riffenc.c @@ -2,20 +2,20 @@ * RIFF muxing functions * Copyright (c) 2000 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -39,10 +39,14 @@ void ff_end_tag(AVIOContext *pb, int64_t start) { int64_t pos; + av_assert0((start&1) == 0); + pos = avio_tell(pb); + if (pos & 1) + avio_w8(pb, 0); avio_seek(pb, start - 4, SEEK_SET); avio_wl32(pb, (uint32_t)(pos - start)); - avio_seek(pb, pos, SEEK_SET); + avio_seek(pb, FFALIGN(pos, 2), SEEK_SET); } /* WAVEFORMATEX header */ @@ -77,8 +81,10 @@ int ff_put_wav_header(AVIOContext *pb, AVCodecContext *enc) avio_wl16(pb, enc->channels); avio_wl32(pb, enc->sample_rate); - if (enc->codec_id == AV_CODEC_ID_MP2 || - enc->codec_id == AV_CODEC_ID_MP3 || + if (enc->codec_id == AV_CODEC_ID_ATRAC3 || + enc->codec_id == AV_CODEC_ID_G723_1 || + enc->codec_id == AV_CODEC_ID_MP2 || + enc->codec_id == AV_CODEC_ID_MP3 || enc->codec_id == AV_CODEC_ID_GSM_MS) { bps = 0; } else { @@ -104,6 +110,10 @@ int ff_put_wav_header(AVIOContext *pb, AVCodecContext *enc) // blkalign = 144 * enc->bit_rate/enc->sample_rate; } else if (enc->codec_id == AV_CODEC_ID_AC3) { blkalign = 3840; /* maximum bytes per frame */ + } else if (enc->codec_id == AV_CODEC_ID_AAC) { + blkalign = 768 * enc->channels; /* maximum bytes per frame */ + } else if (enc->codec_id == AV_CODEC_ID_G723_1) { + blkalign = 24; } else if (enc->block_align != 0) { /* specified by the codec */ blkalign = enc->block_align; } else @@ -115,6 +125,8 @@ int ff_put_wav_header(AVIOContext *pb, AVCodecContext *enc) enc->codec_id == AV_CODEC_ID_PCM_F64LE || enc->codec_id == AV_CODEC_ID_PCM_S16LE) { bytespersec = enc->sample_rate * blkalign; + } else if (enc->codec_id == AV_CODEC_ID_G723_1) { + bytespersec = 800; } else { bytespersec = enc->bit_rate / 8; } @@ -146,6 +158,11 @@ int ff_put_wav_header(AVIOContext *pb, AVCodecContext *enc) bytestream_put_le32(&riff_extradata, 0); /* dwPTSHigh */ bytestream_put_le32(&riff_extradata, 0); + } else if (enc->codec_id == AV_CODEC_ID_G723_1) { + hdrsize += 20; + bytestream_put_le32(&riff_extradata, 0x9ace0002); /* extradata needed for msacm g723.1 codec */ + bytestream_put_le32(&riff_extradata, 0xaea2f732); + bytestream_put_le16(&riff_extradata, 0xacde); } else if (enc->codec_id == AV_CODEC_ID_GSM_MS || enc->codec_id == AV_CODEC_ID_ADPCM_IMA_WAV) { hdrsize += 2; @@ -197,7 +214,7 @@ void ff_put_bmp_header(AVIOContext *pb, AVCodecContext *enc, avio_wl16(pb, enc->bits_per_coded_sample ? enc->bits_per_coded_sample : 24); /* compression type */ avio_wl32(pb, enc->codec_tag); - avio_wl32(pb, enc->width * enc->height * 3); + avio_wl32(pb, (enc->width * enc->height * (enc->bits_per_coded_sample ? enc->bits_per_coded_sample : 24)+7) / 8); avio_wl32(pb, 0); avio_wl32(pb, 0); avio_wl32(pb, 0); @@ -257,7 +274,7 @@ void ff_riff_write_info_tag(AVIOContext *pb, const char *tag, const char *str) static const char riff_tags[][5] = { "IARL", "IART", "ICMS", "ICMT", "ICOP", "ICRD", "ICRP", "IDIM", "IDPI", "IENG", "IGNR", "IKEY", "ILGT", "ILNG", "IMED", "INAM", "IPLT", "IPRD", - "IPRT", "ISBJ", "ISFT", "ISHP", "ISRC", "ISRF", "ITCH", + "IPRT", "ITRK", "ISBJ", "ISFT", "ISHP", "ISMP", "ISRC", "ISRF", "ITCH", { 0 } }; diff --git a/libavformat/rl2.c b/libavformat/rl2.c index ab33aabccc..ff4006497f 100644 --- a/libavformat/rl2.c +++ b/libavformat/rl2.c @@ -2,20 +2,20 @@ * RL2 Format Demuxer * Copyright (c) 2008 Sascha Sommer (saschasommer@freenet.de) * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -107,10 +107,6 @@ static av_cold int rl2_read_header(AVFormatContext *s) rate = avio_rl16(pb); channels = avio_rl16(pb); def_sound_size = avio_rl16(pb); - if (!channels || channels > 42) { - av_log(s, AV_LOG_ERROR, "Invalid number of channels: %d\n", channels); - return AVERROR_INVALIDDATA; - } /** setup video stream */ st = avformat_new_stream(s, NULL); @@ -129,9 +125,7 @@ static av_cold int rl2_read_header(AVFormatContext *s) if(signature == RLV3_TAG && back_size > 0) st->codec->extradata_size += back_size; - st->codec->extradata = av_mallocz(st->codec->extradata_size + - FF_INPUT_BUFFER_PADDING_SIZE); - if(!st->codec->extradata) + if(ff_alloc_extradata(st->codec, st->codec->extradata_size)) return AVERROR(ENOMEM); if(avio_read(pb,st->codec->extradata,st->codec->extradata_size) != @@ -140,6 +134,11 @@ static av_cold int rl2_read_header(AVFormatContext *s) /** setup audio stream if present */ if(sound_rate){ + if (!channels || channels > 42) { + av_log(s, AV_LOG_ERROR, "Invalid number of channels: %d\n", channels); + return AVERROR_INVALIDDATA; + } + pts_num = def_sound_size; pts_den = rate; @@ -233,7 +232,7 @@ static int rl2_read_packet(AVFormatContext *s, } if(stream_id == -1) - return AVERROR(EIO); + return AVERROR_EOF; ++rl2->index_pos[stream_id]; diff --git a/libavformat/rm.c b/libavformat/rm.c index 761be3f556..0591a1735a 100644 --- a/libavformat/rm.c +++ b/libavformat/rm.c @@ -2,20 +2,20 @@ * "Real" compatible muxer and demuxer common code. * Copyright (c) 2009 Aurelien Jacobs <aurel@gnuage.org> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/rm.h b/libavformat/rm.h index a06ea01a5a..a72e8b7997 100644 --- a/libavformat/rm.h +++ b/libavformat/rm.h @@ -2,20 +2,20 @@ * "Real" compatible muxer and demuxer. * Copyright (c) 2000, 2001 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -50,7 +50,7 @@ extern AVInputFormat ff_rdt_demuxer; */ int ff_rm_read_mdpr_codecdata (AVFormatContext *s, AVIOContext *pb, AVStream *st, RMStream *rst, - int codec_data_size); + int codec_data_size, const uint8_t *mime); /** * Parse one rm-stream packet from the input bytestream. diff --git a/libavformat/rmdec.c b/libavformat/rmdec.c index d61f56908c..b0876fe5bd 100644 --- a/libavformat/rmdec.c +++ b/libavformat/rmdec.c @@ -2,29 +2,31 @@ * "Real" compatible demuxer. * Copyright (c) 2000, 2001 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "libavutil/avassert.h" #include "libavutil/avstring.h" #include "libavutil/channel_layout.h" #include "libavutil/internal.h" #include "libavutil/intreadwrite.h" #include "libavutil/dict.h" #include "avformat.h" +#include "avio_internal.h" #include "internal.h" #include "rmsipr.h" #include "rm.h" @@ -84,11 +86,9 @@ static int rm_read_extradata(AVIOContext *pb, AVCodecContext *avctx, unsigned si { if (size >= 1<<24) return -1; - avctx->extradata = av_malloc(size + FF_INPUT_BUFFER_PADDING_SIZE); - if (!avctx->extradata) + if (ff_alloc_extradata(avctx, size)) return AVERROR(ENOMEM); avctx->extradata_size = avio_read(pb, avctx->extradata, size); - memset(avctx->extradata + avctx->extradata_size, 0, FF_INPUT_BUFFER_PADDING_SIZE); if (avctx->extradata_size != size) return AVERROR(EIO); return 0; @@ -98,6 +98,7 @@ static void rm_read_metadata(AVFormatContext *s, AVIOContext *pb, int wide) { char buf[1024]; int i; + for (i=0; i<FF_ARRAY_ELEMS(ff_rm_metadata); i++) { int len = wide ? avio_rb16(pb) : avio_r8(pb); get_strl(pb, buf, sizeof(buf), len); @@ -127,9 +128,12 @@ static int rm_read_audio_stream_info(AVFormatContext *s, AVIOContext *pb, /* ra type header */ version = avio_rb16(pb); /* version */ if (version == 3) { + unsigned bytes_per_minute; int header_size = avio_rb16(pb); int64_t startpos = avio_tell(pb); - avio_skip(pb, 14); + avio_skip(pb, 8); + bytes_per_minute = avio_rb16(pb); + avio_skip(pb, 4); rm_read_metadata(s, pb, 0); if ((startpos + header_size) >= avio_tell(pb) + 2) { // fourcc (should always be "lpcJ") @@ -139,6 +143,8 @@ static int rm_read_audio_stream_info(AVFormatContext *s, AVIOContext *pb, // Skip extra header crap (this should never happen) if ((startpos + header_size) > avio_tell(pb)) avio_skip(pb, header_size + startpos - avio_tell(pb)); + if (bytes_per_minute) + st->codec->bit_rate = 8LL * bytes_per_minute / 60; st->codec->sample_rate = 8000; st->codec->channels = 1; st->codec->channel_layout = AV_CH_LAYOUT_MONO; @@ -148,6 +154,7 @@ static int rm_read_audio_stream_info(AVFormatContext *s, AVIOContext *pb, } else { int flavor, sub_packet_h, coded_framesize, sub_packet_size; int codecdata_length; + unsigned bytes_per_minute; /* old version (4) */ avio_skip(pb, 2); /* unused */ avio_rb32(pb); /* .ra4 */ @@ -157,7 +164,11 @@ static int rm_read_audio_stream_info(AVFormatContext *s, AVIOContext *pb, flavor= avio_rb16(pb); /* add codec info / flavor */ ast->coded_framesize = coded_framesize = avio_rb32(pb); /* coded frame size */ avio_rb32(pb); /* ??? */ - avio_rb32(pb); /* ??? */ + bytes_per_minute = avio_rb32(pb); + if (version == 4) { + if (bytes_per_minute) + st->codec->bit_rate = 8LL * bytes_per_minute / 60; + } avio_rb32(pb); /* ??? */ ast->sub_packet_h = sub_packet_h = avio_rb16(pb); /* 1 */ st->codec->block_align= avio_rb16(pb); /* frame size */ @@ -196,13 +207,17 @@ static int rm_read_audio_stream_info(AVFormatContext *s, AVIOContext *pb, st->need_parsing = AVSTREAM_PARSE_HEADERS; case AV_CODEC_ID_ATRAC3: case AV_CODEC_ID_SIPR: - avio_rb16(pb); avio_r8(pb); - if (version == 5) - avio_r8(pb); - codecdata_length = avio_rb32(pb); - if(codecdata_length + FF_INPUT_BUFFER_PADDING_SIZE <= (unsigned)codecdata_length){ - av_log(s, AV_LOG_ERROR, "codecdata_length too large\n"); - return -1; + if (read_all) { + codecdata_length = 0; + } else { + avio_rb16(pb); avio_r8(pb); + if (version == 5) + avio_r8(pb); + codecdata_length = avio_rb32(pb); + if(codecdata_length + FF_INPUT_BUFFER_PADDING_SIZE <= (unsigned)codecdata_length){ + av_log(s, AV_LOG_ERROR, "codecdata_length too large\n"); + return -1; + } } ast->audio_framesize = st->codec->block_align; @@ -222,6 +237,7 @@ static int rm_read_audio_stream_info(AVFormatContext *s, AVIOContext *pb, } if ((ret = rm_read_extradata(pb, st->codec, codecdata_length)) < 0) return ret; + break; case AV_CODEC_ID_AAC: avio_rb16(pb); avio_r8(pb); @@ -269,7 +285,7 @@ static int rm_read_audio_stream_info(AVFormatContext *s, AVIOContext *pb, case DEINT_ID_VBRF: break; default: - av_log(NULL,0,"Unknown interleaver %X\n", ast->deint_id); + av_log(s, AV_LOG_ERROR, "Unknown interleaver %X\n", ast->deint_id); return AVERROR_INVALIDDATA; } @@ -285,7 +301,7 @@ static int rm_read_audio_stream_info(AVFormatContext *s, AVIOContext *pb, int ff_rm_read_mdpr_codecdata (AVFormatContext *s, AVIOContext *pb, - AVStream *st, RMStream *rst, int codec_data_size) + AVStream *st, RMStream *rst, int codec_data_size, const uint8_t *mime) { unsigned int v; int size; @@ -308,11 +324,38 @@ ff_rm_read_mdpr_codecdata (AVFormatContext *s, AVIOContext *pb, st->codec->codec_tag = AV_RL32(st->codec->extradata); st->codec->codec_id = ff_codec_get_id(ff_rm_codec_tags, st->codec->codec_tag); + } else if(mime && !strcmp(mime, "logical-fileinfo")){ + int stream_count, rule_count, property_count, i; + ff_free_stream(s, st); + if (avio_rb16(pb) != 0) { + av_log(s, AV_LOG_WARNING, "Unsupported version\n"); + goto skip; + } + stream_count = avio_rb16(pb); + avio_skip(pb, 6*stream_count); + rule_count = avio_rb16(pb); + avio_skip(pb, 2*rule_count); + property_count = avio_rb16(pb); + for(i=0; i<property_count; i++){ + uint8_t name[128], val[128]; + avio_rb32(pb); + if (avio_rb16(pb) != 0) { + av_log(s, AV_LOG_WARNING, "Unsupported Name value property version\n"); + goto skip; //FIXME skip just this one + } + get_str8(pb, name, sizeof(name)); + switch(avio_rb32(pb)) { + case 2: get_strl(pb, val, sizeof(val), avio_rb16(pb)); + av_dict_set(&s->metadata, name, val, 0); + break; + default: avio_skip(pb, avio_rb16(pb)); + } + } } else { int fps; if (avio_rl32(pb) != MKTAG('V', 'I', 'D', 'O')) { fail1: - av_log(st->codec, AV_LOG_ERROR, "Unsupported video codec\n"); + av_log(s, AV_LOG_WARNING, "Unsupported stream type %08x\n", v); goto skip; } st->codec->codec_tag = avio_rl32(pb); @@ -335,6 +378,9 @@ ff_rm_read_mdpr_codecdata (AVFormatContext *s, AVIOContext *pb, if (fps > 0) { av_reduce(&st->avg_frame_rate.den, &st->avg_frame_rate.num, 0x10000, fps, (1 << 30) - 1); +#if FF_API_R_FRAME_RATE + st->r_frame_rate = st->avg_frame_rate; +#endif } else if (s->error_recognition & AV_EF_EXPLODE) { av_log(s, AV_LOG_ERROR, "Invalid framerate\n"); return AVERROR_INVALIDDATA; @@ -429,7 +475,7 @@ static int rm_read_header(AVFormatContext *s) int tag_size; unsigned int start_time, duration; unsigned int data_off = 0, indx_off = 0; - char buf[128]; + char buf[128], mime[128]; int flags = 0; tag = avio_rl32(pb); @@ -444,7 +490,7 @@ static int rm_read_header(AVFormatContext *s) avio_skip(pb, tag_size - 8); for(;;) { - if (pb->eof_reached) + if (url_feof(pb)) return -1; tag = avio_rl32(pb); tag_size = avio_rb32(pb); @@ -466,7 +512,8 @@ static int rm_read_header(AVFormatContext *s) avio_rb32(pb); /* max packet size */ avio_rb32(pb); /* avg packet size */ avio_rb32(pb); /* nb packets */ - avio_rb32(pb); /* duration */ + duration = avio_rb32(pb); /* duration */ + s->duration = av_rescale(duration, AV_TIME_BASE, 1000); avio_rb32(pb); /* preroll */ indx_off = avio_rb32(pb); /* index offset */ data_off = avio_rb32(pb); /* data offset */ @@ -490,12 +537,14 @@ static int rm_read_header(AVFormatContext *s) duration = avio_rb32(pb); /* duration */ st->start_time = start_time; st->duration = duration; + if(duration>0) + s->duration = AV_NOPTS_VALUE; get_str8(pb, buf, sizeof(buf)); /* desc */ - get_str8(pb, buf, sizeof(buf)); /* mimetype */ + get_str8(pb, mime, sizeof(mime)); /* mimetype */ st->codec->codec_type = AVMEDIA_TYPE_DATA; st->priv_data = ff_rm_alloc_rmstream(); if (ff_rm_read_mdpr_codecdata(s, s->pb, st, st->priv_data, - avio_rb32(pb)) < 0) + avio_rb32(pb), mime) < 0) return -1; break; case MKTAG('D', 'A', 'T', 'A'): @@ -548,7 +597,7 @@ static int sync(AVFormatContext *s, int64_t *timestamp, int *flags, int *stream_ AVStream *st; uint32_t state=0xFFFFFFFF; - while(!pb->eof_reached){ + while(!url_feof(pb)){ int len, num, i; *pos= avio_tell(pb) - 3; if(rm->remaining_len > 0){ @@ -615,8 +664,10 @@ static int rm_assemble_video_frame(AVFormatContext *s, AVIOContext *pb, AVPacket *pkt, int len, int *pseq, int64_t *timestamp) { - int hdr, seq, pic_num, len2, pos; + int hdr; + int seq = 0, pic_num = 0, len2 = 0, pos = 0; //init to silcense compiler warning int type; + int ret; hdr = avio_r8(pb); len--; type = hdr >> 6; @@ -629,34 +680,47 @@ static int rm_assemble_video_frame(AVFormatContext *s, AVIOContext *pb, pos = get_num(pb, &len); pic_num = avio_r8(pb); len--; } - if(len<0) + if(len<0) { + av_log(s, AV_LOG_ERROR, "Insufficient data\n"); return -1; + } rm->remaining_len = len; if(type&1){ // frame, not slice if(type == 3){ // frame as a part of packet len= len2; *timestamp = pos; } - if(rm->remaining_len < len) + if(rm->remaining_len < len) { + av_log(s, AV_LOG_ERROR, "Insufficient remaining len\n"); return -1; + } rm->remaining_len -= len; if(av_new_packet(pkt, len + 9) < 0) return AVERROR(EIO); pkt->data[0] = 0; AV_WL32(pkt->data + 1, 1); AV_WL32(pkt->data + 5, 0); - avio_read(pb, pkt->data + 9, len); + if ((ret = avio_read(pb, pkt->data + 9, len)) != len) { + av_free_packet(pkt); + av_log(s, AV_LOG_ERROR, "Failed to read %d bytes\n", len); + return ret < 0 ? ret : AVERROR(EIO); + } return 0; } //now we have to deal with single slice *pseq = seq; if((seq & 0x7F) == 1 || vst->curpic_num != pic_num){ + if (len2 > ffio_limit(pb, len2)) { + av_log(s, AV_LOG_ERROR, "Impossibly sized packet\n"); + return AVERROR_INVALIDDATA; + } vst->slices = ((hdr & 0x3F) << 1) + 1; vst->videobufsize = len2 + 8*vst->slices + 1; av_free_packet(&vst->pkt); //FIXME this should be output. if(av_new_packet(&vst->pkt, vst->videobufsize) < 0) return AVERROR(ENOMEM); + memset(vst->pkt.data, 0, vst->pkt.size); vst->videobufpos = 8*vst->slices + 1; vst->cur_slice = 0; vst->curpic_num = pic_num; @@ -665,12 +729,18 @@ static int rm_assemble_video_frame(AVFormatContext *s, AVIOContext *pb, if(type == 2) len = FFMIN(len, pos); - if(++vst->cur_slice > vst->slices) + if(++vst->cur_slice > vst->slices) { + av_log(s, AV_LOG_ERROR, "cur slice %d, too large\n", vst->cur_slice); return 1; + } + if(!vst->pkt.data) + return AVERROR(ENOMEM); AV_WL32(vst->pkt.data - 7 + 8*vst->cur_slice, 1); AV_WL32(vst->pkt.data - 3 + 8*vst->cur_slice, vst->videobufpos - 8*vst->slices - 1); - if(vst->videobufpos + len > vst->videobufsize) + if(vst->videobufpos + len > vst->videobufsize) { + av_log(s, AV_LOG_ERROR, "outside videobufsize\n"); return 1; + } if (avio_read(pb, vst->pkt.data + vst->videobufpos, len) != len) return AVERROR(EIO); vst->videobufpos += len; @@ -721,11 +791,13 @@ ff_rm_parse_packet (AVFormatContext *s, AVIOContext *pb, int *seq, int flags, int64_t timestamp) { RMDemuxContext *rm = s->priv_data; + int ret; if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) { rm->current_stream= st->id; - if(rm_assemble_video_frame(s, pb, rm, ast, pkt, len, seq, ×tamp)) - return -1; //got partial frame + ret = rm_assemble_video_frame(s, pb, rm, ast, pkt, len, seq, ×tamp); + if(ret) + return ret < 0 ? ret : -1; //got partial frame or error } else if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) { if ((ast->deint_id == DEINT_ID_GENR) || (ast->deint_id == DEINT_ID_INT4) || @@ -811,13 +883,14 @@ ff_rm_retrieve_cache (AVFormatContext *s, AVIOContext *pb, { RMDemuxContext *rm = s->priv_data; - assert (rm->audio_pkt_cnt > 0); + av_assert0 (rm->audio_pkt_cnt > 0); if (ast->deint_id == DEINT_ID_VBRF || ast->deint_id == DEINT_ID_VBRS) av_get_packet(pb, pkt, ast->sub_packet_lengths[ast->sub_packet_cnt - rm->audio_pkt_cnt]); else { - av_new_packet(pkt, st->codec->block_align); + if(av_new_packet(pkt, st->codec->block_align) < 0) + return AVERROR(ENOMEM); memcpy(pkt->data, ast->pkt.data + st->codec->block_align * //FIXME avoid this (ast->sub_packet_h * ast->audio_framesize / st->codec->block_align - rm->audio_pkt_cnt), st->codec->block_align); @@ -836,7 +909,7 @@ ff_rm_retrieve_cache (AVFormatContext *s, AVIOContext *pb, static int rm_read_packet(AVFormatContext *s, AVPacket *pkt) { RMDemuxContext *rm = s->priv_data; - AVStream *st; + AVStream *st = NULL; // init to silence compiler warning int i, len, res, seq = 1; int64_t timestamp, pos; int flags; @@ -845,7 +918,9 @@ static int rm_read_packet(AVFormatContext *s, AVPacket *pkt) if (rm->audio_pkt_cnt) { // If there are queued audio packet return them first st = s->streams[rm->audio_stream_num]; - ff_rm_retrieve_cache(s, s->pb, st, st->priv_data, pkt); + res = ff_rm_retrieve_cache(s, s->pb, st, st->priv_data, pkt); + if(res < 0) + return res; flags = 0; } else { if (rm->old_format) { @@ -864,11 +939,13 @@ static int rm_read_packet(AVFormatContext *s, AVPacket *pkt) st = s->streams[i]; } - if(len<0 || s->pb->eof_reached) + if(len<0 || url_feof(s->pb)) return AVERROR(EIO); res = ff_rm_parse_packet (s, s->pb, st, st->priv_data, len, pkt, &seq, flags, timestamp); + if (res < -1) + return res; if((flags&2) && (seq&0x7F) == 1) av_add_index_entry(st, pos, timestamp, 0, 0, AVINDEX_KEYFRAME); if (res) @@ -920,7 +997,9 @@ static int64_t rm_read_dts(AVFormatContext *s, int stream_index, if(rm->old_format) return AV_NOPTS_VALUE; - avio_seek(s->pb, pos, SEEK_SET); + if (avio_seek(s->pb, pos, SEEK_SET) < 0) + return AV_NOPTS_VALUE; + rm->remaining_len=0; for(;;){ int seq=1; @@ -952,6 +1031,18 @@ static int64_t rm_read_dts(AVFormatContext *s, int stream_index, return dts; } +static int rm_read_seek(AVFormatContext *s, int stream_index, + int64_t pts, int flags) +{ + RMDemuxContext *rm = s->priv_data; + + if (ff_seek_frame_binary(s, stream_index, pts, flags) < 0) + return -1; + rm->audio_pkt_cnt = 0; + return 0; +} + + AVInputFormat ff_rm_demuxer = { .name = "rm", .long_name = NULL_IF_CONFIG_SMALL("RealMedia"), @@ -961,6 +1052,7 @@ AVInputFormat ff_rm_demuxer = { .read_packet = rm_read_packet, .read_close = rm_read_close, .read_timestamp = rm_read_dts, + .read_seek = rm_read_seek, }; AVInputFormat ff_rdt_demuxer = { diff --git a/libavformat/rmenc.c b/libavformat/rmenc.c index fba8feb802..17192ff275 100644 --- a/libavformat/rmenc.c +++ b/libavformat/rmenc.c @@ -2,20 +2,20 @@ * "Real" compatible muxer. * Copyright (c) 2000, 2001 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "avformat.h" @@ -217,8 +217,8 @@ static int rv10_write_header(AVFormatContext *ctx, coded_frame_size--; avio_wb32(s, coded_frame_size); /* frame length */ avio_wb32(s, 0x51540); /* unknown */ - avio_wb32(s, 0x249f0); /* unknown */ - avio_wb32(s, 0x249f0); /* unknown */ + avio_wb32(s, stream->enc->bit_rate / 8 * 60); /* bytes per minute */ + avio_wb32(s, stream->enc->bit_rate / 8 * 60); /* bytes per minute */ avio_wb16(s, 0x01); /* frame length : seems to be very important */ avio_wb16(s, coded_frame_size); @@ -309,6 +309,11 @@ static int rm_write_header(AVFormatContext *s) int n; AVCodecContext *codec; + if (s->nb_streams > 2) { + av_log(s, AV_LOG_ERROR, "At most 2 streams are currently supported for muxing in RM\n"); + return AVERROR_PATCHWELCOME; + } + for(n=0;n<s->nb_streams;n++) { s->streams[n]->id = n; codec = s->streams[n]->codec; diff --git a/libavformat/rmsipr.c b/libavformat/rmsipr.c index 89fbd0e398..14ec8ea606 100644 --- a/libavformat/rmsipr.c +++ b/libavformat/rmsipr.c @@ -1,20 +1,20 @@ /* * tables and functions for demuxing SIPR audio muxed RealMedia style * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/rmsipr.h b/libavformat/rmsipr.h index c8d3d0a236..2837336dbb 100644 --- a/libavformat/rmsipr.h +++ b/libavformat/rmsipr.h @@ -1,20 +1,20 @@ /* * tables and functions for demuxing SIPR audio muxed RealMedia style * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/rpl.c b/libavformat/rpl.c index 42b47f8457..61a2316412 100644 --- a/libavformat/rpl.c +++ b/libavformat/rpl.c @@ -2,20 +2,20 @@ * ARMovie/RPL demuxer * Copyright (c) 2007 Christian Ohm, 2008 Eli Friedman * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -58,7 +58,7 @@ static int read_line(AVIOContext * pb, char* line, int bufsize) break; if (b == '\n') { line[i] = '\0'; - return 0; + return url_feof(pb) ? -1 : 0; } line[i] = b; } @@ -168,9 +168,8 @@ static int rpl_read_header(AVFormatContext *s) vst->codec->codec_id = AV_CODEC_ID_ESCAPE130; break; default: - av_log(s, AV_LOG_WARNING, - "RPL video format %i not supported yet!\n", - vst->codec->codec_tag); + avpriv_report_missing_feature(s, "Video format %i", + vst->codec->codec_tag); vst->codec->codec_id = AV_CODEC_ID_NONE; } @@ -220,11 +219,8 @@ static int rpl_read_header(AVFormatContext *s) } break; } - if (ast->codec->codec_id == AV_CODEC_ID_NONE) { - av_log(s, AV_LOG_WARNING, - "RPL audio format %i not supported yet!\n", - audio_format); - } + if (ast->codec->codec_id == AV_CODEC_ID_NONE) + avpriv_request_sample(s, "Audio format %i", audio_format); avpriv_set_pts_info(ast, 32, 1, ast->codec->bit_rate); } else { for (i = 0; i < 3; i++) @@ -252,10 +248,10 @@ static int rpl_read_header(AVFormatContext *s) // Read the index avio_seek(pb, chunk_catalog_offset, SEEK_SET); total_audio_size = 0; - for (i = 0; i < number_of_chunks; i++) { + for (i = 0; !error && i < number_of_chunks; i++) { int64_t offset, video_size, audio_size; error |= read_line(pb, line, sizeof(line)); - if (3 != sscanf(line, "%"PRId64" , %"PRId64" ; %"PRId64, + if (3 != sscanf(line, "%"SCNd64" , %"SCNd64" ; %"SCNd64, &offset, &video_size, &audio_size)) error = -1; av_add_index_entry(vst, offset, i * rpl->frames_per_chunk, @@ -287,7 +283,7 @@ static int rpl_read_packet(AVFormatContext *s, AVPacket *pkt) stream = s->streams[rpl->chunk_part]; if (rpl->chunk_number >= stream->nb_index_entries) - return -1; + return AVERROR_EOF; index_entry = &stream->index_entries[rpl->chunk_number]; diff --git a/libavformat/rsd.c b/libavformat/rsd.c new file mode 100644 index 0000000000..37e544acb1 --- /dev/null +++ b/libavformat/rsd.c @@ -0,0 +1,168 @@ +/* + * RSD demuxer + * Copyright (c) 2013 James Almer + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavcodec/bytestream.h" +#include "libavutil/intreadwrite.h" +#include "avformat.h" +#include "avio.h" +#include "internal.h" + +static const AVCodecTag rsd_tags[] = { + { AV_CODEC_ID_ADPCM_THP, MKTAG('G','A','D','P') }, + { AV_CODEC_ID_ADPCM_IMA_RAD, MKTAG('R','A','D','P') }, + { AV_CODEC_ID_PCM_S16BE, MKTAG('P','C','M','B') }, + { AV_CODEC_ID_PCM_S16LE, MKTAG('P','C','M',' ') }, + { AV_CODEC_ID_NONE, 0 }, +}; + +static const uint32_t rsd_unsupported_tags[] = { + MKTAG('O','G','G',' '), + MKTAG('V','A','G',' '), + MKTAG('W','A','D','P'), + MKTAG('X','A','D','P'), + MKTAG('X','M','A',' '), +}; + +static int rsd_probe(AVProbeData *p) +{ + if (!memcmp(p->buf, "RSD", 3) && + p->buf[3] - '0' >= 2 && p->buf[3] - '0' <= 6) + return AVPROBE_SCORE_EXTENSION; + return 0; +} + +static int rsd_read_header(AVFormatContext *s) +{ + AVIOContext *pb = s->pb; + int i, version, start = 0x800; + AVCodecContext *codec; + AVStream *st = avformat_new_stream(s, NULL); + + if (!st) + return AVERROR(ENOMEM); + + avio_skip(pb, 3); // "RSD" + version = avio_r8(pb) - '0'; + + codec = st->codec; + codec->codec_type = AVMEDIA_TYPE_AUDIO; + codec->codec_tag = avio_rl32(pb); + codec->codec_id = ff_codec_get_id(rsd_tags, codec->codec_tag); + if (!codec->codec_id) { + char tag_buf[5]; + + av_get_codec_tag_string(tag_buf, sizeof(tag_buf), codec->codec_tag); + for (i=0; i < FF_ARRAY_ELEMS(rsd_unsupported_tags); i++) { + if (codec->codec_tag == rsd_unsupported_tags[i]) { + avpriv_request_sample(s, "Codec tag: %s", tag_buf); + return AVERROR_PATCHWELCOME; + } + } + av_log(s, AV_LOG_ERROR, "Unknown codec tag: %s\n", tag_buf); + return AVERROR_INVALIDDATA; + } + + codec->channels = avio_rl32(pb); + if (!codec->channels) + return AVERROR_INVALIDDATA; + + avio_skip(pb, 4); // Bit depth + codec->sample_rate = avio_rl32(pb); + if (!codec->sample_rate) + return AVERROR_INVALIDDATA; + + avio_skip(pb, 4); // Unknown + + switch (codec->codec_id) { + case AV_CODEC_ID_ADPCM_IMA_RAD: + codec->block_align = 20 * codec->channels; + if (pb->seekable) + st->duration = av_get_audio_frame_duration(codec, avio_size(pb) - start); + break; + case AV_CODEC_ID_ADPCM_THP: + /* RSD3GADP is mono, so only alloc enough memory + to store the coeff table for a single channel. */ + + if (ff_alloc_extradata(codec, 32)) + return AVERROR(ENOMEM); + + start = avio_rl32(pb); + + if (avio_read(s->pb, codec->extradata, 32) != 32) + return AVERROR_INVALIDDATA; + + for (i = 0; i < 16; i++) + AV_WB16(codec->extradata + i * 2, AV_RL16(codec->extradata + i * 2)); + + if (pb->seekable) + st->duration = (avio_size(pb) - start) / 8 * 14; + break; + case AV_CODEC_ID_PCM_S16LE: + case AV_CODEC_ID_PCM_S16BE: + if (version != 4) + start = avio_rl32(pb); + + if (pb->seekable) + st->duration = (avio_size(pb) - start) / 2 / codec->channels; + break; + } + + avio_skip(pb, start - avio_tell(pb)); + + avpriv_set_pts_info(st, 64, 1, codec->sample_rate); + + return 0; +} + +static int rsd_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + AVCodecContext *codec = s->streams[0]->codec; + int ret, size = 1024; + + if (url_feof(s->pb)) + return AVERROR_EOF; + + if (codec->codec_id == AV_CODEC_ID_ADPCM_IMA_RAD) + ret = av_get_packet(s->pb, pkt, codec->block_align); + else + ret = av_get_packet(s->pb, pkt, size); + + if (ret != size) { + if (ret < 0) { + av_free_packet(pkt); + return ret; + } + av_shrink_packet(pkt, ret); + } + pkt->stream_index = 0; + + return ret; +} + +AVInputFormat ff_rsd_demuxer = { + .name = "rsd", + .long_name = NULL_IF_CONFIG_SMALL("GameCube RSD"), + .read_probe = rsd_probe, + .read_header = rsd_read_header, + .read_packet = rsd_read_packet, + .extensions = "rsd", + .codec_tag = (const AVCodecTag* const []){rsd_tags, 0}, +}; diff --git a/libavformat/rso.c b/libavformat/rso.c index 4ce61e7a10..7d88f0f5bc 100644 --- a/libavformat/rso.c +++ b/libavformat/rso.c @@ -2,20 +2,20 @@ * RSO format common data * Copyright (c) 2010 Rafael Carre * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/rso.h b/libavformat/rso.h index e3e88ea644..1f65dd90c7 100644 --- a/libavformat/rso.h +++ b/libavformat/rso.h @@ -2,20 +2,20 @@ * RSO format common data * Copyright (c) 2010 Rafael Carre * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/rsodec.c b/libavformat/rsodec.c index 1612572b56..6e3ea0a153 100644 --- a/libavformat/rsodec.c +++ b/libavformat/rsodec.c @@ -3,20 +3,20 @@ * Copyright (c) 2001 Fabrice Bellard (original AU code) * Copyright (c) 2010 Rafael Carre * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -65,36 +65,19 @@ static int rso_read_header(AVFormatContext *s) st->codec->channels = 1; st->codec->channel_layout = AV_CH_LAYOUT_MONO; st->codec->sample_rate = rate; + st->codec->block_align = 1; avpriv_set_pts_info(st, 64, 1, rate); return 0; } -#define BLOCK_SIZE 1024 /* in samples */ - -static int rso_read_packet(AVFormatContext *s, AVPacket *pkt) -{ - int bps = av_get_bits_per_sample(s->streams[0]->codec->codec_id); - int ret = av_get_packet(s->pb, pkt, BLOCK_SIZE * bps >> 3); - - if (ret < 0) - return ret; - - pkt->stream_index = 0; - - /* note: we need to modify the packet size here to handle the last packet */ - pkt->size = ret; - - return 0; -} - AVInputFormat ff_rso_demuxer = { .name = "rso", .long_name = NULL_IF_CONFIG_SMALL("Lego Mindstorms RSO"), .extensions = "rso", .read_header = rso_read_header, - .read_packet = rso_read_packet, + .read_packet = ff_pcm_read_packet, .read_seek = ff_pcm_read_seek, .codec_tag = (const AVCodecTag* const []){ff_codec_rso_tags, 0}, }; diff --git a/libavformat/rsoenc.c b/libavformat/rsoenc.c index cc76f9afd2..ee679b68f7 100644 --- a/libavformat/rsoenc.c +++ b/libavformat/rsoenc.c @@ -3,20 +3,20 @@ * Copyright (c) 2001 Fabrice Bellard (original AU code) * Copyright (c) 2010 Rafael Carre * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/rtmp.h b/libavformat/rtmp.h index dc7ba5bdeb..8fc8040d89 100644 --- a/libavformat/rtmp.h +++ b/libavformat/rtmp.h @@ -2,20 +2,20 @@ * RTMP definitions * Copyright (c) 2009 Konstantin Shishkov * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/rtmpcrypt.c b/libavformat/rtmpcrypt.c index dfdd029a53..2312527d37 100644 --- a/libavformat/rtmpcrypt.c +++ b/libavformat/rtmpcrypt.c @@ -4,20 +4,20 @@ * Copyright (c) 2009-2010 Howard Chu * Copyright (c) 2012 Samuel Pitoiset * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/rtmpcrypt.h b/libavformat/rtmpcrypt.h index 27994333df..590a8c81b8 100644 --- a/libavformat/rtmpcrypt.h +++ b/libavformat/rtmpcrypt.h @@ -2,20 +2,20 @@ * RTMPE encryption utilities * Copyright (c) 2012 Samuel Pitoiset * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/rtmpdh.c b/libavformat/rtmpdh.c index 38c2f3df63..7d6734daac 100644 --- a/libavformat/rtmpdh.c +++ b/libavformat/rtmpdh.c @@ -4,20 +4,20 @@ * Copyright (c) 2009-2010 Howard Chu * Copyright (c) 2012 Samuel Pitoiset * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/rtmpdh.h b/libavformat/rtmpdh.h index 5de8bde821..cf262fc245 100644 --- a/libavformat/rtmpdh.h +++ b/libavformat/rtmpdh.h @@ -2,20 +2,20 @@ * RTMP Diffie-Hellmann utilities * Copyright (c) 2012 Samuel Pitoiset * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/rtmphttp.c b/libavformat/rtmphttp.c index 89a661435c..49909ee8fd 100644 --- a/libavformat/rtmphttp.c +++ b/libavformat/rtmphttp.c @@ -2,20 +2,20 @@ * RTMP HTTP network protocol * Copyright (c) 2012 Samuel Pitoiset * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/rtmppkt.c b/libavformat/rtmppkt.c index f58f668b53..375ae2fcb1 100644 --- a/libavformat/rtmppkt.c +++ b/libavformat/rtmppkt.c @@ -2,20 +2,20 @@ * RTMP input format * Copyright (c) 2009 Konstantin Shishkov * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/rtmppkt.h b/libavformat/rtmppkt.h index 7121d7e268..fb79fedac6 100644 --- a/libavformat/rtmppkt.h +++ b/libavformat/rtmppkt.h @@ -2,20 +2,20 @@ * RTMP packet utilities * Copyright (c) 2009 Konstantin Shishkov * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/rtmpproto.c b/libavformat/rtmpproto.c index 027f6f6941..a4d7f0ee73 100644 --- a/libavformat/rtmpproto.c +++ b/libavformat/rtmpproto.c @@ -2,20 +2,20 @@ * RTMP network protocol * Copyright (c) 2009 Konstantin Shishkov * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -48,7 +48,7 @@ #include <zlib.h> #endif -#define APP_MAX_LENGTH 128 +#define APP_MAX_LENGTH 1024 #define PLAYPATH_MAX_LENGTH 256 #define TCURL_MAX_LENGTH 512 #define FLASHVER_MAX_LENGTH 64 @@ -269,9 +269,6 @@ static int rtmp_write_amf_data(URLContext *s, char *param, uint8_t **p) *value = '\0'; value++; - if (!field || !value) - goto fail; - ff_amf_write_field_name(p, field); } else { goto fail; @@ -318,7 +315,7 @@ static int gen_connect(URLContext *s, RTMPContext *rt) int ret; if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE, - 0, 4096)) < 0) + 0, 4096 + APP_MAX_LENGTH)) < 0) return ret; p = pkt.data; @@ -1171,7 +1168,7 @@ static int rtmp_handshake(URLContext *s, RTMPContext *rt) for (i = 9; i <= RTMP_HANDSHAKE_PACKET_SIZE; i++) tosend[i] = av_lfg_get(&rnd) >> 24; - if (rt->encrypted && CONFIG_FFRTMPCRYPT_PROTOCOL) { + if (CONFIG_FFRTMPCRYPT_PROTOCOL && rt->encrypted) { /* When the client wants to use RTMPE, we have to change the command * byte to 0x06 which means to use encrypted data and we have to set * the flash version to at least 9.0.115.0. */ @@ -1249,7 +1246,7 @@ static int rtmp_handshake(URLContext *s, RTMPContext *rt) if (ret < 0) return ret; - if (rt->encrypted && CONFIG_FFRTMPCRYPT_PROTOCOL) { + if (CONFIG_FFRTMPCRYPT_PROTOCOL && rt->encrypted) { /* Compute the shared secret key sent by the server and initialize * the RC4 encryption. */ if ((ret = ff_rtmpe_compute_secret_key(rt->stream, serverdata + 1, @@ -1279,7 +1276,7 @@ static int rtmp_handshake(URLContext *s, RTMPContext *rt) if (ret < 0) return ret; - if (rt->encrypted && CONFIG_FFRTMPCRYPT_PROTOCOL) { + if (CONFIG_FFRTMPCRYPT_PROTOCOL && rt->encrypted) { /* Encrypt the signature to be send to the server. */ ff_rtmpe_encrypt_sig(rt->stream, tosend + RTMP_HANDSHAKE_PACKET_SIZE - 32, digest, @@ -1291,13 +1288,13 @@ static int rtmp_handshake(URLContext *s, RTMPContext *rt) RTMP_HANDSHAKE_PACKET_SIZE)) < 0) return ret; - if (rt->encrypted && CONFIG_FFRTMPCRYPT_PROTOCOL) { + if (CONFIG_FFRTMPCRYPT_PROTOCOL && rt->encrypted) { /* Set RC4 keys for encryption and update the keystreams. */ if ((ret = ff_rtmpe_update_keystream(rt->stream)) < 0) return ret; } } else { - if (rt->encrypted && CONFIG_FFRTMPCRYPT_PROTOCOL) { + if (CONFIG_FFRTMPCRYPT_PROTOCOL && rt->encrypted) { /* Compute the shared secret key sent by the server and initialize * the RC4 encryption. */ if ((ret = ff_rtmpe_compute_secret_key(rt->stream, serverdata + 1, @@ -1315,7 +1312,7 @@ static int rtmp_handshake(URLContext *s, RTMPContext *rt) RTMP_HANDSHAKE_PACKET_SIZE)) < 0) return ret; - if (rt->encrypted && CONFIG_FFRTMPCRYPT_PROTOCOL) { + if (CONFIG_FFRTMPCRYPT_PROTOCOL && rt->encrypted) { /* Set RC4 keys for encryption and update the keystreams. */ if ((ret = ff_rtmpe_update_keystream(rt->stream)) < 0) return ret; @@ -2272,7 +2269,7 @@ static int get_packet(URLContext *s, int for_header) } } rt->bytes_read += ret; - if (rt->bytes_read > rt->last_bytes_read + rt->client_report_size) { + if (rt->bytes_read - rt->last_bytes_read > rt->client_report_size) { av_log(s, AV_LOG_DEBUG, "Sending bytes read report\n"); if ((ret = gen_bytes_read(s, rt, rpkt.timestamp + 1)) < 0) return ret; @@ -2483,16 +2480,20 @@ reconnect: fname = strchr(p + 1, '/'); if (!fname || (c && c < fname)) { fname = p + 1; - av_strlcpy(rt->app, path + 1, p - path); + av_strlcpy(rt->app, path + 1, FFMIN(p - path, APP_MAX_LENGTH)); } else { fname++; - av_strlcpy(rt->app, path + 1, fname - path - 1); + av_strlcpy(rt->app, path + 1, FFMIN(fname - path - 1, APP_MAX_LENGTH)); } } } if (old_app) { // The name of application has been defined by the user, override it. + if (strlen(old_app) >= APP_MAX_LENGTH) { + ret = AVERROR(EINVAL); + goto fail; + } av_free(rt->app); rt->app = old_app; } diff --git a/libavformat/rtp.c b/libavformat/rtp.c index 0a3c411f51..4d41350f3c 100644 --- a/libavformat/rtp.c +++ b/libavformat/rtp.c @@ -2,23 +2,24 @@ * RTP input/output format * Copyright (c) 2002 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "libavutil/avstring.h" #include "libavutil/opt.h" #include "avformat.h" @@ -144,7 +145,7 @@ enum AVCodecID ff_rtp_codec_id(const char *buf, enum AVMediaType codec_type) int i; for (i = 0; rtp_payload_types[i].pt >= 0; i++) - if (!strcmp(buf, rtp_payload_types[i].enc_name) && (codec_type == rtp_payload_types[i].codec_type)) + if (!av_strcasecmp(buf, rtp_payload_types[i].enc_name) && (codec_type == rtp_payload_types[i].codec_type)) return rtp_payload_types[i].codec_id; return AV_CODEC_ID_NONE; diff --git a/libavformat/rtp.h b/libavformat/rtp.h index feaf167965..66a4f3afbd 100644 --- a/libavformat/rtp.h +++ b/libavformat/rtp.h @@ -2,20 +2,20 @@ * RTP definitions * Copyright (c) 2002 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef AVFORMAT_RTP_H diff --git a/libavformat/rtpdec.c b/libavformat/rtpdec.c index 3984489e2c..fed4e57f75 100644 --- a/libavformat/rtpdec.c +++ b/libavformat/rtpdec.c @@ -2,20 +2,20 @@ * RTP input format * Copyright (c) 2002 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -837,7 +837,7 @@ int ff_parse_fmtp(AVStream *stream, PayloadContext *data, const char *p, int value_size = strlen(p) + 1; if (!(value = av_malloc(value_size))) { - av_log(NULL, AV_LOG_ERROR, "Failed to allocate data for FMTP."); + av_log(NULL, AV_LOG_ERROR, "Failed to allocate data for FMTP.\n"); return AVERROR(ENOMEM); } diff --git a/libavformat/rtpdec.h b/libavformat/rtpdec.h index 6b161172fd..9321066ba7 100644 --- a/libavformat/rtpdec.h +++ b/libavformat/rtpdec.h @@ -3,20 +3,20 @@ * Copyright (c) 2002 Fabrice Bellard * Copyright (c) 2006 Ryan Martell <rdm4@martellventures.com> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/rtpdec_amr.c b/libavformat/rtpdec_amr.c index fd18ff22d4..211cf1798f 100644 --- a/libavformat/rtpdec_amr.c +++ b/libavformat/rtpdec_amr.c @@ -2,20 +2,20 @@ * RTP AMR Depacketizer, RFC 3267 * Copyright (c) 2010 Martin Storsjo * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/rtpdec_asf.c b/libavformat/rtpdec_asf.c index 7c893af396..123894f275 100644 --- a/libavformat/rtpdec_asf.c +++ b/libavformat/rtpdec_asf.c @@ -2,20 +2,20 @@ * Microsoft RTP/ASF support. * Copyright (c) 2008 Ronald S. Bultje * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/rtpdec_formats.h b/libavformat/rtpdec_formats.h index e5f4ccb777..9caeb5c71d 100644 --- a/libavformat/rtpdec_formats.h +++ b/libavformat/rtpdec_formats.h @@ -2,20 +2,20 @@ * RTP depacketizer declarations * Copyright (c) 2010 Martin Storsjo * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/rtpdec_g726.c b/libavformat/rtpdec_g726.c index 7b3f6cbb89..7e9ef56e06 100644 --- a/libavformat/rtpdec_g726.c +++ b/libavformat/rtpdec_g726.c @@ -1,20 +1,20 @@ /* * Copyright (c) 2011 Miroslav Slugeň <Thunder.m@seznam.cz> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/rtpdec_h263.c b/libavformat/rtpdec_h263.c index b3714910eb..ae4cc27040 100644 --- a/libavformat/rtpdec_h263.c +++ b/libavformat/rtpdec_h263.c @@ -2,20 +2,20 @@ * RTP H.263 Depacketizer, RFC 4629 * Copyright (c) 2010 Martin Storsjo * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/rtpdec_h263_rfc2190.c b/libavformat/rtpdec_h263_rfc2190.c index 116db75065..a2279013dc 100644 --- a/libavformat/rtpdec_h263_rfc2190.c +++ b/libavformat/rtpdec_h263_rfc2190.c @@ -8,20 +8,20 @@ * Copyright 2007 Collabora Ltd, Philippe Kalaf * Copyright 2010 Mark Nauwelaerts * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/rtpdec_h264.c b/libavformat/rtpdec_h264.c index 5e0169bab3..be657c0ccc 100644 --- a/libavformat/rtpdec_h264.c +++ b/libavformat/rtpdec_h264.c @@ -2,20 +2,20 @@ * RTP H264 Protocol (RFC3984) * Copyright (c) 2006 Ryan Martell * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/rtpdec_ilbc.c b/libavformat/rtpdec_ilbc.c index 2e99734f2a..2bd86147de 100644 --- a/libavformat/rtpdec_ilbc.c +++ b/libavformat/rtpdec_ilbc.c @@ -2,20 +2,20 @@ * RTP iLBC Depacketizer, RFC 3952 * Copyright (c) 2012 Martin Storsjo * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/rtpdec_jpeg.c b/libavformat/rtpdec_jpeg.c index ed9c86ceb9..80fe2952b3 100644 --- a/libavformat/rtpdec_jpeg.c +++ b/libavformat/rtpdec_jpeg.c @@ -2,20 +2,20 @@ * RTP JPEG-compressed Video Depacketizer, RFC 2435 * Copyright (c) 2012 Samuel Pitoiset * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/rtpdec_latm.c b/libavformat/rtpdec_latm.c index 050595c775..fdeff3a151 100644 --- a/libavformat/rtpdec_latm.c +++ b/libavformat/rtpdec_latm.c @@ -2,20 +2,20 @@ * RTP Depacketization of MP4A-LATM, RFC 3016 * Copyright (c) 2010 Martin Storsjo * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -130,10 +130,7 @@ static int parse_fmtp_config(AVStream *st, char *value) goto end; } av_freep(&st->codec->extradata); - st->codec->extradata_size = (get_bits_left(&gb) + 7)/8; - st->codec->extradata = av_mallocz(st->codec->extradata_size + - FF_INPUT_BUFFER_PADDING_SIZE); - if (!st->codec->extradata) { + if (ff_alloc_extradata(st->codec, (get_bits_left(&gb) + 7)/8)) { ret = AVERROR(ENOMEM); goto end; } diff --git a/libavformat/rtpdec_mpeg12.c b/libavformat/rtpdec_mpeg12.c index b059fcfa5b..d73ff1e144 100644 --- a/libavformat/rtpdec_mpeg12.c +++ b/libavformat/rtpdec_mpeg12.c @@ -2,20 +2,20 @@ * Common code for the RTP depacketization of MPEG-1/2 formats. * Copyright (c) 2002 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/rtpdec_mpeg4.c b/libavformat/rtpdec_mpeg4.c index bec5c7d55c..0ea5594c06 100644 --- a/libavformat/rtpdec_mpeg4.c +++ b/libavformat/rtpdec_mpeg4.c @@ -3,20 +3,20 @@ * Copyright (c) 2010 Fabrice Bellard * Romain Degez * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -105,10 +105,8 @@ static int parse_fmtp_config(AVCodecContext *codec, char *value) /* decode the hexa encoded parameter */ int len = ff_hex_to_data(NULL, value); av_free(codec->extradata); - codec->extradata = av_mallocz(len + FF_INPUT_BUFFER_PADDING_SIZE); - if (!codec->extradata) + if (ff_alloc_extradata(codec, len)) return AVERROR(ENOMEM); - codec->extradata_size = len; ff_hex_to_data(codec->extradata, value); return 0; } @@ -139,7 +137,7 @@ static int rtp_parse_mp4_au(PayloadContext *data, const uint8_t *buf, int len) init_get_bits(&getbitcontext, buf, data->au_headers_length_bytes * 8); - /* XXX: Wrong if optionnal additional sections are present (cts, dts etc...) */ + /* XXX: Wrong if optional additional sections are present (cts, dts etc...) */ au_header_size = data->sizelength + data->indexlength; if (au_header_size <= 0 || (au_headers_length % au_header_size != 0)) return -1; diff --git a/libavformat/rtpdec_mpegts.c b/libavformat/rtpdec_mpegts.c index b3ef261336..5b851c4fd7 100644 --- a/libavformat/rtpdec_mpegts.c +++ b/libavformat/rtpdec_mpegts.c @@ -2,20 +2,20 @@ * RTP MPEG2TS depacketizer * Copyright (c) 2003 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/rtpdec_qcelp.c b/libavformat/rtpdec_qcelp.c index 45a89ae4f7..eebcdd025b 100644 --- a/libavformat/rtpdec_qcelp.c +++ b/libavformat/rtpdec_qcelp.c @@ -2,20 +2,20 @@ * RTP Depacketization of QCELP/PureVoice, RFC 2658 * Copyright (c) 2010 Martin Storsjo * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/rtpdec_qdm2.c b/libavformat/rtpdec_qdm2.c index 0d7b5bb87a..e1dd62fb8e 100644 --- a/libavformat/rtpdec_qdm2.c +++ b/libavformat/rtpdec_qdm2.c @@ -2,20 +2,20 @@ * QDesign Music 2 (QDM2) payload for RTP * Copyright (c) 2010 Ronald S. Bultje * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -26,8 +26,10 @@ */ #include <string.h> +#include "libavutil/avassert.h" #include "libavutil/intreadwrite.h" #include "libavcodec/avcodec.h" +#include "internal.h" #include "rtp.h" #include "rtpdec.h" #include "rtpdec_formats.h" @@ -103,9 +105,7 @@ static int qdm2_parse_config(PayloadContext *qdm, AVStream *st, if (item_len < 30) return AVERROR_INVALIDDATA; av_freep(&st->codec->extradata); - st->codec->extradata_size = 26 + item_len; - if (!(st->codec->extradata = av_mallocz(st->codec->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE))) { - st->codec->extradata_size = 0; + if (ff_alloc_extradata(st->codec, 26 + item_len)) { return AVERROR(ENOMEM); } AV_WB32(st->codec->extradata, 12); @@ -193,7 +193,7 @@ static int qdm2_restore_block(PayloadContext *qdm, AVStream *st, AVPacket *pkt) for (n = 0; n < 0x80; n++) if (qdm->len[n] > 0) break; - assert(n < 0x80); + av_assert0(n < 0x80); if ((res = av_new_packet(pkt, qdm->block_size)) < 0) return res; diff --git a/libavformat/rtpdec_qt.c b/libavformat/rtpdec_qt.c index 2d9c603fcd..ee8a48aeb5 100644 --- a/libavformat/rtpdec_qt.c +++ b/libavformat/rtpdec_qt.c @@ -2,20 +2,20 @@ * RTP/Quicktime support. * Copyright (c) 2009 Ronald S. Bultje * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/rtpdec_svq3.c b/libavformat/rtpdec_svq3.c index aa880e5954..98e8b58673 100644 --- a/libavformat/rtpdec_svq3.c +++ b/libavformat/rtpdec_svq3.c @@ -2,20 +2,20 @@ * Sorenson-3 (SVQ3/SV3V) payload for RTP * Copyright (c) 2010 Ronald S. Bultje * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -28,6 +28,7 @@ #include <string.h> #include "libavutil/intreadwrite.h" +#include "internal.h" #include "rtp.h" #include "rtpdec.h" #include "rtpdec_formats.h" @@ -60,11 +61,9 @@ static int svq3_parse_packet (AVFormatContext *s, PayloadContext *sv, av_freep(&st->codec->extradata); st->codec->extradata_size = 0; - if (len < 2 || !(st->codec->extradata = - av_malloc(len + 8 + FF_INPUT_BUFFER_PADDING_SIZE))) + if (len < 2 || ff_alloc_extradata(st->codec, len + 8)) return AVERROR_INVALIDDATA; - st->codec->extradata_size = len + 8; memcpy(st->codec->extradata, "SEQH", 4); AV_WB32(st->codec->extradata + 4, len); memcpy(st->codec->extradata + 8, buf, len); diff --git a/libavformat/rtpdec_vp8.c b/libavformat/rtpdec_vp8.c index fc86ac1dd3..b4b4f2207d 100644 --- a/libavformat/rtpdec_vp8.c +++ b/libavformat/rtpdec_vp8.c @@ -3,20 +3,20 @@ * Copyright (c) 2010 Josh Allmann * Copyright (c) 2012 Martin Storsjo * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/rtpdec_xiph.c b/libavformat/rtpdec_xiph.c index 2049e4f3d3..887a65ed65 100644 --- a/libavformat/rtpdec_xiph.c +++ b/libavformat/rtpdec_xiph.c @@ -3,20 +3,20 @@ * Copyright (c) 2009 Colin McQuillian * Copyright (c) 2010 Josh Allmann * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -28,12 +28,12 @@ */ #include "libavutil/attributes.h" +#include "libavutil/avassert.h" #include "libavutil/avstring.h" #include "libavutil/base64.h" #include "libavcodec/bytestream.h" -#include <assert.h> - +#include "internal.h" #include "rtpdec.h" #include "rtpdec_formats.h" @@ -192,7 +192,7 @@ static int xiph_handle_packet(AVFormatContext *ctx, PayloadContext *data, data->timestamp = *timestamp; } else { - assert(fragmented < 4); + av_assert1(fragmented < 4); if (data->timestamp != *timestamp) { // skip if fragmented timestamp is incorrect; // a start packet has been lost somewhere @@ -289,11 +289,11 @@ parse_packed_headers(const uint8_t * packed_headers, * -- FF_INPUT_BUFFER_PADDING_SIZE required */ extradata_alloc = length + length/255 + 3 + FF_INPUT_BUFFER_PADDING_SIZE; - ptr = codec->extradata = av_malloc(extradata_alloc); - if (!ptr) { + if (ff_alloc_extradata(codec, extradata_alloc)) { av_log(codec, AV_LOG_ERROR, "Out of memory\n"); return AVERROR(ENOMEM); } + ptr = codec->extradata; *ptr++ = 2; ptr += av_xiphlacing(ptr, length1); ptr += av_xiphlacing(ptr, length2); diff --git a/libavformat/rtpenc.c b/libavformat/rtpenc.c index 83167eba9c..6fdc908d0f 100644 --- a/libavformat/rtpenc.c +++ b/libavformat/rtpenc.c @@ -2,20 +2,20 @@ * RTP output format * Copyright (c) 2002 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -96,7 +96,7 @@ static int rtp_write_header(AVFormatContext *s1) } st = s1->streams[0]; if (!is_supported(st->codec->codec_id)) { - av_log(s1, AV_LOG_ERROR, "Unsupported codec %x\n", st->codec->codec_id); + av_log(s1, AV_LOG_ERROR, "Unsupported codec %s\n", avcodec_get_name(st->codec->codec_id)); return -1; } @@ -126,9 +126,12 @@ static int rtp_write_header(AVFormatContext *s1) // Pick a random sequence start number, but in the lower end of the // available range, so that any wraparound doesn't happen immediately. // (Immediate wraparound would be an issue for SRTP.) - if (s->seq < 0) - s->seq = av_get_random_seed() & 0x0fff; - else + if (s->seq < 0) { + if (st->codec->flags & CODEC_FLAG_BITEXACT) { + s->seq = 0; + } else + s->seq = av_get_random_seed() & 0x0fff; + } else s->seq &= 0xffff; // Use the given parameter, wrapped to the right interval if (s1->packet_size) { diff --git a/libavformat/rtpenc.h b/libavformat/rtpenc.h index 35ee1513fd..913d05a73e 100644 --- a/libavformat/rtpenc.h +++ b/libavformat/rtpenc.h @@ -2,20 +2,20 @@ * RTP muxer definitions * Copyright (c) 2002 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef AVFORMAT_RTPENC_H @@ -92,7 +92,7 @@ void ff_rtp_send_xiph(AVFormatContext *s1, const uint8_t *buff, int size); void ff_rtp_send_vp8(AVFormatContext *s1, const uint8_t *buff, int size); void ff_rtp_send_jpeg(AVFormatContext *s1, const uint8_t *buff, int size); -const uint8_t *ff_h263_find_resync_marker_reverse(const uint8_t *restrict start, - const uint8_t *restrict end); +const uint8_t *ff_h263_find_resync_marker_reverse(const uint8_t *av_restrict start, + const uint8_t *av_restrict end); #endif /* AVFORMAT_RTPENC_H */ diff --git a/libavformat/rtpenc_aac.c b/libavformat/rtpenc_aac.c index 1b2fa0a78c..be8f43ae69 100644 --- a/libavformat/rtpenc_aac.c +++ b/libavformat/rtpenc_aac.c @@ -1,20 +1,20 @@ /* * copyright (c) 2007 Luca Abeni * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/rtpenc_amr.c b/libavformat/rtpenc_amr.c index 73da8c8251..bd1c197101 100644 --- a/libavformat/rtpenc_amr.c +++ b/libavformat/rtpenc_amr.c @@ -3,20 +3,20 @@ * Copyright (c) 2007 Luca Abeni * Copyright (c) 2009 Martin Storsjo * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/rtpenc_chain.c b/libavformat/rtpenc_chain.c index 10c4020cfe..dea1f70dac 100644 --- a/libavformat/rtpenc_chain.c +++ b/libavformat/rtpenc_chain.c @@ -2,27 +2,26 @@ * RTP muxer chaining code * Copyright (c) 2010 Martin Storsjo * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "avformat.h" #include "avio_internal.h" #include "rtpenc_chain.h" -#include "avio_internal.h" #include "rtp.h" #include "libavutil/opt.h" @@ -59,6 +58,8 @@ int ff_rtp_chain_mux_open(AVFormatContext **out, AVFormatContext *s, rtpctx->max_delay = s->max_delay; /* Copy other stream parameters. */ rtpctx->streams[0]->sample_aspect_ratio = st->sample_aspect_ratio; + rtpctx->flags |= s->flags & AVFMT_FLAG_MP4A_LATM; + /* Get the payload type from the codec */ if (st->id < RTP_PT_PRIVATE) rtpctx->streams[0]->id = @@ -66,6 +67,7 @@ int ff_rtp_chain_mux_open(AVFormatContext **out, AVFormatContext *s, else rtpctx->streams[0]->id = st->id; + if (av_opt_get(s, "rtpflags", AV_OPT_SEARCH_CHILDREN, &rtpflags) >= 0) av_dict_set(&opts, "rtpflags", rtpflags, AV_DICT_DONT_STRDUP_VAL); diff --git a/libavformat/rtpenc_chain.h b/libavformat/rtpenc_chain.h index 4117239402..21c27c145d 100644 --- a/libavformat/rtpenc_chain.h +++ b/libavformat/rtpenc_chain.h @@ -2,20 +2,20 @@ * RTP muxer chaining code * Copyright (c) 2010 Martin Storsjo * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/rtpenc_h263.c b/libavformat/rtpenc_h263.c index 87f0bd7981..9cea013013 100644 --- a/libavformat/rtpenc_h263.c +++ b/libavformat/rtpenc_h263.c @@ -3,28 +3,28 @@ * Copyright (c) 2009 Luca Abeni * Copyright (c) 2009 Martin Storsjo * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "avformat.h" #include "rtpenc.h" -const uint8_t *ff_h263_find_resync_marker_reverse(const uint8_t *restrict start, - const uint8_t *restrict end) +const uint8_t *ff_h263_find_resync_marker_reverse(const uint8_t *av_restrict start, + const uint8_t *av_restrict end) { const uint8_t *p = end - 1; start += 1; /* Make sure we never return the original start. */ diff --git a/libavformat/rtpenc_h263_rfc2190.c b/libavformat/rtpenc_h263_rfc2190.c index f714d010f4..04930086a0 100644 --- a/libavformat/rtpenc_h263_rfc2190.c +++ b/libavformat/rtpenc_h263_rfc2190.c @@ -2,20 +2,20 @@ * RTP packetization for H.263 video * Copyright (c) 2012 Martin Storsjo * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/rtpenc_h264.c b/libavformat/rtpenc_h264.c index 206d9bafa3..b6c16e17d8 100644 --- a/libavformat/rtpenc_h264.c +++ b/libavformat/rtpenc_h264.c @@ -2,20 +2,20 @@ * RTP packetization for H.264 (RFC3984) * Copyright (c) 2008 Luca Abeni * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/rtpenc_jpeg.c b/libavformat/rtpenc_jpeg.c index 04df6583df..7eb0e23c6f 100644 --- a/libavformat/rtpenc_jpeg.c +++ b/libavformat/rtpenc_jpeg.c @@ -2,20 +2,20 @@ * RTP JPEG-compressed video Packetizer, RFC 2435 * Copyright (c) 2012 Samuel Pitoiset * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/rtpenc_latm.c b/libavformat/rtpenc_latm.c index 64676771a7..4430c44afe 100644 --- a/libavformat/rtpenc_latm.c +++ b/libavformat/rtpenc_latm.c @@ -2,20 +2,20 @@ * RTP Packetization of MPEG-4 Audio (RFC 3016) * Copyright (c) 2011 Juan Carlos Rodriguez <ing.juancarlosrodriguez@hotmail.com> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/rtpenc_mpv.c b/libavformat/rtpenc_mpv.c index 70248c408a..4b45f51772 100644 --- a/libavformat/rtpenc_mpv.c +++ b/libavformat/rtpenc_mpv.c @@ -3,20 +3,20 @@ * Copyright (c) 2002 Fabrice Bellard * Copyright (c) 2007 Luca Abeni * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/rtpenc_vp8.c b/libavformat/rtpenc_vp8.c index d24c8bc40d..671d245758 100644 --- a/libavformat/rtpenc_vp8.c +++ b/libavformat/rtpenc_vp8.c @@ -2,20 +2,20 @@ * RTP VP8 Packetizer * Copyright (c) 2010 Josh Allmann * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/rtpenc_xiph.c b/libavformat/rtpenc_xiph.c index 07086b1a12..bc40cf88f8 100644 --- a/libavformat/rtpenc_xiph.c +++ b/libavformat/rtpenc_xiph.c @@ -2,23 +2,24 @@ * RTP packetization for Xiph audio and video * Copyright (c) 2010 Josh Allmann * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "libavutil/avassert.h" #include "avformat.h" #include "rtpenc.h" @@ -72,7 +73,7 @@ void ff_rtp_send_xiph(AVFormatContext *s1, const uint8_t *buff, int size) uint8_t *ptr = s->buf_ptr + 2 + size; // what we're going to write int remaining = end_ptr - ptr; - assert(s->num_frames <= s->max_frames_per_packet); + av_assert1(s->num_frames <= s->max_frames_per_packet); if ((s->num_frames > 0 && remaining < 0) || s->num_frames == s->max_frames_per_packet) { // send previous packets now; no room for new data diff --git a/libavformat/rtpproto.c b/libavformat/rtpproto.c index 0f31539ebe..a8cbd97708 100644 --- a/libavformat/rtpproto.c +++ b/libavformat/rtpproto.c @@ -2,20 +2,20 @@ * RTP network protocol * Copyright (c) 2002 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -204,6 +204,7 @@ static void build_udp_url(char *buf, int buf_size, url_add_option(buf, buf_size, "pkt_size=%d", max_packet_size); if (connect) url_add_option(buf, buf_size, "connect=1"); + url_add_option(buf, buf_size, "fifo_size=0"); if (include_sources && include_sources[0]) url_add_option(buf, buf_size, "sources=%s", include_sources); if (exclude_sources && exclude_sources[0]) @@ -280,6 +281,7 @@ static int rtp_open(URLContext *h, const char *uri, int flags) char buf[1024]; char path[1024]; const char *p; + int i, max_retry_count = 3; av_url_split(NULL, 0, NULL, 0, hostname, sizeof(hostname), &rtp_port, path, sizeof(path), uri); @@ -327,19 +329,35 @@ static int rtp_open(URLContext *h, const char *uri, int flags) } } - build_udp_url(buf, sizeof(buf), - hostname, rtp_port, local_rtp_port, ttl, max_packet_size, - connect, include_sources, exclude_sources); - if (ffurl_open(&s->rtp_hd, buf, flags, &h->interrupt_callback, NULL) < 0) - goto fail; - if (local_rtp_port>=0 && local_rtcp_port<0) - local_rtcp_port = ff_udp_get_local_port(s->rtp_hd) + 1; - - build_udp_url(buf, sizeof(buf), - hostname, rtcp_port, local_rtcp_port, ttl, max_packet_size, - connect, include_sources, exclude_sources); - if (ffurl_open(&s->rtcp_hd, buf, flags, &h->interrupt_callback, NULL) < 0) - goto fail; + for (i = 0;i < max_retry_count;i++) { + build_udp_url(buf, sizeof(buf), + hostname, rtp_port, local_rtp_port, ttl, max_packet_size, + connect, include_sources, exclude_sources); + if (ffurl_open(&s->rtp_hd, buf, flags, &h->interrupt_callback, NULL) < 0) + goto fail; + local_rtp_port = ff_udp_get_local_port(s->rtp_hd); + if(local_rtp_port == 65535) { + local_rtp_port = -1; + continue; + } + if (local_rtcp_port<0) { + local_rtcp_port = local_rtp_port + 1; + build_udp_url(buf, sizeof(buf), + hostname, rtcp_port, local_rtcp_port, ttl, max_packet_size, + connect, include_sources, exclude_sources); + if (ffurl_open(&s->rtcp_hd, buf, flags, &h->interrupt_callback, NULL) < 0) { + local_rtp_port = local_rtcp_port = -1; + continue; + } + break; + } + build_udp_url(buf, sizeof(buf), + hostname, rtcp_port, local_rtcp_port, ttl, max_packet_size, + connect, include_sources, exclude_sources); + if (ffurl_open(&s->rtcp_hd, buf, flags, &h->interrupt_callback, NULL) < 0) + goto fail; + break; + } /* just to ease handle access. XXX: need to suppress direct handle access */ diff --git a/libavformat/rtpproto.h b/libavformat/rtpproto.h index 7acc80ef8e..5b243fb248 100644 --- a/libavformat/rtpproto.h +++ b/libavformat/rtpproto.h @@ -1,20 +1,20 @@ /* * RTP network protocol * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/rtsp.c b/libavformat/rtsp.c index 70f8e2104e..3b88fc7271 100644 --- a/libavformat/rtsp.c +++ b/libavformat/rtsp.c @@ -2,23 +2,24 @@ * RTSP/SDP client * Copyright (c) 2002 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "libavutil/avassert.h" #include "libavutil/base64.h" #include "libavutil/avstring.h" #include "libavutil/intreadwrite.h" @@ -89,7 +90,9 @@ const AVOption ff_rtsp_options[] = { { "min_port", "Minimum local UDP port", OFFSET(rtp_port_min), AV_OPT_TYPE_INT, {.i64 = RTSP_RTP_PORT_MIN}, 0, 65535, DEC|ENC }, { "max_port", "Maximum local UDP port", OFFSET(rtp_port_max), AV_OPT_TYPE_INT, {.i64 = RTSP_RTP_PORT_MAX}, 0, 65535, DEC|ENC }, { "timeout", "Maximum timeout (in seconds) to wait for incoming connections. -1 is infinite. Implies flag listen", OFFSET(initial_timeout), AV_OPT_TYPE_INT, {.i64 = -1}, INT_MIN, INT_MAX, DEC }, + { "stimeout", "timeout (in micro seconds) of socket i/o operations.", OFFSET(stimeout), AV_OPT_TYPE_INT, {.i64 = 0}, INT_MIN, INT_MAX, DEC }, RTSP_REORDERING_OPTS(), + { "user-agent", "override User-Agent header", OFFSET(user_agent), AV_OPT_TYPE_STRING, {.str = LIBAVFORMAT_IDENT}, 0, 0, DEC }, { NULL }, }; @@ -726,7 +729,7 @@ int ff_rtsp_open_transport_ctx(AVFormatContext *s, RTSPStream *rtsp_st) s->ctx_flags |= AVFMTCTX_NOHEADER; if (s->oformat && CONFIG_RTSP_MUXER) { - int ret = ff_rtp_chain_mux_open(&rtsp_st->transport_priv, s, st, + int ret = ff_rtp_chain_mux_open((AVFormatContext **)&rtsp_st->transport_priv, s, st, rtsp_st->rtp_handle, RTSP_TCP_MAX_PACKET_SIZE, rtsp_st->stream_index); @@ -1218,7 +1221,7 @@ static int rtsp_send_cmd_with_content_async(AVFormatContext *s, if (headers) av_strlcat(buf, headers, sizeof(buf)); av_strlcatf(buf, sizeof(buf), "CSeq: %d\r\n", rt->seq); - av_strlcatf(buf, sizeof(buf), "User-Agent: %s\r\n", LIBAVFORMAT_IDENT); + av_strlcatf(buf, sizeof(buf), "User-Agent: %s\r\n", rt->user_agent); if (rt->session_id[0] != '\0' && (!headers || !strstr(headers, "\nIf-Match:"))) { av_strlcatf(buf, sizeof(buf), "Session: %s\r\n", rt->session_id); @@ -1329,10 +1332,6 @@ int ff_rtsp_make_setup_request(AVFormatContext *s, const char *host, int port, /* default timeout: 1 minute */ rt->timeout = 60; - /* for each stream, make the setup request */ - /* XXX: we assume the same server is used for the control of each - * RTSP stream */ - /* Choose a random starting offset within the first half of the * port range, to allow for a number of ports to try even if the offset * happens to be at the end of the random range. */ @@ -1386,7 +1385,6 @@ int ff_rtsp_make_setup_request(AVFormatContext *s, const char *host, int port, &s->interrupt_callback, NULL)) goto rtp_opened; } - av_log(s, AV_LOG_ERROR, "Unable to open an input RTP port\n"); err = AVERROR(EIO); goto fail; @@ -1703,7 +1701,8 @@ redirect: } } else { /* open the tcp connection */ - ff_url_join(tcpname, sizeof(tcpname), "tcp", NULL, host, port, NULL); + ff_url_join(tcpname, sizeof(tcpname), "tcp", NULL, host, port, + "?timeout=%d", rt->stimeout); if (ffurl_open(&rt->rtsp_hd, tcpname, AVIO_FLAG_READ_WRITE, &s->interrupt_callback, NULL) < 0) { err = AVERROR(EIO); diff --git a/libavformat/rtsp.h b/libavformat/rtsp.h index 7a910b06db..76c7f181cf 100644 --- a/libavformat/rtsp.h +++ b/libavformat/rtsp.h @@ -2,20 +2,20 @@ * RTSP definitions * Copyright (c) 2002 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef AVFORMAT_RTSP_H @@ -75,7 +75,7 @@ enum RTSPControlTransport { #define RTSP_DEFAULT_NB_AUDIO_CHANNELS 1 #define RTSP_DEFAULT_AUDIO_SAMPLERATE 44100 #define RTSP_RTP_PORT_MIN 5000 -#define RTSP_RTP_PORT_MAX 10000 +#define RTSP_RTP_PORT_MAX 65000 /** * This describes a single item in the "Transport:" line of one stream as @@ -391,9 +391,19 @@ typedef struct RTSPState { int initial_timeout; /** + * timeout of socket i/o operations. + */ + int stimeout; + + /** * Size of RTP packet reordering queue. */ int reordering_queue_size; + + /** + * User-Agent string + */ + char *user_agent; } RTSPState; #define RTSP_FLAG_FILTER_SRC 0x1 /**< Filter incoming UDP packets - diff --git a/libavformat/rtspcodes.h b/libavformat/rtspcodes.h index 31ab33699c..4245e48642 100644 --- a/libavformat/rtspcodes.h +++ b/libavformat/rtspcodes.h @@ -2,20 +2,20 @@ * RTSP definitions * copyright (c) 2002 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/rtspdec.c b/libavformat/rtspdec.c index 160d49bb3d..eb650ff42a 100644 --- a/libavformat/rtspdec.c +++ b/libavformat/rtspdec.c @@ -2,20 +2,20 @@ * RTSP demuxer * Copyright (c) 2002 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -738,7 +738,7 @@ redo: id = buf[0]; len = AV_RB16(buf + 1); av_dlog(s, "id=%d len=%d\n", id, len); - if (len > buf_size || len < 12) + if (len > buf_size || len < 8) goto redo; /* get the data */ ret = ffurl_read_complete(rt->rtsp_hd, buf, len); diff --git a/libavformat/rtspenc.c b/libavformat/rtspenc.c index 8a334ec49b..d76ae872eb 100644 --- a/libavformat/rtspenc.c +++ b/libavformat/rtspenc.c @@ -2,20 +2,20 @@ * RTSP muxer * Copyright (c) 2010 Martin Storsjo * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/samidec.c b/libavformat/samidec.c new file mode 100644 index 0000000000..2eaee6f24b --- /dev/null +++ b/libavformat/samidec.c @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2012 Clément BÅ“sch + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * SAMI subtitle demuxer + * @see http://msdn.microsoft.com/en-us/library/ms971327.aspx + */ + +#include "avformat.h" +#include "internal.h" +#include "subtitles.h" +#include "libavcodec/internal.h" +#include "libavutil/avstring.h" +#include "libavutil/bprint.h" +#include "libavutil/intreadwrite.h" + +typedef struct { + FFDemuxSubtitlesQueue q; +} SAMIContext; + +static int sami_probe(AVProbeData *p) +{ + const unsigned char *ptr = p->buf; + + if (AV_RB24(ptr) == 0xEFBBBF) + ptr += 3; /* skip UTF-8 BOM */ + return !strncmp(ptr, "<SAMI>", 6) ? AVPROBE_SCORE_MAX : 0; +} + +static int sami_read_header(AVFormatContext *s) +{ + SAMIContext *sami = s->priv_data; + AVStream *st = avformat_new_stream(s, NULL); + AVBPrint buf, hdr_buf; + char c = 0; + int res = 0, got_first_sync_point = 0; + + if (!st) + return AVERROR(ENOMEM); + avpriv_set_pts_info(st, 64, 1, 1000); + st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE; + st->codec->codec_id = AV_CODEC_ID_SAMI; + + av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED); + av_bprint_init(&hdr_buf, 0, AV_BPRINT_SIZE_UNLIMITED); + + while (!url_feof(s->pb)) { + AVPacket *sub; + const int64_t pos = avio_tell(s->pb) - (c != 0); + int is_sync, n = ff_smil_extract_next_chunk(s->pb, &buf, &c); + + if (n == 0) + break; + + is_sync = !av_strncasecmp(buf.str, "<SYNC", 5); + if (is_sync) + got_first_sync_point = 1; + + if (!got_first_sync_point) { + av_bprintf(&hdr_buf, "%s", buf.str); + } else { + sub = ff_subtitles_queue_insert(&sami->q, buf.str, buf.len, !is_sync); + if (!sub) { + res = AVERROR(ENOMEM); + goto end; + } + if (is_sync) { + const char *p = ff_smil_get_attr_ptr(buf.str, "Start"); + sub->pos = pos; + sub->pts = p ? strtol(p, NULL, 10) : 0; + sub->duration = -1; + } + } + av_bprint_clear(&buf); + } + + res = avpriv_bprint_to_extradata(st->codec, &hdr_buf); + if (res < 0) + goto end; + + ff_subtitles_queue_finalize(&sami->q); + +end: + av_bprint_finalize(&buf, NULL); + return res; +} + +static int sami_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + SAMIContext *sami = s->priv_data; + return ff_subtitles_queue_read_packet(&sami->q, pkt); +} + +static int sami_read_seek(AVFormatContext *s, int stream_index, + int64_t min_ts, int64_t ts, int64_t max_ts, int flags) +{ + SAMIContext *sami = s->priv_data; + return ff_subtitles_queue_seek(&sami->q, s, stream_index, + min_ts, ts, max_ts, flags); +} + +static int sami_read_close(AVFormatContext *s) +{ + SAMIContext *sami = s->priv_data; + ff_subtitles_queue_clean(&sami->q); + return 0; +} + +AVInputFormat ff_sami_demuxer = { + .name = "sami", + .long_name = NULL_IF_CONFIG_SMALL("SAMI subtitle format"), + .priv_data_size = sizeof(SAMIContext), + .read_probe = sami_probe, + .read_header = sami_read_header, + .read_packet = sami_read_packet, + .read_seek2 = sami_read_seek, + .read_close = sami_read_close, + .extensions = "smi,sami", +}; diff --git a/libavformat/sapdec.c b/libavformat/sapdec.c index 0280cead9c..fe7bd825f2 100644 --- a/libavformat/sapdec.c +++ b/libavformat/sapdec.c @@ -2,20 +2,20 @@ * Session Announcement Protocol (RFC 2974) demuxer * Copyright (c) 2010 Martin Storsjo * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/sapenc.c b/libavformat/sapenc.c index 7e2bba7a28..9fc78a83af 100644 --- a/libavformat/sapenc.c +++ b/libavformat/sapenc.c @@ -2,20 +2,20 @@ * Session Announcement Protocol (RFC 2974) muxer * Copyright (c) 2010 Martin Storsjo * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/sauce.c b/libavformat/sauce.c index a125241335..a2347b0197 100644 --- a/libavformat/sauce.c +++ b/libavformat/sauce.c @@ -2,20 +2,20 @@ * SAUCE header parser * Copyright (c) 2010 Peter Ross <pross@xvid.org> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -71,7 +71,7 @@ int ff_sauce_read(AVFormatContext *avctx, uint64_t *fsize, int *got_width, int g if (get_height && t2) avctx->streams[0]->codec->height = t2<<4; } else if (datatype == 5) { - if (filetype > 1) { + if (filetype) { avctx->streams[0]->codec->width = (filetype == 1 ? t1 : filetype) << 4; *got_width = 1; } diff --git a/libavformat/sauce.h b/libavformat/sauce.h index 62d8e688a4..0ba9ae5b4a 100644 --- a/libavformat/sauce.h +++ b/libavformat/sauce.h @@ -2,20 +2,20 @@ * SAUCE header parser * Copyright (c) 2010 Peter Ross <pross@xvid.org> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/sbgdec.c b/libavformat/sbgdec.c new file mode 100644 index 0000000000..36cd8a3ce2 --- /dev/null +++ b/libavformat/sbgdec.c @@ -0,0 +1,1511 @@ +/* + * SBG (SBaGen) file format decoder + * Copyright (c) 2011 Nicolas George + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <stdio.h> +#include <stdlib.h> +#include <time.h> +#include "libavutil/intreadwrite.h" +#include "libavutil/log.h" +#include "libavutil/opt.h" +#include "avformat.h" +#include "internal.h" + +#define SBG_SCALE (1 << 16) +#define DAY (24 * 60 * 60) +#define DAY_TS ((int64_t)DAY * AV_TIME_BASE) + +struct sbg_demuxer { + AVClass *class; + int sample_rate; + int frame_size; + int max_file_size; +}; + +struct sbg_string { + char *s; + char *e; +}; + +enum sbg_fade_type { + SBG_FADE_SILENCE = 0, + SBG_FADE_SAME = 1, + SBG_FADE_ADAPT = 3, +}; + +struct sbg_fade { + int8_t in, out, slide; +}; + +enum sbg_synth_type { + SBG_TYPE_NONE, + SBG_TYPE_SINE, + SBG_TYPE_NOISE, + SBG_TYPE_BELL, + SBG_TYPE_MIX, + SBG_TYPE_SPIN, +}; + +/* bell: freq constant, ampl decreases exponentially, can be approx lin */ + +struct sbg_timestamp { + int64_t t; + char type; /* 0 for relative, 'N' for now, 'T' for absolute */ +}; + +struct sbg_script_definition { + char *name; + int name_len; + int elements, nb_elements; + char type; /* 'S' or 'B' */ +}; + +struct sbg_script_synth { + int carrier; + int beat; + int vol; + enum sbg_synth_type type; + struct { + int l, r; + } ref; +}; + +struct sbg_script_tseq { + struct sbg_timestamp ts; + char *name; + int name_len; + int lock; + struct sbg_fade fade; +}; + +struct sbg_script_event { + int64_t ts; + int64_t ts_int, ts_trans, ts_next; + int elements, nb_elements; + struct sbg_fade fade; +}; + +struct sbg_script { + struct sbg_script_definition *def; + struct sbg_script_synth *synth; + struct sbg_script_tseq *tseq; + struct sbg_script_tseq *block_tseq; + struct sbg_script_event *events; + int nb_def; + int nb_tseq; + int nb_events; + int nb_synth; + int64_t start_ts; + int64_t end_ts; + int64_t opt_fade_time; + int64_t opt_duration; + char *opt_mix; + int sample_rate; + uint8_t opt_start_at_first; + uint8_t opt_end_at_last; +}; + +struct sbg_parser { + void *log; + char *script, *end; + char *cursor; + struct sbg_script scs; + struct sbg_timestamp current_time; + int nb_block_tseq; + int nb_def_max, nb_synth_max, nb_tseq_max, nb_block_tseq_max; + int line_no; + char err_msg[128]; +}; + +enum ws_interval_type { + WS_SINE = MKTAG('S','I','N','E'), + WS_NOISE = MKTAG('N','O','I','S'), +}; + +struct ws_interval { + int64_t ts1, ts2; + enum ws_interval_type type; + uint32_t channels; + int32_t f1, f2; + int32_t a1, a2; + uint32_t phi; +}; + +struct ws_intervals { + struct ws_interval *inter; + int nb_inter; + int max_inter; +}; + +static void *alloc_array_elem(void **array, size_t elsize, + int *size, int *max_size) +{ + void *ret; + + if (*size == *max_size) { + int m = FFMAX(32, FFMIN(*max_size, INT_MAX / 2) * 2); + if (*size >= m) + return NULL; + *array = av_realloc_f(*array, m, elsize); + if (!*array) + return NULL; + *max_size = m; + } + ret = (char *)*array + elsize * *size; + memset(ret, 0, elsize); + (*size)++; + return ret; +} + +static int str_to_time(const char *str, int64_t *rtime) +{ + const char *cur = str; + char *end; + int hours, minutes; + double seconds = 0; + + if (*cur < '0' || *cur > '9') + return 0; + hours = strtol(cur, &end, 10); + if (end == cur || *end != ':' || end[1] < '0' || end[1] > '9') + return 0; + cur = end + 1; + minutes = strtol(cur, &end, 10); + if (end == cur) + return 0; + cur = end; + if (*end == ':'){ + seconds = strtod(cur + 1, &end); + if (end > cur + 1) + cur = end; + } + *rtime = (hours * 3600 + minutes * 60 + seconds) * AV_TIME_BASE; + return cur - str; +} + +static inline int is_space(char c) +{ + return c == ' ' || c == '\t' || c == '\r'; +} + +static inline int scale_double(void *log, double d, double m, int *r) +{ + m *= d * SBG_SCALE; + if (m < INT_MIN || m >= INT_MAX) { + if (log) + av_log(log, AV_LOG_ERROR, "%g is too large\n", d); + return AVERROR(EDOM); + } + *r = m; + return 0; +} + +static int lex_space(struct sbg_parser *p) +{ + char *c = p->cursor; + + while (p->cursor < p->end && is_space(*p->cursor)) + p->cursor++; + return p->cursor > c; +} + +static int lex_char(struct sbg_parser *p, char c) +{ + int r = p->cursor < p->end && *p->cursor == c; + + p->cursor += r; + return r; +} + +static int lex_double(struct sbg_parser *p, double *r) +{ + double d; + char *end; + + if (p->cursor == p->end || is_space(*p->cursor) || *p->cursor == '\n') + return 0; + d = strtod(p->cursor, &end); + if (end > p->cursor) { + *r = d; + p->cursor = end; + return 1; + } + return 0; +} + +static int lex_fixed(struct sbg_parser *p, const char *t, int l) +{ + if (p->end - p->cursor < l || memcmp(p->cursor, t, l)) + return 0; + p->cursor += l; + return 1; +} + +static int lex_line_end(struct sbg_parser *p) +{ + if (p->cursor < p->end && *p->cursor == '#') { + p->cursor++; + while (p->cursor < p->end && *p->cursor != '\n') + p->cursor++; + } + if (p->cursor == p->end) + /* simulate final LF for files lacking it */ + return 1; + if (*p->cursor != '\n') + return 0; + p->cursor++; + p->line_no++; + lex_space(p); + return 1; +} + +static int lex_wsword(struct sbg_parser *p, struct sbg_string *rs) +{ + char *s = p->cursor, *c = s; + + if (s == p->end || *s == '\n') + return 0; + while (c < p->end && *c != '\n' && !is_space(*c)) + c++; + rs->s = s; + rs->e = p->cursor = c; + lex_space(p); + return 1; +} + +static int lex_name(struct sbg_parser *p, struct sbg_string *rs) +{ + char *s = p->cursor, *c = s; + + while (c < p->end && ((*c >= 'a' && *c <= 'z') || (*c >= 'A' && *c <= 'Z') + || (*c >= '0' && *c <= '9') || *c == '_' || *c == '-')) + c++; + if (c == s) + return 0; + rs->s = s; + rs->e = p->cursor = c; + return 1; +} + +static int lex_time(struct sbg_parser *p, int64_t *rt) +{ + int r = str_to_time(p->cursor, rt); + p->cursor += r; + return r > 0; +} + +#define FORWARD_ERROR(c) \ + do { \ + int errcode = c; \ + if (errcode <= 0) \ + return errcode ? errcode : AVERROR_INVALIDDATA; \ + } while(0); + +static int parse_immediate(struct sbg_parser *p) +{ + snprintf(p->err_msg, sizeof(p->err_msg), + "immediate sequences not yet implemented"); + return AVERROR_PATCHWELCOME; +} + +static int parse_preprogrammed(struct sbg_parser *p) +{ + snprintf(p->err_msg, sizeof(p->err_msg), + "preprogrammed sequences not yet implemented"); + return AVERROR_PATCHWELCOME; +} + +static int parse_optarg(struct sbg_parser *p, char o, struct sbg_string *r) +{ + if (!lex_wsword(p, r)) { + snprintf(p->err_msg, sizeof(p->err_msg), + "option '%c' requires an argument", o); + return AVERROR_INVALIDDATA; + } + return 1; +} + +static int parse_options(struct sbg_parser *p) +{ + struct sbg_string ostr, oarg; + char mode = 0; + int r; + char *tptr; + double v; + + if (p->cursor == p->end || *p->cursor != '-') + return 0; + while (lex_char(p, '-') && lex_wsword(p, &ostr)) { + for (; ostr.s < ostr.e; ostr.s++) { + char opt = *ostr.s; + switch (opt) { + case 'S': + p->scs.opt_start_at_first = 1; + break; + case 'E': + p->scs.opt_end_at_last = 1; + break; + case 'i': + mode = 'i'; + break; + case 'p': + mode = 'p'; + break; + case 'F': + FORWARD_ERROR(parse_optarg(p, opt, &oarg)); + v = strtod(oarg.s, &tptr); + if (oarg.e != tptr) { + snprintf(p->err_msg, sizeof(p->err_msg), + "syntax error for option -F"); + return AVERROR_INVALIDDATA; + } + p->scs.opt_fade_time = v * AV_TIME_BASE / 1000; + break; + case 'L': + FORWARD_ERROR(parse_optarg(p, opt, &oarg)); + r = str_to_time(oarg.s, &p->scs.opt_duration); + if (oarg.e != oarg.s + r) { + snprintf(p->err_msg, sizeof(p->err_msg), + "syntax error for option -L"); + return AVERROR_INVALIDDATA; + } + break; + case 'T': + FORWARD_ERROR(parse_optarg(p, opt, &oarg)); + r = str_to_time(oarg.s, &p->scs.start_ts); + if (oarg.e != oarg.s + r) { + snprintf(p->err_msg, sizeof(p->err_msg), + "syntax error for option -T"); + return AVERROR_INVALIDDATA; + } + break; + case 'm': + FORWARD_ERROR(parse_optarg(p, opt, &oarg)); + tptr = av_malloc(oarg.e - oarg.s + 1); + if (!tptr) + return AVERROR(ENOMEM); + memcpy(tptr, oarg.s, oarg.e - oarg.s); + tptr[oarg.e - oarg.s] = 0; + av_free(p->scs.opt_mix); + p->scs.opt_mix = tptr; + break; + case 'q': + FORWARD_ERROR(parse_optarg(p, opt, &oarg)); + v = strtod(oarg.s, &tptr); + if (oarg.e != tptr) { + snprintf(p->err_msg, sizeof(p->err_msg), + "syntax error for option -q"); + return AVERROR_INVALIDDATA; + } + if (v != 1) { + snprintf(p->err_msg, sizeof(p->err_msg), + "speed factor other than 1 not supported"); + return AVERROR_PATCHWELCOME; + } + break; + case 'r': + FORWARD_ERROR(parse_optarg(p, opt, &oarg)); + r = strtol(oarg.s, &tptr, 10); + if (oarg.e != tptr) { + snprintf(p->err_msg, sizeof(p->err_msg), + "syntax error for option -r"); + return AVERROR_INVALIDDATA; + } + if (r < 40) { + snprintf(p->err_msg, sizeof(p->err_msg), + "invalid sample rate"); + return AVERROR_PATCHWELCOME; + } + p->scs.sample_rate = r; + break; + default: + snprintf(p->err_msg, sizeof(p->err_msg), + "unknown option: '%c'", *ostr.s); + return AVERROR_INVALIDDATA; + } + } + } + switch (mode) { + case 'i': + return parse_immediate(p); + case 'p': + return parse_preprogrammed(p); + case 0: + if (!lex_line_end(p)) + return AVERROR_INVALIDDATA; + return 1; + } + return AVERROR_BUG; +} + +static int parse_timestamp(struct sbg_parser *p, + struct sbg_timestamp *rts, int64_t *rrel) +{ + int64_t abs = 0, rel = 0, dt; + char type = 0; + int r; + + if (lex_fixed(p, "NOW", 3)) { + type = 'N'; + r = 1; + } else { + r = lex_time(p, &abs); + if (r) + type = 'T'; + } + while (lex_char(p, '+')) { + if (!lex_time(p, &dt)) + return AVERROR_INVALIDDATA; + rel += dt; + r = 1; + } + if (r) { + if (!lex_space(p)) + return AVERROR_INVALIDDATA; + rts->type = type; + rts->t = abs; + *rrel = rel; + } + return r; +} + +static int parse_fade(struct sbg_parser *p, struct sbg_fade *fr) +{ + struct sbg_fade f = {0}; + + if (lex_char(p, '<')) + f.in = SBG_FADE_SILENCE; + else if (lex_char(p, '-')) + f.in = SBG_FADE_SAME; + else if (lex_char(p, '=')) + f.in = SBG_FADE_ADAPT; + else + return 0; + if (lex_char(p, '>')) + f.out = SBG_FADE_SILENCE; + else if (lex_char(p, '-')) + f.out = SBG_FADE_SAME; + else if (lex_char(p, '=')) + f.out = SBG_FADE_ADAPT; + else + return AVERROR_INVALIDDATA; + *fr = f; + return 1; +} + +static int parse_time_sequence(struct sbg_parser *p, int inblock) +{ + struct sbg_timestamp ts; + int64_t rel_ts; + int r; + struct sbg_fade fade = { SBG_FADE_SAME, SBG_FADE_SAME, 0 }; + struct sbg_string name; + struct sbg_script_tseq *tseq; + + r = parse_timestamp(p, &ts, &rel_ts); + if (!r) + return 0; + if (r < 0) + return r; + if (ts.type) { + if (inblock) + return AVERROR_INVALIDDATA; + p->current_time.type = ts.type; + p->current_time.t = ts.t; + } else if(!inblock && !p->current_time.type) { + snprintf(p->err_msg, sizeof(p->err_msg), + "relative time without previous absolute time"); + return AVERROR_INVALIDDATA; + } + ts.type = p->current_time.type; + ts.t = p->current_time.t + rel_ts; + r = parse_fade(p, &fade); + if (r < 0) + return r; + lex_space(p); + if (!lex_name(p, &name)) + return AVERROR_INVALIDDATA; + lex_space(p); + if (lex_fixed(p, "->", 2)) { + fade.slide = SBG_FADE_ADAPT; + lex_space(p); + } + if (!lex_line_end(p)) + return AVERROR_INVALIDDATA; + tseq = inblock ? + alloc_array_elem((void **)&p->scs.block_tseq, sizeof(*tseq), + &p->nb_block_tseq, &p->nb_block_tseq_max) : + alloc_array_elem((void **)&p->scs.tseq, sizeof(*tseq), + &p->scs.nb_tseq, &p->nb_tseq_max); + if (!tseq) + return AVERROR(ENOMEM); + tseq->ts = ts; + tseq->name = name.s; + tseq->name_len = name.e - name.s; + tseq->fade = fade; + return 1; +} + +static int parse_wave_def(struct sbg_parser *p, int wavenum) +{ + snprintf(p->err_msg, sizeof(p->err_msg), + "waveform definitions not yet implemented"); + return AVERROR_PATCHWELCOME; +} + +static int parse_block_def(struct sbg_parser *p, + struct sbg_script_definition *def) +{ + int r, tseq; + + lex_space(p); + if (!lex_line_end(p)) + return AVERROR_INVALIDDATA; + tseq = p->nb_block_tseq; + while (1) { + r = parse_time_sequence(p, 1); + if (r < 0) + return r; + if (!r) + break; + } + if (!lex_char(p, '}')) + return AVERROR_INVALIDDATA; + lex_space(p); + if (!lex_line_end(p)) + return AVERROR_INVALIDDATA; + def->type = 'B'; + def->elements = tseq; + def->nb_elements = p->nb_block_tseq - tseq; + if (!def->nb_elements) + return AVERROR_INVALIDDATA; + return 1; +} + +static int parse_volume(struct sbg_parser *p, int *vol) +{ + double v; + + if (!lex_char(p, '/')) + return 0; + if (!lex_double(p, &v)) + return AVERROR_INVALIDDATA; + if (scale_double(p->log, v, 0.01, vol)) + return AVERROR(ERANGE); + return 1; +} + +static int parse_synth_channel_sine(struct sbg_parser *p, + struct sbg_script_synth *synth) +{ + double carrierf, beatf; + int carrier, beat, vol; + + if (!lex_double(p, &carrierf)) + return 0; + if (!lex_double(p, &beatf)) + beatf = 0; + FORWARD_ERROR(parse_volume(p, &vol)); + if (scale_double(p->log, carrierf, 1, &carrier) < 0 || + scale_double(p->log, beatf, 1, &beat) < 0) + return AVERROR(EDOM); + synth->type = SBG_TYPE_SINE; + synth->carrier = carrier; + synth->beat = beat; + synth->vol = vol; + return 1; +} + +static int parse_synth_channel_pink(struct sbg_parser *p, + struct sbg_script_synth *synth) +{ + int vol; + + if (!lex_fixed(p, "pink", 4)) + return 0; + FORWARD_ERROR(parse_volume(p, &vol)); + synth->type = SBG_TYPE_NOISE; + synth->vol = vol; + return 1; +} + +static int parse_synth_channel_bell(struct sbg_parser *p, + struct sbg_script_synth *synth) +{ + double carrierf; + int carrier, vol; + + if (!lex_fixed(p, "bell", 4)) + return 0; + if (!lex_double(p, &carrierf)) + return AVERROR_INVALIDDATA; + FORWARD_ERROR(parse_volume(p, &vol)); + if (scale_double(p->log, carrierf, 1, &carrier) < 0) + return AVERROR(EDOM); + synth->type = SBG_TYPE_BELL; + synth->carrier = carrier; + synth->vol = vol; + return 1; +} + +static int parse_synth_channel_mix(struct sbg_parser *p, + struct sbg_script_synth *synth) +{ + int vol; + + if (!lex_fixed(p, "mix", 3)) + return 0; + FORWARD_ERROR(parse_volume(p, &vol)); + synth->type = SBG_TYPE_MIX; + synth->vol = vol; + return 1; +} + +static int parse_synth_channel_spin(struct sbg_parser *p, + struct sbg_script_synth *synth) +{ + double carrierf, beatf; + int carrier, beat, vol; + + if (!lex_fixed(p, "spin:", 5)) + return 0; + if (!lex_double(p, &carrierf)) + return AVERROR_INVALIDDATA; + if (!lex_double(p, &beatf)) + return AVERROR_INVALIDDATA; + FORWARD_ERROR(parse_volume(p, &vol)); + if (scale_double(p->log, carrierf, 1, &carrier) < 0 || + scale_double(p->log, beatf, 1, &beat) < 0) + return AVERROR(EDOM); + synth->type = SBG_TYPE_SPIN; + synth->carrier = carrier; + synth->beat = beat; + synth->vol = vol; + return 1; +} + +static int parse_synth_channel(struct sbg_parser *p) +{ + int r; + struct sbg_script_synth *synth; + + synth = alloc_array_elem((void **)&p->scs.synth, sizeof(*synth), + &p->scs.nb_synth, &p->nb_synth_max); + if (!synth) + return AVERROR(ENOMEM); + r = lex_char(p, '-'); + if (!r) + r = parse_synth_channel_pink(p, synth); + if (!r) + r = parse_synth_channel_bell(p, synth); + if (!r) + r = parse_synth_channel_mix(p, synth); + if (!r) + r = parse_synth_channel_spin(p, synth); + /* Unimplemented: wave%d:%f%f/vol (carrier, beat) */ + if (!r) + r = parse_synth_channel_sine(p, synth); + if (r <= 0) + p->scs.nb_synth--; + return r; +} + +static int parse_synth_def(struct sbg_parser *p, + struct sbg_script_definition *def) +{ + int r, synth; + + synth = p->scs.nb_synth; + while (1) { + r = parse_synth_channel(p); + if (r < 0) + return r; + if (!r || !lex_space(p)) + break; + } + lex_space(p); + if (synth == p->scs.nb_synth) + return AVERROR_INVALIDDATA; + if (!lex_line_end(p)) + return AVERROR_INVALIDDATA; + def->type = 'S'; + def->elements = synth; + def->nb_elements = p->scs.nb_synth - synth; + return 1; +} + +static int parse_named_def(struct sbg_parser *p) +{ + char *cursor_save = p->cursor; + struct sbg_string name; + struct sbg_script_definition *def; + + if (!lex_name(p, &name) || !lex_char(p, ':') || !lex_space(p)) { + p->cursor = cursor_save; + return 0; + } + if (name.e - name.s == 6 && !memcmp(name.s, "wave", 4) && + name.s[4] >= '0' && name.s[4] <= '9' && + name.s[5] >= '0' && name.s[5] <= '9') { + int wavenum = (name.s[4] - '0') * 10 + (name.s[5] - '0'); + return parse_wave_def(p, wavenum); + } + def = alloc_array_elem((void **)&p->scs.def, sizeof(*def), + &p->scs.nb_def, &p->nb_def_max); + if (!def) + return AVERROR(ENOMEM); + def->name = name.s; + def->name_len = name.e - name.s; + if (lex_char(p, '{')) + return parse_block_def(p, def); + return parse_synth_def(p, def); +} + +static void free_script(struct sbg_script *s) +{ + av_freep(&s->def); + av_freep(&s->synth); + av_freep(&s->tseq); + av_freep(&s->block_tseq); + av_freep(&s->events); + av_freep(&s->opt_mix); +} + +static int parse_script(void *log, char *script, int script_len, + struct sbg_script *rscript) +{ + struct sbg_parser sp = { + .log = log, + .script = script, + .end = script + script_len, + .cursor = script, + .line_no = 1, + .err_msg = "", + .scs = { + /* default values */ + .start_ts = AV_NOPTS_VALUE, + .sample_rate = 44100, + .opt_fade_time = 60 * AV_TIME_BASE, + }, + }; + int r; + + lex_space(&sp); + while (sp.cursor < sp.end) { + r = parse_options(&sp); + if (r < 0) + goto fail; + if (!r && !lex_line_end(&sp)) + break; + } + while (sp.cursor < sp.end) { + r = parse_named_def(&sp); + if (!r) + r = parse_time_sequence(&sp, 0); + if (!r) + r = lex_line_end(&sp) ? 1 : AVERROR_INVALIDDATA; + if (r < 0) + goto fail; + } + *rscript = sp.scs; + return 1; +fail: + free_script(&sp.scs); + if (!*sp.err_msg) + if (r == AVERROR_INVALIDDATA) + snprintf(sp.err_msg, sizeof(sp.err_msg), "syntax error"); + if (log && *sp.err_msg) { + const char *ctx = sp.cursor; + const char *ectx = av_x_if_null(memchr(ctx, '\n', sp.end - sp.cursor), + sp.end); + int lctx = ectx - ctx; + const char *quote = "\""; + if (lctx > 0 && ctx[lctx - 1] == '\r') + lctx--; + if (lctx == 0) { + ctx = "the end of line"; + lctx = strlen(ctx); + quote = ""; + } + av_log(log, AV_LOG_ERROR, "Error line %d: %s near %s%.*s%s.\n", + sp.line_no, sp.err_msg, quote, lctx, ctx, quote); + } + return r; +} + +static int read_whole_file(AVIOContext *io, int max_size, char **rbuf) +{ + char *buf = NULL; + int size = 0, bufsize = 0, r; + + while (1) { + if (bufsize - size < 1024) { + bufsize = FFMIN(FFMAX(2 * bufsize, 8192), max_size); + if (bufsize - size < 2) { + size = AVERROR(EFBIG); + goto fail; + } + buf = av_realloc_f(buf, bufsize, 1); + if (!buf) { + size = AVERROR(ENOMEM); + goto fail; + } + } + r = avio_read(io, buf, bufsize - size - 1); + if (r == AVERROR_EOF) + break; + if (r < 0) + goto fail; + size += r; + } + buf[size] = 0; + *rbuf = buf; + return size; +fail: + av_free(buf); + return size; +} + +static void expand_timestamps(void *log, struct sbg_script *s) +{ + int i, nb_rel = 0; + int64_t now, cur_ts, delta = 0; + + for (i = 0; i < s->nb_tseq; i++) + nb_rel += s->tseq[i].ts.type == 'N'; + if (nb_rel == s->nb_tseq) { + /* All ts are relative to NOW: consider NOW = 0 */ + now = 0; + if (s->start_ts != AV_NOPTS_VALUE) + av_log(log, AV_LOG_WARNING, + "Start time ignored in a purely relative script.\n"); + } else if (nb_rel == 0 && s->start_ts != AV_NOPTS_VALUE || + s->opt_start_at_first) { + /* All ts are absolute and start time is specified */ + if (s->start_ts == AV_NOPTS_VALUE) + s->start_ts = s->tseq[0].ts.t; + now = s->start_ts; + } else { + /* Mixed relative/absolute ts: expand */ + time_t now0; + struct tm *tm; + + av_log(log, AV_LOG_WARNING, + "Scripts with mixed absolute and relative timestamps can give " + "unexpected results (pause, seeking, time zone change).\n"); +#undef time + time(&now0); + tm = localtime(&now0); + now = tm ? tm->tm_hour * 3600 + tm->tm_min * 60 + tm->tm_sec : + now0 % DAY; + av_log(log, AV_LOG_INFO, "Using %02d:%02d:%02d as NOW.\n", + (int)(now / 3600), (int)(now / 60) % 60, (int)now % 60); + now *= AV_TIME_BASE; + for (i = 0; i < s->nb_tseq; i++) { + if (s->tseq[i].ts.type == 'N') { + s->tseq[i].ts.t += now; + s->tseq[i].ts.type = 'T'; /* not necessary */ + } + } + } + if (s->start_ts == AV_NOPTS_VALUE) + s->start_ts = s->opt_start_at_first ? s->tseq[0].ts.t : now; + s->end_ts = s->opt_duration ? s->start_ts + s->opt_duration : + AV_NOPTS_VALUE; /* may be overridden later by -E option */ + cur_ts = now; + for (i = 0; i < s->nb_tseq; i++) { + if (s->tseq[i].ts.t + delta < cur_ts) + delta += DAY_TS; + cur_ts = s->tseq[i].ts.t += delta; + } +} + +static int expand_tseq(void *log, struct sbg_script *s, int *nb_ev_max, + int64_t t0, struct sbg_script_tseq *tseq) +{ + int i, r; + struct sbg_script_definition *def; + struct sbg_script_tseq *be; + struct sbg_script_event *ev; + + if (tseq->lock++) { + av_log(log, AV_LOG_ERROR, "Recursion loop on \"%.*s\"\n", + tseq->name_len, tseq->name); + return AVERROR(EINVAL); + } + t0 += tseq->ts.t; + for (i = 0; i < s->nb_def; i++) { + if (s->def[i].name_len == tseq->name_len && + !memcmp(s->def[i].name, tseq->name, tseq->name_len)) + break; + } + if (i >= s->nb_def) { + av_log(log, AV_LOG_ERROR, "Tone-set \"%.*s\" not defined\n", + tseq->name_len, tseq->name); + return AVERROR(EINVAL); + } + def = &s->def[i]; + if (def->type == 'B') { + be = s->block_tseq + def->elements; + for (i = 0; i < def->nb_elements; i++) { + r = expand_tseq(log, s, nb_ev_max, t0, &be[i]); + if (r < 0) + return r; + } + } else { + ev = alloc_array_elem((void **)&s->events, sizeof(*ev), + &s->nb_events, nb_ev_max); + ev->ts = tseq->ts.t; + ev->elements = def->elements; + ev->nb_elements = def->nb_elements; + ev->fade = tseq->fade; + } + tseq->lock--; + return 0; +} + +static int expand_script(void *log, struct sbg_script *s) +{ + int i, r, nb_events_max = 0; + + expand_timestamps(log, s); + for (i = 0; i < s->nb_tseq; i++) { + r = expand_tseq(log, s, &nb_events_max, 0, &s->tseq[i]); + if (r < 0) + return r; + } + if (!s->nb_events) { + av_log(log, AV_LOG_ERROR, "No events in script\n"); + return AVERROR_INVALIDDATA; + } + if (s->opt_end_at_last) + s->end_ts = s->events[s->nb_events - 1].ts; + return 0; +} + +static int add_interval(struct ws_intervals *inter, + enum ws_interval_type type, uint32_t channels, int ref, + int64_t ts1, int32_t f1, int32_t a1, + int64_t ts2, int32_t f2, int32_t a2) +{ + struct ws_interval *i, *ri; + + if (ref >= 0) { + ri = &inter->inter[ref]; + /* ref and new intervals are constant, identical and adjacent */ + if (ri->type == type && ri->channels == channels && + ri->f1 == ri->f2 && ri->f2 == f1 && f1 == f2 && + ri->a1 == ri->a2 && ri->a2 == a1 && a1 == a2 && + ri->ts2 == ts1) { + ri->ts2 = ts2; + return ref; + } + } + i = alloc_array_elem((void **)&inter->inter, sizeof(*i), + &inter->nb_inter, &inter->max_inter); + if (!i) + return AVERROR(ENOMEM); + i->ts1 = ts1; + i->ts2 = ts2; + i->type = type; + i->channels = channels; + i->f1 = f1; + i->f2 = f2; + i->a1 = a1; + i->a2 = a2; + i->phi = ref >= 0 ? ref | 0x80000000 : 0; + return i - inter->inter; +} + +static int add_bell(struct ws_intervals *inter, struct sbg_script *s, + int64_t ts1, int64_t ts2, int32_t f, int32_t a) +{ + /* SBaGen uses an exponential decrease every 50ms. + We approximate it with piecewise affine segments. */ + int32_t cpoints[][2] = { + { 2, a }, + { 4, a - a / 4 }, + { 8, a / 2 }, + { 16, a / 4 }, + { 25, a / 10 }, + { 50, a / 80 }, + { 75, 0 }, + }; + int i, r; + int64_t dt = s->sample_rate / 20, ts3 = ts1, ts4; + for (i = 0; i < FF_ARRAY_ELEMS(cpoints); i++) { + ts4 = FFMIN(ts2, ts1 + cpoints[i][0] * dt); + r = add_interval(inter, WS_SINE, 3, -1, + ts3, f, a, ts4, f, cpoints[i][1]); + if (r < 0) + return r; + ts3 = ts4; + a = cpoints[i][1]; + } + return 0; +} + +static int generate_interval(void *log, struct sbg_script *s, + struct ws_intervals *inter, + int64_t ts1, int64_t ts2, + struct sbg_script_synth *s1, + struct sbg_script_synth *s2, + int transition) +{ + int r; + + if (ts2 <= ts1 || (s1->vol == 0 && s2->vol == 0)) + return 0; + switch (s1->type) { + case SBG_TYPE_NONE: + break; + case SBG_TYPE_SINE: + if (s1->beat == 0 && s2->beat == 0) { + r = add_interval(inter, WS_SINE, 3, s1->ref.l, + ts1, s1->carrier, s1->vol, + ts2, s2->carrier, s2->vol); + if (r < 0) + return r; + s2->ref.l = s2->ref.r = r; + } else { + r = add_interval(inter, WS_SINE, 1, s1->ref.l, + ts1, s1->carrier + s1->beat / 2, s1->vol, + ts2, s2->carrier + s2->beat / 2, s2->vol); + if (r < 0) + return r; + s2->ref.l = r; + r = add_interval(inter, WS_SINE, 2, s1->ref.r, + ts1, s1->carrier - s1->beat / 2, s1->vol, + ts2, s2->carrier - s2->beat / 2, s2->vol); + if (r < 0) + return r; + s2->ref.r = r; + } + break; + + case SBG_TYPE_BELL: + if (transition == 2) { + r = add_bell(inter, s, ts1, ts2, s1->carrier, s2->vol); + if (r < 0) + return r; + } + break; + + case SBG_TYPE_SPIN: + av_log(log, AV_LOG_WARNING, "Spinning noise not implemented, " + "using pink noise instead.\n"); + /* fall through */ + case SBG_TYPE_NOISE: + /* SBaGen's pink noise generator uses: + - 1 band of white noise, mean square: 1/3; + - 9 bands of subsampled white noise with linear + interpolation, mean square: 2/3 each; + with 1/10 weight each: the total mean square is 7/300. + Our pink noise generator uses 8 bands of white noise with + rectangular subsampling: the total mean square is 1/24. + Therefore, to match SBaGen's volume, we must multiply vol by + sqrt((7/300) / (1/24)) = sqrt(14/25) =~ 0.748 + */ + r = add_interval(inter, WS_NOISE, 3, s1->ref.l, + ts1, 0, s1->vol - s1->vol / 4, + ts2, 0, s2->vol - s2->vol / 4); + if (r < 0) + return r; + s2->ref.l = s2->ref.r = r; + break; + + case SBG_TYPE_MIX: + /* Unimplemented: silence; warning present elsewhere */ + default: + av_log(log, AV_LOG_ERROR, + "Type %d is not implemented\n", s1->type); + return AVERROR_PATCHWELCOME; + } + return 0; +} + +static int generate_plateau(void *log, struct sbg_script *s, + struct ws_intervals *inter, + struct sbg_script_event *ev1) +{ + int64_t ts1 = ev1->ts_int, ts2 = ev1->ts_trans; + int i, r; + struct sbg_script_synth *s1; + + for (i = 0; i < ev1->nb_elements; i++) { + s1 = &s->synth[ev1->elements + i]; + r = generate_interval(log, s, inter, ts1, ts2, s1, s1, 0); + if (r < 0) + return r; + } + return 0; +} + +/* + + ts1 ts2 ts1 tsmid ts2 + | | | | | + v v v | v +____ ____ v ____ + ''''.... ''.. ..'' + ''''....____ ''....'' + + compatible transition incompatible transition + */ + +static int generate_transition(void *log, struct sbg_script *s, + struct ws_intervals *inter, + struct sbg_script_event *ev1, + struct sbg_script_event *ev2) +{ + int64_t ts1 = ev1->ts_trans, ts2 = ev1->ts_next; + /* (ts1 + ts2) / 2 without overflow */ + int64_t tsmid = (ts1 >> 1) + (ts2 >> 1) + (ts1 & ts2 & 1); + enum sbg_fade_type type = ev1->fade.slide | (ev1->fade.out & ev2->fade.in); + int nb_elements = FFMAX(ev1->nb_elements, ev2->nb_elements); + struct sbg_script_synth *s1, *s2, s1mod, s2mod, smid; + int pass, i, r; + + for (pass = 0; pass < 2; pass++) { + /* pass = 0 -> compatible and first half of incompatible + pass = 1 -> second half of incompatible + Using two passes like that ensures that the intervals are generated + in increasing order according to their start timestamp. + Otherwise it would be necessary to sort them + while keeping the mutual references. + */ + for (i = 0; i < nb_elements; i++) { + s1 = i < ev1->nb_elements ? &s->synth[ev1->elements + i] : &s1mod; + s2 = i < ev2->nb_elements ? &s->synth[ev2->elements + i] : &s2mod; + s1mod = s1 != &s1mod ? *s1 : (struct sbg_script_synth){ 0 }; + s2mod = s2 != &s2mod ? *s2 : (struct sbg_script_synth){ 0 }; + if (ev1->fade.slide) { + /* for slides, and only for slides, silence ("-") is equivalent + to anything with volume 0 */ + if (s1mod.type == SBG_TYPE_NONE) { + s1mod = s2mod; + s1mod.vol = 0; + } else if (s2mod.type == SBG_TYPE_NONE) { + s2mod = s1mod; + s2mod.vol = 0; + } + } + if (s1mod.type == s2mod.type && + s1mod.type != SBG_TYPE_BELL && + (type == SBG_FADE_ADAPT || + (s1mod.carrier == s2mod.carrier && + s1mod.beat == s2mod.beat))) { + /* compatible: single transition */ + if (!pass) { + r = generate_interval(log, s, inter, + ts1, ts2, &s1mod, &s2mod, 3); + if (r < 0) + return r; + s2->ref = s2mod.ref; + } + } else { + /* incompatible: silence at midpoint */ + if (!pass) { + smid = s1mod; + smid.vol = 0; + r = generate_interval(log, s, inter, + ts1, tsmid, &s1mod, &smid, 1); + if (r < 0) + return r; + } else { + smid = s2mod; + smid.vol = 0; + r = generate_interval(log, s, inter, + tsmid, ts2, &smid, &s2mod, 2); + if (r < 0) + return r; + s2->ref = s2mod.ref; + } + } + } + } + return 0; +} + +/* + ev1 trats ev2 intts endts ev3 + | | | | | | + v v v v v v + ________________ +.... .... .... + '''....________________....''' '''...._______________ + +\_________/\______________/\_________/\______________/\_________/\_____________/ + tr x->1 int1 tr 1->2 int2 tr 2->3 int3 + */ + +static int generate_intervals(void *log, struct sbg_script *s, int sample_rate, + struct ws_intervals *inter) +{ + int64_t trans_time = s->opt_fade_time / 2; + struct sbg_script_event ev0, *ev1, *ev2; + int64_t period; + int i, r; + + /* SBaGen handles the time before and after the extremal events, + and the corresponding transitions, as if the sequence were cyclic + with a 24-hours period. */ + period = s->events[s->nb_events - 1].ts - s->events[0].ts; + period = (period + (DAY_TS - 1)) / DAY_TS * DAY_TS; + period = FFMAX(period, DAY_TS); + + /* Prepare timestamps for transitions */ + for (i = 0; i < s->nb_events; i++) { + ev1 = &s->events[i]; + ev2 = &s->events[(i + 1) % s->nb_events]; + ev1->ts_int = ev1->ts; + ev1->ts_trans = ev1->fade.slide ? ev1->ts + : ev2->ts + (ev1 < ev2 ? 0 : period); + } + for (i = 0; i < s->nb_events; i++) { + ev1 = &s->events[i]; + ev2 = &s->events[(i + 1) % s->nb_events]; + if (!ev1->fade.slide) { + ev1->ts_trans = FFMAX(ev1->ts_int, ev1->ts_trans - trans_time); + ev2->ts_int = FFMIN(ev2->ts_trans, ev2->ts_int + trans_time); + } + ev1->ts_next = ev2->ts_int + (ev1 < ev2 ? 0 : period); + } + + /* Pseudo event before the first one */ + ev0 = s->events[s->nb_events - 1]; + ev0.ts_int -= period; + ev0.ts_trans -= period; + ev0.ts_next -= period; + + /* Convert timestamps */ + for (i = -1; i < s->nb_events; i++) { + ev1 = i < 0 ? &ev0 : &s->events[i]; + ev1->ts_int = av_rescale(ev1->ts_int, sample_rate, AV_TIME_BASE); + ev1->ts_trans = av_rescale(ev1->ts_trans, sample_rate, AV_TIME_BASE); + ev1->ts_next = av_rescale(ev1->ts_next, sample_rate, AV_TIME_BASE); + } + + /* Generate intervals */ + for (i = 0; i < s->nb_synth; i++) + s->synth[i].ref.l = s->synth[i].ref.r = -1; + for (i = -1; i < s->nb_events; i++) { + ev1 = i < 0 ? &ev0 : &s->events[i]; + ev2 = &s->events[(i + 1) % s->nb_events]; + r = generate_plateau(log, s, inter, ev1); + if (r < 0) + return r; + r = generate_transition(log, s, inter, ev1, ev2); + if (r < 0) + return r; + } + if (!inter->nb_inter) + av_log(log, AV_LOG_WARNING, "Completely silent script.\n"); + return 0; +} + +static int encode_intervals(struct sbg_script *s, AVCodecContext *avc, + struct ws_intervals *inter) +{ + int i, edata_size = 4; + uint8_t *edata; + + for (i = 0; i < inter->nb_inter; i++) { + edata_size += inter->inter[i].type == WS_SINE ? 44 : + inter->inter[i].type == WS_NOISE ? 32 : 0; + if (edata_size < 0) + return AVERROR(ENOMEM); + } + if (ff_alloc_extradata(avc, edata_size)) + return AVERROR(ENOMEM); + edata = avc->extradata; + +#define ADD_EDATA32(v) do { AV_WL32(edata, (v)); edata += 4; } while(0) +#define ADD_EDATA64(v) do { AV_WL64(edata, (v)); edata += 8; } while(0) + ADD_EDATA32(inter->nb_inter); + for (i = 0; i < inter->nb_inter; i++) { + ADD_EDATA64(inter->inter[i].ts1); + ADD_EDATA64(inter->inter[i].ts2); + ADD_EDATA32(inter->inter[i].type); + ADD_EDATA32(inter->inter[i].channels); + switch (inter->inter[i].type) { + case WS_SINE: + ADD_EDATA32(inter->inter[i].f1); + ADD_EDATA32(inter->inter[i].f2); + ADD_EDATA32(inter->inter[i].a1); + ADD_EDATA32(inter->inter[i].a2); + ADD_EDATA32(inter->inter[i].phi); + break; + case WS_NOISE: + ADD_EDATA32(inter->inter[i].a1); + ADD_EDATA32(inter->inter[i].a2); + break; + } + } + if (edata != avc->extradata + edata_size) + return AVERROR_BUG; + return 0; +} + +static av_cold int sbg_read_probe(AVProbeData *p) +{ + int r, score; + struct sbg_script script = { 0 }; + + r = parse_script(NULL, p->buf, p->buf_size, &script); + score = r < 0 || !script.nb_def || !script.nb_tseq ? 0 : + AVPROBE_SCORE_MAX / 3; + free_script(&script); + return score; +} + +static av_cold int sbg_read_header(AVFormatContext *avf) +{ + struct sbg_demuxer *sbg = avf->priv_data; + int r; + char *buf = NULL; + struct sbg_script script = { 0 }; + AVStream *st; + struct ws_intervals inter = { 0 }; + + r = read_whole_file(avf->pb, sbg->max_file_size, &buf); + if (r < 0) + goto fail; + r = parse_script(avf, buf, r, &script); + if (r < 0) + goto fail; + if (!sbg->sample_rate) + sbg->sample_rate = script.sample_rate; + else + script.sample_rate = sbg->sample_rate; + if (!sbg->frame_size) + sbg->frame_size = FFMAX(1, sbg->sample_rate / 10); + if (script.opt_mix) + av_log(avf, AV_LOG_WARNING, "Mix feature not implemented: " + "-m is ignored and mix channels will be silent.\n"); + r = expand_script(avf, &script); + if (r < 0) + goto fail; + av_freep(&buf); + r = generate_intervals(avf, &script, sbg->sample_rate, &inter); + if (r < 0) + goto fail; + + st = avformat_new_stream(avf, NULL); + if (!st) + return AVERROR(ENOMEM); + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_id = AV_CODEC_ID_FFWAVESYNTH; + st->codec->channels = 2; + st->codec->channel_layout = AV_CH_LAYOUT_STEREO; + st->codec->sample_rate = sbg->sample_rate; + st->codec->frame_size = sbg->frame_size; + avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate); + st->probe_packets = 0; + st->start_time = av_rescale(script.start_ts, + sbg->sample_rate, AV_TIME_BASE); + st->duration = script.end_ts == AV_NOPTS_VALUE ? AV_NOPTS_VALUE : + av_rescale(script.end_ts - script.start_ts, + sbg->sample_rate, AV_TIME_BASE); + st->cur_dts = st->start_time; + r = encode_intervals(&script, st->codec, &inter); + if (r < 0) + goto fail; + + av_free(inter.inter); + free_script(&script); + return 0; + +fail: + av_free(inter.inter); + free_script(&script); + av_free(buf); + return r; +} + +static int sbg_read_packet(AVFormatContext *avf, AVPacket *packet) +{ + int64_t ts, end_ts; + + ts = avf->streams[0]->cur_dts; + end_ts = ts + avf->streams[0]->codec->frame_size; + if (avf->streams[0]->duration != AV_NOPTS_VALUE) + end_ts = FFMIN(avf->streams[0]->start_time + avf->streams[0]->duration, + end_ts); + if (end_ts <= ts) + return AVERROR_EOF; + if (av_new_packet(packet, 12) < 0) + return AVERROR(ENOMEM); + packet->dts = packet->pts = ts; + packet->duration = end_ts - ts; + AV_WL64(packet->data + 0, ts); + AV_WL32(packet->data + 8, packet->duration); + return packet->size; +} + +static int sbg_read_seek2(AVFormatContext *avf, int stream_index, + int64_t min_ts, int64_t ts, int64_t max_ts, int flags) +{ + if (flags || stream_index > 0) + return AVERROR(EINVAL); + if (stream_index < 0) + ts = av_rescale_q(ts, AV_TIME_BASE_Q, avf->streams[0]->time_base); + avf->streams[0]->cur_dts = ts; + return 0; +} + +static int sbg_read_seek(AVFormatContext *avf, int stream_index, + int64_t ts, int flags) +{ + return sbg_read_seek2(avf, stream_index, ts, ts, ts, 0); +} + +static const AVOption sbg_options[] = { + { "sample_rate", "", offsetof(struct sbg_demuxer, sample_rate), + AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, + AV_OPT_FLAG_DECODING_PARAM }, + { "frame_size", "", offsetof(struct sbg_demuxer, frame_size), + AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, + AV_OPT_FLAG_DECODING_PARAM }, + { "max_file_size", "", offsetof(struct sbg_demuxer, max_file_size), + AV_OPT_TYPE_INT, { .i64 = 5000000 }, 0, INT_MAX, + AV_OPT_FLAG_DECODING_PARAM }, + { NULL }, +}; + +static const AVClass sbg_demuxer_class = { + .class_name = "sbg_demuxer", + .item_name = av_default_item_name, + .option = sbg_options, + .version = LIBAVUTIL_VERSION_INT, +}; + +AVInputFormat ff_sbg_demuxer = { + .name = "sbg", + .long_name = NULL_IF_CONFIG_SMALL("SBaGen binaural beats script"), + .priv_data_size = sizeof(struct sbg_demuxer), + .read_probe = sbg_read_probe, + .read_header = sbg_read_header, + .read_packet = sbg_read_packet, + .read_seek = sbg_read_seek, + .read_seek2 = sbg_read_seek2, + .extensions = "sbg", + .priv_class = &sbg_demuxer_class, +}; diff --git a/libavformat/sctp.c b/libavformat/sctp.c index 66b31ccec6..f88cd2795d 100644 --- a/libavformat/sctp.c +++ b/libavformat/sctp.c @@ -2,20 +2,20 @@ * SCTP protocol * Copyright (c) 2012 Luca Barbato * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -121,7 +121,7 @@ static int ff_sctp_send(int s, const void *msg, size_t len, outmsg.msg_name = NULL; outmsg.msg_namelen = 0; outmsg.msg_iov = &iov; - iov.iov_base = msg; + iov.iov_base = (void*)msg; iov.iov_len = len; outmsg.msg_iovlen = 1; outmsg.msg_controllen = 0; @@ -296,8 +296,10 @@ static int sctp_write(URLContext *h, const uint8_t *buf, int size) /*StreamId is introduced as a 2byte code into the stream*/ struct sctp_sndrcvinfo info = { 0 }; info.sinfo_stream = AV_RB16(buf); - if (info.sinfo_stream > s->max_streams) - abort(); + if (info.sinfo_stream > s->max_streams) { + av_log(h, AV_LOG_ERROR, "bad input data\n"); + return AVERROR(EINVAL); + } ret = ff_sctp_send(s->fd, buf + 2, size - 2, &info, MSG_EOR); } else ret = send(s->fd, buf, size, 0); diff --git a/libavformat/sdp.c b/libavformat/sdp.c index 186f83b1a0..0124218495 100644 --- a/libavformat/sdp.c +++ b/libavformat/sdp.c @@ -1,20 +1,20 @@ /* * copyright (c) 2007 Luca Abeni * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -438,7 +438,7 @@ static char *sdp_write_media_attributes(char *buff, int size, AVCodecContext *c, payload_type, config ? config : ""); break; case AV_CODEC_ID_AAC: - if (fmt && fmt->oformat->priv_class && + if (fmt && fmt->oformat && fmt->oformat->priv_class && av_opt_flag_is_set(fmt->priv_data, "rtpflags", "latm")) { config = latm_context2config(c); if (!config) @@ -575,6 +575,20 @@ static char *sdp_write_media_attributes(char *buff, int size, AVCodecContext *c, case AV_CODEC_ID_SPEEX: av_strlcatf(buff, size, "a=rtpmap:%d speex/%d\r\n", payload_type, c->sample_rate); + if (c->codec) { + const char *mode; + uint64_t vad_option; + + if (c->flags & CODEC_FLAG_QSCALE) + mode = "on"; + else if (!av_opt_get_int(c, "vad", AV_OPT_FLAG_ENCODING_PARAM, &vad_option) && vad_option) + mode = "vad"; + else + mode = "off"; + + av_strlcatf(buff, size, "a=fmtp:%d vbr=%s\r\n", + payload_type, mode); + } break; case AV_CODEC_ID_OPUS: av_strlcatf(buff, size, "a=rtpmap:%d opus/48000\r\n", diff --git a/libavformat/seek-test.c b/libavformat/seek-test.c index 143f0b5ace..8b0611db71 100644 --- a/libavformat/seek-test.c +++ b/libavformat/seek-test.c @@ -2,20 +2,20 @@ * Copyright (c) 2003 Fabrice Bellard * Copyright (c) 2007 Michael Niedermayer * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -45,13 +45,12 @@ static const char *ret_str(int v) static void ts_str(char buffer[60], int64_t ts, AVRational base) { - double tsval; if (ts == AV_NOPTS_VALUE) { strcpy(buffer, " NOPTS "); return; } - tsval = ts * av_q2d(base); - snprintf(buffer, 60, "%9f", tsval); + ts= av_rescale_q(ts, base, (AVRational){1, 1000000}); + snprintf(buffer, 60, "%c%"PRId64".%06"PRId64"", ts<0 ? '-' : ' ', FFABS(ts)/1000000, FFABS(ts)%1000000); } int main(int argc, char **argv) @@ -59,8 +58,28 @@ int main(int argc, char **argv) const char *filename; AVFormatContext *ic = NULL; int i, ret, stream_id; + int j; int64_t timestamp; AVDictionary *format_opts = NULL; + int64_t seekfirst = AV_NOPTS_VALUE; + int firstback=0; + int frame_count = 1; + int duration = 4; + + for(i=2; i<argc; i+=2){ + if (!strcmp(argv[i], "-seekforw")){ + seekfirst = atoi(argv[i+1]); + } else if(!strcmp(argv[i], "-seekback")){ + seekfirst = atoi(argv[i+1]); + firstback = 1; + } else if(!strcmp(argv[i], "-frames")){ + frame_count = atoi(argv[i+1]); + } else if(!strcmp(argv[i], "-duration")){ + duration = atoi(argv[i+1]); + } else { + argc = 1; + } + } av_dict_set(&format_opts, "channels", "1", 0); av_dict_set(&format_opts, "sample_rate", "22050", 0); @@ -68,7 +87,7 @@ int main(int argc, char **argv) /* initialize libavcodec, and register all codecs and formats */ av_register_all(); - if (argc != 2) { + if (argc < 2) { printf("usage: %s input_file\n" "\n", argv[0]); return 1; @@ -89,12 +108,17 @@ int main(int argc, char **argv) return 1; } + if(seekfirst != AV_NOPTS_VALUE){ + if(firstback) avformat_seek_file(ic, -1, INT64_MIN, seekfirst, seekfirst, 0); + else avformat_seek_file(ic, -1, seekfirst, seekfirst, INT64_MAX, 0); + } for(i=0; ; i++){ AVPacket pkt = { 0 }; AVStream *av_uninit(st); char ts_buf[60]; if(ret>=0){ + for(j=0; j<frame_count; j++) { ret= av_read_frame(ic, &pkt); if(ret>=0){ char dts_buf[60]; @@ -106,12 +130,13 @@ int main(int argc, char **argv) } else printf("ret:%s", ret_str(ret)); // necessary to avoid trailing whitespace printf("\n"); + } } if(i>25) break; stream_id= (i>>1)%(ic->nb_streams+1) - 1; - timestamp= (i*19362894167LL) % (4*AV_TIME_BASE) - AV_TIME_BASE; + timestamp= (i*19362894167LL) % (duration*AV_TIME_BASE) - AV_TIME_BASE; if(stream_id>=0){ st= ic->streams[stream_id]; timestamp= av_rescale_q(timestamp, AV_TIME_BASE_Q, st->time_base); diff --git a/libavformat/seek.c b/libavformat/seek.c index e17cdccfd6..07c3f79522 100644 --- a/libavformat/seek.c +++ b/libavformat/seek.c @@ -3,20 +3,20 @@ * * Copyright (c) 2009 Ivan Schreter * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/seek.h b/libavformat/seek.h index 44cd369f4e..3fa7ae36d9 100644 --- a/libavformat/seek.h +++ b/libavformat/seek.h @@ -3,20 +3,20 @@ * * Copyright (c) 2009 Ivan Schreter * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/segafilm.c b/libavformat/segafilm.c index 5643f33cc8..a56568c300 100644 --- a/libavformat/segafilm.c +++ b/libavformat/segafilm.c @@ -2,20 +2,20 @@ * Sega FILM Format (CPK) Demuxer * Copyright (c) 2003 The ffmpeg Project * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -30,6 +30,7 @@ #include "libavutil/intreadwrite.h" #include "avformat.h" #include "internal.h" +#include "avio_internal.h" #define FILM_TAG MKBETAG('F', 'I', 'L', 'M') #define FDSC_TAG MKBETAG('F', 'D', 'S', 'C') @@ -61,10 +62,6 @@ typedef struct FilmDemuxContext { unsigned int base_clock; unsigned int version; - - /* buffer used for interleaving stereo PCM data */ - unsigned char *stereo_buffer; - int stereo_buffer_size; } FilmDemuxContext; static int film_probe(AVProbeData *p) @@ -86,8 +83,6 @@ static int film_read_header(AVFormatContext *s) unsigned int audio_frame_counter; film->sample_table = NULL; - film->stereo_buffer = NULL; - film->stereo_buffer_size = 0; /* load the main FILM header */ if (avio_read(pb, scratch, 16) != 16) @@ -111,19 +106,14 @@ static int film_read_header(AVFormatContext *s) return AVERROR(EIO); film->audio_samplerate = AV_RB16(&scratch[24]); film->audio_channels = scratch[21]; - if (!film->audio_channels || film->audio_channels > 2) { - av_log(s, AV_LOG_ERROR, - "Invalid number of channels: %d\n", film->audio_channels); - return AVERROR_INVALIDDATA; - } film->audio_bits = scratch[22]; - if (scratch[23] == 2) + if (scratch[23] == 2 && film->audio_channels > 0) film->audio_type = AV_CODEC_ID_ADPCM_ADX; else if (film->audio_channels > 0) { if (film->audio_bits == 8) - film->audio_type = AV_CODEC_ID_PCM_S8; + film->audio_type = AV_CODEC_ID_PCM_S8_PLANAR; else if (film->audio_bits == 16) - film->audio_type = AV_CODEC_ID_PCM_S16BE; + film->audio_type = AV_CODEC_ID_PCM_S16BE_PLANAR; else film->audio_type = AV_CODEC_ID_NONE; } else @@ -213,7 +203,7 @@ static int film_read_header(AVFormatContext *s) for (i = 0; i < film->sample_count; i++) { /* load the next sample record and transfer it to an internal struct */ if (avio_read(pb, scratch, 16) != 16) { - av_free(film->sample_table); + av_freep(&film->sample_table); return AVERROR(EIO); } film->sample_table[i].sample_offset = @@ -250,11 +240,9 @@ static int film_read_packet(AVFormatContext *s, AVIOContext *pb = s->pb; film_sample *sample; int ret = 0; - int i; - int left, right; if (film->current_sample >= film->sample_count) - return AVERROR(EIO); + return AVERROR_EOF; sample = &film->sample_table[film->current_sample]; @@ -268,43 +256,6 @@ static int film_read_packet(AVFormatContext *s, if (av_new_packet(pkt, sample->sample_size)) return AVERROR(ENOMEM); avio_read(pb, pkt->data, sample->sample_size); - } else if ((sample->stream == film->audio_stream_index) && - (film->audio_channels == 2) && - (film->audio_type != AV_CODEC_ID_ADPCM_ADX)) { - /* stereo PCM needs to be interleaved */ - - if (av_new_packet(pkt, sample->sample_size)) - return AVERROR(ENOMEM); - - /* make sure the interleave buffer is large enough */ - if (sample->sample_size > film->stereo_buffer_size) { - av_free(film->stereo_buffer); - film->stereo_buffer_size = sample->sample_size; - film->stereo_buffer = av_malloc(film->stereo_buffer_size); - if (!film->stereo_buffer) { - film->stereo_buffer_size = 0; - return AVERROR(ENOMEM); - } - } - - pkt->pos= avio_tell(pb); - ret = avio_read(pb, film->stereo_buffer, sample->sample_size); - if (ret != sample->sample_size) - ret = AVERROR(EIO); - - left = 0; - right = sample->sample_size / 2; - for (i = 0; i < sample->sample_size; ) { - if (film->audio_bits == 8) { - pkt->data[i++] = film->stereo_buffer[left++]; - pkt->data[i++] = film->stereo_buffer[right++]; - } else { - pkt->data[i++] = film->stereo_buffer[left++]; - pkt->data[i++] = film->stereo_buffer[left++]; - pkt->data[i++] = film->stereo_buffer[right++]; - pkt->data[i++] = film->stereo_buffer[right++]; - } - } } else { ret= av_get_packet(pb, pkt, sample->sample_size); if (ret != sample->sample_size) @@ -323,8 +274,7 @@ static int film_read_close(AVFormatContext *s) { FilmDemuxContext *film = s->priv_data; - av_free(film->sample_table); - av_free(film->stereo_buffer); + av_freep(&film->sample_table); return 0; } diff --git a/libavformat/segment.c b/libavformat/segment.c index d79a32798e..05e29d427a 100644 --- a/libavformat/segment.c +++ b/libavformat/segment.c @@ -1,58 +1,122 @@ /* - * Generic segmenter * Copyright (c) 2011, Luca Barbato * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +/** + * @file generic segmenter + * M3U8 specification can be find here: + * @url{http://tools.ietf.org/id/draft-pantos-http-live-streaming} + */ + +/* #define DEBUG */ + #include <float.h> #include "avformat.h" #include "internal.h" +#include "libavutil/avassert.h" #include "libavutil/log.h" #include "libavutil/opt.h" #include "libavutil/avstring.h" #include "libavutil/parseutils.h" #include "libavutil/mathematics.h" +#include "libavutil/timestamp.h" + +typedef struct SegmentListEntry { + int index; + double start_time, end_time; + int64_t start_pts; + int64_t offset_pts; + char filename[1024]; + struct SegmentListEntry *next; +} SegmentListEntry; + +typedef enum { + LIST_TYPE_UNDEFINED = -1, + LIST_TYPE_FLAT = 0, + LIST_TYPE_CSV, + LIST_TYPE_M3U8, + LIST_TYPE_EXT, ///< deprecated + LIST_TYPE_FFCONCAT, + LIST_TYPE_NB, +} ListType; + +#define SEGMENT_LIST_FLAG_CACHE 1 +#define SEGMENT_LIST_FLAG_LIVE 2 typedef struct { const AVClass *class; /**< Class for private options. */ - int number; + int segment_idx; ///< index of the segment file to write, starting from 0 + int segment_idx_wrap; ///< number after which the index wraps + int segment_count; ///< number of segment files already written AVOutputFormat *oformat; AVFormatContext *avf; - char *format; /**< Set by a private option. */ - char *list; /**< Set by a private option. */ - int list_type; /**< Set by a private option. */ - float time; /**< Set by a private option. */ - int size; /**< Set by a private option. */ - int wrap; /**< Set by a private option. */ + char *format; ///< format to use for output segment files + char *list; ///< filename for the segment list file + int list_flags; ///< flags affecting list generation + int list_size; ///< number of entries for the segment list file + ListType list_type; ///< set the list type + AVIOContext *list_pb; ///< list file put-byte context + char *time_str; ///< segment duration specification string + int64_t time; ///< segment duration + + char *times_str; ///< segment times specification string + int64_t *times; ///< list of segment interval specification + int nb_times; ///< number of elments in the times array + + char *frames_str; ///< segment frame numbers specification string + int *frames; ///< list of frame number specification + int nb_frames; ///< number of elments in the frames array + int frame_count; + + int64_t time_delta; int individual_header_trailer; /**< Set by a private option. */ int write_header_trailer; /**< Set by a private option. */ - int64_t offset_time; - int64_t recording_time; - int has_video; - AVIOContext *pb; + + int reset_timestamps; ///< reset timestamps at the begin of each segment + int64_t initial_offset; ///< initial timestamps offset, expressed in microseconds + char *reference_stream_specifier; ///< reference stream specifier + int reference_stream_index; + + SegmentListEntry cur_entry; + SegmentListEntry *segment_list_entries; + SegmentListEntry *segment_list_entries_end; + + int is_first_pkt; ///< tells if it is the first packet in the segment } SegmentContext; -enum { - LIST_FLAT, - LIST_HLS -}; +static void print_csv_escaped_str(AVIOContext *ctx, const char *str) +{ + int needs_quoting = !!str[strcspn(str, "\",\n\r")]; + + if (needs_quoting) + avio_w8(ctx, '"'); + + for (; *str; str++) { + if (*str == '"') + avio_w8(ctx, '"'); + avio_w8(ctx, *str); + } + if (needs_quoting) + avio_w8(ctx, '"'); +} static int segment_mux_init(AVFormatContext *s) { @@ -66,68 +130,63 @@ static int segment_mux_init(AVFormatContext *s) oc->oformat = seg->oformat; oc->interrupt_callback = s->interrupt_callback; + av_dict_copy(&oc->metadata, s->metadata, 0); for (i = 0; i < s->nb_streams; i++) { AVStream *st; + AVCodecContext *icodec, *ocodec; + if (!(st = avformat_new_stream(oc, NULL))) return AVERROR(ENOMEM); - avcodec_copy_context(st->codec, s->streams[i]->codec); + icodec = s->streams[i]->codec; + ocodec = st->codec; + avcodec_copy_context(ocodec, icodec); + if (!oc->oformat->codec_tag || + av_codec_get_id (oc->oformat->codec_tag, icodec->codec_tag) == ocodec->codec_id || + av_codec_get_tag(oc->oformat->codec_tag, icodec->codec_id) <= 0) { + ocodec->codec_tag = icodec->codec_tag; + } else { + ocodec->codec_tag = 0; + } st->sample_aspect_ratio = s->streams[i]->sample_aspect_ratio; } return 0; } -static int segment_hls_window(AVFormatContext *s, int last) +static int set_segment_filename(AVFormatContext *s) { SegmentContext *seg = s->priv_data; - int i, ret = 0; - char buf[1024]; - - if ((ret = avio_open2(&seg->pb, seg->list, AVIO_FLAG_WRITE, - &s->interrupt_callback, NULL)) < 0) - goto fail; + AVFormatContext *oc = seg->avf; - avio_printf(seg->pb, "#EXTM3U\n"); - avio_printf(seg->pb, "#EXT-X-VERSION:3\n"); - avio_printf(seg->pb, "#EXT-X-TARGETDURATION:%d\n", (int)seg->time); - avio_printf(seg->pb, "#EXT-X-MEDIA-SEQUENCE:%d\n", - FFMAX(0, seg->number - seg->size)); - - for (i = FFMAX(0, seg->number - seg->size); - i < seg->number; i++) { - avio_printf(seg->pb, "#EXTINF:%d,\n", (int)seg->time); - av_get_frame_filename(buf, sizeof(buf), s->filename, i); - avio_printf(seg->pb, "%s\n", buf); + if (seg->segment_idx_wrap) + seg->segment_idx %= seg->segment_idx_wrap; + if (av_get_frame_filename(oc->filename, sizeof(oc->filename), + s->filename, seg->segment_idx) < 0) { + av_log(oc, AV_LOG_ERROR, "Invalid segment filename template '%s'\n", s->filename); + return AVERROR(EINVAL); } - - if (last) - avio_printf(seg->pb, "#EXT-X-ENDLIST\n"); -fail: - avio_closep(&seg->pb); - return ret; + av_strlcpy(seg->cur_entry.filename, oc->filename, sizeof(seg->cur_entry.filename)); + return 0; } static int segment_start(AVFormatContext *s, int write_header) { - SegmentContext *c = s->priv_data; - AVFormatContext *oc = c->avf; + SegmentContext *seg = s->priv_data; + AVFormatContext *oc = seg->avf; int err = 0; if (write_header) { avformat_free_context(oc); - c->avf = NULL; + seg->avf = NULL; if ((err = segment_mux_init(s)) < 0) return err; - oc = c->avf; + oc = seg->avf; } - if (c->wrap) - c->number %= c->wrap; - - if (av_get_frame_filename(oc->filename, sizeof(oc->filename), - s->filename, c->number++) < 0) - return AVERROR(EINVAL); + seg->segment_idx++; + if ((err = set_segment_filename(s)) < 0) + return err; if ((err = avio_open2(&oc->pb, oc->filename, AVIO_FLAG_WRITE, &s->interrupt_callback, NULL)) < 0) @@ -141,21 +200,252 @@ static int segment_start(AVFormatContext *s, int write_header) return err; } + seg->is_first_pkt = 1; return 0; } -static int segment_end(AVFormatContext *oc, int write_trailer) +static int segment_list_open(AVFormatContext *s) +{ + SegmentContext *seg = s->priv_data; + int ret; + + ret = avio_open2(&seg->list_pb, seg->list, AVIO_FLAG_WRITE, + &s->interrupt_callback, NULL); + if (ret < 0) + return ret; + + if (seg->list_type == LIST_TYPE_M3U8 && seg->segment_list_entries) { + SegmentListEntry *entry; + double max_duration = 0; + + avio_printf(seg->list_pb, "#EXTM3U\n"); + avio_printf(seg->list_pb, "#EXT-X-VERSION:3\n"); + avio_printf(seg->list_pb, "#EXT-X-MEDIA-SEQUENCE:%d\n", seg->segment_list_entries->index); + avio_printf(seg->list_pb, "#EXT-X-ALLOW-CACHE:%s\n", + seg->list_flags & SEGMENT_LIST_FLAG_CACHE ? "YES" : "NO"); + + for (entry = seg->segment_list_entries; entry; entry = entry->next) + max_duration = FFMAX(max_duration, entry->end_time - entry->start_time); + avio_printf(seg->list_pb, "#EXT-X-TARGETDURATION:%"PRId64"\n", (int64_t)ceil(max_duration)); + } else if (seg->list_type == LIST_TYPE_FFCONCAT) { + avio_printf(seg->list_pb, "ffconcat version 1.0\n"); + } + + return ret; +} + +static void segment_list_print_entry(AVIOContext *list_ioctx, + ListType list_type, + const SegmentListEntry *list_entry, + void *log_ctx) +{ + switch (list_type) { + case LIST_TYPE_FLAT: + avio_printf(list_ioctx, "%s\n", list_entry->filename); + break; + case LIST_TYPE_CSV: + case LIST_TYPE_EXT: + print_csv_escaped_str(list_ioctx, list_entry->filename); + avio_printf(list_ioctx, ",%f,%f\n", list_entry->start_time, list_entry->end_time); + break; + case LIST_TYPE_M3U8: + avio_printf(list_ioctx, "#EXTINF:%f,\n%s\n", + list_entry->end_time - list_entry->start_time, list_entry->filename); + break; + case LIST_TYPE_FFCONCAT: + { + char *buf; + if (av_escape(&buf, list_entry->filename, NULL, AV_ESCAPE_MODE_AUTO, AV_ESCAPE_FLAG_WHITESPACE) < 0) { + av_log(log_ctx, AV_LOG_WARNING, + "Error writing list entry '%s' in list file\n", list_entry->filename); + return; + } + avio_printf(list_ioctx, "file %s\n", buf); + av_free(buf); + break; + } + default: + av_assert0(!"Invalid list type"); + } +} + +static int segment_end(AVFormatContext *s, int write_trailer, int is_last) { + SegmentContext *seg = s->priv_data; + AVFormatContext *oc = seg->avf; int ret = 0; av_write_frame(oc, NULL); /* Flush any buffered data (fragmented mp4) */ if (write_trailer) - av_write_trailer(oc); + ret = av_write_trailer(oc); + + if (ret < 0) + av_log(s, AV_LOG_ERROR, "Failure occurred when ending segment '%s'\n", + oc->filename); + + if (seg->list) { + if (seg->list_size || seg->list_type == LIST_TYPE_M3U8) { + SegmentListEntry *entry = av_mallocz(sizeof(*entry)); + if (!entry) { + ret = AVERROR(ENOMEM); + goto end; + } + + /* append new element */ + memcpy(entry, &seg->cur_entry, sizeof(*entry)); + if (!seg->segment_list_entries) + seg->segment_list_entries = seg->segment_list_entries_end = entry; + else + seg->segment_list_entries_end->next = entry; + seg->segment_list_entries_end = entry; + + /* drop first item */ + if (seg->list_size && seg->segment_count > seg->list_size) { + entry = seg->segment_list_entries; + seg->segment_list_entries = seg->segment_list_entries->next; + av_freep(&entry); + } + + avio_close(seg->list_pb); + if ((ret = segment_list_open(s)) < 0) + goto end; + for (entry = seg->segment_list_entries; entry; entry = entry->next) + segment_list_print_entry(seg->list_pb, seg->list_type, entry, s); + if (seg->list_type == LIST_TYPE_M3U8 && is_last) + avio_printf(seg->list_pb, "#EXT-X-ENDLIST\n"); + } else { + segment_list_print_entry(seg->list_pb, seg->list_type, &seg->cur_entry, s); + } + avio_flush(seg->list_pb); + } + + av_log(s, AV_LOG_VERBOSE, "segment:'%s' count:%d ended\n", + seg->avf->filename, seg->segment_count); + seg->segment_count++; + +end: avio_close(oc->pb); return ret; } +static int parse_times(void *log_ctx, int64_t **times, int *nb_times, + const char *times_str) +{ + char *p; + int i, ret = 0; + char *times_str1 = av_strdup(times_str); + char *saveptr = NULL; + + if (!times_str1) + return AVERROR(ENOMEM); + +#define FAIL(err) ret = err; goto end + + *nb_times = 1; + for (p = times_str1; *p; p++) + if (*p == ',') + (*nb_times)++; + + *times = av_malloc(sizeof(**times) * *nb_times); + if (!*times) { + av_log(log_ctx, AV_LOG_ERROR, "Could not allocate forced times array\n"); + FAIL(AVERROR(ENOMEM)); + } + + p = times_str1; + for (i = 0; i < *nb_times; i++) { + int64_t t; + char *tstr = av_strtok(p, ",", &saveptr); + p = NULL; + + if (!tstr || !tstr[0]) { + av_log(log_ctx, AV_LOG_ERROR, "Empty time specification in times list %s\n", + times_str); + FAIL(AVERROR(EINVAL)); + } + + ret = av_parse_time(&t, tstr, 1); + if (ret < 0) { + av_log(log_ctx, AV_LOG_ERROR, + "Invalid time duration specification '%s' in times list %s\n", tstr, times_str); + FAIL(AVERROR(EINVAL)); + } + (*times)[i] = t; + + /* check on monotonicity */ + if (i && (*times)[i-1] > (*times)[i]) { + av_log(log_ctx, AV_LOG_ERROR, + "Specified time %f is greater than the following time %f\n", + (float)((*times)[i])/1000000, (float)((*times)[i-1])/1000000); + FAIL(AVERROR(EINVAL)); + } + } + +end: + av_free(times_str1); + return ret; +} + +static int parse_frames(void *log_ctx, int **frames, int *nb_frames, + const char *frames_str) +{ + char *p; + int i, ret = 0; + char *frames_str1 = av_strdup(frames_str); + char *saveptr = NULL; + + if (!frames_str1) + return AVERROR(ENOMEM); + +#define FAIL(err) ret = err; goto end + + *nb_frames = 1; + for (p = frames_str1; *p; p++) + if (*p == ',') + (*nb_frames)++; + + *frames = av_malloc(sizeof(**frames) * *nb_frames); + if (!*frames) { + av_log(log_ctx, AV_LOG_ERROR, "Could not allocate forced frames array\n"); + FAIL(AVERROR(ENOMEM)); + } + + p = frames_str1; + for (i = 0; i < *nb_frames; i++) { + long int f; + char *tailptr; + char *fstr = av_strtok(p, ",", &saveptr); + + p = NULL; + if (!fstr) { + av_log(log_ctx, AV_LOG_ERROR, "Empty frame specification in frame list %s\n", + frames_str); + FAIL(AVERROR(EINVAL)); + } + f = strtol(fstr, &tailptr, 10); + if (*tailptr || f <= 0 || f >= INT_MAX) { + av_log(log_ctx, AV_LOG_ERROR, + "Invalid argument '%s', must be a positive integer <= INT64_MAX\n", + fstr); + FAIL(AVERROR(EINVAL)); + } + (*frames)[i] = f; + + /* check on monotonicity */ + if (i && (*frames)[i-1] > (*frames)[i]) { + av_log(log_ctx, AV_LOG_ERROR, + "Specified frame %d is greater than the following frame %d\n", + (*frames)[i], (*frames)[i-1]); + FAIL(AVERROR(EINVAL)); + } + } + +end: + av_free(frames_str1); + return ret; +} + static int open_null_ctx(AVIOContext **ctx) { int buf_size = 32768; @@ -176,31 +466,117 @@ static void close_null_ctx(AVIOContext *pb) av_free(pb); } +static int select_reference_stream(AVFormatContext *s) +{ + SegmentContext *seg = s->priv_data; + int ret, i; + + seg->reference_stream_index = -1; + if (!strcmp(seg->reference_stream_specifier, "auto")) { + /* select first index of type with highest priority */ + int type_index_map[AVMEDIA_TYPE_NB]; + static const enum AVMediaType type_priority_list[] = { + AVMEDIA_TYPE_VIDEO, + AVMEDIA_TYPE_AUDIO, + AVMEDIA_TYPE_SUBTITLE, + AVMEDIA_TYPE_DATA, + AVMEDIA_TYPE_ATTACHMENT + }; + enum AVMediaType type; + + for (i = 0; i < AVMEDIA_TYPE_NB; i++) + type_index_map[i] = -1; + + /* select first index for each type */ + for (i = 0; i < s->nb_streams; i++) { + type = s->streams[i]->codec->codec_type; + if ((unsigned)type < AVMEDIA_TYPE_NB && type_index_map[type] == -1 + /* ignore attached pictures/cover art streams */ + && !(s->streams[i]->disposition & AV_DISPOSITION_ATTACHED_PIC)) + type_index_map[type] = i; + } + + for (i = 0; i < FF_ARRAY_ELEMS(type_priority_list); i++) { + type = type_priority_list[i]; + if ((seg->reference_stream_index = type_index_map[type]) >= 0) + break; + } + } else { + for (i = 0; i < s->nb_streams; i++) { + ret = avformat_match_stream_specifier(s, s->streams[i], + seg->reference_stream_specifier); + if (ret < 0) + return ret; + if (ret > 0) { + seg->reference_stream_index = i; + break; + } + } + } + + if (seg->reference_stream_index < 0) { + av_log(s, AV_LOG_ERROR, "Could not select stream matching identifier '%s'\n", + seg->reference_stream_specifier); + return AVERROR(EINVAL); + } + + return 0; +} + static int seg_write_header(AVFormatContext *s) { SegmentContext *seg = s->priv_data; AVFormatContext *oc = NULL; - int ret, i; + int ret; - seg->number = 0; - seg->offset_time = 0; - seg->recording_time = seg->time * 1000000; + seg->segment_count = 0; if (!seg->write_header_trailer) seg->individual_header_trailer = 0; - if (seg->list && seg->list_type != LIST_HLS) - if ((ret = avio_open2(&seg->pb, seg->list, AVIO_FLAG_WRITE, - &s->interrupt_callback, NULL)) < 0) - goto fail; + if (!!seg->time_str + !!seg->times_str + !!seg->frames_str > 1) { + av_log(s, AV_LOG_ERROR, + "segment_time, segment_times, and segment_frames options " + "are mutually exclusive, select just one of them\n"); + return AVERROR(EINVAL); + } - for (i = 0; i < s->nb_streams; i++) - seg->has_video += - (s->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO); + if (seg->times_str) { + if ((ret = parse_times(s, &seg->times, &seg->nb_times, seg->times_str)) < 0) + return ret; + } else if (seg->frames_str) { + if ((ret = parse_frames(s, &seg->frames, &seg->nb_frames, seg->frames_str)) < 0) + return ret; + } else { + /* set default value if not specified */ + if (!seg->time_str) + seg->time_str = av_strdup("2"); + if ((ret = av_parse_time(&seg->time, seg->time_str, 1)) < 0) { + av_log(s, AV_LOG_ERROR, + "Invalid time duration specification '%s' for segment_time option\n", + seg->time_str); + return ret; + } + } - if (seg->has_video > 1) - av_log(s, AV_LOG_WARNING, - "More than a single video stream present, " - "expect issues decoding it.\n"); + if (seg->list) { + if (seg->list_type == LIST_TYPE_UNDEFINED) { + if (av_match_ext(seg->list, "csv" )) seg->list_type = LIST_TYPE_CSV; + else if (av_match_ext(seg->list, "ext" )) seg->list_type = LIST_TYPE_EXT; + else if (av_match_ext(seg->list, "m3u8")) seg->list_type = LIST_TYPE_M3U8; + else if (av_match_ext(seg->list, "ffcat,ffconcat")) seg->list_type = LIST_TYPE_FFCONCAT; + else seg->list_type = LIST_TYPE_FLAT; + } + if ((ret = segment_list_open(s)) < 0) + goto fail; + } + if (seg->list_type == LIST_TYPE_EXT) + av_log(s, AV_LOG_WARNING, "'ext' list type option is deprecated in favor of 'csv'\n"); + + if ((ret = select_reference_stream(s)) < 0) + goto fail; + av_log(s, AV_LOG_VERBOSE, "Selected stream id:%d type:%s\n", + seg->reference_stream_index, + av_get_media_type_string(s->streams[seg->reference_stream_index]->codec->codec_type)); seg->oformat = av_guess_format(seg->format, s->filename, NULL); @@ -219,11 +595,8 @@ static int seg_write_header(AVFormatContext *s) goto fail; oc = seg->avf; - if (av_get_frame_filename(oc->filename, sizeof(oc->filename), - s->filename, seg->number++) < 0) { - ret = AVERROR(EINVAL); + if ((ret = set_segment_filename(s)) < 0) goto fail; - } if (seg->write_header_trailer) { if ((ret = avio_open2(&oc->pb, oc->filename, AVIO_FLAG_WRITE, @@ -238,6 +611,10 @@ static int seg_write_header(AVFormatContext *s) avio_close(oc->pb); goto fail; } + seg->is_first_pkt = 1; + + if (oc->avoid_negative_ts > 0 && s->avoid_negative_ts < 0) + s->avoid_negative_ts = 1; if (!seg->write_header_trailer) { close_null_ctx(oc->pb); @@ -246,20 +623,10 @@ static int seg_write_header(AVFormatContext *s) goto fail; } - if (seg->list) { - if (seg->list_type == LIST_HLS) { - if ((ret = segment_hls_window(s, 0)) < 0) - goto fail; - } else { - avio_printf(seg->pb, "%s\n", oc->filename); - avio_flush(seg->pb); - } - } - fail: if (ret) { if (seg->list) - avio_close(seg->pb); + avio_close(seg->list_pb); if (seg->avf) avformat_free_context(seg->avf); } @@ -271,52 +638,81 @@ static int seg_write_packet(AVFormatContext *s, AVPacket *pkt) SegmentContext *seg = s->priv_data; AVFormatContext *oc = seg->avf; AVStream *st = s->streams[pkt->stream_index]; - int64_t end_pts = seg->recording_time * seg->number; - int ret, can_split = 1; + int64_t end_pts = INT64_MAX, offset; + int start_frame = INT_MAX; + int ret; - if (seg->has_video) { - can_split = st->codec->codec_type == AVMEDIA_TYPE_VIDEO && - pkt->flags & AV_PKT_FLAG_KEY; + if (seg->times) { + end_pts = seg->segment_count < seg->nb_times ? + seg->times[seg->segment_count] : INT64_MAX; + } else if (seg->frames) { + start_frame = seg->segment_count <= seg->nb_frames ? + seg->frames[seg->segment_count] : INT_MAX; + } else { + end_pts = seg->time * (seg->segment_count+1); } - if (can_split && av_compare_ts(pkt->pts, st->time_base, end_pts, - AV_TIME_BASE_Q) >= 0) { - av_log(s, AV_LOG_DEBUG, "Next segment starts at %d %"PRId64"\n", - pkt->stream_index, pkt->pts); - - ret = segment_end(oc, seg->individual_header_trailer); - - if (!ret) - ret = segment_start(s, seg->individual_header_trailer); + av_dlog(s, "packet stream:%d pts:%s pts_time:%s is_key:%d frame:%d\n", + pkt->stream_index, av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &st->time_base), + pkt->flags & AV_PKT_FLAG_KEY, + pkt->stream_index == seg->reference_stream_index ? seg->frame_count : -1); + + if (pkt->stream_index == seg->reference_stream_index && + pkt->flags & AV_PKT_FLAG_KEY && + (seg->frame_count >= start_frame || + (pkt->pts != AV_NOPTS_VALUE && + av_compare_ts(pkt->pts, st->time_base, + end_pts-seg->time_delta, AV_TIME_BASE_Q) >= 0))) { + if ((ret = segment_end(s, seg->individual_header_trailer, 0)) < 0) + goto fail; - if (ret) + if ((ret = segment_start(s, seg->individual_header_trailer)) < 0) goto fail; oc = seg->avf; - if (seg->list) { - if (seg->list_type == LIST_HLS) { - if ((ret = segment_hls_window(s, 0)) < 0) - goto fail; - } else { - avio_printf(seg->pb, "%s\n", oc->filename); - avio_flush(seg->pb); - if (seg->size && !(seg->number % seg->size)) { - avio_closep(&seg->pb); - if ((ret = avio_open2(&seg->pb, seg->list, AVIO_FLAG_WRITE, - &s->interrupt_callback, NULL)) < 0) - goto fail; - } - } - } + seg->cur_entry.index = seg->segment_idx; + seg->cur_entry.start_time = (double)pkt->pts * av_q2d(st->time_base); + seg->cur_entry.start_pts = av_rescale_q(pkt->pts, st->time_base, AV_TIME_BASE_Q); + } else if (pkt->pts != AV_NOPTS_VALUE) { + seg->cur_entry.end_time = + FFMAX(seg->cur_entry.end_time, (double)(pkt->pts + pkt->duration) * av_q2d(st->time_base)); + } + + if (seg->is_first_pkt) { + av_log(s, AV_LOG_DEBUG, "segment:'%s' starts with packet stream:%d pts:%s pts_time:%s frame:%d\n", + seg->avf->filename, pkt->stream_index, + av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &st->time_base), seg->frame_count); + seg->is_first_pkt = 0; } + av_log(s, AV_LOG_DEBUG, "stream:%d start_pts_time:%s pts:%s pts_time:%s dts:%s dts_time:%s", + pkt->stream_index, + av_ts2timestr(seg->cur_entry.start_pts, &AV_TIME_BASE_Q), + av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &st->time_base), + av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, &st->time_base)); + + /* compute new timestamps */ + offset = av_rescale_q(seg->initial_offset - (seg->reset_timestamps ? seg->cur_entry.start_pts : 0), + AV_TIME_BASE_Q, st->time_base); + if (pkt->pts != AV_NOPTS_VALUE) + pkt->pts += offset; + if (pkt->dts != AV_NOPTS_VALUE) + pkt->dts += offset; + + av_log(s, AV_LOG_DEBUG, " -> pts:%s pts_time:%s dts:%s dts_time:%s\n", + av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &st->time_base), + av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, &st->time_base)); + ret = ff_write_chained(oc, pkt->stream_index, pkt, s); fail: + if (pkt->stream_index == seg->reference_stream_index) + seg->frame_count++; + if (ret < 0) { if (seg->list) - avio_close(seg->pb); + avio_close(seg->list_pb); avformat_free_context(oc); } @@ -327,27 +723,33 @@ static int seg_write_trailer(struct AVFormatContext *s) { SegmentContext *seg = s->priv_data; AVFormatContext *oc = seg->avf; + SegmentListEntry *cur, *next; + int ret; if (!seg->write_header_trailer) { - if ((ret = segment_end(oc, 0)) < 0) + if ((ret = segment_end(s, 0, 1)) < 0) goto fail; open_null_ctx(&oc->pb); ret = av_write_trailer(oc); close_null_ctx(oc->pb); } else { - ret = segment_end(oc, 1); + ret = segment_end(s, 1, 1); } - - if (ret < 0) - goto fail; - - if (seg->list && seg->list_type == LIST_HLS) { - if ((ret = segment_hls_window(s, 1) < 0)) - goto fail; +fail: + if (seg->list) + avio_close(seg->list_pb); + + av_opt_free(seg); + av_freep(&seg->times); + av_freep(&seg->frames); + + cur = seg->segment_list_entries; + while (cur) { + next = cur->next; + av_free(cur); + cur = next; } -fail: - avio_close(seg->pb); avformat_free_context(oc); return ret; } @@ -355,16 +757,35 @@ fail: #define OFFSET(x) offsetof(SegmentContext, x) #define E AV_OPT_FLAG_ENCODING_PARAM static const AVOption options[] = { - { "segment_format", "container format used for the segments", OFFSET(format), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E }, - { "segment_time", "segment length in seconds", OFFSET(time), AV_OPT_TYPE_FLOAT, {.dbl = 2}, 0, FLT_MAX, E }, - { "segment_list", "output the segment list", OFFSET(list), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E }, - { "segment_list_size", "maximum number of playlist entries", OFFSET(size), AV_OPT_TYPE_INT, {.i64 = 5}, 0, INT_MAX, E }, - { "segment_list_type", "segment list format", OFFSET(list_type), AV_OPT_TYPE_INT, {.i64 = LIST_FLAT}, 0, 2, E, "list_type" }, - { "flat", "plain list (default)", 0, AV_OPT_TYPE_CONST, {.i64 = LIST_FLAT}, 0, 0, E, "list_type" }, - { "hls", "Apple HTTP Live Streaming compatible", 0, AV_OPT_TYPE_CONST, {.i64 = LIST_HLS}, 0, 0, E, "list_type" }, - { "segment_wrap", "number after which the index wraps", OFFSET(wrap), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E }, + { "reference_stream", "set reference stream", OFFSET(reference_stream_specifier), AV_OPT_TYPE_STRING, {.str = "auto"}, CHAR_MIN, CHAR_MAX, E }, + { "segment_format", "set container format used for the segments", OFFSET(format), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E }, + { "segment_list", "set the segment list filename", OFFSET(list), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E }, + + { "segment_list_flags","set flags affecting segment list generation", OFFSET(list_flags), AV_OPT_TYPE_FLAGS, {.i64 = SEGMENT_LIST_FLAG_CACHE }, 0, UINT_MAX, E, "list_flags"}, + { "cache", "allow list caching", 0, AV_OPT_TYPE_CONST, {.i64 = SEGMENT_LIST_FLAG_CACHE }, INT_MIN, INT_MAX, E, "list_flags"}, + { "live", "enable live-friendly list generation (useful for HLS)", 0, AV_OPT_TYPE_CONST, {.i64 = SEGMENT_LIST_FLAG_LIVE }, INT_MIN, INT_MAX, E, "list_flags"}, + + { "segment_list_size", "set the maximum number of playlist entries", OFFSET(list_size), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E }, + + { "segment_list_type", "set the segment list type", OFFSET(list_type), AV_OPT_TYPE_INT, {.i64 = LIST_TYPE_UNDEFINED}, -1, LIST_TYPE_NB-1, E, "list_type" }, + { "flat", "flat format", 0, AV_OPT_TYPE_CONST, {.i64=LIST_TYPE_FLAT }, INT_MIN, INT_MAX, E, "list_type" }, + { "csv", "csv format", 0, AV_OPT_TYPE_CONST, {.i64=LIST_TYPE_CSV }, INT_MIN, INT_MAX, E, "list_type" }, + { "ext", "extended format", 0, AV_OPT_TYPE_CONST, {.i64=LIST_TYPE_EXT }, INT_MIN, INT_MAX, E, "list_type" }, + { "ffconcat", "ffconcat format", 0, AV_OPT_TYPE_CONST, {.i64=LIST_TYPE_FFCONCAT }, INT_MIN, INT_MAX, E, "list_type" }, + { "m3u8", "M3U8 format", 0, AV_OPT_TYPE_CONST, {.i64=LIST_TYPE_M3U8 }, INT_MIN, INT_MAX, E, "list_type" }, + { "hls", "Apple HTTP Live Streaming compatible", 0, AV_OPT_TYPE_CONST, {.i64=LIST_TYPE_M3U8 }, INT_MIN, INT_MAX, E, "list_type" }, + + { "segment_time", "set segment duration", OFFSET(time_str),AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E }, + { "segment_time_delta","set approximation value used for the segment times", OFFSET(time_delta), AV_OPT_TYPE_DURATION, {.i64 = 0}, 0, 0, E }, + { "segment_times", "set segment split time points", OFFSET(times_str),AV_OPT_TYPE_STRING,{.str = NULL}, 0, 0, E }, + { "segment_frames", "set segment split frame numbers", OFFSET(frames_str),AV_OPT_TYPE_STRING,{.str = NULL}, 0, 0, E }, + { "segment_wrap", "set number after which the index wraps", OFFSET(segment_idx_wrap), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E }, + { "segment_start_number", "set the sequence number of the first segment", OFFSET(segment_idx), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E }, + { "individual_header_trailer", "write header/trailer to each segment", OFFSET(individual_header_trailer), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1, E }, { "write_header_trailer", "write a header to the first segment and a trailer to the last one", OFFSET(write_header_trailer), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1, E }, + { "reset_timestamps", "reset timestamps at the begin of each segment", OFFSET(reset_timestamps), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, E }, + { "initial_offset", "set initial timestamp offset", OFFSET(initial_offset), AV_OPT_TYPE_DURATION, {.i64 = 0}, -INT64_MAX, INT64_MAX, E }, { NULL }, }; @@ -375,14 +796,31 @@ static const AVClass seg_class = { .version = LIBAVUTIL_VERSION_INT, }; - AVOutputFormat ff_segment_muxer = { .name = "segment", .long_name = NULL_IF_CONFIG_SMALL("segment"), .priv_data_size = sizeof(SegmentContext), - .flags = AVFMT_NOFILE, + .flags = AVFMT_NOFILE|AVFMT_GLOBALHEADER, .write_header = seg_write_header, .write_packet = seg_write_packet, .write_trailer = seg_write_trailer, .priv_class = &seg_class, }; + +static const AVClass sseg_class = { + .class_name = "stream_segment muxer", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + +AVOutputFormat ff_stream_segment_muxer = { + .name = "stream_segment,ssegment", + .long_name = NULL_IF_CONFIG_SMALL("streaming segment muxer"), + .priv_data_size = sizeof(SegmentContext), + .flags = AVFMT_NOFILE, + .write_header = seg_write_header, + .write_packet = seg_write_packet, + .write_trailer = seg_write_trailer, + .priv_class = &sseg_class, +}; diff --git a/libavformat/sierravmd.c b/libavformat/sierravmd.c index 831638890b..8749ec1640 100644 --- a/libavformat/sierravmd.c +++ b/libavformat/sierravmd.c @@ -2,20 +2,20 @@ * Sierra VMD Format Demuxer * Copyright (c) 2004 The ffmpeg Project * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -31,6 +31,7 @@ #include "libavutil/intreadwrite.h" #include "avformat.h" #include "internal.h" +#include "avio_internal.h" #define VMD_HEADER_SIZE 0x0330 #define BYTES_PER_FRAME_RECORD 16 @@ -63,8 +64,8 @@ typedef struct VmdDemuxContext { static int vmd_probe(AVProbeData *p) { - int w, h; - if (p->buf_size < 16) + int w, h, sample_rate; + if (p->buf_size < 806) return 0; /* check if the first 2 bytes of the file contain the appropriate size * of a VMD header chunk */ @@ -72,7 +73,9 @@ static int vmd_probe(AVProbeData *p) return 0; w = AV_RL16(&p->buf[12]); h = AV_RL16(&p->buf[14]); - if (!w || w > 2048 || !h || h > 2048) + sample_rate = AV_RL16(&p->buf[804]); + if ((!w || w > 2048 || !h || h > 2048) && + sample_rate != 22050) return 0; /* only return half certainty since this check is a bit sketchy */ @@ -83,12 +86,13 @@ static int vmd_read_header(AVFormatContext *s) { VmdDemuxContext *vmd = s->priv_data; AVIOContext *pb = s->pb; - AVStream *st = NULL, *vst; + AVStream *st = NULL, *vst = NULL; unsigned int toc_offset; unsigned char *raw_frame_table; int raw_frame_table_size; int64_t current_offset; int i, j, ret; + int width, height; unsigned int total_frames; int64_t current_audio_pts = 0; unsigned char chunk[BYTES_PER_FRAME_RECORD]; @@ -100,28 +104,33 @@ static int vmd_read_header(AVFormatContext *s) if (avio_read(pb, vmd->vmd_header, VMD_HEADER_SIZE) != VMD_HEADER_SIZE) return AVERROR(EIO); - if(vmd->vmd_header[24] == 'i' && vmd->vmd_header[25] == 'v' && vmd->vmd_header[26] == '3') - vmd->is_indeo3 = 1; - else - vmd->is_indeo3 = 0; - /* start up the decoders */ - vst = avformat_new_stream(s, NULL); - if (!vst) - return AVERROR(ENOMEM); - avpriv_set_pts_info(vst, 33, 1, 10); - vmd->video_stream_index = vst->index; - vst->codec->codec_type = AVMEDIA_TYPE_VIDEO; - vst->codec->codec_id = vmd->is_indeo3 ? AV_CODEC_ID_INDEO3 : AV_CODEC_ID_VMDVIDEO; - vst->codec->codec_tag = 0; /* no fourcc */ - vst->codec->width = AV_RL16(&vmd->vmd_header[12]); - vst->codec->height = AV_RL16(&vmd->vmd_header[14]); - if(vmd->is_indeo3 && vst->codec->width > 320){ - vst->codec->width >>= 1; - vst->codec->height >>= 1; + width = AV_RL16(&vmd->vmd_header[12]); + height = AV_RL16(&vmd->vmd_header[14]); + if (width && height) { + if(vmd->vmd_header[24] == 'i' && vmd->vmd_header[25] == 'v' && vmd->vmd_header[26] == '3') { + vmd->is_indeo3 = 1; + } else { + vmd->is_indeo3 = 0; + } + /* start up the decoders */ + vst = avformat_new_stream(s, NULL); + if (!vst) + return AVERROR(ENOMEM); + avpriv_set_pts_info(vst, 33, 1, 10); + vmd->video_stream_index = vst->index; + vst->codec->codec_type = AVMEDIA_TYPE_VIDEO; + vst->codec->codec_id = vmd->is_indeo3 ? AV_CODEC_ID_INDEO3 : AV_CODEC_ID_VMDVIDEO; + vst->codec->codec_tag = 0; /* no fourcc */ + vst->codec->width = width; + vst->codec->height = height; + if(vmd->is_indeo3 && vst->codec->width > 320){ + vst->codec->width >>= 1; + vst->codec->height >>= 1; + } + if (ff_alloc_extradata(vst->codec, VMD_HEADER_SIZE)) + return AVERROR(ENOMEM); + memcpy(vst->codec->extradata, vmd->vmd_header, VMD_HEADER_SIZE); } - vst->codec->extradata_size = VMD_HEADER_SIZE; - vst->codec->extradata = av_mallocz(VMD_HEADER_SIZE + FF_INPUT_BUFFER_PADDING_SIZE); - memcpy(vst->codec->extradata, vmd->vmd_header, VMD_HEADER_SIZE); /* if sample rate is 0, assume no audio */ vmd->sample_rate = AV_RL16(&vmd->vmd_header[804]); @@ -154,8 +163,9 @@ static int vmd_read_header(AVFormatContext *s) /* calculate pts */ num = st->codec->block_align; den = st->codec->sample_rate * st->codec->channels; - av_reduce(&den, &num, den, num, (1UL<<31)-1); - avpriv_set_pts_info(vst, 33, num, den); + av_reduce(&num, &den, num, den, (1UL<<31)-1); + if (vst) + avpriv_set_pts_info(vst, 33, num, den); avpriv_set_pts_info(st, 33, num, den); } @@ -240,8 +250,8 @@ static int vmd_read_header(AVFormatContext *s) return 0; error: - av_free(raw_frame_table); - av_free(vmd->frame_table); + av_freep(&raw_frame_table); + av_freep(&vmd->frame_table); return ret; } @@ -254,12 +264,14 @@ static int vmd_read_packet(AVFormatContext *s, vmd_frame *frame; if (vmd->current_frame >= vmd->frame_count) - return AVERROR(EIO); + return AVERROR_EOF; frame = &vmd->frame_table[vmd->current_frame]; /* position the stream (will probably be there already) */ avio_seek(pb, frame->frame_offset, SEEK_SET); + if(ffio_limit(pb, frame->frame_size) != frame->frame_size) + return AVERROR(EIO); if (av_new_packet(pkt, frame->frame_size + BYTES_PER_FRAME_RECORD)) return AVERROR(ENOMEM); pkt->pos= avio_tell(pb); @@ -290,7 +302,7 @@ static int vmd_read_close(AVFormatContext *s) { VmdDemuxContext *vmd = s->priv_data; - av_free(vmd->frame_table); + av_freep(&vmd->frame_table); return 0; } diff --git a/libavformat/siff.c b/libavformat/siff.c index 8ba7c6007f..c8b68abe30 100644 --- a/libavformat/siff.c +++ b/libavformat/siff.c @@ -2,20 +2,20 @@ * Beam Software SIFF demuxer * Copyright (c) 2007 Konstantin Shishkov * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -23,6 +23,7 @@ #include "libavutil/intreadwrite.h" #include "avformat.h" #include "internal.h" +#include "avio_internal.h" enum SIFFTags{ TAG_SIFF = MKTAG('S', 'I', 'F', 'F'), @@ -75,7 +76,7 @@ static int create_audio_stream(AVFormatContext *s, SIFFContext *c) AVStream *ast; ast = avformat_new_stream(s, NULL); if (!ast) - return -1; + return AVERROR(ENOMEM); ast->codec->codec_type = AVMEDIA_TYPE_AUDIO; ast->codec->codec_id = AV_CODEC_ID_PCM_U8; ast->codec->channels = 1; @@ -94,15 +95,15 @@ static int siff_parse_vbv1(AVFormatContext *s, SIFFContext *c, AVIOContext *pb) if (avio_rl32(pb) != TAG_VBHD){ av_log(s, AV_LOG_ERROR, "Header chunk is missing\n"); - return -1; + return AVERROR_INVALIDDATA; } if(avio_rb32(pb) != 32){ av_log(s, AV_LOG_ERROR, "Header chunk size is incorrect\n"); - return -1; + return AVERROR_INVALIDDATA; } if(avio_rl16(pb) != 1){ av_log(s, AV_LOG_ERROR, "Incorrect header version\n"); - return -1; + return AVERROR_INVALIDDATA; } width = avio_rl16(pb); height = avio_rl16(pb); @@ -110,7 +111,7 @@ static int siff_parse_vbv1(AVFormatContext *s, SIFFContext *c, AVIOContext *pb) c->frames = avio_rl16(pb); if(!c->frames){ av_log(s, AV_LOG_ERROR, "File contains no frames ???\n"); - return -1; + return AVERROR_INVALIDDATA; } c->bits = avio_rl16(pb); c->rate = avio_rl16(pb); @@ -120,13 +121,15 @@ static int siff_parse_vbv1(AVFormatContext *s, SIFFContext *c, AVIOContext *pb) st = avformat_new_stream(s, NULL); if (!st) - return -1; + return AVERROR(ENOMEM); st->codec->codec_type = AVMEDIA_TYPE_VIDEO; st->codec->codec_id = AV_CODEC_ID_VB; st->codec->codec_tag = MKTAG('V', 'B', 'V', '1'); st->codec->width = width; st->codec->height = height; st->codec->pix_fmt = AV_PIX_FMT_PAL8; + st->nb_frames = + st->duration = c->frames; avpriv_set_pts_info(st, 16, 1, 12); c->cur_frame = 0; @@ -134,7 +137,7 @@ static int siff_parse_vbv1(AVFormatContext *s, SIFFContext *c, AVIOContext *pb) c->has_audio = !!c->rate; c->curstrm = -1; if (c->has_audio && create_audio_stream(s, c) < 0) - return -1; + return AVERROR(ENOMEM); return 0; } @@ -142,11 +145,11 @@ static int siff_parse_soun(AVFormatContext *s, SIFFContext *c, AVIOContext *pb) { if (avio_rl32(pb) != TAG_SHDR){ av_log(s, AV_LOG_ERROR, "Header chunk is missing\n"); - return -1; + return AVERROR_INVALIDDATA; } if(avio_rb32(pb) != 8){ av_log(s, AV_LOG_ERROR, "Header chunk size is incorrect\n"); - return -1; + return AVERROR_INVALIDDATA; } avio_skip(pb, 4); //unknown value c->rate = avio_rl16(pb); @@ -160,24 +163,25 @@ static int siff_read_header(AVFormatContext *s) AVIOContext *pb = s->pb; SIFFContext *c = s->priv_data; uint32_t tag; + int ret; if (avio_rl32(pb) != TAG_SIFF) - return -1; + return AVERROR_INVALIDDATA; avio_skip(pb, 4); //ignore size tag = avio_rl32(pb); if (tag != TAG_VBV1 && tag != TAG_SOUN){ av_log(s, AV_LOG_ERROR, "Not a VBV file\n"); - return -1; + return AVERROR_INVALIDDATA; } - if (tag == TAG_VBV1 && siff_parse_vbv1(s, c, pb) < 0) - return -1; - if (tag == TAG_SOUN && siff_parse_soun(s, c, pb) < 0) - return -1; + if (tag == TAG_VBV1 && (ret = siff_parse_vbv1(s, c, pb)) < 0) + return ret; + if (tag == TAG_SOUN && (ret = siff_parse_soun(s, c, pb)) < 0) + return ret; if (avio_rl32(pb) != MKTAG('B', 'O', 'D', 'Y')){ av_log(s, AV_LOG_ERROR, "'BODY' chunk is missing\n"); - return -1; + return AVERROR_INVALIDDATA; } avio_skip(pb, 4); //ignore size @@ -191,7 +195,7 @@ static int siff_read_packet(AVFormatContext *s, AVPacket *pkt) if (c->has_video){ if (c->cur_frame >= c->frames) - return AVERROR(EIO); + return AVERROR_EOF; if (c->curstrm == -1){ c->pktsize = avio_rl32(s->pb) - 4; c->flags = avio_rl16(s->pb); @@ -203,13 +207,16 @@ static int siff_read_packet(AVFormatContext *s, AVPacket *pkt) } if (!c->curstrm){ - size = c->pktsize - c->sndsize; - if (av_new_packet(pkt, size) < 0) + size = c->pktsize - c->sndsize - c->gmcsize - 2; + size = ffio_limit(s->pb, size); + if(size < 0 || c->pktsize < c->sndsize) + return AVERROR_INVALIDDATA; + if (av_new_packet(pkt, size + c->gmcsize + 2) < 0) return AVERROR(ENOMEM); AV_WL16(pkt->data, c->flags); if (c->gmcsize) memcpy(pkt->data + 2, c->gmc, c->gmcsize); - avio_read(s->pb, pkt->data + 2 + c->gmcsize, size - c->gmcsize - 2); + avio_read(s->pb, pkt->data + 2 + c->gmcsize, size); pkt->stream_index = 0; c->curstrm = -1; }else{ @@ -225,7 +232,9 @@ static int siff_read_packet(AVFormatContext *s, AVPacket *pkt) c->cur_frame++; }else{ size = av_get_packet(s->pb, pkt, c->block_align); - if(size <= 0) + if(!size) + return AVERROR_EOF; + if(size < 0) return AVERROR(EIO); pkt->duration = size; } diff --git a/libavformat/smacker.c b/libavformat/smacker.c index 5af5e50784..527faf7dd8 100644 --- a/libavformat/smacker.c +++ b/libavformat/smacker.c @@ -2,20 +2,20 @@ * Smacker demuxer * Copyright (c) 2006 Konstantin Shishkov * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -110,7 +110,7 @@ static int smacker_read_header(AVFormatContext *s) /* read and check header */ smk->magic = avio_rl32(pb); if (smk->magic != MKTAG('S', 'M', 'K', '2') && smk->magic != MKTAG('S', 'M', 'K', '4')) - return -1; + return AVERROR_INVALIDDATA; smk->width = avio_rl32(pb); smk->height = avio_rl32(pb); smk->frames = avio_rl32(pb); @@ -124,7 +124,7 @@ static int smacker_read_header(AVFormatContext *s) if(smk->treesize >= UINT_MAX/4){ // smk->treesize + 16 must not overflow (this check is probably redundant) av_log(s, AV_LOG_ERROR, "treesize too large\n"); - return -1; + return AVERROR_INVALIDDATA; } //FIXME remove extradata "rebuilding" @@ -140,10 +140,15 @@ static int smacker_read_header(AVFormatContext *s) /* setup data */ if(smk->frames > 0xFFFFFF) { av_log(s, AV_LOG_ERROR, "Too many frames: %i\n", smk->frames); - return -1; + return AVERROR_INVALIDDATA; } - smk->frm_size = av_malloc(smk->frames * 4); + smk->frm_size = av_malloc_array(smk->frames, sizeof(*smk->frm_size)); smk->frm_flags = av_malloc(smk->frames); + if (!smk->frm_size || !smk->frm_flags) { + av_freep(&smk->frm_size); + av_freep(&smk->frm_flags); + return AVERROR(ENOMEM); + } smk->is_ver4 = (smk->magic != MKTAG('S', 'M', 'K', '2')); @@ -158,7 +163,7 @@ static int smacker_read_header(AVFormatContext *s) /* init video codec */ st = avformat_new_stream(s, NULL); if (!st) - return -1; + return AVERROR(ENOMEM); smk->videoindex = st->index; st->codec->width = smk->width; st->codec->height = smk->height; @@ -180,6 +185,8 @@ static int smacker_read_header(AVFormatContext *s) smk->indexes[i] = -1; if (smk->rates[i]) { ast[i] = avformat_new_stream(s, NULL); + if (!ast[i]) + return AVERROR(ENOMEM); smk->indexes[i] = ast[i]->index; ast[i]->codec->codec_type = AVMEDIA_TYPE_AUDIO; if (smk->aflags[i] & SMK_AUD_BINKAUD) { @@ -210,19 +217,16 @@ static int smacker_read_header(AVFormatContext *s) /* load trees to extradata, they will be unpacked by decoder */ - st->codec->extradata = av_mallocz(smk->treesize + 16 + - FF_INPUT_BUFFER_PADDING_SIZE); - st->codec->extradata_size = smk->treesize + 16; - if(!st->codec->extradata){ + if(ff_alloc_extradata(st->codec, smk->treesize + 16)){ av_log(s, AV_LOG_ERROR, "Cannot allocate %i bytes of extradata\n", smk->treesize + 16); - av_free(smk->frm_size); - av_free(smk->frm_flags); - return -1; + av_freep(&smk->frm_size); + av_freep(&smk->frm_flags); + return AVERROR(ENOMEM); } ret = avio_read(pb, st->codec->extradata + 16, st->codec->extradata_size - 16); if(ret != st->codec->extradata_size - 16){ - av_free(smk->frm_size); - av_free(smk->frm_flags); + av_freep(&smk->frm_size); + av_freep(&smk->frm_flags); return AVERROR(EIO); } ((int32_t*)st->codec->extradata)[0] = av_le2ne32(smk->mmap_size); @@ -246,7 +250,7 @@ static int smacker_read_packet(AVFormatContext *s, AVPacket *pkt) int frame_size = 0; int palchange = 0; - if (s->pb->eof_reached || smk->cur_frame >= smk->frames) + if (url_feof(s->pb) || smk->cur_frame >= smk->frames) return AVERROR_EOF; /* if we demuxed all streams, pass another frame */ @@ -263,6 +267,8 @@ static int smacker_read_packet(AVFormatContext *s, AVPacket *pkt) memcpy(oldpal, pal, 768); size = avio_r8(s->pb); size = size * 4 - 1; + if(size + 1 > frame_size) + return AVERROR_INVALIDDATA; frame_size -= size; frame_size--; sz = 0; @@ -308,7 +314,7 @@ static int smacker_read_packet(AVFormatContext *s, AVPacket *pkt) int err; size = avio_rl32(s->pb) - 4; - if (!size || size > frame_size) { + if (!size || size + 4L > frame_size) { av_log(s, AV_LOG_ERROR, "Invalid audio part size\n"); return AVERROR_INVALIDDATA; } @@ -344,7 +350,7 @@ static int smacker_read_packet(AVFormatContext *s, AVPacket *pkt) smk->cur_frame++; smk->nextpos = avio_tell(s->pb); } else { - if (smk->stream_id[smk->curstream] < 0) + if (smk->stream_id[smk->curstream] < 0 || !smk->bufs[smk->curstream]) return AVERROR_INVALIDDATA; if (av_new_packet(pkt, smk->buf_sizes[smk->curstream])) return AVERROR(ENOMEM); @@ -365,16 +371,16 @@ static int smacker_read_close(AVFormatContext *s) int i; for(i = 0; i < 7; i++) - av_free(smk->bufs[i]); - av_free(smk->frm_size); - av_free(smk->frm_flags); + av_freep(&smk->bufs[i]); + av_freep(&smk->frm_size); + av_freep(&smk->frm_flags); return 0; } AVInputFormat ff_smacker_demuxer = { .name = "smk", - .long_name = NULL_IF_CONFIG_SMALL("Smacker video"), + .long_name = NULL_IF_CONFIG_SMALL("Smacker"), .priv_data_size = sizeof(SmackerContext), .read_probe = smacker_probe, .read_header = smacker_read_header, diff --git a/libavformat/smjpeg.c b/libavformat/smjpeg.c index 52e45e98f0..4edf5e8ac4 100644 --- a/libavformat/smjpeg.c +++ b/libavformat/smjpeg.c @@ -2,20 +2,20 @@ * SMJPEG common code * Copyright (c) 2011-2012 Paul B Mahol * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/smjpeg.h b/libavformat/smjpeg.h index c56fe46939..995ddf22da 100644 --- a/libavformat/smjpeg.h +++ b/libavformat/smjpeg.h @@ -2,20 +2,20 @@ * SMJPEG common code * Copyright (c) 2011-2012 Paul B Mahol * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/smjpegdec.c b/libavformat/smjpegdec.c index 623df71d0f..38ac2fb086 100644 --- a/libavformat/smjpegdec.c +++ b/libavformat/smjpegdec.c @@ -2,20 +2,20 @@ * SMJPEG demuxer * Copyright (c) 2011 Paul B Mahol * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -56,7 +56,7 @@ static int smjpeg_read_header(AVFormatContext *s) duration = avio_rb32(pb); // in msec - while (!pb->eof_reached) { + while (!url_feof(pb)) { htype = avio_rl32(pb); switch (htype) { case SMJPEG_TXT: @@ -106,10 +106,10 @@ static int smjpeg_read_header(AVFormatContext *s) hlength = avio_rb32(pb); if (hlength < 12) return AVERROR_INVALIDDATA; - avio_skip(pb, 4); // number of frames vst = avformat_new_stream(s, 0); if (!vst) return AVERROR(ENOMEM); + vst->nb_frames = avio_rb32(pb); vst->codec->codec_type = AVMEDIA_TYPE_VIDEO; vst->codec->width = avio_rb16(pb); vst->codec->height = avio_rb16(pb); @@ -139,7 +139,7 @@ static int smjpeg_read_packet(AVFormatContext *s, AVPacket *pkt) int64_t pos; int ret; - if (s->pb->eof_reached) + if (url_feof(s->pb)) return AVERROR_EOF; pos = avio_tell(s->pb); dtype = avio_rl32(s->pb); diff --git a/libavformat/smjpegenc.c b/libavformat/smjpegenc.c index 551af8911d..430a497c6e 100644 --- a/libavformat/smjpegenc.c +++ b/libavformat/smjpegenc.c @@ -2,20 +2,20 @@ * SMJPEG muxer * Copyright (c) 2012 Paul B Mahol * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -66,7 +66,7 @@ static int smjpeg_write_header(AVFormatContext *s) avio_wl32(pb, SMJPEG_SND); avio_wb32(pb, 8); avio_wb16(pb, codec->sample_rate); - avio_w8(pb, av_get_bits_per_sample(codec->codec_id)); + avio_w8(pb, codec->bits_per_coded_sample); avio_w8(pb, codec->channels); avio_wl32(pb, tag); avpriv_set_pts_info(st, 32, 1, 1000); diff --git a/libavformat/smoothstreamingenc.c b/libavformat/smoothstreamingenc.c index 2fe01b1f59..fe18a95e48 100644 --- a/libavformat/smoothstreamingenc.c +++ b/libavformat/smoothstreamingenc.c @@ -2,20 +2,20 @@ * Live smooth streaming fragmenter * Copyright (c) 2012 Martin Storsjo * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -289,7 +289,11 @@ static int ism_write_header(AVFormatContext *s) int ret = 0, i; AVOutputFormat *oformat; - mkdir(s->filename, 0777); + if (mkdir(s->filename, 0777) < 0) { + av_log(s, AV_LOG_ERROR, "mkdir failed\n"); + ret = AVERROR(errno); + goto fail; + } oformat = av_guess_format("ismv", NULL, NULL); if (!oformat) { @@ -316,7 +320,11 @@ static int ism_write_header(AVFormatContext *s) goto fail; } snprintf(os->dirname, sizeof(os->dirname), "%s/QualityLevels(%d)", s->filename, s->streams[i]->codec->bit_rate); - mkdir(os->dirname, 0777); + if (mkdir(os->dirname, 0777) < 0) { + ret = AVERROR(errno); + av_log(s, AV_LOG_ERROR, "mkdir failed\n"); + goto fail; + } ctx = avformat_alloc_context(); if (!ctx) { @@ -422,7 +430,7 @@ static int parse_fragment(AVFormatContext *s, const char *filename, int64_t *sta if (len < 8 || len >= *moof_size) goto fail; if (tag == MKTAG('u','u','i','d')) { - const uint8_t tfxd[] = { + static const uint8_t tfxd[] = { 0x6d, 0x1d, 0x9b, 0x05, 0x42, 0xd5, 0x44, 0xe6, 0x80, 0xe2, 0x14, 0x1d, 0xaf, 0xf7, 0x57, 0xb2 }; @@ -565,7 +573,7 @@ static int ism_write_packet(AVFormatContext *s, AVPacket *pkt) SmoothStreamingContext *c = s->priv_data; AVStream *st = s->streams[pkt->stream_index]; OutputStream *os = &c->streams[pkt->stream_index]; - int64_t end_dts = (c->nb_fragments + 1) * c->min_frag_duration; + int64_t end_dts = (c->nb_fragments + 1LL) * c->min_frag_duration; int ret; if (st->first_dts == AV_NOPTS_VALUE) diff --git a/libavformat/smush.c b/libavformat/smush.c new file mode 100644 index 0000000000..e9c1937398 --- /dev/null +++ b/libavformat/smush.c @@ -0,0 +1,238 @@ +/* + * LucasArts Smush demuxer + * Copyright (c) 2006 Cyril Zorin + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/intreadwrite.h" +#include "avformat.h" +#include "internal.h" +#include "avio.h" + +typedef struct { + int version; + int audio_stream_index; + int video_stream_index; +} SMUSHContext; + +static int smush_read_probe(AVProbeData *p) +{ + if (((AV_RL32(p->buf) == MKTAG('S', 'A', 'N', 'M') && + AV_RL32(p->buf + 8) == MKTAG('S', 'H', 'D', 'R')) || + (AV_RL32(p->buf) == MKTAG('A', 'N', 'I', 'M') && + AV_RL32(p->buf + 8) == MKTAG('A', 'H', 'D', 'R')))) { + return AVPROBE_SCORE_MAX; + } + + return 0; +} + +static int smush_read_header(AVFormatContext *ctx) +{ + SMUSHContext *smush = ctx->priv_data; + AVIOContext *pb = ctx->pb; + AVStream *vst, *ast; + uint32_t magic, nframes, size, subversion, i; + uint32_t width = 0, height = 0, got_audio = 0, read = 0; + uint32_t sample_rate, channels, palette[256]; + + magic = avio_rb32(pb); + avio_skip(pb, 4); // skip movie size + + if (magic == MKBETAG('A', 'N', 'I', 'M')) { + if (avio_rb32(pb) != MKBETAG('A', 'H', 'D', 'R')) + return AVERROR_INVALIDDATA; + + size = avio_rb32(pb); + if (size < 3 * 256 + 6) + return AVERROR_INVALIDDATA; + + smush->version = 0; + subversion = avio_rl16(pb); + nframes = avio_rl16(pb); + + avio_skip(pb, 2); // skip pad + + for (i = 0; i < 256; i++) + palette[i] = avio_rb24(pb); + + avio_skip(pb, size - (3 * 256 + 6)); + } else if (magic == MKBETAG('S', 'A', 'N', 'M') ) { + if (avio_rb32(pb) != MKBETAG('S', 'H', 'D', 'R')) + return AVERROR_INVALIDDATA; + + size = avio_rb32(pb); + if (size < 14) + return AVERROR_INVALIDDATA; + + smush->version = 1; + subversion = avio_rl16(pb); + nframes = avio_rl32(pb); + avio_skip(pb, 2); // skip pad + width = avio_rl16(pb); + height = avio_rl16(pb); + avio_skip(pb, 2); // skip pad + avio_skip(pb, size - 14); + + if (avio_rb32(pb) != MKBETAG('F', 'L', 'H', 'D')) + return AVERROR_INVALIDDATA; + + size = avio_rb32(pb); + while (!got_audio && ((read + 8) < size)) { + uint32_t sig, chunk_size; + + if (url_feof(pb)) + return AVERROR_EOF; + + sig = avio_rb32(pb); + chunk_size = avio_rb32(pb); + read += 8; + switch (sig) { + case MKBETAG('W', 'a', 'v', 'e'): + got_audio = 1; + sample_rate = avio_rl32(pb); + channels = avio_rl32(pb); + avio_skip(pb, chunk_size - 8); + read += chunk_size; + break; + case MKBETAG('B', 'l', '1', '6'): + case MKBETAG('A', 'N', 'N', 'O'): + avio_skip(pb, chunk_size); + read += chunk_size; + break; + default: + return AVERROR_INVALIDDATA; + break; + } + } + + avio_skip(pb, size - read); + } else { + av_log(ctx, AV_LOG_ERROR, "Wrong magic\n"); + return AVERROR_INVALIDDATA; + } + + vst = avformat_new_stream(ctx, 0); + if (!vst) + return AVERROR(ENOMEM); + + smush->video_stream_index = vst->index; + + vst->start_time = 0; + vst->duration = + vst->nb_frames = nframes; + vst->codec->codec_type = AVMEDIA_TYPE_VIDEO; + vst->codec->codec_id = AV_CODEC_ID_SANM; + vst->codec->codec_tag = 0; + vst->codec->width = width; + vst->codec->height = height; + + avpriv_set_pts_info(vst, 64, 66667, 1000000); + + if (!smush->version) { + if (ff_alloc_extradata(vst->codec, 1024 + 2)) + return AVERROR(ENOMEM); + + AV_WL16(vst->codec->extradata, subversion); + for (i = 0; i < 256; i++) + AV_WL32(vst->codec->extradata + 2 + i * 4, palette[i]); + } + + if (got_audio) { + ast = avformat_new_stream(ctx, 0); + if (!ast) + return AVERROR(ENOMEM); + + smush->audio_stream_index = ast->index; + + ast->start_time = 0; + ast->codec->codec_type = AVMEDIA_TYPE_AUDIO; + ast->codec->codec_id = AV_CODEC_ID_VIMA; + ast->codec->codec_tag = 0; + ast->codec->sample_rate = sample_rate; + ast->codec->channels = channels; + + avpriv_set_pts_info(ast, 64, 1, ast->codec->sample_rate); + } + + return 0; +} + +static int smush_read_packet(AVFormatContext *ctx, AVPacket *pkt) +{ + SMUSHContext *smush = ctx->priv_data; + AVIOContext *pb = ctx->pb; + int done = 0; + + while (!done) { + uint32_t sig, size; + + if (url_feof(pb)) + return AVERROR_EOF; + + sig = avio_rb32(pb); + size = avio_rb32(pb); + + switch (sig) { + case MKBETAG('F', 'R', 'M', 'E'): + if (smush->version) + break; + if (av_get_packet(pb, pkt, size) < 0) + return AVERROR(EIO); + + pkt->stream_index = smush->video_stream_index; + done = 1; + break; + case MKBETAG('B', 'l', '1', '6'): + if (av_get_packet(pb, pkt, size) < 0) + return AVERROR(EIO); + + pkt->stream_index = smush->video_stream_index; + pkt->duration = 1; + done = 1; + break; + case MKBETAG('W', 'a', 'v', 'e'): + if (size < 13) + return AVERROR_INVALIDDATA; + if (av_get_packet(pb, pkt, size) < 13) + return AVERROR(EIO); + + pkt->stream_index = smush->audio_stream_index; + pkt->flags |= AV_PKT_FLAG_KEY; + pkt->duration = AV_RB32(pkt->data); + if (pkt->duration == 0xFFFFFFFFu) + pkt->duration = AV_RB32(pkt->data + 8); + done = 1; + break; + default: + avio_skip(pb, size); + break; + } + } + + return 0; +} + +AVInputFormat ff_smush_demuxer = { + .name = "smush", + .long_name = NULL_IF_CONFIG_SMALL("LucasArts Smush"), + .priv_data_size = sizeof(SMUSHContext), + .read_probe = smush_read_probe, + .read_header = smush_read_header, + .read_packet = smush_read_packet, +}; diff --git a/libavformat/sol.c b/libavformat/sol.c index 92599b163d..77c754a77e 100644 --- a/libavformat/sol.c +++ b/libavformat/sol.c @@ -2,20 +2,20 @@ * Sierra SOL demuxer * Copyright Konstantin Shishkov * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -131,16 +131,13 @@ static int sol_read_packet(AVFormatContext *s, { int ret; - if (s->pb->eof_reached) + if (url_feof(s->pb)) return AVERROR(EIO); ret= av_get_packet(s->pb, pkt, MAX_SIZE); if (ret < 0) return ret; + pkt->flags &= ~AV_PKT_FLAG_CORRUPT; pkt->stream_index = 0; - - /* note: we need to modify the packet size here to handle the last - packet */ - pkt->size = ret; return 0; } diff --git a/libavformat/sox.h b/libavformat/sox.h index e59531bea3..f4a12e93ff 100644 --- a/libavformat/sox.h +++ b/libavformat/sox.h @@ -2,20 +2,20 @@ * SoX native format common data * Copyright (c) 2009 Daniel Verkamp <daniel@drv.nu> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/soxdec.c b/libavformat/soxdec.c index ec94675830..aec42843cb 100644 --- a/libavformat/soxdec.c +++ b/libavformat/soxdec.c @@ -5,20 +5,20 @@ * Based on libSoX sox-fmt.c * Copyright (c) 2008 robs@users.sourceforge.net * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -75,12 +75,12 @@ static int sox_read_header(AVFormatContext *s) if (comment_size > 0xFFFFFFFFU - SOX_FIXED_HDR - 4U) { av_log(s, AV_LOG_ERROR, "invalid comment size (%u)\n", comment_size); - return -1; + return AVERROR_INVALIDDATA; } if (sample_rate <= 0 || sample_rate > INT_MAX) { av_log(s, AV_LOG_ERROR, "invalid sample rate (%f)\n", sample_rate); - return -1; + return AVERROR_INVALIDDATA; } sample_rate_frac = sample_rate - floor(sample_rate); @@ -92,11 +92,13 @@ static int sox_read_header(AVFormatContext *s) if ((header_size + 4) & 7 || header_size < SOX_FIXED_HDR + comment_size || st->codec->channels > 65535) /* Reserve top 16 bits */ { av_log(s, AV_LOG_ERROR, "invalid header\n"); - return -1; + return AVERROR_INVALIDDATA; } if (comment_size && comment_size < UINT_MAX) { char *comment = av_malloc(comment_size+1); + if(!comment) + return AVERROR(ENOMEM); if (avio_read(pb, comment, comment_size) != comment_size) { av_freep(&comment); return AVERROR(EIO); @@ -122,31 +124,11 @@ static int sox_read_header(AVFormatContext *s) return 0; } -#define SOX_SAMPLES 1024 - -static int sox_read_packet(AVFormatContext *s, - AVPacket *pkt) -{ - int ret, size; - - if (s->pb->eof_reached) - return AVERROR_EOF; - - size = SOX_SAMPLES*s->streams[0]->codec->block_align; - ret = av_get_packet(s->pb, pkt, size); - if (ret < 0) - return AVERROR(EIO); - pkt->stream_index = 0; - pkt->size = ret; - - return 0; -} - AVInputFormat ff_sox_demuxer = { .name = "sox", .long_name = NULL_IF_CONFIG_SMALL("SoX native"), .read_probe = sox_probe, .read_header = sox_read_header, - .read_packet = sox_read_packet, + .read_packet = ff_pcm_read_packet, .read_seek = ff_pcm_read_seek, }; diff --git a/libavformat/soxenc.c b/libavformat/soxenc.c index 3e5d2e3515..e219d75b41 100644 --- a/libavformat/soxenc.c +++ b/libavformat/soxenc.c @@ -5,20 +5,20 @@ * Based on libSoX sox-fmt.c * Copyright (c) 2008 robs@users.sourceforge.net * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -34,6 +34,7 @@ #include "libavutil/dict.h" #include "avformat.h" #include "avio_internal.h" +#include "rawenc.h" #include "sox.h" typedef struct { @@ -51,7 +52,7 @@ static int sox_write_header(AVFormatContext *s) comment = av_dict_get(s->metadata, "comment", NULL, 0); if (comment) comment_len = strlen(comment->value); - comment_size = (comment_len + 7) & ~7; + comment_size = FFALIGN(comment_len, 8); sox->header_size = SOX_FIXED_HDR + comment_size; @@ -77,21 +78,13 @@ static int sox_write_header(AVFormatContext *s) if (comment_len) avio_write(pb, comment->value, comment_len); - for ( ; comment_size > comment_len; comment_len++) - avio_w8(pb, 0); + ffio_fill(pb, 0, comment_size - comment_len); avio_flush(pb); return 0; } -static int sox_write_packet(AVFormatContext *s, AVPacket *pkt) -{ - AVIOContext *pb = s->pb; - avio_write(pb, pkt->data, pkt->size); - return 0; -} - static int sox_write_trailer(AVFormatContext *s) { SoXContext *sox = s->priv_data; @@ -123,6 +116,6 @@ AVOutputFormat ff_sox_muxer = { .audio_codec = AV_CODEC_ID_PCM_S32LE, .video_codec = AV_CODEC_ID_NONE, .write_header = sox_write_header, - .write_packet = sox_write_packet, + .write_packet = ff_raw_write_packet, .write_trailer = sox_write_trailer, }; diff --git a/libavformat/spdif.c b/libavformat/spdif.c index 777ac47ba5..604141a261 100644 --- a/libavformat/spdif.c +++ b/libavformat/spdif.c @@ -2,20 +2,20 @@ * IEC 61937 common code * Copyright (c) 2009 Bartlomiej Wolowiec * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/spdif.h b/libavformat/spdif.h index f5b15ebbb1..fee4ff710b 100644 --- a/libavformat/spdif.h +++ b/libavformat/spdif.h @@ -2,20 +2,20 @@ * IEC 61937 common header * Copyright (c) 2009 Bartlomiej Wolowiec * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -23,6 +23,7 @@ #define AVFORMAT_SPDIF_H #include <stdint.h> +#include "avformat.h" #define SYNCWORD1 0xF872 #define SYNCWORD2 0x4E1F @@ -58,5 +59,7 @@ static const uint16_t spdif_mpeg_pkt_offset[2][3] = { }; void ff_spdif_bswap_buf16(uint16_t *dst, const uint16_t *src, int w); +int ff_spdif_read_packet(AVFormatContext *s, AVPacket *pkt); +int ff_spdif_probe(const uint8_t *p_buf, int buf_size, enum AVCodecID *codec); #endif /* AVFORMAT_SPDIF_H */ diff --git a/libavformat/spdifdec.c b/libavformat/spdifdec.c index 2fb94773a5..7da16c95ba 100644 --- a/libavformat/spdifdec.c +++ b/libavformat/spdifdec.c @@ -2,20 +2,20 @@ * IEC 61937 demuxer * Copyright (c) 2010 Anssi Hannula <anssi.hannula at iki.fi> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -57,7 +57,7 @@ static int spdif_get_offset_and_codec(AVFormatContext *s, break; case IEC61937_MPEG2_AAC: init_get_bits(&gbc, buf, AAC_ADTS_HEADER_SIZE * 8); - if (avpriv_aac_parse_header(&gbc, &aac_hdr)) { + if (avpriv_aac_parse_header(&gbc, &aac_hdr) < 0) { if (s) /* be silent during a probe */ av_log(s, AV_LOG_ERROR, "Invalid AAC packet in IEC 61937\n"); return AVERROR_INVALIDDATA; @@ -105,14 +105,19 @@ static int spdif_get_offset_and_codec(AVFormatContext *s, static int spdif_probe(AVProbeData *p) { - const uint8_t *buf = p->buf; - const uint8_t *probe_end = p->buf + FFMIN(2 * SPDIF_MAX_OFFSET, p->buf_size - 1); + enum AVCodecID codec; + return ff_spdif_probe (p->buf, p->buf_size, &codec); +} + +int ff_spdif_probe(const uint8_t *p_buf, int buf_size, enum AVCodecID *codec) +{ + const uint8_t *buf = p_buf; + const uint8_t *probe_end = p_buf + FFMIN(2 * SPDIF_MAX_OFFSET, buf_size - 1); const uint8_t *expected_code = buf + 7; uint32_t state = 0; int sync_codes = 0; int consecutive_codes = 0; int offset; - enum AVCodecID codec; for (; buf < probe_end; buf++) { state = (state << 8) | *buf; @@ -127,16 +132,16 @@ static int spdif_probe(AVProbeData *p) } else consecutive_codes = 0; - if (buf + 4 + AAC_ADTS_HEADER_SIZE > p->buf + p->buf_size) + if (buf + 4 + AAC_ADTS_HEADER_SIZE > p_buf + buf_size) break; /* continue probing to find more sync codes */ - probe_end = FFMIN(buf + SPDIF_MAX_OFFSET, p->buf + p->buf_size - 1); + probe_end = FFMIN(buf + SPDIF_MAX_OFFSET, p_buf + buf_size - 1); /* skip directly to the next sync code */ if (!spdif_get_offset_and_codec(NULL, (buf[2] << 8) | buf[1], - &buf[5], &offset, &codec)) { - if (buf + offset >= p->buf + p->buf_size) + &buf[5], &offset, codec)) { + if (buf + offset >= p_buf + buf_size) break; expected_code = buf + offset; buf = expected_code - 7; @@ -161,7 +166,7 @@ static int spdif_read_header(AVFormatContext *s) return 0; } -static int spdif_read_packet(AVFormatContext *s, AVPacket *pkt) +int ff_spdif_read_packet(AVFormatContext *s, AVPacket *pkt) { AVIOContext *pb = s->pb; enum IEC61937DataType data_type; @@ -171,7 +176,7 @@ static int spdif_read_packet(AVFormatContext *s, AVPacket *pkt) while (state != (AV_BSWAP16C(SYNCWORD1) << 16 | AV_BSWAP16C(SYNCWORD2))) { state = (state << 8) | avio_r8(pb); - if (pb->eof_reached) + if (url_feof(pb)) return AVERROR_EOF; } @@ -230,6 +235,6 @@ AVInputFormat ff_spdif_demuxer = { .long_name = NULL_IF_CONFIG_SMALL("IEC 61937 (compressed data in S/PDIF)"), .read_probe = spdif_probe, .read_header = spdif_read_header, - .read_packet = spdif_read_packet, + .read_packet = ff_spdif_read_packet, .flags = AVFMT_GENERIC_INDEX, }; diff --git a/libavformat/spdifenc.c b/libavformat/spdifenc.c index c350f7251c..210d63f39c 100644 --- a/libavformat/spdifenc.c +++ b/libavformat/spdifenc.c @@ -4,20 +4,20 @@ * Copyright (c) 2010 Anssi Hannula * Copyright (c) 2010 Carl Eugen Hoyos * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -92,7 +92,7 @@ static const AVOption options[] = { { NULL }, }; -static const AVClass class = { +static const AVClass spdif_class = { .class_name = "spdif", .item_name = av_default_item_name, .option = options, @@ -395,15 +395,15 @@ static int spdif_header_truehd(AVFormatContext *s, AVPacket *pkt) { IEC61937Context *ctx = s->priv_data; int mat_code_length = 0; - const char mat_end_code[16] = { 0xC3, 0xC2, 0xC0, 0xC4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x97, 0x11 }; + static const char mat_end_code[16] = { 0xC3, 0xC2, 0xC0, 0xC4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x97, 0x11 }; if (!ctx->hd_buf_count) { - const char mat_start_code[20] = { 0x07, 0x9E, 0x00, 0x03, 0x84, 0x01, 0x01, 0x01, 0x80, 0x00, 0x56, 0xA5, 0x3B, 0xF4, 0x81, 0x83, 0x49, 0x80, 0x77, 0xE0 }; + static const char mat_start_code[20] = { 0x07, 0x9E, 0x00, 0x03, 0x84, 0x01, 0x01, 0x01, 0x80, 0x00, 0x56, 0xA5, 0x3B, 0xF4, 0x81, 0x83, 0x49, 0x80, 0x77, 0xE0 }; mat_code_length = sizeof(mat_start_code) + BURST_HEADER_SIZE; memcpy(ctx->hd_buf, mat_start_code, sizeof(mat_start_code)); } else if (ctx->hd_buf_count == 12) { - const char mat_middle_code[12] = { 0xC3, 0xC1, 0x42, 0x49, 0x3B, 0xFA, 0x82, 0x83, 0x49, 0x80, 0x77, 0xE0 }; + static const char mat_middle_code[12] = { 0xC3, 0xC1, 0x42, 0x49, 0x3B, 0xFA, 0x82, 0x83, 0x49, 0x80, 0x77, 0xE0 }; mat_code_length = sizeof(mat_middle_code) + MAT_MIDDLE_CODE_OFFSET; memcpy(&ctx->hd_buf[12 * TRUEHD_FRAME_OFFSET - BURST_HEADER_SIZE + MAT_MIDDLE_CODE_OFFSET], mat_middle_code, sizeof(mat_middle_code)); @@ -520,13 +520,13 @@ static int spdif_write_packet(struct AVFormatContext *s, AVPacket *pkt) } if (ctx->extra_bswap ^ (ctx->spdif_flags & SPDIF_FLAG_BIGENDIAN)) { - avio_write(s->pb, ctx->out_buf, ctx->out_bytes & ~1); + avio_write(s->pb, ctx->out_buf, ctx->out_bytes & ~1); } else { - av_fast_malloc(&ctx->buffer, &ctx->buffer_size, ctx->out_bytes + FF_INPUT_BUFFER_PADDING_SIZE); - if (!ctx->buffer) - return AVERROR(ENOMEM); - ff_spdif_bswap_buf16((uint16_t *)ctx->buffer, (uint16_t *)ctx->out_buf, ctx->out_bytes >> 1); - avio_write(s->pb, ctx->buffer, ctx->out_bytes & ~1); + av_fast_malloc(&ctx->buffer, &ctx->buffer_size, ctx->out_bytes + FF_INPUT_BUFFER_PADDING_SIZE); + if (!ctx->buffer) + return AVERROR(ENOMEM); + ff_spdif_bswap_buf16((uint16_t *)ctx->buffer, (uint16_t *)ctx->out_buf, ctx->out_bytes >> 1); + avio_write(s->pb, ctx->buffer, ctx->out_bytes & ~1); } /* a final lone byte has to be MSB aligned */ @@ -552,5 +552,5 @@ AVOutputFormat ff_spdif_muxer = { .write_packet = spdif_write_packet, .write_trailer = spdif_write_trailer, .flags = AVFMT_NOTIMESTAMPS, - .priv_class = &class, + .priv_class = &spdif_class, }; diff --git a/libavformat/srtdec.c b/libavformat/srtdec.c index 9db5133a21..7f911bd05b 100644 --- a/libavformat/srtdec.c +++ b/libavformat/srtdec.c @@ -2,101 +2,164 @@ * SubRip subtitle demuxer * Copyright (c) 2010 Aurelien Jacobs <aurel@gnuage.org> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "avformat.h" #include "internal.h" +#include "subtitles.h" +#include "libavutil/bprint.h" #include "libavutil/intreadwrite.h" +typedef struct { + FFDemuxSubtitlesQueue q; +} SRTContext; + static int srt_probe(AVProbeData *p) { - unsigned char *ptr = p->buf; + const unsigned char *ptr = p->buf; int i, v, num = 0; if (AV_RB24(ptr) == 0xEFBBBF) ptr += 3; /* skip UTF-8 BOM */ + while (*ptr == '\r' || *ptr == '\n') + ptr++; for (i=0; i<2; i++) { - if (num == i && sscanf(ptr, "%*d:%*2d:%*2d%*1[,.]%*3d --> %*d:%*2d:%*2d%*1[,.]%3d", &v) == 1) + if ((num == i || num + 1 == i) + && sscanf(ptr, "%*d:%*2d:%*2d%*1[,.]%*3d --> %*d:%*2d:%*2d%*1[,.]%3d", &v) == 1) return AVPROBE_SCORE_MAX; num = atoi(ptr); - ptr += strcspn(ptr, "\n") + 1; + ptr += ff_subtitles_next_line(ptr); } return 0; } +static int64_t get_pts(const char **buf, int *duration, + int32_t *x1, int32_t *y1, int32_t *x2, int32_t *y2) +{ + int i; + + for (i=0; i<2; i++) { + int hh1, mm1, ss1, ms1; + int hh2, mm2, ss2, ms2; + if (sscanf(*buf, "%d:%2d:%2d%*1[,.]%3d --> %d:%2d:%2d%*1[,.]%3d" + "%*[ ]X1:%u X2:%u Y1:%u Y2:%u", + &hh1, &mm1, &ss1, &ms1, + &hh2, &mm2, &ss2, &ms2, + x1, x2, y1, y2) >= 8) { + int64_t start = (hh1*3600LL + mm1*60LL + ss1) * 1000LL + ms1; + int64_t end = (hh2*3600LL + mm2*60LL + ss2) * 1000LL + ms2; + *duration = end - start; + *buf += ff_subtitles_next_line(*buf); + return start; + } + *buf += ff_subtitles_next_line(*buf); + } + return AV_NOPTS_VALUE; +} + static int srt_read_header(AVFormatContext *s) { + SRTContext *srt = s->priv_data; + AVBPrint buf; AVStream *st = avformat_new_stream(s, NULL); + int res = 0; + if (!st) - return -1; + return AVERROR(ENOMEM); avpriv_set_pts_info(st, 64, 1, 1000); st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE; - st->codec->codec_id = AV_CODEC_ID_SRT; - return 0; -} + st->codec->codec_id = AV_CODEC_ID_SUBRIP; -static int64_t get_pts(const char *buf) -{ - int i, v, hour, min, sec, hsec; + av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED); - for (i=0; i<2; i++) { - if (sscanf(buf, "%d:%2d:%2d%*1[,.]%3d --> %*d:%*2d:%*2d%*1[,.]%3d", - &hour, &min, &sec, &hsec, &v) == 5) { - min += 60*hour; - sec += 60*min; - return sec*1000+hsec; + while (!url_feof(s->pb)) { + ff_subtitles_read_chunk(s->pb, &buf); + + if (buf.len) { + int64_t pos = avio_tell(s->pb); + int64_t pts; + int duration; + const char *ptr = buf.str; + int32_t x1 = -1, y1 = -1, x2 = -1, y2 = -1; + AVPacket *sub; + + pts = get_pts(&ptr, &duration, &x1, &y1, &x2, &y2); + if (pts != AV_NOPTS_VALUE) { + int len = buf.len - (ptr - buf.str); + if (len <= 0) + continue; + sub = ff_subtitles_queue_insert(&srt->q, ptr, len, 0); + if (!sub) { + res = AVERROR(ENOMEM); + goto end; + } + sub->pos = pos; + sub->pts = pts; + sub->duration = duration; + if (x1 != -1) { + uint8_t *p = av_packet_new_side_data(sub, AV_PKT_DATA_SUBTITLE_POSITION, 16); + if (p) { + AV_WL32(p, x1); + AV_WL32(p + 4, y1); + AV_WL32(p + 8, x2); + AV_WL32(p + 12, y2); + } + } + } } - buf += strcspn(buf, "\n") + 1; } - return AV_NOPTS_VALUE; + + ff_subtitles_queue_finalize(&srt->q); + +end: + av_bprint_finalize(&buf, NULL); + return res; +} + +static int srt_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + SRTContext *srt = s->priv_data; + return ff_subtitles_queue_read_packet(&srt->q, pkt); } -static inline int is_eol(char c) +static int srt_read_seek(AVFormatContext *s, int stream_index, + int64_t min_ts, int64_t ts, int64_t max_ts, int flags) { - return c == '\r' || c == '\n'; + SRTContext *srt = s->priv_data; + return ff_subtitles_queue_seek(&srt->q, s, stream_index, + min_ts, ts, max_ts, flags); } -static int srt_read_packet(AVFormatContext *s, AVPacket *pkt) +static int srt_read_close(AVFormatContext *s) { - char buffer[2048], *ptr = buffer, *ptr2; - int64_t pos = avio_tell(s->pb); - int res = AVERROR_EOF; - - do { - ptr2 = ptr; - ptr += ff_get_line(s->pb, ptr, sizeof(buffer)+buffer-ptr); - } while (!is_eol(*ptr2) && !s->pb->eof_reached && ptr-buffer<sizeof(buffer)-1); - - if (buffer[0] && !(res = av_new_packet(pkt, ptr-buffer))) { - memcpy(pkt->data, buffer, pkt->size); - pkt->flags |= AV_PKT_FLAG_KEY; - pkt->pos = pos; - pkt->pts = pkt->dts = get_pts(pkt->data); - } - return res; + SRTContext *srt = s->priv_data; + ff_subtitles_queue_clean(&srt->q); + return 0; } AVInputFormat ff_srt_demuxer = { .name = "srt", .long_name = NULL_IF_CONFIG_SMALL("SubRip subtitle"), + .priv_data_size = sizeof(SRTContext), .read_probe = srt_probe, .read_header = srt_read_header, .read_packet = srt_read_packet, - .flags = AVFMT_GENERIC_INDEX, + .read_seek2 = srt_read_seek, + .read_close = srt_read_close, }; diff --git a/libavformat/srtenc.c b/libavformat/srtenc.c new file mode 100644 index 0000000000..b43504bdc4 --- /dev/null +++ b/libavformat/srtenc.c @@ -0,0 +1,115 @@ +/* + * SubRip subtitle muxer + * Copyright (c) 2012 Nicolas George <nicolas.george@normalesup.org> + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "avformat.h" +#include "internal.h" +#include "libavutil/log.h" +#include "libavutil/intreadwrite.h" + +/* TODO: add options for: + - character encoding; + - LF / CRLF; + - byte order mark. + */ + +typedef struct SRTContext{ + unsigned index; +} SRTContext; + +static int srt_write_header(AVFormatContext *avf) +{ + SRTContext *srt = avf->priv_data; + + if (avf->nb_streams != 1 || + avf->streams[0]->codec->codec_type != AVMEDIA_TYPE_SUBTITLE) { + av_log(avf, AV_LOG_ERROR, + "SRT supports only a single subtitles stream.\n"); + return AVERROR(EINVAL); + } + if (avf->streams[0]->codec->codec_id != AV_CODEC_ID_TEXT && + avf->streams[0]->codec->codec_id != AV_CODEC_ID_SUBRIP && + avf->streams[0]->codec->codec_id != AV_CODEC_ID_SRT) { + av_log(avf, AV_LOG_ERROR, + "Unsupported subtitles codec: %s\n", + avcodec_get_name(avf->streams[0]->codec->codec_id)); + return AVERROR(EINVAL); + } + avpriv_set_pts_info(avf->streams[0], 64, 1, 1000); + srt->index = 1; + return 0; +} + +static int srt_write_packet(AVFormatContext *avf, AVPacket *pkt) +{ + SRTContext *srt = avf->priv_data; + int write_ts = avf->streams[0]->codec->codec_id != AV_CODEC_ID_SRT; + + if (write_ts) { + int64_t s = pkt->pts, e, d = pkt->duration; + int size, x1 = -1, y1 = -1, x2 = -1, y2 = -1; + const uint8_t *p; + + p = av_packet_get_side_data(pkt, AV_PKT_DATA_SUBTITLE_POSITION, &size); + if (p && size == 16) { + x1 = AV_RL32(p ); + y1 = AV_RL32(p + 4); + x2 = AV_RL32(p + 8); + y2 = AV_RL32(p + 12); + } + + if (d <= 0) + /* For backward compatibility, fallback to convergence_duration. */ + d = pkt->convergence_duration; + if (s == AV_NOPTS_VALUE || d < 0) { + av_log(avf, AV_LOG_WARNING, + "Insufficient timestamps in event number %d.\n", srt->index); + return 0; + } + e = s + d; + avio_printf(avf->pb, "%d\n%02d:%02d:%02d,%03d --> %02d:%02d:%02d,%03d", + srt->index, + (int)(s / 3600000), (int)(s / 60000) % 60, + (int)(s / 1000) % 60, (int)(s % 1000), + (int)(e / 3600000), (int)(e / 60000) % 60, + (int)(e / 1000) % 60, (int)(e % 1000)); + if (p) + avio_printf(avf->pb, " X1:%03d X2:%03d Y1:%03d Y2:%03d", + x1, x2, y1, y2); + avio_printf(avf->pb, "\n"); + } + avio_write(avf->pb, pkt->data, pkt->size); + if (write_ts) + avio_write(avf->pb, "\n\n", 2); + srt->index++; + return 0; +} + +AVOutputFormat ff_srt_muxer = { + .name = "srt", + .long_name = NULL_IF_CONFIG_SMALL("SubRip subtitle"), + .mime_type = "application/x-subrip", + .extensions = "srt", + .priv_data_size = sizeof(SRTContext), + .write_header = srt_write_header, + .write_packet = srt_write_packet, + .flags = AVFMT_VARIABLE_FPS | AVFMT_TS_NONSTRICT, + .subtitle_codec = AV_CODEC_ID_SUBRIP, +}; diff --git a/libavformat/srtp.c b/libavformat/srtp.c index 6659bfc5c9..b6e821115c 100644 --- a/libavformat/srtp.c +++ b/libavformat/srtp.c @@ -2,20 +2,20 @@ * SRTP encryption/decryption * Copyright (c) 2012 Martin Storsjo * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/srtp.h b/libavformat/srtp.h index 18ed177af5..3189f8f54b 100644 --- a/libavformat/srtp.h +++ b/libavformat/srtp.h @@ -2,20 +2,20 @@ * SRTP encryption/decryption * Copyright (c) 2012 Martin Storsjo * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/srtpproto.c b/libavformat/srtpproto.c index f9b94d7e91..f9d6b1733f 100644 --- a/libavformat/srtpproto.c +++ b/libavformat/srtpproto.c @@ -2,20 +2,20 @@ * SRTP network protocol * Copyright (c) 2012 Martin Storsjo * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/subtitles.c b/libavformat/subtitles.c new file mode 100644 index 0000000000..fce2bf190b --- /dev/null +++ b/libavformat/subtitles.c @@ -0,0 +1,283 @@ +/* + * Copyright (c) 2012-2013 Clément BÅ“sch <u pkh me> + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "avformat.h" +#include "subtitles.h" +#include "libavutil/avassert.h" +#include "libavutil/avstring.h" + +AVPacket *ff_subtitles_queue_insert(FFDemuxSubtitlesQueue *q, + const uint8_t *event, int len, int merge) +{ + AVPacket *subs, *sub; + + if (merge && q->nb_subs > 0) { + /* merge with previous event */ + + int old_len; + sub = &q->subs[q->nb_subs - 1]; + old_len = sub->size; + if (av_grow_packet(sub, len) < 0) + return NULL; + memcpy(sub->data + old_len, event, len); + } else { + /* new event */ + + if (q->nb_subs >= INT_MAX/sizeof(*q->subs) - 1) + return NULL; + subs = av_fast_realloc(q->subs, &q->allocated_size, + (q->nb_subs + 1) * sizeof(*q->subs)); + if (!subs) + return NULL; + q->subs = subs; + sub = &subs[q->nb_subs++]; + if (av_new_packet(sub, len) < 0) + return NULL; + sub->flags |= AV_PKT_FLAG_KEY; + sub->pts = sub->dts = 0; + memcpy(sub->data, event, len); + } + return sub; +} + +static int cmp_pkt_sub_ts_pos(const void *a, const void *b) +{ + const AVPacket *s1 = a; + const AVPacket *s2 = b; + if (s1->pts == s2->pts) { + if (s1->pos == s2->pos) + return 0; + return s1->pos > s2->pos ? 1 : -1; + } + return s1->pts > s2->pts ? 1 : -1; +} + +static int cmp_pkt_sub_pos_ts(const void *a, const void *b) +{ + const AVPacket *s1 = a; + const AVPacket *s2 = b; + if (s1->pos == s2->pos) { + if (s1->pts == s2->pts) + return 0; + return s1->pts > s2->pts ? 1 : -1; + } + return s1->pos > s2->pos ? 1 : -1; +} + +void ff_subtitles_queue_finalize(FFDemuxSubtitlesQueue *q) +{ + int i; + + qsort(q->subs, q->nb_subs, sizeof(*q->subs), + q->sort == SUB_SORT_TS_POS ? cmp_pkt_sub_ts_pos + : cmp_pkt_sub_pos_ts); + for (i = 0; i < q->nb_subs; i++) + if (q->subs[i].duration == -1 && i < q->nb_subs - 1) + q->subs[i].duration = q->subs[i + 1].pts - q->subs[i].pts; +} + +int ff_subtitles_queue_read_packet(FFDemuxSubtitlesQueue *q, AVPacket *pkt) +{ + AVPacket *sub = q->subs + q->current_sub_idx; + + if (q->current_sub_idx == q->nb_subs) + return AVERROR_EOF; + if (av_copy_packet(pkt, sub) < 0) { + return AVERROR(ENOMEM); + } + + pkt->dts = pkt->pts; + q->current_sub_idx++; + return 0; +} + +static int search_sub_ts(const FFDemuxSubtitlesQueue *q, int64_t ts) +{ + int s1 = 0, s2 = q->nb_subs - 1; + + if (s2 < s1) + return AVERROR(ERANGE); + + for (;;) { + int mid; + + if (s1 == s2) + return s1; + if (s1 == s2 - 1) + return q->subs[s1].pts <= q->subs[s2].pts ? s1 : s2; + mid = (s1 + s2) / 2; + if (q->subs[mid].pts <= ts) + s1 = mid; + else + s2 = mid; + } +} + +int ff_subtitles_queue_seek(FFDemuxSubtitlesQueue *q, AVFormatContext *s, int stream_index, + int64_t min_ts, int64_t ts, int64_t max_ts, int flags) +{ + if (flags & AVSEEK_FLAG_BYTE) { + return AVERROR(ENOSYS); + } else if (flags & AVSEEK_FLAG_FRAME) { + if (ts < 0 || ts >= q->nb_subs) + return AVERROR(ERANGE); + q->current_sub_idx = ts; + } else { + int i, idx = search_sub_ts(q, ts); + int64_t ts_selected; + + if (idx < 0) + return idx; + for (i = idx; i < q->nb_subs && q->subs[i].pts < min_ts; i++) + if (stream_index == -1 || q->subs[i].stream_index == stream_index) + idx = i; + for (i = idx; i > 0 && q->subs[i].pts > max_ts; i--) + if (stream_index == -1 || q->subs[i].stream_index == stream_index) + idx = i; + + ts_selected = q->subs[idx].pts; + if (ts_selected < min_ts || ts_selected > max_ts) + return AVERROR(ERANGE); + + /* look back in the latest subtitles for overlapping subtitles */ + for (i = idx - 1; i >= 0; i--) { + int64_t pts = q->subs[i].pts; + if (q->subs[i].duration <= 0 || + (stream_index != -1 && q->subs[i].stream_index != stream_index)) + continue; + if (pts >= min_ts && pts > ts_selected - q->subs[i].duration) + idx = i; + else + break; + } + + /* If the queue is used to store multiple subtitles streams (like with + * VobSub) and the stream index is not specified, we need to make sure + * to focus on the smallest file position offset for a same timestamp; + * queue is ordered by pts and then filepos, so we can take the first + * entry for a given timestamp. */ + if (stream_index == -1) + while (idx > 0 && q->subs[idx - 1].pts == q->subs[idx].pts) + idx--; + + q->current_sub_idx = idx; + } + return 0; +} + +void ff_subtitles_queue_clean(FFDemuxSubtitlesQueue *q) +{ + int i; + + for (i = 0; i < q->nb_subs; i++) + av_free_packet(&q->subs[i]); + av_freep(&q->subs); + q->nb_subs = q->allocated_size = q->current_sub_idx = 0; +} + +int ff_smil_extract_next_chunk(AVIOContext *pb, AVBPrint *buf, char *c) +{ + int i = 0; + char end_chr; + + if (!*c) // cached char? + *c = avio_r8(pb); + if (!*c) + return 0; + + end_chr = *c == '<' ? '>' : '<'; + do { + av_bprint_chars(buf, *c, 1); + *c = avio_r8(pb); + i++; + } while (*c != end_chr && *c); + if (end_chr == '>') { + av_bprint_chars(buf, '>', 1); + *c = 0; + } + return i; +} + +const char *ff_smil_get_attr_ptr(const char *s, const char *attr) +{ + int in_quotes = 0; + const int len = strlen(attr); + + while (*s) { + while (*s) { + if (!in_quotes && av_isspace(*s)) + break; + in_quotes ^= *s == '"'; // XXX: support escaping? + s++; + } + while (av_isspace(*s)) + s++; + if (!av_strncasecmp(s, attr, len) && s[len] == '=') + return s + len + 1 + (s[len + 1] == '"'); + } + return NULL; +} + +static inline int is_eol(char c) +{ + return c == '\r' || c == '\n'; +} + +void ff_subtitles_read_chunk(AVIOContext *pb, AVBPrint *buf) +{ + char eol_buf[5], last_was_cr = 0; + int n = 0, i = 0, nb_eol = 0; + + av_bprint_clear(buf); + + for (;;) { + char c = avio_r8(pb); + + if (!c) + break; + + /* ignore all initial line breaks */ + if (n == 0 && is_eol(c)) + continue; + + /* line break buffering: we don't want to add the trailing \r\n */ + if (is_eol(c)) { + nb_eol += c == '\n' || last_was_cr; + if (nb_eol == 2) + break; + eol_buf[i++] = c; + if (i == sizeof(eol_buf) - 1) + break; + last_was_cr = c == '\r'; + continue; + } + + /* only one line break followed by data: we flush the line breaks + * buffer */ + if (i) { + eol_buf[i] = 0; + av_bprintf(buf, "%s", eol_buf); + i = nb_eol = 0; + } + + av_bprint_chars(buf, c, 1); + n++; + } +} diff --git a/libavformat/subtitles.h b/libavformat/subtitles.h new file mode 100644 index 0000000000..b5a96ec08c --- /dev/null +++ b/libavformat/subtitles.h @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2012 Clément BÅ“sch + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVFORMAT_SUBTITLES_H +#define AVFORMAT_SUBTITLES_H + +#include <stdint.h> +#include "avformat.h" +#include "libavutil/bprint.h" + +enum sub_sort { + SUB_SORT_TS_POS = 0, ///< sort by timestamps, then position + SUB_SORT_POS_TS, ///< sort by position, then timestamps +}; + +typedef struct { + AVPacket *subs; ///< array of subtitles packets + int nb_subs; ///< number of subtitles packets + int allocated_size; ///< allocated size for subs + int current_sub_idx; ///< current position for the read packet callback + enum sub_sort sort; ///< sort method to use when finalizing subtitles +} FFDemuxSubtitlesQueue; + +/** + * Insert a new subtitle event. + * + * @param event the subtitle line, may not be zero terminated + * @param len the length of the event (in strlen() sense, so without '\0') + * @param merge set to 1 if the current event should be concatenated with the + * previous one instead of adding a new entry, 0 otherwise + */ +AVPacket *ff_subtitles_queue_insert(FFDemuxSubtitlesQueue *q, + const uint8_t *event, int len, int merge); + +/** + * Set missing durations and sort subtitles by PTS, and then byte position. + */ +void ff_subtitles_queue_finalize(FFDemuxSubtitlesQueue *q); + +/** + * Generic read_packet() callback for subtitles demuxers using this queue + * system. + */ +int ff_subtitles_queue_read_packet(FFDemuxSubtitlesQueue *q, AVPacket *pkt); + +/** + * Update current_sub_idx to emulate a seek. Except the first parameter, it + * matches AVInputFormat->read_seek2 prototypes. + */ +int ff_subtitles_queue_seek(FFDemuxSubtitlesQueue *q, AVFormatContext *s, int stream_index, + int64_t min_ts, int64_t ts, int64_t max_ts, int flags); + +/** + * Remove and destroy all the subtitles packets. + */ +void ff_subtitles_queue_clean(FFDemuxSubtitlesQueue *q); + +/** + * SMIL helper to load next chunk ("<...>" or untagged content) in buf. + * + * @param c cached character, to avoid a backward seek + */ +int ff_smil_extract_next_chunk(AVIOContext *pb, AVBPrint *buf, char *c); + +/** + * SMIL helper to point on the value of an attribute in the given tag. + * + * @param s SMIL tag ("<...>") + * @param attr the attribute to look for + */ +const char *ff_smil_get_attr_ptr(const char *s, const char *attr); + +/** + * @brief Read a subtitles chunk. + * + * A chunk is defined by a multiline "event", ending with a second line break. + * The trailing line breaks are trimmed. CRLF are supported. + * Example: "foo\r\nbar\r\n\r\nnext" will print "foo\r\nbar" into buf, and pb + * will focus on the 'n' of the "next" string. + * + * @param pb I/O context + * @param buf an initialized buf where the chunk is written + * + * @note buf is cleared before writing into it. + */ +void ff_subtitles_read_chunk(AVIOContext *pb, AVBPrint *buf); + +/** + * Get the number of characters to increment to jump to the next line, or to + * the end of the string. + * The function handles the following line breaks schemes: + * LF, CRLF (MS), or standalone CR (old MacOS). + */ +static av_always_inline int ff_subtitles_next_line(const char *ptr) +{ + int n = strcspn(ptr, "\r\n"); + ptr += n; + if (*ptr == '\r') { + ptr++; + n++; + } + if (*ptr == '\n') + n++; + return n; +} + +#endif /* AVFORMAT_SUBTITLES_H */ diff --git a/libavformat/subviewer1dec.c b/libavformat/subviewer1dec.c new file mode 100644 index 0000000000..1b831b7b8f --- /dev/null +++ b/libavformat/subviewer1dec.c @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2012 Clément BÅ“sch + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * SubViewer v1 subtitle demuxer + */ + +#include "avformat.h" +#include "internal.h" +#include "subtitles.h" + +typedef struct { + FFDemuxSubtitlesQueue q; +} SubViewer1Context; + +static int subviewer1_probe(AVProbeData *p) +{ + const unsigned char *ptr = p->buf; + + if (strstr(ptr, "******** START SCRIPT ********")) + return AVPROBE_SCORE_EXTENSION; + return 0; +} + +static int subviewer1_read_header(AVFormatContext *s) +{ + int delay = 0; + AVPacket *sub = NULL; + SubViewer1Context *subviewer1 = s->priv_data; + AVStream *st = avformat_new_stream(s, NULL); + + if (!st) + return AVERROR(ENOMEM); + avpriv_set_pts_info(st, 64, 1, 1); + st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE; + st->codec->codec_id = AV_CODEC_ID_SUBVIEWER1; + + while (!url_feof(s->pb)) { + char line[4096]; + int len = ff_get_line(s->pb, line, sizeof(line)); + int hh, mm, ss; + + if (!len) + break; + + if (!strncmp(line, "[DELAY]", 7)) { + ff_get_line(s->pb, line, sizeof(line)); + sscanf(line, "%d", &delay); + } + + if (sscanf(line, "[%d:%d:%d]", &hh, &mm, &ss) == 3) { + const int64_t pos = avio_tell(s->pb); + int64_t pts_start = hh*3600LL + mm*60LL + ss + delay; + + len = ff_get_line(s->pb, line, sizeof(line)); + line[strcspn(line, "\r\n")] = 0; + if (!*line) { + if (sub) + sub->duration = pts_start - sub->pts; + } else { + sub = ff_subtitles_queue_insert(&subviewer1->q, line, len, 0); + if (!sub) + return AVERROR(ENOMEM); + sub->pos = pos; + sub->pts = pts_start; + sub->duration = -1; + } + } + } + + ff_subtitles_queue_finalize(&subviewer1->q); + return 0; +} + +static int subviewer1_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + SubViewer1Context *subviewer1 = s->priv_data; + return ff_subtitles_queue_read_packet(&subviewer1->q, pkt); +} + +static int subviewer1_read_seek(AVFormatContext *s, int stream_index, + int64_t min_ts, int64_t ts, int64_t max_ts, int flags) +{ + SubViewer1Context *subviewer1 = s->priv_data; + return ff_subtitles_queue_seek(&subviewer1->q, s, stream_index, + min_ts, ts, max_ts, flags); +} + +static int subviewer1_read_close(AVFormatContext *s) +{ + SubViewer1Context *subviewer1 = s->priv_data; + ff_subtitles_queue_clean(&subviewer1->q); + return 0; +} + +AVInputFormat ff_subviewer1_demuxer = { + .name = "subviewer1", + .long_name = NULL_IF_CONFIG_SMALL("SubViewer v1 subtitle format"), + .priv_data_size = sizeof(SubViewer1Context), + .read_probe = subviewer1_probe, + .read_header = subviewer1_read_header, + .read_packet = subviewer1_read_packet, + .read_seek2 = subviewer1_read_seek, + .read_close = subviewer1_read_close, + .extensions = "sub", +}; diff --git a/libavformat/subviewerdec.c b/libavformat/subviewerdec.c new file mode 100644 index 0000000000..9e645d25c7 --- /dev/null +++ b/libavformat/subviewerdec.c @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2012 Clément BÅ“sch + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * SubViewer subtitle demuxer + * @see https://en.wikipedia.org/wiki/SubViewer + */ + +#include "avformat.h" +#include "internal.h" +#include "subtitles.h" +#include "libavcodec/internal.h" +#include "libavutil/avstring.h" +#include "libavutil/bprint.h" +#include "libavutil/intreadwrite.h" + +typedef struct { + FFDemuxSubtitlesQueue q; +} SubViewerContext; + +static int subviewer_probe(AVProbeData *p) +{ + char c; + const unsigned char *ptr = p->buf; + + if (AV_RB24(ptr) == 0xEFBBBF) + ptr += 3; /* skip UTF-8 BOM */ + if (sscanf(ptr, "%*u:%*u:%*u.%*u,%*u:%*u:%*u.%*u%c", &c) == 1) + return AVPROBE_SCORE_EXTENSION; + if (!strncmp(ptr, "[INFORMATION]", 13)) + return AVPROBE_SCORE_MAX/3; + return 0; +} + +static int read_ts(const char *s, int64_t *start, int *duration) +{ + int64_t end; + int hh1, mm1, ss1, ms1; + int hh2, mm2, ss2, ms2; + + if (sscanf(s, "%u:%u:%u.%u,%u:%u:%u.%u", + &hh1, &mm1, &ss1, &ms1, &hh2, &mm2, &ss2, &ms2) == 8) { + end = (hh2*3600LL + mm2*60LL + ss2) * 100LL + ms2; + *start = (hh1*3600LL + mm1*60LL + ss1) * 100LL + ms1; + *duration = end - *start; + return 0; + } + return -1; +} + +static int subviewer_read_header(AVFormatContext *s) +{ + SubViewerContext *subviewer = s->priv_data; + AVStream *st = avformat_new_stream(s, NULL); + AVBPrint header; + int res = 0, new_event = 1; + int64_t pts_start = AV_NOPTS_VALUE; + int duration = -1; + AVPacket *sub = NULL; + + if (!st) + return AVERROR(ENOMEM); + avpriv_set_pts_info(st, 64, 1, 100); + st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE; + st->codec->codec_id = AV_CODEC_ID_SUBVIEWER; + + av_bprint_init(&header, 0, AV_BPRINT_SIZE_UNLIMITED); + + while (!url_feof(s->pb)) { + char line[2048]; + int64_t pos = 0; + int len = ff_get_line(s->pb, line, sizeof(line)); + + if (!len) + break; + + line[strcspn(line, "\r\n")] = 0; + + if (line[0] == '[' && strncmp(line, "[br]", 4)) { + + /* ignore event style, XXX: add to side_data? */ + if (strstr(line, "[COLF]") || strstr(line, "[SIZE]") || + strstr(line, "[FONT]") || strstr(line, "[STYLE]")) + continue; + + if (!st->codec->extradata) { // header not finalized yet + av_bprintf(&header, "%s\n", line); + if (!strncmp(line, "[END INFORMATION]", 17) || !strncmp(line, "[SUBTITLE]", 10)) { + /* end of header */ + res = avpriv_bprint_to_extradata(st->codec, &header); + if (res < 0) + goto end; + } else if (strncmp(line, "[INFORMATION]", 13)) { + /* assume file metadata at this point */ + int i, j = 0; + char key[32], value[128]; + + for (i = 1; i < sizeof(key) - 1 && line[i] && line[i] != ']'; i++) + key[i - 1] = av_tolower(line[i]); + key[i - 1] = 0; + + if (line[i] == ']') + i++; + while (line[i] == ' ') + i++; + while (j < sizeof(value) - 1 && line[i] && line[i] != ']') + value[j++] = line[i++]; + value[j] = 0; + + av_dict_set(&s->metadata, key, value, 0); + } + } + } else if (read_ts(line, &pts_start, &duration) >= 0) { + new_event = 1; + pos = avio_tell(s->pb); + } else if (*line) { + if (!new_event) { + sub = ff_subtitles_queue_insert(&subviewer->q, "\n", 1, 1); + if (!sub) { + res = AVERROR(ENOMEM); + goto end; + } + } + sub = ff_subtitles_queue_insert(&subviewer->q, line, strlen(line), !new_event); + if (!sub) { + res = AVERROR(ENOMEM); + goto end; + } + if (new_event) { + sub->pos = pos; + sub->pts = pts_start; + sub->duration = duration; + } + new_event = 0; + } + } + + ff_subtitles_queue_finalize(&subviewer->q); + +end: + av_bprint_finalize(&header, NULL); + return res; +} + +static int subviewer_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + SubViewerContext *subviewer = s->priv_data; + return ff_subtitles_queue_read_packet(&subviewer->q, pkt); +} + +static int subviewer_read_seek(AVFormatContext *s, int stream_index, + int64_t min_ts, int64_t ts, int64_t max_ts, int flags) +{ + SubViewerContext *subviewer = s->priv_data; + return ff_subtitles_queue_seek(&subviewer->q, s, stream_index, + min_ts, ts, max_ts, flags); +} + +static int subviewer_read_close(AVFormatContext *s) +{ + SubViewerContext *subviewer = s->priv_data; + ff_subtitles_queue_clean(&subviewer->q); + return 0; +} + +AVInputFormat ff_subviewer_demuxer = { + .name = "subviewer", + .long_name = NULL_IF_CONFIG_SMALL("SubViewer subtitle format"), + .priv_data_size = sizeof(SubViewerContext), + .read_probe = subviewer_probe, + .read_header = subviewer_read_header, + .read_packet = subviewer_read_packet, + .read_seek2 = subviewer_read_seek, + .read_close = subviewer_read_close, + .extensions = "sub", +}; diff --git a/libavformat/swf.c b/libavformat/swf.c index e6adf69c02..1aa434a0ea 100644 --- a/libavformat/swf.c +++ b/libavformat/swf.c @@ -3,20 +3,20 @@ * Copyright (c) 2000 Fabrice Bellard * Copyright (c) 2003 Tinic Uro * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/swf.h b/libavformat/swf.h index 79c3c1d033..c1667b3c24 100644 --- a/libavformat/swf.h +++ b/libavformat/swf.h @@ -3,26 +3,32 @@ * Copyright (c) 2000 Fabrice Bellard * Copyright (c) 2003 Tinic Uro * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef AVFORMAT_SWF_H #define AVFORMAT_SWF_H +#include "config.h" + +#if CONFIG_ZLIB +#include <zlib.h> +#endif + #include "libavutil/fifo.h" #include "avformat.h" #include "avio.h" @@ -32,20 +38,73 @@ #define DUMMY_FILE_SIZE (100 * 1024 * 1024) #define DUMMY_DURATION 600 /* in seconds */ -#define TAG_END 0 -#define TAG_SHOWFRAME 1 -#define TAG_DEFINESHAPE 2 -#define TAG_FREECHARACTER 3 -#define TAG_PLACEOBJECT 4 -#define TAG_REMOVEOBJECT 5 -#define TAG_STREAMHEAD 18 -#define TAG_STREAMBLOCK 19 -#define TAG_JPEG2 21 -#define TAG_PLACEOBJECT2 26 -#define TAG_STREAMHEAD2 45 -#define TAG_VIDEOSTREAM 60 -#define TAG_VIDEOFRAME 61 -#define TAG_FILEATTRIBUTES 69 +enum { + TAG_END = 0, + TAG_SHOWFRAME = 1, + TAG_DEFINESHAPE = 2, + TAG_FREECHARACTER = 3, + TAG_PLACEOBJECT = 4, + TAG_REMOVEOBJECT = 5, + TAG_DEFINEBITS = 6, + TAG_DEFINEBUTTON = 7, + TAG_JPEGTABLES = 8, + TAG_SETBACKGROUNDCOLOR = 9, + TAG_DEFINEFONT = 10, + TAG_DEFINETEXT = 11, + TAG_DOACTION = 12, + TAG_DEFINEFONTINFO = 13, + TAG_DEFINESOUND = 14, + TAG_STARTSOUND = 15, + TAG_DEFINEBUTTONSOUND = 17, + TAG_STREAMHEAD = 18, + TAG_STREAMBLOCK = 19, + TAG_DEFINEBITSLOSSLESS = 20, + TAG_JPEG2 = 21, + TAG_DEFINESHAPE2 = 22, + TAG_DEFINEBUTTONCXFORM = 23, + TAG_PROTECT = 24, + TAG_PLACEOBJECT2 = 26, + TAG_REMOVEOBJECT2 = 28, + TAG_DEFINESHAPE3 = 32, + TAG_DEFINETEXT2 = 33, + TAG_DEFINEBUTTON2 = 34, + TAG_DEFINEBITSJPEG3 = 35, + TAG_DEFINEBITSLOSSLESS2 = 36, + TAG_DEFINEEDITTEXT = 37, + TAG_DEFINESPRITE = 39, + TAG_FRAMELABEL = 43, + TAG_STREAMHEAD2 = 45, + TAG_DEFINEMORPHSHAPE = 46, + TAG_DEFINEFONT2 = 48, + TAG_EXPORTASSETS = 56, + TAG_IMPORTASSETS = 57, + TAG_ENABLEDEBUGGER = 58, + TAG_DOINITACTION = 59, + TAG_VIDEOSTREAM = 60, + TAG_VIDEOFRAME = 61, + TAG_DEFINEFONTINFO2 = 62, + TAG_ENABLEDEBUGGER2 = 64, + TAG_SCRIPTLIMITS = 65, + TAG_SETTABINDEX = 66, + TAG_FILEATTRIBUTES = 69, + TAG_PLACEOBJECT3 = 70, + TAG_IMPORTASSETS2 = 71, + TAG_DEFINEFONTALIGNZONES = 73, + TAG_CSMTEXTSETTINGS = 74, + TAG_DEFINEFONT3 = 75, + TAG_SYMBOLCLASS = 76, + TAG_METADATA = 77, + TAG_DEFINESCALINGGRID = 78, + TAG_DOABC = 82, + TAG_DEFINESHAPE4 = 83, + TAG_DEFINEMORPHSHAPE2 = 84, + TAG_DEFINESCENEANDFRAMELABELDATA = 86, + TAG_DEFINEBINARYDATA = 87, + TAG_DEFINEFONTNAME = 88, + TAG_STARTSOUND2 = 89, + TAG_DEFINEBITSJPEG4 = 90, + TAG_DEFINEFONT4 = 91, +}; #define TAG_LONG 0x100 @@ -61,9 +120,6 @@ #define VIDEO_ID 0 #define SHAPE_ID 1 -#undef NDEBUG -#include <assert.h> - typedef struct SWFContext { int64_t duration_pos; int64_t tag_pos; @@ -76,6 +132,13 @@ typedef struct SWFContext { int tag; AVFifoBuffer *audio_fifo; AVCodecContext *audio_enc, *video_enc; +#if CONFIG_ZLIB + AVIOContext *zpb; +#define ZBUF_SIZE 4096 + uint8_t *zbuf_in; + uint8_t *zbuf_out; + z_stream zstream; +#endif } SWFContext; extern const AVCodecTag ff_swf_codec_tags[]; diff --git a/libavformat/swfdec.c b/libavformat/swfdec.c index d7a53145c6..54e0f6dc0e 100644 --- a/libavformat/swfdec.c +++ b/libavformat/swfdec.c @@ -3,24 +3,26 @@ * Copyright (c) 2000 Fabrice Bellard * Copyright (c) 2003 Tinic Uro * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "libavutil/avassert.h" #include "libavutil/channel_layout.h" +#include "libavutil/imgutils.h" #include "libavutil/intreadwrite.h" #include "swf.h" @@ -37,8 +39,8 @@ static int get_swf_tag(AVIOContext *pb, int *len_ptr) { int tag, len; - if (pb->eof_reached) - return -1; + if (url_feof(pb)) + return AVERROR_EOF; tag = avio_rl16(pb); len = tag & 0x3f; @@ -61,6 +63,39 @@ static int swf_probe(AVProbeData *p) return 0; } +#if CONFIG_ZLIB +static int zlib_refill(void *opaque, uint8_t *buf, int buf_size) +{ + AVFormatContext *s = opaque; + SWFContext *swf = s->priv_data; + z_stream *z = &swf->zstream; + int ret; + +retry: + if (!z->avail_in) { + int n = avio_read(s->pb, swf->zbuf_in, ZBUF_SIZE); + if (n < 0) + return n; + z->next_in = swf->zbuf_in; + z->avail_in = n; + } + + z->next_out = buf; + z->avail_out = buf_size; + + ret = inflate(z, Z_NO_FLUSH); + if (ret < 0) + return AVERROR(EINVAL); + if (ret == Z_STREAM_END) + return AVERROR_EOF; + + if (buf_size - z->avail_out == 0) + goto retry; + + return buf_size - z->avail_out; +} +#endif + static int swf_read_header(AVFormatContext *s) { SWFContext *swf = s->priv_data; @@ -68,14 +103,29 @@ static int swf_read_header(AVFormatContext *s) int nbits, len, tag; tag = avio_rb32(pb) & 0xffffff00; + avio_rl32(pb); if (tag == MKBETAG('C', 'W', 'S', 0)) { - av_log(s, AV_LOG_ERROR, "Compressed SWF format not supported\n"); + av_log(s, AV_LOG_INFO, "SWF compressed file detected\n"); +#if CONFIG_ZLIB + swf->zbuf_in = av_malloc(ZBUF_SIZE); + swf->zbuf_out = av_malloc(ZBUF_SIZE); + swf->zpb = avio_alloc_context(swf->zbuf_out, ZBUF_SIZE, 0, s, + zlib_refill, NULL, NULL); + if (!swf->zbuf_in || !swf->zbuf_out || !swf->zpb) + return AVERROR(ENOMEM); + swf->zpb->seekable = 0; + if (inflateInit(&swf->zstream) != Z_OK) { + av_log(s, AV_LOG_ERROR, "Unable to init zlib context\n"); + return AVERROR(EINVAL); + } + pb = swf->zpb; +#else + av_log(s, AV_LOG_ERROR, "zlib support is required to read SWF compressed files\n"); return AVERROR(EIO); - } - if (tag != MKBETAG('F', 'W', 'S', 0)) +#endif + } else if (tag != MKBETAG('F', 'W', 'S', 0)) return AVERROR(EIO); - avio_rl32(pb); /* skip rectangle size */ nbits = avio_r8(pb) >> 3; len = (4 * nbits - 3 + 7) / 8; @@ -88,6 +138,32 @@ static int swf_read_header(AVFormatContext *s) return 0; } +static AVStream *create_new_audio_stream(AVFormatContext *s, int id, int info) +{ + int sample_rate_code, sample_size_code; + AVStream *ast = avformat_new_stream(s, NULL); + if (!ast) + return NULL; + ast->id = id; + if (info & 1) { + ast->codec->channels = 2; + ast->codec->channel_layout = AV_CH_LAYOUT_STEREO; + } else { + ast->codec->channels = 1; + ast->codec->channel_layout = AV_CH_LAYOUT_MONO; + } + ast->codec->codec_type = AVMEDIA_TYPE_AUDIO; + ast->codec->codec_id = ff_codec_get_id(swf_audio_codec_tags, info>>4 & 15); + ast->need_parsing = AVSTREAM_PARSE_FULL; + sample_rate_code = info>>2 & 3; + sample_size_code = info>>1 & 1; + if (!sample_size_code && ast->codec->codec_id == AV_CODEC_ID_PCM_S16LE) + ast->codec->codec_id = AV_CODEC_ID_PCM_U8; + ast->codec->sample_rate = 44100 >> (3 - sample_rate_code); + avpriv_set_pts_info(ast, 64, 1, ast->codec->sample_rate); + return ast; +} + static int swf_read_packet(AVFormatContext *s, AVPacket *pkt) { SWFContext *swf = s->priv_data; @@ -95,11 +171,16 @@ static int swf_read_packet(AVFormatContext *s, AVPacket *pkt) AVStream *vst = NULL, *ast = NULL, *st = 0; int tag, len, i, frame, v, res; +#if CONFIG_ZLIB + if (swf->zpb) + pb = swf->zpb; +#endif + for(;;) { uint64_t pos = avio_tell(pb); tag = get_swf_tag(pb, &len); if (tag < 0) - return AVERROR(EIO); + return tag; if (len < 0) { av_log(s, AV_LOG_ERROR, "invalid tag length: %d\n", len); return AVERROR_INVALIDDATA; @@ -121,7 +202,7 @@ static int swf_read_packet(AVFormatContext *s, AVPacket *pkt) /* Check for FLV1 */ vst = avformat_new_stream(s, NULL); if (!vst) - return -1; + return AVERROR(ENOMEM); vst->id = ch_id; vst->codec->codec_type = AVMEDIA_TYPE_VIDEO; vst->codec->codec_id = ff_codec_get_id(ff_swf_codec_tags, avio_r8(pb)); @@ -129,7 +210,6 @@ static int swf_read_packet(AVFormatContext *s, AVPacket *pkt) len -= 8; } else if (tag == TAG_STREAMHEAD || tag == TAG_STREAMHEAD2) { /* streaming found */ - int sample_rate_code; for (i=0; i<s->nb_streams; i++) { st = s->streams[i]; @@ -140,24 +220,38 @@ static int swf_read_packet(AVFormatContext *s, AVPacket *pkt) avio_r8(pb); v = avio_r8(pb); swf->samples_per_frame = avio_rl16(pb); - ast = avformat_new_stream(s, NULL); + ast = create_new_audio_stream(s, -1, v); /* -1 to avoid clash with video stream ch_id */ if (!ast) - return -1; - ast->id = -1; /* -1 to avoid clash with video stream ch_id */ - if (v & 1) { - ast->codec->channels = 2; - ast->codec->channel_layout = AV_CH_LAYOUT_STEREO; - } else { - ast->codec->channels = 1; - ast->codec->channel_layout = AV_CH_LAYOUT_MONO; - } - ast->codec->codec_type = AVMEDIA_TYPE_AUDIO; - ast->codec->codec_id = ff_codec_get_id(swf_audio_codec_tags, (v>>4) & 15); - ast->need_parsing = AVSTREAM_PARSE_FULL; - sample_rate_code= (v>>2) & 3; - ast->codec->sample_rate = 44100 >> (3 - sample_rate_code); - avpriv_set_pts_info(ast, 64, 1, ast->codec->sample_rate); + return AVERROR(ENOMEM); len -= 4; + } else if (tag == TAG_DEFINESOUND) { + /* audio stream */ + int ch_id = avio_rl16(pb); + + for (i=0; i<s->nb_streams; i++) { + st = s->streams[i]; + if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO && st->id == ch_id) + goto skip; + } + + // FIXME: The entire audio stream is stored in a single chunk/tag. Normally, + // these are smaller audio streams in DEFINESOUND tags, but it's technically + // possible they could be huge. Break it up into multiple packets if it's big. + v = avio_r8(pb); + ast = create_new_audio_stream(s, ch_id, v); + if (!ast) + return AVERROR(ENOMEM); + ast->duration = avio_rl32(pb); // number of samples + if (((v>>4) & 15) == 2) { // MP3 sound data record + ast->skip_samples = avio_rl16(pb); + len -= 2; + } + len -= 7; + if ((res = av_get_packet(pb, pkt, len)) < 0) + return res; + pkt->pos = pos; + pkt->stream_index = ast->index; + return pkt->size; } else if (tag == TAG_VIDEOFRAME) { int ch_id = avio_rl16(pb); len -= 2; @@ -176,6 +270,132 @@ static int swf_read_packet(AVFormatContext *s, AVPacket *pkt) return pkt->size; } } + } else if (tag == TAG_DEFINEBITSLOSSLESS || tag == TAG_DEFINEBITSLOSSLESS2) { +#if CONFIG_ZLIB + long out_len; + uint8_t *buf = NULL, *zbuf = NULL, *pal; + uint32_t colormap[AVPALETTE_COUNT] = {0}; + const int alpha_bmp = tag == TAG_DEFINEBITSLOSSLESS2; + const int colormapbpp = 3 + alpha_bmp; + int linesize, colormapsize = 0; + + const int ch_id = avio_rl16(pb); + const int bmp_fmt = avio_r8(pb); + const int width = avio_rl16(pb); + const int height = avio_rl16(pb); + + len -= 2+1+2+2; + + switch (bmp_fmt) { + case 3: // PAL-8 + linesize = width; + colormapsize = avio_r8(pb) + 1; + len--; + break; + case 4: // RGB15 + linesize = width * 2; + break; + case 5: // RGB24 (0RGB) + linesize = width * 4; + break; + default: + av_log(s, AV_LOG_ERROR, "invalid bitmap format %d, skipped\n", bmp_fmt); + goto bitmap_end_skip; + } + + linesize = FFALIGN(linesize, 4); + + if (av_image_check_size(width, height, 0, s) < 0 || + linesize >= INT_MAX / height || + linesize * height >= INT_MAX - colormapsize * colormapbpp) { + av_log(s, AV_LOG_ERROR, "invalid frame size %dx%d\n", width, height); + goto bitmap_end_skip; + } + + out_len = colormapsize * colormapbpp + linesize * height; + + av_dlog(s, "bitmap: ch=%d fmt=%d %dx%d (linesize=%d) len=%d->%ld pal=%d\n", + ch_id, bmp_fmt, width, height, linesize, len, out_len, colormapsize); + + zbuf = av_malloc(len); + buf = av_malloc(out_len); + if (!zbuf || !buf) { + res = AVERROR(ENOMEM); + goto bitmap_end; + } + + len = avio_read(pb, zbuf, len); + if (len < 0 || (res = uncompress(buf, &out_len, zbuf, len)) != Z_OK) { + av_log(s, AV_LOG_WARNING, "Failed to uncompress one bitmap\n"); + goto bitmap_end_skip; + } + + for (i = 0; i < s->nb_streams; i++) { + st = s->streams[i]; + if (st->codec->codec_id == AV_CODEC_ID_RAWVIDEO && st->id == -3) + break; + } + if (i == s->nb_streams) { + vst = avformat_new_stream(s, NULL); + if (!vst) { + res = AVERROR(ENOMEM); + goto bitmap_end; + } + vst->id = -3; /* -3 to avoid clash with video stream and audio stream */ + vst->codec->codec_type = AVMEDIA_TYPE_VIDEO; + vst->codec->codec_id = AV_CODEC_ID_RAWVIDEO; + avpriv_set_pts_info(vst, 64, 256, swf->frame_rate); + st = vst; + } + st->codec->width = width; + st->codec->height = height; + + if ((res = av_new_packet(pkt, out_len - colormapsize * colormapbpp)) < 0) + goto bitmap_end; + pkt->pos = pos; + pkt->stream_index = st->index; + + switch (bmp_fmt) { + case 3: + st->codec->pix_fmt = AV_PIX_FMT_PAL8; + for (i = 0; i < colormapsize; i++) + if (alpha_bmp) colormap[i] = buf[3]<<24 | AV_RB24(buf + 4*i); + else colormap[i] = 0xffU <<24 | AV_RB24(buf + 3*i); + pal = av_packet_new_side_data(pkt, AV_PKT_DATA_PALETTE, AVPALETTE_SIZE); + if (!pal) { + res = AVERROR(ENOMEM); + goto bitmap_end; + } + memcpy(pal, colormap, AVPALETTE_SIZE); + break; + case 4: + st->codec->pix_fmt = AV_PIX_FMT_RGB555; + break; + case 5: + st->codec->pix_fmt = alpha_bmp ? AV_PIX_FMT_ARGB : AV_PIX_FMT_0RGB; + break; + default: + av_assert0(0); + } + + if (linesize * height > pkt->size) { + res = AVERROR_INVALIDDATA; + goto bitmap_end; + } + memcpy(pkt->data, buf + colormapsize*colormapbpp, linesize * height); + + res = pkt->size; + +bitmap_end: + av_freep(&zbuf); + av_freep(&buf); + return res; +bitmap_end_skip: + av_freep(&zbuf); + av_freep(&buf); +#else + av_log(s, AV_LOG_ERROR, "this file requires zlib support compiled in\n"); +#endif } else if (tag == TAG_STREAMBLOCK) { for (i = 0; i < s->nb_streams; i++) { st = s->streams[i]; @@ -207,7 +427,7 @@ static int swf_read_packet(AVFormatContext *s, AVPacket *pkt) if (i == s->nb_streams) { vst = avformat_new_stream(s, NULL); if (!vst) - return -1; + return AVERROR(ENOMEM); vst->id = -2; /* -2 to avoid clash with video stream and audio stream */ vst->codec->codec_type = AVMEDIA_TYPE_VIDEO; vst->codec->codec_id = AV_CODEC_ID_MJPEG; @@ -233,13 +453,29 @@ static int swf_read_packet(AVFormatContext *s, AVPacket *pkt) pkt->pos = pos; pkt->stream_index = st->index; return pkt->size; + } else { + av_log(s, AV_LOG_DEBUG, "Unknown tag: %d\n", tag); } skip: + if(len<0) + av_log(s, AV_LOG_WARNING, "Cliping len %d\n", len); len = FFMAX(0, len); avio_skip(pb, len); } } +#if CONFIG_ZLIB +static av_cold int swf_read_close(AVFormatContext *avctx) +{ + SWFContext *s = avctx->priv_data; + inflateEnd(&s->zstream); + av_freep(&s->zbuf_in); + av_freep(&s->zbuf_out); + av_freep(&s->zpb); + return 0; +} +#endif + AVInputFormat ff_swf_demuxer = { .name = "swf", .long_name = NULL_IF_CONFIG_SMALL("SWF (ShockWave Flash)"), @@ -247,4 +483,7 @@ AVInputFormat ff_swf_demuxer = { .read_probe = swf_probe, .read_header = swf_read_header, .read_packet = swf_read_packet, +#if CONFIG_ZLIB + .read_close = swf_read_close, +#endif }; diff --git a/libavformat/swfenc.c b/libavformat/swfenc.c index be2e5cd7d3..8d9cf0c246 100644 --- a/libavformat/swfenc.c +++ b/libavformat/swfenc.c @@ -3,24 +3,25 @@ * Copyright (c) 2000 Fabrice Bellard * Copyright (c) 2003 Tinic Uro * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "libavcodec/put_bits.h" +#include "libavutil/avassert.h" #include "avformat.h" #include "swf.h" @@ -56,7 +57,7 @@ static void put_swf_end_tag(AVFormatContext *s) avio_wl16(pb, (tag << 6) | 0x3f); avio_wl32(pb, tag_len - 4); } else { - assert(tag_len < 0x3f); + av_assert0(tag_len < 0x3f); avio_wl16(pb, (tag << 6) | tag_len); } avio_seek(pb, pos, SEEK_SET); @@ -191,6 +192,10 @@ static int swf_write_header(AVFormatContext *s) return AVERROR_INVALIDDATA; } if (enc->codec_id == AV_CODEC_ID_MP3) { + if (!enc->frame_size) { + av_log(s, AV_LOG_ERROR, "audio frame size not set\n"); + return -1; + } swf->audio_enc = enc; swf->audio_fifo= av_fifo_alloc(AUDIO_FIFO_SIZE); if (!swf->audio_fifo) @@ -425,7 +430,7 @@ static int swf_write_video(AVFormatContext *s, put_swf_tag(s, TAG_STREAMBLOCK | TAG_LONG); avio_wl16(pb, swf->sound_samples); avio_wl16(pb, 0); // seek samples - av_fifo_generic_read(swf->audio_fifo, pb, frame_size, &avio_write); + av_fifo_generic_read(swf->audio_fifo, pb, frame_size, (void*)avio_write); put_swf_end_tag(s); /* update FIFO */ @@ -454,7 +459,7 @@ static int swf_write_audio(AVFormatContext *s, } av_fifo_generic_write(swf->audio_fifo, buf, size, NULL); - swf->sound_samples += av_get_audio_frame_duration(enc, size); + swf->sound_samples += enc->frame_size; /* if audio only stream make sure we add swf frames */ if (!swf->video_enc) @@ -484,8 +489,10 @@ static int swf_write_trailer(AVFormatContext *s) enc = s->streams[i]->codec; if (enc->codec_type == AVMEDIA_TYPE_VIDEO) video_enc = enc; - else + else { av_fifo_free(swf->audio_fifo); + swf->audio_fifo = NULL; + } } put_swf_tag(s, TAG_END); @@ -498,8 +505,10 @@ static int swf_write_trailer(AVFormatContext *s) avio_wl32(pb, file_size); avio_seek(pb, swf->duration_pos, SEEK_SET); avio_wl16(pb, swf->video_frame_number); + if (swf->vframes_pos) { avio_seek(pb, swf->vframes_pos, SEEK_SET); avio_wl16(pb, swf->video_frame_number); + } avio_seek(pb, file_size, SEEK_SET); } return 0; diff --git a/libavformat/takdec.c b/libavformat/takdec.c index 584cbccb50..2ed8a1e3b7 100644 --- a/libavformat/takdec.c +++ b/libavformat/takdec.c @@ -2,25 +2,27 @@ * Raw TAK demuxer * Copyright (c) 2012 Paul B Mahol * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "libavutil/crc.h" #include "libavcodec/tak.h" #include "avformat.h" +#include "avio_internal.h" #include "internal.h" #include "rawdec.h" #include "apetag.h" @@ -37,6 +39,12 @@ static int tak_probe(AVProbeData *p) return 0; } +static unsigned long tak_check_crc(unsigned long checksum, const uint8_t *buf, + unsigned int len) +{ + return av_crc(av_crc_get_table(AV_CRC_24_IEEE), checksum, buf, len); +} + static int tak_read_header(AVFormatContext *s) { TAKDemuxContext *tc = s->priv_data; @@ -52,7 +60,7 @@ static int tak_read_header(AVFormatContext *s) st->codec->codec_type = AVMEDIA_TYPE_AUDIO; st->codec->codec_id = AV_CODEC_ID_TAK; - st->need_parsing = AVSTREAM_PARSE_FULL; + st->need_parsing = AVSTREAM_PARSE_FULL_RAW; tc->mlast_frame = 0; if (avio_rl32(pb) != MKTAG('t', 'B', 'a', 'K')) { @@ -60,7 +68,7 @@ static int tak_read_header(AVFormatContext *s) return 0; } - while (!pb->eof_reached) { + while (!url_feof(pb)) { enum TAKMetaDataType type; int size; @@ -71,16 +79,27 @@ static int tak_read_header(AVFormatContext *s) case TAK_METADATA_STREAMINFO: case TAK_METADATA_LAST_FRAME: case TAK_METADATA_ENCODER: - buffer = av_malloc(size + FF_INPUT_BUFFER_PADDING_SIZE); + if (size <= 3) + return AVERROR_INVALIDDATA; + + buffer = av_malloc(size - 3 + FF_INPUT_BUFFER_PADDING_SIZE); if (!buffer) return AVERROR(ENOMEM); - if (avio_read(pb, buffer, size) != size) { + ffio_init_checksum(pb, tak_check_crc, 0xCE04B7U); + if (avio_read(pb, buffer, size - 3) != size - 3) { av_freep(&buffer); return AVERROR(EIO); } + if (ffio_get_checksum(s->pb) != avio_rb24(pb)) { + av_log(s, AV_LOG_ERROR, "%d metadata block CRC error.\n", type); + if (s->error_recognition & AV_EF_EXPLODE) { + av_freep(&buffer); + return AVERROR_INVALIDDATA; + } + } - init_get_bits(&gb, buffer, size * 8); + init_get_bits8(&gb, buffer, size - 3); break; case TAK_METADATA_MD5: { uint8_t md5[16]; @@ -88,8 +107,14 @@ static int tak_read_header(AVFormatContext *s) if (size != 19) return AVERROR_INVALIDDATA; + ffio_init_checksum(pb, tak_check_crc, 0xCE04B7U); avio_read(pb, md5, 16); - avio_skip(pb, 3); + if (ffio_get_checksum(s->pb) != avio_rb24(pb)) { + av_log(s, AV_LOG_ERROR, "MD5 metadata block CRC error.\n"); + if (s->error_recognition & AV_EF_EXPLODE) + return AVERROR_INVALIDDATA; + } + av_log(s, AV_LOG_VERBOSE, "MD5="); for (i = 0; i < 16; i++) av_log(s, AV_LOG_VERBOSE, "%02x", md5[i]); @@ -127,7 +152,7 @@ static int tak_read_header(AVFormatContext *s) st->start_time = 0; avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate); st->codec->extradata = buffer; - st->codec->extradata_size = size; + st->codec->extradata_size = size - 3; buffer = NULL; } else if (type == TAK_METADATA_LAST_FRAME) { if (size != 11) @@ -155,7 +180,7 @@ static int raw_read_packet(AVFormatContext *s, AVPacket *pkt) AVIOContext *pb = s->pb; int64_t size, left; - left = tc->data_end - avio_tell(s->pb); + left = tc->data_end - avio_tell(pb); size = FFMIN(left, 1024); if (size <= 0) return AVERROR_EOF; diff --git a/libavformat/tcp.c b/libavformat/tcp.c index cbd51422a8..634d99d0d0 100644 --- a/libavformat/tcp.c +++ b/libavformat/tcp.c @@ -2,24 +2,26 @@ * TCP protocol * Copyright (c) 2002 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "avformat.h" #include "libavutil/parseutils.h" +#include "libavutil/opt.h" +#include "libavutil/time.h" #include "internal.h" #include "network.h" #include "os_support.h" @@ -29,22 +31,43 @@ #endif typedef struct TCPContext { + const AVClass *class; int fd; + int listen; + int open_timeout; + int rw_timeout; + int listen_timeout; } TCPContext; +#define OFFSET(x) offsetof(TCPContext, x) +#define D AV_OPT_FLAG_DECODING_PARAM +#define E AV_OPT_FLAG_ENCODING_PARAM +static const AVOption options[] = { +{"listen", "listen on port instead of connecting", OFFSET(listen), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, D|E }, +{"timeout", "timeout of socket i/o operations", OFFSET(rw_timeout), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, D|E }, +{"listen_timeout", "connection awaiting timeout", OFFSET(listen_timeout), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, D|E }, +{NULL} +}; + +static const AVClass tcp_context_class = { + .class_name = "tcp", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + /* return non zero if error */ static int tcp_open(URLContext *h, const char *uri, int flags) { struct addrinfo hints = { 0 }, *ai, *cur_ai; int port, fd = -1; TCPContext *s = h->priv_data; - int listen_socket = 0; const char *p; char buf[256]; int ret; - int timeout = 100, listen_timeout = -1; char hostname[1024],proto[1024],path[1024]; char portstr[10]; + s->open_timeout = 5000000; av_url_split(proto, sizeof(proto), NULL, 0, hostname, sizeof(hostname), &port, path, sizeof(path), uri); @@ -57,18 +80,22 @@ static int tcp_open(URLContext *h, const char *uri, int flags) p = strchr(uri, '?'); if (p) { if (av_find_info_tag(buf, sizeof(buf), "listen", p)) - listen_socket = 1; + s->listen = 1; if (av_find_info_tag(buf, sizeof(buf), "timeout", p)) { - timeout = strtol(buf, NULL, 10); + s->rw_timeout = strtol(buf, NULL, 10); } if (av_find_info_tag(buf, sizeof(buf), "listen_timeout", p)) { - listen_timeout = strtol(buf, NULL, 10); + s->listen_timeout = strtol(buf, NULL, 10); } } + if (s->rw_timeout >= 0) { + s->open_timeout = + h->rw_timeout = s->rw_timeout; + } hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; snprintf(portstr, sizeof(portstr), "%d", port); - if (listen_socket) + if (s->listen) hints.ai_flags |= AI_PASSIVE; if (!hostname[0]) ret = getaddrinfo(NULL, portstr, &hints, &ai); @@ -92,15 +119,15 @@ static int tcp_open(URLContext *h, const char *uri, int flags) goto fail; } - if (listen_socket) { + if (s->listen) { if ((fd = ff_listen_bind(fd, cur_ai->ai_addr, cur_ai->ai_addrlen, - listen_timeout, h)) < 0) { + s->listen_timeout, h)) < 0) { ret = fd; goto fail1; } } else { if ((ret = ff_listen_connect(fd, cur_ai->ai_addr, cur_ai->ai_addrlen, - timeout * 100, h, !!cur_ai->ai_next)) < 0) { + s->open_timeout / 1000, h, !!cur_ai->ai_next)) < 0) { if (ret == AVERROR_EXIT) goto fail1; @@ -136,8 +163,8 @@ static int tcp_read(URLContext *h, uint8_t *buf, int size) int ret; if (!(h->flags & AVIO_FLAG_NONBLOCK)) { - ret = ff_network_wait_fd(s->fd, 0); - if (ret < 0) + ret = ff_network_wait_fd_timeout(s->fd, 0, h->rw_timeout, &h->interrupt_callback); + if (ret) return ret; } ret = recv(s->fd, buf, size, 0); @@ -150,8 +177,8 @@ static int tcp_write(URLContext *h, const uint8_t *buf, int size) int ret; if (!(h->flags & AVIO_FLAG_NONBLOCK)) { - ret = ff_network_wait_fd(s->fd, 1); - if (ret < 0) + ret = ff_network_wait_fd_timeout(s->fd, 1, h->rw_timeout, &h->interrupt_callback); + if (ret) return ret; } ret = send(s->fd, buf, size, 0); @@ -196,5 +223,6 @@ URLProtocol ff_tcp_protocol = { .url_get_file_handle = tcp_get_file_handle, .url_shutdown = tcp_shutdown, .priv_data_size = sizeof(TCPContext), + .priv_data_class = &tcp_context_class, .flags = URL_PROTOCOL_FLAG_NETWORK, }; diff --git a/libavformat/tedcaptionsdec.c b/libavformat/tedcaptionsdec.c new file mode 100644 index 0000000000..68063fafbe --- /dev/null +++ b/libavformat/tedcaptionsdec.c @@ -0,0 +1,366 @@ +/* + * TED Talks captions format decoder + * Copyright (c) 2012 Nicolas George + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/bprint.h" +#include "libavutil/log.h" +#include "libavutil/opt.h" +#include "avformat.h" +#include "internal.h" +#include "subtitles.h" + +typedef struct { + AVClass *class; + int64_t start_time; + FFDemuxSubtitlesQueue subs; +} TEDCaptionsDemuxer; + +static const AVOption tedcaptions_options[] = { + { "start_time", "set the start time (offset) of the subtitles, in ms", + offsetof(TEDCaptionsDemuxer, start_time), FF_OPT_TYPE_INT64, + { .i64 = 15000 }, INT64_MIN, INT64_MAX, + AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_DECODING_PARAM }, + { NULL }, +}; + +static const AVClass tedcaptions_demuxer_class = { + .class_name = "tedcaptions_demuxer", + .item_name = av_default_item_name, + .option = tedcaptions_options, + .version = LIBAVUTIL_VERSION_INT, +}; + +#define BETWEEN(a, amin, amax) ((unsigned)((a) - (amin)) <= (amax) - (amin)) + +#define HEX_DIGIT_TEST(c) (BETWEEN(c, '0', '9') || BETWEEN((c) | 32, 'a', 'z')) +#define HEX_DIGIT_VAL(c) ((c) <= '9' ? (c) - '0' : ((c) | 32) - 'a' + 10) +#define ERR_CODE(c) (c < 0 ? c : AVERROR_INVALIDDATA) + +static void av_bprint_utf8(AVBPrint *bp, unsigned c) +{ + int bytes, i; + + if (c <= 0x7F) { + av_bprint_chars(bp, c, 1); + return; + } + bytes = (av_log2(c) - 2) / 5; + av_bprint_chars(bp, (c >> (bytes * 6)) | ((0xFF80 >> bytes) & 0xFF), 1); + for (i = bytes - 1; i >= 0; i--) + av_bprint_chars(bp, ((c >> (i * 6)) & 0x3F) | 0x80, 1); +} + +static void next_byte(AVIOContext *pb, int *cur_byte) +{ + uint8_t b; + int ret = avio_read(pb, &b, 1); + *cur_byte = ret > 0 ? b : ret == 0 ? AVERROR_EOF : ret; +} + +static void skip_spaces(AVIOContext *pb, int *cur_byte) +{ + while (*cur_byte == ' ' || *cur_byte == '\t' || + *cur_byte == '\n' || *cur_byte == '\r') + next_byte(pb, cur_byte); +} + +static int expect_byte(AVIOContext *pb, int *cur_byte, uint8_t c) +{ + skip_spaces(pb, cur_byte); + if (*cur_byte != c) + return ERR_CODE(*cur_byte); + next_byte(pb, cur_byte); + return 0; +} + +static int parse_string(AVIOContext *pb, int *cur_byte, AVBPrint *bp, int full) +{ + int ret; + + av_bprint_init(bp, 0, full ? -1 : 1); + ret = expect_byte(pb, cur_byte, '"'); + if (ret < 0) + goto fail; + while (*cur_byte > 0 && *cur_byte != '"') { + if (*cur_byte == '\\') { + next_byte(pb, cur_byte); + if (*cur_byte < 0) { + ret = AVERROR_INVALIDDATA; + goto fail; + } + if ((*cur_byte | 32) == 'u') { + unsigned chr = 0, i; + for (i = 0; i < 4; i++) { + next_byte(pb, cur_byte); + if (!HEX_DIGIT_TEST(*cur_byte)) { + ret = ERR_CODE(*cur_byte); + goto fail; + } + chr = chr * 16 + HEX_DIGIT_VAL(*cur_byte); + } + av_bprint_utf8(bp, chr); + } else { + av_bprint_chars(bp, *cur_byte, 1); + } + } else { + av_bprint_chars(bp, *cur_byte, 1); + } + next_byte(pb, cur_byte); + } + ret = expect_byte(pb, cur_byte, '"'); + if (ret < 0) + goto fail; + if (full && !av_bprint_is_complete(bp)) { + ret = AVERROR(ENOMEM); + goto fail; + } + return 0; + +fail: + av_bprint_finalize(bp, NULL); + return ret; +} + +static int parse_label(AVIOContext *pb, int *cur_byte, AVBPrint *bp) +{ + int ret; + + ret = parse_string(pb, cur_byte, bp, 0); + if (ret < 0) + return ret; + ret = expect_byte(pb, cur_byte, ':'); + if (ret < 0) + return ret; + return 0; +} + +static int parse_boolean(AVIOContext *pb, int *cur_byte, int *result) +{ + static const char * const text[] = { "false", "true" }; + const char *p; + int i; + + skip_spaces(pb, cur_byte); + for (i = 0; i < 2; i++) { + p = text[i]; + if (*cur_byte != *p) + continue; + for (; *p; p++, next_byte(pb, cur_byte)) + if (*cur_byte != *p) + return AVERROR_INVALIDDATA; + if (BETWEEN(*cur_byte | 32, 'a', 'z')) + return AVERROR_INVALIDDATA; + *result = i; + return 0; + } + return AVERROR_INVALIDDATA; +} + +static int parse_int(AVIOContext *pb, int *cur_byte, int64_t *result) +{ + int64_t val = 0; + + skip_spaces(pb, cur_byte); + if ((unsigned)*cur_byte - '0' > 9) + return AVERROR_INVALIDDATA; + while (BETWEEN(*cur_byte, '0', '9')) { + val = val * 10 + (*cur_byte - '0'); + next_byte(pb, cur_byte); + } + *result = val; + return 0; +} + +static int parse_file(AVIOContext *pb, FFDemuxSubtitlesQueue *subs) +{ + int ret, cur_byte, start_of_par; + AVBPrint label, content; + int64_t pos, start, duration; + AVPacket *pkt; + + next_byte(pb, &cur_byte); + ret = expect_byte(pb, &cur_byte, '{'); + if (ret < 0) + return AVERROR_INVALIDDATA; + ret = parse_label(pb, &cur_byte, &label); + if (ret < 0 || strcmp(label.str, "captions")) + return AVERROR_INVALIDDATA; + ret = expect_byte(pb, &cur_byte, '['); + if (ret < 0) + return AVERROR_INVALIDDATA; + while (1) { + content.size = 0; + start = duration = AV_NOPTS_VALUE; + ret = expect_byte(pb, &cur_byte, '{'); + if (ret < 0) + return ret; + pos = avio_tell(pb) - 1; + while (1) { + ret = parse_label(pb, &cur_byte, &label); + if (ret < 0) + return ret; + if (!strcmp(label.str, "startOfParagraph")) { + ret = parse_boolean(pb, &cur_byte, &start_of_par); + if (ret < 0) + return ret; + } else if (!strcmp(label.str, "content")) { + ret = parse_string(pb, &cur_byte, &content, 1); + if (ret < 0) + return ret; + } else if (!strcmp(label.str, "startTime")) { + ret = parse_int(pb, &cur_byte, &start); + if (ret < 0) + return ret; + } else if (!strcmp(label.str, "duration")) { + ret = parse_int(pb, &cur_byte, &duration); + if (ret < 0) + return ret; + } else { + return AVERROR_INVALIDDATA; + } + skip_spaces(pb, &cur_byte); + if (cur_byte != ',') + break; + next_byte(pb, &cur_byte); + } + ret = expect_byte(pb, &cur_byte, '}'); + if (ret < 0) + return ret; + + if (!content.size || start == AV_NOPTS_VALUE || + duration == AV_NOPTS_VALUE) + return AVERROR_INVALIDDATA; + pkt = ff_subtitles_queue_insert(subs, content.str, content.len, 0); + if (!pkt) + return AVERROR(ENOMEM); + pkt->pos = pos; + pkt->pts = start; + pkt->duration = duration; + av_bprint_finalize(&content, NULL); + + skip_spaces(pb, &cur_byte); + if (cur_byte != ',') + break; + next_byte(pb, &cur_byte); + } + ret = expect_byte(pb, &cur_byte, ']'); + if (ret < 0) + return ret; + ret = expect_byte(pb, &cur_byte, '}'); + if (ret < 0) + return ret; + skip_spaces(pb, &cur_byte); + if (cur_byte != AVERROR_EOF) + return ERR_CODE(cur_byte); + return 0; +} + +static av_cold int tedcaptions_read_header(AVFormatContext *avf) +{ + TEDCaptionsDemuxer *tc = avf->priv_data; + AVStream *st; + int ret, i; + AVPacket *last; + + ret = parse_file(avf->pb, &tc->subs); + if (ret < 0) { + if (ret == AVERROR_INVALIDDATA) + av_log(avf, AV_LOG_ERROR, "Syntax error near offset %"PRId64".\n", + avio_tell(avf->pb)); + ff_subtitles_queue_clean(&tc->subs); + return ret; + } + ff_subtitles_queue_finalize(&tc->subs); + for (i = 0; i < tc->subs.nb_subs; i++) + tc->subs.subs[i].pts += tc->start_time; + + last = &tc->subs.subs[tc->subs.nb_subs - 1]; + st = avformat_new_stream(avf, NULL); + if (!st) + return AVERROR(ENOMEM); + st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE; + st->codec->codec_id = AV_CODEC_ID_TEXT; + avpriv_set_pts_info(st, 64, 1, 1000); + st->probe_packets = 0; + st->start_time = 0; + st->duration = last->pts + last->duration; + st->cur_dts = 0; + + return 0; +} + +static int tedcaptions_read_packet(AVFormatContext *avf, AVPacket *packet) +{ + TEDCaptionsDemuxer *tc = avf->priv_data; + + return ff_subtitles_queue_read_packet(&tc->subs, packet); +} + +static int tedcaptions_read_close(AVFormatContext *avf) +{ + TEDCaptionsDemuxer *tc = avf->priv_data; + + ff_subtitles_queue_clean(&tc->subs); + return 0; +} + +static av_cold int tedcaptions_read_probe(AVProbeData *p) +{ + static const char *const tags[] = { + "\"captions\"", "\"duration\"", "\"content\"", + "\"startOfParagraph\"", "\"startTime\"", + }; + unsigned i, count = 0; + const char *t; + + if (p->buf[strspn(p->buf, " \t\r\n")] != '{') + return 0; + for (i = 0; i < FF_ARRAY_ELEMS(tags); i++) { + if (!(t = strstr(p->buf, tags[i]))) + continue; + t += strlen(tags[i]); + t += strspn(t, " \t\r\n"); + if (*t == ':') + count++; + } + return count == FF_ARRAY_ELEMS(tags) ? AVPROBE_SCORE_MAX : + count ? AVPROBE_SCORE_EXTENSION : 0; +} + +static int tedcaptions_read_seek(AVFormatContext *avf, int stream_index, + int64_t min_ts, int64_t ts, int64_t max_ts, + int flags) +{ + TEDCaptionsDemuxer *tc = avf->priv_data; + return ff_subtitles_queue_seek(&tc->subs, avf, stream_index, + min_ts, ts, max_ts, flags); +} + +AVInputFormat ff_tedcaptions_demuxer = { + .name = "tedcaptions", + .long_name = NULL_IF_CONFIG_SMALL("TED Talks captions"), + .priv_data_size = sizeof(TEDCaptionsDemuxer), + .priv_class = &tedcaptions_demuxer_class, + .read_header = tedcaptions_read_header, + .read_packet = tedcaptions_read_packet, + .read_close = tedcaptions_read_close, + .read_probe = tedcaptions_read_probe, + .read_seek2 = tedcaptions_read_seek, +}; diff --git a/libavformat/tee.c b/libavformat/tee.c new file mode 100644 index 0000000000..12ea0ea27d --- /dev/null +++ b/libavformat/tee.c @@ -0,0 +1,498 @@ +/* + * Tee pseudo-muxer + * Copyright (c) 2012 Nicolas George + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software * Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#include "libavutil/avutil.h" +#include "libavutil/avstring.h" +#include "libavutil/opt.h" +#include "avformat.h" + +#define MAX_SLAVES 16 + +typedef struct { + AVFormatContext *avf; + AVBitStreamFilterContext **bsfs; ///< bitstream filters per stream + + /** map from input to output streams indexes, + * disabled output streams are set to -1 */ + int *stream_map; +} TeeSlave; + +typedef struct TeeContext { + const AVClass *class; + unsigned nb_slaves; + TeeSlave slaves[MAX_SLAVES]; +} TeeContext; + +static const char *const slave_delim = "|"; +static const char *const slave_opt_open = "["; +static const char *const slave_opt_close = "]"; +static const char *const slave_opt_delim = ":]"; /* must have the close too */ +static const char *const slave_bsfs_spec_sep = "/"; + +static const AVClass tee_muxer_class = { + .class_name = "Tee muxer", + .item_name = av_default_item_name, + .version = LIBAVUTIL_VERSION_INT, +}; + +static int parse_slave_options(void *log, char *slave, + AVDictionary **options, char **filename) +{ + const char *p; + char *key, *val; + int ret; + + if (!strspn(slave, slave_opt_open)) { + *filename = slave; + return 0; + } + p = slave + 1; + if (strspn(p, slave_opt_close)) { + *filename = (char *)p + 1; + return 0; + } + while (1) { + ret = av_opt_get_key_value(&p, "=", slave_opt_delim, 0, &key, &val); + if (ret < 0) { + av_log(log, AV_LOG_ERROR, "No option found near \"%s\"\n", p); + goto fail; + } + ret = av_dict_set(options, key, val, + AV_DICT_DONT_STRDUP_KEY | AV_DICT_DONT_STRDUP_VAL); + if (ret < 0) + goto fail; + if (strspn(p, slave_opt_close)) + break; + p++; + } + *filename = (char *)p + 1; + return 0; + +fail: + av_dict_free(options); + return ret; +} + +/** + * Parse list of bitstream filters and add them to the list of filters + * pointed to by bsfs. + * + * The list must be specified in the form: + * BSFS ::= BSF[,BSFS] + */ +static int parse_bsfs(void *log_ctx, const char *bsfs_spec, + AVBitStreamFilterContext **bsfs) +{ + char *bsf_name, *buf, *dup, *saveptr; + int ret = 0; + + if (!(dup = buf = av_strdup(bsfs_spec))) + return AVERROR(ENOMEM); + + while (bsf_name = av_strtok(buf, ",", &saveptr)) { + AVBitStreamFilterContext *bsf = av_bitstream_filter_init(bsf_name); + + if (!bsf) { + av_log(log_ctx, AV_LOG_ERROR, + "Cannot initialize bitstream filter with name '%s', " + "unknown filter or internal error happened\n", + bsf_name); + ret = AVERROR_UNKNOWN; + goto end; + } + + /* append bsf context to the list of bsf contexts */ + *bsfs = bsf; + bsfs = &bsf->next; + + buf = NULL; + } + +end: + av_free(dup); + return ret; +} + +static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) +{ + int i, ret; + AVDictionary *options = NULL; + AVDictionaryEntry *entry; + char *filename; + char *format = NULL, *select = NULL; + AVFormatContext *avf2 = NULL; + AVStream *st, *st2; + int stream_count; + + if ((ret = parse_slave_options(avf, slave, &options, &filename)) < 0) + return ret; + +#define STEAL_OPTION(option, field) do { \ + if ((entry = av_dict_get(options, option, NULL, 0))) { \ + field = entry->value; \ + entry->value = NULL; /* prevent it from being freed */ \ + av_dict_set(&options, option, NULL, 0); \ + } \ + } while (0) + + STEAL_OPTION("f", format); + STEAL_OPTION("select", select); + + ret = avformat_alloc_output_context2(&avf2, NULL, format, filename); + if (ret < 0) + goto end; + av_dict_copy(&avf2->metadata, avf->metadata, 0); + + tee_slave->stream_map = av_calloc(avf->nb_streams, sizeof(*tee_slave->stream_map)); + if (!tee_slave->stream_map) { + ret = AVERROR(ENOMEM); + goto end; + } + + stream_count = 0; + for (i = 0; i < avf->nb_streams; i++) { + st = avf->streams[i]; + if (select) { + ret = avformat_match_stream_specifier(avf, avf->streams[i], select); + if (ret < 0) { + av_log(avf, AV_LOG_ERROR, + "Invalid stream specifier '%s' for output '%s'\n", + select, slave); + goto end; + } + + if (ret == 0) { /* no match */ + tee_slave->stream_map[i] = -1; + continue; + } + } + tee_slave->stream_map[i] = stream_count++; + + if (!(st2 = avformat_new_stream(avf2, NULL))) { + ret = AVERROR(ENOMEM); + goto end; + } + st2->id = st->id; + st2->r_frame_rate = st->r_frame_rate; + st2->time_base = st->time_base; + st2->start_time = st->start_time; + st2->duration = st->duration; + st2->nb_frames = st->nb_frames; + st2->disposition = st->disposition; + st2->sample_aspect_ratio = st->sample_aspect_ratio; + st2->avg_frame_rate = st->avg_frame_rate; + av_dict_copy(&st2->metadata, st->metadata, 0); + if ((ret = avcodec_copy_context(st2->codec, st->codec)) < 0) + goto end; + } + + if (!(avf2->oformat->flags & AVFMT_NOFILE)) { + if ((ret = avio_open(&avf2->pb, filename, AVIO_FLAG_WRITE)) < 0) { + av_log(avf, AV_LOG_ERROR, "Slave '%s': error opening: %s\n", + slave, av_err2str(ret)); + goto end; + } + } + + if ((ret = avformat_write_header(avf2, &options)) < 0) { + av_log(avf, AV_LOG_ERROR, "Slave '%s': error writing header: %s\n", + slave, av_err2str(ret)); + goto end; + } + + tee_slave->avf = avf2; + tee_slave->bsfs = av_calloc(avf2->nb_streams, sizeof(TeeSlave)); + if (!tee_slave->bsfs) { + ret = AVERROR(ENOMEM); + goto end; + } + + entry = NULL; + while (entry = av_dict_get(options, "bsfs", NULL, AV_DICT_IGNORE_SUFFIX)) { + const char *spec = entry->key + strlen("bsfs"); + if (*spec) { + if (strspn(spec, slave_bsfs_spec_sep) != 1) { + av_log(avf, AV_LOG_ERROR, + "Specifier separator in '%s' is '%c', but only characters '%s' " + "are allowed\n", entry->key, *spec, slave_bsfs_spec_sep); + return AVERROR(EINVAL); + } + spec++; /* consume separator */ + } + + for (i = 0; i < avf2->nb_streams; i++) { + ret = avformat_match_stream_specifier(avf2, avf2->streams[i], spec); + if (ret < 0) { + av_log(avf, AV_LOG_ERROR, + "Invalid stream specifier '%s' in bsfs option '%s' for slave " + "output '%s'\n", spec, entry->key, filename); + goto end; + } + + if (ret > 0) { + av_log(avf, AV_LOG_DEBUG, "spec:%s bsfs:%s matches stream %d of slave " + "output '%s'\n", spec, entry->value, i, filename); + if (tee_slave->bsfs[i]) { + av_log(avf, AV_LOG_WARNING, + "Duplicate bsfs specification associated to stream %d of slave " + "output '%s', filters will be ignored\n", i, filename); + continue; + } + ret = parse_bsfs(avf, entry->value, &tee_slave->bsfs[i]); + if (ret < 0) { + av_log(avf, AV_LOG_ERROR, + "Error parsing bitstream filter sequence '%s' associated to " + "stream %d of slave output '%s'\n", entry->value, i, filename); + goto end; + } + } + } + + av_dict_set(&options, entry->key, NULL, 0); + } + + if (options) { + entry = NULL; + while ((entry = av_dict_get(options, "", entry, AV_DICT_IGNORE_SUFFIX))) + av_log(avf2, AV_LOG_ERROR, "Unknown option '%s'\n", entry->key); + ret = AVERROR_OPTION_NOT_FOUND; + goto end; + } + +end: + av_free(format); + av_free(select); + av_dict_free(&options); + return ret; +} + +static void close_slaves(AVFormatContext *avf) +{ + TeeContext *tee = avf->priv_data; + AVFormatContext *avf2; + unsigned i, j; + + for (i = 0; i < tee->nb_slaves; i++) { + avf2 = tee->slaves[i].avf; + + for (j = 0; j < avf2->nb_streams; j++) { + AVBitStreamFilterContext *bsf_next, *bsf = tee->slaves[i].bsfs[j]; + while (bsf) { + bsf_next = bsf->next; + av_bitstream_filter_close(bsf); + bsf = bsf_next; + } + } + av_freep(&tee->slaves[i].stream_map); + av_freep(&tee->slaves[i].bsfs); + + avio_close(avf2->pb); + avf2->pb = NULL; + avformat_free_context(avf2); + tee->slaves[i].avf = NULL; + } +} + +static void log_slave(TeeSlave *slave, void *log_ctx, int log_level) +{ + int i; + av_log(log_ctx, log_level, "filename:'%s' format:%s\n", + slave->avf->filename, slave->avf->oformat->name); + for (i = 0; i < slave->avf->nb_streams; i++) { + AVStream *st = slave->avf->streams[i]; + AVBitStreamFilterContext *bsf = slave->bsfs[i]; + + av_log(log_ctx, log_level, " stream:%d codec:%s type:%s", + i, avcodec_get_name(st->codec->codec_id), + av_get_media_type_string(st->codec->codec_type)); + if (bsf) { + av_log(log_ctx, log_level, " bsfs:"); + while (bsf) { + av_log(log_ctx, log_level, "%s%s", + bsf->filter->name, bsf->next ? "," : ""); + bsf = bsf->next; + } + } + av_log(log_ctx, log_level, "\n"); + } +} + +static int tee_write_header(AVFormatContext *avf) +{ + TeeContext *tee = avf->priv_data; + unsigned nb_slaves = 0, i; + const char *filename = avf->filename; + char *slaves[MAX_SLAVES]; + int ret; + + while (*filename) { + if (nb_slaves == MAX_SLAVES) { + av_log(avf, AV_LOG_ERROR, "Maximum %d slave muxers reached.\n", + MAX_SLAVES); + ret = AVERROR_PATCHWELCOME; + goto fail; + } + if (!(slaves[nb_slaves++] = av_get_token(&filename, slave_delim))) { + ret = AVERROR(ENOMEM); + goto fail; + } + if (strspn(filename, slave_delim)) + filename++; + } + + for (i = 0; i < nb_slaves; i++) { + if ((ret = open_slave(avf, slaves[i], &tee->slaves[i])) < 0) + goto fail; + log_slave(&tee->slaves[i], avf, AV_LOG_VERBOSE); + av_freep(&slaves[i]); + } + + tee->nb_slaves = nb_slaves; + + for (i = 0; i < avf->nb_streams; i++) { + int j, mapped = 0; + for (j = 0; j < tee->nb_slaves; j++) + mapped += tee->slaves[j].stream_map[i] >= 0; + if (!mapped) + av_log(avf, AV_LOG_WARNING, "Input stream #%d is not mapped " + "to any slave.\n", i); + } + return 0; + +fail: + for (i = 0; i < nb_slaves; i++) + av_freep(&slaves[i]); + close_slaves(avf); + return ret; +} + +static int filter_packet(void *log_ctx, AVPacket *pkt, + AVFormatContext *fmt_ctx, AVBitStreamFilterContext *bsf_ctx) +{ + AVCodecContext *enc_ctx = fmt_ctx->streams[pkt->stream_index]->codec; + int ret = 0; + + while (bsf_ctx) { + AVPacket new_pkt = *pkt; + ret = av_bitstream_filter_filter(bsf_ctx, enc_ctx, NULL, + &new_pkt.data, &new_pkt.size, + pkt->data, pkt->size, + pkt->flags & AV_PKT_FLAG_KEY); + if (ret == 0 && new_pkt.data != pkt->data && new_pkt.destruct) { + if ((ret = av_copy_packet(&new_pkt, pkt)) < 0) + break; + ret = 1; + } + + if (ret > 0) { + av_free_packet(pkt); + new_pkt.buf = av_buffer_create(new_pkt.data, new_pkt.size, + av_buffer_default_free, NULL, 0); + if (!new_pkt.buf) + break; + } + *pkt = new_pkt; + + bsf_ctx = bsf_ctx->next; + } + + if (ret < 0) { + av_log(log_ctx, AV_LOG_ERROR, + "Failed to filter bitstream with filter %s for stream %d in file '%s' with codec %s\n", + bsf_ctx->filter->name, pkt->stream_index, fmt_ctx->filename, + avcodec_get_name(enc_ctx->codec_id)); + } + + return ret; +} + +static int tee_write_trailer(AVFormatContext *avf) +{ + TeeContext *tee = avf->priv_data; + AVFormatContext *avf2; + int ret_all = 0, ret; + unsigned i; + + for (i = 0; i < tee->nb_slaves; i++) { + avf2 = tee->slaves[i].avf; + if ((ret = av_write_trailer(avf2)) < 0) + if (!ret_all) + ret_all = ret; + if (!(avf2->oformat->flags & AVFMT_NOFILE)) { + if ((ret = avio_close(avf2->pb)) < 0) + if (!ret_all) + ret_all = ret; + avf2->pb = NULL; + } + } + close_slaves(avf); + return ret_all; +} + +static int tee_write_packet(AVFormatContext *avf, AVPacket *pkt) +{ + TeeContext *tee = avf->priv_data; + AVFormatContext *avf2; + AVPacket pkt2; + int ret_all = 0, ret; + unsigned i, s; + int s2; + AVRational tb, tb2; + + for (i = 0; i < tee->nb_slaves; i++) { + avf2 = tee->slaves[i].avf; + s = pkt->stream_index; + s2 = tee->slaves[i].stream_map[s]; + if (s2 < 0) + continue; + + if ((ret = av_copy_packet(&pkt2, pkt)) < 0 || + (ret = av_dup_packet(&pkt2))< 0) + if (!ret_all) { + ret = ret_all; + continue; + } + tb = avf ->streams[s ]->time_base; + tb2 = avf2->streams[s2]->time_base; + pkt2.pts = av_rescale_q(pkt->pts, tb, tb2); + pkt2.dts = av_rescale_q(pkt->dts, tb, tb2); + pkt2.duration = av_rescale_q(pkt->duration, tb, tb2); + pkt2.stream_index = s2; + + filter_packet(avf2, &pkt2, avf2, tee->slaves[i].bsfs[s2]); + if ((ret = av_interleaved_write_frame(avf2, &pkt2)) < 0) + if (!ret_all) + ret_all = ret; + } + return ret_all; +} + +AVOutputFormat ff_tee_muxer = { + .name = "tee", + .long_name = NULL_IF_CONFIG_SMALL("Multiple muxer tee"), + .priv_data_size = sizeof(TeeContext), + .write_header = tee_write_header, + .write_trailer = tee_write_trailer, + .write_packet = tee_write_packet, + .priv_class = &tee_muxer_class, + .flags = AVFMT_NOFILE, +}; diff --git a/libavformat/thp.c b/libavformat/thp.c index e8ca04f939..568807d6f7 100644 --- a/libavformat/thp.c +++ b/libavformat/thp.c @@ -2,20 +2,20 @@ * THP Demuxer * Copyright (c) 2007 Marco Gerards * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -26,22 +26,22 @@ typedef struct ThpDemuxContext { int version; - int first_frame; - int first_framesz; - int last_frame; + unsigned first_frame; + unsigned first_framesz; + unsigned last_frame; int compoff; - int framecnt; + unsigned framecnt; AVRational fps; - int frame; - int next_frame; - int next_framesz; + unsigned frame; + int64_t next_frame; + unsigned next_framesz; int video_stream_index; int audio_stream_index; int compcount; unsigned char components[16]; AVStream* vst; int has_audio; - int audiosize; + unsigned audiosize; } ThpDemuxContext; @@ -59,6 +59,7 @@ static int thp_read_header(AVFormatContext *s) ThpDemuxContext *thp = s->priv_data; AVStream *st; AVIOContext *pb = s->pb; + int64_t fsize= avio_size(pb); int i; /* Read the file header. */ @@ -71,7 +72,9 @@ static int thp_read_header(AVFormatContext *s) thp->fps = av_d2q(av_int2float(avio_rb32(pb)), INT_MAX); thp->framecnt = avio_rb32(pb); thp->first_framesz = avio_rb32(pb); - avio_rb32(pb); /* Data size. */ + pb->maxsize = avio_rb32(pb); + if(fsize>0 && (!pb->maxsize || fsize < pb->maxsize)) + pb->maxsize= fsize; thp->compoff = avio_rb32(pb); avio_rb32(pb); /* offsetDataOffset. */ @@ -107,6 +110,8 @@ static int thp_read_header(AVFormatContext *s) st->codec->width = avio_rb32(pb); st->codec->height = avio_rb32(pb); st->codec->sample_rate = av_q2d(thp->fps); + st->nb_frames = + st->duration = thp->framecnt; thp->vst = st; thp->video_stream_index = st->index; @@ -142,18 +147,18 @@ static int thp_read_packet(AVFormatContext *s, { ThpDemuxContext *thp = s->priv_data; AVIOContext *pb = s->pb; - int size; + unsigned int size; int ret; if (thp->audiosize == 0) { /* Terminate when last frame is reached. */ if (thp->frame >= thp->framecnt) - return AVERROR(EIO); + return AVERROR_EOF; avio_seek(pb, thp->next_frame, SEEK_SET); /* Locate the next frame and read out its size. */ - thp->next_frame += thp->next_framesz; + thp->next_frame += FFMAX(thp->next_framesz, 1); thp->next_framesz = avio_rb32(pb); avio_rb32(pb); /* Previous total size. */ diff --git a/libavformat/tiertexseq.c b/libavformat/tiertexseq.c index 45300f9e8a..917a331444 100644 --- a/libavformat/tiertexseq.c +++ b/libavformat/tiertexseq.c @@ -2,20 +2,20 @@ * Tiertex Limited SEQ File Demuxer * Copyright (c) 2006 Gregory Montoir (cyx@users.sourceforge.net) * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/tls.c b/libavformat/tls.c index 1baecf4db0..5da82db796 100644 --- a/libavformat/tls.c +++ b/libavformat/tls.c @@ -2,20 +2,20 @@ * TLS/SSL Protocol * Copyright (c) 2011 Martin Storsjo * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -80,6 +80,7 @@ typedef struct { #define E AV_OPT_FLAG_ENCODING_PARAM static const AVOption options[] = { {"ca_file", "Certificate Authority database file", OFFSET(ca_file), AV_OPT_TYPE_STRING, .flags = D|E }, + {"cafile", "Certificate Authority database file", OFFSET(ca_file), AV_OPT_TYPE_STRING, .flags = D|E }, {"tls_verify", "Verify the peer certificate", OFFSET(verify), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, .flags = D|E }, {"cert_file", "Certificate file", OFFSET(cert_file), AV_OPT_TYPE_STRING, .flags = D|E }, {"key_file", "Private key file", OFFSET(key_file), AV_OPT_TYPE_STRING, .flags = D|E }, @@ -137,6 +138,31 @@ static int do_tls_poll(URLContext *h, int ret) return 0; } +static void set_options(URLContext *h, const char *uri) +{ + TLSContext *c = h->priv_data; + char buf[1024]; + const char *p = strchr(uri, '?'); + if (!p) + return; + + if (!c->ca_file && av_find_info_tag(buf, sizeof(buf), "cafile", p)) + c->ca_file = av_strdup(buf); + + if (!c->verify && av_find_info_tag(buf, sizeof(buf), "verify", p)) { + char *endptr = NULL; + c->verify = strtol(buf, &endptr, 10); + if (buf == endptr) + c->verify = 1; + } + + if (!c->cert_file && av_find_info_tag(buf, sizeof(buf), "cert", p)) + c->cert_file = av_strdup(buf); + + if (!c->key_file && av_find_info_tag(buf, sizeof(buf), "key", p)) + c->key_file = av_strdup(buf); +} + static int tls_open(URLContext *h, const char *uri, int flags) { TLSContext *c = h->priv_data; @@ -147,9 +173,12 @@ static int tls_open(URLContext *h, const char *uri, int flags) struct addrinfo hints = { 0 }, *ai = NULL; const char *proxy_path; int use_proxy; + const char *p = strchr(uri, '?'); ff_tls_init(); + if(p && av_find_info_tag(buf, sizeof(buf), "listen", p)) + c->listen = 1; if (c->listen) snprintf(opts, sizeof(opts), "?listen=1"); @@ -188,8 +217,12 @@ static int tls_open(URLContext *h, const char *uri, int flags) if (!c->listen && !numerichost) gnutls_server_name_set(c->session, GNUTLS_NAME_DNS, host, strlen(host)); gnutls_certificate_allocate_credentials(&c->cred); - if (c->ca_file) - gnutls_certificate_set_x509_trust_file(c->cred, c->ca_file, GNUTLS_X509_FMT_PEM); + set_options(h, uri); + if (c->ca_file) { + ret = gnutls_certificate_set_x509_trust_file(c->cred, c->ca_file, GNUTLS_X509_FMT_PEM); + if (ret < 0) + av_log(h, AV_LOG_ERROR, "%s\n", gnutls_strerror(ret)); + } #if GNUTLS_VERSION_MAJOR >= 3 else gnutls_certificate_set_x509_system_trust(c->cred); @@ -207,7 +240,8 @@ static int tls_open(URLContext *h, const char *uri, int flags) ret = AVERROR(EIO); goto fail; } - } + } else if (c->cert_file || c->key_file) + av_log(h, AV_LOG_ERROR, "cert and key required\n"); gnutls_credentials_set(c->session, GNUTLS_CRD_CERTIFICATE, c->cred); gnutls_transport_set_ptr(c->session, (gnutls_transport_ptr_t) (intptr_t) c->fd); @@ -258,8 +292,11 @@ static int tls_open(URLContext *h, const char *uri, int flags) ret = AVERROR(EIO); goto fail; } - if (c->ca_file) - SSL_CTX_load_verify_locations(c->ctx, c->ca_file, NULL); + set_options(h, uri); + if (c->ca_file) { + if (!SSL_CTX_load_verify_locations(c->ctx, c->ca_file, NULL)) + av_log(h, AV_LOG_ERROR, "SSL_CTX_load_verify_locations %s\n", ERR_error_string(ERR_get_error(), NULL)); + } if (c->cert_file && !SSL_CTX_use_certificate_chain_file(c->ctx, c->cert_file)) { av_log(h, AV_LOG_ERROR, "Unable to load cert file %s: %s\n", c->cert_file, ERR_error_string(ERR_get_error(), NULL)); @@ -275,7 +312,7 @@ static int tls_open(URLContext *h, const char *uri, int flags) // Note, this doesn't check that the peer certificate actually matches // the requested hostname. if (c->verify) - SSL_CTX_set_verify(c->ctx, SSL_VERIFY_PEER, NULL); + SSL_CTX_set_verify(c->ctx, SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL); c->ssl = SSL_new(c->ctx); if (!c->ssl) { av_log(h, AV_LOG_ERROR, "%s\n", ERR_error_string(ERR_get_error(), NULL)); diff --git a/libavformat/tmv.c b/libavformat/tmv.c index 103ac4af20..8be4dbffaf 100644 --- a/libavformat/tmv.c +++ b/libavformat/tmv.c @@ -2,20 +2,20 @@ * 8088flex TMV file demuxer * Copyright (c) 2009 Daniel Verkamp <daniel at drv.nu> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -154,7 +154,7 @@ static int tmv_read_packet(AVFormatContext *s, AVPacket *pkt) int ret, pkt_size = tmv->stream_index ? tmv->audio_chunk_size : tmv->video_chunk_size; - if (pb->eof_reached) + if (url_feof(pb)) return AVERROR_EOF; ret = av_get_packet(pb, pkt, pkt_size); diff --git a/libavformat/tta.c b/libavformat/tta.c index e5e6e7186c..7174fd5438 100644 --- a/libavformat/tta.c +++ b/libavformat/tta.c @@ -2,27 +2,30 @@ * TTA demuxer * Copyright (c) 2006 Alex Beregszaszi * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "libavcodec/get_bits.h" +#include "apetag.h" #include "avformat.h" +#include "avio_internal.h" #include "internal.h" #include "id3v1.h" +#include "libavutil/crc.h" #include "libavutil/dict.h" typedef struct { @@ -31,11 +34,19 @@ typedef struct { int last_frame_size; } TTAContext; -static int tta_probe(AVProbeData *p) +static unsigned long tta_check_crc(unsigned long checksum, const uint8_t *buf, + unsigned int len) { - const uint8_t *d = p->buf; + return av_crc(av_crc_get_table(AV_CRC_32_IEEE_LE), checksum, buf, len); +} - if (d[0] == 'T' && d[1] == 'T' && d[2] == 'A' && d[3] == '1') +static int tta_probe(AVProbeData *p) +{ + if (AV_RL32(&p->buf[0]) == MKTAG('T', 'T', 'A', '1') && + (AV_RL16(&p->buf[4]) == 1 || AV_RL16(&p->buf[4]) == 2) && + AV_RL16(&p->buf[6]) > 0 && + AV_RL16(&p->buf[8]) > 0 && + AV_RL32(&p->buf[10]) > 0) return AVPROBE_SCORE_EXTENSION + 30; return 0; } @@ -44,15 +55,16 @@ static int tta_read_header(AVFormatContext *s) { TTAContext *c = s->priv_data; AVStream *st; - int i, channels, bps, samplerate, datalen; + int i, channels, bps, samplerate; uint64_t framepos, start_offset; + uint32_t nb_samples, crc; - if (!av_dict_get(s->metadata, "", NULL, AV_DICT_IGNORE_SUFFIX)) - ff_id3v1_read(s); + ff_id3v1_read(s); start_offset = avio_tell(s->pb); + ffio_init_checksum(s->pb, tta_check_crc, UINT32_MAX); if (avio_rl32(s->pb) != AV_RL32("TTA1")) - return -1; // not tta file + return AVERROR_INVALIDDATA; avio_skip(s->pb, 2); // FIXME: flags channels = avio_rl16(s->pb); @@ -60,27 +72,31 @@ static int tta_read_header(AVFormatContext *s) samplerate = avio_rl32(s->pb); if(samplerate <= 0 || samplerate > 1000000){ av_log(s, AV_LOG_ERROR, "nonsense samplerate\n"); - return -1; + return AVERROR_INVALIDDATA; } - datalen = avio_rl32(s->pb); - if(datalen < 0){ - av_log(s, AV_LOG_ERROR, "nonsense datalen\n"); - return -1; + nb_samples = avio_rl32(s->pb); + if (!nb_samples) { + av_log(s, AV_LOG_ERROR, "invalid number of samples\n"); + return AVERROR_INVALIDDATA; } - avio_skip(s->pb, 4); // header crc + crc = ffio_get_checksum(s->pb) ^ UINT32_MAX; + if (crc != avio_rl32(s->pb)) { + av_log(s, AV_LOG_ERROR, "Header CRC error\n"); + return AVERROR_INVALIDDATA; + } c->frame_size = samplerate * 256 / 245; - c->last_frame_size = datalen % c->frame_size; + c->last_frame_size = nb_samples % c->frame_size; if (!c->last_frame_size) c->last_frame_size = c->frame_size; - c->totalframes = datalen / c->frame_size + (c->last_frame_size < c->frame_size); + c->totalframes = nb_samples / c->frame_size + (c->last_frame_size < c->frame_size); c->currentframe = 0; - if(c->totalframes >= UINT_MAX/sizeof(uint32_t)){ - av_log(s, AV_LOG_ERROR, "totalframes too large\n"); - return -1; + if(c->totalframes >= UINT_MAX/sizeof(uint32_t) || c->totalframes <= 0){ + av_log(s, AV_LOG_ERROR, "totalframes %d invalid\n", c->totalframes); + return AVERROR_INVALIDDATA; } st = avformat_new_stream(s, NULL); @@ -89,17 +105,28 @@ static int tta_read_header(AVFormatContext *s) avpriv_set_pts_info(st, 64, 1, samplerate); st->start_time = 0; - st->duration = datalen; + st->duration = nb_samples; framepos = avio_tell(s->pb) + 4*c->totalframes + 4; + if (ff_alloc_extradata(st->codec, avio_tell(s->pb) - start_offset)) + return AVERROR(ENOMEM); + + avio_seek(s->pb, start_offset, SEEK_SET); + avio_read(s->pb, st->codec->extradata, st->codec->extradata_size); + + ffio_init_checksum(s->pb, tta_check_crc, UINT32_MAX); for (i = 0; i < c->totalframes; i++) { uint32_t size = avio_rl32(s->pb); av_add_index_entry(st, framepos, i * c->frame_size, size, 0, AVINDEX_KEYFRAME); framepos += size; } - avio_skip(s->pb, 4); // seektable crc + crc = ffio_get_checksum(s->pb) ^ UINT32_MAX; + if (crc != avio_rl32(s->pb)) { + av_log(s, AV_LOG_ERROR, "Seek table CRC error\n"); + return AVERROR_INVALIDDATA; + } st->codec->codec_type = AVMEDIA_TYPE_AUDIO; st->codec->codec_id = AV_CODEC_ID_TTA; @@ -107,19 +134,11 @@ static int tta_read_header(AVFormatContext *s) st->codec->sample_rate = samplerate; st->codec->bits_per_coded_sample = bps; - st->codec->extradata_size = avio_tell(s->pb) - start_offset; - if(st->codec->extradata_size+FF_INPUT_BUFFER_PADDING_SIZE <= (unsigned)st->codec->extradata_size){ - //this check is redundant as avio_read should fail - av_log(s, AV_LOG_ERROR, "extradata_size too large\n"); - return -1; + if (s->pb->seekable) { + int64_t pos = avio_tell(s->pb); + ff_ape_parse_tag(s); + avio_seek(s->pb, pos, SEEK_SET); } - st->codec->extradata = av_mallocz(st->codec->extradata_size+FF_INPUT_BUFFER_PADDING_SIZE); - if (!st->codec->extradata) { - st->codec->extradata_size = 0; - return AVERROR(ENOMEM); - } - avio_seek(s->pb, start_offset, SEEK_SET); - avio_read(s->pb, st->codec->extradata, st->codec->extradata_size); return 0; } diff --git a/libavformat/tty.c b/libavformat/tty.c index 0ae1510491..3afb9b2346 100644 --- a/libavformat/tty.c +++ b/libavformat/tty.c @@ -2,20 +2,20 @@ * Tele-typewriter demuxer * Copyright (c) 2010 Peter Ross <pross@xvid.org> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -38,8 +38,8 @@ typedef struct { AVClass *class; int chars_per_frame; uint64_t fsize; /**< file size less metadata buffer */ - char *video_size;/**< A string describing video size, set by a private option. */ - char *framerate; /**< Set by a private option. */ + int width, height; /**< Set by a private option. */ + AVRational framerate; /**< Set by a private option. */ } TtyDemuxContext; /** @@ -75,9 +75,8 @@ static int efi_read(AVFormatContext *avctx, uint64_t start_pos) static int read_header(AVFormatContext *avctx) { TtyDemuxContext *s = avctx->priv_data; - int width = 0, height = 0, ret = 0; + int ret = 0; AVStream *st = avformat_new_stream(avctx, NULL); - AVRational framerate; if (!st) { ret = AVERROR(ENOMEM); @@ -87,18 +86,10 @@ static int read_header(AVFormatContext *avctx) st->codec->codec_type = AVMEDIA_TYPE_VIDEO; st->codec->codec_id = AV_CODEC_ID_ANSI; - if (s->video_size && (ret = av_parse_video_size(&width, &height, s->video_size)) < 0) { - av_log (avctx, AV_LOG_ERROR, "Couldn't parse video size.\n"); - goto fail; - } - if ((ret = av_parse_video_rate(&framerate, s->framerate)) < 0) { - av_log(avctx, AV_LOG_ERROR, "Could not parse framerate: %s.\n", s->framerate); - goto fail; - } - st->codec->width = width; - st->codec->height = height; - avpriv_set_pts_info(st, 60, framerate.den, framerate.num); - st->avg_frame_rate = framerate; + st->codec->width = s->width; + st->codec->height = s->height; + avpriv_set_pts_info(st, 60, s->framerate.den, s->framerate.num); + st->avg_frame_rate = s->framerate; /* simulate tty display speed */ s->chars_per_frame = FFMAX(av_q2d(st->time_base)*s->chars_per_frame, 1); @@ -122,20 +113,22 @@ static int read_packet(AVFormatContext *avctx, AVPacket *pkt) TtyDemuxContext *s = avctx->priv_data; int n; - if (avctx->pb->eof_reached) + if (url_feof(avctx->pb)) return AVERROR_EOF; n = s->chars_per_frame; if (s->fsize) { // ignore metadata buffer uint64_t p = avio_tell(avctx->pb); + if (p == s->fsize) + return AVERROR_EOF; if (p + s->chars_per_frame > s->fsize) n = s->fsize - p; } pkt->size = av_get_packet(avctx->pb, pkt, n); - if (pkt->size <= 0) - return AVERROR(EIO); + if (pkt->size < 0) + return pkt->size; pkt->flags |= AV_PKT_FLAG_KEY; return 0; } @@ -144,8 +137,8 @@ static int read_packet(AVFormatContext *avctx, AVPacket *pkt) #define DEC AV_OPT_FLAG_DECODING_PARAM static const AVOption options[] = { { "chars_per_frame", "", offsetof(TtyDemuxContext, chars_per_frame), AV_OPT_TYPE_INT, {.i64 = 6000}, 1, INT_MAX, AV_OPT_FLAG_DECODING_PARAM}, - { "video_size", "A string describing frame size, such as 640x480 or hd720.", OFFSET(video_size), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, DEC }, - { "framerate", "", OFFSET(framerate), AV_OPT_TYPE_STRING, {.str = "25"}, 0, 0, DEC }, + { "video_size", "A string describing frame size, such as 640x480 or hd720.", OFFSET(width), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, DEC }, + { "framerate", "", OFFSET(framerate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, 0, DEC }, { NULL }, }; diff --git a/libavformat/txd.c b/libavformat/txd.c index 5897881b9b..194945bbc8 100644 --- a/libavformat/txd.c +++ b/libavformat/txd.c @@ -2,20 +2,20 @@ * Renderware TeXture Dictionary (.txd) demuxer * Copyright (c) 2007 Ivo van Poorten * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -48,6 +48,7 @@ static int txd_read_header(AVFormatContext *s) { st->codec->time_base.den = 5; st->codec->time_base.num = 1; /* the parameters will be extracted from the compressed bitstream */ + return 0; } @@ -61,7 +62,7 @@ next_chunk: chunk_size = avio_rl32(pb); marker = avio_rl32(pb); - if (s->pb->eof_reached) + if (url_feof(s->pb)) return AVERROR_EOF; if (marker != TXD_MARKER && marker != TXD_MARKER2) { av_log(s, AV_LOG_ERROR, "marker does not match\n"); @@ -69,17 +70,17 @@ next_chunk: } switch (id) { - case TXD_INFO: - if (chunk_size > 100) - break; - case TXD_EXTRA: - avio_skip(s->pb, chunk_size); - case TXD_FILE: - case TXD_TEXTURE: - goto next_chunk; - default: - av_log(s, AV_LOG_ERROR, "unknown chunk id %i\n", id); - return AVERROR_INVALIDDATA; + case TXD_INFO: + if (chunk_size > 100) + break; + case TXD_EXTRA: + avio_skip(s->pb, chunk_size); + case TXD_FILE: + case TXD_TEXTURE: + goto next_chunk; + default: + av_log(s, AV_LOG_ERROR, "unknown chunk id %i\n", id); + return AVERROR_INVALIDDATA; } ret = av_get_packet(s->pb, pkt, chunk_size); diff --git a/libavformat/udp.c b/libavformat/udp.c index bfa8cf25e8..3c0d6bf012 100644 --- a/libavformat/udp.c +++ b/libavformat/udp.c @@ -2,20 +2,20 @@ * UDP prototype streaming system * Copyright (c) 2000, 2001, 2002 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -29,31 +29,87 @@ #include "avformat.h" #include "avio_internal.h" #include "libavutil/parseutils.h" +#include "libavutil/fifo.h" +#include "libavutil/intreadwrite.h" #include "libavutil/avstring.h" +#include "libavutil/opt.h" +#include "libavutil/log.h" +#include "libavutil/time.h" #include "internal.h" #include "network.h" #include "os_support.h" #include "url.h" +#if HAVE_PTHREAD_CANCEL +#include <pthread.h> +#endif + +#ifndef HAVE_PTHREAD_CANCEL +#define HAVE_PTHREAD_CANCEL 0 +#endif + #ifndef IPV6_ADD_MEMBERSHIP #define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP #define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP #endif +#define UDP_TX_BUF_SIZE 32768 +#define UDP_MAX_PKT_SIZE 65536 + typedef struct { + const AVClass *class; int udp_fd; int ttl; int buffer_size; int is_multicast; int local_port; int reuse_socket; + int overrun_nonfatal; struct sockaddr_storage dest_addr; int dest_addr_len; int is_connected; + + /* Circular Buffer variables for use in UDP receive code */ + int circular_buffer_size; + AVFifoBuffer *fifo; + int circular_buffer_error; +#if HAVE_PTHREAD_CANCEL + pthread_t circular_buffer_thread; + pthread_mutex_t mutex; + pthread_cond_t cond; + int thread_started; +#endif + uint8_t tmp[UDP_MAX_PKT_SIZE+4]; + int remaining_in_dg; + char *local_addr; + int packet_size; + int timeout; } UDPContext; -#define UDP_TX_BUF_SIZE 32768 -#define UDP_MAX_PKT_SIZE 65536 +#define OFFSET(x) offsetof(UDPContext, x) +#define D AV_OPT_FLAG_DECODING_PARAM +#define E AV_OPT_FLAG_ENCODING_PARAM +static const AVOption options[] = { +{"buffer_size", "Socket buffer size in bytes", OFFSET(buffer_size), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, D|E }, +{"localport", "Set local port to bind to", OFFSET(local_port), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, D|E }, +{"localaddr", "Choose local IP address", OFFSET(local_addr), AV_OPT_TYPE_STRING, {.str = ""}, 0, 0, D|E }, +{"pkt_size", "Set size of UDP packets", OFFSET(packet_size), AV_OPT_TYPE_INT, {.i64 = 1472}, 0, INT_MAX, D|E }, +{"reuse", "Explicitly allow or disallow reusing UDP sockets", OFFSET(reuse_socket), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, D|E }, +{"ttl", "Set the time to live value (for multicast only)", OFFSET(ttl), AV_OPT_TYPE_INT, {.i64 = 16}, 0, INT_MAX, E }, +{"connect", "Should connect() be called on socket", OFFSET(is_connected), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, D|E }, +/* TODO 'sources', 'block' option */ +{"fifo_size", "Set the UDP receiving circular buffer size, expressed as a number of packets with size of 188 bytes", OFFSET(circular_buffer_size), AV_OPT_TYPE_INT, {.i64 = 7*4096}, 0, INT_MAX, D }, +{"overrun_nonfatal", "Survive in case of UDP receiving circular buffer overrun", OFFSET(overrun_nonfatal), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, D }, +{"timeout", "In read mode: if no data arrived in more than this time interval, raise error", OFFSET(timeout), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, D }, +{NULL} +}; + +static const AVClass udp_context_class = { + .class_name = "udp", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; static void log_net_error(void *ctx, int level, const char* prefix) { @@ -317,6 +373,7 @@ static int udp_port(struct sockaddr_storage *addr, int addr_len) * 'localport=n' : set the local port * 'pkt_size=n' : set max packet size * 'reuse=1' : enable reusing the socket + * 'overrun_nonfatal=1': survive in case of circular buffer overrun * * @param h media file context * @param uri of the remote server @@ -378,6 +435,65 @@ static int udp_get_file_handle(URLContext *h) return s->udp_fd; } +#if HAVE_PTHREAD_CANCEL +static void *circular_buffer_task( void *_URLContext) +{ + URLContext *h = _URLContext; + UDPContext *s = h->priv_data; + int old_cancelstate; + + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_cancelstate); + pthread_mutex_lock(&s->mutex); + if (ff_socket_nonblock(s->udp_fd, 0) < 0) { + av_log(h, AV_LOG_ERROR, "Failed to set blocking mode"); + s->circular_buffer_error = AVERROR(EIO); + goto end; + } + while(1) { + int len; + + pthread_mutex_unlock(&s->mutex); + /* Blocking operations are always cancellation points; + see "General Information" / "Thread Cancelation Overview" + in Single Unix. */ + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &old_cancelstate); + len = recv(s->udp_fd, s->tmp+4, sizeof(s->tmp)-4, 0); + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_cancelstate); + pthread_mutex_lock(&s->mutex); + if (len < 0) { + if (ff_neterrno() != AVERROR(EAGAIN) && ff_neterrno() != AVERROR(EINTR)) { + s->circular_buffer_error = ff_neterrno(); + goto end; + } + continue; + } + AV_WL32(s->tmp, len); + + if(av_fifo_space(s->fifo) < len + 4) { + /* No Space left */ + if (s->overrun_nonfatal) { + av_log(h, AV_LOG_WARNING, "Circular buffer overrun. " + "Surviving due to overrun_nonfatal option\n"); + continue; + } else { + av_log(h, AV_LOG_ERROR, "Circular buffer overrun. " + "To avoid, increase fifo_size URL option. " + "To survive in such case, use overrun_nonfatal option\n"); + s->circular_buffer_error = AVERROR(EIO); + goto end; + } + } + av_fifo_generic_write(s->fifo, s->tmp, len+4, NULL); + pthread_cond_signal(&s->cond); + } + +end: + pthread_cond_signal(&s->cond); + pthread_mutex_unlock(&s->mutex); + return NULL; +} +#endif + static int parse_source_list(char *buf, char **sources, int *num_sources, int max_sources) { @@ -416,12 +532,10 @@ static int udp_open(URLContext *h, const char *uri, int flags) char *include_sources[32], *exclude_sources[32]; h->is_streamed = 1; - h->max_packet_size = 1472; is_output = !(flags & AVIO_FLAG_READ); - - s->ttl = 16; - s->buffer_size = is_output ? UDP_TX_BUF_SIZE : UDP_MAX_PKT_SIZE; + if (!s->buffer_size) /* if not set explicitly */ + s->buffer_size = is_output ? UDP_TX_BUF_SIZE : UDP_MAX_PKT_SIZE; p = strchr(uri, '?'); if (p) { @@ -433,6 +547,17 @@ static int udp_open(URLContext *h, const char *uri, int flags) s->reuse_socket = 1; reuse_specified = 1; } + if (av_find_info_tag(buf, sizeof(buf), "overrun_nonfatal", p)) { + char *endptr = NULL; + s->overrun_nonfatal = strtol(buf, &endptr, 10); + /* assume if no digits were found it is a request to enable it */ + if (buf == endptr) + s->overrun_nonfatal = 1; + if (!HAVE_PTHREAD_CANCEL) + av_log(h, AV_LOG_WARNING, + "'overrun_nonfatal' option was set but it is not supported " + "on this build (pthread support is required)\n"); + } if (av_find_info_tag(buf, sizeof(buf), "ttl", p)) { s->ttl = strtol(buf, NULL, 10); } @@ -440,7 +565,7 @@ static int udp_open(URLContext *h, const char *uri, int flags) s->local_port = strtol(buf, NULL, 10); } if (av_find_info_tag(buf, sizeof(buf), "pkt_size", p)) { - h->max_packet_size = strtol(buf, NULL, 10); + s->packet_size = strtol(buf, NULL, 10); } if (av_find_info_tag(buf, sizeof(buf), "buffer_size", p)) { s->buffer_size = strtol(buf, NULL, 10); @@ -448,6 +573,13 @@ static int udp_open(URLContext *h, const char *uri, int flags) if (av_find_info_tag(buf, sizeof(buf), "connect", p)) { s->is_connected = strtol(buf, NULL, 10); } + if (av_find_info_tag(buf, sizeof(buf), "fifo_size", p)) { + s->circular_buffer_size = strtol(buf, NULL, 10); + if (!HAVE_PTHREAD_CANCEL) + av_log(h, AV_LOG_WARNING, + "'circular_buffer_size' option was set but it is not supported " + "on this build (pthread support is required)\n"); + } if (av_find_info_tag(buf, sizeof(buf), "localaddr", p)) { av_strlcpy(localaddr, buf, sizeof(localaddr)); } @@ -461,7 +593,17 @@ static int udp_open(URLContext *h, const char *uri, int flags) FF_ARRAY_ELEMS(exclude_sources))) goto fail; } + if (!is_output && av_find_info_tag(buf, sizeof(buf), "timeout", p)) + s->timeout = strtol(buf, NULL, 10); + } + /* handling needed to support options picking from both AVOption and URL */ + s->circular_buffer_size *= 188; + if (flags & AVIO_FLAG_WRITE) { + h->max_packet_size = s->packet_size; + } else { + h->max_packet_size = UDP_MAX_PKT_SIZE; } + h->rw_timeout = s->timeout; /* fill the dest addr */ av_url_split(NULL, 0, NULL, 0, hostname, sizeof(hostname), &port, NULL, 0, uri); @@ -478,7 +620,7 @@ static int udp_open(URLContext *h, const char *uri, int flags) if ((s->is_multicast || !s->local_port) && (h->flags & AVIO_FLAG_READ)) s->local_port = port; - udp_fd = udp_socket_create(s, &my_addr, &len, localaddr); + udp_fd = udp_socket_create(s, &my_addr, &len, localaddr[0] ? localaddr : s->local_addr); if (udp_fd < 0) goto fail; @@ -566,10 +708,43 @@ static int udp_open(URLContext *h, const char *uri, int flags) av_freep(&exclude_sources[i]); s->udp_fd = udp_fd; + +#if HAVE_PTHREAD_CANCEL + if (!is_output && s->circular_buffer_size) { + int ret; + + /* start the task going */ + s->fifo = av_fifo_alloc(s->circular_buffer_size); + ret = pthread_mutex_init(&s->mutex, NULL); + if (ret != 0) { + av_log(h, AV_LOG_ERROR, "pthread_mutex_init failed : %s\n", strerror(ret)); + goto fail; + } + ret = pthread_cond_init(&s->cond, NULL); + if (ret != 0) { + av_log(h, AV_LOG_ERROR, "pthread_cond_init failed : %s\n", strerror(ret)); + goto cond_fail; + } + ret = pthread_create(&s->circular_buffer_thread, NULL, circular_buffer_task, h); + if (ret != 0) { + av_log(h, AV_LOG_ERROR, "pthread_create failed : %s\n", strerror(ret)); + goto thread_fail; + } + s->thread_started = 1; + } +#endif + return 0; +#if HAVE_PTHREAD_CANCEL + thread_fail: + pthread_cond_destroy(&s->cond); + cond_fail: + pthread_mutex_destroy(&s->mutex); +#endif fail: if (udp_fd >= 0) closesocket(udp_fd); + av_fifo_free(s->fifo); for (i = 0; i < num_include_sources; i++) av_freep(&include_sources[i]); for (i = 0; i < num_exclude_sources; i++) @@ -581,6 +756,50 @@ static int udp_read(URLContext *h, uint8_t *buf, int size) { UDPContext *s = h->priv_data; int ret; + int avail, nonblock = h->flags & AVIO_FLAG_NONBLOCK; + +#if HAVE_PTHREAD_CANCEL + if (s->fifo) { + pthread_mutex_lock(&s->mutex); + do { + avail = av_fifo_size(s->fifo); + if (avail) { // >=size) { + uint8_t tmp[4]; + + av_fifo_generic_read(s->fifo, tmp, 4, NULL); + avail= AV_RL32(tmp); + if(avail > size){ + av_log(h, AV_LOG_WARNING, "Part of datagram lost due to insufficient buffer size\n"); + avail= size; + } + + av_fifo_generic_read(s->fifo, buf, avail, NULL); + av_fifo_drain(s->fifo, AV_RL32(tmp) - avail); + pthread_mutex_unlock(&s->mutex); + return avail; + } else if(s->circular_buffer_error){ + int err = s->circular_buffer_error; + pthread_mutex_unlock(&s->mutex); + return err; + } else if(nonblock) { + pthread_mutex_unlock(&s->mutex); + return AVERROR(EAGAIN); + } + else { + /* FIXME: using the monotonic clock would be better, + but it does not exist on all supported platforms. */ + int64_t t = av_gettime() + 100000; + struct timespec tv = { .tv_sec = t / 1000000, + .tv_nsec = (t % 1000000) * 1000 }; + if (pthread_cond_timedwait(&s->cond, &s->mutex, &tv) < 0) { + pthread_mutex_unlock(&s->mutex); + return AVERROR(errno == ETIMEDOUT ? EAGAIN : errno); + } + nonblock = 1; + } + } while( 1); + } +#endif if (!(h->flags & AVIO_FLAG_NONBLOCK)) { ret = ff_network_wait_fd(s->udp_fd, 0); @@ -588,6 +807,7 @@ static int udp_read(URLContext *h, uint8_t *buf, int size) return ret; } ret = recv(s->udp_fd, buf, size, 0); + return ret < 0 ? ff_neterrno() : ret; } @@ -615,10 +835,22 @@ static int udp_write(URLContext *h, const uint8_t *buf, int size) static int udp_close(URLContext *h) { UDPContext *s = h->priv_data; + int ret; if (s->is_multicast && (h->flags & AVIO_FLAG_READ)) udp_leave_multicast_group(s->udp_fd, (struct sockaddr *)&s->dest_addr); closesocket(s->udp_fd); +#if HAVE_PTHREAD_CANCEL + if (s->thread_started) { + pthread_cancel(s->circular_buffer_thread); + ret = pthread_join(s->circular_buffer_thread, NULL); + if (ret != 0) + av_log(h, AV_LOG_ERROR, "pthread_join(): %s\n", strerror(ret)); + pthread_mutex_destroy(&s->mutex); + pthread_cond_destroy(&s->cond); + } +#endif + av_fifo_free(s->fifo); return 0; } @@ -630,5 +862,6 @@ URLProtocol ff_udp_protocol = { .url_close = udp_close, .url_get_file_handle = udp_get_file_handle, .priv_data_size = sizeof(UDPContext), + .priv_data_class = &udp_context_class, .flags = URL_PROTOCOL_FLAG_NETWORK, }; diff --git a/libavformat/unix.c b/libavformat/unix.c index ab57c68195..0e61318fbd 100644 --- a/libavformat/unix.c +++ b/libavformat/unix.c @@ -2,20 +2,20 @@ * Unix socket protocol * Copyright (c) 2013 Luca Barbato * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -26,12 +26,11 @@ * */ -#include <sys/un.h> - #include "libavutil/avstring.h" #include "libavutil/opt.h" #include "os_support.h" #include "network.h" +#include <sys/un.h> #include "url.h" typedef struct UnixContext { diff --git a/libavformat/url-test.c b/libavformat/url-test.c index 503b36e572..a1da82e493 100644 --- a/libavformat/url-test.c +++ b/libavformat/url-test.c @@ -1,20 +1,20 @@ /* * Copyright (c) 2012 Martin Storsjo * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/url.c b/libavformat/url.c index eeda1f0297..47e15843cf 100644 --- a/libavformat/url.c +++ b/libavformat/url.c @@ -2,20 +2,20 @@ * URL utility functions * Copyright (c) 2000, 2001, 2002 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/url.h b/libavformat/url.h index ff1e21b462..712ea0f5b4 100644 --- a/libavformat/url.h +++ b/libavformat/url.h @@ -1,19 +1,19 @@ /* * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -48,6 +48,7 @@ typedef struct URLContext { int is_streamed; /**< true if streamed (no seek possible), default = false */ int is_connected; AVIOInterruptCB interrupt_callback; + int64_t rw_timeout; /**< maximum time to wait for (network) read/write operation completion, in mcs */ } URLContext; typedef struct URLProtocol { @@ -100,7 +101,7 @@ typedef struct URLProtocol { * is to be opened * @param int_cb interrupt callback to use for the URLContext, may be * NULL - * @return 0 in case of success, a negative value corresponding to an + * @return >= 0 in case of success, a negative value corresponding to an * AVERROR code in case of failure */ int ffurl_alloc(URLContext **puc, const char *filename, int flags, @@ -129,7 +130,7 @@ int ffurl_connect(URLContext *uc, AVDictionary **options); * @param options A dictionary filled with protocol-private options. On return * this parameter will be destroyed and replaced with a dict containing options * that were not found. May be NULL. - * @return 0 in case of success, a negative value corresponding to an + * @return >= 0 in case of success, a negative value corresponding to an * AVERROR code in case of failure */ int ffurl_open(URLContext **puc, const char *filename, int flags, @@ -181,11 +182,12 @@ int64_t ffurl_seek(URLContext *h, int64_t pos, int whence); /** * Close the resource accessed by the URLContext h, and free the - * memory used by it. + * memory used by it. Also set the URLContext pointer to NULL. * * @return a negative value if an error condition occurred, 0 * otherwise */ +int ffurl_closep(URLContext **h); int ffurl_close(URLContext *h); /** @@ -269,7 +271,7 @@ int ff_url_join(char *str, int size, const char *proto, const char *authorization, const char *hostname, int port, const char *fmt, ...) av_printf_format(7, 8); -/* +/** * Convert a relative url into an absolute url, given a base url. * * @param buf the buffer where output absolute url is written diff --git a/libavformat/urldecode.c b/libavformat/urldecode.c index 49af9ba1e0..283d912671 100644 --- a/libavformat/urldecode.c +++ b/libavformat/urldecode.c @@ -9,20 +9,20 @@ * based on http://www.icosaedro.it/apache/urldecode.c * from Umberto Salsi (salsi@icosaedro.it) * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/urldecode.h b/libavformat/urldecode.h index b43f319c9e..cb81ebc6f7 100644 --- a/libavformat/urldecode.h +++ b/libavformat/urldecode.h @@ -1,18 +1,18 @@ /* - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/utils.c b/libavformat/utils.c index bec70a9e01..3bb837a7b2 100644 --- a/libavformat/utils.c +++ b/libavformat/utils.c @@ -1,21 +1,21 @@ /* - * various utility functions for use within Libav + * various utility functions for use within FFmpeg * Copyright (c) 2000, 2001, 2002 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -23,6 +23,7 @@ #include "avio_internal.h" #include "internal.h" #include "libavcodec/internal.h" +#include "libavcodec/raw.h" #include "libavcodec/bytestream.h" #include "libavutil/opt.h" #include "libavutil/dict.h" @@ -35,6 +36,7 @@ #include "libavutil/mathematics.h" #include "libavutil/parseutils.h" #include "libavutil/time.h" +#include "libavutil/timestamp.h" #include "riff.h" #include "audiointerleave.h" #include "url.h" @@ -48,38 +50,115 @@ /** * @file - * various utility functions for use within Libav + * various utility functions for use within FFmpeg */ unsigned avformat_version(void) { + av_assert0(LIBAVFORMAT_VERSION_MICRO >= 100); return LIBAVFORMAT_VERSION_INT; } const char *avformat_configuration(void) { - return LIBAV_CONFIGURATION; + return FFMPEG_CONFIGURATION; } const char *avformat_license(void) { #define LICENSE_PREFIX "libavformat license: " - return LICENSE_PREFIX LIBAV_LICENSE + sizeof(LICENSE_PREFIX) - 1; + return LICENSE_PREFIX FFMPEG_LICENSE + sizeof(LICENSE_PREFIX) - 1; +} + +#define RELATIVE_TS_BASE (INT64_MAX - (1LL<<48)) + +static int is_relative(int64_t ts) { + return ts > (RELATIVE_TS_BASE - (1LL<<48)); +} + +/** + * Wrap a given time stamp, if there is an indication for an overflow + * + * @param st stream + * @param timestamp the time stamp to wrap + * @return resulting time stamp + */ +static int64_t wrap_timestamp(AVStream *st, int64_t timestamp) +{ + if (st->pts_wrap_behavior != AV_PTS_WRAP_IGNORE && + st->pts_wrap_reference != AV_NOPTS_VALUE && timestamp != AV_NOPTS_VALUE) { + if (st->pts_wrap_behavior == AV_PTS_WRAP_ADD_OFFSET && + timestamp < st->pts_wrap_reference) + return timestamp + (1ULL<<st->pts_wrap_bits); + else if (st->pts_wrap_behavior == AV_PTS_WRAP_SUB_OFFSET && + timestamp >= st->pts_wrap_reference) + return timestamp - (1ULL<<st->pts_wrap_bits); + } + return timestamp; +} + +MAKE_ACCESSORS(AVStream, stream, AVRational, r_frame_rate) +MAKE_ACCESSORS(AVFormatContext, format, AVCodec *, video_codec) +MAKE_ACCESSORS(AVFormatContext, format, AVCodec *, audio_codec) +MAKE_ACCESSORS(AVFormatContext, format, AVCodec *, subtitle_codec) + +static AVCodec *find_decoder(AVFormatContext *s, AVStream *st, enum AVCodecID codec_id) +{ + if (st->codec->codec) + return st->codec->codec; + + switch(st->codec->codec_type){ + case AVMEDIA_TYPE_VIDEO: + if(s->video_codec) return s->video_codec; + break; + case AVMEDIA_TYPE_AUDIO: + if(s->audio_codec) return s->audio_codec; + break; + case AVMEDIA_TYPE_SUBTITLE: + if(s->subtitle_codec) return s->subtitle_codec; + break; + } + + return avcodec_find_decoder(codec_id); +} + +int av_format_get_probe_score(const AVFormatContext *s) +{ + return s->probe_score; } /* an arbitrarily chosen "sane" max packet size -- 50M */ #define SANE_CHUNK_SIZE (50000000) +int ffio_limit(AVIOContext *s, int size) +{ + if(s->maxsize>=0){ + int64_t remaining= s->maxsize - avio_tell(s); + if(remaining < size){ + int64_t newsize= avio_size(s); + if(!s->maxsize || s->maxsize<newsize) + s->maxsize= newsize - !newsize; + remaining= s->maxsize - avio_tell(s); + remaining= FFMAX(remaining, 0); + } + + if(s->maxsize>=0 && remaining+1 < size){ + av_log(NULL, remaining ? AV_LOG_ERROR : AV_LOG_DEBUG, "Truncating packet of size %d to %"PRId64"\n", size, remaining+1); + size= remaining+1; + } + } + return size; +} + /* * Read the data in sane-sized chunks and append to pkt. * Return the number of bytes read or an error. */ static int append_packet_chunked(AVIOContext *s, AVPacket *pkt, int size) { - int64_t chunk_size = size; int64_t orig_pos = pkt->pos; // av_grow_packet might reset pos int orig_size = pkt->size; - int ret = 0; + int ret; do { int prev_size = pkt->size; @@ -89,11 +168,13 @@ static int append_packet_chunked(AVIOContext *s, AVPacket *pkt, int size) * When the caller requests a lot of data, limit it to the amount left * in file or SANE_CHUNK_SIZE when it is not known */ - if (size > SANE_CHUNK_SIZE) { - int64_t filesize = avio_size(s) - avio_tell(s); - chunk_size = FFMAX(filesize, SANE_CHUNK_SIZE); + read_size = size; + if (read_size > SANE_CHUNK_SIZE/10) { + read_size = ffio_limit(s, read_size); + // If filesize/maxsize is unknown, limit to SANE_CHUNK_SIZE + if (s->maxsize < 0) + read_size = FFMIN(read_size, SANE_CHUNK_SIZE); } - read_size = FFMIN(size, chunk_size); ret = av_grow_packet(pkt, read_size); if (ret < 0) @@ -107,6 +188,8 @@ static int append_packet_chunked(AVIOContext *s, AVPacket *pkt, int size) size -= read_size; } while (size > 0); + if (size > 0) + pkt->flags |= AV_PKT_FLAG_CORRUPT; pkt->pos = orig_pos; if (!pkt->size) @@ -138,19 +221,23 @@ int av_filename_number_test(const char *filename) return filename && (av_get_frame_filename(buf, sizeof(buf), filename, 1)>=0); } -AVInputFormat *av_probe_input_format2(AVProbeData *pd, int is_opened, int *score_max) +AVInputFormat *av_probe_input_format3(AVProbeData *pd, int is_opened, int *score_ret) { AVProbeData lpd = *pd; AVInputFormat *fmt1 = NULL, *fmt; - int score, id3 = 0; + int score, nodat = 0, score_max=0; + const static uint8_t zerobuffer[AVPROBE_PADDING_SIZE]; + + if (!lpd.buf) + lpd.buf = zerobuffer; if (lpd.buf_size > 10 && ff_id3v2_match(lpd.buf, ID3v2_DEFAULT_MAGIC)) { int id3len = ff_id3v2_tag_len(lpd.buf); if (lpd.buf_size > id3len + 16) { lpd.buf += id3len; lpd.buf_size -= id3len; - } - id3 = 1; + }else + nodat = 1; } fmt = NULL; @@ -160,44 +247,43 @@ AVInputFormat *av_probe_input_format2(AVProbeData *pd, int is_opened, int *score score = 0; if (fmt1->read_probe) { score = fmt1->read_probe(&lpd); + if(fmt1->extensions && av_match_ext(lpd.filename, fmt1->extensions)) + score = FFMAX(score, nodat ? AVPROBE_SCORE_EXTENSION / 2 - 1 : 1); } else if (fmt1->extensions) { if (av_match_ext(lpd.filename, fmt1->extensions)) { score = AVPROBE_SCORE_EXTENSION; } } - if (score > *score_max) { - *score_max = score; + if (score > score_max) { + score_max = score; fmt = fmt1; - }else if (score == *score_max) + }else if (score == score_max) fmt = NULL; } - - /* a hack for files with huge id3v2 tags -- try to guess by file extension. */ - if (!fmt && is_opened && *score_max < AVPROBE_SCORE_EXTENSION / 2) { - while ((fmt = av_iformat_next(fmt))) - if (fmt->extensions && av_match_ext(lpd.filename, fmt->extensions)) { - *score_max = AVPROBE_SCORE_EXTENSION / 2; - break; - } - } - - if (!fmt && id3 && *score_max < AVPROBE_SCORE_EXTENSION / 2 - 1) { - while ((fmt = av_iformat_next(fmt))) - if (fmt->extensions && av_match_ext("mp3", fmt->extensions)) { - *score_max = AVPROBE_SCORE_EXTENSION / 2 - 1; - break; - } - } + if(nodat) + score_max = FFMIN(AVPROBE_SCORE_EXTENSION / 2 - 1, score_max); + *score_ret= score_max; return fmt; } +AVInputFormat *av_probe_input_format2(AVProbeData *pd, int is_opened, int *score_max) +{ + int score_ret; + AVInputFormat *fmt= av_probe_input_format3(pd, is_opened, &score_ret); + if(score_ret > *score_max){ + *score_max= score_ret; + return fmt; + }else + return NULL; +} + AVInputFormat *av_probe_input_format(AVProbeData *pd, int is_opened){ int score=0; return av_probe_input_format2(pd, is_opened, &score); } -static int set_codec_from_probe_data(AVFormatContext *s, AVStream *st, AVProbeData *pd, int score) +static int set_codec_from_probe_data(AVFormatContext *s, AVStream *st, AVProbeData *pd) { static const struct { const char *name; enum AVCodecID id; enum AVMediaType type; @@ -207,14 +293,16 @@ static int set_codec_from_probe_data(AVFormatContext *s, AVStream *st, AVProbeDa { "dts" , AV_CODEC_ID_DTS , AVMEDIA_TYPE_AUDIO }, { "eac3" , AV_CODEC_ID_EAC3 , AVMEDIA_TYPE_AUDIO }, { "h264" , AV_CODEC_ID_H264 , AVMEDIA_TYPE_VIDEO }, + { "loas" , AV_CODEC_ID_AAC_LATM , AVMEDIA_TYPE_AUDIO }, { "m4v" , AV_CODEC_ID_MPEG4 , AVMEDIA_TYPE_VIDEO }, { "mp3" , AV_CODEC_ID_MP3 , AVMEDIA_TYPE_AUDIO }, { "mpegvideo", AV_CODEC_ID_MPEG2VIDEO, AVMEDIA_TYPE_VIDEO }, { 0 } }; - AVInputFormat *fmt = av_probe_input_format2(pd, 1, &score); + int score; + AVInputFormat *fmt = av_probe_input_format3(pd, 1, &score); - if (fmt) { + if (fmt && st->request_probe <= score) { int i; av_log(s, AV_LOG_DEBUG, "Probe with size=%d, packets=%d detected %s with score=%d\n", pd->buf_size, MAX_PROBE_PACKETS - st->probe_packets, fmt->name, score); @@ -226,29 +314,45 @@ static int set_codec_from_probe_data(AVFormatContext *s, AVStream *st, AVProbeDa } } } - return !!fmt; + return score; } /************************************************************/ /* input media file */ -/** size of probe buffer, for guessing file type from file contents */ -#define PROBE_BUF_MIN 2048 -#define PROBE_BUF_MAX (1<<20) +int av_demuxer_open(AVFormatContext *ic){ + int err; -int av_probe_input_buffer(AVIOContext *pb, AVInputFormat **fmt, + if (ic->iformat->read_header) { + err = ic->iformat->read_header(ic); + if (err < 0) + return err; + } + + if (ic->pb && !ic->data_offset) + ic->data_offset = avio_tell(ic->pb); + + return 0; +} + + +int av_probe_input_buffer2(AVIOContext *pb, AVInputFormat **fmt, const char *filename, void *logctx, unsigned int offset, unsigned int max_probe_size) { AVProbeData pd = { filename ? filename : "", NULL, -offset }; unsigned char *buf = NULL; - int ret = 0, probe_size; + uint8_t *mime_type; + int ret = 0, probe_size, buf_offset = 0; + int score = 0; if (!max_probe_size) { max_probe_size = PROBE_BUF_MAX; } else if (max_probe_size > PROBE_BUF_MAX) { max_probe_size = PROBE_BUF_MAX; } else if (max_probe_size < PROBE_BUF_MIN) { + av_log(logctx, AV_LOG_ERROR, + "Specified probe size value %u cannot be < %u\n", max_probe_size, PROBE_BUF_MIN); return AVERROR(EINVAL); } @@ -256,14 +360,20 @@ int av_probe_input_buffer(AVIOContext *pb, AVInputFormat **fmt, return AVERROR(EINVAL); } + if (!*fmt && pb->av_class && av_opt_get(pb, "mime_type", AV_OPT_SEARCH_CHILDREN, &mime_type) >= 0 && mime_type) { + if (!av_strcasecmp(mime_type, "audio/aacp")) { + *fmt = av_find_input_format("aac"); + } + av_freep(&mime_type); + } + for(probe_size= PROBE_BUF_MIN; probe_size<=max_probe_size && !*fmt; probe_size = FFMIN(probe_size<<1, FFMAX(max_probe_size, probe_size+1))) { - int score = probe_size < max_probe_size ? AVPROBE_SCORE_MAX/4 : 0; - int buf_offset = (probe_size == PROBE_BUF_MIN) ? 0 : probe_size>>1; if (probe_size < offset) { continue; } + score = probe_size < max_probe_size ? AVPROBE_SCORE_RETRY : 0; /* read probe data */ if ((ret = av_reallocp(&buf, probe_size + AVPROBE_PADDING_SIZE)) < 0) @@ -277,7 +387,7 @@ int av_probe_input_buffer(AVIOContext *pb, AVInputFormat **fmt, score = 0; ret = 0; /* error was end of file, nothing read */ } - pd.buf_size += ret; + pd.buf_size = buf_offset += ret; pd.buf = &buf[offset]; memset(pd.buf + pd.buf_size, 0, AVPROBE_PADDING_SIZE); @@ -285,10 +395,10 @@ int av_probe_input_buffer(AVIOContext *pb, AVInputFormat **fmt, /* guess file format */ *fmt = av_probe_input_format2(&pd, 1, &score); if(*fmt){ - if(score <= AVPROBE_SCORE_MAX/4){ //this can only be true in the last iteration - av_log(logctx, AV_LOG_WARNING, "Format detected only with low score of %d, misdetection possible!\n", score); + if(score <= AVPROBE_SCORE_RETRY){ //this can only be true in the last iteration + av_log(logctx, AV_LOG_WARNING, "Format %s detected only with low score of %d, misdetection possible!\n", (*fmt)->name, score); }else - av_log(logctx, AV_LOG_DEBUG, "Probed with size=%d and score=%d\n", probe_size, score); + av_log(logctx, AV_LOG_DEBUG, "Format %s probed with size=%d and score=%d\n", (*fmt)->name, probe_size, score); } } @@ -298,37 +408,47 @@ int av_probe_input_buffer(AVIOContext *pb, AVInputFormat **fmt, } /* rewind. reuse probe buffer to avoid seeking */ - if ((ret = ffio_rewind_with_probe_data(pb, buf, pd.buf_size)) < 0) - av_free(buf); + ret = ffio_rewind_with_probe_data(pb, &buf, pd.buf_size); - return ret; + return ret < 0 ? ret : score; +} + +int av_probe_input_buffer(AVIOContext *pb, AVInputFormat **fmt, + const char *filename, void *logctx, + unsigned int offset, unsigned int max_probe_size) +{ + int ret = av_probe_input_buffer2(pb, fmt, filename, logctx, offset, max_probe_size); + return ret < 0 ? ret : 0; } + /* open input file and probe the format if necessary */ static int init_input(AVFormatContext *s, const char *filename, AVDictionary **options) { int ret; AVProbeData pd = {filename, NULL, 0}; + int score = AVPROBE_SCORE_RETRY; if (s->pb) { s->flags |= AVFMT_FLAG_CUSTOM_IO; if (!s->iformat) - return av_probe_input_buffer(s->pb, &s->iformat, filename, s, 0, s->probesize); + return av_probe_input_buffer2(s->pb, &s->iformat, filename, s, 0, s->probesize); else if (s->iformat->flags & AVFMT_NOFILE) - return AVERROR(EINVAL); + av_log(s, AV_LOG_WARNING, "Custom AVIOContext makes no sense and " + "will be ignored with AVFMT_NOFILE format.\n"); return 0; } if ( (s->iformat && s->iformat->flags & AVFMT_NOFILE) || - (!s->iformat && (s->iformat = av_probe_input_format(&pd, 0)))) - return 0; + (!s->iformat && (s->iformat = av_probe_input_format2(&pd, 0, &score)))) + return score; - if ((ret = avio_open2(&s->pb, filename, AVIO_FLAG_READ, + if ((ret = avio_open2(&s->pb, filename, AVIO_FLAG_READ | s->avio_flags, &s->interrupt_callback, options)) < 0) return ret; if (s->iformat) return 0; - return av_probe_input_buffer(s->pb, &s->iformat, filename, s, 0, s->probesize); + return av_probe_input_buffer2(s->pb, &s->iformat, filename, s, 0, s->probesize); } static AVPacket *add_to_pktbuf(AVPacketList **packet_buffer, AVPacket *pkt, @@ -348,7 +468,7 @@ static AVPacket *add_to_pktbuf(AVPacketList **packet_buffer, AVPacket *pkt, return &pktl->pkt; } -static int queue_attached_pictures(AVFormatContext *s) +int avformat_queue_attached_pictures(AVFormatContext *s) { int i; for (i = 0; i < s->nb_streams; i++) @@ -373,6 +493,10 @@ int avformat_open_input(AVFormatContext **ps, const char *filename, AVInputForma if (!s && !(s = avformat_alloc_context())) return AVERROR(ENOMEM); + if (!s->av_class){ + av_log(NULL, AV_LOG_ERROR, "Input context has not been properly allocated by avformat_alloc_context() and is not NULL either\n"); + return AVERROR(EINVAL); + } if (fmt) s->iformat = fmt; @@ -384,6 +508,8 @@ int avformat_open_input(AVFormatContext **ps, const char *filename, AVInputForma if ((ret = init_input(s, filename, &tmp)) < 0) goto fail; + s->probe_score = ret; + avio_skip(s->pb, s->skip_initial_bytes); /* check filename in case an image number is expected */ if (s->iformat->flags & AVFMT_NEEDNUMBER) { @@ -414,19 +540,24 @@ int avformat_open_input(AVFormatContext **ps, const char *filename, AVInputForma if (s->pb) ff_id3v2_read(s, ID3v2_DEFAULT_MAGIC, &id3v2_extra_meta); - if (s->iformat->read_header) + if (!(s->flags&AVFMT_FLAG_PRIV_OPT) && s->iformat->read_header) if ((ret = s->iformat->read_header(s)) < 0) goto fail; - if (id3v2_extra_meta && - (ret = ff_id3v2_parse_apic(s, &id3v2_extra_meta)) < 0) - goto fail; + if (id3v2_extra_meta) { + if (!strcmp(s->iformat->name, "mp3") || !strcmp(s->iformat->name, "aac") || + !strcmp(s->iformat->name, "tta")) { + if((ret = ff_id3v2_parse_apic(s, &id3v2_extra_meta)) < 0) + goto fail; + } else + av_log(s, AV_LOG_DEBUG, "demuxer does not support additional id3 data, skipping\n"); + } ff_id3v2_free_extra_meta(&id3v2_extra_meta); - if ((ret = queue_attached_pictures(s)) < 0) + if ((ret = avformat_queue_attached_pictures(s)) < 0) goto fail; - if (s->pb && !s->data_offset) + if (!(s->flags&AVFMT_FLAG_PRIV_OPT) && s->pb && !s->data_offset) s->data_offset = avio_tell(s->pb); s->raw_packet_buffer_remaining_size = RAW_PACKET_BUFFER_SIZE; @@ -450,38 +581,66 @@ fail: /*******************************************************/ +static void force_codec_ids(AVFormatContext *s, AVStream *st) +{ + switch(st->codec->codec_type){ + case AVMEDIA_TYPE_VIDEO: + if(s->video_codec_id) st->codec->codec_id= s->video_codec_id; + break; + case AVMEDIA_TYPE_AUDIO: + if(s->audio_codec_id) st->codec->codec_id= s->audio_codec_id; + break; + case AVMEDIA_TYPE_SUBTITLE: + if(s->subtitle_codec_id)st->codec->codec_id= s->subtitle_codec_id; + break; + } +} + static int probe_codec(AVFormatContext *s, AVStream *st, const AVPacket *pkt) { - if(st->codec->codec_id == AV_CODEC_ID_PROBE){ + if(st->request_probe>0){ AVProbeData *pd = &st->probe_data; - av_log(s, AV_LOG_DEBUG, "probing stream %d\n", st->index); + int end; + av_log(s, AV_LOG_DEBUG, "probing stream %d pp:%d\n", st->index, st->probe_packets); --st->probe_packets; if (pkt) { - int err; - if ((err = av_reallocp(&pd->buf, pd->buf_size + pkt->size + - AVPROBE_PADDING_SIZE)) < 0) - return err; + uint8_t *new_buf = av_realloc(pd->buf, pd->buf_size+pkt->size+AVPROBE_PADDING_SIZE); + if(!new_buf) { + av_log(s, AV_LOG_WARNING, + "Failed to reallocate probe buffer for stream %d\n", + st->index); + goto no_packet; + } + pd->buf = new_buf; memcpy(pd->buf+pd->buf_size, pkt->data, pkt->size); pd->buf_size += pkt->size; memset(pd->buf+pd->buf_size, 0, AVPROBE_PADDING_SIZE); } else { +no_packet: st->probe_packets = 0; if (!pd->buf_size) { - av_log(s, AV_LOG_ERROR, "nothing to probe for stream %d\n", + av_log(s, AV_LOG_WARNING, "nothing to probe for stream %d\n", st->index); - return 0; } } - if (!st->probe_packets || - av_log2(pd->buf_size) != av_log2(pd->buf_size - pkt->size)) { - set_codec_from_probe_data(s, st, pd, st->probe_packets > 0 ? AVPROBE_SCORE_MAX/4 : 0); - if(st->codec->codec_id != AV_CODEC_ID_PROBE){ + end= s->raw_packet_buffer_remaining_size <= 0 + || st->probe_packets<=0; + + if(end || av_log2(pd->buf_size) != av_log2(pd->buf_size - pkt->size)){ + int score= set_codec_from_probe_data(s, st, pd); + if( (st->codec->codec_id != AV_CODEC_ID_NONE && score > AVPROBE_SCORE_RETRY) + || end){ pd->buf_size=0; av_freep(&pd->buf); - av_log(s, AV_LOG_DEBUG, "probed stream %d\n", st->index); + st->request_probe= -1; + if(st->codec->codec_id != AV_CODEC_ID_NONE){ + av_log(s, AV_LOG_DEBUG, "probed stream %d\n", st->index); + }else + av_log(s, AV_LOG_WARNING, "probed stream %d failed\n", st->index); } + force_codec_ids(s, st); } } return 0; @@ -498,16 +657,11 @@ int ff_read_packet(AVFormatContext *s, AVPacket *pkt) if (pktl) { *pkt = pktl->pkt; st = s->streams[pkt->stream_index]; - if (st->codec->codec_id != AV_CODEC_ID_PROBE || !st->probe_packets || - s->raw_packet_buffer_remaining_size < pkt->size) { - AVProbeData *pd; - if (st->probe_packets) { - if ((err = probe_codec(s, st, NULL)) < 0) - return err; - } - pd = &st->probe_data; - av_freep(&pd->buf); - pd->buf_size = 0; + if (s->raw_packet_buffer_remaining_size <= 0) { + if ((err = probe_codec(s, st, NULL)) < 0) + return err; + } + if(st->request_probe <= 0){ s->raw_packet_buffer = pktl->next; s->raw_packet_buffer_remaining_size += pkt->size; av_free(pktl); @@ -528,6 +682,7 @@ int ff_read_packet(AVFormatContext *s, AVPacket *pkt) if ((err = probe_codec(s, st, NULL)) < 0) return err; } + av_assert0(st->request_probe <= 0); } continue; } @@ -541,22 +696,25 @@ int ff_read_packet(AVFormatContext *s, AVPacket *pkt) continue; } - st= s->streams[pkt->stream_index]; + if(!(s->flags & AVFMT_FLAG_KEEP_SIDE_DATA)) + av_packet_merge_side_data(pkt); - switch(st->codec->codec_type){ - case AVMEDIA_TYPE_VIDEO: - if(s->video_codec_id) st->codec->codec_id= s->video_codec_id; - break; - case AVMEDIA_TYPE_AUDIO: - if(s->audio_codec_id) st->codec->codec_id= s->audio_codec_id; - break; - case AVMEDIA_TYPE_SUBTITLE: - if(s->subtitle_codec_id)st->codec->codec_id= s->subtitle_codec_id; - break; + if(pkt->stream_index >= (unsigned)s->nb_streams){ + av_log(s, AV_LOG_ERROR, "Invalid stream index %d\n", pkt->stream_index); + continue; } - if(!pktl && (st->codec->codec_id != AV_CODEC_ID_PROBE || - !st->probe_packets)) + st= s->streams[pkt->stream_index]; + pkt->dts = wrap_timestamp(st, pkt->dts); + pkt->pts = wrap_timestamp(st, pkt->pts); + + force_codec_ids(s, st); + + /* TODO: audio: time filter; video: frame reordering (pts != dts) */ + if (s->use_wallclock_as_timestamps) + pkt->dts = pkt->pts = av_rescale_q(av_gettime(), AV_TIME_BASE_Q, st->time_base); + + if(!pktl && st->request_probe <= 0) return ret; add_to_pktbuf(&s->raw_packet_buffer, pkt, &s->raw_packet_buffer_end); @@ -567,8 +725,27 @@ int ff_read_packet(AVFormatContext *s, AVPacket *pkt) } } +#if FF_API_READ_PACKET +int av_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + return ff_read_packet(s, pkt); +} +#endif + + /**********************************************************/ +static int determinable_frame_size(AVCodecContext *avctx) +{ + if (/*avctx->codec_id == AV_CODEC_ID_AAC ||*/ + avctx->codec_id == AV_CODEC_ID_MP1 || + avctx->codec_id == AV_CODEC_ID_MP2 || + avctx->codec_id == AV_CODEC_ID_MP3/* || + avctx->codec_id == AV_CODEC_ID_CELT*/) + return 1; + return 0; +} + /** * Get the number of samples of an audio frame. Return -1 on error. */ @@ -587,6 +764,13 @@ int ff_get_audio_frame_size(AVCodecContext *enc, int size, int mux) if (enc->frame_size > 1) return enc->frame_size; + //For WMA we currently have no other means to calculate duration thus we + //do it here by assuming CBR, which is true for all known cases. + if(!mux && enc->bit_rate>0 && size>0 && enc->sample_rate>0 && enc->block_align>1) { + if (enc->codec_id == AV_CODEC_ID_WMAV1 || enc->codec_id == AV_CODEC_ID_WMAV2) + return ((int64_t)size * 8 * enc->sample_rate) / enc->bit_rate; + } + return -1; } @@ -603,9 +787,9 @@ void ff_compute_frame_duration(int *pnum, int *pden, AVStream *st, *pden = 0; switch(st->codec->codec_type) { case AVMEDIA_TYPE_VIDEO: - if (st->avg_frame_rate.num) { - *pnum = st->avg_frame_rate.den; - *pden = st->avg_frame_rate.num; + if (st->r_frame_rate.num && !pc) { + *pnum = st->r_frame_rate.den; + *pden = st->r_frame_rate.num; } else if(st->time_base.num*1000LL > st->time_base.den) { *pnum = st->time_base.num; *pden = st->time_base.den; @@ -637,41 +821,161 @@ void ff_compute_frame_duration(int *pnum, int *pden, AVStream *st, } } -static int is_intra_only(enum AVCodecID id) +static int is_intra_only(AVCodecContext *enc){ + const AVCodecDescriptor *desc; + + if(enc->codec_type != AVMEDIA_TYPE_VIDEO) + return 1; + + desc = av_codec_get_codec_descriptor(enc); + if (!desc) { + desc = avcodec_descriptor_get(enc->codec_id); + av_codec_set_codec_descriptor(enc, desc); + } + if (desc) + return !!(desc->props & AV_CODEC_PROP_INTRA_ONLY); + return 0; +} + +static int has_decode_delay_been_guessed(AVStream *st) { - const AVCodecDescriptor *d = avcodec_descriptor_get(id); - if (!d) - return 0; - if (d->type == AVMEDIA_TYPE_VIDEO && !(d->props & AV_CODEC_PROP_INTRA_ONLY)) - return 0; - return 1; + if(st->codec->codec_id != AV_CODEC_ID_H264) return 1; + if(!st->info) // if we have left find_stream_info then nb_decoded_frames won't increase anymore for stream copy + return 1; +#if CONFIG_H264_DECODER + if(st->codec->has_b_frames && + avpriv_h264_has_num_reorder_frames(st->codec) == st->codec->has_b_frames) + return 1; +#endif + if(st->codec->has_b_frames<3) + return st->nb_decoded_frames >= 7; + else if(st->codec->has_b_frames<4) + return st->nb_decoded_frames >= 18; + else + return st->nb_decoded_frames >= 20; +} + +static AVPacketList *get_next_pkt(AVFormatContext *s, AVStream *st, AVPacketList *pktl) +{ + if (pktl->next) + return pktl->next; + if (pktl == s->parse_queue_end) + return s->packet_buffer; + return NULL; +} + +static int update_wrap_reference(AVFormatContext *s, AVStream *st, int stream_index) +{ + if (s->correct_ts_overflow && st->pts_wrap_bits < 63 && + st->pts_wrap_reference == AV_NOPTS_VALUE && st->first_dts != AV_NOPTS_VALUE) { + int i; + + // reference time stamp should be 60 s before first time stamp + int64_t pts_wrap_reference = st->first_dts - av_rescale(60, st->time_base.den, st->time_base.num); + // if first time stamp is not more than 1/8 and 60s before the wrap point, subtract rather than add wrap offset + int pts_wrap_behavior = (st->first_dts < (1LL<<st->pts_wrap_bits) - (1LL<<st->pts_wrap_bits-3)) || + (st->first_dts < (1LL<<st->pts_wrap_bits) - av_rescale(60, st->time_base.den, st->time_base.num)) ? + AV_PTS_WRAP_ADD_OFFSET : AV_PTS_WRAP_SUB_OFFSET; + + AVProgram *first_program = av_find_program_from_stream(s, NULL, stream_index); + + if (!first_program) { + int default_stream_index = av_find_default_stream_index(s); + if (s->streams[default_stream_index]->pts_wrap_reference == AV_NOPTS_VALUE) { + for (i=0; i<s->nb_streams; i++) { + s->streams[i]->pts_wrap_reference = pts_wrap_reference; + s->streams[i]->pts_wrap_behavior = pts_wrap_behavior; + } + } + else { + st->pts_wrap_reference = s->streams[default_stream_index]->pts_wrap_reference; + st->pts_wrap_behavior = s->streams[default_stream_index]->pts_wrap_behavior; + } + } + else { + AVProgram *program = first_program; + while (program) { + if (program->pts_wrap_reference != AV_NOPTS_VALUE) { + pts_wrap_reference = program->pts_wrap_reference; + pts_wrap_behavior = program->pts_wrap_behavior; + break; + } + program = av_find_program_from_stream(s, program, stream_index); + } + + // update every program with differing pts_wrap_reference + program = first_program; + while(program) { + if (program->pts_wrap_reference != pts_wrap_reference) { + for (i=0; i<program->nb_stream_indexes; i++) { + s->streams[program->stream_index[i]]->pts_wrap_reference = pts_wrap_reference; + s->streams[program->stream_index[i]]->pts_wrap_behavior = pts_wrap_behavior; + } + + program->pts_wrap_reference = pts_wrap_reference; + program->pts_wrap_behavior = pts_wrap_behavior; + } + program = av_find_program_from_stream(s, program, stream_index); + } + } + return 1; + } + return 0; } static void update_initial_timestamps(AVFormatContext *s, int stream_index, - int64_t dts, int64_t pts) + int64_t dts, int64_t pts, AVPacket *pkt) { AVStream *st= s->streams[stream_index]; - AVPacketList *pktl= s->packet_buffer; + AVPacketList *pktl= s->parse_queue ? s->parse_queue : s->packet_buffer; + int64_t pts_buffer[MAX_REORDER_DELAY+1]; + int64_t shift; + int i, delay; - if(st->first_dts != AV_NOPTS_VALUE || dts == AV_NOPTS_VALUE || st->cur_dts == AV_NOPTS_VALUE) + if(st->first_dts != AV_NOPTS_VALUE || dts == AV_NOPTS_VALUE || st->cur_dts == AV_NOPTS_VALUE || is_relative(dts)) return; - st->first_dts= dts - st->cur_dts; + delay = st->codec->has_b_frames; + st->first_dts= dts - (st->cur_dts - RELATIVE_TS_BASE); st->cur_dts= dts; + shift = st->first_dts - RELATIVE_TS_BASE; - for(; pktl; pktl= pktl->next){ + for (i=0; i<MAX_REORDER_DELAY+1; i++) + pts_buffer[i] = AV_NOPTS_VALUE; + + if (is_relative(pts)) + pts += shift; + + for(; pktl; pktl= get_next_pkt(s, st, pktl)){ if(pktl->pkt.stream_index != stream_index) continue; - //FIXME think more about this check - if(pktl->pkt.pts != AV_NOPTS_VALUE && pktl->pkt.pts == pktl->pkt.dts) - pktl->pkt.pts += st->first_dts; + if(is_relative(pktl->pkt.pts)) + pktl->pkt.pts += shift; - if(pktl->pkt.dts != AV_NOPTS_VALUE) - pktl->pkt.dts += st->first_dts; + if(is_relative(pktl->pkt.dts)) + pktl->pkt.dts += shift; if(st->start_time == AV_NOPTS_VALUE && pktl->pkt.pts != AV_NOPTS_VALUE) st->start_time= pktl->pkt.pts; + + if(pktl->pkt.pts != AV_NOPTS_VALUE && delay <= MAX_REORDER_DELAY && has_decode_delay_been_guessed(st)){ + pts_buffer[0]= pktl->pkt.pts; + for(i=0; i<delay && pts_buffer[i] > pts_buffer[i+1]; i++) + FFSWAP(int64_t, pts_buffer[i], pts_buffer[i+1]); + if(pktl->pkt.dts == AV_NOPTS_VALUE) + pktl->pkt.dts= pts_buffer[0]; + } + } + + if (update_wrap_reference(s, st, stream_index) && st->pts_wrap_behavior == AV_PTS_WRAP_SUB_OFFSET) { + // correct first time stamps to negative values + st->first_dts = wrap_timestamp(st, st->first_dts); + st->cur_dts = wrap_timestamp(st, st->cur_dts); + pkt->dts = wrap_timestamp(st, pkt->dts); + pkt->pts = wrap_timestamp(st, pkt->pts); + pts = wrap_timestamp(st, pts); } + if (st->start_time == AV_NOPTS_VALUE) st->start_time = pts; } @@ -679,38 +983,47 @@ static void update_initial_timestamps(AVFormatContext *s, int stream_index, static void update_initial_durations(AVFormatContext *s, AVStream *st, int stream_index, int duration) { - AVPacketList *pktl= s->packet_buffer; - int64_t cur_dts= 0; + AVPacketList *pktl= s->parse_queue ? s->parse_queue : s->packet_buffer; + int64_t cur_dts= RELATIVE_TS_BASE; if(st->first_dts != AV_NOPTS_VALUE){ cur_dts= st->first_dts; - for(; pktl; pktl= pktl->next){ + for(; pktl; pktl= get_next_pkt(s, st, pktl)){ if(pktl->pkt.stream_index == stream_index){ if(pktl->pkt.pts != pktl->pkt.dts || pktl->pkt.dts != AV_NOPTS_VALUE || pktl->pkt.duration) break; cur_dts -= duration; } } - pktl= s->packet_buffer; + if(pktl && pktl->pkt.dts != st->first_dts) { + av_log(s, AV_LOG_DEBUG, "first_dts %s not matching first dts %s (pts %s, duration %d) in the queue\n", + av_ts2str(st->first_dts), av_ts2str(pktl->pkt.dts), av_ts2str(pktl->pkt.pts), pktl->pkt.duration); + return; + } + if(!pktl) { + av_log(s, AV_LOG_DEBUG, "first_dts %s but no packet with dts in the queue\n", av_ts2str(st->first_dts)); + return; + } + pktl= s->parse_queue ? s->parse_queue : s->packet_buffer; st->first_dts = cur_dts; - }else if(st->cur_dts) + }else if(st->cur_dts != RELATIVE_TS_BASE) return; - for(; pktl; pktl= pktl->next){ + for(; pktl; pktl= get_next_pkt(s, st, pktl)){ if(pktl->pkt.stream_index != stream_index) continue; - if(pktl->pkt.pts == pktl->pkt.dts && pktl->pkt.dts == AV_NOPTS_VALUE + if(pktl->pkt.pts == pktl->pkt.dts && (pktl->pkt.dts == AV_NOPTS_VALUE || pktl->pkt.dts == st->first_dts) && !pktl->pkt.duration){ pktl->pkt.dts= cur_dts; if(!st->codec->has_b_frames) pktl->pkt.pts= cur_dts; - cur_dts += duration; - if (st->codec->codec_type != AVMEDIA_TYPE_AUDIO) +// if (st->codec->codec_type != AVMEDIA_TYPE_AUDIO) pktl->pkt.duration = duration; }else break; + cur_dts = pktl->pkt.dts + pktl->pkt.duration; } - if(st->first_dts == AV_NOPTS_VALUE) + if(!pktl) st->cur_dts= cur_dts; } @@ -726,6 +1039,10 @@ static void compute_pkt_fields(AVFormatContext *s, AVStream *st, if((s->flags & AVFMT_FLAG_IGNDTS) && pkt->pts != AV_NOPTS_VALUE) pkt->dts= AV_NOPTS_VALUE; + if (st->codec->codec_id != AV_CODEC_ID_H264 && pc && pc->pict_type == AV_PICTURE_TYPE_B) + //FIXME Set low_delay = 0 when has_b_frames = 1 + st->codec->has_b_frames = 1; + /* do we have a video B-frame ? */ delay= st->codec->has_b_frames; presentation_delayed = 0; @@ -739,26 +1056,29 @@ static void compute_pkt_fields(AVFormatContext *s, AVStream *st, if (pkt->pts != AV_NOPTS_VALUE && pkt->dts != AV_NOPTS_VALUE && st->pts_wrap_bits < 63 && pkt->dts - (1LL << (st->pts_wrap_bits - 1)) > pkt->pts) { - pkt->dts -= 1LL<<st->pts_wrap_bits; + if(is_relative(st->cur_dts) || pkt->dts - (1LL<<(st->pts_wrap_bits-1)) > st->cur_dts) { + pkt->dts -= 1LL<<st->pts_wrap_bits; + } else + pkt->pts += 1LL<<st->pts_wrap_bits; } // some mpeg2 in mpeg-ps lack dts (issue171 / input_file.mpg) // we take the conservative approach and discard both // Note, if this is misbehaving for a H.264 file then possibly presentation_delayed is not set correctly. if(delay==1 && pkt->dts == pkt->pts && pkt->dts != AV_NOPTS_VALUE && presentation_delayed){ - av_log(s, AV_LOG_DEBUG, "invalid dts/pts combination\n"); - pkt->dts= pkt->pts= AV_NOPTS_VALUE; + av_log(s, AV_LOG_DEBUG, "invalid dts/pts combination %"PRIi64"\n", pkt->dts); + if(strcmp(s->iformat->name, "mov,mp4,m4a,3gp,3g2,mj2")) // otherwise we discard correct timestamps for vc1-wmapro.ism + pkt->dts= AV_NOPTS_VALUE; } - if (pkt->duration == 0 && st->codec->codec_type != AVMEDIA_TYPE_AUDIO) { + if (pkt->duration == 0) { ff_compute_frame_duration(&num, &den, st, pc, pkt); if (den && num) { pkt->duration = av_rescale_rnd(1, num * (int64_t)st->time_base.den, den * (int64_t)st->time_base.num, AV_ROUND_DOWN); - - if(pkt->duration != 0 && s->packet_buffer) - update_initial_durations(s, st, pkt->stream_index, pkt->duration); } } + if(pkt->duration != 0 && (s->packet_buffer || s->parse_queue)) + update_initial_durations(s, st, pkt->stream_index, pkt->duration); /* correct timestamps with byte offset if demuxers only have timestamps on packet boundaries */ @@ -775,10 +1095,8 @@ static void compute_pkt_fields(AVFormatContext *s, AVStream *st, if(pkt->dts != AV_NOPTS_VALUE && pkt->pts != AV_NOPTS_VALUE && pkt->pts > pkt->dts) presentation_delayed = 1; - av_dlog(NULL, - "IN delayed:%d pts:%"PRId64", dts:%"PRId64" cur_dts:%"PRId64" st:%d pc:%p\n", - presentation_delayed, pkt->pts, pkt->dts, st->cur_dts, - pkt->stream_index, pc); + av_dlog(NULL, "IN delayed:%d pts:%s, dts:%s cur_dts:%s st:%d pc:%p duration:%d\n", + presentation_delayed, av_ts2str(pkt->pts), av_ts2str(pkt->dts), av_ts2str(st->cur_dts), pkt->stream_index, pc, pkt->duration); /* interpolate PTS and DTS if they are not present */ //We skip H264 currently because delay and has_b_frames are not reliably set if((delay==0 || (delay==1 && pc)) && st->codec->codec_id != AV_CODEC_ID_H264){ @@ -787,7 +1105,7 @@ static void compute_pkt_fields(AVFormatContext *s, AVStream *st, /* PTS = presentation timestamp */ if (pkt->dts == AV_NOPTS_VALUE) pkt->dts = st->last_IP_pts; - update_initial_timestamps(s, pkt->stream_index, pkt->dts, pkt->pts); + update_initial_timestamps(s, pkt->stream_index, pkt->dts, pkt->pts, pkt); if (pkt->dts == AV_NOPTS_VALUE) pkt->dts = st->cur_dts; @@ -803,57 +1121,40 @@ static void compute_pkt_fields(AVFormatContext *s, AVStream *st, by knowing the future */ } else if (pkt->pts != AV_NOPTS_VALUE || pkt->dts != AV_NOPTS_VALUE || - pkt->duration || - st->codec->codec_type == AVMEDIA_TYPE_AUDIO) { + pkt->duration ) { int duration = pkt->duration; - if (!duration && st->codec->codec_type == AVMEDIA_TYPE_AUDIO) { - ff_compute_frame_duration(&num, &den, st, pc, pkt); - if (den && num) { - duration = av_rescale_rnd(1, num * (int64_t)st->time_base.den, - den * (int64_t)st->time_base.num, - AV_ROUND_DOWN); - if (duration != 0 && s->packet_buffer) { - update_initial_durations(s, st, pkt->stream_index, - duration); - } - } - } - if (pkt->pts != AV_NOPTS_VALUE || pkt->dts != AV_NOPTS_VALUE || - duration) { - /* presentation is not delayed : PTS and DTS are the same */ - if (pkt->pts == AV_NOPTS_VALUE) - pkt->pts = pkt->dts; - update_initial_timestamps(s, pkt->stream_index, pkt->pts, - pkt->pts); - if (pkt->pts == AV_NOPTS_VALUE) - pkt->pts = st->cur_dts; - pkt->dts = pkt->pts; - if (pkt->pts != AV_NOPTS_VALUE) - st->cur_dts = pkt->pts + duration; - } + /* presentation is not delayed : PTS and DTS are the same */ + if (pkt->pts == AV_NOPTS_VALUE) + pkt->pts = pkt->dts; + update_initial_timestamps(s, pkt->stream_index, pkt->pts, + pkt->pts, pkt); + if (pkt->pts == AV_NOPTS_VALUE) + pkt->pts = st->cur_dts; + pkt->dts = pkt->pts; + if (pkt->pts != AV_NOPTS_VALUE) + st->cur_dts = pkt->pts + duration; } } - if(pkt->pts != AV_NOPTS_VALUE && delay <= MAX_REORDER_DELAY){ + if(pkt->pts != AV_NOPTS_VALUE && delay <= MAX_REORDER_DELAY && has_decode_delay_been_guessed(st)){ st->pts_buffer[0]= pkt->pts; for(i=0; i<delay && st->pts_buffer[i] > st->pts_buffer[i+1]; i++) FFSWAP(int64_t, st->pts_buffer[i], st->pts_buffer[i+1]); if(pkt->dts == AV_NOPTS_VALUE) pkt->dts= st->pts_buffer[0]; - if(st->codec->codec_id == AV_CODEC_ID_H264){ // we skipped it above so we try here - update_initial_timestamps(s, pkt->stream_index, pkt->dts, pkt->pts); // this should happen on the first packet - } - if(pkt->dts > st->cur_dts) - st->cur_dts = pkt->dts; } + if(st->codec->codec_id == AV_CODEC_ID_H264){ // we skipped it above so we try here + update_initial_timestamps(s, pkt->stream_index, pkt->dts, pkt->pts, pkt); // this should happen on the first packet + } + if(pkt->dts > st->cur_dts) + st->cur_dts = pkt->dts; - av_dlog(NULL, - "OUTdelayed:%d/%d pts:%"PRId64", dts:%"PRId64" cur_dts:%"PRId64"\n", - presentation_delayed, delay, pkt->pts, pkt->dts, st->cur_dts); + av_dlog(NULL, "OUTdelayed:%d/%d pts:%s, dts:%s cur_dts:%s\n", + presentation_delayed, delay, av_ts2str(pkt->pts), av_ts2str(pkt->dts), av_ts2str(st->cur_dts)); /* update flags */ - if (is_intra_only(st->codec->codec_id)) + if (is_intra_only(st->codec)) pkt->flags |= AV_PKT_FLAG_KEY; if (pc) pkt->convergence_duration = pc->convergence_duration; @@ -887,6 +1188,9 @@ static int parse_packet(AVFormatContext *s, AVPacket *pkt, int stream_index) av_init_packet(&flush_pkt); pkt = &flush_pkt; got_output = 1; + } else if (!size && st->parser->flags & PARSER_FLAG_COMPLETE_FRAMES) { + // preserve 0-size sync packets + compute_pkt_fields(s, st, st->parser, pkt); } while (size > 0 || (pkt == &flush_pkt && got_output)) { @@ -898,6 +1202,7 @@ static int parse_packet(AVFormatContext *s, AVPacket *pkt, int stream_index) pkt->pts, pkt->dts, pkt->pos); pkt->pts = pkt->dts = AV_NOPTS_VALUE; + pkt->pos = -1; /* increment read pointer */ data += len; size -= len; @@ -936,19 +1241,18 @@ static int parse_packet(AVFormatContext *s, AVPacket *pkt, int stream_index) out_pkt.dts = st->parser->dts; out_pkt.pos = st->parser->pos; + if(st->need_parsing == AVSTREAM_PARSE_FULL_RAW) + out_pkt.pos = st->parser->frame_offset; + if (st->parser->key_frame == 1 || (st->parser->key_frame == -1 && st->parser->pict_type == AV_PICTURE_TYPE_I)) out_pkt.flags |= AV_PKT_FLAG_KEY; - compute_pkt_fields(s, st, st->parser, &out_pkt); + if(st->parser->key_frame == -1 && st->parser->pict_type==AV_PICTURE_TYPE_NONE && (pkt->flags&AV_PKT_FLAG_KEY)) + out_pkt.flags |= AV_PKT_FLAG_KEY; - if ((s->iformat->flags & AVFMT_GENERIC_INDEX) && - out_pkt.flags & AV_PKT_FLAG_KEY) { - ff_reduce_index(s, st->index); - av_add_index_entry(st, st->parser->frame_offset, out_pkt.dts, - 0, 0, AVINDEX_KEYFRAME); - } + compute_pkt_fields(s, st, st->parser, &out_pkt); if (out_pkt.data == pkt->data && out_pkt.size == pkt->size) { out_pkt.buf = pkt->buf; @@ -1028,17 +1332,17 @@ static int read_frame_internal(AVFormatContext *s, AVPacket *pkt) if (cur_pkt.pts != AV_NOPTS_VALUE && cur_pkt.dts != AV_NOPTS_VALUE && cur_pkt.pts < cur_pkt.dts) { - av_log(s, AV_LOG_WARNING, "Invalid timestamps stream=%d, pts=%"PRId64", dts=%"PRId64", size=%d\n", + av_log(s, AV_LOG_WARNING, "Invalid timestamps stream=%d, pts=%s, dts=%s, size=%d\n", cur_pkt.stream_index, - cur_pkt.pts, - cur_pkt.dts, + av_ts2str(cur_pkt.pts), + av_ts2str(cur_pkt.dts), cur_pkt.size); } if (s->debug & FF_FDEBUG_TS) - av_log(s, AV_LOG_DEBUG, "ff_read_packet stream=%d, pts=%"PRId64", dts=%"PRId64", size=%d, duration=%d, flags=%d\n", + av_log(s, AV_LOG_DEBUG, "ff_read_packet stream=%d, pts=%s, dts=%s, size=%d, duration=%d, flags=%d\n", cur_pkt.stream_index, - cur_pkt.pts, - cur_pkt.dts, + av_ts2str(cur_pkt.pts), + av_ts2str(cur_pkt.dts), cur_pkt.size, cur_pkt.duration, cur_pkt.flags); @@ -1046,12 +1350,17 @@ static int read_frame_internal(AVFormatContext *s, AVPacket *pkt) if (st->need_parsing && !st->parser && !(s->flags & AVFMT_FLAG_NOPARSE)) { st->parser = av_parser_init(st->codec->codec_id); if (!st->parser) { + av_log(s, AV_LOG_VERBOSE, "parser not found for codec " + "%s, packets or times may be invalid.\n", + avcodec_get_name(st->codec->codec_id)); /* no parser available: just output the raw packets */ st->need_parsing = AVSTREAM_PARSE_NONE; } else if(st->need_parsing == AVSTREAM_PARSE_HEADERS) { st->parser->flags |= PARSER_FLAG_COMPLETE_FRAMES; } else if(st->need_parsing == AVSTREAM_PARSE_FULL_ONCE) { st->parser->flags |= PARSER_FLAG_ONCE; + } else if(st->need_parsing == AVSTREAM_PARSE_FULL_RAW) { + st->parser->flags |= PARSER_FLAG_USE_CODEC_TS; } } @@ -1072,16 +1381,25 @@ static int read_frame_internal(AVFormatContext *s, AVPacket *pkt) /* free packet */ av_free_packet(&cur_pkt); } + if (pkt->flags & AV_PKT_FLAG_KEY) + st->skip_to_keyframe = 0; + if (st->skip_to_keyframe) { + av_free_packet(&cur_pkt); + if (got_packet) { + *pkt = cur_pkt; + } + got_packet = 0; + } } if (!got_packet && s->parse_queue) ret = read_from_packet_buffer(&s->parse_queue, &s->parse_queue_end, pkt); if(s->debug & FF_FDEBUG_TS) - av_log(s, AV_LOG_DEBUG, "read_frame_internal stream=%d, pts=%"PRId64", dts=%"PRId64", size=%d, duration=%d, flags=%d\n", + av_log(s, AV_LOG_DEBUG, "read_frame_internal stream=%d, pts=%s, dts=%s, size=%d, duration=%d, flags=%d\n", pkt->stream_index, - pkt->pts, - pkt->dts, + av_ts2str(pkt->pts), + av_ts2str(pkt->dts), pkt->size, pkt->duration, pkt->flags); @@ -1093,15 +1411,19 @@ int av_read_frame(AVFormatContext *s, AVPacket *pkt) { const int genpts = s->flags & AVFMT_FLAG_GENPTS; int eof = 0; + int ret; + AVStream *st; - if (!genpts) - return s->packet_buffer ? read_from_packet_buffer(&s->packet_buffer, - &s->packet_buffer_end, - pkt) : - read_frame_internal(s, pkt); + if (!genpts) { + ret = s->packet_buffer ? + read_from_packet_buffer(&s->packet_buffer, &s->packet_buffer_end, pkt) : + read_frame_internal(s, pkt); + if (ret < 0) + return ret; + goto return_packet; + } for (;;) { - int ret; AVPacketList *pktl = s->packet_buffer; if (pktl) { @@ -1109,22 +1431,40 @@ int av_read_frame(AVFormatContext *s, AVPacket *pkt) if (next_pkt->dts != AV_NOPTS_VALUE) { int wrap_bits = s->streams[next_pkt->stream_index]->pts_wrap_bits; + // last dts seen for this stream. if any of packets following + // current one had no dts, we will set this to AV_NOPTS_VALUE. + int64_t last_dts = next_pkt->dts; while (pktl && next_pkt->pts == AV_NOPTS_VALUE) { if (pktl->pkt.stream_index == next_pkt->stream_index && - (av_compare_mod(next_pkt->dts, pktl->pkt.dts, 2LL << (wrap_bits - 1)) < 0) && - av_compare_mod(pktl->pkt.pts, pktl->pkt.dts, 2LL << (wrap_bits - 1))) { //not b frame - next_pkt->pts = pktl->pkt.dts; + (av_compare_mod(next_pkt->dts, pktl->pkt.dts, 2LL << (wrap_bits - 1)) < 0)) { + if (av_compare_mod(pktl->pkt.pts, pktl->pkt.dts, 2LL << (wrap_bits - 1))) { //not b frame + next_pkt->pts = pktl->pkt.dts; + } + if (last_dts != AV_NOPTS_VALUE) { + // Once last dts was set to AV_NOPTS_VALUE, we don't change it. + last_dts = pktl->pkt.dts; + } } pktl = pktl->next; } + if (eof && next_pkt->pts == AV_NOPTS_VALUE && last_dts != AV_NOPTS_VALUE) { + // Fixing the last reference frame had none pts issue (For MXF etc). + // We only do this when + // 1. eof. + // 2. we are not able to resolve a pts value for current packet. + // 3. the packets for this stream at the end of the files had valid dts. + next_pkt->pts = last_dts + next_pkt->duration; + } pktl = s->packet_buffer; } /* read packet from packet buffer, if there is data */ if (!(next_pkt->pts == AV_NOPTS_VALUE && - next_pkt->dts != AV_NOPTS_VALUE && !eof)) - return read_from_packet_buffer(&s->packet_buffer, + next_pkt->dts != AV_NOPTS_VALUE && !eof)) { + ret = read_from_packet_buffer(&s->packet_buffer, &s->packet_buffer_end, pkt); + goto return_packet; + } } ret = read_frame_internal(s, pkt); @@ -1140,6 +1480,30 @@ int av_read_frame(AVFormatContext *s, AVPacket *pkt) &s->packet_buffer_end)) < 0) return AVERROR(ENOMEM); } + +return_packet: + + st = s->streams[pkt->stream_index]; + if (st->skip_samples) { + uint8_t *p = av_packet_new_side_data(pkt, AV_PKT_DATA_SKIP_SAMPLES, 10); + if (p) { + AV_WL32(p, st->skip_samples); + av_log(s, AV_LOG_DEBUG, "demuxer injecting skip %d\n", st->skip_samples); + } + st->skip_samples = 0; + } + + if ((s->iformat->flags & AVFMT_GENERIC_INDEX) && pkt->flags & AV_PKT_FLAG_KEY) { + ff_reduce_index(s, st->index); + av_add_index_entry(st, pkt->pos, pkt->dts, 0, 0, AVINDEX_KEYFRAME); + } + + if (is_relative(pkt->dts)) + pkt->dts -= RELATIVE_TS_BASE; + if (is_relative(pkt->pts)) + pkt->pts -= RELATIVE_TS_BASE; + + return ret; } /* XXX: suppress the packet queue */ @@ -1194,7 +1558,8 @@ void ff_read_frame_flush(AVFormatContext *s) st->parser = NULL; } st->last_IP_pts = AV_NOPTS_VALUE; - st->cur_dts = AV_NOPTS_VALUE; /* we set the current DTS to an unspecified origin */ + if(st->first_dts == AV_NOPTS_VALUE) st->cur_dts = RELATIVE_TS_BASE; + else st->cur_dts = AV_NOPTS_VALUE; /* we set the current DTS to an unspecified origin */ st->probe_packets = MAX_PROBE_PACKETS; @@ -1240,6 +1605,15 @@ int ff_add_index_entry(AVIndexEntry **index_entries, if((unsigned)*nb_index_entries + 1 >= UINT_MAX / sizeof(AVIndexEntry)) return -1; + if(timestamp == AV_NOPTS_VALUE) + return AVERROR(EINVAL); + + if (size < 0 || size > 0x3FFFFFFF) + return AVERROR(EINVAL); + + if (is_relative(timestamp)) //FIXME this maintains previous behavior but we should shift by the correct offset once known + timestamp -= RELATIVE_TS_BASE; + entries = av_fast_realloc(*index_entries, index_entries_allocated_size, (*nb_index_entries + 1) * @@ -1254,7 +1628,7 @@ int ff_add_index_entry(AVIndexEntry **index_entries, if(index<0){ index= (*nb_index_entries)++; ie= &entries[index]; - assert(index==0 || ie[-1].timestamp < timestamp); + av_assert0(index==0 || ie[-1].timestamp < timestamp); }else{ ie= &entries[index]; if(ie->timestamp != timestamp){ @@ -1278,6 +1652,7 @@ int ff_add_index_entry(AVIndexEntry **index_entries, int av_add_index_entry(AVStream *st, int64_t pos, int64_t timestamp, int size, int distance, int flags) { + timestamp = wrap_timestamp(st, timestamp); return ff_add_index_entry(&st->index_entries, &st->nb_index_entries, &st->index_entries_allocated_size, pos, timestamp, size, distance, flags); @@ -1324,6 +1699,15 @@ int av_index_search_timestamp(AVStream *st, int64_t wanted_timestamp, wanted_timestamp, flags); } +static int64_t ff_read_timestamp(AVFormatContext *s, int stream_index, int64_t *ppos, int64_t pos_limit, + int64_t (*read_timestamp)(struct AVFormatContext *, int , int64_t *, int64_t )) +{ + int64_t ts = read_timestamp(s, stream_index, ppos, pos_limit); + if (stream_index >= 0) + ts = wrap_timestamp(s->streams[stream_index], ts); + return ts; +} + int ff_seek_frame_binary(AVFormatContext *s, int stream_index, int64_t target_ts, int flags) { AVInputFormat *avif= s->iformat; @@ -1336,7 +1720,7 @@ int ff_seek_frame_binary(AVFormatContext *s, int stream_index, int64_t target_ts if (stream_index < 0) return -1; - av_dlog(s, "read_seek: %d %"PRId64"\n", stream_index, target_ts); + av_dlog(s, "read_seek: %d %s\n", stream_index, av_ts2str(target_ts)); ts_max= ts_min= AV_NOPTS_VALUE; @@ -1353,22 +1737,22 @@ int ff_seek_frame_binary(AVFormatContext *s, int stream_index, int64_t target_ts if(e->timestamp <= target_ts || e->pos == e->min_distance){ pos_min= e->pos; ts_min= e->timestamp; - av_dlog(s, "using cached pos_min=0x%"PRIx64" dts_min=%"PRId64"\n", - pos_min,ts_min); + av_dlog(s, "using cached pos_min=0x%"PRIx64" dts_min=%s\n", + pos_min, av_ts2str(ts_min)); }else{ - assert(index==0); + av_assert1(index==0); } index= av_index_search_timestamp(st, target_ts, flags & ~AVSEEK_FLAG_BACKWARD); - assert(index < st->nb_index_entries); + av_assert0(index < st->nb_index_entries); if(index >= 0){ e= &st->index_entries[index]; - assert(e->timestamp >= target_ts); + av_assert1(e->timestamp >= target_ts); pos_max= e->pos; ts_max= e->timestamp; pos_limit= pos_max - e->min_distance; - av_dlog(s, "using cached pos_max=0x%"PRIx64" pos_limit=0x%"PRIx64" dts_max=%"PRId64"\n", - pos_max,pos_limit, ts_max); + av_dlog(s, "using cached pos_max=0x%"PRIx64" pos_limit=0x%"PRIx64" dts_max=%s\n", + pos_max, pos_limit, av_ts2str(ts_max)); } } @@ -1380,54 +1764,83 @@ int ff_seek_frame_binary(AVFormatContext *s, int stream_index, int64_t target_ts if ((ret = avio_seek(s->pb, pos, SEEK_SET)) < 0) return ret; + ff_read_frame_flush(s); ff_update_cur_dts(s, st, ts); return 0; } +int ff_find_last_ts(AVFormatContext *s, int stream_index, int64_t *ts, int64_t *pos, + int64_t (*read_timestamp)(struct AVFormatContext *, int , int64_t *, int64_t )) +{ + int64_t step= 1024; + int64_t limit, ts_max; + int64_t filesize = avio_size(s->pb); + int64_t pos_max = filesize - 1; + do{ + limit = pos_max; + pos_max = FFMAX(0, (pos_max) - step); + ts_max = ff_read_timestamp(s, stream_index, &pos_max, limit, read_timestamp); + step += step; + }while(ts_max == AV_NOPTS_VALUE && 2*limit > step); + if (ts_max == AV_NOPTS_VALUE) + return -1; + + for(;;){ + int64_t tmp_pos = pos_max + 1; + int64_t tmp_ts = ff_read_timestamp(s, stream_index, &tmp_pos, INT64_MAX, read_timestamp); + if(tmp_ts == AV_NOPTS_VALUE) + break; + av_assert0(tmp_pos > pos_max); + ts_max = tmp_ts; + pos_max = tmp_pos; + if(tmp_pos >= filesize) + break; + } + + if (ts) + *ts = ts_max; + if (pos) + *pos = pos_max; + + return 0; +} + int64_t ff_gen_search(AVFormatContext *s, int stream_index, int64_t target_ts, int64_t pos_min, int64_t pos_max, int64_t pos_limit, int64_t ts_min, int64_t ts_max, int flags, int64_t *ts_ret, int64_t (*read_timestamp)(struct AVFormatContext *, int , int64_t *, int64_t )) { int64_t pos, ts; - int64_t start_pos, filesize; + int64_t start_pos; int no_change; + int ret; - av_dlog(s, "gen_seek: %d %"PRId64"\n", stream_index, target_ts); + av_dlog(s, "gen_seek: %d %s\n", stream_index, av_ts2str(target_ts)); if(ts_min == AV_NOPTS_VALUE){ pos_min = s->data_offset; - ts_min = read_timestamp(s, stream_index, &pos_min, INT64_MAX); + ts_min = ff_read_timestamp(s, stream_index, &pos_min, INT64_MAX, read_timestamp); if (ts_min == AV_NOPTS_VALUE) return -1; } - if(ts_max == AV_NOPTS_VALUE){ - int step= 1024; - filesize = avio_size(s->pb); - pos_max = filesize - 1; - do{ - pos_max -= step; - ts_max = read_timestamp(s, stream_index, &pos_max, pos_max + step); - step += step; - }while(ts_max == AV_NOPTS_VALUE && pos_max >= step); - if (ts_max == AV_NOPTS_VALUE) - return -1; + if(ts_min >= target_ts){ + *ts_ret= ts_min; + return pos_min; + } - for(;;){ - int64_t tmp_pos= pos_max + 1; - int64_t tmp_ts= read_timestamp(s, stream_index, &tmp_pos, INT64_MAX); - if(tmp_ts == AV_NOPTS_VALUE) - break; - ts_max= tmp_ts; - pos_max= tmp_pos; - if(tmp_pos >= filesize) - break; - } + if(ts_max == AV_NOPTS_VALUE){ + if ((ret = ff_find_last_ts(s, stream_index, &ts_max, &pos_max, read_timestamp)) < 0) + return ret; pos_limit= pos_max; } + if(ts_max <= target_ts){ + *ts_ret= ts_max; + return pos_max; + } + if(ts_min > ts_max){ return -1; }else if(ts_min == ts_max){ @@ -1436,8 +1849,8 @@ int64_t ff_gen_search(AVFormatContext *s, int stream_index, int64_t target_ts, no_change=0; while (pos_min < pos_limit) { - av_dlog(s, "pos_min=0x%"PRIx64" pos_max=0x%"PRIx64" dts_min=%"PRId64" dts_max=%"PRId64"\n", - pos_min, pos_max, ts_min, ts_max); + av_dlog(s, "pos_min=0x%"PRIx64" pos_max=0x%"PRIx64" dts_min=%s dts_max=%s\n", + pos_min, pos_max, av_ts2str(ts_min), av_ts2str(ts_max)); assert(pos_limit <= pos_max); if(no_change==0){ @@ -1459,13 +1872,14 @@ int64_t ff_gen_search(AVFormatContext *s, int stream_index, int64_t target_ts, pos= pos_limit; start_pos= pos; - ts = read_timestamp(s, stream_index, &pos, INT64_MAX); //may pass pos_limit instead of -1 + ts = ff_read_timestamp(s, stream_index, &pos, INT64_MAX, read_timestamp); //may pass pos_limit instead of -1 if(pos == pos_max) no_change++; else no_change=0; - av_dlog(s, "%"PRId64" %"PRId64" %"PRId64" / %"PRId64" %"PRId64" %"PRId64" target:%"PRId64" limit:%"PRId64" start:%"PRId64" noc:%d\n", - pos_min, pos, pos_max, ts_min, ts, ts_max, target_ts, + av_dlog(s, "%"PRId64" %"PRId64" %"PRId64" / %s %s %s target:%s limit:%"PRId64" start:%"PRId64" noc:%d\n", + pos_min, pos, pos_max, + av_ts2str(ts_min), av_ts2str(ts), av_ts2str(ts_max), av_ts2str(target_ts), pos_limit, start_pos, no_change); if(ts == AV_NOPTS_VALUE){ av_log(s, AV_LOG_ERROR, "read_timestamp() failed in the middle\n"); @@ -1485,12 +1899,14 @@ int64_t ff_gen_search(AVFormatContext *s, int stream_index, int64_t target_ts, pos = (flags & AVSEEK_FLAG_BACKWARD) ? pos_min : pos_max; ts = (flags & AVSEEK_FLAG_BACKWARD) ? ts_min : ts_max; +#if 0 pos_min = pos; - ts_min = read_timestamp(s, stream_index, &pos_min, INT64_MAX); + ts_min = ff_read_timestamp(s, stream_index, &pos_min, INT64_MAX, read_timestamp); pos_min++; - ts_max = read_timestamp(s, stream_index, &pos_min, INT64_MAX); - av_dlog(s, "pos=0x%"PRIx64" %"PRId64"<=%"PRId64"<=%"PRId64"\n", - pos, ts_min, target_ts, ts_max); + ts_max = ff_read_timestamp(s, stream_index, &pos_min, INT64_MAX, read_timestamp); + av_dlog(s, "pos=0x%"PRIx64" %s<=%s<=%s\n", + pos, av_ts2str(ts_min), av_ts2str(target_ts), av_ts2str(ts_max)); +#endif *ts_ret= ts; return pos; } @@ -1506,6 +1922,8 @@ static int seek_frame_byte(AVFormatContext *s, int stream_index, int64_t pos, in avio_seek(s->pb, pos, SEEK_SET); + s->io_repositioned = 1; + return 0; } @@ -1526,9 +1944,10 @@ static int seek_frame_generic(AVFormatContext *s, if(index < 0 || index==st->nb_index_entries-1){ AVPacket pkt; + int nonkey=0; if(st->nb_index_entries){ - assert(st->index_entries); + av_assert0(st->index_entries); ie= &st->index_entries[st->nb_index_entries-1]; if ((ret = avio_seek(s->pb, ie->pos, SEEK_SET)) < 0) return ret; @@ -1545,9 +1964,13 @@ static int seek_frame_generic(AVFormatContext *s, if (read_status < 0) break; av_free_packet(&pkt); - if(stream_index == pkt.stream_index){ - if((pkt.flags & AV_PKT_FLAG_KEY) && pkt.dts > timestamp) + if(stream_index == pkt.stream_index && pkt.dts > timestamp){ + if(pkt.flags & AV_PKT_FLAG_KEY) break; + if(nonkey++ > 1000 && st->codec->codec_id != AV_CODEC_ID_CDGRAPHICS){ + av_log(s, AV_LOG_ERROR,"seek_frame_generic failed as this stream seems to contain no keyframes after the target timestamp, %d non keyframes found\n", nonkey); + break; + } } } index = av_index_search_timestamp(st, timestamp, flags); @@ -1614,10 +2037,22 @@ static int seek_frame_internal(AVFormatContext *s, int stream_index, int av_seek_frame(AVFormatContext *s, int stream_index, int64_t timestamp, int flags) { - int ret = seek_frame_internal(s, stream_index, timestamp, flags); + int ret; + + if (s->iformat->read_seek2 && !s->iformat->read_seek) { + int64_t min_ts = INT64_MIN, max_ts = INT64_MAX; + if ((flags & AVSEEK_FLAG_BACKWARD)) + max_ts = timestamp; + else + min_ts = timestamp; + return avformat_seek_file(s, stream_index, min_ts, timestamp, max_ts, + flags & ~AVSEEK_FLAG_BACKWARD); + } + + ret = seek_frame_internal(s, stream_index, timestamp, flags); if (ret >= 0) - ret = queue_attached_pictures(s); + ret = avformat_queue_attached_pictures(s); return ret; } @@ -1626,14 +2061,32 @@ int avformat_seek_file(AVFormatContext *s, int stream_index, int64_t min_ts, int { if(min_ts > ts || max_ts < ts) return -1; + if (stream_index < -1 || stream_index >= (int)s->nb_streams) + return AVERROR(EINVAL); + + if(s->seek2any>0) + flags |= AVSEEK_FLAG_ANY; + flags &= ~AVSEEK_FLAG_BACKWARD; if (s->iformat->read_seek2) { int ret; ff_read_frame_flush(s); + + if (stream_index == -1 && s->nb_streams == 1) { + AVRational time_base = s->streams[0]->time_base; + ts = av_rescale_q(ts, AV_TIME_BASE_Q, time_base); + min_ts = av_rescale_rnd(min_ts, time_base.den, + time_base.num * (int64_t)AV_TIME_BASE, + AV_ROUND_UP | AV_ROUND_PASS_MINMAX); + max_ts = av_rescale_rnd(max_ts, time_base.den, + time_base.num * (int64_t)AV_TIME_BASE, + AV_ROUND_DOWN | AV_ROUND_PASS_MINMAX); + } + ret = s->iformat->read_seek2(s, stream_index, min_ts, ts, max_ts, flags); if (ret >= 0) - ret = queue_attached_pictures(s); + ret = avformat_queue_attached_pictures(s); return ret; } @@ -1643,10 +2096,19 @@ int avformat_seek_file(AVFormatContext *s, int stream_index, int64_t min_ts, int // Fall back on old API if new is not implemented but old is. // Note the old API has somewhat different semantics. - if(s->iformat->read_seek || 1) - return av_seek_frame(s, stream_index, ts, flags | ((uint64_t)ts - min_ts > (uint64_t)max_ts - ts ? AVSEEK_FLAG_BACKWARD : 0)); + if (s->iformat->read_seek || 1) { + int dir = (ts - (uint64_t)min_ts > (uint64_t)max_ts - ts ? AVSEEK_FLAG_BACKWARD : 0); + int ret = av_seek_frame(s, stream_index, ts, flags | dir); + if (ret<0 && ts != min_ts && max_ts != ts) { + ret = av_seek_frame(s, stream_index, dir ? max_ts : min_ts, flags | dir); + if (ret >= 0) + ret = av_seek_frame(s, stream_index, ts, flags | (dir^AVSEEK_FLAG_BACKWARD)); + } + return ret; + } // try some generic seek like seek_frame_generic() but with new ts semantics + return -1; //unreachable } /*******************************************************/ @@ -1678,43 +2140,71 @@ static int has_duration(AVFormatContext *ic) */ static void update_stream_timings(AVFormatContext *ic) { - int64_t start_time, start_time1, end_time, end_time1; + int64_t start_time, start_time1, start_time_text, end_time, end_time1; int64_t duration, duration1, filesize; int i; AVStream *st; + AVProgram *p; start_time = INT64_MAX; + start_time_text = INT64_MAX; end_time = INT64_MIN; duration = INT64_MIN; for(i = 0;i < ic->nb_streams; i++) { st = ic->streams[i]; if (st->start_time != AV_NOPTS_VALUE && st->time_base.den) { start_time1= av_rescale_q(st->start_time, st->time_base, AV_TIME_BASE_Q); - start_time = FFMIN(start_time, start_time1); + if (st->codec->codec_type == AVMEDIA_TYPE_SUBTITLE || st->codec->codec_type == AVMEDIA_TYPE_DATA) { + if (start_time1 < start_time_text) + start_time_text = start_time1; + } else + start_time = FFMIN(start_time, start_time1); + end_time1 = AV_NOPTS_VALUE; if (st->duration != AV_NOPTS_VALUE) { end_time1 = start_time1 + av_rescale_q(st->duration, st->time_base, AV_TIME_BASE_Q); end_time = FFMAX(end_time, end_time1); } + for(p = NULL; (p = av_find_program_from_stream(ic, p, i)); ){ + if(p->start_time == AV_NOPTS_VALUE || p->start_time > start_time1) + p->start_time = start_time1; + if(p->end_time < end_time1) + p->end_time = end_time1; + } } if (st->duration != AV_NOPTS_VALUE) { duration1 = av_rescale_q(st->duration, st->time_base, AV_TIME_BASE_Q); duration = FFMAX(duration, duration1); } } + if (start_time == INT64_MAX || (start_time > start_time_text && start_time - start_time_text < AV_TIME_BASE)) + start_time = start_time_text; + else if(start_time > start_time_text) + av_log(ic, AV_LOG_VERBOSE, "Ignoring outlier non primary stream starttime %f\n", start_time_text / (float)AV_TIME_BASE); + if (start_time != INT64_MAX) { ic->start_time = start_time; - if (end_time != INT64_MIN) - duration = FFMAX(duration, end_time - start_time); + if (end_time != INT64_MIN) { + if (ic->nb_programs) { + for (i=0; i<ic->nb_programs; i++) { + p = ic->programs[i]; + if(p->start_time != AV_NOPTS_VALUE && p->end_time > p->start_time) + duration = FFMAX(duration, p->end_time - p->start_time); + } + } else + duration = FFMAX(duration, end_time - start_time); + } } - if (duration != INT64_MIN) { + if (duration != INT64_MIN && duration > 0 && ic->duration == AV_NOPTS_VALUE) { ic->duration = duration; - if (ic->pb && (filesize = avio_size(ic->pb)) > 0) { + } + if (ic->pb && (filesize = avio_size(ic->pb)) > 0 && ic->duration != AV_NOPTS_VALUE) { /* compute the bitrate */ - ic->bit_rate = (double)filesize * 8.0 * AV_TIME_BASE / + double bitrate = (double)filesize * 8.0 * AV_TIME_BASE / (double)ic->duration; + if (bitrate >= 0 && bitrate <= INT_MAX) + ic->bit_rate = bitrate; } - } } static void fill_all_stream_timings(AVFormatContext *ic) @@ -1737,7 +2227,7 @@ static void fill_all_stream_timings(AVFormatContext *ic) static void estimate_timings_from_bit_rate(AVFormatContext *ic) { int64_t filesize, duration; - int i; + int i, show_warning = 0; AVStream *st; /* if bit_rate is already set, we believe it */ @@ -1763,16 +2253,21 @@ static void estimate_timings_from_bit_rate(AVFormatContext *ic) if (filesize > 0) { for(i = 0; i < ic->nb_streams; i++) { st = ic->streams[i]; - duration= av_rescale(8*filesize, st->time_base.den, ic->bit_rate*(int64_t)st->time_base.num); - if (st->duration == AV_NOPTS_VALUE) + if ( st->time_base.num <= INT64_MAX / ic->bit_rate + && st->duration == AV_NOPTS_VALUE) { + duration= av_rescale(8*filesize, st->time_base.den, ic->bit_rate*(int64_t)st->time_base.num); st->duration = duration; + show_warning = 1; + } } } } + if (show_warning) + av_log(ic, AV_LOG_WARNING, "Estimating duration from bitrate, this may be inaccurate\n"); } -#define DURATION_MAX_READ_SIZE 250000 -#define DURATION_MAX_RETRY 3 +#define DURATION_MAX_READ_SIZE 250000LL +#define DURATION_MAX_RETRY 4 /* only usable for MPEG-PS streams */ static void estimate_timings_from_pts(AVFormatContext *ic, int64_t old_offset) @@ -1828,11 +2323,11 @@ static void estimate_timings_from_pts(AVFormatContext *ic, int64_t old_offset) duration -= st->start_time; else duration -= st->first_dts; - if (duration < 0) - duration += 1LL<<st->pts_wrap_bits; if (duration > 0) { - if (st->duration == AV_NOPTS_VALUE || st->duration < duration) + if (st->duration == AV_NOPTS_VALUE || st->info->last_duration<=0 || + (st->duration < duration && FFABS(duration - st->info->last_duration) < 60LL*st->time_base.den / st->time_base.num)) st->duration = duration; + st->info->last_duration = duration; } } av_free_packet(pkt); @@ -1868,14 +2363,16 @@ static void estimate_timings(AVFormatContext *ic, int64_t old_offset) file_size && ic->pb->seekable) { /* get accurate estimate from the PTSes */ estimate_timings_from_pts(ic, old_offset); + ic->duration_estimation_method = AVFMT_DURATION_FROM_PTS; } else if (has_duration(ic)) { /* at least one component has timings - we use them for all the components */ fill_all_stream_timings(ic); + ic->duration_estimation_method = AVFMT_DURATION_FROM_STREAM; } else { - av_log(ic, AV_LOG_WARNING, "Estimating duration from bitrate, this may be inaccurate\n"); /* less precise: use bitrate info */ estimate_timings_from_bit_rate(ic); + ic->duration_estimation_method = AVFMT_DURATION_FROM_BITRATE; } update_stream_timings(ic); @@ -1895,40 +2392,58 @@ static void estimate_timings(AVFormatContext *ic, int64_t old_offset) } } -static int has_codec_parameters(AVStream *st) +static int has_codec_parameters(AVStream *st, const char **errmsg_ptr) { AVCodecContext *avctx = st->codec; - int val; + +#define FAIL(errmsg) do { \ + if (errmsg_ptr) \ + *errmsg_ptr = errmsg; \ + return 0; \ + } while (0) + switch (avctx->codec_type) { case AVMEDIA_TYPE_AUDIO: - val = avctx->sample_rate && avctx->channels; + if (!avctx->frame_size && determinable_frame_size(avctx)) + FAIL("unspecified frame size"); if (st->info->found_decoder >= 0 && avctx->sample_fmt == AV_SAMPLE_FMT_NONE) - return 0; + FAIL("unspecified sample format"); + if (!avctx->sample_rate) + FAIL("unspecified sample rate"); + if (!avctx->channels) + FAIL("unspecified number of channels"); + if (st->info->found_decoder >= 0 && !st->nb_decoded_frames && avctx->codec_id == AV_CODEC_ID_DTS) + FAIL("no decodable DTS frames"); break; case AVMEDIA_TYPE_VIDEO: - val = avctx->width; + if (!avctx->width) + FAIL("unspecified size"); if (st->info->found_decoder >= 0 && avctx->pix_fmt == AV_PIX_FMT_NONE) - return 0; + FAIL("unspecified pixel format"); + if (st->codec->codec_id == AV_CODEC_ID_RV30 || st->codec->codec_id == AV_CODEC_ID_RV40) + if (!st->sample_aspect_ratio.num && !st->codec->sample_aspect_ratio.num && !st->codec_info_nb_frames) + FAIL("no frame in rv30/40 and no sar"); break; - default: - val = 1; + case AVMEDIA_TYPE_SUBTITLE: + if (avctx->codec_id == AV_CODEC_ID_HDMV_PGS_SUBTITLE && !avctx->width) + FAIL("unspecified size"); break; + case AVMEDIA_TYPE_DATA: + if(avctx->codec_id == AV_CODEC_ID_NONE) return 1; } - return avctx->codec_id != AV_CODEC_ID_NONE && val != 0; -} -static int has_decode_delay_been_guessed(AVStream *st) -{ - return st->codec->codec_id != AV_CODEC_ID_H264 || - st->info->nb_decoded_frames >= 6; + if (avctx->codec_id == AV_CODEC_ID_NONE) + FAIL("unknown codec"); + return 1; } /* returns 1 or 0 if or if not decoded data was returned, or a negative error */ -static int try_decode_frame(AVStream *st, AVPacket *avpkt, AVDictionary **options) +static int try_decode_frame(AVFormatContext *s, AVStream *st, AVPacket *avpkt, AVDictionary **options) { const AVCodec *codec; int got_picture = 1, ret = 0; AVFrame *frame = avcodec_alloc_frame(); + AVSubtitle subtitle; AVPacket pkt = *avpkt; if (!frame) @@ -1937,8 +2452,7 @@ static int try_decode_frame(AVStream *st, AVPacket *avpkt, AVDictionary **option if (!avcodec_is_open(st->codec) && !st->info->found_decoder) { AVDictionary *thread_opt = NULL; - codec = st->codec->codec ? st->codec->codec : - avcodec_find_decoder(st->codec->codec_id); + codec = find_decoder(s, st, st->codec->codec_id); if (!codec) { st->info->found_decoder = -1; @@ -1967,7 +2481,7 @@ static int try_decode_frame(AVStream *st, AVPacket *avpkt, AVDictionary **option while ((pkt.size > 0 || (!pkt.data && got_picture)) && ret >= 0 && - (!has_codec_parameters(st) || + (!has_codec_parameters(st, NULL) || !has_decode_delay_been_guessed(st) || (!st->codec_info_nb_frames && st->codec->codec->capabilities & CODEC_CAP_CHANNEL_CONF))) { got_picture = 0; @@ -1980,18 +2494,26 @@ static int try_decode_frame(AVStream *st, AVPacket *avpkt, AVDictionary **option case AVMEDIA_TYPE_AUDIO: ret = avcodec_decode_audio4(st->codec, frame, &got_picture, &pkt); break; + case AVMEDIA_TYPE_SUBTITLE: + ret = avcodec_decode_subtitle2(st->codec, &subtitle, + &got_picture, &pkt); + ret = pkt.size; + break; default: break; } if (ret >= 0) { if (got_picture) - st->info->nb_decoded_frames++; + st->nb_decoded_frames++; pkt.data += ret; pkt.size -= ret; ret = got_picture; } } + if(!pkt.data && !got_picture) + ret = -1; + fail: avcodec_free_frame(&frame); return ret; @@ -2030,6 +2552,7 @@ enum AVCodecID ff_get_pcm_codec_id(int bps, int flt, int be, int sflags) default: return AV_CODEC_ID_NONE; } } else { + bps += 7; bps >>= 3; if (sflags & (1 << (bps - 1))) { switch (bps) { @@ -2053,10 +2576,25 @@ enum AVCodecID ff_get_pcm_codec_id(int bps, int flt, int be, int sflags) unsigned int av_codec_get_tag(const AVCodecTag * const *tags, enum AVCodecID id) { + unsigned int tag; + if (!av_codec_get_tag2(tags, id, &tag)) + return 0; + return tag; +} + +int av_codec_get_tag2(const AVCodecTag * const *tags, enum AVCodecID id, + unsigned int *tag) +{ int i; for(i=0; tags && tags[i]; i++){ - int tag= ff_codec_get_tag(tags[i], id); - if(tag) return tag; + const AVCodecTag *codec_tags = tags[i]; + while (codec_tags->id != AV_CODEC_ID_NONE) { + if (codec_tags->id == id) { + *tag = codec_tags->tag; + return 1; + } + codec_tags++; + } } return 0; } @@ -2093,8 +2631,8 @@ static void compute_chapters_end(AVFormatContext *s) } static int get_std_framerate(int i){ - if(i<60*12) return i*1001; - else return ((const int[]){24,30,60,12,15})[i-60*12]*1000*12; + if(i<60*12) return (i+1)*1001; + else return ((const int[]){24,30,60,12,15,48})[i-60*12]*1000*12; } /* @@ -2110,6 +2648,7 @@ static int tb_unreliable(AVCodecContext *c){ || c->time_base.den < 5L*c->time_base.num /* || c->codec_tag == AV_RL32("DIVX") || c->codec_tag == AV_RL32("XVID")*/ + || c->codec_tag == AV_RL32("mp4v") || c->codec_id == AV_CODEC_ID_MPEG2VIDEO || c->codec_id == AV_CODEC_ID_H264 ) @@ -2117,28 +2656,74 @@ static int tb_unreliable(AVCodecContext *c){ return 0; } +#if FF_API_FORMAT_PARAMETERS +int av_find_stream_info(AVFormatContext *ic) +{ + return avformat_find_stream_info(ic, NULL); +} +#endif + +int ff_alloc_extradata(AVCodecContext *avctx, int size) +{ + int ret; + + if (size < 0 || size >= INT32_MAX - FF_INPUT_BUFFER_PADDING_SIZE) { + avctx->extradata_size = 0; + return AVERROR(EINVAL); + } + avctx->extradata = av_malloc(size + FF_INPUT_BUFFER_PADDING_SIZE); + if (avctx->extradata) { + memset(avctx->extradata + size, 0, FF_INPUT_BUFFER_PADDING_SIZE); + avctx->extradata_size = size; + ret = 0; + } else { + avctx->extradata_size = 0; + ret = AVERROR(ENOMEM); + } + return ret; +} + int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options) { - int i, count, ret, read_size, j; + int i, count, ret = 0, j; + int64_t read_size; AVStream *st; AVPacket pkt1, *pkt; int64_t old_offset = avio_tell(ic->pb); int orig_nb_streams = ic->nb_streams; // new streams might appear, no options for those + int flush_codecs = ic->probesize > 0; + + if(ic->pb) + av_log(ic, AV_LOG_DEBUG, "File position before avformat_find_stream_info() is %"PRId64"\n", avio_tell(ic->pb)); for(i=0;i<ic->nb_streams;i++) { const AVCodec *codec; AVDictionary *thread_opt = NULL; st = ic->streams[i]; + if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO || + st->codec->codec_type == AVMEDIA_TYPE_SUBTITLE) { +/* if(!st->time_base.num) + st->time_base= */ + if(!st->codec->time_base.num) + st->codec->time_base= st->time_base; + } //only for the split stuff if (!st->parser && !(ic->flags & AVFMT_FLAG_NOPARSE)) { st->parser = av_parser_init(st->codec->codec_id); - if(st->need_parsing == AVSTREAM_PARSE_HEADERS && st->parser){ - st->parser->flags |= PARSER_FLAG_COMPLETE_FRAMES; + if(st->parser){ + if(st->need_parsing == AVSTREAM_PARSE_HEADERS){ + st->parser->flags |= PARSER_FLAG_COMPLETE_FRAMES; + } else if(st->need_parsing == AVSTREAM_PARSE_FULL_RAW) { + st->parser->flags |= PARSER_FLAG_USE_CODEC_TS; + } + } else if (st->need_parsing) { + av_log(ic, AV_LOG_VERBOSE, "parser not found for codec " + "%s, packets or times may be invalid.\n", + avcodec_get_name(st->codec->codec_id)); } } - codec = st->codec->codec ? st->codec->codec : - avcodec_find_decoder(st->codec->codec_id); + codec = find_decoder(ic, st, st->codec->codec_id); /* force thread count to 1 since the h264 decoder will not extract SPS * and PPS to extradata during multi-threaded decoding */ @@ -2151,7 +2736,7 @@ int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options) : &thread_opt); //try to just open decoders, in case this is enough to get parameters - if (!has_codec_parameters(st)) { + if (!has_codec_parameters(st, NULL) && st->request_probe <= 0) { if (codec && !st->codec->codec) avcodec_open2(st->codec, codec, options ? &options[i] : &thread_opt); @@ -2161,6 +2746,9 @@ int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options) } for (i=0; i<ic->nb_streams; i++) { +#if FF_API_R_FRAME_RATE + ic->streams[i]->info->last_dts = AV_NOPTS_VALUE; +#endif ic->streams[i]->info->fps_first_dts = AV_NOPTS_VALUE; ic->streams[i]->info->fps_last_dts = AV_NOPTS_VALUE; } @@ -2179,7 +2767,7 @@ int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options) int fps_analyze_framecount = 20; st = ic->streams[i]; - if (!has_codec_parameters(st)) + if (!has_codec_parameters(st, NULL)) break; /* if the timebase is coarse (like the usual millisecond precision of mkv), we need to analyze more frames to reliably arrive at @@ -2188,9 +2776,11 @@ int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options) fps_analyze_framecount *= 2; if (ic->fps_probe_size >= 0) fps_analyze_framecount = ic->fps_probe_size; + if (st->disposition & AV_DISPOSITION_ATTACHED_PIC) + fps_analyze_framecount = 0; /* variable fps and no guess at the real fps */ - if( tb_unreliable(st->codec) && !st->avg_frame_rate.num - && st->codec_info_nb_frames < fps_analyze_framecount + if( tb_unreliable(st->codec) && !(st->r_frame_rate.num && st->avg_frame_rate.num) + && st->info->duration_count < fps_analyze_framecount && st->codec->codec_type == AVMEDIA_TYPE_VIDEO) break; if(st->parser && st->parser->parser->split && !st->codec->extradata) @@ -2208,13 +2798,21 @@ int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options) /* if we found the info for all the codecs, we can stop */ ret = count; av_log(ic, AV_LOG_DEBUG, "All info found\n"); + flush_codecs = 0; break; } } /* we did not get all the codec info, but we read too much data */ if (read_size >= ic->probesize) { ret = count; - av_log(ic, AV_LOG_DEBUG, "Probe buffer size limit %d reached\n", ic->probesize); + av_log(ic, AV_LOG_DEBUG, "Probe buffer size limit of %d bytes reached\n", ic->probesize); + for (i = 0; i < ic->nb_streams; i++) + if (!ic->streams[i]->r_frame_rate.num && + ic->streams[i]->info->duration_count <= 1 && + strcmp(ic->iformat->name, "image2")) + av_log(ic, AV_LOG_WARNING, + "Stream #%d: not enough frames to estimate rate; " + "consider increasing probesize\n", i); break; } @@ -2226,43 +2824,18 @@ int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options) if (ret < 0) { /* EOF or error*/ - AVPacket empty_pkt = { 0 }; - int err = 0; - av_init_packet(&empty_pkt); - - ret = -1; /* we could not have all the codec parameters before EOF */ - for(i=0;i<ic->nb_streams;i++) { - st = ic->streams[i]; - - /* flush the decoders */ - if (st->info->found_decoder == 1) { - do { - err = try_decode_frame(st, &empty_pkt, - (options && i < orig_nb_streams) ? - &options[i] : NULL); - } while (err > 0 && !has_codec_parameters(st)); - } - - if (err < 0) { - av_log(ic, AV_LOG_WARNING, - "decoding for stream %d failed\n", st->index); - } else if (!has_codec_parameters(st)) { - char buf[256]; - avcodec_string(buf, sizeof(buf), st->codec, 0); - av_log(ic, AV_LOG_WARNING, - "Could not find codec parameters (%s)\n", buf); - } else { - ret = 0; - } - } break; } - if (ic->flags & AVFMT_FLAG_NOBUFFER) { - pkt = &pkt1; - } else { + if (ic->flags & AVFMT_FLAG_NOBUFFER) + free_packet_buffer(&ic->packet_buffer, &ic->packet_buffer_end); + { pkt = add_to_pktbuf(&ic->packet_buffer, &pkt1, &ic->packet_buffer_end); + if (!pkt) { + ret = AVERROR(ENOMEM); + goto find_stream_info_err; + } if ((ret = av_dup_packet(pkt)) < 0) goto find_stream_info_err; } @@ -2274,7 +2847,7 @@ int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options) /* check for non-increasing dts */ if (st->info->fps_last_dts != AV_NOPTS_VALUE && st->info->fps_last_dts >= pkt->dts) { - av_log(ic, AV_LOG_WARNING, "Non-increasing DTS in stream %d: " + av_log(ic, AV_LOG_DEBUG, "Non-increasing DTS in stream %d: " "packet %d with DTS %"PRId64", packet %d with DTS " "%"PRId64"\n", st->index, st->info->fps_last_dts_idx, st->info->fps_last_dts, st->codec_info_nb_frames, pkt->dts); @@ -2301,23 +2874,70 @@ int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options) } st->info->fps_last_dts = pkt->dts; st->info->fps_last_dts_idx = st->codec_info_nb_frames; - - /* check max_analyze_duration */ - if (av_rescale_q(pkt->dts - st->info->fps_first_dts, st->time_base, - AV_TIME_BASE_Q) >= ic->max_analyze_duration) { - av_log(ic, AV_LOG_WARNING, "max_analyze_duration reached\n"); + } + if (st->codec_info_nb_frames>1) { + int64_t t=0; + if (st->time_base.den > 0) + t = av_rescale_q(st->info->codec_info_duration, st->time_base, AV_TIME_BASE_Q); + if (st->avg_frame_rate.num > 0) + t = FFMAX(t, av_rescale_q(st->codec_info_nb_frames, av_inv_q(st->avg_frame_rate), AV_TIME_BASE_Q)); + + if ( t==0 + && st->codec_info_nb_frames>30 + && st->info->fps_first_dts != AV_NOPTS_VALUE + && st->info->fps_last_dts != AV_NOPTS_VALUE) + t = FFMAX(t, av_rescale_q(st->info->fps_last_dts - st->info->fps_first_dts, st->time_base, AV_TIME_BASE_Q)); + + if (t >= ic->max_analyze_duration) { + av_log(ic, AV_LOG_VERBOSE, "max_analyze_duration %d reached at %"PRId64" microseconds\n", ic->max_analyze_duration, t); break; } + if (pkt->duration) { + st->info->codec_info_duration += pkt->duration; + st->info->codec_info_duration_fields += st->parser && st->need_parsing && st->codec->ticks_per_frame==2 ? st->parser->repeat_pict + 1 : 2; + } } +#if FF_API_R_FRAME_RATE + { + int64_t last = st->info->last_dts; + + if( pkt->dts != AV_NOPTS_VALUE && last != AV_NOPTS_VALUE && pkt->dts > last + && pkt->dts - (uint64_t)last < INT64_MAX){ + double dts= (is_relative(pkt->dts) ? pkt->dts - RELATIVE_TS_BASE : pkt->dts) * av_q2d(st->time_base); + int64_t duration= pkt->dts - last; + + if (!st->info->duration_error) + st->info->duration_error = av_mallocz(sizeof(st->info->duration_error[0])*2); + if (!st->info->duration_error) + return AVERROR(ENOMEM); + +// if(st->codec->codec_type == AVMEDIA_TYPE_VIDEO) +// av_log(NULL, AV_LOG_ERROR, "%f\n", dts); + for (i=0; i<MAX_STD_TIMEBASES; i++) { + int framerate= get_std_framerate(i); + double sdts= dts*framerate/(1001*12); + for(j=0; j<2; j++){ + int64_t ticks= llrint(sdts+j*0.5); + double error= sdts - ticks + j*0.5; + st->info->duration_error[j][0][i] += error; + st->info->duration_error[j][1][i] += error*error; + } + } + st->info->duration_count++; + // ignore the first 4 values, they might have some random jitter + if (st->info->duration_count > 3 && is_relative(pkt->dts) == is_relative(last)) + st->info->duration_gcd = av_gcd(st->info->duration_gcd, duration); + } + if (pkt->dts != AV_NOPTS_VALUE) + st->info->last_dts = pkt->dts; + } +#endif if(st->parser && st->parser->parser->split && !st->codec->extradata){ int i= st->parser->parser->split(st->codec, pkt->data, pkt->size); if (i > 0 && i < FF_MAX_EXTRADATA_SIZE) { - st->codec->extradata_size= i; - st->codec->extradata= av_malloc(st->codec->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE); - if (!st->codec->extradata) + if (ff_alloc_extradata(st->codec, i)) return AVERROR(ENOMEM); memcpy(st->codec->extradata, pkt->data, st->codec->extradata_size); - memset(st->codec->extradata + i, 0, FF_INPUT_BUFFER_PADDING_SIZE); } } @@ -2330,12 +2950,37 @@ int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options) least one frame of codec data, this makes sure the codec initializes the channel configuration and does not only trust the values from the container. */ - try_decode_frame(st, pkt, (options && i < orig_nb_streams ) ? &options[i] : NULL); + try_decode_frame(ic, st, pkt, (options && i < orig_nb_streams ) ? &options[i] : NULL); st->codec_info_nb_frames++; count++; } + if (flush_codecs) { + AVPacket empty_pkt = { 0 }; + int err = 0; + av_init_packet(&empty_pkt); + + for(i=0;i<ic->nb_streams;i++) { + + st = ic->streams[i]; + + /* flush the decoders */ + if (st->info->found_decoder == 1) { + do { + err = try_decode_frame(ic, st, &empty_pkt, + (options && i < orig_nb_streams) ? + &options[i] : NULL); + } while (err > 0 && !has_codec_parameters(st, NULL)); + + if (err < 0) { + av_log(ic, AV_LOG_INFO, + "decoding for stream %d failed\n", st->index); + } + } + } + } + // close codecs which were opened in try_decode_frame() for(i=0;i<ic->nb_streams;i++) { st = ic->streams[i]; @@ -2344,20 +2989,24 @@ int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options) for(i=0;i<ic->nb_streams;i++) { st = ic->streams[i]; if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) { + if(st->codec->codec_id == AV_CODEC_ID_RAWVIDEO && !st->codec->codec_tag && !st->codec->bits_per_coded_sample){ + uint32_t tag= avcodec_pix_fmt_to_codec_tag(st->codec->pix_fmt); + if (avpriv_find_pix_fmt(ff_raw_pix_fmt_tags, tag) == st->codec->pix_fmt) + st->codec->codec_tag= tag; + } + /* estimate average framerate if not set by demuxer */ - if (!st->avg_frame_rate.num && st->info->fps_last_dts != st->info->fps_first_dts) { - int64_t delta_dts = st->info->fps_last_dts - st->info->fps_first_dts; - int delta_packets = st->info->fps_last_dts_idx - st->info->fps_first_dts_idx; + if (st->info->codec_info_duration_fields && !st->avg_frame_rate.num && st->info->codec_info_duration) { int best_fps = 0; double best_error = 0.01; - if (delta_dts >= INT64_MAX / st->time_base.num || - delta_packets >= INT64_MAX / st->time_base.den || - delta_dts < 0) + if (st->info->codec_info_duration >= INT64_MAX / st->time_base.num / 2|| + st->info->codec_info_duration_fields >= INT64_MAX / st->time_base.den || + st->info->codec_info_duration < 0) continue; av_reduce(&st->avg_frame_rate.num, &st->avg_frame_rate.den, - delta_packets*(int64_t)st->time_base.den, - delta_dts*(int64_t)st->time_base.num, 60000); + st->info->codec_info_duration_fields*(int64_t)st->time_base.den, + st->info->codec_info_duration*2*(int64_t)st->time_base.num, 60000); /* round guessed framerate to a "standard" framerate if it's * within 1% of the original estimate*/ @@ -2375,6 +3024,51 @@ int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options) best_fps, 12*1001, INT_MAX); } } + // the check for tb_unreliable() is not completely correct, since this is not about handling + // a unreliable/inexact time base, but a time base that is finer than necessary, as e.g. + // ipmovie.c produces. + if (tb_unreliable(st->codec) && st->info->duration_count > 15 && st->info->duration_gcd > FFMAX(1, st->time_base.den/(500LL*st->time_base.num)) && !st->r_frame_rate.num) + av_reduce(&st->r_frame_rate.num, &st->r_frame_rate.den, st->time_base.den, st->time_base.num * st->info->duration_gcd, INT_MAX); + if (st->info->duration_count>1 && !st->r_frame_rate.num + && tb_unreliable(st->codec)) { + int num = 0; + double best_error= 0.01; + + for (j=0; j<MAX_STD_TIMEBASES; j++) { + int k; + + if(st->info->codec_info_duration && st->info->codec_info_duration*av_q2d(st->time_base) < (1001*12.0)/get_std_framerate(j)) + continue; + if(!st->info->codec_info_duration && 1.0 < (1001*12.0)/get_std_framerate(j)) + continue; + for(k=0; k<2; k++){ + int n= st->info->duration_count; + double a= st->info->duration_error[k][0][j] / n; + double error= st->info->duration_error[k][1][j]/n - a*a; + + if(error < best_error && best_error> 0.000000001){ + best_error= error; + num = get_std_framerate(j); + } + if(error < 0.02) + av_log(NULL, AV_LOG_DEBUG, "rfps: %f %f\n", get_std_framerate(j) / 12.0/1001, error); + } + } + // do not increase frame rate by more than 1 % in order to match a standard rate. + if (num && (!st->r_frame_rate.num || (double)num/(12*1001) < 1.01 * av_q2d(st->r_frame_rate))) + av_reduce(&st->r_frame_rate.num, &st->r_frame_rate.den, num, 12*1001, INT_MAX); + } + + if (!st->r_frame_rate.num){ + if( st->codec->time_base.den * (int64_t)st->time_base.num + <= st->codec->time_base.num * st->codec->ticks_per_frame * (int64_t)st->time_base.den){ + st->r_frame_rate.num = st->codec->time_base.den; + st->r_frame_rate.den = st->codec->time_base.num * st->codec->ticks_per_frame; + }else{ + st->r_frame_rate.num = st->time_base.den; + st->r_frame_rate.den = st->time_base.num; + } + } }else if(st->codec->codec_type == AVMEDIA_TYPE_AUDIO) { if(!st->codec->bits_per_coded_sample) st->codec->bits_per_coded_sample= av_get_bits_per_sample(st->codec->codec_id); @@ -2394,27 +3088,56 @@ int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options) } } + if(ic->probesize) estimate_timings(ic, old_offset); + if (ret >= 0 && ic->nb_streams) + ret = -1; /* we could not have all the codec parameters before EOF */ + for(i=0;i<ic->nb_streams;i++) { + const char *errmsg; + st = ic->streams[i]; + if (!has_codec_parameters(st, &errmsg)) { + char buf[256]; + avcodec_string(buf, sizeof(buf), st->codec, 0); + av_log(ic, AV_LOG_WARNING, + "Could not find codec parameters for stream %d (%s): %s\n" + "Consider increasing the value for the 'analyzeduration' and 'probesize' options\n", + i, buf, errmsg); + } else { + ret = 0; + } + } + compute_chapters_end(ic); find_stream_info_err: for (i=0; i < ic->nb_streams; i++) { - if (ic->streams[i]->codec) + st = ic->streams[i]; + if (ic->streams[i]->codec && ic->streams[i]->codec->codec_type != AVMEDIA_TYPE_AUDIO) ic->streams[i]->codec->thread_count = 0; + if (st->info) + av_freep(&st->info->duration_error); av_freep(&ic->streams[i]->info); } + if(ic->pb) + av_log(ic, AV_LOG_DEBUG, "File position after avformat_find_stream_info() is %"PRId64"\n", avio_tell(ic->pb)); return ret; } -static AVProgram *find_program_from_stream(AVFormatContext *ic, int s) +AVProgram *av_find_program_from_stream(AVFormatContext *ic, AVProgram *last, int s) { int i, j; - for (i = 0; i < ic->nb_programs; i++) - for (j = 0; j < ic->programs[i]->nb_stream_indexes; j++) - if (ic->programs[i]->stream_index[j] == s) - return ic->programs[i]; + for (i = 0; i < ic->nb_programs; i++) { + if (ic->programs[i] == last) { + last = NULL; + } else { + if (!last) + for (j = 0; j < ic->programs[i]->nb_stream_indexes; j++) + if (ic->programs[i]->stream_index[j] == s) + return ic->programs[i]; + } + } return NULL; } @@ -2426,12 +3149,12 @@ int av_find_best_stream(AVFormatContext *ic, int flags) { int i, nb_streams = ic->nb_streams; - int ret = AVERROR_STREAM_NOT_FOUND, best_count = -1; + int ret = AVERROR_STREAM_NOT_FOUND, best_count = -1, best_bitrate = -1, best_multiframe = -1, count, bitrate, multiframe; unsigned *program = NULL; AVCodec *decoder = NULL, *best_decoder = NULL; if (related_stream >= 0 && wanted_stream_nb < 0) { - AVProgram *p = find_program_from_stream(ic, related_stream); + AVProgram *p = av_find_program_from_stream(ic, NULL, related_stream); if (p) { program = p->stream_index; nb_streams = p->nb_stream_indexes; @@ -2448,16 +3171,23 @@ int av_find_best_stream(AVFormatContext *ic, if (st->disposition & (AV_DISPOSITION_HEARING_IMPAIRED|AV_DISPOSITION_VISUAL_IMPAIRED)) continue; if (decoder_ret) { - decoder = avcodec_find_decoder(st->codec->codec_id); + decoder = find_decoder(ic, st, st->codec->codec_id); if (!decoder) { if (ret < 0) ret = AVERROR_DECODER_NOT_FOUND; continue; } } - if (best_count >= st->codec_info_nb_frames) + count = st->codec_info_nb_frames; + bitrate = avctx->bit_rate; + multiframe = FFMIN(5, count); + if ((best_multiframe > multiframe) || + (best_multiframe == multiframe && best_bitrate > bitrate) || + (best_multiframe == multiframe && best_bitrate == bitrate && best_count >= count)) continue; - best_count = st->codec_info_nb_frames; + best_count = count; + best_bitrate = bitrate; + best_multiframe = multiframe; ret = real_stream_index; best_decoder = decoder; if (program && i == nb_streams - 1 && ret < 0) { @@ -2491,32 +3221,41 @@ int av_read_pause(AVFormatContext *s) return AVERROR(ENOSYS); } +void ff_free_stream(AVFormatContext *s, AVStream *st){ + av_assert0(s->nb_streams>0); + av_assert0(s->streams[ s->nb_streams-1 ] == st); + + if (st->parser) { + av_parser_close(st->parser); + } + if (st->attached_pic.data) + av_free_packet(&st->attached_pic); + av_dict_free(&st->metadata); + av_freep(&st->probe_data.buf); + av_freep(&st->index_entries); + av_freep(&st->codec->extradata); + av_freep(&st->codec->subtitle_header); + av_freep(&st->codec); + av_freep(&st->priv_data); + if (st->info) + av_freep(&st->info->duration_error); + av_freep(&st->info); + av_freep(&s->streams[ --s->nb_streams ]); +} + void avformat_free_context(AVFormatContext *s) { int i; - AVStream *st; + + if (!s) + return; av_opt_free(s); if (s->iformat && s->iformat->priv_class && s->priv_data) av_opt_free(s->priv_data); - for(i=0;i<s->nb_streams;i++) { - /* free all data in a stream component */ - st = s->streams[i]; - if (st->parser) { - av_parser_close(st->parser); - } - if (st->attached_pic.data) - av_free_packet(&st->attached_pic); - av_dict_free(&st->metadata); - av_freep(&st->probe_data.buf); - av_free(st->index_entries); - av_free(st->codec->extradata); - av_free(st->codec->subtitle_header); - av_free(st->codec); - av_free(st->priv_data); - av_free(st->info); - av_free(st); + for(i=s->nb_streams-1; i>=0; i--) { + ff_free_stream(s, s->streams[i]); } for(i=s->nb_programs-1; i>=0; i--) { av_dict_free(&s->programs[i]->metadata); @@ -2527,7 +3266,7 @@ void avformat_free_context(AVFormatContext *s) av_freep(&s->priv_data); while(s->nb_chapters--) { av_dict_free(&s->chapters[s->nb_chapters]->metadata); - av_free(s->chapters[s->nb_chapters]); + av_freep(&s->chapters[s->nb_chapters]); } av_freep(&s->chapters); av_dict_free(&s->metadata); @@ -2535,10 +3274,23 @@ void avformat_free_context(AVFormatContext *s) av_free(s); } +#if FF_API_CLOSE_INPUT_FILE +void av_close_input_file(AVFormatContext *s) +{ + avformat_close_input(&s); +} +#endif + void avformat_close_input(AVFormatContext **ps) { - AVFormatContext *s = *ps; - AVIOContext *pb = s->pb; + AVFormatContext *s; + AVIOContext *pb; + + if (!ps || !*ps) + return; + + s = *ps; + pb = s->pb; if ((s->iformat && s->iformat->flags & AVFMT_NOFILE) || (s->flags & AVFMT_FLAG_CUSTOM_IO)) @@ -2558,15 +3310,28 @@ void avformat_close_input(AVFormatContext **ps) avio_close(pb); } -AVStream *avformat_new_stream(AVFormatContext *s, AVCodec *c) +#if FF_API_NEW_STREAM +AVStream *av_new_stream(AVFormatContext *s, int id) +{ + AVStream *st = avformat_new_stream(s, NULL); + if (st) + st->id = id; + return st; +} +#endif + +AVStream *avformat_new_stream(AVFormatContext *s, const AVCodec *c) { AVStream *st; int i; + AVStream **streams; - if (av_reallocp_array(&s->streams, s->nb_streams + 1, sizeof(*s->streams)) < 0) { - s->nb_streams = 0; + if (s->nb_streams >= INT_MAX/sizeof(*streams)) return NULL; - } + streams = av_realloc_array(s->streams, s->nb_streams + 1, sizeof(*streams)); + if (!streams) + return NULL; + s->streams = streams; st = av_mallocz(sizeof(AVStream)); if (!st) @@ -2575,6 +3340,7 @@ AVStream *avformat_new_stream(AVFormatContext *s, AVCodec *c) av_free(st); return NULL; } + st->info->last_dts = AV_NOPTS_VALUE; st->codec = avcodec_alloc_context3(c); if (s->iformat) { @@ -2588,9 +3354,11 @@ AVStream *avformat_new_stream(AVFormatContext *s, AVCodec *c) but durations get some timestamps, formats with some unknown timestamps have their first few packets buffered and the timestamps corrected before they are returned to the user */ - st->cur_dts = 0; + st->cur_dts = s->iformat ? RELATIVE_TS_BASE : 0; st->first_dts = AV_NOPTS_VALUE; st->probe_packets = MAX_PROBE_PACKETS; + st->pts_wrap_reference = AV_NOPTS_VALUE; + st->pts_wrap_behavior = AV_PTS_WRAP_IGNORE; /* default pts setting is MPEG-like */ avpriv_set_pts_info(st, 33, 1, 90000); @@ -2600,6 +3368,9 @@ AVStream *avformat_new_stream(AVFormatContext *s, AVCodec *c) st->sample_aspect_ratio = (AVRational){0,1}; +#if FF_API_R_FRAME_RATE + st->info->last_dts = AV_NOPTS_VALUE; +#endif st->info->fps_first_dts = AV_NOPTS_VALUE; st->info->fps_last_dts = AV_NOPTS_VALUE; @@ -2626,6 +3397,11 @@ AVProgram *av_new_program(AVFormatContext *ac, int id) program->discard = AVDISCARD_NONE; } program->id = id; + program->pts_wrap_reference = AV_NOPTS_VALUE; + program->pts_wrap_behavior = AV_PTS_WRAP_IGNORE; + + program->start_time = + program->end_time = AV_NOPTS_VALUE; return program; } @@ -2658,6 +3434,7 @@ void ff_program_add_stream_index(AVFormatContext *ac, int progid, unsigned int i { int i, j; AVProgram *program=NULL; + void *tmp; if (idx >= ac->nb_streams) { av_log(ac, AV_LOG_ERROR, "stream index %d is not valid\n", idx); @@ -2672,12 +3449,10 @@ void ff_program_add_stream_index(AVFormatContext *ac, int progid, unsigned int i if(program->stream_index[j] == idx) return; - if (av_reallocp_array(&program->stream_index, - program->nb_stream_indexes + 1, - sizeof(*program->stream_index)) < 0) { - program->nb_stream_indexes = 0; + tmp = av_realloc_array(program->stream_index, program->nb_stream_indexes+1, sizeof(unsigned int)); + if(!tmp) return; - } + program->stream_index = tmp; program->stream_index[program->nb_stream_indexes++] = idx; return; } @@ -2697,8 +3472,21 @@ static void dump_metadata(void *ctx, AVDictionary *m, const char *indent) av_log(ctx, AV_LOG_INFO, "%sMetadata:\n", indent); while((tag=av_dict_get(m, "", tag, AV_DICT_IGNORE_SUFFIX))) { - if(strcmp("language", tag->key)) - av_log(ctx, AV_LOG_INFO, "%s %-16s: %s\n", indent, tag->key, tag->value); + if(strcmp("language", tag->key)){ + const char *p = tag->value; + av_log(ctx, AV_LOG_INFO, "%s %-16s: ", indent, tag->key); + while(*p) { + char tmp[256]; + size_t len = strcspn(p, "\x8\xa\xb\xc\xd"); + av_strlcpy(tmp, p, FFMIN(sizeof(tmp), len+1)); + av_log(ctx, AV_LOG_INFO, "%s", tmp); + p += len; + if (*p == 0xd) av_log(ctx, AV_LOG_INFO, " "); + if (*p == 0xa) av_log(ctx, AV_LOG_INFO, "\n%s %-16s: ", indent, ""); + if (*p) p++; + } + av_log(ctx, AV_LOG_INFO, "\n"); + } } } } @@ -2712,7 +3500,7 @@ static void dump_stream_format(AVFormatContext *ic, int i, int index, int is_out int g = av_gcd(st->time_base.num, st->time_base.den); AVDictionaryEntry *lang = av_dict_get(st->metadata, "language", NULL, 0); avcodec_string(buf, sizeof(buf), st->codec, is_output); - av_log(NULL, AV_LOG_INFO, " Stream #%d.%d", index, i); + av_log(NULL, AV_LOG_INFO, " Stream #%d:%d", index, i); /* the pid is an important information, so we display it */ /* XXX: add a generic system */ if (flags & AVFMT_SHOW_IDS) @@ -2728,13 +3516,17 @@ static void dump_stream_format(AVFormatContext *ic, int i, int index, int is_out st->codec->width*st->sample_aspect_ratio.num, st->codec->height*st->sample_aspect_ratio.den, 1024*1024); - av_log(NULL, AV_LOG_INFO, ", PAR %d:%d DAR %d:%d", + av_log(NULL, AV_LOG_INFO, ", SAR %d:%d DAR %d:%d", st->sample_aspect_ratio.num, st->sample_aspect_ratio.den, display_aspect_ratio.num, display_aspect_ratio.den); } if(st->codec->codec_type == AVMEDIA_TYPE_VIDEO){ if(st->avg_frame_rate.den && st->avg_frame_rate.num) print_fps(av_q2d(st->avg_frame_rate), "fps"); +#if FF_API_R_FRAME_RATE + if(st->r_frame_rate.den && st->r_frame_rate.num) + print_fps(av_q2d(st->r_frame_rate), "tbr"); +#endif if(st->time_base.den && st->time_base.num) print_fps(1/av_q2d(st->time_base), "tbn"); if(st->codec->time_base.den && st->codec->time_base.num) @@ -2784,8 +3576,9 @@ void av_dump_format(AVFormatContext *ic, av_log(NULL, AV_LOG_INFO, " Duration: "); if (ic->duration != AV_NOPTS_VALUE) { int hours, mins, secs, us; - secs = ic->duration / AV_TIME_BASE; - us = ic->duration % AV_TIME_BASE; + int64_t duration = ic->duration + 5000; + secs = duration / AV_TIME_BASE; + us = duration % AV_TIME_BASE; mins = secs / 60; secs %= 60; hours = mins / 60; @@ -2985,7 +3778,7 @@ void av_url_split(char *proto, int proto_size, char *path, int path_size, const char *url) { - const char *p, *ls, *at, *col, *brk; + const char *p, *ls, *ls2, *at, *at2, *col, *brk; if (port_ptr) *port_ptr = -1; if (proto_size > 0) proto[0] = 0; @@ -3007,8 +3800,11 @@ void av_url_split(char *proto, int proto_size, /* separate path from hostname */ ls = strchr(p, '/'); + ls2 = strchr(p, '?'); if(!ls) - ls = strchr(p, '?'); + ls = ls2; + else if (ls && ls2) + ls = FFMIN(ls, ls2); if(ls) av_strlcpy(path, ls, path_size); else @@ -3017,9 +3813,10 @@ void av_url_split(char *proto, int proto_size, /* the rest is hostname, use that to parse auth/port */ if (ls != p) { /* authorization (user[:pass]@hostname) */ - if ((at = strchr(p, '@')) && at < ls) { - av_strlcpy(authorization, p, - FFMIN(authorization_size, at + 1 - p)); + at2 = p; + while ((at = strchr(p, '@')) && at < ls) { + av_strlcpy(authorization, at2, + FFMIN(authorization_size, at + 1 - at2)); p = at + 1; /* skip '@' */ } @@ -3088,6 +3885,14 @@ int ff_hex_to_data(uint8_t *data, const char *p) return len; } +#if FF_API_SET_PTS_INFO +void av_set_pts_info(AVStream *s, int pts_wrap_bits, + unsigned int pts_num, unsigned int pts_den) +{ + avpriv_set_pts_info(s, pts_wrap_bits, pts_num, pts_den); +} +#endif + void avpriv_set_pts_info(AVStream *s, int pts_wrap_bits, unsigned int pts_num, unsigned int pts_den) { @@ -3099,10 +3904,11 @@ void avpriv_set_pts_info(AVStream *s, int pts_wrap_bits, av_log(NULL, AV_LOG_WARNING, "st:%d has too large timebase, reducing\n", s->index); if(new_tb.num <= 0 || new_tb.den <= 0) { - av_log(NULL, AV_LOG_ERROR, "Ignoring attempt to set invalid timebase for st:%d\n", s->index); + av_log(NULL, AV_LOG_ERROR, "Ignoring attempt to set invalid timebase %d/%d for st:%d\n", new_tb.num, new_tb.den, s->index); return; } s->time_base = new_tb; + av_codec_set_pkt_timebase(s->codec, new_tb); s->pts_wrap_bits = pts_wrap_bits; } @@ -3172,20 +3978,14 @@ int ff_find_stream_index(AVFormatContext *s, int id) int64_t ff_iso8601_to_unix_time(const char *datestr) { -#if HAVE_STRPTIME struct tm time1 = {0}, time2 = {0}; char *ret1, *ret2; - ret1 = strptime(datestr, "%Y - %m - %d %T", &time1); - ret2 = strptime(datestr, "%Y - %m - %dT%T", &time2); + ret1 = av_small_strptime(datestr, "%Y - %m - %d %H:%M:%S", &time1); + ret2 = av_small_strptime(datestr, "%Y - %m - %dT%H:%M:%S", &time2); if (ret2 && !ret1) return av_timegm(&time2); else return av_timegm(&time1); -#else - av_log(NULL, AV_LOG_WARNING, "strptime() unavailable on this system, cannot convert " - "the date string.\n"); - return 0; -#endif } int avformat_query_codec(AVOutputFormat *ofmt, enum AVCodecID codec_id, int std_compliance) @@ -3264,3 +4064,195 @@ int ff_add_param_change(AVPacket *pkt, int32_t channels, } return 0; } + +AVRational av_guess_sample_aspect_ratio(AVFormatContext *format, AVStream *stream, AVFrame *frame) +{ + AVRational undef = {0, 1}; + AVRational stream_sample_aspect_ratio = stream ? stream->sample_aspect_ratio : undef; + AVRational codec_sample_aspect_ratio = stream && stream->codec ? stream->codec->sample_aspect_ratio : undef; + AVRational frame_sample_aspect_ratio = frame ? frame->sample_aspect_ratio : codec_sample_aspect_ratio; + + av_reduce(&stream_sample_aspect_ratio.num, &stream_sample_aspect_ratio.den, + stream_sample_aspect_ratio.num, stream_sample_aspect_ratio.den, INT_MAX); + if (stream_sample_aspect_ratio.num <= 0 || stream_sample_aspect_ratio.den <= 0) + stream_sample_aspect_ratio = undef; + + av_reduce(&frame_sample_aspect_ratio.num, &frame_sample_aspect_ratio.den, + frame_sample_aspect_ratio.num, frame_sample_aspect_ratio.den, INT_MAX); + if (frame_sample_aspect_ratio.num <= 0 || frame_sample_aspect_ratio.den <= 0) + frame_sample_aspect_ratio = undef; + + if (stream_sample_aspect_ratio.num) + return stream_sample_aspect_ratio; + else + return frame_sample_aspect_ratio; +} + +AVRational av_guess_frame_rate(AVFormatContext *format, AVStream *st, AVFrame *frame) +{ + AVRational fr = st->r_frame_rate; + + if (st->codec->ticks_per_frame > 1) { + AVRational codec_fr = av_inv_q(st->codec->time_base); + AVRational avg_fr = st->avg_frame_rate; + codec_fr.den *= st->codec->ticks_per_frame; + if ( codec_fr.num > 0 && codec_fr.den > 0 && av_q2d(codec_fr) < av_q2d(fr)*0.7 + && fabs(1.0 - av_q2d(av_div_q(avg_fr, fr))) > 0.1) + fr = codec_fr; + } + + return fr; +} + +int avformat_match_stream_specifier(AVFormatContext *s, AVStream *st, + const char *spec) +{ + if (*spec <= '9' && *spec >= '0') /* opt:index */ + return strtol(spec, NULL, 0) == st->index; + else if (*spec == 'v' || *spec == 'a' || *spec == 's' || *spec == 'd' || + *spec == 't') { /* opt:[vasdt] */ + enum AVMediaType type; + + switch (*spec++) { + case 'v': type = AVMEDIA_TYPE_VIDEO; break; + case 'a': type = AVMEDIA_TYPE_AUDIO; break; + case 's': type = AVMEDIA_TYPE_SUBTITLE; break; + case 'd': type = AVMEDIA_TYPE_DATA; break; + case 't': type = AVMEDIA_TYPE_ATTACHMENT; break; + default: av_assert0(0); + } + if (type != st->codec->codec_type) + return 0; + if (*spec++ == ':') { /* possibly followed by :index */ + int i, index = strtol(spec, NULL, 0); + for (i = 0; i < s->nb_streams; i++) + if (s->streams[i]->codec->codec_type == type && index-- == 0) + return i == st->index; + return 0; + } + return 1; + } else if (*spec == 'p' && *(spec + 1) == ':') { + int prog_id, i, j; + char *endptr; + spec += 2; + prog_id = strtol(spec, &endptr, 0); + for (i = 0; i < s->nb_programs; i++) { + if (s->programs[i]->id != prog_id) + continue; + + if (*endptr++ == ':') { + int stream_idx = strtol(endptr, NULL, 0); + return stream_idx >= 0 && + stream_idx < s->programs[i]->nb_stream_indexes && + st->index == s->programs[i]->stream_index[stream_idx]; + } + + for (j = 0; j < s->programs[i]->nb_stream_indexes; j++) + if (st->index == s->programs[i]->stream_index[j]) + return 1; + } + return 0; + } else if (*spec == '#') { + int sid; + char *endptr; + sid = strtol(spec + 1, &endptr, 0); + if (!*endptr) + return st->id == sid; + } else if (!*spec) /* empty specifier, matches everything */ + return 1; + + av_log(s, AV_LOG_ERROR, "Invalid stream specifier: %s.\n", spec); + return AVERROR(EINVAL); +} + +void ff_generate_avci_extradata(AVStream *st) +{ + static const uint8_t avci100_1080p_extradata[] = { + // SPS + 0x00, 0x00, 0x00, 0x01, 0x67, 0x7a, 0x10, 0x29, + 0xb6, 0xd4, 0x20, 0x22, 0x33, 0x19, 0xc6, 0x63, + 0x23, 0x21, 0x01, 0x11, 0x98, 0xce, 0x33, 0x19, + 0x18, 0x21, 0x02, 0x56, 0xb9, 0x3d, 0x7d, 0x7e, + 0x4f, 0xe3, 0x3f, 0x11, 0xf1, 0x9e, 0x08, 0xb8, + 0x8c, 0x54, 0x43, 0xc0, 0x78, 0x02, 0x27, 0xe2, + 0x70, 0x1e, 0x30, 0x10, 0x10, 0x14, 0x00, 0x00, + 0x03, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0xca, + 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // PPS + 0x00, 0x00, 0x00, 0x01, 0x68, 0xce, 0x33, 0x48, + 0xd0 + }; + static const uint8_t avci100_1080i_extradata[] = { + // SPS + 0x00, 0x00, 0x00, 0x01, 0x67, 0x7a, 0x10, 0x29, + 0xb6, 0xd4, 0x20, 0x22, 0x33, 0x19, 0xc6, 0x63, + 0x23, 0x21, 0x01, 0x11, 0x98, 0xce, 0x33, 0x19, + 0x18, 0x21, 0x03, 0x3a, 0x46, 0x65, 0x6a, 0x65, + 0x24, 0xad, 0xe9, 0x12, 0x32, 0x14, 0x1a, 0x26, + 0x34, 0xad, 0xa4, 0x41, 0x82, 0x23, 0x01, 0x50, + 0x2b, 0x1a, 0x24, 0x69, 0x48, 0x30, 0x40, 0x2e, + 0x11, 0x12, 0x08, 0xc6, 0x8c, 0x04, 0x41, 0x28, + 0x4c, 0x34, 0xf0, 0x1e, 0x01, 0x13, 0xf2, 0xe0, + 0x3c, 0x60, 0x20, 0x20, 0x28, 0x00, 0x00, 0x03, + 0x00, 0x08, 0x00, 0x00, 0x03, 0x01, 0x94, 0x00, + // PPS + 0x00, 0x00, 0x00, 0x01, 0x68, 0xce, 0x33, 0x48, + 0xd0 + }; + static const uint8_t avci50_1080i_extradata[] = { + // SPS + 0x00, 0x00, 0x00, 0x01, 0x67, 0x6e, 0x10, 0x28, + 0xa6, 0xd4, 0x20, 0x32, 0x33, 0x0c, 0x71, 0x18, + 0x88, 0x62, 0x10, 0x19, 0x19, 0x86, 0x38, 0x8c, + 0x44, 0x30, 0x21, 0x02, 0x56, 0x4e, 0x6e, 0x61, + 0x87, 0x3e, 0x73, 0x4d, 0x98, 0x0c, 0x03, 0x06, + 0x9c, 0x0b, 0x73, 0xe6, 0xc0, 0xb5, 0x18, 0x63, + 0x0d, 0x39, 0xe0, 0x5b, 0x02, 0xd4, 0xc6, 0x19, + 0x1a, 0x79, 0x8c, 0x32, 0x34, 0x24, 0xf0, 0x16, + 0x81, 0x13, 0xf7, 0xff, 0x80, 0x02, 0x00, 0x01, + 0xf1, 0x80, 0x80, 0x80, 0xa0, 0x00, 0x00, 0x03, + 0x00, 0x20, 0x00, 0x00, 0x06, 0x50, 0x80, 0x00, + // PPS + 0x00, 0x00, 0x00, 0x01, 0x68, 0xee, 0x31, 0x12, + 0x11 + }; + static const uint8_t avci100_720p_extradata[] = { + // SPS + 0x00, 0x00, 0x00, 0x01, 0x67, 0x7a, 0x10, 0x29, + 0xb6, 0xd4, 0x20, 0x2a, 0x33, 0x1d, 0xc7, 0x62, + 0xa1, 0x08, 0x40, 0x54, 0x66, 0x3b, 0x8e, 0xc5, + 0x42, 0x02, 0x10, 0x25, 0x64, 0x2c, 0x89, 0xe8, + 0x85, 0xe4, 0x21, 0x4b, 0x90, 0x83, 0x06, 0x95, + 0xd1, 0x06, 0x46, 0x97, 0x20, 0xc8, 0xd7, 0x43, + 0x08, 0x11, 0xc2, 0x1e, 0x4c, 0x91, 0x0f, 0x01, + 0x40, 0x16, 0xec, 0x07, 0x8c, 0x04, 0x04, 0x05, + 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x03, + 0x00, 0x64, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, + // PPS + 0x00, 0x00, 0x00, 0x01, 0x68, 0xce, 0x31, 0x12, + 0x11 + }; + int size = 0; + const uint8_t *data = 0; + if (st->codec->width == 1920) { + if (st->codec->field_order == AV_FIELD_PROGRESSIVE) { + data = avci100_1080p_extradata; + size = sizeof(avci100_1080p_extradata); + } else { + data = avci100_1080i_extradata; + size = sizeof(avci100_1080i_extradata); + } + } else if (st->codec->width == 1440) { + data = avci50_1080i_extradata; + size = sizeof(avci50_1080i_extradata); + } else if (st->codec->width == 1280) { + data = avci100_720p_extradata; + size = sizeof(avci100_720p_extradata); + } + if (!size) + return; + av_freep(&st->codec->extradata); + if (ff_alloc_extradata(st->codec, size)) + return; + memcpy(st->codec->extradata, data, size); +} diff --git a/libavformat/vc1test.c b/libavformat/vc1test.c index 859188f372..bf1bfe0318 100644 --- a/libavformat/vc1test.c +++ b/libavformat/vc1test.c @@ -2,20 +2,20 @@ * VC1 Test Bitstreams Format Demuxer * Copyright (c) 2006, 2008 Konstantin Shishkov * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -61,8 +61,8 @@ static int vc1t_read_header(AVFormatContext *s) st->codec->codec_type = AVMEDIA_TYPE_VIDEO; st->codec->codec_id = AV_CODEC_ID_WMV3; - st->codec->extradata = av_malloc(VC1_EXTRADATA_SIZE); - st->codec->extradata_size = VC1_EXTRADATA_SIZE; + if (ff_alloc_extradata(st->codec, VC1_EXTRADATA_SIZE)) + return AVERROR(ENOMEM); avio_read(pb, st->codec->extradata, VC1_EXTRADATA_SIZE); st->codec->height = avio_rl32(pb); st->codec->width = avio_rl32(pb); @@ -92,7 +92,7 @@ static int vc1t_read_packet(AVFormatContext *s, int keyframe = 0; uint32_t pts; - if(pb->eof_reached) + if(url_feof(pb)) return AVERROR(EIO); frame_size = avio_rl24(pb); diff --git a/libavformat/vc1testenc.c b/libavformat/vc1testenc.c index 9d55fee46d..751333a4b8 100644 --- a/libavformat/vc1testenc.c +++ b/libavformat/vc1testenc.c @@ -2,20 +2,20 @@ * VC-1 test bitstreams format muxer. * Copyright (c) 2008 Konstantin Shishkov * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "avformat.h" @@ -82,9 +82,8 @@ static int vc1test_write_trailer(AVFormatContext *s) } AVOutputFormat ff_vc1t_muxer = { - .name = "rcv", + .name = "vc1test", .long_name = NULL_IF_CONFIG_SMALL("VC-1 test bitstream"), - .mime_type = "", .extensions = "rcv", .priv_data_size = sizeof(RCVContext), .audio_codec = AV_CODEC_ID_NONE, diff --git a/libavformat/version.h b/libavformat/version.h index 2fa9a0fb67..f6329e4500 100644 --- a/libavformat/version.h +++ b/libavformat/version.h @@ -1,20 +1,20 @@ /* * Version macros. * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -30,8 +30,8 @@ #include "libavutil/avutil.h" #define LIBAVFORMAT_VERSION_MAJOR 55 -#define LIBAVFORMAT_VERSION_MINOR 9 -#define LIBAVFORMAT_VERSION_MICRO 4 +#define LIBAVFORMAT_VERSION_MINOR 20 +#define LIBAVFORMAT_VERSION_MICRO 105 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \ LIBAVFORMAT_VERSION_MINOR, \ @@ -52,4 +52,28 @@ #define FF_API_REFERENCE_DTS (LIBAVFORMAT_VERSION_MAJOR < 56) #endif +#ifndef FF_API_ALLOC_OUTPUT_CONTEXT +#define FF_API_ALLOC_OUTPUT_CONTEXT (LIBAVFORMAT_VERSION_MAJOR < 56) +#endif +#ifndef FF_API_FORMAT_PARAMETERS +#define FF_API_FORMAT_PARAMETERS (LIBAVFORMAT_VERSION_MAJOR < 56) +#endif +#ifndef FF_API_NEW_STREAM +#define FF_API_NEW_STREAM (LIBAVFORMAT_VERSION_MAJOR < 56) +#endif +#ifndef FF_API_SET_PTS_INFO +#define FF_API_SET_PTS_INFO (LIBAVFORMAT_VERSION_MAJOR < 56) +#endif +#ifndef FF_API_CLOSE_INPUT_FILE +#define FF_API_CLOSE_INPUT_FILE (LIBAVFORMAT_VERSION_MAJOR < 56) +#endif +#ifndef FF_API_READ_PACKET +#define FF_API_READ_PACKET (LIBAVFORMAT_VERSION_MAJOR < 56) +#endif +#ifndef FF_API_ASS_SSA +#define FF_API_ASS_SSA (LIBAVFORMAT_VERSION_MAJOR < 56) +#endif +#ifndef FF_API_R_FRAME_RATE +#define FF_API_R_FRAME_RATE 1 +#endif #endif /* AVFORMAT_VERSION_H */ diff --git a/libavformat/vivo.c b/libavformat/vivo.c new file mode 100644 index 0000000000..e0a1814446 --- /dev/null +++ b/libavformat/vivo.c @@ -0,0 +1,313 @@ +/* + * Vivo stream demuxer + * Copyright (c) 2009 Daniel Verkamp <daniel at drv.nu> + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * @brief Vivo stream demuxer + * @author Daniel Verkamp <daniel at drv.nu> + * @sa http://wiki.multimedia.cx/index.php?title=Vivo + */ + +#include "libavutil/parseutils.h" +#include "avformat.h" +#include "internal.h" + +typedef struct VivoContext { + int version; + + int type; + int sequence; + int length; + + uint8_t text[1024 + 1]; +} VivoContext; + +static int vivo_probe(AVProbeData *p) +{ + const unsigned char *buf = p->buf; + unsigned c, length = 0; + + // stream must start with packet of type 0 and sequence number 0 + if (*buf++ != 0) + return 0; + + // read at most 2 bytes of coded length + c = *buf++; + length = c & 0x7F; + if (c & 0x80) { + c = *buf++; + length = (length << 7) | (c & 0x7F); + } + if (c & 0x80 || length > 1024 || length < 21) + return 0; + + if (memcmp(buf, "\r\nVersion:Vivo/", 15)) + return 0; + buf += 15; + + if (*buf < '0' && *buf > '2') + return 0; + + return AVPROBE_SCORE_MAX; +} + +static int vivo_get_packet_header(AVFormatContext *s) +{ + VivoContext *vivo = s->priv_data; + AVIOContext *pb = s->pb; + unsigned c, get_length = 0; + + if (url_feof(pb)) + return AVERROR_EOF; + + c = avio_r8(pb); + if (c == 0x82) { + get_length = 1; + c = avio_r8(pb); + } + + vivo->type = c >> 4; + vivo->sequence = c & 0xF; + + switch (vivo->type) { + case 0: get_length = 1; break; + case 1: vivo->length = 128; break; + case 2: get_length = 1; break; + case 3: vivo->length = 40; break; + case 4: vivo->length = 24; break; + default: + av_log(s, AV_LOG_ERROR, "unknown packet type %d\n", vivo->type); + return AVERROR_INVALIDDATA; + } + + if (get_length) { + c = avio_r8(pb); + vivo->length = c & 0x7F; + if (c & 0x80) { + c = avio_r8(pb); + vivo->length = (vivo->length << 7) | (c & 0x7F); + + if (c & 0x80) { + av_log(s, AV_LOG_ERROR, "coded length is more than two bytes\n"); + return AVERROR_INVALIDDATA; + } + } + } + + return 0; +} + +static int vivo_read_header(AVFormatContext *s) +{ + VivoContext *vivo = s->priv_data; + AVRational fps = { 1, 25}; + AVStream *ast, *vst; + unsigned char *line, *line_end, *key, *value; + long value_int; + int ret, value_used; + int64_t duration = 0; + char *end_value; + + vst = avformat_new_stream(s, NULL); + ast = avformat_new_stream(s, NULL); + if (!ast || !vst) + return AVERROR(ENOMEM); + + ast->codec->sample_rate = 8000; + + while (1) { + if ((ret = vivo_get_packet_header(s)) < 0) + return ret; + + // done reading all text header packets? + if (vivo->sequence || vivo->type) + break; + + if (vivo->length <= 1024) { + avio_read(s->pb, vivo->text, vivo->length); + vivo->text[vivo->length] = 0; + } else { + av_log(s, AV_LOG_WARNING, "too big header, skipping\n"); + avio_skip(s->pb, vivo->length); + continue; + } + + line = vivo->text; + while (*line) { + line_end = strstr(line, "\r\n"); + if (!line_end) + break; + + *line_end = 0; + key = line; + line = line_end + 2; // skip \r\n + + if (line_end == key) // skip blank lines + continue; + + value = strchr(key, ':'); + if (!value) { + av_log(s, AV_LOG_WARNING, "missing colon in key:value pair '%s'\n", + value); + continue; + } + + *value++ = 0; + + av_log(s, AV_LOG_DEBUG, "header: '%s' = '%s'\n", key, value); + + value_int = strtol(value, &end_value, 10); + value_used = 0; + if (*end_value == 0) { // valid integer + av_log(s, AV_LOG_DEBUG, "got a valid integer (%ld)\n", value_int); + value_used = 1; + if (!strcmp(key, "Duration")) { + duration = value_int; + } else if (!strcmp(key, "Width")) { + vst->codec->width = value_int; + } else if (!strcmp(key, "Height")) { + vst->codec->height = value_int; + } else if (!strcmp(key, "TimeUnitNumerator")) { + fps.num = value_int / 1000; + } else if (!strcmp(key, "TimeUnitDenominator")) { + fps.den = value_int; + } else if (!strcmp(key, "SamplingFrequency")) { + ast->codec->sample_rate = value_int; + } else if (!strcmp(key, "NominalBitrate")) { + } else if (!strcmp(key, "Length")) { + // size of file + } else { + value_used = 0; + } + } + + if (!strcmp(key, "Version")) { + if (sscanf(value, "Vivo/%d.", &vivo->version) != 1) + return AVERROR_INVALIDDATA; + value_used = 1; + } else if (!strcmp(key, "FPS")) { + AVRational tmp; + + value_used = 1; + if (!av_parse_ratio(&tmp, value, 10000, AV_LOG_WARNING, s)) + fps = av_inv_q(tmp); + } + + if (!value_used) + av_dict_set(&s->metadata, key, value, 0); + } + } + + avpriv_set_pts_info(ast, 64, 1, ast->codec->sample_rate); + avpriv_set_pts_info(vst, 64, fps.num, fps.den); + if (duration) + s->duration = av_rescale(duration, 1000, 1); + + vst->start_time = 0; + vst->codec->codec_tag = 0; + vst->codec->codec_type = AVMEDIA_TYPE_VIDEO; + + if (vivo->version == 1) { + vst->codec->codec_id = AV_CODEC_ID_H263; + ast->codec->codec_id = AV_CODEC_ID_G723_1; + ast->codec->bits_per_coded_sample = 8; + ast->codec->block_align = 24; + ast->codec->bit_rate = 6400; + } + + ast->start_time = 0; + ast->codec->codec_tag = 0; + ast->codec->codec_type = AVMEDIA_TYPE_AUDIO; + ast->codec->channels = 1; + + return 0; +} + +static int vivo_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + VivoContext *vivo = s->priv_data; + AVIOContext *pb = s->pb; + unsigned old_sequence = vivo->sequence, old_type = vivo->type; + int stream_index, ret = 0; + +restart: + + if (url_feof(pb)) + return AVERROR_EOF; + + switch (vivo->type) { + case 0: + avio_skip(pb, vivo->length); + if ((ret = vivo_get_packet_header(s)) < 0) + return ret; + goto restart; + case 1: + case 2: // video + stream_index = 0; + break; + case 3: + case 4: // audio + stream_index = 1; + break; + default: + av_log(s, AV_LOG_ERROR, "unknown packet type %d\n", vivo->type); + return AVERROR_INVALIDDATA; + } + + if ((ret = av_get_packet(pb, pkt, vivo->length)) < 0) + goto fail; + + // get next packet header + if ((ret = vivo_get_packet_header(s)) < 0) + goto fail; + + while (vivo->sequence == old_sequence && + (((vivo->type - 1) >> 1) == ((old_type - 1) >> 1))) { + if (url_feof(pb)) { + ret = AVERROR_EOF; + break; + } + + if ((ret = av_append_packet(pb, pkt, vivo->length)) < 0) + break; + + // get next packet header + if ((ret = vivo_get_packet_header(s)) < 0) + break; + } + + pkt->stream_index = stream_index; + +fail: + if (ret < 0) + av_free_packet(pkt); + return ret; +} + +AVInputFormat ff_vivo_demuxer = { + .name = "vivo", + .long_name = NULL_IF_CONFIG_SMALL("Vivo"), + .priv_data_size = sizeof(VivoContext), + .read_probe = vivo_probe, + .read_header = vivo_read_header, + .read_packet = vivo_read_packet, + .extensions = "viv", +}; diff --git a/libavformat/voc.c b/libavformat/voc.c index 1b7d499278..2a972344d3 100644 --- a/libavformat/voc.c +++ b/libavformat/voc.c @@ -2,20 +2,20 @@ * Creative Voice File common data. * Copyright (c) 2006 Aurelien Jacobs <aurel@gnuage.org> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/voc.h b/libavformat/voc.h index d2c0cb46cb..08db970ae6 100644 --- a/libavformat/voc.h +++ b/libavformat/voc.h @@ -2,20 +2,20 @@ * Creative Voice File demuxer. * Copyright (c) 2006 Aurelien Jacobs <aurel@gnuage.org> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/vocdec.c b/libavformat/vocdec.c index 2fb8440931..0b6b5a59ae 100644 --- a/libavformat/vocdec.c +++ b/libavformat/vocdec.c @@ -2,20 +2,20 @@ * Creative Voice File demuxer. * Copyright (c) 2006 Aurelien Jacobs <aurel@gnuage.org> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -75,7 +75,7 @@ ff_voc_get_packet(AVFormatContext *s, AVPacket *pkt, AVStream *st, int max_size) while (!voc->remaining_size) { type = avio_r8(pb); if (type == VOC_TYPE_EOF) - return AVERROR(EIO); + return AVERROR_EOF; voc->remaining_size = avio_rl24(pb); if (!voc->remaining_size) { if (!s->pb->seekable) @@ -150,7 +150,7 @@ ff_voc_get_packet(AVFormatContext *s, AVPacket *pkt, AVStream *st, int max_size) } } - dec->bit_rate = dec->sample_rate * dec->bits_per_coded_sample; + dec->bit_rate = dec->sample_rate * dec->channels * dec->bits_per_coded_sample; if (max_size <= 0) max_size = 2048; diff --git a/libavformat/vocenc.c b/libavformat/vocenc.c index d18eac2d95..29e9363492 100644 --- a/libavformat/vocenc.c +++ b/libavformat/vocenc.c @@ -2,20 +2,20 @@ * Creative Voice File muxer. * Copyright (c) 2006 Aurelien Jacobs <aurel@gnuage.org> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -30,6 +30,7 @@ typedef struct voc_enc_context { static int voc_write_header(AVFormatContext *s) { AVIOContext *pb = s->pb; + AVCodecContext *enc = s->streams[0]->codec; const int header_size = 26; const int version = 0x0114; @@ -37,6 +38,11 @@ static int voc_write_header(AVFormatContext *s) || s->streams[0]->codec->codec_type != AVMEDIA_TYPE_AUDIO) return AVERROR_PATCHWELCOME; + if (!enc->codec_tag && enc->codec_id != AV_CODEC_ID_PCM_U8) { + av_log(s, AV_LOG_ERROR, "unsupported codec\n"); + return AVERROR(EINVAL); + } + avio_write(pb, ff_voc_magic, sizeof(ff_voc_magic) - 1); avio_wl16(pb, header_size); avio_wl16(pb, version); @@ -52,7 +58,7 @@ static int voc_write_packet(AVFormatContext *s, AVPacket *pkt) AVIOContext *pb = s->pb; if (!voc->param_written) { - if (enc->codec_tag > 0xFF) { + if (enc->codec_tag > 3) { avio_w8(pb, VOC_TYPE_NEW_VOICE_DATA); avio_wl24(pb, pkt->size + 12); avio_wl32(pb, enc->sample_rate); @@ -64,13 +70,13 @@ static int voc_write_packet(AVFormatContext *s, AVPacket *pkt) if (s->streams[0]->codec->channels > 1) { avio_w8(pb, VOC_TYPE_EXTENDED); avio_wl24(pb, 4); - avio_wl16(pb, 65536-256000000/(enc->sample_rate*enc->channels)); + avio_wl16(pb, 65536-(256000000 + enc->sample_rate*enc->channels/2)/(enc->sample_rate*enc->channels)); avio_w8(pb, enc->codec_tag); avio_w8(pb, enc->channels - 1); } avio_w8(pb, VOC_TYPE_VOICE_DATA); avio_wl24(pb, pkt->size + 2); - avio_w8(pb, 256 - 1000000 / enc->sample_rate); + avio_w8(pb, 256 - (1000000 + enc->sample_rate/2) / enc->sample_rate); avio_w8(pb, enc->codec_tag); } voc->param_written = 1; @@ -95,7 +101,7 @@ AVOutputFormat ff_voc_muxer = { .mime_type = "audio/x-voc", .extensions = "voc", .priv_data_size = sizeof(VocEncContext), - .audio_codec = AV_CODEC_ID_PCM_U8, + .audio_codec = AV_CODEC_ID_PCM_S16LE, .video_codec = AV_CODEC_ID_NONE, .write_header = voc_write_header, .write_packet = voc_write_packet, diff --git a/libavformat/vorbiscomment.c b/libavformat/vorbiscomment.c index 56936d7666..f17a0c1d13 100644 --- a/libavformat/vorbiscomment.c +++ b/libavformat/vorbiscomment.c @@ -2,20 +2,20 @@ * VorbisComment writer * Copyright (c) 2009 James Darnley * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -34,6 +34,7 @@ const AVMetadataConv ff_vorbiscomment_metadata_conv[] = { { "ALBUMARTIST", "album_artist"}, { "TRACKNUMBER", "track" }, { "DISCNUMBER", "disc" }, + { "DESCRIPTION", "comment" }, { 0 } }; diff --git a/libavformat/vorbiscomment.h b/libavformat/vorbiscomment.h index 95e1a5676f..4e631e3553 100644 --- a/libavformat/vorbiscomment.h +++ b/libavformat/vorbiscomment.h @@ -2,20 +2,20 @@ * VorbisComment writer * Copyright (c) 2009 James Darnley * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/vplayerdec.c b/libavformat/vplayerdec.c new file mode 100644 index 0000000000..73a48dbdc5 --- /dev/null +++ b/libavformat/vplayerdec.c @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2012 Clément Bœsch + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * VPlayer subtitles format demuxer + */ + +#include "avformat.h" +#include "internal.h" +#include "subtitles.h" + +typedef struct { + FFDemuxSubtitlesQueue q; +} VPlayerContext; + +static int vplayer_probe(AVProbeData *p) +{ + char c; + const unsigned char *ptr = p->buf; + + if (sscanf(ptr, "%*d:%*d:%*d.%*d%c", &c) == 1 && strchr(": =", c)) + return AVPROBE_SCORE_MAX; + return 0; +} + +static int64_t read_ts(char **line) +{ + char c; + int hh, mm, ss, ms, len; + + if (sscanf(*line, "%d:%d:%d.%d%c%n", + &hh, &mm, &ss, &ms, &c, &len) >= 5) { + *line += len; + return (hh*3600LL + mm*60LL + ss) * 100LL + ms; + } + return AV_NOPTS_VALUE; +} + +static int vplayer_read_header(AVFormatContext *s) +{ + VPlayerContext *vplayer = s->priv_data; + AVStream *st = avformat_new_stream(s, NULL); + + if (!st) + return AVERROR(ENOMEM); + avpriv_set_pts_info(st, 64, 1, 100); + st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE; + st->codec->codec_id = AV_CODEC_ID_VPLAYER; + + while (!url_feof(s->pb)) { + char line[4096]; + char *p = line; + const int64_t pos = avio_tell(s->pb); + int len = ff_get_line(s->pb, line, sizeof(line)); + int64_t pts_start; + + if (!len) + break; + + line[strcspn(line, "\r\n")] = 0; + + pts_start = read_ts(&p); + if (pts_start != AV_NOPTS_VALUE) { + AVPacket *sub; + + sub = ff_subtitles_queue_insert(&vplayer->q, p, strlen(p), 0); + if (!sub) + return AVERROR(ENOMEM); + sub->pos = pos; + sub->pts = pts_start; + sub->duration = -1; + } + } + + ff_subtitles_queue_finalize(&vplayer->q); + return 0; +} + +static int vplayer_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + VPlayerContext *vplayer = s->priv_data; + return ff_subtitles_queue_read_packet(&vplayer->q, pkt); +} + +static int vplayer_read_seek(AVFormatContext *s, int stream_index, + int64_t min_ts, int64_t ts, int64_t max_ts, int flags) +{ + VPlayerContext *vplayer = s->priv_data; + return ff_subtitles_queue_seek(&vplayer->q, s, stream_index, + min_ts, ts, max_ts, flags); +} + +static int vplayer_read_close(AVFormatContext *s) +{ + VPlayerContext *vplayer = s->priv_data; + ff_subtitles_queue_clean(&vplayer->q); + return 0; +} + +AVInputFormat ff_vplayer_demuxer = { + .name = "vplayer", + .long_name = NULL_IF_CONFIG_SMALL("VPlayer subtitles"), + .priv_data_size = sizeof(VPlayerContext), + .read_probe = vplayer_probe, + .read_header = vplayer_read_header, + .read_packet = vplayer_read_packet, + .read_seek2 = vplayer_read_seek, + .read_close = vplayer_read_close, + .extensions = "txt", +}; diff --git a/libavformat/vqf.c b/libavformat/vqf.c index a43829b855..4cd0a6deec 100644 --- a/libavformat/vqf.c +++ b/libavformat/vqf.c @@ -2,20 +2,20 @@ * VQF demuxer * Copyright (c) 2009 Vitor Sessak * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -132,6 +132,11 @@ static int vqf_read_header(AVFormatContext *s) rate_flag = AV_RB32(comm_chunk + 8); avio_skip(s->pb, len-12); + if (st->codec->channels <= 0) { + av_log(s, AV_LOG_ERROR, "Invalid number of channels\n"); + return AVERROR_INVALIDDATA; + } + st->codec->bit_rate = read_bitrate*1000; break; case MKTAG('D','S','I','Z'): // size of compressed data @@ -158,7 +163,7 @@ static int vqf_read_header(AVFormatContext *s) header_size -= len; - } while (header_size >= 0); + } while (header_size >= 0 && !url_feof(s->pb)); switch (rate_flag) { case -1: @@ -215,9 +220,8 @@ static int vqf_read_header(AVFormatContext *s) avpriv_set_pts_info(st, 64, size, st->codec->sample_rate); /* put first 12 bytes of COMM chunk in extradata */ - if (!(st->codec->extradata = av_malloc(12 + FF_INPUT_BUFFER_PADDING_SIZE))) + if (ff_alloc_extradata(st->codec, 12)) return AVERROR(ENOMEM); - st->codec->extradata_size = 12; memcpy(st->codec->extradata, comm_chunk, 12); ff_metadata_conv_ctx(s, NULL, vqf_metadata_conv); diff --git a/libavformat/w64.c b/libavformat/w64.c new file mode 100644 index 0000000000..ef2d90a600 --- /dev/null +++ b/libavformat/w64.c @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2009 Daniel Verkamp + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "w64.h" + +const uint8_t ff_w64_guid_riff[16] = { + 'r', 'i', 'f', 'f', + 0x2E, 0x91, 0xCF, 0x11, 0xA5, 0xD6, 0x28, 0xDB, 0x04, 0xC1, 0x00, 0x00 +}; + +const uint8_t ff_w64_guid_wave[16] = { + 'w', 'a', 'v', 'e', + 0xF3, 0xAC, 0xD3, 0x11, 0x8C, 0xD1, 0x00, 0xC0, 0x4F, 0x8E, 0xDB, 0x8A +}; + +const uint8_t ff_w64_guid_fmt [16] = { + 'f', 'm', 't', ' ', + 0xF3, 0xAC, 0xD3, 0x11, 0x8C, 0xD1, 0x00, 0xC0, 0x4F, 0x8E, 0xDB, 0x8A +}; + +const uint8_t ff_w64_guid_fact[16] = { 'f', 'a', 'c', 't', + 0xF3, 0xAC, 0xD3, 0x11, 0x8C, 0xD1, 0x00, 0xC0, 0x4F, 0x8E, 0xDB, 0x8A +}; + +const uint8_t ff_w64_guid_data[16] = { + 'd', 'a', 't', 'a', + 0xF3, 0xAC, 0xD3, 0x11, 0x8C, 0xD1, 0x00, 0xC0, 0x4F, 0x8E, 0xDB, 0x8A +}; + +const uint8_t ff_w64_guid_summarylist[16] = { + 0xBC, 0x94, 0x5F, 0x92, + 0x5A, 0x52, 0xD2, 0x11, 0x86, 0xDC, 0x00, 0xC0, 0x4F, 0x8E, 0xDB, 0x8A +}; diff --git a/libavformat/w64.h b/libavformat/w64.h new file mode 100644 index 0000000000..0ec3fa9141 --- /dev/null +++ b/libavformat/w64.h @@ -0,0 +1,31 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVFORMAT_W64_H +#define AVFORMAT_W64_H + +#include <stdint.h> + +extern const uint8_t ff_w64_guid_riff[16]; +extern const uint8_t ff_w64_guid_wave[16]; +extern const uint8_t ff_w64_guid_fmt [16]; +extern const uint8_t ff_w64_guid_fact[16]; +extern const uint8_t ff_w64_guid_data[16]; +extern const uint8_t ff_w64_guid_summarylist[16]; + +#endif /* AVFORMAT_W64_H */ diff --git a/libavformat/wavdec.c b/libavformat/wavdec.c index a3471fdf96..202683bb5d 100644 --- a/libavformat/wavdec.c +++ b/libavformat/wavdec.c @@ -6,25 +6,26 @@ * RF64 demuxer * Copyright (c) 2009 Daniel Verkamp * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "libavutil/avassert.h" #include "libavutil/dict.h" +#include "libavutil/intreadwrite.h" #include "libavutil/log.h" #include "libavutil/mathematics.h" #include "libavutil/opt.h" @@ -35,10 +36,25 @@ #include "metadata.h" #include "pcm.h" #include "riff.h" +#include "w64.h" +#include "spdif.h" typedef struct WAVDemuxContext { + const AVClass *class; int64_t data_end; int w64; + int64_t smv_data_ofs; + int smv_block_size; + int smv_frames_per_jpeg; + int smv_block; + int smv_last_stream; + int smv_eof; + int audio_eof; + int ignore_length; + int spdif; + int smv_cur_pt; + int smv_given_first; + int unaligned; // e.g. if an odd number of bytes ID3 tag was prepended } WAVDemuxContext; #if CONFIG_WAV_DEMUXER @@ -49,25 +65,27 @@ static int64_t next_tag(AVIOContext *pb, uint32_t *tag) return avio_rl32(pb); } -/* RIFF chunks are always on a even offset. */ -static int64_t wav_seek_tag(AVIOContext *s, int64_t offset, int whence) +/* RIFF chunks are always at even offsets relative to where they start. */ +static int64_t wav_seek_tag(WAVDemuxContext * wav, AVIOContext *s, int64_t offset, int whence) { - return avio_seek(s, offset + (offset & 1), whence); + offset += offset < INT64_MAX && offset + wav->unaligned & 1; + + return avio_seek(s, offset, whence); } /* return the size of the found tag */ -static int64_t find_tag(AVIOContext *pb, uint32_t tag1) +static int64_t find_tag(WAVDemuxContext * wav, AVIOContext *pb, uint32_t tag1) { unsigned int tag; int64_t size; for (;;) { - if (pb->eof_reached) - return -1; + if (url_feof(pb)) + return AVERROR_EOF; size = next_tag(pb, &tag); if (tag == tag1) break; - wav_seek_tag(pb, size, SEEK_CUR); + wav_seek_tag(wav, pb, size, SEEK_CUR); } return size; } @@ -90,6 +108,14 @@ static int wav_probe(AVProbeData *p) return 0; } +static void handle_stream_probing(AVStream *st) +{ + if (st->codec->codec_id == AV_CODEC_ID_PCM_S16LE) { + st->request_probe = AVPROBE_SCORE_EXTENSION; + st->probe_packets = FFMIN(st->probe_packets, 4); + } +} + static int wav_parse_fmt_tag(AVFormatContext *s, int64_t size, AVStream **st) { AVIOContext *pb = s->pb; @@ -103,7 +129,9 @@ static int wav_parse_fmt_tag(AVFormatContext *s, int64_t size, AVStream **st) ret = ff_get_wav_header(pb, (*st)->codec, size); if (ret < 0) return ret; - (*st)->need_parsing = AVSTREAM_PARSE_FULL; + handle_stream_probing(*st); + + (*st)->need_parsing = AVSTREAM_PARSE_FULL_RAW; avpriv_set_pts_info(*st, 64, 1, (*st)->codec->sample_rate); @@ -165,7 +193,7 @@ static int wav_parse_bext_tag(AVFormatContext *s, int64_t size) /* extended UMID */ snprintf(temp, sizeof(temp), "0x%016"PRIX64"%016"PRIX64"%016"PRIX64"%016"PRIX64 - "0x%016"PRIX64"%016"PRIX64"%016"PRIX64"%016"PRIX64, + "%016"PRIX64"%016"PRIX64"%016"PRIX64"%016"PRIX64, umid_parts[0], umid_parts[1], umid_parts[2], umid_parts[3], umid_parts[4], umid_parts[5], @@ -220,6 +248,10 @@ static int wav_read_header(AVFormatContext *s) int ret, got_fmt = 0; int64_t next_tag_ofs, data_ofs = -1; + wav->unaligned = avio_tell(s->pb) & 1; + + wav->smv_data_ofs = -1; + /* check RIFF header */ tag = avio_rl32(pb); @@ -235,7 +267,7 @@ static int wav_read_header(AVFormatContext *s) if (avio_rl32(pb) != MKTAG('d', 's', '6', '4')) return AVERROR_INVALIDDATA; size = avio_rl32(pb); - if (size < 16) + if (size < 24) return AVERROR_INVALIDDATA; avio_rl64(pb); /* RIFF size */ @@ -248,20 +280,22 @@ static int wav_read_header(AVFormatContext *s) data_size, sample_count); return AVERROR_INVALIDDATA; } - avio_skip(pb, size - 16); /* skip rest of ds64 chunk */ + avio_skip(pb, size - 24); /* skip rest of ds64 chunk */ + } for (;;) { + AVStream *vst; size = next_tag(pb, &tag); next_tag_ofs = avio_tell(pb) + size; - if (pb->eof_reached) + if (url_feof(pb)) break; switch (tag) { case MKTAG('f', 'm', 't', ' '): /* only parse the first 'fmt ' tag found */ - if (!got_fmt && (ret = wav_parse_fmt_tag(s, size, &st) < 0)) { + if (!got_fmt && (ret = wav_parse_fmt_tag(s, size, &st)) < 0) { return ret; } else if (got_fmt) av_log(s, AV_LOG_WARNING, "found more than one 'fmt ' tag\n"); @@ -298,22 +332,62 @@ static int wav_read_header(AVFormatContext *s) if ((ret = wav_parse_bext_tag(s, size)) < 0) return ret; break; + case MKTAG('S','M','V','0'): + if (!got_fmt) { + av_log(s, AV_LOG_ERROR, "found no 'fmt ' tag before the 'SMV0' tag\n"); + return AVERROR_INVALIDDATA; + } + // SMV file, a wav file with video appended. + if (size != MKTAG('0','2','0','0')) { + av_log(s, AV_LOG_ERROR, "Unknown SMV version found\n"); + goto break_loop; + } + av_log(s, AV_LOG_DEBUG, "Found SMV data\n"); + wav->smv_given_first = 0; + vst = avformat_new_stream(s, NULL); + if (!vst) + return AVERROR(ENOMEM); + avio_r8(pb); + vst->id = 1; + vst->codec->codec_type = AVMEDIA_TYPE_VIDEO; + vst->codec->codec_id = AV_CODEC_ID_SMVJPEG; + vst->codec->width = avio_rl24(pb); + vst->codec->height = avio_rl24(pb); + if (ff_alloc_extradata(vst->codec, 4)) { + av_log(s, AV_LOG_ERROR, "Could not allocate extradata.\n"); + return AVERROR(ENOMEM); + } + size = avio_rl24(pb); + wav->smv_data_ofs = avio_tell(pb) + (size - 5) * 3; + avio_rl24(pb); + wav->smv_block_size = avio_rl24(pb); + avpriv_set_pts_info(vst, 32, 1, avio_rl24(pb)); + vst->duration = avio_rl24(pb); + avio_rl24(pb); + avio_rl24(pb); + wav->smv_frames_per_jpeg = avio_rl24(pb); + if (wav->smv_frames_per_jpeg > 65536) { + av_log(s, AV_LOG_ERROR, "too many frames per jpeg\n"); + return AVERROR_INVALIDDATA; + } + AV_WL32(vst->codec->extradata, wav->smv_frames_per_jpeg); + wav->smv_cur_pt = 0; + goto break_loop; case MKTAG('L', 'I', 'S', 'T'): if (size < 4) { - av_log(s, AV_LOG_ERROR, "too short LIST"); + av_log(s, AV_LOG_ERROR, "too short LIST tag\n"); return AVERROR_INVALIDDATA; } switch (avio_rl32(pb)) { case MKTAG('I', 'N', 'F', 'O'): - if ((ret = ff_read_riff_info(s, size - 4)) < 0) - return ret; + ff_read_riff_info(s, size - 4); } break; } /* seek to next tag unless we know that we'll run into EOF */ if ((avio_size(pb) > 0 && next_tag_ofs >= avio_size(pb)) || - wav_seek_tag(pb, next_tag_ofs, SEEK_SET) < 0) { + wav_seek_tag(wav, pb, next_tag_ofs, SEEK_SET) < 0) { break; } } @@ -326,11 +400,15 @@ break_loop: avio_seek(pb, data_ofs, SEEK_SET); - if (!sample_count && st->codec->channels && - av_get_bits_per_sample(st->codec->codec_id)) - sample_count = (data_size << 3) / - (st->codec->channels * - (uint64_t)av_get_bits_per_sample(st->codec->codec_id)); + if (!sample_count || av_get_exact_bits_per_sample(st->codec->codec_id) > 0) + if ( st->codec->channels + && data_size + && av_get_bits_per_sample(st->codec->codec_id) + && wav->data_end <= avio_size(pb)) + sample_count = (data_size << 3) + / + (st->codec->channels * (uint64_t)av_get_bits_per_sample(st->codec->codec_id)); + if (sample_count) st->duration = sample_count; @@ -349,23 +427,18 @@ static int64_t find_guid(AVIOContext *pb, const uint8_t guid1[16]) uint8_t guid[16]; int64_t size; - while (!pb->eof_reached) { + while (!url_feof(pb)) { avio_read(pb, guid, 16); size = avio_rl64(pb); if (size <= 24) - return -1; + return AVERROR_INVALIDDATA; if (!memcmp(guid, guid1, 16)) return size; avio_skip(pb, FFALIGN(size, INT64_C(8)) - 24); } - return -1; + return AVERROR_EOF; } -static const uint8_t guid_data[16] = { - 'd', 'a', 't', 'a', - 0xF3, 0xAC, 0xD3, 0x11,0x8C, 0xD1, 0x00, 0xC0, 0x4F, 0x8E, 0xDB, 0x8A -}; - #define MAX_SIZE 4096 static int wav_read_packet(AVFormatContext *s, AVPacket *pkt) @@ -375,16 +448,84 @@ static int wav_read_packet(AVFormatContext *s, AVPacket *pkt) AVStream *st; WAVDemuxContext *wav = s->priv_data; + if (CONFIG_SPDIF_DEMUXER && wav->spdif == 0 && + s->streams[0]->codec->codec_tag == 1) { + enum AVCodecID codec; + ret = ff_spdif_probe(s->pb->buffer, s->pb->buf_end - s->pb->buffer, + &codec); + if (ret > AVPROBE_SCORE_EXTENSION) { + s->streams[0]->codec->codec_id = codec; + wav->spdif = 1; + } else { + wav->spdif = -1; + } + } + if (CONFIG_SPDIF_DEMUXER && wav->spdif == 1) + return ff_spdif_read_packet(s, pkt); + + if (wav->smv_data_ofs > 0) { + int64_t audio_dts, video_dts; +smv_retry: + audio_dts = (int32_t)s->streams[0]->cur_dts; + video_dts = (int32_t)s->streams[1]->cur_dts; + + if (audio_dts != AV_NOPTS_VALUE && video_dts != AV_NOPTS_VALUE) { + /*We always return a video frame first to get the pixel format first*/ + wav->smv_last_stream = wav->smv_given_first ? + av_compare_ts(video_dts, s->streams[1]->time_base, + audio_dts, s->streams[0]->time_base) > 0 : 0; + wav->smv_given_first = 1; + } + wav->smv_last_stream = !wav->smv_last_stream; + wav->smv_last_stream |= wav->audio_eof; + wav->smv_last_stream &= !wav->smv_eof; + if (wav->smv_last_stream) { + uint64_t old_pos = avio_tell(s->pb); + uint64_t new_pos = wav->smv_data_ofs + + wav->smv_block * wav->smv_block_size; + if (avio_seek(s->pb, new_pos, SEEK_SET) < 0) { + ret = AVERROR_EOF; + goto smv_out; + } + size = avio_rl24(s->pb); + ret = av_get_packet(s->pb, pkt, size); + if (ret < 0) + goto smv_out; + pkt->pos -= 3; + pkt->pts = wav->smv_block * wav->smv_frames_per_jpeg + wav->smv_cur_pt; + wav->smv_cur_pt++; + if (wav->smv_frames_per_jpeg > 0) + wav->smv_cur_pt %= wav->smv_frames_per_jpeg; + if (!wav->smv_cur_pt) + wav->smv_block++; + + pkt->stream_index = 1; +smv_out: + avio_seek(s->pb, old_pos, SEEK_SET); + if (ret == AVERROR_EOF) { + wav->smv_eof = 1; + goto smv_retry; + } + return ret; + } + } + st = s->streams[0]; left = wav->data_end - avio_tell(s->pb); + if (wav->ignore_length) + left = INT_MAX; if (left <= 0) { if (CONFIG_W64_DEMUXER && wav->w64) - left = find_guid(s->pb, guid_data) - 24; + left = find_guid(s->pb, ff_w64_guid_data) - 24; else - left = find_tag(s->pb, MKTAG('d', 'a', 't', 'a')); - if (left < 0) + left = find_tag(wav, s->pb, MKTAG('d', 'a', 't', 'a')); + if (left < 0) { + wav->audio_eof = 1; + if (wav->smv_data_ofs > 0 && !wav->smv_eof) + goto smv_retry; return AVERROR_EOF; + } wav->data_end = avio_tell(s->pb) + left; } @@ -406,7 +547,21 @@ static int wav_read_packet(AVFormatContext *s, AVPacket *pkt) static int wav_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp, int flags) { + WAVDemuxContext *wav = s->priv_data; AVStream *st; + wav->smv_eof = 0; + wav->audio_eof = 0; + if (wav->smv_data_ofs > 0) { + int64_t smv_timestamp = timestamp; + if (stream_index == 0) + smv_timestamp = av_rescale_q(timestamp, s->streams[0]->time_base, s->streams[1]->time_base); + else + timestamp = av_rescale_q(smv_timestamp, s->streams[1]->time_base, s->streams[0]->time_base); + if (wav->smv_frames_per_jpeg > 0) { + wav->smv_block = smv_timestamp / wav->smv_frames_per_jpeg; + wav->smv_cur_pt = smv_timestamp % wav->smv_frames_per_jpeg; + } + } st = s->streams[0]; switch (st->codec->codec_id) { @@ -422,6 +577,19 @@ static int wav_read_seek(AVFormatContext *s, return ff_pcm_read_seek(s, stream_index, timestamp, flags); } +#define OFFSET(x) offsetof(WAVDemuxContext, x) +#define DEC AV_OPT_FLAG_DECODING_PARAM +static const AVOption demux_options[] = { + { "ignore_length", "Ignore length", OFFSET(ignore_length), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, DEC }, + { NULL }, +}; + +static const AVClass wav_demuxer_class = { + .class_name = "WAV demuxer", + .item_name = av_default_item_name, + .option = demux_options, + .version = LIBAVUTIL_VERSION_INT, +}; AVInputFormat ff_wav_demuxer = { .name = "wav", .long_name = NULL_IF_CONFIG_SMALL("WAV / WAVE (Waveform Audio)"), @@ -432,31 +600,17 @@ AVInputFormat ff_wav_demuxer = { .read_seek = wav_read_seek, .flags = AVFMT_GENERIC_INDEX, .codec_tag = (const AVCodecTag * const []) { ff_codec_wav_tags, 0 }, + .priv_class = &wav_demuxer_class, }; #endif /* CONFIG_WAV_DEMUXER */ #if CONFIG_W64_DEMUXER -static const uint8_t guid_riff[16] = { - 'r', 'i', 'f', 'f', - 0x2E, 0x91, 0xCF, 0x11,0xA5, 0xD6, 0x28, 0xDB, 0x04, 0xC1, 0x00, 0x00 -}; - -static const uint8_t guid_wave[16] = { - 'w', 'a', 'v', 'e', - 0xF3, 0xAC, 0xD3, 0x11,0x8C, 0xD1, 0x00, 0xC0, 0x4F, 0x8E, 0xDB, 0x8A -}; - -static const uint8_t guid_fmt[16] = { - 'f', 'm', 't', ' ', - 0xF3, 0xAC, 0xD3, 0x11,0x8C, 0xD1, 0x00, 0xC0, 0x4F, 0x8E, 0xDB, 0x8A -}; - static int w64_probe(AVProbeData *p) { if (p->buf_size <= 40) return 0; - if (!memcmp(p->buf, guid_riff, 16) && - !memcmp(p->buf + 24, guid_wave, 16)) + if (!memcmp(p->buf, ff_w64_guid_riff, 16) && + !memcmp(p->buf + 24, ff_w64_guid_wave, 16)) return AVPROBE_SCORE_MAX; else return 0; @@ -464,7 +618,7 @@ static int w64_probe(AVProbeData *p) static int w64_read_header(AVFormatContext *s) { - int64_t size; + int64_t size, data_ofs = 0; AVIOContext *pb = s->pb; WAVDemuxContext *wav = s->priv_data; AVStream *st; @@ -472,7 +626,7 @@ static int w64_read_header(AVFormatContext *s) int ret; avio_read(pb, guid, 16); - if (memcmp(guid, guid_riff, 16)) + if (memcmp(guid, ff_w64_guid_riff, 16)) return AVERROR_INVALIDDATA; /* riff + wave + fmt + sizes */ @@ -480,38 +634,91 @@ static int w64_read_header(AVFormatContext *s) return AVERROR_INVALIDDATA; avio_read(pb, guid, 16); - if (memcmp(guid, guid_wave, 16)) { + if (memcmp(guid, ff_w64_guid_wave, 16)) { av_log(s, AV_LOG_ERROR, "could not find wave guid\n"); return AVERROR_INVALIDDATA; } - size = find_guid(pb, guid_fmt); - if (size < 0) { - av_log(s, AV_LOG_ERROR, "could not find fmt guid\n"); - return AVERROR_INVALIDDATA; - } + wav->w64 = 1; st = avformat_new_stream(s, NULL); if (!st) return AVERROR(ENOMEM); - /* subtract chunk header size - normal wav file doesn't count it */ - ret = ff_get_wav_header(pb, st->codec, size - 24); - if (ret < 0) - return ret; - avio_skip(pb, FFALIGN(size, INT64_C(8)) - size); + while (!url_feof(pb)) { + if (avio_read(pb, guid, 16) != 16) + break; + size = avio_rl64(pb); + if (size <= 24 || INT64_MAX - size < avio_tell(pb)) + return AVERROR_INVALIDDATA; + + if (!memcmp(guid, ff_w64_guid_fmt, 16)) { + /* subtract chunk header size - normal wav file doesn't count it */ + ret = ff_get_wav_header(pb, st->codec, size - 24); + if (ret < 0) + return ret; + avio_skip(pb, FFALIGN(size, INT64_C(8)) - size); - st->need_parsing = AVSTREAM_PARSE_FULL; + avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate); + } else if (!memcmp(guid, ff_w64_guid_fact, 16)) { + int64_t samples; - avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate); + samples = avio_rl64(pb); + if (samples > 0) + st->duration = samples; + } else if (!memcmp(guid, ff_w64_guid_data, 16)) { + wav->data_end = avio_tell(pb) + size - 24; - size = find_guid(pb, guid_data); - if (size < 0) { - av_log(s, AV_LOG_ERROR, "could not find data guid\n"); - return AVERROR_INVALIDDATA; + data_ofs = avio_tell(pb); + if (!pb->seekable) + break; + + avio_skip(pb, size - 24); + } else if (!memcmp(guid, ff_w64_guid_summarylist, 16)) { + int64_t start, end, cur; + uint32_t count, chunk_size, i; + + start = avio_tell(pb); + end = start + FFALIGN(size, INT64_C(8)) - 24; + count = avio_rl32(pb); + + for (i = 0; i < count; i++) { + char chunk_key[5], *value; + + if (url_feof(pb) || (cur = avio_tell(pb)) < 0 || cur > end - 8 /* = tag + size */) + break; + + chunk_key[4] = 0; + avio_read(pb, chunk_key, 4); + chunk_size = avio_rl32(pb); + + value = av_mallocz(chunk_size + 1); + if (!value) + return AVERROR(ENOMEM); + + ret = avio_get_str16le(pb, chunk_size, value, chunk_size); + avio_skip(pb, chunk_size - ret); + + av_dict_set(&s->metadata, chunk_key, value, AV_DICT_DONT_STRDUP_VAL); + } + + avio_skip(pb, end - avio_tell(pb)); + } else { + av_log(s, AV_LOG_DEBUG, "unknown guid: "FF_PRI_GUID"\n", FF_ARG_GUID(guid)); + avio_skip(pb, FFALIGN(size, INT64_C(8)) - 24); + } } - wav->data_end = avio_tell(pb) + size - 24; - wav->w64 = 1; + + if (!data_ofs) + return AVERROR_EOF; + + ff_metadata_conv_ctx(s, NULL, wav_metadata_conv); + ff_metadata_conv_ctx(s, NULL, ff_riff_info_conv); + + handle_stream_probing(st); + st->need_parsing = AVSTREAM_PARSE_FULL_RAW; + + avio_seek(pb, data_ofs, SEEK_SET); return 0; } diff --git a/libavformat/wavenc.c b/libavformat/wavenc.c index 86ed557802..fea38cf326 100644 --- a/libavformat/wavenc.c +++ b/libavformat/wavenc.c @@ -2,20 +2,26 @@ * WAV muxer * Copyright (c) 2001, 2002 Fabrice Bellard * - * This file is part of Libav. + * Sony Wave64 muxer + * Copyright (c) 2012 Paul B Mahol * - * Libav is free software; you can redistribute it and/or + * WAV muxer RF64 support + * Copyright (c) 2013 Daniel Verkamp <daniel@drv.nu> + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -33,16 +39,23 @@ #include "internal.h" #include "riff.h" +#define RF64_AUTO (-1) +#define RF64_NEVER 0 +#define RF64_ALWAYS 1 + typedef struct WAVMuxContext { const AVClass *class; int64_t data; int64_t fact_pos; + int64_t ds64; int64_t minpts; int64_t maxpts; int last_duration; int write_bext; + int rf64; } WAVMuxContext; +#if CONFIG_WAV_MUXER static inline void bwf_write_bext_string(AVFormatContext *s, const char *key, int maxlen) { AVDictionaryEntry *tag; @@ -103,10 +116,24 @@ static int wav_write_header(AVFormatContext *s) AVIOContext *pb = s->pb; int64_t fmt; - ffio_wfourcc(pb, "RIFF"); - avio_wl32(pb, 0); /* file length */ + if (wav->rf64 == RF64_ALWAYS) { + ffio_wfourcc(pb, "RF64"); + avio_wl32(pb, -1); /* RF64 chunk size: use size in ds64 */ + } else { + ffio_wfourcc(pb, "RIFF"); + avio_wl32(pb, 0); /* file length */ + } + ffio_wfourcc(pb, "WAVE"); + if (wav->rf64 != RF64_NEVER) { + /* write empty ds64 chunk or JUNK chunk to reserve space for ds64 */ + ffio_wfourcc(pb, wav->rf64 == RF64_ALWAYS ? "ds64" : "JUNK"); + avio_wl32(pb, 28); /* chunk size */ + wav->ds64 = avio_tell(pb); + ffio_fill(pb, 0, 28); + } + /* format header */ fmt = ff_start_tag(pb, "fmt "); if (ff_put_wav_header(pb, s->streams[0]->codec) < 0) { @@ -159,29 +186,63 @@ static int wav_write_trailer(AVFormatContext *s) { AVIOContext *pb = s->pb; WAVMuxContext *wav = s->priv_data; - int64_t file_size; + int64_t file_size, data_size; + int64_t number_of_samples = 0; + int rf64 = 0; avio_flush(pb); if (s->pb->seekable) { - ff_end_tag(pb, wav->data); - /* update file size */ file_size = avio_tell(pb); - avio_seek(pb, 4, SEEK_SET); - avio_wl32(pb, (uint32_t)(file_size - 8)); - avio_seek(pb, file_size, SEEK_SET); + data_size = file_size - wav->data; + if (wav->rf64 == RF64_ALWAYS || (wav->rf64 == RF64_AUTO && file_size - 8 > UINT32_MAX)) { + rf64 = 1; + } else { + avio_seek(pb, 4, SEEK_SET); + avio_wl32(pb, (uint32_t)(file_size - 8)); + avio_seek(pb, file_size, SEEK_SET); - avio_flush(pb); + ff_end_tag(pb, wav->data); + avio_flush(pb); + } + + number_of_samples = av_rescale(wav->maxpts - wav->minpts + wav->last_duration, + s->streams[0]->codec->sample_rate * (int64_t)s->streams[0]->time_base.num, + s->streams[0]->time_base.den); if(s->streams[0]->codec->codec_tag != 0x01) { /* Update num_samps in fact chunk */ - int number_of_samples; - number_of_samples = av_rescale(wav->maxpts - wav->minpts + wav->last_duration, - s->streams[0]->codec->sample_rate * (int64_t)s->streams[0]->time_base.num, - s->streams[0]->time_base.den); avio_seek(pb, wav->fact_pos, SEEK_SET); - avio_wl32(pb, number_of_samples); + if (rf64 || (wav->rf64 == RF64_AUTO && number_of_samples > UINT32_MAX)) { + rf64 = 1; + avio_wl32(pb, -1); + } else { + avio_wl32(pb, number_of_samples); + avio_seek(pb, file_size, SEEK_SET); + avio_flush(pb); + } + } + + if (rf64) { + /* overwrite RIFF with RF64 */ + avio_seek(pb, 0, SEEK_SET); + ffio_wfourcc(pb, "RF64"); + avio_wl32(pb, -1); + + /* write ds64 chunk (overwrite JUNK if rf64 == RF64_AUTO) */ + avio_seek(pb, wav->ds64 - 8, SEEK_SET); + ffio_wfourcc(pb, "ds64"); + avio_wl32(pb, 28); /* ds64 chunk size */ + avio_wl64(pb, file_size - 8); /* RF64 chunk size */ + avio_wl64(pb, data_size); /* data chunk size */ + avio_wl64(pb, number_of_samples); /* fact chunk number of samples */ + avio_wl32(pb, 0); /* number of table entries for non-'data' chunks */ + + /* write -1 in data chunk size */ + avio_seek(pb, wav->data - 4, SEEK_SET); + avio_wl32(pb, -1); + avio_seek(pb, file_size, SEEK_SET); avio_flush(pb); } @@ -193,6 +254,10 @@ static int wav_write_trailer(AVFormatContext *s) #define ENC AV_OPT_FLAG_ENCODING_PARAM static const AVOption options[] = { { "write_bext", "Write BEXT chunk.", OFFSET(write_bext), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, ENC }, + { "rf64", "Use RF64 header rather than RIFF for large files.", OFFSET(rf64), AV_OPT_TYPE_INT, { .i64 = RF64_NEVER },-1, 1, ENC, "rf64" }, + { "auto", "Write RF64 header if file grows large enough.", 0, AV_OPT_TYPE_CONST, { .i64 = RF64_AUTO }, 0, 0, ENC, "rf64" }, + { "always", "Always write RF64 header regardless of file size.", 0, AV_OPT_TYPE_CONST, { .i64 = RF64_ALWAYS }, 0, 0, ENC, "rf64" }, + { "never", "Never write RF64 header regardless of file size.", 0, AV_OPT_TYPE_CONST, { .i64 = RF64_NEVER }, 0, 0, ENC, "rf64" }, { NULL }, }; @@ -218,3 +283,101 @@ AVOutputFormat ff_wav_muxer = { .codec_tag = (const AVCodecTag* const []){ ff_codec_wav_tags, 0 }, .priv_class = &wav_muxer_class, }; +#endif /* CONFIG_WAV_MUXER */ + +#if CONFIG_W64_MUXER +#include "w64.h" + +static void start_guid(AVIOContext *pb, const uint8_t *guid, int64_t *pos) +{ + *pos = avio_tell(pb); + + avio_write(pb, guid, 16); + avio_wl64(pb, INT64_MAX); +} + +static void end_guid(AVIOContext *pb, int64_t start) +{ + int64_t end, pos = avio_tell(pb); + + end = FFALIGN(pos, 8); + ffio_fill(pb, 0, end - pos); + avio_seek(pb, start + 16, SEEK_SET); + avio_wl64(pb, end - start); + avio_seek(pb, end, SEEK_SET); +} + +static int w64_write_header(AVFormatContext *s) +{ + WAVMuxContext *wav = s->priv_data; + AVIOContext *pb = s->pb; + int64_t start; + int ret; + + avio_write(pb, ff_w64_guid_riff, sizeof(ff_w64_guid_riff)); + avio_wl64(pb, -1); + avio_write(pb, ff_w64_guid_wave, sizeof(ff_w64_guid_wave)); + start_guid(pb, ff_w64_guid_fmt, &start); + if ((ret = ff_put_wav_header(pb, s->streams[0]->codec)) < 0) { + av_log(s, AV_LOG_ERROR, "%s codec not supported\n", + s->streams[0]->codec->codec ? s->streams[0]->codec->codec->name : "NONE"); + return ret; + } + end_guid(pb, start); + + if (s->streams[0]->codec->codec_tag != 0x01 /* hence for all other than PCM */ + && s->pb->seekable) { + start_guid(pb, ff_w64_guid_fact, &wav->fact_pos); + avio_wl64(pb, 0); + end_guid(pb, wav->fact_pos); + } + + start_guid(pb, ff_w64_guid_data, &wav->data); + + return 0; +} + +static int w64_write_trailer(AVFormatContext *s) +{ + AVIOContext *pb = s->pb; + WAVMuxContext *wav = s->priv_data; + int64_t file_size; + + if (pb->seekable) { + end_guid(pb, wav->data); + + file_size = avio_tell(pb); + avio_seek(pb, 16, SEEK_SET); + avio_wl64(pb, file_size); + + if (s->streams[0]->codec->codec_tag != 0x01) { + int64_t number_of_samples; + + number_of_samples = av_rescale(wav->maxpts - wav->minpts + wav->last_duration, + s->streams[0]->codec->sample_rate * (int64_t)s->streams[0]->time_base.num, + s->streams[0]->time_base.den); + avio_seek(pb, wav->fact_pos + 24, SEEK_SET); + avio_wl64(pb, number_of_samples); + } + + avio_seek(pb, file_size, SEEK_SET); + avio_flush(pb); + } + + return 0; +} + +AVOutputFormat ff_w64_muxer = { + .name = "w64", + .long_name = NULL_IF_CONFIG_SMALL("Sony Wave64"), + .extensions = "w64", + .priv_data_size = sizeof(WAVMuxContext), + .audio_codec = AV_CODEC_ID_PCM_S16LE, + .video_codec = AV_CODEC_ID_NONE, + .write_header = w64_write_header, + .write_packet = wav_write_packet, + .write_trailer = w64_write_trailer, + .flags = AVFMT_TS_NONSTRICT, + .codec_tag = (const AVCodecTag* const []){ ff_codec_wav_tags, 0 }, +}; +#endif /* CONFIG_W64_MUXER */ diff --git a/libavformat/wc3movie.c b/libavformat/wc3movie.c index c8942537cb..657380a6be 100644 --- a/libavformat/wc3movie.c +++ b/libavformat/wc3movie.c @@ -2,20 +2,20 @@ * Wing Commander III Movie (.mve) File Demuxer * Copyright (c) 2003 The ffmpeg Project * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -158,7 +158,7 @@ static int wc3_read_header(AVFormatContext *s) fourcc_tag = avio_rl32(pb); /* chunk sizes are 16-bit aligned */ size = (avio_rb32(pb) + 1) & (~1); - if (pb->eof_reached) + if (url_feof(pb)) return AVERROR(EIO); } while (fourcc_tag != BRCH_TAG); @@ -210,7 +210,7 @@ static int wc3_read_packet(AVFormatContext *s, fourcc_tag = avio_rl32(pb); /* chunk sizes are 16-bit aligned */ size = (avio_rb32(pb) + 1) & (~1); - if (pb->eof_reached) + if (url_feof(pb)) return AVERROR(EIO); switch (fourcc_tag) { diff --git a/libavformat/webvttdec.c b/libavformat/webvttdec.c new file mode 100644 index 0000000000..065448532d --- /dev/null +++ b/libavformat/webvttdec.c @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2012 Clément Bœsch + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * WebVTT subtitle demuxer + * @see http://dev.w3.org/html5/webvtt/ + */ + +#include "avformat.h" +#include "internal.h" +#include "subtitles.h" +#include "libavutil/bprint.h" +#include "libavutil/intreadwrite.h" +#include "libavutil/opt.h" + +typedef struct { + const AVClass *class; + FFDemuxSubtitlesQueue q; + int kind; +} WebVTTContext; + +static int webvtt_probe(AVProbeData *p) +{ + const uint8_t *ptr = p->buf; + + if (AV_RB24(ptr) == 0xEFBBBF) + ptr += 3; /* skip UTF-8 BOM */ + if (!strncmp(ptr, "WEBVTT", 6) && + (!ptr[6] || strchr("\n\r\t ", ptr[6]))) + return AVPROBE_SCORE_MAX; + return 0; +} + +static int64_t read_ts(const char *s) +{ + int hh, mm, ss, ms; + if (sscanf(s, "%u:%u:%u.%u", &hh, &mm, &ss, &ms) == 4) return (hh*3600LL + mm*60LL + ss) * 1000LL + ms; + if (sscanf(s, "%u:%u.%u", &mm, &ss, &ms) == 3) return ( mm*60LL + ss) * 1000LL + ms; + return AV_NOPTS_VALUE; +} + +static int webvtt_read_header(AVFormatContext *s) +{ + WebVTTContext *webvtt = s->priv_data; + AVBPrint header, cue; + int res = 0; + AVStream *st = avformat_new_stream(s, NULL); + + if (!st) + return AVERROR(ENOMEM); + avpriv_set_pts_info(st, 64, 1, 1000); + st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE; + st->codec->codec_id = AV_CODEC_ID_WEBVTT; + st->disposition |= webvtt->kind; + + av_bprint_init(&header, 0, AV_BPRINT_SIZE_UNLIMITED); + av_bprint_init(&cue, 0, AV_BPRINT_SIZE_UNLIMITED); + + for (;;) { + int i; + int64_t pos; + AVPacket *sub; + const char *p, *identifier, *settings; + int identifier_len, settings_len; + int64_t ts_start, ts_end; + + ff_subtitles_read_chunk(s->pb, &cue); + + if (!cue.len) + break; + + p = identifier = cue.str; + pos = avio_tell(s->pb); + + /* ignore header chunk */ + if (!strncmp(p, "\xEF\xBB\xBFWEBVTT", 9) || + !strncmp(p, "WEBVTT", 6)) + continue; + + /* optional cue identifier (can be a number like in SRT or some kind of + * chaptering id) */ + for (i = 0; p[i] && p[i] != '\n' && p[i] != '\r'; i++) { + if (!strncmp(p + i, "-->", 3)) { + identifier = NULL; + break; + } + } + if (!identifier) + identifier_len = 0; + else { + identifier_len = strcspn(p, "\r\n"); + p += identifier_len; + if (*p == '\r') + p++; + if (*p == '\n') + p++; + } + + /* cue timestamps */ + if ((ts_start = read_ts(p)) == AV_NOPTS_VALUE) + break; + if (!(p = strstr(p, "-->"))) + break; + p += 3; + do p++; while (*p == ' ' || *p == '\t'); + if ((ts_end = read_ts(p)) == AV_NOPTS_VALUE) + break; + + /* optional cue settings */ + p += strcspn(p, "\n\t "); + while (*p == '\t' || *p == ' ') + p++; + settings = p; + settings_len = strcspn(p, "\r\n"); + p += settings_len; + if (*p == '\r') + p++; + if (*p == '\n') + p++; + + /* create packet */ + sub = ff_subtitles_queue_insert(&webvtt->q, p, strlen(p), 0); + if (!sub) { + res = AVERROR(ENOMEM); + goto end; + } + sub->pos = pos; + sub->pts = ts_start; + sub->duration = ts_end - ts_start; + +#define SET_SIDE_DATA(name, type) do { \ + if (name##_len) { \ + uint8_t *buf = av_packet_new_side_data(sub, type, name##_len); \ + if (!buf) { \ + res = AVERROR(ENOMEM); \ + goto end; \ + } \ + memcpy(buf, name, name##_len); \ + } \ +} while (0) + + SET_SIDE_DATA(identifier, AV_PKT_DATA_WEBVTT_IDENTIFIER); + SET_SIDE_DATA(settings, AV_PKT_DATA_WEBVTT_SETTINGS); + } + + ff_subtitles_queue_finalize(&webvtt->q); + +end: + av_bprint_finalize(&cue, NULL); + av_bprint_finalize(&header, NULL); + return res; +} + +static int webvtt_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + WebVTTContext *webvtt = s->priv_data; + return ff_subtitles_queue_read_packet(&webvtt->q, pkt); +} + +static int webvtt_read_seek(AVFormatContext *s, int stream_index, + int64_t min_ts, int64_t ts, int64_t max_ts, int flags) +{ + WebVTTContext *webvtt = s->priv_data; + return ff_subtitles_queue_seek(&webvtt->q, s, stream_index, + min_ts, ts, max_ts, flags); +} + +static int webvtt_read_close(AVFormatContext *s) +{ + WebVTTContext *webvtt = s->priv_data; + ff_subtitles_queue_clean(&webvtt->q); + return 0; +} + +#define OFFSET(x) offsetof(WebVTTContext, x) +#define KIND_FLAGS AV_OPT_FLAG_SUBTITLE_PARAM + +static const AVOption options[] = { + { "kind", "Set kind of WebVTT track", OFFSET(kind), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, KIND_FLAGS, "webvtt_kind" }, + { "subtitles", "WebVTT subtitles kind", 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, INT_MIN, INT_MAX, 0, "webvtt_kind" }, + { "captions", "WebVTT captions kind", 0, AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_CAPTIONS }, INT_MIN, INT_MAX, 0, "webvtt_kind" }, + { "descriptions", "WebVTT descriptions kind", 0, AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_DESCRIPTIONS }, INT_MIN, INT_MAX, 0, "webvtt_kind" }, + { "metadata", "WebVTT metadata kind", 0, AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_METADATA }, INT_MIN, INT_MAX, 0, "webvtt_kind" }, + { NULL } +}; + +static const AVClass webvtt_demuxer_class = { + .class_name = "WebVTT demuxer", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + +AVInputFormat ff_webvtt_demuxer = { + .name = "webvtt", + .long_name = NULL_IF_CONFIG_SMALL("WebVTT subtitle"), + .priv_data_size = sizeof(WebVTTContext), + .read_probe = webvtt_probe, + .read_header = webvtt_read_header, + .read_packet = webvtt_read_packet, + .read_seek2 = webvtt_read_seek, + .read_close = webvtt_read_close, + .extensions = "vtt", + .priv_class = &webvtt_demuxer_class, +}; diff --git a/libavformat/webvttenc.c b/libavformat/webvttenc.c new file mode 100644 index 0000000000..bd19a83454 --- /dev/null +++ b/libavformat/webvttenc.c @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2013 Matthew Heaney + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * WebVTT subtitle muxer + * @see http://dev.w3.org/html5/webvtt/ + */ + +#include "avformat.h" +#include "internal.h" + +static void webvtt_write_time(AVIOContext *pb, int64_t millisec) +{ + int64_t sec, min, hour; + sec = millisec / 1000; + millisec -= 1000 * sec; + min = sec / 60; + sec -= 60 * min; + hour = min / 60; + min -= 60 * hour; + + if (hour > 0) + avio_printf(pb, "%"PRId64":", hour); + + avio_printf(pb, "%02"PRId64":%02"PRId64".%03"PRId64"", min, sec, millisec); +} + +static int webvtt_write_header(AVFormatContext *ctx) +{ + AVStream *s = ctx->streams[0]; + AVIOContext *pb = ctx->pb; + + avpriv_set_pts_info(s, 64, 1, 1000); + + avio_printf(pb, "WEBVTT\n"); + avio_flush(pb); + + return 0; +} + +static int webvtt_write_packet(AVFormatContext *ctx, AVPacket *pkt) +{ + AVIOContext *pb = ctx->pb; + int id_size, settings_size; + uint8_t *id, *settings; + + avio_printf(pb, "\n"); + + id = av_packet_get_side_data(pkt, AV_PKT_DATA_WEBVTT_IDENTIFIER, + &id_size); + + if (id && id_size > 0) + avio_printf(pb, "%.*s\n", id_size, id); + + webvtt_write_time(pb, pkt->pts); + avio_printf(pb, " --> "); + webvtt_write_time(pb, pkt->pts + pkt->duration); + + settings = av_packet_get_side_data(pkt, AV_PKT_DATA_WEBVTT_SETTINGS, + &settings_size); + + if (settings && settings_size > 0) + avio_printf(pb, " %.*s", settings_size, settings); + + avio_printf(pb, "\n"); + + avio_write(pb, pkt->data, pkt->size); + avio_printf(pb, "\n"); + + return 0; +} + +AVOutputFormat ff_webvtt_muxer = { + .name = "webvtt", + .long_name = NULL_IF_CONFIG_SMALL("WebVTT subtitle"), + .extensions = "vtt", + .mime_type = "text/vtt", + .subtitle_codec = AV_CODEC_ID_WEBVTT, + .write_header = webvtt_write_header, + .write_packet = webvtt_write_packet, +}; diff --git a/libavformat/westwood_aud.c b/libavformat/westwood_aud.c index 611d223238..f2bd3a130e 100644 --- a/libavformat/westwood_aud.c +++ b/libavformat/westwood_aud.c @@ -2,20 +2,20 @@ * Westwood Studios AUD Format Demuxer * Copyright (c) 2003 The ffmpeg Project * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -69,8 +69,6 @@ static int wsaud_probe(AVProbeData *p) if (p->buf[10] & 0xFC) return 0; - /* note: only check for WS IMA (type 99) right now since there is no - * support for type 1 */ if (p->buf[11] != 99 && p->buf[11] != 1) return 0; diff --git a/libavformat/westwood_vqa.c b/libavformat/westwood_vqa.c index 77da375e70..1d4bb5ad6c 100644 --- a/libavformat/westwood_vqa.c +++ b/libavformat/westwood_vqa.c @@ -2,20 +2,20 @@ * Westwood Studios VQA Format Demuxer * Copyright (c) 2003 The ffmpeg Project * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -81,10 +81,10 @@ static int wsvqa_read_header(AVFormatContext *s) WsVqaDemuxContext *wsvqa = s->priv_data; AVIOContext *pb = s->pb; AVStream *st; - unsigned char *header; - unsigned char scratch[VQA_PREAMBLE_SIZE]; - unsigned int chunk_tag; - unsigned int chunk_size; + uint8_t *header; + uint8_t scratch[VQA_PREAMBLE_SIZE]; + uint32_t chunk_tag; + uint32_t chunk_size; int fps; /* initialize the video decoder stream */ @@ -101,9 +101,9 @@ static int wsvqa_read_header(AVFormatContext *s) avio_seek(pb, 20, SEEK_SET); /* the VQA header needs to go to the decoder */ - st->codec->extradata_size = VQA_HEADER_SIZE; - st->codec->extradata = av_mallocz(VQA_HEADER_SIZE + FF_INPUT_BUFFER_PADDING_SIZE); - header = (unsigned char *)st->codec->extradata; + if (ff_alloc_extradata(st->codec, VQA_HEADER_SIZE)) + return AVERROR(ENOMEM); + header = (uint8_t *)st->codec->extradata; if (avio_read(pb, st->codec->extradata, VQA_HEADER_SIZE) != VQA_HEADER_SIZE) { return AVERROR(EIO); @@ -130,9 +130,8 @@ static int wsvqa_read_header(AVFormatContext *s) /* there are 0 or more chunks before the FINF chunk; iterate until * FINF has been skipped and the file will be ready to be demuxed */ do { - if (avio_read(pb, scratch, VQA_PREAMBLE_SIZE) != VQA_PREAMBLE_SIZE) { + if (avio_read(pb, scratch, VQA_PREAMBLE_SIZE) != VQA_PREAMBLE_SIZE) return AVERROR(EIO); - } chunk_tag = AV_RB32(&scratch[0]); chunk_size = AV_RB32(&scratch[4]); @@ -167,26 +166,23 @@ static int wsvqa_read_packet(AVFormatContext *s, WsVqaDemuxContext *wsvqa = s->priv_data; AVIOContext *pb = s->pb; int ret = -1; - unsigned char preamble[VQA_PREAMBLE_SIZE]; - unsigned int chunk_type; - unsigned int chunk_size; + uint8_t preamble[VQA_PREAMBLE_SIZE]; + uint32_t chunk_type; + uint32_t chunk_size; int skip_byte; while (avio_read(pb, preamble, VQA_PREAMBLE_SIZE) == VQA_PREAMBLE_SIZE) { chunk_type = AV_RB32(&preamble[0]); chunk_size = AV_RB32(&preamble[4]); + skip_byte = chunk_size & 0x01; if ((chunk_type == SND0_TAG) || (chunk_type == SND1_TAG) || (chunk_type == SND2_TAG) || (chunk_type == VQFR_TAG)) { - if (av_new_packet(pkt, chunk_size)) + ret= av_get_packet(pb, pkt, chunk_size); + if (ret<0) return AVERROR(EIO); - ret = avio_read(pb, pkt->data, chunk_size); - if (ret != chunk_size) { - av_free_packet(pkt); - return AVERROR(EIO); - } switch (chunk_type) { case SND0_TAG: @@ -223,9 +219,7 @@ static int wsvqa_read_packet(AVFormatContext *s, break; case SND2_TAG: st->codec->codec_id = AV_CODEC_ID_ADPCM_IMA_WS; - st->codec->extradata_size = 2; - st->codec->extradata = av_mallocz(2 + FF_INPUT_BUFFER_PADDING_SIZE); - if (!st->codec->extradata) + if (ff_alloc_extradata(st->codec, 2)) return AVERROR(ENOMEM); AV_WL16(st->codec->extradata, wsvqa->version); break; @@ -236,7 +230,8 @@ static int wsvqa_read_packet(AVFormatContext *s, switch (chunk_type) { case SND1_TAG: /* unpacked size is stored in header */ - pkt->duration = AV_RL16(pkt->data) / wsvqa->channels; + if(pkt->data) + pkt->duration = AV_RL16(pkt->data) / wsvqa->channels; break; case SND2_TAG: /* 2 samples/byte, 1 or 2 samples per frame depending on stereo */ diff --git a/libavformat/wtv.h b/libavformat/wtv.h new file mode 100644 index 0000000000..efe90d6846 --- /dev/null +++ b/libavformat/wtv.h @@ -0,0 +1,58 @@ +/* + * Windows Television (WTV) + * Copyright (c) 2010-2011 Peter Ross <pross@xvid.org> + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVFORMAT_WTV_H +#define AVFORMAT_WTV_H + +#include "riff.h" +#include "asf.h" + +#define WTV_SECTOR_BITS 12 +#define WTV_SECTOR_SIZE (1 << WTV_SECTOR_BITS) +#define WTV_BIGSECTOR_BITS 18 +#define WTV_PAD8(x) (((x) + 7) & ~7) + +extern const uint8_t ff_timeline_le16[16]; +extern const uint8_t ff_timeline_table_0_entries_Events_le16[62]; +extern const uint8_t ff_table_0_entries_legacy_attrib_le16[58]; +extern const uint8_t ff_table_0_entries_time_le16[40]; + +extern const ff_asf_guid ff_dir_entry_guid; +extern const ff_asf_guid ff_wtv_guid; +extern const ff_asf_guid ff_timestamp_guid; +extern const ff_asf_guid ff_data_guid; +extern const ff_asf_guid ff_SBE2_STREAM_DESC_EVENT; +extern const ff_asf_guid ff_stream1_guid; +extern const ff_asf_guid ff_sync_guid; +extern const ff_asf_guid ff_index_guid; +extern const ff_asf_guid ff_mediatype_audio; +extern const ff_asf_guid ff_mediatype_video; +extern const ff_asf_guid ff_format_none; +extern const AVCodecGuid ff_video_guids[]; + +extern const ff_asf_guid ff_DSATTRIB_TRANSPORT_PROPERTIES; +extern const ff_asf_guid ff_metadata_guid; +extern const ff_asf_guid ff_stream2_guid; +extern const ff_asf_guid ff_mediasubtype_cpfilters_processed; +extern const ff_asf_guid ff_format_cpfilters_processed; +extern const ff_asf_guid ff_format_waveformatex; +extern const ff_asf_guid ff_format_mpeg2_video; +#endif /* AVFORMAT_WTV_H */ diff --git a/libavformat/wtv_common.c b/libavformat/wtv_common.c new file mode 100644 index 0000000000..5b1e61b810 --- /dev/null +++ b/libavformat/wtv_common.c @@ -0,0 +1,82 @@ +/* + * Windows Television (WTV) + * Copyright (c) 2010-2011 Peter Ross <pross@xvid.org> + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "wtv.h" + +/* WTV GUIDs*/ +const ff_asf_guid ff_dir_entry_guid = + {0x92,0xB7,0x74,0x91,0x59,0x70,0x70,0x44,0x88,0xDF,0x06,0x3B,0x82,0xCC,0x21,0x3D}; +const ff_asf_guid ff_wtv_guid = + {0xB7,0xD8,0x00,0x20,0x37,0x49,0xDA,0x11,0xA6,0x4E,0x00,0x07,0xE9,0x5E,0xAD,0x8D}; +const ff_asf_guid ff_timestamp_guid = + {0x5B,0x05,0xE6,0x1B,0x97,0xA9,0x49,0x43,0x88,0x17,0x1A,0x65,0x5A,0x29,0x8A,0x97}; +const ff_asf_guid ff_data_guid = + {0x95,0xC3,0xD2,0xC2,0x7E,0x9A,0xDA,0x11,0x8B,0xF7,0x00,0x07,0xE9,0x5E,0xAD,0x8D}; +const ff_asf_guid ff_SBE2_STREAM_DESC_EVENT = + {0xED,0xA4,0x13,0x23,0x2D,0xBF,0x4F,0x45,0xAD,0x8A,0xD9,0x5B,0xA7,0xF9,0x1F,0xEE}; +const ff_asf_guid ff_stream1_guid = + {0xA1,0xC3,0xD2,0xC2,0x7E,0x9A,0xDA,0x11,0x8B,0xF7,0x00,0x07,0xE9,0x5E,0xAD,0x8D}; +const ff_asf_guid ff_sync_guid = + {0x97,0xC3,0xD2,0xC2,0x7E,0x9A,0xDA,0x11,0x8B,0xF7,0x00,0x07,0xE9,0x5E,0xAD,0x8D}; +const ff_asf_guid ff_index_guid = + {0x96,0xC3,0xD2,0xC2,0x7E,0x9A,0xDA,0x11,0x8B,0xF7,0x00,0x07,0xE9,0x5E,0xAD,0x8D}; +const ff_asf_guid ff_mediatype_audio = + {'a','u','d','s',FF_MEDIASUBTYPE_BASE_GUID}; +const ff_asf_guid ff_mediatype_video = + {'v','i','d','s',FF_MEDIASUBTYPE_BASE_GUID}; +const ff_asf_guid ff_format_none = + {0xD6,0x17,0x64,0x0F,0x18,0xC3,0xD0,0x11,0xA4,0x3F,0x00,0xA0,0xC9,0x22,0x31,0x96}; + +/* declare utf16le strings */ +#define _ , 0, +const uint8_t ff_timeline_le16[] = + {'t'_'i'_'m'_'e'_'l'_'i'_'n'_'e', 0}; +const uint8_t ff_timeline_table_0_entries_Events_le16[] = + {'t'_'i'_'m'_'e'_'l'_'i'_'n'_'e'_'.'_'t'_'a'_'b'_'l'_'e'_'.'_'0'_'.'_'e'_'n'_'t'_'r'_'i'_'e'_'s'_'.'_'E'_'v'_'e'_'n'_'t'_'s', 0}; +const uint8_t ff_table_0_entries_legacy_attrib_le16[] = + {'t'_'a'_'b'_'l'_'e'_'.'_'0'_'.'_'e'_'n'_'t'_'r'_'i'_'e'_'s'_'.'_'l'_'e'_'g'_'a'_'c'_'y'_'_'_'a'_'t'_'t'_'r'_'i'_'b', 0}; +const uint8_t ff_table_0_entries_time_le16[] = + {'t'_'a'_'b'_'l'_'e'_'.'_'0'_'.'_'e'_'n'_'t'_'r'_'i'_'e'_'s'_'.'_'t'_'i'_'m'_'e', 0}; +#undef _ + +const ff_asf_guid ff_DSATTRIB_TRANSPORT_PROPERTIES = + {0x12,0xF6,0x22,0xB6,0xAD,0x47,0x71,0x46,0xAD,0x6C,0x05,0xA9,0x8E,0x65,0xDE,0x3A}; +const ff_asf_guid ff_metadata_guid = + {0x5A,0xFE,0xD7,0x6D,0xC8,0x1D,0x8F,0x4A,0x99,0x22,0xFA,0xB1,0x1C,0x38,0x14,0x53}; +const ff_asf_guid ff_stream2_guid = + {0xA2,0xC3,0xD2,0xC2,0x7E,0x9A,0xDA,0x11,0x8B,0xF7,0x00,0x07,0xE9,0x5E,0xAD,0x8D}; + +/* Media subtypes */ +const ff_asf_guid ff_mediasubtype_cpfilters_processed = + {0x28,0xBD,0xAD,0x46,0xD0,0x6F,0x96,0x47,0x93,0xB2,0x15,0x5C,0x51,0xDC,0x04,0x8D}; + +/* Formats */ +const ff_asf_guid ff_format_cpfilters_processed = + {0x6F,0xB3,0x39,0x67,0x5F,0x1D,0xC2,0x4A,0x81,0x92,0x28,0xBB,0x0E,0x73,0xD1,0x6A}; +const ff_asf_guid ff_format_waveformatex = + {0x81,0x9F,0x58,0x05,0x56,0xC3,0xCE,0x11,0xBF,0x01,0x00,0xAA,0x00,0x55,0x59,0x5A}; +const ff_asf_guid ff_format_mpeg2_video = + {0xE3,0x80,0x6D,0xE0,0x46,0xDB,0xCF,0x11,0xB4,0xD1,0x00,0x80,0x5F,0x6C,0xBB,0xEA}; + +const AVCodecGuid ff_video_guids[] = { + {AV_CODEC_ID_MPEG2VIDEO, {0x26,0x80,0x6D,0xE0,0x46,0xDB,0xCF,0x11,0xB4,0xD1,0x00,0x80,0x5F,0x6C,0xBB,0xEA}}, + {AV_CODEC_ID_NONE} +}; diff --git a/libavformat/wtv.c b/libavformat/wtvdec.c index b144eb6c32..9bbea9ca16 100644 --- a/libavformat/wtv.c +++ b/libavformat/wtvdec.c @@ -2,20 +2,20 @@ * Windows Television (WTV) demuxer * Copyright (c) 2010-2011 Peter Ross <pross@xvid.org> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -28,11 +28,9 @@ #include "libavutil/channel_layout.h" #include "libavutil/intreadwrite.h" #include "libavutil/intfloat.h" -#include "libavutil/dict.h" #include "avformat.h" #include "internal.h" -#include "riff.h" -#include "asf.h" +#include "wtv.h" #include "mpegts.h" /* Macros for formating GUIDs */ @@ -48,18 +46,12 @@ * */ -#define WTV_SECTOR_BITS 12 -#define WTV_SECTOR_SIZE (1 << WTV_SECTOR_BITS) -#define WTV_BIGSECTOR_BITS 18 - -#define SHIFT_SECTOR_BITS(a) ((int64_t)(a) << WTV_SECTOR_BITS) - typedef struct { - AVIOContext *pb_filesystem; /** file system (AVFormatContext->pb) */ + AVIOContext *pb_filesystem; /**< file system (AVFormatContext->pb) */ - int sector_bits; /** sector shift bits; used to convert sector number into pb_filesystem offset */ - uint32_t *sectors; /** file allocation table */ - int nb_sectors; /** number of sectors */ + int sector_bits; /**< sector shift bits; used to convert sector number into pb_filesystem offset */ + uint32_t *sectors; /**< file allocation table */ + int nb_sectors; /**< number of sectors */ int error; int64_t position; @@ -68,7 +60,7 @@ typedef struct { static int64_t seek_by_sector(AVIOContext *pb, int64_t sector, int64_t offset) { - return avio_seek(pb, SHIFT_SECTOR_BITS(sector) + offset, SEEK_SET); + return avio_seek(pb, (sector << WTV_SECTOR_BITS) + offset, SEEK_SET); } /** @@ -82,7 +74,7 @@ static int wtvfile_read_packet(void *opaque, uint8_t *buf, int buf_size) if (wf->error || pb->error) return -1; - if (wf->position >= wf->length || pb->eof_reached) + if (wf->position >= wf->length || url_feof(pb)) return 0; buf_size = FFMIN(buf_size, wf->length - wf->position); @@ -177,7 +169,6 @@ static AVIOContext * wtvfile_open_sector(int first_sector, uint64_t length, int } wf->sectors[0] = first_sector; wf->nb_sectors = 1; - wf->sector_bits = WTV_SECTOR_BITS; } else if (depth == 1) { wf->sectors = av_malloc(WTV_SECTOR_SIZE); if (!wf->sectors) { @@ -185,13 +176,12 @@ static AVIOContext * wtvfile_open_sector(int first_sector, uint64_t length, int return NULL; } wf->nb_sectors = read_ints(s->pb, wf->sectors, WTV_SECTOR_SIZE / 4); - wf->sector_bits = length & (1ULL<<63) ? WTV_SECTOR_BITS : WTV_BIGSECTOR_BITS; } else if (depth == 2) { uint32_t sectors1[WTV_SECTOR_SIZE / 4]; int nb_sectors1 = read_ints(s->pb, sectors1, WTV_SECTOR_SIZE / 4); int i; - wf->sectors = av_malloc(SHIFT_SECTOR_BITS(nb_sectors1)); + wf->sectors = av_malloc_array(nb_sectors1, 1 << WTV_SECTOR_BITS); if (!wf->sectors) { av_free(wf); return NULL; @@ -202,12 +192,12 @@ static AVIOContext * wtvfile_open_sector(int first_sector, uint64_t length, int break; wf->nb_sectors += read_ints(s->pb, wf->sectors + i * WTV_SECTOR_SIZE / 4, WTV_SECTOR_SIZE / 4); } - wf->sector_bits = length & (1ULL<<63) ? WTV_SECTOR_BITS : WTV_BIGSECTOR_BITS; } else { av_log(s, AV_LOG_ERROR, "unsupported file allocation table depth (0x%x)\n", depth); av_free(wf); return NULL; } + wf->sector_bits = length & (1ULL<<63) ? WTV_SECTOR_BITS : WTV_BIGSECTOR_BITS; if (!wf->nb_sectors) { av_free(wf->sectors); @@ -215,6 +205,9 @@ static AVIOContext * wtvfile_open_sector(int first_sector, uint64_t length, int return NULL; } + if ((int64_t)wf->sectors[wf->nb_sectors - 1] << WTV_SECTOR_BITS > avio_tell(s->pb)) + av_log(s, AV_LOG_WARNING, "truncated file\n"); + /* check length */ length &= 0xFFFFFFFFFFFF; if (length > ((int64_t)wf->nb_sectors << wf->sector_bits)) { @@ -249,9 +242,6 @@ static AVIOContext * wtvfile_open_sector(int first_sector, uint64_t length, int return pb; } -static const ff_asf_guid dir_entry_guid = - {0x92,0xB7,0x74,0x91,0x59,0x70,0x70,0x44,0x88,0xDF,0x06,0x3B,0x82,0xCC,0x21,0x3D}; - /** * Open file using filename * @param[in] buf directory buffer @@ -268,7 +258,7 @@ static AVIOContext * wtvfile_open2(AVFormatContext *s, const uint8_t *buf, int b int dir_length, name_size, first_sector, depth; uint64_t file_length; const uint8_t *name; - if (ff_guidcmp(buf, dir_entry_guid)) { + if (ff_guidcmp(buf, ff_dir_entry_guid)) { av_log(s, AV_LOG_ERROR, "unknown guid "FF_PRI_GUID", expected dir_entry_guid; " "remaining directory entries ignored\n", FF_ARG_GUID(buf)); break; @@ -281,7 +271,7 @@ static AVIOContext * wtvfile_open2(AVFormatContext *s, const uint8_t *buf, int b "bad filename length, remaining directory entries ignored\n"); break; } - if (48 + name_size > buf_end - buf) { + if (48 + (int64_t)name_size > buf_end - buf) { av_log(s, AV_LOG_ERROR, "filename exceeds buffer size; remaining directory entries ignored\n"); break; } @@ -310,8 +300,8 @@ static void wtvfile_close(AVIOContext *pb) { WtvFile *wf = pb->opaque; av_free(wf->sectors); - av_free(wf); - av_free(pb->buffer); + av_freep(&pb->opaque); + av_freep(&pb->buffer); av_free(pb); } @@ -326,10 +316,10 @@ typedef struct { } WtvStream; typedef struct { - AVIOContext *pb; /** timeline file */ + AVIOContext *pb; /**< timeline file */ int64_t epoch; - int64_t pts; /** pts for next data chunk */ - int64_t last_valid_pts; /** latest valid pts, used for interative seeking */ + int64_t pts; /**< pts for next data chunk */ + int64_t last_valid_pts; /**< latest valid pts, used for interative seeking */ /* maintain private seek index, as the AVIndexEntry->pos is relative to the start of the 'timeline' file, not the file system (AVFormatContext->pb) */ @@ -339,18 +329,6 @@ typedef struct { } WtvContext; /* WTV GUIDs */ -static const ff_asf_guid wtv_guid = - {0xB7,0xD8,0x00,0x20,0x37,0x49,0xDA,0x11,0xA6,0x4E,0x00,0x07,0xE9,0x5E,0xAD,0x8D}; -static const ff_asf_guid metadata_guid = - {0x5A,0xFE,0xD7,0x6D,0xC8,0x1D,0x8F,0x4A,0x99,0x22,0xFA,0xB1,0x1C,0x38,0x14,0x53}; -static const ff_asf_guid timestamp_guid = - {0x5B,0x05,0xE6,0x1B,0x97,0xA9,0x49,0x43,0x88,0x17,0x1A,0x65,0x5A,0x29,0x8A,0x97}; -static const ff_asf_guid data_guid = - {0x95,0xC3,0xD2,0xC2,0x7E,0x9A,0xDA,0x11,0x8B,0xF7,0x00,0x07,0xE9,0x5E,0xAD,0x8D}; -static const ff_asf_guid stream_guid = - {0xED,0xA4,0x13,0x23,0x2D,0xBF,0x4F,0x45,0xAD,0x8A,0xD9,0x5B,0xA7,0xF9,0x1F,0xEE}; -static const ff_asf_guid stream2_guid = - {0xA2,0xC3,0xD2,0xC2,0x7E,0x9A,0xDA,0x11,0x8B,0xF7,0x00,0x07,0xE9,0x5E,0xAD,0x8D}; static const ff_asf_guid EVENTID_SubtitleSpanningEvent = {0x48,0xC0,0xCE,0x5D,0xB9,0xD0,0x63,0x41,0x87,0x2C,0x4F,0x32,0x22,0x3B,0xE8,0x8A}; static const ff_asf_guid EVENTID_LanguageSpanningEvent = @@ -373,10 +351,6 @@ static const ff_asf_guid EVENTID_AudioTypeSpanningEvent = /* Windows media GUIDs */ /* Media types */ -static const ff_asf_guid mediatype_audio = - {'a','u','d','s',FF_MEDIASUBTYPE_BASE_GUID}; -static const ff_asf_guid mediatype_video = - {'v','i','d','s',FF_MEDIASUBTYPE_BASE_GUID}; static const ff_asf_guid mediasubtype_mpeg1payload = {0x81,0xEB,0x36,0xE4,0x4F,0x52,0xCE,0x11,0x9F,0x53,0x00,0x20,0xAF,0x0B,0xA7,0x70}; static const ff_asf_guid mediatype_mpeg2_sections = @@ -387,8 +361,6 @@ static const ff_asf_guid mediatype_mstvcaption = {0x89,0x8A,0x8B,0xB8,0x49,0xB0,0x80,0x4C,0xAD,0xCF,0x58,0x98,0x98,0x5E,0x22,0xC1}; /* Media subtypes */ -static const ff_asf_guid mediasubtype_cpfilters_processed = - {0x28,0xBD,0xAD,0x46,0xD0,0x6F,0x96,0x47,0x93,0xB2,0x15,0x5C,0x51,0xDC,0x04,0x8D}; static const ff_asf_guid mediasubtype_dvb_subtitle = {0xC3,0xCB,0xFF,0x34,0xB3,0xD5,0x71,0x41,0x90,0x02,0xD4,0xC6,0x03,0x01,0x69,0x7F}; static const ff_asf_guid mediasubtype_teletext = @@ -399,64 +371,54 @@ static const ff_asf_guid mediasubtype_mpeg2_sections = {0x79,0x85,0x9F,0x4A,0xF8,0x6B,0x92,0x43,0x8A,0x6D,0xD2,0xDD,0x09,0xFA,0x78,0x61}; /* Formats */ -static const ff_asf_guid format_cpfilters_processed = - {0x6F,0xB3,0x39,0x67,0x5F,0x1D,0xC2,0x4A,0x81,0x92,0x28,0xBB,0x0E,0x73,0xD1,0x6A}; -static const ff_asf_guid format_waveformatex = - {0x81,0x9F,0x58,0x05,0x56,0xC3,0xCE,0x11,0xBF,0x01,0x00,0xAA,0x00,0x55,0x59,0x5A}; static const ff_asf_guid format_videoinfo2 = {0xA0,0x76,0x2A,0xF7,0x0A,0xEB,0xD0,0x11,0xAC,0xE4,0x00,0x00,0xC0,0xCC,0x16,0xBA}; -static const ff_asf_guid format_mpeg2_video = - {0xE3,0x80,0x6D,0xE0,0x46,0xDB,0xCF,0x11,0xB4,0xD1,0x00,0x80,0x5F,0x6C,0xBB,0xEA}; -static const ff_asf_guid format_none = - {0xD6,0x17,0x64,0x0F,0x18,0xC3,0xD0,0x11,0xA4,0x3F,0x00,0xA0,0xC9,0x22,0x31,0x96}; - -static const AVCodecGuid video_guids[] = { - {AV_CODEC_ID_MPEG2VIDEO, {0x26,0x80,0x6D,0xE0,0x46,0xDB,0xCF,0x11,0xB4,0xD1,0x00,0x80,0x5F,0x6C,0xBB,0xEA}}, - {AV_CODEC_ID_NONE} -}; static int read_probe(AVProbeData *p) { - return ff_guidcmp(p->buf, wtv_guid) ? 0 : AVPROBE_SCORE_MAX; + return ff_guidcmp(p->buf, ff_wtv_guid) ? 0 : AVPROBE_SCORE_MAX; } /** * Convert win32 FILETIME to ISO-8601 string + * @return <0 on error */ -static void filetime_to_iso8601(char *buf, int buf_size, int64_t value) +static int filetime_to_iso8601(char *buf, int buf_size, int64_t value) { time_t t = (value / 10000000LL) - 11644473600LL; struct tm *tm = gmtime(&t); - if (tm) - strftime(buf, buf_size, "%Y-%m-%d %H:%M:%S", gmtime(&t)); - else - buf[0] = '\0'; + if (!tm) + return -1; + strftime(buf, buf_size, "%Y-%m-%d %H:%M:%S", gmtime(&t)); + return 0; } /** * Convert crazy time (100ns since 1 Jan 0001) to ISO-8601 string + * @return <0 on error */ -static void crazytime_to_iso8601(char *buf, int buf_size, int64_t value) +static int crazytime_to_iso8601(char *buf, int buf_size, int64_t value) { time_t t = (value / 10000000LL) - 719162LL*86400LL; struct tm *tm = gmtime(&t); - if (tm) - strftime(buf, buf_size, "%Y-%m-%d %H:%M:%S", gmtime(&t)); - else - buf[0] = '\0'; + if (!tm) + return -1; + strftime(buf, buf_size, "%Y-%m-%d %H:%M:%S", gmtime(&t)); + return 0; } /** * Convert OLE DATE to ISO-8601 string + * @return <0 on error */ -static void oledate_to_iso8601(char *buf, int buf_size, int64_t value) +static int oledate_to_iso8601(char *buf, int buf_size, int64_t value) { - time_t t = 631112400LL + 86400*av_int2double(value); - struct tm *tm = gmtime(&t); - if (tm) - strftime(buf, buf_size, "%Y-%m-%d %H:%M:%S", gmtime(&t)); - else - buf[0] = '\0'; + time_t t = (av_int2double(value) - 25569.0) * 86400; + struct tm *result= gmtime(&t); + if (!result) + return -1; + strftime(buf, buf_size, "%Y-%m-%d %H:%M:%S", result); + return 0; } static void get_attachment(AVFormatContext *s, AVIOContext *pb, int length) @@ -465,6 +427,7 @@ static void get_attachment(AVFormatContext *s, AVIOContext *pb, int length) char description[1024]; unsigned int filesize; AVStream *st; + int ret; int64_t pos = avio_tell(pb); avio_get_str16le(pb, INT_MAX, mime, sizeof(mime)); @@ -481,22 +444,31 @@ static void get_attachment(AVFormatContext *s, AVIOContext *pb, int length) if (!st) goto done; av_dict_set(&st->metadata, "title", description, 0); + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; st->codec->codec_id = AV_CODEC_ID_MJPEG; - st->codec->codec_type = AVMEDIA_TYPE_ATTACHMENT; - st->codec->extradata = av_mallocz(filesize); st->id = -1; - if (!st->codec->extradata) + ret = av_get_packet(pb, &st->attached_pic, filesize); + if (ret < 0) goto done; - st->codec->extradata_size = filesize; - avio_read(pb, st->codec->extradata, filesize); + st->attached_pic.stream_index = st->index; + st->attached_pic.flags |= AV_PKT_FLAG_KEY; + st->disposition |= AV_DISPOSITION_ATTACHED_PIC; done: avio_seek(pb, pos + length, SEEK_SET); } static void get_tag(AVFormatContext *s, AVIOContext *pb, const char *key, int type, int length) { - int buf_size = FFMAX(2*length, LEN_PRETTY_GUID) + 1; - char *buf = av_malloc(buf_size); + int buf_size; + char *buf; + + if (!strcmp(key, "WM/MediaThumbType")) { + avio_skip(pb, length); + return; + } + + buf_size = FFMAX(2*length, LEN_PRETTY_GUID) + 1; + buf = av_malloc(buf_size); if (!buf) return; @@ -513,14 +485,23 @@ static void get_tag(AVFormatContext *s, AVIOContext *pb, const char *key, int ty } else if (type == 4 && length == 8) { int64_t num = avio_rl64(pb); if (!strcmp(key, "WM/EncodingTime") || - !strcmp(key, "WM/MediaOriginalBroadcastDateTime")) - filetime_to_iso8601(buf, buf_size, num); - else if (!strcmp(key, "WM/WMRVEncodeTime") || - !strcmp(key, "WM/WMRVEndTime")) - crazytime_to_iso8601(buf, buf_size, num); - else if (!strcmp(key, "WM/WMRVExpirationDate")) - oledate_to_iso8601(buf, buf_size, num); - else if (!strcmp(key, "WM/WMRVBitrate")) + !strcmp(key, "WM/MediaOriginalBroadcastDateTime")) { + if (filetime_to_iso8601(buf, buf_size, num) < 0) { + av_free(buf); + return; + } + } else if (!strcmp(key, "WM/WMRVEncodeTime") || + !strcmp(key, "WM/WMRVEndTime")) { + if (crazytime_to_iso8601(buf, buf_size, num) < 0) { + av_free(buf); + return; + } + } else if (!strcmp(key, "WM/WMRVExpirationDate")) { + if (oledate_to_iso8601(buf, buf_size, num) < 0 ) { + av_free(buf); + return; + } + } else if (!strcmp(key, "WM/WMRVBitrate")) snprintf(buf, buf_size, "%f", av_int2double(num)); else snprintf(buf, buf_size, "%"PRIi64, num); @@ -552,14 +533,14 @@ static void parse_legacy_attrib(AVFormatContext *s, AVIOContext *pb) { ff_asf_guid guid; int length, type; - while(!pb->eof_reached) { + while(!url_feof(pb)) { char key[1024]; ff_get_guid(pb, &guid); type = avio_rl32(pb); length = avio_rl32(pb); if (!length) break; - if (ff_guidcmp(&guid, metadata_guid)) { + if (ff_guidcmp(&guid, ff_metadata_guid)) { av_log(s, AV_LOG_WARNING, "unknown guid "FF_PRI_GUID", expected metadata_guid; " "remaining metadata entries ignored\n", FF_ARG_GUID(guid)); break; @@ -581,7 +562,7 @@ static int parse_videoinfoheader2(AVFormatContext *s, AVStream *st) AVIOContext *pb = wtv->pb; avio_skip(pb, 72); // picture aspect ratio is unreliable - ff_get_bmp_header(pb, st); + ff_get_bmp_header(pb, st, NULL); return 72 + 40; } @@ -630,8 +611,10 @@ static AVStream * new_stream(AVFormatContext *s, AVStream *st, int sid, int code if (!wst) return NULL; st = avformat_new_stream(s, NULL); - if (!st) + if (!st) { + av_free(wst); return NULL; + } st->id = sid; st->priv_data = wst; } @@ -656,8 +639,8 @@ static AVStream * parse_media_type(AVFormatContext *s, AVStream *st, int sid, { WtvContext *wtv = s->priv_data; AVIOContext *pb = wtv->pb; - if (!ff_guidcmp(subtype, mediasubtype_cpfilters_processed) && - !ff_guidcmp(formattype, format_cpfilters_processed)) { + if (!ff_guidcmp(subtype, ff_mediasubtype_cpfilters_processed) && + !ff_guidcmp(formattype, ff_format_cpfilters_processed)) { ff_asf_guid actual_subtype; ff_asf_guid actual_formattype; @@ -675,16 +658,16 @@ static AVStream * parse_media_type(AVFormatContext *s, AVStream *st, int sid, st = parse_media_type(s, st, sid, mediatype, actual_subtype, actual_formattype, size - 32); avio_skip(pb, 32); return st; - } else if (!ff_guidcmp(mediatype, mediatype_audio)) { + } else if (!ff_guidcmp(mediatype, ff_mediatype_audio)) { st = new_stream(s, st, sid, AVMEDIA_TYPE_AUDIO); if (!st) return NULL; - if (!ff_guidcmp(formattype, format_waveformatex)) { + if (!ff_guidcmp(formattype, ff_format_waveformatex)) { int ret = ff_get_wav_header(pb, st->codec, size); if (ret < 0) return NULL; } else { - if (ff_guidcmp(formattype, format_none)) + if (ff_guidcmp(formattype, ff_format_none)) av_log(s, AV_LOG_WARNING, "unknown formattype:"FF_PRI_GUID"\n", FF_ARG_GUID(formattype)); avio_skip(pb, size); } @@ -702,18 +685,18 @@ static AVStream * parse_media_type(AVFormatContext *s, AVStream *st, int sid, av_log(s, AV_LOG_WARNING, "unknown subtype:"FF_PRI_GUID"\n", FF_ARG_GUID(subtype)); } return st; - } else if (!ff_guidcmp(mediatype, mediatype_video)) { + } else if (!ff_guidcmp(mediatype, ff_mediatype_video)) { st = new_stream(s, st, sid, AVMEDIA_TYPE_VIDEO); if (!st) return NULL; if (!ff_guidcmp(formattype, format_videoinfo2)) { int consumed = parse_videoinfoheader2(s, st); avio_skip(pb, FFMAX(size - consumed, 0)); - } else if (!ff_guidcmp(formattype, format_mpeg2_video)) { + } else if (!ff_guidcmp(formattype, ff_format_mpeg2_video)) { int consumed = parse_videoinfoheader2(s, st); avio_skip(pb, FFMAX(size - consumed, 0)); } else { - if (ff_guidcmp(formattype, format_none)) + if (ff_guidcmp(formattype, ff_format_none)) av_log(s, AV_LOG_WARNING, "unknown formattype:"FF_PRI_GUID"\n", FF_ARG_GUID(formattype)); avio_skip(pb, size); } @@ -721,7 +704,7 @@ static AVStream * parse_media_type(AVFormatContext *s, AVStream *st, int sid, if (!memcmp(subtype + 4, (const uint8_t[]){FF_MEDIASUBTYPE_BASE_GUID}, 12)) { st->codec->codec_id = ff_codec_get_id(ff_codec_bmp_tags, AV_RL32(subtype)); } else { - st->codec->codec_id = ff_codec_guid_get_id(video_guids, subtype); + st->codec->codec_id = ff_codec_guid_get_id(ff_video_guids, subtype); } if (st->codec->codec_id == AV_CODEC_ID_NONE) av_log(s, AV_LOG_WARNING, "unknown subtype:"FF_PRI_GUID"\n", FF_ARG_GUID(subtype)); @@ -731,7 +714,7 @@ static AVStream * parse_media_type(AVFormatContext *s, AVStream *st, int sid, st = new_stream(s, st, sid, AVMEDIA_TYPE_SUBTITLE); if (!st) return NULL; - if (ff_guidcmp(formattype, format_none)) + if (ff_guidcmp(formattype, ff_format_none)) av_log(s, AV_LOG_WARNING, "unknown formattype:"FF_PRI_GUID"\n", FF_ARG_GUID(formattype)); avio_skip(pb, size); st->codec->codec_id = AV_CODEC_ID_DVB_SUBTITLE; @@ -741,14 +724,14 @@ static AVStream * parse_media_type(AVFormatContext *s, AVStream *st, int sid, st = new_stream(s, st, sid, AVMEDIA_TYPE_SUBTITLE); if (!st) return NULL; - if (ff_guidcmp(formattype, format_none)) + if (ff_guidcmp(formattype, ff_format_none)) av_log(s, AV_LOG_WARNING, "unknown formattype:"FF_PRI_GUID"\n", FF_ARG_GUID(formattype)); avio_skip(pb, size); - st->codec->codec_id = AV_CODEC_ID_DVB_TELETEXT; + st->codec->codec_id = !ff_guidcmp(subtype, mediasubtype_teletext) ? AV_CODEC_ID_DVB_TELETEXT : AV_CODEC_ID_EIA_608; return st; } else if (!ff_guidcmp(mediatype, mediatype_mpeg2_sections) && !ff_guidcmp(subtype, mediasubtype_mpeg2_sections)) { - if (ff_guidcmp(formattype, format_none)) + if (ff_guidcmp(formattype, ff_format_none)) av_log(s, AV_LOG_WARNING, "unknown formattype:"FF_PRI_GUID"\n", FF_ARG_GUID(formattype)); avio_skip(pb, size); return NULL; @@ -777,7 +760,7 @@ static int parse_chunks(AVFormatContext *s, int mode, int64_t seekts, int *len_p { WtvContext *wtv = s->priv_data; AVIOContext *pb = wtv->pb; - while (!pb->eof_reached) { + while (!url_feof(pb)) { ff_asf_guid g; int len, sid, consumed; @@ -789,7 +772,7 @@ static int parse_chunks(AVFormatContext *s, int mode, int64_t seekts, int *len_p avio_skip(pb, 8); consumed = 32; - if (!ff_guidcmp(g, stream_guid)) { + if (!ff_guidcmp(g, ff_SBE2_STREAM_DESC_EVENT)) { if (ff_find_stream_index(s, sid) < 0) { ff_asf_guid mediatype, subtype, formattype; int size; @@ -802,9 +785,9 @@ static int parse_chunks(AVFormatContext *s, int mode, int64_t seekts, int *len_p parse_media_type(s, 0, sid, mediatype, subtype, formattype, size); consumed += 92 + size; } - } else if (!ff_guidcmp(g, stream2_guid)) { + } else if (!ff_guidcmp(g, ff_stream2_guid)) { int stream_index = ff_find_stream_index(s, sid); - if (stream_index >= 0 && !((WtvStream*)s->streams[stream_index]->priv_data)->seen_data) { + if (stream_index >= 0 && s->streams[stream_index]->priv_data && !((WtvStream*)s->streams[stream_index]->priv_data)->seen_data) { ff_asf_guid mediatype, subtype, formattype; int size; avio_skip(pb, 12); @@ -878,7 +861,7 @@ static int parse_chunks(AVFormatContext *s, int mode, int64_t seekts, int *len_p } consumed += 15; } - } else if (!ff_guidcmp(g, timestamp_guid)) { + } else if (!ff_guidcmp(g, ff_timestamp_guid)) { int stream_index = ff_find_stream_index(s, sid); if (stream_index >= 0) { avio_skip(pb, 8); @@ -891,15 +874,14 @@ static int parse_chunks(AVFormatContext *s, int mode, int64_t seekts, int *len_p if (wtv->epoch == AV_NOPTS_VALUE || wtv->pts < wtv->epoch) wtv->epoch = wtv->pts; if (mode == SEEK_TO_PTS && wtv->pts >= seekts) { -#define WTV_PAD8(x) (((x) + 7) & ~7) avio_skip(pb, WTV_PAD8(len) - consumed); return 0; } } } - } else if (!ff_guidcmp(g, data_guid)) { + } else if (!ff_guidcmp(g, ff_data_guid)) { int stream_index = ff_find_stream_index(s, sid); - if (mode == SEEK_TO_DATA && stream_index >= 0 && len > 32) { + if (mode == SEEK_TO_DATA && stream_index >= 0 && len > 32 && s->streams[stream_index]->priv_data) { WtvStream *wst = s->streams[stream_index]->priv_data; wst->seen_data = 1; if (len_ptr) { @@ -907,10 +889,15 @@ static int parse_chunks(AVFormatContext *s, int mode, int64_t seekts, int *len_p } return stream_index; } + } else if (!ff_guidcmp(g, /* DSATTRIB_WMDRMProtectionInfo */ (const ff_asf_guid){0x83,0x95,0x74,0x40,0x9D,0x6B,0xEC,0x4E,0xB4,0x3C,0x67,0xA1,0x80,0x1E,0x1A,0x9B})) { + int stream_index = ff_find_stream_index(s, sid); + if (stream_index >= 0) + av_log(s, AV_LOG_WARNING, "encrypted stream detected (st:%d), decoding will likely fail\n", stream_index); } else if ( !ff_guidcmp(g, /* DSATTRIB_CAPTURE_STREAMTIME */ (const ff_asf_guid){0x14,0x56,0x1A,0x0C,0xCD,0x30,0x40,0x4F,0xBC,0xBF,0xD0,0x3E,0x52,0x30,0x62,0x07}) || + !ff_guidcmp(g, /* DSATTRIB_PBDATAG_ATTRIBUTE */ (const ff_asf_guid){0x79,0x66,0xB5,0xE0,0xB9,0x12,0xCC,0x43,0xB7,0xDF,0x57,0x8C,0xAA,0x5A,0x7B,0x63}) || !ff_guidcmp(g, /* DSATTRIB_PicSampleSeq */ (const ff_asf_guid){0x02,0xAE,0x5B,0x2F,0x8F,0x7B,0x60,0x4F,0x82,0xD6,0xE4,0xEA,0x2F,0x1F,0x4C,0x99}) || - !ff_guidcmp(g, /* DSATTRIB_TRANSPORT_PROPERTIES */ (const ff_asf_guid){0x12,0xF6,0x22,0xB6,0xAD,0x47,0x71,0x46,0xAD,0x6C,0x05,0xA9,0x8E,0x65,0xDE,0x3A}) || + !ff_guidcmp(g, /* DSATTRIB_TRANSPORT_PROPERTIES */ ff_DSATTRIB_TRANSPORT_PROPERTIES) || !ff_guidcmp(g, /* dvr_ms_vid_frame_rep_data */ (const ff_asf_guid){0xCC,0x32,0x64,0xDD,0x29,0xE2,0xDB,0x40,0x80,0xF6,0xD2,0x63,0x28,0xD2,0x76,0x1F}) || !ff_guidcmp(g, /* EVENTID_ChannelChangeSpanningEvent */ (const ff_asf_guid){0xE5,0xC5,0x67,0x90,0x5C,0x4C,0x05,0x42,0x86,0xC8,0x7A,0xFE,0x20,0xFE,0x1E,0xFA}) || !ff_guidcmp(g, /* EVENTID_ChannelInfoSpanningEvent */ (const ff_asf_guid){0x80,0x6D,0xF3,0x41,0x32,0x41,0xC2,0x4C,0xB1,0x21,0x01,0xA4,0x32,0x19,0xD8,0x1B}) || @@ -923,9 +910,10 @@ static int parse_chunks(AVFormatContext *s, int mode, int64_t seekts, int *len_p !ff_guidcmp(g, (const ff_asf_guid){0x4E,0x7F,0x4C,0x5B,0xC4,0xD0,0x38,0x4B,0xA8,0x3E,0x21,0x7F,0x7B,0xBF,0x52,0xE7}) || !ff_guidcmp(g, (const ff_asf_guid){0x63,0x36,0xEB,0xFE,0xA1,0x7E,0xD9,0x11,0x83,0x08,0x00,0x07,0xE9,0x5E,0xAD,0x8D}) || !ff_guidcmp(g, (const ff_asf_guid){0x70,0xE9,0xF1,0xF8,0x89,0xA4,0x4C,0x4D,0x83,0x73,0xB8,0x12,0xE0,0xD5,0xF8,0x1E}) || - !ff_guidcmp(g, (const ff_asf_guid){0x96,0xC3,0xD2,0xC2,0x7E,0x9A,0xDA,0x11,0x8B,0xF7,0x00,0x07,0xE9,0x5E,0xAD,0x8D}) || - !ff_guidcmp(g, (const ff_asf_guid){0x97,0xC3,0xD2,0xC2,0x7E,0x9A,0xDA,0x11,0x8B,0xF7,0x00,0x07,0xE9,0x5E,0xAD,0x8D}) || - !ff_guidcmp(g, (const ff_asf_guid){0xA1,0xC3,0xD2,0xC2,0x7E,0x9A,0xDA,0x11,0x8B,0xF7,0x00,0x07,0xE9,0x5E,0xAD,0x8D})) { + !ff_guidcmp(g, ff_index_guid) || + !ff_guidcmp(g, ff_sync_guid) || + !ff_guidcmp(g, ff_stream1_guid) || + !ff_guidcmp(g, (const ff_asf_guid){0xF7,0x10,0x02,0xB9,0xEE,0x7C,0xED,0x4E,0xBD,0x7F,0x05,0x40,0x35,0x86,0x18,0xA1})) { //ignore known guids } else av_log(s, AV_LOG_WARNING, "unsupported chunk:"FF_PRI_GUID"\n", FF_ARG_GUID(g)); @@ -935,18 +923,6 @@ static int parse_chunks(AVFormatContext *s, int mode, int64_t seekts, int *len_p return AVERROR_EOF; } -/* declare utf16le strings */ -#define _ , 0, -static const uint8_t timeline_le16[] = - {'t'_'i'_'m'_'e'_'l'_'i'_'n'_'e', 0}; -static const uint8_t table_0_entries_legacy_attrib_le16[] = - {'t'_'a'_'b'_'l'_'e'_'.'_'0'_'.'_'e'_'n'_'t'_'r'_'i'_'e'_'s'_'.'_'l'_'e'_'g'_'a'_'c'_'y'_'_'_'a'_'t'_'t'_'r'_'i'_'b', 0}; -static const uint8_t table_0_entries_time_le16[] = - {'t'_'a'_'b'_'l'_'e'_'.'_'0'_'.'_'e'_'n'_'t'_'r'_'i'_'e'_'s'_'.'_'t'_'i'_'m'_'e', 0}; -static const uint8_t timeline_table_0_entries_Events_le16[] = - {'t'_'i'_'m'_'e'_'l'_'i'_'n'_'e'_'.'_'t'_'a'_'b'_'l'_'e'_'.'_'0'_'.'_'e'_'n'_'t'_'r'_'i'_'e'_'s'_'.'_'E'_'v'_'e'_'n'_'t'_'s', 0}; -#undef _ - static int read_header(AVFormatContext *s) { WtvContext *wtv = s->priv_data; @@ -976,7 +952,7 @@ static int read_header(AVFormatContext *s) return AVERROR_INVALIDDATA; /* parse chunks up until first data chunk */ - wtv->pb = wtvfile_open(s, root, root_size, timeline_le16); + wtv->pb = wtvfile_open(s, root, root_size, ff_timeline_le16); if (!wtv->pb) { av_log(s, AV_LOG_ERROR, "timeline data missing\n"); return AVERROR_INVALIDDATA; @@ -990,7 +966,7 @@ static int read_header(AVFormatContext *s) timeline_pos = avio_tell(s->pb); // save before opening another file /* read metadata */ - pb = wtvfile_open(s, root, root_size, table_0_entries_legacy_attrib_le16); + pb = wtvfile_open(s, root, root_size, ff_table_0_entries_legacy_attrib_le16); if (pb) { parse_legacy_attrib(s, pb); wtvfile_close(pb); @@ -999,12 +975,12 @@ static int read_header(AVFormatContext *s) /* read seek index */ if (s->nb_streams) { AVStream *st = s->streams[0]; - pb = wtvfile_open(s, root, root_size, table_0_entries_time_le16); + pb = wtvfile_open(s, root, root_size, ff_table_0_entries_time_le16); if (pb) { while(1) { uint64_t timestamp = avio_rl64(pb); uint64_t frame_nb = avio_rl64(pb); - if (pb->eof_reached) + if (url_feof(pb)) break; ff_add_index_entry(&wtv->index_entries, &wtv->nb_index_entries, &wtv->index_entries_allocated_size, 0, timestamp, frame_nb, 0, AVINDEX_KEYFRAME); @@ -1012,13 +988,13 @@ static int read_header(AVFormatContext *s) wtvfile_close(pb); if (wtv->nb_index_entries) { - pb = wtvfile_open(s, root, root_size, timeline_table_0_entries_Events_le16); + pb = wtvfile_open(s, root, root_size, ff_timeline_table_0_entries_Events_le16); if (pb) { int i; while (1) { uint64_t frame_nb = avio_rl64(pb); uint64_t position = avio_rl64(pb); - if (pb->eof_reached) + if (url_feof(pb)) break; for (i = wtv->nb_index_entries - 1; i >= 0; i--) { AVIndexEntry *e = wtv->index_entries + i; @@ -1078,26 +1054,30 @@ static int read_seek(AVFormatContext *s, int stream_index, i = ff_index_search_timestamp(wtv->index_entries, wtv->nb_index_entries, ts_relative, flags); if (i < 0) { - if (wtv->last_valid_pts == AV_NOPTS_VALUE || ts < wtv->last_valid_pts) - avio_seek(pb, 0, SEEK_SET); - else if (st->duration != AV_NOPTS_VALUE && ts_relative > st->duration && wtv->nb_index_entries) - avio_seek(pb, wtv->index_entries[wtv->nb_index_entries - 1].pos, SEEK_SET); + if (wtv->last_valid_pts == AV_NOPTS_VALUE || ts < wtv->last_valid_pts) { + if (avio_seek(pb, 0, SEEK_SET) < 0) + return -1; + } else if (st->duration != AV_NOPTS_VALUE && ts_relative > st->duration && wtv->nb_index_entries) { + if (avio_seek(pb, wtv->index_entries[wtv->nb_index_entries - 1].pos, SEEK_SET) < 0) + return -1; + } if (parse_chunks(s, SEEK_TO_PTS, ts, 0) < 0) return AVERROR(ERANGE); return 0; } + if (avio_seek(pb, wtv->index_entries[i].pos, SEEK_SET) < 0) + return -1; wtv->pts = wtv->index_entries[i].timestamp; if (wtv->epoch != AV_NOPTS_VALUE) wtv->pts += wtv->epoch; wtv->last_valid_pts = wtv->pts; - avio_seek(pb, wtv->index_entries[i].pos, SEEK_SET); return 0; } static int read_close(AVFormatContext *s) { WtvContext *wtv = s->priv_data; - av_free(wtv->index_entries); + av_freep(&wtv->index_entries); wtvfile_close(wtv->pb); return 0; } diff --git a/libavformat/wtvenc.c b/libavformat/wtvenc.c new file mode 100644 index 0000000000..04e6cc22f8 --- /dev/null +++ b/libavformat/wtvenc.c @@ -0,0 +1,805 @@ +/* + * Windows Television (WTV) muxer + * Copyright (c) 2011 Zhentan Feng <spyfeng at gmail dot com> + * Copyright (c) 2011 Peter Ross <pross@xvid.org> + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * Windows Television (WTV) demuxer + * @author Zhentan Feng <spyfeng at gmail dot com> + */ + +#include "libavutil/intreadwrite.h" +#include "libavutil/avassert.h" +#include "avformat.h" +#include "avio_internal.h" +#include "internal.h" +#include "wtv.h" +#include "asf.h" + +#define WTV_BIGSECTOR_SIZE (1 << WTV_BIGSECTOR_BITS) +#define INDEX_BASE 0x2 +#define MAX_NB_INDEX 10 + +/* declare utf16le strings */ +#define _ , 0, +static const uint8_t timeline_table_0_header_events[] = + {'t'_'i'_'m'_'e'_'l'_'i'_'n'_'e'_'.'_'t'_'a'_'b'_'l'_'e'_'.'_'0'_'.'_'h'_'e'_'a'_'d'_'e'_'r'_'.'_'E'_'v'_'e'_'n'_'t'_'s', 0}; +static const uint8_t table_0_header_legacy_attrib[] = + {'t'_'a'_'b'_'l'_'e'_'.'_'0'_'.'_'h'_'e'_'a'_'d'_'e'_'r'_'.'_'l'_'e'_'g'_'a'_'c'_'y'_'_'_'a'_'t'_'t'_'r'_'i'_'b', 0}; +static const uint8_t table_0_redirector_legacy_attrib[] = + {'t'_'a'_'b'_'l'_'e'_'.'_'0'_'.'_'r'_'e'_'d'_'i'_'r'_'e'_'c'_'t'_'o'_'r'_'.'_'l'_'e'_'g'_'a'_'c'_'y'_'_'_'a'_'t'_'t'_'r'_'i'_'b', 0}; +static const uint8_t table_0_header_time[] = + {'t'_'a'_'b'_'l'_'e'_'.'_'0'_'.'_'h'_'e'_'a'_'d'_'e'_'r'_'.'_'t'_'i'_'m'_'e', 0}; +static const uint8_t legacy_attrib[] = + {'l'_'e'_'g'_'a'_'c'_'y'_'_'_'a'_'t'_'t'_'r'_'i'_'b', 0}; +#undef _ + +static const ff_asf_guid sub_wtv_guid = + {0x8C,0xC3,0xD2,0xC2,0x7E,0x9A,0xDA,0x11,0x8B,0xF7,0x00,0x07,0xE9,0x5E,0xAD,0x8D}; + +enum WtvFileIndex { + WTV_TIMELINE_TABLE_0_HEADER_EVENTS = 0, + WTV_TIMELINE_TABLE_0_ENTRIES_EVENTS, + WTV_TIMELINE, + WTV_TABLE_0_HEADER_LEGACY_ATTRIB, + WTV_TABLE_0_ENTRIES_LEGACY_ATTRIB, + WTV_TABLE_0_REDIRECTOR_LEGACY_ATTRIB, + WTV_TABLE_0_HEADER_TIME, + WTV_TABLE_0_ENTRIES_TIME, + WTV_FILES +}; + +typedef struct { + int64_t length; + const void *header; + int depth; + int first_sector; +} WtvFile; + +typedef struct { + int64_t pos; + int64_t serial; + const ff_asf_guid * guid; + int stream_id; +} WtvChunkEntry; + +typedef struct { + int64_t serial; + int64_t value; +} WtvSyncEntry; + +typedef struct { + int64_t timeline_start_pos; + WtvFile file[WTV_FILES]; + int64_t serial; /**< chunk serial number */ + int64_t last_chunk_pos; /**< last chunk position */ + int64_t last_timestamp_pos; /**< last timestamp chunk position */ + int64_t first_index_pos; /**< first index_chunk position */ + + WtvChunkEntry index[MAX_NB_INDEX]; + int nb_index; + int first_video_flag; + + WtvSyncEntry *st_pairs; /* (serial, timestamp) pairs */ + int nb_st_pairs; + WtvSyncEntry *sp_pairs; /* (serial, position) pairs */ + int nb_sp_pairs; + + int64_t last_pts; + int64_t last_serial; + + AVPacket thumbnail; +} WtvContext; + + +static void add_serial_pair(WtvSyncEntry ** list, int * count, int64_t serial, int64_t value) +{ + int new_count = *count + 1; + WtvSyncEntry *new_list = av_realloc(*list, new_count * sizeof(WtvSyncEntry)); + if (!new_list) + return; + new_list[*count] = (WtvSyncEntry){serial, value}; + *list = new_list; + *count = new_count; +} + +typedef int WTVHeaderWriteFunc(AVIOContext *pb); + +typedef struct { + const uint8_t *header; + int header_size; + WTVHeaderWriteFunc *write_header; +} WTVRootEntryTable; + +#define write_pad(pb, size) ffio_fill(pb, 0, size) + +static const ff_asf_guid *get_codec_guid(enum AVCodecID id, const AVCodecGuid *av_guid) +{ + int i; + for (i = 0; av_guid[i].id != AV_CODEC_ID_NONE; i++) { + if (id == av_guid[i].id) + return &(av_guid[i].guid); + } + return NULL; +} + +/** + * Write chunk header. If header chunk (0x80000000 set) then add to list of header chunks + */ +static void write_chunk_header(AVFormatContext *s, const ff_asf_guid *guid, int length, int stream_id) +{ + WtvContext *wctx = s->priv_data; + AVIOContext *pb = s->pb; + + wctx->last_chunk_pos = avio_tell(pb) - wctx->timeline_start_pos; + ff_put_guid(pb, guid); + avio_wl32(pb, 32 + length); + avio_wl32(pb, stream_id); + avio_wl64(pb, wctx->serial); + + if ((stream_id & 0x80000000) && guid != &ff_index_guid) { + WtvChunkEntry *t = wctx->index + wctx->nb_index; + av_assert0(wctx->nb_index < MAX_NB_INDEX); + t->pos = wctx->last_chunk_pos; + t->serial = wctx->serial; + t->guid = guid; + t->stream_id = stream_id & 0x3FFFFFFF; + wctx->nb_index++; + } +} + +static void write_chunk_header2(AVFormatContext *s, const ff_asf_guid *guid, int stream_id) +{ + WtvContext *wctx = s->priv_data; + AVIOContext *pb = s->pb; + + int64_t last_chunk_pos = wctx->last_chunk_pos; + write_chunk_header(s, guid, 0, stream_id); // length updated later + avio_wl64(pb, last_chunk_pos); +} + +static void finish_chunk_noindex(AVFormatContext *s) +{ + WtvContext *wctx = s->priv_data; + AVIOContext *pb = s->pb; + + // update the chunk_len field and pad. + int64_t chunk_len = avio_tell(pb) - (wctx->last_chunk_pos + wctx->timeline_start_pos); + avio_seek(pb, -(chunk_len - 16), SEEK_CUR); + avio_wl32(pb, chunk_len); + avio_seek(pb, chunk_len - (16 + 4), SEEK_CUR); + + write_pad(pb, WTV_PAD8(chunk_len) - chunk_len); + wctx->serial++; +} + +static void write_index(AVFormatContext *s) +{ + AVIOContext *pb = s->pb; + WtvContext *wctx = s->priv_data; + int i; + + write_chunk_header2(s, &ff_index_guid, 0x80000000); + avio_wl32(pb, 0); + avio_wl32(pb, 0); + + for (i = 0; i < wctx->nb_index; i++) { + WtvChunkEntry *t = wctx->index + i; + ff_put_guid(pb, t->guid); + avio_wl64(pb, t->pos); + avio_wl32(pb, t->stream_id); + avio_wl32(pb, 0); // checksum? + avio_wl64(pb, t->serial); + } + wctx->nb_index = 0; // reset index + finish_chunk_noindex(s); + + if (!wctx->first_index_pos) + wctx->first_index_pos = wctx->last_chunk_pos; +} + +static void finish_chunk(AVFormatContext *s) +{ + WtvContext *wctx = s->priv_data; + finish_chunk_noindex(s); + if (wctx->nb_index == MAX_NB_INDEX) + write_index(s); +} + +static int write_stream_codec_info(AVFormatContext *s, AVStream *st) +{ + WtvContext *wctx = s->priv_data; + const ff_asf_guid *g, *media_type, *format_type; + AVIOContext *pb = s->pb; + int64_t hdr_pos_start; + int hdr_size = 0; + + if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) { + g = get_codec_guid(st->codec->codec_id, ff_video_guids); + media_type = &ff_mediatype_video; + format_type = &ff_format_mpeg2_video; + } else if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) { + g = get_codec_guid(st->codec->codec_id, ff_codec_wav_guids); + media_type = &ff_mediatype_audio; + format_type = &ff_format_waveformatex; + } else { + av_log(s, AV_LOG_ERROR, "unknown codec_type (0x%x)\n", st->codec->codec_type); + return -1; + } + + if (g == NULL) { + av_log(s, AV_LOG_ERROR, "can't get video codec_id (0x%x) guid.\n", st->codec->codec_id); + return -1; + } + + ff_put_guid(pb, media_type); // mediatype + ff_put_guid(pb, &ff_mediasubtype_cpfilters_processed); // subtype + write_pad(pb, 12); + ff_put_guid(pb,&ff_format_cpfilters_processed); // format type + avio_wl32(pb, 0); // size + + hdr_pos_start = avio_tell(pb); + if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) { + if (wctx->first_video_flag) { + write_pad(pb, 216); //The size is sensitive. + wctx->first_video_flag = 0; + } else { + write_pad(pb, 72); // aspect ratio + ff_put_bmp_header(pb, st->codec, ff_codec_bmp_tags, 0); + } + } else { + ff_put_wav_header(pb, st->codec); + } + hdr_size = avio_tell(pb) - hdr_pos_start; + + // seek back write hdr_size + avio_seek(pb, -(hdr_size + 4), SEEK_CUR); + avio_wl32(pb, hdr_size + 32); + avio_seek(pb, hdr_size, SEEK_CUR); + ff_put_guid(pb, g); // actual_subtype + ff_put_guid(pb, format_type); // actual_formattype + + return 0; +} + +static int write_stream_codec(AVFormatContext *s, AVStream * st) +{ + AVIOContext *pb = s->pb; + int ret; + write_chunk_header2(s, &ff_stream1_guid, 0x80000000 | 0x01); + + avio_wl32(pb, 0x01); + write_pad(pb, 4); + write_pad(pb, 4); + + ret = write_stream_codec_info(s, st); + if (ret < 0) { + av_log(s, AV_LOG_ERROR, "write stream codec info failed codec_type(0x%x)\n", st->codec->codec_type); + return -1; + } + + finish_chunk(s); + return 0; +} + +static void write_sync(AVFormatContext *s) +{ + AVIOContext *pb = s->pb; + WtvContext *wctx = s->priv_data; + int64_t last_chunk_pos = wctx->last_chunk_pos; + + write_chunk_header(s, &ff_sync_guid, 0x18, 0); + avio_wl64(pb, wctx->first_index_pos); + avio_wl64(pb, wctx->last_timestamp_pos); + avio_wl64(pb, 0); + + finish_chunk(s); + add_serial_pair(&wctx->sp_pairs, &wctx->nb_sp_pairs, wctx->serial, wctx->last_chunk_pos); + + wctx->last_chunk_pos = last_chunk_pos; +} + +static int write_stream_data(AVFormatContext *s, AVStream *st) +{ + AVIOContext *pb = s->pb; + int ret; + + write_chunk_header2(s, &ff_SBE2_STREAM_DESC_EVENT, 0x80000000 | (st->index + INDEX_BASE)); + avio_wl32(pb, 0x00000001); + avio_wl32(pb, st->index + INDEX_BASE); //stream_id + avio_wl32(pb, 0x00000001); + write_pad(pb, 8); + + ret = write_stream_codec_info(s, st); + if (ret < 0) { + av_log(s, AV_LOG_ERROR, "write stream codec info failed codec_type(0x%x)\n", st->codec->codec_type); + return -1; + } + finish_chunk(s); + + avpriv_set_pts_info(st, 64, 1, 10000000); + + return 0; +} + +static int write_header(AVFormatContext *s) +{ + AVIOContext *pb = s->pb; + WtvContext *wctx = s->priv_data; + int i, pad, ret; + AVStream *st; + + wctx->last_chunk_pos = -1; + wctx->last_timestamp_pos = -1; + + ff_put_guid(pb, &ff_wtv_guid); + ff_put_guid(pb, &sub_wtv_guid); + + avio_wl32(pb, 0x01); + avio_wl32(pb, 0x02); + avio_wl32(pb, 1 << WTV_SECTOR_BITS); + avio_wl32(pb, 1 << WTV_BIGSECTOR_BITS); + + //write initial root fields + avio_wl32(pb, 0); // root_size, update later + write_pad(pb, 4); + avio_wl32(pb, 0); // root_sector, update it later. + + write_pad(pb, 32); + avio_wl32(pb, 0); // file ends pointer, update it later. + + pad = (1 << WTV_SECTOR_BITS) - avio_tell(pb); + write_pad(pb, pad); + + wctx->timeline_start_pos = avio_tell(pb); + + wctx->serial = 1; + wctx->last_chunk_pos = -1; + wctx->first_video_flag = 1; + + for (i = 0; i < s->nb_streams; i++) { + st = s->streams[i]; + if (st->codec->codec_id == AV_CODEC_ID_MJPEG) + continue; + ret = write_stream_codec(s, st); + if (ret < 0) { + av_log(s, AV_LOG_ERROR, "write stream codec failed codec_type(0x%x)\n", st->codec->codec_type); + return -1; + } + if (!i) + write_sync(s); + } + + for (i = 0; i < s->nb_streams; i++) { + st = s->streams[i]; + if (st->codec->codec_id == AV_CODEC_ID_MJPEG) + continue; + ret = write_stream_data(s, st); + if (ret < 0) { + av_log(s, AV_LOG_ERROR, "write stream data failed codec_type(0x%x)\n", st->codec->codec_type); + return -1; + } + } + + if (wctx->nb_index) + write_index(s); + + return 0; +} + +static void write_timestamp(AVFormatContext *s, AVPacket *pkt) +{ + AVIOContext *pb = s->pb; + WtvContext *wctx = s->priv_data; + AVCodecContext *enc = s->streams[pkt->stream_index]->codec; + + write_chunk_header(s, &ff_timestamp_guid, 56, 0x40000000 | (INDEX_BASE + pkt->stream_index)); + write_pad(pb, 8); + avio_wl64(pb, pkt->pts == AV_NOPTS_VALUE ? -1 : pkt->pts); + avio_wl64(pb, pkt->pts == AV_NOPTS_VALUE ? -1 : pkt->pts); + avio_wl64(pb, pkt->pts == AV_NOPTS_VALUE ? -1 : pkt->pts); + avio_wl64(pb, 0); + avio_wl64(pb, enc->codec_type == AVMEDIA_TYPE_VIDEO && (pkt->flags & AV_PKT_FLAG_KEY) ? 1 : 0); + avio_wl64(pb, 0); + + wctx->last_timestamp_pos = wctx->last_chunk_pos; +} + +static int write_packet(AVFormatContext *s, AVPacket *pkt) +{ + AVIOContext *pb = s->pb; + WtvContext *wctx = s->priv_data; + + if (s->streams[pkt->stream_index]->codec->codec_id == AV_CODEC_ID_MJPEG && !wctx->thumbnail.size) { + av_copy_packet(&wctx->thumbnail, pkt); + return 0; + } + + /* emit sync chunk and 'timeline.table.0.entries.Event' record every 50 frames */ + if (wctx->serial - (wctx->nb_sp_pairs ? wctx->sp_pairs[wctx->nb_sp_pairs - 1].serial : 0) >= 50) + write_sync(s); + + /* emit 'table.0.entries.time' record every 500ms */ + if (pkt->pts != AV_NOPTS_VALUE && pkt->pts - (wctx->nb_st_pairs ? wctx->st_pairs[wctx->nb_st_pairs - 1].value : 0) >= 5000000) + add_serial_pair(&wctx->st_pairs, &wctx->nb_st_pairs, wctx->serial, pkt->pts); + + if (pkt->pts != AV_NOPTS_VALUE && pkt->pts > wctx->last_pts) { + wctx->last_pts = pkt->pts; + wctx->last_serial = wctx->serial; + } + + // write timestamp chunk + write_timestamp(s, pkt); + + write_chunk_header(s, &ff_data_guid, pkt->size, INDEX_BASE + pkt->stream_index); + avio_write(pb, pkt->data, pkt->size); + write_pad(pb, WTV_PAD8(pkt->size) - pkt->size); + + wctx->serial++; + return 0; +} + +static int write_table0_header_events(AVIOContext *pb) +{ + avio_wl32(pb, 0x10); + write_pad(pb, 84); + avio_wl64(pb, 0x32); + return 96; +} + +static int write_table0_header_legacy_attrib(AVIOContext *pb) +{ + int pad = 0; + avio_wl32(pb, 0xFFFFFFFF); + write_pad(pb, 12); + avio_write(pb, legacy_attrib, sizeof(legacy_attrib)); + pad = WTV_PAD8(sizeof(legacy_attrib)) - sizeof(legacy_attrib); + write_pad(pb, pad); + write_pad(pb, 32); + return 48 + WTV_PAD8(sizeof(legacy_attrib)); +} + +static int write_table0_header_time(AVIOContext *pb) +{ + avio_wl32(pb, 0x10); + write_pad(pb, 76); + avio_wl64(pb, 0x40); + return 88; +} + +static const WTVRootEntryTable wtv_root_entry_table[] = { + { timeline_table_0_header_events, sizeof(timeline_table_0_header_events), write_table0_header_events}, + { ff_timeline_table_0_entries_Events_le16, sizeof(ff_timeline_table_0_entries_Events_le16), NULL}, + { ff_timeline_le16, sizeof(ff_timeline_le16), NULL}, + { table_0_header_legacy_attrib, sizeof(table_0_header_legacy_attrib), write_table0_header_legacy_attrib}, + { ff_table_0_entries_legacy_attrib_le16, sizeof(ff_table_0_entries_legacy_attrib_le16), NULL}, + { table_0_redirector_legacy_attrib, sizeof(table_0_redirector_legacy_attrib), NULL}, + { table_0_header_time, sizeof(table_0_header_time), write_table0_header_time}, + { ff_table_0_entries_time_le16, sizeof(ff_table_0_entries_time_le16), NULL}, +}; + +static int write_root_table(AVFormatContext *s, int64_t sector_pos) +{ + AVIOContext *pb = s->pb; + WtvContext *wctx = s->priv_data; + int size, pad; + int i; + + const WTVRootEntryTable *h = wtv_root_entry_table; + for (i = 0; i < sizeof(wtv_root_entry_table)/sizeof(WTVRootEntryTable); i++, h++) { + WtvFile *w = &wctx->file[i]; + int filename_padding = WTV_PAD8(h->header_size) - h->header_size; + WTVHeaderWriteFunc *write = h->write_header; + int len = 0; + int64_t len_pos; + + ff_put_guid(pb, &ff_dir_entry_guid); + len_pos = avio_tell(pb); + avio_wl16(pb, 40 + h->header_size + filename_padding + 8); // maybe updated later + write_pad(pb, 6); + avio_wl64(pb, write ? 0 : w->length);// maybe update later + avio_wl32(pb, (h->header_size + filename_padding) >> 1); + write_pad(pb, 4); + + avio_write(pb, h->header, h->header_size); + write_pad(pb, filename_padding); + + if (write) { + len = write(pb); + // update length field + avio_seek(pb, len_pos, SEEK_SET); + avio_wl64(pb, 40 + h->header_size + filename_padding + len); + avio_wl64(pb, len |(1ULL<<62) | (1ULL<<60)); + avio_seek(pb, 8 + h->header_size + filename_padding + len, SEEK_CUR); + } else { + avio_wl32(pb, w->first_sector); + avio_wl32(pb, w->depth); + } + } + + // caculate root table size + size = avio_tell(pb) - sector_pos; + pad = WTV_SECTOR_SIZE- size; + write_pad(pb, pad); + + return size; +} + +static void write_fat(AVIOContext *pb, int start_sector, int nb_sectors, int shift) +{ + int i; + for (i = 0; i < nb_sectors; i++) { + avio_wl32(pb, start_sector + (i << shift)); + } + // pad left sector pointer size + write_pad(pb, WTV_SECTOR_SIZE - ((nb_sectors << 2) % WTV_SECTOR_SIZE)); +} + +static int64_t write_fat_sector(AVFormatContext *s, int64_t start_pos, int nb_sectors, int sector_bits, int depth) +{ + int64_t start_sector = start_pos >> WTV_SECTOR_BITS; + int shift = sector_bits - WTV_SECTOR_BITS; + + int64_t fat = avio_tell(s->pb); + write_fat(s->pb, start_sector, nb_sectors, shift); + + if (depth == 2) { + int64_t start_sector1 = fat >> WTV_SECTOR_BITS; + int nb_sectors1 = ((nb_sectors << 2) + WTV_SECTOR_SIZE - 1) / WTV_SECTOR_SIZE; + int64_t fat1 = avio_tell(s->pb); + + write_fat(s->pb, start_sector1, nb_sectors1, 0); + return fat1; + } + + return fat; +} + +static void write_table_entries_events(AVFormatContext *s) +{ + AVIOContext *pb = s->pb; + WtvContext *wctx = s->priv_data; + int i; + for (i = 0; i < wctx->nb_sp_pairs; i++) { + avio_wl64(pb, wctx->sp_pairs[i].serial); + avio_wl64(pb, wctx->sp_pairs[i].value); + } +} + +static void write_table_entries_time(AVFormatContext *s) +{ + AVIOContext *pb = s->pb; + WtvContext *wctx = s->priv_data; + int i; + for (i = 0; i < wctx->nb_st_pairs; i++) { + avio_wl64(pb, wctx->st_pairs[i].value); + avio_wl64(pb, wctx->st_pairs[i].serial); + } + avio_wl64(pb, wctx->last_pts); + avio_wl64(pb, wctx->last_serial); +} + +static void write_metadata_header(AVIOContext *pb, int type, const char *key, int value_size) +{ + ff_put_guid(pb, &ff_metadata_guid); + avio_wl32(pb, type); + avio_wl32(pb, value_size); + avio_put_str16le(pb, key); +} + +static int metadata_header_size(const char *key) +{ + return 16 + 4 + 4 + strlen(key)*2 + 2; +} + +static void write_tag_int32(AVIOContext *pb, const char *key, int value) +{ + write_metadata_header(pb, 0, key, 4); + avio_wl32(pb, value); +} + +static void write_tag(AVIOContext *pb, const char *key, const char *value) +{ + write_metadata_header(pb, 1, key, strlen(value)*2 + 2); + avio_put_str16le(pb, value); +} + +static int attachment_value_size(const AVPacket *pkt, const AVDictionaryEntry *e) +{ + return strlen("image/jpeg")*2 + 2 + 1 + (e ? strlen(e->value)*2 : 0) + 2 + 4 + pkt->size; +} + +static void write_table_entries_attrib(AVFormatContext *s) +{ + WtvContext *wctx = s->priv_data; + AVIOContext *pb = s->pb; + AVDictionaryEntry *tag = 0; + + //FIXME: translate special tags (e.g. WM/Bitrate) to binary representation + ff_metadata_conv(&s->metadata, ff_asf_metadata_conv, NULL); + while ((tag = av_dict_get(s->metadata, "", tag, AV_DICT_IGNORE_SUFFIX))) + write_tag(pb, tag->key, tag->value); + + if (wctx->thumbnail.size) { + AVStream *st = s->streams[wctx->thumbnail.stream_index]; + tag = av_dict_get(st->metadata, "title", NULL, 0); + write_metadata_header(pb, 2, "WM/Picture", attachment_value_size(&wctx->thumbnail, tag)); + + avio_put_str16le(pb, "image/jpeg"); + avio_w8(pb, 0x10); + avio_put_str16le(pb, tag ? tag->value : ""); + + avio_wl32(pb, wctx->thumbnail.size); + avio_write(pb, wctx->thumbnail.data, wctx->thumbnail.size); + + write_tag_int32(pb, "WM/MediaThumbType", 2); + } +} + +static void write_table_redirector_legacy_attrib(AVFormatContext *s) +{ + WtvContext *wctx = s->priv_data; + AVIOContext *pb = s->pb; + AVDictionaryEntry *tag = 0; + int64_t pos = 0; + + //FIXME: translate special tags to binary representation + while ((tag = av_dict_get(s->metadata, "", tag, AV_DICT_IGNORE_SUFFIX))) { + avio_wl64(pb, pos); + pos += metadata_header_size(tag->key) + strlen(tag->value)*2 + 2; + } + + if (wctx->thumbnail.size) { + AVStream *st = s->streams[wctx->thumbnail.stream_index]; + avio_wl64(pb, pos); + pos += metadata_header_size("WM/Picture") + attachment_value_size(&wctx->thumbnail, av_dict_get(st->metadata, "title", NULL, 0)); + + avio_wl64(pb, pos); + pos += metadata_header_size("WM/MediaThumbType") + 4; + } +} + +/** + * Pad the remainder of a file + * Write out fat table + * @return <0 on error + */ +static int finish_file(AVFormatContext *s, enum WtvFileIndex index, int64_t start_pos) +{ + WtvContext *wctx = s->priv_data; + AVIOContext *pb = s->pb; + WtvFile *w = &wctx->file[index]; + int64_t end_pos = avio_tell(pb); + int sector_bits, nb_sectors, pad; + + av_assert0(index < WTV_FILES); + + w->length = (end_pos - start_pos); + + // determine optimal fat table depth, sector_bits, nb_sectors + if (w->length <= WTV_SECTOR_SIZE) { + w->depth = 0; + sector_bits = WTV_SECTOR_BITS; + } else if (w->length <= (WTV_SECTOR_SIZE / 4) * WTV_SECTOR_SIZE) { + w->depth = 1; + sector_bits = WTV_SECTOR_BITS; + } else if (w->length <= (WTV_SECTOR_SIZE / 4) * WTV_BIGSECTOR_SIZE) { + w->depth = 1; + sector_bits = WTV_BIGSECTOR_BITS; + } else if (w->length <= (int64_t)(WTV_SECTOR_SIZE / 4) * (WTV_SECTOR_SIZE / 4) * WTV_SECTOR_SIZE) { + w->depth = 2; + sector_bits = WTV_SECTOR_BITS; + } else if (w->length <= (int64_t)(WTV_SECTOR_SIZE / 4) * (WTV_SECTOR_SIZE / 4) * WTV_BIGSECTOR_SIZE) { + w->depth = 2; + sector_bits = WTV_BIGSECTOR_BITS; + } else { + av_log(s, AV_LOG_ERROR, "unsupported file allocation table depth (%"PRIi64" bytes)\n", w->length); + return -1; + } + + // determine the nb_sectors + nb_sectors = (int)(w->length >> sector_bits); + + // pad sector of timeline + pad = (1 << sector_bits) - (w->length % (1 << sector_bits)); + if (pad) { + nb_sectors++; + write_pad(pb, pad); + } + + //write fat table + if (w->depth > 0) { + w->first_sector = write_fat_sector(s, start_pos, nb_sectors, sector_bits, w->depth) >> WTV_SECTOR_BITS; + } else { + w->first_sector = start_pos >> WTV_SECTOR_BITS; + } + + w->length |= 1ULL<<60; + if (sector_bits == WTV_SECTOR_BITS) + w->length |= 1ULL<<63; + + return 0; +} + +static int write_trailer(AVFormatContext *s) +{ + WtvContext *wctx = s->priv_data; + AVIOContext *pb = s->pb; + int root_size; + int64_t sector_pos; + int64_t start_pos, file_end_pos; + + if (finish_file(s, WTV_TIMELINE, wctx->timeline_start_pos) < 0) + return -1; + + start_pos = avio_tell(pb); + write_table_entries_events(s); + if (finish_file(s, WTV_TIMELINE_TABLE_0_ENTRIES_EVENTS, start_pos) < 0) + return -1; + + start_pos = avio_tell(pb); + write_table_entries_attrib(s); + if (finish_file(s, WTV_TABLE_0_ENTRIES_LEGACY_ATTRIB, start_pos) < 0) + return -1; + + start_pos = avio_tell(pb); + write_table_redirector_legacy_attrib(s); + if (finish_file(s, WTV_TABLE_0_REDIRECTOR_LEGACY_ATTRIB, start_pos) < 0) + return -1; + + start_pos = avio_tell(pb); + write_table_entries_time(s); + if (finish_file(s, WTV_TABLE_0_ENTRIES_TIME, start_pos) < 0) + return -1; + + // write root table + sector_pos = avio_tell(pb); + root_size = write_root_table(s, sector_pos); + + file_end_pos = avio_tell(pb); + // update root value + avio_seek(pb, 0x30, SEEK_SET); + avio_wl32(pb, root_size); + avio_seek(pb, 4, SEEK_CUR); + avio_wl32(pb, sector_pos >> WTV_SECTOR_BITS); + avio_seek(pb, 0x5c, SEEK_SET); + avio_wl32(pb, file_end_pos >> WTV_SECTOR_BITS); + + avio_flush(pb); + + av_free(wctx->sp_pairs); + av_free(wctx->st_pairs); + av_free_packet(&wctx->thumbnail); + return 0; +} + +AVOutputFormat ff_wtv_muxer = { + .name = "wtv", + .long_name = NULL_IF_CONFIG_SMALL("Windows Television (WTV)"), + .extensions = "wtv", + .priv_data_size = sizeof(WtvContext), + .audio_codec = AV_CODEC_ID_AC3, + .video_codec = AV_CODEC_ID_MPEG2VIDEO, + .write_header = write_header, + .write_packet = write_packet, + .write_trailer = write_trailer, + .codec_tag = (const AVCodecTag* const []){ ff_codec_bmp_tags, + ff_codec_wav_tags, 0 }, +}; diff --git a/libavformat/wv.c b/libavformat/wv.c index 724256b19c..0f4f80761a 100644 --- a/libavformat/wv.c +++ b/libavformat/wv.c @@ -1,20 +1,20 @@ /* * WavPack shared functions * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/wv.h b/libavformat/wv.h index ef285d2ebc..07d2e1bc0a 100644 --- a/libavformat/wv.h +++ b/libavformat/wv.h @@ -1,20 +1,20 @@ /* * WavPack shared functions * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/wvdec.c b/libavformat/wvdec.c index 1a2a722cb7..4eaec402f7 100644 --- a/libavformat/wvdec.c +++ b/libavformat/wvdec.c @@ -2,20 +2,20 @@ * WavPack demuxer * Copyright (c) 2006,2011 Konstantin Shishkov * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -64,8 +64,11 @@ static int wv_probe(AVProbeData *p) /* check file header */ if (p->buf_size <= 32) return 0; - if (p->buf[0] == 'w' && p->buf[1] == 'v' && - p->buf[2] == 'p' && p->buf[3] == 'k') + if (AV_RL32(&p->buf[0]) == MKTAG('w', 'v', 'p', 'k') && + AV_RL32(&p->buf[4]) >= 24 && + AV_RL32(&p->buf[4]) <= WV_BLOCK_LIMIT && + AV_RL16(&p->buf[8]) >= 0x402 && + AV_RL16(&p->buf[8]) <= 0x410) return AVPROBE_SCORE_MAX; else return 0; @@ -235,7 +238,8 @@ static int wv_read_header(AVFormatContext *s) st->codec->bits_per_coded_sample = wc->bpp; avpriv_set_pts_info(st, 64, 1, wc->rate); st->start_time = 0; - st->duration = wc->header.total_samples; + if (wc->header.total_samples != 0xFFFFFFFFu) + st->duration = wc->header.total_samples; if (s->pb->seekable) { int64_t cur = avio_tell(s->pb); @@ -256,7 +260,7 @@ static int wv_read_packet(AVFormatContext *s, AVPacket *pkt) int64_t pos; uint32_t block_samples; - if (s->pb->eof_reached) + if (url_feof(s->pb)) return AVERROR_EOF; if (wc->block_parsed) { if ((ret = wv_read_block_header(s, s->pb)) < 0) diff --git a/libavformat/wvenc.c b/libavformat/wvenc.c index 2e150e1a7a..b0d74caaec 100644 --- a/libavformat/wvenc.c +++ b/libavformat/wvenc.c @@ -1,18 +1,22 @@ /* - * This file is part of Libav. + * WavPack muxer + * Copyright (c) 2013 Konstantin Shishkov <kostya.shishkov@gmail.com> + * Copyright (c) 2012 Paul B Mahol * - * Libav is free software; you can redistribute it and/or + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/xa.c b/libavformat/xa.c index 57a36bcbe2..43661dea43 100644 --- a/libavformat/xa.c +++ b/libavformat/xa.c @@ -2,20 +2,20 @@ * Maxis XA (.xa) File Demuxer * Copyright (c) 2008 Robert Marston * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -84,6 +84,9 @@ static int xa_read_header(AVFormatContext *s) avio_skip(pb, 2); /* Skip block align */ avio_skip(pb, 2); /* Skip bits-per-sample */ + if (!st->codec->channels || !st->codec->sample_rate) + return AVERROR_INVALIDDATA; + st->codec->bit_rate = av_clip(15LL * st->codec->channels * 8 * st->codec->sample_rate / 28, 0, INT_MAX); diff --git a/libavformat/xmv.c b/libavformat/xmv.c index 2b2fd4551c..20f9bea55b 100644 --- a/libavformat/xmv.c +++ b/libavformat/xmv.c @@ -3,20 +3,20 @@ * Copyright (c) 2011 Sven Hesse <drmccoy@drmccoy.de> * Copyright (c) 2011 Matthew Hoops <clone2727@gmail.com> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -32,83 +32,81 @@ #include "avformat.h" #include "internal.h" #include "riff.h" +#include "libavutil/avassert.h" +/** The min size of an XMV header. */ #define XMV_MIN_HEADER_SIZE 36 +/** Audio flag: ADPCM'd 5.1 stream, front left / right channels */ #define XMV_AUDIO_ADPCM51_FRONTLEFTRIGHT 1 +/** Audio flag: ADPCM'd 5.1 stream, front center / low frequency channels */ #define XMV_AUDIO_ADPCM51_FRONTCENTERLOW 2 +/** Audio flag: ADPCM'd 5.1 stream, rear left / right channels */ #define XMV_AUDIO_ADPCM51_REARLEFTRIGHT 4 +/** Audio flag: Any of the ADPCM'd 5.1 stream flags. */ #define XMV_AUDIO_ADPCM51 (XMV_AUDIO_ADPCM51_FRONTLEFTRIGHT | \ XMV_AUDIO_ADPCM51_FRONTCENTERLOW | \ XMV_AUDIO_ADPCM51_REARLEFTRIGHT) #define XMV_BLOCK_ALIGN_SIZE 36 -typedef struct XMVAudioTrack { - uint16_t compression; - uint16_t channels; - uint32_t sample_rate; - uint16_t bits_per_sample; - uint32_t bit_rate; - uint16_t flags; - uint16_t block_align; - uint16_t block_samples; - - enum AVCodecID codec_id; -} XMVAudioTrack; - +/** A video packet with an XMV file. */ typedef struct XMVVideoPacket { - /* The decoder stream index for this video packet. */ - int stream_index; - - uint32_t data_size; - uint32_t data_offset; + int stream_index; ///< The decoder stream index for this video packet. - uint32_t current_frame; - uint32_t frame_count; + uint32_t data_size; ///< The size of the remaining video data. + uint64_t data_offset; ///< The offset of the video data within the file. - /* Does the video packet contain extra data? */ - int has_extradata; + uint32_t current_frame; ///< The current frame within this video packet. + uint32_t frame_count; ///< The amount of frames within this video packet. - /* Extra data */ - uint8_t extradata[4]; + int has_extradata; ///< Does the video packet contain extra data? + uint8_t extradata[4]; ///< The extra data - int64_t last_pts; - int64_t pts; + int64_t last_pts; ///< PTS of the last video frame. + int64_t pts; ///< PTS of the most current video frame. } XMVVideoPacket; +/** An audio packet with an XMV file. */ typedef struct XMVAudioPacket { - /* The decoder stream index for this audio packet. */ - int stream_index; + int stream_index; ///< The decoder stream index for this audio packet. - /* The audio track this packet encodes. */ - XMVAudioTrack *track; + /* Stream format properties. */ + uint16_t compression; ///< The type of compression. + uint16_t channels; ///< Number of channels. + uint32_t sample_rate; ///< Sampling rate. + uint16_t bits_per_sample; ///< Bits per compressed sample. + uint32_t bit_rate; ///< Bits of compressed data per second. + uint16_t flags; ///< Flags + unsigned block_align; ///< Bytes per compressed block. + uint16_t block_samples; ///< Decompressed samples per compressed block. - uint32_t data_size; - uint32_t data_offset; + enum AVCodecID codec_id; ///< The codec ID of the compression scheme. - uint32_t frame_size; + uint32_t data_size; ///< The size of the remaining audio data. + uint64_t data_offset; ///< The offset of the audio data within the file. - uint32_t block_count; + uint32_t frame_size; ///< Number of bytes to put into an audio frame. + + uint64_t block_count; ///< Running counter of decompressed audio block. } XMVAudioPacket; +/** Context for demuxing an XMV file. */ typedef struct XMVDemuxContext { - uint16_t audio_track_count; - - XMVAudioTrack *audio_tracks; + uint16_t audio_track_count; ///< Number of audio track in this file. - uint32_t this_packet_size; - uint32_t next_packet_size; + uint32_t this_packet_size; ///< Size of the current packet. + uint32_t next_packet_size; ///< Size of the next packet. - uint32_t this_packet_offset; - uint32_t next_packet_offset; + uint64_t this_packet_offset; ///< Offset of the current packet. + uint64_t next_packet_offset; ///< Offset of the next packet. - uint16_t current_stream; - uint16_t stream_count; + uint16_t current_stream; ///< The index of the stream currently handling. + uint16_t stream_count; ///< The number of streams in this file. - XMVVideoPacket video; - XMVAudioPacket *audio; + XMVVideoPacket video; ///< The video packet contained in each packet. + XMVAudioPacket *audio; ///< The audio packets contained in each packet. } XMVDemuxContext; static int xmv_probe(AVProbeData *p) @@ -132,8 +130,7 @@ static int xmv_read_close(AVFormatContext *s) { XMVDemuxContext *xmv = s->priv_data; - av_free(xmv->audio); - av_free(xmv->audio_tracks); + av_freep(&xmv->audio); return 0; } @@ -185,10 +182,6 @@ static int xmv_read_header(AVFormatContext *s) avio_skip(pb, 2); /* Unknown (padding?) */ - xmv->audio_tracks = av_malloc(xmv->audio_track_count * sizeof(XMVAudioTrack)); - if (!xmv->audio_tracks) - return AVERROR(ENOMEM); - xmv->audio = av_malloc(xmv->audio_track_count * sizeof(XMVAudioPacket)); if (!xmv->audio) { ret = AVERROR(ENOMEM); @@ -196,25 +189,23 @@ static int xmv_read_header(AVFormatContext *s) } for (audio_track = 0; audio_track < xmv->audio_track_count; audio_track++) { - XMVAudioTrack *track = &xmv->audio_tracks[audio_track]; - XMVAudioPacket *packet = &xmv->audio [audio_track]; + XMVAudioPacket *packet = &xmv->audio[audio_track]; AVStream *ast = NULL; - track->compression = avio_rl16(pb); - track->channels = avio_rl16(pb); - track->sample_rate = avio_rl32(pb); - track->bits_per_sample = avio_rl16(pb); - track->flags = avio_rl16(pb); - - track->bit_rate = track->bits_per_sample * - track->sample_rate * - track->channels; - track->block_align = XMV_BLOCK_ALIGN_SIZE * track->channels; - track->block_samples = 64; - track->codec_id = ff_wav_codec_get_id(track->compression, - track->bits_per_sample); - - packet->track = track; + packet->compression = avio_rl16(pb); + packet->channels = avio_rl16(pb); + packet->sample_rate = avio_rl32(pb); + packet->bits_per_sample = avio_rl16(pb); + packet->flags = avio_rl16(pb); + + packet->bit_rate = packet->bits_per_sample * + packet->sample_rate * + packet->channels; + packet->block_align = XMV_BLOCK_ALIGN_SIZE * packet->channels; + packet->block_samples = 64; + packet->codec_id = ff_wav_codec_get_id(packet->compression, + packet->bits_per_sample); + packet->stream_index = -1; packet->frame_size = 0; @@ -222,12 +213,12 @@ static int xmv_read_header(AVFormatContext *s) /* TODO: ADPCM'd 5.1 sound is encoded in three separate streams. * Those need to be interleaved to a proper 5.1 stream. */ - if (track->flags & XMV_AUDIO_ADPCM51) + if (packet->flags & XMV_AUDIO_ADPCM51) av_log(s, AV_LOG_WARNING, "Unsupported 5.1 ADPCM audio stream " - "(0x%04X)\n", track->flags); + "(0x%04X)\n", packet->flags); - if (!track->channels || !track->sample_rate || - track->channels >= UINT16_MAX / XMV_BLOCK_ALIGN_SIZE) { + if (!packet->channels || !packet->sample_rate || + packet->channels >= UINT16_MAX / XMV_BLOCK_ALIGN_SIZE) { av_log(s, AV_LOG_ERROR, "Invalid parameters for audio track %d.\n", audio_track); ret = AVERROR_INVALIDDATA; @@ -241,15 +232,15 @@ static int xmv_read_header(AVFormatContext *s) } ast->codec->codec_type = AVMEDIA_TYPE_AUDIO; - ast->codec->codec_id = track->codec_id; - ast->codec->codec_tag = track->compression; - ast->codec->channels = track->channels; - ast->codec->sample_rate = track->sample_rate; - ast->codec->bits_per_coded_sample = track->bits_per_sample; - ast->codec->bit_rate = track->bit_rate; - ast->codec->block_align = 36 * track->channels; + ast->codec->codec_id = packet->codec_id; + ast->codec->codec_tag = packet->compression; + ast->codec->channels = packet->channels; + ast->codec->sample_rate = packet->sample_rate; + ast->codec->bits_per_coded_sample = packet->bits_per_sample; + ast->codec->bit_rate = packet->bit_rate; + ast->codec->block_align = 36 * packet->channels; - avpriv_set_pts_info(ast, 32, track->block_samples, track->sample_rate); + avpriv_set_pts_info(ast, 32, packet->block_samples, packet->sample_rate); packet->stream_index = ast->index; @@ -257,7 +248,7 @@ static int xmv_read_header(AVFormatContext *s) } - /** Initialize the packet context */ + /* Initialize the packet context */ xmv->next_packet_offset = avio_tell(pb); xmv->next_packet_size = this_packet_size - xmv->next_packet_offset; @@ -306,7 +297,7 @@ static int xmv_process_packet_header(AVFormatContext *s) uint8_t data[8]; uint16_t audio_track; - uint32_t data_offset; + uint64_t data_offset; /* Next packet size */ xmv->next_packet_size = avio_rl32(pb); @@ -337,7 +328,7 @@ static int xmv_process_packet_header(AVFormatContext *s) xmv->current_stream = 0; if (!xmv->video.frame_count) { xmv->video.frame_count = 1; - xmv->current_stream = 1; + xmv->current_stream = xmv->stream_count > 1; } /* Packet audio header */ @@ -357,9 +348,9 @@ static int xmv_process_packet_header(AVFormatContext *s) */ packet->data_size = xmv->audio[audio_track - 1].data_size; - /** Carve up the audio data in frame_count slices */ + /* Carve up the audio data in frame_count slices */ packet->frame_size = packet->data_size / xmv->video.frame_count; - packet->frame_size -= packet->frame_size % packet->track->block_align; + packet->frame_size -= packet->frame_size % packet->block_align; } /* Packet data offsets */ @@ -387,14 +378,12 @@ static int xmv_process_packet_header(AVFormatContext *s) if (xmv->video.stream_index >= 0) { AVStream *vst = s->streams[xmv->video.stream_index]; - assert(xmv->video.stream_index < s->nb_streams); + av_assert0(xmv->video.stream_index < s->nb_streams); if (vst->codec->extradata_size < 4) { av_free(vst->codec->extradata); - vst->codec->extradata = - av_malloc(4 + FF_INPUT_BUFFER_PADDING_SIZE); - vst->codec->extradata_size = 4; + ff_alloc_extradata(vst->codec, 4); } memcpy(vst->codec->extradata, xmv->video.extradata, 4); @@ -411,6 +400,9 @@ static int xmv_fetch_new_packet(AVFormatContext *s) AVIOContext *pb = s->pb; int result; + if (xmv->this_packet_offset == xmv->next_packet_offset) + return AVERROR_EOF; + /* Seek to it */ xmv->this_packet_offset = xmv->next_packet_offset; if (avio_seek(pb, xmv->this_packet_offset, SEEK_SET) != xmv->this_packet_offset) @@ -463,7 +455,7 @@ static int xmv_fetch_audio_packet(AVFormatContext *s, /* Calculate the PTS */ - block_count = data_size / audio->track->block_align; + block_count = data_size / audio->block_align; pkt->duration = block_count; pkt->pts = audio->block_count; @@ -488,7 +480,7 @@ static int xmv_fetch_video_packet(AVFormatContext *s, int result; uint32_t frame_header; uint32_t frame_size, frame_timestamp; - uint32_t i; + uint8_t *data, *end; /* Seek to it */ if (avio_seek(pb, video->data_offset, SEEK_SET) != video->data_offset) @@ -503,17 +495,17 @@ static int xmv_fetch_video_packet(AVFormatContext *s, if ((frame_size + 4) > video->data_size) return AVERROR(EIO); - /* Create the packet */ - result = av_new_packet(pkt, frame_size); - if (result) + /* Get the packet data */ + result = av_get_packet(pb, pkt, frame_size); + if (result != frame_size) return result; /* Contrary to normal WMV2 video, the bit stream in XMV's * WMV2 is little-endian. * TODO: This manual swap is of course suboptimal. */ - for (i = 0; i < frame_size; i += 4) - AV_WB32(pkt->data + i, avio_rl32(pb)); + for (data = pkt->data, end = pkt->data + frame_size; data < end; data += 4) + AV_WB32(data, AV_RL32(data)); pkt->stream_index = video->stream_index; diff --git a/libavformat/xwma.c b/libavformat/xwma.c index f4ff8157f8..135faf2f7a 100644 --- a/libavformat/xwma.c +++ b/libavformat/xwma.c @@ -2,20 +2,20 @@ * xWMA demuxer * Copyright (c) 2011 Max Horn * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/yop.c b/libavformat/yop.c index 8caeb07157..07086d53b8 100644 --- a/libavformat/yop.c +++ b/libavformat/yop.c @@ -5,20 +5,20 @@ * derived from the code by * Copyright (C) 2009 Thomas P. Higdon <thomas.p.higdon@gmail.com> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -39,10 +39,15 @@ typedef struct yop_dec_context { static int yop_probe(AVProbeData *probe_packet) { if (AV_RB16(probe_packet->buf) == AV_RB16("YO") && + probe_packet->buf[2]<10 && + probe_packet->buf[3]<10 && probe_packet->buf[6] && probe_packet->buf[7] && !(probe_packet->buf[8] & 1) && - !(probe_packet->buf[10] & 1)) + !(probe_packet->buf[10] & 1) && + AV_RL16(probe_packet->buf + 12 + 6) >= 920 && + AV_RL16(probe_packet->buf + 12 + 6) < probe_packet->buf[12] * 3 + 4 + probe_packet->buf[7] * 2048 + ) return AVPROBE_SCORE_MAX * 3 / 4; return 0; @@ -60,14 +65,11 @@ static int yop_read_header(AVFormatContext *s) audio_stream = avformat_new_stream(s, NULL); video_stream = avformat_new_stream(s, NULL); + if (!audio_stream || !video_stream) + return AVERROR(ENOMEM); // Extra data that will be passed to the decoder - video_stream->codec->extradata_size = 8; - - video_stream->codec->extradata = av_mallocz(video_stream->codec->extradata_size + - FF_INPUT_BUFFER_PADDING_SIZE); - - if (!video_stream->codec->extradata) + if (ff_alloc_extradata(video_stream->codec, 8)) return AVERROR(ENOMEM); // Audio @@ -99,6 +101,8 @@ static int yop_read_header(AVFormatContext *s) yop->palette_size = video_dec->extradata[0] * 3 + 4; yop->audio_block_length = AV_RL16(video_dec->extradata + 6); + video_dec->bit_rate = 8 * (yop->frame_size - yop->audio_block_length) * frame_rate; + // 1840 samples per frame, 1 nibble per sample; hence 1840/2 = 920 if (yop->audio_block_length < 920 || yop->audio_block_length + yop->palette_size >= yop->frame_size) { diff --git a/libavformat/yuv4mpeg.c b/libavformat/yuv4mpeg.c index cc2755f4b6..1999c73364 100644 --- a/libavformat/yuv4mpeg.c +++ b/libavformat/yuv4mpeg.c @@ -2,20 +2,20 @@ * YUV4MPEG format * Copyright (c) 2001, 2002, 2003 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -27,11 +27,6 @@ #define Y4M_FRAME_MAGIC "FRAME" #define Y4M_LINE_MAX 256 -struct frame_attributes { - int interlaced_frame; - int top_field_first; -}; - #if CONFIG_YUV4MPEGPIPE_MUXER static int yuv4_generate_header(AVFormatContext *s, char* buf) { @@ -57,11 +52,21 @@ static int yuv4_generate_header(AVFormatContext *s, char* buf) inter = 'p'; /* progressive is the default */ if (st->codec->coded_frame && st->codec->coded_frame->interlaced_frame) inter = st->codec->coded_frame->top_field_first ? 't' : 'b'; + if (st->codec->field_order == AV_FIELD_PROGRESSIVE) { + inter = 'p'; + } else if (st->codec->field_order == AV_FIELD_TB || st->codec->field_order == AV_FIELD_TT) { + inter = 't'; + } else if (st->codec->field_order == AV_FIELD_BT || st->codec->field_order == AV_FIELD_BB) { + inter = 'b'; + } switch (st->codec->pix_fmt) { case AV_PIX_FMT_GRAY8: colorspace = " Cmono"; break; + case AV_PIX_FMT_GRAY16: + colorspace = " Cmono16"; + break; case AV_PIX_FMT_YUV411P: colorspace = " C411 XYSCSS=411"; break; @@ -78,6 +83,51 @@ static int yuv4_generate_header(AVFormatContext *s, char* buf) case AV_PIX_FMT_YUV444P: colorspace = " C444 XYSCSS=444"; break; + case AV_PIX_FMT_YUV420P9: + colorspace = " C420p9 XYSCSS=420P9"; + break; + case AV_PIX_FMT_YUV422P9: + colorspace = " C422p9 XYSCSS=422P9"; + break; + case AV_PIX_FMT_YUV444P9: + colorspace = " C444p9 XYSCSS=444P9"; + break; + case AV_PIX_FMT_YUV420P10: + colorspace = " C420p10 XYSCSS=420P10"; + break; + case AV_PIX_FMT_YUV422P10: + colorspace = " C422p10 XYSCSS=422P10"; + break; + case AV_PIX_FMT_YUV444P10: + colorspace = " C444p10 XYSCSS=444P10"; + break; + case AV_PIX_FMT_YUV420P12: + colorspace = " C420p12 XYSCSS=420P12"; + break; + case AV_PIX_FMT_YUV422P12: + colorspace = " C422p12 XYSCSS=422P12"; + break; + case AV_PIX_FMT_YUV444P12: + colorspace = " C444p12 XYSCSS=444P12"; + break; + case AV_PIX_FMT_YUV420P14: + colorspace = " C420p14 XYSCSS=420P14"; + break; + case AV_PIX_FMT_YUV422P14: + colorspace = " C422p14 XYSCSS=422P14"; + break; + case AV_PIX_FMT_YUV444P14: + colorspace = " C444p14 XYSCSS=444P14"; + break; + case AV_PIX_FMT_YUV420P16: + colorspace = " C420p16 XYSCSS=420P16"; + break; + case AV_PIX_FMT_YUV422P16: + colorspace = " C422p16 XYSCSS=422P16"; + break; + case AV_PIX_FMT_YUV444P16: + colorspace = " C444p16 XYSCSS=444P16"; + break; } /* construct stream header, if this is the first frame */ @@ -92,7 +142,7 @@ static int yuv4_write_packet(AVFormatContext *s, AVPacket *pkt) { AVStream *st = s->streams[pkt->stream_index]; AVIOContext *pb = s->pb; - AVPicture *picture; + AVPicture *picture, picture_tmp; int* first_pkt = s->priv_data; int width, height, h_chroma_shift, v_chroma_shift; int i; @@ -100,7 +150,8 @@ static int yuv4_write_packet(AVFormatContext *s, AVPacket *pkt) char buf1[20]; uint8_t *ptr, *ptr1, *ptr2; - picture = (AVPicture *)pkt->data; + memcpy(&picture_tmp, pkt->data, sizeof(AVPicture)); + picture = &picture_tmp; /* for the first packet we have to output the header as well */ if (*first_pkt) { @@ -123,18 +174,50 @@ static int yuv4_write_packet(AVFormatContext *s, AVPacket *pkt) height = st->codec->height; ptr = picture->data[0]; + + switch (st->codec->pix_fmt) { + case AV_PIX_FMT_GRAY8: + case AV_PIX_FMT_YUV411P: + case AV_PIX_FMT_YUV420P: + case AV_PIX_FMT_YUV422P: + case AV_PIX_FMT_YUV444P: + break; + case AV_PIX_FMT_GRAY16: + case AV_PIX_FMT_YUV420P9: + case AV_PIX_FMT_YUV422P9: + case AV_PIX_FMT_YUV444P9: + case AV_PIX_FMT_YUV420P10: + case AV_PIX_FMT_YUV422P10: + case AV_PIX_FMT_YUV444P10: + case AV_PIX_FMT_YUV420P12: + case AV_PIX_FMT_YUV422P12: + case AV_PIX_FMT_YUV444P12: + case AV_PIX_FMT_YUV420P14: + case AV_PIX_FMT_YUV422P14: + case AV_PIX_FMT_YUV444P14: + case AV_PIX_FMT_YUV420P16: + case AV_PIX_FMT_YUV422P16: + case AV_PIX_FMT_YUV444P16: + width *= 2; + break; + default: + av_log(s, AV_LOG_ERROR, "The pixel format '%s' is not supported.\n", + av_get_pix_fmt_name(st->codec->pix_fmt)); + return AVERROR(EINVAL); + } + for (i = 0; i < height; i++) { avio_write(pb, ptr, width); ptr += picture->linesize[0]; } - if (st->codec->pix_fmt != AV_PIX_FMT_GRAY8) { + if (st->codec->pix_fmt != AV_PIX_FMT_GRAY8 && + st->codec->pix_fmt != AV_PIX_FMT_GRAY16) { // Adjust for smaller Cb and Cr planes av_pix_fmt_get_chroma_sub_sample(st->codec->pix_fmt, &h_chroma_shift, &v_chroma_shift); - // Shift right, rounding up - width = -(-width >> h_chroma_shift); - height = -(-height >> v_chroma_shift); + width = FF_CEIL_RSHIFT(width, h_chroma_shift); + height = FF_CEIL_RSHIFT(height, v_chroma_shift); ptr1 = picture->data[1]; ptr2 = picture->data[2]; @@ -147,6 +230,7 @@ static int yuv4_write_packet(AVFormatContext *s, AVPacket *pkt) ptr2 += picture->linesize[2]; } } + return 0; } @@ -162,15 +246,50 @@ static int yuv4_write_header(AVFormatContext *s) return AVERROR_INVALIDDATA; } - if (s->streams[0]->codec->pix_fmt == AV_PIX_FMT_YUV411P) { - av_log(s, AV_LOG_ERROR, "Warning: generating rarely used 4:1:1 YUV " + switch (s->streams[0]->codec->pix_fmt) { + case AV_PIX_FMT_YUV411P: + av_log(s, AV_LOG_WARNING, "Warning: generating rarely used 4:1:1 YUV " "stream, some mjpegtools might not work.\n"); - } else if ((s->streams[0]->codec->pix_fmt != AV_PIX_FMT_YUV420P) && - (s->streams[0]->codec->pix_fmt != AV_PIX_FMT_YUV422P) && - (s->streams[0]->codec->pix_fmt != AV_PIX_FMT_GRAY8) && - (s->streams[0]->codec->pix_fmt != AV_PIX_FMT_YUV444P)) { - av_log(s, AV_LOG_ERROR, "ERROR: yuv4mpeg only handles yuv444p, " - "yuv422p, yuv420p, yuv411p and gray pixel formats. " + break; + case AV_PIX_FMT_GRAY8: + case AV_PIX_FMT_GRAY16: + case AV_PIX_FMT_YUV420P: + case AV_PIX_FMT_YUV422P: + case AV_PIX_FMT_YUV444P: + break; + case AV_PIX_FMT_YUV420P9: + case AV_PIX_FMT_YUV422P9: + case AV_PIX_FMT_YUV444P9: + case AV_PIX_FMT_YUV420P10: + case AV_PIX_FMT_YUV422P10: + case AV_PIX_FMT_YUV444P10: + case AV_PIX_FMT_YUV420P12: + case AV_PIX_FMT_YUV422P12: + case AV_PIX_FMT_YUV444P12: + case AV_PIX_FMT_YUV420P14: + case AV_PIX_FMT_YUV422P14: + case AV_PIX_FMT_YUV444P14: + case AV_PIX_FMT_YUV420P16: + case AV_PIX_FMT_YUV422P16: + case AV_PIX_FMT_YUV444P16: + if (s->streams[0]->codec->strict_std_compliance >= FF_COMPLIANCE_NORMAL) { + av_log(s, AV_LOG_ERROR, "'%s' is not a official yuv4mpegpipe pixel format. " + "Use '-strict -1' to encode to this pixel format.\n", + av_get_pix_fmt_name(s->streams[0]->codec->pix_fmt)); + return AVERROR(EINVAL); + } + av_log(s, AV_LOG_WARNING, "Warning: generating non standard YUV stream. " + "Mjpegtools will not work.\n"); + break; + default: + av_log(s, AV_LOG_ERROR, "ERROR: yuv4mpeg can only handle " + "yuv444p, yuv422p, yuv420p, yuv411p and gray8 pixel formats. " + "And using 'strict -1' also yuv444p9, yuv422p9, yuv420p9, " + "yuv444p10, yuv422p10, yuv420p10, " + "yuv444p12, yuv422p12, yuv420p12, " + "yuv444p14, yuv422p14, yuv420p14, " + "yuv444p16, yuv422p16, yuv420p16 " + "and gray16 pixel formats. " "Use -pix_fmt to select one.\n"); return AVERROR(EIO); } @@ -182,7 +301,6 @@ static int yuv4_write_header(AVFormatContext *s) AVOutputFormat ff_yuv4mpegpipe_muxer = { .name = "yuv4mpegpipe", .long_name = NULL_IF_CONFIG_SMALL("YUV4MPEG pipe"), - .mime_type = "", .extensions = "y4m", .priv_data_size = sizeof(int), .audio_codec = AV_CODEC_ID_NONE, @@ -201,7 +319,7 @@ static int yuv4_read_header(AVFormatContext *s) { char header[MAX_YUV4_HEADER + 10]; // Include headroom for // the longest option - char *tokstart, *tokend, *header_end; + char *tokstart, *tokend, *header_end, interlaced = '?'; int i; AVIOContext *pb = s->pb; int width = -1, height = -1, raten = 0, @@ -209,7 +327,6 @@ static int yuv4_read_header(AVFormatContext *s) enum AVPixelFormat pix_fmt = AV_PIX_FMT_NONE, alt_pix_fmt = AV_PIX_FMT_NONE; enum AVChromaLocation chroma_sample_location = AVCHROMA_LOC_UNSPECIFIED; AVStream *st; - struct frame_attributes *s1 = s->priv_data; for (i = 0; i < MAX_YUV4_HEADER; i++) { header[i] = avio_r8(pb); @@ -225,8 +342,6 @@ static int yuv4_read_header(AVFormatContext *s) if (strncmp(header, Y4M_MAGIC, strlen(Y4M_MAGIC))) return -1; - s1->interlaced_frame = 0; - s1->top_field_first = 0; header_end = &header[i + 1]; // Include space for (tokstart = &header[strlen(Y4M_MAGIC) + 1]; tokstart < header_end; tokstart++) { @@ -251,20 +366,52 @@ static int yuv4_read_header(AVFormatContext *s) } else if (strncmp("420paldv", tokstart, 8) == 0) { pix_fmt = AV_PIX_FMT_YUV420P; chroma_sample_location = AVCHROMA_LOC_TOPLEFT; + } else if (strncmp("420p16", tokstart, 6) == 0) { + pix_fmt = AV_PIX_FMT_YUV420P16; + } else if (strncmp("422p16", tokstart, 6) == 0) { + pix_fmt = AV_PIX_FMT_YUV422P16; + } else if (strncmp("444p16", tokstart, 6) == 0) { + pix_fmt = AV_PIX_FMT_YUV444P16; + } else if (strncmp("420p14", tokstart, 6) == 0) { + pix_fmt = AV_PIX_FMT_YUV420P14; + } else if (strncmp("422p14", tokstart, 6) == 0) { + pix_fmt = AV_PIX_FMT_YUV422P14; + } else if (strncmp("444p14", tokstart, 6) == 0) { + pix_fmt = AV_PIX_FMT_YUV444P14; + } else if (strncmp("420p12", tokstart, 6) == 0) { + pix_fmt = AV_PIX_FMT_YUV420P12; + } else if (strncmp("422p12", tokstart, 6) == 0) { + pix_fmt = AV_PIX_FMT_YUV422P12; + } else if (strncmp("444p12", tokstart, 6) == 0) { + pix_fmt = AV_PIX_FMT_YUV444P12; + } else if (strncmp("420p10", tokstart, 6) == 0) { + pix_fmt = AV_PIX_FMT_YUV420P10; + } else if (strncmp("422p10", tokstart, 6) == 0) { + pix_fmt = AV_PIX_FMT_YUV422P10; + } else if (strncmp("444p10", tokstart, 6) == 0) { + pix_fmt = AV_PIX_FMT_YUV444P10; + } else if (strncmp("420p9", tokstart, 5) == 0) { + pix_fmt = AV_PIX_FMT_YUV420P9; + } else if (strncmp("422p9", tokstart, 5) == 0) { + pix_fmt = AV_PIX_FMT_YUV422P9; + } else if (strncmp("444p9", tokstart, 5) == 0) { + pix_fmt = AV_PIX_FMT_YUV444P9; } else if (strncmp("420", tokstart, 3) == 0) { pix_fmt = AV_PIX_FMT_YUV420P; chroma_sample_location = AVCHROMA_LOC_CENTER; - } else if (strncmp("411", tokstart, 3) == 0) + } else if (strncmp("411", tokstart, 3) == 0) { pix_fmt = AV_PIX_FMT_YUV411P; - else if (strncmp("422", tokstart, 3) == 0) + } else if (strncmp("422", tokstart, 3) == 0) { pix_fmt = AV_PIX_FMT_YUV422P; - else if (strncmp("444alpha", tokstart, 8) == 0 ) { + } else if (strncmp("444alpha", tokstart, 8) == 0 ) { av_log(s, AV_LOG_ERROR, "Cannot handle 4:4:4:4 " "YUV4MPEG stream.\n"); return -1; - } else if (strncmp("444", tokstart, 3) == 0) + } else if (strncmp("444", tokstart, 3) == 0) { pix_fmt = AV_PIX_FMT_YUV444P; - else if (strncmp("mono", tokstart, 4) == 0) { + } else if (strncmp("mono16", tokstart, 6) == 0) { + pix_fmt = AV_PIX_FMT_GRAY16; + } else if (strncmp("mono", tokstart, 4) == 0) { pix_fmt = AV_PIX_FMT_GRAY8; } else { av_log(s, AV_LOG_ERROR, "YUV4MPEG stream contains an unknown " @@ -275,28 +422,7 @@ static int yuv4_read_header(AVFormatContext *s) tokstart++; break; case 'I': // Interlace type - switch (*tokstart++){ - case '?': - break; - case 'p': - s1->interlaced_frame = 0; - break; - case 't': - s1->interlaced_frame = 1; - s1->top_field_first = 1; - break; - case 'b': - s1->interlaced_frame = 1; - s1->top_field_first = 0; - break; - case 'm': - av_log(s, AV_LOG_ERROR, "YUV4MPEG stream contains mixed " - "interlaced and non-interlaced frames.\n"); - return -1; - default: - av_log(s, AV_LOG_ERROR, "YUV4MPEG has invalid header.\n"); - return -1; - } + interlaced = *tokstart++; break; case 'F': // Frame rate sscanf(tokstart, "%d:%d", &raten, &rated); // 0:0 if unknown @@ -318,6 +444,36 @@ static int yuv4_read_header(AVFormatContext *s) alt_pix_fmt = AV_PIX_FMT_YUV420P; else if (strncmp("420PALDV", tokstart, 8) == 0) alt_pix_fmt = AV_PIX_FMT_YUV420P; + else if (strncmp("420P9", tokstart, 5) == 0) + alt_pix_fmt = AV_PIX_FMT_YUV420P9; + else if (strncmp("422P9", tokstart, 5) == 0) + alt_pix_fmt = AV_PIX_FMT_YUV422P9; + else if (strncmp("444P9", tokstart, 5) == 0) + alt_pix_fmt = AV_PIX_FMT_YUV444P9; + else if (strncmp("420P10", tokstart, 6) == 0) + alt_pix_fmt = AV_PIX_FMT_YUV420P10; + else if (strncmp("422P10", tokstart, 6) == 0) + alt_pix_fmt = AV_PIX_FMT_YUV422P10; + else if (strncmp("444P10", tokstart, 6) == 0) + alt_pix_fmt = AV_PIX_FMT_YUV444P10; + else if (strncmp("420P12", tokstart, 6) == 0) + alt_pix_fmt = AV_PIX_FMT_YUV420P12; + else if (strncmp("422P12", tokstart, 6) == 0) + alt_pix_fmt = AV_PIX_FMT_YUV422P12; + else if (strncmp("444P12", tokstart, 6) == 0) + alt_pix_fmt = AV_PIX_FMT_YUV444P12; + else if (strncmp("420P14", tokstart, 6) == 0) + alt_pix_fmt = AV_PIX_FMT_YUV420P14; + else if (strncmp("422P14", tokstart, 6) == 0) + alt_pix_fmt = AV_PIX_FMT_YUV422P14; + else if (strncmp("444P14", tokstart, 6) == 0) + alt_pix_fmt = AV_PIX_FMT_YUV444P14; + else if (strncmp("420P16", tokstart, 6) == 0) + alt_pix_fmt = AV_PIX_FMT_YUV420P16; + else if (strncmp("422P16", tokstart, 6) == 0) + alt_pix_fmt = AV_PIX_FMT_YUV422P16; + else if (strncmp("444P16", tokstart, 6) == 0) + alt_pix_fmt = AV_PIX_FMT_YUV444P16; else if (strncmp("411", tokstart, 3) == 0) alt_pix_fmt = AV_PIX_FMT_YUV411P; else if (strncmp("422", tokstart, 3) == 0) @@ -367,6 +523,27 @@ static int yuv4_read_header(AVFormatContext *s) st->sample_aspect_ratio = (AVRational){ aspectn, aspectd }; st->codec->chroma_sample_location = chroma_sample_location; + switch (interlaced){ + case 'p': + st->codec->field_order = AV_FIELD_PROGRESSIVE; + break; + case 't': + st->codec->field_order = AV_FIELD_TB; + break; + case 'b': + st->codec->field_order = AV_FIELD_BT; + break; + case 'm': + av_log(s, AV_LOG_ERROR, "YUV4MPEG stream contains mixed " + "interlaced and non-interlaced frames.\n"); + case '?': + st->codec->field_order = AV_FIELD_UNKNOWN; + break; + default: + av_log(s, AV_LOG_ERROR, "YUV4MPEG has invalid header.\n"); + return AVERROR(EINVAL); + } + return 0; } @@ -376,7 +553,6 @@ static int yuv4_read_packet(AVFormatContext *s, AVPacket *pkt) char header[MAX_FRAME_HEADER+1]; int packet_size, width, height, ret; AVStream *st = s->streams[0]; - struct frame_attributes *s1 = s->priv_data; for (i = 0; i < MAX_FRAME_HEADER; i++) { header[i] = avio_r8(s->pb); @@ -408,11 +584,6 @@ static int yuv4_read_packet(AVFormatContext *s, AVPacket *pkt) else if (ret != packet_size) return s->pb->eof_reached ? AVERROR_EOF : AVERROR(EIO); - if (st->codec->coded_frame) { - st->codec->coded_frame->interlaced_frame = s1->interlaced_frame; - st->codec->coded_frame->top_field_first = s1->top_field_first; - } - pkt->stream_index = 0; return 0; } @@ -430,7 +601,6 @@ static int yuv4_probe(AVProbeData *pd) AVInputFormat ff_yuv4mpegpipe_demuxer = { .name = "yuv4mpegpipe", .long_name = NULL_IF_CONFIG_SMALL("YUV4MPEG pipe"), - .priv_data_size = sizeof(struct frame_attributes), .read_probe = yuv4_probe, .read_header = yuv4_read_header, .read_packet = yuv4_read_packet, |