diff options
Diffstat (limited to 'libavformat')
385 files changed, 19712 insertions, 6084 deletions
diff --git a/libavformat/4xm.c b/libavformat/4xm.c index e530c34a47..0a003d4b8e 100644 --- a/libavformat/4xm.c +++ b/libavformat/4xm.c @@ -2,20 +2,20 @@ * 4X Technologies .4xm File Demuxer (no muxer) * Copyright (c) 2003 The ffmpeg Project * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -128,6 +128,10 @@ 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) { fourxm->fps = av_int2float(AV_RL32(&header[i + 12])); @@ -169,12 +173,13 @@ static int fourxm_read_header(AVFormatContext *s) current_track = AV_RL32(&header[i + 8]); if((unsigned)current_track >= UINT_MAX / sizeof(AudioTrack) - 1){ av_log(s, AV_LOG_ERROR, "current_track too large\n"); - ret= -1; + ret = AVERROR_INVALIDDATA; goto fail; } if (current_track + 1 > fourxm->track_count) { - fourxm->tracks = av_realloc(fourxm->tracks, - (current_track + 1) * sizeof(AudioTrack)); + fourxm->tracks = av_realloc_f(fourxm->tracks, + sizeof(AudioTrack), + current_track + 1); if (!fourxm->tracks) { ret = AVERROR(ENOMEM); goto fail; @@ -192,7 +197,12 @@ static int fourxm_read_header(AVFormatContext *s) || fourxm->tracks[current_track].sample_rate <= 0 || fourxm->tracks[current_track].bits < 0){ av_log(s, AV_LOG_ERROR, "audio header invalid\n"); - ret= -1; + ret = AVERROR_INVALIDDATA; + goto fail; + } + if(!fourxm->tracks[current_track].adpcm && fourxm->tracks[current_track].bits<8){ + av_log(s, AV_LOG_ERROR, "bits unspecified for non ADPCM\n"); + ret = AVERROR_INVALIDDATA; goto fail; } i += 8 + size; @@ -263,7 +273,7 @@ static int fourxm_read_packet(AVFormatContext *s, return ret; fourcc_tag = AV_RL32(&header[0]); size = AV_RL32(&header[4]); - if (pb->eof_reached) + if (url_feof(pb)) return AVERROR(EIO); switch (fourcc_tag) { diff --git a/libavformat/Makefile b/libavformat/Makefile index acf0500de1..ff16b3b9c4 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -1,3 +1,5 @@ +include $(SUBDIR)../config.mak + NAME = avformat FFLIBS = avcodec avutil @@ -19,6 +21,7 @@ OBJS = allformats.o \ riff.o \ sdp.o \ seek.o \ + subtitles.o \ utils.o \ OBJS-$(CONFIG_NETWORK) += network.o @@ -28,12 +31,15 @@ OBJS-$(CONFIG_A64_MUXER) += a64.o OBJS-$(CONFIG_AAC_DEMUXER) += aacdec.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_ADX_DEMUXER) += adxdec.o OBJS-$(CONFIG_ADX_MUXER) += rawenc.o OBJS-$(CONFIG_ADTS_MUXER) += adtsenc.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_AIFF_DEMUXER) += aiffdec.o pcm.o isom.o \ + mov_chan.o +OBJS-$(CONFIG_AIFF_MUXER) += aiffenc.o isom.o OBJS-$(CONFIG_AMR_DEMUXER) += amr.o OBJS-$(CONFIG_AMR_MUXER) += amr.o OBJS-$(CONFIG_ANM_DEMUXER) += anm.o @@ -54,10 +60,14 @@ 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_C93_DEMUXER) += c93.o vocdec.o voc.o OBJS-$(CONFIG_CAF_DEMUXER) += cafdec.o caf.o mov.o mov_chan.o \ isom.o +OBJS-$(CONFIG_CAF_MUXER) += cafenc.o caf.o riff.o isom.o OBJS-$(CONFIG_CAVSVIDEO_DEMUXER) += cavsvideodec.o rawdec.o OBJS-$(CONFIG_CAVSVIDEO_MUXER) += rawenc.o OBJS-$(CONFIG_CDG_DEMUXER) += cdg.o @@ -71,6 +81,7 @@ OBJS-$(CONFIG_DIRAC_MUXER) += rawenc.o OBJS-$(CONFIG_DNXHD_DEMUXER) += dnxhddec.o rawdec.o OBJS-$(CONFIG_DNXHD_MUXER) += rawenc.o OBJS-$(CONFIG_DSICIN_DEMUXER) += dsicin.o +OBJS-$(CONFIG_DTSHD_DEMUXER) += dtshddec.o OBJS-$(CONFIG_DTS_DEMUXER) += dtsdec.o rawdec.o OBJS-$(CONFIG_DTS_MUXER) += rawenc.o OBJS-$(CONFIG_DV_DEMUXER) += dv.o @@ -104,6 +115,8 @@ 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 @@ -111,7 +124,10 @@ OBJS-$(CONFIG_H263_MUXER) += rawenc.o OBJS-$(CONFIG_H264_DEMUXER) += h264dec.o rawdec.o OBJS-$(CONFIG_H264_MUXER) += rawenc.o OBJS-$(CONFIG_HLS_DEMUXER) += hls.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 OBJS-$(CONFIG_IFF_DEMUXER) += iff.o OBJS-$(CONFIG_ILBC_DEMUXER) += ilbc.o OBJS-$(CONFIG_ILBC_MUXER) += ilbc.o @@ -128,10 +144,14 @@ 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 +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 +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 @@ -141,6 +161,9 @@ OBJS-$(CONFIG_MATROSKA_MUXER) += matroskaenc.o matroska.o \ isom.o avc.o \ flacenc_header.o avlanguage.o OBJS-$(CONFIG_MD5_MUXER) += md5enc.o +OBJS-$(CONFIG_MGSTS_DEMUXER) += mgsts.o +OBJS-$(CONFIG_MICRODVD_DEMUXER) += microdvddec.o +OBJS-$(CONFIG_MICRODVD_MUXER) += microdvdenc.o rawenc.o OBJS-$(CONFIG_MJPEG_DEMUXER) += rawdec.o OBJS-$(CONFIG_MJPEG_MUXER) += rawenc.o OBJS-$(CONFIG_MLP_DEMUXER) += rawdec.o @@ -186,6 +209,7 @@ OBJS-$(CONFIG_OGG_DEMUXER) += oggdec.o \ oggparsedirac.o \ oggparseflac.o \ oggparseogm.o \ + oggparseopus.o \ oggparseskeleton.o \ oggparsespeex.o \ oggparsetheora.o \ @@ -195,6 +219,7 @@ OBJS-$(CONFIG_OGG_MUXER) += oggenc.o \ vorbiscomment.o OBJS-$(CONFIG_OMA_DEMUXER) += omadec.o pcm.o oma.o OBJS-$(CONFIG_OMA_MUXER) += omaenc.o rawenc.o oma.o id3v2enc.o +OBJS-$(CONFIG_PAF_DEMUXER) += paf.o OBJS-$(CONFIG_PCM_ALAW_DEMUXER) += pcmdec.o pcm.o OBJS-$(CONFIG_PCM_ALAW_MUXER) += pcmenc.o rawenc.o OBJS-$(CONFIG_PCM_F32BE_DEMUXER) += pcmdec.o pcm.o @@ -241,6 +266,7 @@ 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 OBJS-$(CONFIG_RL2_DEMUXER) += rl2.o OBJS-$(CONFIG_RM_DEMUXER) += rmdec.o rm.o rmsipr.o OBJS-$(CONFIG_RM_MUXER) += rmenc.o rm.o @@ -285,8 +311,10 @@ OBJS-$(CONFIG_RTSP_DEMUXER) += rtsp.o rtspdec.o httpauth.o \ urldecode.o OBJS-$(CONFIG_RTSP_MUXER) += rtsp.o rtspenc.o httpauth.o \ rtpenc_chain.o urldecode.o +OBJS-$(CONFIG_SAMI_DEMUXER) += samidec.o OBJS-$(CONFIG_SAP_DEMUXER) += sapdec.o OBJS-$(CONFIG_SAP_MUXER) += sapenc.o rtpenc_chain.o +OBJS-$(CONFIG_SBG_DEMUXER) += sbgdec.o OBJS-$(CONFIG_SDP_DEMUXER) += rtsp.o OBJS-$(CONFIG_SEGAFILM_DEMUXER) += segafilm.o OBJS-$(CONFIG_SEGMENT_MUXER) += segment.o @@ -296,18 +324,22 @@ OBJS-$(CONFIG_SMACKER_DEMUXER) += smacker.o OBJS-$(CONFIG_SMJPEG_DEMUXER) += smjpegdec.o smjpeg.o OBJS-$(CONFIG_SMJPEG_MUXER) += smjpegenc.o smjpeg.o OBJS-$(CONFIG_SMOOTHSTREAMING_MUXER) += smoothstreamingenc.o isom.o +OBJS-$(CONFIG_SMUSH_DEMUXER) += smush.o OBJS-$(CONFIG_SOL_DEMUXER) += sol.o pcm.o OBJS-$(CONFIG_SOX_DEMUXER) += soxdec.o pcm.o OBJS-$(CONFIG_SOX_MUXER) += soxenc.o OBJS-$(CONFIG_SPDIF_DEMUXER) += spdif.o spdifdec.o OBJS-$(CONFIG_SPDIF_MUXER) += spdif.o spdifenc.o OBJS-$(CONFIG_SRT_DEMUXER) += srtdec.o -OBJS-$(CONFIG_SRT_MUXER) += rawenc.o +OBJS-$(CONFIG_SRT_MUXER) += srtenc.o OBJS-$(CONFIG_STR_DEMUXER) += psxstr.o +OBJS-$(CONFIG_SUBVIEWER_DEMUXER) += subviewerdec.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_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 @@ -328,12 +360,16 @@ OBJS-$(CONFIG_WC3_DEMUXER) += wc3movie.o OBJS-$(CONFIG_WEBM_MUXER) += matroskaenc.o matroska.o \ isom.o avc.o \ flacenc_header.o avlanguage.o +OBJS-$(CONFIG_WEBVTT_DEMUXER) += webvttdec.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.o asfdec.o asf.o asfcrypt.o \ avlanguage.o mpegts.o isom.o +OBJS-$(CONFIG_WTV_MUXER) += wtvenc.o wtv.o asf.o asfenc.o OBJS-$(CONFIG_WV_DEMUXER) += wv.o apetag.o img2.o +OBJS-$(CONFIG_WV_MUXER) += wvenc.o apetagenc.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 @@ -341,10 +377,15 @@ OBJS-$(CONFIG_YUV4MPEGPIPE_MUXER) += yuv4mpeg.o OBJS-$(CONFIG_YUV4MPEGPIPE_DEMUXER) += yuv4mpeg.o # external libraries +OBJS-$(CONFIG_LIBMODPLUG_DEMUXER) += libmodplug.o +OBJS-$(CONFIG_LIBNUT_DEMUXER) += libnut.o +OBJS-$(CONFIG_LIBNUT_MUXER) += libnut.o OBJS-$(CONFIG_LIBRTMP) += librtmp.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_FFRTMPCRYPT_PROTOCOL) += rtmpcrypt.o rtmpdh.o @@ -373,10 +414,6 @@ OBJS-$(CONFIG_UDP_PROTOCOL) += udp.o SKIPHEADERS-$(CONFIG_FFRTMPCRYPT_PROTOCOL) += rtmpdh.h SKIPHEADERS-$(CONFIG_NETWORK) += network.h rtsp.h - -EXAMPLES = metadata \ - output \ - TESTPROGS = seek \ url \ @@ -384,5 +421,3 @@ TOOLS = aviocat \ ismindex \ pktdumper \ probetest \ - -$(SUBDIR)output-example$(EXESUF): ELIBS = $(patsubst %,$(LD_LIB),swscale) diff --git a/libavformat/a64.c b/libavformat/a64.c index 56b9a446db..c1f6b67904 100644 --- a/libavformat/a64.c +++ b/libavformat/a64.c @@ -2,20 +2,20 @@ * 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 */ diff --git a/libavformat/aacdec.c b/libavformat/aacdec.c index 6f9763facb..e165959dab 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 */ @@ -48,6 +48,7 @@ static int adts_aac_probe(AVProbeData *p) fsize = (AV_RB32(buf2 + 3) >> 13) & 0x1FFF; if(fsize < 7) break; + fsize = FFMIN(fsize, end - buf2); buf2 += fsize; } max_frames = FFMAX(max_frames, frames); @@ -71,7 +72,7 @@ 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); diff --git a/libavformat/ac3dec.c b/libavformat/ac3dec.c index d3f2ec297d..7a34ea8b4b 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 */ @@ -37,14 +37,36 @@ static int ac3_eac3_probe(AVProbeData *p, enum AVCodecID expected_codec_id) end = buf + p->buf_size; for(; buf < end; buf++) { + if(buf > p->buf && !(buf[0] == 0x0B && buf[1] == 0x77) + && !(buf[0] == 0x77 && buf[1] == 0x0B) ) + continue; buf2 = buf; for(frames = 0; buf2 < end; frames++) { - init_get_bits(&gbc, buf2, 54); + uint8_t buf3[4096]; + int i; + if(!memcmp(buf2, "\x1\x10\0\0\0\0\0\0", 8)) + buf2+=16; + if (buf[0] == 0x77 && buf[1] == 0x0B) { + for(i=0; i<8; i+=2) { + buf3[i ] = buf[i+1]; + buf3[i+1] = buf[i ]; + } + init_get_bits(&gbc, buf3, 54); + }else + init_get_bits(&gbc, buf2, 54); if(avpriv_ac3_parse_header(&gbc, &hdr) < 0) break; - if(buf2 + hdr.frame_size > end || - av_crc(av_crc_get_table(AV_CRC_16_ANSI), 0, buf2 + 2, hdr.frame_size - 2)) + if(buf2 + hdr.frame_size > end) + break; + if (buf[0] == 0x77 && buf[1] == 0x0B) { + av_assert0(hdr.frame_size <= sizeof(buf3)); + for(i=8; i<hdr.frame_size; i+=2) { + buf3[i ] = buf[i+1]; + buf3[i+1] = buf[i ]; + } + } + if(av_crc(av_crc_get_table(AV_CRC_16_ANSI), 0, gbc.buffer + 2, hdr.frame_size - 2)) break; if (hdr.bitstream_id > 10) codec_id = AV_CODEC_ID_EAC3; @@ -57,31 +79,11 @@ static int ac3_eac3_probe(AVProbeData *p, enum AVCodecID expected_codec_id) if(codec_id != expected_codec_id) return 0; // keep this in sync with mp3 probe, both need to avoid // issues with MPEG-files! - if (first_frames >= 4) return AVPROBE_SCORE_MAX / 2 + 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_MAX / 2; - else if (max_frames >= 4) return AVPROBE_SCORE_MAX / 4; - else if (max_frames >= 1) return 1; - else return 0; + if (first_frames>=4) return AVPROBE_SCORE_MAX/2+1; + else if(max_frames>200)return AVPROBE_SCORE_MAX/2; + else if(max_frames>=4) return AVPROBE_SCORE_MAX/4; + else if(max_frames>=1) return 1; + else return 0; } #if CONFIG_AC3_DEMUXER diff --git a/libavformat/act.c b/libavformat/act.c new file mode 100644 index 0000000000..78f1d3bb9a --- /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 cant 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/adtsenc.c b/libavformat/adtsenc.c index 0c9194449e..14c72a8c63 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 */ diff --git a/libavformat/aea.c b/libavformat/aea.c index 4b6bf32b05..6aa7ae321c 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/aiff.h b/libavformat/aiff.h index a30e33e09b..6ece0e0ce3 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 */ diff --git a/libavformat/aiffdec.c b/libavformat/aiffdec.c index 6243bcb30f..79b78533e3 100644 --- a/libavformat/aiffdec.c +++ b/libavformat/aiffdec.c @@ -2,29 +2,33 @@ * AIFF/AIFF-C demuxer * Copyright (c) 2006 Patrick Guimond * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "libavutil/intreadwrite.h" #include "libavutil/mathematics.h" #include "libavutil/dict.h" #include "avformat.h" #include "internal.h" #include "pcm.h" #include "aiff.h" +#include "isom.h" +#include "id3v2.h" +#include "mov_chan.h" #define AIFF 0 #define AIFF_C_VERSION1 0xA2805140 @@ -54,7 +58,7 @@ static int get_tag(AVIOContext *pb, uint32_t * tag) { int size; - if (pb->eof_reached) + if (url_feof(pb)) return AVERROR(EIO); *tag = avio_rl32(pb); @@ -70,19 +74,20 @@ static int get_tag(AVIOContext *pb, uint32_t * tag) static void get_meta(AVFormatContext *s, const char *key, int size) { uint8_t *str = av_malloc(size+1); - int res; - if (!str) { - avio_skip(s->pb, size); - return; - } - - res = avio_read(s->pb, str, size); - if (res < 0) - return; + if (str) { + int res = avio_read(s->pb, str, size); + if (res < 0){ + av_free(str); + return; + } + size += (size&1)-res; + str[res] = 0; + av_dict_set(&s->metadata, key, str, AV_DICT_DONT_STRDUP_VAL); + }else + size+= size&1; - str[res] = 0; - av_dict_set(&s->metadata, key, str, AV_DICT_DONT_STRDUP_VAL); + avio_skip(s->pb, size); } /* Returns the number of sound data frames or negative on error */ @@ -146,6 +151,7 @@ static unsigned int get_aiff_header(AVFormatContext *s, int size, 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; + int ret, size, filesize; int64_t offset = 0; 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,15 @@ static int aiff_read_header(AVFormatContext *s) if (offset > 0) // COMM is after SSND goto got_sound; break; + case MKTAG('I', 'D', '3', ' '): + ff_id3v2_read(s, ID3v2_DEFAULT_MAGIC, &id3v2_extra_meta); + if (id3v2_extra_meta) + if ((ret = ff_id3v2_parse_apic(s, &id3v2_extra_meta)) < 0) { + ff_id3v2_free_extra_meta(&id3v2_extra_meta); + return ret; + } + ff_id3v2_free_extra_meta(&id3v2_extra_meta); + break; case MKTAG('F', 'V', 'E', 'R'): /* Version chunk */ version = avio_rb32(pb); break; @@ -248,7 +264,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"); @@ -264,6 +280,12 @@ static int aiff_read_header(AVFormatContext *s) 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); + 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 */ @@ -314,6 +336,8 @@ static int aiff_read_packet(AVFormatContext *s, if (res < 0) return res; + if (size >= st->codec->block_align) + pkt->flags &= ~AV_PKT_FLAG_CORRUPT; /* Only one stream in an AIFF file */ pkt->stream_index = 0; pkt->duration = (res / st->codec->block_align) * aiff->block_duration; diff --git a/libavformat/aiffenc.c b/libavformat/aiffenc.c index abcc424596..8cdb2616c2 100644 --- a/libavformat/aiffenc.c +++ b/libavformat/aiffenc.c @@ -2,20 +2,20 @@ * 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 */ @@ -24,6 +24,7 @@ #include "internal.h" #include "aiff.h" #include "avio_internal.h" +#include "isom.h" typedef struct { int64_t form; @@ -63,6 +64,12 @@ 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); + } + /* Common chunk */ ffio_wfourcc(pb, "COMM"); avio_wb32(pb, aifc ? 24 : 18); /* size */ @@ -91,6 +98,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 */ diff --git a/libavformat/allformats.c b/libavformat/allformats.c index 39daec70c7..35513944f0 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 */ #include "avformat.h" @@ -52,6 +52,8 @@ 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_MUXER (ADTS, adts); REGISTER_MUXDEMUX (ADX, adx); REGISTER_DEMUXER (AEA, aea); @@ -70,10 +72,12 @@ void av_register_all(void) 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 (C93, c93); - REGISTER_DEMUXER (CAF, caf); + REGISTER_MUXDEMUX (CAF, caf); REGISTER_MUXDEMUX (CAVSVIDEO, cavsvideo); REGISTER_DEMUXER (CDG, cdg); REGISTER_DEMUXER (CDXL, cdxl); @@ -84,11 +88,13 @@ void av_register_all(void) REGISTER_MUXDEMUX (DNXHD, dnxhd); REGISTER_DEMUXER (DSICIN, dsicin); REGISTER_MUXDEMUX (DTS, dts); + REGISTER_DEMUXER (DTSHD, dtshd); REGISTER_MUXDEMUX (DV, dv); REGISTER_DEMUXER (DXA, dxa); REGISTER_DEMUXER (EA, ea); REGISTER_DEMUXER (EA_CDATA, ea_cdata); REGISTER_MUXDEMUX (EAC3, eac3); + REGISTER_MUXER (F4V, f4v); REGISTER_MUXDEMUX (FFM, ffm); REGISTER_MUXDEMUX (FFMETADATA, ffmetadata); REGISTER_MUXDEMUX (FILMSTRIP, filmstrip); @@ -99,7 +105,8 @@ void av_register_all(void) REGISTER_MUXER (FRAMECRC, framecrc); REGISTER_MUXER (FRAMEMD5, framemd5); REGISTER_MUXDEMUX (G722, g722); - REGISTER_DEMUXER (G723_1, g723_1); + REGISTER_MUXDEMUX (G723_1, g723_1); + REGISTER_DEMUXER (G729, g729); REGISTER_MUXER (GIF, gif); REGISTER_DEMUXER (GSM, gsm); REGISTER_MUXDEMUX (GXF, gxf); @@ -107,7 +114,9 @@ void av_register_all(void) REGISTER_MUXDEMUX (H263, h263); REGISTER_MUXDEMUX (H264, h264); REGISTER_DEMUXER (HLS, hls); + REGISTER_MUXDEMUX (ICO, ico); REGISTER_DEMUXER (IDCIN, idcin); + REGISTER_DEMUXER (IDF, idf); REGISTER_DEMUXER (IFF, iff); REGISTER_MUXDEMUX (ILBC, ilbc); REGISTER_MUXDEMUX (IMAGE2, image2); @@ -119,14 +128,19 @@ void av_register_all(void) REGISTER_DEMUXER (ISS, iss); REGISTER_DEMUXER (IV8, iv8); REGISTER_MUXDEMUX (IVF, ivf); + REGISTER_MUXDEMUX (JACOSUB, jacosub); REGISTER_DEMUXER (JV, jv); REGISTER_MUXDEMUX (LATM, latm); REGISTER_DEMUXER (LMLM4, lmlm4); + REGISTER_DEMUXER (LOAS, loas); + REGISTER_DEMUXER (LVF, lvf); REGISTER_DEMUXER (LXF, lxf); REGISTER_MUXDEMUX (M4V, m4v); REGISTER_MUXER (MD5, md5); REGISTER_MUXDEMUX (MATROSKA, matroska); REGISTER_MUXER (MATROSKA_AUDIO, matroska_audio); + REGISTER_DEMUXER (MGSTS, mgsts); + REGISTER_MUXDEMUX (MICRODVD, microdvd); REGISTER_MUXDEMUX (MJPEG, mjpeg); REGISTER_MUXDEMUX (MLP, mlp); REGISTER_DEMUXER (MM, mm); @@ -162,6 +176,7 @@ void av_register_all(void) REGISTER_DEMUXER (NUV, nuv); REGISTER_MUXDEMUX (OGG, ogg); REGISTER_MUXDEMUX (OMA, oma); + REGISTER_DEMUXER (PAF, paf); REGISTER_MUXDEMUX (PCM_ALAW, pcm_alaw); REGISTER_MUXDEMUX (PCM_MULAW, pcm_mulaw); REGISTER_MUXDEMUX (PCM_F64BE, pcm_f64be); @@ -188,6 +203,7 @@ void av_register_all(void) REGISTER_DEMUXER (QCP, qcp); REGISTER_DEMUXER (R3D, r3d); REGISTER_MUXDEMUX (RAWVIDEO, rawvideo); + REGISTER_DEMUXER (REALTEXT, realtext); REGISTER_DEMUXER (RL2, rl2); REGISTER_MUXDEMUX (RM, rm); REGISTER_MUXDEMUX (ROQ, roq); @@ -195,7 +211,9 @@ void av_register_all(void) REGISTER_MUXDEMUX (RSO, rso); REGISTER_MUXDEMUX (RTP, rtp); REGISTER_MUXDEMUX (RTSP, rtsp); + REGISTER_DEMUXER (SAMI, sami); REGISTER_MUXDEMUX (SAP, sap); + REGISTER_DEMUXER (SBG, sbg); REGISTER_DEMUXER (SDP, sdp); #if CONFIG_RTPDEC av_register_rtp_dynamic_payload_handlers(); @@ -203,21 +221,26 @@ void av_register_all(void) #endif REGISTER_DEMUXER (SEGAFILM, segafilm); REGISTER_MUXER (SEGMENT, segment); + REGISTER_MUXER (SEGMENT, stream_segment); REGISTER_DEMUXER (SHORTEN, shorten); REGISTER_DEMUXER (SIFF, siff); REGISTER_DEMUXER (SMACKER, smacker); REGISTER_MUXDEMUX (SMJPEG, smjpeg); REGISTER_MUXER (SMOOTHSTREAMING, smoothstreaming); + REGISTER_DEMUXER (SMUSH, smush); REGISTER_DEMUXER (SOL, sol); REGISTER_MUXDEMUX (SOX, sox); REGISTER_MUXDEMUX (SPDIF, spdif); REGISTER_MUXDEMUX (SRT, srt); REGISTER_DEMUXER (STR, str); + REGISTER_DEMUXER (SUBVIEWER, subviewer); REGISTER_MUXDEMUX (SWF, swf); + REGISTER_DEMUXER (TAK, tak); 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); @@ -232,20 +255,28 @@ void av_register_all(void) REGISTER_MUXDEMUX (WAV, wav); REGISTER_DEMUXER (WC3, wc3); REGISTER_MUXER (WEBM, webm); + REGISTER_DEMUXER (WEBVTT, webvtt); REGISTER_DEMUXER (WSAUD, wsaud); REGISTER_DEMUXER (WSVQA, wsvqa); - REGISTER_DEMUXER (WTV, wtv); - REGISTER_DEMUXER (WV, wv); + 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); + /* external libraries */ +#if CONFIG_LIBMODPLUG + REGISTER_DEMUXER (LIBMODPLUG, libmodplug); +#endif /* protocols */ #if FF_API_APPLEHTTP_PROTO REGISTER_PROTOCOL (APPLEHTTP, applehttp); #endif + REGISTER_PROTOCOL (BLURAY, bluray); + REGISTER_PROTOCOL (CACHE, cache); REGISTER_PROTOCOL (CONCAT, concat); REGISTER_PROTOCOL (CRYPTO, crypto); REGISTER_PROTOCOL (FFRTMPCRYPT, ffrtmpcrypt); @@ -273,6 +304,7 @@ void av_register_all(void) REGISTER_PROTOCOL (UDP, udp); /* external libraries */ + REGISTER_MUXDEMUX (LIBNUT, libnut); REGISTER_PROTOCOL (LIBRTMP, librtmp); REGISTER_PROTOCOL (LIBRTMPE, librtmpe); REGISTER_PROTOCOL (LIBRTMPS, librtmps); diff --git a/libavformat/amr.c b/libavformat/amr.c index cc4842ebd5..36ff9b036a 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 */ @@ -25,6 +25,7 @@ Write and read amr data according to RFC3267, http://www.ietf.org/rfc/rfc3267.tx Only mono files are supported. */ +#include "libavutil/avassert.h" #include "avformat.h" #include "internal.h" @@ -109,7 +110,7 @@ static int amr_read_packet(AVFormatContext *s, AVPacket *pkt) int read, size = 0, toc, mode; int64_t pos = avio_tell(s->pb); - if (s->pb->eof_reached) { + if (url_feof(s->pb)) { return AVERROR(EIO); } @@ -130,7 +131,7 @@ static int amr_read_packet(AVFormatContext *s, AVPacket *pkt) size = packed_size[mode]; } else { - assert(0); + av_assert0(0); } if (!size || av_new_packet(pkt, size)) diff --git a/libavformat/anm.c b/libavformat/anm.c index c7bc843c8e..69a5ddab0f 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 */ @@ -134,18 +134,17 @@ static int read_header(AVFormatContext *s) /* color cycling and palette data */ st->codec->extradata_size = 16*8 + 4*256; st->codec->extradata = av_mallocz(st->codec->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE); - if (!st->codec->extradata) { - ret = AVERROR(ENOMEM); - goto fail; - } + if (!st->codec->extradata) + return AVERROR(ENOMEM); + ret = avio_read(pb, st->codec->extradata, st->codec->extradata_size); if (ret < 0) - goto fail; + return ret; /* read page table */ ret = avio_seek(pb, anm->page_table_offset, SEEK_SET); if (ret < 0) - goto fail; + return ret; for (i = 0; i < MAX_PAGES; i++) { Page *p = &anm->pt[i]; @@ -156,20 +155,15 @@ static int read_header(AVFormatContext *s) /* find page of first frame */ anm->page = find_record(anm, 0); - if (anm->page < 0) { - ret = anm->page; - goto fail; - } + if (anm->page < 0) + return anm->page; anm->record = -1; return 0; invalid: av_log_ask_for_sample(s, NULL); - ret = AVERROR_INVALIDDATA; - -fail: - return ret; + return AVERROR_INVALIDDATA; } static int read_packet(AVFormatContext *s, @@ -180,7 +174,7 @@ static int read_packet(AVFormatContext *s, Page *p; int tmp, record_size; - if (s->pb->eof_reached) + if (url_feof(s->pb)) return AVERROR(EIO); if (anm->page < 0) diff --git a/libavformat/apc.c b/libavformat/apc.c index 7ae2423cf8..2ccf7946aa 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 */ @@ -76,6 +76,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 d67e684167..c4a390b4fd 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 { @@ -278,6 +276,9 @@ static int ape_read_header(AVFormatContext * s) return AVERROR(ENOMEM); for (i = 0; i < ape->seektablelength / sizeof(uint32_t); i++) ape->seektable[i] = avio_rl32(pb); + }else{ + av_log(s, AV_LOG_ERROR, "Missing seektable\n"); + return -1; } ape->frames[0].pos = ape->firstframe; @@ -332,8 +333,8 @@ 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; @@ -345,7 +346,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 */ @@ -364,7 +365,7 @@ static int ape_read_packet(AVFormatContext * s, AVPacket * pkt) APEContext *ape = s->priv_data; uint32_t extra_size = 8; - if (s->pb->eof_reached) + if (url_feof(s->pb)) return AVERROR_EOF; if (ape->currentframe >= ape->totalframes) return AVERROR_EOF; diff --git a/libavformat/apetag.c b/libavformat/apetag.c index 28a3ff7753..bf9918a0b3 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 */ @@ -26,8 +26,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_IS_HEADER (1 << 29) #define APE_TAG_FLAG_IS_BINARY (1 << 1) @@ -128,7 +126,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; } diff --git a/libavformat/apetag.h b/libavformat/apetag.h index 279972ff6e..0330c8986a 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 * @@ -32,4 +36,9 @@ */ int64_t ff_ape_parse_tag(AVFormatContext *s); +/** + * Write an APEv2 tag + */ +void ff_ape_write(AVFormatContext *s); + #endif /* AVFORMAT_APETAG_H */ diff --git a/libavformat/apetagenc.c b/libavformat/apetagenc.c new file mode 100644 index 0000000000..42f58362a6 --- /dev/null +++ b/libavformat/apetagenc.c @@ -0,0 +1,65 @@ +/* + * APE tag writer + * 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/dict.h" +#include "avio_internal.h" +#include "avformat.h" +#include "apetag.h" + +static int string_is_ascii(const uint8_t *str) +{ + while (*str && *str >= 0x20 && *str <= 0x7e ) str++; + return !*str; +} + +void ff_ape_write(AVFormatContext *s) +{ + int64_t tag_bytes; + AVDictionaryEntry *t = NULL; + AVIOContext *pb = s->pb; + int tags = 0, vlen; + + tag_bytes = avio_tell(s->pb); + while ((t = av_dict_get(s->metadata, "", t, AV_DICT_IGNORE_SUFFIX))) { + if (!string_is_ascii(t->key)) { + av_log(s, AV_LOG_WARNING, "Non ASCII keys are not allowed\n"); + continue; + } + + vlen = strlen(t->value); + avio_wl32(pb, vlen + 1); + avio_wl32(pb, 0); // flags + avio_put_str(pb, t->key); + avio_put_str(pb, t->value); + tags++; + } + tag_bytes = avio_tell(s->pb) - tag_bytes; + + if (!tags) + return; + + avio_write(pb, APE_TAG_PREAMBLE, 8); + avio_wl32(pb, APE_TAG_VERSION); + avio_wl32(pb, tag_bytes + APE_TAG_FOOTER_BYTES); + avio_wl32(pb, tags); // item count + avio_wl32(pb, 0); // global flags + ffio_fill(pb, 0, 8); // reserved +} diff --git a/libavformat/asf.c b/libavformat/asf.c index cc2833ddf6..8e360d3596 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 */ @@ -152,7 +152,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 1d94a2c924..822cd6bbea 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 */ @@ -24,6 +24,7 @@ #include <stdint.h> #include "avformat.h" #include "metadata.h" +#include "riff.h" #define PACKET_SIZE 3200 @@ -48,8 +49,6 @@ typedef struct ASFStream { uint32_t palette[256]; } ASFStream; -typedef uint8_t ff_asf_guid[16]; - typedef struct ASFMainHeader { ff_asf_guid guid; ///< generated by client computer uint64_t file_size; /**< in bytes @@ -175,11 +174,6 @@ extern const AVMetadataConv ff_asf_metadata_conv[]; extern AVInputFormat ff_asf_demuxer; -static av_always_inline int ff_guidcmp(const void *g1, const void *g2) -{ - return memcmp(g1, g2, sizeof(ff_asf_guid)); -} - -void ff_get_guid(AVIOContext *s, ff_asf_guid *g); +void ff_put_guid(AVIOContext *s, const ff_asf_guid *g); #endif /* AVFORMAT_ASF_H */ diff --git a/libavformat/asfcrypt.c b/libavformat/asfcrypt.c index 6c48a1967a..6a51c0426e 100644 --- a/libavformat/asfcrypt.c +++ b/libavformat/asfcrypt.c @@ -3,20 +3,20 @@ * Copyright (c) 2007 Reimar Doeffinger * This is a rewrite of code contained in freeme/freeme2 * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/asfcrypt.h b/libavformat/asfcrypt.h index 53388b426e..8b80d63c5d 100644 --- a/libavformat/asfcrypt.h +++ b/libavformat/asfcrypt.h @@ -2,20 +2,20 @@ * ASF decryption * Copyright (c) 2007 Reimar Doeffinger * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/asfdec.c b/libavformat/asfdec.c index c6b322d6d3..9f56818312 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 */ @@ -96,12 +96,8 @@ static const AVClass asf_class = { #include <assert.h> #define ASF_MAX_STREAMS 127 -#define FRAME_HEADER_SIZE 17 -// Fix Me! FRAME_HEADER_SIZE may be different. - -static const ff_asf_guid index_guid = { - 0x90, 0x08, 0x00, 0x33, 0xb1, 0xe5, 0xcf, 0x11, 0x89, 0xf4, 0x00, 0xa0, 0xc9, 0x03, 0x49, 0xcb -}; +#define FRAME_HEADER_SIZE 16 +// Fix Me! FRAME_HEADER_SIZE may be different. (17 is known to be too large) #ifdef DEBUG static const ff_asf_guid stream_bitrate_guid = { /* (http://get.to/sdp) */ @@ -127,7 +123,7 @@ static void print_guid(const 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); @@ -150,12 +146,6 @@ static void print_guid(const ff_asf_guid *g) #define print_guid(g) #endif -void ff_get_guid(AVIOContext *s, ff_asf_guid *g) -{ - assert(sizeof(*g) == 16); - avio_read(s, *g, sizeof(*g)); -} - static int asf_probe(AVProbeData *pd) { /* check file header */ @@ -284,6 +274,9 @@ static void get_tag(AVFormatContext *s, const char *key, int type, int len) 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 && type <= 5) { // boolean or DWORD or QWORD or WORD uint64_t num = get_value(s->pb, type); snprintf(value, len, "%"PRIu64, num); @@ -353,14 +346,15 @@ static int asf_read_stream_properties(AVFormatContext *s, int64_t size) 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 / - (10000000 / 1000) - start_time; + int64_t fsize = avio_size(pb); + if (fsize <= 0 || (int64_t)asf->hdr.file_size <= 0 || FFABS(fsize - (int64_t)asf->hdr.file_size) < 10000) + st->duration = asf->hdr.play_time / + (10000000 / 1000) - start_time; } ff_get_guid(pb, &g); @@ -412,7 +406,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) { @@ -686,7 +680,7 @@ 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); @@ -708,7 +702,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) @@ -735,16 +729,28 @@ static int asf_read_header(AVFormatContext *s) continue; } else if (!ff_guidcmp(&g, &ff_asf_marker_header)) { asf_read_marker(s, gsize); - } else if (pb->eof_reached) { - return -1; + } else if (url_feof(pb)) { + return AVERROR_EOF; } else { if (!s->keylen) { if (!ff_guidcmp(&g, &ff_asf_content_encryption)) { + unsigned int len; + AVPacket pkt; av_log(s, AV_LOG_WARNING, "DRM protected stream detected, decoding will likely fail!\n"); + len= avio_rl32(pb); + av_log(s, AV_LOG_DEBUG, "Secret data:\n"); + av_get_packet(pb, &pkt, len); av_hex_dump_log(s, AV_LOG_DEBUG, pkt.data, pkt.size); av_free_packet(&pkt); + len= avio_rl32(pb); + get_tag(s, "ASF_Protection_Type", -1, len); + len= avio_rl32(pb); + get_tag(s, "ASF_Key_ID", -1, len); + len= avio_rl32(pb); + get_tag(s, "ASF_License_URL", -1, len); } 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"); } } } @@ -756,8 +762,8 @@ static int asf_read_header(AVFormatContext *s) avio_rl64(pb); avio_r8(pb); avio_r8(pb); - if (pb->eof_reached) - return -1; + if (url_feof(pb)) + return AVERROR_EOF; asf->data_offset = avio_tell(pb); asf->packet_size_left = 0; @@ -769,12 +775,12 @@ static int asf_read_header(AVFormatContext *s) if (!st->codec->bit_rate) st->codec->bit_rate = asf->stream_bitrates[i]; if (asf->dar[i].num > 0 && asf->dar[i].den > 0){ - av_reduce(&st->sample_aspect_ratio.num, - &st->sample_aspect_ratio.den, + av_reduce(&st->sample_aspect_ratio.den, + &st->sample_aspect_ratio.num, asf->dar[i].num, asf->dar[i].den, INT_MAX); } else if ((asf->dar[0].num > 0) && (asf->dar[0].den > 0) && (st->codec->codec_type==AVMEDIA_TYPE_VIDEO)) // Use ASF container value if the stream doesn't AR set. - av_reduce(&st->sample_aspect_ratio.num, - &st->sample_aspect_ratio.den, + av_reduce(&st->sample_aspect_ratio.den, + &st->sample_aspect_ratio.num, asf->dar[0].num, asf->dar[0].den, INT_MAX); av_dlog(s, "i=%d, st->codec->codec_type:%d, asf->dar %d:%d sar=%d:%d\n", @@ -845,19 +851,19 @@ static int ff_asf_get_packet(AVFormatContext *s, AVIOContext *pb) */ if (pb->error == AVERROR(EAGAIN)) return AVERROR(EAGAIN); - if (!pb->eof_reached) + if (!url_feof(pb)) av_log(s, AV_LOG_ERROR, "ff asf bad header %x at:%"PRId64"\n", c, avio_tell(pb)); } if ((c & 0x8f) == 0x82) { if (d || e) { - if (!pb->eof_reached) + if (!url_feof(pb)) av_log(s, AV_LOG_ERROR, "ff asf bad non zero\n"); - return -1; + return AVERROR_INVALIDDATA; } c= avio_r8(pb); d= avio_r8(pb); rsize+=3; - } else if (!pb->eof_reached) { + }else if(!url_feof(pb)){ avio_seek(pb, -1, SEEK_CUR); //FIXME } @@ -871,11 +877,11 @@ static int ff_asf_get_packet(AVFormatContext *s, AVIOContext *pb) //the following checks prevent overflows and infinite loops if(!packet_length || packet_length >= (1U<<29)){ av_log(s, AV_LOG_ERROR, "invalid packet_length %d at:%"PRId64"\n", packet_length, avio_tell(pb)); - return -1; + return AVERROR_INVALIDDATA; } if(padsize >= packet_length){ av_log(s, AV_LOG_ERROR, "invalid padsize %d at:%"PRId64"\n", padsize, avio_tell(pb)); - return -1; + return AVERROR_INVALIDDATA; } asf->packet_timestamp = avio_rl32(pb); @@ -894,7 +900,7 @@ static int ff_asf_get_packet(AVFormatContext *s, AVIOContext *pb) av_log(s, AV_LOG_ERROR, "invalid packet header length %d for pktlen %d-%d at %"PRId64"\n", rsize, packet_length, padsize, avio_tell(pb)); - return -1; + return AVERROR_INVALIDDATA; } asf->packet_size_left = packet_length - padsize - rsize; if (packet_length < asf->hdr.min_pktsize) @@ -912,7 +918,7 @@ static int asf_read_frame_header(AVFormatContext *s, AVIOContext *pb){ ASFContext *asf = s->priv_data; int rsize = 1; int num = avio_r8(pb); - int64_t ts0; + int64_t ts0, ts1 av_unused; asf->packet_segments--; asf->packet_key_frame = num >> 7; @@ -924,17 +930,21 @@ 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+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){ av_log(s, AV_LOG_ERROR, "packet_obj_size invalid\n"); - return -1; + 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);; + ts1= avio_rl64(pb); avio_skip(pb, 12); avio_rl32(pb); avio_skip(pb, asf->packet_replic_size - 8 - 38 - 4); @@ -953,17 +963,17 @@ static int asf_read_frame_header(AVFormatContext *s, AVIOContext *pb){ rsize++; }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; @@ -971,16 +981,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; @@ -1002,8 +1008,7 @@ static int ff_asf_parse_packet(AVFormatContext *s, AVIOContext *pb, AVPacket *pk ASFStream *asf_st = 0; for (;;) { int ret; - - if (pb->eof_reached) + if (url_feof(pb)) return AVERROR_EOF; if (asf->packet_size_left < FRAME_HEADER_SIZE || @@ -1271,11 +1276,12 @@ static int64_t asf_read_pts(AVFormatContext *s, int stream_index, int64_t *ppos, if (s->packet_size > 0) pos= (pos+s->packet_size-1-s->data_offset)/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; 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; } @@ -1308,17 +1314,21 @@ static void asf_build_simple_index(AVFormatContext *s, int stream_index) ff_asf_guid g; ASFContext *asf = s->priv_data; int64_t current_pos= avio_tell(s->pb); - int i; - avio_seek(s->pb, asf->data_object_offset + asf->data_object_size, SEEK_SET); + if(avio_seek(s->pb, asf->data_object_offset + asf->data_object_size, SEEK_SET) < 0) { + asf->index_read= -1; + return; + } + ff_get_guid(s->pb, &g); /* the data object can be followed by other top-level objects, skip them until the simple index object is reached */ - while (ff_guidcmp(&g, &index_guid)) { + while (ff_guidcmp(&g, &ff_asf_simple_index_header)) { int64_t gsize= avio_rl64(s->pb); - if (gsize < 24 || s->pb->eof_reached) { + if (gsize < 24 || url_feof(s->pb)) { avio_seek(s->pb, current_pos, SEEK_SET); + asf->index_read= -1; return; } avio_skip(s->pb, gsize-24); @@ -1328,6 +1338,7 @@ static void asf_build_simple_index(AVFormatContext *s, int stream_index) { int64_t itime, last_pos=-1; int pct, ict; + int i; int64_t av_unused gsize= avio_rl64(s->pb); ff_get_guid(s->pb, &g); itime=avio_rl64(s->pb); @@ -1347,7 +1358,7 @@ static void asf_build_simple_index(AVFormatContext *s, int stream_index) last_pos=pos; } } - asf->index_read= ict > 0; + asf->index_read= ict > 1; } avio_seek(s->pb, current_pos, SEEK_SET); } @@ -1356,8 +1367,6 @@ static int asf_read_seek(AVFormatContext *s, int stream_index, int64_t pts, int { ASFContext *asf = s->priv_data; AVStream *st = s->streams[stream_index]; - int64_t pos; - int index; if (s->packet_size <= 0) return -1; @@ -1374,15 +1383,16 @@ static int asf_read_seek(AVFormatContext *s, int stream_index, int64_t pts, int if (!asf->index_read) asf_build_simple_index(s, stream_index); - if((asf->index_read && st->index_entries)){ - index= av_index_search_timestamp(st, pts, flags); + if((asf->index_read > 0 && st->index_entries)){ + int index= av_index_search_timestamp(st, pts, flags); if(index >= 0) { /* find the position */ - pos = st->index_entries[index].pos; + uint64_t pos = st->index_entries[index].pos; /* do the seek */ av_log(s, AV_LOG_DEBUG, "SEEKTO: %"PRId64"\n", pos); - avio_seek(s->pb, pos, SEEK_SET); + if(avio_seek(s->pb, pos, SEEK_SET) < 0) + return -1; asf_reset_header(s); return 0; } diff --git a/libavformat/asfenc.c b/libavformat/asfenc.c index 5c820becb2..82681d24f1 100644 --- a/libavformat/asfenc.c +++ b/libavformat/asfenc.c @@ -2,20 +2,20 @@ * 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 "avformat.h" @@ -30,7 +30,7 @@ #define ASF_INDEXED_INTERVAL 10000000 -#define ASF_INDEX_BLOCK 600 +#define ASF_INDEX_BLOCK (1<<9) #define ASF_PACKET_ERROR_CORRECTION_DATA_SIZE 0x2 #define ASF_PACKET_ERROR_CORRECTION_FLAGS (\ @@ -200,31 +200,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; + int next_start_sec; + int end_sec; } ASFContext; static const AVCodecTag codec_asf_bmp_tags[] = { - { AV_CODEC_ID_MPEG4, MKTAG('M', 'P', '4', 'S') }, { AV_CODEC_ID_MPEG4, MKTAG('M', '4', 'S', '2') }, + { AV_CODEC_ID_MPEG4, MKTAG('M', 'P', '4', 'S') }, { AV_CODEC_ID_MSMPEG4V3, MKTAG('M', 'P', '4', '3') }, { AV_CODEC_ID_NONE, 0 }, }; #define PREROLL_TIME 3100 -static void put_guid(AVIOContext *s, const ff_asf_guid *g) +void ff_put_guid(AVIOContext *s, const ff_asf_guid *g) { assert(sizeof(*g) == 16); avio_write(s, *g, sizeof(*g)); @@ -250,7 +252,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; } @@ -331,7 +333,7 @@ static int asf_write_header1(AVFormatContext *s, int64_t file_size, int64_t data 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); /* ??? */ @@ -340,7 +342,7 @@ static int asf_write_header1(AVFormatContext *s, int64_t file_size, int64_t data /* 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)); @@ -356,7 +358,7 @@ static int asf_write_header1(AVFormatContext *s, int64_t file_size, int64_t data /* unknown headers */ hpos = put_header(pb, &ff_asf_head1_guid); - put_guid(pb, &ff_asf_head2_guid); + ff_put_guid(pb, &ff_asf_head2_guid); avio_wl32(pb, 6); avio_wl16(pb, 0); end_header(pb, hpos); @@ -400,7 +402,7 @@ static int asf_write_header1(AVFormatContext *s, int64_t file_size, int64_t data 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) { @@ -419,11 +421,11 @@ static int asf_write_header1(AVFormatContext *s, int64_t file_size, int64_t data 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); @@ -470,7 +472,7 @@ static int asf_write_header1(AVFormatContext *s, int64_t file_size, int64_t data /* media comments */ hpos = put_header(pb, &ff_asf_codec_comment_header); - put_guid(pb, &ff_asf_codec_comment1_header); + ff_put_guid(pb, &ff_asf_codec_comment1_header); avio_wl32(pb, s->nb_streams); for(n=0;n<s->nb_streams;n++) { AVCodec *p; @@ -541,9 +543,9 @@ static int asf_write_header1(AVFormatContext *s, int64_t file_size, int64_t data /* 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); /* ??? */ @@ -557,10 +559,8 @@ static int asf_write_header(AVFormatContext *s) s->packet_size = PACKET_SIZE; asf->nb_packets = 0; - asf->last_indexed_pts = 0; asf->index_ptr = av_malloc( sizeof(ASFIndex) * ASF_INDEX_BLOCK ); asf->nb_index_memory_alloc = ASF_INDEX_BLOCK; - asf->nb_index_count = 0; asf->maximum_packet = 0; /* the data-chunk-size has to be 50, which is data_size - asf->data_offset @@ -579,6 +579,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; } @@ -681,7 +684,7 @@ static void flush_packet(AVFormatContext *s) static void put_payload_header( AVFormatContext *s, ASFStream *stream, - int presentation_time, + int64_t presentation_time, int m_obj_size, int m_obj_offset, int payload_len, @@ -708,7 +711,7 @@ static void put_payload_header( 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 @@ -719,7 +722,7 @@ static void put_frame( AVFormatContext *s, ASFStream *stream, AVStream *avst, - int timestamp, + int64_t timestamp, const uint8_t *buf, int m_obj_size, int flags @@ -782,14 +785,42 @@ static void put_frame( stream->seq++; } +static void update_index(AVFormatContext *s, int start_sec, + uint32_t packet_number, uint16_t packet_count) +{ + 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; + } + + if (start_sec > asf->nb_index_memory_alloc) { + asf->nb_index_memory_alloc = (start_sec + ASF_INDEX_BLOCK) & ~(ASF_INDEX_BLOCK - 1); + asf->index_ptr = av_realloc( asf->index_ptr, sizeof(ASFIndex) * asf->nb_index_memory_alloc ); + } + 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->maximum_packet = FFMAX(asf->maximum_packet, packet_count); + asf->next_packet_number = packet_number; + asf->next_packet_count = packet_count; + asf->next_start_sec = start_sec; +} + static int asf_write_packet(AVFormatContext *s, AVPacket *pkt) { ASFContext *asf = s->priv_data; 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; codec = s->streams[pkt->stream_index]->codec; @@ -800,30 +831,22 @@ static int asf_write_packet(AVFormatContext *s, AVPacket *pkt) 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); + 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) { - asf->nb_index_memory_alloc += ASF_INDEX_BLOCK; - asf->index_ptr = (ASFIndex*)av_realloc( asf->index_ptr, sizeof(ASFIndex) * asf->nb_index_memory_alloc ); - } - // 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->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; + update_index(s, start_sec, packet_number, packet_count); } + asf->end_sec = start_sec; + return 0; } @@ -833,9 +856,9 @@ static int asf_write_index(AVFormatContext *s, ASFIndex *index, uint16_t max, ui 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); @@ -858,8 +881,9 @@ 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) { + update_index(s, asf->end_sec + 1, 0, 0); + asf_write_index(s, asf->index_ptr, asf->maximum_packet, asf->next_start_sec); } avio_flush(s->pb); @@ -883,7 +907,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, @@ -902,7 +926,7 @@ AVOutputFormat ff_asf_stream_muxer = { .mime_type = "video/x-ms-asf", .extensions = "asf,wmv,wma", .priv_data_size = sizeof(ASFContext), - .audio_codec = CONFIG_LIBMP3LAME ? AV_CODEC_ID_MP3 : AV_CODEC_ID_MP2, + .audio_codec = AV_CODEC_ID_WMAV2, .video_codec = AV_CODEC_ID_MSMPEG4V3, .write_header = asf_write_stream_header, .write_packet = asf_write_packet, diff --git a/libavformat/assdec.c b/libavformat/assdec.c index 0041ca4cc3..d7a29bfdc6 100644 --- a/libavformat/assdec.c +++ b/libavformat/assdec.c @@ -2,20 +2,20 @@ * 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 */ @@ -94,7 +94,7 @@ static int read_header(AVFormatContext *s) header_remaining= INT_MAX; dst[0] = &st->codec->extradata; dst[1] = &ass->event_buffer; - while(!pb->eof_reached){ + while(!url_feof(pb)){ uint8_t line[MAX_LINESIZE]; len = ff_get_line(pb, line, sizeof(line)); @@ -148,7 +148,7 @@ static int read_packet(AVFormatContext *s, AVPacket *pkt) uint8_t *p, *end; if(ass->event_index >= ass->event_count) - return AVERROR(EIO); + return AVERROR_EOF; p= ass->event[ ass->event_index ]; diff --git a/libavformat/assenc.c b/libavformat/assenc.c index 5bf2e20134..6b98886c70 100644 --- a/libavformat/assenc.c +++ b/libavformat/assenc.c @@ -2,20 +2,20 @@ * 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 */ diff --git a/libavformat/au.c b/libavformat/au.c index e09d7ac5cd..788e261e70 100644 --- a/libavformat/au.c +++ b/libavformat/au.c @@ -2,20 +2,20 @@ * AU muxer and 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 */ @@ -120,7 +120,7 @@ static int au_probe(AVProbeData *p) /* au input */ static int au_read_header(AVFormatContext *s) { - int size; + int size, bps, data_size = 0; unsigned int tag; AVIOContext *pb = s->pb; unsigned int id, channels, rate; @@ -132,7 +132,12 @@ static int au_read_header(AVFormatContext *s) if (tag != MKTAG('.', 's', 'n', 'd')) return -1; 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); @@ -140,7 +145,7 @@ static int au_read_header(AVFormatContext *s) codec = ff_codec_get_id(codec_au_tags, id); - if (!av_get_bits_per_sample(codec)) { + if (!(bps = av_get_bits_per_sample(codec))) { av_log_ask_for_sample(s, "could not determine bits per sample\n"); return AVERROR_INVALIDDATA; } @@ -164,6 +169,8 @@ static int au_read_header(AVFormatContext *s) st->codec->codec_id = codec; st->codec->channels = channels; st->codec->sample_rate = rate; + if (data_size != AU_UNKNOWN_SIZE) + st->duration = (((int64_t)data_size)<<3) / (st->codec->channels * (int64_t)bps); avpriv_set_pts_info(st, 64, 1, rate); return 0; } @@ -174,17 +181,17 @@ static int au_read_packet(AVFormatContext *s, AVPacket *pkt) { int ret; + int bpcs = av_get_bits_per_sample(s->streams[0]->codec->codec_id); + if (!bpcs) + return AVERROR(EINVAL); ret= av_get_packet(s->pb, pkt, BLOCK_SIZE * s->streams[0]->codec->channels * - av_get_bits_per_sample(s->streams[0]->codec->codec_id) >> 3); + bpcs >> 3); 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/audiointerleave.c b/libavformat/audiointerleave.c index e48f826e14..20323a2c58 100644 --- a/libavformat/audiointerleave.c +++ b/libavformat/audiointerleave.c @@ -3,20 +3,20 @@ * * Copyright (c) 2009 Baptiste Coudurier <baptiste dot coudurier at gmail dot com> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -47,6 +47,10 @@ int ff_audio_interleave_init(AVFormatContext *s, if (!samples_per_frame) return -1; + if (!time_base.num) { + av_log(s, AV_LOG_ERROR, "timebase not set for audio interleave\n"); + return -1; + } for (i = 0; i < s->nb_streams; i++) { AVStream *st = s->streams[i]; AudioInterleaveContext *aic = st->priv_data; @@ -80,7 +84,8 @@ static int ff_interleave_new_audio_packet(AVFormatContext *s, AVPacket *pkt, if (!size || (!flush && size == av_fifo_size(aic->fifo))) return 0; - av_new_packet(pkt, size); + if (av_new_packet(pkt, size) < 0) + return AVERROR(ENOMEM); av_fifo_generic_read(aic->fifo, pkt->data, size, NULL); pkt->dts = pkt->pts = aic->dts; @@ -113,10 +118,13 @@ int ff_audio_rechunk_interleave(AVFormatContext *s, AVPacket *out, AVPacket *pkt } av_fifo_generic_write(aic->fifo, pkt->data, pkt->size, NULL); } else { + int ret; // rewrite pts and dts to be decoded time line position pkt->pts = pkt->dts = aic->dts; aic->dts += pkt->duration; - ff_interleave_add_packet(s, pkt, compare_ts); + ret = ff_interleave_add_packet(s, pkt, compare_ts); + if (ret < 0) + return ret; } pkt = NULL; } @@ -125,8 +133,14 @@ int ff_audio_rechunk_interleave(AVFormatContext *s, AVPacket *out, AVPacket *pkt AVStream *st = s->streams[i]; if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) { AVPacket new_pkt; - while (ff_interleave_new_audio_packet(s, &new_pkt, i, flush)) - ff_interleave_add_packet(s, &new_pkt, compare_ts); + int ret; + while ((ret = ff_interleave_new_audio_packet(s, &new_pkt, i, flush)) > 0) { + ret = ff_interleave_add_packet(s, &new_pkt, compare_ts); + if (ret < 0) + return ret; + } + if (ret < 0) + return ret; } } diff --git a/libavformat/audiointerleave.h b/libavformat/audiointerleave.h index 9c7b548c1d..4d77832fa1 100644 --- a/libavformat/audiointerleave.h +++ b/libavformat/audiointerleave.h @@ -3,20 +3,20 @@ * * Copyright (c) 2009 Baptiste Coudurier <baptiste dot coudurier at gmail dot com> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/avc.c b/libavformat/avc.c index 2fd5ac807b..f5c513bb31 100644 --- a/libavformat/avc.c +++ b/libavformat/avc.c @@ -2,20 +2,20 @@ * AVC helper functions for muxers * Copyright (c) 2006 Baptiste Coudurier <baptiste.coudurier@smartjog.com> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/avc.h b/libavformat/avc.h index 579756ec0f..972e19b94a 100644 --- a/libavformat/avc.h +++ b/libavformat/avc.h @@ -2,20 +2,20 @@ * AVC helper functions for muxers * Copyright (c) 2008 Aurelien Jacobs <aurel@gnuage.org> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/avformat.h b/libavformat/avformat.h index 8af007e640..3d99518990 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). @@ -218,7 +218,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. * @@ -336,6 +336,7 @@ typedef struct AVProbeData { } AVProbeData; #define AVPROBE_SCORE_MAX 100 ///< maximum score, half of that is used for file-extension-based detection +#define AVPROBE_SCORE_RETRY (AVPROBE_SCORE_MAX/4) #define AVPROBE_PADDING_SIZE 32 ///< extra allocated bytes at the end of the probe buffer /// Demuxer will use avio_open, no opened file should be provided by the caller. @@ -355,10 +356,17 @@ typedef struct AVProbeData { #define AVFMT_NOGENSEARCH 0x4000 /**< Format does not allow to fallback to 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_SEEK_TO_PTS 0x4000000 /**< Seeking is based on PTS */ + /** * @addtogroup lavf_encoding * @{ @@ -427,8 +435,12 @@ typedef struct AVOutputFormat { * * @return 1 if the codec is supported, 0 if it is not. * A negative number if unknown. + * MKTAG('A', 'P', 'I', 'C') if the codec is only supported as AV_DISPOSITION_ATTACHED_PIC */ int (*query_codec)(enum AVCodecID id, int std_compliance); + + void (*get_output_timestamp)(struct AVFormatContext *s, int stream, + int64_t *dts, int64_t *wall); } AVOutputFormat; /** * @} @@ -455,7 +467,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; @@ -568,11 +580,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). @@ -660,10 +680,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; @@ -714,15 +736,14 @@ 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 { -#if FF_API_R_FRAME_RATE int64_t last_dts; int64_t duration_gcd; int duration_count; - double duration_error[MAX_STD_TIMEBASES]; -#endif - int nb_decoded_frames; + double duration_error[2][2][MAX_STD_TIMEBASES]; + int64_t codec_info_duration; + int64_t codec_info_duration_fields; int found_decoder; /** @@ -762,6 +783,16 @@ typedef struct AVStream { */ int codec_info_nb_frames; + /** + * 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; + /* av_read_frame() support */ enum AVStreamParseType need_parsing; struct AVCodecParserContext *parser; @@ -778,6 +809,38 @@ typedef struct AVStream { support seeking natively. */ int nb_index_entries; unsigned int index_entries_allocated_size; + + /** + * 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 its 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; + } AVStream; #define AV_PROGRAM_RUNNING 1 @@ -795,6 +858,20 @@ 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; } AVProgram; #define AVFMTCTX_NOHEADER 0x0001 /**< signal that no header is present @@ -807,6 +884,17 @@ typedef struct AVChapter { AVDictionary *metadata; } AVChapter; + +/** + * The duration of a video can be estimated through various ways, and this enum can be used + * to know how the duration was estimated. + */ +enum AVDurationEstimationMethod { + AVFMT_DURATION_FROM_PTS, ///< Duration accurately estimated from PTSes + AVFMT_DURATION_FROM_STREAM, ///< Duration estimated from a stream with a known duration + AVFMT_DURATION_FROM_BITRATE ///< Duration estimated from bitrate (less accurate) +}; + /** * Format I/O context. * New fields can be added to the end with minor version bumps. @@ -885,7 +973,7 @@ typedef struct AVFormatContext { /** * Decoding: total stream bitrate in bit/s, 0 if not * available. Never set it directly if the file_size and the - * duration are known as Libav can compute it automatically. + * duration are known as FFmpeg can compute it automatically. */ int bit_rate; @@ -902,6 +990,10 @@ typedef struct AVFormatContext { #define AVFMT_FLAG_NOBUFFER 0x0040 ///< Do not buffer frames when possible #define AVFMT_FLAG_CUSTOM_IO 0x0080 ///< The caller has supplied a custom AVIOContext, don't avio_close() it. #define AVFMT_FLAG_DISCARD_CORRUPT 0x0100 ///< Discard frames marked corrupted +#define AVFMT_FLAG_MP4A_LATM 0x8000 ///< Enable RTP MP4A-LATM payload +#define AVFMT_FLAG_SORT_DTS 0x10000 ///< try to interleave outputted packets by dts (using this flag can slow demuxing down) +#define AVFMT_FLAG_PRIV_OPT 0x20000 ///< Enable use of private options by delaying codec open (this could be made default once all code is converted) +#define AVFMT_FLAG_KEEP_SIDE_DATA 0x40000 ///< Don't merge side data but keep it separate. /** * decoding: size of data to probe; encoding: unused. @@ -999,6 +1091,71 @@ typedef struct AVFormatContext { */ int debug; #define FF_FDEBUG_TS 0x0001 + + /** + * Transport stream id. + * This will be moved into demuxer private options. Thus no API/ABI compatibility + */ + int ts_id; + + /** + * Audio preload in microseconds. + * Note, not all formats support this and unpredictable things may happen if it is used when not supported. + * - encoding: Set by user via AVOptions (NO direct access) + * - decoding: unused + */ + int audio_preload; + + /** + * Max chunk time in microseconds. + * Note, not all formats support this and unpredictable things may happen if it is used when not supported. + * - encoding: Set by user via AVOptions (NO direct access) + * - decoding: unused + */ + int max_chunk_duration; + + /** + * Max chunk size in bytes + * Note, not all formats support this and unpredictable things may happen if it is used when not supported. + * - encoding: Set by user via AVOptions (NO direct access) + * - decoding: unused + */ + int max_chunk_size; + + /** + * forces the use of wallclock timestamps as pts/dts of packets + * This has undefined results in the presence of B frames. + * - encoding: unused + * - decoding: Set by user via AVOptions (NO direct access) + */ + int use_wallclock_as_timestamps; + + /** + * Avoids negative timestamps during muxing + * 0 -> allow negative timestamps + * 1 -> avoid negative timestamps + * -1 -> choose automatically (default) + * Note, this is 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; + /***************************************************************** * 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 @@ -1038,6 +1195,13 @@ typedef struct AVFormatContext { int raw_packet_buffer_remaining_size; } AVFormatContext; +/** + * 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; @@ -1147,7 +1311,7 @@ const AVClass *avformat_get_class(void); * * @return newly created stream or NULL on error. */ -AVStream *avformat_new_stream(AVFormatContext *s, AVCodec *c); +AVStream *avformat_new_stream(AVFormatContext *s, const AVCodec *c); AVProgram *av_new_program(AVFormatContext *s, int id); @@ -1156,6 +1320,41 @@ AVProgram *av_new_program(AVFormatContext *s, int id); */ +#if FF_API_PKT_DUMP +attribute_deprecated void av_pkt_dump(FILE *f, AVPacket *pkt, int dump_payload); +attribute_deprecated void av_pkt_dump_log(void *avcl, int level, AVPacket *pkt, + int dump_payload); +#endif + +#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 * @{ @@ -1188,6 +1387,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 @@ -1227,6 +1435,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 @@ -1251,6 +1482,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. @@ -1400,6 +1643,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 @@ -1517,6 +1784,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 dts[out] DTS of the last packet output for the stream, in stream + * time_base units + * @param wall[out] 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); + + +/** * @} */ @@ -1526,7 +1812,7 @@ enum AVCodecID av_guess_codec(AVOutputFormat *fmt, const char *short_name, * @ingroup libavf * @{ * - * Miscelaneous utility functions related to both muxing and demuxing + * Miscellaneous utility functions related to both muxing and demuxing * (or neither). */ @@ -1729,11 +2015,50 @@ const struct AVCodecTag *avformat_get_riff_video_tags(void); * @return the table mapping RIFF FourCCs for audio to AVCodecID. */ const struct AVCodecTag *avformat_get_riff_audio_tags(void); + /** * @} */ /** + * Guess the sample aspect ratio of a frame, based on both the stream and the + * frame aspect ratio. + * + * Since the frame aspect ratio is set by the codec but the stream aspect ratio + * is set by the demuxer, these two may not be equal. This function tries to + * return the value that you should use if you would like to display the frame. + * + * Basic logic is to use the stream aspect ratio if it is set to something sane + * otherwise use the frame aspect ratio. This way a container setting, which is + * usually easy to modify can override the coded value in the frames. + * + * @param format the format context which the stream is part of + * @param stream the stream which the frame is part of + * @param frame the frame with the aspect ratio to be determined + * @return the guessed (valid) sample_aspect_ratio, 0/1 if no idea + */ +AVRational av_guess_sample_aspect_ratio(AVFormatContext *format, AVStream *stream, AVFrame *frame); + +/** + * 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); + +void avformat_queue_attached_pictures(AVFormatContext *s); + + +/** * @} */ diff --git a/libavformat/avi.h b/libavformat/avi.h index e05db9c63f..34da76f715 100644 --- a/libavformat/avi.h +++ b/libavformat/avi.h @@ -1,20 +1,20 @@ /* * copyright (c) 2001 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/avidec.c b/libavformat/avidec.c index a3af5cf2de..0314b4d188 100644 --- a/libavformat/avidec.c +++ b/libavformat/avidec.c @@ -2,37 +2,36 @@ * AVI demuxer * Copyright (c) 2001 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "libavutil/intreadwrite.h" #include "libavutil/mathematics.h" #include "libavutil/bswap.h" +#include "libavutil/opt.h" #include "libavutil/dict.h" #include "libavutil/avstring.h" +#include "libavutil/avassert.h" #include "avformat.h" #include "internal.h" #include "avi.h" #include "dv.h" #include "riff.h" -#undef NDEBUG -#include <assert.h> - typedef struct AVIStream { int64_t frame_offset; /* current frame (video) or byte (audio) counter (used to compute the pts) */ @@ -54,9 +53,12 @@ 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; @@ -68,9 +70,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' }, @@ -143,7 +162,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 chunk_id:%X base:%16"PRIX64"\n", longs_pre_entry,index_type, entries_in_use, chunk_id, base); @@ -178,9 +197,10 @@ static int read_braindead_odml_indx(AVFormatContext *s, int frame_num){ int key= len >= 0; len &= 0x7FFFFFFF; - av_dlog(s, "pos:%"PRId64", len:%X\n", pos, len); - - if(pb->eof_reached) +#ifdef DEBUG_SEEK + av_log(s, AV_LOG_ERROR, "pos:%"PRId64", len:%X\n", pos, len); +#endif + if(url_feof(pb)) return -1; if(last_pos == pos || pos == base - 8) @@ -197,7 +217,7 @@ static int read_braindead_odml_indx(AVFormatContext *s, int frame_num){ avio_rl32(pb); /* size */ duration = avio_rl32(pb); - if(pb->eof_reached) + if(url_feof(pb)) return -1; pos = avio_tell(pb); @@ -207,16 +227,21 @@ static int read_braindead_odml_indx(AVFormatContext *s, int frame_num){ return -1; } - 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; } @@ -345,8 +370,10 @@ static int avi_read_header(AVFormatContext *s) if (get_riff(s, pb) < 0) return -1; + av_log(avi, AV_LOG_DEBUG, "use odml:%d\n", avi->use_odml); + avi->fsize = avio_size(pb); - if(avi->fsize<=0) + if(avi->fsize<=0 || avi->fsize < avi->riff_end) avi->fsize= avi->riff_end == 8 ? INT64_MAX : avi->riff_end; /* first list tag */ @@ -354,7 +381,7 @@ static int avi_read_header(AVFormatContext *s) codec_type = -1; frame_period = 0; for(;;) { - if (pb->eof_reached) + if (url_feof(pb)) goto fail; tag = avio_rl32(pb); size = avio_rl32(pb); @@ -372,7 +399,7 @@ static int avi_read_header(AVFormatContext *s) if (tag1 == MKTAG('m', 'o', 'v', 'i')) { avi->movi_list = avio_tell(pb) - 4; if(size) avi->movi_end = avi->movi_list + size + (size & 1); - else avi->movi_end = avio_size(pb); + else avi->movi_end = avi->fsize; av_dlog(NULL, "movi end=%"PRIx64"\n", avi->movi_end); goto end_of_header; } @@ -400,7 +427,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; @@ -456,6 +483,7 @@ 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); + av_freep(&s->streams[0]->info); av_freep(&s->streams[0]); s->nb_streams = 0; if (CONFIG_DV_DEMUXER) { @@ -484,7 +512,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 */ @@ -511,6 +539,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", @@ -532,8 +564,7 @@ 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) st->duration = st->nb_frames; @@ -542,10 +573,13 @@ static int avi_read_header(AVFormatContext *s) 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]; @@ -559,7 +593,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')) { st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE; @@ -568,8 +602,9 @@ static int avi_read_header(AVFormatContext *s) break; } - if(size > 10*4 && size<(1<<30)){ - st->codec->extradata_size= size - 10*4; + 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; st->codec->extradata= av_malloc(st->codec->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE); if (!st->codec->extradata) { st->codec->extradata_size= 0; @@ -583,19 +618,15 @@ 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. */ + /* This is true for all paletted codecs implemented in FFmpeg. */ if (st->codec->extradata_size && (st->codec->bits_per_coded_sample <= 8)) { int pal_size = (1 << st->codec->bits_per_coded_sample) << 2; const uint8_t *pal_src; 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; } @@ -605,15 +636,10 @@ static int avi_read_header(AVFormatContext *s) st->codec->codec_tag = tag1; st->codec->codec_id = ff_codec_get_id(ff_codec_bmp_tags, tag1); st->need_parsing = AVSTREAM_PARSE_HEADERS; // This is needed to get the pict type which is necessary for generating correct pts. - // Support "Resolution 1:1" for Avid AVI Codec - if(tag1 == MKTAG('A', 'V', 'R', 'n') && - st->codec->extradata_size >= 31 && - !memcmp(&st->codec->extradata[28], "1:1", 3)) - st->codec->codec_id = AV_CODEC_ID_RAWVIDEO; if(st->codec->codec_tag==0 && st->codec->height > 0 && st->codec->extradata_size < 1U<<30){ st->codec->extradata_size+= 9; - st->codec->extradata= av_realloc(st->codec->extradata, st->codec->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE); + st->codec->extradata= av_realloc_f(st->codec->extradata, 1, st->codec->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE); if(st->codec->extradata) memcpy(st->codec->extradata + st->codec->extradata_size - 9, "BottomUp", 9); } @@ -645,15 +671,26 @@ 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; break; default: st->codec->codec_type = AVMEDIA_TYPE_DATA; @@ -664,11 +701,33 @@ 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) { + 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)){ + st->codec->extradata_size= size; + 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_read(pb, st->codec->extradata, st->codec->extradata_size); + } + + if(st->codec->extradata_size & 1) //FIXME check if the encoder really did this correctly + avio_r8(pb); + } + break; case MKTAG('i', 'n', 'd', 'x'): i= avio_tell(pb); - if(pb->seekable && !(s->flags & AVFMT_FLAG_IGNIDX) && - read_braindead_odml_indx(s, 0) < 0 && - (s->error_recognition & AV_EF_EXPLODE)) + 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; avio_seek(pb, i+size, SEEK_SET); break; @@ -711,7 +770,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 */ @@ -729,13 +788,17 @@ static int avi_read_header(AVFormatContext *s) if(!avi->index_loaded && pb->seekable) avi_load_index(s); - avi->index_loaded = 1; - avi->non_interleaved |= guess_ni_flag(s); + avi->index_loaded |= 1; + avi->non_interleaved |= guess_ni_flag(s) | (s->flags & AVFMT_FLAG_SORT_DTS); 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"); avi->non_interleaved=0; @@ -842,6 +905,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; @@ -853,7 +920,7 @@ static int avi_sync(AVFormatContext *s, int exit_early) start_sync: memset(d, -1, sizeof(d)); - for(i=sync=avio_tell(pb); !pb->eof_reached; i++) { + for(i=sync=avio_tell(pb); !url_feof(pb); i++) { int j; for(j=0; j<7; j++) @@ -901,6 +968,11 @@ start_sync: st = s->streams[n]; ast = st->priv_data; + if (!ast) { + av_log(s, AV_LOG_WARNING, "Skiping foreign stream %d packet\n", n); + continue; + } + if(s->nb_streams>=2){ AVStream *st1 = s->streams[1]; AVIStream *ast1= st1->priv_data; @@ -937,7 +1009,7 @@ start_sync: avio_rl16(pb); //flags for (; k <= last; k++) - ast->pal[k] = avio_rb32(pb)>>8;// b + (g << 8) + (r << 16); + ast->pal[k] = 0xFFU<<24 | avio_rb32(pb)>>8;// b + (g << 8) + (r << 16); ast->has_pal= 1; goto start_sync; } else if( ((ast->prefix_count<5 || sync+9 > i) && d[2]<128 && d[3]<128) || @@ -969,6 +1041,8 @@ start_sync: } } + if(pb->error) + return pb->error; return AVERROR_EOF; } @@ -1016,10 +1090,10 @@ static int avi_read_packet(AVFormatContext *s, AVPacket *pkt) } } if(!best_st) - return -1; + 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, AVSEEK_FLAG_ANY | AVSEEK_FLAG_BACKWARD); else{ @@ -1031,15 +1105,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: @@ -1065,8 +1142,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, AVPALETTE_SIZE); if(!pal){ @@ -1080,7 +1158,7 @@ resync: if (CONFIG_DV_DEMUXER && avi->dv_demux) { dstr = pkt->destruct; size = avpriv_dv_produce_packet(avi->dv_demux, pkt, - pkt->data, pkt->size); + pkt->data, pkt->size, pkt->pos); pkt->destruct = dstr; pkt->flags |= AV_PKT_FLAG_KEY; if (size < 0) @@ -1105,12 +1183,29 @@ resync: if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) { AVIndexEntry *e; int index; - assert(st->index_entries); + av_assert0(st->index_entries); index= av_index_search_timestamp(st, ast->frame_offset, 0); e= &st->index_entries[index]; if(index >= 0 && e->timestamp == ast->frame_offset){ + if (index == 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; } @@ -1125,6 +1220,22 @@ resync: 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; } @@ -1144,7 +1255,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) @@ -1160,6 +1273,9 @@ static int avi_read_idx1(AVFormatContext *s, int size) /* Read the entries and sort them in each stream component. */ for(i = 0; i < nb_index_entries; i++) { + if(url_feof(pb)) + return -1; + tag = avio_rl32(pb); flags = avio_rl32(pb); pos = avio_rl32(pb); @@ -1182,15 +1298,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 -1; + // 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; } @@ -1200,6 +1326,8 @@ static int guess_ni_flag(AVFormatContext *s){ int64_t last_start=0; int64_t first_end= INT64_MAX; int64_t oldpos= avio_tell(s->pb); + int *idx; + int64_t min_pos, pos; for(i=0; i<s->nb_streams; i++){ AVStream *st = s->streams[i]; @@ -1223,7 +1351,32 @@ static int guess_ni_flag(AVFormatContext *s){ first_end= st->index_entries[n-1].pos; } avio_seek(s->pb, oldpos, SEEK_SET); - return last_start > first_end; + if (last_start > first_end) + return 1; + idx= av_mallocz(sizeof(*idx) * s->nb_streams); + for (min_pos=pos=0; min_pos!=INT64_MAX; pos= min_pos+1LU) { + int64_t max_dts = INT64_MIN/2, min_dts= INT64_MAX/2; + min_pos = INT64_MAX; + + for (i=0; i<s->nb_streams; i++) { + AVStream *st = s->streams[i]; + int n= st->nb_index_entries; + while (idx[i]<n && st->index_entries[idx[i]].pos < pos) + idx[i]++; + if (idx[i] < n) { + min_dts = FFMIN(min_dts, av_rescale_q(st->index_entries[idx[i]].timestamp, st->time_base, AV_TIME_BASE_Q)); + min_pos = FFMIN(min_pos, st->index_entries[idx[i]].pos); + } + if (idx[i]) + max_dts = FFMAX(max_dts, av_rescale_q(st->index_entries[idx[i]-1].timestamp, st->time_base, AV_TIME_BASE_Q)); + } + if(max_dts - min_dts > 2*AV_TIME_BASE) { + av_free(idx); + return 1; + } + } + av_free(idx); + return 0; } static int avi_load_index(AVFormatContext *s) @@ -1232,16 +1385,19 @@ static int avi_load_index(AVFormatContext *s) AVIOContext *pb = s->pb; uint32_t tag, size; int64_t pos= avio_tell(pb); + int64_t next; int ret = -1; if (avio_seek(pb, avi->movi_end, SEEK_SET) < 0) goto the_end; // maybe truncated file av_dlog(s, "movi_end=0x%"PRIx64"\n", avi->movi_end); for(;;) { - if (pb->eof_reached) - break; tag = avio_rl32(pb); size = avio_rl32(pb); + if (url_feof(pb)) + break; + next = avio_tell(pb) + size + (size & 1); + av_dlog(s, "tag=%c%c%c%c size=0x%x\n", tag & 0xff, (tag >> 8) & 0xff, @@ -1251,12 +1407,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 } the_end: @@ -1279,21 +1440,27 @@ static int avi_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp AVIContext *avi = s->priv_data; AVStream *st; int i, index; - int64_t pos; + int64_t pos, pos_min; AVIStream *ast; if (!avi->index_loaded) { /* we only load the index on demand */ avi_load_index(s); - avi->index_loaded = 1; + avi->index_loaded |= 1; } - assert(stream_index>= 0); + 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 -1; + } /* find the position */ pos = st->index_entries[index].pos; @@ -1306,17 +1473,20 @@ static int avi_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp /* One and only one real stream for DV in AVI, and it has video */ /* offsets. Calling with other stream indexes should have failed */ /* the av_index_search_timestamp call above. */ - assert(stream_index == 0); + av_assert0(stream_index == 0); + + 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; @@ -1332,31 +1502,42 @@ static int avi_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp if (st2->nb_index_entries <= 0) continue; -// assert(st2->codec->block_align); - assert((int64_t)st2->time_base.num*ast2->rate == (int64_t)st2->time_base.den*ast2->scale); +// av_assert1(st2->codec->block_align); + av_assert0((int64_t)st2->time_base.num*ast2->rate == (int64_t)st2->time_base.den*ast2->scale); index = av_index_search_timestamp( st2, av_rescale_q(timestamp, st->time_base, st2->time_base) * FFMAX(ast2->sample_size, 1), - flags | AVSEEK_FLAG_BACKWARD); + flags | AVSEEK_FLAG_BACKWARD | (st2->codec->codec_type != AVMEDIA_TYPE_VIDEO ? AVSEEK_FLAG_ANY : 0)); if(index<0) index=0; + ast2->seek_pos= st2->index_entries[index].pos; + pos_min= FFMIN(pos_min,ast2->seek_pos); + } + for(i = 0; i < s->nb_streams; i++) { + AVStream *st2 = s->streams[i]; + AVIStream *ast2 = st2->priv_data; - if(!avi->non_interleaved){ - while(index>0 && st2->index_entries[index].pos > pos) - index--; - while(index+1 < st2->nb_index_entries && st2->index_entries[index].pos < pos) - index++; - } + if (ast2->sub_ctx || st2->nb_index_entries <= 0) + continue; - av_dlog(s, "%"PRId64" %d %"PRId64"\n", - timestamp, index, st2->index_entries[index].timestamp); - /* extract the current frame number */ + index = av_index_search_timestamp( + st2, + av_rescale_q(timestamp, st->time_base, st2->time_base) * FFMAX(ast2->sample_size, 1), + flags | AVSEEK_FLAG_BACKWARD | (st2->codec->codec_type != AVMEDIA_TYPE_VIDEO ? AVSEEK_FLAG_ANY : 0)); + if(index<0) + index=0; + while(!avi->non_interleaved && index>0 && st2->index_entries[index-1].pos >= pos_min) + index--; ast2->frame_offset = st2->index_entries[index].timestamp; } /* do the seek */ - avio_seek(s->pb, pos, SEEK_SET); + if (avio_seek(s->pb, pos_min, SEEK_SET) < 0) { + av_log(s, AV_LOG_ERROR, "Seek failed\n"); + return -1; + } avi->stream_index= -1; + avi->dts_max= INT_MIN; return 0; } @@ -1405,4 +1586,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 9d1f5102a1..fe75d26991 100644 --- a/libavformat/avienc.c +++ b/libavformat/avienc.c @@ -2,20 +2,20 @@ * 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 */ #include "avformat.h" @@ -25,6 +25,7 @@ #include "riff.h" #include "libavutil/intreadwrite.h" #include "libavutil/dict.h" +#include "libavutil/avassert.h" /* * TODO: @@ -117,7 +118,7 @@ static int avi_write_counters(AVFormatContext* s, int riff_id) for(n = 0; n < s->nb_streams; n++) { AVIStream *avist= s->streams[n]->priv_data; - assert(avist->frames_hdr_strm); + av_assert0(avist->frames_hdr_strm); stream = s->streams[n]->codec; avio_seek(pb, avist->frames_hdr_strm, SEEK_SET); ff_parse_specific_params(stream, &au_byterate, &au_ssize, &au_scale); @@ -130,7 +131,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); } @@ -242,9 +243,12 @@ static int avi_write_header(AVFormatContext *s) ff_parse_specific_params(stream, &au_byterate, &au_ssize, &au_scale); + avpriv_set_pts_info(s->streams[i], 64, au_scale, au_byterate); + if(stream->codec_id == AV_CODEC_ID_XSUB) + au_scale = au_byterate = 0; + avio_wl32(pb, au_scale); /* scale */ avio_wl32(pb, au_byterate); /* rate */ - avpriv_set_pts_info(s->streams[i], 64, au_scale, au_byterate); avio_wl32(pb, 0); /* start */ avist->frames_hdr_strm = avio_tell(pb); /* remember this offset to fill later */ @@ -388,7 +392,7 @@ 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; @@ -500,14 +504,21 @@ static int avi_write_packet(AVFormatContext *s, AVPacket *pkt) AVCodecContext *enc= s->streams[stream_index]->codec; int size= pkt->size; - while(enc->block_align==0 && pkt->dts != AV_NOPTS_VALUE && pkt->dts > avist->packet_count){ +// av_log(s, AV_LOG_DEBUG, "%"PRId64" %d %d\n", pkt->dts, avist->packet_count, stream_index); + while(enc->block_align==0 && pkt->dts != AV_NOPTS_VALUE && pkt->dts > avist->packet_count && enc->codec_id != AV_CODEC_ID_XSUB){ AVPacket empty_packet; + if(pkt->dts - avist->packet_count > 60000){ + av_log(s, AV_LOG_ERROR, "Too large number of skiped frames %"PRId64"\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_log(s, AV_LOG_DEBUG, "dup %"PRId64" %d\n", pkt->dts, avist->packet_count); } avist->packet_count++; @@ -537,7 +548,7 @@ static int avi_write_packet(AVFormatContext *s, AVPacket *pkt) int cl = idx->entry / AVI_INDEX_CLUSTER_SIZE; int id = idx->entry % AVI_INDEX_CLUSTER_SIZE; if (idx->ents_allocated <= idx->entry) { - idx->cluster = av_realloc(idx->cluster, (cl+1)*sizeof(void*)); + idx->cluster = av_realloc_f(idx->cluster, sizeof(void*), cl+1); if (!idx->cluster) return -1; idx->cluster[cl] = av_malloc(AVI_INDEX_CLUSTER_SIZE*sizeof(AVIIentry)); diff --git a/libavformat/avio.c b/libavformat/avio.c index a43b241399..6d8a8bb92a 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 */ @@ -134,8 +134,33 @@ static int url_alloc_for_protocol (URLContext **puc, struct URLProtocol *up, if (up->priv_data_size) { uc->priv_data = av_mallocz(up->priv_data_size); 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,11 +205,18 @@ int ffurl_alloc(URLContext **puc, const char *filename, int flags, char proto_str[128], proto_nested[128], *ptr; size_t proto_len = strspn(filename, URL_SCHEME_CHARS); - if (filename[proto_len] != ':' || is_dos_path(filename)) + if (!first_protocol) { + av_log(NULL, AV_LOG_WARNING, "No URL Protocols are registered. " + "Missing call to av_register_all()?\n"); + } + + if (filename[proto_len] != ':' && filename[proto_len] != ',' || 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'; @@ -197,7 +229,7 @@ int ffurl_alloc(URLContext **puc, const char *filename, int flags, return url_alloc_for_protocol (puc, up, filename, flags, int_cb); } *puc = NULL; - return AVERROR(ENOENT); + return AVERROR_PROTOCOL_NOT_FOUND; } int ffurl_open(URLContext **puc, const char *filename, int flags, @@ -223,6 +255,7 @@ static inline int retry_transfer_wrapper(URLContext *h, unsigned char *buf, int { int ret, len; int fast_retries = 5; + int64_t wait_since = 0; len = 0; while (len < size_min) { @@ -233,16 +266,23 @@ static inline int retry_transfer_wrapper(URLContext *h, unsigned char *buf, int return ret; if (ret == AVERROR(EAGAIN)) { ret = 0; - if (fast_retries) + if (fast_retries) { fast_retries--; - else + } else { + if (h->rw_timeout) { + if (!wait_since) + wait_since = av_gettime(); + else if (av_gettime() > wait_since + h->rw_timeout) + return AVERROR(EIO); + } av_usleep(1000); + } } else if (ret < 1) return ret < 0 ? ret : len; if (ret) fast_retries = FFMAX(fast_retries, 2); len += ret; - if (ff_check_interrupt(&h->interrupt_callback)) + if (len < size && ff_check_interrupt(&h->interrupt_callback)) return AVERROR_EXIT; } return len; @@ -270,7 +310,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) @@ -283,8 +323,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 */ @@ -297,12 +338,18 @@ int ffurl_close(URLContext *h) if (h->prot->priv_data_size) { if (h->prot->priv_data_class) av_opt_free(h->priv_data); - av_free(h->priv_data); + av_freep(&h->priv_data); } - av_free(h); + av_freep(hh); return ret; } +int ffurl_close(URLContext *h) +{ + return ffurl_closep(&h); +} + + int avio_check(const char *url, int flags) { URLContext *h; @@ -349,7 +396,7 @@ int ffurl_get_multi_file_handle(URLContext *h, int **handles, int *numhandles) if (!h->prot->url_get_multi_file_handle) { if (!h->prot->url_get_file_handle) return AVERROR(ENOSYS); - *handles = av_malloc(sizeof(*handles)); + *handles = av_malloc(sizeof(**handles)); if (!*handles) return AVERROR(ENOMEM); *numhandles = 1; diff --git a/libavformat/avio.h b/libavformat/avio.h index b6d3cb33b2..17b341d6c7 100644 --- a/libavformat/avio.h +++ b/libavformat/avio.h @@ -1,20 +1,20 @@ /* * copyright (c) 2001 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef AVFORMAT_AVIO_H @@ -115,6 +115,31 @@ 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; } AVIOContext; /* unbuffered I/O */ @@ -146,6 +171,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. @@ -207,10 +233,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 +250,23 @@ static av_always_inline int64_t avio_tell(AVIOContext *s) */ int64_t avio_size(AVIOContext *s); +/** + * feof() equivalent for AVIOContext. + * @return non zero if and only if end of file + */ +int url_feof(AVIOContext *s); + /** @warning currently size is limited */ int avio_printf(AVIOContext *s, const char *fmt, ...) av_printf_format(2, 3); +/** + * Force flushing of buffered data to the output s. + * + * Force the buffered data to be immediately written to the output, + * without to wait to fill the internal buffer. + */ void avio_flush(AVIOContext *s); - /** * Read size bytes from AVIOContext into buf. * @return number of bytes read or AVERROR @@ -311,6 +345,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 @@ -417,13 +459,13 @@ int avio_pause(AVIOContext *h, int pause); * If stream_index is (-1) the timestamp should be in AV_TIME_BASE * units from the beginning of the presentation. * If a stream_index >= 0 is used and the protocol does not support - * seeking based on component streams, the call will fail with ENOTSUP. + * seeking based on component streams, the call will fail. * @param timestamp timestamp in AVStream.time_base units * or if there is no stream specified then in AV_TIME_BASE units. * @param flags Optional combination of AVSEEK_FLAG_BACKWARD, AVSEEK_FLAG_BYTE * and AVSEEK_FLAG_ANY. The protocol may silently ignore * AVSEEK_FLAG_BACKWARD and AVSEEK_FLAG_ANY, but AVSEEK_FLAG_BYTE will - * fail with ENOTSUP if used and not supported. + * fail if used and not supported. * @return >= 0 on success * @see AVInputFormat::read_seek */ diff --git a/libavformat/avio_internal.h b/libavformat/avio_internal.h index f2ccc36623..3c0fc93b3c 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 */ @@ -64,13 +64,15 @@ static av_always_inline void ffio_wfourcc(AVIOContext *pb, const uint8_t *s) * @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); +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); diff --git a/libavformat/aviobuf.c b/libavformat/aviobuf.c index 15d132de29..906415dbac 100644 --- a/libavformat/aviobuf.c +++ b/libavformat/aviobuf.c @@ -2,20 +2,20 @@ * buffered I/O * Copyright (c) 2000,2001 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -24,6 +24,7 @@ #include "libavutil/intreadwrite.h" #include "libavutil/log.h" #include "libavutil/opt.h" +#include "libavutil/avassert.h" #include "avformat.h" #include "avio.h" #include "avio_internal.h" @@ -80,6 +81,7 @@ int ffio_init_context(AVIOContext *s, s->buffer_size = buffer_size; s->buf_ptr = buffer; s->opaque = opaque; + s->direct = 0; url_resetbuf(s, write_flag ? AVIO_FLAG_WRITE : AVIO_FLAG_READ); s->write_packet = write_packet; s->read_packet = read_packet; @@ -117,26 +119,32 @@ 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->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); @@ -158,6 +166,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); @@ -199,13 +212,14 @@ int64_t avio_seek(AVIOContext *s, int64_t offset, int whence) offset += offset1; } offset1 = offset - pos; - if (!s->must_flush && + if (!s->must_flush && (!s->direct || !s->seek) && offset1 >= 0 && offset1 <= (s->buf_end - s->buffer)) { /* can do the seek inside the buffer */ s->buf_ptr = s->buffer + offset1; } else if ((!s->seekable || offset1 <= s->buf_end + SHORT_SEEK_THRESHOLD - s->buffer) && !s->write_flag && offset1 >= 0 && + (!s->direct || !s->seek) && (whence != SEEK_END || force)) { while(s->pos < offset && !s->eof_reached) fill_buffer(s); @@ -223,6 +237,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; @@ -232,6 +247,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; @@ -251,20 +271,31 @@ int64_t avio_size(AVIOContext *s) return size; } +int url_feof(AVIOContext *s) +{ + if(!s) + return 0; + if(s->eof_reached){ + s->eof_reached=0; + fill_buffer(s); + } + return s->eof_reached; +} + void avio_wl32(AVIOContext *s, unsigned int val) { - avio_w8(s, val); - avio_w8(s, val >> 8); - avio_w8(s, val >> 16); - avio_w8(s, val >> 24); + avio_w8(s, (uint8_t) val ); + avio_w8(s, (uint8_t)(val >> 8 )); + avio_w8(s, (uint8_t)(val >> 16)); + avio_w8(s, val >> 24 ); } void avio_wb32(AVIOContext *s, unsigned int val) { - avio_w8(s, val >> 24); - avio_w8(s, val >> 16); - avio_w8(s, val >> 8); - avio_w8(s, val); + avio_w8(s, val >> 24 ); + avio_w8(s, (uint8_t)(val >> 16)); + avio_w8(s, (uint8_t)(val >> 8 )); + avio_w8(s, (uint8_t) val ); } int avio_put_str(AVIOContext *s, const char *str) @@ -308,7 +339,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); } @@ -327,26 +358,26 @@ 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 */ @@ -357,7 +388,7 @@ static void fill_buffer(AVIOContext *s) int len= s->buffer_size - (dst - s->buffer); int max_buffer_size = s->max_packet_size ? s->max_packet_size : IO_BUFFER_SIZE; - /* can't fill the buffer without read_packet, just set EOF if appropiate */ + /* can't fill the buffer without read_packet, just set EOF if appropriate */ if (!s->read_packet && s->buf_ptr >= s->buf_end) s->eof_reached = 1; @@ -372,7 +403,7 @@ static void fill_buffer(AVIOContext *s) } /* make buffer smaller in case it ended up large after probing */ - if (s->buffer_size > max_buffer_size) { + if (s->read_packet && s->buffer_size > max_buffer_size) { ffio_set_buf_size(s, max_buffer_size); s->checksum_ptr = dst = s->buffer; @@ -393,6 +424,7 @@ static void fill_buffer(AVIOContext *s) s->pos += len; s->buf_ptr = dst; s->buf_end = dst + len; + s->bytes_read += len; } } @@ -440,7 +472,7 @@ int avio_read(AVIOContext *s, unsigned char *buf, int size) if (len > size) len = size; if (len == 0) { - 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) { @@ -452,6 +484,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; @@ -471,8 +504,8 @@ int avio_read(AVIOContext *s, unsigned char *buf, int size) } } if (size1 == size) { - if (s->error) return s->error; - if (s->eof_reached) return AVERROR_EOF; + if (s->error) return s->error; + if (url_feof(s)) return AVERROR_EOF; } return size1 - size; } @@ -494,8 +527,8 @@ int ffio_read_partial(AVIOContext *s, unsigned char *buf, int size) memcpy(buf, s->buf_ptr, len); s->buf_ptr += len; if (!len) { - if (s->error) return s->error; - if (s->eof_reached) return AVERROR_EOF; + if (s->error) return s->error; + if (url_feof(s)) return AVERROR_EOF; } return len; } @@ -647,11 +680,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) { @@ -679,7 +713,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; @@ -691,27 +725,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) { @@ -762,6 +801,8 @@ 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: %"PRId64" bytes read, %d seeks\n", s->bytes_read, s->seek_count); av_free(s); return ffurl_close(h); } @@ -840,7 +881,7 @@ static int dyn_buf_write(void *opaque, uint8_t *buf, int buf_size) } if (new_allocated_size > d->allocated_size) { - d->buffer = av_realloc(d->buffer, new_allocated_size); + d->buffer = av_realloc_f(d->buffer, 1, new_allocated_size); if(d->buffer == NULL) return AVERROR(ENOMEM); d->allocated_size = new_allocated_size; diff --git a/libavformat/avisynth.c b/libavformat/avisynth.c index d31c427646..e5d9b7626f 100644 --- a/libavformat/avisynth.c +++ b/libavformat/avisynth.c @@ -2,20 +2,20 @@ * AVISynth support * Copyright (c) 2006 DivX, Inc. * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free 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 avisynth_read_header(AVFormatContext *s) res = AVIFileOpen(&avs->file, filename_char, OF_READ|OF_SHARE_DENY_WRITE, NULL); if (res != S_OK) { - av_log(s, AV_LOG_ERROR, "AVIFileOpen failed with error %ld", res); + av_log(s, AV_LOG_ERROR, "AVIFileOpen failed with error %ld\n", res); AVIFileExit(); return -1; } @@ -68,7 +68,7 @@ static int avisynth_read_header(AVFormatContext *s) res = AVIFileInfo(avs->file, &info, sizeof(info)); if (res != S_OK) { - av_log(s, AV_LOG_ERROR, "AVIFileInfo failed with error %ld", res); + av_log(s, AV_LOG_ERROR, "AVIFileInfo failed with error %ld\n", res); AVIFileExit(); return -1; } @@ -133,6 +133,14 @@ static int avisynth_read_header(AVFormatContext *s) st->codec->bit_rate = (uint64_t)stream->info.dwSampleSize * (uint64_t)stream->info.dwRate * 8 / (uint64_t)stream->info.dwScale; st->codec->codec_tag = imgfmt.bmiHeader.biCompression; st->codec->codec_id = ff_codec_get_id(ff_codec_bmp_tags, imgfmt.bmiHeader.biCompression); + if (st->codec->codec_id == AV_CODEC_ID_RAWVIDEO && imgfmt.bmiHeader.biCompression== BI_RGB) { + st->codec->extradata = av_malloc(9 + FF_INPUT_BUFFER_PADDING_SIZE); + if (st->codec->extradata) { + st->codec->extradata_size = 9; + memcpy(st->codec->extradata, "BottomUp", 9); + } + } + st->duration = stream->info.dwLength; } @@ -176,7 +184,6 @@ static int avisynth_read_packet(AVFormatContext *s, AVPacket *pkt) res = AVIStreamRead(stream->handle, stream->read, stream->chunck_samples, pkt->data, stream->chunck_size, &read_size, NULL); - pkt->pts = stream->read; pkt->size = read_size; stream->read += stream->chunck_samples; diff --git a/libavformat/avlanguage.c b/libavformat/avlanguage.c index e606ef22f9..39f2560d94 100644 --- a/libavformat/avlanguage.c +++ b/libavformat/avlanguage.c @@ -1,20 +1,20 @@ /* * Cyril Comparon, Larbi Joubala, Resonate-MP4 2009 * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/avlanguage.h b/libavformat/avlanguage.h index 2ec3e2d372..7fb8968810 100644 --- a/libavformat/avlanguage.h +++ b/libavformat/avlanguage.h @@ -1,20 +1,20 @@ /* * Cyril Comparon, Larbi Joubala, Resonate-MP4 2009 * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/avs.c b/libavformat/avs.c index a6a90dd0c9..5a1eb2e675 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 */ diff --git a/libavformat/bethsoftvid.c b/libavformat/bethsoftvid.c index ece230ccb7..191f02944e 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 */ @@ -187,7 +187,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); } @@ -207,7 +208,7 @@ static int vid_read_packet(AVFormatContext *s, int audio_length; int ret_value; - if(vid->is_finished || pb->eof_reached) + if(vid->is_finished || url_feof(pb)) return AVERROR(EIO); block_type = avio_r8(pb); diff --git a/libavformat/bfi.c b/libavformat/bfi.c index c520bea136..446ef57fc9 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 */ @@ -110,7 +110,7 @@ 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) { + if (bfi->nframes == 0 || url_feof(pb)) { return AVERROR(EIO); } @@ -118,7 +118,7 @@ static int bfi_read_packet(AVFormatContext * s, AVPacket * pkt) if (!bfi->avflag) { uint32_t state = 0; while(state != MKTAG('S','A','V','I')){ - if (pb->eof_reached) + if (url_feof(pb)) return AVERROR(EIO); state = 256*state + avio_r8(pb); } diff --git a/libavformat/bink.c b/libavformat/bink.c index 7254caf575..9c90480c8d 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 */ @@ -115,6 +115,8 @@ static int read_header(AVFormatContext *s) vst->codec->codec_type = AVMEDIA_TYPE_VIDEO; vst->codec->codec_id = AV_CODEC_ID_BINKVIDEO; vst->codec->extradata = av_mallocz(4 + FF_INPUT_BUFFER_PADDING_SIZE); + if (!vst->codec->extradata) + return AVERROR(ENOMEM); vst->codec->extradata_size = 4; avio_read(pb, vst->codec->extradata, 4); diff --git a/libavformat/bintext.c b/libavformat/bintext.c new file mode 100644 index 0000000000..91f95f3a87 --- /dev/null +++ b/libavformat/bintext.c @@ -0,0 +1,409 @@ +/* + * 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 */ + char *video_size; /**< video size (WxH pixels) (private option) */ + char *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->video_size) { + if (av_parse_video_size(&st->codec->width, &st->codec->height, bin->video_size) < 0) { + av_log(s, AV_LOG_ERROR, "Could not parse video size: '%s'\n", bin->video_size); + return NULL; + } + } else { + st->codec->width = (80<<3); + st->codec->height = (25<<4); + } + + if (bin->framerate) { + AVRational framerate; + if (av_parse_video_rate(&framerate, bin->framerate) < 0) { + av_log(s, AV_LOG_ERROR, "Could not parse framerate: '%s'\n", bin->framerate); + return NULL; + } + avpriv_set_pts_info(st, 60, framerate.den, framerate.num); + } else { + avpriv_set_pts_info(st, 60, 1, 25); + } + + /* simulate tty display speed */ + bin->chars_per_frame = FFMAX(av_q2d(st->time_base) * bin->chars_per_frame, 1); + + 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; + + st->codec->extradata_size = 2; + st->codec->extradata = av_malloc(st->codec->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE); + if (!st->codec->extradata) + 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->video_size) { + 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; + + st->codec->extradata = av_malloc(st->codec->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE); + if (!st->codec->extradata) + 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; + + st->codec->extradata_size = 2 + 48 + 4096; + st->codec->extradata = av_malloc(st->codec->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE); + if (!st->codec->extradata) + 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->video_size) + 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; + + st->codec->extradata_size = 2 + 48 + 4096; + st->codec->extradata = av_malloc(st->codec->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE); + if (!st->codec->extradata) + 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->video_size) + calculate_height(st->codec, bin->fsize); + avio_seek(pb, 12, SEEK_SET); + return 0; +} +#endif /* CONFIG_IDF_DEMUXER */ + +static int read_packet(AVFormatContext *s, + AVPacket *pkt) +{ + BinDemuxContext *bin = s->priv_data; + + if (bin->fsize > 0) { + if (av_get_packet(s->pb, pkt, bin->fsize) < 0) + return AVERROR(EIO); + bin->fsize = -1; /* done */ + } else if (!bin->fsize) { + if (url_feof(s->pb)) + return AVERROR(EIO); + if (av_get_packet(s->pb, pkt, bin->chars_per_frame) < 0) + return AVERROR(EIO); + } else { + return AVERROR(EIO); + } + + pkt->flags |= AV_PKT_FLAG_KEY; + return 0; +} + +#define OFFSET(x) offsetof(BinDemuxContext, x) +static const AVOption options[] = { + { "linespeed", "set simulated line speed (bytes per second)", OFFSET(chars_per_frame), AV_OPT_TYPE_INT, {.i64 = 6000}, 1, INT_MAX, AV_OPT_FLAG_DECODING_PARAM}, + { "video_size", "set video size, such as 640x480 or hd720.", OFFSET(video_size), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, AV_OPT_FLAG_DECODING_PARAM }, + { "framerate", "set framerate (frames per second)", OFFSET(framerate), AV_OPT_TYPE_STRING, {.str = "25"}, 0, 0, AV_OPT_FLAG_DECODING_PARAM }, + { NULL }, +}; + +#define CLASS(name) \ +(const AVClass[1]){{ \ + .class_name = name, \ + .item_name = av_default_item_name, \ + .option = options, \ + .version = LIBAVUTIL_VERSION_INT, \ +}} + +#if CONFIG_BINTEXT_DEMUXER +AVInputFormat ff_bintext_demuxer = { + .name = "bin", + .long_name = NULL_IF_CONFIG_SMALL("Binary text"), + .priv_data_size = sizeof(BinDemuxContext), + .read_header = bintext_read_header, + .read_packet = read_packet, + .extensions = "bin", + .priv_class = CLASS("Binary text demuxer"), +}; +#endif + +#if CONFIG_XBIN_DEMUXER +AVInputFormat ff_xbin_demuxer = { + .name = "xbin", + .long_name = NULL_IF_CONFIG_SMALL("eXtended BINary text (XBIN)"), + .priv_data_size = sizeof(BinDemuxContext), + .read_probe = xbin_probe, + .read_header = xbin_read_header, + .read_packet = read_packet, + .priv_class = CLASS("eXtended BINary text (XBIN) demuxer"), +}; +#endif + +#if CONFIG_ADF_DEMUXER +AVInputFormat ff_adf_demuxer = { + .name = "adf", + .long_name = NULL_IF_CONFIG_SMALL("Artworx Data Format"), + .priv_data_size = sizeof(BinDemuxContext), + .read_header = adf_read_header, + .read_packet = read_packet, + .extensions = "adf", + .priv_class = CLASS("Artworx Data Format demuxer"), +}; +#endif + +#if CONFIG_IDF_DEMUXER +AVInputFormat ff_idf_demuxer = { + .name = "idf", + .long_name = NULL_IF_CONFIG_SMALL("iCE Draw File"), + .priv_data_size = sizeof(BinDemuxContext), + .read_probe = idf_probe, + .read_header = idf_read_header, + .read_packet = read_packet, + .extensions = "idf", + .priv_class = CLASS("iCE Draw File demuxer"), +}; +#endif diff --git a/libavformat/bit.c b/libavformat/bit.c new file mode 100644 index 0000000000..9f6ea4a415 --- /dev/null +++ b/libavformat/bit.c @@ -0,0 +1,157 @@ +/* + * 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_MAX/2; +} + +static int read_header(AVFormatContext *s) +{ + AVStream* st; + + st=avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_id=AV_CODEC_ID_G729; + st->codec->sample_rate=8000; + st->codec->block_align = 16; + st->codec->channels=1; + + avpriv_set_pts_info(st, 64, 1, 100); + return 0; +} + +static int read_packet(AVFormatContext *s, + AVPacket *pkt) +{ + AVIOContext *pb = s->pb; + PutBitContext pbo; + uint16_t buf[8 * MAX_FRAME_SIZE + 2]; + int packet_size; + uint16_t* src=buf; + int i, j, ret; + int64_t pos= avio_tell(pb); + + if(url_feof(pb)) + return AVERROR_EOF; + + avio_rl16(pb); // sync word + packet_size = avio_rl16(pb) / 8; + if(packet_size > MAX_FRAME_SIZE) + return AVERROR_INVALIDDATA; + + ret = avio_read(pb, (uint8_t*)buf, (8 * packet_size) * sizeof(uint16_t)); + if(ret<0) + return ret; + if(ret != 8 * packet_size * sizeof(uint16_t)) + return AVERROR(EIO); + + if (av_new_packet(pkt, packet_size) < 0) + return AVERROR(ENOMEM); + + init_put_bits(&pbo, pkt->data, packet_size); + for(j=0; j < packet_size; j++) + for(i=0; i<8;i++) + put_bits(&pbo,1, AV_RL16(src++) == BIT_1 ? 1 : 0); + + flush_put_bits(&pbo); + + pkt->duration=1; + pkt->pos = pos; + return 0; +} + +AVInputFormat ff_bit_demuxer = { + .name = "bit", + .long_name = NULL_IF_CONFIG_SMALL("G.729 BIT file format"), + .read_probe = probe, + .read_header = read_header, + .read_packet = read_packet, + .extensions = "bit", +}; + +#if CONFIG_MUXERS +static int write_header(AVFormatContext *s) +{ + AVCodecContext *enc = s->streams[0]->codec; + + enc->codec_id = AV_CODEC_ID_G729; + enc->channels = 1; + enc->bits_per_coded_sample = 16; + enc->block_align = (enc->bits_per_coded_sample * enc->channels) >> 3; + + return 0; +} + +static int write_packet(AVFormatContext *s, AVPacket *pkt) +{ + AVIOContext *pb = s->pb; + GetBitContext gb; + int i; + + avio_wl16(pb, SYNC_WORD); + avio_wl16(pb, 8 * 10); + + init_get_bits(&gb, pkt->data, 8*10); + for(i=0; i< 8 * 10; i++) + avio_wl16(pb, get_bits1(&gb) ? BIT_1 : BIT_0); + avio_flush(pb); + + return 0; +} + +AVOutputFormat ff_bit_muxer = { + .name = "bit", + .long_name = NULL_IF_CONFIG_SMALL("G.729 BIT file format"), + .mime_type = "audio/bit", + .extensions = "bit", + .audio_codec = AV_CODEC_ID_G729, + .video_codec = AV_CODEC_ID_NONE, + .write_header = write_header, + .write_packet = write_packet, +}; +#endif diff --git a/libavformat/bluray.c b/libavformat/bluray.c new file mode 100644 index 0000000000..d2c57aab9c --- /dev/null +++ b/libavformat/bluray.c @@ -0,0 +1,235 @@ +/* + * BluRay (libbluray) protocol + * + * Copyright (c) 2012 Petri Hintukainen <phintuka <at> users.sourceforge.net> + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <libbluray/bluray.h> + +#include "libavutil/avstring.h" +#include "libavformat/avformat.h" +#include "libavformat/url.h" +#include "libavutil/opt.h" + +#define BLURAY_PROTO_PREFIX "bluray:" +#define MIN_PLAYLIST_LENGTH 180 /* 3 min */ + +typedef struct { + const AVClass *class; + + BLURAY *bd; + + int playlist; + int angle; + int chapter; + /*int region;*/ +} BlurayContext; + +#define OFFSET(x) offsetof(BlurayContext, x) +static const AVOption options[] = { +{"playlist", "", OFFSET(playlist), AV_OPT_TYPE_INT, { .i64=-1 }, -1, 99999, AV_OPT_FLAG_DECODING_PARAM }, +{"angle", "", OFFSET(angle), AV_OPT_TYPE_INT, { .i64=0 }, 0, 0xfe, AV_OPT_FLAG_DECODING_PARAM }, +{"chapter", "", OFFSET(chapter), AV_OPT_TYPE_INT, { .i64=1 }, 1, 0xfffe, AV_OPT_FLAG_DECODING_PARAM }, +/*{"region", "bluray player region code (1 = region A, 2 = region B, 4 = region C)", OFFSET(region), AV_OPT_TYPE_INT, { .i64=0 }, 0, 3, AV_OPT_FLAG_DECODING_PARAM },*/ +{NULL} +}; + +static const AVClass bluray_context_class = { + .class_name = "bluray", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + + +static int check_disc_info(URLContext *h) +{ + BlurayContext *bd = h->priv_data; + const BLURAY_DISC_INFO *disc_info; + + disc_info = bd_get_disc_info(bd->bd); + if (!disc_info) { + av_log(h, AV_LOG_ERROR, "bd_get_disc_info() failed\n"); + return -1; + } + + if (!disc_info->bluray_detected) { + av_log(h, AV_LOG_ERROR, "BluRay disc not detected\n"); + return -1; + } + + /* AACS */ + if (disc_info->aacs_detected && !disc_info->aacs_handled) { + if (!disc_info->libaacs_detected) { + av_log(h, AV_LOG_ERROR, + "Media stream encrypted with AACS, install and configure libaacs\n"); + } else { + av_log(h, AV_LOG_ERROR, "Your libaacs can't decrypt this media\n"); + } + return -1; + } + + /* BD+ */ + if (disc_info->bdplus_detected && !disc_info->bdplus_handled) { + /* + if (!disc_info->libbdplus_detected) { + av_log(h, AV_LOG_ERROR, + "Media stream encrypted with BD+, install and configure libbdplus"); + } else { + */ + av_log(h, AV_LOG_ERROR, "Unable to decrypt BD+ encrypted media\n"); + /*}*/ + return -1; + } + + return 0; +} + +static int bluray_close(URLContext *h) +{ + BlurayContext *bd = h->priv_data; + if (bd->bd) { + bd_close(bd->bd); + } + + return 0; +} + +static int bluray_open(URLContext *h, const char *path, int flags) +{ + BlurayContext *bd = h->priv_data; + int num_title_idx; + const char *diskname = path; + + av_strstart(path, BLURAY_PROTO_PREFIX, &diskname); + + bd->bd = bd_open(diskname, NULL); + if (!bd->bd) { + av_log(h, AV_LOG_ERROR, "bd_open() failed\n"); + return AVERROR(EIO); + } + + /* check if disc can be played */ + if (check_disc_info(h) < 0) { + return AVERROR(EIO); + } + + /* setup player registers */ + /* region code has no effect without menus + if (bd->region > 0 && bd->region < 5) { + av_log(h, AV_LOG_INFO, "setting region code to %d (%c)\n", bd->region, 'A' + (bd->region - 1)); + bd_set_player_setting(bd->bd, BLURAY_PLAYER_SETTING_REGION_CODE, bd->region); + } + */ + + /* load title list */ + num_title_idx = bd_get_titles(bd->bd, TITLES_RELEVANT, MIN_PLAYLIST_LENGTH); + av_log(h, AV_LOG_INFO, "%d usable playlists:\n", num_title_idx); + if (num_title_idx < 1) { + return AVERROR(EIO); + } + + /* if playlist was not given, select longest playlist */ + if (bd->playlist < 0) { + uint64_t duration = 0; + int i; + for (i = 0; i < num_title_idx; i++) { + BLURAY_TITLE_INFO *info = bd_get_title_info(bd->bd, i, 0); + + av_log(h, AV_LOG_INFO, "playlist %05d.mpls (%d:%02d:%02d)\n", + info->playlist, + ((int)(info->duration / 90000) / 3600), + ((int)(info->duration / 90000) % 3600) / 60, + ((int)(info->duration / 90000) % 60)); + + if (info->duration > duration) { + bd->playlist = info->playlist; + duration = info->duration; + } + + bd_free_title_info(info); + } + av_log(h, AV_LOG_INFO, "selected %05d.mpls\n", bd->playlist); + } + + /* select playlist */ + if (bd_select_playlist(bd->bd, bd->playlist) <= 0) { + av_log(h, AV_LOG_ERROR, "bd_select_playlist(%05d.mpls) failed\n", bd->playlist); + return AVERROR(EIO); + } + + /* select angle */ + if (bd->angle >= 0) { + bd_select_angle(bd->bd, bd->angle); + } + + /* select chapter */ + if (bd->chapter > 1) { + bd_seek_chapter(bd->bd, bd->chapter - 1); + } + + return 0; +} + +static int bluray_read(URLContext *h, unsigned char *buf, int size) +{ + BlurayContext *bd = h->priv_data; + int len; + + if (!bd || !bd->bd) { + return AVERROR(EFAULT); + } + + len = bd_read(bd->bd, buf, size); + + return len; +} + +static int64_t bluray_seek(URLContext *h, int64_t pos, int whence) +{ + BlurayContext *bd = h->priv_data; + + if (!bd || !bd->bd) { + return AVERROR(EFAULT); + } + + switch (whence) { + case SEEK_SET: + case SEEK_CUR: + case SEEK_END: + return bd_seek(bd->bd, pos); + + case AVSEEK_SIZE: + return bd_get_title_size(bd->bd); + } + + av_log(h, AV_LOG_ERROR, "Unsupported whence operation %d\n", whence); + return AVERROR(EINVAL); +} + + +URLProtocol ff_bluray_protocol = { + .name = "bluray", + .url_close = bluray_close, + .url_open = bluray_open, + .url_read = bluray_read, + .url_seek = bluray_seek, + .priv_data_size = sizeof(BlurayContext), + .priv_data_class = &bluray_context_class, +}; diff --git a/libavformat/c93.c b/libavformat/c93.c index 3f2a98fcc4..66516e9c39 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 */ diff --git a/libavformat/cache.c b/libavformat/cache.c new file mode 100644 index 0000000000..35f81e83c1 --- /dev/null +++ b/libavformat/cache.c @@ -0,0 +1,140 @@ +/* + * Input cache protocol. + * Copyright (c) 2011 Michael Niedermayer + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Based on file.c by Fabrice Bellard + */ + +/** + * @TODO + * support non continuous caching + * support keeping files + * support filling with a background thread + */ + +#include "libavutil/avassert.h" +#include "libavutil/avstring.h" +#include "libavutil/file.h" +#include "avformat.h" +#include <fcntl.h> +#if HAVE_IO_H +#include <io.h> +#endif +#if HAVE_UNISTD_H +#include <unistd.h> +#endif +#include <sys/stat.h> +#include <stdlib.h> +#include "os_support.h" +#include "url.h" + +typedef struct Context { + int fd; + int64_t end; + int64_t pos; + URLContext *inner; +} Context; + +static int cache_open(URLContext *h, const char *arg, int flags) +{ + char *buffername; + Context *c= h->priv_data; + + av_strstart(arg, "cache:", &arg); + + c->fd = av_tempfile("ffcache", &buffername, 0, h); + if (c->fd < 0){ + av_log(h, AV_LOG_ERROR, "Failed to create tempfile\n"); + return c->fd; + } + + unlink(buffername); + av_freep(&buffername); + + return ffurl_open(&c->inner, arg, flags, &h->interrupt_callback, NULL); +} + +static int cache_read(URLContext *h, unsigned char *buf, int size) +{ + Context *c= h->priv_data; + int r; + + if(c->pos<c->end){ + r = read(c->fd, buf, FFMIN(size, c->end - c->pos)); + if(r>0) + c->pos += r; + return (-1 == r)?AVERROR(errno):r; + }else{ + r = ffurl_read(c->inner, buf, size); + if(r > 0){ + int r2= write(c->fd, buf, r); + av_assert0(r2==r); // FIXME handle cache failure + c->pos += r; + c->end += r; + } + return r; + } +} + +static int64_t cache_seek(URLContext *h, int64_t pos, int whence) +{ + Context *c= h->priv_data; + + if (whence == AVSEEK_SIZE) { + pos= ffurl_seek(c->inner, pos, whence); + if(pos <= 0){ + pos= ffurl_seek(c->inner, -1, SEEK_END); + ffurl_seek(c->inner, c->end, SEEK_SET); + if(pos <= 0) + return c->end; + } + return pos; + } + + pos= lseek(c->fd, pos, whence); + if(pos<0){ + return pos; + }else if(pos <= c->end){ + c->pos= pos; + return pos; + }else{ + if(lseek(c->fd, c->pos, SEEK_SET) < 0) { + av_log(h, AV_LOG_ERROR, "Failure to seek in cache\n"); + } + return AVERROR(EPIPE); + } +} + +static int cache_close(URLContext *h) +{ + Context *c= h->priv_data; + close(c->fd); + ffurl_close(c->inner); + + return 0; +} + +URLProtocol ff_cache_protocol = { + .name = "cache", + .url_open = cache_open, + .url_read = cache_read, + .url_seek = cache_seek, + .url_close = cache_close, + .priv_data_size = sizeof(Context), +}; diff --git a/libavformat/caf.c b/libavformat/caf.c index cf128d5624..5f490d82d6 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,34 @@ * 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_MACE3, MKTAG('M','A','C','3') }, + { AV_CODEC_ID_MACE6, MKTAG('M','A','C','6') }, + { AV_CODEC_ID_MP1, MKTAG('.','m','p','1') }, + { AV_CODEC_ID_MP2, MKTAG('.','m','p','2') }, + { AV_CODEC_ID_MP3, MKTAG('.','m','p','3') }, + { AV_CODEC_ID_MP3, MKTAG('m','s', 0 ,'U') }, + { AV_CODEC_ID_PCM_ALAW, MKTAG('a','l','a','w') }, + { AV_CODEC_ID_PCM_MULAW, MKTAG('u','l','a','w') }, + { AV_CODEC_ID_QCELP, MKTAG('Q','c','l','p') }, + { AV_CODEC_ID_QDM2, MKTAG('Q','D','M','2') }, + { AV_CODEC_ID_QDM2, MKTAG('Q','D','M','C') }, /* currently unsupported codecs */ - /*{ AC-3 over S/PDIF MKBETAG('c','a','c','3') },*/ - /*{ MPEG4CELP MKBETAG('c','e','l','p') },*/ - /*{ MPEG4HVXC MKBETAG('h','v','x','c') },*/ - /*{ MPEG4TwinVQ MKBETAG('t','w','v','q') },*/ + /*{ AC-3 over S/PDIF MKTAG('c','a','c','3') },*/ + /*{ MPEG4CELP MKTAG('c','e','l','p') },*/ + /*{ MPEG4HVXC MKTAG('h','v','x','c') },*/ + /*{ MPEG4TwinVQ MKTAG('t','w','v','q') },*/ { AV_CODEC_ID_NONE, 0 }, }; + diff --git a/libavformat/caf.h b/libavformat/caf.h index 7ca4dc5c66..9c25f2c683 100644 --- a/libavformat/caf.h +++ b/libavformat/caf.h @@ -2,20 +2,20 @@ * CAF common code * Copyright (c) 2007 Justin Ruggles * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/cafdec.c b/libavformat/cafdec.c index b3cbb39a4c..a576dac07e 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 */ @@ -70,7 +70,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; @@ -87,7 +87,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); @@ -212,7 +212,7 @@ static void read_info_chunk(AVFormatContext *s, int64_t size) for (i = 0; i < nb_entries; i++) { char key[32]; char value[1024]; - avio_get_str(pb, INT_MAX, key, sizeof(key)); + avio_get_str(pb, INT_MAX, key, sizeof(key)); avio_get_str(pb, INT_MAX, value, sizeof(value)); av_dict_set(&s->metadata, key, value, 0); } @@ -245,7 +245,7 @@ static int read_header(AVFormatContext *s) /* parse each chunk */ found_data = 0; - while (!pb->eof_reached) { + while (!url_feof(pb)) { /* stop at data chunk if seeking is not supported or data chunk size is unknown */ @@ -254,7 +254,7 @@ static int read_header(AVFormatContext *s) tag = avio_rb32(pb); size = avio_rb64(pb); - if (pb->eof_reached) + if (url_feof(pb)) break; switch (tag) { @@ -290,8 +290,8 @@ static int read_header(AVFormatContext *s) default: #define _(x) ((x) >= ' ' ? (x) : ' ') - av_log(s, AV_LOG_WARNING, "skipping CAF chunk: %08X (%c%c%c%c)\n", - tag, _(tag>>24), _((tag>>16)&0xFF), _((tag>>8)&0xFF), _(tag&0xFF)); + av_log(s, AV_LOG_WARNING, "skipping CAF chunk: %08X (%c%c%c%c), size %"PRId64"\n", + tag, _(tag>>24), _((tag>>16)&0xFF), _((tag>>8)&0xFF), _(tag&0xFF), size); #undef _ case MKBETAG('f','r','e','e'): if (size < 0) @@ -336,7 +336,7 @@ 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) + if (url_feof(pb)) return AVERROR(EIO); /* don't read past end of data chunk */ diff --git a/libavformat/cafenc.c b/libavformat/cafenc.c new file mode 100644 index 0000000000..cc39a8341c --- /dev/null +++ b/libavformat/cafenc.c @@ -0,0 +1,279 @@ +/* + * 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 "riff.h" +#include "isom.h" +#include "avio_internal.h" +#include "libavutil/intfloat.h" +#include "libavutil/dict.h" + +typedef struct { + int64_t data; + uint8_t *pkt_sizes; + int size_buffer_size; + int size_entries_used; + int packets; +} CAFContext; + +static uint32_t codec_flags(enum AVCodecID codec_id) { + switch (codec_id) { + case AV_CODEC_ID_PCM_F32BE: + case AV_CODEC_ID_PCM_F64BE: + return 1; //< kCAFLinearPCMFormatFlagIsFloat + case AV_CODEC_ID_PCM_S16LE: + case AV_CODEC_ID_PCM_S24LE: + case AV_CODEC_ID_PCM_S32LE: + return 2; //< kCAFLinearPCMFormatFlagIsLittleEndian + case AV_CODEC_ID_PCM_F32LE: + case AV_CODEC_ID_PCM_F64LE: + return 3; //< kCAFLinearPCMFormatFlagIsFloat | kCAFLinearPCMFormatFlagIsLittleEndian + default: + return 0; + } +} + +static uint32_t samples_per_packet(enum AVCodecID codec_id, int channels) { + switch (codec_id) { + case AV_CODEC_ID_PCM_S8: + case AV_CODEC_ID_PCM_S16LE: + case AV_CODEC_ID_PCM_S16BE: + case AV_CODEC_ID_PCM_S24LE: + case AV_CODEC_ID_PCM_S24BE: + case AV_CODEC_ID_PCM_S32LE: + case AV_CODEC_ID_PCM_S32BE: + case AV_CODEC_ID_PCM_F32LE: + case AV_CODEC_ID_PCM_F32BE: + case AV_CODEC_ID_PCM_F64LE: + case AV_CODEC_ID_PCM_F64BE: + case AV_CODEC_ID_PCM_ALAW: + case AV_CODEC_ID_PCM_MULAW: + return 1; + case AV_CODEC_ID_MACE3: + case AV_CODEC_ID_MACE6: + return 6; + case AV_CODEC_ID_ADPCM_IMA_QT: + return 64; + case AV_CODEC_ID_AMR_NB: + case AV_CODEC_ID_GSM: + case AV_CODEC_ID_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_ALAC: + case AV_CODEC_ID_QDM2: + return 4096; + case AV_CODEC_ID_ADPCM_IMA_WAV: + return (1024 - 4 * channels) * 8 / (4 * channels) + 1; + case AV_CODEC_ID_ADPCM_MS: + return (1024 - 7 * channels) * 2 / channels + 2; + default: + return 0; + } +} + +static int caf_write_header(AVFormatContext *s) +{ + AVIOContext *pb = s->pb; + AVCodecContext *enc = s->streams[0]->codec; + CAFContext *caf = s->priv_data; + AVDictionaryEntry *t = NULL; + unsigned int codec_tag = ff_codec_get_tag(ff_codec_caf_tags, enc->codec_id); + int64_t chunk_size = 0; + + switch (enc->codec_id) { + case AV_CODEC_ID_AAC: + case AV_CODEC_ID_AC3: + av_log(s, AV_LOG_ERROR, "muxing codec currently unsupported\n"); + return AVERROR_PATCHWELCOME; + } + + switch (enc->codec_id) { + case AV_CODEC_ID_PCM_S8: + case AV_CODEC_ID_PCM_S16LE: + case AV_CODEC_ID_PCM_S16BE: + case AV_CODEC_ID_PCM_S24LE: + case AV_CODEC_ID_PCM_S24BE: + case AV_CODEC_ID_PCM_S32LE: + case AV_CODEC_ID_PCM_S32BE: + case AV_CODEC_ID_PCM_F32LE: + case AV_CODEC_ID_PCM_F32BE: + case AV_CODEC_ID_PCM_F64LE: + case AV_CODEC_ID_PCM_F64BE: + case AV_CODEC_ID_PCM_ALAW: + case AV_CODEC_ID_PCM_MULAW: + codec_tag = MKTAG('l','p','c','m'); + } + + if (!codec_tag) { + av_log(s, AV_LOG_ERROR, "unsupported codec\n"); + return AVERROR_INVALIDDATA; + } + + if (!enc->block_align && !pb->seekable) { + av_log(s, AV_LOG_ERROR, "Muxing variable packet size not supported on non seekable output\n"); + return AVERROR_INVALIDDATA; + } + + ffio_wfourcc(pb, "caff"); //< mFileType + avio_wb16(pb, 1); //< mFileVersion + avio_wb16(pb, 0); //< mFileFlags + + ffio_wfourcc(pb, "desc"); //< Audio Description chunk + avio_wb64(pb, 32); //< mChunkSize + avio_wb64(pb, av_double2int(enc->sample_rate)); //< mSampleRate + avio_wl32(pb, codec_tag); //< mFormatID + avio_wb32(pb, codec_flags(enc->codec_id)); //< mFormatFlags + avio_wb32(pb, enc->block_align); //< mBytesPerPacket + avio_wb32(pb, samples_per_packet(enc->codec_id, enc->channels)); //< mFramesPerPacket + avio_wb32(pb, enc->channels); //< mChannelsPerFrame + avio_wb32(pb, av_get_bits_per_sample(enc->codec_id)); //< mBitsPerChannel + + if (enc->channel_layout) { + ffio_wfourcc(pb, "chan"); + avio_wb64(pb, 12); + ff_mov_write_chan(pb, enc->channel_layout); + } + + if (enc->codec_id == AV_CODEC_ID_ALAC) { + ffio_wfourcc(pb, "kuki"); + avio_wb64(pb, 12 + enc->extradata_size); + avio_write(pb, "\0\0\0\14frmaalac", 12); + avio_write(pb, enc->extradata, enc->extradata_size); + } else if (enc->codec_id == AV_CODEC_ID_AMR_NB) { + ffio_wfourcc(pb, "kuki"); + avio_wb64(pb, 29); + avio_write(pb, "\0\0\0\14frmasamr", 12); + avio_wb32(pb, 0x11); /* size */ + avio_write(pb, "samrFFMP", 8); + avio_w8(pb, 0); /* decoder version */ + + avio_wb16(pb, 0x81FF); /* Mode set (all modes for AMR_NB) */ + avio_w8(pb, 0x00); /* Mode change period (no restriction) */ + avio_w8(pb, 0x01); /* Frames per sample */ + } else if (enc->codec_id == AV_CODEC_ID_QDM2) { + ffio_wfourcc(pb, "kuki"); + avio_wb64(pb, enc->extradata_size); + avio_write(pb, enc->extradata, enc->extradata_size); + } + + if (av_dict_count(s->metadata)) { + ffio_wfourcc(pb, "info"); //< Information chunk + while ((t = av_dict_get(s->metadata, "", t, AV_DICT_IGNORE_SUFFIX))) { + chunk_size += strlen(t->key) + strlen(t->value) + 2; + } + avio_wb64(pb, chunk_size + 4); + avio_wb32(pb, av_dict_count(s->metadata)); + t = NULL; + while ((t = av_dict_get(s->metadata, "", t, AV_DICT_IGNORE_SUFFIX))) { + avio_put_str(pb, t->key); + avio_put_str(pb, t->value); + } + } + + ffio_wfourcc(pb, "data"); //< Audio Data chunk + caf->data = avio_tell(pb); + avio_wb64(pb, -1); //< mChunkSize + avio_wb32(pb, 0); //< mEditCount + + avio_flush(pb); + return 0; +} + +static int caf_write_packet(AVFormatContext *s, AVPacket *pkt) +{ + CAFContext *caf = s->priv_data; + + avio_write(s->pb, pkt->data, pkt->size); + if (!s->streams[0]->codec->block_align) { + void *pkt_sizes = caf->pkt_sizes; + int i, alloc_size = caf->size_entries_used + 5; + if (alloc_size < 0) { + caf->pkt_sizes = NULL; + } else { + caf->pkt_sizes = av_fast_realloc(caf->pkt_sizes, + &caf->size_buffer_size, + alloc_size); + } + if (!caf->pkt_sizes) { + av_free(pkt_sizes); + return AVERROR(ENOMEM); + } + for (i = 4; i > 0; i--) { + unsigned top = pkt->size >> i * 7; + if (top) + caf->pkt_sizes[caf->size_entries_used++] = 128 | top; + } + caf->pkt_sizes[caf->size_entries_used++] = pkt->size & 127; + caf->packets++; + } + return 0; +} + +static int caf_write_trailer(AVFormatContext *s) +{ + AVIOContext *pb = s->pb; + AVCodecContext *enc = s->streams[0]->codec; + + if (pb->seekable) { + CAFContext *caf = s->priv_data; + int64_t file_size = avio_tell(pb); + + avio_seek(pb, caf->data, SEEK_SET); + avio_wb64(pb, file_size - caf->data - 8); + avio_seek(pb, file_size, SEEK_SET); + if (!enc->block_align) { + ffio_wfourcc(pb, "pakt"); + avio_wb64(pb, caf->size_entries_used + 24); + avio_wb64(pb, caf->packets); ///< mNumberPackets + avio_wb64(pb, caf->packets * samples_per_packet(enc->codec_id, enc->channels)); ///< mNumberValidFrames + avio_wb32(pb, 0); ///< mPrimingFrames + avio_wb32(pb, 0); ///< mRemainderFrames + avio_write(pb, caf->pkt_sizes, caf->size_entries_used); + av_freep(&caf->pkt_sizes); + caf->size_buffer_size = 0; + } + avio_flush(pb); + } + 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 ed9cdb21f5..c035128bcf 100644 --- a/libavformat/cavsvideodec.c +++ b/libavformat/cavsvideodec.c @@ -2,20 +2,20 @@ * RAW Chinese AVS video demuxer * Copyright (c) 2009 Stefan Gehrer <stefan.gehrer@gmx.de> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/cdg.c b/libavformat/cdg.c index 974880a630..2d40d3dc55 100644 --- a/libavformat/cdg.c +++ b/libavformat/cdg.c @@ -2,20 +2,20 @@ * CD Graphics Demuxer * Copyright (c) 2009 Michael Tison * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -60,6 +60,12 @@ static int read_packet(AVFormatContext *s, AVPacket *pkt) } pkt->stream_index = 0; + pkt->dts= + pkt->pts= pkt->pos / CDG_PACKET_SIZE; + + if(ret>5 && (pkt->data[0]&0x3F) == 9 && (pkt->data[1]&0x3F)==1 && !(pkt->data[2+2+1] & 0x0F)){ + pkt->flags = AV_PKT_FLAG_KEY; + } return ret; } @@ -68,5 +74,6 @@ AVInputFormat ff_cdg_demuxer = { .long_name = NULL_IF_CONFIG_SMALL("CD Graphics"), .read_header = read_header, .read_packet = read_packet, + .flags = AVFMT_GENERIC_INDEX, .extensions = "cdg", }; diff --git a/libavformat/cdxl.c b/libavformat/cdxl.c index d61cbd9a3d..b65011a072 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 */ diff --git a/libavformat/concat.c b/libavformat/concat.c index 24c50c1e4e..f97354c788 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 */ diff --git a/libavformat/crcenc.c b/libavformat/crcenc.c index 3b30cc9199..ac0638037e 100644 --- a/libavformat/crcenc.c +++ b/libavformat/crcenc.c @@ -2,20 +2,20 @@ * CRC encoder (for codec/format testing) * Copyright (c) 2002 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -57,7 +57,6 @@ static int crc_write_trailer(struct AVFormatContext *s) AVOutputFormat ff_crc_muxer = { .name = "crc", .long_name = NULL_IF_CONFIG_SMALL("CRC testing"), - .extensions = "", .priv_data_size = sizeof(CRCState), .audio_codec = AV_CODEC_ID_PCM_S16LE, .video_codec = AV_CODEC_ID_RAWVIDEO, diff --git a/libavformat/crypto.c b/libavformat/crypto.c index 3bc33f2bb9..5d518beb01 100644 --- a/libavformat/crypto.c +++ b/libavformat/crypto.c @@ -59,7 +59,7 @@ static const AVClass crypto_class = { .version = LIBAVUTIL_VERSION_INT, }; -static int crypto_open(URLContext *h, const char *uri, int flags) +static int crypto_open2(URLContext *h, const char *uri, int flags, AVDictionary **options) { const char *nested_url; int ret = 0; @@ -83,7 +83,7 @@ static int crypto_open(URLContext *h, const char *uri, int flags) goto err; } if ((ret = ffurl_open(&c->hd, nested_url, AVIO_FLAG_READ, - &h->interrupt_callback, NULL)) < 0) { + &h->interrupt_callback, options)) < 0) { av_log(h, AV_LOG_ERROR, "Unable to open input\n"); goto err; } @@ -161,7 +161,7 @@ static int crypto_close(URLContext *h) URLProtocol ff_crypto_protocol = { .name = "crypto", - .url_open = crypto_open, + .url_open2 = crypto_open2, .url_read = crypto_read, .url_close = crypto_close, .priv_data_size = sizeof(CryptoContext), diff --git a/libavformat/cutils.c b/libavformat/cutils.c index f58e152cac..0458a2dc7c 100644 --- a/libavformat/cutils.c +++ b/libavformat/cutils.c @@ -2,46 +2,25 @@ * various simple utilities for libavformat * Copyright (c) 2000, 2001, 2002 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "avformat.h" #include "internal.h" -/* add one element to a dynamic array */ -void ff_dynarray_add(intptr_t **tab_ptr, int *nb_ptr, intptr_t elem) -{ - /* see similar avconv.c:grow_array() */ - int nb, nb_alloc; - intptr_t *tab; - - nb = *nb_ptr; - tab = *tab_ptr; - if ((nb & (nb - 1)) == 0) { - if (nb == 0) - nb_alloc = 1; - else - nb_alloc = nb * 2; - tab = av_realloc(tab, nb_alloc * sizeof(intptr_t)); - *tab_ptr = tab; - } - tab[nb++] = elem; - *nb_ptr = nb; -} - #define ISLEAP(y) (((y) % 4 == 0) && (((y) % 100) != 0 || ((y) % 400) == 0)) #define LEAPS_COUNT(y) ((y)/4 - (y)/100 + (y)/400) diff --git a/libavformat/daud.c b/libavformat/daud.c index dda83f95a5..bfbcf0a6b0 100644 --- a/libavformat/daud.c +++ b/libavformat/daud.c @@ -2,20 +2,20 @@ * D-Cinema audio demuxer * Copyright (c) 2005 Reimar Döffinger * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "avformat.h" @@ -38,7 +38,7 @@ static int daud_header(AVFormatContext *s) { static int daud_packet(AVFormatContext *s, AVPacket *pkt) { AVIOContext *pb = s->pb; int ret, size; - if (pb->eof_reached) + if (url_feof(pb)) return AVERROR(EIO); size = avio_rb16(pb); avio_rb16(pb); // unknown @@ -75,7 +75,7 @@ AVInputFormat ff_daud_demuxer = { .long_name = NULL_IF_CONFIG_SMALL("D-Cinema audio"), .read_header = daud_header, .read_packet = daud_packet, - .extensions = "302", + .extensions = "302,daud", }; #endif diff --git a/libavformat/dfa.c b/libavformat/dfa.c index fa32d1f2cf..8cf2f24fc8 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 */ diff --git a/libavformat/diracdec.c b/libavformat/diracdec.c index f275212d4a..6636ead30d 100644 --- a/libavformat/diracdec.c +++ b/libavformat/diracdec.c @@ -2,20 +2,20 @@ * RAW Dirac demuxer * Copyright (c) 2007 Marco Gerards <marco@gnu.org> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/dnxhddec.c b/libavformat/dnxhddec.c index 8bb68145fc..7b554003f1 100644 --- a/libavformat/dnxhddec.c +++ b/libavformat/dnxhddec.c @@ -3,20 +3,20 @@ * Copyright (c) 2008 Baptiste Coudurier <baptiste.coudurier@gmail.com> * Copyright (c) 2009 Reimar Döffinger <Reimar.Doeffinger@gmx.de> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -37,7 +37,7 @@ static int dnxhd_probe(AVProbeData *p) if (!w || !h) return 0; compression_id = AV_RB32(p->buf + 0x28); - if (compression_id < 1237 || compression_id > 1253) + if (compression_id < 1235 || compression_id > 1253) return 0; return AVPROBE_SCORE_MAX; } diff --git a/libavformat/dsicin.c b/libavformat/dsicin.c index 8cba461487..afc010a16f 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 */ @@ -27,6 +27,7 @@ #include "libavutil/intreadwrite.h" #include "avformat.h" #include "internal.h" +#include "avio_internal.h" typedef struct CinFileHeader { @@ -147,7 +148,7 @@ static int cin_read_frame_header(CinDemuxContext *cin, AVIOContext *pb) { hdr->video_frame_size = avio_rl32(pb); hdr->audio_frame_size = avio_rl32(pb); - if (pb->eof_reached || pb->error) + if (url_feof(pb) || pb->error) return AVERROR(EIO); if (avio_rl32(pb) != 0xAA55AA55) @@ -179,6 +180,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 28cccd320a..5c05758327 100644 --- a/libavformat/dtsdec.c +++ b/libavformat/dtsdec.c @@ -2,20 +2,20 @@ * RAW DTS demuxer * Copyright (c) 2008 Benjamin Larsson * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/dtshddec.c b/libavformat/dtshddec.c new file mode 100644 index 0000000000..55c4ca607e --- /dev/null +++ b/libavformat/dtshddec.c @@ -0,0 +1,139 @@ +/* + * Raw DTS-HD demuxer + * Copyright (c) 2012 Paul B Mahol + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/intreadwrite.h" +#include "libavutil/dict.h" +#include "avformat.h" + +#define AUPR_HDR 0x415550522D484452 +#define AUPRINFO 0x41555052494E464F +#define BITSHVTB 0x4249545348565442 +#define BLACKOUT 0x424C41434B4F5554 +#define BRANCHPT 0x4252414E43485054 +#define BUILDVER 0x4255494C44564552 +#define CORESSMD 0x434F524553534D44 +#define DTSHDHDR 0x4454534844484452 +#define EXTSS_MD 0x45585453535f4d44 +#define FILEINFO 0x46494C45494E464F +#define NAVI_TBL 0x4E4156492D54424C +#define STRMDATA 0x5354524D44415441 +#define TIMECODE 0x54494D45434F4445 + +typedef struct DTSHDDemuxContext { + uint64_t data_end; +} DTSHDDemuxContext; + +static int dtshd_probe(AVProbeData *p) +{ + if (AV_RB64(p->buf) == DTSHDHDR) + return AVPROBE_SCORE_MAX; + return 0; +} + +static int dtshd_read_header(AVFormatContext *s) +{ + DTSHDDemuxContext *dtshd = s->priv_data; + AVIOContext *pb = s->pb; + uint64_t chunk_type, chunk_size; + AVStream *st; + int ret; + char *value; + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_id = AV_CODEC_ID_DTS; + st->need_parsing = AVSTREAM_PARSE_FULL_RAW; + + while (!url_feof(pb)) { + chunk_type = avio_rb64(pb); + chunk_size = avio_rb64(pb); + + if (chunk_size < 4) { + av_log(s, AV_LOG_ERROR, "chunk size too small\n"); + return AVERROR_INVALIDDATA; + } + if (chunk_size > ((uint64_t)1 << 61)) { + av_log(s, AV_LOG_ERROR, "chunk size too big\n"); + return AVERROR_INVALIDDATA; + } + + switch (chunk_type) { + case STRMDATA: + dtshd->data_end = chunk_size + avio_tell(pb); + if (dtshd->data_end <= chunk_size) + return AVERROR_INVALIDDATA; + return 0; + break; + case FILEINFO: + if (chunk_size > INT_MAX) + goto skip; + value = av_malloc(chunk_size); + if (!value) + goto skip; + avio_read(pb, value, chunk_size); + value[chunk_size - 1] = 0; + av_dict_set(&s->metadata, "fileinfo", value, + AV_DICT_DONT_STRDUP_VAL); + break; + default: +skip: + ret = avio_skip(pb, chunk_size); + if (ret < 0) + return ret; + }; + } + + return AVERROR_EOF; +} + +static int raw_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + DTSHDDemuxContext *dtshd = s->priv_data; + int64_t size, left; + int ret; + + left = dtshd->data_end - avio_tell(s->pb); + size = FFMIN(left, 1024); + if (size <= 0) + return AVERROR_EOF; + + ret = av_get_packet(s->pb, pkt, size); + if (ret < 0) + return ret; + + pkt->stream_index = 0; + + return ret; +} + +AVInputFormat ff_dtshd_demuxer = { + .name = "dtshd", + .long_name = NULL_IF_CONFIG_SMALL("raw DTS-HD"), + .priv_data_size = sizeof(DTSHDDemuxContext), + .read_probe = dtshd_probe, + .read_header = dtshd_read_header, + .read_packet = raw_read_packet, + .flags = AVFMT_GENERIC_INDEX, + .extensions = "dtshd", + .raw_codec_id = AV_CODEC_ID_DTS, +}; diff --git a/libavformat/dv.c b/libavformat/dv.c index 70786b1071..1d5ec2cf89 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> @@ -35,7 +35,9 @@ #include "libavcodec/dvdata.h" #include "libavutil/intreadwrite.h" #include "libavutil/mathematics.h" +#include "libavutil/timecode.h" #include "dv.h" +#include "libavutil/avassert.h" struct DVDemuxContext { const DVprofile* sys; /* Current DV profile. E.g.: 525/60, 625/50 */ @@ -88,6 +90,9 @@ static const uint8_t* dv_extract_pack(uint8_t* frame, enum dv_pack_type t) case dv_video_control: offs = (80*5 + 48 + 5); break; + case dv_timecode: + offs = (80*1 + 3 + 3); + break; default: return NULL; } @@ -138,7 +143,7 @@ static int dv_extract_audio(uint8_t* frame, uint8_t* ppcm[4], /* 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; @@ -148,6 +153,7 @@ static int dv_extract_audio(uint8_t* frame, uint8_t* ppcm[4], 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; @@ -267,11 +273,6 @@ static int dv_extract_video_info(DVDemuxContext *c, uint8_t* frame) avpriv_set_pts_info(c->vst, 64, c->sys->time_base.num, c->sys->time_base.den); avctx->time_base= c->sys->time_base; - if (!avctx->width){ - avctx->width = c->sys->width; - avctx->height = c->sys->height; - } - avctx->pix_fmt = c->sys->pix_fmt; /* finding out SAR is a little bit messy */ vsc_pack = dv_extract_pack(frame, dv_video_control); @@ -286,6 +287,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 */ @@ -331,7 +348,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[4] = {0}; @@ -346,6 +363,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; ppcm[i] = c->audio_buf[i]; @@ -370,6 +388,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->id; @@ -400,9 +419,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; } @@ -416,6 +439,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; @@ -427,7 +482,7 @@ static int dv_read_header(AVFormatContext *s) state = avio_rb32(s->pb); while ((state & 0xffffff7f) != 0x1f07003f) { - if (s->pb->eof_reached) { + if (url_feof(s->pb)) { av_log(s, AV_LOG_ERROR, "Cannot find DV header.\n"); return -1; } @@ -442,7 +497,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); @@ -455,6 +510,9 @@ static int dv_read_header(AVFormatContext *s) s->bit_rate = av_rescale_q(c->dv_demux->sys->frame_size, (AVRational){8,1}, c->dv_demux->sys->time_base); + if (s->pb->seekable) + dv_read_timecode(s); + return 0; } @@ -467,13 +525,14 @@ static int dv_read_packet(AVFormatContext *s, AVPacket *pkt) size = avpriv_dv_get_packet(c->dv_demux, pkt); if (size < 0) { + int64_t pos = avio_tell(s->pb); if (!c->dv_demux->sys) return AVERROR(EIO); size = c->dv_demux->sys->frame_size; if (avio_read(s->pb, c->buf, size) <= 0) return AVERROR(EIO); - size = avpriv_dv_produce_packet(c->dv_demux, pkt, c->buf, size); + size = avpriv_dv_produce_packet(c->dv_demux, pkt, c->buf, size, pos); } return size; diff --git a/libavformat/dv.h b/libavformat/dv.h index e8b2d37904..160c6ab873 100644 --- a/libavformat/dv.h +++ b/libavformat/dv.h @@ -8,20 +8,20 @@ * Raw DV format * Copyright (c) 2002 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -33,7 +33,7 @@ typedef struct DVDemuxContext DVDemuxContext; DVDemuxContext* avpriv_dv_init_demux(AVFormatContext* s); int avpriv_dv_get_packet(DVDemuxContext*, AVPacket *); -int avpriv_dv_produce_packet(DVDemuxContext*, AVPacket*, uint8_t*, int); +int avpriv_dv_produce_packet(DVDemuxContext*, AVPacket*, uint8_t*, int, int64_t); void ff_dv_offset_reset(DVDemuxContext *c, int64_t frame_offset); typedef struct DVMuxContext DVMuxContext; diff --git a/libavformat/dvenc.c b/libavformat/dvenc.c index 27a444ea1f..a132edbadf 100644 --- a/libavformat/dvenc.c +++ b/libavformat/dvenc.c @@ -11,20 +11,20 @@ * 50 Mbps (DVCPRO50) support * Copyright (c) 2006 Daniel Maas <dmaas@maasdigital.com> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include <time.h> @@ -37,10 +37,14 @@ #include "dv.h" #include "libavutil/fifo.h" #include "libavutil/mathematics.h" +#include "libavutil/intreadwrite.h" +#include "libavutil/opt.h" +#include "libavutil/timecode.h" #define MAX_AUDIO_FRAME_SIZE 192000 // 1 second of 48khz 32bit audio struct DVMuxContext { + AVClass *av_class; const DVprofile* sys; /* current DV profile, e.g.: 525/60, 625/50 */ int n_ast; /* number of stereo audio streams (up to 2) */ AVStream *ast[2]; /* stereo audio streams */ @@ -50,6 +54,7 @@ struct DVMuxContext { int has_audio; /* frame under contruction has audio */ int has_video; /* frame under contruction has video */ uint8_t frame_buf[DV_MAX_FRAME_SIZE]; /* frame under contruction */ + AVTimecode tc; /* timecode context */ }; static const int dv_aaux_packs_dist[12][9] = { @@ -77,34 +82,15 @@ static int dv_write_pack(enum dv_pack_type pack_id, DVMuxContext *c, uint8_t* bu { struct tm tc; time_t ct; - int ltc_frame; + uint32_t timecode; va_list ap; buf[0] = (uint8_t)pack_id; switch (pack_id) { case dv_timecode: - ct = (time_t)av_rescale_rnd(c->frames, c->sys->time_base.num, - c->sys->time_base.den, AV_ROUND_DOWN); - ff_brktimegm(ct, &tc); - /* - * LTC drop-frame frame counter drops two frames (0 and 1) every - * minute, unless it is exactly divisible by 10 - */ - ltc_frame = (c->frames + 2 * ct / 60 - 2 * ct / 600) % c->sys->ltc_divisor; - buf[1] = (0 << 7) | /* color frame: 0 - unsync; 1 - sync mode */ - (1 << 6) | /* drop frame timecode: 0 - nondrop; 1 - drop */ - ((ltc_frame / 10) << 4) | /* tens of frames */ - (ltc_frame % 10); /* units of frames */ - buf[2] = (1 << 7) | /* biphase mark polarity correction: 0 - even; 1 - odd */ - ((tc.tm_sec / 10) << 4) | /* tens of seconds */ - (tc.tm_sec % 10); /* units of seconds */ - buf[3] = (1 << 7) | /* binary group flag BGF0 */ - ((tc.tm_min / 10) << 4) | /* tens of minutes */ - (tc.tm_min % 10); /* units of minutes */ - buf[4] = (1 << 7) | /* binary group flag BGF2 */ - (1 << 6) | /* binary group flag BGF1 */ - ((tc.tm_hour / 10) << 4) | /* tens of hours */ - (tc.tm_hour % 10); /* units of hours */ + timecode = av_timecode_get_smpte_from_framenum(&c->tc, c->frames); + timecode |= 1<<23 | 1<<15 | 1<<7 | 1<<6; // biphase and binary group flags + AV_WB32(buf + 1, timecode); break; case dv_audio_source: /* AAUX source pack */ va_start(ap, buf); @@ -369,6 +355,10 @@ static void dv_delete_mux(DVMuxContext *c) static int dv_write_header(AVFormatContext *s) { + AVRational rate; + DVMuxContext *dvc = s->priv_data; + AVDictionaryEntry *tcr = av_dict_get(s->metadata, "timecode", NULL, 0); + if (!dv_init_mux(s)) { av_log(s, AV_LOG_ERROR, "Can't initialize DV format!\n" "Make sure that you supply exactly two streams:\n" @@ -376,7 +366,19 @@ static int dv_write_header(AVFormatContext *s) " (50Mbps allows an optional second audio stream)\n"); return -1; } - return 0; + rate.num = dvc->sys->ltc_divisor; + rate.den = 1; + if (!tcr) { // no global timecode, look into the streams + int i; + for (i = 0; i < s->nb_streams; i++) { + tcr = av_dict_get(s->streams[i]->metadata, "timecode", NULL, 0); + if (tcr) + break; + } + } + if (tcr) + return av_timecode_init_from_string(&dvc->tc, rate, tcr->value, s); + return av_timecode_init(&dvc->tc, rate, 0, 0, s); } static int dv_write_packet(struct AVFormatContext *s, AVPacket *pkt) diff --git a/libavformat/dxa.c b/libavformat/dxa.c index a1b85fe887..30d1e02068 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 */ @@ -110,7 +110,7 @@ static int dxa_read_header(AVFormatContext *s) if (ast->codec->sample_rate > 0) avpriv_set_pts_info(ast, 64, 1, ast->codec->sample_rate); // find 'data' chunk - while(avio_tell(pb) < c->vidpos && !pb->eof_reached){ + while(avio_tell(pb) < c->vidpos && !url_feof(pb)){ tag = avio_rl32(pb); fsize = avio_rl32(pb); if(tag == MKTAG('d', 'a', 't', 'a')) break; @@ -168,7 +168,7 @@ static int dxa_read_packet(AVFormatContext *s, AVPacket *pkt) return 0; } avio_seek(s->pb, c->vidpos, SEEK_SET); - while(!s->pb->eof_reached && c->frames){ + while(!url_feof(s->pb) && c->frames){ avio_read(s->pb, buf, 4); switch(AV_RL32(buf)){ case MKTAG('N', 'U', 'L', 'L'): diff --git a/libavformat/eacdata.c b/libavformat/eacdata.c index b56ba1c90c..be31d007e0 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,7 +74,9 @@ 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; + st->codec->sample_fmt = AV_SAMPLE_FMT_S16; avpriv_set_pts_info(st, 64, 1, sample_rate); cdata->audio_pts = 0; diff --git a/libavformat/electronicarts.c b/libavformat/electronicarts.c index ae2fda079f..c5533f2674 100644 --- a/libavformat/electronicarts.c +++ b/libavformat/electronicarts.c @@ -2,20 +2,20 @@ * Copyright (c) 2004 The ffmpeg Project * Copyright (c) 2006-2008 Peter Ross * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -66,6 +66,7 @@ typedef struct EaDemuxContext { enum AVCodecID video_codec; AVRational time_base; int width, height; + int nb_frames; int video_stream_index; enum AVCodecID audio_codec; @@ -109,7 +110,7 @@ static int process_audio_header_elements(AVFormatContext *s) ea->sample_rate = -1; ea->num_channels = 1; - while (!pb->eof_reached && inHeader) { + while (!url_feof(pb) && inHeader) { int inSubheader; uint8_t byte; byte = avio_r8(pb); @@ -118,7 +119,7 @@ static int process_audio_header_elements(AVFormatContext *s) case 0xFD: av_log (s, AV_LOG_DEBUG, "entered audio subheader\n"); inSubheader = 1; - while (!pb->eof_reached && inSubheader) { + while (!url_feof(pb) && inSubheader) { uint8_t subbyte; subbyte = avio_r8(pb); @@ -274,7 +275,9 @@ 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); ea->video_codec = AV_CODEC_ID_VP6; @@ -348,7 +351,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 : @@ -417,7 +419,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) { @@ -428,14 +430,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); -#if FF_API_R_FRAME_RATE + 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 = -#endif - st->avg_frame_rate = (AVRational){ea->time_base.den, ea->time_base.num}; + st->avg_frame_rate = av_inv_q(ea->time_base); } if (ea->audio_codec) { @@ -483,11 +488,12 @@ static int ea_read_packet(AVFormatContext *s, AVIOContext *pb = s->pb; int ret = 0; int packet_read = 0; + int partial_packet = 0; unsigned int chunk_type, chunk_size; int 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) @@ -515,6 +521,11 @@ static int ea_read_packet(AVFormatContext *s, avio_skip(pb, 8); chunk_size -= 12; } + if (partial_packet) { + av_log_ask_for_sample(s, "video header followed by audio packet not supported.\n"); + av_free_packet(pkt); + partial_packet = 0; + } ret = av_get_packet(pb, pkt, chunk_size); if (ret < 0) return ret; @@ -579,9 +590,15 @@ static int ea_read_packet(AVFormatContext *s, key = AV_PKT_FLAG_KEY; case MV0F_TAG: get_video_packet: - ret = av_get_packet(pb, pkt, chunk_size); - if (ret < 0) - return ret; + if (partial_packet) { + ret = av_append_packet(pb, pkt, chunk_size); + } else + ret = av_get_packet(pb, pkt, chunk_size); + if (ret < 0) { + packet_read = 1; + break; + } + partial_packet = chunk_type == MVIh_TAG; pkt->stream_index = ea->video_stream_index; pkt->flags |= key; packet_read = 1; @@ -593,6 +610,8 @@ get_video_packet: } } + if (ret < 0 && partial_packet) + av_free_packet(pkt); return ret; } diff --git a/libavformat/ffm.h b/libavformat/ffm.h index 70c3e887a0..b392b8d8d8 100644 --- a/libavformat/ffm.h +++ b/libavformat/ffm.h @@ -1,21 +1,21 @@ /* - * FFM (avserver live feed) common header + * FFM (ffserver live feed) common header * Copyright (c) 2001 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -54,6 +54,7 @@ typedef struct FFMContext { int64_t dts; uint8_t *packet_ptr, *packet_end; uint8_t packet[FFM_PACKET_SIZE]; + int64_t start_time; } FFMContext; #endif /* AVFORMAT_FFM_H */ diff --git a/libavformat/ffmdec.c b/libavformat/ffmdec.c index e6730eb2f5..028f1ee797 100644 --- a/libavformat/ffmdec.c +++ b/libavformat/ffmdec.c @@ -1,21 +1,21 @@ /* - * FFM (avserver live feed) demuxer + * FFM (ffserver live feed) demuxer * Copyright (c) 2001 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -24,6 +24,7 @@ #include "avformat.h" #include "internal.h" #include "ffm.h" +#include "avio_internal.h" static int ffm_is_avail_data(AVFormatContext *s, int size) { @@ -60,7 +61,7 @@ static int ffm_resync(AVFormatContext *s, int state) { av_log(s, AV_LOG_ERROR, "resyncing\n"); while (state != PACKET_ID) { - if (s->pb->eof_reached) { + if (url_feof(s->pb)) { av_log(s, AV_LOG_ERROR, "cannot find FFM syncword\n"); return -1; } @@ -89,6 +90,11 @@ static int ffm_read_data(AVFormatContext *s, if (avio_tell(pb) == ffm->file_size) avio_seek(pb, ffm->packet_size, SEEK_SET); retry_read: + if (pb->buffer_size != ffm->packet_size) { + int64_t tell = avio_tell(pb); + ffio_set_buf_size(pb, ffm->packet_size); + avio_seek(pb, tell, SEEK_SET); + } id = avio_rb16(pb); /* PACKET_ID */ if (id != PACKET_ID) if (ffm_resync(s, id) < 0) @@ -105,8 +111,8 @@ static int ffm_read_data(AVFormatContext *s, if (ffm->first_packet || (frame_offset & 0x8000)) { if (!frame_offset) { /* This packet has no frame headers in it */ - if (avio_tell(pb) >= ffm->packet_size * 3) { - avio_seek(pb, -ffm->packet_size * 2, SEEK_CUR); + if (avio_tell(pb) >= ffm->packet_size * 3LL) { + avio_seek(pb, -ffm->packet_size * 2LL, SEEK_CUR); goto retry_read; } /* This is bad, we cannot find a valid frame header */ @@ -243,7 +249,7 @@ static int ffm_read_header(AVFormatContext *s) /* get also filesize */ if (pb->seekable) { ffm->file_size = avio_size(pb); - if (ffm->write_index) + if (ffm->write_index && 0) adjust_write_index(s); } else { ffm->file_size = (UINT64_C(1) << 63) - 1; @@ -380,7 +386,9 @@ static int ffm_read_packet(AVFormatContext *s, AVPacket *pkt) duration = AV_RB24(ffm->header + 5); - av_new_packet(pkt, size); + if (av_new_packet(pkt, size) < 0) { + return AVERROR(ENOMEM); + } pkt->stream_index = ffm->header[0]; if ((unsigned)pkt->stream_index >= s->nb_streams) { av_log(s, AV_LOG_ERROR, "invalid stream index %d\n", pkt->stream_index); @@ -422,11 +430,25 @@ static int ffm_seek(AVFormatContext *s, int stream_index, int64_t wanted_pts, in av_dlog(s, "wanted_pts=%0.6f\n", wanted_pts / 1000000.0); /* find the position using linear interpolation (better than dichotomy in typical cases) */ - pos_min = FFM_PACKET_SIZE; - pos_max = ffm->file_size - FFM_PACKET_SIZE; + if (ffm->write_index && ffm->write_index < ffm->file_size) { + if (get_dts(s, FFM_PACKET_SIZE) < wanted_pts) { + pos_min = FFM_PACKET_SIZE; + pos_max = ffm->write_index - FFM_PACKET_SIZE; + } else { + pos_min = ffm->write_index; + pos_max = ffm->file_size - FFM_PACKET_SIZE; + } + } else { + pos_min = FFM_PACKET_SIZE; + pos_max = ffm->file_size - FFM_PACKET_SIZE; + } while (pos_min <= pos_max) { pts_min = get_dts(s, pos_min); pts_max = get_dts(s, pos_max); + if (pts_min > wanted_pts || pts_max <= wanted_pts) { + pos = pts_min > wanted_pts ? pos_min : pos_max; + goto found; + } /* linear interpolation */ pos1 = (double)(pos_max - pos_min) * (double)(wanted_pts - pts_min) / (double)(pts_max - pts_min); @@ -471,7 +493,7 @@ static int ffm_probe(AVProbeData *p) AVInputFormat ff_ffm_demuxer = { .name = "ffm", - .long_name = NULL_IF_CONFIG_SMALL("FFM (AVserver live feed)"), + .long_name = NULL_IF_CONFIG_SMALL("FFM (FFserver live feed)"), .priv_data_size = sizeof(FFMContext), .read_probe = ffm_probe, .read_header = ffm_read_header, diff --git a/libavformat/ffmenc.c b/libavformat/ffmenc.c index 386487f1f9..d43b7d44da 100644 --- a/libavformat/ffmenc.c +++ b/libavformat/ffmenc.c @@ -1,28 +1,28 @@ /* - * FFM (avserver live feed) muxer + * FFM (ffserver live feed) muxer * Copyright (c) 2001 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <assert.h> - #include "libavutil/intreadwrite.h" #include "libavutil/intfloat.h" +#include "libavutil/avassert.h" +#include "libavutil/parseutils.h" #include "avformat.h" #include "internal.h" #include "ffm.h" @@ -36,7 +36,7 @@ static void flush_packet(AVFormatContext *s) fill_size = ffm->packet_end - ffm->packet_ptr; memset(ffm->packet_ptr, 0, fill_size); - assert(avio_tell(pb) % ffm->packet_size == 0); + av_assert1(avio_tell(pb) % ffm->packet_size == 0); /* put header */ avio_wb16(pb, PACKET_ID); @@ -86,11 +86,18 @@ static void ffm_write_data(AVFormatContext *s, 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 */ @@ -189,7 +196,7 @@ static int ffm_write_header(AVFormatContext *s) /* init packet mux */ ffm->packet_ptr = ffm->packet; ffm->packet_end = ffm->packet + ffm->packet_size - FFM_HEADER_SIZE; - assert(ffm->packet_end >= ffm->packet); + av_assert0(ffm->packet_end >= ffm->packet); ffm->frame_offset = 0; ffm->dts = 0; ffm->first_packet = 1; @@ -199,11 +206,12 @@ static int ffm_write_header(AVFormatContext *s) static int ffm_write_packet(AVFormatContext *s, AVPacket *pkt) { + FFMContext *ffm = s->priv_data; int64_t dts; uint8_t header[FRAME_HEADER_SIZE+4]; int header_size = FRAME_HEADER_SIZE; - dts = pkt->dts; + dts = ffm->start_time + pkt->dts; /* packet size & key_frame */ header[0] = pkt->stream_index; header[1] = 0; @@ -211,7 +219,7 @@ static int ffm_write_packet(AVFormatContext *s, AVPacket *pkt) header[1] |= FLAG_KEY_FRAME; AV_WB24(header+2, pkt->size); AV_WB24(header+5, pkt->duration); - AV_WB64(header+8, pkt->pts); + AV_WB64(header+8, ffm->start_time + pkt->pts); if (pkt->pts != pkt->dts) { header[1] |= FLAG_DTS; AV_WB32(header+16, pkt->pts - pkt->dts); @@ -236,8 +244,7 @@ static int ffm_write_trailer(AVFormatContext *s) AVOutputFormat ff_ffm_muxer = { .name = "ffm", - .long_name = NULL_IF_CONFIG_SMALL("FFM (AVserver live feed)"), - .mime_type = "", + .long_name = NULL_IF_CONFIG_SMALL("FFM (FFserver live feed)"), .extensions = "ffm", .priv_data_size = sizeof(FFMContext), .audio_codec = AV_CODEC_ID_MP2, diff --git a/libavformat/ffmeta.h b/libavformat/ffmeta.h index a5380ca13d..ae8778d614 100644 --- a/libavformat/ffmeta.h +++ b/libavformat/ffmeta.h @@ -2,20 +2,20 @@ * Common data for metadata muxer/demuxer * Copyright (c) 2010 Anton Khirnov * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/ffmetadec.c b/libavformat/ffmetadec.c index 7dbf7ed35d..4bdc98a3d4 100644 --- a/libavformat/ffmetadec.c +++ b/libavformat/ffmetadec.c @@ -2,20 +2,20 @@ * Metadata demuxer * Copyright (c) 2010 Anton Khirnov * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -50,7 +50,7 @@ static void get_line(AVIOContext *s, uint8_t *buf, int size) buf[i++] = c; } buf[i] = 0; - } while (!s->eof_reached && (buf[0] == ';' || buf[0] == '#' || buf[0] == 0)); + } while (!url_feof(s) && (buf[0] == ';' || buf[0] == '#' || buf[0] == 0)); } static AVChapter *read_chapter(AVFormatContext *s) @@ -128,7 +128,7 @@ static int read_header(AVFormatContext *s) AVDictionary **m = &s->metadata; uint8_t line[1024]; - while(!s->pb->eof_reached) { + while(!url_feof(s->pb)) { get_line(s->pb, line, sizeof(line)); if (!memcmp(line, ID_STREAM, strlen(ID_STREAM))) { diff --git a/libavformat/ffmetaenc.c b/libavformat/ffmetaenc.c index 19fe6c960f..a9adbb1d19 100644 --- a/libavformat/ffmetaenc.c +++ b/libavformat/ffmetaenc.c @@ -2,20 +2,20 @@ * Metadata muxer * Copyright (c) 2010 Anton Khirnov * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/file.c b/libavformat/file.c index c552a9eaa5..209957b9d3 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 */ @@ -34,6 +34,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 */ @@ -58,13 +66,15 @@ static const AVClass file_class = { static int file_read(URLContext *h, unsigned char *buf, int size) { FileContext *c = h->priv_data; - return read(c->fd, buf, size); + int 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 = write(c->fd, buf, size); + return (-1 == r)?AVERROR(errno):r; } static int file_get_handle(URLContext *h) @@ -93,6 +103,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); @@ -114,6 +125,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; } @@ -125,9 +139,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); diff --git a/libavformat/filmstripdec.c b/libavformat/filmstripdec.c index 7c327e9324..b41fdb73e5 100644 --- a/libavformat/filmstripdec.c +++ b/libavformat/filmstripdec.c @@ -2,20 +2,20 @@ * Adobe Filmstrip demuxer * Copyright (c) 2010 Peter Ross * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -45,7 +45,7 @@ static int read_header(AVFormatContext *s) avio_seek(pb, avio_size(pb) - 36, SEEK_SET); if (avio_rb32(pb) != RAND_TAG) { - av_log(s, AV_LOG_ERROR, "magic number not found"); + av_log(s, AV_LOG_ERROR, "magic number not found\n"); return AVERROR_INVALIDDATA; } @@ -80,7 +80,7 @@ static int read_packet(AVFormatContext *s, FilmstripDemuxContext *film = s->priv_data; AVStream *st = s->streams[0]; - if (s->pb->eof_reached) + if (url_feof(s->pb)) return AVERROR(EIO); pkt->dts = avio_tell(s->pb) / (st->codec->width * (st->codec->height + film->leading) * 4); pkt->size = av_get_packet(s->pb, pkt, st->codec->width * st->codec->height * 4); diff --git a/libavformat/filmstripenc.c b/libavformat/filmstripenc.c index 90d9a7685c..99fbf0b570 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 */ diff --git a/libavformat/flacdec.c b/libavformat/flacdec.c index f19f95d901..bbb28f435a 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 */ @@ -28,6 +28,8 @@ #include "vorbiscomment.h" #include "libavcodec/bytestream.h" +#define RETURN_ERROR(code) do { ret = (code); goto fail; } while (0) + static int parse_picture(AVFormatContext *s, uint8_t *buf, int buf_size) { const CodecMime *mime = ff_id3v2_mime_tags; @@ -47,8 +49,7 @@ static int 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; } @@ -84,8 +85,7 @@ static int 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) { @@ -111,8 +111,7 @@ static int parse_picture(AVFormatContext *s, uint8_t *buf, int buf_size) goto fail; } if (!(data = av_malloc(len))) { - ret = AVERROR(ENOMEM); - goto fail; + RETURN_ERROR(AVERROR(ENOMEM)); } if (avio_read(pb, data, len) != len) { av_log(s, AV_LOG_ERROR, "Error reading attached picture data.\n"); @@ -123,8 +122,7 @@ static int 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); @@ -165,7 +163,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 */ @@ -175,7 +173,7 @@ static int flac_read_header(AVFormatContext *s) } /* process metadata blocks */ - while (!s->pb->eof_reached && !metadata_last) { + while (!url_feof(s->pb) && !metadata_last) { avio_read(s->pb, header, 4); avpriv_flac_parse_block_header(header, &metadata_last, &metadata_type, &metadata_size); @@ -190,8 +188,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 */ @@ -205,12 +202,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; @@ -232,24 +227,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 = parse_picture(s, buffer, metadata_size); av_freep(&buffer); @@ -260,8 +256,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) { @@ -274,6 +269,10 @@ static int flac_read_header(AVFormatContext *s) } return 0; + +fail: + av_free(buffer); + return ret; } static int flac_probe(AVProbeData *p) diff --git a/libavformat/flacenc.c b/libavformat/flacenc.c index b770623b3c..b625278022 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 */ @@ -41,7 +41,7 @@ static int flac_write_block_padding(AVIOContext *pb, unsigned int n_padding_byte static int flac_write_block_comment(AVIOContext *pb, AVDictionary **m, int last_block, int bitexact) { - const char *vendor = bitexact ? "Libav" : LIBAVFORMAT_IDENT; + const char *vendor = bitexact ? "ffmpeg" : LIBAVFORMAT_IDENT; unsigned int len, count; uint8_t *p, *p0; @@ -69,6 +69,15 @@ static int flac_write_header(struct AVFormatContext *s) int ret; AVCodecContext *codec = s->streams[0]->codec; + if (s->nb_streams > 1) { + av_log(s, AV_LOG_ERROR, "only one stream is supported\n"); + return AVERROR(EINVAL); + } + if (codec->codec_id != AV_CODEC_ID_FLAC) { + av_log(s, AV_LOG_ERROR, "unsupported codec\n"); + return AVERROR(EINVAL); + } + ret = ff_flac_write_header(s->pb, codec, 0); if (ret) return ret; diff --git a/libavformat/flacenc.h b/libavformat/flacenc.h index 2edda67043..e83ee32aeb 100644 --- a/libavformat/flacenc.h +++ b/libavformat/flacenc.h @@ -2,20 +2,20 @@ * raw FLAC muxer * Copyright (C) 2009 Justin Ruggles * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/flacenc_header.c b/libavformat/flacenc_header.c index c1f7c86554..e16c14bf53 100644 --- a/libavformat/flacenc_header.c +++ b/libavformat/flacenc_header.c @@ -2,20 +2,20 @@ * raw FLAC muxer * Copyright (C) 2009 Justin Ruggles * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/flic.c b/libavformat/flic.c index 60197638e5..db3f5d8dfa 100644 --- a/libavformat/flic.c +++ b/libavformat/flic.c @@ -2,20 +2,20 @@ * FLI/FLC Animation File Demuxer * Copyright (c) 2003 The ffmpeg Project * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -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; @@ -187,7 +187,7 @@ static int flic_read_header(AVFormatContext *s) (magic_number == FLIC_FILE_MAGIC_3)) { avpriv_set_pts_info(st, 64, speed, 1000); } else { - av_log(s, AV_LOG_INFO, "Invalid or unsupported magic chunk in file\n"); + av_log(s, AV_LOG_ERROR, "Invalid or unsupported magic chunk in file\n"); return AVERROR_INVALIDDATA; } diff --git a/libavformat/flv.h b/libavformat/flv.h index fe0fc90d83..db9468f469 100644 --- a/libavformat/flv.h +++ b/libavformat/flv.h @@ -1,22 +1,22 @@ /* * FLV common header * - * Copyright (c) 2006 The Libav Project + * Copyright (c) 2006 The FFmpeg Project * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -46,6 +46,11 @@ #define AMF_END_OF_OBJECT 0x09 +#define KEYFRAMES_TAG "keyframes" +#define KEYFRAMES_TIMESTAMP_TAG "times" +#define KEYFRAMES_BYTEOFFSET_TAG "filepositions" + + enum { FLV_HEADER_FLAG_HASVIDEO = 1, FLV_HEADER_FLAG_HASAUDIO = 4, @@ -58,6 +63,13 @@ enum { }; enum { + FLV_STREAM_TYPE_VIDEO, + FLV_STREAM_TYPE_AUDIO, + FLV_STREAM_TYPE_DATA, + FLV_STREAM_TYPE_NB, +}; + +enum { FLV_MONO = 0, FLV_STEREO = 1, }; @@ -95,12 +107,16 @@ enum { FLV_CODECID_VP6A = 5, FLV_CODECID_SCREEN2 = 6, FLV_CODECID_H264 = 7, + FLV_CODECID_REALH263= 8, + FLV_CODECID_MPEG4 = 9, }; enum { - FLV_FRAME_KEY = 1 << FLV_VIDEO_FRAMETYPE_OFFSET, - FLV_FRAME_INTER = 2 << FLV_VIDEO_FRAMETYPE_OFFSET, - FLV_FRAME_DISP_INTER = 3 << FLV_VIDEO_FRAMETYPE_OFFSET, + FLV_FRAME_KEY = 1 << FLV_VIDEO_FRAMETYPE_OFFSET, ///< key frame (for AVC, a seekable frame) + FLV_FRAME_INTER = 2 << FLV_VIDEO_FRAMETYPE_OFFSET, ///< inter frame (for AVC, a non-seekable frame) + FLV_FRAME_DISP_INTER = 3 << FLV_VIDEO_FRAMETYPE_OFFSET, ///< disposable inter frame (H.263 only) + FLV_FRAME_GENERATED_KEY = 4 << FLV_VIDEO_FRAMETYPE_OFFSET, ///< generated key frame (reserved for server use only) + FLV_FRAME_VIDEO_INFO_CMD = 5 << FLV_VIDEO_FRAMETYPE_OFFSET, ///< video info/command frame }; typedef enum { diff --git a/libavformat/flvdec.c b/libavformat/flvdec.c index 868cc6b793..24302f6b53 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 */ @@ -36,18 +36,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 { @@ -56,6 +52,7 @@ typedef struct { } validate_index[2]; int validate_next; int validate_count; + int searched_for_end; } FLVContext; static int flv_probe(AVProbeData *p) @@ -75,6 +72,11 @@ static AVStream *create_stream(AVFormatContext *s, int codec_type) if (!st) return NULL; st->codec->codec_type = codec_type; + if(s->nb_streams>=3 ||( s->nb_streams==2 + && s->streams[0]->codec->codec_type != AVMEDIA_TYPE_DATA + && s->streams[1]->codec->codec_type != AVMEDIA_TYPE_DATA)) + s->ctx_flags &= ~AVFMTCTX_NOHEADER; + avpriv_set_pts_info(st, 32, 1, 1000); /* 32 bit pts in ms */ return st; } @@ -124,8 +126,6 @@ static int flv_same_audio_codec(AVCodecContext *acodec, int flags) default: return acodec->codec_tag == (flv_codecid >> FLV_AUDIO_CODECID_OFFSET); } - - return 0; } static void flv_set_audio_codec(AVFormatContext *s, AVStream *astream, AVCodecContext *acodec, int flv_codecid) { @@ -196,14 +196,13 @@ static int flv_same_video_codec(AVCodecContext *vcodec, int flags) default: return vcodec->codec_tag == flv_codecid; } - - return 0; } static int flv_set_video_codec(AVFormatContext *s, AVStream *vstream, int flv_codecid) { AVCodecContext *vcodec = vstream->codec; switch(flv_codecid) { 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; case FLV_CODECID_SCREEN2: vcodec->codec_id = AV_CODEC_ID_FLASHSV2; break; case FLV_CODECID_VP6 : vcodec->codec_id = AV_CODEC_ID_VP6F ; @@ -212,13 +211,16 @@ static int flv_set_video_codec(AVFormatContext *s, AVStream *vstream, int flv_co vcodec->codec_id = AV_CODEC_ID_VP6A; if(vcodec->extradata_size != 1) { vcodec->extradata_size = 1; - vcodec->extradata = av_malloc(1); + vcodec->extradata = av_malloc(1 + FF_INPUT_BUFFER_PADDING_SIZE); } vcodec->extradata[0] = avio_r8(s->pb); return 1; // 1 byte body size adjustment for flv_read_packet() case FLV_CODECID_H264: vcodec->codec_id = AV_CODEC_ID_H264; return 3; // not 4, reading packet type will consume one byte + case FLV_CODECID_MPEG4: + vcodec->codec_id = AV_CODEC_ID_MPEG4; + return 3; default: av_log(s, AV_LOG_INFO, "Unsupported video codec (%x)\n", flv_codecid); vcodec->codec_tag = flv_codecid; @@ -243,54 +245,51 @@ static int amf_get_string(AVIOContext *ioc, char *buffer, int buffsize) { static int parse_keyframes_index(AVFormatContext *s, AVIOContext *ioc, AVStream *vstream, int64_t max_pos) { FLVContext *flv = s->priv_data; - unsigned int arraylen = 0, timeslen = 0, fileposlen = 0, i; - double num_val; + unsigned int timeslen = 0, fileposlen = 0, i; char str_val[256]; int64_t *times = NULL; int64_t *filepositions = NULL; int ret = AVERROR(ENOSYS); int64_t initial_pos = avio_tell(ioc); + if(vstream->nb_index_entries>0){ + av_log(s, AV_LOG_WARNING, "Skiping duplicate index\n"); + return 0; + } + if (s->flags & AVFMT_FLAG_IGNIDX) return 0; while (avio_tell(ioc) < max_pos - 2 && amf_get_string(ioc, str_val, sizeof(str_val)) > 0) { - int64_t* current_array; + int64_t** current_array; + unsigned int arraylen; // Expect array object in context if (avio_r8(ioc) != AMF_DATA_TYPE_ARRAY) break; arraylen = avio_rb32(ioc); - if (arraylen >> 28) + if(arraylen>>28) break; - /* - * Expect only 'times' or 'filepositions' sub-arrays in other case refuse to use such metadata - * for indexing - */ - if (!strcmp(KEYFRAMES_TIMESTAMP_TAG, str_val) && !times) { - if (!(times = av_mallocz(sizeof(*times) * arraylen))) { - ret = AVERROR(ENOMEM); - goto finish; - } - 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; - } - fileposlen = arraylen; - current_array = filepositions; - } else // unexpected metatag inside keyframes, will not use such metadata for indexing + if (!strcmp(KEYFRAMES_TIMESTAMP_TAG , str_val) && !times){ + current_array= × + timeslen= arraylen; + }else if (!strcmp(KEYFRAMES_BYTEOFFSET_TAG, str_val) && !filepositions){ + current_array= &filepositions; + fileposlen= arraylen; + }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 @@ -300,7 +299,7 @@ static int parse_keyframes_index(AVFormatContext *s, AVIOContext *ioc, AVStream } } - 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); @@ -310,16 +309,15 @@ static int parse_keyframes_index(AVFormatContext *s, AVIOContext *ioc, AVStream 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; } @@ -346,10 +344,10 @@ static int amf_parse_object(AVFormatContext *s, AVStream *astream, AVStream *vst return -1; break; case AMF_DATA_TYPE_OBJECT: - if ((vstream || astream) && key && !strcmp(KEYFRAMES_TAG, key) && depth == 1) + if ((vstream || astream) && ioc->seekable && key && !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) { if (amf_parse_object(s, astream, vstream, str_val, max_pos, depth + 1) < 0) @@ -424,6 +422,11 @@ static int amf_parse_object(AVFormatContext *s, AVStream *astream, AVStream *vst } } + 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") || @@ -453,13 +456,12 @@ static int amf_parse_object(AVFormatContext *s, AVStream *astream, AVStream *vst static int flv_read_metabody(AVFormatContext *s, int64_t next_pos) { AMFDataType type; - AVStream *stream, *astream, *vstream; + AVStream *stream, *astream, *vstream, *dstream; AVIOContext *ioc; int i; char buffer[11]; //only needs to hold the string "onMetaData". Anything longer is something we don't want. - astream = NULL; - vstream = NULL; + vstream = astream = dstream = NULL; ioc = s->pb; //first object needs to be "onMetaData" string @@ -477,8 +479,9 @@ static int flv_read_metabody(AVFormatContext *s, int64_t next_pos) { //find the streams now so that amf_parse_object doesn't need to do 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) vstream = stream; + 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) @@ -511,6 +514,8 @@ static int flv_read_header(AVFormatContext *s) 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); @@ -523,9 +528,10 @@ 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; } @@ -630,10 +636,12 @@ out: static int flv_read_packet(AVFormatContext *s, AVPacket *pkt) { FLVContext *flv = s->priv_data; - int ret, i, type, size, flags, is_audio; + int ret, i, type, size, flags; + int stream_type=-1; int64_t next, 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; for(;;avio_skip(s->pb, 4)){ /* pkt size is repeated at end. skip it */ @@ -643,7 +651,7 @@ static int flv_read_packet(AVFormatContext *s, AVPacket *pkt) dts = avio_rb24(s->pb); dts |= avio_r8(s->pb) << 24; av_dlog(s, "type:%d, size:%d, dts:%"PRId64"\n", type, size, dts); - if (s->pb->eof_reached) + if (url_feof(s->pb)) return AVERROR_EOF; avio_skip(s->pb, 3); /* stream id, always 0 */ flags = 0; @@ -670,22 +678,26 @@ 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) { + if (size > 13+1+4 && dts == 0) { // Header-type metadata stuff + flv_read_metabody(s, next); + goto skip; + } else if (dts != 0) { // Script-data "special" metadata frames - don't skip + stream_type=FLV_STREAM_TYPE_DATA; + } else { goto skip; + } } 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; @@ -698,24 +710,33 @@ static int flv_read_packet(AVFormatContext *s, AVPacket *pkt) /* 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 && + flv_same_audio_codec(st->codec, flags)) { break; } } else - if (!is_audio && st->codec->codec_type == AVMEDIA_TYPE_VIDEO) { - if (flv_same_video_codec(st->codec, flags)) { + if (stream_type == FLV_STREAM_TYPE_VIDEO) { + if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO && + flv_same_video_codec(st->codec, flags)) { break; } + } else if (stream_type == FLV_STREAM_TYPE_DATA) { + if (st->codec->codec_type == AVMEDIA_TYPE_DATA) + break; } } if(i == s->nb_streams){ + av_log(s, AV_LOG_WARNING, "Stream discovered after head already parsed\n"); st = create_stream(s, - is_audio ? AVMEDIA_TYPE_AUDIO : AVMEDIA_TYPE_VIDEO); + (int[]){AVMEDIA_TYPE_VIDEO, AVMEDIA_TYPE_AUDIO, AVMEDIA_TYPE_DATA}[stream_type]); + if (!st) + return AVERROR(ENOMEM); + } - av_dlog(s, "%d %X %d \n", is_audio, flags, st->discard); - if( (st->discard >= AVDISCARD_NONKEY && !((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_KEY || is_audio)) - ||(st->discard >= AVDISCARD_BIDIR && ((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_DISP_INTER && !is_audio)) + av_dlog(s, "%d %X %d \n", stream_type, flags, st->discard); + if( (st->discard >= AVDISCARD_NONKEY && !((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_KEY || (stream_type == FLV_STREAM_TYPE_AUDIO))) + ||(st->discard >= AVDISCARD_BIDIR && ((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_DISP_INTER && (stream_type == FLV_STREAM_TYPE_VIDEO))) || st->discard >= AVDISCARD_ALL ){ avio_seek(s->pb, next, SEEK_SET); @@ -727,22 +748,30 @@ static int flv_read_packet(AVFormatContext *s, AVPacket *pkt) } // 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) >> FLV_AUDIO_SAMPLERATE_OFFSET) >> 3); @@ -762,15 +791,16 @@ static int flv_read_packet(AVFormatContext *s, AVPacket *pkt) 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); } 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) { int32_t cts = (avio_rb24(s->pb)+0xff800000)^0xff800000; // sign extension pts = dts + cts; if (cts < 0) { // dts are wrong @@ -780,9 +810,9 @@ static int flv_read_packet(AVFormatContext *s, AVPacket *pkt) if (flv->wrong_dts) dts = AV_NOPTS_VALUE; } - if (type == 0) { + if (type == 0 && (!st->codec->extradata || st->codec->codec_id == AV_CODEC_ID_AAC)) { if (st->codec->extradata) { - if ((ret = flv_queue_extradata(flv, s->pb, is_audio, size)) < 0) + if ((ret = flv_queue_extradata(flv, s->pb, stream_type, size)) < 0) return ret; ret = AVERROR(EAGAIN); goto leave; @@ -791,8 +821,8 @@ static int flv_read_packet(AVFormatContext *s, AVPacket *pkt) return ret; if (st->codec->codec_id == AV_CODEC_ID_AAC) { MPEG4AudioConfig cfg; - avpriv_mpeg4audio_get_config(&cfg, st->codec->extradata, - st->codec->extradata_size * 8, 1); + if (avpriv_mpeg4audio_get_config(&cfg, st->codec->extradata, + st->codec->extradata_size * 8, 1) >= 0) { st->codec->channels = cfg.channels; if (cfg.ext_sample_rate) st->codec->sample_rate = cfg.ext_sample_rate; @@ -800,6 +830,7 @@ static int flv_read_packet(AVFormatContext *s, AVPacket *pkt) 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); @@ -814,33 +845,31 @@ static int flv_read_packet(AVFormatContext *s, AVPacket *pkt) } 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; + if (ret < 0) + 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: diff --git a/libavformat/flvenc.c b/libavformat/flvenc.c index 8aeea968b4..c528bf5bef 100644 --- a/libavformat/flvenc.c +++ b/libavformat/flvenc.c @@ -1,41 +1,44 @@ /* * 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; @@ -210,8 +214,9 @@ static int flv_write_header(AVFormatContext *s) } video_enc = enc; if (enc->codec_tag == 0) { - av_log(s, AV_LOG_ERROR, "video codec not compatible with flv\n"); - return -1; + av_log(s, AV_LOG_ERROR, "Video codec '%s' for stream %d is not compatible with FLV\n", + avcodec_get_name(enc->codec_id), i); + return AVERROR(EINVAL); } break; case AVMEDIA_TYPE_AUDIO: @@ -221,14 +226,16 @@ static int flv_write_header(AVFormatContext *s) break; case AVMEDIA_TYPE_DATA: if (enc->codec_id != AV_CODEC_ID_TEXT) { - av_log(s, AV_LOG_ERROR, "codec not compatible with flv\n"); + av_log(s, AV_LOG_ERROR, "Data codec '%s' for stream %d is not compatible with FLV\n", + avcodec_get_name(enc->codec_id), i); return AVERROR_INVALIDDATA; } data_enc = enc; break; default: - av_log(s, AV_LOG_ERROR, "codec not compatible with flv\n"); - return -1; + av_log(s, AV_LOG_ERROR, "Codec type '%s' for stream %d is not compatible with FLV\n", + av_get_media_type_string(enc->codec_type), i); + return AVERROR(EINVAL); } avpriv_set_pts_info(s->streams[i], 32, 1, 1000); /* 32 bit pts in ms */ @@ -327,6 +334,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); @@ -353,7 +376,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); @@ -396,7 +419,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); } @@ -421,12 +444,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_VP6 || enc->codec_id == AV_CODEC_ID_VP6F || - enc->codec_id == AV_CODEC_ID_AAC) + enc->codec_id == AV_CODEC_ID_VP6A || enc->codec_id == AV_CODEC_ID_AAC) flags_size = 2; - else if (enc->codec_id == AV_CODEC_ID_H264) + else if (enc->codec_id == AV_CODEC_ID_H264 || enc->codec_id == AV_CODEC_ID_MPEG4) flags_size = 5; else flags_size = 1; @@ -438,9 +461,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; @@ -448,7 +471,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; @@ -459,11 +482,18 @@ 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) { + av_log(s, AV_LOG_ERROR, "Malformed AAC bitstream detected: " + "use audio bistream filter 'aac_adtstoasc' to fix it " + "('-bsf:a aac_adtstoasc' option with ffmpeg)\n"); + return AVERROR_INVALIDDATA; + } if (flv->delay == AV_NOPTS_VALUE) flv->delay = -pkt->dts; @@ -512,16 +542,17 @@ 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) + avio_w8(pb,0); + if (enc->codec_id == AV_CODEC_ID_VP6F || enc->codec_id == AV_CODEC_ID_VP6A) avio_w8(pb, enc->extradata_size ? enc->extradata[0] : 0); else if (enc->codec_id == AV_CODEC_ID_AAC) - avio_w8(pb, 1); // AAC raw - else if (enc->codec_id == AV_CODEC_ID_H264) { - avio_w8(pb, 1); // AVC NALU - avio_wb24(pb, pkt->pts - pkt->dts); + avio_w8(pb,1); // AAC raw + 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); } avio_write(pb, data ? data : pkt->data, size); diff --git a/libavformat/framecrcenc.c b/libavformat/framecrcenc.c index de6fa2b960..8a6974891e 100644 --- a/libavformat/framecrcenc.c +++ b/libavformat/framecrcenc.c @@ -2,24 +2,25 @@ * frame CRC encoder (for codec/format testing) * Copyright (c) 2002 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "libavutil/adler32.h" +#include "libavutil/avstring.h" #include "avformat.h" #include "internal.h" @@ -28,8 +29,13 @@ static int framecrc_write_packet(struct AVFormatContext *s, AVPacket *pkt) uint32_t crc = av_adler32_update(0, pkt->data, pkt->size); char buf[256]; - snprintf(buf, sizeof(buf), "%d, %10"PRId64", %10"PRId64", %8d, %8d, 0x%08x\n", + snprintf(buf, sizeof(buf), "%d, %10"PRId64", %10"PRId64", %8d, %8d, 0x%08x", pkt->stream_index, pkt->dts, pkt->pts, pkt->duration, pkt->size, crc); + if (pkt->flags != AV_PKT_FLAG_KEY) + av_strlcatf(buf, sizeof(buf), ", F=0x%0X", pkt->flags); + if (pkt->side_data_elems) + av_strlcatf(buf, sizeof(buf), ", S=%d", pkt->side_data_elems); + av_strlcatf(buf, sizeof(buf), "\n"); avio_write(s->pb, buf, strlen(buf)); avio_flush(s->pb); return 0; @@ -38,7 +44,6 @@ static int framecrc_write_packet(struct AVFormatContext *s, AVPacket *pkt) AVOutputFormat ff_framecrc_muxer = { .name = "framecrc", .long_name = NULL_IF_CONFIG_SMALL("framecrc testing"), - .extensions = "", .audio_codec = AV_CODEC_ID_PCM_S16LE, .video_codec = AV_CODEC_ID_RAWVIDEO, .write_header = ff_framehash_write_header, diff --git a/libavformat/g722.c b/libavformat/g722.c index 8052939c79..1a87c7d28c 100644 --- a/libavformat/g722.c +++ b/libavformat/g722.c @@ -2,23 +2,24 @@ * g722 raw demuxer * Copyright (c) 2010 Martin Storsjo * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "libavutil/avassert.h" #include "avformat.h" #include "internal.h" #include "rawdec.h" @@ -39,7 +40,7 @@ static int g722_read_header(AVFormatContext *s) st->codec->bits_per_coded_sample = av_get_bits_per_sample(st->codec->codec_id); - assert(st->codec->bits_per_coded_sample > 0); + av_assert0(st->codec->bits_per_coded_sample > 0); avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate); return 0; diff --git a/libavformat/g723_1.c b/libavformat/g723_1.c index 2674ce5327..7d97a4ba8d 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 */ @@ -80,6 +80,6 @@ AVInputFormat ff_g723_1_demuxer = { .long_name = NULL_IF_CONFIG_SMALL("G.723.1"), .read_header = g723_1_init, .read_packet = g723_1_read_packet, - .extensions = "tco", + .extensions = "tco,rco,g723_1", .flags = AVFMT_GENERIC_INDEX }; diff --git a/libavformat/g729dec.c b/libavformat/g729dec.c new file mode 100644 index 0000000000..794558ef10 --- /dev/null +++ b/libavformat/g729dec.c @@ -0,0 +1,103 @@ +/* + * G.729 raw format demuxer + * Copyright (c) 2011 Vladimir Voroshilov + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "avformat.h" +#include "internal.h" +#include "libavutil/log.h" +#include "libavutil/opt.h" + +typedef struct G729DemuxerContext { + AVClass *class; + int bit_rate; +} G729DemuxerContext; + +static int g729_read_header(AVFormatContext *s) +{ + AVStream* st; + G729DemuxerContext *s1 = s->priv_data; + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_id = AV_CODEC_ID_G729; + st->codec->sample_rate = 8000; + st->codec->channels = 1; + + if (s1 && s1->bit_rate) { + s->bit_rate = s1->bit_rate; + } + + if (s->bit_rate == 0) { + av_log(s, AV_LOG_DEBUG, "No bitrate specified. Assuming 8000 b/s\n"); + s->bit_rate = 8000; + } + + if (s->bit_rate == 6400) { + st->codec->block_align = 8; + } else if (s->bit_rate == 8000) { + st->codec->block_align = 10; + } else { + av_log(s, AV_LOG_ERROR, "Only 8000 b/s and 6400 b/s bitrates are supported. Provided: %d b/s\n", s->bit_rate); + return AVERROR_INVALIDDATA; + } + + avpriv_set_pts_info(st, st->codec->block_align << 3, 1, st->codec->sample_rate); + return 0; +} +static int g729_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + int ret; + + ret = av_get_packet(s->pb, pkt, s->streams[0]->codec->block_align); + + pkt->stream_index = 0; + if (ret < 0) + return ret; + + pkt->dts = pkt->pts = pkt->pos / s->streams[0]->codec->block_align; + + return ret; +} + +static const AVOption g729_options[] = { + { "bit_rate", "", offsetof(G729DemuxerContext, bit_rate), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, AV_OPT_FLAG_DECODING_PARAM }, + { NULL }, +}; + +static const AVClass g729_demuxer_class = { + .class_name = "g729 demuxer", + .item_name = av_default_item_name, + .option = g729_options, + .version = LIBAVUTIL_VERSION_INT, +}; + +AVInputFormat ff_g729_demuxer = { + .name = "g729", + .long_name = NULL_IF_CONFIG_SMALL("G.729 raw format demuxer"), + .priv_data_size = sizeof(G729DemuxerContext), + .read_header = g729_read_header, + .read_packet = g729_read_packet, + .flags = AVFMT_GENERIC_INDEX, + .extensions = "g729", + .priv_class = &g729_demuxer_class, +}; diff --git a/libavformat/gif.c b/libavformat/gif.c index eb2db46b3e..31b01018ad 100644 --- a/libavformat/gif.c +++ b/libavformat/gif.c @@ -2,20 +2,20 @@ * Animated GIF 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 */ diff --git a/libavformat/gopher.c b/libavformat/gopher.c index a149f7fd6b..9dc155ad11 100644 --- a/libavformat/gopher.c +++ b/libavformat/gopher.c @@ -5,20 +5,20 @@ * * based on libavformat/http.c, Copyright (c) 2000, 2001 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/gsmdec.c b/libavformat/gsmdec.c index f0cb2799bf..7ec3df54c3 100644 --- a/libavformat/gsmdec.c +++ b/libavformat/gsmdec.c @@ -47,7 +47,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; diff --git a/libavformat/gxf.c b/libavformat/gxf.c index 2593c0642b..0286439c44 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 */ @@ -30,9 +30,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 @@ -222,6 +244,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); @@ -235,7 +258,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); } } @@ -313,8 +338,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; @@ -325,6 +348,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]; @@ -359,10 +392,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 @@ -381,7 +425,7 @@ static int gxf_header(AVFormatContext *s) { #define READ_ONE() \ { \ - if (!max_interval-- || pb->eof_reached) \ + if (!max_interval-- || url_feof(pb)) \ goto out; \ tmp = tmp << 8 | avio_r8(pb); \ } @@ -443,7 +487,7 @@ static int gxf_packet(AVFormatContext *s, AVPacket *pkt) { int field_nr, field_info, skip = 0; int stream_index; if (!parse_packet_header(pb, &pkt_type, &pkt_len)) { - if (!pb->eof_reached) + if (!url_feof(pb)) av_log(s, AV_LOG_ERROR, "sync lost\n"); return -1; } @@ -495,7 +539,7 @@ static int gxf_packet(AVFormatContext *s, AVPacket *pkt) { return ret; } - return AVERROR(EIO); + return AVERROR_EOF; } static int gxf_seek(AVFormatContext *s, int stream_index, int64_t timestamp, int flags) { diff --git a/libavformat/gxf.h b/libavformat/gxf.h index c1ac399458..dcdcdefc2d 100644 --- a/libavformat/gxf.h +++ b/libavformat/gxf.h @@ -2,20 +2,20 @@ * GXF demuxer * copyright (c) 2006 Reimar Doeffinger * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/gxfenc.c b/libavformat/gxfenc.c index c5fb0aebaa..9bec3d52cf 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" @@ -29,6 +32,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; @@ -49,6 +64,7 @@ typedef struct GXFStreamContext { } GXFStreamContext; typedef struct GXFContext { + AVClass *av_class; uint32_t nb_fields; uint16_t audio_tracks; uint16_t mpeg_tracks; @@ -67,6 +83,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 { @@ -189,30 +206,33 @@ 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_timecode_auxiliary(AVIOContext *pb, GXFContext *gxf) { - avio_w8(pb, 0); /* fields */ - avio_w8(pb, 0); /* seconds */ - avio_w8(pb, 0); /* minutes */ - avio_w8(pb, 0); /* flags + hours */ + uint32_t timecode = GXF_TIMECODE(gxf->tc.color, gxf->tc.drop, + gxf->tc.hh, gxf->tc.mm, + gxf->tc.ss, gxf->tc.ff); + + 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; @@ -236,7 +256,7 @@ static int gxf_write_track_description(AVFormatContext *s, GXFStreamContext *sc, avio_w8(pb, TRACK_AUX); avio_w8(pb, 8); if (sc->track_type == 3) - gxf_write_timecode_auxiliary(pb, sc); + gxf_write_timecode_auxiliary(pb, gxf); else avio_wl64(pb, 0); } @@ -343,8 +363,9 @@ static int gxf_write_map_packet(AVFormatContext *s, int rewrite) if (!rewrite) { if (!(gxf->map_offsets_nb % 30)) { - gxf->map_offsets = av_realloc(gxf->map_offsets, - (gxf->map_offsets_nb+30)*sizeof(*gxf->map_offsets)); + gxf->map_offsets = av_realloc_f(gxf->map_offsets, + sizeof(*gxf->map_offsets), + gxf->map_offsets_nb+30); if (!gxf->map_offsets) { av_log(s, AV_LOG_ERROR, "could not realloc map offsets\n"); return -1; @@ -397,25 +418,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 */ @@ -490,9 +522,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 */ @@ -561,7 +593,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) { @@ -624,6 +656,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; @@ -631,9 +682,10 @@ static int gxf_write_header(AVFormatContext *s) GXFStreamContext *vsc = NULL; uint8_t tracks[255] = {0}; int i, media_info = 0; + 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; } @@ -691,6 +743,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; @@ -742,6 +796,9 @@ 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 @@ -874,8 +931,9 @@ static int gxf_write_packet(AVFormatContext *s, AVPacket *pkt) if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) { if (!(gxf->flt_entries_nb % 500)) { - gxf->flt_entries = av_realloc(gxf->flt_entries, - (gxf->flt_entries_nb+500)*sizeof(*gxf->flt_entries)); + gxf->flt_entries = av_realloc_f(gxf->flt_entries, + sizeof(*gxf->flt_entries), + gxf->flt_entries_nb+500); if (!gxf->flt_entries) { av_log(s, AV_LOG_ERROR, "could not reallocate flt entries\n"); return -1; diff --git a/libavformat/h261dec.c b/libavformat/h261dec.c index 9b9e3f46a6..1b254d613a 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 */ diff --git a/libavformat/h263dec.c b/libavformat/h263dec.c index 101ef51748..667fdbdcbb 100644 --- a/libavformat/h263dec.c +++ b/libavformat/h263dec.c @@ -2,20 +2,20 @@ * RAW H.263 video demuxer * Copyright (c) 2009 Michael Niedermayer <michaelni@gmx.at> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/h264dec.c b/libavformat/h264dec.c index 0c85bae6d4..9c67ab9545 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/hls.c b/libavformat/hls.c index 1f6b7d56ed..ef5803ba4b 100644 --- a/libavformat/hls.c +++ b/libavformat/hls.c @@ -2,20 +2,20 @@ * Apple HTTP Live Streaming demuxer * Copyright (c) 2010 Martin Storsjo * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -42,7 +42,7 @@ * An apple http stream consists of a playlist with media segment files, * played sequentially. There may be several playlists with the same * video content, in different bandwidth variants, that are played in - * parallel (preferrably only one bandwidth variant at a time). In this case, + * parallel (preferably only one bandwidth variant at a time). In this case, * the user supplied the url to a main playlist that only lists the variant * playlists. * @@ -212,9 +212,14 @@ static int parse_playlist(HLSContext *c, const char *url, int close_in = 0; if (!in) { + AVDictionary *opts = NULL; close_in = 1; - if ((ret = avio_open2(&in, url, AVIO_FLAG_READ, - c->interrupt_callback, NULL)) < 0) + /* Some HLS servers dont like being sent the range header */ + av_dict_set(&opts, "seekable", "0", 0); + ret = avio_open2(&in, url, AVIO_FLAG_READ, + c->interrupt_callback, &opts); + av_dict_free(&opts); + if (ret < 0) return ret; } @@ -228,7 +233,7 @@ static int parse_playlist(HLSContext *c, const char *url, free_segment_list(var); var->finished = 0; } - while (!in->eof_reached) { + while (!url_feof(in)) { read_chomp_line(in, line, sizeof(line)); if (av_strstart(line, "#EXT-X-STREAM-INF:", &ptr)) { struct variant_info info = {{0}}; @@ -325,17 +330,20 @@ fail: static int open_input(struct variant *var) { + AVDictionary *opts = NULL; + int ret; struct segment *seg = var->segments[var->cur_seq_no - var->start_seq_no]; + av_dict_set(&opts, "seekable", "0", 0); if (seg->key_type == KEY_NONE) { - return ffurl_open(&var->input, seg->url, AVIO_FLAG_READ, - &var->parent->interrupt_callback, NULL); + ret = ffurl_open(&var->input, seg->url, AVIO_FLAG_READ, + &var->parent->interrupt_callback, &opts); + goto cleanup; } else if (seg->key_type == KEY_AES_128) { char iv[33], key[33], url[MAX_URL_SIZE]; - int ret; if (strcmp(seg->key, var->key_url)) { URLContext *uc; if (ffurl_open(&uc, seg->key, AVIO_FLAG_READ, - &var->parent->interrupt_callback, NULL) == 0) { + &var->parent->interrupt_callback, &opts) == 0) { if (ffurl_read_complete(uc, var->key, sizeof(var->key)) != sizeof(var->key)) { av_log(NULL, AV_LOG_ERROR, "Unable to read key file %s\n", @@ -357,17 +365,25 @@ static int open_input(struct variant *var) snprintf(url, sizeof(url), "crypto:%s", seg->url); if ((ret = ffurl_alloc(&var->input, url, AVIO_FLAG_READ, &var->parent->interrupt_callback)) < 0) - return ret; + goto cleanup; av_opt_set(var->input->priv_data, "key", key, 0); av_opt_set(var->input->priv_data, "iv", iv, 0); - if ((ret = ffurl_connect(var->input, NULL)) < 0) { + /* Need to repopulate options */ + av_dict_free(&opts); + av_dict_set(&opts, "seekable", "0", 0); + if ((ret = ffurl_connect(var->input, &opts)) < 0) { ffurl_close(var->input); var->input = NULL; - return ret; + goto cleanup; } - return 0; + ret = 0; } - return AVERROR(ENOSYS); + else + ret = AVERROR(ENOSYS); + +cleanup: + av_dict_free(&opts); + return ret; } static int read_data(void *opaque, uint8_t *buf, int buf_size) @@ -393,7 +409,7 @@ reload: /* 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. */ - reload_interval = v->target_duration * 500000; + reload_interval = v->target_duration * 500000LL; } if (v->cur_seq_no < v->start_seq_no) { av_log(NULL, AV_LOG_WARNING, @@ -427,7 +443,7 @@ reload: c->end_of_segment = 1; c->cur_seq_no = v->cur_seq_no; - if (v->ctx && v->ctx->nb_streams) { + 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++) { @@ -517,6 +533,7 @@ static int hls_read_header(AVFormatContext *s) * so avformat_close_input shouldn't be called. If * avformat_open_input fails below, it frees and zeros the * context, so it doesn't need any special treatment like this. */ + av_log(s, AV_LOG_ERROR, "Error when loading first segment '%s'\n", v->segments[0]->url); avformat_free_context(v->ctx); v->ctx = NULL; goto fail; @@ -525,7 +542,12 @@ static int hls_read_header(AVFormatContext *s) ret = avformat_open_input(&v->ctx, v->segments[0]->url, in_fmt, NULL); if (ret < 0) goto fail; + v->stream_offset = stream_offset; + v->ctx->ctx_flags &= ~AVFMTCTX_NOHEADER; + ret = avformat_find_stream_info(v->ctx, NULL); + if (ret < 0) + goto fail; snprintf(bitrate_str, sizeof(bitrate_str), "%d", v->bandwidth); /* Create new AVStreams for each stream in this variant */ for (j = 0; j < v->ctx->nb_streams; j++) { @@ -560,7 +582,7 @@ static int recheck_discard_flags(AVFormatContext *s, int first) /* Check if any new streams are needed */ for (i = 0; i < c->n_variants; i++) - c->variants[i]->cur_needed = 0;; + c->variants[i]->cur_needed = 0; for (i = 0; i < s->nb_streams; i++) { AVStream *st = s->streams[i]; @@ -610,7 +632,7 @@ start: AVStream *st; ret = av_read_frame(var->ctx, &var->pkt); if (ret < 0) { - if (!var->pb.eof_reached) + if (!url_feof(&var->pb) && ret != AVERROR_EOF) return ret; reset_packet(&var->pkt); break; @@ -640,9 +662,21 @@ start: } /* Check if this stream has the packet with the lowest dts */ if (var->pkt.data) { - if (minvariant < 0 || - var->pkt.dts < c->variants[minvariant]->pkt.dts) + if(minvariant < 0) { minvariant = i; + } else { + struct variant *minvar = c->variants[minvariant]; + 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( 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; + } } } if (c->end_of_segment) { @@ -696,10 +730,11 @@ static int hls_read_seek(AVFormatContext *s, int stream_index, /* Reset reading */ struct variant *var = c->variants[i]; int64_t pos = c->first_timestamp == AV_NOPTS_VALUE ? 0 : - av_rescale_rnd(c->first_timestamp, 1, - stream_index >= 0 ? s->streams[stream_index]->time_base.den : AV_TIME_BASE, - flags & AVSEEK_FLAG_BACKWARD ? AV_ROUND_DOWN : AV_ROUND_UP); - if (var->input) { + av_rescale_rnd(c->first_timestamp, 1, stream_index >= 0 ? + s->streams[stream_index]->time_base.den : + AV_TIME_BASE, flags & AVSEEK_FLAG_BACKWARD ? + AV_ROUND_DOWN : AV_ROUND_UP); + if (var->input) { ffurl_close(var->input); var->input = NULL; } diff --git a/libavformat/hlsproto.c b/libavformat/hlsproto.c index 179bdf1967..79cf0e498b 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 */ @@ -36,7 +36,7 @@ * An apple http stream consists of a playlist with media segment files, * played sequentially. There may be several playlists with the same * video content, in different bandwidth variants, that are played in - * parallel (preferrably only one bandwidth variant at a time). In this case, + * parallel (preferably only one bandwidth variant at a time). In this case, * the user supplied the url to a main playlist that only lists the variant * playlists. * @@ -125,7 +125,7 @@ static int parse_playlist(URLContext *h, const char *url) free_segment_list(s); s->finished = 0; - while (!in->eof_reached) { + while (!url_feof(in)) { read_chomp_line(in, line, sizeof(line)); if (av_strstart(line, "#EXT-X-STREAM-INF:", &ptr)) { struct variant_info info = {{0}}; @@ -293,7 +293,7 @@ retry: /* 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. */ - reload_interval = s->target_duration * 500000; + reload_interval = s->target_duration * 500000LL; } } if (s->cur_seq_no < s->start_seq_no) { diff --git a/libavformat/http.c b/libavformat/http.c index 9666ca3206..308c22dd90 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 */ @@ -29,7 +29,7 @@ #include "url.h" #include "libavutil/opt.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 @@ -46,28 +46,38 @@ typedef struct { int line_count; int http_code; int64_t chunksize; /**< Used if "Transfer-Encoding: chunked" otherwise -1. */ + char *content_type; + char *user_agent; int64_t off, filesize; char location[MAX_URL_SIZE]; HTTPAuthState auth_state; HTTPAuthState proxy_auth_state; char *headers; int willclose; /**< Set if the server correctly handles Connection: close and will close the connection after feeding us the content. */ + int seekable; /**< Control seekability, 0 = disable, 1 = enable, -1 = probe. */ int chunked_post; int end_chunked_post; /**< A flag which indicates if the end of chunked encoding has been sent. */ int end_header; /**< A flag which indicates we have finished to read POST reply. */ int multiple_requests; /**< A flag which indicates if we use persistent connections. */ uint8_t *post_data; int post_datalen; + int is_akamai; + int rw_timeout; } HTTPContext; #define OFFSET(x) offsetof(HTTPContext, x) #define D AV_OPT_FLAG_DECODING_PARAM #define E AV_OPT_FLAG_ENCODING_PARAM +#define DEC AV_OPT_FLAG_DECODING_PARAM static const AVOption options[] = { +{"seekable", "Control seekability of connection", OFFSET(seekable), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 1, D }, {"chunked_post", "use chunked transfer-encoding for posts", OFFSET(chunked_post), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1, E }, {"headers", "custom HTTP headers, can override built in default headers", OFFSET(headers), AV_OPT_TYPE_STRING, { 0 }, 0, 0, D|E }, +{"content_type", "force a content type", OFFSET(content_type), AV_OPT_TYPE_STRING, { 0 }, 0, 0, D|E }, +{"user-agent", "override User-Agent header", OFFSET(user_agent), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, DEC}, {"multiple_requests", "use persistent connections", OFFSET(multiple_requests), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, D|E }, {"post_data", "custom HTTP post data", OFFSET(post_data), AV_OPT_TYPE_BINARY, .flags = D|E }, +{"timeout", "timeout of socket i/o operations", OFFSET(rw_timeout), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, D|E }, {NULL} }; #define HTTP_CLASS(flavor)\ @@ -145,8 +155,15 @@ static int http_open_cnx(URLContext *h) ff_url_join(buf, sizeof(buf), lower_proto, NULL, hostname, port, NULL); if (!s->hd) { + AVDictionary *opts = NULL; + char opts_format[20]; + if (s->rw_timeout != -1) { + snprintf(opts_format, sizeof(opts_format), "%d", s->rw_timeout); + av_dict_set(&opts, "timeout", opts_format, 0); + } /* if option is not given, don't pass it and let tcp use its own default */ err = ffurl_open(&s->hd, buf, AVIO_FLAG_READ_WRITE, - &h->interrupt_callback, NULL); + &h->interrupt_callback, &opts); + av_dict_free(&opts); if (err < 0) goto fail; } @@ -159,8 +176,7 @@ static int http_open_cnx(URLContext *h) 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; @@ -168,8 +184,7 @@ static int http_open_cnx(URLContext *h) 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; @@ -177,8 +192,7 @@ static int http_open_cnx(URLContext *h) if ((s->http_code == 301 || s->http_code == 302 || s->http_code == 303 || s->http_code == 307) && location_changed == 1) { /* url moved, get next */ - ffurl_close(s->hd); - s->hd = NULL; + ffurl_closep(&s->hd); if (redirects++ >= MAX_REDIRECTS) return AVERROR(EIO); /* Restart the authentication process with the new target, which @@ -191,8 +205,7 @@ static int http_open_cnx(URLContext *h) return 0; fail: if (s->hd) - ffurl_close(s->hd); - s->hd = NULL; + ffurl_closep(&s->hd); return AVERROR(EIO); } @@ -210,7 +223,10 @@ static int http_open(URLContext *h, const char *uri, int flags) { HTTPContext *s = h->priv_data; - h->is_streamed = 1; + if( s->seekable == 1 ) + h->is_streamed = 0; + else + h->is_streamed = 1; s->filesize = -1; av_strlcpy(s->location, uri, sizeof(s->location)); @@ -321,8 +337,9 @@ static int process_line(URLContext *h, char *line, int line_count, if ((slash = strchr(p, '/')) && strlen(slash) > 0) s->filesize = strtoll(slash+1, NULL, 10); } - h->is_streamed = 0; /* we _can_ in fact seek */ - } else if (!av_strcasecmp(tag, "Accept-Ranges") && !strncmp(p, "bytes", 5)) { + if (s->seekable == -1 && (!s->is_akamai || s->filesize != 2147483647)) + h->is_streamed = 0; /* we _can_ in fact seek */ + } else if (!av_strcasecmp(tag, "Accept-Ranges") && !strncmp(p, "bytes", 5) && s->seekable == -1) { h->is_streamed = 0; } else if (!av_strcasecmp (tag, "Transfer-Encoding") && !av_strncasecmp(p, "chunked", 7)) { s->filesize = -1; @@ -336,6 +353,8 @@ static int process_line(URLContext *h, char *line, int line_count, } else if (!av_strcasecmp (tag, "Connection")) { if (!strcmp(p, "close")) s->willclose = 1; + } else if (!av_strcasecmp (tag, "Server") && !av_strcasecmp (p, "AkamaiGHost")) { + s->is_akamai = 1; } } return 1; @@ -380,7 +399,7 @@ static int http_connect(URLContext *h, const char *path, const char *local_path, { HTTPContext *s = h->priv_data; int post, err; - char headers[1024] = ""; + char headers[4096] = ""; char *authstr = NULL, *proxyauthstr = NULL; int64_t off = s->off; int len = 0; @@ -405,12 +424,16 @@ static int http_connect(URLContext *h, const char *path, const char *local_path, /* set default headers if needed */ if (!has_header(s->headers, "\r\nUser-Agent: ")) - len += av_strlcatf(headers + len, sizeof(headers) - len, - "User-Agent: %s\r\n", LIBAVFORMAT_IDENT); + len += av_strlcatf(headers + len, sizeof(headers) - len, + "User-Agent: %s\r\n", + s->user_agent ? s->user_agent : LIBAVFORMAT_IDENT); if (!has_header(s->headers, "\r\nAccept: ")) len += av_strlcpy(headers + len, "Accept: */*\r\n", sizeof(headers) - len); - if (!has_header(s->headers, "\r\nRange: ") && !post) + // Note: we send this on purpose even when s->off is 0 when we're probing, + // since it allows us to detect more reliably if a (non-conforming) + // server supports seeking by analysing the reply headers. + if (!has_header(s->headers, "\r\nRange: ") && !post && (s->off > 0 || s->seekable == -1)) len += av_strlcatf(headers + len, sizeof(headers) - len, "Range: bytes=%"PRId64"-\r\n", s->off); @@ -430,6 +453,9 @@ static int http_connect(URLContext *h, const char *path, const char *local_path, if (!has_header(s->headers, "\r\nContent-Length: ") && s->post_data) len += av_strlcatf(headers + len, sizeof(headers) - len, "Content-Length: %d\r\n", s->post_datalen); + if (!has_header(s->headers, "\r\nContent-Type: ") && s->content_type) + len += av_strlcatf(headers + len, sizeof(headers) - len, + "Content-Type: %s\r\n", s->content_type); /* now add in custom headers */ if (s->headers) @@ -600,7 +626,7 @@ static int http_close(URLContext *h) } if (s->hd) - ffurl_close(s->hd); + ffurl_closep(&s->hd); return ret; } @@ -683,7 +709,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; } @@ -697,8 +723,13 @@ static int http_proxy_open(URLContext *h, const char *uri, int flags) HTTPAuthType cur_auth_type; char *authstr; int new_loc; + AVDictionary *opts = NULL; + char opts_format[20]; - 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); @@ -710,8 +741,13 @@ static int http_proxy_open(URLContext *h, const char *uri, int flags) ff_url_join(lower_url, sizeof(lower_url), "tcp", NULL, hostname, port, NULL); redo: + if (s->rw_timeout != -1) { + snprintf(opts_format, sizeof(opts_format), "%d", s->rw_timeout); + av_dict_set(&opts, "timeout", opts_format, 0); + } /* if option is not given, don't pass it and let tcp use its own default */ ret = ffurl_open(&s->hd, lower_url, AVIO_FLAG_READ_WRITE, - &h->interrupt_callback, NULL); + &h->interrupt_callback, &opts); + av_dict_free(&opts); if (ret < 0) return ret; @@ -754,8 +790,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 3579ad745a..a19ad8ed3e 100644 --- a/libavformat/http.h +++ b/libavformat/http.h @@ -2,20 +2,20 @@ * HTTP definitions * Copyright (c) 2010 Josh Allmann * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -38,9 +38,9 @@ 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 occured, 0 + * @return a negative value if an error condition occurred, 0 * otherwise */ int ff_http_do_new_request(URLContext *h, const char *uri); diff --git a/libavformat/httpauth.c b/libavformat/httpauth.c index 774ee2134f..e97aee1f66 100644 --- a/libavformat/httpauth.c +++ b/libavformat/httpauth.c @@ -2,20 +2,20 @@ * HTTP authentication * Copyright (c) 2010 Martin Storsjo * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/httpauth.h b/libavformat/httpauth.h index 99bf43ffdc..fc17c94859 100644 --- a/libavformat/httpauth.h +++ b/libavformat/httpauth.h @@ -2,20 +2,20 @@ * HTTP authentication * Copyright (c) 2010 Martin Storsjo * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/icodec.c b/libavformat/icodec.c new file mode 100644 index 0000000000..37909aab72 --- /dev/null +++ b/libavformat/icodec.c @@ -0,0 +1,181 @@ +/* + * Microsoft Windows ICO demuxer + * Copyright (c) 2011 Peter Ross (pross@xvid.org) + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * Microsoft Windows ICO demuxer + */ + +#include "libavutil/intreadwrite.h" +#include "libavcodec/bytestream.h" +#include "libavcodec/bmp.h" +#include "avformat.h" +#include "internal.h" + +typedef struct { + int offset; + int size; + int nb_pal; +} IcoImage; + +typedef struct { + int current_image; + int nb_images; + IcoImage * images; +} IcoDemuxContext; + +static int probe(AVProbeData *p) +{ + if (AV_RL16(p->buf) == 0 && AV_RL16(p->buf + 2) == 1 && AV_RL16(p->buf + 4)) + return AVPROBE_SCORE_MAX / 3; + return 0; +} + +static int read_header(AVFormatContext *s) +{ + IcoDemuxContext *ico = s->priv_data; + AVIOContext *pb = s->pb; + int i; + + avio_skip(pb, 4); + ico->nb_images = avio_rl16(pb); + + ico->images = av_malloc(ico->nb_images * sizeof(IcoImage)); + if (!ico->images) + return AVERROR(ENOMEM); + + for (i = 0; i < ico->nb_images; i++) { + AVStream *st; + int tmp; + + if (avio_seek(pb, 6 + i * 16, SEEK_SET) < 0) + break; + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->codec->width = avio_r8(pb); + st->codec->height = avio_r8(pb); + ico->images[i].nb_pal = avio_r8(pb); + if (ico->images[i].nb_pal == 255) + ico->images[i].nb_pal = 0; + + avio_skip(pb, 5); + + ico->images[i].size = avio_rl32(pb); + ico->images[i].offset = avio_rl32(pb); + + if (avio_seek(pb, ico->images[i].offset, SEEK_SET) < 0) + break; + + switch(avio_rl32(pb)) { + 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: + av_log_ask_for_sample(s, "unsupported codec\n"); + return AVERROR_INVALIDDATA; + } + } + + return 0; +} + +static int read_packet(AVFormatContext *s, AVPacket *pkt) +{ + IcoDemuxContext *ico = s->priv_data; + IcoImage *image; + AVIOContext *pb = s->pb; + AVStream *st = s->streams[0]; + int ret; + + if (ico->current_image >= ico->nb_images) + return AVERROR(EIO); + + image = &ico->images[ico->current_image]; + + if ((ret = avio_seek(pb, image->offset, SEEK_SET)) < 0) + return ret; + + if (s->streams[ico->current_image]->codec->codec_id == AV_CODEC_ID_PNG) { + if ((ret = av_get_packet(pb, pkt, image->size)) < 0) + return ret; + } else { + uint8_t *buf; + if ((ret = av_new_packet(pkt, 14 + image->size)) < 0) + return ret; + buf = pkt->data; + + /* add BMP header */ + bytestream_put_byte(&buf, 'B'); + bytestream_put_byte(&buf, 'M'); + bytestream_put_le32(&buf, pkt->size); + bytestream_put_le16(&buf, 0); + bytestream_put_le16(&buf, 0); + bytestream_put_le32(&buf, 0); + + if ((ret = avio_read(pb, buf, image->size)) < 0) + return ret; + + st->codec->bits_per_coded_sample = AV_RL16(buf + 14); + + if (AV_RL32(buf + 32)) + image->nb_pal = AV_RL32(buf + 32); + + if (st->codec->bits_per_coded_sample <= 8 && !image->nb_pal) { + image->nb_pal = 1 << st->codec->bits_per_coded_sample; + AV_WL32(buf + 32, image->nb_pal); + } + + AV_WL32(buf - 4, 14 + 40 + image->nb_pal * 4); + AV_WL32(buf + 8, AV_RL32(buf + 8) / 2); + } + + pkt->stream_index = ico->current_image++; + pkt->flags |= AV_PKT_FLAG_KEY; + + return 0; +} + +AVInputFormat ff_ico_demuxer = { + .name = "ico", + .long_name = NULL_IF_CONFIG_SMALL("Microsoft Windows ICO"), + .priv_data_size = sizeof(IcoDemuxContext), + .read_probe = probe, + .read_header = read_header, + .read_packet = read_packet, + .flags = AVFMT_NOTIMESTAMPS, +}; diff --git a/libavformat/icoenc.c b/libavformat/icoenc.c new file mode 100644 index 0000000000..cbf8bd93dd --- /dev/null +++ b/libavformat/icoenc.c @@ -0,0 +1,204 @@ +/* + * 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 == 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 == CODEC_ID_PNG) { + if (c->pix_fmt != AV_PIX_FMT_RGBA) { + av_log(s, AV_LOG_ERROR, "PNG in ico requires pixel format to be rgba\n"); + return AVERROR(EINVAL); + } + } else { + av_log(s, AV_LOG_ERROR, "Unsupported codec %s\n", c->codec_name); + return AVERROR(EINVAL); + } + + if (c->width > 256 || + c->height > 256) { + av_log(s, AV_LOG_ERROR, "Unsupported dimensions %dx%d (dimensions cannot exceed 256x256)\n", c->width, c->height); + return AVERROR(EINVAL); + } + + return 0; +} + +static int ico_write_header(AVFormatContext *s) +{ + IcoMuxContext *ico = s->priv_data; + AVIOContext *pb = s->pb; + int ret; + int i; + + if (!pb->seekable) { + av_log(s, AV_LOG_ERROR, "Output is not seekable\n"); + return AVERROR(EINVAL); + } + + ico->current_image = 0; + ico->nb_images = s->nb_streams; + + avio_wl16(pb, 0); // reserved + avio_wl16(pb, 1); // 1 == icon + avio_skip(pb, 2); // skip the number of images + + for (i = 0; i < s->nb_streams; i++) { + if (ret = ico_check_attributes(s, s->streams[i]->codec)) + return ret; + + // Fill in later when writing trailer... + avio_skip(pb, 16); + } + + ico->images = av_mallocz(ico->nb_images * sizeof(IcoMuxContext)); + if (!ico->images) + return AVERROR(ENOMEM); + + avio_flush(pb); + + return 0; +} + +static int ico_write_packet(AVFormatContext *s, AVPacket *pkt) +{ + IcoMuxContext *ico = s->priv_data; + IcoImage *image; + AVIOContext *pb = s->pb; + AVCodecContext *c = s->streams[pkt->stream_index]->codec; + int i; + + if (ico->current_image >= ico->nb_images) { + av_log(s, AV_LOG_ERROR, "ICO already contains %d images\n", ico->current_image); + return AVERROR(EIO); + } + + image = &ico->images[ico->current_image++]; + + image->offset = avio_tell(pb); + image->width = (c->width == 256) ? 0 : c->width; + image->height = (c->height == 256) ? 0 : c->height; + + if (c->codec_id == 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) + } + + avio_flush(pb); + + 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 == 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 = CODEC_ID_NONE, + .video_codec = CODEC_ID_BMP, + .write_header = ico_write_header, + .write_packet = ico_write_packet, + .write_trailer = ico_write_trailer, + .flags = AVFMT_NOTIMESTAMPS, +}; diff --git a/libavformat/id3v1.c b/libavformat/id3v1.c index 87930ff361..2d1e806920 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 */ diff --git a/libavformat/id3v1.h b/libavformat/id3v1.h index 71070736f1..d5dca35873 100644 --- a/libavformat/id3v1.h +++ b/libavformat/id3v1.h @@ -2,20 +2,20 @@ * ID3v1 header parser * Copyright (c) 2003 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/id3v2.c b/libavformat/id3v2.c index 012d7933a8..9c20287778 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 "id3v2.h" #include "id3v1.h" #include "libavutil/avstring.h" @@ -478,7 +491,7 @@ static void read_apic(AVFormatContext *s, AVIOContext *pb, int taglen, char *tag apic->len = taglen; apic->data = av_malloc(taglen); - if (!apic->data || avio_read(pb, apic->data, taglen) != taglen) + if (!apic->data || !apic->len || avio_read(pb, apic->data, taglen) != taglen) goto fail; new_extra->tag = "APIC"; @@ -517,7 +530,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))) @@ -529,7 +542,8 @@ static const ID3v2EMFunc *get_extra_meta_func(const char *tag, int isv34) static void ff_id3v2_parse(AVFormatContext *s, int len, uint8_t version, uint8_t flags, ID3v2ExtraMeta **extra_meta) { - int isv34, tlen, unsync; + int isv34, unsync; + unsigned tlen; char tag[5]; int64_t next, end = avio_tell(s->pb) + len; int taghdrlen; @@ -538,7 +552,9 @@ static void ff_id3v2_parse(AVFormatContext *s, int len, uint8_t version, uint8_t AVIOContext *pbx; unsigned char *buffer = NULL; int buffer_size = 0; - const ID3v2EMFunc *extra_func; + const ID3v2EMFunc *extra_func = NULL; + unsigned char *compressed_buffer = NULL; + int compressed_buffer_size = 0; switch (version) { case 2: @@ -573,11 +589,19 @@ static void ff_id3v2_parse(AVFormatContext *s, int len, uint8_t version, uint8_t goto error; } avio_skip(s->pb, extlen); + len -= extlen + 4; + if (len < 0) { + reason = "extended header too long."; + goto error; + } } while (len >= taghdrlen) { unsigned int tflags = 0; int tunsync = 0; + int tcomp = 0; + int tencr = 0; + unsigned long dlen; if (isv34) { avio_read(s->pb, tag, 4); @@ -593,11 +617,13 @@ static void ff_id3v2_parse(AVFormatContext *s, int len, uint8_t version, uint8_t tag[3] = 0; tlen = avio_rb24(s->pb); } - if (tlen < 0 || tlen > len - taghdrlen) { - av_log(s, AV_LOG_WARNING, "Invalid size in frame %s, skipping the rest of tag.\n", tag); + if (tlen > (1<<28)) break; - } len -= taghdrlen + tlen; + + if (len < 0) + break; + next = avio_tell(s->pb) + tlen; if (!tlen) { @@ -607,24 +633,67 @@ static void ff_id3v2_parse(AVFormatContext *s, int len, uint8_t version, uint8_t } if (tflags & ID3v2_FLAG_DATALEN) { - avio_rb32(s->pb); + if (tlen < 4) + break; + dlen = avio_rb32(s->pb); tlen -= 4; - } + } else + dlen = tlen; + + tcomp = tflags & ID3v2_FLAG_COMPRESSION; + tencr = tflags & ID3v2_FLAG_ENCRYPTION; + + /* skip encrypted tags and, if no zlib, compressed tags */ + if (tencr || (!CONFIG_ZLIB && tcomp)) { + const char *type; + if (!tcomp) + type = "encrypted"; + else if (!tencr) + type = "compressed"; + else + type = "encrypted and compressed"; - if (tflags & (ID3v2_FLAG_ENCRYPTION | ID3v2_FLAG_COMPRESSION)) { - av_log(s, AV_LOG_WARNING, "Skipping encrypted/compressed ID3v2 frame %s.\n", tag); + av_log(s, AV_LOG_WARNING, "Skipping %s ID3v2 frame %s.\n", type, tag); avio_skip(s->pb, tlen); /* check for text tag or supported special meta tag */ } else if (tag[0] == 'T' || (extra_meta && (extra_func = get_extra_meta_func(tag, isv34)))) { - if (unsync || tunsync) { + if (unsync || tunsync || tcomp) { int i, j; - av_fast_malloc(&buffer, &buffer_size, tlen); + + av_fast_malloc(&buffer, &buffer_size, dlen); if (!buffer) { - av_log(s, AV_LOG_ERROR, "Failed to alloc %d bytes\n", tlen); + av_log(s, AV_LOG_ERROR, "Failed to alloc %ld bytes\n", dlen); goto seek; } - for (i = 0, j = 0; i < tlen; i++, j++) { - buffer[j] = avio_r8(s->pb); +#if CONFIG_ZLIB + if (tcomp) { + int n, err; + + av_log(s, AV_LOG_DEBUG, "Compresssed frame %s tlen=%d dlen=%ld\n", tag, tlen, dlen); + + av_fast_malloc(&compressed_buffer, &compressed_buffer_size, tlen); + if (!compressed_buffer) { + av_log(s, AV_LOG_ERROR, "Failed to alloc %d bytes\n", tlen); + goto seek; + } + + n = avio_read(s->pb, compressed_buffer, tlen); + if (n < 0) { + av_log(s, AV_LOG_ERROR, "Failed to read compressed tag\n"); + goto seek; + } + + err = uncompress(buffer, &dlen, compressed_buffer, n); + if (err != Z_OK) { + av_log(s, AV_LOG_ERROR, "Failed to uncompress tag: %d\n", err); + goto seek; + } + } +#endif + + for (i = 0, j = 0; i < dlen; i++, j++) { + if (!tcomp) + buffer[j] = avio_r8(s->pb); if (j > 0 && !buffer[j] && buffer[j - 1] == 0xff) { /* Unsynchronised byte, skip it */ j--; @@ -645,7 +714,7 @@ static void ff_id3v2_parse(AVFormatContext *s, int len, uint8_t version, uint8_t } else if (!tag[0]) { if (tag[1]) - av_log(s, AV_LOG_WARNING, "invalid frame id, assuming padding"); + av_log(s, AV_LOG_WARNING, "invalid frame id, assuming padding\n"); avio_skip(s->pb, tlen); break; } @@ -662,6 +731,7 @@ seek: av_log(s, AV_LOG_INFO, "ID3v2.%d tag skipped, cannot handle %s\n", version, reason); avio_seek(s->pb, end, SEEK_SET); av_free(buffer); + av_free(compressed_buffer); return; } diff --git a/libavformat/id3v2.h b/libavformat/id3v2.h index cb2fb02093..f7270106a1 100644 --- a/libavformat/id3v2.h +++ b/libavformat/id3v2.h @@ -2,20 +2,20 @@ * ID3v2 header parser * Copyright (c) 2003 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/id3v2enc.c b/libavformat/id3v2enc.c index df00f05b24..a10d679946 100644 --- a/libavformat/id3v2enc.c +++ b/libavformat/id3v2enc.c @@ -1,26 +1,27 @@ /* * ID3v2 header writer * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include <stdint.h> #include <string.h> +#include "libavutil/avstring.h" #include "libavutil/dict.h" #include "libavutil/intreadwrite.h" #include "avformat.h" @@ -109,6 +110,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) { @@ -130,7 +169,9 @@ int ff_id3v2_write_metadata(AVFormatContext *s, ID3v2EncContext *id3) ID3v2_ENCODING_UTF8; ff_metadata_conv(&s->metadata, ff_id3v2_34_metadata_conv, NULL); - if (id3->version == 4) + if (id3->version == 3) + id3v2_3_metadata_split_date(&s->metadata); + else if (id3->version == 4) ff_metadata_conv(&s->metadata, ff_id3v2_4_metadata_conv, NULL); while ((t = av_dict_get(s->metadata, "", t, AV_DICT_IGNORE_SUFFIX))) { diff --git a/libavformat/idcin.c b/libavformat/idcin.c index fde8666788..bede04075e 100644 --- a/libavformat/idcin.c +++ b/libavformat/idcin.c @@ -2,20 +2,20 @@ * id Quake II CIN File Demuxer * Copyright (c) 2003 The ffmpeg Project * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -224,7 +224,7 @@ static int idcin_read_packet(AVFormatContext *s, unsigned char palette_buffer[768]; uint32_t palette[256]; - if (s->pb->eof_reached) + if (url_feof(s->pb)) return AVERROR(EIO); if (idcin->next_chunk_is_video) { @@ -247,13 +247,17 @@ 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; } } chunk_size = avio_rl32(pb); /* 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) @@ -263,8 +267,8 @@ static int idcin_read_packet(AVFormatContext *s, pal = av_packet_new_side_data(pkt, AV_PKT_DATA_PALETTE, AVPALETTE_SIZE); - if (ret < 0) - return ret; + if (!pal) + return AVERROR(ENOMEM); memcpy(pal, palette, AVPALETTE_SIZE); } pkt->stream_index = idcin->video_stream_index; diff --git a/libavformat/idroqdec.c b/libavformat/idroqdec.c index 521f586d90..3b17ff420c 100644 --- a/libavformat/idroqdec.c +++ b/libavformat/idroqdec.c @@ -2,20 +2,20 @@ * id RoQ (.roq) File Demuxer * Copyright (c) 2003 The ffmpeg Project * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -30,6 +30,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 @@ -104,7 +105,7 @@ static int roq_read_packet(AVFormatContext *s, while (!packet_read) { - if (s->pb->eof_reached) + if (url_feof(s->pb)) return AVERROR(EIO); /* get the next chunk preamble */ @@ -117,6 +118,8 @@ static int roq_read_packet(AVFormatContext *s, if(chunk_size > INT_MAX) return AVERROR_INVALIDDATA; + chunk_size = ffio_limit(pb, chunk_size); + switch (chunk_type) { case RoQ_INFO: diff --git a/libavformat/idroqenc.c b/libavformat/idroqenc.c index 2ce4d7d493..50c428046b 100644 --- a/libavformat/idroqenc.c +++ b/libavformat/idroqenc.c @@ -2,20 +2,20 @@ * id RoQ (.roq) File muxer * Copyright (c) 2007 Vitor Sessak * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/iff.c b/libavformat/iff.c index c2bc283753..d559ad3c2b 100644 --- a/libavformat/iff.c +++ b/libavformat/iff.c @@ -1,23 +1,22 @@ /* - * IFF (.iff) file demuxer * Copyright (c) 2008 Jaikrishnan Menon <realityman@gmx.net> * Copyright (c) 2010 Peter Ross <pross@xvid.org> * Copyright (c) 2010 Sebastian Vater <cdgs.basty@googlemail.com> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -29,6 +28,8 @@ * http://wiki.multimedia.cx/index.php?title=IFF */ +#include "libavcodec/bytestream.h" +#include "libavutil/avassert.h" #include "libavutil/intreadwrite.h" #include "libavutil/dict.h" #include "avformat.h" @@ -42,7 +43,11 @@ #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_FORM MKTAG('F','O','R','M') #define ID_ANNO MKTAG('A','N','N','O') @@ -53,13 +58,25 @@ #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 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 9 + typedef enum { COMP_NONE, COMP_FIB, @@ -75,9 +92,15 @@ typedef struct { uint64_t body_pos; uint32_t body_size; uint32_t sent_bytes; + svx8_compression_type svx8_compression; + bitmap_compression_type 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 } IffDemuxContext; - /* Metadata string read */ static int get_metadata(AVFormatContext *s, const char *const tag, @@ -101,19 +124,31 @@ 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_PBM || + AV_RL32(d+8) == ID_ACBM || + AV_RL32(d+8) == ID_DEEP || + AV_RL32(d+8) == ID_ILBM) ) return AVPROBE_SCORE_MAX; return 0; } +static const uint8_t deep_rgb24[] = {0, 0, 0, 3, 0, 1, 0, 8, 0, 2, 0, 8, 0, 3, 0, 8}; +static const uint8_t deep_rgba[] = {0, 0, 0, 4, 0, 1, 0, 8, 0, 2, 0, 8, 0, 3, 0, 8}; + static int iff_read_header(AVFormatContext *s) { IffDemuxContext *iff = s->priv_data; AVIOContext *pb = s->pb; AVStream *st; + uint8_t *buf; uint32_t chunk_id, data_size; - int compression = -1; + uint32_t screenmode = 0; + unsigned transparency = 0; + unsigned masking = 0; // no mask + uint8_t fmt[16]; + int fmt_size; st = avformat_new_stream(s, NULL); if (!st) @@ -124,7 +159,7 @@ static int iff_read_header(AVFormatContext *s) // codec_tag used by ByteRun1 decoder to distinguish progressive (PBM) and interlaced (ILBM) content st->codec->codec_tag = avio_rl32(pb); - while(!pb->eof_reached) { + while(!url_feof(pb)) { uint64_t orig_pos; int res; const char *metadata_tag = NULL; @@ -142,11 +177,13 @@ 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_ABIT: case ID_BODY: + case ID_DBOD: iff->body_pos = avio_tell(pb); iff->body_size = data_size; break; @@ -157,16 +194,23 @@ static int iff_read_header(AVFormatContext *s) st->codec->channels = (avio_rb32(pb) < 6) ? 1 : 2; break; + case ID_CAMG: + if (data_size < 4) + return AVERROR_INVALIDDATA; + screenmode = avio_rb32(pb); + break; + case ID_CMAP: - 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; case ID_BMHD: + iff->bitmap_compression = -1; st->codec->codec_type = AVMEDIA_TYPE_VIDEO; if (data_size <= 8) return AVERROR_INVALIDDATA; @@ -174,38 +218,62 @@ static int iff_read_header(AVFormatContext *s) st->codec->height = avio_rb16(pb); avio_skip(pb, 4); // x, y offset st->codec->bits_per_coded_sample = avio_r8(pb); - if (data_size >= 11) { - avio_skip(pb, 1); // masking - compression = avio_r8(pb); + if (data_size >= 10) + masking = avio_r8(pb); + if (data_size >= 11) + iff->bitmap_compression = avio_r8(pb); + if (data_size >= 14) { + avio_skip(pb, 1); // padding + transparency = avio_rb16(pb); } if (data_size >= 16) { - avio_skip(pb, 3); // paddding, transparent st->sample_aspect_ratio.num = avio_r8(pb); st->sample_aspect_ratio.den = avio_r8(pb); } break; - case ID_ANNO: - case ID_TEXT: - metadata_tag = "comment"; - break; - - case ID_AUTH: - metadata_tag = "artist"; + 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 { + av_log_ask_for_sample(s, "unsupported color format\n"); + return AVERROR_PATCHWELCOME; + } break; - case ID_COPYRIGHT: - metadata_tag = "copyright"; + 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); + if (iff->bitmap_compression != 0) { + av_log(s, AV_LOG_ERROR, + "compression %i not supported\n", iff->bitmap_compression); + return AVERROR_PATCHWELCOME; + } + 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_NAME: - metadata_tag = "title"; - break; + case ID_ANNO: + case ID_TEXT: metadata_tag = "comment"; break; + case ID_AUTH: metadata_tag = "artist"; break; + case ID_COPYRIGHT: metadata_tag = "copyright"; break; + case ID_NAME: metadata_tag = "title"; break; } if (metadata_tag) { if ((res = get_metadata(s, metadata_tag, data_size)) < 0) { - av_log(s, AV_LOG_ERROR, "cannot allocate metadata tag %s!", metadata_tag); + av_log(s, AV_LOG_ERROR, "cannot allocate metadata tag %s!\n", metadata_tag); return res; } } @@ -218,7 +286,7 @@ static int iff_read_header(AVFormatContext *s) case AVMEDIA_TYPE_AUDIO: avpriv_set_pts_info(st, 32, 1, st->codec->sample_rate); - switch(compression) { + switch (iff->svx8_compression) { case COMP_NONE: st->codec->codec_id = AV_CODEC_ID_PCM_S8_PLANAR; break; @@ -229,17 +297,42 @@ static int iff_read_header(AVFormatContext *s) st->codec->codec_id = AV_CODEC_ID_8SVX_EXP; break; default: - av_log(s, AV_LOG_ERROR, "unknown compression method\n"); + av_log(s, AV_LOG_ERROR, + "Unknown SVX8 compression method '%d'\n", iff->svx8_compression); return -1; } - st->codec->bits_per_coded_sample = 8; + st->codec->bits_per_coded_sample = iff->svx8_compression == COMP_NONE ? 8 : 4; 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) { + 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); + } + 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); + + switch (iff->bitmap_compression) { case BITMAP_RAW: st->codec->codec_id = AV_CODEC_ID_IFF_ILBM; break; @@ -247,7 +340,8 @@ static int iff_read_header(AVFormatContext *s) st->codec->codec_id = AV_CODEC_ID_IFF_BYTERUN1; break; default: - av_log(s, AV_LOG_ERROR, "unknown compression method\n"); + av_log(s, AV_LOG_ERROR, + "Unknown bitmap compression method '%d'\n", iff->bitmap_compression); return AVERROR_INVALIDDATA; } break; @@ -263,14 +357,27 @@ static int iff_read_packet(AVFormatContext *s, { IffDemuxContext *iff = s->priv_data; AVIOContext *pb = s->pb; + AVStream *st = s->streams[0]; int ret; if(iff->sent_bytes >= iff->body_size) 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) { + ret = av_get_packet(pb, pkt, iff->body_size); + } else if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) { + uint8_t *buf; + + if (av_new_packet(pkt, iff->body_size + 2) < 0) { + return AVERROR(ENOMEM); + } + + buf = pkt->data; + bytestream_put_be16(&buf, 2); + ret = avio_read(pb, buf, iff->body_size); + } else { + av_assert0(0); + } if(iff->sent_bytes == 0) pkt->flags |= AV_PKT_FLAG_KEY; diff --git a/libavformat/ilbc.c b/libavformat/ilbc.c index c01eb6f796..7a23b2f442 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 14bb3d08cf..3867477dea 100644 --- a/libavformat/img2.c +++ b/libavformat/img2.c @@ -3,20 +3,20 @@ * Copyright (c) 2000, 2001, 2002 Fabrice Bellard * Copyright (c) 2004 Michael Niedermayer * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -31,8 +31,11 @@ typedef struct { static const IdStrMap img_tags[] = { { AV_CODEC_ID_MJPEG , "jpeg"}, { AV_CODEC_ID_MJPEG , "jpg"}, + { AV_CODEC_ID_MJPEG , "jps"}, { AV_CODEC_ID_LJPEG , "ljpg"}, + { AV_CODEC_ID_JPEGLS , "jls"}, { AV_CODEC_ID_PNG , "png"}, + { AV_CODEC_ID_PNG , "pns"}, { AV_CODEC_ID_PNG , "mng"}, { AV_CODEC_ID_PPM , "ppm"}, { AV_CODEC_ID_PPM , "pnm"}, @@ -45,6 +48,7 @@ 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"}, @@ -59,12 +63,18 @@ 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 , "j2k"}, { AV_CODEC_ID_JPEG2000 , "jp2"}, { AV_CODEC_ID_JPEG2000 , "jpc"}, { AV_CODEC_ID_DPX , "dpx"}, + { AV_CODEC_ID_EXR , "exr"}, { AV_CODEC_ID_PICTOR , "pic"}, + { AV_CODEC_ID_V210X , "yuv10"}, { AV_CODEC_ID_XBM , "xbm"}, + { AV_CODEC_ID_XFACE , "xface"}, { AV_CODEC_ID_XWD , "xwd"}, { AV_CODEC_ID_NONE , NULL} }; diff --git a/libavformat/img2dec.c b/libavformat/img2dec.c index 14f7785144..c493f1add7 100644 --- a/libavformat/img2dec.c +++ b/libavformat/img2dec.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 */ @@ -27,6 +27,19 @@ #include "libavutil/parseutils.h" #include "avformat.h" #include "internal.h" +#if HAVE_GLOB +#include <glob.h> + +/* Locally define as 0 (bitwise-OR no-op) any missing glob options that + are non-posix glibc/bsd extensions. */ +#ifndef GLOB_NOMAGIC +#define GLOB_NOMAGIC 0 +#endif +#ifndef GLOB_BRACE +#define GLOB_BRACE 0 +#endif + +#endif /* HAVE_GLOB */ typedef struct { const AVClass *class; /**< Class for private options. */ @@ -35,12 +48,19 @@ typedef struct { int img_number; int img_count; int is_pipe; + int split_planes; /**< use independent file for each Y, U, V plane */ char path[1024]; char *pixel_format; /**< Set by a private option. */ char *video_size; /**< Set by a private option. */ char *framerate; /**< Set by a private option. */ int 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; } VideoDemuxData; static const int sizes[][2] = { @@ -69,15 +89,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; @@ -88,7 +137,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 */ @@ -128,6 +177,10 @@ static int 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")) + return 5; else return AVPROBE_SCORE_MAX/2; } @@ -183,9 +236,67 @@ static int read_header(AVFormatContext *s1) } 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 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; @@ -201,8 +312,12 @@ static int read_header(AVFormatContext *s1) st->codec->codec_type = AVMEDIA_TYPE_AUDIO; st->codec->codec_id = s1->audio_codec_id; }else{ + const char *str= strrchr(s->path, '.'); + s->split_planes = str && !av_strcasecmp(str + 1, "y"); st->codec->codec_type = AVMEDIA_TYPE_VIDEO; st->codec->codec_id = ff_guess_image2_codec(s->path); + if (st->codec->codec_id == AV_CODEC_ID_LJPEG) + st->codec->codec_id = AV_CODEC_ID_MJPEG; } if(st->codec->codec_type == AVMEDIA_TYPE_VIDEO && pix_fmt != AV_PIX_FMT_NONE) st->codec->pix_fmt = pix_fmt; @@ -213,7 +328,8 @@ static int read_header(AVFormatContext *s1) static int 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}; @@ -226,9 +342,15 @@ static int 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) { @@ -239,7 +361,7 @@ static int 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; } @@ -248,12 +370,13 @@ static int read_packet(AVFormatContext *s1, AVPacket *pkt) infer_size(&codec->width, &codec->height, size[0]); } else { f[0] = s1->pb; - if (f[0]->eof_reached) + if (url_feof(f[0])) return AVERROR(EIO); size[0]= 4096; } - 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; @@ -278,14 +401,32 @@ static int read_packet(AVFormatContext *s1, AVPacket *pkt) } } +static int read_close(struct AVFormatContext* s1) +{ + VideoDemuxData *s = s1->priv_data; +#if HAVE_GLOB + if (s->use_glob) { + globfree(&s->globstate); + } +#endif + 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_STRING, {.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","glob/sequence pattern type", 0, AV_OPT_TYPE_CONST, {.i64=PT_GLOB_SEQUENCE}, INT_MIN, INT_MAX, DEC, "pattern_type" }, + { "glob", "glob pattern type", 0, AV_OPT_TYPE_CONST, {.i64=PT_GLOB}, INT_MIN, INT_MAX, DEC, "pattern_type" }, + { "sequence", "glob 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(video_size), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, DEC }, { NULL }, }; @@ -303,6 +444,7 @@ AVInputFormat ff_image2_demuxer = { .read_probe = read_probe, .read_header = read_header, .read_packet = read_packet, + .read_close = read_close, .flags = AVFMT_NOFILE, .priv_class = &img2_class, }; diff --git a/libavformat/img2enc.c b/libavformat/img2enc.c index bf183560e0..dea4572209 100644 --- a/libavformat/img2enc.c +++ b/libavformat/img2enc.c @@ -3,26 +3,27 @@ * Copyright (c) 2000, 2001, 2002 Fabrice Bellard * Copyright (c) 2004 Michael Niedermayer * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "libavutil/intreadwrite.h" #include "libavutil/avstring.h" #include "libavutil/log.h" +#include "libavutil/opt.h" #include "avformat.h" #include "avio_internal.h" #include "internal.h" @@ -32,12 +33,15 @@ 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 updatefirst; } VideoMuxData; static int write_header(AVFormatContext *s) { VideoMuxData *img = s->priv_data; + const char *str; av_strlcpy(img->path, s->filename, sizeof(img->path)); @@ -47,6 +51,8 @@ static int write_header(AVFormatContext *s) else img->is_pipe = 1; + str = strrchr(img->path, '.'); + img->split_planes = str && !av_strcasecmp(str + 1, "y"); return 0; } @@ -60,11 +66,11 @@ static int write_packet(AVFormatContext *s, AVPacket *pkt) if (!img->is_pipe) { if (av_get_frame_filename(filename, sizeof(filename), - img->path, img->img_number) < 0 && img->img_number>1) { + img->path, img->img_number) < 0 && img->img_number>1 && !img->updatefirst) { av_log(s, AV_LOG_ERROR, "Could not get frame filename number %d from pattern '%s'\n", img->img_number, img->path); - return AVERROR(EIO); + return AVERROR(EINVAL); } for(i=0; i<3; i++){ if (avio_open2(&pb[i], filename, AVIO_FLAG_WRITE, @@ -73,7 +79,7 @@ static int write_packet(AVFormatContext *s, AVPacket *pkt) return AVERROR(EIO); } - if(codec->codec_id != AV_CODEC_ID_RAWVIDEO) + if(!img->split_planes) break; filename[ strlen(filename) - 1 ]= 'U' + i; } @@ -81,7 +87,7 @@ static int write_packet(AVFormatContext *s, AVPacket *pkt) 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); @@ -104,11 +110,13 @@ static int write_packet(AVFormatContext *s, AVPacket *pkt) 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 && AV_RB32(pkt->data) == 0xFF4FFF51){ + //jpeg2000 codestream }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"); + av_log(s, AV_LOG_ERROR, "malformed JPEG 2000 codestream %X\n", AV_RB32(pkt->data)); return -1; } } @@ -126,6 +134,7 @@ static int write_packet(AVFormatContext *s, AVPacket *pkt) #define OFFSET(x) offsetof(VideoMuxData, x) #define ENC AV_OPT_FLAG_ENCODING_PARAM static const AVOption muxoptions[] = { + { "updatefirst", "", OFFSET(updatefirst), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, ENC }, { "start_number", "first number in the sequence", OFFSET(img_number), AV_OPT_TYPE_INT, {.i64 = 1}, 1, INT_MAX, ENC }, { NULL }, }; @@ -141,9 +150,9 @@ static const AVClass img2mux_class = { AVOutputFormat ff_image2_muxer = { .name = "image2", .long_name = NULL_IF_CONFIG_SMALL("image2 sequence"), - .extensions = "bmp,dpx,jpeg,jpg,ljpg,pam,pbm,pcx,pgm,pgmyuv,png," - "ppm,sgi,tga,tif,tiff,jp2,xwd,sun,ras,rs,im1,im8,im24," - "sunras,xbm", + .extensions = "bmp,dpx,jls,jpeg,jpg,ljpg,pam,pbm,pcx,pgm,pgmyuv,png," + "ppm,sgi,tga,tif,tiff,jp2,j2c,xwd,sun,ras,rs,im1,im8,im24," + "sunras,xbm,xface", .priv_data_size = sizeof(VideoMuxData), .video_codec = AV_CODEC_ID_MJPEG, .write_header = write_header, diff --git a/libavformat/ingenientdec.c b/libavformat/ingenientdec.c index 42b29ef6e3..94c549c711 100644 --- a/libavformat/ingenientdec.c +++ b/libavformat/ingenientdec.c @@ -2,20 +2,20 @@ * RAW Ingenient MJPEG demuxer * Copyright (c) 2005 Alex Beregszaszi * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -44,17 +44,10 @@ static int ingenient_read_packet(AVFormatContext *s, AVPacket *pkt) av_log(s, AV_LOG_DEBUG, "Ingenient packet: size=%d, width=%d, height=%d, unk1=%d unk2=%d\n", size, w, h, unk1, unk2); - if (av_new_packet(pkt, size) < 0) - return AVERROR(ENOMEM); - - pkt->pos = avio_tell(s->pb); - pkt->stream_index = 0; - ret = avio_read(s->pb, pkt->data, size); - if (ret < 0) { - av_free_packet(pkt); + ret = av_get_packet(s->pb, pkt, size); + if (ret < 0) return ret; - } - pkt->size = ret; + pkt->stream_index = 0; return ret; } diff --git a/libavformat/internal.h b/libavformat/internal.h index 3ef46eaa20..2689b2f48c 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 */ @@ -42,20 +42,18 @@ typedef struct CodecMime{ enum AVCodecID id; } CodecMime; -void ff_dynarray_add(intptr_t **tab_ptr, int *nb_ptr, intptr_t elem); - #ifdef __GNUC__ #define dynarray_add(tab, nb_ptr, elem)\ do {\ __typeof__(tab) _tab = (tab);\ __typeof__(elem) _elem = (elem);\ (void)sizeof(**_tab == _elem); /* check that types are compatible */\ - ff_dynarray_add((intptr_t **)_tab, nb_ptr, (intptr_t)_elem);\ + av_dynarray_add(_tab, nb_ptr, _elem);\ } while(0) #else #define dynarray_add(tab, nb_ptr, elem)\ do {\ - ff_dynarray_add((intptr_t **)(tab), nb_ptr, (intptr_t)(elem));\ + av_dynarray_add((tab), nb_ptr, (elem));\ } while(0) #endif @@ -78,8 +76,9 @@ void ff_program_add_stream_index(AVFormatContext *ac, int progid, unsigned int i /** * Add packet to AVFormatContext->packet_buffer list, determining its * interleaved position using compare() function argument. + * @return 0, or < 0 on error */ -void ff_interleave_add_packet(AVFormatContext *s, AVPacket *pkt, +int ff_interleave_add_packet(AVFormatContext *s, AVPacket *pkt, int (*compare)(AVFormatContext *, AVPacket *, AVPacket *)); void ff_read_frame_flush(AVFormatContext *s); @@ -345,6 +344,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. */ @@ -353,5 +354,13 @@ void ff_compute_frame_duration(int *pnum, int *pden, AVStream *st, int ff_get_audio_frame_size(AVCodecContext *enc, int size, int mux); +/** + * Chooses a timebase for muxing the specified stream. + * + * The choosen timebase allows sample accurate timestamps based + * on the framerate or sample rate for audio streams. It also is + * at least as precisse as 1/min_precission would be. + */ +AVRational ff_choose_timebase(AVFormatContext *s, AVStream *st, int min_precission); #endif /* AVFORMAT_INTERNAL_H */ diff --git a/libavformat/ipmovie.c b/libavformat/ipmovie.c index b567fb2b14..748f4a4467 100644 --- a/libavformat/ipmovie.c +++ b/libavformat/ipmovie.c @@ -2,20 +2,20 @@ * Interplay MVE File Demuxer * Copyright (c) 2003 The ffmpeg Project * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -116,7 +116,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"); @@ -236,7 +236,7 @@ static int process_ipmovie_chunk(IPMVEContext *s, AVIOContext *pb, return chunk_type; /* read the next chunk, wherever the file happens to be pointing */ - if (pb->eof_reached) + if (url_feof(pb)) return CHUNK_EOF; if (avio_read(pb, chunk_preamble, CHUNK_PREAMBLE_SIZE) != CHUNK_PREAMBLE_SIZE) @@ -282,7 +282,7 @@ static int process_ipmovie_chunk(IPMVEContext *s, AVIOContext *pb, while ((chunk_size > 0) && (chunk_type != CHUNK_BAD)) { /* read the next chunk, wherever the file happens to be pointing */ - if (pb->eof_reached) { + if (url_feof(pb)) { chunk_type = CHUNK_EOF; break; } @@ -475,7 +475,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; @@ -528,8 +529,9 @@ static int ipmovie_probe(AVProbeData *p) uint8_t *b = p->buf; 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; @@ -542,14 +544,14 @@ static int ipmovie_read_header(AVFormatContext *s) AVPacket pkt; AVStream *st; unsigned char chunk_preamble[CHUNK_PREAMBLE_SIZE]; - int chunk_type; + int chunk_type, i; uint8_t signature_buffer[sizeof(signature)]; avio_read(pb, signature_buffer, sizeof(signature_buffer)); while (memcmp(signature_buffer, signature, sizeof(signature))) { memmove(signature_buffer, signature_buffer + 1, sizeof(signature_buffer) - 1); signature_buffer[sizeof(signature_buffer) - 1] = avio_r8(pb); - if (pb->eof_reached) + if (url_feof(pb)) return AVERROR_EOF; } /* initialize private context members */ @@ -560,6 +562,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; @@ -619,6 +624,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; @@ -628,10 +634,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/isom.c b/libavformat/isom.c index ad871292cf..32710569a3 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 */ @@ -84,13 +83,28 @@ const AVCodecTag ff_codec_movvideo_tags[] = { { AV_CODEC_ID_RAWVIDEO, MKTAG('A', 'B', 'G', 'R') }, { AV_CODEC_ID_RAWVIDEO, MKTAG('b', '1', '6', 'g') }, { AV_CODEC_ID_RAWVIDEO, MKTAG('b', '4', '8', 'r') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('b', 'x', 'b', 'g') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('b', 'x', 'r', 'g') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('b', 'x', 'y', 'v') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('N', 'O', '1', '6') }, { AV_CODEC_ID_RAWVIDEO, MKTAG('D', 'V', 'O', 'O') }, /* Digital Voodoo SD 8 Bit */ + { AV_CODEC_ID_RAWVIDEO, MKTAG('R', '4', '2', '0') }, /* Radius DV YUV PAL */ + { AV_CODEC_ID_RAWVIDEO, MKTAG('R', '4', '1', '1') }, /* Radius DV YUV NTSC */ { AV_CODEC_ID_R10K, MKTAG('R', '1', '0', 'k') }, /* UNCOMPRESSED 10BIT RGB */ { AV_CODEC_ID_R10K, MKTAG('R', '1', '0', 'g') }, /* UNCOMPRESSED 10BIT RGB */ { AV_CODEC_ID_R210, MKTAG('r', '2', '1', '0') }, /* UNCOMPRESSED 10BIT RGB */ + { AV_CODEC_ID_AVUI, MKTAG('A', 'V', 'U', 'I') }, /* AVID Uncompressed deinterleaved UYVY422 */ + { AV_CODEC_ID_AVRP, MKTAG('A', 'V', 'r', 'p') }, /* Avid 1:1 10-bit RGB Packer */ + { AV_CODEC_ID_AVRP, MKTAG('S', 'U', 'D', 'S') }, /* Avid DS Uncompressed */ { AV_CODEC_ID_V210, MKTAG('v', '2', '1', '0') }, /* UNCOMPRESSED 10BIT 4:2:2 */ + { AV_CODEC_ID_V210, MKTAG('b', 'x', 'y', '2') }, /* BOXX 10BIT 4:2:2 */ + { AV_CODEC_ID_V308, MKTAG('v', '3', '0', '8') }, /* UNCOMPRESSED 8BIT 4:4:4 */ + { AV_CODEC_ID_V408, MKTAG('v', '4', '0', '8') }, /* UNCOMPRESSED 8BIT 4:4:4:4 */ { AV_CODEC_ID_V410, MKTAG('v', '4', '1', '0') }, /* UNCOMPRESSED 10BIT 4:4:4 */ + { AV_CODEC_ID_Y41P, MKTAG('Y', '4', '1', 'P') }, /* UNCOMPRESSED 12BIT 4:1:1 */ + { AV_CODEC_ID_YUV4, MKTAG('y', 'u', 'v', '4') }, /* libquicktime packed yuv420p */ + { AV_CODEC_ID_TARGA_Y216, MKTAG('Y', '2', '1', '6') }, { AV_CODEC_ID_MJPEG, MKTAG('j', 'p', 'e', 'g') }, /* PhotoJPEG */ { AV_CODEC_ID_MJPEG, MKTAG('m', 'j', 'p', 'a') }, /* Motion-JPEG (format A) */ @@ -155,6 +169,7 @@ const AVCodecTag ff_codec_movvideo_tags[] = { { AV_CODEC_ID_MPEG1VIDEO, MKTAG('m', '1', 'v', '1') }, /* Apple MPEG-1 Camcorder */ { AV_CODEC_ID_MPEG1VIDEO, MKTAG('m', 'p', 'e', 'g') }, /* MPEG */ + { AV_CODEC_ID_MPEG1VIDEO, MKTAG('m', '1', 'v', ' ') }, { AV_CODEC_ID_MPEG2VIDEO, MKTAG('m', '2', 'v', '1') }, /* Apple MPEG-2 Camcorder */ { AV_CODEC_ID_MPEG2VIDEO, MKTAG('h', 'd', 'v', '1') }, /* MPEG2 HDV 720p30 */ { AV_CODEC_ID_MPEG2VIDEO, MKTAG('h', 'd', 'v', '2') }, /* MPEG2 HDV 1080i60 */ @@ -206,24 +221,27 @@ const AVCodecTag ff_codec_movvideo_tags[] = { { AV_CODEC_ID_TIFF, MKTAG('t', 'i', 'f', 'f') }, /* TIFF embedded in MOV */ { AV_CODEC_ID_GIF, MKTAG('g', 'i', 'f', ' ') }, /* embedded gif files as frames (usually one "click to play movie" frame) */ { AV_CODEC_ID_PNG, MKTAG('p', 'n', 'g', ' ') }, + { AV_CODEC_ID_PNG, MKTAG('M', 'N', 'G', ' ') }, { AV_CODEC_ID_VC1, MKTAG('v', 'c', '-', '1') }, /* SMPTE RP 2025 */ { AV_CODEC_ID_CAVS, MKTAG('a', 'v', 's', '2') }, { AV_CODEC_ID_DIRAC, MKTAG('d', 'r', 'a', 'c') }, { AV_CODEC_ID_DNXHD, MKTAG('A', 'V', 'd', 'n') }, /* AVID DNxHD */ - { AV_CODEC_ID_FLV1, MKTAG('H', '2', '6', '3') }, /* Flash Media Server */ +// { AV_CODEC_ID_FLV1, MKTAG('H', '2', '6', '3') }, /* Flash Media Server */ { AV_CODEC_ID_MSMPEG4V3, MKTAG('3', 'I', 'V', 'D') }, /* 3ivx DivX Doctor */ { AV_CODEC_ID_RAWVIDEO, MKTAG('A', 'V', '1', 'x') }, /* AVID 1:1x */ { AV_CODEC_ID_RAWVIDEO, MKTAG('A', 'V', 'u', 'p') }, { AV_CODEC_ID_SGI, MKTAG('s', 'g', 'i', ' ') }, /* SGI */ { AV_CODEC_ID_DPX, MKTAG('d', 'p', 'x', ' ') }, /* DPX */ + { AV_CODEC_ID_EXR, MKTAG('e', 'x', 'r', ' ') }, /* OpenEXR */ { AV_CODEC_ID_PRORES, MKTAG('a', 'p', 'c', 'h') }, /* Apple ProRes 422 High Quality */ { AV_CODEC_ID_PRORES, MKTAG('a', 'p', 'c', 'n') }, /* Apple ProRes 422 Standard Definition */ { AV_CODEC_ID_PRORES, MKTAG('a', 'p', 'c', 's') }, /* Apple ProRes 422 LT */ { AV_CODEC_ID_PRORES, MKTAG('a', 'p', 'c', 'o') }, /* Apple ProRes 422 Proxy */ { AV_CODEC_ID_PRORES, MKTAG('a', 'p', '4', 'h') }, /* Apple ProRes 4444 */ + { AV_CODEC_ID_FLIC, MKTAG('f', 'l', 'i', 'c') }, { AV_CODEC_ID_NONE, 0 }, }; @@ -281,6 +299,7 @@ const AVCodecTag ff_codec_movaudio_tags[] = { 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 }, }; @@ -343,7 +362,7 @@ int ff_mov_lang_to_iso639(unsigned code, char to[4]) memset(to, 0, 4); /* is it the mangled iso code? */ /* see http://www.geocities.com/xhelmboyx/quicktime/formats/mp4-layout.txt */ - if (code > 138) { + if (code >= 0x400 && code != 0x7fff) { for (i = 2; i >= 0; i--) { to[i] = 0x60 + (code & 0x1f); code >>= 5; @@ -450,3 +469,95 @@ int ff_mp4_read_dec_config_descr(AVFormatContext *fc, AVStream *st, AVIOContext } return 0; } + +typedef struct MovChannelLayout { + int64_t channel_layout; + uint32_t layout_tag; +} MovChannelLayout; + +static const MovChannelLayout mov_channel_layout[] = { + { AV_CH_LAYOUT_MONO, (100<<16) | 1}, // kCAFChannelLayoutTag_Mono + { AV_CH_LAYOUT_STEREO, (101<<16) | 2}, // kCAFChannelLayoutTag_Stereo + { AV_CH_LAYOUT_STEREO, (102<<16) | 2}, // kCAFChannelLayoutTag_StereoHeadphones + { AV_CH_LAYOUT_2_1, (131<<16) | 3}, // kCAFChannelLayoutTag_ITU_2_1 + { AV_CH_LAYOUT_QUAD, (132<<16) | 4}, // kCAFChannelLayoutTag_ITU_2_2 + { AV_CH_LAYOUT_2_2, (132<<16) | 4}, // kCAFChannelLayoutTag_ITU_2_2 + { AV_CH_LAYOUT_QUAD, (108<<16) | 4}, // kCAFChannelLayoutTag_Quadraphonic + { AV_CH_LAYOUT_SURROUND, (113<<16) | 3}, // kCAFChannelLayoutTag_MPEG_3_0_A + { AV_CH_LAYOUT_4POINT0, (115<<16) | 4}, // kCAFChannelLayoutTag_MPEG_4_0_A + { AV_CH_LAYOUT_5POINT0_BACK, (117<<16) | 5}, // kCAFChannelLayoutTag_MPEG_5_0_A + { AV_CH_LAYOUT_5POINT0, (117<<16) | 5}, // kCAFChannelLayoutTag_MPEG_5_0_A + { AV_CH_LAYOUT_5POINT1_BACK, (121<<16) | 6}, // kCAFChannelLayoutTag_MPEG_5_1_A + { AV_CH_LAYOUT_5POINT1, (121<<16) | 6}, // kCAFChannelLayoutTag_MPEG_5_1_A + { AV_CH_LAYOUT_7POINT1, (128<<16) | 8}, // kCAFChannelLayoutTag_MPEG_7_1_C + { AV_CH_LAYOUT_7POINT1_WIDE, (126<<16) | 8}, // kCAFChannelLayoutTag_MPEG_7_1_A + { AV_CH_LAYOUT_5POINT1_BACK|AV_CH_LAYOUT_STEREO_DOWNMIX, (130<<16) | 8}, // kCAFChannelLayoutTag_SMPTE_DTV + { AV_CH_LAYOUT_STEREO|AV_CH_LOW_FREQUENCY, (133<<16) | 3}, // kCAFChannelLayoutTag_DVD_4 + { AV_CH_LAYOUT_2_1|AV_CH_LOW_FREQUENCY, (134<<16) | 4}, // kCAFChannelLayoutTag_DVD_5 + { AV_CH_LAYOUT_QUAD|AV_CH_LOW_FREQUENCY, (135<<16) | 4}, // kCAFChannelLayoutTag_DVD_6 + { AV_CH_LAYOUT_2_2|AV_CH_LOW_FREQUENCY, (135<<16) | 4}, // kCAFChannelLayoutTag_DVD_6 + { AV_CH_LAYOUT_SURROUND|AV_CH_LOW_FREQUENCY, (136<<16) | 4}, // kCAFChannelLayoutTag_DVD_10 + { AV_CH_LAYOUT_4POINT0|AV_CH_LOW_FREQUENCY, (137<<16) | 5}, // kCAFChannelLayoutTag_DVD_11 + { 0, 0}, +}; +#if 0 +int ff_mov_read_chan(AVFormatContext *s, AVStream *st, int64_t size) +{ + AVCodecContext *codec= st->codec; + uint32_t layout_tag; + AVIOContext *pb = s->pb; + const MovChannelLayout *layouts = mov_channel_layout; + + if (size < 12) + return AVERROR_INVALIDDATA; + + layout_tag = avio_rb32(pb); + size -= 4; + if (layout_tag == 0) { // kCAFChannelLayoutTag_UseChannelDescriptions + // Channel descriptions not implemented + av_log_ask_for_sample(s, "Unimplemented container channel layout.\n"); + avio_skip(pb, size); + return 0; + } + if (layout_tag == 0x10000) { // kCAFChannelLayoutTag_UseChannelBitmap + codec->channel_layout = avio_rb32(pb); + size -= 4; + avio_skip(pb, size); + return 0; + } + while (layouts->channel_layout) { + if (layout_tag == layouts->layout_tag) { + codec->channel_layout = layouts->channel_layout; + break; + } + layouts++; + } + if (!codec->channel_layout) + av_log(s, AV_LOG_WARNING, "Unknown container channel layout.\n"); + avio_skip(pb, size); + + return 0; +} +#endif + +void ff_mov_write_chan(AVIOContext *pb, int64_t channel_layout) +{ + const MovChannelLayout *layouts; + uint32_t layout_tag = 0; + + for (layouts = mov_channel_layout; layouts->channel_layout; layouts++) + if (channel_layout == layouts->channel_layout) { + layout_tag = layouts->layout_tag; + break; + } + + if (layout_tag) { + avio_wb32(pb, layout_tag); // mChannelLayoutTag + avio_wb32(pb, 0); // mChannelBitmap + } else { + avio_wb32(pb, 0x10000); // kCAFChannelLayoutTag_UseChannelBitmap + avio_wb32(pb, channel_layout); + } + avio_wb32(pb, 0); // mNumberChannelDescriptions +} + diff --git a/libavformat/isom.h b/libavformat/isom.h index 932b1d0a68..f6eba56220 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 */ @@ -108,14 +108,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 alt_sample_size; ///< always contains sample size from stsz atom unsigned int sample_count; int *sample_sizes; int keyframe_absent; unsigned int keyframe_count; int *keyframes; int time_scale; - int64_t time_offset; ///< time offset of the first edit list entry + int64_t empty_duration; ///< empty duration of the first edit list entry + int64_t start_time; ///< start time of the media + int64_t time_offset; ///< time offset of the edit list entries int current_sample; unsigned int bytes_per_frame; unsigned int samples_per_frame; @@ -125,6 +128,9 @@ typedef struct MOVStreamContext { unsigned drefs_count; MOVDref *drefs; int dref_id; + unsigned tref_type; + unsigned trefs_count; + uint32_t *trefs; int wrong_dts; ///< dts are wrong due to huge ctts offset (iMovie files) int width; ///< tkhd width int height; ///< tkhd height @@ -132,12 +138,15 @@ typedef struct MOVStreamContext { uint32_t palette[256]; int has_palette; int64_t data_size; + uint32_t tmcd_flags; ///< tmcd track flags int64_t track_end; ///< used for dts generation in fragmented movie files + int start_pad; ///< amount of samples to skip due to enc-dec delay unsigned int rap_group_count; MOVSbgp *rap_group; } MOVStreamContext; typedef struct MOVContext { + AVClass *avclass; AVFormatContext *fc; int time_scale; int64_t duration; ///< duration of the longest track @@ -151,6 +160,8 @@ 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 } MOVContext; @@ -194,5 +205,6 @@ int ff_mov_read_esds(AVFormatContext *fc, AVIOContext *pb, MOVAtom atom); enum AVCodecID ff_mov_get_lpcm_codec_id(int bps, int flags); int ff_mov_read_stsd_entries(MOVContext *c, AVIOContext *pb, int entries); +void ff_mov_write_chan(AVIOContext *pb, int64_t channel_layout); #endif /* AVFORMAT_ISOM_H */ diff --git a/libavformat/iss.c b/libavformat/iss.c index 14f64d2392..4268900840 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 */ @@ -87,6 +87,11 @@ static av_cold int iss_read_header(AVFormatContext *s) get_token(pb, token, sizeof(token)); //Version ID get_token(pb, token, sizeof(token)); //Size + if (iss->packet_size <= 0) { + av_log(s, AV_LOG_ERROR, "packet_size %d is invalid\n", iss->packet_size); + return AVERROR_INVALIDDATA; + } + iss->sample_start_pos = avio_tell(pb); st = avformat_new_stream(s, NULL); diff --git a/libavformat/iv8.c b/libavformat/iv8.c index 56909e3a22..38b79609f6 100644 --- a/libavformat/iv8.c +++ b/libavformat/iv8.c @@ -1,20 +1,20 @@ /* * Copyright (c) 2009 Michael Niedermayer * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/ivfdec.c b/libavformat/ivfdec.c index b3555f42a4..c0fdafbf02 100644 --- a/libavformat/ivfdec.c +++ b/libavformat/ivfdec.c @@ -1,20 +1,20 @@ /* * Copyright (c) 2010 David Conrad * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/ivfenc.c b/libavformat/ivfenc.c index e0455970e5..ddb205b8fa 100644 --- a/libavformat/ivfenc.c +++ b/libavformat/ivfenc.c @@ -1,20 +1,20 @@ /* * Copyright (c) 2010 Reimar Döffinger * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "avformat.h" diff --git a/libavformat/jacosubdec.c b/libavformat/jacosubdec.c new file mode 100644 index 0000000000..f9defd292d --- /dev/null +++ b/libavformat/jacosubdec.c @@ -0,0 +1,260 @@ +/* + * 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/jacosub.h" +#include "libavutil/avstring.h" +#include "libavutil/bprint.h" +#include "libavutil/intreadwrite.h" + +typedef struct { + int shift; + unsigned timeres; + FFDemuxSubtitlesQueue q; +} JACOsubContext; + +static int timed_line(const char *ptr) +{ + char c; + return (sscanf(ptr, "%*u:%*u:%*u.%*u %*u:%*u:%*u.%*u %c", &c) == 1 || + sscanf(ptr, "@%*u @%*u %c", &c) == 1); +} + +static int jacosub_probe(AVProbeData *p) +{ + const char *ptr = p->buf; + const char *ptr_end = p->buf + p->buf_size; + + if (AV_RB24(ptr) == 0xEFBBBF) + ptr += 3; /* skip UTF-8 BOM */ + + while (ptr < ptr_end) { + while (jss_whitespace(*ptr)) + ptr++; + if (*ptr != '#' && *ptr != '\n') { + if (timed_line(ptr)) + return AVPROBE_SCORE_MAX/2 + 1; + return 0; + } + ptr += strcspn(ptr, "\n") + 1; + } + 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; + + AVStream *st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + avpriv_set_pts_info(st, 64, 1, 100); + st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE; + st->codec->codec_id = AV_CODEC_ID_JACOSUB; + + jacosub->timeres = 30; + + av_bprint_init(&header, 1024+FF_INPUT_BUFFER_PADDING_SIZE, 4096); + + while (!url_feof(pb)) { + int cmd_len; + const char *p = line; + int64_t pos = avio_tell(pb); + int len = ff_get_line(pb, line, sizeof(line)); + + p = jss_skip_whitespace(p); + + /* queue timed line */ + if (merge_line || timed_line(p)) { + AVPacket *sub; + + sub = ff_subtitles_queue_insert(&jacosub->q, line, len, merge_line); + if (!sub) + return AVERROR(ENOMEM); + sub->pos = pos; + merge_line = len > 1 && !strcmp(&line[len - 2], "\\\n"); + continue; + } + + /* skip all non-compiler commands and focus on the command */ + if (*p != '#') + continue; + p++; + i = get_jss_cmd(p[0]); + if (i == -1) + continue; + + /* trim command + spaces */ + cmd_len = strlen(cmds[i]); + if (av_strncasecmp(p, cmds[i], cmd_len) == 0) + p += cmd_len; + else + p++; + p = jss_skip_whitespace(p); + + /* handle commands which affect the whole script */ + switch (cmds[i][0]) { + case 'S': // SHIFT command affect the whole script... + if (!shift_set) { + jacosub->shift = get_shift(jacosub->timeres, p); + shift_set = 1; + } + av_bprintf(&header, "#S %s", p); + break; + case 'T': // ...but must be placed after TIMERES + jacosub->timeres = strtol(p, NULL, 10); + if (!jacosub->timeres) + jacosub->timeres = 30; + else + av_bprintf(&header, "#T %s", p); + break; + } + } + + /* general/essential directives in the extradata */ + av_bprint_finalize(&header, (char **)&st->codec->extradata); + st->codec->extradata_size = header.len + 1; + + /* 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); +} + +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_close = jacosub_read_close, + .flags = AVFMT_GENERIC_INDEX, +}; diff --git a/libavformat/jacosubenc.c b/libavformat/jacosubenc.c new file mode 100644 index 0000000000..a11e45a4aa --- /dev/null +++ b/libavformat/jacosubenc.c @@ -0,0 +1,42 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "avformat.h" +#include "rawenc.h" + +static int jacosub_write_header(AVFormatContext *s) +{ + const AVCodecContext *avctx = s->streams[0]->codec; + + if (avctx->extradata_size) { + avio_write(s->pb, avctx->extradata, avctx->extradata_size - 1); + avio_flush(s->pb); + } + return 0; +} + +AVOutputFormat ff_jacosub_muxer = { + .name = "jacosub", + .long_name = NULL_IF_CONFIG_SMALL("JACOsub subtitle format"), + .mime_type = "text/x-jacosub", + .extensions = "jss,js", + .write_header = jacosub_write_header, + .write_packet = ff_raw_write_packet, + .flags = AVFMT_TS_NONSTRICT, + .subtitle_codec = AV_CODEC_ID_JACOSUB, +}; diff --git a/libavformat/jvdec.c b/libavformat/jvdec.c index 47a6142403..2eac265de7 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 */ @@ -52,8 +52,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; } @@ -140,7 +140,7 @@ static int read_packet(AVFormatContext *s, AVPacket *pkt) AVIOContext *pb = s->pb; AVStream *ast = s->streams[0]; - while (!s->pb->eof_reached && jv->pts < ast->nb_index_entries) { + while (!url_feof(s->pb) && jv->pts < ast->nb_index_entries) { const AVIndexEntry *e = ast->index_entries + jv->pts; const JVFrame *jvf = jv->frames + jv->pts; @@ -164,7 +164,7 @@ static int read_packet(AVFormatContext *s, AVPacket *pkt) AV_WL32(pkt->data, jvf->video_size); pkt->data[4] = jvf->video_type; - if (avio_read(pb, pkt->data + JV_PREAMBLE_SIZE, size) < 0) + if ((size = avio_read(pb, pkt->data + JV_PREAMBLE_SIZE, size)) < 0) return AVERROR(EIO); pkt->size = size + JV_PREAMBLE_SIZE; @@ -216,6 +216,15 @@ static int read_seek(AVFormatContext *s, int stream_index, return 0; } +static int read_close(AVFormatContext *s) +{ + JVDemuxContext *jv = s->priv_data; + + av_freep(&jv->frames); + + return 0; +} + AVInputFormat ff_jv_demuxer = { .name = "jv", .long_name = NULL_IF_CONFIG_SMALL("Bitmap Brothers JV"), @@ -224,4 +233,5 @@ AVInputFormat ff_jv_demuxer = { .read_header = read_header, .read_packet = read_packet, .read_seek = read_seek, + .read_close = read_close, }; diff --git a/libavformat/latmenc.c b/libavformat/latmenc.c index 7270292f7a..233eab8382 100644 --- a/libavformat/latmenc.c +++ b/libavformat/latmenc.c @@ -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_bits(&gb, avctx->extradata, avctx->extradata_size * 8); + skip_bits_long(&gb, ctx->off + 3); avpriv_copy_pce_data(bs, &gb); } } @@ -124,28 +139,27 @@ 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; } + if (pkt->size > 0x1fff) + goto too_large; - buf = av_malloc(pkt->size+1024); - if (!buf) - return AVERROR(ENOMEM); - - 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 +172,46 @@ static int latm_write_packet(AVFormatContext *s, AVPacket *pkt) /* The LATM payload is written unaligned */ /* PayloadMux() */ - for (i = 0; i < pkt->size; i++) - put_bits(&bs, 8, pkt->data[i]); + if (pkt->size && (pkt->data[0] & 0xe1) == 0x81) { + // Convert byte-aligned DSE to non-aligned. + // Due to the input format encoding we know that + // it is naturally byte-aligned in the input stream, + // so there are no padding bits to account for. + // To avoid having to add padding bits and rearrange + // the whole stream we just remove the byte-align flag. + // This allows us to remux our FATE AAC samples into latm + // files that are still playable with minimal effort. + put_bits(&bs, 8, pkt->data[0] & 0xfe); + avpriv_copy_bits(&bs, pkt->data + 1, 8*pkt->size - 8); + } else + avpriv_copy_bits(&bs, pkt->data, 8*pkt->size); avpriv_align_put_bits(&bs); flush_put_bits(&bs); len = put_bits_count(&bs) >> 3; + if (len > 0x1fff) + goto too_large; + loas_header[1] |= (len >> 8) & 0x1f; loas_header[2] |= len & 0xff; avio_write(pb, loas_header, 3); - avio_write(pb, buf, len); - - av_free(buf); + avio_write(pb, ctx->buffer, len); return 0; + +too_large: + av_log(s, AV_LOG_ERROR, "LATM packet size larger than maximum size 0x1fff\n"); + return AVERROR_INVALIDDATA; } AVOutputFormat ff_latm_muxer = { .name = "latm", .long_name = NULL_IF_CONFIG_SMALL("LOAS/LATM"), .mime_type = "audio/MP4A-LATM", - .extensions = "latm", + .extensions = "latm,loas", .priv_data_size = sizeof(LATMContext), .audio_codec = AV_CODEC_ID_AAC, .video_codec = AV_CODEC_ID_NONE, diff --git a/libavformat/libavformat.v b/libavformat/libavformat.v index 6f11d600b9..5f86d62130 100644 --- a/libavformat/libavformat.v +++ b/libavformat/libavformat.v @@ -1,4 +1,44 @@ LIBAVFORMAT_$MAJOR { global: av*; + #FIXME those are for ffserver + ff_inet_aton; + ff_socket_nonblock; + ffm_set_write_index; + ffm_read_write_index; + ffm_write_write_index; + ff_mpegts_parse_close; + ff_mpegts_parse_open; + ff_mpegts_parse_packet; + ff_rtsp_parse_line; + ff_rtp_get_local_rtp_port; + ff_rtp_get_local_rtcp_port; + ffio_open_dyn_packet_buf; + ffio_set_buf_size; + ffurl_close; + ffurl_open; + ffurl_read_complete; + ffurl_seek; + ffurl_size; + ffurl_write; + ffurl_protocol_next; + url_open; + url_close; + url_write; + url_get_max_packet_size; + #those are deprecated, remove on next bump + find_info_tag; + parse_date; + dump_format; + url_*; + ff_timefilter_destroy; + ff_timefilter_new; + ff_timefilter_update; + ff_timefilter_reset; + get_*; + put_*; + udp_set_remote_url; + udp_get_local_port; + init_checksum; + init_put_byte; local: *; }; diff --git a/libavformat/libmodplug.c b/libavformat/libmodplug.c new file mode 100644 index 0000000000..aa8edccb59 --- /dev/null +++ b/libavformat/libmodplug.c @@ -0,0 +1,368 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** +* @file +* ModPlug demuxer +* @todo better probing than extensions matching +*/ + +#include <libmodplug/modplug.h> +#include "libavutil/avstring.h" +#include "libavutil/eval.h" +#include "libavutil/opt.h" +#include "avformat.h" +#include "internal.h" + +typedef struct ModPlugContext { + const AVClass *class; + ModPlugFile *f; + uint8_t *buf; ///< input file content + + /* options */ + int noise_reduction; + int reverb_depth; + int reverb_delay; + int bass_amount; + int bass_range; + int surround_depth; + int surround_delay; + + int max_size; ///< max file size to allocate + + /* optional video stream */ + double ts_per_packet; ///< used to define the pts/dts using packet_count; + int packet_count; ///< total number of audio packets + int print_textinfo; ///< bool flag for printing speed, tempo, order, ... + int video_stream; ///< 1 if the user want a video stream, otherwise 0 + int w; ///< video stream width in char (one char = 8x8px) + int h; ///< video stream height in char (one char = 8x8px) + int video_switch; ///< 1 if current packet is video, otherwise 0 + int fsize; ///< constant frame size + int linesize; ///< line size in bytes + char *color_eval; ///< color eval user input expression + AVExpr *expr; ///< parsed color eval expression +} ModPlugContext; + +static const char *var_names[] = { + "x", "y", + "w", "h", + "t", + "speed", "tempo", "order", "pattern", "row", + NULL +}; + +enum var_name { + VAR_X, VAR_Y, + VAR_W, VAR_H, + VAR_TIME, + VAR_SPEED, VAR_TEMPO, VAR_ORDER, VAR_PATTERN, VAR_ROW, + VAR_VARS_NB +}; + +#define FF_MODPLUG_MAX_FILE_SIZE (100 * 1<<20) // 100M +#define FF_MODPLUG_DEF_FILE_SIZE ( 5 * 1<<20) // 5M + +#define OFFSET(x) offsetof(ModPlugContext, x) +#define D AV_OPT_FLAG_DECODING_PARAM +static const AVOption options[] = { + {"noise_reduction", "Enable noise reduction 0(off)-1(on)", OFFSET(noise_reduction), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, D}, + {"reverb_depth", "Reverb level 0(quiet)-100(loud)", OFFSET(reverb_depth), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 100, D}, + {"reverb_delay", "Reverb delay in ms, usually 40-200ms", OFFSET(reverb_delay), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, D}, + {"bass_amount", "XBass level 0(quiet)-100(loud)", OFFSET(bass_amount), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 100, D}, + {"bass_range", "XBass cutoff in Hz 10-100", OFFSET(bass_range), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 100, D}, + {"surround_depth", "Surround level 0(quiet)-100(heavy)", OFFSET(surround_depth), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 100, D}, + {"surround_delay", "Surround delay in ms, usually 5-40ms", OFFSET(surround_delay), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, D}, + {"max_size", "Max file size supported (in bytes). Default is 5MB. Set to 0 for no limit (not recommended)", + OFFSET(max_size), AV_OPT_TYPE_INT, {.i64 = FF_MODPLUG_DEF_FILE_SIZE}, 0, FF_MODPLUG_MAX_FILE_SIZE, D}, + {"video_stream_expr", "Color formula", OFFSET(color_eval), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, D}, + {"video_stream", "Make demuxer output a video stream", OFFSET(video_stream), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, D}, + {"video_stream_w", "Video stream width in char (one char = 8x8px)", OFFSET(w), AV_OPT_TYPE_INT, {.i64 = 30}, 20, 512, D}, + {"video_stream_h", "Video stream height in char (one char = 8x8px)", OFFSET(h), AV_OPT_TYPE_INT, {.i64 = 30}, 20, 512, D}, + {"video_stream_ptxt", "Print speed, tempo, order, ... in video stream", OFFSET(print_textinfo), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1, D}, + {NULL}, +}; + +#define SET_OPT_IF_REQUESTED(libopt, opt, flag) do { \ + if (modplug->opt) { \ + settings.libopt = modplug->opt; \ + settings.mFlags |= flag; \ + } \ +} while (0) + +#define ADD_META_MULTIPLE_ENTRIES(entry_name, fname) do { \ + if (n_## entry_name ##s) { \ + unsigned i, n = 0; \ + \ + for (i = 0; i < n_## entry_name ##s; i++) { \ + char item_name[64] = {0}; \ + fname(f, i, item_name); \ + if (!*item_name) \ + continue; \ + if (n) \ + av_dict_set(&s->metadata, #entry_name, "\n", AV_DICT_APPEND); \ + av_dict_set(&s->metadata, #entry_name, item_name, AV_DICT_APPEND); \ + n++; \ + } \ + \ + extra = av_asprintf(", %u/%u " #entry_name "%s", \ + n, n_## entry_name ##s, n > 1 ? "s" : ""); \ + if (!extra) \ + return AVERROR(ENOMEM); \ + av_dict_set(&s->metadata, "extra info", extra, AV_DICT_APPEND); \ + av_free(extra); \ + } \ +} while (0) + +static int modplug_load_metadata(AVFormatContext *s) +{ + ModPlugContext *modplug = s->priv_data; + ModPlugFile *f = modplug->f; + char *extra; + const char *name = ModPlug_GetName(f); + const char *msg = ModPlug_GetMessage(f); + + unsigned n_instruments = ModPlug_NumInstruments(f); + unsigned n_samples = ModPlug_NumSamples(f); + unsigned n_patterns = ModPlug_NumPatterns(f); + unsigned n_channels = ModPlug_NumChannels(f); + + if (name && *name) av_dict_set(&s->metadata, "name", name, 0); + if (msg && *msg) av_dict_set(&s->metadata, "message", msg, 0); + + extra = av_asprintf("%u pattern%s, %u channel%s", + n_patterns, n_patterns > 1 ? "s" : "", + n_channels, n_channels > 1 ? "s" : ""); + if (!extra) + return AVERROR(ENOMEM); + av_dict_set(&s->metadata, "extra info", extra, AV_DICT_DONT_STRDUP_VAL); + + ADD_META_MULTIPLE_ENTRIES(instrument, ModPlug_InstrumentName); + ADD_META_MULTIPLE_ENTRIES(sample, ModPlug_SampleName); + + return 0; +} + +#define AUDIO_PKT_SIZE 512 + +static int modplug_read_header(AVFormatContext *s) +{ + AVStream *st; + AVIOContext *pb = s->pb; + ModPlug_Settings settings; + ModPlugContext *modplug = s->priv_data; + int 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 %dB " + "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 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_header = modplug_read_header, + .read_packet = modplug_read_packet, + .read_close = modplug_read_close, + .read_seek = modplug_read_seek, + .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", // compressed mods + .priv_class = &modplug_class, +}; diff --git a/libavformat/libnut.c b/libavformat/libnut.c new file mode 100644 index 0000000000..838c55a403 --- /dev/null +++ b/libavformat/libnut.c @@ -0,0 +1,322 @@ +/* + * NUT (de)muxing via libnut + * copyright (c) 2006 Oded Shimon <ods15@ods15.dyndns.org> + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * NUT demuxing and muxing via libnut. + * @author Oded Shimon <ods15@ods15.dyndns.org> + */ + +#include "avformat.h" +#include "internal.h" +#include "riff.h" +#include <libnut.h> + +#define ID_STRING "nut/multimedia container" +#define ID_LENGTH (strlen(ID_STRING) + 1) + +typedef struct { + nut_context_tt * nut; + nut_stream_header_tt * s; +} NUTContext; + +static const AVCodecTag nut_tags[] = { + { AV_CODEC_ID_MPEG4, MKTAG('m', 'p', '4', 'v') }, + { AV_CODEC_ID_MP3, MKTAG('m', 'p', '3', ' ') }, + { AV_CODEC_ID_VORBIS, MKTAG('v', 'r', 'b', 's') }, + { 0, 0 }, +}; + +#if CONFIG_LIBNUT_MUXER +static int av_write(void * h, size_t len, const uint8_t * buf) { + AVIOContext * bc = h; + avio_write(bc, buf, len); + //avio_flush(bc); + return len; +} + +static int nut_write_header(AVFormatContext * avf) { + NUTContext * priv = avf->priv_data; + AVIOContext * bc = avf->pb; + nut_muxer_opts_tt mopts = { + .output = { + .priv = bc, + .write = av_write, + }, + .alloc = { av_malloc, av_realloc, av_free }, + .write_index = 1, + .realtime_stream = 0, + .max_distance = 32768, + .fti = NULL, + }; + nut_stream_header_tt * s; + int i; + + priv->s = s = av_mallocz((avf->nb_streams + 1) * sizeof*s); + if(!s) + return AVERROR(ENOMEM); + + for (i = 0; i < avf->nb_streams; i++) { + AVCodecContext * codec = avf->streams[i]->codec; + int j; + int fourcc = 0; + int num, denom, ssize; + + s[i].type = codec->codec_type == AVMEDIA_TYPE_VIDEO ? NUT_VIDEO_CLASS : NUT_AUDIO_CLASS; + + if (codec->codec_tag) fourcc = codec->codec_tag; + else fourcc = ff_codec_get_tag(nut_tags, codec->codec_id); + + if (!fourcc) { + if (codec->codec_type == AVMEDIA_TYPE_VIDEO) fourcc = ff_codec_get_tag(ff_codec_bmp_tags, codec->codec_id); + if (codec->codec_type == AVMEDIA_TYPE_AUDIO) fourcc = ff_codec_get_tag(ff_codec_wav_tags, codec->codec_id); + } + + s[i].fourcc_len = 4; + s[i].fourcc = av_malloc(s[i].fourcc_len); + for (j = 0; j < s[i].fourcc_len; j++) s[i].fourcc[j] = (fourcc >> (j*8)) & 0xFF; + + ff_parse_specific_params(codec, &num, &ssize, &denom); + avpriv_set_pts_info(avf->streams[i], 60, denom, num); + + s[i].time_base.num = denom; + s[i].time_base.den = num; + + s[i].fixed_fps = 0; + s[i].decode_delay = codec->has_b_frames; + s[i].codec_specific_len = codec->extradata_size; + s[i].codec_specific = codec->extradata; + + if (codec->codec_type == AVMEDIA_TYPE_VIDEO) { + s[i].width = codec->width; + s[i].height = codec->height; + s[i].sample_width = 0; + s[i].sample_height = 0; + s[i].colorspace_type = 0; + } else { + s[i].samplerate_num = codec->sample_rate; + s[i].samplerate_denom = 1; + s[i].channel_count = codec->channels; + } + } + + s[avf->nb_streams].type = -1; + priv->nut = nut_muxer_init(&mopts, s, NULL); + + return 0; +} + +static int nut_write_packet(AVFormatContext * avf, AVPacket * pkt) { + NUTContext * priv = avf->priv_data; + nut_packet_tt p; + + p.len = pkt->size; + p.stream = pkt->stream_index; + p.pts = pkt->pts; + p.flags = pkt->flags & AV_PKT_FLAG_KEY ? NUT_FLAG_KEY : 0; + p.next_pts = 0; + + nut_write_frame_reorder(priv->nut, &p, pkt->data); + + return 0; +} + +static int nut_write_trailer(AVFormatContext * avf) { + AVIOContext * bc = avf->pb; + NUTContext * priv = avf->priv_data; + int i; + + nut_muxer_uninit_reorder(priv->nut); + avio_flush(bc); + + for(i = 0; priv->s[i].type != -1; i++ ) av_freep(&priv->s[i].fourcc); + av_freep(&priv->s); + + return 0; +} + +AVOutputFormat ff_libnut_muxer = { + .name = "libnut", + .long_name = "nut format", + .mime_type = "video/x-nut", + .extensions = "nut", + .priv_data_size = sizeof(NUTContext), + .audio_codec = AV_CODEC_ID_VORBIS, + .video_codec = AV_CODEC_ID_MPEG4, + .write_header = nut_write_header, + .write_packet = nut_write_packet, + .write_trailer = nut_write_trailer, + .flags = AVFMT_GLOBALHEADER, +}; +#endif /* CONFIG_LIBNUT_MUXER */ + +static int nut_probe(AVProbeData *p) { + if (!memcmp(p->buf, ID_STRING, ID_LENGTH)) return AVPROBE_SCORE_MAX; + + return 0; +} + +static size_t av_read(void * h, size_t len, uint8_t * buf) { + AVIOContext * bc = h; + return avio_read(bc, buf, len); +} + +static off_t av_seek(void * h, long long pos, int whence) { + AVIOContext * bc = h; + if (whence == SEEK_END) { + pos = avio_size(bc) + pos; + whence = SEEK_SET; + } + return avio_seek(bc, pos, whence); +} + +static int nut_read_header(AVFormatContext * avf) { + NUTContext * priv = avf->priv_data; + AVIOContext * bc = avf->pb; + nut_demuxer_opts_tt dopts = { + .input = { + .priv = bc, + .seek = av_seek, + .read = av_read, + .eof = NULL, + .file_pos = 0, + }, + .alloc = { av_malloc, av_realloc, av_free }, + .read_index = 1, + .cache_syncpoints = 1, + }; + nut_context_tt * nut = priv->nut = nut_demuxer_init(&dopts); + nut_stream_header_tt * s; + int ret, i; + + if(!nut) + return -1; + + if ((ret = nut_read_headers(nut, &s, NULL))) { + av_log(avf, AV_LOG_ERROR, " NUT error: %s\n", nut_error(ret)); + nut_demuxer_uninit(nut); + priv->nut = NULL; + return -1; + } + + priv->s = s; + + for (i = 0; s[i].type != -1 && i < 2; i++) { + AVStream * st = avformat_new_stream(avf, NULL); + int j; + + 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) { + st->codec->extradata = av_mallocz(st->codec->extradata_size); + if(!st->codec->extradata){ + 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/librtmp.c b/libavformat/librtmp.c index 7133bd655b..5b4c39dde5 100644 --- a/libavformat/librtmp.c +++ b/libavformat/librtmp.c @@ -2,20 +2,20 @@ * RTMP network protocol * Copyright (c) 2010 Howard Chu * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/lmlm4.c b/libavformat/lmlm4.c index b67b68e27b..c3eec02e8f 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 */ diff --git a/libavformat/loasdec.c b/libavformat/loasdec.c new file mode 100644 index 0000000000..ccb9464c33 --- /dev/null +++ b/libavformat/loasdec.c @@ -0,0 +1,88 @@ +/* + * LOAS AudioSyncStream demuxer + * Copyright (c) 2008 Michael Niedermayer <michaelni@gmx.at> + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/intreadwrite.h" +#include "libavutil/internal.h" +#include "avformat.h" +#include "internal.h" +#include "rawdec.h" + +static int loas_probe(AVProbeData *p) +{ + int max_frames = 0, first_frames = 0; + int fsize, frames; + 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) != 0x2B7) + break; + fsize = (header & 0x1FFF) + 3; + if(fsize < 7) + break; + fsize = FFMIN(fsize, end - buf2); + buf2 += fsize; + } + max_frames = FFMAX(max_frames, frames); + if(buf == buf0) + first_frames= frames; + } + if (first_frames>=3) return AVPROBE_SCORE_MAX/2+1; + else if(max_frames>100)return AVPROBE_SCORE_MAX/2; + else if(max_frames>=3) return AVPROBE_SCORE_MAX/4; + else if(max_frames>=1) return 1; + else return 0; +} + +static int loas_read_header(AVFormatContext *s) +{ + AVStream *st; + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_id = s->iformat->raw_codec_id; + st->need_parsing = AVSTREAM_PARSE_FULL_RAW; + + //LCM of all possible AAC sample rates + avpriv_set_pts_info(st, 64, 1, 28224000); + + return 0; +} + +AVInputFormat ff_loas_demuxer = { + .name = "loas", + .long_name = NULL_IF_CONFIG_SMALL("LOAS AudioSyncStream"), + .read_probe = loas_probe, + .read_header = loas_read_header, + .read_packet = ff_raw_read_partial_packet, + .flags= AVFMT_GENERIC_INDEX, + .raw_codec_id = AV_CODEC_ID_AAC_LATM, +}; diff --git a/libavformat/lvfdec.c b/libavformat/lvfdec.c new file mode 100644 index 0000000000..08a089135e --- /dev/null +++ b/libavformat/lvfdec.c @@ -0,0 +1,148 @@ +/* + * LVF demuxer + * Copyright (c) 2012 Paul B Mahol + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/intreadwrite.h" +#include "avformat.h" +#include "riff.h" + +static int lvf_probe(AVProbeData *p) +{ + if (AV_RL32(p->buf) == MKTAG('L', 'V', 'F', 'F')) + return AVPROBE_SCORE_MAX / 2; + return 0; +} + +static int lvf_read_header(AVFormatContext *s) +{ + AVStream *st; + int64_t next_offset; + unsigned size, nb_streams, id; + + avio_skip(s->pb, 16); + nb_streams = avio_rl32(s->pb); + if (!nb_streams) + return AVERROR_INVALIDDATA; + if (nb_streams > 2) { + av_log_ask_for_sample(s, "too many streams\n"); + return AVERROR_PATCHWELCOME; + } + + avio_skip(s->pb, 1012); + + while (!url_feof(s->pb)) { + id = avio_rl32(s->pb); + size = avio_rl32(s->pb); + next_offset = avio_tell(s->pb) + size; + + switch (id) { + case MKTAG('0', '0', 'f', 'm'): + st = avformat_new_stream(s, 0); + if (!st) + return AVERROR(ENOMEM); + + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + avio_skip(s->pb, 4); + st->codec->width = avio_rl32(s->pb); + st->codec->height = avio_rl32(s->pb); + avio_skip(s->pb, 4); + st->codec->codec_tag = avio_rl32(s->pb); + st->codec->codec_id = ff_codec_get_id(ff_codec_bmp_tags, + st->codec->codec_tag); + avpriv_set_pts_info(st, 32, 1, 1000); + break; + case MKTAG('0', '1', 'f', 'm'): + st = avformat_new_stream(s, 0); + if (!st) + return AVERROR(ENOMEM); + + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_tag = avio_rl16(s->pb); + st->codec->channels = avio_rl16(s->pb); + st->codec->sample_rate = avio_rl16(s->pb); + avio_skip(s->pb, 8); + st->codec->bits_per_coded_sample = avio_r8(s->pb); + st->codec->codec_id = ff_codec_get_id(ff_codec_wav_tags, + st->codec->codec_tag); + avpriv_set_pts_info(st, 32, 1, 1000); + break; + case 0: + avio_seek(s->pb, 2048 + 8, SEEK_SET); + return 0; + default: + av_log_ask_for_sample(s, "unknown id\n"); + return AVERROR_PATCHWELCOME; + } + + avio_seek(s->pb, next_offset, SEEK_SET); + } + + return AVERROR_EOF; +} + +static int lvf_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + unsigned size, flags, timestamp, id; + int64_t pos; + int ret, is_video = 0; + + pos = avio_tell(s->pb); + while (!url_feof(s->pb)) { + id = avio_rl32(s->pb); + size = avio_rl32(s->pb); + + if (size == 0xFFFFFFFFu) + return AVERROR_EOF; + + switch (id) { + case MKTAG('0', '0', 'd', 'c'): + is_video = 1; + case MKTAG('0', '1', 'w', 'b'): + if (size < 8) + return AVERROR_INVALIDDATA; + timestamp = avio_rl32(s->pb); + flags = avio_rl32(s->pb); + ret = av_get_packet(s->pb, pkt, size - 8); + if (flags & (1 << 12)) + pkt->flags |= AV_PKT_FLAG_KEY; + pkt->stream_index = is_video ? 0 : 1; + pkt->pts = timestamp; + pkt->pos = pos; + return ret; + default: + ret = avio_skip(s->pb, size); + } + + if (ret < 0) + return ret; + } + + return AVERROR_EOF; +} + +AVInputFormat ff_lvf_demuxer = { + .name = "lvf", + .long_name = NULL_IF_CONFIG_SMALL("LVF"), + .read_probe = lvf_probe, + .read_header = lvf_read_header, + .read_packet = lvf_read_packet, + .extensions = "lvf", + .flags = AVFMT_GENERIC_INDEX, +}; diff --git a/libavformat/lxfdec.c b/libavformat/lxfdec.c index e0eb935f8b..bf17d87a33 100644 --- a/libavformat/lxfdec.c +++ b/libavformat/lxfdec.c @@ -2,29 +2,30 @@ * 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 */ #include "libavutil/intreadwrite.h" +#include "libavcodec/bytestream.h" #include "avformat.h" #include "internal.h" #include "riff.h" -#define LXF_PACKET_HEADER_SIZE 60 +#define LXF_MAX_PACKET_HEADER_SIZE 256 #define LXF_HEADER_DATA_SIZE 120 #define LXF_IDENT "LEITCH\0" #define LXF_IDENT_LENGTH 8 @@ -49,6 +50,7 @@ typedef struct { int channels; ///< number of audio channels. zero means no audio uint8_t temp[LXF_MAX_AUDIO_PACKET]; ///< temp buffer for de-planarizing the audio data int frame_number; ///< current video frame + uint32_t video_format, packet_type, extended_size; } LXFDemuxContext; static int lxf_probe(AVProbeData *p) @@ -65,12 +67,12 @@ static int lxf_probe(AVProbeData *p) * @param[in] header the packet header to check * @return zero if the checksum is OK, non-zero otherwise */ -static int check_checksum(const uint8_t *header) +static int check_checksum(const uint8_t *header, int size) { int x; uint32_t sum = 0; - for (x = 0; x < LXF_PACKET_HEADER_SIZE; x += 4) + for (x = 0; x < size; x += 4) sum += AV_RL32(&header[x]); return sum; @@ -91,7 +93,7 @@ static int sync(AVFormatContext *s, uint8_t *header) return ret < 0 ? ret : AVERROR_EOF; while (memcmp(buf, LXF_IDENT, LXF_IDENT_LENGTH)) { - if (s->pb->eof_reached) + if (url_feof(s->pb)) return AVERROR_EOF; memmove(buf, &buf[1], LXF_IDENT_LENGTH-1); @@ -106,40 +108,60 @@ static int sync(AVFormatContext *s, uint8_t *header) /** * Read and checksum the next packet header * - * @param[out] header the read packet header - * @param[out] format context dependent format information * @return the size of the payload following the header or < 0 on failure */ -static int get_packet_header(AVFormatContext *s, uint8_t *header, uint32_t *format) +static int get_packet_header(AVFormatContext *s) { + LXFDemuxContext *lxf = s->priv_data; AVIOContext *pb = s->pb; int track_size, samples, ret; + uint32_t version, audio_format, header_size, channels, tmp; AVStream *st; + uint8_t header[LXF_MAX_PACKET_HEADER_SIZE]; + const uint8_t *p; //find and read the ident if ((ret = sync(s, header)) < 0) return ret; + ret = avio_read(pb, header + LXF_IDENT_LENGTH, 8); + if (ret != 8) + return ret < 0 ? ret : AVERROR_EOF; + + p = header + LXF_IDENT_LENGTH; + version = bytestream_get_le32(&p); + header_size = bytestream_get_le32(&p); + if (version > 1) + av_log_ask_for_sample(s, "Unknown format version %i\n", version); + if (header_size < (version ? 72 : 60) || + header_size > LXF_MAX_PACKET_HEADER_SIZE || + (header_size & 3)) { + av_log(s, AV_LOG_ERROR, "Invalid header size 0x%x\n", header_size); + return AVERROR_INVALIDDATA; + } + //read the rest of the packet header - if ((ret = avio_read(pb, header + LXF_IDENT_LENGTH, - LXF_PACKET_HEADER_SIZE - LXF_IDENT_LENGTH)) != - LXF_PACKET_HEADER_SIZE - LXF_IDENT_LENGTH) { + 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)) + if (check_checksum(header, header_size)) av_log(s, AV_LOG_ERROR, "checksum error\n"); - *format = AV_RL32(&header[32]); - ret = AV_RL32(&header[36]); + lxf->packet_type = bytestream_get_le32(&p); + p += version ? 20 : 12; - //type - switch (AV_RL32(&header[16])) { + lxf->extended_size = 0; + switch (lxf->packet_type) { case 0: //video + lxf->video_format = bytestream_get_le32(&p); + ret = bytestream_get_le32(&p); //skip VBI data and metadata - avio_skip(pb, (int64_t)(uint32_t)AV_RL32(&header[44]) + - (int64_t)(uint32_t)AV_RL32(&header[52])); + avio_skip(pb, (int64_t)(uint32_t)AV_RL32(p + 4) + + (int64_t)(uint32_t)AV_RL32(p + 12)); break; case 1: //audio @@ -148,12 +170,16 @@ static int get_packet_header(AVFormatContext *s, uint8_t *header, uint32_t *form break; } + if (version == 0) p += 8; + audio_format = bytestream_get_le32(&p); + channels = bytestream_get_le32(&p); + track_size = bytestream_get_le32(&p); + //set codec based on specified audio bitdepth //we only support tightly packed 16-, 20-, 24- and 32-bit PCM at the moment - *format = AV_RL32(&header[40]); - st->codec->bits_per_coded_sample = (*format >> 6) & 0x3F; + st->codec->bits_per_coded_sample = (audio_format >> 6) & 0x3F; - if (st->codec->bits_per_coded_sample != (*format & 0x3F)) { + if (st->codec->bits_per_coded_sample != (audio_format & 0x3F)) { av_log(s, AV_LOG_WARNING, "only tightly packed PCM currently supported\n"); return AVERROR_PATCHWELCOME; } @@ -169,7 +195,6 @@ static int get_packet_header(AVFormatContext *s, uint8_t *header, uint32_t *form return AVERROR_PATCHWELCOME; } - track_size = AV_RL32(&header[48]); samples = track_size * 8 / st->codec->bits_per_coded_sample; //use audio packet size to determine video standard @@ -186,10 +211,14 @@ static int get_packet_header(AVFormatContext *s, uint8_t *header, uint32_t *form } //TODO: warning if track mask != (1 << channels) - 1? - ret = av_popcount(AV_RL32(&header[44])) * track_size; + ret = av_popcount(channels) * track_size; break; default: + tmp = bytestream_get_le32(&p); + ret = bytestream_get_le32(&p); + if (tmp == 1) + lxf->extended_size = bytestream_get_le32(&p); break; } @@ -200,13 +229,13 @@ static int lxf_read_header(AVFormatContext *s) { LXFDemuxContext *lxf = s->priv_data; AVIOContext *pb = s->pb; - uint8_t header[LXF_PACKET_HEADER_SIZE], header_data[LXF_HEADER_DATA_SIZE]; + uint8_t header_data[LXF_HEADER_DATA_SIZE]; int ret; AVStream *st; - uint32_t format, video_params, disk_params; + uint32_t video_params, disk_params; uint16_t record_date, expiration_date; - if ((ret = get_packet_header(s, header, &format)) < 0) + if ((ret = get_packet_header(s)) < 0) return ret; if (ret != LXF_HEADER_DATA_SIZE) { @@ -243,7 +272,7 @@ static int lxf_read_header(AVFormatContext *s) if ((video_params >> 22) & 1) av_log(s, AV_LOG_WARNING, "VBI data not yet supported\n"); - if ((lxf->channels = (disk_params >> 2) & 0xF)) { + if ((lxf->channels = 1 << (disk_params >> 4 & 3) + 1)) { if (!(st = avformat_new_stream(s, NULL))) return AVERROR(ENOMEM); @@ -254,10 +283,7 @@ static int lxf_read_header(AVFormatContext *s) avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate); } - if (format == 1) { - //skip extended field data - avio_skip(s->pb, (uint32_t)AV_RL32(&header[40])); - } + avio_skip(s->pb, lxf->extended_size); return 0; } @@ -283,15 +309,15 @@ static int lxf_read_packet(AVFormatContext *s, AVPacket *pkt) { LXFDemuxContext *lxf = s->priv_data; AVIOContext *pb = s->pb; - uint8_t header[LXF_PACKET_HEADER_SIZE], *buf; + uint8_t *buf; AVStream *ast = NULL; - uint32_t stream, format; + uint32_t stream; int ret, ret2; - if ((ret = get_packet_header(s, header, &format)) < 0) + if ((ret = get_packet_header(s)) < 0) return ret; - stream = AV_RL32(&header[16]); + stream = lxf->packet_type; if (stream > 1) { av_log(s, AV_LOG_WARNING, "got packet with illegal stream index %u\n", stream); @@ -328,7 +354,7 @@ static int lxf_read_packet(AVFormatContext *s, AVPacket *pkt) deplanarize(lxf, ast, pkt->data, ret); } else { //picture type (0 = closed I, 1 = open I, 2 = P, 3 = B) - if (((format >> 22) & 0x3) < 2) + if (((lxf->video_format >> 22) & 0x3) < 2) pkt->flags |= AV_PKT_FLAG_KEY; pkt->dts = lxf->frame_number++; diff --git a/libavformat/m4vdec.c b/libavformat/m4vdec.c index 4bee058a50..e72fb42f1d 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_MAX/2; + return VOP+VO > 3 ? AVPROBE_SCORE_MAX/2 : AVPROBE_SCORE_MAX/4; return 0; } diff --git a/libavformat/matroska.c b/libavformat/matroska.c index edb7ab7910..64d0a459b4 100644 --- a/libavformat/matroska.c +++ b/libavformat/matroska.c @@ -2,20 +2,20 @@ * Matroska common data * Copyright (c) 2003-2004 The ffmpeg Project * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -32,6 +32,7 @@ const CodecTags ff_mkv_codec_tags[]={ {"A_MPEG/L2" , AV_CODEC_ID_MP2}, {"A_MPEG/L1" , AV_CODEC_ID_MP2}, {"A_MPEG/L3" , AV_CODEC_ID_MP3}, + {"A_OPUS", AV_CODEC_ID_OPUS}, {"A_PCM/FLOAT/IEEE" , AV_CODEC_ID_PCM_F32LE}, {"A_PCM/FLOAT/IEEE" , AV_CODEC_ID_PCM_F64LE}, {"A_PCM/INT/BIG" , AV_CODEC_ID_PCM_S16BE}, @@ -52,6 +53,7 @@ const CodecTags ff_mkv_codec_tags[]={ {"A_VORBIS" , AV_CODEC_ID_VORBIS}, {"A_WAVPACK4" , AV_CODEC_ID_WAVPACK}, + {"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}, @@ -92,6 +94,7 @@ const CodecMime ff_mkv_mime_tags[] = { {"image/tiff" , AV_CODEC_ID_TIFF}, {"application/x-truetype-font", AV_CODEC_ID_TTF}, {"application/x-font" , AV_CODEC_ID_TTF}, + {"application/vnd.ms-opentype", AV_CODEC_ID_OTF}, {"" , AV_CODEC_ID_NONE} }; @@ -101,3 +104,27 @@ const AVMetadataConv ff_mkv_metadata_conv[] = { { "PART_NUMBER" , "track" }, { 0 } }; + +const char * const ff_matroska_video_stereo_mode[MATROSKA_VIDEO_STEREO_MODE_COUNT] = { + "mono", + "left_right", + "bottom_top", + "top_bottom", + "checkerboard_rl", + "checkerboard_lr" + "row_interleaved_rl", + "row_interleaved_lr", + "col_interleaved_rl", + "col_interleaved_lr", + "anaglyph_cyan_red", + "right_left", + "anaglyph_green_magenta", + "block_lr", + "block_rl", +}; + +const char * const ff_matroska_video_stereo_plane[MATROSKA_VIDEO_STEREO_PLANE_COUNT] = { + "left", + "right", + "background", +}; diff --git a/libavformat/matroska.h b/libavformat/matroska.h index b1f7ae4ed5..8411633f0a 100644 --- a/libavformat/matroska.h +++ b/libavformat/matroska.h @@ -2,20 +2,20 @@ * Matroska constants * Copyright (c) 2003-2004 The ffmpeg Project * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -78,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 @@ -249,8 +254,13 @@ typedef struct CodecTags{ /* max. depth in the EBML tree structure */ #define EBML_MAX_DEPTH 16 +#define MATROSKA_VIDEO_STEREO_MODE_COUNT 15 +#define MATROSKA_VIDEO_STEREO_PLANE_COUNT 3 + extern const CodecTags ff_mkv_codec_tags[]; extern const CodecMime ff_mkv_mime_tags[]; extern const AVMetadataConv ff_mkv_metadata_conv[]; +extern const char * const ff_matroska_video_stereo_mode[MATROSKA_VIDEO_STEREO_MODE_COUNT]; +extern const char * const ff_matroska_video_stereo_plane[MATROSKA_VIDEO_STEREO_PLANE_COUNT]; #endif /* AVFORMAT_MATROSKA_H */ diff --git a/libavformat/matroskadec.c b/libavformat/matroskadec.c index 26f8707e83..c4a590b27f 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 */ @@ -113,7 +113,8 @@ typedef struct { uint64_t display_height; uint64_t pixel_width; uint64_t pixel_height; - uint64_t fourcc; + EbmlBin color_space; + uint64_t stereo_mode; } MatroskaTrackVideo; typedef struct { @@ -134,6 +135,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; @@ -147,6 +157,7 @@ typedef struct { uint64_t flag_forced; MatroskaTrackVideo video; MatroskaTrackAudio audio; + MatroskaTrackOperation operation; EbmlList encodings; AVStream *stream; @@ -230,6 +241,7 @@ typedef struct { uint64_t time_scale; double duration; char *title; + EbmlBin date_utc; EbmlList tracks; EbmlList attachments; EbmlList chapters; @@ -291,7 +303,7 @@ static EbmlSyntax matroska_info[] = { { MATROSKA_ID_TITLE, EBML_UTF8, 0, offsetof(MatroskaDemuxContext,title) }, { MATROSKA_ID_WRITINGAPP, EBML_NONE }, { MATROSKA_ID_MUXINGAPP, EBML_NONE }, - { MATROSKA_ID_DATEUTC, EBML_NONE }, + { MATROSKA_ID_DATEUTC, EBML_BIN, 0, offsetof(MatroskaDemuxContext,date_utc) }, { MATROSKA_ID_SEGMENTUID, EBML_NONE }, { 0 } }; @@ -302,14 +314,14 @@ static EbmlSyntax matroska_track_video[] = { { MATROSKA_ID_VIDEODISPLAYHEIGHT, EBML_UINT, 0, offsetof(MatroskaTrackVideo,display_height) }, { MATROSKA_ID_VIDEOPIXELWIDTH, EBML_UINT, 0, offsetof(MatroskaTrackVideo,pixel_width) }, { MATROSKA_ID_VIDEOPIXELHEIGHT, EBML_UINT, 0, offsetof(MatroskaTrackVideo,pixel_height) }, - { MATROSKA_ID_VIDEOCOLORSPACE, EBML_UINT, 0, offsetof(MatroskaTrackVideo,fourcc) }, + { MATROSKA_ID_VIDEOCOLORSPACE, EBML_BIN, 0, offsetof(MatroskaTrackVideo,color_space) }, + { MATROSKA_ID_VIDEOSTEREOMODE, EBML_UINT, 0, offsetof(MatroskaTrackVideo,stereo_mode) }, { MATROSKA_ID_VIDEOPIXELCROPB, EBML_NONE }, { MATROSKA_ID_VIDEOPIXELCROPT, EBML_NONE }, { MATROSKA_ID_VIDEOPIXELCROPL, EBML_NONE }, { MATROSKA_ID_VIDEOPIXELCROPR, EBML_NONE }, { MATROSKA_ID_VIDEODISPLAYUNIT, EBML_NONE }, { MATROSKA_ID_VIDEOFLAGINTERLACED,EBML_NONE }, - { MATROSKA_ID_VIDEOSTEREOMODE, EBML_NONE }, { MATROSKA_ID_VIDEOASPECTRATIO, EBML_NONE }, { 0 } }; @@ -341,6 +353,22 @@ static EbmlSyntax matroska_track_encodings[] = { { 0 } }; +static EbmlSyntax matroska_track_plane[] = { + { MATROSKA_ID_TRACKPLANEUID, EBML_UINT, 0, offsetof(MatroskaTrackPlane,uid) }, + { MATROSKA_ID_TRACKPLANETYPE, EBML_UINT, 0, offsetof(MatroskaTrackPlane,type) }, + { 0 } +}; + +static EbmlSyntax matroska_track_combine_planes[] = { + { MATROSKA_ID_TRACKPLANE, EBML_NEST, sizeof(MatroskaTrackPlane), offsetof(MatroskaTrackOperation,combine_planes), {.n=matroska_track_plane} }, + { 0 } +}; + +static EbmlSyntax matroska_track_operation[] = { + { MATROSKA_ID_TRACKCOMBINEPLANES, EBML_NEST, 0, 0, {.n=matroska_track_combine_planes} }, + { 0 } +}; + static EbmlSyntax matroska_track[] = { { MATROSKA_ID_TRACKNUMBER, EBML_UINT, 0, offsetof(MatroskaTrack,num) }, { MATROSKA_ID_TRACKNAME, EBML_UTF8, 0, offsetof(MatroskaTrack,name) }, @@ -355,6 +383,7 @@ static EbmlSyntax matroska_track[] = { { MATROSKA_ID_TRACKFLAGFORCED, EBML_UINT, 0, offsetof(MatroskaTrack,flag_forced), {.u=0} }, { MATROSKA_ID_TRACKVIDEO, EBML_NEST, 0, offsetof(MatroskaTrack,video), {.n=matroska_track_video} }, { MATROSKA_ID_TRACKAUDIO, EBML_NEST, 0, offsetof(MatroskaTrack,audio), {.n=matroska_track_audio} }, + { MATROSKA_ID_TRACKOPERATION, EBML_NEST, 0, offsetof(MatroskaTrack,operation), {.n=matroska_track_operation} }, { MATROSKA_ID_TRACKCONTENTENCODINGS,EBML_NEST, 0, 0, {.n=matroska_track_encodings} }, { MATROSKA_ID_TRACKFLAGENABLED, EBML_NONE }, { MATROSKA_ID_TRACKFLAGLACING, EBML_NONE }, @@ -498,7 +527,7 @@ static EbmlSyntax matroska_segments[] = { static EbmlSyntax matroska_blockgroup[] = { { MATROSKA_ID_BLOCK, EBML_BIN, 0, offsetof(MatroskaBlock,bin) }, { MATROSKA_ID_SIMPLEBLOCK, EBML_BIN, 0, offsetof(MatroskaBlock,bin) }, - { MATROSKA_ID_BLOCKDURATION, EBML_UINT, 0, offsetof(MatroskaBlock,duration), {.u=AV_NOPTS_VALUE} }, + { MATROSKA_ID_BLOCKDURATION, EBML_UINT, 0, offsetof(MatroskaBlock,duration) }, { MATROSKA_ID_BLOCKREFERENCE, EBML_UINT, 0, offsetof(MatroskaBlock,reference) }, { 1, EBML_UINT, 0, offsetof(MatroskaBlock,non_simple), {.u=1} }, { 0 } @@ -556,6 +585,36 @@ static EbmlSyntax matroska_clusters_incremental[] = { static const char *const matroska_doctypes[] = { "matroska", "webm" }; +static int matroska_resync(MatroskaDemuxContext *matroska, int64_t last_pos) +{ + AVIOContext *pb = matroska->ctx->pb; + uint32_t id; + matroska->current_id = 0; + matroska->num_levels = 0; + + // seek to next position to resync from + if (avio_seek(pb, last_pos + 1, SEEK_SET) < 0 || avio_tell(pb) <= last_pos) + goto eof; + + id = avio_rb32(pb); + + // try to find a toplevel element + while (!url_feof(pb)) { + if (id == MATROSKA_ID_INFO || id == MATROSKA_ID_TRACKS || + id == MATROSKA_ID_CUES || id == MATROSKA_ID_TAGS || + id == MATROSKA_ID_SEEKHEAD || id == MATROSKA_ID_ATTACHMENTS || + id == MATROSKA_ID_CLUSTER || id == MATROSKA_ID_CHAPTERS) + { + matroska->current_id = id; + return 0; + } + id = (id << 8) | avio_r8(pb); + } +eof: + matroska->done = 1; + return AVERROR_EOF; +} + /* * Return: Whether we reached the end of a level in the hierarchy or not. */ @@ -593,7 +652,7 @@ static int ebml_read_num(MatroskaDemuxContext *matroska, AVIOContext *pb, * use it safely here to catch EOS. */ if (!(total = avio_r8(pb))) { /* we might encounter EOS here */ - if (!pb->eof_reached) { + if (!url_feof(pb)) { int64_t pos = avio_tell(pb); av_log(matroska->ctx, AV_LOG_ERROR, "Read error at pos. %"PRIu64" (0x%"PRIx64")\n", @@ -887,7 +946,10 @@ static int ebml_parse_elem(MatroskaDemuxContext *matroska, return ebml_parse_nest(matroska, syntax->def.n, data); case EBML_PASS: return ebml_parse_id(matroska, syntax->def.n, id, data); case EBML_STOP: return 1; - default: return avio_skip(pb,length)<0 ? AVERROR(EIO) : 0; + default: + if(ffio_limit(pb, length) != length) + return AVERROR(EIO); + return avio_skip(pb,length)<0 ? AVERROR(EIO) : 0; } if (res == AVERROR_INVALIDDATA) av_log(matroska->ctx, AV_LOG_ERROR, "Invalid element\n"); @@ -991,7 +1053,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) { @@ -999,6 +1061,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(0, AV_LOG_ERROR, "Compression size but no data in headerstrip\n"); + return -1; + } + if (!header_size) return 0; @@ -1047,7 +1114,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); @@ -1078,7 +1148,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); @@ -1110,7 +1183,8 @@ static void matroska_fix_ass_packet(MatroskaDemuxContext *matroska, char *line, *layer, *ptr = pkt->data, *end = ptr+pkt->size; for (; *ptr!=',' && ptr<end-1; ptr++); if (*ptr == ',') - layer = ++ptr; + ptr++; + layer = ptr; for (; *ptr!=',' && ptr<end-1; ptr++); if (*ptr == ',') { int64_t end_pts = pkt->pts + display_duration; @@ -1137,12 +1211,10 @@ static void matroska_fix_ass_packet(MatroskaDemuxContext *matroska, static int matroska_merge_packets(AVPacket *out, AVPacket *in) { - void *newdata = av_realloc(out->data, out->size+in->size); - if (!newdata) - return AVERROR(ENOMEM); - out->data = newdata; - memcpy(out->data+out->size, in->data, in->size); - out->size += in->size; + int ret = av_grow_packet(out, in->size); + if (ret < 0) + return ret; + memcpy(out->data + out->size - in->size, in->data, in->size); av_destruct_packet(in); av_free(in); return 0; @@ -1156,7 +1228,7 @@ static void matroska_convert_tag(AVFormatContext *s, EbmlList *list, int i; for (i=0; i < list->nb_elem; i++) { - const char *lang = strcmp(tags[i].lang, "und") ? tags[i].lang : NULL; + const char *lang= (tags[i].lang && strcmp(tags[i].lang, "und")) ? tags[i].lang : NULL; if (!tags[i].name) { av_log(s, AV_LOG_WARNING, "Skipping invalid tag with no TagName.\n"); @@ -1287,26 +1359,20 @@ static void matroska_execute_seekhead(MatroskaDemuxContext *matroska) continue; } - if (matroska_parse_seekhead_entry(matroska, i) < 0) + if (matroska_parse_seekhead_entry(matroska, i) < 0) { + // mark index as broken + matroska->cues_parsing_deferred = -1; break; + } } } -static void matroska_parse_cues(MatroskaDemuxContext *matroska) { - EbmlList *seekhead_list = &matroska->seekhead; - MatroskaSeekhead *seekhead = seekhead_list->elem; +static void matroska_add_index_entries(MatroskaDemuxContext *matroska) { EbmlList *index_list; MatroskaIndex *index; int index_scale = 1; int i, j; - for (i = 0; i < seekhead_list->nb_elem; i++) - if (seekhead[i].id == MATROSKA_ID_CUES) - break; - assert(i <= seekhead_list->nb_elem); - - matroska_parse_seekhead_entry(matroska, i); - index_list = &matroska->index; index = index_list->elem; if (index_list->nb_elem @@ -1328,6 +1394,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; + assert(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" }; @@ -1349,6 +1430,17 @@ static int matroska_aac_sri(int samplerate) return sri; } +static void matroska_metadata_creation_time(AVDictionary **metadata, int64_t date_utc) +{ + char buffer[32]; + /* Convert to seconds and adjust by number of seconds between 2001-01-01 and Epoch */ + time_t creation_time = date_utc / 1000000000 + 978307200; + struct tm *ptm = gmtime(&creation_time); + if (!ptm) return; + strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", ptm); + av_dict_set(metadata, "creation_time", buffer, 0); +} + static int matroska_read_header(AVFormatContext *s) { MatroskaDemuxContext *matroska = s->priv_data; @@ -1358,22 +1450,28 @@ static int matroska_read_header(AVFormatContext *s) MatroskaChapter *chapters; MatroskaTrack *tracks; uint64_t max_start = 0; + int64_t pos; Ebml ebml = { 0 }; AVStream *st; - int i, j, res; + int i, j, k, res; matroska->ctx = s; /* First read the EBML header. */ if (ebml_parse(matroska, ebml_syntax, &ebml) || ebml.version > EBML_VERSION || ebml.max_size > sizeof(uint64_t) - || ebml.id_length > sizeof(uint32_t) || ebml.doctype_version > 2) { + || ebml.id_length > sizeof(uint32_t) || ebml.doctype_version > 3 || !ebml.doctype) { av_log(matroska->ctx, AV_LOG_ERROR, "EBML header using unsupported features\n" "(EBML version %"PRIu64", doctype %s, doc version %"PRIu64")\n", ebml.version, ebml.doctype, ebml.doctype_version); ebml_free(ebml_syntax, &ebml); return AVERROR_PATCHWELCOME; + } else if (ebml.doctype_version == 3) { + av_log(matroska->ctx, AV_LOG_WARNING, + "EBML header using unsupported features\n" + "(EBML version %"PRIu64", doctype %s, doc version %"PRIu64")\n", + ebml.version, ebml.doctype, ebml.doctype_version); } for (i = 0; i < FF_ARRAY_ELEMS(matroska_doctypes); i++) if (!strcmp(ebml.doctype, matroska_doctypes[i])) @@ -1388,8 +1486,16 @@ static int matroska_read_header(AVFormatContext *s) ebml_free(ebml_syntax, &ebml); /* The next thing is a segment. */ - if ((res = ebml_parse(matroska, matroska_segments, matroska)) < 0) - return res; + pos = avio_tell(matroska->ctx->pb); + res = ebml_parse(matroska, matroska_segments, matroska); + // try resyncing until we find a EBML_STOP type element. + while (res != 1) { + res = matroska_resync(matroska, pos); + if (res < 0) + return res; + pos = avio_tell(matroska->ctx->pb); + res = ebml_parse(matroska, matroska_segment, matroska); + } matroska_execute_seekhead(matroska); if (!matroska->time_scale) @@ -1399,15 +1505,19 @@ static int matroska_read_header(AVFormatContext *s) * 1000 / AV_TIME_BASE; av_dict_set(&s->metadata, "title", matroska->title, 0); + if (matroska->date_utc.size == 8) + matroska_metadata_creation_time(&s->metadata, AV_RB64(matroska->date_utc.data)); + tracks = matroska->tracks.elem; for (i=0; i < matroska->tracks.nb_elem; i++) { MatroskaTrack *track = &tracks[i]; enum AVCodecID codec_id = AV_CODEC_ID_NONE; - EbmlList *encodings_list = &tracks->encodings; + EbmlList *encodings_list = &track->encodings; MatroskaTrackEncoding *encodings = encodings_list->elem; uint8_t *extradata = NULL; int extradata_size = 0; int extradata_offset = 0; + uint32_t fourcc = 0; AVIOContext b; /* Apply some sanity checks. */ @@ -1429,6 +1539,8 @@ static int matroska_read_header(AVFormatContext *s) track->video.display_width = track->video.pixel_width; if (!track->video.display_height) 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; @@ -1485,8 +1597,8 @@ static int matroska_read_header(AVFormatContext *s) && track->codec_priv.size >= 40 && track->codec_priv.data != NULL) { track->ms_compat = 1; - track->video.fourcc = AV_RL32(track->codec_priv.data + 16); - codec_id = ff_codec_get_id(ff_codec_bmp_tags, track->video.fourcc); + fourcc = AV_RL32(track->codec_priv.data + 16); + codec_id = ff_codec_get_id(ff_codec_bmp_tags, fourcc); extradata_offset = 40; } else if (!strcmp(track->codec_id, "A_MS/ACM") && track->codec_priv.size >= 14 @@ -1502,8 +1614,20 @@ static int matroska_read_header(AVFormatContext *s) } else if (!strcmp(track->codec_id, "V_QUICKTIME") && (track->codec_priv.size >= 86) && (track->codec_priv.data != NULL)) { - track->video.fourcc = AV_RL32(track->codec_priv.data); - codec_id=ff_codec_get_id(ff_codec_movvideo_tags, track->video.fourcc); + fourcc = AV_RL32(track->codec_priv.data); + codec_id = ff_codec_get_id(ff_codec_movvideo_tags, fourcc); + } else if (codec_id == AV_CODEC_ID_ALAC && track->codec_priv.size && track->codec_priv.size < INT_MAX-12) { + /* 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. */ + extradata_size = 12 + track->codec_priv.size; + extradata = av_mallocz(extradata_size + FF_INPUT_BUFFER_PADDING_SIZE); + if (extradata == NULL) + return AVERROR(ENOMEM); + AV_WB32(extradata, extradata_size); + memcpy(&extradata[4], "alac", 4); + AV_WB32(&extradata[8], 0); + memcpy(&extradata[12], track->codec_priv.data, track->codec_priv.size); } else if (codec_id == AV_CODEC_ID_PCM_S16BE) { switch (track->audio.bitdepth) { case 8: codec_id = AV_CODEC_ID_PCM_U8; break; @@ -1534,22 +1658,9 @@ static int matroska_read_header(AVFormatContext *s) extradata_size = 5; } else extradata_size = 2; - } else if (codec_id == AV_CODEC_ID_ALAC && track->codec_priv.size) { - /* 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. */ - extradata_size = 12 + track->codec_priv.size; - extradata = av_mallocz(extradata_size + FF_INPUT_BUFFER_PADDING_SIZE); - if (extradata == NULL) - return AVERROR(ENOMEM); - AV_WB32(extradata, extradata_size); - memcpy(&extradata[4], "alac", 4); - AV_WB32(&extradata[8], 0); - memcpy(&extradata[12], track->codec_priv.data, - track->codec_priv.size); } else if (codec_id == AV_CODEC_ID_TTA) { extradata_size = 30; - extradata = av_mallocz(extradata_size); + extradata = av_mallocz(extradata_size + FF_INPUT_BUFFER_PADDING_SIZE); if (extradata == NULL) return AVERROR(ENOMEM); ffio_init_context(&b, extradata, extradata_size, 1, @@ -1566,9 +1677,11 @@ static int matroska_read_header(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); avio_skip(&b, 22); @@ -1630,8 +1743,10 @@ static int matroska_read_header(AVFormatContext *s) } if (track->type == MATROSKA_TRACK_TYPE_VIDEO) { + MatroskaTrackPlane *planes = track->operation.combine_planes.elem; + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; - st->codec->codec_tag = track->video.fourcc; + st->codec->codec_tag = fourcc; st->codec->width = track->video.pixel_width; st->codec->height = track->video.pixel_height; av_reduce(&st->sample_aspect_ratio.num, @@ -1639,7 +1754,6 @@ static int matroska_read_header(AVFormatContext *s) st->codec->height * track->video.display_width, st->codec-> width * track->video.display_height, 255); - if (st->codec->codec_id != AV_CODEC_ID_H264) st->need_parsing = AVSTREAM_PARSE_HEADERS; if (track->default_duration) { av_reduce(&st->avg_frame_rate.num, &st->avg_frame_rate.den, @@ -1648,6 +1762,25 @@ static int matroska_read_header(AVFormatContext *s) st->r_frame_rate = st->avg_frame_rate; #endif } + + /* export stereo mode flag as metadata tag */ + if (track->video.stereo_mode && track->video.stereo_mode < MATROSKA_VIDEO_STEREO_MODE_COUNT) + av_dict_set(&st->metadata, "stereo_mode", ff_matroska_video_stereo_mode[track->video.stereo_mode], 0); + + /* if we have virtual track, mark the real tracks */ + for (j=0; j < track->operation.combine_planes.nb_elem; j++) { + char buf[32]; + if (planes[j].type >= MATROSKA_VIDEO_STEREO_PLANE_COUNT) + continue; + snprintf(buf, sizeof(buf), "%s_%d", + ff_matroska_video_stereo_plane[planes[j].type], i); + for (k=0; k < matroska->tracks.nb_elem; k++) + if (planes[j].uid == tracks[k].uid) { + av_dict_set(&s->streams[k]->metadata, + "stereo_mode", buf, 0); + break; + } + } } else if (track->type == MATROSKA_TRACK_TYPE_AUDIO) { st->codec->codec_type = AVMEDIA_TYPE_AUDIO; st->codec->sample_rate = track->audio.out_samplerate; @@ -1674,7 +1807,7 @@ static int matroska_read_header(AVFormatContext *s) av_dict_set(&st->metadata, "mimetype", attachements[j].mime, 0); st->codec->codec_id = AV_CODEC_ID_NONE; st->codec->codec_type = AVMEDIA_TYPE_ATTACHMENT; - st->codec->extradata = av_malloc(attachements[j].bin.size); + st->codec->extradata = av_malloc(attachements[j].bin.size + FF_INPUT_BUFFER_PADDING_SIZE); if(st->codec->extradata == NULL) break; st->codec->extradata_size = attachements[j].bin.size; @@ -1704,6 +1837,8 @@ static int matroska_read_header(AVFormatContext *s) max_start = chapters[i].start; } + matroska_add_index_entries(matroska); + matroska_convert_tags(s); return 0; @@ -1772,7 +1907,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; @@ -1864,7 +1999,7 @@ static int matroska_parse_rm_audio(MatroskaDemuxContext *matroska, MatroskaTrack *track, AVStream *st, uint8_t *data, int size, - uint64_t timecode, uint64_t duration, + uint64_t timecode, int64_t pos) { int a = st->codec->block_align; @@ -1913,8 +2048,11 @@ static int matroska_parse_rm_audio(MatroskaDemuxContext *matroska, } while (track->audio.pkt_cnt) { - AVPacket *pkt = av_mallocz(sizeof(AVPacket)); - av_new_packet(pkt, a); + AVPacket *pkt = NULL; + if (!(pkt = av_mallocz(sizeof(AVPacket))) || av_new_packet(pkt, a) < 0){ + av_free(pkt); + return AVERROR(ENOMEM); + } memcpy(pkt->data, track->audio.buf + a * (h*w / a - track->audio.pkt_cnt--), a); pkt->pts = track->audio.buf_timecode; @@ -1930,7 +2068,7 @@ static int matroska_parse_frame(MatroskaDemuxContext *matroska, MatroskaTrack *track, AVStream *st, uint8_t *data, int pkt_size, - uint64_t timecode, uint64_t duration, + uint64_t timecode, uint64_t lace_duration, int64_t pos, int is_keyframe) { MatroskaTrackEncoding *encodings = track->encodings.elem; @@ -1973,13 +2111,32 @@ static int matroska_parse_frame(MatroskaDemuxContext *matroska, 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 (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 && @@ -2007,7 +2164,7 @@ 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; if ((n = matroska_ebmlnum_uint(matroska, data, size, &num)) < 0) { av_log(matroska->ctx, AV_LOG_ERROR, "EBML block data error\n"); @@ -2026,6 +2183,7 @@ 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); data += 2; @@ -2045,9 +2203,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 (!st->skip_to_keyframe) { + av_log(matroska->ctx, AV_LOG_ERROR, "File is broken, keyframes not correctly marked!\n"); + matroska->skip_to_keyframe = 0; + } + if (is_keyframe) + matroska->skip_to_keyframe = 0; } res = matroska_parse_laces(matroska, &data, size, (flags & 0x06) >> 1, @@ -2056,22 +2219,21 @@ 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"); - } - } else { - duration = track->default_duration / matroska->time_scale; - block_duration = duration * laces; - } + if (!block_duration) + block_duration = track->default_duration * laces / matroska->time_scale; - if (timecode != AV_NOPTS_VALUE) + 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 || @@ -2079,20 +2241,20 @@ 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, size, - timecode, duration, pos); + timecode, pos); if (res) goto end; } else { res = matroska_parse_frame(matroska, track, st, data, lace_size[n], - timecode, duration, + timecode, lace_duration, pos, !n? is_keyframe : 0); 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]; } @@ -2143,7 +2305,7 @@ static int matroska_parse_cluster_incremental(MatroskaDemuxContext *matroska) 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; + blocks[i].duration = 0; res = matroska_parse_block(matroska, blocks[i].bin.data, blocks[i].bin.size, blocks[i].bin.pos, @@ -2173,11 +2335,9 @@ 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, @@ -2185,27 +2345,22 @@ static int matroska_parse_cluster(MatroskaDemuxContext *matroska) pos); } ebml_free(matroska_cluster, &cluster); - if (res < 0) matroska->done = 1; return res; } 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; - ret = matroska_parse_cluster(matroska); - } - - if (ret == AVERROR_INVALIDDATA && pkt->data) { - pkt->flags |= AV_PKT_FLAG_CORRUPT; - return 0; + if (matroska_parse_cluster(matroska) < 0) + matroska_resync(matroska, pos); } - return ret; + return 0; } static int matroska_read_seek(AVFormatContext *s, int stream_index, @@ -2217,13 +2372,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) { @@ -2238,8 +2393,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++) { @@ -2248,7 +2403,7 @@ static int matroska_read_seek(AVFormatContext *s, int stream_index, tracks[i].audio.buf_timecode = AV_NOPTS_VALUE; tracks[i].end_timecode = 0; if (tracks[i].type == MATROSKA_TRACK_TYPE_SUBTITLE - && !tracks[i].stream->discard != AVDISCARD_ALL) { + && tracks[i].stream->discard != AVDISCARD_ALL) { index_sub = av_index_search_timestamp(tracks[i].stream, st->index_entries[index].timestamp, AVSEEK_FLAG_BACKWARD); if (index_sub >= 0 && st->index_entries[index_sub].pos < st->index_entries[index_min].pos @@ -2259,11 +2414,23 @@ static int matroska_read_seek(AVFormatContext *s, int stream_index, avio_seek(s->pb, st->index_entries[index_min].pos, SEEK_SET); matroska->current_id = 0; + st->skip_to_keyframe = matroska->skip_to_keyframe = !(flags & AVSEEK_FLAG_ANY); matroska->skip_to_timecode = st->index_entries[index].timestamp; matroska->done = 0; + matroska->num_levels = 0; ff_update_cur_dts(s, st, st->index_entries[index].timestamp); return 0; +err: + // slightly hackish but allows proper fallback to + // the generic seeking code. + matroska_clear_queue(matroska); + matroska->current_id = 0; + st->skip_to_keyframe = + matroska->skip_to_keyframe = 0; + matroska->done = 0; + matroska->num_levels = 0; + return -1; } static int matroska_read_close(AVFormatContext *s) diff --git a/libavformat/matroskaenc.c b/libavformat/matroskaenc.c index b37d10cba1..2a8a2eaaa1 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 */ @@ -118,7 +118,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))); } /** @@ -128,7 +128,7 @@ 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); @@ -155,18 +155,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) @@ -178,7 +178,7 @@ static void put_ebml_uint(AVIOContext *pb, unsigned int elementid, uint64_t val) put_ebml_id(pb, elementid); put_ebml_num(pb, bytes, 0); for (i = bytes - 1; i >= 0; i--) - avio_w8(pb, val >> i*8); + avio_w8(pb, (uint8_t)(val >> i*8)); } static void put_ebml_float(AVIOContext *pb, unsigned int elementid, double val) @@ -211,7 +211,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 @@ -590,7 +590,10 @@ static int mkv_write_tracks(AVFormatContext *s) switch (codec->codec_type) { case AVMEDIA_TYPE_VIDEO: put_ebml_uint(pb, MATROSKA_ID_TRACKTYPE, MATROSKA_TRACK_TYPE_VIDEO); - put_ebml_uint(pb, MATROSKA_ID_TRACKDEFAULTDURATION, av_q2d(codec->time_base)*1E9); + if(st->avg_frame_rate.num && st->avg_frame_rate.den && 1.0/av_q2d(st->avg_frame_rate) > av_q2d(codec->time_base)) + put_ebml_uint(pb, MATROSKA_ID_TRACKDEFAULTDURATION, 1E9/av_q2d(st->avg_frame_rate)); + else + put_ebml_uint(pb, MATROSKA_ID_TRACKDEFAULTDURATION, av_q2d(codec->time_base)*1E9); if (!native_id && ff_codec_get_tag(ff_codec_movvideo_tags, codec->codec_id) && @@ -612,30 +615,36 @@ static int mkv_write_tracks(AVFormatContext *s) // XXX: interlace flag? put_ebml_uint (pb, MATROSKA_ID_VIDEOPIXELWIDTH , codec->width); put_ebml_uint (pb, MATROSKA_ID_VIDEOPIXELHEIGHT, codec->height); - if ((tag = av_dict_get(s->metadata, "stereo_mode", NULL, 0))) { - uint8_t stereo_fmt = atoi(tag->value); - int valid_fmt = 0; - - switch (mkv->mode) { - case MODE_WEBM: - if (stereo_fmt <= MATROSKA_VIDEO_STEREOMODE_TYPE_TOP_BOTTOM - || stereo_fmt == MATROSKA_VIDEO_STEREOMODE_TYPE_RIGHT_LEFT) - valid_fmt = 1; - break; - case MODE_MATROSKAv2: - if (stereo_fmt <= MATROSKA_VIDEO_STEREOMODE_TYPE_BOTH_EYES_BLOCK_RL) - valid_fmt = 1; - break; - } - - if (valid_fmt) - put_ebml_uint (pb, MATROSKA_ID_VIDEOSTEREOMODE, stereo_fmt); + + if ((tag = av_dict_get(st->metadata, "stereo_mode", NULL, 0)) || + (tag = av_dict_get( s->metadata, "stereo_mode", NULL, 0))) { + // save stereo mode flag + uint64_t st_mode = MATROSKA_VIDEO_STEREO_MODE_COUNT; + + for (j=0; j<MATROSKA_VIDEO_STEREO_MODE_COUNT; j++) + if (!strcmp(tag->value, ff_matroska_video_stereo_mode[j])){ + st_mode = j; + break; + } + + if ((mkv->mode == MODE_WEBM && st_mode > 3 && st_mode != 11) + || st_mode >= MATROSKA_VIDEO_STEREO_MODE_COUNT) { + av_log(s, AV_LOG_ERROR, + "The specified stereo mode is not valid.\n"); + return AVERROR(EINVAL); + } else + put_ebml_uint(pb, MATROSKA_ID_VIDEOSTEREOMODE, st_mode); } + 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); + } + + 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; @@ -776,7 +785,7 @@ static int mkv_write_tag(AVFormatContext *s, AVDictionary *m, unsigned int eleme end_ebml_master(s->pb, targets); while ((t = av_dict_get(m, "", t, AV_DICT_IGNORE_SUFFIX))) - if (av_strcasecmp(t->key, "title")) + if (av_strcasecmp(t->key, "title") && av_strcasecmp(t->key, "stereo_mode")) mkv_write_simpletag(s->pb, t); end_ebml_master(s->pb, tag); @@ -893,6 +902,9 @@ static int mkv_write_header(AVFormatContext *s) if (!strcmp(s->oformat->name, "webm")) mkv->mode = MODE_WEBM; else mkv->mode = MODE_MATROSKAv2; + if (s->avoid_negative_ts < 0) + s->avoid_negative_ts = 1; + mkv->tracks = av_mallocz(s->nb_streams * sizeof(*mkv->tracks)); if (!mkv->tracks) return AVERROR(ENOMEM); @@ -940,6 +952,14 @@ static int mkv_write_header(AVFormatContext *s) put_ebml_binary(pb, MATROSKA_ID_SEGMENTUID, segment_uid, 16); } + 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 mkv->duration = 0; mkv->duration_offset = avio_tell(pb); @@ -970,6 +990,7 @@ static int mkv_write_header(AVFormatContext *s) av_init_packet(&mkv->cur_audio_pkt); mkv->cur_audio_pkt.size = 0; mkv->audio_buffer_size = 0; + mkv->cluster_pos = -1; avio_flush(pb); return 0; @@ -994,8 +1015,8 @@ 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; } @@ -1145,12 +1166,16 @@ static int mkv_write_packet_internal(AVFormatContext *s, AVPacket *pkt) } if (!s->pb->seekable) { - if (!mkv->dyn_bc) - avio_open_dyn_buf(&mkv->dyn_bc); + if (!mkv->dyn_bc) { + if ((ret = avio_open_dyn_buf(&mkv->dyn_bc)) < 0) { + av_log(s, AV_LOG_ERROR, "Failed to open dynamic buffer\n"); + return ret; + } + } pb = mkv->dyn_bc; } - if (!mkv->cluster_pos) { + if (mkv->cluster_pos == -1) { mkv->cluster_pos = avio_tell(s->pb); mkv->cluster = start_ebml_master(pb, MATROSKA_ID_CLUSTER, 0); put_ebml_uint(pb, MATROSKA_ID_CLUSTERTIMECODE, FFMAX(0, ts)); @@ -1165,7 +1190,10 @@ static int mkv_write_packet_internal(AVFormatContext *s, AVPacket *pkt) duration = mkv_write_srt_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); @@ -1204,14 +1232,14 @@ 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 (mkv->cluster_pos && + if (mkv->cluster_pos != -1 && ((!s->pb->seekable && (cluster_size > 32*1024 || ts > mkv->cluster_pts + 1000)) || cluster_size > 5*1024*1024 || ts > mkv->cluster_pts + 5000 || (codec->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 "\n", avio_tell(pb), ts); end_ebml_master(pb, mkv->cluster); - mkv->cluster_pos = 0; + mkv->cluster_pos = -1; if (mkv->dyn_bc) mkv_flush_dynbuf(s); } @@ -1255,7 +1283,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); } @@ -1319,9 +1347,6 @@ AVOutputFormat ff_matroska_muxer = { .write_trailer = mkv_write_trailer, .flags = AVFMT_GLOBALHEADER | AVFMT_VARIABLE_FPS | AVFMT_TS_NONSTRICT, - .codec_tag = (const AVCodecTag* const []){ - ff_codec_bmp_tags, ff_codec_wav_tags, 0 - }, .subtitle_codec = AV_CODEC_ID_SSA, .query_codec = mkv_query_codec, }; @@ -1358,6 +1383,5 @@ AVOutputFormat ff_matroska_audio_muxer = { .write_packet = mkv_write_packet, .write_trailer = mkv_write_trailer, .flags = AVFMT_GLOBALHEADER | AVFMT_TS_NONSTRICT, - .codec_tag = (const AVCodecTag* const []){ ff_codec_wav_tags, 0 }, }; #endif diff --git a/libavformat/md5enc.c b/libavformat/md5enc.c index 16412c9a34..050efb1513 100644 --- a/libavformat/md5enc.c +++ b/libavformat/md5enc.c @@ -2,20 +2,20 @@ * 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 */ @@ -76,7 +76,6 @@ static int write_trailer(struct AVFormatContext *s) 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, @@ -120,7 +119,6 @@ static int framemd5_write_trailer(struct AVFormatContext *s) 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, diff --git a/libavformat/md5proto.c b/libavformat/md5proto.c index 12ddde3d8c..6af0a6ed26 100644 --- a/libavformat/md5proto.c +++ b/libavformat/md5proto.c @@ -1,20 +1,20 @@ /* * Copyright (c) 2010 Mans Rullgard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/metadata-example.c b/libavformat/metadata-example.c deleted file mode 100644 index 7bf77e7378..0000000000 --- a/libavformat/metadata-example.c +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2011 Reinhard Tartler - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -/** - * @file - * @example libavformat/metadata-example.c - * Shows how the metadata API can be used in application programs. - */ - -#include <stdio.h> - -#include <libavformat/avformat.h> -#include <libavutil/dict.h> - -int main (int argc, char **argv) -{ - AVFormatContext *fmt_ctx = NULL; - AVDictionaryEntry *tag = NULL; - int ret; - - if (argc != 2) { - printf("usage: %s <input_file>\n" - "example program to demonstrate the use of the libavformat metadata API.\n" - "\n", argv[0]); - return 1; - } - - av_register_all(); - if ((ret = avformat_open_input(&fmt_ctx, argv[1], NULL, NULL))) - return ret; - - while ((tag = av_dict_get(fmt_ctx->metadata, "", tag, AV_DICT_IGNORE_SUFFIX))) - printf("%s=%s\n", tag->key, tag->value); - - avformat_free_context(fmt_ctx); - return 0; -} diff --git a/libavformat/metadata.c b/libavformat/metadata.c index 77fb298217..fc3a9d7d55 100644 --- a/libavformat/metadata.c +++ b/libavformat/metadata.c @@ -1,20 +1,20 @@ /* * copyright (c) 2009 Michael Niedermayer * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/metadata.h b/libavformat/metadata.h index eee3ee4391..6586094e82 100644 --- a/libavformat/metadata.h +++ b/libavformat/metadata.h @@ -1,20 +1,20 @@ /* * copyright (c) 2009 Michael Niedermayer * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/mgsts.c b/libavformat/mgsts.c new file mode 100644 index 0000000000..dec6bfc8f6 --- /dev/null +++ b/libavformat/mgsts.c @@ -0,0 +1,106 @@ +/* + * Metar Gear Solid: The Twin Snakes demuxer + * Copyright (c) 2012 Paul B Mahol + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/intreadwrite.h" +#include "libavutil/intfloat.h" +#include "avformat.h" +#include "riff.h" + +static int read_probe(AVProbeData *p) +{ + if (AV_RB32(p->buf ) != 0x000E || + AV_RB32(p->buf + 4) != 0x0050 || + AV_RB32(p->buf + 12) != 0x0034) + return 0; + return AVPROBE_SCORE_MAX; +} + +static int read_header(AVFormatContext *s) +{ + AVIOContext *pb = s->pb; + AVStream *st; + AVRational fps; + uint32_t chunk_size; + + avio_skip(pb, 4); + chunk_size = avio_rb32(pb); + if (chunk_size != 80) + return AVERROR(EIO); + avio_skip(pb, 20); + + st = avformat_new_stream(s, 0); + if (!st) + return AVERROR(ENOMEM); + + st->need_parsing = AVSTREAM_PARSE_HEADERS; + st->start_time = 0; + st->nb_frames = + st->duration = avio_rb32(pb); + fps = av_d2q(av_int2float(avio_rb32(pb)), INT_MAX); + st->codec->width = avio_rb32(pb); + st->codec->height = avio_rb32(pb); + avio_skip(pb, 12); + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->codec->codec_tag = avio_rb32(pb); + st->codec->codec_id = ff_codec_get_id(ff_codec_bmp_tags, + st->codec->codec_tag); + avpriv_set_pts_info(st, 64, fps.den, fps.num); + avio_skip(pb, 20); + + return 0; +} + +static int read_packet(AVFormatContext *s, AVPacket *pkt) +{ + AVIOContext *pb = s->pb; + uint32_t chunk_size, payload_size; + int ret; + + if (url_feof(pb)) + return AVERROR_EOF; + + avio_skip(pb, 4); + chunk_size = avio_rb32(pb); + avio_skip(pb, 4); + payload_size = avio_rb32(pb); + + if (chunk_size < payload_size + 16) + return AVERROR(EIO); + + ret = av_get_packet(pb, pkt, payload_size); + if (ret < 0) + return ret; + + pkt->pos -= 16; + pkt->duration = 1; + avio_skip(pb, chunk_size - (ret + 16)); + + return ret; +} + +AVInputFormat ff_mgsts_demuxer = { + .name = "mgsts", + .long_name = NULL_IF_CONFIG_SMALL("Metal Gear Solid: The Twin Snakes"), + .read_probe = read_probe, + .read_header = read_header, + .read_packet = read_packet, + .flags = AVFMT_GENERIC_INDEX, +}; diff --git a/libavformat/microdvddec.c b/libavformat/microdvddec.c new file mode 100644 index 0000000000..ecc2819943 --- /dev/null +++ b/libavformat/microdvddec.c @@ -0,0 +1,145 @@ +/* + * MicroDVD subtitle demuxer + * Copyright (c) 2010 Aurelien Jacobs <aurel@gnuage.org> + * Copyright (c) 2012 Clément BÅ“sch <ubitux@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 "avformat.h" +#include "internal.h" +#include "subtitles.h" +#include "libavutil/intreadwrite.h" + +#define MAX_LINESIZE 2048 + + +typedef struct { + FFDemuxSubtitlesQueue q; +} MicroDVDContext; + + +static int microdvd_probe(AVProbeData *p) +{ + unsigned char c, *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 += strcspn(ptr, "\n") + 1; + } + return AVPROBE_SCORE_MAX; +} + +static int64_t get_pts(const char *buf) +{ + int frame; + char c; + + if (sscanf(buf, "{%d}{%c", &frame, &c) == 2) + return frame; + return AV_NOPTS_VALUE; +} + +static int get_duration(const char *buf) +{ + int frame_start, frame_end; + + if (sscanf(buf, "{%d}{%d}", &frame_start, &frame_end) == 2) + return frame_end - frame_start; + return -1; +} + +static int microdvd_read_header(AVFormatContext *s) +{ + AVRational pts_info = (AVRational){ 2997, 125 }; /* default: 23.976 fps */ + MicroDVDContext *microdvd = s->priv_data; + AVStream *st = avformat_new_stream(s, NULL); + int i = 0; + char line[MAX_LINESIZE]; + + if (!st) + return AVERROR(ENOMEM); + + while (!url_feof(s->pb)) { + AVPacket *sub; + int64_t pos = avio_tell(s->pb); + int len = ff_get_line(s->pb, line, sizeof(line)); + + if (!len) + break; + if (i < 3) { + int frame; + double fps; + char c; + + i++; + if ((sscanf(line, "{%d}{}%6lf", &frame, &fps) == 2 || + sscanf(line, "{%d}{%*d}%6lf", &frame, &fps) == 2) + && frame <= 1 && fps > 3 && fps < 100) + pts_info = av_d2q(fps, 100000); + if (!st->codec->extradata && sscanf(line, "{DEFAULT}{}%c", &c) == 1) { + st->codec->extradata = av_strdup(line + 11); + if (!st->codec->extradata) + return AVERROR(ENOMEM); + st->codec->extradata_size = strlen(st->codec->extradata) + 1; + continue; + } + } + sub = ff_subtitles_queue_insert(µdvd->q, line, len, 0); + if (!sub) + return AVERROR(ENOMEM); + sub->pos = pos; + sub->pts = get_pts(sub->data); + sub->duration = get_duration(sub->data); + } + ff_subtitles_queue_finalize(µdvd->q); + avpriv_set_pts_info(st, 64, pts_info.den, pts_info.num); + st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE; + st->codec->codec_id = AV_CODEC_ID_MICRODVD; + return 0; +} + +static int microdvd_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + MicroDVDContext *microdvd = s->priv_data; + return ff_subtitles_queue_read_packet(µdvd->q, pkt); +} + +static int microdvd_read_close(AVFormatContext *s) +{ + MicroDVDContext *microdvd = s->priv_data; + ff_subtitles_queue_clean(µdvd->q); + return 0; +} + +AVInputFormat ff_microdvd_demuxer = { + .name = "microdvd", + .long_name = NULL_IF_CONFIG_SMALL("MicroDVD subtitle format"), + .priv_data_size = sizeof(MicroDVDContext), + .read_probe = microdvd_probe, + .read_header = microdvd_read_header, + .read_packet = microdvd_read_packet, + .read_close = microdvd_read_close, + .flags = AVFMT_GENERIC_INDEX, +}; diff --git a/libavformat/microdvdenc.c b/libavformat/microdvdenc.c new file mode 100644 index 0000000000..ba97444044 --- /dev/null +++ b/libavformat/microdvdenc.c @@ -0,0 +1,51 @@ +/* + * 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 "avformat.h" +#include "rawenc.h" + +static int microdvd_write_header(struct AVFormatContext *s) +{ + AVCodecContext *avctx = s->streams[0]->codec; + + 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); + } + 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 = ff_raw_write_packet, + .flags = AVFMT_NOTIMESTAMPS, + .subtitle_codec = AV_CODEC_ID_MICRODVD, +}; diff --git a/libavformat/mkvtimestamp_v2.c b/libavformat/mkvtimestamp_v2.c new file mode 100644 index 0000000000..3ed195ac5c --- /dev/null +++ b/libavformat/mkvtimestamp_v2.c @@ -0,0 +1,51 @@ +/* + * 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)); + avio_flush(s->pb); + return 0; +} + +AVOutputFormat ff_mkvtimestamp_v2_muxer = { + .name = "mkvtimestamp_v2", + .long_name = NULL_IF_CONFIG_SMALL("extract pts as timecode v2 format, as defined by mkvtoolnix"), + .audio_codec = AV_CODEC_ID_NONE, + .video_codec = AV_CODEC_ID_RAWVIDEO, + .write_header = write_header, + .write_packet = write_packet, +}; diff --git a/libavformat/mm.c b/libavformat/mm.c index 13f33a23ad..125a6773c4 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 */ @@ -174,7 +174,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 06d4a2b277..18b69d471f 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 */ #include "avformat.h" @@ -68,7 +68,7 @@ static int mmf_write_header(AVFormatContext *s) rate = mmf_rate_code(s->streams[0]->codec->sample_rate); if(rate < 0) { - av_log(s, AV_LOG_ERROR, "Unsupported sample rate %d\n", s->streams[0]->codec->sample_rate); + 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; } @@ -76,8 +76,8 @@ static int mmf_write_header(AVFormatContext *s) 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 */ avio_write(pb, "VN:libavcodec,", sizeof("VN:libavcodec,") -1); /* metadata ("ST:songtitle,VN:version,...") */ @@ -265,7 +265,7 @@ static int mmf_read_packet(AVFormatContext *s, MMFContext *mmf = s->priv_data; int ret, size; - if (s->pb->eof_reached) + if (url_feof(s->pb)) return AVERROR(EIO); size = MAX_SIZE; @@ -275,17 +275,13 @@ static int mmf_read_packet(AVFormatContext *s, 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; + pkt->stream_index = 0; mmf->data_size -= ret; - pkt->size = ret; return ret; } diff --git a/libavformat/mms.c b/libavformat/mms.c index 192e7039af..46fbede90c 100644 --- a/libavformat/mms.c +++ b/libavformat/mms.c @@ -4,20 +4,20 @@ * Copyright (c) 2007 Björn Axelsson * Copyright (c) 2010 Zhentan Feng <spyfeng at gmail dot com> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mms.h" diff --git a/libavformat/mms.h b/libavformat/mms.h index e89da41bb6..57e3d7e18a 100644 --- a/libavformat/mms.h +++ b/libavformat/mms.h @@ -2,20 +2,20 @@ * MMS protocol common definitions. * Copyright (c) 2010 Zhentan Feng <spyfeng at gmail dot com> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef AVFORMAT_MMS_H diff --git a/libavformat/mmsh.c b/libavformat/mmsh.c index 5e9d0bc134..86a0575e59 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; @@ -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,46 @@ 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 = h->priv_data; + MMSContext *mms = &mmsh->mms; + int ret; + + ret= mmsh_open_internal(h, mmsh->location, 0, FFMAX(timestamp, 0), 0); + + if(ret>=0){ + if (mms->mms_hd) + ffurl_close(mms->mms_hd); + av_freep(&mms->streams); + av_freep(&mms->asf_header); + av_free(mmsh); + mmsh = h->priv_data; + mms = &mmsh->mms; + mms->asf_header_read_size= mms->asf_header_size; + }else + h->priv_data= 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 4b96f5d6af..c3d2ebb8b9 100644 --- a/libavformat/mmst.c +++ b/libavformat/mmst.c @@ -4,20 +4,20 @@ * Copyright (c) 2007 Björn Axelsson * Copyright (c) 2010 Zhentan Feng <spyfeng at gmail dot com> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -152,7 +152,7 @@ static int send_command_packet(MMSTContext *mmst) return 0; } -static void mms_put_utf16(MMSContext *mms, uint8_t *src) +static void mms_put_utf16(MMSContext *mms, const uint8_t *src) { AVIOContext bic; int size = mms->write_out_ptr - mms->out_buffer; diff --git a/libavformat/mov.c b/libavformat/mov.c index 63049f559f..d7d7f21177 100644 --- a/libavformat/mov.c +++ b/libavformat/mov.c @@ -3,20 +3,20 @@ * Copyright (c) 2001 Fabrice Bellard * 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 */ @@ -32,6 +32,8 @@ #include "libavutil/mathematics.h" #include "libavutil/avstring.h" #include "libavutil/dict.h" +#include "libavutil/opt.h" +#include "libavutil/timecode.h" #include "libavcodec/ac3tab.h" #include "avformat.h" #include "internal.h" @@ -129,6 +131,74 @@ static int mov_metadata_gnre(MOVContext *c, AVIOContext *pb, return 0; } +static int mov_read_custom_metadata(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + char key[1024]={0}, data[1024]={0}; + int i; + AVStream *st; + MOVStreamContext *sc; + + if (c->fc->nb_streams < 1) + return 0; + st = c->fc->streams[c->fc->nb_streams-1]; + sc = st->priv_data; + + if (atom.size <= 8) return 0; + + for (i = 0; i < 3; i++) { // Parse up to three sub-atoms looking for name and data. + int data_size = avio_rb32(pb); + int tag = avio_rl32(pb); + int str_size = 0, skip_size = 0; + char *target = NULL; + + switch (tag) { + case MKTAG('n','a','m','e'): + avio_rb32(pb); // version/flags + str_size = skip_size = data_size - 12; + atom.size -= 12; + target = key; + break; + case MKTAG('d','a','t','a'): + avio_rb32(pb); // version/flags + avio_rb32(pb); // reserved (zero) + str_size = skip_size = data_size - 16; + atom.size -= 16; + target = data; + break; + default: + skip_size = data_size - 8; + str_size = 0; + break; + } + + if (target) { + str_size = FFMIN3(sizeof(data)-1, str_size, atom.size); + avio_read(pb, target, str_size); + target[str_size] = 0; + } + atom.size -= skip_size; + + // If we didn't read the full data chunk for the sub-atom, skip to the end of it. + if (skip_size > str_size) avio_skip(pb, skip_size - str_size); + } + + if (*key && *data) { + if (strcmp(key, "iTunSMPB") == 0) { + int priming, remainder, samples; + if(sscanf(data, "%*X %X %X %X", &priming, &remainder, &samples) == 3){ + if(priming>0 && priming<16384) + sc->start_pad = priming; + return 1; + } + } + if (strcmp(key, "cdec") == 0) { +// av_dict_set(&st->metadata, key, data, 0); + return 1; + } + } + return 0; +} + static const uint32_t mac_to_unicode[128] = { 0x00C4,0x00C5,0x00C7,0x00C9,0x00D1,0x00D6,0x00DC,0x00E1, 0x00E0,0x00E2,0x00E4,0x00E3,0x00E5,0x00E7,0x00E9,0x00E8, @@ -159,7 +229,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; @@ -219,6 +289,9 @@ static int mov_read_udta_string(MOVContext *c, AVIOContext *pb, MOVAtom atom) uint32_t data_type = 0, str_size; int (*parse)(MOVContext*, AVIOContext*, unsigned, const char*) = NULL; + if (c->itunes_metadata && atom.type == MKTAG('-','-','-','-')) + return mov_read_custom_metadata(c, pb, atom); + switch (atom.type) { case MKTAG(0xa9,'n','a','m'): key = "title"; break; case MKTAG(0xa9,'a','u','t'): @@ -227,6 +300,8 @@ static int mov_read_udta_string(MOVContext *c, AVIOContext *pb, MOVAtom atom) case MKTAG(0xa9,'w','r','t'): key = "composer"; break; case MKTAG( 'c','p','r','t'): case MKTAG(0xa9,'c','p','y'): key = "copyright"; break; + case MKTAG(0xa9,'g','r','p'): key = "grouping"; break; + case MKTAG(0xa9,'l','y','r'): key = "lyrics"; break; case MKTAG(0xa9,'c','m','t'): case MKTAG(0xa9,'i','n','f'): key = "comment"; break; case MKTAG(0xa9,'a','l','b'): key = "album"; break; @@ -300,7 +375,7 @@ static int mov_read_udta_string(MOVContext *c, AVIOContext *pb, MOVAtom atom) if (parse) parse(c, pb, str_size, key); else { - if (data_type == 3 || (data_type == 0 && langcode < 0x800)) { // MAC Encoded + if (data_type == 3 || (data_type == 0 && (langcode < 0x400 || langcode == 0x7fff))) { // MAC Encoded mov_read_mac_string(c, pb, str_size, str, sizeof(str)); } else { avio_read(pb, str, str_size); @@ -367,6 +442,7 @@ static int mov_read_dref(MOVContext *c, AVIOContext *pb, MOVAtom atom) if (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); @@ -416,7 +492,7 @@ static int mov_read_dref(MOVContext *c, AVIOContext *pb, MOVAtom atom) avio_skip(pb, 16); for (type = 0; type != -1 && avio_tell(pb) < next; ) { - if (pb->eof_reached) + if(url_feof(pb)) return AVERROR_EOF; type = avio_rb16(pb); len = avio_rb16(pb); @@ -463,6 +539,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; @@ -492,6 +570,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; } @@ -598,7 +689,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; } @@ -687,13 +780,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); @@ -707,7 +803,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; @@ -742,7 +838,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 */ @@ -759,6 +855,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->fc->duration = av_rescale(c->duration, AV_TIME_BASE, c->time_scale); avio_rb32(pb); /* preferred scale */ avio_rb16(pb); /* preferred volume */ @@ -774,31 +874,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; } @@ -811,7 +886,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) { @@ -869,7 +944,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; @@ -878,6 +954,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; @@ -893,6 +973,32 @@ static int mov_read_extradata(MOVContext *c, AVIOContext *pb, MOVAtom atom) return 0; } +/* wrapper functions for reading ALAC/AVS/MJPEG/MJPEG2000 extradata atoms only for those codecs */ +static int mov_read_alac(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + return mov_read_extradata(c, pb, atom, AV_CODEC_ID_ALAC); +} + +static int mov_read_avss(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + return mov_read_extradata(c, pb, atom, AV_CODEC_ID_AVS); +} + +static int mov_read_jp2h(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + return mov_read_extradata(c, pb, atom, AV_CODEC_ID_JPEG2000); +} + +static int mov_read_avid(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + return mov_read_extradata(c, pb, atom, AV_CODEC_ID_AVUI); +} + +static int mov_read_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; @@ -907,6 +1013,7 @@ static int mov_read_wave(MOVContext *c, AVIOContext *pb, MOVAtom atom) if (st->codec->codec_id == AV_CODEC_ID_QDM2 || st->codec->codec_id == AV_CODEC_ID_QDMC) { // pass all frma atom to codec, needed at least for QDMC and QDM2 av_free(st->codec->extradata); + st->codec->extradata_size = 0; st->codec->extradata = av_mallocz(atom.size + FF_INPUT_BUFFER_PADDING_SIZE); if (!st->codec->extradata) return AVERROR(ENOMEM); @@ -946,6 +1053,7 @@ 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_size = 0; st->codec->extradata = av_mallocz(atom.size + FF_INPUT_BUFFER_PADDING_SIZE); if (!st->codec->extradata) return AVERROR(ENOMEM); @@ -971,6 +1079,7 @@ static int mov_read_dvc1(MOVContext *c, AVIOContext *pb, MOVAtom atom) return 0; av_free(st->codec->extradata); + st->codec->extradata_size = 0; st->codec->extradata = av_mallocz(atom.size - 7 + FF_INPUT_BUFFER_PADDING_SIZE); if (!st->codec->extradata) return AVERROR(ENOMEM); @@ -999,6 +1108,7 @@ static int mov_read_strf(MOVContext *c, AVIOContext *pb, MOVAtom atom) return AVERROR_INVALIDDATA; av_free(st->codec->extradata); + st->codec->extradata_size = 0; st->codec->extradata = av_mallocz(atom.size - 40 + FF_INPUT_BUFFER_PADDING_SIZE); if (!st->codec->extradata) return AVERROR(ENOMEM); @@ -1067,18 +1177,18 @@ enum AVCodecID ff_mov_get_lpcm_codec_id(int bps, int flags) } } else { if (flags & 2) { - if (bps == 8) + if (bps == 8) { // signed integer if (flags & 4) return AV_CODEC_ID_PCM_S8; else return AV_CODEC_ID_PCM_U8; - else if (bps == 16) return AV_CODEC_ID_PCM_S16BE; + }else if (bps == 16) return AV_CODEC_ID_PCM_S16BE; else if (bps == 24) return AV_CODEC_ID_PCM_S24BE; else if (bps == 32) return AV_CODEC_ID_PCM_S32BE; } else { - if (bps == 8) + if (bps == 8) { if (flags & 4) return AV_CODEC_ID_PCM_S8; else return AV_CODEC_ID_PCM_U8; - else if (bps == 16) return AV_CODEC_ID_PCM_S16LE; + }else if (bps == 16) return AV_CODEC_ID_PCM_S16LE; else if (bps == 24) return AV_CODEC_ID_PCM_S24LE; else if (bps == 32) return AV_CODEC_ID_PCM_S32LE; } @@ -1105,15 +1215,15 @@ int ff_mov_read_stsd_entries(MOVContext *c, AVIOContext *pb, int entries) int dref_id = 1; MOVAtom a = { AV_RL32("stsd") }; int64_t start_pos = avio_tell(pb); - uint32_t size = avio_rb32(pb); /* size */ + int64_t size = avio_rb32(pb); /* size */ uint32_t format = avio_rl32(pb); /* data format */ if (size >= 16) { avio_rb32(pb); /* reserved */ avio_rb16(pb); /* reserved */ dref_id = avio_rb16(pb); - } else { - av_log(c->fc, AV_LOG_ERROR, "invalid size %d in stsd\n", size); + }else if (size <= 7){ + av_log(c->fc, AV_LOG_ERROR, "invalid size %"PRId64" in stsd\n", size); return AVERROR_INVALIDDATA; } @@ -1125,14 +1235,13 @@ int ff_mov_read_stsd_entries(MOVContext *c, AVIOContext *pb, int entries) /* 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. */ - multiple_stsd: av_log(c->fc, AV_LOG_WARNING, "multiple fourcc not supported\n"); avio_skip(pb, size - (avio_tell(pb) - start_pos)); continue; } /* we cannot demux concatenated h264 streams because of different extradata */ if (st->codec->codec_tag && st->codec->codec_tag == AV_RL32("avc1")) - goto multiple_stsd; + av_log(c->fc, AV_LOG_WARNING, "Concatenated H.264 might not play corrently.\n"); sc->pseudo_stream_id = st->codec->codec_tag ? -1 : pseudo_stream_id; sc->dref_id= dref_id; @@ -1150,14 +1259,16 @@ int ff_mov_read_stsd_entries(MOVContext *c, AVIOContext *pb, int entries) 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; } } - av_dlog(c->fc, "size=%d 4CC= %c%c%c%c codec_type=%d\n", size, + av_dlog(c->fc, "size=%"PRId64" 4CC= %c%c%c%c codec_type=%d\n", size, (format >> 0) & 0xff, (format >> 8) & 0xff, (format >> 16) & 0xff, (format >> 24) & 0xff, st->codec->codec_type); @@ -1204,7 +1315,7 @@ int ff_mov_read_stsd_entries(MOVContext *c, AVIOContext *pb, int entries) (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; @@ -1214,9 +1325,12 @@ int ff_mov_read_stsd_entries(MOVContext *c, AVIOContext *pb, int entries) color_index = 255; color_dec = 256 / (color_count - 1); for (j = 0; j < color_count; j++) { + if (id == AV_CODEC_ID_CINEPAK){ + r = g = b = color_count - 1 - color_index; + }else r = g = b = color_index; sc->palette[j] = - (r << 16) | (g << 8) | (b); + (0xFFU << 24) | (r << 16) | (g << 8) | (b); color_index -= color_dec; if (color_index < 0) color_index = 0; @@ -1237,7 +1351,7 @@ int ff_mov_read_stsd_entries(MOVContext *c, AVIOContext *pb, int entries) g = color_table[j * 3 + 1]; b = color_table[j * 3 + 2]; sc->palette[j] = - (r << 16) | (g << 8) | (b); + (0xFFU << 24) | (r << 16) | (g << 8) | (b); } } else { /* load the palette from the file */ @@ -1247,10 +1361,9 @@ int ff_mov_read_stsd_entries(MOVContext *c, AVIOContext *pb, int entries) 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); @@ -1259,7 +1372,7 @@ int ff_mov_read_stsd_entries(MOVContext *c, AVIOContext *pb, int entries) b = avio_r8(pb); avio_r8(pb); sc->palette[j] = - (r << 16) | (g << 8) | (b); + (a << 24 ) | (r << 16) | (g << 8) | (b); } } } @@ -1355,7 +1468,20 @@ int ff_mov_read_stsd_entries(MOVContext *c, AVIOContext *pb, int entries) st->codec->width = sc->width; st->codec->height = sc->height; } else { - /* other codec type, just skip (rtp, mp4s, tmcd ...) */ + if (st->codec->codec_tag == MKTAG('t','m','c','d')) { + MOVStreamContext *tmcd_ctx = st->priv_data; + int val; + avio_rb32(pb); /* reserved */ + val = avio_rb32(pb); /* flags */ + tmcd_ctx->tmcd_flags = val; + if (val & 1) + st->codec->flags2 |= CODEC_FLAG2_DROP_FRAME_TIMECODE; + avio_rb32(pb); /* time scale */ + avio_rb32(pb); /* frame duration */ + st->codec->time_base.den = avio_r8(pb); /* number of frame */ + st->codec->time_base.num = 1; + } + /* other codec type, just skip (rtp, mp4s, ...) */ avio_skip(pb, size - (avio_tell(pb) - start_pos)); } /* this will read extra atoms at the end (wave, alac, damr, avcC, SMI ...) */ @@ -1421,6 +1547,12 @@ int ff_mov_read_stsd_entries(MOVContext *c, AVIOContext *pb, int entries) st->codec->sample_rate = AV_RB32(st->codec->extradata+32); } break; + case AV_CODEC_ID_AC3: + st->need_parsing = AVSTREAM_PARSE_FULL; + break; + case AV_CODEC_ID_MPEG1VIDEO: + st->need_parsing = AVSTREAM_PARSE_FULL; + break; case AV_CODEC_ID_VC1: st->need_parsing = AVSTREAM_PARSE_FULL; break; @@ -1577,6 +1709,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->alt_sample_size = sample_size; field_size = 32; } else { sample_size = 0; @@ -1654,10 +1787,8 @@ static int mov_read_stts(MOVContext *c, AVIOContext *pb, MOVAtom atom) av_dlog(c->fc, "track[%i].stts.entries = %i\n", c->fc->nb_streams-1, entries); - if (!entries) - return 0; if (entries >= UINT_MAX / sizeof(*sc->stts_data)) - return AVERROR(EINVAL); + return -1; sc->stts_data = av_malloc(entries * sizeof(*sc->stts_data)); if (!sc->stts_data) @@ -1669,6 +1800,11 @@ static int mov_read_stts(MOVContext *c, AVIOContext *pb, MOVAtom atom) sample_count=avio_rb32(pb); sample_duration = avio_rb32(pb); + /* sample_duration < 0 is invalid based on the spec */ + if (sample_duration < 0) { + av_log(c->fc, AV_LOG_ERROR, "Invalid SampleDelta in STTS %d\n", sample_duration); + sample_duration = 1; + } sc->stts_data[i].count= sample_count; sc->stts_data[i].duration= sample_duration; @@ -1722,7 +1858,18 @@ 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) + + 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 (duration < 0 && i+2<entries) sc->dts_shift = FFMAX(sc->dts_shift, -duration); } @@ -1790,12 +1937,13 @@ static void mov_build_index(MOVContext *mov, AVStream *st) AVIndexEntry *mem; /* 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; @@ -1813,11 +1961,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_data && 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; @@ -1858,7 +2006,7 @@ static void mov_build_index(MOVContext *mov, AVStream *st) } if (keyframe) distance = 0; - sample_size = sc->sample_size > 0 ? sc->sample_size : sc->sample_sizes[current_sample]; + sample_size = sc->alt_sample_size > 0 ? sc->alt_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++]; @@ -1973,14 +2121,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 */ @@ -2012,6 +2160,11 @@ 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); @@ -2057,7 +2210,8 @@ 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", @@ -2192,6 +2346,21 @@ static int mov_read_tkhd(MOVContext *c, AVIOContext *pb, MOVAtom atom) sc->width = width >> 16; sc->height = height >> 16; + //Assign clockwise rotate values based on transform matrix so that + //we can compensate for iPhone orientation during capture. + + if (display_matrix[1][0] == -65536 && display_matrix[0][1] == 65536) { + av_dict_set(&st->metadata, "rotate", "90", 0); + } + + if (display_matrix[0][0] == -65536 && display_matrix[1][1] == -65536) { + av_dict_set(&st->metadata, "rotate", "180", 0); + } + + if (display_matrix[1][0] == 65536 && display_matrix[0][1] == -65536) { + av_dict_set(&st->metadata, "rotate", "270", 0); + } + // transform the display width/height according to the matrix // skip this if the display matrix is the default identity matrix // or if it is rotating the picture, ex iPhone 3GS @@ -2268,6 +2437,9 @@ static int mov_read_trex(MOVContext *c, AVIOContext *pb, MOVAtom atom) trex = av_realloc(c->trex_data, (c->trex_count+1)*sizeof(*c->trex_data)); if (!trex) return AVERROR(ENOMEM); + + c->fc->duration = AV_NOPTS_VALUE; // the duration from mvhd is not representing the whole file when fragments are used. + c->trex_data = trex; trex = &c->trex_data[c->trex_count++]; avio_r8(pb); /* version */ @@ -2416,7 +2588,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 */ @@ -2455,9 +2627,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; @@ -2468,9 +2641,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); @@ -2478,22 +2653,65 @@ 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_chan2(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + if (atom.size < 16) + return 0; + avio_skip(pb, 4); + ff_mov_read_chan(c->fc, pb, c->fc->streams[0], atom.size - 4); + return 0; +} + +static int mov_read_tref(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + uint32_t i, size; + MOVStreamContext *sc; + + if (c->fc->nb_streams < 1) + return AVERROR_INVALIDDATA; + sc = c->fc->streams[c->fc->nb_streams - 1]->priv_data; + + size = avio_rb32(pb); + if (size < 12) + return 0; + + sc->trefs_count = (size - 4) / 8; + sc->trefs = av_malloc(sc->trefs_count * sizeof(*sc->trefs)); + if (!sc->trefs) + return AVERROR(ENOMEM); + + sc->tref_type = avio_rl32(pb); + for (i = 0; i < sc->trefs_count; i++) + sc->trefs[i] = avio_rb32(pb); 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_avid }, +{ 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 */ @@ -2507,7 +2725,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 }, @@ -2517,8 +2735,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 }, @@ -2535,7 +2753,7 @@ static const MOVParseTableEntry mov_default_parse_table[] = { { MKTAG('t','f','h','d'), mov_read_tfhd }, /* track fragment header */ { 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','r','e','f'), mov_read_tref }, { MKTAG('c','h','a','p'), mov_read_chap }, { MKTAG('t','r','e','x'), mov_read_trex }, { MKTAG('t','r','u','n'), mov_read_trun }, @@ -2561,25 +2779,33 @@ static int mov_read_default(MOVContext *c, AVIOContext *pb, MOVAtom atom) if (atom.size < 0) atom.size = INT64_MAX; - while (total_size + 8 < atom.size && !pb->eof_reached) { + while (total_size + 8 <= atom.size && !url_feof(pb)) { int (*parse)(MOVContext*, AVIOContext*, MOVAtom) = NULL; a.size = atom.size; a.type=0; if (atom.size >= 8) { a.size = avio_rb32(pb); a.type = avio_rl32(pb); + if (atom.type != MKTAG('r','o','o','t') && + atom.type != MKTAG('m','o','o','v')) + { + if (a.type == MKTAG('t','r','a','k') || a.type == MKTAG('m','d','a','t')) + { + av_log(c->fc, AV_LOG_ERROR, "Broken file, trak/mdat not at top-level\n"); + avio_skip(pb, -8); + return 0; + } + } + total_size += 8; + if (a.size == 1) { /* 64 bit extended size */ + a.size = avio_rb64(pb) - 8; + total_size += 8; + } } av_dlog(c->fc, "type: %08x '%.4s' parent:'%.4s' sz: %"PRId64" %"PRId64" %"PRId64"\n", a.type, (char*)&a.type, (char*)&atom.type, a.size, total_size, atom.size); - total_size += 8; - if (a.size == 1) { /* 64 bit extended size */ - a.size = avio_rb64(pb) - 8; - total_size += 8; - } if (a.size == 0) { - a.size = atom.size - total_size; - if (a.size <= 8) - break; + a.size = atom.size - total_size + 8; } a.size -= 8; if (a.size < 0) @@ -2615,6 +2841,10 @@ static int mov_read_default(MOVContext *c, AVIOContext *pb, MOVAtom atom) left = a.size - avio_tell(pb) + start_pos; if (left > 0) /* skip garbage at atom end */ avio_skip(pb, left); + else if(left < 0) { + av_log(c->fc, AV_LOG_DEBUG, "undoing overread of %"PRId64" in '%.4s'\n", -left, (char*)&a.type); + avio_seek(pb, left, SEEK_CUR); + } } total_size += a.size; @@ -2628,46 +2858,81 @@ static int mov_read_default(MOVContext *c, AVIOContext *pb, MOVAtom atom) static int mov_probe(AVProbeData *p) { - unsigned int offset; + int64_t offset; uint32_t tag; int score = 0; + int moov_offset = -1; /* check file header */ offset = 0; for (;;) { /* ignore invalid offset */ if ((offset + 8) > (unsigned int)p->buf_size) - return score; + break; tag = AV_RL32(p->buf + offset + 4); switch(tag) { /* check for obvious tags */ - case MKTAG('j','P',' ',' '): /* jpeg 2000 signature */ case MKTAG('m','o','o','v'): + moov_offset = offset + 4; + case MKTAG('j','P',' ',' '): /* jpeg 2000 signature */ case MKTAG('m','d','a','t'): case MKTAG('p','n','o','t'): /* detect movs with preview pics like ew.mov and april.mov */ case MKTAG('u','d','t','a'): /* Packet Video PVAuthor adds this and a lot of more junk */ case MKTAG('f','t','y','p'): - return AVPROBE_SCORE_MAX; + if (AV_RB32(p->buf+offset) < 8 && + (AV_RB32(p->buf+offset) != 1 || + offset + 12 > (unsigned int)p->buf_size || + AV_RB64(p->buf+offset + 8) == 0)) { + score = FFMAX(score, AVPROBE_SCORE_MAX - 50); + } 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_MAX - 50; + score = FFMAX(score, AVPROBE_SCORE_MAX - 50); + 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 @@ -2729,7 +2994,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); } } @@ -2740,6 +3005,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; @@ -2755,8 +3063,16 @@ static int mov_read_close(AVFormatContext *s) av_freep(&sc->drefs[j].dir); } av_freep(&sc->drefs); + av_freep(&sc->trefs); if (sc->pb && sc->pb != s->pb) avio_close(sc->pb); + sc->pb = NULL; + av_freep(&sc->chunk_offsets); + av_freep(&sc->keyframes); + av_freep(&sc->sample_sizes); + av_freep(&sc->stps_data); + av_freep(&sc->stsc_data); + av_freep(&sc->stts_data); } if (mov->dv_demux) { @@ -2773,11 +3089,47 @@ static int mov_read_close(AVFormatContext *s) return 0; } +static int tmcd_is_referenced(AVFormatContext *s, int tmcd_id) +{ + int i, j; + + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + MOVStreamContext *sc = st->priv_data; + + if (s->streams[i]->codec->codec_type != AVMEDIA_TYPE_VIDEO) + continue; + for (j = 0; j < sc->trefs_count; j++) + if (tmcd_id == sc->trefs[j]) + return 1; + } + return 0; +} + +/* look for a tmcd track not referenced by any video track, and export it globally */ +static void export_orphan_timecode(AVFormatContext *s) +{ + int i; + + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + + if (st->codec->codec_tag == MKTAG('t','m','c','d') && + !tmcd_is_referenced(s, i + 1)) { + AVDictionaryEntry *tcr = av_dict_get(st->metadata, "timecode", NULL, 0); + if (tcr) { + av_dict_set(&s->metadata, "timecode", tcr->value, 0); + break; + } + } + } +} + static int mov_read_header(AVFormatContext *s) { MOVContext *mov = s->priv_data; AVIOContext *pb = s->pb; - int err; + int i, err; MOVAtom atom = { AV_RL32("root") }; mov->fc = s; @@ -2800,11 +3152,40 @@ 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->tref_type == AV_RL32("tmcd") && sc->trefs_count) { + AVDictionaryEntry *tcr; + int tmcd_st_id = sc->trefs[0] - 1; + + if (tmcd_st_id < 0 || tmcd_st_id >= s->nb_streams) + 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; + if(st->codec->codec_type == AVMEDIA_TYPE_AUDIO && st->codec->codec_id == AV_CODEC_ID_AAC) { + st->skip_samples = sc->start_pad; + } + } if (mov->trex_data) { - int i; for (i = 0; i < s->nb_streams; i++) { AVStream *st = s->streams[i]; MOVStreamContext *sc = st->priv_data; @@ -2849,6 +3230,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) { @@ -2858,7 +3240,7 @@ static int mov_read_packet(AVFormatContext *s, AVPacket *pkt) avio_seek(s->pb, mov->next_root_atom, SEEK_SET); mov->next_root_atom = 0; if (mov_read_default(mov, s->pb, (MOVAtom){ AV_RL32("root"), INT64_MAX }) < 0 || - s->pb->eof_reached) + url_feof(s->pb)) return AVERROR_EOF; av_dlog(s, "read fragments, offset 0x%"PRIx64"\n", avio_tell(s->pb)); goto retry; @@ -2889,7 +3271,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); @@ -2966,8 +3348,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); @@ -2978,7 +3358,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; @@ -2988,6 +3371,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 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"), @@ -2997,4 +3397,5 @@ AVInputFormat ff_mov_demuxer = { .read_packet = mov_read_packet, .read_close = mov_read_close, .read_seek = mov_read_seek, + .priv_class = &class, }; diff --git a/libavformat/mov_chan.c b/libavformat/mov_chan.c index 00a2a4bb92..58aec57374 100644 --- a/libavformat/mov_chan.c +++ b/libavformat/mov_chan.c @@ -570,6 +570,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 +585,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/movenc.c b/libavformat/movenc.c index 50371cda96..7d50131d17 100644 --- a/libavformat/movenc.c +++ b/libavformat/movenc.c @@ -4,20 +4,20 @@ * Copyright (c) 2004 Gildas Bazin <gbazin at videolan dot org> * Copyright (c) 2009 Baptiste Coudurier <baptiste dot coudurier at gmail dot com> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -46,19 +46,22 @@ 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" }, { "frag_custom", "Flush fragments on caller requests", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_FRAG_CUSTOM}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, { "isml", "Create a live smooth streaming feed (for pushing to a publishing point)", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_ISML}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, + { "faststart", "Run a second pass to put the moov at the beginning of the file", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_FASTSTART}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, 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}, { NULL }, }; @@ -81,21 +84,41 @@ 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 dont know the duration when its written + return (mov->use_editlist<0 && !(mov->flags & FF_MOV_FLAG_FRAGMENT)) || mov->use_editlist>0; +} + +static int is_co64_required(const MOVTrack *track) +{ + int i; + + for (i = 0; i < track->entry; i++) { + if (!track->cluster[i].chunkNum) + continue; + if (track->cluster[i].pos + track->data_offset > UINT32_MAX) + return 1; + } + return 0; +} + /* Chunk offset atom */ static int mov_write_stco_tag(AVIOContext *pb, MOVTrack *track) { int i; - int mode64 = 0; // use 32 bit size variant if possible + int mode64 = is_co64_required(track); // use 32 bit size variant if possible int64_t pos = avio_tell(pb); avio_wb32(pb, 0); /* size */ - if (pos > UINT32_MAX) { - mode64 = 1; + 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 @@ -153,11 +176,11 @@ 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) + if (oldval != track->cluster[i].samples_in_chunk && track->cluster[i].chunkNum) { - avio_wb32(pb, i+1); // first chunk + 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; @@ -264,6 +287,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; @@ -273,10 +312,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"); @@ -308,11 +359,10 @@ static int mov_write_esds_tag(AVIOContext *pb, MOVTrack *track) // Basic avio_w8(pb, track->enc->rc_buffer_size>>(3+16)); // Buffersize DB (24 bits) avio_wb16(pb, (track->enc->rc_buffer_size>>3)&0xFFFF); // 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 @@ -326,6 +376,22 @@ static int mov_write_esds_tag(AVIOContext *pb, MOVTrack *track) // Basic return update_size(pb, pos); } +static int mov_pcm_le_gt16(enum AVCodecID codec_id) +{ + return codec_id == AV_CODEC_ID_PCM_S24LE || + codec_id == AV_CODEC_ID_PCM_S32LE || + codec_id == AV_CODEC_ID_PCM_F32LE || + codec_id == AV_CODEC_ID_PCM_F64LE; +} + +static int mov_pcm_be_gt16(enum AVCodecID codec_id) +{ + return codec_id == AV_CODEC_ID_PCM_S24BE || + codec_id == AV_CODEC_ID_PCM_S32BE || + codec_id == AV_CODEC_ID_PCM_F32BE || + codec_id == AV_CODEC_ID_PCM_F64BE; +} + static int mov_write_ms_tag(AVIOContext *pb, MOVTrack *track) { int64_t pos = avio_tell(pb); @@ -377,9 +443,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 */ @@ -387,11 +455,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) { @@ -552,6 +625,8 @@ static int get_samples_per_packet(MOVTrack *track) { int i, first_duration; +// return track->enc->frame_size; + /* use 1 for raw PCM */ if (!track->audio_vbr) return 1; @@ -574,9 +649,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 */ @@ -605,10 +688,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 ? @@ -616,13 +708,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); @@ -655,19 +761,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); @@ -776,20 +869,20 @@ static int mov_get_dv_codec_tag(AVFormatContext *s, MOVTrack *track) { int tag; - if (track->enc->width == 720) /* SD */ - if (track->enc->height == 480) /* NTSC */ + if (track->enc->width == 720) { /* SD */ + if (track->enc->height == 480) { /* NTSC */ if (track->enc->pix_fmt == AV_PIX_FMT_YUV422P) tag = MKTAG('d','v','5','n'); else tag = MKTAG('d','v','c',' '); - else if (track->enc->pix_fmt == AV_PIX_FMT_YUV422P) tag = MKTAG('d','v','5','p'); + }else if (track->enc->pix_fmt == AV_PIX_FMT_YUV422P) tag = MKTAG('d','v','5','p'); else if (track->enc->pix_fmt == AV_PIX_FMT_YUV420P) tag = MKTAG('d','v','c','p'); else tag = MKTAG('d','v','p','p'); - else if (track->enc->height == 720) /* HD 720 line */ + } else if (track->enc->height == 720) { /* HD 720 line */ if (track->enc->time_base.den == 50) tag = MKTAG('d','v','h','q'); else tag = MKTAG('d','v','h','p'); - else if (track->enc->height == 1080) /* HD 1080 line */ + } else if (track->enc->height == 1080) { /* HD 1080 line */ if (track->enc->time_base.den == 25) tag = MKTAG('d','v','h','5'); else tag = MKTAG('d','v','h','6'); - else { + } else { av_log(s, AV_LOG_ERROR, "unsupported height for dv codec\n"); return 0; } @@ -802,6 +895,7 @@ static const struct { 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 }, @@ -827,7 +921,8 @@ static int mov_get_rawvideo_codec_tag(AVFormatContext *s, MOVTrack *track) if (track->enc->pix_fmt == mov_pix_fmt_tags[i].pix_fmt) { tag = mov_pix_fmt_tags[i].tag; track->enc->bits_per_coded_sample = mov_pix_fmt_tags[i].bps; - break; + if (track->enc->codec_tag == mov_pix_fmt_tags[i].tag) + break; } } @@ -883,6 +978,14 @@ static const AVCodecTag codec_3gp_tags[] = { { AV_CODEC_ID_NONE, 0 }, }; +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') }, + { AV_CODEC_ID_VP6F, MKTAG('V','P','6','F') }, + { AV_CODEC_ID_NONE, 0 }, +}; + static int mov_find_codec_tag(AVFormatContext *s, MOVTrack *track) { int tag; @@ -897,6 +1000,8 @@ static int mov_find_codec_tag(AVFormatContext *s, MOVTrack *track) tag = ipod_get_codec_tag(s, track); else if (track->mode & MODE_3GP) tag = ff_codec_get_tag(codec_3gp_tags, track->enc->codec_id); + else if (track->mode & MODE_F4V) + tag = ff_codec_get_tag(codec_f4v_tags, track->enc->codec_id); else tag = mov_get_codec_tag(s, track); @@ -1013,9 +1118,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_H264) { mov_write_avcc_tag(pb, track); @@ -1036,6 +1143,26 @@ static int mov_write_video_tag(AVIOContext *pb, MOVTrack *track) return update_size(pb, pos); } +static int mov_write_tmcd_tag(AVIOContext *pb, MOVTrack *track) +{ + int64_t pos = avio_tell(pb); + int frame_duration = av_rescale(track->timescale, track->enc->time_base.num, track->enc->time_base.den); + int nb_frames = (track->timescale + frame_duration/2) / frame_duration; + + avio_wb32(pb, 0); /* size */ + ffio_wfourcc(pb, "tmcd"); /* Data format */ + avio_wb32(pb, 0); /* Reserved */ + avio_wb32(pb, 1); /* Data reference index */ + avio_wb32(pb, 0); /* Flags */ + avio_wb32(pb, track->timecode_flags); /* Flags (timecode) */ + avio_wb32(pb, track->timescale); /* Timescale */ + avio_wb32(pb, frame_duration); /* Frame duration */ + avio_w8(pb, nb_frames); /* Number of frames */ + avio_wb24(pb, 0); /* Reserved */ + /* TODO: source reference string */ + return update_size(pb, pos); +} + static int mov_write_rtp_tag(AVIOContext *pb, MOVTrack *track) { int64_t pos = avio_tell(pb); @@ -1071,6 +1198,8 @@ static int mov_write_stsd_tag(AVIOContext *pb, MOVTrack *track) mov_write_subtitle_tag(pb, track); else if (track->enc->codec_tag == MKTAG('r','t','p',' ')) mov_write_rtp_tag(pb, track); + else if (track->enc->codec_tag == MKTAG('t','m','c','d')) + mov_write_tmcd_tag(pb, track); return update_size(pb, pos); } @@ -1157,6 +1286,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 */ @@ -1202,9 +1332,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 */ @@ -1215,7 +1368,34 @@ 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. + */ + 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) @@ -1258,9 +1438,16 @@ static int mov_write_hdlr_tag(AVIOContext *pb, MOVTrack *track) if (track->tag == MKTAG('t','x','3','g')) hdlr_type = "sbtl"; else hdlr_type = "text"; descr = "SubtitleHandler"; + } else if (track->enc->codec_tag == MKTAG('t','m','c','d')) { + hdlr_type = "tmcd"; + descr = "TimeCodeHandler"; } else if (track->enc->codec_tag == MKTAG('r','t','p',' ')) { hdlr_type = "hint"; descr = "HintHandler"; + } else { + hdlr = "dhlr"; + hdlr_type = "url "; + descr = "DataHandler"; } } @@ -1305,8 +1492,10 @@ static int mov_write_minf_tag(AVIOContext *pb, MOVTrack *track) else if (track->enc->codec_type == AVMEDIA_TYPE_AUDIO) mov_write_smhd_tag(pb); else if (track->enc->codec_type == AVMEDIA_TYPE_SUBTITLE) { - if (track->tag == MKTAG('t','e','x','t')) mov_write_gmhd_tag(pb); + if (track->tag == MKTAG('t','e','x','t')) mov_write_gmhd_tag(pb, track); else mov_write_nmhd_tag(pb); + } else if (track->tag == MKTAG('t','m','c','d')) { + mov_write_gmhd_tag(pb, track); } else if (track->tag == MKTAG('r','t','p',' ')) { mov_write_hmhd_tag(pb); } @@ -1364,11 +1553,30 @@ static int mov_write_mdia_tag(AVIOContext *pb, MOVTrack *track) return update_size(pb, pos); } +/* transformation matrix + |a b u| + |c d v| + |tx ty w| */ +static void write_matrix(AVIOContext *pb, int16_t a, int16_t b, int16_t c, + int16_t d, int16_t tx, int16_t ty) +{ + avio_wb32(pb, a << 16); /* 16.16 format */ + avio_wb32(pb, b << 16); /* 16.16 format */ + avio_wb32(pb, 0); /* u in 2.30 format */ + avio_wb32(pb, c << 16); /* 16.16 format */ + avio_wb32(pb, d << 16); /* 16.16 format */ + avio_wb32(pb, 0); /* v in 2.30 format */ + avio_wb32(pb, tx << 16); /* 16.16 format */ + avio_wb32(pb, ty << 16); /* 16.16 format */ + avio_wb32(pb, 1 << 30); /* w in 2.30 format */ +} + static int mov_write_tkhd_tag(AVIOContext *pb, MOVTrack *track, AVStream *st) { int64_t duration = av_rescale_rnd(track->track_duration, MOV_TIMESCALE, track->timescale, AV_ROUND_UP); int version = duration < INT32_MAX ? 0 : 1; + int rotation = 0; if (track->mode == MODE_ISM) version = 1; @@ -1403,16 +1611,19 @@ static int mov_write_tkhd_tag(AVIOContext *pb, MOVTrack *track, AVStream *st) avio_wb16(pb, 0); /* reserved */ /* Matrix structure */ - avio_wb32(pb, 0x00010000); /* reserved */ - avio_wb32(pb, 0x0); /* reserved */ - avio_wb32(pb, 0x0); /* reserved */ - avio_wb32(pb, 0x0); /* reserved */ - avio_wb32(pb, 0x00010000); /* reserved */ - avio_wb32(pb, 0x0); /* reserved */ - avio_wb32(pb, 0x0); /* reserved */ - avio_wb32(pb, 0x0); /* reserved */ - avio_wb32(pb, 0x40000000); /* reserved */ - + if (st && st->metadata) { + AVDictionaryEntry *rot = av_dict_get(st->metadata, "rotate", NULL, 0); + rotation = (rot && rot->value) ? atoi(rot->value) : 0; + } + if (rotation == 90) { + write_matrix(pb, 0, 1, -1, 0, track->enc->height, 0); + } else if (rotation == 180) { + write_matrix(pb, -1, 0, 0, -1, track->enc->width, track->enc->height); + } else if (rotation == 270) { + write_matrix(pb, 0, -1, 1, 0, 0, track->enc->width); + } else { + write_matrix(pb, 1, 0, 0, 1, 0, 0); + } /* Track width and height, for visual only */ if(st && (track->enc->codec_type == AVMEDIA_TYPE_VIDEO || track->enc->codec_type == AVMEDIA_TYPE_SUBTITLE)) { @@ -1493,6 +1704,10 @@ static int mov_write_edts_tag(AVIOContext *pb, MOVTrack *track) avio_wb32(pb, -1); } avio_wb32(pb, 0x00010000); + } else { + av_assert0(track->cluster[0].dts <= 0); + start_ct = -track->cluster[0].dts; + duration += delay; } /* duration */ @@ -1562,11 +1777,8 @@ static int mov_write_trak_tag(AVIOContext *pb, MOVMuxContext *mov, avio_wb32(pb, 0); /* size */ ffio_wfourcc(pb, "trak"); mov_write_tkhd_tag(pb, track, st); - if (track->mode == MODE_PSP || track->flags & MOV_TRACK_CTTS || - (track->entry && track->cluster[0].dts)) { - if (!(mov->flags & FF_MOV_FLAG_FRAGMENT)) - mov_write_edts_tag(pb, track); // PSP Movies require edts box - } + if (supports_edts(mov)) + mov_write_edts_tag(pb, track); // PSP Movies and several other cases require edts box if (track->tref_tag) mov_write_tref_tag(pb, track); mov_write_mdia_tag(pb, track); @@ -1576,7 +1788,7 @@ static int mov_write_trak_tag(AVIOContext *pb, MOVMuxContext *mov, mov_write_udta_sdp(pb, track->rtp_ctx, track->track_id); if (track->enc->codec_type == AVMEDIA_TYPE_VIDEO && track->mode == MODE_MOV) { double sample_aspect_ratio = av_q2d(st->sample_aspect_ratio); - if (0.0 != sample_aspect_ratio && 1.0 != sample_aspect_ratio) + if (st->sample_aspect_ratio.num && 1.0 != sample_aspect_ratio) mov_write_tapt_tag(pb, track); }; return update_size(pb, pos); @@ -1676,15 +1888,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) */ @@ -1768,6 +1972,24 @@ static int mov_write_string_metadata(AVFormatContext *s, AVIOContext *pb, return mov_write_string_tag(pb, name, t->value, lang, long_style); } +/* 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 number */ static int mov_write_trkn_tag(AVIOContext *pb, MOVMuxContext *mov, AVFormatContext *s) @@ -1815,6 +2037,7 @@ static int mov_write_ilst_tag(AVIOContext *pb, MOVMuxContext *mov, 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_tmpo_tag(pb, s); return update_size(pb, pos); } @@ -1924,32 +2147,35 @@ static int mov_write_udta_tag(AVIOContext *pb, MOVMuxContext *mov, if(ret < 0) return ret; - if (mov->mode & MODE_3GP) { - mov_write_3gp_udta_tag(pb_buf, s, "perf", "artist"); - mov_write_3gp_udta_tag(pb_buf, s, "titl", "title"); - mov_write_3gp_udta_tag(pb_buf, s, "auth", "author"); - mov_write_3gp_udta_tag(pb_buf, s, "gnre", "genre"); - mov_write_3gp_udta_tag(pb_buf, s, "dscp", "comment"); - mov_write_3gp_udta_tag(pb_buf, s, "albm", "album"); - mov_write_3gp_udta_tag(pb_buf, s, "cprt", "copyright"); - mov_write_3gp_udta_tag(pb_buf, s, "yrrc", "date"); - } else if (mov->mode == MODE_MOV) { // the title field breaks gtkpod with mp4 and my suspicion is that stuff is not valid in mp4 - mov_write_string_metadata(s, pb_buf, "\251ART", "artist" , 0); - mov_write_string_metadata(s, pb_buf, "\251nam", "title" , 0); - mov_write_string_metadata(s, pb_buf, "\251aut", "author" , 0); - 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); - mov_write_string_metadata(s, pb_buf, "\251des", "comment" , 0); - mov_write_string_metadata(s, pb_buf, "\251gen", "genre" , 0); - mov_write_string_metadata(s, pb_buf, "\251cpy", "copyright" , 0); - } else { - /* iTunes meta data */ - mov_write_meta_tag(pb_buf, mov, s); - } + if (mov->mode & MODE_3GP) { + mov_write_3gp_udta_tag(pb_buf, s, "perf", "artist"); + mov_write_3gp_udta_tag(pb_buf, s, "titl", "title"); + mov_write_3gp_udta_tag(pb_buf, s, "auth", "author"); + mov_write_3gp_udta_tag(pb_buf, s, "gnre", "genre"); + mov_write_3gp_udta_tag(pb_buf, s, "dscp", "comment"); + mov_write_3gp_udta_tag(pb_buf, s, "albm", "album"); + mov_write_3gp_udta_tag(pb_buf, s, "cprt", "copyright"); + mov_write_3gp_udta_tag(pb_buf, s, "yrrc", "date"); + } else if (mov->mode == MODE_MOV) { // the title field breaks gtkpod with mp4 and my suspicion is that stuff is not valid in mp4 + mov_write_string_metadata(s, pb_buf, "\251ART", "artist" , 0); + mov_write_string_metadata(s, pb_buf, "\251nam", "title" , 0); + mov_write_string_metadata(s, pb_buf, "\251aut", "author" , 0); + mov_write_string_metadata(s, pb_buf, "\251alb", "album" , 0); + mov_write_string_metadata(s, pb_buf, "\251day", "date" , 0); + mov_write_string_metadata(s, pb_buf, "\251swr", "encoder" , 0); + // currently ignored by mov.c + mov_write_string_metadata(s, pb_buf, "\251des", "comment" , 0); + // add support for libquicktime, this atom is also actually read by mov.c + mov_write_string_metadata(s, pb_buf, "\251cmt", "comment" , 0); + mov_write_string_metadata(s, pb_buf, "\251gen", "genre" , 0); + mov_write_string_metadata(s, pb_buf, "\251cpy", "copyright" , 0); + } else { + /* iTunes meta data */ + mov_write_meta_tag(pb_buf, mov, s); + } - if (s->nb_chapters) - mov_write_chpl_tag(pb_buf, s); + if (s->nb_chapters) + mov_write_chpl_tag(pb_buf, s); if ((size = avio_close_dyn_buf(pb_buf, &buf)) > 0) { avio_wb32(pb, size+8); @@ -2011,6 +2237,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) { @@ -2025,6 +2274,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) @@ -2039,6 +2291,14 @@ static int mov_write_moov_tag(AVIOContext *pb, MOVMuxContext *mov, mov->tracks[mov->tracks[i].src_track].track_id; } } + for (i = 0; i < mov->nb_streams; i++) { + if (mov->tracks[i].tag == MKTAG('t','m','c','d')) { + int src_trk = mov->tracks[i].src_track; + mov->tracks[src_trk].tref_tag = mov->tracks[i].tag; + mov->tracks[src_trk].tref_id = mov->tracks[i].track_id; + mov->tracks[i].track_duration = mov->tracks[src_trk].track_duration; + } + } mov_write_mvhd_tag(pb, mov); if (mov->mode != MODE_MOV && !mov->iods_skip) @@ -2495,6 +2755,8 @@ static int mov_write_ftyp_tag(AVIOContext *pb, AVFormatContext *s) ffio_wfourcc(pb, has_video ? "M4V ":"M4A "); else if (mov->mode == MODE_ISM) ffio_wfourcc(pb, "isml"); + else if (mov->mode == MODE_F4V) + ffio_wfourcc(pb, "f4v "); else ffio_wfourcc(pb, "qt "); @@ -2662,6 +2924,21 @@ static void mov_parse_vc1_frame(AVPacket *pkt, MOVTrack *trk, int fragment) } } +static int get_moov_size(AVFormatContext *s) +{ + int ret; + uint8_t *buf; + AVIOContext *moov_buf; + MOVMuxContext *mov = s->priv_data; + + if ((ret = avio_open_dyn_buf(&moov_buf)) < 0) + return ret; + mov_write_moov_tag(moov_buf, mov, s); + ret = avio_close_dyn_buf(moov_buf, &buf); + av_free(buf); + return ret; +} + static int mov_flush_fragment(AVFormatContext *s) { MOVMuxContext *mov = s->priv_data; @@ -2673,10 +2950,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) @@ -2685,13 +2960,9 @@ static int mov_flush_fragment(AVFormatContext *s) if (i < mov->nb_streams) return 0; - if ((ret = avio_open_dyn_buf(&moov_buf)) < 0) - return ret; - mov_write_moov_tag(moov_buf, mov, s); - buf_size = avio_close_dyn_buf(moov_buf, &buf); - av_free(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); @@ -2829,6 +3100,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 @@ -2851,6 +3125,10 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt) } else { size = ff_avc_parse_nal_units(pb, pkt->data, pkt->size); } + } else if (enc->codec_id == AV_CODEC_ID_AAC && pkt->size > 2 && + (AV_RB16(pkt->data) & 0xfff0) == 0xfff0) { + av_log(s, AV_LOG_ERROR, "malformated aac bitstream, use -absf aac_adtstoasc\n"); + return -1; } else { avio_write(pb, pkt->data, size); } @@ -2866,13 +3144,14 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt) } if (!(trk->entry % MOV_INDEX_CLUSTER_SIZE)) { - trk->cluster = av_realloc(trk->cluster, (trk->entry + MOV_INDEX_CLUSTER_SIZE) * sizeof(*trk->cluster)); + trk->cluster = av_realloc_f(trk->cluster, sizeof(*trk->cluster), (trk->entry + MOV_INDEX_CLUSTER_SIZE)); if (!trk->cluster) return -1; } 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; @@ -2883,9 +3162,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"); @@ -2922,12 +3205,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; @@ -2936,7 +3215,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); @@ -2951,6 +3230,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); } } @@ -2958,6 +3304,8 @@ static int mov_write_packet(AVFormatContext *s, AVPacket *pkt) // as samples, and a tref pointing from the other tracks to the chapter one. static void 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 }; @@ -2969,6 +3317,50 @@ static void mov_create_chapter_track(AVFormatContext *s, int tracknum) track->enc = avcodec_alloc_context3(NULL); track->enc->codec_type = AVMEDIA_TYPE_SUBTITLE; + if (avio_open_dyn_buf(&pb) >= 0) { + int size; + uint8_t *buf; + + /* Stub header (usually for Quicktime chapter track) */ + // TextSampleEntry + avio_wb32(pb, 0x01); // displayFlags + avio_w8(pb, 0x00); // horizontal justification + avio_w8(pb, 0x00); // vertical justification + avio_w8(pb, 0x00); // bgColourRed + avio_w8(pb, 0x00); // bgColourGreen + avio_w8(pb, 0x00); // bgColourBlue + avio_w8(pb, 0x00); // bgColourAlpha + // BoxRecord + avio_wb16(pb, 0x00); // defTextBoxTop + avio_wb16(pb, 0x00); // defTextBoxLeft + avio_wb16(pb, 0x00); // defTextBoxBottom + avio_wb16(pb, 0x00); // defTextBoxRight + // StyleRecord + avio_wb16(pb, 0x00); // startChar + avio_wb16(pb, 0x00); // endChar + avio_wb16(pb, 0x01); // fontID + avio_w8(pb, 0x00); // fontStyleFlags + avio_w8(pb, 0x00); // fontSize + avio_w8(pb, 0x00); // fgColourRed + avio_w8(pb, 0x00); // fgColourGreen + avio_w8(pb, 0x00); // fgColourBlue + avio_w8(pb, 0x00); // fgColourAlpha + // FontTableBox + avio_wb32(pb, 0x0D); // box size + ffio_wfourcc(pb, "ftab"); // box atom name + avio_wb16(pb, 0x01); // entry count + // FontRecord + avio_wb16(pb, 0x01); // font ID + avio_w8(pb, 0x00); // font name length + + if ((size = avio_close_dyn_buf(pb, &buf)) > 0) { + track->enc->extradata = buf; + track->enc->extradata_size = size; + } else { + av_free(&buf); + } + } + for (i = 0; i < s->nb_chapters; i++) { AVChapter *c = s->chapters[i]; AVDictionaryEntry *t; @@ -2989,12 +3381,56 @@ static void mov_create_chapter_track(AVFormatContext *s, int tracknum) } } +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 = {src_st->codec->time_base.den, src_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, src_st->avg_frame_rate.num, src_st->avg_frame_rate.den); + rate = src_st->avg_frame_rate; + } + + /* compute the frame number */ + ret = av_timecode_init_from_string(&tc, rate, tcstr, s); + if (ret < 0) + return ret; + + /* tmcd track based on video stream */ + track->mode = mov->mode; + track->tag = MKTAG('t','m','c','d'); + track->src_track = src_index; + track->timescale = mov->tracks[src_index].timescale; + if (tc.flags & AV_TIMECODE_FLAG_DROPFRAME) + track->timecode_flags |= MOV_TIMECODE_FLAG_DROPFRAME; + + /* encode context: tmcd data stream */ + track->enc = avcodec_alloc_context3(NULL); + track->enc->codec_type = AVMEDIA_TYPE_DATA; + track->enc->codec_tag = track->tag; + track->enc->time_base = src_st->codec->time_base; + + /* 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; +} + static int mov_write_header(AVFormatContext *s) { AVIOContext *pb = s->pb; MOVMuxContext *mov = s->priv_data; - AVDictionaryEntry *t; - int i, hint_track = 0; + AVDictionaryEntry *t, *global_tcr = av_dict_get(s->metadata, "timecode", NULL, 0); + int i, hint_track = 0, tmcd_track = 0; /* Set the FRAGMENT flag if any of the fragmentation methods are * enabled. */ @@ -3004,6 +3440,18 @@ static int mov_write_header(AVFormatContext *s) FF_MOV_FLAG_FRAG_CUSTOM)) mov->flags |= FF_MOV_FLAG_FRAGMENT; + /* faststart: moov at the beginning of the file, if supported */ + if (mov->flags & FF_MOV_FLAG_FASTSTART) { + if (mov->flags & FF_MOV_FLAG_FRAGMENT) + mov->flags &= ~FF_MOV_FLAG_FASTSTART; + else + mov->reserved_moov_size = -1; + } + + if (!supports_edts(mov) && s->avoid_negative_ts < 0) { + s->avoid_negative_ts = 1; + } + /* Non-seekable output is ok if using fragmentation. If ism_lookahead * is enabled, we don't support non-seekable output at all. */ if (!s->pb->seekable && @@ -3024,6 +3472,7 @@ static int mov_write_header(AVFormatContext *s) else if (!strcmp("psp", s->oformat->name)) mov->mode = MODE_PSP; else if (!strcmp("ipod",s->oformat->name)) mov->mode = MODE_IPOD; else if (!strcmp("ismv",s->oformat->name)) mov->mode = MODE_ISM; + else if (!strcmp("f4v", s->oformat->name)) mov->mode = MODE_F4V; mov_write_ftyp_tag(pb,s); if (mov->mode == MODE_PSP) { @@ -3051,6 +3500,32 @@ static int mov_write_header(AVFormatContext *s) } } + if (mov->mode == MODE_MOV) { + tmcd_track = mov->nb_streams; + + /* +1 tmcd track for each video stream with a timecode */ + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO && + (global_tcr || av_dict_get(st->metadata, "timecode", NULL, 0))) + mov->nb_meta_tmcd++; + } + + /* check if there is already a tmcd track to remux */ + if (mov->nb_meta_tmcd) { + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + if (st->codec->codec_tag == MKTAG('t','m','c','d')) { + av_log(s, AV_LOG_WARNING, "You requested a copy of the original timecode track " + "so timecode metadata are now ignored\n"); + mov->nb_meta_tmcd = 0; + } + } + } + + mov->nb_streams += mov->nb_meta_tmcd; + } + mov->tracks = av_mallocz(mov->nb_streams*sizeof(*mov->tracks)); if (!mov->tracks) return AVERROR(ENOMEM); @@ -3086,6 +3561,8 @@ static int mov_write_header(AVFormatContext *s) track->height = track->tag>>24 == 'n' ? 486 : 576; } track->timescale = st->codec->time_base.den; + while(track->timescale < 10000) + track->timescale *= 2; if (track->mode == MODE_MOV && track->timescale > 100000) av_log(s, AV_LOG_WARNING, "WARNING codec timebase is very high. If duration is too long,\n" @@ -3093,18 +3570,21 @@ static int mov_write_header(AVFormatContext *s) "or choose different container.\n"); }else if(st->codec->codec_type == AVMEDIA_TYPE_AUDIO){ track->timescale = st->codec->sample_rate; - /* set sample_size for PCM and ADPCM */ - if (av_get_bits_per_sample(st->codec->codec_id) || - st->codec->codec_id == AV_CODEC_ID_ILBC) { + if(!st->codec->frame_size && !av_get_bits_per_sample(st->codec->codec_id)) { + av_log(s, AV_LOG_WARNING, "track %d: codec frame size is not set\n", i); + track->audio_vbr = 1; + }else if(st->codec->codec_id == AV_CODEC_ID_ADPCM_MS || + st->codec->codec_id == AV_CODEC_ID_ADPCM_IMA_WAV || + st->codec->codec_id == AV_CODEC_ID_ILBC){ if (!st->codec->block_align) { - av_log(s, AV_LOG_ERROR, "track %d: codec block align is not set\n", i); + av_log(s, AV_LOG_ERROR, "track %d: codec block align is not set for adpcm\n", i); goto error; } track->sample_size = st->codec->block_align; - } - /* set audio_vbr for compressed audio */ - if (av_get_bits_per_sample(st->codec->codec_id) < 8) { + }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; } if (track->mode != MODE_MOV && track->enc->codec_id == AV_CODEC_ID_MP3 && track->timescale < 16000) { @@ -3114,6 +3594,8 @@ static int mov_write_header(AVFormatContext *s) } }else if(st->codec->codec_type == AVMEDIA_TYPE_SUBTITLE){ track->timescale = st->codec->time_base.den; + }else{ + track->timescale = MOV_TIMESCALE; } if (!track->height) track->height = st->codec->height; @@ -3142,6 +3624,12 @@ static int mov_write_header(AVFormatContext *s) FF_MOV_FLAG_FRAGMENT; } + 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)) mov_write_mdat_tag(pb, mov); @@ -3165,6 +3653,24 @@ static int mov_write_header(AVFormatContext *s) } } + if (mov->nb_meta_tmcd) { + /* Initialize the tmcd tracks */ + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + t = global_tcr; + + if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) { + if (!t) + t = av_dict_get(st->metadata, "timecode", NULL, 0); + if (!t) + continue; + if (mov_create_timecode_track(s, tmcd_track, i, t->value) < 0) + goto error; + tmcd_track++; + } + } + } + avio_flush(pb); if (mov->flags & FF_MOV_FLAG_ISML) @@ -3181,14 +3687,122 @@ static int mov_write_header(AVFormatContext *s) return -1; } +/** + * This function gets the moov size if moved to the top of the file: the chunk + * offset table can switch between stco (32-bit entries) to co64 (64-bit + * entries) when the moov is moved to the top, so the size of the moov would + * change. It also updates the chunk offset tables. + */ +static int compute_moov_size(AVFormatContext *s) +{ + int i, moov_size, moov_size2; + MOVMuxContext *mov = s->priv_data; + + moov_size = get_moov_size(s); + if (moov_size < 0) + return moov_size; + + for (i = 0; i < mov->nb_streams; i++) + mov->tracks[i].data_offset += moov_size; + + moov_size2 = get_moov_size(s); + if (moov_size2 < 0) + return moov_size2; + + /* if the size changed, we just switched from stco to co64 and needs to + * update the offsets */ + if (moov_size2 != moov_size) + for (i = 0; i < mov->nb_streams; i++) + mov->tracks[i].data_offset += moov_size2 - moov_size; + + return moov_size2; +} + +static int shift_data(AVFormatContext *s) +{ + int ret = 0, moov_size; + MOVMuxContext *mov = s->priv_data; + int64_t pos, pos_end = avio_tell(s->pb); + uint8_t *buf, *read_buf[2]; + int read_buf_id = 0; + int read_size[2]; + AVIOContext *read_pb; + + moov_size = compute_moov_size(s); + if (moov_size < 0) + return moov_size; + + buf = av_malloc(moov_size * 2); + if (!buf) + return AVERROR(ENOMEM); + read_buf[0] = buf; + read_buf[1] = buf + moov_size; + + /* Shift the data: the AVIO context of the output can only be used for + * writing, so we re-open the same output, but for reading. It also avoids + * a read/seek/write/seek back and forth. */ + avio_flush(s->pb); + ret = avio_open(&read_pb, s->filename, AVIO_FLAG_READ); + if (ret < 0) { + av_log(s, AV_LOG_ERROR, "Unable to re-open %s output file for " + "the second pass (faststart)\n", s->filename); + goto end; + } + + /* mark the end of the shift to up to the last data we wrote, and get ready + * for writing */ + pos_end = avio_tell(s->pb); + avio_seek(s->pb, mov->reserved_moov_pos + moov_size, SEEK_SET); + + /* start reading at where the new moov will be placed */ + avio_seek(read_pb, mov->reserved_moov_pos, SEEK_SET); + pos = avio_tell(read_pb); + +#define READ_BLOCK do { \ + read_size[read_buf_id] = avio_read(read_pb, read_buf[read_buf_id], moov_size); \ + read_buf_id ^= 1; \ +} while (0) + + /* shift data by chunk of at most moov_size */ + READ_BLOCK; + do { + int n; + READ_BLOCK; + n = read_size[read_buf_id]; + if (n <= 0) + break; + avio_write(s->pb, read_buf[read_buf_id], n); + pos += n; + } while (pos < pos_end); + avio_close(read_pb); + +end: + av_free(buf); + return ret; +} + static int mov_write_trailer(AVFormatContext *s) { MOVMuxContext *mov = s->priv_data; AVIOContext *pb = s->pb; + int64_t moov_pos; int res = 0; int i; - int64_t moov_pos = avio_tell(pb); + /* + * 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; + } + } + + moov_pos = avio_tell(pb); if (!(mov->flags & FF_MOV_FLAG_FRAGMENT)) { /* Write size of mdat tag */ @@ -3204,9 +3818,31 @@ 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); - - mov_write_moov_tag(pb, mov, s); + avio_seek(pb, mov->reserved_moov_size > 0 ? mov->reserved_moov_pos : moov_pos, SEEK_SET); + + if (mov->reserved_moov_size == -1) { + av_log(s, AV_LOG_INFO, "Starting second pass: moving header on top of the file\n"); + res = shift_data(s); + if (res == 0) { + avio_seek(s->pb, mov->reserved_moov_pos, SEEK_SET); + mov_write_moov_tag(pb, mov, s); + } + } else if (mov->reserved_moov_size > 0) { + int64_t size; + mov_write_moov_tag(pb, mov, s); + size = mov->reserved_moov_size - (avio_tell(pb) - mov->reserved_moov_pos); + if(size < 8){ + av_log(s, AV_LOG_ERROR, "reserved_moov_size is too small, needed %"PRId64" additional\n", 8-size); + return -1; + } + avio_wb32(pb, size); + ffio_wfourcc(pb, "free"); + for(i=0; i<size; i++) + avio_w8(pb, 0); + avio_seek(pb, moov_pos, SEEK_SET); + } else { + mov_write_moov_tag(pb, mov, s); + } } else { mov_flush_fragment(s); mov_write_mfra_tag(pb, mov); @@ -3218,6 +3854,8 @@ static int mov_write_trailer(AVFormatContext *s) 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); if (mov->flags & FF_MOV_FLAG_FRAGMENT && mov->tracks[i].vc1_info.struct_offset && s->pb->seekable) { int64_t off = avio_tell(pb); @@ -3368,3 +4006,21 @@ AVOutputFormat ff_ismv_muxer = { .priv_class = &ismv_muxer_class, }; #endif +#if CONFIG_F4V_MUXER +MOV_CLASS(f4v) +AVOutputFormat ff_f4v_muxer = { + .name = "f4v", + .long_name = NULL_IF_CONFIG_SMALL("F4V Adobe Flash Video"), + .mime_type = "application/f4v", + .extensions = "f4v", + .priv_data_size = sizeof(MOVMuxContext), + .audio_codec = AV_CODEC_ID_AAC, + .video_codec = AV_CODEC_ID_H264, + .write_header = mov_write_header, + .write_packet = mov_write_packet, + .write_trailer = mov_write_trailer, + .flags = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH, + .codec_tag = (const AVCodecTag* const []){ codec_f4v_tags, 0 }, + .priv_class = &f4v_muxer_class, +}; +#endif diff --git a/libavformat/movenc.h b/libavformat/movenc.h index e20ef1475b..b5d066f4d9 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 */ @@ -35,16 +35,18 @@ #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 +#define MODE_F4V 0x80 typedef struct MOVIentry { uint64_t pos; 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 @@ -79,12 +81,18 @@ typedef struct MOVIndex { 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 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 @@ -100,7 +108,7 @@ typedef struct MOVIndex { 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; @@ -136,6 +144,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; @@ -143,6 +152,9 @@ typedef struct MOVMuxContext { int flags; int rtp_flags; + int reserved_moov_size; ///< 0 for disabled, -1 for automatic, size otherwise + int64_t reserved_moov_pos; + int iods_skip; int iods_video_profile; int iods_audio_profile; @@ -153,6 +165,8 @@ typedef struct MOVMuxContext { int max_fragment_size; int ism_lookahead; AVIOContext *mdat_buf; + + int use_editlist; } MOVMuxContext; #define FF_MOV_FLAG_RTP_HINT 1 @@ -162,6 +176,7 @@ typedef struct MOVMuxContext { #define FF_MOV_FLAG_SEPARATE_MOOF 16 #define FF_MOV_FLAG_FRAG_CUSTOM 32 #define FF_MOV_FLAG_ISML 64 +#define FF_MOV_FLAG_FASTSTART 128 int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt); diff --git a/libavformat/movenchint.c b/libavformat/movenchint.c index 5ef90f154a..9c4ddeb372 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 */ diff --git a/libavformat/mp3dec.c b/libavformat/mp3dec.c index 7d0c2fb6ed..d3f7cbdfc6 100644 --- a/libavformat/mp3dec.c +++ b/libavformat/mp3dec.c @@ -2,20 +2,20 @@ * 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 */ @@ -35,6 +35,13 @@ #define XING_TOC_COUNT 100 +typedef struct { + int64_t filesize; + int xing_toc; + int start_pad; + int end_pad; +} MP3Context; + /* mp3 read */ static int mp3_read_probe(AVProbeData *p) @@ -69,37 +76,20 @@ 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_MAX / 2 + 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_MAX / 2; - else if (max_frames >= 4) return AVPROBE_SCORE_MAX / 4; - else if (max_frames >= 1) return 1; - else return 0; + if (first_frames>=4) return AVPROBE_SCORE_MAX/2+1; + else if(max_frames>200)return AVPROBE_SCORE_MAX/2; + else if(max_frames>=4) return AVPROBE_SCORE_MAX/4; + else if(ff_id3v2_match(buf0, ID3v2_DEFAULT_MAGIC) && 2*ff_id3v2_tag_len(buf0) >= p->buf_size) + return AVPROBE_SCORE_MAX/8; + else if(max_frames>=1) return 1; + else return 0; //mpegps_mp3_unrecognized_format.mpg has max_frames=3 } static void read_xing_toc(AVFormatContext *s, int64_t filesize, int64_t duration) { int i; + MP3Context *mp3 = s->priv_data; if (!filesize && !(filesize = avio_size(s->pb))) { @@ -115,6 +105,7 @@ static void read_xing_toc(AVFormatContext *s, int64_t filesize, int64_t duration av_rescale(i, duration, XING_TOC_COUNT), 0, 0, AVINDEX_KEYFRAME); } + mp3->xing_toc = 1; } /** @@ -122,6 +113,7 @@ static void read_xing_toc(AVFormatContext *s, int64_t filesize, int64_t duration */ static int mp3_parse_vbr_tags(AVFormatContext *s, AVStream *st, int64_t base) { + MP3Context *mp3 = s->priv_data; uint32_t v, spf; unsigned frames = 0; /* Total number of frames in file */ unsigned size = 0; /* Total number of bytes in the stream */ @@ -152,6 +144,18 @@ static int mp3_parse_vbr_tags(AVFormatContext *s, AVStream *st, int64_t base) if (v & XING_FLAG_TOC && frames) read_xing_toc(s, size, av_rescale_q(frames, (AVRational){spf, c.sample_rate}, st->time_base)); + if(v & 8) + avio_skip(s->pb, 4); + + v = avio_rb32(s->pb); + if(v == MKBETAG('L', 'A', 'M', 'E') || v == MKBETAG('L', 'a', 'v', 'f')) { + avio_skip(s->pb, 21-4); + v= avio_rb24(s->pb); + mp3->start_pad = v>>12; + mp3-> end_pad = v&4095; + st->skip_samples = mp3->start_pad + 528 + 1; + av_log(s, AV_LOG_DEBUG, "pad %d %d\n", mp3->start_pad, mp3-> end_pad); + } } /* Check for VBRI tag (always 32 bytes after end of mpegaudio header) */ @@ -184,6 +188,7 @@ static int mp3_parse_vbr_tags(AVFormatContext *s, AVStream *st, int64_t base) static int mp3_read_header(AVFormatContext *s) { + MP3Context *mp3 = s->priv_data; AVStream *st; int64_t off; @@ -193,17 +198,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); @@ -215,15 +224,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; + MP3Context *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; @@ -233,13 +253,36 @@ 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) { + MP3Context *mp3 = s->priv_data; AVIndexEntry *ie; AVStream *st = s->streams[0]; int64_t ret = av_index_search_timestamp(st, timestamp, flags); uint32_t header = 0; + int i, j; + + if (!mp3->xing_toc) { + st->skip_samples = timestamp <= 0 ? mp3->start_pad + 528 + 1 : 0; + + return -1; + } if (ret < 0) return ret; @@ -249,21 +292,33 @@ static int mp3_seek(AVFormatContext *s, int stream_index, int64_t timestamp, 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; + 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, 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; } AVInputFormat ff_mp3_demuxer = { .name = "mp3", .long_name = NULL_IF_CONFIG_SMALL("MP2/3 (MPEG audio layer 2/3)"), + .priv_data_size = sizeof(MP3Context), .read_probe = mp3_read_probe, .read_header = mp3_read_header, .read_packet = mp3_read_packet, diff --git a/libavformat/mp3enc.c b/libavformat/mp3enc.c index c969777255..044eb29620 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 */ @@ -30,6 +30,10 @@ #include "libavcodec/mpegaudiodecheader.h" #include "libavutil/intreadwrite.h" #include "libavutil/opt.h" +#include "libavcodec/mpegaudio.h" +#include "libavcodec/mpegaudiodata.h" +#include "libavcodec/mpegaudiodecheader.h" +#include "libavformat/avio_internal.h" #include "libavutil/dict.h" #include "libavutil/avassert.h" @@ -51,11 +55,12 @@ static int id3v1_create_tag(AVFormatContext *s, uint8_t *buf) buf[0] = 'T'; buf[1] = 'A'; buf[2] = 'G'; - count += id3v1_set_string(s, "TIT2", buf + 3, 30); //title - count += id3v1_set_string(s, "TPE1", buf + 33, 30); //author|artist - count += id3v1_set_string(s, "TALB", buf + 63, 30); //album - count += id3v1_set_string(s, "TDRL", buf + 93, 4); //date - count += id3v1_set_string(s, "comment", buf + 97, 30); + /* we knowingly overspecify each tag length by one byte to compensate for the mandatory null byte added by av_strlcpy */ + count += id3v1_set_string(s, "TIT2", buf + 3, 30 + 1); //title + count += id3v1_set_string(s, "TPE1", buf + 33, 30 + 1); //author|artist + count += id3v1_set_string(s, "TALB", buf + 63, 30 + 1); //album + count += id3v1_set_string(s, "TDRL", buf + 93, 4 + 1); //date + count += id3v1_set_string(s, "comment", buf + 97, 30 + 1); if ((tag = av_dict_get(s->metadata, "TRCK", NULL, 0))) { //track buf[125] = 0; buf[126] = atoi(tag->value); @@ -110,19 +115,22 @@ static const uint8_t xing_offtbl[2][2] = {{32, 17}, {17, 9}}; /* * Write an empty XING header and initialize respective data. */ -static void mp3_write_xing(AVFormatContext *s) +static int mp3_write_xing(AVFormatContext *s) { MP3Context *mp3 = s->priv_data; AVCodecContext *codec = s->streams[mp3->audio_stream_idx]->codec; - int32_t header; - MPADecodeHeader mpah; - int srate_idx, i, channels; - int bitrate_idx; - int xing_offset; - int ver = 0; + int bitrate_idx; + int best_bitrate_idx = -1; + int best_bitrate_error= INT_MAX; + int xing_offset; + int32_t header, mask; + MPADecodeHeader c; + int srate_idx, ver = 0, i, channels; + int needed; + const char *vendor = (codec->flags & CODEC_FLAG_BITEXACT) ? "Lavf" : LIBAVFORMAT_IDENT; if (!s->pb->seekable) - return; + return 0; for (i = 0; i < FF_ARRAY_ELEMS(avpriv_mpa_freq_tab); i++) { const uint16_t base_freq = avpriv_mpa_freq_tab[i]; @@ -136,9 +144,8 @@ static void mp3_write_xing(AVFormatContext *s) break; } if (i == FF_ARRAY_ELEMS(avpriv_mpa_freq_tab)) { - av_log(s, AV_LOG_WARNING, "Unsupported sample rate, not writing Xing " - "header.\n"); - return; + av_log(s, AV_LOG_WARNING, "Unsupported sample rate, not writing Xing header.\n"); + return -1; } switch (codec->channels) { @@ -146,41 +153,76 @@ static void mp3_write_xing(AVFormatContext *s) case 2: channels = MPA_STEREO; break; default: av_log(s, AV_LOG_WARNING, "Unsupported number of channels, " "not writing Xing header.\n"); - return; + return -1; } - /* 64 kbps frame, should be large enough */ - bitrate_idx = (ver == 3) ? 5 : 8; - /* dummy MPEG audio header */ - header = 0xff << 24; // sync + header = 0xffU << 24; // sync header |= (0x7 << 5 | ver << 3 | 0x1 << 1 | 0x1) << 16; // sync/audio-version/layer 3/no crc*/ - header |= (bitrate_idx << 4 | srate_idx << 2) << 8; + header |= (srate_idx << 2) << 8; header |= channels << 6; - avio_wb32(s->pb, header); - avpriv_mpegaudio_decode_header(&mpah, header); + for (bitrate_idx=1; bitrate_idx<15; bitrate_idx++) { + int error; + avpriv_mpegaudio_decode_header(&c, header | (bitrate_idx << (4+8))); + error= FFABS(c.bit_rate - codec->bit_rate); + if(error < best_bitrate_error){ + best_bitrate_error= error; + best_bitrate_idx = bitrate_idx; + } + } + av_assert0(best_bitrate_idx >= 0); + + for (bitrate_idx= best_bitrate_idx;; bitrate_idx++) { + if (15 == bitrate_idx) + return -1; + mask = bitrate_idx << (4+8); + header |= mask; + avpriv_mpegaudio_decode_header(&c, header); + xing_offset=xing_offtbl[c.lsf == 1][c.nb_channels == 1]; + needed = 4 // header + + xing_offset + + 4 // xing tag + + 4 // frames/size/toc flags + + 4 // frames + + 4 // size + + XING_TOC_SIZE // toc + + 24 + ; + + if (needed <= c.frame_size) + break; + header &= ~mask; + } - av_assert0(mpah.frame_size >= XING_MAX_SIZE); + avio_wb32(s->pb, header); - xing_offset = xing_offtbl[ver != 3][codec->channels == 1]; ffio_fill(s->pb, 0, xing_offset); mp3->xing_offset = avio_tell(s->pb); ffio_wfourcc(s->pb, "Xing"); avio_wb32(s->pb, 0x01 | 0x02 | 0x04); // frames / size / TOC - mp3->size = mpah.frame_size; - mp3->want = 1; + mp3->size = c.frame_size; + mp3->want=1; + mp3->seen=0; + mp3->pos=0; avio_wb32(s->pb, 0); // frames avio_wb32(s->pb, 0); // size - // TOC - for (i = 0; i < XING_TOC_SIZE; i++) - avio_w8(s->pb, 255 * i / XING_TOC_SIZE); + // toc + for (i = 0; i < XING_TOC_SIZE; ++i) + avio_w8(s->pb, (uint8_t)(255 * i / XING_TOC_SIZE)); - mpah.frame_size -= 4 + xing_offset + 4 + 4 + 4 + 4 + XING_TOC_SIZE; - ffio_fill(s->pb, 0, mpah.frame_size); + for (i = 0; i < strlen(vendor); ++i) + avio_w8(s->pb, vendor[i]); + for (; i < 21; ++i) + avio_w8(s->pb, 0); + avio_wb24(s->pb, FFMAX(codec->delay - 528 - 1, 0)<<12); + + ffio_fill(s->pb, 0, c.frame_size - needed); + + return 0; } /* @@ -201,7 +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; @@ -217,8 +259,9 @@ static int mp3_write_audio_packet(AVFormatContext *s, AVPacket *pkt) { MP3Context *mp3 = s->priv_data; - if (mp3->xing_offset && pkt->size >= 4) { + if (pkt->data && pkt->size >= 4) { MPADecodeHeader c; + int av_unused base; avpriv_mpegaudio_decode_header(&c, AV_RB32(pkt->data)); @@ -227,7 +270,26 @@ static int mp3_write_audio_packet(AVFormatContext *s, AVPacket *pkt) if ((c.bit_rate == 0) || (mp3->initial_bitrate != c.bit_rate)) mp3->has_variable_bitrate = 1; - mp3_xing_add_frame(mp3, pkt); +#ifdef FILTER_VBR_HEADERS + /* filter out XING and INFO headers. */ + base = 4 + xing_offtbl[c.lsf == 1][c.nb_channels == 1]; + + if (base + 4 <= pkt->size) { + uint32_t v = AV_RB32(pkt->data + base); + + if (MKBETAG('X','i','n','g') == v || MKBETAG('I','n','f','o') == v) + return 0; + } + + /* filter out VBRI headers. */ + base = 4 + 32; + + if (base + 4 <= pkt->size && MKBETAG('V','B','R','I') == AV_RB32(pkt->data + base)) + return 0; +#endif + + if (mp3->xing_offset) + mp3_xing_add_frame(mp3, pkt); } return ff_raw_write_packet(s, pkt); @@ -301,6 +363,17 @@ static int mp3_write_trailer(struct AVFormatContext *s) return 0; } +static int query_codec(enum AVCodecID id, int std_compliance) +{ + const CodecMime *cm= ff_id3v2_mime_tags; + while(cm->id != AV_CODEC_ID_NONE) { + if(id == cm->id) + return MKTAG('A', 'P', 'I', 'C'); + cm++; + } + return -1; +} + #if CONFIG_MP2_MUXER AVOutputFormat ff_mp2_muxer = { .name = "mp2", @@ -432,6 +505,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 fbf8e92fd9..3b818ed146 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 */ @@ -194,11 +194,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 f60a314b77..56ed7ade13 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 */ @@ -204,7 +204,7 @@ static int mpc8_read_header(AVFormatContext *s) return AVERROR_INVALIDDATA; } - while(!pb->eof_reached){ + while(!url_feof(pb)){ pos = avio_tell(pb); mpc8_get_chunk_header(pb, &tag, &size); if(tag == TAG_STREAMHDR) @@ -242,6 +242,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); @@ -258,7 +260,7 @@ static int mpc8_read_packet(AVFormatContext *s, AVPacket *pkt) int tag; int64_t pos, size; - while(!s->pb->eof_reached){ + while(!url_feof(s->pb)){ pos = avio_tell(s->pb); /* don't return bogus packets with the ape tag data */ diff --git a/libavformat/mpeg.c b/libavformat/mpeg.c index 2d08b17682..22e7869961 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 */ @@ -74,6 +74,7 @@ static int mpegps_probe(AVProbeData *p) // and audio streams else if((code & 0xe0) == AUDIO_ID && pes) {audio++; i+=len;} else if(code == PRIVATE_STREAM_1 && pes) {priv1++; i+=len;} + else if(code == 0x1fd && pes) vid++; //VC1 else if((code & 0xf0) == VIDEO_ID && !pes) invalid++; else if((code & 0xe0) == AUDIO_ID && !pes) invalid++; @@ -81,18 +82,19 @@ static int mpegps_probe(AVProbeData *p) } } - if(vid+audio > invalid) /* invalid VDR files nd short PES streams */ + if(vid+audio > invalid+1) /* invalid VDR files nd short PES streams */ score= AVPROBE_SCORE_MAX/4; if(sys>invalid && sys*9 <= pspack*10) - return pspack > 2 ? AVPROBE_SCORE_MAX/2+2 : AVPROBE_SCORE_MAX/4; // +1 for .mpg + return (audio > 12 || vid > 3 || pspack > 2) ? AVPROBE_SCORE_MAX/2+2 : AVPROBE_SCORE_MAX/4; // +1 for .mpg if(pspack > invalid && (priv1+vid+audio)*10 >= pspack*9) return pspack > 2 ? AVPROBE_SCORE_MAX/2+2 : AVPROBE_SCORE_MAX/4; // +1 for .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_MAX/2+2 : AVPROBE_SCORE_MAX/4; + return (audio > 12 || vid > 3 + 2*invalid) ? AVPROBE_SCORE_MAX/2+2 : AVPROBE_SCORE_MAX/4; //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; } @@ -108,6 +110,7 @@ static int mpegps_read_header(AVFormatContext *s) MpegDemuxContext *m = s->priv_data; const char *sofdec = "Sofdec"; int v, i = 0; + int64_t last_pos = avio_tell(s->pb); m->header_state = 0xff; s->ctx_flags |= AVFMTCTX_NOHEADER; @@ -115,12 +118,14 @@ static int mpegps_read_header(AVFormatContext *s) 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; + if (!m->sofdec) + avio_seek(s->pb, last_pos, SEEK_SET); + /* no need to do more */ return 0; } @@ -144,7 +149,7 @@ static int find_next_start_code(AVIOContext *pb, int *size_ptr, state = *header_state; n = *size_ptr; while (n > 0) { - if (pb->eof_reached) + if (url_feof(pb)) break; v = avio_r8(pb); n--; @@ -218,7 +223,7 @@ static int mpegps_read_pes_header(AVFormatContext *s, startcode = find_next_start_code(s->pb, &size, &m->header_state); last_sync = avio_tell(s->pb); if (startcode < 0){ - if(s->pb->eof_reached) + if(url_feof(s->pb)) return AVERROR_EOF; //FIXME we should remember header_state return AVERROR(EAGAIN); @@ -339,21 +344,9 @@ static int mpegps_read_pes_header(AVFormatContext *s, else if( c!= 0xf ) goto redo; - if (startcode == PRIVATE_STREAM_1 && !m->psm_es_type[startcode & 0xff]) { + if (startcode == PRIVATE_STREAM_1) { startcode = avio_r8(s->pb); len--; - if (startcode >= 0x80 && startcode <= 0xcf) { - /* audio: skip header */ - avio_r8(s->pb); - avio_r8(s->pb); - avio_r8(s->pb); - len -= 3; - if (startcode >= 0xb0 && startcode <= 0xbf) { - /* MLP/TrueHD audio has a 4-byte header */ - avio_r8(s->pb); - len--; - } - } } if(len<0) goto error_redo; @@ -380,20 +373,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; + 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 */ @@ -438,7 +441,7 @@ static int mpegps_read_packet(AVFormatContext *s, if(!memcmp(buf, avs_seqh, 4) && (buf[6] != 0 || buf[7] != 1)) codec_id = AV_CODEC_ID_CAVS; else - codec_id = AV_CODEC_ID_PROBE; + request_probe= 1; type = AVMEDIA_TYPE_VIDEO; } else if (startcode >= 0x1c0 && startcode <= 0x1df) { type = AVMEDIA_TYPE_AUDIO; @@ -453,8 +456,12 @@ static int mpegps_read_packet(AVFormatContext *s, codec_id = AV_CODEC_ID_DTS; } else if (startcode >= 0xa0 && startcode <= 0xaf) { type = AVMEDIA_TYPE_AUDIO; - /* 16 bit form will be handled as AV_CODEC_ID_PCM_S16BE */ - codec_id = AV_CODEC_ID_PCM_DVD; + if(lpcm_header_len == 6) { + codec_id = AV_CODEC_ID_MLP; + } else { + /* 16 bit form will be handled as AV_CODEC_ID_PCM_S16BE */ + codec_id = AV_CODEC_ID_PCM_DVD; + } } else if (startcode >= 0xb0 && startcode <= 0xbf) { type = AVMEDIA_TYPE_AUDIO; codec_id = AV_CODEC_ID_TRUEHD; @@ -468,19 +475,6 @@ static int mpegps_read_packet(AVFormatContext *s, } else if (startcode >= 0xfd55 && startcode <= 0xfd5f) { type = AVMEDIA_TYPE_VIDEO; codec_id = AV_CODEC_ID_VC1; - } else if (startcode == 0x1bd) { - // check dvd audio substream type - type = AVMEDIA_TYPE_AUDIO; - switch(dvdaudio_substream_type & 0xe0) { - case 0xa0: codec_id = AV_CODEC_ID_PCM_DVD; - break; - case 0x80: if((dvdaudio_substream_type & 0xf8) == 0x88) - codec_id = AV_CODEC_ID_DTS; - else codec_id = AV_CODEC_ID_AC3; - break; - default: av_log(s, AV_LOG_ERROR, "Unknown 0x1bd sub-stream\n"); - goto skip; - } } else { skip: /* skip packet */ @@ -494,13 +488,19 @@ static int mpegps_read_packet(AVFormatContext *s, st->id = startcode; st->codec->codec_type = type; st->codec->codec_id = codec_id; + st->request_probe = request_probe; if (codec_id != AV_CODEC_ID_PCM_S16BE) st->need_parsing = AVSTREAM_PARSE_FULL; found: if(st->discard >= AVDISCARD_ALL) goto skip; - if ((startcode >= 0xa0 && startcode <= 0xaf) || - (startcode == 0x1bd && ((dvdaudio_substream_type & 0xe0) == 0xa0))) { + if (startcode >= 0xa0 && startcode <= 0xaf) { + if (lpcm_header_len == 6) { + if (len < 6) + goto skip; + avio_skip(s->pb, 6); + len -=6; + } else { int b1, freq; /* for LPCM, we just skip the header and consider it is raw @@ -522,6 +522,7 @@ static int mpegps_read_packet(AVFormatContext *s, st->codec->codec_id = AV_CODEC_ID_PCM_S16BE; else if (st->codec->bits_per_coded_sample == 28) return AVERROR(EINVAL); + } } ret = av_get_packet(s->pb, pkt, len); pkt->pts = pts; diff --git a/libavformat/mpeg.h b/libavformat/mpeg.h index 75dddf346b..ca019c9eb2 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 */ diff --git a/libavformat/mpegenc.c b/libavformat/mpegenc.c index 4f7bde848c..e2a96bc631 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 */ @@ -65,6 +65,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; @@ -415,7 +416,9 @@ static 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; @@ -837,7 +840,7 @@ static int flush_packet(AVFormatContext *ctx, int stream_index, /* output data */ assert(payload_size - stuffing_size <= av_fifo_size(stream->fifo)); - av_fifo_generic_read(stream->fifo, ctx->pb, payload_size - stuffing_size, &avio_write); + av_fifo_generic_read(stream->fifo, ctx->pb, payload_size - stuffing_size, (void*)avio_write); stream->bytes_to_iframe -= payload_size - stuffing_size; }else{ payload_size= @@ -934,7 +937,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, @@ -1132,7 +1135,7 @@ static int mpeg_mux_end(AVFormatContext *ctx) #define OFFSET(x) offsetof(MpegMuxContext, x) #define E AV_OPT_FLAG_ENCODING_PARAM static const AVOption options[] = { - { "muxrate", NULL, OFFSET(mux_rate), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E }, + { "muxrate", NULL, OFFSET(user_mux_rate), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, 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 4aabcd7e60..33a0eaf08c 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 */ @@ -25,6 +25,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" @@ -122,6 +123,7 @@ 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]; @@ -225,6 +227,17 @@ static void add_pid_to_pmt(MpegTSContext *ts, unsigned int programid, unsigned i p->pids[p->nb_pids++] = pid; } +static void set_pcr_pid(AVFormatContext *s, unsigned int programid, unsigned int pid) +{ + int i; + for(i=0; i<s->nb_programs; i++) { + if(s->programs[i]->id == programid) { + s->programs[i]->pcr_pid = pid; + break; + } + } +} + /** * @brief discard_pid() decides if the pid is to be discarded according * to caller's programs selection @@ -265,6 +278,7 @@ static int discard_pid(MpegTSContext *ts, unsigned int pid) static void write_section_data(AVFormatContext *s, MpegTSFilter *tss1, const uint8_t *buf, int buf_size, int is_start) { + MpegTSContext *ts = s->priv_data; MpegTSSectionFilter *tss = &tss1->u.section_filter; int len; @@ -292,10 +306,19 @@ static void write_section_data(AVFormatContext *s, MpegTSFilter *tss1, } if (tss->section_h_size != -1 && tss->section_index >= tss->section_h_size) { + int crc_valid = 1; tss->end_of_section_reached = 1; - if (!tss->check_crc || - av_crc(av_crc_get_table(AV_CRC_32_IEEE), -1, - tss->section_buf, tss->section_h_size) == 0) + + if (tss->check_crc){ + crc_valid = !av_crc(av_crc_get_table(AV_CRC_32_IEEE), -1, tss->section_buf, tss->section_h_size); + if (crc_valid){ + ts->crc_validity[ tss1->pid ] = 100; + }else if(ts->crc_validity[ tss1->pid ] > -10){ + ts->crc_validity[ tss1->pid ]--; + }else + crc_valid = 2; + } + if (crc_valid) tss->section_cb(tss1, tss->section_buf, tss->section_h_size); } } @@ -385,7 +408,7 @@ static int analyze(const uint8_t *buf, int size, int packet_size, int *index){ memset(stat, 0, packet_size*sizeof(int)); for(x=i=0; i<size-3; i++){ - if(buf[i] == 0x47 && !(buf[i+1] & 0x80) && (buf[i+3] & 0x30)){ + if(buf[i] == 0x47 && !(buf[i+1] & 0x80) && buf[i+3] != 0x47){ stat[x]++; if(stat[x] > best_score){ best_score= stat[x]; @@ -520,7 +543,9 @@ 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 }, - { 0x11, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AAC_LATM }, /* LATM syntax */ + /* Makito encoder sets stream type 0x11 for AAC, + * so auto-detect LOAS/LATM instead of hardcoding it. */ +// { 0x11, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AAC_LATM }, /* LATM syntax */ { 0x1b, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H264 }, { 0xd1, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_DIRAC }, { 0xea, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_VC1 }, @@ -535,6 +560,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 }, }; @@ -574,6 +601,7 @@ static void mpegts_find_stream_type(AVStream *st, 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; } } @@ -582,6 +610,8 @@ 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; avpriv_set_pts_info(st, 33, 1, 90000); st->priv_data = pes; st->codec->codec_type = AVMEDIA_TYPE_DATA; @@ -597,7 +627,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) { @@ -627,6 +658,10 @@ static int mpegts_set_stream_info(AVStream *st, PESContext *pes, } if (st->codec->codec_id == AV_CODEC_ID_NONE) mpegts_find_stream_type(st, pes->stream_type, MISC_types); + if (st->codec->codec_id == AV_CODEC_ID_NONE){ + st->codec->codec_id = old_codec_id; + st->codec->codec_type= old_codec_type; + } return 0; } @@ -668,6 +703,9 @@ static void new_pes_packet(PESContext *pes, AVPacket *pkt) static uint64_t get_bits64(GetBitContext *gb, int bits) { uint64_t ret = 0; + + if (get_bits_left(gb) < bits) + return AV_NOPTS_VALUE; while (bits > 17) { ret <<= 17; ret |= get_bits(gb, 17); @@ -685,6 +723,7 @@ static int read_sl_header(PESContext *pes, SLConfigDescr *sl, const uint8_t *buf int padding_flag = 0, padding_bits = 0, inst_bitrate_flag = 0; int dts_flag = -1, cts_flag = -1; int64_t dts = AV_NOPTS_VALUE, cts = AV_NOPTS_VALUE; + init_get_bits(&gb, buf, buf_size*8); if (sl->use_au_start) @@ -815,10 +854,10 @@ static int mpegts_push_data(MpegTSFilter *filter, code != 0x1ff && code != 0x1f2 && /* program_stream_directory, DSMCC_stream */ code != 0x1f8) { /* ITU-T Rec. H.222.1 type E stream */ pes->state = MPEGTS_PESHEADER; - if (pes->st->codec->codec_id == AV_CODEC_ID_NONE) { + if (pes->st->codec->codec_id == AV_CODEC_ID_NONE && !pes->st->request_probe) { av_dlog(pes->stream, "pid=%x stream_type=%x probing\n", pes->pid, pes->stream_type); - pes->st->codec->codec_id = AV_CODEC_ID_PROBE; + pes->st->request_probe= 1; } } else { pes->state = MPEGTS_PAYLOAD; @@ -1102,7 +1141,7 @@ static int parse_MP4SLDescrTag(MP4DescrParseContext *d, int64_t off, int len) descr->sl.au_seq_num_len = (lengths >> 7) & 0x1f; descr->sl.packet_seq_num_len = (lengths >> 2) & 0x1f; } else { - av_log_missing_feature(d->s, "Predefined SLConfigDescriptor\n", 0); + av_log_missing_feature(d->s, "Predefined SLConfigDescriptor", 0); } return 0; } @@ -1288,15 +1327,17 @@ int ff_parse_mpeg2_descriptor(AVFormatContext *fc, AVStream *st, int stream_type break; case 0x1F: /* FMC descriptor */ get16(pp, desc_end); - if (mp4_descr_count > 0 && st->codec->codec_id == AV_CODEC_ID_AAC_LATM && + if (mp4_descr_count > 0 && (st->codec->codec_id == AV_CODEC_ID_AAC_LATM || st->request_probe>0) && mp4_descr->dec_config_descr_len && mp4_descr->es_id == pid) { AVIOContext pb; ffio_init_context(&pb, mp4_descr->dec_config_descr, mp4_descr->dec_config_descr_len, 0, NULL, NULL, NULL, NULL); ff_mp4_read_dec_config_descr(fc, st, &pb); if (st->codec->codec_id == AV_CODEC_ID_AAC && - st->codec->extradata_size > 0) - st->need_parsing = 0; + st->codec->extradata_size > 0){ + st->request_probe= st->need_parsing = 0; + st->codec->codec_type= AVMEDIA_TYPE_AUDIO; + } } break; case 0x56: /* DVB teletext descriptor */ @@ -1358,6 +1399,9 @@ 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; default: break; } @@ -1400,6 +1444,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); @@ -1436,7 +1481,7 @@ static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len // stop parsing after pmt, we found header if (!ts->stream->nb_streams) - ts->stop_parse = 1; + ts->stop_parse = 2; for(;;) { st = 0; @@ -1516,6 +1561,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); @@ -1527,6 +1573,8 @@ static void pat_cb(MpegTSFilter *filter, const uint8_t *section, int section_len if (h->tid != PAT_TID) return; + ts->stream->ts_id = h->id; + clear_programs(ts); for(;;) { sid = get16(&p, p_end); @@ -1542,7 +1590,9 @@ static void pat_cb(MpegTSFilter *filter, const uint8_t *section, int section_len if (sid == 0x0000) { /* NIT info */ } else { - av_new_program(ts->stream, sid); + program = av_new_program(ts->stream, sid); + program->program_num = sid; + program->pmt_pid = pmt_pid; if (ts->pids[pmt_pid]) mpegts_close_filter(ts, ts->pids[pmt_pid]); mpegts_open_section_filter(ts, pmt_pid, pmt_cb, ts, 1); @@ -1671,7 +1721,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) { @@ -1740,7 +1790,7 @@ static int mpegts_resync(AVFormatContext *s) for(i = 0;i < MAX_RESYNC_SIZE; i++) { c = avio_r8(pb); - if (pb->eof_reached) + if (url_feof(pb)) return -1; if (c == 0x47) { avio_seek(pb, -1, SEEK_CUR); @@ -1783,7 +1833,7 @@ static int read_packet(AVFormatContext *s, uint8_t *buf, int raw_packet_size) static int handle_packets(MpegTSContext *ts, int nb_packets) { AVFormatContext *s = ts->stream; - uint8_t packet[TS_PACKET_SIZE+FF_INPUT_BUFFER_PADDING_SIZE]; + uint8_t packet[TS_PACKET_SIZE + FF_INPUT_BUFFER_PADDING_SIZE]; int packet_num, ret = 0; if (avio_tell(s->pb) != ts->last_pos) { @@ -1807,11 +1857,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); if (ret != 0) break; @@ -1879,18 +1933,18 @@ static int mpegts_read_header(AVFormatContext *s) { MpegTSContext *ts = s->priv_data; AVIOContext *pb = s->pb; - uint8_t buf[5*1024]; + uint8_t buf[8*1024]={0}; int len; int64_t pos; - /* read the first 1024 bytes to get packet size */ + /* read the first 8192 bytes to get packet size */ pos = avio_tell(pb); len = avio_read(pb, buf, sizeof(buf)); - if (len != sizeof(buf)) - goto fail; - ts->raw_packet_size = get_packet_size(buf, sizeof(buf)); - if (ts->raw_packet_size <= 0) - goto fail; + ts->raw_packet_size = get_packet_size(buf, len); + if (ts->raw_packet_size <= 0) { + av_log(s, AV_LOG_WARNING, "Could not detect TS packet size, defaulting to non-FEC/DVHS\n"); + ts->raw_packet_size = TS_PACKET_SIZE; + } ts->stream = s; ts->auto_guess = 0; @@ -1898,8 +1952,11 @@ 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"); + /* 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"); mpegts_open_section_filter(ts, SDT_PID, sdt_cb, ts, 1); @@ -2023,6 +2080,7 @@ static int mpegts_read_packet(AVFormatContext *s, ts->pkt = pkt; ret = handle_packets(ts, 0); if (ret < 0) { + av_free_packet(ts->pkt); /* flush pes data left */ for (i = 0; i < NB_PID_MAX; i++) { if (ts->pids[i] && ts->pids[i]->type == MPEGTS_PES) { @@ -2055,66 +2113,65 @@ static int mpegts_read_close(AVFormatContext *s) return 0; } -static int64_t mpegts_get_pcr(AVFormatContext *s, int stream_index, +static av_unused int64_t mpegts_get_pcr(AVFormatContext *s, int stream_index, int64_t *ppos, int64_t pos_limit) { MpegTSContext *ts = s->priv_data; int64_t pos, timestamp; uint8_t buf[TS_PACKET_SIZE]; int pcr_l, pcr_pid = ((PESContext*)s->streams[stream_index]->priv_data)->pcr_pid; - const int find_next= 1; pos = ((*ppos + ts->raw_packet_size - 1 - ts->pos47) / ts->raw_packet_size) * ts->raw_packet_size + ts->pos47; - if (find_next) { - for(;;) { - avio_seek(s->pb, pos, SEEK_SET); - if (avio_read(s->pb, buf, TS_PACKET_SIZE) != TS_PACKET_SIZE) + 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) { + if (mpegts_resync(s) < 0) return AV_NOPTS_VALUE; - if ((pcr_pid < 0 || (AV_RB16(buf + 1) & 0x1fff) == pcr_pid) && - parse_pcr(×tamp, &pcr_l, buf) == 0) { - break; - } - pos += ts->raw_packet_size; + pos = avio_tell(s->pb); + continue; } - } else { - for(;;) { - pos -= ts->raw_packet_size; - if (pos < 0) - return AV_NOPTS_VALUE; - avio_seek(s->pb, pos, SEEK_SET); - if (avio_read(s->pb, buf, TS_PACKET_SIZE) != TS_PACKET_SIZE) - return AV_NOPTS_VALUE; - if ((pcr_pid < 0 || (AV_RB16(buf + 1) & 0x1fff) == pcr_pid) && - parse_pcr(×tamp, &pcr_l, buf) == 0) { - break; - } + if ((pcr_pid < 0 || (AV_RB16(buf + 1) & 0x1fff) == pcr_pid) && + parse_pcr(×tamp, &pcr_l, buf) == 0) { + *ppos = pos; + return timestamp; } + pos += ts->raw_packet_size; } - *ppos = pos; - return timestamp; + return AV_NOPTS_VALUE; } -static int read_seek(AVFormatContext *s, int stream_index, int64_t target_ts, int flags){ +static int64_t mpegts_get_dts(AVFormatContext *s, int stream_index, + int64_t *ppos, int64_t pos_limit) +{ MpegTSContext *ts = s->priv_data; - uint8_t buf[TS_PACKET_SIZE]; int64_t pos; - - if (ff_seek_frame_binary(s, stream_index, target_ts, flags) < 0) - return -1; - - pos= avio_tell(s->pb); - - for(;;) { - avio_seek(s->pb, pos, SEEK_SET); - if (avio_read(s->pb, buf, TS_PACKET_SIZE) != TS_PACKET_SIZE) - return -1; -// pid = AV_RB16(buf + 1) & 0x1fff; - if(buf[1] & 0x40) break; - pos += ts->raw_packet_size; + pos = ((*ppos + ts->raw_packet_size - 1 - ts->pos47) / ts->raw_packet_size) * ts->raw_packet_size + ts->pos47; + ff_read_frame_flush(s); + if (avio_seek(s->pb, pos, SEEK_SET) < 0) + return AV_NOPTS_VALUE; + while(pos < pos_limit) { + int ret; + AVPacket pkt; + av_init_packet(&pkt); + ret= av_read_frame(s, &pkt); + if(ret < 0) + return AV_NOPTS_VALUE; + av_free_packet(&pkt); + if(pkt.dts != AV_NOPTS_VALUE && pkt.pos >= 0){ + ff_reduce_index(s, pkt.stream_index); + av_add_index_entry(s->streams[pkt.stream_index], pkt.pos, pkt.dts, 0, 0, AVINDEX_KEYFRAME /* FIXME keyframe? */); + if(pkt.stream_index == stream_index){ + *ppos= pkt.pos; + return pkt.dts; + } + } + pos = pkt.pos; } - avio_seek(s->pb, pos, SEEK_SET); - return 0; + return AV_NOPTS_VALUE; } /**************************************************************/ @@ -2131,6 +2188,9 @@ MpegTSContext *ff_mpegts_parse_open(AVFormatContext *s) ts->raw_packet_size = TS_PACKET_SIZE; ts->stream = s; ts->auto_guess = 1; + mpegts_open_section_filter(ts, SDT_PID, sdt_cb, ts, 1); + mpegts_open_section_filter(ts, PAT_PID, pat_cb, ts, 1); + return ts; } @@ -2143,10 +2203,8 @@ int ff_mpegts_parse_packet(MpegTSContext *ts, AVPacket *pkt, len1 = len; ts->pkt = pkt; - ts->stop_parse = 0; for(;;) { - if (ts->stop_parse>0) - break; + ts->stop_parse = 0; if (len < TS_PACKET_SIZE) return -1; if (buf[0] != 0x47) { @@ -2156,6 +2214,8 @@ int ff_mpegts_parse_packet(MpegTSContext *ts, AVPacket *pkt, handle_packet(ts, buf); buf += TS_PACKET_SIZE; len -= TS_PACKET_SIZE; + if (ts->stop_parse == 1) + break; } } return len1 - len; @@ -2178,8 +2238,7 @@ 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, }; @@ -2190,8 +2249,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 89544f06f8..98c4b93736 100644 --- a/libavformat/mpegts.h +++ b/libavformat/mpegts.h @@ -2,20 +2,20 @@ * MPEG2 transport stream defines * Copyright (c) 2003 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/mpegtsenc.c b/libavformat/mpegtsenc.c index 29d83c668a..1b66babea0 100644 --- a/libavformat/mpegtsenc.c +++ b/libavformat/mpegtsenc.c @@ -2,20 +2,20 @@ * MPEG2 transport stream (aka DVB) muxer * Copyright (c) 2003 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -24,6 +24,7 @@ #include "libavutil/dict.h" #include "libavutil/mathematics.h" #include "libavutil/opt.h" +#include "libavutil/avassert.h" #include "libavcodec/mpegvideo.h" #include "avformat.h" #include "internal.h" @@ -75,12 +76,14 @@ typedef struct MpegTSWrite { int pmt_start_pid; int start_pid; + int m2ts_mode; int reemit_pat_pmt; // backward compatibility #define MPEGTS_FLAG_REEMIT_PAT_PMT 0x01 #define MPEGTS_FLAG_AAC_LATM 0x02 int flags; + int copyts; } MpegTSWrite; /* a PES packet header is generated every DEFAULT_PES_HEADER_FREQ packets */ @@ -95,9 +98,12 @@ static const AVOption options[] = { { "mpegts_service_id", "Set service_id field.", offsetof(MpegTSWrite, service_id), AV_OPT_TYPE_INT, {.i64 = 0x0001 }, 0x0001, 0xffff, AV_OPT_FLAG_ENCODING_PARAM}, { "mpegts_pmt_start_pid", "Set the first pid of the PMT.", - offsetof(MpegTSWrite, pmt_start_pid), AV_OPT_TYPE_INT, {.i64 = 0x1000 }, 0x1000, 0x1f00, AV_OPT_FLAG_ENCODING_PARAM}, + offsetof(MpegTSWrite, pmt_start_pid), AV_OPT_TYPE_INT, {.i64 = 0x1000 }, 0x0010, 0x1f00, AV_OPT_FLAG_ENCODING_PARAM}, { "mpegts_start_pid", "Set the first pid.", offsetof(MpegTSWrite, start_pid), AV_OPT_TYPE_INT, {.i64 = 0x0100 }, 0x0100, 0x0f00, AV_OPT_FLAG_ENCODING_PARAM}, + {"mpegts_m2ts_mode", "Enable m2ts mode.", + offsetof(MpegTSWrite, m2ts_mode), AV_OPT_TYPE_INT, {.i64 = -1 }, + -1,1, AV_OPT_FLAG_ENCODING_PARAM}, { "muxrate", NULL, offsetof(MpegTSWrite, mux_rate), AV_OPT_TYPE_INT, {.i64 = 1}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM}, { "pes_payload_size", "Minimum PES packet payload in bytes", offsetof(MpegTSWrite, pes_payload_size), AV_OPT_TYPE_INT, {.i64 = DEFAULT_PES_PAYLOAD_SIZE}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM}, @@ -112,6 +118,8 @@ static const AVOption options[] = { // backward compatibility { "resend_headers", "Reemit PAT/PMT before writing the next packet", offsetof(MpegTSWrite, reemit_pat_pmt), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM}, + { "mpegts_copyts", "dont offset dts/pts", + offsetof(MpegTSWrite, copyts), AV_OPT_TYPE_INT, {.i64=-1}, -1, 1, AV_OPT_FLAG_ENCODING_PARAM}, { NULL }, }; @@ -190,7 +198,7 @@ static int mpegts_write_section1(MpegTSSection *s, int tid, int id, tot_len = 3 + 5 + len + 4; /* check if not too big */ if (tot_len > 1024) - return -1; + return AVERROR_INVALIDDATA; q = section; *q++ = tid; @@ -208,7 +216,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 */ @@ -222,6 +230,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; @@ -307,6 +316,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; @@ -454,9 +477,28 @@ static MpegTSService *mpegts_add_service(MpegTSWrite *ts, return service; } +static int64_t get_pcr(const MpegTSWrite *ts, AVIOContext *pb) +{ + return av_rescale(avio_tell(pb) + 11, 8 * PCR_TIME_BASE, ts->mux_rate) + + ts->first_pcr; +} + +static void mpegts_prefix_m2ts_header(AVFormatContext *s) +{ + MpegTSWrite *ts = s->priv_data; + if (ts->m2ts_mode) { + int64_t pcr = get_pcr(s->priv_data, s->pb); + uint32_t tp_extra_header = pcr % 0x3fffffff; + tp_extra_header = AV_RB32(&tp_extra_header); + avio_write(s->pb, (unsigned char *) &tp_extra_header, + sizeof(tp_extra_header)); + } +} + static void section_write_packet(MpegTSSection *s, const uint8_t *packet) { AVFormatContext *ctx = s->opaque; + mpegts_prefix_m2ts_header(ctx); avio_write(ctx->pb, packet, TS_PACKET_SIZE); } @@ -597,9 +639,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) { @@ -616,6 +659,8 @@ static int mpegts_write_header(AVFormatContext *s) service->pcr_packet_period = pcr_st->codec->time_base.den/(10*pcr_st->codec->time_base.num); } + if(!service->pcr_packet_period) + service->pcr_packet_period = 1; } // output a PCR as soon as possible @@ -632,6 +677,14 @@ static int mpegts_write_header(AVFormatContext *s) service->pcr_packet_period, ts->sdt_packet_period, ts->pat_packet_period); + if (ts->m2ts_mode == -1) { + if (av_match_ext(s->filename, "m2ts")) { + ts->m2ts_mode = 1; + } else { + ts->m2ts_mode = 0; + } + } + avio_flush(s->pb); return 0; @@ -655,7 +708,7 @@ static int mpegts_write_header(AVFormatContext *s) } /* send SDT, PAT and PMT tables regulary */ -static void retransmit_si_info(AVFormatContext *s) +static void retransmit_si_info(AVFormatContext *s, int force_pat) { MpegTSWrite *ts = s->priv_data; int i; @@ -664,7 +717,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++) { @@ -673,12 +726,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; @@ -705,6 +752,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); } @@ -730,6 +778,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); } @@ -751,7 +800,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 @@ -767,7 +816,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; } @@ -797,10 +846,12 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st, int afc_len, stuffing_len; int64_t pcr = -1; /* avoid warning */ int64_t delay = av_rescale(s->max_delay, 90000, AV_TIME_BASE); + int force_pat = st->codec->codec_type == AVMEDIA_TYPE_VIDEO && key && !ts_st->prev_payload_key; is_start = 1; while (payload_size > 0) { - retransmit_si_info(s); + retransmit_si_info(s, force_pat); + force_pat = 0; write_pcr = 0; if (ts_st->pid == ts_st->service->pcr_pid) { @@ -870,6 +921,10 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st, st->codec->codec_id == AV_CODEC_ID_MP3 || st->codec->codec_id == AV_CODEC_ID_AAC)) { *q++ = 0xc0; + } else if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO && + st->codec->codec_id == AV_CODEC_ID_AC3 && + ts->m2ts_mode) { + *q++ = 0xfd; } else { *q++ = 0xbd; if (st->codec->codec_type == AVMEDIA_TYPE_SUBTITLE) { @@ -899,6 +954,17 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st, */ header_len += 3; } + /* for Blu-ray AC3 Audio the PES Extension flag should be as follow + * otherwise it will not play sound on blu-ray + */ + if (ts->m2ts_mode && + st->codec->codec_type == AVMEDIA_TYPE_AUDIO && + st->codec->codec_id == AV_CODEC_ID_AC3) { + /* set PES_extension_flag */ + pes_extension = 1; + flags |= 0x01; + header_len += 3; + } len = payload_size + header_len + 3; if (private_code != 0) len++; @@ -931,6 +997,17 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st, */ *q++ = 0x00 | 0x60; } + /* For Blu-ray AC3 Audio Setting extended flags */ + if (ts->m2ts_mode && + pes_extension && + st->codec->codec_id == AV_CODEC_ID_AC3) { + flags = 0x01; /* set PES_extension_flag_2 */ + *q++ = flags; + *q++ = 0x80 | 0x01; /* marker bit + extension length */ + *q++ = 0x00 | 0x71; /* for AC3 Audio (specifically on blue-rays) */ + } + + if (private_code != 0) *q++ = private_code; is_start = 0; @@ -966,9 +1043,11 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st, memcpy(buf + TS_PACKET_SIZE - len, payload, len); payload += len; payload_size -= len; + mpegts_prefix_m2ts_header(s); avio_write(s->pb, buf, TS_PACKET_SIZE); } avio_flush(s->pb); + ts_st->prev_payload_key = key; } static int mpegts_write_packet_internal(AVFormatContext *s, AVPacket *pkt) @@ -979,8 +1058,8 @@ static int mpegts_write_packet_internal(AVFormatContext *s, AVPacket *pkt) uint8_t *data= NULL; MpegTSWrite *ts = s->priv_data; MpegTSWriteStream *ts_st = st->priv_data; - const uint64_t delay = av_rescale(s->max_delay, 90000, AV_TIME_BASE)*2; - int64_t dts = AV_NOPTS_VALUE, pts = AV_NOPTS_VALUE; + const int64_t delay = av_rescale(s->max_delay, 90000, AV_TIME_BASE)*2; + int64_t dts = pkt->dts, pts = pkt->pts; if (ts->reemit_pat_pmt) { av_log(s, AV_LOG_WARNING, "resend_headers option is deprecated, use -mpegts_flags resend_headers\n"); @@ -994,14 +1073,16 @@ static int mpegts_write_packet_internal(AVFormatContext *s, AVPacket *pkt) ts->flags &= ~MPEGTS_FLAG_REEMIT_PAT_PMT; } - if (pkt->pts != AV_NOPTS_VALUE) - pts = pkt->pts + delay; - if (pkt->dts != AV_NOPTS_VALUE) - dts = pkt->dts + delay; + if(ts->copyts < 1){ + if (pts != AV_NOPTS_VALUE) + pts += delay; + if (dts != AV_NOPTS_VALUE) + dts += delay; + } if (ts_st->first_pts_check && pts == AV_NOPTS_VALUE) { av_log(s, AV_LOG_ERROR, "first pts value must set\n"); - return AVERROR(EINVAL); + return AVERROR_INVALIDDATA; } ts_st->first_pts_check = 0; @@ -1011,7 +1092,7 @@ static int mpegts_write_packet_internal(AVFormatContext *s, AVPacket *pkt) 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"); + "no startcode found, use the h264_mp4toannexb bitstream filter (-bsf h264_mp4toannexb)\n"); return AVERROR(EINVAL); } @@ -1035,7 +1116,7 @@ static int mpegts_write_packet_internal(AVFormatContext *s, AVPacket *pkt) } else if (st->codec->codec_id == AV_CODEC_ID_AAC) { if (pkt->size < 2) { av_log(s, AV_LOG_ERROR, "AAC packet too short\n"); - return AVERROR(EINVAL); + return AVERROR_INVALIDDATA; } if ((AV_RB16(pkt->data) & 0xfff0) != 0xfff0) { int ret; @@ -1044,7 +1125,7 @@ static int mpegts_write_packet_internal(AVFormatContext *s, AVPacket *pkt) if (!ts_st->amux) { av_log(s, AV_LOG_ERROR, "AAC bitstream not in ADTS format " "and extradata missing\n"); - return AVERROR(EINVAL); + return AVERROR_INVALIDDATA; } av_init_packet(&pkt2); @@ -1067,28 +1148,36 @@ static int mpegts_write_packet_internal(AVFormatContext *s, AVPacket *pkt) } } - if (st->codec->codec_type != AVMEDIA_TYPE_AUDIO) { + if (pkt->dts != AV_NOPTS_VALUE) { + int i; + for(i=0; i<s->nb_streams; i++){ + AVStream *st2 = s->streams[i]; + MpegTSWriteStream *ts_st2 = st2->priv_data; + if( ts_st2->payload_size + && (ts_st2->payload_dts == AV_NOPTS_VALUE || dts - ts_st2->payload_dts > delay/2)){ + mpegts_write_pes(s, st2, ts_st2->payload, ts_st2->payload_size, + ts_st2->payload_pts, ts_st2->payload_dts, + ts_st2->payload_flags & AV_PKT_FLAG_KEY); + ts_st2->payload_size = 0; + } + } + } + + if (ts_st->payload_size && ts_st->payload_size + size > ts->pes_payload_size) { + mpegts_write_pes(s, st, ts_st->payload, ts_st->payload_size, + ts_st->payload_pts, ts_st->payload_dts, + ts_st->payload_flags & AV_PKT_FLAG_KEY); + ts_st->payload_size = 0; + } + + if (st->codec->codec_type != AVMEDIA_TYPE_AUDIO || size > ts->pes_payload_size) { + av_assert0(!ts_st->payload_size); // for video and subtitle, write a single pes packet mpegts_write_pes(s, st, buf, size, pts, dts, pkt->flags & AV_PKT_FLAG_KEY); av_free(data); return 0; } - if (ts_st->payload_size + size > ts->pes_payload_size) { - if (ts_st->payload_size) { - mpegts_write_pes(s, st, ts_st->payload, ts_st->payload_size, - ts_st->payload_pts, ts_st->payload_dts, - ts_st->payload_flags & AV_PKT_FLAG_KEY); - ts_st->payload_size = 0; - } - if (size > ts->pes_payload_size) { - mpegts_write_pes(s, st, buf, size, pts, dts, - pkt->flags & AV_PKT_FLAG_KEY); - av_free(data); - return 0; - } - } - if (!ts_st->payload_size) { ts_st->payload_pts = pts; ts_st->payload_dts = dts; @@ -1164,7 +1253,7 @@ AVOutputFormat ff_mpegts_muxer = { .name = "mpegts", .long_name = NULL_IF_CONFIG_SMALL("MPEG-TS (MPEG-2 Transport Stream)"), .mime_type = "video/x-mpegts", - .extensions = "ts,m2t", + .extensions = "ts,m2t,m2ts,mts", .priv_data_size = sizeof(MpegTSWrite), .audio_codec = AV_CODEC_ID_MP2, .video_codec = AV_CODEC_ID_MPEG2VIDEO, diff --git a/libavformat/mpegvideodec.c b/libavformat/mpegvideodec.c index 87e5e038c6..8568ece9a8 100644 --- a/libavformat/mpegvideodec.c +++ b/libavformat/mpegvideodec.c @@ -3,20 +3,20 @@ * Copyright (c) 2002-2003 Fabrice Bellard * Copyright (c) 2006 Michael Niedermayer <michaelni@gmx.at> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -34,7 +34,7 @@ static int mpegvideo_probe(AVProbeData *p) { uint32_t code= -1; - int pic=0, seq=0, slice=0, pspack=0, pes=0; + int pic=0, seq=0, slice=0, pspack=0, vpes=0, apes=0, res=0; int i; for(i=0; i<p->buf_size; i++){ @@ -43,15 +43,19 @@ static int mpegvideo_probe(AVProbeData *p) switch(code){ case SEQ_START_CODE: seq++; break; case PICTURE_START_CODE: pic++; break; - case SLICE_START_CODE: slice++; break; case PACK_START_CODE: pspack++; break; + case 0x1b6: + res++; break; } - if ((code & 0x1f0) == VIDEO_ID) pes++; - else if((code & 0x1e0) == AUDIO_ID) pes++; + if (code >= SLICE_START_CODE && code <= 0x1af) slice++; + if ((code & 0x1f0) == VIDEO_ID) vpes++; + else if((code & 0x1e0) == AUDIO_ID) apes++; } } - if(seq && seq*9<=pic*10 && pic*9<=slice*10 && !pspack && !pes) - return pic>1 ? AVPROBE_SCORE_MAX/2+1 : AVPROBE_SCORE_MAX/4; // +1 for .mpg + if(seq && seq*9<=pic*10 && pic*9<=slice*10 && !pspack && !apes && !res) { + if(vpes) return AVPROBE_SCORE_MAX/8; + else return pic>1 ? AVPROBE_SCORE_MAX/2+1 : AVPROBE_SCORE_MAX/4; // +1 for .mpg + } return 0; } diff --git a/libavformat/mpjpeg.c b/libavformat/mpjpeg.c index 6c8bd0ce17..916938f3e9 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)); avio_flush(s->pb); return 0; diff --git a/libavformat/msnwc_tcp.c b/libavformat/msnwc_tcp.c index b6d30feb9d..0ac1c30747 100644 --- a/libavformat/msnwc_tcp.c +++ b/libavformat/msnwc_tcp.c @@ -1,20 +1,20 @@ /* * Copyright (C) 2008 Ramiro Polla * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -89,10 +89,10 @@ static int msnwc_tcp_read_header(AVFormatContext *ctx) /* Some files start with "connected\r\n\r\n". * So skip until we find the first byte of struct size */ - while(avio_r8(pb) != HEADER_SIZE && !pb->eof_reached); + while(avio_r8(pb) != HEADER_SIZE && !url_feof(pb)); - if(pb->eof_reached) { - av_log(ctx, AV_LOG_ERROR, "Could not find valid start."); + if(url_feof(pb)) { + av_log(ctx, AV_LOG_ERROR, "Could not find valid start.\n"); return -1; } diff --git a/libavformat/mtv.c b/libavformat/mtv.c index 572288894d..622a9b8f5e 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 */ @@ -57,7 +57,7 @@ static int mtv_probe(AVProbeData *p) return 0; /* Check for nonzero in bpp and (width|height) header fields */ - if(!(p->buf[51] && AV_RL16(&p->buf[52]) | AV_RL16(&p->buf[54]))) + if(p->buf_size < 57 || !(p->buf[51] && AV_RL16(&p->buf[52]) | AV_RL16(&p->buf[54]))) return 0; /* If width or height are 0 then imagesize header field should not */ @@ -96,13 +96,19 @@ static int mtv_read_header(AVFormatContext *s) /* Calculate width and height if missing from header */ - if(!mtv->img_width) + if(mtv->img_bpp>>3){ + if(!mtv->img_width && mtv->img_height) mtv->img_width=mtv->img_segment_size / (mtv->img_bpp>>3) / mtv->img_height; - if(!mtv->img_height) + if(!mtv->img_height && mtv->img_width) mtv->img_height=mtv->img_segment_size / (mtv->img_bpp>>3) / mtv->img_width; + } + if(!mtv->img_height || !mtv->img_width){ + av_log(s, AV_LOG_ERROR, "width or height is invalid and I cannot calculate them from other information\n"); + return AVERROR(EINVAL); + } avio_skip(pb, 4); audio_subsegments = avio_rl16(pb); diff --git a/libavformat/mux.c b/libavformat/mux.c index 96eecb5a93..9bcee99ee2 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 */ @@ -29,6 +29,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" @@ -49,7 +50,7 @@ /** * @file - * muxing functions for use within Libav + * muxing functions for use within libavformat */ /* fraction handling */ @@ -102,6 +103,88 @@ static void frac_add(AVFrac *f, int64_t incr) f->num = num; } +AVRational ff_choose_timebase(AVFormatContext *s, AVStream *st, int min_precission) +{ + AVRational q; + int j; + + if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) { + q = (AVRational){1, st->codec->sample_rate}; + } else { + q = st->codec->time_base; + } + for (j=2; j<14; j+= 1+(j>2)) + while (q.den / q.num < min_precission && q.num % j == 0) + q.num /= j; + while (q.den / q.num < min_precission && q.den < (1<<24)) + q.den <<= 1; + + return q; +} + +int avformat_alloc_output_context2(AVFormatContext **avctx, AVOutputFormat *oformat, + const char *format, const char *filename) +{ + AVFormatContext *s = avformat_alloc_context(); + int ret = 0; + + *avctx = NULL; + if (!s) + goto nomem; + + if (!oformat) { + if (format) { + oformat = av_guess_format(format, NULL, NULL); + if (!oformat) { + av_log(s, AV_LOG_ERROR, "Requested output format '%s' is not a suitable output format\n", format); + ret = AVERROR(EINVAL); + goto error; + } + } else { + oformat = av_guess_format(NULL, filename, NULL); + if (!oformat) { + ret = AVERROR(EINVAL); + av_log(s, AV_LOG_ERROR, "Unable to find a suitable output format for '%s'\n", + filename); + goto error; + } + } + } + + s->oformat = oformat; + if (s->oformat->priv_data_size > 0) { + s->priv_data = av_mallocz(s->oformat->priv_data_size); + if (!s->priv_data) + goto nomem; + if (s->oformat->priv_class) { + *(const AVClass**)s->priv_data= s->oformat->priv_class; + av_opt_set_defaults(s->priv_data); + } + } else + s->priv_data = NULL; + + if (filename) + av_strlcpy(s->filename, filename, sizeof(s->filename)); + *avctx = s; + return 0; +nomem: + av_log(s, AV_LOG_ERROR, "Out of memory\n"); + ret = AVERROR(ENOMEM); +error: + avformat_free_context(s); + return ret; +} + +#if FF_API_ALLOC_OUTPUT_CONTEXT +AVFormatContext *avformat_alloc_output_context(const char *format, + AVOutputFormat *oformat, const char *filename) +{ + AVFormatContext *avctx; + int ret = avformat_alloc_output_context2(&avctx, oformat, format, filename); + return ret < 0 ? NULL : avctx; +} +#endif + static int validate_codec_tag(AVFormatContext *s, AVStream *st) { const AVCodecTag *avctag; @@ -149,6 +232,9 @@ static int init_muxer(AVFormatContext *s, AVDictionary **options) if ((ret = av_opt_set_dict(s, &tmp)) < 0) goto fail; + if (s->priv_data && s->oformat->priv_class && *(const AVClass**)s->priv_data==s->oformat->priv_class && + (ret = av_opt_set_dict(s->priv_data, &tmp)) < 0) + goto fail; // some sanity checks if (s->nb_streams == 0 && !(of->flags & AVFMT_NOSTREAMS)) { @@ -186,9 +272,9 @@ static int init_muxer(AVFormatContext *s, AVDictionary **options) ret = AVERROR(EINVAL); goto fail; } - - if (av_cmp_q(st->sample_aspect_ratio, - codec->sample_aspect_ratio)) { + if (av_cmp_q(st->sample_aspect_ratio, codec->sample_aspect_ratio) + && FFABS(av_q2d(st->sample_aspect_ratio) - av_q2d(codec->sample_aspect_ratio)) > 0.004*av_q2d(st->sample_aspect_ratio) + ) { 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, @@ -201,21 +287,23 @@ static int init_muxer(AVFormatContext *s, AVDictionary **options) } if (of->codec_tag) { - if (codec->codec_tag && - codec->codec_id == AV_CODEC_ID_RAWVIDEO && - !av_codec_get_tag(of->codec_tag, codec->codec_id) && - !validate_codec_tag(s, st)) { + if ( codec->codec_tag + && codec->codec_id == AV_CODEC_ID_RAWVIDEO + && ( av_codec_get_tag(of->codec_tag, codec->codec_id) == 0 + || av_codec_get_tag(of->codec_tag, codec->codec_id) == MKTAG('r', 'a', 'w', ' ')) + && !validate_codec_tag(s, st)) { // the current rawvideo encoding system ends up setting - // the wrong codec_tag for avi, we override it here + // the wrong codec_tag for avi/mov, we override it here codec->codec_tag = 0; } if (codec->codec_tag) { if (!validate_codec_tag(s, st)) { - char tagbuf[32]; + char tagbuf[32], cortag[32]; av_get_codec_tag_string(tagbuf, sizeof(tagbuf), codec->codec_tag); + av_get_codec_tag_string(cortag, sizeof(cortag), 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, cortag); ret = AVERROR_INVALIDDATA; goto fail; } @@ -301,6 +389,8 @@ int avformat_write_header(AVFormatContext *s, AVDictionary **options) if (s->oformat->write_header) { ret = s->oformat->write_header(s); + if (ret >= 0 && s->pb && s->pb->error < 0) + ret = s->pb->error; if (ret < 0) return ret; } @@ -314,14 +404,11 @@ int avformat_write_header(AVFormatContext *s, AVDictionary **options) //FIXME merge with compute_pkt_fields static int compute_pkt_fields2(AVFormatContext *s, AVStream *st, AVPacket *pkt) { - int delay = FFMAX(st->codec->has_b_frames, !!st->codec->max_b_frames); + int delay = FFMAX(st->codec->has_b_frames, st->codec->max_b_frames > 0); int num, den, frame_size, i; - av_dlog(s, "compute_pkt_fields2: pts:%" PRId64 " dts:%" PRId64 " cur_dts:%" PRId64 " b:%d size:%d st:%d\n", - pkt->pts, pkt->dts, st->cur_dts, delay, pkt->size, pkt->stream_index); - -/* if(pkt->pts == AV_NOPTS_VALUE && pkt->dts == AV_NOPTS_VALUE) - * return AVERROR(EINVAL);*/ + av_dlog(s, "compute_pkt_fields2: pts:%s dts:%s cur_dts:%s b:%d size:%d st:%d\n", + av_ts2str(pkt->pts), av_ts2str(pkt->dts), av_ts2str(st->cur_dts), delay, pkt->size, pkt->stream_index); /* duration field */ if (pkt->duration == 0) { @@ -336,6 +423,11 @@ static int compute_pkt_fields2(AVFormatContext *s, AVStream *st, AVPacket *pkt) //XXX/FIXME this is a temporary hack until all encoders output pts if ((pkt->pts == 0 || pkt->pts == AV_NOPTS_VALUE) && pkt->dts == AV_NOPTS_VALUE && !delay) { + static int warned; + if (!warned) { + av_log(s, AV_LOG_WARNING, "Encoder did not produce proper pts, making some up.\n"); + warned = 1; + } pkt->dts = // pkt->pts= st->cur_dts; pkt->pts = st->pts.val; @@ -356,17 +448,18 @@ static int compute_pkt_fields2(AVFormatContext *s, AVStream *st, AVPacket *pkt) ((!(s->oformat->flags & AVFMT_TS_NONSTRICT) && st->cur_dts >= pkt->dts) || st->cur_dts > pkt->dts)) { av_log(s, AV_LOG_ERROR, - "Application provided invalid, non monotonically increasing dts to muxer in stream %d: %" PRId64 " >= %" PRId64 "\n", - st->index, st->cur_dts, pkt->dts); + "Application provided invalid, non monotonically increasing dts to muxer in stream %d: %s >= %s\n", + st->index, av_ts2str(st->cur_dts), av_ts2str(pkt->dts)); return AVERROR(EINVAL); } if (pkt->dts != AV_NOPTS_VALUE && pkt->pts != AV_NOPTS_VALUE && pkt->pts < pkt->dts) { - av_log(s, AV_LOG_ERROR, "pts < dts in stream %d\n", st->index); + av_log(s, AV_LOG_ERROR, "pts (%s) < dts (%s) in stream %d\n", + av_ts2str(pkt->pts), av_ts2str(pkt->dts), st->index); return AVERROR(EINVAL); } - av_dlog(s, "av_write_frame: pts2:%"PRId64" dts2:%"PRId64"\n", - pkt->pts, pkt->dts); + av_dlog(s, "av_write_frame: pts2:%s dts2:%s\n", + av_ts2str(pkt->pts), av_ts2str(pkt->dts)); st->cur_dts = pkt->dts; st->pts.val = pkt->dts; @@ -396,8 +489,12 @@ int av_write_frame(AVFormatContext *s, AVPacket *pkt) int ret; if (!pkt) { - if (s->oformat->flags & AVFMT_ALLOW_FLUSH) - return s->oformat->write_packet(s, pkt); + if (s->oformat->flags & AVFMT_ALLOW_FLUSH) { + ret = s->oformat->write_packet(s, pkt); + if (ret >= 0 && s->pb && s->pb->error < 0) + ret = s->pb->error; + return ret; + } return 1; } @@ -407,37 +504,63 @@ int av_write_frame(AVFormatContext *s, AVPacket *pkt) return ret; ret = s->oformat->write_packet(s, pkt); + if (ret >= 0 && s->pb && s->pb->error < 0) + ret = s->pb->error; if (ret >= 0) s->streams[pkt->stream_index]->nb_frames++; return ret; } -void ff_interleave_add_packet(AVFormatContext *s, AVPacket *pkt, +#define CHUNK_START 0x1000 + +int ff_interleave_add_packet(AVFormatContext *s, AVPacket *pkt, int (*compare)(AVFormatContext *, AVPacket *, AVPacket *)) { AVPacketList **next_point, *this_pktl; + AVStream *st = s->streams[pkt->stream_index]; + int chunked = s->max_chunk_size || s->max_chunk_duration; this_pktl = av_mallocz(sizeof(AVPacketList)); + if (!this_pktl) + return AVERROR(ENOMEM); this_pktl->pkt = *pkt; pkt->destruct = NULL; // do not free original but only the copy - av_dup_packet(&this_pktl->pkt); // duplicate the packet if it uses non-alloced memory + av_dup_packet(&this_pktl->pkt); // duplicate the packet if it uses non-allocated memory 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 (*next_point) { + if (chunked) { + uint64_t max= av_rescale_q(s->max_chunk_duration, AV_TIME_BASE_Q, st->time_base); + if ( st->interleaver_chunk_size + pkt->size <= s->max_chunk_size-1U + && st->interleaver_chunk_duration + pkt->duration <= max-1U) { + st->interleaver_chunk_size += pkt->size; + st->interleaver_chunk_duration += pkt->duration; + goto next_non_null; + } else { + st->interleaver_chunk_size = + st->interleaver_chunk_duration = 0; + this_pktl->pkt.flags |= CHUNK_START; + } + } + 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: @@ -446,6 +569,7 @@ next_non_null: s->streams[pkt->stream_index]->last_in_packet_buffer = *next_point = this_pktl; + return 0; } static int ff_interleave_compare_dts(AVFormatContext *s, AVPacket *next, AVPacket *pkt) @@ -454,6 +578,16 @@ static int ff_interleave_compare_dts(AVFormatContext *s, AVPacket *next, AVPacke 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; @@ -464,27 +598,76 @@ int ff_interleave_packet_per_dts(AVFormatContext *s, AVPacket *out, AVPacket *pkt, int flush) { AVPacketList *pktl; - int stream_count = 0; - int i; + int stream_count = 0, noninterleaved_count = 0; + int64_t delta_dts_max = 0; + int i, ret; if (pkt) { - ff_interleave_add_packet(s, pkt, ff_interleave_compare_dts); + ret = ff_interleave_add_packet(s, pkt, ff_interleave_compare_dts); + if (ret < 0) + return ret; } - for (i = 0; i < s->nb_streams; i++) - stream_count += !!s->streams[i]->last_in_packet_buffer; + for (i = 0; i < s->nb_streams; i++) { + if (s->streams[i]->last_in_packet_buffer) { + ++stream_count; + } else if (s->streams[i]->codec->codec_type == AVMEDIA_TYPE_SUBTITLE) { + ++noninterleaved_count; + } + } - if (stream_count && (s->nb_streams == stream_count || flush)) { + if (s->nb_streams == stream_count) { + flush = 1; + } else if (!flush) { + for (i=0; i < s->nb_streams; i++) { + if (s->streams[i]->last_in_packet_buffer) { + int64_t delta_dts = + av_rescale_q(s->streams[i]->last_in_packet_buffer->pkt.dts, + s->streams[i]->time_base, + AV_TIME_BASE_Q) - + av_rescale_q(s->packet_buffer->pkt.dts, + s->streams[s->packet_buffer->pkt.stream_index]->time_base, + AV_TIME_BASE_Q); + delta_dts_max= FFMAX(delta_dts_max, delta_dts); + } + } + if (s->nb_streams == stream_count+noninterleaved_count && + delta_dts_max > 20*AV_TIME_BASE) { + av_log(s, AV_LOG_DEBUG, "flushing with %d noninterleaved\n", noninterleaved_count); + flush = 1; + } + } + if (stream_count && flush) { + AVStream *st; pktl = s->packet_buffer; *out = pktl->pkt; + st = s->streams[out->stream_index]; s->packet_buffer = pktl->next; if (!s->packet_buffer) s->packet_buffer_end = NULL; - if (s->streams[out->stream_index]->last_in_packet_buffer == pktl) - s->streams[out->stream_index]->last_in_packet_buffer = NULL; + if (st->last_in_packet_buffer == pktl) + st->last_in_packet_buffer = NULL; av_freep(&pktl); + + if (s->avoid_negative_ts > 0) { + if (out->dts != AV_NOPTS_VALUE) { + if (!st->mux_ts_offset && out->dts < 0) { + for (i = 0; i < s->nb_streams; i++) { + s->streams[i]->mux_ts_offset = + av_rescale_q_rnd(-out->dts, + st->time_base, + s->streams[i]->time_base, + AV_ROUND_UP); + } + } + out->dts += st->mux_ts_offset; + } + if (out->pts != AV_NOPTS_VALUE) + out->pts += st->mux_ts_offset; + } + return 1; } else { av_init_packet(out); @@ -532,8 +715,8 @@ int av_interleaved_write_frame(AVFormatContext *s, AVPacket *pkt) if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO && pkt->size == 0) return 0; - av_dlog(s, "av_interleaved_write_frame size:%d dts:%" PRId64 " pts:%" PRId64 "\n", - pkt->size, pkt->dts, pkt->pts); + av_dlog(s, "av_interleaved_write_frame size:%d dts:%s pts:%s\n", + pkt->size, av_ts2str(pkt->dts), av_ts2str(pkt->pts)); if ((ret = compute_pkt_fields2(s, st, pkt)) < 0 && !(s->oformat->flags & AVFMT_NOTIMESTAMPS)) return ret; @@ -559,6 +742,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; } } @@ -582,15 +767,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); @@ -600,3 +788,12 @@ fail: av_freep(&s->priv_data); 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; +} diff --git a/libavformat/mvi.c b/libavformat/mvi.c index c5aee5a10d..0833e11b5e 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 */ diff --git a/libavformat/mxf.c b/libavformat/mxf.c index 040d8a26c1..4a4158a1d3 100644 --- a/libavformat/mxf.c +++ b/libavformat/mxf.c @@ -2,20 +2,20 @@ * MXF * Copyright (c) 2006 SmartJog S.A., Baptiste Coudurier <baptiste dot coudurier at smartjog dot com> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -41,9 +41,11 @@ const MXFCodecUL ff_mxf_codec_uls[] = { { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x02,0x01,0x02,0x00 }, 13, AV_CODEC_ID_DVVIDEO }, /* DV25 IEC PAL */ { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x07,0x04,0x01,0x02,0x02,0x03,0x01,0x01,0x00 }, 14, AV_CODEC_ID_JPEG2000 }, /* JPEG2000 Codestream */ { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x01,0x7F,0x00,0x00,0x00 }, 13, AV_CODEC_ID_RAWVIDEO }, /* Uncompressed */ + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0A,0x04,0x01,0x02,0x01,0x01,0x02,0x01,0x00 }, 15, AV_CODEC_ID_RAWVIDEO }, /* Uncompressed 422 8-bit */ + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x71,0x00,0x00,0x00 }, 13, AV_CODEC_ID_DNXHD }, /* SMPTE VC-3/DNxHD */ { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x03,0x02,0x00,0x00 }, 14, AV_CODEC_ID_DNXHD }, /* SMPTE VC-3/DNxHD */ { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0A,0x04,0x01,0x02,0x02,0x01,0x32,0x00,0x00 }, 14, AV_CODEC_ID_H264 }, /* H.264/MPEG-4 AVC Intra */ - { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0A,0x04,0x01,0x02,0x01,0x01,0x02,0x02,0x00 }, 15, AV_CODEC_ID_V210 }, /* V210 */ + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0A,0x04,0x01,0x02,0x01,0x01,0x02,0x02,0x01 }, 16, AV_CODEC_ID_V210 }, /* V210 */ /* SoundEssenceCompression */ { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x02,0x02,0x01,0x00,0x00,0x00,0x00 }, 13, AV_CODEC_ID_PCM_S16LE }, /* Uncompressed */ { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x02,0x02,0x01,0x7F,0x00,0x00,0x00 }, 13, AV_CODEC_ID_PCM_S16LE }, @@ -104,3 +106,32 @@ int ff_mxf_decode_pixel_layout(const char pixel_layout[16], enum AVPixelFormat * return -1; } + +static const MXFSamplesPerFrame mxf_samples_per_frames[] = { + { { 1001, 24000 }, { 2002, 0, 0, 0, 0, 0 } }, // FILM 23.976 + { { 1, 24}, { 2000, 0, 0, 0, 0, 0 } }, // FILM 24 + { { 1001, 30000 }, { 1602, 1601, 1602, 1601, 1602, 0 } }, // NTSC 29.97 + { { 1001, 60000 }, { 801, 801, 801, 801, 800, 0 } }, // NTSC 59.94 + { { 1, 25 }, { 1920, 0, 0, 0, 0, 0 } }, // PAL 25 + { { 1, 50 }, { 960, 0, 0, 0, 0, 0 } }, // PAL 50 +}; + +const MXFSamplesPerFrame *ff_mxf_get_samples_per_frame(AVFormatContext *s, AVRational time_base) +{ + int i; + for (i = 0; i < FF_ARRAY_ELEMS(mxf_samples_per_frames); i++) { + if (!av_cmp_q(mxf_samples_per_frames[i].time_base, time_base)) + return &mxf_samples_per_frames[i]; + } + + // Find closest container time base for approximative codec time base like 1/29.97, 1/30, ... + for (i = 0; i < FF_ARRAY_ELEMS(mxf_samples_per_frames); i++) { + if (fabs(av_q2d(mxf_samples_per_frames[i].time_base) - av_q2d(time_base)) < 0.0001) { + av_log(s, AV_LOG_WARNING, "%d/%d input time base matched %d/%d container time base\n", + time_base.num, time_base.den, + mxf_samples_per_frames[i].time_base.num, mxf_samples_per_frames[i].time_base.den); + return &mxf_samples_per_frames[i]; + } + } + return NULL; +} diff --git a/libavformat/mxf.h b/libavformat/mxf.h index 773f30f3a2..4c751e8eef 100644 --- a/libavformat/mxf.h +++ b/libavformat/mxf.h @@ -2,25 +2,26 @@ * MXF * Copyright (c) 2006 SmartJog S.A., Baptiste Coudurier <baptiste dot coudurier at smartjog dot com> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef AVFORMAT_MXF_H #define AVFORMAT_MXF_H +#include "avformat.h" #include "libavcodec/avcodec.h" #include <stdint.h> @@ -47,11 +48,11 @@ enum MXFMetadataSetType { }; enum MXFFrameLayout { - FullFrame = 0, - MixedFields, - OneField, - SegmentedFrame, - SeparateFields + FullFrame = 0, + SeparateFields, + OneField, + MixedFields, + SegmentedFrame, }; typedef struct KLVPacket { @@ -66,11 +67,17 @@ typedef struct MXFCodecUL { int id; } MXFCodecUL; +typedef struct { + struct AVRational time_base; + int samples_per_frame[6]; +} MXFSamplesPerFrame; + extern const MXFCodecUL ff_mxf_data_definition_uls[]; extern const MXFCodecUL ff_mxf_codec_uls[]; extern const MXFCodecUL ff_mxf_pixel_format_uls[]; int ff_mxf_decode_pixel_layout(const char pixel_layout[16], enum AVPixelFormat *pix_fmt); +const MXFSamplesPerFrame *ff_mxf_get_samples_per_frame(AVFormatContext *s, AVRational time_base); #define PRINT_KEY(pc, s, x) av_dlog(pc, "%s %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n", s, \ (x)[0], (x)[1], (x)[2], (x)[3], (x)[4], (x)[5], (x)[6], (x)[7], (x)[8], (x)[9], (x)[10], (x)[11], (x)[12], (x)[13], (x)[14], (x)[15]) diff --git a/libavformat/mxfdec.c b/libavformat/mxfdec.c index 8595d72083..5fca1a89a6 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 */ @@ -48,6 +48,7 @@ #include "libavutil/aes.h" #include "libavutil/mathematics.h" #include "libavcodec/bytestream.h" +#include "libavutil/timecode.h" #include "avformat.h" #include "internal.h" #include "mxf.h" @@ -69,7 +70,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 { @@ -116,6 +117,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; @@ -262,7 +272,7 @@ static int64_t klv_decode_ber_length(AVIOContext *pb) static int mxf_read_sync(AVIOContext *pb, const uint8_t *key, unsigned size) { int i, b; - for (i = 0; i < size && !pb->eof_reached; i++) { + for (i = 0; i < size && !url_feof(pb); i++) { b = avio_r8(pb); if (b == key[0]) i = 0; @@ -312,7 +322,7 @@ static int mxf_get_d10_aes3_packet(AVIOContext *pb, AVStream *st, AVPacket *pkt, data_ptr = pkt->data; end_ptr = pkt->data + length; buf_ptr = pkt->data + 4; /* skip SMPTE 331M header */ - for (; buf_ptr + st->codec->channels*4 < end_ptr; ) { + for (; buf_ptr + st->codec->channels*4 <= end_ptr; ) { for (i = 0; i < st->codec->channels; i++) { uint32_t sample = bytestream_get_le32(&buf_ptr); if (st->codec->bits_per_coded_sample == 24) @@ -400,12 +410,14 @@ static int mxf_read_primer_pack(void *arg, AVIOContext *pb, int tag, int size, U 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; } @@ -509,7 +521,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: @@ -520,10 +532,8 @@ static int mxf_read_partition_pack(void *arg, AVIOContext *pb, int tag, int size /* only nag once */ if (!mxf->op) - av_log(mxf->fc, AV_LOG_WARNING, - "\"OPAtom\" with %u ECs - assuming %s\n", - nb_essence_containers, - op == OP1a ? "OP1a" : "OPAtom"); + av_log(mxf->fc, AV_LOG_WARNING, "\"OPAtom\" with %u ECs - assuming %s\n", + nb_essence_containers, op == OP1a ? "OP1a" : "OPAtom"); mxf->op = op; } else @@ -536,7 +546,7 @@ static int mxf_read_partition_pack(void *arg, AVIOContext *pb, int tag, int size if (partition->kag_size <= 0 || partition->kag_size > (1 << 20)) { av_log(mxf->fc, AV_LOG_WARNING, "invalid KAGSize %i - guessing ", partition->kag_size); - if (mxf->op == OPSonyOpt) + if (mxf->op == OPSONYOpt) partition->kag_size = 512; else partition->kag_size = 1; @@ -577,9 +587,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 */ @@ -617,9 +625,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 */ @@ -629,6 +635,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; @@ -662,9 +685,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 */ @@ -680,9 +701,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 */ @@ -705,29 +724,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); @@ -779,17 +782,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); @@ -802,9 +806,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 */ @@ -953,9 +955,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); @@ -970,19 +971,23 @@ static int mxf_get_sorted_table_segments(MXFContext *mxf, int *nb_sorted_segment /* sort segments by {BodySID, IndexSID, IndexStartPosition}, remove duplicates while we're at it */ for (i = 0; i < nb_segments; i++) { int best = -1, best_body_sid = -1, best_index_sid = -1, best_index_start = -1; + uint64_t best_index_duration = 0; for (j = 0; j < nb_segments; j++) { MXFIndexTableSegment *s = unsorted_segments[j]; /* Require larger BosySID, IndexSID or IndexStartPosition then the previous entry. This removes duplicates. * We want the smallest values for the keys than what we currently have, unless this is the first such entry this time around. + * If we come across an entry with the same IndexStartPosition but larger IndexDuration, then we'll prefer it over the one we currently have. */ if ((i == 0 || s->body_sid > last_body_sid || s->index_sid > last_index_sid || s->index_start_position > last_index_start) && - (best == -1 || s->body_sid < best_body_sid || s->index_sid < best_index_sid || s->index_start_position < best_index_start)) { + (best == -1 || s->body_sid < best_body_sid || s->index_sid < best_index_sid || s->index_start_position < best_index_start || + (s->index_start_position == best_index_start && s->index_duration > best_index_duration))) { best = j; best_body_sid = s->body_sid; best_index_sid = s->index_sid; best_index_start = s->index_start_position; + best_index_duration = s->index_duration; } } @@ -1123,14 +1128,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); } @@ -1235,9 +1234,7 @@ static int mxf_compute_index_tables(MXFContext *mxf) } } - if (mxf->nb_index_tables > INT_MAX / sizeof(MXFIndexTable) || - !(mxf->index_tables = av_mallocz(mxf->nb_index_tables * - sizeof(MXFIndexTable)))) { + if (!(mxf->index_tables = av_calloc(mxf->nb_index_tables, sizeof(MXFIndexTable)))) { av_log(mxf->fc, AV_LOG_ERROR, "failed to allocate index tables\n"); ret = AVERROR(ENOMEM); goto finish_decoding_index; @@ -1256,12 +1253,8 @@ static int mxf_compute_index_tables(MXFContext *mxf) for (i = j = 0; j < mxf->nb_index_tables; i += mxf->index_tables[j++].nb_segments) { MXFIndexTable *t = &mxf->index_tables[j]; - if (t->nb_segments > - (INT_MAX / sizeof(MXFIndexTableSegment *)) || - !(t->segments = av_mallocz(t->nb_segments * - sizeof(MXFIndexTableSegment*)))) { - av_log(mxf->fc, AV_LOG_ERROR, "failed to allocate IndexTableSegment" - " pointer array\n"); + if (!(t->segments = av_calloc(t->nb_segments, sizeof(MXFIndexTableSegment*)))) { + av_log(mxf->fc, AV_LOG_ERROR, "failed to allocate IndexTableSegment pointer array\n"); ret = AVERROR(ENOMEM); goto finish_decoding_index; } @@ -1305,12 +1298,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) @@ -1337,25 +1338,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; @@ -1474,33 +1498,27 @@ 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: break; case OneField: /* Every other line is stored and needs to be duplicated. */ - av_log(mxf->fc, AV_LOG_INFO, - "OneField frame layout isn't currently supported\n"); + av_log(mxf->fc, AV_LOG_INFO, "OneField frame layout isn't currently supported\n"); + break; /* The correct thing to do here is fall through, but by breaking we might be + able to decode some streams at half the vertical resolution, rather than not al all. + It's also for compatibility with the old behavior. */ + case MixedFields: break; - /* The correct thing to do here is fall through, but by - * breaking we might be able to decode some streams at half - * the vertical resolution, rather than not al all. - * It's also for compatibility with the old behavior. */ case SeparateFields: - case MixedFields: - /* Turn field height into frame height. */ - st->codec->height *= 2; + st->codec->height *= 2; /* Turn field height into frame height. */ + break; default: - av_log(mxf->fc, AV_LOG_INFO, - "Unknown frame layout type: %d\n", - descriptor->frame_layout); + av_log(mxf->fc, AV_LOG_INFO, "Unknown frame layout type: %d\n", descriptor->frame_layout); } if (st->codec->codec_id == AV_CODEC_ID_RAWVIDEO) { st->codec->pix_fmt = descriptor->pix_fmt; @@ -1522,7 +1540,8 @@ static int mxf_parse_structural_metadata(MXFContext *mxf) st->need_parsing = AVSTREAM_PARSE_HEADERS; } else if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) { container_ul = mxf_get_codec_ul(mxf_sound_essence_container_uls, essence_container_ul); - if (st->codec->codec_id == AV_CODEC_ID_NONE) + /* 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 && container_ul->id != AV_CODEC_ID_NONE)) st->codec->codec_id = container_ul->id; st->codec->channels = descriptor->channels; st->codec->bits_per_coded_sample = descriptor->bits_per_sample; @@ -1582,6 +1601,7 @@ static const MXFMetadataReadTableEntry mxf_metadata_read_table[] = { { { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x47,0x00 }, mxf_read_generic_descriptor, sizeof(MXFDescriptor), Descriptor }, /* AES3 */ { { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x3A,0x00 }, mxf_read_track, sizeof(MXFTrack), Track }, /* Static Track */ { { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x3B,0x00 }, mxf_read_track, sizeof(MXFTrack), Track }, /* Generic Track */ + { { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x14,0x00 }, mxf_read_timecode_component, sizeof(MXFTimecodeComponent), TimecodeComponent }, { { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x04,0x01,0x02,0x02,0x00,0x00 }, mxf_read_cryptographic_context, sizeof(MXFCryptoContext), CryptoContext }, { { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x02,0x01,0x01,0x10,0x01,0x00 }, mxf_read_index_table_segment, sizeof(MXFIndexTableSegment), IndexTableSegment }, { { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, NULL, 0, AnyType }, @@ -1595,7 +1615,7 @@ static int mxf_read_local_tags(MXFContext *mxf, KLVPacket *klv, MXFMetadataReadF if (!ctx) return AVERROR(ENOMEM); - while (avio_tell(pb) + 4 < klv_end && !pb->eof_reached) { + while (avio_tell(pb) + 4 < klv_end && !url_feof(pb)) { int ret; int tag = avio_rb16(pb); int size = avio_rb16(pb); /* KLV specified by 0x53 */ @@ -1626,6 +1646,9 @@ static int mxf_read_local_tags(MXFContext *mxf, KLVPacket *klv, MXFMetadataReadF /* Accept the 64k local set limit being exceeded (Avid). Don't accept * it extending past the end of the KLV though (zzuf5.mxf). */ if (avio_tell(pb) > klv_end) { + if (ctx_size) + av_free(ctx); + av_log(mxf->fc, AV_LOG_ERROR, "local tag %#04x extends past end of local set @ %#"PRIx64"\n", tag, klv->offset); @@ -1709,8 +1732,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) { @@ -1750,38 +1772,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 */ @@ -1836,7 +1826,7 @@ static int mxf_read_header(AVFormatContext *s) mxf->fc = s; mxf->run_in = avio_tell(s->pb); - while (!s->pb->eof_reached) { + while (!url_feof(s->pb)) { const MXFMetadataReadTableEntry *metadata; if (klv_read_packet(&klv, s->pb) < 0) { @@ -1855,13 +1845,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) @@ -1954,11 +1963,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) { @@ -1976,8 +1983,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; @@ -1989,16 +1995,17 @@ static int mxf_read_packet_old(AVFormatContext *s, AVPacket *pkt) KLVPacket klv; MXFContext *mxf = s->priv_data; - while (!s->pb->eof_reached) { + while (!url_feof(s->pb)) { + int ret; if (klv_read_packet(&klv, s->pb) < 0) return -1; PRINT_KEY(s, "read packet", klv.key); av_dlog(s, "size %"PRIu64" offset %#"PRIx64"\n", klv.length, klv.offset); if (IS_KLV_KEY(klv.key, mxf_encrypted_triplet_key)) { - int res = mxf_decrypt_triplet(s, pkt, &klv); - if (res < 0) { + ret = mxf_decrypt_triplet(s, pkt, &klv); + if (ret < 0) { av_log(s, AV_LOG_ERROR, "invalid encoded triplet\n"); - return -1; + return AVERROR_INVALIDDATA; } return 0; } @@ -2024,14 +2031,11 @@ 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) */ av_log_ask_for_sample(s, - "KLV for edit unit %i extends into next " - "edit unit - OPAtom misinterpreted as " - "OP1a?\n", - mxf->current_edit_unit); + "KLV for edit unit %i extends into next edit unit - OPAtom misinterpreted as OP1a?\n", + mxf->current_edit_unit); klv.length = next_ofs - avio_tell(s->pb); } @@ -2039,10 +2043,10 @@ static int mxf_read_packet_old(AVFormatContext *s, AVPacket *pkt) if (klv.key[12] == 0x06 && klv.key[13] == 0x01 && klv.key[14] == 0x10) { if (mxf_get_d10_aes3_packet(s->pb, s->streams[index], pkt, klv.length) < 0) { av_log(s, AV_LOG_ERROR, "error reading D-10 aes3 frame\n"); - return -1; + return AVERROR_INVALIDDATA; } } else { - int ret = av_get_packet(s->pb, pkt, klv.length); + ret = av_get_packet(s->pb, pkt, klv.length); if (ret < 0) return ret; } @@ -2050,18 +2054,15 @@ static int mxf_read_packet_old(AVFormatContext *s, AVPacket *pkt) pkt->pos = klv.offset; if (s->streams[index]->codec->codec_type == AVMEDIA_TYPE_VIDEO && next_ofs >= 0) { - /* mxf->current_edit_unit good - see if we have an - * index table to derive timestamps from */ + /* mxf->current_edit_unit good - see if we have an index table to derive timestamps from */ MXFIndexTable *t = &mxf->index_tables[0]; - if (mxf->nb_index_tables >= 1 && - mxf->current_edit_unit < t->nb_ptses) { + if (mxf->nb_index_tables >= 1 && mxf->current_edit_unit < t->nb_ptses) { pkt->dts = mxf->current_edit_unit + t->first_dts; pkt->pts = t->ptses[mxf->current_edit_unit]; } else if (track->intra_only) { /* intra-only -> PTS = EditUnit. - * let utils.c figure out DTS since it can be - * < PTS if low_delay = 0 (Sony IMX30) */ + * let utils.c figure out DTS since it can be < PTS if low_delay = 0 (Sony IMX30) */ pkt->pts = mxf->current_edit_unit; } } @@ -2118,8 +2119,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; if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO && t->ptses && mxf->current_edit_unit >= 0 && mxf->current_edit_unit < t->nb_ptses) { @@ -2133,7 +2134,6 @@ static int mxf_read_packet(AVFormatContext *s, AVPacket *pkt) return 0; } - static int mxf_read_close(AVFormatContext *s) { MXFContext *mxf = s->priv_data; @@ -2210,7 +2210,7 @@ static int mxf_read_seek(AVFormatContext *s, int stream_index, int64_t sample_ti int ret; MXFIndexTable *t; - if (mxf->index_tables <= 0) { + if (mxf->nb_index_tables <= 0) { if (!s->bit_rate) return AVERROR_INVALIDDATA; if (sample_time < 0) diff --git a/libavformat/mxfenc.c b/libavformat/mxfenc.c index 995e411273..c583197a45 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 */ @@ -35,15 +35,17 @@ #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 "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; @@ -69,6 +71,8 @@ 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 @@ -88,6 +92,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 } }; @@ -164,6 +170,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 Unknwon + { { 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 }, @@ -171,6 +272,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; @@ -184,10 +286,9 @@ typedef struct MXFContext { unsigned body_partitions_count; int last_key_index; ///< index of last key frame uint64_t duration; + AVTimecode tc; ///< timecode context AVStream *timecode_track; int timecode_base; ///< rounded time code base (25 or 30) - int timecode_start; ///< frame number computed from mpeg-2 gop header timecode - int timecode_drop_frame; ///< time code use drop frame method frop mpeg-2 essence gop header int edit_unit_byte_count; ///< fixed edit unit byte count uint64_t body_offset; uint32_t instance_number; @@ -280,6 +381,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 */ @@ -427,18 +529,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) @@ -448,7 +557,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); @@ -477,7 +586,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 @@ -500,7 +609,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; @@ -665,7 +774,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); @@ -673,7 +782,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) @@ -721,7 +830,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); @@ -785,8 +894,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); @@ -809,7 +921,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); @@ -821,9 +933,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; @@ -848,6 +960,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) @@ -1054,8 +1172,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 @@ -1179,7 +1297,7 @@ static void mxf_write_klv_fill(AVFormatContext *s) klv_encode_ber4_length(s->pb, pad); for (; pad; pad--) avio_w8(s->pb, 0); - assert(!(avio_tell(s->pb) & (KAG_SIZE-1))); + av_assert1(!(avio_tell(s->pb) & (KAG_SIZE-1))); } } @@ -1215,7 +1333,7 @@ static void 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 @@ -1276,6 +1394,155 @@ static void mxf_write_partition(AVFormatContext *s, int bodysid, avio_flush(pb); } +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; + unsigned 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 ((i = ff_dnxhd_get_cid_table(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; + } + + frame_size = ff_dnxhd_cid_table[i].frame_size; + 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 @@ -1313,7 +1580,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; @@ -1325,6 +1591,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 @@ -1333,21 +1601,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) { @@ -1409,11 +1662,12 @@ static void mxf_gen_umid(AVFormatContext *s) 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; @@ -1425,35 +1679,44 @@ static int mxf_write_header(AVFormatContext *s) return AVERROR(ENOMEM); st->priv_data = sc; + if ((i == 0) ^ (st->codec->codec_type == AVMEDIA_TYPE_VIDEO)) { + av_log(s, AV_LOG_ERROR, "there must be exactly one video stream and it must be the first one\n"); + return -1; + } + if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) { - if (i != 0) { - av_log(s, AV_LOG_ERROR, "video stream must be first track\n"); - return -1; - } - if (fabs(av_q2d(st->codec->time_base) - 1/25.0) < 0.0001) { - samples_per_frame = PAL_samples_per_frame; - mxf->time_base = (AVRational){ 1, 25 }; - mxf->timecode_base = 25; - } else if (fabs(av_q2d(st->codec->time_base) - 1001/30000.0) < 0.0001) { - samples_per_frame = NTSC_samples_per_frame; - mxf->time_base = (AVRational){ 1001, 30000 }; - mxf->timecode_base = 30; - } else { - av_log(s, AV_LOG_ERROR, "unsupported video frame rate\n"); - return -1; + AVRational rate, tbc = st->codec->time_base; + // Default component depth to 8 + sc->component_depth = 8; + mxf->timecode_base = (tbc.den + tbc.num/2) / tbc.num; + spf = ff_mxf_get_samples_per_frame(s, tbc); + if (!spf) { + av_log(s, AV_LOG_ERROR, "Unsupported video frame rate %d/%d\n", + tbc.den, tbc.num); + return AVERROR(EINVAL); } + mxf->time_base = spf->time_base; + rate = av_inv_q(mxf->time_base); avpriv_set_pts_info(st, 64, mxf->time_base.num, mxf->time_base.den); + if (!tcr) + tcr = av_dict_get(st->metadata, "timecode", NULL, 0); + if (tcr) + ret = av_timecode_init_from_string(&mxf->tc, rate, tcr->value, s); + else + ret = av_timecode_init(&mxf->tc, rate, 0, 0, s); + if (ret < 0) + return ret; if (s->oformat == &ff_mxf_d10_muxer) { - if (st->codec->bit_rate == 50000000) + if (st->codec->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 (st->codec->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 (st->codec->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; } @@ -1462,7 +1725,7 @@ static int mxf_write_header(AVFormatContext *s) mxf->edit_unit_byte_count += 16 + 4 + (uint64_t)st->codec->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) { @@ -1516,7 +1779,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)) @@ -1533,10 +1799,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; @@ -1545,24 +1811,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 ff_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; @@ -1570,7 +1818,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); @@ -1579,7 +1827,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 { @@ -1591,7 +1839,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 = ff_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); @@ -1626,7 +1874,7 @@ static void mxf_write_d10_video_packet(AVFormatContext *s, AVStream *st, AVPacke klv_encode_ber4_length(s->pb, pad); for (; pad; pad--) avio_w8(s->pb, 0); - assert(!(avio_tell(s->pb) & (KAG_SIZE-1))); + 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--) @@ -1688,6 +1936,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) { @@ -1753,7 +2011,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 @@ -1856,7 +2114,7 @@ static int mxf_interleave_get_packet(AVFormatContext *s, AVPacket *out, AVPacket } *out = pktl->pkt; - av_dlog(s, "out st:%d dts:%lld\n", (*out).stream_index, (*out).dts); + av_dlog(s, "out st:%d dts:%"PRId64"\n", (*out).stream_index, (*out).dts); s->packet_buffer = pktl->next; if(s->streams[pktl->pkt.stream_index]->last_in_packet_buffer == pktl) s->streams[pktl->pkt.stream_index]->last_in_packet_buffer= NULL; diff --git a/libavformat/mxg.c b/libavformat/mxg.c index 09653ee7b7..be7e6db1c4 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 */ @@ -104,7 +104,7 @@ static int mxg_update_cache(AVFormatContext *s, unsigned int cache_size) /* reallocate internal buffer */ if (current_pos > current_pos + cache_size) return AVERROR(ENOMEM); - if (mxg->soi_ptr) soi_pos = mxg->soi_ptr - mxg->buffer; + 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); @@ -131,7 +131,7 @@ static int mxg_read_packet(AVFormatContext *s, AVPacket *pkt) uint8_t *startmarker_ptr, *end, *search_end, marker; MXGContext *mxg = s->priv_data; - while (!s->pb->eof_reached && !s->pb->error){ + while (!url_feof(s->pb) && !s->pb->error){ if (mxg->cache_size <= OVERREAD_SIZE) { /* update internal buffer */ ret = mxg_update_cache(s, DEFAULT_PACKET_SIZE + OVERREAD_SIZE); diff --git a/libavformat/ncdec.c b/libavformat/ncdec.c index 40d8dac3a7..8cb7ed4bd4 100644 --- a/libavformat/ncdec.c +++ b/libavformat/ncdec.c @@ -3,20 +3,20 @@ * Copyright (c) 2009 Nicolas Martin (martinic at iro dot umontreal dot ca) * Edouard Auvinet * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -67,7 +67,7 @@ static int nc_read_packet(AVFormatContext *s, AVPacket *pkt) uint32_t state=-1; while (state != NC_VIDEO_FLAG) { - if (s->pb->eof_reached) + if (url_feof(s->pb)) return AVERROR(EIO); state = (state<<8) + avio_r8(s->pb); } diff --git a/libavformat/network.c b/libavformat/network.c index fe17bdf2eb..6e924bed66 100644 --- a/libavformat/network.c +++ b/libavformat/network.c @@ -18,9 +18,12 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "libavutil/avutil.h" #include "network.h" #include "libavcodec/internal.h" #include "libavutil/mem.h" +#include "url.h" +#include "libavutil/time.h" #if HAVE_THREADS #if HAVE_PTHREADS @@ -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) { + ret = ff_network_wait_fd(fd, write); + if (ret != AVERROR(EAGAIN)) + return ret; + if (ff_check_interrupt(int_cb)) + return AVERROR_EXIT; + if (timeout) { + if (!wait_start) + wait_start = av_gettime(); + else if (av_gettime() - wait_start > timeout) + return AVERROR(ETIMEDOUT); + } + } +} + void ff_network_close(void) { #if HAVE_WINSOCK2_H diff --git a/libavformat/network.h b/libavformat/network.h index 51607673e2..f8b4dee50e 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" #if HAVE_UNISTD_H #include <unistd.h> @@ -81,6 +82,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 after 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/nsvdec.c b/libavformat/nsvdec.c index 654a968b39..3015d84491 100644 --- a/libavformat/nsvdec.c +++ b/libavformat/nsvdec.c @@ -1,21 +1,21 @@ /* * NSV demuxer - * Copyright (c) 2004 The Libav 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 */ @@ -25,6 +25,7 @@ #include "internal.h" #include "riff.h" #include "libavutil/dict.h" +#include "libavutil/intreadwrite.h" //#define DEBUG_DUMP_INDEX // XXX dumbdriving-271.nsv breaks with it commented!! #define CHECK_SUBSEQUENT_NSVS @@ -73,11 +74,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 */ /* @@ -103,8 +100,8 @@ struct NSVs_header { uint32_t chunk_tag; /* 'NSVs' */ uint32_t v4cc; /* or 'NONE' */ uint32_t a4cc; /* or 'NONE' */ - uint16_t vwidth; /* assert(vwidth%16==0) */ - uint16_t vheight; /* assert(vheight%16==0) */ + uint16_t vwidth; /* av_assert(vwidth%16==0) */ + uint16_t vheight; /* av_assert(vheight%16==0) */ uint8_t framerate; /* value = (framerate&0x80)?frtable[frameratex0x7f]:framerate */ uint16_t unknown; }; @@ -196,6 +193,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') }, @@ -209,6 +207,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 }, @@ -237,7 +236,7 @@ static int nsv_resync(AVFormatContext *s) //nsv->state = NSV_UNSYNC; for (i = 0; i < NSV_MAX_RESYNC; i++) { - if (pb->eof_reached) { + if (url_feof(pb)) { av_dlog(s, "NSV EOF\n"); nsv->state = NSV_UNSYNC; return -1; @@ -304,7 +303,7 @@ static int nsv_parse_NSVf_header(AVFormatContext *s) table_entries_used = avio_rl32(pb); av_dlog(s, "NSV NSVf info-strings size: %d, table entries: %d, bis %d\n", strings_size, table_entries, table_entries_used); - if (pb->eof_reached) + if (url_feof(pb)) return -1; av_dlog(s, "NSV got header; filepos %"PRId64"\n", avio_tell(pb)); @@ -341,7 +340,7 @@ static int nsv_parse_NSVf_header(AVFormatContext *s) } av_free(strings); } - if (pb->eof_reached) + if (url_feof(pb)) return -1; av_dlog(s, "NSV got infos; filepos %"PRId64"\n", avio_tell(pb)); @@ -392,7 +391,7 @@ static int nsv_parse_NSVf_header(AVFormatContext *s) avio_seek(pb, nsv->base_offset + size, SEEK_SET); /* required for dumbdriving-271.nsv (2 extra bytes) */ - if (pb->eof_reached) + if (url_feof(pb)) return -1; nsv->state = NSV_HAS_READ_NSVF; return 0; @@ -574,7 +573,7 @@ static int nsv_read_chunk(AVFormatContext *s, int fill_header) return 0; //-1; /* hey! eat what you've in your plate first! */ null_chunk_retry: - if (pb->eof_reached) + if (url_feof(pb)) return -1; for (i = 0; i < NSV_MAX_RESYNC_TRIES && nsv->state < NSV_FOUND_NSVS && !err; i++) @@ -609,7 +608,7 @@ null_chunk_retry: vsize -= auxsize + sizeof(uint16_t) + sizeof(uint32_t); /* that's becoming braindead */ } - if (pb->eof_reached) + if (url_feof(pb)) return -1; if (!vsize && !asize) { nsv->state = NSV_UNSYNC; @@ -654,7 +653,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 */ @@ -759,10 +761,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 */ @@ -774,19 +774,14 @@ static int nsv_probe(AVProbeData *p) /* seems the servers don't bother starting clean chunks... */ /* sometimes even the first header is at 9KB or something :^) */ for (i = 1; i < p->buf_size - 3; i++) { - if (p->buf[i+0] == 'N' && p->buf[i+1] == 'S' && - p->buf[i+2] == 'V' && p->buf[i+3] == 's') { - score = AVPROBE_SCORE_MAX/5; + if (AV_RL32(p->buf + i) == AV_RL32("NSVs")) { /* Get the chunk size and check if at the end we are getting 0xBEEF */ - auxcount = p->buf[i+19]; - vsize = p->buf[i+20] | p->buf[i+21] << 8; - asize = p->buf[i+22] | p->buf[i+23] << 8; - vsize = (vsize << 4) | (auxcount >> 4); - if ((asize + vsize + i + 23) < p->buf_size - 2) { - if (p->buf[i+23+asize+vsize+1] == 0xEF && - p->buf[i+23+asize+vsize+2] == 0xBE) - return AVPROBE_SCORE_MAX-20; - } + int vsize = AV_RL24(p->buf+i+19) >> 4; + int asize = AV_RL16(p->buf+i+22); + int offset = i + 23 + asize + vsize + 1; + if (offset <= p->buf_size - 2 && AV_RL16(p->buf + offset) == 0xBEEF) + return 4*AVPROBE_SCORE_MAX/5; + score = AVPROBE_SCORE_MAX/5; } } /* so we'll have more luck on extension... */ diff --git a/libavformat/nullenc.c b/libavformat/nullenc.c index 829f2a841e..7da297b63e 100644 --- a/libavformat/nullenc.c +++ b/libavformat/nullenc.c @@ -2,20 +2,20 @@ * RAW null muxer * Copyright (c) 2002 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/nut.c b/libavformat/nut.c index e367d1c649..51e4d38394 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 */ @@ -30,6 +30,7 @@ const AVCodecTag ff_nut_subtitle_tags[] = { { 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 } }; @@ -47,9 +48,13 @@ const AVCodecTag ff_nut_video_tags[] = { { 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( 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') }, @@ -71,12 +76,28 @@ const AVCodecTag ff_nut_video_tags[] = { { AV_CODEC_ID_RAWVIDEO, MKTAG('R', 'G', 'B', 48 ) }, { AV_CODEC_ID_RAWVIDEO, MKTAG(48 , 'B', 'G', 'R') }, { AV_CODEC_ID_RAWVIDEO, MKTAG(48 , 'R', 'G', 'B') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('B', 'R', 'A', 64 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('R', 'B', 'A', 64 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(64 , 'B', 'R', 'A') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(64 , 'R', 'B', 'A') }, { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '3', 11 , 10 ) }, { AV_CODEC_ID_RAWVIDEO, MKTAG(10 , 11 , '3', 'Y') }, { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '3', 10 , 10 ) }, { AV_CODEC_ID_RAWVIDEO, MKTAG(10 , 10 , '3', 'Y') }, { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '3', 0 , 10 ) }, { AV_CODEC_ID_RAWVIDEO, MKTAG(10 , 0 , '3', 'Y') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '3', 11 , 12 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(12 , 11 , '3', 'Y') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '3', 10 , 12 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(12 , 10 , '3', 'Y') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '3', 0 , 12 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(12 , 0 , '3', 'Y') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '3', 11 , 14 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(14 , 11 , '3', 'Y') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '3', 10 , 14 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(14 , 10 , '3', 'Y') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '3', 0 , 14 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(14 , 0 , '3', 'Y') }, { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '1', 0 , 16 ) }, { AV_CODEC_ID_RAWVIDEO, MKTAG(16 , 0 , '1', 'Y') }, { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '3', 11 , 16 ) }, @@ -86,6 +107,8 @@ const AVCodecTag ff_nut_video_tags[] = { { 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_NONE , 0 } }; @@ -116,8 +139,8 @@ const AVCodecTag ff_nut_audio_tags[] = { }; 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, 0 + ff_nut_video_tags, ff_nut_subtitle_tags, + ff_codec_bmp_tags, ff_codec_wav_tags, ff_nut_audio_tags, 0 }; void ff_nut_reset_ts(NUTContext *nut, AVRational time_base, int64_t val){ @@ -132,7 +155,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; } @@ -149,6 +172,8 @@ void ff_nut_add_sp(NUTContext *nut, int64_t pos, int64_t back_ptr, int64_t ts){ Syncpoint *sp= av_mallocz(sizeof(Syncpoint)); struct AVTreeNode *node = av_tree_node_alloc(); + 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 a91a109d40..673f12f4b2 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 */ @@ -79,6 +79,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 { @@ -101,6 +102,9 @@ typedef struct NUTContext { int header_count; AVRational *time_base; struct AVTreeNode *syncpoints; + int sp_count; + int64_t max_pts; + AVRational *max_pts_tb; } NUTContext; extern const AVCodecTag ff_nut_subtitle_tags[]; diff --git a/libavformat/nutdec.c b/libavformat/nutdec.c index 9b1891f92e..1ba79ebcbf 100644 --- a/libavformat/nutdec.c +++ b/libavformat/nutdec.c @@ -3,24 +3,25 @@ * Copyright (c) 2004-2006 Michael Niedermayer * Copyright (c) 2003 Alex Beregszaszi * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "libavutil/avstring.h" +#include "libavutil/avassert.h" #include "libavutil/bswap.h" #include "libavutil/dict.h" #include "libavutil/mathematics.h" @@ -28,11 +29,11 @@ #include "avio_internal.h" #include "nut.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); @@ -96,8 +97,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, @@ -129,7 +140,7 @@ static uint64_t find_any_startcode(AVIOContext *bc, int64_t pos) /* Note, this may fail if the stream is not seekable, but that should * not matter, as in this case we simply start where we currently are */ avio_seek(bc, pos, SEEK_SET); - while (!bc->eof_reached) { + while (!url_feof(bc)) { state = (state << 8) | avio_r8(bc); if ((state >> 56) != 'N') continue; @@ -290,7 +301,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; @@ -310,7 +321,7 @@ static int decode_main_header(NUTContext *nut) avio_read(bc, hdr, nut->header_len[i]); nut->header[i] = hdr; } - assert(nut->header_len[0] == 0); + av_assert0(nut->header_len[0] == 0); } if (skip_reserved(bc, end) || ffio_get_checksum(bc)) { @@ -506,6 +517,12 @@ 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); + continue; + } + if (metadata && av_strcasecmp(name, "Uses") && av_strcasecmp(name, "Depends") && av_strcasecmp(name, "Replaces")) av_dict_set(metadata, name, str_value, 0); @@ -523,7 +540,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; nut->last_syncpoint_pos = avio_tell(bc) - 8; @@ -543,13 +561,32 @@ static int decode_syncpoint(NUTContext *nut, int64_t *ts, int64_t *back_ptr) return -1; } - *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; ff_nut_add_sp(nut, nut->last_syncpoint_pos, *back_ptr, *ts); 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; + + int64_t pos = FFMAX(0, filesize - 2*nut->max_distance); + for(;;){ + int64_t ts = nut_read_timestamp(s, -1, &pos, INT64_MAX); + if(ts < 0) + break; + duration = FFMAX(duration, ts); + pos++; + } + 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; @@ -561,10 +598,16 @@ static int find_and_decode_index(NUTContext *nut) int8_t *has_keyframe; int ret = -1; + 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 -1; } @@ -594,7 +637,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--) @@ -614,7 +657,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); @@ -701,7 +744,7 @@ static int nut_read_header(AVFormatContext *s) find_and_decode_index(nut); avio_seek(bc, orig_pos, SEEK_SET); } - assert(nut->next_startcode == SYNCPOINT_STARTCODE); + av_assert0(nut->next_startcode == SYNCPOINT_STARTCODE); ff_metadata_conv_ctx(s, NULL, ff_nut_metadata_conv); @@ -746,7 +789,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) @@ -811,7 +854,8 @@ static int decode_frame(NUTContext *nut, AVPacket *pkt, int frame_code) return 1; } - av_new_packet(pkt, size + nut->header_len[header_idx]); + if (av_new_packet(pkt, size + nut->header_len[header_idx]) < 0) + return AVERROR(ENOMEM); memcpy(pkt->data, nut->header[header_idx], nut->header_len[header_idx]); pkt->pos = avio_tell(bc); // FIXME avio_read(bc, pkt->data + nut->header_len[header_idx], size); @@ -840,7 +884,7 @@ static int nut_read_packet(AVFormatContext *s, AVPacket *pkt) pos -= 8; } else { frame_code = avio_r8(bc); - if (bc->eof_reached) + if (url_feof(bc)) return -1; if (frame_code == 'N') { tmp = frame_code; @@ -894,21 +938,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; - - assert(0); + av_assert0(stream_index == -1); + return pts; } static int read_seek(AVFormatContext *s, int stream_index, @@ -925,6 +966,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; @@ -957,7 +1000,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); @@ -989,6 +1032,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 581a202f11..08c049ab34 100644 --- a/libavformat/nutenc.c +++ b/libavformat/nutenc.c @@ -2,20 +2,20 @@ * nut muxer * Copyright (c) 2004-2007 Michael Niedermayer * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -23,6 +23,7 @@ #include "libavutil/mathematics.h" #include "libavutil/tree.h" #include "libavutil/dict.h" +#include "libavutil/avassert.h" #include "libavcodec/mpegaudiodata.h" #include "nut.h" #include "internal.h" @@ -59,12 +60,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); @@ -98,8 +96,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]; @@ -168,6 +165,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) { @@ -183,29 +193,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; @@ -228,6 +241,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; @@ -242,15 +257,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; @@ -281,8 +294,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); @@ -341,17 +353,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; @@ -380,22 +387,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++) { @@ -409,30 +408,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); @@ -508,20 +494,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) { @@ -567,6 +561,51 @@ static int write_chapter(NUTContext *nut, AVIOContext *bc, int id) return 0; } +static int write_index(NUTContext *nut, AVIOContext *bc) { + int i; + Syncpoint dummy= { .pos= 0 }; + Syncpoint *next_node[2] = { NULL }; + int64_t startpos = avio_tell(bc); + int64_t payload_size; + + put_tt(nut, nut->max_pts_tb, bc, nut->max_pts); + + ff_put_v(bc, nut->sp_count); + + for (i=0; i<nut->sp_count; i++) { + av_tree_find(nut->syncpoints, &dummy, (void *) ff_nut_sp_pos_cmp, (void**)next_node); + ff_put_v(bc, (next_node[1]->pos >> 4) - (dummy.pos>>4)); + dummy.pos = next_node[1]->pos; + } + + for (i=0; i<nut->avf->nb_streams; i++) { + StreamContext *nus= &nut->stream[i]; + int64_t last_pts= -1; + int j, k; + for (j=0; j<nut->sp_count; j++) { + int flag = (nus->keyframe_pts[j] != AV_NOPTS_VALUE) ^ (j+1 == nut->sp_count); + int n = 0; + for (; j<nut->sp_count && (nus->keyframe_pts[j] != AV_NOPTS_VALUE) == flag; j++) + n++; + + ff_put_v(bc, 1 + 2*flag + 4*n); + for (k= j - n; k<=j && k<nut->sp_count; k++) { + if (nus->keyframe_pts[k] == AV_NOPTS_VALUE) + continue; + av_assert0(nus->keyframe_pts[k] > last_pts); + ff_put_v(bc, nus->keyframe_pts[k] - last_pts); + last_pts = nus->keyframe_pts[k]; + } + } + } + + payload_size = avio_tell(bc) - startpos + 8 + 4; + + avio_wb64(bc, 8 + payload_size + av_log2(payload_size) / 7 + 1 + 4*(payload_size > 4096)); + + return 0; +} + static int write_headers(AVFormatContext *avctx, AVIOContext *bc) { NUTContext *nut = avctx->priv_data; @@ -640,12 +679,11 @@ static int nut_write_header(AVFormatContext *s) nut->avf = s; - nut->stream = av_mallocz(sizeof(StreamContext) * s->nb_streams); - if (s->nb_chapters) - nut->chapter = av_mallocz(sizeof(ChapterContext) * s->nb_chapters); - nut->time_base = av_mallocz(sizeof(AVRational) * (s->nb_streams + - s->nb_chapters)); - if (!nut->stream || (s->nb_chapters && !nut->chapter) || !nut->time_base) { + nut->stream = av_mallocz(sizeof(StreamContext ) * s->nb_streams); + 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 || !nut->chapter || !nut->time_base) { av_freep(&nut->stream); av_freep(&nut->chapter); av_freep(&nut->time_base); @@ -656,8 +694,13 @@ static int nut_write_header(AVFormatContext *s) AVStream *st = s->streams[i]; int ssize; AVRational time_base; - ff_parse_specific_params(st->codec, &time_base.den, &ssize, - &time_base.num); + ff_parse_specific_params(st->codec, &time_base.den, &ssize, &time_base.num); + + if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO && st->codec->sample_rate) { + time_base = (AVRational) {1, st->codec->sample_rate}; + } else { + time_base = ff_choose_timebase(s, st, 48000); + } avpriv_set_pts_info(st, 64, time_base.num, time_base.den); @@ -694,7 +737,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); @@ -702,9 +745,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; } @@ -761,14 +805,16 @@ static int nut_write_packet(AVFormatContext *s, AVPacket *pkt) AVIOContext *bc = s->pb, *dyn_bc; FrameCode *fc; int64_t coded_pts; - int best_length, frame_code, flags, needed_flags, i, header_idx, - best_header_idx; + int best_length, frame_code, flags, needed_flags, i, header_idx; + int best_header_idx; int key_frame = !!(pkt->flags & AV_PKT_FLAG_KEY); int store_sp = 0; int ret; - if (pkt->pts < 0) - return -1; + if (pkt->pts < 0) { + av_log(s, AV_LOG_ERROR, "Invalid negative packet pts %"PRId64" in input\n", pkt->pts); + return AVERROR(EINVAL); + } if (1LL << (20 + 3 * nut->header_count) <= avio_tell(bc)) write_headers(s, bc); @@ -776,8 +822,7 @@ static int nut_write_packet(AVFormatContext *s, AVPacket *pkt) if (key_frame && !(nus->last_flags & FLAG_KEY)) store_sp = 1; - if (pkt->size + 30 /*FIXME check*/ + avio_tell(bc) >= - nut->last_syncpoint_pos + nut->max_distance) + if (pkt->size + 30 /*FIXME check*/ + avio_tell(bc) >= nut->last_syncpoint_pos + nut->max_distance) store_sp = 1; //FIXME: Ensure store_sp is 1 in the first place. @@ -811,8 +856,19 @@ static int nut_write_packet(AVFormatContext *s, AVPacket *pkt) put_packet(nut, bc, dyn_bc, 1, SYNCPOINT_STARTCODE); ff_nut_add_sp(nut, nut->last_syncpoint_pos, 0 /*unused*/, pkt->dts); + + if ((1ll<<60) % nut->sp_count == 0) + for (i=0; i<s->nb_streams; i++) { + int j; + StreamContext *nus = &nut->stream[i]; + nus->keyframe_pts = av_realloc(nus->keyframe_pts, 2*nut->sp_count*sizeof(*nus->keyframe_pts)); + if (!nus->keyframe_pts) + return AVERROR(ENOMEM); + for (j=nut->sp_count == 1 ? 0 : nut->sp_count; j<2*nut->sp_count; j++) + nus->keyframe_pts[j] = AV_NOPTS_VALUE; + } } - assert(nus->last_pts != AV_NOPTS_VALUE); + av_assert0(nus->last_pts != AV_NOPTS_VALUE); coded_pts = pkt->pts & ((1 << nus->msb_pts_shift) - 1); if (ff_lsb2full(nus, coded_pts) != pkt->pts) @@ -856,9 +912,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; } @@ -877,7 +932,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); @@ -889,27 +944,20 @@ static int nut_write_packet(AVFormatContext *s, AVPacket *pkt) ff_put_v(bc, (flags ^ needed_flags) & ~(FLAG_CODED)); flags = needed_flags; } - if (flags & FLAG_STREAM_ID) - ff_put_v(bc, pkt->stream_index); - if (flags & FLAG_CODED_PTS) - ff_put_v(bc, coded_pts); - if (flags & FLAG_SIZE_MSB) - ff_put_v(bc, pkt->size / fc->size_mul); - if (flags & FLAG_HEADER_IDX) - ff_put_v(bc, header_idx = best_header_idx); - - if (flags & FLAG_CHECKSUM) - avio_wl32(bc, ffio_get_checksum(bc)); - else - ffio_get_checksum(bc); + if (flags & FLAG_STREAM_ID) ff_put_v(bc, pkt->stream_index); + if (flags & FLAG_CODED_PTS) ff_put_v(bc, coded_pts); + if (flags & FLAG_SIZE_MSB ) ff_put_v(bc, pkt->size / fc->size_mul); + if (flags & FLAG_HEADER_IDX) ff_put_v(bc, header_idx = best_header_idx); - avio_write(bc, pkt->data + nut->header_len[header_idx], - pkt->size - nut->header_len[header_idx]); + if (flags & FLAG_CHECKSUM) avio_wl32(bc, ffio_get_checksum(bc)); + else ffio_get_checksum(bc); + + avio_write(bc, pkt->data + nut->header_len[header_idx], pkt->size - nut->header_len[header_idx]); nus->last_flags = flags; nus->last_pts = pkt->pts; //FIXME just store one per syncpoint - if (flags & FLAG_KEY) + if (flags & FLAG_KEY) { av_add_index_entry( s->streams[pkt->stream_index], nut->last_syncpoint_pos, @@ -917,6 +965,14 @@ static int nut_write_packet(AVFormatContext *s, AVPacket *pkt) 0, 0, AVINDEX_KEYFRAME); + if (nus->keyframe_pts && nus->keyframe_pts[nut->sp_count] == AV_NOPTS_VALUE) + nus->keyframe_pts[nut->sp_count] = pkt->pts; + } + + if (!nut->max_pts_tb || av_compare_ts(nut->max_pts, *nut->max_pts_tb, pkt->pts, *nus->time_base) < 0) { + nut->max_pts = pkt->pts; + nut->max_pts_tb = nus->time_base; + } return 0; } @@ -924,12 +980,22 @@ static int nut_write_packet(AVFormatContext *s, AVPacket *pkt) static int nut_write_trailer(AVFormatContext *s) { NUTContext *nut = s->priv_data; - AVIOContext *bc = s->pb; + AVIOContext *bc = s->pb, *dyn_bc; + int i, ret; while (nut->header_count < 3) write_headers(s, bc); + ret = avio_open_dyn_buf(&dyn_bc); + if (ret >= 0) { + write_index(nut, dyn_bc); + put_packet(nut, bc, dyn_bc, 1, INDEX_STARTCODE); + } + ff_nut_free_sp(nut); + for (i=0; i<s->nb_streams; i++) + av_freep(&nut->stream[i].keyframe_pts); + av_freep(&nut->stream); av_freep(&nut->chapter); av_freep(&nut->time_base); diff --git a/libavformat/nuv.c b/libavformat/nuv.c index fce96b420c..c192faad98 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 */ @@ -62,7 +62,7 @@ static int get_codec_data(AVIOContext *pb, AVStream *vst, nuv_frametype frametype; if (!vst && !myth) return 1; // no codec data needed - while (!pb->eof_reached) { + while (!url_feof(pb)) { int size, subtype; frametype = avio_r8(pb); switch (frametype) { @@ -200,7 +200,7 @@ static int nuv_packet(AVFormatContext *s, AVPacket *pkt) { uint8_t hdr[HDRSIZE]; nuv_frametype frametype; int ret, size; - while (!pb->eof_reached) { + while (!url_feof(pb)) { int copyhdrsize = ctx->rtjpg_video ? HDRSIZE : 0; uint64_t pos = avio_tell(pb); ret = avio_read(pb, hdr, HDRSIZE); @@ -223,10 +223,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); @@ -262,6 +261,81 @@ static int nuv_packet(AVFormatContext *s, AVPacket *pkt) { return AVERROR(EIO); } +/** + * \brief looks for the string RTjjjjjjjjjj in the stream too resync reading + * \return 1 if the syncword is found 0 otherwise. + */ +static int nuv_resync(AVFormatContext *s, int64_t pos_limit) { + AVIOContext *pb = s->pb; + uint32_t tag = 0; + while(!url_feof(pb) && avio_tell(pb) < pos_limit) { + tag = (tag << 8) | avio_r8(pb); + if (tag == MKBETAG('R','T','j','j') && + (tag = avio_rb32(pb)) == MKBETAG('j','j','j','j') && + (tag = avio_rb32(pb)) == MKBETAG('j','j','j','j')) + return 1; + } + return 0; +} + +/** + * \brief attempts to read a timestamp from stream at the given stream position + * \return timestamp if successful and AV_NOPTS_VALUE if failure + */ +static int64_t nuv_read_dts(AVFormatContext *s, int stream_index, + int64_t *ppos, int64_t pos_limit) +{ + NUVContext *ctx = s->priv_data; + AVIOContext *pb = s->pb; + uint8_t hdr[HDRSIZE]; + nuv_frametype frametype; + int size, key, idx; + int64_t pos, dts; + + if (avio_seek(pb, *ppos, SEEK_SET) < 0) + return AV_NOPTS_VALUE; + + if (!nuv_resync(s, pos_limit)) + return AV_NOPTS_VALUE; + + while (!url_feof(pb) && avio_tell(pb) < pos_limit) { + if (avio_read(pb, hdr, HDRSIZE) < HDRSIZE) + return AV_NOPTS_VALUE; + frametype = hdr[0]; + size = PKTSIZE(AV_RL32(&hdr[8])); + switch (frametype) { + case NUV_SEEKP: + break; + case NUV_AUDIO: + case NUV_VIDEO: + if (frametype == NUV_VIDEO) { + idx = ctx->v_id; + key = hdr[2] == 0; + } else { + idx = ctx->a_id; + key = 1; + } + if (stream_index == idx) { + + pos = avio_tell(s->pb) - HDRSIZE; + dts = AV_RL32(&hdr[4]); + + // TODO - add general support in av_gen_search, so it adds positions after reading timestamps + av_add_index_entry(s->streams[stream_index], pos, dts, size + HDRSIZE, 0, + key ? AVINDEX_KEYFRAME : 0); + + *ppos = pos; + return dts; + } + default: + avio_skip(pb, size); + break; + } + } + return AV_NOPTS_VALUE; +} + + AVInputFormat ff_nuv_demuxer = { .name = "nuv", .long_name = NULL_IF_CONFIG_SMALL("NuppelVideo"), @@ -269,5 +343,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 175feb79e6..0fce8986b1 100644 --- a/libavformat/oggdec.c +++ b/libavformat/oggdec.c @@ -28,8 +28,8 @@ DEALINGS IN THE SOFTWARE. */ - #include <stdio.h> +#include "libavutil/avassert.h" #include "oggdec.h" #include "avformat.h" #include "internal.h" @@ -46,6 +46,7 @@ static const struct ogg_codec * const ogg_codecs[] = { &ff_theora_codec, &ff_flac_codec, &ff_celt_codec, + &ff_opus_codec, &ff_old_dirac_codec, &ff_old_flac_codec, &ff_ogm_video_codec, @@ -55,6 +56,8 @@ static const struct ogg_codec * const ogg_codecs[] = { NULL }; +static int64_t ogg_calc_pts(AVFormatContext *s, int idx, int64_t *dts); + //FIXME We could avoid some structure duplication static int ogg_save(AVFormatContext *s) { @@ -117,9 +120,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; @@ -134,6 +139,10 @@ static int ogg_reset(struct ogg *ogg) os->nsegs = 0; os->segp = 0; os->incomplete = 0; + os->got_data = 0; + if (start_pos <= s->data_offset) { + os->lastpts = 0; + } } ogg->curidx = -1; @@ -153,33 +162,81 @@ 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) { + struct ogg *ogg = s->priv_data; + struct ogg_stream *os; + unsigned bufsize; + uint8_t *buf; + + if (ogg->nstreams != 1) { + av_log_missing_feature(s, "Changing stream parameters in multistream ogg", 0); + return AVERROR_PATCHWELCOME; + } + + os = &ogg->streams[0]; + + buf = os->buf; + bufsize = os->bufsize; + + if (!ogg->state || ogg->state->streams[0].private != os->private) + av_freep(&ogg->streams[0].private); + /* Set Ogg stream settings similar to what is done in ogg_new_stream(). We + * also re-use the ogg_stream allocated buffer */ + memset(os, 0, sizeof(*os)); + os->serial = serial; + os->bufsize = bufsize; + os->buf = buf; + os->header = -1; + + return 0; +} + +static int ogg_new_stream(AVFormatContext *s, uint32_t serial) +{ struct ogg *ogg = s->priv_data; - int idx = ogg->nstreams++; + 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; + } - ogg->streams = av_realloc (ogg->streams, - ogg->nstreams * sizeof (*ogg->streams)); - memset (ogg->streams + idx, 0, sizeof (*ogg->streams)); + /* 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 (new_avstream) { - st = avformat_new_stream(s, NULL); - if (!st) - return AVERROR(ENOMEM); - - st->id = idx; - avpriv_set_pts_info(st, 64, 1, 1000000); + if (!os->buf) + return AVERROR(ENOMEM); + + /* 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; } @@ -199,7 +256,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; @@ -225,7 +292,7 @@ static int ogg_read_page(AVFormatContext *s, int *str) break; c = avio_r8(bc); - if (bc->eof_reached) + if (url_feof(bc)) return AVERROR_EOF; sync[sp++ & 3] = c; }while (i++ < MAX_PAGE_SIZE); @@ -235,8 +302,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); @@ -246,22 +315,15 @@ static int ogg_read_page(AVFormatContext *s, int *str) idx = ogg_find_stream (ogg, serial); if (idx < 0){ - if (ogg->headers) { - int n; + if (data_packets_seen(ogg)) + idx = ogg_replace_stream(s, serial); + else + idx = ogg_new_stream(s, serial); - for (n = 0; n < ogg->nstreams; n++) { - av_freep(&ogg->streams[n].buf); - if (!ogg->state || ogg->state->streams[n].private != ogg->streams[n].private) - av_freep(&ogg->streams[n].private); - } - ogg->curidx = -1; - ogg->nstreams = 0; - 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; @@ -281,8 +343,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; @@ -312,13 +380,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; @@ -328,6 +403,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; @@ -370,12 +447,14 @@ static int ogg_packet(AVFormatContext *s, int *str, int *dstart, int *dsize, if (!complete && os->segp == os->nsegs){ ogg->curidx = -1; - os->incomplete = 1; + // Do not set incomplete for empty packets. + // Together with the code in ogg_read_page + // that discards all continuation of empty packets + // we would get an infinite loop. + os->incomplete = !!os->psize; } }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, "Page at %"PRId64" is missing granule\n", os->page_pos); @@ -416,8 +495,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) @@ -426,6 +505,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; } @@ -444,40 +525,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\n", i); - 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; @@ -499,13 +552,37 @@ static int ogg_get_length(AVFormatContext *s) 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; } @@ -513,20 +590,34 @@ 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) - return ret; + do { + ret = ogg_packet(s, NULL, NULL, NULL, NULL); + if (ret < 0) + 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, "Number of headers (%d) mismatch for stream %d\n", os->nb_header, i); + } + 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; } @@ -560,11 +651,24 @@ static int64_t ogg_calc_pts(AVFormatContext *s, int idx, int64_t *dts) return pts; } +static void ogg_validate_keyframe(AVFormatContext *s, int idx, int pstart, int psize) +{ + struct ogg *ogg = s->priv_data; + struct ogg_stream *os = ogg->streams + idx; + if (psize && s->streams[idx]->codec->codec_id == AV_CODEC_ID_THEORA) { + if (!!(os->pflags & AV_PKT_FLAG_KEY) != !(os->buf[pstart] & 0x40)) { + os->pflags ^= AV_PKT_FLAG_KEY; + av_log(s, AV_LOG_WARNING, "Broken file, %skeyframe not correctly marked.\n", + (os->pflags & AV_PKT_FLAG_KEY) ? "" : "non-"); + } + } +} + static int ogg_read_packet(AVFormatContext *s, AVPacket *pkt) { struct ogg *ogg; struct ogg_stream *os; - int idx = -1, ret; + int idx, ret; int pstart, psize; int64_t fpos, pts, dts; @@ -581,6 +685,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; @@ -621,21 +726,32 @@ static int64_t ogg_read_timestamp(AVFormatContext *s, int stream_index, struct ogg *ogg = s->priv_data; AVIOContext *bc = s->pb; int64_t pts = AV_NOPTS_VALUE; - int i = -1; + int64_t keypos = -1; + int i; + int pstart, psize; avio_seek(bc, *pos_arg, SEEK_SET); - ogg_reset(ogg); + ogg_reset(s); - while (avio_tell(bc) < pos_limit && !ogg_packet(s, &i, NULL, NULL, pos_arg)) { + while (avio_tell(bc) <= pos_limit && !ogg_packet(s, &i, &pstart, &psize, pos_arg)) { if (i == stream_index) { struct ogg_stream *os = ogg->streams + stream_index; pts = ogg_calc_pts(s, i, NULL); - if (os->keyframe_seek && !(os->pflags & AV_PKT_FLAG_KEY)) - pts = AV_NOPTS_VALUE; + ogg_validate_keyframe(s, i, pstart, psize); + if (os->pflags & AV_PKT_FLAG_KEY) { + keypos = *pos_arg; + } else if (os->keyframe_seek) { + // if we had a previous keyframe but no pts for it, + // return that keyframe with this pts value. + if (keypos >= 0) + *pos_arg = keypos; + else + pts = AV_NOPTS_VALUE; + } } if (pts != AV_NOPTS_VALUE) break; } - ogg_reset(ogg); + ogg_reset(s); return pts; } @@ -646,6 +762,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 diff --git a/libavformat/oggdec.h b/libavformat/oggdec.h index bb7b345934..febf8cb642 100644 --- a/libavformat/oggdec.h +++ b/libavformat/oggdec.h @@ -80,6 +80,8 @@ 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 void *private; }; @@ -104,7 +106,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; @@ -115,6 +117,7 @@ extern const struct ogg_codec ff_ogm_text_codec; extern const struct ogg_codec ff_ogm_video_codec; extern const struct ogg_codec ff_old_dirac_codec; extern const struct ogg_codec ff_old_flac_codec; +extern const struct ogg_codec ff_opus_codec; extern const struct ogg_codec ff_skeleton_codec; extern const struct ogg_codec ff_speex_codec; extern const struct ogg_codec ff_theora_codec; diff --git a/libavformat/oggenc.c b/libavformat/oggenc.c index c1f22d15cc..1a0e4e3222 100644 --- a/libavformat/oggenc.c +++ b/libavformat/oggenc.c @@ -2,24 +2,25 @@ * 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 */ #include "libavutil/crc.h" +#include "libavutil/opt.h" #include "libavutil/mathematics.h" #include "libavutil/opt.h" #include "libavutil/random_seed.h" @@ -73,6 +74,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", OFFSET(pref_size), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, MAX_PAGE_SIZE, PARAM }, { NULL }, @@ -133,6 +136,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) @@ -202,9 +210,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; @@ -247,7 +260,7 @@ static int ogg_buffer_data(AVFormatContext *s, AVStream *st, static uint8_t *ogg_write_vorbiscomment(int offset, int bitexact, int *header_len, AVDictionary **m, int framing_bit) { - const char *vendor = bitexact ? "Libav" : LIBAVFORMAT_IDENT; + const char *vendor = bitexact ? "ffmpeg" : LIBAVFORMAT_IDENT; int size; uint8_t *p, *p0; unsigned int count; @@ -374,13 +387,13 @@ static int ogg_write_header(AVFormatContext *s) AVStream *st = s->streams[i]; unsigned serial_num = i; - if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) + if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) { if (st->codec->codec_id == AV_CODEC_ID_OPUS) /* Opus requires a fixed 48kHz clock */ avpriv_set_pts_info(st, 64, 1, 48000); else avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate); - else if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) + } else if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) avpriv_set_pts_info(st, 64, st->codec->time_base.num, st->codec->time_base.den); if (st->codec->codec_id != AV_CODEC_ID_VORBIS && st->codec->codec_id != AV_CODEC_ID_THEORA && @@ -565,7 +578,7 @@ static int ogg_write_trailer(AVFormatContext *s) if (st->codec->codec_id == AV_CODEC_ID_FLAC || st->codec->codec_id == AV_CODEC_ID_SPEEX || st->codec->codec_id == AV_CODEC_ID_OPUS) { - av_free(oggstream->header[0]); + av_freep(&oggstream->header[0]); } av_freep(&oggstream->header[1]); av_freep(&st->priv_data); diff --git a/libavformat/oggparsecelt.c b/libavformat/oggparsecelt.c index 0deccc2d08..afc4392e2c 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 */ @@ -62,18 +62,18 @@ static int celt_header(AVFormatContext *s, int idx) overlap = AV_RL32(p + 48); /* unused bytes per packet field skipped */ extra_headers = AV_RL32(p + 56); - av_free(os->private); - av_free(st->codec->extradata); st->codec->codec_type = AVMEDIA_TYPE_AUDIO; st->codec->codec_id = AV_CODEC_ID_CELT; st->codec->sample_rate = sample_rate; st->codec->channels = nb_channels; st->codec->frame_size = frame_size; + av_free(st->codec->extradata); 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); diff --git a/libavformat/oggparsedirac.c b/libavformat/oggparsedirac.c index 55a0b59127..73bc495aa8 100644 --- a/libavformat/oggparsedirac.c +++ b/libavformat/oggparsedirac.c @@ -1,20 +1,20 @@ /* * Copyright (C) 2008 David Conrad * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/oggparseflac.c b/libavformat/oggparseflac.c index f59b4008dc..3ff594e3c4 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 */ diff --git a/libavformat/oggparseogm.c b/libavformat/oggparseogm.c index 7b3cda221e..a3caac0e15 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) const uint8_t *p = os->buf + os->pstart; uint64_t time_unit; uint64_t spu; + uint32_t size; if(!(*p & 1)) return 0; @@ -67,11 +69,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; } - p += 4; /* useless size field */ - + size = bytestream_get_le32(&p); + size = FFMIN(size, os->psize); time_unit = bytestream_get_le64(&p); spu = bytestream_get_le64(&p); p += 4; /* default_len */ @@ -85,8 +89,19 @@ ogm_header(AVFormatContext *s, int idx) st->codec->channels = bytestream_get_le16(&p); p += 2; /* block_align */ st->codec->bit_rate = bytestream_get_le32(&p) * 8; - st->codec->sample_rate = spu * 10000000 / time_unit; + st->codec->sample_rate = time_unit ? spu * 10000000 / time_unit : 0; avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate); + if (size >= 56 && st->codec->codec_id == AV_CODEC_ID_AAC) { + p += 4; + size -= 4; + } + if (size > 52) { + av_assert0(FF_INPUT_BUFFER_PADDING_SIZE <= 52); + size -= 52; + st->codec->extradata_size = size; + st->codec->extradata = av_malloc(size + FF_INPUT_BUFFER_PADDING_SIZE); + bytestream_get_buffer(&p, st->codec->extradata, size); + } } } else if (*p == 3) { if (os->psize > 8) diff --git a/libavformat/oggparseopus.c b/libavformat/oggparseopus.c new file mode 100644 index 0000000000..0e8f1ca5bb --- /dev/null +++ b/libavformat/oggparseopus.c @@ -0,0 +1,135 @@ +/* + * Opus parser for Ogg + * 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/intreadwrite.h" +#include "avformat.h" +#include "internal.h" +#include "oggdec.h" + +struct oggopus_private { + int need_comments; + unsigned pre_skip; + int64_t cur_dts; +}; + +#define OPUS_HEAD_SIZE 19 + +static int opus_header(AVFormatContext *avf, int idx) +{ + struct ogg *ogg = avf->priv_data; + struct ogg_stream *os = &ogg->streams[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); + priv->pre_skip = AV_RL16(packet + 10); + /*orig_sample_rate = AV_RL32(packet + 12);*/ + /*gain = AV_RL16(packet + 16);*/ + /*channel_map = AV_RL8 (packet + 18);*/ + + extradata = av_malloc(os->psize + FF_INPUT_BUFFER_PADDING_SIZE); + if (!extradata) + return AVERROR(ENOMEM); + memcpy(extradata, packet, os->psize); + st->codec->extradata = extradata; + st->codec->extradata_size = os->psize; + + st->codec->sample_rate = 48000; + avpriv_set_pts_info(st, 64, 1, 48000); + priv->need_comments = 1; + return 1; + } + + if (priv->need_comments) { + if (os->psize < 8 || memcmp(packet, "OpusTags", 8)) + return AVERROR_INVALIDDATA; + ff_vorbis_comment(avf, &st->metadata, packet + 8, os->psize - 8); + priv->need_comments--; + return 1; + } + return 0; +} + +static int opus_packet(AVFormatContext *avf, int idx) +{ + struct ogg *ogg = avf->priv_data; + struct ogg_stream *os = &ogg->streams[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; + + 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; + } + os->pduration = frame_size * nb_frames; + if (os->lastpts != AV_NOPTS_VALUE) { + if (st->start_time == AV_NOPTS_VALUE) + st->start_time = os->lastpts; + priv->cur_dts = os->lastdts = os->lastpts -= priv->pre_skip; + } + priv->cur_dts += os->pduration; + if ((os->flags & OGG_FLAG_EOS)) { + int64_t skip = priv->cur_dts - os->granule + priv->pre_skip; + 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 must be truncated to %d (unimplemented).\n", + os->pduration); + } + } + return 0; +} + +const struct ogg_codec ff_opus_codec = { + .name = "Opus", + .magic = "OpusHead", + .magicsize = 8, + .header = opus_header, + .packet = opus_packet, +}; diff --git a/libavformat/oggparseskeleton.c b/libavformat/oggparseskeleton.c index 2de067d84e..f9ad701145 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 */ @@ -47,7 +47,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; diff --git a/libavformat/oggparsetheora.c b/libavformat/oggparsetheora.c index dfb73c9bd1..6877d1e06c 100644 --- a/libavformat/oggparsetheora.c +++ b/libavformat/oggparsetheora.c @@ -110,16 +110,16 @@ theora_header (AVFormatContext * s, int idx) st->codec->codec_type = AVMEDIA_TYPE_VIDEO; st->codec->codec_id = AV_CODEC_ID_THEORA; st->need_parsing = AVSTREAM_PARSE_HEADERS; - } break; case 0x81: - ff_vorbis_comment(s, &st->metadata, os->buf + os->pstart + 7, os->psize - 8); + ff_vorbis_comment(s, &st->metadata, os->buf + os->pstart + 7, os->psize - 7); case 0x82: if (!thp->version) return -1; break; default: + av_log(s, AV_LOG_ERROR, "Unknown header type %X\n", os->buf[os->pstart]); return -1; } @@ -160,10 +160,47 @@ theora_gptopts(AVFormatContext *ctx, int idx, uint64_t gp, int64_t *dts) 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 396a3e3ea7..09f5205fbb 100644 --- a/libavformat/oggparsevorbis.c +++ b/libavformat/oggparsevorbis.c @@ -203,12 +203,12 @@ vorbis_header (AVFormatContext * s, int idx) int pkt_type = os->buf[os->pstart]; if (!(pkt_type & 1)) - return 0; + return os->private ? 0 : -1; if (!os->private) { os->private = av_mallocz(sizeof(struct oggvorbis_private)); if (!os->private) - return 0; + return -1; } if (os->psize < 1 || pkt_type > 5) @@ -223,6 +223,8 @@ vorbis_header (AVFormatContext * s, int idx) priv->len[pkt_type >> 1] = os->psize; priv->packet[pkt_type >> 1] = av_mallocz(os->psize); + if (!priv->packet[pkt_type >> 1]) + return AVERROR(ENOMEM); memcpy(priv->packet[pkt_type >> 1], os->buf + os->pstart, os->psize); if (os->buf[os->pstart] == 1) { const uint8_t *p = os->buf + os->pstart + 7; /* skip "\001vorbis" tag */ @@ -296,33 +298,39 @@ static int vorbis_packet(AVFormatContext *s, int idx) here we parse the duration of each packet in the first page and compare the total duration to the page granule to find the encoder delay and set the first timestamp */ - if (!os->lastpts) { - int seg; + if ((!os->lastpts || os->lastpts == AV_NOPTS_VALUE) && !(os->flags & OGG_FLAG_EOS)) { + int seg, d; uint8_t *last_pkt = os->buf + os->pstart; uint8_t *next_pkt = last_pkt; - int first_duration = 0; avpriv_vorbis_parse_reset(&priv->vp); duration = 0; - for (seg = 0; seg < os->nsegs; seg++) { + seg = os->segp; + d = avpriv_vorbis_parse_frame(&priv->vp, last_pkt, 1); + if (d < 0) { + os->pflags |= AV_PKT_FLAG_CORRUPT; + return 0; + } + duration += d; + last_pkt = next_pkt = next_pkt + os->psize; + for (; seg < os->nsegs; seg++) { if (os->segments[seg] < 255) { int d = avpriv_vorbis_parse_frame(&priv->vp, last_pkt, 1); if (d < 0) { duration = os->granule; break; } - if (!duration) - first_duration = d; duration += d; last_pkt = next_pkt + os->segments[seg]; } next_pkt += os->segments[seg]; } os->lastpts = os->lastdts = os->granule - duration; - s->streams[idx]->start_time = os->lastpts + first_duration; - if (s->streams[idx]->duration) - s->streams[idx]->duration -= s->streams[idx]->start_time; - s->streams[idx]->cur_dts = AV_NOPTS_VALUE; + if(s->streams[idx]->start_time == AV_NOPTS_VALUE) { + s->streams[idx]->start_time = FFMAX(os->lastpts, 0); + if (s->streams[idx]->duration) + s->streams[idx]->duration -= s->streams[idx]->start_time; + } priv->final_pts = AV_NOPTS_VALUE; avpriv_vorbis_parse_reset(&priv->vp); } @@ -330,7 +338,7 @@ static int vorbis_packet(AVFormatContext *s, int idx) /* parse packet duration */ if (os->psize > 0) { duration = avpriv_vorbis_parse_frame(&priv->vp, os->buf + os->pstart, 1); - if (duration <= 0) { + if (duration < 0) { os->pflags |= AV_PKT_FLAG_CORRUPT; return 0; } diff --git a/libavformat/oma.c b/libavformat/oma.c index f6454d97af..9e4bfbdd77 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 */ @@ -22,7 +22,7 @@ #include "oma.h" #include "libavcodec/avcodec.h" -const uint16_t ff_oma_srate_tab[6] = { 320, 441, 480, 882, 960, 0 }; +const uint16_t ff_oma_srate_tab[8] = { 320, 441, 480, 882, 960, 0, 0, 0}; const AVCodecTag ff_oma_codec_tags[] = { { AV_CODEC_ID_ATRAC3, OMA_CODECID_ATRAC3 }, @@ -31,3 +31,4 @@ const AVCodecTag ff_oma_codec_tags[] = { { AV_CODEC_ID_PCM_S16BE, OMA_CODECID_LPCM }, { 0 }, }; + diff --git a/libavformat/oma.h b/libavformat/oma.h index bac8bcb736..1f0ddf9a88 100644 --- a/libavformat/oma.h +++ b/libavformat/oma.h @@ -37,7 +37,7 @@ enum { OMA_CODECID_WMA = 5, }; -extern const uint16_t ff_oma_srate_tab[6]; +extern const uint16_t ff_oma_srate_tab[8]; extern const AVCodecTag ff_oma_codec_tags[]; diff --git a/libavformat/omadec.c b/libavformat/omadec.c index 022942d242..1e1b980ca9 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 */ @@ -219,6 +219,12 @@ static int decrypt_init(AVFormatContext *s, ID3v2ExtraMeta *em, uint8_t *header) av_log(s, AV_LOG_ERROR, "Invalid encryption header\n"); return -1; } + if ( OMA_ENC_HEADER_SIZE + oc->k_size + oc->e_size + oc->i_size + 8 > geob->datasize + || OMA_ENC_HEADER_SIZE + 48 > geob->datasize + ) { + av_log(s, AV_LOG_ERROR, "Too little GEOB data\n"); + return AVERROR_INVALIDDATA; + } oc->rid = AV_RB32(&gdata[OMA_ENC_HEADER_SIZE + 28]); av_log(s, AV_LOG_DEBUG, "RID: %.8x\n", oc->rid); @@ -242,7 +248,7 @@ static int decrypt_init(AVFormatContext *s, ID3v2ExtraMeta *em, uint8_t *header) if (!rprobe(s, gdata, oc->r_val) || !nprobe(s, gdata, geob->datasize, oc->n_val)) break; } - if (i >= sizeof(leaf_table)) { + if (i >= FF_ARRAY_ELEMS(leaf_table)) { av_log(s, AV_LOG_ERROR, "Invalid key\n"); return -1; } @@ -340,7 +346,7 @@ static int oma_read_header(AVFormatContext *s) av_log(s, AV_LOG_ERROR, "Unsupported codec ATRAC3+!\n"); break; case OMA_CODECID_MP3: - st->need_parsing = AVSTREAM_PARSE_FULL; + st->need_parsing = AVSTREAM_PARSE_FULL_RAW; framesize = 1024; break; case OMA_CODECID_LPCM: @@ -377,7 +383,7 @@ static int oma_read_packet(AVFormatContext *s, AVPacket *pkt) if (oc->encrypted) { /* previous unencrypted block saved in IV for the next packet (CBC mode) */ - av_des_crypt(&oc->av_des, pkt->data, pkt->data, (packet_size >> 3), oc->iv, 1); + av_des_crypt(&oc->av_des, pkt->data, pkt->data, (ret >> 3), oc->iv, 1); } return ret; diff --git a/libavformat/omaenc.c b/libavformat/omaenc.c index ea28e109c4..ab31d65fac 100644 --- a/libavformat/omaenc.c +++ b/libavformat/omaenc.c @@ -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 */ diff --git a/libavformat/options.c b/libavformat/options.c index 3399dd4397..42307d170f 100644 --- a/libavformat/options.c +++ b/libavformat/options.c @@ -1,20 +1,20 @@ /* * Copyright (c) 2000, 2001, 2002 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "avformat.h" @@ -76,6 +76,13 @@ static const AVClass *format_child_class_next(const AVClass *prev) return NULL; } +static AVClassCategory get_category(void *ptr) +{ + AVFormatContext* s = ptr; + if(s->iformat) return AV_CLASS_CATEGORY_DEMUXER; + else return AV_CLASS_CATEGORY_MUXER; +} + static const AVClass av_format_context_class = { .class_name = "AVFormatContext", .item_name = format_to_name, @@ -83,6 +90,8 @@ static const AVClass av_format_context_class = { .version = LIBAVUTIL_VERSION_INT, .child_next = format_child_next, .child_class_next = format_child_class_next, + .category = AV_CLASS_CATEGORY_MUXER, + .get_category = get_category, }; static void avformat_get_context_defaults(AVFormatContext *s) @@ -103,6 +112,11 @@ AVFormatContext *avformat_alloc_context(void) return ic; } +enum AVDurationEstimationMethod av_fmt_ctx_get_duration_estimation_method(const AVFormatContext* ctx) +{ + return ctx->duration_estimation_method; +} + const AVClass *avformat_get_class(void) { return &av_format_context_class; diff --git a/libavformat/options_table.h b/libavformat/options_table.h index 4beee5db24..778c740f92 100644 --- a/libavformat/options_table.h +++ b/libavformat/options_table.h @@ -1,18 +1,20 @@ /* - * This file is part of Libav. + * Copyright (c) 2000, 2001, 2002 Fabrice Bellard * - * Libav is free software; you can redistribute it and/or + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -31,6 +33,8 @@ #define D AV_OPT_FLAG_DECODING_PARAM static const AVOption options[]={ +{"avioflags", NULL, OFFSET(avio_flags), AV_OPT_TYPE_FLAGS, {.i64 = DEFAULT }, INT_MIN, INT_MAX, D|E, "avioflags"}, +{"direct", "reduce buffering", 0, AV_OPT_TYPE_CONST, {.i64 = AVIO_FLAG_DIRECT }, INT_MIN, INT_MAX, D|E, "avioflags"}, {"probesize", "set probing size", OFFSET(probesize), AV_OPT_TYPE_INT, {.i64 = 5000000 }, 32, INT_MAX, D}, {"packetsize", "set packet size", OFFSET(packet_size), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, 0, INT_MAX, E}, {"fflags", NULL, OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64 = DEFAULT }, INT_MIN, INT_MAX, D|E, "fflags"}, @@ -40,6 +44,9 @@ static const AVOption options[]={ {"noparse", "disable AVParsers, this needs nofillin too", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_NOPARSE }, INT_MIN, INT_MAX, D, "fflags"}, {"igndts", "ignore dts", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_IGNDTS }, INT_MIN, INT_MAX, D, "fflags"}, {"discardcorrupt", "discard corrupted frames", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_DISCARD_CORRUPT }, INT_MIN, INT_MAX, D, "fflags"}, +{"sortdts", "try to interleave outputted packets by dts", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_SORT_DTS }, INT_MIN, INT_MAX, D, "fflags"}, +{"keepside", "dont merge side data", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_KEEP_SIDE_DATA }, INT_MIN, INT_MAX, D, "fflags"}, +{"latm", "enable RTP MP4A-LATM payload", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_MP4A_LATM }, INT_MIN, INT_MAX, E, "fflags"}, {"nobuffer", "reduce the latency introduced by optional buffering", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_NOBUFFER }, 0, INT_MAX, D, "fflags"}, {"analyzeduration", "how many microseconds are analyzed to estimate duration", OFFSET(max_analyze_duration), AV_OPT_TYPE_INT, {.i64 = 5*AV_TIME_BASE }, 0, INT_MAX, D}, {"cryptokey", "decryption key", OFFSET(key), AV_OPT_TYPE_BINARY, {.dbl = 0}, 0, 0, D}, @@ -49,6 +56,9 @@ static const AVOption options[]={ {"ts", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_FDEBUG_TS }, INT_MIN, INT_MAX, E|D, "fdebug"}, {"max_delay", "maximum muxing or demuxing delay in microseconds", OFFSET(max_delay), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, INT_MAX, E|D}, {"fpsprobesize", "number of frames used to probe fps", OFFSET(fps_probe_size), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX-1, D}, +{"audio_preload", "microseconds by which audio packets should be interleaved earlier", OFFSET(audio_preload), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX-1, E}, +{"chunk_duration", "microseconds for each chunk", OFFSET(max_chunk_duration), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX-1, E}, +{"chunk_size", "size in bytes for each chunk", OFFSET(max_chunk_size), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX-1, E}, /* this is a crutch for avconv, since it cannot deal with identically named options in different contexts. * to be removed when avconv is fixed */ {"f_err_detect", "set error detection flags (deprecated; use err_detect, save via avconv)", OFFSET(error_recognition), AV_OPT_TYPE_FLAGS, {.i64 = AV_EF_CRCCHECK }, INT_MIN, INT_MAX, D, "err_detect"}, @@ -57,6 +67,11 @@ static const AVOption options[]={ {"bitstream", "detect bitstream specification deviations", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_BITSTREAM }, INT_MIN, INT_MAX, D, "err_detect"}, {"buffer", "detect improper bitstream length", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_BUFFER }, INT_MIN, INT_MAX, D, "err_detect"}, {"explode", "abort decoding on minor error detection", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_EXPLODE }, INT_MIN, INT_MAX, D, "err_detect"}, +{"careful", "consider things that violate the spec 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 shouldnt 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", "avoid negative timestamps", OFFSET(avoid_negative_ts), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 1, E}, {NULL}, }; diff --git a/libavformat/os_support.c b/libavformat/os_support.c index 451801ff70..ad1a23d0ed 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 */ @@ -32,6 +32,7 @@ #include <fcntl.h> #include <io.h> #include <windows.h> +#include <share.h> int ff_win32_open(const char *filename_utf8, int oflag, int pmode) { @@ -46,12 +47,12 @@ int ff_win32_open(const char *filename_utf8, int oflag, int pmode) filename_w = av_mallocz(sizeof(wchar_t) * num_chars); MultiByteToWideChar(CP_UTF8, 0, filename_utf8, -1, filename_w, num_chars); - fd = _wopen(filename_w, oflag, pmode); + fd = _wsopen(filename_w, oflag, SH_DENYNO, pmode); av_freep(&filename_w); /* filename maybe be in CP_ACP */ if (fd == -1 && !(oflag & O_CREAT)) - return open(filename_utf8, oflag, pmode); + return _sopen(filename_utf8, oflag, SH_DENYNO, pmode); return fd; } diff --git a/libavformat/os_support.h b/libavformat/os_support.h index c5d3ab4b33..b06cefd45e 100644 --- a/libavformat/os_support.h +++ b/libavformat/os_support.h @@ -2,20 +2,20 @@ * various OS-feature replacement utilities * copyright (c) 2000, 2001, 2002 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -33,8 +33,17 @@ #if defined(__MINGW32__) && !defined(__MINGW32CE__) # include <fcntl.h> +# ifdef lseek +# undef lseek +# endif # define lseek(f,p,w) _lseeki64((f), (p), (w)) +# ifdef stat +# undef stat +# endif # define stat _stati64 +# ifdef fstat +# undef fstat +# endif # define fstat(f,s) _fstati64((f), (s)) #endif /* defined(__MINGW32__) && !defined(__MINGW32CE__) */ diff --git a/libavformat/output-example.c b/libavformat/output-example.c deleted file mode 100644 index e4babb49a5..0000000000 --- a/libavformat/output-example.c +++ /dev/null @@ -1,542 +0,0 @@ -/* - * Copyright (c) 2003 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -/** - * @file - * libavformat API example. - * - * @example libavformat/output-example.c - * Output a media file in any supported libavformat format. - * The default codecs are used. - */ - -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <math.h> - -#include "libavutil/mathematics.h" -#include "libavformat/avformat.h" -#include "libswscale/swscale.h" - -#undef exit - -/* 5 seconds stream duration */ -#define STREAM_DURATION 5.0 -#define STREAM_FRAME_RATE 25 /* 25 images/s */ -#define STREAM_NB_FRAMES ((int)(STREAM_DURATION * STREAM_FRAME_RATE)) -#define STREAM_PIX_FMT AV_PIX_FMT_YUV420P /* default pix_fmt */ - -static int sws_flags = SWS_BICUBIC; - -/**************************************************************/ -/* audio output */ - -static float t, tincr, tincr2; -static int16_t *samples; -static int audio_input_frame_size; - -/* - * add an audio output stream - */ -static AVStream *add_audio_stream(AVFormatContext *oc, enum AVCodecID codec_id) -{ - AVCodecContext *c; - AVStream *st; - AVCodec *codec; - - /* find the audio encoder */ - codec = avcodec_find_encoder(codec_id); - if (!codec) { - fprintf(stderr, "codec not found\n"); - exit(1); - } - - st = avformat_new_stream(oc, codec); - if (!st) { - fprintf(stderr, "Could not alloc stream\n"); - exit(1); - } - - c = st->codec; - - /* put sample parameters */ - c->sample_fmt = AV_SAMPLE_FMT_S16; - c->bit_rate = 64000; - c->sample_rate = 44100; - c->channels = 2; - - // some formats want stream headers to be separate - if (oc->oformat->flags & AVFMT_GLOBALHEADER) - c->flags |= CODEC_FLAG_GLOBAL_HEADER; - - return st; -} - -static void open_audio(AVFormatContext *oc, AVStream *st) -{ - AVCodecContext *c; - - c = st->codec; - - /* open it */ - if (avcodec_open2(c, NULL, NULL) < 0) { - fprintf(stderr, "could not open codec\n"); - exit(1); - } - - /* init signal generator */ - t = 0; - tincr = 2 * M_PI * 110.0 / c->sample_rate; - /* increment frequency by 110 Hz per second */ - tincr2 = 2 * M_PI * 110.0 / c->sample_rate / c->sample_rate; - - if (c->codec->capabilities & CODEC_CAP_VARIABLE_FRAME_SIZE) - audio_input_frame_size = 10000; - else - audio_input_frame_size = c->frame_size; - samples = av_malloc(audio_input_frame_size * - av_get_bytes_per_sample(c->sample_fmt) * - c->channels); -} - -/* Prepare a 16 bit dummy audio frame of 'frame_size' samples and - * 'nb_channels' channels. */ -static void get_audio_frame(int16_t *samples, int frame_size, int nb_channels) -{ - int j, i, v; - int16_t *q; - - q = samples; - for (j = 0; j < frame_size; j++) { - v = (int)(sin(t) * 10000); - for (i = 0; i < nb_channels; i++) - *q++ = v; - t += tincr; - tincr += tincr2; - } -} - -static void write_audio_frame(AVFormatContext *oc, AVStream *st) -{ - AVCodecContext *c; - AVPacket pkt = { 0 }; // data and size must be 0; - AVFrame *frame = avcodec_alloc_frame(); - int got_packet; - - av_init_packet(&pkt); - c = st->codec; - - get_audio_frame(samples, audio_input_frame_size, c->channels); - frame->nb_samples = audio_input_frame_size; - avcodec_fill_audio_frame(frame, c->channels, c->sample_fmt, - (uint8_t *)samples, - audio_input_frame_size * - av_get_bytes_per_sample(c->sample_fmt) * - c->channels, 1); - - avcodec_encode_audio2(c, &pkt, frame, &got_packet); - if (!got_packet) - return; - - pkt.stream_index = st->index; - - /* Write the compressed frame to the media file. */ - if (av_interleaved_write_frame(oc, &pkt) != 0) { - fprintf(stderr, "Error while writing audio frame\n"); - exit(1); - } - avcodec_free_frame(&frame); -} - -static void close_audio(AVFormatContext *oc, AVStream *st) -{ - avcodec_close(st->codec); - - av_free(samples); -} - -/**************************************************************/ -/* video output */ - -static AVFrame *picture, *tmp_picture; -static uint8_t *video_outbuf; -static int frame_count, video_outbuf_size; - -/* Add a video output stream. */ -static AVStream *add_video_stream(AVFormatContext *oc, enum AVCodecID codec_id) -{ - AVCodecContext *c; - AVStream *st; - AVCodec *codec; - - /* find the video encoder */ - codec = avcodec_find_encoder(codec_id); - if (!codec) { - fprintf(stderr, "codec not found\n"); - exit(1); - } - - st = avformat_new_stream(oc, codec); - if (!st) { - fprintf(stderr, "Could not alloc stream\n"); - exit(1); - } - - c = st->codec; - - /* Put sample parameters. */ - c->bit_rate = 400000; - /* Resolution must be a multiple of two. */ - c->width = 352; - c->height = 288; - /* timebase: This is the fundamental unit of time (in seconds) in terms - * of which frame timestamps are represented. For fixed-fps content, - * timebase should be 1/framerate and timestamp increments should be - * identical to 1. */ - c->time_base.den = STREAM_FRAME_RATE; - c->time_base.num = 1; - c->gop_size = 12; /* emit one intra frame every twelve frames at most */ - c->pix_fmt = STREAM_PIX_FMT; - if (c->codec_id == AV_CODEC_ID_MPEG2VIDEO) { - /* just for testing, we also add B frames */ - c->max_b_frames = 2; - } - if (c->codec_id == AV_CODEC_ID_MPEG1VIDEO) { - /* Needed to avoid using macroblocks in which some coeffs overflow. - * This does not happen with normal video, it just happens here as - * the motion of the chroma plane does not match the luma plane. */ - c->mb_decision = 2; - } - /* Some formats want stream headers to be separate. */ - if (oc->oformat->flags & AVFMT_GLOBALHEADER) - c->flags |= CODEC_FLAG_GLOBAL_HEADER; - - return st; -} - -static AVFrame *alloc_picture(enum AVPixelFormat pix_fmt, int width, int height) -{ - AVFrame *picture; - uint8_t *picture_buf; - int size; - - picture = avcodec_alloc_frame(); - if (!picture) - return NULL; - size = avpicture_get_size(pix_fmt, width, height); - picture_buf = av_malloc(size); - if (!picture_buf) { - av_free(picture); - return NULL; - } - avpicture_fill((AVPicture *)picture, picture_buf, - pix_fmt, width, height); - return picture; -} - -static void open_video(AVFormatContext *oc, AVStream *st) -{ - AVCodecContext *c; - - c = st->codec; - - /* open the codec */ - if (avcodec_open2(c, NULL, NULL) < 0) { - fprintf(stderr, "could not open codec\n"); - exit(1); - } - - video_outbuf = NULL; - if (!(oc->oformat->flags & AVFMT_RAWPICTURE)) { - /* Allocate output buffer. */ - /* XXX: API change will be done. */ - /* Buffers passed into lav* can be allocated any way you prefer, - * as long as they're aligned enough for the architecture, and - * they're freed appropriately (such as using av_free for buffers - * allocated with av_malloc). */ - video_outbuf_size = 200000; - video_outbuf = av_malloc(video_outbuf_size); - } - - /* Allocate the encoded raw picture. */ - picture = alloc_picture(c->pix_fmt, c->width, c->height); - if (!picture) { - fprintf(stderr, "Could not allocate picture\n"); - exit(1); - } - - /* If the output format is not YUV420P, then a temporary YUV420P - * picture is needed too. It is then converted to the required - * output format. */ - tmp_picture = NULL; - if (c->pix_fmt != AV_PIX_FMT_YUV420P) { - tmp_picture = alloc_picture(AV_PIX_FMT_YUV420P, c->width, c->height); - if (!tmp_picture) { - fprintf(stderr, "Could not allocate temporary picture\n"); - exit(1); - } - } -} - -/* Prepare a dummy image. */ -static void fill_yuv_image(AVFrame *pict, int frame_index, - int width, int height) -{ - int x, y, i; - - i = frame_index; - - /* Y */ - for (y = 0; y < height; y++) - for (x = 0; x < width; x++) - pict->data[0][y * pict->linesize[0] + x] = x + y + i * 3; - - /* Cb and Cr */ - for (y = 0; y < height / 2; y++) { - for (x = 0; x < width / 2; x++) { - pict->data[1][y * pict->linesize[1] + x] = 128 + y + i * 2; - pict->data[2][y * pict->linesize[2] + x] = 64 + x + i * 5; - } - } -} - -static void write_video_frame(AVFormatContext *oc, AVStream *st) -{ - int out_size, ret; - AVCodecContext *c; - static struct SwsContext *img_convert_ctx; - - c = st->codec; - - if (frame_count >= STREAM_NB_FRAMES) { - /* No more frames to compress. The codec has a latency of a few - * frames if using B-frames, so we get the last frames by - * passing the same picture again. */ - } else { - if (c->pix_fmt != AV_PIX_FMT_YUV420P) { - /* as we only generate a YUV420P picture, we must convert it - * to the codec pixel format if needed */ - if (img_convert_ctx == NULL) { - img_convert_ctx = sws_getContext(c->width, c->height, - AV_PIX_FMT_YUV420P, - c->width, c->height, - c->pix_fmt, - sws_flags, NULL, NULL, NULL); - if (img_convert_ctx == NULL) { - fprintf(stderr, - "Cannot initialize the conversion context\n"); - exit(1); - } - } - fill_yuv_image(tmp_picture, frame_count, c->width, c->height); - sws_scale(img_convert_ctx, tmp_picture->data, tmp_picture->linesize, - 0, c->height, picture->data, picture->linesize); - } else { - fill_yuv_image(picture, frame_count, c->width, c->height); - } - } - - if (oc->oformat->flags & AVFMT_RAWPICTURE) { - /* Raw video case - the API will change slightly in the near - * future for that. */ - AVPacket pkt; - av_init_packet(&pkt); - - pkt.flags |= AV_PKT_FLAG_KEY; - pkt.stream_index = st->index; - pkt.data = (uint8_t *)picture; - pkt.size = sizeof(AVPicture); - - ret = av_interleaved_write_frame(oc, &pkt); - } else { - /* encode the image */ - out_size = avcodec_encode_video(c, video_outbuf, - video_outbuf_size, picture); - /* If size is zero, it means the image was buffered. */ - if (out_size > 0) { - AVPacket pkt; - av_init_packet(&pkt); - - if (c->coded_frame->pts != AV_NOPTS_VALUE) - pkt.pts = av_rescale_q(c->coded_frame->pts, - c->time_base, st->time_base); - if (c->coded_frame->key_frame) - pkt.flags |= AV_PKT_FLAG_KEY; - pkt.stream_index = st->index; - pkt.data = video_outbuf; - pkt.size = out_size; - - /* Write the compressed frame to the media file. */ - ret = av_interleaved_write_frame(oc, &pkt); - } else { - ret = 0; - } - } - if (ret != 0) { - fprintf(stderr, "Error while writing video frame\n"); - exit(1); - } - frame_count++; -} - -static void close_video(AVFormatContext *oc, AVStream *st) -{ - avcodec_close(st->codec); - av_free(picture->data[0]); - av_free(picture); - if (tmp_picture) { - av_free(tmp_picture->data[0]); - av_free(tmp_picture); - } - av_free(video_outbuf); -} - -/**************************************************************/ -/* media file output */ - -int main(int argc, char **argv) -{ - const char *filename; - AVOutputFormat *fmt; - AVFormatContext *oc; - AVStream *audio_st, *video_st; - double audio_pts, video_pts; - int i; - - /* Initialize libavcodec, and register all codecs and formats. */ - av_register_all(); - - if (argc != 2) { - printf("usage: %s output_file\n" - "API example program to output a media file with libavformat.\n" - "The output format is automatically guessed according to the file extension.\n" - "Raw images can also be output by using '%%d' in the filename\n" - "\n", argv[0]); - return 1; - } - - filename = argv[1]; - - /* Autodetect the output format from the name. default is MPEG. */ - fmt = av_guess_format(NULL, filename, NULL); - if (!fmt) { - printf("Could not deduce output format from file extension: using MPEG.\n"); - fmt = av_guess_format("mpeg", NULL, NULL); - } - if (!fmt) { - fprintf(stderr, "Could not find suitable output format\n"); - return 1; - } - - /* Allocate the output media context. */ - oc = avformat_alloc_context(); - if (!oc) { - fprintf(stderr, "Memory error\n"); - return 1; - } - oc->oformat = fmt; - snprintf(oc->filename, sizeof(oc->filename), "%s", filename); - - /* Add the audio and video streams using the default format codecs - * and initialize the codecs. */ - video_st = NULL; - audio_st = NULL; - if (fmt->video_codec != AV_CODEC_ID_NONE) { - video_st = add_video_stream(oc, fmt->video_codec); - } - if (fmt->audio_codec != AV_CODEC_ID_NONE) { - audio_st = add_audio_stream(oc, fmt->audio_codec); - } - - /* Now that all the parameters are set, we can open the audio and - * video codecs and allocate the necessary encode buffers. */ - if (video_st) - open_video(oc, video_st); - if (audio_st) - open_audio(oc, audio_st); - - av_dump_format(oc, 0, filename, 1); - - /* open the output file, if needed */ - if (!(fmt->flags & AVFMT_NOFILE)) { - if (avio_open(&oc->pb, filename, AVIO_FLAG_WRITE) < 0) { - fprintf(stderr, "Could not open '%s'\n", filename); - return 1; - } - } - - /* Write the stream header, if any. */ - avformat_write_header(oc, NULL); - - for (;;) { - /* Compute current audio and video time. */ - if (audio_st) - audio_pts = (double)audio_st->pts.val * audio_st->time_base.num / audio_st->time_base.den; - else - audio_pts = 0.0; - - if (video_st) - video_pts = (double)video_st->pts.val * video_st->time_base.num / - video_st->time_base.den; - else - video_pts = 0.0; - - if ((!audio_st || audio_pts >= STREAM_DURATION) && - (!video_st || video_pts >= STREAM_DURATION)) - break; - - /* write interleaved audio and video frames */ - if (!video_st || (video_st && audio_st && audio_pts < video_pts)) { - write_audio_frame(oc, audio_st); - } else { - write_video_frame(oc, video_st); - } - } - - /* Write the trailer, if any. The trailer must be written before you - * close the CodecContexts open when you wrote the header; otherwise - * av_write_trailer() may try to use memory that was freed on - * av_codec_close(). */ - av_write_trailer(oc); - - /* Close each codec. */ - if (video_st) - close_video(oc, video_st); - if (audio_st) - close_audio(oc, audio_st); - - /* Free the streams. */ - for (i = 0; i < oc->nb_streams; i++) { - av_freep(&oc->streams[i]->codec); - av_freep(&oc->streams[i]); - } - - if (!(fmt->flags & AVFMT_NOFILE)) - /* Close the output file. */ - avio_close(oc->pb); - - /* free the stream */ - av_free(oc); - - return 0; -} diff --git a/libavformat/paf.c b/libavformat/paf.c new file mode 100644 index 0000000000..e67732bb83 --- /dev/null +++ b/libavformat/paf.c @@ -0,0 +1,259 @@ +/* + * Packed Animation File demuxer + * Copyright (c) 2012 Paul B Mahol + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavcodec/paf.h" +#include "avformat.h" +#include "internal.h" + +#define MAGIC "Packed Animation File V1.0\n(c) 1992-96 Amazing Studio\x0a\x1a" + +typedef struct { + uint32_t buffer_size; + uint32_t frame_blks; + uint32_t nb_frames; + uint32_t start_offset; + uint32_t preload_count; + uint32_t max_video_blks; + uint32_t max_audio_blks; + + uint32_t current_frame; + uint32_t current_frame_count; + uint32_t current_frame_block; + + uint32_t *blocks_count_table; + uint32_t *frames_offset_table; + uint32_t *blocks_offset_table; + + uint8_t *video_frame; + int video_size; + + uint8_t *audio_frame; + uint8_t *temp_audio_frame; + int audio_size; + + int got_audio; +} PAFDemuxContext; + +static int read_probe(AVProbeData *p) +{ + if ((p->buf_size >= strlen(MAGIC)) && + !memcmp(p->buf, MAGIC, strlen(MAGIC))) + return AVPROBE_SCORE_MAX; + return 0; +} + +static int read_close(AVFormatContext *s) +{ + PAFDemuxContext *p = s->priv_data; + + av_freep(&p->blocks_count_table); + av_freep(&p->frames_offset_table); + av_freep(&p->blocks_offset_table); + av_freep(&p->video_frame); + av_freep(&p->audio_frame); + av_freep(&p->temp_audio_frame); + + return 0; +} + +static void read_table(AVFormatContext *s, uint32_t *table, uint32_t count) +{ + int i; + + for (i = 0; i < count; i++) + table[i] = avio_rl32(s->pb); + + avio_skip(s->pb, 4 * (FFALIGN(count, 512) - count)); +} + +static int read_header(AVFormatContext *s) +{ + PAFDemuxContext *p = s->priv_data; + AVIOContext *pb = s->pb; + AVStream *ast, *vst; + int ret = 0; + + avio_skip(pb, 132); + + vst = avformat_new_stream(s, 0); + if (!vst) + return AVERROR(ENOMEM); + + vst->start_time = 0; + vst->nb_frames = + vst->duration = + p->nb_frames = avio_rl32(pb); + avio_skip(pb, 4); + vst->codec->width = avio_rl32(pb); + vst->codec->height = avio_rl32(pb); + avio_skip(pb, 4); + vst->codec->codec_type = AVMEDIA_TYPE_VIDEO; + vst->codec->codec_tag = 0; + vst->codec->codec_id = AV_CODEC_ID_PAF_VIDEO; + avpriv_set_pts_info(vst, 64, 1, 10); + + ast = avformat_new_stream(s, 0); + if (!ast) + return AVERROR(ENOMEM); + + ast->start_time = 0; + ast->codec->codec_type = AVMEDIA_TYPE_AUDIO; + ast->codec->codec_tag = 0; + ast->codec->codec_id = AV_CODEC_ID_PAF_AUDIO; + ast->codec->channels = 2; + ast->codec->sample_rate = 22050; + avpriv_set_pts_info(ast, 64, 1, 22050); + + p->buffer_size = avio_rl32(pb); + p->preload_count = avio_rl32(pb); + p->frame_blks = avio_rl32(pb); + p->start_offset = avio_rl32(pb); + p->max_video_blks = avio_rl32(pb); + p->max_audio_blks = avio_rl32(pb); + if (p->buffer_size < 175 || + p->max_audio_blks < 2 || + p->max_video_blks < 1 || + p->frame_blks < 1 || + p->nb_frames < 1 || + p->preload_count < 1 || + p->buffer_size > 2048 || + p->max_video_blks > 2048 || + p->max_audio_blks > 2048 || + p->nb_frames > INT_MAX / sizeof(uint32_t) || + p->frame_blks > INT_MAX / sizeof(uint32_t)) + return AVERROR_INVALIDDATA; + + p->blocks_count_table = av_mallocz(p->nb_frames * sizeof(uint32_t)); + p->frames_offset_table = av_mallocz(p->nb_frames * sizeof(uint32_t)); + p->blocks_offset_table = av_mallocz(p->frame_blks * sizeof(uint32_t)); + + p->video_size = p->max_video_blks * p->buffer_size; + p->video_frame = av_mallocz(p->video_size); + + p->audio_size = p->max_audio_blks * p->buffer_size; + p->audio_frame = av_mallocz(p->audio_size); + p->temp_audio_frame = av_mallocz(p->audio_size); + + if (!p->blocks_count_table || + !p->frames_offset_table || + !p->blocks_offset_table || + !p->video_frame || + !p->audio_frame || + !p->temp_audio_frame) { + ret = AVERROR(ENOMEM); + goto fail; + } + + avio_seek(pb, p->buffer_size, SEEK_SET); + + read_table(s, p->blocks_count_table, p->nb_frames); + read_table(s, p->frames_offset_table, p->nb_frames); + read_table(s, p->blocks_offset_table, p->frame_blks); + + p->got_audio = 0; + p->current_frame = 0; + p->current_frame_block = 0; + + avio_seek(pb, p->start_offset, SEEK_SET); + + return 0; + +fail: + read_close(s); + + return ret; +} + +static int read_packet(AVFormatContext *s, AVPacket *pkt) +{ + PAFDemuxContext *p = s->priv_data; + AVIOContext *pb = s->pb; + uint32_t count, offset; + int size, i; + + if (p->current_frame >= p->nb_frames) + return AVERROR_EOF; + + if (url_feof(pb)) + return AVERROR_EOF; + + if (p->got_audio) { + if (av_new_packet(pkt, p->audio_size) < 0) + return AVERROR(ENOMEM); + + memcpy(pkt->data, p->temp_audio_frame, p->audio_size); + pkt->duration = PAF_SOUND_SAMPLES * (p->audio_size / PAF_SOUND_FRAME_SIZE); + pkt->flags |= AV_PKT_FLAG_KEY; + pkt->stream_index = 1; + p->got_audio = 0; + return pkt->size; + } + + count = (p->current_frame == 0) ? p->preload_count : p->blocks_count_table[p->current_frame - 1]; + for (i = 0; i < count; i++) { + if (p->current_frame_block >= p->frame_blks) + return AVERROR_INVALIDDATA; + + offset = p->blocks_offset_table[p->current_frame_block] & ~(1U << 31); + if (p->blocks_offset_table[p->current_frame_block] & (1U << 31)) { + if (offset > p->audio_size - p->buffer_size) + return AVERROR_INVALIDDATA; + + avio_read(pb, p->audio_frame + offset, p->buffer_size); + if (offset == (p->max_audio_blks - 2) * p->buffer_size) { + memcpy(p->temp_audio_frame, p->audio_frame, p->audio_size); + p->got_audio = 1; + } + } else { + if (offset > p->video_size - p->buffer_size) + return AVERROR_INVALIDDATA; + + avio_read(pb, p->video_frame + offset, p->buffer_size); + } + p->current_frame_block++; + } + + size = p->video_size - p->frames_offset_table[p->current_frame]; + if (size < 1) + return AVERROR_INVALIDDATA; + + if (av_new_packet(pkt, size) < 0) + return AVERROR(ENOMEM); + + pkt->stream_index = 0; + pkt->duration = 1; + memcpy(pkt->data, p->video_frame + p->frames_offset_table[p->current_frame], size); + if (pkt->data[0] & 0x20) + pkt->flags |= AV_PKT_FLAG_KEY; + p->current_frame++; + + return pkt->size; +} + +AVInputFormat ff_paf_demuxer = { + .name = "paf", + .long_name = NULL_IF_CONFIG_SMALL("Amazing Studio Packed Animation File"), + .priv_data_size = sizeof(PAFDemuxContext), + .read_probe = read_probe, + .read_header = read_header, + .read_packet = read_packet, + .read_close = read_close, +}; diff --git a/libavformat/pcm.c b/libavformat/pcm.c index 892e8ca24e..bba741de53 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 */ diff --git a/libavformat/pcm.h b/libavformat/pcm.h index 30cbc86ee3..60d8eb3da0 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 */ diff --git a/libavformat/pcmdec.c b/libavformat/pcmdec.c index 716d8b7977..30f69d3ff0 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,6 +24,7 @@ #include "pcm.h" #include "libavutil/log.h" #include "libavutil/opt.h" +#include "libavutil/avassert.h" #define RAW_SAMPLES 1024 @@ -51,7 +52,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; @@ -69,12 +70,13 @@ static int pcm_read_packet(AVFormatContext *s, AVPacket *pkt) ret= av_get_packet(s->pb, pkt, size); + pkt->flags &= ~AV_PKT_FLAG_CORRUPT; pkt->stream_index = 0; if (ret < 0) return ret; bps= av_get_bits_per_sample(s->streams[0]->codec->codec_id); - assert(bps); // if false there IS a bug elsewhere (NOT in this function) + av_assert1(bps); // if false there IS a bug elsewhere (NOT in this function) pkt->dts= pkt->pts= pkt->pos*8 / (bps * s->streams[0]->codec->channels); @@ -82,7 +84,7 @@ static int pcm_read_packet(AVFormatContext *s, AVPacket *pkt) } 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 }, }; diff --git a/libavformat/pcmenc.c b/libavformat/pcmenc.c index e45ab8412a..3e4f308057 100644 --- a/libavformat/pcmenc.c +++ b/libavformat/pcmenc.c @@ -2,20 +2,20 @@ * RAW PCM muxers * Copyright (c) 2002 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/pmpdec.c b/libavformat/pmpdec.c index 6cdce107c4..2ea37ef030 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; } @@ -65,7 +65,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 +73,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,21 +89,21 @@ 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; 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; + pos = avio_tell(pb) + 4*index_cnt; for (i = 0; i < index_cnt; i++) { - int size = avio_rl32(pb); + int size = avio_rl32(pb); int flags = size & 1 ? AVINDEX_KEYFRAME : 0; size >>= 1; av_add_index_entry(vst, pos, i, size, 0, flags); @@ -119,11 +119,15 @@ static int pmp_packet(AVFormatContext *s, AVPacket *pkt) int ret = 0; int i; - if (pb->eof_reached) + if (url_feof(pb)) return AVERROR_EOF; if (pmp->cur_stream == 0) { int num_packets; pmp->audio_packets = avio_r8(pb); + if (!pmp->audio_packets) { + av_log_ask_for_sample(s, "0 audio packets\n"); + return AVERROR_PATCHWELCOME; + } num_packets = (pmp->num_streams - 1) * pmp->audio_packets + 1; avio_skip(pb, 8); pmp->current_packet = 0; @@ -138,7 +142,7 @@ static int pmp_packet(AVFormatContext *s, AVPacket *pkt) pmp->packet_sizes[i] = avio_rl32(pb); } ret = av_get_packet(pb, pkt, pmp->packet_sizes[pmp->current_packet]); - if (ret > 0) { + if (ret >= 0) { ret = 0; // FIXME: this is a hack that should be removed once // compute_pkt_fields() can handle timestamps properly @@ -146,14 +150,13 @@ static int pmp_packet(AVFormatContext *s, AVPacket *pkt) pkt->dts = s->streams[0]->cur_dts++; pkt->stream_index = pmp->cur_stream; } - pmp->current_packet++; - if (pmp->current_packet == 1 || pmp->current_packet > pmp->audio_packets) + if (pmp->current_packet % pmp->audio_packets == 0) pmp->cur_stream = (pmp->cur_stream + 1) % pmp->num_streams; - + pmp->current_packet++; return ret; } -static int pmp_seek(AVFormatContext *s, int stream_idx, int64_t ts, int flags) +static int pmp_seek(AVFormatContext *s, int stream_index, int64_t ts, int flags) { PMPContext *pmp = s->priv_data; pmp->cur_stream = 0; diff --git a/libavformat/psxstr.c b/libavformat/psxstr.c index 800d9cbcf3..86fba163ea 100644 --- a/libavformat/psxstr.c +++ b/libavformat/psxstr.c @@ -2,20 +2,20 @@ * Sony Playstation (PSX) STR File Demuxer * Copyright (c) 2003 The ffmpeg Project * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -64,11 +64,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; + uint8_t *end= sector + p->buf_size; + int aud=0, vid=0; if (p->buf_size < RAW_CD_SECTOR_SIZE) return 0; @@ -80,20 +82,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 50; + if(vid+aud > 3) return 50; + else if(vid+aud) return 1; + else return 0; } static int str_read_header(AVFormatContext *s) @@ -243,7 +277,7 @@ static int str_read_packet(AVFormatContext *s, break; } - if (pb->eof_reached) + if (url_feof(pb)) return AVERROR(EIO); } } diff --git a/libavformat/pva.c b/libavformat/pva.c index 53041ba494..ff8ccee852 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(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; + int len = pva_check(buf); - if (AV_RB16(buf) == PVA_MAGIC && buf[2] && buf[2] < 3 && buf[4] == 0x55) + if (len < 0) + return 0; + + if (pd->buf_size >= len + 8 && + pva_check(buf + len) >= 0) return AVPROBE_SCORE_MAX / 2; - return 0; + return AVPROBE_SCORE_MAX / 4; } static int pva_read_header(AVFormatContext *s) { diff --git a/libavformat/qcp.c b/libavformat/qcp.c index bde59901ef..11fc71a466 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 */ @@ -139,7 +139,7 @@ static int qcp_read_packet(AVFormatContext *s, AVPacket *pkt) QCPContext *c = s->priv_data; unsigned int chunk_size, tag; - while(!pb->eof_reached) { + while(!url_feof(pb)) { if (c->data_size) { int pkt_size, ret, mode = avio_r8(pb); diff --git a/libavformat/qtpalette.h b/libavformat/qtpalette.h index ecc85d3408..7d6802f73c 100644 --- a/libavformat/qtpalette.h +++ b/libavformat/qtpalette.h @@ -3,20 +3,20 @@ * Automatically generated from a utility derived from XAnim: * http://xanim.va.pubnix.com/home.html * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/r3d.c b/libavformat/r3d.c index a4cb20add2..35da81eff1 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 */ @@ -141,8 +141,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); @@ -370,7 +369,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 1c9aabd6fd..07c2782c67 100644 --- a/libavformat/rawdec.c +++ b/libavformat/rawdec.c @@ -3,20 +3,20 @@ * Copyright (c) 2001 Fabrice Bellard * Copyright (c) 2005 Alex Beregszaszi * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -27,6 +27,7 @@ #include "libavutil/opt.h" #include "libavutil/parseutils.h" #include "libavutil/pixdesc.h" +#include "libavutil/avassert.h" #define RAW_PACKET_SIZE 1024 @@ -46,7 +47,7 @@ int ff_raw_read_partial_packet(AVFormatContext *s, AVPacket *pkt) av_free_packet(pkt); return ret; } - pkt->size = ret; + av_shrink_packet(pkt, ret); return ret; } @@ -57,7 +58,7 @@ int ff_raw_audio_read_header(AVFormatContext *s) return AVERROR(ENOMEM); st->codec->codec_type = AVMEDIA_TYPE_AUDIO; st->codec->codec_id = s->iformat->raw_codec_id; - st->need_parsing = AVSTREAM_PARSE_FULL; + st->need_parsing = AVSTREAM_PARSE_FULL_RAW; st->start_time = 0; /* the parameters will be extracted from the compressed bitstream */ @@ -81,18 +82,15 @@ 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; } -#if FF_API_R_FRAME_RATE - st->r_frame_rate = -#endif - st->avg_frame_rate = framerate; - avpriv_set_pts_info(st, 64, framerate.den, framerate.num); + st->codec->time_base = av_inv_q(framerate); + avpriv_set_pts_info(st, 64, 1, 1200000); fail: return ret; @@ -120,7 +118,7 @@ AVInputFormat ff_latm_demuxer = { #endif #if CONFIG_MJPEG_DEMUXER -FF_DEF_RAWVIDEO_DEMUXER(mjpeg, "raw MJPEG video", NULL, "mjpg,mjpeg", AV_CODEC_ID_MJPEG) +FF_DEF_RAWVIDEO_DEMUXER(mjpeg, "raw MJPEG video", NULL, "mjpg,mjpeg,mpo", AV_CODEC_ID_MJPEG) #endif #if CONFIG_MLP_DEMUXER diff --git a/libavformat/rawdec.h b/libavformat/rawdec.h index a5487784c9..d978295ce2 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 */ diff --git a/libavformat/rawenc.c b/libavformat/rawenc.c index 171c603549..94e68c5ee4 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 */ @@ -132,6 +132,19 @@ AVOutputFormat ff_g722_muxer = { }; #endif +#if CONFIG_G723_1_MUXER +AVOutputFormat ff_g723_1_muxer = { + .name = "g723_1", + .long_name = NULL_IF_CONFIG_SMALL("raw G.723.1"), + .mime_type = "audio/g723", + .extensions = "tco,rco", + .audio_codec = AV_CODEC_ID_G723_1, + .video_codec = AV_CODEC_ID_NONE, + .write_packet = ff_raw_write_packet, + .flags= AVFMT_NOTIMESTAMPS, +}; +#endif + #if CONFIG_H261_MUXER AVOutputFormat ff_h261_muxer = { .name = "h261", @@ -244,18 +257,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", diff --git a/libavformat/rawenc.h b/libavformat/rawenc.h index daa5489da8..b5523090b8 100644 --- a/libavformat/rawenc.h +++ b/libavformat/rawenc.h @@ -2,20 +2,20 @@ * RAW muxers * Copyright (C) 2007 Aurelien Jacobs <aurel@gnuage.org> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/rawvideodec.c b/libavformat/rawvideodec.c index 5f372c9a2c..b2ca663d0b 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 */ diff --git a/libavformat/rdt.c b/libavformat/rdt.c index 35540de4a6..6404a5d5e3 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 */ @@ -176,7 +176,7 @@ rdt_load_mdpr (PayloadContext *rdt, AVStream *st, int rule_nr) size = rdt->mlti_data_size; avio_seek(&pb, 0, SEEK_SET); } - if (ff_rm_read_mdpr_codecdata(rdt->rmctx, &pb, st, rdt->rmst[st->index], size) < 0) + if (ff_rm_read_mdpr_codecdata(rdt->rmctx, &pb, st, rdt->rmst[st->index], size, NULL) < 0) return -1; return 0; diff --git a/libavformat/rdt.h b/libavformat/rdt.h index a393299415..c2ec94b8b4 100644 --- a/libavformat/rdt.h +++ b/libavformat/rdt.h @@ -2,20 +2,20 @@ * Realmedia RTSP (RDT) definitions * Copyright (c) 2007 Ronald S. Bultje <rbultje@ronald.bitfreak.net> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/realtextdec.c b/libavformat/realtextdec.c new file mode 100644 index 0000000000..cc7c1841b5 --- /dev/null +++ b/libavformat/realtextdec.c @@ -0,0 +1,145 @@ +/* + * 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_MAX/2 : 0; +} + +static int read_ts(const char *s) +{ + int hh, mm, ss, ms; + + if (sscanf(s, "%u:%u:%u.%u", &hh, &mm, &ss, &ms) == 4) return (hh*3600 + mm*60 + ss) * 100 + ms; + if (sscanf(s, "%u:%u:%u" , &hh, &mm, &ss ) == 3) return (hh*3600 + mm*60 + ss) * 100; + if (sscanf(s, "%u:%u.%u", &mm, &ss, &ms) == 3) return ( mm*60 + ss) * 100 + ms; + if (sscanf(s, "%u:%u" , &mm, &ss ) == 2) return ( mm*60 + ss) * 100; + if (sscanf(s, "%u.%u", &ss, &ms) == 2) return ( ss) * 100 + ms; + return strtol(s, NULL, 10) * 100; +} + +static int realtext_read_header(AVFormatContext *s) +{ + RealTextContext *rt = s->priv_data; + AVStream *st = avformat_new_stream(s, NULL); + AVBPrint buf; + char c = 0; + int res = 0, duration = read_ts("60"); // default duration is 60 seconds + + if (!st) + return AVERROR(ENOMEM); + avpriv_set_pts_info(st, 64, 1, 100); + st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE; + st->codec->codec_id = AV_CODEC_ID_REALTEXT; + + av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED); + + while (!url_feof(s->pb)) { + AVPacket *sub; + const int64_t pos = avio_tell(s->pb) - (c != 0); + int n = ff_smil_extract_next_chunk(s->pb, &buf, &c); + + if (n == 0) + break; + + if (!av_strncasecmp(buf.str, "<window", 7)) { + /* save header to extradata */ + const char *p = ff_smil_get_attr_ptr(buf.str, "duration"); + + if (p) + duration = read_ts(p); + st->codec->extradata = av_strdup(buf.str); + if (!st->codec->extradata) { + res = AVERROR(ENOMEM); + goto end; + } + st->codec->extradata_size = buf.len + 1; + } else { + /* if we just read a <time> tag, introduce a new event, otherwise merge + * with the previous one */ + int merge = !av_strncasecmp(buf.str, "<time", 5) ? 0 : 1; + sub = ff_subtitles_queue_insert(&rt->q, buf.str, buf.len, merge); + if (!sub) { + res = AVERROR(ENOMEM); + goto end; + } + if (!merge) { + const char *begin = ff_smil_get_attr_ptr(buf.str, "begin"); + const char *end = ff_smil_get_attr_ptr(buf.str, "end"); + + sub->pos = pos; + sub->pts = begin ? read_ts(begin) : 0; + sub->duration = end ? (read_ts(end) - sub->pts) : duration; + } + } + av_bprint_clear(&buf); + } + ff_subtitles_queue_finalize(&rt->q); + +end: + av_bprint_finalize(&buf, NULL); + return res; +} + +static int realtext_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + RealTextContext *rt = s->priv_data; + return ff_subtitles_queue_read_packet(&rt->q, pkt); +} + +static int realtext_read_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_close = realtext_read_close, + .flags = AVFMT_GENERIC_INDEX, + .extensions = "rt", +}; diff --git a/libavformat/riff.c b/libavformat/riff.c index f44feff9a5..b20630c3ba 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,9 +25,13 @@ #include "avio_internal.h" #include "riff.h" #include "libavcodec/bytestream.h" +#include "libavutil/avassert.h" /* Note: when encoding, the first matching tag is used, so order is - important if multiple tags possible for a given codec. */ + important if multiple tags 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') }, @@ -36,6 +40,7 @@ const AVCodecTag ff_codec_bmp_tags[] = { { AV_CODEC_ID_H264, MKTAG('a', 'v', 'c', '1') }, { AV_CODEC_ID_H264, MKTAG('D', 'A', 'V', 'C') }, { AV_CODEC_ID_H264, MKTAG('V', 'S', 'S', 'H') }, + { AV_CODEC_ID_H264, MKTAG('Q', '2', '6', '4') }, /* QNAP surveillance system */ { 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') }, @@ -46,8 +51,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 h263 */ { 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') }, @@ -86,8 +90,11 @@ const AVCodecTag ff_codec_bmp_tags[] = { { AV_CODEC_ID_MPEG4, MKTAG('U', 'L', 'D', 'X') }, { AV_CODEC_ID_MPEG4, MKTAG('G', 'E', 'O', 'V') }, { AV_CODEC_ID_MPEG4, MKTAG('S', 'I', 'P', 'P') }, /* Samsung SHR-6040 */ + { AV_CODEC_ID_MPEG4, MKTAG('S', 'M', '4', 'V') }, { AV_CODEC_ID_MPEG4, MKTAG('X', 'V', 'I', 'X') }, { AV_CODEC_ID_MPEG4, MKTAG('D', 'r', 'e', 'X') }, + { AV_CODEC_ID_MPEG4, MKTAG('Q', 'M', 'P', '4') }, /* QNAP Systems */ + { AV_CODEC_ID_MPEG4, MKTAG('P', 'L', 'V', '1') }, /* Pelco DVR MPEG-4 */ { AV_CODEC_ID_MSMPEG4V3, MKTAG('M', 'P', '4', '3') }, { AV_CODEC_ID_MSMPEG4V3, MKTAG('D', 'I', 'V', '3') }, { AV_CODEC_ID_MSMPEG4V3, MKTAG('M', 'P', 'G', '3') }, @@ -116,6 +123,10 @@ const AVCodecTag ff_codec_bmp_tags[] = { { AV_CODEC_ID_DVVIDEO, MKTAG('d', 'v', 'c', ' ') }, { AV_CODEC_ID_DVVIDEO, MKTAG('d', 'v', 'c', 's') }, { AV_CODEC_ID_DVVIDEO, MKTAG('d', 'v', 'h', '1') }, + { AV_CODEC_ID_DVVIDEO, MKTAG('d', 'v', 'i', 's') }, + { AV_CODEC_ID_DVVIDEO, MKTAG('p', 'd', 'v', 'c') }, + { AV_CODEC_ID_DVVIDEO, MKTAG('S', 'L', '2', '5') }, + { AV_CODEC_ID_DVVIDEO, MKTAG('S', 'L', 'D', 'V') }, { AV_CODEC_ID_MPEG1VIDEO, MKTAG('m', 'p', 'g', '1') }, { AV_CODEC_ID_MPEG1VIDEO, MKTAG('m', 'p', 'g', '2') }, { AV_CODEC_ID_MPEG2VIDEO, MKTAG('m', 'p', 'g', '2') }, @@ -133,6 +144,8 @@ const AVCodecTag ff_codec_bmp_tags[] = { { AV_CODEC_ID_MPEG2VIDEO, MKTAG('E', 'M', '2', 'V') }, { AV_CODEC_ID_MPEG2VIDEO, MKTAG('M', '7', '0', '1') }, /* Matrox MPEG2 intra-only */ { AV_CODEC_ID_MPEG2VIDEO, MKTAG('m', 'p', 'g', 'v') }, + { AV_CODEC_ID_MPEG1VIDEO, MKTAG('B', 'W', '1', '0') }, + { AV_CODEC_ID_MPEG1VIDEO, MKTAG('X', 'M', 'P', 'G') }, /* Xing MPEG intra only */ { AV_CODEC_ID_MJPEG, MKTAG('M', 'J', 'P', 'G') }, { AV_CODEC_ID_MJPEG, MKTAG('L', 'J', 'P', 'G') }, { AV_CODEC_ID_MJPEG, MKTAG('d', 'm', 'b', '1') }, @@ -144,7 +157,7 @@ const AVCodecTag ff_codec_bmp_tags[] = { { AV_CODEC_ID_MJPEG, MKTAG('M', 'J', 'L', 'S') }, /* JPEG-LS custom FOURCC for avi - decoder */ { 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') }, { AV_CODEC_ID_MJPEG, MKTAG('S', 'L', 'M', 'J') }, /* SL M-JPEG */ @@ -193,11 +206,16 @@ const AVCodecTag ff_codec_bmp_tags[] = { { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', 'U', 'V', '9') }, { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', 'V', 'U', '9') }, { AV_CODEC_ID_RAWVIDEO, MKTAG('a', 'u', 'v', '2') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', 'V', 'Y', 'U') }, { AV_CODEC_ID_FRWU, MKTAG('F', 'R', 'W', 'U') }, { AV_CODEC_ID_R10K, MKTAG('R', '1', '0', 'k') }, { AV_CODEC_ID_R210, MKTAG('r', '2', '1', '0') }, { AV_CODEC_ID_V210, MKTAG('v', '2', '1', '0') }, + { AV_CODEC_ID_V308, MKTAG('v', '3', '0', '8') }, + { AV_CODEC_ID_V408, MKTAG('v', '4', '0', '8') }, + { AV_CODEC_ID_AYUV, MKTAG('A', 'Y', 'U', 'V') }, { AV_CODEC_ID_V410, MKTAG('v', '4', '1', '0') }, + { AV_CODEC_ID_YUV4, MKTAG('y', 'u', 'v', '4') }, { AV_CODEC_ID_INDEO3, MKTAG('I', 'V', '3', '1') }, { AV_CODEC_ID_INDEO3, MKTAG('I', 'V', '3', '2') }, { AV_CODEC_ID_INDEO4, MKTAG('I', 'V', '4', '1') }, @@ -234,6 +252,7 @@ const AVCodecTag ff_codec_bmp_tags[] = { { AV_CODEC_ID_SNOW, MKTAG('S', 'N', 'O', 'W') }, { AV_CODEC_ID_4XM, MKTAG('4', 'X', 'M', 'V') }, { AV_CODEC_ID_FLV1, MKTAG('F', 'L', 'V', '1') }, + { AV_CODEC_ID_FLV1, MKTAG('S', '2', '6', '3') }, { AV_CODEC_ID_FLASHSV, MKTAG('F', 'S', 'V', '1') }, { AV_CODEC_ID_SVQ1, MKTAG('s', 'v', 'q', '1') }, { AV_CODEC_ID_TSCC, MKTAG('t', 's', 'c', 'c') }, @@ -249,6 +268,8 @@ const AVCodecTag ff_codec_bmp_tags[] = { { AV_CODEC_ID_VC1IMAGE, MKTAG('W', 'V', 'P', '2') }, { AV_CODEC_ID_LOCO, MKTAG('L', 'O', 'C', 'O') }, { AV_CODEC_ID_WNV1, MKTAG('W', 'N', 'V', '1') }, + { AV_CODEC_ID_WNV1, MKTAG('Y', 'U', 'V', '8') }, + { AV_CODEC_ID_AASC, MKTAG('A', 'A', 'S', '4') }, { AV_CODEC_ID_AASC, MKTAG('A', 'A', 'S', 'C') }, { AV_CODEC_ID_INDEO2, MKTAG('R', 'T', '2', '1') }, { AV_CODEC_ID_FRAPS, MKTAG('F', 'P', 'S', '1') }, @@ -262,6 +283,7 @@ const AVCodecTag ff_codec_bmp_tags[] = { { AV_CODEC_ID_JPEG2000, MKTAG('M', 'J', '2', 'C') }, { AV_CODEC_ID_JPEG2000, MKTAG('L', 'J', '2', 'C') }, { AV_CODEC_ID_JPEG2000, MKTAG('L', 'J', '2', 'K') }, + { AV_CODEC_ID_JPEG2000, MKTAG('I', 'P', 'J', '2') }, { AV_CODEC_ID_VMNC, MKTAG('V', 'M', 'n', 'c') }, { AV_CODEC_ID_TARGA, MKTAG('t', 'g', 'a', ' ') }, { AV_CODEC_ID_PNG, MKTAG('M', 'P', 'N', 'G') }, @@ -277,13 +299,21 @@ 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_G2M, MKTAG('G', '2', 'M', '2') }, + { AV_CODEC_ID_G2M, MKTAG('G', '2', 'M', '3') }, + { AV_CODEC_ID_G2M, MKTAG('G', '2', 'M', '4') }, + { 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') }, { 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_EXR, MKTAG('e', 'x', 'r', ' ') }, { AV_CODEC_ID_MSS1, MKTAG('M', 'S', 'S', '1') }, { AV_CODEC_ID_MSA1, MKTAG('M', 'S', 'A', '1') }, { AV_CODEC_ID_TSCC2, MKTAG('T', 'S', 'C', '2') }, @@ -310,6 +340,8 @@ const AVCodecTag ff_codec_wav_tags[] = { { AV_CODEC_ID_ADPCM_YAMAHA, 0x0020 }, { AV_CODEC_ID_TRUESPEECH, 0x0022 }, { AV_CODEC_ID_GSM_MS, 0x0031 }, + { AV_CODEC_ID_AMR_NB, 0x0038 }, /* rogue format number */ + { AV_CODEC_ID_G723_1, 0x0042 }, { AV_CODEC_ID_ADPCM_G726, 0x0045 }, { AV_CODEC_ID_MP2, 0x0050 }, { AV_CODEC_ID_MP3, 0x0055 }, @@ -336,9 +368,12 @@ const AVCodecTag ff_codec_wav_tags[] = { { AV_CODEC_ID_AAC_LATM, 0x1602 }, { AV_CODEC_ID_AC3, 0x2000 }, { AV_CODEC_ID_DTS, 0x2001 }, + { AV_CODEC_ID_SONIC, 0x2048 }, + { AV_CODEC_ID_SONIC_LS, 0x2048 }, { AV_CODEC_ID_PCM_MULAW, 0x6c75 }, { AV_CODEC_ID_AAC, 0x706d }, { AV_CODEC_ID_AAC, 0x4143 }, + { AV_CODEC_ID_G723_1, 0xA100 }, { AV_CODEC_ID_SPEEX, 0xA109 }, { AV_CODEC_ID_FLAC, 0xF1AC }, { AV_CODEC_ID_ADPCM_SWF, ('S'<<8)+'F' }, @@ -352,6 +387,14 @@ const AVCodecTag ff_codec_wav_tags[] = { { AV_CODEC_ID_NONE, 0 }, }; +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} +}; + const AVMetadataConv ff_riff_info_conv[] = { { "IART", "artist" }, { "ICMT", "comment" }, @@ -363,6 +406,7 @@ const AVMetadataConv ff_riff_info_conv[] = { { "IPRD", "album" }, { "IPRT", "track" }, { "ISFT", "encoder" }, + { "ISMP", "timecode" }, { "ITCH", "encoded_by"}, { 0 }, }; @@ -370,7 +414,7 @@ const AVMetadataConv ff_riff_info_conv[] = { const char ff_riff_tags[][5] = { "IARL", "IART", "ICMS", "ICMT", "ICOP", "ICRD", "ICRP", "IDIM", "IDPI", "IENG", "IGNR", "IKEY", "ILGT", "ILNG", "IMED", "INAM", "IPLT", "IPRD", - "IPRT", "ISBJ", "ISFT", "ISHP", "ISRC", "ISRF", "ITCH", + "IPRT", "ISBJ", "ISFT", "ISHP", "ISMP", "ISRC", "ISRF", "ITCH", {0} }; @@ -424,7 +468,11 @@ 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 || enc->codec_id == AV_CODEC_ID_GSM_MS) { + if (enc->codec_id == CODEC_ID_ATRAC3 || + enc->codec_id == CODEC_ID_G723_1 || + enc->codec_id == CODEC_ID_GSM_MS || + enc->codec_id == CODEC_ID_MP2 || + enc->codec_id == CODEC_ID_MP3) { bps = 0; } else { if (!(bps = av_get_bits_per_sample(enc->codec_id))) { @@ -445,6 +493,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 @@ -456,6 +508,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; } @@ -479,6 +533,11 @@ int ff_put_wav_header(AVIOContext *pb, AVCodecContext *enc) bytestream_put_le16(&riff_extradata, 16); /* fwHeadFlags */ bytestream_put_le32(&riff_extradata, 0); /* dwPTSLow */ bytestream_put_le32(&riff_extradata, 0); /* dwPTSHigh */ + } else if (enc->codec_id == AV_CODEC_ID_G723_1) { + hdrsize += 20; + bytestream_put_le32(&riff_extradata, 0x9ace0002); /* extradata needed for msacm g723.1 codec */ + bytestream_put_le32(&riff_extradata, 0xaea2f732); + bytestream_put_le16(&riff_extradata, 0xacde); } else if (enc->codec_id == AV_CODEC_ID_GSM_MS || enc->codec_id == AV_CODEC_ID_ADPCM_IMA_WAV) { hdrsize += 2; bytestream_put_le16(&riff_extradata, frame_size); /* wSamplesPerBlock */ @@ -520,7 +579,7 @@ void ff_put_bmp_header(AVIOContext *pb, AVCodecContext *enc, const AVCodecTag *t avio_wl16(pb, enc->bits_per_coded_sample ? enc->bits_per_coded_sample : 24); /* depth */ /* 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); @@ -624,7 +683,6 @@ int ff_get_wav_header(AVIOContext *pb, AVCodecContext *codec, int size) id = avio_rl16(pb); codec->codec_type = AVMEDIA_TYPE_AUDIO; - codec->codec_tag = id; codec->channels = avio_rl16(pb); codec->sample_rate = avio_rl32(pb); codec->bit_rate = avio_rl32(pb) * 8; @@ -633,15 +691,31 @@ int ff_get_wav_header(AVIOContext *pb, AVCodecContext *codec, int size) codec->bits_per_coded_sample = 8; }else codec->bits_per_coded_sample = avio_rl16(pb); + if (id == 0xFFFE) { + codec->codec_tag = 0; + } else { + codec->codec_tag = id; + codec->codec_id = ff_wav_codec_get_id(id, codec->bits_per_coded_sample); + } if (size >= 18) { /* We're obviously dealing with WAVEFORMATEX */ int cbSize = avio_rl16(pb); /* cbSize */ size -= 18; cbSize = FFMIN(size, cbSize); if (cbSize >= 22 && id == 0xfffe) { /* WAVEFORMATEXTENSIBLE */ - codec->bits_per_coded_sample = avio_rl16(pb); + ff_asf_guid subformat; + int bps = avio_rl16(pb); + if (bps) + codec->bits_per_coded_sample = bps; codec->channel_layout = avio_rl32(pb); /* dwChannelMask */ - id = avio_rl32(pb); /* 4 first bytes of GUID */ - avio_skip(pb, 12); /* skip end of GUID */ + ff_get_guid(pb, &subformat); + if (!memcmp(subformat + 4, (const uint8_t[]){FF_MEDIASUBTYPE_BASE_GUID}, 12)) { + codec->codec_tag = AV_RL32(subformat); + codec->codec_id = ff_wav_codec_get_id(codec->codec_tag, codec->bits_per_coded_sample); + } else { + codec->codec_id = ff_codec_guid_get_id(ff_codec_wav_guids, subformat); + if (!codec->codec_id) + av_log(codec, AV_LOG_WARNING, "unknown subformat:"FF_PRI_GUID"\n", FF_ARG_GUID(subformat)); + } cbSize -= 22; size -= 22; } @@ -659,14 +733,13 @@ int ff_get_wav_header(AVIOContext *pb, AVCodecContext *codec, int size) if (size > 0) avio_skip(pb, size); } - codec->codec_id = ff_wav_codec_get_id(id, codec->bits_per_coded_sample); if (codec->codec_id == AV_CODEC_ID_AAC_LATM) { /* channels and sample_rate values are those prior to applying SBR and/or PS */ codec->channels = 0; 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; @@ -693,10 +766,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 */ @@ -710,6 +784,23 @@ int ff_get_bmp_header(AVIOContext *pb, AVStream *st) return tag1; } +void ff_get_guid(AVIOContext *s, ff_asf_guid *g) +{ + av_assert0(sizeof(*g) == 16); //compiler will optimize this out + if (avio_read(s, *g, sizeof(*g)) < (int)sizeof(*g)) + memset(*g, 0, sizeof(*g)); +} + +enum AVCodecID ff_codec_guid_get_id(const AVCodecGuid *guids, ff_asf_guid guid) +{ + int i; + for (i = 0; guids[i].id != AV_CODEC_ID_NONE; i++) { + if (!ff_guidcmp(guids[i].guid, guid)) + return guids[i].id; + } + return AV_CODEC_ID_NONE; +} + int ff_read_riff_info(AVFormatContext *s, int64_t size) { int64_t start, end, cur; @@ -727,13 +818,18 @@ int ff_read_riff_info(AVFormatContext *s, int64_t size) 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_ERROR, "too big INFO subchunk\n"); - return AVERROR_INVALIDDATA; + 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_ERROR, "too big INFO subchunk\n"); + return AVERROR_INVALIDDATA; + } } chunk_size += (chunk_size & 1); - 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"); return AVERROR(ENOMEM); @@ -742,13 +838,9 @@ 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_ERROR, "premature end of file while reading INFO tag\n"); - return AVERROR_INVALIDDATA; + av_log(s, AV_LOG_WARNING, "premature end of file while reading INFO tag\n"); } - value[chunk_size] = 0; - av_dict_set(&s->metadata, key, value, AV_DICT_DONT_STRDUP_VAL); } diff --git a/libavformat/riff.h b/libavformat/riff.h index 167d8fc49b..6f2eeac176 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 */ @@ -44,7 +44,7 @@ void ff_end_tag(AVIOContext *pb, int64_t start); * bits_per_encoded_sample fields. Does not read extradata. * @return codec tag */ -int ff_get_bmp_header(AVIOContext *pb, AVStream *st); +int ff_get_bmp_header(AVIOContext *pb, AVStream *st, unsigned *esize); void ff_put_bmp_header(AVIOContext *pb, AVCodecContext *enc, const AVCodecTag *tags, int for_asf); int ff_put_wav_header(AVIOContext *pb, AVCodecContext *enc); @@ -58,8 +58,34 @@ unsigned int ff_codec_get_tag(const AVCodecTag *tags, enum AVCodecID id); enum AVCodecID ff_codec_get_id(const AVCodecTag *tags, unsigned int tag); void ff_parse_specific_params(AVCodecContext *stream, int *au_rate, int *au_ssize, int *au_scale); +typedef uint8_t ff_asf_guid[16]; + int ff_read_riff_info(AVFormatContext *s, int64_t size); +#define FF_PRI_GUID \ + "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x" +#define FF_ARG_GUID(g) \ + g[0],g[1],g[2],g[3],g[4],g[5],g[6],g[7],g[8],g[9],g[10],g[11],g[12],g[13],g[14],g[15] + +static av_always_inline int ff_guidcmp(const void *g1, const void *g2) +{ + return memcmp(g1, g2, sizeof(ff_asf_guid)); +} + +void ff_get_guid(AVIOContext *s, ff_asf_guid *g); + +typedef struct { + enum AVCodecID id; + ff_asf_guid guid; +} AVCodecGuid; + +enum AVCodecID ff_codec_guid_get_id(const AVCodecGuid *guids, ff_asf_guid guid); + +extern const AVCodecGuid ff_codec_wav_guids[]; + +#define FF_MEDIASUBTYPE_BASE_GUID \ + 0x00,0x00,0x10,0x00,0x80,0x00,0x00,0xAA,0x00,0x38,0x9B,0x71 + /** * Write all recognized RIFF tags from s->metadata */ diff --git a/libavformat/rl2.c b/libavformat/rl2.c index ac0532f1c9..73d6b2767a 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 */ @@ -136,6 +136,9 @@ static av_cold int rl2_read_header(AVFormatContext *s) /** setup audio stream if present */ if(sound_rate){ + if(channels <= 0) + return AVERROR_INVALIDDATA; + pts_num = def_sound_size; pts_den = rate; diff --git a/libavformat/rm.c b/libavformat/rm.c index 761be3f556..0591a1735a 100644 --- a/libavformat/rm.c +++ b/libavformat/rm.c @@ -2,20 +2,20 @@ * "Real" compatible muxer and demuxer common code. * Copyright (c) 2009 Aurelien Jacobs <aurel@gnuage.org> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/rm.h b/libavformat/rm.h index a06ea01a5a..a72e8b7997 100644 --- a/libavformat/rm.h +++ b/libavformat/rm.h @@ -2,20 +2,20 @@ * "Real" compatible muxer and demuxer. * Copyright (c) 2000, 2001 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -50,7 +50,7 @@ extern AVInputFormat ff_rdt_demuxer; */ int ff_rm_read_mdpr_codecdata (AVFormatContext *s, AVIOContext *pb, AVStream *st, RMStream *rst, - int codec_data_size); + int codec_data_size, const uint8_t *mime); /** * Parse one rm-stream packet from the input bytestream. diff --git a/libavformat/rmdec.c b/libavformat/rmdec.c index 07d218e311..a2c8bfbe9c 100644 --- a/libavformat/rmdec.c +++ b/libavformat/rmdec.c @@ -2,23 +2,24 @@ * "Real" compatible demuxer. * Copyright (c) 2000, 2001 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "libavutil/avassert.h" #include "libavutil/avstring.h" #include "libavutil/intreadwrite.h" #include "libavutil/dict.h" @@ -220,6 +221,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); @@ -283,7 +285,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; @@ -306,11 +308,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); @@ -425,7 +454,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); @@ -440,7 +469,7 @@ static int rm_read_header(AVFormatContext *s) avio_skip(pb, tag_size - 8); for(;;) { - if (pb->eof_reached) + if (url_feof(pb)) return -1; tag = avio_rl32(pb); tag_size = avio_rb32(pb); @@ -462,7 +491,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 */ @@ -486,12 +516,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'): @@ -544,7 +576,7 @@ static int sync(AVFormatContext *s, int64_t *timestamp, int *flags, int *stream_ AVStream *st; uint32_t state=0xFFFFFFFF; - while(!pb->eof_reached){ + while(!url_feof(pb)){ int len, num, i; *pos= avio_tell(pb) - 3; if(rm->remaining_len > 0){ @@ -801,13 +833,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 +868,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) { @@ -854,7 +889,7 @@ static int rm_read_packet(AVFormatContext *s, AVPacket *pkt) st = s->streams[i]; } - if(len<0 || s->pb->eof_reached) + if(len<0 || url_feof(s->pb)) return AVERROR(EIO); res = ff_rm_parse_packet (s, s->pb, st, st->priv_data, len, pkt, @@ -910,7 +945,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; diff --git a/libavformat/rmenc.c b/libavformat/rmenc.c index ed1ba7c75b..6c05657a07 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" @@ -309,6 +309,11 @@ static int rm_write_header(AVFormatContext *s) int n; AVCodecContext *codec; + if (s->nb_streams > 2) { + av_log(s, AV_LOG_ERROR, "At most 2 streams are currently supported for muxing in RM\n"); + return AVERROR_PATCHWELCOME; + } + for(n=0;n<s->nb_streams;n++) { s->streams[n]->id = n; codec = s->streams[n]->codec; diff --git a/libavformat/rmsipr.c b/libavformat/rmsipr.c index 89fbd0e398..14ec8ea606 100644 --- a/libavformat/rmsipr.c +++ b/libavformat/rmsipr.c @@ -1,20 +1,20 @@ /* * tables and functions for demuxing SIPR audio muxed RealMedia style * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/rmsipr.h b/libavformat/rmsipr.h index c8d3d0a236..2837336dbb 100644 --- a/libavformat/rmsipr.h +++ b/libavformat/rmsipr.h @@ -1,20 +1,20 @@ /* * tables and functions for demuxing SIPR audio muxed RealMedia style * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/rpl.c b/libavformat/rpl.c index a5b1e6ec69..b790597e33 100644 --- a/libavformat/rpl.c +++ b/libavformat/rpl.c @@ -2,20 +2,20 @@ * ARMovie/RPL demuxer * Copyright (c) 2007 Christian Ohm, 2008 Eli Friedman * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -58,7 +58,7 @@ static int read_line(AVIOContext * pb, char* line, int bufsize) break; if (b == '\n') { line[i] = '\0'; - return 0; + return url_feof(pb) ? -1 : 0; } line[i] = b; } @@ -164,11 +164,9 @@ static int rpl_read_header(AVFormatContext *s) // The header is wrong here, at least sometimes vst->codec->bits_per_coded_sample = 16; break; -#if 0 case 130: vst->codec->codec_id = AV_CODEC_ID_ESCAPE130; break; -#endif default: av_log(s, AV_LOG_WARNING, "RPL video format %i not supported yet!\n", @@ -254,7 +252,7 @@ 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, diff --git a/libavformat/rso.c b/libavformat/rso.c index 4ce61e7a10..7d88f0f5bc 100644 --- a/libavformat/rso.c +++ b/libavformat/rso.c @@ -2,20 +2,20 @@ * RSO format common data * Copyright (c) 2010 Rafael Carre * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/rso.h b/libavformat/rso.h index e3e88ea644..1f65dd90c7 100644 --- a/libavformat/rso.h +++ b/libavformat/rso.h @@ -2,20 +2,20 @@ * RSO format common data * Copyright (c) 2010 Rafael Carre * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/rsodec.c b/libavformat/rsodec.c index f5bcebb7f2..7a22217a63 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 */ @@ -80,11 +80,9 @@ static int rso_read_packet(AVFormatContext *s, AVPacket *pkt) 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/rsoenc.c b/libavformat/rsoenc.c index cc76f9afd2..ee679b68f7 100644 --- a/libavformat/rsoenc.c +++ b/libavformat/rsoenc.c @@ -3,20 +3,20 @@ * Copyright (c) 2001 Fabrice Bellard (original AU code) * Copyright (c) 2010 Rafael Carre * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/rtmp.h b/libavformat/rtmp.h index b9c5f1e430..7c9bb6d766 100644 --- a/libavformat/rtmp.h +++ b/libavformat/rtmp.h @@ -2,20 +2,20 @@ * RTMP definitions * Copyright (c) 2009 Kostya Shishkov * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/rtmpcrypt.c b/libavformat/rtmpcrypt.c index dfdd029a53..2312527d37 100644 --- a/libavformat/rtmpcrypt.c +++ b/libavformat/rtmpcrypt.c @@ -4,20 +4,20 @@ * Copyright (c) 2009-2010 Howard Chu * Copyright (c) 2012 Samuel Pitoiset * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/rtmpcrypt.h b/libavformat/rtmpcrypt.h index 27994333df..590a8c81b8 100644 --- a/libavformat/rtmpcrypt.h +++ b/libavformat/rtmpcrypt.h @@ -2,20 +2,20 @@ * RTMPE encryption utilities * Copyright (c) 2012 Samuel Pitoiset * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/rtmpdh.c b/libavformat/rtmpdh.c index 38c2f3df63..7d6734daac 100644 --- a/libavformat/rtmpdh.c +++ b/libavformat/rtmpdh.c @@ -4,20 +4,20 @@ * Copyright (c) 2009-2010 Howard Chu * Copyright (c) 2012 Samuel Pitoiset * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/rtmpdh.h b/libavformat/rtmpdh.h index 5de8bde821..cf262fc245 100644 --- a/libavformat/rtmpdh.h +++ b/libavformat/rtmpdh.h @@ -2,20 +2,20 @@ * RTMP Diffie-Hellmann utilities * Copyright (c) 2012 Samuel Pitoiset * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/rtmphttp.c b/libavformat/rtmphttp.c index 80a983bd11..d21ac67315 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 f69ce82c66..b607d5b5e2 100644 --- a/libavformat/rtmppkt.c +++ b/libavformat/rtmppkt.c @@ -2,20 +2,20 @@ * RTMP input format * Copyright (c) 2009 Kostya Shishkov * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/rtmppkt.h b/libavformat/rtmppkt.h index a153ca6253..9b588031e7 100644 --- a/libavformat/rtmppkt.h +++ b/libavformat/rtmppkt.h @@ -2,20 +2,20 @@ * RTMP packet utilities * Copyright (c) 2009 Kostya 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 3ab2e57e62..1d00cbb43f 100644 --- a/libavformat/rtmpproto.c +++ b/libavformat/rtmpproto.c @@ -2,20 +2,20 @@ * RTMP network protocol * Copyright (c) 2009 Kostya 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 */ @@ -256,9 +256,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; @@ -1135,7 +1132,7 @@ static int rtmp_handshake(URLContext *s, RTMPContext *rt) for (i = 9; i <= RTMP_HANDSHAKE_PACKET_SIZE; i++) tosend[i] = av_lfg_get(&rnd) >> 24; - if (rt->encrypted && CONFIG_FFRTMPCRYPT_PROTOCOL) { + if (CONFIG_FFRTMPCRYPT_PROTOCOL && rt->encrypted) { /* When the client wants to use RTMPE, we have to change the command * byte to 0x06 which means to use encrypted data and we have to set * the flash version to at least 9.0.115.0. */ @@ -1213,7 +1210,7 @@ static int rtmp_handshake(URLContext *s, RTMPContext *rt) if (ret < 0) return ret; - if (rt->encrypted && CONFIG_FFRTMPCRYPT_PROTOCOL) { + if (CONFIG_FFRTMPCRYPT_PROTOCOL && rt->encrypted) { /* Compute the shared secret key sent by the server and initialize * the RC4 encryption. */ if ((ret = ff_rtmpe_compute_secret_key(rt->stream, serverdata + 1, @@ -1243,7 +1240,7 @@ static int rtmp_handshake(URLContext *s, RTMPContext *rt) if (ret < 0) return ret; - if (rt->encrypted && CONFIG_FFRTMPCRYPT_PROTOCOL) { + if (CONFIG_FFRTMPCRYPT_PROTOCOL && rt->encrypted) { /* Encrypt the signature to be send to the server. */ ff_rtmpe_encrypt_sig(rt->stream, tosend + RTMP_HANDSHAKE_PACKET_SIZE - 32, digest, @@ -1255,13 +1252,13 @@ static int rtmp_handshake(URLContext *s, RTMPContext *rt) RTMP_HANDSHAKE_PACKET_SIZE)) < 0) return ret; - if (rt->encrypted && CONFIG_FFRTMPCRYPT_PROTOCOL) { + if (CONFIG_FFRTMPCRYPT_PROTOCOL && rt->encrypted) { /* Set RC4 keys for encryption and update the keystreams. */ if ((ret = ff_rtmpe_update_keystream(rt->stream)) < 0) return ret; } } else { - if (rt->encrypted && CONFIG_FFRTMPCRYPT_PROTOCOL) { + if (CONFIG_FFRTMPCRYPT_PROTOCOL && rt->encrypted) { /* Compute the shared secret key sent by the server and initialize * the RC4 encryption. */ if ((ret = ff_rtmpe_compute_secret_key(rt->stream, serverdata + 1, @@ -1279,7 +1276,7 @@ static int rtmp_handshake(URLContext *s, RTMPContext *rt) RTMP_HANDSHAKE_PACKET_SIZE)) < 0) return ret; - if (rt->encrypted && CONFIG_FFRTMPCRYPT_PROTOCOL) { + if (CONFIG_FFRTMPCRYPT_PROTOCOL && rt->encrypted) { /* Set RC4 keys for encryption and update the keystreams. */ if ((ret = ff_rtmpe_update_keystream(rt->stream)) < 0) return ret; @@ -1946,7 +1943,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; diff --git a/libavformat/rtp.c b/libavformat/rtp.c index 4314c4631c..f9e444746a 100644 --- a/libavformat/rtp.c +++ b/libavformat/rtp.c @@ -2,20 +2,20 @@ * 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 */ diff --git a/libavformat/rtp.h b/libavformat/rtp.h index 6df4ed4f19..ab2de528a0 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 9a1c4972ca..963a3170e0 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 */ @@ -560,7 +560,8 @@ static int rtp_parse_packet_internal(RTPDemuxContext *s, AVPacket *pkt, h = AV_RB32(buf); len -= 4; buf += 4; - av_new_packet(pkt, len); + if (av_new_packet(pkt, len) < 0) + return AVERROR(ENOMEM); memcpy(pkt->data, buf, len); break; case AV_CODEC_ID_MPEG1VIDEO: @@ -578,11 +579,13 @@ static int rtp_parse_packet_internal(RTPDemuxContext *s, AVPacket *pkt, buf += 4; len -= 4; } - av_new_packet(pkt, len); + if (av_new_packet(pkt, len) < 0) + return AVERROR(ENOMEM); memcpy(pkt->data, buf, len); break; default: - av_new_packet(pkt, len); + if (av_new_packet(pkt, len) < 0) + return AVERROR(ENOMEM); memcpy(pkt->data, buf, len); break; } @@ -785,7 +788,7 @@ int ff_parse_fmtp(AVStream *stream, PayloadContext *data, const char *p, int value_size = strlen(p) + 1; if (!(value = av_malloc(value_size))) { - av_log(NULL, AV_LOG_ERROR, "Failed to allocate data for FMTP."); + av_log(NULL, AV_LOG_ERROR, "Failed to allocate data for FMTP.\n"); return AVERROR(ENOMEM); } diff --git a/libavformat/rtpdec.h b/libavformat/rtpdec.h index 7f14aa2d68..3d7019024a 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 */ #ifndef AVFORMAT_RTPDEC_H diff --git a/libavformat/rtpdec_amr.c b/libavformat/rtpdec_amr.c index cc43e31e6d..6051dce716 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 7c13b967f5..e37fde7906 100644 --- a/libavformat/rtpdec_asf.c +++ b/libavformat/rtpdec_asf.c @@ -2,20 +2,20 @@ * Microsoft RTP/ASF support. * Copyright (c) 2008 Ronald S. Bultje * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/rtpdec_formats.h b/libavformat/rtpdec_formats.h index c538b93cca..471888b703 100644 --- a/libavformat/rtpdec_formats.h +++ b/libavformat/rtpdec_formats.h @@ -2,20 +2,20 @@ * RTP depacketizer declarations * Copyright (c) 2010 Martin Storsjo * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/rtpdec_g726.c b/libavformat/rtpdec_g726.c index adb4ab34cc..ec37f09155 100644 --- a/libavformat/rtpdec_g726.c +++ b/libavformat/rtpdec_g726.c @@ -1,20 +1,20 @@ /* * Copyright (c) 2011 Miroslav Slugeň <Thunder.m@seznam.cz> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/rtpdec_h263.c b/libavformat/rtpdec_h263.c index 78773b9f66..e604dc1c42 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_h264.c b/libavformat/rtpdec_h264.c index 7408b43101..083f8e89b2 100644 --- a/libavformat/rtpdec_h264.c +++ b/libavformat/rtpdec_h264.c @@ -2,20 +2,20 @@ * RTP H264 Protocol (RFC3984) * Copyright (c) 2006 Ryan Martell * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/rtpdec_ilbc.c b/libavformat/rtpdec_ilbc.c index 2e99734f2a..2bd86147de 100644 --- a/libavformat/rtpdec_ilbc.c +++ b/libavformat/rtpdec_ilbc.c @@ -2,20 +2,20 @@ * RTP iLBC Depacketizer, RFC 3952 * Copyright (c) 2012 Martin Storsjo * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/rtpdec_jpeg.c b/libavformat/rtpdec_jpeg.c index 944758d4fc..fb68de9702 100644 --- a/libavformat/rtpdec_jpeg.c +++ b/libavformat/rtpdec_jpeg.c @@ -2,20 +2,20 @@ * RTP JPEG-compressed Video Depacketizer, RFC 2435 * Copyright (c) 2012 Samuel Pitoiset * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/rtpdec_latm.c b/libavformat/rtpdec_latm.c index 3fa04682b8..2ea141b88e 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 */ diff --git a/libavformat/rtpdec_mpeg4.c b/libavformat/rtpdec_mpeg4.c index cc92c88636..a647169e0e 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 */ @@ -130,7 +130,7 @@ static int rtp_parse_mp4_au(PayloadContext *data, const uint8_t *buf) 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_qcelp.c b/libavformat/rtpdec_qcelp.c index 56f3e17819..7e1d0b7972 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 a2ce510d26..3a8ffd5f99 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,6 +26,7 @@ */ #include <string.h> +#include "libavutil/avassert.h" #include "libavutil/intreadwrite.h" #include "libavcodec/avcodec.h" #include "rtp.h" @@ -193,7 +194,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 3093132672..f0f01d9b19 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 bfc602ebb2..99c4c52dee 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 */ diff --git a/libavformat/rtpdec_vp8.c b/libavformat/rtpdec_vp8.c index a52be4b644..aef5f78132 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 38f12bbc39..773f4aa391 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 */ @@ -27,12 +27,11 @@ * @author Josh Allmann <joshua.allmann@gmail.com> */ +#include "libavutil/avassert.h" #include "libavutil/avstring.h" #include "libavutil/base64.h" #include "libavcodec/bytestream.h" -#include <assert.h> - #include "rtpdec.h" #include "rtpdec_formats.h" @@ -183,7 +182,7 @@ static int xiph_handle_packet(AVFormatContext * ctx, 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 diff --git a/libavformat/rtpenc.c b/libavformat/rtpenc.c index b17c4651b6..3a5bbeb398 100644 --- a/libavformat/rtpenc.c +++ b/libavformat/rtpenc.c @@ -2,20 +2,20 @@ * RTP output format * Copyright (c) 2002 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -96,7 +96,7 @@ static int rtp_write_header(AVFormatContext *s1) } st = s1->streams[0]; if (!is_supported(st->codec->codec_id)) { - av_log(s1, AV_LOG_ERROR, "Unsupported codec %x\n", st->codec->codec_id); + av_log(s1, AV_LOG_ERROR, "Unsupported codec %s\n", avcodec_get_name(st->codec->codec_id)); return -1; } diff --git a/libavformat/rtpenc.h b/libavformat/rtpenc.h index 5351b4590c..bc523b442a 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 @@ -89,7 +89,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 86318dfa6e..e19b28697e 100644 --- a/libavformat/rtpenc_aac.c +++ b/libavformat/rtpenc_aac.c @@ -1,20 +1,20 @@ /* * copyright (c) 2007 Luca Abeni * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/rtpenc_amr.c b/libavformat/rtpenc_amr.c index 73da8c8251..bd1c197101 100644 --- a/libavformat/rtpenc_amr.c +++ b/libavformat/rtpenc_amr.c @@ -3,20 +3,20 @@ * Copyright (c) 2007 Luca Abeni * Copyright (c) 2009 Martin Storsjo * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/rtpenc_chain.c b/libavformat/rtpenc_chain.c index 3742099314..c0f9530ac8 100644 --- a/libavformat/rtpenc_chain.c +++ b/libavformat/rtpenc_chain.c @@ -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 */ @@ -57,6 +57,7 @@ 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; 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 66b9e4cd0a..1ba3617188 100644 --- a/libavformat/rtpenc_chain.h +++ b/libavformat/rtpenc_chain.h @@ -2,20 +2,20 @@ * RTP muxer chaining code * Copyright (c) 2010 Martin Storsjo * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/rtpenc_h263.c b/libavformat/rtpenc_h263.c index 87f0bd7981..9cea013013 100644 --- a/libavformat/rtpenc_h263.c +++ b/libavformat/rtpenc_h263.c @@ -3,28 +3,28 @@ * Copyright (c) 2009 Luca Abeni * Copyright (c) 2009 Martin Storsjo * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "avformat.h" #include "rtpenc.h" -const uint8_t *ff_h263_find_resync_marker_reverse(const uint8_t *restrict start, - const uint8_t *restrict end) +const uint8_t *ff_h263_find_resync_marker_reverse(const uint8_t *av_restrict start, + const uint8_t *av_restrict end) { const uint8_t *p = end - 1; start += 1; /* Make sure we never return the original start. */ diff --git a/libavformat/rtpenc_h264.c b/libavformat/rtpenc_h264.c index ac74074307..68f497590b 100644 --- a/libavformat/rtpenc_h264.c +++ b/libavformat/rtpenc_h264.c @@ -2,20 +2,20 @@ * RTP packetization for H.264 (RFC3984) * Copyright (c) 2008 Luca Abeni * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/rtpenc_jpeg.c b/libavformat/rtpenc_jpeg.c index 04df6583df..7eb0e23c6f 100644 --- a/libavformat/rtpenc_jpeg.c +++ b/libavformat/rtpenc_jpeg.c @@ -2,20 +2,20 @@ * RTP JPEG-compressed video Packetizer, RFC 2435 * Copyright (c) 2012 Samuel Pitoiset * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/rtpenc_latm.c b/libavformat/rtpenc_latm.c index 64676771a7..7535a0fd5f 100644 --- a/libavformat/rtpenc_latm.c +++ b/libavformat/rtpenc_latm.c @@ -2,9 +2,9 @@ * 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. diff --git a/libavformat/rtpenc_mpv.c b/libavformat/rtpenc_mpv.c index 37dedc33f5..2708dd183c 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 1730379874..d16e6764c9 100644 --- a/libavformat/rtpenc_vp8.c +++ b/libavformat/rtpenc_vp8.c @@ -2,20 +2,20 @@ * RTP VP8 Packetizer * Copyright (c) 2010 Josh Allmann * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/rtpenc_xiph.c b/libavformat/rtpenc_xiph.c index 07086b1a12..bc40cf88f8 100644 --- a/libavformat/rtpenc_xiph.c +++ b/libavformat/rtpenc_xiph.c @@ -2,23 +2,24 @@ * RTP packetization for Xiph audio and video * Copyright (c) 2010 Josh Allmann * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "libavutil/avassert.h" #include "avformat.h" #include "rtpenc.h" @@ -72,7 +73,7 @@ void ff_rtp_send_xiph(AVFormatContext *s1, const uint8_t *buff, int size) uint8_t *ptr = s->buf_ptr + 2 + size; // what we're going to write int remaining = end_ptr - ptr; - assert(s->num_frames <= s->max_frames_per_packet); + av_assert1(s->num_frames <= s->max_frames_per_packet); if ((s->num_frames > 0 && remaining < 0) || s->num_frames == s->max_frames_per_packet) { // send previous packets now; no room for new data diff --git a/libavformat/rtpproto.c b/libavformat/rtpproto.c index aca6a5a849..17b0f64afb 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 */ @@ -113,6 +113,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"); } /** diff --git a/libavformat/rtsp.c b/libavformat/rtsp.c index bbe08e686e..ec9634f9d2 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" @@ -628,7 +629,7 @@ int ff_rtsp_open_transport_ctx(AVFormatContext *s, RTSPStream *rtsp_st) s->ctx_flags |= AVFMTCTX_NOHEADER; if (s->oformat && CONFIG_RTSP_MUXER) { - int ret = ff_rtp_chain_mux_open(&rtsp_st->transport_priv, s, st, + int ret = ff_rtp_chain_mux_open((AVFormatContext **)&rtsp_st->transport_priv, s, st, rtsp_st->rtp_handle, RTSP_TCP_MAX_PACKET_SIZE); /* Ownership of rtp_handle is passed to the rtp mux context */ @@ -1223,10 +1224,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. */ @@ -1280,7 +1277,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; diff --git a/libavformat/rtsp.h b/libavformat/rtsp.h index 043b67aa6f..3a5ad7117f 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 @@ -71,7 +71,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 diff --git a/libavformat/rtspcodes.h b/libavformat/rtspcodes.h index 31ab33699c..4245e48642 100644 --- a/libavformat/rtspcodes.h +++ b/libavformat/rtspcodes.h @@ -2,20 +2,20 @@ * RTSP definitions * copyright (c) 2002 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/rtspdec.c b/libavformat/rtspdec.c index 7e7fb2dcfd..da571a8ee8 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 */ @@ -737,7 +737,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 9b3c7c937e..bad6fbd3a2 100644 --- a/libavformat/rtspenc.c +++ b/libavformat/rtspenc.c @@ -2,20 +2,20 @@ * RTSP muxer * Copyright (c) 2010 Martin Storsjo * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/samidec.c b/libavformat/samidec.c new file mode 100644 index 0000000000..5f29364028 --- /dev/null +++ b/libavformat/samidec.c @@ -0,0 +1,132 @@ +/* + * 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 "libavutil/avstring.h" +#include "libavutil/bprint.h" +#include "libavutil/intreadwrite.h" + +typedef struct { + FFDemuxSubtitlesQueue q; +} SAMIContext; + +static int sami_probe(AVProbeData *p) +{ + const unsigned char *ptr = p->buf; + + if (AV_RB24(ptr) == 0xEFBBBF) + ptr += 3; /* skip UTF-8 BOM */ + return !strncmp(ptr, "<SAMI>", 6) ? AVPROBE_SCORE_MAX : 0; +} + +static int sami_read_header(AVFormatContext *s) +{ + SAMIContext *sami = s->priv_data; + AVStream *st = avformat_new_stream(s, NULL); + AVBPrint buf, hdr_buf; + char c = 0; + int res = 0, got_first_sync_point = 0; + + if (!st) + return AVERROR(ENOMEM); + avpriv_set_pts_info(st, 64, 1, 1000); + st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE; + st->codec->codec_id = AV_CODEC_ID_SAMI; + + av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED); + av_bprint_init(&hdr_buf, 0, AV_BPRINT_SIZE_UNLIMITED); + + while (!url_feof(s->pb)) { + AVPacket *sub; + const int64_t pos = avio_tell(s->pb) - (c != 0); + int is_sync, n = ff_smil_extract_next_chunk(s->pb, &buf, &c); + + if (n == 0) + break; + + is_sync = !av_strncasecmp(buf.str, "<SYNC", 5); + if (is_sync) + got_first_sync_point = 1; + + if (!got_first_sync_point) { + av_bprintf(&hdr_buf, "%s", buf.str); + } else { + sub = ff_subtitles_queue_insert(&sami->q, buf.str, buf.len, !is_sync); + if (!sub) { + res = AVERROR(ENOMEM); + goto end; + } + if (is_sync) { + const char *p = ff_smil_get_attr_ptr(buf.str, "Start"); + sub->pos = pos; + sub->pts = p ? strtol(p, NULL, 10) : 0; + sub->duration = -1; + } + } + av_bprint_clear(&buf); + } + + st->codec->extradata_size = hdr_buf.len + 1; + av_bprint_finalize(&hdr_buf, (char **)&st->codec->extradata); + if (!st->codec->extradata) { + st->codec->extradata_size = 0; + res = AVERROR(ENOMEM); + 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_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_close = sami_read_close, + .flags = AVFMT_GENERIC_INDEX, + .extensions = "smi,sami", +}; diff --git a/libavformat/sapdec.c b/libavformat/sapdec.c index c1cdc8fb72..ed3e8578d5 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 1575848b3a..3de615d25d 100644 --- a/libavformat/sapenc.c +++ b/libavformat/sapenc.c @@ -2,20 +2,20 @@ * Session Announcement Protocol (RFC 2974) muxer * Copyright (c) 2010 Martin Storsjo * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/sauce.c b/libavformat/sauce.c index a125241335..21cc95bccb 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 */ diff --git a/libavformat/sauce.h b/libavformat/sauce.h index 62d8e688a4..0ba9ae5b4a 100644 --- a/libavformat/sauce.h +++ b/libavformat/sauce.h @@ -2,20 +2,20 @@ * SAUCE header parser * Copyright (c) 2010 Peter Ross <pross@xvid.org> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/sbgdec.c b/libavformat/sbgdec.c new file mode 100644 index 0000000000..93c8cb3fa7 --- /dev/null +++ b/libavformat/sbgdec.c @@ -0,0 +1,1513 @@ +/* + * 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; + + 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, 16, "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, 16, "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); + } + edata = av_malloc(edata_size); + if (!edata) + return AVERROR(ENOMEM); + avc->extradata = edata; + avc->extradata_size = edata_size; + +#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/sdp.c b/libavformat/sdp.c index 0f7eb2f00d..c4edd783be 100644 --- a/libavformat/sdp.c +++ b/libavformat/sdp.c @@ -1,20 +1,20 @@ /* * copyright (c) 2007 Luca Abeni * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -438,7 +438,7 @@ static char *sdp_write_media_attributes(char *buff, int size, AVCodecContext *c, payload_type, config ? config : ""); break; case AV_CODEC_ID_AAC: - if (fmt && fmt->oformat->priv_class && + if (fmt && fmt->oformat && fmt->oformat->priv_class && av_opt_flag_is_set(fmt->priv_data, "rtpflags", "latm")) { config = latm_context2config(c); if (!config) @@ -575,6 +575,20 @@ static char *sdp_write_media_attributes(char *buff, int size, AVCodecContext *c, case AV_CODEC_ID_SPEEX: av_strlcatf(buff, size, "a=rtpmap:%d speex/%d\r\n", payload_type, c->sample_rate); + if (c->codec) { + const char *mode; + uint64_t vad_option; + + if (c->flags & CODEC_FLAG_QSCALE) + mode = "on"; + else if (!av_opt_get_int(c, "vad", AV_OPT_FLAG_ENCODING_PARAM, &vad_option) && vad_option) + mode = "vad"; + else + mode = "off"; + + av_strlcatf(buff, size, "a=fmtp:%d vbr=%s\r\n", + payload_type, mode); + } break; case AV_CODEC_ID_OPUS: av_strlcatf(buff, size, "a=rtpmap:%d opus/48000\r\n", diff --git a/libavformat/seek-test.c b/libavformat/seek-test.c index 57d3fa4da5..49ac3ac06d 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 */ @@ -48,13 +48,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) @@ -62,8 +61,25 @@ 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; + + 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 { + argc = 1; + } + } av_dict_set(&format_opts, "channels", "1", 0); av_dict_set(&format_opts, "sample_rate", "22050", 0); @@ -71,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; @@ -92,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]; @@ -109,6 +130,7 @@ int main(int argc, char **argv) } else printf("ret:%s", ret_str(ret)); // necessary to avoid trailing whitespace printf("\n"); + } } if(i>25) break; diff --git a/libavformat/seek.c b/libavformat/seek.c index 524cd87e6a..0ae99eb211 100644 --- a/libavformat/seek.c +++ b/libavformat/seek.c @@ -3,20 +3,20 @@ * * Copyright (c) 2009 Ivan Schreter * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/seek.h b/libavformat/seek.h index e79d7bd69e..b27cb42a9d 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 068d432083..1be2c5d042 100644 --- a/libavformat/segafilm.c +++ b/libavformat/segafilm.c @@ -2,20 +2,20 @@ * Sega FILM Format (CPK) Demuxer * Copyright (c) 2003 The ffmpeg Project * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -30,6 +30,7 @@ #include "libavutil/intreadwrite.h" #include "avformat.h" #include "internal.h" +#include "avio_internal.h" #define FILM_TAG MKBETAG('F', 'I', 'L', 'M') #define FDSC_TAG MKBETAG('F', 'D', 'S', 'C') @@ -266,6 +267,8 @@ static int film_read_packet(AVFormatContext *s, (film->audio_type != AV_CODEC_ID_ADPCM_ADX)) { /* stereo PCM needs to be interleaved */ + if (ffio_limit(pb, sample->sample_size) != sample->sample_size) + return AVERROR(EIO); if (av_new_packet(pkt, sample->sample_size)) return AVERROR(ENOMEM); @@ -287,7 +290,7 @@ static int film_read_packet(AVFormatContext *s, left = 0; right = sample->sample_size / 2; - for (i = 0; i < sample->sample_size; ) { + for (i = 0; i + 1 + 2*(film->audio_bits != 8) < sample->sample_size; ) { if (film->audio_bits == 8) { pkt->data[i++] = film->stereo_buffer[left++]; pkt->data[i++] = film->stereo_buffer[right++]; diff --git a/libavformat/segment.c b/libavformat/segment.c index fd5283560e..b6697e0940 100644 --- a/libavformat/segment.c +++ b/libavformat/segment.c @@ -1,5 +1,4 @@ /* - * Generic segmenter * Copyright (c) 2011, Luca Barbato * * This file is part of Libav. @@ -19,40 +18,80 @@ * 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-08.txt} + */ + #include <float.h> #include "avformat.h" #include "internal.h" +#include "libavutil/avassert.h" #include "libavutil/log.h" #include "libavutil/opt.h" #include "libavutil/avstring.h" #include "libavutil/parseutils.h" #include "libavutil/mathematics.h" +typedef enum { + LIST_TYPE_UNDEFINED = -1, + LIST_TYPE_FLAT = 0, + LIST_TYPE_CSV, + LIST_TYPE_M3U8, + LIST_TYPE_EXT, ///< deprecated + LIST_TYPE_NB, +} ListType; + + +#define SEGMENT_LIST_FLAG_CACHE 1 +#define SEGMENT_LIST_FLAG_LIVE 2 + typedef struct { const AVClass *class; /**< Class for private options. */ - int number; + int segment_idx; ///< index of the segment file to write, starting from 0 + int segment_idx_wrap; ///< number after which the index wraps + int segment_count; ///< number of segment files already written AVOutputFormat *oformat; AVFormatContext *avf; - char *format; /**< Set by a private option. */ - char *list; /**< Set by a private option. */ - int list_type; /**< Set by a private option. */ - float time; /**< Set by a private option. */ - int size; /**< Set by a private option. */ - int wrap; /**< Set by a private option. */ + char *format; ///< format to use for output segment files + char *list; ///< filename for the segment list file + int list_count; ///< list counter + int list_flags; ///< flags affecting list generation + int list_size; ///< number of entries for the segment list file + double list_max_segment_time; ///< max segment time in the current list + 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 *time_delta_str; ///< approximation value duration used for the segment times + 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; + double start_time, end_time; } 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) { @@ -78,36 +117,6 @@ static int segment_mux_init(AVFormatContext *s) return 0; } -static int segment_hls_window(AVFormatContext *s, int last) -{ - 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; - - avio_printf(seg->pb, "#EXTM3U\n"); - avio_printf(seg->pb, "#EXT-X-VERSION:3\n"); - avio_printf(seg->pb, "#EXT-X-TARGETDURATION:%d\n", (int)seg->time); - avio_printf(seg->pb, "#EXT-X-MEDIA-SEQUENCE:%d\n", - FFMAX(0, seg->number - seg->size)); - - for (i = FFMAX(0, seg->number - seg->size); - i < seg->number; i++) { - avio_printf(seg->pb, "#EXTINF:%d,\n", (int)seg->time); - av_get_frame_filename(buf, sizeof(buf), s->filename, i); - avio_printf(seg->pb, "%s\n", buf); - } - - if (last) - avio_printf(seg->pb, "#EXT-X-ENDLIST\n"); -fail: - avio_closep(&seg->pb); - return ret; -} - static int segment_start(AVFormatContext *s, int write_header) { SegmentContext *c = s->priv_data; @@ -122,12 +131,15 @@ static int segment_start(AVFormatContext *s, int write_header) oc = c->avf; } - if (c->wrap) - c->number %= c->wrap; + if (c->segment_idx_wrap) + c->segment_idx %= c->segment_idx_wrap; if (av_get_frame_filename(oc->filename, sizeof(oc->filename), - s->filename, c->number++) < 0) + s->filename, c->segment_idx++) < 0) { + av_log(oc, AV_LOG_ERROR, "Invalid segment filename template '%s'\n", s->filename); return AVERROR(EINVAL); + } + c->segment_count++; if ((err = avio_open2(&oc->pb, oc->filename, AVIO_FLAG_WRITE, &s->interrupt_callback, NULL)) < 0) @@ -144,18 +156,139 @@ static int segment_start(AVFormatContext *s, int write_header) return 0; } -static int segment_end(AVFormatContext *oc, int write_trailer) +static int segment_list_open(AVFormatContext *s) +{ + SegmentContext *seg = s->priv_data; + int ret; + + ret = avio_open2(&seg->list_pb, seg->list, AVIO_FLAG_WRITE, + &s->interrupt_callback, NULL); + if (ret < 0) + return ret; + seg->list_max_segment_time = 0; + + if (seg->list_type == LIST_TYPE_M3U8) { + 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->list_count); + avio_printf(seg->list_pb, "#EXT-X-ALLOWCACHE:%d\n", + !!(seg->list_flags & SEGMENT_LIST_FLAG_CACHE)); + if (seg->list_flags & SEGMENT_LIST_FLAG_LIVE) + avio_printf(seg->list_pb, + "#EXT-X-TARGETDURATION:%"PRId64"\n", seg->time / 1000000); + } + + return ret; +} + +static void segment_list_close(AVFormatContext *s) { + SegmentContext *seg = s->priv_data; + + if (seg->list_type == LIST_TYPE_M3U8) { + if (!(seg->list_flags & SEGMENT_LIST_FLAG_LIVE)) + avio_printf(seg->list_pb, "#EXT-X-TARGETDURATION:%d\n", + (int)ceil(seg->list_max_segment_time)); + avio_printf(seg->list_pb, "#EXT-X-ENDLIST\n"); + } + seg->list_count++; + + avio_close(seg->list_pb); +} + +static int segment_end(AVFormatContext *s, int write_trailer) +{ + 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->segment_count % seg->list_size)) { + segment_list_close(s); + if ((ret = segment_list_open(s)) < 0) + goto end; + } + + if (seg->list_type == LIST_TYPE_FLAT) { + avio_printf(seg->list_pb, "%s\n", oc->filename); + } else if (seg->list_type == LIST_TYPE_CSV || seg->list_type == LIST_TYPE_EXT) { + print_csv_escaped_str(seg->list_pb, oc->filename); + avio_printf(seg->list_pb, ",%f,%f\n", seg->start_time, seg->end_time); + } else if (seg->list_type == LIST_TYPE_M3U8) { + avio_printf(seg->list_pb, "#EXTINF:%f,\n%s\n", + seg->end_time - seg->start_time, oc->filename); + } + seg->list_max_segment_time = FFMAX(seg->end_time - seg->start_time, seg->list_max_segment_time); + avio_flush(seg->list_pb); + } + +end: avio_close(oc->pb); return ret; } +static int parse_times(void *log_ctx, int64_t **times, int *nb_times, + const char *times_str) +{ + char *p; + int i, ret = 0; + char *times_str1 = av_strdup(times_str); + char *saveptr = NULL; + + if (!times_str1) + return AVERROR(ENOMEM); + +#define FAIL(err) ret = err; goto end + + *nb_times = 1; + for (p = times_str1; *p; p++) + if (*p == ',') + (*nb_times)++; + + *times = av_malloc(sizeof(**times) * *nb_times); + if (!*times) { + av_log(log_ctx, AV_LOG_ERROR, "Could not allocate forced times array\n"); + FAIL(AVERROR(ENOMEM)); + } + + p = times_str1; + for (i = 0; i < *nb_times; i++) { + int64_t t; + char *tstr = av_strtok(p, ",", &saveptr); + av_assert0(tstr); + p = NULL; + + ret = av_parse_time(&t, tstr, 1); + if (ret < 0) { + av_log(log_ctx, AV_LOG_ERROR, + "Invalid time duration specification in %s\n", p); + 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 open_null_ctx(AVIOContext **ctx) { int buf_size = 32768; @@ -182,16 +315,59 @@ static int seg_write_header(AVFormatContext *s) AVFormatContext *oc = NULL; int ret, i; - 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) + if (seg->time_str && seg->times_str) { + av_log(s, AV_LOG_ERROR, + "segment_time and segment_times options are mutually exclusive, select just one of them\n"); + return AVERROR(EINVAL); + } + + if ((seg->list_flags & SEGMENT_LIST_FLAG_LIVE) && seg->times_str) { + av_log(s, AV_LOG_ERROR, + "segment_flags +live and segment_times options are mutually exclusive:" + "specify -segment_time if you want a live-friendly list\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 { + /* set default value if not specified */ + if (!seg->time_str) + seg->time_str = av_strdup("2"); + if ((ret = av_parse_time(&seg->time, seg->time_str, 1)) < 0) { + av_log(s, AV_LOG_ERROR, + "Invalid time duration specification '%s' for segment_time option\n", + seg->time_str); + return ret; + } + } + + if (seg->time_delta_str) { + if ((ret = av_parse_time(&seg->time_delta, seg->time_delta_str, 1)) < 0) { + av_log(s, AV_LOG_ERROR, + "Invalid time duration specification '%s' for delta option\n", + seg->time_delta_str); + return ret; + } + } + + 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 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"); for (i = 0; i < s->nb_streams; i++) seg->has_video += @@ -220,10 +396,11 @@ static int seg_write_header(AVFormatContext *s) oc = seg->avf; if (av_get_frame_filename(oc->filename, sizeof(oc->filename), - s->filename, seg->number++) < 0) { + s->filename, seg->segment_idx++) < 0) { ret = AVERROR(EINVAL); goto fail; } + seg->segment_count++; if (seg->write_header_trailer) { if ((ret = avio_open2(&oc->pb, oc->filename, AVIO_FLAG_WRITE, @@ -239,6 +416,9 @@ static int seg_write_header(AVFormatContext *s) goto fail; } + 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); if ((ret = avio_open2(&oc->pb, oc->filename, AVIO_FLAG_WRITE, @@ -246,20 +426,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); + segment_list_close(s); if (seg->avf) avformat_free_context(seg->avf); } @@ -271,18 +441,26 @@ 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; + int64_t end_pts; int ret; - if ((seg->has_video && st->codec->codec_type == AVMEDIA_TYPE_VIDEO) && + if (seg->times) { + end_pts = seg->segment_count <= seg->nb_times ? + seg->times[seg->segment_count-1] : INT64_MAX; + } else { + end_pts = seg->time * seg->segment_count; + } + + /* if the segment has video, start a new segment *only* with a key video frame */ + if ((st->codec->codec_type == AVMEDIA_TYPE_VIDEO || !seg->has_video) && av_compare_ts(pkt->pts, st->time_base, - end_pts, AV_TIME_BASE_Q) >= 0 && + end_pts-seg->time_delta, AV_TIME_BASE_Q) >= 0 && pkt->flags & AV_PKT_FLAG_KEY) { - av_log(s, AV_LOG_DEBUG, "Next segment starts at %d %"PRId64"\n", - pkt->stream_index, pkt->pts); + av_log(s, AV_LOG_DEBUG, "Next segment starts with packet stream:%d pts:%"PRId64" pts_time:%f\n", + pkt->stream_index, pkt->pts, pkt->pts * av_q2d(st->time_base)); - ret = segment_end(oc, seg->individual_header_trailer); + ret = segment_end(s, seg->individual_header_trailer); if (!ret) ret = segment_start(s, seg->individual_header_trailer); @@ -292,21 +470,10 @@ static int seg_write_packet(AVFormatContext *s, AVPacket *pkt) oc = seg->avf; - if (seg->list) { - if (seg->list_type == LIST_HLS) { - if ((ret = segment_hls_window(s, 0)) < 0) - goto fail; - } else { - avio_printf(seg->pb, "%s\n", oc->filename); - avio_flush(seg->pb); - if (seg->size && !(seg->number % seg->size)) { - avio_closep(&seg->pb); - if ((ret = avio_open2(&seg->pb, seg->list, AVIO_FLAG_WRITE, - &s->interrupt_callback, NULL)) < 0) - goto fail; - } - } - } + seg->start_time = (double)pkt->pts * av_q2d(st->time_base); + } else if (pkt->pts != AV_NOPTS_VALUE) { + seg->end_time = FFMAX(seg->end_time, + (double)(pkt->pts + pkt->duration) * av_q2d(st->time_base)); } ret = ff_write_chained(oc, pkt->stream_index, pkt, s); @@ -314,7 +481,7 @@ static int seg_write_packet(AVFormatContext *s, AVPacket *pkt) fail: if (ret < 0) { if (seg->list) - avio_close(seg->pb); + avio_close(seg->list_pb); avformat_free_context(oc); } @@ -327,25 +494,21 @@ static int seg_write_trailer(struct AVFormatContext *s) AVFormatContext *oc = seg->avf; int ret; if (!seg->write_header_trailer) { - if ((ret = segment_end(oc, 0)) < 0) + if ((ret = segment_end(s, 0)) < 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); } +fail: + if (seg->list) + segment_list_close(s); - if (ret < 0) - goto fail; - - if (seg->list && seg->list_type == LIST_HLS) { - if ((ret = segment_hls_window(s, 1) < 0)) - goto fail; - } + av_opt_free(seg); + av_freep(&seg->times); -fail: - avio_close(seg->pb); avformat_free_context(oc); return ret; } @@ -353,14 +516,24 @@ 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_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, 0, "list_type" }, + { "csv", "csv format", 0, AV_OPT_TYPE_CONST, {.i64=LIST_TYPE_CSV }, INT_MIN, INT_MAX, 0, "list_type" }, + { "ext", "extended format", 0, AV_OPT_TYPE_CONST, {.i64=LIST_TYPE_EXT }, INT_MIN, INT_MAX, 0, "list_type" }, + { "m3u8", "M3U8 format", 0, AV_OPT_TYPE_CONST, {.i64=LIST_TYPE_M3U8 }, INT_MIN, INT_MAX, 0, "list_type" }, + { "hls", "Apple HTTP Live Streaming compatible", 0, AV_OPT_TYPE_CONST, {.i64=LIST_TYPE_M3U8 }, INT_MIN, INT_MAX, 0, "list_type" }, + { "segment_time", "set segment duration", OFFSET(time_str),AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E }, + { "segment_time_delta","set approximation value used for the segment times", OFFSET(time_delta_str), AV_OPT_TYPE_STRING, {.str = "0"}, 0, 0, E }, + { "segment_times", "set segment split time points", OFFSET(times_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 }, { "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 }, { NULL }, @@ -373,7 +546,6 @@ static const AVClass seg_class = { .version = LIBAVUTIL_VERSION_INT, }; - AVOutputFormat ff_segment_muxer = { .name = "segment", .long_name = NULL_IF_CONFIG_SMALL("segment"), @@ -384,3 +556,21 @@ AVOutputFormat ff_segment_muxer = { .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 6490fddc5d..a21ad1f1bc 100644 --- a/libavformat/sierravmd.c +++ b/libavformat/sierravmd.c @@ -2,20 +2,20 @@ * Sierra VMD Format Demuxer * Copyright (c) 2004 The ffmpeg Project * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -30,6 +30,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 @@ -147,7 +148,7 @@ 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); + av_reduce(&num, &den, num, den, (1UL<<31)-1); avpriv_set_pts_info(vst, 33, num, den); avpriv_set_pts_info(st, 33, num, den); } @@ -245,6 +246,8 @@ static int vmd_read_packet(AVFormatContext *s, /* 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); diff --git a/libavformat/siff.c b/libavformat/siff.c index 02af80bfab..26c381753b 100644 --- a/libavformat/siff.c +++ b/libavformat/siff.c @@ -2,26 +2,27 @@ * 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 */ #include "libavutil/intreadwrite.h" #include "avformat.h" #include "internal.h" +#include "avio_internal.h" enum SIFFTags{ TAG_SIFF = MKTAG('S', 'I', 'F', 'F'), @@ -201,13 +202,16 @@ static int siff_read_packet(AVFormatContext *s, AVPacket *pkt) } if (!c->curstrm){ - size = c->pktsize - c->sndsize; - if (av_new_packet(pkt, size) < 0) + size = c->pktsize - c->sndsize - c->gmcsize - 2; + size = ffio_limit(s->pb, size); + if(size < 0 || c->pktsize < c->sndsize) + return AVERROR_INVALIDDATA; + if (av_new_packet(pkt, size + c->gmcsize + 2) < 0) return AVERROR(ENOMEM); AV_WL16(pkt->data, c->flags); if (c->gmcsize) memcpy(pkt->data + 2, c->gmc, c->gmcsize); - avio_read(s->pb, pkt->data + 2 + c->gmcsize, size - c->gmcsize - 2); + avio_read(s->pb, pkt->data + 2 + c->gmcsize, size); pkt->stream_index = 0; c->curstrm = -1; }else{ diff --git a/libavformat/smacker.c b/libavformat/smacker.c index dcca1afd37..829233f032 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 */ @@ -203,7 +203,7 @@ static int smacker_read_header(AVFormatContext *s) /* load trees to extradata, they will be unpacked by decoder */ - st->codec->extradata = av_malloc(smk->treesize + 16); + st->codec->extradata = av_malloc(smk->treesize + 16 + FF_INPUT_BUFFER_PADDING_SIZE); st->codec->extradata_size = smk->treesize + 16; if(!st->codec->extradata){ av_log(s, AV_LOG_ERROR, "Cannot allocate %i bytes of extradata\n", smk->treesize + 16); @@ -238,7 +238,7 @@ static int smacker_read_packet(AVFormatContext *s, AVPacket *pkt) int frame_size = 0; int palchange = 0; - if (s->pb->eof_reached || smk->cur_frame >= smk->frames) + if (url_feof(s->pb) || smk->cur_frame >= smk->frames) return AVERROR_EOF; /* if we demuxed all streams, pass another frame */ @@ -255,6 +255,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; @@ -296,10 +298,12 @@ static int smacker_read_packet(AVFormatContext *s, AVPacket *pkt) /* if audio chunks are present, put them to stack and retrieve later */ for(i = 0; i < 7; i++) { if(flags & 1) { - int size; + unsigned int size; uint8_t *tmpbuf; size = avio_rl32(s->pb) - 4; + if(size + 4L > frame_size) + return AVERROR_INVALIDDATA; frame_size -= size; frame_size -= 4; smk->curstream++; @@ -359,7 +363,7 @@ static int smacker_read_close(AVFormatContext *s) AVInputFormat ff_smacker_demuxer = { .name = "smk", - .long_name = NULL_IF_CONFIG_SMALL("Smacker video"), + .long_name = NULL_IF_CONFIG_SMALL("Smacker"), .priv_data_size = sizeof(SmackerContext), .read_probe = smacker_probe, .read_header = smacker_read_header, diff --git a/libavformat/smjpeg.c b/libavformat/smjpeg.c index 52e45e98f0..4edf5e8ac4 100644 --- a/libavformat/smjpeg.c +++ b/libavformat/smjpeg.c @@ -2,20 +2,20 @@ * SMJPEG common code * Copyright (c) 2011-2012 Paul B Mahol * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/smjpeg.h b/libavformat/smjpeg.h index c56fe46939..995ddf22da 100644 --- a/libavformat/smjpeg.h +++ b/libavformat/smjpeg.h @@ -2,20 +2,20 @@ * SMJPEG common code * Copyright (c) 2011-2012 Paul B Mahol * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/smjpegdec.c b/libavformat/smjpegdec.c index 4cbfa2a580..0cd993f8eb 100644 --- a/libavformat/smjpegdec.c +++ b/libavformat/smjpegdec.c @@ -2,20 +2,20 @@ * SMJPEG demuxer * Copyright (c) 2011 Paul B Mahol * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -56,7 +56,7 @@ static int smjpeg_read_header(AVFormatContext *s) duration = avio_rb32(pb); // in msec - while (!pb->eof_reached) { + while (!url_feof(pb)) { htype = avio_rl32(pb); switch (htype) { case SMJPEG_TXT: @@ -139,7 +139,7 @@ static int smjpeg_read_packet(AVFormatContext *s, AVPacket *pkt) int64_t pos; int ret; - if (s->pb->eof_reached) + if (url_feof(s->pb)) return AVERROR_EOF; pos = avio_tell(s->pb); dtype = avio_rl32(s->pb); diff --git a/libavformat/smjpegenc.c b/libavformat/smjpegenc.c index 59ede7a266..6dbc286102 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 */ diff --git a/libavformat/smoothstreamingenc.c b/libavformat/smoothstreamingenc.c index 1ed675a272..1104d5853f 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 */ diff --git a/libavformat/smush.c b/libavformat/smush.c new file mode 100644 index 0000000000..b164c7516e --- /dev/null +++ b/libavformat/smush.c @@ -0,0 +1,238 @@ +/* + * LucasArts Smush demuxer + * Copyright (c) 2006 Cyril Zorin + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/intreadwrite.h" +#include "avformat.h" +#include "internal.h" +#include "avio.h" + +typedef struct { + int version; + int audio_stream_index; + int video_stream_index; +} SMUSHContext; + +static int smush_read_probe(AVProbeData *p) +{ + if ((AV_RL32(p->buf) == MKTAG('S', 'A', 'N', 'M') || + AV_RL32(p->buf) == MKTAG('A', 'N', 'I', 'M'))) { + return AVPROBE_SCORE_MAX; + } + + return 0; +} + +static int smush_read_header(AVFormatContext *ctx) +{ + SMUSHContext *smush = ctx->priv_data; + AVIOContext *pb = ctx->pb; + AVStream *vst, *ast; + uint32_t magic, nframes, size, subversion, i; + uint32_t width = 0, height = 0, got_audio = 0, read = 0; + uint32_t sample_rate, channels, palette[256]; + + magic = avio_rb32(pb); + avio_skip(pb, 4); // skip movie size + + if (magic == MKBETAG('A', 'N', 'I', 'M')) { + if (avio_rb32(pb) != MKBETAG('A', 'H', 'D', 'R')) + return AVERROR_INVALIDDATA; + + size = avio_rb32(pb); + if (size < 3 * 256 + 6) + return AVERROR_INVALIDDATA; + + smush->version = 0; + subversion = avio_rl16(pb); + nframes = avio_rl16(pb); + + avio_skip(pb, 2); // skip pad + + for (i = 0; i < 256; i++) + palette[i] = avio_rb24(pb); + + avio_skip(pb, size - (3 * 256 + 6)); + } else if (magic == MKBETAG('S', 'A', 'N', 'M') ) { + if (avio_rb32(pb) != MKBETAG('S', 'H', 'D', 'R')) + return AVERROR_INVALIDDATA; + + size = avio_rb32(pb); + if (size < 14) + return AVERROR_INVALIDDATA; + + smush->version = 1; + subversion = avio_rl16(pb); + nframes = avio_rl32(pb); + avio_skip(pb, 2); // skip pad + width = avio_rl16(pb); + height = avio_rl16(pb); + avio_skip(pb, 2); // skip pad + avio_skip(pb, size - 14); + + if (avio_rb32(pb) != MKBETAG('F', 'L', 'H', 'D')) + return AVERROR_INVALIDDATA; + + size = avio_rb32(pb); + while (!got_audio && ((read + 8) < size)) { + uint32_t sig, chunk_size; + + if (url_feof(pb)) + return AVERROR_EOF; + + sig = avio_rb32(pb); + chunk_size = avio_rb32(pb); + read += 8; + switch (sig) { + case MKBETAG('W', 'a', 'v', 'e'): + got_audio = 1; + sample_rate = avio_rl32(pb); + channels = avio_rl32(pb); + avio_skip(pb, chunk_size - 8); + read += chunk_size; + break; + case MKBETAG('B', 'l', '1', '6'): + case MKBETAG('A', 'N', 'N', 'O'): + avio_skip(pb, chunk_size); + read += chunk_size; + break; + default: + return AVERROR_INVALIDDATA; + break; + } + } + + avio_skip(pb, size - read); + } else { + av_log(ctx, AV_LOG_ERROR, "Wrong magic\n"); + return AVERROR_INVALIDDATA; + } + + vst = avformat_new_stream(ctx, 0); + if (!vst) + return AVERROR(ENOMEM); + + smush->video_stream_index = vst->index; + + vst->start_time = 0; + vst->duration = + vst->nb_frames = nframes; + vst->codec->codec_type = AVMEDIA_TYPE_VIDEO; + vst->codec->codec_id = AV_CODEC_ID_SANM; + vst->codec->codec_tag = 0; + vst->codec->width = width; + vst->codec->height = height; + + avpriv_set_pts_info(vst, 64, 66667, 1000000); + + if (!smush->version) { + vst->codec->extradata = av_malloc(1024 + 2 + FF_INPUT_BUFFER_PADDING_SIZE); + if (!vst->codec->extradata) + return AVERROR(ENOMEM); + + vst->codec->extradata_size = 1024 + 2; + AV_WL16(vst->codec->extradata, subversion); + for (i = 0; i < 256; i++) + AV_WL32(vst->codec->extradata + 2 + i * 4, palette[i]); + } + + if (got_audio) { + ast = avformat_new_stream(ctx, 0); + if (!ast) + return AVERROR(ENOMEM); + + smush->audio_stream_index = ast->index; + + ast->start_time = 0; + ast->codec->codec_type = AVMEDIA_TYPE_AUDIO; + ast->codec->codec_id = AV_CODEC_ID_VIMA; + ast->codec->codec_tag = 0; + ast->codec->sample_rate = sample_rate; + ast->codec->channels = channels; + + avpriv_set_pts_info(ast, 64, 1, ast->codec->sample_rate); + } + + return 0; +} + +static int smush_read_packet(AVFormatContext *ctx, AVPacket *pkt) +{ + SMUSHContext *smush = ctx->priv_data; + AVIOContext *pb = ctx->pb; + int done = 0; + + while (!done) { + uint32_t sig, size; + + if (url_feof(pb)) + return AVERROR_EOF; + + sig = avio_rb32(pb); + size = avio_rb32(pb); + + switch (sig) { + case MKBETAG('F', 'R', 'M', 'E'): + if (smush->version) + break; + if (av_get_packet(pb, pkt, size) < 0) + return AVERROR(EIO); + + pkt->stream_index = smush->video_stream_index; + done = 1; + break; + case MKBETAG('B', 'l', '1', '6'): + if (av_get_packet(pb, pkt, size) < 0) + return AVERROR(EIO); + + pkt->stream_index = smush->video_stream_index; + pkt->duration = 1; + done = 1; + break; + case MKBETAG('W', 'a', 'v', 'e'): + if (size < 13) + return AVERROR_INVALIDDATA; + if (av_get_packet(pb, pkt, size) < 0) + return AVERROR(EIO); + + pkt->stream_index = smush->audio_stream_index; + pkt->flags |= AV_PKT_FLAG_KEY; + pkt->duration = AV_RB32(pkt->data); + if (pkt->duration == 0xFFFFFFFFu) + pkt->duration = AV_RB32(pkt->data + 8); + done = 1; + break; + default: + avio_skip(pb, size); + break; + } + } + + return 0; +} + +AVInputFormat ff_smush_demuxer = { + .name = "smush", + .long_name = NULL_IF_CONFIG_SMALL("LucasArts Smush"), + .priv_data_size = sizeof(SMUSHContext), + .read_probe = smush_read_probe, + .read_header = smush_read_header, + .read_packet = smush_read_packet, +}; diff --git a/libavformat/sol.c b/libavformat/sol.c index da846002e8..aca5a860eb 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 */ @@ -128,16 +128,13 @@ static int sol_read_packet(AVFormatContext *s, { int ret; - if (s->pb->eof_reached) + if (url_feof(s->pb)) return AVERROR(EIO); ret= av_get_packet(s->pb, pkt, MAX_SIZE); if (ret < 0) return ret; + pkt->flags &= ~AV_PKT_FLAG_CORRUPT; pkt->stream_index = 0; - - /* note: we need to modify the packet size here to handle the last - packet */ - pkt->size = ret; return 0; } diff --git a/libavformat/sox.h b/libavformat/sox.h index e59531bea3..f4a12e93ff 100644 --- a/libavformat/sox.h +++ b/libavformat/sox.h @@ -2,20 +2,20 @@ * SoX native format common data * Copyright (c) 2009 Daniel Verkamp <daniel@drv.nu> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/soxdec.c b/libavformat/soxdec.c index ec94675830..4c951bbd73 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 */ @@ -97,6 +97,8 @@ static int sox_read_header(AVFormatContext *s) 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); @@ -129,15 +131,15 @@ static int sox_read_packet(AVFormatContext *s, { int ret, size; - if (s->pb->eof_reached) + if (url_feof(s->pb)) 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->flags &= ~AV_PKT_FLAG_CORRUPT; pkt->stream_index = 0; - pkt->size = ret; return 0; } diff --git a/libavformat/soxenc.c b/libavformat/soxenc.c index 3e5d2e3515..6b4412de18 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 */ diff --git a/libavformat/spdif.c b/libavformat/spdif.c index 777ac47ba5..604141a261 100644 --- a/libavformat/spdif.c +++ b/libavformat/spdif.c @@ -2,20 +2,20 @@ * IEC 61937 common code * Copyright (c) 2009 Bartlomiej Wolowiec * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/spdif.h b/libavformat/spdif.h index b2a6b63be4..4b11de20d1 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 */ diff --git a/libavformat/spdifdec.c b/libavformat/spdifdec.c index 6d37c94f75..94cfe8443d 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 */ @@ -171,7 +171,7 @@ static int spdif_read_packet(AVFormatContext *s, AVPacket *pkt) while (state != (AV_BSWAP16C(SYNCWORD1) << 16 | AV_BSWAP16C(SYNCWORD2))) { state = (state << 8) | avio_r8(pb); - if (pb->eof_reached) + if (url_feof(pb)) return AVERROR_EOF; } diff --git a/libavformat/spdifenc.c b/libavformat/spdifenc.c index 77af92e1f3..bb0c363e89 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 */ @@ -520,13 +520,13 @@ static int spdif_write_packet(struct AVFormatContext *s, AVPacket *pkt) } if (ctx->extra_bswap ^ (ctx->spdif_flags & SPDIF_FLAG_BIGENDIAN)) { - avio_write(s->pb, ctx->out_buf, ctx->out_bytes & ~1); + avio_write(s->pb, ctx->out_buf, ctx->out_bytes & ~1); } else { - av_fast_malloc(&ctx->buffer, &ctx->buffer_size, ctx->out_bytes + FF_INPUT_BUFFER_PADDING_SIZE); - if (!ctx->buffer) - return AVERROR(ENOMEM); - ff_spdif_bswap_buf16((uint16_t *)ctx->buffer, (uint16_t *)ctx->out_buf, ctx->out_bytes >> 1); - avio_write(s->pb, ctx->buffer, ctx->out_bytes & ~1); + av_fast_malloc(&ctx->buffer, &ctx->buffer_size, ctx->out_bytes + FF_INPUT_BUFFER_PADDING_SIZE); + if (!ctx->buffer) + return AVERROR(ENOMEM); + ff_spdif_bswap_buf16((uint16_t *)ctx->buffer, (uint16_t *)ctx->out_buf, ctx->out_bytes >> 1); + avio_write(s->pb, ctx->buffer, ctx->out_bytes & ~1); } /* a final lone byte has to be MSB aligned */ diff --git a/libavformat/srtdec.c b/libavformat/srtdec.c index 9db5133a21..4f5f0953f8 100644 --- a/libavformat/srtdec.c +++ b/libavformat/srtdec.c @@ -2,20 +2,20 @@ * 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 */ @@ -44,25 +44,33 @@ static int srt_read_header(AVFormatContext *s) { AVStream *st = avformat_new_stream(s, NULL); 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; + st->codec->codec_id = AV_CODEC_ID_SUBRIP; return 0; } -static int64_t get_pts(const char *buf) +static int64_t get_pts(char **buf, int *duration, + int32_t *x1, int32_t *y1, int32_t *x2, int32_t *y2) { - int i, v, hour, min, sec, hsec; + int i; 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; + 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 += strcspn(*buf, "\n") + 1; + return start; } - buf += strcspn(buf, "\n") + 1; + *buf += strcspn(*buf, "\n") + 1; } return AV_NOPTS_VALUE; } @@ -81,13 +89,33 @@ static int srt_read_packet(AVFormatContext *s, AVPacket *pkt) 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); + } while (!is_eol(*ptr2) && !url_feof(s->pb) && 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); + if (buffer[0]) { + int64_t pts; + int duration; + const char *end = ptr; + int32_t x1 = -1, y1 = -1, x2 = -1, y2 = -1; + + ptr = buffer; + pts = get_pts(&ptr, &duration, &x1, &y1, &x2, &y2); + if (pts != AV_NOPTS_VALUE && + !(res = av_new_packet(pkt, end - ptr))) { + memcpy(pkt->data, ptr, pkt->size); + pkt->flags |= AV_PKT_FLAG_KEY; + pkt->pos = pos; + pkt->pts = pkt->dts = pts; + pkt->duration = duration; + if (x1 != -1) { + uint8_t *p = av_packet_new_side_data(pkt, 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); + } + } + } } return res; } diff --git a/libavformat/srtenc.c b/libavformat/srtenc.c new file mode 100644 index 0000000000..833823839b --- /dev/null +++ b/libavformat/srtenc.c @@ -0,0 +1,97 @@ +/* + * 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" + +/* TODO: add options for: + - character encoding; + - LF / CRLF; + - byte order mark. + */ + +typedef struct SRTContext{ + unsigned index; +} SRTContext; + +static int srt_write_header(AVFormatContext *avf) +{ + 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); + 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; + + srt->index++; + if (write_ts) { + int64_t s = pkt->pts, e, d = pkt->duration; + + 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_ERROR, "Insufficient timestamps.\n"); + return AVERROR(EINVAL); + } + e = s + d; + avio_printf(avf->pb, "%d\n%02d:%02d:%02d,%03d --> %02d:%02d:%02d,%03d\n", + 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)); + } + avio_write(avf->pb, pkt->data, pkt->size); + if (write_ts) + avio_write(avf->pb, "\n\n", 2); + avio_flush(avf->pb); + 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, + .subtitle_codec = AV_CODEC_ID_SUBRIP, +}; diff --git a/libavformat/subtitles.c b/libavformat/subtitles.c new file mode 100644 index 0000000000..12045262eb --- /dev/null +++ b/libavformat/subtitles.c @@ -0,0 +1,145 @@ +/* + * 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 + */ + +#include "avformat.h" +#include "subtitles.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->destruct = NULL; + sub->flags |= AV_PKT_FLAG_KEY; + sub->pts = sub->dts = 0; + memcpy(sub->data, event, len); + } + return sub; +} + +static int cmp_pkt_sub(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; +} + +void ff_subtitles_queue_finalize(FFDemuxSubtitlesQueue *q) +{ + int i; + + qsort(q->subs, q->nb_subs, sizeof(*q->subs), cmp_pkt_sub); + 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; + *pkt = *sub; + pkt->dts = pkt->pts; + q->current_sub_idx++; + return 0; +} + +void ff_subtitles_queue_clean(FFDemuxSubtitlesQueue *q) +{ + int i; + + for (i = 0; i < q->nb_subs; i++) + av_destruct_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 && isspace(*s)) + break; + in_quotes ^= *s == '"'; // XXX: support escaping? + s++; + } + while (isspace(*s)) + s++; + if (!av_strncasecmp(s, attr, len) && s[len] == '=') + return s + len + 1 + (s[len + 1] == '"'); + } + return NULL; +} diff --git a/libavformat/subtitles.h b/libavformat/subtitles.h new file mode 100644 index 0000000000..b089bb2a87 --- /dev/null +++ b/libavformat/subtitles.h @@ -0,0 +1,77 @@ +/* + * 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" + +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 +} 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); + +/** + * 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); + +#endif /* AVFORMAT_SUBTITLES_H */ diff --git a/libavformat/subviewerdec.c b/libavformat/subviewerdec.c new file mode 100644 index 0000000000..99e40fda79 --- /dev/null +++ b/libavformat/subviewerdec.c @@ -0,0 +1,177 @@ +/* + * 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 "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_MAX/2; + 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; + + if (!st) + return AVERROR(ENOMEM); + avpriv_set_pts_info(st, 64, 1, 100); + st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE; + st->codec->codec_id = AV_CODEC_ID_SUBVIEWER; + + av_bprint_init(&header, 0, AV_BPRINT_SIZE_UNLIMITED); + + while (!url_feof(s->pb)) { + char line[2048]; + const int64_t pos = avio_tell(s->pb); + int len = ff_get_line(s->pb, line, sizeof(line)); + + if (!len) + break; + + 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", line); + if (!strncmp(line, "[END INFORMATION]", 17) || !strncmp(line, "[SUBTITLE]", 10)) { + /* end of header */ + av_bprint_finalize(&header, (char **)&st->codec->extradata); + if (!st->codec->extradata) { + res = AVERROR(ENOMEM); + goto end; + } + st->codec->extradata_size = header.len + 1; + } 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] && !strchr("]\r\n", line[i])) + value[j++] = line[i++]; + value[j] = 0; + + av_dict_set(&s->metadata, key, value, 0); + } + } + } else { + int64_t pts_start = AV_NOPTS_VALUE; + int duration = -1; + int timed_line = !read_ts(line, &pts_start, &duration); + AVPacket *sub; + + sub = ff_subtitles_queue_insert(&subviewer->q, line, len, !timed_line); + if (!sub) { + res = AVERROR(ENOMEM); + goto end; + } + if (timed_line) { + sub->pos = pos; + sub->pts = pts_start; + sub->duration = duration; + } + } + } + + 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_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_close = subviewer_read_close, + .flags = AVFMT_GENERIC_INDEX, + .extensions = "sub", +}; diff --git a/libavformat/swf.c b/libavformat/swf.c index e6adf69c02..1aa434a0ea 100644 --- a/libavformat/swf.c +++ b/libavformat/swf.c @@ -3,20 +3,20 @@ * Copyright (c) 2000 Fabrice Bellard * Copyright (c) 2003 Tinic Uro * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/swf.h b/libavformat/swf.h index 97e7b2d4f0..6ecd0da2fe 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 @@ -76,6 +135,13 @@ typedef struct SWFContext { int tag; AVFifoBuffer *audio_fifo; AVCodecContext *audio_enc, *video_enc; +#if CONFIG_ZLIB + AVIOContext *zpb; +#define ZBUF_SIZE 4096 + uint8_t *zbuf_in; + uint8_t *zbuf_out; + z_stream zstream; +#endif } SWFContext; extern const AVCodecTag ff_swf_codec_tags[]; diff --git a/libavformat/swfdec.c b/libavformat/swfdec.c index ff44452f8f..b8c2e930c3 100644 --- a/libavformat/swfdec.c +++ b/libavformat/swfdec.c @@ -3,23 +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 "libavutil/avassert.h" +#include "libavutil/imgutils.h" #include "libavutil/intreadwrite.h" #include "swf.h" @@ -36,8 +38,8 @@ static int get_swf_tag(AVIOContext *pb, int *len_ptr) { int tag, len; - if (pb->eof_reached) - return -1; + if (url_feof(pb)) + return AVERROR_EOF; tag = avio_rl16(pb); len = tag & 0x3f; @@ -60,6 +62,39 @@ static int swf_probe(AVProbeData *p) return 0; } +#if CONFIG_ZLIB +static int zlib_refill(void *opaque, uint8_t *buf, int buf_size) +{ + AVFormatContext *s = opaque; + SWFContext *swf = s->priv_data; + z_stream *z = &swf->zstream; + int ret; + +retry: + if (!z->avail_in) { + int n = avio_read(s->pb, swf->zbuf_in, ZBUF_SIZE); + if (n < 0) + return n; + z->next_in = swf->zbuf_in; + z->avail_in = n; + } + + z->next_out = buf; + z->avail_out = buf_size; + + ret = inflate(z, Z_NO_FLUSH); + if (ret < 0) + return AVERROR(EINVAL); + if (ret == Z_STREAM_END) + return AVERROR_EOF; + + if (buf_size - z->avail_out == 0) + goto retry; + + return buf_size - z->avail_out; +} +#endif + static int swf_read_header(AVFormatContext *s) { SWFContext *swf = s->priv_data; @@ -67,14 +102,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; @@ -94,11 +144,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 (tag == TAG_VIDEOSTREAM) { int ch_id = avio_rl16(pb); len -= 2; @@ -116,7 +171,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)); @@ -137,7 +192,7 @@ static int swf_read_packet(AVFormatContext *s, AVPacket *pkt) swf->samples_per_frame = avio_rl16(pb); ast = avformat_new_stream(s, NULL); if (!ast) - return -1; + return AVERROR(ENOMEM); ast->id = -1; /* -1 to avoid clash with video stream ch_id */ ast->codec->channels = 1 + (v&1); ast->codec->codec_type = AVMEDIA_TYPE_AUDIO; @@ -147,6 +202,44 @@ static int swf_read_packet(AVFormatContext *s, AVPacket *pkt) ast->codec->sample_rate = 44100 >> (3 - sample_rate_code); avpriv_set_pts_info(ast, 64, 1, ast->codec->sample_rate); len -= 4; + } else if (tag == TAG_DEFINESOUND) { + /* audio stream */ + int sample_rate_code; + 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: 8-bit uncompressed PCM audio will be interpreted as 16-bit + // 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 = avformat_new_stream(s, NULL); + if (!ast) + return AVERROR(ENOMEM); + ast->id = ch_id; + ast->codec->channels = 1 + (v&1); + 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); + 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; @@ -162,6 +255,127 @@ 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); + } + 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]; @@ -188,7 +402,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; @@ -217,6 +431,18 @@ static int swf_read_packet(AVFormatContext *s, AVPacket *pkt) } } +#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)"), @@ -224,4 +450,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 31f405d5cd..a8fd9f91f0 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); @@ -425,7 +426,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 */ @@ -486,8 +487,10 @@ static int swf_write_trailer(AVFormatContext *s) enc = s->streams[i]->codec; if (enc->codec_type == AVMEDIA_TYPE_VIDEO) video_enc = enc; - else + else { av_fifo_free(swf->audio_fifo); + swf->audio_fifo = NULL; + } } put_swf_tag(s, TAG_END); @@ -500,8 +503,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 new file mode 100644 index 0000000000..a5290173d8 --- /dev/null +++ b/libavformat/takdec.c @@ -0,0 +1,186 @@ +/* + * Raw TAK 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 "libavcodec/tak.h" +#include "avformat.h" +#include "internal.h" +#include "rawdec.h" +#include "apetag.h" + +typedef struct TAKDemuxContext { + int mlast_frame; + int64_t data_end; +} TAKDemuxContext; + +static int tak_probe(AVProbeData *p) +{ + if (!memcmp(p->buf, "tBaK", 4)) + return AVPROBE_SCORE_MAX / 2; + return 0; +} + +static int tak_read_header(AVFormatContext *s) +{ + TAKDemuxContext *tc = s->priv_data; + AVIOContext *pb = s->pb; + GetBitContext gb; + AVStream *st; + uint8_t *buffer = NULL; + int ret; + + st = avformat_new_stream(s, 0); + if (!st) + return AVERROR(ENOMEM); + + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_id = AV_CODEC_ID_TAK; + st->need_parsing = AVSTREAM_PARSE_FULL_RAW; + + tc->mlast_frame = 0; + if (avio_rl32(pb) != MKTAG('t', 'B', 'a', 'K')) { + avio_seek(pb, -4, SEEK_CUR); + return 0; + } + + while (!url_feof(pb)) { + enum TAKMetaDataType type; + int size; + + type = avio_r8(pb) & 0x7f; + size = avio_rl24(pb); + + switch (type) { + case TAK_METADATA_STREAMINFO: + case TAK_METADATA_LAST_FRAME: + case TAK_METADATA_ENCODER: + buffer = av_malloc(size + FF_INPUT_BUFFER_PADDING_SIZE); + if (!buffer) + return AVERROR(ENOMEM); + + if (avio_read(pb, buffer, size) != size) { + av_freep(&buffer); + return AVERROR(EIO); + } + + init_get_bits(&gb, buffer, size * 8); + break; + case TAK_METADATA_MD5: { + uint8_t md5[16]; + int i; + + if (size != 19) + return AVERROR_INVALIDDATA; + avio_read(pb, md5, 16); + avio_skip(pb, 3); + av_log(s, AV_LOG_VERBOSE, "MD5="); + for (i = 0; i < 16; i++) + av_log(s, AV_LOG_VERBOSE, "%02x", md5[i]); + av_log(s, AV_LOG_VERBOSE, "\n"); + break; + } + case TAK_METADATA_END: { + int64_t curpos = avio_tell(pb); + + if (pb->seekable) { + ff_ape_parse_tag(s); + avio_seek(pb, curpos, SEEK_SET); + } + + tc->data_end += curpos; + return 0; + break; + } + default: + ret = avio_skip(pb, size); + if (ret < 0) + return ret; + } + + if (type == TAK_METADATA_STREAMINFO) { + TAKStreamInfo ti; + + avpriv_tak_parse_streaminfo(&gb, &ti); + if (ti.samples > 0) + st->duration = ti.samples; + st->codec->bits_per_coded_sample = ti.bps; + if (ti.ch_layout) + st->codec->channel_layout = ti.ch_layout; + st->codec->sample_rate = ti.sample_rate; + st->codec->channels = ti.channels; + st->start_time = 0; + avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate); + st->codec->extradata = buffer; + st->codec->extradata_size = size; + buffer = NULL; + } else if (type == TAK_METADATA_LAST_FRAME) { + if (size != 11) + return AVERROR_INVALIDDATA; + tc->mlast_frame = 1; + tc->data_end = get_bits_longlong(&gb, TAK_LAST_FRAME_POS_BITS) + + get_bits(&gb, TAK_LAST_FRAME_SIZE_BITS); + av_freep(&buffer); + } else if (type == TAK_METADATA_ENCODER) { + av_log(s, AV_LOG_VERBOSE, "encoder version: %0X\n", + get_bits_long(&gb, TAK_ENCODER_VERSION_BITS)); + av_freep(&buffer); + } + } + + return AVERROR_EOF; +} + +static int raw_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + TAKDemuxContext *tc = s->priv_data; + int ret; + + if (tc->mlast_frame) { + AVIOContext *pb = s->pb; + int64_t size, left; + + left = tc->data_end - avio_tell(s->pb); + size = FFMIN(left, 1024); + if (size <= 0) + return AVERROR_EOF; + + ret = av_get_packet(pb, pkt, size); + if (ret < 0) + return ret; + + pkt->stream_index = 0; + } else { + ret = ff_raw_read_partial_packet(s, pkt); + } + + return ret; +} + +AVInputFormat ff_tak_demuxer = { + .name = "tak", + .long_name = NULL_IF_CONFIG_SMALL("raw TAK"), + .priv_data_size = sizeof(TAKDemuxContext), + .read_probe = tak_probe, + .read_header = tak_read_header, + .read_packet = raw_read_packet, + .flags = AVFMT_GENERIC_INDEX, + .extensions = "tak", + .raw_codec_id = AV_CODEC_ID_TAK, +}; diff --git a/libavformat/tcp.c b/libavformat/tcp.c index bdaab7f806..8b35fb18a9 100644 --- a/libavformat/tcp.c +++ b/libavformat/tcp.c @@ -2,24 +2,25 @@ * 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 "internal.h" #include "network.h" #include "os_support.h" @@ -29,23 +30,43 @@ #endif typedef struct TCPContext { + const AVClass *class; int fd; + int listen; + int rw_timeout; + int listen_timeout; } TCPContext; +#define OFFSET(x) offsetof(TCPContext, x) +#define D AV_OPT_FLAG_DECODING_PARAM +#define E AV_OPT_FLAG_ENCODING_PARAM +static const AVOption options[] = { +{"listen", "listen on port instead of connecting", OFFSET(listen), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, D|E }, +{"timeout", "timeout of socket i/o operations", OFFSET(rw_timeout), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, D|E }, +{"listen_timeout", "connection awaiting timeout", OFFSET(listen_timeout), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, D|E }, +{NULL} +}; + +static const AVClass tcp_context_class = { + .class_name = "tcp", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + /* return non zero if error */ static int tcp_open(URLContext *h, const char *uri, int flags) { struct addrinfo hints = { 0 }, *ai, *cur_ai; int port, fd = -1; TCPContext *s = h->priv_data; - int listen_socket = 0; const char *p; char buf[256]; int ret; socklen_t optlen; - int timeout = 100, listen_timeout = -1; char hostname[1024],proto[1024],path[1024]; char portstr[10]; + h->rw_timeout = 5000000; av_url_split(proto, sizeof(proto), NULL, 0, hostname, sizeof(hostname), &port, path, sizeof(path), uri); @@ -58,18 +79,19 @@ static int tcp_open(URLContext *h, const char *uri, int flags) p = strchr(uri, '?'); if (p) { if (av_find_info_tag(buf, sizeof(buf), "listen", p)) - listen_socket = 1; + s->listen = 1; if (av_find_info_tag(buf, sizeof(buf), "timeout", p)) { - timeout = strtol(buf, NULL, 10); + s->rw_timeout = strtol(buf, NULL, 10); } if (av_find_info_tag(buf, sizeof(buf), "listen_timeout", p)) { - listen_timeout = strtol(buf, NULL, 10); + s->listen_timeout = strtol(buf, NULL, 10); } } + 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); @@ -90,7 +112,7 @@ static int tcp_open(URLContext *h, const char *uri, int flags) if (fd < 0) goto fail; - if (listen_socket) { + if (s->listen) { int fd1; int reuse = 1; struct pollfd lp = { fd, POLLIN, 0 }; @@ -105,7 +127,7 @@ static int tcp_open(URLContext *h, const char *uri, int flags) ret = ff_neterrno(); goto fail1; } - ret = poll(&lp, 1, listen_timeout >= 0 ? listen_timeout : -1); + ret = poll(&lp, 1, s->listen_timeout >= 0 ? s->listen_timeout : -1); if (ret <= 0) { ret = AVERROR(ETIMEDOUT); goto fail1; @@ -126,6 +148,7 @@ static int tcp_open(URLContext *h, const char *uri, int flags) if (ret < 0) { struct pollfd p = {fd, POLLOUT, 0}; + int64_t wait_started; ret = ff_neterrno(); if (ret == AVERROR(EINTR)) { if (ff_check_interrupt(&h->interrupt_callback)) { @@ -139,7 +162,8 @@ static int tcp_open(URLContext *h, const char *uri, int flags) goto fail; /* wait until we are connected or until abort */ - while(timeout--) { + wait_started = av_gettime(); + do { if (ff_check_interrupt(&h->interrupt_callback)) { ret = AVERROR_EXIT; goto fail1; @@ -147,7 +171,7 @@ static int tcp_open(URLContext *h, const char *uri, int flags) ret = poll(&p, 1, 100); if (ret > 0) break; - } + } while (!h->rw_timeout || (av_gettime() - wait_started < h->rw_timeout)); if (ret <= 0) { ret = AVERROR(ETIMEDOUT); goto fail; @@ -192,8 +216,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); @@ -206,8 +230,8 @@ static int tcp_write(URLContext *h, const uint8_t *buf, int size) int ret; if (!(h->flags & AVIO_FLAG_NONBLOCK)) { - ret = ff_network_wait_fd(s->fd, 1); - if (ret < 0) + ret = ff_network_wait_fd_timeout(s->fd, 1, h->rw_timeout, &h->interrupt_callback); + if (ret) return ret; } ret = send(s->fd, buf, size, 0); @@ -252,5 +276,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/thp.c b/libavformat/thp.c index e8ca04f939..0e96b53a4b 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 */ @@ -41,7 +41,7 @@ typedef struct ThpDemuxContext { unsigned char components[16]; AVStream* vst; int has_audio; - int audiosize; + unsigned audiosize; } ThpDemuxContext; @@ -59,6 +59,7 @@ static int thp_read_header(AVFormatContext *s) ThpDemuxContext *thp = s->priv_data; AVStream *st; AVIOContext *pb = s->pb; + int64_t fsize= avio_size(pb); int i; /* Read the file header. */ @@ -71,7 +72,9 @@ static int thp_read_header(AVFormatContext *s) thp->fps = av_d2q(av_int2float(avio_rb32(pb)), INT_MAX); thp->framecnt = avio_rb32(pb); thp->first_framesz = avio_rb32(pb); - avio_rb32(pb); /* Data size. */ + pb->maxsize = avio_rb32(pb); + if(fsize>0 && (!pb->maxsize || fsize < pb->maxsize)) + pb->maxsize= fsize; thp->compoff = avio_rb32(pb); avio_rb32(pb); /* offsetDataOffset. */ @@ -142,7 +145,7 @@ 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) { diff --git a/libavformat/tiertexseq.c b/libavformat/tiertexseq.c index 5582890cca..88941b7311 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 866e55f2ba..38dd70c9df 100644 --- a/libavformat/tls.c +++ b/libavformat/tls.c @@ -22,6 +22,7 @@ #include "avformat.h" #include "url.h" #include "libavutil/avstring.h" +#include "libavutil/parseutils.h" #if CONFIG_GNUTLS #include <gnutls/gnutls.h> #define TLS_read(c, buf, size) gnutls_record_recv(c->session, buf, size) @@ -103,16 +104,71 @@ 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], key[1024]; + int has_cert, has_key, verify = 0; +#if CONFIG_GNUTLS + int ret; +#endif + const char *p = strchr(uri, '?'); + if (!p) + return; + + if (av_find_info_tag(buf, sizeof(buf), "cafile", p)) { +#if CONFIG_GNUTLS + ret = gnutls_certificate_set_x509_trust_file(c->cred, buf, GNUTLS_X509_FMT_PEM); + if (ret < 0) + av_log(h, AV_LOG_ERROR, "%s\n", gnutls_strerror(ret)); +#elif CONFIG_OPENSSL + if (!SSL_CTX_load_verify_locations(c->ctx, buf, NULL)) + av_log(h, AV_LOG_ERROR, "SSL_CTX_load_verify_locations %s\n", ERR_error_string(ERR_get_error(), NULL)); +#endif + } + + if (av_find_info_tag(buf, sizeof(buf), "verify", p)) { + char *endptr = NULL; + verify = strtol(buf, &endptr, 10); + if (buf == endptr) + verify = 1; + } + + has_cert = av_find_info_tag(buf, sizeof(buf), "cert", p); + has_key = av_find_info_tag(key, sizeof(key), "key", p); +#if CONFIG_GNUTLS + if (has_cert && has_key) { + ret = gnutls_certificate_set_x509_key_file(c->cred, buf, key, GNUTLS_X509_FMT_PEM); + if (ret < 0) + av_log(h, AV_LOG_ERROR, "%s\n", gnutls_strerror(ret)); + } else if (has_cert ^ has_key) { + av_log(h, AV_LOG_ERROR, "cert and key required\n"); + } + gnutls_certificate_set_verify_flags(c->cred, verify); +#elif CONFIG_OPENSSL + if (has_cert && !SSL_CTX_use_certificate_chain_file(c->ctx, buf)) + av_log(h, AV_LOG_ERROR, "SSL_CTX_use_certificate_chain_file %s\n", ERR_error_string(ERR_get_error(), NULL)); + if (has_key && !SSL_CTX_use_PrivateKey_file(c->ctx, key, SSL_FILETYPE_PEM)) + av_log(h, AV_LOG_ERROR, "SSL_CTX_use_PrivateKey_file %s\n", ERR_error_string(ERR_get_error(), NULL)); + if (verify) + SSL_CTX_set_verify(c->ctx, SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT, 0); +#endif +} + static int tls_open(URLContext *h, const char *uri, int flags) { TLSContext *c = h->priv_data; int ret; int port; - char buf[200], host[200]; + char buf[200], host[200], path[1024]; int numerichost = 0; struct addrinfo hints = { 0 }, *ai = NULL; const char *proxy_path; int use_proxy; + int server = 0; + const char *p = strchr(uri, '?'); + if (p && av_find_info_tag(buf, sizeof(buf), "listen", p)) + server = 1; ff_tls_init(); @@ -120,8 +176,8 @@ static int tls_open(URLContext *h, const char *uri, int flags) use_proxy = (proxy_path != NULL) && !getenv("no_proxy") && av_strstart(proxy_path, "http://", NULL); - av_url_split(NULL, 0, NULL, 0, host, sizeof(host), &port, NULL, 0, uri); - ff_url_join(buf, sizeof(buf), "tcp", NULL, host, port, NULL); + av_url_split(NULL, 0, NULL, 0, host, sizeof(host), &port, path, sizeof(path), uri); + ff_url_join(buf, sizeof(buf), "tcp", NULL, host, port, "%s", path); hints.ai_flags = AI_NUMERICHOST; if (!getaddrinfo(host, NULL, &hints, &ai)) { @@ -147,11 +203,11 @@ static int tls_open(URLContext *h, const char *uri, int flags) c->fd = ffurl_get_file_handle(c->tcp); #if CONFIG_GNUTLS - gnutls_init(&c->session, GNUTLS_CLIENT); + gnutls_init(&c->session, server ? GNUTLS_SERVER : GNUTLS_CLIENT); if (!numerichost) gnutls_server_name_set(c->session, GNUTLS_NAME_DNS, host, strlen(host)); gnutls_certificate_allocate_credentials(&c->cred); - gnutls_certificate_set_verify_flags(c->cred, 0); + set_options(h, uri); gnutls_credentials_set(c->session, GNUTLS_CRD_CERTIFICATE, c->cred); gnutls_transport_set_ptr(c->session, (gnutls_transport_ptr_t) (intptr_t) c->fd); @@ -164,12 +220,13 @@ static int tls_open(URLContext *h, const char *uri, int flags) goto fail; } #elif CONFIG_OPENSSL - c->ctx = SSL_CTX_new(TLSv1_client_method()); + c->ctx = SSL_CTX_new(server ? TLSv1_server_method() : TLSv1_client_method()); if (!c->ctx) { av_log(h, AV_LOG_ERROR, "%s\n", ERR_error_string(ERR_get_error(), NULL)); ret = AVERROR(EIO); goto fail; } + set_options(h, uri); c->ssl = SSL_new(c->ctx); if (!c->ssl) { av_log(h, AV_LOG_ERROR, "%s\n", ERR_error_string(ERR_get_error(), NULL)); @@ -177,10 +234,10 @@ static int tls_open(URLContext *h, const char *uri, int flags) goto fail; } SSL_set_fd(c->ssl, c->fd); - if (!numerichost) + if (!server && !numerichost) SSL_set_tlsext_host_name(c->ssl, host); while (1) { - ret = SSL_connect(c->ssl); + ret = server ? SSL_accept(c->ssl) : SSL_connect(c->ssl); if (ret > 0) break; if (ret == 0) { diff --git a/libavformat/tmv.c b/libavformat/tmv.c index 100e12fa53..acc3460cfd 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 */ @@ -147,7 +147,7 @@ static int tmv_read_packet(AVFormatContext *s, AVPacket *pkt) int ret, pkt_size = tmv->stream_index ? tmv->audio_chunk_size : tmv->video_chunk_size; - if (pb->eof_reached) + if (url_feof(pb)) return AVERROR_EOF; ret = av_get_packet(pb, pkt, pkt_size); diff --git a/libavformat/tta.c b/libavformat/tta.c index 9d3d295aba..445389ed95 100644 --- a/libavformat/tta.c +++ b/libavformat/tta.c @@ -2,20 +2,20 @@ * 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 */ @@ -44,8 +44,9 @@ 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 datalen; if (!av_dict_get(s->metadata, "", NULL, AV_DICT_IGNORE_SUFFIX)) ff_id3v1_read(s); @@ -64,9 +65,9 @@ static int tta_read_header(AVFormatContext *s) } datalen = avio_rl32(s->pb); - if(datalen < 0){ - av_log(s, AV_LOG_ERROR, "nonsense datalen\n"); - return -1; + if (!datalen) { + av_log(s, AV_LOG_ERROR, "invalid datalen\n"); + return AVERROR_INVALIDDATA; } avio_skip(s->pb, 4); // header crc @@ -78,8 +79,8 @@ static int tta_read_header(AVFormatContext *s) c->totalframes = datalen / 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"); + if(c->totalframes >= UINT_MAX/sizeof(uint32_t) || c->totalframes <= 0){ + av_log(s, AV_LOG_ERROR, "totalframes %d invalid\n", c->totalframes); return -1; } diff --git a/libavformat/tty.c b/libavformat/tty.c index f85f230000..4c62b5b4cc 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 */ @@ -121,20 +121,22 @@ static int read_packet(AVFormatContext *avctx, AVPacket *pkt) TtyDemuxContext *s = avctx->priv_data; int n; - if (avctx->pb->eof_reached) + if (url_feof(avctx->pb)) return AVERROR_EOF; n = s->chars_per_frame; if (s->fsize) { // ignore metadata buffer uint64_t p = avio_tell(avctx->pb); + if (p == s->fsize) + return AVERROR_EOF; if (p + s->chars_per_frame > s->fsize) n = s->fsize - p; } pkt->size = av_get_packet(avctx->pb, pkt, n); - if (pkt->size <= 0) - return AVERROR(EIO); + if (pkt->size < 0) + return pkt->size; pkt->flags |= AV_PKT_FLAG_KEY; return 0; } diff --git a/libavformat/txd.c b/libavformat/txd.c index 5897881b9b..194945bbc8 100644 --- a/libavformat/txd.c +++ b/libavformat/txd.c @@ -2,20 +2,20 @@ * Renderware TeXture Dictionary (.txd) demuxer * Copyright (c) 2007 Ivo van Poorten * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -48,6 +48,7 @@ static int txd_read_header(AVFormatContext *s) { st->codec->time_base.den = 5; st->codec->time_base.num = 1; /* the parameters will be extracted from the compressed bitstream */ + return 0; } @@ -61,7 +62,7 @@ next_chunk: chunk_size = avio_rl32(pb); marker = avio_rl32(pb); - if (s->pb->eof_reached) + if (url_feof(s->pb)) return AVERROR_EOF; if (marker != TXD_MARKER && marker != TXD_MARKER2) { av_log(s, AV_LOG_ERROR, "marker does not match\n"); @@ -69,17 +70,17 @@ next_chunk: } switch (id) { - case TXD_INFO: - if (chunk_size > 100) - break; - case TXD_EXTRA: - avio_skip(s->pb, chunk_size); - case TXD_FILE: - case TXD_TEXTURE: - goto next_chunk; - default: - av_log(s, AV_LOG_ERROR, "unknown chunk id %i\n", id); - return AVERROR_INVALIDDATA; + case TXD_INFO: + if (chunk_size > 100) + break; + case TXD_EXTRA: + avio_skip(s->pb, chunk_size); + case TXD_FILE: + case TXD_TEXTURE: + goto next_chunk; + default: + av_log(s, AV_LOG_ERROR, "unknown chunk id %i\n", id); + return AVERROR_INVALIDDATA; } ret = av_get_packet(s->pb, pkt, chunk_size); diff --git a/libavformat/udp.c b/libavformat/udp.c index 373a4c97a8..a88fbe48a5 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,86 @@ #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 "internal.h" #include "network.h" #include "os_support.h" #include "url.h" +#if HAVE_PTHREAD_CANCEL +#include <pthread.h> +#endif + +#ifndef HAVE_PTHREAD_CANCEL +#define HAVE_PTHREAD_CANCEL 0 +#endif + #ifndef IPV6_ADD_MEMBERSHIP #define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP #define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP #endif +#define UDP_TX_BUF_SIZE 32768 +#define UDP_MAX_PKT_SIZE 65536 + typedef struct { + const AVClass *class; int udp_fd; int ttl; int buffer_size; int is_multicast; int local_port; int reuse_socket; + int overrun_nonfatal; struct sockaddr_storage dest_addr; int dest_addr_len; int is_connected; + + /* Circular Buffer variables for use in UDP receive code */ + int circular_buffer_size; + AVFifoBuffer *fifo; + int circular_buffer_error; +#if HAVE_PTHREAD_CANCEL + pthread_t circular_buffer_thread; + pthread_mutex_t mutex; + pthread_cond_t cond; + int thread_started; +#endif + uint8_t tmp[UDP_MAX_PKT_SIZE+4]; + int remaining_in_dg; + char *local_addr; + int packet_size; + int timeout; } UDPContext; -#define UDP_TX_BUF_SIZE 32768 -#define UDP_MAX_PKT_SIZE 65536 +#define OFFSET(x) offsetof(UDPContext, x) +#define D AV_OPT_FLAG_DECODING_PARAM +#define E AV_OPT_FLAG_ENCODING_PARAM +static const AVOption options[] = { +{"buffer_size", "Socket buffer size in bytes", OFFSET(buffer_size), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, D|E }, +{"localport", "Set local port to bind to", OFFSET(local_port), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, D|E }, +{"localaddr", "Choose local IP address", OFFSET(local_addr), AV_OPT_TYPE_STRING, {.str = ""}, 0, 0, D|E }, +{"pkt_size", "Set size of UDP packets", OFFSET(packet_size), AV_OPT_TYPE_INT, {.i64 = 1472}, 0, INT_MAX, D|E }, +{"reuse", "Explicitly allow or disallow reusing UDP sockets", OFFSET(reuse_socket), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, D|E }, +{"ttl", "Set the time to live value (for multicast only)", OFFSET(ttl), AV_OPT_TYPE_INT, {.i64 = 16}, 0, INT_MAX, E }, +{"connect", "Should connect() be called on socket", OFFSET(is_connected), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, D|E }, +/* TODO 'sources', 'block' option */ +{"fifo_size", "Set the UDP receiving circular buffer size, expressed as a number of packets with size of 188 bytes", OFFSET(circular_buffer_size), AV_OPT_TYPE_INT, {.i64 = 7*4096}, 0, INT_MAX, D }, +{"overrun_nonfatal", "Survive in case of UDP receiving circular buffer overrun", OFFSET(overrun_nonfatal), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, D }, +{"timeout", "In read mode: if no data arrived in more than this time interval, raise error", OFFSET(timeout), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, D }, +{NULL} +}; + +static const AVClass udp_context_class = { + .class_name = "udp", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; static void log_net_error(void *ctx, int level, const char* prefix) { @@ -317,6 +372,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 +434,61 @@ 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); + ff_socket_nonblock(s->udp_fd, 0); + pthread_mutex_lock(&s->mutex); + 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 + /* put it in UDP context */ /* return non zero if error */ static int udp_open(URLContext *h, const char *uri, int flags) @@ -395,12 +506,10 @@ static int udp_open(URLContext *h, const char *uri, int flags) char *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) { @@ -412,6 +521,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); } @@ -419,7 +539,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); @@ -427,6 +547,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)); } @@ -449,7 +576,13 @@ static int udp_open(URLContext *h, const char *uri, int flags) break; } } + if (!is_output && av_find_info_tag(buf, sizeof(buf), "timeout", p)) + s->timeout = strtol(buf, NULL, 10); } + /* handling needed to support options picking from both AVOption and URL */ + s->circular_buffer_size *= 188; + h->max_packet_size = s->packet_size; + h->rw_timeout = s->timeout; /* fill the dest addr */ av_url_split(NULL, 0, NULL, 0, hostname, sizeof(hostname), &port, NULL, 0, uri); @@ -466,7 +599,7 @@ static int udp_open(URLContext *h, const char *uri, int flags) if ((s->is_multicast || !s->local_port) && (h->flags & AVIO_FLAG_READ)) s->local_port = port; - udp_fd = udp_socket_create(s, &my_addr, &len, localaddr); + udp_fd = udp_socket_create(s, &my_addr, &len, localaddr[0] ? localaddr : s->local_addr); if (udp_fd < 0) goto fail; @@ -549,15 +682,48 @@ static int udp_open(URLContext *h, const char *uri, int flags) } for (i = 0; i < num_sources; i++) - av_free(sources[i]); + av_freep(&sources[i]); s->udp_fd = udp_fd; + +#if HAVE_PTHREAD_CANCEL + if (!is_output && s->circular_buffer_size) { + int ret; + + /* start the task going */ + s->fifo = av_fifo_alloc(s->circular_buffer_size); + ret = pthread_mutex_init(&s->mutex, NULL); + if (ret != 0) { + av_log(h, AV_LOG_ERROR, "pthread_mutex_init failed : %s\n", strerror(ret)); + goto fail; + } + ret = pthread_cond_init(&s->cond, NULL); + if (ret != 0) { + av_log(h, AV_LOG_ERROR, "pthread_cond_init failed : %s\n", strerror(ret)); + goto cond_fail; + } + ret = pthread_create(&s->circular_buffer_thread, NULL, circular_buffer_task, h); + if (ret != 0) { + av_log(h, AV_LOG_ERROR, "pthread_create failed : %s\n", strerror(ret)); + goto thread_fail; + } + s->thread_started = 1; + } +#endif + return 0; +#if HAVE_PTHREAD_CANCEL + thread_fail: + pthread_cond_destroy(&s->cond); + cond_fail: + pthread_mutex_destroy(&s->mutex); +#endif fail: if (udp_fd >= 0) closesocket(udp_fd); + av_fifo_free(s->fifo); for (i = 0; i < num_sources; i++) - av_free(sources[i]); + av_freep(&sources[i]); return AVERROR(EIO); } @@ -565,6 +731,50 @@ static int udp_read(URLContext *h, uint8_t *buf, int size) { UDPContext *s = h->priv_data; int ret; + int avail, nonblock = h->flags & AVIO_FLAG_NONBLOCK; + +#if HAVE_PTHREAD_CANCEL + if (s->fifo) { + pthread_mutex_lock(&s->mutex); + do { + avail = av_fifo_size(s->fifo); + if (avail) { // >=size) { + uint8_t tmp[4]; + + av_fifo_generic_read(s->fifo, tmp, 4, NULL); + avail= AV_RL32(tmp); + if(avail > size){ + av_log(h, AV_LOG_WARNING, "Part of datagram lost due to insufficient buffer size\n"); + avail= size; + } + + av_fifo_generic_read(s->fifo, buf, avail, NULL); + av_fifo_drain(s->fifo, AV_RL32(tmp) - avail); + pthread_mutex_unlock(&s->mutex); + return avail; + } else if(s->circular_buffer_error){ + int err = s->circular_buffer_error; + pthread_mutex_unlock(&s->mutex); + return err; + } else if(nonblock) { + pthread_mutex_unlock(&s->mutex); + return AVERROR(EAGAIN); + } + else { + /* FIXME: using the monotonic clock would be better, + but it does not exist on all supported platforms. */ + int64_t t = av_gettime() + 100000; + struct timespec tv = { .tv_sec = t / 1000000, + .tv_nsec = (t % 1000000) * 1000 }; + if (pthread_cond_timedwait(&s->cond, &s->mutex, &tv) < 0) { + pthread_mutex_unlock(&s->mutex); + return AVERROR(errno == ETIMEDOUT ? EAGAIN : errno); + } + nonblock = 1; + } + } while( 1); + } +#endif if (!(h->flags & AVIO_FLAG_NONBLOCK)) { ret = ff_network_wait_fd(s->udp_fd, 0); @@ -572,6 +782,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; } @@ -599,10 +810,22 @@ static int udp_write(URLContext *h, const uint8_t *buf, int size) static int udp_close(URLContext *h) { UDPContext *s = h->priv_data; + int ret; if (s->is_multicast && (h->flags & AVIO_FLAG_READ)) udp_leave_multicast_group(s->udp_fd, (struct sockaddr *)&s->dest_addr); closesocket(s->udp_fd); +#if HAVE_PTHREAD_CANCEL + if (s->thread_started) { + pthread_cancel(s->circular_buffer_thread); + ret = pthread_join(s->circular_buffer_thread, NULL); + if (ret != 0) + av_log(h, AV_LOG_ERROR, "pthread_join(): %s\n", strerror(ret)); + pthread_mutex_destroy(&s->mutex); + pthread_cond_destroy(&s->cond); + } +#endif + av_fifo_free(s->fifo); return 0; } @@ -614,5 +837,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/url-test.c b/libavformat/url-test.c index 58258e502a..ceaffe8e9a 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.h b/libavformat/url.h index 195a8ff658..5f75dc91e1 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 { @@ -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); /** diff --git a/libavformat/urldecode.c b/libavformat/urldecode.c index 32460da4f9..b1009037a8 100644 --- a/libavformat/urldecode.c +++ b/libavformat/urldecode.c @@ -9,20 +9,20 @@ * based on http://www.icosaedro.it/apache/urldecode.c * from Umberto Salsi (salsi@icosaedro.it) * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/urldecode.h b/libavformat/urldecode.h index b43f319c9e..cb81ebc6f7 100644 --- a/libavformat/urldecode.h +++ b/libavformat/urldecode.h @@ -1,18 +1,18 @@ /* - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/utils.c b/libavformat/utils.c index ca52469bdb..fb5742b815 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 */ @@ -25,7 +25,9 @@ #include "avio_internal.h" #include "internal.h" #include "libavcodec/internal.h" +#include "libavcodec/raw.h" #include "libavcodec/bytestream.h" +#include "libavutil/avassert.h" #include "libavutil/opt.h" #include "libavutil/dict.h" #include "libavutil/pixdesc.h" @@ -36,6 +38,7 @@ #include "libavutil/mathematics.h" #include "libavutil/parseutils.h" #include "libavutil/time.h" +#include "libavutil/timestamp.h" #include "riff.h" #include "audiointerleave.h" #include "url.h" @@ -49,23 +52,30 @@ /** * @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)); } /** head of registered input format linked list */ @@ -167,7 +177,7 @@ AVOutputFormat *av_guess_format(const char *short_name, const char *filename, score_max = 0; while ((fmt = av_oformat_next(fmt))) { score = 0; - if (fmt->name && short_name && !av_strcasecmp(fmt->name, short_name)) + if (fmt->name && short_name && match_format(short_name, fmt->name)) score += 100; if (fmt->mime_type && mime_type && !strcmp(fmt->mime_type, mime_type)) score += 10; @@ -214,10 +224,33 @@ AVInputFormat *av_find_input_format(const char *short_name) return NULL; } +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(0, remaining ? AV_LOG_ERROR : AV_LOG_DEBUG, "Truncating packet of size %d to %"PRId64"\n", size, remaining+1); + size= remaining+1; + } + } + return size; +} int av_get_packet(AVIOContext *s, AVPacket *pkt, int size) { - int ret= av_new_packet(pkt, size); + int ret; + int orig_size = size; + size= ffio_limit(s, size); + + ret= av_new_packet(pkt, size); if(ret<0) return ret; @@ -229,6 +262,8 @@ int av_get_packet(AVIOContext *s, AVPacket *pkt, int size) av_free_packet(pkt); else av_shrink_packet(pkt, ret); + if (pkt->size < orig_size) + pkt->flags |= AV_PKT_FLAG_CORRUPT; return ret; } @@ -255,19 +290,19 @@ int av_filename_number_test(const char *filename) return filename && (av_get_frame_filename(buf, sizeof(buf), filename, 1)>=0); } -AVInputFormat *av_probe_input_format2(AVProbeData *pd, int is_opened, int *score_max) +AVInputFormat *av_probe_input_format3(AVProbeData *pd, int is_opened, int *score_ret) { AVProbeData lpd = *pd; AVInputFormat *fmt1 = NULL, *fmt; - int score, id3 = 0; + int score, nodat = 0, score_max=0; if (lpd.buf_size > 10 && ff_id3v2_match(lpd.buf, ID3v2_DEFAULT_MAGIC)) { int id3len = ff_id3v2_tag_len(lpd.buf); if (lpd.buf_size > id3len + 16) { lpd.buf += id3len; lpd.buf_size -= id3len; - } - id3 = 1; + }else + nodat = 1; } fmt = NULL; @@ -277,44 +312,41 @@ AVInputFormat *av_probe_input_format2(AVProbeData *pd, int is_opened, int *score score = 0; if (fmt1->read_probe) { score = fmt1->read_probe(&lpd); + if(fmt1->extensions && av_match_ext(lpd.filename, fmt1->extensions)) + score = FFMAX(score, nodat ? AVPROBE_SCORE_MAX/4-1 : 1); } else if (fmt1->extensions) { if (av_match_ext(lpd.filename, fmt1->extensions)) { score = 50; } } - if (score > *score_max) { - *score_max = score; + if (score > score_max) { + score_max = score; fmt = fmt1; - }else if (score == *score_max) + }else if (score == score_max) fmt = NULL; } - - /* a hack for files with huge id3v2 tags -- try to guess by file extension. */ - if (!fmt && is_opened && *score_max < AVPROBE_SCORE_MAX/4) { - while ((fmt = av_iformat_next(fmt))) - if (fmt->extensions && av_match_ext(lpd.filename, fmt->extensions)) { - *score_max = AVPROBE_SCORE_MAX/4; - break; - } - } - - if (!fmt && id3 && *score_max < AVPROBE_SCORE_MAX/4-1) { - while ((fmt = av_iformat_next(fmt))) - if (fmt->extensions && av_match_ext("mp3", fmt->extensions)) { - *score_max = AVPROBE_SCORE_MAX/4-1; - break; - } - } + *score_ret= score_max; return fmt; } +AVInputFormat *av_probe_input_format2(AVProbeData *pd, int is_opened, int *score_max) +{ + int score_ret; + AVInputFormat *fmt= av_probe_input_format3(pd, is_opened, &score_ret); + if(score_ret > *score_max){ + *score_max= score_ret; + return fmt; + }else + return NULL; +} + AVInputFormat *av_probe_input_format(AVProbeData *pd, int is_opened){ int score=0; return av_probe_input_format2(pd, is_opened, &score); } -static int set_codec_from_probe_data(AVFormatContext *s, AVStream *st, AVProbeData *pd, int score) +static int set_codec_from_probe_data(AVFormatContext *s, AVStream *st, AVProbeData *pd) { static const struct { const char *name; enum AVCodecID id; enum AVMediaType type; @@ -324,14 +356,16 @@ static int set_codec_from_probe_data(AVFormatContext *s, AVStream *st, AVProbeDa { "dts" , AV_CODEC_ID_DTS , AVMEDIA_TYPE_AUDIO }, { "eac3" , AV_CODEC_ID_EAC3 , AVMEDIA_TYPE_AUDIO }, { "h264" , AV_CODEC_ID_H264 , AVMEDIA_TYPE_VIDEO }, + { "loas" , AV_CODEC_ID_AAC_LATM , AVMEDIA_TYPE_AUDIO }, { "m4v" , AV_CODEC_ID_MPEG4 , AVMEDIA_TYPE_VIDEO }, { "mp3" , AV_CODEC_ID_MP3 , AVMEDIA_TYPE_AUDIO }, { "mpegvideo", AV_CODEC_ID_MPEG2VIDEO, AVMEDIA_TYPE_VIDEO }, { 0 } }; - AVInputFormat *fmt = av_probe_input_format2(pd, 1, &score); + int score; + AVInputFormat *fmt = av_probe_input_format3(pd, 1, &score); - if (fmt) { + if (fmt && st->request_probe <= score) { int i; av_log(s, AV_LOG_DEBUG, "Probe with size=%d, packets=%d detected %s with score=%d\n", pd->buf_size, MAX_PROBE_PACKETS - st->probe_packets, fmt->name, score); @@ -343,12 +377,28 @@ static int set_codec_from_probe_data(AVFormatContext *s, AVStream *st, AVProbeDa } } } - return !!fmt; + return score; } /************************************************************/ /* 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; +} + + /** size of probe buffer, for guessing file type from file contents */ #define PROBE_BUF_MIN 2048 #define PROBE_BUF_MAX (1<<20) @@ -375,15 +425,21 @@ int av_probe_input_buffer(AVIOContext *pb, AVInputFormat **fmt, for(probe_size= PROBE_BUF_MIN; probe_size<=max_probe_size && !*fmt; probe_size = FFMIN(probe_size<<1, FFMAX(max_probe_size, probe_size+1))) { - int score = probe_size < max_probe_size ? AVPROBE_SCORE_MAX/4 : 0; + int score = probe_size < max_probe_size ? AVPROBE_SCORE_RETRY : 0; int buf_offset = (probe_size == PROBE_BUF_MIN) ? 0 : probe_size>>1; + void *buftmp; if (probe_size < offset) { continue; } /* read probe data */ - buf = av_realloc(buf, probe_size + AVPROBE_PADDING_SIZE); + buftmp = av_realloc(buf, probe_size + AVPROBE_PADDING_SIZE); + if(!buftmp){ + av_free(buf); + return AVERROR(ENOMEM); + } + buf=buftmp; 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) { @@ -401,10 +457,10 @@ int av_probe_input_buffer(AVIOContext *pb, AVInputFormat **fmt, /* guess file format */ *fmt = av_probe_input_format2(&pd, 1, &score); if(*fmt){ - if(score <= AVPROBE_SCORE_MAX/4){ //this can only be true in the last iteration - av_log(logctx, AV_LOG_WARNING, "Format detected only with low score of %d, misdetection possible!\n", score); + if(score <= AVPROBE_SCORE_RETRY){ //this can only be true in the last iteration + av_log(logctx, AV_LOG_WARNING, "Format %s detected only with low score of %d, misdetection possible!\n", (*fmt)->name, score); }else - av_log(logctx, AV_LOG_DEBUG, "Probed with size=%d and score=%d\n", probe_size, score); + av_log(logctx, AV_LOG_DEBUG, "Format %s probed with size=%d and score=%d\n", (*fmt)->name, probe_size, score); } } @@ -414,8 +470,7 @@ int av_probe_input_buffer(AVIOContext *pb, AVInputFormat **fmt, } /* rewind. reuse probe buffer to avoid seeking */ - if ((ret = ffio_rewind_with_probe_data(pb, buf, pd.buf_size)) < 0) - av_free(buf); + ret = ffio_rewind_with_probe_data(pb, &buf, pd.buf_size); return ret; } @@ -425,21 +480,23 @@ static int init_input(AVFormatContext *s, const char *filename, AVDictionary **o { 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); 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)))) + (!s->iformat && (s->iformat = av_probe_input_format2(&pd, 0, &score)))) return 0; - 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) @@ -464,7 +521,7 @@ static AVPacket *add_to_pktbuf(AVPacketList **packet_buffer, AVPacket *pkt, return &pktl->pkt; } -static void queue_attached_pictures(AVFormatContext *s) +void avformat_queue_attached_pictures(AVFormatContext *s) { int i; for (i = 0; i < s->nb_streams; i++) @@ -485,6 +542,10 @@ int avformat_open_input(AVFormatContext **ps, const char *filename, AVInputForma if (!s && !(s = avformat_alloc_context())) return AVERROR(ENOMEM); + if (!s->av_class){ + av_log(0, 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; @@ -526,18 +587,22 @@ int avformat_open_input(AVFormatContext **ps, const char *filename, AVInputForma if (s->pb) ff_id3v2_read(s, ID3v2_DEFAULT_MAGIC, &id3v2_extra_meta); - if (s->iformat->read_header) + if (!(s->flags&AVFMT_FLAG_PRIV_OPT) && s->iformat->read_header) if ((ret = s->iformat->read_header(s)) < 0) goto fail; - if (id3v2_extra_meta && - (ret = ff_id3v2_parse_apic(s, &id3v2_extra_meta)) < 0) - goto fail; + if (id3v2_extra_meta) { + if (!strcmp(s->iformat->name, "mp3")) { + 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); - queue_attached_pictures(s); + avformat_queue_attached_pictures(s); - 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; @@ -563,37 +628,63 @@ fail: static void 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) { - pd->buf = av_realloc(pd->buf, pd->buf_size+pkt->size+AVPROBE_PADDING_SIZE); + uint8_t *new_buf = av_realloc(pd->buf, pd->buf_size+pkt->size+AVPROBE_PADDING_SIZE); + if(!new_buf) + goto no_packet; + pd->buf = new_buf; memcpy(pd->buf+pd->buf_size, pkt->data, pkt->size); pd->buf_size += pkt->size; memset(pd->buf+pd->buf_size, 0, AVPROBE_PADDING_SIZE); } else { +no_packet: st->probe_packets = 0; if (!pd->buf_size) { - av_log(s, AV_LOG_ERROR, "nothing to probe for stream %d\n", + av_log(s, AV_LOG_WARNING, "nothing to probe for stream %d\n", st->index); - return; } } - if (!st->probe_packets || - av_log2(pd->buf_size) != av_log2(pd->buf_size - pkt->size)) { - set_codec_from_probe_data(s, st, pd, st->probe_packets > 0 ? AVPROBE_SCORE_MAX/4 : 0); - if(st->codec->codec_id != AV_CODEC_ID_PROBE){ + end= s->raw_packet_buffer_remaining_size <= 0 + || st->probe_packets<=0; + + if(end || av_log2(pd->buf_size) != av_log2(pd->buf_size - pkt->size)){ + int score= set_codec_from_probe_data(s, st, pd); + if( (st->codec->codec_id != AV_CODEC_ID_NONE && score > AVPROBE_SCORE_RETRY) + || end){ pd->buf_size=0; av_freep(&pd->buf); - av_log(s, AV_LOG_DEBUG, "probed stream %d\n", st->index); + st->request_probe= -1; + if(st->codec->codec_id != AV_CODEC_ID_NONE){ + av_log(s, AV_LOG_DEBUG, "probed stream %d\n", st->index); + }else + av_log(s, AV_LOG_WARNING, "probed stream %d failed\n", st->index); } } } } +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; + } +} + int ff_read_packet(AVFormatContext *s, AVPacket *pkt) { int ret, i; @@ -605,15 +696,7 @@ 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) { - probe_codec(s, st, NULL); - } - pd = &st->probe_data; - av_freep(&pd->buf); - pd->buf_size = 0; + if(st->request_probe <= 0){ s->raw_packet_buffer = pktl->next; s->raw_packet_buffer_remaining_size += pkt->size; av_free(pktl); @@ -633,6 +716,7 @@ int ff_read_packet(AVFormatContext *s, AVPacket *pkt) if (st->probe_packets) { probe_codec(s, st, NULL); } + av_assert0(st->request_probe <= 0); } continue; } @@ -646,22 +730,23 @@ int ff_read_packet(AVFormatContext *s, AVPacket *pkt) continue; } - st= s->streams[pkt->stream_index]; + if(!(s->flags & AVFMT_FLAG_KEEP_SIDE_DATA)) + av_packet_merge_side_data(pkt); - switch(st->codec->codec_type){ - case AVMEDIA_TYPE_VIDEO: - if(s->video_codec_id) st->codec->codec_id= s->video_codec_id; - break; - case AVMEDIA_TYPE_AUDIO: - if(s->audio_codec_id) st->codec->codec_id= s->audio_codec_id; - break; - case AVMEDIA_TYPE_SUBTITLE: - if(s->subtitle_codec_id)st->codec->codec_id= s->subtitle_codec_id; - break; + if(pkt->stream_index >= (unsigned)s->nb_streams){ + av_log(s, AV_LOG_ERROR, "Invalid stream index %d\n", pkt->stream_index); + continue; } - if(!pktl && (st->codec->codec_id != AV_CODEC_ID_PROBE || - !st->probe_packets)) + st= s->streams[pkt->stream_index]; + + 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); @@ -681,6 +766,17 @@ int av_read_packet(AVFormatContext *s, AVPacket *pkt) /**********************************************************/ +static int determinable_frame_size(AVCodecContext *avctx) +{ + if (/*avctx->codec_id == AV_CODEC_ID_AAC ||*/ + avctx->codec_id == AV_CODEC_ID_MP1 || + avctx->codec_id == AV_CODEC_ID_MP2 || + avctx->codec_id == AV_CODEC_ID_MP3/* || + avctx->codec_id == AV_CODEC_ID_CELT*/) + return 1; + return 0; +} + /** * Get the number of samples of an audio frame. Return -1 on error. */ @@ -715,9 +811,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; @@ -746,40 +842,91 @@ 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 wont increase anymore for stream copy + return 1; +#if CONFIG_H264_DECODER + if(st->codec->has_b_frames && + avpriv_h264_has_num_reorder_frames(st->codec) == st->codec->has_b_frames) + return 1; +#endif + if(st->codec->has_b_frames<3) + return st->nb_decoded_frames >= 7; + else if(st->codec->has_b_frames<4) + return st->nb_decoded_frames >= 18; + else + return st->nb_decoded_frames >= 20; +} + +static AVPacketList *get_next_pkt(AVFormatContext *s, AVStream *st, AVPacketList *pktl) +{ + if (pktl->next) + return pktl->next; + if (pktl == s->parse_queue_end) + return s->packet_buffer; + return NULL; } static void update_initial_timestamps(AVFormatContext *s, int stream_index, int64_t dts, int64_t pts) { AVStream *st= s->streams[stream_index]; - AVPacketList *pktl= s->packet_buffer; + AVPacketList *pktl= s->parse_queue ? s->parse_queue : s->packet_buffer; + int64_t pts_buffer[MAX_REORDER_DELAY+1]; + int64_t shift; + int i, delay; - if(st->first_dts != AV_NOPTS_VALUE || dts == AV_NOPTS_VALUE || st->cur_dts == AV_NOPTS_VALUE) + if(st->first_dts != AV_NOPTS_VALUE || dts == AV_NOPTS_VALUE || st->cur_dts == AV_NOPTS_VALUE || is_relative(dts)) return; - st->first_dts= dts - st->cur_dts; + delay = st->codec->has_b_frames; + st->first_dts= dts - (st->cur_dts - RELATIVE_TS_BASE); st->cur_dts= dts; + shift = st->first_dts - RELATIVE_TS_BASE; + + for (i=0; i<MAX_REORDER_DELAY+1; i++) + pts_buffer[i] = AV_NOPTS_VALUE; - for(; pktl; pktl= pktl->next){ + if (is_relative(pts)) + pts += shift; + + for(; pktl; pktl= get_next_pkt(s, st, pktl)){ if(pktl->pkt.stream_index != stream_index) continue; - //FIXME think more about this check - if(pktl->pkt.pts != AV_NOPTS_VALUE && pktl->pkt.pts == pktl->pkt.dts) - pktl->pkt.pts += st->first_dts; + if(is_relative(pktl->pkt.pts)) + pktl->pkt.pts += shift; - if(pktl->pkt.dts != AV_NOPTS_VALUE) - pktl->pkt.dts += st->first_dts; + if(is_relative(pktl->pkt.dts)) + pktl->pkt.dts += shift; if(st->start_time == AV_NOPTS_VALUE && pktl->pkt.pts != AV_NOPTS_VALUE) st->start_time= pktl->pkt.pts; + + if(pktl->pkt.pts != AV_NOPTS_VALUE && delay <= MAX_REORDER_DELAY && has_decode_delay_been_guessed(st)){ + pts_buffer[0]= pktl->pkt.pts; + for(i=0; i<delay && pts_buffer[i] > pts_buffer[i+1]; i++) + FFSWAP(int64_t, pts_buffer[i], pts_buffer[i+1]); + if(pktl->pkt.dts == AV_NOPTS_VALUE) + pktl->pkt.dts= pts_buffer[0]; + } } if (st->start_time == AV_NOPTS_VALUE) st->start_time = pts; @@ -788,38 +935,46 @@ static void update_initial_timestamps(AVFormatContext *s, int stream_index, static void update_initial_durations(AVFormatContext *s, AVStream *st, int stream_index, int duration) { - AVPacketList *pktl= s->packet_buffer; - int64_t cur_dts= 0; + AVPacketList *pktl= s->parse_queue ? s->parse_queue : s->packet_buffer; + int64_t cur_dts= RELATIVE_TS_BASE; if(st->first_dts != AV_NOPTS_VALUE){ cur_dts= st->first_dts; - for(; pktl; pktl= pktl->next){ + for(; pktl; pktl= get_next_pkt(s, st, pktl)){ if(pktl->pkt.stream_index == stream_index){ if(pktl->pkt.pts != pktl->pkt.dts || pktl->pkt.dts != AV_NOPTS_VALUE || pktl->pkt.duration) break; cur_dts -= duration; } } - pktl= s->packet_buffer; + if(pktl && pktl->pkt.dts != st->first_dts) { + av_log(s, AV_LOG_DEBUG, "first_dts %s not matching first dts %s in que\n", av_ts2str(st->first_dts), av_ts2str(pktl->pkt.dts)); + return; + } + if(!pktl) { + av_log(s, AV_LOG_DEBUG, "first_dts %s but no packet with dts in ques\n", av_ts2str(st->first_dts)); + return; + } + pktl= s->parse_queue ? s->parse_queue : s->packet_buffer; st->first_dts = cur_dts; - }else if(st->cur_dts) + }else if(st->cur_dts != RELATIVE_TS_BASE) return; - for(; pktl; pktl= pktl->next){ + for(; pktl; pktl= get_next_pkt(s, st, pktl)){ if(pktl->pkt.stream_index != stream_index) continue; - if(pktl->pkt.pts == pktl->pkt.dts && pktl->pkt.dts == AV_NOPTS_VALUE + if(pktl->pkt.pts == pktl->pkt.dts && (pktl->pkt.dts == AV_NOPTS_VALUE || pktl->pkt.dts == st->first_dts) && !pktl->pkt.duration){ pktl->pkt.dts= cur_dts; if(!st->codec->has_b_frames) pktl->pkt.pts= cur_dts; - cur_dts += duration; - if (st->codec->codec_type != AVMEDIA_TYPE_AUDIO) +// if (st->codec->codec_type != AVMEDIA_TYPE_AUDIO) pktl->pkt.duration = duration; }else break; + cur_dts = pktl->pkt.dts + pktl->pkt.duration; } - if(st->first_dts == AV_NOPTS_VALUE) + if(!pktl) st->cur_dts= cur_dts; } @@ -835,6 +990,10 @@ static void compute_pkt_fields(AVFormatContext *s, AVStream *st, if((s->flags & AVFMT_FLAG_IGNDTS) && pkt->pts != AV_NOPTS_VALUE) pkt->dts= AV_NOPTS_VALUE; + if (st->codec->codec_id != AV_CODEC_ID_H264 && pc && pc->pict_type == AV_PICTURE_TYPE_B) + //FIXME Set low_delay = 0 when has_b_frames = 1 + st->codec->has_b_frames = 1; + /* do we have a video B-frame ? */ delay= st->codec->has_b_frames; presentation_delayed = 0; @@ -845,28 +1004,30 @@ static void compute_pkt_fields(AVFormatContext *s, AVStream *st, pc && pc->pict_type != AV_PICTURE_TYPE_B) presentation_delayed = 1; - if(pkt->pts != AV_NOPTS_VALUE && pkt->dts != AV_NOPTS_VALUE && pkt->dts > pkt->pts && st->pts_wrap_bits<63 - /*&& pkt->dts-(1LL<<st->pts_wrap_bits) < pkt->pts*/){ - pkt->dts -= 1LL<<st->pts_wrap_bits; + 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){ + if(is_relative(st->cur_dts) || pkt->dts - (1LL<<(st->pts_wrap_bits-1)) > st->cur_dts) { + pkt->dts -= 1LL<<st->pts_wrap_bits; + } else + pkt->pts += 1LL<<st->pts_wrap_bits; } // some mpeg2 in mpeg-ps lack dts (issue171 / input_file.mpg) // we take the conservative approach and discard both // Note, if this is misbehaving for a H.264 file then possibly presentation_delayed is not set correctly. if(delay==1 && pkt->dts == pkt->pts && pkt->dts != AV_NOPTS_VALUE && presentation_delayed){ - av_log(s, AV_LOG_DEBUG, "invalid dts/pts combination\n"); - pkt->dts= pkt->pts= AV_NOPTS_VALUE; + av_log(s, AV_LOG_DEBUG, "invalid dts/pts combination %"PRIi64"\n", pkt->dts); + if(strcmp(s->iformat->name, "mov,mp4,m4a,3gp,3g2,mj2")) // otherwise we discard correct timestamps for vc1-wmapro.ism + pkt->dts= AV_NOPTS_VALUE; } - if (pkt->duration == 0 && st->codec->codec_type != AVMEDIA_TYPE_AUDIO) { + if (pkt->duration == 0) { ff_compute_frame_duration(&num, &den, st, pc, pkt); if (den && num) { pkt->duration = av_rescale_rnd(1, num * (int64_t)st->time_base.den, den * (int64_t)st->time_base.num, AV_ROUND_DOWN); - - if(pkt->duration != 0 && s->packet_buffer) - update_initial_durations(s, st, pkt->stream_index, pkt->duration); } } + if(pkt->duration != 0 && (s->packet_buffer || s->parse_queue)) + update_initial_durations(s, st, pkt->stream_index, pkt->duration); /* correct timestamps with byte offset if demuxers only have timestamps on packet boundaries */ @@ -902,10 +1063,8 @@ static void compute_pkt_fields(AVFormatContext *s, AVStream *st, if(pkt->dts != AV_NOPTS_VALUE && pkt->pts != AV_NOPTS_VALUE && pkt->pts > pkt->dts) presentation_delayed = 1; - av_dlog(NULL, - "IN delayed:%d pts:%"PRId64", dts:%"PRId64" cur_dts:%"PRId64" st:%d pc:%p\n", - presentation_delayed, pkt->pts, pkt->dts, st->cur_dts, - pkt->stream_index, pc); + av_dlog(NULL, "IN delayed:%d pts:%s, dts:%s cur_dts:%s st:%d pc:%p duration:%d\n", + presentation_delayed, av_ts2str(pkt->pts), av_ts2str(pkt->dts), av_ts2str(st->cur_dts), pkt->stream_index, pc, pkt->duration); /* interpolate PTS and DTS if they are not present */ //We skip H264 currently because delay and has_b_frames are not reliably set if((delay==0 || (delay==1 && pc)) && st->codec->codec_id != AV_CODEC_ID_H264){ @@ -930,57 +1089,54 @@ static void compute_pkt_fields(AVFormatContext *s, AVStream *st, by knowing the future */ } else if (pkt->pts != AV_NOPTS_VALUE || pkt->dts != AV_NOPTS_VALUE || - pkt->duration || - st->codec->codec_type == AVMEDIA_TYPE_AUDIO) { + pkt->duration ) { int duration = pkt->duration; - if (!duration && st->codec->codec_type == AVMEDIA_TYPE_AUDIO) { - ff_compute_frame_duration(&num, &den, st, pc, pkt); - if (den && num) { - duration = av_rescale_rnd(1, num * (int64_t)st->time_base.den, - den * (int64_t)st->time_base.num, - AV_ROUND_DOWN); - if (duration != 0 && s->packet_buffer) { - update_initial_durations(s, st, pkt->stream_index, - duration); - } + + if(st->cur_dts != AV_NOPTS_VALUE && pkt->pts != AV_NOPTS_VALUE && duration){ + int64_t old_diff= FFABS(st->cur_dts - duration - pkt->pts); + int64_t new_diff= FFABS(st->cur_dts - pkt->pts); + if( old_diff < new_diff && old_diff < (duration>>3) + && st->codec->codec_type == AVMEDIA_TYPE_VIDEO + && (!strcmp(s->iformat->name, "mpeg") || + !strcmp(s->iformat->name, "mpegts"))){ + pkt->pts += duration; + av_log(s, AV_LOG_WARNING, "Adjusting PTS forward\n"); +// av_log(NULL, AV_LOG_DEBUG, "id:%d old:%"PRId64" new:%"PRId64" dur:%d cur:%s size:%d\n", +// pkt->stream_index, old_diff, new_diff, pkt->duration, av_ts2str(st->cur_dts), pkt->size); } } - if (pkt->pts != AV_NOPTS_VALUE || pkt->dts != AV_NOPTS_VALUE || - duration) { - /* presentation is not delayed : PTS and DTS are the same */ - if (pkt->pts == AV_NOPTS_VALUE) - pkt->pts = pkt->dts; - update_initial_timestamps(s, pkt->stream_index, pkt->pts, - pkt->pts); - if (pkt->pts == AV_NOPTS_VALUE) - pkt->pts = st->cur_dts; - pkt->dts = pkt->pts; - if (pkt->pts != AV_NOPTS_VALUE) - st->cur_dts = pkt->pts + duration; - } + /* presentation is not delayed : PTS and DTS are the same */ + if (pkt->pts == AV_NOPTS_VALUE) + pkt->pts = pkt->dts; + update_initial_timestamps(s, pkt->stream_index, pkt->pts, + pkt->pts); + if (pkt->pts == AV_NOPTS_VALUE) + pkt->pts = st->cur_dts; + pkt->dts = pkt->pts; + if (pkt->pts != AV_NOPTS_VALUE) + st->cur_dts = pkt->pts + duration; } } - if(pkt->pts != AV_NOPTS_VALUE && delay <= MAX_REORDER_DELAY){ + if(pkt->pts != AV_NOPTS_VALUE && delay <= MAX_REORDER_DELAY && has_decode_delay_been_guessed(st)){ st->pts_buffer[0]= pkt->pts; for(i=0; i<delay && st->pts_buffer[i] > st->pts_buffer[i+1]; i++) FFSWAP(int64_t, st->pts_buffer[i], st->pts_buffer[i+1]); if(pkt->dts == AV_NOPTS_VALUE) pkt->dts= st->pts_buffer[0]; - if(st->codec->codec_id == AV_CODEC_ID_H264){ // we skipped it above so we try here - update_initial_timestamps(s, pkt->stream_index, pkt->dts, pkt->pts); // this should happen on the first packet - } - if(pkt->dts > st->cur_dts) - st->cur_dts = pkt->dts; } + if(st->codec->codec_id == AV_CODEC_ID_H264){ // we skipped it above so we try here + update_initial_timestamps(s, pkt->stream_index, pkt->dts, pkt->pts); // this should happen on the first packet + } + if(pkt->dts > st->cur_dts) + st->cur_dts = pkt->dts; - av_dlog(NULL, - "OUTdelayed:%d/%d pts:%"PRId64", dts:%"PRId64" cur_dts:%"PRId64"\n", - presentation_delayed, delay, pkt->pts, pkt->dts, st->cur_dts); + av_dlog(NULL, "OUTdelayed:%d/%d pts:%s, dts:%s cur_dts:%s\n", + presentation_delayed, delay, av_ts2str(pkt->pts), av_ts2str(pkt->dts), av_ts2str(st->cur_dts)); /* update flags */ - if (is_intra_only(st->codec->codec_id)) + if (is_intra_only(st->codec)) pkt->flags |= AV_PKT_FLAG_KEY; if (pc) pkt->convergence_duration = pc->convergence_duration; @@ -1014,6 +1170,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)) { @@ -1025,6 +1184,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; @@ -1056,19 +1216,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.destruct = pkt->destruct; @@ -1142,17 +1301,17 @@ static int read_frame_internal(AVFormatContext *s, AVPacket *pkt) if (cur_pkt.pts != AV_NOPTS_VALUE && cur_pkt.dts != AV_NOPTS_VALUE && cur_pkt.pts < cur_pkt.dts) { - av_log(s, AV_LOG_WARNING, "Invalid timestamps stream=%d, pts=%"PRId64", dts=%"PRId64", size=%d\n", + av_log(s, AV_LOG_WARNING, "Invalid timestamps stream=%d, pts=%s, dts=%s, size=%d\n", cur_pkt.stream_index, - cur_pkt.pts, - cur_pkt.dts, + av_ts2str(cur_pkt.pts), + av_ts2str(cur_pkt.dts), cur_pkt.size); } if (s->debug & FF_FDEBUG_TS) - av_log(s, AV_LOG_DEBUG, "ff_read_packet stream=%d, pts=%"PRId64", dts=%"PRId64", size=%d, duration=%d, flags=%d\n", + av_log(s, AV_LOG_DEBUG, "ff_read_packet stream=%d, pts=%s, dts=%s, size=%d, duration=%d, flags=%d\n", cur_pkt.stream_index, - cur_pkt.pts, - cur_pkt.dts, + av_ts2str(cur_pkt.pts), + av_ts2str(cur_pkt.dts), cur_pkt.size, cur_pkt.duration, cur_pkt.flags); @@ -1160,12 +1319,17 @@ static int read_frame_internal(AVFormatContext *s, AVPacket *pkt) if (st->need_parsing && !st->parser && !(s->flags & AVFMT_FLAG_NOPARSE)) { st->parser = av_parser_init(st->codec->codec_id); if (!st->parser) { + av_log(s, AV_LOG_VERBOSE, "parser not found for codec " + "%s, packets or times may be invalid.\n", + avcodec_get_name(st->codec->codec_id)); /* no parser available: just output the raw packets */ st->need_parsing = AVSTREAM_PARSE_NONE; } else if(st->need_parsing == AVSTREAM_PARSE_HEADERS) { st->parser->flags |= PARSER_FLAG_COMPLETE_FRAMES; } else if(st->need_parsing == AVSTREAM_PARSE_FULL_ONCE) { st->parser->flags |= PARSER_FLAG_ONCE; + } else if(st->need_parsing == AVSTREAM_PARSE_FULL_RAW) { + st->parser->flags |= PARSER_FLAG_USE_CODEC_TS; } } @@ -1186,16 +1350,22 @@ 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); + got_packet = 0; + } } if (!got_packet && s->parse_queue) ret = read_from_packet_buffer(&s->parse_queue, &s->parse_queue_end, pkt); if(s->debug & FF_FDEBUG_TS) - av_log(s, AV_LOG_DEBUG, "read_frame_internal stream=%d, pts=%"PRId64", dts=%"PRId64", size=%d, duration=%d, flags=%d\n", + av_log(s, AV_LOG_DEBUG, "read_frame_internal stream=%d, pts=%s, dts=%s, size=%d, duration=%d, flags=%d\n", pkt->stream_index, - pkt->pts, - pkt->dts, + av_ts2str(pkt->pts), + av_ts2str(pkt->dts), pkt->size, pkt->duration, pkt->flags); @@ -1207,15 +1377,19 @@ int av_read_frame(AVFormatContext *s, AVPacket *pkt) { const int genpts = s->flags & AVFMT_FLAG_GENPTS; int eof = 0; + int ret; + AVStream *st; - if (!genpts) - return s->packet_buffer ? read_from_packet_buffer(&s->packet_buffer, - &s->packet_buffer_end, - pkt) : - read_frame_internal(s, pkt); + if (!genpts) { + ret = s->packet_buffer ? + read_from_packet_buffer(&s->packet_buffer, &s->packet_buffer_end, pkt) : + read_frame_internal(s, pkt); + if (ret < 0) + return ret; + goto return_packet; + } for (;;) { - int ret; AVPacketList *pktl = s->packet_buffer; if (pktl) { @@ -1223,22 +1397,40 @@ int av_read_frame(AVFormatContext *s, AVPacket *pkt) if (next_pkt->dts != AV_NOPTS_VALUE) { int wrap_bits = s->streams[next_pkt->stream_index]->pts_wrap_bits; + // last dts seen for this stream. if any of packets following + // current one had no dts, we will set this to AV_NOPTS_VALUE. + int64_t last_dts = next_pkt->dts; while (pktl && next_pkt->pts == AV_NOPTS_VALUE) { if (pktl->pkt.stream_index == next_pkt->stream_index && - (av_compare_mod(next_pkt->dts, pktl->pkt.dts, 2LL << (wrap_bits - 1)) < 0) && - av_compare_mod(pktl->pkt.pts, pktl->pkt.dts, 2LL << (wrap_bits - 1))) { //not b frame - next_pkt->pts = pktl->pkt.dts; + (av_compare_mod(next_pkt->dts, pktl->pkt.dts, 2LL << (wrap_bits - 1)) < 0)) { + if (av_compare_mod(pktl->pkt.pts, pktl->pkt.dts, 2LL << (wrap_bits - 1))) { //not b frame + next_pkt->pts = pktl->pkt.dts; + } + if (last_dts != AV_NOPTS_VALUE) { + // Once last dts was set to AV_NOPTS_VALUE, we don't change it. + last_dts = pktl->pkt.dts; + } } pktl = pktl->next; } + if (eof && next_pkt->pts == AV_NOPTS_VALUE && last_dts != AV_NOPTS_VALUE) { + // Fixing the last reference frame had none pts issue (For MXF etc). + // We only do this when + // 1. eof. + // 2. we are not able to resolve a pts value for current packet. + // 3. the packets for this stream at the end of the files had valid dts. + next_pkt->pts = last_dts + next_pkt->duration; + } pktl = s->packet_buffer; } /* read packet from packet buffer, if there is data */ if (!(next_pkt->pts == AV_NOPTS_VALUE && - next_pkt->dts != AV_NOPTS_VALUE && !eof)) - return read_from_packet_buffer(&s->packet_buffer, + next_pkt->dts != AV_NOPTS_VALUE && !eof)) { + ret = read_from_packet_buffer(&s->packet_buffer, &s->packet_buffer_end, pkt); + goto return_packet; + } } ret = read_frame_internal(s, pkt); @@ -1254,6 +1446,28 @@ int av_read_frame(AVFormatContext *s, AVPacket *pkt) &s->packet_buffer_end)) < 0) return AVERROR(ENOMEM); } + +return_packet: + + st = s->streams[pkt->stream_index]; + if (st->skip_samples) { + uint8_t *p = av_packet_new_side_data(pkt, AV_PKT_DATA_SKIP_SAMPLES, 10); + AV_WL32(p, st->skip_samples); + av_log(s, AV_LOG_DEBUG, "demuxer injecting skip %d\n", st->skip_samples); + st->skip_samples = 0; + } + + if ((s->iformat->flags & AVFMT_GENERIC_INDEX) && pkt->flags & AV_PKT_FLAG_KEY) { + ff_reduce_index(s, st->index); + av_add_index_entry(st, pkt->pos, pkt->dts, 0, 0, AVINDEX_KEYFRAME); + } + + if (is_relative(pkt->dts)) + pkt->dts -= RELATIVE_TS_BASE; + if (is_relative(pkt->pts)) + pkt->pts -= RELATIVE_TS_BASE; + + return ret; } /* XXX: suppress the packet queue */ @@ -1308,7 +1522,8 @@ void ff_read_frame_flush(AVFormatContext *s) st->parser = NULL; } st->last_IP_pts = AV_NOPTS_VALUE; - st->cur_dts = AV_NOPTS_VALUE; /* we set the current DTS to an unspecified origin */ + if(st->first_dts == AV_NOPTS_VALUE) st->cur_dts = RELATIVE_TS_BASE; + else st->cur_dts = AV_NOPTS_VALUE; /* we set the current DTS to an unspecified origin */ st->reference_dts = AV_NOPTS_VALUE; st->probe_packets = MAX_PROBE_PACKETS; @@ -1355,6 +1570,12 @@ 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 (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) * @@ -1451,7 +1672,7 @@ int ff_seek_frame_binary(AVFormatContext *s, int stream_index, int64_t target_ts if (stream_index < 0) return -1; - av_dlog(s, "read_seek: %d %"PRId64"\n", stream_index, target_ts); + av_dlog(s, "read_seek: %d %s\n", stream_index, av_ts2str(target_ts)); ts_max= ts_min= AV_NOPTS_VALUE; @@ -1468,8 +1689,8 @@ int ff_seek_frame_binary(AVFormatContext *s, int stream_index, int64_t target_ts if(e->timestamp <= target_ts || e->pos == e->min_distance){ pos_min= e->pos; ts_min= e->timestamp; - av_dlog(s, "using cached pos_min=0x%"PRIx64" dts_min=%"PRId64"\n", - pos_min,ts_min); + av_dlog(s, "using cached pos_min=0x%"PRIx64" dts_min=%s\n", + pos_min, av_ts2str(ts_min)); }else{ assert(index==0); } @@ -1482,8 +1703,8 @@ int ff_seek_frame_binary(AVFormatContext *s, int stream_index, int64_t target_ts pos_max= e->pos; ts_max= e->timestamp; pos_limit= pos_max - e->min_distance; - av_dlog(s, "using cached pos_max=0x%"PRIx64" pos_limit=0x%"PRIx64" dts_max=%"PRId64"\n", - pos_max,pos_limit, ts_max); + av_dlog(s, "using cached pos_max=0x%"PRIx64" pos_limit=0x%"PRIx64" dts_max=%s\n", + pos_max, pos_limit, av_ts2str(ts_max)); } } @@ -1495,6 +1716,7 @@ int ff_seek_frame_binary(AVFormatContext *s, int stream_index, int64_t target_ts if ((ret = avio_seek(s->pb, pos, SEEK_SET)) < 0) return ret; + ff_read_frame_flush(s); ff_update_cur_dts(s, st, ts); return 0; @@ -1509,7 +1731,7 @@ int64_t ff_gen_search(AVFormatContext *s, int stream_index, int64_t target_ts, int64_t start_pos, filesize; int no_change; - 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; @@ -1518,6 +1740,11 @@ int64_t ff_gen_search(AVFormatContext *s, int stream_index, int64_t target_ts, return -1; } + if(ts_min >= target_ts){ + *ts_ret= ts_min; + return pos_min; + } + if(ts_max == AV_NOPTS_VALUE){ int step= 1024; filesize = avio_size(s->pb); @@ -1543,6 +1770,11 @@ int64_t ff_gen_search(AVFormatContext *s, int stream_index, int64_t target_ts, 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){ @@ -1551,8 +1783,8 @@ int64_t ff_gen_search(AVFormatContext *s, int stream_index, int64_t target_ts, no_change=0; while (pos_min < pos_limit) { - av_dlog(s, "pos_min=0x%"PRIx64" pos_max=0x%"PRIx64" dts_min=%"PRId64" dts_max=%"PRId64"\n", - pos_min, pos_max, ts_min, ts_max); + av_dlog(s, "pos_min=0x%"PRIx64" pos_max=0x%"PRIx64" dts_min=%s dts_max=%s\n", + pos_min, pos_max, av_ts2str(ts_min), av_ts2str(ts_max)); assert(pos_limit <= pos_max); if(no_change==0){ @@ -1579,8 +1811,9 @@ int64_t ff_gen_search(AVFormatContext *s, int stream_index, int64_t target_ts, 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"); @@ -1600,12 +1833,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); 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); + 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; } @@ -1641,6 +1876,7 @@ static int seek_frame_generic(AVFormatContext *s, if(index < 0 || index==st->nb_index_entries-1){ AVPacket pkt; + int nonkey=0; if(st->nb_index_entries){ assert(st->index_entries); @@ -1660,9 +1896,13 @@ static int seek_frame_generic(AVFormatContext *s, if (read_status < 0) break; av_free_packet(&pkt); - if(stream_index == pkt.stream_index){ - if((pkt.flags & AV_PKT_FLAG_KEY) && pkt.dts > timestamp) + if(stream_index == pkt.stream_index && pkt.dts > timestamp){ + if(pkt.flags & AV_PKT_FLAG_KEY) break; + if(nonkey++ > 1000 && st->codec->codec_id != AV_CODEC_ID_CDGRAPHICS){ + av_log(s, AV_LOG_ERROR,"seek_frame_generic failed as this stream seems to contain no keyframes after the target timestamp, %d non keyframes found\n", nonkey); + break; + } } } index = av_index_search_timestamp(st, timestamp, flags); @@ -1732,7 +1972,7 @@ int av_seek_frame(AVFormatContext *s, int stream_index, int64_t timestamp, int f int ret = seek_frame_internal(s, stream_index, timestamp, flags); if (ret >= 0) - queue_attached_pictures(s); + avformat_queue_attached_pictures(s); return ret; } @@ -1748,7 +1988,7 @@ int avformat_seek_file(AVFormatContext *s, int stream_index, int64_t min_ts, int ret = s->iformat->read_seek2(s, stream_index, min_ts, ts, max_ts, flags); if (ret >= 0) - queue_attached_pictures(s); + avformat_queue_attached_pictures(s); return ret; } @@ -1757,9 +1997,17 @@ int avformat_seek_file(AVFormatContext *s, int stream_index, int64_t min_ts, int } //Fallback to old API if new is not implemented but old is - //Note the old has somewat different sematics - if(s->iformat->read_seek || 1) - return av_seek_frame(s, stream_index, ts, flags | (ts - min_ts > (uint64_t)(max_ts - ts) ? AVSEEK_FLAG_BACKWARD : 0)); + //Note the old has somewhat different semantics + 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 } @@ -1793,43 +2041,69 @@ static int has_duration(AVFormatContext *ic) */ static void update_stream_timings(AVFormatContext *ic) { - int64_t start_time, start_time1, end_time, end_time1; + int64_t start_time, start_time1, start_time_text, end_time, end_time1; int64_t duration, duration1, filesize; int i; AVStream *st; + AVProgram *p; start_time = INT64_MAX; + start_time_text = INT64_MAX; end_time = INT64_MIN; duration = INT64_MIN; for(i = 0;i < ic->nb_streams; i++) { st = ic->streams[i]; if (st->start_time != AV_NOPTS_VALUE && st->time_base.den) { start_time1= av_rescale_q(st->start_time, st->time_base, AV_TIME_BASE_Q); - start_time = FFMIN(start_time, start_time1); + if (st->codec->codec_type == AVMEDIA_TYPE_SUBTITLE || st->codec->codec_type == AVMEDIA_TYPE_DATA) { + if (start_time1 < start_time_text) + start_time_text = start_time1; + } else + start_time = FFMIN(start_time, start_time1); + end_time1 = AV_NOPTS_VALUE; if (st->duration != AV_NOPTS_VALUE) { end_time1 = start_time1 + av_rescale_q(st->duration, st->time_base, AV_TIME_BASE_Q); end_time = FFMAX(end_time, end_time1); } + for(p = NULL; (p = av_find_program_from_stream(ic, p, i)); ){ + if(p->start_time == AV_NOPTS_VALUE || p->start_time > start_time1) + p->start_time = start_time1; + if(p->end_time < end_time1) + p->end_time = end_time1; + } } if (st->duration != AV_NOPTS_VALUE) { duration1 = av_rescale_q(st->duration, st->time_base, AV_TIME_BASE_Q); duration = FFMAX(duration, duration1); } } + if (start_time == INT64_MAX || (start_time > start_time_text && start_time - start_time_text < AV_TIME_BASE)) + start_time = start_time_text; + else if(start_time > start_time_text) + av_log(ic, AV_LOG_VERBOSE, "Ignoring outlier non primary stream starttime %f\n", start_time_text / (float)AV_TIME_BASE); + if (start_time != INT64_MAX) { ic->start_time = start_time; - if (end_time != INT64_MIN) - duration = FFMAX(duration, end_time - start_time); + if (end_time != INT64_MIN) { + if (ic->nb_programs) { + for (i=0; i<ic->nb_programs; i++) { + p = ic->programs[i]; + if(p->start_time != AV_NOPTS_VALUE && p->end_time > p->start_time) + duration = FFMAX(duration, p->end_time - p->start_time); + } + } else + duration = FFMAX(duration, end_time - start_time); + } } - if (duration != INT64_MIN) { + if (duration != INT64_MIN && duration > 0 && ic->duration == AV_NOPTS_VALUE) { ic->duration = duration; - if (ic->pb && (filesize = avio_size(ic->pb)) > 0) { + } + if (ic->pb && (filesize = avio_size(ic->pb)) > 0 && ic->duration != AV_NOPTS_VALUE) { /* compute the bitrate */ ic->bit_rate = (double)filesize * 8.0 * AV_TIME_BASE / (double)ic->duration; } - } } static void fill_all_stream_timings(AVFormatContext *ic) @@ -1882,7 +2156,7 @@ static void estimate_timings_from_bit_rate(AVFormatContext *ic) } #define DURATION_MAX_READ_SIZE 250000 -#define DURATION_MAX_RETRY 3 +#define DURATION_MAX_RETRY 4 /* only usable for MPEG-PS streams */ static void estimate_timings_from_pts(AVFormatContext *ic, int64_t old_offset) @@ -1979,14 +2253,17 @@ 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); @@ -2006,32 +2283,46 @@ static void estimate_timings(AVFormatContext *ic, int64_t old_offset) } } -static int has_codec_parameters(AVStream *st) +static int has_codec_parameters(AVStream *st, const char **errmsg_ptr) { AVCodecContext *avctx = st->codec; - int val; + +#define FAIL(errmsg) do { \ + if (errmsg_ptr) \ + *errmsg_ptr = errmsg; \ + return 0; \ + } while (0) + switch (avctx->codec_type) { case AVMEDIA_TYPE_AUDIO: - val = avctx->sample_rate && avctx->channels; + if (!avctx->frame_size && determinable_frame_size(avctx)) + FAIL("unspecified frame size"); if (st->info->found_decoder >= 0 && avctx->sample_fmt == AV_SAMPLE_FMT_NONE) - return 0; + FAIL("unspecified sample format"); + if (!avctx->sample_rate) + FAIL("unspecified sample rate"); + if (!avctx->channels) + FAIL("unspecified number of channels"); + if (st->info->found_decoder >= 0 && !st->nb_decoded_frames && avctx->codec_id == AV_CODEC_ID_DTS) + FAIL("no decodable DTS frames"); break; case AVMEDIA_TYPE_VIDEO: - val = avctx->width; + if (!avctx->width) + FAIL("unspecified size"); if (st->info->found_decoder >= 0 && avctx->pix_fmt == AV_PIX_FMT_NONE) - return 0; + FAIL("unspecified pixel format"); break; - default: - val = 1; + case AVMEDIA_TYPE_SUBTITLE: + if (avctx->codec_id == AV_CODEC_ID_HDMV_PGS_SUBTITLE && !avctx->width) + FAIL("unspecified size"); break; + case AVMEDIA_TYPE_DATA: + if(avctx->codec_id == AV_CODEC_ID_NONE) return 1; } - return avctx->codec_id != AV_CODEC_ID_NONE && val != 0; -} -static int has_decode_delay_been_guessed(AVStream *st) -{ - return st->codec->codec_id != AV_CODEC_ID_H264 || - st->info->nb_decoded_frames >= 6; + if (avctx->codec_id == AV_CODEC_ID_NONE) + FAIL("unknown codec"); + return 1; } /* returns 1 or 0 if or if not decoded data was returned, or a negative error */ @@ -2040,6 +2331,7 @@ static int try_decode_frame(AVStream *st, AVPacket *avpkt, AVDictionary **option const AVCodec *codec; int got_picture = 1, ret = 0; AVFrame *frame = avcodec_alloc_frame(); + AVSubtitle subtitle; AVPacket pkt = *avpkt; if (!frame) @@ -2078,7 +2370,7 @@ static int try_decode_frame(AVStream *st, AVPacket *avpkt, AVDictionary **option while ((pkt.size > 0 || (!pkt.data && got_picture)) && ret >= 0 && - (!has_codec_parameters(st) || + (!has_codec_parameters(st, NULL) || !has_decode_delay_been_guessed(st) || (!st->codec_info_nb_frames && st->codec->codec->capabilities & CODEC_CAP_CHANNEL_CONF))) { got_picture = 0; @@ -2091,18 +2383,26 @@ static int try_decode_frame(AVStream *st, AVPacket *avpkt, AVDictionary **option case AVMEDIA_TYPE_AUDIO: ret = avcodec_decode_audio4(st->codec, frame, &got_picture, &pkt); break; + case AVMEDIA_TYPE_SUBTITLE: + ret = avcodec_decode_subtitle2(st->codec, &subtitle, + &got_picture, &pkt); + ret = pkt.size; + break; default: break; } if (ret >= 0) { if (got_picture) - st->info->nb_decoded_frames++; + st->nb_decoded_frames++; pkt.data += ret; pkt.size -= ret; ret = got_picture; } } + if(!pkt.data && !got_picture) + ret = -1; + fail: avcodec_free_frame(&frame); return ret; @@ -2174,8 +2474,8 @@ static void compute_chapters_end(AVFormatContext *s) } static int get_std_framerate(int i){ - if(i<60*12) return i*1001; - else return ((const int[]){24,30,60,12,15})[i-60*12]*1000*12; + if(i<60*12) return (i+1)*1001; + else return ((const int[]){24,30,60,12,15,48})[i-60*12]*1000*12; } /* @@ -2191,6 +2491,7 @@ static int tb_unreliable(AVCodecContext *c){ || c->time_base.den < 5L*c->time_base.num /* || c->codec_tag == AV_RL32("DIVX") || c->codec_tag == AV_RL32("XVID")*/ + || c->codec_tag == AV_RL32("mp4v") || c->codec_id == AV_CODEC_ID_MPEG2VIDEO || c->codec_id == AV_CODEC_ID_H264 ) @@ -2198,6 +2499,13 @@ static int tb_unreliable(AVCodecContext *c){ return 0; } +#if FF_API_FORMAT_PARAMETERS +int av_find_stream_info(AVFormatContext *ic) +{ + return avformat_find_stream_info(ic, NULL); +} +#endif + int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options) { int i, count, ret, read_size, j; @@ -2205,17 +2513,36 @@ int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options) AVPacket pkt1, *pkt; int64_t old_offset = avio_tell(ic->pb); int orig_nb_streams = ic->nb_streams; // new streams might appear, no options for those + int flush_codecs = ic->probesize > 0; + + if(ic->pb) + av_log(ic, AV_LOG_DEBUG, "File position before avformat_find_stream_info() is %"PRId64"\n", avio_tell(ic->pb)); for(i=0;i<ic->nb_streams;i++) { const AVCodec *codec; AVDictionary *thread_opt = NULL; st = ic->streams[i]; + if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO || + st->codec->codec_type == AVMEDIA_TYPE_SUBTITLE) { +/* if(!st->time_base.num) + st->time_base= */ + if(!st->codec->time_base.num) + st->codec->time_base= st->time_base; + } //only for the split stuff if (!st->parser && !(ic->flags & AVFMT_FLAG_NOPARSE)) { st->parser = av_parser_init(st->codec->codec_id); - if(st->need_parsing == AVSTREAM_PARSE_HEADERS && st->parser){ - st->parser->flags |= PARSER_FLAG_COMPLETE_FRAMES; + if(st->parser){ + if(st->need_parsing == AVSTREAM_PARSE_HEADERS){ + st->parser->flags |= PARSER_FLAG_COMPLETE_FRAMES; + } else if(st->need_parsing == AVSTREAM_PARSE_FULL_RAW) { + st->parser->flags |= PARSER_FLAG_USE_CODEC_TS; + } + } else if (st->need_parsing) { + av_log(ic, AV_LOG_VERBOSE, "parser not found for codec " + "%s, packets or times may be invalid.\n", + avcodec_get_name(st->codec->codec_id)); } } codec = st->codec->codec ? st->codec->codec : @@ -2232,7 +2559,7 @@ int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options) : &thread_opt); //try to just open decoders, in case this is enough to get parameters - if (!has_codec_parameters(st)) { + if (!has_codec_parameters(st, NULL) && st->request_probe <= 0) { if (codec && !st->codec->codec) avcodec_open2(st->codec, codec, options ? &options[i] : &thread_opt); @@ -2263,7 +2590,7 @@ int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options) int fps_analyze_framecount = 20; st = ic->streams[i]; - if (!has_codec_parameters(st)) + if (!has_codec_parameters(st, NULL)) break; /* if the timebase is coarse (like the usual millisecond precision of mkv), we need to analyze more frames to reliably arrive at @@ -2273,8 +2600,8 @@ int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options) if (ic->fps_probe_size >= 0) fps_analyze_framecount = ic->fps_probe_size; /* variable fps and no guess at the real fps */ - if( tb_unreliable(st->codec) && !st->avg_frame_rate.num - && st->codec_info_nb_frames < fps_analyze_framecount + if( tb_unreliable(st->codec) && !(st->r_frame_rate.num && st->avg_frame_rate.num) + && st->info->duration_count < fps_analyze_framecount && st->codec->codec_type == AVMEDIA_TYPE_VIDEO) break; if(st->parser && st->parser->parser->split && !st->codec->extradata) @@ -2292,6 +2619,7 @@ 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; } } @@ -2299,6 +2627,12 @@ int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options) if (read_size >= ic->probesize) { ret = count; av_log(ic, AV_LOG_DEBUG, "Probe buffer size limit %d reached\n", ic->probesize); + for (i = 0; i < ic->nb_streams; i++) + if (!ic->streams[i]->r_frame_rate.num && + ic->streams[i]->info->duration_count <= 1) + av_log(ic, AV_LOG_WARNING, + "Stream #%d: not enough frames to estimate rate; " + "consider increasing probesize\n", i); break; } @@ -2310,35 +2644,6 @@ int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options) if (ret < 0) { /* EOF or error*/ - AVPacket empty_pkt = { 0 }; - int err = 0; - av_init_packet(&empty_pkt); - - ret = -1; /* we could not have all the codec parameters before EOF */ - for(i=0;i<ic->nb_streams;i++) { - st = ic->streams[i]; - - /* flush the decoders */ - if (st->info->found_decoder == 1) { - do { - err = try_decode_frame(st, &empty_pkt, - (options && i < orig_nb_streams) ? - &options[i] : NULL); - } while (err > 0 && !has_codec_parameters(st)); - } - - if (err < 0) { - av_log(ic, AV_LOG_WARNING, - "decoding for stream %d failed\n", st->index); - } else if (!has_codec_parameters(st)) { - char buf[256]; - avcodec_string(buf, sizeof(buf), st->codec, 0); - av_log(ic, AV_LOG_WARNING, - "Could not find codec parameters (%s)\n", buf); - } else { - ret = 0; - } - } break; } @@ -2358,7 +2663,7 @@ int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options) /* check for non-increasing dts */ if (st->info->fps_last_dts != AV_NOPTS_VALUE && st->info->fps_last_dts >= pkt->dts) { - av_log(ic, AV_LOG_WARNING, "Non-increasing DTS in stream %d: " + av_log(ic, AV_LOG_DEBUG, "Non-increasing DTS in stream %d: " "packet %d with DTS %"PRId64", packet %d with DTS " "%"PRId64"\n", st->index, st->info->fps_last_dts_idx, st->info->fps_last_dts, st->codec_info_nb_frames, pkt->dts); @@ -2385,36 +2690,49 @@ 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; + } + 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)); - /* check max_analyze_duration */ - if (av_rescale_q(pkt->dts - st->info->fps_first_dts, st->time_base, - AV_TIME_BASE_Q) >= ic->max_analyze_duration) { - av_log(ic, AV_LOG_WARNING, "max_analyze_duration reached\n"); + if (t >= ic->max_analyze_duration) { + av_log(ic, AV_LOG_WARNING, "max_analyze_duration %d reached at %"PRId64"\n", ic->max_analyze_duration, t); break; } + if (pkt->duration) { + st->info->codec_info_duration += pkt->duration; + st->info->codec_info_duration_fields += st->parser && st->codec->ticks_per_frame==2 ? st->parser->repeat_pict + 1 : 2; + } } #if FF_API_R_FRAME_RATE { int64_t last = st->info->last_dts; if(pkt->dts != AV_NOPTS_VALUE && last != AV_NOPTS_VALUE && pkt->dts > last){ + double dts= (is_relative(pkt->dts) ? pkt->dts - RELATIVE_TS_BASE : pkt->dts) * av_q2d(st->time_base); int64_t duration= pkt->dts - last; - double dur= duration * av_q2d(st->time_base); - if (st->info->duration_count < 2) - memset(st->info->duration_error, 0, sizeof(st->info->duration_error)); - for (i=1; i<FF_ARRAY_ELEMS(st->info->duration_error); i++) { +// if(st->codec->codec_type == AVMEDIA_TYPE_VIDEO) +// av_log(NULL, AV_LOG_ERROR, "%f\n", dts); + for (i=0; i<FF_ARRAY_ELEMS(st->info->duration_error[0][0]); i++) { int framerate= get_std_framerate(i); - int ticks= lrintf(dur*framerate/(1001*12)); - double error = dur - (double)ticks*1001*12 / framerate; - st->info->duration_error[i] += error*error; + double sdts= dts*framerate/(1001*12); + for(j=0; j<2; j++){ + int ticks= lrintf(sdts+j*0.5); + double error= sdts - ticks + j*0.5; + st->info->duration_error[j][0][i] += error; + st->info->duration_error[j][1][i] += error*error; + } } st->info->duration_count++; // ignore the first 4 values, they might have some random jitter if (st->info->duration_count > 3) st->info->duration_gcd = av_gcd(st->info->duration_gcd, duration); } - if (last == AV_NOPTS_VALUE || st->info->duration_count <= 1) + if (pkt->dts != AV_NOPTS_VALUE) st->info->last_dts = pkt->dts; } #endif @@ -2445,6 +2763,44 @@ int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options) count++; } + if (flush_codecs) { + AVPacket empty_pkt = { 0 }; + int err = 0; + av_init_packet(&empty_pkt); + + ret = -1; /* we could not have all the codec parameters before EOF */ + for(i=0;i<ic->nb_streams;i++) { + const char *errmsg; + + 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, NULL)); + + if (err < 0) { + av_log(ic, AV_LOG_INFO, + "decoding for stream %d failed\n", st->index); + } + } + + 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; + } + } + } + // close codecs which were opened in try_decode_frame() for(i=0;i<ic->nb_streams;i++) { st = ic->streams[i]; @@ -2453,16 +2809,20 @@ int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options) for(i=0;i<ic->nb_streams;i++) { st = ic->streams[i]; if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) { + if(st->codec->codec_id == AV_CODEC_ID_RAWVIDEO && !st->codec->codec_tag && !st->codec->bits_per_coded_sample){ + uint32_t tag= avcodec_pix_fmt_to_codec_tag(st->codec->pix_fmt); + if(ff_find_pix_fmt(ff_raw_pix_fmt_tags, tag) == st->codec->pix_fmt) + st->codec->codec_tag= tag; + } + /* estimate average framerate if not set by demuxer */ - if (!st->avg_frame_rate.num && st->info->fps_last_dts != st->info->fps_first_dts) { - int64_t delta_dts = st->info->fps_last_dts - st->info->fps_first_dts; - int delta_packets = st->info->fps_last_dts_idx - st->info->fps_first_dts_idx; + if (st->info->codec_info_duration_fields && !st->avg_frame_rate.num && st->info->codec_info_duration) { int best_fps = 0; double best_error = 0.01; 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*/ @@ -2480,30 +2840,51 @@ int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options) best_fps, 12*1001, INT_MAX); } } -#if FF_API_R_FRAME_RATE // 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 > 1 && !st->r_frame_rate.num) + 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 && !st->r_frame_rate.num && tb_unreliable(st->codec)) { int num = 0; - double best_error= 2*av_q2d(st->time_base); - best_error = best_error*best_error*st->info->duration_count*1000*12*30; - - for (j=1; j<FF_ARRAY_ELEMS(st->info->duration_error); j++) { - double error = st->info->duration_error[j] * get_std_framerate(j); - if(error < best_error){ - best_error= error; - num = get_std_framerate(j); + double best_error= 0.01; + + for (j=0; j<FF_ARRAY_ELEMS(st->info->duration_error[0][0]); j++) { + int k; + + if(st->info->codec_info_duration && st->info->codec_info_duration*av_q2d(st->time_base) < (1001*12.0)/get_std_framerate(j)) + continue; + if(!st->info->codec_info_duration && 1.0 < (1001*12.0)/get_std_framerate(j)) + continue; + for(k=0; k<2; k++){ + int n= st->info->duration_count; + double a= st->info->duration_error[k][0][j] / n; + double error= st->info->duration_error[k][1][j]/n - a*a; + + if(error < best_error && best_error> 0.000000001){ + best_error= error; + num = get_std_framerate(j); + } + if(error < 0.02) + av_log(NULL, AV_LOG_DEBUG, "rfps: %f %f\n", get_std_framerate(j) / 12.0/1001, error); } } // do not increase frame rate by more than 1 % in order to match a standard rate. if (num && (!st->r_frame_rate.num || (double)num/(12*1001) < 1.01 * av_q2d(st->r_frame_rate))) av_reduce(&st->r_frame_rate.num, &st->r_frame_rate.den, num, 12*1001, INT_MAX); } -#endif + + if (!st->r_frame_rate.num){ + if( st->codec->time_base.den * (int64_t)st->time_base.num + <= st->codec->time_base.num * st->codec->ticks_per_frame * (int64_t)st->time_base.den){ + st->r_frame_rate.num = st->codec->time_base.den; + st->r_frame_rate.den = st->codec->time_base.num * st->codec->ticks_per_frame; + }else{ + st->r_frame_rate.num = st->time_base.den; + st->r_frame_rate.den = st->time_base.num; + } + } }else if(st->codec->codec_type == AVMEDIA_TYPE_AUDIO) { if(!st->codec->bits_per_coded_sample) st->codec->bits_per_coded_sample= av_get_bits_per_sample(st->codec->codec_id); @@ -2523,6 +2904,7 @@ int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options) } } + if(ic->probesize) estimate_timings(ic, old_offset); compute_chapters_end(ic); @@ -2533,17 +2915,25 @@ int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options) ic->streams[i]->codec->thread_count = 0; av_freep(&ic->streams[i]->info); } + if(ic->pb) + av_log(ic, AV_LOG_DEBUG, "File position after avformat_find_stream_info() is %"PRId64"\n", avio_tell(ic->pb)); return ret; } -static AVProgram *find_program_from_stream(AVFormatContext *ic, int s) +AVProgram *av_find_program_from_stream(AVFormatContext *ic, AVProgram *last, int s) { int i, j; - for (i = 0; i < ic->nb_programs; i++) - for (j = 0; j < ic->programs[i]->nb_stream_indexes; j++) - if (ic->programs[i]->stream_index[j] == s) - return ic->programs[i]; + for (i = 0; i < ic->nb_programs; i++) { + if (ic->programs[i] == last) { + last = NULL; + } else { + if (!last) + for (j = 0; j < ic->programs[i]->nb_stream_indexes; j++) + if (ic->programs[i]->stream_index[j] == s) + return ic->programs[i]; + } + } return NULL; } @@ -2560,7 +2950,7 @@ int av_find_best_stream(AVFormatContext *ic, 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; @@ -2620,31 +3010,39 @@ int av_read_pause(AVFormatContext *s) return AVERROR(ENOSYS); } +void ff_free_stream(AVFormatContext *s, AVStream *st){ + av_assert0(s->nb_streams>0); + av_assert0(s->streams[ s->nb_streams-1 ] == st); + + if (st->parser) { + av_parser_close(st->parser); + } + if (st->attached_pic.data) + av_free_packet(&st->attached_pic); + av_dict_free(&st->metadata); + av_freep(&st->index_entries); + av_freep(&st->codec->extradata); + av_freep(&st->codec->subtitle_header); + av_freep(&st->codec); + av_freep(&st->priv_data); + av_freep(&st->info); + av_freep(&st->probe_data.buf); + av_freep(&s->streams[ --s->nb_streams ]); +} + void avformat_free_context(AVFormatContext *s) { int i; - AVStream *st; + + if (!s) + return; av_opt_free(s); if (s->iformat && s->iformat->priv_class && s->priv_data) av_opt_free(s->priv_data); - for(i=0;i<s->nb_streams;i++) { - /* free all data in a stream component */ - st = s->streams[i]; - if (st->parser) { - av_parser_close(st->parser); - } - if (st->attached_pic.data) - av_free_packet(&st->attached_pic); - av_dict_free(&st->metadata); - av_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); @@ -2655,7 +3053,7 @@ void avformat_free_context(AVFormatContext *s) av_freep(&s->priv_data); while(s->nb_chapters--) { av_dict_free(&s->chapters[s->nb_chapters]->metadata); - av_free(s->chapters[s->nb_chapters]); + av_freep(&s->chapters[s->nb_chapters]); } av_freep(&s->chapters); av_dict_free(&s->metadata); @@ -2693,7 +3091,17 @@ void avformat_close_input(AVFormatContext **ps) avio_close(pb); } -AVStream *avformat_new_stream(AVFormatContext *s, AVCodec *c) +#if FF_API_NEW_STREAM +AVStream *av_new_stream(AVFormatContext *s, int id) +{ + AVStream *st = avformat_new_stream(s, NULL); + if (st) + st->id = id; + return st; +} +#endif + +AVStream *avformat_new_stream(AVFormatContext *s, const AVCodec *c) { AVStream *st; int i; @@ -2713,6 +3121,7 @@ AVStream *avformat_new_stream(AVFormatContext *s, AVCodec *c) av_free(st); return NULL; } + st->info->last_dts = AV_NOPTS_VALUE; st->codec = avcodec_alloc_context3(c); if (s->iformat) { @@ -2726,7 +3135,7 @@ AVStream *avformat_new_stream(AVFormatContext *s, AVCodec *c) but durations get some timestamps, formats with some unknown timestamps have their first few packets buffered and the timestamps corrected before they are returned to the user */ - st->cur_dts = 0; + st->cur_dts = s->iformat ? RELATIVE_TS_BASE : 0; st->first_dts = AV_NOPTS_VALUE; st->probe_packets = MAX_PROBE_PACKETS; @@ -2769,6 +3178,9 @@ AVProgram *av_new_program(AVFormatContext *ac, int id) } program->id = id; + program->start_time = + program->end_time = AV_NOPTS_VALUE; + return program; } @@ -2838,8 +3250,21 @@ static void dump_metadata(void *ctx, AVDictionary *m, const char *indent) av_log(ctx, AV_LOG_INFO, "%sMetadata:\n", indent); while((tag=av_dict_get(m, "", tag, AV_DICT_IGNORE_SUFFIX))) { - if(strcmp("language", tag->key)) - av_log(ctx, AV_LOG_INFO, "%s %-16s: %s\n", indent, tag->key, tag->value); + if(strcmp("language", tag->key)){ + const char *p = tag->value; + av_log(ctx, AV_LOG_INFO, "%s %-16s: ", indent, tag->key); + while(*p) { + char tmp[256]; + size_t len = strcspn(p, "\x8\xa\xb\xc\xd"); + av_strlcpy(tmp, p, FFMIN(sizeof(tmp), len+1)); + av_log(ctx, AV_LOG_INFO, "%s", tmp); + p += len; + if (*p == 0xd) av_log(ctx, AV_LOG_INFO, " "); + if (*p == 0xa) av_log(ctx, AV_LOG_INFO, "\n%s %-16s: ", indent, ""); + if (*p) p++; + } + av_log(ctx, AV_LOG_INFO, "\n"); + } } } } @@ -2853,7 +3278,7 @@ static void dump_stream_format(AVFormatContext *ic, int i, int index, int is_out int g = av_gcd(st->time_base.num, st->time_base.den); AVDictionaryEntry *lang = av_dict_get(st->metadata, "language", NULL, 0); avcodec_string(buf, sizeof(buf), st->codec, is_output); - av_log(NULL, AV_LOG_INFO, " Stream #%d.%d", index, i); + av_log(NULL, AV_LOG_INFO, " Stream #%d:%d", index, i); /* the pid is an important information, so we display it */ /* XXX: add a generic system */ if (flags & AVFMT_SHOW_IDS) @@ -2869,7 +3294,7 @@ static void dump_stream_format(AVFormatContext *ic, int i, int index, int is_out st->codec->width*st->sample_aspect_ratio.num, st->codec->height*st->sample_aspect_ratio.den, 1024*1024); - av_log(NULL, AV_LOG_INFO, ", PAR %d:%d DAR %d:%d", + av_log(NULL, AV_LOG_INFO, ", SAR %d:%d DAR %d:%d", st->sample_aspect_ratio.num, st->sample_aspect_ratio.den, display_aspect_ratio.num, display_aspect_ratio.den); } @@ -3121,11 +3546,27 @@ static void pkt_dump_internal(void *avcl, FILE *f, int level, AVPacket *pkt, int av_hex_dump(f, pkt->data, pkt->size); } +#if FF_API_PKT_DUMP +void av_pkt_dump(FILE *f, AVPacket *pkt, int dump_payload) +{ + AVRational tb = { 1, AV_TIME_BASE }; + pkt_dump_internal(NULL, f, 0, pkt, dump_payload, tb); +} +#endif + void av_pkt_dump2(FILE *f, AVPacket *pkt, int dump_payload, AVStream *st) { pkt_dump_internal(NULL, f, 0, pkt, dump_payload, st->time_base); } +#if FF_API_PKT_DUMP +void av_pkt_dump_log(void *avcl, int level, AVPacket *pkt, int dump_payload) +{ + AVRational tb = { 1, AV_TIME_BASE }; + pkt_dump_internal(avcl, NULL, level, pkt, dump_payload, tb); +} +#endif + void av_pkt_dump_log2(void *avcl, int level, AVPacket *pkt, int dump_payload, AVStream *st) { @@ -3139,7 +3580,7 @@ void av_url_split(char *proto, int proto_size, char *path, int path_size, const char *url) { - const char *p, *ls, *at, *col, *brk; + const char *p, *ls, *ls2, *at, *col, *brk; if (port_ptr) *port_ptr = -1; if (proto_size > 0) proto[0] = 0; @@ -3161,8 +3602,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 @@ -3242,6 +3686,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) { @@ -3253,10 +3705,11 @@ void avpriv_set_pts_info(AVStream *s, int pts_wrap_bits, av_log(NULL, AV_LOG_WARNING, "st:%d has too large timebase, reducing\n", s->index); if(new_tb.num <= 0 || new_tb.den <= 0) { - av_log(NULL, AV_LOG_ERROR, "Ignoring attempt to set invalid timebase for st:%d\n", s->index); + av_log(NULL, AV_LOG_ERROR, "Ignoring attempt to set invalid timebase %d/%d for st:%d\n", new_tb.num, new_tb.den, s->index); return; } s->time_base = new_tb; + av_codec_set_pkt_timebase(s->codec, new_tb); s->pts_wrap_bits = pts_wrap_bits; } @@ -3457,20 +3910,14 @@ void ff_make_absolute_url(char *buf, int size, const char *base, int64_t ff_iso8601_to_unix_time(const char *datestr) { -#if HAVE_STRPTIME struct tm time1 = {0}, time2 = {0}; char *ret1, *ret2; - ret1 = strptime(datestr, "%Y - %m - %d %T", &time1); - ret2 = strptime(datestr, "%Y - %m - %dT%T", &time2); + ret1 = av_small_strptime(datestr, "%Y - %m - %d %H:%M:%S", &time1); + ret2 = av_small_strptime(datestr, "%Y - %m - %dT%H:%M:%S", &time2); if (ret2 && !ret1) return av_timegm(&time2); else return av_timegm(&time1); -#else - av_log(NULL, AV_LOG_WARNING, "strptime() unavailable on this system, cannot convert " - "the date string.\n"); - return 0; -#endif } int avformat_query_codec(AVOutputFormat *ofmt, enum AVCodecID codec_id, int std_compliance) @@ -3558,3 +4005,87 @@ const struct AVCodecTag *avformat_get_riff_audio_tags(void) { return ff_codec_wav_tags; } + +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; +} + +int avformat_match_stream_specifier(AVFormatContext *s, AVStream *st, + const char *spec) +{ + if (*spec <= '9' && *spec >= '0') /* opt:index */ + return strtol(spec, NULL, 0) == st->index; + else if (*spec == 'v' || *spec == 'a' || *spec == 's' || *spec == 'd' || + *spec == 't') { /* opt:[vasdt] */ + enum AVMediaType type; + + switch (*spec++) { + case 'v': type = AVMEDIA_TYPE_VIDEO; break; + case 'a': type = AVMEDIA_TYPE_AUDIO; break; + case 's': type = AVMEDIA_TYPE_SUBTITLE; break; + case 'd': type = AVMEDIA_TYPE_DATA; break; + case 't': type = AVMEDIA_TYPE_ATTACHMENT; break; + default: av_assert0(0); + } + if (type != st->codec->codec_type) + return 0; + if (*spec++ == ':') { /* possibly followed by :index */ + int i, index = strtol(spec, NULL, 0); + for (i = 0; i < s->nb_streams; i++) + if (s->streams[i]->codec->codec_type == type && index-- == 0) + return i == st->index; + return 0; + } + return 1; + } else if (*spec == 'p' && *(spec + 1) == ':') { + int prog_id, i, j; + char *endptr; + spec += 2; + prog_id = strtol(spec, &endptr, 0); + for (i = 0; i < s->nb_programs; i++) { + if (s->programs[i]->id != prog_id) + continue; + + if (*endptr++ == ':') { + int stream_idx = strtol(endptr, NULL, 0); + return stream_idx >= 0 && + stream_idx < s->programs[i]->nb_stream_indexes && + st->index == s->programs[i]->stream_index[stream_idx]; + } + + for (j = 0; j < s->programs[i]->nb_stream_indexes; j++) + if (st->index == s->programs[i]->stream_index[j]) + return 1; + } + return 0; + } else if (*spec == '#') { + int sid; + char *endptr; + sid = strtol(spec + 1, &endptr, 0); + if (!*endptr) + return st->id == sid; + } else if (!*spec) /* empty specifier, matches everything */ + return 1; + + av_log(s, AV_LOG_ERROR, "Invalid stream specifier: %s.\n", spec); + return AVERROR(EINVAL); +} diff --git a/libavformat/vc1test.c b/libavformat/vc1test.c index 1895fc4153..e411a257f1 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 */ @@ -92,7 +92,7 @@ static int vc1t_read_packet(AVFormatContext *s, int keyframe = 0; uint32_t pts; - if(pb->eof_reached) + if(url_feof(pb)) return AVERROR(EIO); frame_size = avio_rl24(pb); diff --git a/libavformat/vc1testenc.c b/libavformat/vc1testenc.c index abe8c6b231..e084516b60 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" @@ -85,7 +85,6 @@ static int vc1test_write_trailer(AVFormatContext *s) AVOutputFormat ff_vc1t_muxer = { .name = "rcv", .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 034fa0adce..bab294c73c 100644 --- a/libavformat/version.h +++ b/libavformat/version.h @@ -1,20 +1,20 @@ /* * Version macros. * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -30,8 +30,8 @@ #include "libavutil/avutil.h" #define LIBAVFORMAT_VERSION_MAJOR 54 -#define LIBAVFORMAT_VERSION_MINOR 19 -#define LIBAVFORMAT_VERSION_MICRO 0 +#define LIBAVFORMAT_VERSION_MINOR 35 +#define LIBAVFORMAT_VERSION_MICRO 100 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \ LIBAVFORMAT_VERSION_MINOR, \ @@ -49,6 +49,24 @@ * the public API and may change, break or disappear at any time. */ +#ifndef FF_API_OLD_AVIO +#define FF_API_OLD_AVIO (LIBAVFORMAT_VERSION_MAJOR < 55) +#endif +#ifndef FF_API_PKT_DUMP +#define FF_API_PKT_DUMP (LIBAVFORMAT_VERSION_MAJOR < 54) +#endif +#ifndef FF_API_ALLOC_OUTPUT_CONTEXT +#define FF_API_ALLOC_OUTPUT_CONTEXT (LIBAVFORMAT_VERSION_MAJOR < 55) +#endif +#ifndef FF_API_FORMAT_PARAMETERS +#define FF_API_FORMAT_PARAMETERS (LIBAVFORMAT_VERSION_MAJOR < 55) +#endif +#ifndef FF_API_NEW_STREAM +#define FF_API_NEW_STREAM (LIBAVFORMAT_VERSION_MAJOR < 55) +#endif +#ifndef FF_API_SET_PTS_INFO +#define FF_API_SET_PTS_INFO (LIBAVFORMAT_VERSION_MAJOR < 55) +#endif #ifndef FF_API_CLOSE_INPUT_FILE #define FF_API_CLOSE_INPUT_FILE (LIBAVFORMAT_VERSION_MAJOR < 55) #endif diff --git a/libavformat/voc.c b/libavformat/voc.c index 1b7d499278..2a972344d3 100644 --- a/libavformat/voc.c +++ b/libavformat/voc.c @@ -2,20 +2,20 @@ * Creative Voice File common data. * Copyright (c) 2006 Aurelien Jacobs <aurel@gnuage.org> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/voc.h b/libavformat/voc.h index 1668618fda..90178b3175 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 4e06513735..62215fc735 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 */ @@ -150,7 +150,7 @@ ff_voc_get_packet(AVFormatContext *s, AVPacket *pkt, AVStream *st, int max_size) } } - dec->bit_rate = dec->sample_rate * dec->bits_per_coded_sample; + dec->bit_rate = dec->sample_rate * dec->channels * dec->bits_per_coded_sample; if (max_size <= 0) max_size = 2048; diff --git a/libavformat/vocenc.c b/libavformat/vocenc.c index d18eac2d95..d97cdc4c15 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 */ @@ -52,7 +52,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 +64,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 +95,7 @@ AVOutputFormat ff_voc_muxer = { .mime_type = "audio/x-voc", .extensions = "voc", .priv_data_size = sizeof(VocEncContext), - .audio_codec = AV_CODEC_ID_PCM_U8, + .audio_codec = AV_CODEC_ID_PCM_S16LE, .video_codec = AV_CODEC_ID_NONE, .write_header = voc_write_header, .write_packet = voc_write_packet, diff --git a/libavformat/vorbiscomment.c b/libavformat/vorbiscomment.c index 56936d7666..9b38e6a791 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 */ diff --git a/libavformat/vorbiscomment.h b/libavformat/vorbiscomment.h index 95e1a5676f..4e631e3553 100644 --- a/libavformat/vorbiscomment.h +++ b/libavformat/vorbiscomment.h @@ -2,20 +2,20 @@ * VorbisComment writer * Copyright (c) 2009 James Darnley * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/vqf.c b/libavformat/vqf.c index 66ced37bad..5fff84e049 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 */ diff --git a/libavformat/wavdec.c b/libavformat/wavdec.c index c9f7abbbcf..7a54b1b8ed 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 */ @@ -37,8 +37,17 @@ #include "metadata.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; } WAVDemuxContext; @@ -57,7 +66,7 @@ static int64_t find_tag(AVIOContext *pb, uint32_t tag1) int64_t size; for (;;) { - if (pb->eof_reached) + if (url_feof(pb)) return -1; size = next_tag(pb, &tag); if (tag == tag1) @@ -87,6 +96,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_MAX/2; + st->probe_packets = FFMIN(st->probe_packets, 4); + } +} + static int wav_parse_fmt_tag(AVFormatContext *s, int64_t size, AVStream **st) { AVIOContext *pb = s->pb; @@ -100,7 +117,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); @@ -158,7 +177,7 @@ static int wav_parse_bext_tag(AVFormatContext *s, int64_t size) } else { /* 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], umid_parts[6], umid_parts[7]); } @@ -211,6 +230,8 @@ static int wav_read_header(AVFormatContext *s) int ret, got_fmt = 0; int64_t next_tag_ofs, data_ofs = -1; + wav->smv_data_ofs = -1; + /* check RIFF header */ tag = avio_rl32(pb); @@ -226,7 +247,7 @@ static int wav_read_header(AVFormatContext *s) if (avio_rl32(pb) != MKTAG('d', 's', '6', '4')) return -1; size = avio_rl32(pb); - if (size < 16) + if (size < 24) return -1; avio_rl64(pb); /* RIFF size */ data_size = avio_rl64(pb); @@ -237,20 +258,22 @@ static int wav_read_header(AVFormatContext *s) data_size, sample_count); return AVERROR_INVALIDDATA; } - avio_skip(pb, size - 16); /* skip rest of ds64 chunk */ + avio_skip(pb, size - 24); /* skip rest of ds64 chunk */ + } for (;;) { + AVStream *vst; size = next_tag(pb, &tag); next_tag_ofs = avio_tell(pb) + size; - if (pb->eof_reached) + if (url_feof(pb)) break; switch (tag) { case MKTAG('f', 'm', 't', ' '): /* only parse the first 'fmt ' tag found */ - if (!got_fmt && (ret = wav_parse_fmt_tag(s, size, &st) < 0)) { + if (!got_fmt && (ret = wav_parse_fmt_tag(s, size, &st)) < 0) { return ret; } else if (got_fmt) av_log(s, AV_LOG_WARNING, "found more than one 'fmt ' tag\n"); @@ -286,9 +309,39 @@ 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"); + 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_MJPEG; + vst->codec->width = avio_rl24(pb); + vst->codec->height = avio_rl24(pb); + 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); + 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)) { @@ -332,7 +385,7 @@ static int64_t find_guid(AVIOContext *pb, const uint8_t guid1[16]) uint8_t guid[16]; int64_t size; - while (!pb->eof_reached) { + while (!url_feof(pb)) { avio_read(pb, guid, 16); size = avio_rl64(pb); if (size <= 24) @@ -357,16 +410,61 @@ static int wav_read_packet(AVFormatContext *s, AVStream *st; WAVDemuxContext *wav = s->priv_data; + if (wav->smv_data_ofs > 0) { + int64_t audio_dts, video_dts; +smv_retry: + audio_dts = s->streams[0]->cur_dts; + video_dts = s->streams[1]->cur_dts; + if (audio_dts != AV_NOPTS_VALUE && video_dts != AV_NOPTS_VALUE) { + audio_dts = av_rescale_q(audio_dts, s->streams[0]->time_base, AV_TIME_BASE_Q); + video_dts = av_rescale_q(video_dts, s->streams[1]->time_base, AV_TIME_BASE_Q); + wav->smv_last_stream = video_dts >= audio_dts; + } + 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_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; else left = find_tag(s->pb, MKTAG('d', 'a', 't', 'a')); - if (left < 0) + 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; } @@ -388,7 +486,18 @@ static int wav_read_packet(AVFormatContext *s, 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); + wav->smv_block = smv_timestamp / wav->smv_frames_per_jpeg; + } st = s->streams[0]; switch (st->codec->codec_id) { @@ -404,6 +513,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)"), @@ -414,6 +536,7 @@ 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 */ @@ -477,7 +600,8 @@ static int w64_read_header(AVFormatContext *s) return ret; avio_skip(pb, FFALIGN(size, INT64_C(8)) - size); - 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); diff --git a/libavformat/wavenc.c b/libavformat/wavenc.c index d5e572fd2f..f8298001c8 100644 --- a/libavformat/wavenc.c +++ b/libavformat/wavenc.c @@ -2,20 +2,20 @@ * WAV muxer * Copyright (c) 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/wc3movie.c b/libavformat/wc3movie.c index 117e4ead47..836f567a20 100644 --- a/libavformat/wc3movie.c +++ b/libavformat/wc3movie.c @@ -2,20 +2,20 @@ * Wing Commander III Movie (.mve) File Demuxer * Copyright (c) 2003 The ffmpeg Project * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -157,7 +157,7 @@ static int wc3_read_header(AVFormatContext *s) fourcc_tag = avio_rl32(pb); /* chunk sizes are 16-bit aligned */ size = (avio_rb32(pb) + 1) & (~1); - if (pb->eof_reached) + if (url_feof(pb)) return AVERROR(EIO); } while (fourcc_tag != BRCH_TAG); @@ -208,7 +208,7 @@ static int wc3_read_packet(AVFormatContext *s, fourcc_tag = avio_rl32(pb); /* chunk sizes are 16-bit aligned */ size = (avio_rb32(pb) + 1) & (~1); - if (pb->eof_reached) + if (url_feof(pb)) return AVERROR(EIO); switch (fourcc_tag) { diff --git a/libavformat/webvttdec.c b/libavformat/webvttdec.c new file mode 100644 index 0000000000..aeb705095f --- /dev/null +++ b/libavformat/webvttdec.c @@ -0,0 +1,188 @@ +/* + * 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" + +typedef struct { + FFDemuxSubtitlesQueue q; +} 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 int64_t extract_cue(AVBPrint *buf, AVIOContext *pb) +{ + int prev_chr_is_eol = 0; + int64_t pos = avio_tell(pb); + + av_bprint_clear(buf); + for (;;) { + char c = avio_r8(pb); + if (!c) + break; + if (c == '\r' || c == '\n') { + if (prev_chr_is_eol) + break; + prev_chr_is_eol = (c == '\n'); + } else + prev_chr_is_eol = 0; + if (c != '\r') + av_bprint_chars(buf, c, 1); + } + av_bprint_chars(buf, '\0', 1); + return pos; +} + +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; + + av_bprint_init(&header, 0, AV_BPRINT_SIZE_UNLIMITED); + av_bprint_init(&cue, 0, AV_BPRINT_SIZE_UNLIMITED); + + for (;;) { + int i, len; + int64_t pos = extract_cue(&cue, s->pb); + AVPacket *sub; + const char *p = cue.str; + const char *identifier = p; + //const char *settings = NULL; + int64_t ts_start, ts_end; + + if (!*p) // EOF + break; + + /* 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), silently skip it */ + for (i = 0; p[i] && p[i] != '\n'; i++) { + if (!strncmp(p + i, "-->", 3)) { + identifier = NULL; + break; + } + } + if (identifier) + p += strcspn(p, "\n"); + + /* cue timestamps */ + if ((ts_start = read_ts(p)) == AV_NOPTS_VALUE) + break; + if (!(p = strstr(p, "-->"))) + break; + p += 3; + do p++; while (*p == ' ' || *p == '\t'); + if ((ts_end = read_ts(p)) == AV_NOPTS_VALUE) + break; + + /* optional cue settings, TODO: store in side_data */ + p += strcspn(p, "\n\t "); + while (*p == '\t' || *p == ' ') + p++; + if (*p != '\n') { + //settings = p; + p += strcspn(p, "\n"); + } + if (*p == '\n') + p++; + + /* create packet */ + len = cue.str + cue.len - p - 1; + sub = ff_subtitles_queue_insert(&webvtt->q, p, len, 0); + if (!sub) { + res = AVERROR(ENOMEM); + goto end; + } + sub->pos = pos; + sub->pts = ts_start; + sub->duration = ts_end - ts_start; + } + + 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_close(AVFormatContext *s) +{ + WebVTTContext *webvtt = s->priv_data; + ff_subtitles_queue_clean(&webvtt->q); + return 0; +} + +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_close = webvtt_read_close, + .flags = AVFMT_GENERIC_INDEX, + .extensions = "vtt", +}; diff --git a/libavformat/westwood_aud.c b/libavformat/westwood_aud.c index 806aea66ea..2762cba405 100644 --- a/libavformat/westwood_aud.c +++ b/libavformat/westwood_aud.c @@ -2,20 +2,20 @@ * Westwood Studios AUD Format Demuxer * Copyright (c) 2003 The ffmpeg Project * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/libavformat/westwood_vqa.c b/libavformat/westwood_vqa.c index 7c9cb0db81..708f3e5fc3 100644 --- a/libavformat/westwood_vqa.c +++ b/libavformat/westwood_vqa.c @@ -2,20 +2,20 @@ * Westwood Studios VQA Format Demuxer * Copyright (c) 2003 The ffmpeg Project * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -131,10 +131,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) { - av_free(st->codec->extradata); + 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]); @@ -177,18 +175,15 @@ static int wsvqa_read_packet(AVFormatContext *s, while (avio_read(pb, preamble, VQA_PREAMBLE_SIZE) == VQA_PREAMBLE_SIZE) { chunk_type = AV_RB32(&preamble[0]); chunk_size = AV_RB32(&preamble[4]); + skip_byte = chunk_size & 0x01; if ((chunk_type == SND0_TAG) || (chunk_type == SND1_TAG) || (chunk_type == SND2_TAG) || (chunk_type == VQFR_TAG)) { - if (av_new_packet(pkt, chunk_size)) + ret= av_get_packet(pb, pkt, chunk_size); + if (ret<0) return AVERROR(EIO); - ret = avio_read(pb, pkt->data, chunk_size); - if (ret != chunk_size) { - av_free_packet(pkt); - return AVERROR(EIO); - } switch (chunk_type) { case SND0_TAG: diff --git a/libavformat/wtv.c b/libavformat/wtv.c index 57d239eb88..d85c3cfc10 100644 --- a/libavformat/wtv.c +++ b/libavformat/wtv.c @@ -1,1125 +1,76 @@ /* - * Windows Television (WTV) demuxer + * Windows Television (WTV) * 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 */ -/** - * @file - * Windows Television (WTV) demuxer - * @author Peter Ross <pross@xvid.org> - */ - -#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 "mpegts.h" - -/* Macros for formating GUIDs */ -#define PRI_GUID \ - "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x" -#define ARG_GUID(g) \ - g[0],g[1],g[2],g[3],g[4],g[5],g[6],g[7],g[8],g[9],g[10],g[11],g[12],g[13],g[14],g[15] - -#define PRI_PRETTY_GUID \ - "%08x-%04x-%04x-%02x%02x%02x%02x%02x%02x%02x%02x" -#define ARG_PRETTY_GUID(g) \ - AV_RL32(g),AV_RL16(g+4),AV_RL16(g+6),g[8],g[9],g[10],g[11],g[12],g[13],g[14],g[15] -#define LEN_PRETTY_GUID 34 - -/* - * - * File system routines - * - */ - -#define WTV_SECTOR_BITS 12 -#define WTV_SECTOR_SIZE (1 << WTV_SECTOR_BITS) -#define WTV_BIGSECTOR_BITS 18 - -typedef struct { - 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 error; - int64_t position; - int64_t length; -} WtvFile; - -/** - * @return bytes read, 0 on end of file, or <0 on error - */ -static int wtvfile_read_packet(void *opaque, uint8_t *buf, int buf_size) -{ - WtvFile *wf = opaque; - AVIOContext *pb = wf->pb_filesystem; - int nread = 0; - - if (wf->error || pb->error) - return -1; - if (wf->position >= wf->length || pb->eof_reached) - return 0; - - buf_size = FFMIN(buf_size, wf->length - wf->position); - while(nread < buf_size) { - int n; - int remaining_in_sector = (1 << wf->sector_bits) - (wf->position & ((1 << wf->sector_bits) - 1)); - int read_request = FFMIN(buf_size - nread, remaining_in_sector); - - n = avio_read(pb, buf, read_request); - if (n <= 0) - break; - nread += n; - buf += n; - wf->position += n; - if (n == remaining_in_sector) { - int i = wf->position >> wf->sector_bits; - if (i >= wf->nb_sectors || - (wf->sectors[i] != wf->sectors[i - 1] + (1 << (wf->sector_bits - WTV_SECTOR_BITS)) && - avio_seek(pb, (int64_t)wf->sectors[i] << WTV_SECTOR_BITS, SEEK_SET) < 0)) { - wf->error = 1; - break; - } - } - } - return nread; -} +#include "wtv.h" -/** - * @return position (or file length) - */ -static int64_t wtvfile_seek(void *opaque, int64_t offset, int whence) -{ - WtvFile *wf = opaque; - AVIOContext *pb = wf->pb_filesystem; - - if (whence == AVSEEK_SIZE) - return wf->length; - else if (whence == SEEK_CUR) - offset = wf->position + offset; - else if (whence == SEEK_END) - offset = wf->length; - - wf->error = offset < 0 || offset >= wf->length || - avio_seek(pb, ((int64_t)wf->sectors[offset >> wf->sector_bits] << WTV_SECTOR_BITS) - + (offset & ((1 << wf->sector_bits) - 1)), SEEK_SET) < 0; - wf->position = offset; - return offset; -} - -/** - * read non-zero integers (le32) from input stream - * @param pb - * @param[out] data destination - * @param count maximum number of integers to read - * @return total number of integers read - */ -static int read_ints(AVIOContext *pb, uint32_t *data, int count) -{ - int i, total = 0; - for (i = 0; i < count; i++) { - if ((data[total] = avio_rl32(pb))) - total++; - } - return total; -} - -/** - * Open file - * @param first_sector First sector - * @param length Length of file (bytes) - * @param depth File allocation table depth - * @return NULL on error - */ -static AVIOContext * wtvfile_open_sector(int first_sector, uint64_t length, int depth, AVFormatContext *s) -{ - AVIOContext *pb; - WtvFile *wf; - uint8_t *buffer; - - if (avio_seek(s->pb, first_sector << WTV_SECTOR_BITS, SEEK_SET) < 0) - return NULL; - - wf = av_mallocz(sizeof(WtvFile)); - if (!wf) - return NULL; - - if (depth == 0) { - wf->sectors = av_malloc(sizeof(uint32_t)); - if (!wf->sectors) { - av_free(wf); - return NULL; - } - 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) { - av_free(wf); - 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(nb_sectors1 << WTV_SECTOR_BITS); - if (!wf->sectors) { - av_free(wf); - return NULL; - } - wf->nb_sectors = 0; - for (i = 0; i < nb_sectors1; i++) { - if (avio_seek(s->pb, (int64_t)sectors1[i] << WTV_SECTOR_BITS, SEEK_SET) < 0) - 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; - } - - if (!wf->nb_sectors) { - av_free(wf->sectors); - av_free(wf); - return NULL; - } - - /* check length */ - length &= 0xFFFFFFFFFFFF; - if (length > ((int64_t)wf->nb_sectors << wf->sector_bits)) { - av_log(s, AV_LOG_WARNING, "reported file length (0x%"PRIx64") exceeds number of available sectors (0x%"PRIx64")\n", length, (int64_t)wf->nb_sectors << wf->sector_bits); - length = (int64_t)wf->nb_sectors << wf->sector_bits; - } - wf->length = length; - - /* seek to intial sector */ - wf->position = 0; - if (avio_seek(s->pb, (int64_t)wf->sectors[0] << WTV_SECTOR_BITS, SEEK_SET) < 0) { - av_free(wf->sectors); - av_free(wf); - return NULL; - } - - wf->pb_filesystem = s->pb; - buffer = av_malloc(1 << wf->sector_bits); - if (!buffer) { - av_free(wf->sectors); - av_free(wf); - return NULL; - } - - pb = avio_alloc_context(buffer, 1 << wf->sector_bits, 0, wf, - wtvfile_read_packet, NULL, wtvfile_seek); - if (!pb) { - av_free(buffer); - av_free(wf->sectors); - av_free(wf); - } - return pb; -} - -static const ff_asf_guid dir_entry_guid = +/* 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}; - -/** - * Open file using filename - * @param[in] buf directory buffer - * @param buf_size directory buffer size - * @param[in] filename - * @param filename_size size of filename - * @return NULL on error - */ -static AVIOContext * wtvfile_open2(AVFormatContext *s, const uint8_t *buf, int buf_size, const uint8_t *filename, int filename_size) -{ - const uint8_t *buf_end = buf + buf_size; - - while(buf + 48 <= buf_end) { - int dir_length, name_size, first_sector, depth; - uint64_t file_length; - const uint8_t *name; - if (ff_guidcmp(buf, dir_entry_guid)) { - av_log(s, AV_LOG_ERROR, "unknown guid "PRI_GUID", expected dir_entry_guid; " - "remaining directory entries ignored\n", ARG_GUID(buf)); - break; - } - dir_length = AV_RL16(buf + 16); - file_length = AV_RL64(buf + 24); - name_size = 2 * AV_RL32(buf + 32); - if (buf + 48 + name_size > buf_end) { - av_log(s, AV_LOG_ERROR, "filename exceeds buffer size; remaining directory entries ignored\n"); - break; - } - first_sector = AV_RL32(buf + 40 + name_size); - depth = AV_RL32(buf + 44 + name_size); - - /* compare file name; test optional null terminator */ - name = buf + 40; - if (name_size >= filename_size && - !memcmp(name, filename, filename_size) && - (name_size < filename_size + 2 || !AV_RN16(name + filename_size))) - return wtvfile_open_sector(first_sector, file_length, depth, s); - - buf += dir_length; - } - return 0; -} - -#define wtvfile_open(s, buf, buf_size, filename) \ - wtvfile_open2(s, buf, buf_size, filename, sizeof(filename)) - -/** - * Close file opened with wtvfile_open_sector(), or wtv_open() - */ -static void wtvfile_close(AVIOContext *pb) -{ - WtvFile *wf = pb->opaque; - av_free(wf->sectors); - av_free(wf); - av_free(pb->buffer); - av_free(pb); -} - -/* - * - * Main demuxer - * - */ - -typedef struct { - int seen_data; -} WtvStream; - -typedef struct { - 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 */ - - /* maintain private seek index, as the AVIndexEntry->pos is relative to the - start of the 'timeline' file, not the file system (AVFormatContext->pb) */ - AVIndexEntry *index_entries; - int nb_index_entries; - unsigned int index_entries_allocated_size; -} WtvContext; - -typedef struct { - enum AVCodecID id; - ff_asf_guid guid; -} AVCodecGuid; - -static enum AVCodecID ff_codec_guid_get_id(const AVCodecGuid *guids, ff_asf_guid guid) -{ - int i; - for (i = 0; guids[i].id != AV_CODEC_ID_NONE; i++) { - if (!ff_guidcmp(guids[i].guid, guid)) - return guids[i].id; - } - return AV_CODEC_ID_NONE; -} - -/* WTV GUIDs */ -static const ff_asf_guid wtv_guid = +const ff_asf_guid ff_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 = +const ff_asf_guid ff_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 = +const ff_asf_guid ff_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 = +const ff_asf_guid ff_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 = - {0x6D,0x66,0x92,0xE2,0x02,0x9C,0x8D,0x44,0xAA,0x8D,0x78,0x1A,0x93,0xFD,0xC3,0x95}; -static const ff_asf_guid EVENTID_AudioDescriptorSpanningEvent = - {0x1C,0xD4,0x7B,0x10,0xDA,0xA6,0x91,0x46,0x83,0x69,0x11,0xB2,0xCD,0xAA,0x28,0x8E}; -static const ff_asf_guid EVENTID_CtxADescriptorSpanningEvent = - {0xE6,0xA2,0xB4,0x3A,0x47,0x42,0x34,0x4B,0x89,0x6C,0x30,0xAF,0xA5,0xD2,0x1C,0x24}; -static const ff_asf_guid EVENTID_CSDescriptorSpanningEvent = - {0xD9,0x79,0xE7,0xEf,0xF0,0x97,0x86,0x47,0x80,0x0D,0x95,0xCF,0x50,0x5D,0xDC,0x66}; -static const ff_asf_guid EVENTID_DVBScramblingControlSpanningEvent = - {0xC4,0xE1,0xD4,0x4B,0xA1,0x90,0x09,0x41,0x82,0x36,0x27,0xF0,0x0E,0x7D,0xCC,0x5B}; -static const ff_asf_guid EVENTID_StreamIDSpanningEvent = - {0x68,0xAB,0xF1,0xCA,0x53,0xE1,0x41,0x4D,0xA6,0xB3,0xA7,0xC9,0x98,0xDB,0x75,0xEE}; -static const ff_asf_guid EVENTID_TeletextSpanningEvent = - {0x50,0xD9,0x99,0x95,0x33,0x5F,0x17,0x46,0xAF,0x7C,0x1E,0x54,0xB5,0x10,0xDA,0xA3}; -static const ff_asf_guid EVENTID_AudioTypeSpanningEvent = - {0xBE,0xBF,0x1C,0x50,0x49,0xB8,0xCE,0x42,0x9B,0xE9,0x3D,0xB8,0x69,0xFB,0x82,0xB3}; - -/* Windows media GUIDs */ +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}; -#define MEDIASUBTYPE_BASE_GUID \ - 0x00,0x00,0x10,0x00,0x80,0x00,0x00,0xAA,0x00,0x38,0x9B,0x71 +/* 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 _ -/* Media types */ -static const ff_asf_guid mediatype_audio = - {'a','u','d','s',MEDIASUBTYPE_BASE_GUID}; -static const ff_asf_guid mediatype_video = - {'v','i','d','s',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 = - {0x6C,0x17,0x5F,0x45,0x06,0x4B,0xCE,0x47,0x9A,0xEF,0x8C,0xAE,0xF7,0x3D,0xF7,0xB5}; -static const ff_asf_guid mediatype_mpeg2_pes = - {0x20,0x80,0x6D,0xE0,0x46,0xDB,0xCF,0x11,0xB4,0xD1,0x00,0x80,0x5F,0x6C,0xBB,0xEA}; -static const ff_asf_guid mediatype_mstvcaption = - {0x89,0x8A,0x8B,0xB8,0x49,0xB0,0x80,0x4C,0xAD,0xCF,0x58,0x98,0x98,0x5E,0x22,0xC1}; +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 */ -static const ff_asf_guid mediasubtype_cpfilters_processed = +const ff_asf_guid ff_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 = - {0xE3,0x76,0x2A,0xF7,0x0A,0xEB,0xD0,0x11,0xAC,0xE4,0x00,0x00,0xC0,0xCC,0x16,0xBA}; -static const ff_asf_guid mediasubtype_dtvccdata = - {0xAA,0xDD,0x2A,0xF5,0xF0,0x36,0xF5,0x43,0x95,0xEA,0x6D,0x86,0x64,0x84,0x26,0x2A}; -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 = +const ff_asf_guid ff_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 = +const ff_asf_guid ff_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 = +const ff_asf_guid ff_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[] = { +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} }; - -static const AVCodecGuid audio_guids[] = { - {AV_CODEC_ID_AC3, {0x2C,0x80,0x6D,0xE0,0x46,0xDB,0xCF,0x11,0xB4,0xD1,0x00,0x80,0x5F,0x6C,0xBB,0xEA}}, - {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} -}; - -static int read_probe(AVProbeData *p) -{ - return ff_guidcmp(p->buf, wtv_guid) ? 0 : AVPROBE_SCORE_MAX; -} - -/** - * Convert win32 FILETIME to ISO-8601 string - */ -static void 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'; -} - -/** - * Convert crazy time (100ns since 1 Jan 0001) to ISO-8601 string - */ -static void 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'; -} - -/** - * Convert OLE DATE to ISO-8601 string - */ -static void 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'; -} - -static void get_attachment(AVFormatContext *s, AVIOContext *pb, int length) -{ - char mime[1024]; - char description[1024]; - unsigned int filesize; - AVStream *st; - int64_t pos = avio_tell(pb); - - avio_get_str16le(pb, INT_MAX, mime, sizeof(mime)); - if (strcmp(mime, "image/jpeg")) - goto done; - - avio_r8(pb); - avio_get_str16le(pb, INT_MAX, description, sizeof(description)); - filesize = avio_rl32(pb); - if (!filesize) - goto done; - - st = avformat_new_stream(s, NULL); - if (!st) - goto done; - av_dict_set(&st->metadata, "title", description, 0); - st->codec->codec_id = AV_CODEC_ID_MJPEG; - st->codec->codec_type = AVMEDIA_TYPE_ATTACHMENT; - st->codec->extradata = av_mallocz(filesize); - if (!st->codec->extradata) - goto done; - st->codec->extradata_size = filesize; - avio_read(pb, st->codec->extradata, filesize); -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); - if (!buf) - return; - - if (type == 0 && length == 4) { - snprintf(buf, buf_size, "%"PRIi32, avio_rl32(pb)); - } else if (type == 1) { - avio_get_str16le(pb, length, buf, buf_size); - if (!strlen(buf)) { - av_free(buf); - return; - } - } else if (type == 3 && length == 4) { - strcpy(buf, avio_rl32(pb) ? "true" : "false"); - } 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")) - snprintf(buf, buf_size, "%f", av_int2double(num)); - else - snprintf(buf, buf_size, "%"PRIi64, num); - } else if (type == 5 && length == 2) { - snprintf(buf, buf_size, "%"PRIi16, avio_rl16(pb)); - } else if (type == 6 && length == 16) { - ff_asf_guid guid; - avio_read(pb, guid, 16); - snprintf(buf, buf_size, PRI_PRETTY_GUID, ARG_PRETTY_GUID(guid)); - } else if (type == 2 && !strcmp(key, "WM/Picture")) { - get_attachment(s, pb, length); - av_freep(&buf); - return; - } else { - av_freep(&buf); - av_log(s, AV_LOG_WARNING, "unsupported metadata entry; key:%s, type:%d, length:0x%x\n", key, type, length); - avio_skip(pb, length); - return; - } - - av_dict_set(&s->metadata, key, buf, 0); - av_freep(&buf); -} - -/** - * Parse metadata entries - */ -static void parse_legacy_attrib(AVFormatContext *s, AVIOContext *pb) -{ - ff_asf_guid guid; - int length, type; - while(!pb->eof_reached) { - 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)) { - av_log(s, AV_LOG_WARNING, "unknown guid "PRI_GUID", expected metadata_guid; " - "remaining metadata entries ignored\n", ARG_GUID(guid)); - break; - } - avio_get_str16le(pb, INT_MAX, key, sizeof(key)); - get_tag(s, pb, key, type, length); - } - - ff_metadata_conv(&s->metadata, NULL, ff_asf_metadata_conv); -} - -/** - * parse VIDEOINFOHEADER2 structure - * @return bytes consumed - */ -static int parse_videoinfoheader2(AVFormatContext *s, AVStream *st) -{ - WtvContext *wtv = s->priv_data; - AVIOContext *pb = wtv->pb; - - avio_skip(pb, 72); // picture aspect ratio is unreliable - ff_get_bmp_header(pb, st); - - return 72 + 40; -} - -/** - * Parse MPEG1WAVEFORMATEX extradata structure - */ -static void parse_mpeg1waveformatex(AVStream *st) -{ - /* fwHeadLayer */ - switch (AV_RL16(st->codec->extradata)) { - case 0x0001 : st->codec->codec_id = AV_CODEC_ID_MP1; break; - case 0x0002 : st->codec->codec_id = AV_CODEC_ID_MP2; break; - case 0x0004 : st->codec->codec_id = AV_CODEC_ID_MP3; break; - } - - st->codec->bit_rate = AV_RL32(st->codec->extradata + 2); /* dwHeadBitrate */ - - /* dwHeadMode */ - switch (AV_RL16(st->codec->extradata + 6)) { - case 1 : case 2 : case 4 : st->codec->channels = 2; break; - case 8 : st->codec->channels = 1; break; - } -} - -/** - * Initialise stream - * @param st Stream to initialise, or NULL to create and initialise new stream - * @return NULL on error - */ -static AVStream * new_stream(AVFormatContext *s, AVStream *st, int sid, int codec_type) -{ - if (st) { - if (st->codec->extradata) { - av_freep(&st->codec->extradata); - st->codec->extradata_size = 0; - } - } else { - WtvStream *wst = av_mallocz(sizeof(WtvStream)); - if (!wst) - return NULL; - st = avformat_new_stream(s, NULL); - if (!st) - return NULL; - st->id = sid; - st->priv_data = wst; - } - st->codec->codec_type = codec_type; - st->need_parsing = AVSTREAM_PARSE_FULL; - avpriv_set_pts_info(st, 64, 1, 10000000); - return st; -} - -/** - * parse Media Type structure and populate stream - * @param st Stream, or NULL to create new stream - * @param mediatype Mediatype GUID - * @param subtype Subtype GUID - * @param formattype Format GUID - * @param size Size of format buffer - * @return NULL on error - */ -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) -{ - WtvContext *wtv = s->priv_data; - AVIOContext *pb = wtv->pb; - if (!ff_guidcmp(subtype, mediasubtype_cpfilters_processed) && - !ff_guidcmp(formattype, format_cpfilters_processed)) { - ff_asf_guid actual_subtype; - ff_asf_guid actual_formattype; - - if (size < 32) { - av_log(s, AV_LOG_WARNING, "format buffer size underflow\n"); - avio_skip(pb, size); - return NULL; - } - - avio_skip(pb, size - 32); - ff_get_guid(pb, &actual_subtype); - ff_get_guid(pb, &actual_formattype); - avio_seek(pb, -size, SEEK_CUR); - - 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)) { - st = new_stream(s, st, sid, AVMEDIA_TYPE_AUDIO); - if (!st) - return NULL; - if (!ff_guidcmp(formattype, format_waveformatex)) { - int ret = ff_get_wav_header(pb, st->codec, size); - if (ret < 0) - return NULL; - } else { - if (ff_guidcmp(formattype, format_none)) - av_log(s, AV_LOG_WARNING, "unknown formattype:"PRI_GUID"\n", ARG_GUID(formattype)); - avio_skip(pb, size); - } - - if (!memcmp(subtype + 4, (const uint8_t[]){MEDIASUBTYPE_BASE_GUID}, 12)) { - st->codec->codec_id = ff_wav_codec_get_id(AV_RL32(subtype), st->codec->bits_per_coded_sample); - } else if (!ff_guidcmp(subtype, mediasubtype_mpeg1payload)) { - if (st->codec->extradata && st->codec->extradata_size >= 22) - parse_mpeg1waveformatex(st); - else - av_log(s, AV_LOG_WARNING, "MPEG1WAVEFORMATEX underflow\n"); - } else { - st->codec->codec_id = ff_codec_guid_get_id(audio_guids, subtype); - if (st->codec->codec_id == AV_CODEC_ID_NONE) - av_log(s, AV_LOG_WARNING, "unknown subtype:"PRI_GUID"\n", ARG_GUID(subtype)); - } - return st; - } else if (!ff_guidcmp(mediatype, mediatype_video)) { - st = new_stream(s, st, sid, AVMEDIA_TYPE_VIDEO); - if (!st) - return NULL; - if (!ff_guidcmp(formattype, format_videoinfo2)) { - int consumed = parse_videoinfoheader2(s, st); - avio_skip(pb, FFMAX(size - consumed, 0)); - } else if (!ff_guidcmp(formattype, format_mpeg2_video)) { - int consumed = parse_videoinfoheader2(s, st); - avio_skip(pb, FFMAX(size - consumed, 0)); - } else { - if (ff_guidcmp(formattype, format_none)) - av_log(s, AV_LOG_WARNING, "unknown formattype:"PRI_GUID"\n", ARG_GUID(formattype)); - avio_skip(pb, size); - } - - if (!memcmp(subtype + 4, (const uint8_t[]){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); - } - if (st->codec->codec_id == AV_CODEC_ID_NONE) - av_log(s, AV_LOG_WARNING, "unknown subtype:"PRI_GUID"\n", ARG_GUID(subtype)); - return st; - } else if (!ff_guidcmp(mediatype, mediatype_mpeg2_pes) && - !ff_guidcmp(subtype, mediasubtype_dvb_subtitle)) { - st = new_stream(s, st, sid, AVMEDIA_TYPE_SUBTITLE); - if (!st) - return NULL; - if (ff_guidcmp(formattype, format_none)) - av_log(s, AV_LOG_WARNING, "unknown formattype:"PRI_GUID"\n", ARG_GUID(formattype)); - avio_skip(pb, size); - st->codec->codec_id = AV_CODEC_ID_DVB_SUBTITLE; - return st; - } else if (!ff_guidcmp(mediatype, mediatype_mstvcaption) && - (!ff_guidcmp(subtype, mediasubtype_teletext) || !ff_guidcmp(subtype, mediasubtype_dtvccdata))) { - st = new_stream(s, st, sid, AVMEDIA_TYPE_SUBTITLE); - if (!st) - return NULL; - if (ff_guidcmp(formattype, format_none)) - av_log(s, AV_LOG_WARNING, "unknown formattype:"PRI_GUID"\n", ARG_GUID(formattype)); - avio_skip(pb, size); - st->codec->codec_id = AV_CODEC_ID_DVB_TELETEXT; - return st; - } else if (!ff_guidcmp(mediatype, mediatype_mpeg2_sections) && - !ff_guidcmp(subtype, mediasubtype_mpeg2_sections)) { - if (ff_guidcmp(formattype, format_none)) - av_log(s, AV_LOG_WARNING, "unknown formattype:"PRI_GUID"\n", ARG_GUID(formattype)); - avio_skip(pb, size); - return NULL; - } - - av_log(s, AV_LOG_WARNING, "unknown media type, mediatype:"PRI_GUID - ", subtype:"PRI_GUID", formattype:"PRI_GUID"\n", - ARG_GUID(mediatype), ARG_GUID(subtype), ARG_GUID(formattype)); - avio_skip(pb, size); - return NULL; -} - -enum { - SEEK_TO_DATA = 0, - SEEK_TO_PTS, -}; - -/** - * Parse WTV chunks - * @param mode SEEK_TO_DATA or SEEK_TO_PTS - * @param seekts timestamp - * @param[out] len_ptr Length of data chunk - * @return stream index of data chunk, or <0 on error - */ -static int parse_chunks(AVFormatContext *s, int mode, int64_t seekts, int *len_ptr) -{ - WtvContext *wtv = s->priv_data; - AVIOContext *pb = wtv->pb; - while (!pb->eof_reached) { - ff_asf_guid g; - int len, sid, consumed; - - ff_get_guid(pb, &g); - len = avio_rl32(pb); - if (len < 32) - break; - sid = avio_rl32(pb) & 0x7FFF; - avio_skip(pb, 8); - consumed = 32; - - if (!ff_guidcmp(g, stream_guid)) { - if (ff_find_stream_index(s, sid) < 0) { - ff_asf_guid mediatype, subtype, formattype; - int size; - avio_skip(pb, 28); - ff_get_guid(pb, &mediatype); - ff_get_guid(pb, &subtype); - avio_skip(pb, 12); - ff_get_guid(pb, &formattype); - size = avio_rl32(pb); - parse_media_type(s, 0, sid, mediatype, subtype, formattype, size); - consumed += 92 + size; - } - } else if (!ff_guidcmp(g, stream2_guid)) { - int stream_index = ff_find_stream_index(s, sid); - if (stream_index >= 0 && !((WtvStream*)s->streams[stream_index]->priv_data)->seen_data) { - ff_asf_guid mediatype, subtype, formattype; - int size; - avio_skip(pb, 12); - ff_get_guid(pb, &mediatype); - ff_get_guid(pb, &subtype); - avio_skip(pb, 12); - ff_get_guid(pb, &formattype); - size = avio_rl32(pb); - parse_media_type(s, s->streams[stream_index], sid, mediatype, subtype, formattype, size); - consumed += 76 + size; - } - } else if (!ff_guidcmp(g, EVENTID_AudioDescriptorSpanningEvent) || - !ff_guidcmp(g, EVENTID_CtxADescriptorSpanningEvent) || - !ff_guidcmp(g, EVENTID_CSDescriptorSpanningEvent) || - !ff_guidcmp(g, EVENTID_StreamIDSpanningEvent) || - !ff_guidcmp(g, EVENTID_SubtitleSpanningEvent) || - !ff_guidcmp(g, EVENTID_TeletextSpanningEvent)) { - int stream_index = ff_find_stream_index(s, sid); - if (stream_index >= 0) { - AVStream *st = s->streams[stream_index]; - uint8_t buf[258]; - const uint8_t *pbuf = buf; - int buf_size; - - avio_skip(pb, 8); - consumed += 8; - if (!ff_guidcmp(g, EVENTID_CtxADescriptorSpanningEvent) || - !ff_guidcmp(g, EVENTID_CSDescriptorSpanningEvent)) { - avio_skip(pb, 6); - consumed += 6; - } - - buf_size = FFMIN(len - consumed, sizeof(buf)); - avio_read(pb, buf, buf_size); - consumed += buf_size; - ff_parse_mpeg2_descriptor(s, st, 0, &pbuf, buf + buf_size, NULL, 0, 0, NULL); - } - } else if (!ff_guidcmp(g, EVENTID_AudioTypeSpanningEvent)) { - int stream_index = ff_find_stream_index(s, sid); - if (stream_index >= 0) { - AVStream *st = s->streams[stream_index]; - int audio_type; - avio_skip(pb, 8); - audio_type = avio_r8(pb); - if (audio_type == 2) - st->disposition |= AV_DISPOSITION_HEARING_IMPAIRED; - else if (audio_type == 3) - st->disposition |= AV_DISPOSITION_VISUAL_IMPAIRED; - consumed += 9; - } - } else if (!ff_guidcmp(g, EVENTID_DVBScramblingControlSpanningEvent)) { - int stream_index = ff_find_stream_index(s, sid); - if (stream_index >= 0) { - avio_skip(pb, 12); - if (avio_rl32(pb)) - av_log(s, AV_LOG_WARNING, "DVB scrambled stream detected (st:%d), decoding will likely fail\n", stream_index); - consumed += 16; - } - } else if (!ff_guidcmp(g, EVENTID_LanguageSpanningEvent)) { - int stream_index = ff_find_stream_index(s, sid); - if (stream_index >= 0) { - AVStream *st = s->streams[stream_index]; - uint8_t language[4]; - avio_skip(pb, 12); - avio_read(pb, language, 3); - if (language[0]) { - language[3] = 0; - av_dict_set(&st->metadata, "language", language, 0); - if (!strcmp(language, "nar") || !strcmp(language, "NAR")) - st->disposition |= AV_DISPOSITION_VISUAL_IMPAIRED; - } - consumed += 15; - } - } else if (!ff_guidcmp(g, timestamp_guid)) { - int stream_index = ff_find_stream_index(s, sid); - if (stream_index >= 0) { - avio_skip(pb, 8); - wtv->pts = avio_rl64(pb); - consumed += 16; - if (wtv->pts == -1) - wtv->pts = AV_NOPTS_VALUE; - else { - wtv->last_valid_pts = wtv->pts; - 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)) { - int stream_index = ff_find_stream_index(s, sid); - if (mode == SEEK_TO_DATA && stream_index >= 0 && len > 32) { - WtvStream *wst = s->streams[stream_index]->priv_data; - wst->seen_data = 1; - if (len_ptr) { - *len_ptr = len; - } - return 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_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, /* 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}) || - !ff_guidcmp(g, /* EVENTID_ChannelTypeSpanningEvent */ (const ff_asf_guid){0x51,0x1D,0xAB,0x72,0xD2,0x87,0x9B,0x48,0xBA,0x11,0x0E,0x08,0xDC,0x21,0x02,0x43}) || - !ff_guidcmp(g, /* EVENTID_PIDListSpanningEvent */ (const ff_asf_guid){0x65,0x8F,0xFC,0x47,0xBB,0xE2,0x34,0x46,0x9C,0xEF,0xFD,0xBF,0xE6,0x26,0x1D,0x5C}) || - !ff_guidcmp(g, /* EVENTID_SignalAndServiceStatusSpanningEvent */ (const ff_asf_guid){0xCB,0xC5,0x68,0x80,0x04,0x3C,0x2B,0x49,0xB4,0x7D,0x03,0x08,0x82,0x0D,0xCE,0x51}) || - !ff_guidcmp(g, /* EVENTID_StreamTypeSpanningEvent */ (const ff_asf_guid){0xBC,0x2E,0xAF,0x82,0xA6,0x30,0x64,0x42,0xA8,0x0B,0xAD,0x2E,0x13,0x72,0xAC,0x60}) || - !ff_guidcmp(g, (const ff_asf_guid){0x1E,0xBE,0xC3,0xC5,0x43,0x92,0xDC,0x11,0x85,0xE5,0x00,0x12,0x3F,0x6F,0x73,0xB9}) || - !ff_guidcmp(g, (const ff_asf_guid){0x3B,0x86,0xA2,0xB1,0xEB,0x1E,0xC3,0x44,0x8C,0x88,0x1C,0xA3,0xFF,0xE3,0xE7,0x6A}) || - !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})) { - //ignore known guids - } else - av_log(s, AV_LOG_WARNING, "unsupported chunk:"PRI_GUID"\n", ARG_GUID(g)); - - avio_skip(pb, WTV_PAD8(len) - consumed); - } - 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; - int root_sector, root_size; - uint8_t root[WTV_SECTOR_SIZE]; - AVIOContext *pb; - int64_t timeline_pos; - int ret; - - wtv->epoch = - wtv->pts = - wtv->last_valid_pts = AV_NOPTS_VALUE; - - /* read root directory sector */ - avio_skip(s->pb, 0x30); - root_size = avio_rl32(s->pb); - if (root_size > sizeof(root)) { - av_log(s, AV_LOG_ERROR, "root directory size exceeds sector size\n"); - return AVERROR_INVALIDDATA; - } - avio_skip(s->pb, 4); - root_sector = avio_rl32(s->pb); - - avio_seek(s->pb, root_sector << WTV_SECTOR_BITS, SEEK_SET); - root_size = avio_read(s->pb, root, root_size); - if (root_size < 0) - return AVERROR_INVALIDDATA; - - /* parse chunks up until first data chunk */ - wtv->pb = wtvfile_open(s, root, root_size, timeline_le16); - if (!wtv->pb) { - av_log(s, AV_LOG_ERROR, "timeline data missing\n"); - return AVERROR_INVALIDDATA; - } - - ret = parse_chunks(s, SEEK_TO_DATA, 0, 0); - if (ret < 0) - return ret; - avio_seek(wtv->pb, -32, SEEK_CUR); - - 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); - if (pb) { - parse_legacy_attrib(s, pb); - wtvfile_close(pb); - } - - /* read seek index */ - if (s->nb_streams) { - AVStream *st = s->streams[0]; - pb = wtvfile_open(s, root, root_size, 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) - break; - ff_add_index_entry(&wtv->index_entries, &wtv->nb_index_entries, &wtv->index_entries_allocated_size, - 0, timestamp, frame_nb, 0, AVINDEX_KEYFRAME); - } - wtvfile_close(pb); - - if (wtv->nb_index_entries) { - pb = wtvfile_open(s, root, root_size, 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) - break; - for (i = wtv->nb_index_entries - 1; i >= 0; i--) { - AVIndexEntry *e = wtv->index_entries + i; - if (frame_nb > e->size) - break; - if (position > e->pos) - e->pos = position; - } - } - wtvfile_close(pb); - st->duration = wtv->index_entries[wtv->nb_index_entries - 1].timestamp; - } - } - } - } - - avio_seek(s->pb, timeline_pos, SEEK_SET); - return 0; -} - -static int read_packet(AVFormatContext *s, AVPacket *pkt) -{ - WtvContext *wtv = s->priv_data; - AVIOContext *pb = wtv->pb; - int stream_index, len, ret; - - stream_index = parse_chunks(s, SEEK_TO_DATA, 0, &len); - if (stream_index < 0) - return stream_index; - - ret = av_get_packet(pb, pkt, len - 32); - if (ret < 0) - return ret; - pkt->stream_index = stream_index; - pkt->pts = wtv->pts; - avio_skip(pb, WTV_PAD8(len) - len); - return 0; -} - -static int read_seek(AVFormatContext *s, int stream_index, - int64_t ts, int flags) -{ - WtvContext *wtv = s->priv_data; - AVIOContext *pb = wtv->pb; - AVStream *st = s->streams[0]; - int64_t ts_relative; - int i; - - if ((flags & AVSEEK_FLAG_FRAME) || (flags & AVSEEK_FLAG_BYTE)) - return AVERROR(ENOSYS); - - /* timestamp adjustment is required because wtv->pts values are absolute, - * whereas AVIndexEntry->timestamp values are relative to epoch. */ - ts_relative = ts; - if (wtv->epoch != AV_NOPTS_VALUE) - ts_relative -= wtv->epoch; - - 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 (parse_chunks(s, SEEK_TO_PTS, ts, 0) < 0) - return AVERROR(ERANGE); - return 0; - } - 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); - wtvfile_close(wtv->pb); - return 0; -} - -AVInputFormat ff_wtv_demuxer = { - .name = "wtv", - .long_name = NULL_IF_CONFIG_SMALL("Windows Television (WTV)"), - .priv_data_size = sizeof(WtvContext), - .read_probe = read_probe, - .read_header = read_header, - .read_packet = read_packet, - .read_seek = read_seek, - .read_close = read_close, - .flags = AVFMT_SHOW_IDS, -}; diff --git a/libavformat/wtv.h b/libavformat/wtv.h new file mode 100644 index 0000000000..7e4f243551 --- /dev/null +++ b/libavformat/wtv.h @@ -0,0 +1,55 @@ +/* + * 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_stream_guid; +extern const ff_asf_guid ff_mediatype_audio; +extern const ff_asf_guid ff_mediatype_video; +extern const ff_asf_guid ff_format_none; +extern const AVCodecGuid ff_video_guids[]; + +extern const ff_asf_guid ff_DSATTRIB_TRANSPORT_PROPERTIES; +extern const ff_asf_guid ff_metadata_guid; +extern const ff_asf_guid ff_stream2_guid; +extern const ff_asf_guid ff_mediasubtype_cpfilters_processed; +extern const ff_asf_guid ff_format_cpfilters_processed; +extern const ff_asf_guid ff_format_waveformatex; +extern const ff_asf_guid ff_format_mpeg2_video; +#endif /* AVFORMAT_WTV_H */ diff --git a/libavformat/wtvdec.c b/libavformat/wtvdec.c new file mode 100644 index 0000000000..5d82db68b0 --- /dev/null +++ b/libavformat/wtvdec.c @@ -0,0 +1,1064 @@ +/* + * Windows Television (WTV) 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 + * Windows Television (WTV) demuxer + * @author Peter Ross <pross@xvid.org> + */ + +#include "libavutil/intreadwrite.h" +#include "libavutil/intfloat.h" +#include "avformat.h" +#include "internal.h" +#include "wtv.h" +#include "mpegts.h" + +/* Macros for formating GUIDs */ +#define PRI_PRETTY_GUID \ + "%08x-%04x-%04x-%02x%02x%02x%02x%02x%02x%02x%02x" +#define ARG_PRETTY_GUID(g) \ + AV_RL32(g),AV_RL16(g+4),AV_RL16(g+6),g[8],g[9],g[10],g[11],g[12],g[13],g[14],g[15] +#define LEN_PRETTY_GUID 34 + +/* + * + * File system routines + * + */ + +typedef struct { + 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 error; + int64_t position; + int64_t length; +} WtvFile; + +/** + * @return bytes read, 0 on end of file, or <0 on error + */ +static int wtvfile_read_packet(void *opaque, uint8_t *buf, int buf_size) +{ + WtvFile *wf = opaque; + AVIOContext *pb = wf->pb_filesystem; + int nread = 0; + + if (wf->error || pb->error) + return -1; + if (wf->position >= wf->length || url_feof(pb)) + return 0; + + buf_size = FFMIN(buf_size, wf->length - wf->position); + while(nread < buf_size) { + int n; + int remaining_in_sector = (1 << wf->sector_bits) - (wf->position & ((1 << wf->sector_bits) - 1)); + int read_request = FFMIN(buf_size - nread, remaining_in_sector); + + n = avio_read(pb, buf, read_request); + if (n <= 0) + break; + nread += n; + buf += n; + wf->position += n; + if (n == remaining_in_sector) { + int i = wf->position >> wf->sector_bits; + if (i >= wf->nb_sectors || + (wf->sectors[i] != wf->sectors[i - 1] + (1 << (wf->sector_bits - WTV_SECTOR_BITS)) && + avio_seek(pb, (int64_t)wf->sectors[i] << WTV_SECTOR_BITS, SEEK_SET) < 0)) { + wf->error = 1; + break; + } + } + } + return nread; +} + +/** + * @return position (or file length) + */ +static int64_t wtvfile_seek(void *opaque, int64_t offset, int whence) +{ + WtvFile *wf = opaque; + AVIOContext *pb = wf->pb_filesystem; + + if (whence == AVSEEK_SIZE) + return wf->length; + else if (whence == SEEK_CUR) + offset = wf->position + offset; + else if (whence == SEEK_END) + offset = wf->length; + + wf->error = offset < 0 || offset >= wf->length || + avio_seek(pb, ((int64_t)wf->sectors[offset >> wf->sector_bits] << WTV_SECTOR_BITS) + + (offset & ((1 << wf->sector_bits) - 1)), SEEK_SET) < 0; + wf->position = offset; + return offset; +} + +/** + * read non-zero integers (le32) from input stream + * @param pb + * @param[out] data destination + * @param count maximum number of integers to read + * @return total number of integers read + */ +static int read_ints(AVIOContext *pb, uint32_t *data, int count) +{ + int i, total = 0; + for (i = 0; i < count; i++) { + if ((data[total] = avio_rl32(pb))) + total++; + } + return total; +} + +/** + * Open file + * @param first_sector First sector + * @param length Length of file (bytes) + * @param depth File allocation table depth + * @return NULL on error + */ +static AVIOContext * wtvfile_open_sector(int first_sector, uint64_t length, int depth, AVFormatContext *s) +{ + AVIOContext *pb; + WtvFile *wf; + uint8_t *buffer; + + if (avio_seek(s->pb, first_sector << WTV_SECTOR_BITS, SEEK_SET) < 0) + return NULL; + + wf = av_mallocz(sizeof(WtvFile)); + if (!wf) + return NULL; + + if (depth == 0) { + wf->sectors = av_malloc(sizeof(uint32_t)); + if (!wf->sectors) { + av_free(wf); + return NULL; + } + wf->sectors[0] = first_sector; + wf->nb_sectors = 1; + } else if (depth == 1) { + wf->sectors = av_malloc(WTV_SECTOR_SIZE); + if (!wf->sectors) { + av_free(wf); + return NULL; + } + wf->nb_sectors = read_ints(s->pb, wf->sectors, WTV_SECTOR_SIZE / 4); + } 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(nb_sectors1 << WTV_SECTOR_BITS); + if (!wf->sectors) { + av_free(wf); + return NULL; + } + wf->nb_sectors = 0; + for (i = 0; i < nb_sectors1; i++) { + if (avio_seek(s->pb, (int64_t)sectors1[i] << WTV_SECTOR_BITS, SEEK_SET) < 0) + break; + wf->nb_sectors += read_ints(s->pb, wf->sectors + i * WTV_SECTOR_SIZE / 4, WTV_SECTOR_SIZE / 4); + } + } 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); + av_free(wf); + return NULL; + } + + /* check length */ + length &= 0xFFFFFFFFFFFF; + if (length > ((int64_t)wf->nb_sectors << wf->sector_bits)) { + av_log(s, AV_LOG_WARNING, "reported file length (0x%"PRIx64") exceeds number of available sectors (0x%"PRIx64")\n", length, (int64_t)wf->nb_sectors << wf->sector_bits); + length = (int64_t)wf->nb_sectors << wf->sector_bits; + } + wf->length = length; + + /* seek to initial sector */ + wf->position = 0; + if (avio_seek(s->pb, (int64_t)wf->sectors[0] << WTV_SECTOR_BITS, SEEK_SET) < 0) { + av_free(wf->sectors); + av_free(wf); + return NULL; + } + + wf->pb_filesystem = s->pb; + buffer = av_malloc(1 << wf->sector_bits); + if (!buffer) { + av_free(wf->sectors); + av_free(wf); + return NULL; + } + + pb = avio_alloc_context(buffer, 1 << wf->sector_bits, 0, wf, + wtvfile_read_packet, NULL, wtvfile_seek); + if (!pb) { + av_free(buffer); + av_free(wf->sectors); + av_free(wf); + } + return pb; +} + +/** + * Open file using filename + * @param[in] buf directory buffer + * @param buf_size directory buffer size + * @param[in] filename + * @param filename_size size of filename + * @return NULL on error + */ +static AVIOContext * wtvfile_open2(AVFormatContext *s, const uint8_t *buf, int buf_size, const uint8_t *filename, int filename_size) +{ + const uint8_t *buf_end = buf + buf_size; + + while(buf + 48 <= buf_end) { + int dir_length, name_size, first_sector, depth; + uint64_t file_length; + const uint8_t *name; + 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; + } + dir_length = AV_RL16(buf + 16); + file_length = AV_RL64(buf + 24); + name_size = 2 * AV_RL32(buf + 32); + if (buf + 48 + (int64_t)name_size > buf_end || name_size<0) { + av_log(s, AV_LOG_ERROR, "filename exceeds buffer size; remaining directory entries ignored\n"); + break; + } + first_sector = AV_RL32(buf + 40 + name_size); + depth = AV_RL32(buf + 44 + name_size); + + /* compare file name; test optional null terminator */ + name = buf + 40; + if (name_size >= filename_size && + !memcmp(name, filename, filename_size) && + (name_size < filename_size + 2 || !AV_RN16(name + filename_size))) + return wtvfile_open_sector(first_sector, file_length, depth, s); + + buf += dir_length; + } + return 0; +} + +#define wtvfile_open(s, buf, buf_size, filename) \ + wtvfile_open2(s, buf, buf_size, filename, sizeof(filename)) + +/** + * Close file opened with wtvfile_open_sector(), or wtv_open() + */ +static void wtvfile_close(AVIOContext *pb) +{ + WtvFile *wf = pb->opaque; + av_free(wf->sectors); + av_freep(&pb->opaque); + av_freep(&pb->buffer); + av_free(pb); +} + +/* + * + * Main demuxer + * + */ + +typedef struct { + int seen_data; +} WtvStream; + +typedef struct { + 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 */ + + /* maintain private seek index, as the AVIndexEntry->pos is relative to the + start of the 'timeline' file, not the file system (AVFormatContext->pb) */ + AVIndexEntry *index_entries; + int nb_index_entries; + unsigned int index_entries_allocated_size; +} WtvContext; + +/* WTV GUIDs */ +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 = + {0x6D,0x66,0x92,0xE2,0x02,0x9C,0x8D,0x44,0xAA,0x8D,0x78,0x1A,0x93,0xFD,0xC3,0x95}; +static const ff_asf_guid EVENTID_AudioDescriptorSpanningEvent = + {0x1C,0xD4,0x7B,0x10,0xDA,0xA6,0x91,0x46,0x83,0x69,0x11,0xB2,0xCD,0xAA,0x28,0x8E}; +static const ff_asf_guid EVENTID_CtxADescriptorSpanningEvent = + {0xE6,0xA2,0xB4,0x3A,0x47,0x42,0x34,0x4B,0x89,0x6C,0x30,0xAF,0xA5,0xD2,0x1C,0x24}; +static const ff_asf_guid EVENTID_CSDescriptorSpanningEvent = + {0xD9,0x79,0xE7,0xEf,0xF0,0x97,0x86,0x47,0x80,0x0D,0x95,0xCF,0x50,0x5D,0xDC,0x66}; +static const ff_asf_guid EVENTID_DVBScramblingControlSpanningEvent = + {0xC4,0xE1,0xD4,0x4B,0xA1,0x90,0x09,0x41,0x82,0x36,0x27,0xF0,0x0E,0x7D,0xCC,0x5B}; +static const ff_asf_guid EVENTID_StreamIDSpanningEvent = + {0x68,0xAB,0xF1,0xCA,0x53,0xE1,0x41,0x4D,0xA6,0xB3,0xA7,0xC9,0x98,0xDB,0x75,0xEE}; +static const ff_asf_guid EVENTID_TeletextSpanningEvent = + {0x50,0xD9,0x99,0x95,0x33,0x5F,0x17,0x46,0xAF,0x7C,0x1E,0x54,0xB5,0x10,0xDA,0xA3}; +static const ff_asf_guid EVENTID_AudioTypeSpanningEvent = + {0xBE,0xBF,0x1C,0x50,0x49,0xB8,0xCE,0x42,0x9B,0xE9,0x3D,0xB8,0x69,0xFB,0x82,0xB3}; + +/* Windows media GUIDs */ + +/* Media types */ +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 = + {0x6C,0x17,0x5F,0x45,0x06,0x4B,0xCE,0x47,0x9A,0xEF,0x8C,0xAE,0xF7,0x3D,0xF7,0xB5}; +static const ff_asf_guid mediatype_mpeg2_pes = + {0x20,0x80,0x6D,0xE0,0x46,0xDB,0xCF,0x11,0xB4,0xD1,0x00,0x80,0x5F,0x6C,0xBB,0xEA}; +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_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 = + {0xE3,0x76,0x2A,0xF7,0x0A,0xEB,0xD0,0x11,0xAC,0xE4,0x00,0x00,0xC0,0xCC,0x16,0xBA}; +static const ff_asf_guid mediasubtype_dtvccdata = + {0xAA,0xDD,0x2A,0xF5,0xF0,0x36,0xF5,0x43,0x95,0xEA,0x6D,0x86,0x64,0x84,0x26,0x2A}; +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_videoinfo2 = + {0xA0,0x76,0x2A,0xF7,0x0A,0xEB,0xD0,0x11,0xAC,0xE4,0x00,0x00,0xC0,0xCC,0x16,0xBA}; + +static int read_probe(AVProbeData *p) +{ + return ff_guidcmp(p->buf, ff_wtv_guid) ? 0 : AVPROBE_SCORE_MAX; +} + +/** + * Convert win32 FILETIME to ISO-8601 string + * @return <0 on error + */ +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) + 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 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) + 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 int oledate_to_iso8601(char *buf, int buf_size, int64_t value) +{ + 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) +{ + char mime[1024]; + char description[1024]; + unsigned int filesize; + AVStream *st; + int64_t pos = avio_tell(pb); + + avio_get_str16le(pb, INT_MAX, mime, sizeof(mime)); + if (strcmp(mime, "image/jpeg")) + goto done; + + avio_r8(pb); + avio_get_str16le(pb, INT_MAX, description, sizeof(description)); + filesize = avio_rl32(pb); + if (!filesize) + goto done; + + st = avformat_new_stream(s, NULL); + if (!st) + goto done; + av_dict_set(&st->metadata, "title", description, 0); + st->codec->codec_id = AV_CODEC_ID_MJPEG; + st->codec->codec_type = AVMEDIA_TYPE_ATTACHMENT; + st->codec->extradata = av_mallocz(filesize); + if (!st->codec->extradata) + goto done; + st->codec->extradata_size = filesize; + avio_read(pb, st->codec->extradata, filesize); +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); + if (!buf) + return; + + if (type == 0 && length == 4) { + snprintf(buf, buf_size, "%"PRIi32, avio_rl32(pb)); + } else if (type == 1) { + avio_get_str16le(pb, length, buf, buf_size); + if (!strlen(buf)) { + av_free(buf); + return; + } + } else if (type == 3 && length == 4) { + strcpy(buf, avio_rl32(pb) ? "true" : "false"); + } else if (type == 4 && length == 8) { + int64_t num = avio_rl64(pb); + if (!strcmp(key, "WM/EncodingTime") || + !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); + } else if (type == 5 && length == 2) { + snprintf(buf, buf_size, "%"PRIi16, avio_rl16(pb)); + } else if (type == 6 && length == 16) { + ff_asf_guid guid; + avio_read(pb, guid, 16); + snprintf(buf, buf_size, PRI_PRETTY_GUID, ARG_PRETTY_GUID(guid)); + } else if (type == 2 && !strcmp(key, "WM/Picture")) { + get_attachment(s, pb, length); + av_freep(&buf); + return; + } else { + av_freep(&buf); + av_log(s, AV_LOG_WARNING, "unsupported metadata entry; key:%s, type:%d, length:0x%x\n", key, type, length); + avio_skip(pb, length); + return; + } + + av_dict_set(&s->metadata, key, buf, 0); + av_freep(&buf); +} + +/** + * Parse metadata entries + */ +static void parse_legacy_attrib(AVFormatContext *s, AVIOContext *pb) +{ + ff_asf_guid guid; + int length, type; + while(!url_feof(pb)) { + char key[1024]; + ff_get_guid(pb, &guid); + type = avio_rl32(pb); + length = avio_rl32(pb); + if (!length) + break; + if (ff_guidcmp(&guid, 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; + } + avio_get_str16le(pb, INT_MAX, key, sizeof(key)); + get_tag(s, pb, key, type, length); + } + + ff_metadata_conv(&s->metadata, NULL, ff_asf_metadata_conv); +} + +/** + * parse VIDEOINFOHEADER2 structure + * @return bytes consumed + */ +static int parse_videoinfoheader2(AVFormatContext *s, AVStream *st) +{ + WtvContext *wtv = s->priv_data; + AVIOContext *pb = wtv->pb; + + avio_skip(pb, 72); // picture aspect ratio is unreliable + ff_get_bmp_header(pb, st, NULL); + + return 72 + 40; +} + +/** + * Parse MPEG1WAVEFORMATEX extradata structure + */ +static void parse_mpeg1waveformatex(AVStream *st) +{ + /* fwHeadLayer */ + switch (AV_RL16(st->codec->extradata)) { + case 0x0001 : st->codec->codec_id = AV_CODEC_ID_MP1; break; + case 0x0002 : st->codec->codec_id = AV_CODEC_ID_MP2; break; + case 0x0004 : st->codec->codec_id = AV_CODEC_ID_MP3; break; + } + + st->codec->bit_rate = AV_RL32(st->codec->extradata + 2); /* dwHeadBitrate */ + + /* dwHeadMode */ + switch (AV_RL16(st->codec->extradata + 6)) { + case 1 : case 2 : case 4 : st->codec->channels = 2; break; + case 8 : st->codec->channels = 1; break; + } +} + +/** + * Initialise stream + * @param st Stream to initialise, or NULL to create and initialise new stream + * @return NULL on error + */ +static AVStream * new_stream(AVFormatContext *s, AVStream *st, int sid, int codec_type) +{ + if (st) { + if (st->codec->extradata) { + av_freep(&st->codec->extradata); + st->codec->extradata_size = 0; + } + } else { + WtvStream *wst = av_mallocz(sizeof(WtvStream)); + if (!wst) + return NULL; + st = avformat_new_stream(s, NULL); + if (!st) { + av_free(wst); + return NULL; + } + st->id = sid; + st->priv_data = wst; + } + st->codec->codec_type = codec_type; + st->need_parsing = AVSTREAM_PARSE_FULL; + avpriv_set_pts_info(st, 64, 1, 10000000); + return st; +} + +/** + * parse Media Type structure and populate stream + * @param st Stream, or NULL to create new stream + * @param mediatype Mediatype GUID + * @param subtype Subtype GUID + * @param formattype Format GUID + * @param size Size of format buffer + * @return NULL on error + */ +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) +{ + WtvContext *wtv = s->priv_data; + AVIOContext *pb = wtv->pb; + 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; + + if (size < 32) { + av_log(s, AV_LOG_WARNING, "format buffer size underflow\n"); + avio_skip(pb, size); + return NULL; + } + + avio_skip(pb, size - 32); + ff_get_guid(pb, &actual_subtype); + ff_get_guid(pb, &actual_formattype); + avio_seek(pb, -size, SEEK_CUR); + + 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, ff_mediatype_audio)) { + st = new_stream(s, st, sid, AVMEDIA_TYPE_AUDIO); + if (!st) + return NULL; + 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, ff_format_none)) + av_log(s, AV_LOG_WARNING, "unknown formattype:"FF_PRI_GUID"\n", FF_ARG_GUID(formattype)); + avio_skip(pb, size); + } + + if (!memcmp(subtype + 4, (const uint8_t[]){FF_MEDIASUBTYPE_BASE_GUID}, 12)) { + st->codec->codec_id = ff_wav_codec_get_id(AV_RL32(subtype), st->codec->bits_per_coded_sample); + } else if (!ff_guidcmp(subtype, mediasubtype_mpeg1payload)) { + if (st->codec->extradata && st->codec->extradata_size >= 22) + parse_mpeg1waveformatex(st); + else + av_log(s, AV_LOG_WARNING, "MPEG1WAVEFORMATEX underflow\n"); + } else { + st->codec->codec_id = ff_codec_guid_get_id(ff_codec_wav_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)); + } + return st; + } else if (!ff_guidcmp(mediatype, ff_mediatype_video)) { + st = new_stream(s, st, sid, AVMEDIA_TYPE_VIDEO); + if (!st) + return NULL; + if (!ff_guidcmp(formattype, format_videoinfo2)) { + int consumed = parse_videoinfoheader2(s, st); + avio_skip(pb, FFMAX(size - consumed, 0)); + } else if (!ff_guidcmp(formattype, ff_format_mpeg2_video)) { + int consumed = parse_videoinfoheader2(s, st); + avio_skip(pb, FFMAX(size - consumed, 0)); + } else { + 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); + } + + 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(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)); + return st; + } else if (!ff_guidcmp(mediatype, mediatype_mpeg2_pes) && + !ff_guidcmp(subtype, mediasubtype_dvb_subtitle)) { + st = new_stream(s, st, sid, AVMEDIA_TYPE_SUBTITLE); + if (!st) + return NULL; + 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; + return st; + } else if (!ff_guidcmp(mediatype, mediatype_mstvcaption) && + (!ff_guidcmp(subtype, mediasubtype_teletext) || !ff_guidcmp(subtype, mediasubtype_dtvccdata))) { + st = new_stream(s, st, sid, AVMEDIA_TYPE_SUBTITLE); + if (!st) + return NULL; + 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 = !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, 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; + } + + av_log(s, AV_LOG_WARNING, "unknown media type, mediatype:"FF_PRI_GUID + ", subtype:"FF_PRI_GUID", formattype:"FF_PRI_GUID"\n", + FF_ARG_GUID(mediatype), FF_ARG_GUID(subtype), FF_ARG_GUID(formattype)); + avio_skip(pb, size); + return NULL; +} + +enum { + SEEK_TO_DATA = 0, + SEEK_TO_PTS, +}; + +/** + * Parse WTV chunks + * @param mode SEEK_TO_DATA or SEEK_TO_PTS + * @param seekts timestamp + * @param[out] len_ptr Length of data chunk + * @return stream index of data chunk, or <0 on error + */ +static int parse_chunks(AVFormatContext *s, int mode, int64_t seekts, int *len_ptr) +{ + WtvContext *wtv = s->priv_data; + AVIOContext *pb = wtv->pb; + while (!url_feof(pb)) { + ff_asf_guid g; + int len, sid, consumed; + + ff_get_guid(pb, &g); + len = avio_rl32(pb); + if (len < 32) + break; + sid = avio_rl32(pb) & 0x7FFF; + avio_skip(pb, 8); + consumed = 32; + + if (!ff_guidcmp(g, ff_stream_guid)) { + if (ff_find_stream_index(s, sid) < 0) { + ff_asf_guid mediatype, subtype, formattype; + int size; + avio_skip(pb, 28); + ff_get_guid(pb, &mediatype); + ff_get_guid(pb, &subtype); + avio_skip(pb, 12); + ff_get_guid(pb, &formattype); + size = avio_rl32(pb); + parse_media_type(s, 0, sid, mediatype, subtype, formattype, size); + consumed += 92 + size; + } + } 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) { + ff_asf_guid mediatype, subtype, formattype; + int size; + avio_skip(pb, 12); + ff_get_guid(pb, &mediatype); + ff_get_guid(pb, &subtype); + avio_skip(pb, 12); + ff_get_guid(pb, &formattype); + size = avio_rl32(pb); + parse_media_type(s, s->streams[stream_index], sid, mediatype, subtype, formattype, size); + consumed += 76 + size; + } + } else if (!ff_guidcmp(g, EVENTID_AudioDescriptorSpanningEvent) || + !ff_guidcmp(g, EVENTID_CtxADescriptorSpanningEvent) || + !ff_guidcmp(g, EVENTID_CSDescriptorSpanningEvent) || + !ff_guidcmp(g, EVENTID_StreamIDSpanningEvent) || + !ff_guidcmp(g, EVENTID_SubtitleSpanningEvent) || + !ff_guidcmp(g, EVENTID_TeletextSpanningEvent)) { + int stream_index = ff_find_stream_index(s, sid); + if (stream_index >= 0) { + AVStream *st = s->streams[stream_index]; + uint8_t buf[258]; + const uint8_t *pbuf = buf; + int buf_size; + + avio_skip(pb, 8); + consumed += 8; + if (!ff_guidcmp(g, EVENTID_CtxADescriptorSpanningEvent) || + !ff_guidcmp(g, EVENTID_CSDescriptorSpanningEvent)) { + avio_skip(pb, 6); + consumed += 6; + } + + buf_size = FFMIN(len - consumed, sizeof(buf)); + avio_read(pb, buf, buf_size); + consumed += buf_size; + ff_parse_mpeg2_descriptor(s, st, 0, &pbuf, buf + buf_size, NULL, 0, 0, NULL); + } + } else if (!ff_guidcmp(g, EVENTID_AudioTypeSpanningEvent)) { + int stream_index = ff_find_stream_index(s, sid); + if (stream_index >= 0) { + AVStream *st = s->streams[stream_index]; + int audio_type; + avio_skip(pb, 8); + audio_type = avio_r8(pb); + if (audio_type == 2) + st->disposition |= AV_DISPOSITION_HEARING_IMPAIRED; + else if (audio_type == 3) + st->disposition |= AV_DISPOSITION_VISUAL_IMPAIRED; + consumed += 9; + } + } else if (!ff_guidcmp(g, EVENTID_DVBScramblingControlSpanningEvent)) { + int stream_index = ff_find_stream_index(s, sid); + if (stream_index >= 0) { + avio_skip(pb, 12); + if (avio_rl32(pb)) + av_log(s, AV_LOG_WARNING, "DVB scrambled stream detected (st:%d), decoding will likely fail\n", stream_index); + consumed += 16; + } + } else if (!ff_guidcmp(g, EVENTID_LanguageSpanningEvent)) { + int stream_index = ff_find_stream_index(s, sid); + if (stream_index >= 0) { + AVStream *st = s->streams[stream_index]; + uint8_t language[4]; + avio_skip(pb, 12); + avio_read(pb, language, 3); + if (language[0]) { + language[3] = 0; + av_dict_set(&st->metadata, "language", language, 0); + if (!strcmp(language, "nar") || !strcmp(language, "NAR")) + st->disposition |= AV_DISPOSITION_VISUAL_IMPAIRED; + } + consumed += 15; + } + } 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); + wtv->pts = avio_rl64(pb); + consumed += 16; + if (wtv->pts == -1) + wtv->pts = AV_NOPTS_VALUE; + else { + wtv->last_valid_pts = wtv->pts; + if (wtv->epoch == AV_NOPTS_VALUE || wtv->pts < wtv->epoch) + wtv->epoch = wtv->pts; + if (mode == SEEK_TO_PTS && wtv->pts >= seekts) { + avio_skip(pb, WTV_PAD8(len) - consumed); + return 0; + } + } + } + } 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 && s->streams[stream_index]->priv_data) { + WtvStream *wst = s->streams[stream_index]->priv_data; + wst->seen_data = 1; + if (len_ptr) { + *len_ptr = len; + } + 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 */ 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}) || + !ff_guidcmp(g, /* EVENTID_ChannelTypeSpanningEvent */ (const ff_asf_guid){0x51,0x1D,0xAB,0x72,0xD2,0x87,0x9B,0x48,0xBA,0x11,0x0E,0x08,0xDC,0x21,0x02,0x43}) || + !ff_guidcmp(g, /* EVENTID_PIDListSpanningEvent */ (const ff_asf_guid){0x65,0x8F,0xFC,0x47,0xBB,0xE2,0x34,0x46,0x9C,0xEF,0xFD,0xBF,0xE6,0x26,0x1D,0x5C}) || + !ff_guidcmp(g, /* EVENTID_SignalAndServiceStatusSpanningEvent */ (const ff_asf_guid){0xCB,0xC5,0x68,0x80,0x04,0x3C,0x2B,0x49,0xB4,0x7D,0x03,0x08,0x82,0x0D,0xCE,0x51}) || + !ff_guidcmp(g, /* EVENTID_StreamTypeSpanningEvent */ (const ff_asf_guid){0xBC,0x2E,0xAF,0x82,0xA6,0x30,0x64,0x42,0xA8,0x0B,0xAD,0x2E,0x13,0x72,0xAC,0x60}) || + !ff_guidcmp(g, (const ff_asf_guid){0x1E,0xBE,0xC3,0xC5,0x43,0x92,0xDC,0x11,0x85,0xE5,0x00,0x12,0x3F,0x6F,0x73,0xB9}) || + !ff_guidcmp(g, (const ff_asf_guid){0x3B,0x86,0xA2,0xB1,0xEB,0x1E,0xC3,0x44,0x8C,0x88,0x1C,0xA3,0xFF,0xE3,0xE7,0x6A}) || + !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, (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)); + + avio_skip(pb, WTV_PAD8(len) - consumed); + } + return AVERROR_EOF; +} + +static int read_header(AVFormatContext *s) +{ + WtvContext *wtv = s->priv_data; + int root_sector, root_size; + uint8_t root[WTV_SECTOR_SIZE]; + AVIOContext *pb; + int64_t timeline_pos; + int ret; + + wtv->epoch = + wtv->pts = + wtv->last_valid_pts = AV_NOPTS_VALUE; + + /* read root directory sector */ + avio_skip(s->pb, 0x30); + root_size = avio_rl32(s->pb); + if (root_size > sizeof(root)) { + av_log(s, AV_LOG_ERROR, "root directory size exceeds sector size\n"); + return AVERROR_INVALIDDATA; + } + avio_skip(s->pb, 4); + root_sector = avio_rl32(s->pb); + + avio_seek(s->pb, root_sector << WTV_SECTOR_BITS, SEEK_SET); + root_size = avio_read(s->pb, root, root_size); + if (root_size < 0) + return AVERROR_INVALIDDATA; + + /* parse chunks up until first data chunk */ + 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; + } + + ret = parse_chunks(s, SEEK_TO_DATA, 0, 0); + if (ret < 0) + return ret; + avio_seek(wtv->pb, -32, SEEK_CUR); + + timeline_pos = avio_tell(s->pb); // save before opening another file + + /* read metadata */ + pb = wtvfile_open(s, root, root_size, ff_table_0_entries_legacy_attrib_le16); + if (pb) { + parse_legacy_attrib(s, pb); + wtvfile_close(pb); + } + + /* read seek index */ + if (s->nb_streams) { + AVStream *st = s->streams[0]; + 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 (url_feof(pb)) + break; + ff_add_index_entry(&wtv->index_entries, &wtv->nb_index_entries, &wtv->index_entries_allocated_size, + 0, timestamp, frame_nb, 0, AVINDEX_KEYFRAME); + } + wtvfile_close(pb); + + if (wtv->nb_index_entries) { + 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 (url_feof(pb)) + break; + for (i = wtv->nb_index_entries - 1; i >= 0; i--) { + AVIndexEntry *e = wtv->index_entries + i; + if (frame_nb > e->size) + break; + if (position > e->pos) + e->pos = position; + } + } + wtvfile_close(pb); + st->duration = wtv->index_entries[wtv->nb_index_entries - 1].timestamp; + } + } + } + } + + avio_seek(s->pb, timeline_pos, SEEK_SET); + return 0; +} + +static int read_packet(AVFormatContext *s, AVPacket *pkt) +{ + WtvContext *wtv = s->priv_data; + AVIOContext *pb = wtv->pb; + int stream_index, len, ret; + + stream_index = parse_chunks(s, SEEK_TO_DATA, 0, &len); + if (stream_index < 0) + return stream_index; + + ret = av_get_packet(pb, pkt, len - 32); + if (ret < 0) + return ret; + pkt->stream_index = stream_index; + pkt->pts = wtv->pts; + avio_skip(pb, WTV_PAD8(len) - len); + return 0; +} + +static int read_seek(AVFormatContext *s, int stream_index, + int64_t ts, int flags) +{ + WtvContext *wtv = s->priv_data; + AVIOContext *pb = wtv->pb; + AVStream *st = s->streams[0]; + int64_t ts_relative; + int i; + + if ((flags & AVSEEK_FLAG_FRAME) || (flags & AVSEEK_FLAG_BYTE)) + return AVERROR(ENOSYS); + + /* timestamp adjustment is required because wtv->pts values are absolute, + * whereas AVIndexEntry->timestamp values are relative to epoch. */ + ts_relative = ts; + if (wtv->epoch != AV_NOPTS_VALUE) + ts_relative -= wtv->epoch; + + 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) { + 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; + return 0; +} + +static int read_close(AVFormatContext *s) +{ + WtvContext *wtv = s->priv_data; + av_freep(&wtv->index_entries); + wtvfile_close(wtv->pb); + return 0; +} + +AVInputFormat ff_wtv_demuxer = { + .name = "wtv", + .long_name = NULL_IF_CONFIG_SMALL("Windows Television (WTV)"), + .priv_data_size = sizeof(WtvContext), + .read_probe = read_probe, + .read_header = read_header, + .read_packet = read_packet, + .read_seek = read_seek, + .read_close = read_close, + .flags = AVFMT_SHOW_IDS, +}; diff --git a/libavformat/wtvenc.c b/libavformat/wtvenc.c new file mode 100644 index 0000000000..5bed51e3c0 --- /dev/null +++ b/libavformat/wtvenc.c @@ -0,0 +1,723 @@ +/* + * 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 "internal.h" +#include "wtv.h" +#include "asf.h" + +#define WTV_BIGSECTOR_SIZE (1 << WTV_BIGSECTOR_BITS) +#define INDEX_BASE 0x2 +#define MAX_NB_INDEX 10 + +/* declare utf16le strings */ +#define _ , 0, +static const uint8_t timeline_table_0_header_events[] = + {'t'_'i'_'m'_'e'_'l'_'i'_'n'_'e'_'.'_'t'_'a'_'b'_'l'_'e'_'.'_'0'_'.'_'h'_'e'_'a'_'d'_'e'_'r'_'.'_'E'_'v'_'e'_'n'_'t'_'s', 0}; +static const uint8_t table_0_header_legacy_attrib[] = + {'t'_'a'_'b'_'l'_'e'_'.'_'0'_'.'_'h'_'e'_'a'_'d'_'e'_'r'_'.'_'l'_'e'_'g'_'a'_'c'_'y'_'_'_'a'_'t'_'t'_'r'_'i'_'b', 0}; +static const uint8_t table_0_redirector_legacy_attrib[] = + {'t'_'a'_'b'_'l'_'e'_'.'_'0'_'.'_'r'_'e'_'d'_'i'_'r'_'e'_'c'_'t'_'o'_'r'_'.'_'l'_'e'_'g'_'a'_'c'_'y'_'_'_'a'_'t'_'t'_'r'_'i'_'b', 0}; +static const uint8_t table_0_header_time[] = + {'t'_'a'_'b'_'l'_'e'_'.'_'0'_'.'_'h'_'e'_'a'_'d'_'e'_'r'_'.'_'t'_'i'_'m'_'e', 0}; +static const uint8_t legacy_attrib[] = + {'l'_'e'_'g'_'a'_'c'_'y'_'_'_'a'_'t'_'t'_'r'_'i'_'b', 0}; +#undef _ + +static const ff_asf_guid sub_wtv_guid = + {0x8C,0xC3,0xD2,0xC2,0x7E,0x9A,0xDA,0x11,0x8B,0xF7,0x00,0x07,0xE9,0x5E,0xAD,0x8D}; +static const ff_asf_guid stream1_guid = + {0xA1,0xC3,0xD2,0xC2,0x7E,0x9A,0xDA,0x11,0x8B,0xF7,0x00,0x07,0xE9,0x5E,0xAD,0x8D}; +static const ff_asf_guid sync_guid = + {0x97,0xC3,0xD2,0xC2,0x7E,0x9A,0xDA,0x11,0x8B,0xF7,0x00,0x07,0xE9,0x5E,0xAD,0x8D}; +static const ff_asf_guid index_guid = + {0x96,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 timeline_start_pos; + WtvFile file[WTV_FILES]; + int64_t serial; /** chunk serial number */ + int64_t last_chunk_pos; /** last chunk position */ + int64_t frame_nb; + + WtvChunkEntry index[MAX_NB_INDEX]; + int nb_index; + int first_video_flag; + int64_t sync_pos; +} WtvContext; + +typedef int WTVHeaderWriteFunc(AVIOContext *pb); + +typedef struct { + const uint8_t *header; + int header_size; + WTVHeaderWriteFunc *write_header; +} WTVRootEntryTable; + +static int write_pad(AVIOContext *pb, int size) +{ + for (; size > 0; size--) + avio_w8(pb, 0); + return 0; +} + +static const ff_asf_guid *get_codec_guid(enum AVCodecID id, const AVCodecGuid *av_guid) +{ + int i; + for (i = 0; av_guid[i].id != AV_CODEC_ID_NONE; i++) { + if (id == av_guid[i].id) + return &(av_guid[i].guid); + } + return NULL; +} + +/** + * Write chunk header. If header chunk (0x80000000 set) then add to list of header chunks + */ +static void write_chunk_header(AVFormatContext *s, const ff_asf_guid *guid, int length, int stream_id) +{ + WtvContext *wctx = s->priv_data; + AVIOContext *pb = s->pb; + + wctx->last_chunk_pos = avio_tell(pb) - wctx->timeline_start_pos; + ff_put_guid(pb, guid); + avio_wl32(pb, 32 + length); + avio_wl32(pb, stream_id); + avio_wl64(pb, wctx->serial); + + if ((stream_id & 0x80000000) && guid != &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, &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); +} + +static void finish_chunk(AVFormatContext *s) +{ + WtvContext *wctx = s->priv_data; + finish_chunk_noindex(s); + if (wctx->nb_index == MAX_NB_INDEX) + write_index(s); +} + +static int write_stream_codec_info(AVFormatContext *s, AVStream *st) +{ + WtvContext *wctx = s->priv_data; + const ff_asf_guid *g, *media_type, *format_type; + AVIOContext *pb = s->pb; + int64_t hdr_pos_start; + int hdr_size = 0; + + if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) { + g = get_codec_guid(st->codec->codec_id, ff_video_guids); + media_type = &ff_mediatype_video; + format_type = &ff_format_mpeg2_video; + } else if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) { + g = get_codec_guid(st->codec->codec_id, ff_codec_wav_guids); + media_type = &ff_mediatype_audio; + format_type = &ff_format_waveformatex; + } else { + av_log(s, AV_LOG_ERROR, "unknown codec_type (0x%x)\n", st->codec->codec_type); + return -1; + } + + if (g == NULL) { + av_log(s, AV_LOG_ERROR, "can't get video codec_id (0x%x) guid.\n", st->codec->codec_id); + return -1; + } + + ff_put_guid(pb, media_type); // mediatype + ff_put_guid(pb, &ff_mediasubtype_cpfilters_processed); // subtype + write_pad(pb, 12); + ff_put_guid(pb,&ff_format_cpfilters_processed); // format type + avio_wl32(pb, 0); // size + + hdr_pos_start = avio_tell(pb); + if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) { + if (wctx->first_video_flag) { + write_pad(pb, 216); //The size is sensitive. + wctx->first_video_flag = 0; + } else { + write_pad(pb, 72); // aspect ratio + ff_put_bmp_header(pb, st->codec, ff_codec_bmp_tags, 0); + } + } else { + ff_put_wav_header(pb, st->codec); + } + hdr_size = avio_tell(pb) - hdr_pos_start; + + // seek back write hdr_size + avio_seek(pb, -(hdr_size + 4), SEEK_CUR); + avio_wl32(pb, hdr_size + 32); + avio_seek(pb, hdr_size, SEEK_CUR); + ff_put_guid(pb, g); // actual_subtype + ff_put_guid(pb, format_type); // actual_formattype + + return 0; +} + +static int write_stream_codec(AVFormatContext *s, AVStream * st) +{ + AVIOContext *pb = s->pb; + int ret; + write_chunk_header2(s, &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; + wctx->sync_pos = avio_tell(pb) - wctx->timeline_start_pos; + + write_chunk_header(s, &sync_guid, 0x18, 0); + write_pad(pb, 24); + + finish_chunk(s); + + wctx->last_chunk_pos = last_chunk_pos; +} + +static void write_DSATTRIB_TRANSPORT_PROPERTIES_init(AVFormatContext *s, int stream_index) +{ + AVIOContext *pb = s->pb; + write_chunk_header2(s, &ff_DSATTRIB_TRANSPORT_PROPERTIES, 0x80000000 | stream_index); + avio_wl64(pb, stream_index); + avio_wl64(pb, -1); + avio_wl64(pb, 0); + finish_chunk(s); +} + +static int write_stream_data(AVFormatContext *s, AVStream *st, int flag) +{ + AVIOContext *pb = s->pb; + int ret; + + if (!flag) { + write_chunk_header2(s, &ff_stream_guid, 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); + } else { + write_chunk_header2(s, &ff_stream2_guid, 0x80000000 | (st->index + INDEX_BASE)); + 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); + + 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; + + 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]; + 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 + 1 < s->nb_streams) { + write_sync(s); + } + } + + for (i = 0; i < s->nb_streams; i++) { + st = s->streams[i]; + ret = write_stream_data(s, st, 0); + if (ret < 0) { + av_log(s, AV_LOG_ERROR, "write stream data failed codec_type(0x%x)\n", st->codec->codec_type); + return -1; + } + ret = write_stream_data(s, st, 1); + if (ret < 0) { + av_log(s, AV_LOG_ERROR, "write stream2 data failed codec_type(0x%x)\n", st->codec->codec_type); + return -1; + } + } + + for (i = 0; i < s->nb_streams; i++) + write_DSATTRIB_TRANSPORT_PROPERTIES_init(s, INDEX_BASE + i); + + 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; + int flag = 0; + int64_t frame_number = 0; + + if (enc->codec_type == AVMEDIA_TYPE_VIDEO) { + wctx->frame_nb++; + frame_number = wctx->frame_nb; + flag = pkt->flags & AV_PKT_FLAG_KEY ? 1 : 0; + } + 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, frame_number); + avio_wl64(pb, 0); + avio_wl64(pb, flag); + avio_wl64(pb, 0); +} + +static int write_packet(AVFormatContext *s, AVPacket *pkt) +{ + AVIOContext *pb = s->pb; + WtvContext *wctx = s->priv_data; + + // 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++; + avio_flush(pb); + return 0; +} + +static int write_table0_header_envents(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_envents}, + { 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 int 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; + + //FIXME: output frame_nb, position pairs. + //We only set the first sync_chunk position here. + avio_wl64(pb, 0x2); avio_wl64(pb, wctx->sync_pos); +} + +static void write_tag(AVIOContext *pb, const char *key, const char *value) +{ + ff_put_guid(pb, &ff_metadata_guid); + avio_wl32(pb, 1); + avio_wl32(pb, strlen(value)*2 + 2); + avio_put_str16le(pb, key); + avio_put_str16le(pb, value); +} + +static void write_table_entries_attrib(AVFormatContext *s) +{ + 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(s->pb, tag->key, tag->value); +} + +static void write_table_redirector_legacy_attrib(AVFormatContext *s) +{ + 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 += 16 + 4 + 4 + strlen(tag->key)*2 + 2 + strlen(tag->value)*2 + 2; + } +} + +/** + * 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); + } else { + w->first_sector = start_pos; + } + w->first_sector >>= 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); + //FIXME: output timestamp, frame_nb pairs here. + 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); + 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 a2ae5a4dba..8d9184c1ca 100644 --- a/libavformat/wv.c +++ b/libavformat/wv.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 */ @@ -255,7 +255,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->samples; + if (wc->samples != 0xFFFFFFFFu) + st->duration = wc->samples; if (s->pb->seekable) { int64_t cur = avio_tell(s->pb); @@ -276,7 +277,7 @@ static int wv_read_packet(AVFormatContext *s, AVPacket *pkt) int64_t pos; uint32_t block_samples; - if (s->pb->eof_reached) + if (url_feof(s->pb)) return AVERROR_EOF; if (wc->block_parsed) { if ((ret = wv_read_block_header(s, s->pb, 0)) < 0) diff --git a/libavformat/wvenc.c b/libavformat/wvenc.c new file mode 100644 index 0000000000..d225ed4280 --- /dev/null +++ b/libavformat/wvenc.c @@ -0,0 +1,100 @@ +/* + * WavPack 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 "avformat.h" +#include "internal.h" +#include "avio_internal.h" +#include "apetag.h" + +typedef struct{ + uint32_t duration; + int off; +} WVMuxContext; + +static int write_header(AVFormatContext *s) +{ + WVMuxContext *wc = s->priv_data; + AVCodecContext *codec = s->streams[0]->codec; + + if (s->nb_streams > 1) { + av_log(s, AV_LOG_ERROR, "only one stream is supported\n"); + return AVERROR(EINVAL); + } + if (codec->codec_id != AV_CODEC_ID_WAVPACK) { + av_log(s, AV_LOG_ERROR, "unsupported codec\n"); + return AVERROR(EINVAL); + } + if (codec->extradata_size > 0) { + av_log_missing_feature(s, "remuxing from matroska container", 0); + return AVERROR_PATCHWELCOME; + } + wc->off = codec->channels > 2 ? 4 : 0; + avpriv_set_pts_info(s->streams[0], 64, 1, codec->sample_rate); + + return 0; +} + +static int write_packet(AVFormatContext *s, AVPacket *pkt) +{ + WVMuxContext *wc = s->priv_data; + AVIOContext *pb = s->pb; + + wc->duration += pkt->duration; + ffio_wfourcc(pb, "wvpk"); + avio_wl32(pb, pkt->size + 12 + wc->off); + avio_wl16(pb, 0x410); + avio_w8(pb, 0); + avio_w8(pb, 0); + avio_wl32(pb, -1); + avio_wl32(pb, pkt->pts); + avio_write(s->pb, pkt->data, pkt->size); + avio_flush(s->pb); + + return 0; +} + +static int write_trailer(AVFormatContext *s) +{ + WVMuxContext *wc = s->priv_data; + AVIOContext *pb = s->pb; + + ff_ape_write(s); + + if (pb->seekable) { + avio_seek(pb, 12, SEEK_SET); + avio_wl32(pb, wc->duration); + avio_flush(pb); + } + + return 0; +} + +AVOutputFormat ff_wv_muxer = { + .name = "wv", + .long_name = NULL_IF_CONFIG_SMALL("WavPack"), + .priv_data_size = sizeof(WVMuxContext), + .extensions = "wv", + .audio_codec = AV_CODEC_ID_WAVPACK, + .video_codec = AV_CODEC_ID_NONE, + .write_header = write_header, + .write_packet = write_packet, + .write_trailer = write_trailer, +}; diff --git a/libavformat/xa.c b/libavformat/xa.c index 02d66c5364..e3d36e61b5 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 */ diff --git a/libavformat/xmv.c b/libavformat/xmv.c index 5041c571c3..f250aad0b2 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,81 +32,79 @@ #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) -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; + int stream_index; ///< The decoder stream index for this video packet. - uint32_t data_size; - uint32_t data_offset; + uint32_t data_size; ///< The size of the remaining video data. + uint64_t data_offset; ///< The offset of the video data within the file. - uint32_t current_frame; - uint32_t frame_count; + uint32_t current_frame; ///< The current frame within this video packet. + uint32_t frame_count; ///< The amount of frames within this video packet. - /* Does the video packet contain extra data? */ - int has_extradata; + int has_extradata; ///< Does the video packet contain extra data? + uint8_t extradata[4]; ///< The extra data - /* Extra data */ - uint8_t extradata[4]; - - 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 + uint16_t 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; + uint16_t audio_track_count; ///< Number of audio track in this file. - XMVAudioTrack *audio_tracks; - - 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) @@ -172,34 +170,33 @@ static int xmv_read_header(AVFormatContext *s) avio_skip(pb, 2); /* Unknown (padding?) */ - xmv->audio_tracks = av_malloc(xmv->audio_track_count * sizeof(XMVAudioTrack)); - if (!xmv->audio_tracks) - return AVERROR(ENOMEM); - xmv->audio = av_malloc(xmv->audio_track_count * sizeof(XMVAudioPacket)); if (!xmv->audio) return AVERROR(ENOMEM); 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 = 36 * 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); + + if (!packet->channels) { + av_log(s, AV_LOG_ERROR, "0 channels\n"); + return AVERROR(EINVAL); + } + + packet->bit_rate = packet->bits_per_sample * + packet->sample_rate * + packet->channels; + packet->block_align = 36 * 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; @@ -207,24 +204,24 @@ 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); ast = avformat_new_stream(s, NULL); if (!ast) return AVERROR(ENOMEM); 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; @@ -232,7 +229,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; @@ -277,7 +274,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); @@ -298,7 +295,7 @@ static int xmv_process_packet_header(AVFormatContext *s) * short for every audio track. But as playing around with XMV files with * ADPCM audio showed, taking the extra 4 bytes from the audio data gives * you either completely distorted audio or click (when skipping the - * remaining 68 bytes of the ADPCM block). Substracting 4 bytes for every + * remaining 68 bytes of the ADPCM block). Subtracting 4 bytes for every * audio track from the video data works at least for the audio. Probably * some alignment thing? * The video data has (always?) lots of padding, so it should work out... @@ -308,7 +305,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 */ @@ -328,9 +325,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 */ @@ -358,7 +355,7 @@ 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); @@ -434,7 +431,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; @@ -459,7 +456,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) @@ -474,17 +471,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; @@ -550,8 +547,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; } diff --git a/libavformat/xwma.c b/libavformat/xwma.c index 46ca0b80f1..04c36296d1 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 */ @@ -42,7 +42,7 @@ static int xwma_probe(AVProbeData *p) static int xwma_read_header(AVFormatContext *s) { - int64_t size, av_uninit(data_size); + int64_t size; int ret; uint32_t dpds_table_size = 0; uint32_t *dpds_table = 0; diff --git a/libavformat/yop.c b/libavformat/yop.c index 51d088bdd2..2d89c6e3f2 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 */ @@ -38,10 +38,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; @@ -59,6 +64,8 @@ static int yop_read_header(AVFormatContext *s) audio_stream = avformat_new_stream(s, NULL); video_stream = avformat_new_stream(s, NULL); + if (!audio_stream || !video_stream) + return AVERROR(ENOMEM); // Extra data that will be passed to the decoder video_stream->codec->extradata_size = 8; diff --git a/libavformat/yuv4mpeg.c b/libavformat/yuv4mpeg.c index a8077a00f0..357b4122c7 100644 --- a/libavformat/yuv4mpeg.c +++ b/libavformat/yuv4mpeg.c @@ -2,24 +2,25 @@ * YUV4MPEG format * Copyright (c) 2001, 2002, 2003 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "avformat.h" #include "internal.h" +#include "libavutil/pixdesc.h" #define Y4M_MAGIC "YUV4MPEG2" #define Y4M_FRAME_MAGIC "FRAME" @@ -60,6 +61,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; @@ -76,6 +80,33 @@ 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_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 */ @@ -90,7 +121,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; @@ -98,7 +129,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) { @@ -121,12 +153,39 @@ 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_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 avcodec_get_chroma_sub_sample(st->codec->pix_fmt, &h_chroma_shift, &v_chroma_shift); @@ -144,6 +203,7 @@ static int yuv4_write_packet(AVFormatContext *s, AVPacket *pkt) ptr2 += picture->linesize[2]; } } + avio_flush(pb); return 0; } @@ -160,15 +220,42 @@ 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_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 standart 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, " + "yuv444p16, yuv422p16, yuv420p16 " + "and gray16 pixel formats. " "Use -pix_fmt to select one.\n"); return AVERROR(EIO); } @@ -180,7 +267,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, @@ -249,20 +335,40 @@ 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("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 " @@ -316,6 +422,24 @@ 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("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) |