diff options
Diffstat (limited to 'libavformat')
479 files changed, 41895 insertions, 8214 deletions
diff --git a/libavformat/4xm.c b/libavformat/4xm.c index 6253ffb..240e5a4 100644 --- a/libavformat/4xm.c +++ b/libavformat/4xm.c @@ -1,21 +1,21 @@ /* * 4X Technologies .4xm File Demuxer (no muxer) - * Copyright (c) 2003 The ffmpeg 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 */ @@ -134,8 +134,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); @@ -155,6 +158,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) @@ -222,9 +230,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; } @@ -280,7 +293,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 (avio_feof(pb)) return AVERROR(EIO); switch (fourcc_tag) { case LIST_TAG: @@ -309,8 +322,10 @@ static int fourxm_read_packet(AVFormatContext *s, if (ret < 0) { av_free_packet(pkt); - } else + } else { packet_read = 1; + av_shrink_packet(pkt, ret + 8); + } break; case snd__TAG: diff --git a/libavformat/Makefile b/libavformat/Makefile index a048157..3d124fb 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -1,3 +1,5 @@ +include $(SUBDIR)../config.mak + NAME = avformat HEADERS = avformat.h \ @@ -12,7 +14,6 @@ OBJS = allformats.o \ format.o \ id3v1.o \ id3v2.o \ - log2_tab.o \ metadata.o \ mux.o \ options.o \ @@ -32,9 +33,11 @@ OBJS-$(CONFIG_RTPDEC) += rdt.o \ rtpdec_amr.o \ rtpdec_asf.o \ rtpdec_g726.o \ + rtpdec_h261.o \ rtpdec_h263.o \ rtpdec_h263_rfc2190.o \ rtpdec_h264.o \ + rtpdec_hevc.o \ rtpdec_ilbc.o \ rtpdec_jpeg.o \ rtpdec_latm.o \ @@ -49,47 +52,68 @@ 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 golomb_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 img2.o \ + id3v2enc.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_AVI_MUXER) += avienc.o mpegtsenc.o avlanguage.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 replaygain.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_CINE_DEMUXER) += cinedec.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) += dauddec.o OBJS-$(CONFIG_DAUD_MUXER) += daudenc.o OBJS-$(CONFIG_DFA_DEMUXER) += dfa.o @@ -97,7 +121,9 @@ OBJS-$(CONFIG_DIRAC_DEMUXER) += diracdec.o rawdec.o OBJS-$(CONFIG_DIRAC_MUXER) += rawenc.o OBJS-$(CONFIG_DNXHD_DEMUXER) += dnxhddec.o rawdec.o OBJS-$(CONFIG_DNXHD_MUXER) += rawenc.o +OBJS-$(CONFIG_DSF_DEMUXER) += dsfdec.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 @@ -107,6 +133,9 @@ 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 OBJS-$(CONFIG_FFMETADATA_MUXER) += ffmetaenc.o OBJS-$(CONFIG_FILMSTRIP_DEMUXER) += filmstripdec.o @@ -120,17 +149,22 @@ OBJS-$(CONFIG_FLAC_MUXER) += flacenc.o flacenc_header.o \ vorbiscomment.o OBJS-$(CONFIG_FLIC_DEMUXER) += flic.o OBJS-$(CONFIG_FLV_DEMUXER) += flvdec.o +OBJS-$(CONFIG_LIVE_FLV_DEMUXER) += flvdec.o 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 @@ -143,7 +177,10 @@ OBJS-$(CONFIG_HEVC_MUXER) += rawenc.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 @@ -151,37 +188,63 @@ OBJS-$(CONFIG_IMAGE2_DEMUXER) += img2dec.o img2.o OBJS-$(CONFIG_IMAGE2_MUXER) += img2enc.o img2.o OBJS-$(CONFIG_IMAGE2PIPE_DEMUXER) += img2dec.o img2.o OBJS-$(CONFIG_IMAGE2PIPE_MUXER) += img2enc.o img2.o +OBJS-$(CONFIG_IMAGE2_ALIAS_PIX_DEMUXER) += img2_alias_pix.o +OBJS-$(CONFIG_IMAGE2_BRENDER_PIX_DEMUXER) += img2_brender_pix.o +OBJS-$(CONFIG_IMAGE_BMP_PIPE_DEMUXER) += img2dec.o img2.o +OBJS-$(CONFIG_IMAGE_DPX_PIPE_DEMUXER) += img2dec.o img2.o +OBJS-$(CONFIG_IMAGE_EXR_PIPE_DEMUXER) += img2dec.o img2.o +OBJS-$(CONFIG_IMAGE_J2K_PIPE_DEMUXER) += img2dec.o img2.o +OBJS-$(CONFIG_IMAGE_JPEGLS_PIPE_DEMUXER) += img2dec.o img2.o +OBJS-$(CONFIG_IMAGE_PICTOR_PIPE_DEMUXER) += img2dec.o img2.o +OBJS-$(CONFIG_IMAGE_PNG_PIPE_DEMUXER) += img2dec.o img2.o +OBJS-$(CONFIG_IMAGE_SGI_PIPE_DEMUXER) += img2dec.o img2.o +OBJS-$(CONFIG_IMAGE_SUNRAST_PIPE_DEMUXER) += img2dec.o img2.o +OBJS-$(CONFIG_IMAGE_TIFF_PIPE_DEMUXER) += img2dec.o img2.o +OBJS-$(CONFIG_IMAGE_WEBP_PIPE_DEMUXER) += img2dec.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_LRC_DEMUXER) += lrcdec.o lrc.o subtitles.o +OBJS-$(CONFIG_LRC_MUXER) += lrcenc.o lrc.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 OBJS-$(CONFIG_MATROSKA_DEMUXER) += matroskadec.o matroska.o \ - isom.o rmsipr.o \ + isom.o rmsipr.o flac_picture.o \ oggparsevorbis.o vorbiscomment.o \ flac_picture.o replaygain.o OBJS-$(CONFIG_MATROSKA_MUXER) += matroskaenc.o matroska.o \ isom.o avc.o hevc.o \ - flacenc_header.o avlanguage.o vorbiscomment.o wv.o + flacenc_header.o avlanguage.o vorbiscomment.o wv.o \ + webmdashenc.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_MLV_DEMUXER) += mlvdec.o riffdec.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 replaygain.o OBJS-$(CONFIG_MOV_MUXER) += movenc.o isom.o avc.o hevc.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 replaygain.o OBJS-$(CONFIG_MP3_MUXER) += mp3enc.o rawenc.o id3v2enc.o @@ -199,6 +262,8 @@ 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_MV_DEMUXER) += mvdec.o @@ -207,6 +272,7 @@ 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 @@ -222,13 +288,18 @@ OBJS-$(CONFIG_OGG_DEMUXER) += oggdec.o \ oggparsespeex.o \ oggparsetheora.o \ oggparsevorbis.o \ + oggparsevp8.o \ replaygain.o \ vorbiscomment.o \ flac_picture.o +OBJS-$(CONFIG_OGA_MUXER) += oggenc.o \ + vorbiscomment.o 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_OPUS_MUXER) += oggenc.o \ + vorbiscomment.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 @@ -270,17 +341,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 @@ -288,6 +364,7 @@ OBJS-$(CONFIG_RTP_MUXER) += rtp.o \ rtpenc_aac.o \ rtpenc_latm.o \ rtpenc_amr.o \ + rtpenc_h261.o \ rtpenc_h263.o \ rtpenc_h263_rfc2190.o \ rtpenc_jpeg.o \ @@ -301,9 +378,12 @@ 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_SDR2_DEMUXER) += sdr2.o OBJS-$(CONFIG_SEGAFILM_DEMUXER) += segafilm.o OBJS-$(CONFIG_SEGMENT_MUXER) += segment.o OBJS-$(CONFIG_SHORTEN_DEMUXER) += rawdec.o @@ -315,45 +395,69 @@ 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_SPEEX_MUXER) += oggenc.o \ + vorbiscomment.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_UNCODEDFRAMECRC_MUXER) += uncodedframecrcenc.o framehash.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 hevc.o \ flacenc_header.o avlanguage.o \ - wv.o vorbiscomment.o + wv.o vorbiscomment.o \ + webmdashenc.o +OBJS-$(CONFIG_WEBM_DASH_MANIFEST_DEMUXER)+= matroskadec.o matroska.o \ + isom.o rmsipr.o flac_picture.o \ + oggparsevorbis.o vorbiscomment.o \ + flac_picture.o replaygain.o +OBJS-$(CONFIG_WEBM_DASH_MANIFEST_MUXER) += webmdashenc.o matroska.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 \ + mpegtsenc.o asf.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 @@ -361,15 +465,26 @@ OBJS-$(CONFIG_YUV4MPEGPIPE_MUXER) += yuv4mpegenc.o OBJS-$(CONFIG_YUV4MPEGPIPE_DEMUXER) += yuv4mpegdec.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 +OBJS-$(CONFIG_LIBSMBCLIENT_PROTOCOL) += libsmbclient.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,6 +504,7 @@ OBJS-$(CONFIG_RTMPTS_PROTOCOL) += rtmpproto.o rtmppkt.o OBJS-$(CONFIG_RTP_PROTOCOL) += rtpproto.o OBJS-$(CONFIG_SCTP_PROTOCOL) += sctp.o OBJS-$(CONFIG_SRTP_PROTOCOL) += srtpproto.o srtp.o +OBJS-$(CONFIG_SUBFILE_PROTOCOL) += subfile.o OBJS-$(CONFIG_TCP_PROTOCOL) += tcp.o OBJS-$(CONFIG_TLS_PROTOCOL) += tls.o OBJS-$(CONFIG_UDP_PROTOCOL) += udp.o @@ -396,6 +512,9 @@ OBJS-$(CONFIG_UNIX_PROTOCOL) += unix.o OBJS-$(HAVE_LIBC_MSVCRT) += file_open.o +# Windows resource file +SLIBOBJS-$(HAVE_GNU_WINDRES) += avformatres.o + SKIPHEADERS-$(CONFIG_FFRTMPCRYPT_PROTOCOL) += rtmpdh.h SKIPHEADERS-$(CONFIG_NETWORK) += network.h rtsp.h @@ -409,3 +528,4 @@ TOOLS = aviocat \ ismindex \ pktdumper \ probetest \ + seek_print \ diff --git a/libavformat/a64.c b/libavformat/a64.c index c8e8e64..a7f93a2 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" @@ -41,7 +40,7 @@ static int a64_write_header(AVFormatContext *s) return AVERROR_INVALIDDATA; } - switch (avctx->codec->id) { + switch (avctx->codec_id) { case AV_CODEC_ID_A64_MULTI: header[2] = 0x00; header[3] = AV_RB32(avctx->extradata+0); diff --git a/libavformat/aacdec.c b/libavformat/aacdec.c index 566c3e7..1b98e1d 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,15 +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; @@ -55,6 +56,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); @@ -84,9 +86,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 4ceffa5..58ef44d 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,8 +27,8 @@ static int ac3_eac3_probe(AVProbeData *p, enum AVCodecID expected_codec_id) { int max_frames, first_frames = 0, frames; - uint8_t *buf, *buf2, *end; - AC3HeaderInfo hdr; + const uint8_t *buf, *buf2, *end; + AC3HeaderInfo *phdr = NULL; GetBitContext gbc; enum AVCodecID codec_id = AV_CODEC_ID_AC3; @@ -37,51 +37,54 @@ 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); - if(avpriv_ac3_parse_header(&gbc, &hdr) < 0) + 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_header2(&gbc, &phdr) < 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 + phdr->frame_size > end) break; - if (hdr.bitstream_id > 10) + if (buf[0] == 0x77 && buf[1] == 0x0B) { + av_assert0(phdr->frame_size <= sizeof(buf3)); + for(i=8; i<phdr->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, phdr->frame_size - 2)) + break; + if (phdr->bitstream_id > 10) codec_id = AV_CODEC_ID_EAC3; - buf2 += hdr.frame_size; + buf2 += phdr->frame_size; } max_frames = FFMAX(max_frames, frames); if(buf == p->buf) first_frames = frames; } + av_freep(&phdr); 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 0000000..3f223d5 --- /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 0000000..8a3661a --- /dev/null +++ b/libavformat/adp.c @@ -0,0 +1,98 @@ +/* + * 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, changes = 0; + char last = 0; + + 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; + if (p->buf[i] != last) + changes++; + last = p->buf[i]; + } + if (changes <= 1) + 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 (avio_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 5194e75..d624b79 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,22 @@ #include "libavcodec/put_bits.h" #include "libavcodec/avcodec.h" #include "libavcodec/mpeg4audio.h" +#include "libavutil/opt.h" #include "avformat.h" +#include "apetag.h" +#include "id3v2.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; + int id3v2tag; uint8_t pce_data[MAX_PCE_SIZE]; } ADTSContext; @@ -93,6 +99,8 @@ static int adts_write_header(AVFormatContext *s) ADTSContext *adts = s->priv_data; AVCodecContext *avc = s->streams[0]->codec; + if (adts->id3v2tag) + ff_id3v2_write_simple(s, 4, ID3v2_DEFAULT_MAGIC); if (avc->extradata_size > 0) return adts_decode_extradata(s, adts, avc->extradata, avc->extradata_size); @@ -162,6 +170,31 @@ 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_id3v2", "Enable ID3v2 tag writing", OFFSET(id3v2tag), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, ENC}, + { "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,5 +205,7 @@ 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, .flags = AVFMT_NOTIMESTAMPS, }; diff --git a/libavformat/adxdec.c b/libavformat/adxdec.c index fc83ff2..ddaa201 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 */ @@ -76,14 +76,8 @@ 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_get_extradata(avctx, s->pb, c->header_size) < 0) return AVERROR(ENOMEM); - if (avio_read(s->pb, avctx->extradata, c->header_size) < c->header_size) { - av_freep(&avctx->extradata); - return AVERROR(EIO); - } - avctx->extradata_size = c->header_size; if (avctx->extradata_size < 12) { av_log(s, AV_LOG_ERROR, "Invalid extradata size.\n"); diff --git a/libavformat/aea.c b/libavformat/aea.c index 7675009..15b9b1c 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 0000000..8dbd232 --- /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 f88f957..4470254 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 6243bcb..91ef2a4 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 (avio_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, size); + 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,31 @@ 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_get_extradata(st->codec, pb, size) < 0) 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,15 +344,23 @@ static int aiff_read_packet(AVFormatContext *s, return AVERROR_EOF; /* Now for that packet */ - if (st->codec->block_align >= 33) // GSM, QCLP, IMA4 + switch (st->codec->codec_id) { + case AV_CODEC_ID_ADPCM_IMA_QT: + case AV_CODEC_ID_GSM: + case AV_CODEC_ID_QDM2: + case AV_CODEC_ID_QCELP: size = st->codec->block_align; - else + break; + default: size = (MAX_SIZE / st->codec->block_align) * st->codec->block_align; + } size = FFMIN(max_size, size); res = av_get_packet(s->pb, pkt, size); 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 d4bffb3..90fc433 100644 --- a/libavformat/aiffenc.c +++ b/libavformat/aiffenc.c @@ -2,44 +2,126 @@ * 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 <stdint.h> #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, s->metadata_header_padding); + + 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) @@ -54,7 +136,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; @@ -65,6 +146,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 */ @@ -93,6 +185,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 */ @@ -100,7 +198,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); @@ -110,16 +209,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; @@ -130,10 +267,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); @@ -145,12 +278,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"), @@ -158,9 +325,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 bef155f..8f70c4b 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,55 +62,76 @@ 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 (CINE, cine); + 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 (DSF, dsf); 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); REGISTER_MUXDEMUX(FILMSTRIP, filmstrip); REGISTER_MUXDEMUX(FLAC, flac); REGISTER_DEMUXER (FLIC, flic); REGISTER_MUXDEMUX(FLV, flv); + REGISTER_DEMUXER (LIVE_FLV, live_flv); 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,28 +141,40 @@ void av_register_all(void) REGISTER_MUXDEMUX(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); REGISTER_MUXDEMUX(IMAGE2PIPE, image2pipe); + REGISTER_DEMUXER (IMAGE2_ALIAS_PIX, image2_alias_pix); + REGISTER_DEMUXER (IMAGE2_BRENDER_PIX, image2_brender_pix); 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_MUXDEMUX(LRC, lrc); + 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 (MLV, mlv); REGISTER_DEMUXER (MM, mm); REGISTER_MUXDEMUX(MMF, mmf); REGISTER_MUXDEMUX(MOV, mov); @@ -162,6 +195,8 @@ 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); @@ -170,6 +205,7 @@ void av_register_all(void) 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); @@ -199,29 +235,39 @@ 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); + REGISTER_DEMUXER (SDR2, sdr2); #if CONFIG_RTPDEC ff_register_rtp_dynamic_payload_handlers(); ff_register_rdt_dynamic_payload_handlers(); #endif REGISTER_DEMUXER (SEGAFILM, segafilm); REGISTER_MUXER (SEGMENT, segment); + REGISTER_MUXER (SEGMENT, stream_segment); REGISTER_DEMUXER (SHORTEN, shorten); REGISTER_DEMUXER (SIFF, siff); + REGISTER_DEMUXER (SLN, sln); REGISTER_DEMUXER (SMACKER, smacker); REGISTER_MUXDEMUX(SMJPEG, smjpeg); REGISTER_MUXER (SMOOTHSTREAMING, smoothstreaming); @@ -232,42 +278,72 @@ void av_register_all(void) 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_MUXER (UNCODEDFRAMECRC, uncodedframecrc); + 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(WEBM_DASH_MANIFEST, webm_dash_manifest); + 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); + /* image demuxers */ + REGISTER_DEMUXER (IMAGE_BMP_PIPE, image_bmp_pipe); + REGISTER_DEMUXER (IMAGE_DPX_PIPE, image_dpx_pipe); + REGISTER_DEMUXER (IMAGE_EXR_PIPE, image_exr_pipe); + REGISTER_DEMUXER (IMAGE_J2K_PIPE, image_j2k_pipe); + REGISTER_DEMUXER (IMAGE_JPEGLS_PIPE, image_jpegls_pipe); + REGISTER_DEMUXER (IMAGE_PICTOR_PIPE, image_pictor_pipe); + REGISTER_DEMUXER (IMAGE_PNG_PIPE, image_png_pipe); + REGISTER_DEMUXER (IMAGE_SGI_PIPE, image_sgi_pipe); + REGISTER_DEMUXER (IMAGE_SUNRAST_PIPE, image_sunrast_pipe); + REGISTER_DEMUXER (IMAGE_TIFF_PIPE, image_tiff_pipe); + REGISTER_DEMUXER (IMAGE_WEBP_PIPE, image_webp_pipe); + + /* 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); @@ -287,15 +363,22 @@ void av_register_all(void) REGISTER_PROTOCOL(RTP, rtp); REGISTER_PROTOCOL(SCTP, sctp); REGISTER_PROTOCOL(SRTP, srtp); + REGISTER_PROTOCOL(SUBFILE, subfile); REGISTER_PROTOCOL(TCP, tcp); REGISTER_PROTOCOL(TLS, tls); REGISTER_PROTOCOL(UDP, udp); 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); + REGISTER_PROTOCOL(LIBSMBCLIENT, libsmbclient); } diff --git a/libavformat/amr.c b/libavformat/amr.c index afd062e..8f44de1 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 */ @@ -30,6 +30,11 @@ Only mono files are supported. #include "avformat.h" #include "internal.h" +typedef struct { + uint64_t cumulated_size; + uint64_t block_count; +} AMRContext; + static const char AMR_header[] = "#!AMR\n"; static const char AMRWB_header[] = "#!AMR-WB\n"; @@ -110,12 +115,13 @@ static int amr_read_packet(AVFormatContext *s, AVPacket *pkt) AVCodecContext *enc = s->streams[0]->codec; int read, size = 0, toc, mode; int64_t pos = avio_tell(s->pb); + AMRContext *amr = s->priv_data; - if (s->pb->eof_reached) { + if (avio_feof(s->pb)) { return AVERROR(EIO); } - // FIXME this is wrong, this should rather be in a AVParset + // FIXME this is wrong, this should rather be in a AVParser toc = avio_r8(s->pb); mode = (toc >> 3) & 0x0F; @@ -131,15 +137,16 @@ static int amr_read_packet(AVFormatContext *s, AVPacket *pkt) }; size = packed_size[mode]; - } else { - assert(0); } if (!size || av_new_packet(pkt, size)) return AVERROR(EIO); - /* Both AMR formats have 50 frames per second */ - s->streams[0]->codec->bit_rate = size*8*50; + if (amr->cumulated_size < UINT64_MAX - size) { + amr->cumulated_size += size; + /* Both AMR formats have 50 frames per second */ + s->streams[0]->codec->bit_rate = amr->cumulated_size / ++amr->block_count * 8 * 50; + } pkt->stream_index = 0; pkt->pos = pos; @@ -159,6 +166,7 @@ static int amr_read_packet(AVFormatContext *s, AVPacket *pkt) AVInputFormat ff_amr_demuxer = { .name = "amr", .long_name = NULL_IF_CONFIG_SMALL("3GPP AMR"), + .priv_data_size = sizeof(AMRContext), .read_probe = amr_probe, .read_header = amr_read_header, .read_packet = amr_read_packet, diff --git a/libavformat/anm.c b/libavformat/anm.c index f781492..5196cb8 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 (avio_feof(s->pb)) return AVERROR(EIO); if (anm->page < 0) diff --git a/libavformat/apc.c b/libavformat/apc.c index 0b6c583..21bb514 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,14 +52,9 @@ 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) - return AVERROR(ENOMEM); - /* initial predictor values for adpcm decoder */ - avio_read(pb, st->codec->extradata, 2 * 4); + if (ff_get_extradata(st->codec, pb, 2 * 4) < 0) + return AVERROR(ENOMEM); if (avio_rl32(pb)) { st->codec->channels = 2; @@ -82,6 +78,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 d967a5d..fafc1df 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 { @@ -88,10 +86,14 @@ typedef struct { static int ape_probe(AVProbeData * p) { - if (p->buf[0] == 'M' && p->buf[1] == 'A' && p->buf[2] == 'C' && p->buf[3] == ' ') - return AVPROBE_SCORE_MAX; + int version = AV_RL16(p->buf+4); + if (AV_RL32(p->buf) != MKTAG('M', 'A', 'C', ' ')) + return 0; - return 0; + if (version < APE_MIN_VERSION || version > APE_MAX_VERSION) + return AVPROBE_SCORE_MAX/4; + + return AVPROBE_SCORE_MAX; } static void ape_dumpinfo(AVFormatContext * s, APEContext * ape_ctx) @@ -172,14 +174,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,11 +260,11 @@ 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, - "Number of seek entries is less than number of frames: %zu vs. %"PRIu32"\n", + "Number of seek entries is less than number of frames: %"SIZE_SPECIFIER" vs. %"PRIu32"\n", ape->seektablelength / sizeof(*ape->seektable), ape->totalframes); return AVERROR_INVALIDDATA; } @@ -280,18 +282,20 @@ static int ape_read_header(AVFormatContext * s) ape->totalsamples += ape->blocksperframe * (ape->totalframes - 1); if (ape->seektablelength > 0) { - ape->seektable = av_malloc(ape->seektablelength); + ape->seektable = av_mallocz(ape->seektablelength); if (!ape->seektable) return AVERROR(ENOMEM); for (i = 0; i < ape->seektablelength / sizeof(uint32_t) && !pb->eof_reached; i++) ape->seektable[i] = avio_rl32(pb); if (ape->fileversion < 3810) { - ape->bittable = av_malloc(ape->totalframes); + ape->bittable = av_mallocz(ape->totalframes); if (!ape->bittable) return AVERROR(ENOMEM); for (i = 0; i < ape->totalframes && !pb->eof_reached; i++) ape->bittable[i] = avio_r8(pb); } + if (pb->eof_reached) + av_log(s, AV_LOG_WARNING, "File truncated\n"); } ape->frames[0].pos = ape->firstframe; @@ -340,7 +344,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 +357,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 +370,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 +389,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 (avio_feof(s->pb)) return AVERROR_EOF; if (ape->currentframe >= ape->totalframes) return AVERROR_EOF; @@ -413,6 +417,10 @@ 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) { + av_free_packet(pkt); + return ret; + } pkt->pts = ape->frames[ape->currentframe].pts; pkt->stream_index = 0; diff --git a/libavformat/apetag.c b/libavformat/apetag.c index 22884ef..7d2f0b3 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 */ @@ -29,8 +29,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) @@ -92,14 +90,8 @@ 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_get_extradata(st->codec, s->pb, size) < 0) return AVERROR(ENOMEM); - if (avio_read(pb, st->codec->extradata, size) != size) { - av_freep(&st->codec->extradata); - return AVERROR(EIO); - } - st->codec->extradata_size = size; st->codec->codec_type = AVMEDIA_TYPE_ATTACHMENT; } } else { @@ -132,7 +124,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; } @@ -174,43 +166,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; - 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 + if (!string_is_ascii(e->key)) { + av_log(s, AV_LOG_WARNING, "Non ASCII keys are not allowed\n"); + continue; + } + + 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 @@ -218,12 +228,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 36e3211..cf2a5f8 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 0000000..9508766 --- /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 (!avio_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 ec34b50..80d24db 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 2f6722a..0c9598a 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,22 @@ #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 pkt_clean; int ds_span; /* descrambling */ int ds_packet_size; @@ -47,6 +55,9 @@ typedef struct ASFStream { int palette_changed; uint32_t palette[256]; + + int payload_ext_ct; + ASFPayload payload[8]; } ASFStream; typedef struct ASFMainHeader { diff --git a/libavformat/asfcrypt.c b/libavformat/asfcrypt.c index c261475..a402758 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 53388b4..8b80d63 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 85e800d..7f7bb4d 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 */ @@ -69,8 +69,8 @@ typedef struct { unsigned int packet_frag_offset; unsigned int packet_frag_size; int64_t packet_frag_timestamp; + int ts_is_pts; int packet_multi_size; - int packet_obj_size; int packet_time_delta; int packet_time_start; int64_t packet_pos; @@ -98,12 +98,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) */ @@ -129,7 +125,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); @@ -273,7 +269,7 @@ static void get_id3_tag(AVFormatContext *s, int len) { ID3v2ExtraMeta *id3v2_extra_meta = NULL; - ff_id3v2_read(s, ID3v2_DEFAULT_MAGIC, &id3v2_extra_meta); + ff_id3v2_read(s, ID3v2_DEFAULT_MAGIC, &id3v2_extra_meta, len); if (id3v2_extra_meta) ff_id3v2_parse_apic(s, &id3v2_extra_meta); ff_id3v2_free_extra_meta(&id3v2_extra_meta); @@ -283,16 +279,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); @@ -304,7 +304,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; @@ -369,17 +369,13 @@ 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) / (float)FFMIN(fsize, asf->hdr.file_size) < 0.05) + st->duration = asf->hdr.play_time / (10000000 / 1000) - start_time; } ff_get_guid(pb, &g); @@ -407,6 +403,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); @@ -432,7 +429,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) @@ -469,9 +466,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); } @@ -504,6 +503,8 @@ static int asf_read_stream_properties(AVFormatContext *s, int64_t size) } if (st->codec->codec_id == AV_CODEC_ID_H264) st->need_parsing = AVSTREAM_PARSE_FULL_ONCE; + if (st->codec->codec_id == AV_CODEC_ID_MPEG4) + st->need_parsing = AVSTREAM_PARSE_FULL_ONCE; } pos2 = avio_tell(pb); avio_skip(pb, size - (pos2 - pos1 + 24)); @@ -540,8 +541,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); @@ -550,10 +553,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; @@ -717,12 +728,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); @@ -739,7 +754,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) @@ -770,19 +785,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 (avio_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"); } } } @@ -796,8 +822,8 @@ static int asf_read_header(AVFormatContext *s) avio_rl64(pb); avio_r8(pb); avio_r8(pb); - if (pb->eof_reached) - return -1; + if (avio_feof(pb)) + return AVERROR_EOF; asf->data_offset = avio_tell(pb); asf->packet_size_left = 0; @@ -896,20 +922,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 (!avio_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 (!avio_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(!avio_feof(pb)) { avio_seek(pb, -1, SEEK_CUR); // FIXME } @@ -925,12 +951,12 @@ static int asf_get_packet(AVFormatContext *s, AVIOContext *pb) av_log(s, AV_LOG_ERROR, "invalid packet_length %"PRIu32" 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 %"PRIu32" at:%"PRId64"\n", padsize, avio_tell(pb)); - return -1; + return AVERROR_INVALIDDATA; } asf->packet_timestamp = avio_rl32(pb); @@ -950,7 +976,7 @@ static int asf_get_packet(AVFormatContext *s, AVIOContext *pb) av_log(s, AV_LOG_ERROR, "invalid packet header length %d for pktlen %"PRIu32"-%"PRIu32" 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) @@ -968,13 +994,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); @@ -982,26 +1011,63 @@ 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; + asf->ts_is_pts = 1; + 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 @@ -1014,18 +1080,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; @@ -1033,16 +1099,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; @@ -1064,12 +1126,10 @@ static int asf_parse_packet(AVFormatContext *s, AVIOContext *pb, AVPacket *pkt) ASFStream *asf_st = 0; for (;;) { int ret; - - if (pb->eof_reached) + if (avio_feof(pb)) return AVERROR_EOF; - if (asf->packet_size_left < FRAME_HEADER_SIZE || - asf->packet_segments < 1) { + asf->packet_segments < 1 && asf->packet_time_start == 0) { int ret = asf->packet_size_left + asf->packet_padsize; assert(ret >= 0); @@ -1084,13 +1144,13 @@ static int asf_parse_packet(AVFormatContext *s, AVIOContext *pb, AVPacket *pkt) } if (asf->packet_time_start == 0) { if (asf_read_frame_header(s, pb) < 0) { - asf->packet_segments = 0; + asf->packet_time_start = asf->packet_segments = 0; continue; } 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); @@ -1100,7 +1160,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); @@ -1119,41 +1180,41 @@ 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) { + int ret; + 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); + if ((ret = av_new_packet(&asf_st->pkt, asf_st->packet_obj_size)) < 0) + return ret; asf_st->seq = asf->packet_seq; - asf_st->pkt.dts = asf->packet_frag_timestamp - asf->hdr.preroll; + if (asf->ts_is_pts) { + asf_st->pkt.pts = asf->packet_frag_timestamp - asf->hdr.preroll; + } else + asf_st->pkt.dts = asf->packet_frag_timestamp - asf->hdr.preroll; asf_st->pkt.stream_index = asf->stream_index; asf_st->pkt.pos = asf_st->packet_pos = asf->packet_pos; + asf_st->pkt_clean = 0; if (asf_st->pkt.data && asf_st->palette_changed) { uint8_t *pal; @@ -1170,7 +1231,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) @@ -1194,6 +1255,11 @@ static int asf_parse_packet(AVFormatContext *s, AVIOContext *pb, AVPacket *pkt) continue; } + if (asf->packet_frag_offset != asf_st->frag_offset && !asf_st->pkt_clean) { + memset(asf_st->pkt.data + asf_st->frag_offset, 0, asf_st->pkt.size - asf_st->frag_offset); + asf_st->pkt_clean = 1; + } + ret = avio_read(pb, asf_st->pkt.data + asf->packet_frag_offset, asf->packet_frag_size); if (ret != asf->packet_frag_size) { @@ -1311,7 +1377,6 @@ static void asf_reset_header(AVFormatContext *s) int i; asf->packet_size_left = 0; - asf->packet_segments = 0; asf->packet_flags = 0; asf->packet_property = 0; asf->packet_timestamp = 0; @@ -1325,21 +1390,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); @@ -1350,6 +1428,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; @@ -1365,11 +1444,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; } @@ -1380,8 +1461,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; @@ -1404,17 +1484,20 @@ static int 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, ret = 0; + int ret = 0; + + if((ret = avio_seek(s->pb, asf->data_object_offset + asf->data_object_size, SEEK_SET)) < 0) { + return ret; + } - avio_seek(s->pb, asf->data_object_offset + asf->data_object_size, SEEK_SET); if ((ret = ff_get_guid(s->pb, &g)) < 0) goto end; /* 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 || avio_feof(s->pb)) { goto end; } avio_skip(s->pb, gsize - 24); @@ -1425,6 +1508,7 @@ static int 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); if ((ret = ff_get_guid(s->pb, &g)) < 0) goto end; @@ -1448,11 +1532,12 @@ static int asf_build_simple_index(AVFormatContext *s, int stream_index) last_pos = pos; } } - asf->index_read = ict > 0; + asf->index_read = ict > 1; } end: - if (s->pb->eof_reached) - ret = 0; +// if (avio_feof(s->pb)) { +// ret = 0; +// } avio_seek(s->pb, current_pos, SEEK_SET); return ret; } @@ -1462,8 +1547,7 @@ 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, ret = 0; + int ret = 0; if (s->packet_size <= 0) return -1; @@ -1484,19 +1568,24 @@ static int asf_read_seek(AVFormatContext *s, int stream_index, return 0; } - if (!asf->index_read) + if (!asf->index_read) { ret = asf_build_simple_index(s, stream_index); + if (ret < 0) + asf->index_read = -1; + } - if (!ret && 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; } } @@ -1504,6 +1593,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 79b44a7..cccbf85 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,8 @@ #define ASF_INDEXED_INTERVAL 10000000 -#define ASF_INDEX_BLOCK 600 +#define ASF_INDEX_BLOCK (1<<9) +#define ASF_PAYLOADS_PER_PACKET 63 #define ASF_PACKET_ERROR_CORRECTION_DATA_SIZE 0x2 #define ASF_PACKET_ERROR_CORRECTION_FLAGS \ @@ -195,36 +197,33 @@ 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) -{ - assert(sizeof(*g) == 16); - avio_write(s, *g, sizeof(*g)); -} - static void put_str16(AVIOContext *s, const char *tag) { int len; @@ -245,7 +244,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 +292,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 +310,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 @@ -354,7 +353,7 @@ static int asf_write_header1(AVFormatContext *s, int64_t file_size, AVIOContext *pb = s->pb; AVDictionaryEntry *tags[5]; int header_size, n, extra_size, extra_size2, wav_extra_size, file_time; - int has_title; + int has_title, has_aspect_ratio = 0; int metadata_count; AVCodecContext *enc; int64_t header_offset, cur_pos, hpos; @@ -380,13 +379,17 @@ static int asf_write_header1(AVFormatContext *s, int64_t file_size, avpriv_set_pts_info(s->streams[n], 32, 1, 1000); /* 32 bit pts in ms */ bit_rate += enc->bit_rate; + if ( enc->codec_type == AVMEDIA_TYPE_VIDEO + && enc->sample_aspect_ratio.num > 0 + && enc->sample_aspect_ratio.den > 0) + has_aspect_ratio++; } if (asf->is_streamed) { 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 +398,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)); @@ -406,14 +409,46 @@ static int asf_write_header1(AVFormatContext *s, int64_t file_size, avio_wl32(pb, (asf->is_streamed || !pb->seekable) ? 3 : 2); /* ??? */ avio_wl32(pb, s->packet_size); /* packet size */ avio_wl32(pb, s->packet_size); /* packet size */ - avio_wl32(pb, bit_rate); /* Nominal data rate in bps */ + avio_wl32(pb, bit_rate ? bit_rate : -1); /* Maximum data rate in bps */ end_header(pb, hpos); /* unknown headers */ hpos = put_header(pb, &ff_asf_head1_guid); - put_guid(pb, &ff_asf_head2_guid); - avio_wl32(pb, 6); - avio_wl16(pb, 0); + ff_put_guid(pb, &ff_asf_head2_guid); + avio_wl16(pb, 6); + if (has_aspect_ratio) { + int64_t hpos2; + avio_wl32(pb, 26 + has_aspect_ratio * 84); + hpos2 = put_header(pb, &ff_asf_metadata_header); + avio_wl16(pb, 2 * has_aspect_ratio); + for (n = 0; n < s->nb_streams; n++) { + enc = s->streams[n]->codec; + if ( enc->codec_type == AVMEDIA_TYPE_VIDEO + && enc->sample_aspect_ratio.num > 0 + && enc->sample_aspect_ratio.den > 0) { + AVRational sar = enc->sample_aspect_ratio; + avio_wl16(pb, 0); + // the stream number is set like this below + avio_wl16(pb, n + 1); + avio_wl16(pb, 26); // name_len + avio_wl16(pb, 3); // value_type + avio_wl32(pb, 4); // value_len + avio_put_str16le(pb, "AspectRatioX"); + avio_wl32(pb, sar.num); + avio_wl16(pb, 0); + // the stream number is set like this below + avio_wl16(pb, n + 1); + avio_wl16(pb, 26); // name_len + avio_wl16(pb, 3); // value_type + avio_wl32(pb, 4); // value_len + avio_put_str16le(pb, "AspectRatioY"); + avio_wl32(pb, sar.den); + } + } + end_header(pb, hpos2); + } else { + avio_wl32(pb, 0); + } end_header(pb, hpos); /* title and other infos */ @@ -460,7 +495,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 +513,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); @@ -493,7 +528,7 @@ static int asf_write_header1(AVFormatContext *s, int64_t file_size, if (enc->codec_type == AVMEDIA_TYPE_AUDIO) { /* WAVEFORMATEX header */ - int wavsize = ff_put_wav_header(pb, enc); + int wavsize = ff_put_wav_header(pb, enc, FF_PUT_WAV_HEADER_FORCE_WAVEFORMATEX); if (wavsize < 0) return -1; @@ -521,7 +556,7 @@ static int asf_write_header1(AVFormatContext *s, int64_t file_size, avio_wl16(pb, 40 + enc->extradata_size); /* size */ /* BITMAPINFOHEADER header */ - ff_put_bmp_header(pb, enc, ff_codec_bmp_tags, 1); + ff_put_bmp_header(pb, enc, ff_codec_bmp_tags, 1, 0); } end_header(pb, hpos); } @@ -529,7 +564,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++) { const AVCodecDescriptor *codec_desc; @@ -603,9 +638,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); /* ??? */ @@ -617,12 +652,11 @@ static int asf_write_header(AVFormatContext *s) ASFContext *asf = s->priv_data; s->packet_size = PACKET_SIZE; + s->max_interleave_delta = 0; 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 @@ -641,6 +675,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; } @@ -667,7 +704,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++) @@ -706,20 +743,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); @@ -734,7 +770,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; @@ -757,7 +793,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 @@ -765,7 +801,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,20 +859,65 @@ static void put_frame(AVFormatContext *s, ASFStream *stream, AVStream *avst, flush_packet(s); else if (asf->packet_size_left <= (PAYLOAD_HEADER_SIZE_MULTIPLE_PAYLOADS + PACKET_HEADER_MIN_SIZE + 1)) flush_packet(s); + else if (asf->packet_nb_payloads == ASF_PAYLOADS_PER_PACKET) + flush_packet(s); } 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; @@ -846,41 +927,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; } @@ -890,9 +956,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); @@ -908,6 +974,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) @@ -915,8 +982,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) { @@ -928,7 +998,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; } @@ -939,7 +1009,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, @@ -958,7 +1028,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 7bd3d17..bb953c7 100644 --- a/libavformat/assdec.c +++ b/libavformat/assdec.c @@ -2,40 +2,36 @@ * 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 <stdint.h> -#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]"; @@ -46,176 +42,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 + if (header_remaining) { + av_bprintf(&header, "%s", line.str); 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++; + } 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 751485d..9fb9f23 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 0000000..9de74aa --- /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 0000000..4a399ea --- /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 0000000..92c208d --- /dev/null +++ b/libavformat/astdec.c @@ -0,0 +1,122 @@ +/* + * 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')) + return 0; + + if (!AV_RB16(p->buf + 10) || + !AV_RB16(p->buf + 12) || AV_RB16(p->buf + 12) > 256 || + !AV_RB32(p->buf + 16) || AV_RB32(p->buf + 16) > 8*48000) + return AVPROBE_SCORE_MAX / 8; + + return AVPROBE_SCORE_MAX / 3 * 2; +} + +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 (avio_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 0000000..cf7a12c --- /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 (s->streams[0]->nb_frames == 0) + 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 * s->streams[0]->nb_frames)) / 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 e682cbf..53702bc 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 e49c77f..80bf768 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 */ @@ -34,7 +34,7 @@ void ff_audio_interleave_close(AVFormatContext *s) AudioInterleaveContext *aic = st->priv_data; if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) - av_fifo_free(aic->fifo); + av_fifo_freep(&aic->fifo); } } @@ -45,8 +45,12 @@ int ff_audio_interleave_init(AVFormatContext *s, int i; if (!samples_per_frame) - return -1; + return AVERROR(EINVAL); + if (!time_base.num) { + av_log(s, AV_LOG_ERROR, "timebase not set for audio interleave\n"); + return AVERROR(EINVAL); + } for (i = 0; i < s->nb_streams; i++) { AVStream *st = s->streams[i]; AudioInterleaveContext *aic = st->priv_data; @@ -56,14 +60,15 @@ int ff_audio_interleave_init(AVFormatContext *s, av_get_bits_per_sample(st->codec->codec_id)) / 8; if (!aic->sample_size) { av_log(s, AV_LOG_ERROR, "could not compute sample size\n"); - return -1; + return AVERROR(EINVAL); } aic->samples_per_frame = samples_per_frame; aic->samples = aic->samples_per_frame; aic->time_base = time_base; aic->fifo_size = 100* *aic->samples; - aic->fifo= av_fifo_alloc(100 * *aic->samples); + if (!(aic->fifo= av_fifo_alloc_array(100, *aic->samples))) + return AVERROR(ENOMEM); } } @@ -80,7 +85,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; @@ -108,7 +114,7 @@ int ff_audio_rechunk_interleave(AVFormatContext *s, AVPacket *out, AVPacket *pkt unsigned new_size = av_fifo_size(aic->fifo) + pkt->size; if (new_size > aic->fifo_size) { if (av_fifo_realloc2(aic->fifo, new_size) < 0) - return -1; + return AVERROR(ENOMEM); aic->fifo_size = new_size; } av_fifo_generic_write(aic->fifo, pkt->data, pkt->size, NULL); @@ -126,9 +132,12 @@ 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)) + while ((ret = interleave_new_audio_packet(s, &new_pkt, i, flush)) > 0) { if ((ret = ff_interleave_add_packet(s, &new_pkt, compare_ts)) < 0) return ret; + } + if (ret < 0) + return ret; } } diff --git a/libavformat/audiointerleave.h b/libavformat/audiointerleave.h index 9c7b548..4d77832 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 2fd5ac8..f5c513b 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 579756e..972e19b 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 923b282..b915148 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). @@ -261,6 +261,8 @@ struct AVFormatContext; +struct AVDeviceInfoList; +struct AVDeviceCapabilitiesQuery; /** * @defgroup metadata_api Public Metadata API @@ -272,7 +274,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. * @@ -399,6 +401,9 @@ typedef struct AVProbeData { uint8_t *mime_type; /**< mime_type, when known. */ } AVProbeData; +#define AVPROBE_SCORE_RETRY (AVPROBE_SCORE_MAX/4) +#define AVPROBE_SCORE_STREAM_RETRY (AVPROBE_SCORE_MAX/4-1) + #define AVPROBE_SCORE_EXTENSION 50 ///< score for file extension #define AVPROBE_SCORE_MIME 75 ///< score for file mime type #define AVPROBE_SCORE_MAX 100 ///< maximum score @@ -422,14 +427,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 @@ -499,8 +514,43 @@ 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); + /** + * Allows sending messages from application to device. + */ + int (*control_message)(struct AVFormatContext *s, int type, + void *data, size_t data_size); + + /** + * Write an uncoded AVFrame. + * + * See av_write_uncoded_frame() for details. + * + * The library will free *frame afterwards, but the muxer can prevent it + * by setting the pointer to NULL. + */ + int (*write_uncoded_frame)(struct AVFormatContext *, int stream_index, + AVFrame **frame, unsigned flags); + /** + * Returns device list with it properties. + * @see avdevice_list_devices() for more details. + */ + int (*get_device_list)(struct AVFormatContext *s, struct AVDeviceInfoList *device_list); + /** + * Initialize device capabilities submodule. + * @see avdevice_capabilities_create() for more details. + */ + int (*create_device_capabilities)(struct AVFormatContext *s, struct AVDeviceCapabilitiesQuery *caps); + /** + * Free device capabilities submodule. + * @see avdevice_capabilities_free() for more details. + */ + int (*free_device_capabilities)(struct AVFormatContext *s, struct AVDeviceCapabilitiesQuery *caps); } AVOutputFormat; /** * @} @@ -527,7 +577,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; @@ -636,6 +686,24 @@ typedef struct AVInputFormat { * Active streams are all streams that have AVStream.discard < AVDISCARD_ALL. */ int (*read_seek2)(struct AVFormatContext *s, int stream_index, int64_t min_ts, int64_t ts, int64_t max_ts, int flags); + + /** + * Returns device list with it properties. + * @see avdevice_list_devices() for more details. + */ + int (*get_device_list)(struct AVFormatContext *s, struct AVDeviceInfoList *device_list); + + /** + * Initialize device capabilities submodule. + * @see avdevice_capabilities_create() for more details. + */ + int (*create_device_capabilities)(struct AVFormatContext *s, struct AVDeviceCapabilitiesQuery *caps); + + /** + * Free device capabilities submodule. + * @see avdevice_capabilities_free() for more details. + */ + int (*free_device_capabilities)(struct AVFormatContext *s, struct AVDeviceCapabilitiesQuery *caps); } AVInputFormat; /** * @} @@ -647,11 +715,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). @@ -683,6 +759,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 @@ -734,10 +824,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; @@ -796,6 +888,8 @@ typedef struct AVStream { * - muxing: May be set by the caller before avformat_write_header(). * * Freed by libavformat in avformat_free_context(). + * + * @see av_format_inject_global_side_data() */ AVPacketSideData *side_data; /** @@ -822,11 +916,25 @@ 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; + int64_t rfps_duration_sum; + double (*duration_error)[2][MAX_STD_TIMEBASES]; + int64_t codec_info_duration; + int64_t codec_info_duration_fields; + + /** + * 0 -> decoder has not been searched for yet. + * >0 -> decoder found + * <0 -> decoder with codec_id == -found_decoder has not been found + */ int found_decoder; + int64_t last_duration; + /** * Those are used for average framerate estimation. */ @@ -840,6 +948,13 @@ typedef struct AVStream { int pts_wrap_bits; /**< number of bits in pts (used for wrapping control) */ // 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; @@ -872,8 +987,114 @@ 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; + + /** + * Internal data to prevent doing update_initial_durations() twice + */ + int update_initial_durations_done; + + /** + * Internal data to generate dts from pts + */ + int64_t pts_reorder_error[MAX_REORDER_DELAY+1]; + uint8_t pts_reorder_error_count[MAX_REORDER_DELAY+1]; + + /** + * Internal data to analyze DTS and detect faulty mpeg streams + */ + int64_t last_dts_for_order_check; + uint8_t dts_ordered; + uint8_t dts_misordered; + + /** + * Internal data to inject global side data + */ + int inject_global_side_data; + } AVStream; +AVRational av_stream_get_r_frame_rate(const AVStream *s); +void av_stream_set_r_frame_rate(AVStream *s, AVRational r); +struct AVCodecParserContext *av_stream_get_parser(const AVStream *s); + +/** + * Returns the pts of the last muxed packet + its duration + * + * the retuned value is undefined when used with a demuxer. + */ +int64_t av_stream_get_end_pts(const AVStream *st); + #define AV_PROGRAM_RUNNING 1 /** @@ -889,6 +1110,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 @@ -901,6 +1139,24 @@ typedef struct AVChapter { AVDictionary *metadata; } AVChapter; + +/** + * Callback used by devices to communicate with application. + */ +typedef int (*av_format_control_message)(struct AVFormatContext *s, int type, + void *data, size_t data_size); + + +/** + * 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) +}; + typedef struct AVFormatInternal AVFormatInternal; /** @@ -1011,7 +1267,7 @@ typedef struct AVFormatContext { /** * 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; @@ -1040,19 +1296,20 @@ typedef struct AVFormatContext { * This flag is mainly intended for testing. */ #define AVFMT_FLAG_BITEXACT 0x0400 +#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. /** - * Maximum size of the data read from input for determining - * the input container format. - * Demuxing only, set by the caller before avformat_open_input(). + * @deprecated deprecated in favor of probesize2 */ unsigned int probesize; /** - * Maximum duration (in AV_TIME_BASE units) of the data read - * from input in avformat_find_stream_info(). - * Demuxing only, set by the caller before avformat_find_stream_info(). + * @deprecated deprecated in favor of max_analyze_duration2 */ + attribute_deprecated int max_analyze_duration; const uint8_t *key; @@ -1125,7 +1382,12 @@ typedef struct AVFormatContext { * Start time of the stream in real world time, in microseconds * since the Unix epoch (00:00 1st January 1970). That is, pts=0 in the * stream was captured at this real world time. - * Muxing only, set by the caller before avformat_write_header(). + * - muxing: Set by the caller before avformat_write_header(). If set to + * either 0 or AV_NOPTS_VALUE, then the current wall-time will + * be used. + * - demuxing: Set by libavformat. AV_NOPTS_VALUE if unknown. Note that + * the value may become known after some number of frames + * have been received. */ int64_t start_time_realtime; @@ -1198,6 +1460,115 @@ typedef struct AVFormatContext { */ int max_ts_probe; + + /** + * 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) + */ + int64_t 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; + + /** + * number of bytes to read maximally to identify format. + * - encoding: unused + * - decoding: set by user through AVOPtions (NO direct access) + */ + int format_probesize; + /***************************************************************** * 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 @@ -1239,6 +1610,7 @@ typedef struct AVFormatContext { /** * Offset to remap timestamps to be non-negative. * Expressed in timebase units. + * @see AVStream.mux_ts_offset */ int64_t offset; @@ -1252,8 +1624,109 @@ typedef struct AVFormatContext { * Must not be accessed in any way by callers. */ AVFormatInternal *internal; + + /** + * 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; + + /** + * Number of bytes to be written as padding in a metadata header. + * Demuxing: Unused. + * Muxing: Set by user via av_format_set_metadata_header_padding. + */ + int metadata_header_padding; + + /** + * User data. + * This is a place for some private data of the user. + * Mostly usable with control_message_cb or any future callbacks in device's context. + */ + void *opaque; + + /** + * Callback used by devices to communicate with application. + */ + av_format_control_message control_message_cb; + + /** + * Output timestamp offset, in microseconds. + * Muxing: set by user via AVOptions (NO direct access) + */ + int64_t output_ts_offset; + + /** + * Maximum duration (in AV_TIME_BASE units) of the data read + * from input in avformat_find_stream_info(). + * Demuxing only, set by the caller before avformat_find_stream_info() + * via AVOptions (NO direct access). + * Can be set to 0 to let avformat choose using a heuristic. + */ + int64_t max_analyze_duration2; + + /** + * Maximum size of the data read from input for determining + * the input container format. + * Demuxing only, set by the caller before avformat_open_input() + * via AVOptions (NO direct access). + */ + int64_t probesize2; } 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); +int av_format_get_metadata_header_padding(const AVFormatContext *s); +void av_format_set_metadata_header_padding(AVFormatContext *s, int c); +void * av_format_get_opaque(const AVFormatContext *s); +void av_format_set_opaque(AVFormatContext *s, void *opaque); +av_format_control_message av_format_get_control_message_cb(const AVFormatContext *s); +void av_format_set_control_message_cb(AVFormatContext *s, av_format_control_message callback); + +/** + * This function will cause global side data to be injected in the next packet + * of each stream as well as after any subsequent seek. + */ +void av_format_inject_global_side_data(AVFormatContext *s); + +/** + * 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; @@ -1291,7 +1764,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); @@ -1357,6 +1829,9 @@ 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 s media file handle * @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 @@ -1384,6 +1859,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 * @{ @@ -1418,6 +1922,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 @@ -1429,9 +1942,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); @@ -1457,6 +1978,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 @@ -1481,6 +2025,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. @@ -1511,6 +2067,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 @@ -1567,6 +2141,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 s media file handle * @param stream_index index of the stream which is used as time base reference @@ -1595,6 +2170,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. @@ -1604,6 +2190,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 @@ -1698,6 +2308,44 @@ int av_write_frame(AVFormatContext *s, AVPacket *pkt); int av_interleaved_write_frame(AVFormatContext *s, AVPacket *pkt); /** + * Write a uncoded frame to an output media file. + * + * The frame must be correctly interleaved according to the container + * specification; if not, then av_interleaved_write_frame() must be used. + * + * See av_interleaved_write_frame() for details. + */ +int av_write_uncoded_frame(AVFormatContext *s, int stream_index, + AVFrame *frame); + +/** + * Write a uncoded frame to an output media file. + * + * If the muxer supports it, this function allows to write an AVFrame + * structure directly, without encoding it into a packet. + * It is mostly useful for devices and similar special muxers that use raw + * video or PCM data and will not serialize it into a byte stream. + * + * To test whether it is possible to use it with a given muxer and stream, + * use av_write_uncoded_frame_query(). + * + * The caller gives up ownership of the frame and must not access it + * afterwards. + * + * @return >=0 for success, a negative code on error + */ +int av_interleaved_write_uncoded_frame(AVFormatContext *s, int stream_index, + AVFrame *frame); + +/** + * Test whether a muxer supports uncoded frame. + * + * @return >=0 if an uncoded frame can be written to that muxer and stream, + * <0 if not + */ +int av_write_uncoded_frame_query(AVFormatContext *s, int stream_index); + +/** * Write the stream trailer to an output media file and free the * file private data. * @@ -1732,6 +2380,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); + + +/** * @} */ @@ -1778,7 +2445,7 @@ void av_hex_dump_log(void *avcl, int level, const uint8_t *buf, int size); * @param dump_payload True if the payload must be displayed, too. * @param st AVStream that the packet belongs to */ -void av_pkt_dump2(FILE *f, AVPacket *pkt, int dump_payload, AVStream *st); +void av_pkt_dump2(FILE *f, const AVPacket *pkt, int dump_payload, const AVStream *st); /** @@ -1792,8 +2459,8 @@ void av_pkt_dump2(FILE *f, AVPacket *pkt, int dump_payload, AVStream *st); * @param dump_payload True if the payload must be displayed, too. * @param st AVStream that the packet belongs to */ -void av_pkt_dump_log2(void *avcl, int level, AVPacket *pkt, int dump_payload, - AVStream *st); +void av_pkt_dump_log2(void *avcl, int level, const AVPacket *pkt, int dump_payload, + const AVStream *st); /** * Get the AVCodecID for the given codec tag tag. @@ -1815,6 +2482,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); /** @@ -1873,9 +2552,9 @@ void av_url_split(char *proto, int proto_size, * codec and time base. * * @param ic the context to analyze - * @param index the index to print, if you have multiple inputs or outputs + * @param index index of the stream to dump information about * @param url the URL to print, such as source or destination file - * @param is_output whether the context is input or ouput + * @param is_output Select whether the specified context is an input(0) or output(1) */ void av_dump_format(AVFormatContext *ic, int index, @@ -1967,10 +2646,67 @@ const struct AVCodecTag *avformat_get_riff_video_tags(void); */ const struct AVCodecTag *avformat_get_riff_audio_tags(void); /** + * @return the table mapping MOV FourCCs for video to libavcodec AVCodecID. + */ +const struct AVCodecTag *avformat_get_mov_video_tags(void); +/** + * @return the table mapping MOV FourCCs for audio to AVCodecID. + */ +const struct AVCodecTag *avformat_get_mov_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/avformatres.rc b/libavformat/avformatres.rc new file mode 100644 index 0000000..ffe61e0 --- /dev/null +++ b/libavformat/avformatres.rc @@ -0,0 +1,55 @@ +/* + * Windows resource file for libavformat + * + * Copyright (C) 2012 James Almer + * Copyright (C) 2013 Tiancheng "Timothy" Gu + * + * 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 <windows.h> +#include "libavformat/version.h" +#include "libavutil/ffversion.h" +#include "config.h" + +1 VERSIONINFO +FILEVERSION LIBAVFORMAT_VERSION_MAJOR, LIBAVFORMAT_VERSION_MINOR, LIBAVFORMAT_VERSION_MICRO, 0 +PRODUCTVERSION LIBAVFORMAT_VERSION_MAJOR, LIBAVFORMAT_VERSION_MINOR, LIBAVFORMAT_VERSION_MICRO, 0 +FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +FILEOS VOS_NT_WINDOWS32 +FILETYPE VFT_DLL +{ + BLOCK "StringFileInfo" + { + BLOCK "040904B0" + { + VALUE "CompanyName", "FFmpeg Project" + VALUE "FileDescription", "FFmpeg container format library" + VALUE "FileVersion", AV_STRINGIFY(LIBAVFORMAT_VERSION) + VALUE "InternalName", "libavformat" + VALUE "LegalCopyright", "Copyright (C) 2000-" AV_STRINGIFY(CONFIG_THIS_YEAR) " FFmpeg Project" + VALUE "OriginalFilename", "avformat" BUILDSUF "-" AV_STRINGIFY(LIBAVFORMAT_VERSION_MAJOR) SLIBSUF + VALUE "ProductName", "FFmpeg" + VALUE "ProductVersion", FFMPEG_VERSION + } + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0409, 0x04B0 + } +} diff --git a/libavformat/avi.h b/libavformat/avi.h index e05db9c..34da76f 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 c24a6c4..5b260e2 100644 --- a/libavformat/avidec.c +++ b/libavformat/avidec.c @@ -2,27 +2,29 @@ * 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 <inttypes.h> +#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" @@ -32,9 +34,8 @@ #include "dv.h" #include "internal.h" #include "riff.h" - -#undef NDEBUG -#include <assert.h> +#include "libavcodec/bytestream.h" +#include "libavcodec/exif.h" typedef struct AVIStream { int64_t frame_offset; /* current frame (video) or byte (audio) counter @@ -58,12 +59,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; @@ -72,9 +77,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' }, @@ -93,8 +115,8 @@ static int avi_load_index(AVFormatContext *s); static int guess_ni_flag(AVFormatContext *s); #define print_tag(str, tag, size) \ - av_dlog(NULL, "%s: tag=%c%c%c%c size=0x%x\n", \ - str, tag & 0xff, \ + av_dlog(NULL, "pos:%"PRIX64" %s: tag=%c%c%c%c size=0x%x\n", \ + avio_tell(pb), str, tag & 0xff, \ (tag >> 8) & 0xff, \ (tag >> 16) & 0xff, \ (tag >> 24) & 0xff, \ @@ -151,7 +173,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 " @@ -194,14 +216,15 @@ 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 (avio_feof(pb)) return AVERROR_INVALIDDATA; if (last_pos == pos || pos == base - 8) avi->non_interleaved = 1; - if (last_pos != pos && (len || !ast->sample_size)) + if (last_pos != pos && len) av_add_index_entry(st, pos, ast->cum_len, len, 0, key ? AVINDEX_KEYFRAME : 0); @@ -214,7 +237,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 (avio_feof(pb)) return AVERROR_INVALIDDATA; pos = avio_tell(pb); @@ -224,16 +247,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; } @@ -324,6 +352,7 @@ static void avi_read_nikon(AVFormatContext *s, uint64_t end) uint16_t size = avio_rl16(s->pb); const char *name = NULL; char buffer[64] = { 0 }; + size = FFMIN(size, tag_end - avio_tell(s->pb)); size -= avio_read(s->pb, buffer, FFMIN(size, sizeof(buffer) - 1)); switch (tag) { @@ -352,6 +381,84 @@ static void avi_read_nikon(AVFormatContext *s, uint64_t end) } } +static int avi_extract_stream_metadata(AVStream *st) +{ + GetByteContext gb; + uint8_t *data = st->codec->extradata; + int data_size = st->codec->extradata_size; + int tag, offset; + + if (!data || data_size < 8) { + return AVERROR_INVALIDDATA; + } + + bytestream2_init(&gb, data, data_size); + + tag = bytestream2_get_le32(&gb); + + switch (tag) { + case MKTAG('A', 'V', 'I', 'F'): + // skip 4 byte padding + bytestream2_skip(&gb, 4); + offset = bytestream2_tell(&gb); + bytestream2_init(&gb, data + offset, data_size - offset); + + // decode EXIF tags from IFD, AVI is always little-endian + return avpriv_exif_decode_ifd(st->codec, &gb, 1, 0, &st->metadata); + break; + case MKTAG('C', 'A', 'S', 'I'): + avpriv_request_sample(st->codec, "RIFF stream data tag type CASI (%u)", tag); + break; + case MKTAG('Z', 'o', 'r', 'a'): + avpriv_request_sample(st->codec, "RIFF stream data tag type Zora (%u)", tag); + break; + default: + break; + } + + return 0; +} + +static int calculate_bitrate(AVFormatContext *s) +{ + AVIContext *avi = s->priv_data; + int i, j; + int64_t lensum = 0; + int64_t maxpos = 0; + + for (i = 0; i<s->nb_streams; i++) { + int64_t len = 0; + AVStream *st = s->streams[i]; + + if (!st->nb_index_entries) + continue; + + for (j = 0; j < st->nb_index_entries; j++) + len += st->index_entries[j].size; + maxpos = FFMAX(maxpos, st->index_entries[j-1].pos); + lensum += len; + } + if (maxpos < avi->io_fsize*9/10) // index does not cover the whole file + return 0; + if (lensum*9/10 > maxpos || lensum < maxpos*9/10) // frame sum and filesize mismatch + return 0; + + for (i = 0; i<s->nb_streams; i++) { + int64_t len = 0; + AVStream *st = s->streams[i]; + int64_t duration; + + for (j = 0; j < st->nb_index_entries; j++) + len += st->index_entries[j].size; + + if (st->nb_index_entries < 2 || st->codec->bit_rate > 0) + continue; + duration = st->index_entries[j-1].timestamp - st->index_entries[0].timestamp; + st->codec->bit_rate = av_rescale(8*len, st->time_base.den, duration * st->time_base.num); + } + return 1; +} + static int avi_read_header(AVFormatContext *s) { AVIContext *avi = s->priv_data; @@ -366,6 +473,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; @@ -373,8 +481,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 */ @@ -382,7 +492,7 @@ static int avi_read_header(AVFormatContext *s) codec_type = -1; frame_period = 0; for (;;) { - if (pb->eof_reached) + if (avio_feof(pb)) goto fail; tag = avio_rl32(pb); size = avio_rl32(pb); @@ -402,7 +512,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')) @@ -430,7 +540,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; @@ -486,6 +596,8 @@ 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; @@ -514,7 +626,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 */ @@ -545,6 +657,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", @@ -555,6 +671,7 @@ static int avi_read_header(AVFormatContext *s) codec_type = AVMEDIA_TYPE_VIDEO; ast->sample_size = 0; + st->avg_frame_rate = av_inv_q(st->time_base); break; case MKTAG('a', 'u', 'd', 's'): codec_type = AVMEDIA_TYPE_AUDIO; @@ -566,23 +683,34 @@ 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]; + if (st->codec->codec_type != AVMEDIA_TYPE_UNKNOWN) { + avio_skip(pb, size); + break; + } switch (codec_type) { case AVMEDIA_TYPE_VIDEO: if (amv_file_format) { @@ -593,7 +721,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')) { @@ -603,17 +731,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_get_extradata(st->codec, pb, st->codec->extradata_size) < 0) return AVERROR(ENOMEM); - } - avio_read(pb, - st->codec->extradata, - st->codec->extradata_size); } // FIXME: check if the encoder really did this correctly @@ -623,7 +747,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; @@ -632,12 +756,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; } @@ -650,11 +770,8 @@ 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 == MKTAG('V', 'S', 'S', 'H')) + st->need_parsing = AVSTREAM_PARSE_FULL; if (st->codec->codec_tag == 0 && st->codec->height > 0 && st->codec->extradata_size < 1U << 30) { @@ -704,15 +821,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; @@ -723,9 +852,35 @@ 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_get_extradata(st->codec, pb, size) < 0) + return AVERROR(ENOMEM); + } + + if (st->codec->extradata_size & 1) //FIXME check if the encoder really did this correctly + avio_r8(pb); + + ret = avi_extract_stream_metadata(st); + if (ret < 0) { + av_log(s, AV_LOG_WARNING, "could not decoding EXIF data in stream header.\n"); + } + } + 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; @@ -774,7 +929,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 */ @@ -794,17 +949,32 @@ fail: if (!avi->index_loaded && pb->seekable) avi_load_index(s); - avi->index_loaded = 1; + calculate_bitrate(s); + avi->index_loaded |= 1; if ((ret = guess_ni_flag(s)) < 0) return ret; - avi->non_interleaved |= ret; + avi->non_interleaved |= ret | (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"); @@ -825,12 +995,14 @@ fail: static int read_gab2_sub(AVStream *st, AVPacket *pkt) { if (pkt->size >= 7 && + pkt->size < INT_MAX - AVPROBE_PADDING_SIZE && !strcmp(pkt->data, "GAB2") && AV_RL16(pkt->data + 5) == 2) { uint8_t desc[256]; int score = AVPROBE_SCORE_EXTENSION, ret; AVIStream *ast = st->priv_data; AVInputFormat *sub_demuxer; AVRational time_base; + int size; AVIOContext *pb = avio_alloc_context(pkt->data + 7, pkt->size - 7, 0, NULL, NULL, NULL, NULL); @@ -848,9 +1020,15 @@ static int read_gab2_sub(AVStream *st, AVPacket *pkt) avio_rl16(pb); /* flags? */ avio_rl32(pb); /* data size */ - pd = (AVProbeData) { .buf = pb->buf_ptr, - .buf_size = pb->buf_end - pb->buf_ptr }; - if (!(sub_demuxer = av_probe_input_format2(&pd, 1, &score))) + size = pb->buf_end - pb->buf_ptr; + pd = (AVProbeData) { .buf = av_mallocz(size + AVPROBE_PADDING_SIZE), + .buf_size = size }; + if (!pd.buf) + goto error; + memcpy(pd.buf, pb->buf_ptr, size); + sub_demuxer = av_probe_input_format2(&pd, 1, &score); + av_freep(&pd.buf); + if (!sub_demuxer) goto error; if (!(ast->sub_ctx = avformat_alloc_context())) @@ -908,7 +1086,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') { @@ -918,6 +1096,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; @@ -929,7 +1111,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); !avio_feof(pb); i++) { int j; for (j = 0; j < 7; j++) @@ -941,7 +1123,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## @@ -978,16 +1160,22 @@ start_sync: st = s->streams[n]; ast = st->priv_data; + if (!ast) { + av_log(s, AV_LOG_WARNING, "Skipping 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; @@ -1004,9 +1192,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) { @@ -1017,7 +1205,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; @@ -1039,7 +1227,7 @@ start_sync: ast->packet_size = size + 8; ast->remaining = size; - if (size || !ast->sample_size) { + if (size) { uint64_t pos = avio_tell(pb) - 8; if (!st->index_entries || !st->nb_index_entries || st->index_entries[st->nb_index_entries - 1].pos < pos) { @@ -1052,6 +1240,8 @@ start_sync: } } + if (pb->error) + return pb->error; return AVERROR_EOF; } @@ -1108,10 +1298,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, @@ -1126,15 +1313,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: @@ -1161,8 +1351,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, @@ -1184,7 +1375,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; @@ -1219,17 +1410,34 @@ FF_ENABLE_DEPRECATION_WARNINGS size); pkt->stream_index = avi->stream_index; - if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) { + if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO && st->index_entries) { AVIndexEntry *e; int index; - assert(st->index_entries); - index = av_index_search_timestamp(st, ast->frame_offset, 0); + index = av_index_search_timestamp(st, ast->frame_offset, AVSEEK_FLAG_ANY); 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; } @@ -1241,6 +1449,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; } @@ -1260,7 +1484,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) @@ -1273,8 +1499,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 (avio_feof(pb)) + return -1; + tag = avio_rl32(pb); flags = avio_rl32(pb); pos = avio_rl32(pb); @@ -1289,7 +1523,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; } @@ -1297,16 +1531,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; } @@ -1320,7 +1563,6 @@ static int check_stream_max_drift(AVFormatContext *s) int *idx = av_mallocz_array(s->nb_streams, sizeof(*idx)); if (!idx) return AVERROR(ENOMEM); - for (min_pos = pos = 0; min_pos != INT64_MAX; pos = min_pos + 1LU) { int64_t max_dts = INT64_MIN / 2; int64_t min_dts = INT64_MAX / 2; @@ -1411,16 +1653,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 (avio_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, @@ -1430,12 +1675,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 } @@ -1460,7 +1710,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 @@ -1472,16 +1722,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; @@ -1495,15 +1752,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; @@ -1519,35 +1779,46 @@ 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(fabs(av_q2d(st2->time_base) - ast2->scale / (double)ast2->rate) < av_q2d(st2->time_base) * 0.00000001); 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; } @@ -1597,4 +1868,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 417a8e9..c45bac2 100644 --- a/libavformat/avienc.c +++ b/libavformat/avienc.c @@ -2,29 +2,39 @@ * 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" #include "avio_internal.h" #include "riff.h" +#include "mpegts.h" +#include "libavformat/avlanguage.h" +#include "libavutil/avstring.h" #include "libavutil/intreadwrite.h" #include "libavutil/dict.h" +#include "libavutil/avassert.h" +#include "libavutil/timestamp.h" +#include "libavutil/pixdesc.h" +#include "libavcodec/raw.h" /* * TODO: @@ -52,9 +62,10 @@ typedef struct { typedef struct { int64_t frames_hdr_strm; - int audio_strm_length; + int64_t audio_strm_length; int packet_count; int entry; + int max_size; AVIIndex indexes; } AVIStream; @@ -117,7 +128,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(s->streams[n], &au_byterate, &au_ssize, &au_scale); @@ -129,7 +140,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); } @@ -147,11 +158,12 @@ static int avi_write_header(AVFormatContext *s) AVStream *video_st = NULL; int64_t list1, list2, strh, strf; AVDictionaryEntry *t = NULL; + int padding; 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++) { @@ -254,9 +266,18 @@ static int avi_write_header(AVFormatContext *s) ff_parse_specific_params(st, &au_byterate, &au_ssize, &au_scale); + if ( enc->codec_type == AVMEDIA_TYPE_VIDEO + && enc->codec_id != AV_CODEC_ID_XSUB + && au_byterate > 1000LL*au_scale) { + au_byterate = 600; + au_scale = 1; + } + avpriv_set_pts_info(st, 64, au_scale, au_byterate); + if (enc->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(st, 64, au_scale, au_byterate); avio_wl32(pb, 0); /* start */ /* remember this offset to fill later */ @@ -267,7 +288,7 @@ static int avi_write_header(AVFormatContext *s) else avio_wl32(pb, 0); /* length, XXX: filled later */ - /* suggested buffer size */ //FIXME set at the end to largest chunk + /* suggested buffer size, is set to largest chunk size in avi_write_trailer */ if (enc->codec_type == AVMEDIA_TYPE_VIDEO) avio_wl32(pb, 1024 * 1024); else if (enc->codec_type == AVMEDIA_TYPE_AUDIO) @@ -282,6 +303,9 @@ static int avi_write_header(AVFormatContext *s) ff_end_tag(pb, strh); if (enc->codec_type != AVMEDIA_TYPE_DATA) { + int ret; + enum AVPixelFormat pix_fmt; + strf = ff_start_tag(pb, "strf"); switch (enc->codec_type) { case AVMEDIA_TYPE_SUBTITLE: @@ -290,20 +314,47 @@ static int avi_write_header(AVFormatContext *s) if (enc->codec_id != AV_CODEC_ID_XSUB) break; case AVMEDIA_TYPE_VIDEO: - ff_put_bmp_header(pb, enc, ff_codec_bmp_tags, 0); + /* WMP expects RGB 5:5:5 rawvideo in avi to have bpp set to 16. */ + if ( !enc->codec_tag + && enc->codec_id == AV_CODEC_ID_RAWVIDEO + && enc->pix_fmt == AV_PIX_FMT_RGB555LE + && enc->bits_per_coded_sample == 15) + enc->bits_per_coded_sample = 16; + ff_put_bmp_header(pb, enc, ff_codec_bmp_tags, 0, 0); + pix_fmt = avpriv_find_pix_fmt(avpriv_pix_fmt_bps_avi, + enc->bits_per_coded_sample); + if ( !enc->codec_tag + && enc->codec_id == AV_CODEC_ID_RAWVIDEO + && enc->pix_fmt != pix_fmt + && enc->pix_fmt != AV_PIX_FMT_NONE) + av_log(s, AV_LOG_ERROR, "%s rawvideo cannot be written to avi, output file will be unreadable\n", + av_get_pix_fmt_name(enc->pix_fmt)); break; case AVMEDIA_TYPE_AUDIO: - if (ff_put_wav_header(pb, enc) < 0) - return -1; + if ((ret = ff_put_wav_header(pb, enc, 0)) < 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(enc->codec_type), "?")); + return AVERROR(EINVAL); } ff_end_tag(pb, strf); if ((t = av_dict_get(st->metadata, "title", NULL, 0))) { ff_riff_write_info_tag(s->pb, "strn", t->value); t = NULL; } + if (enc->codec_id == AV_CODEC_ID_XSUB + && (t = av_dict_get(s->streams[i]->metadata, "language", NULL, 0))) { + const char* langstr = av_convert_lang_to(t->value, AV_LANG_ISO639_1); + t = NULL; + if (langstr) { + char* str = av_asprintf("Subtitle - %s-xx;02", langstr); + ff_riff_write_info_tag(s->pb, "strn", str); + av_free(str); + } + } } if (pb->seekable) { @@ -381,11 +432,18 @@ static int avi_write_header(AVFormatContext *s) ff_riff_write_info(s); + + padding = s->metadata_header_padding; + if (padding < 0) + padding = 1016; + /* some padding for easier tag editing */ - list2 = ff_start_tag(pb, "JUNK"); - for (i = 0; i < 1016; i += 4) - avio_wl32(pb, 0); - ff_end_tag(pb, list2); + if (padding) { + list2 = ff_start_tag(pb, "JUNK"); + for (i = padding; i > 0; i -= 4) + avio_wl32(pb, 0); + ff_end_tag(pb, list2); + } avi->movi_list = ff_start_tag(pb, "LIST"); ffio_wfourcc(pb, "movi"); @@ -403,10 +461,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; @@ -515,15 +576,27 @@ static int avi_write_packet(AVFormatContext *s, AVPacket *pkt) AVIStream *avist = s->streams[stream_index]->priv_data; AVCodecContext *enc = s->streams[stream_index]->codec; + if (enc->codec_id == AV_CODEC_ID_H264 && enc->codec_tag == MKTAG('H','2','6','4')) { + int ret = ff_check_h264_startcode(s, s->streams[stream_index], pkt); + if (ret < 0) + return ret; + } + 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) { + 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++; @@ -547,27 +620,27 @@ static int avi_write_packet(AVFormatContext *s, AVPacket *pkt) avist->audio_strm_length += size; 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; } idx->cluster[cl][id].flags = flags; idx->cluster[cl][id].pos = avio_tell(pb) - avi->movi_list; idx->cluster[cl][id].len = size; + avist->max_size = FFMAX(avist->max_size, size); idx->entry++; } @@ -626,9 +699,13 @@ 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; + if (pb->seekable) { + avio_seek(pb, avist->frames_hdr_strm + 4, SEEK_SET); + avio_wl32(pb, avist->max_size); + } } return res; diff --git a/libavformat/avio.c b/libavformat/avio.c index 7f6449c..326bb0a 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) @@ -180,31 +215,54 @@ int ffurl_connect(URLContext *uc, AVDictionary **options) "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \ "0123456789+-." -int ffurl_alloc(URLContext **puc, const char *filename, int flags, - const AVIOInterruptCB *int_cb) +static struct URLProtocol *url_find_protocol(const char *filename) { URLProtocol *up = NULL; 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 (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'; while (up = ffurl_protocol_next(up)) { if (!strcmp(proto_str, up->name)) - return url_alloc_for_protocol(puc, up, filename, flags, int_cb); + break; if (up->flags & URL_PROTOCOL_FLAG_NESTED_SCHEME && !strcmp(proto_nested, up->name)) - return url_alloc_for_protocol(puc, up, filename, flags, int_cb); + break; + } + + return up; +} + +int ffurl_alloc(URLContext **puc, const char *filename, int flags, + const AVIOInterruptCB *int_cb) +{ + URLProtocol *p = NULL; + + if (!first_protocol) { + av_log(NULL, AV_LOG_WARNING, "No URL Protocols are registered. " + "Missing call to av_register_all()?\n"); } + + p = url_find_protocol(filename); + if (p) + return url_alloc_for_protocol(puc, p, filename, flags, int_cb); + *puc = NULL; + if (av_strstart(filename, "https:", NULL)) + av_log(NULL, AV_LOG_WARNING, "https protocol not found, recompile with openssl or gnutls enabled.\n"); return AVERROR_PROTOCOL_NOT_FOUND; } @@ -212,11 +270,13 @@ int ffurl_open(URLContext **puc, const char *filename, int flags, const AVIOInterruptCB *int_cb, AVDictionary **options) { int ret = ffurl_alloc(puc, filename, flags, int_cb); - if (ret) + if (ret < 0) return ret; if (options && (*puc)->prot->priv_data_class && (ret = av_opt_set_dict((*puc)->priv_data, options)) < 0) goto fail; + if ((ret = av_opt_set_dict(*puc, options)) < 0) + goto fail; ret = ffurl_connect(*puc, options); if (!ret) return 0; @@ -234,9 +294,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 +307,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_relative(); + else if (av_gettime_relative() > 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 +349,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 +362,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,17 +378,30 @@ 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); +} + + +const char *avio_find_protocol_name(const char *url) +{ + URLProtocol *p = url_find_protocol(url); + + return p ? p->name : NULL; +} + int avio_check(const char *url, int flags) { URLContext *h; int ret = ffurl_alloc(&h, url, flags, NULL); - if (ret) + if (ret < 0) return ret; if (h->prot->url_check) { @@ -361,7 +443,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 3360e82..2210c01 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,11 +115,58 @@ 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; + + /** + * Original buffer size + * used internally after probing and ensure seekback to reset the buffer size + * This field is internal to libavformat and access from outside is not allowed. + */ + int orig_buffer_size; } AVIOContext; /* unbuffered I/O */ /** + * Return the name of the protocol that will handle the passed URL. + * + * NULL is returned if no protocol could be found for the given URL. + * + * @return Name of the protocol or NULL. + */ +const char *avio_find_protocol_name(const char *url); + +/** * Return AVIO_FLAG_* access flags corresponding to the access permissions * of the resource in url, or a negative value corresponding to an * AVERROR code in case of failure. The returned access flags are @@ -146,6 +193,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 +239,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 +255,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 +272,30 @@ 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 avio_feof(AVIOContext *s); +#if FF_API_URL_FEOF +/** + * @deprecated use avio_feof() + */ +attribute_deprecated +int url_feof(AVIOContext *s); +#endif + /** @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 +374,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 @@ -321,7 +392,7 @@ int avio_get_str16be(AVIOContext *pb, int maxlen, char *buf, int buflen); * @param url resource to access * @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); @@ -341,7 +412,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, @@ -423,17 +494,28 @@ 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 */ int64_t avio_seek_time(AVIOContext *h, int stream_index, int64_t timestamp, int flags); +/* Avoid a warning. The header can not be included because it breaks c++. */ +struct AVBPrint; + +/** + * Read contents of h into print buffer, up to max_size bytes, or up to EOF. + * + * @return 0 for success (max_size bytes read or EOF reached), negative error + * code otherwise + */ +int avio_read_to_bprint(AVIOContext *h, struct AVBPrint *pb, size_t max_size); + #endif /* AVFORMAT_AVIO_H */ diff --git a/libavformat/avio_internal.h b/libavformat/avio_internal.h index a8bcadd..5864048 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 guarantee is lost. + */ +int ffio_ensure_seekback(AVIOContext *s, int64_t 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); @@ -116,7 +127,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 cc79146..9795ba4 100644 --- a/libavformat/aviobuf.c +++ b/libavformat/aviobuf.c @@ -2,28 +2,30 @@ * 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 */ +#include "libavutil/bprint.h" #include "libavutil/crc.h" #include "libavutil/dict.h" #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" @@ -77,9 +79,11 @@ int ffio_init_context(AVIOContext *s, int64_t (*seek)(void *opaque, int64_t offset, int whence)) { s->buffer = buffer; + s->orig_buffer_size = 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 +125,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 +174,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); @@ -188,12 +203,14 @@ int64_t avio_seek(AVIOContext *s, int64_t offset, int whence) int64_t offset1; int64_t pos; int force = whence & AVSEEK_FORCE; + int buffer_size; whence &= ~AVSEEK_FORCE; if(!s) return AVERROR(EINVAL); - pos = s->pos - (s->write_flag ? 0 : (s->buf_end - s->buffer)); + buffer_size = s->buf_end - s->buffer; + pos = s->pos - (s->write_flag ? 0 : buffer_size); if (whence != SEEK_CUR && whence != SEEK_SET) return AVERROR(EINVAL); @@ -205,22 +222,34 @@ int64_t avio_seek(AVIOContext *s, int64_t offset, int whence) offset += offset1; } offset1 = offset - pos; - if (!s->must_flush && - offset1 >= 0 && offset1 <= (s->buf_end - s->buffer)) { + if (!s->must_flush && (!s->direct || !s->seek) && + offset1 >= 0 && offset1 <= buffer_size) { /* 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); if (s->eof_reached) return AVERROR_EOF; s->buf_ptr = s->buf_end + offset - s->pos; - } else { + } else if(!s->write_flag && offset1 < 0 && -offset1 < buffer_size>>1 && s->seek && offset > 0) { int64_t res; + pos -= FFMIN(buffer_size>>1, pos); + if ((res = s->seek(s->opaque, pos, SEEK_SET)) < 0) + return res; + s->buf_end = + s->buf_ptr = s->buffer; + s->pos = pos; + s->eof_reached = 0; + fill_buffer(s); + return avio_seek(s, offset, SEEK_SET | force); + } else { + int64_t res; if (s->write_flag) { flush_buffer(s); s->must_flush = 1; @@ -229,6 +258,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 +268,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 +292,38 @@ int64_t avio_size(AVIOContext *s) return size; } +int avio_feof(AVIOContext *s) +{ + if(!s) + return 0; + if(s->eof_reached){ + s->eof_reached=0; + fill_buffer(s); + } + return s->eof_reached; +} + +#if FF_API_URL_FEOF +int url_feof(AVIOContext *s) +{ + return avio_feof(s); +} +#endif + 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) @@ -288,15 +341,22 @@ int avio_put_str16le(AVIOContext *s, const char *str) { const uint8_t *q = str; int ret = 0; + int err = 0; while (*q) { uint32_t ch; uint16_t tmp; - GET_UTF8(ch, *q++, break;) + GET_UTF8(ch, *q++, goto invalid;) PUT_UTF16(ch, tmp, avio_wl16(s, tmp); ret += 2;) + continue; +invalid: + av_log(s, AV_LOG_ERROR, "Invaid UTF8 sequence in avio_put_str16le\n"); + err = AVERROR(EINVAL); } avio_wl16(s, 0); + if (err) + return err; ret += 2; return ret; } @@ -316,7 +376,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 +395,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 +443,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->orig_buffer_size && s->buffer_size > s->orig_buffer_size) { + if (dst == s->buffer) { + ffio_set_buf_size(s, s->orig_buffer_size); - s->checksum_ptr = dst = s->buffer; - len = s->buffer_size; + s->checksum_ptr = dst = s->buffer; + } + av_assert0(len >= s->orig_buffer_size); + len = s->orig_buffer_size; } if (s->read_packet) @@ -405,6 +467,7 @@ static void fill_buffer(AVIOContext *s) s->pos += len; s->buf_ptr = dst; s->buf_end = dst + len; + s->bytes_read += len; } } @@ -459,7 +522,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) { @@ -471,6 +534,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; @@ -490,8 +554,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 (avio_feof(s)) return AVERROR_EOF; } return size1 - size; } @@ -539,8 +603,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 (avio_feof(s)) return AVERROR_EOF; } return len; } @@ -609,7 +673,9 @@ int ff_get_line(AVIOContext *s, char *buf, int maxlen) c = avio_r8(s); if (c && i < maxlen-1) buf[i++] = c; - } while (c != '\n' && c); + } while (c != '\n' && c != '\r' && c); + if (c == '\r' && avio_r8(s) != '\n') + avio_skip(s, -1); buf[i] = 0; return i; @@ -692,11 +758,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) { @@ -707,6 +774,32 @@ int ffio_fdopen(AVIOContext **s, URLContext *h) return 0; } +int ffio_ensure_seekback(AVIOContext *s, int64_t buf_size) +{ + uint8_t *buffer; + int max_buffer_size = s->max_packet_size ? + s->max_packet_size : IO_BUFFER_SIZE; + int filled = s->buf_end - s->buffer; + + buf_size += s->buf_ptr - s->buffer + max_buffer_size; + + if (buf_size < filled || s->seekable) + return 0; + av_assert0(!s->write_flag); + + buffer = av_malloc(buf_size); + if (!buffer) + return AVERROR(ENOMEM); + + memcpy(buffer, s->buffer, filled); + 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; @@ -716,6 +809,7 @@ int ffio_set_buf_size(AVIOContext *s, int buf_size) av_free(s->buffer); s->buffer = buffer; + s->orig_buffer_size = s->buffer_size = buf_size; s->buf_ptr = buffer; url_resetbuf(s, s->write_flag ? AVIO_FLAG_WRITE : AVIO_FLAG_READ); @@ -724,7 +818,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; @@ -736,27 +830,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) { @@ -807,6 +906,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); } @@ -858,6 +961,24 @@ int64_t avio_seek_time(AVIOContext *s, int stream_index, return ret; } +int avio_read_to_bprint(AVIOContext *h, AVBPrint *pb, size_t max_size) +{ + int ret; + char buf[1024]; + while (max_size) { + ret = avio_read(h, buf, FFMIN(max_size, sizeof(buf))); + if (ret == AVERROR_EOF) + return 0; + if (ret <= 0) + return ret; + av_bprint_append_data(pb, buf, ret); + if (!av_bprint_is_complete(pb)) + return AVERROR(ENOMEM); + max_size -= ret; + } + return 0; +} + /* output in a dynamic buffer */ typedef struct DynBuffer { @@ -965,7 +1086,7 @@ int ffio_open_dyn_packet_buf(AVIOContext **s, int max_packet_size) int avio_close_dyn_buf(AVIOContext *s, uint8_t **pbuffer) { - DynBuffer *d = s->opaque; + DynBuffer *d; int size; static const char padbuf[FF_INPUT_BUFFER_PADDING_SIZE] = {0}; int padding = 0; @@ -974,6 +1095,7 @@ int avio_close_dyn_buf(AVIOContext *s, uint8_t **pbuffer) *pbuffer = NULL; return 0; } + d = s->opaque; /* don't attempt to pad fixed-size packet buffers */ if (!s->max_packet_size) { diff --git a/libavformat/avisynth.c b/libavformat/avisynth.c index f94f7b7..c01d7e4 100644 --- a/libavformat/avisynth.c +++ b/libavformat/avisynth.c @@ -2,20 +2,19 @@ * AviSynth/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 */ @@ -27,25 +26,17 @@ /* Enable function pointer definitions for runtime loading. */ #define AVSC_NO_DECLSPEC -/* Platform-specific directives for AviSynth vs AvxSynth. - * - * avisynth_c.h needs to be the one provided with x264, as - * the one in AviSynth's CVS hasn't been updated to support - * 2.6's extra colorspaces. A temporary source of that header, - * installable from a GNU-style Makefile is available from - * github.com/qyot27/avisynth_headers -- AvxSynth doesn't - * require this kind of special treatment because like any - * standard *nix application, it installs its headers - * alongside its libs. */ +/* Platform-specific directives for AviSynth vs AvxSynth. */ #ifdef _WIN32 #include <windows.h> #undef EXTERN_C - #include <avisynth/avisynth_c.h> + #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 <avxsynth/avxsynth_c.h> + #include "compat/avisynth/avxsynth_c.h" #if defined (__APPLE__) #define AVISYNTH_LIB "libavxsynth.dylib" #else @@ -393,19 +384,6 @@ static int avisynth_open_file(AVFormatContext *s) avs->clip = avs_library.avs_take_clip(val, avs->env); avs->vi = avs_library.avs_get_video_info(avs->clip); -#ifdef USING_AVISYNTH - /* libav only supports AviSynth 2.6 on Windows. Since AvxSynth - * identifies itself as interface version 3 like 2.5.8, this - * needs to be special-cased. */ - - if (avs_library.avs_get_version(avs->clip) == 3) { - av_log(s, AV_LOG_ERROR, - "AviSynth 2.5.8 not supported. Please upgrade to 2.6.\n"); - ret = AVERROR_UNKNOWN; - goto fail; - } -#endif - /* Release the AVS_Value as it will go out of scope. */ avs_library.avs_release_value(val); @@ -502,8 +480,18 @@ static int avisynth_read_packet_video(AVFormatContext *s, AVPacket *pkt, 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)) { diff --git a/libavformat/avlanguage.c b/libavformat/avlanguage.c index e606ef2..39f2560 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 2ec3e2d..7fb8968 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 0000000..a33134e --- /dev/null +++ b/libavformat/avr.c @@ -0,0 +1,99 @@ +/* + * 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 0; + + if (!AV_RB16(p->buf+12) || AV_RB16(p->buf+12) > 256) // channels + return AVPROBE_SCORE_EXTENSION/2; + if (AV_RB16(p->buf+14) > 256) // bps + return AVPROBE_SCORE_EXTENSION/2; + + return AVPROBE_SCORE_EXTENSION; +} + +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 d8042c5..b699dbf 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 120e145..c98ff14 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 */ @@ -61,6 +61,9 @@ static int vid_probe(AVProbeData *p) if (AV_RL32(p->buf) != MKTAG('V', 'I', 'D', 0)) return 0; + if (p->buf[4] != 2) + return AVPROBE_SCORE_MAX / 4; + return AVPROBE_SCORE_MAX; } @@ -189,7 +192,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 +213,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 || avio_feof(pb)) + return AVERROR_EOF; block_type = avio_r8(pb); switch(block_type){ diff --git a/libavformat/bfi.c b/libavformat/bfi.c index 19060e7..cc7f494 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 || avio_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 (avio_feof(pb)) return AVERROR(EIO); state = 256*state + avio_r8(pb); } diff --git a/libavformat/bink.c b/libavformat/bink.c index 4d2a735..395c8d9 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 */ @@ -61,8 +61,10 @@ static int probe(AVProbeData *p) { const uint8_t *b = p->buf; - if ( b[0] == 'B' && b[1] == 'I' && b[2] == 'K' && - (b[3] == 'b' || b[3] == 'f' || b[3] == 'g' || b[3] == 'h' || b[3] == 'i') && + if (((b[0] == 'B' && b[1] == 'I' && b[2] == 'K' && + (b[3] == 'b' || b[3] == 'f' || b[3] == 'g' || b[3] == 'h' || b[3] == 'i')) || + (b[0] == 'K' && b[1] == 'B' && b[2] == '2' && /* Bink 2 */ + (b[3] == 'a' || b[3] == 'd' || b[3] == 'f' || b[3] == 'g'))) && AV_RL32(b+8) > 0 && // num_frames AV_RL32(b+20) > 0 && AV_RL32(b+20) <= BINK_MAX_WIDTH && AV_RL32(b+24) > 0 && AV_RL32(b+24) <= BINK_MAX_HEIGHT && @@ -81,6 +83,7 @@ static int read_header(AVFormatContext *s) uint32_t pos, next_pos; uint16_t flags; int keyframe; + int ret; vst = avformat_new_stream(s, NULL); if (!vst) @@ -120,11 +123,14 @@ 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 ((vst->codec->codec_tag & 0xFFFFFF) == MKTAG('K', 'B', '2', 0)) { + av_log(s, AV_LOG_WARNING, "Bink 2 video is not implemented\n"); + vst->codec->codec_id = AV_CODEC_ID_NONE; + } + + if (ff_get_extradata(vst->codec, pb, 4) < 0) return AVERROR(ENOMEM); - vst->codec->extradata_size = 4; - avio_read(pb, vst->codec->extradata, 4); bink->num_audio_tracks = avio_rl32(pb); @@ -156,10 +162,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); } @@ -185,11 +189,12 @@ static int read_header(AVFormatContext *s) av_log(s, AV_LOG_ERROR, "invalid frame index table\n"); return AVERROR(EIO); } - av_add_index_entry(vst, pos, i, next_pos - pos, 0, - keyframe ? AVINDEX_KEYFRAME : 0); + if ((ret = av_add_index_entry(vst, pos, i, next_pos - pos, 0, + keyframe ? AVINDEX_KEYFRAME : 0)) < 0) + return ret; } - avio_skip(pb, 4); + avio_seek(pb, vst->index_entries[0].pos, SEEK_SET); bink->current_track = -1; return 0; @@ -206,7 +211,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); @@ -288,4 +293,5 @@ AVInputFormat ff_bink_demuxer = { .read_header = read_header, .read_packet = read_packet, .read_seek = read_seek, + .flags = AVFMT_SHOW_IDS, }; diff --git a/libavformat/bintext.c b/libavformat/bintext.c new file mode 100644 index 0000000..217ea49 --- /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 (avio_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 0000000..7b807b9 --- /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(avio_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 0000000..d2c57aa --- /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 b5572af..f7a6068 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 0000000..45f6b39 --- /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 0000000..19a4a2a --- /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 (!avio_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 additional 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 (avio_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 3f2a98f..93a61be 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 0000000..35f81e8 --- /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 cf128d5..c1ecc94 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 7ca4dc5..9c25f2c 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 0034f00..80c5bb5 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 */ @@ -71,7 +71,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; @@ -88,7 +88,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); @@ -131,8 +131,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. @@ -155,13 +154,9 @@ 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_get_extradata(st->codec, pb, size) < 0) return AVERROR(ENOMEM); - avio_read(pb, st->codec->extradata, size); - st->codec->extradata_size = size; } return 0; @@ -209,10 +204,10 @@ static void read_info_chunk(AVFormatContext *s, int64_t size) AVIOContext *pb = s->pb; unsigned int i; unsigned int nb_entries = avio_rb32(pb); - for (i = 0; i < nb_entries; i++) { + for (i = 0; i < nb_entries && !avio_feof(pb); 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); } @@ -225,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 */ @@ -245,7 +240,7 @@ static int read_header(AVFormatContext *s) /* parse each chunk */ found_data = 0; - while (!pb->eof_reached) { + while (!avio_feof(pb)) { /* stop at data chunk if seeking is not supported or data chunk size is unknown */ @@ -254,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 (avio_feof(pb)) break; switch (tag) { @@ -291,15 +287,20 @@ static int read_header(AVFormatContext *s) default: #define _(x) ((x) >= ' ' ? (x) : ' ') av_log(s, AV_LOG_WARNING, - "skipping CAF chunk: %08"PRIX32" (%"PRIu32"%"PRIu32"%"PRIu32"%"PRIu32")\n", - tag, _(tag>>24), _((tag>>16)&0xFF), _((tag>>8)&0xFF), _(tag&0xFF)); + "skipping CAF chunk: %08"PRIX32" (%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) @@ -308,7 +309,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 { @@ -337,13 +338,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 (avio_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); } @@ -394,7 +397,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 0000000..1708275 --- /dev/null +++ b/libavformat/cafenc.c @@ -0,0 +1,286 @@ +/* + * 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, int block_align) { + 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 (block_align - 4 * channels) * 8 / (4 * channels) + 1; + case AV_CODEC_ID_ADPCM_MS: + return (block_align - 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; + int frame_size = enc->frame_size; + + if (s->nb_streams != 1) { + av_log(s, AV_LOG_ERROR, "CAF files have exactly one stream\n"); + return AVERROR(EINVAL); + } + + switch (enc->codec_id) { + case AV_CODEC_ID_AAC: + 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: + 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; + } + + if (enc->codec_id != AV_CODEC_ID_MP3 || frame_size != 576) + frame_size = samples_per_packet(enc->codec_id, enc->channels, enc->block_align); + + 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, frame_size); //< 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, enc->block_align)); ///< 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 fcca9e1..880f4ab 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 */ @@ -61,7 +61,7 @@ static int cavsvideo_probe(AVProbeData *p) } } if(seq && seq*9<=pic*10) - return AVPROBE_SCORE_EXTENSION; + return AVPROBE_SCORE_EXTENSION+1; return 0; } diff --git a/libavformat/cdg.c b/libavformat/cdg.c index e699691..baf37d4 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 */ @@ -46,10 +46,11 @@ static int read_header(AVFormatContext *s) avpriv_set_pts_info(vst, 32, 1, 300); ret = avio_size(s->pb); - if (ret < 0) - return ret; + if (ret < 0) { + av_log(s, AV_LOG_WARNING, "Cannot calculate duration as file size cannot be determined\n"); + } else + vst->duration = (ret * vst->time_base.den) / (CDG_PACKET_SIZE * 300); - vst->duration = (ret * vst->time_base.den) / (CDG_PACKET_SIZE * 300); return 0; } @@ -71,6 +72,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; } @@ -80,5 +87,6 @@ AVInputFormat ff_cdg_demuxer = { .priv_data_size = sizeof(CDGContext), .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 a2cba52..e3e379a 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 (avio_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/cinedec.c b/libavformat/cinedec.c new file mode 100644 index 0000000..9eed006 --- /dev/null +++ b/libavformat/cinedec.c @@ -0,0 +1,311 @@ +/* + * Phanton Cine demuxer + * 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 + */ + +/** + * @file + * Phantom Cine demuxer + * @author Peter Ross <pross@xvid.org> + */ + +#include "libavutil/intreadwrite.h" +#include "libavcodec/bmp.h" +#include "avformat.h" +#include "internal.h" + +typedef struct { + uint64_t pts; +} CineDemuxContext; + +/** Compression */ +enum { + CC_RGB = 0, /**< Gray */ + CC_LEAD = 1, /**< LEAD (M)JPEG */ + CC_UNINT = 2 /**< Uninterpolated color image (CFA field indicates color ordering) */ +}; + +/** Color Filter Array */ +enum { + CFA_NONE = 0, /**< GRAY */ + CFA_VRI = 1, /**< GBRG/RGGB */ + CFA_VRIV6 = 2, /**< BGGR/GRBG */ + CFA_BAYER = 3, /**< GB/RG */ + CFA_BAYERFLIP = 4, /**< RG/GB */ + + CFA_TLGRAY = 0x80000000, + CFA_TRGRAY = 0x40000000, + CFA_BLGRAY = 0x20000000, + CFA_BRGRAY = 0x10000000 +}; + +static int cine_read_probe(AVProbeData *p) +{ + int HeaderSize; + if (p->buf[0] == 'C' && p->buf[1] == 'I' && // Type + (HeaderSize = AV_RL16(p->buf + 2)) >= 0x2C && // HeaderSize + AV_RL16(p->buf + 4) <= CC_UNINT && // Compression + AV_RL16(p->buf + 6) <= 1 && // Version + AV_RL32(p->buf + 20) && // ImageCount + AV_RL32(p->buf + 24) >= HeaderSize && // OffImageHeader + AV_RL32(p->buf + 28) >= HeaderSize && // OffSetup + AV_RL32(p->buf + 32) >= HeaderSize) // OffImageOffsets + return AVPROBE_SCORE_MAX; + return 0; +} + +static int set_metadata_int(AVDictionary **dict, const char *key, int value, int allow_zero) +{ + if (value || allow_zero) { + return av_dict_set_int(dict, key, value, 0); + } + return 0; +} + +static int cine_read_header(AVFormatContext *avctx) +{ + AVIOContext *pb = avctx->pb; + AVStream *st; + unsigned int version, compression, offImageHeader, offSetup, offImageOffsets, biBitCount, length, CFA; + int vflip; + char *description; + uint64_t i; + + st = avformat_new_stream(avctx, NULL); + if (!st) + return AVERROR(ENOMEM); + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->codec->codec_id = AV_CODEC_ID_RAWVIDEO; + st->codec->codec_tag = 0; + + /* CINEFILEHEADER structure */ + avio_skip(pb, 4); // Type, Headersize + + compression = avio_rl16(pb); + version = avio_rl16(pb); + if (version != 1) { + avpriv_request_sample(avctx, "uknown version %i", version); + return AVERROR_INVALIDDATA; + } + + avio_skip(pb, 12); // FirstMovieImage, TotalImageCount, FirstImageNumber + + st->duration = avio_rl32(pb); + offImageHeader = avio_rl32(pb); + offSetup = avio_rl32(pb); + offImageOffsets = avio_rl32(pb); + + avio_skip(pb, 8); // TriggerTime + + /* BITMAPINFOHEADER structure */ + avio_seek(pb, offImageHeader, SEEK_SET); + avio_skip(pb, 4); //biSize + st->codec->width = avio_rl32(pb); + st->codec->height = avio_rl32(pb); + + if (avio_rl16(pb) != 1) // biPlanes + return AVERROR_INVALIDDATA; + + biBitCount = avio_rl16(pb); + if (biBitCount != 8 && biBitCount != 16 && biBitCount != 24 && biBitCount != 48) { + avpriv_request_sample(avctx, "unsupported biBitCount %i", biBitCount); + return AVERROR_INVALIDDATA; + } + + switch (avio_rl32(pb)) { + case BMP_RGB: + vflip = 0; + break; + case 0x100: /* BI_PACKED */ + st->codec->codec_tag = MKTAG('B', 'I', 'T', 0); + vflip = 1; + break; + default: + avpriv_request_sample(avctx, "unknown bitmap compression"); + return AVERROR_INVALIDDATA; + } + + avio_skip(pb, 4); // biSizeImage + + /* parse SETUP structure */ + avio_seek(pb, offSetup, SEEK_SET); + avio_skip(pb, 140); // FrameRatae16 .. descriptionOld + if (avio_rl16(pb) != 0x5453) + return AVERROR_INVALIDDATA; + length = avio_rl16(pb); + if (length < 0x163C) { + avpriv_request_sample(avctx, "short SETUP header"); + return AVERROR_INVALIDDATA; + } + + avio_skip(pb, 616); // Binning .. bFlipH + if (!avio_rl32(pb) ^ vflip) { + st->codec->extradata = av_strdup("BottomUp"); + st->codec->extradata_size = 9; + } + + avio_skip(pb, 4); // Grid + + avpriv_set_pts_info(st, 64, 1, avio_rl32(pb)); + + avio_skip(pb, 20); // Shutter .. bEnableColor + + set_metadata_int(&st->metadata, "camera_version", avio_rl32(pb), 0); + set_metadata_int(&st->metadata, "firmware_version", avio_rl32(pb), 0); + set_metadata_int(&st->metadata, "software_version", avio_rl32(pb), 0); + set_metadata_int(&st->metadata, "recording_timezone", avio_rl32(pb), 0); + + CFA = avio_rl32(pb); + + set_metadata_int(&st->metadata, "brightness", avio_rl32(pb), 1); + set_metadata_int(&st->metadata, "contrast", avio_rl32(pb), 1); + set_metadata_int(&st->metadata, "gamma", avio_rl32(pb), 1); + + avio_skip(pb, 72); // Reserved1 .. WBView + + st->codec->bits_per_coded_sample = avio_rl32(pb); + + if (compression == CC_RGB) { + if (biBitCount == 8) { + st->codec->pix_fmt = AV_PIX_FMT_GRAY8; + } else if (biBitCount == 16) { + st->codec->pix_fmt = AV_PIX_FMT_GRAY16LE; + } else if (biBitCount == 24) { + st->codec->pix_fmt = AV_PIX_FMT_BGR24; + } else if (biBitCount == 48) { + st->codec->pix_fmt = AV_PIX_FMT_BGR48LE; + } else { + avpriv_request_sample(avctx, "unsupported biBitCount %i", biBitCount); + return AVERROR_INVALIDDATA; + } + } else if (compression == CC_UNINT) { + switch (CFA & 0xFFFFFF) { + case CFA_BAYER: + if (biBitCount == 8) { + st->codec->pix_fmt = AV_PIX_FMT_BAYER_GBRG8; + } else if (biBitCount == 16) { + st->codec->pix_fmt = AV_PIX_FMT_BAYER_GBRG16LE; + } else { + avpriv_request_sample(avctx, "unsupported biBitCount %i", biBitCount); + return AVERROR_INVALIDDATA; + } + break; + case CFA_BAYERFLIP: + if (biBitCount == 8) { + st->codec->pix_fmt = AV_PIX_FMT_BAYER_RGGB8; + } else if (biBitCount == 16) { + st->codec->pix_fmt = AV_PIX_FMT_BAYER_RGGB16LE; + } else { + avpriv_request_sample(avctx, "unsupported biBitCount %i", biBitCount); + return AVERROR_INVALIDDATA; + } + break; + default: + avpriv_request_sample(avctx, "unsupported Color Field Array (CFA) %i", CFA & 0xFFFFFF); + return AVERROR_INVALIDDATA; + } + } else { //CC_LEAD + avpriv_request_sample(avctx, "unsupported compression %i", compression); + return AVERROR_INVALIDDATA; + } + + avio_skip(pb, 668); // Conv8Min ... Sensor + + set_metadata_int(&st->metadata, "shutter_ns", avio_rl32(pb), 0); + + avio_skip(pb, 24); // EDRShutterNs ... ImHeightAcq + +#define DESCRIPTION_SIZE 4096 + description = av_malloc(DESCRIPTION_SIZE + 1); + if (!description) + return AVERROR(ENOMEM); + i = avio_get_str(pb, DESCRIPTION_SIZE, description, DESCRIPTION_SIZE + 1); + if (i < DESCRIPTION_SIZE) + avio_skip(pb, DESCRIPTION_SIZE - i); + if (description[0]) + av_dict_set(&st->metadata, "description", description, AV_DICT_DONT_STRDUP_VAL); + else + av_free(description); + + avio_skip(pb, 1176); // RisingEdge ... cmUser + + set_metadata_int(&st->metadata, "enable_crop", avio_rl32(pb), 1); + set_metadata_int(&st->metadata, "crop_left", avio_rl32(pb), 1); + set_metadata_int(&st->metadata, "crop_top", avio_rl32(pb), 1); + set_metadata_int(&st->metadata, "crop_right", avio_rl32(pb), 1); + set_metadata_int(&st->metadata, "crop_bottom", avio_rl32(pb), 1); + + /* parse image offsets */ + avio_seek(pb, offImageOffsets, SEEK_SET); + for (i = 0; i < st->duration; i++) + av_add_index_entry(st, avio_rl64(pb), i, 0, 0, AVINDEX_KEYFRAME); + + return 0; +} + +static int cine_read_packet(AVFormatContext *avctx, AVPacket *pkt) +{ + CineDemuxContext *cine = avctx->priv_data; + AVStream *st = avctx->streams[0]; + AVIOContext *pb = avctx->pb; + int n, size, ret; + + if (cine->pts >= st->duration) + return AVERROR_EOF; + + avio_seek(pb, st->index_entries[cine->pts].pos, SEEK_SET); + n = avio_rl32(pb); + if (n < 8) + return AVERROR_INVALIDDATA; + avio_skip(pb, n - 8); + size = avio_rl32(pb); + + ret = av_get_packet(pb, pkt, size); + if (ret < 0) + return ret; + + pkt->pts = cine->pts++; + pkt->stream_index = 0; + pkt->flags |= AV_PKT_FLAG_KEY; + return 0; +} + +static int cine_read_seek(AVFormatContext *avctx, int stream_index, int64_t timestamp, int flags) +{ + CineDemuxContext *cine = avctx->priv_data; + + if ((flags & AVSEEK_FLAG_FRAME) || (flags & AVSEEK_FLAG_BYTE)) + return AVERROR(ENOSYS); + + if (!avctx->pb->seekable) + return AVERROR(EIO); + + cine->pts = timestamp; + return 0; +} + +AVInputFormat ff_cine_demuxer = { + .name = "cine", + .long_name = NULL_IF_CONFIG_SMALL("Phantom Cine"), + .priv_data_size = sizeof(CineDemuxContext), + .read_probe = cine_read_probe, + .read_header = cine_read_header, + .read_packet = cine_read_packet, + .read_seek = cine_read_seek, +}; diff --git a/libavformat/concat.c b/libavformat/concat.c index 416bbf5..3bbc83d 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 0000000..4590dc5 --- /dev/null +++ b/libavformat/concatdec.c @@ -0,0 +1,631 @@ +/* + * 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/avassert.h" +#include "libavutil/avstring.h" +#include "libavutil/intreadwrite.h" +#include "libavutil/opt.h" +#include "libavutil/parseutils.h" +#include "avformat.h" +#include "internal.h" +#include "url.h" + +typedef enum ConcatMatchMode { + MATCH_ONE_TO_ONE, + MATCH_EXACT_ID, +} ConcatMatchMode; + +typedef struct ConcatStream { + AVBitStreamFilterContext *bsf; + int out_stream_index; +} ConcatStream; + +typedef struct { + char *url; + int64_t start_time; + int64_t duration; + ConcatStream *streams; + int nb_streams; +} ConcatFile; + +typedef struct { + AVClass *class; + ConcatFile *files; + ConcatFile *cur_file; + unsigned nb_files; + AVFormatContext *avf; + int safe; + int seekable; + ConcatMatchMode stream_match_mode; + unsigned auto_convert; +} 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; + const char *proto; + size_t url_len, proto_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)); + } + + proto = avio_find_protocol_name(filename); + proto_len = proto ? strlen(proto) : 0; + if (!memcmp(filename, proto, proto_len) && + (filename[proto_len] == ':' || filename[proto_len] == ',')) { + url = filename; + filename = NULL; + } else { + 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 copy_stream_props(AVStream *st, AVStream *source_st) +{ + int ret; + + if (st->codec->codec_id || !source_st->codec->codec_id) { + if (st->codec->extradata_size < source_st->codec->extradata_size) { + ret = ff_alloc_extradata(st->codec, + source_st->codec->extradata_size); + if (ret < 0) + return ret; + } + memcpy(st->codec->extradata, source_st->codec->extradata, + source_st->codec->extradata_size); + return 0; + } + if ((ret = avcodec_copy_context(st->codec, source_st->codec)) < 0) + return 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; +} + +static int detect_stream_specific(AVFormatContext *avf, int idx) +{ + ConcatContext *cat = avf->priv_data; + AVStream *st = cat->avf->streams[idx]; + ConcatStream *cs = &cat->cur_file->streams[idx]; + AVBitStreamFilterContext *bsf; + + if (cat->auto_convert && st->codec->codec_id == AV_CODEC_ID_H264 && + (st->codec->extradata_size < 4 || AV_RB32(st->codec->extradata) != 1)) { + av_log(cat->avf, AV_LOG_INFO, + "Auto-inserting h264_mp4toannexb bitstream filter\n"); + if (!(bsf = av_bitstream_filter_init("h264_mp4toannexb"))) { + av_log(avf, AV_LOG_ERROR, "h264_mp4toannexb bitstream filter " + "required for H.264 streams\n"); + return AVERROR_BSF_NOT_FOUND; + } + cs->bsf = bsf; + } + return 0; +} + +static int match_streams_one_to_one(AVFormatContext *avf) +{ + ConcatContext *cat = avf->priv_data; + AVStream *st; + int i, ret; + + for (i = cat->cur_file->nb_streams; i < cat->avf->nb_streams; i++) { + if (i < avf->nb_streams) { + st = avf->streams[i]; + } else { + if (!(st = avformat_new_stream(avf, NULL))) + return AVERROR(ENOMEM); + } + if ((ret = copy_stream_props(st, cat->avf->streams[i])) < 0) + return ret; + cat->cur_file->streams[i].out_stream_index = i; + } + return 0; +} + +static int match_streams_exact_id(AVFormatContext *avf) +{ + ConcatContext *cat = avf->priv_data; + AVStream *st; + int i, j, ret; + + for (i = cat->cur_file->nb_streams; i < cat->avf->nb_streams; i++) { + st = cat->avf->streams[i]; + for (j = 0; j < avf->nb_streams; j++) { + if (avf->streams[j]->id == st->id) { + av_log(avf, AV_LOG_VERBOSE, + "Match slave stream #%d with stream #%d id 0x%x\n", + i, j, st->id); + if ((ret = copy_stream_props(avf->streams[j], st)) < 0) + return ret; + cat->cur_file->streams[i].out_stream_index = j; + } + } + } + return 0; +} + +static int match_streams(AVFormatContext *avf) +{ + ConcatContext *cat = avf->priv_data; + ConcatStream *map; + int i, ret; + + if (cat->cur_file->nb_streams >= cat->avf->nb_streams) + return 0; + map = av_realloc(cat->cur_file->streams, + cat->avf->nb_streams * sizeof(*map)); + if (!map) + return AVERROR(ENOMEM); + cat->cur_file->streams = map; + memset(map + cat->cur_file->nb_streams, 0, + (cat->avf->nb_streams - cat->cur_file->nb_streams) * sizeof(*map)); + + for (i = cat->cur_file->nb_streams; i < cat->avf->nb_streams; i++) + map[i].out_stream_index = -1; + switch (cat->stream_match_mode) { + case MATCH_ONE_TO_ONE: + ret = match_streams_one_to_one(avf); + break; + case MATCH_EXACT_ID: + ret = match_streams_exact_id(avf); + break; + default: + ret = AVERROR_BUG; + } + if (ret < 0) + return ret; + for (i = cat->cur_file->nb_streams; i < cat->avf->nb_streams; i++) + if ((ret = detect_stream_specific(avf, i)) < 0) + return ret; + cat->cur_file->nb_streams = cat->avf->nb_streams; + return 0; +} + +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); + + cat->avf = avformat_alloc_context(); + if (!cat->avf) + return AVERROR(ENOMEM); + + cat->avf->interrupt_callback = avf->interrupt_callback; + 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); + avformat_close_input(&cat->avf); + 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; + if ((ret = match_streams(avf)) < 0) + return ret; + 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[i].streams); + } + 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; + 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, "stream")) { + if (!avformat_new_stream(avf, NULL)) + FAIL(AVERROR(ENOMEM)); + } else if (!strcmp(keyword, "exact_stream_id")) { + if (!avf->nb_streams) { + av_log(avf, AV_LOG_ERROR, "Line %d: exact_stream_id without stream\n", + line); + FAIL(AVERROR_INVALIDDATA); + } + avf->streams[avf->nb_streams - 1]->id = + strtol(get_keyword(&cursor), NULL, 0); + } 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; + } + + cat->stream_match_mode = avf->nb_streams ? MATCH_EXACT_ID : + MATCH_ONE_TO_ONE; + if ((ret = open_file(avf, 0)) < 0) + FAIL(ret); + 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 filter_packet(AVFormatContext *avf, ConcatStream *cs, AVPacket *pkt) +{ + AVStream *st = avf->streams[cs->out_stream_index]; + AVBitStreamFilterContext *bsf; + AVPacket pkt2; + int ret; + + av_assert0(cs->out_stream_index >= 0); + for (bsf = cs->bsf; bsf; bsf = bsf->next) { + pkt2 = *pkt; + ret = av_bitstream_filter_filter(bsf, st->codec, NULL, + &pkt2.data, &pkt2.size, + pkt->data, pkt->size, + !!(pkt->flags & AV_PKT_FLAG_KEY)); + if (ret < 0) { + av_packet_unref(pkt); + return ret; + } + av_assert0(pkt2.buf); + if (ret == 0 && pkt2.data != pkt->data) { + if ((ret = av_copy_packet(&pkt2, pkt)) < 0) { + av_free(pkt2.data); + return ret; + } + ret = 1; + } + if (ret > 0) { + av_free_packet(pkt); + pkt2.buf = av_buffer_create(pkt2.data, pkt2.size, + av_buffer_default_free, NULL, 0); + if (!pkt2.buf) { + av_free(pkt2.data); + return AVERROR(ENOMEM); + } + } + *pkt = pkt2; + } + return 0; +} + +static int concat_read_packet(AVFormatContext *avf, AVPacket *pkt) +{ + ConcatContext *cat = avf->priv_data; + int ret; + int64_t delta; + ConcatStream *cs; + + while (1) { + ret = av_read_frame(cat->avf, pkt); + if (ret == AVERROR_EOF) { + if ((ret = open_next_file(avf)) < 0) + return ret; + continue; + } + if (ret < 0) + return ret; + if ((ret = match_streams(avf)) < 0) { + av_packet_unref(pkt); + return ret; + } + cs = &cat->cur_file->streams[pkt->stream_index]; + if (cs->out_stream_index < 0) { + av_packet_unref(pkt); + continue; + } + pkt->stream_index = cs->out_stream_index; + break; + } + if ((ret = filter_packet(avf, cs, pkt))) + return ret; + + 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 }, + { "auto_convert", "automatically convert bitstream format", + OFFSET(auto_convert), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 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 925646b..3fdfea5 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 */ @@ -59,7 +59,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 3bc33f2..a9b6e47 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 f58e152..0458a2d 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 0000000..1598b99 --- /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/dauddec.c b/libavformat/dauddec.c index 1855e84..f6e7491 100644 --- a/libavformat/dauddec.c +++ b/libavformat/dauddec.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 (avio_feof(pb)) return AVERROR(EIO); size = avio_rb16(pb); avio_rb16(pb); // unknown @@ -55,5 +55,5 @@ 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", }; diff --git a/libavformat/daudenc.c b/libavformat/daudenc.c index 416e895..99b18d3 100644 --- a/libavformat/daudenc.c +++ b/libavformat/daudenc.c @@ -2,20 +2,20 @@ * D-Cinema audio muxer * 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 */ diff --git a/libavformat/dfa.c b/libavformat/dfa.c index c097d72..450bc21 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 */ @@ -30,6 +30,9 @@ static int dfa_probe(AVProbeData *p) if (p->buf_size < 4 || AV_RL32(p->buf) != MKTAG('D', 'F', 'I', 'A')) return 0; + if (AV_RL32(p->buf + 16) != 0x80) + return AVPROBE_SCORE_MAX / 4; + return AVPROBE_SCORE_MAX; } @@ -38,13 +41,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); @@ -64,6 +69,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; } @@ -73,12 +84,12 @@ static int dfa_read_packet(AVFormatContext *s, AVPacket *pkt) uint32_t frame_size; int ret, first = 1; - if (pb->eof_reached) + if (avio_feof(pb)) return AVERROR_EOF; if (av_get_packet(pb, pkt, 12) != 12) return AVERROR(EIO); - while (!pb->eof_reached) { + while (!avio_feof(pb)) { if (!first) { ret = av_append_packet(pb, pkt, 12); if (ret < 0) { diff --git a/libavformat/diracdec.c b/libavformat/diracdec.c index f275212..e061ba5 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 */ @@ -25,10 +25,19 @@ static int dirac_probe(AVProbeData *p) { - if (AV_RL32(p->buf) == MKTAG('B', 'B', 'C', 'D')) - return AVPROBE_SCORE_MAX; - else + unsigned size; + if (AV_RL32(p->buf) != MKTAG('B', 'B', 'C', 'D')) return 0; + + size = AV_RB32(p->buf+5); + if (size < 13) + return 0; + if (size + 13LL > p->buf_size) + return AVPROBE_SCORE_MAX / 4; + if (AV_RL32(p->buf + size) != MKTAG('B', 'B', 'C', 'D')) + return 0; + + return AVPROBE_SCORE_MAX; } FF_DEF_RAWVIDEO_DEMUXER(dirac, "raw Dirac", dirac_probe, NULL, AV_CODEC_ID_DIRAC) diff --git a/libavformat/dnxhddec.c b/libavformat/dnxhddec.c index 8bb6814..7b55400 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/dsfdec.c b/libavformat/dsfdec.c new file mode 100644 index 0000000..ae198b2 --- /dev/null +++ b/libavformat/dsfdec.c @@ -0,0 +1,159 @@ +/* + * DSD Stream File (DSF) demuxer + * Copyright (c) 2014 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 + */ + +#include "libavutil/intreadwrite.h" +#include "avformat.h" +#include "internal.h" +#include "id3v2.h" + +typedef struct { + uint64_t data_end; +} DSFContext; + +static int dsf_probe(AVProbeData *p) +{ + if (p->buf_size < 12 || memcmp(p->buf, "DSD ", 4) || AV_RL64(p->buf + 4) != 28) + return 0; + return AVPROBE_SCORE_MAX; +} + +static const uint64_t dsf_channel_layout[] = { + 0, + AV_CH_LAYOUT_MONO, + AV_CH_LAYOUT_STEREO, + AV_CH_LAYOUT_SURROUND, + AV_CH_LAYOUT_QUAD, + AV_CH_LAYOUT_4POINT0, + AV_CH_LAYOUT_5POINT0_BACK, + AV_CH_LAYOUT_5POINT1_BACK, +}; + +static void read_id3(AVFormatContext *s, uint64_t id3pos) +{ + ID3v2ExtraMeta *id3v2_extra_meta = NULL; + if (avio_seek(s->pb, id3pos, SEEK_SET) < 0) + return; + + ff_id3v2_read(s, ID3v2_DEFAULT_MAGIC, &id3v2_extra_meta, 0); + if (id3v2_extra_meta) + ff_id3v2_parse_apic(s, &id3v2_extra_meta); + ff_id3v2_free_extra_meta(&id3v2_extra_meta); +} + +static int dsf_read_header(AVFormatContext *s) +{ + DSFContext *dsf = s->priv_data; + AVIOContext *pb = s->pb; + AVStream *st; + uint64_t id3pos; + unsigned int channel_type; + + avio_skip(pb, 4); + if (avio_rl64(pb) != 28) + return AVERROR_INVALIDDATA; + + /* create primary stream before any id3 coverart streams */ + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + avio_skip(pb, 8); + id3pos = avio_rl64(pb); + if (pb->seekable) { + read_id3(s, id3pos); + avio_seek(pb, 28, SEEK_SET); + } + + /* fmt chunk */ + + if (avio_rl32(pb) != MKTAG('f', 'm', 't', ' ') || avio_rl64(pb) != 52) + return AVERROR_INVALIDDATA; + + if (avio_rl32(pb) != 1) { + avpriv_request_sample(s, "unknown format version"); + return AVERROR_INVALIDDATA; + } + + if (avio_rl32(pb)) { + avpriv_request_sample(s, "unknown format id"); + return AVERROR_INVALIDDATA; + } + + channel_type = avio_rl32(pb); + if (channel_type < FF_ARRAY_ELEMS(dsf_channel_layout)) + st->codec->channel_layout = dsf_channel_layout[channel_type]; + if (!st->codec->channel_layout) + avpriv_request_sample(s, "channel type %i", channel_type); + + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->channels = avio_rl32(pb); + st->codec->sample_rate = avio_rl32(pb) / 8; + + switch(avio_rl32(pb)) { + case 1: st->codec->codec_id = AV_CODEC_ID_DSD_LSBF_PLANAR; break; + case 8: st->codec->codec_id = AV_CODEC_ID_DSD_MSBF_PLANAR; break; + default: + avpriv_request_sample(s, "unknown most significant bit"); + return AVERROR_INVALIDDATA; + } + + avio_skip(pb, 8); + st->codec->block_align = avio_rl32(pb); + if (st->codec->block_align > INT_MAX / st->codec->channels) { + avpriv_request_sample(s, "block_align overflow"); + return AVERROR_INVALIDDATA; + } + st->codec->block_align *= st->codec->channels; + avio_skip(pb, 4); + + /* data chunk */ + + dsf->data_end = avio_tell(pb); + if (avio_rl32(pb) != MKTAG('d', 'a', 't', 'a')) + return AVERROR_INVALIDDATA; + dsf->data_end += avio_rl64(pb); + + return 0; +} + +static int dsf_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + DSFContext *dsf = s->priv_data; + AVIOContext *pb = s->pb; + AVStream *st = s->streams[0]; + int64_t pos = avio_tell(pb); + + if (pos >= dsf->data_end) + return AVERROR_EOF; + + pkt->stream_index = 0; + return av_get_packet(pb, pkt, FFMIN(dsf->data_end - pos, st->codec->block_align)); +} + +AVInputFormat ff_dsf_demuxer = { + .name = "dsf", + .long_name = NULL_IF_CONFIG_SMALL("DSD Stream File (DSF)"), + .priv_data_size = sizeof(DSFContext), + .read_probe = dsf_probe, + .read_header = dsf_read_header, + .read_packet = dsf_read_packet, + .flags = AVFMT_GENERIC_INDEX | AVFMT_NO_BYTE_SEEK, +}; diff --git a/libavformat/dsicin.c b/libavformat/dsicin.c index 6a7c8b9..4b5a934 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 (avio_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 9010711..f6a939a 100644 --- a/libavformat/dtsdec.c +++ b/libavformat/dtsdec.c @@ -2,24 +2,26 @@ * 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 */ #include "libavcodec/bytestream.h" +#include "libavcodec/get_bits.h" +#include "libavcodec/dca.h" #include "avformat.h" #include "rawdec.h" @@ -32,34 +34,71 @@ static int dts_probe(AVProbeData *p) { const uint8_t *buf, *bufp; uint32_t state = -1; - int markers[3] = {0}; - int sum, max; + int markers[4] = {0}; + int sum, max, i; + int64_t diff = 0; + uint8_t hdr[12 + FF_INPUT_BUFFER_PADDING_SIZE] = { 0 }; - buf = p->buf; + buf = p->buf + FFMIN(4096, p->buf_size); for(; buf < (p->buf+p->buf_size)-2; buf+=2) { + int marker, sample_blocks, sample_rate, sr_code, framesize; + GetBitContext gb; + bufp = buf; state = (state << 16) | bytestream_get_be16(&bufp); + if (buf - p->buf >= 4) + diff += FFABS(((int16_t)AV_RL16(buf)) - (int16_t)AV_RL16(buf-4)); + /* regular bitstream */ - if (state == DCA_MARKER_RAW_BE || state == DCA_MARKER_RAW_LE) - markers[0]++; + if (state == DCA_MARKER_RAW_BE) + marker = 0; + else if (state == DCA_MARKER_RAW_LE) + marker = 1; /* 14 bits big-endian bitstream */ - if (state == DCA_MARKER_14B_BE) - if ((bytestream_get_be16(&bufp) & 0xFFF0) == 0x07F0) - markers[1]++; + else if (state == DCA_MARKER_14B_BE && + (bytestream_get_be16(&bufp) & 0xFFF0) == 0x07F0) + marker = 2; /* 14 bits little-endian bitstream */ - if (state == DCA_MARKER_14B_LE) - if ((bytestream_get_be16(&bufp) & 0xF0FF) == 0xF007) - markers[2]++; + else if (state == DCA_MARKER_14B_LE && + (bytestream_get_be16(&bufp) & 0xF0FF) == 0xF007) + marker = 3; + else + continue; + + if (avpriv_dca_convert_bitstream(buf-2, 12, hdr, 12) < 0) + continue; + + init_get_bits(&gb, hdr, 96); + skip_bits_long(&gb, 39); + + sample_blocks = get_bits(&gb, 7) + 1; + if (sample_blocks < 8) + continue; + + framesize = get_bits(&gb, 14) + 1; + if (framesize < 95) + continue; + + skip_bits(&gb, 6); + sr_code = get_bits(&gb, 4); + sample_rate = avpriv_dca_sample_rates[sr_code]; + if (sample_rate == 0) + continue; + + markers[marker] ++; } - sum = markers[0] + markers[1] + markers[2]; - max = markers[1] > markers[0]; - max = markers[2] > markers[max] ? 2 : max; + sum = markers[0] + markers[1] + markers[2] + markers[3]; + max = 0; + for (i=1; i<4; i++) + if (markers[max] < markers[i]) + max = i; 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 0000000..0fd0304 --- /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 (!avio_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/dump.c b/libavformat/dump.c index 58ed654..3a7adbe 100644 --- a/libavformat/dump.c +++ b/libavformat/dump.c @@ -1,20 +1,21 @@ /* - * Various pretty-printing functions for use within Libav + * Various pretty-printing 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 */ @@ -26,6 +27,7 @@ #include "libavutil/intreadwrite.h" #include "libavutil/log.h" #include "libavutil/mathematics.h" +#include "libavutil/avstring.h" #include "libavutil/replaygain.h" #include "libavutil/stereo3d.h" @@ -76,7 +78,7 @@ void av_hex_dump_log(void *avcl, int level, const uint8_t *buf, int size) hex_dump_internal(avcl, NULL, level, buf, size); } -static void pkt_dump_internal(void *avcl, FILE *f, int level, AVPacket *pkt, +static void pkt_dump_internal(void *avcl, FILE *f, int level, const AVPacket *pkt, int dump_payload, AVRational time_base) { HEXDUMP_PRINT("stream #%d:\n", pkt->stream_index); @@ -100,13 +102,13 @@ static void pkt_dump_internal(void *avcl, FILE *f, int level, AVPacket *pkt, av_hex_dump(f, pkt->data, pkt->size); } -void av_pkt_dump2(FILE *f, AVPacket *pkt, int dump_payload, AVStream *st) +void av_pkt_dump2(FILE *f, const AVPacket *pkt, int dump_payload, const AVStream *st) { pkt_dump_internal(NULL, f, 0, pkt, dump_payload, st->time_base); } -void av_pkt_dump_log2(void *avcl, int level, AVPacket *pkt, int dump_payload, - AVStream *st) +void av_pkt_dump_log2(void *avcl, int level, const AVPacket *pkt, int dump_payload, + const AVStream *st) { pkt_dump_internal(avcl, NULL, level, pkt, dump_payload, st->time_base); } @@ -130,9 +132,22 @@ 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)) + if (strcmp("language", tag->key)) { + const char *p = tag->value; av_log(ctx, AV_LOG_INFO, - "%s %-16s: %s\n", indent, tag->key, tag->value); + "%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"); + } } } @@ -332,7 +347,7 @@ static void dump_stream_format(AVFormatContext *ic, int i, 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 */ @@ -351,7 +366,7 @@ static void dump_stream_format(AVFormatContext *ic, int i, 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); } @@ -359,6 +374,10 @@ static void dump_stream_format(AVFormatContext *ic, int i, 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) @@ -411,8 +430,9 @@ void av_dump_format(AVFormatContext *ic, int index, 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; diff --git a/libavformat/dv.c b/libavformat/dv.c index da201a3..1030d51 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 AVDVProfile* sys; /* Current DV profile. E.g.: 525/60, 625/50 */ @@ -70,27 +72,33 @@ static inline uint16_t dv_audio_12to16(uint16_t sample) return result; } -/* - * This is the dumbest implementation of all -- it simply looks at - * a fixed offset and if pack isn't there -- fails. We might want - * to have a fallback mechanism for complete search of missing packs. - */ static const uint8_t *dv_extract_pack(uint8_t *frame, enum dv_pack_type t) { int offs; + int c; - switch (t) { - case dv_audio_source: - offs = (80 * 6 + 80 * 16 * 3 + 3); - break; - case dv_audio_control: - offs = (80 * 6 + 80 * 16 * 4 + 3); - break; - case dv_video_control: - offs = (80 * 5 + 48 + 5); - break; - default: - return NULL; + for (c = 0; c < 10; c++) { + switch (t) { + case dv_audio_source: + if (c&1) offs = (80 * 6 + 80 * 16 * 0 + 3 + c*12000); + else offs = (80 * 6 + 80 * 16 * 3 + 3 + c*12000); + break; + case dv_audio_control: + if (c&1) offs = (80 * 6 + 80 * 16 * 1 + 3 + c*12000); + else offs = (80 * 6 + 80 * 16 * 4 + 3 + c*12000); + break; + case dv_video_control: + if (c&1) offs = (80 * 3 + 8 + c*12000); + else offs = (80 * 5 + 48 + 5 + c*12000); + break; + case dv_timecode: + offs = (80*1 + 3 + 3); + break; + default: + return NULL; + } + if (frame[offs] == t) + break; } return frame[offs] == t ? &frame[offs] : NULL; @@ -137,9 +145,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 +162,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 +292,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); c->vst->avg_frame_rate = av_inv_q(c->vst->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 +307,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 +366,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 +381,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 +407,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; @@ -414,10 +441,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; } @@ -431,6 +461,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; @@ -442,7 +504,7 @@ static int dv_read_header(AVFormatContext *s) state = avio_rb32(s->pb); while ((state & 0xffffff7f) != 0x1f07003f) { - if (s->pb->eof_reached) { + if (avio_feof(s->pb)) { av_log(s, AV_LOG_ERROR, "Cannot find DV header.\n"); return -1; } @@ -457,7 +519,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); @@ -474,6 +536,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; } @@ -485,13 +550,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; @@ -520,31 +586,37 @@ static int dv_read_close(AVFormatContext *s) static int dv_probe(AVProbeData *p) { - unsigned state, marker_pos = 0; + unsigned marker_pos = 0; int i; int matches = 0; + int firstmatch = 0; int secondary_matches = 0; if (p->buf_size < 5) return 0; - state = AV_RB32(p->buf); - for (i = 4; i < p->buf_size; i++) { - if ((state & 0xffffff7f) == 0x1f07003f) - matches++; - // any section header, also with seq/chan num != 0, - // should appear around every 12000 bytes, at least 10 per frame - if ((state & 0xff07ff7f) == 0x1f07003f) - secondary_matches++; - if (state == 0x003f0700 || state == 0xff3f0700) - marker_pos = i; - if (state == 0xff3f0701 && i - marker_pos == 80) - matches++; - state = (state << 8) | p->buf[i]; + for (i = 0; i < p->buf_size-4; i++) { + unsigned state = AV_RB32(p->buf+i); + if ((state & 0x0007f840) == 0x00070000) { + // any section header, also with seq/chan num != 0, + // should appear around every 12000 bytes, at least 10 per frame + if ((state & 0xff07ff7f) == 0x1f07003f) { + secondary_matches++; + if ((state & 0xffffff7f) == 0x1f07003f) { + matches++; + if (!i) + firstmatch = 1; + } + } + if (state == 0x003f0700 || state == 0xff3f0700) + marker_pos = i; + if (state == 0xff3f0701 && i - marker_pos == 80) + matches++; + } } if (matches && p->buf_size / matches < 1024 * 1024) { - if (matches > 4 || + if (matches > 4 || firstmatch || (secondary_matches >= 10 && p->buf_size / secondary_matches < 24000)) // not max to avoid dv in mov to match diff --git a/libavformat/dv.h b/libavformat/dv.h index e8b2d37..160c6ab 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 a33973f..18fdf9f 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 AVDVProfile* 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] = { @@ -67,8 +72,16 @@ static const int dv_aaux_packs_dist[12][9] = { { 0x50, 0x51, 0x52, 0x53, 0xff, 0xff, 0xff, 0xff, 0xff }, }; -static int dv_audio_frame_size(const AVDVProfile* sys, int frame) +static int dv_audio_frame_size(const AVDVProfile* sys, int frame, int sample_rate) { + if ((sys->time_base.den == 25 || sys->time_base.den == 50) && sys->time_base.num == 1) { + if (sample_rate == 32000) return 1280; + else if (sample_rate == 44100) return 1764; + else return 1920; + } + + av_assert0(sample_rate == 48000); + return sys->audio_samples_dist[frame % (sizeof(sys->audio_samples_dist) / sizeof(sys->audio_samples_dist[0]))]; } @@ -77,41 +90,29 @@ 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; + int audio_type = 0; + int channel; 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); + channel = va_arg(ap, int); + if (c->ast[channel]->codec->sample_rate == 44100) { + audio_type = 1; + } else if (c->ast[channel]->codec->sample_rate == 32000) + audio_type = 2; buf[1] = (1 << 7) | /* locked mode -- SMPTE only supports locked mode */ (1 << 6) | /* reserved -- always 1 */ - (dv_audio_frame_size(c->sys, c->frames) - - c->sys->audio_min_samples[0]); + (dv_audio_frame_size(c->sys, c->frames, c->ast[channel]->codec->sample_rate) - + c->sys->audio_min_samples[audio_type]); /* # of samples */ buf[2] = (0 << 7) | /* multi-stereo */ (0 << 5) | /* #of audio channels per block: 0 -- 1 channel */ @@ -123,8 +124,9 @@ static int dv_write_pack(enum dv_pack_type pack_id, DVMuxContext *c, uint8_t* bu (c->sys->n_difchan & 2); /* definition: 0 -- 25Mbps, 2 -- 50Mbps */ buf[4] = (1 << 7) | /* emphasis: 1 -- off */ (0 << 6) | /* emphasis time constant: 0 -- reserved */ - (0 << 3) | /* frequency: 0 -- 48kHz, 1 -- 44,1kHz, 2 -- 32kHz */ + (audio_type << 3) | /* frequency: 0 -- 48kHz, 1 -- 44,1kHz, 2 -- 32kHz */ 0; /* quantization: 0 -- 16bit linear, 1 -- 12bit nonlinear */ + va_end(ap); break; case dv_audio_control: @@ -184,12 +186,12 @@ static int dv_write_pack(enum dv_pack_type pack_id, DVMuxContext *c, uint8_t* bu static void dv_inject_audio(DVMuxContext *c, int channel, uint8_t* frame_ptr) { int i, j, d, of, size; - size = 4 * dv_audio_frame_size(c->sys, c->frames); + size = 4 * dv_audio_frame_size(c->sys, c->frames, c->ast[channel]->codec->sample_rate); frame_ptr += channel * c->sys->difseg_size * 150 * 80; for (i = 0; i < c->sys->difseg_size; i++) { frame_ptr += 6 * 80; /* skip DIF segment header */ for (j = 0; j < 9; j++) { - dv_write_pack(dv_aaux_packs_dist[i][j], c, &frame_ptr[3], i >= c->sys->difseg_size/2); + dv_write_pack(dv_aaux_packs_dist[i][j], c, &frame_ptr[3], channel, i >= c->sys->difseg_size/2); for (d = 8; d < 80; d+=2) { of = c->sys->audio_shuffle[i][j] + (d - 8)/2 * c->sys->audio_stride; if (of*2 >= size) @@ -242,7 +244,6 @@ static int dv_assemble_frame(DVMuxContext *c, AVStream* st, int i, reqasize; *frame = &c->frame_buf[0]; - reqasize = 4 * dv_audio_frame_size(c->sys, c->frames); switch (st->codec->codec_type) { case AVMEDIA_TYPE_VIDEO: @@ -261,6 +262,8 @@ static int dv_assemble_frame(DVMuxContext *c, AVStream* st, av_log(st->codec, AV_LOG_ERROR, "Can't process DV frame #%d. Insufficient video data or severe sync problem.\n", c->frames); av_fifo_generic_write(c->audio_data[i], data, data_size, NULL); + reqasize = 4 * dv_audio_frame_size(c->sys, c->frames, st->codec->sample_rate); + /* Let us see if we've got enough audio for one DV frame. */ c->has_audio |= ((reqasize <= av_fifo_size(c->audio_data[i])) << i); @@ -275,6 +278,7 @@ static int dv_assemble_frame(DVMuxContext *c, AVStream* st, c->has_audio = 0; for (i=0; i < c->n_ast; i++) { dv_inject_audio(c, i, *frame); + reqasize = 4 * dv_audio_frame_size(c->sys, c->frames, c->ast[i]->codec->sample_rate); av_fifo_drain(c->audio_data[i], reqasize); c->has_audio |= ((reqasize <= av_fifo_size(c->audio_data[i])) << i); } @@ -323,15 +327,27 @@ static DVMuxContext* dv_init_mux(AVFormatContext* s) if (!vst || vst->codec->codec_id != AV_CODEC_ID_DVVIDEO) goto bail_out; for (i=0; i<c->n_ast; i++) { - if (c->ast[i] && (c->ast[i]->codec->codec_id != AV_CODEC_ID_PCM_S16LE || - c->ast[i]->codec->sample_rate != 48000 || - c->ast[i]->codec->channels != 2)) - goto bail_out; + if (c->ast[i]) { + if(c->ast[i]->codec->codec_id != AV_CODEC_ID_PCM_S16LE || + c->ast[i]->codec->channels != 2) + goto bail_out; + if (c->ast[i]->codec->sample_rate != 48000 && + c->ast[i]->codec->sample_rate != 44100 && + c->ast[i]->codec->sample_rate != 32000 ) + goto bail_out; + } } c->sys = av_dv_codec_profile(vst->codec->width, vst->codec->height, vst->codec->pix_fmt); if (!c->sys) goto bail_out; + if ((c->sys->time_base.den != 25 && c->sys->time_base.den != 50) || c->sys->time_base.num != 1) { + if (c->ast[0] && c->ast[0]->codec->sample_rate != 48000) + goto bail_out; + if (c->ast[1] && c->ast[1]->codec->sample_rate != 48000) + goto bail_out; + } + if ((c->n_ast > 1) && (c->sys->n_difchan < 2)) { /* only 1 stereo pair is allowed in 25Mbps mode */ goto bail_out; @@ -345,10 +361,10 @@ static DVMuxContext* dv_init_mux(AVFormatContext* s) c->start_time = ff_iso8601_to_unix_time(t->value); for (i=0; i < c->n_ast; i++) { - if (c->ast[i] && !(c->audio_data[i]=av_fifo_alloc(100*MAX_AUDIO_FRAME_SIZE))) { + if (c->ast[i] && !(c->audio_data[i]=av_fifo_alloc_array(100, MAX_AUDIO_FRAME_SIZE))) { while (i > 0) { i--; - av_fifo_free(c->audio_data[i]); + av_fifo_freep(&c->audio_data[i]); } goto bail_out; } @@ -364,19 +380,35 @@ static void dv_delete_mux(DVMuxContext *c) { int i; for (i=0; i < c->n_ast; i++) - av_fifo_free(c->audio_data[i]); + av_fifo_freep(&c->audio_data[i]); } 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" - " video: 25fps or 29.97fps, audio: 2ch/48kHz/PCM\n" + " video: 25fps or 29.97fps, audio: 2ch/48|44|32kHz/PCM\n" " (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 78e7290..6ad1c9f 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 */ @@ -67,12 +67,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); @@ -92,7 +92,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')){ @@ -105,14 +105,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 && !avio_feof(pb)){ tag = avio_rl32(pb); fsize = avio_rl32(pb); if(tag == MKTAG('d', 'a', 't', 'a')) break; @@ -170,8 +170,11 @@ 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){ - avio_read(s->pb, buf, 4); + while(!avio_feof(s->pb) && c->frames){ + if ((ret = avio_read(s->pb, buf, 4)) != 4) { + av_log(s, AV_LOG_ERROR, "failed reading chunk type\n"); + return ret < 0 ? ret : AVERROR_INVALIDDATA; + } switch(AV_RL32(buf)){ case MKTAG('N', 'U', 'L', 'L'): if(av_new_packet(pkt, 4 + pal_size) < 0) @@ -189,12 +192,15 @@ static int dxa_read_packet(AVFormatContext *s, AVPacket *pkt) avio_read(s->pb, pal + 4, 768); break; case MKTAG('F', 'R', 'A', 'M'): - avio_read(s->pb, buf + 4, DXA_EXTRA_SIZE - 4); + if ((ret = avio_read(s->pb, buf + 4, DXA_EXTRA_SIZE - 4)) != DXA_EXTRA_SIZE - 4) { + av_log(s, AV_LOG_ERROR, "failed reading dxa_extra\n"); + return ret < 0 ? ret : AVERROR_INVALIDDATA; + } size = AV_RB32(buf + 5); if(size > 0xFFFFFF){ av_log(s, AV_LOG_ERROR, "Frame size is too big: %"PRIu32"\n", size); - return -1; + return AVERROR_INVALIDDATA; } if(av_new_packet(pkt, size + DXA_EXTRA_SIZE + pal_size) < 0) return AVERROR(ENOMEM); @@ -212,10 +218,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 b56ba1c..57e5145 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 7c1fabe..d6a396b 100644 --- a/libavformat/electronicarts.c +++ b/libavformat/electronicarts.c @@ -1,21 +1,21 @@ /* Electronic Arts Multimedia File Demuxer - * Copyright (c) 2004 The ffmpeg Project + * 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 */ @@ -68,6 +68,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; @@ -108,7 +109,7 @@ static int process_audio_header_elements(AVFormatContext *s) ea->sample_rate = -1; ea->num_channels = 1; - while (!pb->eof_reached && in_header) { + while (!avio_feof(pb) && in_header) { int in_subheader; uint8_t byte; byte = avio_r8(pb); @@ -117,7 +118,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 (!avio_feof(pb) && in_subheader) { uint8_t subbyte; subbyte = avio_r8(pb); @@ -211,8 +212,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) { @@ -220,7 +220,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; @@ -229,15 +236,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; } @@ -278,9 +284,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); } } @@ -306,15 +312,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) @@ -339,20 +353,25 @@ static int process_ea_header(AVFormatContext *s) int i; for (i = 0; i < 5 && (!ea->audio_codec || !ea->video_codec); i++) { - unsigned int startpos = avio_tell(pb); + uint64_t startpos = avio_tell(pb); int err = 0; blockid = avio_rl32(pb); size = avio_rl32(pb); if (i == 0) - ea->big_endian = size > 0x000FFFFF; + ea->big_endian = size > av_bswap32(size); if (ea->big_endian) size = av_bswap32(size); + if (size < 8) { + av_log(s, AV_LOG_ERROR, "chunk size too small\n"); + return AVERROR_INVALIDDATA; + } + 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); @@ -364,7 +383,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); @@ -380,7 +399,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: @@ -409,7 +427,7 @@ static int process_ea_header(AVFormatContext *s) break; case MVhd_TAG: - process_video_header_vp6(s); + err = process_video_header_vp6(s); break; } @@ -428,6 +446,8 @@ static int process_ea_header(AVFormatContext *s) static int ea_probe(AVProbeData *p) { + unsigned big_endian, size; + switch (AV_RL32(&p->buf[0])) { case ISNh_TAG: case SCHl_TAG: @@ -442,7 +462,11 @@ static int ea_probe(AVProbeData *p) default: return 0; } - if (AV_RL32(&p->buf[4]) > 0xfffff && AV_RB32(&p->buf[4]) > 0xfffff) + size = AV_RL32(&p->buf[4]); + big_endian = size > 0x000FFFFF; + if (big_endian) + size = av_bswap32(size); + if (size > 0xfffff || size < 8) return 0; return AVPROBE_SCORE_MAX; @@ -453,7 +477,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) { @@ -464,12 +488,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) { @@ -519,11 +548,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) @@ -551,6 +581,13 @@ 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; + } + if (!chunk_size) continue; @@ -630,9 +667,15 @@ get_video_packet: if (!chunk_size) continue; - 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; @@ -644,6 +687,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 0000000..c737892 --- /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 new file mode 100644 index 0000000..b392b8d --- /dev/null +++ b/libavformat/ffm.h @@ -0,0 +1,60 @@ +/* + * FFM (ffserver live feed) common header + * Copyright (c) 2001 Fabrice Bellard + * + * 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_FFM_H +#define AVFORMAT_FFM_H + +#include <stdint.h> +#include "avformat.h" +#include "avio.h" + +/* The FFM file is made of blocks of fixed size */ +#define FFM_HEADER_SIZE 14 +#define FFM_PACKET_SIZE 4096 +#define PACKET_ID 0x666d + +/* each packet contains frames (which can span several packets */ +#define FRAME_HEADER_SIZE 16 +#define FLAG_KEY_FRAME 0x01 +#define FLAG_DTS 0x02 + +enum { + READ_HEADER, + READ_DATA, +}; + +typedef struct FFMContext { + /* only reading mode */ + int64_t write_index, file_size; + int read_state; + uint8_t header[FRAME_HEADER_SIZE+4]; + + /* read and write */ + int first_packet; /* true if first packet, needed to set the discontinuity tag */ + int packet_size; + int frame_offset; + 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 new file mode 100644 index 0000000..f8fee2f --- /dev/null +++ b/libavformat/ffmdec.c @@ -0,0 +1,635 @@ +/* + * FFM (ffserver live feed) demuxer + * Copyright (c) 2001 Fabrice Bellard + * + * 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 <stdint.h> + +#include "libavutil/intreadwrite.h" +#include "libavutil/intfloat.h" +#include "avformat.h" +#include "internal.h" +#include "ffm.h" +#include "avio_internal.h" + +static int ffm_is_avail_data(AVFormatContext *s, int size) +{ + FFMContext *ffm = s->priv_data; + int64_t pos, avail_size; + int len; + + len = ffm->packet_end - ffm->packet_ptr; + if (size <= len) + return 1; + pos = avio_tell(s->pb); + if (!ffm->write_index) { + if (pos == ffm->file_size) + return AVERROR_EOF; + avail_size = ffm->file_size - pos; + } else { + if (pos == ffm->write_index) { + /* exactly at the end of stream */ + return AVERROR(EAGAIN); + } else if (pos < ffm->write_index) { + avail_size = ffm->write_index - pos; + } else { + avail_size = (ffm->file_size - pos) + (ffm->write_index - FFM_PACKET_SIZE); + } + } + avail_size = (avail_size / ffm->packet_size) * (ffm->packet_size - FFM_HEADER_SIZE) + len; + if (size <= avail_size) + return 1; + else + return AVERROR(EAGAIN); +} + +static int ffm_resync(AVFormatContext *s, int state) +{ + av_log(s, AV_LOG_ERROR, "resyncing\n"); + while (state != PACKET_ID) { + if (avio_feof(s->pb)) { + av_log(s, AV_LOG_ERROR, "cannot find FFM syncword\n"); + return -1; + } + state = (state << 8) | avio_r8(s->pb); + } + return 0; +} + +/* first is true if we read the frame header */ +static int ffm_read_data(AVFormatContext *s, + uint8_t *buf, int size, int header) +{ + FFMContext *ffm = s->priv_data; + AVIOContext *pb = s->pb; + int len, fill_size, size1, frame_offset, id; + + size1 = size; + while (size > 0) { + redo: + len = ffm->packet_end - ffm->packet_ptr; + if (len < 0) + return -1; + if (len > size) + len = size; + if (len == 0) { + 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) + return -1; + fill_size = avio_rb16(pb); + ffm->dts = avio_rb64(pb); + frame_offset = avio_rb16(pb); + avio_read(pb, ffm->packet, ffm->packet_size - FFM_HEADER_SIZE); + ffm->packet_end = ffm->packet + (ffm->packet_size - FFM_HEADER_SIZE - fill_size); + if (ffm->packet_end < ffm->packet || frame_offset < 0) + return -1; + /* if first packet or resynchronization packet, we must + handle it specifically */ + 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 * 3LL) { + avio_seek(pb, -ffm->packet_size * 2LL, SEEK_CUR); + goto retry_read; + } + /* This is bad, we cannot find a valid frame header */ + return 0; + } + ffm->first_packet = 0; + if ((frame_offset & 0x7fff) < FFM_HEADER_SIZE) + return -1; + ffm->packet_ptr = ffm->packet + (frame_offset & 0x7fff) - FFM_HEADER_SIZE; + if (!header) + break; + } else { + ffm->packet_ptr = ffm->packet; + } + goto redo; + } + memcpy(buf, ffm->packet_ptr, len); + buf += len; + ffm->packet_ptr += len; + size -= len; + header = 0; + } + return size1 - size; +} + +/* ensure that acutal seeking happens between FFM_PACKET_SIZE + and file_size - FFM_PACKET_SIZE */ +static int64_t ffm_seek1(AVFormatContext *s, int64_t pos1) +{ + FFMContext *ffm = s->priv_data; + AVIOContext *pb = s->pb; + int64_t pos; + + pos = FFMIN(pos1, ffm->file_size - FFM_PACKET_SIZE); + pos = FFMAX(pos, FFM_PACKET_SIZE); + av_dlog(s, "seek to %"PRIx64" -> %"PRIx64"\n", pos1, pos); + return avio_seek(pb, pos, SEEK_SET); +} + +static int64_t get_dts(AVFormatContext *s, int64_t pos) +{ + AVIOContext *pb = s->pb; + int64_t dts; + + ffm_seek1(s, pos); + avio_skip(pb, 4); + dts = avio_rb64(pb); + av_dlog(s, "dts=%0.6f\n", dts / 1000000.0); + return dts; +} + +static void adjust_write_index(AVFormatContext *s) +{ + FFMContext *ffm = s->priv_data; + AVIOContext *pb = s->pb; + int64_t pts; + //int64_t orig_write_index = ffm->write_index; + int64_t pos_min, pos_max; + int64_t pts_start; + int64_t ptr = avio_tell(pb); + + + pos_min = 0; + pos_max = ffm->file_size - 2 * FFM_PACKET_SIZE; + + pts_start = get_dts(s, pos_min); + + pts = get_dts(s, pos_max); + + if (pts - 100000 > pts_start) + goto end; + + ffm->write_index = FFM_PACKET_SIZE; + + pts_start = get_dts(s, pos_min); + + pts = get_dts(s, pos_max); + + if (pts - 100000 <= pts_start) { + while (1) { + int64_t newpos; + int64_t newpts; + + newpos = ((pos_max + pos_min) / (2 * FFM_PACKET_SIZE)) * FFM_PACKET_SIZE; + + if (newpos == pos_min) + break; + + newpts = get_dts(s, newpos); + + if (newpts - 100000 <= pts) { + pos_max = newpos; + pts = newpts; + } else { + pos_min = newpos; + } + } + ffm->write_index += pos_max; + } + + end: + avio_seek(pb, ptr, SEEK_SET); +} + + +static int ffm_close(AVFormatContext *s) +{ + int i; + + for (i = 0; i < s->nb_streams; i++) + av_freep(&s->streams[i]->codec->rc_eq); + + 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(!avio_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_get_extradata(codec, pb, avio_rb32(pb)) < 0) + return AVERROR(ENOMEM); + } + 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) +{ + FFMContext *ffm = s->priv_data; + AVStream *st; + AVIOContext *pb = s->pb; + AVCodecContext *codec; + int i, nb_streams; + uint32_t tag; + + /* 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); + 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; + } + + nb_streams = avio_rb32(pb); + avio_rb32(pb); /* total bitrate */ + /* read each stream */ + for(i=0;i<nb_streams;i++) { + char rc_eq_buf[128]; + + 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_type */ + codec->bit_rate = avio_rb32(pb); + codec->flags = avio_rb32(pb); + codec->flags2 = avio_rb32(pb); + codec->debug = avio_rb32(pb); + /* specific info */ + switch(codec->codec_type) { + case AVMEDIA_TYPE_VIDEO: + 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 AVMEDIA_TYPE_AUDIO: + codec->sample_rate = avio_rb32(pb); + codec->channels = avio_rl16(pb); + codec->frame_size = avio_rl16(pb); + break; + default: + goto fail; + } + if (codec->flags & CODEC_FLAG_GLOBAL_HEADER) { + if (ff_get_extradata(codec, pb, avio_rb32(pb)) < 0) + return AVERROR(ENOMEM); + } + } + + /* 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; +} + +/* return < 0 if eof */ +static int ffm_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + int size; + FFMContext *ffm = s->priv_data; + int duration, ret; + + switch(ffm->read_state) { + case READ_HEADER: + if ((ret = ffm_is_avail_data(s, FRAME_HEADER_SIZE+4)) < 0) + return ret; + + av_dlog(s, "pos=%08"PRIx64" spos=%"PRIx64", write_index=%"PRIx64" size=%"PRIx64"\n", + avio_tell(s->pb), s->pb->pos, ffm->write_index, ffm->file_size); + if (ffm_read_data(s, ffm->header, FRAME_HEADER_SIZE, 1) != + FRAME_HEADER_SIZE) + return -1; + if (ffm->header[1] & FLAG_DTS) + if (ffm_read_data(s, ffm->header+16, 4, 1) != 4) + return -1; + ffm->read_state = READ_DATA; + /* fall through */ + case READ_DATA: + size = AV_RB24(ffm->header + 2); + if ((ret = ffm_is_avail_data(s, size)) < 0) + return ret; + + duration = AV_RB24(ffm->header + 5); + + 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); + av_free_packet(pkt); + ffm->read_state = READ_HEADER; + return -1; + } + pkt->pos = avio_tell(s->pb); + if (ffm->header[1] & FLAG_KEY_FRAME) + pkt->flags |= AV_PKT_FLAG_KEY; + + ffm->read_state = READ_HEADER; + if (ffm_read_data(s, pkt->data, size, 0) != size) { + /* bad case: desynchronized packet. we cancel all the packet loading */ + av_free_packet(pkt); + return -1; + } + pkt->pts = AV_RB64(ffm->header+8); + if (ffm->header[1] & FLAG_DTS) + pkt->dts = pkt->pts - AV_RB32(ffm->header+16); + else + pkt->dts = pkt->pts; + pkt->duration = duration; + break; + } + return 0; +} + +/* seek to a given time in the file. The file read pointer is + positioned at or before pts. XXX: the following code is quite + approximative */ +static int ffm_seek(AVFormatContext *s, int stream_index, int64_t wanted_pts, int flags) +{ + FFMContext *ffm = s->priv_data; + int64_t pos_min, pos_max, pos; + int64_t pts_min, pts_max, pts; + double pos1; + + av_dlog(s, "wanted_pts=%0.6f\n", wanted_pts / 1000000.0); + /* find the position using linear interpolation (better than + dichotomy in typical cases) */ + 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); + pos = (((int64_t)pos1) / FFM_PACKET_SIZE) * FFM_PACKET_SIZE; + if (pos <= pos_min) + pos = pos_min; + else if (pos >= pos_max) + pos = pos_max; + pts = get_dts(s, pos); + /* check if we are lucky */ + if (pts == wanted_pts) { + goto found; + } else if (pts > wanted_pts) { + pos_max = pos - FFM_PACKET_SIZE; + } else { + pos_min = pos + FFM_PACKET_SIZE; + } + } + pos = (flags & AVSEEK_FLAG_BACKWARD) ? pos_min : pos_max; + + found: + if (ffm_seek1(s, pos) < 0) + return -1; + + /* reset read state */ + ffm->read_state = READ_HEADER; + ffm->packet_ptr = ffm->packet; + ffm->packet_end = ffm->packet; + ffm->first_packet = 1; + + return 0; +} + +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] == '2')) + return AVPROBE_SCORE_MAX + 1; + return 0; +} + +AVInputFormat ff_ffm_demuxer = { + .name = "ffm", + .long_name = NULL_IF_CONFIG_SMALL("FFM (FFserver live feed)"), + .priv_data_size = sizeof(FFMContext), + .read_probe = ffm_probe, + .read_header = ffm_read_header, + .read_packet = ffm_read_packet, + .read_close = ffm_close, + .read_seek = ffm_seek, +}; diff --git a/libavformat/ffmenc.c b/libavformat/ffmenc.c new file mode 100644 index 0000000..eb809eb --- /dev/null +++ b/libavformat/ffmenc.c @@ -0,0 +1,281 @@ +/* + * FFM (ffserver live feed) muxer + * Copyright (c) 2001 Fabrice Bellard + * + * 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 "libavutil/avassert.h" +#include "libavutil/parseutils.h" +#include "avformat.h" +#include "internal.h" +#include "ffm.h" + +static void flush_packet(AVFormatContext *s) +{ + FFMContext *ffm = s->priv_data; + int fill_size, h; + AVIOContext *pb = s->pb; + + fill_size = ffm->packet_end - ffm->packet_ptr; + memset(ffm->packet_ptr, 0, fill_size); + + av_assert1(avio_tell(pb) % ffm->packet_size == 0); + + /* put header */ + avio_wb16(pb, PACKET_ID); + avio_wb16(pb, fill_size); + avio_wb64(pb, ffm->dts); + h = ffm->frame_offset; + if (ffm->first_packet) + h |= 0x8000; + avio_wb16(pb, h); + avio_write(pb, ffm->packet, ffm->packet_end - ffm->packet); + avio_flush(pb); + + /* prepare next packet */ + ffm->frame_offset = 0; /* no key frame */ + ffm->packet_ptr = ffm->packet; + ffm->first_packet = 0; +} + +/* 'first' is true if first data of a frame */ +static void ffm_write_data(AVFormatContext *s, + const uint8_t *buf, int size, + int64_t dts, int header) +{ + FFMContext *ffm = s->priv_data; + int len; + + if (header && ffm->frame_offset == 0) { + ffm->frame_offset = ffm->packet_ptr - ffm->packet + FFM_HEADER_SIZE; + ffm->dts = dts; + } + + /* write as many packets as needed */ + while (size > 0) { + len = ffm->packet_end - ffm->packet_ptr; + if (len > size) + len = size; + memcpy(ffm->packet_ptr, buf, len); + + ffm->packet_ptr += len; + buf += len; + size -= len; + if (ffm->packet_ptr >= ffm->packet_end) + flush_packet(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', '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++) { + st = s->streams[i]; + bit_rate += st->codec->bit_rate; + } + 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 */ + avio_wb32(pb, codec->codec_id); + avio_w8(pb, codec->codec_type); + avio_wb32(pb, codec->bit_rate); + 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: + avio_wb32(pb, codec->time_base.num); + avio_wb32(pb, codec->time_base.den); + avio_wb16(pb, codec->width); + avio_wb16(pb, codec->height); + avio_wb16(pb, codec->gop_size); + avio_wb32(pb, codec->pix_fmt); + avio_w8(pb, codec->qmin); + avio_w8(pb, codec->qmax); + avio_w8(pb, codec->max_qdiff); + avio_wb16(pb, (int) (codec->qcompress * 10000.0)); + avio_wb16(pb, (int) (codec->qblur * 10000.0)); + avio_wb32(pb, codec->bit_rate_tolerance); + avio_put_str(pb, codec->rc_eq ? codec->rc_eq : "tex^qComp"); + avio_wb32(pb, codec->rc_max_rate); + avio_wb32(pb, codec->rc_min_rate); + avio_wb32(pb, codec->rc_buffer_size); + avio_wb64(pb, av_double2int(codec->i_quant_factor)); + avio_wb64(pb, av_double2int(codec->b_quant_factor)); + avio_wb64(pb, av_double2int(codec->i_quant_offset)); + avio_wb64(pb, av_double2int(codec->b_quant_offset)); + avio_wb32(pb, codec->dct_algo); + avio_wb32(pb, codec->strict_std_compliance); + avio_wb32(pb, codec->max_b_frames); + avio_wb32(pb, codec->mpeg_quant); + avio_wb32(pb, codec->intra_dc_precision); + avio_wb32(pb, codec->me_method); + avio_wb32(pb, codec->mb_decision); + avio_wb32(pb, codec->nsse_weight); + avio_wb32(pb, codec->frame_skip_cmp); + avio_wb64(pb, av_double2int(codec->rc_buffer_aggressivity)); + avio_wb32(pb, codec->codec_tag); + avio_w8(pb, codec->thread_count); + avio_wb32(pb, codec->coder_type); + avio_wb32(pb, codec->me_cmp); + avio_wb32(pb, codec->me_subpel_quality); + avio_wb32(pb, codec->me_range); + avio_wb32(pb, codec->keyint_min); + avio_wb32(pb, codec->scenechange_threshold); + avio_wb32(pb, codec->b_frame_strategy); + avio_wb64(pb, av_double2int(codec->qcompress)); + 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; + } + } + pb = s->pb; + + avio_wb64(pb, 0); // end of header + + /* flush until end of block reached */ + while ((avio_tell(pb) % ffm->packet_size) != 0) + avio_w8(pb, 0); + + avio_flush(pb); + + /* init packet mux */ + ffm->packet_ptr = ffm->packet; + ffm->packet_end = ffm->packet + ffm->packet_size - FFM_HEADER_SIZE; + av_assert0(ffm->packet_end >= ffm->packet); + ffm->frame_offset = 0; + ffm->dts = 0; + ffm->first_packet = 1; + + return 0; +} + +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 = ffm->start_time + pkt->dts; + /* packet size & key_frame */ + header[0] = pkt->stream_index; + header[1] = 0; + if (pkt->flags & AV_PKT_FLAG_KEY) + header[1] |= FLAG_KEY_FRAME; + AV_WB24(header+2, pkt->size); + AV_WB24(header+5, pkt->duration); + 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); + header_size += 4; + } + ffm_write_data(s, header, header_size, dts, 1); + ffm_write_data(s, pkt->data, pkt->size, dts, 0); + + return 0; +} + +static int ffm_write_trailer(AVFormatContext *s) +{ + FFMContext *ffm = s->priv_data; + + /* flush packets */ + if (ffm->packet_ptr > ffm->packet) + flush_packet(s); + + return 0; +} + +AVOutputFormat ff_ffm_muxer = { + .name = "ffm", + .long_name = NULL_IF_CONFIG_SMALL("FFM (FFserver live feed)"), + .extensions = "ffm", + .priv_data_size = sizeof(FFMContext), + .audio_codec = AV_CODEC_ID_MP2, + .video_codec = AV_CODEC_ID_MPEG1VIDEO, + .write_header = ffm_write_header, + .write_packet = ffm_write_packet, + .write_trailer = ffm_write_trailer, + .flags = AVFMT_TS_NEGATIVE, +}; diff --git a/libavformat/ffmeta.h b/libavformat/ffmeta.h index a5380ca..ae8778d 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 7dbf7ed..e4e9dda 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 (!avio_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(!avio_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 19fe6c9..a9adbb1 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 2837e9f..6511328 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 }, + { "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,14 +104,30 @@ static int file_get_handle(URLContext *h) static int file_check(URLContext *h, int mask) { + int ret = 0; + const char *filename = h->filename; + av_strstart(filename, "file:", &filename); + + { +#if HAVE_ACCESS && defined(R_OK) + if (access(filename, F_OK) < 0) + return AVERROR(errno); + if (mask&AVIO_FLAG_READ) + if (access(filename, R_OK) >= 0) + ret |= AVIO_FLAG_READ; + if (mask&AVIO_FLAG_WRITE) + if (access(filename, W_OK) >= 0) + ret |= AVIO_FLAG_WRITE; +#else struct stat st; - int ret = stat(h->filename, &st); + ret = stat(filename, &st); if (ret < 0) return AVERROR(errno); 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 +138,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 +160,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 +174,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 +237,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 3fa6842..abc5f66 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 (avio_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 8d1d2d8..7c9bc6d 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 */ @@ -65,7 +65,7 @@ static int write_trailer(AVFormatContext *s) avio_wb16(pb, st->codec->height); avio_wb16(pb, 0); // leading // TODO: should be avg_frame_rate - avio_wb16(pb, 1/av_q2d(st->time_base)); + avio_wb16(pb, st->time_base.den / st->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 69d2724..5f2026d 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) { @@ -106,10 +107,10 @@ int ff_flac_parse_picture(AVFormatContext *s, uint8_t *buf, int buf_size) ret = AVERROR_INVALIDDATA; goto fail; } - if (!(data = av_buffer_alloc(len))) { - ret = AVERROR(ENOMEM); - goto fail; + if (!(data = av_buffer_alloc(len + FF_INPUT_BUFFER_PADDING_SIZE))) { + RETURN_ERROR(AVERROR(ENOMEM)); } + memset(data->data + len, 0, FF_INPUT_BUFFER_PADDING_SIZE); if (avio_read(pb, data->data, len) != len) { av_log(s, AV_LOG_ERROR, "Error reading attached picture data.\n"); if (s->error_recognition & AV_EF_EXPLODE) @@ -119,8 +120,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 c700582..4374b6f 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 e044fd0..c291393 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 */ @@ -39,7 +39,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 */ @@ -49,7 +49,7 @@ static int flac_read_header(AVFormatContext *s) } /* process metadata blocks */ - while (!s->pb->eof_reached && !metadata_last) { + while (!avio_feof(s->pb) && !metadata_last) { avio_read(s->pb, header, 4); flac_parse_block_header(header, &metadata_last, &metadata_type, &metadata_size); @@ -64,8 +64,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 */ @@ -79,12 +78,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; @@ -106,24 +103,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); @@ -134,8 +132,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) { @@ -170,6 +167,10 @@ static int flac_read_header(AVFormatContext *s) return ret; return 0; + +fail: + av_free(buffer); + return ret; } static int flac_probe(AVProbeData *p) @@ -179,12 +180,61 @@ static int flac_probe(AVProbeData *p) return AVPROBE_SCORE_EXTENSION; } +static av_unused int64_t flac_read_timestamp(AVFormatContext *s, int stream_index, + int64_t *ppos, int64_t pos_limit) +{ + AVPacket pkt, out_pkt; + AVStream *st = s->streams[stream_index]; + AVCodecParserContext *parser; + int ret; + int64_t pts = AV_NOPTS_VALUE; + + if (avio_seek(s->pb, *ppos, SEEK_SET) < 0) + return AV_NOPTS_VALUE; + + av_init_packet(&pkt); + parser = av_parser_init(st->codec->codec_id); + if (!parser){ + return AV_NOPTS_VALUE; + } + parser->flags |= PARSER_FLAG_USE_CODEC_TS; + + for (;;){ + ret = ff_raw_read_partial_packet(s, &pkt); + if (ret < 0){ + if (ret == AVERROR(EAGAIN)) + continue; + else + break; + } + av_init_packet(&out_pkt); + ret = av_parser_parse2(parser, st->codec, + &out_pkt.data, &out_pkt.size, pkt.data, pkt.size, + pkt.pts, pkt.dts, *ppos); + + av_free_packet(&pkt); + if (out_pkt.size){ + int size = out_pkt.size; + if (parser->pts != AV_NOPTS_VALUE){ + // seeking may not have started from beginning of a frame + // calculate frame start position from next frame backwards + *ppos = parser->next_frame_offset - size; + pts = parser->pts; + break; + } + } + } + av_parser_close(parser); + return pts; +} + AVInputFormat ff_flac_demuxer = { .name = "flac", .long_name = NULL_IF_CONFIG_SMALL("raw FLAC"), .read_probe = flac_probe, .read_header = flac_read_header, .read_packet = ff_raw_read_partial_packet, + .read_timestamp = flac_read_timestamp, .flags = AVFMT_GENERIC_INDEX, .extensions = "flac", .raw_codec_id = AV_CODEC_ID_FLAC, diff --git a/libavformat/flacenc.c b/libavformat/flacenc.c index a686826..b3695a2 100644 --- a/libavformat/flacenc.c +++ b/libavformat/flacenc.c @@ -2,20 +2,20 @@ * 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 */ @@ -23,6 +23,7 @@ #include "libavutil/opt.h" #include "libavcodec/flac.h" #include "avformat.h" +#include "avio_internal.h" #include "flacenc.h" #include "vorbiscomment.h" #include "libavcodec/bytestream.h" @@ -41,17 +42,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; uint8_t *p, *p0; @@ -77,12 +75,28 @@ static int flac_write_block_comment(AVIOContext *pb, AVDictionary **m, static int flac_write_header(struct AVFormatContext *s) { int ret; + int padding = s->metadata_header_padding; AVCodecContext *codec = s->streams[0]->codec; FlacMuxerContext *c = s->priv_data; if (!c->write_header) return 0; + 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); + } + + if (padding < 0) + padding = 8192; + /* The FLAC specification states that 24 bits are used to represent the + * size of a metadata block so we must clip this value to 2^24-1. */ + padding = av_clip_c(padding, 0, 16777215); + ret = ff_flac_write_header(s->pb, codec->extradata, codec->extradata_size, 0); if (ret) @@ -105,7 +119,7 @@ static int flac_write_header(struct AVFormatContext *s) } } - ret = flac_write_block_comment(s->pb, &s->metadata, 0, + ret = flac_write_block_comment(s->pb, &s->metadata, !padding, s->flags & AVFMT_FLAG_BITEXACT); if (ret) return ret; @@ -113,8 +127,9 @@ static int flac_write_header(struct AVFormatContext *s) /* The command line flac encoder defaults to placing a seekpoint * every 10s. So one might add padding to allow that later * but there seems to be no simple way to get the duration here. - * So let's try the flac default of 8192 bytes */ - flac_write_block_padding(s->pb, 8192, 1); + * So just add the amount requested by the user. */ + if (padding) + flac_write_block_padding(s->pb, padding, 1); return ret; } diff --git a/libavformat/flacenc.h b/libavformat/flacenc.h index 54dd833..d5d53a5 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 4f9bb20..61833cc 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 2eb6f05..f5e9e84 100644 --- a/libavformat/flic.c +++ b/libavformat/flic.c @@ -1,21 +1,21 @@ /* * FLI/FLC Animation File Demuxer - * Copyright (c) 2003 The ffmpeg 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 */ @@ -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 fe0fc90..db9468f 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 56c932c..8d9ed8b 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,29 +53,49 @@ typedef struct { } validate_index[2]; int validate_next; int validate_count; + int searched_for_end; } FLVContext; -static int flv_probe(AVProbeData *p) +static int probe(AVProbeData *p, int live) { - const uint8_t *d; + const uint8_t *d = p->buf; + unsigned offset = AV_RB32(d + 5); - d = p->buf; if (d[0] == 'F' && d[1] == 'L' && d[2] == 'V' && d[3] < 5 && d[5] == 0 && - AV_RB32(d + 5) > 8) { - return AVPROBE_SCORE_MAX; + offset + 100 < p->buf_size && + offset > 8) { + int is_live = !memcmp(d + offset + 40, "NGINX RTMP", 10); + + if (live == is_live) + return AVPROBE_SCORE_MAX; } return 0; } +static int flv_probe(AVProbeData *p) +{ + return probe(p, 0); +} + +static int live_flv_probe(AVProbeData *p) +{ + return probe(p, 1); +} + static AVStream *create_stream(AVFormatContext *s, int codec_type) { AVStream *st = avformat_new_stream(s, NULL); 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 +205,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 +244,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 +260,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); @@ -253,9 +270,13 @@ static int flv_set_video_codec(AVFormatContext *s, AVStream *vstream, return 1; // 1 byte body size adjustment for flv_read_packet() case FLV_CODECID_H264: vcodec->codec_id = AV_CODEC_ID_H264; + vstream->need_parsing = AVSTREAM_PARSE_HEADERS; 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 +302,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, "Skipping 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 +360,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 +370,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 +410,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) @@ -483,6 +503,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") || @@ -517,6 +542,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". @@ -525,6 +551,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 @@ -543,10 +570,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) @@ -571,6 +600,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); @@ -583,20 +614,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_get_extradata(st->codec, s->pb, size) < 0) return AVERROR(ENOMEM); - st->codec->extradata_size = size; - avio_read(s->pb, st->codec->extradata, st->codec->extradata_size); return 0; } @@ -737,10 +766,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 */ @@ -750,8 +781,8 @@ static int flv_read_packet(AVFormatContext *s, AVPacket *pkt) size = avio_rb24(s->pb); 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) + av_dlog(s, "type:%d, size:%d, dts:%"PRId64" pos:%"PRId64"\n", type, size, dts, avio_tell(s->pb)); + if (avio_feof(s->pb)) return AVERROR_EOF; avio_skip(s->pb, 3); /* stream id, always 0 */ flags = 0; @@ -778,24 +809,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; @@ -808,29 +843,37 @@ 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 (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 (!is_audio && - st->codec->codec_type == AVMEDIA_TYPE_VIDEO) { - if (flv_same_video_codec(st->codec, flags)) + } 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 (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 ((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_KEY || - is_audio) + stream_type == FLV_STREAM_TYPE_AUDIO) av_add_index_entry(st, pos, dts, size, 0, AVINDEX_KEYFRAME); - 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 ( (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; } @@ -839,22 +882,30 @@ skip: // 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) >> @@ -877,48 +928,58 @@ skip: flv->last_channels = channels = st->codec->channels; } else { - AVCodecContext ctx; + AVCodecContext ctx = {0}; ctx.sample_rate = sample_rate; 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; - if (cts < 0 && !flv->wrong_dts) { // dts might be wrong + if (cts < 0) { // dts might be wrong + if (!flv->wrong_dts) + av_log(s, AV_LOG_WARNING, + "Negative cts, previous timestamps might be wrong.\n"); flv->wrong_dts = 1; + } else if (FFABS(dts - pts) > 1000*60*15) { av_log(s, AV_LOG_WARNING, - "Negative cts, previous timestamps might be wrong.\n"); + "invalid timestamps %"PRId64" %"PRId64"\n", dts, pts); + dts = pts = AV_NOPTS_VALUE; } } - if (type == 0) { + if (type == 0 && (!st->codec->extradata || st->codec->codec_id == AV_CODEC_ID_AAC || + st->codec->codec_id == AV_CODEC_ID_H264)) { + AVDictionaryEntry *t; + 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) { - MPEG4AudioConfig cfg; - /* Workaround for buggy Omnia A/XE encoder */ - AVDictionaryEntry *t = av_dict_get(s->metadata, "Encoder", NULL, 0); - if (t && !strcmp(t->value, "Omnia A/XE")) - st->codec->extradata_size = 2; + /* Workaround for buggy Omnia A/XE encoder */ + t = av_dict_get(s->metadata, "Encoder", NULL, 0); + if (st->codec->codec_id == AV_CODEC_ID_AAC && t && !strcmp(t->value, "Omnia A/XE")) + st->codec->extradata_size = 2; - avpriv_mpeg4audio_get_config(&cfg, st->codec->extradata, - st->codec->extradata_size * 8, 1); + if (st->codec->codec_id == AV_CODEC_ID_AAC && 0) { + MPEG4AudioConfig cfg; + + 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) @@ -927,6 +988,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); @@ -942,31 +1004,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: @@ -989,7 +1051,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, @@ -1006,5 +1068,26 @@ AVInputFormat ff_flv_demuxer = { .read_seek = flv_read_seek, .read_close = flv_read_close, .extensions = "flv", - .priv_class = &class, + .priv_class = &flv_class, +}; + +static const AVClass live_flv_class = { + .class_name = "live_flvdec", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + +AVInputFormat ff_live_flv_demuxer = { + .name = "live_flv", + .long_name = NULL_IF_CONFIG_SMALL("live RTMP FLV (Flash Video)"), + .priv_data_size = sizeof(FLVContext), + .read_probe = live_flv_probe, + .read_header = flv_read_header, + .read_packet = flv_read_packet, + .read_seek = flv_read_seek, + .read_close = flv_read_close, + .extensions = "flv", + .priv_class = &live_flv_class, + .flags = AVFMT_TS_DISCONT }; diff --git a/libavformat/flvenc.c b/libavformat/flvenc.c index cc4c782..febc5e5 100644 --- a/libavformat/flvenc.c +++ b/libavformat/flvenc.c @@ -1,40 +1,43 @@ /* * 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 }, + { AV_CODEC_ID_VP6, FLV_CODECID_VP6 }, { AV_CODEC_ID_VP6A, FLV_CODECID_VP6A }, { AV_CODEC_ID_H264, FLV_CODECID_H264 }, { AV_CODEC_ID_NONE, 0 } @@ -77,12 +80,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 +108,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 +151,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; @@ -213,8 +217,21 @@ 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); + } + if (enc->codec_id == AV_CODEC_ID_MPEG4 || + enc->codec_id == AV_CODEC_ID_H263) { + int error = enc->strict_std_compliance > FF_COMPLIANCE_UNOFFICIAL; + av_log(s, error ? AV_LOG_ERROR : AV_LOG_WARNING, + "Codec %s is not supported in the official FLV specification,\n", avcodec_get_name(enc->codec_id)); + + if (error) { + av_log(s, AV_LOG_ERROR, + "use vstrict=-1 / -strict -1 to use it anyway.\n"); + return AVERROR(EINVAL); + } } break; case AVMEDIA_TYPE_AUDIO: @@ -226,17 +243,22 @@ static int flv_write_header(AVFormatContext *s) audio_enc = enc; if (get_audio_flags(s, enc) < 0) return AVERROR_INVALIDDATA; + if (enc->codec_id == AV_CODEC_ID_PCM_S16BE) + av_log(s, AV_LOG_WARNING, + "16-bit big-endian audio in flv is valid but most likely unplayable (hardware dependent); use s16le\n"); 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 */ @@ -338,6 +360,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); @@ -364,7 +402,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); @@ -407,7 +445,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); } @@ -436,12 +474,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) + enc->codec_id == AV_CODEC_ID_VP6 || 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; @@ -453,9 +491,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; @@ -463,7 +501,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; @@ -474,11 +512,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 the 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; @@ -501,7 +549,7 @@ static int flv_write_packet(AVFormatContext *s, AVPacket *pkt) sc->last_ts = ts; avio_wb24(pb, size + flags_size); - avio_wb24(pb, ts); + avio_wb24(pb, ts & 0xFFFFFF); avio_w8(pb, (ts >> 24) & 0x7F); // timestamps are 32 bits _signed_ avio_wb24(pb, flv->reserved); @@ -527,7 +575,10 @@ 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_VP6) + avio_w8(pb,0); if (enc->codec_id == AV_CODEC_ID_VP6F || enc->codec_id == AV_CODEC_ID_VP6A) { if (enc->extradata_size) avio_w8(pb, enc->extradata[0]); @@ -536,7 +587,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 c5a57d5..828ab52 100644 --- a/libavformat/format.c +++ b/libavformat/format.c @@ -2,24 +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 "libavutil/atomic.h" #include "libavutil/avstring.h" +#include "libavutil/bprint.h" #include "libavutil/opt.h" #include "avio_internal.h" @@ -27,6 +29,7 @@ #include "id3v2.h" #include "internal.h" + /** * @file * Format register and lookup @@ -36,6 +39,9 @@ static AVInputFormat *first_iformat = NULL; /** head of registered output format linked list */ static AVOutputFormat *first_oformat = NULL; +static AVInputFormat **last_iformat = &first_iformat; +static AVOutputFormat **last_oformat = &first_oformat; + AVInputFormat *av_iformat_next(const AVInputFormat *f) { if (f) @@ -54,24 +60,22 @@ AVOutputFormat *av_oformat_next(const AVOutputFormat *f) void av_register_input_format(AVInputFormat *format) { - AVInputFormat **p = &first_iformat; - - while (*p) - p = &(*p)->next; + AVInputFormat **p = last_iformat; - *p = format; format->next = NULL; + while(*p || avpriv_atomic_ptr_cas((void * volatile *)p, NULL, format)) + p = &(*p)->next; + last_iformat = &format->next; } void av_register_output_format(AVOutputFormat *format) { - AVOutputFormat **p = &first_oformat; - - while (*p) - p = &(*p)->next; + AVOutputFormat **p = last_oformat; - *p = format; format->next = NULL; + while(*p || avpriv_atomic_ptr_cas((void * volatile *)p, NULL, format)) + p = &(*p)->next; + last_oformat = &format->next; } int av_match_ext(const char *filename, const char *extensions) @@ -120,7 +124,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 && av_match_name(short_name, fmt->name)) score += 100; if (fmt->mime_type && mime_type && !strcmp(fmt->mime_type, mime_type)) score += 10; @@ -140,6 +144,12 @@ enum AVCodecID av_guess_codec(AVOutputFormat *fmt, const char *short_name, const char *filename, const char *mime_type, enum AVMediaType type) { + if (av_match_name("segment", fmt->name) || av_match_name("ssegment", fmt->name)) { + AVOutputFormat *fmt2 = av_guess_format(NULL, filename, NULL); + if (fmt2) + fmt = fmt2; + } + if (type == AVMEDIA_TYPE_VIDEO) { enum AVCodecID codec_id = AV_CODEC_ID_NONE; @@ -168,20 +178,26 @@ AVInputFormat *av_find_input_format(const char *short_name) return NULL; } -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 if (id3len >= PROBE_BUF_MAX) { + nodat = 2; + } else + nodat = 1; } fmt = NULL; @@ -191,81 +207,89 @@ AVInputFormat *av_probe_input_format2(AVProbeData *pd, int is_opened, score = 0; if (fmt1->read_probe) { score = fmt1->read_probe(&lpd); + if (fmt1->extensions && av_match_ext(lpd.filename, fmt1->extensions)) { + if (nodat == 0) score = FFMAX(score, 1); + else if (nodat == 1) score = FFMAX(score, AVPROBE_SCORE_EXTENSION / 2 - 1); + else score = FFMAX(score, AVPROBE_SCORE_EXTENSION); + } } else if (fmt1->extensions) { if (av_match_ext(lpd.filename, fmt1->extensions)) score = AVPROBE_SCORE_EXTENSION; } if (av_match_name(lpd.mime_type, fmt1->mime_type)) score = FFMAX(score, AVPROBE_SCORE_EXTENSION); - if (score > *score_max) { - *score_max = score; - fmt = fmt1; - } else if (score == *score_max) + if (score > score_max) { + score_max = score; + fmt = fmt1; + } 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 == 1) + 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); } -/* size of probe buffer, for guessing file type from file contents */ -#define PROBE_BUF_MIN 2048 -#define PROBE_BUF_MAX (1 << 20) - -int av_probe_input_buffer(AVIOContext *pb, AVInputFormat **fmt, +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 : "" }; uint8_t *buf = NULL; - int ret = 0, probe_size; + int ret = 0, probe_size, buf_offset = 0; + int score = 0; + int ret2; 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) + 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); + } if (offset >= max_probe_size) return AVERROR(EINVAL); - avio_skip(pb, offset); - max_probe_size -= offset; + if (pb->av_class) av_opt_get(pb, "mime_type", AV_OPT_SEARCH_CHILDREN, &pd.mime_type); +#if 0 + 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); + } +#endif + 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; + score = probe_size < max_probe_size ? AVPROBE_SCORE_RETRY : 0; /* Read probe data. */ if ((ret = av_reallocp(&buf, probe_size + AVPROBE_PADDING_SIZE)) < 0) goto fail; - if ((ret = avio_read(pb, buf + pd.buf_size, - probe_size - pd.buf_size)) < 0) { + if ((ret = avio_read(pb, buf + buf_offset, + probe_size - buf_offset)) < 0) { /* Fail if error was not end of file, otherwise, lower score. */ if (ret != AVERROR_EOF) goto fail; @@ -273,8 +297,11 @@ 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 = buf; + buf_offset += ret; + if (buf_offset < offset) + continue; + pd.buf_size = buf_offset - offset; + pd.buf = &buf[offset]; memset(pd.buf + pd.buf_size, 0, AVPROBE_PADDING_SIZE); @@ -282,13 +309,19 @@ int av_probe_input_buffer(AVIOContext *pb, AVInputFormat **fmt, *fmt = av_probe_input_format2(&pd, 1, &score); if (*fmt) { /* This can only be true in the last iteration. */ - if (score <= AVPROBE_SCORE_MAX / 4) { + if (score <= AVPROBE_SCORE_RETRY) { av_log(logctx, AV_LOG_WARNING, - "Format detected only with low score of %d, " - "misdetection possible!\n", score); + "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); + "Format %s probed with size=%d and score=%d\n", + (*fmt)->name, probe_size, score); +#if 0 + FILE *f = fopen("probestat.tmp", "ab"); + fprintf(f, "probe_size:%d format:%s score:%d filename:%s\n", probe_size, (*fmt)->name, score, filename); + fclose(f); +#endif } } @@ -297,10 +330,18 @@ int av_probe_input_buffer(AVIOContext *pb, AVInputFormat **fmt, fail: /* Rewind. Reuse probe buffer to avoid seeking. */ - if (ret < 0 || - (ret = ffio_rewind_with_probe_data(pb, buf, pd.buf_size)) < 0) { - av_free(buf); - } + ret2 = ffio_rewind_with_probe_data(pb, &buf, buf_offset); + if (ret >= 0) + ret = ret2; + av_free(pd.mime_type); - 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; } diff --git a/libavformat/framecrcenc.c b/libavformat/framecrcenc.c index 4d5483a..805b542 100644 --- a/libavformat/framecrcenc.c +++ b/libavformat/framecrcenc.c @@ -2,36 +2,76 @@ * 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 <inttypes.h> #include "libavutil/adler32.h" +#include "libavutil/avstring.h" #include "avformat.h" #include "internal.h" +static int framecrc_write_header(struct AVFormatContext *s) +{ + int i; + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + AVCodecContext *avctx = st->codec; + if (avctx->extradata) { + uint32_t crc = av_adler32_update(0, avctx->extradata, avctx->extradata_size); + avio_printf(s->pb, "#extradata %d: %8d, 0x%08"PRIx32"\n", + i, avctx->extradata_size, crc); + } + } + + return ff_framehash_write_header(s); +} + 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%08"PRIx32"\n", + snprintf(buf, sizeof(buf), "%d, %10"PRId64", %10"PRId64", %8d, %8d, 0x%08"PRIx32, 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; } @@ -39,10 +79,9 @@ 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, + .write_header = framecrc_write_header, .write_packet = framecrc_write_packet, .flags = AVFMT_VARIABLE_FPS | AVFMT_TS_NONSTRICT | AVFMT_TS_NEGATIVE, diff --git a/libavformat/framehash.c b/libavformat/framehash.c index 6a6da98..a8357b0 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->flags & AVFMT_FLAG_BITEXACT)) + avio_printf(s->pb, "#software: %s\n", LIBAVFORMAT_IDENT); for (i = 0; i < s->nb_streams; i++) { AVStream *st = s->streams[i]; avio_printf(s->pb, "#tb %d: %d/%d\n", i, st->time_base.num, st->time_base.den); diff --git a/libavformat/frmdec.c b/libavformat/frmdec.c new file mode 100644 index 0000000..a6f19af --- /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 0000000..7faf4a5 --- /dev/null +++ b/libavformat/ftp.c @@ -0,0 +1,801 @@ +/* + * 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, linesize; + 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); + + linesize = strlen(buf); + err = 0; + if (linesize >= 3) { + for (i = 0; i < 3; ++i) { + if (buf[i] < '0' || buf[i] > '9') { + err = 0; + break; + } + err *= 10; + err += buf[i] - '0'; + } + } + + if (!code_found) { + if (err >= 500) { + code_found = 1; + result = err; + } else + for (i = 0; response_codes[i]; ++i) { + if (err == response_codes[i]) { + code_found = 1; + result = err; + break; + } + } + } + if (code_found) { + if (line) + av_bprintf(&line_buffer, "%s\r\n", buf); + if (linesize >= 4) { + if (!dash && buf[3] == '-') + dash = err; + else if (err == dash && buf[3] == ' ') + dash = 0; + } + } + } + + 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 (response) + *response = NULL; + + 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, 0}; + static const int pass_codes[] = {230, 0}; + + /* 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_epsv(FTPContext *s) +{ + char *res = NULL, *start = NULL, *end = NULL; + int i; + static const char d = '|'; + static const char *command = "EPSV\r\n"; + static const int epsv_codes[] = {229, 0}; + + if (ftp_send_command(s, command, epsv_codes, &res) != 229 || !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'; + if (strlen(start) < 5) + goto fail; + if (start[0] != d || start[1] != d || start[2] != d || end[-1] != d) + goto fail; + start += 3; + end[-1] = '\0'; + + 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; + return AVERROR(ENOSYS); +} + +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, 0}; + + 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; + 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, 0}; + + 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, 0}; + + 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, 0}; + + 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, 0}; + + 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_features(FTPContext *s) +{ + static const char *feat_command = "FEAT\r\n"; + static const char *enable_utf8_command = "OPTS UTF8 ON\r\n"; + static const int feat_codes[] = {211, 0}; + static const int opts_codes[] = {200, 451}; + char *feat = NULL; + + if (ftp_send_command(s, feat_command, feat_codes, &feat) == 211) { + if (av_stristr(feat, "UTF8")) + ftp_send_command(s, enable_utf8_command, opts_codes, NULL); + } + av_freep(&feat); + + return 0; +} + +static int ftp_connect_control_connection(URLContext *h) +{ + char buf[CONTROL_BUFFER_SIZE], *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) { + av_dict_set_int(&opts, "timeout", s->rw_timeout, 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; + } + + ftp_features(s); + } + return 0; +} + +static int ftp_connect_data_connection(URLContext *h) +{ + int err; + char buf[CONTROL_BUFFER_SIZE]; + AVDictionary *opts = NULL; + FTPContext *s = h->priv_data; + + if (!s->conn_data) { + /* Enter passive mode */ + if (ftp_passive_mode_epsv(s) < 0) { + /* Use PASV as fallback */ + 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) { + av_dict_set_int(&opts, "timeout", s->rw_timeout, 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); + + if (new_pos < 0) { + av_log(h, AV_LOG_ERROR, "Seeking to nagative position.\n"); + return AVERROR(EINVAL); + } + + 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 8052939..1a87c7d 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 b67c07c..4f3ce8f 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 0000000..794558e --- /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 085c2e0..e817121 100644 --- a/libavformat/gif.c +++ b/libavformat/gif.c @@ -4,347 +4,194 @@ * * 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, +static int gif_image_write_header(AVFormatContext *s, int width, int height, int loop_count, uint32_t *palette) { + AVIOContext *pb = s->pb; + AVRational sar = s->streams[0]->codec->sample_aspect_ratio; int i; - unsigned int v; + int64_t aspect = 0; + + if (sar.num > 0 && sar.den > 0) { + aspect = sar.num * 64LL / sar.den - 15; + if (aspect < 0 || aspect > 255) + aspect = 0; + } 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, aspect); 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, aspect); } - /* 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(s, width, height, gif->loop, NULL); + } else { + gif_image_write_header(s, 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 +200,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 centiseconds) after the last frame", OFFSET(last_delay), + AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 65535, ENC }, { NULL }, }; @@ -372,9 +221,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 0000000..7db5a27 --- /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 (avio_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)) && !avio_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 || avio_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/golomb_tab.c b/libavformat/golomb_tab.c new file mode 100644 index 0000000..063fae3 --- /dev/null +++ b/libavformat/golomb_tab.c @@ -0,0 +1 @@ +#include "libavcodec/golomb.c" diff --git a/libavformat/gopher.c b/libavformat/gopher.c index a149f7f..9dc155a 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 f9ecbaf..dbe557b 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 1895840..6c624f0 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 */ @@ -33,9 +33,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 @@ -96,12 +118,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; @@ -145,6 +165,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: @@ -152,6 +178,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; @@ -228,6 +258,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); @@ -241,7 +272,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); } } @@ -251,15 +284,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 %"PRIu32" (%"PRIx32")\n", @@ -321,8 +355,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; @@ -333,6 +365,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]; @@ -367,10 +409,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 @@ -389,7 +442,7 @@ static int gxf_header(AVFormatContext *s) { #define READ_ONE() \ { \ - if (!max_interval-- || pb->eof_reached) \ + if (!max_interval-- || avio_feof(pb)) \ goto out; \ tmp = tmp << 8 | avio_r8(pb); \ } @@ -451,7 +504,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 (!avio_feof(pb)) av_log(s, AV_LOG_ERROR, "sync lost\n"); return -1; } @@ -503,7 +556,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 c1ac399..dcdcdef 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 fea1d5d..12031f7 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); } @@ -862,7 +949,8 @@ static int gxf_write_packet(AVFormatContext *s, AVPacket *pkt) AVStream *st = s->streams[pkt->stream_index]; int64_t pos = avio_tell(pb); int padding = 0; - int packet_start_offset = avio_tell(pb) / 1024; + unsigned 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 4a58050..a1d6821 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 */ @@ -25,33 +25,30 @@ static int h261_probe(AVProbeData *p) { - uint32_t code= -1; int i; int valid_psc=0; int invalid_psc=0; int next_gn=0; int src_fmt=0; - GetBitContext gb; - init_get_bits(&gb, p->buf, p->buf_size*8); + for(i=0; i<p->buf_size; i++){ + if ((AV_RB16(&p->buf[i]) - 1) < 0xFFU) { + int shift = av_log2_16bit(p->buf[i+1]); + uint32_t code = AV_RB64(&p->buf[FFMAX(i-1, 0)]) >> (24+shift); + if ((code & 0xffff0000) == 0x10000) { + int gn= (code>>12)&0xf; + if(!gn) + src_fmt= code&8; + if(gn != next_gn) invalid_psc++; + else valid_psc++; - for(i=0; i<p->buf_size*8; i++){ - if ((code & 0x01ff0000) || !(code & 0xff00)) { - code = (code<<8) + get_bits(&gb, 8); - i += 7; - } else - code = (code<<1) + get_bits1(&gb); - if ((code & 0xffff0000) == 0x10000) { - int gn= (code>>12)&0xf; - if(!gn) - src_fmt= code&8; - if(gn != next_gn) invalid_psc++; - else valid_psc++; - - if(src_fmt){ // CIF - next_gn= (gn+1 )%13; - }else{ //QCIF - next_gn= (gn+1+!!gn)% 7; + if(src_fmt){ // CIF + static const int lut[16]={1,2,3,4,5,6,7,8,9,10,11,12,0,16,16,16}; + next_gn = lut[gn]; + }else{ //QCIF + static const int lut[16]={1,3,16,5,16,0,16,16,16,16,16,16,16,16,16,16}; + next_gn = lut[gn]; + } } } } diff --git a/libavformat/h263dec.c b/libavformat/h263dec.c index 4d826d8..145fb85 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 */ @@ -31,24 +31,37 @@ static int h263_probe(AVProbeData *p) int res_change=0; int src_fmt, last_src_fmt=-1; int last_gn=0; + int tr, last_tr = -1; for(i=0; i<p->buf_size; i++){ code = (code<<8) + p->buf[i]; - if ((code & 0xfffffc0000) == 0x800000) { - src_fmt= (code>>2)&3; + if ((code & 0xfffffc000000) == 0x80000000) { + tr = (code >> 18) & 0xFF; + src_fmt= (code>>10)&7; if( src_fmt != last_src_fmt && last_src_fmt>0 && last_src_fmt<6 && src_fmt<6) res_change++; - if((code&0x300)==0x200 && src_fmt){ + if (tr == last_tr) { + invalid_psc++; + continue; + } + + if (src_fmt != 7 && !(code&(1<<9)) && (code&(1<<5))) { + invalid_psc++; + continue; + } + + if((code&0x30000)==0x20000 && src_fmt){ valid_psc++; last_gn=0; }else invalid_psc++; last_src_fmt= src_fmt; - } else if((code & 0xffff800000) == 0x800000) { - int gn= (code>>(23-5)) & 0x1F; + last_tr = tr; + } else if((code & 0xffff80000000) == 0x80000000) { + int gn= (code>>(31-5)) & 0x1F; if(gn<last_gn){ invalid_psc++; }else diff --git a/libavformat/h264dec.c b/libavformat/h264dec.c index 6fd45c1..76073dd 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/hdsenc.c b/libavformat/hdsenc.c index 6217c1f..fac5bcf 100644 --- a/libavformat/hdsenc.c +++ b/libavformat/hdsenc.c @@ -2,20 +2,20 @@ * Live HDS fragmenter * 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 */ @@ -204,7 +204,10 @@ static int write_manifest(AVFormatContext *s, int final) avio_printf(out, "</manifest>\n"); avio_flush(out); avio_close(out); - rename(temp_filename, filename); + if (rename(temp_filename, filename) == -1) { + av_log(s, AV_LOG_ERROR, "failed to rename file %s to %s\n", temp_filename, filename); + return AVERROR(errno); + } return 0; } @@ -286,7 +289,10 @@ static int write_abst(AVFormatContext *s, OutputStream *os, int final) update_size(out, afrt_pos); update_size(out, 0); avio_close(out); - rename(temp_filename, filename); + if (rename(temp_filename, filename) == -1) { + av_log(s, AV_LOG_ERROR, "failed to rename file %s to %s\n", temp_filename, filename); + return AVERROR(errno); + } return 0; } @@ -323,7 +329,14 @@ static int hds_write_header(AVFormatContext *s) int ret = 0, i; AVOutputFormat *oformat; - mkdir(s->filename, 0777); + if (mkdir(s->filename, 0777)) { + int is_error = errno != EEXIST; + av_log(s, is_error ? AV_LOG_ERROR : AV_LOG_VERBOSE, "Failed to create directory %s\n", s->filename); + if (is_error) { + ret = AVERROR(errno); + goto fail; + } + } oformat = av_guess_format("flv", NULL, NULL); if (!oformat) { @@ -331,7 +344,7 @@ static int hds_write_header(AVFormatContext *s) goto fail; } - c->streams = av_mallocz(sizeof(*c->streams) * s->nb_streams); + c->streams = av_mallocz_array(s->nb_streams, sizeof(*c->streams)); if (!c->streams) { ret = AVERROR(ENOMEM); goto fail; @@ -477,7 +490,10 @@ static int hds_flush(AVFormatContext *s, OutputStream *os, int final, snprintf(target_filename, sizeof(target_filename), "%s/stream%dSeg1-Frag%d", s->filename, index, os->fragment_index); - rename(os->temp_filename, target_filename); + if (rename(os->temp_filename, target_filename) == -1) { + av_log(s, AV_LOG_ERROR, "failed to rename file %s to %s\n", os->temp_filename, target_filename); + return AVERROR(errno); + } add_fragment(os, target_filename, os->frag_start_ts, end_ts - os->frag_start_ts); if (!final) { @@ -511,7 +527,7 @@ static int hds_write_packet(AVFormatContext *s, AVPacket *pkt) HDSContext *c = s->priv_data; AVStream *st = s->streams[pkt->stream_index]; OutputStream *os = &c->streams[s->streams[pkt->stream_index]->id]; - int64_t end_dts = os->fragment_index * (int64_t) c->min_frag_duration; + int64_t end_dts = os->fragment_index * (int64_t)c->min_frag_duration; int ret; if (st->first_dts == AV_NOPTS_VALUE) @@ -533,7 +549,7 @@ static int hds_write_packet(AVFormatContext *s, AVPacket *pkt) os->last_ts = pkt->dts; os->packets_written++; - return ff_write_chained(os->ctx, pkt->stream_index - os->first_stream, pkt, s); + return ff_write_chained(os->ctx, pkt->stream_index - os->first_stream, pkt, s, 0); } static int hds_write_trailer(AVFormatContext *s) diff --git a/libavformat/hevc.c b/libavformat/hevc.c index 37b35b4..45b00c4 100644 --- a/libavformat/hevc.c +++ b/libavformat/hevc.c @@ -1,20 +1,20 @@ /* * Copyright (c) 2014 Tim Walker <tdskywalker@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 */ @@ -799,7 +799,7 @@ static void hvcc_init(HEVCDecoderConfigurationRecord *hvcc) /* * Initialize this field with an invalid value which can be used to detect - * whether we didn't see any VUI (in wich case it should be reset to zero). + * whether we didn't see any VUI (in which case it should be reset to zero). */ hvcc->min_spatial_segmentation_idc = MAX_SPATIAL_SEGMENTATION + 1; } diff --git a/libavformat/hevc.h b/libavformat/hevc.h index 03c43bd..796eaf4 100644 --- a/libavformat/hevc.h +++ b/libavformat/hevc.h @@ -1,20 +1,20 @@ /* * Copyright (c) 2014 Tim Walker <tdskywalker@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 */ @@ -89,7 +89,7 @@ int ff_hevc_annexb2mp4_buf(const uint8_t *buf_in, uint8_t **buf_out, * @param size size (in bytes) of the data buffer * @param ps_array_completeness whether all parameter sets are in the hvcC (1) * or there may be additional parameter sets in the bitstream (0) - * @return 0 in case of success, a negative value corresponding to an AVERROR + * @return >=0 in case of success, a negative value corresponding to an AVERROR * code in case of failure */ int ff_isom_write_hvcc(AVIOContext *pb, const uint8_t *data, diff --git a/libavformat/hevcdec.c b/libavformat/hevcdec.c index 65a3cdf..e36a051 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 290f12e..0c86461 100644 --- a/libavformat/hls.c +++ b/libavformat/hls.c @@ -1,21 +1,22 @@ /* * Apple HTTP Live Streaming demuxer * Copyright (c) 2010 Martin Storsjo + * Copyright (c) 2013 Anssi Hannula * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free 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 +27,7 @@ */ #include "libavutil/avstring.h" +#include "libavutil/avassert.h" #include "libavutil/intreadwrite.h" #include "libavutil/mathematics.h" #include "libavutil/opt.h" @@ -35,9 +37,16 @@ #include "internal.h" #include "avio_internal.h" #include "url.h" +#include "id3v2.h" #define INITIAL_BUFFER_SIZE 32768 +#define MAX_FIELD_LEN 64 +#define MAX_CHARACTERISTICS_LEN 512 + +#define MPEG_TIME_BASE 90000 +#define MPEG_TIME_BASE_Q (AVRational){1, MPEG_TIME_BASE} + /* * An apple http stream consists of a playlist with media segment files, * played sequentially. There may be several playlists with the same @@ -57,19 +66,28 @@ enum KeyType { struct segment { int64_t duration; - char url[MAX_URL_SIZE]; - char key[MAX_URL_SIZE]; + int64_t url_offset; + int64_t size; + char *url; + char *key; enum KeyType key_type; uint8_t iv[16]; }; +struct rendition; + +enum PlaylistType { + PLS_TYPE_UNSPECIFIED, + PLS_TYPE_EVENT, + PLS_TYPE_VOD +}; + /* - * Each variant has its own demuxer. If it currently is active, + * Each playlist has its own demuxer. If it currently is active, * it has an open AVIOContext too, and potentially an AVPacket * containing the next packet from this stream. */ -struct variant { - int bandwidth; +struct playlist { char url[MAX_URL_SIZE]; AVIOContext pb; uint8_t* read_buffer; @@ -81,28 +99,86 @@ struct variant { int stream_offset; int finished; + enum PlaylistType type; int64_t target_duration; int start_seq_no; int n_segments; struct segment **segments; int needed, cur_needed; int cur_seq_no; + int64_t cur_seg_offset; int64_t last_load_time; char key_url[MAX_URL_SIZE]; uint8_t key[16]; + + /* ID3 timestamp handling (elementary audio streams have ID3 timestamps + * (and possibly other ID3 tags) in the beginning of each segment) */ + int is_id3_timestamped; /* -1: not yet known */ + int64_t id3_mpegts_timestamp; /* in mpegts tb */ + int64_t id3_offset; /* in stream original tb */ + uint8_t* id3_buf; /* temp buffer for id3 parsing */ + unsigned int id3_buf_size; + AVDictionary *id3_initial; /* data from first id3 tag */ + int id3_found; /* ID3 tag found at some point */ + int id3_changed; /* ID3 tag data has changed at some point */ + ID3v2ExtraMeta *id3_deferred_extra; /* stored here until subdemuxer is opened */ + + int64_t seek_timestamp; + int seek_flags; + int seek_stream_index; /* into subdemuxer stream array */ + + /* Renditions associated with this playlist, if any. + * Alternative rendition playlists have a single rendition associated + * with them, and variant main Media Playlists may have + * multiple (playlist-less) renditions associated with them. */ + int n_renditions; + struct rendition **renditions; +}; + +/* + * Renditions are e.g. alternative subtitle or audio streams. + * The rendition may either be an external playlist or it may be + * contained in the main Media Playlist of the variant (in which case + * playlist is NULL). + */ +struct rendition { + enum AVMediaType type; + struct playlist *playlist; + char group_id[MAX_FIELD_LEN]; + char language[MAX_FIELD_LEN]; + char name[MAX_FIELD_LEN]; + int disposition; +}; + +struct variant { + int bandwidth; + + /* every variant contains at least the main Media Playlist in index 0 */ + int n_playlists; + struct playlist **playlists; + + char audio_group[MAX_FIELD_LEN]; + char video_group[MAX_FIELD_LEN]; + char subtitles_group[MAX_FIELD_LEN]; }; typedef struct HLSContext { int n_variants; struct variant **variants; + int n_playlists; + struct playlist **playlists; + int n_renditions; + struct rendition **renditions; + int cur_seq_no; - int end_of_segment; int first_packet; int64_t first_timestamp; - int64_t seek_timestamp; - int seek_flags; + int64_t cur_timestamp; 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 + char *headers; ///< holds HTTP headers set as an AVOption to the HTTP protocol context } HLSContext; static int read_chomp_line(AVIOContext *s, char *buf, int maxlen) @@ -113,13 +189,42 @@ static int read_chomp_line(AVIOContext *s, char *buf, int maxlen) return len; } -static void free_segment_list(struct variant *var) +static void free_segment_list(struct playlist *pls) +{ + int i; + for (i = 0; i < pls->n_segments; i++) { + av_free(pls->segments[i]->key); + av_free(pls->segments[i]->url); + av_free(pls->segments[i]); + } + av_freep(&pls->segments); + pls->n_segments = 0; +} + +static void free_playlist_list(HLSContext *c) { int i; - for (i = 0; i < var->n_segments; i++) - av_free(var->segments[i]); - av_freep(&var->segments); - var->n_segments = 0; + for (i = 0; i < c->n_playlists; i++) { + struct playlist *pls = c->playlists[i]; + free_segment_list(pls); + av_freep(&pls->renditions); + av_freep(&pls->id3_buf); + av_dict_free(&pls->id3_initial); + ff_id3v2_free_extra_meta(&pls->id3_deferred_extra); + av_free_packet(&pls->pkt); + av_free(pls->pb.buffer); + if (pls->input) + ffurl_close(pls->input); + if (pls->ctx) { + pls->ctx->pb = NULL; + avformat_close_input(&pls->ctx); + } + av_free(pls); + } + av_freep(&c->playlists); + av_freep(&c->cookies); + av_freep(&c->user_agent); + c->n_playlists = 0; } static void free_variant_list(HLSContext *c) @@ -127,21 +232,22 @@ static void free_variant_list(HLSContext *c) int i; for (i = 0; i < c->n_variants; i++) { struct variant *var = c->variants[i]; - free_segment_list(var); - av_free_packet(&var->pkt); - av_free(var->pb.buffer); - if (var->input) - ffurl_close(var->input); - if (var->ctx) { - var->ctx->pb = NULL; - avformat_close_input(&var->ctx); - } + av_freep(&var->playlists); av_free(var); } av_freep(&c->variants); c->n_variants = 0; } +static void free_rendition_list(HLSContext *c) +{ + int i; + for (i = 0; i < c->n_renditions; i++) + av_free(c->renditions[i]); + av_freep(&c->renditions); + c->n_renditions = 0; +} + /* * Used to reset a statically allocated AVPacket to a clean slate, * containing no data. @@ -152,29 +258,72 @@ static void reset_packet(AVPacket *pkt) pkt->data = NULL; } -static struct variant *new_variant(HLSContext *c, int bandwidth, - const char *url, const char *base) +static struct playlist *new_playlist(HLSContext *c, const char *url, + const char *base) { - struct variant *var = av_mallocz(sizeof(struct variant)); - if (!var) + struct playlist *pls = av_mallocz(sizeof(struct playlist)); + if (!pls) return NULL; - reset_packet(&var->pkt); - var->bandwidth = bandwidth; - ff_make_absolute_url(var->url, sizeof(var->url), base, url); - dynarray_add(&c->variants, &c->n_variants, var); - return var; + reset_packet(&pls->pkt); + ff_make_absolute_url(pls->url, sizeof(pls->url), base, url); + pls->seek_timestamp = AV_NOPTS_VALUE; + + pls->is_id3_timestamped = -1; + pls->id3_mpegts_timestamp = AV_NOPTS_VALUE; + + dynarray_add(&c->playlists, &c->n_playlists, pls); + return pls; } struct variant_info { char bandwidth[20]; + /* variant group ids: */ + char audio[MAX_FIELD_LEN]; + char video[MAX_FIELD_LEN]; + char subtitles[MAX_FIELD_LEN]; }; +static struct variant *new_variant(HLSContext *c, struct variant_info *info, + const char *url, const char *base) +{ + struct variant *var; + struct playlist *pls; + + pls = new_playlist(c, url, base); + if (!pls) + return NULL; + + var = av_mallocz(sizeof(struct variant)); + if (!var) + return NULL; + + if (info) { + var->bandwidth = atoi(info->bandwidth); + strcpy(var->audio_group, info->audio); + strcpy(var->video_group, info->video); + strcpy(var->subtitles_group, info->subtitles); + } + + dynarray_add(&c->variants, &c->n_variants, var); + dynarray_add(&var->playlists, &var->n_playlists, pls); + return var; +} + static void handle_variant_args(struct variant_info *info, const char *key, int key_len, char **dest, int *dest_len) { if (!strncmp(key, "BANDWIDTH=", key_len)) { *dest = info->bandwidth; *dest_len = sizeof(info->bandwidth); + } else if (!strncmp(key, "AUDIO=", key_len)) { + *dest = info->audio; + *dest_len = sizeof(info->audio); + } else if (!strncmp(key, "VIDEO=", key_len)) { + *dest = info->video; + *dest_len = sizeof(info->video); + } else if (!strncmp(key, "SUBTITLES=", key_len)) { + *dest = info->subtitles; + *dest_len = sizeof(info->subtitles); } } @@ -199,24 +348,182 @@ static void handle_key_args(struct key_info *info, const char *key, } } +struct rendition_info { + char type[16]; + char uri[MAX_URL_SIZE]; + char group_id[MAX_FIELD_LEN]; + char language[MAX_FIELD_LEN]; + char assoc_language[MAX_FIELD_LEN]; + char name[MAX_FIELD_LEN]; + char defaultr[4]; + char forced[4]; + char characteristics[MAX_CHARACTERISTICS_LEN]; +}; + +static struct rendition *new_rendition(HLSContext *c, struct rendition_info *info, + const char *url_base) +{ + struct rendition *rend; + enum AVMediaType type = AVMEDIA_TYPE_UNKNOWN; + char *characteristic; + char *chr_ptr; + char *saveptr; + + if (!strcmp(info->type, "AUDIO")) + type = AVMEDIA_TYPE_AUDIO; + else if (!strcmp(info->type, "VIDEO")) + type = AVMEDIA_TYPE_VIDEO; + else if (!strcmp(info->type, "SUBTITLES")) + type = AVMEDIA_TYPE_SUBTITLE; + else if (!strcmp(info->type, "CLOSED-CAPTIONS")) + /* CLOSED-CAPTIONS is ignored since we do not support CEA-608 CC in + * AVC SEI RBSP anyway */ + return NULL; + + if (type == AVMEDIA_TYPE_UNKNOWN) + return NULL; + + /* URI is mandatory for subtitles as per spec */ + if (type == AVMEDIA_TYPE_SUBTITLE && !info->uri[0]) + return NULL; + + /* TODO: handle subtitles (each segment has to parsed separately) */ + if (type == AVMEDIA_TYPE_SUBTITLE) + return NULL; + + rend = av_mallocz(sizeof(struct rendition)); + if (!rend) + return NULL; + + dynarray_add(&c->renditions, &c->n_renditions, rend); + + rend->type = type; + strcpy(rend->group_id, info->group_id); + strcpy(rend->language, info->language); + strcpy(rend->name, info->name); + + /* add the playlist if this is an external rendition */ + if (info->uri[0]) { + rend->playlist = new_playlist(c, info->uri, url_base); + if (rend->playlist) + dynarray_add(&rend->playlist->renditions, + &rend->playlist->n_renditions, rend); + } + + if (info->assoc_language[0]) { + int langlen = strlen(rend->language); + if (langlen < sizeof(rend->language) - 3) { + rend->language[langlen] = ','; + strncpy(rend->language + langlen + 1, info->assoc_language, + sizeof(rend->language) - langlen - 2); + } + } + + if (!strcmp(info->defaultr, "YES")) + rend->disposition |= AV_DISPOSITION_DEFAULT; + if (!strcmp(info->forced, "YES")) + rend->disposition |= AV_DISPOSITION_FORCED; + + chr_ptr = info->characteristics; + while ((characteristic = av_strtok(chr_ptr, ",", &saveptr))) { + if (!strcmp(characteristic, "public.accessibility.describes-music-and-sound")) + rend->disposition |= AV_DISPOSITION_HEARING_IMPAIRED; + else if (!strcmp(characteristic, "public.accessibility.describes-video")) + rend->disposition |= AV_DISPOSITION_VISUAL_IMPAIRED; + + chr_ptr = NULL; + } + + return rend; +} + +static void handle_rendition_args(struct rendition_info *info, const char *key, + int key_len, char **dest, int *dest_len) +{ + if (!strncmp(key, "TYPE=", key_len)) { + *dest = info->type; + *dest_len = sizeof(info->type); + } else if (!strncmp(key, "URI=", key_len)) { + *dest = info->uri; + *dest_len = sizeof(info->uri); + } else if (!strncmp(key, "GROUP-ID=", key_len)) { + *dest = info->group_id; + *dest_len = sizeof(info->group_id); + } else if (!strncmp(key, "LANGUAGE=", key_len)) { + *dest = info->language; + *dest_len = sizeof(info->language); + } else if (!strncmp(key, "ASSOC-LANGUAGE=", key_len)) { + *dest = info->assoc_language; + *dest_len = sizeof(info->assoc_language); + } else if (!strncmp(key, "NAME=", key_len)) { + *dest = info->name; + *dest_len = sizeof(info->name); + } else if (!strncmp(key, "DEFAULT=", key_len)) { + *dest = info->defaultr; + *dest_len = sizeof(info->defaultr); + } else if (!strncmp(key, "FORCED=", key_len)) { + *dest = info->forced; + *dest_len = sizeof(info->forced); + } else if (!strncmp(key, "CHARACTERISTICS=", key_len)) { + *dest = info->characteristics; + *dest_len = sizeof(info->characteristics); + } + /* + * ignored: + * - AUTOSELECT: client may autoselect based on e.g. system language + * - INSTREAM-ID: EIA-608 closed caption number ("CC1".."CC4") + */ +} + +/* used by parse_playlist to allocate a new variant+playlist when the + * playlist is detected to be a Media Playlist (not Master Playlist) + * and we have no parent Master Playlist (parsing of which would have + * allocated the variant and playlist already) */ +static int ensure_playlist(HLSContext *c, struct playlist **pls, const char *url) +{ + if (*pls) + return 0; + if (!new_variant(c, NULL, url, NULL)) + return AVERROR(ENOMEM); + *pls = c->playlists[c->n_playlists - 1]; + return 0; +} + +/* pls = NULL => Master Playlist or parentless Media Playlist + * pls = !NULL => parented Media Playlist, playlist+variant allocated */ static int parse_playlist(HLSContext *c, const char *url, - struct variant *var, AVIOContext *in) + struct playlist *pls, AVIOContext *in) { - int ret = 0, is_segment = 0, is_variant = 0, bandwidth = 0; + int ret = 0, is_segment = 0, is_variant = 0; int64_t duration = 0; enum KeyType key_type = KEY_NONE; 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; + int64_t seg_offset = 0; + int64_t seg_size = -1; uint8_t *new_url = NULL; + struct variant_info variant_info; + char tmp_str[MAX_URL_SIZE]; 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); + av_dict_set(&opts, "headers", c->headers, 0); + + ret = avio_open2(&in, url, AVIO_FLAG_READ, + c->interrupt_callback, &opts); + av_dict_free(&opts); + if (ret < 0) return ret; } @@ -229,18 +536,18 @@ static int parse_playlist(HLSContext *c, const char *url, goto fail; } - if (var) { - free_segment_list(var); - var->finished = 0; + if (pls) { + free_segment_list(pls); + pls->finished = 0; + pls->type = PLS_TYPE_UNSPECIFIED; } - while (!in->eof_reached) { + while (!avio_feof(in)) { read_chomp_line(in, line, sizeof(line)); if (av_strstart(line, "#EXT-X-STREAM-INF:", &ptr)) { - struct variant_info info = {{0}}; is_variant = 1; + memset(&variant_info, 0, sizeof(variant_info)); ff_parse_key_value(ptr, (ff_parse_key_val_cb) handle_variant_args, - &info); - bandwidth = atoi(info.bandwidth); + &variant_info); } else if (av_strstart(line, "#EXT-X-KEY:", &ptr)) { struct key_info info = {{0}}; ff_parse_key_value(ptr, (ff_parse_key_val_cb) handle_key_args, @@ -254,49 +561,58 @@ static int parse_playlist(HLSContext *c, const char *url, has_iv = 1; } av_strlcpy(key, info.uri, sizeof(key)); + } else if (av_strstart(line, "#EXT-X-MEDIA:", &ptr)) { + struct rendition_info info = {{0}}; + ff_parse_key_value(ptr, (ff_parse_key_val_cb) handle_rendition_args, + &info); + new_rendition(c, &info, url); } else if (av_strstart(line, "#EXT-X-TARGETDURATION:", &ptr)) { - if (!var) { - var = new_variant(c, 0, url, NULL); - if (!var) { - ret = AVERROR(ENOMEM); - goto fail; - } - } - var->target_duration = atoi(ptr) * AV_TIME_BASE; + ret = ensure_playlist(c, &pls, url); + if (ret < 0) + goto fail; + pls->target_duration = atoi(ptr) * AV_TIME_BASE; } else if (av_strstart(line, "#EXT-X-MEDIA-SEQUENCE:", &ptr)) { - if (!var) { - var = new_variant(c, 0, url, NULL); - if (!var) { - ret = AVERROR(ENOMEM); - goto fail; - } - } - var->start_seq_no = atoi(ptr); + ret = ensure_playlist(c, &pls, url); + if (ret < 0) + goto fail; + pls->start_seq_no = atoi(ptr); + } else if (av_strstart(line, "#EXT-X-PLAYLIST-TYPE:", &ptr)) { + ret = ensure_playlist(c, &pls, url); + if (ret < 0) + goto fail; + if (!strcmp(ptr, "EVENT")) + pls->type = PLS_TYPE_EVENT; + else if (!strcmp(ptr, "VOD")) + pls->type = PLS_TYPE_VOD; } else if (av_strstart(line, "#EXT-X-ENDLIST", &ptr)) { - if (var) - var->finished = 1; + if (pls) + pls->finished = 1; } else if (av_strstart(line, "#EXTINF:", &ptr)) { is_segment = 1; duration = atof(ptr) * AV_TIME_BASE; + } else if (av_strstart(line, "#EXT-X-BYTERANGE:", &ptr)) { + seg_size = atoi(ptr); + ptr = strchr(ptr, '@'); + if (ptr) + seg_offset = atoi(ptr+1); } else if (av_strstart(line, "#", NULL)) { continue; } else if (line[0]) { if (is_variant) { - if (!new_variant(c, bandwidth, line, url)) { + if (!new_variant(c, &variant_info, line, url)) { ret = AVERROR(ENOMEM); goto fail; } is_variant = 0; - bandwidth = 0; } if (is_segment) { struct segment *seg; - if (!var) { - var = new_variant(c, 0, url, NULL); - if (!var) { + if (!pls) { + if (!new_variant(c, 0, url, NULL)) { ret = AVERROR(ENOMEM); goto fail; } + pls = c->playlists[c->n_playlists - 1]; } seg = av_malloc(sizeof(struct segment)); if (!seg) { @@ -308,19 +624,49 @@ static int parse_playlist(HLSContext *c, const char *url, if (has_iv) { memcpy(seg->iv, iv, sizeof(iv)); } else { - int seq = var->start_seq_no + var->n_segments; + int seq = pls->start_seq_no + pls->n_segments; memset(seg->iv, 0, sizeof(seg->iv)); AV_WB32(seg->iv + 12, seq); } - ff_make_absolute_url(seg->key, sizeof(seg->key), url, key); - ff_make_absolute_url(seg->url, sizeof(seg->url), url, line); - dynarray_add(&var->segments, &var->n_segments, seg); + + if (key_type != KEY_NONE) { + ff_make_absolute_url(tmp_str, sizeof(tmp_str), url, key); + seg->key = av_strdup(tmp_str); + if (!seg->key) { + av_free(seg); + ret = AVERROR(ENOMEM); + goto fail; + } + } else { + seg->key = NULL; + } + + ff_make_absolute_url(tmp_str, sizeof(tmp_str), url, line); + seg->url = av_strdup(tmp_str); + if (!seg->url) { + av_free(seg->key); + av_free(seg); + ret = AVERROR(ENOMEM); + goto fail; + } + + dynarray_add(&pls->segments, &pls->n_segments, seg); is_segment = 0; + + seg->size = seg_size; + if (seg_size >= 0) { + seg->url_offset = seg_offset; + seg_offset += seg_size; + seg_size = -1; + } else { + seg->url_offset = 0; + seg_offset = 0; + } } } } - if (var) - var->last_load_time = av_gettime(); + if (pls) + pls->last_load_time = av_gettime(); fail: av_free(new_url); @@ -329,21 +675,269 @@ fail: return ret; } -static int open_input(struct variant *var) +enum ReadFromURLMode { + READ_NORMAL, + READ_COMPLETE, +}; + +/* read from URLContext, limiting read to current segment */ +static int read_from_url(struct playlist *pls, uint8_t *buf, int buf_size, + enum ReadFromURLMode mode) +{ + int ret; + struct segment *seg = pls->segments[pls->cur_seq_no - pls->start_seq_no]; + + /* limit read if the segment was only a part of a file */ + if (seg->size >= 0) + buf_size = FFMIN(buf_size, seg->size - pls->cur_seg_offset); + + if (mode == READ_COMPLETE) + ret = ffurl_read_complete(pls->input, buf, buf_size); + else + ret = ffurl_read(pls->input, buf, buf_size); + + if (ret > 0) + pls->cur_seg_offset += ret; + + return ret; +} + +/* Parse the raw ID3 data and pass contents to caller */ +static void parse_id3(AVFormatContext *s, AVIOContext *pb, + AVDictionary **metadata, int64_t *dts, + ID3v2ExtraMetaAPIC **apic, ID3v2ExtraMeta **extra_meta) +{ + static const char id3_priv_owner_ts[] = "com.apple.streaming.transportStreamTimestamp"; + ID3v2ExtraMeta *meta; + + ff_id3v2_read_dict(pb, metadata, ID3v2_DEFAULT_MAGIC, extra_meta); + for (meta = *extra_meta; meta; meta = meta->next) { + if (!strcmp(meta->tag, "PRIV")) { + ID3v2ExtraMetaPRIV *priv = meta->data; + if (priv->datasize == 8 && !strcmp(priv->owner, id3_priv_owner_ts)) { + /* 33-bit MPEG timestamp */ + int64_t ts = AV_RB64(priv->data); + av_log(s, AV_LOG_DEBUG, "HLS ID3 audio timestamp %"PRId64"\n", ts); + if ((ts & ~((1ULL << 33) - 1)) == 0) + *dts = ts; + else + av_log(s, AV_LOG_ERROR, "Invalid HLS ID3 audio timestamp %"PRId64"\n", ts); + } + } else if (!strcmp(meta->tag, "APIC") && apic) + *apic = meta->data; + } +} + +/* Check if the ID3 metadata contents have changed */ +static int id3_has_changed_values(struct playlist *pls, AVDictionary *metadata, + ID3v2ExtraMetaAPIC *apic) +{ + AVDictionaryEntry *entry = NULL; + AVDictionaryEntry *oldentry; + /* check that no keys have changed values */ + while ((entry = av_dict_get(metadata, "", entry, AV_DICT_IGNORE_SUFFIX))) { + oldentry = av_dict_get(pls->id3_initial, entry->key, NULL, AV_DICT_MATCH_CASE); + if (!oldentry || strcmp(oldentry->value, entry->value) != 0) + return 1; + } + + /* check if apic appeared */ + if (apic && (pls->ctx->nb_streams != 2 || !pls->ctx->streams[1]->attached_pic.data)) + return 1; + + if (apic) { + int size = pls->ctx->streams[1]->attached_pic.size; + if (size != apic->buf->size - FF_INPUT_BUFFER_PADDING_SIZE) + return 1; + + if (memcmp(apic->buf->data, pls->ctx->streams[1]->attached_pic.data, size) != 0) + return 1; + } + + return 0; +} + +/* Parse ID3 data and handle the found data */ +static void handle_id3(AVIOContext *pb, struct playlist *pls) +{ + AVDictionary *metadata = NULL; + ID3v2ExtraMetaAPIC *apic = NULL; + ID3v2ExtraMeta *extra_meta = NULL; + int64_t timestamp = AV_NOPTS_VALUE; + + parse_id3(pls->ctx, pb, &metadata, ×tamp, &apic, &extra_meta); + + if (timestamp != AV_NOPTS_VALUE) { + pls->id3_mpegts_timestamp = timestamp; + pls->id3_offset = 0; + } + + if (!pls->id3_found) { + /* initial ID3 tags */ + av_assert0(!pls->id3_deferred_extra); + pls->id3_found = 1; + + /* get picture attachment and set text metadata */ + if (pls->ctx->nb_streams) + ff_id3v2_parse_apic(pls->ctx, &extra_meta); + else + /* demuxer not yet opened, defer picture attachment */ + pls->id3_deferred_extra = extra_meta; + + av_dict_copy(&pls->ctx->metadata, metadata, 0); + pls->id3_initial = metadata; + + } else { + if (!pls->id3_changed && id3_has_changed_values(pls, metadata, apic)) { + avpriv_report_missing_feature(pls->ctx, "Changing ID3 metadata in HLS audio elementary stream"); + pls->id3_changed = 1; + } + av_dict_free(&metadata); + } + + if (!pls->id3_deferred_extra) + ff_id3v2_free_extra_meta(&extra_meta); +} + +/* Intercept and handle ID3 tags between URLContext and AVIOContext */ +static void intercept_id3(struct playlist *pls, uint8_t *buf, + int buf_size, int *len) { - struct segment *seg = var->segments[var->cur_seq_no - var->start_seq_no]; + /* intercept id3 tags, we do not want to pass them to the raw + * demuxer on all segment switches */ + int bytes; + int id3_buf_pos = 0; + int fill_buf = 0; + + /* gather all the id3 tags */ + while (1) { + /* see if we can retrieve enough data for ID3 header */ + if (*len < ID3v2_HEADER_SIZE && buf_size >= ID3v2_HEADER_SIZE) { + bytes = read_from_url(pls, buf + *len, ID3v2_HEADER_SIZE - *len, READ_COMPLETE); + if (bytes > 0) { + + if (bytes == ID3v2_HEADER_SIZE - *len) + /* no EOF yet, so fill the caller buffer again after + * we have stripped the ID3 tags */ + fill_buf = 1; + + *len += bytes; + + } else if (*len <= 0) { + /* error/EOF */ + *len = bytes; + fill_buf = 0; + } + } + + if (*len < ID3v2_HEADER_SIZE) + break; + + if (ff_id3v2_match(buf, ID3v2_DEFAULT_MAGIC)) { + struct segment *seg = pls->segments[pls->cur_seq_no - pls->start_seq_no]; + int64_t maxsize = seg->size >= 0 ? seg->size : 1024*1024; + int taglen = ff_id3v2_tag_len(buf); + int tag_got_bytes = FFMIN(taglen, *len); + int remaining = taglen - tag_got_bytes; + + if (taglen > maxsize) { + av_log(pls->ctx, AV_LOG_ERROR, "Too large HLS ID3 tag (%d > %"PRId64" bytes)\n", + taglen, maxsize); + break; + } + + /* + * Copy the id3 tag to our temporary id3 buffer. + * We could read a small id3 tag directly without memcpy, but + * we would still need to copy the large tags, and handling + * both of those cases together with the possibility for multiple + * tags would make the handling a bit complex. + */ + pls->id3_buf = av_fast_realloc(pls->id3_buf, &pls->id3_buf_size, id3_buf_pos + taglen); + if (!pls->id3_buf) + break; + memcpy(pls->id3_buf + id3_buf_pos, buf, tag_got_bytes); + id3_buf_pos += tag_got_bytes; + + /* strip the intercepted bytes */ + *len -= tag_got_bytes; + memmove(buf, buf + tag_got_bytes, *len); + av_log(pls->ctx, AV_LOG_DEBUG, "Stripped %d HLS ID3 bytes\n", tag_got_bytes); + + if (remaining > 0) { + /* read the rest of the tag in */ + if (read_from_url(pls, pls->id3_buf + id3_buf_pos, remaining, READ_COMPLETE) != remaining) + break; + id3_buf_pos += remaining; + av_log(pls->ctx, AV_LOG_DEBUG, "Stripped additional %d HLS ID3 bytes\n", remaining); + } + + } else { + /* no more ID3 tags */ + break; + } + } + + /* re-fill buffer for the caller unless EOF */ + if (*len >= 0 && (fill_buf || *len == 0)) { + bytes = read_from_url(pls, buf + *len, buf_size - *len, READ_NORMAL); + + /* ignore error if we already had some data */ + if (bytes >= 0) + *len += bytes; + else if (*len == 0) + *len = bytes; + } + + if (pls->id3_buf) { + /* Now parse all the ID3 tags */ + AVIOContext id3ioctx; + ffio_init_context(&id3ioctx, pls->id3_buf, id3_buf_pos, 0, NULL, NULL, NULL, NULL); + handle_id3(&id3ioctx, pls); + } + + if (pls->is_id3_timestamped == -1) + pls->is_id3_timestamped = (pls->id3_mpegts_timestamp != AV_NOPTS_VALUE); +} + +static int open_input(HLSContext *c, struct playlist *pls) +{ + AVDictionary *opts = NULL; + AVDictionary *opts2 = NULL; + int ret; + struct segment *seg = pls->segments[pls->cur_seq_no - pls->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, "headers", c->headers, 0); + av_dict_set(&opts, "seekable", "0", 0); + + // Same opts for key request (ffurl_open mutilates the opts so it cannot be used twice) + av_dict_copy(&opts2, opts, 0); + + if (seg->size >= 0) { + /* try to restrict the HTTP request to the part we want + * (if this is in fact a HTTP request) */ + av_dict_set_int(&opts, "offset", seg->url_offset, 0); + av_dict_set_int(&opts, "end_offset", seg->url_offset + seg->size, 0); + } + + av_log(pls->parent, AV_LOG_VERBOSE, "HLS request for url '%s', offset %"PRId64", playlist %d\n", + seg->url, seg->url_offset, pls->index); + if (seg->key_type == KEY_NONE) { - return ffurl_open(&var->input, seg->url, AVIO_FLAG_READ, - &var->parent->interrupt_callback, NULL); + ret = ffurl_open(&pls->input, seg->url, AVIO_FLAG_READ, + &pls->parent->interrupt_callback, &opts); + } 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)) { + if (strcmp(seg->key, pls->key_url)) { URLContext *uc; if (ffurl_open(&uc, seg->key, AVIO_FLAG_READ, - &var->parent->interrupt_callback, NULL) == 0) { - if (ffurl_read_complete(uc, var->key, sizeof(var->key)) - != sizeof(var->key)) { + &pls->parent->interrupt_callback, &opts2) == 0) { + if (ffurl_read_complete(uc, pls->key, sizeof(pls->key)) + != sizeof(pls->key)) { av_log(NULL, AV_LOG_ERROR, "Unable to read key file %s\n", seg->key); } @@ -352,49 +946,101 @@ static int open_input(struct variant *var) av_log(NULL, AV_LOG_ERROR, "Unable to open key file %s\n", seg->key); } - av_strlcpy(var->key_url, seg->key, sizeof(var->key_url)); + av_strlcpy(pls->key_url, seg->key, sizeof(pls->key_url)); } ff_data_to_hex(iv, seg->iv, sizeof(seg->iv), 0); - ff_data_to_hex(key, var->key, sizeof(var->key), 0); + ff_data_to_hex(key, pls->key, sizeof(pls->key), 0); iv[32] = key[32] = '\0'; if (strstr(seg->url, "://")) snprintf(url, sizeof(url), "crypto+%s", seg->url); else 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; - 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) { - ffurl_close(var->input); - var->input = NULL; - return ret; + if ((ret = ffurl_alloc(&pls->input, url, AVIO_FLAG_READ, + &pls->parent->interrupt_callback)) < 0) + goto cleanup; + av_opt_set(pls->input->priv_data, "key", key, 0); + av_opt_set(pls->input->priv_data, "iv", iv, 0); + + if ((ret = ffurl_connect(pls->input, &opts)) < 0) { + ffurl_close(pls->input); + pls->input = NULL; + goto cleanup; + } + ret = 0; + } + else + ret = AVERROR(ENOSYS); + + /* Seek to the requested position. If this was a HTTP request, the offset + * should already be where want it to, but this allows e.g. local testing + * without a HTTP server. */ + if (ret == 0 && seg->key_type == KEY_NONE) { + int seekret = ffurl_seek(pls->input, seg->url_offset, SEEK_SET); + if (seekret < 0) { + av_log(pls->parent, AV_LOG_ERROR, "Unable to seek to offset %"PRId64" of HLS segment '%s'\n", seg->url_offset, seg->url); + ret = seekret; + ffurl_close(pls->input); + pls->input = NULL; } - return 0; } - return AVERROR(ENOSYS); + +cleanup: + av_dict_free(&opts); + av_dict_free(&opts2); + pls->cur_seg_offset = 0; + return ret; +} + +static int64_t default_reload_interval(struct playlist *pls) +{ + return pls->n_segments > 0 ? + pls->segments[pls->n_segments - 1]->duration : + pls->target_duration; } static int read_data(void *opaque, uint8_t *buf, int buf_size) { - struct variant *v = opaque; + struct playlist *v = opaque; HLSContext *c = v->parent->priv_data; int ret, i; + int just_opened = 0; restart: + if (!v->needed) + return AVERROR_EOF; + if (!v->input) { + int64_t reload_interval; + + /* Check that the playlist is still needed before opening a new + * segment. */ + if (v->ctx && v->ctx->nb_streams && + v->parent->nb_streams >= v->stream_offset + v->ctx->nb_streams) { + v->needed = 0; + for (i = v->stream_offset; i < v->stream_offset + v->ctx->nb_streams; + i++) { + if (v->parent->streams[i]->discard < AVDISCARD_ALL) + v->needed = 1; + } + } + if (!v->needed) { + av_log(v->parent, AV_LOG_INFO, "No longer receiving playlist %d\n", + v->index); + return AVERROR_EOF; + } + /* If this is a live stream and the reload interval has elapsed since - * the last playlist reload, reload the variant playlists now. */ - int64_t reload_interval = v->n_segments > 0 ? - v->segments[v->n_segments - 1]->duration : - v->target_duration; + * the last playlist reload, reload the playlists now. */ + reload_interval = default_reload_interval(v); reload: if (!v->finished && av_gettime() - v->last_load_time >= reload_interval) { - if ((ret = parse_playlist(c, v->url, v, NULL)) < 0) + if ((ret = parse_playlist(c, v->url, v, NULL)) < 0) { + av_log(v->parent, AV_LOG_WARNING, "Failed to reload playlist %d\n", + v->index); return ret; + } /* If we need to reload the playlist again below (if * there's still no more segments), switch to a reload * interval of half the target duration. */ @@ -418,44 +1064,205 @@ reload: goto reload; } - ret = open_input(v); - if (ret < 0) + ret = open_input(c, v); + if (ret < 0) { + av_log(v->parent, AV_LOG_WARNING, "Failed to open segment of playlist %d\n", + v->index); return ret; + } + just_opened = 1; } - ret = ffurl_read(v->input, buf, buf_size); - if (ret > 0) + + ret = read_from_url(v, buf, buf_size, READ_NORMAL); + if (ret > 0) { + if (just_opened && v->is_id3_timestamped != 0) { + /* Intercept ID3 tags here, elementary audio streams are required + * to convey timestamps using them in the beginning of each segment. */ + intercept_id3(v, buf, buf_size, &ret); + } + return ret; + } ffurl_close(v->input); v->input = NULL; v->cur_seq_no++; - c->end_of_segment = 1; c->cur_seq_no = v->cur_seq_no; - if (v->ctx && v->ctx->nb_streams && - v->parent->nb_streams >= v->stream_offset + v->ctx->nb_streams) { - v->needed = 0; - for (i = v->stream_offset; i < v->stream_offset + v->ctx->nb_streams; - i++) { - if (v->parent->streams[i]->discard < AVDISCARD_ALL) - v->needed = 1; + goto restart; +} + +static int playlist_in_multiple_variants(HLSContext *c, struct playlist *pls) +{ + int variant_count = 0; + int i, j; + + for (i = 0; i < c->n_variants && variant_count < 2; i++) { + struct variant *v = c->variants[i]; + + for (j = 0; j < v->n_playlists; j++) { + if (v->playlists[j] == pls) { + variant_count++; + break; + } + } + } + + return variant_count >= 2; +} + +static void add_renditions_to_variant(HLSContext *c, struct variant *var, + enum AVMediaType type, const char *group_id) +{ + int i; + + for (i = 0; i < c->n_renditions; i++) { + struct rendition *rend = c->renditions[i]; + + if (rend->type == type && !strcmp(rend->group_id, group_id)) { + + if (rend->playlist) + /* rendition is an external playlist + * => add the playlist to the variant */ + dynarray_add(&var->playlists, &var->n_playlists, rend->playlist); + else + /* rendition is part of the variant main Media Playlist + * => add the rendition to the main Media Playlist */ + dynarray_add(&var->playlists[0]->renditions, + &var->playlists[0]->n_renditions, + rend); } } - if (!v->needed) { - av_log(v->parent, AV_LOG_INFO, "No longer receiving variant %d\n", - v->index); - return AVERROR_EOF; +} + +static void add_metadata_from_renditions(AVFormatContext *s, struct playlist *pls, + enum AVMediaType type) +{ + int rend_idx = 0; + int i; + + for (i = 0; i < pls->ctx->nb_streams; i++) { + AVStream *st = s->streams[pls->stream_offset + i]; + + if (st->codec->codec_type != type) + continue; + + for (; rend_idx < pls->n_renditions; rend_idx++) { + struct rendition *rend = pls->renditions[rend_idx]; + + if (rend->type != type) + continue; + + if (rend->language[0]) + av_dict_set(&st->metadata, "language", rend->language, 0); + if (rend->name[0]) + av_dict_set(&st->metadata, "comment", rend->name, 0); + + st->disposition |= rend->disposition; + } + if (rend_idx >=pls->n_renditions) + break; } - goto restart; +} + +/* if timestamp was in valid range: returns 1 and sets seq_no + * if not: returns 0 and sets seq_no to closest segment */ +static int find_timestamp_in_playlist(HLSContext *c, struct playlist *pls, + int64_t timestamp, int *seq_no) +{ + int i; + int64_t pos = c->first_timestamp == AV_NOPTS_VALUE ? + 0 : c->first_timestamp; + + if (timestamp < pos) { + *seq_no = pls->start_seq_no; + return 0; + } + + for (i = 0; i < pls->n_segments; i++) { + int64_t diff = pos + pls->segments[i]->duration - timestamp; + if (diff > 0) { + *seq_no = pls->start_seq_no + i; + return 1; + } + pos += pls->segments[i]->duration; + } + + *seq_no = pls->start_seq_no + pls->n_segments - 1; + + return 0; +} + +static int select_cur_seq_no(HLSContext *c, struct playlist *pls) +{ + int seq_no; + + if (!pls->finished && !c->first_packet && + av_gettime() - pls->last_load_time >= default_reload_interval(pls)) + /* reload the playlist since it was suspended */ + parse_playlist(c, pls->url, pls, NULL); + + /* If playback is already in progress (we are just selecting a new + * playlist) and this is a complete file, find the matching segment + * by counting durations. */ + if (pls->finished && c->cur_timestamp != AV_NOPTS_VALUE) { + find_timestamp_in_playlist(c, pls, c->cur_timestamp, &seq_no); + return seq_no; + } + + if (!pls->finished) { + if (!c->first_packet && /* we are doing a segment selection during playback */ + c->cur_seq_no >= pls->start_seq_no && + c->cur_seq_no < pls->start_seq_no + pls->n_segments) + /* While spec 3.4.3 says that we cannot assume anything about the + * content at the same sequence number on different playlists, + * in practice this seems to work and doing it otherwise would + * require us to download a segment to inspect its timestamps. */ + return c->cur_seq_no; + + /* If this is a live stream with more than 3 segments, start at the + * third last segment. */ + if (pls->n_segments > 3) + return pls->start_seq_no + pls->n_segments - 3; + } + + /* Otherwise just start on the first segment. */ + return pls->start_seq_no; } 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; + c->first_packet = 1; + c->first_timestamp = AV_NOPTS_VALUE; + c->cur_timestamp = AV_NOPTS_VALUE; + + // 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); + + // get the previous headers & set back to null if string size is zero + av_freep(&c->headers); + av_opt_get(u->priv_data, "headers", 0, (uint8_t**)&(c->headers)); + if (c->headers && !strlen(c->headers)) + av_freep(&c->headers); + } + if ((ret = parse_playlist(c, s->filename, NULL, s->pb)) < 0) goto fail; @@ -464,17 +1271,17 @@ static int hls_read_header(AVFormatContext *s) ret = AVERROR_EOF; goto fail; } - /* If the playlist only contained variants, parse each individual - * variant playlist. */ - if (c->n_variants > 1 || c->variants[0]->n_segments == 0) { - for (i = 0; i < c->n_variants; i++) { - struct variant *v = c->variants[i]; - if ((ret = parse_playlist(c, v->url, v, NULL)) < 0) + /* If the playlist only contained playlists (Master Playlist), + * parse each individual playlist. */ + if (c->n_playlists > 1 || c->playlists[0]->n_segments == 0) { + for (i = 0; i < c->n_playlists; i++) { + struct playlist *pls = c->playlists[i]; + if ((ret = parse_playlist(c, pls->url, pls, NULL)) < 0) goto fail; } } - if (c->variants[0]->n_segments == 0) { + if (c->variants[0]->playlists[0]->n_segments == 0) { av_log(NULL, AV_LOG_WARNING, "Empty playlist\n"); ret = AVERROR_EOF; goto fail; @@ -482,96 +1289,137 @@ static int hls_read_header(AVFormatContext *s) /* If this isn't a live stream, calculate the total duration of the * stream. */ - if (c->variants[0]->finished) { + if (c->variants[0]->playlists[0]->finished) { int64_t duration = 0; - for (i = 0; i < c->variants[0]->n_segments; i++) - duration += c->variants[0]->segments[i]->duration; + for (i = 0; i < c->variants[0]->playlists[0]->n_segments; i++) + duration += c->variants[0]->playlists[0]->segments[i]->duration; s->duration = duration; } - /* Open the demuxer for each variant */ + /* Associate renditions with variants */ for (i = 0; i < c->n_variants; i++) { - struct variant *v = c->variants[i]; + struct variant *var = c->variants[i]; + + if (var->audio_group[0]) + add_renditions_to_variant(c, var, AVMEDIA_TYPE_AUDIO, var->audio_group); + if (var->video_group[0]) + add_renditions_to_variant(c, var, AVMEDIA_TYPE_VIDEO, var->video_group); + if (var->subtitles_group[0]) + add_renditions_to_variant(c, var, AVMEDIA_TYPE_SUBTITLE, var->subtitles_group); + } + + /* Open the demuxer for each playlist */ + for (i = 0; i < c->n_playlists; i++) { + struct playlist *pls = c->playlists[i]; AVInputFormat *in_fmt = NULL; - char bitrate_str[20]; - AVProgram *program; - if (v->n_segments == 0) + if (pls->n_segments == 0) continue; - if (!(v->ctx = avformat_alloc_context())) { + if (!(pls->ctx = avformat_alloc_context())) { ret = AVERROR(ENOMEM); goto fail; } - v->index = i; - v->needed = 1; - v->parent = s; - - /* If this is a live stream with more than 3 segments, start at the - * third last segment. */ - v->cur_seq_no = v->start_seq_no; - if (!v->finished && v->n_segments > 3) - v->cur_seq_no = v->start_seq_no + v->n_segments - 3; + pls->index = i; + pls->needed = 1; + pls->parent = s; + pls->cur_seq_no = select_cur_seq_no(c, pls); - v->read_buffer = av_malloc(INITIAL_BUFFER_SIZE); - ffio_init_context(&v->pb, v->read_buffer, INITIAL_BUFFER_SIZE, 0, v, + pls->read_buffer = av_malloc(INITIAL_BUFFER_SIZE); + ffio_init_context(&pls->pb, pls->read_buffer, INITIAL_BUFFER_SIZE, 0, pls, read_data, NULL, NULL); - v->pb.seekable = 0; - ret = av_probe_input_buffer(&v->pb, &in_fmt, v->segments[0]->url, + pls->pb.seekable = 0; + ret = av_probe_input_buffer(&pls->pb, &in_fmt, pls->segments[0]->url, NULL, 0, 0); if (ret < 0) { /* Free the ctx - it isn't initialized properly at this point, * 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. */ - avformat_free_context(v->ctx); - v->ctx = NULL; + av_log(s, AV_LOG_ERROR, "Error when loading first segment '%s'\n", pls->segments[0]->url); + avformat_free_context(pls->ctx); + pls->ctx = NULL; goto fail; } - v->ctx->pb = &v->pb; - v->stream_offset = stream_offset; - ret = avformat_open_input(&v->ctx, v->segments[0]->url, in_fmt, NULL); + pls->ctx->pb = &pls->pb; + pls->stream_offset = stream_offset; + ret = avformat_open_input(&pls->ctx, pls->segments[0]->url, in_fmt, NULL); if (ret < 0) goto fail; - v->ctx->ctx_flags &= ~AVFMTCTX_NOHEADER; - ret = avformat_find_stream_info(v->ctx, NULL); + if (pls->id3_deferred_extra && pls->ctx->nb_streams == 1) { + ff_id3v2_parse_apic(pls->ctx, &pls->id3_deferred_extra); + avformat_queue_attached_pictures(pls->ctx); + ff_id3v2_free_extra_meta(&pls->id3_deferred_extra); + pls->id3_deferred_extra = NULL; + } + + pls->ctx->ctx_flags &= ~AVFMTCTX_NOHEADER; + ret = avformat_find_stream_info(pls->ctx, NULL); if (ret < 0) goto fail; - snprintf(bitrate_str, sizeof(bitrate_str), "%d", v->bandwidth); - program = av_new_program(s, i); - if (!program) - goto fail; - av_dict_set(&program->metadata, "variant_bitrate", bitrate_str, 0); + if (pls->is_id3_timestamped == -1) + av_log(s, AV_LOG_WARNING, "No expected HTTP requests have been made\n"); - /* Create new AVStreams for each stream in this variant */ - for (j = 0; j < v->ctx->nb_streams; j++) { + /* Create new AVStreams for each stream in this playlist */ + for (j = 0; j < pls->ctx->nb_streams; j++) { AVStream *st = avformat_new_stream(s, NULL); - AVStream *ist = v->ctx->streams[j]; + AVStream *ist = pls->ctx->streams[j]; if (!st) { ret = AVERROR(ENOMEM); goto fail; } - ff_program_add_stream_index(s, i, stream_offset + j); st->id = i; - avpriv_set_pts_info(st, ist->pts_wrap_bits, ist->time_base.num, ist->time_base.den); - avcodec_copy_context(st->codec, v->ctx->streams[j]->codec); - if (v->bandwidth) - av_dict_set(&st->metadata, "variant_bitrate", bitrate_str, - 0); + + avcodec_copy_context(st->codec, pls->ctx->streams[j]->codec); + + if (pls->is_id3_timestamped) /* custom timestamps via id3 */ + avpriv_set_pts_info(st, 33, 1, MPEG_TIME_BASE); + else + avpriv_set_pts_info(st, ist->pts_wrap_bits, ist->time_base.num, ist->time_base.den); } - stream_offset += v->ctx->nb_streams; + + add_metadata_from_renditions(s, pls, AVMEDIA_TYPE_AUDIO); + add_metadata_from_renditions(s, pls, AVMEDIA_TYPE_VIDEO); + add_metadata_from_renditions(s, pls, AVMEDIA_TYPE_SUBTITLE); + + stream_offset += pls->ctx->nb_streams; } - c->first_packet = 1; - c->first_timestamp = AV_NOPTS_VALUE; - c->seek_timestamp = AV_NOPTS_VALUE; + /* Create a program for each variant */ + for (i = 0; i < c->n_variants; i++) { + struct variant *v = c->variants[i]; + AVProgram *program; + + program = av_new_program(s, i); + if (!program) + goto fail; + av_dict_set_int(&program->metadata, "variant_bitrate", v->bandwidth, 0); + + for (j = 0; j < v->n_playlists; j++) { + struct playlist *pls = v->playlists[j]; + int is_shared = playlist_in_multiple_variants(c, pls); + int k; + + for (k = 0; k < pls->ctx->nb_streams; k++) { + struct AVStream *st = s->streams[pls->stream_offset + k]; + + ff_program_add_stream_index(s, i, pls->stream_offset + k); + + /* Set variant_bitrate for streams unique to this variant */ + if (!is_shared && v->bandwidth) + av_dict_set_int(&st->metadata, "variant_bitrate", v->bandwidth, 0); + } + } + } return 0; fail: + free_playlist_list(c); free_variant_list(c); + free_rendition_list(c); return ret; } @@ -581,127 +1429,171 @@ static int recheck_discard_flags(AVFormatContext *s, int first) int i, changed = 0; /* Check if any new streams are needed */ - for (i = 0; i < c->n_variants; i++) - c->variants[i]->cur_needed = 0;; + for (i = 0; i < c->n_playlists; i++) + c->playlists[i]->cur_needed = 0; for (i = 0; i < s->nb_streams; i++) { AVStream *st = s->streams[i]; - struct variant *var = c->variants[s->streams[i]->id]; + struct playlist *pls = c->playlists[s->streams[i]->id]; if (st->discard < AVDISCARD_ALL) - var->cur_needed = 1; + pls->cur_needed = 1; } - for (i = 0; i < c->n_variants; i++) { - struct variant *v = c->variants[i]; - if (v->cur_needed && !v->needed) { - v->needed = 1; + for (i = 0; i < c->n_playlists; i++) { + struct playlist *pls = c->playlists[i]; + if (pls->cur_needed && !pls->needed) { + pls->needed = 1; changed = 1; - v->cur_seq_no = c->cur_seq_no; - v->pb.eof_reached = 0; - av_log(s, AV_LOG_INFO, "Now receiving variant %d\n", i); - } else if (first && !v->cur_needed && v->needed) { - if (v->input) - ffurl_close(v->input); - v->input = NULL; - v->needed = 0; + pls->cur_seq_no = select_cur_seq_no(c, pls); + pls->pb.eof_reached = 0; + if (c->cur_timestamp != AV_NOPTS_VALUE) { + /* catch up */ + pls->seek_timestamp = c->cur_timestamp; + pls->seek_flags = AVSEEK_FLAG_ANY; + pls->seek_stream_index = -1; + } + av_log(s, AV_LOG_INFO, "Now receiving playlist %d, segment %d\n", i, pls->cur_seq_no); + } else if (first && !pls->cur_needed && pls->needed) { + if (pls->input) + ffurl_close(pls->input); + pls->input = NULL; + pls->needed = 0; changed = 1; - av_log(s, AV_LOG_INFO, "No longer receiving variant %d\n", i); + av_log(s, AV_LOG_INFO, "No longer receiving playlist %d\n", i); } } return changed; } +static void fill_timing_for_id3_timestamped_stream(struct playlist *pls) +{ + if (pls->id3_offset >= 0) { + pls->pkt.dts = pls->id3_mpegts_timestamp + + av_rescale_q(pls->id3_offset, + pls->ctx->streams[pls->pkt.stream_index]->time_base, + MPEG_TIME_BASE_Q); + if (pls->pkt.duration) + pls->id3_offset += pls->pkt.duration; + else + pls->id3_offset = -1; + } else { + /* there have been packets with unknown duration + * since the last id3 tag, should not normally happen */ + pls->pkt.dts = AV_NOPTS_VALUE; + } + + if (pls->pkt.duration) + pls->pkt.duration = av_rescale_q(pls->pkt.duration, + pls->ctx->streams[pls->pkt.stream_index]->time_base, + MPEG_TIME_BASE_Q); + + pls->pkt.pts = AV_NOPTS_VALUE; +} + +static AVRational get_timebase(struct playlist *pls) +{ + if (pls->is_id3_timestamped) + return MPEG_TIME_BASE_Q; + + return pls->ctx->streams[pls->pkt.stream_index]->time_base; +} + +static int compare_ts_with_wrapdetect(int64_t ts_a, struct playlist *pls_a, + int64_t ts_b, struct playlist *pls_b) +{ + int64_t scaled_ts_a = av_rescale_q(ts_a, get_timebase(pls_a), MPEG_TIME_BASE_Q); + int64_t scaled_ts_b = av_rescale_q(ts_b, get_timebase(pls_b), MPEG_TIME_BASE_Q); + + return av_compare_mod(scaled_ts_a, scaled_ts_b, 1LL << 33); +} + static int hls_read_packet(AVFormatContext *s, AVPacket *pkt) { HLSContext *c = s->priv_data; - int ret, i, minvariant = -1; + int ret, i, minplaylist = -1; - if (c->first_packet) { - recheck_discard_flags(s, 1); - c->first_packet = 0; - } + recheck_discard_flags(s, c->first_packet); -start: - c->end_of_segment = 0; - for (i = 0; i < c->n_variants; i++) { - struct variant *var = c->variants[i]; - /* Make sure we've got one buffered packet from each open variant + for (i = 0; i < c->n_playlists; i++) { + struct playlist *pls = c->playlists[i]; + /* Make sure we've got one buffered packet from each open playlist * stream */ - if (var->needed && !var->pkt.data) { + if (pls->needed && !pls->pkt.data) { while (1) { int64_t ts_diff; - AVStream *st; - ret = av_read_frame(var->ctx, &var->pkt); + AVRational tb; + ret = av_read_frame(pls->ctx, &pls->pkt); if (ret < 0) { - if (!var->pb.eof_reached) + if (!avio_feof(&pls->pb) && ret != AVERROR_EOF) return ret; - reset_packet(&var->pkt); + reset_packet(&pls->pkt); break; } else { + /* stream_index check prevents matching picture attachments etc. */ + if (pls->is_id3_timestamped && pls->pkt.stream_index == 0) { + /* audio elementary streams are id3 timestamped */ + fill_timing_for_id3_timestamped_stream(pls); + } + if (c->first_timestamp == AV_NOPTS_VALUE && - var->pkt.dts != AV_NOPTS_VALUE) - c->first_timestamp = av_rescale_q(var->pkt.dts, - var->ctx->streams[var->pkt.stream_index]->time_base, - AV_TIME_BASE_Q); + pls->pkt.dts != AV_NOPTS_VALUE) + c->first_timestamp = av_rescale_q(pls->pkt.dts, + get_timebase(pls), AV_TIME_BASE_Q); } - if (c->seek_timestamp == AV_NOPTS_VALUE) + if (pls->seek_timestamp == AV_NOPTS_VALUE) break; - if (var->pkt.dts == AV_NOPTS_VALUE) { - c->seek_timestamp = AV_NOPTS_VALUE; - break; - } + if (pls->seek_stream_index < 0 || + pls->seek_stream_index == pls->pkt.stream_index) { - st = var->ctx->streams[var->pkt.stream_index]; - ts_diff = av_rescale_rnd(var->pkt.dts, AV_TIME_BASE, - st->time_base.den, AV_ROUND_DOWN) - - c->seek_timestamp; - if (ts_diff >= 0 && (c->seek_flags & AVSEEK_FLAG_ANY || - var->pkt.flags & AV_PKT_FLAG_KEY)) { - c->seek_timestamp = AV_NOPTS_VALUE; - break; + if (pls->pkt.dts == AV_NOPTS_VALUE) { + pls->seek_timestamp = AV_NOPTS_VALUE; + break; + } + + tb = get_timebase(pls); + ts_diff = av_rescale_rnd(pls->pkt.dts, AV_TIME_BASE, + tb.den, AV_ROUND_DOWN) - + pls->seek_timestamp; + if (ts_diff >= 0 && (pls->seek_flags & AVSEEK_FLAG_ANY || + pls->pkt.flags & AV_PKT_FLAG_KEY)) { + pls->seek_timestamp = AV_NOPTS_VALUE; + break; + } } - av_free_packet(&var->pkt); - reset_packet(&var->pkt); + av_free_packet(&pls->pkt); + reset_packet(&pls->pkt); } } - /* Check if this stream still is on an earlier segment number, or - * has the packet with the lowest dts */ - if (var->pkt.data) { - struct variant *minvar = minvariant < 0 ? - NULL : c->variants[minvariant]; - if (minvariant < 0 || var->cur_seq_no < minvar->cur_seq_no) { - minvariant = i; - } else if (var->cur_seq_no == minvar->cur_seq_no) { - int64_t dts = var->pkt.dts; - int64_t mindts = minvar->pkt.dts; - AVStream *st = var->ctx->streams[var->pkt.stream_index]; - AVStream *minst = minvar->ctx->streams[minvar->pkt.stream_index]; - - if (dts == AV_NOPTS_VALUE) { - minvariant = i; - } else if (mindts != AV_NOPTS_VALUE) { - if (st->start_time != AV_NOPTS_VALUE) - dts -= st->start_time; - if (minst->start_time != AV_NOPTS_VALUE) - mindts -= minst->start_time; - - if (av_compare_ts(dts, st->time_base, - mindts, minst->time_base) < 0) - minvariant = i; - } + /* Check if this stream has the packet with the lowest dts */ + if (pls->pkt.data) { + struct playlist *minpls = minplaylist < 0 ? + NULL : c->playlists[minplaylist]; + if (minplaylist < 0) { + minplaylist = i; + } else { + int64_t dts = pls->pkt.dts; + int64_t mindts = minpls->pkt.dts; + + if (dts == AV_NOPTS_VALUE || + (mindts != AV_NOPTS_VALUE && compare_ts_with_wrapdetect(dts, pls, mindts, minpls) < 0)) + minplaylist = i; } } } - if (c->end_of_segment) { - if (recheck_discard_flags(s, 0)) - goto start; - } + /* If we got a packet, return it */ - if (minvariant >= 0) { - *pkt = c->variants[minvariant]->pkt; - pkt->stream_index += c->variants[minvariant]->stream_offset; - reset_packet(&c->variants[minvariant]->pkt); + if (minplaylist >= 0) { + struct playlist *pls = c->playlists[minplaylist]; + *pkt = pls->pkt; + pkt->stream_index += pls->stream_offset; + reset_packet(&c->playlists[minplaylist]->pkt); + + if (pkt->dts != AV_NOPTS_VALUE) + c->cur_timestamp = av_rescale_q(pkt->dts, + pls->ctx->streams[pls->pkt.stream_index]->time_base, + AV_TIME_BASE_Q); + return 0; } return AVERROR_EOF; @@ -711,7 +1603,9 @@ static int hls_close(AVFormatContext *s) { HLSContext *c = s->priv_data; + free_playlist_list(c); free_variant_list(c); + free_rendition_list(c); return 0; } @@ -719,58 +1613,80 @@ static int hls_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp, int flags) { HLSContext *c = s->priv_data; - int i, j, ret; + struct playlist *seek_pls = NULL; + int i, seq_no; + int64_t first_timestamp, seek_timestamp, duration; - if ((flags & AVSEEK_FLAG_BYTE) || !c->variants[0]->finished) + if ((flags & AVSEEK_FLAG_BYTE) || + !(c->variants[0]->playlists[0]->finished || c->variants[0]->playlists[0]->type == PLS_TYPE_EVENT)) return AVERROR(ENOSYS); - c->seek_flags = flags; - c->seek_timestamp = stream_index < 0 ? timestamp : - av_rescale_rnd(timestamp, AV_TIME_BASE, - s->streams[stream_index]->time_base.den, - flags & AVSEEK_FLAG_BACKWARD ? - AV_ROUND_DOWN : AV_ROUND_UP); - timestamp = av_rescale_rnd(timestamp, AV_TIME_BASE, stream_index >= 0 ? - s->streams[stream_index]->time_base.den : - AV_TIME_BASE, flags & AVSEEK_FLAG_BACKWARD ? - AV_ROUND_DOWN : AV_ROUND_UP); - if (s->duration < c->seek_timestamp) { - c->seek_timestamp = AV_NOPTS_VALUE; + first_timestamp = c->first_timestamp == AV_NOPTS_VALUE ? + 0 : c->first_timestamp; + + seek_timestamp = av_rescale_rnd(timestamp, AV_TIME_BASE, + s->streams[stream_index]->time_base.den, + flags & AVSEEK_FLAG_BACKWARD ? + AV_ROUND_DOWN : AV_ROUND_UP); + + duration = s->duration == AV_NOPTS_VALUE ? + 0 : s->duration; + + if (0 < duration && duration < seek_timestamp - first_timestamp) return AVERROR(EIO); + + /* find the playlist with the specified stream */ + for (i = 0; i < c->n_playlists; i++) { + struct playlist *pls = c->playlists[i]; + if (stream_index >= pls->stream_offset && + stream_index - pls->stream_offset < pls->ctx->nb_streams) { + seek_pls = pls; + break; + } } + /* check if the timestamp is valid for the playlist with the + * specified stream index */ + if (!seek_pls || !find_timestamp_in_playlist(c, seek_pls, seek_timestamp, &seq_no)) + return AVERROR(EIO); - ret = AVERROR(EIO); - for (i = 0; i < c->n_variants; i++) { + /* set segment now so we do not need to search again below */ + seek_pls->cur_seq_no = seq_no; + seek_pls->seek_stream_index = stream_index - seek_pls->stream_offset; + + for (i = 0; i < c->n_playlists; i++) { /* Reset reading */ - struct variant *var = c->variants[i]; - int64_t pos = c->first_timestamp == AV_NOPTS_VALUE ? - 0 : c->first_timestamp; - if (var->input) { - ffurl_close(var->input); - var->input = NULL; + struct playlist *pls = c->playlists[i]; + if (pls->input) { + ffurl_close(pls->input); + pls->input = NULL; } - av_free_packet(&var->pkt); - reset_packet(&var->pkt); - var->pb.eof_reached = 0; + av_free_packet(&pls->pkt); + reset_packet(&pls->pkt); + pls->pb.eof_reached = 0; /* Clear any buffered data */ - var->pb.buf_end = var->pb.buf_ptr = var->pb.buffer; + pls->pb.buf_end = pls->pb.buf_ptr = pls->pb.buffer; /* Reset the pos, to let the mpegts demuxer know we've seeked. */ - var->pb.pos = 0; - - /* Locate the segment that contains the target timestamp */ - for (j = 0; j < var->n_segments; j++) { - if (timestamp >= pos && - timestamp < pos + var->segments[j]->duration) { - var->cur_seq_no = var->start_seq_no + j; - ret = 0; - break; - } - pos += var->segments[j]->duration; + pls->pb.pos = 0; + /* Flush the packet queue of the subdemuxer. */ + ff_read_frame_flush(pls->ctx); + + pls->seek_timestamp = seek_timestamp; + pls->seek_flags = flags; + + if (pls != seek_pls) { + /* set closest segment seq_no for playlists not handled above */ + find_timestamp_in_playlist(c, pls, seek_timestamp, &pls->cur_seq_no); + /* seek the playlist to the given position without taking + * keyframes into account since this playlist does not have the + * specified stream where we should look for the keyframes */ + pls->seek_stream_index = -1; + pls->seek_flags |= AVSEEK_FLAG_ANY; } - if (ret) - c->seek_timestamp = AV_NOPTS_VALUE; } - return ret; + + c->cur_timestamp = seek_timestamp; + + return 0; } static int hls_probe(AVProbeData *p) diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c index aa38d05..11f1e5b 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 */ @@ -31,11 +31,12 @@ #include "avformat.h" #include "internal.h" -typedef struct ListEntry { - char name[1024]; - int duration; - struct ListEntry *next; -} ListEntry; +typedef struct HLSSegment { + char filename[1024]; + double duration; /* in seconds */ + + struct HLSSegment *next; +} HLSSegment; typedef struct HLSContext { const AVClass *class; // Class for private options. @@ -43,20 +44,26 @@ typedef struct HLSContext { int64_t sequence; int64_t start_sequence; AVOutputFormat *oformat; + AVFormatContext *avf; + float time; // Set by a private option. - int size; // Set by a private option. + int max_nb_segments; // Set by a private option. int wrap; // Set by a private option. + int64_t recording_time; int has_video; int64_t start_pts; int64_t end_pts; - int64_t duration; // last segment duration computed so far, in seconds + double duration; // last segment duration computed so far, in seconds int nb_entries; - ListEntry *list; - ListEntry *end_list; + + HLSSegment *segments; + HLSSegment *last_segment; + char *basename; char *baseurl; + AVIOContext *pb; } HLSContext; @@ -72,6 +79,7 @@ static int hls_mux_init(AVFormatContext *s) oc->oformat = hls->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; @@ -84,28 +92,29 @@ static int hls_mux_init(AVFormatContext *s) return 0; } -static int append_entry(HLSContext *hls, uint64_t duration) +/* Create a new segment and append it to the segment list */ +static int hls_append_segment(HLSContext *hls, double duration) { - ListEntry *en = av_malloc(sizeof(*en)); + HLSSegment *en = av_malloc(sizeof(*en)); if (!en) return AVERROR(ENOMEM); - av_strlcpy(en->name, av_basename(hls->avf->filename), sizeof(en->name)); + av_strlcpy(en->filename, av_basename(hls->avf->filename), sizeof(en->filename)); en->duration = duration; en->next = NULL; - if (!hls->list) - hls->list = en; + if (!hls->segments) + hls->segments = en; else - hls->end_list->next = en; + hls->last_segment->next = en; - hls->end_list = en; + hls->last_segment = en; - if (hls->nb_entries >= hls->size) { - en = hls->list; - hls->list = en->next; + if (hls->max_nb_segments && hls->nb_entries >= hls->max_nb_segments) { + en = hls->segments; + hls->segments = en->next; av_free(en); } else hls->nb_entries++; @@ -115,9 +124,9 @@ static int append_entry(HLSContext *hls, uint64_t duration) return 0; } -static void free_entries(HLSContext *hls) +static void hls_free_segments(HLSContext *hls) { - ListEntry *p = hls->list, *en; + HLSSegment *p = hls->segments, *en; while(p) { en = p; @@ -129,18 +138,18 @@ static void free_entries(HLSContext *hls) static int hls_window(AVFormatContext *s, int last) { HLSContext *hls = s->priv_data; - ListEntry *en; + HLSSegment *en; int target_duration = 0; int ret = 0; - int64_t sequence = FFMAX(hls->start_sequence, hls->sequence - hls->size); + int64_t sequence = FFMAX(hls->start_sequence, hls->sequence - hls->nb_entries); if ((ret = avio_open2(&hls->pb, s->filename, AVIO_FLAG_WRITE, &s->interrupt_callback, NULL)) < 0) goto fail; - for (en = hls->list; en; en = en->next) { + for (en = hls->segments; en; en = en->next) { if (target_duration < en->duration) - target_duration = en->duration; + target_duration = ceil(en->duration); } avio_printf(hls->pb, "#EXTM3U\n"); @@ -151,11 +160,11 @@ static int hls_window(AVFormatContext *s, int last) av_log(s, AV_LOG_VERBOSE, "EXT-X-MEDIA-SEQUENCE:%"PRId64"\n", sequence); - for (en = hls->list; en; en = en->next) { - avio_printf(hls->pb, "#EXTINF:%d,\n", en->duration); + for (en = hls->segments; en; en = en->next) { + avio_printf(hls->pb, "#EXTINF:%f,\n", en->duration); if (hls->baseurl) avio_printf(hls->pb, "%s", hls->baseurl); - avio_printf(hls->pb, "%s\n", en->name); + avio_printf(hls->pb, "%s\n", en->filename); } if (last) @@ -173,8 +182,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->sequence % c->wrap : c->sequence) < 0) + c->basename, c->wrap ? c->sequence % c->wrap : c->sequence) < 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, @@ -238,7 +249,7 @@ static int hls_write_header(AVFormatContext *s) goto fail; if ((ret = avformat_write_header(hls->avf, NULL)) < 0) - return ret; + goto fail; fail: @@ -256,6 +267,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) { @@ -266,16 +278,18 @@ 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 - hls->duration = av_rescale(pkt->pts - hls->end_pts, - st->time_base.num, st->time_base.den); + is_ref_pkt = can_split = 0; + + if (is_ref_pkt) + hls->duration = (double)(pkt->pts - hls->end_pts) + * st->time_base.num / st->time_base.den; if (can_split && av_compare_ts(pkt->pts - hls->start_pts, st->time_base, end_pts, AV_TIME_BASE_Q) >= 0) { - ret = append_entry(hls, hls->duration); + ret = hls_append_segment(hls, hls->duration); if (ret) return ret; @@ -296,7 +310,7 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) return ret; } - ret = ff_write_chained(oc, pkt->stream_index, pkt, s); + ret = ff_write_chained(oc, pkt->stream_index, pkt, s, 0); return ret; } @@ -310,10 +324,10 @@ static int hls_write_trailer(struct AVFormatContext *s) avio_closep(&oc->pb); avformat_free_context(oc); av_free(hls->basename); - append_entry(hls, hls->duration); + hls_append_segment(hls, hls->duration); hls_window(s, 1); - free_entries(hls); + hls_free_segments(hls); avio_close(hls->pb); return 0; } @@ -321,10 +335,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(start_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(start_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(max_nb_segments), 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}, {"hls_base_url", "url to prepend to each playlist entry", OFFSET(baseurl), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E}, { NULL }, }; diff --git a/libavformat/hlsproto.c b/libavformat/hlsproto.c index ec357de..e607c10 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 (!avio_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 54ac6c2..1320fa5 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 */ @@ -92,7 +92,7 @@ static int hnm_read_header(AVFormatContext *s) hnm->currentframe = 0; - if (hnm->width < 320 || hnm->width > 640 || + if (hnm->width < 256 || hnm->width > 640 || hnm->height < 150 || hnm->height > 480) { av_log(s, AV_LOG_ERROR, "invalid resolution: %ux%u\n", hnm->width, hnm->height); @@ -150,7 +150,7 @@ static int hnm_read_packet(AVFormatContext *s, AVPacket *pkt) chunk_id = avio_rl16(pb); avio_skip(pb, 2); - if (chunk_size > hnm->superchunk_remaining) { + if (chunk_size > hnm->superchunk_remaining || !chunk_size) { av_log(s, AV_LOG_ERROR, "invalid chunk size: %"PRIu32", offset: %"PRId64"\n", chunk_size, avio_tell(pb)); diff --git a/libavformat/http.c b/libavformat/http.c index f82002c..018d25c 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 */ @@ -36,7 +36,7 @@ #include "os_support.h" #include "url.h" -/* 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 @@ -64,6 +64,7 @@ typedef struct { /* Set if the server correctly handles Connection: close and will close * the connection after feeding us the content. */ int willclose; + int seekable; /**< Control seekability, 0 = disable, 1 = enable, -1 = probe. */ int chunked_post; /* A flag which indicates if the end of chunked encoding has been sent. */ int end_chunked_post; @@ -73,6 +74,9 @@ typedef struct { int multiple_requests; uint8_t *post_data; int post_datalen; + int is_akamai; + int is_mediagateway; + char *cookies; ///< holds newline (\n) delimited Set-Cookie header field values (without the "Set-Cookie: " field name) int icy; /* how much data was read since the last ICY metadata packet */ int icy_data_read; @@ -97,14 +101,16 @@ typedef struct { #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", "set custom HTTP headers, can override built in default headers", OFFSET(headers), AV_OPT_TYPE_STRING, { 0 }, 0, 0, D | E }, { "content_type", "set a specific content type for the POST messages", 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 }, - { "user-agent", "override User-Agent header, for compatibility with ffmpeg", OFFSET(user_agent), AV_OPT_TYPE_STRING, { .str = DEFAULT_USER_AGENT }, 0, 0, D }, + { "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", "set custom HTTP post data", OFFSET(post_data), AV_OPT_TYPE_BINARY, .flags = D | E }, { "mime_type", "export the MIME type", OFFSET(mime_type), AV_OPT_TYPE_STRING, { 0 }, 0, 0, AV_OPT_FLAG_EXPORT | AV_OPT_FLAG_READONLY }, + { "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 = 1 }, 0, 1, D }, { "icy_metadata_headers", "return ICY metadata headers", OFFSET(icy_metadata_headers), AV_OPT_TYPE_STRING, { 0 }, 0, 0, AV_OPT_FLAG_EXPORT }, { "icy_metadata_packet", "return current ICY metadata packet", OFFSET(icy_metadata_packet), AV_OPT_TYPE_STRING, { 0 }, 0, 0, AV_OPT_FLAG_EXPORT }, @@ -212,8 +218,7 @@ redo: 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; @@ -221,8 +226,7 @@ redo: 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; @@ -231,8 +235,7 @@ redo: 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 @@ -246,8 +249,7 @@ redo: fail: if (s->hd) - ffurl_close(s->hd); - s->hd = NULL; + ffurl_closep(&s->hd); return AVERROR(EIO); } @@ -276,7 +278,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; s->location = av_strdup(uri); @@ -379,7 +384,8 @@ static void parse_content_range(URLContext *h, const char *p) if ((slash = strchr(p, '/')) && strlen(slash) > 0) s->filesize = strtoll(slash + 1, NULL, 10); } - h->is_streamed = 0; /* we _can_ in fact seek */ + if (s->seekable == -1 && (!s->is_akamai || s->filesize != 2147483647)) + h->is_streamed = 0; /* we _can_ in fact seek */ } static int parse_content_encoding(URLContext *h, const char *p) @@ -411,7 +417,6 @@ static int parse_content_encoding(URLContext *h, const char *p) // 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 0; } @@ -460,7 +465,7 @@ static int process_line(URLContext *h, char *line, int line_count, p++; s->http_code = strtol(p, &end, 10); - av_dlog(NULL, "http_code=%d\n", s->http_code); + av_log(h, AV_LOG_DEBUG, "http_code=%d\n", s->http_code); if ((ret = check_http_code(h, s->http_code, end)) < 0) return ret; @@ -484,7 +489,8 @@ static int process_line(URLContext *h, char *line, int line_count, } else if (!av_strcasecmp(tag, "Content-Range")) { parse_content_range(h, p); } else if (!av_strcasecmp(tag, "Accept-Ranges") && - !strncmp(p, "bytes", 5)) { + !strncmp(p, "bytes", 5) && + s->seekable == -1) { h->is_streamed = 0; } else if (!av_strcasecmp(tag, "Transfer-Encoding") && !av_strncasecmp(p, "chunked", 7)) { @@ -499,9 +505,29 @@ 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")) { + if (!av_strcasecmp(p, "AkamaiGHost")) { + s->is_akamai = 1; + } else if (!av_strncasecmp(p, "MediaGateway", 12)) { + s->is_mediagateway = 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)) { @@ -515,6 +541,103 @@ static int process_line(URLContext *h, char *line, int line_count, 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))) { + if (cookie) { + // first key-value pair is the actual cookie value + cvalue = av_strdup(param); + cookie = NULL; + } else if (!av_strncasecmp("path=", param, 5)) { + av_free(cpath); + cpath = av_strdup(¶m[5]); + } else if (!av_strncasecmp("domain=", param, 7)) { + // if the cookie specifies a sub-domain, skip the leading dot thereby + // supporting URLs that point to sub-domains and the master domain + int leading_dot = (param[7] == '.'); + av_free(cdomain); + cdomain = av_strdup(¶m[7+leading_dot]); + } else { + // ignore unknown attributes + } + } + 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!) */ @@ -535,7 +658,7 @@ static int http_read_header(URLContext *h, int *new_location) if ((err = http_get_line(s, line, sizeof(line))) < 0) return err; - av_dlog(NULL, "header='%s'\n", line); + av_log(h, AV_LOG_DEBUG, "header='%s'\n", line); err = process_line(h, line, s->line_count, new_location); if (err < 0) @@ -545,6 +668,9 @@ static int http_read_header(URLContext *h, int *new_location) s->line_count++; } + if (s->seekable == -1 && s->is_mediagateway && s->filesize == 2000000000) + h->is_streamed = 1; /* we can in fact _not_ seek */ + return err; } @@ -602,7 +728,7 @@ static int http_connect(URLContext *h, const char *path, const char *local_path, // 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) { + if (!has_header(s->headers, "\r\nRange: ") && !post && (s->off > 0 || s->end_off || s->seekable == -1)) { len += av_strlcatf(headers + len, sizeof(headers) - len, "Range: bytes=%"PRId64"-", s->off); if (s->end_off) @@ -634,6 +760,14 @@ static int http_connect(URLContext *h, const char *path, const char *local_path, 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) && cookies) { + 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); @@ -656,14 +790,14 @@ static int http_connect(URLContext *h, const char *path, const char *local_path, authstr ? authstr : "", proxyauthstr ? "Proxy-" : "", proxyauthstr ? proxyauthstr : ""); - av_freep(&authstr); - av_freep(&proxyauthstr); + av_log(h, AV_LOG_DEBUG, "request: %s\n", s->buffer); + if ((err = ffurl_write(s->hd, s->buffer, strlen(s->buffer))) < 0) - return err; + goto done; if (s->post_data) if ((err = ffurl_write(s->hd, s->post_data, s->post_datalen)) < 0) - return err; + goto done; /* init input buffer */ s->buf_ptr = s->buffer; @@ -680,15 +814,20 @@ static int http_connect(URLContext *h, const char *path, const char *local_path, * we've still to send the POST data, but the code calling this * function will check http_code after we return. */ s->http_code = 200; - return 0; + err = 0; + goto done; } /* wait for header */ err = http_read_header(h, new_location); if (err < 0) - return err; + goto done; - return (off == s->off) ? 0 : -1; + err = (off == s->off) ? 0 : -1; +done: + av_freep(&authstr); + av_freep(&proxyauthstr); + return err; } static int http_buf_read(URLContext *h, uint8_t *buf, int size) @@ -767,7 +906,6 @@ static int http_read_stream(URLContext *h, uint8_t *buf, int size) if (!s->chunksize) { char line[32]; - for (;;) { do { if ((err = http_get_line(s, line, sizeof(line))) < 0) return err; @@ -780,8 +918,6 @@ static int http_read_stream(URLContext *h, uint8_t *buf, int size) if (!s->chunksize) return 0; - break; - } } size = FFMIN(size, s->chunksize); } @@ -792,6 +928,8 @@ static int http_read_stream(URLContext *h, uint8_t *buf, int size) return http_buf_read(h, buf, size); } +// Like http_read_stream(), but no short reads. +// Assumes partial reads are an error. static int http_read_stream_all(URLContext *h, uint8_t *buf, int size) { int pos = 0; @@ -941,7 +1079,7 @@ static int http_close(URLContext *h) ret = http_shutdown(h, h->flags); if (s->hd) - ffurl_close(s->hd); + ffurl_closep(&s->hd); av_dict_free(&s->chained_options); return ret; } @@ -963,16 +1101,21 @@ static int64_t http_seek(URLContext *h, int64_t off, int whence) else if ((s->filesize == -1 && whence == SEEK_END) || h->is_streamed) return AVERROR(ENOSYS); - /* we save the old context in case the seek fails */ - old_buf_size = s->buf_end - s->buf_ptr; - memcpy(old_buf, s->buf_ptr, old_buf_size); - s->hd = NULL; if (whence == SEEK_CUR) off += s->off; else if (whence == SEEK_END) off += s->filesize; + else if (whence != SEEK_SET) + return AVERROR(EINVAL); + if (off < 0) + return AVERROR(EINVAL); s->off = off; + /* we save the old context in case the seek fails */ + old_buf_size = s->buf_end - s->buf_ptr; + memcpy(old_buf, s->buf_ptr, old_buf_size); + s->hd = NULL; + /* if it fails, continue on old connection */ av_dict_copy(&options, s->chained_options, 0); if ((ret = http_open_cnx(h, &options)) < 0) { @@ -1044,7 +1187,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; } @@ -1059,7 +1202,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); @@ -1115,8 +1261,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 3d301a7..be8ae7f 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 */ @@ -24,7 +24,7 @@ #include "url.h" -#define HTTP_HEADERS_SIZE 1024 +#define HTTP_HEADERS_SIZE 4096 /** * Initialize the authentication state based on another HTTP URLContext. @@ -40,7 +40,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 b96da3e..dbe3eff 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 */ @@ -224,8 +224,11 @@ static char *make_digest_auth(HTTPAuthState *state, const char *username, av_strlcatf(authstr, len, ",nonce=\"%s\"", digest->nonce); av_strlcatf(authstr, len, ",uri=\"%s\"", uri); av_strlcatf(authstr, len, ",response=\"%s\"", response); + + // we are violating the RFC and use "" because all others seem to do that too. if (digest->algorithm[0]) - av_strlcatf(authstr, len, ",algorithm=%s", digest->algorithm); + av_strlcatf(authstr, len, ",algorithm=\"%s\"", digest->algorithm); + if (digest->opaque[0]) av_strlcatf(authstr, len, ",opaque=\"%s\"", digest->opaque); if (digest->qop[0]) { diff --git a/libavformat/httpauth.h b/libavformat/httpauth.h index 99bf43f..fc17c94 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/icecast.c b/libavformat/icecast.c index b671d8c..56a2976 100644 --- a/libavformat/icecast.c +++ b/libavformat/icecast.c @@ -1,26 +1,27 @@ /* - * Icecast protocol for Libav + * Icecast protocol for FFmpeg * Copyright (c) 2014 Marvin Scholz * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free 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/bprint.h" #include "libavutil/opt.h" #include "avformat.h" @@ -65,27 +66,10 @@ static const AVOption options[] = { }; -static char *cat_header(char buf[], const char key[], const char value[]) +static void cat_header(AVBPrint *bp, const char key[], const char value[]) { - if (NOT_EMPTY(value)) { - int len = strlen(key) + strlen(value) + 5; - int is_first = !buf; - char *tmp = NULL; - - if (buf) - len += strlen(buf); - if (!(tmp = av_realloc(buf, len))) { - av_freep(&buf); - return NULL; - } else { - buf = tmp; - } - if (is_first) - *buf = '\0'; - - av_strlcatf(buf, len, "%s: %s\r\n", key, value); - } - return buf; + if (NOT_EMPTY(value)) + av_bprintf(bp, "%s: %s\r\n", key, value); } static int icecast_close(URLContext *h) @@ -107,20 +91,24 @@ static int icecast_open(URLContext *h, const char *uri, int flags) char h_url[1024], host[1024], auth[1024], path[1024]; char *headers = NULL, *user = NULL; int port, ret; + AVBPrint bp; if (flags & AVIO_FLAG_READ) return AVERROR(ENOSYS); + av_bprint_init(&bp, 0, 1); + // Build header strings - headers = cat_header(headers, "Ice-Name", s->name); - headers = cat_header(headers, "Ice-Description", s->description); - headers = cat_header(headers, "Ice-URL", s->url); - headers = cat_header(headers, "Ice-Genre", s->genre); - headers = cat_header(headers, "Ice-Public", s->public ? "1" : "0"); - if (!headers) { + cat_header(&bp, "Ice-Name", s->name); + cat_header(&bp, "Ice-Description", s->description); + cat_header(&bp, "Ice-URL", s->url); + cat_header(&bp, "Ice-Genre", s->genre); + cat_header(&bp, "Ice-Public", s->public ? "1" : "0"); + if (!av_bprint_is_complete(&bp)) { ret = AVERROR(ENOMEM); goto cleanup; } + av_bprint_finalize(&bp, &headers); // Set options av_dict_set(&opt_dict, "method", s->legacy_icecast ? "SOURCE" : "PUT", 0); @@ -137,7 +125,7 @@ static int icecast_open(URLContext *h, const char *uri, int flags) // Check for auth data in URI if (auth[0]) { - char *sep = strchr(auth,':'); + char *sep = strchr(auth, ':'); if (sep) { *sep = 0; sep++; @@ -175,7 +163,6 @@ static int icecast_open(URLContext *h, const char *uri, int flags) ret = ffurl_open(&s->hd, h_url, AVIO_FLAG_READ_WRITE, NULL, &opt_dict); cleanup: - // Free variables av_freep(&user); av_freep(&headers); av_dict_free(&opt_dict); diff --git a/libavformat/icodec.c b/libavformat/icodec.c new file mode 100644 index 0000000..847f0ee --- /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 / 4; + 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_array(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 0000000..53d4420 --- /dev/null +++ b/libavformat/icoenc.c @@ -0,0 +1,203 @@ +/* + * 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 { + const AVCodecDescriptor *codesc = avcodec_descriptor_get(c->codec_id); + av_log(s, AV_LOG_ERROR, "Unsupported codec %s\n", codesc ? codesc->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_array(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 87930ff..0617a9c 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", @@ -202,7 +203,6 @@ static void get_string(AVFormatContext *s, const char *key, */ static int parse_tag(AVFormatContext *s, const uint8_t *buf) { - char str[5]; int genre; if (!(buf[0] == 'T' && @@ -215,8 +215,7 @@ static int parse_tag(AVFormatContext *s, const uint8_t *buf) get_string(s, "date", buf + 93, 4); get_string(s, "comment", buf + 97, 30); if (buf[125] == 0 && buf[126] != 0) { - snprintf(str, sizeof(str), "%d", buf[126]); - av_dict_set(&s->metadata, "track", str, 0); + av_dict_set_int(&s->metadata, "track", buf[126], 0); } genre = buf[127]; if (genre <= ID3v1_GENRE_MAX) diff --git a/libavformat/id3v1.h b/libavformat/id3v1.h index 7107073..d5dca35 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 e5f7486..5469e0a 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" @@ -46,6 +59,7 @@ const AVMetadataConv ff_id3v2_34_metadata_conv[] = { }; const AVMetadataConv ff_id3v2_4_metadata_conv[] = { + { "TCMP", "compilation" }, { "TDRL", "date" }, { "TDRC", "date" }, { "TDEN", "creation_time" }, @@ -58,6 +72,7 @@ const AVMetadataConv ff_id3v2_4_metadata_conv[] = { static const AVMetadataConv id3v2_2_metadata_conv[] = { { "TAL", "album" }, { "TCO", "genre" }, + { "TCP", "compilation" }, { "TT2", "title" }, { "TEN", "encoded_by" }, { "TP1", "artist" }, @@ -258,7 +273,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,14 +308,14 @@ 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); } /** * Parse GEOB tag into a ID3v2ExtraMetaGEOB struct. */ static void read_geobtag(AVFormatContext *s, AVIOContext *pb, int taglen, - char *tag, ID3v2ExtraMeta **extra_meta) + char *tag, ID3v2ExtraMeta **extra_meta, int isv34) { ID3v2ExtraMetaGEOB *geob_data = NULL; ID3v2ExtraMeta *new_extra = NULL; @@ -312,14 +327,14 @@ static void read_geobtag(AVFormatContext *s, AVIOContext *pb, int taglen, geob_data = av_mallocz(sizeof(ID3v2ExtraMetaGEOB)); if (!geob_data) { - av_log(s, AV_LOG_ERROR, "Failed to alloc %zu bytes\n", + av_log(s, AV_LOG_ERROR, "Failed to alloc %"SIZE_SPECIFIER" bytes\n", sizeof(ID3v2ExtraMetaGEOB)); return; } new_extra = av_mallocz(sizeof(ID3v2ExtraMeta)); if (!new_extra) { - av_log(s, AV_LOG_ERROR, "Failed to alloc %zu bytes\n", + av_log(s, AV_LOG_ERROR, "Failed to alloc %"SIZE_SPECIFIER" bytes\n", sizeof(ID3v2ExtraMeta)); goto fail; } @@ -432,7 +447,7 @@ static void free_apic(void *obj) } static void read_apic(AVFormatContext *s, AVIOContext *pb, int taglen, - char *tag, ID3v2ExtraMeta **extra_meta) + char *tag, ID3v2ExtraMeta **extra_meta, int isv34) { int enc, pic_type; char mimetype[64]; @@ -454,7 +469,12 @@ static void read_apic(AVFormatContext *s, AVIOContext *pb, int taglen, taglen--; /* mimetype */ + if (isv34) { taglen -= avio_get_str(pb, taglen, mimetype, sizeof(mimetype)); + } else { + avio_read(pb, mimetype, 3); + mimetype[3] = 0; + } while (mime->id != AV_CODEC_ID_NONE) { if (!av_strncasecmp(mime->str, mimetype, sizeof(mimetype))) { id = mime->id; @@ -487,7 +507,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,17 +525,119 @@ fail: avio_seek(pb, end, SEEK_SET); } +static void read_chapter(AVFormatContext *s, AVIOContext *pb, int len, char *ttag, ID3v2ExtraMeta **extra_meta, int isv34) +{ + AVRational time_base = {1, 1000}; + uint32_t start, end; + AVChapter *chapter; + uint8_t *dst = NULL; + int taglen; + char tag[5]; + + if (!s) { + /* We should probably just put the chapter data to extra_meta here + * and do the AVFormatContext-needing part in a separate + * ff_id3v2_parse_apic()-like function. */ + av_log(NULL, AV_LOG_DEBUG, "No AVFormatContext, skipped ID3 chapter data\n"); + return; + } + + 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) { + if (avio_read(pb, tag, 4) < 4) + goto end; + tag[4] = 0; + taglen = avio_rb32(pb); + avio_skip(pb, 2); + len -= 10; + if (taglen < 0 || taglen > len) + goto end; + 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); +end: + av_free(dst); +} + +static void free_priv(void *obj) +{ + ID3v2ExtraMetaPRIV *priv = obj; + av_freep(&priv->owner); + av_freep(&priv->data); + av_freep(&priv); +} + +static void read_priv(AVFormatContext *s, AVIOContext *pb, int taglen, + char *tag, ID3v2ExtraMeta **extra_meta, int isv34) +{ + ID3v2ExtraMeta *meta; + ID3v2ExtraMetaPRIV *priv; + + meta = av_mallocz(sizeof(*meta)); + priv = av_mallocz(sizeof(*priv)); + + if (!meta || !priv) + goto fail; + + if (decode_str(s, pb, ID3v2_ENCODING_ISO8859, &priv->owner, &taglen) < 0) + goto fail; + + priv->data = av_malloc(taglen); + if (!priv->data) + goto fail; + + priv->datasize = taglen; + + if (avio_read(pb, priv->data, priv->datasize) != priv->datasize) + goto fail; + + meta->tag = "PRIV"; + meta->data = priv; + meta->next = *extra_meta; + *extra_meta = meta; + + return; + +fail: + if (priv) + free_priv(priv); + av_freep(&meta); +} + typedef struct ID3v2EMFunc { const char *tag3; const char *tag4; void (*read)(AVFormatContext *, AVIOContext *, int, char *, - ID3v2ExtraMeta **); + ID3v2ExtraMeta **, int isv34); void (*free)(void *obj); } 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 }, + { "PRIV","PRIV", read_priv, free_priv }, { NULL } }; @@ -528,7 +650,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))) @@ -538,19 +660,25 @@ static const ID3v2EMFunc *get_extra_meta_func(const char *tag, int isv34) return NULL; } -static void id3v2_parse(AVFormatContext *s, int len, uint8_t version, +static void id3v2_parse(AVIOContext *pb, AVDictionary **metadata, + 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; + int64_t next, end = avio_tell(pb) + len; int taghdrlen; const char *reason = NULL; - AVIOContext pb; + AVIOContext pb_local; AVIOContext *pbx; unsigned char *buffer = NULL; int buffer_size = 0; - const ID3v2EMFunc *extra_func; + const ID3v2EMFunc *extra_func = NULL; + unsigned char *uncompressed_buffer = NULL; + av_unused 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: @@ -576,7 +704,7 @@ static void id3v2_parse(AVFormatContext *s, int len, uint8_t version, unsync = flags & 0x80; if (isv34 && flags & 0x40) { /* Extended header present, just skip over it */ - int extlen = get_size(s->pb, 4); + int extlen = get_size(pb, 4); if (version == 4) /* In v2.4 the length includes the length field we just read. */ extlen -= 4; @@ -585,35 +713,45 @@ static void id3v2_parse(AVFormatContext *s, int len, uint8_t version, reason = "invalid extended header length"; goto error; } - avio_skip(s->pb, extlen); + avio_skip(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); + if (avio_read(pb, tag, 4) < 4) + break; tag[4] = 0; if (version == 3) { - tlen = avio_rb32(s->pb); + tlen = avio_rb32(pb); } else - tlen = get_size(s->pb, 4); - tflags = avio_rb16(s->pb); + tlen = get_size(pb, 4); + tflags = avio_rb16(pb); tunsync = tflags & ID3v2_FLAG_UNSYNCH; } else { - avio_read(s->pb, tag, 3); + if (avio_read(pb, tag, 3) < 3) + break; tag[3] = 0; - tlen = avio_rb24(s->pb); + tlen = avio_rb24(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; - next = avio_tell(s->pb) + tlen; + + if (len < 0) + break; + + next = avio_tell(pb) + tlen; if (!tlen) { if (tag[0]) @@ -623,57 +761,107 @@ 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(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); - avio_skip(s->pb, tlen); + av_log(s, AV_LOG_WARNING, "Skipping %s ID3v2 frame %s.\n", type, tag); + avio_skip(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 = 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(pb) + tlen; + uint8_t *b; + b = buffer; - while (avio_tell(s->pb) < end && !s->pb->eof_reached) { - *b++ = avio_r8(s->pb); - if (*(b - 1) == 0xff && avio_tell(s->pb) < end - 1 && - !s->pb->eof_reached ) { - uint8_t val = avio_r8(s->pb); - *b++ = val ? val : avio_r8(s->pb); + while (avio_tell(pb) < end && b - buffer < tlen && !pb->eof_reached) { + *b++ = avio_r8(pb); + if (*(b - 1) == 0xff && avio_tell(pb) < end - 1 && + b - buffer < tlen && + !pb->eof_reached ) { + uint8_t val = avio_r8(pb); + *b++ = val ? val : avio_r8(pb); } } - ffio_init_context(&pb, buffer, b - buffer, 0, NULL, NULL, NULL, + ffio_init_context(&pb_local, buffer, b - buffer, 0, NULL, NULL, NULL, NULL); tlen = b - buffer; - pbx = &pb; // read from sync buffer - } else { - pbx = s->pb; // read straight from input + pbx = &pb_local; // read from sync buffer } + +#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(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_local, uncompressed_buffer, dlen, 0, NULL, NULL, NULL, NULL); + tlen = dlen; + pbx = &pb_local; // read from sync buffer + } +#endif if (tag[0] == 'T') /* parse text tag */ - read_ttag(s, pbx, tlen, tag); + read_ttag(s, pbx, tlen, metadata, tag); else /* parse special meta tag */ - extra_func->read(s, pbx, tlen, tag, extra_meta); + extra_func->read(s, pbx, tlen, tag, extra_meta, isv34); } else if (!tag[0]) { if (tag[1]) - av_log(s, AV_LOG_WARNING, "invalid frame id, assuming padding"); - avio_skip(s->pb, tlen); + av_log(s, AV_LOG_WARNING, "invalid frame id, assuming padding\n"); + avio_skip(pb, tlen); break; } /* Skip to end of tag */ seek: - avio_seek(s->pb, next, SEEK_SET); + avio_seek(pb, next, SEEK_SET); } /* Footer preset, always 10 bytes, skip over it */ @@ -684,25 +872,38 @@ error: if (reason) av_log(s, AV_LOG_INFO, "ID3v2.%d tag skipped, cannot handle %s\n", version, reason); - avio_seek(s->pb, end, SEEK_SET); + avio_seek(pb, end, SEEK_SET); av_free(buffer); + av_free(uncompressed_buffer); return; } -void ff_id3v2_read(AVFormatContext *s, const char *magic, - ID3v2ExtraMeta **extra_meta) +static void id3v2_read_internal(AVIOContext *pb, AVDictionary **metadata, + AVFormatContext *s, const char *magic, + ID3v2ExtraMeta **extra_meta, int64_t max_search_size) { int len, ret; uint8_t buf[ID3v2_HEADER_SIZE]; int found_header; - int64_t off; + int64_t start, off; + if (max_search_size && max_search_size < ID3v2_HEADER_SIZE) + return; + + start = avio_tell(pb); do { /* 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) + off = avio_tell(pb); + if (max_search_size && off - start >= max_search_size - ID3v2_HEADER_SIZE) { + avio_seek(pb, off, SEEK_SET); break; + } + + ret = avio_read(pb, buf, ID3v2_HEADER_SIZE); + if (ret != ID3v2_HEADER_SIZE) { + avio_seek(pb, off, SEEK_SET); + break; + } found_header = ff_id3v2_match(buf, magic); if (found_header) { /* parse ID3v2 header */ @@ -710,15 +911,27 @@ void ff_id3v2_read(AVFormatContext *s, const char *magic, ((buf[7] & 0x7f) << 14) | ((buf[8] & 0x7f) << 7) | (buf[9] & 0x7f); - id3v2_parse(s, len, buf[3], buf[5], extra_meta); + id3v2_parse(pb, metadata, s, len, buf[3], buf[5], extra_meta); } else { - avio_seek(s->pb, off, SEEK_SET); + avio_seek(pb, off, SEEK_SET); } } while (found_header); - ff_metadata_conv(&s->metadata, NULL, ff_id3v2_34_metadata_conv); - ff_metadata_conv(&s->metadata, NULL, id3v2_2_metadata_conv); - ff_metadata_conv(&s->metadata, NULL, ff_id3v2_4_metadata_conv); - merge_date(&s->metadata); + ff_metadata_conv(metadata, NULL, ff_id3v2_34_metadata_conv); + ff_metadata_conv(metadata, NULL, id3v2_2_metadata_conv); + ff_metadata_conv(metadata, NULL, ff_id3v2_4_metadata_conv); + merge_date(metadata); +} + +void ff_id3v2_read_dict(AVIOContext *pb, AVDictionary **metadata, + const char *magic, ID3v2ExtraMeta **extra_meta) +{ + id3v2_read_internal(pb, metadata, NULL, magic, extra_meta, 0); +} + +void ff_id3v2_read(AVFormatContext *s, const char *magic, + ID3v2ExtraMeta **extra_meta, unsigned int max_search_size) +{ + id3v2_read_internal(s->pb, &s->metadata, s, magic, extra_meta, max_search_size); } void ff_id3v2_free_extra_meta(ID3v2ExtraMeta **extra_meta) @@ -733,6 +946,8 @@ void ff_id3v2_free_extra_meta(ID3v2ExtraMeta **extra_meta) av_freep(¤t); current = next; } + + *extra_meta = NULL; } int ff_id3v2_parse_apic(AVFormatContext *s, ID3v2ExtraMeta **extra_meta) diff --git a/libavformat/id3v2.h b/libavformat/id3v2.h index 7cb4296..9d7bf1c 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 */ @@ -73,6 +73,12 @@ typedef struct ID3v2ExtraMetaAPIC { enum AVCodecID id; } ID3v2ExtraMetaAPIC; +typedef struct ID3v2ExtraMetaPRIV { + uint8_t *owner; + uint8_t *data; + uint32_t datasize; +} ID3v2ExtraMetaPRIV; + /** * Detect ID3v2 Header. * @param buf must be ID3v2_HEADER_SIZE byte long @@ -89,11 +95,27 @@ int ff_id3v2_match(const uint8_t *buf, const char *magic); int ff_id3v2_tag_len(const uint8_t *buf); /** - * Read an ID3v2 tag, including supported extra metadata + * Read an ID3v2 tag into specified dictionary and retrieve supported extra metadata. + * + * Chapters are not currently read by this variant. + * + * @param metadata Parsed metadata is stored here + * @param extra_meta If not NULL, extra metadata is parsed into a list of + * ID3v2ExtraMeta structs and *extra_meta points to the head of the list + */ +void ff_id3v2_read_dict(AVIOContext *pb, AVDictionary **metadata, const char *magic, ID3v2ExtraMeta **extra_meta); + +/** + * Read an ID3v2 tag, including supported extra metadata and chapters. + * + * Data is read from and stored to AVFormatContext. + * * @param extra_meta If not NULL, extra metadata is parsed into a list of * ID3v2ExtraMeta structs and *extra_meta points to the head of the list + * @param[opt] max_search_search restrict ID3 magic number search (bytes from start) */ -void ff_id3v2_read(AVFormatContext *s, const char *magic, ID3v2ExtraMeta **extra_meta); +void ff_id3v2_read(AVFormatContext *s, const char *magic, ID3v2ExtraMeta **extra_meta, + unsigned int max_search_size); /** * Initialize an ID3v2 tag. @@ -114,7 +136,7 @@ int ff_id3v2_write_apic(AVFormatContext *s, ID3v2EncContext *id3, AVPacket *pkt) /** * Finalize an opened ID3v2 tag. */ -void ff_id3v2_finish(ID3v2EncContext *id3, AVIOContext *pb); +void ff_id3v2_finish(ID3v2EncContext *id3, AVIOContext *pb, int padding_bytes); /** * Write an ID3v2 tag containing all global metadata from s. diff --git a/libavformat/id3v2enc.c b/libavformat/id3v2enc.c index 89181c2..8b804c4 100644 --- a/libavformat/id3v2enc.c +++ b/libavformat/id3v2enc.c @@ -1,20 +1,20 @@ /* * 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 */ @@ -26,6 +26,7 @@ #include "libavutil/intreadwrite.h" #include "avformat.h" #include "avio.h" +#include "avio_internal.h" #include "id3v2.h" static void id3v2_put_size(AVIOContext *pb, int size) @@ -110,6 +111,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) { @@ -124,31 +163,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; } @@ -156,6 +195,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]; @@ -196,6 +293,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); @@ -221,9 +322,25 @@ int ff_id3v2_write_apic(AVFormatContext *s, ID3v2EncContext *id3, AVPacket *pkt) return 0; } -void ff_id3v2_finish(ID3v2EncContext *id3, AVIOContext *pb) +void ff_id3v2_finish(ID3v2EncContext *id3, AVIOContext *pb, + int padding_bytes) { - int64_t cur_pos = avio_tell(pb); + int64_t cur_pos; + + if (padding_bytes < 0) + padding_bytes = 10; + + /* The ID3v2.3 specification states that 28 bits are used to represent the + * size of the whole tag. Therefore the current size of the tag needs to be + * subtracted from the upper limit of 2^28-1 to clip the value correctly. */ + /* The minimum of 10 is an arbitrary amount of padding at the end of the tag + * to fix cover art display with some software such as iTunes, Traktor, + * Serato, Torq. */ + padding_bytes = av_clip(padding_bytes, 10, 268435455 - id3->len); + 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); @@ -238,7 +355,7 @@ int ff_id3v2_write_simple(struct AVFormatContext *s, int id3v2_version, ff_id3v2_start(&id3, s->pb, id3v2_version, magic); if ((ret = ff_id3v2_write_metadata(s, &id3)) < 0) return ret; - ff_id3v2_finish(&id3, s->pb); + ff_id3v2_finish(&id3, s->pb, s->metadata_header_padding); return 0; } diff --git a/libavformat/idcin.c b/libavformat/idcin.c index 2536e8b..d7a46a1 100644 --- a/libavformat/idcin.c +++ b/libavformat/idcin.c @@ -1,21 +1,21 @@ /* * id Quake II CIN File Demuxer - * Copyright (c) 2003 The ffmpeg 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 */ @@ -93,7 +93,9 @@ typedef struct IdcinDemuxContext { static int idcin_probe(AVProbeData *p) { - unsigned int number; + unsigned int number, sample_rate; + unsigned int w, h; + int i; /* * This is what you could call a "probabilistic" file check: id CIN @@ -108,34 +110,41 @@ static int idcin_probe(AVProbeData *p) /* check we have enough data to do all checks, otherwise the 0-padding may cause a wrong recognition */ - if (p->buf_size < 20) + if (p->buf_size < 20 + HUFFMAN_TABLE_SIZE + 12) return 0; /* check the video width */ - number = AV_RL32(&p->buf[0]); - if ((number == 0) || (number > 1024)) + w = AV_RL32(&p->buf[0]); + if ((w == 0) || (w > 1024)) return 0; /* check the video height */ - number = AV_RL32(&p->buf[4]); - if ((number == 0) || (number > 1024)) + h = AV_RL32(&p->buf[4]); + if ((h == 0) || (h > 1024)) 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; + i = 20 + HUFFMAN_TABLE_SIZE; + if (AV_RL32(&p->buf[i]) == 1) + i += 768; + + if (i+12 > p->buf_size || AV_RL32(&p->buf[i+8]) != w*h) + return 1; + /* return half certainty since this check is a bit sketchy */ return AVPROBE_SCORE_EXTENSION; } @@ -196,15 +205,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); - ret = avio_read(pb, st->codec->extradata, HUFFMAN_TABLE_SIZE); - if (ret < 0) { + if ((ret = ff_get_extradata(st->codec, pb, HUFFMAN_TABLE_SIZE)) < 0) return ret; - } else if (ret != HUFFMAN_TABLE_SIZE) { - av_log(s, AV_LOG_ERROR, "incomplete header\n"); - return AVERROR(EIO); - } if (idcin->audio_present) { idcin->audio_present = 1; @@ -260,7 +262,7 @@ static int idcin_read_packet(AVFormatContext *s, unsigned char palette_buffer[768]; uint32_t palette[256]; - if (s->pb->eof_reached) + if (avio_feof(s->pb)) return s->pb->error ? s->pb->error : AVERROR_EOF; if (idcin->next_chunk_is_video) { @@ -288,7 +290,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 +307,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 +323,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 82eff24..76bb392 100644 --- a/libavformat/idroqdec.c +++ b/libavformat/idroqdec.c @@ -1,21 +1,21 @@ /* * id RoQ (.roq) File Demuxer - * Copyright (c) 2003 The ffmpeg 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 */ @@ -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 (avio_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 2ce4d7d..28a3aba 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 */ @@ -25,9 +25,35 @@ static int roq_write_header(struct AVFormatContext *s) { - static const uint8_t header[] = { - 0x84, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0x1E, 0x00 + uint8_t header[] = { + 0x84, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, /* fps: */ 0x1E, 0x00 }; + int n; + AVCodecContext *avctx; + +// set the actual fps + for(n=0;n<s->nb_streams;n++) { + if ((avctx=s->streams[n]->codec)->codec_type == AVMEDIA_TYPE_VIDEO) { + unsigned int fps; + + if (avctx->time_base.num != 1) { + av_log(avctx, AV_LOG_ERROR, "Frame rate must be integer\n"); + return AVERROR(EINVAL); + } + + if ((fps=avctx->time_base.den) > 255) { + av_log(avctx, AV_LOG_ERROR, "Frame rate may not exceed 255fps\n"); + return AVERROR(EINVAL); + } + + if (fps != 30) { + av_log(avctx, AV_LOG_WARNING, "For vintage compatibility fps must be 30\n"); + } + + header[6] = fps; + break; + } + } avio_write(s->pb, header, 8); avio_flush(s->pb); diff --git a/libavformat/iff.c b/libavformat/iff.c index 6be0fb7..8e20303 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 */ @@ -31,13 +30,20 @@ #include <inttypes.h> +#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 "id3v2.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') @@ -45,9 +51,18 @@ #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_DSD MKTAG('D','S','D',' ') +#define ID_ANIM MKTAG('A','N','I','M') #define ID_FORM MKTAG('F','O','R','M') +#define ID_FRM8 MKTAG('F','R','M','8') #define ID_ANNO MKTAG('A','N','N','O') #define ID_AUTH MKTAG('A','U','T','H') #define ID_CHRS MKTAG('C','H','R','S') @@ -56,31 +71,50 @@ #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; + int is_64bit; ///< chunk size is 64-bit + 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, @@ -91,7 +125,7 @@ static int get_metadata(AVFormatContext *s, if (!buf) return AVERROR(ENOMEM); - if (avio_read(s->pb, buf, data_size) < 0) { + if (avio_read(s->pb, buf, data_size) != data_size) { av_free(buf); return AVERROR(EIO); } @@ -104,19 +138,217 @@ 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_RGB8 || + AV_RL32(d+8) == ID_ANIM || + AV_RL32(d+8) == ID_RGBN)) || + (AV_RL32(d) == ID_FRM8 && AV_RL32(d+12) == ID_DSD)) return AVPROBE_SCORE_MAX; return 0; } +static const AVCodecTag dsd_codec_tags[] = { + { AV_CODEC_ID_DSD_MSBF, ID_DSD }, + { AV_CODEC_ID_NONE, 0 }, +}; + + +#define DSD_SLFT MKTAG('S','L','F','T') +#define DSD_SRGT MKTAG('S','R','G','T') +#define DSD_MLFT MKTAG('M','L','F','T') +#define DSD_MRGT MKTAG('M','R','G','T') +#define DSD_C MKTAG('C',' ',' ',' ') +#define DSD_LS MKTAG('L','S',' ',' ') +#define DSD_RS MKTAG('R','S',' ',' ') +#define DSD_LFE MKTAG('L','F','E',' ') + +static const uint32_t dsd_stereo[] = { DSD_SLFT, DSD_SRGT }; +static const uint32_t dsd_5point0[] = { DSD_MLFT, DSD_MRGT, DSD_C, DSD_LS, DSD_RS }; +static const uint32_t dsd_5point1[] = { DSD_MLFT, DSD_MRGT, DSD_C, DSD_LFE, DSD_LS, DSD_RS }; + +typedef struct { + uint64_t layout; + const uint32_t * dsd_layout; +} DSDLayoutDesc; + +static const DSDLayoutDesc dsd_channel_layout[] = { + { AV_CH_LAYOUT_STEREO, dsd_stereo }, + { AV_CH_LAYOUT_5POINT0, dsd_5point0 }, + { AV_CH_LAYOUT_5POINT1, dsd_5point1 }, +}; + +static const uint64_t dsd_loudspeaker_config[] = { + AV_CH_LAYOUT_STEREO, + 0, 0, + AV_CH_LAYOUT_5POINT0, AV_CH_LAYOUT_5POINT1, +}; + +static const char * dsd_source_comment[] = { + "dsd_source_comment", + "analogue_source_comment", + "pcm_source_comment", +}; + +static const char * dsd_history_comment[] = { + "general_remark", + "operator_name", + "creating_machine", + "timezone", + "file_revision" +}; + +static int parse_dsd_diin(AVFormatContext *s, AVStream *st, uint64_t eof) +{ + AVIOContext *pb = s->pb; + + while (avio_tell(pb) + 12 <= eof) { + uint32_t tag = avio_rl32(pb); + uint64_t size = avio_rb64(pb); + uint64_t orig_pos = avio_tell(pb); + const char * metadata_tag = NULL; + + switch(tag) { + case MKTAG('D','I','A','R'): metadata_tag = "artist"; break; + case MKTAG('D','I','T','I'): metadata_tag = "title"; break; + } + + if (metadata_tag && size > 4) { + unsigned int tag_size = avio_rb32(pb); + int ret = get_metadata(s, metadata_tag, FFMIN(tag_size, size - 4)); + if (ret < 0) { + av_log(s, AV_LOG_ERROR, "cannot allocate metadata tag %s!\n", metadata_tag); + return ret; + } + } + + avio_skip(pb, size - (avio_tell(pb) - orig_pos) + (size & 1)); + } + + return 0; +} + +static int parse_dsd_prop(AVFormatContext *s, AVStream *st, uint64_t eof) +{ + AVIOContext *pb = s->pb; + char abss[24]; + int hour, min, sec, i, ret, config; + int dsd_layout[6]; + ID3v2ExtraMeta *id3v2_extra_meta; + + while (avio_tell(pb) + 12 <= eof) { + uint32_t tag = avio_rl32(pb); + uint64_t size = avio_rb64(pb); + uint64_t orig_pos = avio_tell(pb); + + switch(tag) { + case MKTAG('A','B','S','S'): + if (size < 8) + return AVERROR_INVALIDDATA; + hour = avio_rb16(pb); + min = avio_r8(pb); + sec = avio_r8(pb); + snprintf(abss, sizeof(abss), "%02dh:%02dm:%02ds:%d", hour, min, sec, avio_rb32(pb)); + av_dict_set(&st->metadata, "absolute_start_time", abss, 0); + break; + + case MKTAG('C','H','N','L'): + if (size < 2) + return AVERROR_INVALIDDATA; + st->codec->channels = avio_rb16(pb); + if (size < 2 + st->codec->channels * 4) + return AVERROR_INVALIDDATA; + st->codec->channel_layout = 0; + if (st->codec->channels > FF_ARRAY_ELEMS(dsd_layout)) { + avpriv_request_sample(s, "channel layout"); + break; + } + for (i = 0; i < st->codec->channels; i++) + dsd_layout[i] = avio_rl32(pb); + for (i = 0; i < FF_ARRAY_ELEMS(dsd_channel_layout); i++) { + const DSDLayoutDesc * d = &dsd_channel_layout[i]; + if (av_get_channel_layout_nb_channels(d->layout) == st->codec->channels && + !memcmp(d->dsd_layout, dsd_layout, st->codec->channels * sizeof(uint32_t))) { + st->codec->channel_layout = d->layout; + break; + } + } + break; + + case MKTAG('C','M','P','R'): + if (size < 4) + return AVERROR_INVALIDDATA; + st->codec->codec_id = ff_codec_get_id(dsd_codec_tags, avio_rl32(pb)); + break; + + case MKTAG('F','S',' ',' '): + if (size < 4) + return AVERROR_INVALIDDATA; + st->codec->sample_rate = avio_rb32(pb) / 8; + break; + + case MKTAG('I','D','3',' '): + id3v2_extra_meta = NULL; + ff_id3v2_read(s, ID3v2_DEFAULT_MAGIC, &id3v2_extra_meta, size); + 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 (size < avio_tell(pb) - orig_pos) { + av_log(s, AV_LOG_ERROR, "id3 exceeds chunk size\n"); + return AVERROR_INVALIDDATA; + } + break; + + case MKTAG('L','S','C','O'): + if (size < 2) + return AVERROR_INVALIDDATA; + config = avio_rb16(pb); + if (config != 0xFFFF) { + if (config < FF_ARRAY_ELEMS(dsd_loudspeaker_config)) + st->codec->channel_layout = dsd_loudspeaker_config[config]; + if (!st->codec->channel_layout) + avpriv_request_sample(s, "loudspeaker configuration %d", config); + } + break; + } + + avio_skip(pb, size - (avio_tell(pb) - orig_pos) + (size & 1)); + } + + 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; - uint32_t chunk_id, data_size; - int compression = -1; + uint8_t *buf; + uint32_t chunk_id; + uint64_t data_size; + 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) @@ -124,16 +356,26 @@ static int iff_read_header(AVFormatContext *s) st->codec->channels = 1; st->codec->channel_layout = AV_CH_LAYOUT_MONO; - avio_skip(pb, 8); + iff->is_64bit = avio_rl32(pb) == ID_FRM8; + avio_skip(pb, iff->is_64bit ? 8 : 4); // codec_tag used by ByteRun1 decoder to distinguish progressive (PBM) and interlaced (ILBM) content st->codec->codec_tag = avio_rl32(pb); + if (st->codec->codec_tag == ID_ANIM) { + avio_skip(pb, 8); + 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(!avio_feof(pb)) { uint64_t orig_pos; int res; const char *metadata_tag = NULL; + int version, nb_comments, i; chunk_id = avio_rl32(pb); - data_size = avio_rb32(pb); + data_size = iff->is_64bit ? avio_rb64(pb) : avio_rb32(pb); orig_pos = avio_tell(pb); switch(chunk_id) { @@ -146,12 +388,39 @@ 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_DSD: + case ID_MDAT: iff->body_pos = avio_tell(pb); + iff->body_end = iff->body_pos + data_size; iff->body_size = data_size; break; @@ -167,17 +436,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 %"PRIu32"\n", + av_log(s, AV_LOG_ERROR, "Invalid CMAP chunk size %"PRIu64"\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; @@ -189,38 +464,156 @@ 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_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_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_DLOC: + if (data_size < 4) + return AVERROR_INVALIDDATA; + st->codec->width = avio_rb16(pb); + st->codec->height = avio_rb16(pb); + break; + + 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"; + 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; + + /* DSD tags */ + + case MKTAG('F','V','E','R'): + if (data_size < 4) + return AVERROR_INVALIDDATA; + version = avio_rb32(pb); + av_log(s, AV_LOG_DEBUG, "DSIFF v%d.%d.%d.%d\n",version >> 24, (version >> 16) & 0xFF, (version >> 8) & 0xFF, version & 0xFF); + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; break; - case ID_AUTH: - metadata_tag = "artist"; + case MKTAG('D','I','I','N'): + res = parse_dsd_diin(s, st, orig_pos + data_size); + if (res < 0) + return res; break; - case ID_COPYRIGHT: - metadata_tag = "copyright"; + case MKTAG('P','R','O','P'): + if (data_size < 4) + return AVERROR_INVALIDDATA; + if (avio_rl32(pb) != MKTAG('S','N','D',' ')) { + avpriv_request_sample(s, "unknown property type"); + break; + } + res = parse_dsd_prop(s, st, orig_pos + data_size); + if (res < 0) + return res; break; - case ID_NAME: - metadata_tag = "title"; + case MKTAG('C','O','M','T'): + if (data_size < 2) + return AVERROR_INVALIDDATA; + nb_comments = avio_rb16(pb); + for (i = 0; i < nb_comments; i++) { + int year, mon, day, hour, min, type, ref; + char tmp[24]; + const char *tag; + int metadata_size; + + year = avio_rb16(pb); + mon = avio_r8(pb); + day = avio_r8(pb); + hour = avio_r8(pb); + min = avio_r8(pb); + snprintf(tmp, sizeof(tmp), "%04d-%02d-%02d %02d:%02d", year, mon, day, hour, min); + av_dict_set(&st->metadata, "comment_time", tmp, 0); + + type = avio_rb16(pb); + ref = avio_rb16(pb); + switch (type) { + case 1: + if (!i) + tag = "channel_comment"; + else { + snprintf(tmp, sizeof(tmp), "channel%d_comment", ref); + tag = tmp; + } + break; + case 2: + tag = ref < FF_ARRAY_ELEMS(dsd_source_comment) ? dsd_source_comment[ref] : "source_comment"; + break; + case 3: + tag = ref < FF_ARRAY_ELEMS(dsd_history_comment) ? dsd_history_comment[ref] : "file_history"; + break; + default: + tag = "comment"; + } + + metadata_size = avio_rb32(pb); + if ((res = get_metadata(s, tag, metadata_size)) < 0) { + av_log(s, AV_LOG_ERROR, "cannot allocate metadata tag %s!\n", tag); + return res; + } + + if (metadata_size & 1) + avio_skip(pb, 1); + } 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; } } @@ -233,38 +626,71 @@ static int iff_read_header(AVFormatContext *s) case AVMEDIA_TYPE_AUDIO: avpriv_set_pts_info(st, 32, 1, st->codec->sample_rate); - switch(compression) { - case COMP_NONE: - st->codec->codec_id = AV_CODEC_ID_PCM_S8_PLANAR; - break; - case COMP_FIB: - st->codec->codec_id = AV_CODEC_ID_8SVX_FIB; - break; - case COMP_EXP: - st->codec->codec_id = AV_CODEC_ID_8SVX_EXP; - break; - default: - av_log(s, AV_LOG_ERROR, "unknown compression method\n"); - return -1; + 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; + } + } else if (st->codec->codec_tag != ID_DSD) { + switch (iff->svx8_compression) { + case COMP_NONE: + st->codec->codec_id = AV_CODEC_ID_PCM_S8_PLANAR; + break; + case COMP_FIB: + st->codec->codec_id = AV_CODEC_ID_8SVX_FIB; + break; + case COMP_EXP: + st->codec->codec_id = AV_CODEC_ID_8SVX_EXP; + break; + default: + 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; @@ -278,19 +704,41 @@ 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_DSD || 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); + if (ret<0) { + av_free_packet(pkt); + } else if (ret < iff->body_size) + av_shrink_packet(pkt, ret + 2); + } 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; } @@ -302,4 +750,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 e44af48..3f154ce 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 847c613..8002054 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 */ @@ -34,6 +34,7 @@ static const IdStrMap img_tags[] = { { AV_CODEC_ID_MJPEG, "jps" }, { AV_CODEC_ID_MJPEG, "mpo" }, { 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" }, @@ -49,8 +50,8 @@ 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" }, @@ -64,6 +65,7 @@ static const IdStrMap img_tags[] = { { 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" }, @@ -72,8 +74,10 @@ static const IdStrMap img_tags[] = { { 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/img2.h b/libavformat/img2.h new file mode 100644 index 0000000..dc53c5c --- /dev/null +++ b/libavformat/img2.h @@ -0,0 +1,61 @@ +/* + * Image format + * Copyright (c) 2014 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 + */ + +#ifndef AVFORMAT_IMG2_H +#define AVFORMAT_IMG2_H + +#include <stdint.h> +#include "avformat.h" + +#if HAVE_GLOB +#include <glob.h> +#endif + +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. */ + 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; + +int ff_img_read_header(AVFormatContext *s1); + +int ff_img_read_packet(AVFormatContext *s1, AVPacket *pkt); + +#endif diff --git a/libavformat/img2_alias_pix.c b/libavformat/img2_alias_pix.c new file mode 100644 index 0000000..a3d5cfa --- /dev/null +++ b/libavformat/img2_alias_pix.c @@ -0,0 +1,65 @@ +/* + * Alias PIX image demuxer + * Copyright (c) 2014 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 "img2.h" +#include "libavcodec/bytestream.h" + +static int brender_read_probe(AVProbeData *p) +{ + const uint8_t *b = p->buf; + const uint8_t *end = b + p->buf_size; + int width = bytestream_get_be16(&b); + int height = bytestream_get_be16(&b); + av_unused int ox = bytestream_get_be16(&b); + av_unused int oy = bytestream_get_be16(&b); + int bpp = bytestream_get_be16(&b); + int x, y; + + if (!width || !height) + return 0; + + if (bpp != 24 && bpp != 8) + return 0; + + for (y=0; y<2 && y<height; y++) { + for (x=0; x<width; ) { + int count = *b++; + if (count == 0 || x + count > width) + return 0; + if (b > end) + return AVPROBE_SCORE_MAX / 8; + b += bpp / 8; + x += count; + } + } + + return AVPROBE_SCORE_EXTENSION + 1; +} + +AVInputFormat ff_image2_alias_pix_demuxer = { + .name = "alias_pix", + .long_name = NULL_IF_CONFIG_SMALL("Alias/Wavefront PIX image"), + .priv_data_size = sizeof(VideoDemuxData), + .read_probe = brender_read_probe, + .read_header = ff_img_read_header, + .read_packet = ff_img_read_packet, + .raw_codec_id = AV_CODEC_ID_ALIAS_PIX, +}; diff --git a/libavformat/img2_brender_pix.c b/libavformat/img2_brender_pix.c new file mode 100644 index 0000000..f730626 --- /dev/null +++ b/libavformat/img2_brender_pix.c @@ -0,0 +1,49 @@ +/* + * BRender PIX image demuxer + * Copyright (c) 2014 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 "img2.h" +#include "libavutil/intreadwrite.h" + +static int aliaspix_read_probe(AVProbeData *p) +{ + static const uint8_t brender_magic[16] = { + 0,0,0,0x12,0,0,0,8,0,0,0,2,0,0,0,2 + }; + + if (memcmp(p->buf, brender_magic, sizeof(brender_magic))) + return 0; + + if (AV_RB32(p->buf+16) != 0x03 && + AV_RB32(p->buf+16) != 0x3D) + return 0; + + return AVPROBE_SCORE_MAX-10; +} + +AVInputFormat ff_image2_brender_pix_demuxer = { + .name = "brender_pix", + .long_name = NULL_IF_CONFIG_SMALL("BRender PIX image"), + .priv_data_size = sizeof(VideoDemuxData), + .read_probe = aliaspix_read_probe, + .read_header = ff_img_read_header, + .read_packet = ff_img_read_packet, + .raw_codec_id = AV_CODEC_ID_BRENDER_PIX, +}; diff --git a/libavformat/img2dec.c b/libavformat/img2dec.c index 9acb6f6..a82f50f 100644 --- a/libavformat/img2dec.c +++ b/libavformat/img2dec.c @@ -3,45 +3,47 @@ * 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 */ +#define _BSD_SOURCE +#include <sys/stat.h> #include "libavutil/avstring.h" #include "libavutil/log.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "libavutil/parseutils.h" +#include "libavutil/intreadwrite.h" #include "avformat.h" +#include "avio_internal.h" #include "internal.h" +#include "img2.h" -typedef struct { - const AVClass *class; /**< Class for private options. */ - int img_first; - int img_last; - int img_number; - int img_count; - int is_pipe; - 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 loop; - int start_number; -} VideoDemuxData; +#if HAVE_GLOB +/* 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 */ static const int sizes[][2] = { { 640, 480 }, @@ -70,15 +72,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 +120,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,20 +160,22 @@ 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; } return 0; } -static int img_read_header(AVFormatContext *s1) +int ff_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 +190,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 +203,92 @@ 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 == 2) { +#if !HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC + av_log(s1, AV_LOG_ERROR, "POSIX.1-2008 not supported, nanosecond file timestamps unavailable\n"); + return AVERROR(ENOSYS); +#endif + avpriv_set_pts_info(st, 64, 1, 1000000000); + } else 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) { @@ -206,9 +297,52 @@ static int img_read_header(AVFormatContext *s1) } else if (s1->audio_codec_id) { st->codec->codec_type = AVMEDIA_TYPE_AUDIO; st->codec->codec_id = s1->audio_codec_id; + } else if (s1->iformat->raw_codec_id) { + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->codec->codec_id = s1->iformat->raw_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 (s1->pb) { + int probe_buffer_size = 2048; + uint8_t *probe_buffer = av_realloc(NULL, probe_buffer_size + AVPROBE_PADDING_SIZE); + AVInputFormat *fmt = NULL; + AVProbeData pd = { 0 }; + + if (!probe_buffer) + return AVERROR(ENOMEM); + + probe_buffer_size = avio_read(s1->pb, probe_buffer, probe_buffer_size); + if (probe_buffer_size < 0) { + av_free(probe_buffer); + return probe_buffer_size; + } + memset(probe_buffer + probe_buffer_size, 0, AVPROBE_PADDING_SIZE); + + pd.buf = probe_buffer; + pd.buf_size = probe_buffer_size; + pd.filename = s1->filename; + + while ((fmt = av_iformat_next(fmt))) { + if (fmt->read_header != ff_img_read_header || + !fmt->read_probe || + (fmt->flags & AVFMT_NOFILE) || + !fmt->raw_codec_id) + continue; + if (fmt->read_probe(&pd) > 0) { + st->codec->codec_id = fmt->raw_codec_id; + break; + } + } + ffio_rewind_with_probe_data(s1->pb, &probe_buffer, probe_buffer_size); + } + if (st->codec->codec_id == AV_CODEC_ID_NONE) + 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_id == AV_CODEC_ID_ALIAS_PIX) // we cannot distingiush this from BRENDER_PIX + st->codec->codec_id = AV_CODEC_ID_NONE; } if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO && pix_fmt != AV_PIX_FMT_NONE) @@ -217,10 +351,11 @@ static int img_read_header(AVFormatContext *s1) return 0; } -static int img_read_packet(AVFormatContext *s1, AVPacket *pkt) +int ff_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 +368,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,23 +389,64 @@ 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; } + if (codec->codec_id == AV_CODEC_ID_NONE) { + AVProbeData pd = { 0 }; + AVInputFormat *ifmt; + uint8_t header[PROBE_BUF_MIN + AVPROBE_PADDING_SIZE]; + int ret; + int score = 0; + + ret = avio_read(f[0], header, PROBE_BUF_MIN); + if (ret < 0) + return ret; + memset(header + ret, 0, sizeof(header) - ret); + avio_skip(f[0], -ret); + pd.buf = header; + pd.buf_size = ret; + pd.filename = filename; + + ifmt = av_probe_input_format3(&pd, 1, &score); + if (ifmt && ifmt->read_packet == ff_img_read_packet && ifmt->raw_codec_id) + codec->codec_id = ifmt->raw_codec_id; + } + if (codec->codec_id == AV_CODEC_ID_RAWVIDEO && !codec->width) infer_size(&codec->width, &codec->height, size[0]); } else { f[0] = s1->pb; - if (f[0]->eof_reached) + if (avio_feof(f[0])) return AVERROR(EIO); - size[0] = 4096; + if (s->frame_size > 0) { + size[0] = s->frame_size; + } else if (!s1->streams[0]->parser) { + size[0] = avio_size(s1->pb); + } 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; +#if HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC + if (s->ts_from_file == 2) + pkt->pts = 1000000000*pkt->pts + img_stat.st_mtim.tv_nsec; +#endif + 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 +465,62 @@ 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, 2, DEC, "ts_type" }, + { "none", "none", 0, AV_OPT_TYPE_CONST, {.i64 = 0 }, 0, 2, DEC, "ts_type" }, + { "sec", "second precision", 0, AV_OPT_TYPE_CONST, {.i64 = 1 }, 0, 2, DEC, "ts_type" }, + { "ns", "nano second precision", 0, AV_OPT_TYPE_CONST, {.i64 = 2 }, 0, 2, DEC, "ts_type" }, { NULL }, }; @@ -310,8 +536,10 @@ AVInputFormat ff_image2_demuxer = { .long_name = NULL_IF_CONFIG_SMALL("image2 sequence"), .priv_data_size = sizeof(VideoDemuxData), .read_probe = img_read_probe, - .read_header = img_read_header, - .read_packet = img_read_packet, + .read_header = ff_img_read_header, + .read_packet = ff_img_read_packet, + .read_close = img_read_close, + .read_seek = img_read_seek, .flags = AVFMT_NOFILE, .priv_class = &img2_class, }; @@ -327,8 +555,154 @@ AVInputFormat ff_image2pipe_demuxer = { .name = "image2pipe", .long_name = NULL_IF_CONFIG_SMALL("piped image2 sequence"), .priv_data_size = sizeof(VideoDemuxData), - .read_header = img_read_header, - .read_packet = img_read_packet, + .read_header = ff_img_read_header, + .read_packet = ff_img_read_packet, .priv_class = &img2pipe_class, }; #endif + +static int bmp_probe(AVProbeData *p) +{ + const uint8_t *b = p->buf; + int ihsize; + + if (AV_RB16(b) != 0x424d) + return 0; + + ihsize = AV_RL32(b+14); + if (ihsize < 12 || ihsize > 255) + return 0; + + if (!AV_RN32(b + 6)) { + return AVPROBE_SCORE_EXTENSION + 1; + } else { + return AVPROBE_SCORE_EXTENSION / 4; + } + return 0; +} + +static int dpx_probe(AVProbeData *p) +{ + const uint8_t *b = p->buf; + + if (AV_RN32(b) == AV_RN32("SDPX") || AV_RN32(b) == AV_RN32("XPDS")) + return AVPROBE_SCORE_EXTENSION + 1; + return 0; +} + +static int exr_probe(AVProbeData *p) +{ + const uint8_t *b = p->buf; + + if (AV_RL32(b) == 20000630) + return AVPROBE_SCORE_EXTENSION + 1; + return 0; +} + +static int j2k_probe(AVProbeData *p) +{ + const uint8_t *b = p->buf; + + if (AV_RB64(b) == 0x0000000c6a502020 || + AV_RB32(b) == 0xff4fff51) + return AVPROBE_SCORE_EXTENSION + 1; + return 0; +} + +static int jpegls_probe(AVProbeData *p) +{ + const uint8_t *b = p->buf; + + if (AV_RB32(b) == 0xffd8fff7) + return AVPROBE_SCORE_EXTENSION + 1; + return 0; +} + +static int pictor_probe(AVProbeData *p) +{ + const uint8_t *b = p->buf; + + if (AV_RL16(b) == 0x1234) + return AVPROBE_SCORE_EXTENSION / 4; + return 0; +} + +static int png_probe(AVProbeData *p) +{ + const uint8_t *b = p->buf; + + if (AV_RB64(b) == 0x89504e470d0a1a0a) + return AVPROBE_SCORE_MAX - 1; + return 0; +} + +static int sgi_probe(AVProbeData *p) +{ + const uint8_t *b = p->buf; + + if (AV_RB16(b) == 474 && + (b[2] & ~1) == 0 && + (b[3] & ~3) == 0 && b[3] && + (AV_RB16(b + 4) & ~7) == 0 && AV_RB16(b + 4)) + return AVPROBE_SCORE_EXTENSION + 1; + return 0; +} + +static int sunrast_probe(AVProbeData *p) +{ + const uint8_t *b = p->buf; + + if (AV_RB32(b) == 0x59a66a95) + return AVPROBE_SCORE_EXTENSION + 1; + return 0; +} + +static int tiff_probe(AVProbeData *p) +{ + const uint8_t *b = p->buf; + + if (AV_RB32(b) == 0x49492a00 || + AV_RB32(b) == 0x4D4D002a) + return AVPROBE_SCORE_EXTENSION + 1; + return 0; +} + +static int webp_probe(AVProbeData *p) +{ + const uint8_t *b = p->buf; + + if (AV_RB32(b) == 0x52494646 && + AV_RB32(b + 8) == 0x57454250) + return AVPROBE_SCORE_MAX - 1; + return 0; +} + +#define IMAGEAUTO_DEMUXER(imgname, codecid)\ +static const AVClass imgname ## _class = {\ + .class_name = AV_STRINGIFY(imgname) " demuxer",\ + .item_name = av_default_item_name,\ + .option = options,\ + .version = LIBAVUTIL_VERSION_INT,\ +};\ +AVInputFormat ff_image_ ## imgname ## _pipe_demuxer = {\ + .name = AV_STRINGIFY(imgname) "_pipe",\ + .long_name = NULL_IF_CONFIG_SMALL("piped " AV_STRINGIFY(imgname) " sequence"),\ + .priv_data_size = sizeof(VideoDemuxData),\ + .read_probe = imgname ## _probe,\ + .read_header = ff_img_read_header,\ + .read_packet = ff_img_read_packet,\ + .priv_class = & imgname ## _class,\ + .raw_codec_id = codecid,\ +}; + +IMAGEAUTO_DEMUXER(bmp, AV_CODEC_ID_BMP) +IMAGEAUTO_DEMUXER(dpx, AV_CODEC_ID_DPX) +IMAGEAUTO_DEMUXER(exr, AV_CODEC_ID_EXR) +IMAGEAUTO_DEMUXER(j2k, AV_CODEC_ID_JPEG2000) +IMAGEAUTO_DEMUXER(jpegls, AV_CODEC_ID_JPEGLS) +IMAGEAUTO_DEMUXER(pictor, AV_CODEC_ID_PICTOR) +IMAGEAUTO_DEMUXER(png, AV_CODEC_ID_PNG) +IMAGEAUTO_DEMUXER(sgi, AV_CODEC_ID_SGI) +IMAGEAUTO_DEMUXER(sunrast, AV_CODEC_ID_SUNRAST) +IMAGEAUTO_DEMUXER(tiff, AV_CODEC_ID_TIFF) +IMAGEAUTO_DEMUXER(webp, AV_CODEC_ID_WEBP) diff --git a/libavformat/img2enc.c b/libavformat/img2enc.c index 13e6828..37dfbec 100644 --- a/libavformat/img2enc.c +++ b/libavformat/img2enc.c @@ -3,42 +3,49 @@ * 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/avassert.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; + const char *muxer; } 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); av_strlcpy(img->path, s->filename, sizeof(img->path)); @@ -48,74 +55,111 @@ static int write_header(AVFormatContext *s) else img->is_pipe = 1; + if (st->codec->codec_id == AV_CODEC_ID_GIF) { + img->muxer = "gif"; + } else if (st->codec->codec_id == AV_CODEC_ID_RAWVIDEO) { + const char *str = strrchr(img->path, '.'); + img->split_planes = str + && !av_strcasecmp(str + 1, "y") + && s->nb_streams == 1 + && 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] = "UVAx"[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 if (img->muxer) { + int ret; + AVStream *st; + AVPacket pkt2 = {0}; + AVFormatContext *fmt = NULL; + + av_assert0(!img->split_planes); + + ret = avformat_alloc_output_context2(&fmt, NULL, img->muxer, s->filename); + if (ret < 0) + return ret; + st = avformat_new_stream(fmt, NULL); + if (!st) { + avformat_free_context(fmt); + return AVERROR(ENOMEM); + } + st->id = pkt->stream_index; + + fmt->pb = pb[0]; + if ((ret = av_copy_packet(&pkt2, pkt)) < 0 || + (ret = av_dup_packet(&pkt2)) < 0 || + (ret = avcodec_copy_context(st->codec, s->streams[0]->codec)) < 0 || + (ret = avformat_write_header(fmt, NULL)) < 0 || + (ret = av_interleaved_write_frame(fmt, &pkt2)) < 0 || + (ret = av_write_trailer(fmt)) < 0) { + av_free_packet(&pkt2); + avformat_free_context(fmt); + return ret; + } + av_free_packet(&pkt2); + avformat_free_context(fmt); + } else { avio_write(pb[0], pkt->data, pkt->size); } avio_flush(pb[0]); @@ -130,8 +174,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 +192,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,webp,xbm,j2c,pix", + .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,webp,xbm,xface,pix,y", .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 42b29ef..94c549c 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 9921ce1..3011706 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 @@ -48,9 +52,9 @@ struct AVFormatInternal { * Muxing only. */ int nb_interleaved_streams; -}; -void ff_dynarray_add(intptr_t **tab_ptr, int *nb_ptr, intptr_t elem); + int inject_global_side_data; +}; #ifdef __GNUC__ #define dynarray_add(tab, nb_ptr, elem)\ @@ -58,12 +62,12 @@ 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 @@ -86,6 +90,7 @@ 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 */ int ff_interleave_add_packet(AVFormatContext *s, AVPacket *pkt, int (*compare)(AVFormatContext *, AVPacket *, AVPacket *)); @@ -129,10 +134,11 @@ void ff_sdp_write_media(char *buff, int size, AVStream *st, int idx, * @param dst_stream the stream index within dst to write the packet to * @param pkt the packet to be written * @param src the muxer the packet originally was intended for + * @param interleave 0->use av_write_frame, 1->av_write_interleaved_frame * @return the value av_write_frame returned */ int ff_write_chained(AVFormatContext *dst, int dst_stream, AVPacket *pkt, - AVFormatContext *src); + AVFormatContext *src, int interleave); /** * Get the length in bytes which is needed to store val as v. @@ -251,6 +257,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(). * @@ -320,6 +329,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. */ @@ -346,9 +357,60 @@ 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 chosen timebase allows sample accurate timestamps based + * on the framerate or sample rate for audio streams. It also is + * at least as precise as 1/min_precision would be. + */ +AVRational ff_choose_timebase(AVFormatContext *s, AVStream *st, int min_precision); + +/** * Generate standard extradata for AVC-Intra based on width/height and field * order. */ int 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); + +/** + * Allocate extradata with additional FF_INPUT_BUFFER_PADDING_SIZE at end + * which is always set to 0 and fill it from pb. + * + * @param size size of extradata + * @return >= 0 if OK, AVERROR_xxx on error + */ +int ff_get_extradata(AVCodecContext *avctx, AVIOContext *pb, int size); + +/** + * add frame for rfps calculation. + * + * @param dts timestamp of the i-th frame + * @return 0 if OK, AVERROR_xxx on error + */ +int ff_rfps_add_frame(AVFormatContext *ic, AVStream *st, int64_t dts); + +void ff_rfps_calculate(AVFormatContext *ic); + +/** + * Flags for AVFormatContext.write_uncoded_frame() + */ +enum AVWriteUncodedFrameFlags { + + /** + * Query whether the feature is possible on this stream. + * The frame argument is ignored. + */ + AV_WRITE_UNCODED_FRAME_QUERY = 0x0001, + +}; + + #endif /* AVFORMAT_INTERNAL_H */ diff --git a/libavformat/ipmovie.c b/libavformat/ipmovie.c index 60ae939..01e70e8 100644 --- a/libavformat/ipmovie.c +++ b/libavformat/ipmovie.c @@ -1,21 +1,21 @@ /* * Interplay MVE File Demuxer - * Copyright (c) 2003 The ffmpeg 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 */ @@ -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 (avio_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 (avio_feof(pb)) { chunk_type = CHUNK_EOF; break; } @@ -321,7 +321,7 @@ static int process_ipmovie_chunk(IPMVEContext *s, AVIOContext *pb, case OPCODE_CREATE_TIMER: av_dlog(NULL, "create timer\n"); - if ((opcode_version > 0) || (opcode_size > 6)) { + if ((opcode_version > 0) || (opcode_size != 6)) { av_dlog(NULL, "bad create_timer opcode\n"); chunk_type = CHUNK_BAD; break; @@ -339,7 +339,7 @@ static int process_ipmovie_chunk(IPMVEContext *s, AVIOContext *pb, case OPCODE_INIT_AUDIO_BUFFERS: av_dlog(NULL, "initialize audio buffers\n"); - if ((opcode_version > 1) || (opcode_size > 10)) { + if (opcode_version > 1 || opcode_size > 10 || opcode_size < 6) { av_dlog(NULL, "bad init_audio_buffers opcode\n"); chunk_type = CHUNK_BAD; break; @@ -376,7 +376,9 @@ static int process_ipmovie_chunk(IPMVEContext *s, AVIOContext *pb, case OPCODE_INIT_VIDEO_BUFFERS: av_dlog(NULL, "initialize video buffers\n"); - if ((opcode_version > 2) || (opcode_size > 8)) { + if ((opcode_version > 2) || (opcode_size > 8) || opcode_size < 4 + || opcode_version == 2 && opcode_size < 8 + ) { av_dlog(NULL, "bad init_video_buffers opcode\n"); chunk_type = CHUNK_BAD; break; @@ -449,8 +451,8 @@ static int process_ipmovie_chunk(IPMVEContext *s, AVIOContext *pb, av_dlog(NULL, "set palette\n"); /* check for the logical maximum palette size * (3 * 256 + 4 bytes) */ - if (opcode_size > 0x304) { - av_dlog(NULL, "demux_ipmovie: set_palette opcode too large\n"); + if (opcode_size > 0x304 || opcode_size < 4) { + av_dlog(NULL, "demux_ipmovie: set_palette opcode with invalid size\n"); chunk_type = CHUNK_BAD; break; } @@ -463,7 +465,8 @@ static int process_ipmovie_chunk(IPMVEContext *s, AVIOContext *pb, first_color = AV_RL16(&scratch[0]); last_color = first_color + AV_RL16(&scratch[2]) - 1; /* sanity check (since they are 16 bit values) */ - if ((first_color > 0xFF) || (last_color > 0xFF)) { + if ( (first_color > 0xFF) || (last_color > 0xFF) + || (last_color - first_color + 1)*3 + 4 > opcode_size) { av_dlog(NULL, "demux_ipmovie: set_palette indexes out of range (%d -> %d)\n", first_color, last_color); chunk_type = CHUNK_BAD; @@ -476,7 +479,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 +530,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 +548,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 (avio_feof(pb)) return AVERROR_EOF; } /* initialize private context members */ @@ -561,6 +566,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 +630,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 +640,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 0000000..a267c18 --- /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 0000000..f7f9c84 --- /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 0000000..f9533ec --- /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 0000000..38f15fb --- /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 5d10dca..d768c32 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 */ @@ -71,7 +71,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 */ @@ -95,9 +94,17 @@ const AVCodecTag ff_codec_movvideo_tags[] = { { 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) */ @@ -298,12 +305,15 @@ const AVCodecTag ff_codec_movaudio_tags[] = { { AV_CODEC_ID_SPEEX, MKTAG('s', 'p', 'e', 'x') }, /* Flash Media Server */ { AV_CODEC_ID_SPEEX, MKTAG('S', 'P', 'X', 'N') }, /* ZygoAudio (quality 10 mode) */ { 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 }, }; @@ -431,14 +441,23 @@ static const AVCodecTag mp4_audio_types[] = { int ff_mp4_read_dec_config_descr(AVFormatContext *fc, AVStream *st, AVIOContext *pb) { + enum AVCodecID codec_id; int len, tag; + int ret; int object_type_id = avio_r8(pb); avio_r8(pb); /* stream type */ avio_rb24(pb); /* buffer size db */ avio_rb32(pb); /* max bitrate */ avio_rb32(pb); /* avg bitrate */ - st->codec->codec_id= ff_codec_get_id(ff_mp4_obj_type, object_type_id); + if(avcodec_is_open(st->codec)) { + av_log(fc, AV_LOG_DEBUG, "codec open in read_dec_config_descr\n"); + return -1; + } + + codec_id= ff_codec_get_id(ff_mp4_obj_type, object_type_id); + if (codec_id) + st->codec->codec_id= codec_id; av_dlog(fc, "esds object type id 0x%02x\n", object_type_id); len = ff_mp4_read_descr(fc, pb, &tag); if (tag == MP4DecSpecificDescrTag) { @@ -446,13 +465,10 @@ 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) - return AVERROR(ENOMEM); - avio_read(pb, st->codec->extradata, len); - st->codec->extradata_size = len; + if ((ret = ff_get_extradata(st->codec, pb, len)) < 0) + return ret; if (st->codec->codec_id == AV_CODEC_ID_AAC) { - MPEG4AudioConfig cfg; + MPEG4AudioConfig cfg = {0}; avpriv_mpeg4audio_get_config(&cfg, st->codec->extradata, st->codec->extradata_size * 8, 1); st->codec->channels = cfg.channels; @@ -473,3 +489,104 @@ 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 +} + +const struct AVCodecTag *avformat_get_mov_video_tags(void) +{ + return ff_codec_movvideo_tags; +} + +const struct AVCodecTag *avformat_get_mov_audio_tags(void) +{ + return ff_codec_movaudio_tags; +} diff --git a/libavformat/isom.h b/libavformat/isom.h index 0bc912a..979e967 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 */ @@ -95,6 +95,7 @@ typedef struct MOVSbgp { typedef struct MOVStreamContext { AVIOContext *pb; + int pb_is_copied; int ffindex; ///< AVStream index int next_chunk; unsigned int chunk_count; @@ -109,14 +110,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; @@ -126,6 +130,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 @@ -133,14 +138,20 @@ 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; + int nb_frames_for_fps; + int64_t duration_for_fps; + int32_t *display_matrix; } MOVStreamContext; typedef struct MOVContext { + AVClass *avclass; AVFormatContext *fc; int time_scale; int64_t duration; ///< duration of the longest track @@ -154,7 +165,12 @@ 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; + int moov_retry; } MOVContext; int ff_mp4_read_descr_len(AVIOContext *pb); @@ -219,5 +235,6 @@ int ff_mov_read_esds(AVFormatContext *fc, AVIOContext *pb); 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 3f7f4fe..e994531 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 */ @@ -76,18 +76,32 @@ static av_cold int iss_read_header(AVFormatContext *s) get_token(pb, token, sizeof(token)); //"IMA_ADPCM_Sound" get_token(pb, token, sizeof(token)); //packet size - sscanf(token, "%d", &iss->packet_size); + if (sscanf(token, "%d", &iss->packet_size) != 1) { + av_log(s, AV_LOG_ERROR, "Failed parsing packet size\n"); + return AVERROR_INVALIDDATA; + } get_token(pb, token, sizeof(token)); //File ID get_token(pb, token, sizeof(token)); //out size get_token(pb, token, sizeof(token)); //stereo - sscanf(token, "%d", &stereo); + if (sscanf(token, "%d", &stereo) != 1) { + av_log(s, AV_LOG_ERROR, "Failed parsing stereo flag\n"); + return AVERROR_INVALIDDATA; + } get_token(pb, token, sizeof(token)); //Unknown1 get_token(pb, token, sizeof(token)); //RateDivisor - sscanf(token, "%d", &rate_divisor); + if (sscanf(token, "%d", &rate_divisor) != 1) { + av_log(s, AV_LOG_ERROR, "Failed parsing rate_divisor\n"); + return AVERROR_INVALIDDATA; + } get_token(pb, token, sizeof(token)); //Unknown2 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 56909e3..38b7960 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 b3555f4..c0fdafb 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 3cd1616..5700f01 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" @@ -30,8 +30,9 @@ static int ivf_write_header(AVFormatContext *s) return AVERROR(EINVAL); } ctx = s->streams[0]->codec; - if (ctx->codec_type != AVMEDIA_TYPE_VIDEO || ctx->codec_id != AV_CODEC_ID_VP8) { - av_log(s, AV_LOG_ERROR, "Currently only VP8 is supported!\n"); + if (ctx->codec_type != AVMEDIA_TYPE_VIDEO || + !(ctx->codec_id == AV_CODEC_ID_VP8 || ctx->codec_id == AV_CODEC_ID_VP9)) { + av_log(s, AV_LOG_ERROR, "Currently only VP8 and VP9 are supported!\n"); return AVERROR(EINVAL); } avio_write(pb, "DKIF", 4); diff --git a/libavformat/jacosubdec.c b/libavformat/jacosubdec.c new file mode 100644 index 0000000..9a28870 --- /dev/null +++ b/libavformat/jacosubdec.c @@ -0,0 +1,271 @@ +/* + * 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; + int fs, fe; + return (sscanf(ptr, "%*u:%*u:%*u.%*u %*u:%*u:%*u.%*u %c", &c) == 1 || + (sscanf(ptr, "@%u @%u %c", &fs, &fe, &c) == 3 && fs < fe)); +} + +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 (!avio_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 0000000..a11e45a --- /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 84d55da..21eb14d 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 */ @@ -34,10 +34,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 { @@ -54,8 +54,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; } @@ -166,7 +166,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 (!avio_feof(s->pb) && jv->pts < ast->nb_index_entries) { const AVIndexEntry *e = ast->index_entries + jv->pts; const JVFrame *jvf = jv->frames + jv->pts; diff --git a/libavformat/latmenc.c b/libavformat/latmenc.c index e5e2f87..cb79e84 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 6f11d60..e90aef7 100644 --- a/libavformat/libavformat.v +++ b/libavformat/libavformat.v @@ -1,4 +1,20 @@ LIBAVFORMAT_$MAJOR { global: av*; + #FIXME those are for ffserver + ff_inet_aton; + ff_socket_nonblock; + 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; + #those are deprecated, remove on next bump + url_feof; local: *; }; diff --git a/libavformat/libgme.c b/libavformat/libgme.c new file mode 100644 index 0000000..276477b --- /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 ; + 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 0000000..158a630 --- /dev/null +++ b/libavformat/libmodplug.c @@ -0,0 +1,382 @@ +/* + * 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 +*/ + +#define MODPLUG_STATIC +#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 * const 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 0000000..4a9a21a --- /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_array(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 0000000..ca71f9f --- /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 74e2c49..c57699c 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 */ @@ -234,6 +234,9 @@ static int rtmp_open(URLContext *s, const char *uri, int flags) return 0; fail: av_freep(&ctx->temp_filename); + if (rc) + RTMP_Close(r); + return rc; } diff --git a/libavformat/libsmbclient.c b/libavformat/libsmbclient.c new file mode 100644 index 0000000..892d2db --- /dev/null +++ b/libavformat/libsmbclient.c @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2014 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 <libsmbclient.h> +#include "libavutil/avstring.h" +#include "libavutil/opt.h" +#include "avformat.h" +#include "internal.h" +#include "url.h" + +typedef struct { + const AVClass *class; + SMBCCTX *ctx; + int fd; + int64_t filesize; + int trunc; + int timeout; + char *workgroup; +} LIBSMBContext; + +static void libsmbc_get_auth_data(SMBCCTX *c, const char *server, const char *share, + char *workgroup, int workgroup_len, + char *username, int username_len, + char *password, int password_len) +{ + /* Do nothing yet. Credentials are passed via url. + * Callback must exists, there might be a segmentation fault otherwise. */ +} + +static av_cold int libsmbc_connect(URLContext *h) +{ + LIBSMBContext *libsmbc = h->priv_data; + + libsmbc->ctx = smbc_new_context(); + if (!libsmbc->ctx) { + av_log(h, AV_LOG_ERROR, "Cannot create context: %s.\n", strerror(errno)); + return AVERROR(errno); + } + if (!smbc_init_context(libsmbc->ctx)) { + av_log(h, AV_LOG_ERROR, "Cannot initialize context: %s.\n", strerror(errno)); + return AVERROR(errno); + } + smbc_set_context(libsmbc->ctx); + + smbc_setOptionUserData(libsmbc->ctx, h); + smbc_setFunctionAuthDataWithContext(libsmbc->ctx, libsmbc_get_auth_data); + + if (libsmbc->timeout != -1) + smbc_setTimeout(libsmbc->ctx, libsmbc->timeout); + if (libsmbc->workgroup) + smbc_setWorkgroup(libsmbc->ctx, libsmbc->workgroup); + + if (smbc_init(NULL, 0) < 0) { + av_log(h, AV_LOG_ERROR, "Initialization failed: %s\n", strerror(errno)); + return AVERROR(errno); + } + return 0; +} + +static av_cold int libsmbc_close(URLContext *h) +{ + LIBSMBContext *libsmbc = h->priv_data; + if (libsmbc->fd >= 0) { + smbc_close(libsmbc->fd); + libsmbc->fd = -1; + } + if (libsmbc->ctx) { + smbc_free_context(libsmbc->ctx, 1); + libsmbc->ctx = NULL; + } + return 0; +} + +static av_cold int libsmbc_open(URLContext *h, const char *url, int flags) +{ + LIBSMBContext *libsmbc = h->priv_data; + int access, ret; + struct stat st; + + libsmbc->fd = -1; + libsmbc->filesize = -1; + + if ((ret = libsmbc_connect(h)) < 0) + goto fail; + + if ((flags & AVIO_FLAG_WRITE) && (flags & AVIO_FLAG_READ)) { + access = O_CREAT | O_RDWR; + if (libsmbc->trunc) + access |= O_TRUNC; + } else if (flags & AVIO_FLAG_WRITE) { + access = O_CREAT | O_WRONLY; + if (libsmbc->trunc) + access |= O_TRUNC; + } else + access = O_RDONLY; + + /* 0666 = -rw-rw-rw- = read+write for everyone, minus umask */ + if ((libsmbc->fd = smbc_open(url, access, 0666)) < 0) { + ret = AVERROR(errno); + av_log(h, AV_LOG_ERROR, "File open failed: %s\n", strerror(errno)); + goto fail; + } + + if (smbc_fstat(libsmbc->fd, &st) < 0) + av_log(h, AV_LOG_WARNING, "Cannot stat file: %s\n", strerror(errno)); + else + libsmbc->filesize = st.st_size; + + return 0; + fail: + libsmbc_close(h); + return ret; +} + +static int64_t libsmbc_seek(URLContext *h, int64_t pos, int whence) +{ + LIBSMBContext *libsmbc = h->priv_data; + int64_t newpos; + + if (whence == AVSEEK_SIZE) { + if (libsmbc->filesize == -1) { + av_log(h, AV_LOG_ERROR, "Error during seeking: filesize is unknown.\n"); + return AVERROR(EIO); + } else + return libsmbc->filesize; + } + + if ((newpos = smbc_lseek(libsmbc->fd, pos, whence)) < 0) { + int err = errno; + av_log(h, AV_LOG_ERROR, "Error during seeking: %s\n", strerror(err)); + return AVERROR(err); + } + + return newpos; +} + +static int libsmbc_read(URLContext *h, unsigned char *buf, int size) +{ + LIBSMBContext *libsmbc = h->priv_data; + int bytes_read; + + if ((bytes_read = smbc_read(libsmbc->fd, buf, size)) < 0) { + av_log(h, AV_LOG_ERROR, "Read error: %s\n", strerror(errno)); + return AVERROR(errno); + } + + return bytes_read; +} + +static int libsmbc_write(URLContext *h, const unsigned char *buf, int size) +{ + LIBSMBContext *libsmbc = h->priv_data; + int bytes_written; + + if ((bytes_written = smbc_write(libsmbc->fd, buf, size)) < 0) { + av_log(h, AV_LOG_ERROR, "Write error: %s\n", strerror(errno)); + return AVERROR(errno); + } + + return bytes_written; +} + +#define OFFSET(x) offsetof(LIBSMBContext, x) +#define D AV_OPT_FLAG_DECODING_PARAM +#define E AV_OPT_FLAG_ENCODING_PARAM +static const AVOption options[] = { + {"timeout", "set timeout in ms of socket I/O operations", OFFSET(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 }, + {"workgroup", "set the workgroup used for making connections", OFFSET(workgroup), AV_OPT_TYPE_STRING, { 0 }, 0, 0, D|E }, + {NULL} +}; + +static const AVClass libsmbclient_context_class = { + .class_name = "libsmbc", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + +URLProtocol ff_libsmbclient_protocol = { + .name = "smb", + .url_open = libsmbc_open, + .url_read = libsmbc_read, + .url_write = libsmbc_write, + .url_seek = libsmbc_seek, + .url_close = libsmbc_close, + .priv_data_size = sizeof(LIBSMBContext), + .priv_data_class = &libsmbclient_context_class, + .flags = URL_PROTOCOL_FLAG_NETWORK, +}; diff --git a/libavformat/libssh.c b/libavformat/libssh.c new file mode 100644 index 0000000..3ec60cb --- /dev/null +++ b/libavformat/libssh.c @@ -0,0 +1,318 @@ +/* + * 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> +#define LIBSSH_STATIC +#include <libssh/sftp.h> +#include "libavutil/avstring.h" +#include "libavutil/opt.h" +#include "libavutil/attributes.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; + char *priv_key; +} LIBSSHContext; + +static av_cold int libssh_create_ssh_session(LIBSSHContext *libssh, const char* hostname, unsigned int port) +{ + static const int verbosity = SSH_LOG_NOLOG; + + if (!(libssh->session = ssh_new())) { + av_log(libssh, AV_LOG_ERROR, "SSH session creation failed: %s\n", ssh_get_error(libssh->session)); + return AVERROR(ENOMEM); + } + ssh_options_set(libssh->session, SSH_OPTIONS_HOST, hostname); + ssh_options_set(libssh->session, SSH_OPTIONS_PORT, &port); + ssh_options_set(libssh->session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity); + if (libssh->rw_timeout > 0) { + long timeout = libssh->rw_timeout * 1000; + ssh_options_set(libssh->session, SSH_OPTIONS_TIMEOUT_USEC, &timeout); + } + + if (ssh_connect(libssh->session) != SSH_OK) { + av_log(libssh, AV_LOG_ERROR, "Connection failed: %s\n", ssh_get_error(libssh->session)); + return AVERROR(EIO); + } + + return 0; +} + +static av_cold int libssh_authentication(LIBSSHContext *libssh, const char *user, const char *password) +{ + int authorized = 0; + int auth_methods; + + if (user) + ssh_options_set(libssh->session, SSH_OPTIONS_USER, user); + + if (ssh_userauth_none(libssh->session, NULL) == SSH_AUTH_SUCCESS) + return 0; + + auth_methods = ssh_userauth_list(libssh->session, NULL); + + if (auth_methods & SSH_AUTH_METHOD_PUBLICKEY) { + if (libssh->priv_key) { + ssh_string pub_key; + ssh_private_key priv_key; + int type; + if (!ssh_try_publickey_from_file(libssh->session, libssh->priv_key, &pub_key, &type)) { + priv_key = privatekey_from_file(libssh->session, libssh->priv_key, type, password); + if (ssh_userauth_pubkey(libssh->session, NULL, pub_key, priv_key) == SSH_AUTH_SUCCESS) { + av_log(libssh, AV_LOG_DEBUG, "Authentication successful with selected private key.\n"); + authorized = 1; + } + } else { + av_log(libssh, AV_LOG_DEBUG, "Invalid key is provided.\n"); + return AVERROR(EACCES); + } + } else if (ssh_userauth_autopubkey(libssh->session, password) == SSH_AUTH_SUCCESS) { + av_log(libssh, AV_LOG_DEBUG, "Authentication successful with auto selected key.\n"); + authorized = 1; + } + } + + if (!authorized && (auth_methods & SSH_AUTH_METHOD_PASSWORD)) { + if (ssh_userauth_password(libssh->session, NULL, password) == SSH_AUTH_SUCCESS) { + av_log(libssh, AV_LOG_DEBUG, "Authentication successful with password.\n"); + authorized = 1; + } + } + + if (!authorized) { + av_log(libssh, AV_LOG_ERROR, "Authentication failed.\n"); + return AVERROR(EACCES); + } + + return 0; +} + +static av_cold int libssh_create_sftp_session(LIBSSHContext *libssh) +{ + if (!(libssh->sftp = sftp_new(libssh->session))) { + av_log(libssh, AV_LOG_ERROR, "SFTP session creation failed: %s\n", ssh_get_error(libssh->session)); + return AVERROR(ENOMEM); + } + + if (sftp_init(libssh->sftp) != SSH_OK) { + av_log(libssh, AV_LOG_ERROR, "Error initializing sftp session: %s\n", ssh_get_error(libssh->session)); + return AVERROR(EIO); + } + + return 0; +} + +static av_cold int libssh_open_file(LIBSSHContext *libssh, int flags, const char *file) +{ + int access; + + if ((flags & AVIO_FLAG_WRITE) && (flags & AVIO_FLAG_READ)) { + access = O_CREAT | O_RDWR; + if (libssh->trunc) + access |= O_TRUNC; + } else if (flags & AVIO_FLAG_WRITE) { + access = O_CREAT | O_WRONLY; + if (libssh->trunc) + access |= O_TRUNC; + } else + access = O_RDONLY; + + /* 0666 = -rw-rw-rw- = read+write for everyone, minus umask */ + if (!(libssh->file = sftp_open(libssh->sftp, file, access, 0666))) { + av_log(libssh, AV_LOG_ERROR, "Error opening sftp file: %s\n", ssh_get_error(libssh->session)); + return AVERROR(EIO); + } + + return 0; +} + +static av_cold void libssh_stat_file(LIBSSHContext *libssh) +{ + sftp_attributes stat; + + if (!(stat = sftp_fstat(libssh->file))) { + av_log(libssh, AV_LOG_WARNING, "Cannot stat remote file.\n"); + libssh->filesize = -1; + } else { + libssh->filesize = stat->size; + sftp_attributes_free(stat); + } +} + +static av_cold int libssh_close(URLContext *h) +{ + LIBSSHContext *libssh = h->priv_data; + if (libssh->file) { + sftp_close(libssh->file); + libssh->file = NULL; + } + if (libssh->sftp) { + sftp_free(libssh->sftp); + libssh->sftp = NULL; + } + if (libssh->session) { + ssh_disconnect(libssh->session); + ssh_free(libssh->session); + libssh->session = NULL; + } + return 0; +} + +static av_cold int libssh_open(URLContext *h, const char *url, int flags) +{ + LIBSSHContext *libssh = h->priv_data; + char proto[10], path[MAX_URL_SIZE], hostname[1024], credencials[1024]; + int port = 22, ret; + const char *user = NULL, *pass = NULL; + char *end = NULL; + + 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 ((ret = libssh_create_ssh_session(libssh, hostname, port)) < 0) + goto fail; + + user = av_strtok(credencials, ":", &end); + pass = av_strtok(end, ":", &end); + + if ((ret = libssh_authentication(libssh, user, pass)) < 0) + goto fail; + + if ((ret = libssh_create_sftp_session(libssh)) < 0) + goto fail; + + if ((ret = libssh_open_file(libssh, flags, path)) < 0) + goto fail; + + libssh_stat_file(libssh); + + return 0; + + fail: + libssh_close(h); + return ret; +} + +static int64_t libssh_seek(URLContext *h, int64_t pos, int whence) +{ + LIBSSHContext *libssh = h->priv_data; + int64_t newpos; + + if (libssh->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 libssh->filesize; + case SEEK_SET: + newpos = pos; + break; + case SEEK_CUR: + newpos = sftp_tell64(libssh->file) + pos; + break; + case SEEK_END: + newpos = libssh->filesize + pos; + break; + default: + return AVERROR(EINVAL); + } + + if (newpos < 0) { + av_log(h, AV_LOG_ERROR, "Seeking to nagative position.\n"); + return AVERROR(EINVAL); + } + + if (sftp_seek64(libssh->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 *libssh = h->priv_data; + int bytes_read; + + if ((bytes_read = sftp_read(libssh->file, buf, size)) < 0) { + av_log(libssh, 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 *libssh = h->priv_data; + int bytes_written; + + if ((bytes_written = sftp_write(libssh->file, buf, size)) < 0) { + av_log(libssh, 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 }, + {"private_key", "set path to private key", OFFSET(priv_key), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, D|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 b67b68e..c8ea421 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 0000000..c41809b --- /dev/null +++ b/libavformat/loasdec.c @@ -0,0 +1,94 @@ +/* + * 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" + +#define LOAS_SYNC_WORD 0x2b7 + +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) != LOAS_SYNC_WORD) + 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/lrc.c b/libavformat/lrc.c new file mode 100644 index 0000000..139c650 --- /dev/null +++ b/libavformat/lrc.c @@ -0,0 +1,34 @@ +/* + * LRC lyrics file format decoder + * Copyright (c) 2014 StarBrilliant <m13253@hotmail.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 "metadata.h" +#include "lrc.h" + +const AVMetadataConv ff_lrc_metadata_conv[] = { + {"ti", "title"}, + {"al", "album"}, + {"ar", "artist"}, + {"au", "author"}, + {"by", "creator"}, + {"re", "encoder"}, + {"ve", "encoder_version"}, + {0, 0} +}; diff --git a/libavformat/lrc.h b/libavformat/lrc.h new file mode 100644 index 0000000..0297bc1 --- /dev/null +++ b/libavformat/lrc.h @@ -0,0 +1,29 @@ +/* + * LRC lyrics file format decoder + * Copyright (c) 2014 StarBrilliant <m13253@hotmail.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 + */ + +#ifndef AVFORMAT_LRC_H +#define AVFORMAT_LRC_H + +#include "metadata.h" + +extern const AVMetadataConv ff_lrc_metadata_conv[]; + +#endif diff --git a/libavformat/lrcdec.c b/libavformat/lrcdec.c new file mode 100644 index 0000000..df61853 --- /dev/null +++ b/libavformat/lrcdec.c @@ -0,0 +1,248 @@ +/* + * LRC lyrics file format decoder + * Copyright (c) 2014 StarBrilliant <m13253@hotmail.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 <inttypes.h> +#include <stdint.h> +#include <string.h> + +#include "avformat.h" +#include "internal.h" +#include "lrc.h" +#include "metadata.h" +#include "subtitles.h" +#include "libavutil/bprint.h" +#include "libavutil/dict.h" + +typedef struct LRCContext { + FFDemuxSubtitlesQueue q; + int64_t ts_offset; // offset metadata item +} LRCContext; + +static int64_t find_header(const char *p) +{ + int64_t offset = 0; + while(p[offset] == ' ' || p[offset] == '\t') { + offset++; + } + if(p[offset] == '[' && p[offset + 1] >= 'a' && p[offset + 1] <= 'z') { + return offset; + } else { + return -1; + } +} + +static int64_t count_ts(const char *p) +{ + int64_t offset = 0; + int in_brackets = 0; + + for(;;) { + if(p[offset] == ' ' || p[offset] == '\t') { + offset++; + } else if(p[offset] == '[') { + offset++; + in_brackets++; + } else if (p[offset] == ']' && in_brackets) { + offset++; + in_brackets--; + } else if(in_brackets && + (p[offset] == ':' || p[offset] == '.' || p[offset] == '-' || + (p[offset] >= '0' && p[offset] <= '9'))) { + offset++; + } else { + break; + } + } + return offset; +} + +static int64_t read_ts(const char *p, int64_t *start) +{ + int64_t offset = 0; + uint64_t mm, ss, cs; + + while(p[offset] == ' ' || p[offset] == '\t') { + offset++; + } + if(p[offset] != '[') { + return 0; + } + if(sscanf(p, "[-%"SCNu64":%"SCNu64".%"SCNu64"]", &mm, &ss, &cs) == 3) { + /* Just in case negative pts, players may drop it but we won't. */ + *start = -(int64_t) (mm*60000 + ss*1000 + cs*10); + } else if(sscanf(p, "[%"SCNu64":%"SCNu64".%"SCNu64"]", &mm, &ss, &cs) == 3) { + *start = mm*60000 + ss*1000 + cs*10; + } else { + return 0; + } + do { + offset++; + } while(p[offset] && p[offset-1] != ']'); + return offset; +} + +static int64_t read_line(AVBPrint *buf, AVIOContext *pb) +{ + int64_t pos = avio_tell(pb); + + av_bprint_clear(buf); + while(!avio_feof(pb)) { + int c = avio_r8(pb); + if(c != '\r') { + av_bprint_chars(buf, c, 1); + } + if(c == '\n') { + break; + } + } + return pos; +} + +static int lrc_probe(AVProbeData *p) +{ + int64_t offset = 0; + int64_t mm; + uint64_t ss, cs; + const AVMetadataConv *metadata_item; + + if(!memcmp(p->buf, "\xef\xbb\xbf", 3)) { // Skip UTF-8 BOM header + offset += 3; + } + while(p->buf[offset] == '\n' || p->buf[offset] == '\r') { + offset++; + } + if(p->buf[offset] != '[') { + return 0; + } + offset++; + // Common metadata item but not exist in ff_lrc_metadata_conv + if(!memcmp(p->buf + offset, "offset:", 7)) { + return 40; + } + if(sscanf(p->buf + offset, "%"SCNd64":%"SCNu64".%"SCNu64"]", + &mm, &ss, &cs) == 3) { + return 50; + } + // Metadata items exist in ff_lrc_metadata_conv + for(metadata_item = ff_lrc_metadata_conv; + metadata_item->native; metadata_item++) { + size_t metadata_item_len = strlen(metadata_item->native); + if(p->buf[offset + metadata_item_len] == ':' && + !memcmp(p->buf + offset, metadata_item->native, metadata_item_len)) { + return 40; + } + } + return 5; // Give it 5 scores since it starts with a bracket +} + +static int lrc_read_header(AVFormatContext *s) +{ + LRCContext *lrc = s->priv_data; + AVBPrint line; + AVStream *st; + + st = avformat_new_stream(s, NULL); + if(!st) { + return AVERROR(ENOMEM); + } + avpriv_set_pts_info(st, 64, 1, 1000); + lrc->ts_offset = 0; + st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE; + st->codec->codec_id = AV_CODEC_ID_TEXT; + av_bprint_init(&line, 0, AV_BPRINT_SIZE_UNLIMITED); + + while(!avio_feof(s->pb)) { + int64_t pos = read_line(&line, s->pb); + int64_t header_offset = find_header(line.str); + if(header_offset >= 0) { + char *comma_offset = strchr(line.str, ':'); + if(comma_offset) { + char *right_bracket_offset = strchr(line.str, ']'); + if(!right_bracket_offset) { + continue; + } + + *right_bracket_offset = *comma_offset = '\0'; + if(strcmp(line.str + 1, "offset") || + sscanf(comma_offset + 1, "%"SCNd64, &lrc->ts_offset) != 1) { + av_dict_set(&s->metadata, line.str + 1, comma_offset + 1, 0); + } + *comma_offset = ':'; + *right_bracket_offset = ']'; + } + + } else { + AVPacket *sub; + int64_t ts_start = AV_NOPTS_VALUE; + int64_t ts_stroffset = 0; + int64_t ts_stroffset_incr = 0; + int64_t ts_strlength = count_ts(line.str); + + while((ts_stroffset_incr = read_ts(line.str + ts_stroffset, + &ts_start)) != 0) { + ts_stroffset += ts_stroffset_incr; + sub = ff_subtitles_queue_insert(&lrc->q, line.str + ts_strlength, + line.len - ts_strlength, 0); + if(!sub) { + return AVERROR(ENOMEM); + } + sub->pos = pos; + sub->pts = ts_start - lrc->ts_offset; + sub->duration = -1; + } + } + } + ff_subtitles_queue_finalize(&lrc->q); + ff_metadata_conv_ctx(s, NULL, ff_lrc_metadata_conv); + return 0; +} + +static int lrc_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + LRCContext *lrc = s->priv_data; + return ff_subtitles_queue_read_packet(&lrc->q, pkt); +} + +static int lrc_read_seek(AVFormatContext *s, int stream_index, + int64_t min_ts, int64_t ts, int64_t max_ts, int flags) +{ + LRCContext *lrc = s->priv_data; + return ff_subtitles_queue_seek(&lrc->q, s, stream_index, + min_ts, ts, max_ts, flags); +} + +static int lrc_read_close(AVFormatContext *s) +{ + LRCContext *lrc = s->priv_data; + ff_subtitles_queue_clean(&lrc->q); + return 0; +} + +AVInputFormat ff_lrc_demuxer = { + .name = "lrc", + .long_name = NULL_IF_CONFIG_SMALL("LRC lyrics"), + .priv_data_size = sizeof (LRCContext), + .read_probe = lrc_probe, + .read_header = lrc_read_header, + .read_packet = lrc_read_packet, + .read_close = lrc_read_close, + .read_seek2 = lrc_read_seek +}; diff --git a/libavformat/lrcenc.c b/libavformat/lrcenc.c new file mode 100644 index 0000000..b316ccd --- /dev/null +++ b/libavformat/lrcenc.c @@ -0,0 +1,152 @@ +/* + * LRC lyrics file format decoder + * Copyright (c) 2014 StarBrilliant <m13253@hotmail.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 <inttypes.h> +#include <stdint.h> +#include <string.h> + +#include "avformat.h" +#include "internal.h" +#include "lrc.h" +#include "metadata.h" +#include "subtitles.h" +#include "version.h" +#include "libavutil/bprint.h" +#include "libavutil/dict.h" +#include "libavutil/log.h" +#include "libavutil/macros.h" + +static int lrc_write_header(AVFormatContext *s) +{ + const AVDictionaryEntry *metadata_item; + + if(s->nb_streams != 1 || + s->streams[0]->codec->codec_type != AVMEDIA_TYPE_SUBTITLE) { + av_log(s, AV_LOG_ERROR, + "LRC supports only a single subtitle stream.\n"); + return AVERROR(EINVAL); + } + if(s->streams[0]->codec->codec_id != AV_CODEC_ID_SUBRIP && + s->streams[0]->codec->codec_id != AV_CODEC_ID_TEXT) { + av_log(s, AV_LOG_ERROR, "Unsupported subtitle codec: %s\n", + avcodec_get_name(s->streams[0]->codec->codec_id)); + return AVERROR(EINVAL); + } + avpriv_set_pts_info(s->streams[0], 64, 1, 100); + + ff_metadata_conv_ctx(s, ff_lrc_metadata_conv, NULL); + if(!(s->flags & AVFMT_FLAG_BITEXACT)) { // avoid breaking regression tests + /* LRC provides a metadata slot for specifying encoder version + * in addition to encoder name. We will store LIBAVFORMAT_VERSION + * to it. + */ + av_dict_set(&s->metadata, "ve", AV_STRINGIFY(LIBAVFORMAT_VERSION), 0); + } else { + av_dict_set(&s->metadata, "ve", NULL, 0); + } + for(metadata_item = NULL; + (metadata_item = av_dict_get(s->metadata, "", metadata_item, + AV_DICT_IGNORE_SUFFIX));) { + char *delim; + if(!metadata_item->value[0]) { + continue; + } + while((delim = strchr(metadata_item->value, '\n'))) { + *delim = ' '; + } + while((delim = strchr(metadata_item->value, '\r'))) { + *delim = ' '; + } + avio_printf(s->pb, "[%s:%s]\n", + metadata_item->key, metadata_item->value); + } + avio_printf(s->pb, "\n"); + return 0; +} + +static int lrc_write_packet(AVFormatContext *s, AVPacket *pkt) +{ + if(pkt->pts != AV_NOPTS_VALUE) { + char *data = av_malloc(pkt->size + 1); + char *line; + char *delim; + + if(!data) { + return AVERROR(ENOMEM); + } + memcpy(data, pkt->data, pkt->size); + data[pkt->size] = '\0'; + + for(delim = data + pkt->size - 1; + delim >= data && (delim[0] == '\n' || delim[0] == '\r'); delim--) { + delim[0] = '\0'; // Strip last empty lines + } + line = data; + while(line[0] == '\n' || line[0] == '\r') { + line++; // Skip first empty lines + } + + while(line) { + delim = strchr(line, '\n'); + if(delim) { + if(delim > line && delim[-1] == '\r') { + delim[-1] = '\0'; + } + delim[0] = '\0'; + delim++; + } + if(line[0] == '[') { + av_log(s, AV_LOG_WARNING, + "Subtitle starts with '[', may cause problems with LRC format.\n"); + } + + if(pkt->pts >= 0) { + avio_printf(s->pb, "[%02"PRId64":%02"PRId64".%02"PRId64"]", + (pkt->pts / 6000), + ((pkt->pts / 100) % 60), + (pkt->pts % 100)); + } else { + /* Offset feature of LRC can easily make pts negative, + * we just output it directly and let the player drop it. */ + avio_printf(s->pb, "[-%02"PRId64":%02"PRId64".%02"PRId64"]", + (-pkt->pts) / 6000, + ((-pkt->pts) / 100) % 60, + (-pkt->pts) % 100); + } + avio_printf(s->pb, "%s\n", line); + line = delim; + } + av_free(data); + } + return 0; +} + +AVOutputFormat ff_lrc_muxer = { + .name = "lrc", + .long_name = NULL_IF_CONFIG_SMALL("LRC lyrics"), + .extensions = "lrc", + .priv_data_size = 0, + .write_header = lrc_write_header, + .write_packet = lrc_write_packet, + .flags = AVFMT_VARIABLE_FPS | AVFMT_GLOBALHEADER | + AVFMT_TS_NEGATIVE | AVFMT_TS_NONSTRICT, + .subtitle_codec = AV_CODEC_ID_SUBRIP +}; diff --git a/libavformat/lvfdec.c b/libavformat/lvfdec.c new file mode 100644 index 0000000..81aec59 --- /dev/null +++ b/libavformat/lvfdec.c @@ -0,0 +1,152 @@ +/* + * 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 0; + + if (!AV_RL32(p->buf + 16) || AV_RL32(p->buf + 16) > 256) + return AVPROBE_SCORE_MAX / 8; + + return AVPROBE_SCORE_EXTENSION; +} + +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 (!avio_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 (!avio_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 192d4b0..11d6da5 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 */ @@ -92,7 +92,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 (avio_feof(s->pb)) return AVERROR_EOF; memmove(buf, &buf[1], LXF_IDENT_LENGTH-1); @@ -140,8 +140,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)) @@ -260,6 +261,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 04bd062..ead0066 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 > 4 ? AVPROBE_SCORE_EXTENSION : AVPROBE_SCORE_EXTENSION/2; return 0; } diff --git a/libavformat/matroska.c b/libavformat/matroska.c index 237f26f..25cc025 100644 --- a/libavformat/matroska.c +++ b/libavformat/matroska.c @@ -1,21 +1,21 @@ /* * Matroska common data - * Copyright (c) 2003-2004 The ffmpeg Project + * 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 */ @@ -23,6 +23,9 @@ #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}, @@ -35,6 +38,7 @@ const CodecTags ff_mkv_codec_tags[]={ {"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}, @@ -55,14 +59,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}, @@ -80,6 +97,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}, @@ -96,6 +114,8 @@ 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}, + {"binary" , AV_CODEC_ID_BIN_DATA}, {"" , AV_CODEC_ID_NONE} }; @@ -106,6 +126,30 @@ const AVMetadataConv ff_mkv_metadata_conv[] = { { 0 } }; +const char * const ff_matroska_video_stereo_mode[MATROSKA_VIDEO_STEREOMODE_TYPE_NB] = { + "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", +}; + int ff_mkv_stereo3d_conv(AVStream *st, MatroskaVideoStereoModeType stereo_mode) { AVPacketSideData *sd, *tmp; diff --git a/libavformat/matroska.h b/libavformat/matroska.h index d8f4f8e..391c56c 100644 --- a/libavformat/matroska.h +++ b/libavformat/matroska.h @@ -1,21 +1,21 @@ /* * Matroska constants - * Copyright (c) 2003-2004 The ffmpeg Project + * 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,8 +78,13 @@ #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 @@ -87,6 +92,7 @@ #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 @@ -114,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 @@ -132,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 @@ -142,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 */ @@ -171,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 */ @@ -178,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 @@ -212,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 { @@ -245,16 +269,32 @@ 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_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_STEREOMODE_TYPE_NB]; +extern const char * const ff_matroska_video_stereo_plane[MATROSKA_VIDEO_STEREO_PLANE_COUNT]; + +/* AVStream Metadata tag keys for WebM Dash Manifest */ +#define INITIALIZATION_RANGE "webm_dash_manifest_initialization_range" +#define CUES_START "webm_dash_manifest_cues_start" +#define CUES_END "webm_dash_manifest_cues_end" +#define FILENAME "webm_dash_manifest_file_name" +#define BANDWIDTH "webm_dash_manifest_bandwidth" +#define DURATION "webm_dash_manifest_duration" +#define CLUSTER_KEYFRAME "webm_dash_manifest_cluster_keyframe" +#define CUE_TIMESTAMPS "webm_dash_manifest_cue_timestamps" +#define TRACK_NUMBER "webm_dash_manifest_track_number" +#define CODEC_PRIVATE_SIZE "webm_dash_manifest_codec_priv_size" int ff_mkv_stereo3d_conv(AVStream *st, MatroskaVideoStereoModeType stereo_mode); diff --git a/libavformat/matroskadec.c b/libavformat/matroskadec.c index 59fc34b..75a8a5a 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 */ @@ -40,6 +40,7 @@ #endif #include "libavutil/avstring.h" +#include "libavutil/base64.h" #include "libavutil/dict.h" #include "libavutil/intfloat.h" #include "libavutil/intreadwrite.h" @@ -70,6 +71,7 @@ typedef enum { EBML_NEST, EBML_PASS, EBML_STOP, + EBML_SINT, EBML_TYPE_COUNT } EbmlType; @@ -111,9 +113,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 { @@ -122,8 +130,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 { @@ -144,6 +153,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; @@ -155,14 +173,17 @@ typedef struct { uint64_t default_duration; uint64_t flag_default; uint64_t flag_forced; + uint64_t seek_preroll; MatroskaTrackVideo video; MatroskaTrackAudio audio; + MatroskaTrackOperation operation; EbmlList encodings; uint64_t codec_delay; AVStream *stream; int64_t end_timecode; int ms_compat; + uint64_t max_block_additional_id; } MatroskaTrack; typedef struct { @@ -241,6 +262,8 @@ typedef struct { uint64_t time_scale; double duration; char *title; + char *muxingapp; + EbmlBin date_utc; EbmlList tracks; EbmlList attachments; EbmlList chapters; @@ -278,6 +301,9 @@ typedef struct { int64_t reference; uint64_t non_simple; EbmlBin bin; + uint64_t additional_id; + EbmlBin additional; + int64_t discard_padding; } MatroskaBlock; static EbmlSyntax ebml_header[] = { @@ -301,19 +327,20 @@ static EbmlSyntax matroska_info[] = { { MATROSKA_ID_DURATION, EBML_FLOAT, 0, offsetof(MatroskaDemuxContext, duration) }, { 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_MUXINGAPP, EBML_UTF8, 0, offsetof(MatroskaDemuxContext, muxingapp) }, + { 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_VIDEOALPHAMODE, EBML_UINT, 0, offsetof(MatroskaTrackVideo, alpha_mode) }, { MATROSKA_ID_VIDEOPIXELCROPB, EBML_NONE }, { MATROSKA_ID_VIDEOPIXELCROPT, EBML_NONE }, { MATROSKA_ID_VIDEOPIXELCROPL, EBML_NONE }, @@ -339,10 +366,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 } }; @@ -352,6 +390,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) }, @@ -360,14 +414,17 @@ static EbmlSyntax matroska_track[] = { { MATROSKA_ID_CODECID, EBML_STR, 0, offsetof(MatroskaTrack, codec_id) }, { MATROSKA_ID_CODECPRIVATE, EBML_BIN, 0, offsetof(MatroskaTrack, codec_priv) }, { MATROSKA_ID_CODECDELAY, EBML_UINT, 0, offsetof(MatroskaTrack, codec_delay) }, - { MATROSKA_ID_TRACKLANGUAGE, EBML_UTF8, 0, offsetof(MatroskaTrack, language), { .s = "eng" } }, + { MATROSKA_ID_TRACKLANGUAGE, EBML_UTF8, 0, offsetof(MatroskaTrack, language), { .s = "eng" } }, { MATROSKA_ID_TRACKDEFAULTDURATION, EBML_UINT, 0, offsetof(MatroskaTrack, default_duration) }, - { MATROSKA_ID_TRACKTIMECODESCALE, EBML_FLOAT, 0, offsetof(MatroskaTrack, time_scale), { .f = 1.0 } }, - { MATROSKA_ID_TRACKFLAGDEFAULT, EBML_UINT, 0, offsetof(MatroskaTrack, flag_default), { .u = 1 } }, - { MATROSKA_ID_TRACKFLAGFORCED, EBML_UINT, 0, offsetof(MatroskaTrack, flag_forced), { .u = 0 } }, + { MATROSKA_ID_TRACKTIMECODESCALE, EBML_FLOAT, 0, offsetof(MatroskaTrack, time_scale), { .f = 1.0 } }, + { MATROSKA_ID_TRACKFLAGDEFAULT, EBML_UINT, 0, offsetof(MatroskaTrack, flag_default), { .u = 1 } }, + { 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_SEEKPREROLL, EBML_UINT, 0, offsetof(MatroskaTrack, seek_preroll) }, { MATROSKA_ID_TRACKFLAGENABLED, EBML_NONE }, { MATROSKA_ID_TRACKFLAGLACING, EBML_NONE }, { MATROSKA_ID_CODECNAME, EBML_NONE }, @@ -376,7 +433,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 } }; @@ -434,6 +490,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 } }; @@ -507,11 +565,24 @@ 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_BLOCKREFERENCE, EBML_UINT, 0, offsetof(MatroskaBlock, reference) }, + { MATROSKA_ID_BLOCKDURATION, EBML_UINT, 0, offsetof(MatroskaBlock, duration) }, + { MATROSKA_ID_DISCARDPADDING, EBML_SINT, 0, offsetof(MatroskaBlock, discard_padding) }, + { MATROSKA_ID_BLOCKREFERENCE, EBML_SINT, 0, offsetof(MatroskaBlock, reference) }, { MATROSKA_ID_CODECSTATE, EBML_NONE }, { 1, EBML_UINT, 0, offsetof(MatroskaBlock, non_simple), { .u = 1 } }, { 0 } @@ -583,7 +654,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 (!avio_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 || @@ -636,7 +707,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 (!avio_feof(pb)) { int64_t pos = avio_tell(pb); av_log(matroska->ctx, AV_LOG_ERROR, "Read error at pos. %"PRIu64" (0x%"PRIx64")\n", @@ -700,6 +771,30 @@ static int ebml_read_uint(AVIOContext *pb, int size, uint64_t *num) } /* + * Read the next element as a signed int. + * 0 is success, < 0 is failure. + */ +static int ebml_read_sint(AVIOContext *pb, int size, int64_t *num) +{ + int n = 1; + + if (size > 8) + return AVERROR_INVALIDDATA; + + if (size == 0) { + *num = 0; + } else { + *num = sign_extend(avio_r8(pb), 8); + + /* big-endian ordering; build up number */ + while (n++ < size) + *num = (*num << 8) | avio_r8(pb); + } + + return 0; +} + +/* * Read the next element as a float. * 0 is success, < 0 is failure. */ @@ -746,14 +841,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_mallocz(length + FF_INPUT_BUFFER_PADDING_SIZE))) + 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); } @@ -897,16 +993,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++; @@ -928,6 +1023,9 @@ static int ebml_parse_elem(MatroskaDemuxContext *matroska, case EBML_UINT: res = ebml_read_uint(pb, length, data); break; + case EBML_SINT: + res = ebml_read_sint(pb, length, data); + break; case EBML_FLOAT: res = ebml_read_float(pb, length, data); break; @@ -949,6 +1047,8 @@ static int ebml_parse_elem(MatroskaDemuxContext *matroska, case EBML_STOP: return 1; default: + if (ffio_limit(pb, length) != length) + return AVERROR(EIO); return avio_skip(pb, length) < 0 ? AVERROR(EIO) : 0; } if (res == AVERROR_INVALIDDATA) @@ -1058,7 +1158,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) { @@ -1067,6 +1167,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; @@ -1116,7 +1221,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); @@ -1148,7 +1256,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); @@ -1175,6 +1286,7 @@ failed: return result; } +#if FF_API_ASS_SSA static void matroska_fix_ass_packet(MatroskaDemuxContext *matroska, AVPacket *pkt, uint64_t display_duration) { @@ -1184,7 +1296,8 @@ static void matroska_fix_ass_packet(MatroskaDemuxContext *matroska, for (; *ptr != ',' && ptr < end - 1; ptr++) ; if (*ptr == ',') - layer = ++ptr; + ptr++; + layer = ptr; for (; *ptr != ',' && ptr < end - 1; ptr++) ; if (*ptr == ',') { @@ -1220,17 +1333,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) @@ -1375,27 +1488,21 @@ 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) +static void matroska_add_index_entries(MatroskaDemuxContext *matroska) { - EbmlList *seekhead_list = &matroska->seekhead; - MatroskaSeekhead *seekhead = seekhead_list->elem; 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 && @@ -1418,6 +1525,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" }; @@ -1439,6 +1561,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_parse_flac(AVFormatContext *s, MatroskaTrack *track, int *offset) @@ -1500,6 +1633,7 @@ static int matroska_parse_tracks(AVFormatContext *s) MatroskaTrack *tracks = matroska->tracks.elem; AVStream *st; int i, j, ret; + int k; for (i = 0; i < matroska->tracks.nb_elem; i++) { MatroskaTrack *track = &tracks[i]; @@ -1509,12 +1643,16 @@ static int matroska_parse_tracks(AVFormatContext *s) uint8_t *extradata = NULL; int extradata_size = 0; int extradata_offset = 0; + uint32_t fourcc = 0; AVIOContext b; + char* key_id_base64 = NULL; + int bit_depth = -1; /* 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); @@ -1526,10 +1664,12 @@ static int matroska_parse_tracks(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; @@ -1538,8 +1678,24 @@ static int matroska_parse_tracks(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 @@ -1549,7 +1705,7 @@ static int matroska_parse_tracks(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"); @@ -1579,16 +1735,28 @@ static int matroska_parse_tracks(AVFormatContext *s) } st = track->stream = avformat_new_stream(s, NULL); - if (!st) + if (!st) { + 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.size >= 40 && track->codec_priv.data) { track->ms_compat = 1; - track->video.fourcc = AV_RL32(track->codec_priv.data + 16); + bit_depth = AV_RL16(track->codec_priv.data + 14); + fourcc = AV_RL32(track->codec_priv.data + 16); codec_id = ff_codec_get_id(ff_codec_bmp_tags, - track->video.fourcc); + fourcc); + if (!codec_id) + codec_id = ff_codec_get_id(ff_codec_movvideo_tags, + fourcc); extradata_offset = 40; } else if (!strcmp(track->codec_id, "A_MS/ACM") && track->codec_priv.size >= 14 && @@ -1602,12 +1770,26 @@ static int matroska_parse_tracks(AVFormatContext *s) return ret; codec_id = st->codec->codec_id; extradata_offset = FFMIN(track->codec_priv.size, 18); + } else if (!strcmp(track->codec_id, "A_QUICKTIME") + && (track->codec_priv.size >= 86) + && (track->codec_priv.data)) { + fourcc = AV_RL32(track->codec_priv.data + 4); + codec_id = ff_codec_get_id(ff_codec_movaudio_tags, fourcc); + if (ff_codec_get_id(ff_codec_movaudio_tags, AV_RL32(track->codec_priv.data))) { + fourcc = AV_RL32(track->codec_priv.data); + codec_id = ff_codec_get_id(ff_codec_movaudio_tags, fourcc); + } } else if (!strcmp(track->codec_id, "V_QUICKTIME") && - (track->codec_priv.size >= 86) && + (track->codec_priv.size >= 21) && (track->codec_priv.data)) { - 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 + 4); + codec_id = ff_codec_get_id(ff_codec_movvideo_tags, fourcc); + if (ff_codec_get_id(ff_codec_movvideo_tags, AV_RL32(track->codec_priv.data))) { + fourcc = AV_RL32(track->codec_priv.data); + codec_id = ff_codec_get_id(ff_codec_movvideo_tags, fourcc); + } + if (codec_id == AV_CODEC_ID_NONE && AV_RL32(track->codec_priv.data+4) == AV_RL32("SMI ")) + codec_id = AV_CODEC_ID_SVQ3; } else if (codec_id == AV_CODEC_ID_PCM_S16BE) { switch (track->audio.bitdepth) { case 8: @@ -1651,7 +1833,7 @@ static int matroska_parse_tracks(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. */ @@ -1667,7 +1849,7 @@ static int matroska_parse_tracks(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) return AVERROR(ENOMEM); ffio_init_context(&b, extradata, extradata_size, 1, @@ -1676,9 +1858,12 @@ static int matroska_parse_tracks(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 || @@ -1687,11 +1872,13 @@ static int matroska_parse_tracks(AVFormatContext *s) } 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); @@ -1702,20 +1889,22 @@ static int matroska_parse_tracks(AVFormatContext *s) track->audio.sub_packet_h = avio_rb16(&b); track->audio.frame_size = avio_rb16(&b); track->audio.sub_packet_size = avio_rb16(&b); - if (flavor <= 0 || + if (flavor < 0 || track->audio.coded_framesize <= 0 || 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]; } @@ -1726,6 +1915,8 @@ static int matroska_parse_tracks(AVFormatContext *s) ret = matroska_parse_flac(s, track, &extradata_offset); if (ret < 0) return ret; + } else if (codec_id == AV_CODEC_ID_PRORES && track->codec_priv.size == 4) { + fourcc = AV_RL32(track->codec_priv.data); } track->codec_priv.size -= extradata_offset; @@ -1744,7 +1935,7 @@ static int matroska_parse_tracks(AVFormatContext *s) st->time_base); st->codec->codec_id = codec_id; - st->start_time = 0; + if (strcmp(track->language, "und")) av_dict_set(&st->metadata, "language", track->language, 0); av_dict_set(&st->metadata, "title", track->name, 0); @@ -1759,11 +1950,8 @@ static int matroska_parse_tracks(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) + 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); @@ -1771,8 +1959,12 @@ static int matroska_parse_tracks(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; + if (bit_depth >= 0) + st->codec->bits_per_coded_sample = bit_depth; st->codec->width = track->video.pixel_width; st->codec->height = track->video.pixel_height; av_reduce(&st->sample_aspect_ratio.num, @@ -1780,12 +1972,39 @@ static int matroska_parse_tracks(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_STEREOMODE_TYPE_NB) + 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; + } } // add stream level stereo3d side data if it is a supported format if (track->video.stereo_mode < MATROSKA_VIDEO_STEREOMODE_TYPE_NB && @@ -1798,11 +2017,39 @@ static int matroska_parse_tracks(AVFormatContext *s) st->codec->codec_type = AVMEDIA_TYPE_AUDIO; st->codec->sample_rate = track->audio.out_samplerate; st->codec->channels = track->audio.channels; + if (!st->codec->bits_per_coded_sample) + 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, + st->time_base, + (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; } } @@ -1829,13 +2076,19 @@ static int matroska_read_header(AVFormatContext *s) ebml.version > EBML_VERSION || ebml.max_size > sizeof(uint64_t) || ebml.id_length > sizeof(uint32_t) || - ebml.doctype_version > 3) { + 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])) @@ -1868,6 +2121,10 @@ static int matroska_read_header(AVFormatContext *s) matroska->ctx->duration = matroska->duration * matroska->time_scale * 1000 / AV_TIME_BASE; av_dict_set(&s->metadata, "title", matroska->title, 0); + av_dict_set(&s->metadata, "encoder", matroska->muxingapp, 0); + + if (matroska->date_utc.size == 8) + matroska_metadata_creation_time(&s->metadata, AV_RB64(matroska->date_utc.data)); res = matroska_parse_tracks(s); if (res < 0) @@ -1886,10 +2143,8 @@ static int matroska_read_header(AVFormatContext *s) av_dict_set(&st->metadata, "mimetype", attachments[j].mime, 0); st->codec->codec_id = AV_CODEC_ID_NONE; st->codec->codec_type = AVMEDIA_TYPE_ATTACHMENT; - st->codec->extradata = av_malloc(attachments[j].bin.size); - if (!st->codec->extradata) + if (ff_alloc_extradata(st->codec, attachments[j].bin.size)) break; - st->codec->extradata_size = attachments[j].bin.size; memcpy(st->codec->extradata, attachments[j].bin.data, attachments[j].bin.size); @@ -1913,11 +2168,15 @@ static int matroska_read_header(AVFormatContext *s) (AVRational) { 1, 1000000000 }, chapters[i].start, chapters[i].end, chapters[i].title); - av_dict_set(&chapters[i].chapter->metadata, - "title", chapters[i].title, 0); + if (chapters[i].chapter) { + av_dict_set(&chapters[i].chapter->metadata, + "title", chapters[i].title, 0); + } max_start = chapters[i].start; } + matroska_add_index_entries(matroska); + matroska_convert_tags(s); return 0; @@ -1988,7 +2247,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; @@ -2003,18 +2262,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; @@ -2039,10 +2298,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; @@ -2052,10 +2311,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; @@ -2082,7 +2341,7 @@ static int matroska_parse_laces(MatroskaDemuxContext *matroska, uint8_t **buf, static int matroska_parse_rm_audio(MatroskaDemuxContext *matroska, MatroskaTrack *track, AVStream *st, uint8_t *data, int size, uint64_t timecode, - uint64_t duration, int64_t pos) + int64_t pos) { int a = st->codec->block_align; int sps = track->audio.sub_packet_size; @@ -2112,7 +2371,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 || w%sps) { av_log(matroska->ctx, AV_LOG_ERROR, "Corrupt generic RM-style audio packet size\n"); return AVERROR_INVALIDDATA; @@ -2132,8 +2391,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); @@ -2228,18 +2490,134 @@ 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) { + 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) { + 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, + int64_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; @@ -2258,17 +2636,19 @@ static int matroska_parse_frame(MatroskaDemuxContext *matroska, pkt_data = wv_data; } - if (st->codec->codec_id == AV_CODEC_ID_PRORES) + if (st->codec->codec_id == AV_CODEC_ID_PRORES && + AV_RB32(&data[4]) != MKBETAG('i', 'c', 'p', 'f')) offset = 8; pkt = av_mallocz(sizeof(AVPacket)); /* 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) { + if (st->codec->codec_id == AV_CODEC_ID_PRORES && offset == 8) { uint8_t *buf = pkt->data; bytestream_put_be32(&buf, pkt_size); bytestream_put_be32(&buf, MKBETAG('i', 'c', 'p', 'f')); @@ -2277,23 +2657,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) { + 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) { + 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 && @@ -2305,6 +2733,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; @@ -2317,7 +2749,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, int64_t discard_padding) { uint64_t timecode = AV_NOPTS_VALUE; MatroskaTrack *track; @@ -2326,7 +2759,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"); @@ -2345,8 +2779,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; @@ -2366,9 +2801,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, @@ -2377,22 +2817,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 || @@ -2400,20 +2847,31 @@ static int matroska_parse_block(MatroskaDemuxContext *matroska, uint8_t *data, st->codec->block_align && track->audio.sub_packet_size) { 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: @@ -2461,18 +2919,21 @@ 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; } @@ -2493,15 +2954,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); + is_keyframe, NULL, 0, 0, pos, + blocks[i].discard_padding); } ebml_free(matroska_cluster, &cluster); return res; @@ -2510,22 +2970,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, @@ -2537,13 +2991,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) { @@ -2558,8 +3012,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++) { @@ -2572,21 +3026,38 @@ static int matroska_read_seek(AVFormatContext *s, int stream_index, 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) @@ -2606,6 +3077,351 @@ static int matroska_read_close(AVFormatContext *s) return 0; } +typedef struct { + int64_t start_time_ns; + int64_t end_time_ns; + int64_t start_offset; + int64_t end_offset; +} CueDesc; + +/* This function searches all the Cues and returns the CueDesc corresponding the + * the timestamp ts. Returned CueDesc will be such that start_time_ns <= ts < + * end_time_ns. All 4 fields will be set to -1 if ts >= file's duration. + */ +static CueDesc get_cue_desc(AVFormatContext *s, int64_t ts, int64_t cues_start) { + MatroskaDemuxContext *matroska = s->priv_data; + CueDesc cue_desc; + int i; + int nb_index_entries = s->streams[0]->nb_index_entries; + AVIndexEntry *index_entries = s->streams[0]->index_entries; + if (ts >= matroska->duration * matroska->time_scale) return (CueDesc) {-1, -1, -1, -1}; + for (i = 1; i < nb_index_entries; i++) { + if (index_entries[i - 1].timestamp * matroska->time_scale <= ts && + index_entries[i].timestamp * matroska->time_scale > ts) { + break; + } + } + --i; + cue_desc.start_time_ns = index_entries[i].timestamp * matroska->time_scale; + cue_desc.start_offset = index_entries[i].pos - matroska->segment_start; + if (i != nb_index_entries - 1) { + cue_desc.end_time_ns = index_entries[i + 1].timestamp * matroska->time_scale; + cue_desc.end_offset = index_entries[i + 1].pos - matroska->segment_start; + } else { + cue_desc.end_time_ns = matroska->duration * matroska->time_scale; + // FIXME: this needs special handling for files where Cues appear + // before Clusters. the current logic assumes Cues appear after + // Clusters. + cue_desc.end_offset = cues_start - matroska->segment_start; + } + return cue_desc; +} + +static int webm_clusters_start_with_keyframe(AVFormatContext *s) +{ + MatroskaDemuxContext *matroska = s->priv_data; + int64_t cluster_pos, before_pos; + int index, rv = 1; + if (s->streams[0]->nb_index_entries <= 0) return 0; + // seek to the first cluster using cues. + index = av_index_search_timestamp(s->streams[0], 0, 0); + if (index < 0) return 0; + cluster_pos = s->streams[0]->index_entries[index].pos; + before_pos = avio_tell(s->pb); + while (1) { + int64_t cluster_id = 0, cluster_length = 0; + AVPacket *pkt; + avio_seek(s->pb, cluster_pos, SEEK_SET); + // read cluster id and length + ebml_read_num(matroska, matroska->ctx->pb, 4, &cluster_id); + ebml_read_length(matroska, matroska->ctx->pb, &cluster_length); + if (cluster_id != 0xF43B675) { // done with all clusters + break; + } + avio_seek(s->pb, cluster_pos, SEEK_SET); + matroska->current_id = 0; + matroska_clear_queue(matroska); + if (matroska_parse_cluster(matroska) < 0 || + matroska->num_packets <= 0) { + break; + } + pkt = matroska->packets[0]; + cluster_pos += cluster_length + 12; // 12 is the offset of the cluster id and length. + if (!(pkt->flags & AV_PKT_FLAG_KEY)) { + rv = 0; + break; + } + } + avio_seek(s->pb, before_pos, SEEK_SET); + return rv; +} + +static int buffer_size_after_time_downloaded(int64_t time_ns, double search_sec, int64_t bps, + double min_buffer, double* buffer, + double* sec_to_download, AVFormatContext *s, + int64_t cues_start) +{ + double nano_seconds_per_second = 1000000000.0; + double time_sec = time_ns / nano_seconds_per_second; + int rv = 0; + int64_t time_to_search_ns = (int64_t)(search_sec * nano_seconds_per_second); + int64_t end_time_ns = time_ns + time_to_search_ns; + double sec_downloaded = 0.0; + CueDesc desc_curr = get_cue_desc(s, time_ns, cues_start); + if (desc_curr.start_time_ns == -1) + return -1; + *sec_to_download = 0.0; + + // Check for non cue start time. + if (time_ns > desc_curr.start_time_ns) { + int64_t cue_nano = desc_curr.end_time_ns - time_ns; + double percent = (double)(cue_nano) / (desc_curr.end_time_ns - desc_curr.start_time_ns); + double cueBytes = (desc_curr.end_offset - desc_curr.start_offset) * percent; + double timeToDownload = (cueBytes * 8.0) / bps; + + sec_downloaded += (cue_nano / nano_seconds_per_second) - timeToDownload; + *sec_to_download += timeToDownload; + + // Check if the search ends within the first cue. + if (desc_curr.end_time_ns >= end_time_ns) { + double desc_end_time_sec = desc_curr.end_time_ns / nano_seconds_per_second; + double percent_to_sub = search_sec / (desc_end_time_sec - time_sec); + sec_downloaded = percent_to_sub * sec_downloaded; + *sec_to_download = percent_to_sub * *sec_to_download; + } + + if ((sec_downloaded + *buffer) <= min_buffer) { + return 1; + } + + // Get the next Cue. + desc_curr = get_cue_desc(s, desc_curr.end_time_ns, cues_start); + } + + while (desc_curr.start_time_ns != -1) { + int64_t desc_bytes = desc_curr.end_offset - desc_curr.start_offset; + int64_t desc_ns = desc_curr.end_time_ns - desc_curr.start_time_ns; + double desc_sec = desc_ns / nano_seconds_per_second; + double bits = (desc_bytes * 8.0); + double time_to_download = bits / bps; + + sec_downloaded += desc_sec - time_to_download; + *sec_to_download += time_to_download; + + if (desc_curr.end_time_ns >= end_time_ns) { + double desc_end_time_sec = desc_curr.end_time_ns / nano_seconds_per_second; + double percent_to_sub = search_sec / (desc_end_time_sec - time_sec); + sec_downloaded = percent_to_sub * sec_downloaded; + *sec_to_download = percent_to_sub * *sec_to_download; + + if ((sec_downloaded + *buffer) <= min_buffer) + rv = 1; + break; + } + + if ((sec_downloaded + *buffer) <= min_buffer) { + rv = 1; + break; + } + + desc_curr = get_cue_desc(s, desc_curr.end_time_ns, cues_start); + } + *buffer = *buffer + sec_downloaded; + return rv; +} + +/* This function computes the bandwidth of the WebM file with the help of + * buffer_size_after_time_downloaded() function. Both of these functions are + * adapted from WebM Tools project and are adapted to work with FFmpeg's + * Matroska parsing mechanism. + * + * Returns the bandwidth of the file on success; -1 on error. + * */ +static int64_t webm_dash_manifest_compute_bandwidth(AVFormatContext *s, int64_t cues_start) +{ + MatroskaDemuxContext *matroska = s->priv_data; + AVStream *st = s->streams[0]; + double bandwidth = 0.0; + int i; + + for (i = 0; i < st->nb_index_entries; i++) { + int64_t prebuffer_ns = 1000000000; + int64_t time_ns = st->index_entries[i].timestamp * matroska->time_scale; + double nano_seconds_per_second = 1000000000.0; + int64_t prebuffered_ns = time_ns + prebuffer_ns; + double prebuffer_bytes = 0.0; + int64_t temp_prebuffer_ns = prebuffer_ns; + int64_t pre_bytes, pre_ns; + double pre_sec, prebuffer, bits_per_second; + CueDesc desc_beg = get_cue_desc(s, time_ns, cues_start); + + // Start with the first Cue. + CueDesc desc_end = desc_beg; + + // Figure out how much data we have downloaded for the prebuffer. This will + // be used later to adjust the bits per sample to try. + while (desc_end.start_time_ns != -1 && desc_end.end_time_ns < prebuffered_ns) { + // Prebuffered the entire Cue. + prebuffer_bytes += desc_end.end_offset - desc_end.start_offset; + temp_prebuffer_ns -= desc_end.end_time_ns - desc_end.start_time_ns; + desc_end = get_cue_desc(s, desc_end.end_time_ns, cues_start); + } + if (desc_end.start_time_ns == -1) { + // The prebuffer is larger than the duration. + return (matroska->duration * matroska->time_scale >= prebuffered_ns) ? -1 : 0; + } + + // The prebuffer ends in the last Cue. Estimate how much data was + // prebuffered. + pre_bytes = desc_end.end_offset - desc_end.start_offset; + pre_ns = desc_end.end_time_ns - desc_end.start_time_ns; + pre_sec = pre_ns / nano_seconds_per_second; + prebuffer_bytes += + pre_bytes * ((temp_prebuffer_ns / nano_seconds_per_second) / pre_sec); + + prebuffer = prebuffer_ns / nano_seconds_per_second; + + // Set this to 0.0 in case our prebuffer buffers the entire video. + bits_per_second = 0.0; + do { + int64_t desc_bytes = desc_end.end_offset - desc_beg.start_offset; + int64_t desc_ns = desc_end.end_time_ns - desc_beg.start_time_ns; + double desc_sec = desc_ns / nano_seconds_per_second; + double calc_bits_per_second = (desc_bytes * 8) / desc_sec; + + // Drop the bps by the percentage of bytes buffered. + double percent = (desc_bytes - prebuffer_bytes) / desc_bytes; + double mod_bits_per_second = calc_bits_per_second * percent; + + if (prebuffer < desc_sec) { + double search_sec = + (double)(matroska->duration * matroska->time_scale) / nano_seconds_per_second; + + // Add 1 so the bits per second should be a little bit greater than file + // datarate. + int64_t bps = (int64_t)(mod_bits_per_second) + 1; + const double min_buffer = 0.0; + double buffer = prebuffer; + double sec_to_download = 0.0; + + int rv = buffer_size_after_time_downloaded(prebuffered_ns, search_sec, bps, + min_buffer, &buffer, &sec_to_download, + s, cues_start); + if (rv < 0) { + return -1; + } else if (rv == 0) { + bits_per_second = (double)(bps); + break; + } + } + + desc_end = get_cue_desc(s, desc_end.end_time_ns, cues_start); + } while (desc_end.start_time_ns != -1); + if (bandwidth < bits_per_second) bandwidth = bits_per_second; + } + return (int64_t)bandwidth; +} + +static int webm_dash_manifest_cues(AVFormatContext *s) +{ + MatroskaDemuxContext *matroska = s->priv_data; + EbmlList *seekhead_list = &matroska->seekhead; + MatroskaSeekhead *seekhead = seekhead_list->elem; + char *buf; + int64_t cues_start = -1, cues_end = -1, before_pos, bandwidth; + int i; + + // determine cues start and end positions + for (i = 0; i < seekhead_list->nb_elem; i++) + if (seekhead[i].id == MATROSKA_ID_CUES) + break; + + if (i >= seekhead_list->nb_elem) return -1; + + before_pos = avio_tell(matroska->ctx->pb); + cues_start = seekhead[i].pos + matroska->segment_start; + if (avio_seek(matroska->ctx->pb, cues_start, SEEK_SET) == cues_start) { + uint64_t cues_length = 0, cues_id = 0; + ebml_read_num(matroska, matroska->ctx->pb, 4, &cues_id); + ebml_read_length(matroska, matroska->ctx->pb, &cues_length); + cues_end = cues_start + cues_length + 11; // 11 is the offset of Cues ID. + } + avio_seek(matroska->ctx->pb, before_pos, SEEK_SET); + if (cues_start == -1 || cues_end == -1) return -1; + + // parse the cues + matroska_parse_cues(matroska); + + // cues start + av_dict_set_int(&s->streams[0]->metadata, CUES_START, cues_start, 0); + + // cues end + av_dict_set_int(&s->streams[0]->metadata, CUES_END, cues_end, 0); + + // bandwidth + bandwidth = webm_dash_manifest_compute_bandwidth(s, cues_start); + if (bandwidth < 0) return -1; + av_dict_set_int(&s->streams[0]->metadata, BANDWIDTH, bandwidth, 0); + + // check if all clusters start with key frames + av_dict_set_int(&s->streams[0]->metadata, CLUSTER_KEYFRAME, webm_clusters_start_with_keyframe(s), 0); + + // store cue point timestamps as a comma separated list for checking subsegment alignment in + // the muxer. assumes that each timestamp cannot be more than 20 characters long. + buf = av_malloc(s->streams[0]->nb_index_entries * 20 * sizeof(char)); + if (!buf) return -1; + strcpy(buf, ""); + for (i = 0; i < s->streams[0]->nb_index_entries; i++) { + snprintf(buf, (i + 1) * 20 * sizeof(char), + "%s%" PRId64, buf, s->streams[0]->index_entries[i].timestamp); + if (i != s->streams[0]->nb_index_entries - 1) + strncat(buf, ",", sizeof(char)); + } + av_dict_set(&s->streams[0]->metadata, CUE_TIMESTAMPS, buf, 0); + av_free(buf); + + return 0; +} + +static int webm_dash_manifest_read_header(AVFormatContext *s) +{ + char *buf; + int ret = matroska_read_header(s); + MatroskaTrack *tracks; + MatroskaDemuxContext *matroska = s->priv_data; + if (ret) { + av_log(s, AV_LOG_ERROR, "Failed to read file headers\n"); + return -1; + } + + // initialization range + // 5 is the offset of Cluster ID. + av_dict_set_int(&s->streams[0]->metadata, INITIALIZATION_RANGE, avio_tell(s->pb) - 5, 0); + + // basename of the file + buf = strrchr(s->filename, '/'); + if (!buf) return -1; + av_dict_set(&s->streams[0]->metadata, FILENAME, ++buf, 0); + + // duration + buf = av_asprintf("%g", matroska->duration); + if (!buf) return AVERROR(ENOMEM); + av_dict_set(&s->streams[0]->metadata, DURATION, buf, 0); + av_free(buf); + + // track number + tracks = matroska->tracks.elem; + av_dict_set_int(&s->streams[0]->metadata, TRACK_NUMBER, tracks[0].num, 0); + + // parse the cues and populate Cue related fields + return webm_dash_manifest_cues(s); +} + +static int webm_dash_manifest_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + return AVERROR_EOF; +} + AVInputFormat ff_matroska_demuxer = { .name = "matroska,webm", .long_name = NULL_IF_CONFIG_SMALL("Matroska / WebM"), @@ -2618,3 +3434,12 @@ AVInputFormat ff_matroska_demuxer = { .read_seek = matroska_read_seek, .mime_type = "audio/webm,audio/x-matroska,video/webm,video/x-matroska" }; + +AVInputFormat ff_webm_dash_manifest_demuxer = { + .name = "webm_dash_manifest", + .long_name = NULL_IF_CONFIG_SMALL("WebM DASH Manifest"), + .priv_data_size = sizeof(MatroskaDemuxContext), + .read_header = webm_dash_manifest_read_header, + .read_packet = webm_dash_manifest_read_packet, + .read_close = matroska_read_close, +}; diff --git a/libavformat/matroskaenc.c b/libavformat/matroskaenc.c index 225f6a6..367eb0e 100644 --- a/libavformat/matroskaenc.c +++ b/libavformat/matroskaenc.c @@ -2,20 +2,20 @@ * 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 */ @@ -24,12 +24,14 @@ #include "avc.h" #include "hevc.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 "vorbiscomment.h" #include "wv.h" @@ -43,10 +45,12 @@ #include "libavutil/opt.h" #include "libavutil/random_seed.h" #include "libavutil/samplefmt.h" +#include "libavutil/sha.h" #include "libavutil/stereo3d.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 @@ -69,8 +73,11 @@ typedef struct mkv_seekhead { typedef struct { uint64_t pts; + int stream_idx; 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 { @@ -81,6 +88,7 @@ typedef struct { typedef struct { int write_dts; + int has_cue; int64_t ts_offset; } mkv_track; @@ -110,6 +118,10 @@ typedef struct MatroskaMuxContext { int cluster_size_limit; int64_t cues_pos; int64_t cluster_time_limit; + int is_dash; + int dash_track_number; + + uint32_t chapter_id_offset; int wrote_chapters; } MatroskaMuxContext; @@ -118,13 +130,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) { return (av_log2(id + 1) - 1) / 7 + 1; @@ -134,7 +149,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))); } /** @@ -144,10 +159,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); } /** @@ -172,18 +186,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) @@ -196,7 +210,20 @@ 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_sint(AVIOContext *pb, unsigned int elementid, int64_t val) +{ + int i, bytes = 1; + uint64_t tmp = 2*(val < 0 ? val^-1 : val); + + while (tmp>>=8) bytes++; + + put_ebml_id(pb, elementid); + put_ebml_num(pb, bytes, 0); + for (i = bytes - 1; i >= 0; i--) + avio_w8(pb, (uint8_t)(val >> i * 8)); } static void put_ebml_float(AVIOContext *pb, unsigned int elementid, double val) @@ -230,7 +257,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 @@ -240,8 +267,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, @@ -265,9 +291,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); } @@ -305,17 +329,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) + return AVERROR(ENOMEM); + seekhead->entries = entries; seekhead->entries[seekhead->num_entries].elementid = elementid; seekhead->entries[seekhead->num_entries++].segmentpos = filepos - seekhead->segment_offset; @@ -370,7 +393,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; @@ -386,28 +409,32 @@ 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, int tracknum, 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) + 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].stream_idx = stream; + cues->entries[cues->num_entries].tracknum = tracknum; + 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(AVFormatContext *s, mkv_cues *cues, mkv_track *tracks, int num_tracks) { + AVIOContext *pb = s->pb; ebml_master cues_element; int64_t currentpos; int i, j; @@ -419,16 +446,39 @@ static int64_t mkv_write_cues(AVIOContext *pb, mkv_cues *cues, int num_tracks) ebml_master cuepoint, track_positions; mkv_cuepoint *entry = &cues->entries[i]; uint64_t pts = entry->pts; + int ctp_nb = 0; - cuepoint = start_ebml_master(pb, MATROSKA_ID_POINTENTRY, MAX_CUEPOINT_SIZE(num_tracks)); + // Calculate the number of entries, so we know the element size + 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].stream_idx; + av_assert0(tracknum>=0 && tracknum<num_tracks); + if (tracks[tracknum].has_cue && s->streams[tracknum]->codec->codec_type != AVMEDIA_TYPE_SUBTITLE) + continue; + tracks[tracknum].has_cue = 1; + ctp_nb ++; + } + + cuepoint = start_ebml_master(pb, MATROSKA_ID_POINTENTRY, MAX_CUEPOINT_SIZE(ctp_nb)); put_ebml_uint(pb, MATROSKA_ID_CUETIME, pts); // 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].stream_idx; + av_assert0(tracknum>=0 && tracknum<num_tracks); + if (tracks[tracknum].has_cue && s->streams[tracknum]->codec->codec_type != AVMEDIA_TYPE_SUBTITLE) + 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; @@ -490,7 +540,7 @@ static int put_flac_codecpriv(AVFormatContext *s, if (write_comment) { const char *vendor = (s->flags & AVFMT_FLAG_BITEXACT) ? - "Libav" : LIBAVFORMAT_IDENT; + "Lavf" : LIBAVFORMAT_IDENT; AVDictionary *dict = NULL; uint8_t buf[32], *data, *p; int len; @@ -565,8 +615,11 @@ static int mkv_write_native_codecprivate(AVFormatContext *s, codec->extradata_size - 12); break; default: - if (codec->extradata_size) - avio_write(dyn_cp, codec->extradata, codec->extradata_size); + if (codec->codec_id == AV_CODEC_ID_PRORES && + ff_codec_get_id(ff_codec_movvideo_tags, codec->codec_tag) == AV_CODEC_ID_PRORES) { + avio_wl32(dyn_cp, codec->codec_tag); + } else if (codec->extradata_size && codec->codec_id != AV_CODEC_ID_TTA) + avio_write(dyn_cp, codec->extradata, codec->extradata_size); } return 0; @@ -591,30 +644,46 @@ static int mkv_write_codecprivate(AVFormatContext *s, AVIOContext *pb, if (!codec->codec_tag) codec->codec_tag = ff_codec_get_tag(ff_codec_movvideo_tags, codec->codec_id); - if (codec->extradata_size) + if (codec->extradata_size) { + if ( ff_codec_get_id(ff_codec_movvideo_tags, codec->codec_tag) == codec->codec_id + && ff_codec_get_id(ff_codec_movvideo_tags, AV_RL32(codec->extradata + 4)) != codec->codec_id + ) { + int i; + avio_wb32(dyn_cp, 0x5a + codec->extradata_size); + avio_wl32(dyn_cp, codec->codec_tag); + for(i = 0; i < 0x5a - 8; i++) + avio_w8(dyn_cp, 0); + } avio_write(dyn_cp, codec->extradata, codec->extradata_size); + } } else { + if (!ff_codec_get_tag(ff_codec_bmp_tags, codec->codec_id)) + av_log(s, AV_LOG_WARNING, "codec %s is not supported by this format\n", + avcodec_get_name(codec->codec_id)); + 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); + ff_put_bmp_header(dyn_cp, codec, ff_codec_bmp_tags, 0, 0); } } else if (codec->codec_type == AVMEDIA_TYPE_AUDIO) { 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; - ff_put_wav_header(dyn_cp, codec); + ff_put_wav_header(dyn_cp, codec, FF_PUT_WAV_HEADER_FORCE_WAVEFORMATEX); } codecpriv_size = avio_close_dyn_buf(dyn_cp, &codecpriv); @@ -625,16 +694,26 @@ static int mkv_write_codecprivate(AVFormatContext *s, AVIOContext *pb, return ret; } + static int mkv_write_stereo_mode(AVFormatContext *s, AVIOContext *pb, AVStream *st, int mode) { int i; + int ret = 0; AVDictionaryEntry *tag; MatroskaVideoStereoModeType format = MATROSKA_VIDEO_STEREOMODE_TYPE_NB; // convert metadata into proper side data and add it to the stream - if ((tag = av_dict_get(s->metadata, "stereo_mode", NULL, 0))) { + if ((tag = av_dict_get(st->metadata, "stereo_mode", NULL, 0)) || + (tag = av_dict_get( s->metadata, "stereo_mode", NULL, 0))) { int stereo_mode = atoi(tag->value); + + for (i=0; i<MATROSKA_VIDEO_STEREOMODE_TYPE_NB; i++) + if (!strcmp(tag->value, ff_matroska_video_stereo_mode[i])){ + stereo_mode = i; + break; + } + if (stereo_mode < MATROSKA_VIDEO_STEREOMODE_TYPE_NB && stereo_mode != 10 && stereo_mode != 12) { int ret = ff_mkv_stereo3d_conv(st, stereo_mode); @@ -683,24 +762,32 @@ static int mkv_write_stereo_mode(AVFormatContext *s, AVIOContext *pb, format++; break; } + ret = stereo->type; break; } } - if (mode == MODE_WEBM && - (format > MATROSKA_VIDEO_STEREOMODE_TYPE_TOP_BOTTOM && - format != MATROSKA_VIDEO_STEREOMODE_TYPE_RIGHT_LEFT)) + if (format == MATROSKA_VIDEO_STEREOMODE_TYPE_NB) + return ret; + + if ((mode == MODE_WEBM && + format > MATROSKA_VIDEO_STEREOMODE_TYPE_TOP_BOTTOM && + format != MATROSKA_VIDEO_STEREOMODE_TYPE_RIGHT_LEFT) + || format >= MATROSKA_VIDEO_STEREOMODE_TYPE_NB) { + av_log(s, AV_LOG_ERROR, + "The specified stereo mode is not valid.\n"); format = MATROSKA_VIDEO_STEREOMODE_TYPE_NB; + return AVERROR(EINVAL); + } - if (format < MATROSKA_VIDEO_STEREOMODE_TYPE_NB) - put_ebml_uint(pb, MATROSKA_ID_VIDEOSTEREOMODE, format); + put_ebml_uint(pb, MATROSKA_ID_VIDEOSTEREOMODE, format); - return 0; + return ret; } static int mkv_write_track(AVFormatContext *s, MatroskaMuxContext *mkv, - int i, AVIOContext *pb) + int i, AVIOContext *pb, int default_stream_exists) { AVStream *st = s->streams[i]; AVCodecContext *codec = st->codec; @@ -710,6 +797,8 @@ static int mkv_write_track(AVFormatContext *s, MatroskaMuxContext *mkv, 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; int j, ret; AVDictionaryEntry *tag; @@ -721,61 +810,98 @@ static int mkv_write_track(AVFormatContext *s, MatroskaMuxContext *mkv, return 0; } - if (!bit_depth) + if (!bit_depth && codec->codec_id != AV_CODEC_ID_ADPCM_G726) 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); track = start_ebml_master(pb, MATROSKA_ID_TRACKENTRY, 0); - put_ebml_uint (pb, MATROSKA_ID_TRACKNUMBER , i + 1); - put_ebml_uint (pb, MATROSKA_ID_TRACKUID , i + 1); + put_ebml_uint (pb, MATROSKA_ID_TRACKNUMBER, + mkv->is_dash ? mkv->dash_track_number : i + 1); + put_ebml_uint (pb, MATROSKA_ID_TRACKUID, + mkv->is_dash ? mkv->dash_track_number : i + 1); put_ebml_uint (pb, MATROSKA_ID_TRACKFLAGLACING , 0); // no lacing (yet) 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 ? 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)); - if (codec->codec_type == AVMEDIA_TYPE_AUDIO && codec->delay) { - mkv->tracks[i].ts_offset = av_rescale_q(codec->delay, - (AVRational){ 1, codec->sample_rate }, - st->time_base); + 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_type == AVMEDIA_TYPE_AUDIO && codec->delay && codec->codec_id == AV_CODEC_ID_OPUS) { +// mkv->tracks[i].ts_offset = av_rescale_q(codec->delay, +// (AVRational){ 1, codec->sample_rate }, +// st->time_base); put_ebml_uint(pb, MATROSKA_ID_CODECDELAY, av_rescale_q(codec->delay, (AVRational){ 1, codec->sample_rate }, (AVRational){ 1, 1000000000 })); } - - // 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) { + 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_VP9 || codec->codec_id == AV_CODEC_ID_OPUS || - codec->codec_id == AV_CODEC_ID_VORBIS)) { + codec->codec_id == AV_CODEC_ID_VORBIS || + codec->codec_id == AV_CODEC_ID_WEBVTT)) { av_log(s, AV_LOG_ERROR, - "Only VP8 or VP9 video and Vorbis or Opus audio are supported for WebM.\n"); + "Only VP8 or VP9 video and Vorbis or Opus 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); - if (st->avg_frame_rate.num > 0 && st->avg_frame_rate.den > 0) + + if( st->avg_frame_rate.num > 0 && st->avg_frame_rate.den > 0 + && 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) && @@ -804,11 +930,39 @@ static int mkv_write_track(AVFormatContext *s, MatroskaMuxContext *mkv, if (ret < 0) return ret; + switch (ret) { + case AV_STEREO3D_SIDEBYSIDE: + case AV_STEREO3D_COLUMNS: + display_width_div = 2; + break; + case AV_STEREO3D_TOPBOTTOM: + case AV_STEREO3D_LINES: + display_height_div = 2; + break; + } + + if (((tag = av_dict_get(st->metadata, "alpha_mode", NULL, 0)) && atoi(tag->value)) || + ((tag = av_dict_get( s->metadata, "alpha_mode", NULL, 0)) && atoi(tag->value)) || + (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; @@ -831,19 +985,26 @@ static int mkv_write_track(AVFormatContext *s, MatroskaMuxContext *mkv, 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); @@ -855,7 +1016,7 @@ static int mkv_write_tracks(AVFormatContext *s) MatroskaMuxContext *mkv = s->priv_data; AVIOContext *pb = s->pb; ebml_master tracks; - int i, ret; + int i, ret, default_stream_exists = 0; ret = mkv_add_seekhead_entry(mkv->main_seekhead, MATROSKA_ID_TRACKS, avio_tell(pb)); if (ret < 0) @@ -863,7 +1024,11 @@ static int mkv_write_tracks(AVFormatContext *s) tracks = start_ebml_master(pb, MATROSKA_ID_TRACKS, 0); for (i = 0; i < s->nb_streams; i++) { - ret = mkv_write_track(s, mkv, i, pb); + AVStream *st = s->streams[i]; + default_stream_exists |= st->disposition & AV_DISPOSITION_DEFAULT; + } + for (i = 0; i < s->nb_streams; i++) { + ret = mkv_write_track(s, mkv, i, pb, default_stream_exists); if (ret < 0) return ret; } @@ -895,7 +1060,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, @@ -970,6 +1135,7 @@ static int mkv_write_tag(AVFormatContext *s, AVDictionary *m, unsigned int eleme while ((t = av_dict_get(m, "", t, AV_DICT_IGNORE_SUFFIX))) if (av_strcasecmp(t->key, "title") && + av_strcasecmp(t->key, "stereo_mode") && av_strcasecmp(t->key, "encoding_tool")) mkv_write_simpletag(s->pb, t); @@ -977,14 +1143,26 @@ static int mkv_write_tag(AVFormatContext *s, AVDictionary *m, unsigned int eleme 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; } @@ -992,7 +1170,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); @@ -1002,10 +1180,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; } @@ -1037,6 +1215,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; @@ -1066,9 +1245,25 @@ static int mkv_write_attachments(AVFormatContext *s) return AVERROR(EINVAL); } + if (s->flags & AVFMT_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); @@ -1082,14 +1277,40 @@ static int mkv_write_header(AVFormatContext *s) AVIOContext *pb = s->pb; ebml_master ebml_header, segment_info; AVDictionaryEntry *tag; - int ret, i; + int ret, i, version = 2; if (!strcmp(s->oformat->name, "webm")) mkv->mode = MODE_WEBM; else mkv->mode = MODE_MATROSKAv2; - mkv->tracks = av_mallocz(s->nb_streams * sizeof(*mkv->tracks)); + if (s->avoid_negative_ts < 0) + s->avoid_negative_ts = 1; + + if (mkv->mode != MODE_WEBM || + av_dict_get(s->metadata, "stereo_mode", NULL, 0) || + av_dict_get(s->metadata, "alpha_mode", NULL, 0)) + version = 4; + + 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; + } + if (s->streams[i]->codec->codec_id == AV_CODEC_ID_OPUS || + av_dict_get(s->streams[i]->metadata, "stereo_mode", NULL, 0) || + av_dict_get(s->streams[i]->metadata, "alpha_mode", NULL, 0)) + version = 4; + } + + mkv->tracks = av_mallocz_array(s->nb_streams, sizeof(*mkv->tracks)); if (!mkv->tracks) return AVERROR(ENOMEM); @@ -1099,7 +1320,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 , version); put_ebml_uint (pb, EBML_ID_DOCTYPEREADVERSION , 2); end_ebml_master(pb, ebml_header); @@ -1137,6 +1358,18 @@ static int mkv_write_header(AVFormatContext *s) else 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 @@ -1149,6 +1382,9 @@ static int mkv_write_header(AVFormatContext *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) @@ -1177,6 +1413,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); @@ -1216,30 +1453,34 @@ 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; } -static int mkv_write_ass_blocks(AVFormatContext *s, AVIOContext *pb, - AVPacket *pkt) +#if FF_API_ASS_SSA +/* Writes the contents of pkt to a block, using the data starting at *datap. + * If pkt corresponds to more than one block, this writes the contents of the first block + * (starting from *datap) and updates *datap so it points to the beginning of the data + * corresponding to the next block. + */ +static int mkv_write_ass_block(AVFormatContext *s, AVIOContext *pb, + AVPacket *pkt, uint8_t **datap) { MatroskaMuxContext *mkv = s->priv_data; - int i, layer = 0, max_duration = 0, size, line_size, data_size = pkt->size; - uint8_t *start, *end, *data = pkt->data; + int i, layer = 0, size, line_size, data_size = pkt->size - (*datap - pkt->data); + uint8_t *start, *end, *data = *datap; ebml_master blockgroup; char buffer[2048]; - while (data_size) { int duration = ass_get_duration(data); - max_duration = FFMAX(duration, max_duration); end = memchr(data, '\n', data_size); size = line_size = end ? end - data + 1 : data_size; size -= end ? (end[-1] == '\r') + 1 : 0; start = data; for (i = 0; i < 3; i++, start++) if (!(start = memchr(start, ',', size - (start - data)))) - return max_duration; + return duration; size -= start - data; sscanf(data, "Dialogue: %d,", &layer); i = snprintf(buffer, sizeof(buffer), "%" PRId64 ",%d,", @@ -1263,12 +1504,11 @@ static int mkv_write_ass_blocks(AVFormatContext *s, AVIOContext *pb, put_ebml_uint(pb, MATROSKA_ID_BLOCKDURATION, duration); end_ebml_master(pb, blockgroup); - data += line_size; - data_size -= line_size; - } + *datap += line_size; - return max_duration; + return duration; } +#endif static int mkv_strip_wavpack(const uint8_t *src, uint8_t **pdst, int *size) { @@ -1328,9 +1568,12 @@ 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; + int64_t 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", @@ -1358,15 +1601,57 @@ 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); // this assumes stream_index is less than 126 - avio_w8(pb, 0x80 | (pkt->stream_index + 1)); + avio_w8(pb, 0x80 | (mkv->is_dash ? mkv->dash_track_number : (pkt->stream_index + 1))); avio_wb16(pb, ts - mkv->cluster_pts); avio_w8(pb, flags); avio_write(pb, data + offset, size); if (data != pkt->data) av_free(data); + + if (discard_padding) { + put_ebml_sint(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) @@ -1388,7 +1673,7 @@ static int srt_get_duration(uint8_t **buf) duration = e_hsec - s_hsec; } - *buf += strcspn(*buf, "\n") + 1; + *buf += ff_subtitles_next_line(*buf); } return duration; } @@ -1410,6 +1695,44 @@ static int mkv_write_srt_blocks(AVFormatContext *s, AVIOContext *pb, 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; @@ -1425,7 +1748,29 @@ static void mkv_flush_dynbuf(AVFormatContext *s) mkv->dyn_bc = NULL; } -static int mkv_write_packet_internal(AVFormatContext *s, AVPacket *pkt) +static void mkv_start_new_cluster(AVFormatContext *s, AVPacket *pkt) +{ + MatroskaMuxContext *mkv = s->priv_data; + AVIOContext *pb; + + if (s->pb->seekable) { + pb = s->pb; + } else { + pb = mkv->dyn_bc; + } + + av_log(s, AV_LOG_DEBUG, + "Starting new cluster at offset %" PRIu64 " bytes, " + "pts %" PRIu64 "dts %" PRIu64 "\n", + avio_tell(pb), pkt->pts, pkt->dts); + end_ebml_master(pb, mkv->cluster); + mkv->cluster_pos = -1; + if (mkv->dyn_bc) + mkv_flush_dynbuf(s); + avio_flush(s->pb); +} + +static int mkv_write_packet_internal(AVFormatContext *s, AVPacket *pkt, int add_cue) { MatroskaMuxContext *mkv = s->priv_data; AVIOContext *pb = s->pb; @@ -1434,6 +1779,9 @@ 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; + uint8_t *data_offset = pkt->data; + int dash_tracknum = mkv->is_dash ? mkv->dash_track_number : pkt->stream_index + 1; if (ts == AV_NOPTS_VALUE) { av_log(s, AV_LOG_ERROR, "Can't write packet with unknown timestamp\n"); @@ -1441,39 +1789,69 @@ static int mkv_write_packet_internal(AVFormatContext *s, AVPacket *pkt) } ts += mkv->tracks[pkt->stream_index].ts_offset; + if (mkv->cluster_pos != -1) { + int64_t cluster_time = ts - mkv->cluster_pts + mkv->tracks[pkt->stream_index].ts_offset; + if ((int16_t)cluster_time != cluster_time) { + av_log(s, AV_LOG_WARNING, "Starting new cluster due to timestamp\n"); + mkv_start_new_cluster(s, 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 (codec->codec_type == AVMEDIA_TYPE_VIDEO && keyframe || add_cue) { + ret = mkv_add_cuepoint(mkv->cues, pkt->stream_index, dash_tracknum, ts, mkv->cluster_pos, relative_packet_pos, -1); + if (ret < 0) return ret; + } +#if FF_API_ASS_SSA } else if (codec->codec_id == AV_CODEC_ID_SSA) { - duration = mkv_write_ass_blocks(s, pb, pkt); - } else if (codec->codec_id == AV_CODEC_ID_SRT) { + while (data_offset < pkt->data + pkt->size) { + duration = mkv_write_ass_block(s, pb, pkt, &data_offset); + ret = mkv_add_cuepoint(mkv->cues, pkt->stream_index, dash_tracknum, ts, mkv->cluster_pos, relative_packet_pos, duration); + if (ret < 0) return ret; + relative_packet_pos = avio_tell(s->pb) - mkv->cluster.pos; + } +#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 (ret < 0) - return ret; + ret = mkv_add_cuepoint(mkv->cues, pkt->stream_index, dash_tracknum, ts, + mkv->cluster_pos, relative_packet_pos, duration); + if (ret < 0) + return ret; } mkv->duration = FFMAX(mkv->duration, ts + duration); @@ -1487,8 +1865,8 @@ static int mkv_write_packet(AVFormatContext *s, AVPacket *pkt) int keyframe = !!(pkt->flags & AV_PKT_FLAG_KEY); int cluster_size; int64_t cluster_time; - AVIOContext *pb; int ret; + int start_new_cluster; if (mkv->tracks[pkt->stream_index].write_dts) cluster_time = pkt->dts - mkv->cluster_pts; @@ -1499,32 +1877,39 @@ static int mkv_write_packet(AVFormatContext *s, AVPacket *pkt) // start a new cluster every 5 MB or 5 sec, or 32k / 1 sec for streaming or // after 4k and on a keyframe if (s->pb->seekable) { - pb = s->pb; - cluster_size = avio_tell(pb) - mkv->cluster_pos; + cluster_size = avio_tell(s->pb) - mkv->cluster_pos; } else { - pb = mkv->dyn_bc; - cluster_size = avio_tell(pb); + cluster_size = avio_tell(mkv->dyn_bc); } - if (mkv->cluster_pos && - (cluster_size > mkv->cluster_size_limit || - cluster_time > mkv->cluster_time_limit || - (codec_type == AVMEDIA_TYPE_VIDEO && keyframe && - cluster_size > 4 * 1024))) { - av_log(s, AV_LOG_DEBUG, - "Starting new cluster at offset %" PRIu64 " bytes, " - "pts %" PRIu64 "dts %" PRIu64 "\n", - avio_tell(pb), pkt->pts, pkt->dts); - end_ebml_master(pb, mkv->cluster); - mkv->cluster_pos = 0; - if (mkv->dyn_bc) - mkv_flush_dynbuf(s); - avio_flush(s->pb); + if (mkv->is_dash && codec_type == AVMEDIA_TYPE_VIDEO) { + // WebM DASH specification states that the first block of every cluster + // has to be a key frame. So for DASH video, we only create a cluster + // on seeing key frames. + start_new_cluster = keyframe; + } else if (mkv->is_dash && codec_type == AVMEDIA_TYPE_AUDIO && + cluster_time > mkv->cluster_time_limit) { + // For DASH audio, we create a Cluster based on cluster_time_limit + start_new_cluster = 1; + } else if (!mkv->is_dash && + (cluster_size > mkv->cluster_size_limit || + cluster_time > mkv->cluster_time_limit || + (codec_type == AVMEDIA_TYPE_VIDEO && keyframe && + cluster_size > 4 * 1024))) { + start_new_cluster = 1; + } else { + start_new_cluster = 0; + } + + if (mkv->cluster_pos != -1 && start_new_cluster) { + mkv_start_new_cluster(s, pkt); } // check if we have an audio packet cached if (mkv->cur_audio_pkt.size > 0) { - ret = mkv_write_packet_internal(s, &mkv->cur_audio_pkt); + // for DASH audio, a CuePoint has to be added when there is a new cluster. + ret = mkv_write_packet_internal(s, &mkv->cur_audio_pkt, + mkv->is_dash ? start_new_cluster : 0); av_free_packet(&mkv->cur_audio_pkt); if (ret < 0) { av_log(s, AV_LOG_ERROR, @@ -1542,8 +1927,11 @@ 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); + ret = mkv_write_packet_internal(s, pkt, 0); return ret; } @@ -1556,12 +1944,12 @@ 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); @@ -1580,7 +1968,7 @@ static int mkv_write_trailer(AVFormatContext *s) // check if we have an audio packet cached if (mkv->cur_audio_pkt.size > 0) { - ret = mkv_write_packet_internal(s, &mkv->cur_audio_pkt); + ret = mkv_write_packet_internal(s, &mkv->cur_audio_pkt, 0); av_free_packet(&mkv->cur_audio_pkt); if (ret < 0) { av_log(s, AV_LOG_ERROR, @@ -1592,7 +1980,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); } @@ -1610,7 +1998,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(s, 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, @@ -1626,7 +2014,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(s, mkv->cues, mkv->tracks, s->nb_streams); } ret = mkv_add_seekhead_entry(mkv->main_seekhead, MATROSKA_ID_CUES, @@ -1647,7 +2035,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); @@ -1671,12 +2059,39 @@ 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[] = { { "reserve_index_space", "Reserve a given amount of space (in bytes) at the beginning of the file for the index (cues).", OFFSET(reserve_cues_space), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, FLAGS }, { "cluster_size_limit", "Store at most the provided amount of bytes in a cluster. ", OFFSET(cluster_size_limit), AV_OPT_TYPE_INT , { .i64 = -1 }, -1, INT_MAX, FLAGS }, { "cluster_time_limit", "Store at most the provided number of milliseconds in a cluster.", OFFSET(cluster_time_limit), AV_OPT_TYPE_INT64, { .i64 = -1 }, -1, INT64_MAX, FLAGS }, + { "dash", "Create a WebM file conforming to WebM DASH specification", OFFSET(is_dash), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, FLAGS }, + { "dash_track_number", "Track number for the DASH stream", OFFSET(dash_track_number), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 127, FLAGS }, { NULL }, }; @@ -1704,9 +2119,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, }; @@ -1728,6 +2148,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, @@ -1746,7 +2167,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), @@ -1758,7 +2179,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 9249704..8e87f09 100644 --- a/libavformat/md5enc.c +++ b/libavformat/md5enc.c @@ -2,38 +2,46 @@ * 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; + int format_version; }; 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 +52,55 @@ 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 }, + { "format_version", "file format version", OFFSET(format_version), AV_OPT_TYPE_INT, {.i64 = 1}, 1, 1, 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 +108,7 @@ AVOutputFormat ff_md5_muxer = { .write_packet = write_packet, .write_trailer = write_trailer, .flags = AVFMT_NOTIMESTAMPS, + .priv_class = &md5enc_class, }; #endif @@ -91,18 +116,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: %d\n", c->format_version); + 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 +143,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 +165,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 12ddde3..6af0a6e 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.c b/libavformat/metadata.c index 77fb298..b9b6de7 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 */ @@ -33,7 +33,7 @@ void ff_metadata_conv(AVDictionary **pm, const AVMetadataConv *d_conv, AVDictionary *dst = NULL; const char *key; - if (d_conv == s_conv) + if (d_conv == s_conv || !pm) return; while ((mtag = av_dict_get(*pm, "", mtag, AV_DICT_IGNORE_SUFFIX))) { diff --git a/libavformat/metadata.h b/libavformat/metadata.h index eee3ee4..6586094 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 0000000..8cbc952 --- /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 (avio_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 0000000..ce3433c --- /dev/null +++ b/libavformat/microdvddec.c @@ -0,0 +1,203 @@ +/* + * 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" +#include "libavutil/opt.h" + +#define MAX_LINESIZE 2048 + + +typedef struct { + const AVClass *class; + FFDemuxSubtitlesQueue q; + AVRational frame_rate; +} 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 const char *bom = "\xEF\xBB\xBF"; + +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_buf[MAX_LINESIZE]; + int has_real_fps = 0; + + if (!st) + return AVERROR(ENOMEM); + + while (!avio_feof(s->pb)) { + char *p; + AVPacket *sub; + int64_t pos = avio_tell(s->pb); + int len = ff_get_line(s->pb, line_buf, sizeof(line_buf)); + char *line = line_buf; + + if (!strncmp(line, bom, 3)) + line += 3; + p = 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); + has_real_fps = 1; + } + 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); + if (has_real_fps) { + /* export the FPS info only if set in the file */ + microdvd->frame_rate = pts_info; + } else if (microdvd->frame_rate.num) { + /* fallback on user specified frame rate */ + pts_info = microdvd->frame_rate; + } + 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; +} + + +#define OFFSET(x) offsetof(MicroDVDContext, x) +#define SD AV_OPT_FLAG_SUBTITLE_PARAM|AV_OPT_FLAG_DECODING_PARAM +static const AVOption microdvd_options[] = { + { "subfps", "set the movie frame rate fallback", OFFSET(frame_rate), AV_OPT_TYPE_RATIONAL, {.dbl=0}, 0, INT_MAX, SD }, + { NULL } +}; + +static const AVClass microdvd_class = { + .class_name = "microdvddec", + .item_name = av_default_item_name, + .option = microdvd_options, + .version = LIBAVUTIL_VERSION_INT, +}; + +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, + .priv_class = µdvd_class, +}; diff --git a/libavformat/microdvdenc.c b/libavformat/microdvdenc.c new file mode 100644 index 0000000..4d84384 --- /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 0000000..7ba6691 --- /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/mlvdec.c b/libavformat/mlvdec.c new file mode 100644 index 0000000..1855ea4 --- /dev/null +++ b/libavformat/mlvdec.c @@ -0,0 +1,463 @@ +/* + * Magic Lantern Video (MLV) demuxer + * Copyright (c) 2014 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 + * Magic Lantern Video (MLV) demuxer + */ + +#include "libavutil/eval.h" +#include "libavutil/intreadwrite.h" +#include "libavutil/rational.h" +#include "avformat.h" +#include "internal.h" +#include "riff.h" + +#define MLV_VERSION "v2.0" + +#define MLV_VIDEO_CLASS_RAW 1 +#define MLV_VIDEO_CLASS_YUV 2 +#define MLV_VIDEO_CLASS_JPEG 3 +#define MLV_VIDEO_CLASS_H264 4 + +#define MLV_AUDIO_CLASS_WAV 1 + +#define MLV_CLASS_FLAG_DELTA 0x40 +#define MLV_CLASS_FLAG_LZMA 0x80 + +typedef struct { + AVIOContext *pb[101]; + int class[2]; + int stream_index; + uint64_t pts; +} MlvContext; + +static int probe(AVProbeData *p) +{ + if (AV_RL32(p->buf) == MKTAG('M','L','V','I') && + AV_RL32(p->buf + 4) >= 52 && + !memcmp(p->buf + 8, MLV_VERSION, 5)) + return AVPROBE_SCORE_MAX; + return 0; +} + +static int check_file_header(AVIOContext *pb, uint64_t guid) +{ + unsigned int size; + uint8_t version[8]; + + avio_skip(pb, 4); + size = avio_rl32(pb); + if (size < 52) + return AVERROR_INVALIDDATA; + avio_read(pb, version, 8); + if (memcmp(version, MLV_VERSION, 5) || avio_rl64(pb) != guid) + return AVERROR_INVALIDDATA; + avio_skip(pb, size - 24); + return 0; +} + +static void read_string(AVFormatContext *avctx, AVIOContext *pb, const char *tag, int size) +{ + char * value = av_malloc(size + 1); + if (!value) { + avio_skip(pb, size); + return; + } + + avio_read(pb, value, size); + if (!value[0]) { + av_free(value); + return; + } + + value[size] = 0; + av_dict_set(&avctx->metadata, tag, value, AV_DICT_DONT_STRDUP_VAL); +} + +static void read_uint8(AVFormatContext *avctx, AVIOContext *pb, const char *tag, const char *fmt) +{ + av_dict_set_int(&avctx->metadata, tag, avio_r8(pb), 0); +} + +static void read_uint16(AVFormatContext *avctx, AVIOContext *pb, const char *tag, const char *fmt) +{ + av_dict_set_int(&avctx->metadata, tag, avio_rl16(pb), 0); +} + +static void read_uint32(AVFormatContext *avctx, AVIOContext *pb, const char *tag, const char *fmt) +{ + av_dict_set_int(&avctx->metadata, tag, avio_rl32(pb), 0); +} + +static void read_uint64(AVFormatContext *avctx, AVIOContext *pb, const char *tag, const char *fmt) +{ + av_dict_set_int(&avctx->metadata, tag, avio_rl64(pb), 0); +} + +static int scan_file(AVFormatContext *avctx, AVStream *vst, AVStream *ast, int file) +{ + MlvContext *mlv = avctx->priv_data; + AVIOContext *pb = mlv->pb[file]; + int ret; + while (!avio_feof(pb)) { + int type; + unsigned int size; + type = avio_rl32(pb); + size = avio_rl32(pb); + avio_skip(pb, 8); //timestamp + if (size < 16) + break; + size -= 16; + if (vst && type == MKTAG('R','A','W','I') && size >= 164) { + vst->codec->width = avio_rl16(pb); + vst->codec->height = avio_rl16(pb); + if (avio_rl32(pb) != 1) + avpriv_request_sample(avctx, "raw api version"); + avio_skip(pb, 20); // pointer, width, height, pitch, frame_size + vst->codec->bits_per_coded_sample = avio_rl32(pb); + avio_skip(pb, 8 + 16 + 24); // black_level, white_level, xywh, active_area, exposure_bias + if (avio_rl32(pb) != 0x2010100) /* RGGB */ + avpriv_request_sample(avctx, "cfa_pattern"); + avio_skip(pb, 80); // calibration_illuminant1, color_matrix1, dynamic_range + vst->codec->pix_fmt = AV_PIX_FMT_BAYER_RGGB16LE; + vst->codec->codec_tag = MKTAG('B', 'I', 'T', 16); + size -= 164; + } else if (ast && type == MKTAG('W', 'A', 'V', 'I') && size >= 16) { + ret = ff_get_wav_header(pb, ast->codec, 16); + if (ret < 0) + return ret; + size -= 16; + } else if (type == MKTAG('I','N','F','O')) { + if (size > 0) + read_string(avctx, pb, "info", size); + continue; + } else if (type == MKTAG('I','D','N','T') && size >= 36) { + read_string(avctx, pb, "cameraName", 32); + read_uint32(avctx, pb, "cameraModel", "0x%"PRIx32); + size -= 36; + if (size >= 32) { + read_string(avctx, pb, "cameraSerial", 32); + size -= 32; + } + } else if (type == MKTAG('L','E','N','S') && size >= 48) { + read_uint16(avctx, pb, "focalLength", "%i"); + read_uint16(avctx, pb, "focalDist", "%i"); + read_uint16(avctx, pb, "aperture", "%i"); + read_uint8(avctx, pb, "stabilizerMode", "%i"); + read_uint8(avctx, pb, "autofocusMode", "%i"); + read_uint32(avctx, pb, "flags", "0x%"PRIx32); + read_uint32(avctx, pb, "lensID", "%"PRIi32); + read_string(avctx, pb, "lensName", 32); + size -= 48; + if (size >= 32) { + read_string(avctx, pb, "lensSerial", 32); + size -= 32; + } + } else if (vst && type == MKTAG('V', 'I', 'D', 'F') && size >= 4) { + uint64_t pts = avio_rl32(pb); + ff_add_index_entry(&vst->index_entries, &vst->nb_index_entries, &vst->index_entries_allocated_size, + avio_tell(pb) - 20, pts, file, 0, AVINDEX_KEYFRAME); + size -= 4; + } else if (ast && type == MKTAG('A', 'U', 'D', 'F') && size >= 4) { + uint64_t pts = avio_rl32(pb); + ff_add_index_entry(&ast->index_entries, &ast->nb_index_entries, &ast->index_entries_allocated_size, + avio_tell(pb) - 20, pts, file, 0, AVINDEX_KEYFRAME); + size -= 4; + } else if (vst && type == MKTAG('W','B','A','L') && size >= 28) { + read_uint32(avctx, pb, "wb_mode", "%"PRIi32); + read_uint32(avctx, pb, "kelvin", "%"PRIi32); + read_uint32(avctx, pb, "wbgain_r", "%"PRIi32); + read_uint32(avctx, pb, "wbgain_g", "%"PRIi32); + read_uint32(avctx, pb, "wbgain_b", "%"PRIi32); + read_uint32(avctx, pb, "wbs_gm", "%"PRIi32); + read_uint32(avctx, pb, "wbs_ba", "%"PRIi32); + size -= 28; + } else if (type == MKTAG('R','T','C','I') && size >= 20) { + char str[32]; + struct tm time = { 0 }; + time.tm_sec = avio_rl16(pb); + time.tm_min = avio_rl16(pb); + time.tm_hour = avio_rl16(pb); + time.tm_mday = avio_rl16(pb); + time.tm_mon = avio_rl16(pb); + time.tm_year = avio_rl16(pb); + time.tm_wday = avio_rl16(pb); + time.tm_yday = avio_rl16(pb); + time.tm_isdst = avio_rl16(pb); + avio_skip(pb, 2); + strftime(str, sizeof(str), "%Y-%m-%d %H:%M:%S", &time); + av_dict_set(&avctx->metadata, "time", str, 0); + size -= 20; + } else if (type == MKTAG('E','X','P','O') && size >= 16) { + av_dict_set(&avctx->metadata, "isoMode", avio_rl32(pb) ? "auto" : "manual", 0); + read_uint32(avctx, pb, "isoValue", "%"PRIi32); + read_uint32(avctx, pb, "isoAnalog", "%"PRIi32); + read_uint32(avctx, pb, "digitalGain", "%"PRIi32); + size -= 16; + if (size >= 8) { + read_uint64(avctx, pb, "shutterValue", "%"PRIi64); + size -= 8; + } + } else if (type == MKTAG('S','T','Y','L') && size >= 36) { + read_uint32(avctx, pb, "picStyleId", "%"PRIi32); + read_uint32(avctx, pb, "contrast", "%"PRIi32); + read_uint32(avctx, pb, "sharpness", "%"PRIi32); + read_uint32(avctx, pb, "saturation", "%"PRIi32); + read_uint32(avctx, pb, "colortone", "%"PRIi32); + read_string(avctx, pb, "picStyleName", 16); + size -= 36; + } else if (type == MKTAG('M','A','R','K')) { + } else if (type == MKTAG('N','U','L','L')) { + } else if (type == MKTAG('M','L','V','I')) { /* occurs when MLV and Mnn files are concatenated */ + } else { + av_log(avctx, AV_LOG_INFO, "unsupported tag %c%c%c%c, size %u\n", type&0xFF, (type>>8)&0xFF, (type>>16)&0xFF, (type>>24)&0xFF, size); + } + avio_skip(pb, size); + } + return 0; +} + +static int read_header(AVFormatContext *avctx) +{ + MlvContext *mlv = avctx->priv_data; + AVIOContext *pb = avctx->pb; + AVStream *vst = NULL, *ast = NULL; + int size, ret; + uint64_t guid; + char guidstr[32]; + + avio_skip(pb, 4); + size = avio_rl32(pb); + if (size < 52) + return AVERROR_INVALIDDATA; + + avio_skip(pb, 8); + + guid = avio_rl64(pb); + snprintf(guidstr, sizeof(guidstr), "0x%"PRIx64, guid); + av_dict_set(&avctx->metadata, "guid", guidstr, 0); + + avio_skip(pb, 8); //fileNum, fileCount, fileFlags + + mlv->class[0] = avio_rl16(pb); + if (mlv->class[0]) { + vst = avformat_new_stream(avctx, NULL); + if (!vst) + return AVERROR(ENOMEM); + vst->id = 0; + if ((mlv->class[0] & (MLV_CLASS_FLAG_DELTA|MLV_CLASS_FLAG_LZMA))) + avpriv_request_sample(avctx, "compression"); + vst->codec->codec_type = AVMEDIA_TYPE_VIDEO; + switch (mlv->class[0] & ~(MLV_CLASS_FLAG_DELTA|MLV_CLASS_FLAG_LZMA)) { + case MLV_VIDEO_CLASS_RAW: + vst->codec->codec_id = AV_CODEC_ID_RAWVIDEO; + break; + case MLV_VIDEO_CLASS_YUV: + vst->codec->pix_fmt = AV_PIX_FMT_YUV420P; + vst->codec->codec_id = AV_CODEC_ID_RAWVIDEO; + vst->codec->codec_tag = 0; + break; + case MLV_VIDEO_CLASS_JPEG: + vst->codec->codec_id = AV_CODEC_ID_MJPEG; + vst->codec->codec_tag = 0; + break; + case MLV_VIDEO_CLASS_H264: + vst->codec->codec_id = AV_CODEC_ID_H264; + vst->codec->codec_tag = 0; + break; + default: + avpriv_request_sample(avctx, "unknown video class"); + } + } + + mlv->class[1] = avio_rl16(pb); + if (mlv->class[1]) { + ast = avformat_new_stream(avctx, NULL); + if (!ast) + return AVERROR(ENOMEM); + ast->id = 1; + if ((mlv->class[1] & MLV_CLASS_FLAG_LZMA)) + avpriv_request_sample(avctx, "compression"); + if ((mlv->class[1] & ~MLV_CLASS_FLAG_LZMA) != MLV_AUDIO_CLASS_WAV) + avpriv_request_sample(avctx, "unknown audio class"); + + ast->codec->codec_type = AVMEDIA_TYPE_AUDIO; + avpriv_set_pts_info(ast, 33, 1, ast->codec->sample_rate); + } + + if (vst) + vst->nb_frames = avio_rl32(pb); + else + avio_skip(pb, 4); + + if (ast) + ast->nb_frames = avio_rl32(pb); + else + avio_skip(pb, 4); + + if (vst) { + AVRational framerate; + framerate.num = avio_rl32(pb); + framerate.den = avio_rl32(pb); + avpriv_set_pts_info(vst, 64, framerate.den, framerate.num); + } else + avio_skip(pb, 8); + + avio_skip(pb, size - 52); + + /* scan primary file */ + mlv->pb[100] = avctx->pb; + ret = scan_file(avctx, vst, ast, 100); + if (ret < 0) + return ret; + + /* scan secondary files */ + if (strlen(avctx->filename) > 2) { + int i; + char *filename = av_strdup(avctx->filename); + if (!filename) + return AVERROR(ENOMEM); + for (i = 0; i < 100; i++) { + snprintf(filename + strlen(filename) - 2, 3, "%02d", i); + if (avio_open2(&mlv->pb[i], filename, AVIO_FLAG_READ, &avctx->interrupt_callback, NULL) < 0) + break; + if (check_file_header(mlv->pb[i], guid) < 0) { + av_log(avctx, AV_LOG_WARNING, "ignoring %s; bad format or guid mismatch\n", filename); + avio_close(mlv->pb[i]); + mlv->pb[i] = NULL; + continue; + } + av_log(avctx, AV_LOG_INFO, "scanning %s\n", filename); + ret = scan_file(avctx, vst, ast, i); + if (ret < 0) { + av_log(avctx, AV_LOG_WARNING, "ignoring %s; %s\n", filename, av_err2str(ret)); + avio_close(mlv->pb[i]); + mlv->pb[i] = NULL; + continue; + } + } + av_free(filename); + } + + if (vst) + vst->duration = vst->nb_index_entries; + if (ast) + ast->duration = ast->nb_index_entries; + + if (vst && ast) + avio_seek(pb, FFMIN(vst->index_entries[0].pos, ast->index_entries[0].pos), SEEK_SET); + else if (vst) + avio_seek(pb, vst->index_entries[0].pos, SEEK_SET); + else if (ast) + avio_seek(pb, ast->index_entries[0].pos, SEEK_SET); + + return 0; +} + +static int read_packet(AVFormatContext *avctx, AVPacket *pkt) +{ + MlvContext *mlv = avctx->priv_data; + AVIOContext *pb; + AVStream *st = avctx->streams[mlv->stream_index]; + int index, ret; + unsigned int size, space; + + if (mlv->pts >= st->duration) + return AVERROR_EOF; + + index = av_index_search_timestamp(st, mlv->pts, AVSEEK_FLAG_ANY); + if (index < 0) { + av_log(avctx, AV_LOG_ERROR, "could not find index entry for frame %"PRId64"\n", mlv->pts); + return AVERROR(EIO); + } + + pb = mlv->pb[st->index_entries[index].size]; + avio_seek(pb, st->index_entries[index].pos, SEEK_SET); + + avio_skip(pb, 4); // blockType + size = avio_rl32(pb); + if (size < 16) + return AVERROR_INVALIDDATA; + avio_skip(pb, 12); //timestamp, frameNumber + if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) + avio_skip(pb, 8); // cropPosX, cropPosY, panPosX, panPosY + space = avio_rl32(pb); + avio_skip(pb, space); + + if ((mlv->class[st->id] & (MLV_CLASS_FLAG_DELTA|MLV_CLASS_FLAG_LZMA))) { + ret = AVERROR_PATCHWELCOME; + } else if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) { + ret = av_get_packet(pb, pkt, (st->codec->width * st->codec->height * st->codec->bits_per_coded_sample + 7) >> 3); + } else { // AVMEDIA_TYPE_AUDIO + if (space > UINT_MAX - 24 || size < (24 + space)) + return AVERROR_INVALIDDATA; + ret = av_get_packet(pb, pkt, size - (24 + space)); + } + + if (ret < 0) + return ret; + + pkt->stream_index = mlv->stream_index; + pkt->pts = mlv->pts; + + mlv->stream_index++; + if (mlv->stream_index == avctx->nb_streams) { + mlv->stream_index = 0; + mlv->pts++; + } + return 0; +} + +static int read_seek(AVFormatContext *avctx, int stream_index, int64_t timestamp, int flags) +{ + MlvContext *mlv = avctx->priv_data; + + if ((flags & AVSEEK_FLAG_FRAME) || (flags & AVSEEK_FLAG_BYTE)) + return AVERROR(ENOSYS); + + if (!avctx->pb->seekable) + return AVERROR(EIO); + + mlv->pts = timestamp; + return 0; +} + +static int read_close(AVFormatContext *s) +{ + MlvContext *mlv = s->priv_data; + int i; + for (i = 0; i < 100; i++) + if (mlv->pb[i]) + avio_close(mlv->pb[i]); + return 0; +} + +AVInputFormat ff_mlv_demuxer = { + .name = "mlv", + .long_name = NULL_IF_CONFIG_SMALL("Magic Lantern Video (MLV)"), + .priv_data_size = sizeof(MlvContext), + .read_probe = probe, + .read_header = read_header, + .read_packet = read_packet, + .read_close = read_close, + .read_seek = read_seek, +}; diff --git a/libavformat/mm.c b/libavformat/mm.c index 8c9cbd7..4315802 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 61f1d7a..f557eeb 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->flags & AVFMT_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 (avio_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 fb16a3c..7e60b3b 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 e89da41..57e3d7e 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 5e9d0bc..dac5d9f 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; } @@ -118,7 +119,7 @@ static int read_data_packet(MMSHContext *mmsh, const int len) int res; if (len > sizeof(mms->in_buffer)) { av_log(NULL, AV_LOG_ERROR, - "Data packet length %d exceeds the in_buffer size %zu\n", + "Data packet length %d exceeds the in_buffer size %"SIZE_SPECIFIER"\n", len, sizeof(mms->in_buffer)); return AVERROR(EIO); } @@ -193,7 +194,7 @@ static int get_http_header_data(MMSHContext *mmsh) if (len) { if (len > sizeof(mms->in_buffer)) { av_log(NULL, AV_LOG_ERROR, - "Other packet len = %d exceed the in_buffer size %zu\n", + "Other packet len = %d exceed the in_buffer size %"SIZE_SPECIFIER"\n", len, sizeof(mms->in_buffer)); return AVERROR(EIO); } @@ -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 4f896e0..c851187 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 int mms_put_utf16(MMSContext *mms, const uint8_t *src) { AVIOContext bic; int size = mms->write_out_ptr - mms->out_buffer; @@ -161,7 +161,10 @@ static void mms_put_utf16(MMSContext *mms, uint8_t *src) sizeof(mms->out_buffer) - size, 1, NULL, NULL, NULL, NULL); len = avio_put_str16le(&bic, src); + if (len < 0) + return len; mms->write_out_ptr += len; + return 0; } static int send_time_test_data(MMSTContext *mmst) @@ -173,6 +176,7 @@ static int send_time_test_data(MMSTContext *mmst) static int send_protocol_select(MMSTContext *mmst) { + int ret; char data_string[256]; MMSContext *mms = &mmst->mms; @@ -189,18 +193,21 @@ static int send_protocol_select(MMSTContext *mmst) "TCP", // or UDP LOCAL_PORT); - mms_put_utf16(mms, data_string); + if ((ret = mms_put_utf16(mms, data_string)) < 0) + return ret; return send_command_packet(mmst); } static int send_media_file_request(MMSTContext *mmst) { + int ret; MMSContext *mms = &mmst->mms; start_command_packet(mmst, CS_PKT_MEDIA_FILE_REQUEST); insert_command_prefixes(mms, 1, 0xffffffff); bytestream_put_le32(&mms->write_out_ptr, 0); bytestream_put_le32(&mms->write_out_ptr, 0); - mms_put_utf16(mms, mmst->path + 1); // +1 for skip "/" + if ((ret = mms_put_utf16(mms, mmst->path + 1)) < 0) // +1 for skip "/" + return ret; return send_command_packet(mmst); } @@ -277,7 +284,7 @@ static MMSSCPacketType get_tcp_server_response(MMSTContext *mmst) if (length_remaining < 0 || length_remaining > sizeof(mms->in_buffer) - 12) { av_log(NULL, AV_LOG_ERROR, - "Incoming packet length %d exceeds bufsize %zu\n", + "Incoming packet length %d exceeds bufsize %"SIZE_SPECIFIER"\n", length_remaining, sizeof(mms->in_buffer) - 12); return AVERROR_INVALIDDATA; } @@ -313,7 +320,7 @@ static MMSSCPacketType get_tcp_server_response(MMSTContext *mmst) if (length_remaining < 0 || length_remaining > sizeof(mms->in_buffer) - 8) { av_log(NULL, AV_LOG_ERROR, - "Data length %d is invalid or too large (max=%zu)\n", + "Data length %d is invalid or too large (max=%"SIZE_SPECIFIER")\n", length_remaining, sizeof(mms->in_buffer)); return AVERROR_INVALIDDATA; } @@ -417,6 +424,7 @@ static int send_media_header_request(MMSTContext *mmst) static int send_startup_packet(MMSTContext *mmst) { char data_string[256]; + int ret; MMSContext *mms = &mmst->mms; // SubscriberName is defined in MS specification linked below. // The guid value can be any valid value. @@ -429,7 +437,8 @@ static int send_startup_packet(MMSTContext *mmst) start_command_packet(mmst, CS_PKT_INITIAL); insert_command_prefixes(mms, 0, 0x0004000b); bytestream_put_le32(&mms->write_out_ptr, 0x0003001c); - mms_put_utf16(mms, data_string); + if ((ret = mms_put_utf16(mms, data_string)) < 0) + return ret; return send_command_packet(mmst); } diff --git a/libavformat/mov.c b/libavformat/mov.c index 3734689..ae48c02 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 */ @@ -31,11 +31,14 @@ #include "libavutil/attributes.h" #include "libavutil/channel_layout.h" +#include "libavutil/display.h" #include "libavutil/intreadwrite.h" #include "libavutil/intfloat.h" #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" @@ -89,16 +92,13 @@ static int mov_metadata_track_or_disc_number(MOVContext *c, AVIOContext *pb, static int mov_metadata_int8_bypass_padding(MOVContext *c, AVIOContext *pb, unsigned len, const char *key) { - char buf[16]; - /* bypass padding bytes */ avio_r8(pb); avio_r8(pb); avio_r8(pb); - snprintf(buf, sizeof(buf), "%d", avio_r8(pb)); c->fc->event_flags |= AVFMT_EVENT_FLAG_METADATA_UPDATED; - av_dict_set(&c->fc->metadata, key, buf, 0); + av_dict_set_int(&c->fc->metadata, key, avio_r8(pb), 0); return 0; } @@ -106,11 +106,8 @@ static int mov_metadata_int8_bypass_padding(MOVContext *c, AVIOContext *pb, static int mov_metadata_int8_no_padding(MOVContext *c, AVIOContext *pb, unsigned len, const char *key) { - char buf[16]; - - snprintf(buf, sizeof(buf), "%d", avio_r8(pb)); c->fc->event_flags |= AVFMT_EVENT_FLAG_METADATA_UPDATED; - av_dict_set(&c->fc->metadata, key, buf, 0); + av_dict_set_int(&c->fc->metadata, key, avio_r8(pb), 0); return 0; } @@ -119,16 +116,14 @@ static int mov_metadata_gnre(MOVContext *c, AVIOContext *pb, unsigned len, const char *key) { short genre; - char buf[20]; avio_r8(pb); // unknown genre = avio_r8(pb); if (genre < 1 || genre > ID3v1_GENRE_MAX) return 0; - snprintf(buf, sizeof(buf), "%s", ff_id3v1_genre_str[genre-1]); c->fc->event_flags |= AVFMT_EVENT_FLAG_METADATA_UPDATED; - av_dict_set(&c->fc->metadata, key, buf, 0); + av_dict_set(&c->fc->metadata, key, ff_id3v1_genre_str[genre-1], 0); return 0; } @@ -163,7 +158,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; @@ -212,6 +207,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_metadata_loci(MOVContext *c, AVIOContext *pb, unsigned len) { char language[4] = { 0 }; @@ -267,8 +273,12 @@ static int mov_read_udta_string(MOVContext *c, AVIOContext *pb, MOVAtom atom) case MKTAG(0xa9,'A','R','T'): key = "artist"; break; case MKTAG( 'a','A','R','T'): key = "album_artist"; break; case MKTAG(0xa9,'w','r','t'): key = "composer"; break; + case MKTAG( 'c','p','i','l'): key = "compilation"; + parse = mov_metadata_int8_no_padding; 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; @@ -279,6 +289,8 @@ 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; @@ -299,8 +311,14 @@ 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"); case MKTAG( 'l','o','c','i'): return mov_metadata_loci(c, pb, atom.size); + case MKTAG( 'X','M','P','_'): + return mov_metadata_raw(c, pb, atom.size, "xmp"); } if (c->itunes_metadata && atom.size > 8) { @@ -316,8 +334,8 @@ static int mov_read_udta_string(MOVContext *c, AVIOContext *pb, MOVAtom atom) int ret = mov_read_covr(c, pb, data_type, str_size); if (ret < 0) { av_log(c->fc, AV_LOG_ERROR, "Error parsing cover art.\n"); - return ret; } + return ret; } } else return 0; } else if (atom.size > 4 && key && !c->itunes_metadata) { @@ -348,7 +366,9 @@ static int mov_read_udta_string(MOVContext *c, AVIOContext *pb, MOVAtom atom) 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); + int ret = avio_read(pb, str, str_size); + if (ret != str_size) + return ret < 0 ? ret : AVERROR_INVALIDDATA; str[str_size] = 0; } c->fc->event_flags |= AVFMT_EVENT_FLAG_METADATA_UPDATED; @@ -415,6 +435,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); @@ -464,7 +485,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(avio_feof(pb)) return AVERROR_EOF; type = avio_rb16(pb); len = avio_rb16(pb); @@ -491,7 +512,8 @@ static int mov_read_dref(MOVContext *c, AVIOContext *pb, MOVAtom atom) dref->dir = av_malloc(len+1); if (!dref->dir) return AVERROR(ENOMEM); - avio_read(pb, dref->dir, len); + if (avio_read(pb, dref->dir, len) != len) + return AVERROR_INVALIDDATA; dref->dir[len] = 0; for (j = 0; j < len; j++) if (dref->dir[j] == ':') @@ -511,6 +533,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; @@ -540,6 +564,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; } @@ -633,6 +670,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; @@ -646,7 +686,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; } @@ -688,7 +730,6 @@ static int mov_read_ftyp(MOVContext *c, AVIOContext *pb, MOVAtom atom) { uint32_t minor_ver; int comp_brand_size; - char minor_ver_str[11]; /* 32 bit integer -> 10 digits + null */ char* comp_brands_str; uint8_t type[5] = {0}; @@ -698,8 +739,7 @@ static int mov_read_ftyp(MOVContext *c, AVIOContext *pb, MOVAtom atom) av_log(c->fc, AV_LOG_DEBUG, "ISO: File Type Major Brand: %.4s\n",(char *)&type); av_dict_set(&c->fc->metadata, "major_brand", type, 0); minor_ver = avio_rb32(pb); /* minor version */ - snprintf(minor_ver_str, sizeof(minor_ver_str), "%"PRIu32"", minor_ver); - av_dict_set(&c->fc->metadata, "minor_version", minor_ver_str, 0); + av_dict_set_int(&c->fc->metadata, "minor_version", minor_ver, 0); comp_brand_size = atom.size - 8; if (comp_brand_size < 0) @@ -720,6 +760,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' */ @@ -735,13 +781,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); @@ -755,7 +804,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; @@ -795,7 +844,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 */ @@ -812,6 +861,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 */ @@ -827,31 +880,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; } @@ -864,7 +892,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) { @@ -922,7 +950,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; @@ -932,6 +961,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; @@ -943,10 +976,78 @@ static int mov_read_extradata(MOVContext *c, AVIOContext *pb, MOVAtom atom) st->codec->extradata_size= size - FF_INPUT_BUFFER_PADDING_SIZE; AV_WB32( buf , atom.size + 8); AV_WL32( buf + 4, atom.type); - avio_read(pb, buf + 8, atom.size); + err = avio_read(pb, buf + 8, atom.size); + if (err < 0) { + return err; + } else if (err < atom.size) { + av_log(c->fc, AV_LOG_WARNING, "truncated extradata\n"); + st->codec->extradata_size -= atom.size - err; + } + memset(buf + 8 + err, 0, FF_INPUT_BUFFER_PADDING_SIZE); 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) +{ + int ret = mov_read_extradata(c, pb, atom, AV_CODEC_ID_AVUI); + if(ret == 0) + ret = mov_read_extradata(c, pb, atom, AV_CODEC_ID_DNXHD); + return ret; +} + +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) +{ + if (c->fc->nb_streams >= 1) { + 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; @@ -958,14 +1059,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_get_extradata(st->codec, pb, atom.size) < 0) 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; if ((ret = mov_read_default(c, pb, atom)) < 0) @@ -1000,11 +1100,9 @@ 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_get_extradata(st->codec, pb, atom.size) < 0) return AVERROR(ENOMEM); - st->codec->extradata_size = atom.size; - avio_read(pb, st->codec->extradata, atom.size); + return 0; } @@ -1012,6 +1110,7 @@ static int mov_read_dvc1(MOVContext *c, AVIOContext *pb, MOVAtom atom) { AVStream *st; uint8_t profile_level; + int ret; if (c->fc->nb_streams < 1) return 0; @@ -1024,13 +1123,11 @@ static int mov_read_dvc1(MOVContext *c, AVIOContext *pb, MOVAtom atom) if ((profile_level & 0xf0) != 0xc0) return 0; - av_free(st->codec->extradata); - st->codec->extradata = av_mallocz(atom.size - 7 + FF_INPUT_BUFFER_PADDING_SIZE); - if (!st->codec->extradata) - 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); + av_free(st->codec->extradata); + if ((ret = ff_get_extradata(st->codec, pb, atom.size - 7)) < 0) + return ret; + return 0; } @@ -1052,13 +1149,10 @@ static int mov_read_strf(MOVContext *c, AVIOContext *pb, MOVAtom atom) if ((uint64_t)atom.size > (1<<30)) return AVERROR_INVALIDDATA; + avio_skip(pb, 40); av_free(st->codec->extradata); - st->codec->extradata = av_mallocz(atom.size - 40 + FF_INPUT_BUFFER_PADDING_SIZE); - if (!st->codec->extradata) + if (ff_get_extradata(st->codec, pb, atom.size - 40) < 0) 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; } @@ -1138,7 +1232,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; @@ -1183,8 +1279,11 @@ static void mov_parse_stsd_video(MOVContext *c, AVIOContext *pb, av_dict_set(&st->metadata, "encoder", codec_name, 0); /* codec_tag YV12 triggers an UV swap in rawdec.c */ - if (!memcmp(codec_name, "Planar Y'CbCr 8-bit 4:2:0", 25)) + if (!memcmp(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(codec_name, "Sorenson H263", 13)) @@ -1197,12 +1296,15 @@ static void mov_parse_stsd_video(MOVContext *c, AVIOContext *pb, /* figure out the palette situation */ color_depth = st->codec->bits_per_coded_sample & 0x1F; color_greyscale = st->codec->bits_per_coded_sample & 0x20; + /* Do not create a greyscale palette for cinepak */ + if (color_greyscale && st->codec->codec_id == AV_CODEC_ID_CINEPAK) + return; /* if the depth is 2, 4, or 8 bpp, file is palettized */ 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; @@ -1213,7 +1315,7 @@ static void mov_parse_stsd_video(MOVContext *c, AVIOContext *pb, color_dec = 256 / (color_count - 1); for (j = 0; j < color_count; j++) { 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; @@ -1233,7 +1335,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 */ @@ -1242,10 +1344,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); @@ -1253,7 +1354,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); } } } @@ -1266,6 +1367,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 */ @@ -1281,7 +1383,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 */ @@ -1417,11 +1521,38 @@ 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_get_extradata(st->codec, pb, size) < 0) 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; + if (size > 30) { + uint32_t len = AV_RB32(st->codec->extradata + 18); /* name atom length */ + uint32_t format = AV_RB32(st->codec->extradata + 22); + if (format == AV_RB32("name") && (int64_t)size >= (int64_t)len + 18) { + uint16_t str_size = AV_RB16(st->codec->extradata + 26); /* string length */ + if (str_size > 0 && size >= (int)str_size + 26) { + char *reel_name = av_malloc(str_size + 1); + if (!reel_name) + return AVERROR(ENOMEM); + memcpy(reel_name, st->codec->extradata + 30, str_size); + reel_name[str_size] = 0; /* Add null terminator */ + /* don't add reel_name if emtpy string */ + if (*reel_name == 0) { + av_free(reel_name); + } else { + av_dict_set(&st->metadata, "reel_name", reel_name, AV_DICT_DONT_STRDUP_VAL); + } + } + } + } + } } else { /* other codec type, just skip (rtp, mp4s ...) */ avio_skip(pb, size); @@ -1456,6 +1587,10 @@ static int mov_finalize_stsd_codec(MOVContext *c, AVIOContext *pb, // force sample rate for qcelp when not stored in mov if (st->codec->codec_tag != MKTAG('Q','c','l','p')) st->codec->sample_rate = 8000; + // FIXME: Why is the following needed for some files? + sc->samples_per_frame = 160; + if (!sc->bytes_per_frame) + sc->bytes_per_frame = 35; break; case AV_CODEC_ID_AMR_NB: st->codec->channels = 1; @@ -1476,6 +1611,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: @@ -1484,6 +1622,8 @@ 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: + case AV_CODEC_ID_MPEG1VIDEO: case AV_CODEC_ID_VC1: st->need_parsing = AVSTREAM_PARSE_FULL; break; @@ -1495,17 +1635,14 @@ static int mov_finalize_stsd_codec(MOVContext *c, AVIOContext *pb, static int mov_skip_multiple_stsd(MOVContext *c, AVIOContext *pb, int codec_tag, int format, - int size) + int64_t size) { 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. */ @@ -1514,6 +1651,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; } @@ -1537,15 +1679,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 %"PRIu32" 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; } @@ -1558,7 +1700,7 @@ int ff_mov_read_stsd_entries(MOVContext *c, AVIOContext *pb, int entries) id = mov_codec_id(st, format); - av_dlog(c->fc, "size=%"PRIu32" 4CC= %"PRIu8"%"PRIu8"%"PRIu8"%"PRIu8" 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); @@ -1698,6 +1840,8 @@ static int mov_read_stss(MOVContext *c, AVIOContext *pb, MOVAtom atom) if (!entries) { sc->keyframe_absent = 1; + if (!st->need_parsing && st->codec->codec_type == AVMEDIA_TYPE_VIDEO) + st->need_parsing = AVSTREAM_PARSE_HEADERS; return 0; } if (entries >= UINT_MAX / sizeof(int)) @@ -1739,6 +1883,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; @@ -1816,10 +1961,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; av_free(sc->stts_data); sc->stts_data = av_malloc(entries * sizeof(*sc->stts_data)); @@ -1832,6 +1975,13 @@ 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 %d in STTS, at %d st:%d\n", + sample_duration, i, c->fc->nb_streams-1); + sample_duration = 1; + } if (sample_count < 0) { av_log(c->fc, AV_LOG_ERROR, "Invalid sample_count=%d\n", sample_count); return AVERROR_INVALIDDATA; @@ -1842,12 +1992,21 @@ static int mov_read_stts(MOVContext *c, AVIOContext *pb, MOVAtom atom) av_dlog(c->fc, "sample_count=%d, sample_duration=%d\n", sample_count, sample_duration); + if ( i+1 == entries + && i + && sample_count == 1 + && total_sample_count > 100 + && sample_duration/10 > duration / total_sample_count) + sample_duration = duration / total_sample_count; duration+=(int64_t)sample_duration*sample_count; total_sample_count+=sample_count; } sc->stts_count = i; + sc->duration_for_fps += duration; + sc->nb_frames_for_fps += total_sample_count; + if (pb->eof_reached) return AVERROR_EOF; @@ -1858,6 +2017,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; @@ -1889,8 +2055,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; @@ -1956,12 +2133,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; @@ -1979,11 +2157,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; @@ -1996,10 +2174,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) { @@ -2024,9 +2214,14 @@ static void mov_build_index(MOVContext *mov, AVStream *st) rap_group_index++; } } + if (sc->keyframe_absent + && !sc->stps_count + && !rap_group_present + && (st->codec->codec_type == AVMEDIA_TYPE_AUDIO || (i==0 && j==0))) + keyframe = 1; 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++]; @@ -2038,6 +2233,8 @@ static void mov_build_index(MOVContext *mov, AVStream *st) av_dlog(mov->fc, "AVIndex stream %d, sample %d, offset %"PRIx64", dts %"PRId64", " "size %d, distance %d, keyframe %d\n", st->index, current_sample, current_offset, current_dts, sample_size, distance, keyframe); + if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO && st->nb_index_entries < 100) + ff_rfps_add_frame(mov->fc, st, current_dts); } current_offset += sample_size; @@ -2143,14 +2340,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 */ @@ -2182,11 +2379,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; @@ -2214,12 +2426,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); @@ -2227,14 +2434,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 && @@ -2242,6 +2452,12 @@ static int mov_read_trak(MOVContext *c, AVIOContext *pb, MOVAtom atom) st->sample_aspect_ratio = av_d2q(((double)st->codec->height * sc->width) / ((double)st->codec->width * sc->height), 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. @@ -2288,11 +2504,18 @@ static int mov_read_ilst(MOVContext *c, AVIOContext *pb, MOVAtom atom) return ret; } -static int mov_read_replaygain(MOVContext *c, AVIOContext *pb, int size) +static int mov_read_custom_2plus(MOVContext *c, AVIOContext *pb, int size) { int64_t end = avio_tell(pb) + size; uint8_t *key = NULL, *val = NULL; 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; for (i = 0; i < 2; i++) { uint8_t **p; @@ -2326,9 +2549,18 @@ static int mov_read_replaygain(MOVContext *c, AVIOContext *pb, int size) } if (key && val) { - av_dict_set(&c->fc->metadata, key, val, - AV_DICT_DONT_STRDUP_KEY | AV_DICT_DONT_STRDUP_VAL); - key = val = NULL; + if (strcmp(key, "iTunSMPB") == 0) { + int priming, remainder, samples; + if(sscanf(val, "%*X %X %X %X", &priming, &remainder, &samples) == 3){ + if(priming>0 && priming<16384) + sc->start_pad = priming; + } + } + if (strcmp(key, "cdec") != 0) { + av_dict_set(&c->fc->metadata, key, val, + AV_DICT_DONT_STRDUP_KEY | AV_DICT_DONT_STRDUP_VAL); + key = val = NULL; + } } avio_seek(pb, end, SEEK_SET); @@ -2360,8 +2592,7 @@ static int mov_read_custom(MOVContext *c, AVIOContext *pb, MOVAtom atom) domain_len = avio_get_str(pb, len, domain, sizeof(domain)); avio_skip(pb, len - domain_len); - if (!strcmp(domain, "org.hydrogenaudio.replaygain")) - return mov_read_replaygain(c, pb, end - avio_tell(pb)); + return mov_read_custom_2plus(c, pb, end - avio_tell(pb)); } fail: @@ -2440,7 +2671,8 @@ static int mov_read_tkhd(MOVContext *c, AVIOContext *pb, MOVAtom atom) sc->width = width >> 16; sc->height = height >> 16; - // save the matrix when it is not the default identity + // save the matrix and add rotate metadata when it is not the default + // identity if (display_matrix[0][0] != (1 << 16) || display_matrix[1][1] != (1 << 16) || display_matrix[2][2] != (1 << 30) || @@ -2448,6 +2680,7 @@ static int mov_read_tkhd(MOVContext *c, AVIOContext *pb, MOVAtom atom) display_matrix[1][0] || display_matrix[1][2] || display_matrix[2][0] || display_matrix[2][1]) { int i, j; + double rotate; av_freep(&sc->display_matrix); sc->display_matrix = av_malloc(sizeof(int32_t) * 9); @@ -2457,6 +2690,16 @@ static int mov_read_tkhd(MOVContext *c, AVIOContext *pb, MOVAtom atom) for (i = 0; i < 3; i++) for (j = 0; j < 3; j++) sc->display_matrix[i * 3 + j] = display_matrix[j][i]; + + rotate = av_display_rotation_get(sc->display_matrix); + if (!isnan(rotate)) { + char rotate_buf[64]; + rotate = -rotate; + if (rotate < 0) // for backward compatibility + rotate += 360; + snprintf(rotate_buf, sizeof(rotate_buf), "%g", rotate); + av_dict_set(&st->metadata, "rotate", rotate_buf, 0); + } } // transform the display width/height according to the matrix @@ -2539,6 +2782,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 */ @@ -2573,7 +2819,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); @@ -2621,6 +2867,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; @@ -2639,6 +2886,8 @@ static int mov_read_trun(MOVContext *c, AVIOContext *pb, MOVAtom atom) dts += sample_duration; offset += sample_size; sc->data_size += sample_size; + sc->duration_for_fps += sample_duration; + sc->nb_frames_for_fps ++; } if (pb->eof_reached) @@ -2685,7 +2934,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 */ @@ -2724,9 +2973,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; @@ -2737,9 +2987,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); @@ -2747,22 +2999,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=\""))) { + 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 */ @@ -2776,7 +3115,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 }, @@ -2786,8 +3125,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 }, @@ -2805,6 +3144,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 }, @@ -2820,6 +3160,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 }, { MKTAG('-','-','-','-'), mov_read_custom }, { 0, NULL } }; @@ -2832,25 +3174,46 @@ 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 && !avio_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 (a.type == MKTAG('f','r','e','e') && + a.size >= 8 && + c->moov_retry) { + uint8_t buf[8]; + uint32_t *type = (uint32_t *)buf + 1; + avio_read(pb, buf, 8); + avio_seek(pb, -8, SEEK_CUR); + if (*type == MKTAG('m','v','h','d') || + *type == MKTAG('c','m','o','v')) { + av_log(c->fc, AV_LOG_ERROR, "Detected moov in a free atom.\n"); + a.type = MKTAG('m','o','o','v'); + } + } + 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) @@ -2905,46 +3268,83 @@ 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('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 if (tag == MKTAG('f','t','y','p') && + AV_RL32(p->buf + offset + 8) == MKTAG('j','p','2',' ')) { + score = FFMAX(score, 5); + } 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 @@ -2977,6 +3377,11 @@ static void mov_read_chapters(AVFormatContext *s) uint16_t ch; int len, title_len; + if (end < sample->timestamp) { + av_log(s, AV_LOG_WARNING, "ignoring stream duration which is shorter than chapters\n"); + end = AV_NOPTS_VALUE; + } + if (avio_seek(sc->pb, sample->pos, SEEK_SET) != sample->pos) { av_log(s, AV_LOG_ERROR, "Chapter %d not found in file\n", i); goto finish; @@ -3006,7 +3411,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); } } @@ -3017,6 +3422,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; @@ -3032,9 +3480,13 @@ static int mov_read_close(AVFormatContext *s) av_freep(&sc->drefs[j].dir); } av_freep(&sc->drefs); - if (sc->pb && sc->pb != s->pb) + + sc->drefs_count = 0; + + if (!sc->pb_is_copied) avio_close(sc->pb); + sc->pb = NULL; av_freep(&sc->chunk_offsets); av_freep(&sc->stsc_data); av_freep(&sc->sample_sizes); @@ -3051,15 +3503,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 j, err; MOVAtom atom = { AV_RL32("root") }; int i; @@ -3071,11 +3558,15 @@ static int mov_read_header(AVFormatContext *s) atom.size = INT64_MAX; /* check MOV header */ + do { + if (mov->moov_retry) + avio_seek(pb, 0, SEEK_SET); if ((err = mov_read_default(mov, pb, atom)) < 0) { - av_log(s, AV_LOG_ERROR, "error reading header: %d\n", err); + av_log(s, AV_LOG_ERROR, "error reading header\n"); mov_read_close(s); return err; } + } while (pb->seekable && !mov->found_moov && !mov->moov_retry++); if (!mov->found_moov) { av_log(s, AV_LOG_ERROR, "moov atom not found\n"); mov_read_close(s); @@ -3083,15 +3574,47 @@ 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 (st->codec->codec_type == AVMEDIA_TYPE_VIDEO && sc->nb_frames_for_fps > 0 && sc->duration_for_fps > 0) + av_reduce(&st->avg_frame_rate.num, &st->avg_frame_rate.den, + sc->time_scale*(int64_t)sc->nb_frames_for_fps, sc->duration_for_fps, INT_MAX); if (st->codec->codec_type == AVMEDIA_TYPE_SUBTITLE) { - if (st->codec->width <= 0 && st->codec->width <= 0) { + if (st->codec->width <= 0 && st->codec->height <= 0) { st->codec->width = sc->width; st->codec->height = sc->height; } @@ -3111,6 +3634,14 @@ 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]; + } + } + + ff_rfps_calculate(s); + for (i = 0; i < s->nb_streams; i++) { AVStream *st = s->streams[i]; MOVStreamContext *sc = st->priv_data; @@ -3181,6 +3712,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) { @@ -3190,7 +3722,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) + avio_feof(s->pb)) return AVERROR_EOF; av_dlog(s, "read fragments, offset 0x%"PRIx64"\n", avio_tell(s->pb)); goto retry; @@ -3199,6 +3731,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", @@ -3221,7 +3758,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); @@ -3298,8 +3835,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); @@ -3310,7 +3845,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; @@ -3320,6 +3858,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"), @@ -3330,4 +3885,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 aa7ba10..3b91ed7 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 */ @@ -344,7 +344,7 @@ static const struct MovChannelLayoutMap mov_ch_layout_map_9ch[] = { { 0, 0 }, }; -static const struct MovChannelLayoutMap *mov_ch_layout_map[] = { +static const struct MovChannelLayoutMap * const mov_ch_layout_map[] = { mov_ch_layout_map_misc, mov_ch_layout_map_1ch, mov_ch_layout_map_2ch, @@ -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 3fae939..ca28345 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 8af3c34..bf61391 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 */ @@ -34,12 +34,14 @@ #include "libavcodec/get_bits.h" #include "libavcodec/put_bits.h" #include "libavcodec/vc1.h" +#include "libavcodec/raw.h" #include "internal.h" #include "libavutil/avstring.h" #include "libavutil/intfloat.h" #include "libavutil/mathematics.h" #include "libavutil/opt.h" #include "libavutil/dict.h" +#include "libavutil/pixdesc.h" #include "hevc.h" #include "rtpenc.h" #include "mov_chan.h" @@ -50,6 +52,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" }, @@ -59,13 +62,15 @@ static const AVOption options[] = { { "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" }, { "disable_chpl", "Disable Nero chapter atom", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_DISABLE_CHPL}, 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}, { "brand", "Override major brand", offsetof(MOVMuxContext, major_brand), AV_OPT_TYPE_STRING, {.str = NULL}, .flags = AV_OPT_FLAG_ENCODING_PARAM }, { NULL }, }; @@ -78,6 +83,8 @@ static const AVClass flavor ## _muxer_class = {\ .version = LIBAVUTIL_VERSION_INT,\ }; +static int get_moov_size(AVFormatContext *s); + static int utf8len(const uint8_t *b) { int len = 0; @@ -100,6 +107,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) @@ -114,13 +127,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 @@ -176,10 +191,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; @@ -286,6 +301,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; @@ -295,10 +326,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"); @@ -331,12 +374,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 @@ -350,22 +391,42 @@ 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) { + int ret; int64_t pos = avio_tell(pb); avio_wb32(pb, 0); avio_wl32(pb, track->tag); // store it byteswapped track->enc->codec_tag = av_bswap16(track->tag >> 16); - ff_put_wav_header(pb, track->enc); + if ((ret = ff_put_wav_header(pb, track->enc, 0)) < 0) + return ret; return update_size(pb, pos); } static int mov_write_wfex_tag(AVIOContext *pb, MOVTrack *track) { + int ret; int64_t pos = avio_tell(pb); avio_wb32(pb, 0); ffio_wfourcc(pb, "wfex"); - ff_put_wav_header(pb, track->enc); + if ((ret = ff_put_wav_header(pb, track->enc, FF_PUT_WAV_HEADER_FORCE_WAVEFORMATEX)) < 0) + return ret; return update_size(pb, pos); } @@ -383,6 +444,9 @@ static int mov_write_chan_tag(AVIOContext *pb, MOVTrack *track) return 0; } + if (track->multichannel_as_mono) + return 0; + avio_wb32(pb, 0); // Size ffio_wfourcc(pb, "chan"); // Type avio_w8(pb, 0); // Version @@ -401,9 +465,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 */ @@ -411,11 +477,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) { @@ -575,13 +646,20 @@ static int get_cluster_duration(MOVTrack *track, int cluster_idx) else next_dts = track->cluster[cluster_idx + 1].dts; - return next_dts - track->cluster[cluster_idx].dts; + next_dts -= track->cluster[cluster_idx].dts; + + av_assert0(next_dts >= 0); + av_assert0(next_dts <= INT_MAX); + + return next_dts; } 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; @@ -604,9 +682,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 */ @@ -635,10 +721,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 ? @@ -646,13 +741,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); @@ -685,19 +794,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); @@ -726,7 +822,11 @@ static int mov_write_avid_tag(AVIOContext *pb, MOVTrack *track) ffio_wfourcc(pb, "ACLR"); ffio_wfourcc(pb, "ACLR"); ffio_wfourcc(pb, "0001"); - avio_wb32(pb, 2); /* yuv range: full 1 / normal 2 */ + if (track->enc->color_range == AVCOL_RANGE_MPEG) { /* Legal range (16-235) */ + avio_wb32(pb, 1); /* Corresponds to 709 in official encoder */ + } else { /* Full range (0-255) */ + avio_wb32(pb, 2); /* Corresponds to RGB in official encoder */ + } avio_wb32(pb, 0); /* unknown */ avio_wb32(pb, 24); /* size */ @@ -807,8 +907,10 @@ static int ipod_get_codec_tag(AVFormatContext *s, MOVTrack *track) tag == MKTAG('t', 'e', 'x', 't')))) tag = ff_codec_get_tag(codec_ipod_tags, track->enc->codec_id); - if (!av_match_ext(s->filename, "m4a") && !av_match_ext(s->filename, "m4v")) - av_log(s, AV_LOG_WARNING, "Warning, extension is not .m4a nor .m4v " + if (!av_match_ext(s->filename, "m4a") && + !av_match_ext(s->filename, "m4b") && + !av_match_ext(s->filename, "m4v")) + av_log(s, AV_LOG_WARNING, "Warning, extension is not .m4a, .m4v nor .m4b " "Quicktime/Ipod might not play the file\n"); return tag; @@ -818,20 +920,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->st->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->st->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; } @@ -839,11 +941,87 @@ 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 = track->enc->codec_tag; + int interlaced = track->enc->field_order > AV_FIELD_PROGRESSIVE; + AVStream *st = track->st; + int rate = av_q2d(find_fps(s, st)); + + if (!tag) + tag = MKTAG('m', '2', 'v', '1'); //fallback tag + + 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 }, @@ -864,15 +1042,24 @@ static int mov_get_rawvideo_codec_tag(AVFormatContext *s, MOVTrack *track) { int tag = track->enc->codec_tag; int i; + enum AVPixelFormat pix_fmt; for (i = 0; i < FF_ARRAY_ELEMS(mov_pix_fmt_tags); i++) { 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; } } + pix_fmt = avpriv_find_pix_fmt(avpriv_pix_fmt_bps_mov, + track->enc->bits_per_coded_sample); + if (tag == MKTAG('r','a','w',' ') && + track->enc->pix_fmt != pix_fmt && + track->enc->pix_fmt != AV_PIX_FMT_NONE) + av_log(s, AV_LOG_ERROR, "%s rawvideo cannot be written to mov, output file will be unreadable\n", + av_get_pix_fmt_name(track->enc->pix_fmt)); return tag; } @@ -884,11 +1071,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 @@ -925,7 +1115,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') }, @@ -1019,9 +1209,36 @@ static int mov_write_pasp_tag(AVIOContext *pb, MOVTrack *track) return 16; } -static int mov_write_video_tag(AVIOContext *pb, MOVTrack *track) +static void find_compressor(char * compressor_name, int len, MOVTrack *track) { AVDictionaryEntry *encoder; + 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 && + (encoder = av_dict_get(track->st->metadata, "encoder", NULL, 0))) { + av_strlcpy(compressor_name, encoder->value, 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); char compressor_name[32] = { 0 }; @@ -1055,9 +1272,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 && - (encoder = av_dict_get(track->st->metadata, "encoder", NULL, 0))) - av_strlcpy(compressor_name, encoder->value, 32); + find_compressor(compressor_name, 32, track); avio_w8(pb, strlen(compressor_name)); avio_write(pb, compressor_name, 31); @@ -1070,9 +1285,11 @@ 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_HEVC) mov_write_hvcc_tag(pb, track); @@ -1080,9 +1297,7 @@ static int mov_write_video_tag(AVIOContext *pb, MOVTrack *track) 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) { @@ -1091,6 +1306,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); @@ -1119,9 +1340,57 @@ static int mov_write_rtp_tag(AVIOContext *pb, MOVTrack *track) return update_size(pb, pos); } +static int mov_write_source_reference_tag(AVIOContext *pb, MOVTrack *track, const char *reel_name) +{ + uint64_t str_size =strlen(reel_name); + int64_t pos = avio_tell(pb); + + if (str_size >= UINT16_MAX){ + av_log(NULL, AV_LOG_ERROR, "reel_name length %"PRIu64" is too large\n", str_size); + avio_wb16(pb, 0); + return AVERROR(EINVAL); + } + + avio_wb32(pb, 0); /* size */ + ffio_wfourcc(pb, "name"); /* Data format */ + avio_wb16(pb, str_size); /* string size */ + avio_wb16(pb, track->language); /* langcode */ + avio_write(pb, reel_name, str_size); /* reel name */ + return update_size(pb,pos); +} + 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; + AVDictionaryEntry *t = NULL; + + 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_w8(pb, 0); /* Reserved */ + + if (track->st) + t = av_dict_get(track->st->metadata, "reel_name", NULL, 0); + + if (t && utf8len(t->value)) + mov_write_source_reference_tag(pb, track, t->value); + else + avio_wb16(pb, 0); /* zero size */ +#else avio_wb32(pb, 0); /* size */ ffio_wfourcc(pb, "tmcd"); /* Data format */ @@ -1129,6 +1398,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); } @@ -1159,7 +1429,7 @@ static int mov_write_ctts_tag(AVIOContext *pb, MOVTrack *track) uint32_t atom_size; int i; - ctts_entries = av_malloc((track->entry + 1) * sizeof(*ctts_entries)); /* worst case */ + ctts_entries = av_malloc_array((track->entry + 1), sizeof(*ctts_entries)); /* worst case */ ctts_entries[0].count = 1; ctts_entries[0].duration = track->cluster[0].cts; for (i = 1; i < track->entry; i++) { @@ -1200,7 +1470,7 @@ static int mov_write_stts_tag(AVIOContext *pb, MOVTrack *track) entries = 1; } else { stts_entries = track->entry ? - av_malloc(track->entry * sizeof(*stts_entries)) : /* worst case */ + av_malloc_array(track->entry, sizeof(*stts_entries)) : /* worst case */ NULL; for (i = 0; i < track->entry; i++) { int duration = get_cluster_duration(track, i); @@ -1235,6 +1505,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 */ @@ -1280,9 +1551,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) { - avio_wb32(pb, 0x20); /* size */ + 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) +{ + 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 */ @@ -1293,7 +1587,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) @@ -1339,16 +1662,19 @@ 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('t','x','3','g')) { - hdlr_type = "sbtl"; - } else if (track->tag == MKTAG('m','p','4','s')) { - hdlr_type = "subp"; - } else if (is_clcp_track(track)) { + if (is_clcp_track(track)) { hdlr_type = "clcp"; + descr = "ClosedCaptionHandler"; } else { - hdlr_type = "text"; - } + if (track->tag == MKTAG('t','x','3','g')) { + hdlr_type = "sbtl"; + } else if (track->tag == MKTAG('m','p','4','s')) { + hdlr_type = "subp"; + } else { + hdlr_type = "text"; + } descr = "SubtitleHandler"; + } } else if (track->enc->codec_tag == MKTAG('r','t','p',' ')) { hdlr_type = "hint"; descr = "HintHandler"; @@ -1417,14 +1743,14 @@ static int mov_write_minf_tag(AVIOContext *pb, MOVTrack *track) mov_write_smhd_tag(pb); else if (track->enc->codec_type == AVMEDIA_TYPE_SUBTITLE) { if (track->tag == MKTAG('t','e','x','t') || is_clcp_track(track)) { - mov_write_gmhd_tag(pb); + 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); @@ -1480,6 +1806,24 @@ 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, MOVMuxContext *mov, MOVTrack *track, AVStream *st) { @@ -1487,6 +1831,7 @@ static int mov_write_tkhd_tag(AVIOContext *pb, MOVMuxContext *mov, track->timescale, AV_ROUND_UP); int version = duration < INT32_MAX ? 0 : 1; int flags = MOV_TKHD_FLAG_IN_MOVIE; + int rotation = 0; int group = 0; @@ -1533,16 +1878,19 @@ static int mov_write_tkhd_tag(AVIOContext *pb, MOVMuxContext *mov, 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)) { @@ -1628,6 +1976,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 */ @@ -1740,12 +2092,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, mov, track, st); - if (track->mode == MODE_PSP || track->flags & MOV_TRACK_CTTS || - (track->entry && track->cluster[0].dts) || - is_clcp_track(track)) { - 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); @@ -1756,11 +2104,11 @@ static int mov_write_trak_tag(AVIOContext *pb, MOVMuxContext *mov, if (track->mode == MODE_MOV) { if (track->enc->codec_type == AVMEDIA_TYPE_VIDEO) { 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); } } - if (is_clcp_track(track)) { + if (is_clcp_track(track) && st->sample_aspect_ratio.num) { mov_write_tapt_tag(pb, track); } } @@ -1862,15 +2210,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) */ @@ -1956,28 +2296,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) @@ -1991,7 +2382,8 @@ static int mov_write_ilst_tag(AVIOContext *pb, MOVMuxContext *mov, mov_write_string_metadata(s, pb, "\251wrt", "composer" , 1); mov_write_string_metadata(s, pb, "\251alb", "album" , 1); mov_write_string_metadata(s, pb, "\251day", "date" , 1); - if (!mov_write_string_metadata(s, pb, "\251too", "encoding_tool", 1)) + if (!mov->exact && + !mov_write_string_metadata(s, pb, "\251too", "encoding_tool", 1)) mov_write_string_tag(pb, "\251too", LIBAVFORMAT_IDENT, 0, 1); mov_write_string_metadata(s, pb, "\251cmt", "comment" , 1); mov_write_string_metadata(s, pb, "\251gen", "genre" , 1); @@ -2003,7 +2395,15 @@ 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_int8_metadata (s, pb, "cpil", "compilation", 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); } @@ -2022,6 +2422,26 @@ static int mov_write_meta_tag(AVIOContext *pb, MOVMuxContext *mov, return size; } +static int mov_write_raw_metadata_tag(AVFormatContext *s, AVIOContext *pb, + const char *name, const char *key) +{ + int len; + AVDictionaryEntry *t; + + if (!(t = av_dict_get(s->metadata, key, NULL, 0))) + return 0; + + len = strlen(t->value); + if (len > 0) { + int size = len + 8; + avio_wb32(pb, size); + ffio_wfourcc(pb, name); + avio_write(pb, t->value, len); + return size; + } + return 0; +} + static int ascii_to_wc(AVIOContext *pb, const uint8_t *b) { int val; @@ -2095,9 +2515,6 @@ static int mov_write_udta_tag(AVIOContext *pb, MOVMuxContext *mov, int ret, size; uint8_t *buf; - if (s->flags & AVFMT_FLAG_BITEXACT) - return 0; - ret = avio_open_dyn_buf(&pb_buf); if (ret < 0) return ret; @@ -2118,9 +2535,13 @@ 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); + mov_write_raw_metadata_tag(s, pb_buf, "XMP_", "xmp"); } else { /* iTunes meta data */ mov_write_meta_tag(pb_buf, mov, s); @@ -2154,6 +2575,7 @@ static void mov_write_psp_udta_tag(AVIOContext *pb, static int mov_write_uuidusmt_tag(AVIOContext *pb, AVFormatContext *s) { + MOVMuxContext *mov = s->priv_data; AVDictionaryEntry *title = av_dict_get(s->metadata, "title", NULL, 0); int64_t pos, pos2; @@ -2178,7 +2600,8 @@ static int mov_write_uuidusmt_tag(AVIOContext *pb, AVFormatContext *s) avio_wb16(pb, 0x0); /* ? */ avio_wb16(pb, 0x021C); /* data */ - mov_write_psp_udta_tag(pb, LIBAVCODEC_IDENT, "eng", 0x04); + if (!mov->exact) + mov_write_psp_udta_tag(pb, LIBAVCODEC_IDENT, "eng", 0x04); mov_write_psp_udta_tag(pb, title->value, "eng", 0x01); mov_write_psp_udta_tag(pb, "2006/04/01 11:11:11", "und", 0x03); @@ -2189,6 +2612,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) { @@ -2203,6 +2649,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) @@ -2217,6 +2666,17 @@ 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; + //src_trk may have a different timescale than the tmcd track + mov->tracks[i].track_duration = av_rescale(mov->tracks[src_trk].track_duration, + mov->tracks[i].timescale, + mov->tracks[src_trk].timescale); + } + } mov_write_mvhd_tag(pb, mov); if (mov->mode != MODE_MOV && !mov->iods_skip) @@ -2260,7 +2720,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 }; @@ -2273,7 +2733,8 @@ static int mov_write_isml_manifest(AVIOContext *pb, MOVMuxContext *mov) avio_printf(pb, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"); avio_printf(pb, "<smil xmlns=\"http://www.w3.org/2001/SMIL20/Language\">\n"); avio_printf(pb, "<head>\n"); - avio_printf(pb, "<meta name=\"creator\" content=\"%s\" />\n", + if (!mov->exact) + avio_printf(pb, "<meta name=\"creator\" content=\"%s\" />\n", LIBAVFORMAT_IDENT); avio_printf(pb, "</head>\n"); avio_printf(pb, "<body>\n"); @@ -2461,7 +2922,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 }; @@ -2483,7 +2944,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 }; @@ -2507,8 +2968,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; @@ -2868,10 +3328,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) @@ -2880,12 +3338,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); @@ -2997,6 +3452,21 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt) int size = pkt->size; uint8_t *reformatted_data = NULL; + if (trk->entry) { + int64_t duration = pkt->dts - trk->cluster[trk->entry - 1].dts; + if (duration < 0 || duration > INT_MAX) { + av_log(s, AV_LOG_ERROR, "Application provided duration: %"PRId64" / timestamp: %"PRId64" is out of range for mov/mp4 format\n", + duration, pkt->dts + ); + + pkt->dts = trk->cluster[trk->entry - 1].dts + 1; + pkt->pts = AV_NOPTS_VALUE; + } + if (pkt->duration < 0) { + av_log(s, AV_LOG_ERROR, "Application provided duration: %d is invalid\n", pkt->duration); + return AVERROR(EINVAL); + } + } if (mov->flags & FF_MOV_FLAG_FRAGMENT) { int ret; if (mov->fragments > 0) { @@ -3028,6 +3498,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 @@ -3040,6 +3513,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 the 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 */ @@ -3083,6 +3566,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; @@ -3093,9 +3577,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"); @@ -3130,12 +3618,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; @@ -3145,7 +3629,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); @@ -3160,6 +3644,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); } } @@ -3167,12 +3718,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'); @@ -3181,11 +3732,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) +#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_freep(&buf); + } + } +#endif for (i = 0; i < s->nb_chapters; i++) { AVChapter *c = s->chapters[i]; @@ -3216,6 +3813,46 @@ 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; + + /* set st to src_st for metadata access*/ + track->st = src_st; + + /* 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 @@ -3275,18 +3912,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); @@ -3361,8 +4000,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, ret, hint_track = 0, tmcd_track = 0; mov->fc = s; @@ -3379,6 +4018,9 @@ static int mov_write_header(AVFormatContext *s) else if (!strcmp("f4v", s->oformat->name)) mov->mode = MODE_F4V; } + if (s->flags & AVFMT_FLAG_BITEXACT) + mov->exact = 1; + /* Set the FRAGMENT flag if any of the fragmentation methods are * enabled. */ if (mov->max_fragment_duration || mov->max_fragment_size || @@ -3399,7 +4041,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 = 2; } /* Non-seekable output is ok if using fragmentation. If ism_lookahead @@ -3410,10 +4057,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); } @@ -3436,9 +4093,35 @@ 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)); + mov->tracks = av_mallocz_array((mov->nb_streams + 1), sizeof(*mov->tracks)); if (!mov->tracks) return AVERROR(ENOMEM); @@ -3455,8 +4138,10 @@ static int mov_write_header(AVFormatContext *s) track->mode = mov->mode; track->tag = mov_find_codec_tag(s, track); if (!track->tag) { - av_log(s, AV_LOG_ERROR, "track %d: could not find tag, " - "codec not currently supported in container\n", i); + av_log(s, AV_LOG_ERROR, "Could not find tag for codec %s in stream #%d, " + "codec not currently supported in container\n", + avcodec_get_name(st->codec->codec_id), i); + ret = AVERROR(EINVAL); goto error; } /* If hinting of this track is enabled by a later hint track, @@ -3469,11 +4154,23 @@ static int mov_write_header(AVFormatContext *s) track->tag == MKTAG('m','x','5','p') || track->tag == MKTAG('m','x','5','n')) { if (st->codec->width != 720 || (st->codec->height != 608 && st->codec->height != 512)) { av_log(s, AV_LOG_ERROR, "D-10/IMX must use 720x608 or 720x512 video resolution\n"); + ret = AVERROR(EINVAL); goto error; } track->height = track->tag >> 24 == 'n' ? 486 : 576; } - track->timescale = st->time_base.den; + if (mov->video_track_timescale) { + track->timescale = mov->video_track_timescale; + } else { + track->timescale = st->time_base.den; + while(track->timescale < 10000) + track->timescale *= 2; + } + if (st->codec->width > 65535 || st->codec->height > 65535) { + av_log(s, AV_LOG_ERROR, "Resolution %dx%d too large for mov/mp4\n", st->codec->width, st->codec->height); + ret = AVERROR(EINVAL); + goto error; + } 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" @@ -3481,29 +4178,40 @@ 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); + ret = AVERROR(EINVAL); 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 || + st->codec->codec_id == AV_CODEC_ID_ADPCM_IMA_QT) { track->audio_vbr = 1; } if (track->mode != MODE_MOV && track->enc->codec_id == AV_CODEC_ID_MP3 && track->timescale < 16000) { av_log(s, AV_LOG_ERROR, "track %d: muxing mp3 at %dhz is not supported\n", i, track->enc->sample_rate); + ret = AVERROR(EINVAL); goto error; } } else if (st->codec->codec_type == AVMEDIA_TYPE_SUBTITLE) { track->timescale = st->time_base.den; } else if (st->codec->codec_type == AVMEDIA_TYPE_DATA) { track->timescale = st->time_base.den; + } else { + track->timescale = MOV_TIMESCALE; } if (!track->height) track->height = st->codec->height; @@ -3526,8 +4234,40 @@ static int mov_write_header(AVFormatContext *s) } } + for (i = 0; i < s->nb_streams; i++) { + int j; + AVStream *st= s->streams[i]; + MOVTrack *track= &mov->tracks[i]; + + if (st->codec->codec_type != AVMEDIA_TYPE_AUDIO || + track->enc->channel_layout != AV_CH_LAYOUT_MONO) + continue; + + for (j = 0; j < s->nb_streams; j++) { + AVStream *stj= s->streams[j]; + MOVTrack *trackj= &mov->tracks[j]; + if (j == i) + continue; + + if (stj->codec->codec_type != AVMEDIA_TYPE_AUDIO || + trackj->enc->channel_layout != AV_CH_LAYOUT_MONO || + trackj->language != track->language || + trackj->tag != track->tag + ) + continue; + track->multichannel_as_mono++; + } + } + enable_tracks(s); + + 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 no fragmentation options have been set, set a default. */ if (!(mov->flags & (FF_MOV_FLAG_FRAG_KEYFRAME | @@ -3546,7 +4286,7 @@ static int mov_write_header(AVFormatContext *s) mov->time += 0x7C25B080; // 1970 based -> 1904 based if (mov->chapter_track) - if (mov_create_chapter_track(s, mov->chapter_track) < 0) + if ((ret = mov_create_chapter_track(s, mov->chapter_track)) < 0) goto error; if (mov->flags & FF_MOV_FLAG_RTP_HINT) { @@ -3555,12 +4295,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 ((ret = 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 ((ret = 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) @@ -3574,7 +4333,7 @@ static int mov_write_header(AVFormatContext *s) return 0; error: mov_free(s); - return -1; + return ret; } static int get_moov_size(AVFormatContext *s) @@ -3691,6 +4450,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. @@ -3718,7 +4490,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"); @@ -3727,6 +4499,18 @@ 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"); + ffio_fill(pb, 0, size - 8); + avio_seek(pb, moov_pos, SEEK_SET); } else { mov_write_moov_tag(pb, mov, s); } @@ -3894,7 +4678,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 2f2c09c..9ce4a86 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,18 +82,25 @@ 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 multichannel_as_mono; int vos_len; uint8_t *vos_data; @@ -105,7 +113,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; @@ -141,6 +149,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; @@ -148,6 +157,8 @@ typedef struct MOVMuxContext { int flags; int rtp_flags; + int exact; + int iods_skip; int iods_video_profile; int iods_audio_profile; @@ -159,6 +170,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; char *major_brand; diff --git a/libavformat/movenchint.c b/libavformat/movenchint.c index a8f5f34..006aa09 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 */ @@ -72,7 +72,7 @@ static void sample_queue_pop(HintSampleQueue *queue) if (queue->len <= 0) return; if (queue->samples[0].own_data) - av_free(queue->samples[0].data); + av_freep(&queue->samples[0].data); queue->len--; memmove(queue->samples, queue->samples + 1, sizeof(HintSample)*queue->len); } @@ -85,7 +85,7 @@ static void sample_queue_free(HintSampleQueue *queue) int i; for (i = 0; i < queue->len; i++) if (queue->samples[i].own_data) - av_free(queue->samples[i].data); + av_freep(&queue->samples[i].data); av_freep(&queue->samples); queue->len = 0; queue->size = 0; @@ -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; @@ -421,7 +422,7 @@ int ff_mov_add_hinted_packet(AVFormatContext *s, AVPacket *pkt, sample_queue_push(&trk->sample_queue, pkt->data, pkt->size, sample); /* Feed the packet to the RTP muxer */ - ff_write_chained(rtp_ctx, 0, pkt, s); + ff_write_chained(rtp_ctx, 0, pkt, s, 0); /* Fetch the output from the RTP muxer, open a new output buffer * for next time. */ diff --git a/libavformat/mp3dec.c b/libavformat/mp3dec.c index 14d8254..688f235 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/crc.h" @@ -39,10 +40,15 @@ #define XING_TOC_COUNT 100 -typedef struct MP3DecContext { +typedef struct { + AVClass *class; + int64_t filesize; int xing_toc; + int start_pad; + int end_pad; + int usetoc; unsigned frames; /* Total number of frames in file */ - unsigned size; /* Total number of bytes in the stream */ + unsigned header_filesize; /* Total number of bytes in the stream */ int is_cbr; } MP3DecContext; @@ -53,7 +59,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; @@ -66,6 +72,8 @@ static int mp3_read_probe(AVProbeData *p) for(; buf < end; buf= buf2+1) { buf2 = buf; + if(ff_mpa_check_header(AV_RB32(buf2))) + continue; for(frames = 0; buf2 < end; frames++) { header = AV_RB32(buf2); @@ -80,31 +88,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 && max_frames >= p->buf_size/10000) 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 && max_frames >= p->buf_size/10000) return 1; + else return 0; //mpegps_mp3_unrecognized_format.mpg has max_frames=3 } @@ -112,22 +102,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; } static void mp3_parse_info_tag(AVFormatContext *s, AVStream *st, @@ -145,7 +137,8 @@ static void mp3_parse_info_tag(AVFormatContext *s, AVStream *st, int32_t r_gain = INT32_MIN, a_gain = INT32_MIN; MP3DecContext *mp3 = s->priv_data; - const int64_t xing_offtbl[2][2] = {{32, 17}, {17,9}}; + static const int64_t xing_offtbl[2][2] = {{32, 17}, {17,9}}; + uint64_t fsize = avio_size(s->pb); /* Check for Xing / Info tag */ avio_skip(s->pb, xing_offtbl[c->lsf == 1][c->nb_channels == 1]); @@ -158,14 +151,25 @@ static void mp3_parse_info_tag(AVFormatContext *s, AVStream *st, if (v & XING_FLAG_FRAMES) mp3->frames = avio_rb32(s->pb); if (v & XING_FLAG_SIZE) - mp3->size = avio_rb32(s->pb); - if (v & XING_FLAG_TOC && mp3->frames) - read_xing_toc(s, mp3->size, av_rescale_q(mp3->frames, + mp3->header_filesize = avio_rb32(s->pb); + if (fsize && mp3->header_filesize) { + uint64_t min, delta; + min = FFMIN(fsize, mp3->header_filesize); + delta = FFMAX(fsize, mp3->header_filesize) - min; + if (fsize > mp3->header_filesize && delta > min >> 4) { + mp3->frames = 0; + } else if (delta > min >> 4) { + av_log(s, AV_LOG_WARNING, + "filesize and duration do not match (growing file?)\n"); + } + } + if (v & XING_FLAG_TOC) + read_xing_toc(s, mp3->header_filesize, av_rescale_q(mp3->frames, (AVRational){spf, c->sample_rate}, st->time_base)); - /* VBR quality */ - avio_rb32(s->pb); + if(v & 8) + avio_skip(s->pb, 4); /* Encoder short version string */ memset(version, 0, sizeof(version)); @@ -208,7 +212,19 @@ static void mp3_parse_info_tag(AVFormatContext *s, AVStream *st, avio_r8(s->pb); /* Encoder delays */ - avio_rb24(s->pb); + v= avio_rb24(s->pb); + if(AV_RB32(version) == MKBETAG('L', 'A', 'M', 'E') + || AV_RB32(version) == MKBETAG('L', 'a', 'v', 'f')) { + + mp3->start_pad = v>>12; + mp3-> end_pad = v&4095; + st->skip_samples = mp3->start_pad + 528 + 1; + if (!st->start_time) + st->start_time = av_rescale_q(st->skip_samples, + (AVRational){1, c->sample_rate}, + st->time_base); + av_log(s, AV_LOG_DEBUG, "pad %d %d\n", mp3->start_pad, mp3-> end_pad); + } /* Misc */ avio_r8(s->pb); @@ -248,7 +264,7 @@ static void mp3_parse_vbri_tag(AVFormatContext *s, AVStream *st, int64_t base) if (avio_rb16(s->pb) == 1) { /* skip delay and quality */ avio_skip(s->pb, 4); - mp3->size = avio_rb32(s->pb); + mp3->header_filesize = avio_rb32(s->pb); mp3->frames = avio_rb32(s->pb); } } @@ -278,12 +294,12 @@ static int mp3_parse_vbr_tags(AVFormatContext *s, AVStream *st, int64_t base) spf = c.lsf ? 576 : 1152; /* Samples per frame, layer 3 */ mp3->frames = 0; - mp3->size = 0; + mp3->header_filesize = 0; mp3_parse_info_tag(s, st, &c, spf); mp3_parse_vbri_tag(s, st, base); - if (!mp3->frames && !mp3->size) + if (!mp3->frames && !mp3->header_filesize) return -1; /* Skip the vbr tag frame */ @@ -292,14 +308,15 @@ static int mp3_parse_vbr_tags(AVFormatContext *s, AVStream *st, int64_t base) if (mp3->frames) st->duration = av_rescale_q(mp3->frames, (AVRational){spf, c.sample_rate}, st->time_base); - if (mp3->size && mp3->frames && !mp3->is_cbr) - st->codec->bit_rate = av_rescale(mp3->size, 8 * c.sample_rate, mp3->frames * (int64_t)spf); + if (mp3->header_filesize && mp3->frames && !mp3->is_cbr) + st->codec->bit_rate = av_rescale(mp3->header_filesize, 8 * c.sample_rate, mp3->frames * (int64_t)spf); return 0; } static int mp3_read_header(AVFormatContext *s) { + MP3DecContext *mp3 = s->priv_data; AVStream *st; int64_t off; int ret; @@ -310,17 +327,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); @@ -336,15 +357,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; @@ -354,38 +386,98 @@ 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 + && mp3->frames) { + 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]; + if (dir < 0) + avio_seek(s->pb, FFMAX(ie->pos - 4096, 0), SEEK_SET); 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)"), @@ -396,4 +488,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 476d7f7..7e6b2e1 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); @@ -111,22 +112,23 @@ 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; + int32_t header; MPADecodeHeader mpah; int srate_idx, i, channels; int bitrate_idx; - int best_bitrate_idx; + int best_bitrate_idx = -1; int best_bitrate_error = INT_MAX; int xing_offset; int ver = 0; - int lsf, bytes_needed; + int bytes_needed; + const char *vendor = (s->flags & AVFMT_FLAG_BITEXACT) ? "Lavf" : LIBAVFORMAT_IDENT; if (!s->pb->seekable || !mp3->write_xing) - 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]; @@ -140,9 +142,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) { @@ -150,41 +151,43 @@ 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; } /* 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 |= (srate_idx << 2) << 8; header |= channels << 6; - lsf = !((header & (1 << 20) && header & (1 << 19))); - - xing_offset = xing_offtbl[ver != 3][channels == 1]; - bytes_needed = 4 // header - + xing_offset - + 4 // xing tag - + 4 // frames/size/toc flags - + 4 // frames - + 4 // size - + XING_TOC_SIZE; // toc - for (bitrate_idx = 1; bitrate_idx < 15; bitrate_idx++) { - int bit_rate = 1000 * avpriv_mpa_bitrate_tab[lsf][3 - 1][bitrate_idx]; + int bit_rate = 1000 * avpriv_mpa_bitrate_tab[ver != 3][3 - 1][bitrate_idx]; int error = FFABS(bit_rate - codec->bit_rate); - if (error < best_bitrate_error){ + 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 < 15; bitrate_idx++) { + for (bitrate_idx = best_bitrate_idx; ; bitrate_idx++) { int32_t mask = bitrate_idx << (4 + 8); + if (15 == bitrate_idx) + return -1; header |= mask; avpriv_mpegaudio_decode_header(&mpah, header); + xing_offset=xing_offtbl[mpah.lsf == 1][mpah.nb_channels == 1]; + bytes_needed = 4 // header + + xing_offset + + 4 // xing tag + + 4 // frames/size/toc flags + + 4 // frames + + 4 // size + + XING_TOC_SIZE // toc + + 24 + ; if (bytes_needed <= mpah.frame_size) break; @@ -194,26 +197,32 @@ static void mp3_write_xing(AVFormatContext *s) avio_wb32(s->pb, header); - avpriv_mpegaudio_decode_header(&mpah, header); - - av_assert0(mpah.frame_size >= XING_MAX_SIZE); - 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->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)); + + 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, mpah.frame_size - bytes_needed); + + return 0; } /* @@ -234,7 +243,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; @@ -250,20 +259,43 @@ static int mp3_write_audio_packet(AVFormatContext *s, AVPacket *pkt) { MP3Context *mp3 = s->priv_data; - if (mp3->xing_offset && pkt->size >= 4) { - MPADecodeHeader c; + if (pkt->data && pkt->size >= 4) { + MPADecodeHeader mpah; + int av_unused base; uint32_t h; h = AV_RB32(pkt->data); if (ff_mpa_check_header(h) == 0) { - avpriv_mpegaudio_decode_header(&c, h); + avpriv_mpegaudio_decode_header(&mpah, h); if (!mp3->initial_bitrate) - mp3->initial_bitrate = c.bit_rate; - if ((c.bit_rate == 0) || (mp3->initial_bitrate != c.bit_rate)) + mp3->initial_bitrate = mpah.bit_rate; + if ((mpah.bit_rate == 0) || (mp3->initial_bitrate != mpah.bit_rate)) mp3->has_variable_bitrate = 1; + } else { + av_log(s, AV_LOG_WARNING, "Audio packet of size %d (starting with %08X...) " + "is invalid, writing it anyway.\n", pkt->size, h); + } + +#ifdef FILTER_VBR_HEADERS + /* filter out XING and INFO headers. */ + base = 4 + xing_offtbl[mpah.lsf == 1][mpah.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; } - mp3_xing_add_frame(mp3, pkt); + /* 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); @@ -275,7 +307,7 @@ static int mp3_queue_flush(AVFormatContext *s) AVPacketList *pktl; int ret = 0, write = 1; - ff_id3v2_finish(&mp3->id3, s->pb); + ff_id3v2_finish(&mp3->id3, s->pb, s->metadata_header_padding); mp3_write_xing(s); while ((pktl = mp3->queue)) { @@ -337,11 +369,22 @@ 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", .long_name = NULL_IF_CONFIG_SMALL("MP2 (MPEG audio layer 2)"), - .mime_type = "audio/x-mpeg", + .mime_type = "audio/mpeg", .extensions = "mp2,m2a,mpa", .audio_codec = AV_CODEC_ID_MP2, .video_codec = AV_CODEC_ID_NONE, @@ -377,14 +420,18 @@ static int mp3_write_packet(AVFormatContext *s, AVPacket *pkt) if (mp3->pics_to_write) { /* buffer audio packets until we get all the pictures */ AVPacketList *pktl = av_mallocz(sizeof(*pktl)); - if (!pktl) - return AVERROR(ENOMEM); + int ret; + if (!pktl) { + av_log(s, AV_LOG_WARNING, "Not enough memory to buffer audio. Skipping picture streams\n"); + mp3->pics_to_write = 0; + mp3_queue_flush(s); + return mp3_write_audio_packet(s, pkt); + } - pktl->pkt = *pkt; - pktl->pkt.buf = av_buffer_ref(pkt->buf); - if (!pktl->pkt.buf) { + ret = av_copy_packet(&pktl->pkt, pkt); + if (ret < 0) { av_freep(&pktl); - return AVERROR(ENOMEM); + return ret; } if (mp3->queue_end) @@ -473,7 +520,7 @@ static int mp3_write_header(struct AVFormatContext *s) if (!mp3->pics_to_write) { if (mp3->id3v2_version) - ff_id3v2_finish(&mp3->id3, s->pb); + ff_id3v2_finish(&mp3->id3, s->pb, s->metadata_header_padding); mp3_write_xing(s); } @@ -483,7 +530,7 @@ static int mp3_write_header(struct AVFormatContext *s) AVOutputFormat ff_mp3_muxer = { .name = "mp3", .long_name = NULL_IF_CONFIG_SMALL("MP3 (MPEG audio layer 3)"), - .mime_type = "audio/x-mpeg", + .mime_type = "audio/mpeg", .extensions = "mp3", .priv_data_size = sizeof(MP3Context), .audio_codec = AV_CODEC_ID_MP3, @@ -491,6 +538,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 5e771ae..8f8ac9c 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,9 +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); - avio_read(s->pb, st->codec->extradata, 16); + if (ff_get_extradata(st->codec, s->pb, 16) < 0) + return AVERROR(ENOMEM); st->codec->sample_rate = mpc_rate[st->codec->extradata[2] & 3]; avpriv_set_pts_info(st, 32, MPC_FRAMESIZE, st->codec->sample_rate); /* scan for seekpoints */ @@ -153,7 +152,7 @@ static int mpc_read_packet(AVFormatContext *s, AVPacket *pkt) } c->curbits = (curbits + size2) & 0x1F; - if ((ret = av_new_packet(pkt, size)) < 0) + if ((ret = av_new_packet(pkt, size + 4)) < 0) return ret; pkt->data[0] = curbits; @@ -196,11 +195,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 3564e81..a15dc25 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) @@ -136,7 +136,7 @@ static void mpc8_parse_seektable(AVFormatContext *s, int64_t off) int tag; int64_t size, pos, ppos[2]; uint8_t *buf; - int i, t, seekd; + int i, t, seekd, ret; GetBitContext gb; if (s->nb_streams == 0) { @@ -150,13 +150,20 @@ 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; } if(!(buf = av_malloc(size + FF_INPUT_BUFFER_PADDING_SIZE))) return; - avio_read(s->pb, buf, size); + ret = avio_read(s->pb, buf, size); + if (ret != size) { + av_log(s, AV_LOG_ERROR, "seek table truncated\n"); + av_free(buf); + return; + } + memset(buf+size, 0, FF_INPUT_BUFFER_PADDING_SIZE); + init_get_bits(&gb, buf, size * 8); size = gb_get_v(&gb); if(size > UINT_MAX/4 || size > c->samples/1152){ @@ -213,7 +220,7 @@ static int mpc8_read_header(AVFormatContext *s) return AVERROR_INVALIDDATA; } - while(!pb->eof_reached){ + while(!avio_feof(pb)){ pos = avio_tell(pb); mpc8_get_chunk_header(pb, &tag, &size); if(tag == TAG_STREAMHDR) @@ -241,9 +248,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); - avio_read(pb, st->codec->extradata, st->codec->extradata_size); + if (ff_get_extradata(st->codec, pb, 2) < 0) + return AVERROR(ENOMEM); st->codec->channels = (st->codec->extradata[1] >> 4) + 1; st->codec->sample_rate = mpc8_rate[st->codec->extradata[0] >> 5]; @@ -251,6 +257,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 +275,7 @@ static int mpc8_read_packet(AVFormatContext *s, AVPacket *pkt) int tag; int64_t pos, size; - while(!s->pb->eof_reached){ + while(!avio_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 7c87517..056db2e 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 && @@ -80,37 +86,33 @@ static int mpegps_probe(AVProbeData *p) vid++; // skip pes payload to avoid start code emulation for private // 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 & 0xf0) == VIDEO_ID && !pes) - invalid++; - else if ((code & 0xe0) == AUDIO_ID && !pes) - invalid++; - else if (code == PRIVATE_STREAM_1 && !pes) - invalid++; + 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++; + else if (code == PRIVATE_STREAM_1 && !pes) invalid++; } } - 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; } @@ -118,25 +120,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; @@ -161,7 +168,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 (avio_feof(pb)) break; v = avio_r8(pb); n--; @@ -198,6 +205,8 @@ static long mpegps_psm_parse(MpegDemuxContext *m, AVIOContext *pb) /* skip program_stream_info */ avio_skip(pb, ps_info_length); es_map_length = avio_rb16(pb); + /* Ignore es_map_length, trust psm_length */ + es_map_length = psm_length - ps_info_length - 10; /* at least one es available? */ while (es_map_length >= 4) { @@ -237,7 +246,7 @@ redo: 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 (avio_feof(s->pb)) return AVERROR_EOF; // FIXME we should remember header_state return AVERROR(EAGAIN); @@ -252,21 +261,82 @@ redo: 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); @@ -276,7 +346,9 @@ redo: /* 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; @@ -284,6 +356,8 @@ redo: len = avio_rb16(s->pb); pts = dts = AV_NOPTS_VALUE; + if (startcode != PRIVATE_STREAM_2) + { /* stuffing */ for (;;) { if (len < 1) @@ -358,22 +432,11 @@ redo: avio_skip(s->pb, header_len); } 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; @@ -401,20 +464,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 suppress 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 */ @@ -425,7 +498,6 @@ redo: } 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; @@ -448,9 +520,9 @@ redo: } 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]; @@ -460,11 +532,20 @@ redo: 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; + if (m->sofdec > 0) { + codec_id = AV_CODEC_ID_ADPCM_ADX; + // Auto-detect AC-3 + request_probe = 50; + } else { + codec_id = AV_CODEC_ID_MP2; + } } else if (startcode >= 0x80 && startcode <= 0x87) { type = AVMEDIA_TYPE_AUDIO; codec_id = AV_CODEC_ID_AC3; @@ -475,7 +556,11 @@ redo: 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; @@ -489,23 +574,6 @@ redo: } 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 */ @@ -519,11 +587,25 @@ skip: 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; @@ -575,3 +657,329 @@ 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," +#define MAX_LINE_SIZE 2048 + +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; + int stream_id = -1; + char id[64] = {0}; + char alt[MAX_LINE_SIZE] = {0}; + + 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 (!avio_feof(s->pb)) { + char line[MAX_LINE_SIZE]; + int len = ff_get_line(s->pb, line, sizeof(line)); + + if (!len) + break; + + line[strcspn(line, "\r\n")] = 0; + + if (!strncmp(line, "id:", 3)) { + if (sscanf(line, "id: %63[^,], index: %u", id, &stream_id) != 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; + } + + header_parsed = 1; + alt[0] = '\0'; + /* We do not create the stream immediately to avoid adding empty + * streams. See the following timestamp entry. */ + + av_log(s, AV_LOG_DEBUG, "IDX stream[%d] id=%s\n", stream_id, id); + + } else if (!strncmp(line, "timestamp:", 10)) { + AVPacket *sub; + int hh, mm, ss, ms; + int64_t pos, timestamp; + const char *p = line + 10; + + if (stream_id == -1) { + av_log(s, AV_LOG_ERROR, "Timestamp declared before any stream\n"); + ret = AVERROR_INVALIDDATA; + goto end; + } + + if (!st || st->id != stream_id) { + 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); + if (alt[0]) + av_dict_set(&st->metadata, "title", alt, 0); + } + + 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); + ret = AVERROR_INVALIDDATA; + goto end; + } + timestamp = (hh*3600LL + mm*60LL + ss) * 1000LL + ms + delay; + timestamp = av_rescale_q(timestamp, av_make_q(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 (!strncmp(line, "alt:", 4)) { + const char *p = line + 4; + + while (*p == ' ') + p++; + av_log(s, AV_LOG_DEBUG, "IDX stream[%d] name=%s\n", st->id, p); + av_strlcpy(alt, p, sizeof(alt)); + 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; +} + +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; + av_assert0(tmpq->nb_subs); + 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; + goto fail; + } + 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) + goto fail; + + 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 d0b36ee..b43517c 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 */ @@ -42,7 +42,7 @@ #define VIDEO_ID 0xe0 #define H264_ID 0xe2 #define AC3_ID 0x80 -#define DTS_ID 0x8a +#define DTS_ID 0x88 #define LPCM_ID 0xa0 #define SUB_ID 0x20 @@ -58,7 +58,6 @@ #define STREAM_TYPE_VIDEO_CAVS 0x42 #define STREAM_TYPE_AUDIO_AC3 0x81 -#define STREAM_TYPE_AUDIO_DTS 0x8a static const int lpcm_freq_tab[4] = { 48000, 96000, 44100, 32000 }; diff --git a/libavformat/mpegenc.c b/libavformat/mpegenc.c index fed03ed..d3af9e1 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 */ @@ -70,6 +70,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; @@ -269,8 +270,7 @@ static int put_system_header(AVFormatContext *ctx, uint8_t *buf, 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; } @@ -325,7 +325,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; @@ -352,6 +352,15 @@ static av_cold int mpeg_mux_init(AVFormatContext *ctx) switch (st->codec->codec_type) { case AVMEDIA_TYPE_AUDIO: + if (!s->is_mpeg2 && + (st->codec->codec_id == AV_CODEC_ID_AC3 || + st->codec->codec_id == AV_CODEC_ID_DTS || + st->codec->codec_id == AV_CODEC_ID_PCM_S16BE)) + av_log(ctx, AV_LOG_WARNING, + "%s in MPEG-1 system streams is not widely supported, " + "consider using the vob or the dvd muxer " + "to force a MPEG-2 program stream.\n", + avcodec_get_name(st->codec->codec_id)); if (st->codec->codec_id == AV_CODEC_ID_AC3) { stream->id = ac3_id++; } else if (st->codec->codec_id == AV_CODEC_ID_DTS) { @@ -388,10 +397,16 @@ static av_cold int mpeg_mux_init(AVFormatContext *ctx) stream->max_buffer_size = 6 * 1024 + st->codec->rc_buffer_size / 8; else { av_log(ctx, AV_LOG_WARNING, - "VBV buffer size not set, muxing may fail\n"); + "VBV buffer size not set, using default size of 130KB\n" + "If you want the mpeg file to be compliant to some specification\n" + "Like DVD, VCD or others, make sure you set the correct buffer size\n"); // FIXME: this is probably too small as default stream->max_buffer_size = 230 * 1024; } + 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: @@ -430,12 +445,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) { @@ -494,7 +515,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: @@ -921,7 +942,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; } @@ -953,7 +974,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, @@ -963,14 +984,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; @@ -1085,14 +1108,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); @@ -1154,7 +1185,7 @@ static int mpeg_mux_end(AVFormatContext *ctx) stream = ctx->streams[i]->priv_data; assert(av_fifo_size(stream->fifo) == 0); - av_fifo_free(stream->fifo); + av_fifo_freep(&stream->fifo); } return 0; } @@ -1162,7 +1193,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, (1 << 22) - 1, 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 dced537..1e18813 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 "avformat.h" @@ -54,6 +55,7 @@ enum MpegTSFilterType { MPEGTS_PES, MPEGTS_SECTION, + MPEGTS_PCR, }; typedef struct MpegTSFilter MpegTSFilter; @@ -84,6 +86,7 @@ struct MpegTSFilter { int pid; int es_id; int last_cc; /* last cc code (-1 if first packet) */ + int64_t last_pcr; enum MpegTSFilterType type; union { MpegTSPESFilter pes_filter; @@ -96,6 +99,9 @@ struct Program { unsigned int id; // program id/service id unsigned int nb_pids; unsigned int pids[MAX_PIDS_PER_PROGRAM]; + + /** have we found pmt for this program */ + int pmt_found; }; struct MpegTSContext { @@ -105,9 +111,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; @@ -115,6 +123,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,6 +137,9 @@ struct MpegTSContext { /** to detect seek */ int64_t last_pos; + int skip_changes; + int skip_clear; + /******************************************/ /* private mpegts data */ /* scan context */ @@ -133,11 +147,13 @@ 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 }, @@ -151,7 +167,26 @@ 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 }, + {"ts_packetsize", "Output option carrying the raw packet size.", offsetof(MpegTSContext, raw_packet_size), AV_OPT_TYPE_INT, + {.i64 = 0}, 0, 0, AV_OPT_FLAG_DECODING_PARAM | AV_OPT_FLAG_EXPORT | AV_OPT_FLAG_READONLY }, + {"skip_changes", "Skip changing / adding streams / programs.", offsetof(MpegTSContext, skip_changes), AV_OPT_TYPE_INT, + {.i64 = 0}, 0, 1, 0 }, + {"skip_clear", "Skip clearing programs.", offsetof(MpegTSContext, skip_clear), AV_OPT_TYPE_INT, + {.i64 = 0}, 0, 1, 0 }, + { NULL }, +}; + +static const AVClass mpegts_class = { + .class_name = "mpegts demuxer", + .item_name = av_default_item_name, + .option = mpegts_options, .version = LIBAVUTIL_VERSION_INT, }; @@ -194,13 +229,42 @@ typedef struct PESContext { extern AVInputFormat ff_mpegts_demuxer; +static struct Program * get_program(MpegTSContext *ts, unsigned int programid) +{ + int i; + for (i = 0; i < ts->nb_prg; i++) { + if (ts->prg[i].id == programid) { + return &ts->prg[i]; + } + } + return NULL; +} + +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) + if (ts->prg[i].id == programid) { ts->prg[i].nb_pids = 0; + ts->prg[i].pmt_found = 0; + } } static void clear_programs(MpegTSContext *ts) @@ -219,20 +283,14 @@ static void add_pat_entry(MpegTSContext *ts, unsigned int programid) p = &ts->prg[ts->nb_prg]; p->id = programid; p->nb_pids = 0; + p->pmt_found = 0; ts->nb_prg++; } static void add_pid_to_pmt(MpegTSContext *ts, unsigned int programid, unsigned int pid) { - int i; - struct Program *p = NULL; - for (i = 0; i < ts->nb_prg; i++) { - if (ts->prg[i].id == programid) { - p = &ts->prg[i]; - break; - } - } + struct Program *p = get_program(ts, programid); if (!p) return; @@ -241,6 +299,26 @@ static void add_pid_to_pmt(MpegTSContext *ts, unsigned int programid, p->pids[p->nb_pids++] = pid; } +static void set_pmt_found(MpegTSContext *ts, unsigned int programid) +{ + struct Program *p = get_program(ts, programid); + if (!p) + return; + + p->pmt_found = 1; +} + +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 @@ -318,22 +396,27 @@ static void write_section_data(MpegTSContext *ts, 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); } } -static MpegTSFilter *mpegts_open_section_filter(MpegTSContext *ts, - unsigned int pid, - SectionCallback *section_cb, - void *opaque, - int check_crc) +static MpegTSFilter *mpegts_open_filter(MpegTSContext *ts, unsigned int pid, + enum MpegTSFilterType type) { MpegTSFilter *filter; - MpegTSSectionFilter *sec; av_dlog(ts->stream, "Filter: pid=0x%x\n", pid); @@ -344,11 +427,26 @@ static MpegTSFilter *mpegts_open_section_filter(MpegTSContext *ts, return NULL; ts->pids[pid] = filter; - filter->type = MPEGTS_SECTION; + filter->type = type; filter->pid = pid; filter->es_id = -1; filter->last_cc = -1; + filter->last_pcr= -1; + + return filter; +} +static MpegTSFilter *mpegts_open_section_filter(MpegTSContext *ts, + unsigned int pid, + SectionCallback *section_cb, + void *opaque, + int check_crc) +{ + MpegTSFilter *filter; + MpegTSSectionFilter *sec; + + if (!(filter = mpegts_open_filter(ts, pid, MPEGTS_SECTION))) + return NULL; sec = &filter->u.section_filter; sec->section_cb = section_cb; sec->opaque = opaque; @@ -368,24 +466,20 @@ static MpegTSFilter *mpegts_open_pes_filter(MpegTSContext *ts, unsigned int pid, MpegTSFilter *filter; MpegTSPESFilter *pes; - if (pid >= NB_PID_MAX || ts->pids[pid]) - return NULL; - filter = av_mallocz(sizeof(MpegTSFilter)); - if (!filter) + if (!(filter = mpegts_open_filter(ts, pid, MPEGTS_PES))) return NULL; - ts->pids[pid] = filter; - filter->type = MPEGTS_PES; - filter->pid = pid; - filter->es_id = -1; - filter->last_cc = -1; - pes = &filter->u.pes_filter; pes->pes_cb = pes_cb; pes->opaque = opaque; return filter; } +static MpegTSFilter *mpegts_open_pcr_filter(MpegTSContext *ts, unsigned int pid) +{ + return mpegts_open_filter(ts, pid, MPEGTS_PCR); +} + static void mpegts_close_filter(MpegTSContext *ts, MpegTSFilter *filter) { int pid; @@ -411,27 +505,26 @@ static int analyze(const uint8_t *buf, int size, int packet_size, int *index) { int stat[TS_MAX_PACKET_SIZE]; int i; - int x = 0; int best_score = 0; + int best_score2 = 0; - memset(stat, 0, packet_size * sizeof(int)); + memset(stat, 0, packet_size * sizeof(*stat)); - for (x = i = 0; i < size - 3; i++) { - if (buf[i] == 0x47 && !(buf[i + 1] & 0x80) && (buf[i + 3] & 0x30)) { + for (i = 0; i < size - 3; i++) { + if (buf[i] == 0x47 && !(buf[i + 1] & 0x80) && buf[i + 3] != 0x47) { + int x = i % packet_size; stat[x]++; if (stat[x] > best_score) { best_score = stat[x]; if (index) *index = x; + } else if (stat[x] > best_score2) { + best_score2 = stat[x]; } } - - x++; - if (x == packet_size) - x = 0; } - return best_score; + return best_score - best_score2; } /* autodetect fec presence. Must have at least 1024 bytes */ @@ -558,7 +651,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 }, @@ -575,6 +672,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 }, }; @@ -594,10 +693,17 @@ static const StreamType REGD_types[] = { { 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 }, + { MKTAG('I','D','3',' '), AVMEDIA_TYPE_DATA, AV_CODEC_ID_TIMED_ID3 }, + { 0 }, +}; + /* descriptor present */ static const StreamType DESC_types[] = { { 0x6a, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AC3 }, /* AC-3 descriptor */ @@ -612,10 +718,16 @@ 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; } } @@ -623,6 +735,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; @@ -638,7 +758,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) { @@ -668,10 +789,23 @@ 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; } +static void reset_pes_packet_state(PESContext *pes) +{ + pes->pts = AV_NOPTS_VALUE; + pes->dts = AV_NOPTS_VALUE; + pes->data_index = 0; + pes->flags = 0; + av_buffer_unref(&pes->buffer); +} + static void new_pes_packet(PESContext *pes, AVPacket *pkt) { av_init_packet(pkt); @@ -699,12 +833,15 @@ static void new_pes_packet(PESContext *pes, AVPacket *pkt) pkt->pos = pes->ts_packet_pos; pkt->flags = pes->flags; - /* reset pts values */ - pes->pts = AV_NOPTS_VALUE; - pes->dts = AV_NOPTS_VALUE; - pes->buffer = NULL; - pes->data_index = 0; - pes->flags = 0; + pes->buffer = NULL; + reset_pes_packet_state(pes); +} + +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, @@ -715,6 +852,7 @@ static int read_sl_header(PESContext *pes, SLConfigDescr *sl, 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) @@ -753,9 +891,9 @@ static int read_sl_header(PESContext *pes, SLConfigDescr *sl, 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) @@ -790,9 +928,10 @@ static int mpegts_push_data(MpegTSFilter *filter, if (pes->state == MPEGTS_PAYLOAD && pes->data_index > 0) { new_pes_packet(pes, ts->pkt); ts->stop_parse = 1; + } else { + reset_pes_packet_state(pes); } pes->state = MPEGTS_HEADER; - pes->data_index = 0; pes->ts_packet_pos = pos; } p = buf; @@ -824,6 +963,9 @@ static int mpegts_push_data(MpegTSFilter *filter, /* stream not present in PMT */ if (!pes->st) { + if (ts->skip_changes) + goto skip; + pes->st = avformat_new_stream(ts->stream, NULL); if (!pes->st) return AVERROR(ENOMEM); @@ -848,12 +990,12 @@ 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; @@ -937,10 +1079,54 @@ skip: 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) { + AVStream *st = NULL; + if (f->type == MPEGTS_PES) { + PESContext *pcrpes = f->u.pes_filter.opaque; + if (pcrpes) + st = pcrpes->st; + } else if (f->type == MPEGTS_PCR) { + int i; + for (i = 0; i < p->nb_stream_indexes; i++) { + AVStream *pst = pes->stream->streams[p->stream_index[i]]; + if (pst->codec->codec_type == AVMEDIA_TYPE_VIDEO) + st = pst; + } + } + if (f->last_pcr != -1 && st && 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 = f->last_pcr / 300; + pes->st->pts_wrap_reference = st->pts_wrap_reference; + pes->st->pts_wrap_behavior = 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: - if (buf_size > 0 && pes->buffer) { + if (pes->buffer) { if (pes->data_index > 0 && pes->data_index + buf_size > pes->total_size) { new_pes_packet(pes, ts->pkt); @@ -958,18 +1144,18 @@ skip: } memcpy(pes->buffer->data + pes->data_index, p, buf_size); pes->data_index += buf_size; + /* emit complete packets with known packet size + * decreases demuxer delay for infrequent packets like subtitles from + * a couple of seconds to milliseconds for properly muxed files. + * total_size is the number of bytes following pes_packet_length + * in the pes header, i.e. not counting the first PES_START_SIZE bytes */ + if (!ts->stop_parse && pes->total_size < MAX_PES_PAYLOAD && + pes->pes_header_size + pes->data_index == pes->total_size + PES_START_SIZE) { + ts->stop_parse = 1; + new_pes_packet(pes, ts->pkt); + } } buf_size = 0; - /* emit complete packets with known packet size - * decreases demuxer delay for infrequent packets like subtitles from - * a couple of seconds to milliseconds for properly muxed files. - * total_size is the number of bytes following pes_packet_length - * in the pes header, i.e. not counting the first PES_START_SIZE bytes */ - if (!ts->stop_parse && pes->total_size < MAX_PES_PAYLOAD && - pes->pes_header_size + pes->data_index == pes->total_size + PES_START_SIZE) { - ts->stop_parse = 1; - new_pes_packet(pes, ts->pkt); - } break; case MPEGTS_SKIP: buf_size = 0; @@ -1013,6 +1199,7 @@ typedef struct { int descr_count; int max_descr_count; int level; + int predefined_SLConfigDescriptor_seen; } MP4DescrParseContext; static int init_MP4DescrParseContext(MP4DescrParseContext *d, AVFormatContext *s, @@ -1136,6 +1323,11 @@ static int parse_MP4SLDescrTag(MP4DescrParseContext *d, int64_t off, int len) descr->sl.timestamp_res = avio_rb32(&d->pb); avio_rb32(&d->pb); descr->sl.timestamp_len = avio_r8(&d->pb); + if (descr->sl.timestamp_len > 64) { + avpriv_request_sample(NULL, "timestamp_len > 64"); + descr->sl.timestamp_len = 64; + return AVERROR_PATCHWELCOME; + } descr->sl.ocr_len = avio_r8(&d->pb); descr->sl.au_len = avio_r8(&d->pb); descr->sl.inst_bitrate_len = avio_r8(&d->pb); @@ -1143,8 +1335,9 @@ static int parse_MP4SLDescrTag(MP4DescrParseContext *d, int64_t off, int len) descr->sl.degr_prior_len = lengths >> 12; descr->sl.au_seq_num_len = (lengths >> 7) & 0x1f; descr->sl.packet_seq_num_len = (lengths >> 2) & 0x1f; - } else { + } else if (!d->predefined_SLConfigDescriptor_seen){ avpriv_report_missing_feature(d->s, "Predefined SLConfigDescriptor"); + d->predefined_SLConfigDescriptor_seen = 1; } return 0; } @@ -1260,7 +1453,7 @@ static void m4sl_cb(MpegTSFilter *filter, const uint8_t *section, AVStream *st; if (ts->pids[pid]->es_id != mp4_descr[i].es_id) continue; - if (!(ts->pids[pid] && ts->pids[pid]->type == MPEGTS_PES)) { + if (ts->pids[pid]->type != MPEGTS_PES) { av_log(s, AV_LOG_ERROR, "pid %x is not PES\n", pid); continue; } @@ -1318,7 +1511,7 @@ int ff_parse_mpeg2_descriptor(AVFormatContext *fc, AVStream *st, int stream_type av_dlog(fc, "tag: 0x%02x len=%d\n", desc_tag, desc_len); - if (st->codec->codec_id == AV_CODEC_ID_NONE && + if ((st->codec->codec_id == AV_CODEC_ID_NONE || st->request_probe > 0) && stream_type == STREAM_TYPE_PRIVATE_DATA) mpegts_find_stream_type(st, desc_tag, DESC_types); @@ -1345,7 +1538,7 @@ int ff_parse_mpeg2_descriptor(AVFormatContext *fc, AVStream *st, int stream_type case 0x1F: /* FMC descriptor */ get16(pp, desc_end); if (mp4_descr_count > 0 && - st->codec->codec_id == AV_CODEC_ID_AAC_LATM && + (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, @@ -1353,46 +1546,118 @@ int ff_parse_mpeg2_descriptor(AVFormatContext *fc, AVStream *st, int stream_type 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 */ - language[0] = get8(pp, desc_end); - language[1] = get8(pp, desc_end); - language[2] = get8(pp, desc_end); - language[3] = 0; - av_dict_set(&st->metadata, "language", language, 0); + { + uint8_t *extradata = NULL; + int language_count = desc_len / 5; + + if (desc_len > 0 && desc_len % 5 != 0) + return AVERROR_INVALIDDATA; + + if (language_count > 0) { + /* 4 bytes per language code (3 bytes) with comma or NUL byte should fit language buffer */ + if (language_count > sizeof(language) / 4) { + language_count = sizeof(language) / 4; + } + + if (st->codec->extradata == NULL) { + if (ff_alloc_extradata(st->codec, language_count * 2)) { + return AVERROR(ENOMEM); + } + } + + if (st->codec->extradata_size < language_count * 2) + return AVERROR_INVALIDDATA; + + extradata = st->codec->extradata; + + for (i = 0; i < language_count; i++) { + language[i * 4 + 0] = get8(pp, desc_end); + language[i * 4 + 1] = get8(pp, desc_end); + language[i * 4 + 2] = get8(pp, desc_end); + language[i * 4 + 3] = ','; + + memcpy(extradata, *pp, 2); + extradata += 2; + + *pp += 2; + } + + language[i * 4 - 1] = 0; + av_dict_set(&st->metadata, "language", language, 0); + } + } break; case 0x59: /* subtitling descriptor */ - language[0] = get8(pp, desc_end); - language[1] = get8(pp, desc_end); - language[2] = get8(pp, desc_end); - language[3] = 0; - /* hearing impaired subtitles detection */ - switch (get8(pp, desc_end)) { - case 0x20: /* DVB subtitles (for the hard of hearing) with no monitor aspect ratio criticality */ - case 0x21: /* DVB subtitles (for the hard of hearing) for display on 4:3 aspect ratio monitor */ - case 0x22: /* DVB subtitles (for the hard of hearing) for display on 16:9 aspect ratio monitor */ - case 0x23: /* DVB subtitles (for the hard of hearing) for display on 2.21:1 aspect ratio monitor */ - case 0x24: /* DVB subtitles (for the hard of hearing) for display on a high definition monitor */ - case 0x25: /* DVB subtitles (for the hard of hearing) with plano-stereoscopic disparity for display on a high definition monitor */ - st->disposition |= AV_DISPOSITION_HEARING_IMPAIRED; - break; - } - if (st->codec->extradata) { - 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; - memcpy(st->codec->extradata, *pp, 4); + { + /* 8 bytes per DVB subtitle substream data: + * ISO_639_language_code (3 bytes), + * subtitling_type (1 byte), + * composition_page_id (2 bytes), + * ancillary_page_id (2 bytes) */ + int language_count = desc_len / 8; + + if (desc_len > 0 && desc_len % 8 != 0) + return AVERROR_INVALIDDATA; + + if (language_count > 1) { + avpriv_request_sample(fc, "DVB subtitles with multiple languages"); + } + + if (language_count > 0) { + uint8_t *extradata; + + /* 4 bytes per language code (3 bytes) with comma or NUL byte should fit language buffer */ + if (language_count > sizeof(language) / 4) { + language_count = sizeof(language) / 4; + } + + if (st->codec->extradata == NULL) { + if (ff_alloc_extradata(st->codec, language_count * 5)) { + return AVERROR(ENOMEM); + } + } + + if (st->codec->extradata_size < language_count * 5) + return AVERROR_INVALIDDATA; + + extradata = st->codec->extradata; + + for (i = 0; i < language_count; i++) { + language[i * 4 + 0] = get8(pp, desc_end); + language[i * 4 + 1] = get8(pp, desc_end); + language[i * 4 + 2] = get8(pp, desc_end); + language[i * 4 + 3] = ','; + + /* hearing impaired subtitles detection using subtitling_type */ + switch (*pp[0]) { + case 0x20: /* DVB subtitles (for the hard of hearing) with no monitor aspect ratio criticality */ + case 0x21: /* DVB subtitles (for the hard of hearing) for display on 4:3 aspect ratio monitor */ + case 0x22: /* DVB subtitles (for the hard of hearing) for display on 16:9 aspect ratio monitor */ + case 0x23: /* DVB subtitles (for the hard of hearing) for display on 2.21:1 aspect ratio monitor */ + case 0x24: /* DVB subtitles (for the hard of hearing) for display on a high definition monitor */ + case 0x25: /* DVB subtitles (for the hard of hearing) with plano-stereoscopic disparity for display on a high definition monitor */ + st->disposition |= AV_DISPOSITION_HEARING_IMPAIRED; + break; + } + + extradata[4] = get8(pp, desc_end); /* subtitling_type */ + memcpy(extradata, *pp, 4); /* composition_page_id and ancillary_page_id */ + extradata += 5; + + *pp += 4; + } + + language[i * 4 - 1] = 0; + av_dict_set(&st->metadata, "language", language, 0); } } - *pp += 4; - av_dict_set(&st->metadata, "language", language, 0); break; case 0x0a: /* ISO 639 language descriptor */ for (i = 0; i + 4 <= desc_len; i += 4) { @@ -1423,6 +1688,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; } @@ -1458,6 +1735,8 @@ static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len if (h->tid != PMT_TID) return; + if (ts->skip_changes) + return; clear_program(ts, h->id); pcr_pid = get16(&p, p_end); @@ -1465,6 +1744,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); @@ -1501,7 +1781,9 @@ 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; + + set_pmt_found(ts, h->id); for (;;) { @@ -1512,14 +1794,18 @@ static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len break; pid = get16(&p, p_end); if (pid < 0) - break; + goto out; pid &= 0x1fff; + if (pid == ts->current_pid) + goto out; /* 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; @@ -1529,6 +1815,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 { @@ -1537,6 +1825,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; } @@ -1554,11 +1844,11 @@ static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len desc_list_len = get16(&p, p_end); if (desc_list_len < 0) - break; + goto out; desc_list_len &= 0xfff; desc_list_end = p + desc_list_len; if (desc_list_end > p_end) - break; + goto out; for (;;) { if (ff_parse_mpeg2_descriptor(ts->stream, st, stream_type, &p, desc_list_end, mp4_descr, @@ -1575,6 +1865,9 @@ static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len p = desc_list_end; } + if (!ts->pids[pcr_pid]) + mpegts_open_pcr_filter(ts, pcr_pid); + out: for (i = 0; i < mp4_descr_count; i++) av_free(mp4_descr[i].dec_config_descr); @@ -1586,6 +1879,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); @@ -1596,6 +1890,10 @@ static void pat_cb(MpegTSFilter *filter, const uint8_t *section, int section_len return; if (h->tid != PAT_TID) return; + if (ts->skip_changes) + return; + + ts->stream->ts_id = h->id; clear_programs(ts); for (;;) { @@ -1607,20 +1905,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 && !ts->skip_clear) + clear_avprogram(ts, ts->stream->programs[j]->id); + } + } } static void sdt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len) @@ -1640,6 +1960,8 @@ static void sdt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len return; if (h->tid != SDT_TID) return; + if (ts->skip_changes) + return; onid = get16(&p, p_end); if (onid < 0) return; @@ -1701,6 +2023,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) { @@ -1721,6 +2046,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 */ @@ -1741,7 +2067,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) { @@ -1750,20 +2076,25 @@ static int handle_packet(MpegTSContext *ts, const uint8_t *packet) } } - if (!has_payload) - return 0; p = packet + 4; if (has_adaptation) { + int64_t pcr_h; + int pcr_l; + if (parse_pcr(&pcr_h, &pcr_l, packet) == 0) + tss->last_pcr = pcr_h * 300 + pcr_l; /* skip adaptation field */ p += p[0] + 1; } /* if past the end of packet, ignore */ p_end = packet + TS_PACKET_SIZE; - if (p >= p_end) + if (p >= p_end || !has_payload) 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) { @@ -1790,17 +2121,69 @@ static int handle_packet(MpegTSContext *ts, const uint8_t *packet) p, p_end - p, 0); } } + + // stop find_stream_info from waiting for more streams + // when all programs have received a PMT + if (ts->stream->ctx_flags & AVFMTCTX_NOHEADER) { + int i; + for (i = 0; i < ts->nb_prg; i++) { + if (!ts->prg[i].pmt_found) + break; + } + if (i == ts->nb_prg && ts->nb_prg > 0) { + if (ts->stream->nb_streams > 1 || pos > 100000) { + av_log(ts->stream, AV_LOG_DEBUG, "All programs have pmt, headers found\n"); + ts->stream->ctx_flags &= ~AVFMTCTX_NOHEADER; + } + } + } + } else { int ret; // 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) - return ret; + if (tss->type == MPEGTS_PES) { + if ((ret = tss->u.pes_filter.pes_cb(tss, p, p_end - p, is_start, + pos - ts->raw_packet_size)) < 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) @@ -1810,10 +2193,11 @@ static int mpegts_resync(AVFormatContext *s) for (i = 0; i < MAX_RESYNC_SIZE; i++) { c = avio_r8(pb); - if (pb->eof_reached) + if (avio_feof(pb)) return AVERROR_EOF; if (c == 0x47) { avio_seek(pb, -1, SEEK_CUR); + reanalyze(s->priv_data); return 0; } } @@ -1837,7 +2221,9 @@ static int read_packet(AVFormatContext *s, uint8_t *buf, int raw_packet_size, /* check packet sync byte */ if ((*data)[0] != 0x47) { /* find a new packet start */ - avio_seek(pb, -TS_PACKET_SIZE, SEEK_CUR); + uint64_t pos = avio_tell(pb); + avio_seek(pb, -FFMIN(raw_packet_size, pos), SEEK_CUR); + if (mpegts_resync(s) < 0) return AVERROR(EAGAIN); else @@ -1857,12 +2243,13 @@ static void finished_reading_packet(AVFormatContext *s, int raw_packet_size) avio_skip(pb, skip); } -static int handle_packets(MpegTSContext *ts, int nb_packets) +static int handle_packets(MpegTSContext *ts, int64_t nb_packets) { AVFormatContext *s = ts->stream; uint8_t packet[TS_PACKET_SIZE + FF_INPUT_BUFFER_PADDING_SIZE]; const uint8_t *data; - int packet_num, ret = 0; + int64_t packet_num; + int ret = 0; if (avio_tell(s->pb) != ts->last_pos) { int i; @@ -1877,6 +2264,7 @@ static int handle_packets(MpegTSContext *ts, int nb_packets) pes->state = MPEGTS_SKIP; /* skip until pes header */ } ts->pids[i]->last_cc = -1; + ts->pids[i]->last_pcr = -1; } } } @@ -1885,11 +2273,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; @@ -1905,30 +2297,33 @@ 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 AVERROR_INVALIDDATA; - 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; + 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 AVERROR_INVALIDDATA; } @@ -1961,24 +2356,33 @@ static int parse_pcr(int64_t *ppcr_high, int *ppcr_low, const uint8_t *packet) 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; + int64_t pos, probesize = s->probesize ? s->probesize : s->probesize2; + + ffio_ensure_seekback(pb, probesize); - /* read the first 1024 bytes to get packet size */ + /* read the first 8192 bytes to get packet size */ pos = avio_tell(pb); len = avio_read(pb, buf, sizeof(buf)); - if (len < 0) - return len; - if (len != sizeof(buf)) - return AVERROR_BUG; - ts->raw_packet_size = get_packet_size(buf, sizeof(buf)); - if (ts->raw_packet_size <= 0) - return AVERROR_INVALIDDATA; + 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; @@ -1986,14 +2390,13 @@ 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); mpegts_open_section_filter(ts, PAT_PID, pat_cb, ts, 1); - handle_packets(ts, s->probesize / ts->raw_packet_size); + handle_packets(ts, probesize / ts->raw_packet_size); /* if could not find service, enable auto_guess */ ts->auto_guess = 1; @@ -2053,7 +2456,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; } @@ -2116,6 +2519,7 @@ static int mpegts_read_packet(AVFormatContext *s, AVPacket *pkt) 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) { @@ -2152,7 +2556,7 @@ 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; @@ -2160,74 +2564,69 @@ static int64_t mpegts_get_pcr(AVFormatContext *s, int stream_index, 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; + int pos47 = ts->pos47_full % ts->raw_packet_size; 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) + ((*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; - int ret; - - ret = ff_seek_frame_binary(s, stream_index, target_ts, flags); - if (ret < 0) - return ret; - - pos = avio_tell(s->pb); - - for (;;) { - avio_seek(s->pb, pos, SEEK_SET); - ret = avio_read(s->pb, buf, 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; + 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 ret; - if (ret != TS_PACKET_SIZE) - return AVERROR_EOF; - // pid = AV_RB16(buf + 1) & 0x1fff; - if (buf[1] & 0x40) - break; - pos += ts->raw_packet_size; + 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; } /**************************************************************/ /* parsing functions - called from other demuxers such as RTP */ -MpegTSContext *ff_mpegts_parse_open(AVFormatContext *s) +MpegTSContext *avpriv_mpegts_parse_open(AVFormatContext *s) { MpegTSContext *ts; @@ -2238,22 +2637,23 @@ 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; } /* return the consumed length if a packet was output, or -1 if no * packet is output */ -int ff_mpegts_parse_packet(MpegTSContext *ts, AVPacket *pkt, - const uint8_t *buf, int len) +int avpriv_mpegts_parse_packet(MpegTSContext *ts, AVPacket *pkt, + const uint8_t *buf, int len) { int len1; 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 AVERROR_INVALIDDATA; if (buf[0] != 0x47) { @@ -2263,12 +2663,14 @@ 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; } -void ff_mpegts_parse_close(MpegTSContext *ts) +void avpriv_mpegts_parse_close(MpegTSContext *ts) { mpegts_free(ts); av_free(ts); @@ -2282,9 +2684,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 = { @@ -2294,8 +2696,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 7dbbf8d..84f3098 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 */ @@ -58,14 +58,15 @@ #define STREAM_TYPE_VIDEO_DIRAC 0xd1 #define STREAM_TYPE_AUDIO_AC3 0x81 -#define STREAM_TYPE_AUDIO_DTS 0x8a +#define STREAM_TYPE_AUDIO_DTS 0x82 +#define STREAM_TYPE_AUDIO_TRUEHD 0x83 typedef struct MpegTSContext MpegTSContext; -MpegTSContext *ff_mpegts_parse_open(AVFormatContext *s); -int ff_mpegts_parse_packet(MpegTSContext *ts, AVPacket *pkt, - const uint8_t *buf, int len); -void ff_mpegts_parse_close(MpegTSContext *ts); +MpegTSContext *avpriv_mpegts_parse_open(AVFormatContext *s); +int avpriv_mpegts_parse_packet(MpegTSContext *ts, AVPacket *pkt, + const uint8_t *buf, int len); +void avpriv_mpegts_parse_close(MpegTSContext *ts); typedef struct SLConfigDescr { int use_au_start; @@ -105,4 +106,10 @@ int ff_parse_mpeg2_descriptor(AVFormatContext *fc, AVStream *st, int stream_type Mp4Descr *mp4_descr, int mp4_descr_count, int pid, MpegTSContext *ts); +/** + * Check presence of H264 startcode + * @return <0 to stop processing + */ +int ff_check_h264_startcode(AVFormatContext *s, const AVStream *st, const AVPacket *pkt); + #endif /* AVFORMAT_MPEGTS_H */ diff --git a/libavformat/mpegtsenc.c b/libavformat/mpegtsenc.c index 7621103..0184d87 100644 --- a/libavformat/mpegtsenc.c +++ b/libavformat/mpegtsenc.c @@ -2,23 +2,24 @@ * 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 */ +#include "libavutil/avassert.h" #include "libavutil/bswap.h" #include "libavutil/crc.h" #include "libavutil/dict.h" @@ -78,6 +79,7 @@ typedef struct MpegTSWrite { int pmt_start_pid; int start_pid; + int m2ts_mode; int reemit_pat_pmt; // backward compatibility @@ -85,6 +87,10 @@ typedef struct MpegTSWrite { #define MPEGTS_FLAG_REEMIT_PAT_PMT 0x01 #define MPEGTS_FLAG_AAC_LATM 0x02 int flags; + int copyts; + int tables_version; + + int omit_video_pes_length; } MpegTSWrite; /* a PES packet header is generated every DEFAULT_PES_HEADER_FREQ packets */ @@ -183,7 +189,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 */ @@ -197,6 +203,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; @@ -218,11 +225,11 @@ 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); } -static void mpegts_write_pmt(AVFormatContext *s, MpegTSService *service) +static int mpegts_write_pmt(AVFormatContext *s, MpegTSService *service) { MpegTSWrite *ts = s->priv_data; uint8_t data[SECTION_LENGTH], *q, *desc_length_ptr, *program_info_length_ptr; @@ -245,7 +252,7 @@ static void mpegts_write_pmt(AVFormatContext *s, MpegTSService *service) MpegTSWriteStream *ts_st = st->priv_data; AVDictionaryEntry *lang = av_dict_get(st->metadata, "language", NULL, 0); - if (q - data > SECTION_LENGTH - 3 - 2 - 6) { + if (q - data > SECTION_LENGTH - 32) { err = 1; break; } @@ -284,10 +291,17 @@ static void mpegts_write_pmt(AVFormatContext *s, MpegTSService *service) case AV_CODEC_ID_AC3: stream_type = STREAM_TYPE_AUDIO_AC3; break; + case AV_CODEC_ID_DTS: + stream_type = STREAM_TYPE_AUDIO_DTS; + break; + case AV_CODEC_ID_TRUEHD: + stream_type = STREAM_TYPE_AUDIO_TRUEHD; + break; default: stream_type = STREAM_TYPE_PRIVATE_DATA; break; } + *q++ = stream_type; put16(&q, 0xe000 | ts_st->pid); desc_length_ptr = q; @@ -296,6 +310,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; @@ -336,26 +364,82 @@ static void mpegts_write_pmt(AVFormatContext *s, MpegTSService *service) break; case AVMEDIA_TYPE_SUBTITLE: { - const char *language; - language = lang && strlen(lang->value) == 3 ? lang->value : "eng"; - *q++ = 0x59; - *q++ = 8; - *q++ = language[0]; - *q++ = language[1]; - *q++ = language[2]; - *q++ = 0x10; /* normal subtitles (0x20 = if hearing pb) */ - - if (q - data > SECTION_LENGTH - 4) { - err = 1; - break; - } - - if (st->codec->extradata_size == 4) { - memcpy(q, st->codec->extradata, 4); - q += 4; - } else { - put16(&q, 1); /* page id */ - put16(&q, 1); /* ancillary page id */ + const char default_language[] = "und"; + const char *language = lang && strlen(lang->value) >= 3 ? lang->value : default_language; + + if (st->codec->codec_id == AV_CODEC_ID_DVB_SUBTITLE) { + uint8_t *len_ptr; + int extradata_copied = 0; + + *q++ = 0x59; /* subtitling_descriptor */ + len_ptr = q++; + + while (strlen(language) >= 3) { + if (sizeof(data) - (q - data) < 8) { /* 8 bytes per DVB subtitle substream data */ + err = 1; + break; + } + *q++ = *language++; + *q++ = *language++; + *q++ = *language++; + /* Skip comma */ + if (*language != '\0') + language++; + + if (st->codec->extradata_size - extradata_copied >= 5) { + *q++ = st->codec->extradata[extradata_copied + 4]; /* subtitling_type */ + memcpy(q, st->codec->extradata + extradata_copied, 4); /* composition_page_id and ancillary_page_id */ + extradata_copied += 5; + q += 4; + } else { + /* subtitling_type: + * 0x10 - normal with no monitor aspect ratio criticality + * 0x20 - for the hard of hearing with no monitor aspect ratio criticality */ + *q++ = (st->disposition & AV_DISPOSITION_HEARING_IMPAIRED) ? 0x20 : 0x10; + if ((st->codec->extradata_size == 4) && (extradata_copied == 0)) { + /* support of old 4-byte extradata format */ + memcpy(q, st->codec->extradata, 4); /* composition_page_id and ancillary_page_id */ + extradata_copied += 4; + q += 4; + } else { + put16(&q, 1); /* composition_page_id */ + put16(&q, 1); /* ancillary_page_id */ + } + } + } + + *len_ptr = q - len_ptr - 1; + } else if (st->codec->codec_id == AV_CODEC_ID_DVB_TELETEXT) { + uint8_t *len_ptr = NULL; + int extradata_copied = 0; + + /* The descriptor tag. teletext_descriptor */ + *q++ = 0x56; + len_ptr = q++; + + while (strlen(language) >= 3 && q - data < sizeof(data) - 6) { + *q++ = *language++; + *q++ = *language++; + *q++ = *language++; + /* Skip comma */ + if (*language != '\0') + language++; + + if (st->codec->extradata_size - 1 > extradata_copied) { + memcpy(q, st->codec->extradata + extradata_copied, 2); + extradata_copied += 2; + q += 2; + } else { + /* The Teletext descriptor: + * teletext_type: This 5-bit field indicates the type of Teletext page indicated. (0x01 Initial Teletext page) + * teletext_magazine_number: This is a 3-bit field which identifies the magazine number. + * teletext_page_number: This is an 8-bit field giving two 4-bit hex digits identifying the page number. */ + *q++ = 0x08; + *q++ = 0x00; + } + } + + *len_ptr = q - len_ptr - 1; } } break; @@ -369,6 +453,16 @@ 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); @@ -382,11 +476,12 @@ static void mpegts_write_pmt(AVFormatContext *s, MpegTSService *service) "Try reducing the number of languages in the audio streams " "or the total number of streams.\n", i); - 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); + return 0; } -/* NOTE: str == NULL is accepted for an empty string */ +/* NOTE: !str is accepted for an empty string */ static void putstr8(uint8_t **q_ptr, const char *str) { uint8_t *q; @@ -437,7 +532,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); } @@ -455,19 +550,41 @@ static MpegTSService *mpegts_add_service(MpegTSWrite *ts, int sid, service->pcr_pid = 0x1fff; service->provider_name = av_strdup(provider_name); service->name = av_strdup(name); - if (!service->provider_name || !service->name) { - av_free(service->provider_name); - av_free(service->name); - av_free(service); - return NULL; - } - dynarray_add(&ts->services, &ts->nb_services, service); + if (!service->provider_name || !service->name) + goto fail; + if (av_dynarray_add_nofree(&ts->services, &ts->nb_services, service) < 0) + goto fail; + return service; +fail: + av_freep(&service->provider_name); + av_freep(&service->name); + av_free(service); + return NULL; +} + +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); } @@ -521,10 +638,10 @@ static int mpegts_write_header(AVFormatContext *s) ts->sdt.write_packet = section_write_packet; ts->sdt.opaque = s; - pids = av_malloc(s->nb_streams * sizeof(*pids)); + pids = av_malloc_array(s->nb_streams, sizeof(*pids)); if (!pids) { - av_free(service); - return AVERROR(ENOMEM); + ret = AVERROR(ENOMEM); + goto fail; } /* assign pids to each stream */ @@ -610,7 +727,7 @@ static int mpegts_write_header(AVFormatContext *s) } } - av_free(pids); + av_freep(&pids); /* if no video stream, use the first stream as PCR */ if (service->pcr_pid == 0x1fff && s->nb_streams > 0) { @@ -628,9 +745,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) { @@ -648,6 +766,8 @@ static int mpegts_write_header(AVFormatContext *s) service->pcr_packet_period = ts_st->user_tb.den / (10 * ts_st->user_tb.num); } + if (!service->pcr_packet_period) + service->pcr_packet_period = 1; } // output a PCR as soon as possible @@ -664,13 +784,18 @@ static int mpegts_write_header(AVFormatContext *s) service->pcr_packet_period, ts->sdt_packet_period, ts->pat_packet_period); - avio_flush(s->pb); + if (ts->m2ts_mode == -1) { + if (av_match_ext(s->filename, "m2ts")) { + ts->m2ts_mode = 1; + } else { + ts->m2ts_mode = 0; + } + } return 0; fail: - av_free(service); - av_free(pids); + av_freep(&pids); for (i = 0; i < s->nb_streams; i++) { st = s->streams[i]; ts_st = st->priv_data; @@ -683,11 +808,19 @@ fail: } av_freep(&st->priv_data); } + + for (i = 0; i < ts->nb_services; i++) { + service = ts->services[i]; + av_freep(&service->provider_name); + av_freep(&service->name); + av_free(service); + } + av_free(ts->services); return ret; } /* 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; @@ -696,7 +829,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++) @@ -704,12 +837,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; @@ -736,6 +863,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); } @@ -761,6 +889,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); } @@ -782,7 +911,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 @@ -798,7 +927,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; } @@ -823,14 +952,16 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st, MpegTSWrite *ts = s->priv_data; uint8_t buf[TS_PACKET_SIZE]; uint8_t *q; - int val, is_start, len, header_len, write_pcr, private_code, flags; + int val, is_start, len, header_len, write_pcr, is_dvb_subtitle, is_dvb_teletext, flags; 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) { @@ -886,11 +1017,13 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st, } if (is_start) { int pes_extension = 0; + int pes_header_stuffing_bytes = 0; /* write PES header */ *q++ = 0x00; *q++ = 0x00; *q++ = 0x01; - private_code = 0; + is_dvb_subtitle = 0; + is_dvb_teletext = 0; if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) { if (st->codec->codec_id == AV_CODEC_ID_DIRAC) *q++ = 0xfd; @@ -901,10 +1034,19 @@ 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) - private_code = 0x20; + if (st->codec->codec_type == AVMEDIA_TYPE_SUBTITLE) { + if (st->codec->codec_id == AV_CODEC_ID_DVB_SUBTITLE) { + is_dvb_subtitle = 1; + } else if (st->codec->codec_id == AV_CODEC_ID_DVB_TELETEXT) { + is_dvb_teletext = 1; + } + } } header_len = 0; flags = 0; @@ -927,16 +1069,37 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st, * one byte for extension id */ 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; + } + if (is_dvb_teletext) { + pes_header_stuffing_bytes = 0x24 - header_len; + header_len = 0x24; + } len = payload_size + header_len + 3; - if (private_code != 0) - len++; + /* 3 extra bytes should be added to DVB subtitle payload: 0x20 0x00 at the beginning and trailing 0xff */ + if (is_dvb_subtitle) { + len += 3; + payload_size++; + } if (len > 0xffff) len = 0; + if (ts->omit_video_pes_length && st->codec->codec_type == AVMEDIA_TYPE_VIDEO) { + len = 0; + } *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; @@ -957,8 +1120,28 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st, * write the extended stream ID. */ *q++ = 0x00 | 0x60; } - if (private_code != 0) - *q++ = private_code; + /* 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 (is_dvb_subtitle) { + /* First two fields of DVB subtitles PES data: + * data_identifier: for DVB subtitle streams shall be coded with the value 0x20 + * subtitle_stream_id: for DVB subtitle stream shall be identified by the value 0x00 */ + *q++ = 0x20; + *q++ = 0x00; + } + if (is_dvb_teletext) { + memset(q, 0xff, pes_header_stuffing_bytes); + q += pes_header_stuffing_bytes; + } is_start = 0; } /* header size */ @@ -989,12 +1172,46 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st, } } } - memcpy(buf + TS_PACKET_SIZE - len, payload, len); + + if (is_dvb_subtitle && payload_size == len) { + memcpy(buf + TS_PACKET_SIZE - len, payload, len - 1); + buf[TS_PACKET_SIZE - 1] = 0xff; /* end_of_PES_data_field_marker: an 8-bit field with fixed contents 0xff for DVB subtitle */ + } else { + 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; +} + +int ff_check_h264_startcode(AVFormatContext *s, const AVStream *st, const AVPacket *pkt) +{ + 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 the video bitstream filter 'h264_mp4toannexb' to fix it " + "('-bsf:v h264_mp4toannexb' option with ffmpeg)\n"); + return AVERROR_INVALIDDATA; + } + av_log(s, AV_LOG_WARNING, "H.264 bitstream error, startcode missing\n"); + } + return 0; +} + +static int check_hevc_startcode(AVFormatContext *s, const AVStream *st, const AVPacket *pkt) +{ + if (pkt->size < 5 || AV_RB32(pkt->data) != 0x0000001) { + if (!st->nb_frames) { + av_log(s, AV_LOG_ERROR, "HEVC bitstream malformed, no startcode found\n"); + return AVERROR_PATCHWELCOME; + } + av_log(s, AV_LOG_WARNING, "HEVC bitstream error, startcode missing\n"); + } + return 0; } static int mpegts_write_packet_internal(AVFormatContext *s, AVPacket *pkt) @@ -1005,8 +1222,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, @@ -1021,13 +1238,15 @@ 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"); + av_log(s, AV_LOG_ERROR, "first pts value must be set\n"); return AVERROR_INVALIDDATA; } ts_st->first_pts_check = 0; @@ -1035,12 +1254,9 @@ static int mpegts_write_packet_internal(AVFormatContext *s, AVPacket *pkt) if (st->codec->codec_id == AV_CODEC_ID_H264) { const uint8_t *p = buf, *buf_end = p + size; uint32_t state = -1; - - if (pkt->size < 5 || AV_RB32(pkt->data) != 0x0000001) { - av_log(s, AV_LOG_ERROR, "H.264 bitstream malformed, " - "no startcode found, use -bsf h264_mp4toannexb\n"); - return AVERROR_INVALIDDATA; - } + int ret = ff_check_h264_startcode(s, st, pkt); + if (ret < 0) + return ret; do { p = avpriv_find_start_code(p, buf_end, &state); @@ -1077,6 +1293,8 @@ static int mpegts_write_packet_internal(AVFormatContext *s, AVPacket *pkt) av_init_packet(&pkt2); pkt2.data = pkt->data; pkt2.size = pkt->size; + av_assert0(pkt->dts != AV_NOPTS_VALUE); + pkt2.dts = av_rescale_q(pkt->dts, st->time_base, ts_st->amux->streams[0]->time_base); ret = avio_open_dyn_buf(&ts_st->amux->pb); if (ret < 0) @@ -1093,9 +1311,36 @@ static int mpegts_write_packet_internal(AVFormatContext *s, AVPacket *pkt) ts_st->amux->pb = NULL; buf = data; } + } else if (st->codec->codec_id == AV_CODEC_ID_HEVC) { + int ret = check_hevc_startcode(s, st, pkt); + if (ret < 0) + return ret; + } + + 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) { + 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); @@ -1103,21 +1348,6 @@ static int mpegts_write_packet_internal(AVFormatContext *s, AVPacket *pkt) 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; @@ -1147,7 +1377,6 @@ static void mpegts_write_flush(AVFormatContext *s) ts_st->payload_size = 0; } } - avio_flush(s->pb); } static int mpegts_write_packet(AVFormatContext *s, AVPacket *pkt) @@ -1201,10 +1430,13 @@ static const AVOption options[] = { { .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 }, + { .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 }, @@ -1224,6 +1456,15 @@ static const AVOption options[] = { { "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 }, + { "omit_video_pes_length", "Omit the PES packet length for video packets", + offsetof(MpegTSWrite, omit_video_pes_length), AV_OPT_TYPE_INT, + { .i64 = 1 }, 0, 1, AV_OPT_FLAG_ENCODING_PARAM }, { "pcr_period", "PCR retransmission time", offsetof(MpegTSWrite, pcr_period), AV_OPT_TYPE_INT, { .i64 = PCR_RETRANS_TIME }, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM }, @@ -1240,8 +1481,8 @@ static const AVClass mpegts_muxer_class = { 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", + .mime_type = "video/MP2T", + .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 96b95a5..ade76d8 100644 --- a/libavformat/mpegvideodec.c +++ b/libavformat/mpegvideodec.c @@ -3,26 +3,28 @@ * 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 */ #include "avformat.h" #include "rawdec.h" +#include "libavutil/intreadwrite.h" + #define SEQ_START_CODE 0x000001b3 #define GOP_START_CODE 0x000001b8 #define PICTURE_START_CODE 0x00000100 @@ -34,24 +36,53 @@ static int mpegvideo_probe(AVProbeData *p) { uint32_t code= -1; - int pic=0, seq=0, slice=0, pspack=0, pes=0; - int i; + int pic=0, seq=0, slice=0, pspack=0, vpes=0, apes=0, res=0, sicle=0; + int i, j; + uint32_t last = 0; for(i=0; i<p->buf_size; i++){ code = (code<<8) + p->buf[i]; if ((code & 0xffffff00) == 0x100) { switch(code){ - case SEQ_START_CODE: seq++; break; + case SEQ_START_CODE: + if (!(p->buf[i+1+3+1+2] & 0x20)) + break; + j = i; + if (p->buf[j+8] & 2) + j+= 64; + if (j >= p->buf_size) + break; + if (p->buf[j+8] & 1) + j+= 64; + if (j >= p->buf_size) + break; + if (AV_RB24(p->buf + j + 9) & 0xFFFFFE) + break; + 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 >= 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) pes++; - else if((code & 0x1e0) == AUDIO_ID) pes++; + 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 9e21099..a6e2a89 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 0000000..260b7be --- /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 (!avio_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 0000000..eddc594 --- /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 (!avio_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 b6d30fe..60225af 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 && !avio_feof(pb)); - if(pb->eof_reached) { - av_log(ctx, AV_LOG_ERROR, "Could not find valid start."); + if(avio_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 5a7f047..1d5f266 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 */ @@ -32,7 +32,8 @@ #define MTV_ASUBCHUNK_DATA_SIZE 500 #define MTV_HEADER_SIZE 512 #define MTV_AUDIO_PADDING_SIZE 12 -#define AUDIO_SAMPLING_RATE 44100 +#define MTV_IMAGE_DEFAULT_BPP 16 +#define MTV_AUDIO_SAMPLING_RATE 44100 typedef struct MTVDemuxContext { @@ -42,20 +43,30 @@ 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; static int mtv_probe(AVProbeData *p) { + /* we need at least 57 bytes from the header + * to try parsing all required fields + */ + if (p->buf_size < 57) + return 0; + /* Magic is 'AMV' */ if (*p->buf != 'A' || *(p->buf + 1) != 'M' || *(p->buf + 2) != 'V') return 0; + /* Audio magic is always MP3 */ + if (p->buf[43] != 'M' || p->buf[44] != 'P' || p->buf[45] != '3') + 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]))) return 0; @@ -69,8 +80,20 @@ static int mtv_probe(AVProbeData *p) return 0; } - if(p->buf[51] != 16) - return AVPROBE_SCORE_EXTENSION / 2; // But we are going to assume 16bpp anyway .. + /* Image bpp is not an absolutely required + * field as we latter claim it should be 16 + * no matter what. All samples in the wild + * are RGB565/555. + */ + if(p->buf[51] != MTV_IMAGE_DEFAULT_BPP) + return AVPROBE_SCORE_EXTENSION / 2; + + /* We had enough data to parse header values + * but we expect to be able to get 512 bytes + * of header to be sure. + */ + if (p->buf_size < MTV_HEADER_SIZE) + return AVPROBE_SCORE_EXTENSION; return AVPROBE_SCORE_MAX; } @@ -89,21 +112,35 @@ static int mtv_read_header(AVFormatContext *s) mtv->audio_identifier = avio_rl24(pb); mtv->audio_br = avio_rl16(pb); mtv->img_colorfmt = avio_rl24(pb); - mtv->img_bpp = avio_r8(pb); + mtv->img_bpp = avio_r8(pb)>>3; mtv->img_width = avio_rl16(pb); mtv->img_height = avio_rl16(pb); mtv->img_segment_size = avio_rl16(pb); + /* Assume 16bpp even if claimed otherwise. + * We know its going to be RGBG565/555 anyway + */ + if (mtv->img_bpp != MTV_IMAGE_DEFAULT_BPP) { + av_log (s, AV_LOG_WARNING, "Header claims %dbpp (!= 16). Ignoring\n", + mtv->img_bpp); + mtv->img_bpp = MTV_IMAGE_DEFAULT_BPP; + } + /* Calculate width and height if missing from header */ - if(!mtv->img_width) - mtv->img_width=mtv->img_segment_size / (mtv->img_bpp>>3) + if(!mtv->img_width && mtv->img_height) + mtv->img_width=mtv->img_segment_size / (mtv->img_bpp) / mtv->img_height; - if(!mtv->img_height) - mtv->img_height=mtv->img_segment_size / (mtv->img_bpp>>3) + if(!mtv->img_height && mtv->img_width) + mtv->img_height=mtv->img_segment_size / (mtv->img_bpp) / 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); @@ -142,7 +179,7 @@ static int mtv_read_header(AVFormatContext *s) if(!st) return AVERROR(ENOMEM); - avpriv_set_pts_info(st, 64, 1, AUDIO_SAMPLING_RATE); + avpriv_set_pts_info(st, 64, 1, MTV_AUDIO_SAMPLING_RATE); st->codec->codec_type = AVMEDIA_TYPE_AUDIO; st->codec->codec_id = AV_CODEC_ID_MP3; st->codec->bit_rate = mtv->audio_br; diff --git a/libavformat/mux.c b/libavformat/mux.c index df4f57a..55add43 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,15 +49,144 @@ /** * @file - * muxing functions for use within Libav + * muxing functions for use within libavformat */ +/* fraction handling */ + +/** + * f = val + (num / den) + 0.5. + * + * 'num' is normalized so that it is such as 0 <= num < den. + * + * @param f fractional number + * @param val integer value + * @param num must be >= 0 + * @param den must be >= 1 + */ +static void frac_init(AVFrac *f, int64_t val, int64_t num, int64_t den) +{ + num += (den >> 1); + if (num >= den) { + val += num / den; + num = num % den; + } + f->val = val; + f->num = num; + f->den = den; +} + +/** + * Fractional addition to f: f = f + (incr / f->den). + * + * @param f fractional number + * @param incr increment, can be positive or negative + */ +static void frac_add(AVFrac *f, int64_t incr) +{ + int64_t num, den; + + num = f->num + incr; + den = f->den; + if (num < 0) { + f->val += num / den; + num = num % den; + if (num < 0) { + num += den; + f->val--; + } + } else if (num >= den) { + f->val += num / den; + num = num % den; + } + f->num = num; +} + +AVRational ff_choose_timebase(AVFormatContext *s, AVStream *st, int min_precision) +{ + AVRational q; + int j; + + q = st->time_base; + + for (j=2; j<14; j+= 1+(j>2)) + while (q.den / q.num < min_precision && q.num % j == 0) + q.num /= j; + while (q.den / q.num < min_precision && 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; int n; enum AVCodecID id = AV_CODEC_ID_NONE; - unsigned int tag = 0; + int64_t tag = -1; /** * Check that tag + id is in the table @@ -79,7 +209,7 @@ static int validate_codec_tag(AVFormatContext *s, AVStream *st) } if (id != AV_CODEC_ID_NONE) return 0; - if (tag && (st->codec->strict_std_compliance >= FF_COMPLIANCE_NORMAL)) + if (tag >= 0 && (st->codec->strict_std_compliance >= FF_COMPLIANCE_NORMAL)) return 0; return 1; } @@ -92,12 +222,16 @@ static int init_muxer(AVFormatContext *s, AVDictionary **options) AVDictionary *tmp = NULL; AVCodecContext *codec = NULL; AVOutputFormat *of = s->oformat; + AVDictionaryEntry *e; if (options) av_dict_copy(&tmp, *options, 0); 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_dict2(s->priv_data, &tmp, AV_OPT_SEARCH_CHILDREN)) < 0) + goto fail; #if FF_API_LAVF_BITEXACT if (s->nb_streams && s->streams[0]->codec->flags & CODEC_FLAG_BITEXACT) @@ -106,7 +240,7 @@ static int init_muxer(AVFormatContext *s, AVDictionary **options) // some sanity checks if (s->nb_streams == 0 && !(of->flags & AVFMT_NOSTREAMS)) { - av_log(s, AV_LOG_ERROR, "no streams\n"); + av_log(s, AV_LOG_ERROR, "No streams to mux were specified\n"); ret = AVERROR(EINVAL); goto fail; } @@ -152,18 +286,18 @@ FF_ENABLE_DEPRECATION_WARNINGS 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.num != 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; } @@ -172,21 +306,23 @@ FF_ENABLE_DEPRECATION_WARNINGS } 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; } @@ -213,7 +349,7 @@ FF_ENABLE_DEPRECATION_WARNINGS if (of->priv_class) { *(const AVClass **)s->priv_data = of->priv_class; av_opt_set_defaults(s->priv_data); - if ((ret = av_opt_set_dict(s->priv_data, &tmp)) < 0) + if ((ret = av_opt_set_dict2(s->priv_data, &tmp, AV_OPT_SEARCH_CHILDREN)) < 0) goto fail; } } @@ -221,6 +357,12 @@ FF_ENABLE_DEPRECATION_WARNINGS /* set muxer identification string */ if (!(s->flags & AVFMT_FLAG_BITEXACT)) { av_dict_set(&s->metadata, "encoder", LIBAVFORMAT_IDENT, 0); + } else { + av_dict_set(&s->metadata, "encoder", NULL, 0); + } + + for (e = NULL; e = av_dict_get(s->metadata, "encoder-", e, AV_DICT_IGNORE_SUFFIX); ) { + av_dict_set(&s->metadata, e->key, NULL, 0); } if (options) { @@ -235,6 +377,37 @@ fail: return ret; } +static int init_pts(AVFormatContext *s) +{ + int i; + AVStream *st; + + /* init PTS generation */ + for (i = 0; i < s->nb_streams; i++) { + int64_t den = AV_NOPTS_VALUE; + st = s->streams[i]; + + switch (st->codec->codec_type) { + case AVMEDIA_TYPE_AUDIO: + den = (int64_t)st->time_base.num * st->codec->sample_rate; + break; + case AVMEDIA_TYPE_VIDEO: + den = (int64_t)st->time_base.num * st->codec->time_base.den; + break; + default: + break; + } + if (den != AV_NOPTS_VALUE) { + if (den <= 0) + return AVERROR_INVALIDDATA; + + frac_init(&st->pts, 0, 0, den); + } + } + + return 0; +} + int avformat_write_header(AVFormatContext *s, AVDictionary **options) { int ret = 0; @@ -244,24 +417,51 @@ 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; + if (s->flush_packets && s->pb && s->pb->error >= 0 && s->flags & AVFMT_FLAG_FLUSH_PACKETS) + avio_flush(s->pb); + } + + 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; } +#define AV_PKT_FLAG_UNCODED_FRAME 0x2000 + +/* Note: using sizeof(AVFrame) from outside lavu is unsafe in general, but + it is only being used internally to this file as a consistency check. + The value is chosen to be very unlikely to appear on its own and to cause + immediate failure if used anywhere as a real size. */ +#define UNCODED_FRAME_PACKET_SIZE (INT_MIN / 3 * 2 + (int)sizeof(AVFrame)) + + //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, i; + int frame_size; - 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); + 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); -/* if(pkt->pts == AV_NOPTS_VALUE && pkt->dts == AV_NOPTS_VALUE) - * return AVERROR(EINVAL);*/ + if (pkt->duration < 0 && st->codec->codec_type != AVMEDIA_TYPE_SUBTITLE) { + av_log(s, AV_LOG_WARNING, "Packet with invalid duration %d in stream %d\n", + pkt->duration, pkt->stream_index); + pkt->duration = 0; + } /* duration field */ if (pkt->duration == 0) { @@ -274,6 +474,18 @@ static int compute_pkt_fields2(AVFormatContext *s, AVStream *st, AVPacket *pkt) if (pkt->pts == AV_NOPTS_VALUE && pkt->dts != AV_NOPTS_VALUE && delay == 0) pkt->pts = pkt->dts; + //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; + } + //calculate dts from pts if (pkt->pts != AV_NOPTS_VALUE && pkt->dts == AV_NOPTS_VALUE && delay <= MAX_REORDER_DELAY) { st->pts_buffer[0] = pkt->pts; @@ -289,53 +501,106 @@ 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; + + /* update pts */ + switch (st->codec->codec_type) { + case AVMEDIA_TYPE_AUDIO: + frame_size = (pkt->flags & AV_PKT_FLAG_UNCODED_FRAME) ? + ((AVFrame *)pkt->data)->nb_samples : + av_get_audio_frame_duration(st->codec, pkt->size); + + /* HACK/FIXME, we skip the initial 0 size packets as they are most + * likely equal to the encoder delay, but it would be better if we + * had the real timestamps from the encoder */ + if (frame_size >= 0 && (pkt->size || st->pts.num != st->pts.den >> 1 || st->pts.val)) { + frac_add(&st->pts, (int64_t)st->time_base.den * frame_size); + } + break; + case AVMEDIA_TYPE_VIDEO: + frac_add(&st->pts, (int64_t)st->time_base.den * st->codec->time_base.num); + break; + } 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->output_ts_offset) { + AVStream *st = s->streams[pkt->stream_index]; + int64_t offset = av_rescale_q(s->output_ts_offset, AV_TIME_BASE_Q, st->time_base); + + if (pkt->dts != AV_NOPTS_VALUE) + pkt->dts += offset; + if (pkt->pts != AV_NOPTS_VALUE) + pkt->pts += offset; + } - 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 || s->avoid_negative_ts == 2) && 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); + if ((pkt->flags & AV_PKT_FLAG_UNCODED_FRAME)) { + AVFrame *frame = (AVFrame *)pkt->data; + av_assert0(pkt->size == UNCODED_FRAME_PACKET_SIZE); + ret = s->oformat->write_uncoded_frame(s, pkt->stream_index, &frame, 0); + av_frame_free(&frame); + } else { + ret = s->oformat->write_packet(s, 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; } @@ -367,8 +632,14 @@ int av_write_frame(AVFormatContext *s, AVPacket *pkt) return 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 && s->flags & AVFMT_FLAG_FLUSH_PACKETS) + avio_flush(s->pb); + if (ret >= 0 && s->pb && s->pb->error < 0) + ret = s->pb->error; + return ret; + } return 1; } @@ -378,17 +649,23 @@ 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; } +#define CHUNK_START 0x1000 + int ff_interleave_add_packet(AVFormatContext *s, AVPacket *pkt, int (*compare)(AVFormatContext *, AVPacket *, AVPacket *)) { int ret; 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) @@ -400,27 +677,58 @@ FF_DISABLE_DEPRECATION_WARNINGS FF_ENABLE_DEPRECATION_WARNINGS #endif pkt->buf = NULL; - // Duplicate the packet if it uses non-allocated memory - if ((ret = av_dup_packet(&this_pktl->pkt)) < 0) { - av_free(this_pktl); - return ret; + pkt->side_data = NULL; + pkt->side_data_elems = 0; + if ((pkt->flags & AV_PKT_FLAG_UNCODED_FRAME)) { + av_assert0(pkt->size == UNCODED_FRAME_PACKET_SIZE); + av_assert0(((AVFrame *)pkt->data)->buf); + } else { + // Duplicate the packet if it uses non-allocated memory + if ((ret = av_dup_packet(&this_pktl->pkt)) < 0) { + av_free(this_pktl); + return ret; + } } 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: @@ -440,6 +748,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; @@ -451,6 +769,7 @@ int ff_interleave_packet_per_dts(AVFormatContext *s, AVPacket *out, { AVPacketList *pktl; int stream_count = 0; + int noninterleaved_count = 0; int i, ret; if (pkt) { @@ -458,7 +777,24 @@ int ff_interleave_packet_per_dts(AVFormatContext *s, AVPacket *out, return ret; } - if (s->max_interleave_delta > 0 && s->packet_buffer && !flush) { + 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_ATTACHMENT && + s->streams[i]->codec->codec_id != AV_CODEC_ID_VP8 && + s->streams[i]->codec->codec_id != AV_CODEC_ID_VP9) { + ++noninterleaved_count; + } + } + + if (s->internal->nb_interleaved_streams == stream_count) + flush = 1; + + if (s->max_interleave_delta > 0 && + s->packet_buffer && + !flush && + s->internal->nb_interleaved_streams == stream_count+noninterleaved_count + ) { AVPacket *top_pkt = &s->packet_buffer->pkt; int64_t delta_dts = INT64_MIN; int64_t top_dts = av_rescale_q(top_pkt->dts, @@ -476,7 +812,6 @@ int ff_interleave_packet_per_dts(AVFormatContext *s, AVPacket *out, s->streams[i]->time_base, AV_TIME_BASE_Q); delta_dts = FFMAX(delta_dts, last_dts - top_dts); - stream_count++; } if (delta_dts > s->max_interleave_delta) { @@ -486,23 +821,22 @@ int ff_interleave_packet_per_dts(AVFormatContext *s, AVPacket *out, delta_dts, s->max_interleave_delta); flush = 1; } - } else { - for (i = 0; i < s->nb_streams; i++) - stream_count += !!s->streams[i]->last_in_packet_buffer; } - - if (stream_count && (s->internal->nb_interleaved_streams == stream_count || flush)) { + 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); @@ -541,8 +875,8 @@ int av_interleaved_write_frame(AVFormatContext *s, AVPacket *pkt) if (pkt) { AVStream *st = s->streams[pkt->stream_index]; - 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)) goto fail; @@ -574,6 +908,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; } fail: av_packet_unref(pkt); @@ -600,15 +936,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); @@ -619,10 +958,20 @@ 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) + AVFormatContext *src, int interleave) { AVPacket local_pkt; + int ret; local_pkt = *pkt; local_pkt.stream_index = dst_stream; @@ -634,5 +983,62 @@ 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); - return av_write_frame(dst, &local_pkt); + if (pkt->duration) + local_pkt.duration = av_rescale_q(pkt->duration, + src->streams[pkt->stream_index]->time_base, + dst->streams[dst_stream]->time_base); + + if (interleave) ret = av_interleaved_write_frame(dst, &local_pkt); + else ret = av_write_frame(dst, &local_pkt); + pkt->buf = local_pkt.buf; + pkt->destruct = local_pkt.destruct; + return ret; +} + +static int av_write_uncoded_frame_internal(AVFormatContext *s, int stream_index, + AVFrame *frame, int interleaved) +{ + AVPacket pkt, *pktp; + + av_assert0(s->oformat); + if (!s->oformat->write_uncoded_frame) + return AVERROR(ENOSYS); + + if (!frame) { + pktp = NULL; + } else { + pktp = &pkt; + av_init_packet(&pkt); + pkt.data = (void *)frame; + pkt.size = UNCODED_FRAME_PACKET_SIZE; + pkt.pts = + pkt.dts = frame->pts; + pkt.duration = av_frame_get_pkt_duration(frame); + pkt.stream_index = stream_index; + pkt.flags |= AV_PKT_FLAG_UNCODED_FRAME; + } + + return interleaved ? av_interleaved_write_frame(s, pktp) : + av_write_frame(s, pktp); +} + +int av_write_uncoded_frame(AVFormatContext *s, int stream_index, + AVFrame *frame) +{ + return av_write_uncoded_frame_internal(s, stream_index, frame, 0); +} + +int av_interleaved_write_uncoded_frame(AVFormatContext *s, int stream_index, + AVFrame *frame) +{ + return av_write_uncoded_frame_internal(s, stream_index, frame, 1); +} + +int av_write_uncoded_frame_query(AVFormatContext *s, int stream_index) +{ + av_assert0(s->oformat); + if (!s->oformat->write_uncoded_frame) + return AVERROR(ENOSYS); + return s->oformat->write_uncoded_frame(s, stream_index, NULL, + AV_WRITE_UNCODED_FRAME_QUERY); } diff --git a/libavformat/mvdec.c b/libavformat/mvdec.c index e21ec06..6e7c3ff 100644 --- a/libavformat/mvdec.c +++ b/libavformat/mvdec.c @@ -2,20 +2,20 @@ * Silicon Graphics Movie demuxer * Copyright (c) 2012 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 */ @@ -261,7 +261,7 @@ static int mv_read_header(AVFormatContext *avctx) { MvContext *mv = avctx->priv_data; AVIOContext *pb = avctx->pb; - AVStream *ast = NULL, *vst = NULL; + AVStream *ast = NULL, *vst = NULL; //initialization to suppress warning int version, i; avio_skip(pb, 4); diff --git a/libavformat/mvi.c b/libavformat/mvi.c index 03a1f40..a7cfcb9 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 */ @@ -54,8 +54,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 19ee35f..4dc54d7 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 */ @@ -28,6 +28,8 @@ const MXFCodecUL ff_mxf_data_definition_uls[] = { { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x01,0x03,0x02,0x02,0x01,0x00,0x00,0x00 }, 13, AVMEDIA_TYPE_VIDEO }, { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x01,0x03,0x02,0x02,0x02,0x00,0x00,0x00 }, 13, AVMEDIA_TYPE_AUDIO }, + { { 0x80,0x7D,0x00,0x60,0x08,0x14,0x3E,0x6F,0x6F,0x3C,0x8C,0xE1,0x6C,0xEF,0x11,0xD2 }, 16, AVMEDIA_TYPE_VIDEO }, /* LegacyPicture Avid Media Composer MXF */ + { { 0x80,0x7D,0x00,0x60,0x08,0x14,0x3E,0x6F,0x78,0xE1,0xEB,0xE1,0x6C,0xEF,0x11,0xD2 }, 16, AVMEDIA_TYPE_AUDIO }, /* LegacySound Avid Media Composer MXF */ { { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, 0, AVMEDIA_TYPE_DATA }, }; @@ -44,9 +46,10 @@ const MXFCodecUL ff_mxf_codec_uls[] = { { { 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,0x01,0x0E,0x04,0x02,0x01,0x02,0x04,0x01,0x00 }, 16, AV_CODEC_ID_DNXHD }, /* SMPTE VC-3/DNxHD Legacy Avid Media Composer MXF */ { { 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,0x02,0x01,0x31,0x11,0x01 }, 14, AV_CODEC_ID_H264 }, /* H.264/MPEG-4 AVC SPS/PPS in-band */ - { { 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,0x03,0x04,0x02,0x02,0x02,0x03,0x03,0x01,0x00 }, 14, AV_CODEC_ID_AAC }, /* MPEG2 AAC ADTS (legacy) */ { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x02,0x02,0x01,0x00,0x00,0x00,0x00 }, 13, AV_CODEC_ID_PCM_S16LE }, /* Uncompressed */ diff --git a/libavformat/mxf.h b/libavformat/mxf.h index 2443207..036c15e 100644 --- a/libavformat/mxf.h +++ b/libavformat/mxf.h @@ -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 */ #ifndef AVFORMAT_MXF_H @@ -48,11 +48,11 @@ enum MXFMetadataSetType { }; enum MXFFrameLayout { - FullFrame = 0, - MixedFields, - OneField, - SegmentedFrame, - SeparateFields + FullFrame = 0, + SeparateFields, + OneField, + MixedFields, + SegmentedFrame, }; typedef struct KLVPacket { @@ -67,7 +67,7 @@ typedef struct MXFCodecUL { int id; } MXFCodecUL; -typedef struct MXFSamplesPerFrame { +typedef struct { struct AVRational time_base; int samples_per_frame[6]; } MXFSamplesPerFrame; diff --git a/libavformat/mxfdec.c b/libavformat/mxfdec.c index 879e73e..7a4633f 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 */ @@ -46,8 +46,11 @@ #include <inttypes.h> #include "libavutil/aes.h" +#include "libavutil/avassert.h" #include "libavutil/mathematics.h" #include "libavcodec/bytestream.h" +#include "libavutil/intreadwrite.h" +#include "libavutil/timecode.h" #include "avformat.h" #include "internal.h" #include "mxf.h" @@ -69,7 +72,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 { @@ -117,6 +120,15 @@ 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; @@ -237,6 +249,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 }; @@ -270,7 +284,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 && !avio_feof(pb); i++) { b = avio_r8(pb); if (b == key[0]) i = 0; @@ -407,12 +421,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; } @@ -420,17 +436,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 ((err = av_reallocp_array(&mxf->partitions, mxf->partitions_count + 1, - sizeof(*mxf->partitions))) < 0) { - mxf->partitions_count = 0; - return err; - } + tmp_part = av_realloc_array(mxf->partitions, mxf->partitions_count + 1, sizeof(*mxf->partitions)); + if (!tmp_part) + return AVERROR(ENOMEM); + mxf->partitions = tmp_part; if (mxf->parsing_backward) { /* insert the new partition pack in the middle @@ -476,7 +490,10 @@ static int mxf_read_partition_pack(void *arg, AVIOContext *pb, int tag, int size partition->index_sid = avio_rb32(pb); avio_skip(pb, 8); partition->body_sid = avio_rb32(pb); - avio_read(pb, op, sizeof(UID)); + if (avio_read(pb, op, sizeof(UID)) != sizeof(UID)) { + av_log(mxf->fc, AV_LOG_ERROR, "Failed reading UID\n"); + return AVERROR_INVALIDDATA; + } nb_essence_containers = avio_rb32(pb); if (partition->this_partition && @@ -535,7 +552,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: @@ -563,7 +580,7 @@ static int mxf_read_partition_pack(void *arg, AVIOContext *pb, int tag, int size av_log(mxf->fc, AV_LOG_WARNING, "invalid KAGSize %"PRId32" - guessing ", partition->kag_size); - if (mxf->op == OPSonyOpt) + if (mxf->op == OPSONYOpt) partition->kag_size = 512; else partition->kag_size = 1; @@ -576,13 +593,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 ((err = av_reallocp_array(&mxf->metadata_sets, mxf->metadata_sets_count + 1, - sizeof(*mxf->metadata_sets))) < 0) { - mxf->metadata_sets_count = 0; - return err; - } + tmp = av_realloc_array(mxf->metadata_sets, mxf->metadata_sets_count + 1, sizeof(*mxf->metadata_sets)); + if (!tmp) + return AVERROR(ENOMEM); + mxf->metadata_sets = tmp; mxf->metadata_sets[mxf->metadata_sets_count] = metadata_set; mxf->metadata_sets_count++; return 0; @@ -604,9 +620,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 */ @@ -644,9 +658,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 */ @@ -656,6 +668,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; @@ -692,9 +721,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 */ @@ -710,9 +737,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 */ @@ -735,29 +760,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); @@ -809,17 +818,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); @@ -832,9 +842,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 */ @@ -893,9 +901,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)) { + 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 + FF_INPUT_BUFFER_PADDING_SIZE); + descriptor->extradata = av_malloc(size); if (!descriptor->extradata) return AVERROR(ENOMEM); descriptor->extradata_size = size; @@ -981,7 +991,7 @@ static const MXFCodecUL mxf_data_essence_container_uls[] = { { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x09,0x0d,0x01,0x03,0x01,0x02,0x0e,0x00,0x00 }, 16, AV_CODEC_ID_NONE }, }; -static const char* mxf_data_essence_descriptor[] = { +static const char* const mxf_data_essence_descriptor[] = { "vbi_vanc_smpte_436M", }; @@ -999,9 +1009,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); @@ -1173,14 +1182,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); } @@ -1355,12 +1358,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) @@ -1387,25 +1398,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; @@ -1526,7 +1560,16 @@ 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; + 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); @@ -1534,28 +1577,24 @@ static int mxf_parse_structural_metadata(MXFContext *mxf) 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: switch (descriptor->field_dominance) { case MXF_TFF: st->codec->field_order = AV_FIELD_TT; @@ -1571,17 +1610,16 @@ static int mxf_parse_structural_metadata(MXFContext *mxf) } /* Turn field height into frame height. */ st->codec->height *= 2; + 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 */ @@ -1595,19 +1633,16 @@ static int mxf_parse_structural_metadata(MXFContext *mxf) } st->need_parsing = AVSTREAM_PARSE_HEADERS; if (material_track->sequence->origin) { - char material_origin[3]; - snprintf(material_origin, sizeof(material_origin), "%d", material_track->sequence->origin); - av_dict_set(&st->metadata, "material_track_origin", material_origin, 0); + av_dict_set_int(&st->metadata, "material_track_origin", material_track->sequence->origin, 0); } if (source_track->sequence->origin) { - char source_origin[3]; - snprintf(source_origin, sizeof(source_origin), "%d", source_track->sequence->origin); - av_dict_set(&st->metadata, "source_track_origin", source_origin, 0); + av_dict_set_int(&st->metadata, "source_track_origin", source_track->sequence->origin, 0); } } 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; @@ -1652,10 +1687,8 @@ static int mxf_parse_structural_metadata(MXFContext *mxf) } } if (descriptor->extradata) { - st->codec->extradata = av_mallocz(descriptor->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE); - if (st->codec->extradata) { + if (!ff_alloc_extradata(st->codec, descriptor->extradata_size)) { memcpy(st->codec->extradata, descriptor->extradata, descriptor->extradata_size); - st->codec->extradata_size = descriptor->extradata_size; } } else if (st->codec->codec_id == AV_CODEC_ID_H264) { ret = ff_generate_avci_extradata(st); @@ -1798,7 +1831,6 @@ static int mxf_read_identification_metadata(void *arg, AVIOContext *pb, int tag, 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,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x2f,0x00 }, mxf_read_preface_pack }, { { 0x06,0x0e,0x2b,0x34,0x02,0x05,0x01,0x01,0x0d,0x01,0x02,0x01,0x01,0x02,0x01,0x00 }, mxf_read_partition_pack }, { { 0x06,0x0e,0x2b,0x34,0x02,0x05,0x01,0x01,0x0d,0x01,0x02,0x01,0x01,0x02,0x02,0x00 }, mxf_read_partition_pack }, { { 0x06,0x0e,0x2b,0x34,0x02,0x05,0x01,0x01,0x0d,0x01,0x02,0x01,0x01,0x02,0x03,0x00 }, mxf_read_partition_pack }, @@ -1813,7 +1845,7 @@ static const MXFMetadataReadTableEntry mxf_metadata_read_table[] = { { { 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 }, - { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x0F,0x00 }, mxf_read_sequence, sizeof(MXFSequence), Sequence }, + { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x0f,0x00 }, mxf_read_sequence, sizeof(MXFSequence), Sequence }, { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x11,0x00 }, mxf_read_source_clip, sizeof(MXFStructuralComponent), SourceClip }, { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x44,0x00 }, mxf_read_generic_descriptor, sizeof(MXFDescriptor), MultipleDescriptor }, { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x42,0x00 }, mxf_read_generic_descriptor, sizeof(MXFDescriptor), Descriptor }, /* Generic Sound */ @@ -1827,6 +1859,7 @@ static const MXFMetadataReadTableEntry mxf_metadata_read_table[] = { { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x5e,0x00 }, mxf_read_generic_descriptor, sizeof(MXFDescriptor), Descriptor }, /* MPEG2AudioDescriptor */ { { 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 }, @@ -1840,7 +1873,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 && !avio_feof(pb)) { int ret; int tag = avio_rb16(pb); int size = avio_rb16(pb); /* KLV specified by 0x53 */ @@ -1962,8 +1995,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) { @@ -2003,38 +2035,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 */ @@ -2104,6 +2104,8 @@ static int mxf_read_header(AVFormatContext *s) MXFContext *mxf = s->priv_data; KLVPacket klv; int64_t essence_offset = 0; + int64_t last_pos = -1; + uint64_t last_pos_index = 1; int ret; mxf->last_forward_tell = INT64_MAX; @@ -2119,10 +2121,14 @@ static int mxf_read_header(AVFormatContext *s) mxf_read_random_index_pack(s); - while (!s->pb->eof_reached) { - + while (!avio_feof(s->pb)) { const MXFMetadataReadTableEntry *metadata; - + if (avio_tell(s->pb) == last_pos) { + av_log(mxf->fc, AV_LOG_ERROR, "MXF structure loop detected\n"); + return AVERROR_INVALIDDATA; + } + if ((1ULL<<61) % last_pos_index++ == 0) + last_pos = avio_tell(s->pb); if (klv_read_packet(&klv, s->pb) < 0) { /* EOF - seek to previous partition or stop */ if(mxf_parse_handle_partition_or_eof(mxf) <= 0) @@ -2139,13 +2145,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) @@ -2208,10 +2233,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 */ @@ -2219,12 +2244,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; } /** @@ -2241,11 +2271,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) { @@ -2263,8 +2291,7 @@ 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; @@ -2302,8 +2329,7 @@ static int mxf_compute_sample_count(MXFContext *mxf, int stream_index, size++; } - if (!size) - return 0; + av_assert2(size); *sample_count = (mxf->current_edit_unit / size) * (uint64_t)total; for (i = 0; i < mxf->current_edit_unit % size; i++) { @@ -2324,10 +2350,11 @@ static int mxf_set_audio_pts(MXFContext *mxf, AVCodecContext *codec, pkt->pts = track->sample_count; - if (codec->channels <= 0 || codec->channels * bits_per_sample < 8) - return AVERROR_INVALIDDATA; - - track->sample_count += pkt->size / (codec->channels * bits_per_sample / 8); + if ( codec->channels <= 0 + || bits_per_sample <= 0 + || codec->channels * (int64_t)bits_per_sample < 8) + return AVERROR(EINVAL); + track->sample_count += pkt->size / (codec->channels * (int64_t)bits_per_sample / 8); return 0; } @@ -2337,9 +2364,7 @@ static int mxf_read_packet_old(AVFormatContext *s, AVPacket *pkt) MXFContext *mxf = s->priv_data; int ret; - while (!s->pb->eof_reached) { - if ((ret = klv_read_packet(&klv, s->pb)) < 0) - return ret; + while ((ret = klv_read_packet(&klv, s->pb)) == 0) { 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)) { @@ -2375,9 +2400,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 " @@ -2409,14 +2433,12 @@ static int mxf_read_packet_old(AVFormatContext *s, AVPacket *pkt) * 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) { @@ -2433,7 +2455,7 @@ static int mxf_read_packet_old(AVFormatContext *s, AVPacket *pkt) skip: avio_skip(s->pb, klv.length); } - return AVERROR_EOF; + return avio_feof(s->pb) ? AVERROR_EOF : ret; } static int mxf_read_packet(AVFormatContext *s, AVPacket *pkt) @@ -2477,8 +2499,8 @@ 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; @@ -2497,7 +2519,6 @@ static int mxf_read_packet(AVFormatContext *s, AVPacket *pkt) return 0; } - static int mxf_read_close(AVFormatContext *s) { MXFContext *mxf = s->priv_data; @@ -2540,10 +2561,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); @@ -2551,18 +2574,27 @@ 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; /* Must skip Run-In Sequence and search for MXF header partition pack key SMPTE 377M 5.5 */ end -= sizeof(mxf_header_partition_pack_key); - for (; bufp < end; bufp++) { - if (IS_KLV_KEY(bufp, mxf_header_partition_pack_key)) - return AVPROBE_SCORE_MAX; + + for (; bufp < end;) { + if (!((bufp[13] - 1) & 0xF2)){ + if (AV_RN32(bufp ) == AV_RN32(mxf_header_partition_pack_key ) && + AV_RN32(bufp+ 4) == AV_RN32(mxf_header_partition_pack_key+ 4) && + AV_RN32(bufp+ 8) == AV_RN32(mxf_header_partition_pack_key+ 8) && + AV_RN16(bufp+12) == AV_RN16(mxf_header_partition_pack_key+12)) + return AVPROBE_SCORE_MAX; + bufp ++; + } else + bufp += 10; } + return 0; } @@ -2613,7 +2645,7 @@ static int mxf_read_seek(AVFormatContext *s, int stream_index, int64_t sample_ti 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) + if ((ret = mxf_edit_unit_absolute_offset(mxf, t, sample_time, &sample_time, &seekpos, 1)) < 0) return ret; ff_update_cur_dts(s, st, sample_time); diff --git a/libavformat/mxfenc.c b/libavformat/mxfenc.c index 841e727..6a6b7c2 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,14 +287,14 @@ 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; uint8_t umid[16]; ///< unique material identifier + int channel_count; } MXFContext; static const uint8_t uuid_base[] = { 0xAD,0xAB,0x44,0x24,0x2f,0x25,0x4d,0xc7,0x92,0xff,0x29,0xbd }; @@ -279,6 +383,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 +531,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 +559,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 +588,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 +611,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 +776,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 +784,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 +832,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 +896,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 +923,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 +935,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 +962,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 +978,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); @@ -875,6 +997,8 @@ static void mxf_write_mpegvideo_desc(AVFormatContext *s, AVStream *st) static void mxf_write_generic_sound_common(AVFormatContext *s, AVStream *st, const UID key, unsigned size) { AVIOContext *pb = s->pb; + MXFContext *mxf = s->priv_data; + int show_warnings = !mxf->footer_partition_offset; mxf_write_generic_desc(s, st, key, size+5+12+8+8); @@ -888,7 +1012,21 @@ static void mxf_write_generic_sound_common(AVFormatContext *s, AVStream *st, con avio_wb32(pb, 1); mxf_write_local_tag(pb, 4, 0x3D07); - avio_wb32(pb, st->codec->channels); + if (mxf->channel_count == -1) { + if (show_warnings && (s->oformat == &ff_mxf_d10_muxer) && (st->codec->channels != 4) && (st->codec->channels != 8)) + av_log(s, AV_LOG_WARNING, "the number of audio channels shall be 4 or 8 : the output will not comply to MXF D-10 specs, use -d10_channelcount to fix this\n"); + avio_wb32(pb, st->codec->channels); + } else if (s->oformat == &ff_mxf_d10_muxer) { + if (show_warnings && (mxf->channel_count < st->codec->channels)) + av_log(s, AV_LOG_WARNING, "d10_channelcount < actual number of audio channels : some channels will be discarded\n"); + if (show_warnings && (mxf->channel_count != 4) && (mxf->channel_count != 8)) + av_log(s, AV_LOG_WARNING, "d10_channelcount shall be set to 4 or 8 : the output will not comply to MXF D-10 specs\n"); + avio_wb32(pb, mxf->channel_count); + } else { + if (show_warnings && mxf->channel_count != -1) + av_log(s, AV_LOG_ERROR, "-d10_channelcount requires MXF D-10 and will be ignored\n"); + avio_wb32(pb, st->codec->channels); + } mxf_write_local_tag(pb, 4, 0x3D01); avio_wb32(pb, av_get_bits_per_sample(st->codec->codec_id)); @@ -1053,8 +1191,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 +1314,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 +1353,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 +1416,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 +1601,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 +1612,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 +1622,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 +1677,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,45 +1700,55 @@ 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; - } // TODO: should be avg_frame_rate - if (fabs(av_q2d(st->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->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->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) { @@ -1520,7 +1802,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)) @@ -1537,10 +1822,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; @@ -1549,24 +1834,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; @@ -1574,7 +1841,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); @@ -1583,7 +1850,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 { @@ -1595,8 +1862,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); @@ -1613,7 +1879,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; @@ -1629,13 +1896,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); } } @@ -1694,6 +1959,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) { @@ -1761,7 +2036,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 @@ -1898,6 +2173,19 @@ static int mxf_interleave(AVFormatContext *s, AVPacket *out, AVPacket *pkt, int mxf_interleave_get_packet, mxf_compare_timestamps); } +static const AVOption d10_options[] = { + { "d10_channelcount", "Force/set channelcount in generic sound essence descriptor", + offsetof(MXFContext, channel_count), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 8, AV_OPT_FLAG_ENCODING_PARAM}, + { NULL }, +}; + +static const AVClass mxf_d10_muxer_class = { + .class_name = "MXF-D10 muxer", + .item_name = av_default_item_name, + .option = d10_options, + .version = LIBAVUTIL_VERSION_INT, +}; + AVOutputFormat ff_mxf_muxer = { .name = "mxf", .long_name = NULL_IF_CONFIG_SMALL("MXF (Material eXchange Format)"), @@ -1925,4 +2213,5 @@ AVOutputFormat ff_mxf_d10_muxer = { .write_trailer = mxf_write_footer, .flags = AVFMT_NOTIMESTAMPS, .interleave_packet = mxf_interleave, + .priv_class = &mxf_d10_muxer_class, }; diff --git a/libavformat/mxg.c b/libavformat/mxg.c index 1d1488c..34977b8 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 (!avio_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 40d8dac..062899f 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 (avio_feof(s->pb)) return AVERROR(EIO); state = (state<<8) + avio_r8(s->pb); } diff --git a/libavformat/network.c b/libavformat/network.c index 6d308eb..9f02ec6 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) { @@ -73,7 +76,7 @@ void ff_tls_init(void) #if HAVE_THREADS if (!CRYPTO_get_locking_callback()) { int i; - openssl_mutexes = av_malloc(sizeof(pthread_mutex_t) * CRYPTO_num_locks()); + openssl_mutexes = av_malloc_array(sizeof(pthread_mutex_t), CRYPTO_num_locks()); for (i = 0; i < CRYPTO_num_locks(); i++) pthread_mutex_init(&openssl_mutexes[i], NULL); CRYPTO_set_locking_callback(openssl_lock); @@ -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_relative(); + else if (av_gettime_relative() - 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(); @@ -254,7 +281,9 @@ int ff_listen_bind(int fd, const struct sockaddr *addr, closesocket(fd); - ff_socket_nonblock(ret, 1); + if (ff_socket_nonblock(ret, 1) < 0) + av_log(NULL, AV_LOG_DEBUG, "ff_socket_nonblock failed\n"); + return ret; } @@ -266,7 +295,8 @@ int ff_listen_connect(int fd, const struct sockaddr *addr, int ret; socklen_t optlen; - ff_socket_nonblock(fd, 1); + if (ff_socket_nonblock(fd, 1) < 0) + av_log(NULL, AV_LOG_DEBUG, "ff_socket_nonblock failed\n"); while ((ret = connect(fd, addr, addrlen))) { ret = ff_neterrno(); diff --git a/libavformat/network.h b/libavformat/network.h index 09cee58..d89a62d 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 0000000..2f17f9e --- /dev/null +++ b/libavformat/nistspheredec.c @@ -0,0 +1,131 @@ +/* + * 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 = -1; + 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 (!avio_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]; + if (sscanf(buffer, "%31s %*s %31s", key, value) == 3) { + av_dict_set(&s->metadata, key, value, AV_DICT_APPEND); + } else { + av_log(s, AV_LOG_ERROR, "Failed to parse '%s' as metadata\n", buffer); + } + } + } + + 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 e6cc421..4524764 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 670b867..05dfd53 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_assert0(vwidth%16==0) */ + uint16_t vheight; /* av_assert0(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 (avio_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 (avio_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 (avio_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 (avio_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 (avio_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 (avio_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 */ @@ -717,10 +719,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 */ @@ -732,19 +732,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 829f2a8..7c08c39 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 */ @@ -32,5 +32,5 @@ AVOutputFormat ff_null_muxer = { .audio_codec = AV_NE(AV_CODEC_ID_PCM_S16BE, AV_CODEC_ID_PCM_S16LE), .video_codec = AV_CODEC_ID_RAWVIDEO, .write_packet = null_write_packet, - .flags = AVFMT_NOFILE | AVFMT_NOTIMESTAMPS | AVFMT_RAWPICTURE, + .flags = AVFMT_VARIABLE_FPS | AVFMT_NOFILE | AVFMT_NOTIMESTAMPS | AVFMT_RAWPICTURE, }; diff --git a/libavformat/nut.c b/libavformat/nut.c index 43ae8a0..9224a96 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,36 +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('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('R', 'B', 'A', 64 ) }, { AV_CODEC_ID_RAWVIDEO, MKTAG('B', 'R', 'A', 64 ) }, { AV_CODEC_ID_RAWVIDEO, MKTAG(64 , 'R', 'B', 'A') }, { AV_CODEC_ID_RAWVIDEO, MKTAG(64 , 'B', 'R', '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', '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('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') }, @@ -125,40 +144,76 @@ 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_RAWVIDEO, MKTAG('X', 'Y', 'Z' , 36 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(36 , 'Z' , 'Y', 'X') }, + + { AV_CODEC_ID_RAWVIDEO, MKTAG(0xBA, 'B', 'G', 8 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(0xBA, 'B', 'G', 16 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(16 , 'G', 'B', 0xBA) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(0xBA, 'R', 'G', 8 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(0xBA, 'R', 'G', 16 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(16 , 'G', 'R', 0xBA) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(0xBA, 'G', 'B', 8 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(0xBA, 'G', 'B', 16 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(16, 'B', 'G', 0xBA) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(0xBA, 'G', 'R', 8 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(0xBA, 'G', 'R', 16 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(16, 'R', 'G', 0xBA) }, + { AV_CODEC_ID_NONE, 0 } }; -const AVCodecTag ff_nut_audio_tags[] = { +const AVCodecTag ff_nut_audio_extra_tags[] = { + { AV_CODEC_ID_COMFORT_NOISE, MKTAG('3', '3', '8', '9') }, { 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, ff_nut_audio_extra_tags, ff_nut_data_tags, 0 }; void ff_nut_reset_ts(NUTContext *nut, AVRational time_base, int64_t val) @@ -174,7 +229,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; } @@ -200,6 +255,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 16f3c12..943081c 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 */ @@ -48,6 +48,7 @@ typedef enum{ FLAG_SIZE_MSB = 32, ///<if set, data_size_msb is at frame header, otherwise data_size_msb is 0 FLAG_CHECKSUM = 64, ///<if set, the frame header contains a checksum FLAG_RESERVED = 128, ///<if set, reserved_count is coded in the frame header + FLAG_SM_DATA = 256, ///<if set, side / meta data is stored in the frame header. FLAG_HEADER_IDX =1024, ///<If set, header_idx is coded in the frame header. FLAG_MATCH_TIME =2048, ///<If set, match_time_delta is coded in the frame header FLAG_CODED =4096, ///<if set, coded_flags are stored in the frame header @@ -80,6 +81,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 { @@ -103,15 +105,21 @@ typedef struct NUTContext { int header_count; AVRational *time_base; struct AVTreeNode *syncpoints; + int sp_count; + int write_index; + int64_t max_pts; + AVRational *max_pts_tb; #define NUT_BROADCAST 1 // use extended syncpoints #define NUT_PIPE 2 // do not write syncpoints int flags; int version; // version currently in use + int minor_version; } NUTContext; extern const AVCodecTag ff_nut_subtitle_tags[]; extern const AVCodecTag ff_nut_video_tags[]; extern const AVCodecTag ff_nut_audio_tags[]; +extern const AVCodecTag ff_nut_audio_extra_tags[]; extern const AVCodecTag ff_nut_data_tags[]; extern const AVCodecTag * const ff_nut_codec_tags[]; diff --git a/libavformat/nutdec.c b/libavformat/nutdec.c index 6c95d55..1b00cdb 100644 --- a/libavformat/nutdec.c +++ b/libavformat/nutdec.c @@ -3,37 +3,41 @@ * 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/intreadwrite.h" #include "libavutil/mathematics.h" #include "libavutil/tree.h" +#include "libavcodec/bytestream.h" #include "avio_internal.h" +#include "isom.h" #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 +76,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 +103,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 +146,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 (!avio_feof(bc)) { state = (state << 8) | avio_r8(bc); if ((state >> 56) != 'N') continue; @@ -168,11 +184,11 @@ static int64_t find_startcode(AVIOContext *bc, uint64_t code, int64_t pos) static int nut_probe(AVProbeData *p) { int i; - uint64_t code = 0; - for (i = 0; i < p->buf_size; i++) { - code = (code << 8) | p->buf[i]; - if (code == MAIN_STARTCODE) + for (i = 0; i < p->buf_size-8; i++) { + if (AV_RB32(p->buf+i) != MAIN_STARTCODE>>32) + continue; + if (AV_RB32(p->buf+i+4) == (MAIN_STARTCODE & 0xFFFFFFFF)) return AVPROBE_SCORE_MAX; } return 0; @@ -220,6 +236,8 @@ static int decode_main_header(NUTContext *nut) nut->version); return AVERROR(ENOSYS); } + if (nut->version > 3) + nut->minor_version = ffio_read_varlen(bc); GET_V(stream_count, tmp > 0 && tmp <= NUT_MAX_STREAMS); @@ -231,6 +249,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)); @@ -298,7 +318,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; @@ -318,11 +338,11 @@ 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); } // flags had been effectively introduced in version 4 - if (nut->version > NUT_STABLE_VERSION) { + if (nut->version > 3 && end > avio_tell(bc) + 4) { nut->flags = ffio_read_varlen(bc); } @@ -331,7 +351,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); @@ -365,6 +387,7 @@ static int decode_stream_header(NUTContext *nut) st->codec->codec_id = av_codec_get_id((const AVCodecTag * const []) { ff_nut_video_tags, ff_codec_bmp_tags, + ff_codec_movvideo_tags, 0 }, tmp); @@ -374,6 +397,7 @@ static int decode_stream_header(NUTContext *nut) st->codec->codec_id = av_codec_get_id((const AVCodecTag * const []) { ff_nut_audio_tags, ff_codec_wav_tags, + ff_nut_audio_extra_tags, 0 }, tmp); @@ -404,9 +428,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); - avio_read(bc, st->codec->extradata, st->codec->extradata_size); + if (ff_get_extradata(st->codec, bc, st->codec->extradata_size) < 0) + return AVERROR(ENOMEM); } if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) { @@ -461,7 +484,7 @@ static int decode_info_header(NUTContext *nut) int64_t value, end; char name[256], str_value[1024], type_str[256]; const char *type; - int *event_flags; + int *event_flags = NULL; AVChapter *chapter = NULL; AVStream *st = NULL; AVDictionary **metadata = NULL; @@ -527,9 +550,18 @@ 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")) { - *event_flags |= metadata_flag; + if (event_flags) + *event_flags |= metadata_flag; av_dict_set(metadata, name, str_value, 0); } } @@ -546,7 +578,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; @@ -557,7 +590,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); @@ -575,8 +608,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; @@ -584,6 +617,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; @@ -592,23 +638,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) @@ -628,7 +690,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--) @@ -648,7 +710,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); @@ -735,13 +797,123 @@ 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); return 0; } +static int read_sm_data(AVFormatContext *s, AVIOContext *bc, AVPacket *pkt, int is_meta, int64_t maxpos) +{ + int count = ffio_read_varlen(bc); + int skip_start = 0; + int skip_end = 0; + int channels = 0; + int64_t channel_layout = 0; + int sample_rate = 0; + int width = 0; + int height = 0; + int i; + + for (i=0; i<count; i++) { + uint8_t name[256], str_value[256], type_str[256]; + int value; + if (avio_tell(bc) >= maxpos) + return AVERROR_INVALIDDATA; + get_str(bc, name, sizeof(name)); + value = get_s(bc); + + if (value == -1) { + get_str(bc, str_value, sizeof(str_value)); + av_log(s, AV_LOG_WARNING, "Unknown string %s / %s\n", name, str_value); + } else if (value == -2) { + uint8_t *dst = NULL; + int64_t v64, value_len; + + get_str(bc, type_str, sizeof(type_str)); + value_len = ffio_read_varlen(bc); + if (avio_tell(bc) + value_len >= maxpos) + return AVERROR_INVALIDDATA; + if (!strcmp(name, "Palette")) { + dst = av_packet_new_side_data(pkt, AV_PKT_DATA_PALETTE, value_len); + } else if (!strcmp(name, "Extradata")) { + dst = av_packet_new_side_data(pkt, AV_PKT_DATA_NEW_EXTRADATA, value_len); + } else if (sscanf(name, "CodecSpecificSide%"SCNd64"", &v64) == 1) { + dst = av_packet_new_side_data(pkt, AV_PKT_DATA_MATROSKA_BLOCKADDITIONAL, value_len + 8); + if(!dst) + return AVERROR(ENOMEM); + AV_WB64(dst, v64); + dst += 8; + } else if (!strcmp(name, "ChannelLayout") && value_len == 8) { + channel_layout = avio_rl64(bc); + continue; + } else { + av_log(s, AV_LOG_WARNING, "Unknown data %s / %s\n", name, type_str); + avio_skip(bc, value_len); + continue; + } + if(!dst) + return AVERROR(ENOMEM); + avio_read(bc, dst, value_len); + } else if (value == -3) { + value = get_s(bc); + } else if (value == -4) { + value = ffio_read_varlen(bc); + } else if (value < -4) { + get_s(bc); + } else { + if (!strcmp(name, "SkipStart")) { + skip_start = value; + } else if (!strcmp(name, "SkipEnd")) { + skip_end = value; + } else if (!strcmp(name, "Channels")) { + channels = value; + } else if (!strcmp(name, "SampleRate")) { + sample_rate = value; + } else if (!strcmp(name, "Width")) { + width = value; + } else if (!strcmp(name, "Height")) { + height = value; + } else { + av_log(s, AV_LOG_WARNING, "Unknown integer %s\n", name); + } + } + } + + if (channels || channel_layout || sample_rate || width || height) { + uint8_t *dst = av_packet_new_side_data(pkt, AV_PKT_DATA_PARAM_CHANGE, 28); + if (!dst) + return AVERROR(ENOMEM); + bytestream_put_le32(&dst, + AV_SIDE_DATA_PARAM_CHANGE_CHANNEL_COUNT*(!!channels) + + AV_SIDE_DATA_PARAM_CHANGE_CHANNEL_LAYOUT*(!!channel_layout) + + AV_SIDE_DATA_PARAM_CHANGE_SAMPLE_RATE*(!!sample_rate) + + AV_SIDE_DATA_PARAM_CHANGE_DIMENSIONS*(!!(width|height)) + ); + if (channels) + bytestream_put_le32(&dst, channels); + if (channel_layout) + bytestream_put_le64(&dst, channel_layout); + if (sample_rate) + bytestream_put_le32(&dst, sample_rate); + if (width || height){ + bytestream_put_le32(&dst, width); + bytestream_put_le32(&dst, height); + } + } + + if (skip_start || skip_end) { + uint8_t *dst = av_packet_new_side_data(pkt, AV_PKT_DATA_SKIP_SAMPLES, 10); + if (!dst) + return AVERROR(ENOMEM); + AV_WL32(dst, skip_start); + AV_WL32(dst+4, skip_end); + } + + return 0; +} + static int decode_frame_header(NUTContext *nut, int64_t *pts, int *stream_id, uint8_t *header_idx, int frame_code) { @@ -781,7 +953,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) @@ -826,6 +998,7 @@ static int decode_frame(NUTContext *nut, AVPacket *pkt, int frame_code) int64_t pts, last_IP_pts; StreamContext *stc; uint8_t header_idx; + int ret; size = decode_frame_header(nut, &pts, &stream_id, &header_idx, frame_code); if (size < 0) @@ -847,10 +1020,27 @@ 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); + if (stc->last_flags & FLAG_SM_DATA) { + int sm_size; + if (read_sm_data(s, bc, pkt, 0, pkt->pos + size) < 0) + return AVERROR_INVALIDDATA; + if (read_sm_data(s, bc, pkt, 1, pkt->pos + size) < 0) + return AVERROR_INVALIDDATA; + sm_size = avio_tell(bc) - pkt->pos; + size -= sm_size; + pkt->size -= sm_size; + } + + ret = avio_read(bc, pkt->data + nut->header_len[header_idx], size); + if (ret != size) { + if (ret < 0) + return ret; + } + av_shrink_packet(pkt, nut->header_len[header_idx] + ret); pkt->stream_index = stream_id; if (stc->last_flags & FLAG_KEY) @@ -876,7 +1066,7 @@ static int nut_read_packet(AVFormatContext *s, AVPacket *pkt) pos -= 8; } else { frame_code = avio_r8(bc); - if (bc->eof_reached) + if (avio_feof(bc)) return AVERROR_EOF; if (frame_code == 'N') { tmp = frame_code; @@ -930,21 +1120,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, @@ -965,6 +1152,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; @@ -997,7 +1186,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); @@ -1029,6 +1218,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 b68e00e..5db380f 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 */ @@ -25,8 +25,10 @@ #include "libavutil/mathematics.h" #include "libavutil/tree.h" #include "libavutil/dict.h" +#include "libavutil/avassert.h" #include "libavutil/time.h" #include "libavutil/opt.h" +#include "libavcodec/bytestream.h" #include "libavcodec/mpegaudiodata.h" #include "nut.h" #include "internal.h" @@ -64,12 +66,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); @@ -103,8 +102,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]; @@ -173,6 +171,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) { @@ -188,29 +199,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; @@ -233,6 +247,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; @@ -247,15 +263,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; @@ -286,8 +300,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); @@ -328,6 +341,8 @@ static void write_mainheader(NUTContext *nut, AVIOContext *bc) int64_t tmp_match; ff_put_v(bc, nut->version); + if (nut->version > 3) + ff_put_v(bc, nut->minor_version = 1); ff_put_v(bc, nut->avf->nb_streams); ff_put_v(bc, nut->max_distance); ff_put_v(bc, nut->time_base_count); @@ -346,17 +361,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; @@ -385,22 +395,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++) { @@ -408,7 +410,7 @@ static void write_mainheader(NUTContext *nut, AVIOContext *bc) avio_write(bc, nut->header[i], nut->header_len[i]); } // flags had been effectively introduced in version 4 - if (nut->version > NUT_STABLE_VERSION) + if (nut->version > 3) ff_put_v(bc, nut->flags); } @@ -417,30 +419,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); @@ -516,20 +505,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) { @@ -575,6 +572,58 @@ 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; + int n = 0; + + if (j && nus->keyframe_pts[j] == nus->keyframe_pts[j-1]) { + av_log(nut->avf, AV_LOG_WARNING, "Multiple keyframes with same PTS\n"); + nus->keyframe_pts[j] = AV_NOPTS_VALUE; + } + + flag = (nus->keyframe_pts[j] != AV_NOPTS_VALUE) ^ (j+1 == nut->sp_count); + 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; @@ -648,8 +697,8 @@ static int nut_write_header(AVFormatContext *s) nut->avf = s; - nut->version = NUT_STABLE_VERSION + !!nut->flags; - if (nut->flags && s->strict_std_compliance > FF_COMPLIANCE_EXPERIMENTAL) { + nut->version = FFMAX(NUT_STABLE_VERSION, 3 + !!nut->flags); + if (nut->version > 3 && s->strict_std_compliance > FF_COMPLIANCE_EXPERIMENTAL) { av_log(s, AV_LOG_ERROR, "The additional syncpoint modes require version %d, " "that is currently not finalized, " @@ -658,12 +707,11 @@ static int nut_write_header(AVFormatContext *s) return AVERROR_EXPERIMENTAL; } - 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); @@ -674,8 +722,13 @@ static int nut_write_header(AVFormatContext *s) AVStream *st = s->streams[i]; int ssize; AVRational time_base; - ff_parse_specific_params(st, &time_base.den, &ssize, - &time_base.num); + ff_parse_specific_params(st, &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); @@ -712,7 +765,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); @@ -720,9 +773,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; } @@ -740,6 +794,8 @@ static int get_needed_flags(NUTContext *nut, StreamContext *nus, FrameCode *fc, flags |= FLAG_SIZE_MSB; if (pkt->pts - nus->last_pts != fc->pts_delta) flags |= FLAG_CODED_PTS; + if (pkt->side_data_elems && nut->version > 3) + flags |= FLAG_SM_DATA; if (pkt->size > 2 * nut->max_distance) flags |= FLAG_CHECKSUM; if (FFABS(pkt->pts - nus->last_pts) > nus->max_pts_distance) @@ -772,24 +828,164 @@ static int find_best_header_idx(NUTContext *nut, AVPacket *pkt) return best_i; } +static int write_sm_data(AVFormatContext *s, AVIOContext *bc, AVPacket *pkt, int is_meta) +{ + int ret, i, dyn_size; + unsigned flags; + AVIOContext *dyn_bc; + int sm_data_count = 0; + uint8_t tmp[256]; + uint8_t *dyn_buf; + + ret = avio_open_dyn_buf(&dyn_bc); + if (ret < 0) + return ret; + + for (i = 0; i<pkt->side_data_elems; i++) { + const uint8_t *data = pkt->side_data[i].data; + int size = pkt->side_data[i].size; + const uint8_t *data_end = data + size; + + if (is_meta) { + if ( pkt->side_data[i].type == AV_PKT_DATA_METADATA_UPDATE + || pkt->side_data[i].type == AV_PKT_DATA_STRINGS_METADATA) { + if (!size || data[size-1]) { + ret = AVERROR(EINVAL); + goto fail; + } + while (data < data_end) { + const uint8_t *key = data; + const uint8_t *val = data + strlen(key) + 1; + + if(val >= data_end) { + ret = AVERROR(EINVAL); + goto fail; + } + put_str(dyn_bc, key); + put_s(dyn_bc, -1); + put_str(dyn_bc, val); + data = val + strlen(val) + 1; + sm_data_count++; + } + } + } else { + switch (pkt->side_data[i].type) { + case AV_PKT_DATA_PALETTE: + case AV_PKT_DATA_NEW_EXTRADATA: + case AV_PKT_DATA_MATROSKA_BLOCKADDITIONAL: + default: + if (pkt->side_data[i].type == AV_PKT_DATA_PALETTE) { + put_str(dyn_bc, "Palette"); + } else if(pkt->side_data[i].type == AV_PKT_DATA_NEW_EXTRADATA) { + put_str(dyn_bc, "Extradata"); + } else if(pkt->side_data[i].type == AV_PKT_DATA_MATROSKA_BLOCKADDITIONAL) { + snprintf(tmp, sizeof(tmp), "CodecSpecificSide%"PRId64"", AV_RB64(data)); + put_str(dyn_bc, tmp); + } else { + snprintf(tmp, sizeof(tmp), "UserData%s-SD-%d", + (s->flags & AVFMT_FLAG_BITEXACT) ? "Lavf" : LIBAVFORMAT_IDENT, + pkt->side_data[i].type); + put_str(dyn_bc, tmp); + } + put_s(dyn_bc, -2); + put_str(dyn_bc, "bin"); + ff_put_v(dyn_bc, pkt->side_data[i].size); + avio_write(dyn_bc, data, pkt->side_data[i].size); + sm_data_count++; + break; + case AV_PKT_DATA_PARAM_CHANGE: + flags = bytestream_get_le32(&data); + if (flags & AV_SIDE_DATA_PARAM_CHANGE_CHANNEL_COUNT) { + put_str(dyn_bc, "Channels"); + put_s(dyn_bc, bytestream_get_le32(&data)); + sm_data_count++; + } + if (flags & AV_SIDE_DATA_PARAM_CHANGE_CHANNEL_LAYOUT) { + put_str(dyn_bc, "ChannelLayout"); + put_s(dyn_bc, -2); + put_str(dyn_bc, "u64"); + ff_put_v(bc, 8); + avio_write(dyn_bc, data, 8); data+=8; + sm_data_count++; + } + if (flags & AV_SIDE_DATA_PARAM_CHANGE_SAMPLE_RATE) { + put_str(dyn_bc, "SampleRate"); + put_s(dyn_bc, bytestream_get_le32(&data)); + sm_data_count++; + } + if (flags & AV_SIDE_DATA_PARAM_CHANGE_DIMENSIONS) { + put_str(dyn_bc, "Width"); + put_s(dyn_bc, bytestream_get_le32(&data)); + put_str(dyn_bc, "Height"); + put_s(dyn_bc, bytestream_get_le32(&data)); + sm_data_count+=2; + } + break; + case AV_PKT_DATA_SKIP_SAMPLES: + if (AV_RL32(data)) { + put_str(dyn_bc, "SkipStart"); + put_s(dyn_bc, (unsigned)AV_RL32(data)); + sm_data_count++; + } + if (AV_RL32(data+4)) { + put_str(dyn_bc, "SkipEnd"); + put_s(dyn_bc, (unsigned)AV_RL32(data+4)); + sm_data_count++; + } + break; + case AV_PKT_DATA_METADATA_UPDATE: + case AV_PKT_DATA_STRINGS_METADATA: + // belongs into meta, not side data + break; + } + } + } + +fail: + ff_put_v(bc, sm_data_count); + dyn_size = avio_close_dyn_buf(dyn_bc, &dyn_buf); + avio_write(bc, dyn_buf, dyn_size); + av_freep(&dyn_buf); + + return ret; +} + static int nut_write_packet(AVFormatContext *s, AVPacket *pkt) { NUTContext *nut = s->priv_data; StreamContext *nus = &nut->stream[pkt->stream_index]; - AVIOContext *bc = s->pb, *dyn_bc; + AVIOContext *bc = s->pb, *dyn_bc, *sm_bc = NULL; 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; + int ret = 0; + int sm_size = 0; + int data_size = pkt->size; + uint8_t *sm_buf = NULL; if (pkt->pts < 0) { av_log(s, AV_LOG_ERROR, "Negative pts not supported stream %d, pts %"PRId64"\n", pkt->stream_index, pkt->pts); - return AVERROR_INVALIDDATA; + if (pkt->pts == AV_NOPTS_VALUE) + av_log(s, AV_LOG_ERROR, "Try to enable the genpts flag\n"); + return AVERROR(EINVAL); + } + + if (pkt->side_data_elems && nut->version > 3) { + ret = avio_open_dyn_buf(&sm_bc); + if (ret < 0) + return ret; + ret = write_sm_data(s, sm_bc, pkt, 0); + if (ret >= 0) + ret = write_sm_data(s, sm_bc, pkt, 1); + sm_size = avio_close_dyn_buf(sm_bc, &sm_buf); + if (ret < 0) + goto fail; + data_size += sm_size; } if (1LL << (20 + 3 * nut->header_count) <= avio_tell(bc)) @@ -798,15 +994,14 @@ 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 (data_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. if (store_sp && (!(nut->flags & NUT_PIPE) || nut->last_syncpoint_pos == INT_MIN)) { - Syncpoint *sp, dummy = { .pos = INT64_MAX }; + int64_t sp_pos = INT64_MAX; ff_nut_reset_ts(nut, *nus->time_base, pkt->dts); for (i = 0; i < s->nb_streams; i++) { @@ -817,20 +1012,23 @@ static int nut_write_packet(AVFormatContext *s, AVPacket *pkt) AV_ROUND_DOWN); int index = av_index_search_timestamp(st, dts_tb, AVSEEK_FLAG_BACKWARD); - if (index >= 0) - dummy.pos = FFMIN(dummy.pos, st->index_entries[index].pos); + if (index >= 0) { + sp_pos = FFMIN(sp_pos, st->index_entries[index].pos); + if (!nut->write_index && 2*index > st->nb_index_entries) { + memmove(st->index_entries, + st->index_entries + index, + sizeof(*st->index_entries) * (st->nb_index_entries - index)); + st->nb_index_entries -= index; + } + } } - if (dummy.pos == INT64_MAX) - dummy.pos = 0; - sp = av_tree_find(nut->syncpoints, &dummy, (void *)ff_nut_sp_pos_cmp, - NULL); nut->last_syncpoint_pos = avio_tell(bc); ret = avio_open_dyn_buf(&dyn_bc); if (ret < 0) - return ret; + goto fail; put_tt(nut, nus->time_base, dyn_bc, pkt->dts); - ff_put_v(dyn_bc, sp ? (nut->last_syncpoint_pos - sp->pos) >> 4 : 0); + ff_put_v(dyn_bc, sp_pos != INT64_MAX ? (nut->last_syncpoint_pos - sp_pos) >> 4 : 0); if (nut->flags & NUT_BROADCAST) { put_tt(nut, nus->time_base, dyn_bc, @@ -838,10 +1036,25 @@ static int nut_write_packet(AVFormatContext *s, AVPacket *pkt) } put_packet(nut, bc, dyn_bc, 1, SYNCPOINT_STARTCODE); + if (nut->write_index) { if ((ret = ff_nut_add_sp(nut, nut->last_syncpoint_pos, 0 /*unused*/, pkt->dts)) < 0) - return ret; + goto fail; + + 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) { + ret = AVERROR(ENOMEM); + goto fail; + } + 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) @@ -874,10 +1087,10 @@ static int nut_write_packet(AVFormatContext *s, AVPacket *pkt) if (flags & FLAG_STREAM_ID) length += ff_get_v_length(pkt->stream_index); - if (pkt->size % fc->size_mul != fc->size_lsb) + if (data_size % fc->size_mul != fc->size_lsb) continue; if (flags & FLAG_SIZE_MSB) - length += ff_get_v_length(pkt->size / fc->size_mul); + length += ff_get_v_length(data_size / fc->size_mul); if (flags & FLAG_CHECKSUM) length += 4; @@ -885,9 +1098,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; } @@ -906,7 +1118,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); @@ -918,27 +1130,24 @@ 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_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, data_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_CHECKSUM) avio_wl32(bc, ffio_get_checksum(bc)); + else ffio_get_checksum(bc); + + if (flags & FLAG_SM_DATA) { + avio_write(bc, sm_buf, sm_size); + } + 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 && !(nut->flags & NUT_PIPE)) + if (flags & FLAG_KEY && !(nut->flags & NUT_PIPE)) { av_add_index_entry( s->streams[pkt->stream_index], nut->last_syncpoint_pos, @@ -946,19 +1155,41 @@ 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; + } - return 0; + 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; + } + +fail: + av_freep(&sm_buf); + + return ret; } 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) { + av_assert1(nut->write_index); + 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); @@ -973,6 +1204,7 @@ static const AVOption options[] = { { "default", "", 0, AV_OPT_TYPE_CONST, {.i64 = 0}, INT_MIN, INT_MAX, E, "syncpoints" }, { "none", "Disable syncpoints, low overhead and unseekable", 0, AV_OPT_TYPE_CONST, {.i64 = NUT_PIPE}, INT_MIN, INT_MAX, E, "syncpoints" }, { "timestamped", "Extend syncpoints with a wallclock timestamp", 0, AV_OPT_TYPE_CONST, {.i64 = NUT_BROADCAST}, INT_MIN, INT_MAX, E, "syncpoints" }, + { "write_index", "Write index", OFFSET(write_index), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1, E, }, { NULL }, }; diff --git a/libavformat/nuv.c b/libavformat/nuv.c index 9336912..e7f0eea 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 (!avio_feof(pb)) { int size, subtype; frametype = avio_r8(pb); @@ -86,11 +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_get_extradata(vst->codec, pb, size) < 0) return AVERROR(ENOMEM); - vst->codec->extradata_size = size; - avio_read(pb, vst->codec->extradata, size); size = 0; if (!myth) return 0; @@ -195,6 +192,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 +236,7 @@ static int nuv_packet(AVFormatContext *s, AVPacket *pkt) nuv_frametype frametype; int ret, size; - while (!pb->eof_reached) { + while (!avio_feof(pb)) { int copyhdrsize = ctx->rtjpg_video ? HDRSIZE : 0; uint64_t pos = avio_tell(pb); @@ -262,10 +262,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 +302,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(!avio_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 (!avio_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 +384,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 ae9da3a..8f146e4 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" @@ -47,6 +48,7 @@ static const struct ogg_codec * const ogg_codecs[] = { &ff_flac_codec, &ff_celt_codec, &ff_opus_codec, + &ff_vp8_codec, &ff_old_dirac_codec, &ff_old_flac_codec, &ff_ogm_video_codec, @@ -56,6 +58,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) { @@ -73,6 +78,8 @@ static int ogg_save(AVFormatContext *s) struct ogg_stream *os = ogg->streams + i; os->buf = av_mallocz(os->bufsize + FF_INPUT_BUFFER_PADDING_SIZE); memcpy(os->buf, ost->streams[i].buf, os->bufpos); + os->new_metadata = NULL; + os->new_metadata_size = 0; } ogg->state = ost; @@ -95,9 +102,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 +122,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 +141,16 @@ 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; + } + os->end_trimming = 0; + av_freep(&os->new_metadata); + os->new_metadata_size = 0; } + ogg->page_pos = -1; ogg->curidx = -1; return 0; @@ -150,38 +168,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; - memset(ogg->streams + idx, 0, sizeof(*ogg->streams)); + if (!ogg->state || ogg->state->streams[i].private != os->private) + av_freep(&ogg->streams[i].private); - os = ogg->streams + idx; + /* 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; + + 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; + } + + /* 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 +288,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 +323,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 (avio_feof(bc)) return AVERROR_EOF; sync[sp++ & 3] = c; @@ -241,8 +342,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 +355,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); - } + if (data_packets_seen(ogg)) + idx = ogg_replace_stream(s, serial, nsegs); + else + idx = ogg_new_stream(s, serial); - ogg->curidx = -1; - ogg->nstreams = 0; - - 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 +384,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 +423,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 +446,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 +498,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 +541,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 +551,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 +571,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 +592,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 +639,18 @@ 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_freep(&ogg->streams[i].new_metadata); } - av_free(ogg->streams); + + ogg->nstreams = 0; + + av_freep(&ogg->streams); return 0; } @@ -551,22 +658,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 +726,40 @@ 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; + int invalid = 0; + if (psize) { + switch (s->streams[idx]->codec->codec_id) { + case AV_CODEC_ID_THEORA: + invalid = !!(os->pflags & AV_PKT_FLAG_KEY) != !(os->buf[pstart] & 0x40); + break; + case AV_CODEC_ID_VP8: + invalid = !!(os->pflags & AV_PKT_FLAG_KEY) != !(os->buf[pstart] & 1); + } + if (invalid) { + 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 +773,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,7 +792,32 @@ 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) + goto fail; + AV_WL32(side_data + 4, os->end_trimming); + os->end_trimming = 0; + } + + if (os->new_metadata) { + uint8_t *side_data = av_packet_new_side_data(pkt, + AV_PKT_DATA_METADATA_UPDATE, + os->new_metadata_size); + if(!side_data) + goto fail; + + memcpy(side_data, os->new_metadata, os->new_metadata_size); + av_freep(&os->new_metadata); + os->new_metadata_size = 0; + } + return psize; +fail: + av_free_packet(pkt); + return AVERROR(ENOMEM); } static int64_t ogg_read_timestamp(AVFormatContext *s, int stream_index, @@ -648,22 +826,38 @@ 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; + // Do not trust the last timestamps of a ogm video + if ( (os->flags & OGG_FLAG_EOS) + && !(os->flags & OGG_FLAG_BOS) + && os->codec == &ff_ogm_video_codec) + continue; 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 +868,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 +904,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 b8f0a8b..7dc7716 100644 --- a/libavformat/oggdec.h +++ b/libavformat/oggdec.h @@ -81,7 +81,12 @@ 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 + uint8_t *new_metadata; + unsigned int new_metadata_size; void *private; }; @@ -98,6 +103,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 +111,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; @@ -121,6 +127,7 @@ extern const struct ogg_codec ff_skeleton_codec; extern const struct ogg_codec ff_speex_codec; extern const struct ogg_codec ff_theora_codec; extern const struct ogg_codec ff_vorbis_codec; +extern const struct ogg_codec ff_vp8_codec; int ff_vorbis_comment(AVFormatContext *ms, AVDictionary **m, const uint8_t *buf, int size, int parse_picture); diff --git a/libavformat/oggenc.c b/libavformat/oggenc.c index 489f010..4a54126 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 */ @@ -77,6 +77,8 @@ 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", @@ -84,9 +86,9 @@ static const AVOption options[] = { { NULL }, }; -#define OGG_CLASS(flavor)\ +#define OGG_CLASS(flavor, name)\ static const AVClass flavor ## _muxer_class = {\ - .class_name = #flavor " muxer",\ + .class_name = #name " muxer",\ .item_name = av_default_item_name,\ .option = options,\ .version = LIBAVUTIL_VERSION_INT,\ @@ -139,6 +141,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) @@ -209,9 +216,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; @@ -270,7 +282,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; @@ -412,7 +424,7 @@ static void ogg_write_pages(AVFormatContext *s, int flush) static int ogg_write_header(AVFormatContext *s) { OGGContext *ogg = s->priv_data; - OGGStreamContext *oggstream; + OGGStreamContext *oggstream = NULL; int i, j; if (ogg->pref_size) @@ -422,12 +434,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); + } if (st->codec->codec_id != AV_CODEC_ID_VORBIS && st->codec->codec_id != AV_CODEC_ID_THEORA && @@ -443,6 +456,9 @@ static int ogg_write_header(AVFormatContext *s) return -1; } oggstream = av_mallocz(sizeof(*oggstream)); + if (!oggstream) + return AVERROR(ENOMEM); + oggstream->page.stream_index = i; if (!(s->flags & AVFMT_FLAG_BITEXACT)) @@ -456,11 +472,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, s->flags & AVFMT_FLAG_BITEXACT, - &s->metadata); + &st->metadata); if (err) { av_log(s, AV_LOG_ERROR, "Error writing FLAC headers\n"); av_freep(&st->priv_data); @@ -469,7 +487,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, s->flags & AVFMT_FLAG_BITEXACT, - &s->metadata); + &st->metadata); if (err) { av_log(s, AV_LOG_ERROR, "Error writing Speex headers\n"); av_freep(&st->priv_data); @@ -478,7 +496,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, s->flags & AVFMT_FLAG_BITEXACT, - &s->metadata); + &st->metadata); if (err) { av_log(s, AV_LOG_ERROR, "Error writing Opus headers\n"); av_freep(&st->priv_data); @@ -499,7 +517,7 @@ static int ogg_write_header(AVFormatContext *s) } p = ogg_write_vorbiscomment(7, s->flags & AVFMT_FLAG_BITEXACT, - &oggstream->header_len[1], &s->metadata, + &oggstream->header_len[1], &st->metadata, framing_bit); oggstream->header[1] = p; if (!p) @@ -530,7 +548,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); } @@ -619,7 +637,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); @@ -628,12 +646,19 @@ static int ogg_write_trailer(AVFormatContext *s) } #if CONFIG_OGG_MUXER -OGG_CLASS(ogg) +OGG_CLASS(ogg, Ogg) AVOutputFormat ff_ogg_muxer = { .name = "ogg", .long_name = NULL_IF_CONFIG_SMALL("Ogg"), .mime_type = "application/ogg", - .extensions = "ogg,ogv", + .extensions = "ogg,ogv" +#if !CONFIG_SPX_MUXER + ",spx" +#endif +#if !CONFIG_OPUS_MUXER + ",opus" +#endif + , .priv_data_size = sizeof(OGGContext), .audio_codec = CONFIG_LIBVORBIS_ENCODER ? AV_CODEC_ID_VORBIS : AV_CODEC_ID_FLAC, @@ -647,7 +672,7 @@ AVOutputFormat ff_ogg_muxer = { #endif #if CONFIG_OGA_MUXER -OGG_CLASS(oga) +OGG_CLASS(oga, Ogg audio) AVOutputFormat ff_oga_muxer = { .name = "oga", .long_name = NULL_IF_CONFIG_SMALL("Ogg Audio"), @@ -665,7 +690,7 @@ AVOutputFormat ff_oga_muxer = { #endif #if CONFIG_SPX_MUXER -OGG_CLASS(spx) +OGG_CLASS(spx, Ogg Speex) AVOutputFormat ff_spx_muxer = { .name = "spx", .long_name = NULL_IF_CONFIG_SMALL("Ogg Speex"), @@ -682,7 +707,7 @@ AVOutputFormat ff_spx_muxer = { #endif #if CONFIG_OPUS_MUXER -OGG_CLASS(opus) +OGG_CLASS(opus, Ogg Opus) AVOutputFormat ff_opus_muxer = { .name = "opus", .long_name = NULL_IF_CONFIG_SMALL("Ogg Opus"), diff --git a/libavformat/oggparsecelt.c b/libavformat/oggparsecelt.c index 7084e76..2c0c511 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,13 +44,11 @@ static int celt_header(AVFormatContext *s, int idx) uint32_t version, sample_rate, nb_channels; 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); + if (!priv) + return AVERROR(ENOMEM); + if (ff_alloc_extradata(st->codec, 2 * sizeof(uint32_t)) < 0) { av_free(priv); return AVERROR(ENOMEM); } @@ -61,20 +59,17 @@ 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->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 55a0b59..73bc495 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 f9c15f9..9f0f808 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 913282a..54024e0 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); if (!time_unit || !spu) { @@ -93,6 +97,16 @@ ogm_header(AVFormatContext *s, int idx) st->codec->bit_rate = bytestream2_get_le32(&p) * 8; st->codec->sample_rate = spu * 10000000 / time_unit; 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); @@ -117,15 +131,23 @@ ogm_dshow_header(AVFormatContext *s, int idx) if(*p != 1) return 1; + if (os->psize < 100) + return AVERROR_INVALIDDATA; t = AV_RL32(p + 96); if(t == 0x05589f80){ + if (os->psize < 184) + return AVERROR_INVALIDDATA; + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; st->codec->codec_id = ff_codec_get_id(ff_codec_bmp_tags, AV_RL32(p + 68)); avpriv_set_pts_info(st, 64, AV_RL64(p + 164), 10000000); st->codec->width = AV_RL32(p + 176); st->codec->height = AV_RL32(p + 180); } else if(t == 0x05589f81){ + if (os->psize < 136) + return AVERROR_INVALIDDATA; + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; st->codec->codec_id = ff_codec_get_id(ff_codec_wav_tags, AV_RL16(p + 124)); st->codec->channels = AV_RL16(p + 126); diff --git a/libavformat/oggparseopus.c b/libavformat/oggparseopus.c index 5931ab5..c8b02fa 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 */ @@ -32,6 +32,7 @@ struct oggopus_private { int64_t cur_dts; }; +#define OPUS_SEEK_PREROLL_MS 80 #define OPUS_HEAD_SIZE 19 static int opus_header(AVFormatContext *avf, int idx) @@ -41,31 +42,34 @@ 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); + st->codec->delay = priv->pre_skip; + /*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; + av_codec_set_seek_preroll(st->codec, + av_rescale(OPUS_SEEK_PREROLL_MS, + st->codec->sample_rate, 1000)); avpriv_set_pts_info(st, 64, 1, 48000); priv->need_comments = 1; return 1; @@ -82,6 +86,26 @@ static int opus_header(AVFormatContext *avf, int idx) return 0; } +static int opus_duration(uint8_t *src, int size) +{ + unsigned nb_frames = 1; + unsigned toc = src[0]; + unsigned toc_config = toc >> 3; + unsigned toc_count = toc & 3; + unsigned frame_size = toc_config < 12 ? FFMAX(480, 960 * (toc_config & 3)) : + toc_config < 16 ? 480 << (toc_config & 1) : + 120 << (toc_config & 3); + if (toc_count == 3) { + if (size<2) + return AVERROR_INVALIDDATA; + nb_frames = src[1] & 0x3F; + } else if (toc_count) { + nb_frames = 2; + } + + return frame_size * nb_frames; +} + static int opus_packet(AVFormatContext *avf, int idx) { struct ogg *ogg = avf->priv_data; @@ -89,26 +113,43 @@ static int opus_packet(AVFormatContext *avf, int idx) AVStream *st = avf->streams[idx]; struct oggopus_private *priv = os->private; uint8_t *packet = os->buf + os->pstart; - unsigned toc, toc_config, toc_count, frame_size, nb_frames = 1; + int ret; if (!os->psize) return AVERROR_INVALIDDATA; - toc = *packet; - toc_config = toc >> 3; - toc_count = toc & 3; - frame_size = toc_config < 12 ? FFMAX(480, 960 * (toc_config & 3)) : - toc_config < 16 ? 480 << (toc_config & 1) : - 120 << (toc_config & 3); - if (toc_count == 3) { - if (os->psize < 2) - return AVERROR_INVALIDDATA; - nb_frames = packet[1] & 0x3F; - } else if (toc_count) { - nb_frames = 2; + if ((!os->lastpts || os->lastpts == AV_NOPTS_VALUE) && !(os->flags & OGG_FLAG_EOS)) { + int seg, d; + int duration; + uint8_t *last_pkt = os->buf + os->pstart; + uint8_t *next_pkt = last_pkt; + + duration = 0; + seg = os->segp; + d = opus_duration(last_pkt, os->psize); + 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++) { + next_pkt += os->segments[seg]; + if (os->segments[seg] < 255 && next_pkt != last_pkt) { + int d = opus_duration(last_pkt, next_pkt - last_pkt); + if (d > 0) + duration += d; + last_pkt = next_pkt; + } + } + os->lastpts = + os->lastdts = os->granule - duration; } - os->pduration = frame_size * nb_frames; + if ((ret = opus_duration(packet, os->psize)) < 0) + return ret; + + os->pduration = ret; if (os->lastpts != AV_NOPTS_VALUE) { if (st->start_time == AV_NOPTS_VALUE) st->start_time = os->lastpts; @@ -121,10 +162,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 +178,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 5333e17..6c2105f 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 */ @@ -36,6 +36,9 @@ static int skeleton_header(AVFormatContext *s, int idx) st->codec->codec_type = AVMEDIA_TYPE_DATA; + if ((os->flags & OGG_FLAG_EOS) && os->psize == 0) + return 1; + if (os->psize < 8) return -1; @@ -46,7 +49,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; @@ -60,7 +63,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); @@ -73,12 +76,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 b2779e7..9b5c65f 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 d78f02d..91c70df 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; } @@ -130,6 +131,8 @@ static int theora_header(AVFormatContext *s, int idx) st->codec->extradata_size = 0; return err; } + memset(st->codec->extradata + cds, 0, FF_INPUT_BUFFER_PADDING_SIZE); + cdp = st->codec->extradata + st->codec->extradata_size; *cdp++ = os->psize >> 8; *cdp++ = os->psize & 0xff; @@ -165,10 +168,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 6bd1411..5e34be5 100644 --- a/libavformat/oggparsevorbis.c +++ b/libavformat/oggparsevorbis.c @@ -156,9 +156,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); @@ -171,16 +172,20 @@ int ff_vorbis_comment(AVFormatContext *as, AVDictionary **m, } } else if (!ogm_chapter(as, tt, ct)) { updates++; + if (av_dict_get(*m, tt, NULL, 0)) { + av_dict_set(m, tt, ";", AV_DICT_APPEND); + } av_dict_set(m, tt, ct, AV_DICT_DONT_STRDUP_KEY | - AV_DICT_DONT_STRDUP_VAL); + AV_DICT_APPEND); + av_freep(&ct); } } } if (p != end) av_log(as, AV_LOG_INFO, - "%ti bytes of comment header remain\n", end - p); + "%"PTRDIFF_SPECIFIER" bytes of comment header remain\n", end - p); if (n > 0) av_log(as, AV_LOG_INFO, "truncated comment header, %i comments not found\n", n); @@ -218,12 +223,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; @@ -250,6 +258,36 @@ static void vorbis_cleanup(AVFormatContext *s, int idx) av_freep(&priv->packet[i]); } +static int vorbis_update_metadata(AVFormatContext *s, int idx) +{ + struct ogg *ogg = s->priv_data; + struct ogg_stream *os = ogg->streams + idx; + AVStream *st = s->streams[idx]; + int ret; + + if (os->psize <= 8) + return 0; + + /* New metadata packet; release old data. */ + av_dict_free(&st->metadata); + ret = ff_vorbis_stream_comment(s, st, os->buf + os->pstart + 7, + os->psize - 8); + if (ret < 0) + return ret; + + /* Update the metadata if possible. */ + av_freep(&os->new_metadata); + if (st->metadata) { + os->new_metadata = av_packet_pack_dictionary(st->metadata, &os->new_metadata_size); + /* Send an empty dictionary to indicate that metadata has been cleared. */ + } else { + os->new_metadata = av_malloc(1); + os->new_metadata_size = 0; + } + + return ret; +} + static int vorbis_header(AVFormatContext *s, int idx) { struct ogg *ogg = s->priv_data; @@ -286,6 +324,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; @@ -293,7 +332,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 @@ -319,9 +363,7 @@ static int vorbis_header(AVFormatContext *s, int idx) avpriv_set_pts_info(st, 64, 1, srate); } } else if (os->buf[os->pstart] == 3) { - if (os->psize > 8 && - ff_vorbis_stream_comment(s, st, os->buf + os->pstart + 7, - os->psize - 8) >= 0) { + if (vorbis_update_metadata(s, idx) >= 0 && priv->len[1] > 10) { unsigned new_len; int ret = ff_replaygain_export(st, st->metadata); @@ -358,29 +400,40 @@ static int vorbis_packet(AVFormatContext *s, int idx) struct ogg *ogg = s->priv_data; struct ogg_stream *os = ogg->streams + idx; struct oggvorbis_private *priv = os->private; - int duration; + int duration, flags = 0; /* 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) { - int seg; + if ((!os->lastpts || os->lastpts == AV_NOPTS_VALUE) && !(os->flags & OGG_FLAG_EOS) && (int64_t)os->granule>=0) { + 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_flags(&priv->vp, last_pkt, 1, &flags); + if (d < 0) { + os->pflags |= AV_PKT_FLAG_CORRUPT; + return 0; + } else if (flags & VORBIS_FLAG_COMMENT) { + vorbis_update_metadata(s, idx); + flags = 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); + int d = avpriv_vorbis_parse_frame_flags(&priv->vp, last_pkt, 1, &flags); if (d < 0) { duration = os->granule; break; + } else if (flags & VORBIS_FLAG_COMMENT) { + vorbis_update_metadata(s, idx); + flags = 0; } - if (!duration) - first_duration = d; duration += d; last_pkt = next_pkt + os->segments[seg]; } @@ -388,20 +441,28 @@ 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 (!os->granule && duration) //hack to deal with broken files (Ticket3710) + os->lastpts = os->lastdts = 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 != AV_NOPTS_VALUE) + s->streams[idx]->duration -= s->streams[idx]->start_time; + } priv->final_pts = AV_NOPTS_VALUE; avpriv_vorbis_parse_reset(&priv->vp); } /* parse packet duration */ if (os->psize > 0) { - duration = avpriv_vorbis_parse_frame(&priv->vp, os->buf + os->pstart, 1); - if (duration <= 0) { + duration = avpriv_vorbis_parse_frame_flags(&priv->vp, os->buf + os->pstart, 1, &flags); + if (duration < 0) { os->pflags |= AV_PKT_FLAG_CORRUPT; return 0; + } else if (flags & VORBIS_FLAG_COMMENT) { + vorbis_update_metadata(s, idx); + flags = 0; } os->pduration = duration; } diff --git a/libavformat/oggparsevp8.c b/libavformat/oggparsevp8.c new file mode 100644 index 0000000..5959d32 --- /dev/null +++ b/libavformat/oggparsevp8.c @@ -0,0 +1,137 @@ +/* + * On2 VP8 parser for Ogg + * 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/intreadwrite.h" +#include "avformat.h" +#include "internal.h" +#include "oggdec.h" + +#define VP8_HEADER_SIZE 26 + +static int vp8_header(AVFormatContext *s, int idx) +{ + struct ogg *ogg = s->priv_data; + struct ogg_stream *os = ogg->streams + idx; + uint8_t *p = os->buf + os->pstart; + AVStream *st = s->streams[idx]; + AVRational framerate; + + if (os->psize < 7 || p[0] != 0x4f) + return 0; + + switch (p[5]){ + case 0x01: + if (os->psize < VP8_HEADER_SIZE) { + av_log(s, AV_LOG_ERROR, "Invalid OggVP8 header packet"); + return AVERROR_INVALIDDATA; + } + + if (p[6] != 1) { + av_log(s, AV_LOG_WARNING, "Unknown OggVP8 version %d.%d\n", p[6], p[7]); + return AVERROR_INVALIDDATA; + } + + st->codec->width = AV_RB16(p + 8); + st->codec->height = AV_RB16(p + 10); + st->sample_aspect_ratio.num = AV_RB24(p + 12); + st->sample_aspect_ratio.den = AV_RB24(p + 15); + framerate.den = AV_RB32(p + 18); + framerate.num = AV_RB32(p + 22); + + avpriv_set_pts_info(st, 64, framerate.num, framerate.den); + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->codec->codec_id = AV_CODEC_ID_VP8; + st->need_parsing = AVSTREAM_PARSE_HEADERS; + break; + case 0x02: + if (p[6] != 0x20) + return AVERROR_INVALIDDATA; + ff_vorbis_stream_comment(s, st, p + 7, os->psize - 7); + break; + default: + av_log(s, AV_LOG_ERROR, "Unknown VP8 header type 0x%02X\n", p[5]); + return AVERROR_INVALIDDATA; + } + + return 1; +} + +static uint64_t vp8_gptopts(AVFormatContext *s, int idx, uint64_t granule, int64_t *dts) +{ + struct ogg *ogg = s->priv_data; + struct ogg_stream *os = ogg->streams + idx; + + uint64_t pts = (granule >> 32); + uint32_t dist = (granule >> 3) & 0x07ffffff; + + if (!dist) + os->pflags |= AV_PKT_FLAG_KEY; + + if (dts) + *dts = pts; + + return pts; +} + +static int vp8_packet(AVFormatContext *s, int idx) +{ + struct ogg *ogg = s->priv_data; + struct ogg_stream *os = ogg->streams + idx; + uint8_t *p = os->buf + os->pstart; + + if ((!os->lastpts || os->lastpts == AV_NOPTS_VALUE) && !(os->flags & OGG_FLAG_EOS)) { + int seg; + int duration; + uint8_t *last_pkt = p; + uint8_t *next_pkt; + + seg = os->segp; + duration = (last_pkt[0] >> 4) & 1; + next_pkt = last_pkt += os->psize; + for (; seg < os->nsegs; seg++) { + if (os->segments[seg] < 255) { + duration += (last_pkt[0] >> 4) & 1; + last_pkt = next_pkt + os->segments[seg]; + } + next_pkt += os->segments[seg]; + } + os->lastpts = os->lastdts = vp8_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; + } + } + + if (os->psize > 0) + os->pduration = (p[0] >> 4) & 1; + + return 0; +} + +const struct ogg_codec ff_vp8_codec = { + .magic = "OVP80", + .magicsize = 5, + .header = vp8_header, + .packet = vp8_packet, + .gptopts = vp8_gptopts, + .nb_header = 1, +}; diff --git a/libavformat/oma.c b/libavformat/oma.c index 27b5988..2702867 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 9a35da2..e2a187b 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 9d9c726..9f3d3aa 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 */ @@ -174,13 +174,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); @@ -299,7 +295,7 @@ static int oma_read_header(AVFormatContext *s) ID3v2ExtraMeta *extra_meta = NULL; OMAContext *oc = s->priv_data; - ff_id3v2_read(s, ID3v2_EA3_MAGIC, &extra_meta); + ff_id3v2_read(s, ID3v2_EA3_MAGIC, &extra_meta, 0); ret = avio_read(s->pb, buf, EA3_HEADER_SIZE); if (ret < EA3_HEADER_SIZE) return -1; @@ -355,12 +351,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 @@ -390,7 +384,7 @@ static int oma_read_header(AVFormatContext *s) avpriv_set_pts_info(st, 64, 1, samplerate); 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: @@ -456,7 +450,7 @@ static int oma_read_probe(AVProbeData *p) /* This check cannot overflow as tag_len has at most 28 bits */ if (p->buf_size < tag_len + 5) /* EA3 header comes late, might be outside of the probe buffer */ - return tag_len ? AVPROBE_SCORE_EXTENSION : 0; + return tag_len ? AVPROBE_SCORE_EXTENSION/2 : 0; buf += tag_len; diff --git a/libavformat/omaenc.c b/libavformat/omaenc.c index bfd552a..fe0669f 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 a5646df..e0d6df6 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" @@ -77,6 +77,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, @@ -84,6 +91,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) @@ -111,6 +120,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 0f7207c..eb4115c 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 */ @@ -23,6 +25,7 @@ #include "libavutil/opt.h" #include "avformat.h" +#include "internal.h" #define OFFSET(x) offsetof(AVFormatContext,x) #define DEFAULT 0 //should be NAN but it does not work as it is not a constant in glibc as required by ANSI/ISO C @@ -31,26 +34,37 @@ #define D AV_OPT_FLAG_DECODING_PARAM static const AVOption avformat_options[] = { -{"probesize", "set probing size", OFFSET(probesize), AV_OPT_TYPE_INT, {.i64 = 5000000 }, 32, INT_MAX, D}, +{"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(probesize2), AV_OPT_TYPE_INT64, {.i64 = 5000000 }, 32, INT64_MAX, D}, +{"formatprobesize", "number of bytes to probe file format", OFFSET(format_probesize), AV_OPT_TYPE_INT, {.i64 = PROBE_BUF_MAX}, 0, INT_MAX-1, 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"}, -{"flush_packets", "reduce the latency by flushing out packets immediately", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_FLUSH_PACKETS }, INT_MIN, INT_MAX, D, "fflags"}, +{"flush_packets", "reduce the latency by flushing out packets immediately", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_FLUSH_PACKETS }, INT_MIN, INT_MAX, E, "fflags"}, {"ignidx", "ignore index", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_IGNIDX }, INT_MIN, INT_MAX, D, "fflags"}, {"genpts", "generate pts", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_GENPTS }, INT_MIN, INT_MAX, D, "fflags"}, {"nofillin", "do not fill in missing values that can be exactly calculated", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_NOFILLIN }, INT_MIN, INT_MAX, D, "fflags"}, {"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"}, +{"seek2any", "allow seeking to non-keyframes on demuxer level when supported", OFFSET(seek2any), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, 1, D}, {"bitexact", "do not write random/volatile data", 0, AV_OPT_TYPE_CONST, { .i64 = AVFMT_FLAG_BITEXACT }, 0, 0, E, "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}, +{"analyzeduration", "specify how many microseconds are analyzed to probe the input", OFFSET(max_analyze_duration2), AV_OPT_TYPE_INT64, {.i64 = 0 }, 0, INT64_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 */ {"fdebug", "print specific debug info", OFFSET(debug), AV_OPT_TYPE_FLAGS, {.i64 = DEFAULT }, 0, INT_MAX, E|D, "fdebug"}, {"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}, +{"start_time_realtime", "wall-clock time when stream begins (PTS==0)", OFFSET(start_time_realtime), AV_OPT_TYPE_INT64, {.i64 = AV_NOPTS_VALUE}, INT64_MIN, INT64_MAX, E}, {"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"}, @@ -59,6 +73,21 @@ 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"}, +{"ignore_err", "ignore errors", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_IGNORE_ERR }, 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 so they start at 0", OFFSET(avoid_negative_ts), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 2, E, "avoid_negative_ts"}, +{"auto", "enabled when required by target format", 0, AV_OPT_TYPE_CONST, {.i64 = -1 }, INT_MIN, INT_MAX, E, "avoid_negative_ts"}, +{"disabled", "do not change timestamps", 0, AV_OPT_TYPE_CONST, {.i64 = 0 }, INT_MIN, INT_MAX, E, "avoid_negative_ts"}, +{"make_zero", "shift timestamps so they start at 0", 0, AV_OPT_TYPE_CONST, {.i64 = 2 }, INT_MIN, INT_MAX, E, "avoid_negative_ts"}, +{"make_non_negative", "shift timestamps so they are non negative", 0, AV_OPT_TYPE_CONST, {.i64 = 1 }, INT_MIN, INT_MAX, E, "avoid_negative_ts"}, +{"skip_initial_bytes", "set number of bytes to skip before reading header and frames", OFFSET(skip_initial_bytes), AV_OPT_TYPE_INT64, {.i64 = 0}, 0, INT64_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}, +{"metadata_header_padding", "set number of bytes to be written as padding in a metadata header", OFFSET(metadata_header_padding), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, E}, +{"output_ts_offset", "set output timestamp offset", OFFSET(output_ts_offset), AV_OPT_TYPE_DURATION, {.i64 = 0}, -INT64_MAX, INT64_MAX, E}, {"max_interleave_delta", "maximum buffering duration for interleaving", OFFSET(max_interleave_delta), AV_OPT_TYPE_INT64, { .i64 = 10000000 }, 0, INT64_MAX, E }, {"f_strict", "how strictly to follow the standards (deprecated; use strict, save via avconv)", OFFSET(strict_std_compliance), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, INT_MIN, INT_MAX, D|E, "strict"}, {"strict", "how strictly to follow the standards", OFFSET(strict_std_compliance), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, INT_MIN, INT_MAX, D|E, "strict"}, diff --git a/libavformat/os_support.c b/libavformat/os_support.c index 650baea..e8f063a 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 f751d41..1522740 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,11 +33,17 @@ #if defined(_WIN32) && !defined(__MINGW32CE__) # include <fcntl.h> -# undef lseek +# ifdef lseek +# undef lseek +# endif # define lseek(f,p,w) _lseeki64((f), (p), (w)) -# undef stat +# ifdef stat +# undef stat +# endif # define stat _stati64 -# undef fstat +# ifdef fstat +# undef fstat +# endif # define fstat(f,s) _fstati64((f), (s)) #endif /* defined(_WIN32) && !defined(__MINGW32CE__) */ @@ -48,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/paf.c b/libavformat/paf.c index 18bf353..618994c 100644 --- a/libavformat/paf.c +++ b/libavformat/paf.c @@ -2,31 +2,29 @@ * Packed Animation File 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/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" -#define PAF_SOUND_SAMPLES 2205 -#define PAF_SOUND_FRAME_SIZE ((256 + PAF_SOUND_SAMPLES) * 2) typedef struct { uint32_t buffer_size; @@ -195,10 +193,13 @@ static int read_packet(AVFormatContext *s, AVPacket *pkt) { PAFDemuxContext *p = s->priv_data; AVIOContext *pb = s->pb; - uint32_t count, offset; - int size, i; + uint32_t count, offset; + int size, i; + + if (p->current_frame >= p->nb_frames) + return AVERROR_EOF; - if (p->current_frame >= p->nb_frames || pb->eof_reached) + if (avio_feof(pb)) return AVERROR_EOF; if (p->got_audio) { diff --git a/libavformat/pcm.c b/libavformat/pcm.c index 892e8ca..a57a4b6 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,24 @@ #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; + + 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 30cbc86..9af36d5 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 1bbbc66..2584c33 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,33 +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); - if (!bps) { - av_log(s, AV_LOG_ERROR, "Unknown number of bytes per sample.\n"); - return AVERROR(EINVAL); - } - - 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 }, }; @@ -103,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, \ @@ -170,3 +144,29 @@ PCMDEF(alaw, "PCM A-law", PCMDEF(mulaw, "PCM mu-law", "ul", AV_CODEC_ID_PCM_MULAW) + +static const AVOption sln_options[] = { + { "sample_rate", "", offsetof(PCMAudioDemuxerContext, sample_rate), AV_OPT_TYPE_INT, {.i64 = 8000}, 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 }, +}; + +static const AVClass sln_demuxer_class = { + .class_name = "sln demuxer", + .item_name = av_default_item_name, + .option = sln_options, + .version = LIBAVUTIL_VERSION_INT, +}; + +AVInputFormat ff_sln_demuxer = { + .name = "sln", + .long_name = NULL_IF_CONFIG_SMALL("Asterisk raw pcm"), + .priv_data_size = sizeof(PCMAudioDemuxerContext), + .read_header = pcm_read_header, + .read_packet = ff_pcm_read_packet, + .read_seek = ff_pcm_read_seek, + .flags = AVFMT_GENERIC_INDEX, + .extensions = "sln", + .raw_codec_id = AV_CODEC_ID_PCM_S16LE, + .priv_class = &sln_demuxer_class, +}; diff --git a/libavformat/pcmenc.c b/libavformat/pcmenc.c index e45ab84..3e4f308 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 0000000..5129b70 --- /dev/null +++ b/libavformat/pjsdec.c @@ -0,0 +1,138 @@ +/* + * 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, "\""); + *line += !!**line; + *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 (!avio_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 8ae147f..ec966b3 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 (avio_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,12 @@ static int pmp_packet(AVFormatContext *s, AVPacket *pkt) int ret = 0; int i; - if (pb->eof_reached) + if (avio_feof(pb)) return AVERROR_EOF; if (pmp->cur_stream == 0) { int num_packets; pmp->audio_packets = avio_r8(pb); + if (!pmp->audio_packets) { av_log(s, AV_LOG_ERROR, "No audio packets.\n"); return AVERROR_INVALIDDATA; @@ -143,22 +158,17 @@ 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 - if (pmp->cur_stream == 0) - 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 e662ed7..fd50e54 100644 --- a/libavformat/psxstr.c +++ b/libavformat/psxstr.c @@ -1,21 +1,21 @@ /* * Sony Playstation (PSX) STR File Demuxer - * Copyright (c) 2003 The ffmpeg 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 */ @@ -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) @@ -187,6 +221,7 @@ static int str_read_packet(AVFormatContext *s, av_free_packet(pkt); if (av_new_packet(pkt, sector_count*VIDEO_DATA_CHUNK_SIZE)) return AVERROR(EIO); + memset(pkt->data, 0, sector_count*VIDEO_DATA_CHUNK_SIZE); pkt->pos= avio_tell(pb) - RAW_CD_SECTOR_SIZE; pkt->stream_index = @@ -257,7 +292,7 @@ FF_ENABLE_DEPRECATION_WARNINGS break; } - if (pb->eof_reached) + if (avio_feof(pb)) return AVERROR(EIO); } } diff --git a/libavformat/pva.c b/libavformat/pva.c index 3abfc18..18ab1cd 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 (len < 0) + return 0; - if (AV_RB16(buf) == PVA_MAGIC && buf[2] && buf[2] < 3 && buf[4] == 0x55) + 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) { @@ -72,6 +85,7 @@ static int read_part_of_packet(AVFormatContext *s, int64_t *pts, PVAContext *pvactx = s->priv_data; int syncword, streamid, reserved, flags, length, pts_flag; int64_t pva_pts = AV_NOPTS_VALUE, startpos; + int ret; recover: startpos = avio_tell(pb); @@ -120,8 +134,8 @@ recover: pes_flags = avio_rb16(pb); pes_header_data_length = avio_r8(pb); - if (pes_signal != 1) { - pva_log(s, AV_LOG_WARNING, "expected signaled PES packet, " + if (pes_signal != 1 || pes_header_data_length == 0) { + pva_log(s, AV_LOG_WARNING, "expected non empty signaled PES packet, " "trying to recover\n"); avio_skip(pb, length - 9); if (!read_packet) @@ -129,15 +143,23 @@ recover: goto recover; } - avio_read(pb, pes_header_data, pes_header_data_length); + ret = avio_read(pb, pes_header_data, pes_header_data_length); + if (ret != pes_header_data_length) + return ret < 0 ? ret : AVERROR_INVALIDDATA; length -= 9 + pes_header_data_length; pes_packet_length -= 3 + pes_header_data_length; pvactx->continue_pes = pes_packet_length; - if (pes_flags & 0x80 && (pes_header_data[0] & 0xf0) == 0x20) + if (pes_flags & 0x80 && (pes_header_data[0] & 0xf0) == 0x20) { + if (pes_header_data_length < 5) { + pva_log(s, AV_LOG_ERROR, "header too short\n"); + avio_skip(pb, length); + return AVERROR_INVALIDDATA; + } pva_pts = ff_parse_pes_pts(pes_header_data); + } } pvactx->continue_pes -= length; diff --git a/libavformat/pvfdec.c b/libavformat/pvfdec.c new file mode 100644 index 0000000..c678046 --- /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 30d362f..4d42197 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(!avio_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 ecc85d3..7d6802f 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 74a1f2b..0719fb6 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 09fdbc3..ca9b282 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" #include "libavutil/intreadwrite.h" #define RAW_PACKET_SIZE 1024 @@ -46,12 +47,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; } @@ -62,7 +59,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 */ @@ -74,7 +71,6 @@ int ff_raw_video_read_header(AVFormatContext *s) { AVStream *st; FFRawVideoDemuxerContext *s1 = s->priv_data; - AVRational framerate; int ret = 0; @@ -86,85 +82,50 @@ 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_LATM_DEMUXER - -#define LOAS_SYNC_WORD 0x2b7 - -static int latm_read_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 - 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) != LOAS_SYNC_WORD) { - if (buf != buf0) { - // Found something that isn't a LOAS header, starting - // from a position other than the start of the buffer. - // Discard the count we've accumulated so far since it - // probably was a false positive. - frames = 0; - } - break; - } - fsize = (header & 0x1FFF) + 3; - if (fsize < 7) - break; - buf2 += fsize; - } - max_frames = FFMAX(max_frames, frames); - if (buf == buf0) - first_frames = frames; - } +#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 (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 if (max_frames >= 1) - return 1; - else - return 0; -} +#if CONFIG_LATM_DEMUXER AVInputFormat ff_latm_demuxer = { .name = "latm", .long_name = NULL_IF_CONFIG_SMALL("raw LOAS/LATM"), - .read_probe = latm_read_probe, .read_header = ff_raw_audio_read_header, .read_packet = ff_raw_read_partial_packet, .flags = AVFMT_GENERIC_INDEX, @@ -174,7 +135,73 @@ AVInputFormat ff_latm_demuxer = { #endif #if CONFIG_MJPEG_DEMUXER -FF_DEF_RAWVIDEO_DEMUXER(mjpeg, "raw MJPEG video", NULL, "mjpg,mjpeg", AV_CODEC_ID_MJPEG) +static int mjpeg_probe(AVProbeData *p) +{ + int i; + int state = -1; + int nb_invalid = 0; + int nb_frames = 0; + + for (i=0; i<p->buf_size-2; i++) { + int c; + if (p->buf[i] != 0xFF) + continue; + c = p->buf[i+1]; + switch (c) { + case 0xD8: + state = 0xD8; + break; + case 0xC0: + case 0xC1: + case 0xC2: + case 0xC3: + case 0xC5: + case 0xC6: + case 0xC7: + case 0xF7: + if (state == 0xD8) { + state = 0xC0; + } else + nb_invalid++; + break; + case 0xDA: + if (state == 0xC0) { + state = 0xDA; + } else + nb_invalid++; + break; + case 0xD9: + if (state == 0xDA) { + state = 0xD9; + nb_frames++; + } else + nb_invalid++; + break; + default: + if ( (c >= 0x02 && c <= 0xBF) + || c == 0xC8) { + nb_invalid++; + } + } + } + + if (nb_invalid*4 + 1 < nb_frames) { + static const char ct_jpeg[] = "\r\nContent-Type: image/jpeg\r\n\r\n"; + int i; + + for (i=0; i<FFMIN(p->buf_size - sizeof(ct_jpeg), 100); i++) + if (!memcmp(p->buf + i, ct_jpeg, sizeof(ct_jpeg) - 1)) + return AVPROBE_SCORE_EXTENSION; + + if (nb_invalid == 0 && nb_frames > 2) + return AVPROBE_SCORE_EXTENSION / 2; + return AVPROBE_SCORE_EXTENSION / 4; + } + + return 0; +} + +FF_DEF_RAWVIDEO_DEMUXER2(mjpeg, "raw MJPEG video", mjpeg_probe, "mjpg,mjpeg,mpo", AV_CODEC_ID_MJPEG, AVFMT_GENERIC_INDEX|AVFMT_NOTIMESTAMPS) #endif #if CONFIG_MLP_DEMUXER @@ -214,5 +241,5 @@ AVInputFormat ff_shorten_demuxer = { #endif #if CONFIG_VC1_DEMUXER -FF_DEF_RAWVIDEO_DEMUXER(vc1, "raw VC-1", NULL, "vc1", AV_CODEC_ID_VC1) +FF_DEF_RAWVIDEO_DEMUXER2(vc1, "raw VC-1", NULL, "vc1", AV_CODEC_ID_VC1, AVFMT_GENERIC_INDEX|AVFMT_NOTIMESTAMPS) #endif diff --git a/libavformat/rawdec.h b/libavformat/rawdec.h index a548778..328a4fb 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[]; @@ -49,7 +49,7 @@ static const AVClass name ## _demuxer_class = {\ .version = LIBAVUTIL_VERSION_INT,\ }; -#define FF_DEF_RAWVIDEO_DEMUXER(shortname, longname, probe, ext, id)\ +#define FF_DEF_RAWVIDEO_DEMUXER2(shortname, longname, probe, ext, id, flag)\ FF_RAWVIDEO_DEMUXER_CLASS(shortname)\ AVInputFormat ff_ ## shortname ## _demuxer = {\ .name = #shortname,\ @@ -58,10 +58,13 @@ AVInputFormat ff_ ## shortname ## _demuxer = {\ .read_header = ff_raw_video_read_header,\ .read_packet = ff_raw_read_partial_packet,\ .extensions = ext,\ - .flags = AVFMT_GENERIC_INDEX,\ + .flags = flag,\ .raw_codec_id = id,\ .priv_data_size = sizeof(FFRawVideoDemuxerContext),\ .priv_class = &shortname ## _demuxer_class,\ }; +#define FF_DEF_RAWVIDEO_DEMUXER(shortname, longname, probe, ext, id)\ +FF_DEF_RAWVIDEO_DEMUXER2(shortname, longname, probe, ext, id, AVFMT_GENERIC_INDEX) + #endif /* AVFORMAT_RAWDEC_H */ diff --git a/libavformat/rawenc.c b/libavformat/rawenc.c index eb50626..8b50fe1 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 */ @@ -29,6 +29,16 @@ int ff_raw_write_packet(AVFormatContext *s, AVPacket *pkt) return 0; } +static int force_one_stream(AVFormatContext *s) +{ + if (s->nb_streams != 1) { + av_log(s, AV_LOG_ERROR, "%s files have exactly one stream\n", + s->oformat->name); + return AVERROR(EINVAL); + } + return 0; +} + /* Note: Do not forget to add new entries to the Makefile as well. */ #if CONFIG_AC3_MUXER @@ -39,6 +49,7 @@ AVOutputFormat ff_ac3_muxer = { .extensions = "ac3", .audio_codec = AV_CODEC_ID_AC3, .video_codec = AV_CODEC_ID_NONE, + .write_header = force_one_stream, .write_packet = ff_raw_write_packet, .flags = AVFMT_NOTIMESTAMPS, }; @@ -51,6 +62,7 @@ AVOutputFormat ff_adx_muxer = { .extensions = "adx", .audio_codec = AV_CODEC_ID_ADPCM_ADX, .video_codec = AV_CODEC_ID_NONE, + .write_header = force_one_stream, .write_packet = ff_raw_write_packet, .flags = AVFMT_NOTIMESTAMPS, }; @@ -63,6 +75,17 @@ AVOutputFormat ff_cavsvideo_muxer = { .extensions = "cavs", .audio_codec = AV_CODEC_ID_NONE, .video_codec = AV_CODEC_ID_CAVS, + .write_header = force_one_stream, + .write_packet = ff_raw_write_packet, + .flags = AVFMT_NOTIMESTAMPS, +}; +#endif + +#if CONFIG_DATA_MUXER +AVOutputFormat ff_data_muxer = { + .name = "data", + .long_name = NULL_IF_CONFIG_SMALL("raw data"), + .write_header = force_one_stream, .write_packet = ff_raw_write_packet, .flags = AVFMT_NOTIMESTAMPS, }; @@ -75,6 +98,7 @@ AVOutputFormat ff_dirac_muxer = { .extensions = "drc", .audio_codec = AV_CODEC_ID_NONE, .video_codec = AV_CODEC_ID_DIRAC, + .write_header = force_one_stream, .write_packet = ff_raw_write_packet, .flags = AVFMT_NOTIMESTAMPS, }; @@ -87,6 +111,7 @@ AVOutputFormat ff_dnxhd_muxer = { .extensions = "dnxhd", .audio_codec = AV_CODEC_ID_NONE, .video_codec = AV_CODEC_ID_DNXHD, + .write_header = force_one_stream, .write_packet = ff_raw_write_packet, .flags = AVFMT_NOTIMESTAMPS, }; @@ -100,6 +125,7 @@ AVOutputFormat ff_dts_muxer = { .extensions = "dts", .audio_codec = AV_CODEC_ID_DTS, .video_codec = AV_CODEC_ID_NONE, + .write_header = force_one_stream, .write_packet = ff_raw_write_packet, .flags = AVFMT_NOTIMESTAMPS, }; @@ -113,6 +139,7 @@ AVOutputFormat ff_eac3_muxer = { .extensions = "eac3", .audio_codec = AV_CODEC_ID_EAC3, .video_codec = AV_CODEC_ID_NONE, + .write_header = force_one_stream, .write_packet = ff_raw_write_packet, .flags = AVFMT_NOTIMESTAMPS, }; @@ -126,6 +153,21 @@ AVOutputFormat ff_g722_muxer = { .extensions = "g722", .audio_codec = AV_CODEC_ID_ADPCM_G722, .video_codec = AV_CODEC_ID_NONE, + .write_header = force_one_stream, + .write_packet = ff_raw_write_packet, + .flags = AVFMT_NOTIMESTAMPS, +}; +#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_header = force_one_stream, .write_packet = ff_raw_write_packet, .flags = AVFMT_NOTIMESTAMPS, }; @@ -139,6 +181,7 @@ AVOutputFormat ff_h261_muxer = { .extensions = "h261", .audio_codec = AV_CODEC_ID_NONE, .video_codec = AV_CODEC_ID_H261, + .write_header = force_one_stream, .write_packet = ff_raw_write_packet, .flags = AVFMT_NOTIMESTAMPS, }; @@ -152,6 +195,7 @@ AVOutputFormat ff_h263_muxer = { .extensions = "h263", .audio_codec = AV_CODEC_ID_NONE, .video_codec = AV_CODEC_ID_H263, + .write_header = force_one_stream, .write_packet = ff_raw_write_packet, .flags = AVFMT_NOTIMESTAMPS, }; @@ -164,6 +208,7 @@ AVOutputFormat ff_h264_muxer = { .extensions = "h264", .audio_codec = AV_CODEC_ID_NONE, .video_codec = AV_CODEC_ID_H264, + .write_header = force_one_stream, .write_packet = ff_raw_write_packet, .flags = AVFMT_NOTIMESTAMPS, }; @@ -201,6 +246,7 @@ AVOutputFormat ff_mjpeg_muxer = { .extensions = "mjpg,mjpeg", .audio_codec = AV_CODEC_ID_NONE, .video_codec = AV_CODEC_ID_MJPEG, + .write_header = force_one_stream, .write_packet = ff_raw_write_packet, .flags = AVFMT_NOTIMESTAMPS, }; @@ -213,6 +259,7 @@ AVOutputFormat ff_mlp_muxer = { .extensions = "mlp", .audio_codec = AV_CODEC_ID_MLP, .video_codec = AV_CODEC_ID_NONE, + .write_header = force_one_stream, .write_packet = ff_raw_write_packet, .flags = AVFMT_NOTIMESTAMPS, }; @@ -222,10 +269,11 @@ AVOutputFormat ff_mlp_muxer = { AVOutputFormat ff_mpeg1video_muxer = { .name = "mpeg1video", .long_name = NULL_IF_CONFIG_SMALL("raw MPEG-1 video"), - .mime_type = "video/x-mpeg", + .mime_type = "video/mpeg", .extensions = "mpg,mpeg,m1v", .audio_codec = AV_CODEC_ID_NONE, .video_codec = AV_CODEC_ID_MPEG1VIDEO, + .write_header = force_one_stream, .write_packet = ff_raw_write_packet, .flags = AVFMT_NOTIMESTAMPS, }; @@ -238,6 +286,7 @@ AVOutputFormat ff_mpeg2video_muxer = { .extensions = "m2v", .audio_codec = AV_CODEC_ID_NONE, .video_codec = AV_CODEC_ID_MPEG2VIDEO, + .write_header = force_one_stream, .write_packet = ff_raw_write_packet, .flags = AVFMT_NOTIMESTAMPS, }; @@ -255,18 +304,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", @@ -274,6 +311,20 @@ AVOutputFormat ff_truehd_muxer = { .extensions = "thd", .audio_codec = AV_CODEC_ID_TRUEHD, .video_codec = AV_CODEC_ID_NONE, + .write_header = force_one_stream, + .write_packet = ff_raw_write_packet, + .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_header = force_one_stream, .write_packet = ff_raw_write_packet, .flags = AVFMT_NOTIMESTAMPS, }; diff --git a/libavformat/rawenc.h b/libavformat/rawenc.h index daa5489..b552309 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 5f372c9..cbcae43 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 304f4cf..e8dc8f5 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; @@ -301,7 +301,7 @@ rdt_parse_packet (AVFormatContext *ctx, PayloadContext *rdt, AVStream *st, if (rdt->audio_pkt_cnt == 0) { int pos; - ffio_init_context(&pb, buf, len, 0, NULL, NULL, NULL, NULL); + ffio_init_context(&pb, (uint8_t *)buf, len, 0, NULL, NULL, NULL, NULL); flags = (flags & RTP_FLAG_KEY) ? 2 : 0; res = ff_rm_parse_packet (rdt->rmctx, &pb, st, rdt->rmst[st->index], len, pkt, &seq, flags, *timestamp); diff --git a/libavformat/rdt.h b/libavformat/rdt.h index bd16890..ce6026f 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 0000000..e1225d5 --- /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 (!avio_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 0000000..13a7b37 --- /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 (avio_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/replaygain.c b/libavformat/replaygain.c index 6c5b582..8b8c81a 100644 --- a/libavformat/replaygain.c +++ b/libavformat/replaygain.c @@ -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 */ @@ -71,11 +71,12 @@ int ff_replaygain_export_raw(AVStream *st, int32_t tg, uint32_t tp, { AVPacketSideData *sd, *tmp; AVReplayGain *replaygain; + int i; if (tg == INT32_MIN && ag == INT32_MIN) return 0; - for (int i = 0; i < st->nb_side_data; i++) { + for (i = 0; i < st->nb_side_data; i++) { AVPacketSideData *src_sd = &st->side_data[i]; if (src_sd->type == AV_PKT_DATA_REPLAYGAIN) diff --git a/libavformat/replaygain.h b/libavformat/replaygain.h index ea56c17..ceacb21 100644 --- a/libavformat/replaygain.h +++ b/libavformat/replaygain.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/riff.c b/libavformat/riff.c index 0e16ec7..735dea0 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') }, @@ -39,6 +42,7 @@ const AVCodecTag ff_codec_bmp_tags[] = { { AV_CODEC_ID_H264, MKTAG('V', '2', '6', '4') }, /* CCTV recordings */ { AV_CODEC_ID_H264, MKTAG('G', 'A', 'V', 'C') }, /* GeoVision camera */ { AV_CODEC_ID_H264, MKTAG('U', 'M', 'S', 'V') }, + { AV_CODEC_ID_H264, MKTAG('I', 'N', 'M', 'C') }, { 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') }, @@ -50,8 +54,7 @@ const AVCodecTag ff_codec_bmp_tags[] = { { 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') }, @@ -90,7 +93,6 @@ const AVCodecTag ff_codec_bmp_tags[] = { { AV_CODEC_ID_MPEG4, MKTAG('D', 'M', 'K', '2') }, { AV_CODEC_ID_MPEG4, MKTAG('D', 'Y', 'M', '4') }, { AV_CODEC_ID_MPEG4, MKTAG('D', 'I', 'G', 'I') }, - { AV_CODEC_ID_MPEG4, MKTAG('I', 'N', 'M', 'C') }, /* Ephv MPEG-4 */ { AV_CODEC_ID_MPEG4, MKTAG('E', 'P', 'H', 'V') }, { AV_CODEC_ID_MPEG4, MKTAG('E', 'M', '4', 'A') }, @@ -178,7 +180,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 */ @@ -238,7 +240,11 @@ const AVCodecTag ff_codec_bmp_tags[] = { { 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') }, @@ -276,6 +282,7 @@ 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') }, @@ -325,16 +332,19 @@ 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') }, @@ -343,6 +353,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') }, @@ -363,13 +375,15 @@ 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 }, + { 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 }, @@ -385,6 +399,7 @@ const AVCodecTag ff_codec_wav_tags[] = { { 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 }, @@ -403,7 +418,11 @@ const AVCodecTag ff_codec_wav_tags[] = { { AV_CODEC_ID_AAC, 0x1600 }, { AV_CODEC_ID_AAC_LATM, 0x1602 }, { AV_CODEC_ID_AC3, 0x2000 }, + /* There is no Microsoft Format Tag for E-AC3, the GUID has to be used */ + { AV_CODEC_ID_EAC3, 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 }, @@ -444,3 +463,11 @@ const struct AVCodecTag *avformat_get_riff_audio_tags(void) { return ff_codec_wav_tags; } + +const AVCodecGuid ff_codec_wav_guids[] = { + { AV_CODEC_ID_AC3, { 0x2C, 0x80, 0x6D, 0xE0, 0x46, 0xDB, 0xCF, 0x11, 0xB4, 0xD1, 0x00, 0x80, 0x5F, 0x6C, 0xBB, 0xEA } }, + { AV_CODEC_ID_ATRAC3P, { 0xBF, 0xAA, 0x23, 0xE9, 0x58, 0xCB, 0x71, 0x44, 0xA1, 0x19, 0xFF, 0xFA, 0x01, 0xE4, 0xCE, 0x62 } }, + { AV_CODEC_ID_EAC3, { 0xAF, 0x87, 0xFB, 0xA7, 0x02, 0x2D, 0xFB, 0x42, 0xA4, 0xD4, 0x05, 0xCD, 0x93, 0x84, 0x3B, 0xDD } }, + { AV_CODEC_ID_MP2, { 0x2B, 0x80, 0x6D, 0xE0, 0x46, 0xDB, 0xCF, 0x11, 0xB4, 0xD1, 0x00, 0x80, 0x5F, 0x6C, 0xBB, 0xEA } }, + { AV_CODEC_ID_NONE } +}; diff --git a/libavformat/riff.h b/libavformat/riff.h index ddfb0fa..88a77b0 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,28 @@ 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 ignore_extradata); + +/** + * Tell ff_put_wav_header() to use WAVEFORMATEX even for PCM codecs. + */ +#define FF_PUT_WAV_HEADER_FORCE_WAVEFORMATEX 0x00000001 + +/** + * Write WAVEFORMAT header structure. + * + * @param flags a combination of FF_PUT_WAV_HEADER_* constants + * + * @return the size or -1 on error + */ +int ff_put_wav_header(AVIOContext *pb, AVCodecContext *enc, int flags); -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(AVStream *st, int *au_rate, int *au_ssize, int *au_scale); @@ -91,10 +105,9 @@ 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)); -} +int ff_get_guid(AVIOContext *s, ff_asf_guid *g); +void ff_put_guid(AVIOContext *s, const ff_asf_guid *g); +const ff_asf_guid *get_codec_guid(enum AVCodecID id, const AVCodecGuid *av_guid); 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 74f93ac..09fee9d 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 */ @@ -29,13 +29,15 @@ #include "avio_internal.h" #include "riff.h" -const AVCodecGuid ff_codec_wav_guids[] = { - { AV_CODEC_ID_AC3, { 0x2C, 0x80, 0x6D, 0xE0, 0x46, 0xDB, 0xCF, 0x11, 0xB4, 0xD1, 0x00, 0x80, 0x5F, 0x6C, 0xBB, 0xEA } }, - { AV_CODEC_ID_ATRAC3P, { 0xBF, 0xAA, 0x23, 0xE9, 0x58, 0xCB, 0x71, 0x44, 0xA1, 0x19, 0xFF, 0xFA, 0x01, 0xE4, 0xCE, 0x62 } }, - { AV_CODEC_ID_EAC3, { 0xAF, 0x87, 0xFB, 0xA7, 0x02, 0x2D, 0xFB, 0x42, 0xA4, 0xD4, 0x05, 0xCD, 0x93, 0x84, 0x3B, 0xDD } }, - { AV_CODEC_ID_MP2, { 0x2B, 0x80, 0x6D, 0xE0, 0x46, 0xDB, 0xCF, 0x11, 0xB4, 0xD1, 0x00, 0x80, 0x5F, 0x6C, 0xBB, 0xEA } }, - { AV_CODEC_ID_NONE } -}; +int 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)); + return AVERROR_INVALIDDATA; + } + return 0; +} enum AVCodecID ff_codec_guid_get_id(const AVCodecGuid *guids, ff_asf_guid guid) { @@ -57,7 +59,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,14 +110,10 @@ 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_get_extradata(codec, pb, cbSize) < 0) return AVERROR(ENOMEM); - avio_read(pb, codec->extradata, codec->extradata_size); size -= cbSize; } @@ -132,7 +133,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 +156,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 +191,23 @@ int ff_read_riff_info(AVFormatContext *s, int64_t size) chunk_code = avio_rl32(pb); chunk_size = avio_rl32(pb); - + if (avio_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 +222,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 +232,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 b83533a..ef4d399 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 */ @@ -31,7 +31,7 @@ int64_t ff_start_tag(AVIOContext *pb, const char *tag) { ffio_wfourcc(pb, tag); - avio_wl32(pb, 0); + avio_wl32(pb, -1); return avio_tell(pb); } @@ -39,18 +39,23 @@ 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 */ /* returns the size or -1 on error */ -int ff_put_wav_header(AVIOContext *pb, AVCodecContext *enc) +int ff_put_wav_header(AVIOContext *pb, AVCodecContext *enc, int flags) { int bps, blkalign, bytespersec, frame_size; - int hdrsize = 18; + int hdrsize; + int64_t hdrstart = avio_tell(pb); int waveformatextensible; uint8_t temp[256]; uint8_t *riff_extradata = temp; @@ -68,6 +73,7 @@ int ff_put_wav_header(AVIOContext *pb, AVCodecContext *enc) waveformatextensible = (enc->channels > 2 && enc->channel_layout) || enc->sample_rate > 48000 || + enc->codec_id == AV_CODEC_ID_EAC3 || av_get_bits_per_sample(enc->codec_id) > 16; if (waveformatextensible) @@ -77,8 +83,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 +112,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 +127,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; } @@ -122,14 +136,12 @@ int ff_put_wav_header(AVIOContext *pb, AVCodecContext *enc) avio_wl16(pb, blkalign); /* block align */ avio_wl16(pb, bps); /* bits per sample */ if (enc->codec_id == AV_CODEC_ID_MP3) { - hdrsize += 12; bytestream_put_le16(&riff_extradata, 1); /* wID */ bytestream_put_le32(&riff_extradata, 2); /* fdwFlags */ bytestream_put_le16(&riff_extradata, 1152); /* nBlockSize */ bytestream_put_le16(&riff_extradata, 1); /* nFramesPerBlock */ bytestream_put_le16(&riff_extradata, 1393); /* nCodecDelay */ } else if (enc->codec_id == AV_CODEC_ID_MP2) { - hdrsize += 22; /* fwHeadLayer */ bytestream_put_le16(&riff_extradata, 2); /* dwHeadBitrate */ @@ -146,34 +158,45 @@ 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) { + 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; /* wSamplesPerBlock */ bytestream_put_le16(&riff_extradata, frame_size); } else if (enc->extradata_size) { riff_extradata_start = enc->extradata; riff_extradata = enc->extradata + enc->extradata_size; - hdrsize += enc->extradata_size; } /* write WAVEFORMATEXTENSIBLE extensions */ if (waveformatextensible) { - hdrsize += 22; + int write_channel_mask = enc->strict_std_compliance < FF_COMPLIANCE_NORMAL || + enc->channel_layout < 0x40000; /* 22 is WAVEFORMATEXTENSIBLE size */ avio_wl16(pb, riff_extradata - riff_extradata_start + 22); /* ValidBitsPerSample || SamplesPerBlock || Reserved */ avio_wl16(pb, bps); /* dwChannelMask */ - avio_wl32(pb, enc->channel_layout); + avio_wl32(pb, write_channel_mask ? enc->channel_layout : 0); /* GUID + next 3 */ + if (enc->codec_id == AV_CODEC_ID_EAC3) { + ff_put_guid(pb, get_codec_guid(enc->codec_id, ff_codec_wav_guids)); + } else { avio_wl32(pb, enc->codec_tag); avio_wl32(pb, 0x00100000); avio_wl32(pb, 0xAA000080); avio_wl32(pb, 0x719B3800); - } else { + } + } else if ((flags & FF_PUT_WAV_HEADER_FORCE_WAVEFORMATEX) || + enc->codec_tag != 0x0001 /* PCM */ || + riff_extradata - riff_extradata_start) { + /* WAVEFORMATEX */ avio_wl16(pb, riff_extradata - riff_extradata_start); /* cbSize */ - } + } /* else PCMWAVEFORMAT */ avio_write(pb, riff_extradata_start, riff_extradata - riff_extradata_start); + hdrsize = avio_tell(pb) - hdrstart; if (hdrsize & 1) { hdrsize++; avio_w8(pb, 0); @@ -184,10 +207,10 @@ int ff_put_wav_header(AVIOContext *pb, AVCodecContext *enc) /* BITMAPINFOHEADER header */ void ff_put_bmp_header(AVIOContext *pb, AVCodecContext *enc, - const AVCodecTag *tags, int for_asf) + const AVCodecTag *tags, int for_asf, int ignore_extradata) { /* size */ - avio_wl32(pb, 40 + enc->extradata_size); + avio_wl32(pb, 40 + (ignore_extradata ? 0 : enc->extradata_size)); avio_wl32(pb, enc->width); //We always store RGB TopDown avio_wl32(pb, enc->codec_tag ? enc->height : -enc->height); @@ -197,16 +220,18 @@ 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); avio_wl32(pb, 0); - avio_write(pb, enc->extradata, enc->extradata_size); + if (!ignore_extradata) { + avio_write(pb, enc->extradata, enc->extradata_size); - if (!for_asf && enc->extradata_size & 1) - avio_w8(pb, 0); + if (!for_asf && enc->extradata_size & 1) + avio_w8(pb, 0); + } } void ff_parse_specific_params(AVStream *st, int *au_rate, @@ -294,3 +319,19 @@ void ff_riff_write_info(AVFormatContext *s) ff_riff_write_info_tag(s->pb, t->key, t->value); ff_end_tag(pb, list_pos); } + +void ff_put_guid(AVIOContext *s, const ff_asf_guid *g) +{ + av_assert0(sizeof(*g) == 16); + avio_write(s, *g, sizeof(*g)); +} + +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; +} diff --git a/libavformat/rl2.c b/libavformat/rl2.c index 5d30bf8..d354339 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 */ @@ -109,10 +109,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); @@ -131,17 +127,16 @@ 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_get_extradata(st->codec, pb, st->codec->extradata_size) < 0) return AVERROR(ENOMEM); - if(avio_read(pb,st->codec->extradata,st->codec->extradata_size) != - st->codec->extradata_size) - return AVERROR(EIO); - /** 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; @@ -235,7 +230,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 761be3f..0591a17 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 a06ea01..a72e8b7 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 79a0756..f53ab8c 100644 --- a/libavformat/rmdec.c +++ b/libavformat/rmdec.c @@ -2,31 +2,33 @@ * "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 <inttypes.h> +#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" @@ -86,12 +88,8 @@ static int rm_read_extradata(AVIOContext *pb, AVCodecContext *avctx, unsigned si { if (size >= 1<<24) return -1; - avctx->extradata = av_mallocz(size + FF_INPUT_BUFFER_PADDING_SIZE); - if (!avctx->extradata) + if (ff_get_extradata(avctx, pb, size) < 0) return AVERROR(ENOMEM); - avctx->extradata_size = avio_read(pb, avctx->extradata, size); - if (avctx->extradata_size != size) - return AVERROR(EIO); return 0; } @@ -99,6 +97,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); @@ -128,9 +127,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") @@ -140,6 +142,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; @@ -149,6 +153,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 */ @@ -158,7 +163,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 */ @@ -175,6 +184,7 @@ static int rm_read_audio_stream_info(AVFormatContext *s, AVIOContext *pb, avio_read(pb, buf, 4); buf[4] = 0; } else { + AV_WL32(buf, 0); get_str8(pb, buf, sizeof(buf)); /* desc */ ast->deint_id = AV_RL32(buf); get_str8(pb, buf, sizeof(buf)); /* desc */ @@ -197,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; @@ -223,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); @@ -240,27 +255,23 @@ static int rm_read_audio_stream_info(AVFormatContext *s, AVIOContext *pb, } break; } - if (ast->deint_id == DEINT_ID_INT4 || - ast->deint_id == DEINT_ID_GENR || - ast->deint_id == DEINT_ID_SIPR) { - if (st->codec->block_align <= 0 || - ast->audio_framesize * sub_packet_h > (unsigned)INT_MAX || - ast->audio_framesize * sub_packet_h < st->codec->block_align) - return AVERROR_INVALIDDATA; - if (av_new_packet(&ast->pkt, ast->audio_framesize * sub_packet_h) < 0) - return AVERROR(ENOMEM); - } switch (ast->deint_id) { case DEINT_ID_INT4: if (ast->coded_framesize > ast->audio_framesize || sub_packet_h <= 1 || ast->coded_framesize * sub_packet_h > (2 + (sub_packet_h & 1)) * ast->audio_framesize) return AVERROR_INVALIDDATA; + if (ast->coded_framesize * sub_packet_h != 2*ast->audio_framesize) { + avpriv_request_sample(s, "mismatching interleaver parameters"); + return AVERROR_INVALIDDATA; + } break; case DEINT_ID_GENR: if (ast->sub_packet_size <= 0 || ast->sub_packet_size > ast->audio_framesize) return AVERROR_INVALIDDATA; + if (ast->audio_framesize % ast->sub_packet_size) + return AVERROR_INVALIDDATA; break; case DEINT_ID_SIPR: case DEINT_ID_INT0: @@ -268,9 +279,19 @@ static int rm_read_audio_stream_info(AVFormatContext *s, AVIOContext *pb, case DEINT_ID_VBRF: break; default: - av_log(NULL, 0 ,"Unknown interleaver %"PRIX32"\n", ast->deint_id); + av_log(s, AV_LOG_ERROR ,"Unknown interleaver %"PRIX32"\n", ast->deint_id); return AVERROR_INVALIDDATA; } + if (ast->deint_id == DEINT_ID_INT4 || + ast->deint_id == DEINT_ID_GENR || + ast->deint_id == DEINT_ID_SIPR) { + if (st->codec->block_align <= 0 || + ast->audio_framesize * sub_packet_h > (unsigned)INT_MAX || + ast->audio_framesize * sub_packet_h < st->codec->block_align) + return AVERROR_INVALIDDATA; + if (av_new_packet(&ast->pkt, ast->audio_framesize * sub_packet_h) < 0) + return AVERROR(ENOMEM); + } if (read_all) { avio_r8(pb); @@ -284,7 +305,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; @@ -307,11 +328,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); @@ -334,6 +382,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; @@ -428,7 +479,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); @@ -443,7 +494,7 @@ static int rm_read_header(AVFormatContext *s) avio_skip(pb, tag_size - 8); for(;;) { - if (pb->eof_reached) + if (avio_feof(pb)) return -1; tag = avio_rl32(pb); tag_size = avio_rb32(pb); @@ -465,7 +516,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 */ @@ -489,12 +541,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'): @@ -547,7 +601,7 @@ static int sync(AVFormatContext *s, int64_t *timestamp, int *flags, int *stream_ AVStream *st; uint32_t state=0xFFFFFFFF; - while(!pb->eof_reached){ + while(!avio_feof(pb)){ int len, num, i; *pos= avio_tell(pb) - 3; if(rm->remaining_len > 0){ @@ -614,8 +668,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; @@ -628,34 +684,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; @@ -664,12 +733,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; @@ -714,17 +789,29 @@ rm_ac3_swap_bytes (AVStream *st, AVPacket *pkt) } } +static int readfull(AVFormatContext *s, AVIOContext *pb, uint8_t *dst, int n) { + int ret = avio_read(pb, dst, n); + if (ret != n) { + if (ret >= 0) memset(dst + ret, 0, n - ret); + else memset(dst , 0, n); + av_log(s, AV_LOG_ERROR, "Failed to fully read block\n"); + } + return ret; +} + int ff_rm_parse_packet (AVFormatContext *s, AVIOContext *pb, AVStream *st, RMStream *ast, int len, AVPacket *pkt, 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) || @@ -744,14 +831,14 @@ ff_rm_parse_packet (AVFormatContext *s, AVIOContext *pb, switch (ast->deint_id) { case DEINT_ID_INT4: for (x = 0; x < h/2; x++) - avio_read(pb, ast->pkt.data+x*2*w+y*cfs, cfs); + readfull(s, pb, ast->pkt.data+x*2*w+y*cfs, cfs); break; case DEINT_ID_GENR: for (x = 0; x < w/sps; x++) - avio_read(pb, ast->pkt.data+sps*(h*x+((h+1)/2)*(y&1)+(y>>1)), sps); + readfull(s, pb, ast->pkt.data+sps*(h*x+((h+1)/2)*(y&1)+(y>>1)), sps); break; case DEINT_ID_SIPR: - avio_read(pb, ast->pkt.data + y * w, w); + readfull(s, pb, ast->pkt.data + y * w, w); break; } @@ -810,13 +897,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); @@ -835,7 +923,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; @@ -844,7 +932,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) { @@ -863,11 +953,13 @@ static int rm_read_packet(AVFormatContext *s, AVPacket *pkt) st = s->streams[i]; } - if(len<0 || s->pb->eof_reached) + if(len<0 || avio_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) @@ -919,7 +1011,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; @@ -951,6 +1045,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"), @@ -960,6 +1066,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 9ff9f31..2e50ed3 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++) { AVStream *st = s->streams[n]; diff --git a/libavformat/rmsipr.c b/libavformat/rmsipr.c index 89fbd0e..14ec8ea 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 c8d3d0a..2837336 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 b72b8d0..c1229e8 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 */ @@ -60,7 +60,7 @@ static int read_line(AVIOContext * pb, char* line, int bufsize) break; if (b == '\n') { line[i] = '\0'; - return 0; + return avio_feof(pb) ? -1 : 0; } line[i] = b; } @@ -170,9 +170,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; } @@ -222,11 +221,9 @@ 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 %"PRId32" not supported yet!\n", - audio_format); - } + if (ast->codec->codec_id == AV_CODEC_ID_NONE) + avpriv_request_sample(s, "Audio format %"PRId32, + audio_format); avpriv_set_pts_info(ast, 32, 1, ast->codec->bit_rate); } else { for (i = 0; i < 3; i++) @@ -254,12 +251,14 @@ 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, - &offset, &video_size, &audio_size)) + if (3 != sscanf(line, "%"SCNd64" , %"SCNd64" ; %"SCNd64, + &offset, &video_size, &audio_size)) { error = -1; + continue; + } av_add_index_entry(vst, offset, i * rpl->frames_per_chunk, video_size, rpl->frames_per_chunk, 0); if (ast) @@ -289,7 +288,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 0000000..bb2f3bc --- /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 0; + if (AV_RL32(p->buf + 8) > 256 || !AV_RL32(p->buf + 8)) + return AVPROBE_SCORE_MAX / 8; + if (AV_RL32(p->buf + 16) > 8*48000 || !AV_RL32(p->buf + 16)) + return AVPROBE_SCORE_MAX / 8; + return AVPROBE_SCORE_MAX; +} + +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. */ + + start = avio_rl32(pb); + + if (ff_get_extradata(codec, s->pb, 32) < 0) + return AVERROR(ENOMEM); + + 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 (avio_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 4ce61e7..7d88f0f 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 e3e88ea..1f65dd9 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 1612572..6e3ea0a 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 8ebcf81..6da9594 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 dc7ba5b..8fc8040 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 dfdd029..2312527 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 2799433..590a8c8 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 38c2f3d..7d6734d 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 5de8bde..cf262fc 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 34c68fb..0334ba5 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 37fbb5f..92172dc 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 */ @@ -279,6 +279,7 @@ static int rtmp_packet_read_one_chunk(URLContext *h, RTMPPacket *p, prev->data = p->data; prev->read = p->read; prev->offset = p->offset; + p->data = NULL; return AVERROR(EAGAIN); } diff --git a/libavformat/rtmppkt.h b/libavformat/rtmppkt.h index 149c153..a082b45 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 4aaa420..3cde966 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 @@ -274,9 +274,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; @@ -323,7 +320,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; @@ -420,6 +417,7 @@ static int read_connect(URLContext *s, RTMPContext *rt) if (pkt.type == RTMP_PT_CHUNK_SIZE) { if ((ret = handle_chunk_size(s, &pkt)) < 0) return ret; + ff_rtmp_packet_destroy(&pkt); if ((ret = ff_rtmp_packet_read(rt->stream, &pkt, rt->in_chunk_size, &rt->prev_pkt[0], &rt->nb_prev_pkt[0])) < 0) @@ -1717,18 +1715,23 @@ static int handle_connect_error(URLContext *s, const char *desc) char *value = strchr(ptr, '='); if (next) *next++ = '\0'; - if (value) + if (value) { *value++ = '\0'; - if (!strcmp(ptr, "user")) { - user = value; - } else if (!strcmp(ptr, "salt")) { - salt = value; - } else if (!strcmp(ptr, "opaque")) { - opaque = value; - } else if (!strcmp(ptr, "challenge")) { - challenge = value; - } else if (!strcmp(ptr, "nonce")) { - nonce = value; + if (!strcmp(ptr, "user")) { + user = value; + } else if (!strcmp(ptr, "salt")) { + salt = value; + } else if (!strcmp(ptr, "opaque")) { + opaque = value; + } else if (!strcmp(ptr, "challenge")) { + challenge = value; + } else if (!strcmp(ptr, "nonce")) { + nonce = value; + } else { + av_log(s, AV_LOG_INFO, "Ignoring unsupported var %s\n", ptr); + } + } else { + av_log(s, AV_LOG_WARNING, "Variable %s has NULL value\n", ptr); } ptr = next; } @@ -2325,7 +2328,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; @@ -2559,6 +2562,10 @@ reconnect: 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 0a3c411..4d41350 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 feaf167..66a4f3a 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 308a48a..a78af87 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 */ @@ -32,6 +32,12 @@ #define MIN_FEEDBACK_INTERVAL 200000 /* 200 ms in us */ +static RTPDynamicProtocolHandler gsm_dynamic_handler = { + .enc_name = "GSM", + .codec_type = AVMEDIA_TYPE_AUDIO, + .codec_id = AV_CODEC_ID_GSM, +}; + static RTPDynamicProtocolHandler realmedia_mp3_dynamic_handler = { .enc_name = "X-MP3-draft-00", .codec_type = AVMEDIA_TYPE_AUDIO, @@ -66,10 +72,13 @@ void ff_register_rtp_dynamic_payload_handlers(void) ff_register_dynamic_payload_handler(&ff_g726_24_dynamic_handler); ff_register_dynamic_payload_handler(&ff_g726_32_dynamic_handler); ff_register_dynamic_payload_handler(&ff_g726_40_dynamic_handler); + ff_register_dynamic_payload_handler(&ff_h261_dynamic_handler); ff_register_dynamic_payload_handler(&ff_h263_1998_dynamic_handler); ff_register_dynamic_payload_handler(&ff_h263_2000_dynamic_handler); ff_register_dynamic_payload_handler(&ff_h263_rfc2190_dynamic_handler); ff_register_dynamic_payload_handler(&ff_h264_dynamic_handler); + ff_register_dynamic_payload_handler(&ff_h265_dynamic_handler); + ff_register_dynamic_payload_handler(&ff_hevc_dynamic_handler); ff_register_dynamic_payload_handler(&ff_ilbc_dynamic_handler); ff_register_dynamic_payload_handler(&ff_jpeg_dynamic_handler); ff_register_dynamic_payload_handler(&ff_mp4a_latm_dynamic_handler); @@ -90,6 +99,7 @@ void ff_register_rtp_dynamic_payload_handlers(void) ff_register_dynamic_payload_handler(&ff_theora_dynamic_handler); ff_register_dynamic_payload_handler(&ff_vorbis_dynamic_handler); ff_register_dynamic_payload_handler(&ff_vp8_dynamic_handler); + ff_register_dynamic_payload_handler(&gsm_dynamic_handler); ff_register_dynamic_payload_handler(&opus_dynamic_handler); ff_register_dynamic_payload_handler(&realmedia_mp3_dynamic_handler); ff_register_dynamic_payload_handler(&speex_dynamic_handler); @@ -839,7 +849,7 @@ int ff_parse_fmtp(AVFormatContext *s, 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 46b08ce..7b41cc4 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 */ @@ -173,9 +173,9 @@ struct RTPDemuxContext { /*@}*/ /* rtcp sender statistics receive */ - int64_t last_rtcp_ntp_time; + uint64_t last_rtcp_ntp_time; int64_t last_rtcp_reception_time; - int64_t first_rtcp_ntp_time; + uint64_t first_rtcp_ntp_time; uint32_t last_rtcp_timestamp; int64_t rtcp_ts_offset; diff --git a/libavformat/rtpdec_amr.c b/libavformat/rtpdec_amr.c index 86348bb..31acfe7 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 61b1419..8e19654 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 */ @@ -188,7 +188,7 @@ static int asfrtp_parse_packet(AVFormatContext *s, PayloadContext *asf, av_freep(&asf->buf); - ffio_init_context(pb, buf, len, 0, NULL, NULL, NULL, NULL); + ffio_init_context(pb, (uint8_t *)buf, len, 0, NULL, NULL, NULL, NULL); while (avio_tell(pb) + 4 < len) { int start_off = avio_tell(pb); diff --git a/libavformat/rtpdec_formats.h b/libavformat/rtpdec_formats.h index e5f4ccb..d915fab 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 */ @@ -31,6 +31,10 @@ */ int ff_wms_parse_sdp_a_line(AVFormatContext *s, const char *p); +int ff_h261_handle_packet(AVFormatContext *ctx, PayloadContext *data, + AVStream *st, AVPacket *pkt, uint32_t *timestamp, + const uint8_t *buf, int len, uint16_t seq, int flags); + int ff_h263_handle_packet(AVFormatContext *ctx, PayloadContext *data, AVStream *st, AVPacket *pkt, uint32_t *timestamp, const uint8_t *buf, int len, uint16_t seq, int flags); @@ -41,10 +45,13 @@ extern RTPDynamicProtocolHandler ff_g726_16_dynamic_handler; extern RTPDynamicProtocolHandler ff_g726_24_dynamic_handler; extern RTPDynamicProtocolHandler ff_g726_32_dynamic_handler; extern RTPDynamicProtocolHandler ff_g726_40_dynamic_handler; +extern RTPDynamicProtocolHandler ff_h261_dynamic_handler; extern RTPDynamicProtocolHandler ff_h263_1998_dynamic_handler; extern RTPDynamicProtocolHandler ff_h263_2000_dynamic_handler; extern RTPDynamicProtocolHandler ff_h263_rfc2190_dynamic_handler; extern RTPDynamicProtocolHandler ff_h264_dynamic_handler; +extern RTPDynamicProtocolHandler ff_h265_dynamic_handler; +extern RTPDynamicProtocolHandler ff_hevc_dynamic_handler; extern RTPDynamicProtocolHandler ff_ilbc_dynamic_handler; extern RTPDynamicProtocolHandler ff_jpeg_dynamic_handler; extern RTPDynamicProtocolHandler ff_mp4a_latm_dynamic_handler; diff --git a/libavformat/rtpdec_g726.c b/libavformat/rtpdec_g726.c index 7b3f6cb..7e9ef56 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_h261.c b/libavformat/rtpdec_h261.c new file mode 100644 index 0000000..b902d2a --- /dev/null +++ b/libavformat/rtpdec_h261.c @@ -0,0 +1,205 @@ +/* + * RTP parser for H.261 payload format (RFC 4587) + * Copyright (c) 2014 Thomas Volkert <thomas@homer-conferencing.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 "avformat.h" +#include "rtpdec_formats.h" +#include "libavcodec/get_bits.h" + +#define RTP_H261_PAYLOAD_HEADER_SIZE 4 + +struct PayloadContext { + AVIOContext *buf; + uint8_t endbyte; + int endbyte_bits; + uint32_t timestamp; +}; + +static PayloadContext *h261_new_context(void) +{ + return av_mallocz(sizeof(PayloadContext)); +} + +static void h261_free_dyn_buffer(AVIOContext **dyn_buf) +{ + uint8_t *ptr_dyn_buffer; + avio_close_dyn_buf(*dyn_buf, &ptr_dyn_buffer); + av_free(ptr_dyn_buffer); + *dyn_buf = NULL; +} + +static void h261_free_context(PayloadContext *pl_ctx) +{ + /* return if context is invalid */ + if (!pl_ctx) + return; + + /* free buffer if it is valid */ + if (pl_ctx->buf) { + h261_free_dyn_buffer(&pl_ctx->buf); + } + + /* free context */ + av_free(pl_ctx); +} + +static av_cold int h261_init(AVFormatContext *ctx, int st_index, + PayloadContext *data) +{ + //av_log(ctx, AV_LOG_DEBUG, "h261_init() for stream %d\n", st_index); + + if (st_index < 0) + return 0; + + ctx->streams[st_index]->need_parsing = AVSTREAM_PARSE_FULL; + + return 0; +} + +int ff_h261_handle_packet(AVFormatContext *ctx, PayloadContext *data, + AVStream *st, AVPacket *pkt, uint32_t *timestamp, + const uint8_t *buf, int len, uint16_t seq, int flags) +{ + int sbit, ebit, gobn, mbap, quant; + int res; + + //av_log(ctx, AV_LOG_DEBUG, "got h261 RTP packet with time: %u\n", timestamp); + + /* drop data of previous packets in case of non-continuous (loss) packet stream */ + if (data->buf && data->timestamp != *timestamp) { + h261_free_dyn_buffer(&data->buf); + } + + /* sanity check for size of input packet */ + if (len < 5 /* 4 bytes header and 1 byte payload at least */) { + av_log(ctx, AV_LOG_ERROR, "Too short H.261 RTP packet\n"); + return AVERROR_INVALIDDATA; + } + + /* + decode the H.261 payload header according to section 4.1 of RFC 4587: + (uses 4 bytes between RTP header and H.261 stream per packet) + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |SBIT |EBIT |I|V| GOBN | MBAP | QUANT | HMVD | VMVD | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Start bit position (SBIT): 3 bits + End bit position (EBIT): 3 bits + INTRA-frame encoded data (I): 1 bit + Motion Vector flag (V): 1 bit + GOB number (GOBN): 4 bits + Macroblock address predictor (MBAP): 5 bits + Quantizer (QUANT): 5 bits + Horizontal motion vector data (HMVD): 5 bits + Vertical motion vector data (VMVD): 5 bits + + */ + sbit = (buf[0] >> 5) & 0x07; + ebit = (buf[0] >> 2) & 0x07; + gobn = (buf[1] >> 4) & 0x0f; + mbap = ((buf[1] << 1) & 0x1e) | ((buf[2] >> 7) & 0x01); + quant = (buf[2] >> 2) & 0x1f; + + /* pass the H.261 payload header and continue with the actual payload */ + buf += RTP_H261_PAYLOAD_HEADER_SIZE; + len -= RTP_H261_PAYLOAD_HEADER_SIZE; + + /* start frame buffering with new dynamic buffer */ + if (!data->buf) { + /* sanity check: a new frame starts with gobn=0, sbit=0, mbap=0, uqnat=0 */ + if (!gobn && !sbit && !mbap && !quant){ + res = avio_open_dyn_buf(&data->buf); + if (res < 0) + return res; + /* update the timestamp in the frame packet with the one from the RTP packet */ + data->timestamp = *timestamp; + } else { + /* frame not started yet, need more packets */ + return AVERROR(EAGAIN); + } + } + + /* do the "byte merging" at the boundaries of two consecutive frame fragments */ + if (data->endbyte_bits || sbit) { + if (data->endbyte_bits == sbit) { + data->endbyte |= buf[0] & (0xff >> sbit); + data->endbyte_bits = 0; + buf++; + len--; + avio_w8(data->buf, data->endbyte); + } else { + /* ebit/sbit values inconsistent, assuming packet loss */ + GetBitContext gb; + init_get_bits(&gb, buf, len*8 - ebit); + skip_bits(&gb, sbit); + if (data->endbyte_bits) { + data->endbyte |= get_bits(&gb, 8 - data->endbyte_bits); + avio_w8(data->buf, data->endbyte); + } + while (get_bits_left(&gb) >= 8) + avio_w8(data->buf, get_bits(&gb, 8)); + data->endbyte_bits = get_bits_left(&gb); + if (data->endbyte_bits) + data->endbyte = get_bits(&gb, data->endbyte_bits) << + (8 - data->endbyte_bits); + ebit = 0; + len = 0; + } + } + if (ebit) { + if (len > 0) + avio_write(data->buf, buf, len - 1); + data->endbyte_bits = 8 - ebit; + data->endbyte = buf[len - 1] & (0xff << ebit); + } else { + avio_write(data->buf, buf, len); + } + + /* RTP marker bit means: last fragment of current frame was received; + otherwise, an additional fragment is needed for the current frame */ + if (!(flags & RTP_FLAG_MARKER)) + return AVERROR(EAGAIN); + + /* write the completed last byte from the "byte merging" */ + if (data->endbyte_bits) + avio_w8(data->buf, data->endbyte); + data->endbyte_bits = 0; + + /* close frame buffering and create resulting A/V packet */ + res = ff_rtp_finalize_packet(pkt, &data->buf, st->index); + if (res < 0) + return res; + + return 0; +} + +RTPDynamicProtocolHandler ff_h261_dynamic_handler = { + .enc_name = "H261", + .codec_type = AVMEDIA_TYPE_VIDEO, + .codec_id = AV_CODEC_ID_H261, + .init = h261_init, + .parse_packet = ff_h261_handle_packet, + .alloc = h261_new_context, + .free = h261_free_context, + .static_payload_id = 31, +}; diff --git a/libavformat/rtpdec_h263.c b/libavformat/rtpdec_h263.c index b371491..ae4cc27 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 116db75..a227901 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 abf1f39..4dbbae7 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 */ @@ -71,7 +71,7 @@ static int sdp_parse_fmtp_config_h264(AVFormatContext *s, { AVCodecContext *codec = stream->codec; assert(codec->codec_id == AV_CODEC_ID_H264); - assert(h264_data != NULL); + assert(h264_data); if (!strcmp(attr, "packetization-mode")) { av_log(s, AV_LOG_DEBUG, "RTP Packetization Mode: %d\n", atoi(value)); diff --git a/libavformat/rtpdec_hevc.c b/libavformat/rtpdec_hevc.c new file mode 100644 index 0000000..4504e12 --- /dev/null +++ b/libavformat/rtpdec_hevc.c @@ -0,0 +1,407 @@ +/* + * RTP parser for HEVC/H.265 payload format (draft version 6) + * Copyright (c) 2014 Thomas Volkert <thomas@homer-conferencing.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 "rtpdec.h" + +#define RTP_HEVC_PAYLOAD_HEADER_SIZE 2 +#define RTP_HEVC_FU_HEADER_SIZE 1 +#define RTP_HEVC_DONL_FIELDS_SIZE 2 +#define HEVC_SPECIFIED_NAL_UNIT_TYPES 48 + +struct PayloadContext { + /* SDP out-of-band signaling data */ + int using_donl_field; + int profile_id; + /* debugging */ +#ifdef DEBUG + int packets_received[HEVC_SPECIFIED_NAL_UNIT_TYPES]; +#endif +}; + +#ifdef DEBUG +#define COUNT_HEVC_NAL_TYPE(data, nal_type) if (nal_type < HEVC_SPECIFIED_NAL_UNIT_TYPES) data->packets_received[(nal_type)]++ +#else +#define COUNT_HEVC_NAL_TYPE(data, nal_type) do { } while (0) +#endif + +static const uint8_t start_sequence[] = { 0x00, 0x00, 0x00, 0x01 }; + +static PayloadContext *hevc_new_context(void) +{ + return av_mallocz(sizeof(PayloadContext)); +} + +static av_cold void hevc_free_context(PayloadContext *data) +{ +#ifdef DEBUG + int i; + + for (i = 0; i < HEVC_SPECIFIED_NAL_UNIT_TYPES; i++) { + if (data->packets_received[i]) + av_log(NULL, AV_LOG_DEBUG, "Received %d packets of NAL unit type %d\n", + data->packets_received[i], i); + } +#endif + + av_free(data); +} + +static av_cold int hevc_init(AVFormatContext *ctx, int st_index, + PayloadContext *data) +{ +#ifdef DEBUG + av_log(ctx, AV_LOG_DEBUG, "hevc_init() for stream %d\n", st_index); +#endif + + if (st_index < 0) + return 0; + + ctx->streams[st_index]->need_parsing = AVSTREAM_PARSE_FULL; + + return 0; +} + +static av_cold int hevc_sdp_parse_fmtp_config(AVFormatContext *s, + AVStream *stream, + PayloadContext *hevc_data, + char *attr, char *value) +{ + +#ifdef DEBUG + av_log(s, AV_LOG_DEBUG, "SDP: fmtp value %s is %s\n", attr, value); +#endif + + /* profile-space: 0-3 */ + /* profile-id: 0-31 */ + if (!strcmp(attr, "profile-id")) { + hevc_data->profile_id = atoi(value); + +#ifdef DEBUG + av_log(s, AV_LOG_DEBUG, "SDP: found profile-id: %d\n", hevc_data->profile_id); +#endif + } + + /* tier-flag: 0-1 */ + /* level-id: 0-255 */ + /* interop-constraints: [base16] */ + /* profile-compatibility-indicator: [base16] */ + /* sprop-sub-layer-id: 0-6, defines highest possible value for TID, default: 6 */ + /* recv-sub-layer-id: 0-6 */ + /* max-recv-level-id: 0-255 */ + /* tx-mode: MSM,SSM */ + /* sprop-vps: [base64] */ + /* sprop-sps: [base64] */ + /* sprop-pps: [base64] */ + /* sprop-sei: [base64] */ + /* max-lsr, max-lps, max-cpb, max-dpb, max-br, max-tr, max-tc */ + /* max-fps */ + + /* sprop-max-don-diff: 0-32767 + + When the RTP stream depends on one or more other RTP + streams (in this case tx-mode MUST be equal to "MSM" and + MSM is in use), this parameter MUST be present and the + value MUST be greater than 0. + */ + if (!strcmp(attr, "sprop-max-don-diff")) { + if(atoi(value) > 0) + hevc_data->using_donl_field = 1; + +#ifdef DEBUG + av_log(s, AV_LOG_DEBUG, "SDP: found sprop-max-don-diff in SDP, DON field usage is: %d\n", hevc_data->using_donl_field); +#endif + } + + /* sprop-depack-buf-nalus: 0-32767 */ + if (!strcmp(attr, "sprop-depack-buf-nalus")) { + if(atoi(value) > 0) + hevc_data->using_donl_field = 1; + +#ifdef DEBUG + av_log(s, AV_LOG_DEBUG, "SDP: found sprop-depack-buf-nalus in SDP, DON field usage is: %d\n", hevc_data->using_donl_field); +#endif + } + + /* sprop-depack-buf-bytes: 0-4294967295 */ + /* depack-buf-cap */ + /* sprop-segmentation-id: 0-3 */ + /* sprop-spatial-segmentation-idc: [base16] */ + /* dec-parallel-ca: */ + /* include-dph */ + + return 0; +} + +static av_cold int hevc_parse_sdp_line(AVFormatContext *ctx, int st_index, + PayloadContext *hevc_data, const char *line) +{ + AVStream *current_stream; + AVCodecContext *codec; + const char *sdp_line_ptr = line; + +#ifdef DEBUG + av_log(ctx, AV_LOG_DEBUG, "parse_hevc_sdp_line() got SDP line %s\n", line); +#endif + + if (st_index < 0) + return 0; + + current_stream = ctx->streams[st_index]; + codec = current_stream->codec; + + if (av_strstart(sdp_line_ptr, "framesize:", &sdp_line_ptr)) { + char str_video_width[50]; + char *str_video_width_ptr = str_video_width; + + /** + * parse "a=framesize:96 320-240" + */ + /* ignore spaces */ + while (*sdp_line_ptr && *sdp_line_ptr == ' ') + sdp_line_ptr++; + /* ignore RTP payload ID */ + while (*sdp_line_ptr && *sdp_line_ptr != ' ') + sdp_line_ptr++; + /* ignore spaces */ + while (*sdp_line_ptr && *sdp_line_ptr == ' ') + sdp_line_ptr++; + /* extract the actual video resolution description */ + while (*sdp_line_ptr && *sdp_line_ptr != '-' && (str_video_width_ptr - str_video_width) < sizeof(str_video_width) - 1) + *str_video_width_ptr++ = *sdp_line_ptr++; + /* add trailing zero byte */ + *str_video_width_ptr = '\0'; + + /* determine the width value */ + codec->width = atoi(str_video_width); + // jump beyond the "-" and determine the height value + codec->height = atoi(sdp_line_ptr + 1); + } else if (av_strstart(sdp_line_ptr, "fmtp:", &sdp_line_ptr)) { + return ff_parse_fmtp(ctx, current_stream, hevc_data, sdp_line_ptr, hevc_sdp_parse_fmtp_config); + } else if (av_strstart(sdp_line_ptr, "cliprect:", &sdp_line_ptr)) { + // could use this if we wanted. + } + + return 0; +} + +static int hevc_handle_packet(AVFormatContext *ctx, PayloadContext *rtp_hevc_ctx, + AVStream *st, AVPacket *pkt, uint32_t *timestamp, + const uint8_t *buf, int len, uint16_t seq, + int flags) +{ + const uint8_t *rtp_pl = buf; + int tid, lid, nal_type; + int first_fragment, last_fragment, fu_type; + uint8_t new_nal_header[2]; + int res=0; + + /* sanity check for size of input packet */ + if (len < 3 /* 2 bytes header and 1 byte payload at least */) { + av_log(ctx, AV_LOG_ERROR, "Too short RTP/HEVC packet, got %d bytes\n", len); + return AVERROR_INVALIDDATA; + } + + /* + decode the HEVC payload header according to section 4 of draft version 6: + + 0 1 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |F| Type | LayerId | TID | + +-------------+-----------------+ + + Forbidden zero (F): 1 bit + NAL unit type (Type): 6 bits + NUH layer ID (LayerId): 6 bits + NUH temporal ID plus 1 (TID): 3 bits + */ + nal_type = (buf[0] >> 1) & 0x3f; + lid = ((buf[0] << 5) & 0x20) | ((buf[1] >> 3) & 0x1f); + tid = buf[1] & 0x07; + + /* sanity check for correct layer ID */ + if(lid){ + /* future scalable or 3D video coding extensions */ + av_log(ctx, AV_LOG_ERROR, "Multi-layer HEVC coding is not supported yet, a patch is welcome\n"); + return AVERROR_PATCHWELCOME; + } + + /* sanity check for correct temporal ID */ + if(!tid){ + av_log(ctx, AV_LOG_ERROR, "Illegal temporal ID in RTP/HEVC packet\n"); + return AVERROR_INVALIDDATA; + } + + /* sanity check for correct NAL unit type */ + if (nal_type > 50){ + av_log(ctx, AV_LOG_ERROR, "Unsupported (HEVC) NAL type (%d)\n", nal_type); + return AVERROR_INVALIDDATA; + } + +#ifdef DEBUG + av_log(ctx, AV_LOG_DEBUG, "hevc_handle_packet() got NAL type %d with %d bytes\n", nal_type, len); +#endif + + switch(nal_type) + { + /* aggregated packets (AP) */ + case 48: + /* pass the HEVC payload header */ + buf += RTP_HEVC_PAYLOAD_HEADER_SIZE; + len -= RTP_HEVC_PAYLOAD_HEADER_SIZE; + + /* pass the HEVC DONL fields */ + if(rtp_hevc_ctx->using_donl_field){ + buf += RTP_HEVC_DONL_FIELDS_SIZE; + len -= RTP_HEVC_DONL_FIELDS_SIZE; + } + + /* fall-through */ + /* video parameter set (VPS) */ + case 32: + /* sequence parameter set (SPS) */ + case 33: + /* picture parameter set (PPS) */ + case 34: + /* supplemental enhancement information (SEI) */ + case 39: + /* single NAL unit packet */ + default: + /* create A/V packet */ + if ((res = av_new_packet(pkt, sizeof(start_sequence) + len)) < 0) + return res; + /* A/V packet: copy start sequence */ + memcpy(pkt->data, start_sequence, sizeof(start_sequence)); + /* A/V packet: copy NAL unit data */ + memcpy(pkt->data + sizeof(start_sequence), buf, len); + + COUNT_HEVC_NAL_TYPE(rtp_hevc_ctx, nal_type); + + break; + /* fragmentation unit (FU) */ + case 49: + /* pass the HEVC payload header */ + buf += RTP_HEVC_PAYLOAD_HEADER_SIZE; + len -= RTP_HEVC_PAYLOAD_HEADER_SIZE; + + /** + decode the FU header + + 0 1 2 3 4 5 6 7 + +-+-+-+-+-+-+-+-+ + |S|E| FuType | + +---------------+ + + Start fragment (S): 1 bit + End fragment (E): 1 bit + FuType: 6 bits + */ + first_fragment = buf[0] & 0x80; + last_fragment = buf[0] & 0x40; + fu_type = buf[0] & 0x3f; + + /* pass the HEVC FU header */ + buf += RTP_HEVC_FU_HEADER_SIZE; + len -= RTP_HEVC_FU_HEADER_SIZE; + + /* pass the HEVC DONL fields */ + if(rtp_hevc_ctx->using_donl_field){ + buf += RTP_HEVC_DONL_FIELDS_SIZE; + len -= RTP_HEVC_DONL_FIELDS_SIZE; + } + +#ifdef DEBUG + av_log(ctx, AV_LOG_DEBUG, " FU type %d with %d bytes\n", fu_type, len); +#endif + + if (len > 0) { + new_nal_header[0] = (rtp_pl[0] & 0x81) | (fu_type << 1); + new_nal_header[1] = rtp_pl[1]; + + /* start fragment vs. subsequent fragments */ + if (first_fragment) { + if(!last_fragment){ + /* create A/V packet which is big enough */ + if ((res = av_new_packet(pkt, sizeof(start_sequence) + sizeof(new_nal_header) + len)) < 0) + return res; + /* A/V packet: copy start sequence */ + memcpy(pkt->data, start_sequence, sizeof(start_sequence)); + /* A/V packet: copy new NAL header */ + memcpy(pkt->data + sizeof(start_sequence), new_nal_header, sizeof(new_nal_header)); + /* A/V packet: copy NAL unit data */ + memcpy(pkt->data + sizeof(start_sequence) + sizeof(new_nal_header), buf, len); + }else{ + av_log(ctx, AV_LOG_ERROR, "Illegal combination of S and E bit in RTP/HEVC packet\n"); + res = AVERROR_INVALIDDATA; + } + } else { + /* create A/V packet */ + if ((res = av_new_packet(pkt, len)) < 0) + return res; + /* A/V packet: copy NAL unit data */ + memcpy(pkt->data, buf, len); + } + } else { + av_log(ctx, AV_LOG_ERROR, "Too short data for (HEVC) NAL unit, got %d bytes\n", len); + res = AVERROR_INVALIDDATA; + } + + if(!res){ + COUNT_HEVC_NAL_TYPE(rtp_hevc_ctx, fu_type); + } + + break; + /* PACI packet */ + case 50: + /* Temporal scalability control information (TSCI) */ + av_log(ctx, AV_LOG_ERROR, "PACI packets for RTP/HEVC are not supported yet, a patch is welcome\n"); + res = AVERROR_PATCHWELCOME; + break; + } + + pkt->stream_index = st->index; + + return res; +} + +RTPDynamicProtocolHandler ff_hevc_dynamic_handler = { + .enc_name = "HEVC", + .codec_type = AVMEDIA_TYPE_VIDEO, + .codec_id = AV_CODEC_ID_HEVC, + .init = hevc_init, + .parse_sdp_a_line = hevc_parse_sdp_line, + .alloc = hevc_new_context, + .free = hevc_free_context, + .parse_packet = hevc_handle_packet +}; + +RTPDynamicProtocolHandler ff_h265_dynamic_handler = { + .enc_name = "H265", + .codec_type = AVMEDIA_TYPE_VIDEO, + .codec_id = AV_CODEC_ID_H265, + .init = hevc_init, + .parse_sdp_a_line = hevc_parse_sdp_line, + .alloc = hevc_new_context, + .free = hevc_free_context, + .parse_packet = hevc_handle_packet +}; diff --git a/libavformat/rtpdec_ilbc.c b/libavformat/rtpdec_ilbc.c index bff47c7..aa1579f 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 ed9c86c..ccd80ad 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 */ @@ -106,7 +106,8 @@ static void jpeg_put_marker(PutByteContext *pbc, int code) } static int jpeg_create_header(uint8_t *buf, int size, uint32_t type, uint32_t w, - uint32_t h, const uint8_t *qtable, int nb_qtable) + uint32_t h, const uint8_t *qtable, int nb_qtable, + int dri) { PutByteContext pbc; uint8_t *dht_size_ptr; @@ -132,6 +133,12 @@ static int jpeg_create_header(uint8_t *buf, int size, uint32_t type, uint32_t w, bytestream2_put_byte(&pbc, 0); bytestream2_put_byte(&pbc, 0); + if (dri) { + jpeg_put_marker(&pbc, DRI); + bytestream2_put_be16(&pbc, 4); + bytestream2_put_be16(&pbc, dri); + } + /* DQT */ jpeg_put_marker(&pbc, DQT); bytestream2_put_be16(&pbc, 2 + nb_qtable * (1 + 64)); @@ -226,7 +233,7 @@ static int jpeg_parse_packet(AVFormatContext *ctx, PayloadContext *jpeg, const uint8_t *qtables = NULL; uint16_t qtable_len; uint32_t off; - int ret; + int ret, dri = 0; if (len < 8) { av_log(ctx, AV_LOG_ERROR, "Too short RTP/JPEG packet.\n"); @@ -242,6 +249,16 @@ static int jpeg_parse_packet(AVFormatContext *ctx, PayloadContext *jpeg, buf += 8; len -= 8; + if (type & 0x40) { + if (len < 4) { + av_log(ctx, AV_LOG_ERROR, "Too short RTP/JPEG packet.\n"); + return AVERROR_INVALIDDATA; + } + dri = AV_RB16(buf); + buf += 4; + len -= 4; + type &= ~0x40; + } /* Parse the restart marker header. */ if (type > 63) { av_log(ctx, AV_LOG_ERROR, @@ -332,7 +349,7 @@ static int jpeg_parse_packet(AVFormatContext *ctx, PayloadContext *jpeg, * interchange format. */ jpeg->hdr_size = jpeg_create_header(hdr, sizeof(hdr), type, width, height, qtables, - qtable_len / 64); + qtable_len / 64, dri); /* Copy JPEG header to frame buffer. */ avio_write(jpeg->frame, hdr, jpeg->hdr_size); diff --git a/libavformat/rtpdec_latm.c b/libavformat/rtpdec_latm.c index 6ab3f78..3896482 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 b059fcf..d73ff1e 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 4aedeea..53921f2 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 b3ef261..0c2b717 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 */ @@ -40,14 +40,14 @@ static void mpegts_free_context(PayloadContext *data) if (!data) return; if (data->ts) - ff_mpegts_parse_close(data->ts); + avpriv_mpegts_parse_close(data->ts); av_free(data); } static av_cold int mpegts_init(AVFormatContext *ctx, int st_index, PayloadContext *data) { - data->ts = ff_mpegts_parse_open(ctx); + data->ts = avpriv_mpegts_parse_open(ctx); if (!data->ts) return AVERROR(ENOMEM); return 0; @@ -72,8 +72,8 @@ static int mpegts_handle_packet(AVFormatContext *ctx, PayloadContext *data, if (!buf) { if (data->read_buf_index >= data->read_buf_size) return AVERROR(EAGAIN); - ret = ff_mpegts_parse_packet(data->ts, pkt, data->buf + data->read_buf_index, - data->read_buf_size - data->read_buf_index); + ret = avpriv_mpegts_parse_packet(data->ts, pkt, data->buf + data->read_buf_index, + data->read_buf_size - data->read_buf_index); if (ret < 0) return AVERROR(EAGAIN); data->read_buf_index += ret; @@ -83,8 +83,8 @@ static int mpegts_handle_packet(AVFormatContext *ctx, PayloadContext *data, return 0; } - ret = ff_mpegts_parse_packet(data->ts, pkt, buf, len); - /* The only error that can be returned from ff_mpegts_parse_packet + ret = avpriv_mpegts_parse_packet(data->ts, pkt, buf, len); + /* The only error that can be returned from avpriv_mpegts_parse_packet * is "no more data to return from the provided buffer", so return * AVERROR(EAGAIN) for all errors */ if (ret < 0) diff --git a/libavformat/rtpdec_qcelp.c b/libavformat/rtpdec_qcelp.c index 45a89ae..eebcdd0 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 0d7b5bb..e1dd62f 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 2d9c603..ee8a48a 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 aa880e5..98e8b58 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 fc86ac1..b4b4f22 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 9e4fbc5..dc34f9e 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 @@ -256,7 +256,7 @@ parse_packed_headers(const uint8_t * packed_headers, if (packed_headers_end - packed_headers < 9) { av_log(codec, AV_LOG_ERROR, - "Invalid %td byte packed header.", + "Invalid %"PTRDIFF_SPECIFIER" byte packed header.", packed_headers_end - packed_headers); return AVERROR_INVALIDDATA; } @@ -278,7 +278,7 @@ parse_packed_headers(const uint8_t * packed_headers, if (packed_headers_end - packed_headers != length || length1 > length || length2 > length - length1) { av_log(codec, AV_LOG_ERROR, - "Bad packed header lengths (%d,%d,%td,%d)\n", length1, + "Bad packed header lengths (%d,%d,%"PTRDIFF_SPECIFIER",%d)\n", length1, length2, packed_headers_end - packed_headers, length); return AVERROR_INVALIDDATA; } @@ -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 7bc7373..9c587d2 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 */ @@ -49,6 +49,7 @@ static const AVClass rtp_muxer_class = { static int is_supported(enum AVCodecID id) { switch(id) { + case AV_CODEC_ID_H261: case AV_CODEC_ID_H263: case AV_CODEC_ID_H263P: case AV_CODEC_ID_H264: @@ -96,7 +97,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; } @@ -119,16 +120,19 @@ static int rtp_write_header(AVFormatContext *s1) s->ssrc = av_get_random_seed(); s->first_packet = 1; s->first_rtcp_ntp_time = ff_ntp_time(); - if (s1->start_time_realtime) + if (s1->start_time_realtime != 0 && s1->start_time_realtime != AV_NOPTS_VALUE) /* Round the NTP time to whole milliseconds. */ s->first_rtcp_ntp_time = (s1->start_time_realtime / 1000) * 1000 + NTP_OFFSET_US; // 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 (s1->flags & AVFMT_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) { @@ -553,12 +557,19 @@ static int rtp_write_packet(AVFormatContext *s1, AVPacket *pkt) case AV_CODEC_ID_H264: ff_rtp_send_h264(s1, pkt->data, size); break; + case AV_CODEC_ID_H261: + ff_rtp_send_h261(s1, pkt->data, size); + break; case AV_CODEC_ID_H263: if (s->flags & FF_RTP_FLAG_RFC2190) { int mb_info_size = 0; const uint8_t *mb_info = av_packet_get_side_data(pkt, AV_PKT_DATA_H263_MB_INFO, &mb_info_size); + if (!mb_info) { + av_log(s1, AV_LOG_ERROR, "failed to allocate side data\n"); + return AVERROR(ENOMEM); + } ff_rtp_send_h263_rfc2190(s1, pkt->data, size, mb_info, mb_info_size); break; } diff --git a/libavformat/rtpenc.h b/libavformat/rtpenc.h index 35ee151..48b6b02 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 @@ -81,6 +81,7 @@ typedef struct RTPMuxContext RTPMuxContext; void ff_rtp_send_data(AVFormatContext *s1, const uint8_t *buf1, int len, int m); void ff_rtp_send_h264(AVFormatContext *s1, const uint8_t *buf1, int size); +void ff_rtp_send_h261(AVFormatContext *s1, const uint8_t *buf1, int size); void ff_rtp_send_h263(AVFormatContext *s1, const uint8_t *buf1, int size); void ff_rtp_send_h263_rfc2190(AVFormatContext *s1, const uint8_t *buf1, int size, const uint8_t *mb_info, int mb_info_size); @@ -92,7 +93,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 1b2fa0a..be8f43a 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 73da8c8..bd1c197 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 10c4020..dea1f70 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 4117239..21c27c1 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_h261.c b/libavformat/rtpenc_h261.c new file mode 100644 index 0000000..6f63703 --- /dev/null +++ b/libavformat/rtpenc_h261.c @@ -0,0 +1,57 @@ +/* + * RTP packetization for H.261 video (RFC 4587) + * Copyright (c) 2014 Thomas Volkert <thomas@homer-conferencing.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 "avformat.h" +#include "rtpenc.h" + +void ff_rtp_send_h261(AVFormatContext *s1, const uint8_t *frame_buf, int frame_size) +{ + RTPMuxContext *rtp_ctx = s1->priv_data; + int processed_frame_size; + int last_packet_of_frame; + uint8_t *tmp_buf_ptr; + + /* use the default 90 KHz time stamp */ + rtp_ctx->timestamp = rtp_ctx->cur_timestamp; + + /* continue as long as not all frame data is processed */ + while (frame_size > 0) { + tmp_buf_ptr = rtp_ctx->buf; + *tmp_buf_ptr++ = 1; /* V=1 */ + *tmp_buf_ptr++ = 0; + *tmp_buf_ptr++ = 0; + *tmp_buf_ptr++ = 0; + + processed_frame_size = FFMIN(rtp_ctx->max_payload_size - 4, frame_size); + + //XXX: parse the h.261 bitstream and improve frame splitting here + + last_packet_of_frame = (processed_frame_size == frame_size); + + memcpy(tmp_buf_ptr, frame_buf, processed_frame_size); + tmp_buf_ptr += processed_frame_size; + + ff_rtp_send_data(s1, rtp_ctx->buf, tmp_buf_ptr - rtp_ctx->buf, last_packet_of_frame); + + frame_buf += processed_frame_size; + frame_size -= processed_frame_size; + } +} diff --git a/libavformat/rtpenc_h263.c b/libavformat/rtpenc_h263.c index 87f0bd7..9cea013 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 f714d01..0493008 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 206d9ba..b6c16e1 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 9d0915b..c353329 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 6467677..4430c44 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 70248c4..4b45f51 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 d24c8bc..671d245 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 07086b1..bc40cf8 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 0f31539..a8cbd97 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 7acc80e..5b243fb 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 23d7da1..ce4763e 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" @@ -64,7 +65,7 @@ #define RTSP_FLAG_OPTS(name, longname) \ { name, longname, OFFSET(rtsp_flags), AV_OPT_TYPE_FLAGS, {.i64 = 0}, INT_MIN, INT_MAX, DEC, "rtsp_flags" }, \ - { "filter_src", "Only receive packets from the negotiated peer IP", 0, AV_OPT_TYPE_CONST, {.i64 = RTSP_FLAG_FILTER_SRC}, 0, 0, DEC, "rtsp_flags" } + { "filter_src", "only receive packets from the negotiated peer IP", 0, AV_OPT_TYPE_CONST, {.i64 = RTSP_FLAG_FILTER_SRC}, 0, 0, DEC, "rtsp_flags" } #define RTSP_MEDIATYPE_OPTS(name, longname) \ { name, longname, OFFSET(media_type_mask), AV_OPT_TYPE_FLAGS, { .i64 = (1 << (AVMEDIA_TYPE_DATA+1)) - 1 }, INT_MIN, INT_MAX, DEC, "allowed_media_types" }, \ @@ -73,37 +74,40 @@ { "data", "Data", 0, AV_OPT_TYPE_CONST, {.i64 = 1 << AVMEDIA_TYPE_DATA}, 0, 0, DEC, "allowed_media_types" } #define RTSP_REORDERING_OPTS() \ - { "reorder_queue_size", "Number of packets to buffer for handling of reordered packets", OFFSET(reordering_queue_size), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, DEC } + { "reorder_queue_size", "set number of packets to buffer for handling of reordered packets", OFFSET(reordering_queue_size), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, DEC } const AVOption ff_rtsp_options[] = { - { "initial_pause", "Don't start playing the stream immediately", OFFSET(initial_pause), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, DEC }, + { "initial_pause", "do not start playing the stream immediately", OFFSET(initial_pause), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, DEC }, FF_RTP_FLAG_OPTS(RTSPState, rtp_muxer_flags), - { "rtsp_transport", "RTSP transport protocols", OFFSET(lower_transport_mask), AV_OPT_TYPE_FLAGS, {.i64 = 0}, INT_MIN, INT_MAX, DEC|ENC, "rtsp_transport" }, \ + { "rtsp_transport", "set RTSP transport protocols", OFFSET(lower_transport_mask), AV_OPT_TYPE_FLAGS, {.i64 = 0}, INT_MIN, INT_MAX, DEC|ENC, "rtsp_transport" }, \ { "udp", "UDP", 0, AV_OPT_TYPE_CONST, {.i64 = 1 << RTSP_LOWER_TRANSPORT_UDP}, 0, 0, DEC|ENC, "rtsp_transport" }, \ { "tcp", "TCP", 0, AV_OPT_TYPE_CONST, {.i64 = 1 << RTSP_LOWER_TRANSPORT_TCP}, 0, 0, DEC|ENC, "rtsp_transport" }, \ { "udp_multicast", "UDP multicast", 0, AV_OPT_TYPE_CONST, {.i64 = 1 << RTSP_LOWER_TRANSPORT_UDP_MULTICAST}, 0, 0, DEC, "rtsp_transport" }, { "http", "HTTP tunneling", 0, AV_OPT_TYPE_CONST, {.i64 = (1 << RTSP_LOWER_TRANSPORT_HTTP)}, 0, 0, DEC, "rtsp_transport" }, - RTSP_FLAG_OPTS("rtsp_flags", "RTSP flags"), - { "listen", "Wait for incoming connections", 0, AV_OPT_TYPE_CONST, {.i64 = RTSP_FLAG_LISTEN}, 0, 0, DEC, "rtsp_flags" }, - RTSP_MEDIATYPE_OPTS("allowed_media_types", "Media types to accept from the server"), - { "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 }, + RTSP_FLAG_OPTS("rtsp_flags", "set RTSP flags"), + { "listen", "wait for incoming connections", 0, AV_OPT_TYPE_CONST, {.i64 = RTSP_FLAG_LISTEN}, 0, 0, DEC, "rtsp_flags" }, + { "prefer_tcp", "try RTP via TCP first, if available", 0, AV_OPT_TYPE_CONST, {.i64 = RTSP_FLAG_PREFER_TCP}, 0, 0, DEC|ENC, "rtsp_flags" }, + RTSP_MEDIATYPE_OPTS("allowed_media_types", "set media types to accept from the server"), + { "min_port", "set minimum local UDP port", OFFSET(rtp_port_min), AV_OPT_TYPE_INT, {.i64 = RTSP_RTP_PORT_MIN}, 0, 65535, DEC|ENC }, + { "max_port", "set maximum local UDP port", OFFSET(rtp_port_max), AV_OPT_TYPE_INT, {.i64 = RTSP_RTP_PORT_MAX}, 0, 65535, DEC|ENC }, + { "timeout", "set maximum timeout (in seconds) to wait for incoming connections (-1 is infinite, imply flag listen)", OFFSET(initial_timeout), AV_OPT_TYPE_INT, {.i64 = -1}, INT_MIN, INT_MAX, DEC }, + { "stimeout", "set timeout (in microseconds) of socket TCP 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 }, }; static const AVOption sdp_options[] = { RTSP_FLAG_OPTS("sdp_flags", "SDP flags"), - { "custom_io", "Use custom IO", 0, AV_OPT_TYPE_CONST, {.i64 = RTSP_FLAG_CUSTOM_IO}, 0, 0, DEC, "rtsp_flags" }, - { "rtcp_to_source", "Send RTCP packets to the source address of received packets", 0, AV_OPT_TYPE_CONST, {.i64 = RTSP_FLAG_RTCP_TO_SOURCE}, 0, 0, DEC, "rtsp_flags" }, - RTSP_MEDIATYPE_OPTS("allowed_media_types", "Media types to accept from the server"), + { "custom_io", "use custom I/O", 0, AV_OPT_TYPE_CONST, {.i64 = RTSP_FLAG_CUSTOM_IO}, 0, 0, DEC, "rtsp_flags" }, + { "rtcp_to_source", "send RTCP packets to the source address of received packets", 0, AV_OPT_TYPE_CONST, {.i64 = RTSP_FLAG_RTCP_TO_SOURCE}, 0, 0, DEC, "rtsp_flags" }, + RTSP_MEDIATYPE_OPTS("allowed_media_types", "set media types to accept from the server"), RTSP_REORDERING_OPTS(), { NULL }, }; static const AVOption rtp_options[] = { - RTSP_FLAG_OPTS("rtp_flags", "RTP flags"), + RTSP_FLAG_OPTS("rtp_flags", "set RTP flags"), RTSP_REORDERING_OPTS(), { NULL }, }; @@ -435,7 +439,7 @@ static void sdp_parse_line(AVFormatContext *s, SDPParseState *s1, /* no corresponding stream */ if (rt->transport == RTSP_TRANSPORT_RAW) { if (!rt->ts && CONFIG_RTPDEC) - rt->ts = ff_mpegts_parse_open(s); + rt->ts = avpriv_mpegts_parse_open(s); } else { RTPDynamicProtocolHandler *handler; handler = ff_rtp_handler_find_by_id( @@ -654,7 +658,7 @@ int ff_sdp_parse(AVFormatContext *s, const char *content) av_free(s1->default_exclude_source_addrs[i]); av_freep(&s1->default_exclude_source_addrs); - rt->p = av_malloc(sizeof(struct pollfd)*2*(rt->nb_rtsp_streams+1)); + rt->p = av_malloc_array(rt->nb_rtsp_streams + 1, sizeof(struct pollfd) * 2); if (!rt->p) return AVERROR(ENOMEM); return 0; } @@ -724,7 +728,7 @@ void ff_rtsp_close_streams(AVFormatContext *s) avformat_close_input(&rt->asf_ctx); } if (rt->ts && CONFIG_RTPDEC) - ff_mpegts_parse_close(rt->ts); + avpriv_mpegts_parse_close(rt->ts); av_free(rt->p); av_free(rt->recvbuf); } @@ -1241,7 +1245,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); @@ -1352,10 +1356,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. */ @@ -1409,7 +1409,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; @@ -1726,7 +1725,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); @@ -1790,6 +1790,10 @@ redirect: int lower_transport = ff_log2_tab[lower_transport_mask & ~(lower_transport_mask - 1)]; + if ((lower_transport_mask & (1 << RTSP_LOWER_TRANSPORT_TCP)) + && (rt->rtsp_flags & RTSP_FLAG_PREFER_TCP)) + lower_transport = RTSP_LOWER_TRANSPORT_TCP; + err = ff_rtsp_make_setup_request(s, host, port, lower_transport, rt->server_type == RTSP_SERVER_REAL ? real_challenge : NULL); @@ -1973,7 +1977,7 @@ int ff_rtsp_fetch_packet(AVFormatContext *s, AVPacket *pkt) } else if (rt->transport == RTSP_TRANSPORT_RTP) { ret = ff_rtp_parse_packet(rt->cur_transport_priv, pkt, NULL, 0); } else if (rt->ts && CONFIG_RTPDEC) { - ret = ff_mpegts_parse_packet(rt->ts, pkt, rt->recvbuf + rt->recvbuf_pos, rt->recvbuf_len - rt->recvbuf_pos); + ret = avpriv_mpegts_parse_packet(rt->ts, pkt, rt->recvbuf + rt->recvbuf_pos, rt->recvbuf_len - rt->recvbuf_pos); if (ret >= 0) { rt->recvbuf_pos += ret; ret = rt->recvbuf_pos < rt->recvbuf_len; @@ -2090,6 +2094,16 @@ redo: st2->time_base); } } + // Make real NTP start time available in AVFormatContext + if (s->start_time_realtime == AV_NOPTS_VALUE) { + s->start_time_realtime = av_rescale (rtpctx->first_rtcp_ntp_time - (NTP_OFFSET << 32), 1000000, 1LL << 32); + if (rtpctx->st) { + s->start_time_realtime -= + av_rescale (rtpctx->rtcp_ts_offset, + (uint64_t) rtpctx->st->time_base.num * 1000000, + rtpctx->st->time_base.den); + } + } } if (ret == -RTCP_BYE) { rt->nb_byes++; @@ -2102,7 +2116,7 @@ redo: } } } else if (rt->ts && CONFIG_RTPDEC) { - ret = ff_mpegts_parse_packet(rt->ts, pkt, rt->recvbuf, len); + ret = avpriv_mpegts_parse_packet(rt->ts, pkt, rt->recvbuf, len); if (ret >= 0) { if (ret < len) { rt->recvbuf_len = len; diff --git a/libavformat/rtsp.h b/libavformat/rtsp.h index 7a910b0..d159793 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 - @@ -403,6 +413,7 @@ typedef struct RTSPState { #define RTSP_FLAG_CUSTOM_IO 0x4 /**< Do all IO via the AVIOContext. */ #define RTSP_FLAG_RTCP_TO_SOURCE 0x8 /**< Send RTCP packets to the source address of received packets. */ +#define RTSP_FLAG_PREFER_TCP 0x10 /**< Try RTP via TCP first if possible. */ typedef struct RTSPSource { char addr[128]; /**< Source-specific multicast include source IP address (from SDP content) */ diff --git a/libavformat/rtspcodes.h b/libavformat/rtspcodes.h index 31ab336..d4ae0a8 100644 --- a/libavformat/rtspcodes.h +++ b/libavformat/rtspcodes.h @@ -1,42 +1,130 @@ /* * RTSP definitions * copyright (c) 2002 Fabrice Bellard + * copyright (c) 2014 Samsung Electronics. All rights reserved. + * @Author: Reynaldo H. Verdejo Pinochet <r.verdejo@sisa.samsung.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_RTSPCODES_H #define AVFORMAT_RTSPCODES_H +#include "libavutil/common.h" + /** RTSP handling */ enum RTSPStatusCode { -RTSP_STATUS_OK =200, /**< OK */ -RTSP_STATUS_METHOD =405, /**< Method Not Allowed */ -RTSP_STATUS_BANDWIDTH =453, /**< Not Enough Bandwidth */ -RTSP_STATUS_SESSION =454, /**< Session Not Found */ -RTSP_STATUS_STATE =455, /**< Method Not Valid in This State */ -RTSP_STATUS_AGGREGATE =459, /**< Aggregate operation not allowed */ -RTSP_STATUS_ONLY_AGGREGATE =460, /**< Only aggregate operation allowed */ -RTSP_STATUS_TRANSPORT =461, /**< Unsupported transport */ -RTSP_STATUS_INTERNAL =500, /**< Internal Server Error */ -RTSP_STATUS_SERVICE =503, /**< Service Unavailable */ -RTSP_STATUS_VERSION =505, /**< RTSP Version not supported */ +RTSP_STATUS_CONTINUE =100, +RTSP_STATUS_OK =200, +RTSP_STATUS_CREATED =201, +RTSP_STATUS_LOW_ON_STORAGE_SPACE =250, +RTSP_STATUS_MULTIPLE_CHOICES =300, +RTSP_STATUS_MOVED_PERMANENTLY =301, +RTSP_STATUS_MOVED_TEMPORARILY =302, +RTSP_STATUS_SEE_OTHER =303, +RTSP_STATUS_NOT_MODIFIED =304, +RTSP_STATUS_USE_PROXY =305, +RTSP_STATUS_BAD_REQUEST =400, +RTSP_STATUS_UNAUTHORIZED =401, +RTSP_STATUS_PAYMENT_REQUIRED =402, +RTSP_STATUS_FORBIDDEN =403, +RTSP_STATUS_NOT_FOUND =404, +RTSP_STATUS_METHOD =405, +RTSP_STATUS_NOT_ACCEPTABLE =406, +RTSP_STATUS_PROXY_AUTH_REQUIRED =407, +RTSP_STATUS_REQ_TIME_OUT =408, +RTSP_STATUS_GONE =410, +RTSP_STATUS_LENGTH_REQUIRED =411, +RTSP_STATUS_PRECONDITION_FAILED =412, +RTSP_STATUS_REQ_ENTITY_2LARGE =413, +RTSP_STATUS_REQ_URI_2LARGE =414, +RTSP_STATUS_UNSUPPORTED_MTYPE =415, +RTSP_STATUS_PARAM_NOT_UNDERSTOOD =451, +RTSP_STATUS_CONFERENCE_NOT_FOUND =452, +RTSP_STATUS_BANDWIDTH =453, +RTSP_STATUS_SESSION =454, +RTSP_STATUS_STATE =455, +RTSP_STATUS_INVALID_HEADER_FIELD =456, +RTSP_STATUS_INVALID_RANGE =457, +RTSP_STATUS_RONLY_PARAMETER =458, +RTSP_STATUS_AGGREGATE =459, +RTSP_STATUS_ONLY_AGGREGATE =460, +RTSP_STATUS_TRANSPORT =461, +RTSP_STATUS_UNREACHABLE =462, +RTSP_STATUS_INTERNAL =500, +RTSP_STATUS_NOT_IMPLEMENTED =501, +RTSP_STATUS_BAD_GATEWAY =502, +RTSP_STATUS_SERVICE =503, +RTSP_STATUS_GATEWAY_TIME_OUT =504, +RTSP_STATUS_VERSION =505, +RTSP_STATUS_UNSUPPORTED_OPTION =551, +}; + +static const av_unused char * const rtsp_status_strings[] = { +[RTSP_STATUS_CONTINUE] ="Continue", +[RTSP_STATUS_OK] ="OK", +[RTSP_STATUS_CREATED] ="Created", +[RTSP_STATUS_LOW_ON_STORAGE_SPACE] ="Low on Storage Space", +[RTSP_STATUS_MULTIPLE_CHOICES] ="Multiple Choices", +[RTSP_STATUS_MOVED_PERMANENTLY] ="Moved Permanently", +[RTSP_STATUS_MOVED_TEMPORARILY] ="Moved Temporarily", +[RTSP_STATUS_SEE_OTHER] ="See Other", +[RTSP_STATUS_NOT_MODIFIED] ="Not Modified", +[RTSP_STATUS_USE_PROXY] ="Use Proxy", +[RTSP_STATUS_BAD_REQUEST] ="Bad Request", +[RTSP_STATUS_UNAUTHORIZED] ="Unauthorized", +[RTSP_STATUS_PAYMENT_REQUIRED] ="Payment Required", +[RTSP_STATUS_FORBIDDEN] ="Forbidden", +[RTSP_STATUS_NOT_FOUND] ="Not Found", +[RTSP_STATUS_METHOD] ="Method Not Allowed", +[RTSP_STATUS_NOT_ACCEPTABLE] ="Not Acceptable", +[RTSP_STATUS_PROXY_AUTH_REQUIRED] ="Proxy Authentication Required", +[RTSP_STATUS_REQ_TIME_OUT] ="Request Time-out", +[RTSP_STATUS_GONE] ="Gone", +[RTSP_STATUS_LENGTH_REQUIRED] ="Length Required", +[RTSP_STATUS_PRECONDITION_FAILED] ="Precondition Failed", +[RTSP_STATUS_REQ_ENTITY_2LARGE] ="Request Entity Too Large", +[RTSP_STATUS_REQ_URI_2LARGE] ="Request URI Too Large", +[RTSP_STATUS_UNSUPPORTED_MTYPE] ="Unsupported Media Type", +[RTSP_STATUS_PARAM_NOT_UNDERSTOOD] ="Parameter Not Understood", +[RTSP_STATUS_CONFERENCE_NOT_FOUND] ="Conference Not Found", +[RTSP_STATUS_BANDWIDTH] ="Not Enough Bandwidth", +[RTSP_STATUS_SESSION] ="Session Not Found", +[RTSP_STATUS_STATE] ="Method Not Valid in This State", +[RTSP_STATUS_INVALID_HEADER_FIELD] ="Header Field Not Valid for Resource", +[RTSP_STATUS_INVALID_RANGE] ="Invalid Range", +[RTSP_STATUS_RONLY_PARAMETER] ="Parameter Is Read-Only", +[RTSP_STATUS_AGGREGATE] ="Aggregate Operation no Allowed", +[RTSP_STATUS_ONLY_AGGREGATE] ="Only Aggregate Operation Allowed", +[RTSP_STATUS_TRANSPORT] ="Unsupported Transport", +[RTSP_STATUS_UNREACHABLE] ="Destination Unreachable", +[RTSP_STATUS_INTERNAL] ="Internal Server Error", +[RTSP_STATUS_NOT_IMPLEMENTED] ="Not Implemented", +[RTSP_STATUS_BAD_GATEWAY] ="Bad Gateway", +[RTSP_STATUS_SERVICE] ="Service Unavailable", +[RTSP_STATUS_GATEWAY_TIME_OUT] ="Gateway Time-out", +[RTSP_STATUS_VERSION] ="RTSP Version not Supported", +[RTSP_STATUS_UNSUPPORTED_OPTION] ="Option not supported", }; +#define RTSP_STATUS_CODE2STRING(x) (\ +x >= 100 && x < FF_ARRAY_ELEMS(rtsp_status_strings) && rtsp_status_strings[x] \ +)? rtsp_status_strings[x] : NULL + enum RTSPMethod { DESCRIBE, ANNOUNCE, diff --git a/libavformat/rtspdec.c b/libavformat/rtspdec.c index a14ec57..b4b4f12 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 */ @@ -360,6 +360,10 @@ static inline int parse_command_line(AVFormatContext *s, const char *line, RTSPState *rt = s->priv_data; const char *linept, *searchlinept; linept = strchr(line, ' '); + if (!linept) { + av_log(s, AV_LOG_ERROR, "Error parsing method string\n"); + return AVERROR_INVALIDDATA; + } if (linept - line > methodsize - 1) { av_log(s, AV_LOG_ERROR, "Method string too long\n"); return AVERROR(EIO); @@ -692,7 +696,7 @@ static int rtsp_read_header(AVFormatContext *s) return ret; rt->real_setup_cache = !s->nb_streams ? NULL : - av_mallocz(2 * s->nb_streams * sizeof(*rt->real_setup_cache)); + av_mallocz_array(s->nb_streams, 2 * sizeof(*rt->real_setup_cache)); if (!rt->real_setup_cache && s->nb_streams) return AVERROR(ENOMEM); rt->real_setup = rt->real_setup_cache + s->nb_streams; @@ -738,7 +742,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 3db53ac..12fb410 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 */ @@ -51,7 +51,8 @@ int ff_rtsp_setup_output_streams(AVFormatContext *s, const char *addr) char *sdp; AVFormatContext sdp_ctx, *ctx_array[1]; - s->start_time_realtime = av_gettime(); + if (s->start_time_realtime == 0 || s->start_time_realtime == AV_NOPTS_VALUE) + s->start_time_realtime = av_gettime(); /* Announce the stream */ sdp = av_mallocz(SDP_MAX_SIZE); @@ -211,7 +212,7 @@ static int rtsp_write_packet(AVFormatContext *s, AVPacket *pkt) rtsp_st = rt->rtsp_streams[pkt->stream_index]; rtpctx = rtsp_st->transport_priv; - ret = ff_write_chained(rtpctx, 0, pkt, s); + ret = ff_write_chained(rtpctx, 0, pkt, s, 0); /* ff_write_chained does all the RTP packetization. If using TCP as * transport, rtpctx->pb is only a dyn_packet_buf that queues up the * packets, so we need to send them out on the TCP connection separately. diff --git a/libavformat/samidec.c b/libavformat/samidec.c new file mode 100644 index 0000000..1a12eca --- /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 (!avio_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 0280cea..fe7bd82 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 246b7cf..f909557 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 */ @@ -134,13 +134,14 @@ static int sap_write_header(AVFormatContext *s) freeaddrinfo(ai); } - contexts = av_mallocz(sizeof(AVFormatContext*) * s->nb_streams); + contexts = av_mallocz_array(s->nb_streams, sizeof(AVFormatContext*)); if (!contexts) { ret = AVERROR(ENOMEM); goto fail; } - s->start_time_realtime = av_gettime(); + if (s->start_time_realtime == 0 || s->start_time_realtime == AV_NOPTS_VALUE) + s->start_time_realtime = av_gettime(); for (i = 0; i < s->nb_streams; i++) { URLContext *fd; @@ -245,7 +246,7 @@ static int sap_write_packet(AVFormatContext *s, AVPacket *pkt) { AVFormatContext *rtpctx; struct SAPState *sap = s->priv_data; - int64_t now = av_gettime(); + int64_t now = av_gettime_relative(); if (!sap->last_time || now - sap->last_time > 5000000) { int ret = ffurl_write(sap->ann_fd, sap->ann, sap->ann_size); @@ -255,7 +256,7 @@ static int sap_write_packet(AVFormatContext *s, AVPacket *pkt) sap->last_time = now; } rtpctx = s->streams[pkt->stream_index]->priv_data; - return ff_write_chained(rtpctx, 0, pkt, s); + return ff_write_chained(rtpctx, 0, pkt, s, 0); } AVOutputFormat ff_sap_muxer = { diff --git a/libavformat/sauce.c b/libavformat/sauce.c index a125241..a2347b0 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 62d8e68..0ba9ae5 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 0000000..36cd8a3 --- /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 84a4ac1..35b9ad1 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 */ @@ -98,7 +98,7 @@ static int ff_sctp_recvmsg(int s, void *msg, size_t len, struct sockaddr *from, if (msg_flags) *msg_flags = inmsg.msg_flags; - for (cmsg = CMSG_FIRSTHDR(&inmsg); cmsg != NULL; + for (cmsg = CMSG_FIRSTHDR(&inmsg); cmsg; cmsg = CMSG_NXTHDR(&inmsg, cmsg)) { if ((IPPROTO_SCTP == cmsg->cmsg_level) && (SCTP_SNDRCV == cmsg->cmsg_type)) @@ -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, MSG_NOSIGNAL); diff --git a/libavformat/sdp.c b/libavformat/sdp.c index a690219..8c831f3 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 */ @@ -217,7 +217,7 @@ static char *extradata2psets(AVCodecContext *c) sps_end = r1; } if (!av_base64_encode(p, MAX_PSET_SIZE - (p - psets), r, r1 - r)) { - av_log(c, AV_LOG_ERROR, "Cannot Base64-encode %td %td!\n", MAX_PSET_SIZE - (p - psets), r1 - r); + av_log(c, AV_LOG_ERROR, "Cannot Base64-encode %"PTRDIFF_SPECIFIER" %"PTRDIFF_SPECIFIER"!\n", MAX_PSET_SIZE - (p - psets), r1 - r); av_free(psets); return NULL; @@ -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/sdr2.c b/libavformat/sdr2.c new file mode 100644 index 0000000..82405f69d --- /dev/null +++ b/libavformat/sdr2.c @@ -0,0 +1,121 @@ +/* + * SDR2 demuxer + * Copyright (c) 2014 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" + +static int sdr2_probe(AVProbeData *p) +{ + if (AV_RL32(p->buf) != MKTAG('S', 'R', 'A', 1)) + return 0; + + return AVPROBE_SCORE_EXTENSION; +} + +#define FIRST 0xA8 + +static int sdr2_read_header(AVFormatContext *s) +{ + AVStream *st, *ast; + + ast = avformat_new_stream(s, 0); + if (!ast) + return AVERROR(ENOMEM); + + st = avformat_new_stream(s, 0); + if (!st) + return AVERROR(ENOMEM); + + avio_skip(s->pb, 20); + avpriv_set_pts_info(st, 64, 1, avio_rl32(s->pb)); + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->codec->width = avio_rl32(s->pb); + st->codec->height = avio_rl32(s->pb); + st->codec->codec_id = AV_CODEC_ID_H264; + st->need_parsing = AVSTREAM_PARSE_FULL; + + ast->codec->codec_type = AVMEDIA_TYPE_AUDIO; + ast->codec->channels = 1; + ast->codec->sample_rate = 8000; + ast->codec->codec_id = AV_CODEC_ID_PCM_S16LE; + avpriv_set_pts_info(ast, 64, 1, 8000); + + avio_seek(s->pb, FIRST, SEEK_SET); + + return 0; +} + +static const uint8_t header[24] = { + 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0x00, 0x1e, + 0xa6, 0x80, 0xb0, 0x7e, 0x40, 0x00, 0x00, 0x00, + 0x01, 0x68, 0xce, 0x38, 0x80, 0x00, 0x00, 0x00 +}; + +static int sdr2_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + int64_t pos; + unsigned next; + int flags, ret = 0, is_video; + + pos = avio_tell(s->pb); + + flags = avio_rl32(s->pb); + avio_skip(s->pb, 4); + + next = avio_rl32(s->pb); + if (next <= 52) + return AVERROR_INVALIDDATA; + + avio_skip(s->pb, 6); + is_video = avio_rl32(s->pb); + avio_skip(s->pb, 30); + + if (pos == FIRST) { + if (av_new_packet(pkt, next - 52 + 24) < 0) + return AVERROR(ENOMEM); + memcpy(pkt->data, header, 24); + ret = avio_read(s->pb, pkt->data + 24, next - 52); + if (ret < 0) { + av_free_packet(pkt); + return ret; + } + av_shrink_packet(pkt, ret + 24); + } else { + ret = av_get_packet(s->pb, pkt, next - 52); + } + pkt->stream_index = !!is_video; + pkt->pos = pos; + if (flags & (1 << 12)) + pkt->flags |= AV_PKT_FLAG_KEY; + + return ret; +} + +AVInputFormat ff_sdr2_demuxer = { + .name = "sdr2", + .long_name = NULL_IF_CONFIG_SMALL("SDR2"), + .read_probe = sdr2_probe, + .read_header = sdr2_read_header, + .read_packet = sdr2_read_packet, + .extensions = "sdr2", + .flags = AVFMT_GENERIC_INDEX, +}; diff --git a/libavformat/seek-test.c b/libavformat/seek-test.c index 143f0b5..8b0611d 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 9be8db9..2368b5e 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 */ @@ -280,7 +280,7 @@ int64_t ff_gen_syncpoint_search(AVFormatContext *s, } // Initialize syncpoint structures for each stream. - sync = av_malloc(s->nb_streams * sizeof(AVSyncPoint)); + sync = av_malloc_array(s->nb_streams, sizeof(AVSyncPoint)); if (!sync) // cannot allocate helper structure return -1; @@ -402,7 +402,7 @@ AVParserState *ff_store_parser_state(AVFormatContext *s) if (!state) return NULL; - state->stream_states = av_malloc(sizeof(AVParserStreamState) * s->nb_streams); + state->stream_states = av_malloc_array(s->nb_streams, sizeof(AVParserStreamState)); if (!state->stream_states) { av_free(state); return NULL; diff --git a/libavformat/seek.h b/libavformat/seek.h index 44cd369..3fa7ae3 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 53e2066..b0f0c4e 100644 --- a/libavformat/segafilm.c +++ b/libavformat/segafilm.c @@ -1,21 +1,21 @@ /* * Sega FILM Format (CPK) Demuxer - * Copyright (c) 2003 The ffmpeg 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 */ @@ -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) @@ -72,6 +69,9 @@ static int film_probe(AVProbeData *p) if (AV_RB32(&p->buf[0]) != FILM_TAG) return 0; + if (AV_RB32(&p->buf[16]) != FDSC_TAG) + return 0; + return AVPROBE_SCORE_MAX; } @@ -80,7 +80,6 @@ static int film_read_close(AVFormatContext *s) FilmDemuxContext *film = s->priv_data; av_freep(&film->sample_table); - av_freep(&film->stereo_buffer); return 0; } @@ -96,8 +95,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) @@ -121,19 +118,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 @@ -265,66 +257,19 @@ 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]; /* position the stream (will probably be there anyway) */ avio_seek(pb, sample->sample_offset, SEEK_SET); - /* do a special song and dance when loading FILM Cinepak chunks */ - if ((sample->stream == film->video_stream_index) && - (film->video_type == AV_CODEC_ID_CINEPAK)) { - pkt->pos= avio_tell(pb); - 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) - ret = AVERROR(EIO); - } + ret= av_get_packet(pb, pkt, sample->sample_size); + if (ret != sample->sample_size) + ret = AVERROR(EIO); pkt->stream_index = sample->stream; pkt->pts = sample->pts; diff --git a/libavformat/segment.c b/libavformat/segment.c index 9c757e4..ce784da 100644 --- a/libavformat/segment.c +++ b/libavformat/segment.c @@ -1,59 +1,132 @@ /* - * 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 <time.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/time.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; + struct SegmentListEntry *next; + int64_t last_duration; +} 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_idx_wrap_nb; ///< number of time the index has wraped + 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. */ - char *entry_prefix; /**< 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 + + int use_clocktime; ///< flag to cut segments at regular clock time + int64_t last_val; ///< remember last time for wrap around detection + int64_t last_cut; ///< remember last cut + int cut_pending; + + char *entry_prefix; ///< prefix to add to list entry filenames + 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; ///< total number of reference frames + int segment_frame_count; ///< number of reference frames in the segment + + 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; } 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) { @@ -67,78 +140,85 @@ 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; + size_t size; - 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)); + 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); + } - av_log(s, AV_LOG_VERBOSE, "EXT-X-MEDIA-SEQUENCE:%d\n", - FFMAX(0, seg->number - seg->size)); + /* copy modified name in list entry */ + size = strlen(av_basename(oc->filename)) + 1; + if (seg->entry_prefix) + size += strlen(seg->entry_prefix); - for (i = FFMAX(0, seg->number - seg->size); - i < seg->number; i++) { - avio_printf(seg->pb, "#EXTINF:%d,\n", (int)seg->time); - if (seg->entry_prefix) { - avio_printf(seg->pb, "%s", seg->entry_prefix); - } - av_get_frame_filename(buf, sizeof(buf), s->filename, i); - avio_printf(seg->pb, "%s\n", buf); - } + seg->cur_entry.filename = av_mallocz(size); + if (!seg->cur_entry.filename) + return AVERROR(ENOMEM); + snprintf(seg->cur_entry.filename, size, "%s%s", + seg->entry_prefix ? seg->entry_prefix : "", + av_basename(oc->filename)); - if (last) - avio_printf(seg->pb, "#EXT-X-ENDLIST\n"); -fail: - avio_closep(&seg->pb); - return ret; + 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; + seg->segment_idx++; + if ((seg->segment_idx_wrap) && (seg->segment_idx%seg->segment_idx_wrap == 0)) + seg->segment_idx_wrap_nb++; - if (av_get_frame_filename(oc->filename, sizeof(oc->filename), - s->filename, c->number++) < 0) - return AVERROR(EINVAL); + 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) + &s->interrupt_callback, NULL)) < 0) { + av_log(s, AV_LOG_ERROR, "Failed to open segment '%s'\n", oc->filename); return err; + } if (oc->oformat->priv_class && oc->priv_data) av_opt_set(oc->priv_data, "resend_headers", "1", 0); /* mpegts specific */ @@ -148,21 +228,258 @@ static int segment_start(AVFormatContext *s, int write_header) return err; } + seg->segment_frame_count = 0; 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) { + av_log(s, AV_LOG_ERROR, "Failed to open segment list '%s'\n", seg->list); + 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"); + + av_log(s, AV_LOG_VERBOSE, "EXT-X-MEDIA-SEQUENCE:%d\n", + seg->segment_list_entries->index); + + 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_free(entry->filename); + 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_array(*nb_times, sizeof(**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_array(*nb_frames, sizeof(**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; @@ -183,31 +500,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); + } + + 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; + } + } - for (i = 0; i < s->nb_streams; i++) - seg->has_video += - (s->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO); + 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 (seg->has_video > 1) - av_log(s, AV_LOG_WARNING, - "More than a single video stream present, " - "expect issues decoding it.\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); @@ -226,16 +629,15 @@ 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, - &s->interrupt_callback, NULL)) < 0) + &s->interrupt_callback, NULL)) < 0) { + av_log(s, AV_LOG_ERROR, "Failed to open segment '%s'\n", oc->filename); goto fail; + } } else { if ((ret = open_null_ctx(&oc->pb)) < 0) goto fail; @@ -245,6 +647,10 @@ static int seg_write_header(AVFormatContext *s) avio_close(oc->pb); goto fail; } + seg->segment_frame_count = 0; + + 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); @@ -253,20 +659,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); } @@ -276,55 +672,106 @@ fail: 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; - - if (seg->has_video) { - can_split = st->codec->codec_type == AVMEDIA_TYPE_VIDEO && - pkt->flags & AV_PKT_FLAG_KEY; + int64_t end_pts = INT64_MAX, offset; + int start_frame = INT_MAX; + int ret; + struct tm ti; + int64_t usecs; + int64_t wrapped_val; + + 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 { + if (seg->use_clocktime) { + int64_t avgt = av_gettime(); + time_t sec = avgt / 1000000; +#if HAVE_LOCALTIME_R + localtime_r(&sec, &ti); +#else + ti = *localtime(&sec); +#endif + usecs = (int64_t)(ti.tm_hour*3600 + ti.tm_min*60 + ti.tm_sec) * 1000000 + (avgt % 1000000); + wrapped_val = usecs % seg->time; + if (seg->last_cut != usecs && wrapped_val < seg->last_val) { + seg->cut_pending = 1; + seg->last_cut = usecs; + } + seg->last_val = wrapped_val; + } 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 duration_time:%s is_key:%d frame:%d\n", + pkt->stream_index, av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &st->time_base), + av_ts2timestr(pkt->duration, &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->segment_frame_count > 0 && + (seg->cut_pending || 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))) { + /* sanitize end time in case last packet didn't have a defined duration */ + if (seg->cur_entry.last_duration == 0) + seg->cur_entry.end_time = (double)pkt->pts * av_q2d(st->time_base); + + 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; + seg->cut_pending = 0; + seg->cur_entry.index = seg->segment_idx + seg->segment_idx_wrap*seg->segment_idx_wrap_nb; + 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); + seg->cur_entry.end_time = seg->cur_entry.start_time + + pkt->pts != AV_NOPTS_VALUE ? (double)(pkt->pts + pkt->duration) * av_q2d(st->time_base) : 0; + } else if (pkt->pts != AV_NOPTS_VALUE && pkt->stream_index == seg->reference_stream_index) { + seg->cur_entry.end_time = + FFMAX(seg->cur_entry.end_time, (double)(pkt->pts + pkt->duration) * av_q2d(st->time_base)); + seg->cur_entry.last_duration = pkt->duration; + } - 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; - } - } - } + if (seg->segment_frame_count == 0) { + av_log(s, AV_LOG_VERBOSE, "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); } - ret = ff_write_chained(oc, pkt->stream_index, pkt, s); + 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(seg->avf, pkt->stream_index, pkt, s, seg->initial_offset || seg->reset_timestamps); fail: - if (ret < 0) { - if (seg->list) - avio_close(seg->pb); - avformat_free_context(oc); + if (pkt->stream_index == seg->reference_stream_index) { + seg->frame_count++; + seg->segment_frame_count++; } return ret; @@ -334,27 +781,34 @@ 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->filename); + av_free(cur); + cur = next; } -fail: - avio_close(seg->pb); avformat_free_context(oc); return ret; } @@ -362,17 +816,38 @@ 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 }, - { "segment_list_entry_prefix", "base url prefix for segments", OFFSET(entry_prefix), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, 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_atclocktime", "set segment to be cut at clocktime", OFFSET(use_clocktime), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, E}, + { "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_list_entry_prefix", "set base url prefix for segments", OFFSET(entry_prefix), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, 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 }, + { "segment_wrap_number", "set the number of wrap before the first segment", OFFSET(segment_idx_wrap_nb), 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 }, }; @@ -383,14 +858,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 8316388..2046f91 100644 --- a/libavformat/sierravmd.c +++ b/libavformat/sierravmd.c @@ -1,21 +1,21 @@ /* * Sierra VMD Format Demuxer - * Copyright (c) 2004 The ffmpeg Project + * 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); } @@ -194,7 +204,12 @@ static int vmd_read_header(AVFormatContext *s) int type; uint32_t size; - avio_read(pb, chunk, BYTES_PER_FRAME_RECORD); + if ((ret = avio_read(pb, chunk, BYTES_PER_FRAME_RECORD)) != BYTES_PER_FRAME_RECORD) { + av_log(s, AV_LOG_ERROR, "Failed to read frame record\n"); + if (ret >= 0) + ret = AVERROR_INVALIDDATA; + goto error; + } type = chunk[0]; size = AV_RL32(&chunk[2]); if (size > INT_MAX / 2) { @@ -240,8 +255,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 +269,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 +307,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 8ba7c60..8da6c2f 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,19 @@ 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); + if (avio_read(s->pb, pkt->data + 2 + c->gmcsize, size) != size) { + av_free_packet(pkt); + return AVERROR_INVALIDDATA; + } pkt->stream_index = 0; c->curstrm = -1; }else{ @@ -225,7 +235,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 2fa531b..284cdc1 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 */ @@ -94,11 +94,14 @@ static const uint8_t smk_pal[64] = { static int smacker_probe(AVProbeData *p) { - if(p->buf[0] == 'S' && p->buf[1] == 'M' && p->buf[2] == 'K' - && (p->buf[3] == '2' || p->buf[3] == '4')) - return AVPROBE_SCORE_MAX; - else + if ( AV_RL32(p->buf) != MKTAG('S', 'M', 'K', '2') + && AV_RL32(p->buf) != MKTAG('S', 'M', 'K', '4')) return 0; + + if (AV_RL32(p->buf+4) > 32768U || AV_RL32(p->buf+8) > 32768U) + return AVPROBE_SCORE_MAX/4; + + return AVPROBE_SCORE_MAX; } static int smacker_read_header(AVFormatContext *s) @@ -112,7 +115,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); @@ -126,7 +129,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" @@ -142,10 +145,15 @@ static int smacker_read_header(AVFormatContext *s) /* setup data */ if(smk->frames > 0xFFFFFF) { av_log(s, AV_LOG_ERROR, "Too many frames: %"PRIu32"\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')); @@ -160,7 +168,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; @@ -182,6 +190,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) { @@ -212,21 +222,18 @@ 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 %"PRIu32" 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); @@ -250,7 +257,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 (avio_feof(s->pb) || smk->cur_frame >= smk->frames) return AVERROR_EOF; /* if we demuxed all streams, pass another frame */ @@ -267,6 +274,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; @@ -312,7 +321,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; } @@ -348,7 +357,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); @@ -369,16 +378,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 52e45e9..4edf5e8 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 c56fe46..995ddf2 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 a9ed28e..99ca2ff 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 */ @@ -58,7 +58,7 @@ static int smjpeg_read_header(AVFormatContext *s) duration = avio_rb32(pb); // in msec - while (!pb->eof_reached) { + while (!avio_feof(pb)) { htype = avio_rl32(pb); switch (htype) { case SMJPEG_TXT: @@ -108,10 +108,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); @@ -141,7 +141,7 @@ static int smjpeg_read_packet(AVFormatContext *s, AVPacket *pkt) int64_t pos; int ret; - if (s->pb->eof_reached) + if (avio_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 551af89..430a497 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 457472d..0781a0a 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 */ @@ -292,7 +292,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) { @@ -300,7 +304,7 @@ static int ism_write_header(AVFormatContext *s) goto fail; } - c->streams = av_mallocz(sizeof(*c->streams) * s->nb_streams); + c->streams = av_mallocz_array(s->nb_streams, sizeof(*c->streams)); if (!c->streams) { ret = AVERROR(ENOMEM); goto fail; @@ -311,7 +315,6 @@ static int ism_write_header(AVFormatContext *s) AVFormatContext *ctx; AVStream *st; AVDictionary *opts = NULL; - char buf[10]; if (!s->streams[i]->codec->bit_rate) { av_log(s, AV_LOG_ERROR, "No bit rate set for stream %d\n", i); @@ -319,7 +322,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) { @@ -343,8 +350,7 @@ static int ism_write_header(AVFormatContext *s) goto fail; } - snprintf(buf, sizeof(buf), "%d", c->lookahead_count); - av_dict_set(&opts, "ism_lookahead", buf, 0); + av_dict_set_int(&opts, "ism_lookahead", c->lookahead_count, 0); av_dict_set(&opts, "movflags", "frag_custom", 0); if ((ret = avformat_write_header(ctx, &opts)) < 0) { goto fail; @@ -425,7 +431,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 }; @@ -568,7 +574,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) @@ -585,7 +591,7 @@ static int ism_write_packet(AVFormatContext *s, AVPacket *pkt) } os->packets_written++; - return ff_write_chained(os->ctx, 0, pkt, s); + return ff_write_chained(os->ctx, 0, pkt, s, 0); } static int ism_write_trailer(AVFormatContext *s) diff --git a/libavformat/smush.c b/libavformat/smush.c index 1615b45..abb6989 100644 --- a/libavformat/smush.c +++ b/libavformat/smush.c @@ -2,20 +2,20 @@ * LucasArts Smush demuxer * Copyright (c) 2006 Cyril Zorin * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free 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,7 +102,7 @@ static int smush_read_header(AVFormatContext *ctx) while (!got_audio && ((read + 8) < size)) { uint32_t sig, chunk_size; - if (pb->eof_reached) + if (avio_feof(pb)) return AVERROR_EOF; sig = avio_rb32(pb); @@ -158,11 +158,7 @@ static int smush_read_header(AVFormatContext *ctx) vst->codec->height = height; if (!smush->version) { - av_free(vst->codec->extradata); - vst->codec->extradata_size = 1024 + 2; - vst->codec->extradata = av_malloc(vst->codec->extradata_size + - FF_INPUT_BUFFER_PADDING_SIZE); - if (!vst->codec->extradata) + if (ff_alloc_extradata(vst->codec, 1024 + 2)) return AVERROR(ENOMEM); AV_WL16(vst->codec->extradata, subversion); @@ -200,7 +196,7 @@ static int smush_read_packet(AVFormatContext *ctx, AVPacket *pkt) while (!done) { uint32_t sig, size; - if (pb->eof_reached) + if (avio_feof(pb)) return AVERROR_EOF; sig = avio_rb32(pb); diff --git a/libavformat/sol.c b/libavformat/sol.c index 92599b1..27d3551 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 (avio_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 e59531b..f4a12e9 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 ec94675..aec4284 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 4b8955c..95af10c 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; @@ -71,27 +72,19 @@ static int sox_write_header(AVFormatContext *s) avio_wb32(pb, comment_size); } else { av_log(s, AV_LOG_ERROR, "invalid codec; use pcm_s32le or pcm_s32be\n"); - return -1; + return AVERROR(EINVAL); } 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,7 +116,7 @@ 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, .flags = AVFMT_NOTIMESTAMPS, }; diff --git a/libavformat/spdif.c b/libavformat/spdif.c index 777ac47..604141a 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 f5b15eb..fee4ff71 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 2fb9477..7c04afa 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 (avio_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 f3acf48..18afd42 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 */ @@ -94,7 +94,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, @@ -397,15 +397,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)); @@ -522,13 +522,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 */ @@ -554,5 +554,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 9db5133..53182cd 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 (!avio_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 0000000..b43504b --- /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 6659bfc..b6e8211 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 18ed177..3189f8f 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 f9b94d7..f9d6b17 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/subfile.c b/libavformat/subfile.c new file mode 100644 index 0000000..0e84384 --- /dev/null +++ b/libavformat/subfile.c @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2014 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/avassert.h" +#include "libavutil/avstring.h" +#include "libavutil/opt.h" +#include "avformat.h" +#include "url.h" + +typedef struct SubfileContext { + const AVClass *class; + URLContext *h; + int64_t start; + int64_t end; + int64_t pos; +} SubfileContext; + +#define OFFSET(field) offsetof(SubfileContext, field) +#define D AV_OPT_FLAG_DECODING_PARAM + +static const AVOption subfile_options[] = { + { "start", "start offset", OFFSET(start), AV_OPT_TYPE_INT64, {.i64 = 0}, 0, INT64_MAX, D }, + { "end", "end offset", OFFSET(end), AV_OPT_TYPE_INT64, {.i64 = 0}, 0, INT64_MAX, D }, + { NULL } +}; + +#undef OFFSET +#undef D + +static const AVClass subfile_class = { + .class_name = "subfile", + .item_name = av_default_item_name, + .option = subfile_options, + .version = LIBAVUTIL_VERSION_INT, +}; + +static int slave_seek(URLContext *h) +{ + SubfileContext *c = h->priv_data; + int64_t ret; + + if ((ret = ffurl_seek(c->h, c->pos, SEEK_SET)) != c->pos) { + if (ret >= 0) + ret = AVERROR_BUG; + av_log(h, AV_LOG_ERROR, "Impossible to seek in file: %s\n", + av_err2str(ret)); + return ret; + } + return 0; +} + +static int subfile_open(URLContext *h, const char *filename, int flags, + AVDictionary **options) +{ + SubfileContext *c = h->priv_data; + int ret; + + if (c->end <= c->start) { + av_log(h, AV_LOG_ERROR, "end before start\n"); + return AVERROR(EINVAL); + } + av_strstart(filename, "subfile:", &filename); + ret = ffurl_open(&c->h, filename, flags, &h->interrupt_callback, options); + if (ret < 0) + return ret; + c->pos = c->start; + if ((ret = slave_seek(h)) < 0) { + ffurl_close(c->h); + return ret; + } + return 0; +} + +static int subfile_close(URLContext *h) +{ + SubfileContext *c = h->priv_data; + return ffurl_close(c->h); +} + +static int subfile_read(URLContext *h, unsigned char *buf, int size) +{ + SubfileContext *c = h->priv_data; + int64_t rest = c->end - c->pos; + int ret; + + if (rest <= 0) + return 0; + size = FFMIN(size, rest); + ret = ffurl_read(c->h, buf, size); + if (ret >= 0) + c->pos += ret; + return ret; +} + +static int64_t subfile_seek(URLContext *h, int64_t pos, int whence) +{ + SubfileContext *c = h->priv_data; + int64_t new_pos = -1; + int ret; + + if (whence == AVSEEK_SIZE) + return c->end - c->start; + switch (whence) { + case SEEK_SET: + new_pos = c->start + pos; + break; + case SEEK_CUR: + new_pos += pos; + break; + case SEEK_END: + new_pos = c->end + c->pos; + break; + } + if (new_pos < c->start) + return AVERROR(EINVAL); + c->pos = new_pos; + if ((ret = slave_seek(h)) < 0) + return ret; + return c->pos - c->start; +} + +URLProtocol ff_subfile_protocol = { + .name = "subfile", + .url_open2 = subfile_open, + .url_read = subfile_read, + .url_seek = subfile_seek, + .url_close = subfile_close, + .priv_data_size = sizeof(SubfileContext), + .priv_data_class = &subfile_class, +}; diff --git a/libavformat/subtitles.c b/libavformat/subtitles.c new file mode 100644 index 0000000..fce2bf1 --- /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 0000000..b5a96ec --- /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 0000000..6b38533 --- /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 (!avio_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 0000000..f1b0fdf --- /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 (!avio_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 e6adf69..1aa434a 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 8eb3f70..93a094c 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; @@ -77,6 +133,13 @@ typedef struct SWFContext { AVFifoBuffer *audio_fifo; AVCodecContext *audio_enc, *video_enc; AVStream *video_st; +#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 d7a5314..0f78b17 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 (avio_feof(pb)) + return AVERROR_EOF; tag = avio_rl16(pb); len = tag & 0x3f; @@ -53,13 +55,52 @@ static int get_swf_tag(AVIOContext *pb, int *len_ptr) static int swf_probe(AVProbeData *p) { + if(p->buf_size < 15) + return 0; + /* check file header */ - if ((p->buf[0] == 'F' || p->buf[0] == 'C') && p->buf[1] == 'W' && - p->buf[2] == 'S') - return AVPROBE_SCORE_MAX; - else + if ( AV_RB24(p->buf) != AV_RB24("CWS") + && AV_RB24(p->buf) != AV_RB24("FWS")) return 0; + + if (p->buf[3] >= 20) + return AVPROBE_SCORE_MAX / 4; + + return AVPROBE_SCORE_MAX; +} + +#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) { @@ -68,14 +109,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 +144,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 +177,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 +208,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 +216,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 +226,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 +276,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 +433,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; @@ -220,26 +446,56 @@ static int swf_read_packet(AVFormatContext *s, AVPacket *pkt) goto skip; if ((res = av_new_packet(pkt, len)) < 0) return res; - avio_read(pb, pkt->data, 4); + if (avio_read(pb, pkt->data, 4) != 4) { + av_free_packet(pkt); + return AVERROR_INVALIDDATA; + } if (AV_RB32(pkt->data) == 0xffd8ffd9 || AV_RB32(pkt->data) == 0xffd9ffd8) { /* old SWF files containing SOI/EOI as data start */ /* files created by swink have reversed tag */ pkt->size -= 4; - avio_read(pb, pkt->data, pkt->size); + memset(pkt->data+pkt->size, 0, 4); + res = avio_read(pb, pkt->data, pkt->size); } else { - avio_read(pb, pkt->data + 4, pkt->size - 4); + res = avio_read(pb, pkt->data + 4, pkt->size - 4); + if (res >= 0) + res += 4; + } + if (res != pkt->size) { + if (res < 0) { + av_free_packet(pkt); + return res; + } + av_shrink_packet(pkt, res); } + 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 +503,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 a1fc7b3..3b6f9b3 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) @@ -427,7 +432,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 */ @@ -456,7 +461,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) @@ -486,8 +491,9 @@ static int swf_write_trailer(AVFormatContext *s) enc = s->streams[i]->codec; if (enc->codec_type == AVMEDIA_TYPE_VIDEO) video_enc = enc; - else - av_fifo_free(swf->audio_fifo); + else { + av_fifo_freep(&swf->audio_fifo); + } } put_swf_tag(s, TAG_END); @@ -500,8 +506,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 584cbcc..3eb1a8e 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 (!avio_feof(pb)) { enum TAKMetaDataType type; int size; @@ -71,16 +79,28 @@ 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); + memset(buffer + size - 3, 0, FF_INPUT_BUFFER_PADDING_SIZE); - 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 +108,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 +153,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 +181,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 27a0184..3c3f4f5 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", "set timeout of socket I/O operations", OFFSET(rw_timeout), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, D|E }, +{"listen_timeout", "set 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); @@ -56,19 +79,28 @@ 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; + if (av_find_info_tag(buf, sizeof(buf), "listen", p)) { + char *endptr = NULL; + s->listen = strtol(buf, &endptr, 10); + /* assume if no digits were found it is a request to enable it */ + if (buf == endptr) + 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 +124,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 +168,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 +182,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, MSG_NOSIGNAL); @@ -196,5 +228,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 0000000..68063fa --- /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 0000000..681f943 --- /dev/null +++ b/libavformat/tee.c @@ -0,0 +1,497 @@ +/* + * 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; + } + 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)); + } + *pkt = new_pkt; + + bsf_ctx = bsf_ctx->next; + } + + 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_all = ret; + 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 e8ca04f..714cec6 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,32 +26,37 @@ 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; static int thp_probe(AVProbeData *p) { + double d; /* check file header */ - if (AV_RL32(p->buf) == MKTAG('T', 'H', 'P', '\0')) - return AVPROBE_SCORE_MAX; - else + if (AV_RL32(p->buf) != MKTAG('T', 'H', 'P', '\0')) return 0; + + d = av_int2float(AV_RB32(p->buf + 16)); + if (d < 0.1 || d > 1000 || isnan(d)) + return AVPROBE_SCORE_MAX/4; + + return AVPROBE_SCORE_MAX; } static int thp_read_header(AVFormatContext *s) @@ -59,6 +64,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 +77,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. */ @@ -90,7 +98,7 @@ static int thp_read_header(AVFormatContext *s) for (i = 0; i < thp->compcount; i++) { if (thp->components[i] == 0) { - if (thp->vst != 0) + if (thp->vst) break; /* Video component. */ @@ -106,7 +114,8 @@ static int thp_read_header(AVFormatContext *s) st->codec->codec_tag = 0; /* no fourcc */ 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 +151,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 45300f9..917a331 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 7c78fda..61fb2bf 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 */ @@ -24,6 +24,9 @@ #include "libavutil/avstring.h" #include "libavutil/opt.h" #include "libavutil/parseutils.h" +#include "network.h" +#include "os_support.h" +#include "internal.h" #if CONFIG_GNUTLS #include <gnutls/gnutls.h> #include <gnutls/x509.h> @@ -50,9 +53,6 @@ SSL_CTX_free(c->ctx); \ } while (0) #endif -#include "network.h" -#include "os_support.h" -#include "internal.h" #if HAVE_POLL_H #include <poll.h> #endif @@ -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 103ac4a..ad172f4 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 (avio_feof(pb)) return AVERROR_EOF; ret = av_get_packet(pb, pkt, pkt_size); diff --git a/libavformat/tta.c b/libavformat/tta.c index e5e6e71..7174fd5 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 0ae1510..909b550 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 (avio_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 e6522c3..cac37d5 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 */ @@ -49,6 +49,7 @@ static int txd_read_header(AVFormatContext *s) { avpriv_set_pts_info(st, 64, 1, 5); st->avg_frame_rate = av_inv_q(st->time_base); /* the parameters will be extracted from the compressed bitstream */ + return 0; } @@ -62,7 +63,7 @@ next_chunk: chunk_size = avio_rl32(pb); marker = avio_rl32(pb); - if (s->pb->eof_reached) + if (avio_feof(s->pb)) return AVERROR_EOF; if (marker != TXD_MARKER && marker != TXD_MARKER2) { av_log(s, AV_LOG_ERROR, "marker does not match\n"); @@ -70,17 +71,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 bfa8cf2..376a544 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,90 @@ #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 is_broadcast; 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; + struct sockaddr_storage local_addr_storage; } 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", "set packet 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 }, +{"broadcast", "explicitly allow or disallow broadcast destination", OFFSET(is_broadcast), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, E }, +{"ttl", "set the time to live value (for multicast only)", OFFSET(ttl), AV_OPT_TYPE_INT, {.i64 = 16}, 0, INT_MAX, E }, +{"connect", "set if connect() should 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", "set raise error timeout (only in read mode)", 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) { @@ -84,14 +143,17 @@ static int udp_set_multicast_ttl(int sockfd, int mcastTTL, return 0; } -static int udp_join_multicast_group(int sockfd, struct sockaddr *addr) +static int udp_join_multicast_group(int sockfd, struct sockaddr *addr,struct sockaddr *local_addr) { #ifdef IP_ADD_MEMBERSHIP if (addr->sa_family == AF_INET) { struct ip_mreq mreq; mreq.imr_multiaddr.s_addr = ((struct sockaddr_in *)addr)->sin_addr.s_addr; - mreq.imr_interface.s_addr= INADDR_ANY; + if (local_addr) + mreq.imr_interface= ((struct sockaddr_in *)local_addr)->sin_addr; + else + mreq.imr_interface.s_addr= INADDR_ANY; if (setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (const void *)&mreq, sizeof(mreq)) < 0) { log_net_error(NULL, AV_LOG_ERROR, "setsockopt(IP_ADD_MEMBERSHIP)"); return -1; @@ -113,14 +175,17 @@ static int udp_join_multicast_group(int sockfd, struct sockaddr *addr) return 0; } -static int udp_leave_multicast_group(int sockfd, struct sockaddr *addr) +static int udp_leave_multicast_group(int sockfd, struct sockaddr *addr,struct sockaddr *local_addr) { #ifdef IP_DROP_MEMBERSHIP if (addr->sa_family == AF_INET) { struct ip_mreq mreq; mreq.imr_multiaddr.s_addr = ((struct sockaddr_in *)addr)->sin_addr.s_addr; - mreq.imr_interface.s_addr= INADDR_ANY; + if (local_addr) + mreq.imr_interface= ((struct sockaddr_in *)local_addr)->sin_addr; + else + mreq.imr_interface.s_addr= INADDR_ANY; if (setsockopt(sockfd, IPPROTO_IP, IP_DROP_MEMBERSHIP, (const void *)&mreq, sizeof(mreq)) < 0) { log_net_error(NULL, AV_LOG_ERROR, "setsockopt(IP_DROP_MEMBERSHIP)"); return -1; @@ -248,7 +313,7 @@ static int udp_set_url(struct sockaddr_storage *addr, int addr_len; res0 = udp_resolve_host(hostname, port, SOCK_DGRAM, AF_UNSPEC, 0); - if (res0 == 0) return AVERROR(EIO); + if (!res0) return AVERROR(EIO); memcpy(addr, res0->ai_addr, res0->ai_addrlen); addr_len = res0->ai_addrlen; freeaddrinfo(res0); @@ -260,14 +325,14 @@ static int udp_socket_create(UDPContext *s, struct sockaddr_storage *addr, socklen_t *addr_len, const char *localaddr) { int udp_fd = -1; - struct addrinfo *res0 = NULL, *res = NULL; + struct addrinfo *res0, *res; int family = AF_UNSPEC; if (((struct sockaddr *) &s->dest_addr)->sa_family) family = ((struct sockaddr *) &s->dest_addr)->sa_family; res0 = udp_resolve_host(localaddr[0] ? localaddr : NULL, s->local_port, SOCK_DGRAM, family, AI_PASSIVE); - if (res0 == 0) + if (!res0) goto fail; for (res = res0; res; res=res->ai_next) { udp_fd = ff_socket(res->ai_family, SOCK_DGRAM, 0); @@ -317,6 +382,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 +444,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 +541,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 +556,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 +574,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 +582,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 +602,19 @@ 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); + if (is_output && av_find_info_tag(buf, sizeof(buf), "broadcast", p)) + s->is_broadcast = 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,10 +631,12 @@ 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; + s->local_addr_storage=my_addr; //store for future multicast join + /* Follow the requested reuse option, unless it's multicast in which * case enable reuse unless explicitly disabled. */ @@ -491,6 +646,13 @@ static int udp_open(URLContext *h, const char *uri, int flags) goto fail; } + if (s->is_broadcast) { +#ifdef SO_BROADCAST + if (setsockopt (udp_fd, SOL_SOCKET, SO_BROADCAST, &(s->is_broadcast), sizeof(s->is_broadcast)) != 0) +#endif + goto fail; + } + /* If multicast, try binding the multicast address first, to avoid * receiving UDP packets from other sources aimed at the same UDP * port. This fails on windows. This makes sending to the same address @@ -526,7 +688,7 @@ static int udp_open(URLContext *h, const char *uri, int flags) if (udp_set_multicast_sources(udp_fd, (struct sockaddr *)&s->dest_addr, s->dest_addr_len, include_sources, num_include_sources, 1) < 0) goto fail; } else { - if (udp_join_multicast_group(udp_fd, (struct sockaddr *)&s->dest_addr) < 0) + if (udp_join_multicast_group(udp_fd, (struct sockaddr *)&s->dest_addr,(struct sockaddr *)&s->local_addr_storage) < 0) goto fail; } if (num_exclude_sources) { @@ -544,12 +706,20 @@ static int udp_open(URLContext *h, const char *uri, int flags) goto fail; } } else { - /* set udp recv buffer size to the largest possible udp packet size to - * avoid losing data on OSes that set this too low by default. */ + /* set udp recv buffer size to the requested value (default 64K) */ tmp = s->buffer_size; if (setsockopt(udp_fd, SOL_SOCKET, SO_RCVBUF, &tmp, sizeof(tmp)) < 0) { log_net_error(h, AV_LOG_WARNING, "setsockopt(SO_RECVBUF)"); } + len = sizeof(tmp); + if (getsockopt(udp_fd, SOL_SOCKET, SO_RCVBUF, &tmp, &len) < 0) { + log_net_error(h, AV_LOG_WARNING, "getsockopt(SO_RCVBUF)"); + } else { + av_log(h, AV_LOG_DEBUG, "end receive buffer size reported is %d\n", tmp); + if(tmp < s->buffer_size) + av_log(h, AV_LOG_WARNING, "attempted to set receive buffer to size %d but it only ended up set as %d", s->buffer_size, tmp); + } + /* make the socket non-blocking */ ff_socket_nonblock(udp_fd, 1); } @@ -566,10 +736,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_freep(&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 +784,50 @@ static int udp_read(URLContext *h, uint8_t *buf, int size) { UDPContext *s = h->priv_data; int ret; +#if HAVE_PTHREAD_CANCEL + int avail, nonblock = h->flags & AVIO_FLAG_NONBLOCK; + + 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 +835,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; } @@ -617,8 +865,20 @@ static int udp_close(URLContext *h) UDPContext *s = h->priv_data; if (s->is_multicast && (h->flags & AVIO_FLAG_READ)) - udp_leave_multicast_group(s->udp_fd, (struct sockaddr *)&s->dest_addr); + udp_leave_multicast_group(s->udp_fd, (struct sockaddr *)&s->dest_addr,(struct sockaddr *)&s->local_addr_storage); closesocket(s->udp_fd); +#if HAVE_PTHREAD_CANCEL + if (s->thread_started) { + int ret; + 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_freep(&s->fifo); return 0; } @@ -630,5 +890,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/uncodedframecrcenc.c b/libavformat/uncodedframecrcenc.c new file mode 100644 index 0000000..414683f --- /dev/null +++ b/libavformat/uncodedframecrcenc.c @@ -0,0 +1,172 @@ +/* +* Copyright (c) 2013 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/adler32.h" +#include "libavutil/avassert.h" +#include "libavutil/bprint.h" +#include "libavutil/imgutils.h" +#include "libavutil/pixdesc.h" +#include "avformat.h" +#include "internal.h" + +/* Identical to Adler32 when the type is uint8_t. */ +#define DEFINE_CKSUM_LINE(name, type, conv) \ +static void cksum_line_ ## name(unsigned *cksum, void *data, unsigned size) \ +{ \ + type *p = data; \ + unsigned a = *cksum & 0xFFFF, b = *cksum >> 16; \ + for (; size > 0; size--, p++) { \ + a = (a + (unsigned)(conv)) % 65521; \ + b = (b + a) % 65521; \ + } \ + *cksum = a | (b << 16); \ +} + +DEFINE_CKSUM_LINE(u8, uint8_t, *p) +DEFINE_CKSUM_LINE(s16, int16_t, *p + 0x8000) +DEFINE_CKSUM_LINE(s32, int32_t, *p + 0x80000000) +DEFINE_CKSUM_LINE(flt, float, *p * 0x80000000 + 0x80000000) +DEFINE_CKSUM_LINE(dbl, double, *p * 0x80000000 + 0x80000000) + +static void video_frame_cksum(AVBPrint *bp, AVFrame *frame) +{ + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(frame->format); + int i, y; + uint8_t *data; + int linesize[5] = { 0 }; + + av_bprintf(bp, ", %d x %d", frame->width, frame->height); + if (!desc) { + av_bprintf(bp, ", unknown"); + return; + } + if (av_image_fill_linesizes(linesize, frame->format, frame->width) < 0) + return; + av_bprintf(bp, ", %s", desc->name); + for (i = 0; linesize[i]; i++) { + unsigned cksum = 0; + int h = frame->height; + if ((i == 1 || i == 2) && desc->nb_components >= 3) + h = -((-h) >> desc->log2_chroma_h); + data = frame->data[i]; + for (y = 0; y < h; y++) { + cksum = av_adler32_update(cksum, data, linesize[i]); + data += frame->linesize[i]; + } + av_bprintf(bp, ", 0x%08x", cksum); + } +} + +static void audio_frame_cksum(AVBPrint *bp, AVFrame *frame) +{ + int nb_planes, nb_samples, p; + const char *name; + + nb_planes = av_frame_get_channels(frame); + nb_samples = frame->nb_samples; + if (!av_sample_fmt_is_planar(frame->format)) { + nb_samples *= nb_planes; + nb_planes = 1; + } + name = av_get_sample_fmt_name(frame->format); + av_bprintf(bp, ", %d samples", frame->nb_samples); + av_bprintf(bp, ", %s", name ? name : "unknown"); + for (p = 0; p < nb_planes; p++) { + uint32_t cksum = 0; + void *d = frame->extended_data[p]; + switch (frame->format) { + case AV_SAMPLE_FMT_U8: + case AV_SAMPLE_FMT_U8P: + cksum_line_u8(&cksum, d, nb_samples); + break; + case AV_SAMPLE_FMT_S16: + case AV_SAMPLE_FMT_S16P: + cksum_line_s16(&cksum, d, nb_samples); + break; + case AV_SAMPLE_FMT_S32: + case AV_SAMPLE_FMT_S32P: + cksum_line_s32(&cksum, d, nb_samples); + break; + case AV_SAMPLE_FMT_FLT: + case AV_SAMPLE_FMT_FLTP: + cksum_line_flt(&cksum, d, nb_samples); + break; + case AV_SAMPLE_FMT_DBL: + case AV_SAMPLE_FMT_DBLP: + cksum_line_dbl(&cksum, d, nb_samples); + break; + default: + av_assert0(!"reached"); + } + av_bprintf(bp, ", 0x%08x", cksum); + } +} + +static int write_frame(struct AVFormatContext *s, int stream_index, + AVFrame **frame, unsigned flags) +{ + AVBPrint bp; + int ret = 0; + enum AVMediaType type; + const char *type_name; + + if ((flags & AV_WRITE_UNCODED_FRAME_QUERY)) + return 0; + + av_bprint_init(&bp, 0, AV_BPRINT_SIZE_UNLIMITED); + av_bprintf(&bp, "%d, %10"PRId64"", + stream_index, (*frame)->pts); + type = s->streams[stream_index]->codec->codec_type; + type_name = av_get_media_type_string(type); + av_bprintf(&bp, ", %s", type_name ? type_name : "unknown"); + switch (type) { + case AVMEDIA_TYPE_VIDEO: + video_frame_cksum(&bp, *frame); + break; + case AVMEDIA_TYPE_AUDIO: + audio_frame_cksum(&bp, *frame); + break; + } + + av_bprint_chars(&bp, '\n', 1); + if (av_bprint_is_complete(&bp)) + avio_write(s->pb, bp.str, bp.len); + else + ret = AVERROR(ENOMEM); + av_bprint_finalize(&bp, NULL); + return ret; +} + +static int write_packet(struct AVFormatContext *s, AVPacket *pkt) +{ + return AVERROR(ENOSYS); +} + +AVOutputFormat ff_uncodedframecrc_muxer = { + .name = "uncodedframecrc", + .long_name = NULL_IF_CONFIG_SMALL("uncoded framecrc testing"), + .audio_codec = AV_CODEC_ID_PCM_S16LE, + .video_codec = AV_CODEC_ID_RAWVIDEO, + .write_header = ff_framehash_write_header, + .write_packet = write_packet, + .write_uncoded_frame = write_frame, + .flags = AVFMT_VARIABLE_FPS | AVFMT_TS_NONSTRICT | + AVFMT_TS_NEGATIVE, +}; diff --git a/libavformat/unix.c b/libavformat/unix.c index 5c6f576..3977065 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 503b36e..a1da82e 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 92cd5f1..acfb0cf 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 40be4d5..c4d1642 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 49af9ba..283d912 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 b43f319..cb81ebc 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 faad9c9..6e828f7 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 */ @@ -35,12 +35,15 @@ #include "libavutil/parseutils.h" #include "libavutil/pixdesc.h" #include "libavutil/time.h" +#include "libavutil/timestamp.h" #include "libavcodec/bytestream.h" #include "libavcodec/internal.h" +#include "libavcodec/raw.h" #include "audiointerleave.h" #include "avformat.h" +#include "avio_internal.h" #include "id3v2.h" #include "internal.h" #include "metadata.h" @@ -52,36 +55,136 @@ /** * @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) +MAKE_ACCESSORS(AVFormatContext, format, int, metadata_header_padding) +MAKE_ACCESSORS(AVFormatContext, format, void *, opaque) +MAKE_ACCESSORS(AVFormatContext, format, av_format_control_message, control_message_cb) + +int64_t av_stream_get_end_pts(const AVStream *st) +{ + return st->pts.val; +} + +struct AVCodecParserContext *av_stream_get_parser(const AVStream *st) +{ + return st->parser; +} + +void av_format_inject_global_side_data(AVFormatContext *s) +{ + int i; + s->internal->inject_global_side_data = 1; + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + st->inject_global_side_data = 1; + } +} + +static const 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 +192,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 +212,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) @@ -139,7 +246,7 @@ int av_filename_number_test(const char *filename) } static int set_codec_from_probe_data(AVFormatContext *s, AVStream *st, - AVProbeData *pd, int score) + AVProbeData *pd) { static const struct { const char *name; @@ -151,15 +258,17 @@ static int set_codec_from_probe_data(AVFormatContext *s, AVStream *st, { "dts", AV_CODEC_ID_DTS, AVMEDIA_TYPE_AUDIO }, { "eac3", AV_CODEC_ID_EAC3, AVMEDIA_TYPE_AUDIO }, { "h264", AV_CODEC_ID_H264, AVMEDIA_TYPE_VIDEO }, - { "latm", AV_CODEC_ID_AAC_LATM, AVMEDIA_TYPE_AUDIO }, + { "hevc", AV_CODEC_ID_HEVC, 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", @@ -169,44 +278,61 @@ static int set_codec_from_probe_data(AVFormatContext *s, AVStream *st, if (!strcmp(fmt->name, fmt_id_type[i].name)) { st->codec->codec_id = fmt_id_type[i].id; st->codec->codec_type = fmt_id_type[i].type; - break; + return score; } } } - return !!fmt; + return 0; } /************************************************************/ /* input media file */ +int av_demuxer_open(AVFormatContext *ic) { + int err; + + 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; +} + /* 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->format_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->format_probesize); } static AVPacket *add_to_pktbuf(AVPacketList **packet_buffer, AVPacket *pkt, @@ -227,13 +353,19 @@ 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++) if (s->streams[i]->disposition & AV_DISPOSITION_ATTACHED_PIC && s->streams[i]->discard < AVDISCARD_ALL) { AVPacket copy = s->streams[i]->attached_pic; + if (copy.size <= 0) { + av_log(s, AV_LOG_WARNING, + "Attached picture on stream %d has invalid size, " + "ignoring\n", i); + continue; + } copy.buf = av_buffer_ref(copy.buf); if (!copy.buf) return AVERROR(ENOMEM); @@ -254,6 +386,10 @@ int avformat_open_input(AVFormatContext **ps, const char *filename, 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; @@ -265,6 +401,8 @@ int avformat_open_input(AVFormatContext **ps, const char *filename, 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) { @@ -293,21 +431,26 @@ int avformat_open_input(AVFormatContext **ps, const char *filename, /* e.g. AVFMT_NOFILE formats will not have a AVIOContext */ if (s->pb) - ff_id3v2_read(s, ID3v2_DEFAULT_MAGIC, &id3v2_extra_meta); + ff_id3v2_read(s, ID3v2_DEFAULT_MAGIC, &id3v2_extra_meta, 0); - 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; @@ -331,44 +474,138 @@ 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, + 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_STREAM_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; } +static int update_wrap_reference(AVFormatContext *s, AVStream *st, int stream_index, AVPacket *pkt) +{ + int64_t ref = pkt->dts; + int i, pts_wrap_behavior; + int64_t pts_wrap_reference; + AVProgram *first_program; + + if (ref == AV_NOPTS_VALUE) + ref = pkt->pts; + if (st->pts_wrap_reference != AV_NOPTS_VALUE || st->pts_wrap_bits >= 63 || ref == AV_NOPTS_VALUE || !s->correct_ts_overflow) + return 0; + ref &= (1LL << st->pts_wrap_bits)-1; + + // reference time stamp should be 60 s before first time stamp + pts_wrap_reference = ref - 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 + pts_wrap_behavior = (ref < (1LL << st->pts_wrap_bits) - (1LL << st->pts_wrap_bits-3)) || + (ref < (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; + + 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; +} + int ff_read_packet(AVFormatContext *s, AVPacket *pkt) { int ret, i, err; @@ -380,16 +617,10 @@ 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); @@ -409,6 +640,7 @@ int ff_read_packet(AVFormatContext *s, AVPacket *pkt) if (st->probe_packets) if ((err = probe_codec(s, st, NULL)) < 0) return err; + av_assert0(st->request_probe <= 0); } continue; } @@ -422,25 +654,33 @@ int ff_read_packet(AVFormatContext *s, AVPacket *pkt) continue; } + if (pkt->stream_index >= (unsigned)s->nb_streams) { + av_log(s, AV_LOG_ERROR, "Invalid stream index %d\n", pkt->stream_index); + continue; + } + st = s->streams[pkt->stream_index]; - 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 (update_wrap_reference(s, st, pkt->stream_index, pkt) && st->pts_wrap_behavior == AV_PTS_WRAP_SUB_OFFSET) { + // correct first time stamps to negative values + if (!is_relative(st->first_dts)) + st->first_dts = wrap_timestamp(st, st->first_dts); + if (!is_relative(st->start_time)) + st->start_time = wrap_timestamp(st, st->start_time); + if (!is_relative(st->cur_dts)) + st->cur_dts = wrap_timestamp(st, st->cur_dts); } - if (!pktl && (st->codec->codec_id != AV_CODEC_ID_PROBE || - !st->probe_packets)) + 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); @@ -451,8 +691,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; +} + /** * Return the frame duration in seconds. Return 0 if not available. */ @@ -465,9 +724,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; @@ -499,43 +758,138 @@ 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->packet_buffer_end) + return s->parse_queue; + return NULL; +} + +static int64_t select_from_pts_buffer(AVStream *st, int64_t *pts_buffer, int64_t dts) { + int onein_oneout = st->codec->codec_id != AV_CODEC_ID_H264 && + st->codec->codec_id != AV_CODEC_ID_HEVC; + + if(!onein_oneout) { + int delay = st->codec->has_b_frames; + int i; + + if (dts == AV_NOPTS_VALUE) { + int64_t best_score = INT64_MAX; + for (i = 0; i<delay; i++) { + if (st->pts_reorder_error_count[i]) { + int64_t score = st->pts_reorder_error[i] / st->pts_reorder_error_count[i]; + if (score < best_score) { + best_score = score; + dts = pts_buffer[i]; + } + } + } + } else { + for (i = 0; i<delay; i++) { + if (pts_buffer[i] != AV_NOPTS_VALUE) { + int64_t diff = FFABS(pts_buffer[i] - dts) + + (uint64_t)st->pts_reorder_error[i]; + diff = FFMAX(diff, st->pts_reorder_error[i]); + st->pts_reorder_error[i] = diff; + st->pts_reorder_error_count[i]++; + if (st->pts_reorder_error_count[i] > 250) { + st->pts_reorder_error[i] >>= 1; + st->pts_reorder_error_count[i] >>= 1; + } + } + } + } + } + + if (dts == AV_NOPTS_VALUE) + dts = pts_buffer[0]; + + return dts; } 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->packet_buffer ? s->packet_buffer : s->parse_queue; + 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) + 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]); + + pktl->pkt.dts = select_from_pts_buffer(st, pts_buffer, pktl->pkt.dts); + } } + if (st->start_time == AV_NOPTS_VALUE) st->start_time = pts; } @@ -543,12 +897,15 @@ 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->packet_buffer ? s->packet_buffer : s->parse_queue; + int64_t cur_dts = RELATIVE_TS_BASE; if (st->first_dts != AV_NOPTS_VALUE) { + if (st->update_initial_durations_done) + return; + st->update_initial_durations_done = 1; 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 || @@ -557,27 +914,36 @@ static void update_initial_durations(AVFormatContext *s, AVStream *st, 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->packet_buffer ? s->packet_buffer : s->parse_queue; 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 && + (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; } @@ -586,13 +952,43 @@ static void compute_pkt_fields(AVFormatContext *s, AVStream *st, { int num, den, presentation_delayed, delay, i; int64_t offset; + AVRational duration; + int onein_oneout = st->codec->codec_id != AV_CODEC_ID_H264 && + st->codec->codec_id != AV_CODEC_ID_HEVC; if (s->flags & AVFMT_FLAG_NOFILLIN) return; + if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO && pkt->dts != AV_NOPTS_VALUE) { + if (pkt->dts == pkt->pts && st->last_dts_for_order_check != AV_NOPTS_VALUE) { + if (st->last_dts_for_order_check <= pkt->dts) { + st->dts_ordered++; + } else { + av_log(s, st->dts_misordered ? AV_LOG_DEBUG : AV_LOG_WARNING, + "DTS %"PRIi64" < %"PRIi64" out of order\n", + pkt->dts, + st->last_dts_for_order_check); + st->dts_misordered++; + } + if (st->dts_ordered + st->dts_misordered > 250) { + st->dts_ordered >>= 1; + st->dts_misordered >>= 1; + } + } + + st->last_dts_for_order_check = pkt->dts; + if (st->dts_ordered < 8*st->dts_misordered && pkt->dts == pkt->pts) + pkt->dts = AV_NOPTS_VALUE; + } + if ((s->flags & AVFMT_FLAG_IGNDTS) && pkt->pts != AV_NOPTS_VALUE) pkt->dts = AV_NOPTS_VALUE; + if (pc && pc->pict_type == AV_PICTURE_TYPE_B + && !st->codec->has_b_frames) + //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; @@ -606,7 +1002,10 @@ 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 MPEG-2 in MPEG-PS lack dts (issue #171 / input_file.mpg). @@ -615,23 +1014,27 @@ static void compute_pkt_fields(AVFormatContext *s, AVStream *st, * 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") + && strcmp(s->iformat->name, "flv")) // 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) { + duration = av_mul_q((AVRational) {pkt->duration, 1}, st->time_base); + 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, + duration = (AVRational) {num, den}; + 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 */ if (pc && st->need_parsing == AVSTREAM_PARSE_TIMESTAMPS && pkt->size) { @@ -650,20 +1053,19 @@ static void compute_pkt_fields(AVFormatContext *s, AVStream *st, 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); - /* Interpolate PTS and DTS if they are not present. We skip H.264 + "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) { + onein_oneout) { if (presentation_delayed) { /* DTS = decompression timestamp */ /* 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; @@ -679,58 +1081,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) { - 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; - } + pkt->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 = av_add_stable(st->time_base, pkt->pts, duration, 1); } } - 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]; - // We skipped it above so we try here. - if (st->codec->codec_id == AV_CODEC_ID_H264) - // This should happen on the first packet - update_initial_timestamps(s, pkt->stream_index, pkt->dts, pkt->pts); - if (pkt->dts > st->cur_dts) - st->cur_dts = pkt->dts; + + pkt->dts = select_from_pts_buffer(st, st->pts_buffer, pkt->dts); } + // We skipped it above so we try here. + if (!onein_oneout) + // This should happen on the first packet + update_initial_timestamps(s, pkt->stream_index, pkt->dts, pkt->pts, pkt); + 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; @@ -764,6 +1148,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)) { @@ -775,6 +1162,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; @@ -808,19 +1196,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; @@ -901,27 +1288,34 @@ static int read_frame_internal(AVFormatContext *s, AVPacket *pkt) 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", - cur_pkt.stream_index, cur_pkt.pts, - cur_pkt.dts, cur_pkt.size); + "Invalid timestamps stream=%d, pts=%s, dts=%s, size=%d\n", + cur_pkt.stream_index, + 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", - cur_pkt.stream_index, cur_pkt.pts, cur_pkt.dts, + "ff_read_packet stream=%d, pts=%s, dts=%s, size=%d, duration=%d, flags=%d\n", + cur_pkt.stream_index, + av_ts2str(cur_pkt.pts), + av_ts2str(cur_pkt.dts), cur_pkt.size, cur_pkt.duration, cur_pkt.flags); if (st->need_parsing && !st->parser && !(s->flags & AVFMT_FLAG_NOPARSE)) { st->parser = av_parser_init(st->codec->codec_id); - if (!st->parser) + 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) + } 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; } if (!st->need_parsing || !st->parser) { @@ -942,11 +1336,54 @@ 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 (ret >= 0) { + AVStream *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 (st->inject_global_side_data) { + for (i = 0; i < st->nb_side_data; i++) { + AVPacketSideData *src_sd = &st->side_data[i]; + uint8_t *dst_data; + + if (av_packet_get_side_data(pkt, src_sd->type, NULL)) + continue; + + dst_data = av_packet_new_side_data(pkt, src_sd->type, src_sd->size); + if (!dst_data) { + av_log(s, AV_LOG_WARNING, "Could not inject global side data\n"); + continue; + } + + memcpy(dst_data, src_sd->data, src_sd->size); + } + st->inject_global_side_data = 0; + } + + if (!(s->flags & AVFMT_FLAG_KEEP_SIDE_DATA)) + av_packet_merge_side_data(pkt); + } + av_opt_get_dict_val(s, "metadata", AV_OPT_SEARCH_CHILDREN, &metadata); if (metadata) { s->event_flags |= AVFMT_EVENT_FLAG_METADATA_UPDATED; @@ -957,9 +1394,11 @@ static int read_frame_internal(AVFormatContext *s, AVPacket *pkt) if (s->debug & FF_FDEBUG_TS) av_log(s, AV_LOG_DEBUG, - "read_frame_internal stream=%d, pts=%"PRId64", dts=%"PRId64", " + "read_frame_internal stream=%d, pts=%s, dts=%s, " "size=%d, duration=%d, flags=%d\n", - pkt->stream_index, pkt->pts, pkt->dts, + pkt->stream_index, + av_ts2str(pkt->pts), + av_ts2str(pkt->dts), pkt->size, pkt->duration, pkt->flags); return ret; @@ -969,15 +1408,20 @@ 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) { @@ -985,23 +1429,42 @@ 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, + st = s->streams[next_pkt->stream_index]; + if (!(next_pkt->pts == AV_NOPTS_VALUE && st->discard < AVDISCARD_ALL && + 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); @@ -1017,6 +1480,21 @@ 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 ((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 */ @@ -1034,23 +1512,36 @@ static void flush_packet_queue(AVFormatContext *s) int av_find_default_stream_index(AVFormatContext *s) { - int first_audio_index = -1; int i; AVStream *st; + int best_stream = 0; + int best_score = -1; if (s->nb_streams <= 0) return -1; for (i = 0; i < s->nb_streams; i++) { + int score = 0; st = s->streams[i]; if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO && !(st->disposition & AV_DISPOSITION_ATTACHED_PIC)) { - return i; + if (!st->codec->width && !st->codec->height && !st->codec_info_nb_frames) + score += 25; + else + score += 100; + } + if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) { + if (!st->codec->sample_rate && !st->codec_info_nb_frames) + score += 12; + else + score += 50; + } + + if (score > best_score) { + best_score = score; + best_stream = i; } - if (first_audio_index < 0 && - st->codec->codec_type == AVMEDIA_TYPE_AUDIO) - first_audio_index = i; } - return first_audio_index >= 0 ? first_audio_index : 0; + return best_stream; } /** Flush the frame reader. */ @@ -1070,13 +1561,20 @@ void ff_read_frame_flush(AVFormatContext *s) st->parser = NULL; } st->last_IP_pts = AV_NOPTS_VALUE; - /* We set the current DTS to an unspecified origin. */ - st->cur_dts = AV_NOPTS_VALUE; + st->last_dts_for_order_check = AV_NOPTS_VALUE; + if (st->first_dts == AV_NOPTS_VALUE) + st->cur_dts = RELATIVE_TS_BASE; + else + /* We set the current DTS to an unspecified origin. */ + st->cur_dts = AV_NOPTS_VALUE; st->probe_packets = MAX_PROBE_PACKETS; for (j = 0; j < MAX_REORDER_DELAY + 1; j++) st->pts_buffer[j] = AV_NOPTS_VALUE; + + if (s->internal->inject_global_side_data) + st->inject_global_side_data = 1; } } @@ -1119,6 +1617,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) * @@ -1134,7 +1641,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) { @@ -1160,6 +1667,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); @@ -1204,6 +1712,15 @@ int av_index_search_timestamp(AVStream *st, int64_t wanted_timestamp, int flags) 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) { @@ -1217,7 +1734,7 @@ int ff_seek_frame_binary(AVFormatContext *s, int stream_index, 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; @@ -1237,23 +1754,23 @@ int ff_seek_frame_binary(AVFormatContext *s, int stream_index, 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); + " dts_max=%s\n", pos_max, pos_limit, av_ts2str(ts_max)); } } @@ -1266,11 +1783,50 @@ int ff_seek_frame_binary(AVFormatContext *s, int stream_index, 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, @@ -1279,45 +1835,35 @@ int64_t ff_gen_search(AVFormatContext *s, int stream_index, int64_t target_ts, 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) @@ -1325,8 +1871,9 @@ 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) { @@ -1350,20 +1897,20 @@ int64_t ff_gen_search(AVFormatContext *s, int stream_index, int64_t target_ts, start_pos = pos; // May pass pos_limit instead of -1. - ts = read_timestamp(s, stream_index, &pos, INT64_MAX); + ts = ff_read_timestamp(s, stream_index, &pos, INT64_MAX, read_timestamp); 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"); return -1; } - assert(ts != AV_NOPTS_VALUE); if (target_ts <= ts) { pos_limit = start_pos - 1; pos_max = pos; @@ -1377,12 +1924,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; } @@ -1402,6 +1951,8 @@ static int seek_frame_byte(AVFormatContext *s, int stream_index, avio_seek(s->pb, pos, SEEK_SET); + s->io_repositioned = 1; + return 0; } @@ -1423,9 +1974,10 @@ static int seek_frame_generic(AVFormatContext *s, int stream_index, 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; @@ -1442,9 +1994,14 @@ static int seek_frame_generic(AVFormatContext *s, int stream_index, 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); } @@ -1510,10 +2067,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; } @@ -1523,15 +2092,33 @@ int avformat_seek_file(AVFormatContext *s, int stream_index, int64_t min_ts, { 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; } @@ -1541,13 +2128,19 @@ int avformat_seek_file(AVFormatContext *s, int stream_index, int64_t min_ts, // 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 } /*******************************************************/ @@ -1579,12 +2172,14 @@ 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++) { @@ -1592,13 +2187,24 @@ static void update_stream_timings(AVFormatContext *ic) 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, @@ -1606,17 +2212,33 @@ static void update_stream_timings(AVFormatContext *ic) 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) - /* compute the bitrate */ - ic->bit_rate = (double) filesize * 8.0 * AV_TIME_BASE / - (double) ic->duration; + } + if (ic->pb && (filesize = avio_size(ic->pb)) > 0 && ic->duration != AV_NOPTS_VALUE) { + /* compute the bitrate */ + double bitrate = (double) filesize * 8.0 * AV_TIME_BASE / + (double) ic->duration; + if (bitrate >= 0 && bitrate <= INT_MAX) + ic->bit_rate = bitrate; } } @@ -1642,7 +2264,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 */ @@ -1667,27 +2289,34 @@ static void estimate_timings_from_bit_rate(AVFormatContext *ic) filesize = ic->pb ? avio_size(ic->pb) : 0; 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) + st = ic->streams[i]; + 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) { AVPacket pkt1, *pkt = &pkt1; AVStream *st; - int read_size, i, ret; - int64_t end_time; + int num, den, read_size, i, ret; + int found_duration = 0; + int is_end; int64_t filesize, offset, duration; int retry = 0; @@ -1696,9 +2325,11 @@ static void estimate_timings_from_pts(AVFormatContext *ic, int64_t old_offset) for (i = 0; i < ic->nb_streams; i++) { st = ic->streams[i]; - if (st->start_time == AV_NOPTS_VALUE && st->first_dts == AV_NOPTS_VALUE) + if (st->start_time == AV_NOPTS_VALUE && + st->first_dts == AV_NOPTS_VALUE && + st->codec->codec_type != AVMEDIA_TYPE_UNKNOWN) av_log(st->codec, AV_LOG_WARNING, - "start time is not set in estimate_timings_from_pts\n"); + "start time for stream %d is not set in estimate_timings_from_pts\n", i); if (st->parser) { av_parser_close(st->parser); @@ -1706,11 +2337,12 @@ static void estimate_timings_from_pts(AVFormatContext *ic, int64_t old_offset) } } + av_opt_set(ic, "skip_changes", "1", AV_OPT_SEARCH_CHILDREN); /* estimate the end time (duration) */ /* XXX: may need to support wrapping */ filesize = ic->pb ? avio_size(ic->pb) : 0; - end_time = AV_NOPTS_VALUE; do { + is_end = found_duration; offset = filesize - (DURATION_MAX_READ_SIZE << retry); if (offset < 0) offset = 0; @@ -1731,31 +2363,76 @@ static void estimate_timings_from_pts(AVFormatContext *ic, int64_t old_offset) if (pkt->pts != AV_NOPTS_VALUE && (st->start_time != AV_NOPTS_VALUE || st->first_dts != AV_NOPTS_VALUE)) { - duration = end_time = pkt->pts; + if (pkt->duration == 0) { + ff_compute_frame_duration(&num, &den, st, st->parser, 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); + } + } + duration = pkt->pts + pkt->duration; + found_duration = 1; if (st->start_time != AV_NOPTS_VALUE) 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); } - } while (end_time == AV_NOPTS_VALUE && - filesize > (DURATION_MAX_READ_SIZE << retry) && + + /* check if all audio/video streams have valid duration */ + if (!is_end) { + is_end = 1; + for (i = 0; i < ic->nb_streams; i++) { + st = ic->streams[i]; + switch (st->codec->codec_type) { + case AVMEDIA_TYPE_VIDEO: + case AVMEDIA_TYPE_AUDIO: + if (st->duration == AV_NOPTS_VALUE) + is_end = 0; + } + } + } + } while (!is_end && + offset && ++retry <= DURATION_MAX_RETRY); + av_opt_set(ic, "skip_changes", "0", AV_OPT_SEARCH_CHILDREN); + + /* warn about audio/video streams which duration could not be estimated */ + for (i = 0; i < ic->nb_streams; i++) { + st = ic->streams[i]; + if (st->duration == AV_NOPTS_VALUE) { + switch (st->codec->codec_type) { + case AVMEDIA_TYPE_VIDEO: + case AVMEDIA_TYPE_AUDIO: + if (st->start_time != AV_NOPTS_VALUE || st->first_dts != AV_NOPTS_VALUE) { + av_log(ic, AV_LOG_DEBUG, "stream %d : no PTS found at end of file, duration not set\n", i); + } else + av_log(ic, AV_LOG_DEBUG, "stream %d : no TS found at start of file, duration not set\n", i); + } + } + } fill_all_stream_timings(ic); avio_seek(ic->pb, old_offset, SEEK_SET); for (i = 0; i < ic->nb_streams; i++) { + int j; + st = ic->streams[i]; st->cur_dts = st->first_dts; st->last_IP_pts = AV_NOPTS_VALUE; + st->last_dts_for_order_check = AV_NOPTS_VALUE; + for (j = 0; j < MAX_REORDER_DELAY + 1; j++) + st->pts_buffer[j] = AV_NOPTS_VALUE; } } @@ -1776,15 +2453,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); @@ -1805,56 +2483,75 @@ 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) + + if ( avctx->codec_id == AV_CODEC_ID_NONE + && avctx->codec_type != AVMEDIA_TYPE_DATA) + FAIL("unknown codec"); 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; + 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, +static int try_decode_frame(AVFormatContext *s, AVStream *st, AVPacket *avpkt, AVDictionary **options) { const AVCodec *codec; int got_picture = 1, ret = 0; AVFrame *frame = av_frame_alloc(); + AVSubtitle subtitle; AVPacket pkt = *avpkt; if (!frame) return AVERROR(ENOMEM); - if (!avcodec_is_open(st->codec) && !st->info->found_decoder) { + if (!avcodec_is_open(st->codec) && + st->info->found_decoder <= 0 && + (st->codec->codec_id != -st->info->found_decoder || !st->codec->codec_id)) { 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; + st->info->found_decoder = -st->codec->codec_id; ret = -1; goto fail; } @@ -1866,7 +2563,7 @@ static int try_decode_frame(AVStream *st, AVPacket *avpkt, if (!options) av_dict_free(&thread_opt); if (ret < 0) { - st->info->found_decoder = -1; + st->info->found_decoder = -st->codec->codec_id; goto fail; } st->info->found_decoder = 1; @@ -1880,7 +2577,7 @@ static int try_decode_frame(AVStream *st, AVPacket *avpkt, while ((pkt.size > 0 || (!pkt.data && got_picture)) && ret >= 0 && - (!has_codec_parameters(st) || !has_decode_delay_been_guessed(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; @@ -1892,18 +2589,26 @@ static int try_decode_frame(AVStream *st, AVPacket *avpkt, 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: av_frame_free(&frame); return ret; @@ -1943,6 +2648,7 @@ enum AVCodecID ff_get_pcm_codec_id(int bps, int flt, int be, int sflags) return AV_CODEC_ID_NONE; } } else { + bps += 7; bps >>= 3; if (sflags & (1 << (bps - 1))) { switch (bps) { @@ -1976,11 +2682,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; } @@ -2025,31 +2745,255 @@ static int get_std_framerate(int i) if (i < 60 * 12) return (i + 1) * 1001; else - return ((const int[]) { 24, 30, 60, 12, 15 })[i - 60 * 12] * 1000 * 12; + return ((const int[]) { 24, 30, 60, 12, 15, 48 })[i - 60 * 12] * 1000 * 12; +} + +/* Is the time base unreliable? + * This is a heuristic to balance between quick acceptance of the values in + * the headers vs. some extra checks. + * Old DivX and Xvid often have nonsense timebases like 1fps or 2fps. + * MPEG-2 commonly misuses field repeat flags to store different framerates. + * And there are "variable" fps files this needs to detect as well. */ +static int tb_unreliable(AVCodecContext *c) +{ + if (c->time_base.den >= 101L * c->time_base.num || + 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_GIF || + c->codec_id == AV_CODEC_ID_H264) + return 1; + 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 ff_get_extradata(AVCodecContext *avctx, AVIOContext *pb, int size) +{ + int ret = ff_alloc_extradata(avctx, size); + if (ret < 0) + return ret; + ret = avio_read(pb, avctx->extradata, size); + if (ret != size) { + av_freep(&avctx->extradata); + avctx->extradata_size = 0; + av_log(avctx, AV_LOG_ERROR, "Failed to read extradata of size %d\n", size); + return ret < 0 ? ret : AVERROR_INVALIDDATA; + } + + return ret; +} + +int ff_rfps_add_frame(AVFormatContext *ic, AVStream *st, int64_t ts) +{ + int i, j; + int64_t last = st->info->last_dts; + + if ( ts != AV_NOPTS_VALUE && last != AV_NOPTS_VALUE && ts > last + && ts - (uint64_t)last < INT64_MAX) { + double dts = (is_relative(ts) ? ts - RELATIVE_TS_BASE : ts) * av_q2d(st->time_base); + int64_t duration = ts - 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++) { + if (st->info->duration_error[0][1][i] < 1e10) { + 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++; + st->info->rfps_duration_sum += duration; + + if (st->info->duration_count % 10 == 0) { + int n = st->info->duration_count; + for (i = 0; i<MAX_STD_TIMEBASES; i++) { + if (st->info->duration_error[0][1][i] < 1e10) { + double a0 = st->info->duration_error[0][0][i] / n; + double error0 = st->info->duration_error[0][1][i] / n - a0*a0; + double a1 = st->info->duration_error[1][0][i] / n; + double error1 = st->info->duration_error[1][1][i] / n - a1*a1; + if (error0 > 0.04 && error1 > 0.04) { + st->info->duration_error[0][1][i] = 2e10; + st->info->duration_error[1][1][i] = 2e10; + } + } + } + } + + // ignore the first 4 values, they might have some random jitter + if (st->info->duration_count > 3 && is_relative(ts) == is_relative(last)) + st->info->duration_gcd = av_gcd(st->info->duration_gcd, duration); + } + if (ts != AV_NOPTS_VALUE) + st->info->last_dts = ts; + + return 0; +} + +void ff_rfps_calculate(AVFormatContext *ic) +{ + int i, j; + + for (i = 0; i < ic->nb_streams; i++) { + AVStream *st = ic->streams[i]; + + if (st->codec->codec_type != AVMEDIA_TYPE_VIDEO) + continue; + // 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; + AVRational ref_rate = st->r_frame_rate.num ? st->r_frame_rate : av_inv_q(st->time_base); + + 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; + + if (av_q2d(st->time_base) * st->info->rfps_duration_sum / st->info->duration_count < (1001*12.0 * 0.8)/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 && (!ref_rate.num || (double)num/(12*1001) < 1.01 * av_q2d(ref_rate))) + av_reduce(&st->r_frame_rate.num, &st->r_frame_rate.den, num, 12*1001, INT_MAX); + } + if ( !st->avg_frame_rate.num + && st->r_frame_rate.num && st->info->rfps_duration_sum + && st->info->codec_info_duration <= 0 + && st->info->duration_count > 2 + && fabs(1.0 / (av_q2d(st->r_frame_rate) * av_q2d(st->time_base)) - st->info->rfps_duration_sum / (double)st->info->duration_count) <= 1.0 + ) { + av_log(ic, AV_LOG_DEBUG, "Setting avg frame rate based on r frame rate\n"); + st->avg_frame_rate = st->r_frame_rate; + } + + av_freep(&st->info->duration_error); + st->info->last_dts = AV_NOPTS_VALUE; + st->info->duration_count = 0; + st->info->rfps_duration_sum = 0; + } } 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); // new streams might appear, no options for those int orig_nb_streams = ic->nb_streams; + int flush_codecs; + int64_t max_analyze_duration = ic->max_analyze_duration2; + int64_t probesize = ic->probesize2; + + if (!max_analyze_duration) + max_analyze_duration = ic->max_analyze_duration; + if (ic->probesize) + probesize = ic->probesize; + flush_codecs = probesize > 0; + + av_opt_set(ic, "skip_clear", "1", AV_OPT_SEARCH_CHILDREN); + + if (!max_analyze_duration) { + if (!strcmp(ic->iformat->name, "flv") && !(ic->ctx_flags & AVFMTCTX_NOHEADER)) { + max_analyze_duration = 10*AV_TIME_BASE; + } else + max_analyze_duration = 5*AV_TIME_BASE; + } + + if (ic->pb) + av_log(ic, AV_LOG_DEBUG, "Before avformat_find_stream_info() pos: %"PRId64" bytes read:%"PRId64" seeks:%d\n", + avio_tell(ic->pb), ic->pb->bytes_read, ic->pb->seek_count); 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 H.264 decoder will not extract * SPS and PPS to extradata during multi-threaded decoding. */ @@ -2057,21 +3001,27 @@ int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options) /* Ensure that subtitle_header is properly set. */ if (st->codec->codec_type == AVMEDIA_TYPE_SUBTITLE - && codec && !st->codec->codec) - avcodec_open2(st->codec, codec, - options ? &options[i] : &thread_opt); + && codec && !st->codec->codec) { + if (avcodec_open2(st->codec, codec, options ? &options[i] : &thread_opt) < 0) + av_log(ic, AV_LOG_WARNING, + "Failed to open codec in av_find_stream_info\n"); + } // 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); + if (avcodec_open2(st->codec, codec, options ? &options[i] : &thread_opt) < 0) + av_log(ic, AV_LOG_WARNING, + "Failed to open codec in av_find_stream_info\n"); } if (!options) av_dict_free(&thread_opt); } 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; } @@ -2090,24 +3040,29 @@ 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 * the correct fps. */ if (av_q2d(st->time_base) > 0.0005) fps_analyze_framecount *= 2; + if (!tb_unreliable(st->codec)) + fps_analyze_framecount = 0; 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 (!st->avg_frame_rate.num && - st->codec_info_nb_frames < fps_analyze_framecount && + if (!(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) break; if (st->first_dts == AV_NOPTS_VALUE && + !(ic->iformat->flags & AVFMT_NOTIMESTAMPS) && st->codec_info_nb_frames < ic->max_ts_probe && (st->codec->codec_type == AVMEDIA_TYPE_VIDEO || st->codec->codec_type == AVMEDIA_TYPE_AUDIO)) @@ -2120,14 +3075,23 @@ 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) { + if (read_size >= probesize) { ret = count; av_log(ic, AV_LOG_DEBUG, - "Probe buffer size limit %d reached\n", ic->probesize); + "Probe buffer size limit of %"PRId64" bytes reached\n", probesize); + for (i = 0; i < ic->nb_streams; i++) + if (!ic->streams[i]->r_frame_rate.num && + ic->streams[i]->info->duration_count <= 1 && + ic->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO && + 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; } @@ -2139,56 +3103,31 @@ 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); - - /* We could not have all the codec parameters before EOF. */ - ret = -1; - 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; } - read_size += pkt->size; - st = ic->streams[pkt->stream_index]; + if (!(st->disposition & AV_DISPOSITION_ATTACHED_PIC)) + read_size += pkt->size; + if (pkt->dts != AV_NOPTS_VALUE && st->codec_info_nb_frames > 1) { /* 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, + 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, @@ -2222,22 +3161,40 @@ 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 %d reached\n", - ic->max_analyze_duration); + } + 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 >= max_analyze_duration) { + av_log(ic, AV_LOG_VERBOSE, "max_analyze_duration %"PRId64" reached at %"PRId64" microseconds\n", + 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 + if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) + ff_rfps_add_frame(ic, st, 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_mallocz(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); @@ -2253,38 +3210,70 @@ 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, + 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); + } + } + } + } + av_opt_set(ic, "skip_clear", "0", AV_OPT_SEARCH_CHILDREN); + // close codecs which were opened in try_decode_frame() for (i = 0; i < ic->nb_streams; i++) { st = ic->streams[i]; avcodec_close(st->codec); } + + ff_rfps_calculate(ic); + 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(avpriv_get_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. */ @@ -2302,6 +3291,17 @@ int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options) av_reduce(&st->avg_frame_rate.num, &st->avg_frame_rate.den, best_fps, 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 = @@ -2327,26 +3327,58 @@ int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options) } } + if (probesize) estimate_timings(ic, old_offset); + if (ret >= 0 && ic->nb_streams) + /* We could not have all the codec parameters before EOF. */ + ret = -1; + 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++) { - ic->streams[i]->codec->thread_count = 0; + st = ic->streams[i]; + if (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, "After avformat_find_stream_info() pos: %"PRId64" bytes read:%"PRId64" seeks:%d frames:%d\n", + avio_tell(ic->pb), ic->pb->bytes_read, ic->pb->seek_count, count); 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; } @@ -2355,12 +3387,12 @@ int av_find_best_stream(AVFormatContext *ic, enum AVMediaType type, AVCodec **decoder_ret, 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; + const 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; @@ -2374,20 +3406,32 @@ int av_find_best_stream(AVFormatContext *ic, enum AVMediaType type, continue; if (wanted_stream_nb >= 0 && real_stream_index != wanted_stream_nb) continue; - if (st->disposition & (AV_DISPOSITION_HEARING_IMPAIRED | + if (wanted_stream_nb != real_stream_index && + st->disposition & (AV_DISPOSITION_HEARING_IMPAIRED | AV_DISPOSITION_VISUAL_IMPAIRED)) continue; + if (type == AVMEDIA_TYPE_AUDIO && !avctx->channels) + 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; + if (!bitrate) + bitrate = avctx->rc_max_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) { @@ -2398,7 +3442,7 @@ int av_find_best_stream(AVFormatContext *ic, enum AVMediaType type, } } if (decoder_ret) - *decoder_ret = best_decoder; + *decoder_ret = (AVCodec*)best_decoder; return ret; } @@ -2422,38 +3466,49 @@ int av_read_pause(AVFormatContext *s) return AVERROR(ENOSYS); } +void ff_free_stream(AVFormatContext *s, AVStream *st) { + int j; + av_assert0(s->nb_streams>0); + av_assert0(s->streams[ s->nb_streams - 1 ] == st); + + for (j = 0; j < st->nb_side_data; j++) + av_freep(&st->side_data[j].data); + av_freep(&st->side_data); + st->nb_side_data = 0; + + 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, j; - AVStream *st; + int i; + + if (!s) + return; av_opt_free(s); if (s->iformat && s->iformat->priv_class && s->priv_data) av_opt_free(s->priv_data); + if (s->oformat && s->oformat->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]; - - for (j = 0; j < st->nb_side_data; j++) - av_freep(&st->side_data[j].data); - av_freep(&st->side_data); - st->nb_side_data = 0; - - 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); @@ -2464,19 +3519,33 @@ 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); av_freep(&s->streams); av_freep(&s->internal); + flush_packet_queue(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)) @@ -2495,16 +3564,28 @@ void avformat_close_input(AVFormatContext **ps) avio_close(pb); } +#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) @@ -2513,6 +3594,7 @@ AVStream *avformat_new_stream(AVFormatContext *s, const AVCodec *c) av_free(st); return NULL; } + st->info->last_dts = AV_NOPTS_VALUE; st->codec = avcodec_alloc_context3(c); if (s->iformat) { @@ -2530,19 +3612,27 @@ AVStream *avformat_new_stream(AVFormatContext *s, const 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; st->last_IP_pts = AV_NOPTS_VALUE; + st->last_dts_for_order_check = AV_NOPTS_VALUE; for (i = 0; i < MAX_REORDER_DELAY + 1; i++) st->pts_buffer[i] = AV_NOPTS_VALUE; 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; + st->inject_global_side_data = s->internal->inject_global_side_data; + s->streams[s->nb_streams++] = st; return st; } @@ -2566,6 +3656,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; } @@ -2576,6 +3671,11 @@ AVChapter *avpriv_new_chapter(AVFormatContext *s, int id, AVRational time_base, AVChapter *chapter = NULL; int i; + if (end != AV_NOPTS_VALUE && start > end) { + av_log(s, AV_LOG_ERROR, "Chapter end time %"PRId64" before start %"PRId64"\n", end, start); + return NULL; + } + for (i = 0; i < s->nb_chapters; i++) if (s->chapters[i]->id == id) chapter = s->chapters[i]; @@ -2599,6 +3699,7 @@ void ff_program_add_stream_index(AVFormatContext *ac, int progid, unsigned idx) { 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); @@ -2613,12 +3714,10 @@ void ff_program_add_stream_index(AVFormatContext *ac, int progid, unsigned idx) 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; } @@ -2687,7 +3786,7 @@ void av_url_split(char *proto, int proto_size, char *hostname, int hostname_size, int *port_ptr, 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; @@ -2716,8 +3815,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 @@ -2726,9 +3828,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 '@' */ } @@ -2798,6 +3901,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) { @@ -2813,11 +3924,13 @@ void avpriv_set_pts_info(AVStream *s, int pts_wrap_bits, 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", + "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; } @@ -2886,21 +3999,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(const AVOutputFormat *ofmt, enum AVCodecID codec_id, @@ -2982,6 +4088,137 @@ 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; + AVRational codec_fr = av_inv_q(st->codec->time_base); + AVRational avg_fr = st->avg_frame_rate; + + if (avg_fr.num > 0 && avg_fr.den > 0 && fr.num > 0 && fr.den > 0 && + av_q2d(avg_fr) < 70 && av_q2d(fr) > 210) { + fr = avg_fr; + } + + + if (st->codec->ticks_per_frame > 1) { + 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 == '#' || + (*spec == 'i' && *(spec + 1) == ':')) { + int stream_id; + char *endptr; + spec += 1 + (*spec == 'i'); + stream_id = strtol(spec, &endptr, 0); + if (!*endptr) + return stream_id == st->id; + } else if (*spec == 'm' && *(spec + 1) == ':') { + AVDictionaryEntry *tag; + char *key, *val; + int ret; + + spec += 2; + val = strchr(spec, ':'); + + key = val ? av_strndup(spec, val - spec) : av_strdup(spec); + if (!key) + return AVERROR(ENOMEM); + + tag = av_dict_get(st->metadata, key, NULL, 0); + if (tag) { + if (!val || !strcmp(tag->value, val + 1)) + ret = 1; + else + ret = 0; + } else + ret = 0; + + av_freep(&key); + return ret; + } else if (!*spec) /* empty specifier, matches everything */ + return 1; + + av_log(s, AV_LOG_ERROR, "Invalid stream specifier: %s.\n", spec); + return AVERROR(EINVAL); +} + int ff_generate_avci_extradata(AVStream *st) { static const uint8_t avci100_1080p_extradata[] = { @@ -3026,8 +4263,8 @@ int ff_generate_avci_extradata(AVStream *st) 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, 0x01, 0x80, 0x02, - 0x71, 0x80, 0x80, 0x80, 0xa0, 0x00, 0x00, 0x03, + 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, @@ -3073,13 +4310,9 @@ int ff_generate_avci_extradata(AVStream *st) return 0; av_freep(&st->codec->extradata); - st->codec->extradata_size = 0; - 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); - memcpy(st->codec->extradata, data, size); - st->codec->extradata_size = size; return 0; } diff --git a/libavformat/vc1test.c b/libavformat/vc1test.c index f302c2b..3afe398 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,11 +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); - if (!st->codec->extradata) + if (ff_get_extradata(st->codec, pb, VC1_EXTRADATA_SIZE) < 0) return AVERROR(ENOMEM); - st->codec->extradata_size = VC1_EXTRADATA_SIZE; - avio_read(pb, st->codec->extradata, VC1_EXTRADATA_SIZE); st->codec->height = avio_rl32(pb); st->codec->width = avio_rl32(pb); if(avio_rl32(pb) != 0xC) @@ -94,7 +91,7 @@ static int vc1t_read_packet(AVFormatContext *s, int keyframe = 0; uint32_t pts; - if(pb->eof_reached) + if(avio_feof(pb)) return AVERROR(EIO); frame_size = avio_rl24(pb); diff --git a/libavformat/vc1testenc.c b/libavformat/vc1testenc.c index 9d55fee..751333a 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 d4dee72..b802bbb 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 */ @@ -31,7 +31,7 @@ #define LIBAVFORMAT_VERSION_MAJOR 56 #define LIBAVFORMAT_VERSION_MINOR 3 -#define LIBAVFORMAT_VERSION_MICRO 0 +#define LIBAVFORMAT_VERSION_MICRO 100 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \ LIBAVFORMAT_VERSION_MINOR, \ @@ -57,5 +57,32 @@ #ifndef FF_API_LAVF_CODEC_TB #define FF_API_LAVF_CODEC_TB (LIBAVFORMAT_VERSION_MAJOR < 57) #endif +#ifndef FF_API_URL_FEOF +#define FF_API_URL_FEOF (LIBAVFORMAT_VERSION_MAJOR < 57) +#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 0000000..7287379 --- /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 (avio_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 (avio_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 (avio_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 1b7d499..2a97234 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 d2c0cb4..08db970 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 2fb8440..0b6b5a5 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 ed10a96..0bb532e 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 ee06a57..cc9b662 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 d9ec099..f4212d2 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 0000000..619ccfd --- /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 (!avio_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 a43829b..15e8246 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 */ @@ -43,6 +43,9 @@ static int vqf_probe(AVProbeData *probe_packet) if (!memcmp(probe_packet->buf + 4, "00052200", 8)) return AVPROBE_SCORE_MAX; + if (AV_RL32(probe_packet->buf + 12) > (1<<27)) + return AVPROBE_SCORE_EXTENSION/2; + return AVPROBE_SCORE_EXTENSION; } @@ -132,15 +135,16 @@ 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 { - char buf[8] = {0}; - int size = avio_rb32(s->pb); - - snprintf(buf, sizeof(buf), "%d", size); - av_dict_set(&s->metadata, "size", buf, 0); + av_dict_set_int(&s->metadata, "size", avio_rb32(s->pb), 0); } break; case MKTAG('Y','E','A','R'): // recording date @@ -158,7 +162,7 @@ static int vqf_read_header(AVFormatContext *s) header_size -= len; - } while (header_size >= 0); + } while (header_size >= 0 && !avio_feof(s->pb)); switch (rate_flag) { case -1: @@ -215,9 +219,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); @@ -242,7 +245,7 @@ static int vqf_read_packet(AVFormatContext *s, AVPacket *pkt) pkt->data[1] = c->last_frame_bits; ret = avio_read(s->pb, pkt->data+2, size); - if (ret<=0) { + if (ret != size) { av_free_packet(pkt); return AVERROR(EIO); } diff --git a/libavformat/w64.c b/libavformat/w64.c new file mode 100644 index 0000000..ef2d90a --- /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 0000000..0ec3fa9 --- /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 f65a66a..9c4e2df 100644 --- a/libavformat/wavdec.c +++ b/libavformat/wavdec.c @@ -6,20 +6,20 @@ * 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 */ @@ -27,6 +27,7 @@ #include "libavutil/avassert.h" #include "libavutil/dict.h" +#include "libavutil/intreadwrite.h" #include "libavutil/log.h" #include "libavutil/mathematics.h" #include "libavutil/opt.h" @@ -37,10 +38,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 @@ -51,25 +67,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 (avio_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; } @@ -92,6 +110,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, 14); + } +} + static int wav_parse_fmt_tag(AVFormatContext *s, int64_t size, AVStream **st) { AVIOContext *pb = s->pb; @@ -105,7 +131,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); @@ -167,7 +195,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], @@ -222,6 +250,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); @@ -237,7 +269,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 */ @@ -250,20 +282,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 (avio_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"); @@ -300,22 +334,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; } } @@ -328,11 +402,21 @@ 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 ( data_size > 0 && sample_count && st->codec->channels + && data_size / sample_count / st->codec->channels > 8) { + av_log(s, AV_LOG_WARNING, "ignoring wrong sample_count %"PRId64"\n", sample_count); + sample_count = 0; + } + + 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; @@ -351,23 +435,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 (!avio_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) @@ -377,16 +456,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; } @@ -408,7 +555,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) { @@ -424,6 +585,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)"), @@ -434,31 +608,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; @@ -466,7 +626,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; @@ -474,7 +634,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 */ @@ -482,38 +642,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 (!avio_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; - st->need_parsing = AVSTREAM_PARSE_FULL; + 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); - avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate); + avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate); + } else if (!memcmp(guid, ff_w64_guid_fact, 16)) { + int64_t samples; - size = find_guid(pb, guid_data); - if (size < 0) { - av_log(s, AV_LOG_ERROR, "could not find data guid\n"); - return AVERROR_INVALIDDATA; + 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; + + 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 (avio_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 25f6ffc..c73c900 100644 --- a/libavformat/wavenc.c +++ b/libavformat/wavenc.c @@ -2,30 +2,42 @@ * 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> + * + * EBU Tech 3285 - Supplement 3 - Peak Envelope Chunk encoder + * Copyright (c) 2014 Georg Lippitsch <georg.lippitsch@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. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free 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/common.h" +#include "libavutil/intreadwrite.h" #include "libavutil/mathematics.h" #include "libavutil/opt.h" +#include "libavutil/time.h" #include "avformat.h" #include "avio.h" @@ -33,16 +45,49 @@ #include "internal.h" #include "riff.h" +#define RF64_AUTO (-1) +#define RF64_NEVER 0 +#define RF64_ALWAYS 1 + +#define PEAK_BUFFER_SIZE 1024 + +typedef enum { + PEAK_OFF = 0, + PEAK_ON, + PEAK_ONLY +} PeakType; + +typedef enum { + PEAK_FORMAT_UINT8 = 1, + PEAK_FORMAT_UINT16 +} PeakFormat; + typedef struct WAVMuxContext { const AVClass *class; int64_t data; int64_t fact_pos; + int64_t ds64; int64_t minpts; int64_t maxpts; + int16_t *peak_maxpos, *peak_maxneg; + uint32_t peak_num_frames; + uint32_t peak_outbuf_size; + uint32_t peak_outbuf_bytes; + uint32_t peak_pos_pop; + uint16_t peak_pop; + uint8_t *peak_output; int last_duration; int write_bext; + int write_peak; + int rf64; + int peak_block_size; + PeakFormat peak_format; + int peak_block_pos; + int peak_ppv; + int peak_bps; } WAVMuxContext; +#if CONFIG_WAV_MUXER static inline void bwf_write_bext_string(AVFormatContext *s, const char *key, int maxlen) { AVDictionaryEntry *tag; @@ -97,25 +142,197 @@ static void bwf_write_bext_chunk(AVFormatContext *s) ff_end_tag(s->pb, bext); } +static av_cold void peak_free_buffers(AVFormatContext *s) +{ + WAVMuxContext *wav = s->priv_data; + + av_freep(&wav->peak_maxpos); + av_freep(&wav->peak_maxneg); + av_freep(&wav->peak_output); +} + +static av_cold int peak_init_writer(AVFormatContext *s) +{ + WAVMuxContext *wav = s->priv_data; + AVCodecContext *enc = s->streams[0]->codec; + + if (enc->codec_id != AV_CODEC_ID_PCM_S8 && + enc->codec_id != AV_CODEC_ID_PCM_S16LE && + enc->codec_id != AV_CODEC_ID_PCM_U8 && + enc->codec_id != AV_CODEC_ID_PCM_U16LE) { + av_log(s, AV_LOG_ERROR, "%s codec not supported for Peak Chunk\n", + s->streams[0]->codec->codec ? s->streams[0]->codec->codec->name : "NONE"); + return -1; + } + + wav->peak_bps = av_get_bits_per_sample(enc->codec_id) / 8; + + if (wav->peak_bps == 1 && wav->peak_format == PEAK_FORMAT_UINT16) { + av_log(s, AV_LOG_ERROR, + "Writing 16 bit peak for 8 bit audio does not make sense\n"); + return AVERROR(EINVAL); + } + + wav->peak_maxpos = av_mallocz_array(enc->channels, sizeof(*wav->peak_maxpos)); + wav->peak_maxneg = av_mallocz_array(enc->channels, sizeof(*wav->peak_maxneg)); + wav->peak_output = av_malloc(PEAK_BUFFER_SIZE); + if (!wav->peak_maxpos || !wav->peak_maxneg || !wav->peak_output) + goto nomem; + + wav->peak_outbuf_size = PEAK_BUFFER_SIZE; + + return 0; + +nomem: + av_log(s, AV_LOG_ERROR, "Out of memory\n"); + peak_free_buffers(s); + return AVERROR(ENOMEM); +} + +static void peak_write_frame(AVFormatContext *s) +{ + WAVMuxContext *wav = s->priv_data; + AVCodecContext *enc = s->streams[0]->codec; + int peak_of_peaks; + int c; + + if (!wav->peak_output) + return; + + for (c = 0; c < enc->channels; c++) { + wav->peak_maxneg[c] = -wav->peak_maxneg[c]; + + if (wav->peak_bps == 2 && wav->peak_format == PEAK_FORMAT_UINT8) { + wav->peak_maxpos[c] = wav->peak_maxpos[c] / 256; + wav->peak_maxneg[c] = wav->peak_maxneg[c] / 256; + } + + if (wav->peak_ppv == 1) + wav->peak_maxpos[c] = + FFMAX(wav->peak_maxpos[c], wav->peak_maxneg[c]); + + peak_of_peaks = FFMAX3(wav->peak_maxpos[c], wav->peak_maxneg[c], + wav->peak_pop); + if (peak_of_peaks > wav->peak_pop) + wav->peak_pos_pop = wav->peak_num_frames; + wav->peak_pop = peak_of_peaks; + + if (wav->peak_outbuf_size - wav->peak_outbuf_bytes < + wav->peak_format * wav->peak_ppv) { + wav->peak_outbuf_size += PEAK_BUFFER_SIZE; + wav->peak_output = av_realloc(wav->peak_output, + wav->peak_outbuf_size); + if (!wav->peak_output) { + av_log(s, AV_LOG_ERROR, "No memory for peak data\n"); + return; + } + } + + if (wav->peak_format == PEAK_FORMAT_UINT8) { + wav->peak_output[wav->peak_outbuf_bytes++] = + wav->peak_maxpos[c]; + if (wav->peak_ppv == 2) { + wav->peak_output[wav->peak_outbuf_bytes++] = + wav->peak_maxneg[c]; + } + } else { + AV_WL16(wav->peak_output + wav->peak_outbuf_bytes, + wav->peak_maxpos[c]); + wav->peak_outbuf_bytes += 2; + if (wav->peak_ppv == 2) { + AV_WL16(wav->peak_output + wav->peak_outbuf_bytes, + wav->peak_maxneg[c]); + wav->peak_outbuf_bytes += 2; + } + } + wav->peak_maxpos[c] = 0; + wav->peak_maxneg[c] = 0; + } + wav->peak_num_frames++; +} + +static void peak_write_chunk(AVFormatContext *s) +{ + WAVMuxContext *wav = s->priv_data; + AVIOContext *pb = s->pb; + AVCodecContext *enc = s->streams[0]->codec; + int64_t peak = ff_start_tag(s->pb, "levl"); + int64_t now0; + time_t now_secs; + char timestamp[28]; + + /* Peak frame of incomplete block at end */ + if (wav->peak_block_pos) + peak_write_frame(s); + + memset(timestamp, 0, sizeof(timestamp)); + if (!(s->flags & AVFMT_FLAG_BITEXACT)) { + av_log(s, AV_LOG_INFO, "Writing local time and date to Peak Envelope Chunk\n"); + now0 = av_gettime(); + now_secs = now0 / 1000000; + strftime(timestamp, sizeof(timestamp), "%Y:%m:%d:%H:%M:%S:", localtime(&now_secs)); + av_strlcatf(timestamp, sizeof(timestamp), "%03d", (int)((now0 / 1000) % 1000)); + } + + avio_wl32(pb, 1); /* version */ + avio_wl32(pb, wav->peak_format); /* 8 or 16 bit */ + avio_wl32(pb, wav->peak_ppv); /* positive and negative */ + avio_wl32(pb, wav->peak_block_size); /* frames per value */ + avio_wl32(pb, enc->channels); /* number of channels */ + avio_wl32(pb, wav->peak_num_frames); /* number of peak frames */ + avio_wl32(pb, wav->peak_pos_pop); /* audio sample frame index */ + avio_wl32(pb, 128); /* equal to size of header */ + avio_write(pb, timestamp, 28); /* ASCII time stamp */ + ffio_fill(pb, 0, 60); + + avio_write(pb, wav->peak_output, wav->peak_outbuf_bytes); + + ff_end_tag(pb, peak); + + if (!wav->data) + wav->data = peak; +} + static int wav_write_header(AVFormatContext *s) { WAVMuxContext *wav = s->priv_data; AVIOContext *pb = s->pb; int64_t fmt; - ffio_wfourcc(pb, "RIFF"); - avio_wl32(pb, 0); /* file length */ + if (s->nb_streams != 1) { + av_log(s, AV_LOG_ERROR, "WAVE files have exactly one stream\n"); + return AVERROR(EINVAL); + } + + 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, -1); /* file length */ + } + ffio_wfourcc(pb, "WAVE"); - /* format header */ - fmt = ff_start_tag(pb, "fmt "); - if (ff_put_wav_header(pb, s->streams[0]->codec) < 0) { - const AVCodecDescriptor *desc = avcodec_descriptor_get(s->streams[0]->codec->codec_id); - av_log(s, AV_LOG_ERROR, "%s codec not supported in WAVE format\n", - desc ? desc->name : "unknown"); - return AVERROR(ENOSYS); + 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); + } + + if (wav->write_peak != 2) { + /* format header */ + fmt = ff_start_tag(pb, "fmt "); + if (ff_put_wav_header(pb, s->streams[0]->codec, 0) < 0) { + const AVCodecDescriptor *desc = avcodec_descriptor_get(s->streams[0]->codec->codec_id); + av_log(s, AV_LOG_ERROR, "%s codec not supported in WAVE format\n", + desc ? desc->name : "unknown"); + return AVERROR(ENOSYS); + } + ff_end_tag(pb, fmt); } - ff_end_tag(pb, fmt); if (s->streams[0]->codec->codec_tag != 0x01 /* hence for all other than PCM */ && s->pb->seekable) { @@ -127,15 +344,23 @@ static int wav_write_header(AVFormatContext *s) if (wav->write_bext) bwf_write_bext_chunk(s); + if (wav->write_peak) { + int ret; + if ((ret = peak_init_writer(s)) < 0) + return ret; + } + avpriv_set_pts_info(s->streams[0], 64, 1, s->streams[0]->codec->sample_rate); wav->maxpts = wav->last_duration = 0; wav->minpts = INT64_MAX; - /* info header */ - ff_riff_write_info(s); + if (wav->write_peak != 2) { + /* info header */ + ff_riff_write_info(s); - /* data header */ - wav->data = ff_start_tag(pb, "data"); + /* data header */ + wav->data = ff_start_tag(pb, "data"); + } avio_flush(pb); @@ -146,7 +371,31 @@ static int wav_write_packet(AVFormatContext *s, AVPacket *pkt) { AVIOContext *pb = s->pb; WAVMuxContext *wav = s->priv_data; - avio_write(pb, pkt->data, pkt->size); + + if (wav->write_peak != 2) + avio_write(pb, pkt->data, pkt->size); + + if (wav->write_peak) { + int c = 0; + int i; + for (i = 0; i < pkt->size; i += wav->peak_bps) { + if (wav->peak_bps == 1) { + wav->peak_maxpos[c] = FFMAX(wav->peak_maxpos[c], *(int8_t*)(pkt->data + i)); + wav->peak_maxneg[c] = FFMIN(wav->peak_maxneg[c], *(int8_t*)(pkt->data + i)); + } else { + wav->peak_maxpos[c] = FFMAX(wav->peak_maxpos[c], (int16_t)AV_RL16(pkt->data + i)); + wav->peak_maxneg[c] = FFMIN(wav->peak_maxneg[c], (int16_t)AV_RL16(pkt->data + i)); + } + if (++c == s->streams[0]->codec->channels) { + c = 0; + if (++wav->peak_block_pos == wav->peak_block_size) { + peak_write_frame(s); + wav->peak_block_pos = 0; + } + } + } + } + if(pkt->pts != AV_NOPTS_VALUE) { wav->minpts = FFMIN(wav->minpts, pkt->pts); wav->maxpts = FFMAX(wav->maxpts, pkt->pts); @@ -160,33 +409,80 @@ 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); + if (wav->write_peak != 2) { + ff_end_tag(pb, wav->data); + avio_flush(pb); + } + + if (wav->write_peak && wav->peak_output) { + peak_write_chunk(s); + avio_flush(pb); + } /* 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); + 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); } } + + if (wav->write_peak) + peak_free_buffers(s); + return 0; } @@ -194,6 +490,17 @@ 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 }, + { "write_peak", "Write Peak Envelope chunk.", OFFSET(write_peak), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 2, ENC, "peak" }, + { "off", "Do not write peak chunk.", 0, AV_OPT_TYPE_CONST, { .i64 = PEAK_OFF }, 0, 0, ENC, "peak" }, + { "on", "Append peak chunk after wav data.", 0, AV_OPT_TYPE_CONST, { .i64 = PEAK_ON }, 0, 0, ENC, "peak" }, + { "only", "Write only peak chunk, omit wav data.", 0, AV_OPT_TYPE_CONST, { .i64 = PEAK_ONLY }, 0, 0, ENC, "peak" }, + { "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" }, + { "peak_block_size", "Number of audio samples used to generate each peak frame.", OFFSET(peak_block_size), AV_OPT_TYPE_INT, { .i64 = 256 }, 0, 65536, ENC }, + { "peak_format", "The format of the peak envelope data (1: uint8, 2: uint16).", OFFSET(peak_format), AV_OPT_TYPE_INT, { .i64 = PEAK_FORMAT_UINT16 }, PEAK_FORMAT_UINT8, PEAK_FORMAT_UINT16, ENC }, + { "peak_ppv", "Number of peak points per peak value (1 or 2).", OFFSET(peak_ppv), AV_OPT_TYPE_INT, { .i64 = 2 }, 1, 2, ENC }, { NULL }, }; @@ -219,3 +526,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)) < 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 c894253..7bd09e3 100644 --- a/libavformat/wc3movie.c +++ b/libavformat/wc3movie.c @@ -1,21 +1,21 @@ /* * Wing Commander III Movie (.mve) File Demuxer - * Copyright (c) 2003 The ffmpeg 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 */ @@ -27,6 +27,7 @@ * http://www.pcisys.net/~melanson/codecs/ */ +#include "libavutil/avstring.h" #include "libavutil/channel_layout.h" #include "libavutil/intreadwrite.h" #include "libavutil/dict.h" @@ -158,7 +159,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 (avio_feof(pb)) return AVERROR(EIO); } while (fourcc_tag != BRCH_TAG); @@ -210,7 +211,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 (avio_feof(pb)) return AVERROR(EIO); switch (fourcc_tag) { @@ -249,10 +250,16 @@ static int wc3_read_packet(AVFormatContext *s, else { int i = 0; av_log (s, AV_LOG_DEBUG, "Subtitle time!\n"); + if (i >= size || av_strnlen(&text[i + 1], size - i - 1) >= size - i - 1) + return AVERROR_INVALIDDATA; av_log (s, AV_LOG_DEBUG, " inglish: %s\n", &text[i + 1]); i += text[i] + 1; + if (i >= size || av_strnlen(&text[i + 1], size - i - 1) >= size - i - 1) + return AVERROR_INVALIDDATA; av_log (s, AV_LOG_DEBUG, " doytsch: %s\n", &text[i + 1]); i += text[i] + 1; + if (i >= size || av_strnlen(&text[i + 1], size - i - 1) >= size - i - 1) + return AVERROR_INVALIDDATA; av_log (s, AV_LOG_DEBUG, " fronsay: %s\n", &text[i + 1]); } #endif diff --git a/libavformat/webmdashenc.c b/libavformat/webmdashenc.c new file mode 100644 index 0000000..77f6170 --- /dev/null +++ b/libavformat/webmdashenc.c @@ -0,0 +1,317 @@ +/* + * WebM DASH Manifest XML muxer + * Copyright (c) 2014 Vignesh Venkatasubramanian + * + * 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 + */ + +/* + * WebM DASH Specification: + * https://sites.google.com/a/webmproject.org/wiki/adaptive-streaming/webm-dash-specification + */ + +#include <stdint.h> +#include <string.h> + +#include "avformat.h" +#include "avio_internal.h" +#include "matroska.h" + +#include "libavutil/avstring.h" +#include "libavutil/dict.h" +#include "libavutil/opt.h" + +typedef struct AdaptationSet { + char id[10]; + int *streams; + int nb_streams; +} AdaptationSet; + +typedef struct WebMDashMuxContext { + const AVClass *class; + char *adaptation_sets; + AdaptationSet *as; + int nb_as; +} WebMDashMuxContext; + +static const char *get_codec_name(int codec_id) +{ + switch (codec_id) { + case AV_CODEC_ID_VP8: + return "vp8"; + case AV_CODEC_ID_VP9: + return "vp9"; + case AV_CODEC_ID_VORBIS: + return "vorbis"; + case AV_CODEC_ID_OPUS: + return "opus"; + } + return NULL; +} + +static double get_duration(AVFormatContext *s) +{ + int i = 0; + double max = 0.0; + for (i = 0; i < s->nb_streams; i++) { + AVDictionaryEntry *duration = av_dict_get(s->streams[i]->metadata, + DURATION, NULL, 0); + if (!duration || atof(duration->value) < 0) continue; + if (atof(duration->value) > max) max = atof(duration->value); + } + return max / 1000; +} + +static void write_header(AVFormatContext *s) +{ + double min_buffer_time = 1.0; + avio_printf(s->pb, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); + avio_printf(s->pb, "<MPD\n"); + avio_printf(s->pb, " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"); + avio_printf(s->pb, " xmlns=\"urn:mpeg:DASH:schema:MPD:2011\"\n"); + avio_printf(s->pb, " xsi:schemaLocation=\"urn:mpeg:DASH:schema:MPD:2011\"\n"); + avio_printf(s->pb, " type=\"static\"\n"); + avio_printf(s->pb, " mediaPresentationDuration=\"PT%gS\"\n", + get_duration(s)); + avio_printf(s->pb, " minBufferTime=\"PT%gS\"\n", + min_buffer_time); + avio_printf(s->pb, " profiles=\"urn:webm:dash:profile:webm-on-demand:2012\""); + avio_printf(s->pb, ">\n"); +} + +static void write_footer(AVFormatContext *s) +{ + avio_printf(s->pb, "</MPD>"); +} + +static int subsegment_alignment(AVFormatContext *s, AdaptationSet *as) { + int i; + AVDictionaryEntry *gold = av_dict_get(s->streams[as->streams[0]]->metadata, + CUE_TIMESTAMPS, NULL, 0); + if (!gold) return 0; + for (i = 1; i < as->nb_streams; i++) { + AVDictionaryEntry *ts = av_dict_get(s->streams[as->streams[i]]->metadata, + CUE_TIMESTAMPS, NULL, 0); + if (!ts || strncmp(gold->value, ts->value, strlen(gold->value))) return 0; + } + return 1; +} + +static int bitstream_switching(AVFormatContext *s, AdaptationSet *as) { + int i; + AVDictionaryEntry *gold_track_num = av_dict_get(s->streams[as->streams[0]]->metadata, + TRACK_NUMBER, NULL, 0); + AVCodecContext *gold_codec = s->streams[as->streams[0]]->codec; + if (!gold_track_num) return 0; + for (i = 1; i < as->nb_streams; i++) { + AVDictionaryEntry *track_num = av_dict_get(s->streams[as->streams[i]]->metadata, + TRACK_NUMBER, NULL, 0); + AVCodecContext *codec = s->streams[as->streams[i]]->codec; + if (!track_num || + strncmp(gold_track_num->value, track_num->value, strlen(gold_track_num->value)) || + gold_codec->codec_id != codec->codec_id || + gold_codec->extradata_size != codec->extradata_size || + memcmp(gold_codec->extradata, codec->extradata, codec->extradata_size)) { + return 0; + } + } + return 1; +} + +/* + * Writes an Adaptation Set. Returns 0 on success and < 0 on failure. + */ +static int write_adaptation_set(AVFormatContext *s, int as_index) +{ + WebMDashMuxContext *w = s->priv_data; + AdaptationSet *as = &w->as[as_index]; + AVCodecContext *codec = s->streams[as->streams[0]]->codec; + int i; + static const char boolean[2][6] = { "false", "true" }; + int subsegmentStartsWithSAP = 1; + AVDictionaryEntry *lang; + avio_printf(s->pb, "<AdaptationSet id=\"%s\"", as->id); + avio_printf(s->pb, " mimeType=\"%s/webm\"", + codec->codec_type == AVMEDIA_TYPE_VIDEO ? "video" : "audio"); + avio_printf(s->pb, " codecs=\"%s\"", get_codec_name(codec->codec_id)); + + lang = av_dict_get(s->streams[as->streams[0]]->metadata, "language", NULL, 0); + if (lang) avio_printf(s->pb, " lang=\"%s\"", lang->value); + + if (codec->codec_type == AVMEDIA_TYPE_VIDEO) { + avio_printf(s->pb, " width=\"%d\"", codec->width); + avio_printf(s->pb, " height=\"%d\"", codec->height); + } else { + avio_printf(s->pb, " audioSamplingRate=\"%d\"", codec->sample_rate); + } + + avio_printf(s->pb, " bitstreamSwitching=\"%s\"", + boolean[bitstream_switching(s, as)]); + avio_printf(s->pb, " subsegmentAlignment=\"%s\"", + boolean[subsegment_alignment(s, as)]); + + for (i = 0; i < as->nb_streams; i++) { + AVDictionaryEntry *kf = av_dict_get(s->streams[as->streams[i]]->metadata, + CLUSTER_KEYFRAME, NULL, 0); + if (!kf || !strncmp(kf->value, "0", 1)) subsegmentStartsWithSAP = 0; + } + avio_printf(s->pb, " subsegmentStartsWithSAP=\"%d\"", subsegmentStartsWithSAP); + avio_printf(s->pb, ">\n"); + + for (i = 0; i < as->nb_streams; i++) { + AVStream *stream = s->streams[as->streams[i]]; + AVDictionaryEntry *irange = av_dict_get(stream->metadata, INITIALIZATION_RANGE, NULL, 0); + AVDictionaryEntry *cues_start = av_dict_get(stream->metadata, CUES_START, NULL, 0); + AVDictionaryEntry *cues_end = av_dict_get(stream->metadata, CUES_END, NULL, 0); + AVDictionaryEntry *filename = av_dict_get(stream->metadata, FILENAME, NULL, 0); + AVDictionaryEntry *bandwidth = av_dict_get(stream->metadata, BANDWIDTH, NULL, 0); + if (!irange || cues_start == NULL || cues_end == NULL || filename == NULL || + !bandwidth) { + return -1; + } + avio_printf(s->pb, "<Representation id=\"%d\"", i); + avio_printf(s->pb, " bandwidth=\"%s\"", bandwidth->value); + avio_printf(s->pb, ">\n"); + avio_printf(s->pb, "<BaseURL>%s</BaseURL>\n", filename->value); + avio_printf(s->pb, "<SegmentBase\n"); + avio_printf(s->pb, " indexRange=\"%s-%s\">\n", cues_start->value, cues_end->value); + avio_printf(s->pb, "<Initialization\n"); + avio_printf(s->pb, " range=\"0-%s\" />\n", irange->value); + avio_printf(s->pb, "</SegmentBase>\n"); + avio_printf(s->pb, "</Representation>\n"); + } + avio_printf(s->pb, "</AdaptationSet>\n"); + return 0; +} + +static int to_integer(char *p, int len) +{ + int ret; + char *q = av_malloc(sizeof(char) * len); + if (!q) return -1; + av_strlcpy(q, p, len); + ret = atoi(q); + av_free(q); + return ret; +} + +static int parse_adaptation_sets(AVFormatContext *s) +{ + WebMDashMuxContext *w = s->priv_data; + char *p = w->adaptation_sets; + char *q; + enum { new_set, parsed_id, parsing_streams } state; + // syntax id=0,streams=0,1,2 id=1,streams=3,4 and so on + state = new_set; + while (p < w->adaptation_sets + strlen(w->adaptation_sets)) { + if (*p == ' ') + continue; + else if (state == new_set && !strncmp(p, "id=", 3)) { + w->as = av_realloc(w->as, sizeof(*w->as) * ++w->nb_as); + if (w->as == NULL) return -1; + w->as[w->nb_as - 1].nb_streams = 0; + w->as[w->nb_as - 1].streams = NULL; + p += 3; // consume "id=" + q = w->as[w->nb_as - 1].id; + while (*p != ',') *q++ = *p++; + *q = 0; + p++; + state = parsed_id; + } else if (state == parsed_id && !strncmp(p, "streams=", 8)) { + p += 8; // consume "streams=" + state = parsing_streams; + } else if (state == parsing_streams) { + struct AdaptationSet *as = &w->as[w->nb_as - 1]; + q = p; + while (*q != '\0' && *q != ',' && *q != ' ') q++; + as->streams = av_realloc(as->streams, sizeof(*as->streams) * ++as->nb_streams); + if (as->streams == NULL) return -1; + as->streams[as->nb_streams - 1] = to_integer(p, q - p + 1); + if (as->streams[as->nb_streams - 1] < 0) return -1; + if (*q == '\0') break; + if (*q == ' ') state = new_set; + p = ++q; + } else { + return -1; + } + } + return 0; +} + +static int webm_dash_manifest_write_header(AVFormatContext *s) +{ + int i; + double start = 0.0; + WebMDashMuxContext *w = s->priv_data; + parse_adaptation_sets(s); + write_header(s); + avio_printf(s->pb, "<Period id=\"0\""); + avio_printf(s->pb, " start=\"PT%gS\"", start); + avio_printf(s->pb, " duration=\"PT%gS\"", get_duration(s)); + avio_printf(s->pb, " >\n"); + + for (i = 0; i < w->nb_as; i++) { + if (write_adaptation_set(s, i) < 0) return -1; + } + + avio_printf(s->pb, "</Period>\n"); + write_footer(s); + return 0; +} + +static int webm_dash_manifest_write_packet(AVFormatContext *s, AVPacket *pkt) +{ + return AVERROR_EOF; +} + +static int webm_dash_manifest_write_trailer(AVFormatContext *s) +{ + WebMDashMuxContext *w = s->priv_data; + int i; + for (i = 0; i < w->nb_as; i++) { + av_freep(&w->as[i].streams); + } + av_freep(&w->as); + return 0; +} + +#define OFFSET(x) offsetof(WebMDashMuxContext, x) +static const AVOption options[] = { + { "adaptation_sets", "Adaptation sets. Syntax: id=0,streams=0,1,2 id=1,streams=3,4 and so on", OFFSET(adaptation_sets), AV_OPT_TYPE_STRING, { 0 }, 0, 0, AV_OPT_FLAG_ENCODING_PARAM }, + { NULL }, +}; + +#if CONFIG_WEBM_DASH_MANIFEST_MUXER +static const AVClass webm_dash_class = { + .class_name = "WebM DASH Manifest muxer", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + +AVOutputFormat ff_webm_dash_manifest_muxer = { + .name = "webm_dash_manifest", + .long_name = NULL_IF_CONFIG_SMALL("WebM DASH Manifest"), + .mime_type = "application/xml", + .extensions = "xml", + .priv_data_size = sizeof(WebMDashMuxContext), + .write_header = webm_dash_manifest_write_header, + .write_packet = webm_dash_manifest_write_packet, + .write_trailer = webm_dash_manifest_write_trailer, + .priv_class = &webm_dash_class, +}; +#endif diff --git a/libavformat/webvttdec.c b/libavformat/webvttdec.c new file mode 100644 index 0000000..e457e8f --- /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 += 2; + 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 0000000..b93993d --- /dev/null +++ b/libavformat/webvttenc.c @@ -0,0 +1,100 @@ +/* + * 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", + .flags = AVFMT_VARIABLE_FPS | AVFMT_TS_NONSTRICT, + .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 611d223..5286875 100644 --- a/libavformat/westwood_aud.c +++ b/libavformat/westwood_aud.c @@ -1,21 +1,21 @@ /* * Westwood Studios AUD Format Demuxer - * Copyright (c) 2003 The ffmpeg 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 */ @@ -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 77da375..c827fcd 100644 --- a/libavformat/westwood_vqa.c +++ b/libavformat/westwood_vqa.c @@ -1,21 +1,21 @@ /* * Westwood Studios VQA Format Demuxer - * Copyright (c) 2003 The ffmpeg 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 */ @@ -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,13 +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 (avio_read(pb, st->codec->extradata, VQA_HEADER_SIZE) != - VQA_HEADER_SIZE) { - return AVERROR(EIO); - } + if (ff_get_extradata(st->codec, pb, VQA_HEADER_SIZE) < 0) + return AVERROR(ENOMEM); + header = (uint8_t *)st->codec->extradata; st->codec->width = AV_RL16(&header[6]); st->codec->height = AV_RL16(&header[8]); fps = header[12]; @@ -130,9 +126,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 +162,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)) - return AVERROR(EIO); - ret = avio_read(pb, pkt->data, chunk_size); - if (ret != chunk_size) { - av_free_packet(pkt); + ret= av_get_packet(pb, pkt, chunk_size); + if (ret<0) return AVERROR(EIO); - } switch (chunk_type) { case SND0_TAG: @@ -223,9 +215,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 +226,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 0000000..f26ad5e --- /dev/null +++ b/libavformat/wtv.h @@ -0,0 +1,60 @@ +/* + * 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; +extern const ff_asf_guid ff_format_videoinfo2; + +#endif /* AVFORMAT_WTV_H */ diff --git a/libavformat/wtv_common.c b/libavformat/wtv_common.c new file mode 100644 index 0000000..ce4349d --- /dev/null +++ b/libavformat/wtv_common.c @@ -0,0 +1,84 @@ +/* + * 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 ff_asf_guid ff_format_videoinfo2 = + {0xA0,0x76,0x2A,0xF7,0x0A,0xEB,0xD0,0x11,0xAC,0xE4,0x00,0x00,0xC0,0xCC,0x16,0xBA}; + +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 c85f374..4cb3295 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 */ @@ -30,11 +30,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 */ @@ -50,18 +48,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; @@ -70,7 +62,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); } /** @@ -84,7 +76,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 || avio_feof(pb)) return 0; buf_size = FFMIN(buf_size, wf->length - wf->position); @@ -163,6 +155,7 @@ static AVIOContext * wtvfile_open_sector(int first_sector, uint64_t length, int AVIOContext *pb; WtvFile *wf; uint8_t *buffer; + int64_t size; if (seek_by_sector(s->pb, first_sector, 0) < 0) return NULL; @@ -179,7 +172,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) { @@ -187,13 +179,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; @@ -204,12 +195,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); @@ -217,6 +208,10 @@ static AVIOContext * wtvfile_open_sector(int first_sector, uint64_t length, int return NULL; } + size = avio_size(s->pb); + if (size >= 0 && (int64_t)wf->sectors[wf->nb_sectors - 1] << WTV_SECTOR_BITS > size) + av_log(s, AV_LOG_WARNING, "truncated file\n"); + /* check length */ length &= 0xFFFFFFFFFFFF; if (length > ((int64_t)wf->nb_sectors << wf->sector_bits)) { @@ -251,9 +246,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 @@ -270,7 +262,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; @@ -283,7 +275,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; } @@ -312,8 +304,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); } @@ -328,10 +320,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) */ @@ -341,18 +333,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 = @@ -375,10 +355,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 = @@ -389,8 +365,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 = @@ -400,65 +374,51 @@ static const ff_asf_guid mediasubtype_dtvccdata = 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) @@ -467,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)); @@ -483,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; @@ -515,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); @@ -554,14 +533,14 @@ static void parse_legacy_attrib(AVFormatContext *s, AVIOContext *pb) { ff_asf_guid guid; int length, type; - while(!pb->eof_reached) { + while(!avio_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; @@ -583,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); + st->codec->codec_tag = ff_get_bmp_header(pb, st, NULL); return 72 + 40; } @@ -632,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; } @@ -654,12 +635,12 @@ static AVStream * new_stream(AVFormatContext *s, AVStream *st, int sid, int code */ static AVStream * parse_media_type(AVFormatContext *s, AVStream *st, int sid, ff_asf_guid mediatype, ff_asf_guid subtype, - ff_asf_guid formattype, int size) + ff_asf_guid formattype, uint64_t size) { 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; @@ -677,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); } @@ -704,18 +685,19 @@ 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)) { + if (!ff_guidcmp(formattype, ff_format_videoinfo2)) { int consumed = parse_videoinfoheader2(s, st); avio_skip(pb, FFMAX(size - consumed, 0)); - } else if (!ff_guidcmp(formattype, format_mpeg2_video)) { - int consumed = parse_videoinfoheader2(s, st); + } else if (!ff_guidcmp(formattype, ff_format_mpeg2_video)) { + uint64_t consumed = parse_videoinfoheader2(s, st); + /* ignore extradata; files produced by windows media center contain meaningless mpeg1 sequence header */ 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); } @@ -723,7 +705,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)); @@ -733,7 +715,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; @@ -743,14 +725,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; @@ -769,6 +751,26 @@ enum { }; /** + * Try to seek over a broken chunk + * @return <0 on error + */ +static int recover(WtvContext *wtv, uint64_t broken_pos) +{ + AVIOContext *pb = wtv->pb; + int i; + for (i = 0; i < wtv->nb_index_entries; i++) { + if (wtv->index_entries[i].pos > broken_pos) { + int ret = avio_seek(pb, wtv->index_entries[i].pos, SEEK_SET); + if (ret < 0) + return ret; + wtv->pts = wtv->index_entries[i].timestamp; + return 0; + } + } + return AVERROR(EIO); +} + +/** * Parse WTV chunks * @param mode SEEK_TO_DATA or SEEK_TO_PTS * @param seekts timestamp @@ -779,19 +781,24 @@ 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 (!avio_feof(pb)) { ff_asf_guid g; int len, sid, consumed; ff_get_guid(pb, &g); len = avio_rl32(pb); - if (len < 32) - break; + if (len < 32) { + int ret; + av_log(s, AV_LOG_WARNING, "encountered broken chunk\n"); + if ((ret = recover(wtv, avio_tell(pb) - 20)) < 0) + return ret; + continue; + } sid = avio_rl32(pb) & 0x7FFF; 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; @@ -804,9 +811,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); @@ -880,7 +887,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); @@ -893,15 +900,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) { @@ -909,10 +915,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}) || @@ -925,9 +936,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)); @@ -937,18 +949,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; @@ -978,7 +978,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; @@ -992,7 +992,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); @@ -1001,12 +1001,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 (avio_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); @@ -1014,13 +1014,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 (avio_feof(pb)) break; for (i = wtv->nb_index_entries - 1; i >= 0; i--) { AVIndexEntry *e = wtv->index_entries + i; @@ -1080,26 +1080,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 0000000..634545d --- /dev/null +++ b/libavformat/wtvenc.c @@ -0,0 +1,845 @@ +/* + * 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 "mpegts.h" +#include "wtv.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) + +/** + * 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 void put_videoinfoheader2(AVIOContext *pb, AVStream *st) +{ + AVRational dar = av_mul_q(st->sample_aspect_ratio, (AVRational){st->codec->width, st->codec->height}); + unsigned int num, den; + av_reduce(&num, &den, dar.num, dar.den, 0xFFFFFFFF); + + /* VIDEOINFOHEADER2 */ + avio_wl32(pb, 0); + avio_wl32(pb, 0); + avio_wl32(pb, st->codec->width); + avio_wl32(pb, st->codec->height); + + avio_wl32(pb, 0); + avio_wl32(pb, 0); + avio_wl32(pb, 0); + avio_wl32(pb, 0); + + avio_wl32(pb, st->codec->bit_rate); + avio_wl32(pb, 0); + avio_wl64(pb, st->avg_frame_rate.num && st->avg_frame_rate.den ? INT64_C(10000000) / av_q2d(st->avg_frame_rate) : 0); + avio_wl32(pb, 0); + avio_wl32(pb, 0); + + avio_wl32(pb, num); + avio_wl32(pb, den); + avio_wl32(pb, 0); + avio_wl32(pb, 0); + + ff_put_bmp_header(pb, st->codec, ff_codec_bmp_tags, 0, 1); + + if (st->codec->codec_id == AV_CODEC_ID_MPEG2VIDEO) { + int padding = (st->codec->extradata_size & 3) ? 4 - (st->codec->extradata_size & 3) : 0; + /* MPEG2VIDEOINFO */ + avio_wl32(pb, 0); + avio_wl32(pb, st->codec->extradata_size + padding); + avio_wl32(pb, -1); + avio_wl32(pb, -1); + avio_wl32(pb, 0); + avio_write(pb, st->codec->extradata, st->codec->extradata_size); + ffio_fill(pb, 0, padding); + } +} + +static int write_stream_codec_info(AVFormatContext *s, AVStream *st) +{ + const ff_asf_guid *g, *media_type, *format_type; + const AVCodecTag *tags; + 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 = st->codec->codec_id == AV_CODEC_ID_MPEG2VIDEO ? &ff_format_mpeg2_video : &ff_format_videoinfo2; + tags = ff_codec_bmp_tags; + } 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; + tags = ff_codec_wav_tags; + } else { + av_log(s, AV_LOG_ERROR, "unknown codec_type (0x%x)\n", st->codec->codec_type); + 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) { + put_videoinfoheader2(pb, st); + } else { + if (ff_put_wav_header(pb, st->codec, 0) < 0) + format_type = &ff_format_none; + } + 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); + if (g) { + ff_put_guid(pb, g); // actual_subtype + } else { + int tag = ff_codec_get_tag(tags, st->codec->codec_id); + if (!tag) { + av_log(s, AV_LOG_ERROR, "unsupported codec_id (0x%x)\n", st->codec->codec_id); + return -1; + } + avio_wl32(pb, tag); + avio_write(pb, (const uint8_t[]){FF_MEDIASUBTYPE_BASE_GUID}, 12); + } + 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; + AVStream *st = s->streams[pkt->stream_index]; + + if (st->codec->codec_id == AV_CODEC_ID_MJPEG && !wctx->thumbnail.size) { + av_copy_packet(&wctx->thumbnail, pkt); + return 0; + } else if (st->codec->codec_id == AV_CODEC_ID_H264) { + int ret = ff_check_h264_startcode(s, st, pkt); + if (ret < 0) + return ret; + } + + /* 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 724256b..0f4f807 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 ef285d2..07d2e1b 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 1a2a722..76768cd 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; @@ -121,7 +124,7 @@ static int wv_read_block_header(AVFormatContext *ctx, AVIOContext *pb) "Cannot determine additional parameters\n"); return AVERROR_INVALIDDATA; } - while (avio_tell(pb) < block_end) { + while (avio_tell(pb) < block_end && !avio_feof(pb)) { int id, size; id = avio_r8(pb); size = (id & 0x80) ? avio_rl24(pb) : avio_r8(pb); @@ -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 (avio_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 2e150e1..b0d74ca 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 57a36bc..43661de 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 bcb17f8..6eac4d2 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,36 +182,30 @@ 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)); + xmv->audio = av_malloc_array(xmv->audio_track_count, sizeof(XMVAudioPacket)); if (!xmv->audio) { ret = AVERROR(ENOMEM); goto fail; } 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 %"PRIu16".\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 45d74de..127c097 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 */ @@ -46,7 +46,7 @@ static int xwma_read_header(AVFormatContext *s) int64_t size; int ret; uint32_t dpds_table_size = 0; - uint32_t *dpds_table = 0; + uint32_t *dpds_table = NULL; unsigned int tag; AVIOContext *pb = s->pb; AVStream *st; @@ -130,8 +130,10 @@ static int xwma_read_header(AVFormatContext *s) /* parse the remaining RIFF chunks */ for (;;) { - if (pb->eof_reached) - return -1; + if (pb->eof_reached) { + ret = AVERROR_EOF; + goto end; + } /* read next chunk tag */ tag = avio_rl32(pb); size = avio_rl32(pb); @@ -152,7 +154,8 @@ static int xwma_read_header(AVFormatContext *s) /* Error out if there is more than one dpds chunk. */ if (dpds_table) { av_log(s, AV_LOG_ERROR, "two dpds chunks present\n"); - return -1; + ret = AVERROR_INVALIDDATA; + goto end; } /* Compute the number of entries in the dpds chunk. */ @@ -164,7 +167,7 @@ static int xwma_read_header(AVFormatContext *s) if (dpds_table_size == 0 || dpds_table_size >= INT_MAX / 4) { av_log(s, AV_LOG_ERROR, "dpds chunk size %"PRId64" invalid\n", size); - return -1; + return AVERROR_INVALIDDATA; } /* Allocate some temporary storage to keep the dpds data around. @@ -184,8 +187,10 @@ static int xwma_read_header(AVFormatContext *s) } /* Determine overall data length */ - if (size < 0) - return -1; + if (size < 0) { + ret = AVERROR_INVALIDDATA; + goto end; + } if (!size) { xwma->data_end = INT64_MAX; } else @@ -204,7 +209,8 @@ static int xwma_read_header(AVFormatContext *s) av_log(s, AV_LOG_ERROR, "Invalid bits_per_coded_sample %d for %d channels\n", st->codec->bits_per_coded_sample, st->codec->channels); - return AVERROR_INVALIDDATA; + ret = AVERROR_INVALIDDATA; + goto end; } st->duration = total_decoded_bytes / bytes_per_sample; @@ -239,9 +245,10 @@ static int xwma_read_header(AVFormatContext *s) st->duration = (size<<3) * st->codec->sample_rate / st->codec->bit_rate; } +end: av_free(dpds_table); - return 0; + return ret; } static int xwma_read_packet(AVFormatContext *s, AVPacket *pkt) diff --git a/libavformat/yop.c b/libavformat/yop.c index ea3175e..07086d5 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,12 @@ 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 = av_mallocz(8 + FF_INPUT_BUFFER_PADDING_SIZE); - - if (!video_stream->codec->extradata) + if (ff_alloc_extradata(video_stream->codec, 8)) return AVERROR(ENOMEM); - video_stream->codec->extradata_size = 8; // Audio audio_dec = audio_stream->codec; @@ -98,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.h b/libavformat/yuv4mpeg.h index 7891ed6..750f498 100644 --- a/libavformat/yuv4mpeg.h +++ b/libavformat/yuv4mpeg.h @@ -1,20 +1,20 @@ /* * YUV4MPEG common definitions * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free 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/yuv4mpegdec.c b/libavformat/yuv4mpegdec.c index e5c4456..c02b291 100644 --- a/libavformat/yuv4mpegdec.c +++ b/libavformat/yuv4mpegdec.c @@ -2,20 +2,20 @@ * YUV4MPEG demuxer * 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 */ @@ -79,20 +79,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 " @@ -119,10 +151,9 @@ static int yuv4_read_header(AVFormatContext *s) 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; + return AVERROR(EINVAL); } break; case 'F': // Frame rate @@ -145,6 +176,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) diff --git a/libavformat/yuv4mpegenc.c b/libavformat/yuv4mpegenc.c index 2caa364..a7f2d8a 100644 --- a/libavformat/yuv4mpegenc.c +++ b/libavformat/yuv4mpegenc.c @@ -2,20 +2,20 @@ * YUV4MPEG muxer * 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 */ @@ -49,7 +49,9 @@ static int yuv4_generate_header(AVFormatContext *s, char* buf) aspectd = 0; // 0:0 means unknown switch (st->codec->field_order) { + case AV_FIELD_TB: case AV_FIELD_TT: inter = 't'; break; + case AV_FIELD_BT: case AV_FIELD_BB: inter = 'b'; break; default: inter = 'p'; break; } @@ -58,6 +60,9 @@ static int yuv4_generate_header(AVFormatContext *s, char* buf) 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; @@ -74,6 +79,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 */ @@ -88,7 +138,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; @@ -96,7 +146,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) { @@ -119,18 +170,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]; @@ -143,6 +226,7 @@ static int yuv4_write_packet(AVFormatContext *s, AVPacket *pkt) ptr2 += picture->linesize[2]; } } + return 0; } @@ -158,15 +242,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); } @@ -178,7 +297,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, |